PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusPrincipalMotionDetectionAlgo.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 "vtkObjectFactory.h"
10 #include "vtkTransform.h"
11 #include "vtkMath.h"
12 #include "vtkDoubleArray.h"
13 #include "vtkTable.h"
14 #include "vtkPCAStatistics.h"
15 
17 #include "vtkIGSIOTransformRepository.h"
18 #include "vtkIGSIOTrackedFrameList.h"
19 #include "igsioTrackedFrame.h"
20 
21 //-----------------------------------------------------------------------------
23 
24 //-----------------------------------------------------------------------------
26 {
28  m_SignalTimeRangeMax = -1.0;
29 }
30 
31 //-----------------------------------------------------------------------------
33 {
34 }
35 
36 //----------------------------------------------------------------------------
37 void vtkPlusPrincipalMotionDetectionAlgo::PrintSelf(ostream& os, vtkIndent indent)
38 {
39  this->Superclass::PrintSelf(os, indent);
40 }
41 
42 //-----------------------------------------------------------------------------
43 void vtkPlusPrincipalMotionDetectionAlgo::SetTrackerFrames(vtkIGSIOTrackedFrameList* trackerFrames)
44 {
45  m_TrackerFrames = trackerFrames;
46 }
47 
48 //-----------------------------------------------------------------------------
49 void vtkPlusPrincipalMotionDetectionAlgo::SetSignalTimeRange(double rangeMin, double rangeMax)
50 {
51  m_SignalTimeRangeMin = rangeMin;
52  m_SignalTimeRangeMax = rangeMax;
53 }
54 
55 //-----------------------------------------------------------------------------
57 {
58  m_ProbeToReferenceTransformName = transformName;
59 }
60 
61 //-----------------------------------------------------------------------------
63 {
64  if (m_TrackerFrames == NULL)
65  {
66  LOG_ERROR("Tracker input data verification failed: no tracker data is set");
67  return PLUS_FAIL;
68  }
69  // Make sure tracker frame list is not empty
70  if (m_TrackerFrames->GetNumberOfTrackedFrames() == 0)
71  {
72  LOG_ERROR("Tracker input data verification failed: tracker data set is empty");
73  return PLUS_FAIL;
74  }
75  return PLUS_SUCCESS;
76 }
77 
78 //-----------------------------------------------------------------------------
80 {
81  vtkSmartPointer<vtkIGSIOTransformRepository> transformRepository = vtkSmartPointer<vtkIGSIOTransformRepository>::New();
82  igsioTransformName transformName;
83 
84  if (transformName.SetTransformName(m_ProbeToReferenceTransformName.c_str()) != PLUS_SUCCESS)
85  {
86  LOG_ERROR("Cannot compute tracker position metric, transform name is invalid (" << m_ProbeToReferenceTransformName << ")");
87  return PLUS_FAIL;
88  }
89 
90  // Clear the old tracker timestamps, preparing for an update
91  m_SignalTimestamps.clear();
92  m_SignalValues.clear();
93 
94  // Find the mean tracker position
95  itk::Point<double, 3> trackerPositionSum;
96  trackerPositionSum[0] = trackerPositionSum[1] = trackerPositionSum[2] = 0.0;
97  std::deque<itk::Point<double, 3> > trackerPositions;
98  int numberOfValidFrames = 0;
99  bool signalTimeRangeDefined = (m_SignalTimeRangeMin <= m_SignalTimeRangeMax);
100  for (unsigned int frame = 0; frame < m_TrackerFrames->GetNumberOfTrackedFrames(); ++frame)
101  {
102  igsioTrackedFrame* trackedFrame = m_TrackerFrames->GetTrackedFrame(frame);
103 
104  if (signalTimeRangeDefined && (trackedFrame->GetTimestamp() < m_SignalTimeRangeMin || trackedFrame->GetTimestamp() > m_SignalTimeRangeMax))
105  {
106  // frame is out of the specified signal range
107  continue;
108  }
109 
110  transformRepository->SetTransforms(*trackedFrame);
111 
112 
113  vtkSmartPointer<vtkMatrix4x4> probeToReferenceTransform = vtkSmartPointer<vtkMatrix4x4>::New();
114  ToolStatus status(TOOL_INVALID);
115  transformRepository->GetTransform(transformName, probeToReferenceTransform, &status);
116  if (status != TOOL_OK)
117  {
118  // There is no available transform for this frame; skip that frame
119  continue;
120  }
121 
122  // Store current tracker position
123  itk::Point<double, 3> currTrackerPosition;
124  currTrackerPosition[0] = probeToReferenceTransform->GetElement(0, 3);
125  currTrackerPosition[1] = probeToReferenceTransform->GetElement(1, 3);
126  currTrackerPosition[2] = probeToReferenceTransform->GetElement(2, 3);
127  trackerPositions.push_back(currTrackerPosition);
128 
129  // Add current tracker position to the running total
130  trackerPositionSum[0] = trackerPositionSum[0] + probeToReferenceTransform->GetElement(0, 3);
131  trackerPositionSum[1] = trackerPositionSum[1] + probeToReferenceTransform->GetElement(1, 3);
132  trackerPositionSum[2] = trackerPositionSum[2] + probeToReferenceTransform->GetElement(2, 3);
133  ++numberOfValidFrames;
134 
135  m_SignalTimestamps.push_back(trackedFrame->GetTimestamp()); // These timestamps will be in the desired time range
136  }
137 
138  // Calculate the principal axis of motion (using PCA)
139  itk::Point<double, 3> principalAxisOfMotion;
140  ComputePrincipalAxis(trackerPositions, principalAxisOfMotion, numberOfValidFrames);
141 
142  // Compute the mean tracker position
143  itk::Point<double, 3> meanTrackerPosition;
144  meanTrackerPosition[0] = (trackerPositionSum[0] / static_cast<double>(numberOfValidFrames));
145  meanTrackerPosition[1] = (trackerPositionSum[1] / static_cast<double>(numberOfValidFrames));
146  meanTrackerPosition[2] = (trackerPositionSum[2] / static_cast<double>(numberOfValidFrames));
147 
148  // For each tracker position in the recorded tracker sequence, get its translation from reference.
149  for (unsigned int frame = 0; frame < m_SignalTimestamps.size(); ++frame)
150  {
151  // Project the current tracker position onto the principal axis of motion
152  double currTrackerPositionProjection = trackerPositions.at(frame).GetElement(0) * principalAxisOfMotion[0]
153  + trackerPositions.at(frame).GetElement(1) * principalAxisOfMotion[1]
154  + trackerPositions.at(frame).GetElement(2) * principalAxisOfMotion[2];
155 
156  // Store this translation and corresponding timestamp
157  m_SignalValues.push_back(currTrackerPositionProjection);
158  }
159 
160  return PLUS_SUCCESS;
161 }
162 
163 //-----------------------------------------------------------------------------
164 void vtkPlusPrincipalMotionDetectionAlgo::ComputePrincipalAxis(std::deque<itk::Point<double, 3> >& trackerPositions, itk::Point<double, 3>& principalAxisOfMotion, int numValidFrames)
165 {
166  // Set the X-values
167  const char m0Name[] = "M0";
168  vtkSmartPointer<vtkDoubleArray> dataset1Arr = vtkSmartPointer<vtkDoubleArray>::New();
169  dataset1Arr->SetNumberOfComponents(1);
170  dataset1Arr->SetName(m0Name);
171  for (int i = 0; i < numValidFrames; ++i)
172  {
173  dataset1Arr->InsertNextValue(trackerPositions[i].GetElement(0));
174  }
175 
176  // Set the Y-values
177  const char m1Name[] = "M1";
178  vtkSmartPointer<vtkDoubleArray> dataset2Arr = vtkSmartPointer<vtkDoubleArray>::New();
179  dataset2Arr->SetNumberOfComponents(1);
180  dataset2Arr->SetName(m1Name);
181  for (int i = 0; i < numValidFrames; ++i)
182  {
183  dataset2Arr->InsertNextValue(trackerPositions[i].GetElement(1));
184  }
185 
186  // Set the Z-values
187  const char m2Name[] = "M2";
188  vtkSmartPointer<vtkDoubleArray> dataset3Arr = vtkSmartPointer<vtkDoubleArray>::New();
189  dataset3Arr->SetNumberOfComponents(1);
190  dataset3Arr->SetName(m2Name);
191  for (int i = 0; i < numValidFrames; ++i)
192  {
193  dataset3Arr->InsertNextValue(trackerPositions[i].GetElement(2));
194  }
195 
196  vtkSmartPointer<vtkTable> datasetTable = vtkSmartPointer<vtkTable>::New();
197  datasetTable->AddColumn(dataset1Arr);
198  datasetTable->AddColumn(dataset2Arr);
199  datasetTable->AddColumn(dataset3Arr);
200 
201  vtkSmartPointer<vtkPCAStatistics> pcaStatistics = vtkSmartPointer<vtkPCAStatistics>::New();
202 
203  pcaStatistics->SetInputData(vtkStatisticsAlgorithm::INPUT_DATA, datasetTable);
204 
205  pcaStatistics->SetColumnStatus("M0", 1);
206  pcaStatistics->SetColumnStatus("M1", 1);
207  pcaStatistics->SetColumnStatus("M2", 1);
208  pcaStatistics->RequestSelectedColumns();
209  pcaStatistics->SetDeriveOption(true);
210  pcaStatistics->Update();
211 
212  // Get the eigenvector corresponding to the largest eigenvalue (i.e. the principal axis). The
213  // eigenvectors are stored with the eigenvector corresponding to the largest eigenvalue stored
214  // first (i.e. in the "zero" position) and the eigenvector corresponding to the smallest eigenvalue
215  // stored last.
216  vtkSmartPointer<vtkDoubleArray> eigenvector = vtkSmartPointer<vtkDoubleArray>::New();
217  pcaStatistics->GetEigenvector(0, eigenvector);
218 
219  for (int i = 0; i < eigenvector->GetNumberOfComponents(); ++i)
220  {
221  principalAxisOfMotion[i] = eigenvector->GetComponent(0, i);
222  }
223 }
224 
225 //-----------------------------------------------------------------------------
227 {
229  {
230  return PLUS_FAIL;
231  }
233  {
234  return PLUS_FAIL;
235  }
236  return PLUS_SUCCESS;
237 }
238 
239 //-----------------------------------------------------------------------------
241 {
242  timestamps = m_SignalTimestamps;
243 }
244 
245 //-----------------------------------------------------------------------------
247 {
248  positions = m_SignalValues;
249 }
void ComputePrincipalAxis(std::deque< itk::Point< double, 3 > > &trackerPositions, itk::Point< double, 3 > &principalAxisOfMotion, int numValidFrames)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
for i
void SetSignalTimeRange(double rangeMin, double rangeMax)
#define PLUS_FAIL
Definition: PlusCommon.h:43
void SetTrackerFrames(vtkIGSIOTrackedFrameList *trackerFrames)
void SetProbeToReferenceTransformName(const std::string &probeToReferenceTransformName)
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
Extract the motion component along the the principal axis of the motion. Used for computing a positio...
void GetDetectedPositions(std::deque< double > &positions)
vtkStandardNewMacro(vtkPlusPrincipalMotionDetectionAlgo)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
void GetDetectedTimestamps(std::deque< double > &timestamps)