Skip to content
Snippets Groups Projects
Commit 60ee1933 authored by Daniel Müller's avatar Daniel Müller
Browse files

* Improved the Replace color block

* Added helper classes to make working with color and grayscale pixels easier
parent 9b0a196e
No related branches found
No related tags found
No related merge requests found
......@@ -17,7 +17,7 @@
#define GRNDR_VERSION_MAJOR 0
#define GRNDR_VERSION_MINOR 5
#define GRNDR_VERSION_REVISION 0
#define GRNDR_VERSION_BUILD 191
#define GRNDR_VERSION_BUILD 192
namespace grndr
{
......
......@@ -6,22 +6,21 @@
#include "Grinder.h"
#include "Pixel.h"
QColor Pixel::getColor() const
QColor ColorPixel::getColor() const
{
return QColor{z, y, x}; // Colors are in BGR format in OpenCV
return QColor{_r, _g, _b};
}
void Pixel::setColor(const QColor& color)
void ColorPixel::setColor(const QColor& color)
{
// Colors are in BGR format in OpenCV
z = color.red();
y = color.green();
x = color.blue();
_r = color.red();
_g = color.green();
_b = color.blue();
}
bool Pixel::operator ==(const Pixel& other) const
bool ColorPixel::operator ==(const ColorPixel& other) const
{
return x == other.x && y == other.y && z == other.z;
return _r == other._r && _g == other._g && _b == other._b;
}
QColor GrayscalePixel::getColor() const
......@@ -33,3 +32,50 @@ void GrayscalePixel::setColor(const QColor& color)
{
_p = std::lround((color.red() + color.green() + color.blue()) / 3.0f);
}
PixelRef::PixelRef(ColorPixel* pixel) :
_pixel{pixel}, _pixelGetter{&PixelRef::getPixel}, _pixelSetter{&PixelRef::setPixel}
{
}
PixelRef::PixelRef(GrayscalePixel* grayscalePixel) :
_grayscalePixel{grayscalePixel}, _pixelGetter{&PixelRef::getGrayscalePixel}, _pixelSetter{&PixelRef::setGrayscalePixel}
{
}
PixelRef PixelAccessor::at(int x, int y)
{
if (_matrix.channels() == 3) // Color
return PixelRef{&_matrix.at<ColorPixel>(y, x)};
else
return PixelRef{&_matrix.at<GrayscalePixel>(y, x)};
}
void PixelAccessor::forEach(std::function<void (PixelRef, QPoint)> callback)
{
if (_matrix.channels() == 3) // Color
{
_matrix.forEach<ColorPixel>([callback](ColorPixel& pixel, const int pos[]) {
callback(PixelRef{&pixel}, QPoint{pos[1], pos[0]});
});
}
else // Grayscale
{
_matrix.forEach<GrayscalePixel>([callback](GrayscalePixel& pixel, const int pos[]) {
callback(PixelRef{&pixel}, QPoint{pos[1], pos[0]});
});
}
}
void PixelAccessor::verifyMatrix()
{
if (CV_MAT_DEPTH(_matrix.type()) == CV_8U || CV_MAT_DEPTH(_matrix.type()) == CV_8S)
{
if (_matrix.channels() != 1 && _matrix.channels() != 3)
throw std::invalid_argument{_EXCPT("The matrix must have 1 or 3 channels")};
}
else
throw std::invalid_argument{_EXCPT("The matrix must have a depth of 8 bit")};
}
......@@ -7,22 +7,21 @@
#define PIXEL_H
#include <QColor>
#include <QPoint>
#include <opencv2/core.hpp>
namespace grndr
{
class Pixel : public cv::Point3_<uint8_t>
class ColorPixel
{
public:
using base_type = cv::Point3_<uint8_t>;
ColorPixel() { }
ColorPixel(uint8_t r, uint8_t g, uint8_t b) : _b{b}, _g{g}, _r{r} { }
ColorPixel(const QColor& color) { setColor(color); }
ColorPixel(const ColorPixel& other) = default;
ColorPixel(ColorPixel&& other) = default;
public:
using base_type::base_type;
using base_type::operator =;
Pixel(const QColor& color) { setColor(color); }
Pixel& operator =(const QColor& color) { setColor(color); return *this; }
ColorPixel& operator =(const QColor& color) { setColor(color); return *this; }
public:
QColor getColor() const;
......@@ -31,8 +30,13 @@ namespace grndr
operator QColor() const { return getColor(); }
public:
bool operator ==(const Pixel& other) const;
bool operator ==(const QColor& other) const { return Pixel{other} == *this; }
bool operator ==(const ColorPixel& other) const;
bool operator ==(const QColor& other) const { return ColorPixel{other} == *this; }
private:
uint8_t _b{0};
uint8_t _g{0};
uint8_t _r{0};
};
class GrayscalePixel
......@@ -41,9 +45,13 @@ namespace grndr
GrayscalePixel() { }
GrayscalePixel(uint8_t p) : _p{p} { }
GrayscalePixel(const QColor& color) { setColor(color); }
GrayscalePixel(const GrayscalePixel& other) = default;
GrayscalePixel(GrayscalePixel&& other) = default;
GrayscalePixel& operator =(uint8_t p) { _p = p; return *this; }
GrayscalePixel& operator =(const QColor& color) { setColor(color); return *this; }
GrayscalePixel& operator =(const GrayscalePixel& other) = default;
GrayscalePixel& operator =(GrayscalePixel&& other) = default;
public:
QColor getColor() const;
......@@ -59,15 +67,63 @@ namespace grndr
private:
uint8_t _p{0};
};
class PixelRef
{
public:
PixelRef(ColorPixel* pixel);
PixelRef(GrayscalePixel* grayscalePixel);
public:
PixelRef& operator =(const QColor& color) { set(color); return *this; }
public:
QColor get() const { return _pixelGetter(this); }
void set(const QColor& color) { _pixelSetter(this, color); }
operator QColor() const { return get(); }
private:
QColor getPixel() const { return _pixel->getColor(); }
void setPixel(const QColor& color) { _pixel->setColor(color); }
QColor getGrayscalePixel() const { return _grayscalePixel->getColor(); }
void setGrayscalePixel(const QColor& color) { _grayscalePixel->setColor(color); }
private:
ColorPixel* _pixel{nullptr};
GrayscalePixel* _grayscalePixel{nullptr};
std::function<QColor(const PixelRef*)> _pixelGetter{nullptr};
std::function<void(PixelRef*, QColor)> _pixelSetter{nullptr};
};
class PixelAccessor
{
public:
PixelAccessor(cv::Mat matrix) : _matrix{matrix} { verifyMatrix(); }
public:
PixelRef at(int x, int y);
PixelRef at(QPoint pos) { return at(pos.x(), pos.y()); }
void forEach(std::function<void(PixelRef, QPoint)> callback);
PixelRef operator [](QPoint pos) { return at(pos); }
private:
void verifyMatrix();
private:
cv::Mat _matrix;
};
}
namespace cv
{
template<>
class DataType<grndr::Pixel>
class DataType<grndr::ColorPixel>
{
public:
using value_type = grndr::Pixel;
using value_type = grndr::ColorPixel;
using work_type = typename cv::DataType<uint8_t>::work_type;
using channel_type = uint8_t;
......@@ -102,9 +158,9 @@ namespace cv
namespace traits
{
template<>
struct Depth<grndr::Pixel> { enum { value = Depth<uint8_t>::value }; };
struct Depth<grndr::ColorPixel> { enum { value = Depth<uint8_t>::value }; };
template<>
struct Type<grndr::Pixel> { enum { value = CV_MAKETYPE(Depth<uint8_t>::value, 3) }; };
struct Type<grndr::ColorPixel> { enum { value = CV_MAKETYPE(Depth<uint8_t>::value, 3) }; };
template<>
struct Depth<grndr::GrayscalePixel> { enum { value = Depth<uint8_t>::value }; };
......
......@@ -20,25 +20,18 @@ void ReplaceColorProcessor::execute(EngineExecutionContext& ctx)
if (auto dataBlob = portData(ctx, _block->inPort()))
{
cv::Mat processedImage = dataBlob->getMatrix().clone();
PixelAccessor pixels{processedImage};
QColor fromColor = *_block->fromColor();
if (dataBlob->dataDescriptor().isColor())
{
processedImage.forEach<Pixel>([this](Pixel& pixel, const int pos[]) {
Q_UNUSED(pos);
if (CVUtils::compareColors(pixel, *_block->fromColor(), *_block->tolerance() / 100.0f))
pixel = *_block->toColor();
});
}
else if (dataBlob->dataDescriptor().isGrayscale())
{
processedImage.forEach<GrayscalePixel>([this](GrayscalePixel& pixel, const int pos[]) {
Q_UNUSED(pos);
if (CVUtils::compareColors(pixel, *_block->fromColor(), *_block->tolerance() / 100.0f))
pixel = *_block->toColor();
});
}
if (dataBlob->dataDescriptor().isGrayscale())
fromColor = CVUtils::colorToGrayscale(fromColor);
pixels.forEach([this, fromColor](PixelRef pixel, QPoint pos) {
Q_UNUSED(pos);
if (CVUtils::compareColors(pixel, fromColor, *_block->tolerance() / 100.0f))
pixel = *_block->toColor();
});
ctx.setContextEntry(_block->outPort(), DataBlob{getPortDataDescriptor(_block->outPort()), std::move(processedImage), dataBlob->getColorSpace()});
}
......
......@@ -39,6 +39,12 @@ QPixmap CVUtils::matrixToPixmap(const cv::Mat& matrix)
return QPixmap::fromImage(matrixToImage(matrix));
}
QColor CVUtils::colorToGrayscale(QColor color)
{
auto p = std::lround((color.red() + color.green() + color.blue()) / 3.0f);
return QColor{p, p, p};
}
bool CVUtils::compareColors(QColor clr1, QColor clr2, float tolerance)
{
if (tolerance > 0.0f)
......
......@@ -24,6 +24,7 @@ namespace grndr
template<typename DataType>
static QString matrixToString(const cv::Mat& matrix, std::function<QString(const DataType&)> formatter);
static QColor colorToGrayscale(QColor color);
static bool compareColors(QColor clr1, QColor clr2, float tolerance = 0.0f);
private:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment