1 /*M///////////////////////////////////////////////////////////////////////////////////////
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
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.
11 // For Open Source Computer Vision Library
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.
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
20 // * Redistribution's of source code must retain the above copyright notice,
21 // this list of conditions and the following disclaimer.
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.
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.
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.
43 #include "precomp.hpp"
47 Stitcher Stitcher::createDefault(bool try_use_gpu)
50 stitcher.setRegistrationResol(0.6);
51 stitcher.setSeamEstimationResol(0.1);
52 stitcher.setCompositingResol(ORIG_RESOL);
53 stitcher.setPanoConfidenceThresh(1);
54 stitcher.setWaveCorrection(true);
55 stitcher.setWaveCorrectKind(detail::WAVE_CORRECT_HORIZ);
56 stitcher.setFeaturesMatcher(makePtr<detail::BestOf2NearestMatcher>(try_use_gpu));
57 stitcher.setBundleAdjuster(makePtr<detail::BundleAdjusterRay>());
59 #ifdef HAVE_OPENCV_CUDA
60 if (try_use_gpu && cuda::getCudaEnabledDeviceCount() > 0)
62 #ifdef HAVE_OPENCV_NONFREE
63 stitcher.setFeaturesFinder(makePtr<detail::SurfFeaturesFinderGpu>());
65 stitcher.setFeaturesFinder(makePtr<detail::OrbFeaturesFinder>());
67 stitcher.setWarper(makePtr<SphericalWarperGpu>());
68 stitcher.setSeamFinder(makePtr<detail::GraphCutSeamFinderGpu>());
73 #ifdef HAVE_OPENCV_NONFREE
74 stitcher.setFeaturesFinder(makePtr<detail::SurfFeaturesFinder>());
76 stitcher.setFeaturesFinder(makePtr<detail::OrbFeaturesFinder>());
78 stitcher.setWarper(makePtr<SphericalWarper>());
79 stitcher.setSeamFinder(makePtr<detail::GraphCutSeamFinder>(detail::GraphCutSeamFinderBase::COST_COLOR));
82 stitcher.setExposureCompensator(makePtr<detail::BlocksGainCompensator>());
83 stitcher.setBlender(makePtr<detail::MultiBandBlender>(try_use_gpu));
89 Stitcher::Status Stitcher::estimateTransform(InputArrayOfArrays images)
91 return estimateTransform(images, std::vector<std::vector<Rect> >());
95 Stitcher::Status Stitcher::estimateTransform(InputArrayOfArrays images, const std::vector<std::vector<Rect> > &rois)
97 images.getUMatVector(imgs_);
102 if ((status = matchImages()) != OK)
105 if ((status = estimateCameraParams()) != OK)
113 Stitcher::Status Stitcher::composePanorama(OutputArray pano)
115 return composePanorama(std::vector<UMat>(), pano);
119 Stitcher::Status Stitcher::composePanorama(InputArrayOfArrays images, OutputArray pano)
121 LOGLN("Warping images (auxiliary)... ");
123 std::vector<UMat> imgs;
124 images.getUMatVector(imgs);
127 CV_Assert(imgs.size() == imgs_.size());
130 seam_est_imgs_.resize(imgs.size());
132 for (size_t i = 0; i < imgs.size(); ++i)
135 resize(imgs[i], img, Size(), seam_scale_, seam_scale_);
136 seam_est_imgs_[i] = img.clone();
139 std::vector<UMat> seam_est_imgs_subset;
140 std::vector<UMat> imgs_subset;
142 for (size_t i = 0; i < indices_.size(); ++i)
144 imgs_subset.push_back(imgs_[indices_[i]]);
145 seam_est_imgs_subset.push_back(seam_est_imgs_[indices_[i]]);
148 seam_est_imgs_ = seam_est_imgs_subset;
155 int64 t = getTickCount();
158 std::vector<Point> corners(imgs_.size());
159 std::vector<UMat> masks_warped(imgs_.size());
160 std::vector<UMat> images_warped(imgs_.size());
161 std::vector<Size> sizes(imgs_.size());
162 std::vector<UMat> masks(imgs_.size());
164 // Prepare image masks
165 for (size_t i = 0; i < imgs_.size(); ++i)
167 masks[i].create(seam_est_imgs_[i].size(), CV_8U);
168 masks[i].setTo(Scalar::all(255));
171 // Warp images and their masks
172 Ptr<detail::RotationWarper> w = warper_->create(float(warped_image_scale_ * seam_work_aspect_));
173 for (size_t i = 0; i < imgs_.size(); ++i)
176 cameras_[i].K().convertTo(K, CV_32F);
177 K(0,0) *= (float)seam_work_aspect_;
178 K(0,2) *= (float)seam_work_aspect_;
179 K(1,1) *= (float)seam_work_aspect_;
180 K(1,2) *= (float)seam_work_aspect_;
182 corners[i] = w->warp(seam_est_imgs_[i], K, cameras_[i].R, INTER_LINEAR, BORDER_CONSTANT, images_warped[i]);
183 sizes[i] = images_warped[i].size();
185 w->warp(masks[i], K, cameras_[i].R, INTER_NEAREST, BORDER_CONSTANT, masks_warped[i]);
188 std::vector<UMat> images_warped_f(imgs_.size());
189 for (size_t i = 0; i < imgs_.size(); ++i)
190 images_warped[i].convertTo(images_warped_f[i], CV_32F);
192 LOGLN("Warping images, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
195 exposure_comp_->feed(corners, images_warped, masks_warped);
196 seam_finder_->find(images_warped_f, corners, masks_warped);
198 // Release unused memory
199 seam_est_imgs_.clear();
200 images_warped.clear();
201 images_warped_f.clear();
204 LOGLN("Compositing...");
209 UMat img_warped, img_warped_s;
210 UMat dilated_mask, seam_mask, mask, mask_warped;
212 //double compose_seam_aspect = 1;
213 double compose_work_aspect = 1;
214 bool is_blender_prepared = false;
216 double compose_scale = 1;
217 bool is_compose_scale_set = false;
220 for (size_t img_idx = 0; img_idx < imgs_.size(); ++img_idx)
222 LOGLN("Compositing image #" << indices_[img_idx] + 1);
224 int64 compositing_t = getTickCount();
227 // Read image and resize it if necessary
228 full_img = imgs_[img_idx];
229 if (!is_compose_scale_set)
231 if (compose_resol_ > 0)
232 compose_scale = std::min(1.0, std::sqrt(compose_resol_ * 1e6 / full_img.size().area()));
233 is_compose_scale_set = true;
235 // Compute relative scales
236 //compose_seam_aspect = compose_scale / seam_scale_;
237 compose_work_aspect = compose_scale / work_scale_;
239 // Update warped image scale
240 warped_image_scale_ *= static_cast<float>(compose_work_aspect);
241 w = warper_->create((float)warped_image_scale_);
243 // Update corners and sizes
244 for (size_t i = 0; i < imgs_.size(); ++i)
247 cameras_[i].focal *= compose_work_aspect;
248 cameras_[i].ppx *= compose_work_aspect;
249 cameras_[i].ppy *= compose_work_aspect;
251 // Update corner and size
252 Size sz = full_img_sizes_[i];
253 if (std::abs(compose_scale - 1) > 1e-1)
255 sz.width = cvRound(full_img_sizes_[i].width * compose_scale);
256 sz.height = cvRound(full_img_sizes_[i].height * compose_scale);
260 cameras_[i].K().convertTo(K, CV_32F);
261 Rect roi = w->warpRoi(sz, K, cameras_[i].R);
262 corners[i] = roi.tl();
263 sizes[i] = roi.size();
266 if (std::abs(compose_scale - 1) > 1e-1)
269 int64 resize_t = getTickCount();
271 resize(full_img, img, Size(), compose_scale, compose_scale);
272 LOGLN(" resize time: " << ((getTickCount() - resize_t) / getTickFrequency()) << " sec");
277 Size img_size = img.size();
279 LOGLN(" after resize time: " << ((getTickCount() - compositing_t) / getTickFrequency()) << " sec");
282 cameras_[img_idx].K().convertTo(K, CV_32F);
285 int64 pt = getTickCount();
287 // Warp the current image
288 w->warp(img, K, cameras_[img_idx].R, INTER_LINEAR, BORDER_CONSTANT, img_warped);
289 LOGLN(" warp the current image: " << ((getTickCount() - pt) / getTickFrequency()) << " sec");
294 // Warp the current image mask
295 mask.create(img_size, CV_8U);
296 mask.setTo(Scalar::all(255));
297 w->warp(mask, K, cameras_[img_idx].R, INTER_NEAREST, BORDER_CONSTANT, mask_warped);
298 LOGLN(" warp the current image mask: " << ((getTickCount() - pt) / getTickFrequency()) << " sec");
303 // Compensate exposure
304 exposure_comp_->apply((int)img_idx, corners[img_idx], img_warped, mask_warped);
305 LOGLN(" compensate exposure: " << ((getTickCount() - pt) / getTickFrequency()) << " sec");
310 img_warped.convertTo(img_warped_s, CV_16S);
311 img_warped.release();
315 // Make sure seam mask has proper size
316 dilate(masks_warped[img_idx], dilated_mask, Mat());
317 resize(dilated_mask, seam_mask, mask_warped.size());
319 bitwise_and(seam_mask, mask_warped, mask_warped);
321 LOGLN(" other: " << ((getTickCount() - pt) / getTickFrequency()) << " sec");
326 if (!is_blender_prepared)
328 blender_->prepare(corners, sizes);
329 is_blender_prepared = true;
332 LOGLN(" other2: " << ((getTickCount() - pt) / getTickFrequency()) << " sec");
336 int64 feed_t = getTickCount();
338 // Blend the current image
339 blender_->feed(img_warped_s, mask_warped, corners[img_idx]);
340 LOGLN(" feed time: " << ((getTickCount() - feed_t) / getTickFrequency()) << " sec");
341 LOGLN("Compositing ## time: " << ((getTickCount() - compositing_t) / getTickFrequency()) << " sec");
345 int64 blend_t = getTickCount();
347 UMat result, result_mask;
348 blender_->blend(result, result_mask);
349 LOGLN("blend time: " << ((getTickCount() - blend_t) / getTickFrequency()) << " sec");
351 LOGLN("Compositing, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
353 // Preliminary result is in CV_16SC3 format, but all values are in [0,255] range,
354 // so convert it to avoid user confusing
355 result.convertTo(pano, CV_8U);
361 Stitcher::Status Stitcher::stitch(InputArrayOfArrays images, OutputArray pano)
363 Status status = estimateTransform(images);
366 return composePanorama(pano);
370 Stitcher::Status Stitcher::stitch(InputArrayOfArrays images, const std::vector<std::vector<Rect> > &rois, OutputArray pano)
372 Status status = estimateTransform(images, rois);
375 return composePanorama(pano);
379 Stitcher::Status Stitcher::matchImages()
381 if ((int)imgs_.size() < 2)
383 LOGLN("Need more images");
384 return ERR_NEED_MORE_IMGS;
388 seam_work_aspect_ = 1;
390 bool is_work_scale_set = false;
391 bool is_seam_scale_set = false;
393 features_.resize(imgs_.size());
394 seam_est_imgs_.resize(imgs_.size());
395 full_img_sizes_.resize(imgs_.size());
397 LOGLN("Finding features...");
399 int64 t = getTickCount();
402 for (size_t i = 0; i < imgs_.size(); ++i)
405 full_img_sizes_[i] = full_img.size();
407 if (registr_resol_ < 0)
411 is_work_scale_set = true;
415 if (!is_work_scale_set)
417 work_scale_ = std::min(1.0, std::sqrt(registr_resol_ * 1e6 / full_img.size().area()));
418 is_work_scale_set = true;
420 resize(full_img, img, Size(), work_scale_, work_scale_);
422 if (!is_seam_scale_set)
424 seam_scale_ = std::min(1.0, std::sqrt(seam_est_resol_ * 1e6 / full_img.size().area()));
425 seam_work_aspect_ = seam_scale_ / work_scale_;
426 is_seam_scale_set = true;
430 (*features_finder_)(img, features_[i]);
433 std::vector<Rect> rois(rois_[i].size());
434 for (size_t j = 0; j < rois_[i].size(); ++j)
436 Point tl(cvRound(rois_[i][j].x * work_scale_), cvRound(rois_[i][j].y * work_scale_));
437 Point br(cvRound(rois_[i][j].br().x * work_scale_), cvRound(rois_[i][j].br().y * work_scale_));
438 rois[j] = Rect(tl, br);
440 (*features_finder_)(img, features_[i], rois);
442 features_[i].img_idx = (int)i;
443 LOGLN("Features in image #" << i+1 << ": " << features_[i].keypoints.size());
445 resize(full_img, img, Size(), seam_scale_, seam_scale_);
446 seam_est_imgs_[i] = img.clone();
449 // Do it to save memory
450 features_finder_->collectGarbage();
454 LOGLN("Finding features, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
456 LOG("Pairwise matching");
460 (*features_matcher_)(features_, pairwise_matches_, matching_mask_);
461 features_matcher_->collectGarbage();
462 LOGLN("Pairwise matching, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
464 // Leave only images we are sure are from the same panorama
465 indices_ = detail::leaveBiggestComponent(features_, pairwise_matches_, (float)conf_thresh_);
466 std::vector<UMat> seam_est_imgs_subset;
467 std::vector<UMat> imgs_subset;
468 std::vector<Size> full_img_sizes_subset;
469 for (size_t i = 0; i < indices_.size(); ++i)
471 imgs_subset.push_back(imgs_[indices_[i]]);
472 seam_est_imgs_subset.push_back(seam_est_imgs_[indices_[i]]);
473 full_img_sizes_subset.push_back(full_img_sizes_[indices_[i]]);
475 seam_est_imgs_ = seam_est_imgs_subset;
477 full_img_sizes_ = full_img_sizes_subset;
479 if ((int)imgs_.size() < 2)
481 LOGLN("Need more images");
482 return ERR_NEED_MORE_IMGS;
489 Stitcher::Status Stitcher::estimateCameraParams()
491 detail::HomographyBasedEstimator estimator;
492 if (!estimator(features_, pairwise_matches_, cameras_))
493 return ERR_HOMOGRAPHY_EST_FAIL;
495 for (size_t i = 0; i < cameras_.size(); ++i)
498 cameras_[i].R.convertTo(R, CV_32F);
500 //LOGLN("Initial intrinsic parameters #" << indices_[i] + 1 << ":\n " << cameras_[i].K());
503 bundle_adjuster_->setConfThresh(conf_thresh_);
504 if (!(*bundle_adjuster_)(features_, pairwise_matches_, cameras_))
505 return ERR_CAMERA_PARAMS_ADJUST_FAIL;
507 // Find median focal length and use it as final image scale
508 std::vector<double> focals;
509 for (size_t i = 0; i < cameras_.size(); ++i)
511 //LOGLN("Camera #" << indices_[i] + 1 << ":\n" << cameras_[i].K());
512 focals.push_back(cameras_[i].focal);
515 std::sort(focals.begin(), focals.end());
516 if (focals.size() % 2 == 1)
517 warped_image_scale_ = static_cast<float>(focals[focals.size() / 2]);
519 warped_image_scale_ = static_cast<float>(focals[focals.size() / 2 - 1] + focals[focals.size() / 2]) * 0.5f;
521 if (do_wave_correct_)
523 std::vector<Mat> rmats;
524 for (size_t i = 0; i < cameras_.size(); ++i)
525 rmats.push_back(cameras_[i].R.clone());
526 detail::waveCorrect(rmats, wave_correct_kind_);
527 for (size_t i = 0; i < cameras_.size(); ++i)
528 cameras_[i].R = rmats[i];