Visual Servoing Platform  version 3.6.1 under development (2024-11-15)
catchMbtJsonSettings.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * Test vpMbGenericTracker JSON parse / save.
32  */
33 
40 #include <visp3/core/vpIoTools.h>
41 #include <visp3/mbt/vpMbGenericTracker.h>
42 
43 #if defined(VISP_HAVE_NLOHMANN_JSON) && defined(VISP_HAVE_CATCH2)
44 #include VISP_NLOHMANN_JSON(json.hpp)
45 using json = nlohmann::json;
46 
47 #include <catch_amalgamated.hpp>
48 
49 #ifdef ENABLE_VISP_NAMESPACE
50 using namespace VISP_NAMESPACE_NAME;
51 #endif
52 
53 vpMbGenericTracker baseTrackerConstructor()
54 {
55  const std::vector<std::string> names = { "C1", "C2" };
56  std::vector<int> featureTypes;
57 #if defined(VISP_HAVE_MODULE_KLT) && defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC) && defined(HAVE_OPENCV_VIDEO)
58  featureTypes.push_back(vpMbGenericTracker::EDGE_TRACKER | vpMbGenericTracker::KLT_TRACKER);
59 #else
60  featureTypes.push_back(vpMbGenericTracker::EDGE_TRACKER);
61 #endif
63  vpCameraParameters cam1;
64  cam1.initPersProjWithoutDistortion(300, 300, 200, 200);
65  vpCameraParameters cam2;
66  cam2.initPersProjWithoutDistortion(500, 400, 250, 250);
67 
68  vpMbGenericTracker t = vpMbGenericTracker(names, featureTypes);
69 
70  std::map<std::string, vpCameraParameters> cams;
71  cams[names[0]] = cam1;
72  cams[names[1]] = cam2;
73  t.setCameraParameters(cams);
74 
75  t.setLod(false);
76 
79  vpMe me;
80  me.setSampleStep(2.0);
81  me.setMaskSize(7);
82  me.setMaskNumber(160);
83  me.setRange(8);
85  me.setThreshold(20);
86  me.setMu1(0.2);
87  me.setMu2(0.3);
88  me.setSampleStep(4);
89  t.setMovingEdge(me);
90  return t;
91 }
92 
93 template<typename T, typename C>
94 void checkProperties(const T &t1, const T &t2, C fn, const std::string &message)
95 {
96  THEN(message)
97  {
98  REQUIRE((t1.*fn)() == (t2.*fn)());
99  }
100 }
101 
102 template<typename T, typename C, typename... Fns>
103 void checkProperties(const T &t1, const T &t2, C fn, const std::string &message, Fns... fns)
104 {
105  checkProperties(t1, t2, fn, message);
106  checkProperties(t1, t2, fns...);
107 }
108 
109 void compareNamesAndTypes(const vpMbGenericTracker &t1, const vpMbGenericTracker &t2)
110 {
111  REQUIRE(t1.getCameraNames() == t2.getCameraNames());
112  REQUIRE(t1.getCameraTrackerTypes() == t2.getCameraTrackerTypes());
113 }
114 
115 void compareCameraParameters(const vpMbGenericTracker &t1, const vpMbGenericTracker &t2)
116 {
117  std::map<std::string, vpCameraParameters> c1, c2;
118  t1.getCameraParameters(c1);
119  t2.getCameraParameters(c2);
120  REQUIRE(c1 == c2);
121 }
122 
123 json loadJson(const std::string &path)
124 {
125  std::ifstream json_file(path);
126  if (!json_file.good()) {
127  throw vpException(vpException::ioError, "Could not open JSON settings file");
128  }
129  json j = json::parse(json_file);
130  json_file.close();
131  return j;
132 }
133 
134 void saveJson(const json &j, const std::string &path)
135 {
136  std::ofstream json_file(path);
137  if (!json_file.good()) {
138  throw vpException(vpException::ioError, "Could not open JSON settings file to write modifications");
139  }
140  json_file << j.dump();
141  json_file.close();
142 }
143 
144 SCENARIO("MBT JSON Serialization", "[json]")
145 {
146  // setup test dir
147  // Get the user login name
148 
149  std::string tmp_dir = vpIoTools::makeTempDirectory(vpIoTools::getTempPath() + vpIoTools::path("/") + "visp_test_json_parsing_mbt");
150 
151  GIVEN("A generic tracker with two cameras, one with edge and KLT features, the other with depth features")
152  {
153  vpMbGenericTracker t1 = baseTrackerConstructor();
154  WHEN("Saving to a JSON settings file")
155  {
156  const std::string jsonPath = tmp_dir + "/" + "tracker_save.json";
157 
158  const auto modifyJson = [&jsonPath](std::function<void(json &)> modify) -> void {
159  json j = loadJson(jsonPath);
160  modify(j);
161  saveJson(j, jsonPath);
162  };
163 
164  REQUIRE_NOTHROW(t1.saveConfigFile(jsonPath));
165  THEN("Reloading this tracker has the same basic properties")
166  {
168  REQUIRE_NOTHROW(t2.loadConfigFile(jsonPath));
169  compareNamesAndTypes(t1, t2);
170  compareCameraParameters(t1, t2);
171  }
172 
173  THEN("Reloading this tracker has the same basic properties")
174  {
176  REQUIRE_NOTHROW(t2.loadConfigFile(jsonPath));
177  checkProperties(t1, t2,
178  &vpMbGenericTracker::getAngleAppear, "Angle appear should be the same",
179  &vpMbGenericTracker::getAngleDisappear, "Angle appear should be the same"
180  );
181  }
182 
183  THEN("Reloaded edge tracker parameters should be the same")
184  {
185  std::map<std::string, vpMe> oldvpMe, newvpMe;
186  t1.getMovingEdge(oldvpMe);
188  t2.loadConfigFile(jsonPath);
189  t2.getMovingEdge(newvpMe);
190  for (const auto &it : oldvpMe) {
191  vpMe o = it.second;
192  vpMe n;
193  REQUIRE_NOTHROW(n = newvpMe[it.first]);
194  checkProperties(o, n,
195  &vpMe::getAngleStep, "Angle step should be equal",
196  &vpMe::getMaskNumber, "Mask number should be equal",
197  &vpMe::getMaskSign, "Mask sign should be equal",
198  &vpMe::getMinSampleStep, "Min sample step should be equal",
199  &vpMe::getSampleStep, "Min sample step should be equal",
200 
201  &vpMe::getMu1, "Mu 1 should be equal",
202  &vpMe::getMu2, "Mu 2 should be equal",
203  &vpMe::getNbTotalSample, "Nb total sample should be equal",
204  &vpMe::getPointsToTrack, "Number of points to track should be equal",
205  &vpMe::getRange, "Range should be equal",
206  &vpMe::getStrip, "Strip should be equal"
207  );
208  }
209  }
210 
211 #if defined(VISP_HAVE_MODULE_KLT) && defined(VISP_HAVE_OPENCV)
212  THEN("Reloaded KLT tracker parameters should be the same")
213  {
214  std::map<std::string, vpKltOpencv> oldvpKlt, newvpKlt;
215  t1.getKltOpencv(oldvpKlt);
217  t2.loadConfigFile(jsonPath);
218  t2.getKltOpencv(newvpKlt);
219  for (const auto &it : oldvpKlt) {
220  vpKltOpencv o = it.second;
221  vpKltOpencv n;
222  REQUIRE_NOTHROW(n = newvpKlt[it.first]);
223  checkProperties(o, n,
224  &vpKltOpencv::getBlockSize, "Block size should be equal",
225  &vpKltOpencv::getHarrisFreeParameter, "Harris parameter should be equal",
226  &vpKltOpencv::getMaxFeatures, "Max number of features should be equal",
227  &vpKltOpencv::getMinDistance, "Minimum distance should be equal",
228  &vpKltOpencv::getPyramidLevels, "Pyramid levels should be equal",
229  &vpKltOpencv::getQuality, "Quality should be equal",
230  &vpKltOpencv::getWindowSize, "Window size should be equal"
231  );
232  }
233  }
234 #endif
235 
236  THEN("Clipping properties should be the same")
237  {
238  vpMbGenericTracker t2 = baseTrackerConstructor();
239  t2.setNearClippingDistance(0.1);
240  t2.setFarClippingDistance(2.0);
242  t2.loadConfigFile(jsonPath);
243  std::map<std::string, unsigned int> oldFlags, newFlags;
244  t1.getClipping(oldFlags);
245  t2.getClipping(newFlags);
246  for (const auto &it : oldFlags) {
247  unsigned int o = it.second;
248  unsigned int n = 0;
249  REQUIRE_NOTHROW(n = newFlags[it.first]);
250  THEN("Clipping flags for camera " + it.first + " should be the same")
251  {
252  REQUIRE(o == n);
253  }
254  }
255  checkProperties(t1, t2,
256  &vpMbGenericTracker::getNearClippingDistance, "Near clipping distance should be the same",
257  &vpMbGenericTracker::getFarClippingDistance, "Far clipping distance should be the same"
258  );
259  }
260 
261  THEN("VVS properties should be the same")
262  {
263  vpMbGenericTracker t2 = baseTrackerConstructor();
264  t2.setMaxIter(4096);
265  t2.setLambda(5.0);
266  t2.setInitialMu(5.0);
267 
268  t2.loadConfigFile(jsonPath);
269 
270  checkProperties(t1, t2,
271  &vpMbGenericTracker::getMaxIter, "VVS m iterations be the same",
272  &vpMbGenericTracker::getLambda, "VVS lambda should be the same",
273  &vpMbGenericTracker::getInitialMu, "VVS initial mu be the same"
274  );
275  }
276 
277  WHEN("Modifying JSON file/Using a custom JSON file")
278  {
279  THEN("Removing version from file generates an error on load")
280  {
281  modifyJson([](json &j) -> void {
282  j.erase("version");
283  });
284  REQUIRE_THROWS(t1.loadConfigFile(jsonPath));
285  }
286 
287  THEN("Using an unsupported version generates an error on load")
288  {
289  modifyJson([](json &j) -> void {
290  j["version"] = "0.0.0";
291  });
292  REQUIRE_THROWS(t1.loadConfigFile(jsonPath));
293  }
294 
295  THEN("Using an undefined reference camera generates an error")
296  {
297  modifyJson([](json &j) -> void {
298  j["referenceCameraName"] = "C3";
299  });
300  REQUIRE_THROWS(t1.loadConfigFile(jsonPath));
301  }
302 
303  THEN("Not defining a transformation matrix for the reference camera is valid")
304  {
305  modifyJson([&t1](json &j) -> void {
306  j["trackers"][t1.getReferenceCameraName()].erase("camTref");
307  });
308  REQUIRE_NOTHROW(t1.loadConfigFile(jsonPath));
309  }
310 
311  THEN("Not defining a transformation from a non-reference camera to the reference camera generates an error")
312  {
313  modifyJson([&t1](json &j) -> void {
314  std::string otherCamName = t1.getReferenceCameraName() == "C1" ? "C2" : "C1";
315  j["trackers"][otherCamName].erase("camTref");
316  });
317  REQUIRE_THROWS(t1.loadConfigFile(jsonPath));
318  }
319 
320  THEN("The full clipping config is optional")
321  {
322  vpMbGenericTracker t2 = baseTrackerConstructor();
323  const double clipping_near = 0.21;
324  const double clipping_far = 5.2;
325  const int clipping = vpPolygon3D::LEFT_CLIPPING;
326  t2.setNearClippingDistance(clipping_near);
327  t2.setFarClippingDistance(clipping_far);
328  t2.setClipping(clipping);
329  modifyJson([&t1](json &j) -> void {
330  for (const auto &c : t1.getCameraNames()) {
331  j["trackers"][c].erase("clipping");
332  }
333  });
334  REQUIRE_NOTHROW(t2.loadConfigFile(jsonPath, false));
335  REQUIRE(t2.getClipping() == clipping);
336  REQUIRE(t2.getNearClippingDistance() == clipping_near);
337  REQUIRE(t2.getFarClippingDistance() == clipping_far);
338  }
339 
340  THEN("Each clipping param is optional on its own")
341  {
342  vpMbGenericTracker t2 = baseTrackerConstructor();
343  const double clipping_near = 0.21;
344  const double clipping_far = 5.2;
345  const int clipping = vpPolygon3D::LEFT_CLIPPING;
346  t2.setNearClippingDistance(clipping_near);
347  t2.setFarClippingDistance(clipping_far);
348  t2.setClipping(clipping);
349  THEN("Near clipping is optional")
350  {
351  modifyJson([&t1](json &j) -> void {
352  for (const auto &c : t1.getCameraNames()) {
353  j["trackers"][c]["clipping"].erase("near");
354  }
355  });
356  t2.loadConfigFile(jsonPath);
357  REQUIRE(t2.getNearClippingDistance() == clipping_near);
358  REQUIRE(t2.getFarClippingDistance() == t1.getFarClippingDistance());
359  REQUIRE(t2.getClipping() == t1.getClipping());
360  }
361  THEN("Far clipping is optional")
362  {
363  modifyJson([&t1](json &j) -> void {
364  for (const auto &c : t1.getCameraNames()) {
365  j["trackers"][c]["clipping"].erase("far");
366  }
367  });
368  t2.loadConfigFile(jsonPath);
369  REQUIRE(t2.getNearClippingDistance() == t1.getNearClippingDistance());
370  REQUIRE(t2.getFarClippingDistance() == clipping_far);
371  REQUIRE(t2.getClipping() == t1.getClipping());
372  }
373  THEN("Clipping flags are optional")
374  {
375  modifyJson([&t1](json &j) -> void {
376  for (const auto &c : t1.getCameraNames()) {
377  j["trackers"][c]["clipping"].erase("flags");
378  }
379  });
380  t2.loadConfigFile(jsonPath);
381  REQUIRE(t2.getNearClippingDistance() == t1.getNearClippingDistance());
382  REQUIRE(t2.getFarClippingDistance() == t1.getFarClippingDistance());
383  REQUIRE(t2.getClipping() & clipping);
384  }
385  }
386  }
387  }
388  }
389 }
390 
391 int main(int argc, char *argv[])
392 {
393  Catch::Session session;
394  session.applyCommandLine(argc, argv);
395  int numFailed = session.run();
396  return numFailed;
397 }
398 
399 #else
400 
401 int main()
402 {
403  return EXIT_SUCCESS;
404 }
405 
406 #endif
Generic class defining intrinsic camera parameters.
void initPersProjWithoutDistortion(double px, double py, double u0, double v0)
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ ioError
I/O error.
Definition: vpException.h:67
static std::string path(const std::string &pathname)
Definition: vpIoTools.cpp:1005
static std::string getTempPath()
Definition: vpIoTools.cpp:189
static std::string makeTempDirectory(const std::string &dirname)
Definition: vpIoTools.cpp:708
Wrapper for the KLT (Kanade-Lucas-Tomasi) feature tracker implemented in OpenCV. Thus to enable this ...
Definition: vpKltOpencv.h:74
double getQuality() const
Definition: vpKltOpencv.h:211
int getMaxFeatures() const
Get the list of lost feature.
Definition: vpKltOpencv.h:193
int getWindowSize() const
Get the window size used to refine the corner locations.
Definition: vpKltOpencv.h:213
double getHarrisFreeParameter() const
Get the free parameter of the Harris detector.
Definition: vpKltOpencv.h:189
double getMinDistance() const
Definition: vpKltOpencv.h:196
int getBlockSize() const
Get the size of the averaging block used to track the features.
Definition: vpKltOpencv.h:169
int getPyramidLevels() const
Get the list of features id.
Definition: vpKltOpencv.h:208
static double rad(double deg)
Definition: vpMath.h:129
Real-time 6D object pose tracking using its CAD model.
virtual void setCameraParameters(const vpCameraParameters &camera) VP_OVERRIDE
virtual void getCameraParameters(vpCameraParameters &camera) const VP_OVERRIDE
virtual void setLod(bool useLod, const std::string &name="") VP_OVERRIDE
virtual void saveConfigFile(const std::string &settingsFile) const
virtual std::string getReferenceCameraName() const
virtual std::map< std::string, int > getCameraTrackerTypes() const
virtual void setMovingEdge(const vpMe &me)
virtual void setAngleDisappear(const double &a) VP_OVERRIDE
virtual std::vector< std::string > getCameraNames() const
virtual void getClipping(unsigned int &clippingFlag1, unsigned int &clippingFlag2) const
virtual void setClipping(const unsigned int &flags) VP_OVERRIDE
virtual vpMe getMovingEdge() const
virtual void setNearClippingDistance(const double &dist) VP_OVERRIDE
virtual void loadConfigFile(const std::string &configFile, bool verbose=true) VP_OVERRIDE
virtual void setAngleAppear(const double &a) VP_OVERRIDE
virtual void setFarClippingDistance(const double &dist) VP_OVERRIDE
virtual double getNearClippingDistance() const
Definition: vpMbTracker.h:377
virtual void setMaxIter(unsigned int max)
Definition: vpMbTracker.h:547
virtual double getInitialMu() const
Definition: vpMbTracker.h:286
virtual double getAngleAppear() const
Definition: vpMbTracker.h:240
virtual double getAngleDisappear() const
Definition: vpMbTracker.h:243
virtual void setInitialMu(double mu)
Definition: vpMbTracker.h:531
virtual void setLambda(double gain)
Definition: vpMbTracker.h:538
virtual unsigned int getMaxIter() const
Definition: vpMbTracker.h:300
virtual double getLambda() const
Definition: vpMbTracker.h:293
virtual double getFarClippingDistance() const
Definition: vpMbTracker.h:341
Definition: vpMe.h:134
int getMaskSign() const
Definition: vpMe.h:216
void setMu1(const double &mu_1)
Definition: vpMe.h:385
double getMinSampleStep() const
Definition: vpMe.h:233
void setRange(const unsigned int &range)
Definition: vpMe.h:415
void setLikelihoodThresholdType(const vpLikelihoodThresholdType likelihood_threshold_type)
Definition: vpMe.h:505
void setMaskNumber(const unsigned int &mask_number)
Definition: vpMe.cpp:552
int getNbTotalSample() const
Definition: vpMe.h:254
void setThreshold(const double &threshold)
Definition: vpMe.h:466
unsigned int getAngleStep() const
Definition: vpMe.h:193
double getMu1() const
Definition: vpMe.h:240
unsigned int getMaskNumber() const
Definition: vpMe.h:209
int getPointsToTrack() const
Definition: vpMe.h:261
int getStrip() const
Definition: vpMe.h:282
void setSampleStep(const double &sample_step)
Definition: vpMe.h:422
double getMu2() const
Definition: vpMe.h:247
void setMaskSize(const unsigned int &mask_size)
Definition: vpMe.cpp:560
void setMu2(const double &mu_2)
Definition: vpMe.h:392
double getSampleStep() const
Definition: vpMe.h:275
unsigned int getRange() const
Definition: vpMe.h:268
@ NORMALIZED_THRESHOLD
Definition: vpMe.h:145