1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 // By downloading, copying, installing or using the software you agree to this license.
6 // If you do not agree to this license, do not download, install,
7 // copy or use the software.
8 //
9 //
10 // License Agreement
11 // For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2013, OpenCV Foundation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 // * Redistribution's of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
21 //
22 // * Redistribution's in binary form must reproduce the above copyright notice,
23 // this list of conditions and the following disclaimer in the documentation
24 // and/or other materials provided with the distribution.
25 //
26 // * The name of the copyright holders may not be used to endorse or promote products
27 // derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 #include "precomp.hpp"
43 #include "opencv2/photo.hpp"
44 #include "opencv2/imgproc.hpp"
45 #include "hdr_common.hpp"
46
47 namespace cv
48 {
49
log_(const Mat & src,Mat & dst)50 inline void log_(const Mat& src, Mat& dst)
51 {
52 max(src, Scalar::all(1e-4), dst);
53 log(dst, dst);
54 }
55
56 class TonemapImpl : public Tonemap
57 {
58 public:
TonemapImpl(float _gamma)59 TonemapImpl(float _gamma) : name("Tonemap"), gamma(_gamma)
60 {
61 }
62
process(InputArray _src,OutputArray _dst)63 void process(InputArray _src, OutputArray _dst)
64 {
65 Mat src = _src.getMat();
66 CV_Assert(!src.empty());
67 _dst.create(src.size(), CV_32FC3);
68 Mat dst = _dst.getMat();
69
70 double min, max;
71 minMaxLoc(src, &min, &max);
72 if(max - min > DBL_EPSILON) {
73 dst = (src - min) / (max - min);
74 } else {
75 src.copyTo(dst);
76 }
77
78 pow(dst, 1.0f / gamma, dst);
79 }
80
getGamma() const81 float getGamma() const { return gamma; }
setGamma(float val)82 void setGamma(float val) { gamma = val; }
83
write(FileStorage & fs) const84 void write(FileStorage& fs) const
85 {
86 fs << "name" << name
87 << "gamma" << gamma;
88 }
89
read(const FileNode & fn)90 void read(const FileNode& fn)
91 {
92 FileNode n = fn["name"];
93 CV_Assert(n.isString() && String(n) == name);
94 gamma = fn["gamma"];
95 }
96
97 protected:
98 String name;
99 float gamma;
100 };
101
createTonemap(float gamma)102 Ptr<Tonemap> createTonemap(float gamma)
103 {
104 return makePtr<TonemapImpl>(gamma);
105 }
106
107 class TonemapDragoImpl : public TonemapDrago
108 {
109 public:
TonemapDragoImpl(float _gamma,float _saturation,float _bias)110 TonemapDragoImpl(float _gamma, float _saturation, float _bias) :
111 name("TonemapDrago"),
112 gamma(_gamma),
113 saturation(_saturation),
114 bias(_bias)
115 {
116 }
117
process(InputArray _src,OutputArray _dst)118 void process(InputArray _src, OutputArray _dst)
119 {
120 Mat src = _src.getMat();
121 CV_Assert(!src.empty());
122 _dst.create(src.size(), CV_32FC3);
123 Mat img = _dst.getMat();
124
125 Ptr<Tonemap> linear = createTonemap(1.0f);
126 linear->process(src, img);
127
128 Mat gray_img;
129 cvtColor(img, gray_img, COLOR_RGB2GRAY);
130 Mat log_img;
131 log_(gray_img, log_img);
132 float mean = expf(static_cast<float>(sum(log_img)[0]) / log_img.total());
133 gray_img /= mean;
134 log_img.release();
135
136 double max;
137 minMaxLoc(gray_img, NULL, &max);
138
139 Mat map;
140 log(gray_img + 1.0f, map);
141 Mat div;
142 pow(gray_img / static_cast<float>(max), logf(bias) / logf(0.5f), div);
143 log(2.0f + 8.0f * div, div);
144 map = map.mul(1.0f / div);
145 div.release();
146
147 mapLuminance(img, img, gray_img, map, saturation);
148
149 linear->setGamma(gamma);
150 linear->process(img, img);
151 }
152
getGamma() const153 float getGamma() const { return gamma; }
setGamma(float val)154 void setGamma(float val) { gamma = val; }
155
getSaturation() const156 float getSaturation() const { return saturation; }
setSaturation(float val)157 void setSaturation(float val) { saturation = val; }
158
getBias() const159 float getBias() const { return bias; }
setBias(float val)160 void setBias(float val) { bias = val; }
161
write(FileStorage & fs) const162 void write(FileStorage& fs) const
163 {
164 fs << "name" << name
165 << "gamma" << gamma
166 << "bias" << bias
167 << "saturation" << saturation;
168 }
169
read(const FileNode & fn)170 void read(const FileNode& fn)
171 {
172 FileNode n = fn["name"];
173 CV_Assert(n.isString() && String(n) == name);
174 gamma = fn["gamma"];
175 bias = fn["bias"];
176 saturation = fn["saturation"];
177 }
178
179 protected:
180 String name;
181 float gamma, saturation, bias;
182 };
183
createTonemapDrago(float gamma,float saturation,float bias)184 Ptr<TonemapDrago> createTonemapDrago(float gamma, float saturation, float bias)
185 {
186 return makePtr<TonemapDragoImpl>(gamma, saturation, bias);
187 }
188
189 class TonemapDurandImpl : public TonemapDurand
190 {
191 public:
TonemapDurandImpl(float _gamma,float _contrast,float _saturation,float _sigma_color,float _sigma_space)192 TonemapDurandImpl(float _gamma, float _contrast, float _saturation, float _sigma_color, float _sigma_space) :
193 name("TonemapDurand"),
194 gamma(_gamma),
195 contrast(_contrast),
196 saturation(_saturation),
197 sigma_color(_sigma_color),
198 sigma_space(_sigma_space)
199 {
200 }
201
process(InputArray _src,OutputArray _dst)202 void process(InputArray _src, OutputArray _dst)
203 {
204 Mat src = _src.getMat();
205 CV_Assert(!src.empty());
206 _dst.create(src.size(), CV_32FC3);
207 Mat img = _dst.getMat();
208 Ptr<Tonemap> linear = createTonemap(1.0f);
209 linear->process(src, img);
210
211 Mat gray_img;
212 cvtColor(img, gray_img, COLOR_RGB2GRAY);
213 Mat log_img;
214 log_(gray_img, log_img);
215 Mat map_img;
216 bilateralFilter(log_img, map_img, -1, sigma_color, sigma_space);
217
218 double min, max;
219 minMaxLoc(map_img, &min, &max);
220 float scale = contrast / static_cast<float>(max - min);
221 exp(map_img * (scale - 1.0f) + log_img, map_img);
222 log_img.release();
223
224 mapLuminance(img, img, gray_img, map_img, saturation);
225 pow(img, 1.0f / gamma, img);
226 }
227
getGamma() const228 float getGamma() const { return gamma; }
setGamma(float val)229 void setGamma(float val) { gamma = val; }
230
getSaturation() const231 float getSaturation() const { return saturation; }
setSaturation(float val)232 void setSaturation(float val) { saturation = val; }
233
getContrast() const234 float getContrast() const { return contrast; }
setContrast(float val)235 void setContrast(float val) { contrast = val; }
236
getSigmaColor() const237 float getSigmaColor() const { return sigma_color; }
setSigmaColor(float val)238 void setSigmaColor(float val) { sigma_color = val; }
239
getSigmaSpace() const240 float getSigmaSpace() const { return sigma_space; }
setSigmaSpace(float val)241 void setSigmaSpace(float val) { sigma_space = val; }
242
write(FileStorage & fs) const243 void write(FileStorage& fs) const
244 {
245 fs << "name" << name
246 << "gamma" << gamma
247 << "contrast" << contrast
248 << "sigma_color" << sigma_color
249 << "sigma_space" << sigma_space
250 << "saturation" << saturation;
251 }
252
read(const FileNode & fn)253 void read(const FileNode& fn)
254 {
255 FileNode n = fn["name"];
256 CV_Assert(n.isString() && String(n) == name);
257 gamma = fn["gamma"];
258 contrast = fn["contrast"];
259 sigma_color = fn["sigma_color"];
260 sigma_space = fn["sigma_space"];
261 saturation = fn["saturation"];
262 }
263
264 protected:
265 String name;
266 float gamma, contrast, saturation, sigma_color, sigma_space;
267 };
268
createTonemapDurand(float gamma,float contrast,float saturation,float sigma_color,float sigma_space)269 Ptr<TonemapDurand> createTonemapDurand(float gamma, float contrast, float saturation, float sigma_color, float sigma_space)
270 {
271 return makePtr<TonemapDurandImpl>(gamma, contrast, saturation, sigma_color, sigma_space);
272 }
273
274 class TonemapReinhardImpl : public TonemapReinhard
275 {
276 public:
TonemapReinhardImpl(float _gamma,float _intensity,float _light_adapt,float _color_adapt)277 TonemapReinhardImpl(float _gamma, float _intensity, float _light_adapt, float _color_adapt) :
278 name("TonemapReinhard"),
279 gamma(_gamma),
280 intensity(_intensity),
281 light_adapt(_light_adapt),
282 color_adapt(_color_adapt)
283 {
284 }
285
process(InputArray _src,OutputArray _dst)286 void process(InputArray _src, OutputArray _dst)
287 {
288 Mat src = _src.getMat();
289 CV_Assert(!src.empty());
290 _dst.create(src.size(), CV_32FC3);
291 Mat img = _dst.getMat();
292 Ptr<Tonemap> linear = createTonemap(1.0f);
293 linear->process(src, img);
294
295 Mat gray_img;
296 cvtColor(img, gray_img, COLOR_RGB2GRAY);
297 Mat log_img;
298 log_(gray_img, log_img);
299
300 float log_mean = static_cast<float>(sum(log_img)[0] / log_img.total());
301 double log_min, log_max;
302 minMaxLoc(log_img, &log_min, &log_max);
303 log_img.release();
304
305 double key = static_cast<float>((log_max - log_mean) / (log_max - log_min));
306 float map_key = 0.3f + 0.7f * pow(static_cast<float>(key), 1.4f);
307 intensity = exp(-intensity);
308 Scalar chan_mean = mean(img);
309 float gray_mean = static_cast<float>(mean(gray_img)[0]);
310
311 std::vector<Mat> channels(3);
312 split(img, channels);
313
314 for(int i = 0; i < 3; i++) {
315 float global = color_adapt * static_cast<float>(chan_mean[i]) + (1.0f - color_adapt) * gray_mean;
316 Mat adapt = color_adapt * channels[i] + (1.0f - color_adapt) * gray_img;
317 adapt = light_adapt * adapt + (1.0f - light_adapt) * global;
318 pow(intensity * adapt, map_key, adapt);
319 channels[i] = channels[i].mul(1.0f / (adapt + channels[i]));
320 }
321 gray_img.release();
322 merge(channels, img);
323
324 linear->setGamma(gamma);
325 linear->process(img, img);
326 }
327
getGamma() const328 float getGamma() const { return gamma; }
setGamma(float val)329 void setGamma(float val) { gamma = val; }
330
getIntensity() const331 float getIntensity() const { return intensity; }
setIntensity(float val)332 void setIntensity(float val) { intensity = val; }
333
getLightAdaptation() const334 float getLightAdaptation() const { return light_adapt; }
setLightAdaptation(float val)335 void setLightAdaptation(float val) { light_adapt = val; }
336
getColorAdaptation() const337 float getColorAdaptation() const { return color_adapt; }
setColorAdaptation(float val)338 void setColorAdaptation(float val) { color_adapt = val; }
339
write(FileStorage & fs) const340 void write(FileStorage& fs) const
341 {
342 fs << "name" << name
343 << "gamma" << gamma
344 << "intensity" << intensity
345 << "light_adapt" << light_adapt
346 << "color_adapt" << color_adapt;
347 }
348
read(const FileNode & fn)349 void read(const FileNode& fn)
350 {
351 FileNode n = fn["name"];
352 CV_Assert(n.isString() && String(n) == name);
353 gamma = fn["gamma"];
354 intensity = fn["intensity"];
355 light_adapt = fn["light_adapt"];
356 color_adapt = fn["color_adapt"];
357 }
358
359 protected:
360 String name;
361 float gamma, intensity, light_adapt, color_adapt;
362 };
363
createTonemapReinhard(float gamma,float contrast,float sigma_color,float sigma_space)364 Ptr<TonemapReinhard> createTonemapReinhard(float gamma, float contrast, float sigma_color, float sigma_space)
365 {
366 return makePtr<TonemapReinhardImpl>(gamma, contrast, sigma_color, sigma_space);
367 }
368
369 class TonemapMantiukImpl : public TonemapMantiuk
370 {
371 public:
TonemapMantiukImpl(float _gamma,float _scale,float _saturation)372 TonemapMantiukImpl(float _gamma, float _scale, float _saturation) :
373 name("TonemapMantiuk"),
374 gamma(_gamma),
375 scale(_scale),
376 saturation(_saturation)
377 {
378 }
379
process(InputArray _src,OutputArray _dst)380 void process(InputArray _src, OutputArray _dst)
381 {
382 Mat src = _src.getMat();
383 CV_Assert(!src.empty());
384 _dst.create(src.size(), CV_32FC3);
385 Mat img = _dst.getMat();
386 Ptr<Tonemap> linear = createTonemap(1.0f);
387 linear->process(src, img);
388
389 Mat gray_img;
390 cvtColor(img, gray_img, COLOR_RGB2GRAY);
391 Mat log_img;
392 log_(gray_img, log_img);
393
394 std::vector<Mat> x_contrast, y_contrast;
395 getContrast(log_img, x_contrast, y_contrast);
396
397 for(size_t i = 0; i < x_contrast.size(); i++) {
398 mapContrast(x_contrast[i]);
399 mapContrast(y_contrast[i]);
400 }
401
402 Mat right(src.size(), CV_32F);
403 calculateSum(x_contrast, y_contrast, right);
404
405 Mat p, r, product, x = log_img;
406 calculateProduct(x, r);
407 r = right - r;
408 r.copyTo(p);
409
410 const float target_error = 1e-3f;
411 float target_norm = static_cast<float>(right.dot(right)) * powf(target_error, 2.0f);
412 int max_iterations = 100;
413 float rr = static_cast<float>(r.dot(r));
414
415 for(int i = 0; i < max_iterations; i++)
416 {
417 calculateProduct(p, product);
418 float alpha = rr / static_cast<float>(p.dot(product));
419
420 r -= alpha * product;
421 x += alpha * p;
422
423 float new_rr = static_cast<float>(r.dot(r));
424 p = r + (new_rr / rr) * p;
425 rr = new_rr;
426
427 if(rr < target_norm) {
428 break;
429 }
430 }
431 exp(x, x);
432 mapLuminance(img, img, gray_img, x, saturation);
433
434 linear = createTonemap(gamma);
435 linear->process(img, img);
436 }
437
getGamma() const438 float getGamma() const { return gamma; }
setGamma(float val)439 void setGamma(float val) { gamma = val; }
440
getScale() const441 float getScale() const { return scale; }
setScale(float val)442 void setScale(float val) { scale = val; }
443
getSaturation() const444 float getSaturation() const { return saturation; }
setSaturation(float val)445 void setSaturation(float val) { saturation = val; }
446
write(FileStorage & fs) const447 void write(FileStorage& fs) const
448 {
449 fs << "name" << name
450 << "gamma" << gamma
451 << "scale" << scale
452 << "saturation" << saturation;
453 }
454
read(const FileNode & fn)455 void read(const FileNode& fn)
456 {
457 FileNode n = fn["name"];
458 CV_Assert(n.isString() && String(n) == name);
459 gamma = fn["gamma"];
460 scale = fn["scale"];
461 saturation = fn["saturation"];
462 }
463
464 protected:
465 String name;
466 float gamma, scale, saturation;
467
signedPow(Mat src,float power,Mat & dst)468 void signedPow(Mat src, float power, Mat& dst)
469 {
470 Mat sign = (src > 0);
471 sign.convertTo(sign, CV_32F, 1.0f/255.0f);
472 sign = sign * 2.0f - 1.0f;
473 pow(abs(src), power, dst);
474 dst = dst.mul(sign);
475 }
476
mapContrast(Mat & contrast)477 void mapContrast(Mat& contrast)
478 {
479 const float response_power = 0.4185f;
480 signedPow(contrast, response_power, contrast);
481 contrast *= scale;
482 signedPow(contrast, 1.0f / response_power, contrast);
483 }
484
getGradient(Mat src,Mat & dst,int pos)485 void getGradient(Mat src, Mat& dst, int pos)
486 {
487 dst = Mat::zeros(src.size(), CV_32F);
488 Mat a, b;
489 Mat grad = src.colRange(1, src.cols) - src.colRange(0, src.cols - 1);
490 grad.copyTo(dst.colRange(pos, src.cols + pos - 1));
491 if(pos == 1) {
492 src.col(0).copyTo(dst.col(0));
493 }
494 }
495
getContrast(Mat src,std::vector<Mat> & x_contrast,std::vector<Mat> & y_contrast)496 void getContrast(Mat src, std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast)
497 {
498 int levels = static_cast<int>(logf(static_cast<float>(min(src.rows, src.cols))) / logf(2.0f));
499 x_contrast.resize(levels);
500 y_contrast.resize(levels);
501
502 Mat layer;
503 src.copyTo(layer);
504 for(int i = 0; i < levels; i++) {
505 getGradient(layer, x_contrast[i], 0);
506 getGradient(layer.t(), y_contrast[i], 0);
507 resize(layer, layer, Size(layer.cols / 2, layer.rows / 2));
508 }
509 }
510
calculateSum(std::vector<Mat> & x_contrast,std::vector<Mat> & y_contrast,Mat & sum)511 void calculateSum(std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast, Mat& sum)
512 {
513 sum = Mat::zeros(x_contrast[x_contrast.size() - 1].size(), CV_32F);
514 for(int i = (int)x_contrast.size() - 1; i >= 0; i--)
515 {
516 Mat grad_x, grad_y;
517 getGradient(x_contrast[i], grad_x, 1);
518 getGradient(y_contrast[i], grad_y, 1);
519 resize(sum, sum, x_contrast[i].size());
520 sum += grad_x + grad_y.t();
521 }
522 }
523
calculateProduct(Mat src,Mat & dst)524 void calculateProduct(Mat src, Mat& dst)
525 {
526 std::vector<Mat> x_contrast, y_contrast;
527 getContrast(src, x_contrast, y_contrast);
528 calculateSum(x_contrast, y_contrast, dst);
529 }
530 };
531
createTonemapMantiuk(float gamma,float scale,float saturation)532 Ptr<TonemapMantiuk> createTonemapMantiuk(float gamma, float scale, float saturation)
533 {
534 return makePtr<TonemapMantiukImpl>(gamma, scale, saturation);
535 }
536
537 }
538