@@ -221,63 +221,107 @@ using KdTree3D = nanoflann::KDTreeSingleIndexAdaptor<
221221 nanoflann::L2_Simple_Adaptor<double , LandmarksAdaptator>,
222222 LandmarksAdaptator, 3 /* dim */ ,size_t >;
223223
224- class RadiusKnnSearch
224+ class PixSizeSearch
225225{
226226public:
227227 const double _radius_sq;
228- const int _nb_neighbors;
229- int nb_found = 0 ;
228+ const std::vector<double >& _pixSize;
229+ const double _pixSize_i;
230+ bool found = false ;
230231
231- inline RadiusKnnSearch (double radius, int k )
232+ inline PixSizeSearch (double radius, const std::vector< double >& pixSize, size_t i )
232233 : _radius_sq(radius * radius)
233- , _nb_neighbors(k)
234+ , _pixSize(pixSize)
235+ , _pixSize_i(pixSize[i])
234236 {
235237 }
236238
237- inline bool full () const { return nb_found == _nb_neighbors ; }
239+ inline bool full () const { return found ; }
238240
239- inline bool addPoint (double dist, IndexT index)
241+ inline bool addPoint (double dist, size_t index)
240242 {
241- if (dist < _radius_sq)
243+ // strict comparison because a point in the tree can be a neighbor of itself
244+ if (dist < _radius_sq && _pixSize[index] < _pixSize_i)
242245 {
243- nb_found++ ;
244- return nb_found < _nb_neighbors ;
246+ found = true ;
247+ return false ;
245248 }
246249 return true ;
247250 }
248251
249252 inline double worstDist () const { return _radius_sq; }
250253};
251254
252- class PixSizeSearch
255+ class KnnNonZeroSearch
253256{
257+
254258public:
255- const double _radius_sq;
256- const std::vector<double >& _pixSize;
257- const double _pixSize_i;
258- bool found = false ;
259+ size_t * indices;
260+ double * dists;
261+ int capacity;
262+ int count;
263+ double _epsilon = 1e-6 ;
264+
265+ inline KnnNonZeroSearch (int capacity_)
266+ : indices(nullptr )
267+ , dists(nullptr )
268+ , capacity(capacity_)
269+ , count(0 )
270+ {
271+ }
259272
260- inline PixSizeSearch (double radius, const std::vector<double >& pixSize, size_t i)
261- : _radius_sq(radius * radius)
262- , _pixSize(pixSize)
263- , _pixSize_i(pixSize[i])
273+ inline void init (size_t * indices_, double * dists_)
264274 {
275+ indices = indices_;
276+ dists = dists_;
277+ count = 0 ;
278+ if (capacity)
279+ dists[capacity - 1 ] = (std::numeric_limits<double >::max)();
265280 }
266281
267- inline bool full () const { return found; }
282+ inline int size () const { return count; }
283+
284+ inline bool full () const { return count == capacity; }
268285
269286 inline bool addPoint (double dist, size_t index)
270287 {
271- // strict comparison because a point in the tree can be a neighbor of itself
272- if (dist < _radius_sq && _pixSize[index] < _pixSize_i)
288+ if (dist < _epsilon)
289+ return true ;
290+ int i;
291+ bool isDuplicate = false ;
292+ for (i = count; i > 0 ; --i)
273293 {
274- found = true ;
275- return false ;
294+ if (!(dists[i - 1 ] > dist))
295+ {
296+ isDuplicate = (dists[i - 1 ] == dist);
297+ break ;
298+ }
276299 }
300+ if (isDuplicate)
301+ {
302+ if (indices[i - 1 ] > index)
303+ indices[i - 1 ] = index;
304+ }
305+ else
306+ {
307+ if (i < capacity)
308+ {
309+ if (i > 0 )
310+ if (count < capacity)
311+ std::move_backward (&indices[i], &indices[count], &indices[count + 1 ]);
312+ else
313+ std::move_backward (&indices[i], &indices[capacity - 1 ], &indices[capacity]);
314+ dists[i] = dist;
315+ indices[i] = index;
316+ }
317+ if (count < capacity)
318+ count++;
319+ }
320+ // tell caller that the search shall continue
277321 return true ;
278322 }
279323
280- inline double worstDist () const { return _radius_sq ; }
324+ inline double worstDist () const { return dists[capacity - 1 ] ; }
281325};
282326
283327using ObservationsPerView = stl::flat_map<std::size_t , std::pair<std::vector<Observation>, std::vector<Landmark*>>>;
@@ -349,7 +393,7 @@ void filterLandmarks_step2(SfMData& sfmData,
349393
350394 ALICEVISION_LOG_INFO (" Computing landmarks neighbors: started." );
351395 // note that the landmark is a neighbor to itself with zero distance, hence the +/- 1
352- int nbNeighbors_ = std::min ( params.nbNeighbors3D , static_cast < int >(landmarksData. size () - 1 )) + 1 ;
396+ int nbNeighbors_ = params.nbNeighbors3D ;
353397 // contains the observing view ids and neighbors for each landmark
354398 std::vector<std::pair<std::vector<IndexT>, std::vector<size_t >>> viewData (landmarksData.size ());
355399#pragma omp parallel for
@@ -369,9 +413,12 @@ void filterLandmarks_step2(SfMData& sfmData,
369413
370414 neighbors.resize (nbNeighbors_);
371415 std::vector<double > weights_ (nbNeighbors_);
372- tree.knnSearch (landmark.X .data (), nbNeighbors_, &neighbors[0 ], &weights_[0 ]);
373- // a landmark is a neighbor to itself with zero distance, remove it
374- neighbors.erase (neighbors.begin ());
416+ KnnNonZeroSearch resultSet (nbNeighbors_);
417+ resultSet.init (&neighbors[0 ], &weights_[0 ]);
418+ tree.findNeighbors (resultSet, landmark.X .data ());
419+ const auto & nbFound = resultSet.size ();
420+ neighbors.resize (nbFound);
421+ weights_.resize (nbFound);
375422 }
376423 ALICEVISION_LOG_INFO (" Computing landmarks neighbors: done." );
377424
@@ -601,31 +648,28 @@ void computeNeighborsInfo(std::vector<Landmark*>& landmarksData, const FilterPar
601648 KdTree3D tree (3 , dataAdaptor, nanoflann::KDTreeSingleIndexAdaptorParams (MAX_LEAF_ELEMENTS));
602649 tree.buildIndex ();
603650 ALICEVISION_LOG_INFO (" KdTree created for " << landmarksData.size () << " points." );
604- // note that the landmark is a neighbor to itself with zero distance, hence the +/- 1
605- int nbNeighbors_ = std::min (params.nbNeighbors3D , static_cast <int >(landmarksData.size () - 1 )) + 1 ;
651+ int nbNeighbors_ = params.nbNeighbors3D ;
606652#pragma omp parallel for
607653 for (auto i = 0 ; i < landmarksData.size (); i++)
608654 {
609655 const sfmData::Landmark& landmark = *landmarksData[i];
610656 auto & [indices_, weights_] = neighborsData[i];
611657 indices_.resize (nbNeighbors_);
612658 weights_.resize (nbNeighbors_);
613- tree.knnSearch (landmark.X .data (), nbNeighbors_, &indices_[0 ], &weights_[0 ]);
614- // a landmark is a neighbor to itself with zero distance, remove it
615- indices_.erase (indices_.begin ());
616- weights_.erase (weights_.begin ());
659+ KnnNonZeroSearch resultSet (nbNeighbors_);
660+ resultSet.init (&indices_[0 ], &weights_[0 ]);
661+ tree.findNeighbors (resultSet, landmark.X .data ());
662+ const auto & nbFound = resultSet.size ();
663+ indices_.resize (nbFound);
664+ weights_.resize (nbFound);
617665 // accumulator used for normalisation
618666 double total = 0 .;
619667 for (auto & w : weights_)
620668 {
621669 // weight is the inverse of distance between a landmark and its neighbor
622670 w = 1 . / std::sqrt (w);
623- if (std::isinf (w))
624- w = std::numeric_limits<double >::max ();
625671 total += w;
626672 }
627- if (std::isinf (total))
628- total = std::numeric_limits<double >::max ();
629673 // normalize weights
630674 for (auto & w : weights_)
631675 {
@@ -651,7 +695,7 @@ void computeNewScores(const std::vector<Landmark*>& landmarksData,
651695 // accumulator for normalisation
652696 double viewScores_total = 0 .;
653697 auto & [indices_, weights_] = neighborsData[id];
654- for (auto j = 0 ; j < params. nbNeighbors3D ; j++)
698+ for (auto j = 0 ; j < indices_. size () ; j++)
655699 {
656700 const auto & neighborId = indices_[j];
657701 const auto & neighborWeight = weights_[j];
@@ -689,7 +733,8 @@ void computeNewScores(const std::vector<Landmark*>& landmarksData,
689733 for (auto j = 0 ; j < viewScores_acc.size (); j++)
690734 {
691735 // normalize score and apply influence factor
692- viewScores_acc[j] *= params.neighborsInfluence / viewScores_total;
736+ // viewScores_acc[j] *= params.neighborsInfluence / viewScores_total;
737+ viewScores_acc[j] *= params.neighborsInfluence ;
693738 // combine weighted neighbor scores and the landmark's own scores
694739 viewScores_acc[j] += (1 - params.neighborsInfluence ) * viewScores[j];
695740 }
@@ -742,7 +787,8 @@ void propagateNeighborsInfo(std::vector<Landmark*>& landmarksData,
742787 const auto & v = viewScoresData_t[id][j] - viewScoresData[id].second [j];
743788 error_j += v * v;
744789 }
745- error_j /= viewScoresData_t[id].size ();
790+ if (error_j > 0 .)
791+ error_j /= viewScoresData_t[id].size ();
746792 error += error_j;
747793 }
748794 // update scores
@@ -799,6 +845,9 @@ bool filterObservations3D(SfMData& sfmData, const FilterParams::FilterObservatio
799845 Observations filteredObservations;
800846 for (auto j = 0 ; j < params.maxNbObservationsPerLandmark ; j++)
801847 {
848+ const auto & viewScore = viewScores[idx[j]];
849+ if (viewScore < 0.3 )
850+ break ;
802851 const auto & viewId = viewIds[idx[j]];
803852 filteredObservations[viewId] = landmark.observations [viewId];
804853 }
@@ -818,24 +867,31 @@ double filter2DView(SfMData& sfmData, const FilterParams::FilterObservations2DPa
818867 tree.buildIndex ();
819868
820869 // note that the observation is a neighbor to itself with zero distance, hence the +/- 1
821- size_t nbNeighbors_ = std::min ( static_cast < size_t >( params.nbNeighbors2D ), observations. size () - 1 ) + 1 ;
870+ size_t nbNeighbors_ = params.nbNeighbors2D ;
822871 // average neighbors distance for each observation
823- std::vector<double > means (observations.size ());
872+ std::vector<double > means (observations.size (), std::numeric_limits< double >:: max () );
824873 const std::size_t cacheSize = 1000 ;
825874 // accumulator for quantile computation
826- accumulator_set<double , stats<tag::tail_quantile<right>>> acc (tag::tail<right>::cache_size = cacheSize);
875+ accumulator_set<double , stats<tag::median, tag:: tail_quantile<right>>> acc (tag::tail<right>::cache_size = cacheSize);
827876 for (auto j = 0 ; j < observations.size (); j++)
828877 {
829878 // Find neighbors and the corresponding distances
830879 const auto & obs = observations[j];
831880 std::vector<size_t > indices_ (nbNeighbors_);
832881 std::vector<double > distances_ (nbNeighbors_);
833- tree.knnSearch (obs.x .data (), nbNeighbors_, &indices_[0 ], &distances_[0 ]);
882+ KnnNonZeroSearch resultSet (nbNeighbors_);
883+ resultSet.init (&indices_[0 ], &distances_[0 ]);
884+ tree.findNeighbors (resultSet, obs.x .data ());
885+ const auto & nbFound = resultSet.size ();
886+ if (nbFound == 0 )
887+ continue ;
888+ indices_.resize (nbFound);
889+ distances_.resize (nbFound);
834890 // returned distances are L2 -> apply squared root
835891 std::transform (distances_.begin (), distances_.end (), distances_.begin (),
836892 static_cast <double (*)(double )>(std::sqrt));
837893 // average distance
838- const auto & mean = std::accumulate (distances_.begin (), distances_.end (), 0.0 ) / (nbNeighbors_ - 1 ) ;
894+ const auto & mean = std::accumulate (distances_.begin (), distances_.end (), 0.0 ) / nbFound ;
839895 means[j] = mean;
840896 // update accumulator
841897 acc (mean);
@@ -846,14 +902,16 @@ double filter2DView(SfMData& sfmData, const FilterParams::FilterObservations2DPa
846902 mean_max = quantile (acc, quantile_probability = params.percentile );
847903 // estimated mask radius is the average of distance means
848904 // quantile is used to avoid outlier bias
849- double radius = quantile (acc, quantile_probability = ( 1 . f - params. percentile ) * 0 . 5f );
905+ double radius = median (acc);
850906 // check if estimated radius is too large
851907 {
852908 const View& view = *(sfmData.getViews ().at (viewId));
853909 double radiusMax =
854910 params.maskRadiusThreshold * 0.5 * (view.getImage ().getWidth () + view.getImage ().getHeight ());
855911 if (radius > radiusMax)
856912 radius = radiusMax;
913+ if (mean_max > radiusMax)
914+ mean_max = radiusMax;
857915 }
858916
859917 // filter outlier observations
0 commit comments