ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
RealSenseSensor.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #include <FileSystem.h>
11 #include <Logging.h>
12 #include <json/json.h>
13 
14 #include <string>
15 #include <thread>
16 #include <vector>
17 
19 
20 namespace cloudViewer {
21 namespace t {
22 namespace io {
23 
25  auto all_device_info = EnumerateDevices();
26  if (all_device_info.empty()) {
27  utility::LogWarning("No RealSense devices detected.");
28  return false;
29  } else {
30  size_t sensor_index = 0;
31  for (auto& dev_info : all_device_info) {
32  utility::LogInfo("[{}] {}: {}", sensor_index++, dev_info.name,
33  dev_info.serial);
34  for (auto& config : dev_info.valid_configs)
35  utility::LogInfo("\t{}: [{}]", config.first,
36  fmt::join(config.second, " | "));
37  }
39  "CloudViewer only supports synchronized color and depth "
40  "capture "
41  "(color_fps = depth_fps).");
42  return true;
43  }
44 }
45 
46 std::vector<RealSenseValidConfigs> RealSenseSensor::EnumerateDevices() {
47  rs2::context ctx;
48  std::vector<RealSenseValidConfigs> all_device_info;
49  for (auto&& dev : ctx.query_devices()) {
50  RealSenseValidConfigs cfg{dev.get_info(RS2_CAMERA_INFO_SERIAL_NUMBER),
51  dev.get_info(RS2_CAMERA_INFO_NAME),
52  {{"color_format", {}},
53  {"color_resolution", {}},
54  {"color_fps", {}},
55  {"depth_format", {}},
56  {"depth_resolution", {}},
57  {"depth_fps", {}},
58  {"visual_preset", {}}}};
59  for (auto&& sensor : dev.query_sensors()) {
60  for (auto&& stream : sensor.get_stream_profiles()) {
61  if (stream.stream_type() == RS2_STREAM_COLOR) {
62  cfg.valid_configs["color_format"].insert(
63  enum_to_string(stream.format()));
64  cfg.valid_configs["color_resolution"].insert(fmt::format(
65  "{},{}",
66  stream.as<rs2::video_stream_profile>().width(),
67  stream.as<rs2::video_stream_profile>().height()));
68  cfg.valid_configs["color_fps"].insert(
69  std::to_string(stream.fps()));
70  } else if (stream.stream_type() == RS2_STREAM_DEPTH) {
71  cfg.valid_configs["depth_format"].insert(
72  enum_to_string(stream.format()));
73  cfg.valid_configs["depth_resolution"].insert(fmt::format(
74  "{},{}",
75  stream.as<rs2::video_stream_profile>().width(),
76  stream.as<rs2::video_stream_profile>().height()));
77  cfg.valid_configs["depth_fps"].insert(
78  std::to_string(stream.fps()));
79  }
80  }
81  if (sensor.supports(RS2_OPTION_VISUAL_PRESET)) {
82  std::string product_line =
83  dev.get_info(RS2_CAMERA_INFO_PRODUCT_LINE);
84  if (product_line == "L500") {
85  for (int k = 0;
86  k < static_cast<int>(RS2_L500_VISUAL_PRESET_COUNT);
87  ++k)
88  cfg.valid_configs["visual_preset"].insert(
89  enum_to_string(
90  static_cast<rs2_l500_visual_preset>(
91  k)));
92  } else if (product_line == "RS400") {
93  for (int k = 0;
94  k < static_cast<int>(RS2_RS400_VISUAL_PRESET_COUNT);
95  ++k)
96  cfg.valid_configs["visual_preset"].insert(
97  enum_to_string(
98  static_cast<rs2_rs400_visual_preset>(
99  k)));
100  } else if (product_line == "SR300") {
101  for (int k = 0;
102  k < static_cast<int>(RS2_SR300_VISUAL_PRESET_COUNT);
103  ++k)
104  cfg.valid_configs["visual_preset"].insert(
105  enum_to_string(
106  static_cast<rs2_sr300_visual_preset>(
107  k)));
108  }
109  }
110  }
111  all_device_info.emplace_back(cfg);
112  }
113  return all_device_info;
114 }
115 
117  : pipe_{new rs2::pipeline},
118  align_to_color_{new rs2::align(rs2_stream::RS2_STREAM_COLOR)},
119  rs_config_{new rs2::config} {
121 }
122 
124 
126  size_t sensor_index,
127  const std::string& filename) try {
128  *rs_config_ = sensor_config.ConvertToNativeConfig();
129  if (sensor_config.config_.find("serial") == sensor_config.config_.cend()) {
130  // serial number not specified, use sensor_index
131  rs2::context ctx;
132  auto device_list = ctx.query_devices();
133  if (sensor_index >= device_list.size()) {
135  "No device for sensor_index {}. Only {} devices detected.",
136  sensor_index, device_list.size());
137  } else {
138  rs_config_->enable_device(
139  device_list[(uint32_t)sensor_index].get_info(
140  RS2_CAMERA_INFO_SERIAL_NUMBER));
141  }
142  }
143  if (!filename.empty()) {
144  if (utility::filesystem::FileExists(filename_)) {
145  enable_recording_ = false;
146  utility::LogError("Will not overwrite existing file {}.", filename);
147  }
148  const std::string parent_dir =
150  if (!utility::filesystem::DirectoryExists(parent_dir)) {
152  }
153  filename_ = filename;
154  enable_recording_ = true;
155  } else {
156  filename_.clear();
157  enable_recording_ = false;
158  }
159  auto profile = rs_config_->resolve(*pipe_);
160  auto dev = profile.get_device().first<rs2::depth_sensor>();
161  auto it = sensor_config.config_.find("visual_preset");
162  // unknown option will map to default for each product line
163  std::string option_str{"RS2_VISUAL_PRESET_DEFAULT"};
164  if (it != sensor_config.config_.cend() && !it->second.empty())
165  option_str = it->second;
166  std::string product_line = dev.get_info(RS2_CAMERA_INFO_PRODUCT_LINE);
167  if (product_line == "L500") {
168  rs2_l500_visual_preset option;
169  enum_from_string(option_str, option);
170  if (option != RS2_L500_VISUAL_PRESET_DEFAULT)
171  dev.set_option(RS2_OPTION_VISUAL_PRESET,
172  static_cast<float>(option));
173  } else if (product_line == "RS400") {
174  rs2_rs400_visual_preset option;
175  enum_from_string(option_str, option);
176  if (option != RS2_RS400_VISUAL_PRESET_DEFAULT)
177  dev.set_option(RS2_OPTION_VISUAL_PRESET,
178  static_cast<float>(option));
179  } else if (product_line == "SR300") {
180  rs2_sr300_visual_preset option;
181  enum_from_string(option_str, option);
182  if (option != RS2_SR300_VISUAL_PRESET_DEFAULT)
183  dev.set_option(RS2_OPTION_VISUAL_PRESET,
184  static_cast<float>(option));
185  }
186  metadata_.ConvertFromJsonValue(
189  return true;
190 } catch (const rs2::error& e) {
192  "Invalid RealSense camera configuration, or camera not connected:"
193  "\n{}: {}",
194  rs2_exception_type_to_string(e.get_type()), e.what());
195  return false;
196 }
197 
198 bool RealSenseSensor::StartCapture(bool start_record) {
199  if (is_capturing_) {
200  utility::LogWarning("Capture already in progress.");
201  return true;
202  }
203  try {
204  is_recording_ = enable_recording_ && start_record;
205  if (is_recording_) rs_config_->enable_record_to_file(filename_);
206  const auto profile = pipe_->start(*rs_config_);
207  // This step is repeated here since the user may bypass InitSensor()
208  metadata_.ConvertFromJsonValue(
211 
212  is_capturing_ = true;
214  "Capture started with RealSense camera {}",
215  profile.get_device().get_info(RS2_CAMERA_INFO_SERIAL_NUMBER));
216  if (enable_recording_) {
217  utility::LogInfo("Recording {}to bag file {}",
218  start_record ? "" : "[Paused] ", filename_);
219  }
220  return true;
221  } catch (const rs2::error& e) {
222  utility::LogError("StartCapture() failed: {}: {}",
223  rs2_exception_type_to_string(e.get_type()), e.what());
224  return false;
225  }
226 }
227 
229  if (!enable_recording_ || !is_recording_) return;
230  pipe_->get_active_profile().get_device().as<rs2::recorder>().pause();
231  is_recording_ = false;
232  utility::LogDebug("Recording paused.");
233 }
234 
236  if (!enable_recording_ || is_recording_) return;
237  try {
238  if (auto dev = pipe_->get_active_profile()
239  .get_device()
240  .as<rs2::recorder>()) {
241  dev.resume();
242  utility::LogDebug("Recording resumed.");
243  } else {
244  rs_config_->enable_record_to_file(filename_);
245  pipe_.reset(new rs2::pipeline);
246  pipe_->start(*rs_config_);
247  utility::LogDebug("Recording started.");
248  }
249  is_recording_ = true;
250  } catch (const rs2::error& e) {
251  utility::LogError("ResumeRecord() failed: {}: {}",
252  rs2_exception_type_to_string(e.get_type()), e.what());
253  }
254 }
255 
257  bool align_depth_to_color) {
258  if (!is_capturing_) {
259  utility::LogError("Please StartCapture() first.");
260  return geometry::RGBDImage();
261  }
262  try {
263  rs2::frameset frames;
264  if (!((wait && pipe_->try_wait_for_frames(&frames)) ||
265  (!wait && pipe_->poll_for_frames(&frames))))
266  return geometry::RGBDImage();
267  if (align_depth_to_color) frames = align_to_color_->process(frames);
268  timestamp_ = uint64_t(frames.get_timestamp() * MILLISEC_TO_MICROSEC);
269  // Copy frame data to Tensors
270  const auto& color_frame = frames.get_color_frame();
271  current_frame_.color_ = core::Tensor(
272  static_cast<const uint8_t*>(color_frame.get_data()),
273  {color_frame.get_height(), color_frame.get_width(),
274  metadata_.color_channels_},
275  metadata_.color_dt_);
276  const auto& depth_frame = frames.get_depth_frame();
277  current_frame_.depth_ = core::Tensor(
278  static_cast<const uint16_t*>(depth_frame.get_data()),
279  {depth_frame.get_height(), depth_frame.get_width()},
280  metadata_.depth_dt_);
281  return current_frame_;
282  } catch (const rs2::error& e) {
283  utility::LogError("CaptureFrame() failed: {}: {}",
284  rs2_exception_type_to_string(e.get_type()), e.what());
285  return geometry::RGBDImage();
286  }
287 }
288 
290  if (is_capturing_) {
291  pipe_->stop();
292  is_recording_ = false;
293  is_capturing_ = false;
294  utility::LogInfo("Capture stopped.");
295  }
296 }
297 
298 } // namespace io
299 } // namespace t
300 } // namespace cloudViewer
std::string filename
filament::Texture::InternalFormat format
CloudViewerScene::LightingProfile profile
RGBDImage A pair of color and depth images.
Definition: RGBDImage.h:21
Image depth_
The depth image.
Definition: RGBDImage.h:106
Image color_
The color image.
Definition: RGBDImage.h:104
core::Dtype depth_dt_
Pixel Dtype for depth data.
bool ConvertFromJsonValue(const Json::Value &value) override
core::Dtype color_dt_
Pixel Dtype for color data.
rs2::config ConvertToNativeConfig() const
Convert to RealSense config.
static void GetPixelDtypes(const rs2::pipeline_profile &profile, RGBDVideoMetadata &metadata)
std::unordered_map< std::string, std::string > config_
static Json::Value GetMetadataJson(const rs2::pipeline_profile &profile)
Get metadata for a streaming RealSense camera or bag file.
virtual bool InitSensor(const RealSenseSensorConfig &sensor_config=RealSenseSensorConfig{}, size_t sensor_index=0, const std::string &filename="")
virtual void StopCapture() override
Stop capturing frames.
static std::vector< RealSenseValidConfigs > EnumerateDevices()
RealSenseSensor()
Default constructor. Initialize with default settings.
virtual void ResumeRecord() override
virtual geometry::RGBDImage CaptureFrame(bool wait=true, bool align_depth_to_color=true) override
virtual bool StartCapture(bool start_record=false) override
#define LogWarning(...)
Definition: Logging.h:72
#define LogInfo(...)
Definition: Logging.h:81
#define LogError(...)
Definition: Logging.h:60
#define LogDebug(...)
Definition: Logging.h:90
ImGuiContext * context
Definition: Window.cpp:76
static void error(char *msg)
Definition: lsd.c:159
bool MakeDirectoryHierarchy(const std::string &directory)
Definition: FileSystem.cpp:499
std::string GetFileParentDirectory(const std::string &filename)
Definition: FileSystem.cpp:314
bool DirectoryExists(const std::string &directory)
Definition: FileSystem.cpp:473
bool FileExists(const std::string &filename)
Definition: FileSystem.cpp:524
Generic file read and write utility for python interface.
std::string to_string(const T &n)
Definition: Common.h:20
std::unordered_map< std::string, std::set< std::string > > valid_configs