2 * calculations.c: calculations needed by the input devices
4 * See the README file for copyright information and how to reach the author.
13 #include "AtmoCalculations.h"
14 #include "AtmoConfig.h"
15 #include "AtmoZoneDefinition.h"
18 // set accuracy of color calculation
24 #define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
25 #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
26 #define POS_DIV(a, b) ( (a)/(b) + ( ((a)%(b) >= (b)/2 ) ? 1 : 0) )
28 tColorPacket CalcColorsAnalyzeHSV(CAtmoConfig *pAtmoConfig, tHSVColor *HSV_Img)
32 // static tWeightPacket Weight[IMAGE_SIZE];
33 // Flip instead having a array with (64x48) entries of values for each channel
34 // I have x arrays of 64x48 so each channel has its own array...
35 // (or gradient which is use to judge about the pixels)
36 static int Weight[ATMO_NUM_CHANNELS][IMAGE_SIZE];
38 /***************************************************************************/
40 /***************************************************************************/
41 static int LastEdgeWeighting = -1;
42 static int LastWidescreenMode = -1;
44 int AtmoSetup_EdgeWeighting = pAtmoConfig->getLiveView_EdgeWeighting();
45 int AtmoSetup_WidescreenMode = pAtmoConfig->getLiveView_WidescreenMode();
46 int AtmoSetup_DarknessLimit = pAtmoConfig->getLiveView_DarknessLimit();
47 int AtmoSetup_BrightCorrect = pAtmoConfig->getLiveView_BrightCorrect();
48 int AtmoSetup_SatWinSize = pAtmoConfig->getLiveView_SatWinSize();
51 // calculate only if setup has changed
52 if ((AtmoSetup_EdgeWeighting != LastEdgeWeighting) ||
53 (AtmoSetup_WidescreenMode != LastWidescreenMode))
55 for(i =0 ;i < ATMO_NUM_CHANNELS; i++)
56 pAtmoConfig->getZoneDefinition(i)->UpdateWeighting(&Weight[i][0],
57 AtmoSetup_WidescreenMode,
58 AtmoSetup_EdgeWeighting);
61 original code from VDR sources... my one is just more flexible?*g*
64 for (int row = 0; row < CAP_HEIGHT; row++)
66 float row_norm = (float)row / ((float)CAP_HEIGHT - 1.0f); // [0;Height] -> [0;1]
67 float weight_3 = pow(1.0f - row_norm, AtmoSetup_EdgeWeighting); // top
68 float weight_4 = pow(row_norm, AtmoSetup_EdgeWeighting); // bottom
70 for (int column = 0; column < CAP_WIDTH; column++)
72 // if widescreen mode, top and bottom of the picture are not
73 if ((AtmoSetup_WidescreenMode == 1) && ((row <= CAP_HEIGHT/8) || (row >= (7*CAP_HEIGHT)/8)))
75 Weight[i].channel[0] = Weight[i].channel[1] = Weight[i].channel[2] = Weight[i].channel[3] = Weight[i].channel[4] = 0;
79 float column_norm = (float)column / ((float)CAP_WIDTH - 1.0f); // [0;Width] -> [0;1]
80 Weight[i].channel[0] = 255;
81 Weight[i].channel[1] = (int)(255.0 * (float)pow((1.0 - column_norm), AtmoSetup_EdgeWeighting));
82 Weight[i].channel[2] = (int)(255.0 * (float)pow(column_norm, AtmoSetup_EdgeWeighting));
83 Weight[i].channel[3] = (int)(255.0 * (float)weight_3);
84 Weight[i].channel[4] = (int)(255.0 * (float)weight_4);
90 LastEdgeWeighting = AtmoSetup_EdgeWeighting;
91 LastWidescreenMode = AtmoSetup_WidescreenMode;
94 /***************************************************************************/
96 /***************************************************************************/
98 /*----------------------------*/
99 /* hue histogram builtup */
100 /*----------------------------*/
102 long int hue_hist[ATMO_NUM_CHANNELS][h_MAX+1];
104 memset(&hue_hist, 0, sizeof(hue_hist));
107 for (int row = 0; row < CAP_HEIGHT; row++)
109 for (int column = 0; column < CAP_WIDTH; column++)
111 // forget black bars: perform calculations only if pixel has some luminosity
112 if (HSV_Img[i].v > 10*AtmoSetup_DarknessLimit)
114 // builtup histogram for the 5 channels
115 for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
117 // Add weight to channel
118 hue_hist[channel][HSV_Img[i].h] += Weight[channel][i] * HSV_Img[i].v;
125 /*----------------------------*/
126 /* hue histogram windowing */
127 /*----------------------------*/
128 // windowed HSV histogram
129 long int w_hue_hist[ATMO_NUM_CHANNELS][h_MAX+1];
130 // clean windowed histogram
131 memset(&w_hue_hist, 0, sizeof(w_hue_hist));
132 // steps in each direction; eg. 2 => -2 -1 0 1 2 windowing
133 int hue_windowsize = pAtmoConfig->getLiveView_HueWinSize();
135 for (i = 0; i < h_MAX+1; i++) // walk through histogram [0;h_MAX]
137 // windowing from -hue_windowsize -> +hue_windowsize
138 for (int mywin = -hue_windowsize; mywin < hue_windowsize+1; mywin++)
140 // adressed histogram candlestick
141 int myidx = i + mywin;
143 // handle beginning of windowing -> roll back
144 if (myidx < 0) { myidx = myidx + h_MAX + 1; }
146 // handle end of windowing -> roll forward
147 if (myidx > h_MAX) { myidx = myidx - h_MAX - 1; }
149 // Apply windowing to all 5 channels
150 for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
152 // apply lite triangular window design with gradient of 10% per discrete step
153 w_hue_hist[channel][i] += hue_hist[channel][myidx] * ((hue_windowsize+1)-abs(mywin)); // apply window
158 /*--------------------------------------*/
159 /* analyze histogram for most used hue */
160 /*--------------------------------------*/
161 // index of last maximum
162 static int most_used_hue_last[ATMO_NUM_CHANNELS] = {0, 0, 0, 0, 0};
164 // resulting hue for each channel
165 int most_used_hue[ATMO_NUM_CHANNELS];
166 memset(&most_used_hue, 0, sizeof(most_used_hue));
168 for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
171 for (i = 0; i < h_MAX+1; i++) // walk through histogram
173 if (w_hue_hist[channel][i] > value) // if new value bigger then old one
175 most_used_hue[channel] = i; // remember index
176 value = w_hue_hist[channel][i]; // and value
180 float percent = (float)w_hue_hist[channel][most_used_hue_last[channel]] / (float)value;
181 if (percent > 0.93f) // less than 7% difference?
183 most_used_hue[channel] = most_used_hue_last[channel]; // use last index
185 most_used_hue_last[channel] = most_used_hue[channel]; // save current index of most used hue
188 /***************************************************************************/
190 /***************************************************************************/
192 long int sat_hist[ATMO_NUM_CHANNELS][s_MAX+1];
193 // hue of the pixel we are working at
196 memset(&sat_hist, 0, sizeof(sat_hist));
198 /*--------------------------------------*/
199 /* saturation histogram builtup */
200 /*--------------------------------------*/
202 for (int row = 0; row < CAP_HEIGHT; row++)
204 for (int column = 0; column < CAP_WIDTH; column++)
206 // forget black bars: perform calculations only if pixel has some luminosity
207 if (HSV_Img[i].v > 10*AtmoSetup_DarknessLimit)
209 // find histogram position for pixel
210 pixel_hue = HSV_Img[i].h;
212 // TODO: brightness calculation(if we require it some time)
214 for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
216 // only use pixel for histogram if hue is near most_used_hue
217 if ((pixel_hue > most_used_hue[channel] - hue_windowsize) &&
218 (pixel_hue < most_used_hue[channel] + hue_windowsize))
221 // sat_hist[channel][HSV_Img[i].s] += Weight[i].channel[channel] * HSV_Img[i].v;
222 sat_hist[channel][HSV_Img[i].s] += Weight[channel][i] * HSV_Img[i].v;
230 /*--------------------------------------*/
231 /* saturation histogram windowing */
232 /*--------------------------------------*/
233 // windowed HSV histogram
234 long int w_sat_hist[ATMO_NUM_CHANNELS][s_MAX+1];
235 // clean windowed histogram
236 memset(&w_sat_hist, 0, sizeof(w_sat_hist));
237 // steps in each direction; eg. 2 => -2 -1 0 1 2 windowing
238 int sat_windowsize = AtmoSetup_SatWinSize;
240 // walk through histogram [0;h_MAX]
241 for (i = 0; i < s_MAX + 1; i++)
243 // windowing from -hue_windowsize -> +hue_windowsize
244 for (int mywin = -sat_windowsize; mywin < sat_windowsize+1; mywin++)
246 // adressed histogram candlestick
247 int myidx = i + mywin;
249 // handle beginning of windowing -> roll back
250 if (myidx < 0) { myidx = myidx + s_MAX + 1; }
252 // handle end of windowing -> roll forward
253 if (myidx > h_MAX) { myidx = myidx - s_MAX - 1; }
255 for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
258 apply lite triangular window design with
259 gradient of 10% per discrete step
261 w_sat_hist[channel][i] += sat_hist[channel][myidx] *
262 ((sat_windowsize+1)-abs(mywin)); // apply window
267 /*--------------------------------------*/
268 /* analyze histogram for most used sat */
269 /*--------------------------------------*/
270 // resulting sat (most_used_hue) for each channel
271 int most_used_sat[ATMO_NUM_CHANNELS];
272 memset(&most_used_sat, 0, sizeof(most_used_sat));
274 for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
277 // walk trough histogram
278 for (i = 0; i < s_MAX+1; i++)
280 // if new value bigger then old one
281 if (w_sat_hist[channel][i] > value)
284 most_used_sat[channel] = i;
286 value = w_sat_hist[channel][i];
291 /*----------------------------------------------------------*/
292 /* calculate average brightness within HSV image */
293 /* uniform Brightness for all channels is calculated */
294 /*----------------------------------------------------------*/
296 // average brightness (value)
297 long int value_avg = 0;
299 // TODO: extract into a function? in sat-histo-built
302 for (int row = 0; row < CAP_HEIGHT; row++)
304 for (int column = 0; column < CAP_WIDTH; column++)
306 // find average value: only use bright pixels for luminance average
307 if (HSV_Img[i].v > 10*AtmoSetup_DarknessLimit)
309 // build brightness average
310 value_avg += HSV_Img[i].v;
317 // calculate brightness average
318 if (l_counter > 0) { value_avg = value_avg / l_counter; }
319 else { value_avg = 10 * AtmoSetup_DarknessLimit; }
321 /*----------------------------*/
322 /* adjust and copy results */
323 /*----------------------------*/
325 // storage container for resulting RGB values
326 tColorPacket ColorChannels;
328 for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
331 hsv_pixel.h = most_used_hue[channel];
332 hsv_pixel.s = most_used_sat[channel];
335 int new_value = (int) ((float)value_avg * ((float)AtmoSetup_BrightCorrect / 100.0));
336 if (new_value > 255) { new_value = 255; } // ensure brightness isn't set too high
337 hsv_pixel.v = (unsigned char)new_value;
339 // convert back to rgb
340 ColorChannels.channel[channel] = HSV2RGB(hsv_pixel);
342 return ColorChannels;
345 tHSVColor RGB2HSV(tRGBColor color)
356 min = MIN(MIN(r, g), b);
357 max = MAX(MAX(r, g), b);
361 hsv.v = (unsigned char) POS_DIV( max*v_MAX, 255 );
363 if (delta == 0) // This is a gray, no chroma...
365 h = 0; // HSV results = 0 / 1
368 else // Chromatic data...
370 hsv.s = (unsigned char) POS_DIV( (delta*s_MAX) , max );
372 int dr = (max - r) + 3*delta;
373 int dg = (max - g) + 3*delta;
374 int db = (max - b) + 3*delta;
375 int divisor = 6*delta;
379 h = POS_DIV(( (db - dg) * h_MAX ) , divisor);
383 h = POS_DIV( ((dr - db) * h_MAX) , divisor) + (h_MAX/3);
387 h = POS_DIV(( (dg - dr) * h_MAX) , divisor) + (h_MAX/3)*2;
390 if ( h < 0 ) { h += h_MAX; }
391 if ( h > h_MAX ) { h -= h_MAX; }
393 hsv.h = (unsigned char)h;
398 tRGBColor HSV2RGB(tHSVColor color)
400 tRGBColor rgb = {0, 0, 0};
402 float h = (float)color.h/(float)h_MAX;
403 float s = (float)color.s/(float)s_MAX;
404 float v = (float)color.v/(float)v_MAX;
408 rgb.r = (int)((v*255.0)+0.5);
415 if (h == 6.0) { h = 0.0; }
419 float p = v*(1.0f-s);
420 float q = v*(1.0f-(s*f));
421 float t = v*(1.0f-(s*(1.0f-f)));
425 rgb.r = (int)((v*255.0)+0.5);
426 rgb.g = (int)((t*255.0)+0.5);
427 rgb.b = (int)((p*255.0)+0.5);
431 rgb.r = (int)((q*255.0)+0.5);
432 rgb.g = (int)((v*255.0)+0.5);
433 rgb.b = (int)((p*255.0)+0.5);
437 rgb.r = (int)((p*255.0)+0.5);
438 rgb.g = (int)((v*255.0)+0.5);
439 rgb.b = (int)((t*255.0)+0.5);
443 rgb.r = (int)((p*255.0)+0.5);
444 rgb.g = (int)((q*255.0)+0.5);
445 rgb.b = (int)((v*255.0)+0.5);
449 rgb.r = (int)((t*255.0)+0.5);
450 rgb.g = (int)((p*255.0)+0.5);
451 rgb.b = (int)((v*255.0)+0.5);
455 rgb.r = (int)((v*255.0)+0.5);
456 rgb.g = (int)((p*255.0)+0.5);
457 rgb.b = (int)((q*255.0)+0.5);