14 #include <boost/math/special_functions/fpclassify.hpp>
16 #include "vtkClipPolyData.h"
17 #include "vtkPlanes.h"
19 #include "vtkPoints.h"
20 #include "vtkPolyData.h"
21 #include "vtkCellArray.h"
22 #include "vtkCellLocator.h"
23 #include "vtkMINCImageReader.h"
24 #include "vtkTransform.h"
25 #include "vtkImageData.h"
26 #include "vtkGeneralTransform.h"
28 #include "vtkSortDataArray.h"
29 #include "vtkMaskPoints.h"
30 #include "vtkPointData.h"
31 #include "vtkLandmarkTransform.h"
32 #include "vtkFloatArray.h"
66 reporter()->sendDebug(
"SOURCE: " + source->getUid());
67 reporter()->sendDebug(
"TARGET: " + target->getUid());
70 std::cout <<
"sigma:" <<
mt_sigma << endl;
76 QTime start = QTime::currentTime();
87 report(QString(
"Auto LTS: Found best match using %1\%.").arg(context->mLtsRatio));
100 printOutResults(logPath +
"/Vessel_Based_Registration_", context->mConcatenation);
103 std::cout << QString(
"\n\nV2V Execution time: %1s").arg(start.secsTo(QTime::currentTime())) << endl;
118 std::vector<int> lts;
130 std::vector<ContextPtr> paths;
133 for (
unsigned i=0; i<lts.size(); ++i)
136 paths.push_back(current);
137 current->mLtsRatio = lts[i];
140 std::cout << QString(
"LTS=%1, metric=%2").arg(current->mLtsRatio).arg(current->mMetric) << std::endl;
145 for (
unsigned i=0; i<paths.size(); ++i)
147 if (paths[i]->mMetric < paths[bestPath]->mMetric)
152 return paths[bestPath];
161 double previousMetric = 1E6;
162 QDateTime t0 = QDateTime::currentDateTime();
166 double difference = context->mMetric - previousMetric;
172 std::cout << QString(
"iteration\t%1\trms:\t%2").arg(iteration).arg(context->mMetric) << std::endl;
179 previousMetric = context->mMetric;
190 retval->mLtsRatio = context->mLtsRatio;
191 retval->mInvertedTransform = context->mInvertedTransform;
194 retval->mTargetPointLocator = context->mTargetPointLocator;
195 retval->mTargetPoints = context->mTargetPoints;
198 retval->mSourcePoints = vtkPointsPtr::New();
199 retval->mSourcePoints->DeepCopy(context->mSourcePoints);
202 retval->mConcatenation = vtkGeneralTransformPtr::New();;
219 if (!inputSourcePolyData->GetNumberOfPoints() || !targetPolyData->GetNumberOfPoints())
221 std::cerr <<
"Can't execute with empty source or target data" << std::endl;
226 vtkPolyDataPtr sourcePolyData = this->
crop(inputSourcePolyData, targetPolyData, margin);
229 if (!sourcePolyData->GetNumberOfPoints() || !targetPolyData->GetNumberOfPoints())
231 std::cerr <<
"Can't execute with empty source or target data" << std::endl;
237 context->mConcatenation = vtkGeneralTransformPtr::New();
238 context->mInvertedTransform =
false;
242 if (sourcePolyData->GetNumberOfPoints() > targetPolyData->GetNumberOfPoints())
246 std::cout <<
"inverted vessel reg" << std::endl;
247 context->mInvertedTransform =
true;
248 std::swap(sourcePolyData, targetPolyData);
251 vtkIdType numPoints = sourcePolyData->GetNumberOfPoints();
254 std::cout <<
"total number of source points:" << numPoints <<
", target points: " << targetPolyData->GetNumberOfPoints() << endl;
255 std::cout <<
"number of source points to be sampled:" << ((int) (numPoints *
mt_ltsRatio) / 100) <<
"\n" << endl;
260 context->mTargetPoints = targetPolyData;
261 context->mTargetPointLocator = vtkCellLocatorPtr::New();
262 context->mTargetPointLocator->SetDataSet(targetPolyData);
263 context->mTargetPointLocator->SetNumberOfCellsPerBucket(1);
264 context->mTargetPointLocator->BuildLocator();
267 context->mSourcePoints = vtkPointsPtr::New();
268 context->mSourcePoints->DeepCopy(sourcePolyData->GetPoints());
284 int numPoints = context->mSourcePoints->GetNumberOfPoints();
286 int nb_points = ((int) (numPoints * context->mLtsRatio) / 100);
293 closestPoint->SetNumberOfPoints(numPoints);
297 residuals->SetNumberOfValues(numPoints);
300 IdList->SetNumberOfIds(numPoints);
301 double total_distance = 0;
302 double distanceSquared = 0;
304 for (
int i = 0; i < numPoints; ++i)
310 context->mTargetPointLocator->FindClosestPoint(context->mSourcePoints->GetPoint(i), outPoint, cell_id, sub_id, distanceSquared);
311 closestPoint->SetPoint(i, outPoint);
312 if ((boost::math::isnan)(distanceSquared))
314 std::cout <<
"nan found during findClosestPoint!" << std::endl;
316 context->mMetric = 1E6;
320 residuals->InsertValue(i, distanceSquared);
321 IdList->InsertId(i, i);
322 total_distance += sqrt(distanceSquared);
326 context->mMetric = total_distance / numPoints;
329 sort->Sort(residuals, IdList);
330 context->mSortedSourcePoints = this->
createSortedPoints(IdList, context->mSourcePoints, nb_points);
331 context->mSortedTargetPoints = this->
createSortedPoints(IdList, closestPoint, nb_points);
348 if (!context->mSortedSourcePoints || !context->mSortedTargetPoints)
351 if (!context->mSortedSourcePoints || !context->mSortedTargetPoints)
356 context->mTransform =
linearRegistration(context->mSortedSourcePoints, context->mSortedTargetPoints);
360 context->mTransform =
nonLinearRegistration(context->mSortedSourcePoints, context->mSortedTargetPoints);
365 context->mSourcePoints = this->
transformPoints(context->mSourcePoints, context->mTransform);
372 context->mConcatenation->Concatenate(context->mTransform);
382 retval->SetNumberOfPoints(numPoints);
384 double temp_point[3];
386 for (
int i = 0; i < numPoints; ++i)
388 vtkIdType index = sortedIDList->GetId(i);
389 unsortedPoints->GetPoint(index, temp_point);
390 retval->SetPoint(i, temp_point);
403 int N = input->GetNumberOfPoints();
405 retval->SetNumberOfPoints(N);
409 for (
int i = 0; i < N; ++i)
411 transform->InternalTransformPoint(input->GetPoint(i), temp);
412 retval->SetPoint(i, temp);
425 lmt->SetSourceLandmarks(sortedSourcePoints);
426 lmt->SetTargetLandmarks(sortedTargetPoints);
427 lmt->SetModeToRigidBody();
442 mask1->SetInputData(tpsSourcePolyData);
446 mask2->SetInputData(tpsTargetPolyData);
452 tps->SetSourceLandmarks(mask1->GetOutput()->GetPoints());
453 tps->SetTargetLandmarks(mask2->GetOutput()->GetPoints());
456 QTime start = QTime::currentTime();
458 std::cout << QString(
"\nV2V NL time: %1s").arg(start.secsTo(QTime::currentTime())) << endl;
466 MeshPtr mesh = boost::dynamic_pointer_cast<
Mesh>(data);
476 return mesh->getTransformedPolyData(mesh->get_rMd());
496 if (mInvertedTransform)
502 return mTargetPoints;
509 int N = input->GetNumberOfPoints();
511 for (
int i=0; i<N ; ++i)
513 cellArray->InsertNextCell(1);
514 cellArray->InsertCellPoint(i);
518 retval->SetPoints(input);
519 retval->SetVerts(cellArray);
526 for (
int q = 0; q < points->GetNumberOfPoints(); ++q)
529 std::cout << q <<
"\t" << p[0] <<
" " << p[1] <<
" " << p[2] <<
" " << std::endl;
535 print(data->GetPoints());
545 fixed->GetPoints()->ComputeBounds();
546 double* targetBounds = fixed->GetPoints()->GetBounds();
547 targetBounds[0] -= margin;
548 targetBounds[1] += margin;
549 targetBounds[2] -= margin;
550 targetBounds[3] += margin;
551 targetBounds[4] -= margin;
552 targetBounds[5] += margin;
557 box->SetBounds(targetBounds);
561 clipper->SetInputData(input);
562 clipper->SetClipFunction(box);
563 clipper->SetInsideOut(
true);
566 int oldSource = input->GetPoints()->GetNumberOfPoints();
567 int clippedSource = clipper->GetOutput()->GetPoints()->GetNumberOfPoints();
569 if (clippedSource < oldSource)
571 double ratio = double(oldSource - clippedSource) / double(oldSource);
573 std::cout <<
"Removed " << ratio * 100 <<
"%" <<
" of the source data. Outside the target data bounds." << std::endl;
576 return clipper->GetOutput();
584 Transform3D retval = this->getLinearTransform(context->mConcatenation);
585 if (context->mInvertedTransform)
586 retval = retval.inv();
595 return context->mMetric;
602 return context->mLtsRatio;
613 l_tempMatrix->DeepCopy(((vtkLandmarkTransform*) myConcatenation->GetConcatenatedTransform(0))->GetMatrix());
615 l_resultMatrix->Identity();
616 for (
int i = 1; i < myConcatenation->GetNumberOfConcatenatedTransforms(); ++i)
618 vtkMatrix4x4::Multiply4x4(l_tempMatrix,
619 ((vtkLandmarkTransform*) myConcatenation->GetConcatenatedTransform(i))->GetMatrix(), l_resultMatrix);
620 l_tempMatrix->DeepCopy(l_resultMatrix);
634 l_tempMatrix->DeepCopy(((vtkLandmarkTransform*) myConcatenation->GetConcatenatedTransform(0))->GetMatrix());
636 l_resultMatrix->Identity();
637 for (
int i = 1; i < myConcatenation->GetNumberOfConcatenatedTransforms(); ++i)
639 vtkMatrix4x4::Multiply4x4(l_tempMatrix,
640 ((vtkLandmarkTransform*) myConcatenation->GetConcatenatedTransform(i))->GetMatrix(), l_resultMatrix);
641 l_tempMatrix->DeepCopy(l_resultMatrix);
645 std::cout <<
"Filenameprefix: " << fileNamePrefix << std::endl;
649 std::string nonLinearFile = fileNamePrefix.toStdString();
650 nonLinearFile +=
"--NonLinear";
651 nonLinearFile +=
".txt";
653 std::string linearFile = fileNamePrefix.toStdString();
654 linearFile +=
"--Linear";
655 linearFile +=
".txt";
658 std::cout <<
"Writing Results to " << nonLinearFile <<
" and " << linearFile << std::endl;
662 ofstream file_out(nonLinearFile.c_str());
668 file_out <<
"MNI Transform File\n" << std::endl;
671 file_out <<
"Transform_Type = Thin_Plate_Spline_Transform;" << std::endl;
672 file_out <<
"Invert_Flag = True;" << std::endl;
673 file_out <<
"Number_Dimensions = 3;" << std::endl;
676 const double*
const * theWarpMatrix = l_theWarpTransform->GetWarpMatrix();
678 file_out <<
"Points = " << std::endl;
679 for (
int i = 0; i < n; i++)
681 l_theWarpTransform->GetSourceLandmarks()->GetPoint(i, point);
682 file_out << point[0] <<
" " << point[1] <<
" " << point[2];
684 file_out <<
";" << std::endl;
686 file_out << std::endl;
689 file_out <<
"Displacements = " << std::endl;
690 for (
int i = 0; i < n + 4; i++)
692 file_out << theWarpMatrix[i][0] <<
" " << theWarpMatrix[i][1] <<
" " << theWarpMatrix[i][2];
694 file_out <<
";" << std::endl;
696 file_out << std::endl;
703 ofstream file_out2(linearFile.c_str());
706 file_out2 <<
"MNI Transform File\n" << std::endl;
709 file_out2 <<
"Transform_Type = Linear;" << std::endl;
710 file_out2 <<
"Linear_Transform = ";
711 file_out2 << std::endl;
713 for (
int i = 0; i < 3; i++)
715 for (
int j = 0; j < 4; j++)
718 file_out2 << l_resultMatrix->GetElement(i, j);
723 file_out2 <<
";" << std::endl;
725 file_out2 << std::endl;
781 double p_BoundingBox[6])
786 image->getBaseVtkImageData()->GetDimensions(l_dimensions);
787 Vector3D spacing(image->getBaseVtkImageData()->GetSpacing());
791 l_dataTransform->SetMatrix(image->get_rMd().getVtkMatrix());
793 int l_startPosX, l_startPosY, l_startPosZ;
794 int l_stopPosX, l_stopPosY, l_stopPosZ;
795 int i, j, k, ii, jj, kk, l_counts = 0;
797 bool l_isNeighborFound;
800 int l_dimX = l_dimensions[0];
801 int l_dimY = l_dimensions[1];
802 int l_dimZ = l_dimensions[2];
804 double l_tempPoint[3];
809 int l_kkjjOffset = 0;
810 int l_kkjjiiOffset = 0;
811 int l_offsetConstIJ = l_dimX * l_dimY;
813 vtkDataArrayPtr l_allTheData = image->getBaseVtkImageData()->GetPointData()->GetScalars();
819 for (k = 0; k < l_dimZ; ++k)
822 l_startPosZ = k - p_neighborhoodFilterThreshold;
826 l_startPosZ *= l_offsetConstIJ;
828 l_stopPosZ = k + p_neighborhoodFilterThreshold;
829 if (l_stopPosZ >= l_dimZ)
830 l_stopPosZ = (l_dimZ - 1) * l_offsetConstIJ;
832 l_stopPosZ *= l_offsetConstIJ;
834 l_offsetK = k * l_offsetConstIJ;
836 for (j = 0; j < l_dimY; ++j)
839 l_startPosY = j - p_neighborhoodFilterThreshold;
843 l_startPosY *= l_dimX;
845 l_stopPosY = j + p_neighborhoodFilterThreshold;
846 if (l_stopPosY >= l_dimY)
847 l_stopPosY = (l_dimY - 1) * l_dimX;
849 l_stopPosY *= l_dimX;
851 l_offsetJ = l_offsetK + (j * l_dimX);
853 for (i = 0; i < l_dimX; ++i)
856 l_startPosX = i - p_neighborhoodFilterThreshold;
860 l_stopPosX = i + p_neighborhoodFilterThreshold;
861 if (l_stopPosX >= l_dimX)
862 l_stopPosX = l_dimX - 1;
864 l_offsetI = l_offsetJ + i;
865 l_isNeighborFound = 0;
868 if (*(l_allTheData->GetTuple(l_offsetI)))
871 l_tempPoint[0] = spacing[0] * i;
872 l_tempPoint[1] = spacing[1] * j;
873 l_tempPoint[2] = spacing[2] * k;
874 l_dataTransform->TransformPoint(l_tempPoint, l_tempPoint);
878 if (!p_BoundingBox || (p_BoundingBox[0] < l_tempPoint[0] && p_BoundingBox[1] > l_tempPoint[0]
879 && p_BoundingBox[2] < l_tempPoint[1] && p_BoundingBox[3] > l_tempPoint[1] && p_BoundingBox[4]
880 < l_tempPoint[2] && p_BoundingBox[5] > l_tempPoint[2]))
884 for (kk = l_startPosZ; kk <= l_stopPosZ && !l_isNeighborFound; kk += l_offsetConstIJ)
886 for (jj = l_startPosY; jj <= l_stopPosY && !l_isNeighborFound; jj += l_dimX)
888 l_kkjjOffset = kk + jj;
890 for (ii = l_startPosX; ii <= l_stopPosX && !l_isNeighborFound; ++ii)
892 l_kkjjiiOffset = ii + l_kkjjOffset;
894 if (l_offsetI != l_kkjjiiOffset &&
895 *(l_allTheData->GetTuple(l_kkjjiiOffset)))
897 l_isNeighborFound = 1;
898 l_dataPoints->InsertNextPoint(l_tempPoint);
899 l_dataCellArray->InsertNextCell(1);
900 l_dataCellArray->InsertCellPoint(l_counts++);
911 p_thePolyData->SetPoints(l_dataPoints);
912 p_thePolyData->SetVerts(l_dataCellArray);
914 return p_thePolyData;
924 Vector3D t_delta = linearTransform.matrix().block<3, 1>(0, 3);
925 Eigen::AngleAxisd angleAxis = Eigen::AngleAxisd(linearTransform.matrix().block<3, 3>(0, 0));
926 double angle = angleAxis.angle();
928 QString qualityText = QString(
"|t_delta|=%1mm, angle=%2*")
929 .arg(t_delta.length(), 6,
'f', 2)
930 .arg(angle /
M_PI * 180.0, 6,
'f', 2);
932 if (t_delta.length() > 20 || fabs(angle) > 10 / 180.0 *
M_PI)
935 QString text = QString(
936 "The registration matrix' angle-axis representation shows a large shift. Retry registration.");
vtkSmartPointer< class vtkMatrix4x4 > vtkMatrix4x4Ptr
vtkPolyDataPtr getMovingPoints()
the moving data (one of target or source, depending on inversion)
vtkAbstractTransformPtr nonLinearRegistration(vtkPointsPtr sortedSourcePoints, vtkPointsPtr sortedTargetPoints)
DoubleBoundingBox3D transform(const Transform3D &m, const DoubleBoundingBox3D &bb)
void print(vtkPointsPtr points)
vtkSmartPointer< class vtkPlanes > vtkPlanesPtr
void performOneRegistration(ContextPtr context, bool linear)
Register the source points to the target point in a single ste.
double mt_distanceDeltaStopThreshold
vtkSmartPointer< class vtkLandmarkTransform > vtkLandmarkTransformPtr
int mt_maximumNumberOfIterations
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
vtkSmartPointer< class vtkCellArray > vtkCellArrayPtr
ContextPtr mLastRun
result from last run of execute()
boost::shared_ptr< class Image > ImagePtr
vtkSmartPointer< class vtkFloatArray > vtkFloatArrayPtr
bool mInvertedTransform
the calculated registration goes from target to source instead of source to target ...
double mt_maximumDurationSeconds
vtkSmartPointer< class vtkThinPlateSplineTransform > vtkThinPlateSplineTransformPtr
bool execute(DataPtr source, DataPtr target, QString logPath)
vtkPolyDataPtr mTargetPoints
input: target data
vtkSmartPointer< class vtkIdList > vtkIdListPtr
vtkSmartPointer< class vtkClipPolyData > vtkClipPolyDataPtr
vtkSmartPointer< class vtkTransform > vtkTransformPtr
int mt_singlePointThreshold
void printOutResults(QString fileNamePrefix, vtkGeneralTransformPtr myConcatenation)
vtkSmartPointer< class vtkPolyData > vtkPolyDataPtr
void computeDistances(ContextPtr context)
Compute distances between the two datasets.
boost::shared_ptr< class Data > DataPtr
vtkAbstractTransformPtr linearRegistration(vtkPointsPtr sortedSourcePoints, vtkPointsPtr sortedTargetPoints)
vtkSmartPointer< class vtkMaskPoints > vtkMaskPointsPtr
double getResultLtsRatio(ContextPtr context=ContextPtr())
void reportWarning(QString msg)
ContextPtr linearRefineAllLTS(ContextPtr context)
SeansVesselReg::ContextPtr splitContext(ContextPtr context)
double getResultMetric(ContextPtr context=ContextPtr())
vtkSmartPointer< class vtkSortDataArray > vtkSortDataArrayPtr
vtkSmartPointer< class vtkAbstractTransform > vtkAbstractTransformPtr
ContextPtr createContext(DataPtr source, DataPtr target)
vtkPointsPtr createSortedPoints(vtkIdListPtr sortedIDList, vtkPointsPtr unsortedPoints, int numPoints)
Representation of a floating-point bounding box in 3D. The data are stored as {xmin,xmax,ymin,ymax,zmin,zmax}, in order to simplify communication with vtk.
static vtkPolyDataPtr extractPolyData(ImagePtr image, int p_neighborhoodFilterThreshold, double p_BoundingBox[6])
void linearRefine(ContextPtr context)
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
vtkSmartPointer< class vtkDataArray > vtkDataArrayPtr
vtkSmartPointer< class vtkGeneralTransform > vtkGeneralTransformPtr
void checkQuality(Transform3D linearTransform)
boost::shared_ptr< Context > ContextPtr
vtkPointsPtr transformPoints(vtkPointsPtr input, vtkAbstractTransformPtr transform)
boost::shared_ptr< class Mesh > MeshPtr
static vtkPolyDataPtr convertToPolyData(vtkPointsPtr input)
vtkPolyDataPtr crop(vtkPolyDataPtr input, vtkPolyDataPtr fixed, double margin)
Transform3D getLinearResult(ContextPtr context=ContextPtr())
vtkPolyDataPtr getFixedPoints()
the fixed data (one of target or source, depending on inversion)
vtkSmartPointer< class vtkPoints > vtkPointsPtr
vtkPointsPtr mSourcePoints
input: current source data, modified according to last iteration