PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusBkProFocusCameraLinkVideoSource.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"
8 
11 #include "vtkImageData.h"
12 #include "vtkImageImport.h"
13 #include "vtkPlusChannel.h"
14 #include "vtkPlusDataSource.h"
15 #include "vtkPlusRfProcessor.h"
18 
19 // BK Includes
20 #include "AcquisitionGrabberSapera.h"
21 #include "AcquisitionInjector.h"
22 #include "AcquisitionSettings.h"
23 #include "BmodeViewDataReceiver.h"
24 #include "CommandAndControl.h"
25 #include "ParamConnectionSettings.h"
26 #include "SaperaViewDataReceiver.h"
27 #include "TcpClient.h"
28 
29 #include "vtkMath.h"
30 
31 //----------------------------------------------------------------------------
32 
34 
35 //----------------------------------------------------------------------------
36 std::string ParseResponse(std::string str, int item)
37 {
38  int nItems = 0;
39  std::string rest = str;
40  while (rest.size())
41  {
42  int pos = rest.find(",");
43  std::string curItem;
44  if (pos == -1)
45  {
46  curItem = rest;
47  rest = "";
48  }
49  else
50  {
51  curItem = rest.substr(0, pos);
52  rest = rest.substr(pos + 1, rest.length());
53  }
54  if (nItems++ == item)
55  {
56  return curItem;
57  }
58  }
59  return "";
60 }
61 
62 //----------------------------------------------------------------------------
63 std::string ParseResponseQuoted(std::string str, int item)
64 {
65  std::string result = ParseResponse(str, item);
66  if (result.length() > 2)
67  { return result.substr(1, result.length() - 2); }
68  else
69  { return ""; }
70 }
71 
72 class vtkPlusBkProFocusCameraLinkVideoSource::vtkInternal
73 {
74 public:
76  vtkPlusChannel* Channel;
77 
79  bool SubscribeScanPlane;
80 
81  ParamConnectionSettings BKparamSettings; // parConnectSettings, for read/write settings from ini file
82 
83  AcquisitionInjector BKAcqInjector; // injector
84  AcquisitionSettings BKAcqSettings; // settings
85  AcquisitionGrabberSapera BKAcqSapera; // sapera
86  BmodeViewDataReceiver BKBModeView; // bmodeView;
87  SaperaViewDataReceiver* pBKSaperaView; // saperaView
89 
90  CmdCtrlSettings BKcmdCtrlSettings; // cmdCtrlSet
91  CommandAndControl* pBKcmdCtrl; // cmdctrl
92 
93  vtkPlusBkProFocusCameraLinkVideoSource::vtkInternal::vtkInternal(vtkPlusBkProFocusCameraLinkVideoSource* external)
94  : External(external)
95  , pBKSaperaView(NULL)
96  , pBKcmdCtrl(NULL)
98  , SubscribeScanPlane(false)
99  {
100  this->PlusReceiver.SetPlusVideoSource(this->External);
101  }
102 
103  virtual vtkPlusBkProFocusCameraLinkVideoSource::vtkInternal::~vtkInternal()
104  {
105  this->PlusReceiver.SetPlusVideoSource(NULL);
106  this->Channel = NULL;
107  delete this->pBKSaperaView;
108  this->pBKSaperaView = NULL;
109  delete this->pBKcmdCtrl;
110  this->pBKcmdCtrl = NULL;
111  this->External = NULL;
112  }
113 
114  PlusStatus vtkPlusBkProFocusCameraLinkVideoSource::vtkInternal::InitializeParametersFromOEM()
115  {
116  //WSAIF wsaif;
117  TcpClient* oemClient = (this->pBKcmdCtrl->GetOEMClient());
118  std::string value;
119 
120  // Explanation of the queries/responses is from the BK document
121  // "Product Specification for Pro Focus OEM Interface"
122  // DATA:TRANSDUCER:A "A","8848";
123  // the first value is the connector used, the second is the transducer type
124  value = QueryParameter(oemClient, "TRANSDUCER");
125  if (value.empty())
126  {
127  return PLUS_FAIL;
128  }
129  std::string transducer = ParseResponseQuoted(value, 1);
130  LOG_INFO("Transducer: " << transducer);
131  // DATA:SCAN_PLANE:A "S";
132  // reply depends on the transducer type; for 8848, it is either "T" (transverse) or "S"
133  // (sagittal). For the abdominal 8820, the response apparently is "" (!)
134  value = QueryParameter(oemClient, "SCAN_PLANE");
135  if (value.empty())
136  {
137  return PLUS_FAIL;
138  }
139  std::string scanPlane = ParseResponseQuoted(value, 0);
140  LOG_INFO("Scan plane: " << scanPlane);
141  this->CurrentPlane = (scanPlane.find("S") == std::string::npos ? vtkPlusBkProFocusCameraLinkVideoSource::Transverse : vtkPlusBkProFocusCameraLinkVideoSource::Sagittal);
142 
143  value = QueryParameter(oemClient, "B_FRAMERATE"); // DATA:B_FRAMERATE:A 17.8271;
144  if (value.empty())
145  {
146  return PLUS_FAIL;
147  }
148  float frameRate = atof(ParseResponse(value, 0).c_str());
149  LOG_INFO("Frame rate: " << frameRate);
150 
151  LOG_INFO("Queried value: " << value);
152  // DATA:B_GEOMETRY_SCANAREA:A
153  // StartLineX,StartLineY,StartLineAngle,StartDepth,StopLineX,StopLineY,StopLineAngle,StopDepth
154  // StartLineX/Y: coordinate of the start line origin in mm
155  // StartLineAngle: angle of the start line in radians
156  // StartDepth: start depth of the scanning area in m
157  // StopLineX/Y: coordinate of the stop line origin in mm
158  // StopDepth: stop depth of the scanning area in mm
159  value = QueryParameter(oemClient, "B_GEOMETRY_SCANAREA");
160  if (value.empty())
161  {
162  return PLUS_FAIL;
163  }
164  float startLineXMm = fabs(atof(ParseResponse(value, 0).c_str())) * 1000.;
165  float stopLineXMm = fabs(atof(ParseResponse(value, 4).c_str())) * 1000.;
166  float startAngleDeg =
167  vtkMath::DegreesFromRadians(atof(ParseResponse(value, 2).c_str()));
168  // start depth is defined at the distance from the outer surface of the transducer
169  // to the surface of the crystal. stop depth is from the outer surface to the scan depth.
170  // start depth has negative depth in this coordinate system (the transducer surface pixels are inside the image)
171  float startDepthMm = atof(ParseResponse(value, 3).c_str()) * 1000.;
172  float stopAngleDeg =
173  vtkMath::DegreesFromRadians(atof(ParseResponse(value, 6).c_str()));
174  float stopDepthMm = atof(ParseResponse(value, 7).c_str()) * 1000.;
175 
176  // DATA:B_SCANLINES_COUNT:A 517;
177  // Number of scanning lines in specified view
178  value = QueryParameter(oemClient, "B_SCANLINES_COUNT");
179  if (value.empty())
180  {
181  return PLUS_FAIL;
182  }
183  float scanlinesCount = atof(ParseResponse(value, 0).c_str());
184  value = QueryParameter(oemClient, "B_RF_LINE_LENGTH");
185  if (value.empty())
186  {
187  return PLUS_FAIL;
188  }
189  float rfLineLength = atof(ParseResponse(value, 0).c_str());
190 
191  // BK defines angles start at 9:00, not at 12:00
192  startAngleDeg = -(startAngleDeg - stopAngleDeg) / 2.;
193  stopAngleDeg = -startAngleDeg;
194 
195  // Update the RfProcessor with scan conversion parameters
196 
197  if (transducer == "8848")
198  {
199  if (scanPlane == "S")
200  {
201  LOG_DEBUG("Linear transducer");
203  {
204  // The current scan converter is not for a linear transducer, so change it now
205  vtkSmartPointer<vtkPlusUsScanConvertLinear> scanConverter = vtkSmartPointer<vtkPlusUsScanConvertLinear>::New();
206  this->Channel->GetRfProcessor()->SetScanConverter(scanConverter);
207  }
208  }
209  else if (scanPlane == "T")
210  {
211  LOG_DEBUG("Curvilinear transducer");
213  {
214  // The current scan converter is not for a curvilinear transducer, so change it now
215  vtkSmartPointer<vtkPlusUsScanConvertCurvilinear> scanConverter = vtkSmartPointer<vtkPlusUsScanConvertCurvilinear>::New();
216  this->Channel->GetRfProcessor()->SetScanConverter(scanConverter);
217  }
218  }
219  else
220  {
221  LOG_WARNING("Unknown transducer scan plane (" << scanPlane << "). Cannot determine transducer geometry.");
222  }
223  }
224  else
225  {
226  LOG_WARNING("Unknown transducer model (" << transducer << "). Cannot determine transducer geometry.");
227  }
228 
229  vtkPlusUsScanConvert* scanConverter = this->Channel->GetRfProcessor()->GetScanConverter();
230  if (scanConverter != NULL)
231  {
232  vtkPlusUsScanConvertLinear* scanConverterLinear = vtkPlusUsScanConvertLinear::SafeDownCast(scanConverter);
233  vtkPlusUsScanConvertCurvilinear* scanConverterCurvilinear = vtkPlusUsScanConvertCurvilinear::SafeDownCast(scanConverter);
234  if (scanConverterLinear != NULL)
235  {
236  scanConverterLinear->SetTransducerWidthMm(startLineXMm + stopLineXMm);
237  scanConverterLinear->SetImagingDepthMm(stopDepthMm);
238  }
239  else if (scanConverterCurvilinear != NULL)
240  {
241  // this is a predefined value for 8848 transverse array, which
242  // apparently cannot be queried from OEM. It is not clear if ROC is the distance to
243  // crystal surface or to the outer surface of the transducer (waiting for the response from BK).
244  scanConverterCurvilinear->SetOutputImageStartDepthMm(startDepthMm);
245  scanConverterCurvilinear->SetRadiusStartMm(9.74);
246  scanConverterCurvilinear->SetRadiusStopMm(stopDepthMm);
247  scanConverterCurvilinear->SetThetaStartDeg(startAngleDeg);
248  scanConverterCurvilinear->SetThetaStopDeg(stopAngleDeg);
249  }
250  else
251  {
252  LOG_WARNING("Unknown scan converter type: " << scanConverter->GetTransducerGeometry());
253  }
254 
255  scanConverter->SetTransducerName((std::string("BK-") + transducer + scanPlane).c_str());
256  }
257  else
258  {
259  LOG_WARNING("Scan converter is not defined in either manually or through the OEM interface");
260  }
261 
262  /* Not used in reconstruction
263  std::cout << "Queried value: " << value << std::endl;
264  // DATA:3D_SPACING:A 0.25;
265  // Returns the spacing between the frames;
266  // Fan-type movers return spacing in degrees
267  // Linear-type movers returm spacing in mm
268  value = QueryParameter(oemClient, "3D_SPACING");
269  std::cout << "Queried value: " << value << std::endl;
270  // DATA:3D_CAPTURE_AREA:A Left,Top,Right,Bottom;
271  // Returns the capture area for the acquisition in screen pixel coordinates
272  value = QueryParameter(oemClient, "3D_CAPTURE_AREA");
273  std::cout << "Queried value: " << value << std::endl;
274  */
275 
276  return PLUS_SUCCESS;
277  }
278 
279  std::string vtkPlusBkProFocusCameraLinkVideoSource::vtkInternal::QueryParameter(TcpClient* oemClient, const char* parameter)
280  {
281  std::string query;
282  char buffer[1024];
283 
284  query = std::string("QUERY:") + parameter + ":A;";
285  oemClient->Write(query.c_str(), strlen(query.c_str()));
286  try
287  {
288  oemClient->Read(&buffer[0], 1024);
289  }
290  catch (TcpClientWaitException exc)
291  {
292  LOG_WARNING("QueryParameter::" << exc.Message);
293  return "";
294  }
295  std::string value = std::string(&buffer[0]);
296  std::string prefix = std::string("DATA:") + parameter + ":A ";
297  return value.substr(prefix.length(), value.length() - prefix.length() - 1);
298  }
299 
300  void vtkPlusBkProFocusCameraLinkVideoSource::vtkInternal::RegisterEventCallback(void* owner, void (*func)(void*, char*, size_t))
301  {
302  if (this->pBKcmdCtrl != NULL)
303  {
304  this->pBKcmdCtrl->RegisterEventCallback(owner, func);
305  }
306  }
307 };
308 
309 //----------------------------------------------------------------------------
311 {
312  this->Internal = new vtkInternal(this);
313 
314  this->IniFileName = NULL;
315  this->ShowSaperaWindow = false;
316  this->ShowBModeWindow = false;
317 
318  this->ImagingMode = RfMode;
319  SetLogFunc(LogInfoMessageCallback);
320  SetDbgFunc(LogDebugMessageCallback);
321 
323 
324  // No need for StartThreadForInternalUpdates, as we are notified about each new frame through a callback function
325 }
326 
327 //----------------------------------------------------------------------------
329 {
330  SetIniFileName(NULL);
331 
332  delete this->Internal;
333  this->Internal = NULL;
334 }
335 
336 //----------------------------------------------------------------------------
337 void vtkPlusBkProFocusCameraLinkVideoSource::PrintSelf(ostream& os, vtkIndent indent)
338 {
339  this->Superclass::PrintSelf(os, indent);
340 }
341 
342 //----------------------------------------------------------------------------
344 {
345  LOG_INFO(msg);
346 }
347 
348 //----------------------------------------------------------------------------
350 {
351  LOG_INFO(msg);
352 }
353 
354 //----------------------------------------------------------------------------
355 void vtkPlusBkProFocusCameraLinkVideoSource::EventCallback(void* owner, char* eventText, size_t eventTextLength)
356 {
357  vtkPlusBkProFocusCameraLinkVideoSource* self = static_cast<vtkPlusBkProFocusCameraLinkVideoSource*>(owner);
358 
359  igsioLockGuard<vtkIGSIORecursiveCriticalSection> critSectionGuard(self->UpdateMutex);
360 
361  if (self->Internal->SubscribeScanPlane && !_strnicmp("SCAN_PLANE", &eventText[strlen("SDATA:")], strlen("SCAN_PLANE")))
362  {
363  char* probeId = &eventText[strlen("SDATA:SCAN_PLANE:")];
364  std::string eventStr(probeId);
365  std::string details = eventStr.substr(eventStr.find(' '));
366  if (details.find('S') != std::string::npos)
367  {
368  self->Internal->CurrentPlane = Sagittal;
369  }
370  if (details.find('T') != std::string::npos)
371  {
372  self->Internal->CurrentPlane = Transverse;
373  }
374  self->Internal->Channel = self->FindChannelByPlane();
375  if (self->Internal->Channel == NULL)
376  {
377  LOG_ERROR("Unable to find a channel by plane. Check configuration.");
378  return;
379  }
380  if (self->ChannelConfiguredMap.find(self->Internal->Channel) == self->ChannelConfiguredMap.end()
381  || self->ChannelConfiguredMap[self->Internal->Channel] == false)
382  {
383  self->Internal->BKAcqSapera.StopGrabbing();
384  self->Internal->InitializeParametersFromOEM();
385  self->Internal->BKAcqSapera.StartGrabbing(&self->Internal->BKAcqInjector);
386  self->ChannelConfiguredMap[self->Internal->Channel] = true;
387  }
388  }
389 }
390 
391 //----------------------------------------------------------------------------
393 {
394  std::string iniFilePath;
395  GetFullIniFilePath(iniFilePath);
396  if (!this->Internal->BKparamSettings.LoadSettingsFromIniFile(iniFilePath.c_str()))
397  {
398  LOG_ERROR("Could not load BK parameter settings from file: " << iniFilePath.c_str());
399  return PLUS_FAIL;
400  }
401 
402  LOG_DEBUG("BK scanner address: " << this->Internal->BKparamSettings.GetScannerAddress());
403  LOG_DEBUG("BK scanner OEM port: " << this->Internal->BKparamSettings.GetOemPort());
404  LOG_DEBUG("BK scanner toolbox port: " << this->Internal->BKparamSettings.GetToolboxPort());
405 
406  this->Internal->BKcmdCtrlSettings.LoadFromIniFile(iniFilePath.c_str());
407 
408  if (!this->Internal->BKAcqSettings.LoadIni(iniFilePath.c_str()))
409  {
410  LOG_ERROR("Failed to load acquisition settings from file: " << iniFilePath.c_str());
411  return PLUS_FAIL;
412  }
413 
414  this->Internal->BKcmdCtrlSettings.autoUpdate = true;
415  this->Internal->pBKcmdCtrl = new CommandAndControl(&this->Internal->BKparamSettings, &this->Internal->BKcmdCtrlSettings);
416  this->Internal->BKcmdCtrlSettings = this->Internal->pBKcmdCtrl->GetCmdCtrlSettings(); // Get what has not failed !!!
417 
418  if (this->Internal->InitializeParametersFromOEM() != PLUS_SUCCESS)
419  {
420  LOG_ERROR("Unable to initialize BK parameters.");
421  return PLUS_FAIL;
422  }
423 
424  int numSamples = 0;
425  int numLines = 0;
426  if (!this->Internal->pBKcmdCtrl->CalcSaperaBufSize(&numSamples, &numLines))
427  {
428  LOG_ERROR("Failed to get Sapera framegrabber buffer size for RF data");
429  delete this->Internal->pBKcmdCtrl;
430  this->Internal->pBKcmdCtrl = NULL;
431  return PLUS_FAIL;
432  }
433 
434  LOG_DEBUG("Sapera buffer size: numSamples=" << numSamples << ", numLines=" << numLines);
435 
436  this->Internal->BKAcqSettings.SetLinesPerFrame(numLines);
437  this->Internal->BKAcqSettings.SetRFLineLength(numSamples);
438  this->Internal->BKAcqSettings.SetFramesToGrab(0); // continuous
439 
440  if (this->Internal->SubscribeScanPlane)
441  {
442  LOG_INFO("Subscribing to scan plane events.");
443  this->Internal->pBKcmdCtrl->SubscribeScanPlaneEvents();
444  this->Internal->RegisterEventCallback((void*)this, EventCallback);
445  }
446 
447  if (!this->Internal->BKAcqSapera.Init(this->Internal->BKAcqSettings))
448  {
449  LOG_ERROR("Failed to initialize framegrabber");
450  delete this->Internal->pBKcmdCtrl;
451  this->Internal->pBKcmdCtrl = NULL;
452  return PLUS_FAIL;
453  }
454 
455  this->Internal->pBKSaperaView = new SaperaViewDataReceiver(this->Internal->BKAcqSapera.GetBuffer());
456  if (this->ShowSaperaWindow)
457  {
458  // show Sapera viewer
459  this->Internal->BKAcqInjector.AddDataReceiver(this->Internal->pBKSaperaView);
460  }
461 
462  if (this->ShowBModeWindow)
463  {
464  // show B-mode image
465  this->Internal->BKAcqInjector.AddDataReceiver(&this->Internal->BKBModeView);
466  }
467 
468  // send frames to this video source
469  this->Internal->BKAcqInjector.AddDataReceiver(&this->Internal->PlusReceiver);
470 
471  // Clear buffer on connect because the new frames that we will acquire might have a different size
472  for (ChannelContainerIterator it = this->OutputChannels.begin(); it != this->OutputChannels.end(); ++it)
473  {
474  (*it)->Clear();
475  }
476 
477  return PLUS_SUCCESS;
478 }
479 
480 //----------------------------------------------------------------------------
482 {
483  this->Internal->BKAcqSapera.Destroy();
484 
485  this->Internal->BKAcqInjector.RemoveDataReceiver(&this->Internal->PlusReceiver);
486 
487  if (this->ShowBModeWindow)
488  {
489  this->Internal->BKAcqInjector.RemoveDataReceiver(&this->Internal->BKBModeView);
490  }
491 
492  if (this->ShowSaperaWindow)
493  {
494  this->Internal->BKAcqInjector.RemoveDataReceiver(this->Internal->pBKSaperaView);
495  }
496 
497  delete this->Internal->pBKSaperaView;
498  this->Internal->pBKSaperaView = NULL;
499  delete this->Internal->pBKcmdCtrl;
500  this->Internal->pBKcmdCtrl = NULL;
501  return PLUS_SUCCESS;
502 }
503 
504 //----------------------------------------------------------------------------
506 {
507  if (!this->Internal->BKAcqSapera.StartGrabbing(&this->Internal->BKAcqInjector))
508  {
509  LOG_ERROR("Failed to start grabbing");
510  return PLUS_FAIL;
511  }
512  return PLUS_SUCCESS;
513 }
514 
515 //----------------------------------------------------------------------------
517 {
518  /*
519  Sleep(500);
520  if (!this->Internal->BKAcqSapera.StopGrabbing())
521  {
522  LOG_ERROR("Failed to start grabbing");
523  return PLUS_FAIL;
524  }
525  */
526  return PLUS_SUCCESS;
527 }
528 
529 //----------------------------------------------------------------------------
530 void vtkPlusBkProFocusCameraLinkVideoSource::NewFrameCallback(void* pixelDataPtr, const FrameSizeType& inputFrameSizeInPix, igsioCommon::VTKScalarPixelType pixelType, US_IMAGE_TYPE imageType)
531 {
532  igsioLockGuard<vtkIGSIORecursiveCriticalSection> critSectionGuard(this->UpdateMutex);
533 
534  // we may need to overwrite these, so create a copy that will be used internally
535  FrameSizeType frameSizeInPix =
536  {
537  inputFrameSizeInPix[0],
538  inputFrameSizeInPix[1],
539  inputFrameSizeInPix[2]
540  };
541 
542  LOG_TRACE("New frame received: " << frameSizeInPix[0] << "x" << frameSizeInPix[1]
543  << ", pixel type: " << vtkImageScalarTypeNameMacro(pixelType)
544  << ", image type: " << igsioCommon::GetStringFromUsImageType(imageType));
545 
547 
548  if (channel == NULL)
549  {
550  std::string type("Unknown");
551  if (this->Internal->CurrentPlane == Sagittal)
552  {
553  type = "Sagittal";
554  }
555  if (this->Internal->CurrentPlane == Transverse)
556  {
557  type = "Transverse";
558  }
559  LOG_ERROR("No channel returned. Verify configuration. Requested plane: " << type);
560  return;
561  }
562 
563  switch (this->ImagingMode)
564  {
565  case RfMode:
566  {
567  if (imageType == US_IMG_RF_REAL || imageType == US_IMG_RF_IQ_LINE || imageType == US_IMG_RF_I_LINE_Q_LINE)
568  {
569  // RF image is received and RF image is needed => no need for conversion
570  break;
571  }
572  LOG_ERROR("The received frame is discarded, as it cannot be convert from " << igsioCommon::GetStringFromUsImageType(imageType) << " to RF");
573  return;
574  }
575  case BMode:
576  {
577  if (imageType == US_IMG_BRIGHTNESS)
578  {
579  // B-mode image is received and B-mode image is needed => no need for conversion
580  break;
581  }
582  else if (imageType == US_IMG_RF_REAL || imageType == US_IMG_RF_IQ_LINE || imageType == US_IMG_RF_I_LINE_Q_LINE)
583  {
584  // convert from RF to Brightness
585 
586  // Create a VTK image input for the RF to Brightness converter
587  vtkSmartPointer<vtkImageImport> bufferToVtkImage = vtkSmartPointer<vtkImageImport>::New();
588  bufferToVtkImage->SetDataScalarType(pixelType);
589  bufferToVtkImage->SetImportVoidPointer((unsigned char*)pixelDataPtr);
590  bufferToVtkImage->SetDataExtent(0, frameSizeInPix[0] - 1, 0, frameSizeInPix[1] - 1, 0, 0);
591  bufferToVtkImage->SetWholeExtent(0, frameSizeInPix[0] - 1, 0, frameSizeInPix[1] - 1, 0, 0);
592  bufferToVtkImage->Update();
593 
594  channel->GetRfProcessor()->SetRfFrame(bufferToVtkImage->GetOutput(), imageType);
595  channel->SetSaveRfProcessingParameters(true); // RF processing parameters were used, make sure they will be saved into the config file
596 
597  // Overwrite the input parameters with the converted image; it will look as if we received a B-mode image
598  vtkImageData* convertedBmodeImage = channel->GetRfProcessor()->GetBrightnessScanConvertedImage();
599  pixelDataPtr = convertedBmodeImage->GetScalarPointer();
600  int* resultExtent = convertedBmodeImage->GetExtent();
601  frameSizeInPix[0] = resultExtent[1] - resultExtent[0] + 1;
602  frameSizeInPix[1] = resultExtent[3] - resultExtent[2] + 1;
603  pixelType = convertedBmodeImage->GetScalarType();
604  imageType = US_IMG_BRIGHTNESS;
605  break;
606  }
607  LOG_ERROR("The received frame is discarded, as it cannot be convert from " << igsioCommon::GetStringFromUsImageType(imageType) << " to Brightness");
608  return;
609  }
610  default:
611  LOG_ERROR("The received frame is discarded, as the requested imaging mode (" << igsioCommon::GetStringFromUsImageType(imageType) << ") is not supported");
612  return;
613  }
614 
615  vtkPlusDataSource* aSource(NULL);
616  if (channel->GetVideoSource(aSource) != PLUS_SUCCESS)
617  {
618  LOG_ERROR("Output channel does not have video source. Unable to record a new frame.");
619  return;
620  }
621  // If the buffer is empty, set the pixel type and frame size to the first received properties
622  if (aSource->GetNumberOfItems() == 0)
623  {
624  LOG_DEBUG("Set up BK ProFocus image buffer");
625  aSource->SetPixelType(pixelType);
626  aSource->SetImageType(imageType);
627  aSource->SetInputFrameSize(frameSizeInPix[0], frameSizeInPix[1], 1);
628  if (imageType == US_IMG_BRIGHTNESS)
629  {
630  // Store B-mode images in MF orientation
631  aSource->SetOutputImageOrientation(US_IMG_ORIENT_MF);
632  }
633  else
634  {
635  // RF data is stored line-by-line, therefore set the temporary storage buffer to FM orientation
636  aSource->SetOutputImageOrientation(US_IMG_ORIENT_FM);
637  }
638  LOG_INFO("Frame size: " << frameSizeInPix[0] << "x" << frameSizeInPix[1]
639  << ", pixel type: " << vtkImageScalarTypeNameMacro(pixelType)
640  << ", image type: " << igsioCommon::GetStringFromUsImageType(imageType)
641  << ", device image orientation: " << igsioCommon::GetStringFromUsImageOrientation(aSource->GetInputImageOrientation())
642  << ", buffer image orientation: " << igsioCommon::GetStringFromUsImageOrientation(aSource->GetOutputImageOrientation()));
643 
644  }
645 
646  aSource->AddItem(pixelDataPtr, aSource->GetInputImageOrientation(), frameSizeInPix, pixelType, 1, imageType, 0, this->FrameNumber);
647  this->Modified();
648  this->FrameNumber++;
649 
650  // just for testing: igsioVideoFrame::SaveImageToFile( (unsigned char*)pixelDataPtr, frameSizeInPix, numberOfBitsPerPixel, (char *)"test.jpg");
651 }
652 
653 
654 //-----------------------------------------------------------------------------
656 {
657  this->ChannelConfiguredMap.clear();
658 
659  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
660 
661  XML_READ_CSTRING_ATTRIBUTE_REQUIRED(IniFileName, deviceConfig);
662  XML_READ_ENUM2_ATTRIBUTE_OPTIONAL(ImagingMode, deviceConfig, "BMode", BMode, "RfMode", RfMode);
663 
664  const char* subscribe = deviceConfig->GetAttribute("SubscribeScanPlane");
665  if (subscribe != NULL)
666  {
667  this->Internal->SubscribeScanPlane = (STRCASECMP(subscribe, "TRUE") == 0);
668  }
669 
670  if (this->Internal->SubscribeScanPlane && this->OutputChannels.size() != 2)
671  {
672  LOG_ERROR("Scan plane switching requested but there are not exactly two output channels.");
673  return PLUS_FAIL;
674  }
675 
676  return PLUS_SUCCESS;
677 }
678 
679 //-----------------------------------------------------------------------------
681 {
682  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceElement, rootConfigElement);
683  deviceElement->SetAttribute("IniFileName", this->IniFileName);
684  return PLUS_SUCCESS;
685 }
686 
687 //-----------------------------------------------------------------------------
689 {
690  if (this->IniFileName == NULL)
691  {
692  LOG_ERROR("Ini file name has not been set");
693  return PLUS_FAIL;
694  }
695  fullPath = vtkPlusConfig::GetInstance()->GetDeviceSetConfigurationDirectory() + std::string("/") + this->IniFileName;
696  return PLUS_SUCCESS;
697 }
698 
699 //-----------------------------------------------------------------------------
701 {
702  this->ImagingMode = imagingMode;
703  // always keep the receiver in RF mode and if B-mode image is requested then do the B-mode conversion in this class
704  // this->Internal->PlusReceiver.SetImagingMode(imagingMode);
705 }
706 
707 //----------------------------------------------------------------------------
709 {
710  if (!this->Internal->SubscribeScanPlane && this->OutputChannels.size() > 1)
711  {
712  LOG_WARNING("vtkPlusBkProFocusCameraLinkVideoSource is expecting one output channel and there are " << this->OutputChannels.size() << " channels. First output channel will be used.");
713  }
714 
715  if (this->OutputChannels.empty())
716  {
717  LOG_ERROR("No output channels defined for vtkPlusBkProFocusCameraLinkVideoSource. Cannot proceed.");
718  this->CorrectlyConfigured = false;
719  return PLUS_FAIL;
720  }
721 
722  if (!this->Internal->SubscribeScanPlane)
723  {
724  this->Internal->Channel = this->OutputChannels[0];
725  }
726  else
727  {
728  this->Internal->Channel = this->FindChannelByPlane();
729  }
730 
731  return PLUS_SUCCESS;
732 }
733 
734 //----------------------------------------------------------------------------
736 {
737  if (this->OutputChannels.size() != 2)
738  {
739  LOG_ERROR("vtkPlusBkProFocusCameraLinkVideoSource::FindChannelByPlane failed: expected two output channels");
740  return NULL;
741  }
742 
743  std::string desiredPlane;
744  if (this->Internal->CurrentPlane == Transverse)
745  {
746  desiredPlane = "Transverse";
747  }
748  else
749  {
750  desiredPlane = "Sagittal";
751  }
752 
753  for (ChannelContainerIterator it = this->OutputChannels.begin(); it != this->OutputChannels.end(); ++it)
754  {
755  std::string plane;
756  if ((*it)->GetCustomAttribute("Plane", plane) != PLUS_SUCCESS)
757  {
758  LOG_ERROR("Plane switching requested but channel \"" << (*it)->GetChannelId() << "\" doesn't have attribute \"Plane\" defined. Please fix configuration.");
759  continue;
760  }
761  if (plane.compare(desiredPlane) == 0)
762  {
763  return *it;
764  }
765  }
766  LOG_ERROR(desiredPlane << " requested but no channel with custom attribute Plane::" << desiredPlane << " detected.");
767  return NULL;
768 }
const char int const char * func
Definition: phidget22.h:2458
virtual vtkPlusRfProcessor * GetRfProcessor()
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
int * channel
Definition: phidget22.h:1303
virtual PlusStatus SetOutputImageOrientation(US_IMAGE_ORIENTATION imageOrientation)
virtual void SetThetaStartDeg(double)
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
std::string GetDeviceSetConfigurationDirectory()
virtual void SetScanConverter(vtkPlusUsScanConvert *scanConverter)
static vtkPlusUsScanConvertCurvilinear * SafeDownCast(vtkObject *o)
virtual void SetIniFileName(const char *)
virtual void SetImagingDepthMm(double)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
virtual vtkPlusUsScanConvert * GetScanConverter()
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *config)
PlusStatus SetInputFrameSize(unsigned int x, unsigned int y, unsigned int z)
virtual PlusStatus AddItem(vtkImageData *frame, US_IMAGE_ORIENTATION usImageOrientation, US_IMAGE_TYPE imageType, long frameNumber, double unfilteredTimestamp=UNDEFINED_TIMESTAMP, double filteredTimestamp=UNDEFINED_TIMESTAMP, const igsioFieldMapType *customFields=NULL)
virtual void SetThetaStopDeg(double)
std::string ParseResponseQuoted(std::string str, int item)
bool RequireImageOrientationInConfiguration
PlusStatus SetImageType(US_IMAGE_TYPE imageType)
virtual void SetRadiusStopMm(double)
#define PLUS_FAIL
Definition: PlusCommon.h:43
PlusStatus SetPixelType(igsioCommon::VTKScalarPixelType pixelType)
static vtkPlusConfig * GetInstance()
vtkStandardNewMacro(vtkPlusBkProFocusCameraLinkVideoSource)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
unsigned long FrameNumber
virtual void SetOutputImageStartDepthMm(double)
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
vtkIGSIORecursiveCriticalSection * UpdateMutex
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
virtual void SetTransducerWidthMm(double)
const char const char * value
Definition: phidget22.h:5111
virtual US_IMAGE_ORIENTATION GetInputImageOrientation()
int VTKScalarPixelType
Definition: PlusCommon.h:55
This class performs scan conversion from scan lines for curvilinear probes.
void NewFrameCallback(void *pixelDataPtr, const FrameSizeType &frameSizeInPix, igsioCommon::VTKScalarPixelType pixelType, US_IMAGE_TYPE imageType)
Class for acquiring ultrasound images from BK ProFocus scanners.
Contains an optional timestamped circular buffer containing the video images and a number of timestam...
virtual const char * GetTransducerGeometry()=0
std::string ParseResponse(std::string str, int item)
ChannelContainer OutputChannels
virtual void SetTransducerName(const char *)
This is a base class for defining a common scan conversion algorithm interface for all kinds of probe...
virtual US_IMAGE_ORIENTATION GetOutputImageOrientation()
This class performs scan conversion from scan lines for curvilinear probes.
virtual void SetPlusVideoSource(vtkPlusBkProFocusCameraLinkVideoSource *videoSource)
Class for receiving images through the BK ProFocus SDK (Grabbie)
static vtkPlusUsScanConvertLinear * SafeDownCast(vtkObject *o)
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *config)
ChannelContainer::iterator ChannelContainerIterator
Definition: vtkPlusDevice.h:36
virtual void SetRadiusStartMm(double)
virtual int GetNumberOfItems()
bool CorrectlyConfigured
Interface to a 3D positioning tool, video source, or generalized data stream.