#include <iostream>
#include <visp3/core/vpConfig.h>
#if defined(VISP_HAVE_THREADS) && defined(VISP_HAVE_DISPLAY)
#include <condition_variable>
#include <fstream>
#include <mutex>
#include <queue>
#include <thread>
#include <visp3/core/vpImageConvert.h>
#include <visp3/core/vpIoException.h>
#include <visp3/core/vpIoTools.h>
#include <visp3/gui/vpDisplayFactory.h>
#include <visp3/gui/vpDisplayPCL.h>
#include <visp3/io/vpImageIo.h>
#include <visp3/io/vpVideoReader.h>
#include <visp3/io/vpVideoWriter.h>
#if defined(VISP_HAVE_PCL)
#include <pcl/pcl_config.h>
#if defined(VISP_HAVE_PCL_COMMON)
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#endif
#if defined(VISP_HAVE_PCL_IO)
#include <pcl/io/pcd_io.h>
#endif
#endif
#ifdef ENABLE_VISP_NAMESPACE
#endif
namespace
{
void usage(const char *argv[], int error)
{
std::cout << "\nNAME " << std::endl
<< " - Replay data (color, depth, infrared, point cloud) acquired" << std::endl
<< " with a Realsense device using visp-save-rs-dataset app." << std::endl;
std::cout << "\nDESCRIPTION " << std::endl
<< " This app allows to replay a dataset (color, depth, infrared, point cloud)" << std::endl
<< " acquired with a Realsense device using visp-save-rs-dataset app." << std::endl;
std::cout << "\nSYNOPSIS " << std::endl
<< " [--input-folder,-i <input folder>]"
<< " [--pattern,-e <filename numbering pattern (e.g. %06d)>]"
<< " [--step-by-step,-s]"
<< " [--save-video,-o]"
<< " [--display-colored-depth,-colored-depth]"
<< " [--loop,-l]"
<< " [--fps,-f <framerate>]"
<< " [--help,-h]"
<< std::endl;
std::cout << "\nOPTIONS " << std::endl
<< " --input-folder,-i <input folder>" << std::endl
<< " Input folder that contains the data to read." << std::endl
<< std::endl
<< " --pattern,-e <filename numbering pattern (e.g. %06d)>" << std::endl
<< " Filename numbering pattern used when saving data." << std::endl
<< std::endl
<< " --step-by-step,-s" << std::endl
<< " Flag to display data in step by step mode triggered by a user click." << std::endl
<< std::endl
<< " --save-video,-o" << std::endl
<< " Creates and saves a new image with the color image on the left and" << std::endl
<< " the depth image on the right. Images are saved in a new folder" << std::endl
<< " that corresponds to the date of the day with the following format" << std::endl
<< " YYYY-MM-DD_HH.MM.SS, for example 2025-11-10_17.06.57."
<< std::endl
<< " --display-colored-depth,-colored-depth" << std::endl
<< " Display depth using a cumulative histogram." << std::endl
<< " Warning: this operation is time consuming" << std::endl
<< std::endl
<< " --loop,-l" << std::endl
<< " When this option is enabled, once the sequence has reached the end," << std::endl
<< " playback will restart from the first frame." << std::endl
<< std::endl
<< " --fps,-f <framerate>" << std::endl
<< " Framerate in Hz used to playback the dataset." << std::endl
<< " Default: 30 fps" << std::endl
<< std::endl
<< " --help,-h" << std::endl
<< " Display this helper message." << std::endl
<< std::endl;
std::cout << "EXAMPLE " << std::endl
<< "- Read dataset in data folder" << std::endl
<< " " << argv[0] << " --input-folder data --display-colored-depth" << std::endl
<< std::endl;
if (error) {
std::cout << "Error" << std::endl
<< " "
<< "Unsupported parameter " << argv[error] << std::endl;
}
}
bool getOptions(int argc, const char *argv[], std::string &input_folder, std::string &numbering_pattern,
bool &step_by_step, bool &save_video, bool &display_colored_depth, bool &loop, double &fps)
{
for (int i = 1; i < argc; i++) {
if (((std::string(argv[i]) == "--input-folder") || (std::string(argv[i]) == "-i")) && (i + 1 < argc)) {
input_folder = std::string(argv[++i]);
}
else if (((std::string(argv[i]) == "--pattern") || (std::string(argv[i]) == "-e")) && (i + 1 < argc)) {
numbering_pattern = std::string(argv[++i]);
}
else if ((std::string(argv[i]) == "--step-by-step") || (std::string(argv[i]) == "-s")) {
step_by_step = true;
}
else if ((std::string(argv[i]) == "--save-video") || (std::string(argv[i]) == "-o")) {
save_video = true;
}
else if ((std::string(argv[i]) == "--display-colored-depth") || (std::string(argv[i]) == "-colored-depth")) {
display_colored_depth = true;
}
else if ((std::string(argv[i]) == "--loop") || (std::string(argv[i]) == "-l")) {
loop = true;
}
else if (((std::string(argv[i]) == "--fps") || (std::string(argv[i]) == "-f")) && (i + 1 < argc)) {
fps = std::atof(argv[++i]);
}
else if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h") {
usage(argv, 0);
return false;
}
else {
usage(argv, i);
return false;
}
}
return true;
}
void checkData(unsigned int cpt_frame, const std::string &input_folder, const std::string &input_pattern,
bool &color_found, std::string &color_ext,
bool &depth_found, std::string &depth_ext,
bool &infra_found, std::string &infra_ext,
bool &pcl_found, std::string &pcl_ext,
unsigned int &frame_first, unsigned int &frame_last)
{
{
std::vector<std::string> ext;
ext.push_back(".jpg");
ext.push_back(".png");
for (size_t i = 0; i < ext.size(); ++i) {
color_ext = ext[i];
color_found = true;
break;
}
}
}
{
std::vector<std::string> ext;
ext.push_back(".bin");
#if defined(VISP_HAVE_MINIZ) && defined(VISP_HAVE_WORKING_REGEX)
ext.push_back(".npz");
#endif
for (size_t i = 0; i < ext.size(); ++i) {
depth_ext = ext[i];
depth_found = true;
break;
}
}
}
{
std::vector<std::string> ext;
ext.push_back(".jpg");
ext.push_back(".png");
for (size_t i = 0; i < ext.size(); ++i) {
infra_ext = ext[i];
infra_found = true;
break;
}
}
}
{
std::vector<std::string> ext;
ext.push_back(".bin");
#if defined(VISP_HAVE_MINIZ) && defined(VISP_HAVE_WORKING_REGEX)
ext.push_back(".npz");
#endif
#if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_IO) && defined(VISP_HAVE_PCL_COMMON)
ext.push_back(".pcd");
#endif
for (size_t i = 0; i < ext.size(); ++i) {
pcl_ext = ext[i];
pcl_found = true;
break;
}
}
}
{
if (color_found) {
g.
setFileName(input_folder +
"/color_image_" + input_pattern + color_ext);
}
else if (infra_found) {
g.
setFileName(input_folder +
"/infrared_image_" + input_pattern + infra_ext);
}
else {
std::cout << "WARNING: Unable to find first and last frame index images" << std::endl;
return;
}
}
}
bool readData(int cpt, const std::string &input_folder, const std::string &input_pattern,
bool color_found, std::string color_ext,
bool depth_found, std::string depth_ext,
bool infra_found, std::string infra_ext,
std::string &filename_color, std::string &filename_depth, std::string &filename_infra
#if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
, bool pcl_found, std::string pcl_ext, std::string &filename_pcl
, pcl::PointCloud<pcl::PointXYZ>::Ptr point_cloud
#endif
)
{
#if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
#endif
#if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
#endif
) {
std::cerr << "End of sequence." << std::endl;
return false;
}
if (color_found) {
}
}
if (depth_found) {
if (depth_ext == ".bin") {
std::ifstream file_depth(filename_depth.c_str(), std::ios::in | std::ios::binary);
if (file_depth.is_open()) {
unsigned int height = 0, width = 0;
I_depth_raw.
resize(height, width);
uint16_t depth_value = 0;
for (unsigned int i = 0; i < height; i++) {
for (unsigned int j = 0; j < width; j++) {
I_depth_raw[i][j] = depth_value;
}
}
}
}
else if (depth_ext == ".npz") {
#if defined(VISP_HAVE_MINIZ) && defined(VISP_HAVE_WORKING_REGEX)
}
uint16_t *depth_data_ptr = arr_depth_data.
data<uint16_t>();
assert(arr_depth_data.
shape.size() == 3);
assert(arr_depth_data.
shape[2] == 1);
unsigned int height =
static_cast<unsigned int>(arr_depth_data.
shape[0]);
unsigned int width =
static_cast<unsigned int>(arr_depth_data.
shape[1]);
const bool copyData = true;
#else
#endif
}
}
}
if (infra_found) {
}
}
#if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
if (pcl_found) {
if (pcl_ext == ".npz") {
#if defined(VISP_HAVE_MINIZ) && defined(VISP_HAVE_WORKING_REGEX)
}
float *pcl_data_ptr = arr_pcl_data.
data<
float>();
assert(arr_pcl_data.
shape.size() == 3);
assert(arr_pcl_data.
shape[2] == 3);
uint32_t height = arr_pcl_data.
shape[0], width = arr_pcl_data.
shape[1];
const char is_dense = 1;
point_cloud->width = width;
point_cloud->height = height;
point_cloud->is_dense = (is_dense != 0);
point_cloud->resize(static_cast<size_t>(width * height));
for (uint32_t i = 0; i < height; i++) {
for (uint32_t j = 0; j < width; j++) {
point_cloud->points[static_cast<size_t>(i * width + j)].x = pcl_data_ptr[static_cast<size_t>(i * width + j)*3 + 0];
point_cloud->points[static_cast<size_t>(i * width + j)].y = pcl_data_ptr[static_cast<size_t>(i * width + j)*3 + 1];
point_cloud->points[static_cast<size_t>(i * width + j)].z = pcl_data_ptr[static_cast<size_t>(i * width + j)*3 + 2];
}
}
#endif
}
else if (pcl_ext == ".pcd") {
#if defined(VISP_HAVE_PCL_IO)
if (pcl::io::loadPCDFile<pcl::PointXYZ>(filename_pcl, *point_cloud) == -1) {
std::cerr << "Cannot read PCD: " << filename_pcl << std::endl;
}
#else
#endif
}
else if (pcl_ext == ".bin") {
std::ifstream file_pointcloud(filename_pcl.c_str(), std::ios::in | std::ios::binary);
if (!file_pointcloud.is_open()) {
std::cerr << "Cannot read pointcloud file: " << filename_pcl << std::endl;
}
uint32_t height = 0, width = 0;
const char is_dense = 1;
file_pointcloud.read((char *)(&is_dense), sizeof(is_dense));
point_cloud->width = width;
point_cloud->height = height;
point_cloud->is_dense = (is_dense != 0);
point_cloud->resize(static_cast<size_t>(width * height));
float x = 0.0f, y = 0.0f, z = 0.0f;
for (uint32_t i = 0; i < height; i++) {
for (uint32_t j = 0; j < width; j++) {
point_cloud->points[static_cast<size_t>(i * width + j)].x = x;
point_cloud->points[static_cast<size_t>(i * width + j)].y = y;
point_cloud->points[static_cast<size_t>(i * width + j)].z = z;
}
}
}
}
}
#endif
return true;
}
}
int main(int argc, const char *argv[])
{
std::string opt_input_folder = "";
std::string opt_input_pattern = "%04d";
bool opt_step_by_step = false;
bool opt_save_video = false;
bool opt_display_colored_depth = false;
bool opt_loop = false;
double opt_fps = 30.0;
if (!getOptions(argc, argv, opt_input_folder, opt_input_pattern, opt_step_by_step,
opt_save_video, opt_display_colored_depth, opt_loop, opt_fps)) {
return EXIT_FAILURE;
}
#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
#else
#endif
bool init_display = false;
#if defined(VISP_HAVE_PCL)
std::mutex mutex;
pcl::PointCloud<pcl::PointXYZ>::Ptr pointcloud(new pcl::PointCloud<pcl::PointXYZ>());
#if defined(VISP_HAVE_PCL_VISUALIZATION)
#endif
#endif
if (opt_save_video) {
writer.
setFileName(output_folder +
"/" + opt_input_pattern +
".png");
}
unsigned int cpt_frame = 0;
bool color_found = false;
bool depth_found = false;
bool infra_found = false;
bool pcl_found = false;
std::string color_ext, depth_ext, infra_ext, pcl_ext;
unsigned int frame_first = 0, frame_last = 0;
checkData(cpt_frame, opt_input_folder, opt_input_pattern,
color_found, color_ext,
depth_found, depth_ext,
infra_found, infra_ext,
pcl_found, pcl_ext,
frame_first, frame_last);
std::cout << "Dataset in " << opt_input_folder << " contains" << std::endl;
std::cout << " - Color images : " << (color_found ? "yes" : "no") << std::endl;
std::cout << " - Depth images : " << (depth_found ? "yes" : "no") << std::endl;
std::cout << " - Infrared images : " << (infra_found ? "yes" : "no") << std::endl;
std::cout << " - Point cloud : " << (pcl_found ? "yes" : "no") << std::endl;
std::cout << "Dataset" << std::endl;
std::cout << " - First frame index: " << frame_first << std::endl;
std::cout << " - Last frame index: " << frame_last << std::endl;
std::cout << "Options summary" << std::endl;
std::cout << " Data visualization " << std::endl;
std::cout << " Colored depth : " << (opt_display_colored_depth ? "yes" : "no") << std::endl;
std::cout << " Frame per seconds: " << opt_fps << std::endl;
std::cout << " Save dataset : " << (opt_save_video ? "yes" : "no") << std::endl;
if (opt_save_video) {
std::cout << " Output folder : " << output_folder << std::endl;
}
if (!color_found && !depth_found && !infra_found && !pcl_found) {
std::cout << "\nError: No data found in " << opt_input_folder << " folder" << std::endl;
return EXIT_FAILURE;
}
bool quit = false;
std::string filename_color, filename_depth, filename_infra, filename_pcl;
while (!quit) {
#if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_COMMON)
{
std::lock_guard<std::mutex> lock(mutex);
quit = !readData(cpt_frame, opt_input_folder, opt_input_pattern,
color_found, color_ext,
depth_found, depth_ext,
infra_found, infra_ext,
I_color, I_depth_raw, I_infra,
filename_color, filename_depth, filename_infra,
pcl_found, pcl_ext, filename_pcl, pointcloud);
}
#else
quit = !readData(cpt_frame, opt_input_folder, opt_input_pattern,
color_found, color_ext,
depth_found, depth_ext,
infra_found, infra_ext,
I_color, I_depth_raw, I_infra,
filename_color, filename_depth, filename_infra);
#endif
if (opt_display_colored_depth) {
}
else {
}
if (!init_display) {
init_display = true;
if (color_found) {
d1->init(I_color, 0, 0, "Color image");
}
if (depth_found) {
if (opt_display_colored_depth) {
d2->init(I_depth_color, I_color.
getWidth() + 80, 0,
"Depth image");
}
else {
d2->init(I_depth_gray, I_color.
getWidth() + 80, 0,
"Depth image");
}
}
if (infra_found) {
d3->init(I_infra, I_color.
getWidth() + 80, I_color.
getHeight() + 70,
"Infrared image");
}
#if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_VISUALIZATION)
if (pcl_found) {
if (pointcloud->size() > 0) {
}
}
#endif
}
if (opt_step_by_step) {
}
else {
}
if (opt_display_colored_depth) {
}
else {
}
if (opt_display_colored_depth) {
}
else {
}
if (opt_save_video) {
}
if (!opt_display_colored_depth) {
}
}
switch (button) {
if (!quit)
quit = !opt_step_by_step;
break;
opt_step_by_step = !opt_step_by_step;
break;
default:
break;
}
}
cpt_frame++;
if (opt_loop) {
if (cpt_frame == frame_last) {
std::cout << "End of sequence reached" << std::endl;
cpt_frame = frame_first;
}
}
}
#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
if (d1 != nullptr) {
delete d1;
}
if (d2 != nullptr) {
delete d2;
}
if (d3 != nullptr) {
delete d3;
}
#endif
return EXIT_SUCCESS;
}
#else
int main()
{
std::cerr << "Enable C++11 or higher (cmake -DUSE_CXX_STANDARD=11) and install X11 or GDI!" << std::endl;
return EXIT_SUCCESS;
}
#endif
void setPosition(int posx, int posy)
void setWindowName(const std::string &window_name)
void startThread(const bool &colorThread=false)
Class that defines generic functionalities for display.
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
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)
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 void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition of the vpImage class member functions.
unsigned int getWidth() const
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
void insert(const vpImage< Type > &src, const vpImagePoint &topLeft)
unsigned int getSize() const
unsigned int getHeight() const
Error that can be emitted by the vpIoTools class and its derivatives.
Class that enables to manipulate easily a video file or a sequence of images. As it inherits from the...
void open(vpImage< vpRGBa > &I) VP_OVERRIDE
void setFileName(const std::string &filename)
long getFirstFrameIndex()
Class that enables to write easily a video file or a sequence of images.
void saveFrame(vpImage< vpRGBa > &I)
void setFileName(const std::string &filename)
void open(vpImage< vpRGBa > &I)
VISP_EXPORT npz_t npz_load(const std::string &fname)
std::map< std::string, NpyArray > npz_t
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()
VISP_EXPORT int wait(double t0, double t)
VISP_EXPORT std::string getDateTime(const std::string &format="%Y/%m/%d %H:%M:%S")
std::shared_ptr< std::vector< char > > data_holder
std::vector< size_t > shape