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 pointcloudTask->start();
367#ifdef INCLUDE_BODY_TRACKING
368 if (bodyTrackingEnabled)
371 humanPoseWriter.connect(memoryNameSystem());
376 &AzureKinectPointCloudProvider::runPublishBodyTrackingResults);
385 {
"Vision",
"Camera"},
386 "AzureKinectPointCloudProvider");
399 std::lock_guard<std::mutex> lock(pointcloudProcMutex);
400 ARMARX_DEBUG <<
"Stopping pointcloud processing thread...";
401 const bool WAIT_FOR_JOIN =
false;
402 pointcloudTask->stop(WAIT_FOR_JOIN);
403 pointcloudProcSignal.notify_all();
404 ARMARX_DEBUG <<
"Waiting for pointcloud processing thread to stop...";
405 pointcloudTask->waitForStop();
409#ifdef INCLUDE_BODY_TRACKING
410 if (bodyTrackingIsRunning)
412 bodyTrackingPublishTask->stop();
442 const uint32_t DEVICE_COUNT = k4a::device::get_installed_count();
443 if (DEVICE_COUNT == 0)
446 throw armarx::LocalException(
"No Azure Kinect devices detected!");
451 device = k4a::device::open(
static_cast<uint32_t
>(mDeviceId));
452 ARMARX_DEBUG <<
"Opened device id #" << mDeviceId <<
" with serial number "
453 << device.get_serialnum() <<
".";
462 k4aCalibration = device.get_calibration(config.depth_mode, config.color_resolution);
464 <<
"Color camera calibration:"
466 <<
"cx: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.cx
468 <<
"cy: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.cy
470 <<
"fx: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.fx
472 <<
"fy: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.fy
474 <<
"k1: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k1
476 <<
"k2: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k2
478 <<
"p1: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.p1
480 <<
"p2: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.p2
482 <<
"k3: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k3
484 <<
"k4: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k4
486 <<
"k5: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k5
488 <<
"k6: " << k4aCalibration.color_camera_calibration.intrinsics.parameters.param.k6;
493 c_left->PrintCameraParameters();
496 c_right->PrintCameraParameters();
499 transformation = k4a::transformation(k4aCalibration);
501#ifdef INCLUDE_BODY_TRACKING
502 if (bodyTrackingEnabled)
508 bodyTrackingModelFilename);
509 ARMARX_CHECK(found) <<
"Body tracking DNN model could not be found/resolved at `"
510 << bodyTrackingModelFilename <<
"`.";
512 ARMARX_INFO <<
"Using body tracking DNN model from directory `"
513 << bodyTrackingModelFilename <<
"`.";
515 ARMARX_CHECK(std::filesystem::exists(bodyTrackingModelFilename))
516 <<
"The path `" << bodyTrackingModelFilename <<
"` does not exist!";
518 k4abt_tracker_configuration_t
const bodyTrackingConfig{
519 .sensor_orientation = K4ABT_SENSOR_ORIENTATION_DEFAULT,
520 .processing_mode = useCPU ? K4ABT_TRACKER_PROCESSING_MODE_CPU
521 : K4ABT_TRACKER_PROCESSING_MODE_GPU_CUDA,
522 .gpu_device_id = bodyTrackingGPUDeviceID,
523 .model_path = bodyTrackingModelFilename.c_str()};
525 bodyTracker = k4abt::tracker::create(k4aCalibration, bodyTrackingConfig);
526 bodyTracker.set_temporal_smoothing(bodyTrackingTemporalSmoothingFactor);
528 if (bodyTrackingRunAtStart and not bodyTrackingIsRunning)
530 bodyTrackingIsRunning =
true;
531 bodyTrackingPublishTask->start();
537 device.set_color_control(K4A_COLOR_CONTROL_BRIGHTNESS, K4A_COLOR_CONTROL_MODE_MANUAL, 128);
539 device.start_cameras(&config);
566 const k4a_calibration_camera_t calibration{k4aCalibration.color_camera_calibration};
567 const k4a_calibration_intrinsic_parameters_t::_param param =
568 calibration.intrinsics.parameters.param;
575 cv::Mat1f camera_matrix(3, 3);
576 camera_matrix << param.fx, 0, param.cx, 0, param.fy, param.cy, 0, 0, 1;
577 cv::Mat1f new_camera_matrix(3, 3);
578 new_camera_matrix << param.fx, 0, param.cx, 0, param.fx, param.cy, 0, 0, 1;
579 cv::Mat1f distortion_coeff(1, 8);
580 distortion_coeff << param.k1, param.k2, param.p1, param.p2, param.k3, param.k4,
582 cv::Mat map1, map2, map3;
583 cv::initUndistortRectifyMap(
588 cv::Size{calibration.resolution_width, calibration.resolution_height},
592 cv::convertMaps(map1, map2, colorDistortionMap, map3, CV_16SC2,
true);
614 const std::chrono::milliseconds TIMEOUT{1000};
616 StopWatch sw_get_capture;
621 status = device.get_capture(&
capture, TIMEOUT);
624 catch (
const std::exception&)
626 ARMARX_WARNING <<
"Failed to get capture from device (#" << ++mDiagnostics.num_crashes
627 <<
"). Restarting camera.";
629 device.stop_cameras();
630 device.start_cameras(&config);
631 ARMARX_INFO <<
"Restarting took " << sw.stop() <<
".";
640 std::lock_guard g{metaInfoMtx};
645 const bool mustInitializeTimestampOffset = [&]()
647 std::lock_guard g{deviceToRealtimeOffsetMtx};
648 return device_to_realtime_offset_.count() == 0;
650 if (mustInitializeTimestampOffset)
657 capture.get_ir_image().get_system_timestamp());
659 const k4a::image DEPTH_IMAGE =
capture.get_depth_image();
663 const k4a_image_format_t IMAGE_FORMAT = DEPTH_IMAGE.get_format();
664 if (IMAGE_FORMAT != K4A_IMAGE_FORMAT_DEPTH16 && IMAGE_FORMAT != K4A_IMAGE_FORMAT_IR16)
666 const char* format_str = ::k4aImageFormatToString(IMAGE_FORMAT);
667 std::stringstream error_msg;
668 error_msg <<
"Attempted to colorize a non-depth image with format: " << format_str;
669 throw std::logic_error(error_msg.str());
673 if (not framerate.pointCloud.skip())
679 std::lock_guard lock{pointcloudProcMutex};
680 transformation.depth_image_to_color_camera(DEPTH_IMAGE, &alignedDepthImage);
683 depthImageReady =
true;
687 pointcloudProcSignal.notify_one();
690#ifdef INCLUDE_BODY_TRACKING
691 if (bodyTrackingIsRunning)
694 std::scoped_lock lock(bodyTrackingParameterMutex);
696 if (not framerate.bodyTracking.skip())
698 k4a::image ir_image =
capture.get_ir_image();
699 std::uint8_t* ir_image_buffer = ir_image.get_buffer();
701 k4a::image depth_image =
capture.get_depth_image();
702 std::uint8_t* depth_image_buffer = depth_image.get_buffer();
706 depth_image.get_height_pixels());
708 const int stride = ir_image.get_stride_bytes() / ir_image.get_width_pixels();
710 for (
int x = 0;
x < ir_image.get_width_pixels();
x++)
712 for (
int y = 0; y < ir_image.get_height_pixels(); y++)
714 const int i = (y * ir_image.get_width_pixels() * stride) + (
x * stride);
715 const int z = (
static_cast<int>(depth_image_buffer[i])) +
716 (
static_cast<int>(depth_image_buffer[i + 1]) << 8);
718 if ((bodyTrackingDepthMaskMinX > 0 and
x < bodyTrackingDepthMaskMinX) or
719 (bodyTrackingDepthMaskMaxX > 0 and
x > bodyTrackingDepthMaskMaxX) or
720 (bodyTrackingDepthMaskMaxZ > 0 and z > bodyTrackingDepthMaskMaxZ))
722 ir_image_buffer[i] = std::numeric_limits<std::uint8_t>::max();
723 ir_image_buffer[i + 1] = std::numeric_limits<std::uint8_t>::max();
724 depth_image_buffer[i] = std::numeric_limits<std::uint8_t>::max();
725 depth_image_buffer[i + 1] =
726 std::numeric_limits<std::uint8_t>::max();
731 if (not bodyTracker.enqueue_capture(
capture))
739 const k4a::image COLOR_IMAGE =
capture.get_color_image();
742 auto real_time = IceUtil::Time::now();
743 auto monotonic_time = IceUtil::Time::now(IceUtil::Time::Monotonic);
744 auto clock_diff = real_time - monotonic_time;
746 auto image_monotonic_time =
747 IceUtil::Time::microSeconds(std::chrono::duration_cast<std::chrono::microseconds>(
748 DEPTH_IMAGE.get_system_timestamp())
752 imagesTime = image_monotonic_time + clock_diff - IceUtil::Time::microSeconds(offset);
755 std::lock_guard g{metaInfoMtx};
761 std::lock_guard g{debugObserverMtx};
763 (real_time - imagesTime).toMilliSecondsDouble());
766 if (enableColorUndistortion)
769 cv::Mat tmp_rgb_image;
771 if (COLOR_IMAGE.get_format() == K4A_IMAGE_FORMAT_COLOR_YUY2)
774 cv::Mat yuy2_image(COLOR_IMAGE.get_height_pixels(),
775 COLOR_IMAGE.get_width_pixels(),
777 const_cast<uint8_t*
>(COLOR_IMAGE.get_buffer()));
779 cv::cvtColor(yuy2_image, tmp_rgb_image, cv::COLOR_YUV2RGB_YUY2);
781 else if (COLOR_IMAGE.get_format() == K4A_IMAGE_FORMAT_COLOR_BGRA32)
783 cv::cvtColor(cv::Mat{cv::Size{COLOR_IMAGE.get_width_pixels(),
784 COLOR_IMAGE.get_height_pixels()},
786 (
void*)COLOR_IMAGE.get_buffer(),
793 throw std::runtime_error(
"Unsupported color format in k4a image");
796 cv::Mat cv_color_image_undistorted(COLOR_IMAGE.get_width_pixels(),
797 COLOR_IMAGE.get_height_pixels(),
800 cv::remap(tmp_rgb_image,
801 cv_color_image_undistorted,
805 cv::BORDER_CONSTANT);
813 ::k4aToIvtImage(COLOR_IMAGE, *resultColorImage);
821 const int DW = alignedDepthImage.get_width_pixels();
822 const int DH = alignedDepthImage.get_height_pixels();
828 auto result_depth_buffer = resultDepthImage->pixels;
829 const auto* depth_buffer =
830 reinterpret_cast<const uint16_t*
>(alignedDepthImage.get_buffer());
834 for (
int y = 0; y < DH; ++y)
836 for (
int x = 0;
x < DW; ++
x)
838 uint16_t depth_value = depth_buffer[index_2];
840 result_depth_buffer[
index],
841 result_depth_buffer[
index + 1],
842 result_depth_buffer[
index + 2]);
852 CByteImage* images[2] = {resultColorImage.get(), resultDepthImage.get()};
858 std::unique_lock signal_lock{pointcloudProcMutex};
859 ARMARX_DEBUG <<
"Capturing thread waiting for signal...";
860 pointcloudProcSignal.wait(signal_lock, [&] {
return depthImageProcessed; });
865 std::lock_guard g{debugObserverMtx};
871 heartbeatPlugin->heartbeat();
881 std::lock_guard g{debugObserverMtx};
892 Eigen::Vector3f accMean = Eigen::Vector3f::Zero();
895 k4a_imu_sample_t imu_sample;
896 while (device.get_imu_sample(&imu_sample, std::chrono::milliseconds(0)))
898 const Eigen::Vector3f acceleration{imu_sample.acc_sample.xyz.x,
899 imu_sample.acc_sample.xyz.y,
900 imu_sample.acc_sample.xyz.z};
902 accMean += acceleration;
906 accMean /=
static_cast<float>(cnt);
911 std::lock_guard g{debugObserverMtx};
920 catch (
const std::exception&)
923 << ++mDiagnostics.num_crashes <<
").";
930#ifdef INCLUDE_BODY_TRACKING
932 AzureKinectPointCloudProvider::runPublishBodyTrackingResults()
935 while (bodyTrackingIsRunning)
939 const k4abt::frame body_frame = [&]() -> k4abt::frame
943 auto result = bodyTracker.pop_result();
954 if (body_frame !=
nullptr)
956 armarx::core::time::ScopedStopWatch sw{
963 auto real_time = IceUtil::Time::now();
964 auto monotonic_time = IceUtil::Time::now(IceUtil::Time::Monotonic);
965 auto clock_diff = real_time - monotonic_time;
967 auto image_monotonic_time = IceUtil::Time::microSeconds(
968 std::chrono::duration_cast<std::chrono::microseconds>(
969 body_frame.get_system_timestamp())
974 IceUtil::Time imageTime = image_monotonic_time + clock_diff;
977 std::lock_guard g{debugObserverMtx};
979 imageTime.toMicroSeconds());
984 const armarx::Clock realtimeClock = armarx::Clock(armarx::ClockType::Realtime);
985 const armarx::Clock monotonicClock =
986 armarx::Clock(armarx::ClockType::Monotonic);
988 auto real_time = realtimeClock.
now();
989 auto monotonic_time = monotonicClock.
now();
990 auto clock_diff = real_time - monotonic_time;
993 std::chrono::duration_cast<std::chrono::microseconds>(
994 body_frame.get_system_timestamp())
999 auto imageTime = image_monotonic_time + clock_diff;
1001 armarx::DateTime imageTimestamp = armarx::DateTime(imageTime);
1004 std::lock_guard g{debugObserverMtx};
1006 imageTime.toMicroSeconds());
1011 std::uint32_t num_bodies = body_frame.get_num_bodies();
1013 std::lock_guard g{debugObserverMtx};
1017 std::vector<armarx::armem::human::HumanPose> humanPoses;
1018 humanPoses.reserve(num_bodies);
1020 for (std::uint32_t i = 0; i < num_bodies; i++)
1022 k4abt_body_t body = body_frame.get_body(i);
1023 printBodyInformation(body);
1025 armarx::armem::human::HumanPose humanPose;
1030 for (
int i = 0; i < static_cast<int>(K4ABT_JOINT_COUNT); i++)
1037 k4a_float3_t position = body.skeleton.joints[i].position;
1038 k4a_quaternion_t orientation = body.skeleton.joints[i].orientation;
1039 k4abt_joint_confidence_level_t confidence_level =
1040 body.skeleton.joints[i].confidence_level;
1042 humanPose.
keypoints[name] = armarx::armem::human::PoseKeypoint{
1044 .confidence =
static_cast<float>(
static_cast<int>(confidence_level)),
1045 .positionCamera = armarx::FramedPosition(
1046 Eigen::Vector3f{position.v[0], position.v[1], position.v[2]},
1047 bodyCameraFrameName,
1049 .orientationCamera =
1054 .toRotationMatrix(),
1055 bodyCameraFrameName,
1059 humanPoses.push_back(humanPose);
1063 std::lock_guard g{debugObserverMtx};
1072 humanPoseWriter.commitHumanPosesInCameraFrame(humanPoses,
getName(),
timestamp);
1092 metronome.waitForNextTick();
1105 while (not pointcloudTask->isStopped())
1108 std::unique_lock signal_lock{pointcloudProcMutex};
1109 ARMARX_DEBUG <<
"Pointcloud thread waiting for signal...";
1110 pointcloudProcSignal.wait(signal_lock,
1112 {
return pointcloudTask->isStopped() or depthImageReady; });
1116 const IceUtil::Time TIMESTAMP = imagesTime;
1117 depthImageReady =
false;
1118 depthImageProcessed =
false;
1123 transformation.depth_image_to_point_cloud(alignedDepthImage,
1124 K4A_CALIBRATION_TYPE_COLOR,
1126 ARMARX_DEBUG <<
"Transforming depth image to point cloud took "
1136 pointcloud->width =
static_cast<uint32_t
>(xyzImage.get_width_pixels());
1137 pointcloud->height =
static_cast<uint32_t
>(xyzImage.get_height_pixels());
1141 pointcloud->is_dense =
false;
1142 pointcloud->points.resize(pointcloud->width * pointcloud->height);
1144 auto k4a_cloud_buffer =
reinterpret_cast<const int16_t*
>(xyzImage.get_buffer());
1148 unsigned char* color_buffer = resultColorImage->pixels;
1149 while (color_buffer ==
nullptr)
1151 ARMARX_INFO <<
"color_buffer is null. This should never happen. There is "
1152 "probably a race condition somewhere that needs to be fixed. "
1153 "Temporarily we ignore this and continue.\n Timestamp: "
1155 color_buffer = resultColorImage->pixels;
1162 pointcloud->width * pointcloud->height);
1168 for (
auto& p : pointcloud->points)
1170 p.r = color_buffer[
index];
1171 p.x = k4a_cloud_buffer[
index];
1175 p.g = color_buffer[
index];
1176 p.y = k4a_cloud_buffer[
index];
1180 p.b = color_buffer[
index];
1181 auto z = k4a_cloud_buffer[
index];
1185 if (z <= max_depth and z != 0)
1191 p.z = std::numeric_limits<float>::quiet_NaN();
1200 depthImageProcessed =
true;
1201 signal_lock.unlock();
1203 pointcloudProcSignal.notify_all();
1210 pointcloud->header.stamp =
static_cast<unsigned long>(TIMESTAMP.toMicroSeconds());
1229 return "AzureKinectPointCloudProvider";
1265 using namespace Eigen;
1270 const auto convert_calibration =
1271 [](
const k4a_calibration_camera_t& k4a_calib,
float scale = 1.f)
1273 MonocularCalibration monocular_calibration;
1275 const k4a_calibration_intrinsic_parameters_t& params = k4a_calib.intrinsics.parameters;
1276 monocular_calibration.cameraParam.principalPoint = {params.param.cx * scale,
1277 params.param.cy * scale};
1278 monocular_calibration.cameraParam.focalLength = {params.param.fx * scale,
1279 params.param.fy * scale};
1292 monocular_calibration.cameraParam.distortion = {
1300 monocular_calibration.cameraParam.width =
1301 std::floor(
float(k4a_calib.resolution_width) * scale);
1302 monocular_calibration.cameraParam.height =
1303 std::floor(
float(k4a_calib.resolution_height) * scale);
1305 const Matrix3FRowMajor rotation =
1306 Map<const Matrix3FRowMajor>{k4a_calib.extrinsics.rotation};
1308 monocular_calibration.cameraParam.rotation = convertEigenMatToVisionX(rotation);
1309 monocular_calibration.cameraParam.translation = {k4a_calib.extrinsics.translation,
1310 k4a_calib.extrinsics.translation + 3};
1312 return monocular_calibration;
1315 StereoCalibration stereo_calibration;
1317 stereo_calibration.calibrationLeft =
1318 convert_calibration(k4aCalibration.color_camera_calibration);
1321 if (enableColorUndistortion)
1323 auto& colorDistortionParams = stereo_calibration.calibrationLeft.cameraParam.distortion;
1324 std::fill(colorDistortionParams.begin(), colorDistortionParams.end(), 0);
1331 stereo_calibration.calibrationRight =
1332 convert_calibration(k4aCalibration.color_camera_calibration);
1334 auto& depthDistortionParams =
1335 stereo_calibration.calibrationRight.cameraParam.distortion;
1336 std::fill(depthDistortionParams.begin(), depthDistortionParams.end(), 0);
1339 stereo_calibration.rectificationHomographyLeft =
1340 convertEigenMatToVisionX(Matrix3f::Identity());
1341 stereo_calibration.rectificationHomographyRight =
1342 convertEigenMatToVisionX(Matrix3f::Identity());
1344 return stereo_calibration;
1350 return enableColorUndistortion;
1359 std::vector<imrec::ChannelPreferences>
1364 imrec::ChannelPreferences rgb;
1365 rgb.requiresLossless =
false;
1368 imrec::ChannelPreferences depth;
1369 depth.requiresLossless =
true;
1370 depth.name =
"depth";
1372 return {rgb, depth};
1377 const armarx::EnableHumanPoseEstimationInput& input,
1378 const Ice::Current&)
1380#ifndef INCLUDE_BODY_TRACKING
1382 ARMARX_ERROR <<
"INCLUDE_BODY_TRACKING is not defined.";
1387#ifdef INCLUDE_BODY_TRACKING
1388 if (bodyTrackingEnabled)
1391 if (not bodyTrackingIsRunning and input.enable3d)
1393 bodyTrackingIsRunning =
true;
1394 bodyTrackingPublishTask->start();
1396 else if (bodyTrackingIsRunning and not input.enable3d)
1398 bodyTrackingIsRunning =
false;
1399 bodyTrackingPublishTask->stop();
1404 ARMARX_ERROR <<
"Azure Kinect Body Tracking is not enabled";
1412 std::scoped_lock lock(bodyTrackingParameterMutex);
1413 bodyTrackingDepthMaskMaxZ = maxDepthInMM;
1419 const Ice::Current&)
1421 std::scoped_lock lock(bodyTrackingParameterMutex);
1422 bodyTrackingDepthMaskMinX = minXinPixel;
1423 bodyTrackingDepthMaskMaxX = maxXinPixel;
1431 const std::chrono::microseconds& k4a_device_timestamp_us,
1432 const std::chrono::nanoseconds& k4a_system_timestamp_ns)
1440 std::chrono::nanoseconds realtime_clock =
1441 std::chrono::system_clock::now().time_since_epoch();
1442 std::chrono::nanoseconds monotonic_clock =
1443 std::chrono::steady_clock::now().time_since_epoch();
1445 std::chrono::nanoseconds monotonic_to_realtime = realtime_clock - monotonic_clock;
1448 std::chrono::nanoseconds device_to_realtime =
1449 k4a_system_timestamp_ns - k4a_device_timestamp_us + monotonic_to_realtime;
1452 std::lock_guard g{deviceToRealtimeOffsetMtx};
1455 std::lock_guard g{debugObserverMtx};
1457 device_to_realtime_offset_.count() /
1461 std::abs<float>((device_to_realtime_offset_ - device_to_realtime).count()) /
1465 const std::int64_t timeOffsetThreshold = 1e7;
1468 if (device_to_realtime_offset_.count() == 0 ||
1469 std::abs((device_to_realtime_offset_ - device_to_realtime).count()) >
1470 timeOffsetThreshold)
1473 <<
"Initializing or re-initializing the device to realtime offset: "
1474 << device_to_realtime.count() <<
" ns";
1475 device_to_realtime_offset_ = device_to_realtime;
1480 constexpr double alpha = 0.10;
1482 const std::chrono::nanoseconds timeCorrection(
static_cast<int64_t
>(
1483 std::floor(alpha * (device_to_realtime - device_to_realtime_offset_).count())));
1484 device_to_realtime_offset_ = device_to_realtime_offset_ + timeCorrection;
1487 std::lock_guard g{debugObserverMtx};
1489 timeCorrection.count() / 1'000);
1497 const std::chrono::microseconds& k4a_timestamp_us)
1499 std::lock_guard g{deviceToRealtimeOffsetMtx};
1504 std::chrono::nanoseconds timestamp_in_realtime =
1505 k4a_timestamp_us + device_to_realtime_offset_;
1513 const std::chrono::microseconds& k4a_device_timestamp_us)
1515 std::lock_guard g{deviceToRealtimeOffsetMtx};
1518 std::chrono::nanoseconds realtime_clock =
1519 std::chrono::system_clock::now().time_since_epoch();
1521 device_to_realtime_offset_ = realtime_clock - k4a_device_timestamp_us;
1523 ARMARX_INFO <<
"Initializing the device to realtime offset based on wall clock: "
1524 << device_to_realtime_offset_.count() <<
" ns";
1535 if (std::abs(std::fmod(higherFramerate,
value)) > 1e-6)
1537 std::stringstream ss;
1538 ss <<
"Invalid value (" <<
value <<
" fps) for framerate of '" <<
name <<
"'."
1539 <<
" Reason: The framerate has to be a divider of the property framerate.image ("
1540 << higherFramerate <<
" fps).";
1541 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)