Skip to content

Commit

Permalink
added new datatype yarp::sig::LayeredImage with test
Browse files Browse the repository at this point in the history
modified yarpopencvdisplay to support `yarp::sig::LayeredImage`
added yarp::sig::utils::sum() to transform a`yarp::sig::LayeredImage` to `yarp::sig::Image`
  • Loading branch information
randaz81 committed Dec 3, 2024
1 parent ba79e30 commit ef2193d
Show file tree
Hide file tree
Showing 7 changed files with 605 additions and 29 deletions.
2 changes: 2 additions & 0 deletions src/libYARP_sig/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ set(YARP_sig_HDRS
yarp/sig/ImageNetworkHeader.h
yarp/sig/ImageUtils.h
yarp/sig/IntrinsicParams.h
yarp/sig/LayeredImage.h
yarp/sig/LaserMeasurementData.h
yarp/sig/Matrix.h
yarp/sig/PointCloud.h
Expand All @@ -40,6 +41,7 @@ set(YARP_sig_SRCS
yarp/sig/ImageUtils.cpp
yarp/sig/IntrinsicParams.cpp
yarp/sig/LaserMeasurementData.cpp
yarp/sig/LayeredImage.cpp
yarp/sig/Matrix.cpp
yarp/sig/PointCloudBase.cpp
yarp/sig/PointCloudUtils.cpp
Expand Down
67 changes: 67 additions & 0 deletions src/libYARP_sig/src/yarp/sig/ImageUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <yarp/sig/ImageUtils.h>
#include <cstring>
#include <algorithm> // std::math
#include <yarp/os/Log.h>
#include <yarp/os/LogStream.h>

using namespace yarp::sig;

Expand Down Expand Up @@ -141,3 +143,68 @@ bool utils::cropRect(const yarp::sig::Image& inImg,

return true;
}

bool utils::sum(Image& OutImg, const Image& InImg, bool enable_colorkey, int colorkey, bool enable_alpha, float alpha, size_t offset_x, size_t offset_y)
{
if (OutImg.getPixelCode() != InImg.getPixelCode())
{
yError() << "utils::sum() Input and Output images must have the same pixelcode";
return false;
}

if (InImg.getPixelCode() != VOCAB_PIXEL_RGB)
{
yError() << "utils::sum() Unimplemented pixelcode";
return false;
}

yarp::sig::PixelRgb ColorkeyRGB;
ColorkeyRGB.r = colorkey;
ColorkeyRGB.g = colorkey;
ColorkeyRGB.b = colorkey;

size_t yis = InImg.height();
size_t xis = InImg.width();
size_t yos = OutImg.height();
size_t xos = OutImg.width();

for (size_t y = 0; y < yis; ++y)
{
for (size_t x = 0; x < xis; ++x)
{
size_t xo = x + offset_x;
size_t yo = y + offset_y;
if (xo > xos) {
xo = xos;
}
if (yo > yos) {
yo = yos;
}

unsigned char* layer_pointer = InImg.getPixelAddress(x, y);
unsigned char* outimg_pointer = OutImg.getPixelAddress(xo, yo);

yarp::sig::PixelRgb* layer_pointer_rgb = reinterpret_cast<yarp::sig::PixelRgb*>(layer_pointer);
yarp::sig::PixelRgb* outimg_pointer_rgb = reinterpret_cast<yarp::sig::PixelRgb*>(outimg_pointer);

if (enable_colorkey && layer_pointer_rgb->r == ColorkeyRGB.r && layer_pointer_rgb->g == ColorkeyRGB.g && layer_pointer_rgb->b == ColorkeyRGB.b)
{
continue;
}
else if (enable_alpha)
{
outimg_pointer_rgb->r = layer_pointer_rgb->r * alpha + outimg_pointer_rgb->r * (1 - alpha);
outimg_pointer_rgb->g = layer_pointer_rgb->g * alpha + outimg_pointer_rgb->g * (1 - alpha);
outimg_pointer_rgb->b = layer_pointer_rgb->b * alpha + outimg_pointer_rgb->b * (1 - alpha);
}
else
{
outimg_pointer_rgb->r = layer_pointer_rgb->r;
outimg_pointer_rgb->g = layer_pointer_rgb->g;
outimg_pointer_rgb->b = layer_pointer_rgb->b;
}
}
}

return true;
}
22 changes: 22 additions & 0 deletions src/libYARP_sig/src/yarp/sig/ImageUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,28 @@ bool YARP_sig_API cropRect(const yarp::sig::Image& inImg,
const std::pair<unsigned int, unsigned int>& vertex1,
const std::pair<unsigned int, unsigned int>& vertex2,
yarp::sig::Image& outImg);

/**
* @brief applies an image on the top over another image.
* @param[in/out] OutImg the output image. It must be a valid image on the top of which data will be summed. It may contain a backgroud or it can be zero.
* @param[in] InImg the layer to be applied
* @param[in] colorkey colorkey for the InImg image. If a pixel is == colorkey, then it will be made transparent and the backgroud will be visible.
* @param[in] alpha to be applied to InImg.
* @param[in] off_x horizontal offset applied to InImg. Excess will be cropped.
* @param[in] off_y vertical offset applied to InImg. Excess will be cropped.
* @note The two images must have the same pixelCode.
* @return true on success, false if image cannot be summed because of incompatible format.
*/
bool YARP_sig_API sum(yarp::sig::Image& OutImg,
const yarp::sig::Image& InImg,
bool colorkey_enable,
int colorkey,
bool alpha_enable,
float alpha,
size_t off_x,
size_t off_y);

} // namespace yarp::sig::utils


#endif // YARP_SIG_IMAGEUTILS_H
261 changes: 261 additions & 0 deletions src/libYARP_sig/src/yarp/sig/LayeredImage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
/*
* SPDX-FileCopyrightText: 2024-2024 Istituto Italiano di Tecnologia (IIT)
* SPDX-License-Identifier: BSD-3-Clause
*/

#include <yarp/sig/Image.h>
#include <yarp/sig/ImageNetworkHeader.h>

#include <yarp/os/Bottle.h>
#include <yarp/os/ConnectionReader.h>
#include <yarp/os/ConnectionWriter.h>
#include <yarp/os/Log.h>
#include <yarp/os/Time.h>
#include <yarp/os/Vocab.h>
#include <yarp/os/LogStream.h>

#include <yarp/sig/LayeredImage.h>
#include <yarp/sig/ImageUtils.h>

#include <cstdio>
#include <cstring>
#include <string>
#include <utility>

#include <opencv2/opencv.hpp>

using namespace yarp::sig;
using namespace yarp::os;

inline void writeToConnection(const Image& img, ConnectionWriter& connection)
{
ImageNetworkHeader imghdr;

imghdr.setFromImage(img);
size_t hdrsize = sizeof(imghdr);
connection.appendInt32(BOTTLE_TAG_BLOB);
connection.appendInt32(hdrsize);
connection.appendBlock((char*)(&imghdr), hdrsize);

size_t imgsize = img.getRawImageSize();
connection.appendInt32(BOTTLE_TAG_BLOB);
connection.appendInt32(imgsize);
connection.appendBlock((char*)(img.getRawImage()), imgsize);

return;
}

inline bool readFromConnection(FlexImage& dest, ConnectionReader& connection)
{
bool ok = true;
ImageNetworkHeader imghdr;

connection.expectInt32();
size_t sizeData = connection.expectInt32();
ok &= connection.expectBlock((char*)(&imghdr), sizeData);
if (!ok) { return false; }
imghdr.setToImage(dest);

connection.expectInt32();
size_t sizeImg = connection.expectInt32();
size_t psizeImg = dest.getRawImageSize();
if (sizeImg != psizeImg)
{
return false;
}
unsigned char* pImg = dest.getRawImage();
ok &= connection.expectBlock((char*)pImg, sizeImg);

return ok;
}

LayeredImage::LayeredImage()
{
}


LayeredImage::~LayeredImage()
{
}


void LayeredImage::clear()
{
background.zero();
layers.clear();
}

bool LayeredImage::read(yarp::os::ConnectionReader& connection)
{
bool ok = true;

connection.convertTextMode();

// LIST OF ELEMENTS
connection.expectInt32();
size_t elems = connection.expectInt32();

//ELEMENT 1
connection.expectInt32();
size_t layersNum = connection.expectInt32();
if (elems != 1 + (1 * 2) + (layersNum*10))
{
return false;
}

// ELEMENT 2-3
ok &= readFromConnection(background, connection);

// ELEMENT 4-...
// each layer contains 8+2 elems
layers.clear();
for (size_t i = 0; i < layersNum; i++)
{
yarp::sig::ImageLayer::colorkey_s colorkey = yarp::sig::ImageLayer::colorkey_s {};
yarp::sig::ImageLayer::alpha_s alpha = yarp::sig::ImageLayer::alpha_s {};

connection.expectInt32();
int32_t enable_val = connection.expectInt8(); //1
connection.expectInt32();
colorkey.enable = connection.expectInt8(); //2
connection.expectInt32();
colorkey.value = connection.expectInt32(); //3
connection.expectInt32();
alpha.enable = connection.expectInt8(); //4
connection.expectInt32();
alpha.value = connection.expectFloat32(); // 5
connection.expectInt32();
bool can_be_compressed = connection.expectInt8(); //6
connection.expectInt32();
int32_t offset_x = connection.expectInt32(); //7
connection.expectInt32();
int32_t offset_y = connection.expectInt32(); //8

FlexImage fleximg;
ok &= readFromConnection(fleximg, connection); //9-10
yarp::sig::ImageLayer oneLayer(fleximg, enable_val, colorkey, alpha, can_be_compressed, offset_x, offset_y);
layers.emplace_back(oneLayer);
}

return true;
}


bool LayeredImage::write(yarp::os::ConnectionWriter& connection) const
{
bool ok = true;
size_t layers_num = layers.size();

//LIST OF ELEMENTS
connection.appendInt32(BOTTLE_TAG_LIST);
connection.appendInt32(1+(1*2)+(layers_num*10));

//ELEMENT 1
connection.appendInt32(BOTTLE_TAG_INT32);
connection.appendInt32(layers_num);

// ELEMENT 2-3
writeToConnection(background, connection);

// ELEMENT 4-...
// each layer contains 8+2 elems
for (size_t i = 0; i < layers_num; i++)
{
connection.appendInt32(BOTTLE_TAG_INT8); //1
connection.appendInt8 (layers[i].enable);
connection.appendInt32(BOTTLE_TAG_INT8); //2
connection.appendInt8(layers[i].colorkey.enable);
connection.appendInt32(BOTTLE_TAG_INT32); //3
connection.appendInt32(layers[i].colorkey.value);
connection.appendInt32(BOTTLE_TAG_INT8); //4
connection.appendInt8(layers[i].alpha.enable);
connection.appendInt32(BOTTLE_TAG_FLOAT32); //5
connection.appendFloat32(layers[i].alpha.value);
connection.appendInt32(BOTTLE_TAG_INT8); //6
connection.appendInt8(layers[i].can_be_compressed);
connection.appendInt32(BOTTLE_TAG_INT32); //7
connection.appendInt32(layers[i].offset_x);
connection.appendInt32(BOTTLE_TAG_INT32); //8
connection.appendInt32(layers[i].offset_y);
writeToConnection(layers[i].layer, connection); // 9-10
}

connection.convertTextMode();
return !connection.isError();
}


LayeredImage::LayeredImage(const LayeredImage& alt) :
Portable()
{
background = alt.background;
this->layers = alt.layers;
}

LayeredImage::LayeredImage(LayeredImage&& other) noexcept
{
}

LayeredImage& LayeredImage::operator=(const LayeredImage& alt)
{
background = alt.background;
this->layers = alt.layers;
return *this;
}

bool LayeredImage::operator==(const LayeredImage& alt) const
{
size_t l1 = this->layers.size();
size_t l2 = alt.layers.size();

if (l1 != l2)
{
return false;
}

if (background != alt.background)
{
return false;
}

for (size_t i = 0; i < l1; i++)
{
if ((this->layers[i].enable != alt.layers[i].enable) ||
(this->layers[i].colorkey.enable != alt.layers[i].colorkey.enable) ||
(this->layers[i].colorkey.value != alt.layers[i].colorkey.value) ||
(this->layers[i].alpha.enable != alt.layers[i].alpha.enable) ||
(fabs(this->layers[i].alpha.value - alt.layers[i].alpha.value) > 0.001) ||
(this->layers[i].layer != alt.layers[i].layer) ||
(this->layers[i].can_be_compressed != alt.layers[i].can_be_compressed) ||
(this->layers[i].offset_x != alt.layers[i].offset_x) ||
(this->layers[i].offset_y != alt.layers[i].offset_y))
{
return false;
}
}

return true;
}

yarp::sig::FlexImage LayeredImage::convert_to_flexImage()
{
yarp::sig::FlexImage outimg = background;

bool ret = true;
for (size_t i = 0; i < this->layers.size(); i++)
{
if (layers[i].enable == false)
{
continue;
}

ret &= yarp::sig::utils::sum(outimg, layers[i].layer, layers[i].colorkey.enable, layers[i].colorkey.value, layers[i].alpha.enable, layers[i].alpha.value, layers[i].offset_x, layers[i].offset_y);
}

return outimg;
}

LayeredImage::operator yarp::sig::FlexImage()
{
return convert_to_flexImage();
}
Loading

0 comments on commit ef2193d

Please sign in to comment.