MRPT  1.9.9
test.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | https://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2019, Individual contributors, see AUTHORS file |
6  | See: https://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See: https://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 /*
11  Example : kinect-online-offline-demo
12  Web page :
13  https://www.mrpt.org/Switching_between_reading_live_Kinect_RGBD_dataset_for_debugging
14 
15  Purpose : Demonstrate how to programmatically switch between online Kinect
16  grabbing and offline parsing a Rawlog dataset. Refer to the launch
17  of the grabbing thread in Test_KinectOnlineOffline()
18 */
19 
20 #include <mrpt/gui.h>
21 #include <mrpt/hwdrivers/CKinect.h>
22 #include <mrpt/img/TColor.h>
25 #include <mrpt/obs/CRawlog.h>
26 #include <mrpt/opengl/CFrustum.h>
32 #include <mrpt/system/CTicTac.h>
34 #include <iostream>
35 #include <memory>
36 
37 // Demonstrate MRPT RGB+D --> PCL point cloud conversion:
38 #if MRPT_HAS_PCL
39 #include <mrpt/maps/PCL_adapters.h>
40 #endif
41 
42 using namespace mrpt;
43 using namespace mrpt::hwdrivers;
44 using namespace mrpt::gui;
45 using namespace mrpt::obs;
46 using namespace mrpt::system;
47 using namespace mrpt::img;
48 using namespace mrpt::serialization;
49 using namespace mrpt::io;
50 using namespace std;
51 
52 // Thread for online/offline capturing: This should be done in another thread
53 // specially in the online mode, but also in offline to emulate the
54 // possibility
55 // of losing frames if our program doesn't process them quickly.
56 struct TThreadParam
57 {
59  bool _is_online, const string& _rawlog_file = string(),
60  bool _generate_3D_pointcloud_in_this_thread = false)
61  : is_online(_is_online),
62  rawlog_file(_rawlog_file),
63  generate_3D_pointcloud_in_this_thread(
64  _generate_3D_pointcloud_in_this_thread),
65  quit(false),
66  Hz(0)
67  {
68  }
69 
70  /** true: live grabbing from the sensor, false: from rawlog */
71  const bool is_online;
72  /** Only when is_online==false */
73  const string rawlog_file;
74  /** true: populate the 3D point fields in the output observation; false:
75  * only RGB and Depth images. */
76  const bool generate_3D_pointcloud_in_this_thread;
77 
78  /** In/Out variable: Forces the thread to exit or indicates an error in the
79  * thread that caused it to end. */
80  volatile bool quit;
81  /** Out variable: Approx. capturing rate from the thread. */
82  volatile double Hz;
83 
84  /** RGB+D (+ optionally, 3D point cloud) */
86 };
87 
88 // Only for offline operation:
89 // Uncoment to force the simulated sensor to run at this given rate.
90 // If commented out, the simulated sensor will reproduce the real rate
91 // as indicated by timestamps in the dataset.
92 //#define FAKE_KINECT_FPS_RATE 10 // In Hz
93 
94 // ----------------------------------------------------
95 // The online/offline grabbing thread
96 // ----------------------------------------------------
98 {
99  try
100  {
101  typedef std::unique_ptr<CKinect>
102  CKinectPtr; // This assures automatic destruction
103 
104  // Only one of these will be actually used:
105  CKinectPtr kinect;
106  CFileGZInputStream dataset;
107 
108  mrpt::system::TTimeStamp dataset_prev_tim = INVALID_TIMESTAMP;
109  mrpt::system::TTimeStamp my_last_read_obs_tim = INVALID_TIMESTAMP;
110 
111  if (p.is_online)
112  {
113  // Online:
114  // ---------------------
115  kinect.reset(new CKinect());
116 
117  // Set params:
118  kinect->enableGrabRGB(true);
119  kinect->enableGrabDepth(true);
120  kinect->enableGrabAccelerometers(false);
121  kinect->enableGrab3DPoints(p.generate_3D_pointcloud_in_this_thread);
122 
123  // Open:
124  cout << "Calling CKinect::initialize()...";
125  kinect->initialize();
126  cout << "OK\n";
127  }
128  else
129  {
130  // Offline:
131  // ---------------------
132  if (!dataset.open(p.rawlog_file))
133  throw std::runtime_error(
134  "Couldn't open rawlog dataset file for input...");
135 
136  // Set external images directory:
137  CImage::setImagesPathBase(
138  CRawlog::detectImagesDirectory(p.rawlog_file));
139  }
140 
141  CTicTac tictac;
142  int nImgs = 0;
143 
144  while (!p.quit)
145  {
146  if (p.is_online)
147  {
148  // Grab new observation from the camera:
149  bool there_is_obs = true, hard_error = false;
150 
151  // Smart pointers to observations. Memory pooling used inside
152  auto obs = CObservation3DRangeScan::Create();
153  kinect->getNextObservation(*obs, there_is_obs, hard_error);
154 
155  if (hard_error)
156  throw std::runtime_error(
157  "Sensor returned 'hardware error'");
158  else if (there_is_obs)
159  {
160  // Send object to the main thread:
161  std::atomic_store(&p.new_obs, obs);
162  }
163  }
164  else
165  {
166  // Offline:
167  CSerializable::Ptr obs;
168  do
169  {
170  try
171  {
172  archiveFrom(dataset) >> obs;
173  }
174  catch (const std::exception& e)
175  {
176  throw std::runtime_error(
177  string(
178  "\nError reading from dataset file (EOF?):\n") +
179  string(e.what()));
180  }
181  ASSERT_(obs);
182  } while (!IS_CLASS(*obs, CObservation3DRangeScan));
183 
184  // We have one observation:
186  std::dynamic_pointer_cast<CObservation3DRangeScan>(obs);
187  obs3D->load(); // *Important* This is needed to load the range
188  // image if stored as a separate file.
189 
190  // Do we have to wait to emulate real-time behavior?
191  const mrpt::system::TTimeStamp cur_tim = obs3D->timestamp;
193 
194  if (dataset_prev_tim != INVALID_TIMESTAMP &&
195  my_last_read_obs_tim != INVALID_TIMESTAMP)
196  {
197 #ifndef FAKE_KINECT_FPS_RATE
198  const double At_dataset =
199  mrpt::system::timeDifference(dataset_prev_tim, cur_tim);
200 #else
201  const double At_dataset = 1.0 / FAKE_KINECT_FPS_RATE;
202 #endif
203  const double At_actual = mrpt::system::timeDifference(
204  my_last_read_obs_tim, now_tim);
205 
206  const double need_to_wait_ms =
207  1000. * (At_dataset - At_actual);
208  // cout << "[Kinect grab thread] Need to wait (ms): " <<
209  // need_to_wait_ms << endl;
210  if (need_to_wait_ms > 0)
211  std::this_thread::sleep_for(
212  std::chrono::duration<double, std::milli>(
213  need_to_wait_ms));
214  }
215 
216  // Send observation to main thread:
217  std::atomic_store(&p.new_obs, obs3D);
218 
219  dataset_prev_tim = cur_tim;
220  my_last_read_obs_tim = mrpt::system::now();
221  }
222 
223  // Update Hz rate estimate
224  nImgs++;
225  if (nImgs > 10)
226  {
227  p.Hz = nImgs / tictac.Tac();
228  nImgs = 0;
229  tictac.Tic();
230  }
231  }
232  }
233  catch (const std::exception& e)
234  {
235  cout << "Exception in Kinect thread: " << e.what() << endl;
236  p.quit = true;
237  }
238 }
239 
240 // ------------------------------------------------------
241 // Test_KinectOnlineOffline
242 // ------------------------------------------------------
244  bool is_online, const string& rawlog_file = string())
245 {
246  // Launch grabbing thread:
247  // --------------------------------------------------------
248  TThreadParam thrPar(
249  is_online, rawlog_file,
250  false // generate_3D_pointcloud_in_this_thread -> Don't, we'll do it in
251  // this main thread.
252  );
253 
254  std::thread thHandle(thread_grabbing, std::ref(thrPar));
255 
256  // Wait until data stream starts so we can say for sure the sensor has been
257  // initialized OK:
258  cout << "Waiting for sensor initialization...\n";
259  do
260  {
261  CObservation3DRangeScan::Ptr newObs = std::atomic_load(&thrPar.new_obs);
262  if (newObs && newObs->timestamp != INVALID_TIMESTAMP)
263  break;
264  else
265  std::this_thread::sleep_for(10ms);
266  } while (!thrPar.quit);
267 
268  // Check error condition:
269  if (thrPar.quit) return;
270  cout << "OK! Sensor started to emit observations.\n";
271 
272  // Create window and prepare OpenGL object in the scene:
273  // --------------------------------------------------------
274  mrpt::gui::CDisplayWindow3D win3D("Kinect 3D view", 800, 600);
275 
276  win3D.setCameraAzimuthDeg(140);
277  win3D.setCameraElevationDeg(20);
278  win3D.setCameraZoom(8.0);
279  win3D.setFOV(90);
280  win3D.setCameraPointingToPoint(2.5, 0, 0);
281 
284  gl_points->setPointSize(2.5);
285 
287  viewInt; // Extra viewports for the RGB images.
288  {
289  mrpt::opengl::COpenGLScene::Ptr& scene = win3D.get3DSceneAndLock();
290 
291  // Create the Opengl object for the point cloud:
292  scene->insert(gl_points);
293  scene->insert(mrpt::opengl::CGridPlaneXY::Create());
295 
296  const double aspect_ratio = 480.0 / 640.0;
297  const int VW_WIDTH =
298  400; // Size of the viewport into the window, in pixel units.
299  const int VW_HEIGHT = aspect_ratio * VW_WIDTH;
300 
301  // Create an extra opengl viewport for the RGB image:
302  viewInt = scene->createViewport("view2d_int");
303  viewInt->setViewportPosition(5, 30, VW_WIDTH, VW_HEIGHT);
304  win3D.addTextMessage(
305  10, 30 + VW_HEIGHT + 10, "Intensity data", TColorf(1, 1, 1), 2,
307 
308  win3D.addTextMessage(
309  5, 5, format("'o'/'i'-zoom out/in, ESC: quit"), TColorf(0, 0, 1),
311 
312  win3D.unlockAccess3DScene();
313  win3D.repaint();
314  }
315 
317 
318  while (win3D.isOpen() && !thrPar.quit)
319  {
320  CObservation3DRangeScan::Ptr newObs = std::atomic_load(&thrPar.new_obs);
321  if (newObs && newObs->timestamp != INVALID_TIMESTAMP &&
322  newObs->timestamp != last_obs_tim)
323  {
324  // It IS a new observation:
325  last_obs_tim = newObs->timestamp;
326 
327  // Update visualization ---------------------------------------
328 
329  win3D.get3DSceneAndLock();
330 
331  // Estimated grabbing rate:
332  win3D.addTextMessage(
333  -350, -13,
334  format(
335  "Timestamp: %s",
336  mrpt::system::dateTimeLocalToString(last_obs_tim).c_str()),
337  TColorf(0.6, 0.6, 0.6), "mono", 10, mrpt::opengl::FILL, 100);
338  win3D.addTextMessage(
339  -100, -30, format("%.02f Hz", thrPar.Hz), TColorf(1, 1, 1),
340  "mono", 10, mrpt::opengl::FILL, 101);
341 
342  // Show intensity image:
343  if (newObs->hasIntensityImage)
344  {
345  viewInt->setImageView(newObs->intensityImage); // This is not
346  // "_fast" since
347  // the intensity
348  // image may be
349  // needed later
350  // on.
351  }
352  win3D.unlockAccess3DScene();
353 
354  // -------------------------------------------------------
355  // Create 3D points from RGB+D data
356  //
357  // There are several methods to do this.
358  // Switch the #if's to select among the options:
359  // See also:
360  // https://www.mrpt.org/Generating_3D_point_clouds_from_RGB_D_observations
361  // -------------------------------------------------------
362  if (newObs->hasRangeImage)
363  {
364  static mrpt::system::CTimeLogger logger;
365  logger.enter("RGBD->3D");
366 
367 // Pathway: RGB+D --> PCL <PointXYZ> --> XYZ opengl
368 #if 0
369  static pcl::PointCloud<pcl::PointXYZ> cloud;
370  logger.enter("RGBD->3D.projectInto");
371  newObs->project3DPointsFromDepthImageInto(cloud, false /* without obs.sensorPose */);
372  logger.leave("RGBD->3D.projectInto");
373 
374  win3D.get3DSceneAndLock();
375  logger.enter("RGBD->3D.load in OpenGL");
376  gl_points->loadFromPointsMap(&cloud);
377  logger.leave("RGBD->3D.load in OpenGL");
378  win3D.unlockAccess3DScene();
379 #endif
380 
381 // Pathway: RGB+D --> PCL <PointXYZRGB> --> XYZ+RGB opengl
382 #if 0
383  static pcl::PointCloud<pcl::PointXYZRGB> cloud;
384  logger.enter("RGBD->3D.projectInto");
385  newObs->project3DPointsFromDepthImageInto(cloud, false /* without obs.sensorPose */);
386  logger.leave("RGBD->3D.projectInto");
387 
388  win3D.get3DSceneAndLock();
389  logger.enter("RGBD->3D.load in OpenGL");
390  gl_points->loadFromPointsMap(&cloud);
391  logger.leave("RGBD->3D.load in OpenGL");
392  win3D.unlockAccess3DScene();
393 #endif
394 
395 // Pathway: RGB+D --> XYZ+RGB opengl
396 #if 1
397  win3D.get3DSceneAndLock();
398  logger.enter("RGBD->3D.projectInto");
401 
402  newObs->project3DPointsFromDepthImageInto(*gl_points, pp);
403 
404  logger.leave("RGBD->3D.projectInto");
405  win3D.unlockAccess3DScene();
406 #endif
407 
408 // Pathway: RGB+D --> XYZ+RGB opengl (With a 6D global pose of the robot)
409 #if 0
410  const CPose3D globalPose(1,2,3,DEG2RAD(10),DEG2RAD(20),DEG2RAD(30));
411  win3D.get3DSceneAndLock();
412  logger.enter("RGBD->3D.projectInto");
413  newObs->project3DPointsFromDepthImageInto(*gl_points, false /* without obs.sensorPose */, &globalPose);
414  logger.leave("RGBD->3D.projectInto");
415  win3D.unlockAccess3DScene();
416 #endif
417 
418 // Pathway: RGB+D --> internal local XYZ pointcloud --> XYZ+RGB point cloud map
419 // --> XYZ+RGB opengl
420 #if 0
421  // Project 3D points:
422  if (!newObs->hasPoints3D)
423  {
424  logger.enter("RGBD->3D.projectInto");
425  newObs->project3DPointsFromDepthImage();
426  logger.leave("RGBD->3D.projectInto");
427  }
428 
429  CColouredPointsMap pntsMap;
430  pntsMap.colorScheme.scheme = CColouredPointsMap::cmFromIntensityImage;
431  pntsMap.loadFromRangeScan(*newObs);
432 
433  win3D.get3DSceneAndLock();
434  logger.enter("RGBD->3D.load in OpenGL");
435  gl_points->loadFromPointsMap(&pntsMap);
436  logger.leave("RGBD->3D.load in OpenGL");
437  win3D.unlockAccess3DScene();
438 #endif
439 
440  logger.leave("RGBD->3D");
441  }
442 
443  win3D.repaint();
444  } // end update visualization:
445 
446  // Process possible keyboard commands:
447  // --------------------------------------
448  if (win3D.keyHit())
449  {
450  const int key = tolower(win3D.getPushedKey());
451 
452  switch (key)
453  {
454  // Some of the keys are processed in this thread:
455  case 'o':
456  win3D.setCameraZoom(win3D.getCameraZoom() * 1.2);
457  win3D.repaint();
458  break;
459  case 'i':
460  win3D.setCameraZoom(win3D.getCameraZoom() / 1.2);
461  win3D.repaint();
462  break;
463  case 27: // ESC
464  thrPar.quit = true;
465  break;
466  default:
467  break;
468  };
469  }
470 
471  std::this_thread::sleep_for(1ms);
472  }
473 
474  cout << "Waiting for grabbing thread to exit...\n";
475  thrPar.quit = true;
476  thHandle.join();
477  cout << "Bye!\n";
478 }
479 
480 int main(int argc, char** argv)
481 {
482  try
483  {
484  // Determine online/offline operation:
485  if (argc != 1 && argc != 2)
486  {
487  cerr << "Usage:\n"
488  << argv[0] << " --> Online grab from sensor\n"
489  << argv[0] << " [DATASET.rawlog] --> Offline from dataset\n";
490  return 1;
491  }
492 
493  if (argc == 1)
494  {
495  // Online
496  cout << "Using online operation" << endl;
498  }
499  else
500  {
501  // Offline:
502  cout << "Using offline operation with: " << argv[1] << endl;
503  Test_KinectOnlineOffline(false, string(argv[1]));
504  }
505 
506  std::this_thread::sleep_for(50ms);
507  return 0;
508  }
509  catch (const std::exception& e)
510  {
511  std::cerr << "MRPT error: " << mrpt::exception_to_str(e) << std::endl;
512  return -1;
513  }
514  catch (...)
515  {
516  printf("Another exception!!");
517  return -1;
518  }
519 }
double Tac() noexcept
Stops the stopwatch.
Definition: CTicTac.cpp:86
static Ptr Create(Args &&... args)
GLenum GLint ref
Definition: glext.h:4062
double DEG2RAD(const double x)
Degrees to radians.
A high-performance stopwatch, with typical resolution of nanoseconds.
Declares a class derived from "CObservation" that encapsules a 3D range scan measurement, as from a time-of-flight range camera or any other RGBD sensor.
mrpt::system::TTimeStamp now()
A shortcut for system::getCurrentTime.
Definition: datetime.h:86
Contains classes for various device interfaces.
STL namespace.
CArchiveStreamBase< STREAM > archiveFrom(STREAM &s)
Helper function to create a templatized wrapper CArchive object for a: MRPT&#39;s CStream, std::istream, std::ostream, std::stringstream.
Definition: CArchive.h:586
bool open(const std::string &fileName, mrpt::optional_ref< std::string > error_msg=std::nullopt)
Opens the file for read.
#define ASSERT_(f)
Defines an assertion mechanism.
Definition: exceptions.h:120
mrpt::Clock::time_point TTimeStamp
A system independent time type, it holds the the number of 100-nanosecond intervals since January 1...
Definition: datetime.h:40
A class for grabing "range images", intensity images (either RGB or IR) and other information from an...
Definition: CKinect.h:265
This namespace contains representation of robot actions and observations.
void Test_KinectOnlineOffline(bool is_online, const string &rawlog_file=string())
#define IS_CLASS(obj, class_name)
True if the given reference to object (derived from mrpt::rtti::CObject) is of the given class...
Definition: CObject.h:133
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
A versatile "profiler" that logs the time spent within each pair of calls to enter(X)-leave(X), among other stats.
CSetOfObjects::Ptr CornerXYZ(float scale=1.0)
Returns three arrows representing a X,Y,Z 3D corner.
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:16
void thread_grabbing(TThreadParam &p)
Used in CObservation3DRangeScan::project3DPointsFromDepthImageInto()
A RGB color - floats in the range [0,1].
Definition: TColor.h:77
std::string exception_to_str(const std::exception &e)
Builds a nice textual representation of a nested exception, which if generated using MRPT macros (THR...
Definition: exceptions.cpp:59
Transparently opens a compressed "gz" file and reads uncompressed data from it.
Classes for creating GUI windows for 2D and 3D visualization.
Definition: about_box.h:14
double timeDifference(const mrpt::system::TTimeStamp t_first, const mrpt::system::TTimeStamp t_later)
Returns the time difference from t1 to t2 (positive if t2 is posterior to t1), in seconds...
Definition: datetime.h:123
void Tic() noexcept
Starts the stopwatch.
Definition: CTicTac.cpp:75
std::string dateTimeLocalToString(const mrpt::system::TTimeStamp t)
Convert a timestamp into this textual form (in local time): YEAR/MONTH/DAY,HH:MM:SS.MMM.
Definition: datetime.cpp:170
renders glyphs as filled polygons
Definition: opengl_fonts.h:35
GLfloat GLfloat p
Definition: glext.h:6398
static Ptr Create(Args &&... args)
Definition: CGridPlaneXY.h:31
void enter(const std::string_view &func_name)
Start of a named section.
#define INVALID_TIMESTAMP
Represents an invalid timestamp, where applicable.
Definition: datetime.h:43
double leave(const std::string_view &func_name)
End of a named section.
bool takeIntoAccountSensorPoseOnRobot
(Default: false) If false, local (sensor-centric) coordinates of points are generated.
A graphical user interface (GUI) for efficiently rendering 3D scenes in real-time.



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: 8fe78517f Sun Jul 14 19:43:28 2019 +0200 at lun oct 28 02:10:00 CET 2019