mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
ColorTransform functions added to repo
This commit is contained in:
parent
cbbb1d740b
commit
491d6ff608
@ -14,6 +14,8 @@ add_library(hyperion
|
|||||||
${CURRENT_SOURCE_DIR}/LedString.cpp
|
${CURRENT_SOURCE_DIR}/LedString.cpp
|
||||||
${CURRENT_SOURCE_DIR}/Hyperion.cpp
|
${CURRENT_SOURCE_DIR}/Hyperion.cpp
|
||||||
${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp
|
${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp
|
||||||
|
${CURRENT_SOURCE_DIR}/ColorTransform.h
|
||||||
|
${CURRENT_SOURCE_DIR}/ColorTransform.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(hyperion
|
target_link_libraries(hyperion
|
||||||
|
102
libsrc/hyperion/ColorTransform.cpp
Normal file
102
libsrc/hyperion/ColorTransform.cpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "ColorTransform.h"
|
||||||
|
|
||||||
|
ColorTransform::ColorTransform() :
|
||||||
|
_threshold(0),
|
||||||
|
_gamma(1.0),
|
||||||
|
_blacklevel(0.0),
|
||||||
|
_whitelevel(1.0)
|
||||||
|
{
|
||||||
|
initializeMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorTransform::ColorTransform(double threshold, double gamma, double blacklevel, double whitelevel) :
|
||||||
|
_threshold(threshold),
|
||||||
|
_gamma(gamma),
|
||||||
|
_blacklevel(blacklevel),
|
||||||
|
_whitelevel(whitelevel)
|
||||||
|
{
|
||||||
|
initializeMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorTransform::~ColorTransform()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
double ColorTransform::getThreshold() const
|
||||||
|
{
|
||||||
|
return _threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorTransform::setThreshold(double threshold)
|
||||||
|
{
|
||||||
|
_threshold = threshold;
|
||||||
|
initializeMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
double ColorTransform::getGamma() const
|
||||||
|
{
|
||||||
|
return _gamma;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorTransform::setGamma(double gamma)
|
||||||
|
{
|
||||||
|
_gamma = gamma;
|
||||||
|
initializeMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
double ColorTransform::getBlacklevel() const
|
||||||
|
{
|
||||||
|
return _blacklevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorTransform::setBlacklevel(double blacklevel)
|
||||||
|
{
|
||||||
|
_blacklevel = blacklevel;
|
||||||
|
initializeMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
double ColorTransform::getWhitelevel() const
|
||||||
|
{
|
||||||
|
return _whitelevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorTransform::setWhitelevel(double whitelevel)
|
||||||
|
{
|
||||||
|
_whitelevel = whitelevel;
|
||||||
|
initializeMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorTransform::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;
|
||||||
|
}
|
||||||
|
}
|
49
libsrc/hyperion/ColorTransform.h
Normal file
49
libsrc/hyperion/ColorTransform.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "stdint.h"
|
||||||
|
|
||||||
|
/// Transform for a single color byte value
|
||||||
|
///
|
||||||
|
/// Transforms are applied in the following order:
|
||||||
|
/// 1) a threshold is applied. All values below threshold will be set to zero
|
||||||
|
/// 2) gamma color correction is applied
|
||||||
|
/// 3) the output value is scaled from the [0:1] to [blacklevel:whitelevel]
|
||||||
|
/// 4) finally, in case of a weird choice of parameters, the output is clamped between [0:1]
|
||||||
|
///
|
||||||
|
/// All configuration values are doubles and assume the color value to be between 0 and 1
|
||||||
|
class ColorTransform
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ColorTransform();
|
||||||
|
ColorTransform(double threshold, double gamma, double whitelevel, double blacklevel);
|
||||||
|
~ColorTransform();
|
||||||
|
|
||||||
|
double getThreshold() const;
|
||||||
|
void setThreshold(double threshold);
|
||||||
|
|
||||||
|
double getGamma() const;
|
||||||
|
void setGamma(double gamma);
|
||||||
|
|
||||||
|
double getBlacklevel() const;
|
||||||
|
void setBlacklevel(double blacklevel);
|
||||||
|
|
||||||
|
double getWhitelevel() const;
|
||||||
|
void setWhitelevel(double whitelevel);
|
||||||
|
|
||||||
|
/// get the transformed value for the given byte value
|
||||||
|
uint8_t transform(uint8_t input) const
|
||||||
|
{
|
||||||
|
return _mapping[input];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initializeMapping();
|
||||||
|
|
||||||
|
private:
|
||||||
|
double _threshold;
|
||||||
|
double _gamma;
|
||||||
|
double _blacklevel;
|
||||||
|
double _whitelevel;
|
||||||
|
|
||||||
|
uint8_t _mapping[256];
|
||||||
|
};
|
@ -1,3 +1,5 @@
|
|||||||
|
# Needed for testing non-public components
|
||||||
|
include_directories(../libsrc)
|
||||||
|
|
||||||
# Add the simple test executable 'TestSpi'
|
# Add the simple test executable 'TestSpi'
|
||||||
add_executable(TestSpi
|
add_executable(TestSpi
|
||||||
@ -25,13 +27,17 @@ add_executable(TestRgbImage
|
|||||||
target_link_libraries(TestRgbImage
|
target_link_libraries(TestRgbImage
|
||||||
hyperion-utils)
|
hyperion-utils)
|
||||||
|
|
||||||
|
add_executable(TestColorTransform
|
||||||
|
TestColorTransform.cpp)
|
||||||
|
target_link_libraries(TestColorTransform
|
||||||
|
hyperion)
|
||||||
|
|
||||||
# Find the libPNG
|
# Find the libPNG
|
||||||
find_package(PNG REQUIRED QUIET)
|
find_package(PNG REQUIRED QUIET)
|
||||||
|
|
||||||
# Add additional includes dirs
|
|
||||||
include_directories(${PNG_INCLUDE_DIR})
|
|
||||||
|
|
||||||
#if(PNG_FOUND)
|
#if(PNG_FOUND)
|
||||||
|
# Add additional includes dirs
|
||||||
|
include_directories(${PNG_INCLUDE_DIR})
|
||||||
|
|
||||||
add_executable(TestHyperionPng
|
add_executable(TestHyperionPng
|
||||||
TestHyperionPng.cpp)
|
TestHyperionPng.cpp)
|
||||||
|
93
test/TestColorTransform.cpp
Normal file
93
test/TestColorTransform.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <hyperion/ColorTransform.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::cout << "Testing linear transform" << std::endl;
|
||||||
|
ColorTransform t;
|
||||||
|
for (int i = 0; i < 256; ++i)
|
||||||
|
{
|
||||||
|
uint8_t input = i;
|
||||||
|
uint8_t output = t.transform(input);
|
||||||
|
uint8_t expected = input;
|
||||||
|
|
||||||
|
if (output != expected)
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: input (" << (int)input << ") => output (" << (int)output << ") : expected (" << (int) expected << ")" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "OK: input (" << (int)input << ") => output (" << (int)output << ")" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::cout << "Testing threshold" << std::endl;
|
||||||
|
ColorTransform t(.10, 1.0, 0.0, 1.0);
|
||||||
|
for (int i = 0; i < 256; ++i)
|
||||||
|
{
|
||||||
|
uint8_t input = i;
|
||||||
|
uint8_t output = t.transform(input);
|
||||||
|
uint8_t expected = ((i/255.0) < t.getThreshold() ? 0 : output);
|
||||||
|
|
||||||
|
if (output != expected)
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: input (" << (int)input << ") => output (" << (int)output << ") : expected (" << (int) expected << ")" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "OK: input (" << (int)input << ") => output (" << (int)output << ")" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::cout << "Testing blacklevel and whitelevel" << std::endl;
|
||||||
|
ColorTransform t(0, 1.0, 0.2, 0.8);
|
||||||
|
for (int i = 0; i < 256; ++i)
|
||||||
|
{
|
||||||
|
uint8_t input = i;
|
||||||
|
uint8_t output = t.transform(input);
|
||||||
|
uint8_t expected = (uint8_t)(input * (t.getWhitelevel()-t.getBlacklevel()) + 255 * t.getBlacklevel());
|
||||||
|
|
||||||
|
if (output != expected)
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: input (" << (int)input << ") => output (" << (int)output << ") : expected (" << (int) expected << ")" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "OK: input (" << (int)input << ") => output (" << (int)output << ")" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::cout << "Testing gamma" << std::endl;
|
||||||
|
ColorTransform t(0, 2.0, 0.0, 1.0);
|
||||||
|
for (int i = 0; i < 256; ++i)
|
||||||
|
{
|
||||||
|
uint8_t input = i;
|
||||||
|
uint8_t output = t.transform(input);
|
||||||
|
uint8_t expected = (uint8_t)(255 * std::pow(i / 255.0, 2));
|
||||||
|
|
||||||
|
if (output != expected)
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: input (" << (int)input << ") => output (" << (int)output << ") : expected (" << (int) expected << ")" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "OK: input (" << (int)input << ") => output (" << (int)output << ")" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user