11#include <IceUtil/Time.h>
13#include <opencv2/core/hal/interface.h>
14#include <opencv2/imgproc/imgproc.hpp>
34#include <Calibration/Calibration.h>
35#include <Image/ImageProcessor.h>
37#ifdef INCLUDE_BODY_TRACKING
50 k4aToIvtImage(
const k4a::image& color_image, ::CByteImage& result)
52 if (color_image.get_format() == K4A_IMAGE_FORMAT_COLOR_YUY2)
55 cv::Mat yuy2_image(color_image.get_height_pixels(),
56 color_image.get_width_pixels(),
58 const_cast<uint8_t*
>(color_image.get_buffer()));
60 cv::cvtColor(yuy2_image, rgb_image, cv::COLOR_YUV2RGB_YUY2);
65 else if (color_image.get_format() == K4A_IMAGE_FORMAT_COLOR_BGRA32)
67 auto cw =
static_cast<unsigned int>(color_image.get_width_pixels());
68 auto ch =
static_cast<unsigned int>(color_image.get_height_pixels());
72 auto color_buffer = color_image.get_buffer();
73 auto rgb_buffer_ivt = result.pixels;
81 for (
unsigned int y = 0; y < ch; ++y)
83 for (
unsigned int x = 0;
x < cw; ++
x)
85 rgb_buffer_ivt[index_ivt] = color_buffer[index_k4a + 2];
86 rgb_buffer_ivt[index_ivt + 1] = color_buffer[index_k4a + 1];
87 rgb_buffer_ivt[index_ivt + 2] = color_buffer[index_k4a + 0];
95 throw std::runtime_error(
"Unsupported color format in k4a image");
100#ifdef INCLUDE_BODY_TRACKING
102 printBodyInformation(k4abt_body_t body)
105 for (
int i = 0; i < static_cast<int>(K4ABT_JOINT_COUNT); i++)
107 const k4a_float3_t position = body.skeleton.joints[i].position;
108 const k4a_quaternion_t orientation = body.skeleton.joints[i].orientation;
109 const k4abt_joint_confidence_level_t confidence_level =
110 body.skeleton.joints[i].confidence_level;
113 <<
"Position[mm] (" << position.v[0] <<
"," << position.v[1] <<
","
114 << position.v[2] <<
"); "
115 <<
"Orientation (" << orientation.v[0] <<
"," << orientation.v[1] <<
","
116 << orientation.v[2] <<
"," << orientation.v[3] <<
"); "
117 <<
"Confidence Level " << confidence_level;
123 k4aImageFormatToString(k4a_image_format_t format)
127 case K4A_IMAGE_FORMAT_COLOR_MJPG:
129 case K4A_IMAGE_FORMAT_COLOR_NV12:
131 case K4A_IMAGE_FORMAT_COLOR_YUY2:
133 case K4A_IMAGE_FORMAT_COLOR_BGRA32:
134 return "COLOR_BGRA32";
135 case K4A_IMAGE_FORMAT_DEPTH16:
137 case K4A_IMAGE_FORMAT_IR16:
139 case K4A_IMAGE_FORMAT_CUSTOM:
151 std::function<void(armarx::Duration)>
156 std::string name =
"duration " + description +
" in [ms]";
158 std::lock_guard g{metaInfoMtx};
163 std::lock_guard g{debugObserverMtx};
174 K4A_COLOR_RESOLUTION_720P,
175 "Resolution of the RGB camera image.")
176 .map(
"0", K4A_COLOR_RESOLUTION_OFF)
177 .map(
"720", K4A_COLOR_RESOLUTION_720P)
178 .map(
"1080", K4A_COLOR_RESOLUTION_1080P)
179 .map(
"1440", K4A_COLOR_RESOLUTION_1440P)
180 .map(
"1536", K4A_COLOR_RESOLUTION_1536P)
181 .map(
"2160", K4A_COLOR_RESOLUTION_2160P)
182 .map(
"3072", K4A_COLOR_RESOLUTION_3072P);
184 K4A_DEPTH_MODE_NFOV_UNBINNED,
185 "Resolution/mode of the depth camera image.")
186 .setCaseInsensitive(
true)
187 .map(
"OFF", K4A_DEPTH_MODE_OFF)
188 .map(
"NFOV_2X2BINNED", K4A_DEPTH_MODE_NFOV_2X2BINNED)
189 .map(
"NFOV_UNBINNED", K4A_DEPTH_MODE_NFOV_UNBINNED)
190 .map(
"WFOV_2X2BINNED", K4A_DEPTH_MODE_WFOV_2X2BINNED)
191 .map(
"WFOV_UNBINNED", K4A_DEPTH_MODE_WFOV_UNBINNED)
192 .map(
"PASSIVE_IR", K4A_DEPTH_MODE_PASSIVE_IR);
196 "Max. allowed depth value in mm. Depth values above this "
197 "threshold will be set to nan.");
201 "In Milliseconds. Time offset between capturing the image on "
202 "the hardware and receiving the image in this process.",
206 K4A_IMAGE_FORMAT_COLOR_BGRA32,
207 "Color format of the RGB camera image.")
208 .map(
"BGRA", K4A_IMAGE_FORMAT_COLOR_BGRA32)
209 .map(
"YUY2", K4A_IMAGE_FORMAT_COLOR_YUY2);
219 enableColorUndistortion,
220 "EnableColorUndistortion",
221 "Undistort the color images using the full 8 radial and tangential distortion "
222 "parameters provided by the Azure Kinect.\n"
223 "This can help for processing tasks which cannot handle radial parameters k3-k6.\n"
224 "Note that this drastically reduces the FPS (to something like 3).");
225 defs->optional(externalCalibrationFilePath,
226 "ExternalCalibrationFilePath",
227 "Path to an optional external"
228 " calibration file, which has a"
229 " camera matrix and distortion"
231 defs->optional(mDeviceId,
"device_id",
"ID of the device.");
233 defs->optional(robotName,
"robotName");
234 defs->optional(bodyCameraFrameName,
"bodyCameraFrameName");
235 defs->optional(framerate.value,
237 "The framerate of RGB-D images [frames per second]."
238 "\nNote that the point cloud and body tracking frame rates are controlled by"
239 " the properties 'framerate' (point cloud) and 'framerate.bodyTracking'"
240 " (body tracking), respectively.")
244#ifdef INCLUDE_BODY_TRACKING
245 defs->optional(bodyTrackingEnabled,
246 "bodyTrackingEnabled",
247 "Whether the Azure Kinect Body Tracking SDK should be enabled or not.");
248 defs->optional(bodyTrackingRunAtStart,
249 "bodyTrackingRunAtStart",
250 "Whether the Azure Kinect Body Tracking SDK should directly run when the "
251 "component is startet."
252 "Otherwise it has to be activated by the ice interface.");
253 defs->optional(bodyTrackingModelFilename,
254 "bodyTrackingModelPath",
255 "Path where the .onnx DNN files can be found");
256 defs->optional(bodyTrackingGPUDeviceID,
"bodyTrackingGPUDeviceID",
"GPU Device ID.");
257 defs->optional(bodyTrackingTemporalSmoothingFactor,
258 "bodyTrackingTemporalSmoothingFactor",
259 "Temporal smoothing factor for Azure Kinect body tracking.");
260 defs->optional(useCPU,
"useCPU",
"Whether to use cpu");
262 defs->optional(bodyTrackingDepthMaskMinX,
"bodyTrackingDepthMaskMinX");
263 defs->optional(bodyTrackingDepthMaskMaxX,
"bodyTrackingDepthMaskMaxX");
264 defs->optional(bodyTrackingDepthMaskMaxZ,
"bodyTrackingDepthMaskMaxZ");
265 defs->optional(framerate.bodyTracking.value,
266 "framerate.bodyTracking",
267 "The framerate with with the body tracking is run [frames per second]."
268 "\nNote that the RGB-D image and point cloud frame rates are controlled by"
269 " the properties 'framerate.image' (RGB-D images) and 'framerate'"
270 " (point cloud), respectively.")
274 defs->optional(startIMU,
"startIMU");
276 humanPoseWriter.registerPropertyDefinitions(defs);
279 defs->optional(enableHeartbeat,
"enableHeartbeat");
287 config = K4A_DEVICE_CONFIG_INIT_DISABLE_ALL;
288 if (framerate.value == 5)
290 config.camera_fps = K4A_FRAMES_PER_SECOND_5;
292 else if (framerate.value == 15)
294 config.camera_fps = K4A_FRAMES_PER_SECOND_15;
296 else if (framerate.value == 30)
298 config.camera_fps = K4A_FRAMES_PER_SECOND_30;
302 throw armarx::LocalException(
"Invalid image framerate (property 'framerate.images'): ")
303 << framerate.value <<
". Only framerates 5, 15 and 30 are "
304 <<
"supported by Azure Kinect.";
307 framerate.pointCloud.value =
309 framerate.pointCloud.update(framerate.value);
311#ifdef INCLUDE_BODY_TRACKING
312 framerate.bodyTracking.update(framerate.value);
321 config.synchronized_images_only =
true;
329 ARMARX_INFO <<
"Depth image size: " << depth_dim.first <<
"x" << depth_dim.second;
330 ARMARX_INFO <<
"Color image size: " << color_dim.first <<
"x" << color_dim.second;
333 k4a::image::create(K4A_IMAGE_FORMAT_DEPTH16,
336 color_dim.first * 2 *
static_cast<int32_t
>(
sizeof(uint8_t)));
338 setImageFormat(visionx::ImageDimension(color_dim.first, color_dim.second),
340 visionx::eBayerPatternGr);
344 std::make_unique<CByteImage>(color_dim.first, color_dim.second, CByteImage::eRGB24);
346 xyzImage = k4a::image::create(K4A_IMAGE_FORMAT_CUSTOM,
349 color_dim.first * 3 *
static_cast<int32_t
>(
sizeof(int16_t)));
351 pointcloud = std::make_unique<pcl::PointCloud<CloudPointType>>();
365#ifdef INCLUDE_BODY_TRACKING
366 if (bodyTrackingEnabled)
369 humanPoseWriter.connect(memoryNameSystem());
374 &AzureKinectPointCloudProvider::runPublishBodyTrackingResults);
383 {
"Vision",
"Camera"},
384 "AzureKinectPointCloudProvider");
398 std::lock_guard<std::mutex> lock(pointcloudProcMutex);
399 ARMARX_DEBUG <<
"Stopping pointcloud processing thread...";
400 const bool WAIT_FOR_JOIN =
false;
401 pointcloudTask->stop(WAIT_FOR_JOIN);
402 pointcloudProcSignal.notify_all();
403 ARMARX_DEBUG <<
"Waiting for pointcloud processing thread to stop...";
404 pointcloudTask->waitForStop();
408#ifdef INCLUDE_BODY_TRACKING
409 if (bodyTrackingIsRunning)
411 bodyTrackingPublishTask->stop();
441 const uint32_t DEVICE_COUNT = k4a::device::get_installed_count();
442 if (DEVICE_COUNT == 0)
445 throw armarx::LocalException(
"No Azure Kinect devices detected!");
450 device = k4a::device::open(
static_cast<uint32_t
>(mDeviceId));
451 ARMARX_DEBUG <<
"Opened device id #" << mDeviceId <<
" with serial number "
452 << device.get_serialnum() <<
".";
461 k4aCalibration = device.get_calibration(config.depth_mode, config.color_resolution);
463 <<
"Color camera calibration:"
465 <<
"cx: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.cx
467 <<
"cy: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.cy
469 <<
"fx: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.fx
471 <<
"fy: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.fy
473 <<
"k1: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k1
475 <<
"k2: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k2
477 <<
"p1: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.p1
479 <<
"p2: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.p2
481 <<
"k3: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k3
483 <<
"k4: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k4
485 <<
"k5: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k5
487 <<
"k6: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k6;
492 c_left->PrintCameraParameters();
495 c_right->PrintCameraParameters();
502 k4a_transformation_t transformationHandle = k4a_transformation_create(&k4aCalibration);
504 <<
"Failed to create the Azure Kinect transformation. The most common cause is that "
505 "the depth engine plugin 'libdepthengine.so.2.0' could not be loaded. Make sure "
506 "it is on LD_LIBRARY_PATH (axii module 'deps/azure-kinect/depth-engine').";
507 transformation = k4a::transformation(transformationHandle);
509#ifdef INCLUDE_BODY_TRACKING
510 if (bodyTrackingEnabled)
516 bodyTrackingModelFilename);
517 ARMARX_CHECK(found) <<
"Body tracking DNN model could not be found/resolved at `"
518 << bodyTrackingModelFilename <<
"`.";
520 ARMARX_INFO <<
"Using body tracking DNN model from directory `"
521 << bodyTrackingModelFilename <<
"`.";
523 ARMARX_CHECK(std::filesystem::exists(bodyTrackingModelFilename))
524 <<
"The path `" << bodyTrackingModelFilename <<
"` does not exist!";
526 k4abt_tracker_configuration_t
const bodyTrackingConfig{
527 .sensor_orientation = K4ABT_SENSOR_ORIENTATION_DEFAULT,
528 .processing_mode = useCPU ? K4ABT_TRACKER_PROCESSING_MODE_CPU
529 : K4ABT_TRACKER_PROCESSING_MODE_GPU_CUDA,
530 .gpu_device_id = bodyTrackingGPUDeviceID,
531 .model_path = bodyTrackingModelFilename.c_str()};
533 bodyTracker = k4abt::tracker::create(k4aCalibration, bodyTrackingConfig);
534 bodyTracker.set_temporal_smoothing(bodyTrackingTemporalSmoothingFactor);
536 if (bodyTrackingRunAtStart and not bodyTrackingIsRunning)
538 bodyTrackingIsRunning =
true;
539 bodyTrackingPublishTask->start();
545 device.set_color_control(K4A_COLOR_CONTROL_BRIGHTNESS, K4A_COLOR_CONTROL_MODE_MANUAL, 128);
547 device.start_cameras(&config);
574 const k4a_calibration_camera_t calibration{k4aCalibration.color_camera_calibration};
575 const k4a_calibration_intrinsic_parameters_t::_param param =
576 calibration.intrinsics.parameters.param;
583 cv::Mat1f camera_matrix(3, 3);
584 camera_matrix << param.fx, 0, param.cx, 0, param.fy, param.cy, 0, 0, 1;
585 cv::Mat1f new_camera_matrix(3, 3);
586 new_camera_matrix << param.fx, 0, param.cx, 0, param.fx, param.cy, 0, 0, 1;
587 cv::Mat1f distortion_coeff(1, 8);
588 distortion_coeff << param.k1, param.k2, param.p1, param.p2, param.k3, param.k4,
590 cv::Mat map1, map2, map3;
591 cv::initUndistortRectifyMap(
596 cv::Size{calibration.resolution_width, calibration.resolution_height},
600 cv::convertMaps(map1, map2, colorDistortionMap, map3, CV_16SC2,
true);
608 std::lock_guard<std::mutex> lock(pointcloudProcMutex);
609 pointcloudTask->stop(
false);
610 pointcloudProcSignal.notify_all();
614 pointcloudTask->waitForStop();
617 std::lock_guard<std::mutex> lock(pointcloudProcMutex);
618 depthImageReady =
false;
619 depthImageProcessed =
false;
625 pointcloudTask->start();
646 const std::chrono::milliseconds TIMEOUT{1000};
648 StopWatch sw_get_capture;
653 status = device.get_capture(&
capture, TIMEOUT);
656 catch (
const std::exception&)
658 ARMARX_WARNING <<
"Failed to get capture from device (#" << ++mDiagnostics.num_crashes
659 <<
"). Restarting camera.";
661 device.stop_cameras();
662 device.start_cameras(&config);
663 ARMARX_INFO <<
"Restarting took " << sw.stop() <<
".";
672 std::lock_guard g{metaInfoMtx};
677 const bool mustInitializeTimestampOffset = [&]()
679 std::lock_guard g{deviceToRealtimeOffsetMtx};
680 return device_to_realtime_offset_.count() == 0;
682 if (mustInitializeTimestampOffset)
689 capture.get_ir_image().get_system_timestamp());
691 const k4a::image DEPTH_IMAGE =
capture.get_depth_image();
695 const k4a_image_format_t IMAGE_FORMAT = DEPTH_IMAGE.get_format();
696 if (IMAGE_FORMAT != K4A_IMAGE_FORMAT_DEPTH16 && IMAGE_FORMAT != K4A_IMAGE_FORMAT_IR16)
698 const char* format_str = ::k4aImageFormatToString(IMAGE_FORMAT);
699 std::stringstream error_msg;
700 error_msg <<
"Attempted to colorize a non-depth image with format: " << format_str;
701 throw std::logic_error(error_msg.str());
705 if (not framerate.pointCloud.skip())
711 std::lock_guard lock{pointcloudProcMutex};
712 transformation.depth_image_to_color_camera(DEPTH_IMAGE, &alignedDepthImage);
715 depthImageReady =
true;
719 pointcloudProcSignal.notify_one();
722#ifdef INCLUDE_BODY_TRACKING
723 if (bodyTrackingIsRunning)
726 std::scoped_lock lock(bodyTrackingParameterMutex);
728 if (not framerate.bodyTracking.skip())
730 k4a::image ir_image =
capture.get_ir_image();
731 std::uint8_t* ir_image_buffer = ir_image.get_buffer();
733 k4a::image depth_image =
capture.get_depth_image();
734 std::uint8_t* depth_image_buffer = depth_image.get_buffer();
738 depth_image.get_height_pixels());
740 const int stride = ir_image.get_stride_bytes() / ir_image.get_width_pixels();
742 for (
int x = 0;
x < ir_image.get_width_pixels();
x++)
744 for (
int y = 0; y < ir_image.get_height_pixels(); y++)
746 const int i = (y * ir_image.get_width_pixels() * stride) + (
x * stride);
747 const int z = (
static_cast<int>(depth_image_buffer[i])) +
748 (
static_cast<int>(depth_image_buffer[i + 1]) << 8);
750 if ((bodyTrackingDepthMaskMinX > 0 and
x < bodyTrackingDepthMaskMinX) or
751 (bodyTrackingDepthMaskMaxX > 0 and
x > bodyTrackingDepthMaskMaxX) or
752 (bodyTrackingDepthMaskMaxZ > 0 and z > bodyTrackingDepthMaskMaxZ))
754 ir_image_buffer[i] = std::numeric_limits<std::uint8_t>::max();
755 ir_image_buffer[i + 1] = std::numeric_limits<std::uint8_t>::max();
756 depth_image_buffer[i] = std::numeric_limits<std::uint8_t>::max();
757 depth_image_buffer[i + 1] =
758 std::numeric_limits<std::uint8_t>::max();
763 if (not bodyTracker.enqueue_capture(
capture))
771 const k4a::image COLOR_IMAGE =
capture.get_color_image();
774 auto real_time = IceUtil::Time::now();
775 auto monotonic_time = IceUtil::Time::now(IceUtil::Time::Monotonic);
776 auto clock_diff = real_time - monotonic_time;
778 auto image_monotonic_time =
779 IceUtil::Time::microSeconds(std::chrono::duration_cast<std::chrono::microseconds>(
780 DEPTH_IMAGE.get_system_timestamp())
784 imagesTime = image_monotonic_time + clock_diff - IceUtil::Time::microSeconds(offset);
787 std::lock_guard g{metaInfoMtx};
793 std::lock_guard g{debugObserverMtx};
795 (real_time - imagesTime).toMilliSecondsDouble());
798 if (enableColorUndistortion)
801 cv::Mat tmp_rgb_image;
803 if (COLOR_IMAGE.get_format() == K4A_IMAGE_FORMAT_COLOR_YUY2)
806 cv::Mat yuy2_image(COLOR_IMAGE.get_height_pixels(),
807 COLOR_IMAGE.get_width_pixels(),
809 const_cast<uint8_t*
>(COLOR_IMAGE.get_buffer()));
811 cv::cvtColor(yuy2_image, tmp_rgb_image, cv::COLOR_YUV2RGB_YUY2);
813 else if (COLOR_IMAGE.get_format() == K4A_IMAGE_FORMAT_COLOR_BGRA32)
815 cv::cvtColor(cv::Mat{cv::Size{COLOR_IMAGE.get_width_pixels(),
816 COLOR_IMAGE.get_height_pixels()},
818 (
void*)COLOR_IMAGE.get_buffer(),
825 throw std::runtime_error(
"Unsupported color format in k4a image");
828 cv::Mat cv_color_image_undistorted(COLOR_IMAGE.get_width_pixels(),
829 COLOR_IMAGE.get_height_pixels(),
832 cv::remap(tmp_rgb_image,
833 cv_color_image_undistorted,
837 cv::BORDER_CONSTANT);
845 ::k4aToIvtImage(COLOR_IMAGE, *resultColorImage);
853 const int DW = alignedDepthImage.get_width_pixels();
854 const int DH = alignedDepthImage.get_height_pixels();
860 auto result_depth_buffer = resultDepthImage->pixels;
861 const auto* depth_buffer =
862 reinterpret_cast<const uint16_t*
>(alignedDepthImage.get_buffer());
866 for (
int y = 0; y < DH; ++y)
868 for (
int x = 0;
x < DW; ++
x)
870 uint16_t depth_value = depth_buffer[index_2];
872 result_depth_buffer[
index],
873 result_depth_buffer[
index + 1],
874 result_depth_buffer[
index + 2]);
884 CByteImage* images[2] = {resultColorImage.get(), resultDepthImage.get()};
890 std::unique_lock signal_lock{pointcloudProcMutex};
891 ARMARX_DEBUG <<
"Capturing thread waiting for signal...";
895 const std::chrono::milliseconds POINTCLOUD_TIMEOUT{1000};
896 if (not pointcloudProcSignal.wait_for(
897 signal_lock, POINTCLOUD_TIMEOUT, [&] { return depthImageProcessed; }))
900 <<
"Point cloud processing did not signal completion within "
901 << POINTCLOUD_TIMEOUT.count()
902 <<
" ms. Continuing capture without it.";
908 std::lock_guard g{debugObserverMtx};
914 heartbeatPlugin->heartbeat();
924 std::lock_guard g{debugObserverMtx};
935 Eigen::Vector3f accMean = Eigen::Vector3f::Zero();
938 k4a_imu_sample_t imu_sample;
939 while (device.get_imu_sample(&imu_sample, std::chrono::milliseconds(0)))
941 const Eigen::Vector3f acceleration{imu_sample.acc_sample.xyz.x,
942 imu_sample.acc_sample.xyz.y,
943 imu_sample.acc_sample.xyz.z};
945 accMean += acceleration;
949 accMean /=
static_cast<float>(cnt);
954 std::lock_guard g{debugObserverMtx};
963 catch (
const std::exception&)
966 << ++mDiagnostics.num_crashes <<
").";
973#ifdef INCLUDE_BODY_TRACKING
975 AzureKinectPointCloudProvider::runPublishBodyTrackingResults()
978 while (bodyTrackingIsRunning)
982 const k4abt::frame body_frame = [&]() -> k4abt::frame
986 auto result = bodyTracker.pop_result();
997 if (body_frame !=
nullptr)
999 armarx::core::time::ScopedStopWatch sw{
1006 auto real_time = IceUtil::Time::now();
1007 auto monotonic_time = IceUtil::Time::now(IceUtil::Time::Monotonic);
1008 auto clock_diff = real_time - monotonic_time;
1010 auto image_monotonic_time = IceUtil::Time::microSeconds(
1011 std::chrono::duration_cast<std::chrono::microseconds>(
1012 body_frame.get_system_timestamp())
1017 IceUtil::Time imageTime = image_monotonic_time + clock_diff;
1020 std::lock_guard g{debugObserverMtx};
1022 imageTime.toMicroSeconds());
1027 const armarx::Clock realtimeClock = armarx::Clock(armarx::ClockType::Realtime);
1028 const armarx::Clock monotonicClock =
1029 armarx::Clock(armarx::ClockType::Monotonic);
1031 auto real_time = realtimeClock.
now();
1032 auto monotonic_time = monotonicClock.
now();
1033 auto clock_diff = real_time - monotonic_time;
1036 std::chrono::duration_cast<std::chrono::microseconds>(
1037 body_frame.get_system_timestamp())
1042 auto imageTime = image_monotonic_time + clock_diff;
1044 armarx::DateTime imageTimestamp = armarx::DateTime(imageTime);
1047 std::lock_guard g{debugObserverMtx};
1049 imageTime.toMicroSeconds());
1054 std::uint32_t num_bodies = body_frame.get_num_bodies();
1056 std::lock_guard g{debugObserverMtx};
1060 std::vector<armarx::armem::human::HumanPose> humanPoses;
1061 humanPoses.reserve(num_bodies);
1063 for (std::uint32_t i = 0; i < num_bodies; i++)
1065 k4abt_body_t body = body_frame.get_body(i);
1066 printBodyInformation(body);
1068 armarx::armem::human::HumanPose humanPose;
1073 for (
int i = 0; i < static_cast<int>(K4ABT_JOINT_COUNT); i++)
1080 k4a_float3_t position = body.skeleton.joints[i].position;
1081 k4a_quaternion_t orientation = body.skeleton.joints[i].orientation;
1082 k4abt_joint_confidence_level_t confidence_level =
1083 body.skeleton.joints[i].confidence_level;
1085 humanPose.
keypoints[name] = armarx::armem::human::PoseKeypoint{
1087 .confidence =
static_cast<float>(
static_cast<int>(confidence_level)),
1088 .positionCamera = armarx::FramedPosition(
1089 Eigen::Vector3f{position.v[0], position.v[1], position.v[2]},
1090 bodyCameraFrameName,
1092 .orientationCamera =
1097 .toRotationMatrix(),
1098 bodyCameraFrameName,
1102 humanPoses.push_back(humanPose);
1106 std::lock_guard g{debugObserverMtx};
1115 humanPoseWriter.commitHumanPosesInCameraFrame(humanPoses,
getName(),
timestamp);
1135 metronome.waitForNextTick();
1148 while (not pointcloudTask->isStopped())
1151 std::unique_lock signal_lock{pointcloudProcMutex};
1152 ARMARX_DEBUG <<
"Pointcloud thread waiting for signal...";
1153 pointcloudProcSignal.wait(signal_lock,
1155 {
return pointcloudTask->isStopped() or depthImageReady; });
1158 if (pointcloudTask->isStopped())
1164 const IceUtil::Time TIMESTAMP = imagesTime;
1165 depthImageReady =
false;
1166 depthImageProcessed =
false;
1171 bool pointcloudValid =
true;
1178 transformation.depth_image_to_point_cloud(
1179 alignedDepthImage, K4A_CALIBRATION_TYPE_COLOR, &xyzImage);
1180 ARMARX_DEBUG <<
"Transforming depth image to point cloud took "
1191 pointcloud->width =
static_cast<uint32_t
>(xyzImage.get_width_pixels());
1192 pointcloud->height =
static_cast<uint32_t
>(xyzImage.get_height_pixels());
1196 pointcloud->is_dense =
false;
1197 pointcloud->points.resize(pointcloud->width * pointcloud->height);
1199 auto k4a_cloud_buffer =
reinterpret_cast<const int16_t*
>(xyzImage.get_buffer());
1203 unsigned char* color_buffer = resultColorImage->pixels;
1204 while (color_buffer ==
nullptr)
1207 <<
"color_buffer is null. This should never happen. There is "
1208 "probably a race condition somewhere that needs to be fixed. "
1209 "Temporarily we ignore this and continue.\n Timestamp: "
1211 color_buffer = resultColorImage->pixels;
1218 pointcloud->width * pointcloud->height);
1224 for (
auto& p : pointcloud->points)
1226 p.r = color_buffer[
index];
1227 p.x = k4a_cloud_buffer[
index];
1231 p.g = color_buffer[
index];
1232 p.y = k4a_cloud_buffer[
index];
1236 p.b = color_buffer[
index];
1237 auto z = k4a_cloud_buffer[
index];
1241 if (z <= max_depth and z != 0)
1247 p.z = std::numeric_limits<float>::quiet_NaN();
1256 catch (
const std::exception& e)
1258 pointcloudValid =
false;
1260 <<
"Failed to process depth image into a point cloud: " << e.what()
1261 <<
"\nSkipping this frame.";
1266 depthImageProcessed =
true;
1267 signal_lock.unlock();
1269 pointcloudProcSignal.notify_all();
1274 if (pointcloudValid)
1277 pointcloud->header.stamp =
static_cast<unsigned long>(TIMESTAMP.toMicroSeconds());
1282 catch (
const std::exception& e)
1285 <<
"Failed to broadcast point cloud: " << e.what();
1304 return "AzureKinectPointCloudProvider";
1340 using namespace Eigen;
1345 const auto convert_calibration =
1346 [](
const k4a_calibration_camera_t& k4a_calib,
float scale = 1.f)
1348 MonocularCalibration monocular_calibration;
1350 const k4a_calibration_intrinsic_parameters_t& params = k4a_calib.intrinsics.parameters;
1351 monocular_calibration.cameraParam.principalPoint = {params.param.cx * scale,
1352 params.param.cy * scale};
1353 monocular_calibration.cameraParam.focalLength = {params.param.fx * scale,
1354 params.param.fy * scale};
1367 monocular_calibration.cameraParam.distortion = {
1375 monocular_calibration.cameraParam.width =
1376 std::floor(
float(k4a_calib.resolution_width) * scale);
1377 monocular_calibration.cameraParam.height =
1378 std::floor(
float(k4a_calib.resolution_height) * scale);
1380 const Matrix3FRowMajor rotation =
1381 Map<const Matrix3FRowMajor>{k4a_calib.extrinsics.rotation};
1383 monocular_calibration.cameraParam.rotation = convertEigenMatToVisionX(rotation);
1384 monocular_calibration.cameraParam.translation = {k4a_calib.extrinsics.translation,
1385 k4a_calib.extrinsics.translation + 3};
1387 return monocular_calibration;
1390 StereoCalibration stereo_calibration;
1392 stereo_calibration.calibrationLeft =
1393 convert_calibration(k4aCalibration.color_camera_calibration);
1396 if (enableColorUndistortion)
1398 auto& colorDistortionParams = stereo_calibration.calibrationLeft.cameraParam.distortion;
1399 std::fill(colorDistortionParams.begin(), colorDistortionParams.end(), 0);
1406 stereo_calibration.calibrationRight =
1407 convert_calibration(k4aCalibration.color_camera_calibration);
1409 auto& depthDistortionParams =
1410 stereo_calibration.calibrationRight.cameraParam.distortion;
1411 std::fill(depthDistortionParams.begin(), depthDistortionParams.end(), 0);
1414 stereo_calibration.rectificationHomographyLeft =
1415 convertEigenMatToVisionX(Matrix3f::Identity());
1416 stereo_calibration.rectificationHomographyRight =
1417 convertEigenMatToVisionX(Matrix3f::Identity());
1419 return stereo_calibration;
1425 return enableColorUndistortion;
1434 std::vector<imrec::ChannelPreferences>
1439 imrec::ChannelPreferences rgb;
1440 rgb.requiresLossless =
false;
1443 imrec::ChannelPreferences depth;
1444 depth.requiresLossless =
true;
1445 depth.name =
"depth";
1447 return {rgb, depth};
1452 const armarx::EnableHumanPoseEstimationInput& input,
1453 const Ice::Current&)
1455#ifndef INCLUDE_BODY_TRACKING
1457 ARMARX_ERROR <<
"INCLUDE_BODY_TRACKING is not defined.";
1462#ifdef INCLUDE_BODY_TRACKING
1463 if (bodyTrackingEnabled)
1466 if (not bodyTrackingIsRunning and input.enable3d)
1468 bodyTrackingIsRunning =
true;
1469 bodyTrackingPublishTask->start();
1471 else if (bodyTrackingIsRunning and not input.enable3d)
1473 bodyTrackingIsRunning =
false;
1474 bodyTrackingPublishTask->stop();
1479 ARMARX_ERROR <<
"Azure Kinect Body Tracking is not enabled";
1487 std::scoped_lock lock(bodyTrackingParameterMutex);
1488 bodyTrackingDepthMaskMaxZ = maxDepthInMM;
1494 const Ice::Current&)
1496 std::scoped_lock lock(bodyTrackingParameterMutex);
1497 bodyTrackingDepthMaskMinX = minXinPixel;
1498 bodyTrackingDepthMaskMaxX = maxXinPixel;
1506 const std::chrono::microseconds& k4a_device_timestamp_us,
1507 const std::chrono::nanoseconds& k4a_system_timestamp_ns)
1515 std::chrono::nanoseconds realtime_clock =
1516 std::chrono::system_clock::now().time_since_epoch();
1517 std::chrono::nanoseconds monotonic_clock =
1518 std::chrono::steady_clock::now().time_since_epoch();
1520 std::chrono::nanoseconds monotonic_to_realtime = realtime_clock - monotonic_clock;
1523 std::chrono::nanoseconds device_to_realtime =
1524 k4a_system_timestamp_ns - k4a_device_timestamp_us + monotonic_to_realtime;
1527 std::lock_guard g{deviceToRealtimeOffsetMtx};
1530 std::lock_guard g{debugObserverMtx};
1532 device_to_realtime_offset_.count() /
1536 std::abs<float>((device_to_realtime_offset_ - device_to_realtime).count()) /
1540 const std::int64_t timeOffsetThreshold = 1e7;
1543 if (device_to_realtime_offset_.count() == 0 ||
1544 std::abs((device_to_realtime_offset_ - device_to_realtime).count()) >
1545 timeOffsetThreshold)
1548 <<
"Initializing or re-initializing the device to realtime offset: "
1549 << device_to_realtime.count() <<
" ns";
1550 device_to_realtime_offset_ = device_to_realtime;
1555 constexpr double alpha = 0.10;
1557 const std::chrono::nanoseconds timeCorrection(
static_cast<int64_t
>(
1558 std::floor(alpha * (device_to_realtime - device_to_realtime_offset_).count())));
1559 device_to_realtime_offset_ = device_to_realtime_offset_ + timeCorrection;
1562 std::lock_guard g{debugObserverMtx};
1564 timeCorrection.count() / 1'000);
1572 const std::chrono::microseconds& k4a_timestamp_us)
1574 std::lock_guard g{deviceToRealtimeOffsetMtx};
1579 std::chrono::nanoseconds timestamp_in_realtime =
1580 k4a_timestamp_us + device_to_realtime_offset_;
1588 const std::chrono::microseconds& k4a_device_timestamp_us)
1590 std::lock_guard g{deviceToRealtimeOffsetMtx};
1593 std::chrono::nanoseconds realtime_clock =
1594 std::chrono::system_clock::now().time_since_epoch();
1596 device_to_realtime_offset_ = realtime_clock - k4a_device_timestamp_us;
1598 ARMARX_INFO <<
"Initializing the device to realtime offset based on wall clock: "
1599 << device_to_realtime_offset_.count() <<
" ns";
1610 if (std::abs(std::fmod(higherFramerate,
value)) > 1e-6)
1612 std::stringstream ss;
1613 ss <<
"Invalid value (" <<
value <<
" fps) for framerate of '" <<
name <<
"'."
1614 <<
" Reason: The framerate has to be a divider of the property framerate.image ("
1615 << higherFramerate <<
" fps).";
1616 throw armarx::LocalException() << ss.str();
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Eigen::Matrix< T, 3, 3 > Matrix
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
static bool ReplaceEnvVars(std::string &string)
ReplaceEnvVars replaces environment variables in a string with their values, if the env.
static DateTime Now()
Current time on the virtual clock.
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Property< PropertyType > getProperty(const std::string &name)
void setDebugObserverDatafield(Ts &&... ts) const
void sendDebugObserverBatch()
void setDebugObserverBatchModeEnabled(bool enable)
static Duration MicroSeconds(std::int64_t microSeconds)
Constructs a duration in microseconds.
static Duration MilliSeconds(std::int64_t milliSeconds)
Constructs a duration in milliseconds.
static Frequency Hertz(std::int64_t hertz)
SpamFilterDataPtr deactivateSpam(float deactivationDurationSec=10.0f, const std::string &identifier="", bool deactivate=true) const
disables the logging for the current line for the given amount of seconds.
virtual void onExitComponent()
Hook for subclass.
virtual void onDisconnectComponent()
Hook for subclass.
PluginT * addPlugin(const std::string prefix="", ParamsT &&... params)
virtual void onConnectComponent()=0
Pure virtual hook for the subclass.
std::string getName() const
Retrieve name of object.
virtual void onInitComponent()=0
Pure virtual hook for the subclass.
void setMetaInfo(const std::string &id, const VariantBasePtr &value)
Allows to set meta information that can be queried live via Ice interface on the ArmarXManager.
ArmarXManagerPtr getArmarXManager() const
Returns the ArmarX manager used to add and remove components.
std::string prefix
Prefix of the properties such as namespace, domain, component name, etc.
PropertyDefinition< PropertyType > & defineOptionalProperty(const std::string &name, PropertyType defaultValue, const std::string &description="", PropertyDefinitionBase::PropertyConstness constness=PropertyDefinitionBase::eConstant)
IceUtil::Handle< RunningTask< T > > pointer_type
Shared pointer type for convenience.
static IceUtil::Time GetTime(TimeMode timeMode=TimeMode::VirtualTime)
Get the current time.
static IceUtil::Time GetTimeSince(IceUtil::Time referenceTime, TimeMode timeMode=TimeMode::VirtualTime)
Get the difference between the current time and a reference time.
The Variant class is described here: Variants.
DateTime now() const
Current date/time of the clock.
Represents a point in time.
std::int64_t toMilliSecondsSinceEpoch() const
Simple rate limiter for use in loops to maintain a certain frequency given a clock.
Measures the time this stop watch was inside the current scope.
Measures the passed time between the construction or calling reset() and stop().
AzureKinectPointCloudProviderPropertyDefinitions(std::string prefix)
Brief description of class AzureKinectPointCloudProvider.
void onInitComponent() override
Pure virtual hook for the subclass.
void setMaxDepthBodyTracking(int maxDepthInMM, const Ice::Current &=Ice::emptyCurrent) override
armarx::DateTime timestampToArmarX(const std::chrono::microseconds &k4a_timestamp_us)
bool doCapture() override
Main capturing function.
void enableHumanPoseEstimation(const armarx::EnableHumanPoseEstimationInput &input, const Ice::Current &=Ice::emptyCurrent) override
void onExitCapturingPointCloudProvider() override
This is called when the Component::onExitComponent() setup is called.
void onDisconnectComponent() override
Hook for subclass.
static std::pair< int, int > GetDepthDimensions(const k4a_depth_mode_t depth_mode)
Returns the dimension of the depth images that will be produced for a certain resolution.
void onStartCapture(float frames_per_second) override
This is called when the point cloud provider capturing has been started.
static std::pair< int, int > GetColorDimensions(const k4a_color_resolution_t resolution)
Returns the dimension of the color images that will be produced for a certain resolution.
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void runPointcloudPublishing()
void onConnectImageProvider() override
This is called when the Component::onConnectComponent() setup is called.
visionx::StereoCalibration getStereoCalibration(const Ice::Current &c) override
std::string getReferenceFrame(const Ice::Current &c) override
static std::string VersionToString(const k4a_version_t &version)
Creates a string from a k4a_version_t.
std::function< void(armarx::Duration)> createSwCallback(const std::string &description)
void onInitCapturingPointCloudProvider() override
This is called when the Component::onInitComponent() is called.
void onConnectComponent() override
Pure virtual hook for the subclass.
void onStopCapture() override
This is called when the point cloud provider capturing has been stopped.
static std::string GetDefaultName()
bool getImagesAreUndistorted(const ::Ice::Current &c) override
std::vector< imrec::ChannelPreferences > getImageRecordingChannelPreferences(const Ice::Current &) override
void onInitImageProvider() override
This is called when the Component::onInitComponent() is called.
void setWidthBodyTracking(int minXinPixel, int maxXinPixel, const Ice::Current &=Ice::emptyCurrent) override
void onExitComponent() override
Hook for subclass.
void initializeTimestampOffset(const std::chrono::microseconds &k4a_device_timestamp_us)
void onDisconnectImageProvider() override
AzureKinectPointCloudProvider()
std::string getDefaultName() const override
void updateTimestampOffset(const std::chrono::microseconds &k4a_device_timestamp_us, const std::chrono::nanoseconds &k4a_system_timestamp_ns)
CapturingPointCloudProviderPropertyDefinitions(std::string prefix)
std::atomic_bool captureEnabled
Indicates that capturing is enabled and running.
void setPointCloudSyncMode(ImageSyncMode pointCloudSyncMode)
Sets the point cloud synchronization mode.
void onInitComponent() override
void onDisconnectComponent() override
Hook for subclass.
ImageFormatInfo getImageFormat(const Ice::Current &c=Ice::emptyCurrent) override
Returns the entire image format info struct via Ice.
void setImageFormat(ImageDimension imageDimension, ImageType imageType, BayerPatternType bayerPatternType=visionx::eBayerPatternRg)
Sets the image basic format data.
void provideImages(void **inputBuffers, const IceUtil::Time &imageTimestamp=IceUtil::Time())
send images raw.
void onConnectComponent() override
void setNumberImages(int numberImages)
Sets the number of images on each capture.
void onExitComponent() override
MetaPointCloudFormatPtr getPointCloudFormat(const Ice::Current &c=Ice::emptyCurrent) override
Returns the point cloud format info struct via Ice.
void providePointCloud(PointCloudPtrT pointCloudPtr)
offer the new point cloud.
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#define ARMARX_INFO
The normal logging level.
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
#define ARMARX_VERBOSE
The logging level for verbose information.
Quaternion< float, 0 > Quaternionf
const std::string ModelId
const simox::meta::EnumNames< Joints > JointNames
Names of the joints as defined in the body model.
Joints
Joints with index as defined in the body model.
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::string GetHandledExceptionString()
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
void convert(const CByteImage &in, cv::Mat &out)
Converts an IVT CByteImage to OpenCV's BGR Mat.
void convert_rgb2cbi(const cv::Mat &in, CByteImage &out)
Converts an OpenCV RGB Mat to IVT's CByteImage.
std::optional< std::string > humanTrackingId
std::string cameraFrameName
unsigned int skipFramesCount
void update(int higherFramerate)