- HSV transform added

- Moved transform of led values to before the device write so transform changes are taken into account
This commit is contained in:
johan 2013-08-21 21:50:17 +02:00
parent 271917bac7
commit ee7a596d53
14 changed files with 317 additions and 38 deletions

View File

@ -12,11 +12,14 @@
},
"color" :
{
"hsv" : {
"saturationGain" : 1.0,
"valueGain" : 1.0
},
"red" :
{
"threshold" : 0.0,
"gamma" : 1.0,
"adjust" : 1.0,
"blacklevel" : 0.0,
"whitelevel" : 1.0
},
@ -24,7 +27,6 @@
{
"threshold" : 0.0,
"gamma" : 1.0,
"adjust" : 1.0,
"blacklevel" : 0.0,
"whitelevel" : 1.0
},
@ -32,7 +34,6 @@
{
"threshold" : 0.0,
"gamma" : 1.0,
"adjust" : 1.0,
"blacklevel" : 0.0,
"whitelevel" : 1.0
}

View File

@ -33,6 +33,23 @@
"type":"object",
"required":true,
"properties": {
"hsv" : {
"type" : "object",
"required" : true,
"properties" : {
"saturationGain" : {
"type" : "number",
"required" : true,
"minimum" : 0.0
},
"valueGain" : {
"type" : "number",
"required" : true,
"minimum" : 0.0
}
},
"additionalProperties" : false
},
"red": {
"type":"object",
"required":true,
@ -41,10 +58,6 @@
"type":"number",
"required":true
},
"adjust": {
"type":"number",
"required":true
},
"blacklevel": {
"type":"number",
"required":true
@ -55,7 +68,9 @@
},
"threshold": {
"type":"number",
"required":true
"required":true,
"minimum" : 0.0,
"maximum" : 1.0
}
}
},
@ -67,13 +82,19 @@
"type":"number",
"required":true
},
"adjust": {
"type":"number",
"required":true
},
"blacklevel": {
"type":"number",
"required":true
},
"whitelevel": {
"type":"number",
"required":true
},
"threshold": {
"type":"number",
"required":true,
"minimum" : 0.0,
"maximum" : 1.0
}
}
},
@ -85,13 +106,19 @@
"type":"number",
"required":true
},
"adjust": {
"whitelevel": {
"type":"number",
"required":true
},
"blacklevel": {
"type":"number",
"required":true
},
"threshold": {
"type":"number",
"required":true,
"minimum" : 0.0,
"maximum" : 1.0
}
}
}

View File

@ -16,8 +16,10 @@
#include <hyperion/PriorityMuxer.h>
// Forward class declaration
namespace hyperion { class ColorTransform; }
namespace hyperion {
class HsvTransform;
class ColorTransform;
}
class Hyperion : public QObject
{
@ -27,12 +29,12 @@ public:
enum Color
{
RED, GREEN, BLUE
RED, GREEN, BLUE, INVALID
};
enum Transform
{
THRESHOLD, GAMMA, BLACKLEVEL, WHITELEVEL
SATURATION_GAIN, VALUE_GAIN, THRESHOLD, GAMMA, BLACKLEVEL, WHITELEVEL
};
static LedString createLedString(const Json::Value& ledsConfig);
@ -69,6 +71,7 @@ private:
PriorityMuxer _muxer;
hyperion::HsvTransform * _hsvTransform;
hyperion::ColorTransform * _redTransform;
hyperion::ColorTransform * _greenTransform;
hyperion::ColorTransform * _blueTransform;

View File

@ -8,8 +8,7 @@
#include <hyperion/ImageProcessorFactory.h>
// Forward class declaration
namespace hyperion { class ImageToLedsMap;
class ColorTransform; }
namespace hyperion { class ImageToLedsMap; }
/**
* The ImageProcessor translates an RGB-image to RGB-values for the leds. The processing is

View File

@ -19,6 +19,7 @@ SET(Hyperion_HEADERS
${CURRENT_SOURCE_DIR}/LedDeviceTest.h
${CURRENT_SOURCE_DIR}/ImageToLedsMap.h
${CURRENT_SOURCE_DIR}/ColorTransform.h
${CURRENT_SOURCE_DIR}/HsvTransform.h
)
SET(Hyperion_SOURCES
@ -32,6 +33,7 @@ SET(Hyperion_SOURCES
${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp
${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp
${CURRENT_SOURCE_DIR}/ColorTransform.cpp
${CURRENT_SOURCE_DIR}/HsvTransform.cpp
)
QT4_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS})

View File

@ -0,0 +1,134 @@
#include "HsvTransform.h"
using namespace hyperion;
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)
{
uint8_t hue, 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, uint8_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)
hue = 0 + 43 * (green - blue) / (rgbMax - rgbMin);
else if (rgbMax == green)
hue = 85 + 43 * (blue - red) / (rgbMax - rgbMin);
else
hue = 171 + 43 * (red - green) / (rgbMax - rgbMin);
}
void HsvTransform::hsv2rgb(uint8_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 / 43;
remainder = (hue - (region * 43)) * 6;
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

@ -0,0 +1,33 @@
#pragma once
#include <cstdint>
namespace hyperion
{
class HsvTransform
{
public:
HsvTransform();
HsvTransform(double saturationGain, double valueGain);
~HsvTransform();
void setSaturationGain(double saturationGain);
double getSaturationGain() const;
void setValueGain(double valueGain);
double getValueGain() const;
void transform(uint8_t & red, uint8_t & green, uint8_t & blue) const;
private:
// integer version of the conversion are faster, but a little less accurate
static void rgb2hsv(uint8_t red, uint8_t green, uint8_t blue, uint8_t & hue, uint8_t & saturation, uint8_t & value);
static void hsv2rgb(uint8_t hue, uint8_t saturation, uint8_t value, uint8_t & red, uint8_t & green, uint8_t & blue);
private:
double _saturationGain;
double _valueGain;
};
} // namespace hyperion

View File

@ -15,6 +15,7 @@
#include "LedDeviceWs2801.h"
#include "LedDeviceTest.h"
#include "ColorTransform.h"
#include "HsvTransform.h"
using namespace hyperion;
@ -45,6 +46,11 @@ LedDevice* constructDevice(const Json::Value& deviceConfig)
return device;
}
HsvTransform * createHsvTransform(const Json::Value & hsvConfig)
{
return new HsvTransform(hsvConfig["saturationGain"].asDouble(), hsvConfig["valueGain"].asDouble());
}
ColorTransform* createColorTransform(const Json::Value& colorConfig)
{
const double threshold = colorConfig["threshold"].asDouble();
@ -78,6 +84,7 @@ LedString Hyperion::createLedString(const Json::Value& ledsConfig)
Hyperion::Hyperion(const Json::Value &jsonConfig) :
_ledString(createLedString(jsonConfig["leds"])),
_muxer(_ledString.leds().size()),
_hsvTransform(createHsvTransform(jsonConfig["color"]["hsv"])),
_redTransform(createColorTransform(jsonConfig["color"]["red"])),
_greenTransform(createColorTransform(jsonConfig["color"]["green"])),
_blueTransform(createColorTransform(jsonConfig["color"]["blue"])),
@ -99,6 +106,9 @@ Hyperion::~Hyperion()
// Delete the Led-String
delete _device;
// delete he hsv transform
delete _hsvTransform;
// Delete the color-transform
delete _blueTransform;
delete _greenTransform;
@ -121,14 +131,6 @@ void Hyperion::setColor(int priority, RgbColor & color, const int timeout_ms)
void Hyperion::setColors(int priority, std::vector<RgbColor>& ledColors, const int timeout_ms)
{
// Apply the transform to each led and color-channel
for (RgbColor& color : ledColors)
{
color.red = _redTransform->transform(color.red);
color.green = _greenTransform->transform(color.green);
color.blue = _blueTransform->transform(color.blue);
}
if (timeout_ms > 0)
{
const uint64_t timeoutTime = QDateTime::currentMSecsSinceEpoch() + timeout_ms;
@ -161,27 +163,40 @@ void Hyperion::setTransform(Hyperion::Transform transform, Hyperion::Color color
t = _blueTransform;
break;
default:
assert(false);
break;
}
// set transform value
switch (transform)
{
case SATURATION_GAIN:
_hsvTransform->setSaturationGain(value);
break;
case VALUE_GAIN:
_hsvTransform->setValueGain(value);
break;
case THRESHOLD:
assert (t != nullptr);
t->setThreshold(value);
break;
case GAMMA:
assert (t != nullptr);
t->setGamma(value);
break;
case BLACKLEVEL:
assert (t != nullptr);
t->setBlacklevel(value);
break;
case WHITELEVEL:
assert (t != nullptr);
t->setWhitelevel(value);
break;
default:
assert(false);
}
// update the led output
update();
}
void Hyperion::clear(int priority)
@ -222,19 +237,27 @@ double Hyperion::getTransform(Hyperion::Transform transform, Hyperion::Color col
t = _blueTransform;
break;
default:
assert(false);
break;
}
// set transform value
switch (transform)
{
case SATURATION_GAIN:
return _hsvTransform->getSaturationGain();
case VALUE_GAIN:
return _hsvTransform->getValueGain();
case THRESHOLD:
assert (t != nullptr);
return t->getThreshold();
case GAMMA:
assert (t != nullptr);
return t->getGamma();
case BLACKLEVEL:
assert (t != nullptr);
return t->getBlacklevel();
case WHITELEVEL:
assert (t != nullptr);
return t->getWhitelevel();
default:
assert(false);
@ -262,8 +285,18 @@ 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<RgbColor> ledColors(priorityInfo.ledColors);
for (RgbColor& color : ledColors)
{
_hsvTransform->transform(color.red, color.green, color.blue);
color.red = _redTransform->transform(color.red);
color.green = _greenTransform->transform(color.green);
color.blue = _blueTransform->transform(color.blue);
}
// Write the data to the device
_device->write(priorityInfo.ledColors);
_device->write(ledColors);
// Start the timeout-timer
if (priorityInfo.timeoutTime_ms == -1)

View File

@ -171,6 +171,8 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &message)
// collect transform information
Json::Value & transform = info["transform"];
transform["saturationGain"] = _hyperion->getTransform(Hyperion::SATURATION_GAIN, Hyperion::INVALID);
transform["valueGain"] = _hyperion->getTransform(Hyperion::VALUE_GAIN, Hyperion::INVALID);
Json::Value & threshold = transform["threshold"];
threshold.append(_hyperion->getTransform(Hyperion::THRESHOLD, Hyperion::RED));
threshold.append(_hyperion->getTransform(Hyperion::THRESHOLD, Hyperion::GREEN));
@ -217,6 +219,16 @@ void JsonClientConnection::handleTransformCommand(const Json::Value &message)
{
const Json::Value & transform = message["transform"];
if (transform.isMember("saturationGain"))
{
_hyperion->setTransform(Hyperion::SATURATION_GAIN, Hyperion::INVALID, transform["saturationGain"].asDouble());
}
if (transform.isMember("valueGain"))
{
_hyperion->setTransform(Hyperion::VALUE_GAIN, Hyperion::INVALID, transform["valueGain"].asDouble());
}
if (transform.isMember("threshold"))
{
const Json::Value & threshold = transform["threshold"];

View File

@ -11,6 +11,16 @@
"type": "object",
"required": true,
"properties": {
"saturationGain" : {
"type" : "double",
"required" : false,
"minimum" : 0.0
},
"valueGain" : {
"type" : "double",
"required" : false,
"minimum" : 0.0
},
"threshold": {
"type": "array",
"required": false,

View File

@ -30,5 +30,5 @@ qt4_use_modules(hyperion-remote
Network)
target_link_libraries(hyperion-remote
hyperion-utils
jsoncpp
getoptPlusPlus)

View File

@ -160,7 +160,7 @@ void JsonConnection::clearAll()
parseReply(reply);
}
void JsonConnection::setTransform(ColorTransformValues *threshold, ColorTransformValues *gamma, ColorTransformValues *blacklevel, ColorTransformValues *whitelevel)
void JsonConnection::setTransform(double * saturation, double * value, ColorTransformValues *threshold, ColorTransformValues *gamma, ColorTransformValues *blacklevel, ColorTransformValues *whitelevel)
{
std::cout << "Set color transforms" << std::endl;
@ -169,6 +169,16 @@ void JsonConnection::setTransform(ColorTransformValues *threshold, ColorTransfor
command["command"] = "transform";
Json::Value & transform = command["transform"];
if (saturation != nullptr)
{
transform["saturationGain"] = *saturation;
}
if (value != nullptr)
{
transform["valueGain"] = *value;
}
if (threshold != nullptr)
{
Json::Value & v = transform["threshold"];

View File

@ -39,7 +39,13 @@ public:
/// Set the color transform of the leds
/// Note that providing a NULL will leave the settings on the server unchanged
void setTransform(ColorTransformValues * threshold, ColorTransformValues * gamma, ColorTransformValues * blacklevel, ColorTransformValues * whitelevel);
void setTransform(
double * saturation,
double * value,
ColorTransformValues * threshold,
ColorTransformValues * gamma,
ColorTransformValues * blacklevel,
ColorTransformValues * whitelevel);
private:
/// Send a json command message and receive its reply

View File

@ -42,9 +42,11 @@ int main(int argc, char * argv[])
IntParameter & argDuration = parameters.add<IntParameter> ('d', "duration" , "Specify how long the leds should be switched on in millseconds [default: infinity]");
ColorParameter & argColor = parameters.add<ColorParameter> ('c', "color" , "Set all leds to a constant color (either RRGGBB hex value or a color name)");
ImageParameter & argImage = parameters.add<ImageParameter> ('i', "image" , "Set the leds to the colors according to the given image file");
SwitchParameter<> & argServerInfo = parameters.add<SwitchParameter<> >('s', "info" , "List server info");
SwitchParameter<> & argServerInfo = parameters.add<SwitchParameter<> >('l', "list" , "List server info");
SwitchParameter<> & argClear = parameters.add<SwitchParameter<> >('x', "clear" , "Clear data for the priority channel provided by the -p option");
SwitchParameter<> & argClearAll = parameters.add<SwitchParameter<> >(0x0, "clearall" , "Clear data for all active priority channels");
DoubleParameter & argSaturation = parameters.add<DoubleParameter> ('s', "saturation", "Set the HSV saturation gain of the leds");
DoubleParameter & argValue = parameters.add<DoubleParameter> ('v', "value ", "Set the HSV value gain of the leds");
TransformParameter & argGamma = parameters.add<TransformParameter>('g', "gamma" , "Set the gamma of the leds (requires 3 space seperated values)");
TransformParameter & argThreshold = parameters.add<TransformParameter>('t', "threshold" , "Set the threshold of the leds (requires 3 space seperated values between 0.0 and 1.0)");
TransformParameter & argBlacklevel = parameters.add<TransformParameter>('b', "blacklevel", "Set the blacklevel of the leds (requires 3 space seperated values which are normally between 0.0 and 1.0)");
@ -68,7 +70,7 @@ int main(int argc, char * argv[])
}
// check if at least one of the available color transforms is set
bool colorTransform = argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet();
bool colorTransform = argSaturation.isSet() || argValue.isSet() || argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet();
// check that exactly one command was given
int commandCount = count({argColor.isSet(), argImage.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform});
@ -81,6 +83,8 @@ int main(int argc, char * argv[])
std::cerr << " " << argClear.usageLine() << std::endl;
std::cerr << " " << argClearAll.usageLine() << std::endl;
std::cerr << "or one or more of the available color transformations:" << std::endl;
std::cerr << " " << argSaturation.usageLine() << std::endl;
std::cerr << " " << argValue.usageLine() << std::endl;
std::cerr << " " << argThreshold.usageLine() << std::endl;
std::cerr << " " << argGamma.usageLine() << std::endl;
std::cerr << " " << argBlacklevel.usageLine() << std::endl;
@ -115,14 +119,19 @@ int main(int argc, char * argv[])
}
else if (colorTransform)
{
double saturation, value;
ColorTransformValues threshold, gamma, blacklevel, whitelevel;
if (argSaturation.isSet()) saturation = argSaturation.getValue();
if (argValue.isSet()) value = argValue.getValue();
if (argThreshold.isSet()) threshold = argThreshold.getValue();
if (argGamma.isSet()) gamma = argGamma.getValue();
if (argBlacklevel.isSet()) blacklevel = argBlacklevel.getValue();
if (argWhitelevel.isSet()) whitelevel = argWhitelevel.getValue();
connection.setTransform(
argSaturation.isSet() ? &saturation : nullptr,
argValue.isSet() ? &value : nullptr,
argThreshold.isSet() ? &threshold : nullptr,
argGamma.isSet() ? &gamma : nullptr,
argBlacklevel.isSet() ? &blacklevel : nullptr,