Home | History | Annotate | Download | only in src
      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 
     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 
     64 void Blender::prepare(const std::vector<Point> &corners, const std::vector<Size> &sizes)
     65 {
     66     prepare(resultRoi(corners, sizes));
     67 }
     68 
     69 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    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 
    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 
    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
    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 
    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 
    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 
    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 
    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 
    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 
    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