Skip to content

Commit 10c8463

Browse files
committed
[filterSfM] add support for filtering landmarks
1 parent c77ad5c commit 10c8463

File tree

1 file changed

+93
-52
lines changed

1 file changed

+93
-52
lines changed

src/software/pipeline/main_filterSfM.cpp

Lines changed: 93 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -45,30 +45,32 @@ namespace fs = boost::filesystem;
4545

4646
static const std::size_t MAX_LEAF_ELEMENTS = 10;
4747

48-
template <typename DATA>
49-
struct PointVectorAdaptator
48+
struct LandmarksAdaptator
5049
{
51-
using Derived = PointVectorAdaptator; //!< In this case the dataset class is myself.
50+
using Derived = LandmarksAdaptator; //!< In this case the dataset class is myself.
5251
using T = double;
5352

54-
const DATA& _data;
55-
PointVectorAdaptator(const DATA& data)
56-
: _data(data)
57-
{
58-
}
59-
6053
/// CRTP helper method
6154
inline const Derived& derived() const { return *static_cast<const Derived*>(this); }
6255
/// CRTP helper method
6356
inline Derived& derived() { return *static_cast<Derived*>(this); }
6457

58+
const sfmData::Landmarks& _data;
59+
LandmarksAdaptator(const sfmData::Landmarks& data)
60+
: _data(data)
61+
{
62+
}
63+
6564
// Must return the number of data points
6665
inline size_t kdtree_get_point_count() const { return _data.size(); }
6766

6867
// Returns the dim'th component of the idx'th point in the class:
69-
// Since this is inlined and the "dim" argument is typically an immediate value, the
70-
// "if/else's" are actually solved at compile time.
71-
inline T kdtree_get_pt(const size_t idx, int dim) const { return _data.at(idx).m[dim]; }
68+
inline T kdtree_get_pt(const size_t idx, int dim) const
69+
{
70+
auto it = _data.begin();
71+
std::advance(it, idx);
72+
return it->second.X(dim);
73+
}
7274

7375
// Optional bounding-box computation: return false to default to a standard bbox computation loop.
7476
// Return true if the BBOX was already computed by the class and returned in "bb" so it can be avoided to redo it
@@ -80,50 +82,52 @@ struct PointVectorAdaptator
8082
}
8183
};
8284

83-
template <typename DATA>
8485
using KdTree = nanoflann::KDTreeSingleIndexAdaptor<
85-
nanoflann::L2_Simple_Adaptor<double, PointVectorAdaptator>,
86-
PointVectorAdaptator, 3 /* dim */
86+
nanoflann::L2_Simple_Adaptor<double, LandmarksAdaptator>,
87+
LandmarksAdaptator,
88+
3, /* dim */
89+
IndexT
8790
>;
8891

89-
template <typename DATA>
90-
class Tree
92+
/**
93+
* A result-set class used when performing a radius based search.
94+
*/
95+
class PixSizeSearch
9196
{
92-
std::unique_ptr<KdTree> _tree;
93-
std::unique_ptr<PointVectorAdaptator> _pointCloudRef;
94-
9597
public:
96-
Tree(const DATA& data) { initKdTree(data); }
97-
98-
void initKdTree(const DATA& data)
98+
const double _radius_sq;
99+
const std::vector<double>& _pixSize;
100+
const double _pixSize_i;
101+
bool found = false;
102+
103+
inline PixSizeSearch(double radius, const std::vector<double>& pixSize, int i)
104+
: _radius_sq(radius * radius)
105+
, _pixSize(pixSize)
106+
, _pixSize_i(pixSize[i])
99107
{
100-
ALICEVISION_LOG_INFO("Build nanoflann KdTree index.");
101-
_pointCloudRef = std::make_unique<PointVectorAdaptator>(data);
102-
_tree = std::make_unique<KdTree>(3 /*dim*/, *_pointCloudRef.get(),
103-
nanoflann::KDTreeSingleIndexAdaptorParams(MAX_LEAF_ELEMENTS));
104-
_tree->buildIndex();
105-
ALICEVISION_LOG_INFO("KdTree created for " << data.size() << " points.");
106108
}
107109

108-
bool locateNearestVertex(const Point3d& p, std::size_t& index, double& sq_dist) const
110+
inline bool full() const { return found; }
111+
112+
inline bool addPoint(double dist, IndexT index)
109113
{
110-
index = std::numeric_limits<std::size_t>::max();
111-
sq_dist = std::numeric_limits<double>::max();
112-
nanoflann::KNNResultSet<double, std::size_t> resultSet(1);
113-
resultSet.init(&index, &sq_dist);
114-
if(!_tree->findNeighbors(resultSet, p.m, nanoflann::SearchParams()))
114+
if(dist < _radius_sq && _pixSize[index] < _pixSize_i)
115115
{
116+
found = true;
116117
return false;
117118
}
118119
return true;
119120
}
121+
122+
inline double worstDist() const { return _radius_sq; }
120123
};
121124

122-
bool filterLandmarks(SfMData& sfmData)
125+
bool filterLandmarks(SfMData& sfmData, double radiusScale)
123126
{
124127
mvsUtils::MultiViewParams mp(sfmData, "", "", "", false);
125-
std::vector<std::pair<double, IndexT>> landmarksPixSize(sfmData.getLandmarks().size());
128+
std::vector<double> landmarksPixSize(sfmData.getLandmarks().size());
126129

130+
ALICEVISION_LOG_INFO("Computing pixel size: started.");
127131
#pragma omp parallel for
128132
for(auto i = 0; i < sfmData.getLandmarks().size(); i++)
129133
{
@@ -137,26 +141,56 @@ bool filterLandmarks(SfMData& sfmData)
137141
for(const auto& observationPair : landmark.observations)
138142
{
139143
const IndexT viewId = observationPair.first;
140-
auto d = mp.getCamPixelSize(Point3d(landmark.X.x(), landmark.X.y(), landmark.X.z()),
144+
pixSize += mp.getCamPixelSize(Point3d(landmark.X.x(), landmark.X.y(), landmark.X.z()),
141145
mp.getIndexFromViewId(viewId), observationPair.second.scale);
142-
if(d < 0.)
143-
d = 0.;
144-
pixSize += d;
145-
if(pixSize < 0.)
146-
pixSize = 0.;
147146
n++;
148147
}
149-
if(pixSize < 0.)
150-
pixSize = 0.;
151148
pixSize /= n;
152-
if(pixSize < 0.)
153-
pixSize = 0.;
154149

155-
landmarksPixSize[i] = std::pair<double, IndexT>(pixSize, landmarkPair->first);
150+
landmarksPixSize[i] = pixSize;
156151
}
152+
ALICEVISION_LOG_INFO("Computing pixel size: done.");
153+
154+
//// sort landmarks by descending pixSize order
155+
//std::stable_sort(landmarksPixSize.begin(), landmarksPixSize.end(), std::greater<>{});
157156

158-
// sort landmarks by descending pixSize order
159-
std::stable_sort(landmarksPixSize.begin(), landmarksPixSize.end(), std::greater<>{});
157+
ALICEVISION_LOG_INFO("Build nanoflann KdTree index.");
158+
LandmarksAdaptator data(sfmData.getLandmarks());
159+
KdTree tree(3, data, nanoflann::KDTreeSingleIndexAdaptorParams(10));
160+
tree.buildIndex();
161+
ALICEVISION_LOG_INFO("KdTree created for " << sfmData.getLandmarks().size() << " points.");
162+
std::vector<IndexT> newIdx(sfmData.getLandmarks().size());
163+
IndexT currentIdx = 0;
164+
165+
ALICEVISION_LOG_INFO("Identifying landmarks to remove: started.");
166+
#pragma omp parallel for
167+
for (auto i = 0; i < sfmData.getLandmarks().size(); i++)
168+
{
169+
PixSizeSearch search(landmarksPixSize[i] * radiusScale, landmarksPixSize, i);
170+
bool found = tree.findNeighbors(search, sfmData.getLandmarks().at(i).X.data(), nanoflann::SearchParams());
171+
if (!found)
172+
{
173+
newIdx[i] = -1;
174+
}
175+
else
176+
{
177+
newIdx[i] = currentIdx++;
178+
}
179+
}
180+
ALICEVISION_LOG_INFO("Identifying landmarks to remove: done.");
181+
182+
ALICEVISION_LOG_INFO("Removing landmarks: started.");
183+
Landmarks filteredLandmarks;
184+
#pragma omp parallel for
185+
for (auto i = 0; i < sfmData.getLandmarks().size(); i++)
186+
{
187+
if(newIdx[i] != -1)
188+
{
189+
filteredLandmarks[newIdx[i]] = sfmData.getLandmarks().at(i);
190+
}
191+
}
192+
sfmData.getLandmarks() = filteredLandmarks;
193+
ALICEVISION_LOG_INFO("Removing landmarks: done.");
160194

161195
//// take only best observations
162196
//observationScores.resize(maxNbObservationsPerLandmark);
@@ -230,6 +264,7 @@ int aliceVision_main(int argc, char *argv[])
230264
std::string inputSfmFilename;
231265
std::string outputSfmFilename;
232266
int maxNbObservationsPerLandmark = 5;
267+
double radiusScale = 2;
233268

234269
po::options_description requiredParams("Required parameters");
235270
requiredParams.add_options()
@@ -241,7 +276,9 @@ int aliceVision_main(int argc, char *argv[])
241276
po::options_description optionalParams("Optional parameters");
242277
optionalParams.add_options()
243278
("maxNbObservationsPerLandmark", po::value<int>(&maxNbObservationsPerLandmark)->default_value(maxNbObservationsPerLandmark),
244-
"Maximum number of allowed observations per landmark.");
279+
"Maximum number of allowed observations per landmark.")
280+
("radiusScale", po::value<double>(&radiusScale)->default_value(radiusScale),
281+
"Scale factor applied to pixel size based radius filter applied to landmarks.");
245282

246283
CmdLine cmdline("AliceVision SfM filtering.");
247284
cmdline.add(requiredParams);
@@ -267,8 +304,12 @@ int aliceVision_main(int argc, char *argv[])
267304
}
268305

269306
// filter SfM data
270-
bool success2 = filterLandmarks(sfmData);
307+
ALICEVISION_LOG_INFO("Filtering landmarks: started.");
308+
bool success2 = filterLandmarks(sfmData, radiusScale);
309+
ALICEVISION_LOG_INFO("Filtering landmarks: done.");
310+
ALICEVISION_LOG_INFO("Filtering observations: started.");
271311
bool success1 = filterObservations(sfmData, maxNbObservationsPerLandmark);
312+
ALICEVISION_LOG_INFO("Filtering observations: done.");
272313

273314
if(success1)
274315
{

0 commit comments

Comments
 (0)