diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractsToVectorImageFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractsToVectorImageFilter.cpp index c09ae5e4b3..682002bd3e 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractsToVectorImageFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractsToVectorImageFilter.cpp @@ -1,832 +1,832 @@ #include "itkTractsToVectorImageFilter.h" // VTK #include #include #include // ITK #include #include // misc #define _USE_MATH_DEFINES #include #include namespace itk{ static bool CompareVectorLengths(const vnl_vector_fixed< double, 3 >& v1, const vnl_vector_fixed< double, 3 >& v2) { return (v1.magnitude()>v2.magnitude()); } template< class PixelType > TractsToVectorImageFilter< PixelType >::TractsToVectorImageFilter(): m_AngularThreshold(0.7), m_Epsilon(0.999), m_MaskImage(NULL), m_NormalizeVectors(false), m_UseWorkingCopy(true), m_UseTrilinearInterpolation(false), m_MaxNumDirections(3), m_Thres(0.5), m_NumDirectionsImage(NULL) { this->SetNumberOfRequiredOutputs(1); } template< class PixelType > TractsToVectorImageFilter< PixelType >::~TractsToVectorImageFilter() { } template< class PixelType > vnl_vector_fixed TractsToVectorImageFilter< PixelType >::GetVnlVector(double point[3]) { vnl_vector_fixed vnlVector; vnlVector[0] = point[0]; vnlVector[1] = point[1]; vnlVector[2] = point[2]; return vnlVector; } template< class PixelType > itk::Point TractsToVectorImageFilter< PixelType >::GetItkPoint(double point[3]) { itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; return itkPoint; } template< class PixelType > void TractsToVectorImageFilter< PixelType >::GenerateData() { mitk::Geometry3D::Pointer geometry = m_FiberBundle->GetGeometry(); // calculate new image parameters itk::Vector spacing; itk::Point origin; itk::Matrix direction; ImageRegion<3> imageRegion; if (!m_MaskImage.IsNull()) { spacing = m_MaskImage->GetSpacing(); imageRegion = m_MaskImage->GetLargestPossibleRegion(); origin = m_MaskImage->GetOrigin(); direction = m_MaskImage->GetDirection(); } else { spacing = geometry->GetSpacing(); origin = geometry->GetOrigin(); mitk::Geometry3D::BoundsArrayType bounds = geometry->GetBounds(); origin[0] += bounds.GetElement(0); origin[1] += bounds.GetElement(2); origin[2] += bounds.GetElement(4); for (int i=0; i<3; i++) for (int j=0; j<3; j++) direction[j][i] = geometry->GetMatrixColumn(i)[j]; imageRegion.SetSize(0, geometry->GetExtent(0)); imageRegion.SetSize(1, geometry->GetExtent(1)); imageRegion.SetSize(2, geometry->GetExtent(2)); m_MaskImage = ItkUcharImgType::New(); m_MaskImage->SetSpacing( spacing ); m_MaskImage->SetOrigin( origin ); m_MaskImage->SetDirection( direction ); m_MaskImage->SetRegions( imageRegion ); m_MaskImage->Allocate(); m_MaskImage->FillBuffer(1); } OutputImageType::RegionType::SizeType outImageSize = imageRegion.GetSize(); m_OutImageSpacing = m_MaskImage->GetSpacing(); m_ClusteredDirectionsContainer = ContainerType::New(); // initialize crossings image m_CrossingsImage = ItkUcharImgType::New(); m_CrossingsImage->SetSpacing( spacing ); m_CrossingsImage->SetOrigin( origin ); m_CrossingsImage->SetDirection( direction ); m_CrossingsImage->SetRegions( imageRegion ); m_CrossingsImage->Allocate(); m_CrossingsImage->FillBuffer(0); // initialize num directions image m_NumDirectionsImage = ItkUcharImgType::New(); m_NumDirectionsImage->SetSpacing( spacing ); m_NumDirectionsImage->SetOrigin( origin ); m_NumDirectionsImage->SetDirection( direction ); m_NumDirectionsImage->SetRegions( imageRegion ); m_NumDirectionsImage->Allocate(); m_NumDirectionsImage->FillBuffer(0); // resample fiber bundle float minSpacing = 1; if(m_OutImageSpacing[0]GetDeepCopy(); // resample fiber bundle for sufficient voxel coverage m_FiberBundle->ResampleFibers(minSpacing/3); // iterate over all fibers vtkSmartPointer fiberPolyData = m_FiberBundle->GetFiberPolyData(); vtkSmartPointer vLines = fiberPolyData->GetLines(); vLines->InitTraversal(); int numFibers = m_FiberBundle->GetNumFibers(); itk::TimeProbe clock; m_DirectionsContainer = ContainerType::New(); if (m_UseTrilinearInterpolation) MITK_INFO << "Generating directions from tractogram (trilinear interpolation)"; else MITK_INFO << "Generating directions from tractogram"; boost::progress_display disp(numFibers); for( int i=0; iGetNextCell ( numPoints, points ); if (numPoints<2) continue; itk::Index<3> index; index.Fill(0); itk::ContinuousIndex contIndex; vnl_vector_fixed dir, wDir; itk::Point worldPos; vnl_vector v; for( int j=0; jGetPoint(points[j]); worldPos = GetItkPoint(temp); v = GetVnlVector(temp); dir = GetVnlVector(fiberPolyData->GetPoint(points[j+1]))-v; dir.normalize(); m_MaskImage->TransformPhysicalPointToIndex(worldPos, index); m_MaskImage->TransformPhysicalPointToContinuousIndex(worldPos, contIndex); if (m_MaskImage->GetPixel(index)==0) continue; if (!m_UseTrilinearInterpolation) { if (index[0] < 0 || (unsigned long)index[0] >= outImageSize[0]) continue; if (index[1] < 0 || (unsigned long)index[1] >= outImageSize[1]) continue; if (index[2] < 0 || (unsigned long)index[2] >= outImageSize[2]) continue; int idx = index[0] + outImageSize[0]*(index[1] + outImageSize[1]*index[2]); DirectionContainerType::Pointer dirCont = DirectionContainerType::New(); if (m_DirectionsContainer->IndexExists(idx)) { dirCont = m_DirectionsContainer->GetElement(idx); if (dirCont.IsNull()) { dirCont = DirectionContainerType::New(); dirCont->InsertElement(0, dir); m_DirectionsContainer->InsertElement(idx, dirCont); } else dirCont->InsertElement(dirCont->Size(), dir); } else { dirCont->InsertElement(0, dir); m_DirectionsContainer->InsertElement(idx, dirCont); } continue; } float frac_x = contIndex[0] - index[0]; float frac_y = contIndex[1] - index[1]; float frac_z = contIndex[2] - index[2]; if (frac_x<0) { index[0] -= 1; frac_x += 1; } if (frac_y<0) { index[1] -= 1; frac_y += 1; } if (frac_z<0) { index[2] -= 1; frac_z += 1; } frac_x = 1-frac_x; frac_y = 1-frac_y; frac_z = 1-frac_z; // int coordinates inside image? if (index[0] < 0 || (unsigned long)index[0] >= outImageSize[0]-1) continue; if (index[1] < 0 || (unsigned long)index[1] >= outImageSize[1]-1) continue; if (index[2] < 0 || (unsigned long)index[2] >= outImageSize[2]-1) continue; DirectionContainerType::Pointer dirCont; int idx; wDir = dir; float weight = ( frac_x)*( frac_y)*( frac_z); if (weight>m_Thres) { wDir *= weight; idx = index[0] + outImageSize[0]*(index[1] + outImageSize[1]*index[2] ); dirCont = DirectionContainerType::New(); if (m_DirectionsContainer->IndexExists(idx)) { dirCont = m_DirectionsContainer->GetElement(idx); if (dirCont.IsNull()) { dirCont = DirectionContainerType::New(); dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } else dirCont->InsertElement(dirCont->Size(), wDir); } else { dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } } wDir = dir; weight = ( frac_x)*(1-frac_y)*( frac_z); if (weight>m_Thres) { wDir *= weight; idx = index[0] + outImageSize[0]*(index[1]+1+ outImageSize[1]*index[2] ); dirCont = DirectionContainerType::New(); if (m_DirectionsContainer->IndexExists(idx)) { dirCont = m_DirectionsContainer->GetElement(idx); if (dirCont.IsNull()) { dirCont = DirectionContainerType::New(); dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } else dirCont->InsertElement(dirCont->Size(), wDir); } else { dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } } wDir = dir; weight = ( frac_x)*( frac_y)*(1-frac_z); if (weight>m_Thres) { wDir *= weight; idx = index[0] + outImageSize[0]*(index[1] + outImageSize[1]*index[2]+outImageSize[1]); dirCont = DirectionContainerType::New(); if (m_DirectionsContainer->IndexExists(idx)) { dirCont = m_DirectionsContainer->GetElement(idx); if (dirCont.IsNull()) { dirCont = DirectionContainerType::New(); dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } else dirCont->InsertElement(dirCont->Size(), wDir); } else { dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } } wDir = dir; weight = ( frac_x)*(1-frac_y)*(1-frac_z); if (weight>m_Thres) { wDir *= weight; idx = index[0] + outImageSize[0]*(index[1]+1+ outImageSize[1]*index[2]+outImageSize[1]); dirCont = DirectionContainerType::New(); if (m_DirectionsContainer->IndexExists(idx)) { dirCont = m_DirectionsContainer->GetElement(idx); if (dirCont.IsNull()) { dirCont = DirectionContainerType::New(); dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } else dirCont->InsertElement(dirCont->Size(), wDir); } else { dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } } wDir = dir; weight = (1-frac_x)*( frac_y)*( frac_z); if (weight>m_Thres) { wDir *= weight; idx = index[0]+1 + outImageSize[0]*(index[1] + outImageSize[1]*index[2] ); dirCont = DirectionContainerType::New(); if (m_DirectionsContainer->IndexExists(idx)) { dirCont = m_DirectionsContainer->GetElement(idx); if (dirCont.IsNull()) { dirCont = DirectionContainerType::New(); dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } else dirCont->InsertElement(dirCont->Size(), wDir); } else { dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } } wDir = dir; weight = (1-frac_x)*( frac_y)*(1-frac_z); if (weight>m_Thres) { wDir *= weight; idx = index[0]+1 + outImageSize[0]*(index[1] + outImageSize[1]*index[2]+outImageSize[1]); dirCont = DirectionContainerType::New(); if (m_DirectionsContainer->IndexExists(idx)) { dirCont = m_DirectionsContainer->GetElement(idx); if (dirCont.IsNull()) { dirCont = DirectionContainerType::New(); dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } else dirCont->InsertElement(dirCont->Size(), wDir); } else { dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } } wDir = dir; weight = (1-frac_x)*(1-frac_y)*( frac_z); if (weight>m_Thres) { wDir *= weight; idx = index[0]+1 + outImageSize[0]*(index[1]+1+ outImageSize[1]*index[2] ); dirCont = DirectionContainerType::New(); if (m_DirectionsContainer->IndexExists(idx)) { dirCont = m_DirectionsContainer->GetElement(idx); if (dirCont.IsNull()) { dirCont = DirectionContainerType::New(); dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } else dirCont->InsertElement(dirCont->Size(), wDir); } else { dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } } wDir = dir; weight = (1-frac_x)*(1-frac_y)*(1-frac_z); if (weight>m_Thres) { wDir *= weight; idx = index[0]+1 + outImageSize[0]*(index[1]+1+ outImageSize[1]*index[2]+outImageSize[1]); dirCont = DirectionContainerType::New(); if (m_DirectionsContainer->IndexExists(idx)) { dirCont = m_DirectionsContainer->GetElement(idx); if (dirCont.IsNull()) { dirCont = DirectionContainerType::New(); dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } else dirCont->InsertElement(dirCont->Size(), wDir); } else { dirCont->InsertElement(0, wDir); m_DirectionsContainer->InsertElement(idx, dirCont); } } } clock.Stop(); } vtkSmartPointer m_VtkCellArray = vtkSmartPointer::New(); vtkSmartPointer m_VtkPoints = vtkSmartPointer::New(); itk::ImageRegionIterator dirIt(m_NumDirectionsImage, m_NumDirectionsImage->GetLargestPossibleRegion()); itk::ImageRegionIterator crossIt(m_CrossingsImage, m_CrossingsImage->GetLargestPossibleRegion()); m_DirectionImageContainer = DirectionImageContainerType::New(); unsigned int maxNumDirections = 0; MITK_INFO << "Clustering directions"; boost::progress_display disp2(outImageSize[0]*outImageSize[1]*outImageSize[2]); for(crossIt.GoToBegin(); !crossIt.IsAtEnd(); ++crossIt) { ++disp2; OutputImageType::IndexType index = crossIt.GetIndex(); int idx = index[0]+(index[1]+index[2]*outImageSize[1])*outImageSize[0]; if (!m_DirectionsContainer->IndexExists(idx)) { ++dirIt; continue; } DirectionContainerType::Pointer dirCont = m_DirectionsContainer->GetElement(idx); if (dirCont.IsNull() || index[0] < 0 || (unsigned long)index[0] >= outImageSize[0] || index[1] < 0 || (unsigned long)index[1] >= outImageSize[1] || index[2] < 0 || (unsigned long)index[2] >= outImageSize[2]) { ++dirIt; continue; } std::vector< DirectionType > directions; for (int i=0; iSize(); i++) if (dirCont->ElementAt(i).magnitude()>0.0001) directions.push_back(dirCont->ElementAt(i)); if (!directions.empty()) directions = FastClustering(directions); std::sort( directions.begin(), directions.end(), CompareVectorLengths ); if ( directions.size() > maxNumDirections ) { - for (unsigned int i=maxNumDirections; i(directions.size(), m_MaxNumDirections); i++) { ItkDirectionImageType::Pointer directionImage = ItkDirectionImageType::New(); directionImage->SetSpacing( spacing ); directionImage->SetOrigin( origin ); directionImage->SetDirection( direction ); directionImage->SetRegions( imageRegion ); directionImage->Allocate(); Vector< float, 3 > nullVec; nullVec.Fill(0.0); directionImage->FillBuffer(nullVec); m_DirectionImageContainer->InsertElement(i, directionImage); } - maxNumDirections = std::min(directions.size(), m_MaxNumDirections); + maxNumDirections = std::min(directions.size(), m_MaxNumDirections); } unsigned int numDir = directions.size(); if (numDir>m_MaxNumDirections) numDir = m_MaxNumDirections; for (unsigned int i=0; i container = vtkSmartPointer::New(); itk::ContinuousIndex center; center[0] = index[0]; center[1] = index[1]; center[2] = index[2]; itk::Point worldCenter; m_MaskImage->TransformContinuousIndexToPhysicalPoint( center, worldCenter ); DirectionType dir = directions.at(i); // set direction image pixel ItkDirectionImageType::Pointer directionImage = m_DirectionImageContainer->GetElement(i); Vector< float, 3 > pixel; pixel.SetElement(0, dir[0]); pixel.SetElement(1, dir[1]); pixel.SetElement(2, dir[2]); directionImage->SetPixel(index, pixel); // add direction to vector field (with spacing compensation) itk::Point worldStart; worldStart[0] = worldCenter[0]-dir[0]/2*minSpacing; worldStart[1] = worldCenter[1]-dir[1]/2*minSpacing; worldStart[2] = worldCenter[2]-dir[2]/2*minSpacing; vtkIdType id = m_VtkPoints->InsertNextPoint(worldStart.GetDataPointer()); container->GetPointIds()->InsertNextId(id); itk::Point worldEnd; worldEnd[0] = worldCenter[0]+dir[0]/2*minSpacing; worldEnd[1] = worldCenter[1]+dir[1]/2*minSpacing; worldEnd[2] = worldCenter[2]+dir[2]/2*minSpacing; id = m_VtkPoints->InsertNextPoint(worldEnd.GetDataPointer()); container->GetPointIds()->InsertNextId(id); m_VtkCellArray->InsertNextCell(container); } dirIt.Set(numDir); ++dirIt; } vtkSmartPointer directionsPolyData = vtkSmartPointer::New(); directionsPolyData->SetPoints(m_VtkPoints); directionsPolyData->SetLines(m_VtkCellArray); m_OutputFiberBundle = mitk::FiberBundleX::New(directionsPolyData); } template< class PixelType > std::vector< vnl_vector_fixed< double, 3 > > TractsToVectorImageFilter< PixelType >::FastClustering(std::vector< vnl_vector_fixed< double, 3 > >& inDirs) { std::vector< vnl_vector_fixed< double, 3 > > outDirs; if (inDirs.empty()) return outDirs; vnl_vector_fixed< double, 3 > oldMean, currentMean, workingMean; std::vector< vnl_vector_fixed< double, 3 > > normalizedDirs; std::vector< int > touched; for (unsigned int i=0; i0.0001) { counter = 0; oldMean = currentMean; workingMean = oldMean; workingMean.normalize(); currentMean.fill(0.0); for (unsigned int i=0; i=m_AngularThreshold) { currentMean += inDirs[i]; touched[i] = 1; counter++; } else if (-angle>=m_AngularThreshold) { currentMean -= inDirs[i]; touched[i] = 1; counter++; } } } // found stable mean if (counter>0) { currentMean /= counter; float mag = currentMean.magnitude(); if (mag>0) { if (mag>max) max = mag; outDirs.push_back(currentMean); } } // find next unused seed free = false; for (unsigned int i=0; i0) for (unsigned int i=0; i std::vector< vnl_vector_fixed< double, 3 > > TractsToVectorImageFilter< PixelType >::Clustering(std::vector< vnl_vector_fixed< double, 3 > >& inDirs) { std::vector< vnl_vector_fixed< double, 3 > > outDirs; if (inDirs.empty()) return outDirs; vnl_vector_fixed< double, 3 > oldMean, currentMean, workingMean; std::vector< vnl_vector_fixed< double, 3 > > normalizedDirs; std::vector< int > touched; for (int i=0; i0.0001) { counter = 0; oldMean = currentMean; workingMean = oldMean; workingMean.normalize(); currentMean.fill(0.0); for (int i=0; i=m_AngularThreshold) { currentMean += inDirs[i]; counter++; } else if (-angle>=m_AngularThreshold) { currentMean -= inDirs[i]; counter++; } } } // found stable mean if (counter>0) { bool add = true; vnl_vector_fixed< double, 3 > normMean = currentMean; normMean.normalize(); for (int i=0; i dir = outDirs[i]; dir.normalize(); if ((normMean-dir).magnitude()<=0.0001) { add = false; break; } } currentMean /= counter; if (add) { float mag = currentMean.magnitude(); if (mag>0) { if (mag>max) max = mag; outDirs.push_back(currentMean); } } } } if (m_NormalizeVectors) for (int i=0; i0) for (int i=0; i TractsToVectorImageFilter< PixelType >::DirectionContainerType::Pointer TractsToVectorImageFilter< PixelType >::MeanShiftClustering(DirectionContainerType::Pointer dirCont) { DirectionContainerType::Pointer container = DirectionContainerType::New(); float max = 0; for (DirectionContainerType::ConstIterator it = dirCont->Begin(); it!=dirCont->End(); ++it) { vnl_vector_fixed mean = ClusterStep(dirCont, it.Value()); if (mean.is_zero()) continue; bool addMean = true; for (DirectionContainerType::ConstIterator it2 = container->Begin(); it2!=container->End(); ++it2) { vnl_vector_fixed dir = it2.Value(); float angle = fabs(dot_product(mean, dir)/(mean.magnitude()*dir.magnitude())); if (angle>=m_Epsilon) { addMean = false; break; } } if (addMean) { if (m_NormalizeVectors) mean.normalize(); else if (mean.magnitude()>max) max = mean.magnitude(); container->InsertElement(container->Size(), mean); } } // max normalize voxel directions if (max>0 && !m_NormalizeVectors) for (int i=0; iSize(); i++) container->ElementAt(i) /= max; if (container->Size()Size()) return MeanShiftClustering(container); else return container; } template< class PixelType > vnl_vector_fixed TractsToVectorImageFilter< PixelType >::ClusterStep(DirectionContainerType::Pointer dirCont, vnl_vector_fixed currentMean) { vnl_vector_fixed newMean; newMean.fill(0); for (DirectionContainerType::ConstIterator it = dirCont->Begin(); it!=dirCont->End(); ++it) { vnl_vector_fixed dir = it.Value(); float angle = dot_product(currentMean, dir)/(currentMean.magnitude()*dir.magnitude()); if (angle>=m_AngularThreshold) newMean += dir; else if (-angle>=m_AngularThreshold) newMean -= dir; } if (fabs(dot_product(currentMean, newMean)/(currentMean.magnitude()*newMean.magnitude()))>=m_Epsilon || newMean.is_zero()) return newMean; else return ClusterStep(dirCont, newMean); } }