@@ -61,7 +61,7 @@ struct FilterParams
6161 struct FilterLandmarksStep2Params
6262 {
6363 bool enabled = true ;
64- int maxNbObservationsPerLandmark = 2 ;
64+ float minScore = . 3f ;
6565 int nbNeighbors3D = 10 ;
6666 } step2;
6767 struct FilterLandmarksStep3Params
@@ -77,6 +77,9 @@ struct FilterParams
7777 int maxNbObservationsPerLandmark = 2 ;
7878 bool propagationEnabled = true ;
7979 int nbNeighbors3D = 10 ;
80+ bool observationsPropagationEnabled = true ;
81+ int observationsPropagationFrequency = 1 ;
82+ int observationsPropagationCount = 1 ;
8083 double neighborsInfluence = 0.5 ;
8184 int nbIterations = 5 ;
8285 bool dampingEnabled = true ;
@@ -97,7 +100,7 @@ std::istream& operator>>(std::istream& in, FilterParams& params)
97100 in >> token;
98101 std::vector<std::string> splitParams;
99102 boost::split (splitParams, token, boost::algorithm::is_any_of (" :" ));
100- if (splitParams.size () != 21 )
103+ if (splitParams.size () != 24 )
101104 throw std::invalid_argument (" Failed to parse FilterParams from: \n " + token);
102105 int i = 0 ;
103106
@@ -107,7 +110,7 @@ std::istream& operator>>(std::istream& in, FilterParams& params)
107110 params.filterLandmarks .step1 .minNbObservationsPerLandmark = boost::lexical_cast<int >(splitParams[i++]);
108111
109112 params.filterLandmarks .step2 .enabled = boost::to_lower_copy (splitParams[i++]) == " true" ;
110- params.filterLandmarks .step2 .maxNbObservationsPerLandmark = boost::lexical_cast<int >(splitParams[i++]);
113+ params.filterLandmarks .step2 .minScore = boost::lexical_cast<float >(splitParams[i++]);
111114 params.filterLandmarks .step2 .nbNeighbors3D = boost::lexical_cast<int >(splitParams[i++]);
112115
113116 params.filterLandmarks .step3 .enabled = boost::to_lower_copy (splitParams[i++]) == " true" ;
@@ -118,6 +121,9 @@ std::istream& operator>>(std::istream& in, FilterParams& params)
118121 params.filterObservations3D .maxNbObservationsPerLandmark = boost::lexical_cast<int >(splitParams[i++]);
119122 params.filterObservations3D .propagationEnabled = boost::to_lower_copy (splitParams[i++]) == " true" ;
120123 params.filterObservations3D .nbNeighbors3D = boost::lexical_cast<int >(splitParams[i++]);
124+ params.filterObservations3D .observationsPropagationEnabled = boost::to_lower_copy (splitParams[i++]) == " true" ;
125+ params.filterObservations3D .observationsPropagationFrequency = boost::lexical_cast<int >(splitParams[i++]);
126+ params.filterObservations3D .observationsPropagationCount = boost::lexical_cast<int >(splitParams[i++]);
121127 params.filterObservations3D .neighborsInfluence = boost::lexical_cast<double >(splitParams[i++]);
122128 params.filterObservations3D .nbIterations = boost::lexical_cast<int >(splitParams[i++]);
123129 params.filterObservations3D .dampingEnabled = boost::to_lower_copy (splitParams[i++]) == " true" ;
@@ -436,40 +442,24 @@ void filterLandmarks_step2(SfMData& sfmData,
436442#pragma omp parallel for reduction(+ : nbToRemove)
437443 for (auto i = 0 ; i < landmarksData.size (); i++)
438444 {
439- int score = 0 ;
445+ float score = 0 . f ;
440446 auto & [viewIds, neighbors] = viewData[i];
441447 for (auto j = 0 ; j < neighbors.size (); j++)
442448 {
443449 int scoreNeighbor = 0 ;
444450 const auto & neighborId = neighbors[j];
445451 const auto & viewIds_neighbor = viewData[neighborId].first ;
446- // loop over common views
447- auto viewIds_it = viewIds.begin ();
448- auto viewIds_neighbor_it = viewIds_neighbor.begin ();
449- while (viewIds_it != viewIds.end () && viewIds_neighbor_it != viewIds_neighbor.end ())
450- {
451- if (*viewIds_it < *viewIds_neighbor_it)
452- {
453- ++viewIds_it;
454- }
455- else
456- {
457- // if same view
458- if (!(*viewIds_neighbor_it < *viewIds_it))
459- {
460- scoreNeighbor++;
461- if (scoreNeighbor == params.maxNbObservationsPerLandmark )
462- {
463- score++;
464- break ;
465- }
466- ++viewIds_it;
467- }
468- ++viewIds_neighbor_it;
469- }
470- }
452+
453+ std::vector<IndexT> viewIds_intersection;
454+ std::set_intersection (viewIds.begin (), viewIds.end (), viewIds_neighbor.begin (), viewIds_neighbor.end (),
455+ std::back_inserter (viewIds_intersection));
456+ float nbIntersection = static_cast <float >(viewIds_intersection.size ());
457+ // Jaccard index or Intersection over Union (IoU): #overlap / #union
458+ score += nbIntersection / ((viewIds.size () + viewIds_neighbor.size ()) - nbIntersection);
471459 }
472- if (score < params.nbNeighbors3D / 2 )
460+ // score /= neighbors.size();
461+ score /= params.nbNeighbors3D ;
462+ if (score < params.minScore )
473463 {
474464 toRemove[i] = true ;
475465 nbToRemove++;
@@ -686,10 +676,10 @@ void computeNeighborsInfo(std::vector<Landmark*>& landmarksData, const FilterPar
686676 ALICEVISION_LOG_INFO (" Computing landmark neighbors and distance-based weights: done" );
687677}
688678
689- void augmentInitialObservations (std::vector<std::pair<std::vector<IndexT>, std::vector<double >>>& viewScoresData,
679+ void propagateNeighborsObservations (std::vector<std::pair<std::vector<IndexT>, std::vector<double >>>& viewScoresData,
690680 std::vector<std::pair<std::vector<size_t >, std::vector<double >>>& neighborsData)
691681{
692- ALICEVISION_LOG_INFO (" Augment initial observations based on neighbors observations: started" );
682+ ALICEVISION_LOG_INFO (" Augment observations with neighbors observations: started" );
693683 std::vector<std::vector<IndexT>> viewScoresData_t (viewScoresData.size ());
694684 // for each landmark, identify the new observations coming from its neighborhood
695685#pragma omp parallel for
@@ -712,12 +702,14 @@ void augmentInitialObservations(std::vector<std::pair<std::vector<IndexT>, std::
712702 std::back_inserter (viewIds_toAdd));
713703 viewIds_t = std::move (viewIds_toAdd);
714704 }
705+ double nbAdded = 0 ;
715706 // for each landmark, add the previously identified observations
716- #pragma omp parallel for
707+ #pragma omp parallel for reduction(+ : nbAdded)
717708 for (int id = 0 ; id < viewScoresData.size (); id++)
718709 {
719710 auto & [viewIds, viewScores] = viewScoresData[id];
720711 const auto & viewIds_toAdd = viewScoresData_t[id];
712+ nbAdded += static_cast <double >(viewIds_toAdd.size ()) / viewIds.size ();
721713 viewIds.insert (viewIds.end (), viewIds_toAdd.begin (), viewIds_toAdd.end ());
722714 viewScores.insert (viewScores.end (), viewIds_toAdd.size (), 0 .);
723715
@@ -737,21 +729,24 @@ void augmentInitialObservations(std::vector<std::pair<std::vector<IndexT>, std::
737729 viewScores[j] = scores_temp[idx[j]];
738730 }
739731 }
740- ALICEVISION_LOG_INFO (" Augment initial observations based on neighbors observations: done" );
732+ ALICEVISION_LOG_INFO (
733+ " On average, observations per landmark are augmented by: " << nbAdded * 100 . / viewScoresData.size () << " %." );
734+ ALICEVISION_LOG_INFO (" Augment observations with neighbors observations: done" );
741735}
742736
743- void computeNewScores (const std::vector<Landmark*>& landmarksData,
737+ void propagateNeighborsScores (const std::vector<Landmark*>& landmarksData,
744738 const FilterParams::FilterObservations3DParams& params,
745739 const std::vector<std::pair<std::vector<size_t >, std::vector<double >>>& neighborsData,
746740 const std::vector<std::pair<std::vector<IndexT>, std::vector<double >>>& viewScoresData,
747741 std::vector<std::vector<double >>& viewScoresData_t)
748742{
743+ ALICEVISION_LOG_INFO (" Propagating neighbors observation scores: started" );
749744#pragma omp parallel for
750745 for (auto id = 0 ; id < landmarksData.size (); id++)
751746 {
752747 const auto & [viewIds, viewScores] = viewScoresData[id];
753748 auto & viewScores_acc = viewScoresData_t[id];
754- // initialize to zero, will first contain the weighted average scores of neighbors
749+ // initialize to zero, will contain the weighted average scores of neighbors
755750 viewScores_acc.assign (viewScores.size (), 0 .);
756751 // accumulator for normalisation
757752 double viewScores_total = 0 .;
@@ -789,19 +784,16 @@ void computeNewScores(const std::vector<Landmark*>& landmarksData,
789784 }
790785 }
791786 }
792- // if common views with neighbor landmarks
793- // if(viewScores_total != 0.)
794787 for (auto j = 0 ; j < viewScores_acc.size (); j++)
795788 {
796- // normalize score and apply influence factor
797- // viewScores_acc[j] *= params.neighborsInfluence / viewScores_total;
789+ // apply influence factor
798790 viewScores_acc[j] *= params.neighborsInfluence ;
799791 // combine weighted neighbor scores and the landmark's own scores
800792 viewScores_acc[j] += (1 - params.neighborsInfluence ) * viewScores[j];
801793 }
802794
803795 // dampen scores of non-chosen observations
804- if (params.dampingEnabled && viewScores_acc.size () <= params.maxNbObservationsPerLandmark )
796+ if (params.dampingEnabled && viewScores_acc.size () > params.maxNbObservationsPerLandmark )
805797 {
806798 // sort by descending view score order
807799 std::vector<size_t > idx (viewScores_acc.size ());
@@ -820,19 +812,27 @@ void computeNewScores(const std::vector<Landmark*>& landmarksData,
820812 viewScores_acc[j] /= viewScores_total;
821813 }
822814 }
815+ ALICEVISION_LOG_INFO (" Propagating neighbors observation scores: done" );
823816}
824817
825818void propagateNeighborsInfo (std::vector<Landmark*>& landmarksData,
826819 const FilterParams::FilterObservations3DParams& params,
827820 std::vector<std::pair<std::vector<size_t >, std::vector<double >>>& neighborsData,
828821 std::vector<std::pair<std::vector<IndexT>, std::vector<double >>>& viewScoresData)
829822{
830- ALICEVISION_LOG_INFO (" Propagating neighbors observation scores: started" );
831823 // new view scores at iteration t
832824 std::vector<std::vector<double >> viewScoresData_t (landmarksData.size ());
825+ int observationsPropagationCount =
826+ params.observationsPropagationCount == 0 ? params.nbIterations : params.observationsPropagationCount ;
833827 for (auto i = 0 ; i < params.nbIterations ; i++)
834828 {
835- computeNewScores (landmarksData, params, neighborsData, viewScoresData, viewScoresData_t);
829+ if (params.observationsPropagationEnabled && (i % params.observationsPropagationFrequency == 0 ) &&
830+ observationsPropagationCount > 0 )
831+ {
832+ propagateNeighborsObservations (viewScoresData, neighborsData);
833+ --observationsPropagationCount;
834+ }
835+ propagateNeighborsScores (landmarksData, params, neighborsData, viewScoresData, viewScoresData_t);
836836
837837 // Mean Squared Error
838838 double error = 0 .;
@@ -858,7 +858,6 @@ void propagateNeighborsInfo(std::vector<Landmark*>& landmarksData,
858858 error /= landmarksData.size ();
859859 ALICEVISION_LOG_INFO (" MSE at iteration " << i << " : " << error);
860860 }
861- ALICEVISION_LOG_INFO (" Propagating neighbors observation scores: done" );
862861}
863862
864863void removeNonObservedLandmarks (SfMData& sfmData)
@@ -901,7 +900,6 @@ bool filterObservations3D(SfMData& sfmData, const FilterParams::FilterObservatio
901900 // contains the neighbor landmarks ids with their corresponding weights
902901 std::vector<std::pair<std::vector<size_t >, std::vector<double >>> neighborsData (landmarksData.size ());
903902 computeNeighborsInfo (landmarksData, params, neighborsData);
904- augmentInitialObservations (viewScoresData, neighborsData);
905903 propagateNeighborsInfo (landmarksData, params, neighborsData, viewScoresData);
906904 }
907905
@@ -913,24 +911,17 @@ bool filterObservations3D(SfMData& sfmData, const FilterParams::FilterObservatio
913911 auto & [viewIds, viewScores] = viewScoresData[i];
914912 const auto & nbObservations = viewIds.size ();
915913
916- // // check number of observations
917- // if(landmark.observations.size() <= params.maxNbObservationsPerLandmark)
918- // continue;
919-
920914 // sort by descending view score order
921915 std::vector<size_t > idx (nbObservations);
922916 std::iota (idx.begin (), idx.end (), 0 );
923917 std::stable_sort (idx.begin (), idx.end (), [&v = viewScores](size_t i1, size_t i2) { return v[i1] > v[i2]; });
924918
925919 // keep only observations with best scores
926920 Observations filteredObservations;
927- // double threshold = 0.1 / params.maxNbObservationsPerLandmark;
928- for (auto j = 0 ; j < params.maxNbObservationsPerLandmark ; j++)
921+ size_t maxNbObservationsPerLandmark =
922+ std::min (static_cast <size_t >(params.maxNbObservationsPerLandmark ), landmark.observations .size ());
923+ for (auto j = 0 ; j < maxNbObservationsPerLandmark; j++)
929924 {
930- // const auto& viewScore = viewScores[idx[j]];
931- /* if(viewScore < threshold)
932- break;*/
933-
934925 // add observation only if it's an original observation and not augmented
935926 const auto & viewId = viewIds[idx[j]];
936927 const auto & obsIt = landmark.observations .find (viewId);
@@ -954,7 +945,7 @@ double filter2DView(SfMData& sfmData, const FilterParams::FilterObservations2DPa
954945 KdTree2D tree (2 , data, nanoflann::KDTreeSingleIndexAdaptorParams (MAX_LEAF_ELEMENTS));
955946 tree.buildIndex ();
956947
957- size_t nbNeighbors_ = params.nbNeighbors2D ;
948+ int nbNeighbors_ = params.nbNeighbors2D ;
958949 // average neighbors distance for each observation
959950 std::vector<double > means (observations.size (), std::numeric_limits<double >::max ());
960951 const std::size_t cacheSize = 1000 ;
@@ -1106,7 +1097,7 @@ int aliceVision_main(int argc, char *argv[])
11061097 " * Min Nb of Observations: Minimum number of observations required to keep a landmark.\n "
11071098 " * Step 2 parameters:\n "
11081099 " * Enabled: Remove landmarks with dissimilar observations of 3D landmark neighbors.\n "
1109- " * Max Nb of Observations: Maximum number of allowed observations per landmark.\n "
1100+ " * Min Score: Minimum similarity score between the observations of the landmark and of its neighbors .\n "
11101101 " * Nb of Neighbors in 3D: Number of neighbor landmarks used in making the decision for best observations.\n "
11111102 " * Step 3 parameters:\n "
11121103 " * Enabled: Remove landmarks with worse resolution than neighbors.\n "
@@ -1116,8 +1107,11 @@ int aliceVision_main(int argc, char *argv[])
11161107 " Filter Observations 3D parameters:\n "
11171108 " * Enabled: Select best observations for observation consistency between 3D neighboring landmarks.\n "
11181109 " * Max Nb of Observations: Maximum number of allowed observations per landmark.\n "
1119- " * Enable Neighbors Influence: nable propagating neighbors' scores iteratively.\n "
1110+ " * Enable Neighbors Influence: Enable propagating neighbors' info ( scores, observations) iteratively.\n "
11201111 " * Nb of Neighbors in 3D: Number of neighbor landmarks used in making the decision for best observations.\n "
1112+ " * Enable Propagating Observations: Propagate neighbors observations before propagating the scores.\n "
1113+ " * Frequency: Specifies every how many iterations should the observations be propagated.\n "
1114+ " * Count: Maximum number of times the observations are propagated (0 for no limit).\n "
11211115 " * Neighbors Influence: Specifies how much influential the neighbors are in selecting the best observations.\n "
11221116 " Between 0. and 1., the closer to 1., the more influencial the neighborhood is.\n "
11231117 " * Nb of Iterations: Number of iterations to propagate neighbors information.\n "
0 commit comments