Skip to content

Commit bec6491

Browse files
committed
[WIP] filter sfm
1 parent ff13496 commit bec6491

File tree

1 file changed

+177
-4
lines changed

1 file changed

+177
-4
lines changed

src/software/pipeline/main_filterSfM.cpp

+177-4
Original file line numberDiff line numberDiff line change
@@ -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_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+
299472
int 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

Comments
 (0)