@@ -84,7 +84,7 @@ using KdTree = nanoflann::KDTreeSingleIndexAdaptor<
84
84
nanoflann::L2_Simple_Adaptor<double , LandmarksAdaptator>,
85
85
LandmarksAdaptator,
86
86
3 , /* dim */
87
- IndexT
87
+ size_t
88
88
>;
89
89
90
90
/* *
@@ -107,7 +107,7 @@ class PixSizeSearch
107
107
108
108
inline bool full () const { return found; }
109
109
110
- inline bool addPoint (double dist, IndexT index)
110
+ inline bool addPoint (double dist, size_t index)
111
111
{
112
112
if (dist < _radius_sq && _pixSize[index ] < _pixSize_i)
113
113
{
@@ -291,11 +291,184 @@ bool filterObservations(SfMData& sfmData, int maxNbObservationsPerLandmark)
291
291
{
292
292
filteredObservations[observationScorePair.second ] = landmark.observations [observationScorePair.second ];
293
293
}
294
- landmark.observations = filteredObservations;
294
+ landmark.observations = std::move ( filteredObservations) ;
295
295
}
296
296
return true ;
297
297
}
298
298
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_acc.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
+
299
472
int aliceVision_main (int argc, char *argv[])
300
473
{
301
474
// command-line parameters
@@ -372,7 +545,7 @@ int aliceVision_main(int argc, char *argv[])
372
545
if (maxNbObservationsPerLandmark > 0 )
373
546
{
374
547
ALICEVISION_LOG_INFO (" Filtering observations: started." );
375
- filterObservations (sfmData, maxNbObservationsPerLandmark);
548
+ filterObservations2 (sfmData, maxNbObservationsPerLandmark);
376
549
ALICEVISION_LOG_INFO (" Filtering observations: done." );
377
550
}
378
551
0 commit comments