Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
tutorial-mb-generic-tracker-rgbd-blender.cpp
1
2#include <iostream>
3
4#include <visp3/core/vpConfig.h>
5#include <visp3/core/vpDisplay.h>
6#include <visp3/core/vpIoTools.h>
7#include <visp3/core/vpXmlParserCamera.h>
8#include <visp3/gui/vpDisplayFactory.h>
9#include <visp3/io/vpImageIo.h>
10#include <visp3/mbt/vpMbGenericTracker.h>
11
12#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGCODECS) && defined(VISP_HAVE_PUGIXML)
13#ifdef ENABLE_VISP_NAMESPACE
14using namespace VISP_NAMESPACE_NAME;
15#endif
16
17namespace
18{
19bool read_data(unsigned int cpt, const std::string &video_color_images, const std::string &video_depth_images,
20 bool disable_depth, const std::string &video_ground_truth,
22 unsigned int &depth_width, unsigned int &depth_height,
23 std::vector<vpColVector> &pointcloud, const vpCameraParameters &cam_depth,
24 vpHomogeneousMatrix &cMo_ground_truth)
25{
26 // Read color
27 std::string filename_color = vpIoTools::formatString(video_color_images, cpt);
28
29 if (!vpIoTools::checkFilename(filename_color)) {
30 std::cerr << "Cannot read: " << filename_color << std::endl;
31 return false;
32 }
33 vpImageIo::read(I, filename_color);
34
35 if (!disable_depth) {
36 // Read depth
37 std::string filename_depth = vpIoTools::formatString(video_depth_images, cpt);
38
39 if (!vpIoTools::checkFilename(filename_depth)) {
40 std::cerr << "Cannot read: " << filename_depth << std::endl;
41 return false;
42 }
43 cv::Mat depth_raw = cv::imread(filename_depth, cv::IMREAD_ANYDEPTH | cv::IMREAD_ANYCOLOR);
44 if (depth_raw.empty()) {
45 std::cerr << "Cannot read: " << filename_depth << std::endl;
46 return false;
47 }
48
49 depth_width = static_cast<unsigned int>(depth_raw.cols);
50 depth_height = static_cast<unsigned int>(depth_raw.rows);
51 I_depth_raw.resize(depth_height, depth_width);
52 pointcloud.resize(depth_width * depth_height);
53
54 for (int i = 0; i < depth_raw.rows; i++) {
55 for (int j = 0; j < depth_raw.cols; j++) {
56 I_depth_raw[i][j] = static_cast<uint16_t>(32767.5f * depth_raw.at<cv::Vec3f>(i, j)[0]);
57 double x = 0.0, y = 0.0;
58 // Manually limit the field of view of the depth camera
59 double Z = depth_raw.at<cv::Vec3f>(i, j)[0] > 2.0f ? 0.0 : static_cast<double>(depth_raw.at<cv::Vec3f>(i, j)[0]);
60 vpPixelMeterConversion::convertPoint(cam_depth, j, i, x, y);
61 size_t idx = static_cast<size_t>(i * depth_raw.cols + j);
62 pointcloud[idx].resize(3);
63 pointcloud[idx][0] = x * Z;
64 pointcloud[idx][1] = y * Z;
65 pointcloud[idx][2] = Z;
66 }
67 }
68 }
69
70 // Read ground truth
71 std::string filename_pose = vpIoTools::formatString(video_ground_truth, cpt);
72
73 cMo_ground_truth.load(filename_pose);
74
75 return true;
76}
77} // namespace
78
79void usage(const char **argv, int error, const std::string &data_path, const std::string &model_path, int first_frame)
80{
81 std::cout << "Synopsis" << std::endl
82 << " " << argv[0]
83 << " [--data-path <path>] [--model-path <path>] [--first-frame <index>] [--depth-dense-mode <0|1>] "
84 << " [--depth-normals-mode <0|1>] [--me-mode <0|1>] [--klt-mode <0|1>] [--step-by-step] [--display-ground-truth] [--help, -h]" << std::endl
85 << std::endl;
86 std::cout << "Description" << std::endl
87 << " --data-path <path> Path to the data generated by Blender get_camera_pose_teabox.py" << std::endl
88 << " Python script."
89 << " Default: " << data_path << std::endl
90 << std::endl
91 << " --model-path <path> Path to the cad model and tracker settings." << std::endl
92 << " Default: " << model_path << std::endl
93 << std::endl
94 << " --first-frame <index> First frame number to process." << std::endl
95 << " Default: " << first_frame << std::endl
96 << std::endl
97
98 << " --depth-dense-mode Whether to use dense depth features (0 = off, 1 = on). default: 1" << std::endl
99 << std::endl
100 << " --depth-normals-mode Whether to use normal depth features (0 = off, 1 = on). default: 0" << std::endl
101 << std::endl
102 << " --me-mode Whether to use moving edge features (0 = off, 1 = on). default: 1" << std::endl
103 << std::endl
104 << " --klt-mode Whether to use KLT features (0 = off, 1 = on). Requires OpenCV. default: 1" << std::endl
105 << std::endl
106
107 << " --step-by-step Flag to enable step by step mode." << std::endl
108 << std::endl
109 << " --display-ground-truth Flag to enable displaying ground truth." << std::endl
110 << " When this flag is enabled, there is no tracking. This flag is useful" << std::endl
111 << " to validate the ground truth over the rendered images." << std::endl
112 << std::endl
113 << " --help, -h Print this helper message." << std::endl
114 << std::endl;
115 if (error) {
116 std::cout << "Error" << std::endl
117 << " "
118 << "Unsupported parameter " << argv[error] << std::endl;
119 }
120}
121
122int main(int argc, const char **argv)
123{
124 std::string opt_data_path = "data/teabox";
125 std::string opt_model_path = "model/teabox";
126 unsigned int opt_first_frame = 1;
127 int opt_meMode = 1, opt_kltMode = 1, opt_normalsMode = 0, opt_denseMode = 1;
128
129 bool disable_depth = false;
130 bool opt_disable_klt = false;
131
132 bool opt_display_ground_truth = false;
133 bool opt_step_by_step = false;
134
135 for (int i = 1; i < argc; i++) {
136 if (std::string(argv[i]) == "--data-path" && i + 1 < argc) {
137 opt_data_path = std::string(argv[i + 1]);
138 i++;
139 }
140 else if (std::string(argv[i]) == "--model-path" && i + 1 < argc) {
141 opt_model_path = std::string(argv[i + 1]);
142 i++;
143 }
144 else if (std::string(argv[i]) == "--depth-dense-mode" && i + 1 < argc) {
145 opt_denseMode = static_cast<unsigned int>(atoi(argv[i + 1]));
146 if (opt_denseMode < 0 || opt_denseMode > 1) {
147 usage(argv, 0, opt_data_path, opt_model_path, opt_first_frame);
148 return EXIT_FAILURE;
149 }
150 i++;
151 }
152 else if (std::string(argv[i]) == "--depth-normals-mode" && i + 1 < argc) {
153 opt_normalsMode = static_cast<unsigned int>(atoi(argv[i + 1]));
154 if (opt_normalsMode < 0 || opt_normalsMode > 1) {
155 usage(argv, 0, opt_data_path, opt_model_path, opt_first_frame);
156 return EXIT_FAILURE;
157 }
158 i++;
159 }
160 else if (std::string(argv[i]) == "--me-mode" && i + 1 < argc) {
161 opt_meMode = static_cast<unsigned int>(atoi(argv[i + 1]));
162 if (opt_meMode < 0 || opt_meMode > 1) {
163 usage(argv, 0, opt_data_path, opt_model_path, opt_first_frame);
164 return EXIT_FAILURE;
165 }
166 i++;
167 }
168 else if (std::string(argv[i]) == "--klt-mode" && i + 1 < argc) {
169 opt_kltMode = static_cast<unsigned int>(atoi(argv[i + 1]));
170 if (opt_kltMode < 0 || opt_kltMode > 1) {
171 usage(argv, 0, opt_data_path, opt_model_path, opt_first_frame);
172 return EXIT_FAILURE;
173 }
174 i++;
175 }
176 else if (std::string(argv[i]) == "--display-ground-truth") {
177 opt_display_ground_truth = true;
178 }
179 else if (std::string(argv[i]) == "--step-by-step") {
180 opt_step_by_step = true;
181 }
182 else if (std::string(argv[i]) == "--first-frame" && i + 1 < argc) {
183 opt_first_frame = static_cast<unsigned int>(atoi(argv[i + 1]));
184 i++;
185 }
186 else if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h") {
187 usage(argv, 0, opt_data_path, opt_model_path, opt_first_frame);
188 return EXIT_SUCCESS;
189 }
190 else {
191 usage(argv, i, opt_data_path, opt_model_path, opt_first_frame);
192 return EXIT_FAILURE;
193 }
194 }
195
196 disable_depth = opt_denseMode == 0 && opt_normalsMode == 0;
197
198 std::string video_color_images = vpIoTools::createFilePath(opt_data_path, "color/%04d_L.jpg");
199 std::string video_depth_images = vpIoTools::createFilePath(opt_data_path, "depth/Image%04d_R.exr");
200 std::string ground_truth = vpIoTools::createFilePath(opt_data_path, "ground-truth/Camera_L_%04d.txt");
201 std::string extrinsic_file = vpIoTools::createFilePath(opt_data_path, "depth_M_color.txt");
202 std::string color_camera_name = "Camera_L";
203 std::string depth_camera_name = "Camera_R";
204 std::string color_intrinsic_file = vpIoTools::createFilePath(opt_data_path, color_camera_name + ".xml");
205 std::string depth_intrinsic_file = vpIoTools::createFilePath(opt_data_path, depth_camera_name + ".xml");
206 std::string mbt_config_color = vpIoTools::createFilePath(opt_model_path, "teabox_color.xml");
207 std::string mbt_config_depth = vpIoTools::createFilePath(opt_model_path, "teabox_depth.xml");
208 std::string mbt_cad_model = vpIoTools::createFilePath(opt_model_path, "teabox.cao");
209 std::string mbt_init_file = vpIoTools::createFilePath(opt_model_path, "teabox.init");
210
211 std::cout << "Input data" << std::endl;
212 std::cout << " Color images : " << video_color_images << std::endl;
213 std::cout << " Depth images : " << (disable_depth ? "Disabled" : video_depth_images) << std::endl;
214 std::cout << " Extrinsics : " << (disable_depth ? "Disabled" : extrinsic_file) << std::endl;
215 std::cout << " Color intrinsics: " << color_intrinsic_file << std::endl;
216 std::cout << " Depth intrinsics: " << (disable_depth ? "Disabled" : depth_intrinsic_file) << std::endl;
217 std::cout << " Ground truth : " << ground_truth << std::endl;
218 std::cout << "Tracker settings" << std::endl;
219 std::cout << " Color config : " << mbt_config_color << std::endl;
220 std::cout << " Depth config : " << mbt_config_depth << std::endl;
221 std::cout << " CAD model : " << mbt_cad_model << std::endl;
222 std::cout << " First frame : " << opt_first_frame << std::endl;
223 std::cout << " Step by step : " << opt_step_by_step << std::endl;
224 if (opt_display_ground_truth) {
225 std::cout << " Ground truth is used to project the cad model (no tracking)" << std::endl;
226 }
227 else {
228 std::cout << " Init file : " << mbt_init_file << std::endl;
229 std::cout << " Features : moving-edges " << (opt_disable_klt ? "" : "+ keypoints") << (disable_depth ? "" : " + depth") << std::endl;
230 }
231
232
233 std::vector<int> tracker_types;
234 int colorTracker = 0;
235
236
237 if (opt_meMode == 1) {
238 colorTracker |= vpMbGenericTracker::EDGE_TRACKER;
239 }
240 if (opt_kltMode == 1) {
241#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGPROC) && defined(HAVE_OPENCV_VIDEO)
242 colorTracker |= vpMbGenericTracker::KLT_TRACKER;
243#else
244 std::cerr << "Warning: keypoints cannot be used as features since ViSP is not built with OpenCV 3rd party" << std::endl;
245#endif
246 }
247
248 if (colorTracker == 0) {
249 std::cerr << "You should use at least one type of color feature. If OpenCV is not installed, KLT features are disabled" << std::endl;
250 return EXIT_FAILURE;
251 }
252
253 tracker_types.push_back(colorTracker);
254
255 if (!disable_depth) {
256 int depthTracker = 0;
257 if (opt_denseMode == 1) {
259 }
260 if (opt_normalsMode == 1) {
262 }
263
264 tracker_types.push_back(depthTracker);
265 }
266
267 vpMbGenericTracker tracker(tracker_types);
268 if (!disable_depth) {
269 tracker.loadConfigFile(mbt_config_color, mbt_config_depth, true);
270 }
271 else {
272 tracker.loadConfigFile(mbt_config_color);
273
274 }
275 tracker.loadModel(mbt_cad_model);
277
278 // Update intrinsics camera parameters from Blender generated data
280 if (p.parse(cam_color, color_intrinsic_file, color_camera_name, vpCameraParameters::perspectiveProjWithoutDistortion)
282 std::cout << "Cannot found intrinsics for camera " << color_camera_name << std::endl;
283 }
284 if (p.parse(cam_depth, depth_intrinsic_file, depth_camera_name, vpCameraParameters::perspectiveProjWithoutDistortion)
286 std::cout << "Cannot found intrinsics for camera " << depth_camera_name << std::endl;
287 }
288
289 if (!disable_depth)
290 tracker.setCameraParameters(cam_color, cam_depth);
291 else
292 tracker.setCameraParameters(cam_color);
293
294 // Reload intrinsics from tracker (useless)
295 if (!disable_depth)
296 tracker.getCameraParameters(cam_color, cam_depth);
297 else
298 tracker.getCameraParameters(cam_color);
299 tracker.setDisplayFeatures(true);
300 std::cout << "cam_color:\n" << cam_color << std::endl;
301
302 if (!disable_depth)
303 std::cout << "cam_depth:\n" << cam_depth << std::endl;
304
305 vpImage<uint16_t> I_depth_raw;
307 unsigned int depth_width = 0, depth_height = 0;
308 std::vector<vpColVector> pointcloud;
309 vpHomogeneousMatrix cMo_ground_truth;
310
311 unsigned int frame_cpt = opt_first_frame;
312 read_data(frame_cpt, video_color_images, video_depth_images, disable_depth, ground_truth,
313 I, I_depth_raw, depth_width, depth_height, pointcloud, cam_depth, cMo_ground_truth);
314 vpImageConvert::createDepthHistogram(I_depth_raw, I_depth);
315
316#if defined(VISP_HAVE_DISPLAY)
317#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
318 std::shared_ptr<vpDisplay> display1 = vpDisplayFactory::createDisplay();
319 std::shared_ptr<vpDisplay> display2 = vpDisplayFactory::createDisplay();
320#else
323#endif
324
325 display1->init(I, 0, 0, "Color image");
326 if (!disable_depth) {
327 display2->init(I_depth, static_cast<int>(I.getWidth()), 0, "Depth image");
328 }
329#endif
330
331
332
334 if (!disable_depth) {
335 depth_M_color.load(extrinsic_file);
336 tracker.setCameraTransformationMatrix("Camera2", depth_M_color);
337 std::cout << "depth_M_color:\n" << depth_M_color << std::endl;
338 }
339
340 if (opt_display_ground_truth) {
341 tracker.initFromPose(I, cMo_ground_truth); // I and I_depth must be the same size when using depth features!
342 }
343 else {
344 tracker.initClick(I, mbt_init_file, true); // I and I_depth must be the same size when using depth features!
345 }
346
347 try {
348 bool quit = false;
349 while (!quit && read_data(frame_cpt, video_color_images, video_depth_images, disable_depth,
350 ground_truth, I, I_depth_raw, depth_width, depth_height, pointcloud, cam_depth,
351 cMo_ground_truth)) {
352 vpImageConvert::createDepthHistogram(I_depth_raw, I_depth);
354 vpDisplay::display(I_depth);
355
356 if (opt_display_ground_truth) {
357 tracker.initFromPose(I, cMo_ground_truth); // I and I_depth must be the same size when using depth features!
358 }
359 else {
360 if (!disable_depth) {
361 std::map<std::string, const vpImage<unsigned char> *> mapOfImages;
362 std::map<std::string, const std::vector<vpColVector> *> mapOfPointClouds;
363 std::map<std::string, unsigned int> mapOfPointCloudWidths;
364 std::map<std::string, unsigned int> mapOfPointCloudHeights;
365
366 mapOfImages["Camera1"] = &I;
367 mapOfPointClouds["Camera2"] = &pointcloud;
368 mapOfPointCloudWidths["Camera2"] = depth_width;
369 mapOfPointCloudHeights["Camera2"] = depth_height;
370 tracker.track(mapOfImages, mapOfPointClouds, mapOfPointCloudWidths, mapOfPointCloudHeights);
371 }
372 else {
373 tracker.track(I);
374 }
375 }
376
377 vpHomogeneousMatrix cMo = tracker.getPose();
378 std::cout << "\nFrame: " << frame_cpt << std::endl;
379 if (!opt_display_ground_truth)
380 std::cout << "cMo:\n" << cMo << std::endl;
381 std::cout << "cMo ground truth:\n" << cMo_ground_truth << std::endl;
382 if (!disable_depth) {
383 tracker.display(I, I_depth, cMo, depth_M_color * cMo, cam_color, cam_depth, vpColor::red, 2);
384 vpDisplay::displayFrame(I_depth, depth_M_color * cMo, cam_depth, 0.05, vpColor::none, 2);
385 }
386 else {
387 tracker.display(I, cMo, cam_color, vpColor::red, 2);
388 }
389
390 vpDisplay::displayFrame(I, cMo, cam_color, 0.05, vpColor::none, 2);
391 std::ostringstream oss;
392 oss << "Frame: " << frame_cpt;
393 vpDisplay::setTitle(I, oss.str());
394 if (opt_step_by_step) {
395 vpDisplay::displayText(I, 20, 10, "Left click to trigger next step", vpColor::red);
396 vpDisplay::displayText(I, 40, 10, "Right click to quit step-by-step mode", vpColor::red);
397 }
398 else {
399 vpDisplay::displayText(I, 20, 10, "Left click to trigger step-by-step mode", vpColor::red);
400 vpDisplay::displayText(I, 40, 10, "Right click to exit...", vpColor::red);
401 }
402 if (!opt_display_ground_truth) {
403 {
404 std::stringstream ss;
405 ss << "Nb features: " << tracker.getError().size();
406 vpDisplay::displayText(I, I.getHeight() - 50, 20, ss.str(), vpColor::red);
407 }
408 {
409 std::stringstream ss;
410 ss << "Features: edges " << tracker.getNbFeaturesEdge() << ", klt " << tracker.getNbFeaturesKlt()
411 << ", dense depth " << tracker.getNbFeaturesDepthDense() << ", depth normals " << tracker.getNbFeaturesDepthNormal();
412 vpDisplay::displayText(I, I.getHeight() - 30, 20, ss.str(), vpColor::red);
413 }
414 }
415
417 vpDisplay::flush(I_depth);
418
419 // Button 1: start step by step if not enabled from command line option
420 // Button 2: enables step by step mode
421 // Button 3: ends step by step mode if enabled
422 // quit otherwise
424 if (vpDisplay::getClick(I, button, opt_step_by_step)) {
425 if (button == vpMouseButton::button1 && opt_step_by_step == false) {
426 opt_step_by_step = true;
427 }
428 else if (button == vpMouseButton::button3 && opt_step_by_step == true) {
429 opt_step_by_step = false;
430 }
431 else if (button == vpMouseButton::button3 && opt_step_by_step == false) {
432 quit = true;
433 }
434 else if (button == vpMouseButton::button2) {
435 opt_step_by_step = true;
436 }
437 }
438
439 frame_cpt++;
440 }
441
444 }
445 catch (std::exception &e) {
446 std::cerr << "Catch exception: " << e.what() << std::endl;
447 }
448
449#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11) && defined(VISP_HAVE_DISPLAY)
450 if (display1 != nullptr) {
451 delete display1;
452 }
453 if (display2 != nullptr) {
454 delete display2;
455 }
456#endif
457
458 return EXIT_SUCCESS;
459}
460#else
461int main()
462{
463 std::cout << "To run this tutorial, ViSP should be built with OpenCV and pugixml libraries." << std::endl;
464 return EXIT_SUCCESS;
465}
466#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 setTitle(const vpImage< unsigned char > &I, const std::string &windowtitle)
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)
Implementation of an homogeneous matrix and operations on such kind of matrices.
void load(std::ifstream &f)
static void createDepthHistogram(const vpImage< uint16_t > &src_depth, vpImage< vpRGBa > &dest_rgba)
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition of the vpImage class member functions.
Definition vpImage.h:131
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition vpImage.h:544
static bool checkFilename(const std::string &filename)
static std::string formatString(const std::string &name, unsigned int val)
static std::string createFilePath(const std::string &parent, const std::string &child)
Real-time 6D object pose tracking using its CAD model.
static void convertPoint(const vpCameraParameters &cam, const double &u, const double &v, double &x, double &y)
XML parser to load and save intrinsic camera parameters.
read_data(CameraParameters|None cam_depth, ImageGray I, rs.pipeline pipe)
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.