From ef966e15c1df0874be594b7e39b02fe310c6bdf8 Mon Sep 17 00:00:00 2001 From: Alexey Spizhevoy Date: Tue, 24 May 2011 05:58:15 +0000 Subject: [PATCH] updated focal estimation (opencv_stitching) + refactoring --- modules/stitching/autocalib.cpp | 45 +++++++++++++-------- modules/stitching/autocalib.hpp | 3 +- modules/stitching/blenders.cpp | 11 ++++- modules/stitching/matchers.cpp | 54 +++++++++++++------------ modules/stitching/matchers.hpp | 16 ++++---- modules/stitching/motion_estimators.cpp | 5 ++- 6 files changed, 80 insertions(+), 54 deletions(-) diff --git a/modules/stitching/autocalib.cpp b/modules/stitching/autocalib.cpp index 82013fb22..9e4ad7fd5 100644 --- a/modules/stitching/autocalib.cpp +++ b/modules/stitching/autocalib.cpp @@ -76,35 +76,46 @@ void focalsFromHomography(const Mat& H, double &f0, double &f1, bool &f0_ok, boo } -double estimateFocal(const vector &features, const vector &pairwise_matches) +void estimateFocal(const vector &features, const vector &pairwise_matches, + vector &focals) { const int num_images = static_cast(features.size()); + focals.resize(num_images); - vector focals; - for (int src_idx = 0; src_idx < num_images; ++src_idx) - { - for (int dst_idx = 0; dst_idx < num_images; ++dst_idx) + vector > all_focals(num_images); + + for (int i = 0; i < num_images; ++i) + { + for (int j = 0; j < num_images; ++j) { - const MatchesInfo &m = pairwise_matches[src_idx*num_images + dst_idx]; + const MatchesInfo &m = pairwise_matches[i*num_images + j]; if (m.H.empty()) continue; double f0, f1; bool f0ok, f1ok; focalsFromHomography(m.H, f0, f1, f0ok, f1ok); - if (f0ok && f1ok) focals.push_back(sqrt(f0*f1)); + if (f0ok && f1ok) + { + all_focals[i].push_back(f0); + all_focals[j].push_back(f1); + } } } - if (static_cast(focals.size()) >= num_images * (num_images - 1) / 2) - { - nth_element(focals.begin(), focals.end(), focals.begin() + focals.size()/2); - return focals[focals.size()/2]; - } - - LOGLN("Can't estimate focal length, will use naive approach"); - double focals_sum = 0; for (int i = 0; i < num_images; ++i) - focals_sum += features[i].img_size.width + features[i].img_size.height; - return focals_sum / num_images; + { + if (all_focals[i].size() > 0) + { + nth_element(all_focals[i].begin(), all_focals[i].end(), + all_focals[i].begin() + all_focals[i].size()/2); + focals[i] = all_focals[i][all_focals[i].size()/2]; + } + else + { + LOGLN("Can't estimate focal length #" << i << ", will use naive approach"); + focals[i] = features[i].img_size.width + features[i].img_size.height; + } + + } } diff --git a/modules/stitching/autocalib.hpp b/modules/stitching/autocalib.hpp index 6051bec86..53ecc4d94 100644 --- a/modules/stitching/autocalib.hpp +++ b/modules/stitching/autocalib.hpp @@ -49,6 +49,7 @@ // by Heung-Yeung Shum and Richard Szeliski. void focalsFromHomography(const cv::Mat &H, double &f0, double &f1, bool &f0_ok, bool &f1_ok); -double estimateFocal(const std::vector &features, const std::vector &pairwise_matches); +void estimateFocal(const std::vector &features, const std::vector &pairwise_matches, + std::vector &focals); #endif // __OPENCV_AUTOCALIB_HPP__ diff --git a/modules/stitching/blenders.cpp b/modules/stitching/blenders.cpp index 9d6fc46a1..1d9c4fa5d 100644 --- a/modules/stitching/blenders.cpp +++ b/modules/stitching/blenders.cpp @@ -185,6 +185,11 @@ void MultiBandBlender::feed(const Mat &img, const Mat &mask, Point tl) CV_Assert(img.type() == CV_16SC3); CV_Assert(mask.type() == CV_8U); + //int gap = 10 * (1 << num_bands_); + //Point tl_new(max(dst_roi_.x, tl.x - gap), + // max(dst_roi_.y, tl.y - gap)); + //Point br_new(min(dst_roi_.br().x, tl.x + img.cols + gap), + // min(dst_roi_.br().y, tl.y + img.rows + gap)); Point tl_new(dst_roi_.tl()); Point br_new(dst_roi_.br()); int top = tl.y - tl_new.y; @@ -215,8 +220,10 @@ void MultiBandBlender::feed(const Mat &img, const Mat &mask, Point tl) // Add weighted layer of the source image to the final Laplacian pyramid layer for (int i = 0; i <= num_bands_; ++i) { - int dx = 0;//(tl_new.x >> i) - (dst_roi_.x >> i); - int dy = 0;//(tl_new.y >> i) - (dst_roi_.y >> i); + int dx = 0; + int dy = 0; + //int dx = (tl_new.x >> i) - (dst_roi_.x >> i); + //int dy = (tl_new.y >> i) - (dst_roi_.y >> i); for (int y = 0; y < src_pyr_laplace[i].rows; ++y) { diff --git a/modules/stitching/matchers.cpp b/modules/stitching/matchers.cpp index 86b4abca3..2a396c9e4 100644 --- a/modules/stitching/matchers.cpp +++ b/modules/stitching/matchers.cpp @@ -288,8 +288,11 @@ void FeaturesMatcher::operator ()(const vector &features, vector< ////////////////////////////////////////////////////////////////////////////// -namespace +namespace { + // These two classes are aimed to find features matches only, not to + // estimate homography + class CpuMatcher : public FeaturesMatcher { public: @@ -300,10 +303,24 @@ namespace float match_conf_; }; + + class GpuMatcher : public FeaturesMatcher + { + public: + GpuMatcher(float match_conf) : match_conf_(match_conf) {} + void match(const ImageFeatures &features1, const ImageFeatures &features2, MatchesInfo& matches_info); + + private: + float match_conf_; + GpuMat descriptors1_; + GpuMat descriptors2_; + GpuMat train_idx_, distance_, all_dist_; + }; + + void CpuMatcher::match(const ImageFeatures &features1, const ImageFeatures &features2, MatchesInfo& matches_info) { matches_info.matches.clear(); - BruteForceMatcher< L2 > matcher; vector< vector > pair_matches; @@ -332,34 +349,19 @@ namespace matches_info.matches.push_back(DMatch(m0.trainIdx, m0.queryIdx, m0.distance)); } } - - class GpuMatcher : public FeaturesMatcher - { - public: - GpuMatcher(float match_conf) : match_conf_(match_conf) {} - void match(const ImageFeatures &features1, const ImageFeatures &features2, MatchesInfo& matches_info); - - private: - float match_conf_; - GpuMat descriptors1_; - GpuMat descriptors2_; - GpuMat trainIdx_, distance_, allDist_; - }; + void GpuMatcher::match(const ImageFeatures &features1, const ImageFeatures &features2, MatchesInfo& matches_info) { - matches_info.matches.clear(); - - BruteForceMatcher_GPU< L2 > matcher; - + matches_info.matches.clear(); descriptors1_.upload(features1.descriptors); descriptors2_.upload(features2.descriptors); - + BruteForceMatcher_GPU< L2 > matcher; vector< vector > pair_matches; // Find 1->2 matches - matcher.knnMatch(descriptors1_, descriptors2_, trainIdx_, distance_, allDist_, 2); - matcher.knnMatchDownload(trainIdx_, distance_, pair_matches); + matcher.knnMatch(descriptors1_, descriptors2_, train_idx_, distance_, all_dist_, 2); + matcher.knnMatchDownload(train_idx_, distance_, pair_matches); for (size_t i = 0; i < pair_matches.size(); ++i) { if (pair_matches[i].size() < 2) @@ -376,8 +378,8 @@ namespace // Find 2->1 matches pair_matches.clear(); - matcher.knnMatch(descriptors2_, descriptors1_, trainIdx_, distance_, allDist_, 2); - matcher.knnMatchDownload(trainIdx_, distance_, pair_matches); + matcher.knnMatch(descriptors2_, descriptors1_, train_idx_, distance_, all_dist_, 2); + matcher.knnMatchDownload(train_idx_, distance_, pair_matches); for (size_t i = 0; i < pair_matches.size(); ++i) { if (pair_matches[i].size() < 2) @@ -392,7 +394,9 @@ namespace matches_info.matches.push_back(DMatch(m0.trainIdx, m0.queryIdx, m0.distance)); } } -} + +} // anonymous namespace + BestOf2NearestMatcher::BestOf2NearestMatcher(bool try_use_gpu, float match_conf, int num_matches_thresh1, int num_matches_thresh2) { diff --git a/modules/stitching/matchers.hpp b/modules/stitching/matchers.hpp index 5b3521f2e..f331d1e66 100644 --- a/modules/stitching/matchers.hpp +++ b/modules/stitching/matchers.hpp @@ -97,16 +97,18 @@ class FeaturesMatcher { public: virtual ~FeaturesMatcher() {} - void operator ()(const ImageFeatures &features1, const ImageFeatures &features2, MatchesInfo& matches_info) - { - match(features1, features2, matches_info); - } + + void operator ()(const ImageFeatures &features1, const ImageFeatures &features2, + MatchesInfo& matches_info) { match(features1, features2, matches_info); } void operator ()(const std::vector &features, std::vector &pairwise_matches); + bool isThreadSafe() const { return is_thread_safe_; } protected: FeaturesMatcher(bool is_thread_safe = false) : is_thread_safe_(is_thread_safe) {} - virtual void match(const ImageFeatures &features1, const ImageFeatures &features2, MatchesInfo& matches_info) = 0; + + virtual void match(const ImageFeatures &features1, const ImageFeatures &features2, + MatchesInfo& matches_info) = 0; bool is_thread_safe_; }; @@ -115,14 +117,14 @@ protected: class BestOf2NearestMatcher : public FeaturesMatcher { public: - BestOf2NearestMatcher(bool try_use_gpu = true, float match_conf = 0.55f, int num_matches_thresh1 = 6, int num_matches_thresh2 = 6); + BestOf2NearestMatcher(bool try_use_gpu = true, float match_conf = 0.55f, int num_matches_thresh1 = 6, + int num_matches_thresh2 = 6); protected: void match(const ImageFeatures &features1, const ImageFeatures &features2, MatchesInfo &matches_info); int num_matches_thresh1_; int num_matches_thresh2_; - cv::Ptr impl_; }; diff --git a/modules/stitching/motion_estimators.cpp b/modules/stitching/motion_estimators.cpp index 18662e219..8a7a08466 100644 --- a/modules/stitching/motion_estimators.cpp +++ b/modules/stitching/motion_estimators.cpp @@ -109,10 +109,11 @@ void HomographyBasedEstimator::estimate(const vector &features, c const int num_images = static_cast(features.size()); // Estimate focal length and set it for all cameras - double focal = estimateFocal(features, pairwise_matches); + vector focals; + estimateFocal(features, pairwise_matches, focals); cameras.resize(num_images); for (int i = 0; i < num_images; ++i) - cameras[i].focal = focal; + cameras[i].focal = focals[i]; // Restore global motion Graph span_tree;