/// 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;
}
Beispiel #2
0
/// 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;
}
Beispiel #4
0
/// 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());
  }
}
Beispiel #5
0
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() );
}
Beispiel #6
0
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));
}
Beispiel #7
0
/// 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;
}
Beispiel #8
0
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;
}
Beispiel #10
0
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]);
        }
      }
    }
  }
}
Beispiel #13
0
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 &regionsJ = *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) ));
        }
      }
    }
  }
}