PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusBkProFocusOemVideoSource.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 // GRAB_FRAME API was partly contributed by by Xin Kang at SZI, Children's National Medical Center
8 
9 // Local includes
10 #include "PlusConfigure.h"
11 #include "PlusCommon.h"
12 #include "PixelCodec.h"
14 #include "vtkPlusChannel.h"
15 #include "vtkPlusDataSource.h"
16 
17 // VTK includes
18 #include <vtkImageData.h>
19 #include <vtkObjectFactory.h>
20 #include <vtk_png.h>
21 #include <vtksys/SystemTools.hxx>
22 #include <vtkClientSocket.h>
23 #include <vtkMath.h>
24 
25 // pngpriv is not copied into install directory with the rest of the vtkpng classes
26 // This CMake configured header specifies the path to the header in the source code
27 // #include <vtkpng/pngpriv.h>
28 #include <vtkPNGPrivate.h>
29 
30 // STL includes
31 #include <iostream>
32 #include <ostream>
33 #include <string>
34 
35 // System includes
36 #include <stdint.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 static const int TIMESTAMP_SIZE = 4;
42 
43 const char* vtkPlusBkProFocusOemVideoSource::KEY_DEPTH = "Depth";
45 
47 
48 class vtkPlusBkProFocusOemVideoSource::vtkInternal
49 {
50 public:
52 
53  vtkPlusChannel* Channel;
54 
55  vtkSmartPointer<vtkClientSocket> VtkSocket;
56  std::vector<char> OemMessage;
57 
58  // Image buffer to hold the decoded image frames, it's a member variable to avoid memory allocation at each frame receiving
59  vtkImageData* DecodedImageFrame;
60  // Data buffer to hold temporary data during decoding, it's a member variable to avoid memory allocation at each frame receiving
61  std::vector<unsigned char> DecodingBuffer;
62  // Data buffer to hold temporary data during decoding (pointers to image lines), it's a member variable to avoid memory allocation at each frame receiving
63  std::vector<png_bytep> DecodingLineBuffer;
64 
65 
66  vtkInternal(vtkPlusBkProFocusOemVideoSource* external)
67  : External(external)
68  , Channel(NULL)
69  {
70  this->DecodedImageFrame = vtkImageData::New();
71  }
72 
73  virtual ~vtkInternal()
74  {
75  this->Channel = NULL;
76  this->DecodedImageFrame->Delete();
77  this->DecodedImageFrame = NULL;
78  this->External = NULL;
79  }
80 
81 };
82 
83 
84 //----------------------------------------------------------------------------
86 {
87  this->Internal = new vtkInternal(this);
88 
89  this->ScannerAddress = "";
90  this->OemPort = 0;
91 
92  this->UltrasoundWindowSize[0] = 0;
93  this->UltrasoundWindowSize[1] = 0;
94  this->ContinuousStreamingEnabled = false;
95  this->ColorEnabled = false;
96  this->OfflineTesting = false;
97  this->OfflineTestingFilePath = NULL;
98  this->StartLineX_m = 0;
99  this->StartLineY_m = 0;
100  this->StartLineAngle_rad = 0;
101  this->StartDepth_m = 0;
102  this->StopLineX_m = 0;
103  this->StopLineY_m = 0;
104  this->StopLineAngle_rad = 0;
105  this->StopDepth_m = 0;
106  this->pixelLeft_pix = 0;
107  this->pixelTop_pix = 0;
108  this->pixelRight_pix = 0;
109  this->pixelBottom_pix = 0;
110  this->tissueLeft_m = 0;
111  this->tissueTop_m = 0;
112  this->tissueRight_m = 0;
113  this->tissueBottom_m = 0;
114  this->gain_percent = 0;
115  this->probeTypePortA = UNKNOWN;
116  this->probeTypePortB = UNKNOWN;
117  this->probeTypePortC = UNKNOWN;
118  this->probeTypePortM = UNKNOWN;
119  this->probePort = "";
120 
122 
123  // No callback function provided by the device, so the data capture thread will be used to poll the hardware and add new items to the buffer
124  this->StartThreadForInternalUpdates = true;
125  this->AcquisitionRate = 1; // image retrieval may slow down the exam software, so keep the frame rate low by default
126 }
127 
128 //----------------------------------------------------------------------------
130 {
131  if (!this->Connected)
132  {
133  this->Disconnect();
134  }
135 
136  delete this->Internal;
137  this->Internal = NULL;
138  this->ScannerAddress = "";
139  this->OfflineTestingFilePath = NULL;
140 }
141 
142 //----------------------------------------------------------------------------
143 void vtkPlusBkProFocusOemVideoSource::PrintSelf(ostream& os, vtkIndent indent)
144 {
145  this->Superclass::PrintSelf(os, indent);
146 
147 }
148 
149 //----------------------------------------------------------------------------
151 {
152  LOG_TRACE("vtkPlusBkProFocusOemVideoSource::InternalConnect");
153 
154  if (this->Internal->Channel == NULL)
155  {
156  if (this->OutputChannels.empty())
157  {
158  LOG_ERROR("Cannot connect: no output channel is specified for device " << this->GetDeviceId());
159  return PLUS_FAIL;
160  }
161  this->Internal->Channel = this->OutputChannels[0];
162  }
163 
164  // Clear buffer on connect because the new frames that we will acquire might have a different size
165  this->Internal->Channel->Clear();
166  this->Internal->VtkSocket = vtkSmartPointer<vtkClientSocket>::New();
167 
168  LOG_DEBUG("BK scanner address: " << this->ScannerAddress);
169  LOG_DEBUG("BK scanner OEM port: " << this->OemPort);
170  LOG_DEBUG("BK scanner Acquisition rate: " << this->AcquisitionRate);
171 
172  if (this->OfflineTesting)
173  {
174  LOG_INFO("Offline testing on");
175  LOG_DEBUG("Offline testing file path: " << this->OfflineTestingFilePath);
176  }
177  else
178  {
179  LOG_DEBUG("Connecting to BK scanner");
180  bool connected = (this->Internal->VtkSocket->ConnectToServer(this->ScannerAddress.c_str(), this->OemPort) == 0);
181  if (!connected)
182  {
183  LOG_ERROR("Could not connect to BKProFocusOem:"
184  << " scanner address = " << this->ScannerAddress
185  << ", OEM port = " << this->OemPort);
186  return PLUS_FAIL;
187  }
188  LOG_DEBUG("Connected to BK scanner");
189 
190  if (!(this->RequestParametersFromScanner()
191  && this->ConfigEventsOn()
192  && this->SubscribeToParameterChanges()
193  ))
194  {
195  LOG_ERROR("Cound not init BK scanner");
196  return PLUS_FAIL;
197  }
198  if (this->ContinuousStreamingEnabled)
199  {
200  if (!this->StartContinuousDataStreaming())
201  {
202  return PLUS_FAIL;
203  }
204 
205  LOG_DEBUG("Connected to BK scanner");
206  }
207  }
208  return PLUS_SUCCESS;
209 }
210 
211 //----------------------------------------------------------------------------
213 {
214  std::string query;
215 
216  /* Build the query string including the acquisition rate
217  as defined in the config file.
218  Example - Colour Enabled "QUERY:GRAB_FRAME \"ON\",20,\"OVERLAY\";";
219  Example - Colour Disabled "QUERY:GRAB_FRAME \"ON\",20;";
220  */
221 
222  query = "QUERY:GRAB_FRAME \"ON\",";
223 
224  std::stringstream ss;
225  ss << this->AcquisitionRate;
226  query += ss.str().c_str();
227 
228  if (this->ColorEnabled)
229  {
230  //Switch to power doppler
231  /*if (this->CommandPowerDopplerOn() != PLUS_SUCCESS)
232  {
233  return PLUS_FAIL;
234  }*/
235  query += ",\"OVERLAY\"";
236  }
237 
238  query += ";";
239 
240  LOG_DEBUG("Start data streaming. Query: " << query);
241  if (!SendQuery(query))
242  {
243  return PLUS_FAIL;
244  }
245  return PLUS_SUCCESS;
246 }
247 
248 //----------------------------------------------------------------------------
250 {
251  std::string query = "QUERY:GRAB_FRAME \"OFF\";";
252  LOG_DEBUG("Stop data streaming. Query: " << query);
253  if (!SendQuery(query))
254  {
255  return PLUS_FAIL;
256  }
257 
258  return PLUS_SUCCESS;
259 }
260 
261 //----------------------------------------------------------------------------
263 {
264  LOG_TRACE("Disconnect from BKProFocusOem");
265 
266  if (!this->OfflineTesting && this->ContinuousStreamingEnabled)
267  {
268  if (!this->StopContinuousDataStreaming())
269  {
270  return PLUS_FAIL;
271  }
272  }
273  this->StopRecording();
274 
275  if (this->Internal->VtkSocket->GetConnected())
276  {
277  this->Internal->VtkSocket->CloseSocket();
278  }
279 
280  return PLUS_SUCCESS;
281 }
282 
283 //----------------------------------------------------------------------------
285 {
286  return PLUS_SUCCESS;
287 }
288 
289 //----------------------------------------------------------------------------
291 {
292  return PLUS_SUCCESS;
293 }
294 
295 //----------------------------------------------------------------------------
297 {
298  if (!this->Recording)
299  {
300  // drop the frame, we are not recording data now
301  return PLUS_SUCCESS;
302  }
303 
304  unsigned char* uncompressedPixelBuffer = 0;
305  unsigned int uncompressedPixelBufferSize = 0;
306  int numBytesProcessed = 0;
307  if (!this->OfflineTesting)
308  {
309  if (!this->ContinuousStreamingEnabled)
310  {
311  std::string query = "query:capture_image \"PNG\";";
312  LOG_TRACE("Query from vtkPlusBkProFocusOemVideoSource: " << query);
313  if (!SendQuery(query))
314  {
315  return PLUS_FAIL;
316  }
317  }
318  //Process all incoming messages until an image message is found
319  if (!this->ProcessMessagesAndReadNextImage())
320  {
321  return PLUS_FAIL;
322  }
323  size_t numBytesReceived = this->Internal->OemMessage.size();
324 
325  // First detect the #
326  for (numBytesProcessed = 0; this->Internal->OemMessage[numBytesProcessed] != '#' && numBytesProcessed < numBytesReceived; numBytesProcessed++)
327  ;
328  numBytesProcessed++;
329 
330  int numChars = (int)this->Internal->OemMessage[numBytesProcessed] - (int)('0');
331  numBytesProcessed++;
332  LOG_TRACE("Number of bytes in the image size: " << numChars); // 7 or 6
333  if (numChars == 0)
334  {
335  LOG_ERROR("Failed to read image from BK OEM interface");
336  return PLUS_FAIL;
337  }
338 
339  for (int k = 0; k < numChars; k++, numBytesProcessed++)
340  {
341  uncompressedPixelBufferSize = uncompressedPixelBufferSize * 10 + ((int)this->Internal->OemMessage[numBytesProcessed] - '0');
342  }
343  LOG_TRACE("uncompressedPixelBufferSize = " << uncompressedPixelBufferSize);
344 
345  uncompressedPixelBuffer = (unsigned char*) & (this->Internal->OemMessage[numBytesProcessed]);
346 
347  if (this->ContinuousStreamingEnabled)
348  {
349  // Extract timestamp of the image
350  char timeStamp[TIMESTAMP_SIZE];
351  for (int k = 0; k < TIMESTAMP_SIZE; k++, numBytesProcessed++)
352  {
353  timeStamp[k] = this->Internal->OemMessage[numBytesProcessed];
354  }
355  // Seems this is NOT correct, but the format is NOT described in the manual
356  unsigned int _timestamp = *(int*)timeStamp;
357  //LOG_TRACE("Image timestamp = " << static_cast<std::ostringstream*>(&(std::ostringstream() << _timestamp))->str());
358  }
359  }
360 
361  if (!this->OfflineTesting && this->ContinuousStreamingEnabled)
362  {
363  this->Internal->DecodedImageFrame->SetExtent(0, this->UltrasoundWindowSize[0] - 1, 0, this->UltrasoundWindowSize[1] - 1, 0, 0);
364 
365  if (uncompressedPixelBufferSize > (this->UltrasoundWindowSize[0] * this->UltrasoundWindowSize[1] + TIMESTAMP_SIZE))
366  {
367  // we received color image
368  this->Internal->DecodedImageFrame->AllocateScalars(VTK_UNSIGNED_CHAR, 3);
370  (unsigned char*) & (this->Internal->OemMessage[numBytesProcessed]),
371  (unsigned char*)this->Internal->DecodedImageFrame->GetScalarPointer());
372  }
373  else
374  {
375  this->Internal->DecodedImageFrame->AllocateScalars(VTK_UNSIGNED_CHAR, 1);
376  std::memcpy(this->Internal->DecodedImageFrame->GetScalarPointer(),
377  (void*) & (this->Internal->OemMessage[numBytesProcessed]),
378  uncompressedPixelBufferSize);
379  LOG_TRACE(uncompressedPixelBufferSize << " bytes copied, start at " << numBytesProcessed); // 29
380  }
381  }
382  else
383  {
384  if (DecodePngImage(uncompressedPixelBuffer, uncompressedPixelBufferSize, this->Internal->DecodedImageFrame) != PLUS_SUCCESS)
385  {
386  LOG_ERROR("Failed to decode received PNG image on channel " << this->Internal->Channel->GetChannelId());
387  return PLUS_FAIL;
388  }
389  }
390  this->FrameNumber++;
391 
392  vtkPlusDataSource* aSource(NULL);
393  if (this->Internal->Channel->GetVideoSource(aSource) != PLUS_SUCCESS)
394  {
395  LOG_ERROR("Unable to retrieve the video source in the BKProFocusOem device on channel " << this->Internal->Channel->GetChannelId());
396  return PLUS_FAIL;
397  }
398  // If the buffer is empty, set the pixel type and frame size to the first received properties
399  if (aSource->GetNumberOfItems() == 0)
400  {
401  LOG_DEBUG("Set up BK ProFocus image buffer");
402  int* frameDimensions = this->Internal->DecodedImageFrame->GetDimensions();
403  if (frameDimensions[0] < 0 || frameDimensions[1] < 0 || frameDimensions[2] < 0)
404  {
405  LOG_ERROR("Invalid frame dimensions.");
406  return PLUS_FAIL;
407  }
408  FrameSizeType frameSizeInPix = { static_cast<unsigned int>(frameDimensions[0]), static_cast<unsigned int>(frameDimensions[1]), static_cast<unsigned int>(frameDimensions[2]) };
409  aSource->SetPixelType(this->Internal->DecodedImageFrame->GetScalarType());
410  if (this->Internal->DecodedImageFrame->GetNumberOfScalarComponents() == 1)
411  {
412  aSource->SetImageType(US_IMG_BRIGHTNESS);
413  }
414  else
415  {
416  aSource->SetNumberOfScalarComponents(this->Internal->DecodedImageFrame->GetNumberOfScalarComponents());
417  aSource->SetImageType(US_IMG_RGB_COLOR);
418  }
419  aSource->SetInputFrameSize(frameSizeInPix[0], frameSizeInPix[1], frameSizeInPix[2]);
420 
421  LOG_DEBUG("Frame size: " << frameSizeInPix[0] << "x" << frameSizeInPix[1]
422  << ", pixel type: " << vtkImageScalarTypeNameMacro(this->Internal->DecodedImageFrame->GetScalarType())
423  << ", buffer image orientation: " << igsioCommon::GetStringFromUsImageOrientation(aSource->GetInputImageOrientation()));
424  LOG_DEBUG("NumberOfScalarComponents: " << aSource->GetNumberOfScalarComponents());
425  }
426 
427  double spacingZ_mm = 1.0;
428  //TODO: Send spacing with image
429  this->Internal->DecodedImageFrame->SetSpacing(GetSpacingX(), GetSpacingY(), spacingZ_mm);//Spacing is not being sent to IGTLink?
430 
432  if (aSource->AddItem(this->Internal->DecodedImageFrame, aSource->GetInputImageOrientation(), aSource->GetImageType(), this->FrameNumber, UNDEFINED_TIMESTAMP, UNDEFINED_TIMESTAMP, &this->FrameFields) != PLUS_SUCCESS)
433  {
434  LOG_ERROR("Error adding item to video source " << aSource->GetSourceId() << " on channel " << this->Internal->Channel->GetChannelId());
435  return PLUS_FAIL;
436  }
437  this->Modified();
438 
439  return PLUS_SUCCESS;
440 }
441 
442 //----------------------------------------------------------------------------
444 {
445  while (true)
446  {
447  LOG_TRACE("Before client read");
448  if (!this->ReadNextMessage())
449  {
450  LOG_ERROR("Failed to read response from BK OEM interface");
451  return PLUS_FAIL;
452  }
453 
454  std::string fullMessage = this->ReadBufferIntoString();
455  std::istringstream replyStream(fullMessage);
456 
457  std::string messageString;
458  std::getline(replyStream, messageString, ' ');
459 
460  std::istringstream messageStream(messageString);
461  std::string messageType;
462  std::string messageName;
463  std::string messageSubtype;//Typically A or B (view A or B on the scanner). Currently only view A is handled.
464  std::getline(messageStream, messageType, ':');
465  std::getline(messageStream, messageName, ':');
466  std::getline(messageStream, messageSubtype, ':');
467 
468  LOG_DEBUG("Process message from BK: " << fullMessage);
469 
470  if (messageString.compare("DATA:CAPTURE_IMAGE") == 0)
471  {
472  return PLUS_SUCCESS;
473  }
474  else if (messageString.compare("DATA:GRAB_FRAME") == 0)
475  {
476  return PLUS_SUCCESS;
477  }
478  //Handle both replies to queries (DATA) and subscribed data (SDATA)
479  else if ((messageType.compare("DATA") == 0) || (messageType.compare("SDATA") == 0))
480  {
481  if (messageName.compare("US_WIN_SIZE") == 0)
482  {
483  this->ParseImageSize(replyStream);
484  }
485  else if ((messageName.compare("B_GEOMETRY_SCANAREA") == 0) && (messageSubtype.compare("A") == 0))
486  {
487  this->ParseGeometryScanarea(replyStream);
488  }
489  else if ((messageName.compare("B_GEOMETRY_PIXEL") == 0) && (messageSubtype.compare("A") == 0))
490  {
491  this->ParseGeometryPixel(replyStream);
492  }
493  else if ((messageName.compare("B_GEOMETRY_TISSUE") == 0) && (messageSubtype.compare("A") == 0))
494  {
495  this->ParseGeometryTissue(replyStream);
496  }
497  else if ((messageName.compare("B_GEOMETRY_US_FRAME_GRAB") == 0) && (messageSubtype.compare("A") == 0))
498  {
499  this->ParseGeometryUsGrabFrame(replyStream);
500  }
501  else if ((messageName.compare("B_GAIN") == 0) && (messageSubtype.compare("A") == 0))
502  {
503  this->ParseGain(replyStream);
504  }
505  else if (messageName.compare("TRANSDUCER_LIST") == 0)
506  {
507  this->ParseTransducerList(replyStream);
508  }
509  else if ((messageName.compare("TRANSDUCER") == 0) && (messageSubtype.compare("A") == 0))
510  {
511  this->ParseTransducerData(replyStream);
512  }
513  }
514  else if ((messageString.compare("EVENT:TRANSDUCER_CONNECT;") == 0)
515  || (messageString.compare("EVENT:TRANSDUCER_DISCONNECT;") == 0)
516  || (messageString.compare("EVENT:TRANSDUCER_SELECTED;") == 0))
517  {
518  this->QueryTransducerList();//Need to query, as this can't be subscribed to
519  }
520  else if (messageString.compare("EVENT:FREEZE;") == 0)
521  {
522  LOG_DEBUG("Freeze");
523  }
524  else if (messageString.compare("EVENT:UNFREEZE;") == 0)
525  {
526  LOG_DEBUG("Unfreeze");
527  }
528  else if (messageString.compare("ACK;") == 0)
529  {
530  LOG_DEBUG("Acknowledge message received");
531  }
532  else
533  {
534  LOG_WARNING("Received unknown message from BK: " << messageString);
535  }
536  }
537 
538  return PLUS_SUCCESS; //Should newer reach this
539 }
540 
541 //-----------------------------------------------------------------------------
543 {
544  std::string retval(this->Internal->OemMessage.begin(), this->Internal->OemMessage.end());
545  return retval;
546 }
547 
548 //-----------------------------------------------------------------------------
550 {
551  std::vector<char> rawMessage;
552  char character(0);
553  unsigned totalBytes = 0;
554  int receivedBytes = 1;
555  while (character != EOT && receivedBytes >= 1)
556  {
557  receivedBytes = this->Internal->VtkSocket->Receive(&character, 1);
558  rawMessage.push_back(character);
559  totalBytes++;
560  //Speedup by reading the large data blocks in one operation
561  if (character == '#') //Read binary block
562  {
563  receivedBytes = this->Internal->VtkSocket->Receive(&character, 1);
564  if (receivedBytes == 1)
565  {
566  int numChars = (int)character - (int)('0');
567  rawMessage.push_back(character);
568  totalBytes++;
569  LOG_TRACE("Number of bytes in binary data block size: " << numChars); // Typically 7 or 6
570  if (numChars <= 1 || numChars >= 9)
571  {
572  LOG_ERROR("Error in binary data block from BK OEM interface. Incorrect character after block start (#): " << character << "(char num: " << (int)character << ")" << " (should be 6 or 7) ");
573  return PLUS_FAIL;
574  }
575  else
576  {
577  unsigned int uncompressedPixelBufferSize = 0;
578  for (int k = 0; k < numChars; k++, totalBytes++)
579  {
580  receivedBytes = this->Internal->VtkSocket->Receive(&character, 1);
581  if (receivedBytes != 1)
582  {
583  LOG_ERROR("Error in binary data block from BK OEM interface. Missing block size character.");
584  }
585  rawMessage.push_back(character);
586  uncompressedPixelBufferSize = uncompressedPixelBufferSize * 10 + ((int)character - (int)'0');
587  }
588  LOG_DEBUG("uncompressedPixelBufferSize = " << uncompressedPixelBufferSize);
589 
590  int rawSize = rawMessage.size();
591  rawMessage.resize(rawSize + uncompressedPixelBufferSize);
592  receivedBytes = this->Internal->VtkSocket->Receive(&rawMessage[rawSize], uncompressedPixelBufferSize, true);
593 
594  if (receivedBytes != uncompressedPixelBufferSize)
595  {
596  LOG_ERROR("Failed to read full binary data block from BK OEM interface receivedBytes: " << receivedBytes);
597  return PLUS_FAIL;
598  }
599  totalBytes += uncompressedPixelBufferSize;
600 
601  // The BK5000 scanner used for testing did not send data blocks matching the specified size.
602  // Currently all the binary data are added to the image.
603  receivedBytes = this->addAdditionalBinaryDataToImageUntilEOTReached(character, rawMessage);
604 
605  totalBytes += receivedBytes;
606  }
607  }
608  else
609  {
610  LOG_ERROR("Error in binary data block from BK OEM interface. No character after block start #");
611  receivedBytes = 1;
612  }
613  }//if
614  }//while
615  this->Internal->OemMessage = removeSpecialCharacters(rawMessage);
616 
617  if (receivedBytes < 1)
618  {
619  LOG_ERROR("Error in binary data block from BK OEM interface. No data.");
620  return PLUS_FAIL;
621  }
622  else
623  {
624  return PLUS_SUCCESS;
625  }
626 }
627 
628 //-----------------------------------------------------------------------------
630 {
631  int receivedBytes = this->Internal->VtkSocket->Receive(&character, 1);
632  if (receivedBytes != 0 && character != EOT)
633  {
634  LOG_WARNING("Unspecified charactes received. Adding these to image.");
635  while (character != EOT)
636  {
637  rawMessage.push_back(character);
638  receivedBytes = receivedBytes + this->Internal->VtkSocket->Receive(&character, 1);
639  }
640  LOG_DEBUG("Added additional characters to image: " << receivedBytes);
641  }
642  rawMessage.push_back(character); //Add EOT character
643  return receivedBytes;
644 }
645 
646 //-----------------------------------------------------------------------------
647 std::vector<char> vtkPlusBkProFocusOemVideoSource::removeSpecialCharacters(std::vector<char> inMessage)
648 {
649  std::vector<char> retval;
650  unsigned int inPos = 1;//Skip starting character SOH
651  while (inPos < inMessage.size() - 1)//Skip ending character EOT
652  {
653  if ((inMessage[inPos]) != ESC)
654  {
655  retval.push_back(inMessage[inPos++]);
656  }
657  else
658  {
659  inPos++;
660  retval.push_back(~inMessage[inPos++]);//Character after ESC is inverted
661  }
662  }
663  return retval;
664 }
665 
666 //-----------------------------------------------------------------------------
668 {
669  if (this->QueryImageSize() != PLUS_SUCCESS)
670  {
671  return PLUS_FAIL;
672  }
673  if (this->QueryGeometryScanarea() != PLUS_SUCCESS)
674  {
675  return PLUS_FAIL;
676  }
677  if (this->QueryGeometryPixel() != PLUS_SUCCESS)
678  {
679  return PLUS_FAIL;
680  }
681  if (this->QueryGeometryUsGrabFrame() != PLUS_SUCCESS)
682  {
683  return PLUS_FAIL;
684  }
685  if (this->QueryGeometryTissue() != PLUS_SUCCESS)
686  {
687  return PLUS_FAIL;
688  }
689  if (this->QueryGain() != PLUS_SUCCESS)
690  {
691  return PLUS_FAIL;
692  }
693  if (this->QueryTransducerList() != PLUS_SUCCESS)
694  {
695  return PLUS_FAIL;
696  }
697  if (this->QueryTransducer() != PLUS_SUCCESS)
698  {
699  return PLUS_FAIL;
700  }
701 
702  return PLUS_SUCCESS;
703 }
704 
705 //-----------------------------------------------------------------------------
706 // QUERY:US_WIN_SIZE;
708 {
709  LOG_DEBUG("Get ultrasound image size from BKProFocusOem");
710 
711  std::string query = "QUERY:US_WIN_SIZE;";
712  LOG_TRACE("Query from vtkPlusBkProFocusOemVideoSource: " << query);
713  return SendQuery(query);
714 }
715 
716 //-----------------------------------------------------------------------------
717 void vtkPlusBkProFocusOemVideoSource::ParseImageSize(std::istringstream& replyStream)
718 {
719  std::string stringVal;
720  std::getline(replyStream, stringVal, ',');
721  this->UltrasoundWindowSize[0] = atoi(stringVal.c_str());
722  std::getline(replyStream, stringVal, ';');
723  this->UltrasoundWindowSize[1] = atoi(stringVal.c_str());
724 
725  LOG_TRACE("Ultrasound image size = " << this->UltrasoundWindowSize[0] << " x " << this->UltrasoundWindowSize[1]);
726 }
727 
728 //-----------------------------------------------------------------------------
729 // QUERY:B_GEOMETRY_SCANAREA;
731 {
732  LOG_DEBUG("Get ultrasound geometry from BKProFocusOem");
733 
734  std::string query = "QUERY:B_GEOMETRY_SCANAREA:A;";
735  LOG_TRACE("Query from vtkPlusBkProFocusOemVideoSource: " << query);
736 
737  return SendQuery(query);
738 }
739 
740 //-----------------------------------------------------------------------------
741 void vtkPlusBkProFocusOemVideoSource::ParseGeometryScanarea(std::istringstream& replyStream)
742 {
743  std::string stringVal;
744  std::getline(replyStream, stringVal, ',');
745  StartLineX_m = atof(stringVal.c_str());
746  std::getline(replyStream, stringVal, ',');
747  StartLineY_m = atof(stringVal.c_str());
748  std::getline(replyStream, stringVal, ',');
749  StartLineAngle_rad = atof(stringVal.c_str());
750  std::getline(replyStream, stringVal, ',');
751  StartDepth_m = atof(stringVal.c_str());
752  std::getline(replyStream, stringVal, ',');
753  StopLineX_m = atof(stringVal.c_str());
754  std::getline(replyStream, stringVal, ',');
755  StopLineY_m = atof(stringVal.c_str());
756  std::getline(replyStream, stringVal, ',');
757  StopLineAngle_rad = atof(stringVal.c_str());
758  std::getline(replyStream, stringVal, ';');
759  StopDepth_m = atof(stringVal.c_str());
760 
761  LOG_DEBUG("Ultrasound geometry. StartLineX_m: " << StartLineX_m << " StartLineY_m: " << StartLineY_m << " StartLineAngle_rad: " << StartLineAngle_rad <<
762  " StartDepth_m: " << StartDepth_m << " StopLineX_m: " << StopLineX_m << " StopLineY_m: " << StopLineY_m << " StopLineAngle_rad: " << StopLineAngle_rad << " StopDepth_m: " << StopDepth_m);
763 }
764 
765 //-----------------------------------------------------------------------------
766 // QUERY:B_GEOMETRY_PIXEL;
768 {
769  std::string query = "QUERY:B_GEOMETRY_PIXEL:A;";
770  LOG_TRACE("Query from vtkPlusBkProFocusOemVideoSource: " << query);
771 
772  return SendQuery(query);
773 }
774 
775 //-----------------------------------------------------------------------------
776 void vtkPlusBkProFocusOemVideoSource::ParseGeometryPixel(std::istringstream& replyStream)
777 {
778  std::string stringVal;
779  std::getline(replyStream, stringVal, ',');
780  pixelLeft_pix = atoi(stringVal.c_str());
781  std::getline(replyStream, stringVal, ',');
782  pixelTop_pix = atoi(stringVal.c_str());
783  std::getline(replyStream, stringVal, ',');
784  pixelRight_pix = atoi(stringVal.c_str());
785  std::getline(replyStream, stringVal, ';');
786  pixelBottom_pix = atoi(stringVal.c_str());
787 
788  LOG_DEBUG("Ultrasound geometry. pixelLeft_pix: " << pixelLeft_pix << " pixelTop_pix: " << pixelTop_pix << " pixelRight_pix: " << pixelRight_pix << " pixelBottom_pix: " << pixelBottom_pix);
789 }
790 
791 //-----------------------------------------------------------------------------
792 // QUERY:B_GEOMETRY_US_FRAME_GRAB;
794 {
795  std::string query = "QUERY:B_GEOMETRY_US_FRAME_GRAB:A;";
796  LOG_TRACE("Query from vtkPlusBkProFocusOemVideoSource: " << query);
797 
798  return SendQuery(query);
799 }
800 
801 //-----------------------------------------------------------------------------
803 {
804  std::string stringVal;
805  std::getline(replyStream, stringVal, ',');
806  grabFramePixelLeft_pix = atoi(stringVal.c_str());
807  std::getline(replyStream, stringVal, ',');
808  grabFramePixelTop_pix = atoi(stringVal.c_str());
809  std::getline(replyStream, stringVal, ',');
810  grabFramePixelRight_pix = atoi(stringVal.c_str());
811  std::getline(replyStream, stringVal, ';');
812  grabFramePixelBottom_pix = atoi(stringVal.c_str());
813 
814  LOG_DEBUG("Ultrasound grab frame geometry. grabFramePixelLeft_pix: " << grabFramePixelLeft_pix
815  << " grabFramePixelTop_pix: " << grabFramePixelTop_pix
816  << " grabFramePixelRight_pix: " << grabFramePixelRight_pix
817  << " grabFramePixelBottom_pix: " << grabFramePixelBottom_pix);
818 }
819 
820 //-----------------------------------------------------------------------------
821 // QUERY:B_GEOMETRY_TISSUE;
823 {
824  std::string query = "QUERY:B_GEOMETRY_TISSUE:A;";
825  LOG_TRACE("Query from vtkPlusBkProFocusOemVideoSource: " << query);
826 
827  return SendQuery(query);
828 }
829 
830 //-----------------------------------------------------------------------------
831 void vtkPlusBkProFocusOemVideoSource::ParseGeometryTissue(std::istringstream& replyStream)
832 {
833  std::string stringVal;
834  std::getline(replyStream, stringVal, ',');
835  tissueLeft_m = atof(stringVal.c_str());
836  std::getline(replyStream, stringVal, ',');
837  tissueTop_m = atof(stringVal.c_str());
838  std::getline(replyStream, stringVal, ',');
839  tissueRight_m = atof(stringVal.c_str());
840  std::getline(replyStream, stringVal, ';');
841  tissueBottom_m = atof(stringVal.c_str());
842  LOG_DEBUG("Ultrasound geometry. tissueLeft_m: " << tissueLeft_m << " tissueTop_m: " << tissueTop_m << " tissueRight_m: " << tissueRight_m << " tissueBottom_m: " << tissueBottom_m);
843 }
844 
845 //-----------------------------------------------------------------------------
846 // QUERY:B_GAIN;
848 {
849  std::string query = "QUERY:B_GAIN:A;";
850  LOG_TRACE("Query from vtkPlusBkProFocusOemVideoSource: " << query);
851 
852  return SendQuery(query);
853 }
854 
855 //-----------------------------------------------------------------------------
856 void vtkPlusBkProFocusOemVideoSource::ParseGain(std::istringstream& replyStream)
857 {
858  std::string stringVal;
859  std::getline(replyStream, stringVal, ';');
860  gain_percent = atoi(stringVal.c_str());
861  LOG_DEBUG("Ultrasound gain. gain_percent: " << gain_percent);
862 }
863 
864 //-----------------------------------------------------------------------------
865 // QUERY:TRANSDUCER_LIST;
866 // Get list of transducers, connected to which port, and transducer type
868 {
869  std::string query = "QUERY:TRANSDUCER_LIST;";
870  LOG_TRACE("Query from vtkPlusBkProFocusOemVideoSource: " << query);
871 
872  return SendQuery(query);
873 }
874 
875 //-----------------------------------------------------------------------------
876 void vtkPlusBkProFocusOemVideoSource::ParseTransducerList(std::istringstream& replyStream)
877 {
878  std::string probeName;
879  std::string probeType;
880  //Port A
881  std::getline(replyStream, probeName, ',');
882  std::getline(replyStream, probeType, ',');
883  this->SetProbeTypeForPort("A", RemoveQuotationMarks(probeType));
884  //Port B
885  std::getline(replyStream, probeName, ',');
886  std::getline(replyStream, probeType, ',');
887  this->SetProbeTypeForPort("B", RemoveQuotationMarks(probeType));
888  //Port C
889  std::getline(replyStream, probeName, ',');
890  std::getline(replyStream, probeType, ',');
891  this->SetProbeTypeForPort("C", RemoveQuotationMarks(probeType));
892  //Port M
893  std::getline(replyStream, probeName, ',');
894  std::getline(replyStream, probeType, ';');
895  this->SetProbeTypeForPort("M", RemoveQuotationMarks(probeType));
896 }
897 
898 //-----------------------------------------------------------------------------
899 void vtkPlusBkProFocusOemVideoSource::SetProbeTypeForPort(std::string port, std::string probeTypeString)
900 {
901  IGTLIO_PROBE_TYPE probeTypeEnum = UNKNOWN;
902 
903  if (probeTypeString.compare("C") == 0)
904  {
905  probeTypeEnum = SECTOR;
906  }
907  else if (probeTypeString.compare("L") == 0)
908  {
909  probeTypeEnum = LINEAR;
910  }
911  else if (probeTypeString.compare("M") == 0)
912  {
913  probeTypeEnum = MECHANICAL;
914  }
915 
916  if (port.compare("A") == 0)
917  {
918  probeTypePortA = probeTypeEnum;
919  }
920  else if (port.compare("B") == 0)
921  {
922  probeTypePortB = probeTypeEnum;
923  }
924  else if (port.compare("C") == 0)
925  {
926  probeTypePortC = probeTypeEnum;
927  }
928  else if (port.compare("M") == 0)
929  {
930  probeTypePortM = probeTypeEnum;
931  }
932 }
933 
934 //-----------------------------------------------------------------------------
936 {
937  std::string retval;
938  std::istringstream inStream(inString);
939  std::getline(inStream, retval, '"');//Removes first "
940  std::getline(inStream, retval, '"');//Read characters until next " is found
941  return retval;
942 }
943 
944 //-----------------------------------------------------------------------------
945 // QUERY:TRANSDUCER:A;
946 // Get transducer that is used to create view A
948 {
949  std::string query = "QUERY:TRANSDUCER:A;";
950  LOG_TRACE("Query from vtkPlusBkProFocusOemVideoSource: " << query);
951 
952  return SendQuery(query);
953 }
954 
955 //-----------------------------------------------------------------------------
956 void vtkPlusBkProFocusOemVideoSource::ParseTransducerData(std::istringstream& replyStream)
957 {
958  std::string probePortString;
959  std::string probeName;
960  std::getline(replyStream, probePortString, ',');
961  std::getline(replyStream, probeName, ';');
962  probePort = this->RemoveQuotationMarks(probePortString);
963 }
964 
965 //-----------------------------------------------------------------------------
966 // CONFIG:DATA:SUBSCRIBE;
968 {
969  std::string query = "CONFIG:DATA:SUBSCRIBE ";
970  query += "\"US_WIN_SIZE\"";
971  query += ",\"B_GEOMETRY_SCANAREA\"";
972  query += ",\"B_GEOMETRY_PIXEL\"";
973  query += ",\"B_GEOMETRY_US_FRAME_GRAB\"";
974  query += ",\"B_GEOMETRY_TISSUE\"";
975  query += ",\"B_GAIN\"";
976  query += ",\"TRANSDUCER\"";
977  query += ";";
978  LOG_TRACE("Query from vtkPlusBkProFocusOemVideoSource: " << query);
979 
980  return SendQuery(query);
981 }
982 
983 //-----------------------------------------------------------------------------
984 // CONFIG:EVENTS;
986 {
987  std::string query = "CONFIG:EVENTS 1;";
988  LOG_TRACE("Command from vtkPlusBkProFocusOemVideoSource: " << query);
989  return SendQuery(query);
990 }
991 
992 //-----------------------------------------------------------------------------
993 // COMMAND:P_MODE;
994 /*PlusStatus vtkPlusBkProFocusOemVideoSource::CommandPowerDopplerOn()
995 {
996  std::string query = "COMMAND:P_MODE: \"ON\";";
997  LOG_TRACE("Command from vtkPlusBkProFocusOemVideoSource: " << query);
998  return SendQuery(query);
999 }*/
1000 
1001 //-----------------------------------------------------------------------------
1003 {
1004  std::string codedQuery = this->AddSpecialCharacters(query);
1005 
1006  if (!this->Internal->VtkSocket->Send(codedQuery.c_str(), codedQuery.size()))
1007  {
1008  return PLUS_FAIL;
1009  }
1010  return PLUS_SUCCESS;
1011 }
1012 
1013 //-----------------------------------------------------------------------------
1015 {
1016  std::string retval;
1017  const char special[] = { SOH, EOT, ESC, 0 }; // 0 is not special, it is an indicator for end of string
1018  retval += SOH; //Add start character
1019  for (unsigned int i = 0; i < query.size(); i++)
1020  {
1021  char ch = query[i];
1022  if (NULL != strchr(special, ch))
1023  {
1024  retval += ESC; //Escape special character
1025  ch = ~ch; //Invert special character
1026  }
1027  retval += ch;
1028  }
1029  retval += EOT; //Add end character
1030  return retval;
1031 }
1032 
1033 //-----------------------------------------------------------------------------
1035 {
1036  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
1037  XML_READ_STRING_ATTRIBUTE_REQUIRED(ScannerAddress, deviceConfig);
1038  XML_READ_SCALAR_ATTRIBUTE_REQUIRED(int, OemPort, deviceConfig);
1039  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(ContinuousStreamingEnabled, deviceConfig);
1040  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(ColorEnabled, deviceConfig);
1041  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(OfflineTesting, deviceConfig);
1042  XML_READ_CSTRING_ATTRIBUTE_REQUIRED(OfflineTestingFilePath, deviceConfig);
1043  return PLUS_SUCCESS;
1044 }
1045 
1046 //-----------------------------------------------------------------------------
1048 {
1049  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement);
1050  XML_WRITE_STRING_ATTRIBUTE_IF_NOT_EMPTY(ScannerAddress, deviceConfig);
1051  std::stringstream ss;
1052  ss << this->OemPort;
1053  deviceConfig->SetAttribute("OemPort", ss.str().c_str());
1054  XML_WRITE_BOOL_ATTRIBUTE(ContinuousStreamingEnabled, deviceConfig);
1055  XML_WRITE_BOOL_ATTRIBUTE(ColorEnabled, deviceConfig);
1056  XML_WRITE_BOOL_ATTRIBUTE(OfflineTesting, deviceConfig);
1057  XML_WRITE_CSTRING_ATTRIBUTE_IF_NOT_NULL(OfflineTestingFilePath, deviceConfig);
1058  return PLUS_SUCCESS;
1059 }
1060 
1061 //----------------------------------------------------------------------------
1063 {
1064  if (this->OutputChannels.size() > 1)
1065  {
1066  LOG_WARNING("vtkPlusBkProFocusOemVideoSource is expecting one output channel and there are " << this->OutputChannels.size() << " channels. First output channel will be used.");
1067  }
1068 
1069  if (this->OutputChannels.empty())
1070  {
1071  LOG_ERROR("No output channels defined for vtkPlusBkProFocusOemVideoSource. Cannot proceed.");
1072  this->CorrectlyConfigured = false;
1073  return PLUS_FAIL;
1074  }
1075 
1076  this->Internal->Channel = this->OutputChannels[0];
1077 
1078  return PLUS_SUCCESS;
1079 }
1080 
1081 //----------------------------------------------------------------------------
1082 void ReadDataFromByteArray(png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead)
1083 {
1084  if (png_ptr->io_ptr == NULL)
1085  {
1086  LOG_ERROR("ReadDataFromInputStream failed, no input pointer is set");
1087  png_error(png_ptr, "ReadDataFromInputStream failed, no input pointer is set");
1088  return;
1089  }
1090 
1091  unsigned char* bufferPointer = (unsigned char*)png_ptr->io_ptr;
1092  memcpy(outBytes, bufferPointer, byteCountToRead);
1093  bufferPointer += byteCountToRead;
1094 
1095  png_ptr->io_ptr = bufferPointer;
1096 }
1097 
1098 //----------------------------------------------------------------------------
1099 void PngErrorCallback(png_structp png_ptr, png_const_charp message)
1100 {
1101  LOG_ERROR("PNG error: " << (message ? message : "no details available"));
1102 }
1103 
1104 //----------------------------------------------------------------------------
1105 void PngWarningCallback(png_structp png_ptr, png_const_charp message)
1106 {
1107  LOG_WARNING("PNG warning: " << (message ? message : "no details available"));
1108 }
1109 
1110 //----------------------------------------------------------------------------
1111 PlusStatus vtkPlusBkProFocusOemVideoSource::DecodePngImage(unsigned char* pngBuffer, unsigned int pngBufferSize, vtkImageData* decodedImage)
1112 {
1113  std::vector<unsigned char> fileReadBuffer;
1114  if (this->OfflineTesting)
1115  {
1116  FILE* fp = fopen(this->OfflineTestingFilePath, "rb");
1117  if (!fp)
1118  {
1119  LOG_ERROR("Failed to read png");
1120  return PLUS_FAIL;
1121  }
1122  fseek(fp, 0, SEEK_END);
1123  size_t fileSizeInBytes = ftell(fp);
1124  rewind(fp);
1125  fileReadBuffer.resize(fileSizeInBytes);
1126  pngBuffer = &(fileReadBuffer[0]);
1127  fread(pngBuffer, 1, fileSizeInBytes, fp);
1128  fclose(fp);
1129  }
1130 
1131  unsigned int headerSize = 8;
1132  unsigned char* header = pngBuffer; // a 8-byte header
1133  int is_png = !png_sig_cmp(header, 0, headerSize);
1134  if (!is_png)
1135  {
1136  LOG_ERROR("Invalid PNG header");
1137  return PLUS_FAIL;
1138  }
1139 
1140  png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
1141  if (!png_ptr)
1142  {
1143  LOG_ERROR("Failed to decode PNG buffer");
1144  return PLUS_FAIL;
1145  }
1146 
1147  png_infop info_ptr = png_create_info_struct(png_ptr);
1148  if (!info_ptr)
1149  {
1150  png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
1151  LOG_ERROR("Failed to decode PNG buffer");
1152  return PLUS_FAIL;
1153  }
1154 
1155  png_infop end_info = png_create_info_struct(png_ptr);
1156  if (!end_info)
1157  {
1158  png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
1159  LOG_ERROR("Failed to decode PNG buffer");
1160  return PLUS_FAIL;
1161  }
1162 
1163  png_set_error_fn(png_ptr, NULL, PngErrorCallback, PngWarningCallback);
1164 
1165  // Set error handling
1166  if (setjmp(png_jmpbuf(png_ptr)))
1167  {
1168  png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
1169  LOG_ERROR("Failed to decode PNG buffer");
1170  return PLUS_FAIL;
1171  }
1172 
1173  png_set_read_fn(png_ptr, pngBuffer + 8, ReadDataFromByteArray);
1174 
1175  //png_init_io(png_ptr, fp);
1176  png_set_sig_bytes(png_ptr, 8);
1177  png_read_info(png_ptr, info_ptr);
1178 
1179  png_uint_32 width, height;
1180  int bit_depth, color_type, interlace_type;
1181  int compression_type, filter_method;
1182  // get size and bit-depth of the PNG-image
1183  png_get_IHDR(png_ptr, info_ptr,
1184  &width, &height,
1185  &bit_depth, &color_type, &interlace_type,
1186  &compression_type, &filter_method);
1187 
1188  // set-up the transformations
1189  // convert palettes to RGB
1190  if (color_type == PNG_COLOR_TYPE_PALETTE)
1191  {
1192  png_set_palette_to_rgb(png_ptr);
1193  }
1194 
1195  // minimum of a byte per pixel
1196  if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
1197  {
1198 #if PNG_LIBPNG_VER >= 10400
1199  png_set_expand_gray_1_2_4_to_8(png_ptr);
1200 #else
1201  png_set_gray_1_2_4_to_8(png_ptr);
1202 #endif
1203  }
1204 
1205  // add alpha if any alpha found
1206  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
1207  {
1208  png_set_tRNS_to_alpha(png_ptr);
1209  }
1210 
1211  if (bit_depth > 8)
1212  {
1213 #ifndef VTK_WORDS_BIGENDIAN
1214  png_set_swap(png_ptr);
1215 #endif
1216  }
1217 
1218  // have libpng handle interlacing
1219  //int number_of_passes = png_set_interlace_handling(png_ptr);
1220 
1221  // update the info now that we have defined the filters
1222  png_read_update_info(png_ptr, info_ptr);
1223 
1224  int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
1225  this->Internal->DecodingBuffer.resize(rowbytes * height);
1226  unsigned char* tempImage = &(this->Internal->DecodingBuffer[0]);
1227 
1228  this->Internal->DecodingLineBuffer.resize(height);
1229  png_bytep* row_pointers = &(this->Internal->DecodingLineBuffer[0]);
1230  for (unsigned int ui = 0; ui < height; ++ui)
1231  {
1232  row_pointers[ui] = tempImage + rowbytes * ui;
1233  }
1234  png_read_image(png_ptr, row_pointers);
1235 
1236  int numberOfScalarComponents = png_get_channels(png_ptr, info_ptr);
1237 
1238  if (width * numberOfScalarComponents != rowbytes)
1239  {
1240  LOG_WARNING("There is padding at the end of PNG lines, image may be skewed");
1241  }
1242 
1243  decodedImage->SetExtent(0, width - 1, 0, height - 1, 0, 0);
1244 
1245  if (!this->ColorEnabled)
1246  {
1247  numberOfScalarComponents = 1;
1248  }
1249 
1250  int bitDepth = png_get_bit_depth(png_ptr, info_ptr);
1251 #if (VTK_MAJOR_VERSION < 6)
1252  if (bitDepth <= 8)
1253  {
1254  decodedImage->SetScalarTypeToUnsignedChar();
1255  }
1256  else
1257  {
1258  decodedImage->SetScalarTypeToUnsignedShort();
1259  }
1260  decodedImage->SetNumberOfScalarComponents(numberOfScalarComponents);
1261  decodedImage->AllocateScalars();
1262 #else
1263  if (bitDepth <= 8)
1264  {
1265  decodedImage->AllocateScalars(VTK_UNSIGNED_CHAR, numberOfScalarComponents);
1266  }
1267  else
1268  {
1269  decodedImage->AllocateScalars(VTK_UNSIGNED_SHORT, numberOfScalarComponents);
1270  }
1271 #endif
1272 
1273 
1274  if (!this->ColorEnabled)
1275  {
1276  PlusStatus status = PixelCodec::ConvertToGray(PixelCodec::PixelEncoding_RGBA32, width, height, &(this->Internal->DecodingBuffer[0]), (unsigned char*)decodedImage->GetScalarPointer());
1277  }
1278  else
1279  {
1280  unsigned char* source = &(this->Internal->DecodingBuffer[0]);
1281  unsigned char* destination = (unsigned char*)decodedImage->GetScalarPointer();
1282  for (unsigned int i = 0; i < width * height * numberOfScalarComponents; ++i)
1283  {
1284  destination[i] = source[i];
1285  }
1286  }
1287 
1288  // close the file
1289  png_read_end(png_ptr, NULL);
1290  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1291 
1292  return PLUS_SUCCESS;
1293 }
1294 
1295 //----------------------------------------------------------------------------
1297 {
1298  vtkPlusUsDevice::InternalUpdate();// Move to beginning of vtkPlusBkProFocusOemVideoSource::InternalUpdate()?
1299 
1300  this->FrameFields[IGTLIO_KEY_PROBE_TYPE].first = FRAMEFIELD_FORCE_SERVER_SEND;
1301  this->FrameFields[IGTLIO_KEY_PROBE_TYPE].second = igsioCommon::ToString<int>(this->GetProbeType());
1302  std::string output;
1303  igsioCommon::JoinTokensIntoString<double>(this->CalculateOrigin(), output, ' ');
1304  this->FrameFields[IGTLIO_KEY_ORIGIN].first = FRAMEFIELD_FORCE_SERVER_SEND;
1305  this->FrameFields[IGTLIO_KEY_ORIGIN].second = output;
1306  igsioCommon::JoinTokensIntoString<double>(this->CalculateAngles(), output, ' ');
1307  this->FrameFields[IGTLIO_KEY_ANGLES].first = FRAMEFIELD_FORCE_SERVER_SEND;
1308  this->FrameFields[IGTLIO_KEY_ANGLES].second = output;
1309  igsioCommon::JoinTokensIntoString<double>(this->CalculateBoundingBox(), output, ' ');
1310  this->FrameFields[IGTLIO_KEY_BOUNDING_BOX].first = FRAMEFIELD_FORCE_SERVER_SEND;
1311  this->FrameFields[IGTLIO_KEY_BOUNDING_BOX].second = output;
1312  igsioCommon::JoinTokensIntoString<double>(this->CalculateDepths(), output, ' ');
1313  this->FrameFields[IGTLIO_KEY_DEPTHS].first = FRAMEFIELD_FORCE_SERVER_SEND;
1314  this->FrameFields[IGTLIO_KEY_DEPTHS].second = output;
1315  this->FrameFields[IGTLIO_KEY_LINEAR_WIDTH].first = FRAMEFIELD_FORCE_SERVER_SEND;
1316  this->FrameFields[IGTLIO_KEY_LINEAR_WIDTH].second = igsioCommon::ToString<double>(this->CalculateLinearWidth());
1317 
1318  this->FrameFields[IGTLIO_KEY_SPACING_X].first = FRAMEFIELD_FORCE_SERVER_SEND;
1319  this->FrameFields[IGTLIO_KEY_SPACING_X].second = igsioCommon::ToString<double>(this->GetSpacingX());
1320  this->FrameFields[IGTLIO_KEY_SPACING_Y].first = FRAMEFIELD_FORCE_SERVER_SEND;
1321  this->FrameFields[IGTLIO_KEY_SPACING_Y].second = igsioCommon::ToString<double>(this->GetSpacingY());
1322 
1323  this->FrameFields[KEY_DEPTH].first = FRAMEFIELD_FORCE_SERVER_SEND;
1324  this->FrameFields[KEY_DEPTH].second = igsioCommon::ToString<double>(this->CalculateDepthMm());
1325  this->FrameFields[KEY_GAIN].first = FRAMEFIELD_FORCE_SERVER_SEND;
1326  this->FrameFields[KEY_GAIN].second = igsioCommon::ToString<int>(this->CalculateGain());
1327 
1328  return PLUS_SUCCESS;
1329 }
1330 
1331 //----------------------------------------------------------------------------
1333 {
1334  std::vector<double> retval;
1335  int* dimensions = this->Internal->DecodedImageFrame->GetDimensions();
1336  double originX = dimensions[0] / 2.0; //For both continuous streaming and offline testing
1337 
1338  if (!this->ContinuousStreamingEnabled && !this->OfflineTesting)
1339  {
1340  // Even if the X-value of UltrasoundWindowSize and pixelLeft_pix don't refer to the actual ultrasound boundaries,
1341  // they seem to refer to an area where the ultrasound is placed in the middle,
1342  // so it looks like the calculation below works for finding originX.
1343  // (Studying a grabbed picture in detail, there seems to be an offset of 1 pixel,
1344  // but this is not verified for other settings and probes, so it is not added the the equation.)
1345  originX = pixelLeft_pix + this->UltrasoundWindowSize[0] / 2.0; //+1?
1346  }
1347 
1348  double originY = 0;
1349  if (this->IsSectorProbe())
1350  {
1351  double originHeightAboveBox_mm = (this->GetStartLineX()) / tan(this->CalculateWidthInRadians() / 2.0);
1352  originY = 0 - (originHeightAboveBox_mm / this->GetSpacingY());
1353  }
1354 
1355  double originZ = 0;
1356 
1357  retval.push_back(originX);
1358  retval.push_back(originY);
1359  retval.push_back(originZ);
1360  return retval;
1361 }
1362 
1363 //----------------------------------------------------------------------------
1365 {
1366  std::vector<double> retval;
1367  if (this->OfflineTesting)
1368  {
1369  retval.push_back(0);
1370  retval.push_back(0);
1371  }
1372  else
1373  {
1374  retval.push_back(this->GetStartLineAngle() - vtkMath::Pi() / 2.0);
1375  retval.push_back(this->GetStopLineAngle() - vtkMath::Pi() / 2.0);
1376  }
1377  retval.push_back(0);
1378  retval.push_back(0);
1379  return retval;
1380 }
1381 
1382 //----------------------------------------------------------------------------
1384 {
1385  std::vector<double> retval;
1386 
1387  int* dimensions = this->Internal->DecodedImageFrame->GetDimensions();
1388 
1389  retval.push_back(0);
1390  retval.push_back(dimensions[0] - 1);
1391  retval.push_back(0);
1392  retval.push_back(dimensions[1] - 1);
1393  retval.push_back(0);
1394  retval.push_back(0);
1395  return retval;
1396 }
1397 
1398 //----------------------------------------------------------------------------
1400 {
1401  std::vector<double> retval;
1402  if (this->OfflineTesting)
1403  {
1404  int* dimensions = this->Internal->DecodedImageFrame->GetDimensions();
1405  double* spacing = this->Internal->DecodedImageFrame->GetSpacing();
1406  retval.push_back(0);
1407  retval.push_back(dimensions[1] * spacing[1]);
1408  return retval;
1409  }
1410 
1411  double originDistanceToStartLine_mm = 0.0;
1412  if (!this->ContinuousStreamingEnabled)
1413  {
1414  originDistanceToStartLine_mm = pixelTop_pix * this->GetSpacingY();
1415  }
1416 
1417  if (this->IsSectorProbe())
1418  {
1419  originDistanceToStartLine_mm += (this->GetStartLineX()) / sin(this->CalculateWidthInRadians() / 2.0);
1420  }
1421 
1422  double depthStart = this->GetStartDepth() + originDistanceToStartLine_mm;
1423  double depthEnd = this->GetStopDepth() + originDistanceToStartLine_mm;
1424 
1425  retval.push_back(depthStart);
1426  retval.push_back(depthEnd);
1427  return retval;
1428 }
1429 
1430 //----------------------------------------------------------------------------
1432 {
1433  if (this->OfflineTesting)
1434  {
1435  int* dimensions = this->Internal->DecodedImageFrame->GetDimensions();
1436  double* spacing = this->Internal->DecodedImageFrame->GetSpacing();
1437  return dimensions[0] * spacing[0];
1438  }
1439 
1440  double width = fabs(this->GetStartLineX() - this->GetStopLineX());
1441  return width;
1442 }
1443 
1444 //----------------------------------------------------------------------------
1446 {
1447  bool sectorProbe = false;
1448  if (this->GetProbeType() == SECTOR)
1449  {
1450  sectorProbe = true;
1451  }
1452  return sectorProbe;
1453 }
1454 
1455 //----------------------------------------------------------------------------
1457 {
1458  double width_radians = fabs(this->GetStartLineAngle() - this->GetStopLineAngle());
1459  return width_radians;
1460 }
1461 
1462 //----------------------------------------------------------------------------
1464 {
1465  double depth_mm = (StopDepth_m - StartDepth_m) * 1000.0;
1466  return depth_mm;
1467 }
1468 
1469 //----------------------------------------------------------------------------
1471 {
1472  return gain_percent;
1473 }
1474 
1475 //----------------------------------------------------------------------------
1477 {
1478  return StartDepth_m * 1000.0;
1479 }
1480 
1481 //----------------------------------------------------------------------------
1483 {
1484  return StopDepth_m * 1000.0;
1485 }
1486 
1487 //----------------------------------------------------------------------------
1489 {
1490  return StartLineX_m * 1000.0;
1491 }
1492 
1493 //----------------------------------------------------------------------------
1495 {
1496  return StartLineY_m * 1000.0;
1497 }
1498 
1499 //----------------------------------------------------------------------------
1501 {
1502  return StopLineX_m * 1000.0;
1503 }
1504 
1505 //----------------------------------------------------------------------------
1507 {
1508  return StopLineY_m * 1000.0;
1509 }
1510 
1511 //----------------------------------------------------------------------------
1513 {
1514  return StartLineAngle_rad;
1515 }
1516 
1517 //----------------------------------------------------------------------------
1519 {
1520  return StopLineAngle_rad;
1521 }
1522 
1523 //----------------------------------------------------------------------------
1525 {
1526  double spacingX_mm = 0.1;
1527  if (!this->OfflineTesting)
1528  {
1529  if (this->ContinuousStreamingEnabled)
1530  {
1531  spacingX_mm = 1000.0 * (tissueRight_m - tissueLeft_m) / (grabFramePixelRight_pix - grabFramePixelLeft_pix + 1);
1532  }
1533  else
1534  {
1535  // spacingX_mm = 1000.0 * (tissueRight_m - tissueLeft_m) / this->UltrasoundWindowSize[0];
1536  //The values for pixelRight_pix, pixelLeft_pix and UltrasoundWindowSize[0] seem to be referring
1537  //to a larger area than the actual ultrasound and cannot be used here.
1538  //Assume equal spacing in x and y and return y spacing instead
1539  return this->GetSpacingY();
1540  }
1541  }
1542  return spacingX_mm;
1543 }
1544 
1545 //----------------------------------------------------------------------------
1547 {
1548  double spacingY_mm = 0.1;
1549  if (!this->OfflineTesting)
1550  {
1551  if (this->ContinuousStreamingEnabled)
1552  {
1553  spacingY_mm = 1000.0 * (tissueTop_m - tissueBottom_m) / (grabFramePixelBottom_pix - grabFramePixelTop_pix + 1);
1554  }
1555  else
1556  {
1557  spacingY_mm = 1000.0 * (tissueTop_m - tissueBottom_m) / this->UltrasoundWindowSize[1];
1558  }
1559  }
1560  return spacingY_mm;
1561 }
1562 
1563 //----------------------------------------------------------------------------
1565 {
1566  if (this->OfflineTesting)
1567  {
1568  return LINEAR;
1569  }
1570 
1571  if (probePort.compare("A"))
1572  {
1573  return probeTypePortA;
1574  }
1575  else if (probePort.compare("B"))
1576  {
1577  return probeTypePortB;
1578  }
1579  else if (probePort.compare("C"))
1580  {
1581  return probeTypePortC;
1582  }
1583  else if (probePort.compare("M"))
1584  {
1585  return probeTypePortM;
1586  }
1587  else
1588  {
1589  return UNKNOWN;
1590  }
1591 }
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
void ParseGeometryPixel(std::istringstream &replyStream)
PhidgetLCD_Font const char * character
Definition: phidget22.h:4250
void ParseGeometryTissue(std::istringstream &replyStream)
US_IMAGE_TYPE GetImageType()
const char * source
Definition: phidget22.h:2461
virtual std::vector< double > CalculateAngles() override
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
std::string RemoveQuotationMarks(std::string inString)
Class for acquiring ultrasound images from BK ultrasound systems through the OEM interface.
std::string GetSourceId() const
int
Definition: phidget22.h:3069
igsioStatus PlusStatus
Definition: PlusCommon.h:40
virtual std::string GetDeviceId() const
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)
void ParseTransducerList(std::istringstream &replyStream)
static PlusStatus ConvertToBGR24(ComponentOrdering outputOrdering, PixelEncoding inputCompression, int width, int height, unsigned char *s, unsigned char *d)
Definition: PixelCodec.h:197
bool RequireImageOrientationInConfiguration
virtual unsigned int GetNumberOfScalarComponents()
PlusStatus SetImageType(US_IMAGE_TYPE imageType)
for i
double AcquisitionRate
#define PLUS_FAIL
Definition: PlusCommon.h:43
int port
Definition: phidget22.h:2454
PlusStatus SetPixelType(igsioCommon::VTKScalarPixelType pixelType)
void PngErrorCallback(png_structp png_ptr, png_const_charp message)
void SetProbeTypeForPort(std::string port, std::string probeTypeString)
virtual std::vector< double > CalculateDepths() override
virtual PlusStatus Disconnect()
unsigned long FrameNumber
virtual IGTLIO_PROBE_TYPE GetProbeType() override
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
void PngWarningCallback(png_structp png_ptr, png_const_charp message)
void ParseGain(std::istringstream &replyStream)
void ParseGeometryScanarea(std::istringstream &replyStream)
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *config)
void ParseTransducerData(std::istringstream &replyStream)
virtual PlusStatus StopRecording()
igsioFieldMapType FrameFields
Container to hold calculated field values.
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
vtkStandardNewMacro(vtkPlusBkProFocusOemVideoSource)
PhidgetLCD_Font int * width
Definition: phidget22.h:4275
virtual std::vector< double > CalculateOrigin() override
virtual US_IMAGE_ORIENTATION GetInputImageOrientation()
static const int TIMESTAMP_SIZE
std::array< unsigned int, 2 > UltrasoundWindowSize
bool StartThreadForInternalUpdates
int addAdditionalBinaryDataToImageUntilEOTReached(char &character, std::vector< char > &rawMessage)
static PlusStatus ConvertToGray(int inputCompression, int width, int height, unsigned char *s, unsigned char *d)
Definition: PixelCodec.h:146
PhidgetLCD_Font int int * height
Definition: phidget22.h:4275
virtual std::vector< double > CalculateBoundingBox() override
Contains an optional timestamped circular buffer containing the video images and a number of timestam...
ChannelContainer OutputChannels
void ParseGeometryUsGrabFrame(std::istringstream &replyStream)
PlusStatus SetNumberOfScalarComponents(unsigned int numberOfScalarComponents)
const char * destination
Definition: phidget22.h:2452
const char * message
Definition: phidget22.h:2457
PlusStatus DecodePngImage(unsigned char *pngBuffer, unsigned int pngBufferSize, vtkImageData *decodedImage)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
std::vector< char > removeSpecialCharacters(std::vector< char > inMessage)
virtual PlusStatus InternalUpdate()
void ParseImageSize(std::istringstream &replyStream)
virtual int GetNumberOfItems()
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *config)
std::string AddSpecialCharacters(std::string query)
bool CorrectlyConfigured
void ReadDataFromByteArray(png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead)
Interface to a 3D positioning tool, video source, or generalized data stream.