Skip to content

Commit fd256ba

Browse files
Merge pull request #1990 from alicevision/dev/fixBootstrap
Fix sfm bootstrap
2 parents c599387 + 157a614 commit fd256ba

File tree

10 files changed

+197
-30
lines changed

10 files changed

+197
-30
lines changed

.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,7 @@ Production
7777
**/.vscode
7878
**/.vs
7979

80-
vcpkg_installed/
81-
export/
80+
/vcpkg_installed/
81+
/export/
82+
/.cache/
83+
/compile_commands.json

meshroom/aliceVision/RelativePoseEstimating.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "3.0"
1+
__version__ = "3.1"
22

33
from meshroom.core import desc
44
from meshroom.core.utils import DESCRIBER_TYPES, VERBOSE_LEVEL
@@ -48,6 +48,14 @@ class RelativePoseEstimating(desc.AVCommandLineNode):
4848
range=(1, 1000, 1),
4949
advanced=True,
5050
),
51+
desc.FloatParam(
52+
name="distanceThreshold",
53+
label="Distance Threshold",
54+
description="Threshold on geometric distance (epipolar distance or reprojection distance for pure rotation)",
55+
value=4.0,
56+
range=(0.0, 50.0, 1.0),
57+
advanced=True,
58+
),
5159
desc.File(
5260
name="imagePairsList",
5361
label="Image Pairs",

src/aliceVision/multiview/triangulation/triangulationDLT.cpp

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,63 @@ void TriangulateDLT(const Mat34& P1, const Vec2& x1, const Mat34& P2, const Vec2
3434
homogeneousToEuclidean(X_homogeneous, X_euclidean);
3535
}
3636

37-
// Solve:
38-
// [cross(x0,P0) X = 0]
39-
// [cross(x1,P1) X = 0]
37+
/**
38+
* let v = R.transpose() * x; (to get a world coordinates direction)
39+
* then lambda * v = X - center
40+
* (v) cross (X - center) = 0
41+
* [v]x*X - [V]x*center) = 0
42+
*/
4043
void TriangulateSphericalDLT(const Mat4& T1, const Vec3& x1, const Mat4& T2, const Vec3& x2, Vec4& X_homogeneous)
4144
{
42-
Mat design(6, 4);
43-
for (int i = 0; i < 4; ++i)
44-
{
45-
design(0, i) = -x1[2] * T1(1, i) + x1[1] * T1(2, i);
46-
design(1, i) = x1[2] * T1(0, i) - x1[0] * T1(2, i);
47-
design(2, i) = -x1[1] * T1(0, i) + x1[0] * T1(1, i);
45+
const Mat3 R1 = T1.block<3, 3>(0, 0);
46+
const Mat3 R2 = T2.block<3, 3>(0, 0);
47+
const Vec3 t1 = T1.block<3, 1>(0, 3);
48+
const Vec3 t2 = T2.block<3, 1>(0, 3);
49+
const Vec3 c1 = -R1.transpose() * t1;
50+
const Vec3 c2 = -R2.transpose() * t2;
4851

49-
design(3, i) = -x2[2] * T2(1, i) + x2[1] * T2(2, i);
50-
design(4, i) = x2[2] * T2(0, i) - x2[0] * T2(2, i);
51-
design(5, i) = -x2[1] * T2(0, i) + x2[0] * T2(1, i);
52-
}
52+
const Vec3 v1 = R1.transpose() * x1;
53+
const Vec3 v2 = R2.transpose() * x2;
54+
55+
Mat4 design;
56+
57+
//We keep last 2 rows of the equation for each point
58+
// [ 0 -z y]
59+
// [ z 0 -x]
60+
// [-y x 0]
61+
// -->
62+
// [ z 0 -x]
63+
// [-y x 0]
64+
65+
// [ z 0 -x] * [cx cy cz]^t = z * cx - x * cz
66+
// [-y x 0] * [cx cy cz]^t = -y * cx + x * cy
67+
68+
//[v]x
69+
design(0, 0) = v1.z();
70+
design(0, 1) = 0;
71+
design(0, 2) = -v1.x();
72+
design(1, 0) = - v1.y();
73+
design(1, 1) = v1.x();
74+
design(1, 2) = 0;
75+
76+
//-[V]x*center
77+
design(0, 3) = - (v1.z() * c1.x() - v1.x() * c1.z());
78+
design(1, 3) = - (-v1.y() * c1.x() + v1.x() * c1.y());
79+
80+
//[v]x
81+
design(2, 0) = v2.z();
82+
design(2, 1) = 0;
83+
design(2, 2) = -v2.x();
84+
design(3, 0) = - v2.y();
85+
design(3, 1) = v2.x();
86+
design(3, 2) = 0;
87+
88+
//-[V]x*center
89+
design(2, 3) = - (v2.z() * c2.x() - v2.x() * c2.z());
90+
design(3, 3) = - (-v2.y() * c2.x() + v2.x() * c2.y());
91+
92+
// Solve AX=0, where X is the homogeneous coordinates vector of the 3d point
93+
// in the reference frame
5394
Nullspace(design, X_homogeneous);
5495
}
5596

src/aliceVision/multiview/triangulation/triangulationDLT.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ void TriangulateSphericalDLT(const Mat4& T1, const Vec3& x1, const Mat4& T2, con
5050
* Triangulate a point given a set of bearing vectors
5151
* and associated projection matrices (in meters)
5252
* @param T1 a projection matrix (R | t)
53-
* @param x1 a unit bearing vector
53+
* @param x1 a unit bearing vector (on the unit sphere)
5454
* @param T2 a projection matrix K (R | t)
55-
* @param x2 a unit bearing vector
55+
* @param x2 a unit bearing vector (on the unit sphere)
5656
* @param X_homogeneous a 3d point
5757
*/
5858
void TriangulateSphericalDLT(const Mat4& T1, const Vec3& x1, const Mat4& T2, const Vec3& x2, Vec3& X_euclidean);

src/aliceVision/multiview/triangulation/triangulationDLT_test.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <aliceVision/numeric/projection.hpp>
1010
#include <aliceVision/multiview/NViewDataSet.hpp>
1111
#include <aliceVision/multiview/triangulation/triangulationDLT.hpp>
12+
#include <aliceVision/geometry/lie.hpp>
1213

1314
#define BOOST_TEST_MODULE triangulationDLT
1415

@@ -32,3 +33,45 @@ BOOST_AUTO_TEST_CASE(Triangulation_TriangulateDLT)
3233
BOOST_CHECK_SMALL(DistanceLInfinity(X_estimated, X_gt), 1e-8);
3334
}
3435
}
36+
37+
BOOST_AUTO_TEST_CASE(Triangulation_TriangulateSphericalDLT_base)
38+
{
39+
Vec3 X_gt;
40+
41+
X_gt << 2, -3, 4;
42+
43+
Mat4 T1 = Mat4::Identity();
44+
Mat4 T2 = Mat4::Identity();
45+
46+
T1.block<3, 3>(0, 0) = SO3::expm({0.1, 0.2, -0.5});
47+
T2.block<3, 3>(0, 0) = SO3::expm({0.01, -0.3, -0.2});
48+
T1.block<3, 1>(0, 3) << 0.5, 0.6, -0.2;
49+
T2.block<3, 1>(0, 3) << -0.1, 1.6, 2.2;
50+
51+
Vec3 x1 = (T1 * X_gt.homogeneous()).head(3).normalized();
52+
Vec3 x2 = (T2 * X_gt.homogeneous()).head(3).normalized();
53+
54+
Vec3 X_estimated;
55+
multiview::TriangulateSphericalDLT(T1, x1, T2, x2, X_estimated);
56+
57+
BOOST_CHECK_SMALL(DistanceLInfinity(X_estimated, X_gt), 1e-8);
58+
}
59+
60+
BOOST_AUTO_TEST_CASE(Triangulation_TriangulateSphericalDLT_Zero)
61+
{
62+
Vec3 X_gt;
63+
64+
X_gt << 2, -3, 4;
65+
66+
Mat4 T1 = Mat4::Identity();
67+
Mat4 T2 = Mat4::Identity();
68+
69+
Vec3 x1 = (T1 * X_gt.homogeneous()).head(3).normalized();
70+
Vec3 x2 = (T2 * X_gt.homogeneous()).head(3).normalized();
71+
72+
Vec3 X_estimated;
73+
multiview::TriangulateSphericalDLT(T1, x1, T2, x2, X_estimated);
74+
75+
//Result should be null vector
76+
BOOST_CHECK(X_estimated.norm() < 1e-3);
77+
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ bool estimatePairAngle(const sfmData::SfMData & sfmData,
1616
const IndexT referenceViewId,
1717
const IndexT otherViewId,
1818
const geometry::Pose3 & otherTreference,
19+
const double & errorMax,
1920
const track::TracksMap& tracksMap,
2021
const track::TracksPerView & tracksPerView,
2122
double & resultAngle,
@@ -33,7 +34,9 @@ bool estimatePairAngle(const sfmData::SfMData & sfmData,
3334
track::getCommonTracksInImagesFast({referenceViewId, otherViewId}, tracksMap, tracksPerView, mapTracksCommon);
3435

3536
const Mat4 T1 = Eigen::Matrix4d::Identity();
36-
const Mat4 T2 = otherTreference.getHomogeneous();
37+
Mat4 T2 = otherTreference.getHomogeneous();
38+
const Mat3 R2 = otherTreference.rotation();
39+
const Mat3 E = CrossProductMatrix(otherTreference.translation()) * R2;
3740

3841
const Eigen::Vector3d c = otherTreference.center();
3942

@@ -52,6 +55,13 @@ bool estimatePairAngle(const sfmData::SfMData & sfmData,
5255
const Vec3 pt3d1 = refIntrinsics->toUnitSphere(refIntrinsics->removeDistortion(refIntrinsics->ima2cam(refpt)));
5356
const Vec3 pt3d2 = nextIntrinsics->toUnitSphere(nextIntrinsics->removeDistortion(nextIntrinsics->ima2cam(nextpt)));
5457

58+
const Vec3 x = (E * pt3d1).normalized();
59+
double error = std::abs(std::asin(pt3d2.dot(x)));
60+
if (error > errorMax)
61+
{
62+
continue;
63+
}
64+
5565

5666
Vec3 X;
5767
multiview::TriangulateSphericalDLT(T1, pt3d1, T2, pt3d2, X);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ namespace sfm {
1818
* @param referenceViewId the reference view id
1919
* @param otherViewId the other view id
2020
* @param otherTreference the relative pose
21+
* @param errorMax the maximal tolerated error
2122
* @param tracksMap the input map of tracks
2223
* @param tracksPerView tracks grouped by views
2324
* @param resultAngle the output median angle
@@ -28,6 +29,7 @@ bool estimatePairAngle(const sfmData::SfMData & sfmData,
2829
const IndexT referenceViewId,
2930
const IndexT otherViewId,
3031
const geometry::Pose3 & otherTreference,
32+
const double & errorMax,
3133
const track::TracksMap& tracksMap,
3234
const track::TracksPerView & tracksPerView,
3335
double & resultAngle,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ IndexT findBestPair(const sfmData::SfMData & sfmData,
7070

7171
double angle = 0.0;
7272
std::vector<size_t> usedTracks;
73-
if (!sfm::estimatePairAngle(sfmData, pair.reference, pair.next, pair.pose, tracksMap, tracksPerView, angle, usedTracks))
73+
if (!sfm::estimatePairAngle(sfmData, pair.reference, pair.next, pair.pose, pair.errorMax, tracksMap, tracksPerView, angle, usedTracks))
7474
{
7575
continue;
7676
}

src/aliceVision/sfm/pipeline/relativePoses.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ struct ReconstructedPair
2020
IndexT next;
2121
geometry::Pose3 pose;
2222
double score;
23+
double errorMax;
2324
};
2425

2526
void tag_invoke(const boost::json::value_from_tag&, boost::json::value& jv, sfm::ReconstructedPair const& input)
@@ -28,7 +29,9 @@ void tag_invoke(const boost::json::value_from_tag&, boost::json::value& jv, sfm:
2829
{"next", input.next},
2930
{"R", boost::json::value_from(SO3::logm(input.pose.rotation()))},
3031
{"t", boost::json::value_from(input.pose.translation())},
31-
{"score", boost::json::value_from(input.score)}};
32+
{"score", boost::json::value_from(input.score)},
33+
{"errorMax", boost::json::value_from(input.errorMax)}
34+
};
3235
}
3336

3437
ReconstructedPair tag_invoke(boost::json::value_to_tag<ReconstructedPair>, boost::json::value const& jv)
@@ -42,6 +45,7 @@ ReconstructedPair tag_invoke(boost::json::value_to_tag<ReconstructedPair>, boost
4245
ret.pose.setRotation(SO3::expm(boost::json::value_to<Vec3>(obj.at("R"))));
4346
ret.pose.setTranslation(boost::json::value_to<Vec3>(obj.at("t")));
4447
ret.score = boost::json::value_to<double>(obj.at("score"));
48+
ret.errorMax = boost::json::value_to<double>(obj.at("errorMax"));
4549

4650
return ret;
4751
}

0 commit comments

Comments
 (0)