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