41 #include <visp3/core/vpConfig.h>
42 #if (defined(VISP_HAVE_REALSENSE) || defined(VISP_HAVE_REALSENSE2)) && defined(VISP_HAVE_THREADS) \
43 && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI)) && defined(VISP_HAVE_PUGIXML)
45 #include <condition_variable>
51 #if defined(VISP_HAVE_PCL)
52 #include <pcl/pcl_config.h>
53 #if defined(VISP_HAVE_PCL_COMMON)
54 #include <pcl/point_types.h>
55 #include <pcl/point_cloud.h>
57 #if defined(VISP_HAVE_PCL_IO)
58 #include <pcl/io/pcd_io.h>
62 #include <visp3/core/vpImageConvert.h>
63 #include <visp3/core/vpIoTools.h>
64 #include <visp3/core/vpXmlParserCamera.h>
65 #include <visp3/gui/vpDisplayGDI.h>
66 #include <visp3/gui/vpDisplayX.h>
67 #include <visp3/io/vpImageIo.h>
68 #include <visp3/io/vpParseArgv.h>
69 #include <visp3/sensor/vpRealSense.h>
70 #include <visp3/sensor/vpRealSense2.h>
73 #if defined(VISP_HAVE_REALSENSE2)
74 #define USE_REALSENSE2
77 #define GETOPTARGS "so:acdpiCf:bh"
81 void usage(
const char *name,
const char *badparam,
int fps)
83 std::cout <<
"\nSYNOPSIS " << std::endl
94 <<
" [-o <directory>]"
97 std::cout <<
"\nOPTIONS " << std::endl
99 <<
" Flag to enable data saving." << std::endl
101 <<
" -a" << std::endl
102 <<
" Color and depth are aligned." << std::endl
104 <<
" -c" << std::endl
105 <<
" Add color stream to saved data when -s option is enable." << std::endl
107 <<
" -d" << std::endl
108 <<
" Add depth stream to saved data when -s option is enable." << std::endl
110 <<
" -p" << std::endl
111 <<
" Add point cloud stream to saved data when -s option is enabled." << std::endl
112 <<
" By default, the point cloud is saved in Point Cloud Data file format (.PCD extension file)." << std::endl
113 <<
" You can also use -b option to save the point cloud in binary format." << std::endl
115 <<
" -b" << std::endl
116 <<
" Point cloud stream is saved in binary format." << std::endl
118 <<
" -i" << std::endl
119 <<
" Add infrared stream to saved data when -s option is enabled." << std::endl
121 <<
" -C" << std::endl
122 <<
" Trigger one shot data saver after each user click." << std::endl
124 <<
" -f <fps>" << std::endl
125 <<
" Set camera framerate." << std::endl
126 <<
" Default: " << fps << std::endl
128 <<
" -o <directory>" << std::endl
129 <<
" Output directory that will host saved data." << std::endl
131 <<
" --help, -h" << std::endl
132 <<
" Display this helper message." << std::endl
134 std::cout <<
"\nEXAMPLE " << std::endl
135 <<
"- Save aligned color + depth + point cloud in data folder" << std::endl
136 <<
" " << name <<
" -s -a -c -d -p -b -o data" << std::endl
140 std::cout <<
"\nERROR: Bad parameter " << badparam << std::endl;
144 bool getOptions(
int argc,
const char *argv[],
bool &save, std::string &output_directory,
bool &use_aligned_stream,
145 bool &save_color,
bool &save_depth,
bool &save_pointcloud,
bool &save_infrared,
bool &click_to_save,
146 int &stream_fps,
bool &save_pointcloud_binary_format)
149 const char **argv1 = (
const char **)argv;
158 output_directory = optarg;
161 use_aligned_stream =
true;
170 save_pointcloud =
true;
173 save_infrared =
true;
176 click_to_save =
true;
179 stream_fps = atoi(optarg);
182 save_pointcloud_binary_format =
true;
186 usage(argv[0],
nullptr, stream_fps);
191 usage(argv[0], optarg, stream_fps);
197 if ((c == 1) || (c == -1)) {
199 usage(argv[0],
nullptr, stream_fps);
200 std::cerr <<
"ERROR: " << std::endl;
201 std::cerr <<
" Bad argument " << optarg << std::endl
216 : m_cancelled(false), m_cond(), m_queueColor(), m_queueDepth(), m_queuePointCloud(), m_queueInfrared(),
217 m_maxQueueSize(1024 * 8), m_mutex()
222 std::lock_guard<std::mutex> lock(m_mutex);
229 #
if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
230 const pcl::PointCloud<pcl::PointXYZ>::Ptr &pointCloud,
232 const std::vector<vpColVector> &pointCloud,
236 std::lock_guard<std::mutex> lock(m_mutex);
238 m_queueColor.push(colorImg);
239 m_queueDepth.push(depthImg);
240 m_queuePointCloud.push(pointCloud);
241 m_queueInfrared.push(infraredImg);
244 while (m_queueColor.size() > m_maxQueueSize) {
249 while (m_queueDepth.size() > m_maxQueueSize) {
254 while (m_queuePointCloud.size() > m_maxQueueSize) {
255 m_queuePointCloud.pop();
259 while (m_queueInfrared.size() > m_maxQueueSize) {
260 m_queueInfrared.pop();
268 #
if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
269 pcl::PointCloud<pcl::PointXYZ>::Ptr &pointCloud,
271 std::vector<vpColVector> &pointCloud,
275 std::unique_lock<std::mutex> lock(m_mutex);
277 while (m_queueColor.empty() || m_queueDepth.empty() || m_queuePointCloud.empty() || m_queueInfrared.empty()) {
279 throw vpCancelled_t();
285 throw vpCancelled_t();
289 colorImg = m_queueColor.front();
290 depthImg = m_queueDepth.front();
291 pointCloud = m_queuePointCloud.front();
292 infraredImg = m_queueInfrared.front();
296 m_queuePointCloud.pop();
297 m_queueInfrared.pop();
300 void setMaxQueueSize(
const size_t max_queue_size) { m_maxQueueSize = max_queue_size; }
304 std::condition_variable m_cond;
305 std::queue<vpImage<vpRGBa>> m_queueColor;
306 std::queue<vpImage<uint16_t>> m_queueDepth;
307 #if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
308 std::queue<pcl::PointCloud<pcl::PointXYZ>::Ptr> m_queuePointCloud;
310 std::queue<std::vector<vpColVector>> m_queuePointCloud;
312 std::queue<vpImage<unsigned char>> m_queueInfrared;
313 size_t m_maxQueueSize;
317 class vpStorageWorker
320 vpStorageWorker(vpFrameQueue &queue,
const std::string &directory,
bool save_color,
bool save_depth,
bool save_pointcloud,
321 bool save_infrared,
bool save_pointcloud_binary_format,
323 #ifndef VISP_HAVE_PCL
328 #ifndef VISP_HAVE_PCL
332 : m_queue(queue), m_directory(directory), m_cpt(0), m_save_color(save_color), m_save_depth(save_depth),
333 m_save_pointcloud(save_pointcloud), m_save_infrared(save_infrared),
334 m_save_pointcloud_binary_format(save_pointcloud_binary_format)
335 #ifndef VISP_HAVE_PCL
337 m_size_height(height), m_size_width(width)
347 #if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
348 pcl::PointCloud<pcl::PointXYZ>::Ptr pointCloud;
350 std::vector<vpColVector> pointCloud;
354 char buffer[FILENAME_MAX];
356 m_queue.pop(colorImg, depthImg, pointCloud, infraredImg);
358 if (!m_directory.empty()) {
359 std::stringstream ss;
362 ss << m_directory <<
"/color_image_%04d.jpg";
363 snprintf(buffer, FILENAME_MAX, ss.str().c_str(), m_cpt);
365 std::string filename_color = buffer;
371 ss << m_directory <<
"/depth_image_%04d.bin";
372 snprintf(buffer, FILENAME_MAX, ss.str().c_str(), m_cpt);
373 std::string filename_depth = buffer;
375 std::ofstream file_depth(filename_depth.c_str(), std::ios::out | std::ios::binary);
376 if (file_depth.is_open()) {
382 for (
unsigned int i = 0; i < height; i++) {
383 for (
unsigned int j = 0; j < width; j++) {
384 value = depthImg[i][j];
391 if (m_save_pointcloud) {
393 ss << m_directory <<
"/point_cloud_%04d" << (m_save_pointcloud_binary_format ?
".bin" :
".pcd");
394 snprintf(buffer, FILENAME_MAX, ss.str().c_str(), m_cpt);
395 std::string filename_point_cloud = buffer;
397 if (m_save_pointcloud_binary_format) {
398 std::ofstream file_pointcloud(filename_point_cloud.c_str(), std::ios::out | std::ios::binary);
400 if (file_pointcloud.is_open()) {
401 #if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
402 uint32_t width = pointCloud->width;
403 uint32_t height = pointCloud->height;
405 char is_dense = pointCloud->is_dense;
409 file_pointcloud.write((
char *)(&is_dense),
sizeof(is_dense));
411 for (uint32_t i = 0; i < height; i++) {
412 for (uint32_t j = 0; j < width; j++) {
413 pcl::PointXYZ pt = (*pointCloud)(j, i);
421 uint32_t width = m_size_width;
422 uint32_t height = m_size_height;
428 file_pointcloud.write((
char *)(&is_dense),
sizeof(is_dense));
430 for (uint32_t i = 0; i < height; i++) {
431 for (uint32_t j = 0; j < width; j++) {
432 float x = (float)pointCloud[i * width + j][0];
433 float y = (float)pointCloud[i * width + j][1];
434 float z = (float)pointCloud[i * width + j][2];
445 #if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_IO) && defined(VISP_HAVE_PCL_COMMON)
446 pcl::io::savePCDFileBinary(filename_point_cloud, *pointCloud);
451 if (m_save_infrared) {
453 ss << m_directory <<
"/infrared_image_%04d.jpg";
454 snprintf(buffer, FILENAME_MAX, ss.str().c_str(), m_cpt);
456 std::string filename_infrared = buffer;
464 catch (
const vpFrameQueue::vpCancelled_t &) {
465 std::cout <<
"Receive cancel vpFrameQueue." << std::endl;
470 vpFrameQueue &m_queue;
471 std::string m_directory;
475 bool m_save_pointcloud;
476 bool m_save_infrared;
477 bool m_save_pointcloud_binary_format;
478 #ifndef VISP_HAVE_PCL
485 int main(
int argc,
const char *argv[])
489 std::string output_directory_custom =
"";
490 bool use_aligned_stream =
false;
491 bool save_color =
false;
492 bool save_depth =
false;
493 bool save_pointcloud =
false;
494 bool save_infrared =
false;
495 bool click_to_save =
false;
497 bool save_pointcloud_binary_format =
false;
500 if (!getOptions(argc, argv, save, output_directory_custom, use_aligned_stream, save_color, save_depth,
501 save_pointcloud, save_infrared, click_to_save, stream_fps, save_pointcloud_binary_format)) {
505 if (!output_directory_custom.empty())
506 output_directory = output_directory_custom +
"/" + output_directory;
508 #ifndef VISP_HAVE_PCL
509 save_pointcloud_binary_format =
true;
512 std::cout <<
"save: " << save << std::endl;
513 std::cout <<
"output_directory: " << output_directory << std::endl;
514 std::cout <<
"use_aligned_stream: " << use_aligned_stream << std::endl;
515 std::cout <<
"save_color: " << save_color << std::endl;
516 std::cout <<
"save_depth: " << save_depth << std::endl;
517 std::cout <<
"save_pointcloud: " << save_pointcloud << std::endl;
518 std::cout <<
"save_infrared: " << save_infrared << std::endl;
519 std::cout <<
"stream_fps: " << stream_fps << std::endl;
520 std::cout <<
"save_pointcloud_binary_format: " << save_pointcloud_binary_format << std::endl;
521 std::cout <<
"click_to_save: " << click_to_save << std::endl;
523 int width = 640, height = 480;
524 #ifdef USE_REALSENSE2
528 config.enable_stream(RS2_STREAM_COLOR, width, height, RS2_FORMAT_RGBA8, stream_fps);
529 config.enable_stream(RS2_STREAM_DEPTH, width, height, RS2_FORMAT_Z16, stream_fps);
530 config.enable_stream(RS2_STREAM_INFRARED, width, height, RS2_FORMAT_Y8, stream_fps);
531 realsense.
open(config);
556 d1.
init(I_color, 0, 0,
"RealSense color stream");
557 d2.
init(I_depth, I_color.getWidth() + 80, 0,
"RealSense depth stream");
558 d3.
init(I_infrared, I_color.getWidth() + 80, I_color.getHeight() + 70,
"RealSense infrared stream");
561 realsense.
acquire((
unsigned char *)I_color.bitmap, (
unsigned char *)I_depth_raw.bitmap,
nullptr,
nullptr);
580 #ifdef USE_REALSENSE2
583 xml_camera.
save(cam_color, output_directory +
"/camera.xml",
"color_camera", width, height);
585 if (use_aligned_stream) {
586 xml_camera.
save(cam_color, output_directory +
"/camera.xml",
"depth_camera", width, height);
590 xml_camera.
save(cam_depth, output_directory +
"/camera.xml",
"depth_camera", width, height);
594 xml_camera.
save(cam_infrared, output_directory +
"/camera.xml",
"infrared_camera", width, height);
596 if (!use_aligned_stream) {
602 xml_camera.
save(cam_color, output_directory +
"/camera.xml",
"color_camera", width, height);
605 xml_camera.
save(cam_color_rectified, output_directory +
"/camera.xml",
"color_camera_rectified", width, height);
607 if (use_aligned_stream) {
609 xml_camera.
save(cam_depth, output_directory +
"/camera.xml",
"depth_camera", width, height);
612 xml_camera.
save(cam_color, output_directory +
"/camera.xml",
"depth_camera", width, height);
617 xml_camera.
save(cam_depth_aligned_to_rectified_color, output_directory +
"/camera.xml",
618 "depth_camera_aligned_to_rectified_color", width, height);
621 xml_camera.
save(cam_infrared, output_directory +
"/camera.xml",
"infrared_camera", width, height);
623 if (!use_aligned_stream) {
624 depth_M_color = realsense.
getTransformation(rs::stream::color, rs::stream::depth);
627 std::ofstream file(std::string(output_directory +
"/depth_M_color.txt"));
628 depth_M_color.
save(file);
632 vpFrameQueue save_queue;
633 vpStorageWorker storage(std::ref(save_queue), std::cref(output_directory), save_color, save_depth, save_pointcloud,
634 save_infrared, save_pointcloud_binary_format, width, height);
635 std::thread storage_thread(&vpStorageWorker::run, &storage);
637 #ifdef USE_REALSENSE2
638 rs2::align align_to(RS2_STREAM_COLOR);
639 if (use_aligned_stream && save_infrared) {
640 std::cerr <<
"Cannot use aligned streams with infrared acquisition currently."
641 <<
"\nInfrared stream acquisition is disabled!"
648 #if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
649 pcl::PointCloud<pcl::PointXYZ>::Ptr pointCloud(
new pcl::PointCloud<pcl::PointXYZ>);
651 std::vector<vpColVector> pointCloud;
654 if (use_aligned_stream) {
655 #ifdef USE_REALSENSE2
656 #if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
657 realsense.
acquire((
unsigned char *)I_color.bitmap, (
unsigned char *)I_depth_raw.bitmap,
nullptr, pointCloud,
nullptr,
660 realsense.
acquire((
unsigned char *)I_color.bitmap, (
unsigned char *)I_depth_raw.bitmap, &pointCloud,
nullptr,
664 #if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
665 realsense.
acquire((
unsigned char *)I_color.bitmap, (
unsigned char *)I_depth_raw.bitmap,
nullptr, pointCloud,
666 (
unsigned char *)I_infrared.bitmap,
nullptr, rs::stream::rectified_color,
667 rs::stream::depth_aligned_to_rectified_color);
669 realsense.
acquire((
unsigned char *)I_color.bitmap, (
unsigned char *)I_depth_raw.bitmap, &pointCloud,
670 (
unsigned char *)I_infrared.bitmap,
nullptr, rs::stream::rectified_color,
671 rs::stream::depth_aligned_to_rectified_color);
676 #if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
677 realsense.
acquire((
unsigned char *)I_color.bitmap, (
unsigned char *)I_depth_raw.bitmap,
nullptr, pointCloud,
678 (
unsigned char *)I_infrared.bitmap,
nullptr);
680 realsense.
acquire((
unsigned char *)I_color.bitmap, (
unsigned char *)I_depth_raw.bitmap, &pointCloud,
681 (
unsigned char *)I_infrared.bitmap);
691 if (!click_to_save) {
695 std::stringstream ss;
696 ss <<
"Images saved: " << nb_saves;
704 if (save && !click_to_save) {
705 #if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
706 pcl::PointCloud<pcl::PointXYZ>::Ptr pointCloud_copy = pointCloud->makeShared();
707 save_queue.push(I_color, I_depth_raw, pointCloud_copy, I_infrared);
709 save_queue.push(I_color, I_depth_raw, pointCloud, I_infrared);
715 if (!click_to_save) {
724 #if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
725 pcl::PointCloud<pcl::PointXYZ>::Ptr pointCloud_copy = pointCloud->makeShared();
726 save_queue.push(I_color, I_depth_raw, pointCloud_copy, I_infrared);
728 save_queue.push(I_color, I_depth_raw, pointCloud, I_infrared);
744 storage_thread.join();
751 std::cerr <<
"Need libRealSense or libRealSense2 and C++11 and displayX or displayGDI!" << std::endl;
753 #if !defined(VISP_HAVE_PUGIXML)
754 std::cout <<
"pugixml built-in 3rdparty is requested." << std::endl;
Generic class defining intrinsic camera parameters.
Display for windows using GDI (available on any windows 32 platform).
Use the X11 console to display images on unix-like OS. Thus to enable this class X11 should be instal...
void init(vpImage< unsigned char > &I, int win_x=-1, int win_y=-1, const std::string &win_title="") vp_override
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
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 save(std::ofstream &f) const
static void createDepthHistogram(const vpImage< uint16_t > &src_depth, vpImage< vpRGBa > &dest_rgba)
static void write(const vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
unsigned int getWidth() const
unsigned int getHeight() const
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
bool open(const rs2::config &cfg=rs2::config())
void setStreamSettings(const rs::stream &stream, const rs::preset &preset)
vpCameraParameters getCameraParameters(const rs::stream &stream, vpCameraParameters::vpCameraParametersProjType type=vpCameraParameters::perspectiveProjWithDistortion) const
void acquire(std::vector< vpColVector > &pointcloud)
vpHomogeneousMatrix getTransformation(const rs::stream &from, const rs::stream &to) const
XML parser to load and save intrinsic camera parameters.
int save(const vpCameraParameters &cam, const std::string &filename, const std::string &camera_name, unsigned int image_width=0, unsigned int image_height=0, const std::string &additionalInfo="", bool verbose=true)
VISP_EXPORT std::string getDateTime(const std::string &format="%Y/%m/%d %H:%M:%S")