PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
PlusMath.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 
11 #include "vnl/vnl_sparse_matrix.h"
12 #include "vnl/vnl_sparse_matrix_linear_system.h"
13 #include "vnl/algo/vnl_lsqr.h"
14 #include "vnl/vnl_cross.h"
15 
16 #include "vtkMath.h"
17 #include "vtkTransform.h"
18 
19 #define MINIMUM_NUMBER_OF_CALIBRATION_EQUATIONS 8
20 
21 //----------------------------------------------------------------------------
23 {
24 
25 }
26 
27 //----------------------------------------------------------------------------
29 {
30 
31 }
32 
33 //----------------------------------------------------------------------------
34 PlusStatus PlusMath::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*/)
35 {
36  LOG_TRACE("PlusMath::LSQRMinimize");
37 
38  if (aMatrix.size() == 0)
39  {
40  LOG_ERROR("LSQRMinimize: A matrix is empty");
41  resultVector.clear();
42  return PLUS_FAIL;
43  }
44  if (bVector.size() == 0)
45  {
46  LOG_ERROR("LSQRMinimize: b vector is empty");
47  resultVector.clear();
48  return PLUS_FAIL;
49  }
50 
51  // The coefficient matrix aMatrix should be m-by-n and the column vector bVector must have length m.
52  const int n = aMatrix.begin()->size();
53  const int m = bVector.size();
54 
55  std::vector<vnl_vector<double> > aMatrixVnl(m);
56  vnl_vector<double> row(n);
57  for (unsigned int i = 0; i < aMatrix.size(); ++i)
58  {
59  for (unsigned int r = 0; r < aMatrix[i].size(); ++r)
60  {
61  row[r] = aMatrix[i][r];
62  }
63  aMatrixVnl.push_back(row);
64  }
65 
66  return PlusMath::LSQRMinimize(aMatrixVnl, bVector, resultVector, mean, stdev, notOutliersIndices);
67 }
68 
69 //----------------------------------------------------------------------------
70 PlusStatus PlusMath::LSQRMinimize(const std::vector< vnl_vector<double> >& aMatrix, const std::vector<double>& bVector, vnl_vector<double>& resultVector, double* mean/*=NULL*/, double* stdev/*=NULL*/, vnl_vector<unsigned int>* notOutliersIndices /*=NULL*/)
71 {
72  LOG_TRACE("PlusMath::LSQRMinimize");
73 
74  if (aMatrix.size() == 0)
75  {
76  LOG_ERROR("LSQRMinimize: A matrix is empty");
77  resultVector.clear();
78  return PLUS_FAIL;
79  }
80  if (bVector.size() == 0)
81  {
82  LOG_ERROR("LSQRMinimize: b vector is empty");
83  resultVector.clear();
84  return PLUS_FAIL;
85  }
86 
87  // The coefficient matrix aMatrix should be m-by-n and the column vector bVector must have length m.
88  const int n = aMatrix.begin()->size();
89  const int m = bVector.size();
90 
91  vnl_sparse_matrix<double> sparseMatrixLeftSide(m, n);
92  vnl_vector<double> vectorRightSide(m);
93 
94  for (int row = 0; row < m; row++)
95  {
96  // Populate the sparse matrix
97  for (int i = 0; i < n; i++)
98  {
99  sparseMatrixLeftSide(row, i) = aMatrix[row].get(i);
100  }
101 
102  // Populate the vector
103  vectorRightSide.put(row, bVector[row]);
104  }
105 
106  return PlusMath::LSQRMinimize(sparseMatrixLeftSide, vectorRightSide, resultVector, mean, stdev, notOutliersIndices);
107 }
108 
109 
110 //----------------------------------------------------------------------------
111 PlusStatus PlusMath::LSQRMinimize(const vnl_sparse_matrix<double>& sparseMatrixLeftSide, const vnl_vector<double>& vectorRightSide, vnl_vector<double>& resultVector, double* mean/*=NULL*/, double* stdev/*=NULL*/, vnl_vector<unsigned int>* notOutliersIndices/*NULL*/)
112 {
113  LOG_TRACE("PlusMath::LSQRMinimize");
114 
115  PlusStatus returnStatus = PLUS_SUCCESS;
116 
117  vnl_sparse_matrix<double> aMatrix(sparseMatrixLeftSide);
118  vnl_vector<double> bVector(vectorRightSide);
119 
120  bool outlierFound(true);
121 
122  while (outlierFound && (bVector.size() > MINIMUM_NUMBER_OF_CALIBRATION_EQUATIONS))
123  {
124  // Construct linear system defined in VNL
125  vnl_sparse_matrix_linear_system<double> linearSystem(aMatrix, bVector);
126 
127  // Instantiate the LSQR solver
128  vnl_lsqr lsqr(linearSystem);
129 
130  // call minimize on the solver
131  int returnCode = lsqr.minimize(resultVector);
132 
133  switch (returnCode)
134  {
135  case 0: // x = 0 is the exact solution. No iterations were performed.
136  returnStatus = PLUS_SUCCESS;
137  break;
138  case 1: // The equations A*x = b are probably compatible. "
139  // Norm(A*x - b) is sufficiently small, given the "
140  // "values of ATOL and BTOL.",
141  returnStatus = PLUS_SUCCESS;
142  break;
143  case 2: // "The system A*x = b is probably not compatible. "
144  // "A least-squares solution has been obtained that is "
145  // "sufficiently accurate, given the value of ATOL.",
146  returnStatus = PLUS_SUCCESS;
147  break;
148  case 3: // "An estimate of cond(Abar) has exceeded CONLIM. "
149  //"The system A*x = b appears to be ill-conditioned. "
150  // "Otherwise, there could be an error in subroutine APROD.",
151  LOG_WARNING("LSQR fit may be inaccurate, CONLIM exceeded");
152  returnStatus = PLUS_SUCCESS;
153  break;
154  case 4: // "The equations A*x = b are probably compatible. "
155  // "Norm(A*x - b) is as small as seems reasonable on this machine.",
156  returnStatus = PLUS_SUCCESS;
157  break;
158  case 5: // "The system A*x = b is probably not compatible. A least-squares "
159  // "solution has been obtained that is as accurate as seems "
160  // "reasonable on this machine.",
161  returnStatus = PLUS_SUCCESS;
162  break;
163  case 6: // "Cond(Abar) seems to be so large that there is no point in doing further "
164  // "iterations, given the precision of this machine. "
165  // "There could be an error in subroutine APROD.",
166  LOG_ERROR("LSQR fit may be inaccurate, ill-conditioned matrix");
167  return PLUS_FAIL;
168  case 7: // "The iteration limit ITNLIM was reached."
169  LOG_WARNING("LSQR fit may be inaccurate, ITNLIM was reached");
170  returnStatus = PLUS_SUCCESS;
171  break;
172  default:
173  LOG_ERROR("Unkown LSQR return code " << returnCode);
174  return PLUS_FAIL;
175  }
176 
177  const double thresholdMultiplier = 3.0;
178 
179  if (PlusMath::RemoveOutliersFromLSQR(aMatrix, bVector, resultVector, outlierFound, thresholdMultiplier, mean, stdev, notOutliersIndices) != PLUS_SUCCESS)
180  {
181  LOG_WARNING("Failed to remove outliers from linear equations!");
182  return PLUS_FAIL;
183  }
184 
185  if (bVector.size() <= MINIMUM_NUMBER_OF_CALIBRATION_EQUATIONS)
186  {
187  LOG_ERROR("It was not possible calibrate! Not enough equations!");
188  return PLUS_FAIL;
189  }
190  }
191 
192  return returnStatus;
193 }
194 
195 //----------------------------------------------------------------------------
196 PlusStatus PlusMath::RemoveOutliersFromLSQR(vnl_sparse_matrix<double>& sparseMatrixLeftSide,
197  vnl_vector<double>& vectorRightSide,
198  vnl_vector<double>& resultVector,
199  bool& outlierFound,
200  double thresholdMultiplier/* = 3.0*/,
201  double* mean/*=NULL*/,
202  double* stdev/*=NULL*/,
203  vnl_vector<unsigned int>* nonOutlierIndices /*NULL*/)
204 {
205  // Set outlierFound flag to false by default
206  outlierFound = false;
207 
208  const unsigned int numberOfEquations = sparseMatrixLeftSide.rows();
209  const unsigned int numberOfUnknowns = resultVector.size();
210 
211  if (vectorRightSide.size() != numberOfEquations)
212  {
213  LOG_ERROR("Input A matrix and b vector dimensions were not met (number of equations were not the same)!");
214  return PLUS_FAIL;
215  }
216 
217  if (sparseMatrixLeftSide.cols() != numberOfUnknowns)
218  {
219  LOG_ERROR("Input A matrix dimension (columns) and number of unknowns are different (cols: " << sparseMatrixLeftSide.cols() << " unknowns: " << numberOfUnknowns << ")");
220  return PLUS_FAIL;
221  }
222 
223 
224  vnl_vector<double> differenceVector(numberOfEquations, 0);
225  // Compute the difference between the measured and computed data ( Ax - b )
226  for (unsigned int row = 0; row < numberOfEquations; ++row)
227  {
228  vnl_sparse_matrix<double>::row matrixRow = sparseMatrixLeftSide.get_row(row);
229  double difference(0);
230  // Compute Ax - b for each row
231  for (unsigned int i = 0; i < numberOfUnknowns; ++i)
232  {
233  // difference = A1x = a11*x1 + a12*x2 + ...
234  difference += (matrixRow[i].second * resultVector[i]);
235  }
236  // difference = A1x - b1 = a11*x1 + a12*x2 + ... - b1
237  difference -= vectorRightSide[row];
238 
239  // Add the difference to the vector
240  differenceVector.put(row, difference);
241  }
242 
243  // Compute the mean difference
244  const double meanDifference = differenceVector.mean();
245 
246  // Compute the stdev of differences
247  vnl_vector<double> diffFromMean = differenceVector - meanDifference;
248  const double stdevDifference = sqrt(diffFromMean.squared_magnitude() / (1.0 * diffFromMean.size()));
249 
250  LOG_DEBUG("Mean = " << std::fixed << meanDifference << " Stdev = " << stdevDifference);
251 
252  if (mean != NULL)
253  {
254  *mean = meanDifference;
255  }
256 
257  if (stdev != NULL)
258  {
259  *stdev = stdevDifference;
260  }
261 
262  // Temporary containers for input data
263  std::vector< vnl_vector<double> > matrixRowsData;
264  std::vector< vnl_vector<int> > matrixRowsIndecies;
265  std::vector<double> bVector;
266  std::vector<double> auxiliarNonOutlierIndicesVector;
267 
268  vnl_vector<double> rowData(numberOfUnknowns, 0.0);
269  vnl_vector<int> rowIndecies(numberOfUnknowns, 0);
270 
271  // Look for outliers in each equations
272  // If the difference from mean larger than thresholdMultiplier * stdev, remove it from equation
273  for (unsigned int row = 0; row < numberOfEquations; ++row)
274  {
275  if (fabs(differenceVector[row] - meanDifference) < thresholdMultiplier * stdevDifference)
276  {
277  // Not an outlier
278 
279  // Copy data from matrix row in a format which is good for vnl_sparse_matrix<T>::set_row function
280  vnl_sparse_matrix<double>::row matrixRow = sparseMatrixLeftSide.get_row(row);
281  for (unsigned int i = 0; i < matrixRow.size(); ++i)
282  {
283  rowIndecies[i] = matrixRow[i].first;
284  rowData[i] = matrixRow[i].second;
285  }
286 
287  // Save matrix row data, indecies and result vector into std::vectors
288  matrixRowsData.push_back(rowData);
289  matrixRowsIndecies.push_back(rowIndecies);
290  bVector.push_back(vectorRightSide[row]);
291  if (nonOutlierIndices != NULL)
292  {
293  auxiliarNonOutlierIndicesVector.push_back(nonOutlierIndices->get(row));
294  }
295  }
296  else
297  {
298  // Outlier found
299  outlierFound = true;
300  LOG_DEBUG("Outlier: " << std::fixed << differenceVector[row] << "(mean: " << meanDifference << " stdev: " << stdevDifference << " outlierTreshold: " << thresholdMultiplier * stdevDifference << ")");
301  }
302  }
303 
304  // Resize matrices only if we found an outlier
305  if (outlierFound)
306  {
307  // Copy back the new aMatrix and bVector
308  vectorRightSide.clear();
309  vectorRightSide.set_size(bVector.size());
310  for (unsigned int i = 0; i < bVector.size(); ++i)
311  {
312  vectorRightSide.put(i, bVector[i]);
313  }
314 
315  if (nonOutlierIndices != NULL)
316  {
317  (*nonOutlierIndices).clear();
318  (*nonOutlierIndices).set_size(auxiliarNonOutlierIndicesVector.size());
319  for (unsigned int i = 0; i < auxiliarNonOutlierIndicesVector.size(); ++i)
320  {
321  (*nonOutlierIndices).put(i, auxiliarNonOutlierIndicesVector[i]);
322  }
323  }
324 
325 
326  sparseMatrixLeftSide.resize(matrixRowsData.size(), numberOfUnknowns);
327  for (unsigned int r = 0; r < matrixRowsData.size(); r++)
328  {
329  std::vector<int> rowIndices;
330  rowIndices.resize(matrixRowsIndecies[r].size());
331  memcpy(rowIndices.data(), matrixRowsIndecies[r].begin(), sizeof(int)*matrixRowsIndecies[r].size());
332  std::vector<double> rowData;
333  rowData.resize(matrixRowsData[r].size());
334  memcpy(rowData.data(), matrixRowsData[r].begin(), sizeof(double)*matrixRowsData[r].size());
335  sparseMatrixLeftSide.set_row(r, rowIndices, rowData);
336  }
337  }
338  else
339  {
340  LOG_DEBUG("*** Outlier removal was successful! No more outlier found!");
341  }
342 
343  return PLUS_SUCCESS;
344 }
345 
346 //----------------------------------------------------------------------------
347 void PlusMath::ConvertVtkMatrixToVnlMatrix(const vtkMatrix4x4* inVtkMatrix, vnl_matrix_fixed<double, 4, 4>& outVnlMatrix)
348 {
349  LOG_TRACE("PlusMath::ConvertVtkMatrixToVnlMatrix");
350 
351  for (int row = 0; row < 4; row++)
352  {
353  for (int column = 0; column < 4; column++)
354  {
355  outVnlMatrix.put(row, column, inVtkMatrix->GetElement(row, column));
356  }
357  }
358 }
359 
360 //----------------------------------------------------------------------------
361 void PlusMath::ConvertVnlMatrixToVtkMatrix(const vnl_matrix_fixed<double, 4, 4>& inVnlMatrix, vtkMatrix4x4* outVtkMatrix)
362 {
363  LOG_TRACE("PlusMath::ConvertVnlMatrixToVtkMatrix");
364 
365  outVtkMatrix->Identity();
366 
367  for (int row = 0; row < 3; row++)
368  {
369  for (int column = 0; column < 4; column++)
370  {
371  outVtkMatrix->SetElement(row, column, inVnlMatrix.get(row, column));
372  }
373  }
374 }
375 
376 //----------------------------------------------------------------------------
377 void PlusMath::PrintMatrix(vnl_matrix_fixed<double, 4, 4> matrix, std::ostringstream& stream, int precision/* = 3*/)
378 {
379  vtkSmartPointer<vtkMatrix4x4> matrixVtk = vtkSmartPointer<vtkMatrix4x4>::New();
380  ConvertVnlMatrixToVtkMatrix(matrix, matrixVtk);
381  igsioMath::PrintVtkMatrix(matrixVtk, stream, precision);
382 }
383 
384 //----------------------------------------------------------------------------
385 void PlusMath::LogMatrix(const vnl_matrix_fixed<double, 4, 4>& matrix, int precision/* = 3*/)
386 {
387  vtkSmartPointer<vtkMatrix4x4> matrixVtk = vtkSmartPointer<vtkMatrix4x4>::New();
388  ConvertVnlMatrixToVtkMatrix(matrix, matrixVtk);
389  igsioMath::LogVtkMatrix(matrixVtk, precision);
390 }
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
int
Definition: phidget22.h:3069
igsioStatus PlusStatus
Definition: PlusCommon.h:40
for i
#define PLUS_FAIL
Definition: PlusCommon.h:43
#define MINIMUM_NUMBER_OF_CALIBRATION_EQUATIONS
Definition: PlusMath.cxx:19
PlusMath()
Definition: PlusMath.cxx:22
static void PrintMatrix(vnl_matrix_fixed< double, 4, 4 > matrix, std::ostringstream &stream, int precision=3)
Definition: PlusMath.cxx:377
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
static void ConvertVtkMatrixToVnlMatrix(const vtkMatrix4x4 *inVtkMatrix, vnl_matrix_fixed< double, 4, 4 > &outVnlMatrix)
Definition: PlusMath.cxx:347
~PlusMath()
Definition: PlusMath.cxx:28
static void LogMatrix(const vnl_matrix_fixed< double, 4, 4 > &matrix, int precision=3)
Definition: PlusMath.cxx:385
static void ConvertVnlMatrixToVtkMatrix(const vnl_matrix_fixed< double, 4, 4 > &inVnlMatrix, vtkMatrix4x4 *outVtkMatrix)
Definition: PlusMath.cxx:361
static PlusStatus RemoveOutliersFromLSQR(vnl_sparse_matrix< double > &sparseMatrixLeftSide, vnl_vector< double > &vectorRightSide, vnl_vector< double > &resultVector, bool &outlierFound, double thresholdMultiplier=3.0, double *mean=NULL, double *stdev=NULL, vnl_vector< unsigned int > *nonOutlierIndices=NULL)
Definition: PlusMath.cxx:196