Saturation & Brightness/Value Gain using Oklab color space (#1477)

* Imported Oklab reference implementation

* Add Okhsv conversions

* Fixed formatting error

* Add saturation and value gain to schemas

* Add english translation for saturation, value gain

* Created OkhsvTransform

* Make OkhsvTransform configurable

* Apply OkhvsTransform

* Clamped values during transform

* Precalculate isIdentity in OkhsvTransform

* Skip OkhsvTransform if it is the identity function

* Added changelog message

* Allow for full desaturation

* Imported recommended changes by LordGrey

* Fixed typo in constant

* Fixed anti-pattern in ok_color.h

* Correct indentions

* Correct remote-control

* Limited maximum gain settings to practical range

* Renane valueGain to brightnessGain for clarity and understanding

Co-authored-by: LordGrey <lordgrey.emmel@gmail.com>
This commit is contained in:
xkns
2022-08-17 23:26:19 +02:00
committed by GitHub
parent a2266b177f
commit 2fb2fc9dd7
17 changed files with 1033 additions and 32 deletions

View File

@@ -134,6 +134,18 @@
"required" : false,
"minimum" : 0,
"maximum" : 100
},
"saturationGain" : {
"type" : "number",
"required" : false,
"minimum" : 0.0,
"maximum": 10.0
},
"brightnessGain" : {
"type" : "number",
"required" : false,
"minimum" : 0.1,
"maximum": 10.0
}
},
"additionalProperties": false

View File

@@ -508,6 +508,9 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
adjustment["gammaGreen"] = colorAdjustment->_rgbTransform.getGammaG();
adjustment["gammaBlue"] = colorAdjustment->_rgbTransform.getGammaB();
adjustment["saturationGain"] = colorAdjustment->_okhsvTransform.getSaturationGain();
adjustment["brightnessGain"] = colorAdjustment->_okhsvTransform.getBrightnessGain();
adjustmentArray.append(adjustment);
}
@@ -700,7 +703,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
transform["id"] = transformId;
transform["saturationGain"] = 1.0;
transform["valueGain"] = 1.0;
transform["brightnessGain"] = 1.0;
transform["saturationLGain"] = 1.0;
transform["luminanceGain"] = 1.0;
transform["luminanceMinimum"] = 0.0;
@@ -917,6 +920,16 @@ void JsonAPI::handleAdjustmentCommand(const QJsonObject &message, const QString
colorAdjustment->_rgbTransform.setBrightnessCompensation(adjustment["brightnessCompensation"].toInt());
}
if (adjustment.contains("saturationGain"))
{
colorAdjustment->_okhsvTransform.setSaturationGain(adjustment["saturationGain"].toDouble());
}
if (adjustment.contains("brightnessGain"))
{
colorAdjustment->_okhsvTransform.setBrightnessGain(adjustment["brightnessGain"].toDouble());
}
// commit the changes
_hyperion->adjustmentsUpdated();

View File

@@ -112,8 +112,14 @@ void MultiColorAdjustment::applyAdjustment(std::vector<ColorRgb>& ledColors)
uint8_t ored = color.red;
uint8_t ogreen = color.green;
uint8_t oblue = color.blue;
uint8_t B_RGB = 0, B_CMY = 0, B_W = 0;
uint8_t B_RGB = 0;
uint8_t B_CMY = 0;
uint8_t B_W = 0;
if (!adjustment->_okhsvTransform.isIdentity())
{
adjustment->_okhsvTransform.transform(ored, ogreen, oblue);
}
adjustment->_rgbTransform.transform(ored,ogreen,oblue);
adjustment->_rgbTransform.getBrightnessComponents(B_RGB, B_CMY, B_W);

View File

@@ -158,6 +158,17 @@
"maxItems" : 3,
"propertyOrder" : 10
},
"saturationGain" :
{
"type" : "number",
"title" : "edt_conf_color_saturationGain_title",
"required" : true,
"minimum" : 0.0,
"maximum": 10.0,
"default" : 1.0,
"step" : 0.1,
"propertyOrder" : 11
},
"backlightThreshold" :
{
"type" : "integer",
@@ -167,7 +178,7 @@
"maximum": 100,
"default" : 0,
"append" : "edt_append_percent",
"propertyOrder" : 11
"propertyOrder" : 12
},
"backlightColored" :
{
@@ -175,7 +186,7 @@
"title" : "edt_conf_color_backlightColored_title",
"required" : true,
"default" : false,
"propertyOrder" : 12
"propertyOrder" : 13
},
"brightness" :
{
@@ -186,7 +197,7 @@
"maximum": 100,
"default" : 100,
"append" : "edt_append_percent",
"propertyOrder" : 13
"propertyOrder" : 14
},
"brightnessCompensation" :
{
@@ -197,7 +208,18 @@
"maximum": 100,
"default" : 0,
"append" : "edt_append_percent",
"propertyOrder" : 14
"propertyOrder" : 15
},
"brightnessGain" :
{
"type" : "number",
"title" : "edt_conf_color_brightnessGain_title",
"required" : true,
"minimum" : 0.1,
"maximum": 10.0,
"default" : 1.0,
"step" : 0.1,
"propertyOrder" : 16
},
"gammaRed" :
{
@@ -208,7 +230,7 @@
"maximum": 100.0,
"default" : 2.2,
"step" : 0.1,
"propertyOrder" : 15
"propertyOrder" : 17
},
"gammaGreen" :
{
@@ -219,7 +241,7 @@
"maximum": 100.0,
"default" : 2.2,
"step" : 0.1,
"propertyOrder" : 16
"propertyOrder" : 18
},
"gammaBlue" :
{
@@ -230,7 +252,7 @@
"maximum": 100.0,
"default" : 2.2,
"step" : 0.1,
"propertyOrder" : 17
"propertyOrder" : 19
}
},
"additionalProperties" : false

View File

@@ -19,6 +19,8 @@ SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/utils)
FILE ( GLOB_RECURSE Utils_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
list(APPEND Utils_SOURCES "${CMAKE_SOURCE_DIR}/dependencies/include/oklab/ok_color.h")
if ( NOT ENABLE_PROFILER )
LIST ( REMOVE_ITEM Utils_SOURCES ${CURRENT_HEADER_DIR}/Profiler.h ${CURRENT_SOURCE_DIR}/Profiler.cpp )
endif()

View File

@@ -1,6 +1,7 @@
#include <utils/ColorSys.h>
#include <QColor>
#include <oklab/ok_color.h>
inline uint8_t clamp(int x)
{
@@ -56,3 +57,22 @@ void ColorSys::yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t &r, uint8_t &g,
g = clamp((298 * c - 100 * d - 208 * e + 128) >> 8);
b = clamp((298 * c + 516 * d + 128) >> 8);
}
void ColorSys::rgb2okhsv(uint8_t red, uint8_t green, uint8_t blue, double & hue, double & saturation, double & value)
{
ok_color::HSV color = ok_color::srgb_to_okhsv({ static_cast<float>(red) / 255.F,
static_cast<float>(green) / 255.F,
static_cast<float>(blue) / 255.F
});
hue = color.h;
saturation = color.s;
value = color.v;
}
void ColorSys::okhsv2rgb(double hue, double saturation, double value, uint8_t & red, uint8_t & green, uint8_t & blue)
{
ok_color::RGB color = ok_color::okhsv_to_srgb({ static_cast<float>(hue), static_cast<float>(saturation), static_cast<float>(value) });
red = static_cast<uint8_t>(std::lround(color.r * 255));
green = static_cast<uint8_t>(std::lround(color.g * 255));
blue = static_cast<uint8_t>(std::lround(color.b * 255));
}

View File

@@ -0,0 +1,69 @@
#include <algorithm>
#include <utils/OkhsvTransform.h>
#include <utils/ColorSys.h>
/// Clamps between 0.f and 1.f. Should generally be branchless
double clamp(double value)
{
return std::max(0.0, std::min(value, 1.0));
}
OkhsvTransform::OkhsvTransform()
{
_saturationGain = 1.0;
_brightnessGain = 1.0;
_isIdentity = true;
}
OkhsvTransform::OkhsvTransform(double saturationGain, double brightnessGain)
{
_saturationGain = saturationGain;
_brightnessGain = brightnessGain;
updateIsIdentity();
}
double OkhsvTransform::getSaturationGain() const
{
return _saturationGain;
}
void OkhsvTransform::setSaturationGain(double saturationGain)
{
_saturationGain = saturationGain;
updateIsIdentity();
}
double OkhsvTransform::getBrightnessGain() const
{
return _brightnessGain;
}
void OkhsvTransform::setBrightnessGain(double brightnessGain)
{
_brightnessGain= brightnessGain;
updateIsIdentity();
}
bool OkhsvTransform::isIdentity() const
{
return _isIdentity;
}
void OkhsvTransform::transform(uint8_t & red, uint8_t & green, uint8_t & blue) const
{
double hue;
double saturation;
double brightness;
ColorSys::rgb2okhsv(red, green, blue, hue, saturation, brightness);
saturation = clamp(saturation * _saturationGain);
brightness = clamp(brightness * _brightnessGain);
ColorSys::okhsv2rgb(hue, saturation, brightness, red, green, blue);
}
void OkhsvTransform::updateIsIdentity()
{
_isIdentity = _saturationGain == 1.0 && _brightnessGain == 1.0;
}