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