1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 15:53:03 +04:00

Merge pull request #12827 from hrnr:stitching_4

[evolution] Stitching for OpenCV 4.0

* stitching: wrap Stitcher::create for bindings

* provide method for consistent stitcher usage across languages

* samples: add python stitching sample

* port cpp stitching sample to python

* stitching: consolidate Stitcher create methods

* remove Stitcher::createDefault, it returns Stitcher, not Ptr<Stitcher> -> inconsistent API
* deprecate cv::createStitcher and cv::createStitcherScans in favor of Stitcher::create

* stitching: avoid anonymous enum in Stitcher

* ORIG_RESOL should be double
* add documentatiton

* stitching: improve documentation in Stitcher

* stitching: expose estimator in Stitcher

* remove ABI hack

* stitching: drop try_use_gpu flag

* OCL will be used automatically through T-API in OCL-enable paths
* CUDA won't be used unless user sets CUDA-enabled classes manually

* stitching: drop FeaturesFinder

* use Feature2D instead of FeaturesFinder
* interoperability with features2d module
* detach from dependency on xfeatures2d

* features2d: fix compute and detect to work with UMat vectors

* correctly pass UMats as UMats to allow OCL paths
* support vector of UMats as output arg

* stitching: use nearest interpolation for resizing masks

* fix warnings
This commit is contained in:
Jiri Horner
2018-11-10 17:53:48 +01:00
committed by Alexander Alekhin
parent be9b676db3
commit 1ba7c728a6
18 changed files with 329 additions and 810 deletions
+45 -112
View File
@@ -44,67 +44,40 @@
namespace cv {
Stitcher Stitcher::createDefault(bool try_use_gpu)
Ptr<Stitcher> Stitcher::create(Mode mode)
{
Stitcher stitcher;
stitcher.setRegistrationResol(0.6);
stitcher.setSeamEstimationResol(0.1);
stitcher.setCompositingResol(ORIG_RESOL);
stitcher.setPanoConfidenceThresh(1);
stitcher.setWaveCorrection(true);
stitcher.setWaveCorrectKind(detail::WAVE_CORRECT_HORIZ);
stitcher.setFeaturesMatcher(makePtr<detail::BestOf2NearestMatcher>(try_use_gpu));
stitcher.setBundleAdjuster(makePtr<detail::BundleAdjusterRay>());
Ptr<Stitcher> stitcher = makePtr<Stitcher>();
#ifdef HAVE_OPENCV_CUDALEGACY
if (try_use_gpu && cuda::getCudaEnabledDeviceCount() > 0)
{
#ifdef HAVE_OPENCV_XFEATURES2D
stitcher.setFeaturesFinder(makePtr<detail::SurfFeaturesFinderGpu>());
#else
stitcher.setFeaturesFinder(makePtr<detail::OrbFeaturesFinder>());
#endif
stitcher.setWarper(makePtr<SphericalWarperGpu>());
stitcher.setSeamFinder(makePtr<detail::GraphCutSeamFinderGpu>());
}
else
#endif
{
#ifdef HAVE_OPENCV_XFEATURES2D
stitcher.setFeaturesFinder(makePtr<detail::SurfFeaturesFinder>());
#else
stitcher.setFeaturesFinder(makePtr<detail::OrbFeaturesFinder>());
#endif
stitcher.setWarper(makePtr<SphericalWarper>());
stitcher.setSeamFinder(makePtr<detail::GraphCutSeamFinder>(detail::GraphCutSeamFinderBase::COST_COLOR));
}
stitcher->setRegistrationResol(0.6);
stitcher->setSeamEstimationResol(0.1);
stitcher->setCompositingResol(ORIG_RESOL);
stitcher->setPanoConfidenceThresh(1);
stitcher->setSeamFinder(makePtr<detail::GraphCutSeamFinder>(detail::GraphCutSeamFinderBase::COST_COLOR));
stitcher->setBlender(makePtr<detail::MultiBandBlender>(false));
stitcher->setFeaturesFinder(ORB::create());
stitcher.setExposureCompensator(makePtr<detail::BlocksGainCompensator>());
stitcher.setBlender(makePtr<detail::MultiBandBlender>(try_use_gpu));
stitcher.work_scale_ = 1;
stitcher.seam_scale_ = 1;
stitcher.seam_work_aspect_ = 1;
stitcher.warped_image_scale_ = 1;
return stitcher;
}
Ptr<Stitcher> Stitcher::create(Mode mode, bool try_use_gpu)
{
Stitcher stit = createDefault(try_use_gpu);
Ptr<Stitcher> stitcher = makePtr<Stitcher>(stit);
stitcher->work_scale_ = 1;
stitcher->seam_scale_ = 1;
stitcher->seam_work_aspect_ = 1;
stitcher->warped_image_scale_ = 1;
switch (mode)
{
case PANORAMA: // PANORAMA is the default
// already setup
// mostly already setup
stitcher->setEstimator(makePtr<detail::HomographyBasedEstimator>());
stitcher->setWaveCorrection(true);
stitcher->setWaveCorrectKind(detail::WAVE_CORRECT_HORIZ);
stitcher->setFeaturesMatcher(makePtr<detail::BestOf2NearestMatcher>(false));
stitcher->setBundleAdjuster(makePtr<detail::BundleAdjusterRay>());
stitcher->setWarper(makePtr<SphericalWarper>());
stitcher->setExposureCompensator(makePtr<detail::BlocksGainCompensator>());
break;
case SCANS:
stitcher->setEstimator(makePtr<detail::AffineBasedEstimator>());
stitcher->setWaveCorrection(false);
stitcher->setFeaturesMatcher(makePtr<detail::AffineBestOf2NearestMatcher>(false, try_use_gpu));
stitcher->setFeaturesMatcher(makePtr<detail::AffineBestOf2NearestMatcher>(false, false));
stitcher->setBundleAdjuster(makePtr<detail::BundleAdjusterAffinePartial>());
stitcher->setWarper(makePtr<AffineWarper>());
stitcher->setExposureCompensator(makePtr<detail::NoExposureCompensator>());
@@ -119,20 +92,12 @@ Ptr<Stitcher> Stitcher::create(Mode mode, bool try_use_gpu)
}
Stitcher::Status Stitcher::estimateTransform(InputArrayOfArrays images)
{
CV_INSTRUMENT_REGION();
return estimateTransform(images, std::vector<std::vector<Rect> >());
}
Stitcher::Status Stitcher::estimateTransform(InputArrayOfArrays images, const std::vector<std::vector<Rect> > &rois)
Stitcher::Status Stitcher::estimateTransform(InputArrayOfArrays images, InputArrayOfArrays masks)
{
CV_INSTRUMENT_REGION();
images.getUMatVector(imgs_);
rois_ = rois;
masks.getUMatVector(masks_);
Status status;
@@ -407,20 +372,15 @@ Stitcher::Status Stitcher::composePanorama(InputArrayOfArrays images, OutputArra
Stitcher::Status Stitcher::stitch(InputArrayOfArrays images, OutputArray pano)
{
CV_INSTRUMENT_REGION();
Status status = estimateTransform(images);
if (status != OK)
return status;
return composePanorama(pano);
return stitch(images, noArray(), pano);
}
Stitcher::Status Stitcher::stitch(InputArrayOfArrays images, const std::vector<std::vector<Rect> > &rois, OutputArray pano)
Stitcher::Status Stitcher::stitch(InputArrayOfArrays images, InputArrayOfArrays masks, OutputArray pano)
{
CV_INSTRUMENT_REGION();
Status status = estimateTransform(images, rois);
Status status = estimateTransform(images, masks);
if (status != OK)
return status;
return composePanorama(pano);
@@ -440,7 +400,6 @@ Stitcher::Status Stitcher::matchImages()
seam_scale_ = 1;
bool is_work_scale_set = false;
bool is_seam_scale_set = false;
UMat full_img, img;
features_.resize(imgs_.size());
seam_est_imgs_.resize(imgs_.size());
full_img_sizes_.resize(imgs_.size());
@@ -451,16 +410,14 @@ Stitcher::Status Stitcher::matchImages()
#endif
std::vector<UMat> feature_find_imgs(imgs_.size());
std::vector<std::vector<Rect> > feature_find_rois(rois_.size());
std::vector<UMat> feature_find_masks(masks_.size());
for (size_t i = 0; i < imgs_.size(); ++i)
{
full_img = imgs_[i];
full_img_sizes_[i] = full_img.size();
full_img_sizes_[i] = imgs_[i].size();
if (registr_resol_ < 0)
{
img = full_img;
feature_find_imgs[i] = imgs_[i];
work_scale_ = 1;
is_work_scale_set = true;
}
@@ -468,50 +425,34 @@ Stitcher::Status Stitcher::matchImages()
{
if (!is_work_scale_set)
{
work_scale_ = std::min(1.0, std::sqrt(registr_resol_ * 1e6 / full_img.size().area()));
work_scale_ = std::min(1.0, std::sqrt(registr_resol_ * 1e6 / full_img_sizes_[i].area()));
is_work_scale_set = true;
}
resize(full_img, img, Size(), work_scale_, work_scale_, INTER_LINEAR_EXACT);
resize(imgs_[i], feature_find_imgs[i], Size(), work_scale_, work_scale_, INTER_LINEAR_EXACT);
}
if (!is_seam_scale_set)
{
seam_scale_ = std::min(1.0, std::sqrt(seam_est_resol_ * 1e6 / full_img.size().area()));
seam_scale_ = std::min(1.0, std::sqrt(seam_est_resol_ * 1e6 / full_img_sizes_[i].area()));
seam_work_aspect_ = seam_scale_ / work_scale_;
is_seam_scale_set = true;
}
if (rois_.empty())
feature_find_imgs[i] = img;
else
if (!masks_.empty())
{
feature_find_rois[i].resize(rois_[i].size());
for (size_t j = 0; j < rois_[i].size(); ++j)
{
Point tl(cvRound(rois_[i][j].x * work_scale_), cvRound(rois_[i][j].y * work_scale_));
Point br(cvRound(rois_[i][j].br().x * work_scale_), cvRound(rois_[i][j].br().y * work_scale_));
feature_find_rois[i][j] = Rect(tl, br);
}
feature_find_imgs[i] = img;
resize(masks_[i], feature_find_masks[i], Size(), work_scale_, work_scale_, INTER_NEAREST);
}
features_[i].img_idx = (int)i;
LOGLN("Features in image #" << i+1 << ": " << features_[i].keypoints.size());
resize(full_img, img, Size(), seam_scale_, seam_scale_, INTER_LINEAR_EXACT);
seam_est_imgs_[i] = img.clone();
resize(imgs_[i], seam_est_imgs_[i], Size(), seam_scale_, seam_scale_, INTER_LINEAR_EXACT);
}
// find features possibly in parallel
if (rois_.empty())
(*features_finder_)(feature_find_imgs, features_);
else
(*features_finder_)(feature_find_imgs, features_, feature_find_rois);
detail::computeImageFeatures(features_finder_, feature_find_imgs, features_, feature_find_masks);
// Do it to save memory
features_finder_->collectGarbage();
full_img.release();
img.release();
feature_find_imgs.clear();
feature_find_rois.clear();
feature_find_masks.clear();
LOGLN("Finding features, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
@@ -550,16 +491,8 @@ Stitcher::Status Stitcher::matchImages()
Stitcher::Status Stitcher::estimateCameraParams()
{
/* TODO OpenCV ABI 4.x
get rid of this dynamic_cast hack and use estimator_
*/
Ptr<detail::Estimator> estimator;
if (dynamic_cast<detail::AffineBestOf2NearestMatcher*>(features_matcher_.get()))
estimator = makePtr<detail::AffineBasedEstimator>();
else
estimator = makePtr<detail::HomographyBasedEstimator>();
if (!(*estimator)(features_, pairwise_matches_, cameras_))
// estimate homography in global frame
if (!(*estimator_)(features_, pairwise_matches_, cameras_))
return ERR_HOMOGRAPHY_EST_FAIL;
for (size_t i = 0; i < cameras_.size(); ++i)
@@ -602,17 +535,17 @@ Stitcher::Status Stitcher::estimateCameraParams()
}
Ptr<Stitcher> createStitcher(bool try_use_gpu)
CV_DEPRECATED Ptr<Stitcher> createStitcher(bool /*ignored*/)
{
CV_INSTRUMENT_REGION();
return Stitcher::create(Stitcher::PANORAMA, try_use_gpu);
return Stitcher::create(Stitcher::PANORAMA);
}
Ptr<Stitcher> createStitcherScans(bool try_use_gpu)
CV_DEPRECATED Ptr<Stitcher> createStitcherScans(bool /*ignored*/)
{
CV_INSTRUMENT_REGION();
return Stitcher::create(Stitcher::SCANS, try_use_gpu);
return Stitcher::create(Stitcher::SCANS);
}
} // namespace cv