ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
AzureKinectSensor.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 <Parallel.h>
11 #include <RGBDImage.h>
12 #include <k4a/k4a.h>
13 #include <k4arecord/record.h>
14 #include <turbojpeg.h>
15 
16 #include <memory>
17 
19 
20 namespace cloudViewer {
21 namespace io {
22 
24  const AzureKinectSensorConfig &sensor_config)
25  : RGBDSensor(), sensor_config_(sensor_config) {}
26 
28 
29 bool AzureKinectSensor::Connect(size_t sensor_index) {
30  utility::LogInfo("AzureKinectSensor::Connect");
31  utility::LogInfo("sensor_index {}", sensor_index);
32  auto device_config = sensor_config_.ConvertToNativeConfig();
33 
34  // check mode
35  int camera_fps;
36  switch (device_config.camera_fps) {
37  case K4A_FRAMES_PER_SECOND_5:
38  camera_fps = 5;
39  break;
40  case K4A_FRAMES_PER_SECOND_15:
41  camera_fps = 15;
42  break;
43  case K4A_FRAMES_PER_SECOND_30:
44  camera_fps = 30;
45  break;
46  default:
47  camera_fps = 30;
48  break;
49  }
50  timeout_ = int(1000.0f / camera_fps);
51 
52  if (device_config.color_resolution == K4A_COLOR_RESOLUTION_OFF &&
53  device_config.depth_mode == K4A_DEPTH_MODE_OFF) {
55  "Config error: either the color or depth modes must be "
56  "enabled to record.");
57  return false;
58  }
59 
60  // check available devices
61  const uint32_t installed_devices =
63  if (sensor_index >= installed_devices) {
64  utility::LogWarning("Device not found.");
65  return false;
66  }
67 
68  // open device
69  if (K4A_FAILED(k4a_plugin::k4a_device_open(
70  static_cast<uint32_t>(sensor_index), &device_))) {
72  "Runtime error: k4a_plugin::k4a_device_open() failed");
73  return false;
74  }
75 
76  // get and print device info
78 
80  &device_config))) {
82  "Runtime error: k4a_plugin::k4a_device_start_cameras() "
83  "failed");
85  return false;
86  }
87 
88  // set color control, assume absoluteExposureValue == 0
90  device_, K4A_COLOR_CONTROL_EXPOSURE_TIME_ABSOLUTE,
91  K4A_COLOR_CONTROL_MODE_AUTO, 0))) {
93  "Runtime error: k4a_plugin::k4a_device_set_color_control() "
94  "failed");
96  return false;
97  }
98 
99  // set calibration
100  k4a_calibration_t calibration;
101  k4a_plugin::k4a_device_get_calibration(device_, device_config.depth_mode,
102  device_config.color_resolution,
103  &calibration);
106 
107  return true;
108 }
109 
113 }
114 
115 k4a_capture_t AzureKinectSensor::CaptureRawFrame() const {
116  k4a_capture_t capture;
117  auto result =
119  if (result == K4A_WAIT_RESULT_TIMEOUT) {
120  return nullptr;
121  } else if (result != K4A_WAIT_RESULT_SUCCEEDED) {
123  "Runtime error: k4a_plugin::k4a_device_get_capture() returned "
124  "{}",
125  result);
126  return nullptr;
127  }
128 
129  return capture;
130 }
131 
132 std::shared_ptr<geometry::RGBDImage> AzureKinectSensor::CaptureFrame(
133  bool enable_align_depth_to_color) const {
134  k4a_capture_t capture = CaptureRawFrame();
135  auto im_rgbd = DecompressCapture(
136  capture,
137  enable_align_depth_to_color ? transform_depth_to_color_ : nullptr);
139  return im_rgbd;
140 }
141 
143  if (bgra.bytes_per_channel_ != 1) {
144  utility::LogError("BGRA input image must have 1 byte per channel.");
145  }
146  if (rgb.bytes_per_channel_ != 1) {
147  utility::LogError("RGB output image must have 1 byte per channel.");
148  }
149  if (bgra.num_of_channels_ != 4) {
150  utility::LogError("BGRA input image must have 4 channels.");
151  }
152  if (rgb.num_of_channels_ != 3) {
153  utility::LogError("RGB output image must have 3 channels.");
154  }
155  if (bgra.width_ != rgb.width_ || bgra.height_ != rgb.height_) {
157  "BGRA input image and RGB output image have different "
158  "dimensions.");
159  }
160 
161 #ifdef _WIN32
162 #pragma omp parallel for schedule(static) \
163  num_threads(utility::EstimateMaxThreads())
164 #else
165 #pragma omp parallel for collapse(3) schedule(static)
166 #endif
167  for (int v = 0; v < bgra.height_; ++v) {
168  for (int u = 0; u < bgra.width_; ++u) {
169  for (int c = 0; c < 3; ++c) {
170  *rgb.PointerAt<uint8_t>(u, v, c) =
171  *bgra.PointerAt<uint8_t>(u, v, 2 - c);
172  }
173  }
174  }
175 }
176 
177 bool AzureKinectSensor::PrintFirmware(k4a_device_t device) {
178  char serial_number_buffer[256];
179  size_t serial_number_buffer_size = sizeof(serial_number_buffer);
180  if (K4A_BUFFER_RESULT_SUCCEEDED !=
181  k4a_plugin::k4a_device_get_serialnum(device, serial_number_buffer,
182  &serial_number_buffer_size)) {
184  "Runtime error: k4a_plugin::k4a_device_get_serialnum() "
185  "failed");
186  return false;
187  }
188 
189  k4a_hardware_version_t version_info;
190  if (K4A_FAILED(k4a_plugin::k4a_device_get_version(device, &version_info))) {
192  "Runtime error: k4a_plugin::k4a_device_get_version() failed");
193  return false;
194  }
195 
196  utility::LogInfo("Serial number: {}", serial_number_buffer);
197  utility::LogInfo("Firmware build: {}",
198  (version_info.firmware_build == K4A_FIRMWARE_BUILD_RELEASE
199  ? "Rel"
200  : "Dbg"));
201  utility::LogInfo("> Color: {}.{}.{}", version_info.rgb.major,
202  version_info.rgb.minor, version_info.rgb.iteration);
203  utility::LogInfo("> Depth: {}.{}.{}[{}.{}]", version_info.depth.major,
204  version_info.depth.minor, version_info.depth.iteration,
205  version_info.depth_sensor.major,
206  version_info.depth_sensor.minor);
207  return true;
208 }
209 
211  uint32_t device_count = k4a_plugin::k4a_device_get_installed_count();
212  if (device_count > 0) {
213  for (uint8_t i = 0; i < device_count; i++) {
214  utility::LogInfo("Device index {}", i);
215  k4a_device_t device;
216  if (K4A_SUCCEEDED(k4a_plugin::k4a_device_open(i, &device))) {
219  } else {
220  utility::LogWarning("Device Open Failed");
221  return false;
222  }
223  }
224  } else {
225  utility::LogError("No devices connected.");
226  }
227 
228  return true;
229 }
230 
231 std::shared_ptr<geometry::RGBDImage> AzureKinectSensor::DecompressCapture(
232  k4a_capture_t capture, k4a_transformation_t transformation) {
233  static std::shared_ptr<geometry::Image> color_buffer = nullptr;
234  static std::shared_ptr<geometry::RGBDImage> rgbd_buffer = nullptr;
235 
236  if (color_buffer == nullptr) {
237  color_buffer = std::make_shared<geometry::Image>();
238  }
239  if (rgbd_buffer == nullptr) {
240  rgbd_buffer = std::make_shared<geometry::RGBDImage>();
241  }
242 
243  k4a_image_t k4a_color = k4a_plugin::k4a_capture_get_color_image(capture);
244  k4a_image_t k4a_depth = k4a_plugin::k4a_capture_get_depth_image(capture);
245  if (k4a_color == nullptr || k4a_depth == nullptr) {
246  utility::LogDebug("Skipping empty captures.");
247  return nullptr;
248  }
249 
250  /* Process color */
251  if (K4A_IMAGE_FORMAT_COLOR_MJPG !=
254  "Unexpected image format. The stream may have been corrupted.");
255  return nullptr;
256  }
257 
260 
261  /* resize */
262  rgbd_buffer->color_.Prepare(width, height, 3, sizeof(uint8_t));
263  color_buffer->Prepare(width, height, 4, sizeof(uint8_t));
264 
265  tjhandle tjHandle;
266  tjHandle = tjInitDecompress();
267  if (0 !=
268  tjDecompress2(tjHandle, k4a_plugin::k4a_image_get_buffer(k4a_color),
269  static_cast<unsigned long>(
270  k4a_plugin::k4a_image_get_size(k4a_color)),
271  color_buffer->data_.data(), width, 0 /* pitch */, height,
272  TJPF_BGRA, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE)) {
273  utility::LogWarning("Failed to decompress color image.");
274  return nullptr;
275  }
276  tjDestroy(tjHandle);
277  ConvertBGRAToRGB(*color_buffer, rgbd_buffer->color_);
278 
279  /* transform depth to color plane */
280  k4a_image_t k4a_transformed_depth = nullptr;
281  if (transformation) {
282  rgbd_buffer->depth_.Prepare(width, height, 1, sizeof(uint16_t));
284  K4A_IMAGE_FORMAT_DEPTH16, width, height,
285  width * sizeof(uint16_t), rgbd_buffer->depth_.data_.data(),
286  width * height * sizeof(uint16_t), NULL, NULL,
287  &k4a_transformed_depth);
288  if (K4A_RESULT_SUCCEEDED !=
290  transformation, k4a_depth, k4a_transformed_depth)) {
292  "Failed to transform depth frame to color frame.");
293  return nullptr;
294  }
295  } else {
296  rgbd_buffer->depth_.Prepare(
299  sizeof(uint16_t));
300  memcpy(rgbd_buffer->depth_.data_.data(),
302  k4a_plugin::k4a_image_get_size(k4a_depth));
303  }
304 
305  /* process depth */
308  if (transformation) {
309  k4a_plugin::k4a_image_release(k4a_transformed_depth);
310  }
311 
312  return rgbd_buffer;
313 }
314 
315 } // namespace io
316 } // namespace cloudViewer
int width
int height
#define NULL
core::Tensor result
Definition: VtkUtils.cpp:76
The Image class stores image with customizable width, height, num of channels and bytes per channel.
Definition: Image.h:33
int num_of_channels_
Number of chanels in the image.
Definition: Image.h:223
int height_
Height of the image.
Definition: Image.h:221
int bytes_per_channel_
Number of bytes per channel.
Definition: Image.h:225
int width_
Width of the image.
Definition: Image.h:219
T * PointerAt(int u, int v) const
Function to access the raw data of a single-channel Image.
_k4a_device_configuration_t ConvertToNativeConfig() const
static bool PrintFirmware(_k4a_device_t *device)
AzureKinectSensorConfig sensor_config_
static bool ListDevices()
List available Azure Kinect devices.
bool Connect(size_t sensor_index) override
std::shared_ptr< geometry::RGBDImage > CaptureFrame(bool enable_align_depth_to_color) const override
AzureKinectSensor(const AzureKinectSensorConfig &sensor_config)
Default Constructor.
static std::shared_ptr< geometry::RGBDImage > DecompressCapture(_k4a_capture_t *capture, _k4a_transformation_t *transformation)
_k4a_capture_t * CaptureRawFrame() const
_k4a_transformation_t * transform_depth_to_color_
#define LogWarning(...)
Definition: Logging.h:72
#define LogInfo(...)
Definition: Logging.h:81
#define LogError(...)
Definition: Logging.h:60
#define LogDebug(...)
Definition: Logging.h:90
uint32_t k4a_device_get_installed_count()
Definition: K4aPlugin.cpp:346
k4a_image_t k4a_capture_get_depth_image(k4a_capture_t capture_handle)
Definition: K4aPlugin.cpp:414
void k4a_capture_release(k4a_capture_t capture_handle)
Definition: K4aPlugin.cpp:396
k4a_result_t k4a_image_create_from_buffer(k4a_image_format_t format, int width_pixels, int height_pixels, int stride_bytes, uint8_t *buffer, size_t buffer_size, k4a_memory_destroy_cb_t *buffer_release_cb, void *buffer_release_cb_context, k4a_image_t *image_handle)
Definition: K4aPlugin.cpp:494
k4a_result_t k4a_device_open(uint32_t index, k4a_device_t *device_handle)
Definition: K4aPlugin.cpp:364
k4a_result_t k4a_device_get_calibration(k4a_device_t device_handle, const k4a_depth_mode_t depth_mode, const k4a_color_resolution_t color_resolution, k4a_calibration_t *calibration)
Definition: K4aPlugin.cpp:693
k4a_result_t k4a_device_get_version(k4a_device_t device_handle, k4a_hardware_version_t *version)
Definition: K4aPlugin.cpp:627
void k4a_image_release(k4a_image_t image_handle)
Definition: K4aPlugin.cpp:586
k4a_result_t k4a_device_set_color_control(k4a_device_t device_handle, k4a_color_control_command_t command, k4a_color_control_mode_t mode, int32_t value)
Definition: K4aPlugin.cpp:671
uint8_t * k4a_image_get_buffer(k4a_image_t image_handle)
Definition: K4aPlugin.cpp:497
void k4a_device_close(k4a_device_t device_handle)
Definition: K4aPlugin.cpp:367
int k4a_image_get_height_pixels(k4a_image_t image_handle)
Definition: K4aPlugin.cpp:518
k4a_image_format_t k4a_image_get_format(k4a_image_t image_handle)
Definition: K4aPlugin.cpp:506
k4a_buffer_result_t k4a_device_get_serialnum(k4a_device_t device_handle, char *serial_number, size_t *serial_number_size)
Definition: K4aPlugin.cpp:619
k4a_transformation_t k4a_transformation_create(const k4a_calibration_t *calibration)
Definition: K4aPlugin.cpp:789
k4a_result_t k4a_transformation_depth_image_to_color_camera(k4a_transformation_t transformation_handle, const k4a_image_t depth_image, k4a_image_t transformed_depth_image)
Definition: K4aPlugin.cpp:805
void k4a_device_stop_cameras(k4a_device_t device_handle)
Definition: K4aPlugin.cpp:600
k4a_image_t k4a_capture_get_color_image(k4a_capture_t capture_handle)
Definition: K4aPlugin.cpp:408
k4a_wait_result_t k4a_device_get_capture(k4a_device_t device_handle, k4a_capture_t *capture_handle, int32_t timeout_in_ms)
Definition: K4aPlugin.cpp:377
size_t k4a_image_get_size(k4a_image_t image_handle)
Definition: K4aPlugin.cpp:500
int k4a_image_get_width_pixels(k4a_image_t image_handle)
Definition: K4aPlugin.cpp:512
k4a_result_t k4a_device_start_cameras(k4a_device_t device_handle, k4a_device_configuration_t *config)
Definition: K4aPlugin.cpp:594
void ConvertBGRAToRGB(geometry::Image &bgra, geometry::Image &rgb)
Generic file read and write utility for python interface.