Commit 1b637157 authored by dionoea's avatar dionoea
Browse files

video filter module - for thehomebrewAmbiLight (AtmoLight) [2/3]: atmo-video_filter.diff

parent bcb07441
/*
* calculations.c: calculations needed by the input devices
*
* See the README file for copyright information and how to reach the author.
*
* $Id$
*/
#include <stdlib.h>
#include <string.h>
#include "AtmoDefs.h"
#include "AtmoCalculations.h"
#include "AtmoConfig.h"
#include "AtmoZoneDefinition.h"
// set accuracy of color calculation
#define h_MAX 255
#define s_MAX 255
#define v_MAX 255
// macros
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
#define POS_DIV(a, b) ( (a)/(b) + ( ((a)%(b) >= (b)/2 ) ? 1 : 0) )
tColorPacket CalcColorsAnalyzeHSV(CAtmoConfig *pAtmoConfig, tHSVColor *HSV_Img)
{
int i; // counter
// static tWeightPacket Weight[IMAGE_SIZE];
// Flip instead having a array with (64x48) entries of values for each channel
// I have x arrays of 64x48 so each channel has its own array...
// (or gradient which is use to judge about the pixels)
static int Weight[ATMO_NUM_CHANNELS][IMAGE_SIZE];
/***************************************************************************/
/* Weight */
/***************************************************************************/
static int LastEdgeWeighting = -1;
static int LastWidescreenMode = -1;
int AtmoSetup_EdgeWeighting = pAtmoConfig->getLiveView_EdgeWeighting();
int AtmoSetup_WidescreenMode = pAtmoConfig->getLiveView_WidescreenMode();
int AtmoSetup_DarknessLimit = pAtmoConfig->getLiveView_DarknessLimit();
int AtmoSetup_BrightCorrect = pAtmoConfig->getLiveView_BrightCorrect();
int AtmoSetup_SatWinSize = pAtmoConfig->getLiveView_SatWinSize();
// calculate only if setup has changed
if ((AtmoSetup_EdgeWeighting != LastEdgeWeighting) ||
(AtmoSetup_WidescreenMode != LastWidescreenMode))
{
for(i =0 ;i < ATMO_NUM_CHANNELS; i++)
pAtmoConfig->getZoneDefinition(i)->UpdateWeighting(&Weight[i][0],
AtmoSetup_WidescreenMode,
AtmoSetup_EdgeWeighting);
/*
original code from VDR sources... my one is just more flexible?*g*
i = 0;
for (int row = 0; row < CAP_HEIGHT; row++)
{
float row_norm = (float)row / ((float)CAP_HEIGHT - 1.0f); // [0;Height] -> [0;1]
float weight_3 = pow(1.0f - row_norm, AtmoSetup_EdgeWeighting); // top
float weight_4 = pow(row_norm, AtmoSetup_EdgeWeighting); // bottom
for (int column = 0; column < CAP_WIDTH; column++)
{
// if widescreen mode, top and bottom of the picture are not
if ((AtmoSetup_WidescreenMode == 1) && ((row <= CAP_HEIGHT/8) || (row >= (7*CAP_HEIGHT)/8)))
{
Weight[i].channel[0] = Weight[i].channel[1] = Weight[i].channel[2] = Weight[i].channel[3] = Weight[i].channel[4] = 0;
}
else
{
float column_norm = (float)column / ((float)CAP_WIDTH - 1.0f); // [0;Width] -> [0;1]
Weight[i].channel[0] = 255;
Weight[i].channel[1] = (int)(255.0 * (float)pow((1.0 - column_norm), AtmoSetup_EdgeWeighting));
Weight[i].channel[2] = (int)(255.0 * (float)pow(column_norm, AtmoSetup_EdgeWeighting));
Weight[i].channel[3] = (int)(255.0 * (float)weight_3);
Weight[i].channel[4] = (int)(255.0 * (float)weight_4);
}
i++;
}
}
*/
LastEdgeWeighting = AtmoSetup_EdgeWeighting;
LastWidescreenMode = AtmoSetup_WidescreenMode;
}
/***************************************************************************/
/* Hue */
/***************************************************************************/
/*----------------------------*/
/* hue histogram builtup */
/*----------------------------*/
// HSV histogram
long int hue_hist[ATMO_NUM_CHANNELS][h_MAX+1];
// clean histogram
memset(&hue_hist, 0, sizeof(hue_hist));
i = 0;
for (int row = 0; row < CAP_HEIGHT; row++)
{
for (int column = 0; column < CAP_WIDTH; column++)
{
// forget black bars: perform calculations only if pixel has some luminosity
if (HSV_Img[i].v > 10*AtmoSetup_DarknessLimit)
{
// builtup histogram for the 5 channels
for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
{
// Add weight to channel
hue_hist[channel][HSV_Img[i].h] += Weight[channel][i] * HSV_Img[i].v;
}
}
i++;
}
}
/*----------------------------*/
/* hue histogram windowing */
/*----------------------------*/
// windowed HSV histogram
long int w_hue_hist[ATMO_NUM_CHANNELS][h_MAX+1];
// clean windowed histogram
memset(&w_hue_hist, 0, sizeof(w_hue_hist));
// steps in each direction; eg. 2 => -2 -1 0 1 2 windowing
int hue_windowsize = pAtmoConfig->getLiveView_HueWinSize();
for (i = 0; i < h_MAX+1; i++) // walk through histogram [0;h_MAX]
{
// windowing from -hue_windowsize -> +hue_windowsize
for (int mywin = -hue_windowsize; mywin < hue_windowsize+1; mywin++)
{
// adressed histogram candlestick
int myidx = i + mywin;
// handle beginning of windowing -> roll back
if (myidx < 0) { myidx = myidx + h_MAX + 1; }
// handle end of windowing -> roll forward
if (myidx > h_MAX) { myidx = myidx - h_MAX - 1; }
// Apply windowing to all 5 channels
for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
{
// apply lite triangular window design with gradient of 10% per discrete step
w_hue_hist[channel][i] += hue_hist[channel][myidx] * ((hue_windowsize+1)-abs(mywin)); // apply window
}
}
}
/*--------------------------------------*/
/* analyze histogram for most used hue */
/*--------------------------------------*/
// index of last maximum
static int most_used_hue_last[ATMO_NUM_CHANNELS] = {0, 0, 0, 0, 0};
// resulting hue for each channel
int most_used_hue[ATMO_NUM_CHANNELS];
memset(&most_used_hue, 0, sizeof(most_used_hue));
for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
{
int value = 0;
for (i = 0; i < h_MAX+1; i++) // walk through histogram
{
if (w_hue_hist[channel][i] > value) // if new value bigger then old one
{
most_used_hue[channel] = i; // remember index
value = w_hue_hist[channel][i]; // and value
}
}
float percent = (float)w_hue_hist[channel][most_used_hue_last[channel]] / (float)value;
if (percent > 0.93f) // less than 7% difference?
{
most_used_hue[channel] = most_used_hue_last[channel]; // use last index
}
most_used_hue_last[channel] = most_used_hue[channel]; // save current index of most used hue
}
/***************************************************************************/
/* saturation */
/***************************************************************************/
// sat histogram
long int sat_hist[ATMO_NUM_CHANNELS][s_MAX+1];
// hue of the pixel we are working at
int pixel_hue = 0;
// clean histogram
memset(&sat_hist, 0, sizeof(sat_hist));
/*--------------------------------------*/
/* saturation histogram builtup */
/*--------------------------------------*/
i = 0;
for (int row = 0; row < CAP_HEIGHT; row++)
{
for (int column = 0; column < CAP_WIDTH; column++)
{
// forget black bars: perform calculations only if pixel has some luminosity
if (HSV_Img[i].v > 10*AtmoSetup_DarknessLimit)
{
// find histogram position for pixel
pixel_hue = HSV_Img[i].h;
// TODO: brightness calculation(if we require it some time)
for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
{
// only use pixel for histogram if hue is near most_used_hue
if ((pixel_hue > most_used_hue[channel] - hue_windowsize) &&
(pixel_hue < most_used_hue[channel] + hue_windowsize))
{
// build histogram
// sat_hist[channel][HSV_Img[i].s] += Weight[i].channel[channel] * HSV_Img[i].v;
sat_hist[channel][HSV_Img[i].s] += Weight[channel][i] * HSV_Img[i].v;
}
}
}
i++;
}
}
/*--------------------------------------*/
/* saturation histogram windowing */
/*--------------------------------------*/
// windowed HSV histogram
long int w_sat_hist[ATMO_NUM_CHANNELS][s_MAX+1];
// clean windowed histogram
memset(&w_sat_hist, 0, sizeof(w_sat_hist));
// steps in each direction; eg. 2 => -2 -1 0 1 2 windowing
int sat_windowsize = AtmoSetup_SatWinSize;
// walk through histogram [0;h_MAX]
for (i = 0; i < s_MAX + 1; i++)
{
// windowing from -hue_windowsize -> +hue_windowsize
for (int mywin = -sat_windowsize; mywin < sat_windowsize+1; mywin++)
{
// adressed histogram candlestick
int myidx = i + mywin;
// handle beginning of windowing -> roll back
if (myidx < 0) { myidx = myidx + s_MAX + 1; }
// handle end of windowing -> roll forward
if (myidx > h_MAX) { myidx = myidx - s_MAX - 1; }
for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
{
/*
apply lite triangular window design with
gradient of 10% per discrete step
*/
w_sat_hist[channel][i] += sat_hist[channel][myidx] *
((sat_windowsize+1)-abs(mywin)); // apply window
}
}
}
/*--------------------------------------*/
/* analyze histogram for most used sat */
/*--------------------------------------*/
// resulting sat (most_used_hue) for each channel
int most_used_sat[ATMO_NUM_CHANNELS];
memset(&most_used_sat, 0, sizeof(most_used_sat));
for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
{
int value = 0;
// walk trough histogram
for (i = 0; i < s_MAX+1; i++)
{
// if new value bigger then old one
if (w_sat_hist[channel][i] > value)
{
// remember index
most_used_sat[channel] = i;
// and value
value = w_sat_hist[channel][i];
}
}
}
/*----------------------------------------------------------*/
/* calculate average brightness within HSV image */
/* uniform Brightness for all channels is calculated */
/*----------------------------------------------------------*/
int l_counter = 0;
// average brightness (value)
long int value_avg = 0;
// TODO: extract into a function? in sat-histo-built
i = 0;
for (int row = 0; row < CAP_HEIGHT; row++)
{
for (int column = 0; column < CAP_WIDTH; column++)
{
// find average value: only use bright pixels for luminance average
if (HSV_Img[i].v > 10*AtmoSetup_DarknessLimit)
{
// build brightness average
value_avg += HSV_Img[i].v;
l_counter++;
}
i++;
}
}
// calculate brightness average
if (l_counter > 0) { value_avg = value_avg / l_counter; }
else { value_avg = 10 * AtmoSetup_DarknessLimit; }
/*----------------------------*/
/* adjust and copy results */
/*----------------------------*/
tHSVColor hsv_pixel;
// storage container for resulting RGB values
tColorPacket ColorChannels;
for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
{
// copy values
hsv_pixel.h = most_used_hue[channel];
hsv_pixel.s = most_used_sat[channel];
// adjust brightness
int new_value = (int) ((float)value_avg * ((float)AtmoSetup_BrightCorrect / 100.0));
if (new_value > 255) { new_value = 255; } // ensure brightness isn't set too high
hsv_pixel.v = (unsigned char)new_value;
// convert back to rgb
ColorChannels.channel[channel] = HSV2RGB(hsv_pixel);
}
return ColorChannels;
}
tHSVColor RGB2HSV(tRGBColor color)
{
int min, max, delta;
int r, g, b;
int h = 0;
tHSVColor hsv;
r = color.r;
g = color.g;
b = color.b;
min = MIN(MIN(r, g), b);
max = MAX(MAX(r, g), b);
delta = max - min;
hsv.v = (unsigned char) POS_DIV( max*v_MAX, 255 );
if (delta == 0) // This is a gray, no chroma...
{
h = 0; // HSV results = 0 / 1
hsv.s = 0;
}
else // Chromatic data...
{
hsv.s = (unsigned char) POS_DIV( (delta*s_MAX) , max );
int dr = (max - r) + 3*delta;
int dg = (max - g) + 3*delta;
int db = (max - b) + 3*delta;
int divisor = 6*delta;
if (r == max)
{
h = POS_DIV(( (db - dg) * h_MAX ) , divisor);
}
else if (g == max)
{
h = POS_DIV( ((dr - db) * h_MAX) , divisor) + (h_MAX/3);
}
else if (b == max)
{
h = POS_DIV(( (dg - dr) * h_MAX) , divisor) + (h_MAX/3)*2;
}
if ( h < 0 ) { h += h_MAX; }
if ( h > h_MAX ) { h -= h_MAX; }
}
hsv.h = (unsigned char)h;
return hsv;
}
tRGBColor HSV2RGB(tHSVColor color)
{
tRGBColor rgb = {0, 0, 0};
float h = (float)color.h/(float)h_MAX;
float s = (float)color.s/(float)s_MAX;
float v = (float)color.v/(float)v_MAX;
if (s == 0)
{
rgb.r = (int)((v*255.0)+0.5);
rgb.g = rgb.r;
rgb.b = rgb.r;
}
else
{
h = h * 6.0f;
if (h == 6.0) { h = 0.0; }
int i = (int)h;
float f = h - i;
float p = v*(1.0f-s);
float q = v*(1.0f-(s*f));
float t = v*(1.0f-(s*(1.0f-f)));
if (i == 0)
{
rgb.r = (int)((v*255.0)+0.5);
rgb.g = (int)((t*255.0)+0.5);
rgb.b = (int)((p*255.0)+0.5);
}
else if (i == 1)
{
rgb.r = (int)((q*255.0)+0.5);
rgb.g = (int)((v*255.0)+0.5);
rgb.b = (int)((p*255.0)+0.5);
}
else if (i == 2)
{
rgb.r = (int)((p*255.0)+0.5);
rgb.g = (int)((v*255.0)+0.5);
rgb.b = (int)((t*255.0)+0.5);
}
else if (i == 3)
{
rgb.r = (int)((p*255.0)+0.5);
rgb.g = (int)((q*255.0)+0.5);
rgb.b = (int)((v*255.0)+0.5);
}
else if (i == 4)
{
rgb.r = (int)((t*255.0)+0.5);
rgb.g = (int)((p*255.0)+0.5);
rgb.b = (int)((v*255.0)+0.5);
}
else
{
rgb.r = (int)((v*255.0)+0.5);
rgb.g = (int)((p*255.0)+0.5);
rgb.b = (int)((q*255.0)+0.5);
}
}
return rgb;
}
/*
* AtmoCalculations.h: see calculations.h of the linux version... one to one copy
* calculations.h: calculations needed by the input devices
*
* See the README.txt file for copyright information and how to reach the author(s).
*
* $Id$
*/
#ifndef _AtmoCalculations_h_
#define _AtmoCalculations_h_
#include "AtmoDefs.h"
#include "AtmoConfig.h"
tColorPacket CalcColorsAnalyzeHSV(CAtmoConfig *pAtmoConfig, tHSVColor *HSV_Img);
tHSVColor RGB2HSV(tRGBColor color);
tRGBColor HSV2RGB(tHSVColor color);
#endif
/*
* AtmoConfig.cpp: Class for holding all configuration values of AtmoWin - stores
* the values and retrieves its values from registry
*
* See the README.txt file for copyright information and how to reach the author(s).
*
* $Id$
*/
#include <stdio.h>
#include <string.h>
#include "AtmoConfig.h"
/* Import Hint
if somebody Adds new config option this has to be done in the following
files and includes!
AtmoConfig.h -- extend class definition!, and add get... and set... Methods!
so that the real variables are still hidden inside the class!
AtmoConfigRegistry.cpp --> SaveToRegistry();
AtmoConfigRegistry.cpp --> LoadFromRegistry();
AtmoConfig.cpp --> Assign( ... );
*/
CAtmoConfig::CAtmoConfig()
{
// setup basic configruation structures...
m_IsShowConfigDialog = 0;
m_eAtmoConnectionType = actSerialPort;
for(int i=0;i<10;i++)
m_ChannelAssignments[i] = NULL;
#if defined (_ATMO_VLC_PLUGIN_)
m_devicename = NULL;
#endif
// load all config values with there defaults
LoadDefaults();
// CAtmoZoneDefinition *m_ZoneDefinitions[ATMO_NUM_CHANNELS];
// generate default channel parameters which may be loaded later from .bmp files
for(int i=0;i<ATMO_NUM_CHANNELS;i++) {
m_ZoneDefinitions[i] = new CAtmoZoneDefinition();
m_ZoneDefinitions[i]->setZoneNumber(i);
switch(i) {
case 0: // summary channel
m_ZoneDefinitions[i]->Fill(255);
break;
case 1: // left channel
m_ZoneDefinitions[i]->FillGradientFromLeft();
break;
case 2: // right channel
m_ZoneDefinitions[i]->FillGradientFromRight();
break;
case 3: // top channel
m_ZoneDefinitions[i]->FillGradientFromTop();
break;
case 4: // bottom channel
m_ZoneDefinitions[i]->FillGradientFromBottom();
break;
}
}
}
CAtmoConfig::~CAtmoConfig() {
// and finally cleanup...
clearAllChannelMappings();
#if !defined (WIN32)
if(m_devicename)
free(m_devicename);
#endif
}
void CAtmoConfig::LoadDefaults() {
// m_eAtmoConnectionType = actSerialPort;
// m_Comport
m_eEffectMode = emDisabled;
m_WhiteAdjustment_Red = 255;
m_WhiteAdjustment_Green = 255;
m_WhiteAdjustment_Blue = 255;
m_UseSoftwareWhiteAdj = 1;
m_ColorChanger_iSteps = 50;
m_ColorChanger_iDelay = 25;
m_LrColorChanger_iSteps = 50;
m_LrColorChanger_iDelay = 25;
m_IsSetShutdownColor = 1;
m_ShutdownColor_Red = 0;
m_ShutdownColor_Green = 0;
m_ShutdownColor_Blue = 0;
m_StaticColor_Red = 127; // ??
m_StaticColor_Green = 192;
m_StaticColor_Blue = 255;
m_LiveViewFilterMode = afmCombined;
m_LiveViewFilter_PercentNew = 50;
m_LiveViewFilter_MeanLength = 300;
m_LiveViewFilter_MeanThreshold = 40;
m_LiveView_EdgeWeighting = 8;
m_LiveView_BrightCorrect = 100;
m_LiveView_DarknessLimit = 5;
m_LiveView_HueWinSize = 3;
m_LiveView_SatWinSize = 3;
m_LiveView_WidescreenMode = 0;