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 return mBranchListPtr;
100 if (mBloodVesselBranchListPtr)
101 mBloodVesselBranchListPtr->deleteAllBranches();
103 mBloodVesselBranchListPtr->findBranchesInCenterline(positions,
false);
105 mBloodVesselBranchListPtr->smoothOrientations();
106 mBloodVesselBranchListPtr->smoothBranchPositions(40);
108 mBloodVesselBranchListPtr->smoothRadius();
110 BranchPtr branchWithLargestRadius = mBloodVesselBranchListPtr->findBranchWithLargestRadius();
112 if (branchWithLargestRadius->getParentBranch())
114 Eigen::MatrixXd positions = branchWithLargestRadius->getPositions();
115 Eigen::VectorXd radius = branchWithLargestRadius->getRadius();
117 radius.maxCoeff(&maxRadiusIndex);
118 positions.col(positions.cols()-1).swap(positions.col(maxRadiusIndex));
120 std::vector<BranchPtr> allBranches = mBloodVesselBranchListPtr->getBranches();
121 for (
int i = 1; i < allBranches.size(); i++)
122 if (allBranches[i] != branchWithLargestRadius)
124 Eigen::MatrixXd positionsToAppend = allBranches[i]->getPositions();
125 Eigen::MatrixXd resizedPositions(positions.rows(), positions.cols() + positionsToAppend.cols());
126 resizedPositions.rightCols(positions.cols()) = positions;
127 resizedPositions.leftCols(positionsToAppend.cols()) = positionsToAppend;
128 positions = resizedPositions;
131 mBloodVesselBranchListPtr->deleteAllBranches();
133 mBloodVesselBranchListPtr->findBranchesInCenterline(positions,
false);
134 mBloodVesselBranchListPtr->smoothOrientations();
135 mBloodVesselBranchListPtr->smoothBranchPositions(40);
137 mBloodVesselBranchListPtr->smoothRadius();
141 CX_LOG_INFO() <<
"Number of branches in CT blood vessel centerline: " << mBloodVesselBranchListPtr->getBranches().size();
146 double minDistance = 100000;
147 int minDistancePositionIndex;
149 std::vector<BranchPtr> branches = mBranchListPtr->getBranches();
150 for (
int i = 1; i < branches.size(); i++)
152 Eigen::MatrixXd positions = branches[i]->getPositions();
153 for (
int j = 0; j < positions.cols(); j++)
155 double D =
findDistance(positions.col(j), targetCoordinate_r);
159 minDistanceBranch = branches[i];
160 minDistancePositionIndex = j;
165 mProjectedBranchPtr = minDistanceBranch;
166 mProjectedIndex = minDistancePositionIndex;
171 double minDistance = 100000;
172 int minDistancePositionIndex;
174 std::vector<BranchPtr> branches = mBloodVesselBranchListPtr->getBranches();
175 for (
int i = 0; i < branches.size(); i++)
177 Eigen::MatrixXd positions = branches[i]->getPositions();
178 for (
int j = 0; j < positions.cols(); j++)
180 double D =
findDistance(positions.col(j), targetCoordinate_r);
184 minDistanceBranch = branches[i];
185 minDistancePositionIndex = j;
190 mProjectedBloodVesselBranchPtr = minDistanceBranch;
191 mProjectedBloodVesselIndex = minDistancePositionIndex;
197 mRoutePositions.clear();
204 mBloodVesselRoutePositions.clear();
217 if (!searchBranchPtr)
220 std::vector< Eigen::Vector3d > positions;
222 positions =
smoothBranch(searchBranchPtr, startIndex, searchBranchPtr->getPositions().col(startIndex));
226 double cameraRotation = searchBranchPtr->getBronchoscopeRotation();
228 for (
int i = 0; i<=startIndex && i<positions.size(); i++)
230 mRoutePositions.push_back(positions[i]);
231 mRoutePositionsBranch.push_back(searchBranchPtr);
232 mCameraRotation.push_back(cameraRotation);
235 mBranchingIndex.push_back(mRoutePositions.size()-1);
237 BranchPtr parentBranchPtr = searchBranchPtr->getParentBranch();
239 searchBranchUp(parentBranchPtr, parentBranchPtr->getPositions().cols()-1);
244 if (!searchBranchPtr)
248 std::vector< Eigen::Vector3d > positions =
getBranchPositions(searchBranchPtr, startIndex);
250 for (
int i = 0; i<=startIndex && i<positions.size(); i++)
252 mBloodVesselRoutePositions.push_back(positions[i]);
255 BranchPtr parentBranchPtr = searchBranchPtr->getParentBranch();
258 if(parentBranchPtr->getAverageRadius() >= searchBranchPtr->getAverageRadius() &&
variance(parentBranchPtr->getRadius()) < 1.0 )
268 mTargetPosition = targetPoint->getCoordinate();
281 mTargetPosition = targetPoint->getCoordinate();
282 double extensionPointIncrement = 0.25;
283 mExtendedRoutePositions.clear();
284 mExtendedRoutePositions = mRoutePositions;
285 mExtendedCameraRotation.clear();
286 mExtendedCameraRotation = mCameraRotation;
287 if(mRoutePositions.size() > 0)
289 double extensionDistance =
findDistance(mRoutePositions.front(),mTargetPosition);
290 Eigen::Vector3d extensionVectorNormalized = ( mTargetPosition - mRoutePositions.front() ) / extensionDistance;
291 int numberOfextensionPoints = int(extensionDistance / extensionPointIncrement);
292 Eigen::Vector3d extensionPointIncrementVector = extensionVectorNormalized * extensionDistance / numberOfextensionPoints;
294 for (
int i = 1; i<= numberOfextensionPoints; i++)
296 mExtendedRoutePositions.insert(mExtendedRoutePositions.begin(), mRoutePositions.front() + extensionPointIncrementVector*i);
297 mExtendedCameraRotation.insert(mExtendedCameraRotation.begin(), mExtendedCameraRotation.front());
310 vtkPolyDataPtr BVcenterline_r = bloodVesselCenterlineMesh->getTransformedPolyDataCopy(bloodVesselCenterlineMesh->get_rMd());
314 if (mRoutePositions.empty())
317 Eigen::MatrixXd::Index closestPositionToEndOfAirwayRTTIndex =
dsearch(mRoutePositions[0], mConnectedPointsInBVCL).first;
320 mConnectedPointsInBVCL.col(mConnectedPointsInBVCL.cols()-1).swap(mConnectedPointsInBVCL.col(closestPositionToEndOfAirwayRTTIndex));
335 if (mConnectedPointsInBVCL.cols() == 0 || !mPathToBloodVesselsFound)
339 airwaysFromBVCenterlinePtr->setTypeToBloodVessel(
true);
340 mBloodVesselBranchListPtr->interpolateBranchPositions(0.1);
341 airwaysFromBVCenterlinePtr->setBranches(mBloodVesselBranchListPtr);
343 airwayMesh = airwaysFromBVCenterlinePtr->generateTubes(2);
352 if ( mRoutePositions.empty() )
355 if ( mBloodVesselRoutePositions.empty() )
357 mMergedAirwayAndBloodVesselRoutePositions = mRoutePositions;
361 if (
findDistance(mRoutePositions.front(),mTargetPosition) <
findDistance(mBloodVesselRoutePositions.front(),mTargetPosition) )
363 CX_LOG_INFO() <<
"No improved route to target found along blood vessels";
364 mMergedAirwayAndBloodVesselRoutePositions = mRoutePositions;
368 std::vector<BranchPtr> branches = mBranchListPtr->getBranches();
369 double minDistance = ( mRoutePositions[0] - mBloodVesselRoutePositions[mBloodVesselRoutePositions.size()-1] ).norm();
370 int connectionIndexBloodVesselRoute = mBloodVesselRoutePositions.size()-1;
371 bool closerAirwayFound =
false;
373 for (
int i = 0; i<branches.size(); i++)
375 if ( branches[i]->findGenerationNumber() > 2 )
377 Eigen::MatrixXd branchPositinos = branches[i]->getPositions();
378 for (
int j = 0; j<branchPositinos.cols(); j++)
381 if (minDistance > distance)
383 minDistance = distance;
384 closestPosition = branchPositinos.col(j);
385 closerAirwayFound =
true;
386 mProjectedBranchPtr = branches[i];
394 if (closerAirwayFound)
400 std::vector< Eigen::Vector3d > mergedPositions;
401 mergedPositions.insert( mergedPositions.end(), mBloodVesselRoutePositions.begin(), mBloodVesselRoutePositions.begin() + connectionIndexBloodVesselRoute );
402 mergedPositions.insert( mergedPositions.end(), mRoutePositions.begin(), mRoutePositions.end() );
405 double extensionPointIncrement = 0.25;
406 double extensionDistance =
findDistance(mergedPositions.front(),mTargetPosition);
407 Eigen::Vector3d extensionVectorNormalized = ( mTargetPosition - mergedPositions.front() ) / extensionDistance;
408 int numberOfextensionPoints = int(extensionDistance / extensionPointIncrement);
409 Eigen::Vector3d extensionPointIncrementVector = extensionVectorNormalized * extensionDistance / numberOfextensionPoints;
411 for (
int i = 1; i<= numberOfextensionPoints; i++)
413 mergedPositions.insert(mergedPositions.begin(), mBloodVesselRoutePositions.front() + extensionPointIncrementVector*i);
416 mMergedAirwayAndBloodVesselRoutePositions = mergedPositions;
423 return addVTKPoints(mMergedAirwayAndBloodVesselRoutePositions);
427 bool RouteToTarget::checkIfRouteToTargetEndsAtEndOfLastBranch()
429 if (!mProjectedBranchPtr)
432 if (!mProjectedIndex)
435 if (mProjectedBranchPtr->getChildBranches().empty())
436 if (mProjectedBranchPtr->getPositions().cols()-1 == mProjectedIndex)
448 int numberOfPositions = positions.size();
449 for (
int j = numberOfPositions - 1; j >= 0; j--)
451 points->InsertNextPoint(positions[j](0),positions[j](1),positions[j](2));
453 for (
int j = 0; j < numberOfPositions-1; j++)
455 vtkIdType connection[2] = {j, j+1};
456 lines->InsertNextCell(2, connection);
458 retval->SetPoints(points);
459 retval->SetLines(lines);
468 QString RTTpath = services->patient()->getActivePatientFolder() +
"/RouteToTargetInformation/";
469 QDir RTTDirectory(RTTpath);
470 if (!RTTDirectory.exists())
471 RTTDirectory.mkpath(RTTpath);
474 QString filePath = RTTpath + QDateTime::currentDateTime().toString(format) +
"_RouteToTargetInformation.txt";
476 QFile outfile(filePath);
477 if (outfile.open(QIODevice::ReadWrite))
479 QTextStream stream(&outfile);
481 stream <<
"#Target position:" << endl;
482 stream << mTargetPosition(0) <<
" " << mTargetPosition(1) <<
" " << mTargetPosition(2) <<
" " << endl;
483 if (mProjectedBranchPtr)
485 stream <<
"#Route to target generations:" << endl;
486 stream << mProjectedBranchPtr->findGenerationNumber() << endl;
489 stream <<
"#Trachea length (mm):" << endl;
491 stream << tracheaLength << endl;
493 stream <<
"#Route to target length - from Carina (mm):" << endl;
495 stream <<
"#Extended route to target length - from Carina (mm):" << endl;
502 BranchPtr trachea = mBranchListPtr->getBranches()[0];
503 int numberOfPositionsInTrachea = trachea->getPositions().cols();
504 double tracheaLength =
calculateRouteLength(
smoothBranch(trachea, numberOfPositionsInTrachea-1, trachea->getPositions().col(numberOfPositionsInTrachea-1)));
505 return tracheaLength;
510 vtkPolyDataPtr centerline_r = route->getTransformedPolyDataCopy(route->get_rMd());
512 std::vector< Eigen::Vector3d > routePositions;
515 int N = centerline_r->GetNumberOfPoints();
516 for(vtkIdType i = 0; i < N; i++)
519 centerline_r->GetPoint(i,p);
520 Eigen::Vector3d position;
521 position(0) = p[0]; position(1) = p[1]; position(2) = p[2];
522 routePositions.push_back(position);
524 return routePositions;
529 double routeLenght = 0;
530 for (
int i=0; i<route.size()-1; i++)
532 double d0 = route[i+1](0) - route[i](0);
533 double d1 = route[i+1](1) - route[i](1);
534 double d2 = route[i+1](2) - route[i](2);
536 routeLenght += sqrt( d0*d0 +d1*d1 + d2*d2 );
544 std::vector<BranchPtr> branches = mBloodVesselBranchListPtr->getBranches();
546 for (
int i = 0; i < branches.size(); i++)
548 Eigen::MatrixXd positions = branches[i]->getPositions();
549 Eigen::MatrixXd orientations = branches[i]->getOrientations();
550 Eigen::VectorXd radius(positions.cols());
552 for (
int j = 0; j < positions.cols(); j++)
557 branches[i]->setRadius(radius);
565 if (!mBloodVesselVolume)
568 vtkImageDataPtr bloodVesselImage = mBloodVesselVolume->getBaseVtkImageData();
569 int* dim = bloodVesselImage->GetDimensions();
570 double* spacing = bloodVesselImage->GetSpacing();
571 Transform3D dMr = mBloodVesselVolume->get_rMd().inverse();
572 Eigen::Vector3d position_r = dMr.coord(position);
576 Eigen::Vector3i indexVector;
581 Eigen::MatrixXd maxRadius(3,2);
582 Eigen::Vector3d perpendicularX = orientation.cross(Eigen::Vector3d::UnitX());
585 Eigen::Vector3d perpendicularY = orientation.cross(Eigen::Vector3d::UnitY());
588 Eigen::Vector3d perpendicularZ = orientation.cross(Eigen::Vector3d::UnitZ());
592 radius = maxRadius.rowwise().mean().minCoeff();
594 if (std::isnan(radius))
603 double maxValue = bloodVesselImage->GetScalarRange()[1];
604 for (
int radiusVoxels=1; radiusVoxels<30; radiusVoxels++)
606 if (perpendicularVector.sum() != 0)
608 Eigen::Vector3d searchDirection = perpendicularVector.normalized() * radiusVoxels;
609 int xIndex = std::max(std::min(indexVector(0) + direction * (
int)
std::round(searchDirection(0)), dim[0]-1), 0);
610 int yIndex = std::max(std::min(indexVector(1) + direction * (
int)
std::round(searchDirection(1)), dim[1]-1), 0);
611 int zIndex = std::max(std::min(indexVector(2) + direction * (
int)
std::round(searchDirection(2)), dim[2]-1), 0);
612 if (bloodVesselImage->GetScalarComponentAsDouble(xIndex, yIndex, zIndex, 0) < maxValue)
614 searchDirection = perpendicularVector.normalized() * (radiusVoxels-1);
615 retval = std::sqrt( std::pow(searchDirection(0)*spacing[0],2) + std::pow(searchDirection(1)*spacing[1],2) + std::pow(searchDirection(2)*spacing[2],2) );
626 std::vector< Eigen::Vector3d > positions;
628 positions = mExtendedRoutePositions;
630 positions = mRoutePositions;
632 std::reverse(positions.begin(), positions.end());
638 std::vector< BranchPtr > routePositionsBranch = mRoutePositionsBranch;
639 std::reverse(routePositionsBranch.begin(), routePositionsBranch.end());
640 return routePositionsBranch;
645 std::vector< double > rotations = mExtendedCameraRotation;
646 std::reverse(rotations.begin(), rotations.end());
652 return mBranchingIndex;
657 if (mExtendedRoutePositions.empty())
659 std::cout <<
"mExtendedRoutePositions is empty." << std::endl;
663 int numberOfExtendedPositions = mExtendedRoutePositions.size() - mRoutePositions.size();
665 ofstream out(filename.toStdString().c_str());
666 out <<
"# [xPos yPos zPos BranchingPoint (0=Normal, 1=Branching position, 2=Extended from airway to target)] ";
667 out <<
"Number of positions: ";
668 out << mExtendedRoutePositions.size();
671 for (
int i = 1; i < mExtendedRoutePositions.size(); i++)
673 out << mExtendedRoutePositions[i](0) <<
" ";
674 out << mExtendedRoutePositions[i](1) <<
" ";
675 out << mExtendedRoutePositions[i](2) <<
" ";
677 if ( std::find(mBranchingIndex.begin(), mBranchingIndex.end(), i - numberOfExtendedPositions) != mBranchingIndex.end() )
679 else if (i <= numberOfExtendedPositions)
693 if ( mRoutePositions.empty() || mExtendedRoutePositions.empty() )
695 std::cout <<
"mRoutePositions is empty." << std::endl;
699 int numberOfExtendedPositions = mExtendedRoutePositions.size() - mRoutePositions.size();
701 for (
int i = 1; i < mExtendedRoutePositions.size(); i++)
703 QJsonObject position;
704 position.insert(
"x", mExtendedRoutePositions[i](0) );
705 position.insert(
"y", mExtendedRoutePositions[i](1) );
706 position.insert(
"z", mExtendedRoutePositions[i](2) );
708 if ( std::find(mBranchingIndex.begin(), mBranchingIndex.end(), i - numberOfExtendedPositions) != mBranchingIndex.end() )
709 position.insert(
"Flag", 1);
710 else if (i <= numberOfExtendedPositions)
711 position.insert(
"Flag", 2);
713 position.insert(
"Flag", 0);
715 array.append(position);
725 std::vector<BranchPtr> branches = branchList->getBranches();
726 for (
int i = 0; i < branches.size(); i++)
728 Eigen::MatrixXd positions = branches[i]->getPositions();
729 for (
int j = 0; j < positions.cols(); j++)
731 QJsonObject JsonPosition;
732 JsonPosition.insert(
"x", positions(0,j) );
733 JsonPosition.insert(
"y", positions(1,j) );
734 JsonPosition.insert(
"z", positions(2,j) );
735 array.append(JsonPosition);
748 Eigen::MatrixXd branchPositions = branchPtr->getPositions();
749 std::vector< Eigen::Vector3d > positions;
751 for (
int i = startIndex; i >=0; i--)
752 positions.push_back(branchPositions.col(i));
761 double maxDistanceToAirway = 10;
762 int minNumberOfPositionsInSegment = 100;
764 Eigen::MatrixXd bloodVesselSegment;
766 while (bloodVesselPositions.cols() > minNumberOfPositionsInSegment)
768 Eigen::MatrixXd::Index closestBloodVesselPositionToTarget =
dsearch(targetPosition, bloodVesselPositions).first;
769 std::pair< Eigen::MatrixXd, Eigen::MatrixXd > localPositions =
findLocalPointsInCT(closestBloodVesselPositionToTarget , bloodVesselPositions);
770 bloodVesselPositions = localPositions.second;
772 if ( localPositions.first.cols() >= minNumberOfPositionsInSegment &&
773 dsearchn(airwayPositions, localPositions.first).second.minCoeff() <= maxDistanceToAirway )
775 bloodVesselSegment = localPositions.first;
780 return bloodVesselSegment;
783 std::pair< Eigen::MatrixXd, Eigen::MatrixXd >
findLocalPointsInCT(
int closestCLIndex , Eigen::MatrixXd CLpositions)
785 Eigen::MatrixXd includedPositions;
786 Eigen::MatrixXd positionsNotIncluded = CLpositions;
787 int startIndex = closestCLIndex;
789 bool closePositionFound =
true;
790 while (closePositionFound)
792 closePositionFound =
false;
793 std::pair<Eigen::MatrixXd,Eigen::MatrixXd> connectedPoints =
findConnectedPointsInCT(startIndex , positionsNotIncluded);
794 positionsNotIncluded = connectedPoints.second;
796 if (includedPositions.cols() > 0)
798 includedPositions.conservativeResize(Eigen::NoChange, includedPositions.cols() + connectedPoints.first.cols());
799 includedPositions.rightCols(connectedPoints.first.cols()) = connectedPoints.first;
802 includedPositions = connectedPoints.first;
804 for (
int i = 0; i < includedPositions.cols(); i++)
806 std::pair<Eigen::MatrixXd::Index, double> closePositionSearch =
dsearch(includedPositions.col(i), positionsNotIncluded);
807 if (closePositionSearch.second < 3)
809 closePositionFound =
true;
810 startIndex = closePositionSearch.first;
816 return std::make_pair(includedPositions, positionsNotIncluded);
823 for (
int i=1; i<line.size(); i++)
830 return std::make_pair(index , minDistance);
833 double findDistance(Eigen::MatrixXd p1, Eigen::MatrixXd p2)
835 double d0 = p1(0) - p2(0);
836 double d1 = p1(1) - p2(1);
837 double d2 = p1(2) - p2(2);
839 double D = sqrt( d0*d0 + d1*d1 + d2*d2 );
846 Eigen::MatrixXd positionsMatrix(3, positionsVector.size());
847 for (
int i = 0; i < positionsVector.size(); i++)
849 positionsMatrix(0, i) = positionsVector[i](0);
850 positionsMatrix(1, i) = positionsVector[i](1);
851 positionsMatrix(2, i) = positionsVector[i](2);
853 return positionsMatrix;
858 double mean_X = X.mean();
860 for (
int i = 0; i < X.size(); i++)
861 var += ( X[i]-mean_X ) * ( X[i]-mean_X );