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 CV_FINAL : 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) CV_OVERRIDE
64     {
65         CV_INSTRUMENT_REGION();
66 
67         Mat src = _src.getMat();
68         CV_Assert(!src.empty());
69         CV_Assert(_src.dims() == 2 && _src.type() == CV_32FC3);
70         _dst.create(src.size(), CV_32FC3);
71         Mat dst = _dst.getMat();
72 
73         double min, max;
74         minMaxLoc(src, &min, &max);
75         if(max - min > DBL_EPSILON) {
76             dst = (src - min) / (max - min);
77         } else {
78             src.copyTo(dst);
79         }
80 
81         pow(dst, 1.0f / gamma, dst);
82     }
83 
getGamma() const84     float getGamma() const CV_OVERRIDE { return gamma; }
setGamma(float val)85     void setGamma(float val) CV_OVERRIDE { gamma = val; }
86 
write(FileStorage & fs) const87     void write(FileStorage& fs) const CV_OVERRIDE
88     {
89         writeFormat(fs);
90         fs << "name" << name
91            << "gamma" << gamma;
92     }
93 
read(const FileNode & fn)94     void read(const FileNode& fn) CV_OVERRIDE
95     {
96         FileNode n = fn["name"];
97         CV_Assert(n.isString() && String(n) == name);
98         gamma = fn["gamma"];
99     }
100 
101 protected:
102     String name;
103     float gamma;
104 };
105 
createTonemap(float gamma)106 Ptr<Tonemap> createTonemap(float gamma)
107 {
108     return makePtr<TonemapImpl>(gamma);
109 }
110 
111 class TonemapDragoImpl CV_FINAL : public TonemapDrago
112 {
113 public:
TonemapDragoImpl(float _gamma,float _saturation,float _bias)114     TonemapDragoImpl(float _gamma, float _saturation, float _bias) :
115         name("TonemapDrago"),
116         gamma(_gamma),
117         saturation(_saturation),
118         bias(_bias)
119     {
120     }
121 
process(InputArray _src,OutputArray _dst)122     void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
123     {
124         CV_INSTRUMENT_REGION();
125 
126         Mat src = _src.getMat();
127         CV_Assert(!src.empty());
128         _dst.create(src.size(), CV_32FC3);
129         Mat img = _dst.getMat();
130 
131         Ptr<Tonemap> linear = createTonemap(1.0f);
132         linear->process(src, img);
133 
134         Mat gray_img;
135         cvtColor(img, gray_img, COLOR_RGB2GRAY);
136         Mat log_img;
137         log_(gray_img, log_img);
138         float mean = expf(static_cast<float>(sum(log_img)[0]) / log_img.total());
139         gray_img /= mean;
140         log_img.release();
141 
142         double max;
143         minMaxLoc(gray_img, NULL, &max);
144         CV_Assert(max > 0);
145 
146         Mat map;
147         log(gray_img + 1.0f, map);
148         Mat div;
149         pow(gray_img / static_cast<float>(max), logf(bias) / logf(0.5f), div);
150         log(2.0f + 8.0f * div, div);
151         map = map.mul(1.0f / div);
152         div.release();
153 
154         mapLuminance(img, img, gray_img, map, saturation);
155 
156         linear->setGamma(gamma);
157         linear->process(img, img);
158     }
159 
getGamma() const160     float getGamma() const CV_OVERRIDE { return gamma; }
setGamma(float val)161     void setGamma(float val) CV_OVERRIDE { gamma = val; }
162 
getSaturation() const163     float getSaturation() const CV_OVERRIDE { return saturation; }
setSaturation(float val)164     void setSaturation(float val) CV_OVERRIDE { saturation = val; }
165 
getBias() const166     float getBias() const CV_OVERRIDE { return bias; }
setBias(float val)167     void setBias(float val) CV_OVERRIDE { bias = val; }
168 
write(FileStorage & fs) const169     void write(FileStorage& fs) const CV_OVERRIDE
170     {
171         writeFormat(fs);
172         fs << "name" << name
173            << "gamma" << gamma
174            << "bias" << bias
175            << "saturation" << saturation;
176     }
177 
read(const FileNode & fn)178     void read(const FileNode& fn) CV_OVERRIDE
179     {
180         FileNode n = fn["name"];
181         CV_Assert(n.isString() && String(n) == name);
182         gamma = fn["gamma"];
183         bias = fn["bias"];
184         saturation = fn["saturation"];
185     }
186 
187 protected:
188     String name;
189     float gamma, saturation, bias;
190 };
191 
createTonemapDrago(float gamma,float saturation,float bias)192 Ptr<TonemapDrago> createTonemapDrago(float gamma, float saturation, float bias)
193 {
194     return makePtr<TonemapDragoImpl>(gamma, saturation, bias);
195 }
196 
197 class TonemapReinhardImpl CV_FINAL : public TonemapReinhard
198 {
199 public:
TonemapReinhardImpl(float _gamma,float _intensity,float _light_adapt,float _color_adapt)200     TonemapReinhardImpl(float _gamma, float _intensity, float _light_adapt, float _color_adapt) :
201         name("TonemapReinhard"),
202         gamma(_gamma),
203         intensity(_intensity),
204         light_adapt(_light_adapt),
205         color_adapt(_color_adapt)
206     {
207     }
208 
process(InputArray _src,OutputArray _dst)209     void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
210     {
211         CV_INSTRUMENT_REGION();
212 
213         Mat src = _src.getMat();
214         CV_Assert(!src.empty());
215         _dst.create(src.size(), CV_32FC3);
216         Mat img = _dst.getMat();
217         Ptr<Tonemap> linear = createTonemap(1.0f);
218         linear->process(src, img);
219 
220         Mat gray_img;
221         cvtColor(img, gray_img, COLOR_RGB2GRAY);
222         Mat log_img;
223         log_(gray_img, log_img);
224 
225         float log_mean = static_cast<float>(sum(log_img)[0] / log_img.total());
226         double log_min, log_max;
227         minMaxLoc(log_img, &log_min, &log_max);
228         log_img.release();
229 
230         double key = static_cast<float>((log_max - log_mean) / (log_max - log_min));
231         float map_key = 0.3f + 0.7f * pow(static_cast<float>(key), 1.4f);
232         intensity = exp(-intensity);
233         Scalar chan_mean = mean(img);
234         float gray_mean = static_cast<float>(mean(gray_img)[0]);
235 
236         std::vector<Mat> channels(3);
237         split(img, channels);
238 
239         for(int i = 0; i < 3; i++) {
240             float global = color_adapt * static_cast<float>(chan_mean[i]) + (1.0f - color_adapt) * gray_mean;
241             Mat adapt = color_adapt * channels[i] + (1.0f - color_adapt) * gray_img;
242             adapt = light_adapt * adapt + (1.0f - light_adapt) * global;
243             pow(intensity * adapt, map_key, adapt);
244             channels[i] = channels[i].mul(1.0f / (adapt + channels[i]));
245         }
246         gray_img.release();
247         merge(channels, img);
248 
249         linear->setGamma(gamma);
250         linear->process(img, img);
251     }
252 
getGamma() const253     float getGamma() const CV_OVERRIDE { return gamma; }
setGamma(float val)254     void setGamma(float val) CV_OVERRIDE { gamma = val; }
255 
getIntensity() const256     float getIntensity() const CV_OVERRIDE { return intensity; }
setIntensity(float val)257     void setIntensity(float val) CV_OVERRIDE { intensity = val; }
258 
getLightAdaptation() const259     float getLightAdaptation() const CV_OVERRIDE { return light_adapt; }
setLightAdaptation(float val)260     void setLightAdaptation(float val) CV_OVERRIDE { light_adapt = val; }
261 
getColorAdaptation() const262     float getColorAdaptation() const CV_OVERRIDE { return color_adapt; }
setColorAdaptation(float val)263     void setColorAdaptation(float val) CV_OVERRIDE { color_adapt = val; }
264 
write(FileStorage & fs) const265     void write(FileStorage& fs) const CV_OVERRIDE
266     {
267         writeFormat(fs);
268         fs << "name" << name
269            << "gamma" << gamma
270            << "intensity" << intensity
271            << "light_adapt" << light_adapt
272            << "color_adapt" << color_adapt;
273     }
274 
read(const FileNode & fn)275     void read(const FileNode& fn) CV_OVERRIDE
276     {
277         FileNode n = fn["name"];
278         CV_Assert(n.isString() && String(n) == name);
279         gamma = fn["gamma"];
280         intensity = fn["intensity"];
281         light_adapt = fn["light_adapt"];
282         color_adapt = fn["color_adapt"];
283     }
284 
285 protected:
286     String name;
287     float gamma, intensity, light_adapt, color_adapt;
288 };
289 
createTonemapReinhard(float gamma,float contrast,float sigma_color,float sigma_space)290 Ptr<TonemapReinhard> createTonemapReinhard(float gamma, float contrast, float sigma_color, float sigma_space)
291 {
292     return makePtr<TonemapReinhardImpl>(gamma, contrast, sigma_color, sigma_space);
293 }
294 
295 class TonemapMantiukImpl CV_FINAL : public TonemapMantiuk
296 {
297 public:
TonemapMantiukImpl(float _gamma,float _scale,float _saturation)298     TonemapMantiukImpl(float _gamma, float _scale, float _saturation) :
299         name("TonemapMantiuk"),
300         gamma(_gamma),
301         scale(_scale),
302         saturation(_saturation)
303     {
304     }
305 
process(InputArray _src,OutputArray _dst)306     void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
307     {
308         CV_INSTRUMENT_REGION();
309 
310         Mat src = _src.getMat();
311         CV_Assert(!src.empty());
312         _dst.create(src.size(), CV_32FC3);
313         Mat img = _dst.getMat();
314         Ptr<Tonemap> linear = createTonemap(1.0f);
315         linear->process(src, img);
316 
317         Mat gray_img;
318         cvtColor(img, gray_img, COLOR_RGB2GRAY);
319         Mat log_img;
320         log_(gray_img, log_img);
321 
322         std::vector<Mat> x_contrast, y_contrast;
323         getContrast(log_img, x_contrast, y_contrast);
324 
325         for(size_t i = 0; i < x_contrast.size(); i++) {
326             mapContrast(x_contrast[i]);
327             mapContrast(y_contrast[i]);
328         }
329 
330         Mat right(src.size(), CV_32F);
331         calculateSum(x_contrast, y_contrast, right);
332 
333         Mat p, r, product, x = log_img;
334         calculateProduct(x, r);
335         r = right - r;
336         r.copyTo(p);
337 
338         const float target_error = 1e-3f;
339         float target_norm = static_cast<float>(right.dot(right)) * powf(target_error, 2.0f);
340         int max_iterations = 100;
341         float rr = static_cast<float>(r.dot(r));
342 
343         for(int i = 0; i < max_iterations; i++)
344         {
345             calculateProduct(p, product);
346             double dprod = p.dot(product);
347             CV_Assert(fabs(dprod) > 0);
348             float alpha = rr / static_cast<float>(dprod);
349 
350             r -= alpha * product;
351             x += alpha * p;
352 
353             float new_rr = static_cast<float>(r.dot(r));
354             CV_Assert(fabs(rr) > 0);
355             p = r + (new_rr / rr) * p;
356             rr = new_rr;
357 
358             if(rr < target_norm) {
359                 break;
360             }
361         }
362         exp(x, x);
363         mapLuminance(img, img, gray_img, x, saturation);
364 
365         linear = createTonemap(gamma);
366         linear->process(img, img);
367     }
368 
getGamma() const369     float getGamma() const CV_OVERRIDE { return gamma; }
setGamma(float val)370     void setGamma(float val) CV_OVERRIDE { gamma = val; }
371 
getScale() const372     float getScale() const CV_OVERRIDE { return scale; }
setScale(float val)373     void setScale(float val) CV_OVERRIDE { scale = val; }
374 
getSaturation() const375     float getSaturation() const CV_OVERRIDE { return saturation; }
setSaturation(float val)376     void setSaturation(float val) CV_OVERRIDE { saturation = val; }
377 
write(FileStorage & fs) const378     void write(FileStorage& fs) const CV_OVERRIDE
379     {
380         writeFormat(fs);
381         fs << "name" << name
382            << "gamma" << gamma
383            << "scale" << scale
384            << "saturation" << saturation;
385     }
386 
read(const FileNode & fn)387     void read(const FileNode& fn) CV_OVERRIDE
388     {
389         FileNode n = fn["name"];
390         CV_Assert(n.isString() && String(n) == name);
391         gamma = fn["gamma"];
392         scale = fn["scale"];
393         saturation = fn["saturation"];
394     }
395 
396 protected:
397     String name;
398     float gamma, scale, saturation;
399 
signedPow(Mat src,float power,Mat & dst)400     void signedPow(Mat src, float power, Mat& dst)
401     {
402         Mat sign = (src > 0);
403         sign.convertTo(sign, CV_32F, 1.0f/255.0f);
404         sign = sign * 2.0f - 1.0f;
405         pow(abs(src), power, dst);
406         dst = dst.mul(sign);
407     }
408 
mapContrast(Mat & contrast)409     void mapContrast(Mat& contrast)
410     {
411         const float response_power = 0.4185f;
412         signedPow(contrast, response_power, contrast);
413         contrast *= scale;
414         signedPow(contrast, 1.0f / response_power, contrast);
415     }
416 
getGradient(Mat src,Mat & dst,int pos)417     void getGradient(Mat src, Mat& dst, int pos)
418     {
419         dst = Mat::zeros(src.size(), CV_32F);
420         Mat a, b;
421         Mat grad = src.colRange(1, src.cols) - src.colRange(0, src.cols - 1);
422         grad.copyTo(dst.colRange(pos, src.cols + pos - 1));
423         if(pos == 1) {
424             src.col(0).copyTo(dst.col(0));
425         }
426     }
427 
getContrast(Mat src,std::vector<Mat> & x_contrast,std::vector<Mat> & y_contrast)428     void getContrast(Mat src, std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast)
429     {
430         int levels = static_cast<int>(logf(static_cast<float>(min(src.rows, src.cols))) / logf(2.0f));
431         x_contrast.resize(levels);
432         y_contrast.resize(levels);
433 
434         Mat layer;
435         src.copyTo(layer);
436         for(int i = 0; i < levels; i++) {
437             getGradient(layer, x_contrast[i], 0);
438             getGradient(layer.t(), y_contrast[i], 0);
439             resize(layer, layer, Size(layer.cols / 2, layer.rows / 2), 0, 0, INTER_LINEAR);
440         }
441     }
442 
calculateSum(std::vector<Mat> & x_contrast,std::vector<Mat> & y_contrast,Mat & sum)443     void calculateSum(std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast, Mat& sum)
444     {
445         if (x_contrast.empty())
446             return;
447         const int last = (int)x_contrast.size() - 1;
448         sum = Mat::zeros(x_contrast[last].size(), CV_32F);
449         for(int i = last; i >= 0; i--)
450         {
451             Mat grad_x, grad_y;
452             getGradient(x_contrast[i], grad_x, 1);
453             getGradient(y_contrast[i], grad_y, 1);
454             resize(sum, sum, x_contrast[i].size(), 0, 0, INTER_LINEAR);
455             sum += grad_x + grad_y.t();
456         }
457     }
458 
calculateProduct(Mat src,Mat & dst)459     void calculateProduct(Mat src, Mat& dst)
460     {
461         std::vector<Mat> x_contrast, y_contrast;
462         getContrast(src, x_contrast, y_contrast);
463         calculateSum(x_contrast, y_contrast, dst);
464     }
465 };
466 
createTonemapMantiuk(float gamma,float scale,float saturation)467 Ptr<TonemapMantiuk> createTonemapMantiuk(float gamma, float scale, float saturation)
468 {
469     return makePtr<TonemapMantiukImpl>(gamma, scale, saturation);
470 }
471 
472 }
473