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) 2000-2008, Intel Corporation, all rights reserved.
14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15 // Third party copyrights are property of their respective owners.
16 //
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
19 //
20 //   * Redistribution's of source code must retain the above copyright notice,
21 //     this list of conditions and the following disclaimer.
22 //
23 //   * Redistribution's in binary form must reproduce the above copyright notice,
24 //     this list of conditions and the following disclaimer in the documentation
25 //     and/or other materials provided with the distribution.
26 //
27 //   * The name of the copyright holders may not be used to endorse or promote products
28 //     derived from this software without specific prior written permission.
29 //
30 // This software is provided by the copyright holders and contributors "as is" and
31 // any express or implied warranties, including, but not limited to, the implied
32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
33 // In no event shall the Intel Corporation or contributors be liable for any direct,
34 // indirect, incidental, special, exemplary, or consequential damages
35 // (including, but not limited to, procurement of substitute goods or services;
36 // loss of use, data, or profits; or business interruption) however caused
37 // and on any theory of liability, whether in contract, strict liability,
38 // or tort (including negligence or otherwise) arising in any way out of
39 // the use of this software, even if advised of the possibility of such damage.
40 //
41 //M*/
42 
43 #include "precomp.hpp"
44 #include "opencl_kernels_stitching.hpp"
45 
46 namespace cv {
47 namespace detail {
48 
49 static const float WEIGHT_EPS = 1e-5f;
50 
createDefault(int type,bool try_gpu)51 Ptr<Blender> Blender::createDefault(int type, bool try_gpu)
52 {
53     if (type == NO)
54         return makePtr<Blender>();
55     if (type == FEATHER)
56         return makePtr<FeatherBlender>();
57     if (type == MULTI_BAND)
58         return makePtr<MultiBandBlender>(try_gpu);
59     CV_Error(Error::StsBadArg, "unsupported blending method");
60     return Ptr<Blender>();
61 }
62 
63 
prepare(const std::vector<Point> & corners,const std::vector<Size> & sizes)64 void Blender::prepare(const std::vector<Point> &corners, const std::vector<Size> &sizes)
65 {
66     prepare(resultRoi(corners, sizes));
67 }
68 
69 
prepare(Rect dst_roi)70 void Blender::prepare(Rect dst_roi)
71 {
72     dst_.create(dst_roi.size(), CV_16SC3);
73     dst_.setTo(Scalar::all(0));
74     dst_mask_.create(dst_roi.size(), CV_8U);
75     dst_mask_.setTo(Scalar::all(0));
76     dst_roi_ = dst_roi;
77 }
78 
79 
feed(InputArray _img,InputArray _mask,Point tl)80 void Blender::feed(InputArray _img, InputArray _mask, Point tl)
81 {
82     Mat img = _img.getMat();
83     Mat mask = _mask.getMat();
84     Mat dst = dst_.getMat(ACCESS_RW);
85     Mat dst_mask = dst_mask_.getMat(ACCESS_RW);
86 
87     CV_Assert(img.type() == CV_16SC3);
88     CV_Assert(mask.type() == CV_8U);
89     int dx = tl.x - dst_roi_.x;
90     int dy = tl.y - dst_roi_.y;
91 
92     for (int y = 0; y < img.rows; ++y)
93     {
94         const Point3_<short> *src_row = img.ptr<Point3_<short> >(y);
95         Point3_<short> *dst_row = dst.ptr<Point3_<short> >(dy + y);
96         const uchar *mask_row = mask.ptr<uchar>(y);
97         uchar *dst_mask_row = dst_mask.ptr<uchar>(dy + y);
98 
99         for (int x = 0; x < img.cols; ++x)
100         {
101             if (mask_row[x])
102                 dst_row[dx + x] = src_row[x];
103             dst_mask_row[dx + x] |= mask_row[x];
104         }
105     }
106 }
107 
108 
blend(InputOutputArray dst,InputOutputArray dst_mask)109 void Blender::blend(InputOutputArray dst, InputOutputArray dst_mask)
110 {
111     UMat mask;
112     compare(dst_mask_, 0, mask, CMP_EQ);
113     dst_.setTo(Scalar::all(0), mask);
114     dst.assign(dst_);
115     dst_mask.assign(dst_mask_);
116     dst_.release();
117     dst_mask_.release();
118 }
119 
120 
prepare(Rect dst_roi)121 void FeatherBlender::prepare(Rect dst_roi)
122 {
123     Blender::prepare(dst_roi);
124     dst_weight_map_.create(dst_roi.size(), CV_32F);
125     dst_weight_map_.setTo(0);
126 }
127 
128 
feed(InputArray _img,InputArray mask,Point tl)129 void FeatherBlender::feed(InputArray _img, InputArray mask, Point tl)
130 {
131     Mat img = _img.getMat();
132     Mat dst = dst_.getMat(ACCESS_RW);
133 
134     CV_Assert(img.type() == CV_16SC3);
135     CV_Assert(mask.type() == CV_8U);
136 
137     createWeightMap(mask, sharpness_, weight_map_);
138     Mat weight_map = weight_map_.getMat(ACCESS_READ);
139     Mat dst_weight_map = dst_weight_map_.getMat(ACCESS_RW);
140 
141     int dx = tl.x - dst_roi_.x;
142     int dy = tl.y - dst_roi_.y;
143 
144     for (int y = 0; y < img.rows; ++y)
145     {
146         const Point3_<short>* src_row = img.ptr<Point3_<short> >(y);
147         Point3_<short>* dst_row = dst.ptr<Point3_<short> >(dy + y);
148         const float* weight_row = weight_map.ptr<float>(y);
149         float* dst_weight_row = dst_weight_map.ptr<float>(dy + y);
150 
151         for (int x = 0; x < img.cols; ++x)
152         {
153             dst_row[dx + x].x += static_cast<short>(src_row[x].x * weight_row[x]);
154             dst_row[dx + x].y += static_cast<short>(src_row[x].y * weight_row[x]);
155             dst_row[dx + x].z += static_cast<short>(src_row[x].z * weight_row[x]);
156             dst_weight_row[dx + x] += weight_row[x];
157         }
158     }
159 }
160 
161 
blend(InputOutputArray dst,InputOutputArray dst_mask)162 void FeatherBlender::blend(InputOutputArray dst, InputOutputArray dst_mask)
163 {
164     normalizeUsingWeightMap(dst_weight_map_, dst_);
165     compare(dst_weight_map_, WEIGHT_EPS, dst_mask_, CMP_GT);
166     Blender::blend(dst, dst_mask);
167 }
168 
169 
createWeightMaps(const std::vector<UMat> & masks,const std::vector<Point> & corners,std::vector<UMat> & weight_maps)170 Rect FeatherBlender::createWeightMaps(const std::vector<UMat> &masks, const std::vector<Point> &corners,
171                                       std::vector<UMat> &weight_maps)
172 {
173     weight_maps.resize(masks.size());
174     for (size_t i = 0; i < masks.size(); ++i)
175         createWeightMap(masks[i], sharpness_, weight_maps[i]);
176 
177     Rect dst_roi = resultRoi(corners, masks);
178     Mat weights_sum(dst_roi.size(), CV_32F);
179     weights_sum.setTo(0);
180 
181     for (size_t i = 0; i < weight_maps.size(); ++i)
182     {
183         Rect roi(corners[i].x - dst_roi.x, corners[i].y - dst_roi.y,
184                  weight_maps[i].cols, weight_maps[i].rows);
185         add(weights_sum(roi), weight_maps[i], weights_sum(roi));
186     }
187 
188     for (size_t i = 0; i < weight_maps.size(); ++i)
189     {
190         Rect roi(corners[i].x - dst_roi.x, corners[i].y - dst_roi.y,
191                  weight_maps[i].cols, weight_maps[i].rows);
192         Mat tmp = weights_sum(roi);
193         tmp.setTo(1, tmp < std::numeric_limits<float>::epsilon());
194         divide(weight_maps[i], tmp, weight_maps[i]);
195     }
196 
197     return dst_roi;
198 }
199 
200 
MultiBandBlender(int try_gpu,int num_bands,int weight_type)201 MultiBandBlender::MultiBandBlender(int try_gpu, int num_bands, int weight_type)
202 {
203     setNumBands(num_bands);
204 
205 #if defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING)
206     can_use_gpu_ = try_gpu && cuda::getCudaEnabledDeviceCount();
207 #else
208     (void) try_gpu;
209     can_use_gpu_ = false;
210 #endif
211 
212     CV_Assert(weight_type == CV_32F || weight_type == CV_16S);
213     weight_type_ = weight_type;
214 }
215 
216 
prepare(Rect dst_roi)217 void MultiBandBlender::prepare(Rect dst_roi)
218 {
219     dst_roi_final_ = dst_roi;
220 
221     // Crop unnecessary bands
222     double max_len = static_cast<double>(std::max(dst_roi.width, dst_roi.height));
223     num_bands_ = std::min(actual_num_bands_, static_cast<int>(ceil(std::log(max_len) / std::log(2.0))));
224 
225     // Add border to the final image, to ensure sizes are divided by (1 << num_bands_)
226     dst_roi.width += ((1 << num_bands_) - dst_roi.width % (1 << num_bands_)) % (1 << num_bands_);
227     dst_roi.height += ((1 << num_bands_) - dst_roi.height % (1 << num_bands_)) % (1 << num_bands_);
228 
229     Blender::prepare(dst_roi);
230 
231     dst_pyr_laplace_.resize(num_bands_ + 1);
232     dst_pyr_laplace_[0] = dst_;
233 
234     dst_band_weights_.resize(num_bands_ + 1);
235     dst_band_weights_[0].create(dst_roi.size(), weight_type_);
236     dst_band_weights_[0].setTo(0);
237 
238     for (int i = 1; i <= num_bands_; ++i)
239     {
240         dst_pyr_laplace_[i].create((dst_pyr_laplace_[i - 1].rows + 1) / 2,
241                                    (dst_pyr_laplace_[i - 1].cols + 1) / 2, CV_16SC3);
242         dst_band_weights_[i].create((dst_band_weights_[i - 1].rows + 1) / 2,
243                                     (dst_band_weights_[i - 1].cols + 1) / 2, weight_type_);
244         dst_pyr_laplace_[i].setTo(Scalar::all(0));
245         dst_band_weights_[i].setTo(0);
246     }
247 }
248 
249 #ifdef HAVE_OPENCL
ocl_MultiBandBlender_feed(InputArray _src,InputArray _weight,InputOutputArray _dst,InputOutputArray _dst_weight)250 static bool ocl_MultiBandBlender_feed(InputArray _src, InputArray _weight,
251         InputOutputArray _dst, InputOutputArray _dst_weight)
252 {
253     String buildOptions = "-D DEFINE_feed";
254     ocl::buildOptionsAddMatrixDescription(buildOptions, "src", _src);
255     ocl::buildOptionsAddMatrixDescription(buildOptions, "weight", _weight);
256     ocl::buildOptionsAddMatrixDescription(buildOptions, "dst", _dst);
257     ocl::buildOptionsAddMatrixDescription(buildOptions, "dstWeight", _dst_weight);
258     ocl::Kernel k("feed", ocl::stitching::multibandblend_oclsrc, buildOptions);
259     if (k.empty())
260         return false;
261 
262     UMat src = _src.getUMat();
263 
264     k.args(ocl::KernelArg::ReadOnly(src),
265            ocl::KernelArg::ReadOnly(_weight.getUMat()),
266            ocl::KernelArg::ReadWrite(_dst.getUMat()),
267            ocl::KernelArg::ReadWrite(_dst_weight.getUMat())
268            );
269 
270     size_t globalsize[2] = {src.cols, src.rows };
271     return k.run(2, globalsize, NULL, false);
272 }
273 #endif
274 
feed(InputArray _img,InputArray mask,Point tl)275 void MultiBandBlender::feed(InputArray _img, InputArray mask, Point tl)
276 {
277 #if ENABLE_LOG
278     int64 t = getTickCount();
279 #endif
280 
281     UMat img = _img.getUMat();
282     CV_Assert(img.type() == CV_16SC3 || img.type() == CV_8UC3);
283     CV_Assert(mask.type() == CV_8U);
284 
285     // Keep source image in memory with small border
286     int gap = 3 * (1 << num_bands_);
287     Point tl_new(std::max(dst_roi_.x, tl.x - gap),
288                  std::max(dst_roi_.y, tl.y - gap));
289     Point br_new(std::min(dst_roi_.br().x, tl.x + img.cols + gap),
290                  std::min(dst_roi_.br().y, tl.y + img.rows + gap));
291 
292     // Ensure coordinates of top-left, bottom-right corners are divided by (1 << num_bands_).
293     // After that scale between layers is exactly 2.
294     //
295     // We do it to avoid interpolation problems when keeping sub-images only. There is no such problem when
296     // image is bordered to have size equal to the final image size, but this is too memory hungry approach.
297     tl_new.x = dst_roi_.x + (((tl_new.x - dst_roi_.x) >> num_bands_) << num_bands_);
298     tl_new.y = dst_roi_.y + (((tl_new.y - dst_roi_.y) >> num_bands_) << num_bands_);
299     int width = br_new.x - tl_new.x;
300     int height = br_new.y - tl_new.y;
301     width += ((1 << num_bands_) - width % (1 << num_bands_)) % (1 << num_bands_);
302     height += ((1 << num_bands_) - height % (1 << num_bands_)) % (1 << num_bands_);
303     br_new.x = tl_new.x + width;
304     br_new.y = tl_new.y + height;
305     int dy = std::max(br_new.y - dst_roi_.br().y, 0);
306     int dx = std::max(br_new.x - dst_roi_.br().x, 0);
307     tl_new.x -= dx; br_new.x -= dx;
308     tl_new.y -= dy; br_new.y -= dy;
309 
310     int top = tl.y - tl_new.y;
311     int left = tl.x - tl_new.x;
312     int bottom = br_new.y - tl.y - img.rows;
313     int right = br_new.x - tl.x - img.cols;
314 
315     // Create the source image Laplacian pyramid
316     UMat img_with_border;
317     copyMakeBorder(_img, img_with_border, top, bottom, left, right,
318                    BORDER_REFLECT);
319     LOGLN("  Add border to the source image, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
320 #if ENABLE_LOG
321     t = getTickCount();
322 #endif
323 
324     std::vector<UMat> src_pyr_laplace;
325     if (can_use_gpu_ && img_with_border.depth() == CV_16S)
326         createLaplacePyrGpu(img_with_border, num_bands_, src_pyr_laplace);
327     else
328         createLaplacePyr(img_with_border, num_bands_, src_pyr_laplace);
329 
330     LOGLN("  Create the source image Laplacian pyramid, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
331 #if ENABLE_LOG
332     t = getTickCount();
333 #endif
334 
335     // Create the weight map Gaussian pyramid
336     UMat weight_map;
337     std::vector<UMat> weight_pyr_gauss(num_bands_ + 1);
338 
339     if(weight_type_ == CV_32F)
340     {
341         mask.getUMat().convertTo(weight_map, CV_32F, 1./255.);
342     }
343     else // weight_type_ == CV_16S
344     {
345         mask.getUMat().convertTo(weight_map, CV_16S);
346         UMat add_mask;
347         compare(mask, 0, add_mask, CMP_NE);
348         add(weight_map, Scalar::all(1), weight_map, add_mask);
349     }
350 
351     copyMakeBorder(weight_map, weight_pyr_gauss[0], top, bottom, left, right, BORDER_CONSTANT);
352 
353     for (int i = 0; i < num_bands_; ++i)
354         pyrDown(weight_pyr_gauss[i], weight_pyr_gauss[i + 1]);
355 
356     LOGLN("  Create the weight map Gaussian pyramid, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
357 #if ENABLE_LOG
358     t = getTickCount();
359 #endif
360 
361     int y_tl = tl_new.y - dst_roi_.y;
362     int y_br = br_new.y - dst_roi_.y;
363     int x_tl = tl_new.x - dst_roi_.x;
364     int x_br = br_new.x - dst_roi_.x;
365 
366     // Add weighted layer of the source image to the final Laplacian pyramid layer
367     for (int i = 0; i <= num_bands_; ++i)
368     {
369         Rect rc(x_tl, y_tl, x_br - x_tl, y_br - y_tl);
370 #ifdef HAVE_OPENCL
371         if ( !cv::ocl::useOpenCL() ||
372              !ocl_MultiBandBlender_feed(src_pyr_laplace[i], weight_pyr_gauss[i],
373                     dst_pyr_laplace_[i](rc), dst_band_weights_[i](rc)) )
374 #endif
375         {
376             Mat _src_pyr_laplace = src_pyr_laplace[i].getMat(ACCESS_READ);
377             Mat _dst_pyr_laplace = dst_pyr_laplace_[i](rc).getMat(ACCESS_RW);
378             Mat _weight_pyr_gauss = weight_pyr_gauss[i].getMat(ACCESS_READ);
379             Mat _dst_band_weights = dst_band_weights_[i](rc).getMat(ACCESS_RW);
380             if(weight_type_ == CV_32F)
381             {
382                 for (int y = 0; y < rc.height; ++y)
383                 {
384                     const Point3_<short>* src_row = _src_pyr_laplace.ptr<Point3_<short> >(y);
385                     Point3_<short>* dst_row = _dst_pyr_laplace.ptr<Point3_<short> >(y);
386                     const float* weight_row = _weight_pyr_gauss.ptr<float>(y);
387                     float* dst_weight_row = _dst_band_weights.ptr<float>(y);
388 
389                     for (int x = 0; x < rc.width; ++x)
390                     {
391                         dst_row[x].x += static_cast<short>(src_row[x].x * weight_row[x]);
392                         dst_row[x].y += static_cast<short>(src_row[x].y * weight_row[x]);
393                         dst_row[x].z += static_cast<short>(src_row[x].z * weight_row[x]);
394                         dst_weight_row[x] += weight_row[x];
395                     }
396                 }
397             }
398             else // weight_type_ == CV_16S
399             {
400                 for (int y = 0; y < y_br - y_tl; ++y)
401                 {
402                     const Point3_<short>* src_row = _src_pyr_laplace.ptr<Point3_<short> >(y);
403                     Point3_<short>* dst_row = _dst_pyr_laplace.ptr<Point3_<short> >(y);
404                     const short* weight_row = _weight_pyr_gauss.ptr<short>(y);
405                     short* dst_weight_row = _dst_band_weights.ptr<short>(y);
406 
407                     for (int x = 0; x < x_br - x_tl; ++x)
408                     {
409                         dst_row[x].x += short((src_row[x].x * weight_row[x]) >> 8);
410                         dst_row[x].y += short((src_row[x].y * weight_row[x]) >> 8);
411                         dst_row[x].z += short((src_row[x].z * weight_row[x]) >> 8);
412                         dst_weight_row[x] += weight_row[x];
413                     }
414                 }
415             }
416         }
417 #ifdef HAVE_OPENCL
418         else
419         {
420             CV_IMPL_ADD(CV_IMPL_OCL);
421         }
422 #endif
423 
424         x_tl /= 2; y_tl /= 2;
425         x_br /= 2; y_br /= 2;
426     }
427 
428     LOGLN("  Add weighted layer of the source image to the final Laplacian pyramid layer, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
429 }
430 
431 
blend(InputOutputArray dst,InputOutputArray dst_mask)432 void MultiBandBlender::blend(InputOutputArray dst, InputOutputArray dst_mask)
433 {
434     for (int i = 0; i <= num_bands_; ++i)
435         normalizeUsingWeightMap(dst_band_weights_[i], dst_pyr_laplace_[i]);
436 
437     if (can_use_gpu_)
438         restoreImageFromLaplacePyrGpu(dst_pyr_laplace_);
439     else
440         restoreImageFromLaplacePyr(dst_pyr_laplace_);
441 
442     Rect dst_rc(0, 0, dst_roi_final_.width, dst_roi_final_.height);
443     dst_ = dst_pyr_laplace_[0](dst_rc);
444     UMat _dst_mask;
445     compare(dst_band_weights_[0](dst_rc), WEIGHT_EPS, dst_mask_, CMP_GT);
446     dst_pyr_laplace_.clear();
447     dst_band_weights_.clear();
448 
449     Blender::blend(dst, dst_mask);
450 }
451 
452 
453 //////////////////////////////////////////////////////////////////////////////
454 // Auxiliary functions
455 
456 #ifdef HAVE_OPENCL
ocl_normalizeUsingWeightMap(InputArray _weight,InputOutputArray _mat)457 static bool ocl_normalizeUsingWeightMap(InputArray _weight, InputOutputArray _mat)
458 {
459     String buildOptions = "-D DEFINE_normalizeUsingWeightMap";
460     ocl::buildOptionsAddMatrixDescription(buildOptions, "mat", _mat);
461     ocl::buildOptionsAddMatrixDescription(buildOptions, "weight", _weight);
462     ocl::Kernel k("normalizeUsingWeightMap", ocl::stitching::multibandblend_oclsrc, buildOptions);
463     if (k.empty())
464         return false;
465 
466     UMat mat = _mat.getUMat();
467 
468     k.args(ocl::KernelArg::ReadWrite(mat),
469            ocl::KernelArg::ReadOnly(_weight.getUMat())
470            );
471 
472     size_t globalsize[2] = {mat.cols, mat.rows };
473     return k.run(2, globalsize, NULL, false);
474 }
475 #endif
476 
normalizeUsingWeightMap(InputArray _weight,InputOutputArray _src)477 void normalizeUsingWeightMap(InputArray _weight, InputOutputArray _src)
478 {
479     Mat src;
480     Mat weight;
481 #ifdef HAVE_TEGRA_OPTIMIZATION
482     src = _src.getMat();
483     weight = _weight.getMat();
484     if(tegra::useTegra() && tegra::normalizeUsingWeightMap(weight, src))
485         return;
486 #endif
487 
488 #ifdef HAVE_OPENCL
489     if ( !cv::ocl::useOpenCL() ||
490             !ocl_normalizeUsingWeightMap(_weight, _src) )
491 #endif
492     {
493         src = _src.getMat();
494         weight = _weight.getMat();
495 
496         CV_Assert(src.type() == CV_16SC3);
497 
498         if (weight.type() == CV_32FC1)
499         {
500             for (int y = 0; y < src.rows; ++y)
501             {
502                 Point3_<short> *row = src.ptr<Point3_<short> >(y);
503                 const float *weight_row = weight.ptr<float>(y);
504 
505                 for (int x = 0; x < src.cols; ++x)
506                 {
507                     row[x].x = static_cast<short>(row[x].x / (weight_row[x] + WEIGHT_EPS));
508                     row[x].y = static_cast<short>(row[x].y / (weight_row[x] + WEIGHT_EPS));
509                     row[x].z = static_cast<short>(row[x].z / (weight_row[x] + WEIGHT_EPS));
510                 }
511             }
512         }
513         else
514         {
515             CV_Assert(weight.type() == CV_16SC1);
516 
517             for (int y = 0; y < src.rows; ++y)
518             {
519                 const short *weight_row = weight.ptr<short>(y);
520                 Point3_<short> *row = src.ptr<Point3_<short> >(y);
521 
522                 for (int x = 0; x < src.cols; ++x)
523                 {
524                     int w = weight_row[x] + 1;
525                     row[x].x = static_cast<short>((row[x].x << 8) / w);
526                     row[x].y = static_cast<short>((row[x].y << 8) / w);
527                     row[x].z = static_cast<short>((row[x].z << 8) / w);
528                 }
529             }
530         }
531     }
532 #ifdef HAVE_OPENCL
533     else
534     {
535         CV_IMPL_ADD(CV_IMPL_OCL);
536     }
537 #endif
538 }
539 
540 
createWeightMap(InputArray mask,float sharpness,InputOutputArray weight)541 void createWeightMap(InputArray mask, float sharpness, InputOutputArray weight)
542 {
543     CV_Assert(mask.type() == CV_8U);
544     distanceTransform(mask, weight, DIST_L1, 3);
545     UMat tmp;
546     multiply(weight, sharpness, tmp);
547     threshold(tmp, weight, 1.f, 1.f, THRESH_TRUNC);
548 }
549 
550 
createLaplacePyr(InputArray img,int num_levels,std::vector<UMat> & pyr)551 void createLaplacePyr(InputArray img, int num_levels, std::vector<UMat> &pyr)
552 {
553 #ifdef HAVE_TEGRA_OPTIMIZATION
554     cv::Mat imgMat = img.getMat();
555     if(tegra::useTegra() && tegra::createLaplacePyr(imgMat, num_levels, pyr))
556         return;
557 #endif
558 
559     pyr.resize(num_levels + 1);
560 
561     if(img.depth() == CV_8U)
562     {
563         if(num_levels == 0)
564         {
565             img.getUMat().convertTo(pyr[0], CV_16S);
566             return;
567         }
568 
569         UMat downNext;
570         UMat current = img.getUMat();
571         pyrDown(img, downNext);
572 
573         for(int i = 1; i < num_levels; ++i)
574         {
575             UMat lvl_up;
576             UMat lvl_down;
577 
578             pyrDown(downNext, lvl_down);
579             pyrUp(downNext, lvl_up, current.size());
580             subtract(current, lvl_up, pyr[i-1], noArray(), CV_16S);
581 
582             current = downNext;
583             downNext = lvl_down;
584         }
585 
586         {
587             UMat lvl_up;
588             pyrUp(downNext, lvl_up, current.size());
589             subtract(current, lvl_up, pyr[num_levels-1], noArray(), CV_16S);
590 
591             downNext.convertTo(pyr[num_levels], CV_16S);
592         }
593     }
594     else
595     {
596         pyr[0] = img.getUMat();
597         for (int i = 0; i < num_levels; ++i)
598             pyrDown(pyr[i], pyr[i + 1]);
599         UMat tmp;
600         for (int i = 0; i < num_levels; ++i)
601         {
602             pyrUp(pyr[i + 1], tmp, pyr[i].size());
603             subtract(pyr[i], tmp, pyr[i]);
604         }
605     }
606 }
607 
608 
createLaplacePyrGpu(InputArray img,int num_levels,std::vector<UMat> & pyr)609 void createLaplacePyrGpu(InputArray img, int num_levels, std::vector<UMat> &pyr)
610 {
611 #if defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING)
612     pyr.resize(num_levels + 1);
613 
614     std::vector<cuda::GpuMat> gpu_pyr(num_levels + 1);
615     gpu_pyr[0].upload(img);
616     for (int i = 0; i < num_levels; ++i)
617         cuda::pyrDown(gpu_pyr[i], gpu_pyr[i + 1]);
618 
619     cuda::GpuMat tmp;
620     for (int i = 0; i < num_levels; ++i)
621     {
622         cuda::pyrUp(gpu_pyr[i + 1], tmp);
623         cuda::subtract(gpu_pyr[i], tmp, gpu_pyr[i]);
624         gpu_pyr[i].download(pyr[i]);
625     }
626 
627     gpu_pyr[num_levels].download(pyr[num_levels]);
628 #else
629     (void)img;
630     (void)num_levels;
631     (void)pyr;
632     CV_Error(Error::StsNotImplemented, "CUDA optimization is unavailable");
633 #endif
634 }
635 
636 
restoreImageFromLaplacePyr(std::vector<UMat> & pyr)637 void restoreImageFromLaplacePyr(std::vector<UMat> &pyr)
638 {
639     if (pyr.empty())
640         return;
641     UMat tmp;
642     for (size_t i = pyr.size() - 1; i > 0; --i)
643     {
644         pyrUp(pyr[i], tmp, pyr[i - 1].size());
645         add(tmp, pyr[i - 1], pyr[i - 1]);
646     }
647 }
648 
649 
restoreImageFromLaplacePyrGpu(std::vector<UMat> & pyr)650 void restoreImageFromLaplacePyrGpu(std::vector<UMat> &pyr)
651 {
652 #if defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING)
653     if (pyr.empty())
654         return;
655 
656     std::vector<cuda::GpuMat> gpu_pyr(pyr.size());
657     for (size_t i = 0; i < pyr.size(); ++i)
658         gpu_pyr[i].upload(pyr[i]);
659 
660     cuda::GpuMat tmp;
661     for (size_t i = pyr.size() - 1; i > 0; --i)
662     {
663         cuda::pyrUp(gpu_pyr[i], tmp);
664         cuda::add(tmp, gpu_pyr[i - 1], gpu_pyr[i - 1]);
665     }
666 
667     gpu_pyr[0].download(pyr[0]);
668 #else
669     (void)pyr;
670     CV_Error(Error::StsNotImplemented, "CUDA optimization is unavailable");
671 #endif
672 }
673 
674 } // namespace detail
675 } // namespace cv
676