23
23
* --------------------------------------------------------------------------- */
24
24
TMOAncuti10::TMOAncuti10 ()
25
25
{
26
- SetName (L" Ancuti10" ); // TODO - Insert operator name
27
- SetDescription (L" Add your TMO description here" ); // TODO - Insert description
28
-
29
- dParameter.SetName (L" ParameterName" ); // TODO - Insert parameters names
30
- dParameter.SetDescription (L" ParameterDescription" ); // TODO - Insert parameter descriptions
31
- dParameter.SetDefault (1 ); // TODO - Add default values
32
- dParameter = 1 .;
33
- dParameter.SetRange (-1000.0 , 1000.0 ); // TODO - Add acceptable range if needed
34
- this ->Register (dParameter);
26
+ SetName (L" Ancuti10" );
27
+ SetDescription (L" Color to grayscale operator for images and video, method from paper: Image and Video Decolorization by Fusion" );
35
28
}
36
29
37
30
TMOAncuti10::~TMOAncuti10 ()
38
31
{
39
32
}
40
- // function to conver RGB to HSL color space, inputs are R, G, B values in the range [0, 255] and outputs are H, S, L values in the range [0, 1]
41
- void TMOAncuti10::convertRGBtoHSL (double R, double G, double B, double &H, double &S, double &L)
42
- {
43
- // normalize RGB values
44
- R /= 255.0 ;
45
- G /= 255.0 ;
46
- B /= 255.0 ;
47
- // find the maximum and minimum values of R, G, B
48
- double max = std::max ({R, G, B});
49
- double min = std::min ({R, G, B});
50
- // calculate luminance
51
- L = (max + min) / 2.0 ;
52
- if (max == min){
53
- H = S = 0.0 ; // achromatic
54
- }
55
- else {
56
- double tmp = max - min;
57
- S = (L > 0.5 ) ? tmp / (2.0 - max - min) : tmp / (max + min);
58
- double del_R = (((max - R) / 6.0 ) + (tmp / 2.0 )) / tmp;
59
- double del_G = (((max - G) / 6.0 ) + (tmp / 2.0 )) / tmp;
60
- double del_B = (((max - B) / 6.0 ) + (tmp / 2.0 )) / tmp;
61
- if (R == max)
62
- H = del_B - del_G;
63
- else if (G == max)
64
- H = (1.0 / 3.0 ) + del_R - del_B;
65
- else if (B == max)
66
- H = (2.0 / 3.0 ) + del_G - del_R;
67
- if (H < 0.0 )
68
- H += 1.0 ;
69
- if (H > 1.0 )
70
- H -= 1.0 ;
71
- }
72
- }
73
- // function to convert RGB to XYZ color space, inputs are R, G, B values in the range [0, 255] and outputs are X, Y, Z values in the range [0, 100]
74
- void TMOAncuti10::convertRGBtoXYZ (double R, double G, double B, double &X, double &Y, double &Z)
75
- {
76
- // normalize RGB values
77
- R /= 255.0 ;
78
- G /= 255.0 ;
79
- B /= 255.0 ;
80
- // apply gamma correction
81
- R = (R > 0.04045 ) ? pow ((R + 0.055 ) / 1.055 , 2.4 ) : R / 12.92 ;
82
- G = (G > 0.04045 ) ? pow ((G + 0.055 ) / 1.055 , 2.4 ) : G / 12.92 ;
83
- B = (B > 0.04045 ) ? pow ((B + 0.055 ) / 1.055 , 2.4 ) : B / 12.92 ;
84
- // multiply by 100 to scale
85
- R *= 100.0 ;
86
- G *= 100.0 ;
87
- B *= 100.0 ;
88
- // convert RGB to XYZ using D65 illuminant
89
- X = R * 0.4124564 + G * 0.3575761 + B * 0.1804375 ;
90
- Y = R * 0.2126729 + G * 0.7151522 + B * 0.0721750 ;
91
- Z = R * 0.0193339 + G * 0.1191920 + B * 0.9503041 ;
92
- }
93
- // function to convert XYZ to CIELAB color space, inputs are X, Y, Z values in the range [0, 100]
94
- void TMOAncuti10::convertXYZtoCIELAB (double X, double Y, double Z, double &L, double &a, double &b)
95
- {
96
- // reference white points
97
- const double ref_X = 95.047 ;
98
- const double ref_Y = 100.000 ;
99
- const double ref_Z = 108.883 ;
100
- // normalize XYZ values
101
- X /= ref_X;
102
- Y /= ref_Y;
103
- Z /= ref_Z;
104
- // apply CIE-L*ab transformation
105
- X = (X > 0.008856 ) ? pow (X, 1.0 / 3.0 ) : (7.787 * X) + (16.0 / 116.0 );
106
- Y = (Y > 0.008856 ) ? pow (Y, 1.0 / 3.0 ) : (7.787 * Y) + (16.0 / 116.0 );
107
- Z = (Z > 0.008856 ) ? pow (Z, 1.0 / 3.0 ) : (7.787 * Z) + (16.0 / 116.0 );
108
-
109
- L = (116.0 * Y) - 16.0 ;
110
- a = 500.0 * (X - Y);
111
- b = 200.0 * (Y - Z);
112
- }
113
- // function to convert CIELAB to CIELCh color space, inputs are L, a, b values and outputs are C, h values
114
- void TMOAncuti10::convertCIELABtoCIELCh (double L, double a, double b, double &C, double &h)
115
- {
116
- C = sqrt (a * a + b * b);
117
- double var_h = atan2 (b, a);
118
- if (var_h > 0 )
119
- var_h = (var_h / M_PI) * 180.0 ;
120
- else
121
- var_h = 360 - (abs (var_h) / M_PI) * 180.0 ;
122
- h = var_h;
123
- }
124
- // function to compute L_HK value, inputs are L, C, h values
125
- double TMOAncuti10::computeL_HK (double L, double C, double H)
126
- {
127
- return L + (2.5 - 0.025 * L)*(0.116 * fabs (sin ((H-90 )/2 )) + 0.085 ) * C; // formula from the paper
128
- }
129
33
130
- // function to calculate average pixel value, inputs are channel data, width and height
131
- double TMOAncuti10::calculateAMPV (double * channel, int width, int height)
132
- {
133
- double sum = 0.0 ;
134
- int totalCount = width * height;
135
- for (int i = 0 ; i < totalCount; i++)
136
- {
137
- sum += channel[i];
138
- }
139
- return sum / totalCount;
140
- }
141
34
142
- // function to apply separable binomial kernel, inputs are input data, output data, width and height
35
+ // function to apply separable binomial kernel
143
36
void TMOAncuti10::applySeparableBinomialKernel (double * input, double * output, int width, int height)
144
37
{
145
38
double kernel[5 ] = {1.0 , 4.0 , 6.0 , 4.0 , 1.0 }; // 1D kernel
@@ -175,13 +68,13 @@ void TMOAncuti10::applySeparableBinomialKernel(double* input, double* output, in
175
68
// function to compute saliency map, inputs are channel data, width and height and output is saliency map
176
69
void TMOAncuti10::computeSaliencyMap (double * channel, double * saliencyMap, int width, int height)
177
70
{
178
- double meanValue = calculateAMPV (channel, width, height); // mean value of the channel
71
+ double meanValue = calculateAPV (channel, width, height); // mean value of the channel
179
72
double * blurredChannel = new double [width * height];
180
73
applySeparableBinomialKernel (channel, blurredChannel, width, height);
181
74
// compute map
182
75
for (int i = 0 ; i < width * height; i++)
183
76
{
184
- saliencyMap[i] = fabs (meanValue - blurredChannel[i]);
77
+ saliencyMap[i] = fabs (meanValue - blurredChannel[i]); // paper equation (2)
185
78
}
186
79
delete[] blurredChannel;
187
80
}
@@ -194,7 +87,7 @@ void TMOAncuti10::computeExposednessMap(double* channel, double* exposednessMap,
194
87
195
88
for (int i = 0 ; i < width * height; i++)
196
89
{
197
- exposednessMap[i] = exp (- ((channel[i] - 0.5 ) * (channel[i] - 0.5 )/(sigma2))); // formula from the paper
90
+ exposednessMap[i] = exp (- ((channel[i] - 0.5 ) * (channel[i] - 0.5 )/(sigma2))); // formula from the paper, equation (3)
198
91
}
199
92
}
200
93
@@ -205,7 +98,7 @@ void TMOAncuti10::computeChromaticMap(double* channel, double* chromaticMap, dou
205
98
// compute chromatic map
206
99
for (int i = 0 ; i < totalCount; i++)
207
100
{
208
- chromaticMap[i] = fabs (channel[i] - saturation[i]);
101
+ chromaticMap[i] = fabs (channel[i] - saturation[i]) / sqrt ( 2.0 );
209
102
}
210
103
}
211
104
@@ -259,7 +152,7 @@ void TMOAncuti10::fusePyramids(const std::vector<std::vector<cv::Mat>>& laplacia
259
152
for (int i = 0 ; i < levels; i++){
260
153
fusedPyramid[i] = cv::Mat::zeros (laplacianPyramids[0 ][i].size (), laplacianPyramids[0 ][i].type ()); // initialize the output pyramid
261
154
for (int k = 0 ; k < laplacianPyramids.size (); k++){
262
- fusedPyramid[i] += gaussianPyramids[k][i].mul (laplacianPyramids[k][i]); // fuse the pyramids according to the formula in paper
155
+ fusedPyramid[i] += gaussianPyramids[k][i].mul (laplacianPyramids[k][i]); // fuse the pyramids according to the formula in paper, equation (5)
263
156
}
264
157
}
265
158
}
@@ -276,25 +169,13 @@ cv::Mat TMOAncuti10::reconstructFromPyramid(const std::vector<cv::Mat>& pyramid)
276
169
return current;
277
170
}
278
171
279
-
280
-
281
- /* --------------------------------------------------------------------------- *
282
- * This overloaded function is an implementation of your tone mapping operator *
283
- * --------------------------------------------------------------------------- */
172
+ // main function of the c2g operator
284
173
int TMOAncuti10::Transform ()
285
174
{
286
- // Source image is stored in local parameter pSrc
287
- // Destination image is in pDst
288
-
289
- // Initialy images are in RGB format, but you can
290
- // convert it into other format
291
- // pSrc->Convert(TMO_Yxy); // This is format of Y as luminance
292
- // pDst->Convert(TMO_Yxy); // x, y as color information
293
175
294
176
double *pSourceData = pSrc->GetData (); // You can work at low level data
295
177
double *pDestinationData = pDst->GetData (); // Data are stored in form of array
296
- // of three doubles representing
297
- // three colour components
178
+
298
179
int width = pSrc->GetWidth ();
299
180
int height = pSrc->GetHeight ();
300
181
double *R_Channel = new double [width * height];
@@ -313,10 +194,12 @@ int TMOAncuti10::Transform()
313
194
// compute HKL channel
314
195
for (int i = 0 ; i < width * height; i++)
315
196
{
197
+ // firstly we need to convert RGB intput into CIELch color space, that is done as RGB -> XYZ -> CIELab -> CIELch
316
198
double X, Y, Z, L, a, b, C, h;
317
199
convertRGBtoXYZ (R_Channel[i], G_Channel[i], B_Channel[i], X, Y, Z);
318
200
convertXYZtoCIELAB (X, Y, Z, L, a, b);
319
201
convertCIELABtoCIELCh (L, a, b, C, h);
202
+ // then we compute the L_hk channel frame
320
203
HKL_Channel[i] = computeL_HK (L, C, h);
321
204
}
322
205
double *saliencyMapR = new double [width * height];
@@ -416,40 +299,29 @@ int TMOAncuti10::Transform()
416
299
computeGaussianPyramid (finalWeightMaps[i], gaussianPyramids[i], levels);
417
300
}
418
301
fprintf (stderr, " Gaussian pyramids computed\n " );
419
- // ensure the sizes of the pyramids match
420
- for (int l = 0 ; l < levels; l++) {
421
- for (int k = 0 ; k < inputs.size (); k++) {
422
- if (laplacianPyramids[k][l].size () != gaussianPyramids[k][l].size ()) {
423
- fprintf (stderr, " Error: Pyramid sizes do not match at level %d for input %d\n " , l, k);
424
- return -1 ;
425
- }
426
- }
427
- }
428
- fprintf (stderr, " Pyramid sizes match\n " );
429
302
// fuse the pyramids
430
303
std::vector<cv::Mat> fusedPyramid;
431
304
fusePyramids (laplacianPyramids, gaussianPyramids, fusedPyramid);
432
305
// reconstruct the final image from the fused pyramid
433
306
fprintf (stderr, " Pyramids fused\n " );
434
307
cv::Mat fusedImage = reconstructFromPyramid (fusedPyramid);
435
308
fprintf (stderr, " Image reconstructed\n " );
436
- cv::normalize (fusedImage, fusedImage, 0 , 255 , cv::NORM_MINMAX);
437
- fusedImage.convertTo (fusedImage, CV_32F);
438
- fprintf (stderr, " Reconstruction done\n " );
309
+ cv::normalize (fusedImage, fusedImage, 0 , 1 , cv::NORM_MINMAX);
310
+ fprintf (stderr, " Reconstruction done.\n " );
439
311
440
312
441
313
double pY, px, py;
442
314
int j = 0 ;
443
315
for (j = 0 ; j < pSrc->GetHeight (); j++)
444
316
{
445
- pSrc->ProgressBar (j, pSrc->GetHeight ()); // You can provide progress bar
317
+ pSrc->ProgressBar (j, pSrc->GetHeight ());
446
318
for (int i = 0 ; i < pSrc->GetWidth (); i++)
447
319
{
448
320
449
- float pixel = fusedImage.at <float >(j, i);
450
- *pDestinationData++ = pixel / 255.0 ;
451
- *pDestinationData++ = pixel / 255.0 ;
452
- *pDestinationData++ = pixel / 255.0 ;
321
+ double pixel = fusedImage.at <double >(j, i);
322
+ *pDestinationData++ = pixel;
323
+ *pDestinationData++ = pixel;
324
+ *pDestinationData++ = pixel;
453
325
}
454
326
}
455
327
pSrc->ProgressBar (j, pSrc->GetHeight ());
@@ -474,3 +346,95 @@ int TMOAncuti10::Transform()
474
346
delete[] saturation;
475
347
return 0 ;
476
348
}
349
+
350
+ // function to conver RGB to HSL color space
351
+ void TMOAncuti10::convertRGBtoHSL (double R, double G, double B, double &H, double &S, double &L)
352
+ {
353
+ // find the maximum and minimum values of R, G, B
354
+ double max = std::max ({R, G, B});
355
+ double min = std::min ({R, G, B});
356
+ // calculate luminance
357
+ L = (max + min) / 2.0 ;
358
+ if (max == min){
359
+ H = S = 0.0 ; // achromatic
360
+ }
361
+ else {
362
+ double tmp = max - min;
363
+ S = (L > 0.5 ) ? tmp / (2.0 - max - min) : tmp / (max + min);
364
+ double del_R = (((max - R) / 6.0 ) + (tmp / 2.0 )) / tmp;
365
+ double del_G = (((max - G) / 6.0 ) + (tmp / 2.0 )) / tmp;
366
+ double del_B = (((max - B) / 6.0 ) + (tmp / 2.0 )) / tmp;
367
+ if (R == max)
368
+ H = del_B - del_G;
369
+ else if (G == max)
370
+ H = (1.0 / 3.0 ) + del_R - del_B;
371
+ else if (B == max)
372
+ H = (2.0 / 3.0 ) + del_G - del_R;
373
+ if (H < 0.0 )
374
+ H += 1.0 ;
375
+ if (H > 1.0 )
376
+ H -= 1.0 ;
377
+ }
378
+ }
379
+ // function to convert RGB to XYZ color space
380
+ void TMOAncuti10::convertRGBtoXYZ (double R, double G, double B, double &X, double &Y, double &Z)
381
+ {
382
+
383
+ // //apply gamma correction
384
+ R = (R/255.0 > 0.04045 ) ? pow ((R + 0.055 ) / 1.055 , 2.4 ) : R / 12.92 ;
385
+ G = (G/255.0 > 0.04045 ) ? pow ((G + 0.055 ) / 1.055 , 2.4 ) : G / 12.92 ;
386
+ B = (B/255.0 > 0.04045 ) ? pow ((B + 0.055 ) / 1.055 , 2.4 ) : B / 12.92 ;
387
+
388
+ // convert RGB to XYZ using D65 illuminant
389
+ X = R * 0.4124564 + G * 0.3575761 + B * 0.1804375 ;
390
+ Y = R * 0.2126729 + G * 0.7151522 + B * 0.0721750 ;
391
+ Z = R * 0.0193339 + G * 0.1191920 + B * 0.9503041 ;
392
+ }
393
+ // function to convert XYZ to CIELAB color space
394
+ void TMOAncuti10::convertXYZtoCIELAB (double X, double Y, double Z, double &L, double &a, double &b)
395
+ {
396
+ // reference white points
397
+ const double ref_X = 95.047 ;
398
+ const double ref_Y = 100.000 ;
399
+ const double ref_Z = 108.883 ;
400
+ // normalize XYZ values
401
+ X /= ref_X;
402
+ Y /= ref_Y;
403
+ Z /= ref_Z;
404
+
405
+ X = (X > 0.008856 ) ? pow (X, 1.0 / 3.0 ) : (7.787 * X) + (16.0 / 116.0 );
406
+ Y = (Y > 0.008856 ) ? pow (Y, 1.0 / 3.0 ) : (7.787 * Y) + (16.0 / 116.0 );
407
+ Z = (Z > 0.008856 ) ? pow (Z, 1.0 / 3.0 ) : (7.787 * Z) + (16.0 / 116.0 );
408
+
409
+ L = (116.0 * Y) - 16.0 ;
410
+ a = 500.0 * (X - Y);
411
+ b = 200.0 * (Y - Z);
412
+ }
413
+ // function to convert CIELAB to CIELCh color space
414
+ void TMOAncuti10::convertCIELABtoCIELCh (double L, double a, double b, double &C, double &h)
415
+ {
416
+ C = sqrt (a * a + b * b);
417
+ double var_h = atan2 (b, a);
418
+ if (var_h > 0 )
419
+ var_h = (var_h / M_PI) * 180.0 ;
420
+ else
421
+ var_h = 360 - (abs (var_h) / M_PI) * 180.0 ;
422
+ h = var_h;
423
+ }
424
+ // function to compute L_HK value for given pixel
425
+ double TMOAncuti10::computeL_HK (double L, double C, double H)
426
+ {
427
+ return L + (2.5 - 0.025 * L)*(0.116 * fabs (sin ((H-90 )/2 )) + 0.085 ) * C; // computation of L_hk value for one pixel based on equation (1)
428
+ }
429
+
430
+ // function to calculate average pixel value
431
+ double TMOAncuti10::calculateAPV (double * channel, int width, int height)
432
+ {
433
+ double sum = 0.0 ;
434
+ int totalCount = width * height;
435
+ for (int i = 0 ; i < totalCount; i++)
436
+ {
437
+ sum += channel[i];
438
+ }
439
+ return sum / totalCount;
440
+ }
0 commit comments