1 // This file is part of OpenMVG, an Open Multiple View Geometry C++ library.
2
3 // Copyright (c) 2015 Pierre MOULON.
4
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
9 //-----------------
10 // Test summary:
11 //-----------------
12 // - Create features points and matching from the synthetic dataset
13 // - Init a SfM_Data scene View and Intrinsic from a synthetic dataset
14 // - Compute some stellar reconstruction on the data
15 // - Assert that:
16 // - The computed poses are correct
17 //-----------------
18
19 #include "openMVG/multiview/essential.hpp"
20 #include "openMVG/sfm/pipelines/pipelines_test.hpp"
21 #include "openMVG/sfm/pipelines/relative_pose_engine.hpp"
22 #include "openMVG/sfm/pipelines/stellar/stellar_solver.hpp"
23 #include "openMVG/sfm/sfm.hpp"
24
25 #include "testing/testing.h"
26
27 #include <cmath>
28 #include <cstdio>
29 #include <iostream>
30
31 using namespace openMVG;
32 using namespace openMVG::cameras;
33 using namespace openMVG::geometry;
34 using namespace openMVG::sfm;
35
36 // Test a scene where all the camera intrinsics are known
TEST(STELLAR_SOLVER,Known_Intrinsics)37 TEST(STELLAR_SOLVER, Known_Intrinsics) {
38
39 const int nviews = 6;
40 const int npoints = 32;
41 const nViewDatasetConfigurator config;
42 const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);
43
44 // Translate the input dataset to a SfM_Data scene
45 const SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA);
46
47 // Remove poses and structure
48 SfM_Data sfm_data_2 = sfm_data;
49 sfm_data_2.poses.clear();
50 sfm_data_2.structure.clear();
51
52 // Configure the features_provider & the matches_provider from the synthetic dataset
53 std::shared_ptr<Features_Provider> feats_provider =
54 std::make_shared<Synthetic_Features_Provider>();
55 // Add a tiny noise in 2D observations to make data more realistic
56 std::normal_distribution<double> distribution(0.0, 1e-2);
57 dynamic_cast<Synthetic_Features_Provider*>(feats_provider.get())->load(d,distribution);
58
59 std::shared_ptr<Matches_Provider> matches_provider =
60 std::make_shared<Synthetic_Matches_Provider>();
61 dynamic_cast<Synthetic_Matches_Provider*>(matches_provider.get())->load(d);
62
63 const std::vector<StellarPod> stellar_pods = {
64 {{0,1}, {0,2}, {1,2}}, // Test with a triplet
65 {{0,1}, {0,2}} // Test with a 2-uplet
66 };
67
68 for (const auto pod : stellar_pods)
69 {
70 // Compute how many pose ids are defined by the stellar pod
71 const std::set<IndexT> set_pose = [&]{
72 std::set<IndexT> pose_set;
73 for (const Pair & it : pod)
74 {
75 pose_set.insert(it.first);
76 pose_set.insert(it.second);
77 }
78 return pose_set;
79 }();
80
81 const Relative_Pose_Engine::Relative_Pair_Poses relative_poses = [&]
82 {
83 Relative_Pose_Engine relative_pose_engine;
84 if (!relative_pose_engine.Process(
85 pod,
86 sfm_data_2,
87 matches_provider.get(),
88 feats_provider.get()))
89 return Relative_Pose_Engine::Relative_Pair_Poses();
90 else
91 return relative_pose_engine.Get_Relative_Poses();
92 }();
93
94 EXPECT_EQ(pod.size(), relative_poses.size());
95
96 Stellar_Solver stellar_pod_solver(
97 pod,
98 relative_poses,
99 sfm_data_2,
100 matches_provider.get(),
101 feats_provider.get());
102
103 EXPECT_TRUE(stellar_pod_solver.Solve(sfm_data_2.poses));
104
105 EXPECT_EQ(set_pose.size(), sfm_data_2.poses.size());
106
107 const double kEpsilon = 1e-3;
108 for (const auto & pair : pod)
109 {
110 const int I = pair.first;
111 const int J = pair.second;
112 //-- Compute Ground Truth motion
113 Mat3 R_gt;
114 Vec3 t_gt;
115 RelativeCameraMotion(d._R[I], d._t[I], d._R[J], d._t[J], &R_gt, &t_gt);
116
117 // Compute the SfM motion
118 Mat3 R_computed;
119 Vec3 t_computed;
120 RelativeCameraMotion(
121 sfm_data_2.poses[I].rotation(),
122 sfm_data_2.poses[I].translation(),
123 sfm_data_2.poses[J].rotation(),
124 sfm_data_2.poses[J].translation(),
125 &R_computed, &t_computed);
126
127 // Compare the motion
128 std::cout << FrobeniusDistance(R_gt, R_computed) << std::endl;
129 std::cout << "translation: " << (t_gt.normalized() - t_computed.normalized()).norm() << std::endl;
130 EXPECT_TRUE(FrobeniusDistance(R_gt, R_computed) < kEpsilon);
131 EXPECT_TRUE((t_gt.normalized() - t_computed.normalized()).norm() < kEpsilon )
132 }
133 }
134 }
135
136 /* ************************************************************************* */
main()137 int main() { TestResult tr; return TestRegistry::runAllTests(tr);}
138 /* ************************************************************************* */
139