Path: blob/master/modules/stitching/src/motion_estimators.cpp
16337 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.13// Copyright (C) 2009, Willow Garage Inc., all rights reserved.14// Third party copyrights are property of their respective owners.15//16// Redistribution and use in source and binary forms, with or without modification,17// are permitted provided that the following conditions are met:18//19// * Redistribution's of source code must retain the above copyright notice,20// this list of conditions and the following disclaimer.21//22// * Redistribution's in binary form must reproduce the above copyright notice,23// this list of conditions and the following disclaimer in the documentation24// and/or other materials provided with the distribution.25//26// * The name of the copyright holders may not be used to endorse or promote products27// derived from this software without specific prior written permission.28//29// This software is provided by the copyright holders and contributors "as is" and30// any express or implied warranties, including, but not limited to, the implied31// warranties of merchantability and fitness for a particular purpose are disclaimed.32// In no event shall the Intel Corporation or contributors be liable for any direct,33// indirect, incidental, special, exemplary, or consequential damages34// (including, but not limited to, procurement of substitute goods or services;35// loss of use, data, or profits; or business interruption) however caused36// and on any theory of liability, whether in contract, strict liability,37// or tort (including negligence or otherwise) arising in any way out of38// the use of this software, even if advised of the possibility of such damage.39//40//M*/4142#include "precomp.hpp"43#include "opencv2/calib3d/calib3d_c.h"44#include "opencv2/core/cvdef.h"4546using namespace cv;47using namespace cv::detail;4849namespace {5051struct IncDistance52{53IncDistance(std::vector<int> &vdists) : dists(&vdists[0]) {}54void operator ()(const GraphEdge &edge) { dists[edge.to] = dists[edge.from] + 1; }55int* dists;56};575859struct CalcRotation60{61CalcRotation(int _num_images, const std::vector<MatchesInfo> &_pairwise_matches, std::vector<CameraParams> &_cameras)62: num_images(_num_images), pairwise_matches(&_pairwise_matches[0]), cameras(&_cameras[0]) {}6364void operator ()(const GraphEdge &edge)65{66int pair_idx = edge.from * num_images + edge.to;6768Mat_<double> K_from = Mat::eye(3, 3, CV_64F);69K_from(0,0) = cameras[edge.from].focal;70K_from(1,1) = cameras[edge.from].focal * cameras[edge.from].aspect;71K_from(0,2) = cameras[edge.from].ppx;72K_from(1,2) = cameras[edge.from].ppy;7374Mat_<double> K_to = Mat::eye(3, 3, CV_64F);75K_to(0,0) = cameras[edge.to].focal;76K_to(1,1) = cameras[edge.to].focal * cameras[edge.to].aspect;77K_to(0,2) = cameras[edge.to].ppx;78K_to(1,2) = cameras[edge.to].ppy;7980Mat R = K_from.inv() * pairwise_matches[pair_idx].H.inv() * K_to;81cameras[edge.to].R = cameras[edge.from].R * R;82}8384int num_images;85const MatchesInfo* pairwise_matches;86CameraParams* cameras;87};888990/**91* @brief Functor calculating final transformation by chaining linear transformations92*/93struct CalcAffineTransform94{95CalcAffineTransform(int _num_images,96const std::vector<MatchesInfo> &_pairwise_matches,97std::vector<CameraParams> &_cameras)98: num_images(_num_images), pairwise_matches(&_pairwise_matches[0]), cameras(&_cameras[0]) {}99100void operator()(const GraphEdge &edge)101{102int pair_idx = edge.from * num_images + edge.to;103cameras[edge.to].R = cameras[edge.from].R * pairwise_matches[pair_idx].H;104}105106int num_images;107const MatchesInfo *pairwise_matches;108CameraParams *cameras;109};110111112//////////////////////////////////////////////////////////////////////////////113114void calcDeriv(const Mat &err1, const Mat &err2, double h, Mat res)115{116for (int i = 0; i < err1.rows; ++i)117res.at<double>(i, 0) = (err2.at<double>(i, 0) - err1.at<double>(i, 0)) / h;118}119120} // namespace121122123namespace cv {124namespace detail {125126bool HomographyBasedEstimator::estimate(127const std::vector<ImageFeatures> &features,128const std::vector<MatchesInfo> &pairwise_matches,129std::vector<CameraParams> &cameras)130{131LOGLN("Estimating rotations...");132#if ENABLE_LOG133int64 t = getTickCount();134#endif135136const int num_images = static_cast<int>(features.size());137138#if 0139// Robustly estimate focal length from rotating cameras140std::vector<Mat> Hs;141for (int iter = 0; iter < 100; ++iter)142{143int len = 2 + rand()%(pairwise_matches.size() - 1);144std::vector<int> subset;145selectRandomSubset(len, pairwise_matches.size(), subset);146Hs.clear();147for (size_t i = 0; i < subset.size(); ++i)148if (!pairwise_matches[subset[i]].H.empty())149Hs.push_back(pairwise_matches[subset[i]].H);150Mat_<double> K;151if (Hs.size() >= 2)152{153if (calibrateRotatingCamera(Hs, K))154cin.get();155}156}157#endif158159if (!is_focals_estimated_)160{161// Estimate focal length and set it for all cameras162std::vector<double> focals;163estimateFocal(features, pairwise_matches, focals);164cameras.assign(num_images, CameraParams());165for (int i = 0; i < num_images; ++i)166cameras[i].focal = focals[i];167}168else169{170for (int i = 0; i < num_images; ++i)171{172cameras[i].ppx -= 0.5 * features[i].img_size.width;173cameras[i].ppy -= 0.5 * features[i].img_size.height;174}175}176177// Restore global motion178Graph span_tree;179std::vector<int> span_tree_centers;180findMaxSpanningTree(num_images, pairwise_matches, span_tree, span_tree_centers);181span_tree.walkBreadthFirst(span_tree_centers[0], CalcRotation(num_images, pairwise_matches, cameras));182183// As calculations were performed under assumption that p.p. is in image center184for (int i = 0; i < num_images; ++i)185{186cameras[i].ppx += 0.5 * features[i].img_size.width;187cameras[i].ppy += 0.5 * features[i].img_size.height;188}189190LOGLN("Estimating rotations, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");191return true;192}193194195//////////////////////////////////////////////////////////////////////////////196197bool AffineBasedEstimator::estimate(const std::vector<ImageFeatures> &features,198const std::vector<MatchesInfo> &pairwise_matches,199std::vector<CameraParams> &cameras)200{201cameras.assign(features.size(), CameraParams());202const int num_images = static_cast<int>(features.size());203204// find maximum spaning tree on pairwise matches205cv::detail::Graph span_tree;206std::vector<int> span_tree_centers;207// uses number of inliers as weights208findMaxSpanningTree(num_images, pairwise_matches, span_tree,209span_tree_centers);210211// compute final transform by chaining H together212span_tree.walkBreadthFirst(213span_tree_centers[0],214CalcAffineTransform(num_images, pairwise_matches, cameras));215// this estimator never fails216return true;217}218219220//////////////////////////////////////////////////////////////////////////////221222bool BundleAdjusterBase::estimate(const std::vector<ImageFeatures> &features,223const std::vector<MatchesInfo> &pairwise_matches,224std::vector<CameraParams> &cameras)225{226LOG_CHAT("Bundle adjustment");227#if ENABLE_LOG228int64 t = getTickCount();229#endif230231num_images_ = static_cast<int>(features.size());232features_ = &features[0];233pairwise_matches_ = &pairwise_matches[0];234235setUpInitialCameraParams(cameras);236237// Leave only consistent image pairs238edges_.clear();239for (int i = 0; i < num_images_ - 1; ++i)240{241for (int j = i + 1; j < num_images_; ++j)242{243const MatchesInfo& matches_info = pairwise_matches_[i * num_images_ + j];244if (matches_info.confidence > conf_thresh_)245edges_.push_back(std::make_pair(i, j));246}247}248249// Compute number of correspondences250total_num_matches_ = 0;251for (size_t i = 0; i < edges_.size(); ++i)252total_num_matches_ += static_cast<int>(pairwise_matches[edges_[i].first * num_images_ +253edges_[i].second].num_inliers);254255CvLevMarq solver(num_images_ * num_params_per_cam_,256total_num_matches_ * num_errs_per_measurement_,257cvTermCriteria(term_criteria_));258259Mat err, jac;260CvMat matParams = cvMat(cam_params_);261cvCopy(&matParams, solver.param);262263int iter = 0;264for(;;)265{266const CvMat* _param = 0;267CvMat* _jac = 0;268CvMat* _err = 0;269270bool proceed = solver.update(_param, _jac, _err);271272cvCopy(_param, &matParams);273274if (!proceed || !_err)275break;276277if (_jac)278{279calcJacobian(jac);280CvMat tmp = cvMat(jac);281cvCopy(&tmp, _jac);282}283284if (_err)285{286calcError(err);287LOG_CHAT(".");288iter++;289CvMat tmp = cvMat(err);290cvCopy(&tmp, _err);291}292}293294LOGLN_CHAT("");295LOGLN_CHAT("Bundle adjustment, final RMS error: " << std::sqrt(err.dot(err) / total_num_matches_));296LOGLN_CHAT("Bundle adjustment, iterations done: " << iter);297298// Check if all camera parameters are valid299bool ok = true;300for (int i = 0; i < cam_params_.rows; ++i)301{302if (cvIsNaN(cam_params_.at<double>(i,0)))303{304ok = false;305break;306}307}308if (!ok)309return false;310311obtainRefinedCameraParams(cameras);312313// Normalize motion to center image314Graph span_tree;315std::vector<int> span_tree_centers;316findMaxSpanningTree(num_images_, pairwise_matches, span_tree, span_tree_centers);317Mat R_inv = cameras[span_tree_centers[0]].R.inv();318for (int i = 0; i < num_images_; ++i)319cameras[i].R = R_inv * cameras[i].R;320321LOGLN_CHAT("Bundle adjustment, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");322return true;323}324325326//////////////////////////////////////////////////////////////////////////////327328void BundleAdjusterReproj::setUpInitialCameraParams(const std::vector<CameraParams> &cameras)329{330cam_params_.create(num_images_ * 7, 1, CV_64F);331SVD svd;332for (int i = 0; i < num_images_; ++i)333{334cam_params_.at<double>(i * 7, 0) = cameras[i].focal;335cam_params_.at<double>(i * 7 + 1, 0) = cameras[i].ppx;336cam_params_.at<double>(i * 7 + 2, 0) = cameras[i].ppy;337cam_params_.at<double>(i * 7 + 3, 0) = cameras[i].aspect;338339svd(cameras[i].R, SVD::FULL_UV);340Mat R = svd.u * svd.vt;341if (determinant(R) < 0)342R *= -1;343344Mat rvec;345Rodrigues(R, rvec);346CV_Assert(rvec.type() == CV_32F);347cam_params_.at<double>(i * 7 + 4, 0) = rvec.at<float>(0, 0);348cam_params_.at<double>(i * 7 + 5, 0) = rvec.at<float>(1, 0);349cam_params_.at<double>(i * 7 + 6, 0) = rvec.at<float>(2, 0);350}351}352353354void BundleAdjusterReproj::obtainRefinedCameraParams(std::vector<CameraParams> &cameras) const355{356for (int i = 0; i < num_images_; ++i)357{358cameras[i].focal = cam_params_.at<double>(i * 7, 0);359cameras[i].ppx = cam_params_.at<double>(i * 7 + 1, 0);360cameras[i].ppy = cam_params_.at<double>(i * 7 + 2, 0);361cameras[i].aspect = cam_params_.at<double>(i * 7 + 3, 0);362363Mat rvec(3, 1, CV_64F);364rvec.at<double>(0, 0) = cam_params_.at<double>(i * 7 + 4, 0);365rvec.at<double>(1, 0) = cam_params_.at<double>(i * 7 + 5, 0);366rvec.at<double>(2, 0) = cam_params_.at<double>(i * 7 + 6, 0);367Rodrigues(rvec, cameras[i].R);368369Mat tmp;370cameras[i].R.convertTo(tmp, CV_32F);371cameras[i].R = tmp;372}373}374375376void BundleAdjusterReproj::calcError(Mat &err)377{378err.create(total_num_matches_ * 2, 1, CV_64F);379380int match_idx = 0;381for (size_t edge_idx = 0; edge_idx < edges_.size(); ++edge_idx)382{383int i = edges_[edge_idx].first;384int j = edges_[edge_idx].second;385double f1 = cam_params_.at<double>(i * 7, 0);386double f2 = cam_params_.at<double>(j * 7, 0);387double ppx1 = cam_params_.at<double>(i * 7 + 1, 0);388double ppx2 = cam_params_.at<double>(j * 7 + 1, 0);389double ppy1 = cam_params_.at<double>(i * 7 + 2, 0);390double ppy2 = cam_params_.at<double>(j * 7 + 2, 0);391double a1 = cam_params_.at<double>(i * 7 + 3, 0);392double a2 = cam_params_.at<double>(j * 7 + 3, 0);393394double R1[9];395Mat R1_(3, 3, CV_64F, R1);396Mat rvec(3, 1, CV_64F);397rvec.at<double>(0, 0) = cam_params_.at<double>(i * 7 + 4, 0);398rvec.at<double>(1, 0) = cam_params_.at<double>(i * 7 + 5, 0);399rvec.at<double>(2, 0) = cam_params_.at<double>(i * 7 + 6, 0);400Rodrigues(rvec, R1_);401402double R2[9];403Mat R2_(3, 3, CV_64F, R2);404rvec.at<double>(0, 0) = cam_params_.at<double>(j * 7 + 4, 0);405rvec.at<double>(1, 0) = cam_params_.at<double>(j * 7 + 5, 0);406rvec.at<double>(2, 0) = cam_params_.at<double>(j * 7 + 6, 0);407Rodrigues(rvec, R2_);408409const ImageFeatures& features1 = features_[i];410const ImageFeatures& features2 = features_[j];411const MatchesInfo& matches_info = pairwise_matches_[i * num_images_ + j];412413Mat_<double> K1 = Mat::eye(3, 3, CV_64F);414K1(0,0) = f1; K1(0,2) = ppx1;415K1(1,1) = f1*a1; K1(1,2) = ppy1;416417Mat_<double> K2 = Mat::eye(3, 3, CV_64F);418K2(0,0) = f2; K2(0,2) = ppx2;419K2(1,1) = f2*a2; K2(1,2) = ppy2;420421Mat_<double> H = K2 * R2_.inv() * R1_ * K1.inv();422423for (size_t k = 0; k < matches_info.matches.size(); ++k)424{425if (!matches_info.inliers_mask[k])426continue;427428const DMatch& m = matches_info.matches[k];429Point2f p1 = features1.keypoints[m.queryIdx].pt;430Point2f p2 = features2.keypoints[m.trainIdx].pt;431double x = H(0,0)*p1.x + H(0,1)*p1.y + H(0,2);432double y = H(1,0)*p1.x + H(1,1)*p1.y + H(1,2);433double z = H(2,0)*p1.x + H(2,1)*p1.y + H(2,2);434435err.at<double>(2 * match_idx, 0) = p2.x - x/z;436err.at<double>(2 * match_idx + 1, 0) = p2.y - y/z;437match_idx++;438}439}440}441442443void BundleAdjusterReproj::calcJacobian(Mat &jac)444{445jac.create(total_num_matches_ * 2, num_images_ * 7, CV_64F);446jac.setTo(0);447448double val;449const double step = 1e-4;450451for (int i = 0; i < num_images_; ++i)452{453if (refinement_mask_.at<uchar>(0, 0))454{455val = cam_params_.at<double>(i * 7, 0);456cam_params_.at<double>(i * 7, 0) = val - step;457calcError(err1_);458cam_params_.at<double>(i * 7, 0) = val + step;459calcError(err2_);460calcDeriv(err1_, err2_, 2 * step, jac.col(i * 7));461cam_params_.at<double>(i * 7, 0) = val;462}463if (refinement_mask_.at<uchar>(0, 2))464{465val = cam_params_.at<double>(i * 7 + 1, 0);466cam_params_.at<double>(i * 7 + 1, 0) = val - step;467calcError(err1_);468cam_params_.at<double>(i * 7 + 1, 0) = val + step;469calcError(err2_);470calcDeriv(err1_, err2_, 2 * step, jac.col(i * 7 + 1));471cam_params_.at<double>(i * 7 + 1, 0) = val;472}473if (refinement_mask_.at<uchar>(1, 2))474{475val = cam_params_.at<double>(i * 7 + 2, 0);476cam_params_.at<double>(i * 7 + 2, 0) = val - step;477calcError(err1_);478cam_params_.at<double>(i * 7 + 2, 0) = val + step;479calcError(err2_);480calcDeriv(err1_, err2_, 2 * step, jac.col(i * 7 + 2));481cam_params_.at<double>(i * 7 + 2, 0) = val;482}483if (refinement_mask_.at<uchar>(1, 1))484{485val = cam_params_.at<double>(i * 7 + 3, 0);486cam_params_.at<double>(i * 7 + 3, 0) = val - step;487calcError(err1_);488cam_params_.at<double>(i * 7 + 3, 0) = val + step;489calcError(err2_);490calcDeriv(err1_, err2_, 2 * step, jac.col(i * 7 + 3));491cam_params_.at<double>(i * 7 + 3, 0) = val;492}493for (int j = 4; j < 7; ++j)494{495val = cam_params_.at<double>(i * 7 + j, 0);496cam_params_.at<double>(i * 7 + j, 0) = val - step;497calcError(err1_);498cam_params_.at<double>(i * 7 + j, 0) = val + step;499calcError(err2_);500calcDeriv(err1_, err2_, 2 * step, jac.col(i * 7 + j));501cam_params_.at<double>(i * 7 + j, 0) = val;502}503}504}505506507//////////////////////////////////////////////////////////////////////////////508509void BundleAdjusterRay::setUpInitialCameraParams(const std::vector<CameraParams> &cameras)510{511cam_params_.create(num_images_ * 4, 1, CV_64F);512SVD svd;513for (int i = 0; i < num_images_; ++i)514{515cam_params_.at<double>(i * 4, 0) = cameras[i].focal;516517svd(cameras[i].R, SVD::FULL_UV);518Mat R = svd.u * svd.vt;519if (determinant(R) < 0)520R *= -1;521522Mat rvec;523Rodrigues(R, rvec);524CV_Assert(rvec.type() == CV_32F);525cam_params_.at<double>(i * 4 + 1, 0) = rvec.at<float>(0, 0);526cam_params_.at<double>(i * 4 + 2, 0) = rvec.at<float>(1, 0);527cam_params_.at<double>(i * 4 + 3, 0) = rvec.at<float>(2, 0);528}529}530531532void BundleAdjusterRay::obtainRefinedCameraParams(std::vector<CameraParams> &cameras) const533{534for (int i = 0; i < num_images_; ++i)535{536cameras[i].focal = cam_params_.at<double>(i * 4, 0);537538Mat rvec(3, 1, CV_64F);539rvec.at<double>(0, 0) = cam_params_.at<double>(i * 4 + 1, 0);540rvec.at<double>(1, 0) = cam_params_.at<double>(i * 4 + 2, 0);541rvec.at<double>(2, 0) = cam_params_.at<double>(i * 4 + 3, 0);542Rodrigues(rvec, cameras[i].R);543544Mat tmp;545cameras[i].R.convertTo(tmp, CV_32F);546cameras[i].R = tmp;547}548}549550551void BundleAdjusterRay::calcError(Mat &err)552{553err.create(total_num_matches_ * 3, 1, CV_64F);554555int match_idx = 0;556for (size_t edge_idx = 0; edge_idx < edges_.size(); ++edge_idx)557{558int i = edges_[edge_idx].first;559int j = edges_[edge_idx].second;560double f1 = cam_params_.at<double>(i * 4, 0);561double f2 = cam_params_.at<double>(j * 4, 0);562563double R1[9];564Mat R1_(3, 3, CV_64F, R1);565Mat rvec(3, 1, CV_64F);566rvec.at<double>(0, 0) = cam_params_.at<double>(i * 4 + 1, 0);567rvec.at<double>(1, 0) = cam_params_.at<double>(i * 4 + 2, 0);568rvec.at<double>(2, 0) = cam_params_.at<double>(i * 4 + 3, 0);569Rodrigues(rvec, R1_);570571double R2[9];572Mat R2_(3, 3, CV_64F, R2);573rvec.at<double>(0, 0) = cam_params_.at<double>(j * 4 + 1, 0);574rvec.at<double>(1, 0) = cam_params_.at<double>(j * 4 + 2, 0);575rvec.at<double>(2, 0) = cam_params_.at<double>(j * 4 + 3, 0);576Rodrigues(rvec, R2_);577578const ImageFeatures& features1 = features_[i];579const ImageFeatures& features2 = features_[j];580const MatchesInfo& matches_info = pairwise_matches_[i * num_images_ + j];581582Mat_<double> K1 = Mat::eye(3, 3, CV_64F);583K1(0,0) = f1; K1(0,2) = features1.img_size.width * 0.5;584K1(1,1) = f1; K1(1,2) = features1.img_size.height * 0.5;585586Mat_<double> K2 = Mat::eye(3, 3, CV_64F);587K2(0,0) = f2; K2(0,2) = features2.img_size.width * 0.5;588K2(1,1) = f2; K2(1,2) = features2.img_size.height * 0.5;589590Mat_<double> H1 = R1_ * K1.inv();591Mat_<double> H2 = R2_ * K2.inv();592593for (size_t k = 0; k < matches_info.matches.size(); ++k)594{595if (!matches_info.inliers_mask[k])596continue;597598const DMatch& m = matches_info.matches[k];599600Point2f p1 = features1.keypoints[m.queryIdx].pt;601double x1 = H1(0,0)*p1.x + H1(0,1)*p1.y + H1(0,2);602double y1 = H1(1,0)*p1.x + H1(1,1)*p1.y + H1(1,2);603double z1 = H1(2,0)*p1.x + H1(2,1)*p1.y + H1(2,2);604double len = std::sqrt(x1*x1 + y1*y1 + z1*z1);605x1 /= len; y1 /= len; z1 /= len;606607Point2f p2 = features2.keypoints[m.trainIdx].pt;608double x2 = H2(0,0)*p2.x + H2(0,1)*p2.y + H2(0,2);609double y2 = H2(1,0)*p2.x + H2(1,1)*p2.y + H2(1,2);610double z2 = H2(2,0)*p2.x + H2(2,1)*p2.y + H2(2,2);611len = std::sqrt(x2*x2 + y2*y2 + z2*z2);612x2 /= len; y2 /= len; z2 /= len;613614double mult = std::sqrt(f1 * f2);615err.at<double>(3 * match_idx, 0) = mult * (x1 - x2);616err.at<double>(3 * match_idx + 1, 0) = mult * (y1 - y2);617err.at<double>(3 * match_idx + 2, 0) = mult * (z1 - z2);618619match_idx++;620}621}622}623624625void BundleAdjusterRay::calcJacobian(Mat &jac)626{627jac.create(total_num_matches_ * 3, num_images_ * 4, CV_64F);628629double val;630const double step = 1e-3;631632for (int i = 0; i < num_images_; ++i)633{634for (int j = 0; j < 4; ++j)635{636val = cam_params_.at<double>(i * 4 + j, 0);637cam_params_.at<double>(i * 4 + j, 0) = val - step;638calcError(err1_);639cam_params_.at<double>(i * 4 + j, 0) = val + step;640calcError(err2_);641calcDeriv(err1_, err2_, 2 * step, jac.col(i * 4 + j));642cam_params_.at<double>(i * 4 + j, 0) = val;643}644}645}646647//////////////////////////////////////////////////////////////////////////////648649void BundleAdjusterAffine::setUpInitialCameraParams(const std::vector<CameraParams> &cameras)650{651cam_params_.create(num_images_ * 6, 1, CV_64F);652for (size_t i = 0; i < static_cast<size_t>(num_images_); ++i)653{654CV_Assert(cameras[i].R.type() == CV_32F);655// cameras[i].R is656// a b tx657// c d ty658// 0 0 1. (optional)659// cam_params_ model for LevMarq is660// (a, b, tx, c, d, ty)661Mat params (2, 3, CV_64F, cam_params_.ptr<double>() + i * 6);662cameras[i].R.rowRange(0, 2).convertTo(params, CV_64F);663}664}665666667void BundleAdjusterAffine::obtainRefinedCameraParams(std::vector<CameraParams> &cameras) const668{669for (int i = 0; i < num_images_; ++i)670{671// cameras[i].R will be672// a b tx673// c d ty674// 0 0 1675cameras[i].R = Mat::eye(3, 3, CV_32F);676Mat params = cam_params_.rowRange(i * 6, i * 6 + 6).reshape(1, 2);677params.convertTo(cameras[i].R.rowRange(0, 2), CV_32F);678}679}680681682void BundleAdjusterAffine::calcError(Mat &err)683{684err.create(total_num_matches_ * 2, 1, CV_64F);685686int match_idx = 0;687for (size_t edge_idx = 0; edge_idx < edges_.size(); ++edge_idx)688{689size_t i = edges_[edge_idx].first;690size_t j = edges_[edge_idx].second;691692const ImageFeatures& features1 = features_[i];693const ImageFeatures& features2 = features_[j];694const MatchesInfo& matches_info = pairwise_matches_[i * num_images_ + j];695696Mat H1 (2, 3, CV_64F, cam_params_.ptr<double>() + i * 6);697Mat H2 (2, 3, CV_64F, cam_params_.ptr<double>() + j * 6);698699// invert H1700Mat H1_inv;701invertAffineTransform(H1, H1_inv);702703// convert to representation in homogeneous coordinates704Mat last_row = Mat::zeros(1, 3, CV_64F);705last_row.at<double>(2) = 1.;706H1_inv.push_back(last_row);707H2.push_back(last_row);708709Mat_<double> H = H1_inv * H2;710711for (size_t k = 0; k < matches_info.matches.size(); ++k)712{713if (!matches_info.inliers_mask[k])714continue;715716const DMatch& m = matches_info.matches[k];717const Point2f& p1 = features1.keypoints[m.queryIdx].pt;718const Point2f& p2 = features2.keypoints[m.trainIdx].pt;719720double x = H(0,0)*p1.x + H(0,1)*p1.y + H(0,2);721double y = H(1,0)*p1.x + H(1,1)*p1.y + H(1,2);722723err.at<double>(2 * match_idx + 0, 0) = p2.x - x;724err.at<double>(2 * match_idx + 1, 0) = p2.y - y;725726++match_idx;727}728}729}730731732void BundleAdjusterAffine::calcJacobian(Mat &jac)733{734jac.create(total_num_matches_ * 2, num_images_ * 6, CV_64F);735736double val;737const double step = 1e-4;738739for (int i = 0; i < num_images_; ++i)740{741for (int j = 0; j < 6; ++j)742{743val = cam_params_.at<double>(i * 6 + j, 0);744cam_params_.at<double>(i * 6 + j, 0) = val - step;745calcError(err1_);746cam_params_.at<double>(i * 6 + j, 0) = val + step;747calcError(err2_);748calcDeriv(err1_, err2_, 2 * step, jac.col(i * 6 + j));749cam_params_.at<double>(i * 6 + j, 0) = val;750}751}752}753754755//////////////////////////////////////////////////////////////////////////////756757void BundleAdjusterAffinePartial::setUpInitialCameraParams(const std::vector<CameraParams> &cameras)758{759cam_params_.create(num_images_ * 4, 1, CV_64F);760for (size_t i = 0; i < static_cast<size_t>(num_images_); ++i)761{762CV_Assert(cameras[i].R.type() == CV_32F);763// cameras[i].R is764// a -b tx765// b a ty766// 0 0 1. (optional)767// cam_params_ model for LevMarq is768// (a, b, tx, ty)769double *params = cam_params_.ptr<double>() + i * 4;770params[0] = cameras[i].R.at<float>(0, 0);771params[1] = cameras[i].R.at<float>(1, 0);772params[2] = cameras[i].R.at<float>(0, 2);773params[3] = cameras[i].R.at<float>(1, 2);774}775}776777778void BundleAdjusterAffinePartial::obtainRefinedCameraParams(std::vector<CameraParams> &cameras) const779{780for (size_t i = 0; i < static_cast<size_t>(num_images_); ++i)781{782// cameras[i].R will be783// a -b tx784// b a ty785// 0 0 1786// cam_params_ model for LevMarq is787// (a, b, tx, ty)788const double *params = cam_params_.ptr<double>() + i * 4;789double transform_buf[9] =790{791params[0], -params[1], params[2],792params[1], params[0], params[3],7930., 0., 1.794};795Mat transform(3, 3, CV_64F, transform_buf);796transform.convertTo(cameras[i].R, CV_32F);797}798}799800801void BundleAdjusterAffinePartial::calcError(Mat &err)802{803err.create(total_num_matches_ * 2, 1, CV_64F);804805int match_idx = 0;806for (size_t edge_idx = 0; edge_idx < edges_.size(); ++edge_idx)807{808size_t i = edges_[edge_idx].first;809size_t j = edges_[edge_idx].second;810811const ImageFeatures& features1 = features_[i];812const ImageFeatures& features2 = features_[j];813const MatchesInfo& matches_info = pairwise_matches_[i * num_images_ + j];814815const double *H1_ptr = cam_params_.ptr<double>() + i * 4;816double H1_buf[9] =817{818H1_ptr[0], -H1_ptr[1], H1_ptr[2],819H1_ptr[1], H1_ptr[0], H1_ptr[3],8200., 0., 1.821};822Mat H1 (3, 3, CV_64F, H1_buf);823const double *H2_ptr = cam_params_.ptr<double>() + j * 4;824double H2_buf[9] =825{826H2_ptr[0], -H2_ptr[1], H2_ptr[2],827H2_ptr[1], H2_ptr[0], H2_ptr[3],8280., 0., 1.829};830Mat H2 (3, 3, CV_64F, H2_buf);831832// invert H1833Mat H1_aff (H1, Range(0, 2));834double H1_inv_buf[6];835Mat H1_inv (2, 3, CV_64F, H1_inv_buf);836invertAffineTransform(H1_aff, H1_inv);837H1_inv.copyTo(H1_aff);838839Mat_<double> H = H1 * H2;840841for (size_t k = 0; k < matches_info.matches.size(); ++k)842{843if (!matches_info.inliers_mask[k])844continue;845846const DMatch& m = matches_info.matches[k];847const Point2f& p1 = features1.keypoints[m.queryIdx].pt;848const Point2f& p2 = features2.keypoints[m.trainIdx].pt;849850double x = H(0,0)*p1.x + H(0,1)*p1.y + H(0,2);851double y = H(1,0)*p1.x + H(1,1)*p1.y + H(1,2);852853err.at<double>(2 * match_idx + 0, 0) = p2.x - x;854err.at<double>(2 * match_idx + 1, 0) = p2.y - y;855856++match_idx;857}858}859}860861862void BundleAdjusterAffinePartial::calcJacobian(Mat &jac)863{864jac.create(total_num_matches_ * 2, num_images_ * 4, CV_64F);865866double val;867const double step = 1e-4;868869for (int i = 0; i < num_images_; ++i)870{871for (int j = 0; j < 4; ++j)872{873val = cam_params_.at<double>(i * 4 + j, 0);874cam_params_.at<double>(i * 4 + j, 0) = val - step;875calcError(err1_);876cam_params_.at<double>(i * 4 + j, 0) = val + step;877calcError(err2_);878calcDeriv(err1_, err2_, 2 * step, jac.col(i * 4 + j));879cam_params_.at<double>(i * 4 + j, 0) = val;880}881}882}883884885//////////////////////////////////////////////////////////////////////////////886887void waveCorrect(std::vector<Mat> &rmats, WaveCorrectKind kind)888{889LOGLN("Wave correcting...");890#if ENABLE_LOG891int64 t = getTickCount();892#endif893if (rmats.size() <= 1)894{895LOGLN("Wave correcting, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");896return;897}898899Mat moment = Mat::zeros(3, 3, CV_32F);900for (size_t i = 0; i < rmats.size(); ++i)901{902Mat col = rmats[i].col(0);903moment += col * col.t();904}905Mat eigen_vals, eigen_vecs;906eigen(moment, eigen_vals, eigen_vecs);907908Mat rg1;909if (kind == WAVE_CORRECT_HORIZ)910rg1 = eigen_vecs.row(2).t();911else if (kind == WAVE_CORRECT_VERT)912rg1 = eigen_vecs.row(0).t();913else914CV_Error(CV_StsBadArg, "unsupported kind of wave correction");915916Mat img_k = Mat::zeros(3, 1, CV_32F);917for (size_t i = 0; i < rmats.size(); ++i)918img_k += rmats[i].col(2);919Mat rg0 = rg1.cross(img_k);920double rg0_norm = norm(rg0);921922if( rg0_norm <= DBL_MIN )923{924return;925}926927rg0 /= rg0_norm;928929Mat rg2 = rg0.cross(rg1);930931double conf = 0;932if (kind == WAVE_CORRECT_HORIZ)933{934for (size_t i = 0; i < rmats.size(); ++i)935conf += rg0.dot(rmats[i].col(0));936if (conf < 0)937{938rg0 *= -1;939rg1 *= -1;940}941}942else if (kind == WAVE_CORRECT_VERT)943{944for (size_t i = 0; i < rmats.size(); ++i)945conf -= rg1.dot(rmats[i].col(0));946if (conf < 0)947{948rg0 *= -1;949rg1 *= -1;950}951}952953Mat R = Mat::zeros(3, 3, CV_32F);954Mat tmp = R.row(0);955Mat(rg0.t()).copyTo(tmp);956tmp = R.row(1);957Mat(rg1.t()).copyTo(tmp);958tmp = R.row(2);959Mat(rg2.t()).copyTo(tmp);960961for (size_t i = 0; i < rmats.size(); ++i)962rmats[i] = R * rmats[i];963964LOGLN("Wave correcting, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");965}966967968//////////////////////////////////////////////////////////////////////////////969970String matchesGraphAsString(std::vector<String> &pathes, std::vector<MatchesInfo> &pairwise_matches,971float conf_threshold)972{973std::stringstream str;974str << "graph matches_graph{\n";975976const int num_images = static_cast<int>(pathes.size());977std::set<std::pair<int,int> > span_tree_edges;978DisjointSets comps(num_images);979980for (int i = 0; i < num_images; ++i)981{982for (int j = 0; j < num_images; ++j)983{984if (pairwise_matches[i*num_images + j].confidence < conf_threshold)985continue;986int comp1 = comps.findSetByElem(i);987int comp2 = comps.findSetByElem(j);988if (comp1 != comp2)989{990comps.mergeSets(comp1, comp2);991span_tree_edges.insert(std::make_pair(i, j));992}993}994}995996for (std::set<std::pair<int,int> >::const_iterator itr = span_tree_edges.begin();997itr != span_tree_edges.end(); ++itr)998{999std::pair<int,int> edge = *itr;1000if (span_tree_edges.find(edge) != span_tree_edges.end())1001{1002String name_src = pathes[edge.first];1003size_t prefix_len = name_src.find_last_of("/\\");1004if (prefix_len != String::npos) prefix_len++; else prefix_len = 0;1005name_src = name_src.substr(prefix_len, name_src.size() - prefix_len);10061007String name_dst = pathes[edge.second];1008prefix_len = name_dst.find_last_of("/\\");1009if (prefix_len != String::npos) prefix_len++; else prefix_len = 0;1010name_dst = name_dst.substr(prefix_len, name_dst.size() - prefix_len);10111012int pos = edge.first*num_images + edge.second;1013str << "\"" << name_src.c_str() << "\" -- \"" << name_dst.c_str() << "\""1014<< "[label=\"Nm=" << pairwise_matches[pos].matches.size()1015<< ", Ni=" << pairwise_matches[pos].num_inliers1016<< ", C=" << pairwise_matches[pos].confidence << "\"];\n";1017}1018}10191020for (size_t i = 0; i < comps.size.size(); ++i)1021{1022if (comps.size[comps.findSetByElem((int)i)] == 1)1023{1024String name = pathes[i];1025size_t prefix_len = name.find_last_of("/\\");1026if (prefix_len != String::npos) prefix_len++; else prefix_len = 0;1027name = name.substr(prefix_len, name.size() - prefix_len);1028str << "\"" << name.c_str() << "\";\n";1029}1030}10311032str << "}";1033return str.str().c_str();1034}10351036std::vector<int> leaveBiggestComponent(std::vector<ImageFeatures> &features, std::vector<MatchesInfo> &pairwise_matches,1037float conf_threshold)1038{1039const int num_images = static_cast<int>(features.size());10401041DisjointSets comps(num_images);1042for (int i = 0; i < num_images; ++i)1043{1044for (int j = 0; j < num_images; ++j)1045{1046if (pairwise_matches[i*num_images + j].confidence < conf_threshold)1047continue;1048int comp1 = comps.findSetByElem(i);1049int comp2 = comps.findSetByElem(j);1050if (comp1 != comp2)1051comps.mergeSets(comp1, comp2);1052}1053}10541055int max_comp = static_cast<int>(std::max_element(comps.size.begin(), comps.size.end()) - comps.size.begin());10561057std::vector<int> indices;1058std::vector<int> indices_removed;1059for (int i = 0; i < num_images; ++i)1060if (comps.findSetByElem(i) == max_comp)1061indices.push_back(i);1062else1063indices_removed.push_back(i);10641065std::vector<ImageFeatures> features_subset;1066std::vector<MatchesInfo> pairwise_matches_subset;1067for (size_t i = 0; i < indices.size(); ++i)1068{1069features_subset.push_back(features[indices[i]]);1070for (size_t j = 0; j < indices.size(); ++j)1071{1072pairwise_matches_subset.push_back(pairwise_matches[indices[i]*num_images + indices[j]]);1073pairwise_matches_subset.back().src_img_idx = static_cast<int>(i);1074pairwise_matches_subset.back().dst_img_idx = static_cast<int>(j);1075}1076}10771078if (static_cast<int>(features_subset.size()) == num_images)1079return indices;10801081LOG("Removed some images, because can't match them or there are too similar images: (");1082LOG(indices_removed[0] + 1);1083for (size_t i = 1; i < indices_removed.size(); ++i)1084LOG(", " << indices_removed[i]+1);1085LOGLN(").");1086LOGLN("Try to decrease the match confidence threshold and/or check if you're stitching duplicates.");10871088features = features_subset;1089pairwise_matches = pairwise_matches_subset;10901091return indices;1092}109310941095void findMaxSpanningTree(int num_images, const std::vector<MatchesInfo> &pairwise_matches,1096Graph &span_tree, std::vector<int> ¢ers)1097{1098Graph graph(num_images);1099std::vector<GraphEdge> edges;11001101// Construct images graph and remember its edges1102for (int i = 0; i < num_images; ++i)1103{1104for (int j = 0; j < num_images; ++j)1105{1106if (pairwise_matches[i * num_images + j].H.empty())1107continue;1108float conf = static_cast<float>(pairwise_matches[i * num_images + j].num_inliers);1109graph.addEdge(i, j, conf);1110edges.push_back(GraphEdge(i, j, conf));1111}1112}11131114DisjointSets comps(num_images);1115span_tree.create(num_images);1116std::vector<int> span_tree_powers(num_images, 0);11171118// Find maximum spanning tree1119sort(edges.begin(), edges.end(), std::greater<GraphEdge>());1120for (size_t i = 0; i < edges.size(); ++i)1121{1122int comp1 = comps.findSetByElem(edges[i].from);1123int comp2 = comps.findSetByElem(edges[i].to);1124if (comp1 != comp2)1125{1126comps.mergeSets(comp1, comp2);1127span_tree.addEdge(edges[i].from, edges[i].to, edges[i].weight);1128span_tree.addEdge(edges[i].to, edges[i].from, edges[i].weight);1129span_tree_powers[edges[i].from]++;1130span_tree_powers[edges[i].to]++;1131}1132}11331134// Find spanning tree leafs1135std::vector<int> span_tree_leafs;1136for (int i = 0; i < num_images; ++i)1137if (span_tree_powers[i] == 1)1138span_tree_leafs.push_back(i);11391140// Find maximum distance from each spanning tree vertex1141std::vector<int> max_dists(num_images, 0);1142std::vector<int> cur_dists;1143for (size_t i = 0; i < span_tree_leafs.size(); ++i)1144{1145cur_dists.assign(num_images, 0);1146span_tree.walkBreadthFirst(span_tree_leafs[i], IncDistance(cur_dists));1147for (int j = 0; j < num_images; ++j)1148max_dists[j] = std::max(max_dists[j], cur_dists[j]);1149}11501151// Find min-max distance1152int min_max_dist = max_dists[0];1153for (int i = 1; i < num_images; ++i)1154if (min_max_dist > max_dists[i])1155min_max_dist = max_dists[i];11561157// Find spanning tree centers1158centers.clear();1159for (int i = 0; i < num_images; ++i)1160if (max_dists[i] == min_max_dist)1161centers.push_back(i);1162CV_Assert(centers.size() > 0 && centers.size() <= 2);1163}11641165} // namespace detail1166} // namespace cv116711681169