7 #include "PlusConfigure.h" 15 #include <vtkImageData.h> 16 #include <vtkObjectFactory.h> 19 #include <k4a/k4a.hpp> 39 struct KinectStreamConfig
43 uint32_t FrameRate{0};
53 case KinectSourceType::RGB:
55 case KinectSourceType::DEPTH:
57 case KinectSourceType::PCL:
64 std::tuple<bool, k4a_fps_t> ToKinectFrameRate(
int rate)
68 return std::make_tuple(
true, K4A_FRAMES_PER_SECOND_5);
72 return std::make_tuple(
true, K4A_FRAMES_PER_SECOND_15);
76 return std::make_tuple(
true, K4A_FRAMES_PER_SECOND_30);
79 return std::make_tuple(
false, K4A_FRAMES_PER_SECOND_5);
82 std::tuple<bool, k4a_depth_mode_t, int> ToKinectDepthMode(
int width)
86 return std::make_tuple(
true, K4A_DEPTH_MODE_NFOV_2X2BINNED, 288);
88 else if (
width == 640)
90 return std::make_tuple(
true, K4A_DEPTH_MODE_NFOV_UNBINNED, 576);
92 else if (
width == 512)
94 return std::make_tuple(
true, K4A_DEPTH_MODE_WFOV_2X2BINNED, 512);
96 else if (
width == 1024)
98 return std::make_tuple(
true, K4A_DEPTH_MODE_WFOV_UNBINNED, 1024);
101 return std::make_tuple(
false, K4A_DEPTH_MODE_OFF, 0);
104 std::tuple<bool, k4a_color_resolution_t, int> ToKinectColorMode(
int width)
108 return std::make_tuple(
true, K4A_COLOR_RESOLUTION_720P, 720);
110 else if (
width == 1920)
112 return std::make_tuple(
true, K4A_COLOR_RESOLUTION_1080P, 1080);
114 else if (
width == 2560)
116 return std::make_tuple(
true, K4A_COLOR_RESOLUTION_1440P, 1440);
118 else if (
width == 2048)
120 return std::make_tuple(
true, K4A_COLOR_RESOLUTION_1536P, 1536);
122 else if (
width == 3840)
124 return std::make_tuple(
true, K4A_COLOR_RESOLUTION_2160P, 2160);
126 else if (
width == 4096)
128 return std::make_tuple(
true, K4A_COLOR_RESOLUTION_3072P, 3072);
131 return std::make_tuple(
false, K4A_COLOR_RESOLUTION_OFF, 0);
136 class vtkPlusAzureKinect::vtkInternal
140 std::vector<KinectStreamConfig> StreamList;
142 k4a_device_configuration_t DeviceConfig;
143 k4a::calibration Calibration;
144 k4a::transformation Transformation;
145 k4a::image ColorImage;
146 k4a::image DepthImage;
147 k4a::image PointsCloudImage;
148 bool AlignDepthStream{
false};
149 int AvailableStreamsFlag{0};
154 this->DeviceConfig.color_format = K4A_IMAGE_FORMAT_COLOR_BGRA32;
155 this->DeviceConfig.synchronized_images_only =
true;
156 this->DeviceConfig.depth_mode = K4A_DEPTH_MODE_OFF;
157 this->DeviceConfig.color_resolution = K4A_COLOR_RESOLUTION_OFF;
158 this->DeviceConfig.wired_sync_mode = K4A_WIRED_SYNC_MODE_STANDALONE;
159 this->DeviceConfig.depth_delay_off_color_usec = 0;
160 this->DeviceConfig.subordinate_delay_off_master_usec = 0;
166 std::vector<uint32_t> frameRates;
167 for (
auto& stream : this->StreamList)
169 std::tie(status, DeviceConfig.camera_fps) = ToKinectFrameRate(stream.FrameRate);
172 LOG_ERROR(
"Invalid frame rate detected: 5, 15, 30 expected");
176 frameRates.emplace_back(stream.FrameRate);
178 if (stream.Type == KinectSourceType::RGB)
180 std::tie(status, DeviceConfig.color_resolution, stream.Height) = ToKinectColorMode(stream.Width);
182 else if (stream.Type == KinectSourceType::DEPTH)
184 std::tie(status, DeviceConfig.depth_mode, stream.Height) = ToKinectDepthMode(stream.Width);
189 LOG_ERROR(
"Invalid stream configuration");
194 auto rgbStream = std::find_if(std::begin(this->StreamList), std::end(this->StreamList), [](
const KinectStreamConfig & stream)
196 return stream.Type == KinectSourceType::RGB;
198 auto depthStream = std::find_if(std::begin(this->StreamList), std::end(this->StreamList), [](
const KinectStreamConfig & stream)
200 return stream.Type == KinectSourceType::DEPTH;
202 auto pclStream = std::find_if(std::begin(this->StreamList), std::end(this->StreamList), [](
const KinectStreamConfig & stream)
204 return stream.Type == KinectSourceType::PCL;
207 if (rgbStream != std::end(this->StreamList))
209 this->AvailableStreamsFlag |= KinectSourceType::RGB;
212 if (depthStream != std::end(this->StreamList))
214 this->AvailableStreamsFlag |= KinectSourceType::DEPTH;
217 if (pclStream != std::end(this->StreamList))
219 this->AvailableStreamsFlag |= KinectSourceType::PCL;
222 if ((this->AvailableStreamsFlag & KinectSourceType::PCL) && !(this->AvailableStreamsFlag & KinectSourceType::DEPTH))
224 LOG_ERROR(
"Invalid configuration: PCL stream is required but no Depth stream set");
228 if (!(this->AvailableStreamsFlag & KinectSourceType::RGB) || !(this->AvailableStreamsFlag & KinectSourceType::DEPTH))
230 DeviceConfig.synchronized_images_only =
false;
233 if (AlignDepthStream)
235 if (!DeviceConfig.synchronized_images_only)
237 LOG_ERROR(
"Invalid configuration: AlignDepthStream set to TRUE but no RGB or Depth stream set");
241 depthStream->Width = rgbStream->Width;
242 depthStream->Height = rgbStream->Height;
245 if ((this->AvailableStreamsFlag & KinectSourceType::PCL) && (this->AvailableStreamsFlag & KinectSourceType::RGB))
247 pclStream->Width = rgbStream->Width;
248 pclStream->Height = rgbStream->Height;
250 else if (this->AvailableStreamsFlag & KinectSourceType::PCL)
252 pclStream->Width = depthStream->Width;
253 pclStream->Height = depthStream->Height;
256 std::sort(std::begin(frameRates), std::end(frameRates));
257 frameRates.erase(std::unique(std::begin(frameRates), std::end(frameRates)), std::end(frameRates));
259 if (frameRates.size() > 1)
261 LOG_WARNING(
"Detecting different frame rates for the streams: selecting the lowest rate (" << frameRates[0] <<
" fps)");
274 this->Device = k4a::device::open(0);
276 catch (
const k4a::error& err)
278 LOG_ERROR(
"Failed to connect: " << err.what());
287 this->Device.close();
295 this->Device.start_cameras(&(this->DeviceConfig));
296 this->Calibration = this->Device.get_calibration(this->DeviceConfig.depth_mode, this->DeviceConfig.color_resolution);
297 this->Transformation = k4a::transformation(this->Calibration);
299 catch (
const k4a::error& err)
301 LOG_ERROR(
"Failed to start camera: " << err.what());
310 this->Device.stop_cameras();
316 k4a::capture capture;
317 if (!this->Device.get_capture(&capture, std::chrono::milliseconds(0)))
322 if (this->AvailableStreamsFlag & KinectSourceType::DEPTH)
324 this->DepthImage = capture.get_depth_image();
327 if (this->AvailableStreamsFlag & KinectSourceType::RGB)
329 this->ColorImage = capture.get_color_image();
332 if (this->ColorImage && this->DepthImage && this->AlignDepthStream)
334 this->DepthImage = this->Transformation.depth_image_to_color_camera(this->DepthImage);
337 if (!(this->AvailableStreamsFlag & KinectSourceType::PCL))
342 if (this->AvailableStreamsFlag & KinectSourceType::RGB)
344 this->PointsCloudImage = this->Transformation.depth_image_to_point_cloud(
345 (this->AlignDepthStream ? this->DepthImage : this->Transformation.depth_image_to_color_camera(this->DepthImage)), K4A_CALIBRATION_TYPE_COLOR);
349 this->PointsCloudImage = this->Transformation.depth_image_to_point_cloud(this->DepthImage,
350 K4A_CALIBRATION_TYPE_DEPTH);
357 : Internal(new vtkInternal(this))
366 delete this->Internal;
367 this->Internal =
nullptr;
375 os << indent <<
"Kinect Azure Configuration" << std::endl;
376 os << indent <<
"AlignDepthStream: " << this->Internal->AlignDepthStream << std::endl;
377 for (
const auto& config : this->Internal->StreamList)
379 os << indent <<
"Source: " << config.Id << std::endl;
380 os << indent << indent <<
"Type: " << KinectSourceTypeAsString(config.Type) << std::endl;
381 os << indent << indent <<
"Frame Rate: " << config.FrameRate << std::endl;
382 os << indent << indent <<
"Frame Width: " << config.Width << std::endl;
383 os << indent << indent <<
"Frame Height: " << config.Height << std::endl;
391 XML_READ_BOOL_ATTRIBUTE_NONMEMBER_OPTIONAL(AlignDepthStream, this->Internal->AlignDepthStream, deviceConfig);
392 XML_FIND_NESTED_ELEMENT_REQUIRED(dataSourcesElement, deviceConfig,
"DataSources");
394 LOG_TRACE(
"Reading custom configuration fields in " << dataSourcesElement->GetNumberOfNestedElements() <<
" nested elements");
395 for (
int nestedElementIndex = 0; nestedElementIndex < dataSourcesElement->GetNumberOfNestedElements(); ++nestedElementIndex)
397 vtkXMLDataElement* dataElement = dataSourcesElement->GetNestedElement(nestedElementIndex);
398 if (STRCASECMP(dataElement->GetName(),
"DataSource") != 0)
403 LOG_TRACE(
"Found a new data source");
405 if (dataElement->GetAttribute(
"Type") != NULL && STRCASECMP(dataElement->GetAttribute(
"Type"),
"Video") == 0)
407 const char* toolId = dataElement->GetAttribute(
"Id");
408 if (toolId ==
nullptr)
411 LOG_ERROR(
"Failed to initialize Kinect Azure DataSource: Id is missing");
415 LOG_TRACE(
"Data source name: " << toolId);
417 KinectStreamConfig config;
419 XML_READ_ENUM3_ATTRIBUTE_NONMEMBER_REQUIRED(FrameType, config.Type, dataElement,
"RGB", KinectSourceType::RGB,
"DEPTH", KinectSourceType::DEPTH,
"PCL", KinectSourceType::PCL);
420 XML_READ_SCALAR_ATTRIBUTE_NONMEMBER_REQUIRED(
int, FrameRate, config.FrameRate, dataElement);
421 XML_READ_SCALAR_ATTRIBUTE_NONMEMBER_REQUIRED(
int, FrameSize, config.Width, dataElement);
422 this->Internal->StreamList.push_back(config);
426 LOG_ERROR(
"DataSource with unknown Type.");
439 if (this->Internal->AlignDepthStream)
441 deviceConfig->SetAttribute(
"AlignDepthStream",
"TRUE");
445 deviceConfig->SetAttribute(
"AlignDepthStream",
"FALSE");
452 return this->Internal->UpdateConfig();
458 for (
auto& stream : this->Internal->StreamList)
461 if (stream.Source ==
nullptr)
463 LOG_ERROR(
"Invalid source for tool id " << stream.Id);
467 return this->Internal->Connect();
473 return this->Internal->Disconnect();
479 this->FrameNumber = 0;
481 return this->Internal->Start();
487 return this->Internal->Stop();
495 this->Internal->Update();
498 for (
auto& stream : this->Internal->StreamList)
500 if (stream.Source->GetNumberOfItems() == 0 && stream.Type == KinectSourceType::RGB)
502 LOG_TRACE(
"Setting up color frame");
503 stream.Source->SetImageType(US_IMG_RGB_COLOR);
504 stream.Source->SetPixelType(VTK_UNSIGNED_CHAR);
505 stream.Source->SetNumberOfScalarComponents(3);
506 stream.Source->SetInputFrameSize(stream.Width, stream.Height, 1);
508 else if (stream.Source->GetNumberOfItems() == 0 && stream.Type == KinectSourceType::DEPTH)
511 LOG_TRACE(
"Setting up depth frame");
512 stream.Source->SetImageType(US_IMG_BRIGHTNESS);
513 stream.Source->SetPixelType(VTK_TYPE_UINT16);
514 stream.Source->SetNumberOfScalarComponents(1);
515 stream.Source->SetInputFrameSize(stream.Width, stream.Height, 1);
517 else if (stream.Source->GetNumberOfItems() == 0 && stream.Type == KinectSourceType::PCL)
520 LOG_TRACE(
"Setting up pcl frame");
521 stream.Source->SetImageType(US_IMG_BRIGHTNESS);
522 stream.Source->SetPixelType(VTK_TYPE_INT16);
523 stream.Source->SetNumberOfScalarComponents(3);
524 stream.Source->SetInputFrameSize(stream.Width, stream.Height, 1);
527 if (stream.Type == KinectSourceType::RGB)
529 if (!this->Internal->ColorImage)
531 if (stream.Source->GetNumberOfItems() == 0)
533 LOG_TRACE(
"Waiting for KinectAzure RGB images");
537 LOG_WARNING(
"Failed to get KinectAzure RGB image");
543 const auto* bgraBuffer = this->Internal->ColorImage.get_buffer();
544 unsigned rgbSize = this->Internal->ColorImage.get_size() * 3 / 4;
545 std::unique_ptr<uint8_t[]> rgbBuffer(
new uint8_t[rgbSize]);
547 for (
auto i = 0U;
i < this->Internal->ColorImage.get_size();
i += 4)
549 rgbBuffer[3 *
i / 4] = bgraBuffer[
i + 2];
550 rgbBuffer[3 *
i / 4 + 1] = bgraBuffer[
i + 1];
551 rgbBuffer[3 *
i / 4 + 2] = bgraBuffer[
i];
554 FrameSizeType frameSizeColor = {stream.Width, stream.Height, 1};
555 if (stream.Source->AddItem(reinterpret_cast<void*>(rgbBuffer.get()), stream.Source->GetInputImageOrientation(), frameSizeColor, VTK_UNSIGNED_CHAR, 3, US_IMG_RGB_COLOR, 0, this->FrameNumber) ==
PLUS_FAIL)
557 LOG_ERROR(
"Unable to send RGB image. Skipping frame.");
562 else if (stream.Type == KinectSourceType::DEPTH)
564 if (!this->Internal->DepthImage)
566 if (stream.Source->GetNumberOfItems() == 0)
568 LOG_TRACE(
"Waiting for KinectAzure DEPTH images");
572 LOG_WARNING(
"Failed to get KinectAzure DEPTH image");
577 FrameSizeType frameSizeDepth = {stream.Width, stream.Height, 1};
578 if (stream.Source->AddItem(reinterpret_cast<void*>(this->Internal->DepthImage.get_buffer()), stream.Source->GetInputImageOrientation(), frameSizeDepth, VTK_TYPE_UINT16, 1, US_IMG_BRIGHTNESS, 0, this->FrameNumber) ==
PLUS_FAIL)
580 LOG_ERROR(
"Unable to send DEPTH image. Skipping frame.");
585 else if (stream.Type == KinectSourceType::PCL)
587 if (!this->Internal->PointsCloudImage)
589 if (stream.Source->GetNumberOfItems() == 0)
591 LOG_TRACE(
"Waiting for KinectAzure PCL images");
595 LOG_WARNING(
"Failed to get KinectAzure PCL image");
600 FrameSizeType frameSizeDepth = {stream.Width, stream.Height, 1};
601 if (stream.Source->AddItem(reinterpret_cast<void*>(this->Internal->PointsCloudImage.get_buffer()), stream.Source->GetInputImageOrientation(), frameSizeDepth, VTK_TYPE_INT16, 3, US_IMG_BRIGHTNESS, 0, this->FrameNumber) ==
PLUS_FAIL)
603 LOG_ERROR(
"Unable to send PCL image. Skipping frame.");
PlusStatus InternalStopRecording()
vtkStandardNewMacro(vtkPlusAzureKinect)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
PlusStatus InternalStartRecording()
PlusStatus WriteConfiguration(vtkXMLDataElement *config)
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
bool RequireImageOrientationInConfiguration
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *config)
virtual PlusStatus Disconnect()
virtual PlusStatus Connect()
double InternalUpdateRate
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
PhidgetLCD_Font int * width
virtual PlusStatus InternalDisconnect()
Interface class to Kinect Azure cameras.
virtual PlusStatus InternalUpdate()
bool StartThreadForInternalUpdates
virtual PlusStatus InternalConnect()
virtual void PrintSelf(ostream &os, vtkIndent indent)
PlusStatus GetVideoSource(const char *aSourceId, vtkPlusDataSource *&aVideoSource)
virtual PlusStatus NotifyConfigured()
Interface to a 3D positioning tool, video source, or generalized data stream.