CustusX  20.03-rc1
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 "cxPointMetric.h"
8 #include <vtkCellArray.h>
9 #include "vtkCardinalSpline.h"
10 #include <QDir>
11 #include "cxTime.h"
12 #include "cxVisServices.h"
13 #include <QTextStream>
14 #include <QJsonObject>
15 #include <QJsonArray>
16 #include <QList>
17 
18 typedef vtkSmartPointer<class vtkCardinalSpline> vtkCardinalSplinePtr;
19 
20 namespace cx
21 {
22 
24  mBranchListPtr(new BranchList),
25  mProjectedIndex(0)
26 {
27 }
28 
29 
31 {
32 }
33 
34 /*
35 void RouteToTarget::setCenterline(vtkPolyDataPtr centerline)
36 {
37  mCLpoints = this->getCenterlinePositions(centerline);
38 }
39 */
40 
42 {
43 
44  int N = centerline_r->GetNumberOfPoints();
45  Eigen::MatrixXd CLpoints(3,N);
46  for(vtkIdType i = 0; i < N; i++)
47  {
48  double p[3];
49  centerline_r->GetPoint(i,p);
50  Eigen::Vector3d position;
51  position(0) = p[0]; position(1) = p[1]; position(2) = p[2];
52  CLpoints.block(0 , i , 3 , 1) = position;
53  }
54  return CLpoints;
55 }
56 
58 {
59  if (mBranchListPtr)
60  mBranchListPtr->deleteAllBranches();
61 
62  vtkPolyDataPtr centerline_r = mesh->getTransformedPolyDataCopy(mesh->get_rMd());
63 
64  Eigen::MatrixXd CLpoints_r = getCenterlinePositions(centerline_r);
65 
66  mBranchListPtr->findBranchesInCenterline(CLpoints_r);
67 
68  mBranchListPtr->calculateOrientations();
69  mBranchListPtr->smoothOrientations();
70  //mBranchListPtr->smoothBranchPositions(40);
71 
72  std::cout << "Number of branches in CT centerline: " << mBranchListPtr->getBranches().size() << std::endl;
73 }
74 
75 
77 {
78  double minDistance = 100000;
79  int minDistancePositionIndex;
80  BranchPtr minDistanceBranch;
81  std::vector<BranchPtr> branches = mBranchListPtr->getBranches();
82  for (int i = 0; i < branches.size(); i++)
83  {
84  Eigen::MatrixXd positions = branches[i]->getPositions();
85  for (int j = 0; j < positions.cols(); j++)
86  {
87  double D = findDistance(positions.col(j), targetCoordinate_r);
88  if (D < minDistance)
89  {
90  minDistance = D;
91  minDistanceBranch = branches[i];
92  minDistancePositionIndex = j;
93  }
94  }
95  }
96 
97  mProjectedBranchPtr = minDistanceBranch;
98  mProjectedIndex = minDistancePositionIndex;
99 }
100 
101 
103 {
104  mRoutePositions.clear();
105 
106  searchBranchUp(mProjectedBranchPtr, mProjectedIndex);
107 }
108 
109 /*
110  RouteToTarget::searchBranchUp is finding all positions from a given index on a branch and up
111  the airway tree to the top of trachea. All positions are added to mRoutePositions, which stores
112  all positions along the route-to-target.
113  Before the positions are added they are smoothed by RouteToTarget::smoothBranch.
114 */
115 void RouteToTarget::searchBranchUp(BranchPtr searchBranchPtr, int startIndex)
116 {
117  if (!searchBranchPtr)
118  return;
119 
120  std::vector< Eigen::Vector3d > positions = smoothBranch(searchBranchPtr, startIndex, searchBranchPtr->getPositions().col(startIndex));
121  //std::vector< Eigen::Vector3d > positions = getBranchPositions(searchBranchPtr, startIndex);
122 
123  for (int i = 0; i<=startIndex && i<positions.size(); i++)
124  mRoutePositions.push_back(positions[i]);
125 
126  mBranchingIndex.push_back(mRoutePositions.size()-1);
127 
128  BranchPtr parentBranchPtr = searchBranchPtr->getParentBranch();
129  if (parentBranchPtr)
130  searchBranchUp(parentBranchPtr, parentBranchPtr->getPositions().cols()-1);
131 }
132 
133 
135 {
136  mTargetPosition = targetPoint->getCoordinate();
137 
138  findClosestPointInBranches(mTargetPosition);
140 
141  vtkPolyDataPtr retval = addVTKPoints(mRoutePositions);
142 
143  return retval;
144 }
145 
147 {
148  mTargetPosition = targetPoint->getCoordinate();
149  double extentionPointIncrement = 0.25; //mm
150  mExtendedRoutePositions.clear();
151  mExtendedRoutePositions = mRoutePositions;
152  if(mRoutePositions.size() > 0)
153  {
154  double extentionDistance = findDistance(mRoutePositions.front(),mTargetPosition);
155  Eigen::Vector3d extentionVectorNormalized = ( mTargetPosition - mRoutePositions.front() ) / extentionDistance;
156  int numberOfextentionPoints = int(extentionDistance / extentionPointIncrement);
157  Eigen::Vector3d extentionPointIncrementVector = extentionVectorNormalized * extentionDistance / numberOfextentionPoints;
158 
159  for (int i = 1; i<= numberOfextentionPoints; i++)
160  {
161  mExtendedRoutePositions.insert(mExtendedRoutePositions.begin(), mRoutePositions.front() + extentionPointIncrementVector*i);
162  }
163  }
164 
165  vtkPolyDataPtr retval = addVTKPoints(mExtendedRoutePositions);
166  return retval;
167 }
168 
169 vtkPolyDataPtr RouteToTarget::addVTKPoints(std::vector<Eigen::Vector3d> positions)
170 {
171  vtkPolyDataPtr retval = vtkPolyDataPtr::New();
172  vtkPointsPtr points = vtkPointsPtr::New();
173  vtkCellArrayPtr lines = vtkCellArrayPtr::New();
174  int numberOfPositions = positions.size();
175  for (int j = numberOfPositions - 1; j >= 0; j--)
176  {
177  points->InsertNextPoint(positions[j](0),positions[j](1),positions[j](2));
178  }
179  for (int j = 0; j < numberOfPositions-1; j++)
180  {
181  vtkIdType connection[2] = {j, j+1};
182  lines->InsertNextCell(2, connection);
183  }
184  retval->SetPoints(points);
185  retval->SetLines(lines);
186  return retval;
187 }
188 
189 
190 /*
191  RouteToTarget::getBranchPositions is used to get positions of a branch without smoothing.
192  Equivalent to RouteToTarget::smoothBranch without smoothing.
193 */
194 std::vector< Eigen::Vector3d > RouteToTarget::getBranchPositions(BranchPtr branchPtr, int startIndex)
195 {
196  Eigen::MatrixXd branchPositions = branchPtr->getPositions();
197  std::vector< Eigen::Vector3d > positions;
198 
199  for (int i = startIndex; i >=0; i--)
200  positions.push_back(branchPositions.col(i));
201 
202  return positions;
203 }
204 
205 /*
206  RouteToTarget::smoothBranch is smoothing the positions of a centerline branch by using vtkCardinalSpline.
207  The degree of smoothing is dependent on the branch radius and the shape of the branch.
208  First, the method tests if a straight line from start to end of the branch is sufficient by the condition of
209  all positions along the line being within the lumen of the airway (max distance from original centerline
210  is set to branch radius).
211  If this fails, one more control point is added to the spline at the time, until the condition is fulfilled.
212  The control point added for each iteration is the position with the larges deviation from the original/unfiltered
213  centerline.
214 */
215 std::vector< Eigen::Vector3d > RouteToTarget::smoothBranch(BranchPtr branchPtr, int startIndex, Eigen::MatrixXd startPosition)
216 {
217  vtkCardinalSplinePtr splineX = vtkSmartPointer<vtkCardinalSpline>::New();
218  vtkCardinalSplinePtr splineY = vtkSmartPointer<vtkCardinalSpline>::New();
219  vtkCardinalSplinePtr splineZ = vtkSmartPointer<vtkCardinalSpline>::New();
220 
221  double branchRadius = branchPtr->findBranchRadius();
222 
223  //add control points to spline
224 
225  //add first position
226  Eigen::MatrixXd positions = branchPtr->getPositions();
227  splineX->AddPoint(0,startPosition(0));
228  splineY->AddPoint(0,startPosition(1));
229  splineZ->AddPoint(0,startPosition(2));
230 
231 
232  // Add last position if no parent branch, else add parents position closest to current branch.
233  // Branch positions are stored in order from head to feet (e.g. first position is top of trachea),
234  // while route-to-target is generated from target to top of trachea.
235  if(!branchPtr->getParentBranch())
236  {
237  splineX->AddPoint(startIndex,positions(0,0));
238  splineY->AddPoint(startIndex,positions(1,0));
239  splineZ->AddPoint(startIndex,positions(2,0));
240  }
241  else
242  {
243  Eigen::MatrixXd parentPositions = branchPtr->getParentBranch()->getPositions();
244  splineX->AddPoint(startIndex,parentPositions(0,parentPositions.cols()-1));
245  splineY->AddPoint(startIndex,parentPositions(1,parentPositions.cols()-1));
246  splineZ->AddPoint(startIndex,parentPositions(2,parentPositions.cols()-1));
247 
248  }
249 
250  //Add points until all filtered/smoothed postions are minimum 1 mm inside the airway wall, (within r - 1 mm).
251  //This is to make sure the smoothed centerline is within the lumen of the airways.
252  double maxAcceptedDistanceToOriginalPosition = std::max(branchRadius - 1, 1.0);
253  double maxDistanceToOriginalPosition = maxAcceptedDistanceToOriginalPosition + 1;
254  int maxDistanceIndex = -1;
255  std::vector< Eigen::Vector3d > smoothingResult;
256 
257  //add positions to spline
258  while (maxDistanceToOriginalPosition >= maxAcceptedDistanceToOriginalPosition && splineX->GetNumberOfPoints() < startIndex)
259  {
260  if(maxDistanceIndex > 0)
261  {
262  //add to spline the position with largest distance to original position
263  splineX->AddPoint(maxDistanceIndex,positions(0,startIndex - maxDistanceIndex));
264  splineY->AddPoint(maxDistanceIndex,positions(1,startIndex - maxDistanceIndex));
265  splineZ->AddPoint(maxDistanceIndex,positions(2,startIndex - maxDistanceIndex));
266  }
267 
268  //evaluate spline - get smoothed positions
269  maxDistanceToOriginalPosition = 0.0;
270  smoothingResult.clear();
271  for(int j=0; j<=startIndex; j++)
272  {
273  double splineParameter = j;
274  Eigen::Vector3d tempPoint;
275  tempPoint(0) = splineX->Evaluate(splineParameter);
276  tempPoint(1) = splineY->Evaluate(splineParameter);
277  tempPoint(2) = splineZ->Evaluate(splineParameter);
278  smoothingResult.push_back(tempPoint);
279 
280  //calculate distance to original (non-filtered) position
281  double distance = findDistanceToLine(tempPoint, positions);
282  //finding the index with largest distance
283  if (distance > maxDistanceToOriginalPosition)
284  {
285  maxDistanceToOriginalPosition = distance;
286  maxDistanceIndex = j;
287  }
288  }
289  }
290 
291  return smoothingResult;
292 }
293 
295 {
296  QString RTTpath = services->patient()->getActivePatientFolder() + "/RouteToTargetInformation/";
297  QDir RTTDirectory(RTTpath);
298  if (!RTTDirectory.exists()) // Creating RouteToTargetInformation folder if it does not exist
299  RTTDirectory.mkpath(RTTpath);
300 
301  QString format = timestampSecondsFormat();
302  QString filePath = RTTpath + QDateTime::currentDateTime().toString(format) + "_RouteToTargetInformation.txt";
303 
304  QFile outfile(filePath);
305  if (outfile.open(QIODevice::ReadWrite))
306  {
307  QTextStream stream(&outfile);
308 
309  stream << "#Target position:" << endl;
310  stream << mTargetPosition(0) << " " << mTargetPosition(1) << " " << mTargetPosition(2) << " " << endl;
311  if (mProjectedBranchPtr)
312  {
313  stream << "#Route to target generations:" << endl;
314  stream << mProjectedBranchPtr->findGenerationNumber() << endl;
315  }
316 
317  stream << "#Trachea length (mm):" << endl;
318  BranchPtr trachea = mBranchListPtr->getBranches()[0];
319  int numberOfPositionsInTrachea = trachea->getPositions().cols();
320  double tracheaLength = calculateRouteLength(smoothBranch(trachea, numberOfPositionsInTrachea-1, trachea->getPositions().col(numberOfPositionsInTrachea-1)));
321  stream << tracheaLength << endl;
322 
323  stream << "#Route to target length - from Carina (mm):" << endl;
324  stream << calculateRouteLength(mRoutePositions) - tracheaLength << endl;
325  stream << "#Extended route to target length - from Carina (mm):" << endl;
326  stream << calculateRouteLength(mExtendedRoutePositions) - tracheaLength << endl;
327  }
328 }
329 
330 double RouteToTarget::calculateRouteLength(std::vector< Eigen::Vector3d > route)
331 {
332  double routeLenght = 0;
333  for (int i=0; i<route.size()-1; i++)
334  {
335  double d0 = route[i+1](0) - route[i](0);
336  double d1 = route[i+1](1) - route[i](1);
337  double d2 = route[i+1](2) - route[i](2);
338 
339  routeLenght += sqrt( d0*d0 +d1*d1 + d2*d2 );
340  }
341 
342  return routeLenght;
343 }
344 
345 
347 {
348  if (mExtendedRoutePositions.empty())
349  {
350  std::cout << "mExtendedRoutePositions is empty." << std::endl;
351  return;
352  }
353 
354  int numberOfExtendedPositions = mExtendedRoutePositions.size() - mRoutePositions.size();
355 
356  ofstream out(filename.toStdString().c_str());
357  out << "# [xPos yPos zPos BranchingPoint (0=Normal, 1=Branching position, 2=Extended from airway to target)] ";
358  out << "Number of positions: ";
359  out << mExtendedRoutePositions.size(); // write number of positions
360  out << "\n";
361 
362  for (int i = 1; i < mExtendedRoutePositions.size(); i++)
363  {
364  out << mExtendedRoutePositions[i](0) << " "; // write x coordinate
365  out << mExtendedRoutePositions[i](1) << " "; // write y coordinate
366  out << mExtendedRoutePositions[i](2) << " "; // write z coordinate
367 
368  if ( std::find(mBranchingIndex.begin(), mBranchingIndex.end(), i - numberOfExtendedPositions) != mBranchingIndex.end() )
369  out << "1 ";
370  else if (i <= numberOfExtendedPositions)
371  out << "2 ";
372  else
373  out << "0 ";
374 
375  out << "\n";
376  }
377 
378  out.close();
379 }
380 
382 {
383  QJsonArray array;
384  if ( mRoutePositions.empty() || mExtendedRoutePositions.empty() )
385  {
386  std::cout << "mRoutePositions is empty." << std::endl;
387  return array;
388  }
389 
390  int numberOfExtendedPositions = mExtendedRoutePositions.size() - mRoutePositions.size();
391 
392  for (int i = 1; i < mExtendedRoutePositions.size(); i++)
393  {
394  QJsonObject position;
395  position.insert( "x", mExtendedRoutePositions[i](0) );
396  position.insert( "y", mExtendedRoutePositions[i](1) );
397  position.insert( "z", mExtendedRoutePositions[i](2) );
398 
399  if ( std::find(mBranchingIndex.begin(), mBranchingIndex.end(), i - numberOfExtendedPositions) != mBranchingIndex.end() )
400  position.insert("Flag", 1);
401  else if (i <= numberOfExtendedPositions)
402  position.insert("Flag", 2);
403  else
404  position.insert("Flag", 0);
405 
406  array.append(position);
407  }
408 
409  return array;
410 }
411 
412 
413 double findDistanceToLine(Eigen::MatrixXd point, Eigen::MatrixXd line)
414 {
415  double minDistance = findDistance(point, line.col(0));
416  for (int i=1; i<line.cols(); i++)
417  if (minDistance > findDistance(point, line.col(i)))
418  minDistance = findDistance(point, line.col(i));
419 
420  return minDistance;
421 }
422 
423 double findDistance(Eigen::MatrixXd p1, Eigen::MatrixXd p2)
424 {
425  double d0 = p1(0) - p2(0);
426  double d1 = p1(1) - p2(1);
427  double d2 = p1(2) - p2(2);
428 
429  double D = sqrt( d0*d0 + d1*d1 + d2*d2 );
430 
431  return D;
432 }
433 
434 
435 } /* namespace cx */
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
void processCenterline(MeshPtr mesh)
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)
vtkPolyDataPtr findRouteToTarget(PointMetricPtr targetPoint)
void addRouteInformationToFile(VisServicesPtr services)
vtkPolyDataPtr findExtendedRoute(PointMetricPtr targetPoint)
vtkSmartPointer< class vtkCardinalSpline > vtkCardinalSplinePtr
QJsonArray makeMarianaCenterlineJSON()
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
boost::shared_ptr< class Mesh > MeshPtr
void makeMarianaCenterlineFile(QString filename)
Eigen::MatrixXd getCenterlinePositions(vtkPolyDataPtr centerline_r)
Namespace for all CustusX production code.
boost::shared_ptr< class PointMetric > PointMetricPtr