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