Fraxinus  18.10
An IGT application
cxRouteToTarget.cpp
Go to the documentation of this file.
1 
2 
3 #include "cxRouteToTarget.h"
4 #include <vtkPolyData.h>
5 #include "cxBranchList.h"
6 #include "cxBranch.h"
7 #include <vtkCellArray.h>
8 #include "vtkCardinalSpline.h"
9 #include <QDir>
10 #include "cxTime.h"
11 #include "cxVisServices.h"
12 #include <QTextStream>
13 
14 typedef vtkSmartPointer<class vtkCardinalSpline> vtkCardinalSplinePtr;
15 
16 namespace cx
17 {
18 
20  mBranchListPtr(new BranchList),
21  mProjectedIndex(0)
22 {
23 }
24 
25 
27 {
28 }
29 
30 /*
31 void RouteToTarget::setCenterline(vtkPolyDataPtr centerline)
32 {
33  mCLpoints = this->getCenterlinePositions(centerline);
34 }
35 */
36 
38 {
39 
40  int N = centerline_r->GetNumberOfPoints();
41  Eigen::MatrixXd CLpoints(3,N);
42  for(vtkIdType i = 0; i < N; i++)
43  {
44  double p[3];
45  centerline_r->GetPoint(i,p);
46  Eigen::Vector3d position;
47  position(0) = p[0]; position(1) = p[1]; position(2) = p[2];
48  CLpoints.block(0 , i , 3 , 1) = position;
49  }
50  return CLpoints;
51 }
52 
54 {
55  if (mBranchListPtr)
56  mBranchListPtr->deleteAllBranches();
57 
58  Eigen::MatrixXd CLpoints_r = getCenterlinePositions(centerline_r);
59 
60  mBranchListPtr->findBranchesInCenterline(CLpoints_r);
61 
62  mBranchListPtr->calculateOrientations();
63  mBranchListPtr->smoothOrientations();
64  //mBranchListPtr->smoothBranchPositions(40);
65 
66  std::cout << "Number of branches in CT centerline: " << mBranchListPtr->getBranches().size() << std::endl;
67 }
68 
69 
71 {
72  double minDistance = 100000;
73  int minDistancePositionIndex;
74  BranchPtr minDistanceBranch;
75  std::vector<BranchPtr> branches = mBranchListPtr->getBranches();
76  for (int i = 0; i < branches.size(); i++)
77  {
78  Eigen::MatrixXd positions = branches[i]->getPositions();
79  for (int j = 0; j < positions.cols(); j++)
80  {
81  double D = findDistance(positions.col(j), targetCoordinate_r);
82  if (D < minDistance)
83  {
84  minDistance = D;
85  minDistanceBranch = branches[i];
86  minDistancePositionIndex = j;
87  }
88  }
89  }
90 
91  mProjectedBranchPtr = minDistanceBranch;
92  mProjectedIndex = minDistancePositionIndex;
93 }
94 
95 
97 {
98  mRoutePositions.clear();
99 
100  searchBranchUp(mProjectedBranchPtr, mProjectedIndex);
101 }
102 
103 /*
104  RouteToTarget::searchBranchUp is finding all positions from a given index on a branch and up
105  the airway tree to the top of trachea. All positions are added to mRoutePositions, which stores
106  all positions along the route-to-target.
107  Before the positions are added they are smoothed by RouteToTarget::smoothBranch.
108 */
109 void RouteToTarget::searchBranchUp(BranchPtr searchBranchPtr, int startIndex)
110 {
111  if (!searchBranchPtr)
112  return;
113 
114  std::vector< Eigen::Vector3d > positions = smoothBranch(searchBranchPtr, startIndex, searchBranchPtr->getPositions().col(startIndex));
115  //std::vector< Eigen::Vector3d > positions = getBranchPositions(searchBranchPtr, startIndex);
116 
117  for (int i = 0; i<=startIndex && i<positions.size(); i++)
118  mRoutePositions.push_back(positions[i]);
119 
120  BranchPtr parentBranchPtr = searchBranchPtr->getParentBranch();
121  if (parentBranchPtr)
122  searchBranchUp(parentBranchPtr, parentBranchPtr->getPositions().cols()-1);
123 }
124 
125 
127 {
128  mTargetPosition = targetCoordinate_r;
129  findClosestPointInBranches(targetCoordinate_r);
131 
132  vtkPolyDataPtr retval = addVTKPoints(mRoutePositions);
133 
134  return retval;
135 }
136 
138 {
139  mTargetPosition = targetCoordinate_r;
140  double extentionPointIncrement = 0.25; //mm
141  mExtendedRoutePositions.clear();
142  mExtendedRoutePositions = mRoutePositions;
143  if(mRoutePositions.size() > 0)
144  {
145  double extentionDistance = findDistance(mRoutePositions.front(),targetCoordinate_r);
146  Eigen::Vector3d extentionVectorNormalized = ( targetCoordinate_r - mRoutePositions.front() ) / extentionDistance;
147  int numberOfextentionPoints = int(extentionDistance / extentionPointIncrement);
148  Eigen::Vector3d extentionPointIncrementVector = extentionVectorNormalized * extentionDistance / numberOfextentionPoints;
149 
150  for (int i = 1; i<= numberOfextentionPoints; i++)
151  {
152  mExtendedRoutePositions.insert(mExtendedRoutePositions.begin(), mRoutePositions.front() + extentionPointIncrementVector*i);
153  }
154  }
155 
156  vtkPolyDataPtr retval = addVTKPoints(mExtendedRoutePositions);
157  return retval;
158 }
159 
160 vtkPolyDataPtr RouteToTarget::addVTKPoints(std::vector<Eigen::Vector3d> positions)
161 {
162  vtkPolyDataPtr retval = vtkPolyDataPtr::New();
163  vtkPointsPtr points = vtkPointsPtr::New();
164  vtkCellArrayPtr lines = vtkCellArrayPtr::New();
165  int numberOfPositions = positions.size();
166  for (int j = numberOfPositions - 1; j >= 0; j--)
167  {
168  points->InsertNextPoint(positions[j](0),positions[j](1),positions[j](2));
169  }
170  for (int j = 0; j < numberOfPositions-1; j++)
171  {
172  vtkIdType connection[2] = {j, j+1};
173  lines->InsertNextCell(2, connection);
174  }
175  retval->SetPoints(points);
176  retval->SetLines(lines);
177  return retval;
178 }
179 
180 
181 /*
182  RouteToTarget::getBranchPositions is used to get positions of a branch without smoothing.
183  Equivalent to RouteToTarget::smoothBranch without smoothing.
184 */
185 std::vector< Eigen::Vector3d > RouteToTarget::getBranchPositions(BranchPtr branchPtr, int startIndex)
186 {
187  Eigen::MatrixXd branchPositions = branchPtr->getPositions();
188  std::vector< Eigen::Vector3d > positions;
189 
190  for (int i = startIndex; i >=0; i--)
191  positions.push_back(branchPositions.col(i));
192 
193  return positions;
194 }
195 
196 /*
197  RouteToTarget::smoothBranch is smoothing the positions of a centerline branch by using vtkCardinalSpline.
198  The degree of smoothing is dependent on the branch radius and the shape of the branch.
199  First, the method tests if a straight line from start to end of the branch is sufficient by the condition of
200  all positions along the line being within the lumen of the airway (max distance from original centerline
201  is set to branch radius).
202  If this fails, one more control point is added to the spline at the time, until the condition is fulfilled.
203  The control point added for each iteration is the position with the larges deviation from the original/unfiltered
204  centerline.
205 */
206 std::vector< Eigen::Vector3d > RouteToTarget::smoothBranch(BranchPtr branchPtr, int startIndex, Eigen::MatrixXd startPosition)
207 {
208  vtkCardinalSplinePtr splineX = vtkSmartPointer<vtkCardinalSpline>::New();
209  vtkCardinalSplinePtr splineY = vtkSmartPointer<vtkCardinalSpline>::New();
210  vtkCardinalSplinePtr splineZ = vtkSmartPointer<vtkCardinalSpline>::New();
211 
212  double branchRadius = branchPtr->findBranchRadius();
213 
214  //add control points to spline
215 
216  //add first position
217  Eigen::MatrixXd positions = branchPtr->getPositions();
218  splineX->AddPoint(0,startPosition(0));
219  splineY->AddPoint(0,startPosition(1));
220  splineZ->AddPoint(0,startPosition(2));
221 
222 
223  // Add last position if no parent branch, else add parents position closest to current branch.
224  // Branch positions are stored in order from head to feet (e.g. first position is top of trachea),
225  // while route-to-target is generated from target to top of trachea.
226  if(!branchPtr->getParentBranch())
227  {
228  splineX->AddPoint(startIndex,positions(0,0));
229  splineY->AddPoint(startIndex,positions(1,0));
230  splineZ->AddPoint(startIndex,positions(2,0));
231  }
232  else
233  {
234  Eigen::MatrixXd parentPositions = branchPtr->getParentBranch()->getPositions();
235  splineX->AddPoint(startIndex,parentPositions(0,parentPositions.cols()-1));
236  splineY->AddPoint(startIndex,parentPositions(1,parentPositions.cols()-1));
237  splineZ->AddPoint(startIndex,parentPositions(2,parentPositions.cols()-1));
238 
239  }
240 
241  //Add points until all filtered/smoothed postions are minimum 1 mm inside the airway wall, (within r - 1 mm).
242  //This is to make sure the smoothed centerline is within the lumen of the airways.
243  double maxAcceptedDistanceToOriginalPosition = std::max(branchRadius - 1, 1.0);
244  double maxDistanceToOriginalPosition = maxAcceptedDistanceToOriginalPosition + 1;
245  int maxDistanceIndex = -1;
246  std::vector< Eigen::Vector3d > smoothingResult;
247 
248  //add positions to spline
249  while (maxDistanceToOriginalPosition >= maxAcceptedDistanceToOriginalPosition && splineX->GetNumberOfPoints() < startIndex)
250  {
251  if(maxDistanceIndex > 0)
252  {
253  //add to spline the position with largest distance to original position
254  splineX->AddPoint(maxDistanceIndex,positions(0,startIndex - maxDistanceIndex));
255  splineY->AddPoint(maxDistanceIndex,positions(1,startIndex - maxDistanceIndex));
256  splineZ->AddPoint(maxDistanceIndex,positions(2,startIndex - maxDistanceIndex));
257  }
258 
259  //evaluate spline - get smoothed positions
260  maxDistanceToOriginalPosition = 0.0;
261  smoothingResult.clear();
262  for(int j=0; j<=startIndex; j++)
263  {
264  double splineParameter = j;
265  Eigen::Vector3d tempPoint;
266  tempPoint(0) = splineX->Evaluate(splineParameter);
267  tempPoint(1) = splineY->Evaluate(splineParameter);
268  tempPoint(2) = splineZ->Evaluate(splineParameter);
269  smoothingResult.push_back(tempPoint);
270 
271  //calculate distance to original (non-filtered) position
272  double distance = findDistanceToLine(tempPoint, positions);
273  //finding the index with largest distance
274  if (distance > maxDistanceToOriginalPosition)
275  {
276  maxDistanceToOriginalPosition = distance;
277  maxDistanceIndex = j;
278  }
279  }
280  }
281 
282  return smoothingResult;
283 }
284 
286 {
287  QString RTTpath = services->patient()->getActivePatientFolder() + "/RouteToTargetInformation/";
288  QDir RTTDirectory(RTTpath);
289  if (!RTTDirectory.exists()) // Creating RouteToTargetInformation folder if it does not exist
290  RTTDirectory.mkpath(RTTpath);
291 
292  QString format = timestampSecondsFormat();
293  QString filePath = RTTpath + QDateTime::currentDateTime().toString(format) + "_RouteToTargetInformation.txt";
294 
295  QFile outfile(filePath);
296  if (outfile.open(QIODevice::ReadWrite))
297  {
298  QTextStream stream(&outfile);
299 
300  stream << "#Target position:" << endl;
301  stream << mTargetPosition(0) << " " << mTargetPosition(1) << " " << mTargetPosition(2) << " " << endl;
302  if (mProjectedBranchPtr)
303  {
304  stream << "#Route to target generations:" << endl;
305  stream << mProjectedBranchPtr->findGenerationNumber() << endl;
306  }
307 
308  stream << "#Trachea length (mm):" << endl;
309  BranchPtr trachea = mBranchListPtr->getBranches()[0];
310  int numberOfPositionsInTrachea = trachea->getPositions().cols();
311  double tracheaLength = calculateRouteLength(smoothBranch(trachea, numberOfPositionsInTrachea-1, trachea->getPositions().col(numberOfPositionsInTrachea-1)));
312  stream << tracheaLength << endl;
313 
314  stream << "#Route to target length - from Carina (mm):" << endl;
315  stream << calculateRouteLength(mRoutePositions) - tracheaLength << endl;
316  stream << "#Extended route to target length - from Carina (mm):" << endl;
317  stream << calculateRouteLength(mExtendedRoutePositions) - tracheaLength << endl;
318  }
319 }
320 
321 double RouteToTarget::calculateRouteLength(std::vector< Eigen::Vector3d > route)
322 {
323  double routeLenght = 0;
324  for (int i=0; i<route.size()-1; i++)
325  {
326  double d0 = route[i+1](0) - route[i](0);
327  double d1 = route[i+1](1) - route[i](1);
328  double d2 = route[i+1](2) - route[i](2);
329 
330  routeLenght += sqrt( d0*d0 +d1*d1 + d2*d2 );
331  }
332 
333  return routeLenght;
334 }
335 
336 double findDistanceToLine(Eigen::MatrixXd point, Eigen::MatrixXd line)
337 {
338  double minDistance = findDistance(point, line.col(0));
339  for (int i=1; i<line.cols(); i++)
340  if (minDistance > findDistance(point, line.col(i)))
341  minDistance = findDistance(point, line.col(i));
342 
343  return minDistance;
344 }
345 
346 double findDistance(Eigen::MatrixXd p1, Eigen::MatrixXd p2)
347 {
348  double d0 = p1(0) - p2(0);
349  double d1 = p1(1) - p2(1);
350  double d2 = p1(2) - p2(2);
351 
352  double D = sqrt( d0*d0 + d1*d1 + d2*d2 );
353 
354  return D;
355 }
356 
357 
358 } /* namespace cx */
vtkPolyDataPtr findExtendedRoute(Vector3D targetCoordinate_r)
double findDistanceToLine(Eigen::MatrixXd point, Eigen::MatrixXd line)
boost::shared_ptr< class VisServices > VisServicesPtr
Definition: cxMainWindow.h:40
vtkSmartPointer< class vtkCellArray > vtkCellArrayPtr
vtkSmartPointer< vtkPoints > vtkPointsPtr
vtkPolyDataPtr addVTKPoints(std::vector< Eigen::Vector3d > positions)
std::vector< Eigen::Vector3d > getBranchPositions(BranchPtr branchPtr, int startIndex)
QString timestampSecondsFormat()
Definition: cxTime.cpp:18
boost::shared_ptr< class Branch > BranchPtr
double findDistance(Eigen::MatrixXd p1, Eigen::MatrixXd p2)
vtkSmartPointer< class vtkCardinalSpline > vtkCardinalSplinePtr
Definition: cxAccusurf.cpp:17
void findClosestPointInBranches(Vector3D targetCoordinate_r)
void searchBranchUp(BranchPtr searchBranchPtr, int startIndex)
void processCenterline(vtkPolyDataPtr centerline_r)
void addRouteInformationToFile(VisServicesPtr services)
vtkSmartPointer< class vtkCardinalSpline > vtkCardinalSplinePtr
double calculateRouteLength(std::vector< Eigen::Vector3d > route)
vtkSmartPointer< vtkPolyData > vtkPolyDataPtr
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:42
vtkPolyDataPtr findRouteToTarget(Vector3D targetCoordinate_r)
Eigen::MatrixXd getCenterlinePositions(vtkPolyDataPtr centerline_r)
Namespace for all CustusX production code.