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