Skip to content

Commit b16da19

Browse files
committed
[WIP][filterSfM] improve landmarks filtering
remove landmarks that don't have enough common observations with their neighbors
1 parent 190f42f commit b16da19

File tree

1 file changed

+105
-0
lines changed

1 file changed

+105
-0
lines changed

src/software/pipeline/main_filterSfM.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,111 @@ bool filterLandmarks(SfMData& sfmData, double radiusScale, bool useFeatureScale,
224224
}
225225
}
226226

227+
{
228+
ALICEVISION_LOG_INFO("Build nanoflann KdTree index for landmarks in 3D.");
229+
LandmarksAdaptator data(landmarksData);
230+
KdTree3D tree(3, data, nanoflann::KDTreeSingleIndexAdaptorParams(MAX_LEAF_ELEMENTS));
231+
tree.buildIndex();
232+
ALICEVISION_LOG_INFO("KdTree created for " << landmarksData.size() << " points.");
233+
234+
// TODO as parameter
235+
int nbNeighbors3D = 10;
236+
// note that the landmark is a neighbor to itself with zero distance, hence the +/- 1
237+
int nbNeighbors_ = std::min(nbNeighbors3D, static_cast<int>(landmarksData.size() - 1)) + 1;
238+
239+
// contains the observing view ids and neighbors for each landmark
240+
std::vector<std::pair<std::vector<IndexT>, std::vector<size_t>>> viewData(landmarksData.size());
241+
242+
#pragma omp parallel for
243+
for(auto i = 0; i < landmarksData.size(); i++)
244+
{
245+
const sfmData::Landmark& landmark = landmarksData[i];
246+
const auto& nbObservations = landmark.observations.size();
247+
auto& [viewIds, neighbors] = viewData[i];
248+
viewIds.reserve(nbObservations);
249+
for(const auto& observationPair : landmark.observations)
250+
{
251+
const IndexT viewId = observationPair.first;
252+
viewIds.push_back(viewId);
253+
}
254+
// sort by ascending view id order for consequent faster access
255+
std::stable_sort(viewIds.begin(), viewIds.end());
256+
257+
neighbors.resize(nbNeighbors_);
258+
std::vector<double> weights_(nbNeighbors_);
259+
tree.knnSearch(landmark.X.data(), nbNeighbors_, &neighbors[0], &weights_[0]);
260+
// a landmark is a neighbor to itself with zero distance, remove it
261+
neighbors.erase(neighbors.begin());
262+
}
263+
264+
std::vector<bool> toRemove(landmarksData.size(), false);
265+
size_t nbToRemove = 0;
266+
// TODO as parameter
267+
int maxNbObservationsPerLandmark = 2;
268+
const auto initialNbLandmarks = sfmData.getLandmarks().size();
269+
#pragma omp parallel for reduction(+ : nbToRemove)
270+
for(auto i = 0; i < landmarksData.size(); i++)
271+
{
272+
int score = 0;
273+
auto& [viewIds, neighbors] = viewData[i];
274+
for(auto j = 0; j < neighbors.size(); j++)
275+
{
276+
int scoreNeighbor = 0;
277+
const auto& neighborId = neighbors[j];
278+
const auto& viewIds_neighbor = viewData[neighborId].first;
279+
// loop over common views
280+
auto viewIds_it = viewIds.begin();
281+
auto viewIds_neighbor_it = viewIds_neighbor.begin();
282+
while(viewIds_it != viewIds.end() && viewIds_neighbor_it != viewIds_neighbor.end())
283+
{
284+
if(*viewIds_it < *viewIds_neighbor_it)
285+
{
286+
++viewIds_it;
287+
}
288+
else
289+
{
290+
// if same view
291+
if(!(*viewIds_neighbor_it < *viewIds_it))
292+
{
293+
scoreNeighbor++;
294+
if(scoreNeighbor == maxNbObservationsPerLandmark)
295+
{
296+
score++;
297+
break;
298+
}
299+
++viewIds_it;
300+
}
301+
++viewIds_neighbor_it;
302+
}
303+
}
304+
}
305+
if(score < nbNeighbors3D / 2)
306+
{
307+
toRemove[i] = true;
308+
nbToRemove++;
309+
}
310+
}
311+
ALICEVISION_LOG_INFO("Identified " << nbToRemove << " landmarks to remove out of " << initialNbLandmarks
312+
<< ", i.e. " << (nbToRemove * 100.f / initialNbLandmarks) << " % ");
313+
ALICEVISION_LOG_INFO("Identifying landmarks to remove: done.");
314+
315+
ALICEVISION_LOG_INFO("Removing landmarks based on TODO: started.");
316+
std::vector<std::pair<IndexT, Landmark>> filteredLandmarks(landmarksData.size() - nbToRemove);
317+
std::vector<Landmark> landmarksData_filtered(landmarksData.size() - nbToRemove);
318+
IndexT newIdx = 0;
319+
for(size_t i = 0; i < landmarksData.size(); i++)
320+
{
321+
if(!toRemove[i])
322+
{
323+
landmarksData_filtered[newIdx] = landmarksData[i];
324+
filteredLandmarks[newIdx++] = std::make_pair(newIdx, landmarksData[i]);
325+
}
326+
}
327+
sfmData.getLandmarks() = std::move(Landmarks(filteredLandmarks.begin(), filteredLandmarks.end()));
328+
landmarksData = std::move(landmarksData_filtered);
329+
ALICEVISION_LOG_INFO("Removing landmarks based on TODO: done.");
330+
}
331+
227332
if (radiusScale <= 0)
228333
{
229334
std::vector<std::pair<IndexT, Landmark>> filteredLandmarks(landmarksData.size());

0 commit comments

Comments
 (0)