Visual Servoing Platform  version 3.6.1 under development (2024-05-02)
testRealSense2_D435_opencv.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See https://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Test Intel RealSense acquisition with librealsense2 (OpenCV demo).
33  *
34 *****************************************************************************/
40 #include <iostream>
41 
42 #include <visp3/core/vpConfig.h>
43 
44 #if defined(VISP_HAVE_REALSENSE2) && (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11) && \
45  (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI)) && (VISP_HAVE_OPENCV_VERSION >= 0x030000)
46 
47 #include <visp3/core/vpImage.h>
48 #include <visp3/core/vpImageConvert.h>
49 #include <visp3/core/vpMeterPixelConversion.h>
50 #include <visp3/sensor/vpRealSense2.h>
51 
52 #include <opencv2/core.hpp>
53 #include <opencv2/highgui.hpp>
54 
55 namespace
56 {
57 struct float3
58 {
59  float x, y, z;
60  float3() : x(0), y(0), z(0) { }
61  float3(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { }
62 };
63 
64 void getPointcloud(const rs2::depth_frame &depth_frame, std::vector<float3> &pointcloud)
65 {
66  auto vf = depth_frame.as<rs2::video_frame>();
67  const int width = vf.get_width();
68  const int height = vf.get_height();
69  pointcloud.resize((size_t)(width * height));
70 
71  rs2::pointcloud pc;
72  rs2::points points = pc.calculate(depth_frame);
73  auto vertices = points.get_vertices();
74  for (size_t i = 0; i < points.size(); i++) {
75  float3 pcl;
76  if (vertices[i].z > std::numeric_limits<float>::epsilon()) {
77  pcl.x = vertices[i].x;
78  pcl.y = vertices[i].y;
79  pcl.z = vertices[i].z;
80  }
81 
82  pointcloud[i] = pcl;
83  }
84 }
85 
86 void createDepthHist(std::vector<uint32_t> &histogram, const std::vector<float3> &pointcloud, float depth_scale)
87 {
88  std::fill(histogram.begin(), histogram.end(), 0);
89 
90  for (size_t i = 0; i < pointcloud.size(); i++) {
91  const float3 &pt = pointcloud[i];
92  ++histogram[static_cast<uint32_t>(pt.z * depth_scale)];
93  }
94 
95  for (int i = 2; i < 0x10000; i++)
96  histogram[i] += histogram[i - 1]; // Build a cumulative histogram for
97  // the indices in [1,0xFFFF]
98 }
99 
100 unsigned char getDepthColor(const std::vector<uint32_t> &histogram, float z, float depth_scale)
101 {
102  // 0-255 based on histogram location
103  return static_cast<unsigned char>(histogram[static_cast<uint32_t>(z * depth_scale)] * 255 / histogram[0xFFFF]);
104 }
105 
106 void getNativeFrame(const rs2::frame &frame, unsigned char *const data)
107 {
108  auto vf = frame.as<rs2::video_frame>();
109  int size = vf.get_width() * vf.get_height();
110 
111  switch (frame.get_profile().format()) {
112  case RS2_FORMAT_RGB8:
113  case RS2_FORMAT_BGR8:
114  memcpy(data, frame.get_data(), size * 3);
115  break;
116 
117  case RS2_FORMAT_RGBA8:
118  case RS2_FORMAT_BGRA8:
119  memcpy(data, frame.get_data(), size * 4);
120  break;
121 
122  case RS2_FORMAT_Y16:
123  case RS2_FORMAT_Z16:
124  memcpy(data, frame.get_data(), size * 2);
125  break;
126 
127  case RS2_FORMAT_Y8:
128  memcpy(data, frame.get_data(), size);
129  break;
130 
131  default:
132  break;
133  }
134 }
135 
136 void frame_to_mat(const rs2::frame &f, cv::Mat &img)
137 {
138  auto vf = f.as<rs2::video_frame>();
139  const int w = vf.get_width();
140  const int h = vf.get_height();
141  const int size = w * h;
142 
143  if (f.get_profile().format() == RS2_FORMAT_BGR8) {
144  memcpy(static_cast<void *>(img.ptr<cv::Vec3b>()), f.get_data(), size * 3);
145  }
146  else if (f.get_profile().format() == RS2_FORMAT_RGB8) {
147  cv::Mat tmp(h, w, CV_8UC3, const_cast<void *>(f.get_data()), cv::Mat::AUTO_STEP);
148  cv::cvtColor(tmp, img, cv::COLOR_RGB2BGR);
149  }
150  else if (f.get_profile().format() == RS2_FORMAT_Y8) {
151  memcpy(img.ptr<uchar>(), f.get_data(), size);
152  }
153 }
154 } // namespace
155 
156 int main()
157 {
158  const int width = 640, height = 480, fps = 60;
159  vpRealSense2 rs;
160  rs2::config config;
161  config.enable_stream(RS2_STREAM_COLOR, width, height, RS2_FORMAT_BGR8, fps);
162  config.enable_stream(RS2_STREAM_DEPTH, width, height, RS2_FORMAT_Z16, fps);
163  config.enable_stream(RS2_STREAM_INFRARED, 1, width, height, RS2_FORMAT_Y8, fps);
164  config.enable_stream(RS2_STREAM_INFRARED, 2, width, height, RS2_FORMAT_Y8, fps);
165  rs.open(config);
166 
167  rs2::pipeline_profile &profile = rs.getPipelineProfile();
168  rs2::pipeline &pipe = rs.getPipeline();
169  float depth_scale = 1 / rs.getDepthScale();
170 
171  // initialize the image sizes
172  // width and height can also be used instead
173  auto color_profile = profile.get_stream(RS2_STREAM_COLOR).as<rs2::video_stream_profile>();
174  cv::Mat mat_color(color_profile.height(), color_profile.width(), CV_8UC3);
175 
176  auto depth_profile = profile.get_stream(RS2_STREAM_DEPTH).as<rs2::video_stream_profile>();
177  cv::Mat mat_depth(depth_profile.height(), depth_profile.width(), CV_8UC3);
178  rs2::colorizer color_map;
179 
180  auto infrared_profile = profile.get_stream(RS2_STREAM_INFRARED).as<rs2::video_stream_profile>();
181  cv::Mat mat_infrared1(infrared_profile.height(), infrared_profile.width(), CV_8UC1);
182  cv::Mat mat_infrared2(infrared_profile.height(), infrared_profile.width(), CV_8UC1);
183 
184  std::vector<float3> pointcloud;
185  cv::Mat mat_pointcloud(depth_profile.height(), depth_profile.width(), CV_8UC1);
186  std::vector<uint32_t> histogram(0x10000);
187 
188  vpCameraParameters cam_projection = rs.getCameraParameters(RS2_STREAM_DEPTH);
189 
190  std::vector<double> time_vector;
191  vpChrono chrono;
192  while (true) {
193  chrono.start();
194 
195  auto data = pipe.wait_for_frames();
196  frame_to_mat(data.get_color_frame(), mat_color);
197 #if (RS2_API_VERSION >= ((2 * 10000) + (16 * 100) + 0))
198  frame_to_mat(data.get_depth_frame().apply_filter(color_map), mat_depth);
199 #else
200  frame_to_mat(color_map(data.get_depth_frame()), mat_depth);
201 #endif
202 
203  cv::imshow("OpenCV color", mat_color);
204  cv::imshow("OpenCV depth", mat_depth);
205 
206 #if (RS2_API_VERSION >= ((2 * 10000) + (10 * 100) + 0))
207  // rs2::frameset::get_infrared_frame() introduced in librealsense 2.10.0
208  frame_to_mat(data.get_infrared_frame(1), mat_infrared1);
209  frame_to_mat(data.get_infrared_frame(2), mat_infrared2);
210 
211  cv::imshow("OpenCV infrared left", mat_infrared1);
212  cv::imshow("OpenCV infrared right", mat_infrared2);
213 #endif
214 
215  getPointcloud(data.get_depth_frame(), pointcloud);
216  createDepthHist(histogram, pointcloud, depth_scale);
217 
218  mat_pointcloud = 0;
219  for (size_t i = 0; i < pointcloud.size(); i++) {
220  const float3 &pt = pointcloud[i];
221  float Z = pt.z;
222  if (Z > 1e-2) {
223  double x = pt.x / Z;
224  double y = pt.y / Z;
225 
226  vpImagePoint imPt;
227  vpMeterPixelConversion::convertPoint(cam_projection, x, y, imPt);
228  int u = std::min<int>(static_cast<int>(width - 1), static_cast<int>(std::max<double>(0.0, imPt.get_u())));
229  int v = std::min<int>(static_cast<int>(height - 1), static_cast<int>(std::max<double>(0.0, imPt.get_v())));
230  unsigned char depth_viz = getDepthColor(histogram, Z, depth_scale);
231  mat_pointcloud.at<uchar>(v, u) = depth_viz;
232  }
233  }
234  cv::imshow("OpenCV projected pointcloud", mat_pointcloud);
235 
236  chrono.stop();
237  time_vector.push_back(chrono.getDurationMs());
238  if (cv::waitKey(5) == 27 || cv::waitKey(5) == 113) { // Esc or q
239  break;
240  }
241  }
242 
243  std::cout << "Acquisition - Mean time: " << vpMath::getMean(time_vector)
244  << " ms ; Median time: " << vpMath::getMedian(time_vector) << " ms" << std::endl;
245 
246  return EXIT_SUCCESS;
247 }
248 #else
249 int main()
250 {
251 #if !defined(VISP_HAVE_REALSENSE2)
252  std::cout << "Install librealsense2 to make this test work." << std::endl;
253 #endif
254 #if !(VISP_HAVE_OPENCV_VERSION >= 0x030000)
255  std::cout << "Install OpenCV version >= 3 to make this test work." << std::endl;
256 #endif
257  return EXIT_SUCCESS;
258 }
259 #endif
Generic class defining intrinsic camera parameters.
void start(bool reset=true)
Definition: vpTime.cpp:399
void stop()
Definition: vpTime.cpp:414
double getDurationMs()
Definition: vpTime.cpp:388
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
double get_u() const
Definition: vpImagePoint.h:136
double get_v() const
Definition: vpImagePoint.h:147
static double getMedian(const std::vector< double > &v)
Definition: vpMath.cpp:323
static double getMean(const std::vector< double > &v)
Definition: vpMath.cpp:303
static void convertPoint(const vpCameraParameters &cam, const double &x, const double &y, double &u, double &v)
vpCameraParameters getCameraParameters(const rs2_stream &stream, vpCameraParameters::vpCameraParametersProjType type=vpCameraParameters::perspectiveProjWithDistortion, int index=-1) const
rs2::pipeline_profile & getPipelineProfile()
Get a reference to rs2::pipeline_profile.
Definition: vpRealSense2.h:362
bool open(const rs2::config &cfg=rs2::config())
float getDepthScale()
rs2::pipeline & getPipeline()
Get a reference to rs2::pipeline.
Definition: vpRealSense2.h:359