1
/*M///////////////////////////////////////////////////////////////////////////////////////
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
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.
11
// For Open Source Computer Vision Library
13
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
14
// Third party copyrights are property of their respective owners.
16
// Redistribution and use in source and binary forms, with or without modification,
17
// are permitted provided that the following conditions are met:
19
// * Redistribution's of source code must retain the above copyright notice,
20
// this list of conditions and the following disclaimer.
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.
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.
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.
42
#include "precomp.hpp"
43
#include "opencv2/photo.hpp"
44
#include "opencv2/imgproc.hpp"
45
#include "hdr_common.hpp"
50
inline void log_(const Mat& src, Mat& dst)
52
max(src, Scalar::all(1e-4), dst);
56
class TonemapImpl : public Tonemap
59
TonemapImpl(float _gamma) : name("Tonemap"), gamma(_gamma)
63
void process(InputArray _src, OutputArray _dst)
65
Mat src = _src.getMat();
66
CV_Assert(!src.empty());
67
_dst.create(src.size(), CV_32FC3);
68
Mat dst = _dst.getMat();
71
minMaxLoc(src, &min, &max);
72
if(max - min > DBL_EPSILON) {
73
dst = (src - min) / (max - min);
78
pow(dst, 1.0f / gamma, dst);
81
float getGamma() const { return gamma; }
82
void setGamma(float val) { gamma = val; }
84
void write(FileStorage& fs) const
90
void read(const FileNode& fn)
92
FileNode n = fn["name"];
93
CV_Assert(n.isString() && String(n) == name);
102
Ptr<Tonemap> createTonemap(float gamma)
104
return makePtr<TonemapImpl>(gamma);
107
class TonemapDragoImpl : public TonemapDrago
110
TonemapDragoImpl(float _gamma, float _saturation, float _bias) :
111
name("TonemapDrago"),
113
saturation(_saturation),
118
void process(InputArray _src, OutputArray _dst)
120
Mat src = _src.getMat();
121
CV_Assert(!src.empty());
122
_dst.create(src.size(), CV_32FC3);
123
Mat img = _dst.getMat();
125
Ptr<Tonemap> linear = createTonemap(1.0f);
126
linear->process(src, img);
129
cvtColor(img, gray_img, COLOR_RGB2GRAY);
131
log_(gray_img, log_img);
132
float mean = expf(static_cast<float>(sum(log_img)[0]) / log_img.total());
137
minMaxLoc(gray_img, NULL, &max);
140
log(gray_img + 1.0f, map);
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);
147
mapLuminance(img, img, gray_img, map, saturation);
149
linear->setGamma(gamma);
150
linear->process(img, img);
153
float getGamma() const { return gamma; }
154
void setGamma(float val) { gamma = val; }
156
float getSaturation() const { return saturation; }
157
void setSaturation(float val) { saturation = val; }
159
float getBias() const { return bias; }
160
void setBias(float val) { bias = val; }
162
void write(FileStorage& fs) const
167
<< "saturation" << saturation;
170
void read(const FileNode& fn)
172
FileNode n = fn["name"];
173
CV_Assert(n.isString() && String(n) == name);
176
saturation = fn["saturation"];
181
float gamma, saturation, bias;
184
Ptr<TonemapDrago> createTonemapDrago(float gamma, float saturation, float bias)
186
return makePtr<TonemapDragoImpl>(gamma, saturation, bias);
189
class TonemapDurandImpl : public TonemapDurand
192
TonemapDurandImpl(float _gamma, float _contrast, float _saturation, float _sigma_color, float _sigma_space) :
193
name("TonemapDurand"),
196
saturation(_saturation),
197
sigma_color(_sigma_color),
198
sigma_space(_sigma_space)
202
void process(InputArray _src, OutputArray _dst)
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);
212
cvtColor(img, gray_img, COLOR_RGB2GRAY);
214
log_(gray_img, log_img);
216
bilateralFilter(log_img, map_img, -1, sigma_color, sigma_space);
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);
224
mapLuminance(img, img, gray_img, map_img, saturation);
225
pow(img, 1.0f / gamma, img);
228
float getGamma() const { return gamma; }
229
void setGamma(float val) { gamma = val; }
231
float getSaturation() const { return saturation; }
232
void setSaturation(float val) { saturation = val; }
234
float getContrast() const { return contrast; }
235
void setContrast(float val) { contrast = val; }
237
float getSigmaColor() const { return sigma_color; }
238
void setSigmaColor(float val) { sigma_color = val; }
240
float getSigmaSpace() const { return sigma_space; }
241
void setSigmaSpace(float val) { sigma_space = val; }
243
void write(FileStorage& fs) const
247
<< "contrast" << contrast
248
<< "sigma_color" << sigma_color
249
<< "sigma_space" << sigma_space
250
<< "saturation" << saturation;
253
void read(const FileNode& fn)
255
FileNode n = fn["name"];
256
CV_Assert(n.isString() && String(n) == name);
258
contrast = fn["contrast"];
259
sigma_color = fn["sigma_color"];
260
sigma_space = fn["sigma_space"];
261
saturation = fn["saturation"];
266
float gamma, contrast, saturation, sigma_color, sigma_space;
269
Ptr<TonemapDurand> createTonemapDurand(float gamma, float contrast, float saturation, float sigma_color, float sigma_space)
271
return makePtr<TonemapDurandImpl>(gamma, contrast, saturation, sigma_color, sigma_space);
274
class TonemapReinhardImpl : public TonemapReinhard
277
TonemapReinhardImpl(float _gamma, float _intensity, float _light_adapt, float _color_adapt) :
278
name("TonemapReinhard"),
280
intensity(_intensity),
281
light_adapt(_light_adapt),
282
color_adapt(_color_adapt)
286
void process(InputArray _src, OutputArray _dst)
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);
296
cvtColor(img, gray_img, COLOR_RGB2GRAY);
298
log_(gray_img, log_img);
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);
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]);
311
std::vector<Mat> channels(3);
312
split(img, channels);
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]));
322
merge(channels, img);
324
linear->setGamma(gamma);
325
linear->process(img, img);
328
float getGamma() const { return gamma; }
329
void setGamma(float val) { gamma = val; }
331
float getIntensity() const { return intensity; }
332
void setIntensity(float val) { intensity = val; }
334
float getLightAdaptation() const { return light_adapt; }
335
void setLightAdaptation(float val) { light_adapt = val; }
337
float getColorAdaptation() const { return color_adapt; }
338
void setColorAdaptation(float val) { color_adapt = val; }
340
void write(FileStorage& fs) const
344
<< "intensity" << intensity
345
<< "light_adapt" << light_adapt
346
<< "color_adapt" << color_adapt;
349
void read(const FileNode& fn)
351
FileNode n = fn["name"];
352
CV_Assert(n.isString() && String(n) == name);
354
intensity = fn["intensity"];
355
light_adapt = fn["light_adapt"];
356
color_adapt = fn["color_adapt"];
361
float gamma, intensity, light_adapt, color_adapt;
364
Ptr<TonemapReinhard> createTonemapReinhard(float gamma, float contrast, float sigma_color, float sigma_space)
366
return makePtr<TonemapReinhardImpl>(gamma, contrast, sigma_color, sigma_space);
369
class TonemapMantiukImpl : public TonemapMantiuk
372
TonemapMantiukImpl(float _gamma, float _scale, float _saturation) :
373
name("TonemapMantiuk"),
376
saturation(_saturation)
380
void process(InputArray _src, OutputArray _dst)
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);
390
cvtColor(img, gray_img, COLOR_RGB2GRAY);
392
log_(gray_img, log_img);
394
std::vector<Mat> x_contrast, y_contrast;
395
getContrast(log_img, x_contrast, y_contrast);
397
for(size_t i = 0; i < x_contrast.size(); i++) {
398
mapContrast(x_contrast[i]);
399
mapContrast(y_contrast[i]);
402
Mat right(src.size(), CV_32F);
403
calculateSum(x_contrast, y_contrast, right);
405
Mat p, r, product, x = log_img;
406
calculateProduct(x, r);
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));
415
for(int i = 0; i < max_iterations; i++)
417
calculateProduct(p, product);
418
float alpha = rr / static_cast<float>(p.dot(product));
420
r -= alpha * product;
423
float new_rr = static_cast<float>(r.dot(r));
424
p = r + (new_rr / rr) * p;
427
if(rr < target_norm) {
432
mapLuminance(img, img, gray_img, x, saturation);
434
linear = createTonemap(gamma);
435
linear->process(img, img);
438
float getGamma() const { return gamma; }
439
void setGamma(float val) { gamma = val; }
441
float getScale() const { return scale; }
442
void setScale(float val) { scale = val; }
444
float getSaturation() const { return saturation; }
445
void setSaturation(float val) { saturation = val; }
447
void write(FileStorage& fs) const
452
<< "saturation" << saturation;
455
void read(const FileNode& fn)
457
FileNode n = fn["name"];
458
CV_Assert(n.isString() && String(n) == name);
461
saturation = fn["saturation"];
466
float gamma, scale, saturation;
468
void signedPow(Mat src, float power, Mat& dst)
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);
477
void mapContrast(Mat& contrast)
479
const float response_power = 0.4185f;
480
signedPow(contrast, response_power, contrast);
482
signedPow(contrast, 1.0f / response_power, contrast);
485
void getGradient(Mat src, Mat& dst, int pos)
487
dst = Mat::zeros(src.size(), CV_32F);
489
Mat grad = src.colRange(1, src.cols) - src.colRange(0, src.cols - 1);
490
grad.copyTo(dst.colRange(pos, src.cols + pos - 1));
492
src.col(0).copyTo(dst.col(0));
496
void getContrast(Mat src, std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast)
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);
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));
511
void calculateSum(std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast, Mat& sum)
513
if (x_contrast.empty())
515
const int last = (int)x_contrast.size() - 1;
516
sum = Mat::zeros(x_contrast[last].size(), CV_32F);
517
for(int i = last; i >= 0; i--)
520
getGradient(x_contrast[i], grad_x, 1);
521
getGradient(y_contrast[i], grad_y, 1);
522
resize(sum, sum, x_contrast[i].size());
523
sum += grad_x + grad_y.t();
527
void calculateProduct(Mat src, Mat& dst)
529
std::vector<Mat> x_contrast, y_contrast;
530
getContrast(src, x_contrast, y_contrast);
531
calculateSum(x_contrast, y_contrast, dst);
535
Ptr<TonemapMantiuk> createTonemapMantiuk(float gamma, float scale, float saturation)
537
return makePtr<TonemapMantiukImpl>(gamma, scale, saturation);