PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkCenterOfRotationCalibAlgoTest.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 
16 #ifdef PLUS_RENDERING_ENABLED
17 #include "PlusPlotter.h"
18 #endif
20 #include "vtkPlusHTMLGenerator.h"
21 #include "vtkIGSIOSequenceIO.h"
23 #include "vtkXMLDataElement.h"
24 #include "vtkXMLUtilities.h"
25 #include "vtksys/CommandLineArguments.hxx"
26 #include "vtksys/SystemTools.hxx"
27 
28 // define tolerance used for comparing double numbers
29 #ifndef _WIN32
30 const double DOUBLE_DIFF = LINUXTOLERANCE;
31 #else
32 const double DOUBLE_DIFF = 0.0001;
33 #endif
34 
35 //----------------------------------------------------------------------------
36 int main(int argc, char** argv)
37 {
38  int numberOfFailures(0);
39 
40  int verboseLevel = vtkPlusLogger::LOG_LEVEL_UNDEFINED;
41 
42  bool printHelp(false);
43  vtksys::CommandLineArguments args;
44  args.Initialize(argc, argv);
45  std::string inputSequenceMetafile("");
46  std::string inputBaselineFileName("");
47  std::string inputConfigFileName("");
48 
49  args.AddArgument("--help", vtksys::CommandLineArguments::NO_ARGUMENT, &printHelp, "Print this help.");
50  args.AddArgument("--verbose", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &verboseLevel, "Verbose level (1=error only, 2=warning, 3=info, 4=debug, 5=trace)");
51  args.AddArgument("--source-seq-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputSequenceMetafile, "Input sequence metafile name with path");
52  args.AddArgument("--baseline-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputBaselineFileName, "Input xml baseline file name with path");
53  args.AddArgument("--config-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputConfigFileName, "Input xml config file name with path");
54 
55  if (!args.Parse())
56  {
57  std::cerr << "Problem parsing arguments" << std::endl;
58  std::cout << "Help: " << args.GetHelp() << std::endl;
59  exit(EXIT_FAILURE);
60  }
61 
62  if (printHelp)
63  {
64  std::cout << args.GetHelp() << std::endl;
65  exit(EXIT_SUCCESS);
66  }
67 
68  vtkPlusLogger::Instance()->SetLogLevel(verboseLevel);
69 
70  if (inputSequenceMetafile.empty() || inputConfigFileName.empty() || inputBaselineFileName.empty())
71  {
72  std::cerr << "input-translation-sequence-metafile, input-baseline-file-name and input-config-file-name are required arguments!" << std::endl;
73  std::cout << "Help: " << args.GetHelp() << std::endl;
74  exit(EXIT_FAILURE);
75  }
76 
77  // Read configuration
78  vtkSmartPointer<vtkXMLDataElement> configRootElement = vtkSmartPointer<vtkXMLDataElement>::New();
79  if (PlusXmlUtils::ReadDeviceSetConfigurationFromFile(configRootElement, inputConfigFileName.c_str()) == PLUS_FAIL)
80  {
81  LOG_ERROR("Unable to read configuration from file " << inputConfigFileName.c_str());
82  return EXIT_FAILURE;
83  }
84 
86 
87  PlusFidPatternRecognition patternRecognition;
89  patternRecognition.ReadConfiguration(configRootElement);
90 
91  LOG_INFO("Read center of rotation data from metafile...");
92 
93  vtkSmartPointer<vtkIGSIOTrackedFrameList> trackedFrameList = vtkSmartPointer<vtkIGSIOTrackedFrameList>::New();
94  if (vtkIGSIOSequenceIO::Read(inputSequenceMetafile, trackedFrameList) != PLUS_SUCCESS)
95  {
96  LOG_ERROR("Failed to read sequence metafile: " << inputSequenceMetafile);
97  return EXIT_FAILURE;
98  }
99 
100  LOG_INFO("Create tracked frame indices vector...");
101  std::vector<int> trackedFrameIndices(trackedFrameList->GetNumberOfTrackedFrames(), 0);
102  for (unsigned int i = 0; i < trackedFrameList->GetNumberOfTrackedFrames(); ++i)
103  {
104  trackedFrameIndices[i] = i;
105  }
106 
107  LOG_INFO("Testing image data segmentation...");
108  int numberOfSuccessfullySegmentedImages = 0;
109  patternRecognition.RecognizePattern(trackedFrameList, error, &numberOfSuccessfullySegmentedImages);
110  LOG_INFO("Segmentation success rate: " << numberOfSuccessfullySegmentedImages << " out of " << trackedFrameList->GetNumberOfTrackedFrames()
111  << " (" << (100.0 * numberOfSuccessfullySegmentedImages) / trackedFrameList->GetNumberOfTrackedFrames() << "%)");
112 
113  LOG_INFO("Testing spacing computation...");
114  vtkSmartPointer<vtkPlusSpacingCalibAlgo> spacingCalibAlgo = vtkSmartPointer<vtkPlusSpacingCalibAlgo>::New();
115  spacingCalibAlgo->SetInputs(trackedFrameList, patternRecognition.GetFidLineFinder()->GetNWires());
116 
117  double spacing[2] = {0};
118  if (spacingCalibAlgo->GetSpacing(spacing) != PLUS_SUCCESS)
119  {
120  LOG_ERROR("Spacing calibration failed!");
121  numberOfFailures++;
122  }
123  else
124  {
125  LOG_INFO("Spacing: " << std::fixed << spacing[0] << " " << spacing[1] << " mm/px");
126  }
127 
128  // Get calibration error
129  double spacingErrorMean(0), spacingErrorStdev(0);
130  if (spacingCalibAlgo->GetError(spacingErrorMean, spacingErrorStdev) != PLUS_SUCCESS)
131  {
132  LOG_ERROR("Failed to get spacing calibration error!");
133  numberOfFailures++;
134  }
135  else
136  {
137  LOG_INFO("Spacing calibration error - mean: " << std::fixed << spacingErrorMean << " stdev: " << spacingErrorStdev);
138  }
139 
140  LOG_INFO("Testing center of rotation computation algorithm...");
141  vtkSmartPointer<vtkPlusCenterOfRotationCalibAlgo> centerOfRotationCalibAlgo = vtkSmartPointer<vtkPlusCenterOfRotationCalibAlgo>::New();
142  centerOfRotationCalibAlgo->SetInputs(trackedFrameList, trackedFrameIndices, spacing);
143 
144  // Get center of rotation calibration output
145  double centerOfRotationPx[2] = {0};
146  if (centerOfRotationCalibAlgo->GetCenterOfRotationPx(centerOfRotationPx) != PLUS_SUCCESS)
147  {
148  LOG_ERROR("Center of rotation calibration failed!");
149  numberOfFailures++;
150  }
151  else
152  {
153  LOG_INFO("Center of rotation (px): " << std::fixed << centerOfRotationPx[0] << " " << centerOfRotationPx[1]);
154  }
155 
156  // Get calibration error
157  double errorMean(0), errorStdev(0);
158  if (centerOfRotationCalibAlgo->GetError(errorMean, errorStdev) != PLUS_SUCCESS)
159  {
160  LOG_ERROR("Failed to get center of rotation calibration error!");
161  numberOfFailures++;
162  }
163  else
164  {
165  LOG_INFO("Center of rotation calibration error - mean: " << std::fixed << errorMean << " stdev: " << errorStdev);
166  }
167 
168 #ifdef PLUS_RENDERING_ENABLED
169  LOG_INFO("Testing report table generation and saving into file...");
170  vtkTable* reportTable = centerOfRotationCalibAlgo->GetReportTable();
171  if (reportTable != NULL)
172  {
173  if (vtkPlusLogger::Instance()->GetLogLevel() >= vtkPlusLogger::LOG_LEVEL_DEBUG)
174  {
175  reportTable->Dump(25);
176  }
177  PlusPlotter::WriteTableToFile(*reportTable, "CenterOfRotationCalibrationErrorReport.txt");
178  }
179  else
180  {
181  LOG_ERROR("Failed to get report table!");
182  numberOfFailures++;
183  }
184 #else
185  LOG_WARNING("Cannot generate table. Rendering is disabled!");
186 #endif
187 
188  LOG_INFO("Testing HTML report generation...");
189  vtkSmartPointer<vtkPlusHTMLGenerator> htmlGenerator = vtkSmartPointer<vtkPlusHTMLGenerator>::New();
190  htmlGenerator->SetBaseFilename("CenterOfRotationCalibrationReport");
191  htmlGenerator->SetTitle("Center of Rotation Calibration Report");
192  spacingCalibAlgo->GenerateReport(htmlGenerator);
193  centerOfRotationCalibAlgo->GenerateReport(htmlGenerator);
194  htmlGenerator->SaveHtmlPageAutoFilename();
195 
196  std::ostringstream centerOfRotationCalibAlgoStream;
197  centerOfRotationCalibAlgo->PrintSelf(centerOfRotationCalibAlgoStream, vtkIndent(0));
198  LOG_DEBUG("CenterOfRotationCalibAlgo::PrintSelf: " << centerOfRotationCalibAlgoStream.str());
199 
200  //*********************************************************************
201  // Save results to file
202 
203  const char calibResultSaveFilename[] = "CenterOfRotationCalibrationResults.xml";
204  LOG_INFO("Save calibration results to XML file: " << calibResultSaveFilename);
205  std::ofstream outFile;
206  outFile.open(calibResultSaveFilename);
207  outFile << "<CalibrationResults>" << std::endl;
208  outFile << " <CenterOfRotationCalibrationResult " << std::fixed << std::setprecision(8)
209  << "CenterOfRotationPx=\"" << centerOfRotationPx[0] << " " << centerOfRotationPx[1] << "\" "
210  << "ErrorMean=\"" << errorMean << "\" "
211  << "ErrorStdev=\"" << errorStdev << "\" "
212  << " />" << std::endl;
213  outFile << "</CalibrationResults>" << std::endl;
214  outFile.close();
215 
216  //*********************************************************************
217  // Compare result to baseline
218 
219  LOG_INFO("Comparing result with baseline...");
220 
221  vtkSmartPointer<vtkXMLDataElement> xmlBaseline = vtkSmartPointer<vtkXMLDataElement>::Take(vtkXMLUtilities::ReadElementFromFile(inputBaselineFileName.c_str()));
222  vtkXMLDataElement* xmlCenterOfRotationCalibrationBaseline = NULL;
223  if (xmlBaseline != NULL)
224  {
225  xmlCenterOfRotationCalibrationBaseline = xmlBaseline->FindNestedElementWithName("CenterOfRotationCalibrationResult");
226  }
227  else
228  {
229  LOG_ERROR("Failed to read baseline file!");
230  numberOfFailures++;
231  }
232 
233  if (xmlCenterOfRotationCalibrationBaseline == NULL)
234  {
235  LOG_ERROR("Unable to find CenterOfRotationCalibrationResult XML data element in baseline: " << inputBaselineFileName);
236  numberOfFailures++;
237  }
238  else
239  {
240  // Compare CenterOfRotationPx to baseline
241  double baseCenterOfRotationPx[2] = {0};
242  if (!xmlCenterOfRotationCalibrationBaseline->GetVectorAttribute("CenterOfRotationPx", 2, baseCenterOfRotationPx))
243  {
244  LOG_ERROR("Unable to find CenterOfRotationPx XML data element in baseline.");
245  numberOfFailures++;
246  }
247  else
248  {
249  if (fabs(baseCenterOfRotationPx[0] - centerOfRotationPx[0]) > DOUBLE_DIFF
250  || fabs(baseCenterOfRotationPx[1] - centerOfRotationPx[1]) > DOUBLE_DIFF)
251  {
252  LOG_ERROR("Center of rotation result in pixel differ from baseline: current(" << std::fixed << centerOfRotationPx[0] << ", " << centerOfRotationPx[1]
253  << ") base (" << baseCenterOfRotationPx[0] << ", " << baseCenterOfRotationPx[1] << ").");
254  numberOfFailures++;
255  }
256  }
257 
258  // Compare errorMean
259  double baseErrorMean = 0;
260  if (!xmlCenterOfRotationCalibrationBaseline->GetScalarAttribute("ErrorMean", baseErrorMean))
261  {
262  LOG_ERROR("Unable to find ErrorMean XML data element in baseline.");
263  numberOfFailures++;
264  }
265  else
266  {
267  if (fabs(baseErrorMean - errorMean) > DOUBLE_DIFF)
268  {
269  LOG_ERROR("Center of rotation mean error differ from baseline: current(" << std::fixed << errorMean << ") base (" << baseErrorMean << ").");
270  numberOfFailures++;
271  }
272  }
273 
274  // Compare errorStdev
275  double baseErrorStdev = 0;
276  if (!xmlCenterOfRotationCalibrationBaseline->GetScalarAttribute("ErrorStdev", baseErrorStdev))
277  {
278  LOG_ERROR("Unable to find ErrorStdev XML data element in baseline.");
279  numberOfFailures++;
280  }
281  else
282  {
283  if (fabs(baseErrorStdev - errorStdev) > DOUBLE_DIFF)
284  {
285  LOG_ERROR("Center of rotation stdev of error differ from baseline: current(" << std::fixed << errorStdev << ") base (" << baseErrorStdev << ").");
286  numberOfFailures++;
287  }
288  }
289  }
290 
291 
292  if (numberOfFailures > 0)
293  {
294  LOG_INFO("Test failed!");
295  return EXIT_FAILURE;
296  }
297 
298  LOG_INFO("Test finished successfully!");
299  return EXIT_SUCCESS;
300 }
static PlusStatus WriteTableToFile(vtkTable &table, const std::string &filename)
const double DOUBLE_DIFF
int main(int argc, char **argv)
for i
#define PLUS_FAIL
Definition: PlusCommon.h:43
static vtkPlusConfig * GetInstance()
std::vector< PlusNWire > GetNWires()
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
static vtkIGSIOLogger * Instance()
PlusFidLineFinder * GetFidLineFinder()
PlusStatus RecognizePattern(vtkIGSIOTrackedFrameList *trackedFrameList, PatternRecognitionError &patternRecognitionError, int *numberOfSuccessfullySegmentedImages=NULL, std::vector< unsigned int > *segmentedFramesIndices=NULL)
void SetDeviceSetConfigurationData(vtkXMLDataElement *deviceSetConfigurationData)
PlusStatus ReadConfiguration(vtkXMLDataElement *rootConfigElement)
static PlusStatus ReadDeviceSetConfigurationFromFile(vtkXMLDataElement *config, const char *filename)
Definition: PlusXmlUtils.h:23