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 // - Perform Global SfM on the data
15 // - Assert that:
16 //   - mean residual error is below the gaussian noise added to observation
17 //   - the desired number of tracks are found,
18 //   - the desired number of poses are found.
19 //-----------------
20 
21 #include "openMVG/sfm/pipelines/pipelines_test.hpp"
22 #include "openMVG/sfm/sfm.hpp"
23 
24 #include "testing/testing.h"
25 
26 #include <cmath>
27 #include <cstdio>
28 #include <iostream>
29 
30 using namespace openMVG;
31 using namespace openMVG::cameras;
32 using namespace openMVG::geometry;
33 using namespace openMVG::sfm;
34 
35 
TEST(GLOBAL_SFM,RotationAveragingL2_TranslationAveragingL1)36 TEST(GLOBAL_SFM, RotationAveragingL2_TranslationAveragingL1) {
37 
38   const int nviews = 6;
39   const int npoints = 64;
40   const nViewDatasetConfigurator config;
41   const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);
42 
43   // Translate the input dataset to a SfM_Data scene
44   const SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA);
45 
46   // Remove poses and structure
47   SfM_Data sfm_data_2 = sfm_data;
48   sfm_data_2.poses.clear();
49   sfm_data_2.structure.clear();
50 
51   GlobalSfMReconstructionEngine_RelativeMotions sfmEngine(
52     sfm_data_2,
53     "./",
54     stlplus::create_filespec("./", "Reconstruction_Report.html"));
55 
56   // Configure the features_provider & the matches_provider from the synthetic dataset
57   std::shared_ptr<Features_Provider> feats_provider =
58     std::make_shared<Synthetic_Features_Provider>();
59   // Add a tiny noise in 2D observations to make data more realistic
60   std::normal_distribution<double> distribution(0.0,0.5);
61   dynamic_cast<Synthetic_Features_Provider*>(feats_provider.get())->load(d,distribution);
62 
63   std::shared_ptr<Matches_Provider> matches_provider =
64     std::make_shared<Synthetic_Matches_Provider>();
65   dynamic_cast<Synthetic_Matches_Provider*>(matches_provider.get())->load(d);
66 
67   // Configure data provider (Features and Matches)
68   sfmEngine.SetFeaturesProvider(feats_provider.get());
69   sfmEngine.SetMatchesProvider(matches_provider.get());
70 
71   // Configure reconstruction parameters (intrinsic parameters are held constant)
72   sfmEngine.Set_Intrinsics_Refinement_Type(cameras::Intrinsic_Parameter_Type::NONE);
73 
74   // Configure motion averaging methods
75   sfmEngine.SetRotationAveragingMethod(ROTATION_AVERAGING_L2);
76   sfmEngine.SetTranslationAveragingMethod(TRANSLATION_AVERAGING_L1);
77 
78   EXPECT_TRUE (sfmEngine.Process());
79 
80   const double dResidual = RMSE(sfmEngine.Get_SfM_Data());
81   std::cout << "RMSE residual: " << dResidual << std::endl;
82   EXPECT_TRUE( dResidual < 0.5);
83   EXPECT_EQ( nviews, sfmEngine.Get_SfM_Data().GetPoses().size());
84   EXPECT_EQ( npoints, sfmEngine.Get_SfM_Data().GetLandmarks().size());
85   EXPECT_TRUE( IsTracksOneCC(sfmEngine.Get_SfM_Data()));
86 }
87 
TEST(GLOBAL_SFM,RotationAveragingL1_TranslationAveragingL1)88 TEST(GLOBAL_SFM, RotationAveragingL1_TranslationAveragingL1) {
89 
90   const int nviews = 6;
91   const int npoints = 64;
92   const nViewDatasetConfigurator config;
93   const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);
94 
95   // Translate the input dataset to a SfM_Data scene
96   const SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA);
97 
98   // Remove poses and structure
99   SfM_Data sfm_data_2 = sfm_data;
100   sfm_data_2.poses.clear();
101   sfm_data_2.structure.clear();
102 
103   GlobalSfMReconstructionEngine_RelativeMotions sfmEngine(
104     sfm_data_2,
105     "./",
106     stlplus::create_filespec("./", "Reconstruction_Report.html"));
107 
108   // Configure the features_provider & the matches_provider from the synthetic dataset
109   std::shared_ptr<Features_Provider> feats_provider =
110     std::make_shared<Synthetic_Features_Provider>();
111   // Add a tiny noise in 2D observations to make data more realistic
112   std::normal_distribution<double> distribution(0.0,0.5);
113   dynamic_cast<Synthetic_Features_Provider*>(feats_provider.get())->load(d,distribution);
114 
115   std::shared_ptr<Matches_Provider> matches_provider =
116     std::make_shared<Synthetic_Matches_Provider>();
117   dynamic_cast<Synthetic_Matches_Provider*>(matches_provider.get())->load(d);
118 
119   // Configure data provider (Features and Matches)
120   sfmEngine.SetFeaturesProvider(feats_provider.get());
121   sfmEngine.SetMatchesProvider(matches_provider.get());
122 
123   // Configure reconstruction parameters (intrinsic parameters are held constant)
124   sfmEngine.Set_Intrinsics_Refinement_Type(cameras::Intrinsic_Parameter_Type::NONE);
125 
126   // Configure motion averaging methods
127   sfmEngine.SetRotationAveragingMethod(ROTATION_AVERAGING_L1);
128   sfmEngine.SetTranslationAveragingMethod(TRANSLATION_AVERAGING_L1);
129 
130   EXPECT_TRUE (sfmEngine.Process());
131 
132   const double dResidual = RMSE(sfmEngine.Get_SfM_Data());
133   std::cout << "RMSE residual: " << dResidual << std::endl;
134   EXPECT_TRUE( dResidual < 0.5);
135   EXPECT_EQ( nviews, sfmEngine.Get_SfM_Data().GetPoses().size());
136   EXPECT_EQ( npoints, sfmEngine.Get_SfM_Data().GetLandmarks().size());
137   EXPECT_TRUE( IsTracksOneCC(sfmEngine.Get_SfM_Data()));
138 }
139 
140 
TEST(GLOBAL_SFM,RotationAveragingL2_TranslationAveragingL2_Chordal)141 TEST(GLOBAL_SFM, RotationAveragingL2_TranslationAveragingL2_Chordal) {
142 
143   const int nviews = 6;
144   const int npoints = 64;
145   const nViewDatasetConfigurator config;
146   const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);
147 
148   // Translate the input dataset to a SfM_Data scene
149   const SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA);
150 
151   // Remove poses and structure
152   SfM_Data sfm_data_2 = sfm_data;
153   sfm_data_2.poses.clear();
154   sfm_data_2.structure.clear();
155 
156   GlobalSfMReconstructionEngine_RelativeMotions sfmEngine(
157     sfm_data_2,
158     "./",
159     stlplus::create_filespec("./", "Reconstruction_Report.html"));
160 
161   // Configure the features_provider & the matches_provider from the synthetic dataset
162   std::shared_ptr<Features_Provider> feats_provider =
163     std::make_shared<Synthetic_Features_Provider>();
164   // Add a tiny noise in 2D observations to make data more realistic
165   std::normal_distribution<double> distribution(0.0,0.5);
166   dynamic_cast<Synthetic_Features_Provider*>(feats_provider.get())->load(d,distribution);
167 
168   std::shared_ptr<Matches_Provider> matches_provider =
169     std::make_shared<Synthetic_Matches_Provider>();
170   dynamic_cast<Synthetic_Matches_Provider*>(matches_provider.get())->load(d);
171 
172   // Configure data provider (Features and Matches)
173   sfmEngine.SetFeaturesProvider(feats_provider.get());
174   sfmEngine.SetMatchesProvider(matches_provider.get());
175 
176   // Configure reconstruction parameters (intrinsic parameters are held constant)
177   sfmEngine.Set_Intrinsics_Refinement_Type(cameras::Intrinsic_Parameter_Type::NONE);
178 
179   // Configure motion averaging method
180   sfmEngine.SetRotationAveragingMethod(ROTATION_AVERAGING_L2);
181   sfmEngine.SetTranslationAveragingMethod(TRANSLATION_AVERAGING_L2_DISTANCE_CHORDAL);
182 
183   EXPECT_TRUE (sfmEngine.Process());
184 
185   const double dResidual = RMSE(sfmEngine.Get_SfM_Data());
186   std::cout << "RMSE residual: " << dResidual << std::endl;
187   EXPECT_TRUE( dResidual < 0.5);
188   EXPECT_EQ( nviews, sfmEngine.Get_SfM_Data().GetPoses().size());
189   EXPECT_EQ( npoints, sfmEngine.Get_SfM_Data().GetLandmarks().size());
190   EXPECT_TRUE( IsTracksOneCC(sfmEngine.Get_SfM_Data()));
191 }
192 
TEST(GLOBAL_SFM,RotationAveragingL2_TranslationAveragingSoftL1)193 TEST(GLOBAL_SFM, RotationAveragingL2_TranslationAveragingSoftL1) {
194 
195   const int nviews = 6;
196   const int npoints = 64;
197   const nViewDatasetConfigurator config;
198   const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);
199 
200   // Translate the input dataset to a SfM_Data scene
201   const SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA);
202 
203   // Remove poses and structure
204   SfM_Data sfm_data_2 = sfm_data;
205   sfm_data_2.poses.clear();
206   sfm_data_2.structure.clear();
207 
208   GlobalSfMReconstructionEngine_RelativeMotions sfmEngine(
209     sfm_data_2,
210     "./",
211     stlplus::create_filespec("./", "Reconstruction_Report.html"));
212 
213   // Configure the features_provider & the matches_provider from the synthetic dataset
214   std::shared_ptr<Features_Provider> feats_provider =
215     std::make_shared<Synthetic_Features_Provider>();
216   // Add a tiny noise in 2D observations to make data more realistic
217   std::normal_distribution<double> distribution(0.0,0.5);
218   dynamic_cast<Synthetic_Features_Provider*>(feats_provider.get())->load(d,distribution);
219 
220   std::shared_ptr<Matches_Provider> matches_provider =
221     std::make_shared<Synthetic_Matches_Provider>();
222   dynamic_cast<Synthetic_Matches_Provider*>(matches_provider.get())->load(d);
223 
224   // Configure data provider (Features and Matches)
225   sfmEngine.SetFeaturesProvider(feats_provider.get());
226   sfmEngine.SetMatchesProvider(matches_provider.get());
227 
228   // Configure reconstruction parameters (intrinsic parameters are held constant)
229   sfmEngine.Set_Intrinsics_Refinement_Type(cameras::Intrinsic_Parameter_Type::NONE);
230 
231   // Configure motion averaging methods
232   sfmEngine.SetRotationAveragingMethod(ROTATION_AVERAGING_L2);
233   sfmEngine.SetTranslationAveragingMethod(TRANSLATION_AVERAGING_SOFTL1);
234 
235   EXPECT_TRUE (sfmEngine.Process());
236 
237   const double dResidual = RMSE(sfmEngine.Get_SfM_Data());
238   std::cout << "RMSE residual: " << dResidual << std::endl;
239   EXPECT_TRUE( dResidual < 0.5);
240   EXPECT_EQ( nviews, sfmEngine.Get_SfM_Data().GetPoses().size());
241   EXPECT_EQ( npoints, sfmEngine.Get_SfM_Data().GetLandmarks().size());
242   EXPECT_TRUE( IsTracksOneCC(sfmEngine.Get_SfM_Data()));
243 }
244 
245 /* ************************************************************************* */
main()246 int main() { TestResult tr; return TestRegistry::runAllTests(tr);}
247 /* ************************************************************************* */
248