b0a36d4f6f49c6fead1a66a6aa5ed4c78c37e0d1
[platform/upstream/opencv.git] / modules / photo / src / merge.cpp
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 class MergeDebevecImpl CV_FINAL : public MergeDebevec
51 {
52 public:
53     MergeDebevecImpl() :
54         name("MergeDebevec"),
55         weights(triangleWeights())
56     {
57     }
58
59     void process(InputArrayOfArrays src, OutputArray dst, InputArray _times, InputArray input_response) CV_OVERRIDE
60     {
61         CV_INSTRUMENT_REGION();
62
63         std::vector<Mat> images;
64         src.getMatVector(images);
65         Mat times = _times.getMat();
66
67         CV_Assert(images.size() == times.total());
68         checkImageDimensions(images);
69         CV_Assert(images[0].depth() == CV_8U);
70
71         int channels = images[0].channels();
72         Size size = images[0].size();
73         int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
74
75         dst.create(images[0].size(), CV_32FCC);
76         Mat result = dst.getMat();
77
78         Mat response = input_response.getMat();
79
80         if(response.empty()) {
81             response = linearResponse(channels);
82             response.at<Vec3f>(0) = response.at<Vec3f>(1);
83         }
84
85         Mat log_response;
86         log(response, log_response);
87         CV_Assert(log_response.rows == LDR_SIZE && log_response.cols == 1 &&
88                   log_response.channels() == channels);
89
90         Mat exp_values(times.clone());
91         log(exp_values, exp_values);
92
93         result = Mat::zeros(size, CV_32FCC);
94         std::vector<Mat> result_split;
95         split(result, result_split);
96         Mat weight_sum = Mat::zeros(size, CV_32F);
97
98         for(size_t i = 0; i < images.size(); i++) {
99             std::vector<Mat> splitted;
100             split(images[i], splitted);
101
102             Mat w = Mat::zeros(size, CV_32F);
103             for(int c = 0; c < channels; c++) {
104                 LUT(splitted[c], weights, splitted[c]);
105                 w += splitted[c];
106             }
107             w /= channels;
108
109             Mat response_img;
110             LUT(images[i], log_response, response_img);
111             split(response_img, splitted);
112             for(int c = 0; c < channels; c++) {
113                 result_split[c] += w.mul(splitted[c] - exp_values.at<float>((int)i));
114             }
115             weight_sum += w;
116         }
117         weight_sum = 1.0f / weight_sum;
118         for(int c = 0; c < channels; c++) {
119             result_split[c] = result_split[c].mul(weight_sum);
120         }
121         merge(result_split, result);
122         exp(result, result);
123     }
124
125     void process(InputArrayOfArrays src, OutputArray dst, InputArray times) CV_OVERRIDE
126     {
127         CV_INSTRUMENT_REGION();
128
129         process(src, dst, times, Mat());
130     }
131
132 protected:
133     String name;
134     Mat weights;
135 };
136
137 Ptr<MergeDebevec> createMergeDebevec()
138 {
139     return makePtr<MergeDebevecImpl>();
140 }
141
142 class MergeMertensImpl CV_FINAL : public MergeMertens
143 {
144 public:
145     MergeMertensImpl(float _wcon, float _wsat, float _wexp) :
146         name("MergeMertens"),
147         wcon(_wcon),
148         wsat(_wsat),
149         wexp(_wexp)
150     {
151     }
152
153     void process(InputArrayOfArrays src, OutputArrayOfArrays dst, InputArray, InputArray) CV_OVERRIDE
154     {
155         CV_INSTRUMENT_REGION();
156
157         process(src, dst);
158     }
159
160     void process(InputArrayOfArrays src, OutputArray dst) CV_OVERRIDE
161     {
162         CV_INSTRUMENT_REGION();
163
164         std::vector<Mat> images;
165         src.getMatVector(images);
166         checkImageDimensions(images);
167
168         int channels = images[0].channels();
169         CV_Assert(channels == 1 || channels == 3);
170         Size size = images[0].size();
171         int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
172
173         std::vector<Mat> weights(images.size());
174         Mat weight_sum = Mat::zeros(size, CV_32F);
175         Mutex weight_sum_mutex;
176
177         parallel_for_(Range(0, static_cast<int>(images.size())), [&](const Range& range) {
178             for(int i = range.start; i < range.end; i++) {
179                 Mat& img = images[i];
180                 Mat gray, contrast, saturation, wellexp;
181                 std::vector<Mat> splitted(channels);
182
183                 img.convertTo(img, CV_32F, 1.0f/255.0f);
184                 if(channels == 3) {
185                     cvtColor(img, gray, COLOR_RGB2GRAY);
186                 } else {
187                     img.copyTo(gray);
188                 }
189                 split(img, splitted);
190
191                 Laplacian(gray, contrast, CV_32F);
192                 contrast = abs(contrast);
193
194                 Mat mean = Mat::zeros(size, CV_32F);
195                 for(int c = 0; c < channels; c++) {
196                     mean += splitted[c];
197                 }
198                 mean /= channels;
199
200                 saturation = Mat::zeros(size, CV_32F);
201                 for(int c = 0; c < channels;  c++) {
202                     Mat deviation = splitted[c] - mean;
203                     pow(deviation, 2.0f, deviation);
204                     saturation += deviation;
205                 }
206                 sqrt(saturation, saturation);
207
208                 wellexp = Mat::ones(size, CV_32F);
209                 for(int c = 0; c < channels; c++) {
210                     Mat expo = splitted[c] - 0.5f;
211                     pow(expo, 2.0f, expo);
212                     expo = -expo / 0.08f;
213                     exp(expo, expo);
214                     wellexp = wellexp.mul(expo);
215                 }
216
217                 pow(contrast, wcon, contrast);
218                 pow(saturation, wsat, saturation);
219                 pow(wellexp, wexp, wellexp);
220
221                 weights[i] = contrast;
222                 if(channels == 3) {
223                     weights[i] = weights[i].mul(saturation);
224                 }
225                 weights[i] = weights[i].mul(wellexp) + 1e-12f;
226
227                 AutoLock lock(weight_sum_mutex);
228                 weight_sum += weights[i];
229             }
230         });
231
232         int maxlevel = static_cast<int>(logf(static_cast<float>(min(size.width, size.height))) / logf(2.0f));
233         std::vector<Mat> res_pyr(maxlevel + 1);
234         std::vector<Mutex> res_pyr_mutexes(maxlevel + 1);
235
236         parallel_for_(Range(0, static_cast<int>(images.size())), [&](const Range& range) {
237             for(int i = range.start; i < range.end; i++) {
238                 weights[i] /= weight_sum;
239
240                 std::vector<Mat> img_pyr, weight_pyr;
241                 buildPyramid(images[i], img_pyr, maxlevel);
242                 buildPyramid(weights[i], weight_pyr, maxlevel);
243
244                 for(int lvl = 0; lvl < maxlevel; lvl++) {
245                     Mat up;
246                     pyrUp(img_pyr[lvl + 1], up, img_pyr[lvl].size());
247                     img_pyr[lvl] -= up;
248                 }
249                 for(int lvl = 0; lvl <= maxlevel; lvl++) {
250                     std::vector<Mat> splitted(channels);
251                     split(img_pyr[lvl], splitted);
252                     for(int c = 0; c < channels; c++) {
253                         splitted[c] = splitted[c].mul(weight_pyr[lvl]);
254                     }
255                     merge(splitted, img_pyr[lvl]);
256
257                     AutoLock lock(res_pyr_mutexes[lvl]);
258                     if(res_pyr[lvl].empty()) {
259                         res_pyr[lvl] = img_pyr[lvl];
260                     } else {
261                         res_pyr[lvl] += img_pyr[lvl];
262                     }
263                 }
264             }
265         });
266         for(int lvl = maxlevel; lvl > 0; lvl--) {
267             Mat up;
268             pyrUp(res_pyr[lvl], up, res_pyr[lvl - 1].size());
269             res_pyr[lvl - 1] += up;
270         }
271         dst.create(size, CV_32FCC);
272         res_pyr[0].copyTo(dst);
273     }
274
275     float getContrastWeight() const CV_OVERRIDE { return wcon; }
276     void setContrastWeight(float val) CV_OVERRIDE { wcon = val; }
277
278     float getSaturationWeight() const CV_OVERRIDE { return wsat; }
279     void setSaturationWeight(float val) CV_OVERRIDE { wsat = val; }
280
281     float getExposureWeight() const CV_OVERRIDE { return wexp; }
282     void setExposureWeight(float val) CV_OVERRIDE { wexp = val; }
283
284     void write(FileStorage& fs) const CV_OVERRIDE
285     {
286         writeFormat(fs);
287         fs << "name" << name
288            << "contrast_weight" << wcon
289            << "saturation_weight" << wsat
290            << "exposure_weight" << wexp;
291     }
292
293     void read(const FileNode& fn) CV_OVERRIDE
294     {
295         FileNode n = fn["name"];
296         CV_Assert(n.isString() && String(n) == name);
297         wcon = fn["contrast_weight"];
298         wsat = fn["saturation_weight"];
299         wexp = fn["exposure_weight"];
300     }
301
302 protected:
303     String name;
304     float wcon, wsat, wexp;
305 };
306
307 Ptr<MergeMertens> createMergeMertens(float wcon, float wsat, float wexp)
308 {
309     return makePtr<MergeMertensImpl>(wcon, wsat, wexp);
310 }
311
312 class MergeRobertsonImpl CV_FINAL : public MergeRobertson
313 {
314 public:
315     MergeRobertsonImpl() :
316         name("MergeRobertson"),
317         weight(RobertsonWeights())
318     {
319     }
320
321     void process(InputArrayOfArrays src, OutputArray dst, InputArray _times, InputArray input_response) CV_OVERRIDE
322     {
323         CV_INSTRUMENT_REGION();
324
325         std::vector<Mat> images;
326         src.getMatVector(images);
327         Mat times = _times.getMat();
328
329         CV_Assert(images.size() == times.total());
330         checkImageDimensions(images);
331         CV_Assert(images[0].depth() == CV_8U);
332
333         int channels = images[0].channels();
334         int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
335
336         dst.create(images[0].size(), CV_32FCC);
337         Mat result = dst.getMat();
338
339         Mat response = input_response.getMat();
340         if(response.empty()) {
341             float middle = LDR_SIZE / 2.0f;
342             response = linearResponse(channels) / middle;
343         }
344         CV_Assert(response.rows == LDR_SIZE && response.cols == 1 &&
345                   response.channels() == channels);
346
347         result = Mat::zeros(images[0].size(), CV_32FCC);
348         Mat wsum = Mat::zeros(images[0].size(), CV_32FCC);
349         for(size_t i = 0; i < images.size(); i++) {
350             Mat im, w;
351             LUT(images[i], weight, w);
352             LUT(images[i], response, im);
353
354             result += times.at<float>((int)i) * w.mul(im);
355             wsum += times.at<float>((int)i) * times.at<float>((int)i) * w;
356         }
357         result = result.mul(1 / (wsum + Scalar::all(DBL_EPSILON)));
358     }
359
360     void process(InputArrayOfArrays src, OutputArray dst, InputArray times) CV_OVERRIDE
361     {
362         CV_INSTRUMENT_REGION();
363
364         process(src, dst, times, Mat());
365     }
366
367 protected:
368     String name;
369     Mat weight;
370 };
371
372 Ptr<MergeRobertson> createMergeRobertson()
373 {
374     return makePtr<MergeRobertsonImpl>();
375 }
376
377 }