Merge pull request #534 from AEtHeLsYn/master

Color correction

Former-commit-id: 7be726f1adb8698684a2b4726650082ed517901f
This commit is contained in:
brindosch
2016-03-22 00:56:41 +01:00
39 changed files with 1586 additions and 233 deletions

View File

@@ -1,41 +1,41 @@
# Find the BCM-package (VC control)
find_package(BCM REQUIRED)
include_directories(${BCM_INCLUDE_DIRS})
# Define the current source locations
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx)
# Group the headers that go through the MOC compiler
SET(DispmanxGrabberQT_HEADERS
${CURRENT_HEADER_DIR}/DispmanxWrapper.h
)
SET(DispmanxGrabberHEADERS
${CURRENT_HEADER_DIR}/DispmanxFrameGrabber.h
)
SET(DispmanxGrabberSOURCES
${CURRENT_SOURCE_DIR}/DispmanxWrapper.cpp
${CURRENT_SOURCE_DIR}/DispmanxFrameGrabber.cpp
)
if(ENABLE_QT5)
QT5_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS})
else(ENABLE_QT5)
QT4_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS})
endif(ENABLE_QT5)
add_library(dispmanx-grabber
${DispmanxGrabberHEADERS}
${DispmanxGrabberQT_HEADERS}
${DispmanxGrabberHEADERS_MOC}
${DispmanxGrabberSOURCES}
)
target_link_libraries(dispmanx-grabber
hyperion
${QT_LIBRARIES}
${BCM_LIBRARIES}
)
# Find the BCM-package (VC control)
find_package(BCM REQUIRED)
include_directories(${BCM_INCLUDE_DIRS})
# Define the current source locations
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx)
# Group the headers that go through the MOC compiler
SET(DispmanxGrabberQT_HEADERS
${CURRENT_HEADER_DIR}/DispmanxWrapper.h
)
SET(DispmanxGrabberHEADERS
${CURRENT_HEADER_DIR}/DispmanxFrameGrabber.h
)
SET(DispmanxGrabberSOURCES
${CURRENT_SOURCE_DIR}/DispmanxWrapper.cpp
${CURRENT_SOURCE_DIR}/DispmanxFrameGrabber.cpp
)
if(ENABLE_QT5)
QT5_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS})
else(ENABLE_QT5)
QT4_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS})
endif(ENABLE_QT5)
add_library(dispmanx-grabber
${DispmanxGrabberHEADERS}
${DispmanxGrabberQT_HEADERS}
${DispmanxGrabberHEADERS_MOC}
${DispmanxGrabberSOURCES}
)
target_link_libraries(dispmanx-grabber
hyperion
${QT_LIBRARIES}
${BCM_LIBRARIES}
)

View File

@@ -1,68 +1,70 @@
# Define the current source locations
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion)
# Group the headers that go through the MOC compiler
SET(Hyperion_QT_HEADERS
${CURRENT_HEADER_DIR}/Hyperion.h
${CURRENT_SOURCE_DIR}/LinearColorSmoothing.h
)
SET(Hyperion_HEADERS
${CURRENT_HEADER_DIR}/ImageProcessor.h
${CURRENT_HEADER_DIR}/ImageProcessorFactory.h
${CURRENT_HEADER_DIR}/ImageToLedsMap.h
${CURRENT_HEADER_DIR}/LedString.h
${CURRENT_HEADER_DIR}/PriorityMuxer.h
${CURRENT_SOURCE_DIR}/MultiColorTransform.h
${CURRENT_HEADER_DIR}/MessageForwarder.h
)
SET(Hyperion_SOURCES
${CURRENT_SOURCE_DIR}/Hyperion.cpp
${CURRENT_SOURCE_DIR}/ImageProcessor.cpp
${CURRENT_SOURCE_DIR}/ImageProcessorFactory.cpp
${CURRENT_SOURCE_DIR}/LedString.cpp
${CURRENT_SOURCE_DIR}/PriorityMuxer.cpp
${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp
${CURRENT_SOURCE_DIR}/MultiColorTransform.cpp
${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp
${CURRENT_SOURCE_DIR}/MessageForwarder.cpp
)
set(Hyperion_RESOURCES
${CURRENT_SOURCE_DIR}/resource.qrc
)
if(ENABLE_QT5)
QT5_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS})
QT5_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress")
else(ENABLE_QT5)
QT4_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS})
QT4_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress")
endif(ENABLE_QT5)
add_library(hyperion
${Hyperion_HEADERS}
${Hyperion_QT_HEADERS}
${Hyperion_HEADERS_MOC}
${Hyperion_SOURCES}
${Hyperion_RESOURCES_RCC}
)
if(ENABLE_QT5)
qt5_use_modules(hyperion Widgets)
endif(ENABLE_QT5)
target_link_libraries(hyperion
blackborder
hyperion-utils
leddevice
effectengine
serialport
${QT_LIBRARIES}
)
# Define the current source locations
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion)
# Group the headers that go through the MOC compiler
SET(Hyperion_QT_HEADERS
${CURRENT_HEADER_DIR}/Hyperion.h
${CURRENT_SOURCE_DIR}/LinearColorSmoothing.h
)
SET(Hyperion_HEADERS
${CURRENT_HEADER_DIR}/ImageProcessor.h
${CURRENT_HEADER_DIR}/ImageProcessorFactory.h
${CURRENT_HEADER_DIR}/ImageToLedsMap.h
${CURRENT_HEADER_DIR}/LedString.h
${CURRENT_HEADER_DIR}/PriorityMuxer.h
${CURRENT_SOURCE_DIR}/MultiColorTransform.h
${CURRENT_SOURCE_DIR}/MultiColorCorrection.h
${CURRENT_HEADER_DIR}/MessageForwarder.h
)
SET(Hyperion_SOURCES
${CURRENT_SOURCE_DIR}/Hyperion.cpp
${CURRENT_SOURCE_DIR}/ImageProcessor.cpp
${CURRENT_SOURCE_DIR}/ImageProcessorFactory.cpp
${CURRENT_SOURCE_DIR}/LedString.cpp
${CURRENT_SOURCE_DIR}/PriorityMuxer.cpp
${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp
${CURRENT_SOURCE_DIR}/MultiColorTransform.cpp
${CURRENT_SOURCE_DIR}/MultiColorCorrection.cpp
${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp
${CURRENT_SOURCE_DIR}/MessageForwarder.cpp
)
set(Hyperion_RESOURCES
${CURRENT_SOURCE_DIR}/resource.qrc
)
if(ENABLE_QT5)
QT5_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS})
QT5_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress")
else(ENABLE_QT5)
QT4_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS})
QT4_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress")
endif(ENABLE_QT5)
add_library(hyperion
${Hyperion_HEADERS}
${Hyperion_QT_HEADERS}
${Hyperion_HEADERS_MOC}
${Hyperion_SOURCES}
${Hyperion_RESOURCES_RCC}
)
if(ENABLE_QT5)
qt5_use_modules(hyperion Widgets)
endif(ENABLE_QT5)
target_link_libraries(hyperion
blackborder
hyperion-utils
leddevice
effectengine
serialport
${QT_LIBRARIES}
)

View File

@@ -15,12 +15,15 @@
// hyperion include
#include <hyperion/Hyperion.h>
#include <hyperion/ImageProcessorFactory.h>
#include <hyperion/ColorTransform.h>
#include <hyperion/ColorCorrection.h>
// Leddevice includes
#include <leddevice/LedDevice.h>
#include <leddevice/LedDeviceFactory.h>
#include "MultiColorTransform.h"
#include "MultiColorCorrection.h"
#include "LinearColorSmoothing.h"
// effect engine includes
@@ -77,6 +80,7 @@ ColorTransform * Hyperion::createColorTransform(const Json::Value & transformCon
RgbChannelTransform * blueTransform = createRgbChannelTransform(transformConfig["blue"]);
HsvTransform * hsvTransform = createHsvTransform(transformConfig["hsv"]);
HslTransform * hslTransform = createHslTransform(transformConfig["hsl"]);
ColorTransform * transform = new ColorTransform();
transform->_id = id;
@@ -84,16 +88,35 @@ ColorTransform * Hyperion::createColorTransform(const Json::Value & transformCon
transform->_rgbGreenTransform = *greenTransform;
transform->_rgbBlueTransform = *blueTransform;
transform->_hsvTransform = *hsvTransform;
transform->_hslTransform = *hslTransform;
// Cleanup the allocated individual transforms
delete redTransform;
delete greenTransform;
delete blueTransform;
delete hsvTransform;
delete hslTransform;
return transform;
}
ColorCorrection * Hyperion::createColorCorrection(const Json::Value & correctionConfig)
{
const std::string id = correctionConfig.get("id", "default").asString();
RgbChannelCorrection * rgbCorrection = createRgbChannelCorrection(correctionConfig["correctionValues"]);
ColorCorrection * correction = new ColorCorrection();
correction->_id = id;
correction->_rgbCorrection = *rgbCorrection;
// Cleanup the allocated individual transforms
delete rgbCorrection;
return correction;
}
MultiColorTransform * Hyperion::createLedColorsTransform(const unsigned ledCnt, const Json::Value & colorConfig)
{
// Create the result, the transforms are added to this
@@ -168,6 +191,154 @@ MultiColorTransform * Hyperion::createLedColorsTransform(const unsigned ledCnt,
return transform;
}
MultiColorCorrection * Hyperion::createLedColorsCorrection(const unsigned ledCnt, const Json::Value & colorConfig)
{
// Create the result, the corrections are added to this
MultiColorCorrection * correction = new MultiColorCorrection(ledCnt);
const Json::Value correctionConfig = colorConfig.get("correction", Json::nullValue);
if (correctionConfig.isNull())
{
// Old style color correction config (just one for all leds)
ColorCorrection * colorCorrection = createColorCorrection(colorConfig);
correction->addCorrection(colorCorrection);
correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1);
}
else if (!correctionConfig.isArray())
{
ColorCorrection * colorCorrection = createColorCorrection(colorConfig);
correction->addCorrection(colorCorrection);
correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1);
}
else
{
const QRegExp overallExp("([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*");
for (Json::UInt i = 0; i < correctionConfig.size(); ++i)
{
const Json::Value & config = correctionConfig[i];
ColorCorrection * colorCorrection = createColorCorrection(config);
correction->addCorrection(colorCorrection);
const QString ledIndicesStr = QString(config.get("leds", "").asCString()).trimmed();
if (ledIndicesStr.compare("*") == 0)
{
// Special case for indices '*' => all leds
correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1);
std::cout << "ColorCorrection '" << colorCorrection->_id << "' => [0; "<< ledCnt-1 << "]" << std::endl;
continue;
}
if (!overallExp.exactMatch(ledIndicesStr))
{
std::cerr << "Given led indices " << i << " not correct format: " << ledIndicesStr.toStdString() << std::endl;
continue;
}
std::cout << "ColorCorrection '" << colorCorrection->_id << "' => [";
const QStringList ledIndexList = ledIndicesStr.split(",");
for (int i=0; i<ledIndexList.size(); ++i) {
if (i > 0)
{
std::cout << ", ";
}
if (ledIndexList[i].contains("-"))
{
QStringList ledIndices = ledIndexList[i].split("-");
int startInd = ledIndices[0].toInt();
int endInd = ledIndices[1].toInt();
correction->setCorrectionForLed(colorCorrection->_id, startInd, endInd);
std::cout << startInd << "-" << endInd;
}
else
{
int index = ledIndexList[i].toInt();
correction->setCorrectionForLed(colorCorrection->_id, index, index);
std::cout << index;
}
}
std::cout << "]" << std::endl;
}
}
return correction;
}
MultiColorCorrection * Hyperion::createLedColorsTemperature(const unsigned ledCnt, const Json::Value & colorConfig)
{
// Create the result, the corrections are added to this
MultiColorCorrection * correction = new MultiColorCorrection(ledCnt);
const Json::Value correctionConfig = colorConfig.get("temperature", Json::nullValue);
if (correctionConfig.isNull())
{
// Old style color correction config (just one for all leds)
ColorCorrection * colorCorrection = createColorCorrection(colorConfig);
correction->addCorrection(colorCorrection);
correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1);
}
else if (!correctionConfig.isArray())
{
ColorCorrection * colorCorrection = createColorCorrection(colorConfig);
correction->addCorrection(colorCorrection);
correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1);
}
else
{
const QRegExp overallExp("([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*");
for (Json::UInt i = 0; i < correctionConfig.size(); ++i)
{
const Json::Value & config = correctionConfig[i];
ColorCorrection * colorCorrection = createColorCorrection(config);
correction->addCorrection(colorCorrection);
const QString ledIndicesStr = QString(config.get("leds", "").asCString()).trimmed();
if (ledIndicesStr.compare("*") == 0)
{
// Special case for indices '*' => all leds
correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1);
std::cout << "ColorCorrection '" << colorCorrection->_id << "' => [0; "<< ledCnt-1 << "]" << std::endl;
continue;
}
if (!overallExp.exactMatch(ledIndicesStr))
{
std::cerr << "Given led indices " << i << " not correct format: " << ledIndicesStr.toStdString() << std::endl;
continue;
}
std::cout << "ColorCorrection '" << colorCorrection->_id << "' => [";
const QStringList ledIndexList = ledIndicesStr.split(",");
for (int i=0; i<ledIndexList.size(); ++i) {
if (i > 0)
{
std::cout << ", ";
}
if (ledIndexList[i].contains("-"))
{
QStringList ledIndices = ledIndexList[i].split("-");
int startInd = ledIndices[0].toInt();
int endInd = ledIndices[1].toInt();
correction->setCorrectionForLed(colorCorrection->_id, startInd, endInd);
std::cout << startInd << "-" << endInd;
}
else
{
int index = ledIndexList[i].toInt();
correction->setCorrectionForLed(colorCorrection->_id, index, index);
std::cout << index;
}
}
std::cout << "]" << std::endl;
}
}
return correction;
}
HsvTransform * Hyperion::createHsvTransform(const Json::Value & hsvConfig)
{
const double saturationGain = hsvConfig.get("saturationGain", 1.0).asDouble();
@@ -176,6 +347,14 @@ HsvTransform * Hyperion::createHsvTransform(const Json::Value & hsvConfig)
return new HsvTransform(saturationGain, valueGain);
}
HslTransform * Hyperion::createHslTransform(const Json::Value & hslConfig)
{
const double saturationGain = hslConfig.get("saturationGain", 1.0).asDouble();
const double luminanceGain = hslConfig.get("luminanceGain", 1.0).asDouble();
return new HslTransform(saturationGain, luminanceGain);
}
RgbChannelTransform* Hyperion::createRgbChannelTransform(const Json::Value& colorConfig)
{
const double threshold = colorConfig.get("threshold", 0.0).asDouble();
@@ -187,6 +366,16 @@ RgbChannelTransform* Hyperion::createRgbChannelTransform(const Json::Value& colo
return transform;
}
RgbChannelCorrection* Hyperion::createRgbChannelCorrection(const Json::Value& colorConfig)
{
const int varR = colorConfig.get("red", 255).asInt();
const int varG = colorConfig.get("green", 255).asInt();
const int varB = colorConfig.get("blue", 255).asInt();
RgbChannelCorrection* correction = new RgbChannelCorrection(varR, varG, varB);
return correction;
}
LedString Hyperion::createLedString(const Json::Value& ledsConfig, const ColorOrder deviceOrder)
{
LedString ledString;
@@ -266,7 +455,6 @@ LedDevice * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig,
return ledDevice;
}
MessageForwarder * Hyperion::createMessageForwarder(const Json::Value & forwarderConfig)
{
MessageForwarder * forwarder = new MessageForwarder();
@@ -302,12 +490,22 @@ MessageForwarder * Hyperion::getForwarder()
Hyperion::Hyperion(const Json::Value &jsonConfig) :
_ledString(createLedString(jsonConfig["leds"], createColorOrder(jsonConfig["device"]))),
_muxer(_ledString.leds().size()),
_raw2ledCorrection(createLedColorsCorrection(_ledString.leds().size(), jsonConfig["color"])),
_raw2ledTemperature(createLedColorsTemperature(_ledString.leds().size(), jsonConfig["color"])),
_raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"])),
_device(LedDeviceFactory::construct(jsonConfig["device"])),
_effectEngine(nullptr),
_messageForwarder(createMessageForwarder(jsonConfig["forwarder"])),
_timer()
{
if (!_raw2ledCorrection->verifyCorrections())
{
throw std::runtime_error("Color correction incorrectly set");
}
if (!_raw2ledTemperature->verifyCorrections())
{
throw std::runtime_error("Color temperature incorrectly set");
}
if (!_raw2ledTransform->verifyTransforms())
{
throw std::runtime_error("Color transformation incorrectly set");
@@ -347,6 +545,12 @@ Hyperion::~Hyperion()
// delete the color transform
delete _raw2ledTransform;
// delete the color correction
delete _raw2ledCorrection;
// delete the color temperature correction
delete _raw2ledTemperature;
// delete the message forwarder
delete _messageForwarder;
@@ -395,16 +599,46 @@ const std::vector<std::string> & Hyperion::getTransformIds() const
return _raw2ledTransform->getTransformIds();
}
const std::vector<std::string> & Hyperion::getCorrectionIds() const
{
return _raw2ledCorrection->getCorrectionIds();
}
const std::vector<std::string> & Hyperion::getTemperatureIds() const
{
return _raw2ledTemperature->getCorrectionIds();
}
ColorTransform * Hyperion::getTransform(const std::string& id)
{
return _raw2ledTransform->getTransform(id);
}
ColorCorrection * Hyperion::getCorrection(const std::string& id)
{
return _raw2ledCorrection->getCorrection(id);
}
ColorCorrection * Hyperion::getTemperature(const std::string& id)
{
return _raw2ledTemperature->getCorrection(id);
}
void Hyperion::transformsUpdated()
{
update();
}
void Hyperion::correctionsUpdated()
{
update();
}
void Hyperion::temperaturesUpdated()
{
update();
}
void Hyperion::clear(int priority)
{
if (_muxer.hasPriority(priority))
@@ -468,8 +702,10 @@ void Hyperion::update()
int priority = _muxer.getCurrentPriority();
const PriorityMuxer::InputInfo & priorityInfo = _muxer.getInputInfo(priority);
// Apply the transform to each led and color-channel
std::vector<ColorRgb> ledColors = _raw2ledTransform->applyTransform(priorityInfo.ledColors);
// Apply the correction and the transform to each led and color-channel
std::vector<ColorRgb> correctedColors = _raw2ledCorrection->applyCorrection(priorityInfo.ledColors);
std::vector<ColorRgb> temperatureColors = _raw2ledTemperature->applyCorrection(correctedColors);
std::vector<ColorRgb> ledColors =_raw2ledTransform->applyTransform(temperatureColors);
const std::vector<Led>& leds = _ledString.leds();
int i = 0;
for (ColorRgb& color : ledColors)

View File

@@ -0,0 +1,96 @@
// STL includes
#include <cassert>
// Hyperion includes
#include "MultiColorCorrection.h"
MultiColorCorrection::MultiColorCorrection(const unsigned ledCnt) :
_ledCorrections(ledCnt, nullptr)
{
}
MultiColorCorrection::~MultiColorCorrection()
{
// Clean up all the correctinos
for (ColorCorrection * correction : _correction)
{
delete correction;
}
}
void MultiColorCorrection::addCorrection(ColorCorrection * correction)
{
_correctionIds.push_back(correction->_id);
_correction.push_back(correction);
}
void MultiColorCorrection::setCorrectionForLed(const std::string& id, const unsigned startLed, const unsigned endLed)
{
assert(startLed <= endLed);
assert(endLed < _ledCorrections.size());
// Get the identified correction (don't care if is nullptr)
ColorCorrection * correction = getCorrection(id);
for (unsigned iLed=startLed; iLed<=endLed; ++iLed)
{
_ledCorrections[iLed] = correction;
}
}
bool MultiColorCorrection::verifyCorrections() const
{
bool allLedsSet = true;
for (unsigned iLed=0; iLed<_ledCorrections.size(); ++iLed)
{
if (_ledCorrections[iLed] == nullptr)
{
std::cerr << "No correction set for " << iLed << std::endl;
allLedsSet = false;
}
}
return allLedsSet;
}
const std::vector<std::string> & MultiColorCorrection::getCorrectionIds()
{
return _correctionIds;
}
ColorCorrection* MultiColorCorrection::getCorrection(const std::string& id)
{
// Iterate through the unique corrections until we find the one with the given id
for (ColorCorrection * correction : _correction)
{
if (correction->_id == id)
{
return correction;
}
}
// The ColorCorrection was not found
return nullptr;
}
std::vector<ColorRgb> MultiColorCorrection::applyCorrection(const std::vector<ColorRgb>& rawColors)
{
// Create a copy, as we will do the rest of the correction in place
std::vector<ColorRgb> ledColors(rawColors);
const size_t itCnt = std::min(_ledCorrections.size(), rawColors.size());
for (size_t i=0; i<itCnt; ++i)
{
ColorCorrection * correction = _ledCorrections[i];
if (correction == nullptr)
{
// No correction set for this led (do nothing)
continue;
}
ColorRgb& color = ledColors[i];
color.red = correction->_rgbCorrection.correctionR(color.red);
color.green = correction->_rgbCorrection.correctionG(color.green);
color.blue = correction->_rgbCorrection.correctionB(color.blue);
}
return ledColors;
}

View File

@@ -0,0 +1,66 @@
#pragma once
// STL includes
#include <vector>
// Utils includes
#include <utils/ColorRgb.h>
// Hyperion includes
#include <hyperion/ColorCorrection.h>
///
/// The LedColorCorrection is responsible for performing color correction from 'raw' colors
/// received as input to colors mapped to match the color-properties of the leds.
///
class MultiColorCorrection
{
public:
MultiColorCorrection(const unsigned ledCnt);
~MultiColorCorrection();
/**
* Adds a new ColorCorrection to this MultiColorCorrection
*
* @param Correction The new ColorCorrection (ownership is transfered)
*/
void addCorrection(ColorCorrection * correction);
void setCorrectionForLed(const std::string& id, const unsigned startLed, const unsigned endLed);
bool verifyCorrections() const;
///
/// Returns the identifier of all the unique ColorCorrection
///
/// @return The list with unique id's of the ColorCorrections
const std::vector<std::string> & getCorrectionIds();
///
/// Returns the pointer to the ColorCorrection with the given id
///
/// @param id The identifier of the ColorCorrection
///
/// @return The ColorCorrection with the given id (or nullptr if it does not exist)
///
ColorCorrection* getCorrection(const std::string& id);
///
/// Performs the color transoformation from raw-color to led-color
///
/// @param rawColors The list with raw colors
///
/// @return The list with led-colors
///
std::vector<ColorRgb> applyCorrection(const std::vector<ColorRgb>& rawColors);
private:
/// List with Correction ids
std::vector<std::string> _correctionIds;
/// List with unique ColorCorrections
std::vector<ColorCorrection*> _correction;
/// List with a pointer to the ColorCorrection for each individual led
std::vector<ColorCorrection*> _ledCorrections;
};

View File

@@ -89,6 +89,7 @@ std::vector<ColorRgb> MultiColorTransform::applyTransform(const std::vector<Colo
ColorRgb& color = ledColors[i];
transform->_hsvTransform.transform(color.red, color.green, color.blue);
transform->_hslTransform.transform(color.red, color.green, color.blue);
color.red = transform->_rgbRedTransform.transform(color.red);
color.green = transform->_rgbGreenTransform.transform(color.green);
color.blue = transform->_rgbBlueTransform.transform(color.blue);

View File

@@ -38,7 +38,7 @@
"type":"object",
"required":false,
"properties": {
"hsv" : {
"hsv" : {
"type" : "object",
"required" : false,
"properties" : {
@@ -54,6 +54,23 @@
}
},
"additionalProperties" : false
},
"hsl" : {
"type" : "object",
"required" : false,
"properties" : {
"saturationGain" : {
"type" : "number",
"required" : false,
"minimum" : 0.0
},
"luminanceGain" : {
"type" : "number",
"required" : false,
"minimum" : 0.0
}
},
"additionalProperties" : false
},
"red": {
"type":"object",

View File

@@ -18,6 +18,7 @@
#include <hyperion/ImageProcessor.h>
#include <hyperion/MessageForwarder.h>
#include <hyperion/ColorTransform.h>
#include <hyperion/ColorCorrection.h>
#include <utils/ColorRgb.h>
// project includes
@@ -231,7 +232,7 @@ void JsonClientConnection::handleMessage(const std::string &messageString)
sendErrorReply("Error while validating json: " + errors);
return;
}
// switch over all possible commands and handle them
if (command == "color")
handleColorCommand(message);
@@ -247,6 +248,10 @@ void JsonClientConnection::handleMessage(const std::string &messageString)
handleClearallCommand(message);
else if (command == "transform")
handleTransformCommand(message);
else if (command == "correction")
handleCorrectionCommand(message);
else if (command == "temperature")
handleTemperatureCommand(message);
else
handleNotImplemented();
}
@@ -387,6 +392,47 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &)
item["duration_ms"] = Json::Value::UInt(priorityInfo.timeoutTime_ms - now);
}
}
// collect correction information
Json::Value & correctionArray = info["correction"];
for (const std::string& correctionId : _hyperion->getCorrectionIds())
{
const ColorCorrection * colorCorrection = _hyperion->getCorrection(correctionId);
if (colorCorrection == nullptr)
{
std::cerr << "Incorrect color correction id: " << correctionId << std::endl;
continue;
}
Json::Value & correction = correctionArray.append(Json::Value());
correction["id"] = correctionId;
Json::Value & corrValues = correction["correctionValues"];
corrValues.append(colorCorrection->_rgbCorrection.getcorrectionR());
corrValues.append(colorCorrection->_rgbCorrection.getcorrectionG());
corrValues.append(colorCorrection->_rgbCorrection.getcorrectionB());
}
// collect temperature correction information
Json::Value & temperatureArray = info["temperature"];
for (const std::string& tempId : _hyperion->getTemperatureIds())
{
const ColorCorrection * colorTemp = _hyperion->getTemperature(tempId);
if (colorTemp == nullptr)
{
std::cerr << "Incorrect color temperature correction id: " << tempId << std::endl;
continue;
}
Json::Value & temperature = temperatureArray.append(Json::Value());
temperature["id"] = tempId;
Json::Value & tempValues = temperature["correctionValues"];
tempValues.append(colorTemp->_rgbCorrection.getcorrectionR());
tempValues.append(colorTemp->_rgbCorrection.getcorrectionG());
tempValues.append(colorTemp->_rgbCorrection.getcorrectionB());
}
// collect transform information
Json::Value & transformArray = info["transform"];
@@ -404,6 +450,8 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &)
transform["saturationGain"] = colorTransform->_hsvTransform.getSaturationGain();
transform["valueGain"] = colorTransform->_hsvTransform.getValueGain();
transform["saturationLGain"] = colorTransform->_hslTransform.getSaturationGain();
transform["luminanceGain"] = colorTransform->_hslTransform.getLuminanceGain();
Json::Value & threshold = transform["threshold"];
threshold.append(colorTransform->_rgbRedTransform.getThreshold());
@@ -476,7 +524,7 @@ void JsonClientConnection::handleTransformCommand(const Json::Value &message)
//sendErrorReply(std::string("Incorrect transform identifier: ") + transformId);
return;
}
if (transform.isMember("saturationGain"))
{
colorTransform->_hsvTransform.setSaturationGain(transform["saturationGain"].asDouble());
@@ -486,6 +534,16 @@ void JsonClientConnection::handleTransformCommand(const Json::Value &message)
{
colorTransform->_hsvTransform.setValueGain(transform["valueGain"].asDouble());
}
if (transform.isMember("saturationLGain"))
{
colorTransform->_hslTransform.setSaturationGain(transform["saturationLGain"].asDouble());
}
if (transform.isMember("luminanceGain"))
{
colorTransform->_hslTransform.setLuminanceGain(transform["luminanceGain"].asDouble());
}
if (transform.isMember("threshold"))
{
@@ -518,13 +576,65 @@ void JsonClientConnection::handleTransformCommand(const Json::Value &message)
colorTransform->_rgbGreenTransform.setWhitelevel(values[1u].asDouble());
colorTransform->_rgbBlueTransform .setWhitelevel(values[2u].asDouble());
}
// commit the changes
_hyperion->transformsUpdated();
sendSuccessReply();
}
void JsonClientConnection::handleCorrectionCommand(const Json::Value &message)
{
const Json::Value & correction = message["correction"];
const std::string correctionId = correction.get("id", _hyperion->getCorrectionIds().front()).asString();
ColorCorrection * colorCorrection = _hyperion->getCorrection(correctionId);
if (colorCorrection == nullptr)
{
//sendErrorReply(std::string("Incorrect correction identifier: ") + correctionId);
return;
}
if (correction.isMember("correctionValues"))
{
const Json::Value & values = correction["correctionValues"];
colorCorrection->_rgbCorrection.setcorrectionR(values[0u].asInt());
colorCorrection->_rgbCorrection.setcorrectionG(values[1u].asInt());
colorCorrection->_rgbCorrection.setcorrectionB(values[2u].asInt());
}
// commit the changes
_hyperion->correctionsUpdated();
sendSuccessReply();
}
void JsonClientConnection::handleTemperatureCommand(const Json::Value &message)
{
const Json::Value & temperature = message["temperature"];
const std::string tempId = temperature.get("id", _hyperion->getTemperatureIds().front()).asString();
ColorCorrection * colorTemperature = _hyperion->getTemperature(tempId);
if (colorTemperature == nullptr)
{
//sendErrorReply(std::string("Incorrect temperature identifier: ") + tempId);
return;
}
if (temperature.isMember("correctionValues"))
{
const Json::Value & values = temperature["correctionValues"];
colorTemperature->_rgbCorrection.setcorrectionR(values[0u].asInt());
colorTemperature->_rgbCorrection.setcorrectionG(values[1u].asInt());
colorTemperature->_rgbCorrection.setcorrectionB(values[2u].asInt());
}
// commit the changes
_hyperion->temperaturesUpdated();
sendSuccessReply();
}
void JsonClientConnection::handleNotImplemented()
{
sendErrorReply("Command not implemented");

View File

@@ -112,6 +112,20 @@ private:
/// @param message the incoming message
///
void handleTransformCommand(const Json::Value & message);
///
/// Handle an incoming JSON Correction message
///
/// @param message the incoming message
///
void handleCorrectionCommand(const Json::Value & message);
///
/// Handle an incoming JSON Temperature message
///
/// @param message the incoming message
///
void handleTemperatureCommand(const Json::Value & message);
///
/// Handle an incoming JSON message of unknown type

View File

@@ -7,6 +7,8 @@
<file alias="schema-clear">schema/schema-clear.json</file>
<file alias="schema-clearall">schema/schema-clearall.json</file>
<file alias="schema-transform">schema/schema-transform.json</file>
<file alias="schema-correction">schema/schema-correction.json</file>
<file alias="schema-temperature">schema/schema-temperature.json</file>
<file alias="schema-effect">schema/schema-effect.json</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,34 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["correction"]
},
"correction": {
"type": "object",
"required": true,
"properties": {
"id" : {
"type" : "string",
"required" : false
},
"correctionValues" : {
"type": "array",
"required": false,
"items" : {
"type": "integer",
"minimum": 0,
"maximum": 255
},
"minItems": 3,
"maxItems": 3
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,34 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["temperature"]
},
"temperature": {
"type": "object",
"required": true,
"properties": {
"id" : {
"type" : "string",
"required" : false
},
"correctionValues" : {
"type": "array",
"required": false,
"items" : {
"type": "integer",
"minimum": 0,
"maximum": 255
},
"minItems": 3,
"maxItems": 3
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}

View File

@@ -25,6 +25,16 @@
"required" : false,
"minimum" : 0.0
},
"saturationLGain" : {
"type" : "number",
"required" : false,
"minimum" : 0.0
},
"luminanceGain" : {
"type" : "number",
"required" : false,
"minimum" : 0.0
},
"threshold": {
"type": "array",
"required": false,

View File

@@ -5,7 +5,7 @@
"command": {
"type" : "string",
"required" : true,
"enum" : ["color", "image", "effect", "serverinfo", "clear", "clearall", "transform"]
"enum" : ["color", "image", "effect", "serverinfo", "clear", "clearall", "transform", "correction", "temperature"]
}
}
}

0
libsrc/leddevice/LedDeviceFadeCandy.cpp Executable file → Normal file
View File

0
libsrc/leddevice/LedDeviceFadeCandy.h Executable file → Normal file
View File

View File

@@ -23,8 +23,12 @@ add_library(hyperion-utils
${CURRENT_HEADER_DIR}/HsvTransform.h
${CURRENT_SOURCE_DIR}/HsvTransform.cpp
${CURRENT_HEADER_DIR}/HslTransform.h
${CURRENT_SOURCE_DIR}/HslTransform.cpp
${CURRENT_HEADER_DIR}/RgbChannelTransform.h
${CURRENT_SOURCE_DIR}/RgbChannelTransform.cpp
${CURRENT_HEADER_DIR}/RgbChannelCorrection.h
${CURRENT_SOURCE_DIR}/RgbChannelCorrection.cpp
${CURRENT_HEADER_DIR}/jsonschema/JsonFactory.h
${CURRENT_HEADER_DIR}/jsonschema/JsonSchemaChecker.h

View File

@@ -0,0 +1,158 @@
#include <algorithm>
#include <cmath>
#include <utils/HslTransform.h>
HslTransform::HslTransform() :
_saturationGain(1.0),
_luminanceGain(1.0)
{
}
HslTransform::HslTransform(double saturationGain, double luminanceGain) :
_saturationGain(saturationGain),
_luminanceGain(luminanceGain)
{
}
HslTransform::~HslTransform()
{
}
void HslTransform::setSaturationGain(double saturationGain)
{
_saturationGain = saturationGain;
}
double HslTransform::getSaturationGain() const
{
return _saturationGain;
}
void HslTransform::setLuminanceGain(double luminanceGain)
{
_luminanceGain = luminanceGain;
}
double HslTransform::getLuminanceGain() const
{
return _luminanceGain;
}
void HslTransform::transform(uint8_t & red, uint8_t & green, uint8_t & blue) const
{
if (_saturationGain != 1.0 || _luminanceGain != 1.0)
{
uint16_t hue;
float saturation, luminance;
rgb2hsl(red, green, blue, hue, saturation, luminance);
float s = saturation * _saturationGain;
if (s > 1.0f)
saturation = 1.0f;
else
saturation = s;
float l = luminance * _luminanceGain;
if (l > 1.0f)
luminance = 1.0f;
else
luminance = l;
hsl2rgb(hue, saturation, luminance, red, green, blue);
}
}
void HslTransform::rgb2hsl(uint8_t red, uint8_t green, uint8_t blue, uint16_t & hue, float & saturation, float & luminance)
{
float r = red / 255.0f;
float g = green / 255.0f;
float b = blue / 255.0f;
float rgbMin = r < g ? (r < b ? r : b) : (g < b ? g : b);
float rgbMax = r > g ? (r > b ? r : b) : (g > b ? g : b);
float diff = rgbMax - rgbMin;
//luminance
luminance = (rgbMin + rgbMax) / 2.0f;
if (diff == 0.0f) {
saturation = 0.0f;
hue = 0;
return;
}
//saturation
if (luminance < 0.5f)
saturation = diff / (rgbMin + rgbMax);
else
saturation = diff / (2.0f - rgbMin - rgbMax);
if (rgbMax == r)
{
// start from 360 to be sure that we won't assign a negative number to the unsigned hue value
hue = 360 + 60 * (g - b) / (rgbMax - rgbMin);
if (hue > 359)
hue -= 360;
}
else if (rgbMax == g)
{
hue = 120 + 60 * (b - r) / (rgbMax - rgbMin);
}
else
{
hue = 240 + 60 * (r - g) / (rgbMax - rgbMin);
}
}
void HslTransform::hsl2rgb(uint16_t hue, float saturation, float luminance, uint8_t & red, uint8_t & green, uint8_t & blue)
{
if (saturation == 0.0f){
red = (uint8_t)(luminance * 255.0f);
green = (uint8_t)(luminance * 255.0f);
blue = (uint8_t)(luminance * 255.0f);
return;
}
float q;
if (luminance < 0.5f)
q = luminance * (1.0f + saturation);
else
q = (luminance + saturation) - (luminance * saturation);
float p = (2.0f * luminance) - q;
float h = hue / 360.0f;
float t[3];
t[0] = h + (1.0f / 3.0f);
t[1] = h;
t[2] = h - (1.0f / 3.0f);
for (int i = 0; i < 3; i++) {
if (t[i] < 0.0f)
t[i] += 1.0f;
if (t[i] > 1.0f)
t[i] -= 1.0f;
}
float out[3];
for (int i = 0; i < 3; i++) {
if (t[i] * 6.0f < 1.0f)
out[i] = p + (q - p) * 6.0f * t[i];
else if (t[i] * 2.0f < 1.0f)
out[i] = q;
else if (t[i] * 3.0f < 2.0f)
out[i] = p + (q - p) * ((2.0f / 3.0f) - t[i]) * 6.0f;
else out[i] = p;
}
//convert back to 0...255 range
red = (uint8_t)(out[0] * 255.0f);
green = (uint8_t)(out[1] * 255.0f);
blue = (uint8_t)(out[2] * 255.0f);
}

View File

@@ -0,0 +1,120 @@
// STL includes
#include <cmath>
// Utils includes
#include <utils/RgbChannelCorrection.h>
RgbChannelCorrection::RgbChannelCorrection() :
_correctionR(255),
_correctionB(255),
_correctionG(255)
{
initializeMapping();
}
RgbChannelCorrection::RgbChannelCorrection(int correctionR, int correctionG, int correctionB) :
_correctionR(correctionR),
_correctionG(correctionG),
_correctionB(correctionB)
{
initializeMapping();
}
RgbChannelCorrection::~RgbChannelCorrection()
{
}
uint8_t RgbChannelCorrection::getcorrectionR() const
{
return _correctionR;
}
void RgbChannelCorrection::setcorrectionR(uint8_t correctionR)
{
_correctionR = correctionR;
initializeMapping();
}
uint8_t RgbChannelCorrection::getcorrectionG() const
{
return _correctionG;
}
void RgbChannelCorrection::setcorrectionG(uint8_t correctionG)
{
_correctionG = correctionG;
initializeMapping();
}
uint8_t RgbChannelCorrection::getcorrectionB() const
{
return _correctionB;
}
void RgbChannelCorrection::setcorrectionB(uint8_t correctionB)
{
_correctionB = correctionB;
initializeMapping();
}
uint8_t RgbChannelCorrection::correctionR(uint8_t inputR) const
{
return _mappingR[inputR];
}
uint8_t RgbChannelCorrection::correctionG(uint8_t inputG) const
{
return _mappingG[inputG];
}
uint8_t RgbChannelCorrection::correctionB(uint8_t inputB) const
{
return _mappingB[inputB];
}
void RgbChannelCorrection::initializeMapping()
{
// initialize the mapping
for (int i = 0; i < 256; ++i)
{
int outputR = (i * _correctionR) / 255;
if (outputR < -255)
{
outputR = -255;
}
else if (outputR > 255)
{
outputR = 255;
}
_mappingR[i] = outputR;
}
for (int i = 0; i < 256; ++i)
{
int outputG = (i * _correctionG) / 255;
if (outputG < -255)
{
outputG = -255;
}
else if (outputG > 255)
{
outputG = 255;
}
_mappingG[i] = outputG;
}
for (int i = 0; i < 256; ++i)
{
int outputB = (i * _correctionB) / 255;
if (outputB < -255)
{
outputB = -255;
}
else if (outputB > 255)
{
outputB = 255;
}
_mappingB[i] = outputB;
}
}