/// List the pairs used by the relative rotations inline Pair_Set getPairs(const RelativeRotations & relRots) { Pair_Set pairs; for (const auto & cur_rotation : relRots ) pairs.insert({cur_rotation.i, cur_rotation.j}); return pairs; }
/// Generate the pairs that have a distance inferior to the overlapSize /// Usable to match video sequence inline Pair_Set contiguousWithOverlap(const size_t N, const size_t overlapSize) { Pair_Set pairs; for (IndexT I = 0; I < static_cast<IndexT>(N); ++I) for (IndexT J = I+1; J < I+1+overlapSize && J < static_cast<IndexT>(N); ++J) pairs.insert({I,J}); return pairs; }
Pair_Set Frustum_Filter::getFrustumIntersectionPairs ( const std::vector<HalfPlaneObject>& bounding_volume ) const { Pair_Set pairs; // List active view Id std::vector<IndexT> viewIds; viewIds.reserve(z_near_z_far_perView.size()); std::transform(z_near_z_far_perView.cbegin(), z_near_z_far_perView.cend(), std::back_inserter(viewIds), stl::RetrieveKey()); C_Progress_display my_progress_bar( viewIds.size() * (viewIds.size()-1)/2, std::cout, "\nCompute frustum intersection\n"); // Exhaustive comparison (use the fact that the intersect function is symmetric) #ifdef OPENMVG_USE_OPENMP #pragma omp parallel for #endif for (int i = 0; i < (int)viewIds.size(); ++i) { // Prepare vector of intersecting objects (within loop to keep it // thread-safe) std::vector<HalfPlaneObject> objects = bounding_volume; objects.insert(objects.end(), { frustum_perView.at(viewIds[i]), HalfPlaneObject() }); for (size_t j = i+1; j < viewIds.size(); ++j) { objects.back() = frustum_perView.at(viewIds[j]); if (intersect(objects)) { #ifdef OPENMVG_USE_OPENMP #pragma omp critical #endif { pairs.insert({viewIds[i], viewIds[j]}); } } // Progress bar update ++my_progress_bar; } } return pairs; }
/// Test to get back node id of each CC // 1-2 // // 3-4 // | | // 5-6 // // 7-8-9-10 // |/ // 11 TEST(Subgraphs, CC_Subgraph_CC_count) { using namespace openMVG; Pair_Set pairs; { std::set<IndexT> node_largest_cc = graph::KeepLargestCC_Nodes<Pair_Set, IndexT>(pairs); EXPECT_EQ(0, node_largest_cc.size()); } // two pairs.insert(Pair(1,2)); { std::set<IndexT> node_largest_cc = graph::KeepLargestCC_Nodes<Pair_Set, IndexT>(pairs); EXPECT_EQ(2, node_largest_cc.size()); } // four pairs.insert(Pair(3,4)); pairs.insert(Pair(3,5)); pairs.insert(Pair(4,6)); pairs.insert(Pair(5,6)); { std::set<IndexT> node_largest_cc = graph::KeepLargestCC_Nodes<Pair_Set, IndexT>(pairs); EXPECT_EQ(4, node_largest_cc.size()); } // five pairs.insert(Pair(7,8)); pairs.insert(Pair(8,9)); pairs.insert(Pair(9,10)); pairs.insert(Pair(8,11)); pairs.insert(Pair(9,11)); { std::set<IndexT> node_largest_cc = graph::KeepLargestCC_Nodes<Pair_Set, IndexT>(pairs); EXPECT_EQ(5, node_largest_cc.size()); } //-- // Test with a vector of pairs //-- Pair_Vec pairs_vec(pairs.begin(), pairs.end()); //random shuffle to assert that contiguous edges are close together. std::random_shuffle(pairs_vec.begin(), pairs_vec.end()); { std::set<IndexT> node_largest_cc = graph::KeepLargestCC_Nodes<Pair_Vec, IndexT>(pairs_vec); EXPECT_EQ(5, node_largest_cc.size()); } }
TEST(matching_image_collection, exhaustivePairs) { Pair_Set pairSet = exhaustivePairs(0); EXPECT_EQ( 0, pairSet.size()); pairSet = exhaustivePairs(4); EXPECT_TRUE( checkPairOrder(pairSet) ); EXPECT_EQ( 6, pairSet.size()); EXPECT_TRUE( pairSet.find(std::make_pair(0,1)) != pairSet.end() ); EXPECT_TRUE( pairSet.find(std::make_pair(0,2)) != pairSet.end() ); EXPECT_TRUE( pairSet.find(std::make_pair(0,3)) != pairSet.end() ); EXPECT_TRUE( pairSet.find(std::make_pair(1,2)) != pairSet.end() ); EXPECT_TRUE( pairSet.find(std::make_pair(1,3)) != pairSet.end() ); EXPECT_TRUE( pairSet.find(std::make_pair(2,3)) != pairSet.end() ); }
TEST(matching_image_collection, IO_InvalidInput) { // A pair with index superior to the expected picture count Pair_Set pairSetGT; pairSetGT.insert( std::make_pair(0,1) ); pairSetGT.insert( std::make_pair(10,20) ); EXPECT_TRUE( savePairs("pairsT_IO_InvalidInput.txt", pairSetGT)); Pair_Set loaded_Pairs; const int expectedPicCount = 2; EXPECT_FALSE( loadPairs(expectedPicCount, "pairsT_IO_InvalidInput.txt", loaded_Pairs)); // A pair with equal index pairSetGT.clear(); pairSetGT.insert( std::make_pair(0,1) ); pairSetGT.insert( std::make_pair(0,0) ); EXPECT_FALSE( loadPairs(expectedPicCount, "pairsT_IO_InvalidInput.txt", loaded_Pairs)); }
/// Load a set of Pair_Set from a file /// I J K L (pair that link I) inline bool loadPairs( const size_t N, // number of image in the current project (to check index validity) const std::string &sFileName, // filename of the list file, Pair_Set & pairs) // output pairs read from the list file { std::ifstream in(sFileName.c_str()); if (!in.is_open()) { std::cerr << std::endl << "loadPairs: Impossible to read the specified file: \"" << sFileName << "\"." << std::endl; return false; } std::string sValue; std::vector<std::string> vec_str; while (std::getline( in, sValue ) ) { vec_str.clear(); stl::split(sValue, ' ', vec_str); const IndexT str_size (vec_str.size()); if (str_size < 2) { std::cerr << "loadPairs: Invalid input file: \"" << sFileName << "\"." << std::endl; return false; } std::stringstream oss; oss.clear(); oss.str(vec_str[0]); IndexT I, J; oss >> I; for (IndexT i=1; i<str_size; ++i) { oss.clear(); oss.str(vec_str[i]); oss >> J; if ( I > N-1 || J > N-1) //I&J always > 0 since we use unsigned type { std::cerr << "loadPairs: Invalid input file. Image out of range. " << "I: " << I << " J:" << J << " N:" << N << std::endl << "File: \"" << sFileName << "\"." << std::endl; return false; } if ( I == J ) { std::cerr << "loadPairs: Invalid input file. Image " << I << " see itself. File: \"" << sFileName << "\"." << std::endl; return false; } // Insert the pair such that .first < .second pairs.insert( {std::min(I, J), std::max(I,J)} ); } } in.close(); return true; }
TEST(matching_image_collection, contiguousWithOverlap) { Pair_Set pairSet = exhaustivePairs(0); EXPECT_EQ( 0, pairSet.size()); pairSet = contiguousWithOverlap(4,1); EXPECT_TRUE( checkPairOrder(pairSet) ); EXPECT_EQ( 3, pairSet.size()); EXPECT_TRUE( pairSet.find(std::make_pair(0,1)) != pairSet.end() ); EXPECT_TRUE( pairSet.find(std::make_pair(1,2)) != pairSet.end() ); EXPECT_TRUE( pairSet.find(std::make_pair(2,3)) != pairSet.end() ); }
/// Build a list of pair that share visibility content from the SfM_Data structure Pair_Set BuildPairsFromStructureObservations(const SfM_Data & sfm_data) { Pair_Set pairs; for (Landmarks::const_iterator itL = sfm_data.GetLandmarks().begin(); itL != sfm_data.GetLandmarks().end(); ++itL) { const Landmark & landmark = itL->second; for (Observations::const_iterator iterI = landmark.obs.begin(); iterI != landmark.obs.end(); ++iterI) { const IndexT id_viewI = iterI->first; Observations::const_iterator iterJ = landmark.obs.begin(); std::advance(iterJ, 1); for (; iterJ != landmark.obs.end(); ++iterJ) { const IndexT id_viewJ = iterJ->first; pairs.insert( std::make_pair(id_viewI,id_viewJ)); } } } return pairs; }
TEST(matching_image_collection, IO) { Pair_Set pairSetGT; pairSetGT.insert( std::make_pair(0,1) ); pairSetGT.insert( std::make_pair(1,2) ); pairSetGT.insert( std::make_pair(2,0) ); Pair_Set pairSetGTsorted; pairSetGTsorted.insert( std::make_pair(0,1) ); pairSetGTsorted.insert( std::make_pair(0,2) ); pairSetGTsorted.insert( std::make_pair(1,2) ); EXPECT_TRUE( savePairs("pairsT_IO.txt", pairSetGT)); Pair_Set loaded_Pairs; EXPECT_TRUE( loadPairs(3, "pairsT_IO.txt", loaded_Pairs)); EXPECT_TRUE( std::equal(loaded_Pairs.begin(), loaded_Pairs.end(), pairSetGTsorted.begin()) ); }
/// Use guided matching to find corresponding 2-view correspondences void SfM_Data_Structure_Estimation_From_Known_Poses::match( const SfM_Data & sfm_data, const Pair_Set & pairs, const std::shared_ptr<Regions_Provider> & regions_provider) { C_Progress_display my_progress_bar( pairs.size(), std::cout, "Compute pairwise fundamental guided matching:\n" ); #ifdef OPENMVG_USE_OPENMP #pragma omp parallel #endif // OPENMVG_USE_OPENMP for (Pair_Set::const_iterator it = pairs.begin(); it != pairs.end(); ++it) { #ifdef OPENMVG_USE_OPENMP #pragma omp single nowait #endif // OPENMVG_USE_OPENMP { // -- // Perform GUIDED MATCHING // -- // Use the computed model to check valid correspondences // - by considering geometric error and descriptor distance ratio. std::vector<IndMatch> vec_corresponding_indexes; const View * viewL = sfm_data.GetViews().at(it->first).get(); const Pose3 poseL = sfm_data.GetPoseOrDie(viewL); const Intrinsics::const_iterator iterIntrinsicL = sfm_data.GetIntrinsics().find(viewL->id_intrinsic); const View * viewR = sfm_data.GetViews().at(it->second).get(); const Pose3 poseR = sfm_data.GetPoseOrDie(viewR); const Intrinsics::const_iterator iterIntrinsicR = sfm_data.GetIntrinsics().find(viewR->id_intrinsic); Mat xL, xR; PointsToMat(iterIntrinsicL->second.get(), regions_provider->regions_per_view.at(it->first)->GetRegionsPositions(), xL); PointsToMat(iterIntrinsicR->second.get(), regions_provider->regions_per_view.at(it->second)->GetRegionsPositions(), xR); const Mat34 P_L = iterIntrinsicL->second.get()->get_projective_equivalent(poseL); const Mat34 P_R = iterIntrinsicR->second.get()->get_projective_equivalent(poseR); const Mat3 F_lr = F_from_P(P_L, P_R); const double thresholdF = 4.0; #if defined(EXHAUSTIVE_MATCHING) // Guided matching considering geometric error and descriptor distance ratio geometry_aware::GuidedMatching <Mat3, openMVG::fundamental::kernel::EpipolarDistanceError, DescriptorT, L2_Vectorized<DescriptorT::bin_type> >( F_lr, xL, desc_provider.at(it->first), xR, desc_provider.at(it->second), Square(thresholdF), Square(0.8), vec_corresponding_indexes); #else const Vec3 epipole2 = epipole_from_P(P_R, poseL); const features::Regions * regions = regions_provider->regions_per_view.at(it->first).get(); if (regions->IsScalar()) { // L2 Metric (Handle descriptor internal type) if(regions->Type_id() == typeid(unsigned char).name()) { geometry_aware::GuidedMatching_Fundamental_Fast< openMVG::fundamental::kernel::EpipolarDistanceError, L2_Vectorized<unsigned char> > ( F_lr, epipole2, regions_provider->regions_per_view.at(it->first).get(), iterIntrinsicR->second.get()->w(), iterIntrinsicR->second.get()->h(), regions_provider->regions_per_view.at(it->second).get(), Square(thresholdF), Square(0.8), vec_corresponding_indexes); } else if(regions->Type_id() == typeid(float).name()) { geometry_aware::GuidedMatching_Fundamental_Fast< openMVG::fundamental::kernel::EpipolarDistanceError, L2_Vectorized<float> > ( F_lr, epipole2, regions_provider->regions_per_view.at(it->first).get(), iterIntrinsicR->second.get()->w(), iterIntrinsicR->second.get()->h(), regions_provider->regions_per_view.at(it->second).get(), Square(thresholdF), Square(0.8), vec_corresponding_indexes); } else if(regions->Type_id() == typeid(double).name()) { geometry_aware::GuidedMatching_Fundamental_Fast< openMVG::fundamental::kernel::EpipolarDistanceError, L2_Vectorized<double> > ( F_lr, epipole2, regions_provider->regions_per_view.at(it->first).get(), iterIntrinsicR->second.get()->w(), iterIntrinsicR->second.get()->h(), regions_provider->regions_per_view.at(it->second).get(), Square(thresholdF), Square(0.8), vec_corresponding_indexes); } } else if (regions->IsBinary() && regions->Type_id() == typeid(unsigned char).name()) { // Hamming metric geometry_aware::GuidedMatching_Fundamental_Fast< openMVG::fundamental::kernel::EpipolarDistanceError, Hamming<unsigned char> > ( F_lr, epipole2, regions_provider->regions_per_view.at(it->first).get(), iterIntrinsicR->second.get()->w(), iterIntrinsicR->second.get()->h(), regions_provider->regions_per_view.at(it->second).get(), Square(thresholdF), 0.8, vec_corresponding_indexes); } #endif #ifdef OPENMVG_USE_OPENMP #pragma omp critical #endif // OPENMVG_USE_OPENMP { ++my_progress_bar; for (size_t i = 0; i < vec_corresponding_indexes.size(); ++i) putatives_matches[*it].push_back(vec_corresponding_indexes[i]); } } } }
/// Use guided matching to find corresponding 2-view correspondences void SfM_Data_Structure_Estimation_From_Known_Poses::match( const SfM_Data & sfm_data, const Pair_Set & pairs, const std::shared_ptr<Regions_Provider> & regions_provider) { C_Progress_display my_progress_bar( pairs.size(), std::cout, "Compute pairwise fundamental guided matching:\n" ); #ifdef OPENMVG_USE_OPENMP #pragma omp parallel #endif // OPENMVG_USE_OPENMP for (Pair_Set::const_iterator it = pairs.begin(); it != pairs.end(); ++it) { #ifdef OPENMVG_USE_OPENMP #pragma omp single nowait #endif // OPENMVG_USE_OPENMP { // -- // Perform GUIDED MATCHING // -- // Use the computed model to check valid correspondences // - by considering geometric error and descriptor distance ratio. std::vector<IndMatch> vec_corresponding_indexes; const View * viewL = sfm_data.GetViews().at(it->first).get(); const Pose3 poseL = sfm_data.GetPoseOrDie(viewL); const Intrinsics::const_iterator iterIntrinsicL = sfm_data.GetIntrinsics().find(viewL->id_intrinsic); const View * viewR = sfm_data.GetViews().at(it->second).get(); const Pose3 poseR = sfm_data.GetPoseOrDie(viewR); const Intrinsics::const_iterator iterIntrinsicR = sfm_data.GetIntrinsics().find(viewR->id_intrinsic); if (sfm_data.GetIntrinsics().count(viewL->id_intrinsic) != 0 || sfm_data.GetIntrinsics().count(viewR->id_intrinsic) != 0) { const Mat34 P_L = iterIntrinsicL->second.get()->get_projective_equivalent(poseL); const Mat34 P_R = iterIntrinsicR->second.get()->get_projective_equivalent(poseR); const Mat3 F_lr = F_from_P(P_L, P_R); const double thresholdF = 4.0; #if defined(EXHAUSTIVE_MATCHING) geometry_aware::GuidedMatching <Mat3, openMVG::fundamental::kernel::EpipolarDistanceError> ( F_lr, iterIntrinsicL->second.get(), *regions_provider->regions_per_view.at(it->first), iterIntrinsicR->second.get(), *regions_provider->regions_per_view.at(it->second), Square(thresholdF), Square(0.8), vec_corresponding_indexes ); #else const Vec3 epipole2 = epipole_from_P(P_R, poseL); const features::Regions * regions = regions_provider->regions_per_view.at(it->first).get(); geometry_aware::GuidedMatching_Fundamental_Fast <openMVG::fundamental::kernel::EpipolarDistanceError> ( F_lr, epipole2, iterIntrinsicL->second.get(), *regions_provider->regions_per_view.at(it->first), iterIntrinsicR->second.get(), *regions_provider->regions_per_view.at(it->second), iterIntrinsicR->second->w(), iterIntrinsicR->second->h(), Square(thresholdF), Square(0.8), vec_corresponding_indexes ); #endif #ifdef OPENMVG_USE_OPENMP #pragma omp critical #endif // OPENMVG_USE_OPENMP { ++my_progress_bar; for (size_t i = 0; i < vec_corresponding_indexes.size(); ++i) putatives_matches[*it].push_back(vec_corresponding_indexes[i]); } } } } }
void Match ( const sfm::SfM_Data & sfm_data, const sfm::Regions_Provider & regions_provider, const Pair_Set & pairs, float fDistRatio, PairWiseMatchesContainer & map_PutativesMatches // the pairwise photometric corresponding points ) { C_Progress_display my_progress_bar( pairs.size() ); // Collect used view indexes std::set<IndexT> used_index; // Sort pairs according the first index to minimize later memory swapping using Map_vectorT = std::map<IndexT, std::vector<IndexT>>; Map_vectorT map_Pairs; for (Pair_Set::const_iterator iter = pairs.begin(); iter != pairs.end(); ++iter) { map_Pairs[iter->first].push_back(iter->second); used_index.insert(iter->first); used_index.insert(iter->second); } using BaseMat = Eigen::Matrix<ScalarT, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>; // Init the cascade hasher CascadeHasher cascade_hasher; if (!used_index.empty()) { const IndexT I = *used_index.begin(); std::shared_ptr<features::Regions> regionsI = regions_provider.get(I); const size_t dimension = regionsI.get()->DescriptorLength(); cascade_hasher.Init(dimension); } std::map<IndexT, HashedDescriptions> hashed_base_; // Compute the zero mean descriptor that will be used for hashing (one for all the image regions) Eigen::VectorXf zero_mean_descriptor; { Eigen::MatrixXf matForZeroMean; for (int i =0; i < used_index.size(); ++i) { std::set<IndexT>::const_iterator iter = used_index.begin(); std::advance(iter, i); const IndexT I = *iter; std::shared_ptr<features::Regions> regionsI = regions_provider.get(I); const ScalarT * tabI = reinterpret_cast<const ScalarT*>(regionsI.get()->DescriptorRawData()); const size_t dimension = regionsI.get()->DescriptorLength(); if (i==0) { matForZeroMean.resize(used_index.size(), dimension); matForZeroMean.fill(0.0f); } if (regionsI.get()->RegionCount() > 0) { Eigen::Map<BaseMat> mat_I( (ScalarT*)tabI, regionsI.get()->RegionCount(), dimension); matForZeroMean.row(i) = CascadeHasher::GetZeroMeanDescriptor(mat_I); } } zero_mean_descriptor = CascadeHasher::GetZeroMeanDescriptor(matForZeroMean); } // Index the input regions #ifdef OPENMVG_USE_OPENMP #pragma omp parallel for schedule(dynamic) #endif for (int i =0; i < used_index.size(); ++i) { std::set<IndexT>::const_iterator iter = used_index.begin(); std::advance(iter, i); const IndexT I = *iter; std::shared_ptr<features::Regions> regionsI = regions_provider.get(I); const ScalarT * tabI = reinterpret_cast<const ScalarT*>(regionsI.get()->DescriptorRawData()); const size_t dimension = regionsI.get()->DescriptorLength(); Eigen::Map<BaseMat> mat_I( (ScalarT*)tabI, regionsI.get()->RegionCount(), dimension); HashedDescriptions hashed_description = cascade_hasher.CreateHashedDescriptions(mat_I, zero_mean_descriptor); #ifdef OPENMVG_USE_OPENMP #pragma omp critical #endif { hashed_base_[I] = std::move(hashed_description); } } // Perform matching between all the pairs for (Map_vectorT::const_iterator iter = map_Pairs.begin(); iter != map_Pairs.end(); ++iter) { const IndexT I = iter->first; const std::vector<IndexT> & indexToCompare = iter->second; std::shared_ptr<features::Regions> regionsI = regions_provider.get(I); if (regionsI.get()->RegionCount() == 0) { my_progress_bar += indexToCompare.size(); continue; } const std::vector<features::PointFeature> pointFeaturesI = regionsI.get()->GetRegionsPositions(); const ScalarT * tabI = reinterpret_cast<const ScalarT*>(regionsI.get()->DescriptorRawData()); const size_t dimension = regionsI.get()->DescriptorLength(); Eigen::Map<BaseMat> mat_I( (ScalarT*)tabI, regionsI.get()->RegionCount(), dimension); #ifdef OPENMVG_USE_OPENMP #pragma omp parallel for schedule(dynamic) #endif for (int j = 0; j < (int)indexToCompare.size(); ++j) { size_t J = indexToCompare[j]; std::shared_ptr<features::Regions> regionsJ = regions_provider.get(J); if (regionsI.get()->Type_id() != regionsJ.get()->Type_id()) { #ifdef OPENMVG_USE_OPENMP #pragma omp critical #endif ++my_progress_bar; continue; } // Matrix representation of the query input data; const ScalarT * tabJ = reinterpret_cast<const ScalarT*>(regionsJ.get()->DescriptorRawData()); Eigen::Map<BaseMat> mat_J( (ScalarT*)tabJ, regionsJ.get()->RegionCount(), dimension); IndMatches pvec_indices; using ResultType = typename Accumulator<ScalarT>::Type; std::vector<ResultType> pvec_distances; pvec_distances.reserve(regionsJ.get()->RegionCount() * 2); pvec_indices.reserve(regionsJ.get()->RegionCount() * 2); // Match the query descriptors to the database cascade_hasher.Match_HashedDescriptions<BaseMat, ResultType>( hashed_base_[J], mat_J, hashed_base_[I], mat_I, &pvec_indices, &pvec_distances); std::vector<int> vec_nn_ratio_idx; // Filter the matches using a distance ratio test: // The probability that a match is correct is determined by taking // the ratio of distance from the closest neighbor to the distance // of the second closest. matching::NNdistanceRatio( pvec_distances.begin(), // distance start pvec_distances.end(), // distance end 2, // Number of neighbor in iterator sequence (minimum required 2) vec_nn_ratio_idx, // output (indices that respect the distance Ratio) Square(fDistRatio)); matching::IndMatches vec_putative_matches; vec_putative_matches.reserve(vec_nn_ratio_idx.size()); for (size_t k=0; k < vec_nn_ratio_idx.size(); ++k) { const size_t index = vec_nn_ratio_idx[k]; vec_putative_matches.emplace_back(pvec_indices[index*2].j_, pvec_indices[index*2].i_); } // Remove duplicates matching::IndMatch::getDeduplicated(vec_putative_matches); // Remove matches that have the same (X,Y) coordinates const std::vector<features::PointFeature> pointFeaturesJ = regionsJ.get()->GetRegionsPositions(); matching::IndMatchDecorator<float> matchDeduplicator(vec_putative_matches, pointFeaturesI, pointFeaturesJ); matchDeduplicator.getDeduplicated(vec_putative_matches); #ifdef OPENMVG_USE_OPENMP #pragma omp critical #endif { ++my_progress_bar; if (!vec_putative_matches.empty()) { map_PutativesMatches.insert( make_pair( make_pair(I,J), std::move(vec_putative_matches) )); } } } } }
int main(int argc, char ** argv) { CmdLine cmd; std::string sSfM_Data_Filename; std::string sMatchesDir; std::string sMatchFile; std::string sOutDir = ""; cmd.add( make_option('i', sSfM_Data_Filename, "input_file") ); cmd.add( make_option('d', sMatchesDir, "matchdir") ); cmd.add( make_option('m', sMatchFile, "matchfile") ); cmd.add( make_option('o', sOutDir, "outdir") ); try { if (argc == 1) throw std::string("Invalid command line parameter."); cmd.process(argc, argv); } catch(const std::string& s) { std::cerr << "Export pairwise matches.\nUsage: " << argv[0] << "\n" << "[-i|--input_file file] path to a SfM_Data scene\n" << "[-d|--matchdir path]\n" << "[-m|--sMatchFile filename]\n" << "[-o|--outdir path]\n" << std::endl; std::cerr << s << std::endl; return EXIT_FAILURE; } if (sOutDir.empty()) { std::cerr << "\nIt is an invalid output directory" << std::endl; return EXIT_FAILURE; } //--------------------------------------- // Read SfM Scene (image view names) //--------------------------------------- SfM_Data sfm_data; if (!Load(sfm_data, sSfM_Data_Filename, ESfM_Data(VIEWS|INTRINSICS))) { std::cerr << std::endl << "The input SfM_Data file \""<< sSfM_Data_Filename << "\" cannot be read." << std::endl; return EXIT_FAILURE; } //--------------------------------------- // Load SfM Scene regions //--------------------------------------- // Init the regions_type from the image describer file (used for image regions extraction) const std::string sImage_describer = stlplus::create_filespec(sMatchesDir, "image_describer", "json"); std::unique_ptr<Regions> regions_type = Init_region_type_from_file(sImage_describer); if (!regions_type) { std::cerr << "Invalid: " << sImage_describer << " regions type file." << std::endl; return EXIT_FAILURE; } // Read the features std::shared_ptr<Features_Provider> feats_provider = std::make_shared<Features_Provider>(); if (!feats_provider->load(sfm_data, sMatchesDir, regions_type)) { std::cerr << std::endl << "Invalid features." << std::endl; return EXIT_FAILURE; } std::shared_ptr<Matches_Provider> matches_provider = std::make_shared<Matches_Provider>(); if (!matches_provider->load(sfm_data, sMatchFile)) { std::cerr << std::endl << "Invalid matches file." << std::endl; return EXIT_FAILURE; } // ------------ // For each pair, export the matches // ------------ stlplus::folder_create(sOutDir); std::cout << "\n Export pairwise matches" << std::endl; const Pair_Set pairs = matches_provider->getPairs(); C_Progress_display my_progress_bar( pairs.size() ); for (Pair_Set::const_iterator iter = pairs.begin(); iter != pairs.end(); ++iter, ++my_progress_bar) { const size_t I = iter->first; const size_t J = iter->second; const View * view_I = sfm_data.GetViews().at(I).get(); const std::string sView_I= stlplus::create_filespec(sfm_data.s_root_path, view_I->s_Img_path); const View * view_J = sfm_data.GetViews().at(J).get(); const std::string sView_J= stlplus::create_filespec(sfm_data.s_root_path, view_J->s_Img_path); const std::pair<size_t, size_t> dimImage_I = std::make_pair(view_I->ui_width, view_I->ui_height), dimImage_J = std::make_pair(view_J->ui_width, view_J->ui_height); svgDrawer svgStream( dimImage_I.first + dimImage_J.first, max(dimImage_I.second, dimImage_J.second)); svgStream.drawImage(sView_I, dimImage_I.first, dimImage_I.second); svgStream.drawImage(sView_J, dimImage_J.first, dimImage_J.second, dimImage_I.first); const vector<IndMatch> & vec_FilteredMatches = matches_provider->_pairWise_matches.at(*iter); if (!vec_FilteredMatches.empty()) { const PointFeatures & vec_feat_I = feats_provider->getFeatures(view_I->id_view); const PointFeatures & vec_feat_J = feats_provider->getFeatures(view_J->id_view); //-- Draw link between features : for (size_t i=0; i< vec_FilteredMatches.size(); ++i) { const PointFeature & imaA = vec_feat_I[vec_FilteredMatches[i]._i]; const PointFeature & imaB = vec_feat_J[vec_FilteredMatches[i]._j]; // Compute a flashy colour for the correspondence unsigned char r,g,b; hslToRgb( (rand()%360) / 360., 1.0, .5, r, g, b); std::ostringstream osCol; osCol << "rgb(" << (int)r <<',' << (int)g << ',' << (int)b <<")"; svgStream.drawLine(imaA.x(), imaA.y(), imaB.x()+dimImage_I.first, imaB.y(), svgStyle().stroke(osCol.str(), 2.0)); } //-- Draw features (in two loop, in order to have the features upper the link, svg layer order): for (size_t i=0; i< vec_FilteredMatches.size(); ++i) { const PointFeature & imaA = vec_feat_I[vec_FilteredMatches[i]._i]; const PointFeature & imaB = vec_feat_J[vec_FilteredMatches[i]._j]; svgStream.drawCircle(imaA.x(), imaA.y(), 3.0, svgStyle().stroke("yellow", 2.0)); svgStream.drawCircle(imaB.x() + dimImage_I.first, imaB.y(), 3.0, svgStyle().stroke("yellow", 2.0)); } } std::ostringstream os; os << stlplus::folder_append_separator(sOutDir) << iter->first << "_" << iter->second << "_" << vec_FilteredMatches.size() << "_.svg"; ofstream svgFile( os.str().c_str() ); svgFile << svgStream.closeSvgFile().str(); svgFile.close(); } return EXIT_SUCCESS; }
int main(int argc, char **argv) { using namespace std; std::cout << std::endl << "-----------------------------------------------------------\n" << "Frustum filtering:\n" << "-----------------------------------------------------------\n" << "Compute camera cones that share some putative visual content.\n" << "------------------------------------------------------------" << std::endl; CmdLine cmd; std::string sSfM_Data_Filename; std::string sOutFile; double z_near = -1.; double z_far = -1.; cmd.add( make_option('i', sSfM_Data_Filename, "input_file") ); cmd.add( make_option('o', sOutFile, "output_file") ); cmd.add( make_option('n', z_near, "z_near") ); cmd.add( make_option('f', z_far, "z_far") ); try { if (argc == 1) throw std::string("Invalid parameter."); cmd.process(argc, argv); } catch (const std::string& s) { std::cerr << "Usage: " << argv[0] << '\n' << "[-i|--input_file] path to a SfM_Data scene\n" << "[-o|--output_file] filename of the output pair file\n" << "[-n|--z_near] 'optional' distance of the near camera plane\n" << "[-f|--z_far] 'optional' distance of the far camera plane\n" << std::endl; std::cerr << s << std::endl; return EXIT_FAILURE; } // Assert that we can create the output directory if (!stlplus::folder_exists( stlplus::folder_part(sOutFile)) && !stlplus::folder_create( stlplus::folder_part(sOutFile))) return EXIT_FAILURE; // Load input SfM_Data scene SfM_Data sfm_data; if (!Load(sfm_data, sSfM_Data_Filename, ESfM_Data(ALL))) { std::cerr << std::endl << "The input SfM_Data file \""<< sSfM_Data_Filename << "\" cannot be read." << std::endl; return EXIT_FAILURE; } openMVG::system::Timer timer; const Pair_Set pairs = BuildPairsFromFrustumsIntersections(sfm_data, z_near, z_far, stlplus::folder_part(sOutFile)); /*const Pair_Set pairs = BuildPairsFromStructureObservations(sfm_data); */ std::cout << "#pairs: " << pairs.size() << std::endl; std::cout << std::endl << " Pair filtering took (s): " << timer.elapsed() << std::endl; // export pairs on disk if (savePairs(sOutFile, pairs)) { return EXIT_SUCCESS; } return EXIT_FAILURE; }
void Matcher_Regions_AllInMemory::Match( const sfm::SfM_Data & sfm_data, const std::shared_ptr<sfm::Regions_Provider> & regions_provider, const Pair_Set & pairs, PairWiseMatches & map_PutativesMatches)const // the pairwise photometric corresponding points { #ifdef OPENMVG_USE_OPENMP std::cout << "Using the OPENMP thread interface" << std::endl; #endif const bool b_multithreaded_pair_search = (eMatcherType_ == CASCADE_HASHING_L2); // -> set to true for CASCADE_HASHING_L2, since OpenMP instructions are not used in this matcher C_Progress_display my_progress_bar( pairs.size() ); // Sort pairs according the first index to minimize the MatcherT build operations typedef std::map<size_t, std::vector<size_t> > Map_vectorT; Map_vectorT map_Pairs; for (Pair_Set::const_iterator iter = pairs.begin(); iter != pairs.end(); ++iter) { map_Pairs[iter->first].push_back(iter->second); } // Perform matching between all the pairs for (Map_vectorT::const_iterator iter = map_Pairs.begin(); iter != map_Pairs.end(); ++iter) { const size_t I = iter->first; const std::vector<size_t> & indexToCompare = iter->second; const features::Regions & regionsI = *regions_provider->regions_per_view.at(I).get(); if (regionsI.RegionCount() == 0) { my_progress_bar += indexToCompare.size(); continue; } // Initialize the matching interface matching::Matcher_Regions_Database matcher(eMatcherType_, regionsI); #ifdef OPENMVG_USE_OPENMP #pragma omp parallel for schedule(dynamic) if(b_multithreaded_pair_search) #endif for (int j = 0; j < (int)indexToCompare.size(); ++j) { const size_t J = indexToCompare[j]; const features::Regions ®ionsJ = *regions_provider->regions_per_view.at(J).get(); if (regionsJ.RegionCount() == 0 || regionsI.Type_id() != regionsJ.Type_id()) { #ifdef OPENMVG_USE_OPENMP #pragma omp critical #endif ++my_progress_bar; continue; } IndMatches vec_putatives_matches; matcher.Match(f_dist_ratio_, regionsJ, vec_putatives_matches); #ifdef OPENMVG_USE_OPENMP #pragma omp critical #endif { ++my_progress_bar; if (!vec_putatives_matches.empty()) { map_PutativesMatches.insert( make_pair( make_pair(I,J), std::move(vec_putatives_matches) )); } } } } }