PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusProbeCalibrationAlgo.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"
9 
10 #include "float.h"
11 #include <vnl/vnl_inverse.h>
12 
13 #include "vtkIGSIOTrackedFrameList.h"
14 #include "igsioTrackedFrame.h"
15 #include "vtkIGSIOTransformRepository.h"
16 
17 #include "PlusMath.h"
19 
20 #include "vtkObjectFactory.h"
21 #include "vtkMatrix4x4.h"
22 #include "vtkPlane.h"
23 #include "vtkXMLUtilities.h"
24 #include "vtkXMLDataElement.h"
25 
26 #include "vtkTransform.h"
27 #include "vtkMath.h"
28 #include "vtksys/SystemTools.hxx"
29 #include "vtkPoints.h"
30 #include "vtkLine.h"
31 #include "vtkPlane.h"
32 
33 static const int MIN_NUMBER_OF_VALID_CALIBRATION_FRAMES = 10; // minimum number of successfully calibrated frames required for calibration
34 static const double DEFAULT_ERROR_CONFIDENCE_INTERVAL = 0.95; // this fraction of the data is taken into account when computing mean and standard deviation in the final calibration error report
35 
37 
38 //----------------------------------------------------------------------------
39 void vtkPlusProbeCalibrationAlgo::PrintSelf(ostream& os, vtkIndent indent)
40 {
41  this->Superclass::PrintSelf(os, indent);
42 }
43 
44 //----------------------------------------------------------------------------
46  : CalibrationDate(NULL)
47  , ImageCoordinateFrame(NULL)
48  , ProbeCoordinateFrame(NULL)
49  , PhantomCoordinateFrame(NULL)
50  , ReferenceCoordinateFrame(NULL)
51  , ErrorConfidenceLevel(DEFAULT_ERROR_CONFIDENCE_INTERVAL)
52 {
55 }
56 
57 //----------------------------------------------------------------------------
59 {
60  if (this->Optimizer)
61  {
62  this->Optimizer->Delete();
63  this->Optimizer = NULL;
64  }
65 }
66 
67 //----------------------------------------------------------------------------
69 {
70  LOG_TRACE("vtkPlusProbeCalibrationAlgo::ReadConfiguration");
71  XML_FIND_NESTED_ELEMENT_REQUIRED(probeCalibrationElement, aConfig, "vtkPlusProbeCalibrationAlgo");
72 
73  XML_READ_CSTRING_ATTRIBUTE_REQUIRED(ImageCoordinateFrame, probeCalibrationElement);
74  XML_READ_CSTRING_ATTRIBUTE_REQUIRED(ProbeCoordinateFrame, probeCalibrationElement);
75  XML_READ_CSTRING_ATTRIBUTE_REQUIRED(PhantomCoordinateFrame, probeCalibrationElement);
76  XML_READ_CSTRING_ATTRIBUTE_REQUIRED(ReferenceCoordinateFrame, probeCalibrationElement);
77 
78  // Optimization options
79  if (this->Optimizer->ReadConfiguration(probeCalibrationElement) != PLUS_SUCCESS)
80  {
81  LOG_ERROR("vtkPlusProbeCalibrationOptimizerAlgo is not well specified in vtkPlusProbeCalibrationOptimizerAlgo element of the configuration!");
82  return PLUS_FAIL;
83  }
84 
85  return PLUS_SUCCESS;
86 }
87 
88 //----------------------------------------------------------------------------
89 PlusStatus vtkPlusProbeCalibrationAlgo::Calibrate(vtkIGSIOTrackedFrameList* validationTrackedFrameList, vtkIGSIOTrackedFrameList* calibrationTrackedFrameList, vtkIGSIOTransformRepository* transformRepository, const std::vector<PlusNWire>& nWires)
90 {
91  LOG_TRACE("vtkPlusProbeCalibrationAlgo::Calibrate");
92 
93  return Calibrate(validationTrackedFrameList, -1, -1, calibrationTrackedFrameList, -1, -1, transformRepository, nWires);
94 }
95 
96 //----------------------------------------------------------------------------
97 PlusStatus vtkPlusProbeCalibrationAlgo::ComputeImageToProbeTransformByLinearLeastSquaresMethod(vnl_matrix_fixed<double, 4, 4>& imageToProbeTransformMatrix, std::set<int>& outliers)
98 {
99  // Do calibration for all dimensions and assemble output matrix
100  const int n = 4; // number of point dimensions + 1 (homogeneous coordinate system representation: x, y, z, 1)
101  const unsigned int numberOfNWiresOnEachFrame = this->NWires.size();
102  const unsigned int numberOfFrames = PreProcessedWirePositions[CALIBRATION_ALL].FramePositions.size();
103  const unsigned int m = numberOfFrames * numberOfNWiresOnEachFrame; // number of all middle line intersection points on all frames
104 
105  // If we attempt to run least square with not enough points then vnl_lsqr crashes. Return with an error if there are very few frames to avoid crashing.
106  if (numberOfFrames < MIN_NUMBER_OF_VALID_CALIBRATION_FRAMES)
107  {
108  LOG_ERROR("Unable to perform calibration - there are " << numberOfFrames << " frames with segmented points and minimum " << MIN_NUMBER_OF_VALID_CALIBRATION_FRAMES << " frames are needed");
109  return PLUS_FAIL;
110  }
111 
112  vnl_sparse_matrix<double> middleWireIntersectionPointsPos_Image(m, n);
113  {
114  int outputColumnIndex = 0;
115  for (unsigned int frameIndex = 0; frameIndex < this->PreProcessedWirePositions[CALIBRATION_ALL].FramePositions.size(); ++frameIndex)
116  {
117  for (unsigned int nWireIndex = 0; nWireIndex < numberOfNWiresOnEachFrame; ++nWireIndex)
118  {
119  const vnl_vector_fixed<double, 4> middleWireIntersectionPointPos_Image = PreProcessedWirePositions[CALIBRATION_ALL].FramePositions[frameIndex].AllWiresIntersectionPointsPos_Image[nWireIndex * 3 + 1];
120  middleWireIntersectionPointsPos_Image(outputColumnIndex, 0) = middleWireIntersectionPointPos_Image[0];
121  middleWireIntersectionPointsPos_Image(outputColumnIndex, 1) = middleWireIntersectionPointPos_Image[1];
122  middleWireIntersectionPointsPos_Image(outputColumnIndex, 2) = middleWireIntersectionPointPos_Image[2];
123  middleWireIntersectionPointsPos_Image(outputColumnIndex, 3) = middleWireIntersectionPointPos_Image[3];
124  outputColumnIndex++;
125  }
126  }
127  }
128 
129  imageToProbeTransformMatrix.fill(0);
130 
131  for (int row = 0; row < n; ++row)
132  {
133  vnl_vector<double> probePositionRowVector(m, 0);
134  {
135  int posIndex = 0;
136  for (unsigned int frameIndex = 0; frameIndex < this->PreProcessedWirePositions[CALIBRATION_ALL].FramePositions.size(); ++frameIndex)
137  {
138  for (unsigned int nWireIndex = 0; nWireIndex < numberOfNWiresOnEachFrame; ++nWireIndex)
139  {
140  probePositionRowVector[posIndex] = this->PreProcessedWirePositions[CALIBRATION_ALL].FramePositions[frameIndex].MiddleWireIntersectionPointsPos_Probe[nWireIndex][row];
141  posIndex++;
142  }
143  }
144  }
145 
146  // Fill the nonOutliers vector with the indices of all the points (0, 1, 2, 3, ... m)
147  vnl_vector<unsigned int> nonOutliers(m);
148  for (unsigned int i = 0; i < m; ++i)
149  {
150  nonOutliers.put(i, i);
151  }
152 
153  vnl_vector<double> resultVector(n, 0);
154  if (PlusMath::LSQRMinimize(middleWireIntersectionPointsPos_Image, probePositionRowVector, resultVector, NULL, NULL, &nonOutliers) != PLUS_SUCCESS)
155  {
156  LOG_ERROR("Failed to run LSQRMinimize!");
157  return PLUS_FAIL;
158  }
159 
160  // Update outlier list
161  for (unsigned int pointIndex = 0; pointIndex < m; pointIndex++)
162  {
163  bool validPoint = false; // not an outlier
164  for (unsigned int validPointListIndex = 0; validPointListIndex < nonOutliers.size(); validPointListIndex++)
165  {
166  if (nonOutliers.get(validPointListIndex) == pointIndex)
167  {
168  validPoint = true;
169  break;
170  }
171  }
172  if (!validPoint)
173  {
174  outliers.insert(pointIndex);
175  }
176  }
177 
178  // Save result into ImageToProbe matrix
179  imageToProbeTransformMatrix.set_row(row, resultVector);
180  }
181 
182  // Force the last row to be exactly (0,0,0,1) - sometimes it contains numbers of 1e-18 magnitude
183  imageToProbeTransformMatrix(3, 0) = 0;
184  imageToProbeTransformMatrix(3, 1) = 0;
185  imageToProbeTransformMatrix(3, 2) = 0;
186  imageToProbeTransformMatrix(3, 3) = 1;
187 
188  // Complete the transformation matrix from a projection matrix to a 3D-3D transformation matrix (so that it can be inverted or can be used to transform 3D widgets to the image plane)
189  // Make the z vector have about the same length as x an y, so that when a 3D widget is transformed using this transform, the aspect ratio is maintained
190 
191  double xVector[3] = {imageToProbeTransformMatrix(0, 0), imageToProbeTransformMatrix(1, 0), imageToProbeTransformMatrix(2, 0)};
192  double yVector[3] = {imageToProbeTransformMatrix(0, 1), imageToProbeTransformMatrix(1, 1), imageToProbeTransformMatrix(2, 1)};
193  double zVector[3] = {0, 0, 0};
194  vtkMath::Cross(xVector, yVector, zVector);
195  vtkMath::Normalize(zVector);
196  double normZ = (vtkMath::Norm(xVector) + vtkMath::Norm(yVector)) / 2;
197  vtkMath::MultiplyScalar(zVector, normZ);
198  imageToProbeTransformMatrix(0, 2) = zVector[0];
199  imageToProbeTransformMatrix(1, 2) = zVector[1];
200  imageToProbeTransformMatrix(2, 2) = zVector[2];
201 
202  LOG_DEBUG(outliers.size() << " outliers points were found");
203 
204  return PLUS_SUCCESS;
205 }
206 
207 //----------------------------------------------------------------------------
208 PlusStatus vtkPlusProbeCalibrationAlgo::Calibrate(vtkIGSIOTrackedFrameList* validationTrackedFrameList, int validationStartFrame, int validationEndFrame, vtkIGSIOTrackedFrameList* calibrationTrackedFrameList, int calibrationStartFrame, int calibrationEndFrame, vtkIGSIOTransformRepository* transformRepository, const std::vector<PlusNWire>& nWires)
209 {
210  LOG_TRACE("vtkPlusProbeCalibrationAlgo::Calibrate(validation: " << validationStartFrame << "-" << validationEndFrame << ", calibration: " << calibrationStartFrame << "-" << calibrationEndFrame << ")");
211 
212  // Set range boundaries
213  if (validationStartFrame < 0)
214  {
215  validationStartFrame = 0;
216  }
217 
218  // Check if TrackedFrameLists are MF oriented BRIGHTNESS images
219  if (vtkIGSIOTrackedFrameList::VerifyProperties(validationTrackedFrameList, US_IMG_ORIENT_MF, US_IMG_BRIGHTNESS) != PLUS_SUCCESS)
220  {
221  LOG_ERROR("Failed to perform calibration - validation tracked frame list is invalid");
222  return PLUS_FAIL;
223  }
224  if (vtkIGSIOTrackedFrameList::VerifyProperties(calibrationTrackedFrameList, US_IMG_ORIENT_MF, US_IMG_BRIGHTNESS) != PLUS_SUCCESS)
225  {
226  LOG_ERROR("Failed to perform calibration - calibration tracked frame list is invalid");
227  return PLUS_FAIL;
228  }
229 
230  int numberOfValidationFrames = validationTrackedFrameList->GetNumberOfTrackedFrames();
231  if (validationEndFrame < 0 || validationEndFrame >= numberOfValidationFrames)
232  {
233  validationEndFrame = numberOfValidationFrames;
234  }
235 
236  if (calibrationStartFrame < 0)
237  {
238  calibrationStartFrame = 0;
239  }
240 
241  int numberOfCalibrationFrames = calibrationTrackedFrameList->GetNumberOfTrackedFrames();
242  if (calibrationEndFrame < 0 || calibrationEndFrame >= numberOfCalibrationFrames)
243  {
244  calibrationEndFrame = numberOfCalibrationFrames;
245  }
246 
247  this->NWires = nWires;
248 
252 
253  // Add tracked frames for calibration and validation
254  for (int frameNumber = validationStartFrame; frameNumber < validationEndFrame; ++frameNumber)
255  {
256  LOG_DEBUG("\n----------------------------------");
257  LOG_DEBUG("Add frame #" << frameNumber << " for validation data");
258  if (AddPositionsPerImage(validationTrackedFrameList->GetTrackedFrame(frameNumber), transformRepository, VALIDATION_ALL) != PLUS_SUCCESS)
259  {
260  LOG_ERROR("Add validation position failed on frame #" << frameNumber);
261  // we cannot continue the calibration, as there would be a mismatch between
262  // the number of frames and the number of wire positions
263  return PLUS_FAIL;
264  }
265  }
266 
267  for (int frameNumber = calibrationStartFrame; frameNumber < calibrationEndFrame; ++frameNumber)
268  {
269  LOG_DEBUG("\n----------------------------------");
270  LOG_DEBUG("Add frame #" << frameNumber << " for calibration data");
271  if (AddPositionsPerImage(calibrationTrackedFrameList->GetTrackedFrame(frameNumber), transformRepository, CALIBRATION_ALL) != PLUS_SUCCESS)
272  {
273  LOG_ERROR("Add calibration position failed on frame #" << frameNumber);
274  // we cannot continue the calibration, as there would be a mismatch between
275  // the number of frames and the number of wire positions
276  return PLUS_FAIL;
277  }
278  }
279 
280  if (PreProcessedWirePositions[CALIBRATION_ALL].FramePositions.empty())
281  {
282  LOG_ERROR("Unable to perform calibration - calibration data is empty!");
283  return PLUS_FAIL;
284  }
285 
286  vnl_matrix_fixed<double, 4, 4> imageToProbeTransformMatrix;
287  std::set<int> outliers;
288  if (ComputeImageToProbeTransformByLinearLeastSquaresMethod(imageToProbeTransformMatrix, outliers) != PLUS_SUCCESS)
289  {
290  LOG_ERROR("Calibration with linear least squares method failed");
291  return PLUS_FAIL;
292  }
293  // Validate calibration result and set it to member variable and transform repository
294  SetAndValidateImageToProbeTransform(imageToProbeTransformMatrix, transformRepository);
295 
296  if (this->Optimizer->Enabled())
297  {
298  LOG_INFO("Additional calibration optimization is requested");
299  UpdateNonOutlierData(outliers);
300  this->Optimizer->SetImageToProbeSeedTransform(imageToProbeTransformMatrix);
301  this->Optimizer->Update();
302  imageToProbeTransformMatrix = this->Optimizer->GetOptimizedImageToProbeTransformMatrix();
303  SetAndValidateImageToProbeTransform(imageToProbeTransformMatrix, transformRepository);
304  }
305 
306  // Log the calibration result and error
307  LOG_INFO("Image to probe transform matrix = ");
309 
310  // Compute 3D reprojection errors
312  {
313  LOG_ERROR("Failed to compute validation 3D reprojection errors!");
314  return PLUS_FAIL;
315  }
316 
317  igsioTransformName imageToProbeTransformName(this->ImageCoordinateFrame, this->ProbeCoordinateFrame);
318  transformRepository->SetTransformError(imageToProbeTransformName, this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError3DMean);
319  LOG_INFO("Validation 3D Reprojection Error (OPE): Mean: " << this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError3DMean << "mm, StDdev: " << this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError3DStdDev << "mm");
320 
322  {
323  LOG_ERROR("Failed to compute calibration 3D reprojection errors!");
324  return PLUS_FAIL;
325  }
326 
327  LOG_INFO("Calibration 3D Reprojection Error (OPE): Mean: " << this->PreProcessedWirePositions[CALIBRATION_ALL].NWireErrors.ReprojectionError3DMean << "mm, StDdev: " << this->PreProcessedWirePositions[CALIBRATION_ALL].NWireErrors.ReprojectionError3DStdDev << "mm");
328 
329  // Compute 2D reprojection errors
331  {
332  LOG_ERROR("Failed to compute validation 2D reprojection errors!");
333  return PLUS_FAIL;
334  }
335  for (unsigned int wire = 0; wire < this->NWires.size() * 3; ++wire)
336  {
337  LOG_INFO("Validation 2D Reprojection Error (IPE) for wire " << this->NWires[wire / 3].GetWires()[wire % 3].GetName() << ": "
338  << "Mean: (" << this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError2DMeans[wire][0] << "px, "
339  << this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError2DMeans[wire][1] << "px), "
340  << "StdDev: (" << this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError2DStdDevs[wire][0] << "px, "
341  << this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError2DStdDevs[wire][1] << "px)");
342  }
343 
345  {
346  LOG_ERROR("Failed to compute calibration 2D reprojection errors!");
347  return PLUS_FAIL;
348  }
349  for (unsigned int wire = 0; wire < this->NWires.size() * 3; ++wire)
350  {
351  LOG_INFO("Calibration 2D Reprojection Error (IPE) for wire " << this->NWires[wire / 3].GetWires()[wire % 3].GetName() << ": "
352  << "Mean: (" << this->PreProcessedWirePositions[CALIBRATION_ALL].NWireErrors.ReprojectionError2DMeans[wire][0] << "px, "
353  << this->PreProcessedWirePositions[CALIBRATION_ALL].NWireErrors.ReprojectionError2DMeans[wire][1] << "px), StdDev: ("
354  << this->PreProcessedWirePositions[CALIBRATION_ALL].NWireErrors.ReprojectionError2DStdDevs[wire][0] << "px, "
355  << this->PreProcessedWirePositions[CALIBRATION_ALL].NWireErrors.ReprojectionError2DStdDevs[wire][1] << "px)");
356  }
357 
358  // Save the calibration results and error reports into a file
359  if (SaveCalibrationResultAndErrorReportToXML(validationTrackedFrameList, validationStartFrame, validationEndFrame, calibrationTrackedFrameList, calibrationStartFrame, calibrationEndFrame) != PLUS_SUCCESS)
360  {
361  LOG_ERROR("Failed to save report!");
362  return PLUS_FAIL;
363  }
364 
365  return PLUS_SUCCESS;
366 }
367 
368 //----------------------------------------------------------------------------
369 PlusStatus vtkPlusProbeCalibrationAlgo::AddPositionsPerImage(igsioTrackedFrame* trackedFrame, vtkIGSIOTransformRepository* transformRepository, PreProcessedWirePositionIdType datasetType)
370 {
371  LOG_TRACE("vtkPlusProbeCalibrationAlgo::AddPositionsPerImage(type=" << datasetType << ")");
372 
373  // Get position of segmented fiducial points in the image
374  std::vector<vnl_vector<double> > segmentedWireIntersectionPointsPos_Image;
375  {
376  vtkPoints* segmentedPointsVtk = trackedFrame->GetFiducialPointsCoordinatePx();
377  if (segmentedPointsVtk == NULL)
378  {
379  LOG_WARNING("Segmentation has not been run on frame!");
380  return PLUS_FAIL;
381  }
382  if (segmentedPointsVtk->GetNumberOfPoints() == 0)
383  {
384  LOG_DEBUG("Segmentation failed on frame, so it will be ignored");
385  return PLUS_SUCCESS;
386  }
387  if (segmentedPointsVtk->GetNumberOfPoints() % 3 != 0)
388  {
389  LOG_ERROR("Frame does not contain N-Wires only!");
390  return PLUS_FAIL;
391  }
392  // Convert to vnl
393  for (int wire = 0; wire < segmentedPointsVtk->GetNumberOfPoints(); wire++)
394  {
395  vnl_vector<double> vnlPoint(4, 0);
396  double* point = segmentedPointsVtk->GetPoint(wire);
397  vnlPoint[0] = point[0];
398  vnlPoint[1] = point[1];
399  vnlPoint[2] = 0.0;
400  vnlPoint[3] = 1.0;
401  segmentedWireIntersectionPointsPos_Image.push_back(vnlPoint);
402  }
403  }
404 
405  // Compute PhantomToProbe transform
406  vnl_matrix_fixed<double, 4, 4> phantomToProbeTransformMatrix;
407  {
408  igsioTransformName probeToReferenceTransformName(this->ProbeCoordinateFrame, this->ReferenceCoordinateFrame);
409  vtkSmartPointer<vtkMatrix4x4> probeToReferenceVtkTransformMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
410  transformRepository->SetTransforms(*trackedFrame);
411  ToolStatus status(TOOL_INVALID);
412  if (transformRepository->GetTransform(probeToReferenceTransformName, probeToReferenceVtkTransformMatrix, &status) != PLUS_SUCCESS || status != TOOL_OK)
413  {
414  std::string transformName;
415  probeToReferenceTransformName.GetTransformName(transformName);
416  LOG_ERROR("Cannot get frame transform '" << transformName << "' from tracked frame!");
417  return PLUS_FAIL;
418  }
419 
420  // Get phantom registration matrix and convert it to vnl
421  igsioTransformName phantomToReferenceTransformName(this->PhantomCoordinateFrame, this->ReferenceCoordinateFrame);
422  vtkSmartPointer<vtkMatrix4x4> phantomToReferenceTransformMatrixVtk = vtkSmartPointer<vtkMatrix4x4>::New();
423  if (transformRepository->GetTransform(phantomToReferenceTransformName, phantomToReferenceTransformMatrixVtk, &status) != PLUS_SUCCESS || status != TOOL_OK)
424  {
425  LOG_ERROR("No valid transform found from phantom to reference");
426  return PLUS_FAIL;
427  }
428 
429  vnl_matrix_fixed<double, 4, 4> phantomToReferenceTransformMatrix;
430  PlusMath::ConvertVtkMatrixToVnlMatrix(phantomToReferenceTransformMatrixVtk, phantomToReferenceTransformMatrix);
431 
432  // Get reference to probe transform in vnl
433  vnl_matrix_fixed<double, 4, 4> probeToReferenceTransformMatrix;
434  PlusMath::ConvertVtkMatrixToVnlMatrix(probeToReferenceVtkTransformMatrix, probeToReferenceTransformMatrix);
435 
436  vnl_matrix_fixed<double, 4, 4> referenceToProbeTransformMatrix = vnl_inverse(probeToReferenceTransformMatrix);
437 
438  // Make sure the last row in homogeneous transform is [0 0 0 1]
439  vnl_vector<double> lastRow(4, 0);
440  lastRow.put(3, 1);
441  referenceToProbeTransformMatrix.set_row(3, lastRow);
442  LOG_DEBUG("Reference to probe transform = \n" << referenceToProbeTransformMatrix);
443 
444  phantomToProbeTransformMatrix = referenceToProbeTransformMatrix * phantomToReferenceTransformMatrix;
445  }
446 
447  // Create a new element to store all the position information for the frame
448  NWirePositionType framePosition;
449 
450  {
451  // Store all the probe to phantom transforms, used only in 2D minimization
452  igsioTransformName probeToPhantomTransformName(this->ProbeCoordinateFrame, this->PhantomCoordinateFrame);
453  vtkSmartPointer<vtkMatrix4x4> probeToPhantomVtkTransformMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
454 
455  ToolStatus status(TOOL_INVALID);
456  if (transformRepository->GetTransform(probeToPhantomTransformName, probeToPhantomVtkTransformMatrix, &status) != PLUS_SUCCESS || status != TOOL_OK)
457  {
458  std::string transformName;
459  probeToPhantomTransformName.GetTransformName(transformName);
460  LOG_ERROR("Cannot get frame transform '" << transformName << "' from tracked frame!");
461  return PLUS_FAIL;
462  }
463  // Get probe to phantom transform in vnl
464  vnl_matrix_fixed<double, 4, 4> probeToPhantomTransformMatrix;
465  PlusMath::ConvertVtkMatrixToVnlMatrix(probeToPhantomVtkTransformMatrix, probeToPhantomTransformMatrix);
466 
467  framePosition.ProbeToPhantomTransform = probeToPhantomTransformMatrix;
468  }
469 
470  // Calculate wire position in probe coordinate system using the segmentation result and the phantom geometry
471  std::vector< vnl_vector_fixed<double, 4> > middleWireIntersectionPointsPos_Phantom;
472  for (unsigned int nWireIndex = 0; nWireIndex < this->NWires.size(); ++nWireIndex)
473  {
474  int wire1Index = nWireIndex * 3 + 0;
475  int wire2Index = nWireIndex * 3 + 1;
476  int wire3Index = nWireIndex * 3 + 2;
477 
478  // Calculate the alpha value (how far the middle point is from the two side points)
479  vnl_vector<double> wire12vector_Image = segmentedWireIntersectionPointsPos_Image[wire2Index] - segmentedWireIntersectionPointsPos_Image[wire1Index];
480  vnl_vector<double> wire13vector_Image = segmentedWireIntersectionPointsPos_Image[wire3Index] - segmentedWireIntersectionPointsPos_Image[wire1Index];
481  double alpha = (double)wire12vector_Image.magnitude() / wire13vector_Image.magnitude();
482 
483  // Compute middle point position in phantom frame using alpha and the imaginary intersection points
484  vnl_vector_fixed<double, 4> middleWireIntersectionPointPos_Phantom(4);
485  vnl_vector_fixed<double, 4> intersectPosW12_Phantom(this->NWires[nWireIndex].IntersectPosW12[0], this->NWires[nWireIndex].IntersectPosW12[1], this->NWires[nWireIndex].IntersectPosW12[2], 1.0);
486  vnl_vector_fixed<double, 4> intersectPosW32_Phantom(this->NWires[nWireIndex].IntersectPosW32[0], this->NWires[nWireIndex].IntersectPosW32[1], this->NWires[nWireIndex].IntersectPosW32[2], 1.0);
487  middleWireIntersectionPointPos_Phantom = intersectPosW12_Phantom + alpha * (intersectPosW32_Phantom - intersectPosW12_Phantom);
488 
489  LOG_DEBUG("NWire #" << nWireIndex);
490  LOG_DEBUG(" Segmented point #" << wire1Index << " = " << segmentedWireIntersectionPointsPos_Image[wire1Index]);
491  LOG_DEBUG(" Segmented point #" << wire2Index << " = " << segmentedWireIntersectionPointsPos_Image[wire2Index] << " (middle wire)");
492  LOG_DEBUG(" Segmented point #" << wire3Index << " = " << segmentedWireIntersectionPointsPos_Image[wire3Index]);
493  LOG_DEBUG(" Alpha = " << alpha);
494  LOG_DEBUG(" Middle wire position in phantom frame = " << middleWireIntersectionPointPos_Phantom);
495 
496  {
497  // Compute middle point position in probe frame
498  vnl_vector<double> middleWireIntersectionPointPos_Probe = phantomToProbeTransformMatrix * middleWireIntersectionPointPos_Phantom;
499 
500  LOG_DEBUG(" Middle wire position in probe frame = " << middleWireIntersectionPointPos_Probe);
501 
502  // Store into the list of middle wire intersections in the probe frame
503  framePosition.MiddleWireIntersectionPointsPos_Probe.push_back(middleWireIntersectionPointPos_Probe);
504 
505  // Store all the segmented points, used only in 2D minimization
506  framePosition.AllWiresIntersectionPointsPos_Image.push_back(segmentedWireIntersectionPointsPos_Image[wire1Index]);
507  framePosition.AllWiresIntersectionPointsPos_Image.push_back(segmentedWireIntersectionPointsPos_Image[wire2Index]);
508  framePosition.AllWiresIntersectionPointsPos_Image.push_back(segmentedWireIntersectionPointsPos_Image[wire3Index]);
509  }
510  }
511 
512  this->PreProcessedWirePositions[datasetType].FramePositions.push_back(framePosition);
513 
514  return PLUS_SUCCESS;
515 }
516 
517 //-----------------------------------------------------------------------------
518 void vtkPlusProbeCalibrationAlgo::SetAndValidateImageToProbeTransform(const vnl_matrix_fixed<double, 4, 4>& imageToProbeTransformMatrix, vtkIGSIOTransformRepository* transformRepository)
519 {
520  // Check orthogonality
521  vnl_matrix_fixed<double, 3, 3> imageToProbeTransformMatrixRot = imageToProbeTransformMatrix.extract(3, 3);
522 
523  vnl_vector_fixed<double, 3> xVector = imageToProbeTransformMatrixRot.get_column(0);
524  vnl_vector_fixed<double, 3> yVector = imageToProbeTransformMatrixRot.get_column(1);
525  xVector.normalize();
526  yVector.normalize();
527  double angleXYdeg = fabs(vtkMath::DegreesFromRadians(acos(dot_product<double, 3>(xVector, yVector))));
528  const double angleToleranceDeg = 1.0;
529  if (fabs(angleXYdeg - 90.0) > angleToleranceDeg)
530  {
531  LOG_WARNING("ImageToProbe transformation matrix X and Y axes are not orthogonal. XY axis angle = " << angleXYdeg << " deg");
532  }
533 
534  // Set result matrix
535  this->ImageToProbeTransformMatrix = imageToProbeTransformMatrix;
536 
537  // Save results into transform repository
538  vtkSmartPointer<vtkMatrix4x4> imageToProbeMatrixVtk = vtkSmartPointer<vtkMatrix4x4>::New();
539  PlusMath::ConvertVnlMatrixToVtkMatrix(imageToProbeTransformMatrix, imageToProbeMatrixVtk);
540  igsioTransformName imageToProbeTransformName(this->ImageCoordinateFrame, this->ProbeCoordinateFrame);
541  transformRepository->SetTransform(imageToProbeTransformName, imageToProbeMatrixVtk);
542  transformRepository->SetTransformPersistent(imageToProbeTransformName, true);
543  transformRepository->SetTransformDate(imageToProbeTransformName, vtkIGSIOAccurateTimer::GetInstance()->GetDateAndTimeString().c_str());
544 
545  // Set calibration date
546  this->SetCalibrationDate(vtksys::SystemTools::GetCurrentDateTime("%Y.%m.%d %X").c_str());
547 }
548 
549 //-----------------------------------------------------------------------------
550 PlusStatus vtkPlusProbeCalibrationAlgo::ComputeReprojectionErrors3D(PreProcessedWirePositionIdType datasetType, const vnl_matrix_fixed<double, 4, 4>& imageToProbeTransformMatrix)
551 {
552  LOG_TRACE("vtkPlusProbeCalibrationAlgo::ComputeReprojectionErrors3D");
553 
554  std::vector<double> reprojectionErrors;
555  ComputeError3d(reprojectionErrors, datasetType, imageToProbeTransformMatrix);
556 
557  int numberOfNWiresPerFrame = this->NWires.size();
559  this->PreProcessedWirePositions[datasetType].NWireErrors.ReprojectionError3Ds.resize(numberOfNWiresPerFrame);
560  for (unsigned int frameIndex = 0; frameIndex < reprojectionErrors.size() / numberOfNWiresPerFrame; frameIndex++)
561  {
562  for (int nWireIndex = 0; nWireIndex < numberOfNWiresPerFrame; nWireIndex++)
563  {
564  this->PreProcessedWirePositions[datasetType].NWireErrors.ReprojectionError3Ds.at(nWireIndex).push_back(reprojectionErrors[frameIndex * numberOfNWiresPerFrame + nWireIndex]);
565  }
566  }
567 
568  igsioMath::ComputePercentile(reprojectionErrors, this->ErrorConfidenceLevel,
569  this->PreProcessedWirePositions[datasetType].NWireErrors.ReprojectionError3DMax,
570  this->PreProcessedWirePositions[datasetType].NWireErrors.ReprojectionError3DMean,
571  this->PreProcessedWirePositions[datasetType].NWireErrors.ReprojectionError3DStdDev);
572 
573  return PLUS_SUCCESS;
574 }
575 
576 //-----------------------------------------------------------------------------
577 PlusStatus vtkPlusProbeCalibrationAlgo::ComputeReprojectionErrors2D(PreProcessedWirePositionIdType datasetType, const vnl_matrix_fixed<double, 4, 4>& imageToProbeMatrix)
578 {
579  LOG_TRACE("vtkPlusProbeCalibrationAlgo::ComputeReprojectionErrors2D");
580 
581  // Initialize objects
582 
583  double errorMean = 0.0;
584  double errorStdev = 0.0;
585  ComputeError2d(datasetType, imageToProbeMatrix, errorMean, errorStdev, this->PreProcessedWirePositions[datasetType].NWireErrors.RmsError2D,
586  &(this->PreProcessedWirePositions[datasetType].NWireErrors.ReprojectionError2Ds));
587 
590  const int numberOfWires = this->PreProcessedWirePositions[datasetType].NWireErrors.ReprojectionError2Ds.size();
591  const int numberOfFrames = this->PreProcessedWirePositions[datasetType].NWireErrors.ReprojectionError2Ds[0].size();
592  for (int wire = 0; wire < numberOfWires; wire++)
593  {
594  std::vector<double> xErrors;
595  std::vector<double> yErrors;
596  for (int frameIndex = 0; frameIndex < numberOfFrames; frameIndex++)
597  {
598  xErrors.push_back(this->PreProcessedWirePositions[datasetType].NWireErrors.ReprojectionError2Ds[wire][frameIndex][0]);
599  yErrors.push_back(this->PreProcessedWirePositions[datasetType].NWireErrors.ReprojectionError2Ds[wire][frameIndex][1]);
600  }
601  double xErrorMean = 0.0;
602  double xErrorStdev = 0.0;
603  igsioMath::ComputeMeanAndStdev(xErrors, xErrorMean, xErrorStdev);
604  double yErrorMean = 0.0;
605  double yErrorStdev = 0.0;
606  igsioMath::ComputeMeanAndStdev(yErrors, yErrorMean, yErrorStdev);
607  vnl_vector_fixed<double, 2> errorMean(xErrorMean, yErrorMean);
608  this->PreProcessedWirePositions[datasetType].NWireErrors.ReprojectionError2DMeans.push_back(errorMean);
609  vnl_vector_fixed<double, 2> errorStdev(xErrorStdev, yErrorStdev);
610  this->PreProcessedWirePositions[datasetType].NWireErrors.ReprojectionError2DStdDevs.push_back(errorStdev);
611  }
612 
613  return PLUS_SUCCESS;
614 }
615 
616 //-----------------------------------------------------------------------------
617 std::string vtkPlusProbeCalibrationAlgo::GetResultString(int precision/* = 3*/)
618 {
619  LOG_TRACE("vtkPlusProbeCalibrationAlgo::GetResultString");
620 
621  std::ostringstream matrixStringStream;
622  matrixStringStream << "Image to probe transform:" << std::endl;
623 
624  // Print matrix rows
625  PlusMath::PrintMatrix(this->ImageToProbeTransformMatrix, matrixStringStream, precision + 2);
626  matrixStringStream << std::endl;
627 
628  double pixelSizeX = this->ImageToProbeTransformMatrix.get_column(0).magnitude();
629  double pixelSizeY = this->ImageToProbeTransformMatrix.get_column(1).magnitude();
630  matrixStringStream << "Image pixel size (mm):" << std::endl;
631  matrixStringStream << " " << std::fixed << std::setprecision(precision + 1) << pixelSizeX << " x " << pixelSizeY << std::endl;
632 
633  std::ostringstream errorsStringStream;
634 
635  errorsStringStream << std::fixed << std::setprecision(precision) << "3D Reprojection Error (mm)" << std::endl <<
637  ", StdDev: " << this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError3DStdDev << std::endl << std::endl;
638 
639  errorsStringStream << "2D Reprojection Errors (px)" << std::endl;;
640  for (unsigned int wire = 0; wire < this->NWires.size() * 3; ++wire)
641  {
642  errorsStringStream << std::fixed << std::setprecision(precision - 1) <<
643  "Wire #" << wire << " (" << this->NWires[wire / 3].GetWires()[wire % 3].GetName() << ")" << std::endl <<
647  << "," << this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError2DStdDevs[wire][1] << ")" << std::endl;
648  }
649 
650  std::ostringstream resultStringStream;
651  resultStringStream << matrixStringStream.str() << std::endl << errorsStringStream.str();
652 
653  //resultStringStream << std::endl << "Error confidence: " << (int)(this->ErrorConfidenceLevel*100) << "%";
654 
655  return resultStringStream.str();
656 }
657 
658 //----------------------------------------------------------------------------
659 PlusStatus vtkPlusProbeCalibrationAlgo::SaveCalibrationResultAndErrorReportToXML(vtkIGSIOTrackedFrameList* validationTrackedFrameList, int validationStartFrame, int validationEndFrame, vtkIGSIOTrackedFrameList* calibrationTrackedFrameList, int calibrationStartFrame, int calibrationEndFrame)
660 {
661  LOG_TRACE("vtkPlusProbeCalibrationAlgo::SaveCalibrationResultsAndErrorReportsToXML");
662 
663  std::string calibrationResultFileName = std::string(vtkPlusConfig::GetInstance()->GetApplicationStartTimestamp()) + ".Calibration.results.xml";
664  std::string calibrationResultFileNameWithPath = vtkPlusConfig::GetInstance()->GetOutputPath(calibrationResultFileName);
665 
666  // ProbeCalibrationResult
667  vtkSmartPointer<vtkXMLDataElement> probeCalibrationResult = vtkSmartPointer<vtkXMLDataElement>::New();
668  // ProbeCalibrationResult
669  probeCalibrationResult->SetName("ProbeCalibrationResult");
670  probeCalibrationResult->SetAttribute("version", "2.0");
671 
672  // CalibrationFile
673  vtkSmartPointer<vtkXMLDataElement> calibrationFile = vtkSmartPointer<vtkXMLDataElement>::New();
674  calibrationFile->SetName("CalibrationFile");
675  calibrationFile->SetAttribute("Timestamp", vtkPlusConfig::GetInstance()->GetApplicationStartTimestamp().c_str());
676  calibrationFile->SetAttribute("FileName", calibrationResultFileName.c_str());
677 
678  PlusStatus status = this->GetXMLCalibrationResultAndErrorReport(validationTrackedFrameList, validationStartFrame, validationEndFrame, calibrationTrackedFrameList, calibrationStartFrame, calibrationEndFrame, probeCalibrationResult);
679 
680  if (status == PLUS_SUCCESS)
681  {
682  probeCalibrationResult->AddNestedElement(calibrationFile);
683  igsioCommon::XML::PrintXML(calibrationResultFileNameWithPath.c_str(), probeCalibrationResult);
684  }
685 
686  return status;
687 }
688 
689 //----------------------------------------------------------------------------
690 PlusStatus vtkPlusProbeCalibrationAlgo::GetXMLCalibrationResultAndErrorReport(vtkIGSIOTrackedFrameList* validationTrackedFrameList, int validationStartFrame, int validationEndFrame, vtkIGSIOTrackedFrameList* calibrationTrackedFrameList, int calibrationStartFrame, int calibrationEndFrame, vtkXMLDataElement* probeCalibrationResult)
691 {
692  LOG_TRACE("vtkPlusProbeCalibrationAlgo::GetXMLCalibrationResultAndErrorReport");
693 
694  std::string calibrationResultFileName = std::string(vtkPlusConfig::GetInstance()->GetApplicationStartTimestamp()) + ".Calibration.results.xml";
695  std::string calibrationResultFileNameWithPath = vtkPlusConfig::GetInstance()->GetOutputPath(calibrationResultFileName);
696 
697  if (probeCalibrationResult == NULL)
698  {
699  LOG_ERROR("Unable to get xml calibration result and error report - xml data element is NULL");
700  return PLUS_FAIL;
701  }
702 
703  if (validationTrackedFrameList == NULL)
704  {
705  LOG_ERROR("Unable to get xml calibration result and error report - validationTrackedFrameList is NULL");
706  return PLUS_FAIL;
707  }
708 
709  if (calibrationTrackedFrameList == NULL)
710  {
711  LOG_ERROR("Unable to get xml calibration result and error report - calibrationTrackedFrameList is NULL");
712  return PLUS_FAIL;
713  }
714 
715  // CalibrationResults
716  vtkSmartPointer<vtkXMLDataElement> calibrationResults = vtkSmartPointer<vtkXMLDataElement>::New();
717  calibrationResults->SetName("CalibrationResults");
718 
719  // Image to Probe transform
720  vtkSmartPointer<vtkXMLDataElement> imageToProbeTransformElement = vtkSmartPointer<vtkXMLDataElement>::New();
721  imageToProbeTransformElement->SetName("Transform");
722  imageToProbeTransformElement->SetAttribute("From", this->ImageCoordinateFrame);
723  imageToProbeTransformElement->SetAttribute("To", this->ProbeCoordinateFrame);
724  double* imageToProbeTransformVector = this->ImageToProbeTransformMatrix.data_block();
725  imageToProbeTransformElement->SetVectorAttribute("Matrix", 16, imageToProbeTransformVector);
726 
727  calibrationResults->AddNestedElement(imageToProbeTransformElement);
728 
729 
730  // Error report
731  vtkSmartPointer<vtkXMLDataElement> errorReport = vtkSmartPointer<vtkXMLDataElement>::New();
732  errorReport->SetName("ErrorReport");
733 
734  // ReprojectionError3D
735  vtkSmartPointer<vtkXMLDataElement> reprojectionError3DStatistics = vtkSmartPointer<vtkXMLDataElement>::New();
736  reprojectionError3DStatistics->SetName("ReprojectionError3DStatistics");
737  reprojectionError3DStatistics->SetDoubleAttribute("ValidationMeanMm", this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError3DMean);
738  reprojectionError3DStatistics->SetDoubleAttribute("ValidationStdDevMm", this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError3DStdDev);
739  reprojectionError3DStatistics->SetDoubleAttribute("CalibrationMeanMm", this->PreProcessedWirePositions[CALIBRATION_ALL].NWireErrors.ReprojectionError3DMean);
740  reprojectionError3DStatistics->SetDoubleAttribute("CalibrationStdDevMm", this->PreProcessedWirePositions[CALIBRATION_ALL].NWireErrors.ReprojectionError3DStdDev);
741 
742  // ReprojectionError2D
743  vtkSmartPointer<vtkXMLDataElement> reprojectionError2DStatistics = vtkSmartPointer<vtkXMLDataElement>::New();
744  reprojectionError2DStatistics->SetName("ReprojectionError2DStatistics");
745 
746  for (unsigned int wire = 0; wire < this->NWires.size() * 3; ++wire)
747  {
748  vtkSmartPointer<vtkXMLDataElement> wireElem = vtkSmartPointer<vtkXMLDataElement>::New();
749  wireElem->SetName("Wire");
750  wireElem->SetAttribute("Name", this->NWires[wire / 3].GetWires()[wire % 3].GetName().c_str());
751 
756  wireElem->SetVectorAttribute("ValidationMeanPx", 2, validationMean2D);
757  wireElem->SetVectorAttribute("ValidationStdDevPx", 2, validationStdDev2D);
758  wireElem->SetVectorAttribute("CalibrationMeanPx", 2, calibrationMean2D);
759  wireElem->SetVectorAttribute("CalibrationStdDevPx", 2, calibrationStdDev2D);
760 
761  reprojectionError2DStatistics->AddNestedElement(wireElem);
762  }
763 
764  // ValidationData
765  vtkSmartPointer<vtkXMLDataElement> validationData = vtkSmartPointer<vtkXMLDataElement>::New();
766  validationData->SetName("ValidationData");
767 
768  int numberOfSegmentedFramesSoFar = 0;
769  for (int frameNumber = validationStartFrame; frameNumber < validationEndFrame; ++frameNumber)
770  {
771  vtkPoints* segmentedPointsVtk = validationTrackedFrameList->GetTrackedFrame(frameNumber)->GetFiducialPointsCoordinatePx();
772 
773  // Frame
774  vtkSmartPointer<vtkXMLDataElement> frame = vtkSmartPointer<vtkXMLDataElement>::New();
775  frame->SetName("Frame");
776  frame->SetIntAttribute("Index", frameNumber);
777 
778  if (segmentedPointsVtk == NULL)
779  {
780  frame->SetAttribute("SegmentationStatus", "HasNotBeenRun");
781  validationData->AddNestedElement(frame);
782  continue;
783  }
784  if (segmentedPointsVtk->GetNumberOfPoints() == 0)
785  {
786  frame->SetAttribute("SegmentationStatus", "Failed");
787  validationData->AddNestedElement(frame);
788  continue;
789  }
790  if (segmentedPointsVtk->GetNumberOfPoints() % 3 != 0)
791  {
792  frame->SetAttribute("SegmentationStatus", "InvalidPatterns");
793  validationData->AddNestedElement(frame);
794  continue;
795  }
796  frame->SetAttribute("SegmentationStatus", "OK");
797 
798  // SegmentedPoints
799  vtkSmartPointer<vtkXMLDataElement> segmentedWireIntersectionPointsPos_Image = vtkSmartPointer<vtkXMLDataElement>::New();
800  segmentedWireIntersectionPointsPos_Image->SetName("SegmentedPoints");
801 
802  int numberOfSegmentedPoints = segmentedPointsVtk->GetNumberOfPoints();
803  for (int wire = 0; wire < numberOfSegmentedPoints; wire++)
804  {
805  double point[3];
806  segmentedPointsVtk->GetPoint(wire, point);
807 
808  vtkSmartPointer<vtkXMLDataElement> pointElement = vtkSmartPointer<vtkXMLDataElement>::New();
809  pointElement->SetName("Point");
810  pointElement->SetAttribute("WireName", this->NWires[wire / 3].GetWires()[wire % 3].GetName().c_str());
811  pointElement->SetVectorAttribute("Position", 3, point);
812  segmentedWireIntersectionPointsPos_Image->AddNestedElement(pointElement);
813  }
814 
815  // ReprojectionError3Ds
816  vtkSmartPointer<vtkXMLDataElement> reprojectionError3Ds = vtkSmartPointer<vtkXMLDataElement>::New();
817  reprojectionError3Ds->SetName("ReprojectionError3DList");
818 
819  for (int nWire = 0; nWire < numberOfSegmentedPoints / 3; ++nWire)
820  {
821  vtkSmartPointer<vtkXMLDataElement> reprojectionError3DElement = vtkSmartPointer<vtkXMLDataElement>::New();
822  reprojectionError3DElement->SetName("ReprojectionError3D");
823  reprojectionError3DElement->SetAttribute("WireName", this->NWires[nWire].GetWires()[1].GetName().c_str());
824  reprojectionError3DElement->SetDoubleAttribute("ErrorMm", this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError3Ds[nWire][numberOfSegmentedFramesSoFar]);
825  reprojectionError3Ds->AddNestedElement(reprojectionError3DElement);
826  }
827 
828  // ReprojectionError2Ds
829  vtkSmartPointer<vtkXMLDataElement> reprojectionError2Ds = vtkSmartPointer<vtkXMLDataElement>::New();
830  reprojectionError2Ds->SetName("ReprojectionError2DList");
831 
832  for (int wire = 0; wire < numberOfSegmentedPoints; ++wire)
833  {
834  double reprojectionError2D[2] = { this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError2Ds[wire][numberOfSegmentedFramesSoFar][0], this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.ReprojectionError2Ds[wire][numberOfSegmentedFramesSoFar][1] };
835  vtkSmartPointer<vtkXMLDataElement> reprojectionError2DElement = vtkSmartPointer<vtkXMLDataElement>::New();
836  reprojectionError2DElement->SetName("ReprojectionError2D");
837  reprojectionError2DElement->SetAttribute("WireName", this->NWires[wire / 3].GetWires()[wire % 3].GetName().c_str());
838  reprojectionError2DElement->SetVectorAttribute("ErrorPx", 2, reprojectionError2D);
839  reprojectionError2Ds->AddNestedElement(reprojectionError2DElement);
840  }
841 
842  frame->AddNestedElement(segmentedWireIntersectionPointsPos_Image);
843  frame->AddNestedElement(reprojectionError3Ds);
844  frame->AddNestedElement(reprojectionError2Ds);
845 
846  vtkSmartPointer<vtkXMLDataElement> trackedFrame = vtkSmartPointer<vtkXMLDataElement>::New();
847  if (validationTrackedFrameList->GetTrackedFrame(frameNumber)->PrintToXML(trackedFrame, std::vector<igsioTransformName>()) == PLUS_SUCCESS)
848  {
849  frame->AddNestedElement(trackedFrame);
850  }
851 
852  validationData->AddNestedElement(frame);
853 
854  numberOfSegmentedFramesSoFar++;
855  }
856 
857  // CalibrationData
858  vtkSmartPointer<vtkXMLDataElement> calibrationData = vtkSmartPointer<vtkXMLDataElement>::New();
859  calibrationData->SetName("CalibrationData");
860 
861  numberOfSegmentedFramesSoFar = 0;
862  for (int frameNumber = calibrationStartFrame; frameNumber < calibrationEndFrame; ++frameNumber)
863  {
864  vtkPoints* segmentedPointsVtk = calibrationTrackedFrameList->GetTrackedFrame(frameNumber)->GetFiducialPointsCoordinatePx();
865 
866  // Frame
867  vtkSmartPointer<vtkXMLDataElement> frame = vtkSmartPointer<vtkXMLDataElement>::New();
868  frame->SetName("Frame");
869  frame->SetIntAttribute("Index", frameNumber);
870 
871  if (segmentedPointsVtk == NULL)
872  {
873  frame->SetAttribute("SegmentationStatus", "HasNotBeenRun");
874  calibrationData->AddNestedElement(frame);
875  continue;
876  }
877  if (segmentedPointsVtk->GetNumberOfPoints() == 0)
878  {
879  frame->SetAttribute("SegmentationStatus", "Failed");
880  calibrationData->AddNestedElement(frame);
881  continue;
882  }
883  if (segmentedPointsVtk->GetNumberOfPoints() % 3 != 0)
884  {
885  frame->SetAttribute("SegmentationStatus", "InvalidPatterns");
886  calibrationData->AddNestedElement(frame);
887  continue;
888  }
889  frame->SetAttribute("SegmentationStatus", "OK");
890 
891  // SegmentedPoints
892  vtkSmartPointer<vtkXMLDataElement> segmentedWireIntersectionPointsPos_Image = vtkSmartPointer<vtkXMLDataElement>::New();
893  segmentedWireIntersectionPointsPos_Image->SetName("SegmentedPoints");
894 
895  int numberOfSegmentedPoints = segmentedPointsVtk->GetNumberOfPoints();
896  for (int wire = 0; wire < numberOfSegmentedPoints; wire++)
897  {
898  double point[3];
899  segmentedPointsVtk->GetPoint(wire, point);
900 
901  vtkSmartPointer<vtkXMLDataElement> pointElement = vtkSmartPointer<vtkXMLDataElement>::New();
902  pointElement->SetName("Point");
903  pointElement->SetAttribute("WireName", this->NWires[wire / 3].GetWires()[wire % 3].GetName().c_str());
904  pointElement->SetVectorAttribute("Position", 3, point);
905  segmentedWireIntersectionPointsPos_Image->AddNestedElement(pointElement);
906  }
907 
908  // MiddleWire
909  vtkSmartPointer<vtkXMLDataElement> middleWires = vtkSmartPointer<vtkXMLDataElement>::New();
910  middleWires->SetName("MiddleWires");
911 
912  for (unsigned int nWireIndex = 0; nWireIndex < this->PreProcessedWirePositions[CALIBRATION_ALL].FramePositions[numberOfSegmentedFramesSoFar].MiddleWireIntersectionPointsPos_Probe.size(); ++nWireIndex)
913  {
914  vtkSmartPointer<vtkXMLDataElement> middleWire = vtkSmartPointer<vtkXMLDataElement>::New();
915  middleWire->SetName("MiddleWire");
916 
917  double middleWirePositionInImageFrame[4];
918  double middleWirePositionInProbeFrame[4];
919  for (int k = 0; k < 4; ++k)
920  {
921  middleWirePositionInImageFrame[k] = this->PreProcessedWirePositions[CALIBRATION_ALL].FramePositions[numberOfSegmentedFramesSoFar].AllWiresIntersectionPointsPos_Image[nWireIndex * 3 + 1].get(k);
922  middleWirePositionInProbeFrame[k] = this->PreProcessedWirePositions[CALIBRATION_ALL].FramePositions[numberOfSegmentedFramesSoFar].MiddleWireIntersectionPointsPos_Probe[nWireIndex].get(k);
923  }
924  middleWire->SetVectorAttribute("PositionInImageFrame", 4, middleWirePositionInImageFrame);
925  middleWire->SetVectorAttribute("PositionInProbeFrame", 4, middleWirePositionInProbeFrame);
926  middleWires->AddNestedElement(middleWire);
927  }
928 
929  // ReprojectionError3Ds
930  vtkSmartPointer<vtkXMLDataElement> reprojectionError3Ds = vtkSmartPointer<vtkXMLDataElement>::New();
931  reprojectionError3Ds->SetName("ReprojectionError3DList");
932 
933  for (int nWire = 0; nWire < numberOfSegmentedPoints / 3; ++nWire)
934  {
935  vtkSmartPointer<vtkXMLDataElement> reprojectionError3DElement = vtkSmartPointer<vtkXMLDataElement>::New();
936  reprojectionError3DElement->SetName("ReprojectionError3D");
937  reprojectionError3DElement->SetAttribute("WireName", this->NWires[nWire].GetWires()[1].GetName().c_str());
938  reprojectionError3DElement->SetDoubleAttribute("ErrorMm", this->PreProcessedWirePositions[CALIBRATION_ALL].NWireErrors.ReprojectionError3Ds[nWire][numberOfSegmentedFramesSoFar]);
939  reprojectionError3Ds->AddNestedElement(reprojectionError3DElement);
940  }
941 
942  // ReprojectionError2Ds
943  vtkSmartPointer<vtkXMLDataElement> reprojectionError2Ds = vtkSmartPointer<vtkXMLDataElement>::New();
944  reprojectionError2Ds->SetName("ReprojectionError2DList");
945 
946  for (int wire = 0; wire < numberOfSegmentedPoints; ++wire)
947  {
948  double reprojectionError2D[2] = { this->PreProcessedWirePositions[CALIBRATION_ALL].NWireErrors.ReprojectionError2Ds[wire][numberOfSegmentedFramesSoFar][0], this->PreProcessedWirePositions[CALIBRATION_ALL].NWireErrors.ReprojectionError2Ds[wire][numberOfSegmentedFramesSoFar][1] };
949  vtkSmartPointer<vtkXMLDataElement> reprojectionError2DElement = vtkSmartPointer<vtkXMLDataElement>::New();
950  reprojectionError2DElement->SetName("ReprojectionError2D");
951  reprojectionError2DElement->SetAttribute("WireName", this->NWires[wire / 3].GetWires()[wire % 3].GetName().c_str());
952  reprojectionError2DElement->SetVectorAttribute("ErrorPx", 2, reprojectionError2D);
953  reprojectionError2Ds->AddNestedElement(reprojectionError2DElement);
954  }
955 
956  frame->AddNestedElement(segmentedWireIntersectionPointsPos_Image);
957  frame->AddNestedElement(middleWires);
958  frame->AddNestedElement(reprojectionError3Ds);
959  frame->AddNestedElement(reprojectionError2Ds);
960 
961  vtkSmartPointer<vtkXMLDataElement> trackedFrame = vtkSmartPointer<vtkXMLDataElement>::New();
962  if (calibrationTrackedFrameList->GetTrackedFrame(frameNumber)->PrintToXML(trackedFrame, std::vector<igsioTransformName>()) == PLUS_SUCCESS)
963  {
964  frame->AddNestedElement(trackedFrame);
965  }
966 
967  calibrationData->AddNestedElement(frame);
968 
969  numberOfSegmentedFramesSoFar++;
970  }
971 
972  errorReport->AddNestedElement(reprojectionError3DStatistics);
973  errorReport->AddNestedElement(reprojectionError2DStatistics);
974  errorReport->AddNestedElement(validationData);
975  errorReport->AddNestedElement(calibrationData);
976 
977  probeCalibrationResult->AddNestedElement(calibrationResults);
978  probeCalibrationResult->AddNestedElement(errorReport);
979 
980  return PLUS_SUCCESS;
981 }
982 
983 //------------------------------------------------------------------------------------------------------
984 PlusStatus vtkPlusProbeCalibrationAlgo::GetCalibrationReport(std::vector<double>* calibError, std::vector<double>* validError, vnl_matrix_fixed<double, 4, 4>* imageToProbeTransformMatrix)
985 {
986  (*imageToProbeTransformMatrix) = this->ImageToProbeTransformMatrix;
987  calibError->push_back(this->PreProcessedWirePositions[CALIBRATION_ALL].NWireErrors.RmsError2D);
988  validError->push_back(this->PreProcessedWirePositions[VALIDATION_ALL].NWireErrors.RmsError2D);
989  return PLUS_SUCCESS;
990 }
991 
992 //----------------------------------------------------------------------------
993 void vtkPlusProbeCalibrationAlgo::UpdateNonOutlierData(const std::set<int>& outliers)
994 {
995  for (unsigned int frameIndex = 0; frameIndex < this->PreProcessedWirePositions[CALIBRATION_ALL].FramePositions.size(); ++frameIndex)
996  {
997  bool outlier = false;
998  for (unsigned int nWireIndex = 0; nWireIndex < this->NWires.size(); ++nWireIndex)
999  {
1000  if (outliers.find(frameIndex * this->NWires.size() + nWireIndex) != outliers.end())
1001  {
1002  // any of the nWires is an outlier, so skip the whole frame
1003  outlier = true;
1004  break;
1005  }
1006  }
1007  if (outlier)
1008  {
1009  continue;
1010  }
1011  // not outlier frame, copy to the non-outlier list
1013  }
1014 }
1015 
1016 //-----------------------------------------------------------------------------
1017 double vtkPlusProbeCalibrationAlgo::PointToWireDistance(const vnl_double_3& aPoint, const vnl_double_3& aLineEndPoint1, const vnl_double_3& aLineEndPoint2)
1018 {
1019  // Convert point from vnl to vtk format
1020  double wireP0[3] = {aLineEndPoint1[0], aLineEndPoint1[1], aLineEndPoint1[2]};
1021  double wireP1[3] = {aLineEndPoint2[0], aLineEndPoint2[1], aLineEndPoint2[2]};
1022  double segmentedPoint[3] = {aPoint(0), aPoint(1), aPoint(2)};
1023  double distanceToWire = sqrt(vtkLine::DistanceToLine(segmentedPoint, wireP0, wireP1));
1024  return distanceToWire;
1025 }
1026 
1027 //--------------------------------------------------------------------------------
1028 void vtkPlusProbeCalibrationAlgo::ComputeError2d(const vnl_matrix_fixed<double, 4, 4>& imageToProbeMatrix, double& errorMean, double& errorStDev, double& errorRms)
1029 {
1030  ComputeError2d(CALIBRATION_NOT_OUTLIER, imageToProbeMatrix, errorMean, errorStDev, errorRms);
1031 }
1032 
1033 //--------------------------------------------------------------------------------
1034 void vtkPlusProbeCalibrationAlgo::ComputeError2d(PreProcessedWirePositionIdType datasetType, const vnl_matrix_fixed<double, 4, 4>& imageToProbeMatrix,
1035  double& errorMean, double& errorStDev, double& errorRms,
1036  std::vector< std::vector< vnl_vector_fixed<double, 2> > >* reprojectionError2Ds /*=NULL*/)
1037 {
1038  std::vector<double> reprojectionErrors;
1039 
1040  int nWires = this->NWires.size(); // number of N-wires in the calibration pattern
1041 
1042  vnl_vector_fixed<double, 4> segmentedInPhantomFrame_vnl;
1043  segmentedInPhantomFrame_vnl(3) = 1;
1044  vnl_vector_fixed<double, 4> wireFrontPoint_Phantom;
1045  vnl_vector_fixed<double, 4> wireBackPoint_Phantom;
1046  wireFrontPoint_Phantom(3) = 1;
1047  wireBackPoint_Phantom(3) = 1;
1048  vnl_vector_fixed<double, 4> wireFrontPoint_Image;
1049  vnl_vector_fixed<double, 4> wireBackPoint_Image;
1050  wireFrontPoint_Image(3) = 1;
1051  wireBackPoint_Image(3) = 1;
1052 
1053  int numberOfFrames = this->PreProcessedWirePositions[datasetType].FramePositions.size();
1054  if (reprojectionError2Ds != NULL)
1055  {
1056  reprojectionError2Ds->clear();
1057  reprojectionError2Ds->resize(this->NWires.size() * 3);
1058  }
1059 
1060  for (int frameIndex = 0; frameIndex < numberOfFrames; frameIndex++) // for each frame
1061  {
1062  vnl_matrix_fixed<double, 4, 4> probeToPhantomTransform_vnl = this->PreProcessedWirePositions[datasetType].FramePositions[frameIndex].ProbeToPhantomTransform;
1063  vnl_matrix_fixed<double, 4, 4> phantomToImageTransform_vnl = vnl_inverse(imageToProbeMatrix) * vnl_inverse(probeToPhantomTransform_vnl);
1064  vtkSmartPointer<vtkMatrix4x4> phantomToImageTransform = vtkSmartPointer<vtkMatrix4x4>::New();
1065  PlusMath::ConvertVnlMatrixToVtkMatrix(phantomToImageTransform_vnl, phantomToImageTransform);
1066 
1067  double normalVector[3] = { 0.0, 0.0, 1.0 };
1068  double origin[3] = { 0.0, 0.0, 0.0 };
1069 
1070  for (int nWireIndex = 0; nWireIndex < nWires; nWireIndex++) // for each segmented N-wire
1071  {
1072  for (int wireIndex = 0; wireIndex < 3; wireIndex++) // for each segmented point
1073  {
1074  // Compute the wire intersection point position in the image
1075  PlusFidWire wire = this->NWires[nWireIndex].GetWires()[wireIndex];
1076  double wireEndPointFrontInPhantomFrame[4] = { wire.EndPointFront[0], wire.EndPointFront[1], wire.EndPointFront[2], 1.0 };
1077  double wireEndPointBackInPhantomFrame[4] = { wire.EndPointBack[0], wire.EndPointBack[1], wire.EndPointBack[2], 1.0 };
1078  double wireEndPointFrontInImageFrame[4] = {0};
1079  double wireEndPointBackInImageFrame[4] = {0};
1080  phantomToImageTransform->MultiplyPoint(wireEndPointFrontInPhantomFrame, wireEndPointFrontInImageFrame);
1081  phantomToImageTransform->MultiplyPoint(wireEndPointBackInPhantomFrame, wireEndPointBackInImageFrame);
1082 
1083  double computedPositionInImagePlane[3];
1084  double t = 0; // Parametric coordinate along the line
1085 
1086  // Compute intersection of wire and image plane
1087  if ((! vtkPlane::IntersectWithLine(wireEndPointFrontInImageFrame, wireEndPointBackInImageFrame, normalVector, origin, t, computedPositionInImagePlane))
1088  && (wireEndPointFrontInImageFrame[3] * wireEndPointBackInImageFrame[3] < 0)) // This condition to ensure that warning is thrown only if the zero value is returned because both points are on the same side of the image plane (in that case the intersection is still valid although the return value is zero)
1089  {
1090  LOG_WARNING("Image plane and wire are parallel!");
1091  if (reprojectionError2Ds != NULL)
1092  {
1093  vnl_vector_fixed<double, 2> reprojectionError2D(2, DBL_MAX);
1094  (*reprojectionError2Ds)[nWireIndex * 3 + wireIndex].push_back(reprojectionError2D);
1095  }
1096  continue;
1097  }
1098 
1099  // Get the segmented intersection position in the image
1100  vnl_vector_fixed<double, 4> segmentedPoint_Image = this->PreProcessedWirePositions[datasetType].FramePositions[frameIndex].AllWiresIntersectionPointsPos_Image[3 * nWireIndex + wireIndex];
1101 
1102  vnl_vector_fixed<double, 2> reprojectionError2D;
1103  reprojectionError2D[0] = segmentedPoint_Image[0] - computedPositionInImagePlane[0];
1104  reprojectionError2D[1] = segmentedPoint_Image[1] - computedPositionInImagePlane[1];
1105 
1106  if (reprojectionError2Ds != NULL)
1107  {
1108  (*reprojectionError2Ds)[nWireIndex * 3 + wireIndex].push_back(reprojectionError2D);
1109  }
1110  reprojectionErrors.push_back(reprojectionError2D.magnitude());
1111  }
1112  }
1113  }
1114 
1115  igsioMath::ComputeMeanAndStdev(reprojectionErrors, errorMean, errorStDev);
1116  igsioMath::ComputeRms(reprojectionErrors, errorRms);
1117 }
1118 
1119 //--------------------------------------------------------------------------------
1120 void vtkPlusProbeCalibrationAlgo::ComputeError3d(std::vector<double>& reprojectionErrors, PreProcessedWirePositionIdType datasetType, const vnl_matrix_fixed<double, 4, 4>& imageToProbeMatrix)
1121 {
1122  reprojectionErrors.clear();
1123  int numberOfFrames = this->PreProcessedWirePositions[datasetType].FramePositions.size();
1124  vnl_vector_fixed<double, 4> segmentedInProbeFrame_vnl;
1125  vnl_vector_fixed<double, 4> pointErrorVector;
1126  for (int frameIndex = 0; frameIndex < numberOfFrames; frameIndex++)
1127  {
1128  for (unsigned int nWireIndex = 0; nWireIndex < this->NWires.size(); nWireIndex++)
1129  {
1130  // Find the projection in the probe frame
1131  segmentedInProbeFrame_vnl = imageToProbeMatrix * this->PreProcessedWirePositions[datasetType].FramePositions[frameIndex].AllWiresIntersectionPointsPos_Image[nWireIndex * 3 + 1];
1132 
1133  pointErrorVector = segmentedInProbeFrame_vnl - this->PreProcessedWirePositions[datasetType].FramePositions[frameIndex].MiddleWireIntersectionPointsPos_Probe[nWireIndex];
1134  reprojectionErrors.push_back(pointErrorVector.magnitude());
1135  }
1136  }
1137 }
1138 
1139 //--------------------------------------------------------------------------------
1140 void vtkPlusProbeCalibrationAlgo::ComputeError3d(const vnl_matrix_fixed<double, 4, 4>& imageToProbeMatrix, double& errorMean, double& errorStDev, double& errorRms)
1141 {
1142  std::vector<double> reprojectionErrors;
1143  ComputeError3d(reprojectionErrors, CALIBRATION_NOT_OUTLIER, imageToProbeMatrix);
1144  igsioMath::ComputeMeanAndStdev(reprojectionErrors, errorMean, errorStDev);
1145  igsioMath::ComputeRms(reprojectionErrors, errorRms);
1146 }
1147 
1148 //--------------------------------------------------------------------------------
1150 {
1152 }
1153 
1154 //--------------------------------------------------------------------------------
1156 {
1158 }
1159 
1160 //--------------------------------------------------------------------------------
1162 {
1164 }
1165 
1166 //--------------------------------------------------------------------------------
1168 {
1170 }
1171 
1172 //--------------------------------------------------------------------------------
1174 {
1176 }
vnl_matrix_fixed< double, 4, 4 > ImageToProbeTransformMatrix
vtkPlusProbeCalibrationOptimizerAlgo * Optimizer
std::string GetOutputPath(const std::string &subPath)
std::string GetResultString(int precision=3)
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
void ComputeError2d(const vnl_matrix_fixed< double, 4, 4 > &imageToProbeMatrix, double &errorMean, double &errorStDev, double &errorRms)
PlusStatus ComputeImageToProbeTransformByLinearLeastSquaresMethod(vnl_matrix_fixed< double, 4, 4 > &imageToProbeTransformMatrix, std::set< int > &outliers)
PlusStatus Calibrate(vtkIGSIOTrackedFrameList *validationTrackedFrameList, int validationStartFrame, int validationEndFrame, vtkIGSIOTrackedFrameList *calibrationTrackedFrameList, int calibrationStartFrame, int calibrationEndFrame, vtkIGSIOTransformRepository *transformRepository, const std::vector< PlusNWire > &nWires)
static const double DEFAULT_ERROR_CONFIDENCE_INTERVAL
std::vector< vnl_vector_fixed< double, 2 > > ReprojectionError2DMeans
igsioStatus PlusStatus
Definition: PlusCommon.h:40
void SetImageToProbeSeedTransform(const vnl_matrix_fixed< double, 4, 4 > &imageToProbeTransformMatrix)
void SetProbeCalibrationAlgo(vtkPlusProbeCalibrationAlgo *probeCalibrationAlgo)
for i
PlusStatus SaveCalibrationResultAndErrorReportToXML(vtkIGSIOTrackedFrameList *validationTrackedFrameList, int validationStartFrame, int validationEndFrame, vtkIGSIOTrackedFrameList *calibrationTrackedFrameList, int calibrationStartFrame, int calibrationEndFrame)
#define PLUS_FAIL
Definition: PlusCommon.h:43
vnl_matrix_fixed< double, 4, 4 > GetOptimizedImageToProbeTransformMatrix()
void UpdateNonOutlierData(const std::set< int > &outliers)
static vtkPlusConfig * GetInstance()
static const int MIN_NUMBER_OF_VALID_CALIBRATION_FRAMES
static void PrintMatrix(vnl_matrix_fixed< double, 4, 4 > matrix, std::ostringstream &stream, int precision=3)
Definition: PlusMath.cxx:377
PlusStatus ComputeReprojectionErrors3D(PreProcessedWirePositionIdType datasetType, const vnl_matrix_fixed< double, 4, 4 > &imageToProbeTransformMatrix)
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
std::vector< vnl_vector_fixed< double, 4 > > MiddleWireIntersectionPointsPos_Probe
static void ConvertVtkMatrixToVnlMatrix(const vtkMatrix4x4 *inVtkMatrix, vnl_matrix_fixed< double, 4, 4 > &outVnlMatrix)
Definition: PlusMath.cxx:347
std::vector< std::vector< double > > ReprojectionError3Ds
virtual void SetCalibrationDate(const char *)
PlusStatus ComputeReprojectionErrors2D(PreProcessedWirePositionIdType datasetType, const vnl_matrix_fixed< double, 4, 4 > &imageToProbeTransformMatrix)
void GetImageToProbeTransformMatrix(vtkMatrix4x4 *imageToProbeMatrix)
PlusStatus ReadConfiguration(vtkXMLDataElement *aConfig)
std::vector< std::vector< vnl_vector_fixed< double, 2 > > > ReprojectionError2Ds
Initial translation vector alpha
Definition: algo3.m:26
void SetAndValidateImageToProbeTransform(const vnl_matrix_fixed< double, 4, 4 > &imageToProbeTransformMatrix, vtkIGSIOTransformRepository *transformRepository)
static void LogMatrix(const vnl_matrix_fixed< double, 4, 4 > &matrix, int precision=3)
Definition: PlusMath.cxx:385
void ComputeError3d(const vnl_matrix_fixed< double, 4, 4 > &imageToProbeMatrix, double &errorMean, double &errorStDev, double &errorRms)
std::vector< vnl_vector_fixed< double, 2 > > ReprojectionError2DStdDevs
std::vector< vnl_vector_fixed< double, 4 > > AllWiresIntersectionPointsPos_Image
PlusStatus GetXMLCalibrationResultAndErrorReport(vtkIGSIOTrackedFrameList *validationTrackedFrameList, int validationStartFrame, int validationEndFrame, vtkIGSIOTrackedFrameList *calibrationTrackedFrameList, int calibrationStartFrame, int calibrationEndFrame, vtkXMLDataElement *probeCalibrationResult)
static vtkPlusProbeCalibrationOptimizerAlgo * New()
PreProcessedWirePositionsType PreProcessedWirePositions[LAST_PREPROCESSED_WIRE_POS_ID]
static double PointToWireDistance(const vnl_double_3 &aPoint, const vnl_double_3 &aLineEndPoint1, const vnl_double_3 &aLineEndPoint2)
PlusStatus ReadConfiguration(vtkXMLDataElement *aConfig)
vtkStandardNewMacro(vtkPlusProbeCalibrationAlgo)
PlusStatus AddPositionsPerImage(igsioTrackedFrame *trackedFrame, vtkIGSIOTransformRepository *transformRepository, PreProcessedWirePositionIdType datasetType)
for t
Definition: exploreFolders.m:9
static void ConvertVnlMatrixToVtkMatrix(const vnl_matrix_fixed< double, 4, 4 > &inVnlMatrix, vtkMatrix4x4 *outVtkMatrix)
Definition: PlusMath.cxx:361
PlusStatus GetCalibrationReport(std::vector< double > *calibError, std::vector< double > *validError, vnl_matrix_fixed< double, 4, 4 > *imageToProbeTransformMatrix)
virtual void PrintSelf(ostream &os, vtkIndent indent)
Probe calibration algorithm class.