Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/photo/src/tonemap.cpp
16344 views
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
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:
59
TonemapImpl(float _gamma) : name("Tonemap"), gamma(_gamma)
60
{
61
}
62
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
_dst.create(src.size(), CV_32FC3);
70
Mat dst = _dst.getMat();
71
72
double min, max;
73
minMaxLoc(src, &min, &max);
74
if(max - min > DBL_EPSILON) {
75
dst = (src - min) / (max - min);
76
} else {
77
src.copyTo(dst);
78
}
79
80
pow(dst, 1.0f / gamma, dst);
81
}
82
83
float getGamma() const CV_OVERRIDE { return gamma; }
84
void setGamma(float val) CV_OVERRIDE { gamma = val; }
85
86
void write(FileStorage& fs) const CV_OVERRIDE
87
{
88
writeFormat(fs);
89
fs << "name" << name
90
<< "gamma" << gamma;
91
}
92
93
void read(const FileNode& fn) CV_OVERRIDE
94
{
95
FileNode n = fn["name"];
96
CV_Assert(n.isString() && String(n) == name);
97
gamma = fn["gamma"];
98
}
99
100
protected:
101
String name;
102
float gamma;
103
};
104
105
Ptr<Tonemap> createTonemap(float gamma)
106
{
107
return makePtr<TonemapImpl>(gamma);
108
}
109
110
class TonemapDragoImpl CV_FINAL : public TonemapDrago
111
{
112
public:
113
TonemapDragoImpl(float _gamma, float _saturation, float _bias) :
114
name("TonemapDrago"),
115
gamma(_gamma),
116
saturation(_saturation),
117
bias(_bias)
118
{
119
}
120
121
void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
122
{
123
CV_INSTRUMENT_REGION();
124
125
Mat src = _src.getMat();
126
CV_Assert(!src.empty());
127
_dst.create(src.size(), CV_32FC3);
128
Mat img = _dst.getMat();
129
130
Ptr<Tonemap> linear = createTonemap(1.0f);
131
linear->process(src, img);
132
133
Mat gray_img;
134
cvtColor(img, gray_img, COLOR_RGB2GRAY);
135
Mat log_img;
136
log_(gray_img, log_img);
137
float mean = expf(static_cast<float>(sum(log_img)[0]) / log_img.total());
138
gray_img /= mean;
139
log_img.release();
140
141
double max;
142
minMaxLoc(gray_img, NULL, &max);
143
CV_Assert(max > 0);
144
145
Mat map;
146
log(gray_img + 1.0f, map);
147
Mat div;
148
pow(gray_img / static_cast<float>(max), logf(bias) / logf(0.5f), div);
149
log(2.0f + 8.0f * div, div);
150
map = map.mul(1.0f / div);
151
div.release();
152
153
mapLuminance(img, img, gray_img, map, saturation);
154
155
linear->setGamma(gamma);
156
linear->process(img, img);
157
}
158
159
float getGamma() const CV_OVERRIDE { return gamma; }
160
void setGamma(float val) CV_OVERRIDE { gamma = val; }
161
162
float getSaturation() const CV_OVERRIDE { return saturation; }
163
void setSaturation(float val) CV_OVERRIDE { saturation = val; }
164
165
float getBias() const CV_OVERRIDE { return bias; }
166
void setBias(float val) CV_OVERRIDE { bias = val; }
167
168
void write(FileStorage& fs) const CV_OVERRIDE
169
{
170
writeFormat(fs);
171
fs << "name" << name
172
<< "gamma" << gamma
173
<< "bias" << bias
174
<< "saturation" << saturation;
175
}
176
177
void read(const FileNode& fn) CV_OVERRIDE
178
{
179
FileNode n = fn["name"];
180
CV_Assert(n.isString() && String(n) == name);
181
gamma = fn["gamma"];
182
bias = fn["bias"];
183
saturation = fn["saturation"];
184
}
185
186
protected:
187
String name;
188
float gamma, saturation, bias;
189
};
190
191
Ptr<TonemapDrago> createTonemapDrago(float gamma, float saturation, float bias)
192
{
193
return makePtr<TonemapDragoImpl>(gamma, saturation, bias);
194
}
195
196
class TonemapDurandImpl CV_FINAL : public TonemapDurand
197
{
198
public:
199
TonemapDurandImpl(float _gamma, float _contrast, float _saturation, float _sigma_color, float _sigma_space) :
200
name("TonemapDurand"),
201
gamma(_gamma),
202
contrast(_contrast),
203
saturation(_saturation),
204
sigma_color(_sigma_color),
205
sigma_space(_sigma_space)
206
{
207
}
208
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
Mat map_img;
225
bilateralFilter(log_img, map_img, -1, sigma_color, sigma_space);
226
227
double min, max;
228
minMaxLoc(map_img, &min, &max);
229
float scale = contrast / static_cast<float>(max - min);
230
exp(map_img * (scale - 1.0f) + log_img, map_img);
231
log_img.release();
232
233
mapLuminance(img, img, gray_img, map_img, saturation);
234
pow(img, 1.0f / gamma, img);
235
}
236
237
float getGamma() const CV_OVERRIDE { return gamma; }
238
void setGamma(float val) CV_OVERRIDE { gamma = val; }
239
240
float getSaturation() const CV_OVERRIDE { return saturation; }
241
void setSaturation(float val) CV_OVERRIDE { saturation = val; }
242
243
float getContrast() const CV_OVERRIDE { return contrast; }
244
void setContrast(float val) CV_OVERRIDE { contrast = val; }
245
246
float getSigmaColor() const CV_OVERRIDE { return sigma_color; }
247
void setSigmaColor(float val) CV_OVERRIDE { sigma_color = val; }
248
249
float getSigmaSpace() const CV_OVERRIDE { return sigma_space; }
250
void setSigmaSpace(float val) CV_OVERRIDE { sigma_space = val; }
251
252
void write(FileStorage& fs) const CV_OVERRIDE
253
{
254
writeFormat(fs);
255
fs << "name" << name
256
<< "gamma" << gamma
257
<< "contrast" << contrast
258
<< "sigma_color" << sigma_color
259
<< "sigma_space" << sigma_space
260
<< "saturation" << saturation;
261
}
262
263
void read(const FileNode& fn) CV_OVERRIDE
264
{
265
FileNode n = fn["name"];
266
CV_Assert(n.isString() && String(n) == name);
267
gamma = fn["gamma"];
268
contrast = fn["contrast"];
269
sigma_color = fn["sigma_color"];
270
sigma_space = fn["sigma_space"];
271
saturation = fn["saturation"];
272
}
273
274
protected:
275
String name;
276
float gamma, contrast, saturation, sigma_color, sigma_space;
277
};
278
279
Ptr<TonemapDurand> createTonemapDurand(float gamma, float contrast, float saturation, float sigma_color, float sigma_space)
280
{
281
return makePtr<TonemapDurandImpl>(gamma, contrast, saturation, sigma_color, sigma_space);
282
}
283
284
class TonemapReinhardImpl CV_FINAL : public TonemapReinhard
285
{
286
public:
287
TonemapReinhardImpl(float _gamma, float _intensity, float _light_adapt, float _color_adapt) :
288
name("TonemapReinhard"),
289
gamma(_gamma),
290
intensity(_intensity),
291
light_adapt(_light_adapt),
292
color_adapt(_color_adapt)
293
{
294
}
295
296
void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
297
{
298
CV_INSTRUMENT_REGION();
299
300
Mat src = _src.getMat();
301
CV_Assert(!src.empty());
302
_dst.create(src.size(), CV_32FC3);
303
Mat img = _dst.getMat();
304
Ptr<Tonemap> linear = createTonemap(1.0f);
305
linear->process(src, img);
306
307
Mat gray_img;
308
cvtColor(img, gray_img, COLOR_RGB2GRAY);
309
Mat log_img;
310
log_(gray_img, log_img);
311
312
float log_mean = static_cast<float>(sum(log_img)[0] / log_img.total());
313
double log_min, log_max;
314
minMaxLoc(log_img, &log_min, &log_max);
315
log_img.release();
316
317
double key = static_cast<float>((log_max - log_mean) / (log_max - log_min));
318
float map_key = 0.3f + 0.7f * pow(static_cast<float>(key), 1.4f);
319
intensity = exp(-intensity);
320
Scalar chan_mean = mean(img);
321
float gray_mean = static_cast<float>(mean(gray_img)[0]);
322
323
std::vector<Mat> channels(3);
324
split(img, channels);
325
326
for(int i = 0; i < 3; i++) {
327
float global = color_adapt * static_cast<float>(chan_mean[i]) + (1.0f - color_adapt) * gray_mean;
328
Mat adapt = color_adapt * channels[i] + (1.0f - color_adapt) * gray_img;
329
adapt = light_adapt * adapt + (1.0f - light_adapt) * global;
330
pow(intensity * adapt, map_key, adapt);
331
channels[i] = channels[i].mul(1.0f / (adapt + channels[i]));
332
}
333
gray_img.release();
334
merge(channels, img);
335
336
linear->setGamma(gamma);
337
linear->process(img, img);
338
}
339
340
float getGamma() const CV_OVERRIDE { return gamma; }
341
void setGamma(float val) CV_OVERRIDE { gamma = val; }
342
343
float getIntensity() const CV_OVERRIDE { return intensity; }
344
void setIntensity(float val) CV_OVERRIDE { intensity = val; }
345
346
float getLightAdaptation() const CV_OVERRIDE { return light_adapt; }
347
void setLightAdaptation(float val) CV_OVERRIDE { light_adapt = val; }
348
349
float getColorAdaptation() const CV_OVERRIDE { return color_adapt; }
350
void setColorAdaptation(float val) CV_OVERRIDE { color_adapt = val; }
351
352
void write(FileStorage& fs) const CV_OVERRIDE
353
{
354
writeFormat(fs);
355
fs << "name" << name
356
<< "gamma" << gamma
357
<< "intensity" << intensity
358
<< "light_adapt" << light_adapt
359
<< "color_adapt" << color_adapt;
360
}
361
362
void read(const FileNode& fn) CV_OVERRIDE
363
{
364
FileNode n = fn["name"];
365
CV_Assert(n.isString() && String(n) == name);
366
gamma = fn["gamma"];
367
intensity = fn["intensity"];
368
light_adapt = fn["light_adapt"];
369
color_adapt = fn["color_adapt"];
370
}
371
372
protected:
373
String name;
374
float gamma, intensity, light_adapt, color_adapt;
375
};
376
377
Ptr<TonemapReinhard> createTonemapReinhard(float gamma, float contrast, float sigma_color, float sigma_space)
378
{
379
return makePtr<TonemapReinhardImpl>(gamma, contrast, sigma_color, sigma_space);
380
}
381
382
class TonemapMantiukImpl CV_FINAL : public TonemapMantiuk
383
{
384
public:
385
TonemapMantiukImpl(float _gamma, float _scale, float _saturation) :
386
name("TonemapMantiuk"),
387
gamma(_gamma),
388
scale(_scale),
389
saturation(_saturation)
390
{
391
}
392
393
void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
394
{
395
CV_INSTRUMENT_REGION();
396
397
Mat src = _src.getMat();
398
CV_Assert(!src.empty());
399
_dst.create(src.size(), CV_32FC3);
400
Mat img = _dst.getMat();
401
Ptr<Tonemap> linear = createTonemap(1.0f);
402
linear->process(src, img);
403
404
Mat gray_img;
405
cvtColor(img, gray_img, COLOR_RGB2GRAY);
406
Mat log_img;
407
log_(gray_img, log_img);
408
409
std::vector<Mat> x_contrast, y_contrast;
410
getContrast(log_img, x_contrast, y_contrast);
411
412
for(size_t i = 0; i < x_contrast.size(); i++) {
413
mapContrast(x_contrast[i]);
414
mapContrast(y_contrast[i]);
415
}
416
417
Mat right(src.size(), CV_32F);
418
calculateSum(x_contrast, y_contrast, right);
419
420
Mat p, r, product, x = log_img;
421
calculateProduct(x, r);
422
r = right - r;
423
r.copyTo(p);
424
425
const float target_error = 1e-3f;
426
float target_norm = static_cast<float>(right.dot(right)) * powf(target_error, 2.0f);
427
int max_iterations = 100;
428
float rr = static_cast<float>(r.dot(r));
429
430
for(int i = 0; i < max_iterations; i++)
431
{
432
calculateProduct(p, product);
433
double dprod = p.dot(product);
434
CV_Assert(fabs(dprod) > 0);
435
float alpha = rr / static_cast<float>(dprod);
436
437
r -= alpha * product;
438
x += alpha * p;
439
440
float new_rr = static_cast<float>(r.dot(r));
441
CV_Assert(fabs(rr) > 0);
442
p = r + (new_rr / rr) * p;
443
rr = new_rr;
444
445
if(rr < target_norm) {
446
break;
447
}
448
}
449
exp(x, x);
450
mapLuminance(img, img, gray_img, x, saturation);
451
452
linear = createTonemap(gamma);
453
linear->process(img, img);
454
}
455
456
float getGamma() const CV_OVERRIDE { return gamma; }
457
void setGamma(float val) CV_OVERRIDE { gamma = val; }
458
459
float getScale() const CV_OVERRIDE { return scale; }
460
void setScale(float val) CV_OVERRIDE { scale = val; }
461
462
float getSaturation() const CV_OVERRIDE { return saturation; }
463
void setSaturation(float val) CV_OVERRIDE { saturation = val; }
464
465
void write(FileStorage& fs) const CV_OVERRIDE
466
{
467
writeFormat(fs);
468
fs << "name" << name
469
<< "gamma" << gamma
470
<< "scale" << scale
471
<< "saturation" << saturation;
472
}
473
474
void read(const FileNode& fn) CV_OVERRIDE
475
{
476
FileNode n = fn["name"];
477
CV_Assert(n.isString() && String(n) == name);
478
gamma = fn["gamma"];
479
scale = fn["scale"];
480
saturation = fn["saturation"];
481
}
482
483
protected:
484
String name;
485
float gamma, scale, saturation;
486
487
void signedPow(Mat src, float power, Mat& dst)
488
{
489
Mat sign = (src > 0);
490
sign.convertTo(sign, CV_32F, 1.0f/255.0f);
491
sign = sign * 2.0f - 1.0f;
492
pow(abs(src), power, dst);
493
dst = dst.mul(sign);
494
}
495
496
void mapContrast(Mat& contrast)
497
{
498
const float response_power = 0.4185f;
499
signedPow(contrast, response_power, contrast);
500
contrast *= scale;
501
signedPow(contrast, 1.0f / response_power, contrast);
502
}
503
504
void getGradient(Mat src, Mat& dst, int pos)
505
{
506
dst = Mat::zeros(src.size(), CV_32F);
507
Mat a, b;
508
Mat grad = src.colRange(1, src.cols) - src.colRange(0, src.cols - 1);
509
grad.copyTo(dst.colRange(pos, src.cols + pos - 1));
510
if(pos == 1) {
511
src.col(0).copyTo(dst.col(0));
512
}
513
}
514
515
void getContrast(Mat src, std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast)
516
{
517
int levels = static_cast<int>(logf(static_cast<float>(min(src.rows, src.cols))) / logf(2.0f));
518
x_contrast.resize(levels);
519
y_contrast.resize(levels);
520
521
Mat layer;
522
src.copyTo(layer);
523
for(int i = 0; i < levels; i++) {
524
getGradient(layer, x_contrast[i], 0);
525
getGradient(layer.t(), y_contrast[i], 0);
526
resize(layer, layer, Size(layer.cols / 2, layer.rows / 2), 0, 0, INTER_LINEAR);
527
}
528
}
529
530
void calculateSum(std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast, Mat& sum)
531
{
532
if (x_contrast.empty())
533
return;
534
const int last = (int)x_contrast.size() - 1;
535
sum = Mat::zeros(x_contrast[last].size(), CV_32F);
536
for(int i = last; i >= 0; i--)
537
{
538
Mat grad_x, grad_y;
539
getGradient(x_contrast[i], grad_x, 1);
540
getGradient(y_contrast[i], grad_y, 1);
541
resize(sum, sum, x_contrast[i].size(), 0, 0, INTER_LINEAR);
542
sum += grad_x + grad_y.t();
543
}
544
}
545
546
void calculateProduct(Mat src, Mat& dst)
547
{
548
std::vector<Mat> x_contrast, y_contrast;
549
getContrast(src, x_contrast, y_contrast);
550
calculateSum(x_contrast, y_contrast, dst);
551
}
552
};
553
554
Ptr<TonemapMantiuk> createTonemapMantiuk(float gamma, float scale, float saturation)
555
{
556
return makePtr<TonemapMantiukImpl>(gamma, scale, saturation);
557
}
558
559
}
560
561