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// Intel License Agreement
11// For Open Source Computer Vision Library
12//
13// Copyright (C) 2000, Intel Corporation, all rights reserved.
14// Third party copyrights are property of their respective icvers.
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 Intel Corporation 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
44#include "fast_nlmeans_denoising_invoker.hpp"
45#include "fast_nlmeans_multi_denoising_invoker.hpp"
46#include "fast_nlmeans_denoising_opencl.hpp"
47
48template<typename ST, typename IT, typename UIT, typename D>
49static void fastNlMeansDenoising_( const Mat& src, Mat& dst, const std::vector<float>& h,
50 int templateWindowSize, int searchWindowSize)
51{
52 int hn = (int)h.size();
53 double granularity = (double)std::max(a: 1., b: (double)dst.total()/(1 << 17));
54
55 switch (CV_MAT_CN(src.type())) {
56 case 1:
57 parallel_for_(cv::Range(0, src.rows),
58 FastNlMeansDenoisingInvoker<ST, IT, UIT, D, int>(
59 src, dst, templateWindowSize, searchWindowSize, &h[0]),
60 granularity);
61 break;
62 case 2:
63 if (hn == 1)
64 parallel_for_(cv::Range(0, src.rows),
65 FastNlMeansDenoisingInvoker<Vec<ST, 2>, IT, UIT, D, int>(
66 src, dst, templateWindowSize, searchWindowSize, &h[0]),
67 granularity);
68 else
69 parallel_for_(cv::Range(0, src.rows),
70 FastNlMeansDenoisingInvoker<Vec<ST, 2>, IT, UIT, D, Vec2i>(
71 src, dst, templateWindowSize, searchWindowSize, &h[0]),
72 granularity);
73 break;
74 case 3:
75 if (hn == 1)
76 parallel_for_(cv::Range(0, src.rows),
77 FastNlMeansDenoisingInvoker<Vec<ST, 3>, IT, UIT, D, int>(
78 src, dst, templateWindowSize, searchWindowSize, &h[0]),
79 granularity);
80 else
81 parallel_for_(cv::Range(0, src.rows),
82 FastNlMeansDenoisingInvoker<Vec<ST, 3>, IT, UIT, D, Vec3i>(
83 src, dst, templateWindowSize, searchWindowSize, &h[0]),
84 granularity);
85 break;
86 case 4:
87 if (hn == 1)
88 parallel_for_(cv::Range(0, src.rows),
89 FastNlMeansDenoisingInvoker<Vec<ST, 4>, IT, UIT, D, int>(
90 src, dst, templateWindowSize, searchWindowSize, &h[0]),
91 granularity);
92 else
93 parallel_for_(cv::Range(0, src.rows),
94 FastNlMeansDenoisingInvoker<Vec<ST, 4>, IT, UIT, D, Vec4i>(
95 src, dst, templateWindowSize, searchWindowSize, &h[0]),
96 granularity);
97 break;
98 default:
99 CV_Error(Error::StsBadArg,
100 "Unsupported number of channels! Only 1, 2, 3, and 4 are supported");
101 }
102}
103
104void cv::fastNlMeansDenoising( InputArray _src, OutputArray _dst, float h,
105 int templateWindowSize, int searchWindowSize)
106{
107 CV_INSTRUMENT_REGION();
108
109 fastNlMeansDenoising(src: _src, dst: _dst, h: std::vector<float>(1, h),
110 templateWindowSize, searchWindowSize);
111}
112
113void cv::fastNlMeansDenoising( InputArray _src, OutputArray _dst, const std::vector<float>& h,
114 int templateWindowSize, int searchWindowSize, int normType)
115{
116 CV_INSTRUMENT_REGION();
117
118 int hn = (int)h.size(), type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
119 CV_Assert(!_src.empty());
120 CV_Assert(hn == 1 || hn == cn);
121
122 Size src_size = _src.size();
123 CV_OCL_RUN(_src.dims() <= 2 && (_src.isUMat() || _dst.isUMat()) &&
124 src_size.width > 5 && src_size.height > 5, // low accuracy on small sizes
125 ocl_fastNlMeansDenoising(_src, _dst, h: &h[0], hn,
126 templateWindowSize, searchWindowSize, normType))
127
128 Mat src = _src.getMat();
129 _dst.create(sz: src_size, type: src.type());
130 Mat dst = _dst.getMat();
131
132 switch (normType) {
133 case NORM_L2:
134 switch (depth) {
135 case CV_8U:
136 fastNlMeansDenoising_<uchar, int, unsigned, DistSquared>(src, dst, h,
137 templateWindowSize,
138 searchWindowSize);
139 break;
140 default:
141 CV_Error(Error::StsBadArg,
142 "Unsupported depth! Only CV_8U is supported for NORM_L2");
143 }
144 break;
145 case NORM_L1:
146 switch (depth) {
147 case CV_8U:
148 fastNlMeansDenoising_<uchar, int, unsigned, DistAbs>(src, dst, h,
149 templateWindowSize,
150 searchWindowSize);
151 break;
152 case CV_16U:
153 fastNlMeansDenoising_<ushort, int64, uint64, DistAbs>(src, dst, h,
154 templateWindowSize,
155 searchWindowSize);
156 break;
157 default:
158 CV_Error(Error::StsBadArg,
159 "Unsupported depth! Only CV_8U and CV_16U are supported for NORM_L1");
160 }
161 break;
162 default:
163 CV_Error(Error::StsBadArg,
164 "Unsupported norm type! Only NORM_L2 and NORM_L1 are supported");
165 }
166}
167
168void cv::fastNlMeansDenoisingColored( InputArray _src, OutputArray _dst,
169 float h, float hForColorComponents,
170 int templateWindowSize, int searchWindowSize)
171{
172 CV_INSTRUMENT_REGION();
173
174 int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
175 Size src_size = _src.size();
176 if (type != CV_8UC3 && type != CV_8UC4)
177 {
178 CV_Error(Error::StsBadArg, "Type of input image should be CV_8UC3 or CV_8UC4!");
179 return;
180 }
181
182 CV_OCL_RUN(_src.dims() <= 2 && (_dst.isUMat() || _src.isUMat()) &&
183 src_size.width > 5 && src_size.height > 5, // low accuracy on small sizes
184 ocl_fastNlMeansDenoisingColored(_src, _dst, h, hForColorComponents,
185 templateWindowSize, searchWindowSize))
186
187 Mat src = _src.getMat();
188 _dst.create(sz: src_size, type);
189 Mat dst = _dst.getMat();
190
191 Mat src_lab;
192 cvtColor(src, dst: src_lab, code: COLOR_LBGR2Lab);
193
194 Mat l(src_size, CV_MAKE_TYPE(depth, 1));
195 Mat ab(src_size, CV_MAKE_TYPE(depth, 2));
196 Mat l_ab[] = { l, ab };
197 int from_to[] = { 0,0, 1,1, 2,2 };
198 mixChannels(src: &src_lab, nsrcs: 1, dst: l_ab, ndsts: 2, fromTo: from_to, npairs: 3);
199
200 fastNlMeansDenoising(src: l, dst: l, h, templateWindowSize, searchWindowSize);
201 fastNlMeansDenoising(src: ab, dst: ab, h: hForColorComponents, templateWindowSize, searchWindowSize);
202
203 Mat l_ab_denoised[] = { l, ab };
204 Mat dst_lab(src_size, CV_MAKE_TYPE(depth, 3));
205 mixChannels(src: l_ab_denoised, nsrcs: 2, dst: &dst_lab, ndsts: 1, fromTo: from_to, npairs: 3);
206
207 cvtColor(src: dst_lab, dst, code: COLOR_Lab2LBGR, dstCn: cn);
208}
209
210static void fastNlMeansDenoisingMultiCheckPreconditions(
211 const std::vector<Mat>& srcImgs,
212 int imgToDenoiseIndex, int temporalWindowSize,
213 int templateWindowSize, int searchWindowSize)
214{
215 int src_imgs_size = static_cast<int>(srcImgs.size());
216 if (src_imgs_size == 0)
217 {
218 CV_Error(Error::StsBadArg, "Input images vector should not be empty!");
219 }
220
221 if (temporalWindowSize % 2 == 0 ||
222 searchWindowSize % 2 == 0 ||
223 templateWindowSize % 2 == 0) {
224 CV_Error(Error::StsBadArg, "All windows sizes should be odd!");
225 }
226
227 int temporalWindowHalfSize = temporalWindowSize / 2;
228 if (imgToDenoiseIndex - temporalWindowHalfSize < 0 ||
229 imgToDenoiseIndex + temporalWindowHalfSize >= src_imgs_size)
230 {
231 CV_Error(Error::StsBadArg,
232 "imgToDenoiseIndex and temporalWindowSize "
233 "should be chosen corresponding srcImgs size!");
234 }
235
236 for (int i = 1; i < src_imgs_size; i++)
237 if (srcImgs[0].size() != srcImgs[i].size() || srcImgs[0].type() != srcImgs[i].type())
238 {
239 CV_Error(Error::StsBadArg, "Input images should have the same size and type!");
240 }
241}
242
243template<typename ST, typename IT, typename UIT, typename D>
244static void fastNlMeansDenoisingMulti_( const std::vector<Mat>& srcImgs, Mat& dst,
245 int imgToDenoiseIndex, int temporalWindowSize,
246 const std::vector<float>& h,
247 int templateWindowSize, int searchWindowSize)
248{
249 int hn = (int)h.size();
250 double granularity = (double)std::max(a: 1., b: (double)dst.total()/(1 << 16));
251
252 switch (CV_MAT_CN(srcImgs[0].type())) {
253 case 1:
254 parallel_for_(cv::Range(0, srcImgs[0].rows),
255 FastNlMeansMultiDenoisingInvoker<ST, IT, UIT, D, int>(
256 srcImgs, imgToDenoiseIndex, temporalWindowSize,
257 dst, templateWindowSize, searchWindowSize, &h[0]),
258 granularity);
259 break;
260 case 2:
261 if (hn == 1)
262 parallel_for_(cv::Range(0, srcImgs[0].rows),
263 FastNlMeansMultiDenoisingInvoker<Vec<ST, 2>, IT, UIT, D, int>(
264 srcImgs, imgToDenoiseIndex, temporalWindowSize,
265 dst, templateWindowSize, searchWindowSize, &h[0]),
266 granularity);
267 else
268 parallel_for_(cv::Range(0, srcImgs[0].rows),
269 FastNlMeansMultiDenoisingInvoker<Vec<ST, 2>, IT, UIT, D, Vec2i>(
270 srcImgs, imgToDenoiseIndex, temporalWindowSize,
271 dst, templateWindowSize, searchWindowSize, &h[0]),
272 granularity);
273 break;
274 case 3:
275 if (hn == 1)
276 parallel_for_(cv::Range(0, srcImgs[0].rows),
277 FastNlMeansMultiDenoisingInvoker<Vec<ST, 3>, IT, UIT, D, int>(
278 srcImgs, imgToDenoiseIndex, temporalWindowSize,
279 dst, templateWindowSize, searchWindowSize, &h[0]),
280 granularity);
281 else
282 parallel_for_(cv::Range(0, srcImgs[0].rows),
283 FastNlMeansMultiDenoisingInvoker<Vec<ST, 3>, IT, UIT, D, Vec3i>(
284 srcImgs, imgToDenoiseIndex, temporalWindowSize,
285 dst, templateWindowSize, searchWindowSize, &h[0]),
286 granularity);
287 break;
288 case 4:
289 if (hn == 1)
290 parallel_for_(cv::Range(0, srcImgs[0].rows),
291 FastNlMeansMultiDenoisingInvoker<Vec<ST, 4>, IT, UIT, D, int>(
292 srcImgs, imgToDenoiseIndex, temporalWindowSize,
293 dst, templateWindowSize, searchWindowSize, &h[0]),
294 granularity);
295 else
296 parallel_for_(cv::Range(0, srcImgs[0].rows),
297 FastNlMeansMultiDenoisingInvoker<Vec<ST, 4>, IT, UIT, D, Vec4i>(
298 srcImgs, imgToDenoiseIndex, temporalWindowSize,
299 dst, templateWindowSize, searchWindowSize, &h[0]),
300 granularity);
301 break;
302 default:
303 CV_Error(Error::StsBadArg,
304 "Unsupported number of channels! Only 1, 2, 3, and 4 are supported");
305 }
306}
307
308void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs, OutputArray _dst,
309 int imgToDenoiseIndex, int temporalWindowSize,
310 float h, int templateWindowSize, int searchWindowSize)
311{
312 CV_INSTRUMENT_REGION();
313
314 fastNlMeansDenoisingMulti(srcImgs: _srcImgs, dst: _dst, imgToDenoiseIndex, temporalWindowSize,
315 h: std::vector<float>(1, h), templateWindowSize, searchWindowSize);
316}
317
318void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs, OutputArray _dst,
319 int imgToDenoiseIndex, int temporalWindowSize,
320 const std::vector<float>& h,
321 int templateWindowSize, int searchWindowSize, int normType)
322{
323 CV_INSTRUMENT_REGION();
324
325 std::vector<Mat> srcImgs;
326 _srcImgs.getMatVector(mv&: srcImgs);
327
328 fastNlMeansDenoisingMultiCheckPreconditions(
329 srcImgs, imgToDenoiseIndex,
330 temporalWindowSize, templateWindowSize, searchWindowSize);
331
332 int hn = (int)h.size();
333 int type = srcImgs[0].type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
334 CV_Assert(hn == 1 || hn == cn);
335
336 _dst.create(sz: srcImgs[0].size(), type: srcImgs[0].type());
337 Mat dst = _dst.getMat();
338
339 switch (normType) {
340 case NORM_L2:
341 switch (depth) {
342 case CV_8U:
343 fastNlMeansDenoisingMulti_<uchar, int, unsigned,
344 DistSquared>(srcImgs, dst,
345 imgToDenoiseIndex, temporalWindowSize,
346 h,
347 templateWindowSize, searchWindowSize);
348 break;
349 default:
350 CV_Error(Error::StsBadArg,
351 "Unsupported depth! Only CV_8U is supported for NORM_L2");
352 }
353 break;
354 case NORM_L1:
355 switch (depth) {
356 case CV_8U:
357 fastNlMeansDenoisingMulti_<uchar, int, unsigned,
358 DistAbs>(srcImgs, dst,
359 imgToDenoiseIndex, temporalWindowSize,
360 h,
361 templateWindowSize, searchWindowSize);
362 break;
363 case CV_16U:
364 fastNlMeansDenoisingMulti_<ushort, int64, uint64,
365 DistAbs>(srcImgs, dst,
366 imgToDenoiseIndex, temporalWindowSize,
367 h,
368 templateWindowSize, searchWindowSize);
369 break;
370 default:
371 CV_Error(Error::StsBadArg,
372 "Unsupported depth! Only CV_8U and CV_16U are supported for NORM_L1");
373 }
374 break;
375 default:
376 CV_Error(Error::StsBadArg,
377 "Unsupported norm type! Only NORM_L2 and NORM_L1 are supported");
378 }
379}
380
381void cv::fastNlMeansDenoisingColoredMulti( InputArrayOfArrays _srcImgs, OutputArray _dst,
382 int imgToDenoiseIndex, int temporalWindowSize,
383 float h, float hForColorComponents,
384 int templateWindowSize, int searchWindowSize)
385{
386 CV_INSTRUMENT_REGION();
387
388 std::vector<Mat> srcImgs;
389 _srcImgs.getMatVector(mv&: srcImgs);
390
391 fastNlMeansDenoisingMultiCheckPreconditions(
392 srcImgs, imgToDenoiseIndex,
393 temporalWindowSize, templateWindowSize, searchWindowSize);
394
395 _dst.create(sz: srcImgs[0].size(), type: srcImgs[0].type());
396 Mat dst = _dst.getMat();
397
398 int type = srcImgs[0].type(), depth = CV_MAT_DEPTH(type);
399 int src_imgs_size = static_cast<int>(srcImgs.size());
400
401 if (type != CV_8UC3)
402 {
403 CV_Error(Error::StsBadArg, "Type of input images should be CV_8UC3!");
404 return;
405 }
406
407 int from_to[] = { 0,0, 1,1, 2,2 };
408
409 // TODO convert only required images
410 std::vector<Mat> src_lab(src_imgs_size);
411 std::vector<Mat> l(src_imgs_size);
412 std::vector<Mat> ab(src_imgs_size);
413 for (int i = 0; i < src_imgs_size; i++)
414 {
415 src_lab[i] = Mat::zeros(size: srcImgs[0].size(), type);
416 l[i] = Mat::zeros(size: srcImgs[0].size(), CV_MAKE_TYPE(depth, 1));
417 ab[i] = Mat::zeros(size: srcImgs[0].size(), CV_MAKE_TYPE(depth, 2));
418 cvtColor(src: srcImgs[i], dst: src_lab[i], code: COLOR_LBGR2Lab);
419
420 Mat l_ab[] = { l[i], ab[i] };
421 mixChannels(src: &src_lab[i], nsrcs: 1, dst: l_ab, ndsts: 2, fromTo: from_to, npairs: 3);
422 }
423
424 Mat dst_l;
425 Mat dst_ab;
426
427 fastNlMeansDenoisingMulti(
428 srcImgs: l, dst: dst_l, imgToDenoiseIndex, temporalWindowSize,
429 h, templateWindowSize, searchWindowSize);
430
431 fastNlMeansDenoisingMulti(
432 srcImgs: ab, dst: dst_ab, imgToDenoiseIndex, temporalWindowSize,
433 h: hForColorComponents, templateWindowSize, searchWindowSize);
434
435 Mat l_ab_denoised[] = { dst_l, dst_ab };
436 Mat dst_lab(srcImgs[0].size(), srcImgs[0].type());
437 mixChannels(src: l_ab_denoised, nsrcs: 2, dst: &dst_lab, ndsts: 1, fromTo: from_to, npairs: 3);
438
439 cvtColor(src: dst_lab, dst, code: COLOR_Lab2LBGR);
440}
441

source code of opencv/modules/photo/src/denoising.cpp