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