refactored opencv_stitching, added possibility to turn off BA

pull/13383/head
Alexey Spizhevoy 14 years ago
parent 6e3a1f7b49
commit b97ecdff0d
  1. 2
      modules/stitching/blenders.hpp
  2. 21
      modules/stitching/exposure_compensate.cpp
  3. 23
      modules/stitching/main.cpp
  4. 20
      modules/stitching/motion_estimators.cpp
  5. 2
      modules/stitching/motion_estimators.hpp
  6. 5
      modules/stitching/seam_finders.cpp

@ -114,7 +114,7 @@ void createLaplacePyr(const cv::Mat &img, int num_levels, std::vector<cv::Mat>&
void createLaplacePyrGpu(const cv::Mat &img, int num_levels, std::vector<cv::Mat>& pyr); void createLaplacePyrGpu(const cv::Mat &img, int num_levels, std::vector<cv::Mat>& pyr);
#endif #endif
// Restores source image in-place (result will be stored in pyr[0]) // Restores source image
void restoreImageFromLaplacePyr(std::vector<cv::Mat>& pyr); void restoreImageFromLaplacePyr(std::vector<cv::Mat>& pyr);
#endif // __OPENCV_BLENDERS_HPP__ #endif // __OPENCV_BLENDERS_HPP__

@ -50,13 +50,13 @@ using namespace cv::gpu;
Ptr<ExposureCompensator> ExposureCompensator::createDefault(int type) Ptr<ExposureCompensator> ExposureCompensator::createDefault(int type)
{ {
if (type == NO) if (type == NO)
return new NoExposureCompensator(); return new NoExposureCompensator();
if (type == GAIN) if (type == GAIN)
return new GainCompensator(); return new GainCompensator();
if (type == GAIN_BLOCKS) if (type == GAIN_BLOCKS)
return new BlocksGainCompensator(); return new BlocksGainCompensator();
CV_Error(CV_StsBadArg, "unsupported exposure compensation method"); CV_Error(CV_StsBadArg, "unsupported exposure compensation method");
return NULL; return NULL;
} }
@ -74,6 +74,9 @@ void ExposureCompensator::feed(const vector<Point> &corners, const vector<Mat> &
void GainCompensator::feed(const vector<Point> &corners, const vector<Mat> &images, void GainCompensator::feed(const vector<Point> &corners, const vector<Mat> &images,
const vector<pair<Mat,uchar> > &masks) const vector<pair<Mat,uchar> > &masks)
{ {
LOGLN("Exposure compensation...");
int64 t = getTickCount();
CV_Assert(corners.size() == images.size() && images.size() == masks.size()); CV_Assert(corners.size() == images.size() && images.size() == masks.size());
const int num_images = static_cast<int>(images.size()); const int num_images = static_cast<int>(images.size());
@ -138,6 +141,8 @@ void GainCompensator::feed(const vector<Point> &corners, const vector<Mat> &imag
} }
solve(A, b, gains_); solve(A, b, gains_);
LOGLN("Exposure compensation, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
} }
@ -237,4 +242,4 @@ void BlocksGainCompensator::apply(int index, Point /*corner*/, Mat &image, const
row[x].z = saturate_cast<uchar>(row[x].z * gain_row[x]); row[x].z = saturate_cast<uchar>(row[x].z * gain_row[x]);
} }
} }
} }

@ -80,7 +80,7 @@ void printUsage()
" --conf_thresh <float>\n" " --conf_thresh <float>\n"
" Threshold for two images are from the same panorama confidence.\n" " Threshold for two images are from the same panorama confidence.\n"
" The default is 1.0.\n" " The default is 1.0.\n"
" --ba (ray|focal_ray)\n" " --ba (no|ray|focal_ray)\n"
" Bundle adjustment cost function. The default is 'focal_ray'.\n" " Bundle adjustment cost function. The default is 'focal_ray'.\n"
" --wave_correct (no|yes)\n" " --wave_correct (no|yes)\n"
" Perform wave effect correction. The default is 'yes'.\n" " Perform wave effect correction. The default is 'yes'.\n"
@ -187,7 +187,9 @@ int parseCmdArgs(int argc, char** argv)
} }
else if (string(argv[i]) == "--ba") else if (string(argv[i]) == "--ba")
{ {
if (string(argv[i + 1]) == "ray") if (string(argv[i + 1]) == "no")
ba_space = BundleAdjuster::NO;
else if (string(argv[i + 1]) == "ray")
ba_space = BundleAdjuster::RAY_SPACE; ba_space = BundleAdjuster::RAY_SPACE;
else if (string(argv[i + 1]) == "focal_ray") else if (string(argv[i + 1]) == "focal_ray")
ba_space = BundleAdjuster::FOCAL_RAY_SPACE; ba_space = BundleAdjuster::FOCAL_RAY_SPACE;
@ -423,12 +425,9 @@ int main(int argc, char* argv[])
return -1; return -1;
} }
LOGLN("Estimating rotations...");
t = getTickCount();
HomographyBasedEstimator estimator; HomographyBasedEstimator estimator;
vector<CameraParams> cameras; vector<CameraParams> cameras;
estimator(features, pairwise_matches, cameras); estimator(features, pairwise_matches, cameras);
LOGLN("Estimating rotations, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
for (size_t i = 0; i < cameras.size(); ++i) for (size_t i = 0; i < cameras.size(); ++i)
{ {
@ -438,11 +437,8 @@ int main(int argc, char* argv[])
LOGLN("Initial focal length #" << indices[i]+1 << ": " << cameras[i].focal); LOGLN("Initial focal length #" << indices[i]+1 << ": " << cameras[i].focal);
} }
LOG("Bundle adjustment");
t = getTickCount();
BundleAdjuster adjuster(ba_space, conf_thresh); BundleAdjuster adjuster(ba_space, conf_thresh);
adjuster(features, pairwise_matches, cameras); adjuster(features, pairwise_matches, cameras);
LOGLN("Bundle adjustment, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
// Find median focal length // Find median focal length
vector<double> focals; vector<double> focals;
@ -456,15 +452,12 @@ int main(int argc, char* argv[])
if (wave_correct) if (wave_correct)
{ {
LOGLN("Wave correcting...");
t = getTickCount();
vector<Mat> rmats; vector<Mat> rmats;
for (size_t i = 0; i < cameras.size(); ++i) for (size_t i = 0; i < cameras.size(); ++i)
rmats.push_back(cameras[i].R); rmats.push_back(cameras[i].R);
waveCorrect(rmats); waveCorrect(rmats);
for (size_t i = 0; i < cameras.size(); ++i) for (size_t i = 0; i < cameras.size(); ++i)
cameras[i].R = rmats[i]; cameras[i].R = rmats[i];
LOGLN("Wave correcting, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
} }
LOGLN("Warping images (auxiliary)... "); LOGLN("Warping images (auxiliary)... ");
@ -501,17 +494,11 @@ int main(int argc, char* argv[])
LOGLN("Warping images, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec"); LOGLN("Warping images, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
LOGLN("Exposure compensation (feed)...");
t = getTickCount();
Ptr<ExposureCompensator> compensator = ExposureCompensator::createDefault(expos_comp_type); Ptr<ExposureCompensator> compensator = ExposureCompensator::createDefault(expos_comp_type);
compensator->feed(corners, images_warped, masks_warped); compensator->feed(corners, images_warped, masks_warped);
LOGLN("Exposure compensation (feed), time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
LOGLN("Finding seams...");
t = getTickCount();
Ptr<SeamFinder> seam_finder = SeamFinder::createDefault(seam_find_type); Ptr<SeamFinder> seam_finder = SeamFinder::createDefault(seam_find_type);
seam_finder->find(images_warped_f, corners, masks_warped); seam_finder->find(images_warped_f, corners, masks_warped);
LOGLN("Finding seams, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
// Release unused memory // Release unused memory
images.clear(); images.clear();
@ -612,7 +599,7 @@ int main(int argc, char* argv[])
{ {
FeatherBlender* fb = dynamic_cast<FeatherBlender*>(static_cast<Blender*>(blender)); FeatherBlender* fb = dynamic_cast<FeatherBlender*>(static_cast<Blender*>(blender));
fb->setSharpness(1.f/blend_width); fb->setSharpness(1.f/blend_width);
LOGLN("Feather blender, number of bands: " << fb->sharpness()); LOGLN("Feather blender, sharpness: " << fb->sharpness());
} }
blender->prepare(corners, sizes); blender->prepare(corners, sizes);
} }

@ -107,6 +107,9 @@ struct CalcRotation
void HomographyBasedEstimator::estimate(const vector<ImageFeatures> &features, const vector<MatchesInfo> &pairwise_matches, void HomographyBasedEstimator::estimate(const vector<ImageFeatures> &features, const vector<MatchesInfo> &pairwise_matches,
vector<CameraParams> &cameras) vector<CameraParams> &cameras)
{ {
LOGLN("Estimating rotations...");
int64 t = getTickCount();
const int num_images = static_cast<int>(features.size()); const int num_images = static_cast<int>(features.size());
#if 0 #if 0
@ -142,6 +145,8 @@ void HomographyBasedEstimator::estimate(const vector<ImageFeatures> &features, c
vector<int> span_tree_centers; vector<int> span_tree_centers;
findMaxSpanningTree(num_images, pairwise_matches, span_tree, span_tree_centers); findMaxSpanningTree(num_images, pairwise_matches, span_tree, span_tree_centers);
span_tree.walkBreadthFirst(span_tree_centers[0], CalcRotation(num_images, pairwise_matches, cameras)); span_tree.walkBreadthFirst(span_tree_centers[0], CalcRotation(num_images, pairwise_matches, cameras));
LOGLN("Estimating rotations, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
} }
@ -150,6 +155,12 @@ void HomographyBasedEstimator::estimate(const vector<ImageFeatures> &features, c
void BundleAdjuster::estimate(const vector<ImageFeatures> &features, const vector<MatchesInfo> &pairwise_matches, void BundleAdjuster::estimate(const vector<ImageFeatures> &features, const vector<MatchesInfo> &pairwise_matches,
vector<CameraParams> &cameras) vector<CameraParams> &cameras)
{ {
if (cost_space_ == NO)
return;
LOG("Bundle adjustment");
int64 t = getTickCount();
num_images_ = static_cast<int>(features.size()); num_images_ = static_cast<int>(features.size());
features_ = &features[0]; features_ = &features[0];
pairwise_matches_ = &pairwise_matches[0]; pairwise_matches_ = &pairwise_matches[0];
@ -251,6 +262,8 @@ void BundleAdjuster::estimate(const vector<ImageFeatures> &features, const vecto
Mat R_inv = cameras[span_tree_centers[0]].R.inv(); Mat R_inv = cameras[span_tree_centers[0]].R.inv();
for (int i = 0; i < num_images_; ++i) for (int i = 0; i < num_images_; ++i)
cameras[i].R = R_inv * cameras[i].R; cameras[i].R = R_inv * cameras[i].R;
LOGLN("Bundle adjustment, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
} }
@ -306,7 +319,7 @@ void BundleAdjuster::calcError(Mat &err)
p2.x * R2[3] + p2.y * R2[4] + p2.z * R2[5], p2.x * R2[3] + p2.y * R2[4] + p2.z * R2[5],
p2.x * R2[6] + p2.y * R2[7] + p2.z * R2[8]); p2.x * R2[6] + p2.y * R2[7] + p2.z * R2[8]);
double mult = 1; double mult = 1; // For cost_space_ == RAY_SPACE
if (cost_space_ == FOCAL_RAY_SPACE) if (cost_space_ == FOCAL_RAY_SPACE)
mult = sqrt(f1 * f2); mult = sqrt(f1 * f2);
err.at<double>(3 * match_idx, 0) = mult * (d1.x - d2.x); err.at<double>(3 * match_idx, 0) = mult * (d1.x - d2.x);
@ -374,6 +387,9 @@ void BundleAdjuster::calcJacobian()
void waveCorrect(vector<Mat> &rmats) void waveCorrect(vector<Mat> &rmats)
{ {
LOGLN("Wave correcting...");
int64 t = getTickCount();
float data[9]; float data[9];
Mat r0(1, 3, CV_32F, data); Mat r0(1, 3, CV_32F, data);
Mat r1(1, 3, CV_32F, data + 3); Mat r1(1, 3, CV_32F, data + 3);
@ -403,6 +419,8 @@ void waveCorrect(vector<Mat> &rmats)
for (size_t i = 0; i < rmats.size(); ++i) for (size_t i = 0; i < rmats.size(); ++i)
rmats[i] = R * rmats[i]; rmats[i] = R * rmats[i];
LOGLN("Wave correcting, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
} }

@ -90,7 +90,7 @@ private:
class BundleAdjuster : public Estimator class BundleAdjuster : public Estimator
{ {
public: public:
enum { RAY_SPACE, FOCAL_RAY_SPACE }; enum { NO, RAY_SPACE, FOCAL_RAY_SPACE };
BundleAdjuster(int cost_space = FOCAL_RAY_SPACE, float conf_thresh = 1.f) BundleAdjuster(int cost_space = FOCAL_RAY_SPACE, float conf_thresh = 1.f)
: cost_space_(cost_space), conf_thresh_(conf_thresh) {} : cost_space_(cost_space), conf_thresh_(conf_thresh) {}

@ -64,6 +64,9 @@ Ptr<SeamFinder> SeamFinder::createDefault(int type)
void PairwiseSeamFinder::find(const vector<Mat> &src, const vector<Point> &corners, void PairwiseSeamFinder::find(const vector<Mat> &src, const vector<Point> &corners,
vector<Mat> &masks) vector<Mat> &masks)
{ {
LOGLN("Finding seams...");
int64 t = getTickCount();
if (src.size() == 0) if (src.size() == 0)
return; return;
@ -80,6 +83,8 @@ void PairwiseSeamFinder::find(const vector<Mat> &src, const vector<Point> &corne
findInPair(i, j, roi); findInPair(i, j, roi);
} }
} }
LOGLN("Finding seams, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
} }

Loading…
Cancel
Save