diff --git a/Grinder/cv/CVUtils.cpp b/Grinder/cv/CVUtils.cpp index 71625f7888f8e519db04222256bd410c11b0cea3..629b170201cb140f1e85d45305f03e5028a4a44e 100644 --- a/Grinder/cv/CVUtils.cpp +++ b/Grinder/cv/CVUtils.cpp @@ -1,117 +1,122 @@ -/****************************************************************************** - * File: CVUtils.cpp - * Date: 19.2.2018 - *****************************************************************************/ - -#include "Grinder.h" -#include "CVUtils.h" -#include "image/ImageExceptions.h" - -QImage CVUtils::matrixToImage(const cv::Mat& matrix) -{ - cv::Mat imageMatrix = matrix; - - // Check if the matrix has an unsupported type; if so, convert it first - if (matrix.type() != CV_8UC4 && matrix.type() != CV_8UC3 && matrix.type() != CV_8UC1) - { - matrix.convertTo(imageMatrix, CV_8U); - cv::normalize(imageMatrix, imageMatrix, std::numeric_limits<unsigned char>::max(), std::numeric_limits<unsigned char>::min(), cv::NORM_MINMAX); - } - - switch (imageMatrix.type()) - { - case CV_8UC4: - return QImage(imageMatrix.data, imageMatrix.cols, imageMatrix.rows, static_cast<int>(imageMatrix.step), QImage::Format_ARGB32); - - case CV_8UC3: - return QImage(imageMatrix.data, imageMatrix.cols, imageMatrix.rows, static_cast<int>(imageMatrix.step), QImage::Format_RGB888).rgbSwapped(); - - case CV_8UC1: - return QImage(imageMatrix.data, imageMatrix.cols, imageMatrix.rows, static_cast<int>(imageMatrix.step), QImage::Format_Grayscale8); - - default: - throw ImageException{_EXCPT("Unsupported matrix format for conversion to an image")}; - } -} - -QPixmap CVUtils::matrixToPixmap(const cv::Mat& matrix) -{ - return QPixmap::fromImage(matrixToImage(matrix)); -} - -QColor CVUtils::colorToGrayscale(QColor color) -{ - auto p = static_cast<int>(std::lround((color.red() + color.green() + color.blue()) / 3.0f)); - return QColor{p, p, p}; -} - -bool CVUtils::compareColors(QColor clr1, QColor clr2, float tolerance, bool perceivedDifference) -{ - if (tolerance >= 1.0f) - { - return true; - } - else if (!clr1.isValid() || !clr2.isValid()) - { - return false; - } - else if (tolerance > 0.0f) - { - if (perceivedDifference) - { - // Based on https://www.compuphase.com/cmetric.htm - auto rmean = (clr1.red() + clr2.red() ) / 2; - auto r = clr1.red() - clr2.red(); - auto g = clr1.green() - clr2.green(); - auto b = clr1.blue()- clr2.blue(); - auto delta = std::sqrt((((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8)); - auto rate = static_cast<float>(delta / 650.0f); - - return rate <= tolerance; - } - else - { - // Euklidean distance - float sum = 0; - - auto diff = clr1.red() - clr2.red(); - sum += diff * diff; - - diff = clr1.green() - clr2.green(); - sum += diff * diff; - - diff = clr1.blue() - clr2.blue(); - sum += diff * diff; - - return static_cast<float>((sum / 3.0f) / 255.0f) <= tolerance; - } - } - else - return clr1 == clr2; -} - -std::vector<QColor> CVUtils::generateColors(unsigned int count, float saturation, float value) -{ - std::vector<QColor> colors; - float colorStep = 1.0f / count; - - for (unsigned int i = 0; i < count; ++i) - { - QColor color; - color.setHsvF(i * colorStep, saturation, value); - colors.push_back(color); - } - - return colors; -} - -QColor CVUtils::mixColors(QColor clr1, QColor clr2, float alpha) -{ - auto red = (1 - alpha) * clr1.redF() + alpha * clr2.redF(); - auto green = (1 - alpha) * clr1.greenF() + alpha * clr2.greenF(); - auto blue = (1 - alpha) * clr1.blueF() + alpha * clr2.blueF(); - - QColor color; - color.setRgbF(red, green, blue); - return color; -} +/****************************************************************************** + * File: CVUtils.cpp + * Date: 19.2.2018 + *****************************************************************************/ + +#include "Grinder.h" +#include "CVUtils.h" +#include "image/ImageExceptions.h" + +QImage CVUtils::matrixToImage(const cv::Mat& matrix) +{ + cv::Mat imageMatrix = matrix; + + // Check if the matrix has an unsupported type; if so, convert it first + if (matrix.type() != CV_8UC4 && matrix.type() != CV_8UC3 && matrix.type() != CV_8UC1) + { + matrix.convertTo(imageMatrix, CV_8U); + cv::normalize(imageMatrix, imageMatrix, std::numeric_limits<unsigned char>::max(), std::numeric_limits<unsigned char>::min(), cv::NORM_MINMAX); + } + + switch (imageMatrix.type()) + { + case CV_8UC4: + return QImage(imageMatrix.data, imageMatrix.cols, imageMatrix.rows, static_cast<int>(imageMatrix.step), QImage::Format_ARGB32); + + case CV_8UC3: + return QImage(imageMatrix.data, imageMatrix.cols, imageMatrix.rows, static_cast<int>(imageMatrix.step), QImage::Format_RGB888).rgbSwapped(); + + case CV_8UC1: + return QImage(imageMatrix.data, imageMatrix.cols, imageMatrix.rows, static_cast<int>(imageMatrix.step), QImage::Format_Grayscale8); + + default: + throw ImageException{_EXCPT("Unsupported matrix format for conversion to an image")}; + } +} + +QPixmap CVUtils::matrixToPixmap(const cv::Mat& matrix) +{ + return QPixmap::fromImage(matrixToImage(matrix)); +} + +QColor CVUtils::colorToGrayscale(QColor color) +{ + auto p = static_cast<int>(std::lround((color.red() + color.green() + color.blue()) / 3.0f)); + return QColor{p, p, p}; +} + +bool CVUtils::compareColors(QColor clr1, QColor clr2, float tolerance, bool perceivedDifference) +{ + if (tolerance >= 1.0f) + { + return true; + } + else if (!clr1.isValid() || !clr2.isValid()) + { + return false; + } + else if (tolerance > 0.0f) + { + if (perceivedDifference) + { + // Based on https://www.compuphase.com/cmetric.htm + auto rmean = (clr1.red() + clr2.red() ) / 2; + auto r = clr1.red() - clr2.red(); + auto g = clr1.green() - clr2.green(); + auto b = clr1.blue()- clr2.blue(); + auto delta = std::sqrt((((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8)); + auto rate = static_cast<float>(delta / 650.0f); + + return rate <= tolerance; + } + else + { + // Euklidean distance + float sum = 0; + + auto diff = clr1.red() - clr2.red(); + sum += diff * diff; + + diff = clr1.green() - clr2.green(); + sum += diff * diff; + + diff = clr1.blue() - clr2.blue(); + sum += diff * diff; + + return static_cast<float>((sum / 3.0f) / 255.0f) <= tolerance; + } + } + else + return clr1 == clr2; +} + +bool CVUtils::compareColorsNoAlpha(QColor clr1, QColor clr2) +{ + return clr1.red() == clr2.red() && clr1.green() == clr2.green() && clr1.blue() == clr2.blue(); +} + +std::vector<QColor> CVUtils::generateColors(unsigned int count, float saturation, float value) +{ + std::vector<QColor> colors; + float colorStep = 1.0f / count; + + for (unsigned int i = 0; i < count; ++i) + { + QColor color; + color.setHsvF(i * colorStep, saturation, value); + colors.push_back(color.toRgb()); + } + + return colors; +} + +QColor CVUtils::mixColors(QColor clr1, QColor clr2, float alpha) +{ + auto red = (1 - alpha) * clr1.redF() + alpha * clr2.redF(); + auto green = (1 - alpha) * clr1.greenF() + alpha * clr2.greenF(); + auto blue = (1 - alpha) * clr1.blueF() + alpha * clr2.blueF(); + + QColor color; + color.setRgbF(red, green, blue); + return color; +} diff --git a/Grinder/cv/CVUtils.h b/Grinder/cv/CVUtils.h index 5e0f2e20b3e28e3420367ccbd61f12d71ba8296f..f780bf61f9a1485ba3be75993db36e8f0ad399bf 100644 --- a/Grinder/cv/CVUtils.h +++ b/Grinder/cv/CVUtils.h @@ -1,39 +1,40 @@ -/****************************************************************************** - * File: CVUtils.h - * Date: 19.2.2018 - *****************************************************************************/ - -#ifndef CVUTILS_H -#define CVUTILS_H - -#include <QString> -#include <QImage> -#include <functional> -#include <opencv2/core.hpp> - -namespace grndr -{ - class CVUtils final - { - public: - static QImage matrixToImage(const cv::Mat& matrix); - static QPixmap matrixToPixmap(const cv::Mat& matrix); - - template<typename DataType> - static QString matrixToString(const cv::Mat& matrix); - 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, bool perceivedDifference = true); - static std::vector<QColor> generateColors(unsigned int count, float saturation = 1.0, float value = 1.0); - static QColor mixColors(QColor clr1, QColor clr2, float alpha); - - private: - CVUtils() { } - }; -} - -#include "CVUtils.impl.h" - -#endif +/****************************************************************************** + * File: CVUtils.h + * Date: 19.2.2018 + *****************************************************************************/ + +#ifndef CVUTILS_H +#define CVUTILS_H + +#include <QString> +#include <QImage> +#include <functional> +#include <opencv2/core.hpp> + +namespace grndr +{ + class CVUtils final + { + public: + static QImage matrixToImage(const cv::Mat& matrix); + static QPixmap matrixToPixmap(const cv::Mat& matrix); + + template<typename DataType> + static QString matrixToString(const cv::Mat& matrix); + 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, bool perceivedDifference = true); + static bool compareColorsNoAlpha(QColor clr1, QColor clr2); + static std::vector<QColor> generateColors(unsigned int count, float saturation = 1.0, float value = 1.0); + static QColor mixColors(QColor clr1, QColor clr2, float alpha); + + private: + CVUtils() { } + }; +} + +#include "CVUtils.impl.h" + +#endif diff --git a/Grinder/image/ImageTagsBitmap.cpp b/Grinder/image/ImageTagsBitmap.cpp index 813b0acf0346a0650486886c48dc3cb6916a8b23..509a330f8af43fc7643bf944ea4f5764add4c449 100644 --- a/Grinder/image/ImageTagsBitmap.cpp +++ b/Grinder/image/ImageTagsBitmap.cpp @@ -8,6 +8,7 @@ #include "ImageTags.h" #include "ImageExceptions.h" #include "LayerPixels.h" +#include "cv/CVUtils.h" ImageTagsBitmap::ImageTagsBitmap(const ImageTags* imageTags, QSize bitmapSize) : _imageTags{imageTags}, _imageTagsBitmap{bitmapSize} @@ -39,7 +40,7 @@ void ImageTagsBitmap::renderLayerPixels(const LayerPixels& layerPixels, const Im { QRgb rgb = pixelsData[c]; - if (rgb != 0 && (assignToAll || QColor{qRed(rgb), qGreen(rgb), qBlue(rgb)} == imageTag->getColor())) + if (rgb != 0 && (assignToAll || CVUtils::compareColorsNoAlpha(QColor{qRed(rgb), qGreen(rgb), qBlue(rgb)}, imageTag->getColor()))) canvasData[c] = rgb; } }