Skip to content

Commit ffdc025

Browse files
author
Fabien Servant
committed
Bootstrap with prior depth
1 parent 14b0b8f commit ffdc025

File tree

10 files changed

+589
-80
lines changed

10 files changed

+589
-80
lines changed

meshroom/aliceVision/SfmBootstrapping.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ class SfMBootStrapping(desc.AVCommandLineNode):
1818
description="SfMData file.",
1919
value="",
2020
),
21+
desc.ChoiceParam(
22+
name="method",
23+
label="Method",
24+
description="Bootstrapping method: classic (epipolar geometry), mesh (3D mesh constraints), or depth (depth map information).",
25+
values=["classic", "mesh", "depth"],
26+
value="classic",
27+
),
2128
desc.File(
2229
name="tracksFilename",
2330
label="Tracks File",
@@ -29,6 +36,7 @@ class SfMBootStrapping(desc.AVCommandLineNode):
2936
label="Mesh File",
3037
description="Mesh file (*.obj).",
3138
value="",
39+
enabled=lambda node: node.method.value == "mesh"
3240
),
3341
desc.File(
3442
name="pairs",

src/aliceVision/sfm/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ set(sfm_files_headers
2727
pipeline/bootstrapping/EstimateAngle.hpp
2828
pipeline/bootstrapping/PairsScoring.hpp
2929
pipeline/bootstrapping/Bootstrap.hpp
30+
pipeline/bootstrapping/TracksDepths.hpp
3031
pipeline/expanding/SfmTriangulation.hpp
3132
pipeline/expanding/SfmResection.hpp
3233
pipeline/expanding/SfmBundle.hpp
@@ -74,6 +75,7 @@ set(sfm_files_sources
7475
pipeline/bootstrapping/EstimateAngle.cpp
7576
pipeline/bootstrapping/PairsScoring.cpp
7677
pipeline/bootstrapping/Bootstrap.cpp
78+
pipeline/bootstrapping/TracksDepths.cpp
7779
pipeline/expanding/SfmTriangulation.cpp
7880
pipeline/expanding/SfmResection.cpp
7981
pipeline/expanding/SfmBundle.cpp

src/aliceVision/sfm/pipeline/bootstrapping/Bootstrap.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <aliceVision/multiview/triangulation/triangulationDLT.hpp>
1111
#include <aliceVision/sfm/pipeline/expanding/SfmResection.hpp>
1212
#include <aliceVision/sfm/pipeline/expanding/LocalizationValidationPolicyLegacy.hpp>
13+
#include <aliceVision/sfm/pipeline/bootstrapping/TracksDepths.hpp>
1314
#include <vector>
1415
#include <random>
1516

@@ -174,5 +175,102 @@ bool bootstrapMesh(sfmData::SfMData & sfmData,
174175
return true;
175176
}
176177

178+
bool bootstrapDepth(sfmData::SfMData & sfmData,
179+
const IndexT referenceViewId,
180+
const IndexT nextViewId,
181+
const track::TracksMap& tracksMap,
182+
const track::TracksPerView & tracksPerView)
183+
{
184+
std::mt19937 randomNumberGenerator;
185+
186+
const sfmData::View & viewReference = sfmData.getView(referenceViewId);
187+
const sfmData::View & viewNext = sfmData.getView(nextViewId);
188+
189+
camera::IntrinsicBase::sptr camReference = sfmData.getIntrinsicSharedPtr(viewReference.getIntrinsicId());
190+
camera::IntrinsicBase::sptr camNext = sfmData.getIntrinsicSharedPtr(viewNext.getIntrinsicId());
191+
192+
sfmData::CameraPose & poseReference = sfmData.getPoses()[viewReference.getPoseId()];
193+
sfmData::CameraPose & poseNext = sfmData.getPoses()[viewNext.getPoseId()];
194+
195+
196+
sfmData::SfMData miniSfm;
197+
if (!buildSfmDataFromDepthMap(miniSfm, sfmData, tracksMap, tracksPerView, referenceViewId))
198+
{
199+
return false;
200+
}
201+
202+
//Compute resection for selected view
203+
sfm::LocalizationValidationPolicy::uptr resectionValidationPolicy = std::make_unique<sfm::LocalizationValidationPolicyLegacy>();
204+
205+
sfm::SfmResection resection;
206+
resection.setMaxIterations(50000);
207+
resection.setResectionMaxError(std::numeric_limits<double>::infinity());
208+
resection.setValidationPolicy(resectionValidationPolicy);
209+
210+
Eigen::Matrix4d pose;
211+
double newThreshold;
212+
size_t inliersCount;
213+
214+
if (!resection.processView(miniSfm,
215+
tracksMap, tracksPerView,
216+
randomNumberGenerator,
217+
nextViewId, pose, newThreshold, inliersCount))
218+
{
219+
return false;
220+
}
221+
222+
223+
geometry::Pose3 pose3(pose);
224+
poseNext.setTransform(pose3);
225+
226+
const auto & landmarks = miniSfm.getLandmarks();
227+
auto & outLandmarks = sfmData.getLandmarks();
228+
229+
for (const auto & [landmarkId, landmark] : landmarks)
230+
{
231+
//Retrieve track object
232+
const auto & track = tracksMap.at(landmarkId);
233+
234+
const track::TrackItem & itemReference = track.featPerView.at(referenceViewId);
235+
236+
//Maybe this track is not observed in the next view
237+
if (track.featPerView.find(nextViewId) == track.featPerView.end())
238+
{
239+
continue;
240+
}
241+
242+
//Compute error
243+
const track::TrackItem & item = track.featPerView.at(nextViewId);
244+
const Vec2 pt = item.coords;
245+
const Vec2 estpt = camNext->transformProject(pose3, landmark.X.homogeneous(), true);
246+
double err = (pt - estpt).norm();
247+
248+
//If error is ok, then we add it to the sfmData
249+
if (err <= newThreshold)
250+
{
251+
sfmData::Observation obs;
252+
obs.setFeatureId(item.featureId);
253+
obs.setScale(item.scale);
254+
obs.setCoordinates(item.coords);
255+
256+
sfmData::Observation obsReference;
257+
obsReference.setFeatureId(itemReference.featureId);
258+
obsReference.setScale(itemReference.scale);
259+
obsReference.setCoordinates(itemReference.coords);
260+
261+
//Add landmark to sfmData
262+
outLandmarks[landmarkId] = landmark;
263+
outLandmarks[landmarkId].setParallaxRobust(true);
264+
265+
//Add observation to landmark
266+
sfmData::Observations & observations = outLandmarks[landmarkId].getObservations();
267+
observations[referenceViewId] = obsReference;
268+
observations[nextViewId] = obs;
269+
}
270+
}
271+
272+
return true;
273+
}
274+
177275
}
178276
}

src/aliceVision/sfm/pipeline/bootstrapping/Bootstrap.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,20 @@ bool bootstrapMesh(sfmData::SfMData & sfmData,
4646
const track::TracksMap& tracksMap,
4747
const track::TracksPerView & tracksPerView);
4848

49+
/**
50+
* @brief Create a minimal SfmData with poses and landmarks for two views
51+
* @param sfmData the input sfmData which contains camera information
52+
* @param referenceViewId the reference view id
53+
* @param otherViewId the other view id
54+
* @param tracksMap the input map of tracks
55+
* @param tracksPerView tracks grouped by views
56+
* @return true
57+
*/
58+
bool bootstrapDepth(sfmData::SfMData & sfmData,
59+
const IndexT referenceViewId,
60+
const IndexT otherViewId,
61+
const track::TracksMap& tracksMap,
62+
const track::TracksPerView & tracksPerView);
63+
4964
}
5065
}

src/aliceVision/sfm/pipeline/bootstrapping/PairsScoring.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
#include <aliceVision/sfm/pipeline/bootstrapping/EstimateAngle.hpp>
99
#include <aliceVision/sfm/pipeline/expanding/ExpansionPolicyLegacy.hpp>
1010
#include <aliceVision/multiview/triangulation/triangulationDLT.hpp>
11+
#include <aliceVision/sfm/pipeline/bootstrapping/TracksDepths.hpp>
12+
#include <aliceVision/sfm/pipeline/expanding/SfmResection.hpp>
13+
#include <aliceVision/sfm/pipeline/expanding/LocalizationValidationPolicyLegacy.hpp>
1114

1215
namespace aliceVision {
1316
namespace sfm {
@@ -116,5 +119,110 @@ IndexT findBestPair(const sfmData::SfMData & sfmData,
116119
return bestPair;
117120
}
118121

122+
sfm::ReconstructedPair findBestPairFromTrackDepths(const sfmData::SfMData & sfmData,
123+
const std::vector<sfm::ReconstructedPair> & pairs,
124+
const track::TracksMap& tracksMap,
125+
const track::TracksPerView & tracksPerView,
126+
std::mt19937 & randomNumberGenerator)
127+
{
128+
//Create set of unique view ids
129+
std::set<IndexT> views;
130+
for (IndexT pairId = 0; pairId < pairs.size(); pairId++)
131+
{
132+
views.insert(pairs[pairId].reference);
133+
views.insert(pairs[pairId].next);
134+
}
135+
136+
sfm::ReconstructedPair bestPair;
137+
bestPair.reference = UndefinedIndexT;
138+
size_t bestCount = 0;
139+
140+
sfm::LocalizationValidationPolicy::uptr resectionValidationPolicy = std::make_unique<sfm::LocalizationValidationPolicyLegacy>();
141+
142+
sfm::SfmResection resection;
143+
resection.setMaxIterations(1024);
144+
resection.setResectionMaxError(std::numeric_limits<double>::infinity());
145+
resection.setValidationPolicy(resectionValidationPolicy);
146+
147+
//Loop over all views relatively located to another view
148+
for (const auto & idView: views)
149+
{
150+
//Build a local sfm where the landmarks are the points on the depthmap
151+
sfmData::SfMData miniSfm;
152+
if (!buildSfmDataFromDepthMap(miniSfm, sfmData, tracksMap, tracksPerView, idView))
153+
{
154+
continue;
155+
}
156+
157+
sfm::ReconstructedPair bestPairLocal;
158+
size_t maxInliers = 0;
159+
size_t totalInliers = 0;
160+
161+
//Loop over all other views which are linked to this view
162+
for (IndexT pairId = 0; pairId < pairs.size(); pairId++)
163+
{
164+
const auto & pairSource = pairs[pairId];
165+
sfm::ReconstructedPair pair;
166+
pair.reference = idView;
167+
168+
169+
// Create the correct pair with order
170+
if (pairSource.reference == idView)
171+
{
172+
pair.next = pairSource.next;
173+
}
174+
else if (pairSource.next == idView)
175+
{
176+
pair.next = pairSource.reference;
177+
}
178+
else
179+
{
180+
continue;
181+
}
182+
183+
//Make sure the local sfm has this view
184+
if (miniSfm.getViews().find(pair.next) == miniSfm.getViews().end())
185+
{
186+
continue;
187+
}
188+
189+
//Try to locate this view in the local sfm
190+
Eigen::Matrix4d pose;
191+
double newThreshold;
192+
size_t inliersCount;
193+
if (!resection.processView(miniSfm,
194+
tracksMap,
195+
tracksPerView,
196+
randomNumberGenerator,
197+
pair.next,
198+
pose, newThreshold, inliersCount))
199+
{
200+
continue;
201+
}
202+
203+
//Keep the best second view
204+
pair.pose = geometry::Pose3(pose);
205+
pair.score = inliersCount;
206+
if (inliersCount > maxInliers)
207+
{
208+
maxInliers = inliersCount;
209+
bestPairLocal = pair;
210+
}
211+
212+
totalInliers += inliersCount;
213+
}
214+
215+
//Keep the best pair
216+
if (totalInliers > bestCount)
217+
{
218+
bestPair = bestPairLocal;
219+
bestCount = totalInliers;
220+
}
221+
}
222+
223+
224+
return bestPair;
225+
}
226+
119227
}
120228
}

src/aliceVision/sfm/pipeline/bootstrapping/PairsScoring.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,20 @@ IndexT findBestPair(const sfmData::SfMData & sfmData,
3737
double softMinAngle,
3838
double maxAngle);
3939

40+
/**
41+
* @brief Get best pair from track Depths with highest score
42+
* @param sfmData the input sfmData which contains camera information
43+
* @param pairs the input list of reconstructed pairs
44+
* @param tracksMap the input map of tracks
45+
* @param tracksPerView tracks grouped by views
46+
* @param randomNumberGenerator the random number generator used for drawing numbers
47+
* @return The best pair (pair.reference is UndefinedIndexT if nothing found)
48+
*/
49+
sfm::ReconstructedPair findBestPairFromTrackDepths(const sfmData::SfMData & sfmData,
50+
const std::vector<sfm::ReconstructedPair> & pairs,
51+
const track::TracksMap& tracksMap,
52+
const track::TracksPerView & tracksPerView,
53+
std::mt19937 & randomNumberGenerator);
54+
4055
}
4156
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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+
#include <aliceVision/sfm/pipeline/bootstrapping/TracksDepths.hpp>
8+
9+
namespace aliceVision {
10+
namespace sfm {
11+
12+
bool buildSfmDataFromDepthMap(sfmData::SfMData & output,
13+
const sfmData::SfMData & sfmData,
14+
const track::TracksMap& tracksMap,
15+
const track::TracksPerView & tracksPerView,
16+
IndexT viewId)
17+
{
18+
output.clear();
19+
20+
const sfmData::View & view = sfmData.getView(viewId);
21+
const camera::IntrinsicBase & intrinsic = sfmData.getIntrinsic(view.getIntrinsicId());
22+
23+
if (tracksPerView.find(viewId) == tracksPerView.end())
24+
{
25+
return false;
26+
}
27+
28+
sfmData::Landmarks & landmarks = output.getLandmarks();
29+
30+
//Copy all intrinsics, because it's light.
31+
for (const auto & [intrinsicId, intrinsic] : sfmData.getIntrinsics())
32+
{
33+
output.getIntrinsics().insert(
34+
std::make_pair(intrinsicId,
35+
camera::IntrinsicBase::sptr(intrinsic->clone()))
36+
);
37+
}
38+
39+
std::set<IndexT> usedViewIds;
40+
const auto & trackIds = tracksPerView.at(viewId);
41+
for (const auto & trackId : trackIds)
42+
{
43+
const auto & track = tracksMap.at(trackId);
44+
const auto & feat = track.featPerView.at(viewId);
45+
const double & Z = feat.depth;
46+
47+
if (Z <= 0.0)
48+
{
49+
continue;
50+
}
51+
52+
const Vec2 meters = intrinsic.removeDistortion(intrinsic.ima2cam(feat.coords.cast<double>()));
53+
54+
sfmData::Landmark & landmark = landmarks[trackId];
55+
landmark.X.x() = meters.x() * Z;
56+
landmark.X.y() = meters.y() * Z;
57+
landmark.X.z() = Z;
58+
59+
landmark.descType = track.descType;
60+
61+
for (const auto & [otherViewId, otherFeat] : track.featPerView)
62+
{
63+
usedViewIds.insert(otherViewId);
64+
}
65+
}
66+
67+
// Copy only used views
68+
for (const auto & usedViewId : usedViewIds)
69+
{
70+
const auto & iview = sfmData.getViewSharedPtr(usedViewId);
71+
72+
output.getViews().insert(
73+
std::make_pair(usedViewId,
74+
sfmData::View::sptr(iview->clone()))
75+
);
76+
}
77+
78+
return true;
79+
}
80+
81+
}
82+
}

0 commit comments

Comments
 (0)