mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
ImageToLED - Add reduced pixel processing, make dominant color advanced configurable
This commit is contained in:
parent
9b25b76723
commit
1063eadec5
@ -251,6 +251,8 @@
|
|||||||
"edt_conf_bb_unknownFrameCnt_title": "Unknown frames",
|
"edt_conf_bb_unknownFrameCnt_title": "Unknown frames",
|
||||||
"edt_conf_bge_heading_title": "Background Effect/Color",
|
"edt_conf_bge_heading_title": "Background Effect/Color",
|
||||||
"edt_conf_bobls_heading_title": "Boblight Server",
|
"edt_conf_bobls_heading_title": "Boblight Server",
|
||||||
|
"edt_conf_color_accuracyLevel_expl": "Level how accurate dominat colors are evaluated. A higher level creates more accurate results, but also requries more processing power. Should to be combined with reduced pixel processing.",
|
||||||
|
"edt_conf_color_accuracyLevel_title": "Accuracy level",
|
||||||
"edt_conf_color_backlightColored_expl": "Add some color to your backlight.",
|
"edt_conf_color_backlightColored_expl": "Add some color to your backlight.",
|
||||||
"edt_conf_color_backlightColored_title": "Colored backlight",
|
"edt_conf_color_backlightColored_title": "Colored backlight",
|
||||||
"edt_conf_color_backlightThreshold_expl": "The minimum amount of brightness (backlight). Disabled during effects, colors and in status \"Off\"",
|
"edt_conf_color_backlightThreshold_expl": "The minimum amount of brightness (backlight). Disabled during effects, colors and in status \"Off\"",
|
||||||
@ -291,6 +293,8 @@
|
|||||||
"edt_conf_color_saturationGain_title": "Saturation gain",
|
"edt_conf_color_saturationGain_title": "Saturation gain",
|
||||||
"edt_conf_color_brightnessGain_expl": "Adjusts the brightness of colors. 1.0 means no change, over 1.0 increases brightness, under 1.0 decreases brightness.",
|
"edt_conf_color_brightnessGain_expl": "Adjusts the brightness of colors. 1.0 means no change, over 1.0 increases brightness, under 1.0 decreases brightness.",
|
||||||
"edt_conf_color_brightnessGain_title": "Brightness gain",
|
"edt_conf_color_brightnessGain_title": "Brightness gain",
|
||||||
|
"edt_conf_color_reducedPixelSetFactorFactor_expl": "Evaluate only a set of pixels per LED area defined, Low ~25%, Medium ~10%, High ~6%",
|
||||||
|
"edt_conf_color_reducedPixelSetFactorFactor_title": "Reduced pixel processing",
|
||||||
"edt_conf_color_white_expl": "The calibrated white value.",
|
"edt_conf_color_white_expl": "The calibrated white value.",
|
||||||
"edt_conf_color_white_title": "White",
|
"edt_conf_color_white_title": "White",
|
||||||
"edt_conf_color_yellow_expl": "The calibrated yellow value.",
|
"edt_conf_color_yellow_expl": "The calibrated yellow value.",
|
||||||
@ -321,6 +325,7 @@
|
|||||||
"edt_conf_enum_custom": "Custom",
|
"edt_conf_enum_custom": "Custom",
|
||||||
"edt_conf_enum_decay": "Decay",
|
"edt_conf_enum_decay": "Decay",
|
||||||
"edt_conf_enum_delay": "Delay only",
|
"edt_conf_enum_delay": "Delay only",
|
||||||
|
"edt_conf_enum_disabled": "Disabled",
|
||||||
"edt_conf_enum_dl_error": "Error",
|
"edt_conf_enum_dl_error": "Error",
|
||||||
"edt_conf_enum_dl_informational": "Informational",
|
"edt_conf_enum_dl_informational": "Informational",
|
||||||
"edt_conf_enum_dl_nodebug": "No Debug output",
|
"edt_conf_enum_dl_nodebug": "No Debug output",
|
||||||
@ -334,6 +339,7 @@
|
|||||||
"edt_conf_enum_effect": "Effect",
|
"edt_conf_enum_effect": "Effect",
|
||||||
"edt_conf_enum_gbr": "GBR",
|
"edt_conf_enum_gbr": "GBR",
|
||||||
"edt_conf_enum_grb": "GRB",
|
"edt_conf_enum_grb": "GRB",
|
||||||
|
"edt_conf_enum_high": "High",
|
||||||
"edt_conf_enum_hsv": "HSV",
|
"edt_conf_enum_hsv": "HSV",
|
||||||
"edt_conf_enum_left_right": "Left to right",
|
"edt_conf_enum_left_right": "Left to right",
|
||||||
"edt_conf_enum_linear": "Linear",
|
"edt_conf_enum_linear": "Linear",
|
||||||
@ -341,6 +347,8 @@
|
|||||||
"edt_conf_enum_logsilent": "Silent",
|
"edt_conf_enum_logsilent": "Silent",
|
||||||
"edt_conf_enum_logverbose": "Verbose",
|
"edt_conf_enum_logverbose": "Verbose",
|
||||||
"edt_conf_enum_logwarn": "Warning",
|
"edt_conf_enum_logwarn": "Warning",
|
||||||
|
"edt_conf_enum_low": "Low",
|
||||||
|
"edt_conf_enum_medium": "Medium",
|
||||||
"edt_conf_enum_multicolor_mean": "Mean Color Simple - per LED",
|
"edt_conf_enum_multicolor_mean": "Mean Color Simple - per LED",
|
||||||
"edt_conf_enum_multicolor_mean_squared": "Mean Color Squared - per LED",
|
"edt_conf_enum_multicolor_mean_squared": "Mean Color Squared - per LED",
|
||||||
"edt_conf_enum_please_select": "Please Select",
|
"edt_conf_enum_please_select": "Please Select",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
// Utils includes
|
// Utils includes
|
||||||
#include <utils/Image.h>
|
#include <utils/Image.h>
|
||||||
@ -46,7 +47,7 @@ public:
|
|||||||
/// @param[in] width The new width of the buffer-image
|
/// @param[in] width The new width of the buffer-image
|
||||||
/// @param[in] height The new height of the buffer-image
|
/// @param[in] height The new height of the buffer-image
|
||||||
///
|
///
|
||||||
void setSize(unsigned width, unsigned height);
|
void setSize(int width, int height);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// @brief Update the led string (eg on settings change)
|
/// @brief Update the led string (eg on settings change)
|
||||||
@ -56,6 +57,19 @@ public:
|
|||||||
/// Returns state of black border detector
|
/// Returns state of black border detector
|
||||||
bool blackBorderDetectorEnabled() const;
|
bool blackBorderDetectorEnabled() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Factor to reduce the number of pixels evaluated during processing
|
||||||
|
///
|
||||||
|
/// @param[in] count Use every "count" pixel
|
||||||
|
void setReducedPixelSetFactorFactor(int count);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Set the accuracy used during processing
|
||||||
|
/// (only for selected types)
|
||||||
|
///
|
||||||
|
/// @param[in] level The accuracy level (0-4)
|
||||||
|
void setAccuracyLevel(int level);
|
||||||
|
|
||||||
/// Returns the current _userMappingType, this may not be the current applied type!
|
/// Returns the current _userMappingType, this may not be the current applied type!
|
||||||
int getUserLedMappingType() const { return _userMappingType; }
|
int getUserLedMappingType() const { return _userMappingType; }
|
||||||
|
|
||||||
@ -109,11 +123,14 @@ public:
|
|||||||
std::vector<ColorRgb> process(const Image<Pixel_T>& image)
|
std::vector<ColorRgb> process(const Image<Pixel_T>& image)
|
||||||
{
|
{
|
||||||
std::vector<ColorRgb> colors;
|
std::vector<ColorRgb> colors;
|
||||||
|
|
||||||
if (image.width()>0 && image.height()>0)
|
if (image.width()>0 && image.height()>0)
|
||||||
{
|
{
|
||||||
// Ensure that the buffer-image is the proper size
|
// Ensure that the buffer-image is the proper size
|
||||||
setSize(image);
|
setSize(image);
|
||||||
|
|
||||||
|
assert(!_imageToLedColors.isNull());
|
||||||
|
|
||||||
// Check black border detection
|
// Check black border detection
|
||||||
verifyBorder(image);
|
verifyBorder(image);
|
||||||
|
|
||||||
@ -121,19 +138,19 @@ public:
|
|||||||
switch (_mappingType)
|
switch (_mappingType)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
colors = _imageToLeds->getUniLedColor(image);
|
colors = _imageToLedColors->getUniLedColor(image);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
colors = _imageToLeds->getMeanLedColorSqrt(image);
|
colors = _imageToLedColors->getMeanLedColorSqrt(image);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
colors = _imageToLeds->getDominantLedColor(image);
|
colors = _imageToLedColors->getDominantLedColor(image);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
colors = _imageToLeds->getDominantLedColorAdv(image);
|
colors = _imageToLedColors->getDominantLedColorAdv(image);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
colors = _imageToLeds->getMeanLedColor(image);
|
colors = _imageToLedColors->getMeanLedColor(image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -166,19 +183,19 @@ public:
|
|||||||
switch (_mappingType)
|
switch (_mappingType)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
_imageToLeds->getUniLedColor(image, ledColors);
|
_imageToLedColors->getUniLedColor(image, ledColors);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
_imageToLeds->getMeanLedColorSqrt(image, ledColors);
|
_imageToLedColors->getMeanLedColorSqrt(image, ledColors);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
_imageToLeds->getDominantLedColor(image, ledColors);
|
_imageToLedColors->getDominantLedColor(image, ledColors);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
_imageToLeds->getDominantLedColorAdv(image, ledColors);
|
_imageToLedColors->getDominantLedColorAdv(image, ledColors);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_imageToLeds->getMeanLedColor(image, ledColors);
|
_imageToLedColors->getMeanLedColor(image, ledColors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -199,6 +216,13 @@ public:
|
|||||||
bool getScanParameters(size_t led, double & hscanBegin, double & hscanEnd, double & vscanBegin, double & vscanEnd) const;
|
bool getScanParameters(size_t led, double & hscanBegin, double & hscanEnd, double & vscanBegin, double & vscanEnd) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void registerProcessingUnit(
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int horizontalBorder,
|
||||||
|
int verticalBorder);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Performs black-border detection (if enabled) on the given image
|
/// Performs black-border detection (if enabled) on the given image
|
||||||
///
|
///
|
||||||
@ -207,30 +231,24 @@ private:
|
|||||||
template <typename Pixel_T>
|
template <typename Pixel_T>
|
||||||
void verifyBorder(const Image<Pixel_T> & image)
|
void verifyBorder(const Image<Pixel_T> & image)
|
||||||
{
|
{
|
||||||
if (!_borderProcessor->enabled() && ( _imageToLeds->horizontalBorder()!=0 || _imageToLeds->verticalBorder()!=0 ))
|
if (!_borderProcessor->enabled() && ( _imageToLedColors->horizontalBorder()!=0 || _imageToLedColors->verticalBorder()!=0 ))
|
||||||
{
|
{
|
||||||
Debug(_log, "Reset border");
|
Debug(_log, "Reset border");
|
||||||
_borderProcessor->process(image);
|
_borderProcessor->process(image);
|
||||||
delete _imageToLeds;
|
registerProcessingUnit(image.width(), image.height(), 0, 0);
|
||||||
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), 0, 0, _ledString.leds());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_borderProcessor->enabled() && _borderProcessor->process(image))
|
if(_borderProcessor->enabled() && _borderProcessor->process(image))
|
||||||
{
|
{
|
||||||
const hyperion::BlackBorder border = _borderProcessor->getCurrentBorder();
|
const hyperion::BlackBorder border = _borderProcessor->getCurrentBorder();
|
||||||
|
|
||||||
// Clean up the old mapping
|
|
||||||
delete _imageToLeds;
|
|
||||||
|
|
||||||
if (border.unknown)
|
if (border.unknown)
|
||||||
{
|
{
|
||||||
// Construct a new buffer and mapping
|
registerProcessingUnit(image.width(), image.height(), 0, 0);
|
||||||
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), 0, 0, _ledString.leds());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Construct a new buffer and mapping
|
registerProcessingUnit(image.width(), image.height(), border.horizontalSize, border.verticalSize);
|
||||||
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), border.horizontalSize, border.verticalSize, _ledString.leds());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -239,6 +257,7 @@ private slots:
|
|||||||
void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
|
void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Logger * _log;
|
Logger * _log;
|
||||||
/// The Led-string specification
|
/// The Led-string specification
|
||||||
LedString _ledString;
|
LedString _ledString;
|
||||||
@ -247,7 +266,7 @@ private:
|
|||||||
hyperion::BlackBorderProcessor * _borderProcessor;
|
hyperion::BlackBorderProcessor * _borderProcessor;
|
||||||
|
|
||||||
/// The mapping of image-pixels to LEDs
|
/// The mapping of image-pixels to LEDs
|
||||||
hyperion::ImageToLedsMap* _imageToLeds;
|
QSharedPointer<hyperion::ImageToLedsMap> _imageToLedColors;
|
||||||
|
|
||||||
/// Type of image to LED mapping
|
/// Type of image to LED mapping
|
||||||
int _mappingType;
|
int _mappingType;
|
||||||
@ -256,6 +275,9 @@ private:
|
|||||||
/// Type of last requested hard type
|
/// Type of last requested hard type
|
||||||
int _hardMappingType;
|
int _hardMappingType;
|
||||||
|
|
||||||
|
int _accuraryLevel;
|
||||||
|
int _reducedPixelSetFactorFactor;
|
||||||
|
|
||||||
/// Hyperion instance pointer
|
/// Hyperion instance pointer
|
||||||
Hyperion* _hyperion;
|
Hyperion* _hyperion;
|
||||||
};
|
};
|
||||||
|
@ -17,15 +17,14 @@
|
|||||||
|
|
||||||
namespace hyperion
|
namespace hyperion
|
||||||
{
|
{
|
||||||
/// Number of clusters for k-means calculation
|
|
||||||
const int CLUSTER_COUNT {5};
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// The ImageToLedsMap holds a mapping of indices into an image to LEDs. It can be used to
|
/// The ImageToLedsMap holds a mapping of indices into an image to LEDs. It can be used to
|
||||||
/// calculate the average (aka mean) or dominant color per LED for a given region.
|
/// calculate the average (aka mean) or dominant color per LED for a given region.
|
||||||
///
|
///
|
||||||
class ImageToLedsMap
|
class ImageToLedsMap : public QObject
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -35,17 +34,26 @@ namespace hyperion
|
|||||||
/// The mapping is created purely on size (width and height). The given borders are excluded
|
/// The mapping is created purely on size (width and height). The given borders are excluded
|
||||||
/// from indexing.
|
/// from indexing.
|
||||||
///
|
///
|
||||||
|
/// @param[in] log Logger
|
||||||
|
/// @param[in] mappingType Type of the mapping algorithm
|
||||||
/// @param[in] width The width of the indexed image
|
/// @param[in] width The width of the indexed image
|
||||||
/// @param[in] height The width of the indexed image
|
/// @param[in] height The width of the indexed image
|
||||||
/// @param[in] horizontalBorder The size of the horizontal border (0=no border)
|
/// @param[in] horizontalBorder The size of the horizontal border (0=no border)
|
||||||
/// @param[in] verticalBorder The size of the vertical border (0=no border)
|
/// @param[in] verticalBorder The size of the vertical border (0=no border)
|
||||||
/// @param[in] leds The list with led specifications
|
/// @param[in] leds The list with led specifications
|
||||||
|
/// @param[in] reducedProcessingFactor Factor to reduce the number of pixels evaluated during processing
|
||||||
|
/// @param[in] accuraryLevel The accuracy used during processing (only for selected types)
|
||||||
///
|
///
|
||||||
ImageToLedsMap(int width,
|
ImageToLedsMap(
|
||||||
|
Logger* log,
|
||||||
|
int mappingType,
|
||||||
|
int width,
|
||||||
int height,
|
int height,
|
||||||
int horizontalBorder,
|
int horizontalBorder,
|
||||||
int verticalBorder,
|
int verticalBorder,
|
||||||
const std::vector<Led> & leds);
|
const std::vector<Led> & leds,
|
||||||
|
int reducedProcessingFactor = 0,
|
||||||
|
int accuraryLevel = 0);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns the width of the indexed image
|
/// Returns the width of the indexed image
|
||||||
@ -64,6 +72,13 @@ namespace hyperion
|
|||||||
int horizontalBorder() const { return _horizontalBorder; }
|
int horizontalBorder() const { return _horizontalBorder; }
|
||||||
int verticalBorder() const { return _verticalBorder; }
|
int verticalBorder() const { return _verticalBorder; }
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Set the accuracy used during processing
|
||||||
|
/// (only for selected types)
|
||||||
|
///
|
||||||
|
/// @param[in] level The accuracy level (0-4)
|
||||||
|
void setAccuracyLevel (int level);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Determines the mean color for each LED using the LED area mapping given
|
/// Determines the mean color for each LED using the LED area mapping given
|
||||||
/// at construction.
|
/// at construction.
|
||||||
@ -92,7 +107,7 @@ namespace hyperion
|
|||||||
{
|
{
|
||||||
if(_colorsMap.size() != ledColors.size())
|
if(_colorsMap.size() != ledColors.size())
|
||||||
{
|
{
|
||||||
Debug(Logger::getInstance("HYPERION"), "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +148,7 @@ namespace hyperion
|
|||||||
{
|
{
|
||||||
if(_colorsMap.size() != ledColors.size())
|
if(_colorsMap.size() != ledColors.size())
|
||||||
{
|
{
|
||||||
Debug(Logger::getInstance("HYPERION"), "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +187,7 @@ namespace hyperion
|
|||||||
{
|
{
|
||||||
if(_colorsMap.size() != ledColors.size())
|
if(_colorsMap.size() != ledColors.size())
|
||||||
{
|
{
|
||||||
Debug(Logger::getInstance("HYPERION"), "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +226,7 @@ namespace hyperion
|
|||||||
// Sanity check for the number of LEDs
|
// Sanity check for the number of LEDs
|
||||||
if(_colorsMap.size() != ledColors.size())
|
if(_colorsMap.size() != ledColors.size())
|
||||||
{
|
{
|
||||||
Debug(Logger::getInstance("HYPERION"), "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +268,7 @@ namespace hyperion
|
|||||||
// Sanity check for the number of LEDs
|
// Sanity check for the number of LEDs
|
||||||
if(_colorsMap.size() != ledColors.size())
|
if(_colorsMap.size() != ledColors.size())
|
||||||
{
|
{
|
||||||
Debug(Logger::getInstance("HYPERION"), "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,6 +282,11 @@ namespace hyperion
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
Logger* _log;
|
||||||
|
|
||||||
|
int _mappingType;
|
||||||
|
|
||||||
/// The width of the indexed image
|
/// The width of the indexed image
|
||||||
const int _width;
|
const int _width;
|
||||||
/// The height of the indexed image
|
/// The height of the indexed image
|
||||||
@ -275,6 +295,12 @@ namespace hyperion
|
|||||||
const int _horizontalBorder;
|
const int _horizontalBorder;
|
||||||
const int _verticalBorder;
|
const int _verticalBorder;
|
||||||
|
|
||||||
|
/// Evaluate every "count" pixel
|
||||||
|
int _nextPixelCount;
|
||||||
|
|
||||||
|
/// Number of clusters used during dominant color advanced processing (k-means)
|
||||||
|
int _clusterCount;
|
||||||
|
|
||||||
/// The absolute indices into the image for each led
|
/// The absolute indices into the image for each led
|
||||||
std::vector<std::vector<int>> _colorsMap;
|
std::vector<std::vector<int>> _colorsMap;
|
||||||
|
|
||||||
@ -496,12 +522,21 @@ namespace hyperion
|
|||||||
struct ColorCluster {
|
struct ColorCluster {
|
||||||
|
|
||||||
ColorCluster():count(0) {}
|
ColorCluster():count(0) {}
|
||||||
|
ColorCluster(Pixel_T color):count(0),color(color) {}
|
||||||
|
|
||||||
Pixel_T color;
|
Pixel_T color;
|
||||||
Pixel_T newColor;
|
Pixel_T newColor;
|
||||||
int count;
|
int count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ColorRgb DEFAULT_CLUSTER_COLORS[5] {
|
||||||
|
{ColorRgb::BLACK},
|
||||||
|
{ColorRgb::GREEN},
|
||||||
|
{ColorRgb::WHITE},
|
||||||
|
{ColorRgb::RED},
|
||||||
|
{ColorRgb::YELLOW}
|
||||||
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Calculates the 'dominant color' of an image area defined by a list of pixel indices
|
/// Calculates the 'dominant color' of an image area defined by a list of pixel indices
|
||||||
/// using a k-means algorithm (https://robocraft.ru/computervision/1063)
|
/// using a k-means algorithm (https://robocraft.ru/computervision/1063)
|
||||||
@ -519,31 +554,11 @@ namespace hyperion
|
|||||||
const auto pixelNum = pixels.size();
|
const auto pixelNum = pixels.size();
|
||||||
if (pixelNum > 0)
|
if (pixelNum > 0)
|
||||||
{
|
{
|
||||||
ColorCluster<ColorRgbScalar> clusters[CLUSTER_COUNT];
|
// initial cluster with different colors
|
||||||
|
ColorCluster<ColorRgbScalar> clusters[_clusterCount];
|
||||||
// initial cluster colors
|
for(int k = 0; k < _clusterCount; ++k)
|
||||||
switch (CLUSTER_COUNT) {
|
{
|
||||||
case 5:
|
clusters[k].newColor = DEFAULT_CLUSTER_COLORS[k];
|
||||||
clusters[4].newColor = ColorRgbScalar(ColorRgb::YELLOW);
|
|
||||||
case 4:
|
|
||||||
clusters[3].newColor = ColorRgbScalar(ColorRgb::RED);
|
|
||||||
case 3:
|
|
||||||
clusters[2].newColor = ColorRgbScalar(ColorRgb::WHITE);
|
|
||||||
case 2:
|
|
||||||
clusters[1].newColor = ColorRgbScalar(ColorRgb::GREEN);
|
|
||||||
case 1:
|
|
||||||
clusters[0].newColor = ColorRgbScalar(ColorRgb::BLACK);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
for(int k = 0; k < CLUSTER_COUNT; ++k)
|
|
||||||
{
|
|
||||||
int randomRed = rand() % static_cast<int>(256);
|
|
||||||
int randomGreen = rand() % static_cast<int>(256);
|
|
||||||
int randomBlue = rand() % static_cast<int>(256);
|
|
||||||
|
|
||||||
clusters[k].newColor = ColorRgbScalar(randomRed, randomGreen, randomBlue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// k-means
|
// k-means
|
||||||
@ -552,7 +567,7 @@ namespace hyperion
|
|||||||
|
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
for(int k = 0; k < CLUSTER_COUNT; ++k)
|
for(int k = 0; k < _clusterCount; ++k)
|
||||||
{
|
{
|
||||||
clusters[k].count = 0;
|
clusters[k].count = 0;
|
||||||
clusters[k].color = clusters[k].newColor;
|
clusters[k].color = clusters[k].newColor;
|
||||||
@ -566,7 +581,7 @@ namespace hyperion
|
|||||||
|
|
||||||
min_rgb_euclidean = 255 * 255 * 255;
|
min_rgb_euclidean = 255 * 255 * 255;
|
||||||
int clusterIndex = -1;
|
int clusterIndex = -1;
|
||||||
for(int k = 0; k < CLUSTER_COUNT; ++k)
|
for(int k = 0; k < _clusterCount; ++k)
|
||||||
{
|
{
|
||||||
double euclid = ColorSys::rgb_euclidean(ColorRgbScalar(pixel), clusters[k].color);
|
double euclid = ColorSys::rgb_euclidean(ColorRgbScalar(pixel), clusters[k].color);
|
||||||
|
|
||||||
@ -581,7 +596,7 @@ namespace hyperion
|
|||||||
}
|
}
|
||||||
|
|
||||||
min_rgb_euclidean = 0;
|
min_rgb_euclidean = 0;
|
||||||
for(int k = 0; k < CLUSTER_COUNT; ++k)
|
for(int k = 0; k < _clusterCount; ++k)
|
||||||
{
|
{
|
||||||
if (clusters[k].count > 0)
|
if (clusters[k].count > 0)
|
||||||
{
|
{
|
||||||
@ -606,7 +621,7 @@ namespace hyperion
|
|||||||
int colorsFoundMax = 0;
|
int colorsFoundMax = 0;
|
||||||
int dominantClusterIdx {0};
|
int dominantClusterIdx {0};
|
||||||
|
|
||||||
for(int clusterIdx=0; clusterIdx < CLUSTER_COUNT; ++clusterIdx){
|
for(int clusterIdx=0; clusterIdx < _clusterCount; ++clusterIdx){
|
||||||
int colorsFoundinCluster = clusters[clusterIdx].count;
|
int colorsFoundinCluster = clusters[clusterIdx].count;
|
||||||
if (colorsFoundinCluster > colorsFoundMax) {
|
if (colorsFoundinCluster > colorsFoundMax) {
|
||||||
colorsFoundMax = colorsFoundinCluster;
|
colorsFoundMax = colorsFoundinCluster;
|
||||||
|
@ -4,13 +4,40 @@
|
|||||||
#include <hyperion/ImageProcessor.h>
|
#include <hyperion/ImageProcessor.h>
|
||||||
#include <hyperion/ImageToLedsMap.h>
|
#include <hyperion/ImageToLedsMap.h>
|
||||||
|
|
||||||
// Blacborder includes
|
// Blackborder includes
|
||||||
#include <blackborder/BlackBorderProcessor.h>
|
#include <blackborder/BlackBorderProcessor.h>
|
||||||
|
|
||||||
|
#include <QSharedPointer>
|
||||||
#include <QRgb>
|
#include <QRgb>
|
||||||
|
|
||||||
using namespace hyperion;
|
using namespace hyperion;
|
||||||
|
|
||||||
|
void ImageProcessor::registerProcessingUnit(
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int horizontalBorder,
|
||||||
|
int verticalBorder)
|
||||||
|
{
|
||||||
|
if (width > 0 && height > 0)
|
||||||
|
{
|
||||||
|
_imageToLedColors = QSharedPointer<ImageToLedsMap>(new ImageToLedsMap(
|
||||||
|
_log,
|
||||||
|
_mappingType,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
horizontalBorder,
|
||||||
|
verticalBorder,
|
||||||
|
_ledString.leds(),
|
||||||
|
_reducedPixelSetFactorFactor,
|
||||||
|
_accuraryLevel
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_imageToLedColors = QSharedPointer<ImageToLedsMap>(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// global transform method
|
// global transform method
|
||||||
int ImageProcessor::mappingTypeToInt(const QString& mappingType)
|
int ImageProcessor::mappingTypeToInt(const QString& mappingType)
|
||||||
{
|
{
|
||||||
@ -62,10 +89,12 @@ ImageProcessor::ImageProcessor(const LedString& ledString, Hyperion* hyperion)
|
|||||||
, _log(nullptr)
|
, _log(nullptr)
|
||||||
, _ledString(ledString)
|
, _ledString(ledString)
|
||||||
, _borderProcessor(new BlackBorderProcessor(hyperion, this))
|
, _borderProcessor(new BlackBorderProcessor(hyperion, this))
|
||||||
, _imageToLeds(nullptr)
|
, _imageToLedColors(nullptr)
|
||||||
, _mappingType(0)
|
, _mappingType(0)
|
||||||
, _userMappingType(0)
|
, _userMappingType(0)
|
||||||
, _hardMappingType(0)
|
, _hardMappingType(-1)
|
||||||
|
, _accuraryLevel(0)
|
||||||
|
, _reducedPixelSetFactorFactor(1)
|
||||||
, _hyperion(hyperion)
|
, _hyperion(hyperion)
|
||||||
{
|
{
|
||||||
QString subComponent = hyperion->property("instance").toString();
|
QString subComponent = hyperion->property("instance").toString();
|
||||||
@ -79,7 +108,6 @@ ImageProcessor::ImageProcessor(const LedString& ledString, Hyperion* hyperion)
|
|||||||
|
|
||||||
ImageProcessor::~ImageProcessor()
|
ImageProcessor::~ImageProcessor()
|
||||||
{
|
{
|
||||||
delete _imageToLeds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageProcessor::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
void ImageProcessor::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||||
@ -92,39 +120,40 @@ void ImageProcessor::handleSettingsUpdate(settings::type type, const QJsonDocume
|
|||||||
{
|
{
|
||||||
setLedMappingType(newType);
|
setLedMappingType(newType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int reducedPixelSetFactorFactor = obj["reducedPixelSetFactorFactor"].toString().toInt();
|
||||||
|
setReducedPixelSetFactorFactor(reducedPixelSetFactorFactor);
|
||||||
|
|
||||||
|
int accuracyLevel = obj["accuracyLevel"].toInt();
|
||||||
|
setAccuracyLevel(accuracyLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageProcessor::setSize(unsigned width, unsigned height)
|
void ImageProcessor::setSize(int width, int height)
|
||||||
{
|
{
|
||||||
// Check if the existing buffer-image is already the correct dimensions
|
// Check if the existing buffer-image is already the correct dimensions
|
||||||
if (_imageToLeds && _imageToLeds->width() == width && _imageToLeds->height() == height)
|
if (!_imageToLedColors.isNull() && _imageToLedColors->width() == width && _imageToLedColors->height() == height)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the old buffer and mapping
|
|
||||||
delete _imageToLeds;
|
|
||||||
|
|
||||||
// Construct a new buffer and mapping
|
// Construct a new buffer and mapping
|
||||||
_imageToLeds = (width>0 && height>0) ? (new ImageToLedsMap(width, height, 0, 0, _ledString.leds())) : nullptr;
|
registerProcessingUnit(width, height, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageProcessor::setLedString(const LedString& ledString)
|
void ImageProcessor::setLedString(const LedString& ledString)
|
||||||
{
|
{
|
||||||
if ( _imageToLeds != nullptr)
|
Debug(_log,"");
|
||||||
|
if ( !_imageToLedColors.isNull() )
|
||||||
{
|
{
|
||||||
_ledString = ledString;
|
_ledString = ledString;
|
||||||
|
|
||||||
// get current width/height
|
// get current width/height
|
||||||
unsigned width = _imageToLeds->width();
|
int width = _imageToLedColors->width();
|
||||||
unsigned height = _imageToLeds->height();
|
int height = _imageToLedColors->height();
|
||||||
|
|
||||||
// Clean up the old buffer and mapping
|
|
||||||
delete _imageToLeds;
|
|
||||||
|
|
||||||
// Construct a new buffer and mapping
|
// Construct a new buffer and mapping
|
||||||
_imageToLeds = new ImageToLedsMap(width, height, 0, 0, _ledString.leds());
|
registerProcessingUnit(width, height, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,15 +167,55 @@ bool ImageProcessor::blackBorderDetectorEnabled() const
|
|||||||
return _borderProcessor->enabled();
|
return _borderProcessor->enabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageProcessor::setReducedPixelSetFactorFactor(int count)
|
||||||
|
{
|
||||||
|
int currentReducedPixelSetFactor= _reducedPixelSetFactorFactor;
|
||||||
|
|
||||||
|
_reducedPixelSetFactorFactor = count;
|
||||||
|
Debug(_log, "Set reduced pixel set factor to %d", _reducedPixelSetFactorFactor);
|
||||||
|
|
||||||
|
if (currentReducedPixelSetFactor != _reducedPixelSetFactorFactor && !_imageToLedColors.isNull())
|
||||||
|
{
|
||||||
|
int width = _imageToLedColors->width();
|
||||||
|
int height = _imageToLedColors->height();
|
||||||
|
|
||||||
|
// Construct a new buffer and mapping
|
||||||
|
registerProcessingUnit(width, height, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageProcessor::setAccuracyLevel(int level)
|
||||||
|
{
|
||||||
|
_accuraryLevel = level;
|
||||||
|
Debug(_log, "Set processing accuracy level to %d", _accuraryLevel);
|
||||||
|
|
||||||
|
if (!_imageToLedColors.isNull())
|
||||||
|
{
|
||||||
|
_imageToLedColors->setAccuracyLevel(_accuraryLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ImageProcessor::setLedMappingType(int mapType)
|
void ImageProcessor::setLedMappingType(int mapType)
|
||||||
{
|
{
|
||||||
|
int currentMappingType = _mappingType;
|
||||||
|
|
||||||
// if the _hardMappingType is >-1 we aren't allowed to overwrite it
|
// if the _hardMappingType is >-1 we aren't allowed to overwrite it
|
||||||
_userMappingType = mapType;
|
_userMappingType = mapType;
|
||||||
Debug(_log, "set user led mapping to %s", QSTRING_CSTR(mappingTypeToStr(mapType)));
|
|
||||||
|
Debug(_log, "Set user LED mapping to %s", QSTRING_CSTR(mappingTypeToStr(mapType)));
|
||||||
|
|
||||||
if(_hardMappingType == -1)
|
if(_hardMappingType == -1)
|
||||||
{
|
{
|
||||||
_mappingType = mapType;
|
_mappingType = mapType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentMappingType != _mappingType && !_imageToLedColors.isNull())
|
||||||
|
{
|
||||||
|
int width = _imageToLedColors->width();
|
||||||
|
int height = _imageToLedColors->height();
|
||||||
|
|
||||||
|
registerProcessingUnit(width, height, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageProcessor::setHardLedMappingType(int mapType)
|
void ImageProcessor::setHardLedMappingType(int mapType)
|
||||||
|
@ -3,17 +3,28 @@
|
|||||||
using namespace hyperion;
|
using namespace hyperion;
|
||||||
|
|
||||||
ImageToLedsMap::ImageToLedsMap(
|
ImageToLedsMap::ImageToLedsMap(
|
||||||
|
Logger* log,
|
||||||
|
int mappingType,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
int horizontalBorder,
|
int horizontalBorder,
|
||||||
int verticalBorder,
|
int verticalBorder,
|
||||||
const std::vector<Led>& leds)
|
const std::vector<Led>& leds,
|
||||||
: _width(width)
|
int reducedPixelSetFactor,
|
||||||
|
int accuracyLevel)
|
||||||
|
: _log(log)
|
||||||
|
, _mappingType(mappingType)
|
||||||
|
, _width(width)
|
||||||
, _height(height)
|
, _height(height)
|
||||||
, _horizontalBorder(horizontalBorder)
|
, _horizontalBorder(horizontalBorder)
|
||||||
, _verticalBorder(verticalBorder)
|
, _verticalBorder(verticalBorder)
|
||||||
|
, _nextPixelCount(reducedPixelSetFactor)
|
||||||
|
, _clusterCount()
|
||||||
, _colorsMap()
|
, _colorsMap()
|
||||||
{
|
{
|
||||||
|
_nextPixelCount = reducedPixelSetFactor + 1;
|
||||||
|
setAccuracyLevel(accuracyLevel);
|
||||||
|
|
||||||
// Sanity check of the size of the borders (and width and height)
|
// Sanity check of the size of the borders (and width and height)
|
||||||
Q_ASSERT(_width > 2*_verticalBorder);
|
Q_ASSERT(_width > 2*_verticalBorder);
|
||||||
Q_ASSERT(_height > 2*_horizontalBorder);
|
Q_ASSERT(_height > 2*_horizontalBorder);
|
||||||
@ -30,6 +41,7 @@ ImageToLedsMap::ImageToLedsMap(
|
|||||||
|
|
||||||
size_t totalCount = 0;
|
size_t totalCount = 0;
|
||||||
size_t totalCapacity = 0;
|
size_t totalCapacity = 0;
|
||||||
|
int ledCounter = 0;
|
||||||
|
|
||||||
for (const Led& led : leds)
|
for (const Led& led : leds)
|
||||||
{
|
{
|
||||||
@ -65,14 +77,27 @@ ImageToLedsMap::ImageToLedsMap(
|
|||||||
const int realYLedCount = qAbs(maxYLedCount - minY_idx);
|
const int realYLedCount = qAbs(maxYLedCount - minY_idx);
|
||||||
const int realXLedCount = qAbs(maxXLedCount - minX_idx);
|
const int realXLedCount = qAbs(maxXLedCount - minX_idx);
|
||||||
|
|
||||||
size_t totalSize = realYLedCount* realXLedCount;
|
bool skipPixelProcessing {false};
|
||||||
|
if (_nextPixelCount > 1)
|
||||||
|
{
|
||||||
|
skipPixelProcessing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t totalSize = static_cast<size_t>(realYLedCount * realXLedCount);
|
||||||
|
|
||||||
|
if (!skipPixelProcessing && totalSize > 1600)
|
||||||
|
{
|
||||||
|
skipPixelProcessing = true;
|
||||||
|
_nextPixelCount = 2;
|
||||||
|
Warning(_log, "Mapping LED/light [%d]. The current mapping area contains %d pixels which is huge. Therefore every %d pixels will be skipped. You can enable reduced processing to hide that warning.", ledCounter, totalSize, _nextPixelCount);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<int> ledColors;
|
std::vector<int> ledColors;
|
||||||
ledColors.reserve(totalSize);
|
ledColors.reserve(totalSize);
|
||||||
|
|
||||||
for (int y = minY_idx; y < maxYLedCount; ++y)
|
for (int y = minY_idx; y < maxYLedCount; y += _nextPixelCount)
|
||||||
{
|
{
|
||||||
for (int x = minX_idx; x < maxXLedCount; ++x)
|
for (int x = minX_idx; x < maxXLedCount; x += _nextPixelCount)
|
||||||
{
|
{
|
||||||
ledColors.push_back( y * width + x);
|
ledColors.push_back( y * width + x);
|
||||||
}
|
}
|
||||||
@ -84,9 +109,10 @@ ImageToLedsMap::ImageToLedsMap(
|
|||||||
totalCount += ledColors.size();
|
totalCount += ledColors.size();
|
||||||
totalCapacity += ledColors.capacity();
|
totalCapacity += ledColors.capacity();
|
||||||
|
|
||||||
|
ledCounter++;
|
||||||
}
|
}
|
||||||
Debug(Logger::getInstance("HYPERION"), "Total index number is: %d (memory: %d). image size: %d x %d, LED areas: %d",
|
Debug(_log, "Total index number is: %d (memory: %d). Reduced pixel set factor: %d, Accuracy level: %d, Image size: %d x %d, LED areas: %d",
|
||||||
totalCount, totalCapacity, width, height, leds.size());
|
totalCount, totalCapacity, reducedPixelSetFactor, accuracyLevel, width, height, leds.size());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,3 +125,16 @@ int ImageToLedsMap::height() const
|
|||||||
{
|
{
|
||||||
return _height;
|
return _height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageToLedsMap::setAccuracyLevel (int accuracyLevel)
|
||||||
|
{
|
||||||
|
if (accuracyLevel > 4 )
|
||||||
|
{
|
||||||
|
Warning(_log, "Accuracy level %d is too high, it will be set to 4", accuracyLevel);
|
||||||
|
accuracyLevel = 4;
|
||||||
|
}
|
||||||
|
//Set cluster number for dominant color advanced
|
||||||
|
_clusterCount = accuracyLevel + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -16,13 +16,37 @@
|
|||||||
},
|
},
|
||||||
"propertyOrder" : 1
|
"propertyOrder" : 1
|
||||||
},
|
},
|
||||||
|
"accuracyLevel": {
|
||||||
|
"type": "integer",
|
||||||
|
"title": "edt_conf_color_accuracyLevel_title",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 4,
|
||||||
|
"default": 2,
|
||||||
|
"propertyOrder": 2,
|
||||||
|
"options": {
|
||||||
|
"dependencies": {
|
||||||
|
"imageToLedMappingType": "dominant_color_advanced"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"reducedPixelSetFactorFactor": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "edt_conf_color_reducedPixelSetFactorFactor_title",
|
||||||
|
"default": 0,
|
||||||
|
"enum" : ["0", "1", "2", "3"],
|
||||||
|
"default" : "0",
|
||||||
|
"options" : {
|
||||||
|
"enum_titles" : ["edt_conf_enum_disabled", "edt_conf_enum_low", "edt_conf_enum_medium", "edt_conf_enum_high"]
|
||||||
|
},
|
||||||
|
"propertyOrder": 3
|
||||||
|
},
|
||||||
"channelAdjustment" :
|
"channelAdjustment" :
|
||||||
{
|
{
|
||||||
"type" : "array",
|
"type" : "array",
|
||||||
"title" : "edt_conf_color_channelAdjustment_header_title",
|
"title" : "edt_conf_color_channelAdjustment_header_title",
|
||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
"required" : true,
|
"required" : true,
|
||||||
"propertyOrder" : 3,
|
"propertyOrder" : 4,
|
||||||
"items" :
|
"items" :
|
||||||
{
|
{
|
||||||
"type" : "object",
|
"type" : "object",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user