PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusRevopoint3DCamera.cxx
Go to the documentation of this file.
1 /*=Plus=header=begin======================================================
2 Program: Plus
3 Copyright (c) Laboratory for Percutaneous Surgery. All rights reserved.
4 See License.txt for details.
5 =========================================================Plus=header=end*/
6 
7 #include "PlusConfigure.h"
9 
10 // Local includes
11 #include "vtkPlusChannel.h"
12 #include "vtkPlusDataSource.h"
13 
14 // VTK includes
15 #include <vtkImageData.h>
16 #include <vtkObjectFactory.h>
17 
18 // Revopoint SDK includes
19 #include "3DCamera.hpp"
20 
21 // Stl includes
22 #include <vector>
23 #include <string>
24 #include <memory>
25 #include <algorithm>
26 
27 //----------------------------------------------------------------------------
29 
30 namespace
31 {
33  {
34  DEPTH = 1 << 0,
35  PCL = 1 << 1
36  };
37 
38  struct StreamConfigBase
39  {
40  std::string Id;
41  StreamInfo StreamProps;
42  vtkPlusDataSource* Source{nullptr};
43  };
44 
45  struct PCLStreamConfig : StreamConfigBase
46  {
47  };
48 
49  struct ConcreteStreamConfig : StreamConfigBase
50  {
51  Intrinsics Intr;
52  Extrinsics Extr;
53  };
54 
55  struct DepthStreamConfig : ConcreteStreamConfig
56  {
57  int DepthRange[2];
58  };
59 
60  std::string convertErrorCode(ERROR_CODE error)
61  {
62  switch (error)
63  {
64  case ERROR_CODE::ERROR_PARAM:
65  return "Bad param";
66  case ERROR_CODE::ERROR_DEVICE_NOT_FOUND:
67  return "Device not found";
68  case ERROR_CODE::ERROR_DEVICE_NOT_CONNECT:
69  return "Device not connected";
70  case ERROR_CODE::ERROR_DEVICE_BUSY:
71  return "Device busy";
72  case ERROR_CODE::ERROR_STREAM_NOT_START:
73  return "Device not started";
74  case ERROR_CODE::ERROR_STREAM_BUSY:
75  return "Stream busy";
76  case ERROR_CODE::ERROR_FRAME_TIMEOUT:
77  return "Frame timeout";
78  case ERROR_CODE::ERROR_NOT_SUPPORT:
79  return "Not supported";
80  case ERROR_CODE::ERROR_PROPERTY_GET_FAILED:
81  return "Getting property failed";
82  case ERROR_CODE::ERROR_PROPERTY_SET_FAILED:
83  return "Setting property failed";
84  default:
85  break;
86  }
87  return "Success";
88  }
89 }
90 
91 //----------------------------------------------------------------------------
92 class vtkPlusRevopoint3DCamera::vtkInternal
93 {
94 public:
95  vtkPlusRevopoint3DCamera* External;
96  DepthStreamConfig DepthStream;
97  PCLStreamConfig PCLStream;
98  cs::ICameraPtr Camera;
99  cs::IFramePtr DepthFrame;
100  std::unique_ptr<float[]> PCLFrame;
101  int AvailableStreamsFlag{0};
102 
103  vtkInternal(vtkPlusRevopoint3DCamera* external)
104  : External(external)
105  {
106  this->Camera = cs::getCameraPtr();
107  }
108 
109  PlusStatus CheckBasicConfig()
110  {
111  if (!this->DepthStream.Id.empty())
112  {
113  this->AvailableStreamsFlag |= RevoSourceType::DEPTH;
114  }
115 
116  if (!this->PCLStream.Id.empty())
117  {
118  this->AvailableStreamsFlag |= RevoSourceType::PCL;
119  }
120 
121  if ((this->AvailableStreamsFlag & RevoSourceType::PCL) && !(this->AvailableStreamsFlag & RevoSourceType::DEPTH))
122  {
123  LOG_ERROR("Invalid configuration: PCL stream is required but no Depth stream set");
124  return PLUS_FAIL;
125  }
126 
127  return PLUS_SUCCESS;
128  }
129 
130  PlusStatus UpdateDepthStreamConfig()
131  {
132  std::vector<StreamInfo> streamProps;
133  this->Camera->getStreamInfos(STREAM_TYPE::STREAM_TYPE_DEPTH, streamProps);
134 
135  bool foundMatchingConfig{false};
136  for (auto streamProp : streamProps)
137  {
138  LOG_TRACE("New stream configuration for format: " << streamProp.format);
139  if (streamProp.format == this->DepthStream.StreamProps.format)
140  {
141  LOG_INFO("New depth stream compliant configuration: " << streamProp.width << "x" << streamProp.height << "@" << streamProp.fps);
142  if (this->DepthStream.StreamProps.width != streamProp.width)
143  {
144  continue;
145  }
146 
147  if (this->DepthStream.StreamProps.fps > streamProp.fps)
148  {
149  this->DepthStream.StreamProps.fps = streamProp.fps;
150  LOG_WARNING("Expected frame rate too high for current bandwidth. Actual framerate will be @" << streamProp.fps << " fps.");
151  }
152 
153  this->DepthStream.StreamProps.height = streamProp.height;
154  foundMatchingConfig = true;
155  break;
156  }
157  }
158 
159  if (!foundMatchingConfig)
160  {
161  LOG_ERROR("No matching configuration found");
162  LOG_ERROR("Available configurations:");
163  for (auto streamProp : streamProps)
164  {
165  if (streamProp.format == this->DepthStream.StreamProps.format)
166  {
167  LOG_ERROR("- " << streamProp.width << "x" << streamProp.height << "@" << streamProp.fps);
168  }
169  }
170  return PLUS_FAIL;
171  }
172 
173  this->Camera->getIntrinsics(STREAM_TYPE::STREAM_TYPE_DEPTH, this->DepthStream.Intr);
174  this->Camera->getExtrinsics(this->DepthStream.Extr);
175 
176  PropertyExtension value;
177  value.depthRange.min = this->DepthStream.DepthRange[0];
178  value.depthRange.max = this->DepthStream.DepthRange[1];
179  value.algorithmContrast = 5;
180 
181  auto ret = this->Camera->setPropertyExtension(PROPERTY_TYPE_EXTENSION::PROPERTY_EXT_DEPTH_RANGE, value);
182  if (ret != ERROR_CODE::SUCCESS)
183  {
184  LOG_ERROR("Failed to set camera depth range: " << convertErrorCode(ret));
185  return PLUS_FAIL;
186  }
187 
188  return PLUS_SUCCESS;
189  }
190 
191  PlusStatus UpdatePCLStreamConfig()
192  {
193  this->PCLStream.StreamProps = this->DepthStream.StreamProps;
194  return PLUS_SUCCESS;
195  }
196 
197  PlusStatus UpdateConfig()
198  {
199  if ((this->AvailableStreamsFlag & RevoSourceType::DEPTH) && this->UpdateDepthStreamConfig() != PLUS_SUCCESS)
200  {
201  return PLUS_FAIL;
202  }
203 
204  if ((this->AvailableStreamsFlag & RevoSourceType::PCL) && this->UpdatePCLStreamConfig() != PLUS_SUCCESS)
205  {
206  return PLUS_FAIL;
207  }
208 
209  this->External->InternalUpdateRate = this->DepthStream.StreamProps.fps;
210  this->External->AcquisitionRate = this->DepthStream.StreamProps.fps;
211 
212  return PLUS_SUCCESS;
213  }
214 
216  {
217  auto ret = this->Camera->connect();
218  if (ret != ERROR_CODE::SUCCESS)
219  {
220  LOG_ERROR("Failed to connect camera: " << convertErrorCode(ret));
221  return PLUS_FAIL;
222  }
223 
224  return PLUS_SUCCESS;
225  }
226 
228  {
229  auto ret = this->Camera->disconnect();
230  if (ret != ERROR_CODE::SUCCESS)
231  {
232  LOG_ERROR("Failed to disconnect camera: " << convertErrorCode(ret));
233  return PLUS_FAIL;
234  }
235 
236  return PLUS_SUCCESS;
237  }
238 
239  PlusStatus Start()
240  {
241  auto ret = this->Camera->startStream(STREAM_TYPE::STREAM_TYPE_DEPTH, this->DepthStream.StreamProps);
242  if (ret != ERROR_CODE::SUCCESS)
243  {
244  LOG_ERROR("Failed to start camera: " << convertErrorCode(ret));
245  return PLUS_FAIL;
246  }
247 
248  return PLUS_SUCCESS;
249  }
250 
251  PlusStatus Stop()
252  {
253  auto ret = this->Camera->stopStream(STREAM_TYPE::STREAM_TYPE_DEPTH);
254  if (ret != ERROR_CODE::SUCCESS)
255  {
256  LOG_ERROR("Failed to stop camera: " << convertErrorCode(ret));
257  return PLUS_FAIL;
258  }
259 
260  return PLUS_SUCCESS;
261  }
262 
263  void Update()
264  {
265  if (this->AvailableStreamsFlag & RevoSourceType::DEPTH)
266  {
267  this->DepthFrame.reset();
268  auto ret = this->Camera->getFrame(STREAM_TYPE::STREAM_TYPE_DEPTH, DepthFrame);
269  if (ret != ERROR_CODE::SUCCESS)
270  {
271  LOG_ERROR("Failed to retrieve frame: " << convertErrorCode(ret));
272  }
273  }
274 
275  if ((this->AvailableStreamsFlag & RevoSourceType::PCL) && this->IsDepthFrameValid())
276  {
277  float scale = 0.1f;
278  PropertyExtension value;
279  if (SUCCESS ==
280  this->Camera->getPropertyExtension(PROPERTY_EXT_DEPTH_SCALE, value))
281  {
282  scale = value.depthScale;
283  }
284 
285  cs::Pointcloud pc;
286  pc.generatePoints(reinterpret_cast<unsigned short*>(const_cast<char*>(this->DepthFrame->getData())),
287  this->DepthFrame->getWidth(), this->DepthFrame->getHeight(),
288  scale, &this->DepthStream.Intr, nullptr, &this->DepthStream.Extr);
289  auto vertices = pc.getVertices();
290  this->PCLFrame.reset(new float[vertices.size() * 3]);
291 
292  for (std::size_t idx = 0; idx < vertices.size(); ++idx)
293  {
294  this->PCLFrame[idx * 3] = vertices[idx].x;
295  this->PCLFrame[idx * 3 + 1] = vertices[idx].y;
296  this->PCLFrame[idx * 3 + 2] = vertices[idx].z;
297  }
298  }
299  }
300 
301  bool IsDepthFrameValid() const
302  {
303  return this->DepthFrame && !(this->DepthFrame->empty()) && (this->DepthFrame->getWidth() == this->DepthStream.StreamProps.width) &&
304  (this->DepthFrame->getHeight() == this->DepthStream.StreamProps.height);
305  }
306 };
307 
308 //----------------------------------------------------------------------------
310  : Internal(new vtkInternal(this))
311 {
313  this->StartThreadForInternalUpdates = true;
314 }
315 
316 //----------------------------------------------------------------------------
318 {
319  delete this->Internal;
320  this->Internal = nullptr;
321 }
322 
323 //----------------------------------------------------------------------------
324 void vtkPlusRevopoint3DCamera::PrintSelf(ostream& os, vtkIndent indent)
325 {
326  this->Superclass::PrintSelf(os, indent);
327 
328  os << indent << "Revopoint Surface Configuration" << std::endl;
329 
330  if (this->Internal->AvailableStreamsFlag & RevoSourceType::DEPTH)
331  {
332  os << indent << "DEPTH Stream" << std::endl;
333  os << indent << "Source: " << this->Internal->DepthStream.Id << std::endl;
334  os << indent << indent << "Frame Rate: " << this->Internal->DepthStream.StreamProps.fps << std::endl;
335  os << indent << indent << "Frame Width: " << this->Internal->DepthStream.StreamProps.width << std::endl;
336  os << indent << indent << "Frame Height: " << this->Internal->DepthStream.StreamProps.height << std::endl;
337  os << indent << indent << "Depth Range: [" << this->Internal->DepthStream.DepthRange[0] << "," << Internal->DepthStream.DepthRange[1] << "]" << std::endl;
338  }
339 
340  if (this->Internal->AvailableStreamsFlag & RevoSourceType::PCL)
341  {
342  os << indent << "PCL Stream" << std::endl;
343  os << indent << "Source: " << this->Internal->PCLStream.Id << std::endl;
344  }
345 }
346 
347 //-----------------------------------------------------------------------------
348 PlusStatus vtkPlusRevopoint3DCamera::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
349 {
350  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
351  XML_FIND_NESTED_ELEMENT_REQUIRED(dataSourcesElement, deviceConfig, "DataSources");
352 
353  LOG_TRACE("Reading custom configuration fields in " << dataSourcesElement->GetNumberOfNestedElements() << " nested elements");
354 
355  for (int nestedElementIndex = 0; nestedElementIndex < dataSourcesElement->GetNumberOfNestedElements(); ++nestedElementIndex)
356  {
357  vtkXMLDataElement* dataElement = dataSourcesElement->GetNestedElement(nestedElementIndex);
358  if (std::string(dataElement->GetName()) != "DataSource")
359  {
360  continue;
361  }
362 
363  LOG_TRACE("Found a new data source");
364  if (dataElement->GetAttribute("Type") != nullptr && std::string(dataElement->GetAttribute("Type")) == "Video")
365  {
366  const char* toolId = dataElement->GetAttribute("Id");
367  if (toolId == nullptr)
368  {
369  LOG_ERROR("Failed to initialize Revopoint Surface DataSource: Id is missing");
370  return PLUS_FAIL;
371  }
372 
373  LOG_TRACE("Data source name: " << toolId);
374 
375  RevoSourceType sourceType;
376  XML_READ_ENUM2_ATTRIBUTE_NONMEMBER_REQUIRED(FrameType, sourceType, dataElement, "DEPTH", RevoSourceType::DEPTH, "PCL", RevoSourceType::PCL);
377 
378  if (sourceType == RevoSourceType::DEPTH)
379  {
380  DepthStreamConfig stream;
381  stream.Id = toolId;
382  stream.DepthRange[0] = 5;
383  stream.DepthRange[1] = 500;
384  XML_READ_SCALAR_ATTRIBUTE_NONMEMBER_REQUIRED(int, FrameRate, stream.StreamProps.fps, dataElement);
385  XML_READ_SCALAR_ATTRIBUTE_NONMEMBER_REQUIRED(int, FrameWidth, stream.StreamProps.width, dataElement);
386  XML_READ_VECTOR_ATTRIBUTE_NONMEMBER_OPTIONAL(int, 2, DepthRange, stream.DepthRange, dataElement);
387  stream.StreamProps.format = STREAM_FORMAT::STREAM_FORMAT_Z16;
388  this->Internal->DepthStream = stream;
389  }
390  else
391  {
392  this->Internal->PCLStream.Id = toolId;
393  }
394  }
395  else
396  {
397  LOG_ERROR("DataSource with unknown type.");
398  return PLUS_FAIL;
399  }
400  }
401 
402  if (this->Internal->DepthStream.Id.empty())
403  {
404  LOG_ERROR("Required DEPTH type data source is missing.");
405  return PLUS_FAIL;
406  }
407 
408  return PLUS_SUCCESS;
409 }
410 
411 //-----------------------------------------------------------------------------
412 PlusStatus vtkPlusRevopoint3DCamera::WriteConfiguration(vtkXMLDataElement* rootConfigElement)
413 {
414  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement);
415  return PLUS_SUCCESS;
416 }
417 
418 //----------------------------------------------------------------------------
420 {
421  return this->Internal->CheckBasicConfig();
422 }
423 
424 //----------------------------------------------------------------------------
426 {
427  if (this->Internal->AvailableStreamsFlag & RevoSourceType::DEPTH)
428  {
429  GetVideoSource(this->Internal->DepthStream.Id.c_str(), this->Internal->DepthStream.Source);
430  if (Internal->DepthStream.Source == nullptr)
431  {
432  LOG_ERROR("Invalid source for tool id " << this->Internal->DepthStream.Id);
433  return PLUS_FAIL;
434  }
435  }
436 
437  if (this->Internal->AvailableStreamsFlag & RevoSourceType::PCL)
438  {
439  GetVideoSource(this->Internal->PCLStream.Id.c_str(), this->Internal->PCLStream.Source);
440  if (Internal->PCLStream.Source == nullptr)
441  {
442  LOG_ERROR("Invalid source for tool id " << this->Internal->PCLStream.Id);
443  return PLUS_FAIL;
444  }
445  }
446 
447  if (this->Internal->Connect() != PLUS_SUCCESS)
448  {
449  return PLUS_FAIL;
450  }
451 
452  // Checking for configuration needs a connected camera
453  return this->Internal->UpdateConfig();
454 }
455 
456 //----------------------------------------------------------------------------
458 {
459  return this->Internal->Disconnect();
460 }
461 
462 //----------------------------------------------------------------------------
464 {
465  this->FrameNumber = 0;
466 
467  return this->Internal->Start();
468 }
469 
470 //----------------------------------------------------------------------------
472 {
473  return this->Internal->Stop();
474 }
475 
476 //----------------------------------------------------------------------------
477 
478 //----------------------------------------------------------------------------
480 {
481  this->Internal->Update();
482 
483  if ((this->Internal->AvailableStreamsFlag & RevoSourceType::DEPTH) && this->Internal->DepthStream.Source->GetNumberOfItems() == 0)
484  {
485  // depth output is raw depth data
486  LOG_TRACE("Setting up depth frame");
487  this->Internal->DepthStream.Source->SetImageType(US_IMG_BRIGHTNESS);
488  this->Internal->DepthStream.Source->SetPixelType(VTK_TYPE_UINT16);
489  this->Internal->DepthStream.Source->SetNumberOfScalarComponents(1);
490  this->Internal->DepthStream.Source->SetInputFrameSize(this->Internal->DepthStream.StreamProps.width, this->Internal->DepthStream.StreamProps.height, 1);
491  }
492 
493  if ((this->Internal->AvailableStreamsFlag & RevoSourceType::PCL) && this->Internal->PCLStream.Source->GetNumberOfItems() == 0)
494  {
495  LOG_TRACE("Setting up pcl frame");
496  this->Internal->PCLStream.Source->SetImageType(US_IMG_BRIGHTNESS);
497  this->Internal->PCLStream.Source->SetPixelType(VTK_TYPE_FLOAT32);
498  this->Internal->PCLStream.Source->SetNumberOfScalarComponents(3);
499  this->Internal->PCLStream.Source->SetInputFrameSize(this->Internal->PCLStream.StreamProps.width, this->Internal->PCLStream.StreamProps.height, 1);
500  }
501 
502  if (this->Internal->AvailableStreamsFlag & RevoSourceType::DEPTH)
503  {
504  if (!this->Internal->IsDepthFrameValid())
505  {
506  if (this->Internal->DepthStream.Source->GetNumberOfItems() == 0)
507  {
508  LOG_TRACE("Waiting for Revopoint Surface depth images");
509  }
510  else
511  {
512  LOG_WARNING("Failed to get Revopoint Surface depth image");
513  }
514  return PLUS_FAIL;
515  }
516  FrameSizeType frameSizeDepth = {static_cast<unsigned>(this->Internal->DepthStream.StreamProps.width), static_cast<unsigned>(this->Internal->DepthStream.StreamProps.height), 1};
517  if (this->Internal->DepthStream.Source->AddItem(reinterpret_cast<void*>(const_cast<char*>(this->Internal->DepthFrame->getData(FRAME_DATA_FORMAT::FRAME_DATA_FORMAT_Z16))), this->Internal->DepthStream.Source->GetInputImageOrientation(), frameSizeDepth, VTK_TYPE_UINT16, 1, US_IMG_BRIGHTNESS, 0, this->FrameNumber) == PLUS_FAIL)
518  {
519  LOG_ERROR("Unable to send DEPTH image. Skipping frame.");
520  return PLUS_FAIL;
521  }
522  this->Modified();
523  }
524 
525  if (this->Internal->AvailableStreamsFlag & RevoSourceType::PCL)
526  {
527  FrameSizeType frameSizeDepth = {static_cast<unsigned>(this->Internal->PCLStream.StreamProps.width), static_cast<unsigned>(this->Internal->PCLStream.StreamProps.height), 1};
528  if (this->Internal->PCLStream.Source->AddItem(reinterpret_cast<void*>(this->Internal->PCLFrame.get()), this->Internal->PCLStream.Source->GetInputImageOrientation(), frameSizeDepth, VTK_TYPE_FLOAT32, 3, US_IMG_BRIGHTNESS, 0, this->FrameNumber) == PLUS_FAIL)
529  {
530  LOG_ERROR("Unable to send PCL image. Skipping frame.");
531  return PLUS_FAIL;
532  }
533  this->Modified();
534  }
535 
536  this->FrameNumber++;
537  return PLUS_SUCCESS;
538 }
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
vtkStandardNewMacro(vtkPlusRevopoint3DCamera)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
bool RequireImageOrientationInConfiguration
double AcquisitionRate
#define PLUS_FAIL
Definition: PlusCommon.h:43
virtual PlusStatus Disconnect()
PlusStatus WriteConfiguration(vtkXMLDataElement *config)
virtual PlusStatus InternalDisconnect()
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
virtual PlusStatus Connect()
double InternalUpdateRate
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
const char const char * value
Definition: phidget22.h:5111
bool StartThreadForInternalUpdates
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *config)
Interface class to Revopoint 3D cameras.
PlusStatus GetVideoSource(const char *aSourceId, vtkPlusDataSource *&aVideoSource)
virtual void PrintSelf(ostream &os, vtkIndent indent)
Interface to a 3D positioning tool, video source, or generalized data stream.