Skip to content

Commit fdd68ed

Browse files
almaroukcbentejac
authored andcommitted
[sfm] identify and merge tracks having corresponding duplicate features
- affects building and filtering the tracks - duplicate features are features in the same view, with same describer type, same position and scale but different rotations
1 parent 1a30873 commit fdd68ed

File tree

4 files changed

+183
-11
lines changed

4 files changed

+183
-11
lines changed

src/aliceVision/feature/FeaturesPerView.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ class FeaturesPerView
8989
*/
9090
feature::MapFeaturesPerView& getData() { return _data; }
9191

92+
const feature::MapFeaturesPerView& getData() const { return _data; }
93+
9294
private:
9395
/// PointFeature array per ViewId of the considered SfMData container
9496
MapFeaturesPerView _data;

src/aliceVision/sfm/pipeline/sequential/ReconstructionEngine_sequentialSfM.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ std::size_t ReconstructionEngine_sequentialSfM::fuseMatchesIntoTracks()
246246
const aliceVision::matching::PairwiseMatches& matches = *_pairwiseMatches;
247247

248248
ALICEVISION_LOG_DEBUG("Track building");
249-
tracksBuilder.build(matches);
249+
tracksBuilder.build(matches, _featuresPerView->getData());
250250

251251
ALICEVISION_LOG_DEBUG("Track filtering");
252252
tracksBuilder.filter(_params.filterTrackForks, _params.minInputTrackLength);

src/aliceVision/track/TracksBuilder.cpp

+167-10
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,38 @@
1010
#include <lemon/list_graph.h>
1111
#include <lemon/unionfind.h>
1212

13+
/**
14+
* @brief Contains necessary information to uniquely identify a duplicate feature
15+
*/
16+
struct DuplicateFeatureId
17+
{
18+
DuplicateFeatureId(float x_, float y_, float scale_)
19+
: x(x_),
20+
y(y_),
21+
scale(scale_)
22+
{}
23+
24+
// for uniqueness test when used as a map key
25+
bool operator<(const DuplicateFeatureId& other) const
26+
{
27+
if (x == other.x)
28+
{
29+
if (y == other.y)
30+
return scale < other.scale;
31+
return y < other.y;
32+
}
33+
return x < other.x;
34+
}
35+
36+
float x, y, scale;
37+
};
38+
1339
namespace aliceVision {
1440
namespace track {
1541

1642
using namespace aliceVision::matching;
1743
using namespace lemon;
1844

19-
/// IndexedFeaturePair is: map<viewId, keypointId>
20-
using IndexedFeaturePair = std::pair<std::size_t, KeypointId>;
2145
using IndexMap = lemon::ListDigraph::NodeMap<std::size_t>;
2246
using UnionFindObject = lemon::UnionFindEnum<IndexMap>;
2347

@@ -42,7 +66,7 @@ TracksBuilder::TracksBuilder() { _d.reset(new TracksBuilderData()); }
4266

4367
TracksBuilder::~TracksBuilder() = default;
4468

45-
void TracksBuilder::build(const PairwiseMatches& pairwiseMatches)
69+
void buildTracks(const PairwiseMatches& pairwiseMatches, std::unique_ptr<TracksBuilderData>& _d, MapIndexToNode& map_indexToNode)
4670
{
4771
typedef std::set<IndexedFeaturePair> SetIndexedPair;
4872

@@ -72,7 +96,6 @@ void TracksBuilder::build(const PairwiseMatches& pairwiseMatches)
7296
}
7397

7498
// build the node indirection for each referenced feature
75-
MapIndexToNode map_indexToNode;
7699
map_indexToNode.reserve(allFeatures.size());
77100
_d->map_nodeToIndex.reserve(allFeatures.size());
78101

@@ -114,6 +137,119 @@ void TracksBuilder::build(const PairwiseMatches& pairwiseMatches)
114137
}
115138
}
116139

140+
// Merge tracks that have corresponding duplicate features.
141+
// Make the union according to duplicate features
142+
// (same position, scale and describer type, but different orientations)
143+
void mergeTracks(const feature::MapFeaturesPerView& featuresPerView,
144+
const MapIndexToNode& map_indexToNode,
145+
const PairwiseMatches& pairwiseMatches,
146+
std::unique_ptr<TracksBuilderData>& _d,
147+
stl::flat_map<IndexedFeaturePair, size_t>& _duplicateFeaturesMap)
148+
{
149+
// map of (viewId) to
150+
// map of (descType) to
151+
// map of DuplicateFeatureId(x, y, scale) to
152+
// pair of (set<featureId>, node)
153+
HashMap<size_t,
154+
HashMap<feature::EImageDescriberType, HashMap<DuplicateFeatureId, std::pair<std::set<size_t>, const MapIndexToNode::mapped_type*>>>>
155+
duplicateFeaturesPerView;
156+
157+
// per viewId pair
158+
for (const auto& matchesPerDescIt : pairwiseMatches)
159+
{
160+
const std::size_t& I = matchesPerDescIt.first.first;
161+
const std::size_t& J = matchesPerDescIt.first.second;
162+
const MatchesPerDescType& matchesPerDesc = matchesPerDescIt.second;
163+
164+
auto& featuresPerDescI = featuresPerView.at(I);
165+
auto& featuresPerDescJ = featuresPerView.at(J);
166+
auto& duplicateFeaturesPerDescI = duplicateFeaturesPerView[I];
167+
auto& duplicateFeaturesPerDescJ = duplicateFeaturesPerView[J];
168+
169+
// per descType
170+
for (const auto& matchesIt : matchesPerDesc)
171+
{
172+
const feature::EImageDescriberType descType = matchesIt.first;
173+
const IndMatches& matches = matchesIt.second;
174+
175+
auto& featuresI = featuresPerDescI.at(descType);
176+
auto& featuresJ = featuresPerDescJ.at(descType);
177+
auto& duplicateFeaturesI = duplicateFeaturesPerDescI[descType];
178+
auto& duplicateFeaturesJ = duplicateFeaturesPerDescJ[descType];
179+
180+
// per features match
181+
for (const IndMatch& m : matches)
182+
{
183+
{
184+
auto& featureI = featuresI[m._i];
185+
auto& [duplicateFeatureIdsI, duplicateFeatureNodeI] =
186+
duplicateFeaturesI[DuplicateFeatureId(featureI.x(), featureI.y(), featureI.scale())];
187+
IndexedFeaturePair pairI(I, KeypointId(descType, m._i));
188+
auto& nodeI = map_indexToNode.at(pairI);
189+
// if no duplicates yet found, add to map and update values
190+
if (duplicateFeatureNodeI == nullptr)
191+
{
192+
duplicateFeatureIdsI.insert(m._i);
193+
duplicateFeatureNodeI = &nodeI;
194+
}
195+
// if not already in corresponding duplicates set, add to set and join nodes
196+
else if (duplicateFeatureIdsI.insert(m._i).second)
197+
{
198+
_d->tracksUF->join(nodeI, *duplicateFeatureNodeI);
199+
}
200+
}
201+
{
202+
auto& featureJ = featuresJ[m._j];
203+
auto& [duplicateFeatureIdsJ, duplicateFeatureNodeJ] =
204+
duplicateFeaturesJ[DuplicateFeatureId(featureJ.x(), featureJ.y(), featureJ.scale())];
205+
IndexedFeaturePair pairJ(J, KeypointId(descType, m._j));
206+
auto& nodeJ = map_indexToNode.at(pairJ);
207+
// if no duplicates yet found, add to map and update values
208+
if (duplicateFeatureNodeJ == nullptr)
209+
{
210+
duplicateFeatureIdsJ.insert(m._j);
211+
duplicateFeatureNodeJ = &nodeJ;
212+
}
213+
// if not already in corresponding duplicates set, add to set and join nodes
214+
else if (duplicateFeatureIdsJ.insert(m._j).second)
215+
{
216+
_d->tracksUF->join(nodeJ, *duplicateFeatureNodeJ);
217+
}
218+
}
219+
}
220+
}
221+
}
222+
223+
// fill duplicate features map
224+
for (const auto& [viewId, duplicateFeaturesPerDesc] : duplicateFeaturesPerView)
225+
for (const auto& [descType, duplicateFeatures] : duplicateFeaturesPerDesc)
226+
for (const auto& [duplicateFeatureId, duplicateFeature] : duplicateFeatures)
227+
{
228+
auto& duplicateFeatureIdsSet = duplicateFeature.first;
229+
size_t indexedFeaturePair_0 = *duplicateFeatureIdsSet.begin();
230+
for (const auto& featureId : duplicateFeatureIdsSet)
231+
{
232+
const auto& indexedFeaturePair_i = IndexedFeaturePair(viewId, KeypointId(descType, featureId));
233+
_duplicateFeaturesMap[indexedFeaturePair_i] = indexedFeaturePair_0;
234+
}
235+
}
236+
}
237+
238+
void TracksBuilder::build(const PairwiseMatches& pairwiseMatches)
239+
{
240+
// the node indirection for each referenced feature
241+
MapIndexToNode map_indexToNode;
242+
buildTracks(pairwiseMatches, _d, map_indexToNode);
243+
}
244+
245+
void TracksBuilder::build(const PairwiseMatches& pairwiseMatches, const feature::MapFeaturesPerView& featuresPerView)
246+
{
247+
// the node indirection for each referenced feature
248+
MapIndexToNode map_indexToNode;
249+
buildTracks(pairwiseMatches, _d, map_indexToNode);
250+
mergeTracks(featuresPerView, map_indexToNode, pairwiseMatches, _d, _duplicateFeaturesMap);
251+
}
252+
117253
void TracksBuilder::filter(bool clearForks, std::size_t minTrackLength, bool multithreaded)
118254
{
119255
// remove bad tracks:
@@ -129,14 +265,30 @@ void TracksBuilder::filter(bool clearForks, std::size_t minTrackLength, bool mul
129265
{
130266
#pragma omp single nowait
131267
{
132-
std::size_t cpt = 0;
133-
std::set<std::size_t> myset;
268+
bool flag = false;
269+
stl::flat_map<size_t, IndexedFeaturePair> myset;
134270
for (lemon::UnionFindEnum<IndexMap>::ItemIt iit(*_d->tracksUF, cit); iit != INVALID; ++iit)
135271
{
136-
myset.insert(_d->map_nodeToIndex[iit].first);
137-
++cpt;
272+
IndexedFeaturePair currentPair = _d->map_nodeToIndex[iit];
273+
{
274+
const auto& duplicateIt = _duplicateFeaturesMap.find(currentPair);
275+
if (duplicateIt != _duplicateFeaturesMap.end())
276+
currentPair.second.featIndex = duplicateIt->second;
277+
}
278+
const auto& myIt = myset.find(currentPair.first);
279+
if (myIt != myset.end())
280+
{
281+
if (myIt->second < currentPair || currentPair < myIt->second)
282+
{
283+
flag = true;
284+
}
285+
}
286+
else
287+
{
288+
myset[currentPair.first] = currentPair;
289+
}
138290
}
139-
if ((clearForks && myset.size() != cpt) || myset.size() < minTrackLength)
291+
if ((clearForks && flag) || myset.size() < minTrackLength)
140292
{
141293
#pragma omp critical
142294
set_classToErase.insert(cit.operator int());
@@ -186,7 +338,12 @@ void TracksBuilder::exportToSTL(TracksMap& allTracks) const
186338
const IndexedFeaturePair& currentPair = _d->map_nodeToIndex.at(iit);
187339
// all descType inside the track will be the same
188340
outTrack.descType = currentPair.second.descType;
189-
outTrack.featPerView[currentPair.first] = currentPair.second.featIndex;
341+
// Warning: overwrites featureIndex if clearForks is False
342+
const auto& duplicateIt = _duplicateFeaturesMap.find(currentPair);
343+
if (duplicateIt != _duplicateFeaturesMap.end())
344+
outTrack.featPerView[currentPair.first] = duplicateIt->second;
345+
else
346+
outTrack.featPerView[currentPair.first] = currentPair.second.featIndex;
190347
}
191348
}
192349
}

src/aliceVision/track/TracksBuilder.hpp

+13
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@
77
#pragma once
88

99
#include <aliceVision/track/Track.hpp>
10+
#include <aliceVision/feature/FeaturesPerView.hpp>
1011

1112
#include <memory>
1213

1314
namespace aliceVision {
1415
namespace track {
1516

17+
// IndexedFeaturePair is: pair<viewId, keypointId>
18+
using IndexedFeaturePair = std::pair<std::size_t, KeypointId>;
19+
1620
struct TracksBuilderData;
1721

1822
/**
@@ -53,6 +57,14 @@ class TracksBuilder
5357
*/
5458
void build(const PairwiseMatches& pairwiseMatches);
5559

60+
/**
61+
* @brief Build tracks for a given series of pairWise matches,
62+
* also merge tracks based on duplicate features
63+
* @param[in] pairwiseMatches PairWise matches
64+
* @param[in] featuresPerView Map Features Per View, used to get duplicate features
65+
*/
66+
void build(const PairwiseMatches& pairwiseMatches, const feature::MapFeaturesPerView& featuresPerView);
67+
5668
/**
5769
* @brief Remove bad tracks (too short or track with ids collision)
5870
* @param[in] clearForks: remove tracks with multiple observation in a single image
@@ -82,6 +94,7 @@ class TracksBuilder
8294

8395
private:
8496
std::unique_ptr<TracksBuilderData> _d;
97+
stl::flat_map<IndexedFeaturePair, size_t> _duplicateFeaturesMap;
8598
};
8699

87100
} // namespace track

0 commit comments

Comments
 (0)