PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusSpacingCalibAlgo.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 #include "PlusConfigure.h"
8 
9 #include "PlusMath.h"
10 #ifdef PLUS_RENDERING_ENABLED
11 #include "PlusPlotter.h"
12 #endif
14 #include "vtkObjectFactory.h"
15 #include "vtkIGSIOTrackedFrameList.h"
16 #include "igsioTrackedFrame.h"
17 #include "vtkPoints.h"
18 #include "vtksys/SystemTools.hxx"
19 #include "vtkPlusHTMLGenerator.h"
20 #include "vtkDoubleArray.h"
21 #include "vtkVariantArray.h"
22 
23 #include "vtkOrderStatistics.h"
24 
25 //----------------------------------------------------------------------------
27 
28 //----------------------------------------------------------------------------
30 {
31  this->TrackedFrameList = NULL;
32  this->SetSpacing(0, 0);
33  this->ReportTable = NULL;
34  this->ErrorMean = 0.0;
35  this->ErrorStdev = 0.0;
36 }
37 
38 //----------------------------------------------------------------------------
40 {
41  this->SetTrackedFrameList(NULL);
42  this->SetReportTable(NULL);
43 }
44 
45 //----------------------------------------------------------------------------
46 void vtkPlusSpacingCalibAlgo::PrintSelf(ostream& os, vtkIndent indent)
47 {
48  os << std::endl;
49  this->Superclass::PrintSelf(os, indent);
50  os << indent << "Update time: " << UpdateTime.GetMTime() << std::endl;
51  os << indent << "Spacing: " << this->Spacing[0] << " " << this->Spacing[1] << std::endl;
52  os << indent << "Calibration error: mean=" << this->ErrorMean << " stdev=" << this->ErrorStdev << std::endl;
53 
54  if (this->TrackedFrameList != NULL)
55  {
56  os << indent << "TrackedFrameList:" << std::endl;
57  this->TrackedFrameList->PrintSelf(os, indent);
58  }
59 
60  if (this->ReportTable != NULL)
61  {
62  os << indent << "ReportTable:" << std::endl;
63  this->ReportTable->PrintSelf(os, indent);
64  }
65 }
66 
67 
68 //----------------------------------------------------------------------------
69 void vtkPlusSpacingCalibAlgo::SetInputs(vtkIGSIOTrackedFrameList* trackedFrameList, const std::vector<PlusNWire>& nWires)
70 {
71  LOG_TRACE("vtkPlusSpacingCalibAlgo::SetInputs");
72  this->SetTrackedFrameList(trackedFrameList);
73  this->NWires = nWires;
74  this->Modified();
75 }
76 
77 
78 //----------------------------------------------------------------------------
80 {
81  LOG_TRACE("vtkPlusSpacingCalibAlgo::GetSpacing");
82  // Update calibration result
83  PlusStatus status = this->Update();
84 
85  spacing[0] = this->Spacing[0];
86  spacing[1] = this->Spacing[1];
87 
88  return status;
89 }
90 
91 //----------------------------------------------------------------------------
92 PlusStatus vtkPlusSpacingCalibAlgo::GetError(double& mean, double& stdev)
93 {
94  LOG_TRACE("vtkPlusSpacingCalibAlgo::GetError");
95  // Update calibration result
96  PlusStatus status = this->Update();
97 
98  mean = this->ErrorMean;
99  stdev = this->ErrorStdev;
100 
101  return status;
102 }
103 
104 //----------------------------------------------------------------------------
106 {
107  LOG_TRACE("vtkPlusSpacingCalibAlgo::Update");
108 
109  if (this->GetMTime() < this->UpdateTime.GetMTime())
110  {
111  LOG_DEBUG("Spacing calibration result is up-to-date!");
112  return PLUS_SUCCESS;
113  }
114 
115  // Check if TrackedFrameList is MF oriented BRIGHTNESS image
116  if (vtkIGSIOTrackedFrameList::VerifyProperties(this->TrackedFrameList, US_IMG_ORIENT_MF, US_IMG_BRIGHTNESS) != PLUS_SUCCESS)
117  {
118  LOG_ERROR("Failed to perform calibration - tracked frame list is invalid");
119  return PLUS_FAIL;
120  }
121 
122  // Construct linear equations Ax = b, where A is a matrix with m rows and
123  // n columns, b is an m-vector.
124  std::vector<vnl_vector<double> > aMatrix;
125  std::vector<double> bVector;
126 
127  // Construct linear equation for spacing calibration
128  if (this->ConstructLinearEquationForCalibration(aMatrix, bVector) != PLUS_SUCCESS)
129  {
130  LOG_ERROR("Unable to construct linear equations for spacing calibration!");
131  return PLUS_FAIL;
132  }
133 
134  if (aMatrix.size() == 0 || bVector.size() == 0)
135  {
136  LOG_ERROR("Spacing calibration failed, no data found!");
137  return PLUS_FAIL;
138  }
139 
140  vnl_vector<double> scalingCalibResult(2, 0); // [sx, sy]
141  if (PlusMath::LSQRMinimize(aMatrix, bVector, scalingCalibResult, &this->ErrorMean, &this->ErrorStdev) != PLUS_SUCCESS)
142  {
143  LOG_WARNING("Failed to run LSQRMinimize!");
144  return PLUS_FAIL;
145  }
146 
147  if (scalingCalibResult.empty() || scalingCalibResult.size() < 2)
148  {
149  LOG_ERROR("Unable to calibrate spacing! Minimizer returned empty result.");
150  return PLUS_FAIL;
151  }
152 
153  // Set spacing
154  // don't use set macro, it changes the modification time of the algorithm
155  this->Spacing[0] = sqrt(scalingCalibResult[0]);
156  this->Spacing[1] = sqrt(scalingCalibResult[1]);
157 
158  this->UpdateReportTable(aMatrix, bVector, scalingCalibResult);
159 
160  this->UpdateTime.Modified();
161 
162  return PLUS_SUCCESS;
163 }
164 
165 
166 //----------------------------------------------------------------------------
167 PlusStatus vtkPlusSpacingCalibAlgo::ConstructLinearEquationForCalibration(std::vector<vnl_vector<double> >& aMatrix, std::vector<double>& bVector)
168 {
169  LOG_TRACE("vtkPlusSpacingCalibAlgo::ConstructLinearEquationForCalibration");
170  aMatrix.clear();
171  bVector.clear();
172 
173  if (this->TrackedFrameList == NULL)
174  {
175  LOG_ERROR("Failed to construct linear equation for spacing calibration - tracked frame list is NULL!");
176  return PLUS_FAIL;
177  }
178 
179  // Check the number of
180  if (this->NWires.size() < 2)
181  {
182  LOG_ERROR("Failed to construct linear equation for spacing calibration - phantom wire definition is not correct!");
183  return PLUS_FAIL;
184  }
185 
186  std::vector<double> verticalDistanceMm;
187  std::vector<double> horizontalDistanceMm;
188  for (unsigned int i = 0; i < this->NWires.size() - 1; ++i)
189  {
190  // Distance between the two parallel stem of the N fiducial
191  double hd = fabs(this->NWires[i].GetWires()[0].EndPointFront[0] - this->NWires[i].GetWires()[2].EndPointFront[0]); // horizontal distance
192  horizontalDistanceMm.push_back(hd);
193 
194  // Distance between the neighboring N wire patterns
195  double vd = fabs(this->NWires[i].GetWires()[2].EndPointFront[1] - this->NWires[i + 1].GetWires()[2].EndPointFront[1]); // vertical distance
196  verticalDistanceMm.push_back(vd);
197  }
198 
199  for (unsigned int frame = 0; frame < this->TrackedFrameList->GetNumberOfTrackedFrames(); ++frame)
200  {
201  igsioTrackedFrame* trackedFrame = this->TrackedFrameList->GetTrackedFrame(frame);
202  if (trackedFrame == NULL)
203  {
204  LOG_ERROR("Unable to get tracked frame from the list - tracked frame is NULL (position in the list: " << frame << ")!");
205  continue;
206  }
207 
208  vtkPoints* fiduacialPointsCoordinatePx = trackedFrame->GetFiducialPointsCoordinatePx();
209  if (fiduacialPointsCoordinatePx == NULL)
210  {
211  LOG_ERROR("Unable to get segmented fiducial points from tracked frame - FiducialPointsCoordinatePx is NULL, frame is not yet segmented (position in the list: " << frame << ")!");
212  continue;
213  }
214 
215  if (fiduacialPointsCoordinatePx->GetNumberOfPoints() == 0)
216  {
217  LOG_DEBUG("Unable to get segmented fiducial points from tracked frame - couldn't segment image (position in the list: " << frame << ")!");
218  continue;
219  }
220 
221  for (unsigned int w = 0; w < this->NWires.size() - 1; ++w)
222  {
223  double wRightPx[3] = {0};
224  fiduacialPointsCoordinatePx->GetPoint(w * 3, wRightPx);
225 
226  double wLeftPx[3] = {0};
227  fiduacialPointsCoordinatePx->GetPoint(w * 3 + 2, wLeftPx);
228 
229  double wTopPx[3] = {0};
230  fiduacialPointsCoordinatePx->GetPoint((w + 1) * 3 + 2, wTopPx);
231 
232  double wBottomPx[3] = {0};
233  fiduacialPointsCoordinatePx->GetPoint(w * 3 + 2, wBottomPx);
234 
235  // Compute horizontal distance
236  double xHorizontalDistance = fabs(wRightPx[0] - wLeftPx[0]);
237  double yHorizontalDistance = fabs(wRightPx[1] - wLeftPx[1]);
238 
239  // Populate the sparse matrix with squared distances in pixel
240  vnl_vector<double> scaleFactorHorizontal(2, 0);
241  scaleFactorHorizontal.put(0, pow(xHorizontalDistance, 2));
242  scaleFactorHorizontal.put(1, pow(yHorizontalDistance, 2));
243  aMatrix.push_back(scaleFactorHorizontal);
244 
245  // Add the squared distance in mm
246  bVector.push_back(pow(horizontalDistanceMm[w], 2));
247 
248  // Compute vertical distance
249  double xVerticalDistance = fabs(wBottomPx[0] - wTopPx[0]);
250  double yVerticalDistance = fabs(wBottomPx[1] - wTopPx[1]);
251 
252  // Populate the sparse matrix with squared distances in pixel
253  vnl_vector<double> scaleFactorVertical(2, 0);
254  scaleFactorVertical.put(0, pow(xVerticalDistance, 2));
255  scaleFactorVertical.put(1, pow(yVerticalDistance, 2));
256  aMatrix.push_back(scaleFactorVertical);
257 
258  // Add the squared distance in mm
259  bVector.push_back(pow(verticalDistanceMm[w], 2));
260 
261  }
262  } // end of frames
263 
264  return PLUS_SUCCESS;
265 }
266 
267 //----------------------------------------------------------------------------
268 PlusStatus vtkPlusSpacingCalibAlgo::UpdateReportTable(const std::vector<vnl_vector<double> >& aMatrix,
269  const std::vector<double>& bVector,
270  const vnl_vector<double>& resultVector)
271 {
272  LOG_TRACE("vtkPlusSpacingCalibAlgo::UpdateReportTable");
273 
274  // Clear table before update
275  this->SetReportTable(NULL);
276 
277  if (this->ReportTable == NULL)
278  {
279  this->AddNewColumnToReportTable("Computed-Measured Distance - X (mm)");
280  this->AddNewColumnToReportTable("Measured Distance - X (mm)");
281  this->AddNewColumnToReportTable("Computed-Measured Distance - Y (mm)");
282  this->AddNewColumnToReportTable("Measured Distance - Y (mm)");
283  }
284 
285  const double sX = resultVector[0];
286  const double sY = resultVector[1];
287 
288  const int numberOfAxes(2);
289 
290  for (unsigned int row = 0; row < bVector.size(); row = row + numberOfAxes)
291  {
292  vtkSmartPointer<vtkVariantArray> tableRow = vtkSmartPointer<vtkVariantArray>::New();
293 
294  tableRow->InsertNextValue(sqrt(aMatrix[row].get(0) * sX + aMatrix[row].get(1) * sY) - sqrt(bVector[row])); // Computed-Measured Distance - X (mm)
295  tableRow->InsertNextValue(sqrt(bVector[row])); // Measured Distance - X (mm)
296  tableRow->InsertNextValue(sqrt(aMatrix[row + 1].get(0) * sX + aMatrix[row + 1].get(1) * sY) - sqrt(bVector[row + 1])); // Computed-Measured Distance - Y (mm)
297  tableRow->InsertNextValue(sqrt(bVector[row + 1])); // Measured Distance - Y (mm)
298 
299  if (tableRow->GetNumberOfTuples() == this->ReportTable->GetNumberOfColumns())
300  {
301  this->ReportTable->InsertNextRow(tableRow);
302  }
303  else
304  {
305  LOG_WARNING("Unable to insert new row to translation axis calibration report table, number of columns are different ("
306  << tableRow->GetNumberOfTuples() << " vs. " << this->ReportTable->GetNumberOfColumns() << ").");
307  }
308  }
309 
310  return PLUS_SUCCESS;
311 }
312 
313 //----------------------------------------------------------------------------
315 {
316  if (this->ReportTable == NULL)
317  {
318  vtkSmartPointer<vtkTable> table = vtkSmartPointer<vtkTable>::New();
319  this->SetReportTable(table);
320  }
321 
322  if (columnName == NULL)
323  {
324  LOG_ERROR("Failed to add new column to table - column name is NULL!");
325  return PLUS_FAIL;
326  }
327 
328  if (this->ReportTable->GetColumnByName(columnName) != NULL)
329  {
330  LOG_WARNING("Column name " << columnName << " already exist!");
331  return PLUS_FAIL;
332  }
333 
334  // Create table column
335  vtkSmartPointer<vtkDoubleArray> col = vtkSmartPointer<vtkDoubleArray>::New();
336  col->SetName(columnName);
337  this->ReportTable->AddColumn(col);
338 
339  return PLUS_SUCCESS;
340 }
341 
342 //----------------------------------------------------------------------------
344 {
345  LOG_TRACE("vtkPlusSpacingCalibAlgo::GenerateReport");
346 
347  if (htmlReport == NULL)
348  {
349  LOG_ERROR("vtkPlusSpacingCalibAlgo::GenerateReport failed: HTML report generator is invalid");
350  return PLUS_FAIL;
351  }
352 
353  // Update result before report generation
354  if (this->Update() != PLUS_SUCCESS)
355  {
356  LOG_ERROR("Unable to generate report - spacing calibration failed!");
357  return PLUS_FAIL;
358  }
359 
360  htmlReport->AddText("Spacing Calculation Analysis", vtkPlusHTMLGenerator::H1);
361 
362  std::ostringstream report;
363  report << "Image spacing (mm/px): " << this->Spacing[0] << " " << this->Spacing[1] << "</br>" ;
364  report << "Mean error (mm): " << this->ErrorMean << "</br>" ;
365  report << "Standard deviation (mm): " << this->ErrorStdev << "</br>" ;
366  htmlReport->AddParagraph(report.str().c_str());
367 
368  double valueRangeMin = -2.5;
369  double valueRangeMax = 2.5;
370  int numberOfBins = 41;
371  int imageSize[2] = {800, 400};
372 
373  std::string outputImageFilename = htmlReport->AddImageAutoFilename("ErrorHistogramX.png", "X spacing calculation error histogram");
374 #ifdef PLUS_RENDERING_ENABLED
375  PlusPlotter::WriteHistogramChartToFile("X spacing error histogram", *this->ReportTable, 0 /* "Computed-Measured Distance - X (mm)" */, valueRangeMin, valueRangeMax, numberOfBins, imageSize, outputImageFilename.c_str());
376 #endif
377  htmlReport->AddParagraph("<p>");
378 
379  outputImageFilename = htmlReport->AddImageAutoFilename("ErrorHistogramY.png", "Y spacing calculation error histogram");
380 #ifdef PLUS_RENDERING_ENABLED
381  PlusPlotter::WriteHistogramChartToFile("Y spacing error histogram", *this->ReportTable, 2 /* "Computed-Measured Distance - Y (mm)" */, valueRangeMin, valueRangeMax, numberOfBins, imageSize, outputImageFilename.c_str());
382 #endif
383 
384  htmlReport->AddHorizontalLine();
385 
386  return PLUS_SUCCESS;
387 }
vtkStandardNewMacro(vtkPlusSpacingCalibAlgo)
virtual void AddText(const char *text, HEADINGS h, const char *style=NULL)
virtual PlusStatus UpdateReportTable(const std::vector< vnl_vector< double > > &aMatrix, const std::vector< double > &bVector, const vnl_vector< double > &resultVector)
static PlusStatus LSQRMinimize(const std::vector< std::vector< double > > &aMatrix, const std::vector< double > &bVector, vnl_vector< double > &resultVector, double *mean=NULL, double *stdev=NULL, vnl_vector< unsigned int > *notOutliersIndices=NULL)
Definition: PlusMath.cxx:34
igsioStatus PlusStatus
Definition: PlusCommon.h:40
virtual PlusStatus ConstructLinearEquationForCalibration(std::vector< vnl_vector< double > > &aMatrix, std::vector< double > &bVector)
for i
PlusStatus AddNewColumnToReportTable(const char *columnName)
#define PLUS_FAIL
Definition: PlusCommon.h:43
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
virtual void AddHorizontalLine()
virtual void SetTrackedFrameList(vtkIGSIOTrackedFrameList *)
virtual void SetReportTable(vtkTable *)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
virtual PlusStatus GenerateReport(vtkPlusHTMLGenerator *htmlReport)
virtual void SetInputs(vtkIGSIOTrackedFrameList *trackedFrameList, const std::vector< PlusNWire > &nWires)
virtual void AddParagraph(const char *paragraph)
vtkIGSIOTrackedFrameList * TrackedFrameList
static PlusStatus WriteHistogramChartToFile(const std::string &chartTitle, vtkTable &inputTable, int inputColumnIndex, double valueRangeMin, double valueRangeMax, int numberOfBins, int imageSize[2], const std::string &outputImageFilename)
Definition: PlusPlotter.cxx:95
virtual PlusStatus GetError(double &mean, double &stdev)
virtual std::string AddImageAutoFilename(const char *filenamePostfix, const char *description, const int widthPx=0, const int heightPx=0)
virtual PlusStatus GetSpacing(double spacing[2])
std::vector< PlusNWire > NWires
class for generating basic html tags
virtual void SetSpacing(double, double)
Calculates ultrasound image spacing from phantom definition file.