Adjustment merge + new brightness settings (#359)

* add new rgbtransform

* activate rgbtransform

* integrate new transform and gamma in adjustment, disable transform

* fix brighness limit

* advance upper and lower thresholds

* start removing color transform

* adjust configs/schema

* implement json for new color adjustment

* finish hyperion-remote extension for new adjustment settings

* fix typos

* rename luminance to brightness
fix jsonapi for new adjustment

* fix some bugs in adjustments

* fix i18n

* fix gamma via json

* now brighness values goes from 0-1 with 0.5 is the default for all brighness is equal between the channels. less 0.5 all channels scaled down
to new brighness, above 0.5 if possible channel gets brighter - but brighness is not equal between the channels anymore
brighness value curve is now exponential instead of linear - this feels more natural

* hslv cleanup
This commit is contained in:
redPanther
2017-01-06 14:25:55 +01:00
committed by GitHub
parent c433504b81
commit caab8e819b
34 changed files with 645 additions and 1807 deletions

View File

@@ -19,9 +19,8 @@ SET(Utils_HEADERS
${CURRENT_HEADER_DIR}/PixelFormat.h
${CURRENT_HEADER_DIR}/VideoMode.h
${CURRENT_HEADER_DIR}/ImageResampler.h
${CURRENT_HEADER_DIR}/HsvTransform.h
${CURRENT_HEADER_DIR}/HslTransform.h
${CURRENT_HEADER_DIR}/RgbChannelTransform.h
${CURRENT_HEADER_DIR}/RgbTransform.h
${CURRENT_HEADER_DIR}/ColorSys.h
${CURRENT_HEADER_DIR}/RgbChannelAdjustment.h
${CURRENT_HEADER_DIR}/RgbToRgbw.h
${CURRENT_HEADER_DIR}/jsonschema/QJsonFactory.h
@@ -38,10 +37,9 @@ SET(Utils_SOURCES
${CURRENT_SOURCE_DIR}/Process.cpp
${CURRENT_SOURCE_DIR}/Logger.cpp
${CURRENT_SOURCE_DIR}/ImageResampler.cpp
${CURRENT_SOURCE_DIR}/HsvTransform.cpp
${CURRENT_SOURCE_DIR}/HslTransform.cpp
${CURRENT_SOURCE_DIR}/RgbChannelTransform.cpp
${CURRENT_SOURCE_DIR}/ColorSys.cpp
${CURRENT_SOURCE_DIR}/RgbChannelAdjustment.cpp
${CURRENT_SOURCE_DIR}/RgbTransform.cpp
${CURRENT_SOURCE_DIR}/RgbToRgbw.cpp
${CURRENT_SOURCE_DIR}/jsonschema/QJsonSchemaChecker.cpp
)
@@ -61,7 +59,7 @@ add_library(hyperion-utils
${PROFILER_SOURCE}
)
qt5_use_modules(hyperion-utils Core)
qt5_use_modules(hyperion-utils Core Gui)
target_link_libraries(hyperion-utils
${QT_LIBRARIES})

36
libsrc/utils/ColorSys.cpp Normal file
View File

@@ -0,0 +1,36 @@
#include <utils/ColorSys.h>
#include <QColor>
void ColorSys::rgb2hsl(uint8_t red, uint8_t green, uint8_t blue, uint16_t & hue, float & saturation, float & luminance)
{
QColor color(red,green,blue);
qreal h,s,l;
color.getHslF(&h,&s,&l);
hue = h;
saturation = s;
luminance = l;
}
void ColorSys::hsl2rgb(uint16_t hue, float saturation, float luminance, uint8_t & red, uint8_t & green, uint8_t & blue)
{
QColor color(QColor::fromHslF(hue,(qreal)saturation,(qreal)luminance));
red = (uint8_t)color.red();
green = (uint8_t)color.green();
blue = (uint8_t)color.blue();
}
void ColorSys::rgb2hsv(uint8_t red, uint8_t green, uint8_t blue, uint16_t & hue, uint8_t & saturation, uint8_t & value)
{
QColor color(red,green,blue);
hue = color.hsvHue();
saturation = color.hsvSaturation();
value = color.value();
}
void ColorSys::hsv2rgb(uint16_t hue, uint8_t saturation, uint8_t value, uint8_t & red, uint8_t & green, uint8_t & blue)
{
QColor color(QColor::fromHsv(hue,saturation,value));
red = (uint8_t)color.red();
green = (uint8_t)color.green();
blue = (uint8_t)color.blue();
}

View File

@@ -1,169 +0,0 @@
#include <algorithm>
#include <cmath>
#include <utils/HslTransform.h>
HslTransform::HslTransform()
: _saturationGain(1.0)
, _luminanceGain(1.0)
, _luminanceMinimum(0.0)
{
}
HslTransform::HslTransform(double saturationGain, double luminanceGain, double luminanceMinimum) :
_saturationGain(saturationGain),
_luminanceGain(luminanceGain),
_luminanceMinimum(luminanceMinimum)
{
}
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::setLuminanceMinimum(double luminanceMinimum)
{
_luminanceMinimum = luminanceMinimum;
}
double HslTransform::getLuminanceMinimum() const
{
return _luminanceMinimum;
}
void HslTransform::transform(uint8_t & red, uint8_t & green, uint8_t & blue) const
{
if (_saturationGain != 1.0 || _luminanceGain != 1.0 || _luminanceMinimum != 0.0)
{
uint16_t hue;
float saturation, luminance;
rgb2hsl(red, green, blue, hue, saturation, luminance);
float s = saturation * _saturationGain;
saturation = std::min(s, 1.0f);
float l = luminance * _luminanceGain;
if (l < _luminanceMinimum)
{
saturation = 0;
l = _luminanceMinimum;
}
luminance = std::min(l, 1.0f);
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 = (float)red / 255.0f;
float g = (float)green / 255.0f;
float b = (float)blue / 255.0f;
float rgbMin = std::min(r,std::min(g,b));
float rgbMax = std::max(r,std::max(g,b));
float diff = rgbMax - rgbMin;
//luminance
luminance = (rgbMin + rgbMax) / 2.0f;
if (diff == 0.0f)
{
saturation = 0.0f;
hue = 0;
return;
}
//saturation
saturation = (luminance < 0.5f)
? (diff / (rgbMin + rgbMax))
: (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 = (luminance < 0.5f)
? luminance * (1.0f + saturation)
: (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

@@ -1,144 +0,0 @@
#include <iostream>
#include <utils/HsvTransform.h>
HsvTransform::HsvTransform()
: _saturationGain(1.0)
, _valueGain(1.0)
{
}
HsvTransform::HsvTransform(double saturationGain, double valueGain) :
_saturationGain(saturationGain),
_valueGain(valueGain)
{
}
HsvTransform::~HsvTransform()
{
}
void HsvTransform::setSaturationGain(double saturationGain)
{
_saturationGain = saturationGain;
}
double HsvTransform::getSaturationGain() const
{
return _saturationGain;
}
void HsvTransform::setValueGain(double valueGain)
{
_valueGain = valueGain;
}
double HsvTransform::getValueGain() const
{
return _valueGain;
}
void HsvTransform::transform(uint8_t & red, uint8_t & green, uint8_t & blue) const
{
if (_saturationGain != 1.0 || _valueGain != 1.0)
{
uint16_t hue;
uint8_t saturation, value;
rgb2hsv(red, green, blue, hue, saturation, value);
int s = saturation * _saturationGain;
if (s > 255)
saturation = 255;
else
saturation = s;
int v = value * _valueGain;
if (v > 255)
value = 255;
else
value = v;
hsv2rgb(hue, saturation, value, red, green, blue);
}
}
void HsvTransform::rgb2hsv(uint8_t red, uint8_t green, uint8_t blue, uint16_t & hue, uint8_t & saturation, uint8_t & value)
{
uint8_t rgbMin, rgbMax;
rgbMin = red < green ? (red < blue ? red : blue) : (green < blue ? green : blue);
rgbMax = red > green ? (red > blue ? red : blue) : (green > blue ? green : blue);
value = rgbMax;
if (value == 0)
{
hue = 0;
saturation = 0;
return;
}
saturation = 255 * long(rgbMax - rgbMin) / value;
if (saturation == 0)
{
hue = 0;
return;
}
if (rgbMax == red)
{
// start from 360 to be sure that we won't assign a negative number to the unsigned hue value
hue = 360 + 60 * (green - blue) / (rgbMax - rgbMin);
if (hue > 359)
hue -= 360;
}
else if (rgbMax == green)
{
hue = 120 + 60 * (blue - red) / (rgbMax - rgbMin);
}
else
{
hue = 240 + 60 * (red - green) / (rgbMax - rgbMin);
}
}
void HsvTransform::hsv2rgb(uint16_t hue, uint8_t saturation, uint8_t value, uint8_t & red, uint8_t & green, uint8_t & blue)
{
uint8_t region, remainder, p, q, t;
if (saturation == 0)
{
red = value;
green = value;
blue = value;
return;
}
region = hue / 60;
remainder = (hue - (region * 60)) * 256 / 60;
p = (value * (255 - saturation)) >> 8;
q = (value * (255 - ((saturation * remainder) >> 8))) >> 8;
t = (value * (255 - ((saturation * (255 - remainder)) >> 8))) >> 8;
switch (region)
{
case 0:
red = value; green = t; blue = p;
break;
case 1:
red = q; green = value; blue = p;
break;
case 2:
red = p; green = value; blue = t;
break;
case 3:
red = p; green = q; blue = value;
break;
case 4:
red = t; green = p; blue = value;
break;
default:
red = value; green = p; blue = q;
break;
}
}

View File

@@ -1,101 +0,0 @@
// STL includes
#include <cmath>
// Utils includes
#include <utils/RgbChannelTransform.h>
RgbChannelTransform::RgbChannelTransform()
{
setTransform(0.0, 1.0, 0.0, 1.0);
}
RgbChannelTransform::RgbChannelTransform(double threshold, double gamma, double blacklevel, double whitelevel)
{
setTransform(threshold, gamma, blacklevel, whitelevel);
}
RgbChannelTransform::~RgbChannelTransform()
{
}
void RgbChannelTransform::setTransform(double threshold, double gamma, double blacklevel, double whitelevel)
{
_threshold = threshold;
_gamma = gamma;
_blacklevel = blacklevel;
_whitelevel = whitelevel;
initializeMapping();
}
double RgbChannelTransform::getThreshold() const
{
return _threshold;
}
void RgbChannelTransform::setThreshold(double threshold)
{
setTransform(threshold, _gamma, _blacklevel, _whitelevel);
}
double RgbChannelTransform::getGamma() const
{
return _gamma;
}
void RgbChannelTransform::setGamma(double gamma)
{
setTransform(_threshold, gamma, _blacklevel, _whitelevel);
}
double RgbChannelTransform::getBlacklevel() const
{
return _blacklevel;
}
void RgbChannelTransform::setBlacklevel(double blacklevel)
{
setTransform(_threshold, _gamma, blacklevel, _whitelevel);
}
double RgbChannelTransform::getWhitelevel() const
{
return _whitelevel;
}
void RgbChannelTransform::setWhitelevel(double whitelevel)
{
setTransform(_threshold, _gamma, _blacklevel, whitelevel);
}
void RgbChannelTransform::initializeMapping()
{
// initialize the mapping as a linear array
for (int i = 0; i < 256; ++i)
{
double output = i / 255.0;
// apply linear transform
if (output < _threshold)
{
output = 0.0;
}
// apply gamma correction
output = std::pow(output, _gamma);
// apply blacklevel and whitelevel
output = _blacklevel + (_whitelevel - _blacklevel) * output;
// calc mapping
int mappingValue = output * 255;
if (mappingValue < 0)
{
mappingValue = 0;
}
else if (mappingValue > 255)
{
mappingValue = 255;
}
_mapping[i] = mappingValue;
}
}

View File

@@ -0,0 +1,114 @@
#include <iostream>
#include <cmath>
#include <utils/RgbTransform.h>
RgbTransform::RgbTransform()
{
init(1.0, 1.0, 1.0, 0.0, 1.0);
}
RgbTransform::RgbTransform(double gammaR, double gammaG, double gammaB, double brightnessLow, double brightnessHigh)
{
init(gammaR, gammaG, gammaB, brightnessLow, brightnessHigh);
}
void RgbTransform::init(double gammaR, double gammaG, double gammaB, double brightnessLow, double brightnessHigh)
{
setGamma(gammaR,gammaG,gammaB);
setBrightnessMin(brightnessLow);
setBrightness(brightnessHigh);
initializeMapping();
}
RgbTransform::~RgbTransform()
{
}
double RgbTransform::getGammaR() const
{
return _gammaR;
}
double RgbTransform::getGammaG() const
{
return _gammaG;
}
double RgbTransform::getGammaB() const
{
return _gammaB;
}
void RgbTransform::setGamma(double gammaR, double gammaG, double gammaB)
{
_gammaR = gammaR;
_gammaG = (gammaG < 0.0) ? _gammaR : gammaG;
_gammaB = (gammaB < 0.0) ? _gammaR : gammaB;
initializeMapping();
}
void RgbTransform::initializeMapping()
{
for (int i = 0; i < 256; ++i)
{
_mappingR[i] = std::min(std::max((int)(std::pow(i / 255.0, _gammaR) * 255), 0), 255);
_mappingG[i] = std::min(std::max((int)(std::pow(i / 255.0, _gammaG) * 255), 0), 255);
_mappingB[i] = std::min(std::max((int)(std::pow(i / 255.0, _gammaB) * 255), 0), 255);
}
}
double RgbTransform::getBrightnessMin() const
{
return _brightnessLow;
}
void RgbTransform::setBrightnessMin(double brightness)
{
_brightnessLow = brightness;
_sumBrightnessLow = 765.0 * ((std::pow(2.0,brightness*2)-1) / 3.0);
}
double RgbTransform::getBrightness() const
{
return _brightnessHigh;
}
void RgbTransform::setBrightness(double brightness)
{
_brightnessHigh = brightness;
_sumBrightnessHigh = 765.0 * ((std::pow(2.0,brightness*2)-1) / 3.0);
}
void RgbTransform::transform(uint8_t & red, uint8_t & green, uint8_t & blue)
{
// apply gamma
red = _mappingR[red];
green = _mappingR[green];
blue = _mappingR[blue];
//std::cout << (int)red << " " << (int)green << " " << (int)blue << " => ";
// apply brightnesss
if (red ==0) red = 1;
if (green==0) green = 1;
if (blue ==0) blue = 1;
int rgbSum = red+green+blue;
if (rgbSum > _sumBrightnessHigh)
{
double cH = _sumBrightnessHigh / rgbSum;
red *= cH;
green *= cH;
blue *= cH;
}
else if (rgbSum < _sumBrightnessLow)
{
double cL = _sumBrightnessLow / rgbSum;
red *= cL;
green *= cL;
blue *= cL;
}
//std::cout << (int)red << " " << (int)green << " " << (int)blue << std::endl;
}