PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkTelemedVideoSourceTest.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 
17 #include "PlusConfigure.h"
19 #include "vtkPlusDataSource.h"
20 #include "vtkCallbackCommand.h"
21 #include "vtkChartXY.h"
22 #include "vtkCommand.h"
23 #include "vtkContextScene.h"
24 #include "vtkContextView.h"
25 #include "vtkFloatArray.h"
26 #include "vtkImageData.h"
27 #include "vtkImageViewer.h"
28 #include "vtkInformation.h"
29 #include "vtkInformationVector.h"
30 #include "vtkPlot.h"
31 #include "vtkRenderWindow.h"
32 #include "vtkRenderWindowInteractor.h"
33 #include "vtkRenderer.h"
34 #include "vtkSmartPointer.h"
35 #include "vtkTable.h"
36 #include "vtkTableAlgorithm.h"
37 #include "vtkXMLUtilities.h"
38 #include "vtksys/CommandLineArguments.hxx"
39 #include <stdlib.h>
40 
41 //----------------------------------------------------------------------------
42 
44 {
47 };
48 
49 //----------------------------------------------------------------------------
50 
51 class vtkExtractImageRow : public vtkTableAlgorithm
52 {
53 public:
54  static vtkExtractImageRow* New();
55  vtkTypeMacro(vtkExtractImageRow,vtkTableAlgorithm);
56  void PrintSelf(ostream& os, vtkIndent indent)
57  {
58  this->Superclass::PrintSelf(os, indent);
59  }
60 
61  // Description:
62  // Specify the first vtkGraph input and the second vtkSelection input.
63  int FillInputPortInformation(int port, vtkInformation* info)
64  {
65  if (port == 0)
66  {
67  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData");
68  return 1;
69  }
70  return 0;
71  }
72 
73 protected:
74  vtkExtractImageRow()
75  {
76  this->SetNumberOfInputPorts(1);
77  }
78  virtual ~vtkExtractImageRow()
79  {
80  }
81 
82  int RequestData(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector)
83  {
84  vtkImageData* inputImage = vtkImageData::GetData(inputVector[0]);
85  vtkInformation* outInfo = outputVector->GetInformationObject(0);
86  vtkTable* outputTable = vtkTable::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
87 
88  if(!inputImage)
89  {
90  LOG_ERROR("No input image is available");
91  return 0;
92  }
93 
94  // Create the tables if they haven't been created yet
95  if (outputTable->GetColumnByName("time")==NULL)
96  {
97  vtkSmartPointer<vtkFloatArray> arrXnew = vtkSmartPointer<vtkFloatArray>::New();
98  arrXnew->SetName("time");
99  outputTable->AddColumn(arrXnew);
100  }
101  if (outputTable->GetColumnByName("RF value")==NULL)
102  {
103  vtkSmartPointer<vtkFloatArray> arrRfValNew = vtkSmartPointer<vtkFloatArray>::New();
104  arrRfValNew->SetName("RF value");
105  outputTable->AddColumn(arrRfValNew);
106  }
107 
108  if (inputImage->GetScalarType()!=VTK_SHORT)
109  {
110  LOG_ERROR("Plotting is only supported for signed short data");
111  return 0;
112  }
113  int rowCount=inputImage->GetDimensions()[1]; // number of transducer crystals
114  int numPoints=inputImage->GetDimensions()[0]; // number of data points (RF data values) recorded for one crystal
115  int selectedRow=rowCount/2; // plot the center column of the image
116  short* pixelBuffer=reinterpret_cast<short*>(inputImage->GetScalarPointer())+selectedRow*numPoints;
117 
118  outputTable->SetNumberOfRows(numPoints);
119  int timeIndex=numPoints-1; // the RF data set starts with the latest time
120  for (int i = 0; i < numPoints; ++i)
121  {
122  outputTable->SetValue(i, 0, timeIndex);
123  short value=*pixelBuffer;
124  outputTable->SetValue(i, 1, value);
125  pixelBuffer++;
126  timeIndex--;
127  }
128 
129  return 1;
130  }
131 
132 private:
133  vtkExtractImageRow(const vtkExtractImageRow&); // Not implemented
134  void operator=(const vtkExtractImageRow&); // Not implemented
135 };
136 
137 vtkStandardNewMacro(vtkExtractImageRow);
138 
139 //---------------------------------------------------------------------------------
140 
141 class vtkMyPlotCallback : public vtkCommand
142 {
143 public:
144  static vtkMyPlotCallback *New() { return new vtkMyPlotCallback; }
145 
146  virtual void Execute(vtkObject *caller, unsigned long eventId, void* callData)
147  {
148  if (eventId==vtkCommand::KeyPressEvent)
149  {
150  if (m_Interactor->GetKeyCode()=='q')
151  {
152  m_Interactor->ExitCallback();
153  }
154  return;
155  }
156 
157  m_ImageToTableAdaptor->Update();
158  m_Viewer->Render();
159  //update the timer so it will trigger again
160  m_Interactor->CreateTimer(VTKI_TIMER_UPDATE);
161  }
162 
163  vtkRenderWindowInteractor *m_Interactor;
164  vtkContextView *m_Viewer;
165  vtkExtractImageRow *m_ImageToTableAdaptor;
166 
167 private:
168 
169  vtkMyPlotCallback()
170  {
171  m_Interactor=NULL;
172  m_Viewer=NULL;
173  m_ImageToTableAdaptor=NULL;
174  }
175 };
176 
177 //----------------------------------------------------------------------------
179 {
180  // Set up a 2D scene, add an XY chart to it
181  vtkSmartPointer<vtkContextView> view = vtkSmartPointer<vtkContextView>::New();
182  view->GetRenderer()->SetBackground(1.0, 1.0, 1.0);
183  view->GetRenderWindow()->SetSize(400, 300);
184  vtkSmartPointer<vtkChartXY> chart = vtkSmartPointer<vtkChartXY>::New();
185  view->GetScene()->AddItem(chart);
186 
187  vtkSmartPointer<vtkExtractImageRow> imageToTableAdaptor=vtkSmartPointer<vtkExtractImageRow>::New();
188  imageToTableAdaptor->SetInputConnection(TelemedDevice->GetOutputPort());
189  imageToTableAdaptor->Update();
190 
191  // Add multiple line plots, setting the colors etc
192  vtkPlot *line = chart->AddPlot(vtkChart::LINE);
193  line->SetInputData(imageToTableAdaptor->GetOutput(), 0, 1);
194  line->SetColor(0, 255, 0, 255);
195  line->SetWidth(1.0);
196 
197  vtkSmartPointer<vtkMyPlotCallback> call = vtkSmartPointer<vtkMyPlotCallback>::New();
198  call->m_Interactor=view->GetInteractor();
199  call->m_Viewer=view;
200  call->m_ImageToTableAdaptor=imageToTableAdaptor;
201 
202  view->GetInteractor()->Initialize();
203 
204  view->GetInteractor()->AddObserver(vtkCommand::TimerEvent, call);
205  view->GetInteractor()->CreateTimer(VTKI_TIMER_FIRST);
206 
207  view->GetInteractor()->AddObserver(vtkCommand::KeyPressEvent, call);
208 
209  view->GetInteractor()->Start();
210 
211 }
212 
213 //---------------------------------------------------------------------------------
214 
215 class vtkMyCallback : public vtkCommand
216 {
217 public:
218  static vtkMyCallback *New() { return new vtkMyCallback; }
219 
220  virtual void Execute(vtkObject *caller, unsigned long, void*)
221  {
222  m_Viewer->Render();
223 
224  //update the timer so it will trigger again
225  m_Interactor->CreateTimer(VTKI_TIMER_UPDATE);
226  }
227 
228  vtkRenderWindowInteractor *m_Interactor;
229  vtkImageViewer *m_Viewer;
230 
231 private:
232 
233  vtkMyCallback()
234  {
235  m_Interactor=NULL;
236  m_Viewer=NULL;
237  }
238 };
239 
240 //-------------------------------------------------------------------------------------------
241 
242 int main(int argc, char* argv[])
243 {
244  bool printHelp(false);
245  bool renderingOff(false);
246  bool printParams(true);
247  std::string inputConfigFile;
248  double depthCm = -1;
249  double gainPercent = -1;
250  double zoomFactor = -1;
251  double frequencyMhz = -1;
252 
253 
254  std::string acqMode("B");
255 
256  vtksys::CommandLineArguments args;
257  args.Initialize(argc, argv);
258 
259  int verboseLevel = vtkPlusLogger::LOG_LEVEL_UNDEFINED;
260 
261  args.AddArgument("--help", vtksys::CommandLineArguments::NO_ARGUMENT, &printHelp, "Print this help.");
262  args.AddArgument("--config-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputConfigFile, "Config file containing the device configuration.");
263  args.AddArgument("--depth", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &depthCm, "Depth in cm.");
264  args.AddArgument("--frequencyMhz", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &frequencyMhz, "Frequency in MHz");
265  args.AddArgument("--gain", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &gainPercent, "Gain in percentage. 100 corresponds to the maximum gain");
266  args.AddArgument("--zoomFactor", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &zoomFactor, "Zoom used to display de US Image");
267  args.AddArgument("--rendering-off", vtksys::CommandLineArguments::NO_ARGUMENT, &renderingOff, "Run test without rendering.");
268  args.AddArgument("--print-params", vtksys::CommandLineArguments::NO_ARGUMENT, &printParams, "Print all the supported imaging parameters (for diagnostic purposes only).");
269  args.AddArgument("--verbose", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &verboseLevel, "Verbose level 1=error only, 2=warning, 3=info, 4=debug, 5=trace)");
270 
271  if ( !args.Parse() )
272  {
273  std::cerr << "Problem parsing arguments" << std::endl;
274  std::cout << "\n\nvtkPlusSonixVideoSourceTest1 help:" << args.GetHelp() << std::endl;
275  exit(EXIT_FAILURE);
276  }
277 
278  vtkPlusLogger::Instance()->SetLogLevel(verboseLevel);
279 
280  if ( printHelp )
281  {
282  std::cout << "\n\nvtkPlusTelemedVideoSourceTest help:" << args.GetHelp() << std::endl;
283  exit(EXIT_SUCCESS);
284  }
285 
286  vtkSmartPointer<vtkPlusTelemedVideoSource> TelemedDevice = vtkSmartPointer<vtkPlusTelemedVideoSource>::New();
287  TelemedDevice->SetDeviceId("VideoDevice");
288 
289  // Read config file
290  if (STRCASECMP(inputConfigFile.c_str(), "")!=0)
291  {
292  LOG_DEBUG("Reading config file...");
293  vtkSmartPointer<vtkXMLDataElement> configRead = vtkSmartPointer<vtkXMLDataElement>::Take(::vtkXMLUtilities::ReadElementFromFile(inputConfigFile.c_str()));
294  LOG_DEBUG("Reading config file finished.");
295  if ( configRead != NULL )
296  {
297  TelemedDevice->ReadConfiguration(configRead);
298  }
299  }
300 
301  if(depthCm != -1)
302  {
303  // TODO : Set the depth (in mm) on the device
304  }
305  if(gainPercent != -1)
306  {
307  TelemedDevice->SetGainPercent(gainPercent);
308  }
309  if(frequencyMhz != -1)
310  {
311  // TODO : Set the Frequency (in MHz) on the device
312  }
313 
314  TelemedDevice->CreateDefaultOutputChannel(NULL, true);
315 
316  vtkPlusDataSource* videoSource=NULL;
317  if (TelemedDevice->GetFirstActiveOutputVideoSource(videoSource) != PLUS_SUCCESS )
318  {
319  LOG_ERROR("Unable to retrieve the video source.");
320  exit(EXIT_FAILURE);
321  }
322  videoSource->SetInputImageOrientation(US_IMG_ORIENT_UN);
323 
324  DisplayMode displayMode=SHOW_IMAGE;
325  //DisplayMode displayMode=SHOW_PLOT;
326 
327  if (STRCASECMP(acqMode.c_str(), "B")==0)
328  {
329  LOG_DEBUG("Acquisition mode: B");
330  //TelemedDevice->SetImagingMode(BMode);
331  displayMode=SHOW_IMAGE;
332  }
333  //else if (STRCASECMP(acqMode.c_str(), "RF")==0)
334  //{
335  // LOG_DEBUG("Acquisition mode: RF");
336  // //TelemedDevice->SetImagingMode(RfMode);
337  // //TelemedDevice->SetAcquisitionDataType(udtRF);
338  // displayMode=SHOW_PLOT;
339  //}
340  else
341  {
342  LOG_ERROR("Unsupported AcquisitionDataType requested: "<<acqMode);
343  exit(EXIT_FAILURE);
344  }
345 
346  if ( TelemedDevice->Connect()!=PLUS_SUCCESS )
347  {
348  LOG_ERROR("Unable to connect to Telemed device");
349  exit(EXIT_FAILURE);
350  }
351 
352  TelemedDevice->StartRecording(); //start recording frame from the video
353 
354  if (renderingOff)
355  {
356  // just run the recording for a few seconds then exit
357  LOG_DEBUG("Rendering disabled. Wait for just a few seconds to acquire data before exiting");
358  Sleep(5000); // no need to use accurate timer, it's just an approximate delay
359  TelemedDevice->StopRecording();
360  TelemedDevice->Disconnect();
361  }
362  else
363  {
364  if (displayMode==SHOW_PLOT)
365  {
366  TestLinePlot(TelemedDevice);
367  }
368  else
369  {
370  // Show the live ultrasound image in a VTK renderer window
371 
372  vtkSmartPointer<vtkImageViewer> viewer = vtkSmartPointer<vtkImageViewer>::New();
373  viewer->SetInputConnection(TelemedDevice->GetOutputPort()); //set image to the render and window
374  viewer->SetColorWindow(255);
375  viewer->SetColorLevel(127.5);
376  viewer->SetZSlice(0);
377 
378  //Create the interactor that handles the event loop
379  vtkSmartPointer<vtkRenderWindowInteractor> iren = vtkSmartPointer<vtkRenderWindowInteractor>::New();
380  iren->SetRenderWindow(viewer->GetRenderWindow());
381  viewer->SetupInteractor(iren);
382 
383  viewer->Render(); //must be called after iren and viewer are linked or there will be problems
384 
385  // Establish timer event and create timer to update the live image
386  vtkSmartPointer<vtkMyCallback> call = vtkSmartPointer<vtkMyCallback>::New();
387  call->m_Interactor=iren;
388  call->m_Viewer=viewer;
389  iren->AddObserver(vtkCommand::TimerEvent, call);
390  iren->CreateTimer(VTKI_TIMER_FIRST);
391 
392  //iren must be initialized so that it can handle events
393  iren->Initialize();
394  iren->Start();
395  }
396  }
397 
398  TelemedDevice->Disconnect();
399  return EXIT_SUCCESS;
400 }
401 
const char int line
Definition: phidget22.h:2458
vtkRenderWindowInteractor * iren
for i
void TestLinePlot(vtkPlusTelemedVideoSource *TelemedDevice)
virtual PlusStatus SetInputImageOrientation(US_IMAGE_ORIENTATION imageOrientation)
int port
Definition: phidget22.h:2454
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
vtkStandardNewMacro(vtkExtractImageRow)
vtkImageViewer * viewer
const char const char * value
Definition: phidget22.h:5111
static vtkIGSIOLogger * Instance()
int main(int argc, char *argv[])
Class for interfacing the device manager class, and the PLUS library. The devices are Telemed ultraso...
Interface to a 3D positioning tool, video source, or generalized data stream.