Visual Servoing Platform  version 3.6.1 under development (2025-02-18)
tutorial-mb-generic-tracker-rgbd-realsense-json.cpp
1 #include <iostream>
3 
4 #include <visp3/core/vpConfig.h>
5 
6 #if defined(VISP_HAVE_REALSENSE2) && defined(VISP_HAVE_OPENCV) && defined(VISP_HAVE_NLOHMANN_JSON)
7 #include <visp3/core/vpDisplay.h>
8 #include <visp3/core/vpIoTools.h>
9 #include <visp3/gui/vpDisplayFactory.h>
10 #include <visp3/mbt/vpMbGenericTracker.h>
11 #include <visp3/sensor/vpRealSense2.h>
12 
13 #include VISP_NLOHMANN_JSON(json.hpp)
14 using json = nlohmann::json;
15 
16 
17 int main(int argc, char *argv[])
18 {
19 #ifdef ENABLE_VISP_NAMESPACE
20  using namespace VISP_NAMESPACE_NAME;
21 #endif
22 
23  std::string config_file = "";
24  std::string model = "";
25  std::string init_file = "";
26 
27  double proj_error_threshold = 25;
28 
29  for (int i = 1; i < argc; i++) {
30  if (std::string(argv[i]) == "--config" && i + 1 < argc) {
31  config_file = std::string(argv[i + 1]);
32  }
33  else if (std::string(argv[i]) == "--model" && i + 1 < argc) {
34  model = std::string(argv[i + 1]);
35  }
36  else if (std::string(argv[i]) == "--init_file" && i + 1 < argc) {
37  init_file = std::string(argv[i + 1]);
38  }
39  else if (std::string(argv[i]) == "--proj_error_threshold" && i + 1 < argc) {
40  proj_error_threshold = std::atof(argv[i + 1]);
41  }
42  else if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h") {
43  std::cout << "Usage: \n"
44  << argv[0]
45  << "--config <settings.json>"
46  << " --model <object.cao>"
47  " --init_file <object.init>"
48  " [--proj_error_threshold <threshold between 0 and 90> (default: "
49  << proj_error_threshold
50  << ")]"
51  << std::endl;
52 
53  std::cout << "\n** How to track a 4.2 cm width cube with manual initialization:\n"
54  << argv[0] << "--config model/cube/rgbd-tracker.json --model model/cube/cube.cao" << std::endl;
55  return EXIT_SUCCESS;
56  }
57  }
58 
59  std::cout << "Config files: " << std::endl;
60  std::cout << " JSON config: "
61  << "\"" << config_file << "\"" << std::endl;
62  std::cout << " Model: "
63  << "\"" << model << "\"" << std::endl;
64  std::cout << " Init file: "
65  << "\"" << init_file << "\"" << std::endl;
66 
67  if (config_file.empty()) {
68  std::cout << "No JSON configuration was provided!" << std::endl;
69  return EXIT_FAILURE;
70  }
72  vpRealSense2 realsense;
73  int width = 640, height = 480;
74  int fps = 30;
75  rs2::config config;
76  config.enable_stream(RS2_STREAM_COLOR, width, height, RS2_FORMAT_RGBA8, fps);
77  config.enable_stream(RS2_STREAM_DEPTH, width, height, RS2_FORMAT_Z16, fps);
78 
79  try {
80  realsense.open(config);
81  }
82  catch (const vpException &e) {
83  std::cout << "Catch an exception: " << e.what() << std::endl;
84  std::cout << "Check if the Realsense camera is connected..." << std::endl;
85  return EXIT_SUCCESS;
86  }
87 
88  vpCameraParameters cam_color =
90  vpCameraParameters cam_depth =
92 
93  std::cout << "Sensor internal camera parameters for color camera: " << cam_color << std::endl;
94  std::cout << "Sensor internal camera parameters for depth camera: " << cam_depth << std::endl;
95 
96  vpImage<vpRGBa> I_color(height, width); // acquired by the realsense
97  vpImage<unsigned char> I_gray(height, width); // Fed to the tracker if we use edge or KLT features
98  vpImage<unsigned char> I_depth(height, width); // Depth histogram used for display
99  vpImage<uint16_t> I_depth_raw(height, width); // Raw depth acquired by the realsense
100  std::vector<vpColVector> pointcloud; // fed to the tracker
102 
103  vpHomogeneousMatrix depth_M_color = realsense.getTransformation(RS2_STREAM_COLOR, RS2_STREAM_DEPTH);
104  std::map<std::string, vpHomogeneousMatrix> mapOfCameraTransformations;
105  std::map<std::string, const vpImage<unsigned char> *> mapOfImages;
106  std::map<std::string, std::string> mapOfInitFiles;
107  std::map<std::string, const std::vector<vpColVector> *> mapOfPointclouds;
108  std::map<std::string, unsigned int> mapOfWidths, mapOfHeights;
109  std::map<std::string, vpCameraParameters> mapOfCameraIntrinsics;
110 
112  vpMbGenericTracker tracker;
113  tracker.loadConfigFile(config_file);
115  if (model.empty() && init_file.empty()) {
116  std::ifstream config(config_file);
117  const json j = json::parse(config);
118  config.close();
119  if (j.contains("model")) {
120  model = j["model"];
121  }
122  else {
123  std::cerr << "No model was provided in either JSON file or arguments" << std::endl;
124  return EXIT_FAILURE;
125  }
126  }
127  if (init_file.empty()) {
128  const std::string parentname = vpIoTools::getParent(model);
129  init_file = (parentname.empty() ? "" : (parentname + "/")) + vpIoTools::getNameWE(model) + ".init";
130  }
131 
133  std::string color_key = "", depth_key = "";
134  for (const auto &tracker_type : tracker.getCameraTrackerTypes()) {
135  std::cout << "tracker key == " << tracker_type.first << std::endl;
136  // Initialise for color features
137  if (tracker_type.second & vpMbGenericTracker::EDGE_TRACKER || tracker_type.second & vpMbGenericTracker::KLT_TRACKER) {
138  color_key = tracker_type.first;
139  mapOfImages[color_key] = &I_gray;
140  mapOfInitFiles[color_key] = init_file;
141  mapOfWidths[color_key] = width;
142  mapOfHeights[color_key] = height;
143  mapOfCameraIntrinsics[color_key] = cam_color;
144  }
145  // Initialize for depth features
146  if (tracker_type.second & vpMbGenericTracker::DEPTH_DENSE_TRACKER || tracker_type.second & vpMbGenericTracker::DEPTH_NORMAL_TRACKER) {
147  depth_key = tracker_type.first;
148  mapOfImages[depth_key] = &I_depth;
149  mapOfWidths[depth_key] = width;
150  mapOfHeights[depth_key] = height;
151  mapOfCameraIntrinsics[depth_key] = cam_depth;
152  mapOfCameraTransformations[depth_key] = depth_M_color;
153  mapOfPointclouds[depth_key] = &pointcloud;
154  }
155  }
156  const bool use_depth = !depth_key.empty();
157  const bool use_color = !color_key.empty();
160  if (tracker.getNbPolygon() == 0) { // Not already loaded by JSON
161  tracker.loadModel(model);
162  }
164 
166  std::cout << "Updating configuration with parameters provided by RealSense SDK..." << std::endl;
167  tracker.setCameraParameters(mapOfCameraIntrinsics);
168  if (use_color && use_depth) {
169  tracker.setCameraTransformationMatrix(mapOfCameraTransformations);
170  }
172  unsigned int _posx = 100, _posy = 50;
173 
174 #if defined(VISP_HAVE_DISPLAY)
175 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
176  std::shared_ptr<vpDisplay> display1 = vpDisplayFactory::createDisplay();
177  std::shared_ptr<vpDisplay> display2 = vpDisplayFactory::createDisplay();
178 #else
181 #endif
182  if (use_color)
183  display1->init(I_gray, _posx, _posy, "Color stream");
184  if (use_depth)
185  display2->init(I_depth, _posx + I_gray.getWidth() + 10, _posy, "Depth stream");
186 #endif
187 
188  while (true) {
189  realsense.acquire((unsigned char *)I_color.bitmap, (unsigned char *)I_depth_raw.bitmap, nullptr, nullptr);
190 
191  if (use_color) {
192  vpImageConvert::convert(I_color, I_gray);
193  vpDisplay::display(I_gray);
194  vpDisplay::displayText(I_gray, 20, 20, "Click when ready.", vpColor::red);
195  vpDisplay::flush(I_gray);
196 
197  if (vpDisplay::getClick(I_gray, false)) {
198  break;
199  }
200  }
201  if (use_depth) {
202  vpImageConvert::createDepthHistogram(I_depth_raw, I_depth);
203 
204  vpDisplay::display(I_depth);
205  vpDisplay::displayText(I_depth, 20, 20, "Click when ready.", vpColor::red);
206  vpDisplay::flush(I_depth);
207 
208  if (vpDisplay::getClick(I_depth, false)) {
209  break;
210  }
211  }
212  }
213 
214  tracker.setProjectionErrorComputation(true);
216  tracker.initClick(mapOfImages, mapOfInitFiles, true);
219  std::vector<double> times_vec;
220  try {
221  bool quit = false;
222  double loop_t = 0;
224 
225  while (!quit) {
226  double t = vpTime::measureTimeMs();
227  bool tracking_failed = false;
228 
229  // Acquire images and update tracker input data
230  realsense.acquire((unsigned char *)I_color.bitmap, (unsigned char *)I_depth_raw.bitmap, &pointcloud, nullptr, nullptr);
231 
232  if (use_color) {
233  vpImageConvert::convert(I_color, I_gray);
234  vpDisplay::display(I_gray);
235  }
236  if (use_depth) {
237  vpImageConvert::createDepthHistogram(I_depth_raw, I_depth);
238  vpDisplay::display(I_depth);
239  }
240 
241  if (use_color && use_depth) {
242  mapOfImages[color_key] = &I_gray;
243  mapOfPointclouds[depth_key] = &pointcloud;
244  mapOfWidths[depth_key] = width;
245  mapOfHeights[depth_key] = height;
246  }
247  else if (use_color) {
248  mapOfImages[color_key] = &I_gray;
249  }
250  else if (use_depth) {
251  mapOfPointclouds[depth_key] = &pointcloud;
252  }
253 
254  // Run the tracker
255  try {
256  if (use_color && use_depth) {
257  tracker.track(mapOfImages, mapOfPointclouds, mapOfWidths, mapOfHeights);
258  }
259  else if (use_color) {
260  tracker.track(I_gray);
261  }
262  else if (use_depth) {
263  tracker.track(mapOfImages, mapOfPointclouds, mapOfWidths, mapOfHeights);
264  }
265  }
266  catch (const vpException &e) {
267  std::cout << "Tracker exception: " << e.getStringMessage() << std::endl;
268  tracking_failed = true;
269  }
270 
271  // Get object pose
272  cMo = tracker.getPose();
273 
274  // Check tracking errors
275  double proj_error = 0;
277  // Check tracking errors
278  proj_error = tracker.getProjectionError();
279  }
280  else {
281  proj_error = tracker.computeCurrentProjectionError(I_gray, cMo, cam_color);
282  }
283 
284  if (proj_error > proj_error_threshold) {
285  std::cout << "Tracker needs to restart (projection error detected: " << proj_error << ")" << std::endl;
286  }
287 
288  // Display tracking results
289  if (!tracking_failed) {
290  // Turn display features on
291  tracker.setDisplayFeatures(true);
292 
293  if (use_color && use_depth) {
294  tracker.display(I_gray, I_depth, cMo, depth_M_color * cMo, cam_color, cam_depth, vpColor::red, 3);
295  vpDisplay::displayFrame(I_gray, cMo, cam_color, 0.05, vpColor::none, 3);
296  vpDisplay::displayFrame(I_depth, depth_M_color * cMo, cam_depth, 0.05, vpColor::none, 3);
297  }
298  else if (use_color) {
299  tracker.display(I_gray, cMo, cam_color, vpColor::red, 3);
300  vpDisplay::displayFrame(I_gray, cMo, cam_color, 0.05, vpColor::none, 3);
301  }
302  else if (use_depth) {
303  tracker.display(I_depth, cMo, cam_depth, vpColor::red, 3);
304  vpDisplay::displayFrame(I_depth, cMo, cam_depth, 0.05, vpColor::none, 3);
305  }
306 
307  { // Display total number of features
308  std::stringstream ss;
309  ss << "Nb features: " << tracker.getError().size();
310  vpDisplay::displayText(I_gray, I_gray.getHeight() - 50, 20, ss.str(), vpColor::red);
311  }
312  { // Display number of feature per type
313  std::stringstream ss;
314  ss << "Features: edges " << tracker.getNbFeaturesEdge() << ", klt " << tracker.getNbFeaturesKlt()
315  << ", depth dense " << tracker.getNbFeaturesDepthDense() << ", depth normal" << tracker.getNbFeaturesDepthNormal();
316  vpDisplay::displayText(I_gray, I_gray.getHeight() - 30, 20, ss.str(), vpColor::red);
317  }
318  }
319 
320  std::stringstream ss;
321  loop_t = vpTime::measureTimeMs() - t;
322  times_vec.push_back(loop_t);
323  ss << "Loop time: " << loop_t << " ms";
324 
326  if (use_color) {
327  vpDisplay::displayText(I_gray, 20, 20, ss.str(), vpColor::red);
328  vpDisplay::displayText(I_gray, 35, 20, "Right click: quit", vpColor::red);
329 
330  vpDisplay::flush(I_gray);
331 
332  if (vpDisplay::getClick(I_gray, button, false)) {
333  if (button == vpMouseButton::button3) {
334  quit = true;
335  }
336  }
337  }
338  if (use_depth) {
339  vpDisplay::displayText(I_depth, 20, 20, ss.str(), vpColor::red);
340  vpDisplay::displayText(I_depth, 40, 20, "Click to quit", vpColor::red);
341  vpDisplay::flush(I_depth);
342 
343  if (vpDisplay::getClick(I_depth, false)) {
344  quit = true;
345  }
346  }
347 
348  }
349  }
350  catch (const vpException &e) {
351  std::cout << "Caught an exception: " << e.what() << std::endl;
352  }
353  // Show tracking performance
354  if (!times_vec.empty()) {
355  std::cout << "\nProcessing time, Mean: " << vpMath::getMean(times_vec)
356  << " ms ; Median: " << vpMath::getMedian(times_vec) << " ; Std: " << vpMath::getStdev(times_vec) << " ms"
357  << std::endl;
358  }
360 
361 #if defined(VISP_HAVE_DISPLAY) && (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
362  if (display != nullptr) {
363  delete display;
364  }
365 #endif
366  return EXIT_SUCCESS;
367 }
368 #elif defined(VISP_HAVE_REALSENSE2) && defined(VISP_HAVE_OPENCV)
369 int main()
370 {
371  std::cout << "Install the JSON 3rd party library (Nlohmann JSON), reconfigure VISP and build again" << std::endl;
372  return EXIT_SUCCESS;
373 }
374 #elif defined(VISP_HAVE_REALSENSE2) && defined(VISP_HAVE_NLOHMANN_JSON)
375 int main()
376 {
377  std::cout << "Install OpenCV, reconfigure VISP and build again" << std::endl;
378  return EXIT_SUCCESS;
379 }
380 #else
381 int main()
382 {
383  std::cout << "Install librealsense2 3rd party, configure and build ViSP again to use this example" << std::endl;
384  return EXIT_SUCCESS;
385 }
386 #endif
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:429
Generic class defining intrinsic camera parameters.
@ perspectiveProjWithoutDistortion
Perspective projection without distortion model.
static const vpColor red
Definition: vpColor.h:198
static const vpColor none
Definition: vpColor.h:210
Class that defines generic functionalities for display.
Definition: vpDisplay.h:178
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
static void displayFrame(const vpImage< unsigned char > &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, double size, const vpColor &color=vpColor::none, unsigned int thickness=1, const vpImagePoint &offset=vpImagePoint(0, 0), const std::string &frameName="", const vpColor &textColor=vpColor::black, const vpImagePoint &textOffset=vpImagePoint(15, 15))
static void flush(const vpImage< unsigned char > &I)
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
error that can be emitted by ViSP classes.
Definition: vpException.h:60
const std::string & getStringMessage() const
Definition: vpException.cpp:67
const char * what() const
Definition: vpException.cpp:71
Implementation of an homogeneous matrix and operations on such kind of matrices.
static void createDepthHistogram(const vpImage< uint16_t > &src_depth, vpImage< vpRGBa > &dest_rgba)
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
static std::string getNameWE(const std::string &pathname)
Definition: vpIoTools.cpp:1227
static std::string getParent(const std::string &pathname)
Definition: vpIoTools.cpp:1314
static double getMedian(const std::vector< double > &v)
Definition: vpMath.cpp:322
static double getStdev(const std::vector< double > &v, bool useBesselCorrection=false)
Definition: vpMath.cpp:353
static double getMean(const std::vector< double > &v)
Definition: vpMath.cpp:302
Real-time 6D object pose tracking using its CAD model.
virtual void setCameraParameters(const vpCameraParameters &camera) VP_OVERRIDE
virtual int getTrackerType() const
virtual void setProjectionErrorComputation(const bool &flag) VP_OVERRIDE
virtual void setDisplayFeatures(bool displayF) VP_OVERRIDE
virtual vpColVector getError() const VP_OVERRIDE
virtual unsigned int getNbFeaturesEdge() const
virtual double computeCurrentProjectionError(const vpImage< unsigned char > &I, const vpHomogeneousMatrix &_cMo, const vpCameraParameters &_cam) VP_OVERRIDE
virtual void getPose(vpHomogeneousMatrix &cMo) const VP_OVERRIDE
virtual std::map< std::string, int > getCameraTrackerTypes() const
virtual unsigned int getNbFeaturesKlt() const
virtual void track(const vpImage< unsigned char > &I) VP_OVERRIDE
virtual unsigned int getNbFeaturesDepthDense() const
virtual void loadModel(const std::string &modelFile, bool verbose=false, const vpHomogeneousMatrix &T=vpHomogeneousMatrix()) VP_OVERRIDE
virtual unsigned int getNbFeaturesDepthNormal() const
virtual unsigned int getNbPolygon() const VP_OVERRIDE
virtual void setCameraTransformationMatrix(const std::string &cameraName, const vpHomogeneousMatrix &cameraTransformationMatrix)
virtual void display(const vpImage< unsigned char > &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, const vpColor &col, unsigned int thickness=1, bool displayFullModel=false) VP_OVERRIDE
virtual void loadConfigFile(const std::string &configFile, bool verbose=true) VP_OVERRIDE
virtual double getProjectionError() const
Definition: vpMbTracker.h:312
vpCameraParameters getCameraParameters(const rs2_stream &stream, vpCameraParameters::vpCameraParametersProjType type=vpCameraParameters::perspectiveProjWithDistortion, int index=-1) const
void acquire(vpImage< unsigned char > &grey, double *ts=nullptr)
bool open(const rs2::config &cfg=rs2::config())
vpHomogeneousMatrix getTransformation(const rs2_stream &from, const rs2_stream &to, int from_index=-1) const
std::shared_ptr< vpDisplay > createDisplay()
Return a smart pointer vpDisplay specialization if a GUI library is available or nullptr otherwise.
vpDisplay * allocateDisplay()
Return a newly allocated vpDisplay specialization if a GUI library is available or nullptr otherwise.
VISP_EXPORT double measureTimeMs()