@@ -84,7 +84,7 @@ using KdTree = nanoflann::KDTreeSingleIndexAdaptor<
8484 nanoflann::L2_Simple_Adaptor<double , LandmarksAdaptator>,
8585 LandmarksAdaptator,
8686 3 , /* dim */
87- IndexT
87+ size_t
8888>;
8989
9090/* *
@@ -107,7 +107,7 @@ class PixSizeSearch
107107
108108 inline bool full () const { return found; }
109109
110- inline bool addPoint (double dist, IndexT index)
110+ inline bool addPoint (double dist, size_t index)
111111 {
112112 if (dist < _radius_sq && _pixSize[index] < _pixSize_i)
113113 {
@@ -291,11 +291,184 @@ bool filterObservations(SfMData& sfmData, int maxNbObservationsPerLandmark)
291291 {
292292 filteredObservations[observationScorePair.second ] = landmark.observations [observationScorePair.second ];
293293 }
294- landmark.observations = filteredObservations;
294+ landmark.observations = std::move ( filteredObservations) ;
295295 }
296296 return true ;
297297}
298298
299+ bool filterObservations2 (SfMData& sfmData, int maxNbObservationsPerLandmark, int nbNeighbors = 10 , int nbIterations = 5 ,
300+ double fraction = 0.5 )
301+ {
302+ std::vector<Landmark> landmarksData (sfmData.getLandmarks ().size ());
303+ {
304+ size_t i = 0 ;
305+ for (auto & it : sfmData.getLandmarks ())
306+ {
307+ landmarksData[i++] = it.second ;
308+ }
309+ }
310+
311+ std::vector<std::pair<std::vector<IndexT>, std::vector<double >>> viewScoresData (landmarksData.size ());
312+
313+ ALICEVISION_LOG_INFO (" Computing initial observation scores based on distance to observing view: started" );
314+ #pragma omp parallel for
315+ for (auto i = 0 ; i < landmarksData.size (); i++)
316+ {
317+ const sfmData::Landmark& landmark = landmarksData[i];
318+
319+ // compute observation scores
320+
321+ const auto & nbObservations = landmark.observations .size ();
322+ auto & [viewIds, viewScores] = viewScoresData[i];
323+ viewIds.reserve (nbObservations);
324+ viewScores.reserve (nbObservations);
325+ double total = 0 .;
326+ for (const auto & observationPair : landmark.observations )
327+ {
328+ const IndexT viewId = observationPair.first ;
329+ const sfmData::View& view = *(sfmData.getViews ().at (viewId));
330+ const geometry::Pose3 pose = sfmData.getPose (view).getTransform ();
331+
332+ viewIds.push_back (viewId);
333+ const auto & v = 1 . / (pose.center () - landmark.X ).squaredNorm ();
334+ total += v;
335+ viewScores.push_back (v);
336+ }
337+
338+ // normalize view scores
339+ for (auto j = 0 ; j < nbObservations; j++)
340+ {
341+ viewScores[j] /= total;
342+ }
343+
344+ // sort by ascending view id order
345+ // for consequent faster access
346+ std::vector<size_t > idx (nbObservations);
347+ std::iota (idx.begin (), idx.end (), 0 );
348+ std::stable_sort (idx.begin (), idx.end (), [&v = viewIds](size_t i1, size_t i2) { return v[i1] < v[i2]; });
349+ auto ids_temp = viewIds;
350+ auto scores_temp = viewScores;
351+ for (auto j = 0 ; j < nbObservations; j++)
352+ {
353+ viewIds[j] = ids_temp[idx[j]];
354+ viewScores[j] = scores_temp[idx[j]];
355+ }
356+ }
357+ ALICEVISION_LOG_INFO (" Computing initial observation scores based on distance to observing view: done" );
358+
359+ ALICEVISION_LOG_INFO (" Computing landmark neighbors and distance-based weights: started" );
360+ ALICEVISION_LOG_INFO (" Build nanoflann KdTree index." );
361+ LandmarksAdaptator dataAdaptor (landmarksData);
362+ KdTree tree (3 , dataAdaptor, nanoflann::KDTreeSingleIndexAdaptorParams (MAX_LEAF_ELEMENTS));
363+ tree.buildIndex ();
364+ ALICEVISION_LOG_INFO (" KdTree created for " << landmarksData.size () << " points." );
365+ std::vector<std::pair<std::vector<size_t >, std::vector<double >>> neighborsData (landmarksData.size ());
366+ #pragma omp parallel for
367+ for (auto i = 0 ; i < landmarksData.size (); i++)
368+ {
369+ const sfmData::Landmark& landmark = landmarksData[i];
370+ auto & [indices_, weights_] = neighborsData[i];
371+ indices_.resize (nbNeighbors);
372+ weights_.resize (nbNeighbors);
373+ tree.knnSearch (landmark.X .data (), nbNeighbors, &indices_[0 ], &weights_[0 ]);
374+ double total = 0 .;
375+ for (auto & w : weights_)
376+ {
377+ w = 1 . / std::sqrt (w);
378+ total += w;
379+ }
380+ for (auto & w : weights_)
381+ {
382+ w /= total;
383+ }
384+ }
385+ ALICEVISION_LOG_INFO (" Computing landmark neighbors and distance-based weights: done" );
386+
387+ ALICEVISION_LOG_INFO (" Propagating neighbors observation scores: started" );
388+ std::vector<std::vector<double >> viewScoresData_t (landmarksData.size ());
389+ for (auto i = 0 ; i < nbIterations; i++)
390+ {
391+ ALICEVISION_LOG_INFO (" Iteration " << i << " ..." );
392+ #pragma omp parallel for
393+ for (auto id = 0 ; id < landmarksData.size (); id++)
394+ {
395+ const auto & [viewIds, viewScores] = viewScoresData[id];
396+ auto & viewScores_acc = viewScoresData_t[id];
397+ viewScores_acc.assign (viewScores.size (), 0 .);
398+ double viewScores_total = 0 .;
399+ auto & [indices_, weights_] = neighborsData[id];
400+ for (auto j = 0 ; j < nbNeighbors; j++)
401+ {
402+ const auto & neighborId = indices_[j];
403+ const auto & neighborWeight = weights_[j];
404+ const auto & [viewIds_neighbor, viewScores_neighbor] = viewScoresData[neighborId];
405+ auto viewIds_it = viewIds.begin ();
406+ auto viewIds_neighbor_it = viewIds_neighbor.begin ();
407+ auto viewScores_neighbor_it = viewScores_neighbor.begin ();
408+ auto viewScores_acc_it = viewScores_acc.begin ();
409+ while (viewIds_it != viewIds.end () && viewIds_neighbor_it != viewIds_neighbor.end ())
410+ {
411+ if (*viewIds_it < *viewIds_neighbor_it)
412+ {
413+ ++viewIds_it;
414+ ++viewScores_acc_it;
415+ }
416+ else
417+ {
418+ if (!(*viewIds_neighbor_it < *viewIds_it))
419+ {
420+ const auto & v = *viewScores_neighbor_it * neighborWeight;
421+ (*viewScores_acc_it) += v;
422+ viewScores_total += v;
423+ ++viewIds_it;
424+ ++viewScores_acc_it;
425+ }
426+ ++viewIds_neighbor_it;
427+ ++viewScores_neighbor_it;
428+ }
429+ }
430+ }
431+ for (auto j = 0 ; j < viewScores_acc.size (); j++)
432+ {
433+ viewScores_acc[j] *= fraction / viewScores_total;
434+ viewScores_acc[j] += (1 - fraction) * viewScores[j];
435+ }
436+ }
437+ #pragma omp parallel for
438+ for (auto id = 0 ; id < landmarksData.size (); id++)
439+ {
440+ viewScoresData[id].second = std::move (viewScoresData_t[id]);
441+ }
442+ }
443+ ALICEVISION_LOG_INFO (" Propagating neighbors observation scores: done" );
444+
445+ ALICEVISION_LOG_INFO (" Selecting observations with best scores: started" );
446+ #pragma omp parallel for
447+ for (auto i = 0 ; i < landmarksData.size (); i++)
448+ {
449+ sfmData::Landmark& landmark = landmarksData[i];
450+ const auto & nbObservations = landmark.observations .size ();
451+ auto & [viewIds, viewScores] = viewScoresData[i];
452+
453+ // sort by descending view score order
454+ std::vector<size_t > idx (nbObservations);
455+ std::iota (idx.begin (), idx.end (), 0 );
456+ std::stable_sort (idx.begin (), idx.end (), [&v = viewScores](size_t i1, size_t i2) { return v[i1] > v[i2]; });
457+
458+ // replace the observations
459+ Observations filteredObservations;
460+ for (auto j = 0 ; j < maxNbObservationsPerLandmark; j++)
461+ {
462+ const auto & viewId = viewIds[idx[j]];
463+ filteredObservations[viewId] = landmark.observations [viewId];
464+ }
465+ landmark.observations = std::move (filteredObservations);
466+ }
467+ ALICEVISION_LOG_INFO (" Selecting observations with best scores: done" );
468+
469+ return true ;
470+ }
471+
299472int aliceVision_main (int argc, char *argv[])
300473{
301474 // command-line parameters
@@ -372,7 +545,7 @@ int aliceVision_main(int argc, char *argv[])
372545 if (maxNbObservationsPerLandmark > 0 )
373546 {
374547 ALICEVISION_LOG_INFO (" Filtering observations: started." );
375- filterObservations (sfmData, maxNbObservationsPerLandmark);
548+ filterObservations2 (sfmData, maxNbObservationsPerLandmark);
376549 ALICEVISION_LOG_INFO (" Filtering observations: done." );
377550 }
378551
0 commit comments