static void PointsToMat( const IntrinsicBase * cam, const PointFeatures & vec_feats, MatT & m) { m.resize(2, vec_feats.size()); typedef typename MatT::Scalar Scalar; // Output matrix type size_t i = 0; for( PointFeatures::const_iterator iter = vec_feats.begin(); iter != vec_feats.end(); ++iter, ++i) { m.col(i) = cam->get_ud_pixel(Vec2(iter->x(), iter->y())); } }
int main() { const std::string sInputDir = stlplus::folder_up(string(THIS_SOURCE_DIR)) + "/imageData/SceauxCastle/"; Image<RGBColor> image; const string jpg_filenameL = sInputDir + "100_7101.jpg"; const string jpg_filenameR = sInputDir + "100_7102.jpg"; Image<unsigned char> imageL, imageR; ReadImage(jpg_filenameL.c_str(), &imageL); ReadImage(jpg_filenameR.c_str(), &imageR); //-- // Detect regions thanks to an image_describer //-- using namespace openMVG::features; std::unique_ptr<Image_describer> image_describer(new SIFT_Image_describer); std::map<IndexT, std::unique_ptr<features::Regions> > regions_perImage; image_describer->Describe(imageL, regions_perImage[0]); image_describer->Describe(imageR, regions_perImage[1]); const SIFT_Regions* regionsL = dynamic_cast<SIFT_Regions*>(regions_perImage.at(0).get()); const SIFT_Regions* regionsR = dynamic_cast<SIFT_Regions*>(regions_perImage.at(1).get()); const PointFeatures featsL = regions_perImage.at(0)->GetRegionsPositions(), featsR = regions_perImage.at(1)->GetRegionsPositions(); // Show both images side by side { Image<unsigned char> concat; ConcatH(imageL, imageR, concat); string out_filename = "01_concat.jpg"; WriteImage(out_filename.c_str(), concat); } //- Draw features on the two image (side by side) { Image<unsigned char> concat; ConcatH(imageL, imageR, concat); //-- Draw features : for (size_t i=0; i < featsL.size(); ++i ) { const SIOPointFeature point = regionsL->Features()[i]; DrawCircle(point.x(), point.y(), point.scale(), 255, &concat); } for (size_t i=0; i < featsR.size(); ++i ) { const SIOPointFeature point = regionsR->Features()[i]; DrawCircle(point.x()+imageL.Width(), point.y(), point.scale(), 255, &concat); } string out_filename = "02_features.jpg"; WriteImage(out_filename.c_str(), concat); } std::vector<IndMatch> vec_PutativeMatches; //-- Perform matching -> find Nearest neighbor, filtered with Distance ratio { // Define a matcher and a metric to find corresponding points typedef SIFT_Regions::DescriptorT DescriptorT; typedef L2_Vectorized<DescriptorT::bin_type> Metric; typedef ArrayMatcherBruteForce<DescriptorT::bin_type, Metric> MatcherT; // Distance ratio squared due to squared metric getPutativesMatches<DescriptorT, MatcherT>( ((SIFT_Regions*)regions_perImage.at(0).get())->Descriptors(), ((SIFT_Regions*)regions_perImage.at(1).get())->Descriptors(), Square(0.8), vec_PutativeMatches); // Draw correspondences after Nearest Neighbor ratio filter svgDrawer svgStream( imageL.Width() + imageR.Width(), max(imageL.Height(), imageR.Height())); svgStream.drawImage(jpg_filenameL, imageL.Width(), imageL.Height()); svgStream.drawImage(jpg_filenameR, imageR.Width(), imageR.Height(), imageL.Width()); for (size_t i = 0; i < vec_PutativeMatches.size(); ++i) { //Get back linked feature, draw a circle and link them by a line const SIOPointFeature L = regionsL->Features()[vec_PutativeMatches[i]._i]; const SIOPointFeature R = regionsR->Features()[vec_PutativeMatches[i]._j]; svgStream.drawLine(L.x(), L.y(), R.x()+imageL.Width(), R.y(), svgStyle().stroke("green", 2.0)); svgStream.drawCircle(L.x(), L.y(), L.scale(), svgStyle().stroke("yellow", 2.0)); svgStream.drawCircle(R.x()+imageL.Width(), R.y(), R.scale(),svgStyle().stroke("yellow", 2.0)); } const string out_filename = "03_siftMatches.svg"; ofstream svgFile( out_filename.c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); } // Fundamental geometry filtering of putative matches { //A. get back interest point and send it to the robust estimation framework Mat xL(2, vec_PutativeMatches.size()); Mat xR(2, vec_PutativeMatches.size()); for (size_t k = 0; k < vec_PutativeMatches.size(); ++k) { const PointFeature & imaL = featsL[vec_PutativeMatches[k]._i]; const PointFeature & imaR = featsR[vec_PutativeMatches[k]._j]; xL.col(k) = imaL.coords().cast<double>(); xR.col(k) = imaR.coords().cast<double>(); } //-- Fundamental robust estimation std::vector<size_t> vec_inliers; typedef ACKernelAdaptor< openMVG::fundamental::kernel::SevenPointSolver, openMVG::fundamental::kernel::SymmetricEpipolarDistanceError, UnnormalizerT, Mat3> KernelType; KernelType kernel( xL, imageL.Width(), imageL.Height(), xR, imageR.Width(), imageR.Height(), true); // configure as point to line error model. Mat3 F; std::pair<double,double> ACRansacOut = ACRANSAC(kernel, vec_inliers, 1024, &F, 4.0, // Upper bound of authorized threshold true); const double & thresholdF = ACRansacOut.first; // Check the fundamental support some point to be considered as valid if (vec_inliers.size() > KernelType::MINIMUM_SAMPLES *2.5) { std::cout << "\nFound a fundamental under the confidence threshold of: " << thresholdF << " pixels\n\twith: " << vec_inliers.size() << " inliers" << " from: " << vec_PutativeMatches.size() << " putatives correspondences" << std::endl; //Show fundamental validated point and compute residuals std::vector<double> vec_residuals(vec_inliers.size(), 0.0); svgDrawer svgStream( imageL.Width() + imageR.Width(), max(imageL.Height(), imageR.Height())); svgStream.drawImage(jpg_filenameL, imageL.Width(), imageL.Height()); svgStream.drawImage(jpg_filenameR, imageR.Width(), imageR.Height(), imageL.Width()); for ( size_t i = 0; i < vec_inliers.size(); ++i) { const SIOPointFeature & LL = regionsL->Features()[vec_PutativeMatches[vec_inliers[i]]._i]; const SIOPointFeature & RR = regionsR->Features()[vec_PutativeMatches[vec_inliers[i]]._j]; const Vec2f L = LL.coords(); const Vec2f R = RR.coords(); svgStream.drawLine(L.x(), L.y(), R.x()+imageL.Width(), R.y(), svgStyle().stroke("green", 2.0)); svgStream.drawCircle(L.x(), L.y(), LL.scale(), svgStyle().stroke("yellow", 2.0)); svgStream.drawCircle(R.x()+imageL.Width(), R.y(), RR.scale(),svgStyle().stroke("yellow", 2.0)); // residual computation vec_residuals[i] = std::sqrt(KernelType::ErrorT::Error(F, LL.coords().cast<double>(), RR.coords().cast<double>())); } const string out_filename = "04_ACRansacFundamental.svg"; ofstream svgFile( out_filename.c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); // Display some statistics of reprojection errors float dMin, dMax, dMean, dMedian; minMaxMeanMedian<float>(vec_residuals.begin(), vec_residuals.end(), dMin, dMax, dMean, dMedian); std::cout << std::endl << "Fundamental matrix estimation, residuals statistics:" << "\n" << "\t-- Residual min:\t" << dMin << std::endl << "\t-- Residual median:\t" << dMedian << std::endl << "\t-- Residual max:\t " << dMax << std::endl << "\t-- Residual mean:\t " << dMean << std::endl; } else { std::cout << "ACRANSAC was unable to estimate a rigid fundamental" << std::endl; } } return EXIT_SUCCESS; }
int main() { std::cout << "Compute the relative pose between two spherical image." << "\nUse an Acontrario robust estimation based on angular errors." << std::endl; const std::string sInputDir = std::string(THIS_SOURCE_DIR); const string jpg_filenameL = sInputDir + "/SponzaLion000.jpg"; Image<unsigned char> imageL; ReadImage(jpg_filenameL.c_str(), &imageL); const string jpg_filenameR = sInputDir + "/SponzaLion001.jpg"; Image<unsigned char> imageR; ReadImage(jpg_filenameR.c_str(), &imageR); //-- // Detect regions thanks to an image_describer //-- using namespace openMVG::features; std::unique_ptr<Image_describer> image_describer(new SIFT_Image_describer(SiftParams(-1))); std::map<IndexT, std::unique_ptr<features::Regions> > regions_perImage; image_describer->Describe(imageL, regions_perImage[0]); image_describer->Describe(imageR, regions_perImage[1]); const SIFT_Regions* regionsL = dynamic_cast<SIFT_Regions*>(regions_perImage.at(0).get()); const SIFT_Regions* regionsR = dynamic_cast<SIFT_Regions*>(regions_perImage.at(1).get()); const PointFeatures featsL = regions_perImage.at(0)->GetRegionsPositions(), featsR = regions_perImage.at(1)->GetRegionsPositions(); std::cout << "Left image SIFT count: " << featsL.size() << std::endl; std::cout << "Right image SIFT count: "<< featsR.size() << std::endl; // Show both images side by side { Image<unsigned char> concat; ConcatH(imageL, imageR, concat); string out_filename = "01_concat.jpg"; WriteImage(out_filename.c_str(), concat); } //- Draw features on the two image (side by side) { Image<unsigned char> concat; ConcatH(imageL, imageR, concat); //-- Draw features : for (size_t i=0; i < featsL.size(); ++i ) { const SIOPointFeature point = regionsL->Features()[i]; DrawCircle(point.x(), point.y(), point.scale(), 255, &concat); } for (size_t i=0; i < featsR.size(); ++i ) { const SIOPointFeature point = regionsR->Features()[i]; DrawCircle(point.x()+imageL.Width(), point.y(), point.scale(), 255, &concat); } string out_filename = "02_features.jpg"; WriteImage(out_filename.c_str(), concat); } std::vector<IndMatch> vec_PutativeMatches; //-- Perform matching -> find Nearest neighbor, filtered with Distance ratio { // Find corresponding points matching::DistanceRatioMatch( 0.8, matching::ANN_L2, *regions_perImage.at(0).get(), *regions_perImage.at(1).get(), vec_PutativeMatches); IndMatchDecorator<float> matchDeduplicator(vec_PutativeMatches, featsL, featsR); matchDeduplicator.getDeduplicated(vec_PutativeMatches); // Draw correspondences after Nearest Neighbor ratio filter svgDrawer svgStream( imageL.Width() + imageR.Width(), max(imageL.Height(), imageR.Height())); svgStream.drawImage(jpg_filenameL, imageL.Width(), imageL.Height()); svgStream.drawImage(jpg_filenameR, imageR.Width(), imageR.Height(), imageL.Width()); for (size_t i = 0; i < vec_PutativeMatches.size(); ++i) { //Get back linked feature, draw a circle and link them by a line const SIOPointFeature L = regionsL->Features()[vec_PutativeMatches[i].i_]; const SIOPointFeature R = regionsR->Features()[vec_PutativeMatches[i].j_]; svgStream.drawLine(L.x(), L.y(), R.x()+imageL.Width(), R.y(), svgStyle().stroke("green", 2.0)); svgStream.drawCircle(L.x(), L.y(), L.scale(), svgStyle().stroke("yellow", 2.0)); svgStream.drawCircle(R.x()+imageL.Width(), R.y(), R.scale(),svgStyle().stroke("yellow", 2.0)); } string out_filename = "03_siftMatches.svg"; ofstream svgFile( out_filename.c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); } // Essential geometry filtering of putative matches { //A. get back interest point and send it to the robust estimation framework Mat xL(2, vec_PutativeMatches.size()); Mat xR(2, vec_PutativeMatches.size()); for (size_t k = 0; k < vec_PutativeMatches.size(); ++k) { const PointFeature & imaL = featsL[vec_PutativeMatches[k].i_]; const PointFeature & imaR = featsR[vec_PutativeMatches[k].j_]; xL.col(k) = imaL.coords().cast<double>(); xR.col(k) = imaR.coords().cast<double>(); } //-- Convert planar to spherical coordinates Mat xL_spherical(3,vec_PutativeMatches.size()), xR_spherical(3,vec_PutativeMatches.size()); spherical_cam::planarToSpherical(xL, imageL.Width(), imageL.Height(), xL_spherical); spherical_cam::planarToSpherical(xR, imageR.Width(), imageR.Height(), xR_spherical); //-- Essential matrix robust estimation from spherical bearing vectors { std::vector<size_t> vec_inliers; // Use the 8 point solver in order to estimate E typedef openMVG::spherical_cam::EssentialKernel_spherical Kernel; // Define the AContrario angular error adaptor typedef openMVG::robust::ACKernelAdaptor_AngularRadianError< openMVG::spherical_cam::EightPointRelativePoseSolver, openMVG::spherical_cam::AngularError, Mat3> KernelType; KernelType kernel(xL_spherical, xR_spherical); // Robust estimation of the Essential matrix and it's precision Mat3 E; const double precision = std::numeric_limits<double>::infinity(); const std::pair<double,double> ACRansacOut = ACRANSAC(kernel, vec_inliers, 1024, &E, precision, true); const double & threshold = ACRansacOut.first; const double & NFA = ACRansacOut.second; std::cout << "\n Angular threshold found: " << R2D(threshold) << "(Degree)"<<std::endl; std::cout << "\n #Putatives/#inliers : " << xL_spherical.cols() << "/" << vec_inliers.size() << "\n" << std::endl; if (vec_inliers.size() > 120) { // If an essential matrix have been found // Extract R|t // - From the 4 possible solutions extracted from E keep the best // - (check cheirality of correspondence once triangulation is done) // Accumulator to find the best solution std::vector<size_t> f(4, 0); std::vector<Mat3> Es; // Essential, std::vector<Mat3> Rs; // Rotation matrix. std::vector<Vec3> ts; // Translation matrix. Es.push_back(E); // Recover best rotation and translation from E. MotionFromEssential(E, &Rs, &ts); //-> Test the 4 solutions will all the point Mat34 P1; P_From_KRt(Mat3::Identity(), Mat3::Identity(), Vec3::Zero(), &P1); std::vector< std::vector<size_t> > vec_newInliers(4); std::vector< std::vector<Vec3> > vec_3D(4); for (int kk = 0; kk < 4; ++kk) { const Mat3 &R2 = Rs[kk]; const Vec3 &t2 = ts[kk]; Mat34 P2; P_From_KRt(Mat3::Identity(), R2, t2, &P2); //-- For each inlier: // - triangulate // - check chierality for (size_t k = 0; k < vec_inliers.size(); ++k) { const Vec3 & x1_ = xL_spherical.col(vec_inliers[k]); const Vec3 & x2_ = xR_spherical.col(vec_inliers[k]); //Triangulate Vec3 X; openMVG::spherical_cam::TriangulateDLT(P1, x1_, P2, x2_, &X); //Check positivity of the depth (sign of the dot product) const Vec3 Mc = R2 * X + t2; if (x2_.dot(Mc) > 0 && x1_.dot(X) > 0) { ++f[kk]; vec_newInliers[kk].push_back(vec_inliers[k]); vec_3D[kk].push_back(X); } } } std::cout << std::endl << "estimate_Rt_fromE" << std::endl; std::cout << " #points in front of both cameras for each solution: " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << std::endl; std::vector<size_t>::iterator iter = max_element(f.begin(), f.end()); if(*iter != 0) { const size_t index = std::distance(f.begin(),iter); if (f[index] < 120) { std::cout << "Found solution have too few 3D points." << std::endl; } else { std::cout << "Export found 3D scene in current directory." << std::endl; vec_inliers.clear(); vec_inliers = vec_newInliers[index]; std::ostringstream os; os << "./" << "relativePose_Spherical"<< ".ply"; plyHelper::exportToPly(vec_3D[index], os.str()); } } } } } return EXIT_SUCCESS; }
int main() { Image<RGBColor> image; const string jpg_filenameL = stlplus::folder_up(string(THIS_SOURCE_DIR)) + "/imageData/StanfordMobileVisualSearch/Ace_0.png"; const string jpg_filenameR = stlplus::folder_up(string(THIS_SOURCE_DIR)) + "/imageData/StanfordMobileVisualSearch/Ace_1.png"; Image<unsigned char> imageL, imageR; ReadImage(jpg_filenameL.c_str(), &imageL); ReadImage(jpg_filenameR.c_str(), &imageR); //-- // Detect regions thanks to an image_describer //-- using namespace openMVG::features; std::unique_ptr<Image_describer> image_describer(new SIFT_Image_describer(SiftParams(-1))); std::map<IndexT, std::unique_ptr<features::Regions> > regions_perImage; image_describer->Describe(imageL, regions_perImage[0]); image_describer->Describe(imageR, regions_perImage[1]); const SIFT_Regions* regionsL = dynamic_cast<SIFT_Regions*>(regions_perImage.at(0).get()); const SIFT_Regions* regionsR = dynamic_cast<SIFT_Regions*>(regions_perImage.at(1).get()); const PointFeatures featsL = regions_perImage.at(0)->GetRegionsPositions(), featsR = regions_perImage.at(1)->GetRegionsPositions(); // Show both images side by side { Image<unsigned char> concat; ConcatH(imageL, imageR, concat); string out_filename = "01_concat.jpg"; WriteImage(out_filename.c_str(), concat); } //- Draw features on the two image (side by side) { Image<unsigned char> concat; ConcatH(imageL, imageR, concat); //-- Draw features : for (size_t i=0; i < featsL.size(); ++i ) { const SIOPointFeature point = regionsL->Features()[i]; DrawCircle(point.x(), point.y(), point.scale(), 255, &concat); } for (size_t i=0; i < featsR.size(); ++i ) { const SIOPointFeature point = regionsR->Features()[i]; DrawCircle(point.x()+imageL.Width(), point.y(), point.scale(), 255, &concat); } string out_filename = "02_features.jpg"; WriteImage(out_filename.c_str(), concat); } std::vector<IndMatch> vec_PutativeMatches; //-- Perform matching -> find Nearest neighbor, filtered with Distance ratio { // Find corresponding points matching::DistanceRatioMatch( 0.8, matching::BRUTE_FORCE_L2, *regions_perImage.at(0).get(), *regions_perImage.at(1).get(), vec_PutativeMatches); // Draw correspondences after Nearest Neighbor ratio filter svgDrawer svgStream( imageL.Width() + imageR.Width(), max(imageL.Height(), imageR.Height())); svgStream.drawImage(jpg_filenameL, imageL.Width(), imageL.Height()); svgStream.drawImage(jpg_filenameR, imageR.Width(), imageR.Height(), imageL.Width()); for (size_t i = 0; i < vec_PutativeMatches.size(); ++i) { //Get back linked feature, draw a circle and link them by a line const SIOPointFeature L = regionsL->Features()[vec_PutativeMatches[i]._i]; const SIOPointFeature R = regionsR->Features()[vec_PutativeMatches[i]._j]; svgStream.drawLine(L.x(), L.y(), R.x()+imageL.Width(), R.y(), svgStyle().stroke("green", 2.0)); svgStream.drawCircle(L.x(), L.y(), L.scale(), svgStyle().stroke("yellow", 2.0)); svgStream.drawCircle(R.x()+imageL.Width(), R.y(), R.scale(),svgStyle().stroke("yellow", 2.0)); } const std::string out_filename = "03_siftMatches.svg"; std::ofstream svgFile( out_filename.c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); } // Homography geometry filtering of putative matches { //A. get back interest point and send it to the robust estimation framework Mat xL(2, vec_PutativeMatches.size()); Mat xR(2, vec_PutativeMatches.size()); for (size_t k = 0; k < vec_PutativeMatches.size(); ++k) { const PointFeature & imaL = featsL[vec_PutativeMatches[k]._i]; const PointFeature & imaR = featsR[vec_PutativeMatches[k]._j]; xL.col(k) = imaL.coords().cast<double>(); xR.col(k) = imaR.coords().cast<double>(); } //-- Homography robust estimation std::vector<size_t> vec_inliers; typedef ACKernelAdaptor< openMVG::homography::kernel::FourPointSolver, openMVG::homography::kernel::AsymmetricError, UnnormalizerI, Mat3> KernelType; KernelType kernel( xL, imageL.Width(), imageL.Height(), xR, imageR.Width(), imageR.Height(), false); // configure as point to point error model. Mat3 H; std::pair<double,double> ACRansacOut = ACRANSAC(kernel, vec_inliers, 1024, &H, std::numeric_limits<double>::infinity(), true); const double & thresholdH = ACRansacOut.first; // Check the homography support some point to be considered as valid if (vec_inliers.size() > KernelType::MINIMUM_SAMPLES *2.5) { std::cout << "\nFound a homography under the confidence threshold of: " << thresholdH << " pixels\n\twith: " << vec_inliers.size() << " inliers" << " from: " << vec_PutativeMatches.size() << " putatives correspondences" << std::endl; //Show homography validated point and compute residuals std::vector<double> vec_residuals(vec_inliers.size(), 0.0); svgDrawer svgStream( imageL.Width() + imageR.Width(), max(imageL.Height(), imageR.Height())); svgStream.drawImage(jpg_filenameL, imageL.Width(), imageL.Height()); svgStream.drawImage(jpg_filenameR, imageR.Width(), imageR.Height(), imageL.Width()); for ( size_t i = 0; i < vec_inliers.size(); ++i) { const SIOPointFeature & LL = regionsL->Features()[vec_PutativeMatches[vec_inliers[i]]._i]; const SIOPointFeature & RR = regionsR->Features()[vec_PutativeMatches[vec_inliers[i]]._j]; const Vec2f L = LL.coords(); const Vec2f R = RR.coords(); svgStream.drawLine(L.x(), L.y(), R.x()+imageL.Width(), R.y(), svgStyle().stroke("green", 2.0)); svgStream.drawCircle(L.x(), L.y(), LL.scale(), svgStyle().stroke("yellow", 2.0)); svgStream.drawCircle(R.x()+imageL.Width(), R.y(), RR.scale(),svgStyle().stroke("yellow", 2.0)); // residual computation vec_residuals[i] = std::sqrt(KernelType::ErrorT::Error(H, LL.coords().cast<double>(), RR.coords().cast<double>())); } string out_filename = "04_ACRansacHomography.svg"; ofstream svgFile( out_filename.c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); // Display some statistics of reprojection errors float dMin, dMax, dMean, dMedian; minMaxMeanMedian<float>(vec_residuals.begin(), vec_residuals.end(), dMin, dMax, dMean, dMedian); std::cout << std::endl << "Homography matrix estimation, residuals statistics:" << "\n" << "\t-- Residual min:\t" << dMin << std::endl << "\t-- Residual median:\t" << dMedian << std::endl << "\t-- Residual max:\t " << dMax << std::endl << "\t-- Residual mean:\t " << dMean << std::endl; //--------------------------------------- // Warp the images to fit the reference view //--------------------------------------- // reread right image that will be warped to fit left image ReadImage(jpg_filenameR.c_str(), &image); WriteImage("query.png", image); // Create and fill the output image Image<RGBColor> imaOut(imageL.Width(), imageL.Height()); image::Warp(image, H, imaOut); const std::string imageNameOut = "query_warped.png"; WriteImage(imageNameOut.c_str(), imaOut); } else { std::cout << "ACRANSAC was unable to estimate a rigid homography" << std::endl; } } return EXIT_SUCCESS; }
int main(int argc, char **argv) { CmdLine cmd; std::string sImg1 = stlplus::folder_up(string(THIS_SOURCE_DIR)) + "/imageData/StanfordMobileVisualSearch/Ace_0.png"; std::string sImg2 = stlplus::folder_up(string(THIS_SOURCE_DIR)) + "/imageData/StanfordMobileVisualSearch/Ace_1.png"; std::string sOutDir = "./kvldOut"; std::cout << sImg1 << std::endl << sImg2 << std::endl; cmd.add( make_option('i', sImg1, "img1") ); cmd.add( make_option('j', sImg2, "img2") ); cmd.add( make_option('o', sOutDir, "outdir") ); if (argc > 1) { try { if (argc == 1) throw std::string("Invalid command line parameter."); cmd.process(argc, argv); } catch(const std::string& s) { std::cerr << "Usage: " << argv[0] << ' ' << "[-i|--img1 file] " << "[-j|--img2 file] " << "[-o|--outdir path] " << std::endl; std::cerr << s << std::endl; return EXIT_FAILURE; } } std::cout << " You called : " <<std::endl << argv[0] << std::endl << "--img1 " << sImg1 << std::endl << "--img2 " << sImg2 << std::endl << "--outdir " << sOutDir << std::endl; if (sOutDir.empty()) { std::cerr << "\nIt is an invalid output directory" << std::endl; return EXIT_FAILURE; } // ----------------------------- // a. List images // b. Compute features and descriptor // c. Compute putatives descriptor matches // d. Geometric filtering of putatives matches // e. Export some statistics // ----------------------------- // Create output dir if (!stlplus::folder_exists(sOutDir)) stlplus::folder_create( sOutDir ); const string jpg_filenameL = sImg1; const string jpg_filenameR = sImg2; Image<unsigned char> imageL, imageR; ReadImage(jpg_filenameL.c_str(), &imageL); ReadImage(jpg_filenameR.c_str(), &imageR); //-- // Detect regions thanks to an image_describer //-- using namespace openMVG::features; std::unique_ptr<Image_describer> image_describer(new SIFT_Image_describer(SiftParams(-1))); std::map<IndexT, std::unique_ptr<features::Regions> > regions_perImage; image_describer->Describe(imageL, regions_perImage[0]); image_describer->Describe(imageR, regions_perImage[1]); const SIFT_Regions* regionsL = dynamic_cast<SIFT_Regions*>(regions_perImage.at(0).get()); const SIFT_Regions* regionsR = dynamic_cast<SIFT_Regions*>(regions_perImage.at(1).get()); const PointFeatures featsL = regions_perImage.at(0)->GetRegionsPositions(), featsR = regions_perImage.at(1)->GetRegionsPositions(); // Show both images side by side { Image<unsigned char> concat; ConcatH(imageL, imageR, concat); string out_filename = "00_images.jpg"; WriteImage(out_filename.c_str(), concat); } //- Draw features on the two image (side by side) { Image<unsigned char> concat; ConcatH(imageL, imageR, concat); //-- Draw features : for (size_t i=0; i < featsL.size(); ++i ) { const SIOPointFeature point = regionsL->Features()[i]; DrawCircle(point.x(), point.y(), point.scale(), 255, &concat); } for (size_t i=0; i < featsR.size(); ++i ) { const SIOPointFeature point = regionsR->Features()[i]; DrawCircle(point.x()+imageL.Width(), point.y(), point.scale(), 255, &concat); } string out_filename = "01_features.jpg"; WriteImage(out_filename.c_str(), concat); } std::vector<IndMatch> vec_PutativeMatches; //-- Perform matching -> find Nearest neighbor, filtered with Distance ratio { // Define a matcher and a metric to find corresponding points typedef SIFT_Regions::DescriptorT DescriptorT; typedef L2_Vectorized<DescriptorT::bin_type> Metric; typedef ArrayMatcherBruteForce<DescriptorT::bin_type, Metric> MatcherT; // Distance ratio squared due to squared metric getPutativesMatches<DescriptorT, MatcherT>( ((SIFT_Regions*)regions_perImage.at(0).get())->Descriptors(), ((SIFT_Regions*)regions_perImage.at(1).get())->Descriptors(), Square(0.8), vec_PutativeMatches); // Draw correspondences after Nearest Neighbor ratio filter svgDrawer svgStream( imageL.Width() + imageR.Width(), max(imageL.Height(), imageR.Height())); svgStream.drawImage(jpg_filenameL, imageL.Width(), imageL.Height()); svgStream.drawImage(jpg_filenameR, imageR.Width(), imageR.Height(), imageL.Width()); for (size_t i = 0; i < vec_PutativeMatches.size(); ++i) { //Get back linked feature, draw a circle and link them by a line const SIOPointFeature L = regionsL->Features()[vec_PutativeMatches[i]._i]; const SIOPointFeature R = regionsR->Features()[vec_PutativeMatches[i]._j]; svgStream.drawLine(L.x(), L.y(), R.x()+imageL.Width(), R.y(), svgStyle().stroke("green", 2.0)); svgStream.drawCircle(L.x(), L.y(), L.scale(), svgStyle().stroke("yellow", 2.0)); svgStream.drawCircle(R.x()+imageL.Width(), R.y(), R.scale(),svgStyle().stroke("yellow", 2.0)); } string out_filename = "02_siftMatches.svg"; ofstream svgFile( out_filename.c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); } //K-VLD filter Image<float> imgA (imageL.GetMat().cast<float>()); Image<float> imgB (imageR.GetMat().cast<float>()); std::vector<Pair> matchesFiltered; std::vector<Pair> matchesPair; for (std::vector<IndMatch>::const_iterator i_match = vec_PutativeMatches.begin(); i_match != vec_PutativeMatches.end(); ++i_match){ matchesPair.push_back(std::make_pair(i_match->_i, i_match->_j)); } std::vector<double> vec_score; //In order to illustrate the gvld(or vld)-consistant neighbors, // the following two parameters has been externalized as inputs of the function KVLD. openMVG::Mat E = openMVG::Mat::Ones(vec_PutativeMatches.size(), vec_PutativeMatches.size())*(-1); // gvld-consistancy matrix, intitialized to -1, >0 consistancy value, -1=unknow, -2=false std::vector<bool> valide(vec_PutativeMatches.size(), true);// indices of match in the initial matches, if true at the end of KVLD, a match is kept. size_t it_num=0; KvldParameters kvldparameters; // initial parameters of KVLD while (it_num < 5 && kvldparameters.inlierRate > KVLD(imgA, imgB, regionsL->Features(), regionsR->Features(), matchesPair, matchesFiltered, vec_score,E,valide,kvldparameters)) { kvldparameters.inlierRate /= 2; //std::cout<<"low inlier rate, re-select matches with new rate="<<kvldparameters.inlierRate<<std::endl; kvldparameters.K = 2; it_num++; } std::vector<IndMatch> vec_FilteredMatches; for (std::vector<Pair>::const_iterator i_matchFilter = matchesFiltered.begin(); i_matchFilter != matchesFiltered.end(); ++i_matchFilter){ vec_FilteredMatches.push_back(IndMatch(i_matchFilter->first, i_matchFilter->second)); } //Print K-VLD consistent matches { svgDrawer svgStream(imageL.Width() + imageR.Width(), max(imageL.Height(), imageR.Height())); // ".svg" svgStream.drawImage(jpg_filenameL, imageL.Width(), imageL.Height()); svgStream.drawImage(jpg_filenameR, imageR.Width(), imageR.Height(), imageL.Width()); for (int it1=0; it1<matchesPair.size()-1;it1++){ for (int it2=it1+1; it2<matchesPair.size();it2++){ if (valide[it1] && valide[it2] && E(it1,it2)>=0){ const PointFeature & l1 = featsL[matchesPair[it1].first]; const PointFeature & r1 = featsR[matchesPair[it1].second]; const PointFeature & l2 = featsL[matchesPair[it2].first]; const PointFeature & r2 = featsR[matchesPair[it2].second]; // Compute the width of the current VLD segment float L = (l1.coords() - l2.coords()).norm(); float width = std::max(1.f, L / (dimension+1.f)); // ".svg" svgStream.drawLine(l1.x(), l1.y(), l2.x(), l2.y(), svgStyle().stroke("yellow", width)); svgStream.drawLine(r1.x() + imageL.Width(), r1.y(), r2.x() + imageL.Width(), r2.y(), svgStyle().stroke("yellow", width)); } } } string out_filename = "05_KVLD_Matches.svg"; out_filename = stlplus::create_filespec(sOutDir, out_filename); ofstream svgFile( out_filename.c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); } { //Print keypoints kept by K-VLD svgDrawer svgStream(imageL.Width() + imageR.Width(), max(imageL.Height(), imageR.Height())); // ".svg" svgStream.drawImage(jpg_filenameL, imageL.Width(), imageL.Height()); svgStream.drawImage(jpg_filenameR, imageR.Width(), imageR.Height(), imageL.Width()); for (int it=0; it<matchesPair.size();it++){ if (valide[it]){ const PointFeature & l = featsL[matchesPair[it].first]; const PointFeature & r = featsR[matchesPair[it].second]; // ".svg" svgStream.drawCircle(l.x(), l.y(), 10, svgStyle().stroke("yellow", 2.0)); svgStream.drawCircle(r.x() + imageL.Width(), r.y(), 10, svgStyle().stroke("yellow", 2.0)); } } string out_filename = "06_KVLD_Keypoints.svg"; out_filename = stlplus::create_filespec(sOutDir, out_filename); ofstream svgFile( out_filename.c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); } Image <unsigned char> imageOutL = imageL; Image <unsigned char> imageOutR = imageR; getKVLDMask( &imageOutL, &imageOutR, regionsL->Features(), regionsR->Features(), matchesPair, valide, E); { string out_filename = "07_Left-K-VLD-MASK.jpg"; out_filename = stlplus::create_filespec(sOutDir, out_filename); WriteImage(out_filename.c_str(), imageOutL); } { string out_filename = "08_Right-K-VLD-MASK.jpg"; out_filename = stlplus::create_filespec(sOutDir, out_filename); WriteImage(out_filename.c_str(), imageOutR); } return EXIT_SUCCESS; }
int main() { const std::string sInputDir = stlplus::folder_up(string(THIS_SOURCE_DIR)) + "/imageData/SceauxCastle/"; const string jpg_filenameL = sInputDir + "100_7101.jpg"; const string jpg_filenameR = sInputDir + "100_7102.jpg"; Image<unsigned char> imageL, imageR; ReadImage(jpg_filenameL.c_str(), &imageL); ReadImage(jpg_filenameR.c_str(), &imageR); //-- // Detect regions thanks to an image_describer //-- using namespace openMVG::features; std::unique_ptr<Image_describer> image_describer(new SIFT_Image_describer); std::map<IndexT, std::unique_ptr<features::Regions> > regions_perImage; image_describer->Describe(imageL, regions_perImage[0]); image_describer->Describe(imageR, regions_perImage[1]); const SIFT_Regions* regionsL = dynamic_cast<SIFT_Regions*>(regions_perImage.at(0).get()); const SIFT_Regions* regionsR = dynamic_cast<SIFT_Regions*>(regions_perImage.at(1).get()); const PointFeatures featsL = regions_perImage.at(0)->GetRegionsPositions(), featsR = regions_perImage.at(1)->GetRegionsPositions(); // Show both images side by side { Image<unsigned char> concat; ConcatH(imageL, imageR, concat); string out_filename = "01_concat.jpg"; WriteImage(out_filename.c_str(), concat); } //- Draw features on the two image (side by side) { Image<unsigned char> concat; ConcatH(imageL, imageR, concat); //-- Draw features : for (size_t i=0; i < featsL.size(); ++i ) { const SIOPointFeature point = regionsL->Features()[i]; DrawCircle(point.x(), point.y(), point.scale(), 255, &concat); } for (size_t i=0; i < featsR.size(); ++i ) { const SIOPointFeature point = regionsR->Features()[i]; DrawCircle(point.x()+imageL.Width(), point.y(), point.scale(), 255, &concat); } string out_filename = "02_features.jpg"; WriteImage(out_filename.c_str(), concat); } std::vector<IndMatch> vec_PutativeMatches; //-- Perform matching -> find Nearest neighbor, filtered with Distance ratio { // Define a matcher and a metric to find corresponding points typedef SIFT_Regions::DescriptorT DescriptorT; typedef L2_Vectorized<DescriptorT::bin_type> Metric; typedef ArrayMatcherBruteForce<DescriptorT::bin_type, Metric> MatcherT; // Distance ratio squared due to squared metric getPutativesMatches<DescriptorT, MatcherT>( ((SIFT_Regions*)regions_perImage.at(0).get())->Descriptors(), ((SIFT_Regions*)regions_perImage.at(1).get())->Descriptors(), Square(0.8), vec_PutativeMatches); IndMatchDecorator<float> matchDeduplicator( vec_PutativeMatches, featsL, featsR); matchDeduplicator.getDeduplicated(vec_PutativeMatches); std::cout << regions_perImage.at(0)->RegionCount() << " #Features on image A" << std::endl << regions_perImage.at(1)->RegionCount() << " #Features on image B" << std::endl << vec_PutativeMatches.size() << " #matches with Distance Ratio filter" << std::endl; // Draw correspondences after Nearest Neighbor ratio filter svgDrawer svgStream( imageL.Width() + imageR.Width(), max(imageL.Height(), imageR.Height())); svgStream.drawImage(jpg_filenameL, imageL.Width(), imageL.Height()); svgStream.drawImage(jpg_filenameR, imageR.Width(), imageR.Height(), imageL.Width()); for (size_t i = 0; i < vec_PutativeMatches.size(); ++i) { //Get back linked feature, draw a circle and link them by a line const SIOPointFeature L = regionsL->Features()[vec_PutativeMatches[i]._i]; const SIOPointFeature R = regionsR->Features()[vec_PutativeMatches[i]._j]; svgStream.drawLine(L.x(), L.y(), R.x()+imageL.Width(), R.y(), svgStyle().stroke("green", 2.0)); svgStream.drawCircle(L.x(), L.y(), L.scale(), svgStyle().stroke("yellow", 2.0)); svgStream.drawCircle(R.x()+imageL.Width(), R.y(), R.scale(),svgStyle().stroke("yellow", 2.0)); } const std::string out_filename = "03_siftMatches.svg"; std::ofstream svgFile( out_filename.c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); } // Essential geometry filtering of putative matches { Mat3 K; //read K from file if (!readIntrinsic(stlplus::create_filespec(sInputDir,"K","txt"), K)) { std::cerr << "Cannot read intrinsic parameters." << std::endl; return EXIT_FAILURE; } //A. prepare the corresponding putatives points Mat xL(2, vec_PutativeMatches.size()); Mat xR(2, vec_PutativeMatches.size()); for (size_t k = 0; k < vec_PutativeMatches.size(); ++k) { const PointFeature & imaL = featsL[vec_PutativeMatches[k]._i]; const PointFeature & imaR = featsR[vec_PutativeMatches[k]._j]; xL.col(k) = imaL.coords().cast<double>(); xR.col(k) = imaR.coords().cast<double>(); } //B. Compute the relative pose thanks to a essential matrix estimation std::pair<size_t, size_t> size_imaL(imageL.Width(), imageL.Height()); std::pair<size_t, size_t> size_imaR(imageR.Width(), imageR.Height()); sfm::RelativePose_Info relativePose_info; if (!sfm::robustRelativePose(K, K, xL, xR, relativePose_info, size_imaL, size_imaR, 256)) { std::cerr << " /!\\ Robust relative pose estimation failure." << std::endl; return EXIT_FAILURE; } std::cout << "\nFound an Essential matrix:\n" << "\tprecision: " << relativePose_info.found_residual_precision << " pixels\n" << "\t#inliers: " << relativePose_info.vec_inliers.size() << "\n" << "\t#matches: " << vec_PutativeMatches.size() << std::endl; // Show Essential validated point svgDrawer svgStream( imageL.Width() + imageR.Width(), max(imageL.Height(), imageR.Height())); svgStream.drawImage(jpg_filenameL, imageL.Width(), imageL.Height()); svgStream.drawImage(jpg_filenameR, imageR.Width(), imageR.Height(), imageL.Width()); for (size_t i = 0; i < relativePose_info.vec_inliers.size(); ++i) { const SIOPointFeature & LL = regionsL->Features()[vec_PutativeMatches[relativePose_info.vec_inliers[i]]._i]; const SIOPointFeature & RR = regionsR->Features()[vec_PutativeMatches[relativePose_info.vec_inliers[i]]._j]; const Vec2f L = LL.coords(); const Vec2f R = RR.coords(); svgStream.drawLine(L.x(), L.y(), R.x()+imageL.Width(), R.y(), svgStyle().stroke("green", 2.0)); svgStream.drawCircle(L.x(), L.y(), LL.scale(), svgStyle().stroke("yellow", 2.0)); svgStream.drawCircle(R.x()+imageL.Width(), R.y(), RR.scale(),svgStyle().stroke("yellow", 2.0)); } const std::string out_filename = "04_ACRansacEssential.svg"; std::ofstream svgFile( out_filename.c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); //C. Triangulate and export inliers as a PLY scene std::vector<Vec3> vec_3DPoints; // Setup camera intrinsic and poses Pinhole_Intrinsic intrinsic0(imageL.Width(), imageL.Height(), K(0, 0), K(0, 2), K(1, 2)); Pinhole_Intrinsic intrinsic1(imageR.Width(), imageR.Height(), K(0, 0), K(0, 2), K(1, 2)); const Pose3 pose0 = Pose3(Mat3::Identity(), Vec3::Zero()); const Pose3 pose1 = relativePose_info.relativePose; // Init structure by inlier triangulation const Mat34 P1 = intrinsic0.get_projective_equivalent(pose0); const Mat34 P2 = intrinsic1.get_projective_equivalent(pose1); std::vector<double> vec_residuals; vec_residuals.reserve(relativePose_info.vec_inliers.size() * 4); for (size_t i = 0; i < relativePose_info.vec_inliers.size(); ++i) { const SIOPointFeature & LL = regionsL->Features()[vec_PutativeMatches[relativePose_info.vec_inliers[i]]._i]; const SIOPointFeature & RR = regionsR->Features()[vec_PutativeMatches[relativePose_info.vec_inliers[i]]._j]; // Point triangulation Vec3 X; TriangulateDLT(P1, LL.coords().cast<double>(), P2, RR.coords().cast<double>(), &X); // Reject point that is behind the camera if (pose0.depth(X) < 0 && pose1.depth(X) < 0) continue; const Vec2 residual0 = intrinsic0.residual(pose0, X, LL.coords().cast<double>()); const Vec2 residual1 = intrinsic1.residual(pose1, X, RR.coords().cast<double>()); vec_residuals.push_back(fabs(residual0(0))); vec_residuals.push_back(fabs(residual0(1))); vec_residuals.push_back(fabs(residual1(0))); vec_residuals.push_back(fabs(residual1(1))); } // Display some statistics of reprojection errors float dMin, dMax, dMean, dMedian; minMaxMeanMedian<float>(vec_residuals.begin(), vec_residuals.end(), dMin, dMax, dMean, dMedian); std::cout << std::endl << "Triangulation residuals statistics:" << "\n" << "\t-- Residual min:\t" << dMin << "\n" << "\t-- Residual median:\t" << dMedian << "\n" << "\t-- Residual max:\t " << dMax << "\n" << "\t-- Residual mean:\t " << dMean << std::endl; // Export as PLY (camera pos + 3Dpoints) std::vector<Vec3> vec_camPos; vec_camPos.push_back( pose0.center() ); vec_camPos.push_back( pose1.center() ); exportToPly(vec_3DPoints, vec_camPos, "EssentialGeometry.ply"); } return EXIT_SUCCESS; }
int main() { Image<RGBColor> image; const string jpg_filenameL = stlplus::folder_up(string(THIS_SOURCE_DIR)) + "/imageData/StanfordMobileVisualSearch/Ace_0.png"; const string jpg_filenameR = stlplus::folder_up(string(THIS_SOURCE_DIR)) + "/imageData/StanfordMobileVisualSearch/Ace_1.png"; Image<unsigned char> imageL, imageR; ReadImage(jpg_filenameL.c_str(), &imageL); ReadImage(jpg_filenameR.c_str(), &imageR); //-- // Detect regions thanks to an image_describer //-- using namespace openMVG::features; std::unique_ptr<Image_describer> image_describer(new SIFT_Image_describer(SiftParams(-1))); std::map<IndexT, std::unique_ptr<features::Regions> > regions_perImage; image_describer->Describe(imageL, regions_perImage[0]); image_describer->Describe(imageR, regions_perImage[1]); const SIFT_Regions* regionsL = dynamic_cast<SIFT_Regions*>(regions_perImage.at(0).get()); const SIFT_Regions* regionsR = dynamic_cast<SIFT_Regions*>(regions_perImage.at(1).get()); const PointFeatures featsL = regions_perImage.at(0)->GetRegionsPositions(), featsR = regions_perImage.at(1)->GetRegionsPositions(); // Show both images side by side { Image<unsigned char> concat; ConcatH(imageL, imageR, concat); string out_filename = "01_concat.jpg"; WriteImage(out_filename.c_str(), concat); } //- Draw features on the two image (side by side) { Image<unsigned char> concat; ConcatH(imageL, imageR, concat); //-- Draw features : for (size_t i=0; i < featsL.size(); ++i ) { const SIOPointFeature point = regionsL->Features()[i]; DrawCircle(point.x(), point.y(), point.scale(), 255, &concat); } for (size_t i=0; i < featsR.size(); ++i ) { const SIOPointFeature point = regionsR->Features()[i]; DrawCircle(point.x()+imageL.Width(), point.y(), point.scale(), 255, &concat); } string out_filename = "02_features.jpg"; WriteImage(out_filename.c_str(), concat); } std::vector<IndMatch> vec_PutativeMatches; //-- Perform matching -> find Nearest neighbor, filtered with Distance ratio { // Define a matcher and a metric to find corresponding points typedef SIFT_Regions::DescriptorT DescriptorT; typedef L2_Vectorized<DescriptorT::bin_type> Metric; typedef ArrayMatcherBruteForce<DescriptorT::bin_type, Metric> MatcherT; // Distance ratio squared due to squared metric getPutativesMatches<DescriptorT, MatcherT>( ((SIFT_Regions*)regions_perImage.at(0).get())->Descriptors(), ((SIFT_Regions*)regions_perImage.at(1).get())->Descriptors(), Square(0.8), vec_PutativeMatches); // Draw correspondences after Nearest Neighbor ratio filter svgDrawer svgStream( imageL.Width() + imageR.Width(), max(imageL.Height(), imageR.Height())); svgStream.drawImage(jpg_filenameL, imageL.Width(), imageL.Height()); svgStream.drawImage(jpg_filenameR, imageR.Width(), imageR.Height(), imageL.Width()); for (size_t i = 0; i < vec_PutativeMatches.size(); ++i) { //Get back linked feature, draw a circle and link them by a line const SIOPointFeature L = regionsL->Features()[vec_PutativeMatches[i]._i]; const SIOPointFeature R = regionsR->Features()[vec_PutativeMatches[i]._j]; svgStream.drawLine(L.x(), L.y(), R.x()+imageL.Width(), R.y(), svgStyle().stroke("green", 2.0)); svgStream.drawCircle(L.x(), L.y(), L.scale(), svgStyle().stroke("yellow", 2.0)); svgStream.drawCircle(R.x()+imageL.Width(), R.y(), R.scale(),svgStyle().stroke("yellow", 2.0)); } string out_filename = "03_siftMatches.svg"; ofstream svgFile( out_filename.c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); } // Homography geometry filtering of putative matches { //A. get back interest point and send it to the robust estimation framework Mat xL(2, vec_PutativeMatches.size()); Mat xR(2, vec_PutativeMatches.size()); for (size_t k = 0; k < vec_PutativeMatches.size(); ++k) { const PointFeature & imaL = featsL[vec_PutativeMatches[k]._i]; const PointFeature & imaR = featsR[vec_PutativeMatches[k]._j]; xL.col(k) = imaL.coords().cast<double>(); xR.col(k) = imaR.coords().cast<double>(); } //-- Homography robust estimation std::vector<size_t> vec_inliers; typedef ACKernelAdaptor< openMVG::homography::kernel::FourPointSolver, openMVG::homography::kernel::AsymmetricError, UnnormalizerI, Mat3> KernelType; KernelType kernel( xL, imageL.Width(), imageL.Height(), xR, imageR.Width(), imageR.Height(), false); // configure as point to point error model. Mat3 H; std::pair<double,double> ACRansacOut = ACRANSAC(kernel, vec_inliers, 1024, &H, std::numeric_limits<double>::infinity(), true); const double & thresholdH = ACRansacOut.first; // Check the homography support some point to be considered as valid if (vec_inliers.size() > KernelType::MINIMUM_SAMPLES *2.5) { std::cout << "\nFound a homography under the confidence threshold of: " << thresholdH << " pixels\n\twith: " << vec_inliers.size() << " inliers" << " from: " << vec_PutativeMatches.size() << " putatives correspondences" << std::endl; //Show homography validated point and compute residuals std::vector<double> vec_residuals(vec_inliers.size(), 0.0); svgDrawer svgStream( imageL.Width() + imageR.Width(), max(imageL.Height(), imageR.Height())); svgStream.drawImage(jpg_filenameL, imageL.Width(), imageL.Height()); svgStream.drawImage(jpg_filenameR, imageR.Width(), imageR.Height(), imageL.Width()); for ( size_t i = 0; i < vec_inliers.size(); ++i) { const SIOPointFeature & LL = regionsL->Features()[vec_PutativeMatches[vec_inliers[i]]._i]; const SIOPointFeature & RR = regionsR->Features()[vec_PutativeMatches[vec_inliers[i]]._j]; const Vec2f L = LL.coords(); const Vec2f R = RR.coords(); svgStream.drawLine(L.x(), L.y(), R.x()+imageL.Width(), R.y(), svgStyle().stroke("green", 2.0)); svgStream.drawCircle(L.x(), L.y(), LL.scale(), svgStyle().stroke("yellow", 2.0)); svgStream.drawCircle(R.x()+imageL.Width(), R.y(), RR.scale(),svgStyle().stroke("yellow", 2.0)); // residual computation vec_residuals[i] = std::sqrt(KernelType::ErrorT::Error(H, LL.coords().cast<double>(), RR.coords().cast<double>())); } string out_filename = "04_ACRansacHomography.svg"; ofstream svgFile( out_filename.c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); // Display some statistics of reprojection errors float dMin, dMax, dMean, dMedian; minMaxMeanMedian<float>(vec_residuals.begin(), vec_residuals.end(), dMin, dMax, dMean, dMedian); std::cout << std::endl << "Homography matrix estimation, residuals statistics:" << "\n" << "\t-- Residual min:\t" << dMin << std::endl << "\t-- Residual median:\t" << dMedian << std::endl << "\t-- Residual max:\t " << dMax << std::endl << "\t-- Residual mean:\t " << dMean << std::endl; // -- // Perform GUIDED MATCHING // -- // Use the computed model to check valid correspondences // a. by considering only the geometric error, // b. by considering geometric error and descriptor distance ratio. std::vector< std::vector<IndMatch> > vec_corresponding_indexes(2); Mat xL, xR; PointsToMat(featsL, xL); PointsToMat(featsR, xR); //a. by considering only the geometric error geometry_aware::GuidedMatching<Mat3, openMVG::homography::kernel::AsymmetricError>( H, xL, xR, Square(thresholdH), vec_corresponding_indexes[0]); std::cout << "\nGuided homography matching (geometric error) found " << vec_corresponding_indexes[0].size() << " correspondences." << std::endl; // b. by considering geometric error and descriptor distance ratio typedef SIFT_Regions::DescriptorT DescriptorT; geometry_aware::GuidedMatching <Mat3, openMVG::homography::kernel::AsymmetricError, DescriptorT, L2_Vectorized<DescriptorT::bin_type> >( H, xL, ((SIFT_Regions*)regions_perImage.at(0).get())->Descriptors(), xR, ((SIFT_Regions*)regions_perImage.at(1).get())->Descriptors(), Square(thresholdH), Square(0.8), vec_corresponding_indexes[1]); std::cout << "\nGuided homography matching " << "(geometric + descriptor distance ratio) found " << vec_corresponding_indexes[1].size() << " correspondences." << std::endl; for (size_t idx = 0; idx < 2; ++idx) { const std::vector<IndMatch> & vec_corresponding_index = vec_corresponding_indexes[idx]; //Show homography validated correspondences svgDrawer svgStream( imageL.Width() + imageR.Width(), max(imageL.Height(), imageR.Height())); svgStream.drawImage(jpg_filenameL, imageL.Width(), imageL.Height()); svgStream.drawImage(jpg_filenameR, imageR.Width(), imageR.Height(), imageL.Width()); for ( size_t i = 0; i < vec_corresponding_index.size(); ++i) { const SIOPointFeature & LL = regionsL->Features()[vec_corresponding_index[i]._i]; const SIOPointFeature & RR = regionsR->Features()[vec_corresponding_index[i]._j]; const Vec2f L = LL.coords(); const Vec2f R = RR.coords(); svgStream.drawLine(L.x(), L.y(), R.x()+imageL.Width(), R.y(), svgStyle().stroke("green", 2.0)); svgStream.drawCircle(L.x(), L.y(), LL.scale(), svgStyle().stroke("yellow", 2.0)); svgStream.drawCircle(R.x()+imageL.Width(), R.y(), RR.scale(),svgStyle().stroke("yellow", 2.0)); } const string out_filename = (idx == 0) ? "04_ACRansacHomography_guided_geom.svg" : "04_ACRansacHomography_guided_geom_distratio.svg"; ofstream svgFile( out_filename.c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); } } else { std::cout << "ACRANSAC was unable to estimate a rigid homography" << std::endl; } } return EXIT_SUCCESS; }