v4l: make suggestions for all no signal values (#339)

* on v4l screenshot, print out nosignal threshold values

* separate fractional parameters for no signal detection

* fully implement handling for "rainbow grabber"

* hyperion-v4l2 --screenshot can also evaluate best reagion and color for no signal detection

* tune output of no signal detection

* v4l outputs warninsg only, todo: make it adyustable

* typo

* v4l signal detection, now with validation checks

* remove obsolete schema files

* fix code style

* fix text typos

* code style
This commit is contained in:
redPanther 2016-12-18 00:47:53 +01:00 committed by GitHub
parent 8aa0fbaa1e
commit 23194730c2
9 changed files with 264 additions and 157 deletions

View File

@ -7,8 +7,6 @@
<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-adjustment">schema/schema-adjustment.json</file>
<file alias="schema-effect">schema/schema-effect.json</file>
<file alias="schema-create-effect">schema/schema-create-effect.json</file>

View File

@ -1,37 +0,0 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["correction"]
},
"tan" : {
"type" : "integer"
},
"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

@ -1,37 +0,0 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["temperature"]
},
"tan" : {
"type" : "integer"
},
"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

@ -5,7 +5,7 @@
"command": {
"type" : "string",
"required" : true,
"enum" : ["color", "image", "effect", "create-effect", "delete-effect", "serverinfo", "clear", "clearall", "transform", "correction", "temperature", "adjustment", "sourceselect", "config", "componentstate", "ledcolors", "logging"]
"enum" : ["color", "image", "effect", "create-effect", "delete-effect", "serverinfo", "clear", "clearall", "transform", "adjustment", "sourceselect", "config", "componentstate", "ledcolors", "logging"]
}
}
}

View File

@ -2,10 +2,10 @@
#include <cmath>
#include <utils/HslTransform.h>
HslTransform::HslTransform() :
_saturationGain(1.0),
_luminanceGain(1.0),
_luminanceMinimum(0.0)
HslTransform::HslTransform()
: _saturationGain(1.0)
, _luminanceGain(1.0)
, _luminanceMinimum(0.0)
{
}
@ -57,12 +57,9 @@ void HslTransform::transform(uint8_t & red, uint8_t & green, uint8_t & blue) con
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;
saturation = std::min(s, 1.0f);
float l = luminance * _luminanceGain;
if (l < _luminanceMinimum)
@ -70,39 +67,36 @@ void HslTransform::transform(uint8_t & red, uint8_t & green, uint8_t & blue) con
saturation = 0;
l = _luminanceMinimum;
}
if (l > 1.0f)
luminance = 1.0f;
else
luminance = l;
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 = red / 255.0f;
float g = green / 255.0f;
float b = blue / 255.0f;
float r = (float)red / 255.0f;
float g = (float)green / 255.0f;
float b = (float)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 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) {
if (diff == 0.0f)
{
saturation = 0.0f;
hue = 0;
hue = 0;
return;
}
}
//saturation
if (luminance < 0.5f)
saturation = diff / (rgbMin + rgbMax);
else
saturation = diff / (2.0f - rgbMin - rgbMax);
saturation = (luminance < 0.5f)
? (diff / (rgbMin + rgbMax))
: (diff / (2.0f - rgbMin - rgbMax));
if (rgbMax == r)
{
@ -125,30 +119,29 @@ void HslTransform::rgb2hsl(uint8_t red, uint8_t green, uint8_t blue, uint16_t &
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);
if (saturation == 0.0f)
{
red = (uint8_t)(luminance * 255.0f);
green = (uint8_t)(luminance * 255.0f);
blue = (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 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++) {
for (int i = 0; i < 3; i++)
{
if (t[i] < 0.0f)
t[i] += 1.0f;
if (t[i] > 1.0f)
@ -157,7 +150,8 @@ void HslTransform::hsl2rgb(uint16_t hue, float saturation, float luminance, uint
float out[3];
for (int i = 0; i < 3; i++) {
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)
@ -168,8 +162,8 @@ void HslTransform::hsl2rgb(uint16_t hue, float saturation, float luminance, uint
}
//convert back to 0...255 range
red = (uint8_t)(out[0] * 255.0f);
red = (uint8_t)(out[0] * 255.0f);
green = (uint8_t)(out[1] * 255.0f);
blue = (uint8_t)(out[2] * 255.0f);
blue = (uint8_t)(out[2] * 255.0f);
}

View File

@ -1,9 +1,9 @@
#include <iostream>
#include <utils/HsvTransform.h>
HsvTransform::HsvTransform() :
_saturationGain(1.0),
_valueGain(1.0)
HsvTransform::HsvTransform()
: _saturationGain(1.0)
, _valueGain(1.0)
{
}

View File

@ -1,6 +1,8 @@
// Qt includes
#include <QImage>
#include <QCoreApplication>
#include <QVector>
#include <algorithm>
// hyperion-v4l2 includes
#include "ScreenshotHandler.h"
@ -17,41 +19,7 @@ ScreenshotHandler::~ScreenshotHandler()
void ScreenshotHandler::receiveImage(const Image<ColorRgb> & image)
{
double x_frac_min = _signalDetectionOffset.x();
double y_frac_min = _signalDetectionOffset.y();
double x_frac_max = _signalDetectionOffset.width();
double y_frac_max = _signalDetectionOffset.height();
int xOffset = image.width() * x_frac_min;
int yOffset = image.height() * y_frac_min;
int xMax = image.width() * x_frac_max;
int yMax = image.height() * y_frac_max;
std::cout << std::endl << "Screenshot details"
<< std::endl << "=================="
<< std::endl << "dimension after decimation: " << image.width() << " x " << image.height()
<< std::endl << "signal detection area : " << xOffset << "," << yOffset << " x " << xMax << "," << yMax << std::endl;
ColorRgb noSignalThresholdColor = {0,0,0};
for (unsigned x = 0; x < (image.width()>>1); ++x)
{
int xImage = (image.width()>>2) + x;
for (unsigned y = 0; y < (image.height()>>1); ++y)
{
int yImage = (image.height()>>2) + y;
ColorRgb rgb = image(xImage, yImage);
if (rgb > noSignalThresholdColor)
{
noSignalThresholdColor = rgb;
}
}
}
std::cout << "signal threshold color : " << noSignalThresholdColor << std::endl;
std::cout << "signal threshold values: " << (float)noSignalThresholdColor.red/255.0f << ", "<< (float)noSignalThresholdColor.green/255.0f << ", " << (float)noSignalThresholdColor.blue/255.0f << std::endl;
findNoSignalSettings(image);
// store as PNG
QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888);
pngImage.save(_filename);
@ -59,3 +27,220 @@ void ScreenshotHandler::receiveImage(const Image<ColorRgb> & image)
// Quit the application after the first image
QCoreApplication::quit();
}
bool ScreenshotHandler::findNoSignalSettings(const Image<ColorRgb> & image)
{
double x_frac_min = _signalDetectionOffset.x();
double y_frac_min = _signalDetectionOffset.y();
double x_frac_max = _signalDetectionOffset.width();
double y_frac_max = _signalDetectionOffset.height();
unsigned xOffset = image.width() * x_frac_min;
unsigned yOffset = image.height() * y_frac_min;
unsigned xMax = image.width() * x_frac_max;
unsigned yMax = image.height() * y_frac_max;
ColorRgb noSignalThresholdColor = {0,0,0};
unsigned yMid = (yMax+yOffset) / 2;
ColorRgb redThresoldColor = {255,75,75};
ColorRgb greenThresoldColor = {75,255,75};
ColorRgb blueThresoldColor = {75,75,255};
QVector<unsigned> redOffsets;
QVector<unsigned> redCounts;
QVector<unsigned> greenOffsets;
QVector<unsigned> greenCounts;
QVector<unsigned> blueOffsets;
QVector<unsigned> blueCounts;
unsigned currentColor = 255;
for (unsigned x = xOffset; x < xMax; ++x)
{
ColorRgb rgb = image(x, yMid);
if (rgb <= redThresoldColor)
{
if ( currentColor != 0)
{
redOffsets.append(x);
redCounts.append(1);
}
else
{
redCounts[redCounts.size()-1]++;
}
currentColor = 0;
}
if (rgb <= greenThresoldColor){
if ( currentColor != 1)
{
greenOffsets.append(x);
greenCounts.append(1);
}
else
{
greenCounts[greenCounts.size()-1]++;
}
currentColor = 1;
}
if (rgb <= blueThresoldColor)
{
if ( currentColor != 2)
{
blueOffsets.append(x);
blueCounts.append(1);
}
else
{
blueCounts[blueCounts.size()-1]++;
}
currentColor = 2;
}
}
auto itR = std::max_element(std::begin(redCounts), std::end(redCounts));
auto itG = std::max_element(std::begin(greenCounts), std::end(greenCounts));
auto itB = std::max_element(std::begin(blueCounts), std::end(blueCounts));
//std::cout << *itR << " " << *itG << " " << *itB << std::endl;
double xOffsetSuggested = xOffset;
double yOffsetSuggested = yOffset;
double xMaxSuggested = xMax;
double yMaxSuggested = yMax;
bool noSignalBlack = false;
noSignalThresholdColor = {0,0,0};
if (*itR >= *itG && *itR >= *itB && *itR > 1)
{
xOffsetSuggested = redOffsets[redCounts.indexOf(*itR)];
xMaxSuggested = xOffsetSuggested + *itR;
noSignalThresholdColor = redThresoldColor;
}
else if (*itG >= *itR && *itG >= *itB && *itG > 1 )
{
xOffsetSuggested = greenOffsets[greenCounts.indexOf(*itG)];
xMaxSuggested = xOffsetSuggested + *itG;
noSignalThresholdColor = greenThresoldColor;
}
else if ( *itB > 1 )
{
xOffsetSuggested = blueOffsets[blueCounts.indexOf(*itB)];
xMaxSuggested = xOffsetSuggested + *itB;
noSignalThresholdColor = blueThresoldColor;
}
else
{
noSignalThresholdColor = {75,75,75};
noSignalBlack = true;
}
// serach vertical max
if (!noSignalBlack)
{
unsigned xMid = (xMaxSuggested + xOffsetSuggested) / 2;
for (unsigned y = yMid; y >= yOffset && yOffsetSuggested != y; --y)
{
ColorRgb rgb = image(xMid, y);
if (rgb <= noSignalThresholdColor)
{
yOffsetSuggested = y;
}
}
for (unsigned y = yMid; y <= yMax && yMaxSuggested != y; ++y)
{
ColorRgb rgb = image(xMid, y);
if (rgb <= noSignalThresholdColor)
{
yMaxSuggested = y;
}
}
}
// optimize thresold color
noSignalThresholdColor = {0,0,0};
for (unsigned x = xOffsetSuggested; x < xMaxSuggested; ++x)
{
for (unsigned y = yOffsetSuggested; y < yMaxSuggested; ++y)
{
ColorRgb rgb = image(x, y);
if (rgb >= noSignalThresholdColor)
{
noSignalThresholdColor = rgb;
}
}
}
// calculate fractional values
xOffsetSuggested = (int)(((float)xOffsetSuggested/image.width())*100+0.5)/100.0;
xMaxSuggested = (int)(((float)xMaxSuggested/image.width())*100)/100.0;
yOffsetSuggested = (int)(((float)yOffsetSuggested/image.height())*100+0.5)/100.0;
yMaxSuggested = (int)(((float)yMaxSuggested/image.height())*100)/100.0;
double thresholdRed = (int)(((float)noSignalThresholdColor.red/255.0f)*100+0.5)/100.0;
double thresholdGreen = (int)(((float)noSignalThresholdColor.green/255.0f)*100+0.5)/100.0;
double thresholdBlue = (int)(((float)noSignalThresholdColor.blue/255.0f)*100+0.5)/100.0;
thresholdRed = (thresholdRed<0.1f) ?0.1f : thresholdRed;
thresholdGreen = (thresholdGreen<0.1f)?0.1f : thresholdGreen;
thresholdBlue = (thresholdBlue<0.1f) ?0.1f : thresholdBlue;
std::cout << std::endl << "Signal detection informations"
<< std::endl << "============================="
<< std::endl << "dimension after decimation: " << image.width() << " x " << image.height()
<< std::endl << "signal detection area : " << xOffset << "," << yOffset << " x " << xMax << "," << yMax << std::endl << std::endl;
// check if values make sense
if (thresholdRed < 0.5 && thresholdGreen < 0.5 && thresholdBlue < 0.5 && thresholdRed > 0.15 && thresholdGreen > 0.15 && thresholdBlue > 0.15)
{
std::cout << "WARNING \"no signal image\" is to dark, signal detection is not relaiable." << std::endl;
}
if (thresholdRed > 0.5 && thresholdGreen > 0.5 && thresholdBlue > 0.5)
{
std::cout << "WARNING \"no signal image\" is to bright, signal detection is not relaiable." << std::endl;
}
if (thresholdRed > thresholdGreen && thresholdRed > thresholdBlue && ((thresholdRed-thresholdGreen) <= 0.5 || (thresholdRed-thresholdBlue) <= 0.5))
{
std::cout << "WARNING difference between threshold color and the other color components is to small, signal detection might have problems." << std::endl;
}
if (thresholdGreen > thresholdRed && thresholdGreen > thresholdBlue && ((thresholdGreen-thresholdRed) <= 0.5 || (thresholdGreen-thresholdBlue) <= 0.5))
{
std::cout << "WARNING difference between threshold color and the other color components is to small, signal detection might have problems." << std::endl;
}
if (thresholdBlue > thresholdGreen && thresholdBlue > thresholdRed && ((thresholdBlue-thresholdGreen) <= 0.5 || (thresholdBlue-thresholdRed) <= 0.5))
{
std::cout << "WARNING difference between threshold color and the other color components is to small, signal detection might have problems." << std::endl;
}
if (noSignalBlack)
{
std::cout << "WARNING no red, green or blue \"no signal area\" detected, signal detection might have problems." << std::endl;
}
if (xOffsetSuggested >= xMaxSuggested || (xMaxSuggested - xOffsetSuggested) < 0.029 )
{
std::cout << "WARNING horizontal values of signal detection are invalid or detection area is to small, signal detection is not relaiable." << std::endl;
}
if (yOffsetSuggested >= yMaxSuggested || (yMaxSuggested - yOffsetSuggested) < 0.029 )
{
std::cout << "WARNING horizontal values of signal detection are invalid or detection area is to small, signal detection is not relaiable." << std::endl;
}
std::cout << std::endl
<< "suggested config values for signal detection:" << std::endl
<< "\t\"redSignalThreshold\" : " << thresholdRed << "," << std::endl
<< "\t\"greenSignalThreshold\" : " << thresholdGreen << "," << std::endl
<< "\t\"blueSignalThreshold\" : " << thresholdBlue << "," << std::endl
<< "\t\"signalDetectionHorizontalOffsetMin\" : " << xOffsetSuggested << "," << std::endl
<< "\t\"signalDetectionVerticalOffsetMin\" : " << yOffsetSuggested << "," << std::endl
<< "\t\"signalDetectionHorizontalOffsetMax\" : " << xMaxSuggested << "," << std::endl
<< "\t\"signalDetectionVerticalOffsetMax\" : " << yMaxSuggested << std::endl;
return true;
}

View File

@ -21,6 +21,8 @@ public slots:
void receiveImage(const Image<ColorRgb> & image);
private:
bool findNoSignalSettings(const Image<ColorRgb> & image);
const QString _filename;
const QRectF _signalDetectionOffset;
};

View File

@ -35,6 +35,8 @@ void saveScreenshot(QString filename, const Image<ColorRgb> & image)
int main(int argc, char** argv)
{
Logger *log = Logger::getInstance("V4L2GRABBER");
Logger::setLogLevel(Logger::WARNING);
std::cout
<< "hyperion-v4l2:" << std::endl
<< "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl