4 #include <vtkPolyData.h>
9 #include <vtkCellArray.h>
10 #include "vtkCardinalSpline.h"
11 #include "vtkImageData.h"
12 #include <boost/math/special_functions/round.hpp>
18 #include <QTextStream>
19 #include <QJsonObject>
23 #define PI 3.1415926535897
34 mProjectedBloodVesselIndex(0)
45 mBloodVesselVolume = bloodVesselVolume;
51 int N = centerline_r->GetNumberOfPoints();
52 Eigen::MatrixXd CLpoints(3,N);
53 for(vtkIdType i = 0; i < N; i++)
56 centerline_r->GetPoint(i,p);
57 Eigen::Vector3d position;
58 position(0) = p[0]; position(1) = p[1]; position(2) = p[2];
59 CLpoints.block(0 , i , 3 , 1) = position;
66 mSmoothing = smoothing;
72 mBranchListPtr->deleteAllBranches();
74 vtkPolyDataPtr centerline_r = mesh->getTransformedPolyDataCopy(mesh->get_rMd());
78 mBranchListPtr->findBranchesInCenterline(mCLpoints);
80 mBranchListPtr->smoothOrientations();
82 mBranchListPtr->findBronchoscopeRotation();
84 std::cout <<
"Number of branches in CT centerline: " << mBranchListPtr->getBranches().size() << std::endl;
90 mBranchListPtr = branchList;
95 if (mBloodVesselBranchListPtr)
96 mBloodVesselBranchListPtr->deleteAllBranches();
98 mBloodVesselBranchListPtr->findBranchesInCenterline(positions,
false);
100 mBloodVesselBranchListPtr->smoothOrientations();
101 mBloodVesselBranchListPtr->smoothBranchPositions(40);
103 mBloodVesselBranchListPtr->smoothRadius();
105 BranchPtr branchWithLargestRadius = mBloodVesselBranchListPtr->findBranchWithLargestRadius();
107 if (branchWithLargestRadius->getParentBranch())
109 Eigen::MatrixXd positions = branchWithLargestRadius->getPositions();
110 Eigen::VectorXd radius = branchWithLargestRadius->getRadius();
112 radius.maxCoeff(&maxRadiusIndex);
113 positions.col(positions.cols()-1).swap(positions.col(maxRadiusIndex));
115 std::vector<BranchPtr> allBranches = mBloodVesselBranchListPtr->getBranches();
116 for (
int i = 1; i < allBranches.size(); i++)
117 if (allBranches[i] != branchWithLargestRadius)
119 Eigen::MatrixXd positionsToAppend = allBranches[i]->getPositions();
120 Eigen::MatrixXd resizedPositions(positions.rows(), positions.cols() + positionsToAppend.cols());
121 resizedPositions.rightCols(positions.cols()) = positions;
122 resizedPositions.leftCols(positionsToAppend.cols()) = positionsToAppend;
123 positions = resizedPositions;
126 mBloodVesselBranchListPtr->deleteAllBranches();
128 mBloodVesselBranchListPtr->findBranchesInCenterline(positions,
false);
129 mBloodVesselBranchListPtr->smoothOrientations();
130 mBloodVesselBranchListPtr->smoothBranchPositions(40);
132 mBloodVesselBranchListPtr->smoothRadius();
136 CX_LOG_INFO() <<
"Number of branches in CT blood vessel centerline: " << mBloodVesselBranchListPtr->getBranches().size();
141 double minDistance = 100000;
142 int minDistancePositionIndex;
144 std::vector<BranchPtr> branches = mBranchListPtr->getBranches();
145 for (
int i = 0; i < branches.size(); i++)
147 Eigen::MatrixXd positions = branches[i]->getPositions();
148 for (
int j = 0; j < positions.cols(); j++)
150 double D =
findDistance(positions.col(j), targetCoordinate_r);
154 minDistanceBranch = branches[i];
155 minDistancePositionIndex = j;
160 mProjectedBranchPtr = minDistanceBranch;
161 mProjectedIndex = minDistancePositionIndex;
166 double minDistance = 100000;
167 int minDistancePositionIndex;
169 std::vector<BranchPtr> branches = mBloodVesselBranchListPtr->getBranches();
170 for (
int i = 0; i < branches.size(); i++)
172 Eigen::MatrixXd positions = branches[i]->getPositions();
173 for (
int j = 0; j < positions.cols(); j++)
175 double D =
findDistance(positions.col(j), targetCoordinate_r);
179 minDistanceBranch = branches[i];
180 minDistancePositionIndex = j;
185 mProjectedBloodVesselBranchPtr = minDistanceBranch;
186 mProjectedBloodVesselIndex = minDistancePositionIndex;
192 mRoutePositions.clear();
199 mBloodVesselRoutePositions.clear();
212 if (!searchBranchPtr)
215 std::vector< Eigen::Vector3d > positions;
217 positions =
smoothBranch(searchBranchPtr, startIndex, searchBranchPtr->getPositions().col(startIndex));
221 double cameraRotation = searchBranchPtr->getBronchoscopeRotation();
223 for (
int i = 0; i<=startIndex && i<positions.size(); i++)
225 mRoutePositions.push_back(positions[i]);
226 mCameraRotation.push_back(cameraRotation);
229 mBranchingIndex.push_back(mRoutePositions.size()-1);
231 BranchPtr parentBranchPtr = searchBranchPtr->getParentBranch();
233 searchBranchUp(parentBranchPtr, parentBranchPtr->getPositions().cols()-1);
238 if (!searchBranchPtr)
242 std::vector< Eigen::Vector3d > positions =
getBranchPositions(searchBranchPtr, startIndex);
244 for (
int i = 0; i<=startIndex && i<positions.size(); i++)
246 mBloodVesselRoutePositions.push_back(positions[i]);
249 BranchPtr parentBranchPtr = searchBranchPtr->getParentBranch();
252 if(parentBranchPtr->getAverageRadius() >= searchBranchPtr->getAverageRadius() &&
variance(parentBranchPtr->getRadius()) < 1.0 )
262 mTargetPosition = targetPoint->getCoordinate();
275 mTargetPosition = targetPoint->getCoordinate();
276 double extensionPointIncrement = 0.25;
277 mExtendedRoutePositions.clear();
278 mExtendedRoutePositions = mRoutePositions;
279 mExtendedCameraRotation.clear();
280 mExtendedCameraRotation = mCameraRotation;
281 if(mRoutePositions.size() > 0)
283 double extensionDistance =
findDistance(mRoutePositions.front(),mTargetPosition);
284 Eigen::Vector3d extensionVectorNormalized = ( mTargetPosition - mRoutePositions.front() ) / extensionDistance;
285 int numberOfextensionPoints = int(extensionDistance / extensionPointIncrement);
286 Eigen::Vector3d extensionPointIncrementVector = extensionVectorNormalized * extensionDistance / numberOfextensionPoints;
288 for (
int i = 1; i<= numberOfextensionPoints; i++)
290 mExtendedRoutePositions.insert(mExtendedRoutePositions.begin(), mRoutePositions.front() + extensionPointIncrementVector*i);
291 mExtendedCameraRotation.insert(mExtendedCameraRotation.begin(), mExtendedCameraRotation.front());
304 vtkPolyDataPtr BVcenterline_r = bloodVesselCenterlineMesh->getTransformedPolyDataCopy(bloodVesselCenterlineMesh->get_rMd());
308 if (mRoutePositions.empty())
311 Eigen::MatrixXd::Index closestPositionToEndOfAirwayRTTIndex =
dsearch(mRoutePositions[0], mConnectedPointsInBVCL).first;
314 mConnectedPointsInBVCL.col(mConnectedPointsInBVCL.cols()-1).swap(mConnectedPointsInBVCL.col(closestPositionToEndOfAirwayRTTIndex));
329 if (mConnectedPointsInBVCL.cols() == 0 || !mPathToBloodVesselsFound)
333 airwaysFromBVCenterlinePtr->setTypeToBloodVessel(
true);
334 mBloodVesselBranchListPtr->interpolateBranchPositions(0.1);
335 airwaysFromBVCenterlinePtr->setBranches(mBloodVesselBranchListPtr);
337 airwayMesh = airwaysFromBVCenterlinePtr->generateTubes(2);
346 if ( mRoutePositions.empty() )
349 if ( mBloodVesselRoutePositions.empty() )
351 mMergedAirwayAndBloodVesselRoutePositions = mRoutePositions;
355 if (
findDistance(mRoutePositions.front(),mTargetPosition) <
findDistance(mBloodVesselRoutePositions.front(),mTargetPosition) )
357 CX_LOG_INFO() <<
"No improved route to target found along blood vessels";
358 mMergedAirwayAndBloodVesselRoutePositions = mRoutePositions;
362 std::vector<BranchPtr> branches = mBranchListPtr->getBranches();
363 double minDistance = ( mRoutePositions[0] - mBloodVesselRoutePositions[mBloodVesselRoutePositions.size()-1] ).norm();
364 int connectionIndexBloodVesselRoute = mBloodVesselRoutePositions.size()-1;
365 bool closerAirwayFound =
false;
367 for (
int i = 0; i<branches.size(); i++)
369 if ( branches[i]->findGenerationNumber() > 2 )
371 Eigen::MatrixXd branchPositinos = branches[i]->getPositions();
372 for (
int j = 0; j<branchPositinos.cols(); j++)
375 if (minDistance > distance)
377 minDistance = distance;
378 closestPosition = branchPositinos.col(j);
379 closerAirwayFound =
true;
380 mProjectedBranchPtr = branches[i];
388 if (closerAirwayFound)
394 std::vector< Eigen::Vector3d > mergedPositions;
395 mergedPositions.insert( mergedPositions.end(), mBloodVesselRoutePositions.begin(), mBloodVesselRoutePositions.begin() + connectionIndexBloodVesselRoute );
396 mergedPositions.insert( mergedPositions.end(), mRoutePositions.begin(), mRoutePositions.end() );
399 double extensionPointIncrement = 0.25;
400 double extensionDistance =
findDistance(mergedPositions.front(),mTargetPosition);
401 Eigen::Vector3d extensionVectorNormalized = ( mTargetPosition - mergedPositions.front() ) / extensionDistance;
402 int numberOfextensionPoints = int(extensionDistance / extensionPointIncrement);
403 Eigen::Vector3d extensionPointIncrementVector = extensionVectorNormalized * extensionDistance / numberOfextensionPoints;
405 for (
int i = 1; i<= numberOfextensionPoints; i++)
407 mergedPositions.insert(mergedPositions.begin(), mBloodVesselRoutePositions.front() + extensionPointIncrementVector*i);
410 mMergedAirwayAndBloodVesselRoutePositions = mergedPositions;
417 return addVTKPoints(mMergedAirwayAndBloodVesselRoutePositions);
421 bool RouteToTarget::checkIfRouteToTargetEndsAtEndOfLastBranch()
423 if (!mProjectedBranchPtr)
426 if (!mProjectedIndex)
429 if (mProjectedBranchPtr->getChildBranches().empty())
430 if (mProjectedBranchPtr->getPositions().cols()-1 == mProjectedIndex)
442 int numberOfPositions = positions.size();
443 for (
int j = numberOfPositions - 1; j >= 0; j--)
445 points->InsertNextPoint(positions[j](0),positions[j](1),positions[j](2));
447 for (
int j = 0; j < numberOfPositions-1; j++)
449 vtkIdType connection[2] = {j, j+1};
450 lines->InsertNextCell(2, connection);
452 retval->SetPoints(points);
453 retval->SetLines(lines);
462 QString RTTpath = services->patient()->getActivePatientFolder() +
"/RouteToTargetInformation/";
463 QDir RTTDirectory(RTTpath);
464 if (!RTTDirectory.exists())
465 RTTDirectory.mkpath(RTTpath);
468 QString filePath = RTTpath + QDateTime::currentDateTime().toString(format) +
"_RouteToTargetInformation.txt";
470 QFile outfile(filePath);
471 if (outfile.open(QIODevice::ReadWrite))
473 QTextStream stream(&outfile);
475 stream <<
"#Target position:" << endl;
476 stream << mTargetPosition(0) <<
" " << mTargetPosition(1) <<
" " << mTargetPosition(2) <<
" " << endl;
477 if (mProjectedBranchPtr)
479 stream <<
"#Route to target generations:" << endl;
480 stream << mProjectedBranchPtr->findGenerationNumber() << endl;
483 stream <<
"#Trachea length (mm):" << endl;
485 stream << tracheaLength << endl;
487 stream <<
"#Route to target length - from Carina (mm):" << endl;
489 stream <<
"#Extended route to target length - from Carina (mm):" << endl;
496 BranchPtr trachea = mBranchListPtr->getBranches()[0];
497 int numberOfPositionsInTrachea = trachea->getPositions().cols();
498 double tracheaLength =
calculateRouteLength(
smoothBranch(trachea, numberOfPositionsInTrachea-1, trachea->getPositions().col(numberOfPositionsInTrachea-1)));
499 return tracheaLength;
504 vtkPolyDataPtr centerline_r = route->getTransformedPolyDataCopy(route->get_rMd());
506 std::vector< Eigen::Vector3d > routePositions;
509 int N = centerline_r->GetNumberOfPoints();
510 for(vtkIdType i = 0; i < N; i++)
513 centerline_r->GetPoint(i,p);
514 Eigen::Vector3d position;
515 position(0) = p[0]; position(1) = p[1]; position(2) = p[2];
516 routePositions.push_back(position);
518 return routePositions;
523 double routeLenght = 0;
524 for (
int i=0; i<route.size()-1; i++)
526 double d0 = route[i+1](0) - route[i](0);
527 double d1 = route[i+1](1) - route[i](1);
528 double d2 = route[i+1](2) - route[i](2);
530 routeLenght += sqrt( d0*d0 +d1*d1 + d2*d2 );
538 std::vector<BranchPtr> branches = mBloodVesselBranchListPtr->getBranches();
540 for (
int i = 0; i < branches.size(); i++)
542 Eigen::MatrixXd positions = branches[i]->getPositions();
543 Eigen::MatrixXd orientations = branches[i]->getOrientations();
544 Eigen::VectorXd radius(positions.cols());
546 for (
int j = 0; j < positions.cols(); j++)
551 branches[i]->setRadius(radius);
559 if (!mBloodVesselVolume)
562 vtkImageDataPtr bloodVesselImage = mBloodVesselVolume->getBaseVtkImageData();
563 int* dim = bloodVesselImage->GetDimensions();
564 double* spacing = bloodVesselImage->GetSpacing();
565 Transform3D dMr = mBloodVesselVolume->get_rMd().inverse();
566 Eigen::Vector3d position_r = dMr.coord(position);
570 Eigen::Vector3i indexVector;
575 Eigen::MatrixXd maxRadius(3,2);
576 Eigen::Vector3d perpendicularX = orientation.cross(Eigen::Vector3d::UnitX());
579 Eigen::Vector3d perpendicularY = orientation.cross(Eigen::Vector3d::UnitY());
582 Eigen::Vector3d perpendicularZ = orientation.cross(Eigen::Vector3d::UnitZ());
586 radius = maxRadius.rowwise().mean().minCoeff();
588 if (std::isnan(radius))
597 double maxValue = bloodVesselImage->GetScalarRange()[1];
598 for (
int radiusVoxels=1; radiusVoxels<30; radiusVoxels++)
600 if (perpendicularVector.sum() != 0)
602 Eigen::Vector3d searchDirection = perpendicularVector.normalized() * radiusVoxels;
603 int xIndex = std::max(std::min(indexVector(0) + direction * (
int)
std::round(searchDirection(0)), dim[0]-1), 0);
604 int yIndex = std::max(std::min(indexVector(1) + direction * (
int)
std::round(searchDirection(1)), dim[1]-1), 0);
605 int zIndex = std::max(std::min(indexVector(2) + direction * (
int)
std::round(searchDirection(2)), dim[2]-1), 0);
606 if (bloodVesselImage->GetScalarComponentAsDouble(xIndex, yIndex, zIndex, 0) < maxValue)
608 searchDirection = perpendicularVector.normalized() * (radiusVoxels-1);
609 retval = std::sqrt( std::pow(searchDirection(0)*spacing[0],2) + std::pow(searchDirection(1)*spacing[1],2) + std::pow(searchDirection(2)*spacing[2],2) );
619 std::vector< Eigen::Vector3d > positions = mExtendedRoutePositions;
620 std::reverse(positions.begin(), positions.end());
626 std::vector< double > rotations = mExtendedCameraRotation;
627 std::reverse(rotations.begin(), rotations.end());
633 if (mExtendedRoutePositions.empty())
635 std::cout <<
"mExtendedRoutePositions is empty." << std::endl;
639 int numberOfExtendedPositions = mExtendedRoutePositions.size() - mRoutePositions.size();
641 ofstream out(filename.toStdString().c_str());
642 out <<
"# [xPos yPos zPos BranchingPoint (0=Normal, 1=Branching position, 2=Extended from airway to target)] ";
643 out <<
"Number of positions: ";
644 out << mExtendedRoutePositions.size();
647 for (
int i = 1; i < mExtendedRoutePositions.size(); i++)
649 out << mExtendedRoutePositions[i](0) <<
" ";
650 out << mExtendedRoutePositions[i](1) <<
" ";
651 out << mExtendedRoutePositions[i](2) <<
" ";
653 if ( std::find(mBranchingIndex.begin(), mBranchingIndex.end(), i - numberOfExtendedPositions) != mBranchingIndex.end() )
655 else if (i <= numberOfExtendedPositions)
669 if ( mRoutePositions.empty() || mExtendedRoutePositions.empty() )
671 std::cout <<
"mRoutePositions is empty." << std::endl;
675 int numberOfExtendedPositions = mExtendedRoutePositions.size() - mRoutePositions.size();
677 for (
int i = 1; i < mExtendedRoutePositions.size(); i++)
679 QJsonObject position;
680 position.insert(
"x", mExtendedRoutePositions[i](0) );
681 position.insert(
"y", mExtendedRoutePositions[i](1) );
682 position.insert(
"z", mExtendedRoutePositions[i](2) );
684 if ( std::find(mBranchingIndex.begin(), mBranchingIndex.end(), i - numberOfExtendedPositions) != mBranchingIndex.end() )
685 position.insert(
"Flag", 1);
686 else if (i <= numberOfExtendedPositions)
687 position.insert(
"Flag", 2);
689 position.insert(
"Flag", 0);
691 array.append(position);
701 std::vector<BranchPtr> branches = branchList->getBranches();
702 for (
int i = 0; i < branches.size(); i++)
704 Eigen::MatrixXd positions = branches[i]->getPositions();
705 for (
int j = 0; j < positions.cols(); j++)
707 QJsonObject JsonPosition;
708 JsonPosition.insert(
"x", positions(0,j) );
709 JsonPosition.insert(
"y", positions(1,j) );
710 JsonPosition.insert(
"z", positions(2,j) );
711 array.append(JsonPosition);
724 Eigen::MatrixXd branchPositions = branchPtr->getPositions();
725 std::vector< Eigen::Vector3d > positions;
727 for (
int i = startIndex; i >=0; i--)
728 positions.push_back(branchPositions.col(i));
737 double maxDistanceToAirway = 10;
738 int minNumberOfPositionsInSegment = 100;
740 Eigen::MatrixXd bloodVesselSegment;
742 while (bloodVesselPositions.cols() > minNumberOfPositionsInSegment)
744 Eigen::MatrixXd::Index closestBloodVesselPositionToTarget =
dsearch(targetPosition, bloodVesselPositions).first;
745 std::pair< Eigen::MatrixXd, Eigen::MatrixXd > localPositions =
findLocalPointsInCT(closestBloodVesselPositionToTarget , bloodVesselPositions);
746 bloodVesselPositions = localPositions.second;
748 if ( localPositions.first.cols() >= minNumberOfPositionsInSegment &&
749 dsearchn(airwayPositions, localPositions.first).second.minCoeff() <= maxDistanceToAirway )
751 bloodVesselSegment = localPositions.first;
756 return bloodVesselSegment;
759 std::pair< Eigen::MatrixXd, Eigen::MatrixXd >
findLocalPointsInCT(
int closestCLIndex , Eigen::MatrixXd CLpositions)
761 Eigen::MatrixXd includedPositions;
762 Eigen::MatrixXd positionsNotIncluded = CLpositions;
763 int startIndex = closestCLIndex;
765 bool closePositionFound =
true;
766 while (closePositionFound)
768 closePositionFound =
false;
769 std::pair<Eigen::MatrixXd,Eigen::MatrixXd> connectedPoints =
findConnectedPointsInCT(startIndex , positionsNotIncluded);
770 positionsNotIncluded = connectedPoints.second;
772 if (includedPositions.cols() > 0)
774 includedPositions.conservativeResize(Eigen::NoChange, includedPositions.cols() + connectedPoints.first.cols());
775 includedPositions.rightCols(connectedPoints.first.cols()) = connectedPoints.first;
778 includedPositions = connectedPoints.first;
780 for (
int i = 0; i < includedPositions.cols(); i++)
782 std::pair<Eigen::MatrixXd::Index, double> closePositionSearch =
dsearch(includedPositions.col(i), positionsNotIncluded);
783 if (closePositionSearch.second < 3)
785 closePositionFound =
true;
786 startIndex = closePositionSearch.first;
792 return std::make_pair(includedPositions, positionsNotIncluded);
799 for (
int i=1; i<line.size(); i++)
806 return std::make_pair(index , minDistance);
809 double findDistance(Eigen::MatrixXd p1, Eigen::MatrixXd p2)
811 double d0 = p1(0) - p2(0);
812 double d1 = p1(1) - p2(1);
813 double d2 = p1(2) - p2(2);
815 double D = sqrt( d0*d0 + d1*d1 + d2*d2 );
822 Eigen::MatrixXd positionsMatrix(3, positionsVector.size());
823 for (
int i = 0; i < positionsVector.size(); i++)
825 positionsMatrix(0, i) = positionsVector[i](0);
826 positionsMatrix(1, i) = positionsVector[i](1);
827 positionsMatrix(2, i) = positionsVector[i](2);
829 return positionsMatrix;
834 double mean_X = X.mean();
836 for (
int i = 0; i < X.size(); i++)
837 var += ( X[i]-mean_X ) * ( X[i]-mean_X );