Skip to content

Commit d0ed530

Browse files
author
Fabien Servant
committed
Use constraint points in expansion
1 parent 47f1a58 commit d0ed530

File tree

7 files changed

+311
-6
lines changed

7 files changed

+311
-6
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// This file is part of the AliceVision project.
2+
// Copyright (c) 2025 AliceVision contributors.
3+
// This Source Code Form is subject to the terms of the Mozilla Public License,
4+
// v. 2.0. If a copy of the MPL was not distributed with this file,
5+
// You can obtain one at https://mozilla.org/MPL/2.0/.
6+
7+
#pragma once
8+
9+
#include <aliceVision/camera/camera.hpp>
10+
11+
namespace aliceVision {
12+
namespace sfm {
13+
14+
struct ConstraintPointErrorFunctor
15+
{
16+
explicit ConstraintPointErrorFunctor(const Vec3 & normal, const Vec3 & point)
17+
: _normal(normal)
18+
{
19+
_constraintDistance = _normal.dot(point);
20+
}
21+
22+
template<typename T>
23+
bool operator()(T const* const* parameters, T* residuals) const
24+
{
25+
const T* parameter_point = parameters[0];
26+
27+
T distance = parameter_point[0] * _normal[0] + parameter_point[1] * _normal[1] + parameter_point[2] * _normal[2];
28+
residuals[0] = 100.0 * (distance - _constraintDistance);
29+
30+
return true;
31+
}
32+
33+
Vec3 _normal;
34+
double _constraintDistance;
35+
};
36+
37+
} // namespace sfm
38+
} // namespace aliceVision

src/aliceVision/sfm/pipeline/expanding/ExpansionChunk.cpp

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ bool ExpansionChunk::process(sfmData::SfMData & sfmData, const track::TracksHand
8686
return false;
8787
}
8888

89+
if (_pointFetcherHandler)
90+
{
91+
setConstraints(sfmData, tracksHandler, validViewIds);
92+
}
93+
8994
if (_historyHandler)
9095
{
9196
_historyHandler->saveState(sfmData);
@@ -158,6 +163,125 @@ void ExpansionChunk::addPose(sfmData::SfMData & sfmData, IndexT viewId, const Ei
158163
sfmData.setPose(v, cpose);
159164
}
160165

166+
void ExpansionChunk::setConstraints(sfmData::SfMData & sfmData, const track::TracksHandler & tracksHandler, const std::set<IndexT> & viewIds)
167+
{
168+
ALICEVISION_LOG_INFO("ExpansionChunk::setConstraints start");
169+
const track::TracksMap & tracks = tracksHandler.getAllTracks();
170+
const track::TracksPerView & tracksPerView = tracksHandler.getTracksPerView();
171+
172+
const sfmData::Landmarks & landmarks = sfmData.getLandmarks();
173+
sfmData::ConstraintsPoint & constraints = sfmData.getConstraintsPoint();
174+
175+
std::map<IndexT, std::vector<std::pair<Vec3, Vec3>>> infoPerLandmark;
176+
177+
// Fetch all points and normals and store them for further voting
178+
for (const auto & viewId: sfmData.getValidViews())
179+
{
180+
const sfmData::View & v = sfmData.getView(viewId);
181+
const sfmData::CameraPose & cp = sfmData.getAbsolutePose(v.getPoseId());
182+
const camera::IntrinsicBase & intrinsics = *sfmData.getIntrinsics().at(v.getIntrinsicId());
183+
184+
_pointFetcherHandler->setPose(cp.getTransform());
185+
186+
const auto & trackIds = tracksPerView.at(viewId);
187+
for (const auto trackId : trackIds)
188+
{
189+
const track::Track & track = tracks.at(trackId);
190+
const track::TrackItem & trackItem = track.featPerView.at(viewId);
191+
192+
if (landmarks.find(trackId) == landmarks.end())
193+
{
194+
continue;
195+
}
196+
197+
Vec3 point, normal;
198+
if (!_pointFetcherHandler->peekPointAndNormal(point, normal, intrinsics, trackItem.coords))
199+
{
200+
continue;
201+
}
202+
203+
infoPerLandmark[trackId].push_back(std::make_pair(point, normal));
204+
}
205+
}
206+
207+
//Find the consensus
208+
const double maxDist = 0.1;
209+
const double maxDistLandmark = 1.0;
210+
const double cosMaxAngle = cos(M_PI_4);
211+
212+
for (const auto & [trackId, vecInfo] : infoPerLandmark)
213+
{
214+
if (vecInfo.size() == 0)
215+
{
216+
continue;
217+
}
218+
219+
int idBest = -1;
220+
int countBest = -1;
221+
222+
223+
//Consider each point
224+
for (int idRef = 0; idRef < vecInfo.size(); idRef++)
225+
{
226+
const Vec3 & refpt = vecInfo[idRef].first;
227+
const Vec3 & refnormal = vecInfo[idRef].second;
228+
229+
//Compare it with all other points
230+
int count = 0;
231+
for (int idCur = 0; idCur < vecInfo.size(); idCur++)
232+
{
233+
if (idCur == idRef)
234+
{
235+
continue;
236+
}
237+
238+
const Vec3 & curpt = vecInfo[idRef].first;
239+
const Vec3 & curnormal = vecInfo[idRef].second;
240+
241+
double dist = (refpt - curpt).norm();
242+
if (dist > maxDist)
243+
{
244+
continue;
245+
}
246+
247+
if (curnormal.dot(refnormal) < cosMaxAngle)
248+
{
249+
continue;
250+
}
251+
252+
count++;
253+
}
254+
255+
if (count > countBest)
256+
{
257+
idBest = idRef;
258+
countBest = count;
259+
}
260+
}
261+
262+
const auto & landmark = landmarks.at(trackId);
263+
const Vec3 point = vecInfo[idBest].first;
264+
const Vec3 normal = vecInfo[idBest].second;
265+
266+
double dist = (point - landmark.X).norm();
267+
if (dist > maxDistLandmark)
268+
{
269+
continue;
270+
}
271+
272+
if (idBest < 0)
273+
{
274+
ALICEVISION_THROW_ERROR("Impossible value");
275+
}
276+
277+
sfmData::ConstraintPoint cp(trackId, normal, point);
278+
constraints[trackId] = cp;
279+
}
280+
281+
ALICEVISION_LOG_INFO("ExpansionChunk::setConstraints added " << constraints.size() << " constraints");
282+
ALICEVISION_LOG_INFO("ExpansionChunk::setConstraints end");
283+
}
284+
161285
} // namespace sfm
162286
} // namespace aliceVision
163287

src/aliceVision/sfm/pipeline/expanding/ExpansionChunk.hpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <aliceVision/sfmData/SfMData.hpp>
1212
#include <aliceVision/sfm/pipeline/expanding/ExpansionHistory.hpp>
1313
#include <aliceVision/sfm/pipeline/expanding/SfmBundle.hpp>
14+
#include <aliceVision/sfm/pipeline/expanding/PointFetcher.hpp>
1415

1516
namespace aliceVision {
1617
namespace sfm {
@@ -19,7 +20,7 @@ class ExpansionChunk
1920
{
2021
public:
2122
using uptr = std::unique_ptr<ExpansionChunk>;
22-
23+
2324
public:
2425

2526
/**
@@ -51,6 +52,15 @@ class ExpansionChunk
5152
_historyHandler = expansionHistory;
5253
}
5354

55+
/**
56+
* brief setup the point fetcher handler
57+
* @param pointFetcher a unique ptr. the Ownership will be taken
58+
*/
59+
void setPointFetcherHandler(PointFetcher::uptr & pointFetcherHandler)
60+
{
61+
_pointFetcherHandler = std::move(pointFetcherHandler);
62+
}
63+
5464
void setResectionMaxIterations(size_t maxIterations)
5565
{
5666
_resectionIterations = maxIterations;
@@ -106,9 +116,18 @@ class ExpansionChunk
106116
*/
107117
bool triangulate(sfmData::SfMData & sfmData, const track::TracksHandler & tracksHandler, const std::set<IndexT> & viewIds);
108118

119+
/**
120+
* @brief Add constraints on points
121+
* @param sfmData the object to update
122+
* @param tracks all tracks of the scene as a map {trackId, track}
123+
* @param viewIds the set of views to process
124+
*/
125+
void setConstraints(sfmData::SfMData & sfmData, const track::TracksHandler & tracksHandler, const std::set<IndexT> & viewIds);
126+
109127
private:
110128
SfmBundle::uptr _bundleHandler;
111129
ExpansionHistory::sptr _historyHandler;
130+
PointFetcher::uptr _pointFetcherHandler;
112131

113132
private:
114133
size_t _resectionIterations = 1024;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// This file is part of the AliceVision project.
2+
// Copyright (c) 2025 AliceVision contributors.
3+
// This Source Code Form is subject to the terms of the Mozilla Public License,
4+
// v. 2.0. If a copy of the MPL was not distributed with this file,
5+
// You can obtain one at https://mozilla.org/MPL/2.0/.
6+
7+
#pragma once
8+
9+
#include <aliceVision/numeric/numeric.hpp>
10+
#include <aliceVision/camera/IntrinsicBase.hpp>
11+
12+
namespace aliceVision
13+
{
14+
namespace sfm
15+
{
16+
17+
class PointFetcher
18+
{
19+
public:
20+
using uptr = std::unique_ptr<PointFetcher>;
21+
22+
public:
23+
/**
24+
* Set the pose of the camera
25+
* @param the pose of the camera wrt some global coordinates frame
26+
*/
27+
virtual void setPose(const geometry::Pose3 & pose) = 0;
28+
29+
/**
30+
* @brief virtual method to get coordinates and normals of a pixel of an image
31+
* @param point result point in some global coordinates frame
32+
* @param normal result normal in some global coordinates frame
33+
* @param pose pose of the camera wrt some global coordinates frame
34+
* @param intrinsic the camera intrinsic object
35+
* @param imageCoords the input image pixel coordinates in 2D.
36+
* @return false on error
37+
*/
38+
virtual bool peekPointAndNormal(Vec3 & point,
39+
Vec3 & normal,
40+
const camera::IntrinsicBase & intrinsic,
41+
const Vec2 & imageCoords) = 0;
42+
};
43+
44+
}
45+
}

src/aliceVision/sfm/pipeline/expanding/SfmResection.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,12 @@ bool SfmResection::processView(
9191
}
9292

9393
//Refine the pose
94-
if (!internalRefinement(structure, observations, inliers, pose, intrinsic))
94+
if (!internalRefinement(structure, observations, inliers, pose, intrinsic, errorMax))
9595
{
9696
ALICEVISION_LOG_INFO("SfmResection::processView internalRefinemanet failed " << viewId);
9797
return false;
9898
}
99+
99100

100101
updatedThreshold = errorMax;
101102
updatedPose = pose;
@@ -140,7 +141,8 @@ bool SfmResection::internalRefinement(
140141
const std::vector<Eigen::Vector2d> & observations,
141142
const std::vector<size_t> & inliers,
142143
Eigen::Matrix4d & pose,
143-
std::shared_ptr<camera::IntrinsicBase> & intrinsics)
144+
std::shared_ptr<camera::IntrinsicBase> & intrinsics,
145+
double & errorMax)
144146
{
145147
// Setup a tiny SfM scene with the corresponding 2D-3D data
146148
sfmData::SfMData tinyScene;
@@ -155,7 +157,7 @@ bool SfmResection::internalRefinement(
155157
// Intrinsics
156158
tinyScene.getIntrinsics().emplace(0, intrinsics);
157159

158-
const double unknownScale = 0.0;
160+
const double unknownScale = 1.0;
159161

160162
// structure data (2D-3D correspondences)
161163
for(std::size_t i = 0; i < inliers.size(); ++i)
@@ -179,8 +181,21 @@ bool SfmResection::internalRefinement(
179181

180182
pose = tinyScene.getPose(*view).getTransform().getHomogeneous();
181183

184+
// Compute errorMax
185+
errorMax = 0.0;
186+
for(std::size_t i = 0; i < inliers.size(); ++i)
187+
{
188+
const std::size_t idx = inliers[i];
189+
190+
Vec2 est = intrinsics->transformProject(pose, structure[idx].homogeneous(), true);
191+
Vec2 diff = observations[idx] - est;
192+
193+
errorMax = std::max(errorMax, diff.norm());
194+
}
195+
196+
182197
return true;
183198
}
184199

185200
} // namespace sfm
186-
} // namespace aliceVision
201+
} // namespace aliceVision

src/aliceVision/sfm/pipeline/expanding/SfmResection.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ class SfmResection
6161
const std::vector<Eigen::Vector2d> & observations,
6262
const std::vector<size_t> & inliers,
6363
Eigen::Matrix4d & pose,
64-
std::shared_ptr<camera::IntrinsicBase> & intrinsics
64+
std::shared_ptr<camera::IntrinsicBase> & intrinsics,
65+
double & errorMax
6566
);
6667

6768
private:

0 commit comments

Comments
 (0)