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