PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPhantomRegistrationTest.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 
13 #include "PlusConfigure.h"
14 #include "PlusMath.h"
15 #include "igsioTrackedFrame.h"
16 #include "vtkPlusDataCollector.h"
17 #include "vtkPlusFakeTracker.h"
18 #include "vtkMath.h"
19 #include "vtkMatrix4x4.h"
21 #include "vtkPlusChannel.h"
22 #include "vtkSmartPointer.h"
23 #include "vtkIGSIOTrackedFrameList.h"
24 #include "vtkTransform.h"
25 #include "vtkIGSIOTransformRepository.h"
26 #include "vtkXMLDataElement.h"
27 #include "vtkXMLUtilities.h"
28 #include "vtksys/CommandLineArguments.hxx"
29 #include "vtksys/SystemTools.hxx"
30 #include <iostream>
31 #include <stdlib.h>
32 
34 const double ERROR_THRESHOLD = 0.001; // error threshold
35 
36 PlusStatus CompareRegistrationResultsWithBaseline(const char* baselineFileName, const char* currentResultFileName, const char* phantomCoordinateFrame, const char* referenceCoordinateFrame);
37 
38 int main(int argc, char* argv[])
39 {
40  std::string inputConfigFileName;
41  std::string inputBaselineFileName;
42 
43  int verboseLevel = vtkPlusLogger::LOG_LEVEL_UNDEFINED;
44 
45  vtksys::CommandLineArguments cmdargs;
46  cmdargs.Initialize(argc, argv);
47 
48  cmdargs.AddArgument("--config-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputConfigFileName, "Configuration file name");
49  cmdargs.AddArgument("--baseline-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputBaselineFileName, "Name of file storing baseline calibration results");
50  cmdargs.AddArgument("--verbose", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &verboseLevel, "Verbose level (1=error only, 2=warning, 3=info, 4=debug, 5=trace)");
51 
52  if (!cmdargs.Parse())
53  {
54  std::cerr << "Problem parsing arguments" << std::endl;
55  std::cout << "Help: " << cmdargs.GetHelp() << std::endl;
56  exit(EXIT_FAILURE);
57  }
58 
59  vtkPlusLogger::Instance()->SetLogLevel(verboseLevel);
60 
61  LOG_INFO("Initialize");
62 
63  // Read configuration
64  vtkSmartPointer<vtkXMLDataElement> configRootElement = vtkSmartPointer<vtkXMLDataElement>::New();
65  if (PlusXmlUtils::ReadDeviceSetConfigurationFromFile(configRootElement, inputConfigFileName.c_str()) == PLUS_FAIL)
66  {
67  LOG_ERROR("Unable to read configuration from file " << inputConfigFileName.c_str());
68  return EXIT_FAILURE;
69  }
70 
72 
73  // Initialize data collection
74  vtkSmartPointer<vtkPlusDataCollector> dataCollector = vtkSmartPointer<vtkPlusDataCollector>::New();
75  if (dataCollector->ReadConfiguration(configRootElement) != PLUS_SUCCESS)
76  {
77  LOG_ERROR("Unable to parse configuration from file " << inputConfigFileName.c_str());
78  exit(EXIT_FAILURE);
79  }
80 
81  if (dataCollector->Connect() != PLUS_SUCCESS)
82  {
83  LOG_ERROR("Data collector was unable to connect to devices!");
84  exit(EXIT_FAILURE);
85  }
86  if (dataCollector->Start() != PLUS_SUCCESS)
87  {
88  LOG_ERROR("Unable to start data collection!");
89  exit(EXIT_FAILURE);
90  }
91  vtkPlusChannel* aChannel(NULL);
92  vtkPlusDevice* aDevice(NULL);
93  if (dataCollector->GetDevice(aDevice, std::string("TrackerDevice")) != PLUS_SUCCESS)
94  {
95  LOG_ERROR("Unable to locate device by ID: \'TrackerDevice\'");
96  exit(EXIT_FAILURE);
97  }
98  if (aDevice->GetOutputChannelByName(aChannel, "TrackerStream") != PLUS_SUCCESS)
99  {
100  LOG_ERROR("Unable to locate channel by ID: \'TrackerStream\'");
101  exit(EXIT_FAILURE);
102  }
103  if (aChannel->GetTrackingDataAvailable() == false)
104  {
105  LOG_ERROR("Channel \'" << aChannel->GetChannelId() << "\' is not tracking!");
106  exit(EXIT_FAILURE);
107  }
108  if (aChannel->GetTrackingDataAvailable() == false)
109  {
110  LOG_ERROR("Data collector is not tracking!");
111  exit(EXIT_FAILURE);
112  }
113 
114  // Read coordinate definitions
115  vtkSmartPointer<vtkIGSIOTransformRepository> transformRepository = vtkSmartPointer<vtkIGSIOTransformRepository>::New();
116  if (transformRepository->ReadConfiguration(configRootElement) != PLUS_SUCCESS)
117  {
118  LOG_ERROR("Failed to read CoordinateDefinitions!");
119  exit(EXIT_FAILURE);
120  }
121 
122  // Initialize phantom registration
123  vtkSmartPointer<vtkPlusPhantomLandmarkRegistrationAlgo> phantomRegistration = vtkSmartPointer<vtkPlusPhantomLandmarkRegistrationAlgo>::New();
124  if (phantomRegistration == NULL)
125  {
126  LOG_ERROR("Unable to instantiate phantom registration algorithm class!");
127  exit(EXIT_FAILURE);
128  }
129  if (phantomRegistration->ReadConfiguration(configRootElement) != PLUS_SUCCESS)
130  {
131  LOG_ERROR("Unable to read phantom definition!");
132  exit(EXIT_FAILURE);
133  }
134 
135  int numberOfLandmarks = phantomRegistration->GetDefinedLandmarks_Phantom()->GetNumberOfPoints();
136  if (numberOfLandmarks != 8)
137  {
138  LOG_ERROR("Number of defined landmarks should be 8 instead of " << numberOfLandmarks << "!");
139  exit(EXIT_FAILURE);
140  }
141 
142  // Acquire landmarks
143  vtkPlusFakeTracker* fakeTracker = dynamic_cast<vtkPlusFakeTracker*>(aDevice);
144  if (fakeTracker == NULL)
145  {
146  LOG_ERROR("Invalid tracker object!");
147  exit(EXIT_FAILURE);
148  }
149  fakeTracker->SetTransformRepository(transformRepository);
150 
151  igsioTrackedFrame trackedFrame;
152  igsioTransformName stylusTipToReferenceTransformName(phantomRegistration->GetStylusTipCoordinateFrame(), phantomRegistration->GetReferenceCoordinateFrame());
153 
154  for (int landmarkCounter = 0; landmarkCounter < numberOfLandmarks; ++landmarkCounter)
155  {
156  fakeTracker->SetCounter(landmarkCounter);
157  vtkIGSIOAccurateTimer::Delay(2.1 / fakeTracker->GetAcquisitionRate());
158 
159  vtkSmartPointer<vtkMatrix4x4> stylusTipToReferenceMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
160 
161  aChannel->GetTrackedFrame(trackedFrame);
162  transformRepository->SetTransforms(trackedFrame);
163 
164  ToolStatus status(TOOL_INVALID);
165  if (transformRepository->GetTransform(stylusTipToReferenceTransformName, stylusTipToReferenceMatrix, &status) != PLUS_SUCCESS || status != TOOL_OK)
166  {
167  LOG_ERROR("No valid transform found between stylus tip to reference!");
168  continue;
169  }
170 
171  // Compute point position from matrix
172  double stylusTipPosition[3] = {stylusTipToReferenceMatrix->GetElement(0, 3), stylusTipToReferenceMatrix->GetElement(1, 3), stylusTipToReferenceMatrix->GetElement(2, 3) };
173 
174  // Add recorded point to algorithm
175  phantomRegistration->GetRecordedLandmarks_Reference()->InsertPoint(landmarkCounter, stylusTipPosition);
176  phantomRegistration->GetRecordedLandmarks_Reference()->Modified();
177 
178  vtkPlusLogger::PrintProgressbar((100.0 * landmarkCounter) / numberOfLandmarks);
179  }
180 
181  if (phantomRegistration->LandmarkRegister(transformRepository) != PLUS_SUCCESS)
182  {
183  LOG_ERROR("Phantom registration failed!");
184  exit(EXIT_FAILURE);
185  }
186 
187  vtkPlusLogger::PrintProgressbar(100);
188 
189  LOG_INFO("Registration error = " << phantomRegistration->GetRegistrationErrorMm());
190 
191  // Save result
192  if (transformRepository->WriteConfiguration(configRootElement) != PLUS_SUCCESS)
193  {
194  LOG_ERROR("Failed to write phantom registration result to configuration element!");
195  exit(EXIT_FAILURE);
196  }
197 
198  std::string registrationResultFileName = "PhantomRegistrationTest.xml";
199  vtksys::SystemTools::RemoveFile(registrationResultFileName.c_str());
200  igsioCommon::XML::PrintXML(registrationResultFileName.c_str(), configRootElement);
201 
202  if (CompareRegistrationResultsWithBaseline(inputBaselineFileName.c_str(), registrationResultFileName.c_str(), phantomRegistration->GetPhantomCoordinateFrame(), phantomRegistration->GetReferenceCoordinateFrame()) != PLUS_SUCCESS)
203  {
204  LOG_ERROR("Comparison of calibration data to baseline failed");
205  std::cout << "Exit failure!!!" << std::endl;
206  return EXIT_FAILURE;
207  }
208 
209  std::cout << "Exit success!!!" << std::endl;
210  return EXIT_SUCCESS;
211 }
212 
213 //-----------------------------------------------------------------------------
214 
215 // return the number of differences
216 PlusStatus CompareRegistrationResultsWithBaseline(const char* baselineFileName, const char* currentResultFileName, const char* phantomCoordinateFrame, const char* referenceCoordinateFrame)
217 {
218  if (baselineFileName == NULL)
219  {
220  LOG_ERROR("Unable to read the baseline configuration file - filename is NULL");
221  return PLUS_FAIL;
222  }
223 
224  if (currentResultFileName == NULL)
225  {
226  LOG_ERROR("Unable to read the current configuration file - filename is NULL");
227  return PLUS_FAIL;
228  }
229 
230  igsioTransformName tnPhantomToPhantomReference(phantomCoordinateFrame, referenceCoordinateFrame);
231 
232  // Load current phantom registration
233  vtkSmartPointer<vtkXMLDataElement> currentRootElem = vtkSmartPointer<vtkXMLDataElement>::Take(
234  vtkXMLUtilities::ReadElementFromFile(currentResultFileName));
235  if (currentRootElem == NULL)
236  {
237  LOG_ERROR("Unable to read the current configuration file: " << currentResultFileName);
238  return PLUS_FAIL;
239  }
240 
241  vtkSmartPointer<vtkIGSIOTransformRepository> currentTransformRepository = vtkSmartPointer<vtkIGSIOTransformRepository>::New();
242  if (currentTransformRepository->ReadConfiguration(currentRootElem) != PLUS_SUCCESS)
243  {
244  LOG_ERROR("Unable to read the current CoordinateDefinitions from configuration file: " << currentResultFileName);
245  return PLUS_FAIL;
246  }
247 
248  vtkSmartPointer<vtkMatrix4x4> currentMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
249  if (currentTransformRepository->GetTransform(tnPhantomToPhantomReference, currentMatrix) != PLUS_SUCCESS)
250  {
251  std::string strTransformName;
252  tnPhantomToPhantomReference.GetTransformName(strTransformName);
253  LOG_ERROR("Unable to get '" << strTransformName << "' coordinate definition from configuration file: " << currentResultFileName);
254  return PLUS_FAIL;
255  }
256 
257  // Load baseline phantom registration
258  vtkSmartPointer<vtkXMLDataElement> baselineRootElem = vtkSmartPointer<vtkXMLDataElement>::Take(
259  vtkXMLUtilities::ReadElementFromFile(baselineFileName));
260  if (baselineFileName == NULL)
261  {
262  LOG_ERROR("Unable to read the baseline configuration file: " << baselineFileName);
263  return PLUS_FAIL;
264  }
265 
266  vtkSmartPointer<vtkIGSIOTransformRepository> baselineTransformRepository = vtkSmartPointer<vtkIGSIOTransformRepository>::New();
267  if (baselineTransformRepository->ReadConfiguration(baselineRootElem) != PLUS_SUCCESS)
268  {
269  LOG_ERROR("Unable to read the baseline CoordinateDefinitions from configuration file: " << baselineFileName);
270  return PLUS_FAIL;
271  }
272 
273  vtkSmartPointer<vtkMatrix4x4> baselineMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
274  if (baselineTransformRepository->GetTransform(tnPhantomToPhantomReference, baselineMatrix) != PLUS_SUCCESS)
275  {
276  std::string strTransformName;
277  tnPhantomToPhantomReference.GetTransformName(strTransformName);
278  LOG_ERROR("Unable to get '" << strTransformName << "' coordinate definition from configuration file: " << baselineFileName);
279  return PLUS_FAIL;
280  }
281 
282  // Compare the transforms
283  double posDiff = igsioMath::GetPositionDifference(currentMatrix, baselineMatrix);
284  double orientDiff = igsioMath::GetOrientationDifference(currentMatrix, baselineMatrix);
285 
286  if (fabs(posDiff) > ERROR_THRESHOLD || fabs(orientDiff) > ERROR_THRESHOLD)
287  {
288  LOG_ERROR("Transform mismatch (position difference: " << posDiff << " orientation difference: " << orientDiff);
289  return PLUS_FAIL;
290  }
291 
292  return PLUS_SUCCESS;
293 }
Abstract interface for tracker and video devices.
Definition: vtkPlusDevice.h:60
PlusStatus CompareRegistrationResultsWithBaseline(const char *baselineFileName, const char *currentResultFileName, const char *phantomCoordinateFrame, const char *referenceCoordinateFrame)
const double ERROR_THRESHOLD
virtual PlusStatus GetTrackedFrame(double timestamp, igsioTrackedFrame &trackedFrame, bool enableImageData=true)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
virtual void SetTransformRepository(vtkIGSIOTransformRepository *)
bool GetTrackingDataAvailable()
#define PLUS_FAIL
Definition: PlusCommon.h:43
Represents a fake tracking system as a simulator.
static vtkPlusConfig * GetInstance()
virtual double GetAcquisitionRate() const
PlusStatus GetOutputChannelByName(vtkPlusChannel *&aChannel, const char *aChannelId)
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
virtual char * GetChannelId()
static vtkIGSIOLogger * Instance()
Contains an optional timestamped circular buffer containing the video images and a number of timestam...
int main(int argc, char *argv[])
virtual void SetCounter(int)
void SetDeviceSetConfigurationData(vtkXMLDataElement *deviceSetConfigurationData)
static PlusStatus ReadDeviceSetConfigurationFromFile(vtkXMLDataElement *config, const char *filename)
Definition: PlusXmlUtils.h:23