9 #include <opencv2/core/hal/interface.h>
10 #include <opencv2/imgproc/imgproc.hpp>
12 #include <IceUtil/Time.h>
32 #include <Calibration/Calibration.h>
33 #include <Image/ImageProcessor.h>
35 #ifdef INCLUDE_BODY_TRACKING
48 k4aToIvtImage(
const k4a::image& color_image, ::CByteImage& result)
50 if (color_image.get_format() == K4A_IMAGE_FORMAT_COLOR_YUY2)
53 cv::Mat yuy2_image(color_image.get_height_pixels(),
54 color_image.get_width_pixels(),
56 const_cast<uint8_t*
>(color_image.get_buffer()));
58 cv::cvtColor(yuy2_image, rgb_image, cv::COLOR_YUV2RGB_YUY2);
63 else if (color_image.get_format() == K4A_IMAGE_FORMAT_COLOR_BGRA32)
65 auto cw =
static_cast<unsigned int>(color_image.get_width_pixels());
66 auto ch =
static_cast<unsigned int>(color_image.get_height_pixels());
70 auto color_buffer = color_image.get_buffer();
71 auto rgb_buffer_ivt = result.pixels;
79 for (
unsigned int y = 0; y < ch; ++y)
81 for (
unsigned int x = 0; x < cw; ++x)
83 rgb_buffer_ivt[index_ivt] = color_buffer[index_k4a + 2];
84 rgb_buffer_ivt[index_ivt + 1] = color_buffer[index_k4a + 1];
85 rgb_buffer_ivt[index_ivt + 2] = color_buffer[index_k4a + 0];
93 throw std::runtime_error(
"Unsupported color format in k4a image");
98 #ifdef INCLUDE_BODY_TRACKING
100 printBodyInformation(k4abt_body_t body)
103 for (
int i = 0; i < static_cast<int>(K4ABT_JOINT_COUNT); i++)
105 const k4a_float3_t position = body.skeleton.joints[i].position;
106 const k4a_quaternion_t orientation = body.skeleton.joints[i].orientation;
107 const k4abt_joint_confidence_level_t confidence_level =
108 body.skeleton.joints[i].confidence_level;
110 ARMARX_VERBOSE <<
"Joint" << i <<
": " <<
"Position[mm] (" << position.v[0] <<
","
111 << position.v[1] <<
"," << position.v[2] <<
"); " <<
"Orientation ("
112 << orientation.v[0] <<
"," << orientation.v[1] <<
"," << orientation.v[2]
113 <<
"," << orientation.v[3] <<
"); " <<
"Confidence Level "
120 k4aImageFormatToString(k4a_image_format_t format)
124 case K4A_IMAGE_FORMAT_COLOR_MJPG:
126 case K4A_IMAGE_FORMAT_COLOR_NV12:
128 case K4A_IMAGE_FORMAT_COLOR_YUY2:
130 case K4A_IMAGE_FORMAT_COLOR_BGRA32:
131 return "COLOR_BGRA32";
132 case K4A_IMAGE_FORMAT_DEPTH16:
134 case K4A_IMAGE_FORMAT_IR16:
136 case K4A_IMAGE_FORMAT_CUSTOM:
153 std::string name =
"duration " + description +
" in [ms]";
155 std::lock_guard g{metaInfoMtx};
160 std::lock_guard g{debugObserverMtx};
170 defineOptionalProperty<k4a_color_resolution_t>(
171 "ColorResolution", K4A_COLOR_RESOLUTION_720P,
"Resolution of the RGB camera image.")
172 .map(
"0", K4A_COLOR_RESOLUTION_OFF)
173 .map(
"720", K4A_COLOR_RESOLUTION_720P)
174 .map(
"1080", K4A_COLOR_RESOLUTION_1080P)
175 .map(
"1440", K4A_COLOR_RESOLUTION_1440P)
176 .map(
"1536", K4A_COLOR_RESOLUTION_1536P)
177 .map(
"2160", K4A_COLOR_RESOLUTION_2160P)
178 .map(
"3072", K4A_COLOR_RESOLUTION_3072P);
179 defineOptionalProperty<k4a_depth_mode_t>(
180 "DepthMode", K4A_DEPTH_MODE_NFOV_UNBINNED,
"Resolution/mode of the depth camera image.")
181 .setCaseInsensitive(
true)
182 .map(
"OFF", K4A_DEPTH_MODE_OFF)
183 .map(
"NFOV_2X2BINNED", K4A_DEPTH_MODE_NFOV_2X2BINNED)
184 .map(
"NFOV_UNBINNED", K4A_DEPTH_MODE_NFOV_UNBINNED)
185 .map(
"WFOV_2X2BINNED", K4A_DEPTH_MODE_WFOV_2X2BINNED)
186 .map(
"WFOV_UNBINNED", K4A_DEPTH_MODE_WFOV_UNBINNED)
187 .map(
"PASSIVE_IR", K4A_DEPTH_MODE_PASSIVE_IR);
189 defineOptionalProperty<float>(
"MaxDepth",
191 "Max. allowed depth value in mm. Depth values above this "
192 "threshold will be set to nan.");
194 defineOptionalProperty<float>(
"CaptureTimeOffset",
196 "In Milliseconds. Time offset between capturing the image on "
197 "the hardware and receiving the image in this process.",
200 defineOptionalProperty<k4a_image_format_t>(
201 "ColorFormat", K4A_IMAGE_FORMAT_COLOR_BGRA32,
"Color format of the RGB camera image.")
202 .map(
"BGRA", K4A_IMAGE_FORMAT_COLOR_BGRA32)
203 .map(
"YUY2", K4A_IMAGE_FORMAT_COLOR_YUY2);
213 enableColorUndistortion,
214 "EnableColorUndistortion",
215 "Undistort the color images using the full 8 radial and tangential distortion "
216 "parameters provided by the Azure Kinect.\n"
217 "This can help for processing tasks which cannot handle radial parameters k3-k6.\n"
218 "Note that this drastically reduces the FPS (to something like 3).");
219 defs->optional(externalCalibrationFilePath,
220 "ExternalCalibrationFilePath",
221 "Path to an optional external"
222 " calibration file, which has a"
223 " camera matrix and distortion"
225 defs->optional(mDeviceId,
"device_id",
"ID of the device.");
227 defs->optional(robotName,
"robotName");
228 defs->optional(bodyCameraFrameName,
"bodyCameraFrameName");
229 defs->optional(framerate.value,
231 "The framerate of RGB-D images [frames per second]."
232 "\nNote that the point cloud and body tracking frame rates are controlled by"
233 " the properties 'framerate' (point cloud) and 'framerate.bodyTracking'"
234 " (body tracking), respectively.")
238 #ifdef INCLUDE_BODY_TRACKING
239 defs->optional(bodyTrackingEnabled,
240 "bodyTrackingEnabled",
241 "Whether the Azure Kinect Body Tracking SDK should be enabled or not.");
242 defs->optional(bodyTrackingRunAtStart,
243 "bodyTrackingRunAtStart",
244 "Whether the Azure Kinect Body Tracking SDK should directly run when the "
245 "component is startet."
246 "Otherwise it has to be activated by the ice interface.");
247 defs->optional(bodyTrackingModelFilename,
248 "bodyTrackingModelPath",
249 "Path where the .onnx DNN files can be found");
250 defs->optional(bodyTrackingGPUDeviceID,
"bodyTrackingGPUDeviceID",
"GPU Device ID.");
251 defs->optional(bodyTrackingTemporalSmoothingFactor,
252 "bodyTrackingTemporalSmoothingFactor",
253 "Temporal smoothing factor for Azure Kinect body tracking.");
255 defs->optional(bodyTrackingDepthMaskMinX,
"bodyTrackingDepthMaskMinX");
256 defs->optional(bodyTrackingDepthMaskMaxX,
"bodyTrackingDepthMaskMaxX");
257 defs->optional(bodyTrackingDepthMaskMaxZ,
"bodyTrackingDepthMaskMaxZ");
258 defs->optional(framerate.bodyTracking.value,
259 "framerate.bodyTracking",
260 "The framerate with with the body tracking is run [frames per second]."
261 "\nNote that the RGB-D image and point cloud frame rates are controlled by"
262 " the properties 'framerate.image' (RGB-D images) and 'framerate'"
263 " (point cloud), respectively.")
267 defs->optional(startIMU,
"startIMU");
269 humanPoseWriter.registerPropertyDefinitions(defs);
272 defs->optional(enableHeartbeat,
"enableHeartbeat");
280 config = K4A_DEVICE_CONFIG_INIT_DISABLE_ALL;
281 if (framerate.value == 5)
283 config.camera_fps = K4A_FRAMES_PER_SECOND_5;
285 else if (framerate.value == 15)
287 config.camera_fps = K4A_FRAMES_PER_SECOND_15;
289 else if (framerate.value == 30)
291 config.camera_fps = K4A_FRAMES_PER_SECOND_30;
295 throw armarx::LocalException(
"Invalid image framerate (property 'framerate.images'): ")
296 << framerate.value <<
". Only framerates 5, 15 and 30 are "
297 <<
"supported by Azure Kinect.";
300 framerate.pointCloud.value =
301 getProperty<float>(
"framerate");
302 framerate.pointCloud.update(framerate.value);
304 #ifdef INCLUDE_BODY_TRACKING
305 framerate.bodyTracking.update(framerate.value);
308 config.depth_mode = getProperty<k4a_depth_mode_t>(
"DepthMode");
309 config.color_format = getProperty<k4a_image_format_t>(
"ColorFormat");
310 config.color_resolution = getProperty<k4a_color_resolution_t>(
"ColorResolution");
314 config.synchronized_images_only =
true;
322 ARMARX_INFO <<
"Depth image size: " << depth_dim.first <<
"x" << depth_dim.second;
323 ARMARX_INFO <<
"Color image size: " << color_dim.first <<
"x" << color_dim.second;
326 k4a::image::create(K4A_IMAGE_FORMAT_DEPTH16,
329 color_dim.first * 2 *
static_cast<int32_t
>(
sizeof(uint8_t)));
331 setImageFormat(visionx::ImageDimension(color_dim.first, color_dim.second),
333 visionx::eBayerPatternGr);
337 std::make_unique<CByteImage>(color_dim.first, color_dim.second, CByteImage::eRGB24);
339 xyzImage = k4a::image::create(K4A_IMAGE_FORMAT_CUSTOM,
342 color_dim.first * 3 *
static_cast<int32_t
>(
sizeof(int16_t)));
344 pointcloud = std::make_unique<pcl::PointCloud<CloudPointType>>();
357 pointcloudTask->start();
359 #ifdef INCLUDE_BODY_TRACKING
360 if (bodyTrackingEnabled)
363 humanPoseWriter.connect(memoryNameSystem());
367 this, &AzureKinectPointCloudProvider::runPublishBodyTrackingResults);
376 {
"Vision",
"Camera"},
377 "AzureKinectPointCloudProvider");
390 std::lock_guard<std::mutex> lock(pointcloudProcMutex);
391 ARMARX_DEBUG <<
"Stopping pointcloud processing thread...";
392 const bool WAIT_FOR_JOIN =
false;
393 pointcloudTask->stop(WAIT_FOR_JOIN);
394 pointcloudProcSignal.notify_all();
395 ARMARX_DEBUG <<
"Waiting for pointcloud processing thread to stop...";
396 pointcloudTask->waitForStop();
400 #ifdef INCLUDE_BODY_TRACKING
401 if (bodyTrackingIsRunning)
403 bodyTrackingPublishTask->stop();
433 const uint32_t DEVICE_COUNT = k4a::device::get_installed_count();
434 if (DEVICE_COUNT == 0)
437 throw armarx::LocalException(
"No Azure Kinect devices detected!");
442 device = k4a::device::open(
static_cast<uint32_t
>(mDeviceId));
443 ARMARX_DEBUG <<
"Opened device id #" << mDeviceId <<
" with serial number "
444 << device.get_serialnum() <<
".";
453 k4aCalibration = device.get_calibration(config.depth_mode, config.color_resolution);
455 <<
"Color camera calibration:" <<
"\n"
456 <<
"cx: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.cx
458 <<
"cy: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.cy
460 <<
"fx: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.fx
462 <<
"fy: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.fy
464 <<
"k1: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k1
466 <<
"k2: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k2
468 <<
"p1: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.p1
470 <<
"p2: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.p2
472 <<
"k3: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k3
474 <<
"k4: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k4
476 <<
"k5: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k5
478 <<
"k6: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k6;
483 c_left->PrintCameraParameters();
486 c_right->PrintCameraParameters();
489 transformation = k4a::transformation(k4aCalibration);
491 #ifdef INCLUDE_BODY_TRACKING
492 if (bodyTrackingEnabled)
498 bodyTrackingModelFilename);
499 ARMARX_CHECK(found) <<
"Body tracking DNN model could not be found/resolved at `"
500 << bodyTrackingModelFilename <<
"`.";
502 ARMARX_INFO <<
"Using body tracking DNN model from directory `"
503 << bodyTrackingModelFilename <<
"`.";
505 ARMARX_CHECK(std::filesystem::exists(bodyTrackingModelFilename))
506 <<
"The path `" << bodyTrackingModelFilename <<
"` does not exist!";
508 k4abt_tracker_configuration_t
const bodyTrackingConfig{
509 .sensor_orientation = K4ABT_SENSOR_ORIENTATION_DEFAULT,
510 .processing_mode = K4ABT_TRACKER_PROCESSING_MODE_GPU_CUDA,
511 .gpu_device_id = bodyTrackingGPUDeviceID,
512 .model_path = bodyTrackingModelFilename.c_str()};
514 bodyTracker = k4abt::tracker::create(k4aCalibration, bodyTrackingConfig);
515 bodyTracker.set_temporal_smoothing(bodyTrackingTemporalSmoothingFactor);
517 if (bodyTrackingRunAtStart and not bodyTrackingIsRunning)
519 bodyTrackingIsRunning =
true;
520 bodyTrackingPublishTask->start();
526 device.set_color_control(K4A_COLOR_CONTROL_BRIGHTNESS, K4A_COLOR_CONTROL_MODE_MANUAL, 128);
528 device.start_cameras(&config);
555 const k4a_calibration_camera_t calibration{k4aCalibration.color_camera_calibration};
556 const k4a_calibration_intrinsic_parameters_t::_param param =
557 calibration.intrinsics.parameters.param;
564 cv::Mat1f camera_matrix(3, 3);
565 camera_matrix << param.fx, 0, param.cx, 0, param.fy, param.cy, 0, 0, 1;
566 cv::Mat1f new_camera_matrix(3, 3);
567 new_camera_matrix << param.fx, 0, param.cx, 0, param.fx, param.cy, 0, 0, 1;
568 cv::Mat1f distortion_coeff(1, 8);
569 distortion_coeff << param.k1, param.k2, param.p1, param.p2, param.k3, param.k4,
571 cv::Mat map1, map2, map3;
572 cv::initUndistortRectifyMap(
577 cv::Size{calibration.resolution_width, calibration.resolution_height},
581 cv::convertMaps(map1, map2, colorDistortionMap, map3, CV_16SC2,
true);
603 const std::chrono::milliseconds TIMEOUT{1000};
605 StopWatch sw_get_capture;
613 catch (
const std::exception&)
615 ARMARX_WARNING <<
"Failed to get capture from device (#" << ++mDiagnostics.num_crashes
616 <<
"). Restarting camera.";
618 device.stop_cameras();
619 device.start_cameras(&config);
620 ARMARX_INFO <<
"Restarting took " << sw.stop() <<
".";
629 std::lock_guard g{metaInfoMtx};
634 const bool mustInitializeTimestampOffset = [&]()
636 std::lock_guard g{deviceToRealtimeOffsetMtx};
637 return device_to_realtime_offset_.count() == 0;
639 if (mustInitializeTimestampOffset)
646 capture.get_ir_image().get_system_timestamp());
648 const k4a::image DEPTH_IMAGE =
capture.get_depth_image();
652 const k4a_image_format_t IMAGE_FORMAT = DEPTH_IMAGE.get_format();
653 if (IMAGE_FORMAT != K4A_IMAGE_FORMAT_DEPTH16 && IMAGE_FORMAT != K4A_IMAGE_FORMAT_IR16)
655 const char* format_str = ::k4aImageFormatToString(IMAGE_FORMAT);
656 std::stringstream error_msg;
657 error_msg <<
"Attempted to colorize a non-depth image with format: " << format_str;
658 throw std::logic_error(error_msg.str());
662 if (not framerate.pointCloud.skip())
668 std::lock_guard lock{pointcloudProcMutex};
669 transformation.depth_image_to_color_camera(DEPTH_IMAGE, &alignedDepthImage);
672 depthImageReady =
true;
676 pointcloudProcSignal.notify_one();
679 #ifdef INCLUDE_BODY_TRACKING
680 if (bodyTrackingIsRunning)
683 std::scoped_lock lock(bodyTrackingParameterMutex);
685 if (not framerate.bodyTracking.skip())
687 k4a::image ir_image =
capture.get_ir_image();
688 std::uint8_t* ir_image_buffer = ir_image.get_buffer();
690 k4a::image depth_image =
capture.get_depth_image();
691 std::uint8_t* depth_image_buffer = depth_image.get_buffer();
695 depth_image.get_height_pixels());
697 const int stride = ir_image.get_stride_bytes() / ir_image.get_width_pixels();
699 for (
int x = 0; x < ir_image.get_width_pixels(); x++)
701 for (
int y = 0; y < ir_image.get_height_pixels(); y++)
703 const int i = (y * ir_image.get_width_pixels() * stride) + (x * stride);
704 const int z = (
static_cast<int>(depth_image_buffer[i])) +
705 (
static_cast<int>(depth_image_buffer[i + 1]) << 8);
707 if ((bodyTrackingDepthMaskMinX > 0 and x < bodyTrackingDepthMaskMinX) or
708 (bodyTrackingDepthMaskMaxX > 0 and x > bodyTrackingDepthMaskMaxX) or
709 (bodyTrackingDepthMaskMaxZ > 0 and z > bodyTrackingDepthMaskMaxZ))
714 depth_image_buffer[i + 1] =
720 if (not bodyTracker.enqueue_capture(
capture))
728 const k4a::image COLOR_IMAGE =
capture.get_color_image();
731 auto real_time = IceUtil::Time::now();
732 auto monotonic_time = IceUtil::Time::now(IceUtil::Time::Monotonic);
733 auto clock_diff = real_time - monotonic_time;
735 auto image_monotonic_time =
736 IceUtil::Time::microSeconds(std::chrono::duration_cast<std::chrono::microseconds>(
737 DEPTH_IMAGE.get_system_timestamp())
739 long offset = long(getProperty<float>(
"CaptureTimeOffset").getValue() * 1000.0f);
741 imagesTime = image_monotonic_time + clock_diff - IceUtil::Time::microSeconds(offset);
744 std::lock_guard g{metaInfoMtx};
750 std::lock_guard g{debugObserverMtx};
752 (real_time - imagesTime).toMilliSecondsDouble());
755 if (enableColorUndistortion)
758 cv::Mat tmp_rgb_image;
760 if (COLOR_IMAGE.get_format() == K4A_IMAGE_FORMAT_COLOR_YUY2)
763 cv::Mat yuy2_image(COLOR_IMAGE.get_height_pixels(),
764 COLOR_IMAGE.get_width_pixels(),
766 const_cast<uint8_t*
>(COLOR_IMAGE.get_buffer()));
768 cv::cvtColor(yuy2_image, tmp_rgb_image, cv::COLOR_YUV2RGB_YUY2);
770 else if (COLOR_IMAGE.get_format() == K4A_IMAGE_FORMAT_COLOR_BGRA32)
772 cv::cvtColor(cv::Mat{cv::Size{COLOR_IMAGE.get_width_pixels(),
773 COLOR_IMAGE.get_height_pixels()},
775 (
void*)COLOR_IMAGE.get_buffer(),
782 throw std::runtime_error(
"Unsupported color format in k4a image");
785 cv::Mat cv_color_image_undistorted(
786 COLOR_IMAGE.get_width_pixels(), COLOR_IMAGE.get_height_pixels(), CV_8UC3);
788 cv::remap(tmp_rgb_image,
789 cv_color_image_undistorted,
793 cv::BORDER_CONSTANT);
801 ::k4aToIvtImage(COLOR_IMAGE, *resultColorImage);
809 const int DW = alignedDepthImage.get_width_pixels();
810 const int DH = alignedDepthImage.get_height_pixels();
816 auto result_depth_buffer = resultDepthImage->pixels;
817 const auto* depth_buffer =
818 reinterpret_cast<const uint16_t*
>(alignedDepthImage.get_buffer());
822 for (
int y = 0; y < DH; ++y)
824 for (
int x = 0; x < DW; ++x)
826 uint16_t depth_value = depth_buffer[index_2];
828 result_depth_buffer[
index],
829 result_depth_buffer[
index + 1],
830 result_depth_buffer[
index + 2]);
840 CByteImage* images[2] = {resultColorImage.get(), resultDepthImage.get()};
846 std::unique_lock signal_lock{pointcloudProcMutex};
847 ARMARX_DEBUG <<
"Capturing thread waiting for signal...";
848 pointcloudProcSignal.wait(signal_lock, [&] {
return depthImageProcessed; });
853 std::lock_guard g{debugObserverMtx};
869 std::lock_guard g{debugObserverMtx};
880 Eigen::Vector3f accMean = Eigen::Vector3f::Zero();
883 k4a_imu_sample_t imu_sample;
884 while (device.get_imu_sample(&imu_sample, std::chrono::milliseconds(0)))
886 const Eigen::Vector3f acceleration{imu_sample.acc_sample.xyz.x,
887 imu_sample.acc_sample.xyz.y,
888 imu_sample.acc_sample.xyz.z};
890 accMean += acceleration;
894 accMean /=
static_cast<float>(cnt);
899 std::lock_guard g{debugObserverMtx};
908 catch (
const std::exception&)
911 << ++mDiagnostics.num_crashes <<
").";
918 #ifdef INCLUDE_BODY_TRACKING
920 AzureKinectPointCloudProvider::runPublishBodyTrackingResults()
923 while (bodyTrackingIsRunning)
927 const k4abt::frame body_frame = [&]() -> k4abt::frame
931 auto result = bodyTracker.pop_result();
942 if (body_frame !=
nullptr)
951 auto real_time = IceUtil::Time::now();
952 auto monotonic_time = IceUtil::Time::now(IceUtil::Time::Monotonic);
953 auto clock_diff = real_time - monotonic_time;
955 auto image_monotonic_time = IceUtil::Time::microSeconds(
956 std::chrono::duration_cast<std::chrono::microseconds>(
957 body_frame.get_system_timestamp())
965 std::lock_guard g{debugObserverMtx};
967 imageTime.toMicroSeconds());
976 auto real_time = realtimeClock.
now();
977 auto monotonic_time = monotonicClock.
now();
978 auto clock_diff = real_time - monotonic_time;
981 std::chrono::duration_cast<std::chrono::microseconds>(
982 body_frame.get_system_timestamp())
987 auto imageTime = image_monotonic_time + clock_diff;
992 std::lock_guard g{debugObserverMtx};
994 imageTime.toMicroSeconds());
999 std::uint32_t num_bodies = body_frame.get_num_bodies();
1001 std::lock_guard g{debugObserverMtx};
1005 std::vector<armarx::armem::human::HumanPose> humanPoses;
1006 humanPoses.reserve(num_bodies);
1008 for (std::uint32_t i = 0; i < num_bodies; i++)
1010 k4abt_body_t body = body_frame.get_body(i);
1011 printBodyInformation(body);
1018 for (
int i = 0; i < static_cast<int>(K4ABT_JOINT_COUNT); i++)
1025 k4a_float3_t position = body.skeleton.joints[i].position;
1026 k4a_quaternion_t orientation = body.skeleton.joints[i].orientation;
1027 k4abt_joint_confidence_level_t confidence_level =
1028 body.skeleton.joints[i].confidence_level;
1032 .confidence =
static_cast<float>(
static_cast<int>(confidence_level)),
1034 Eigen::Vector3f{position.v[0], position.v[1], position.v[2]},
1035 bodyCameraFrameName,
1037 .orientationCamera =
1042 .toRotationMatrix(),
1043 bodyCameraFrameName,
1047 humanPoses.push_back(humanPose);
1051 std::lock_guard g{debugObserverMtx};
1056 humanPoseWriter.commitHumanPosesInCameraFrame(humanPoses,
getName(), timestamp);
1075 metronome.waitForNextTick();
1088 while (not pointcloudTask->isStopped())
1091 std::unique_lock signal_lock{pointcloudProcMutex};
1092 ARMARX_DEBUG <<
"Pointcloud thread waiting for signal...";
1093 pointcloudProcSignal.wait(
1094 signal_lock, [&] {
return pointcloudTask->isStopped() or depthImageReady; });
1099 depthImageReady =
false;
1100 depthImageProcessed =
false;
1105 transformation.depth_image_to_point_cloud(
1106 alignedDepthImage, K4A_CALIBRATION_TYPE_COLOR, &xyzImage);
1107 ARMARX_DEBUG <<
"Transforming depth image to point cloud took "
1117 pointcloud->width =
static_cast<uint32_t
>(xyzImage.get_width_pixels());
1118 pointcloud->height =
static_cast<uint32_t
>(xyzImage.get_height_pixels());
1122 pointcloud->is_dense =
false;
1123 pointcloud->points.resize(pointcloud->width * pointcloud->height);
1125 auto k4a_cloud_buffer =
reinterpret_cast<const int16_t*
>(xyzImage.get_buffer());
1129 unsigned char* color_buffer = resultColorImage->pixels;
1130 while (color_buffer ==
nullptr)
1132 ARMARX_WARNING <<
"color_buffer is null. This should never happen. There is "
1133 "probably a race condition somewhere that needs to be fixed. "
1134 "Temporarily we ignore this and continue.\n Timestamp: "
1136 color_buffer = resultColorImage->pixels;
1143 pointcloud->width * pointcloud->height);
1148 float max_depth = getProperty<float>(
"MaxDepth").getValue();
1149 for (
auto& p : pointcloud->points)
1151 p.r = color_buffer[
index];
1152 p.x = k4a_cloud_buffer[
index];
1156 p.g = color_buffer[
index];
1157 p.y = k4a_cloud_buffer[
index];
1161 p.b = color_buffer[
index];
1162 auto z = k4a_cloud_buffer[
index];
1166 if (z <= max_depth and z != 0)
1172 p.z = std::numeric_limits<float>::quiet_NaN();
1181 depthImageProcessed =
true;
1182 signal_lock.unlock();
1184 pointcloudProcSignal.notify_all();
1191 pointcloud->header.stamp =
static_cast<unsigned long>(
TIMESTAMP.toMicroSeconds());
1204 return "AzureKinectPointCloudProvider";
1240 using namespace Eigen;
1245 const auto convert_calibration =
1246 [](
const k4a_calibration_camera_t& k4a_calib,
float scale = 1.f)
1248 MonocularCalibration monocular_calibration;
1250 const k4a_calibration_intrinsic_parameters_t& params = k4a_calib.intrinsics.parameters;
1251 monocular_calibration.cameraParam.principalPoint = {params.param.cx * scale,
1252 params.param.cy * scale};
1253 monocular_calibration.cameraParam.focalLength = {params.param.fx * scale,
1254 params.param.fy * scale};
1267 monocular_calibration.cameraParam.distortion = {
1275 monocular_calibration.cameraParam.width =
1276 std::floor(
float(k4a_calib.resolution_width) * scale);
1277 monocular_calibration.cameraParam.height =
1278 std::floor(
float(k4a_calib.resolution_height) * scale);
1280 const Matrix3FRowMajor rotation =
1281 Map<const Matrix3FRowMajor>{k4a_calib.extrinsics.rotation};
1284 monocular_calibration.cameraParam.translation = {k4a_calib.extrinsics.translation,
1285 k4a_calib.extrinsics.translation + 3};
1287 return monocular_calibration;
1290 StereoCalibration stereo_calibration;
1292 stereo_calibration.calibrationLeft =
1293 convert_calibration(k4aCalibration.color_camera_calibration);
1296 if (enableColorUndistortion)
1298 auto& colorDistortionParams = stereo_calibration.calibrationLeft.cameraParam.distortion;
1299 std::fill(colorDistortionParams.begin(), colorDistortionParams.end(), 0);
1306 stereo_calibration.calibrationRight =
1307 convert_calibration(k4aCalibration.color_camera_calibration);
1309 auto& depthDistortionParams =
1310 stereo_calibration.calibrationRight.cameraParam.distortion;
1311 std::fill(depthDistortionParams.begin(), depthDistortionParams.end(), 0);
1314 stereo_calibration.rectificationHomographyLeft =
1316 stereo_calibration.rectificationHomographyRight =
1319 return stereo_calibration;
1325 return enableColorUndistortion;
1331 return getProperty<std::string>(
"frameName");
1334 std::vector<imrec::ChannelPreferences>
1339 imrec::ChannelPreferences rgb;
1340 rgb.requiresLossless =
false;
1343 imrec::ChannelPreferences depth;
1344 depth.requiresLossless =
true;
1345 depth.name =
"depth";
1347 return {rgb, depth};
1352 const armarx::EnableHumanPoseEstimationInput&
input,
1353 const Ice::Current&)
1355 #ifndef INCLUDE_BODY_TRACKING
1357 ARMARX_ERROR <<
"INCLUDE_BODY_TRACKING is not defined.";
1362 #ifdef INCLUDE_BODY_TRACKING
1363 if (bodyTrackingEnabled)
1366 if (not bodyTrackingIsRunning and
input.enable3d)
1368 bodyTrackingIsRunning =
true;
1369 bodyTrackingPublishTask->start();
1371 else if (bodyTrackingIsRunning and not
input.enable3d)
1373 bodyTrackingIsRunning =
false;
1374 bodyTrackingPublishTask->stop();
1379 ARMARX_ERROR <<
"Azure Kinect Body Tracking is not enabled";
1387 std::scoped_lock lock(bodyTrackingParameterMutex);
1388 bodyTrackingDepthMaskMaxZ = maxDepthInMM;
1394 const Ice::Current&)
1396 std::scoped_lock lock(bodyTrackingParameterMutex);
1397 bodyTrackingDepthMaskMinX = minXinPixel;
1398 bodyTrackingDepthMaskMaxX = maxXinPixel;
1406 const std::chrono::microseconds& k4a_device_timestamp_us,
1407 const std::chrono::nanoseconds& k4a_system_timestamp_ns)
1415 std::chrono::nanoseconds realtime_clock =
1416 std::chrono::system_clock::now().time_since_epoch();
1417 std::chrono::nanoseconds monotonic_clock =
1418 std::chrono::steady_clock::now().time_since_epoch();
1420 std::chrono::nanoseconds monotonic_to_realtime = realtime_clock - monotonic_clock;
1423 std::chrono::nanoseconds device_to_realtime =
1424 k4a_system_timestamp_ns - k4a_device_timestamp_us + monotonic_to_realtime;
1427 std::lock_guard g{deviceToRealtimeOffsetMtx};
1430 std::lock_guard g{debugObserverMtx};
1432 device_to_realtime_offset_.count() /
1436 std::abs<float>((device_to_realtime_offset_ - device_to_realtime).count()) /
1437 1
'000.f); // [ns] -> [µs]
1440 const std::int64_t timeOffsetThreshold = 1e7; // 10 ms
1442 // If we're over
a certain time off, just snap into place.
1443 if (device_to_realtime_offset_.count() == 0 ||
1444 std::abs((device_to_realtime_offset_ - device_to_realtime).count()) >
1445 timeOffsetThreshold)
1447 ARMARX_VERBOSE << deactivateSpam()
1448 <<
"Initializing or re-initializing the device to realtime offset: "
1449 << device_to_realtime.count() <<
" ns";
1450 device_to_realtime_offset_ = device_to_realtime;
1455 constexpr double alpha = 0.10;
1457 const std::chrono::nanoseconds timeCorrection(static_cast<int64_t>(
1458 std::floor(alpha * (device_to_realtime - device_to_realtime_offset_).count())));
1459 device_to_realtime_offset_ = device_to_realtime_offset_ + timeCorrection;
1462 std::lock_guard g{debugObserverMtx};
1463 setDebugObserverDatafield(
"timeCorrection [µs]",
1464 timeCorrection.count() / 1
'000); // [ns] -> [µs]
1471 AzureKinectPointCloudProvider::timestampToArmarX(
1472 const std::chrono::microseconds& k4a_timestamp_us)
1474 std::lock_guard g{deviceToRealtimeOffsetMtx};
1476 // must be initialized beforehand
1477 ARMARX_CHECK(device_to_realtime_offset_.count() != 0);
1479 std::chrono::nanoseconds timestamp_in_realtime =
1480 k4a_timestamp_us + device_to_realtime_offset_;
1482 return armarx::Duration::MicroSeconds(timestamp_in_realtime.count() /
1488 const std::chrono::microseconds& k4a_device_timestamp_us)
1490 std::lock_guard g{deviceToRealtimeOffsetMtx};
1493 std::chrono::nanoseconds realtime_clock =
1494 std::chrono::system_clock::now().time_since_epoch();
1496 device_to_realtime_offset_ = realtime_clock - k4a_device_timestamp_us;
1498 ARMARX_INFO <<
"Initializing the device to realtime offset based on wall clock: "
1499 << device_to_realtime_offset_.count() <<
" ns";
1512 std::stringstream ss;
1513 ss <<
"Invalid value (" <<
value <<
" fps) for framerate of '" << name <<
"'."
1514 <<
" Reason: The framerate has to be a divider of the property framerate.image ("
1515 << higherFramerate <<
" fps).";
1516 throw armarx::LocalException() << ss.str();
1518 skipFrames = std::round(higherFramerate /
value) - 1;
1519 if (skipFrames != 0)
1521 ARMARX_INFO_S <<
"Only publishing every " << (skipFrames + 1) <<
"'th " << name
1532 skip = skipFramesCount;
1534 skipFramesCount %= (skipFrames + 1);