ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
sensor.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 
8 #include <RGBDImage.h>
9 
10 #include <memory>
11 
12 #include "t/io/sensor/RGBDSensor.h"
14 #ifdef BUILD_LIBREALSENSE
18 #endif
19 #include "pybind/docstring.h"
20 #include "pybind/t/io/io.h"
21 
22 namespace cloudViewer {
23 namespace t {
24 namespace io {
25 
26 void pybind_sensor(py::module &m) {
27  static const std::unordered_map<std::string, std::string>
29  {"timestamp", "Timestamp in the video (usec)."},
30  {"filename", "Path to the RGBD video file."},
31  {"frame_path",
32  "Frames will be stored in stream subfolders 'color' and "
33  "'depth' here. The intrinsic camera calibration for the "
34  "color stream will be saved in 'intrinsic.json'"},
35  {"start_time_us",
36  "Start saving frames from this time (us)"},
37  {"end_time_us",
38  "(default video length) Save frames till this time (us)"},
39  {"buffer_size",
40  "Size of internal frame buffer, increase this if you "
41  "experience frame drops."}};
42 
43  py::native_enum<SensorType>(m, "SensorType", "enum.Enum", "Sensor type.")
44  .value("AZURE_KINECT", SensorType::AZURE_KINECT)
45  .value("REAL_SENSE", SensorType::REAL_SENSE)
46  .finalize();
47 
48  // Class RGBD video metadata
49  py::class_<RGBDVideoMetadata> rgbd_video_metadata(m, "RGBDVideoMetadata",
50  "RGBD Video metadata.");
51  rgbd_video_metadata.def(py::init<>())
52  .def_readwrite("intrinsics", &RGBDVideoMetadata::intrinsics_,
53  "Shared intrinsics between RGB & depth")
54  .def_readwrite("device_name", &RGBDVideoMetadata::device_name_,
55  "Capture device name")
56  .def_readwrite("serial_number", &RGBDVideoMetadata::serial_number_,
57  "Capture device serial number")
58  .def_readwrite("stream_length_usec",
60  "Length of the video (usec). 0 for live capture.")
61  .def_readwrite("width", &RGBDVideoMetadata::width_,
62  "Width of the video")
63  .def_readwrite("height", &RGBDVideoMetadata::height_,
64  "Height of the video")
65  .def_readwrite("fps", &RGBDVideoMetadata::fps_,
66  "Video frame rate (common for both color and depth)")
67  .def_readwrite("color_format", &RGBDVideoMetadata::color_format_,
68  "Pixel format for color data")
69  .def_readwrite("color_dt", &RGBDVideoMetadata::color_dt_,
70  "Pixel Dtype for color data.")
71  .def_readwrite("depth_format", &RGBDVideoMetadata::depth_format_,
72  "Pixel format for depth data")
73  .def_readwrite("depth_dt", &RGBDVideoMetadata::depth_dt_,
74  "Pixel Dtype for depth data.")
75  .def_readwrite("depth_scale", &RGBDVideoMetadata::depth_scale_,
76  "Number of depth units per meter (depth in m = "
77  "depth_pixel_value/depth_scale).")
78  .def_readwrite("color_channels",
80  "Number of color channels.")
81  .def("__repr__", &RGBDVideoMetadata::ToString);
82 
83  // RGBD video reader trampoline
84  class PyRGBDVideoReader : public RGBDVideoReader {
85  public:
87  bool IsOpened() const override {
88  PYBIND11_OVERLOAD_PURE(bool, RGBDVideoReader, );
89  }
90 
91  bool IsEOF() const override {
92  PYBIND11_OVERLOAD_PURE(bool, RGBDVideoReader, );
93  }
94 
95  bool Open(const std::string &filename) override {
96  PYBIND11_OVERLOAD_PURE(bool, RGBDVideoReader, Open, filename);
97  }
98 
99  void Close() override {
100  PYBIND11_OVERLOAD_PURE(void, RGBDVideoReader, );
101  }
102 
103  RGBDVideoMetadata &GetMetadata() override {
104  PYBIND11_OVERLOAD_PURE(RGBDVideoMetadata &, RGBDVideoReader, );
105  }
106 
107  const RGBDVideoMetadata &GetMetadata() const override {
108  PYBIND11_OVERLOAD_PURE(const RGBDVideoMetadata &,
109  RGBDVideoReader, );
110  }
111 
112  bool SeekTimestamp(uint64_t timestamp) override {
113  PYBIND11_OVERLOAD_PURE(bool, RGBDVideoReader, SeekTimestamp,
114  timestamp);
115  }
116 
117  uint64_t GetTimestamp() const override {
118  PYBIND11_OVERLOAD_PURE(uint64_t, RGBDVideoReader, );
119  }
120 
121  t::geometry::RGBDImage NextFrame() override {
122  PYBIND11_OVERLOAD_PURE(t::geometry::RGBDImage, RGBDVideoReader, );
123  }
124 
125  std::string GetFilename() const override {
126  PYBIND11_OVERLOAD_PURE(std::string, RGBDVideoReader, );
127  }
128  };
129 
130  // Class RGBD video reader
131  py::class_<RGBDVideoReader, PyRGBDVideoReader,
132  std::unique_ptr<RGBDVideoReader>>
133  rgbd_video_reader(m, "RGBDVideoReader", "RGBD Video file reader.");
134  rgbd_video_reader.def(py::init<>())
135  .def_static(
136  "create",
137  [](const fs::path &filename) {
138  return RGBDVideoReader::Create(filename.string());
139  },
140  "filename"_a, "Create RGBD video reader based on filename")
141  .def("save_frames", &RGBDVideoReader::SaveFrames, "frame_path"_a,
142  "start_time_us"_a = 0, "end_time_us"_a = UINT64_MAX,
143  "Save synchronized and aligned individual frames to "
144  "subfolders.")
145  .def("__repr__", &RGBDVideoReader::ToString);
146  docstring::ClassMethodDocInject(m, "RGBDVideoReader", "create",
148  docstring::ClassMethodDocInject(m, "RGBDVideoReader", "save_frames",
150 
151  // Class RGBD sensor
152  py::class_<RGBDSensor> rgbd_sensor(
153  m, "RGBDSensor", "Interface class for control of RGBD cameras.");
154  rgbd_sensor.def("__repr__", &RGBDSensor::ToString);
155 
156 #ifdef BUILD_LIBREALSENSE
157  // Class RS bag reader
158  py::class_<RSBagReader, std::unique_ptr<RSBagReader>, RGBDVideoReader>
159  rs_bag_reader(
160  m, "RSBagReader",
161  "RealSense Bag file reader.\n"
162  "\tOnly the first color and depth streams from the bag "
163  "file will be read.\n"
164  " - The streams must have the same frame rate.\n"
165  " - The color stream must have RGB 8 bit (RGB8/BGR8) pixel "
166  "format\n"
167  " - The depth stream must have 16 bit unsigned int (Z16) "
168  "pixel format\n"
169  "The output is synchronized color and depth frame pairs "
170  "with the depth frame aligned to the color frame. "
171  "Unsynchronized frames will be dropped. With alignment, "
172  "the depth and color frames have the same viewpoint and "
173  "resolution. See format documentation `here "
174  "<https://intelrealsense.github.io/librealsense/doxygen/"
175  "rs__sensor_8h.html#ae04b7887ce35d16dbd9d2d295d23aac7>`__"
176  "\n\n"
177  ".. warning:: A few frames may be dropped if user code "
178  "takes a long time (>10 frame intervals) to process a "
179  "frame.");
180  rs_bag_reader.def(py::init<>())
181  .def(py::init<size_t>(),
182  "buffer_size"_a = RSBagReader::DEFAULT_BUFFER_SIZE)
183  .def("is_opened", &RSBagReader::IsOpened,
184  "Check if the RS bag file is opened.")
185  .def("open", &RSBagReader::Open,
186  py::call_guard<py::gil_scoped_release>(), "filename"_a,
187  "Open an RS bag playback.")
188  .def("close", &RSBagReader::Close,
189  "Close the opened RS bag playback.")
190  .def("is_eof", &RSBagReader::IsEOF,
191  "Check if the RS bag file is all read.")
192  .def_property(
193  "metadata",
194  py::overload_cast<>(&RSBagReader::GetMetadata, py::const_),
195  py::overload_cast<>(&RSBagReader::GetMetadata),
196  "Get metadata of the RS bag playback.")
197  .def("seek_timestamp", &RSBagReader::SeekTimestamp,
198  py::call_guard<py::gil_scoped_release>(), "timestamp"_a,
199  "Seek to the timestamp (in us).")
200  .def("get_timestamp", &RSBagReader::GetTimestamp,
201  "Get current timestamp (in us).")
202  .def("next_frame", &RSBagReader::NextFrame,
203  py::call_guard<py::gil_scoped_release>(),
204  "Get next frame from the RS bag playback and returns the RGBD "
205  "object.")
206  // Release Python GIL for SaveFrames, since this will take a while
207  .def("save_frames", &RSBagReader::SaveFrames,
208  py::call_guard<py::gil_scoped_release>(), "frame_path"_a,
209  "start_time_us"_a = 0, "end_time_us"_a = UINT64_MAX,
210  "Save synchronized and aligned individual frames to "
211  "subfolders.")
212  .def("__repr__", &RSBagReader::ToString);
213  docstring::ClassMethodDocInject(m, "RSBagReader", "__init__",
215  docstring::ClassMethodDocInject(m, "RSBagReader", "open",
217  docstring::ClassMethodDocInject(m, "RSBagReader", "seek_timestamp",
219  docstring::ClassMethodDocInject(m, "RSBagReader", "save_frames",
221 
222  // Class RealSenseSensorConfig
223  py::class_<RealSenseSensorConfig> realsense_sensor_config(
224  m, "RealSenseSensorConfig", "Configuration for a RealSense camera");
225 
226  realsense_sensor_config.def(py::init<>(), "Default config will be used")
227  .def(py::init<const std::unordered_map<std::string, std::string>
228  &>(),
229  "config"_a, "Initialize config with a map");
230 
231  py::class_<RealSenseValidConfigs> realsense_valid_configs(
232  m, "RealSenseValidConfigs",
233  "Store set of valid configuration options for a connected "
234  "RealSense device. From this structure, a user can construct a "
235  "RealSenseSensorConfig object meeting their specifications.");
236  realsense_valid_configs
237  .def_readwrite("serial", &RealSenseValidConfigs::serial,
238  "Device serial number.")
239  .def_readwrite("name", &RealSenseValidConfigs::name, "Device name.")
240  .def_readwrite("valid_configs",
242  "Mapping between configuraiton option name and a "
243  "list of valid values.");
244 
245  // Class RealSenseSensor
246  py::class_<RealSenseSensor, RGBDSensor> realsense_sensor(
247  m, "RealSenseSensor",
248  "RealSense camera discovery, configuration, streaming and "
249  "recording");
250  realsense_sensor.def(py::init<>(), "Initialize with default settings.")
251  .def_static("list_devices", &RealSenseSensor::ListDevices,
252  py::call_guard<py::gil_scoped_release>(),
253  "List all RealSense cameras connected to the system "
254  "along with their capabilities. Use this listing to "
255  "select an appropriate configuration for a camera")
256  .def_static("enumerate_devices", &RealSenseSensor::EnumerateDevices,
257  py::call_guard<py::gil_scoped_release>(),
258  "Query all connected RealSense cameras for their "
259  "capabilities.")
260  .def("init_sensor",
261  py::overload_cast<const RGBDSensorConfig &, size_t,
262  const std::string &>(
264  py::call_guard<py::gil_scoped_release>(),
265  "sensor_config"_a = RealSenseSensorConfig{},
266  "sensor_index"_a = 0, "filename"_a = "",
267  "Configure sensor with custom settings. If this is skipped, "
268  "default settings will be used. You can enable recording to a "
269  "bag file by specifying a filename.")
270  .def("init_sensor",
271  py::overload_cast<const RealSenseSensorConfig &, size_t,
272  const std::string &>(
274  py::call_guard<py::gil_scoped_release>(),
275  "sensor_config"_a = RealSenseSensorConfig{},
276  "sensor_index"_a = 0, "filename"_a = "",
277  "Configure sensor with custom settings. If this is skipped, "
278  "default settings will be used. You can enable recording to a "
279  "bag file by specifying a filename.")
280  .def("start_capture", &RealSenseSensor::StartCapture,
281  py::call_guard<py::gil_scoped_release>(),
282  "start_record"_a = false,
283  "Start capturing synchronized depth and color frames.")
284  .def("pause_record", &RealSenseSensor::PauseRecord,
285  "Pause recording to the bag file. Note: If this is called "
286  "immediately after start_capture, the bag file may have an "
287  "incorrect end time.")
288  .def("resume_record", &RealSenseSensor::ResumeRecord,
289  "Resume recording to the bag file. The file will contain "
290  "discontinuous segments.")
291  .def("capture_frame", &RealSenseSensor::CaptureFrame,
292  py::call_guard<py::gil_scoped_release>(), "wait"_a = true,
293  "align_depth_to_color"_a = true,
294  "Acquire the next synchronized RGBD frameset from the camera.")
295  .def("get_timestamp", &RealSenseSensor::GetTimestamp,
296  "Get current timestamp (in us)")
297  .def("stop_capture", &RealSenseSensor::StopCapture,
298  py::call_guard<py::gil_scoped_release>(),
299  "Stop capturing frames.")
300  .def("get_metadata", &RealSenseSensor::GetMetadata,
301  "Get metadata of the RealSense video capture.")
302  .def("get_filename", &RealSenseSensor::GetFilename,
303  "Get filename being written.")
304  .def("__repr__", &RealSenseSensor::ToString);
305 
307  m, "RealSenseSensor", "init_sensor",
308  {{"sensor_config",
309  "Camera configuration, such as resolution and framerate. A "
310  "serial number can be entered here to connect to a specific "
311  "camera."},
312  {"sensor_index",
313  "Connect to a camera at this position in the enumeration of "
314  "RealSense cameras that are currently connected. Use "
315  "enumerate_devices() or list_devices() to obtain a list of "
316  "connected cameras. This is ignored if sensor_config contains "
317  "a serial entry."},
318  {"filename", "Save frames to a bag file"}});
320  m, "RealSenseSensor", "start_capture",
321  {{"start_record",
322  "Start recording to the specified bag file as well."}});
324  m, "RealSenseSensor", "capture_frame",
325  {{"wait",
326  "If true wait for the next frame set, else return immediately "
327  "with an empty RGBDImage if it is not yet available."},
328  {"align_depth_to_color",
329  "Enable aligning WFOV depth image to the color image in "
330  "visualizer."}});
331 
332 #endif
333 }
334 
335 } // namespace io
336 } // namespace t
337 } // namespace cloudViewer
std::string filename
RGBDImage A pair of color and depth images.
Definition: RGBDImage.h:21
virtual const std::string ToString() const
Text Description.
Definition: RGBDSensor.h:81
std::string color_format_
Pixel format for color data.
core::Dtype depth_dt_
Pixel Dtype for depth data.
std::string serial_number_
Capture device serial number.
camera::PinholeCameraIntrinsic intrinsics_
Shared intrinsics between RGB & depth.
uint8_t color_channels_
Number of color channels.
std::string depth_format_
Pixel format for depth data.
core::Dtype color_dt_
Pixel Dtype for color data.
std::string device_name_
Capture device name.
int width_
Width of the video frame.
int height_
Height of the video frame.
uint64_t stream_length_usec_
Length of the video (usec). 0 for live capture.
static std::unique_ptr< RGBDVideoReader > Create(const std::string &filename)
Factory function to create object based on RGBD video file type.
virtual void SaveFrames(const std::string &frame_path, uint64_t start_time_us=0, uint64_t end_time_us=UINT64_MAX)
virtual std::string ToString() const
Text description.
virtual bool SeekTimestamp(uint64_t timestamp) override
static const size_t DEFAULT_BUFFER_SIZE
Definition: RSBagReader.h:51
virtual bool IsEOF() const override
Check if the RSBag file is all read.
virtual void SaveFrames(const std::string &frame_path, uint64_t start_time_us=0, uint64_t end_time_us=UINT64_MAX)
virtual bool IsOpened() const override
Check If the RSBag file is opened.
Definition: RSBagReader.h:64
virtual uint64_t GetTimestamp() const override
Get current timestamp (in us).
virtual bool Open(const std::string &filename) override
Definition: RSBagReader.cpp:44
virtual t::geometry::RGBDImage NextFrame() override
Copy next frame from the bag file and return the RGBDImage object.
virtual const RGBDVideoMetadata & GetMetadata() const override
Get (read-only) metadata of the playback.
Definition: RSBagReader.h:78
virtual std::string ToString() const
Text description.
virtual void Close() override
Close the opened RSBag playback.
Definition: RSBagReader.cpp:77
virtual bool InitSensor(const RealSenseSensorConfig &sensor_config=RealSenseSensorConfig{}, size_t sensor_index=0, const std::string &filename="")
virtual std::string GetFilename() const override
Get filename being written.
virtual void StopCapture() override
Stop capturing frames.
static std::vector< RealSenseValidConfigs > EnumerateDevices()
virtual uint64_t GetTimestamp() const override
virtual void ResumeRecord() override
virtual const RGBDVideoMetadata & GetMetadata() const override
Get metadata of the RealSense video capture.
virtual geometry::RGBDImage CaptureFrame(bool wait=true, bool align_depth_to_color=true) override
virtual bool StartCapture(bool start_record=false) override
virtual const std::string ToString() const
Text Description.
Definition: RGBDSensor.h:81
virtual std::string ToString() const
Convert to a styled string representation of JSON data for display.
void ClassMethodDocInject(py::module &pybind_module, const std::string &class_name, const std::string &function_name, const std::unordered_map< std::string, std::string > &map_parameter_body_docs)
Definition: docstring.cpp:27
static const std::string path
Definition: PointCloud.cpp:59
void pybind_sensor(py::module &m)
Definition: sensor.cpp:26
static const std::unordered_map< std::string, std::string > map_shared_argument_docstrings
Definition: class_io.cpp:25
Generic file read and write utility for python interface.
std::unordered_map< std::string, std::set< std::string > > valid_configs
std::string serial
Device serial number.