]> git.sesse.net Git - vlc/blob - modules/video_filter/atmo/AtmoCalculations.cpp
atmo filter: disable until it is fixed
[vlc] / modules / video_filter / atmo / AtmoCalculations.cpp
1 /*
2  * calculations.c: calculations needed by the input devices
3  *
4  * See the README file for copyright information and how to reach the author.
5  *
6  * $Id$
7  */
8 #include <stdlib.h>
9 #include <string.h>
10
11
12 #include "AtmoDefs.h"
13 #include "AtmoCalculations.h"
14 #include "AtmoConfig.h"
15 #include "AtmoZoneDefinition.h"
16
17
18 // set accuracy of color calculation
19 #define h_MAX   255
20 #define s_MAX   255
21 #define v_MAX   255
22
23 // macros
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) )
27
28 tColorPacket CalcColorsAnalyzeHSV(CAtmoConfig *pAtmoConfig, tHSVColor *HSV_Img)
29 {
30   int i; // counter
31
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];
37
38   /***************************************************************************/
39   /* Weight                                                                  */
40   /***************************************************************************/
41   static int LastEdgeWeighting = -1;
42   static int LastWidescreenMode = -1;
43
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();
49
50
51   // calculate only if setup has changed
52   if ((AtmoSetup_EdgeWeighting != LastEdgeWeighting) ||
53       (AtmoSetup_WidescreenMode != LastWidescreenMode))
54   {
55      for(i =0 ;i < ATMO_NUM_CHANNELS; i++)
56          pAtmoConfig->getZoneDefinition(i)->UpdateWeighting(&Weight[i][0],
57                                                             AtmoSetup_WidescreenMode,
58                                                             AtmoSetup_EdgeWeighting);
59     /*
60
61     original code from VDR sources... my one is just more flexible?*g*
62
63         i = 0;
64     for (int row = 0; row < CAP_HEIGHT; row++)
65     {
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
69
70       for (int column = 0; column < CAP_WIDTH; column++)
71       {
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)))
74         {
75           Weight[i].channel[0] = Weight[i].channel[1] = Weight[i].channel[2] = Weight[i].channel[3] = Weight[i].channel[4] = 0;
76         }
77         else
78         {
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);
85         }
86         i++;
87       }
88     }
89     */
90         LastEdgeWeighting = AtmoSetup_EdgeWeighting;
91     LastWidescreenMode = AtmoSetup_WidescreenMode;
92   }
93
94   /***************************************************************************/
95   /* Hue                                                                     */
96   /***************************************************************************/
97
98   /*----------------------------*/
99   /* hue histogram builtup      */
100   /*----------------------------*/
101   // HSV histogram
102   long int hue_hist[ATMO_NUM_CHANNELS][h_MAX+1];
103   // clean histogram
104   memset(&hue_hist, 0, sizeof(hue_hist));
105
106   i = 0;
107   for (int row = 0; row < CAP_HEIGHT; row++)
108   {
109     for (int column = 0; column < CAP_WIDTH; column++)
110     {
111       // forget black bars: perform calculations only if pixel has some luminosity
112           if (HSV_Img[i].v > 10*AtmoSetup_DarknessLimit)
113       {
114         // builtup histogram for the 5 channels
115         for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
116         {
117           // Add weight to channel
118           hue_hist[channel][HSV_Img[i].h] += Weight[channel][i] * HSV_Img[i].v;
119         }
120       }
121       i++;
122     }
123   }
124
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();
134
135   for (i = 0; i < h_MAX+1; i++) // walk through histogram [0;h_MAX]
136   {
137     // windowing from -hue_windowsize -> +hue_windowsize
138     for (int mywin = -hue_windowsize; mywin < hue_windowsize+1; mywin++)
139     {
140       // adressed histogram candlestick
141       int myidx = i + mywin;
142
143       // handle beginning of windowing -> roll back
144       if (myidx < 0)     { myidx = myidx + h_MAX + 1; }
145
146       // handle end of windowing -> roll forward
147       if (myidx > h_MAX) { myidx = myidx - h_MAX - 1; }
148
149       // Apply windowing to all 5 channels
150       for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
151       {
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
154       }
155     }
156   }
157
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};
163
164   // resulting hue for each channel
165   int most_used_hue[ATMO_NUM_CHANNELS];
166   memset(&most_used_hue, 0, sizeof(most_used_hue));
167
168   for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
169   {
170     int value = 0;
171     for (i = 0; i < h_MAX+1; i++) // walk through histogram
172     {
173       if (w_hue_hist[channel][i] > value) // if new value bigger then old one
174       {
175         most_used_hue[channel] = i;     // remember index
176         value = w_hue_hist[channel][i]; // and value
177       }
178     }
179
180     float percent = (float)w_hue_hist[channel][most_used_hue_last[channel]] / (float)value;
181     if (percent > 0.93f) // less than 7% difference?
182     {
183       most_used_hue[channel] = most_used_hue_last[channel]; // use last index
184     }
185     most_used_hue_last[channel] = most_used_hue[channel]; // save current index of most used hue
186   }
187
188   /***************************************************************************/
189   /* saturation                                                              */
190   /***************************************************************************/
191   // sat histogram
192   long int sat_hist[ATMO_NUM_CHANNELS][s_MAX+1];
193   // hue of the pixel we are working at
194   int pixel_hue = 0;
195   // clean histogram
196   memset(&sat_hist, 0, sizeof(sat_hist));
197
198   /*--------------------------------------*/
199   /* saturation histogram builtup         */
200   /*--------------------------------------*/
201   i = 0;
202   for (int row = 0; row < CAP_HEIGHT; row++)
203   {
204     for (int column = 0; column < CAP_WIDTH; column++)
205     {
206       // forget black bars: perform calculations only if pixel has some luminosity
207           if (HSV_Img[i].v > 10*AtmoSetup_DarknessLimit)
208       {
209         // find histogram position for pixel
210         pixel_hue = HSV_Img[i].h;
211
212         // TODO:   brightness calculation(if we require it some time)
213
214         for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
215         {
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))
219           {
220             // build histogram
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;
223           }
224         }
225       }
226       i++;
227     }
228   }
229
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;
239
240    // walk through histogram [0;h_MAX]
241    for (i = 0; i < s_MAX + 1; i++)
242    {
243      // windowing from -hue_windowsize -> +hue_windowsize
244      for (int mywin = -sat_windowsize; mywin < sat_windowsize+1; mywin++)
245      {
246        // adressed histogram candlestick
247        int myidx = i + mywin;
248
249        // handle beginning of windowing -> roll back
250        if (myidx < 0)     { myidx = myidx + s_MAX + 1; }
251
252        // handle end of windowing -> roll forward
253        if (myidx > h_MAX) { myidx = myidx - s_MAX - 1; }
254
255        for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
256        {
257          /*
258             apply lite triangular window design with
259             gradient of 10% per discrete step
260          */
261          w_sat_hist[channel][i] += sat_hist[channel][myidx] *
262                                   ((sat_windowsize+1)-abs(mywin)); // apply window
263        }
264      }
265    }
266
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));
273
274   for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
275   {
276     int value = 0;
277     // walk trough histogram
278     for (i = 0; i < s_MAX+1; i++)
279     {
280       // if new value bigger then old one
281       if (w_sat_hist[channel][i] > value)
282       {
283         // remember index
284         most_used_sat[channel] = i;
285         // and value
286         value = w_sat_hist[channel][i];
287       }
288     }
289   }
290
291   /*----------------------------------------------------------*/
292   /* calculate average brightness within HSV image            */
293   /* uniform Brightness for all channels is calculated        */
294   /*----------------------------------------------------------*/
295   int l_counter = 0;
296   // average brightness (value)
297   long int value_avg = 0;
298
299   // TODO: extract into a function? in sat-histo-built
300
301   i = 0;
302   for (int row = 0; row < CAP_HEIGHT; row++)
303   {
304     for (int column = 0; column < CAP_WIDTH; column++)
305     {
306       // find average value: only use bright pixels for luminance average
307           if (HSV_Img[i].v > 10*AtmoSetup_DarknessLimit)
308       {
309         // build brightness average
310         value_avg += HSV_Img[i].v;
311         l_counter++;
312       }
313       i++;
314     }
315   }
316
317   // calculate brightness average
318   if (l_counter > 0) { value_avg = value_avg / l_counter; }
319     else { value_avg = 10 * AtmoSetup_DarknessLimit; }
320
321   /*----------------------------*/
322   /* adjust and copy results    */
323   /*----------------------------*/
324   tHSVColor hsv_pixel;
325   // storage container for resulting RGB values
326   tColorPacket ColorChannels;
327
328   for (int channel = 0; channel < ATMO_NUM_CHANNELS; channel++)
329   {
330     // copy values
331     hsv_pixel.h = most_used_hue[channel];
332     hsv_pixel.s = most_used_sat[channel];
333
334     // adjust brightness
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;
338
339     // convert back to rgb
340     ColorChannels.channel[channel] = HSV2RGB(hsv_pixel);
341   }
342   return ColorChannels;
343 }
344
345 tHSVColor RGB2HSV(tRGBColor color)
346 {
347  int min, max, delta;
348  int r, g, b;
349  int h = 0;
350  tHSVColor hsv;
351
352  r = color.r;
353  g = color.g;
354  b = color.b;
355
356  min = MIN(MIN(r, g), b);
357  max = MAX(MAX(r, g), b);
358
359  delta = max - min;
360
361  hsv.v = (unsigned char) POS_DIV( max*v_MAX, 255 );
362
363  if (delta == 0) // This is a gray, no chroma...
364  {
365    h = 0;        // HSV results = 0 / 1
366    hsv.s = 0;
367  }
368  else // Chromatic data...
369  {
370    hsv.s = (unsigned char) POS_DIV( (delta*s_MAX) , max );
371
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;
376
377    if (r == max)
378    {
379      h = POS_DIV(( (db - dg) * h_MAX ) , divisor);
380    }
381    else if (g == max)
382    {
383      h = POS_DIV( ((dr - db) * h_MAX) , divisor) + (h_MAX/3);
384    }
385    else if (b == max)
386    {
387      h = POS_DIV(( (dg - dr) * h_MAX) , divisor) + (h_MAX/3)*2;
388    }
389
390    if ( h < 0 )     { h += h_MAX; }
391    if ( h > h_MAX ) { h -= h_MAX; }
392  }
393  hsv.h = (unsigned char)h;
394
395  return hsv;
396 }
397
398 tRGBColor HSV2RGB(tHSVColor color)
399 {
400  tRGBColor rgb = {0, 0, 0};
401
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;
405
406  if (s == 0)
407  {
408    rgb.r = (int)((v*255.0)+0.5);
409    rgb.g = rgb.r;
410    rgb.b = rgb.r;
411  }
412  else
413  {
414    h = h * 6.0f;
415    if (h == 6.0) { h = 0.0; }
416    int i = (int)h;
417
418    float f = h - i;
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)));
422
423    if (i == 0)
424    {
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);
428    }
429    else if (i == 1)
430    {
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);
434    }
435    else if (i == 2)
436    {
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);
440    }
441    else if (i == 3)
442    {
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);
446    }
447    else if (i == 4)
448    {
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);
452    }
453    else
454    {
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);
458    }
459  }
460  return rgb;
461 }