diff --git a/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.cpp b/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.cpp index f681c8ba00..e6e4124b70 100644 --- a/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.cpp +++ b/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.cpp @@ -1,548 +1,652 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __itkStreamlineTrackingFilter_txx #define __itkStreamlineTrackingFilter_txx #include #include #include #include "itkStreamlineTrackingFilter.h" #include #include #include #define _USE_MATH_DEFINES #include namespace itk { //#define QBALL_RECON_PI M_PI template< class TTensorPixelType, class TPDPixelType> StreamlineTrackingFilter< TTensorPixelType, TPDPixelType> ::StreamlineTrackingFilter(): m_FaThreshold(0.2), m_StepSize(1), m_MaxLength(10000), m_SeedsPerVoxel(1), m_AngularThreshold(0.7), m_F(1.0), - m_G(0.0) + m_G(0.0), + m_Interpolate(true) { // At least 1 inputs is necessary for a vector image. // For images added one at a time we need at least six this->SetNumberOfRequiredInputs( 1 ); } template< class TTensorPixelType, class TPDPixelType> double StreamlineTrackingFilter< TTensorPixelType, TPDPixelType> ::RoundToNearest(double num) { return (num > 0.0) ? floor(num + 0.5) : ceil(num - 0.5); } template< class TTensorPixelType, class TPDPixelType> void StreamlineTrackingFilter< TTensorPixelType, TPDPixelType> ::BeforeThreadedGenerateData() { m_FiberPolyData = FiberPolyDataType::New(); m_Points = vtkPoints::New(); m_Cells = vtkCellArray::New(); m_InputImage = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); m_ImageSize.resize(3); m_ImageSize[0] = m_InputImage->GetLargestPossibleRegion().GetSize()[0]; m_ImageSize[1] = m_InputImage->GetLargestPossibleRegion().GetSize()[1]; m_ImageSize[2] = m_InputImage->GetLargestPossibleRegion().GetSize()[2]; + + if (m_ImageSize[0]<3 || m_ImageSize[1]<3 || m_ImageSize[2]<3) + m_Interpolate = false; + m_ImageSpacing.resize(3); m_ImageSpacing[0] = m_InputImage->GetSpacing()[0]; m_ImageSpacing[1] = m_InputImage->GetSpacing()[1]; m_ImageSpacing[2] = m_InputImage->GetSpacing()[2]; - if (m_StepSize<0.005) + float minSpacing; + if(m_ImageSpacing[0]::New(); for (int i=0; iGetNumberOfThreads(); i++) { FiberPolyDataType poly = FiberPolyDataType::New(); m_PolyDataContainer->InsertElement(i, poly); } if (m_SeedImage.IsNull()) { // initialize mask image m_SeedImage = ItkUcharImgType::New(); m_SeedImage->SetSpacing( m_InputImage->GetSpacing() ); m_SeedImage->SetOrigin( m_InputImage->GetOrigin() ); m_SeedImage->SetDirection( m_InputImage->GetDirection() ); m_SeedImage->SetRegions( m_InputImage->GetLargestPossibleRegion() ); m_SeedImage->Allocate(); m_SeedImage->FillBuffer(1); } if (m_MaskImage.IsNull()) { // initialize mask image m_MaskImage = ItkUcharImgType::New(); m_MaskImage->SetSpacing( m_InputImage->GetSpacing() ); m_MaskImage->SetOrigin( m_InputImage->GetOrigin() ); m_MaskImage->SetDirection( m_InputImage->GetDirection() ); m_MaskImage->SetRegions( m_InputImage->GetLargestPossibleRegion() ); m_MaskImage->Allocate(); m_MaskImage->FillBuffer(1); } m_FaImage = ItkFloatImgType::New(); m_FaImage->SetSpacing( m_InputImage->GetSpacing() ); m_FaImage->SetOrigin( m_InputImage->GetOrigin() ); m_FaImage->SetDirection( m_InputImage->GetDirection() ); m_FaImage->SetRegions( m_InputImage->GetLargestPossibleRegion() ); m_FaImage->Allocate(); m_FaImage->FillBuffer(0.0); m_PdImage = ItkPDImgType::New(); m_PdImage->SetSpacing( m_InputImage->GetSpacing() ); m_PdImage->SetOrigin( m_InputImage->GetOrigin() ); m_PdImage->SetDirection( m_InputImage->GetDirection() ); m_PdImage->SetRegions( m_InputImage->GetLargestPossibleRegion() ); m_PdImage->Allocate(); m_EmaxImage = ItkFloatImgType::New(); m_EmaxImage->SetSpacing( m_InputImage->GetSpacing() ); m_EmaxImage->SetOrigin( m_InputImage->GetOrigin() ); m_EmaxImage->SetDirection( m_InputImage->GetDirection() ); m_EmaxImage->SetRegions( m_InputImage->GetLargestPossibleRegion() ); m_EmaxImage->Allocate(); m_EmaxImage->FillBuffer(1.0); typedef itk::DiffusionTensor3D TensorType; typename TensorType::EigenValuesArrayType eigenvalues; typename TensorType::EigenVectorsMatrixType eigenvectors; for (int x=0; xGetPixel(index); vnl_vector_fixed dir; tensor.ComputeEigenAnalysis(eigenvalues, eigenvectors); dir[0] = eigenvectors(2, 0); dir[1] = eigenvectors(2, 1); dir[2] = eigenvectors(2, 2); dir.normalize(); m_PdImage->SetPixel(index, dir); m_FaImage->SetPixel(index, tensor.GetFractionalAnisotropy()); m_EmaxImage->SetPixel(index, 2/eigenvalues[2]); } std::cout << "StreamlineTrackingFilter: Angular threshold: " << m_AngularThreshold << std::endl; std::cout << "StreamlineTrackingFilter: FA threshold: " << m_FaThreshold << std::endl; std::cout << "StreamlineTrackingFilter: stepsize: " << m_StepSize << " mm" << std::endl; std::cout << "StreamlineTrackingFilter: f: " << m_F << std::endl; std::cout << "StreamlineTrackingFilter: g: " << m_G << std::endl; std::cout << "StreamlineTrackingFilter: starting streamline tracking" << std::endl; } template< class TTensorPixelType, class TPDPixelType> void StreamlineTrackingFilter< TTensorPixelType, TPDPixelType> ::CalculateNewPosition(itk::ContinuousIndex& pos, vnl_vector_fixed& dir, typename InputImageType::IndexType& index) { if (true) { dir *= m_StepSize; pos[0] += dir[0]/m_ImageSpacing[0]; pos[1] += dir[1]/m_ImageSpacing[1]; pos[2] += dir[2]/m_ImageSpacing[2]; index[0] = RoundToNearest(pos[0]); index[1] = RoundToNearest(pos[1]); index[2] = RoundToNearest(pos[2]); } else { dir[0] /= m_ImageSpacing[0]; dir[1] /= m_ImageSpacing[1]; dir[2] /= m_ImageSpacing[2]; int smallest = 0; float x = 100000; if (dir[0]>0) { if (fabs(fabs(RoundToNearest(pos[0])-pos[0])-0.5)>mitk::eps) x = fabs(pos[0]-RoundToNearest(pos[0])-0.5)/dir[0]; else x = fabs(pos[0]-std::ceil(pos[0])-0.5)/dir[0]; } else if (dir[0]<0) { if (fabs(fabs(RoundToNearest(pos[0])-pos[0])-0.5)>mitk::eps) x = -fabs(pos[0]-RoundToNearest(pos[0])+0.5)/dir[0]; else x = -fabs(pos[0]-std::floor(pos[0])+0.5)/dir[0]; } float s = x; float y = 100000; if (dir[1]>0) { if (fabs(fabs(RoundToNearest(pos[1])-pos[1])-0.5)>mitk::eps) y = fabs(pos[1]-RoundToNearest(pos[1])-0.5)/dir[1]; else y = fabs(pos[1]-std::ceil(pos[1])-0.5)/dir[1]; } else if (dir[1]<0) { if (fabs(fabs(RoundToNearest(pos[1])-pos[1])-0.5)>mitk::eps) y = -fabs(pos[1]-RoundToNearest(pos[1])+0.5)/dir[1]; else y = -fabs(pos[1]-std::floor(pos[1])+0.5)/dir[1]; } if (s>y) { s=y; smallest = 1; } float z = 100000; if (dir[2]>0) { if (fabs(fabs(RoundToNearest(pos[2])-pos[2])-0.5)>mitk::eps) z = fabs(pos[2]-RoundToNearest(pos[2])-0.5)/dir[2]; else z = fabs(pos[2]-std::ceil(pos[2])-0.5)/dir[2]; } else if (dir[2]<0) { if (fabs(fabs(RoundToNearest(pos[2])-pos[2])-0.5)>mitk::eps) z = -fabs(pos[2]-RoundToNearest(pos[2])+0.5)/dir[2]; else z = -fabs(pos[2]-std::floor(pos[2])+0.5)/dir[2]; } if (s>z) { s=z; smallest = 2; } // MITK_INFO << "---------------------------------------------"; // MITK_INFO << "s: " << s; // MITK_INFO << "dir: " << dir; // MITK_INFO << "old: " << pos[0] << ", " << pos[1] << ", " << pos[2]; pos[0] += dir[0]*s; pos[1] += dir[1]*s; pos[2] += dir[2]*s; switch (smallest) { case 0: if (dir[0]<0) index[0] = std::floor(pos[0]); else index[0] = std::ceil(pos[0]); index[1] = RoundToNearest(pos[1]); index[2] = RoundToNearest(pos[2]); break; case 1: if (dir[1]<0) index[1] = std::floor(pos[1]); else index[1] = std::ceil(pos[1]); index[0] = RoundToNearest(pos[0]); index[2] = RoundToNearest(pos[2]); break; case 2: if (dir[2]<0) index[2] = std::floor(pos[2]); else index[2] = std::ceil(pos[2]); index[1] = RoundToNearest(pos[1]); index[0] = RoundToNearest(pos[0]); } // float x = 100000; // if (dir[0]>0) // x = fabs(pos[0]-RoundToNearest(pos[0])-0.5)/dir[0]; // else if (dir[0]<0) // x = -fabs(pos[0]-RoundToNearest(pos[0])+0.5)/dir[0]; // float s = x; // float y = 100000; // if (dir[1]>0) // y = fabs(pos[1]-RoundToNearest(pos[1])-0.5)/dir[1]; // else if (dir[1]<0) // y = -fabs(pos[1]-RoundToNearest(pos[1])+0.5)/dir[1]; // if (s>y) // s=y; // float z = 100000; // if (dir[2]>0) // z = fabs(pos[2]-RoundToNearest(pos[2])-0.5)/dir[2]; // else if (dir[2]<0) // z = -fabs(pos[2]-RoundToNearest(pos[2])+0.5)/dir[2]; // if (s>z) // s=z; // s *= 1.001; // pos[0] += dir[0]*s; // pos[1] += dir[1]*s; // pos[2] += dir[2]*s; // index[0] = RoundToNearest(pos[0]); // index[1] = RoundToNearest(pos[1]); // index[2] = RoundToNearest(pos[2]); // MITK_INFO << "new: " << pos[0] << ", " << pos[1] << ", " << pos[2]; } } template< class TTensorPixelType, class TPDPixelType> void StreamlineTrackingFilter< TTensorPixelType, TPDPixelType> ::FollowStreamline(itk::ContinuousIndex pos, int dirSign, vtkPoints* points, std::vector< vtkIdType >& ids) { + typedef itk::DiffusionTensor3D TensorType; + typename TensorType::EigenValuesArrayType eigenvalues; + typename TensorType::EigenVectorsMatrixType eigenvectors; + typename InputImageType::IndexType index, indexOld; indexOld[0] = -1; indexOld[1] = -1; indexOld[2] = -1; itk::Point worldPos; + float distance = 0; // starting index and direction index[0] = RoundToNearest(pos[0]); index[1] = RoundToNearest(pos[1]); index[2] = RoundToNearest(pos[2]); vnl_vector_fixed dir = m_PdImage->GetPixel(index); dir *= dirSign; // reverse direction vnl_vector_fixed dirOld = dir; + if (dir.magnitude()GetLargestPossibleRegion().IsInside(index) || m_FaImage->GetPixel(index)GetPixel(index)==0) { m_InputImage->TransformContinuousIndexToPhysicalPoint( pos, worldPos ); ids.push_back(points->InsertNextPoint(worldPos.GetDataPointer())); break; } - if (indexOld!=index) // did we enter a new voxel? if yes, calculate new direction + if (!m_Interpolate) { - dir = m_PdImage->GetPixel(index); // get principal direction - dir *= dirSign; // reverse direction - - if (!dirOld.is_zero()) + if (indexOld!=index) // did we enter a new voxel? if yes, calculate new direction { + dir = m_PdImage->GetPixel(index); // get principal direction + typename InputImageType::PixelType tensor = m_InputImage->GetPixel(index); float scale = m_EmaxImage->GetPixel(index); dir[0] = m_F*dir[0] + (1-m_F)*( (1-m_G)*dirOld[0] + scale*m_G*(tensor[0]*dirOld[0] + tensor[1]*dirOld[1] + tensor[2]*dirOld[2])); dir[1] = m_F*dir[1] + (1-m_F)*( (1-m_G)*dirOld[1] + scale*m_G*(tensor[1]*dirOld[0] + tensor[3]*dirOld[1] + tensor[4]*dirOld[2])); dir[2] = m_F*dir[2] + (1-m_F)*( (1-m_G)*dirOld[2] + scale*m_G*(tensor[2]*dirOld[0] + tensor[4]*dirOld[1] + tensor[5]*dirOld[2])); dir.normalize(); float angle = dot_product(dirOld, dir); if (angle<0) dir *= -1; angle = dot_product(dirOld, dir); if (angleTransformContinuousIndexToPhysicalPoint( pos, worldPos ); + ids.push_back(points->InsertNextPoint(worldPos.GetDataPointer())); + } + else + dir = dirOld; + } + else + { + float frac_x = pos[0] - index[0]; + float frac_y = pos[1] - index[1]; + float frac_z = pos[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; } - dirOld = dir; + + frac_x = 1-frac_x; + frac_y = 1-frac_y; + frac_z = 1-frac_z; + + // int coordinates inside image? + if (index[0] < 0 || index[0] >= m_ImageSize[0]-1) + continue; + if (index[1] < 0 || index[1] >= m_ImageSize[1]-1) + continue; + if (index[2] < 0 || index[2] >= m_ImageSize[2]-1) + continue; + + typename InputImageType::IndexType tmpIdx; + typename InputImageType::PixelType tensor = m_InputImage->GetPixel(index); + tensor *= ( frac_x)*( frac_y)*( frac_z); + + tmpIdx = index; tmpIdx[0]++; + tensor += m_InputImage->GetPixel(tmpIdx) * (1-frac_x)*( frac_y)*( frac_z); + tmpIdx = index; tmpIdx[1]++; + tensor += m_InputImage->GetPixel(tmpIdx) * ( frac_x)*(1-frac_y)*( frac_z); + tmpIdx = index; tmpIdx[2]++; + tensor += m_InputImage->GetPixel(tmpIdx) * ( frac_x)*( frac_y)*(1-frac_z); + tmpIdx = index; tmpIdx[0]++; tmpIdx[1]++; + tensor += m_InputImage->GetPixel(tmpIdx) * (1-frac_x)*(1-frac_y)*( frac_z); + tmpIdx = index; tmpIdx[1]++; tmpIdx[2]++; + tensor += m_InputImage->GetPixel(tmpIdx) * ( frac_x)*(1-frac_y)*(1-frac_z); + tmpIdx = index; tmpIdx[2]++; tmpIdx[0]++; + tensor += m_InputImage->GetPixel(tmpIdx) * (1-frac_x)*( frac_y)*(1-frac_z); + tmpIdx = index; tmpIdx[0]++; tmpIdx[1]++; tmpIdx[2]++; + tensor += m_InputImage->GetPixel(tmpIdx) * (1-frac_x)*(1-frac_y)*(1-frac_z); + + tensor.ComputeEigenAnalysis(eigenvalues, eigenvectors); + dir[0] = eigenvectors(2, 0); + dir[1] = eigenvectors(2, 1); + dir[2] = eigenvectors(2, 2); + dir.normalize(); + + float scale = 2/eigenvalues[2]; + dir[0] = m_F*dir[0] + (1-m_F)*( (1-m_G)*dirOld[0] + scale*m_G*(tensor[0]*dirOld[0] + tensor[1]*dirOld[1] + tensor[2]*dirOld[2])); + dir[1] = m_F*dir[1] + (1-m_F)*( (1-m_G)*dirOld[1] + scale*m_G*(tensor[1]*dirOld[0] + tensor[3]*dirOld[1] + tensor[4]*dirOld[2])); + dir[2] = m_F*dir[2] + (1-m_F)*( (1-m_G)*dirOld[2] + scale*m_G*(tensor[2]*dirOld[0] + tensor[4]*dirOld[1] + tensor[5]*dirOld[2])); + dir.normalize(); + + float angle = dot_product(dirOld, dir); + if (angle<0) + dir *= -1; + angle = dot_product(dirOld, dir); + if (angleTransformContinuousIndexToPhysicalPoint( pos, worldPos ); - ids.push_back(points->InsertNextPoint(worldPos.GetDataPointer())); + if (distance>=m_PointPistance) + { + m_InputImage->TransformContinuousIndexToPhysicalPoint( pos, worldPos ); + ids.push_back(points->InsertNextPoint(worldPos.GetDataPointer())); + distance = 0; + } } - else // keep old direction - dir = dirOld; } } template< class TTensorPixelType, class TPDPixelType> void StreamlineTrackingFilter< TTensorPixelType, TPDPixelType> ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, int threadId) { FiberPolyDataType poly = m_PolyDataContainer->GetElement(threadId); vtkSmartPointer points = vtkPoints::New(); vtkSmartPointer Cells = vtkCellArray::New(); typedef itk::DiffusionTensor3D TensorType; typedef ImageRegionConstIterator< InputImageType > InputIteratorType; typedef ImageRegionConstIterator< ItkUcharImgType > MaskIteratorType; typedef ImageRegionConstIterator< ItkFloatImgType > FloatIteratorType; typedef typename InputImageType::PixelType InputTensorType; InputIteratorType it(m_InputImage, outputRegionForThread ); MaskIteratorType mit(m_SeedImage, outputRegionForThread ); FloatIteratorType fit(m_FaImage, outputRegionForThread ); MaskIteratorType mit2(m_MaskImage, outputRegionForThread ); it.GoToBegin(); mit.GoToBegin(); mit2.GoToBegin(); fit.GoToBegin(); itk::Point worldPos; while( !it.IsAtEnd() ) { if (mit.Value()==0 || fit.Value() line = vtkSmartPointer::New(); std::vector< vtkIdType > pointIDs; typename InputImageType::IndexType index = it.GetIndex(); itk::ContinuousIndex start; unsigned int counter = 0; if (m_SeedsPerVoxel>1) { start[0] = index[0]+(double)(rand()%99-49)/100; start[1] = index[1]+(double)(rand()%99-49)/100; start[2] = index[2]+(double)(rand()%99-49)/100; } else { start[0] = index[0]; start[1] = index[1]; start[2] = index[2]; } // forward tracking FollowStreamline(start, 1, points, pointIDs); // add ids to line counter += pointIDs.size(); while (!pointIDs.empty()) { line->GetPointIds()->InsertNextId(pointIDs.back()); pointIDs.pop_back(); } // insert start point m_InputImage->TransformContinuousIndexToPhysicalPoint( start, worldPos ); line->GetPointIds()->InsertNextId(points->InsertNextPoint(worldPos.GetDataPointer())); // backward tracking FollowStreamline(start, -1, points, pointIDs); // add ids to line counter += pointIDs.size(); for (int i=0; iGetPointIds()->InsertNextId(pointIDs.at(i)); if (counter>1) Cells->InsertNextCell(line); } ++mit; ++mit2; ++it; ++fit; } poly->SetPoints(points); poly->SetLines(Cells); std::cout << "Thread " << threadId << " finished tracking" << std::endl; } template< class TTensorPixelType, class TPDPixelType> vtkSmartPointer< vtkPolyData > StreamlineTrackingFilter< TTensorPixelType, TPDPixelType> ::AddPolyData(FiberPolyDataType poly1, FiberPolyDataType poly2) { vtkSmartPointer vNewPolyData = vtkSmartPointer::New(); vtkSmartPointer vNewLines = poly1->GetLines(); vtkSmartPointer vNewPoints = poly1->GetPoints(); vtkSmartPointer vLines = poly2->GetLines(); vLines->InitTraversal(); for( int i=0; iGetNumberOfCells(); i++ ) { vtkIdType numPoints(0); vtkIdType* points(NULL); vLines->GetNextCell ( numPoints, points ); vtkSmartPointer container = vtkSmartPointer::New(); for( int j=0; jInsertNextPoint(poly2->GetPoint(points[j])); container->GetPointIds()->InsertNextId(id); } vNewLines->InsertNextCell(container); } // initialize polydata vNewPolyData->SetPoints(vNewPoints); vNewPolyData->SetLines(vNewLines); return vNewPolyData; } template< class TTensorPixelType, class TPDPixelType> void StreamlineTrackingFilter< TTensorPixelType, TPDPixelType> ::AfterThreadedGenerateData() { MITK_INFO << "Generating polydata "; m_FiberPolyData = m_PolyDataContainer->GetElement(0); for (int i=1; iGetNumberOfThreads(); i++) { m_FiberPolyData = AddPolyData(m_FiberPolyData, m_PolyDataContainer->GetElement(i)); } MITK_INFO << "done"; } template< class TTensorPixelType, class TPDPixelType> void StreamlineTrackingFilter< TTensorPixelType, TPDPixelType> ::PrintSelf(std::ostream& os, Indent indent) const { } } #endif // __itkDiffusionQballPrincipleDirectionsImageFilter_txx diff --git a/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.h b/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.h index 5b80bdd702..18b0bb2438 100644 --- a/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.h +++ b/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.h @@ -1,130 +1,133 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ /*=================================================================== This file is based heavily on a corresponding ITK filter. ===================================================================*/ #ifndef __itkStreamlineTrackingFilter_h_ #define __itkStreamlineTrackingFilter_h_ #include "MitkDiffusionImagingExports.h" #include #include #include #include #include #include #include #include #include namespace itk{ /** \class StreamlineTrackingFilter */ template< class TTensorPixelType, class TPDPixelType=double> class StreamlineTrackingFilter : public ImageToImageFilter< Image< DiffusionTensor3D, 3 >, Image< Vector< TPDPixelType, 3 >, 3 > > { public: typedef StreamlineTrackingFilter Self; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef ImageToImageFilter< Image< DiffusionTensor3D, 3 >, Image< Vector< TPDPixelType, 3 >, 3 > > Superclass; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Runtime information support. */ itkTypeMacro(StreamlineTrackingFilter, ImageToImageFilter) typedef TTensorPixelType TensorComponentType; typedef TPDPixelType DirectionPixelType; typedef typename Superclass::InputImageType InputImageType; typedef typename Superclass::OutputImageType OutputImageType; typedef typename Superclass::OutputImageRegionType OutputImageRegionType; typedef itk::Image ItkUcharImgType; typedef itk::Image ItkFloatImgType; typedef itk::Image< vnl_vector_fixed, 3> ItkPDImgType; typedef vtkSmartPointer< vtkPolyData > FiberPolyDataType; itkGetMacro( FiberPolyData, FiberPolyDataType ) itkSetMacro( SeedImage, ItkUcharImgType::Pointer) itkSetMacro( MaskImage, ItkUcharImgType::Pointer) itkSetMacro( SeedsPerVoxel, int) itkSetMacro( FaThreshold, float) itkSetMacro( AngularThreshold, float) itkSetMacro( StepSize, float) itkSetMacro( F, float ) itkSetMacro( G, float ) + itkSetMacro( Interpolate, bool ) protected: StreamlineTrackingFilter(); ~StreamlineTrackingFilter() {} void PrintSelf(std::ostream& os, Indent indent) const; void CalculateNewPosition(itk::ContinuousIndex& pos, vnl_vector_fixed& dir, typename InputImageType::IndexType& index); void FollowStreamline(itk::ContinuousIndex pos, int dirSign, vtkPoints* points, std::vector< vtkIdType >& ids); double RoundToNearest(double num); void BeforeThreadedGenerateData(); void ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, int threadId); void AfterThreadedGenerateData(); FiberPolyDataType AddPolyData(FiberPolyDataType poly1, FiberPolyDataType poly2); FiberPolyDataType m_FiberPolyData; vtkSmartPointer m_Points; vtkSmartPointer m_Cells; ItkFloatImgType::Pointer m_EmaxImage; ItkFloatImgType::Pointer m_FaImage; ItkPDImgType::Pointer m_PdImage; typename InputImageType::Pointer m_InputImage; float m_FaThreshold; float m_AngularThreshold; float m_StepSize; int m_MaxLength; int m_SeedsPerVoxel; float m_F; float m_G; std::vector< int > m_ImageSize; std::vector< float > m_ImageSpacing; ItkUcharImgType::Pointer m_SeedImage; ItkUcharImgType::Pointer m_MaskImage; + bool m_Interpolate; + float m_PointPistance; itk::VectorContainer< int, FiberPolyDataType >::Pointer m_PolyDataContainer; private: }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkStreamlineTrackingFilter.cpp" #endif #endif //__itkStreamlineTrackingFilter_h_ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingView.cpp index 40a9ce486d..8bedbb5b9b 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingView.cpp @@ -1,242 +1,244 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include #include // Qmitk #include "QmitkStreamlineTrackingView.h" #include "QmitkStdMultiWidget.h" // Qt #include // MITK #include #include // VTK #include #include #include #include #include #include const std::string QmitkStreamlineTrackingView::VIEW_ID = "org.mitk.views.streamlinetracking"; const std::string id_DataManager = "org.mitk.views.datamanager"; using namespace berry; QmitkStreamlineTrackingView::QmitkStreamlineTrackingView() : QmitkFunctionality() , m_Controls( 0 ) , m_MultiWidget( NULL ) , m_TensorImage( NULL ) , m_SeedRoi( NULL ) { } // Destructor QmitkStreamlineTrackingView::~QmitkStreamlineTrackingView() { } void QmitkStreamlineTrackingView::CreateQtPartControl( QWidget *parent ) { if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkStreamlineTrackingViewControls; m_Controls->setupUi( parent ); connect( m_Controls->commandLinkButton, SIGNAL(clicked()), this, SLOT(DoFiberTracking()) ); connect( m_Controls->m_SeedsPerVoxelSlider, SIGNAL(valueChanged(int)), this, SLOT(OnSeedsPerVoxelChanged(int)) ); connect( m_Controls->m_MinTractLengthSlider, SIGNAL(valueChanged(int)), this, SLOT(OnMinTractLengthChanged(int)) ); connect( m_Controls->m_FaThresholdSlider, SIGNAL(valueChanged(int)), this, SLOT(OnFaThresholdChanged(int)) ); connect( m_Controls->m_AngularThresholdSlider, SIGNAL(valueChanged(int)), this, SLOT(OnAngularThresholdChanged(int)) ); connect( m_Controls->m_StepsizeSlider, SIGNAL(valueChanged(int)), this, SLOT(OnStepsizeChanged(int)) ); connect( m_Controls->m_fSlider, SIGNAL(valueChanged(int)), this, SLOT(OnfChanged(int)) ); connect( m_Controls->m_gSlider, SIGNAL(valueChanged(int)), this, SLOT(OngChanged(int)) ); } } void QmitkStreamlineTrackingView::OnfChanged(int value) { m_Controls->m_fLabel->setText(QString("f: ")+QString::number((float)value/100)); } void QmitkStreamlineTrackingView::OngChanged(int value) { m_Controls->m_gLabel->setText(QString("g: ")+QString::number((float)value/100)); } void QmitkStreamlineTrackingView::OnAngularThresholdChanged(int value) { m_Controls->m_AngularThresholdLabel->setText(QString("Angular Threshold: ")+QString::number(value)+QString("°")); } void QmitkStreamlineTrackingView::OnSeedsPerVoxelChanged(int value) { m_Controls->m_SeedsPerVoxelLabel->setText(QString("Seeds per Voxel: ")+QString::number(value)); } void QmitkStreamlineTrackingView::OnMinTractLengthChanged(int value) { m_Controls->m_MinTractLengthLabel->setText(QString("Min. Tract Length: ")+QString::number(value)+QString("mm")); } void QmitkStreamlineTrackingView::OnFaThresholdChanged(int value) { m_Controls->m_FaThresholdLabel->setText(QString("FA Threshold: ")+QString::number((float)value/100)); } void QmitkStreamlineTrackingView::OnStepsizeChanged(int value) { if (value==0) m_Controls->m_StepsizeLabel->setText(QString("Stepsize: auto")); else m_Controls->m_StepsizeLabel->setText(QString("Stepsize: ")+QString::number((float)value/10)+QString("mm")); } void QmitkStreamlineTrackingView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkStreamlineTrackingView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkStreamlineTrackingView::OnSelectionChanged( std::vector nodes ) { m_TensorImageNode = NULL; m_TensorImage = NULL; m_SeedRoi = NULL; m_MaskImage = NULL; m_Controls->m_TensorImageLabel->setText("-"); m_Controls->m_RoiImageLabel->setText("-"); m_Controls->m_MaskImageLabel->setText("-"); if(nodes.empty()) return; for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; if( node.IsNotNull() && dynamic_cast(node->GetData()) ) { if( dynamic_cast(node->GetData()) ) { m_TensorImageNode = node; m_TensorImage = dynamic_cast(node->GetData()); m_Controls->m_TensorImageLabel->setText(node->GetName().c_str()); } else { bool isBinary = false; node->GetPropertyValue("binary", isBinary); if (isBinary && m_SeedRoi.IsNull()) { m_SeedRoi = dynamic_cast(node->GetData()); m_Controls->m_RoiImageLabel->setText(node->GetName().c_str()); } else if (isBinary) { m_MaskImage = dynamic_cast(node->GetData()); m_Controls->m_MaskImageLabel->setText(node->GetName().c_str()); } } } } if(m_TensorImage.IsNotNull()) m_Controls->commandLinkButton->setEnabled(true); else m_Controls->commandLinkButton->setEnabled(false); } void QmitkStreamlineTrackingView::DoFiberTracking() { if (m_TensorImage.IsNull()) return; typedef itk::Image< itk::DiffusionTensor3D, 3> TensorImageType; typedef mitk::ImageToItk CastType; typedef mitk::ImageToItk CastType2; CastType::Pointer caster = CastType::New(); caster->SetInput(m_TensorImage); caster->Update(); TensorImageType::Pointer image = caster->GetOutput(); typedef itk::StreamlineTrackingFilter< float > FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput(image); filter->SetSeedsPerVoxel(m_Controls->m_SeedsPerVoxelSlider->value()); filter->SetFaThreshold((float)m_Controls->m_FaThresholdSlider->value()/100); filter->SetAngularThreshold(cos((float)m_Controls->m_AngularThresholdSlider->value()*M_PI/180)); filter->SetStepSize((float)m_Controls->m_StepsizeSlider->value()/10); filter->SetF((float)m_Controls->m_fSlider->value()/100); filter->SetG((float)m_Controls->m_gSlider->value()/100); + filter->SetInterpolate(m_Controls->m_InterpolationBox->isChecked()); + //filter->SetNumberOfThreads(1); if (m_SeedRoi.IsNotNull()) { CastType2::Pointer caster2 = CastType2::New(); caster2->SetInput(m_SeedRoi); caster2->Update(); ItkUCharImageType::Pointer mask = caster2->GetOutput(); filter->SetSeedImage(mask); } if (m_MaskImage.IsNotNull()) { CastType2::Pointer caster2 = CastType2::New(); caster2->SetInput(m_MaskImage); caster2->Update(); ItkUCharImageType::Pointer mask = caster2->GetOutput(); filter->SetMaskImage(mask); } filter->Update(); vtkSmartPointer fiberBundle = filter->GetFiberPolyData(); if ( fiberBundle->GetNumberOfLines()==0 ) return; mitk::FiberBundleX::Pointer fib = mitk::FiberBundleX::New(fiberBundle); if (fib->RemoveShortFibers(m_Controls->m_MinTractLengthSlider->value())) { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(fib); QString name(m_TensorImageNode->GetName().c_str()); name += "_FiberBundle"; node->SetName(name.toStdString()); node->SetVisibility(true); GetDataStorage()->Add(node); } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingViewControls.ui index 487f267a83..64c86d2904 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingViewControls.ui @@ -1,375 +1,385 @@ QmitkStreamlineTrackingViewControls 0 0 480 553 0 0 QmitkTemplate 3 3 0 - - - Number of tracts started in each voxel of the seed ROI. - - - Mori et al. Annals Neurology 1999 - - - - - - - false - - - Start Tracking - - - - Qt::Vertical QSizePolicy::Expanding 20 220 Parameters - - - - Minimum tract length in mm. - - - Angular Threshold: 45° - - - - - - - Minimum tract length in mm. - - - Step Size: auto - - - - - - - Minimum tract length in mm. - - - f: 1 - - - Number of tracts started in each voxel of the seed ROI. 1 10 Qt::Horizontal Fractional Anisotropy Threshold 0 90 45 Qt::Horizontal Minimum tract length in mm. 0 500 40 Qt::Horizontal Number of tracts started in each voxel of the seed ROI. Seeds per Voxel: 1 Qt::Horizontal QSizePolicy::Fixed 200 0 Fractional Anisotropy Threshold 0 100 20 Qt::Horizontal Weighting factor between first eigenvector (f=1 equals FACT tracking) and input vector dependent direction (f=0). 0 100 100 Qt::Horizontal Minimum tract length in mm. FA Threshold: 0.2 Minimum tract length in mm. Min. Tract Length: 40mm Stepsize in mm (auto = 0.1*minimal spacing) 0 100 0 Qt::Horizontal Minimum tract length in mm. g: 0 Weighting factor between input vector (g=0) and tensor deflection (g=1 equals TEND tracking) 0 100 0 Qt::Horizontal + + + + Minimum tract length in mm. + + + Angular Threshold: 45° + + + + + + + Minimum tract length in mm. + + + Step Size: auto + + + + + + + Minimum tract length in mm. + + + f: 1 + + + + + + + Default is nearest neighbor interpolation. + + + Enable trilinear interpolation + + + + + + + Number of tracts started in each voxel of the seed ROI. + + + Mori et al. Annals Neurology 1999 + + + + + + + false + + + Start Tracking + + + Data - - Seed ROI Image: Tensor Image: Stop tracking if leaving mask Mask Image: - - + Number of tracts started in each voxel of the seed ROI. Lazar et al. Human Brain Mapping 2003 - + Number of tracts started in each voxel of the seed ROI. Weinstein et al. Proceedings of IEEE Visualization 1999 commandLinkButton