Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
tutorial-mb-generic-tracker-rgbd-realsense-json.cpp
1
2#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)
14using json = nlohmann::json;
15
16
17int 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
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
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 if (j.at("trackers").begin()->contains("model")) {
123 model = (*j["trackers"].begin())["model"];
124 }
125 else {
126 std::cerr << "No model was provided in either JSON file or arguments" << std::endl;
127 return EXIT_FAILURE;
128 }
129 }
130 if (init_file.empty()) {
131 const std::string parentname = vpIoTools::getParent(model);
132 init_file = (parentname.empty() ? "" : (parentname + "/")) + vpIoTools::getNameWE(model) + ".init";
133 }
134
136 std::string color_key = "", depth_key = "";
137 for (const auto &tracker_type : tracker.getCameraTrackerTypes()) {
138 std::cout << "tracker key == " << tracker_type.first << std::endl;
139 // Initialise for color features
140 if (tracker_type.second & vpMbGenericTracker::EDGE_TRACKER || tracker_type.second & vpMbGenericTracker::KLT_TRACKER) {
141 color_key = tracker_type.first;
142 mapOfImages[color_key] = &I_gray;
143 mapOfInitFiles[color_key] = init_file;
144 mapOfWidths[color_key] = width;
145 mapOfHeights[color_key] = height;
146 mapOfCameraIntrinsics[color_key] = cam_color;
147 }
148 // Initialize for depth features
149 if (tracker_type.second & vpMbGenericTracker::DEPTH_DENSE_TRACKER || tracker_type.second & vpMbGenericTracker::DEPTH_NORMAL_TRACKER) {
150 depth_key = tracker_type.first;
151 mapOfImages[depth_key] = &I_depth;
152 mapOfWidths[depth_key] = width;
153 mapOfHeights[depth_key] = height;
154 mapOfCameraIntrinsics[depth_key] = cam_depth;
155 mapOfCameraTransformations[depth_key] = depth_M_color;
156 mapOfPointclouds[depth_key] = &pointcloud;
157 }
158 }
159 const bool use_depth = !depth_key.empty();
160 const bool use_color = !color_key.empty();
163 if (tracker.getNbPolygon() == 0) { // Not already loaded by JSON
164 tracker.loadModel(model);
165 }
167
169 std::cout << "Updating configuration with parameters provided by RealSense SDK..." << std::endl;
170 tracker.setCameraParameters(mapOfCameraIntrinsics);
171 if (use_color && use_depth) {
172 tracker.setCameraTransformationMatrix(mapOfCameraTransformations);
173 }
175 unsigned int _posx = 100, _posy = 50;
176
177#if defined(VISP_HAVE_DISPLAY)
178#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
179 std::shared_ptr<vpDisplay> display1 = vpDisplayFactory::createDisplay();
180 std::shared_ptr<vpDisplay> display2 = vpDisplayFactory::createDisplay();
181#else
184#endif
185 if (use_color)
186 display1->init(I_gray, _posx, _posy, "Color stream");
187 if (use_depth)
188 display2->init(I_depth, _posx + I_gray.getWidth() + 10, _posy, "Depth stream");
189#endif
190
191 while (true) {
192 realsense.acquire((unsigned char *)I_color.bitmap, (unsigned char *)I_depth_raw.bitmap, nullptr, nullptr);
193
194 if (use_color) {
195 vpImageConvert::convert(I_color, I_gray);
196 vpDisplay::display(I_gray);
197 vpDisplay::displayText(I_gray, 20, 20, "Click when ready.", vpColor::red);
198 vpDisplay::flush(I_gray);
199
200 if (vpDisplay::getClick(I_gray, false)) {
201 break;
202 }
203 }
204 if (use_depth) {
205 vpImageConvert::createDepthHistogram(I_depth_raw, I_depth);
206
207 vpDisplay::display(I_depth);
208 vpDisplay::displayText(I_depth, 20, 20, "Click when ready.", vpColor::red);
209 vpDisplay::flush(I_depth);
210
211 if (vpDisplay::getClick(I_depth, false)) {
212 break;
213 }
214 }
215 }
216
217 tracker.setProjectionErrorComputation(true);
219 tracker.initClick(mapOfImages, mapOfInitFiles, true);
222 std::vector<double> times_vec;
223 try {
224 bool quit = false;
225 double loop_t = 0;
227
228 while (!quit) {
229 double t = vpTime::measureTimeMs();
230 bool tracking_failed = false;
231
232 // Acquire images and update tracker input data
233 realsense.acquire((unsigned char *)I_color.bitmap, (unsigned char *)I_depth_raw.bitmap, &pointcloud, nullptr, nullptr);
234
235 if (use_color) {
236 vpImageConvert::convert(I_color, I_gray);
237 vpDisplay::display(I_gray);
238 }
239 if (use_depth) {
240 vpImageConvert::createDepthHistogram(I_depth_raw, I_depth);
241 vpDisplay::display(I_depth);
242 }
243
244 if (use_color && use_depth) {
245 mapOfImages[color_key] = &I_gray;
246 mapOfPointclouds[depth_key] = &pointcloud;
247 mapOfWidths[depth_key] = width;
248 mapOfHeights[depth_key] = height;
249 }
250 else if (use_color) {
251 mapOfImages[color_key] = &I_gray;
252 }
253 else if (use_depth) {
254 mapOfPointclouds[depth_key] = &pointcloud;
255 }
256
257 // Run the tracker
258 try {
259 if (use_color && use_depth) {
260 tracker.track(mapOfImages, mapOfPointclouds, mapOfWidths, mapOfHeights);
261 }
262 else if (use_color) {
263 tracker.track(I_gray);
264 }
265 else if (use_depth) {
266 tracker.track(mapOfImages, mapOfPointclouds, mapOfWidths, mapOfHeights);
267 }
268 }
269 catch (const vpException &e) {
270 std::cout << "Tracker exception: " << e.getStringMessage() << std::endl;
271 tracking_failed = true;
272 }
273
274 // Get object pose
275 cMo = tracker.getPose();
276
277 // Check tracking errors
278 double proj_error = 0;
279 if (tracker.getTrackerType() & vpMbGenericTracker::EDGE_TRACKER) {
280 // Check tracking errors
281 proj_error = tracker.getProjectionError();
282 }
283 else {
284 proj_error = tracker.computeCurrentProjectionError(I_gray, cMo, cam_color);
285 }
286
287 if (proj_error > proj_error_threshold) {
288 std::cout << "Tracker needs to restart (projection error detected: " << proj_error << ")" << std::endl;
289 }
290
291 // Display tracking results
292 if (!tracking_failed) {
293 // Turn display features on
294 tracker.setDisplayFeatures(true);
295
296 if (use_color && use_depth) {
297 tracker.display(I_gray, I_depth, cMo, depth_M_color * cMo, cam_color, cam_depth, vpColor::red, 3);
298 vpDisplay::displayFrame(I_gray, cMo, cam_color, 0.05, vpColor::none, 3);
299 vpDisplay::displayFrame(I_depth, depth_M_color * cMo, cam_depth, 0.05, vpColor::none, 3);
300 }
301 else if (use_color) {
302 tracker.display(I_gray, cMo, cam_color, vpColor::red, 3);
303 vpDisplay::displayFrame(I_gray, cMo, cam_color, 0.05, vpColor::none, 3);
304 }
305 else if (use_depth) {
306 tracker.display(I_depth, cMo, cam_depth, vpColor::red, 3);
307 vpDisplay::displayFrame(I_depth, cMo, cam_depth, 0.05, vpColor::none, 3);
308 }
309
310 { // Display total number of features
311 std::stringstream ss;
312 ss << "Nb features: " << tracker.getError().size();
313 vpDisplay::displayText(I_gray, I_gray.getHeight() - 50, 20, ss.str(), vpColor::red);
314 }
315 { // Display number of feature per type
316 std::stringstream ss;
317 ss << "Features: edges " << tracker.getNbFeaturesEdge() << ", klt " << tracker.getNbFeaturesKlt()
318 << ", depth dense " << tracker.getNbFeaturesDepthDense() << ", depth normal" << tracker.getNbFeaturesDepthNormal();
319 vpDisplay::displayText(I_gray, I_gray.getHeight() - 30, 20, ss.str(), vpColor::red);
320 }
321 }
322
323 std::stringstream ss;
324 loop_t = vpTime::measureTimeMs() - t;
325 times_vec.push_back(loop_t);
326 ss << "Loop time: " << loop_t << " ms";
327
329 if (use_color) {
330 vpDisplay::displayText(I_gray, 20, 20, ss.str(), vpColor::red);
331 vpDisplay::displayText(I_gray, 35, 20, "Right click: quit", vpColor::red);
332
333 vpDisplay::flush(I_gray);
334
335 if (vpDisplay::getClick(I_gray, button, false)) {
336 if (button == vpMouseButton::button3) {
337 quit = true;
338 }
339 }
340 }
341 if (use_depth) {
342 vpDisplay::displayText(I_depth, 20, 20, ss.str(), vpColor::red);
343 vpDisplay::displayText(I_depth, 40, 20, "Click to quit", vpColor::red);
344 vpDisplay::flush(I_depth);
345
346 if (vpDisplay::getClick(I_depth, false)) {
347 quit = true;
348 }
349 }
350
351 }
352 }
353 catch (const vpException &e) {
354 std::cout << "Caught an exception: " << e.what() << std::endl;
355 }
356 // Show tracking performance
357 if (!times_vec.empty()) {
358 std::cout << "\nProcessing time, Mean: " << vpMath::getMean(times_vec)
359 << " ms ; Median: " << vpMath::getMedian(times_vec) << " ; Std: " << vpMath::getStdev(times_vec) << " ms"
360 << std::endl;
361 }
363
364#if defined(VISP_HAVE_DISPLAY) && (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
365 if (display != nullptr) {
366 delete display;
367 }
368#endif
369 return EXIT_SUCCESS;
370}
371#elif defined(VISP_HAVE_REALSENSE2) && defined(VISP_HAVE_OPENCV)
372int main()
373{
374 std::cout << "Install the JSON 3rd party library (Nlohmann JSON), reconfigure VISP and build again" << std::endl;
375 return EXIT_SUCCESS;
376}
377#elif defined(VISP_HAVE_REALSENSE2) && defined(VISP_HAVE_NLOHMANN_JSON)
378int main()
379{
380 std::cout << "Install OpenCV, reconfigure VISP and build again" << std::endl;
381 return EXIT_SUCCESS;
382}
383#else
384int main()
385{
386 std::cout << "Install librealsense2 3rd party, configure and build ViSP again to use this example" << std::endl;
387 return EXIT_SUCCESS;
388}
389#endif
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:171
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
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)
Definition of the vpImage class member functions.
Definition vpImage.h:131
static std::string getNameWE(const std::string &pathname)
static std::string getParent(const std::string &pathname)
static double getMedian(const std::vector< double > &v)
Definition vpMath.cpp:343
static double getStdev(const std::vector< double > &v, bool useBesselCorrection=false)
Definition vpMath.cpp:374
static double getMean(const std::vector< double > &v)
Definition vpMath.cpp:323
Real-time 6D object pose tracking using its CAD model.
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()