diff --git a/Modules/DiffusionCmdApps/FiberProcessing/CMakeLists.txt b/Modules/DiffusionCmdApps/FiberProcessing/CMakeLists.txt index 4f5a308..2acf97a 100644 --- a/Modules/DiffusionCmdApps/FiberProcessing/CMakeLists.txt +++ b/Modules/DiffusionCmdApps/FiberProcessing/CMakeLists.txt @@ -1,49 +1,50 @@ option(BUILD_DiffusionFiberProcessingCmdApps "Build commandline tools for diffusion fiber processing" OFF) if(BUILD_DiffusionFiberProcessingCmdApps OR MITK_BUILD_ALL_APPS) # needed include directories include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) # list of diffusion cmdapps # if an app requires additional dependencies # they are added after a "^^" and separated by "_" set( diffusionFiberProcessingcmdapps TractDensity^^ Sift2WeightCopy^^ FiberExtraction^^ FiberExtractionRoi^^ FiberProcessing^^ FitFibersToImage^^ FiberDirectionExtraction^^ FiberJoin^^ FiberClustering^^ TractDensityFilter^^ PrintFiberStatistics^^ DistanceToSegmentation^^ + TractParcellation^^ ) foreach(diffusionFiberProcessingcmdapp ${diffusionFiberProcessingcmdapps}) # extract cmd app name and dependencies string(REPLACE "^^" "\\;" cmdapp_info ${diffusionFiberProcessingcmdapp}) set(cmdapp_info_list ${cmdapp_info}) list(GET cmdapp_info_list 0 appname) list(GET cmdapp_info_list 1 raw_dependencies) string(REPLACE "_" "\\;" dependencies "${raw_dependencies}") set(dependencies_list ${dependencies}) mitkFunctionCreateCommandLineApp( NAME ${appname} DEPENDS MitkDiffusionCmdApps MitkMriSimulation MitkFiberTracking ${dependencies_list} PACKAGE_DEPENDS ) endforeach() if(EXECUTABLE_IS_ENABLED) MITK_INSTALL_TARGETS(EXECUTABLES ${EXECUTABLE_TARGET}) endif() endif() diff --git a/Modules/DiffusionCmdApps/FiberProcessing/TractParcellation.cpp b/Modules/DiffusionCmdApps/FiberProcessing/TractParcellation.cpp new file mode 100644 index 0000000..0be6698 --- /dev/null +++ b/Modules/DiffusionCmdApps/FiberProcessing/TractParcellation.cpp @@ -0,0 +1,152 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center. + +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. + +===================================================================*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include "mitkDiffusionCommandLineParser.h" +#include +#include +#include +#include +#include + + +mitk::FiberBundle::Pointer LoadFib(std::string filename) +{ + std::vector fibInfile = mitk::IOUtil::Load(filename); + if( fibInfile.empty() ) + std::cout << "File " << filename << " could not be read!"; + mitk::BaseData::Pointer baseData = fibInfile.at(0); + return dynamic_cast(baseData.GetPointer()); +} + +/*! +\brief Modify input tractogram: fiber resampling, compression, pruning and transformation. +*/ +int main(int argc, char* argv[]) +{ + mitkDiffusionCommandLineParser parser; + + parser.setTitle("Tract Density"); + parser.setCategory("Fiber Tracking and Processing Methods"); + parser.setDescription("Generate tract density image, fiber envelope or fiber endpoints image."); + parser.setContributor("MIC"); + + parser.setArgumentPrefix("--", "-"); + parser.addArgument("", "i", mitkDiffusionCommandLineParser::String, "Input:", "input fiber bundle", us::Any(), false); + parser.addArgument("", "o", mitkDiffusionCommandLineParser::String, "Output:", "output image", us::Any(), false); + parser.addArgument("reference_image", "", mitkDiffusionCommandLineParser::String, "Reference image:", "output image will have geometry of this reference image", us::Any(), false); + parser.addArgument("binary", "", mitkDiffusionCommandLineParser::Bool, "Binary output:", "", false); + + parser.addArgument("num_parcels", "", mitkDiffusionCommandLineParser::Int, "Number of parcels:", "", 15); + parser.addArgument("num_centroids", "", mitkDiffusionCommandLineParser::Int, "Number of centroids:", "", 0); + parser.addArgument("start_cluster_size", "", mitkDiffusionCommandLineParser::Float, "Cluster size (in mm):", "", 5.0); + + std::map parsedArgs = parser.parseArguments(argc, argv); + if (parsedArgs.size()==0) + return EXIT_FAILURE; + + bool binary = false; + if (parsedArgs.count("binary")) + binary = us::any_cast(parsedArgs["binary"]); + + int num_parcels = 15; + if (parsedArgs.count("num_parcels")) + num_parcels = us::any_cast(parsedArgs["num_parcels"]); + + int num_centroids = 0; + if (parsedArgs.count("num_centroids")) + num_centroids = us::any_cast(parsedArgs["num_centroids"]); + + float start_cluster_size = 5; + if (parsedArgs.count("start_cluster_size")) + start_cluster_size = us::any_cast(parsedArgs["start_cluster_size"]); + + std::string reference_image = us::any_cast(parsedArgs["reference_image"]); + std::string inFileName = us::any_cast(parsedArgs["i"]); + std::string outFileName = us::any_cast(parsedArgs["o"]); + + try + { + mitk::FiberBundle::Pointer fib = LoadFib(inFileName); + mitk::Image::Pointer ref_img = mitk::IOUtil::Load(reference_image); + + typedef unsigned char OutPixType; + typedef itk::Image OutImageType; + + OutImageType::Pointer itkImage = OutImageType::New(); + CastToItkImage(ref_img, itkImage); + + itk::TractParcellationFilter< >::Pointer parcellator = itk::TractParcellationFilter< >::New(); + parcellator->SetInputImage(itkImage); + parcellator->SetNumParcels(num_parcels); + parcellator->SetInputTract(fib); + parcellator->SetNumCentroids(num_centroids); + parcellator->SetStartClusterSize(start_cluster_size); + parcellator->Update(); + OutImageType::Pointer out_image = parcellator->GetOutput(0); + OutImageType::Pointer out_image_pp = parcellator->GetOutput(1); + + if (binary) + { + auto binary_segments = parcellator->GetBinarySplit(out_image_pp); + + int c=1; + for (auto itk_segment : binary_segments) + { + mitk::Image::Pointer mitk_segment = mitk::Image::New(); + mitk_segment->InitializeByItk(itk_segment.GetPointer()); + mitk_segment->SetVolume(itk_segment->GetBufferPointer()); + mitk::IOUtil::Save(mitk_segment, outFileName + "_" + boost::lexical_cast(c) + ".nrrd"); + ++c; + } + } + else + { + mitk::Image::Pointer mitk_img_pp = mitk::Image::New(); + mitk_img_pp->InitializeByItk(out_image_pp.GetPointer()); + mitk_img_pp->SetVolume(out_image_pp->GetBufferPointer()); + mitk::IOUtil::Save(mitk_img_pp, outFileName ); + } + } + catch (const itk::ExceptionObject& e) + { + std::cout << e.what(); + return EXIT_FAILURE; + } + catch (std::exception& e) + { + std::cout << e.what(); + return EXIT_FAILURE; + } + catch (...) + { + std::cout << "ERROR!?!"; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/Modules/FiberTracking/Algorithms/itkTractParcellationFilter.cpp b/Modules/FiberTracking/Algorithms/itkTractParcellationFilter.cpp index 83e47f3..ed7928b 100644 --- a/Modules/FiberTracking/Algorithms/itkTractParcellationFilter.cpp +++ b/Modules/FiberTracking/Algorithms/itkTractParcellationFilter.cpp @@ -1,379 +1,404 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. 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. ===================================================================*/ #include "itkTractParcellationFilter.h" // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include namespace itk{ template< class OutImageType > TractParcellationFilter< OutImageType >::TractParcellationFilter() : m_UpsamplingFactor(1) , m_NumParcels(15) , m_NumCentroids(0) , m_StartClusterSize(5) , m_InputImage(nullptr) { this->SetNumberOfRequiredOutputs(2); } template< class OutImageType > TractParcellationFilter< OutImageType >::~TractParcellationFilter() { } template< class OutImageType > bool TractParcellationFilter< OutImageType >::Flip(vtkSmartPointer< vtkPolyData > polydata1, int i, vtkSmartPointer< vtkPolyData > ref_poly, int ref_i) { double d_direct = 0; double d_flipped = 0; vtkCell* cell1 = polydata1->GetCell(ref_i); if (ref_poly!=nullptr) cell1 = ref_poly->GetCell(ref_i); auto numPoints1 = cell1->GetNumberOfPoints(); vtkPoints* points1 = cell1->GetPoints(); std::vector> ref_points; for (int j=0; jGetPoint(j); itk::Point itk_p; itk_p[0] = p1[0]; itk_p[1] = p1[1]; itk_p[2] = p1[2]; ref_points.push_back(itk_p); } vtkCell* cell2 = polydata1->GetCell(i); vtkPoints* points2 = cell2->GetPoints(); for (int j=0; jGetPoint(j); d_direct += (p1[0]-p2[0])*(p1[0]-p2[0]) + (p1[1]-p2[1])*(p1[1]-p2[1]) + (p1[2]-p2[2])*(p1[2]-p2[2]); double* p3 = points2->GetPoint(numPoints1-j-1); d_flipped += (p1[0]-p3[0])*(p1[0]-p3[0]) + (p1[1]-p3[1])*(p1[1]-p3[1]) + (p1[2]-p3[2])*(p1[2]-p3[2]); } if (d_direct>d_flipped) return true; return false; } template< class OutImageType > mitk::FiberBundle::Pointer TractParcellationFilter< OutImageType >::GetWorkingFib() { mitk::FiberBundle::Pointer fib_static_resampled = m_InputTract->GetDeepCopy(); fib_static_resampled->ResampleToNumPoints(m_NumParcels); // clustering std::vector< mitk::ClusteringMetric* > metrics; metrics.push_back({new mitk::ClusteringMetricEuclideanStd()}); metrics.push_back({new mitk::ClusteringMetricEuclideanMean()}); metrics.push_back({new mitk::ClusteringMetricLength()}); if (m_NumCentroids>0) { std::vector centroids; std::shared_ptr< mitk::TractClusteringFilter > clusterer = std::make_shared(); int c=0; while (c<30 && (centroids.empty() || centroids.size()>static_cast(m_NumCentroids))) { float cluster_size = m_StartClusterSize + m_StartClusterSize*0.2*c; float max_d = 0; int i=1; std::vector< float > distances; while (max_d < m_InputTract->GetGeometry()->GetDiagonalLength()/2) { distances.push_back(cluster_size*i); max_d = cluster_size*i; ++i; } clusterer->SetDistances(distances); clusterer->SetTractogram(fib_static_resampled); clusterer->SetMetrics(metrics); clusterer->SetMergeDuplicateThreshold(cluster_size); clusterer->SetDoResampling(false); clusterer->SetNumPoints(m_NumParcels); // clusterer->SetMaxClusters(m_Controls->m_MaxCentroids->value()); clusterer->SetMinClusterSize(1); clusterer->Update(); centroids = clusterer->GetOutCentroids(); ++c; } mitk::FiberBundle::Pointer centroid_bundle = mitk::FiberBundle::New(); centroid_bundle = centroid_bundle->AddBundles(centroids); return centroid_bundle; } return fib_static_resampled; } template< class OutImageType > -std::vector< typename OutImageType::Pointer > TractParcellationFilter< OutImageType >::GetBinarySplit(typename OutImageType::Pointer inImage) +std::vector< typename itk::Image::Pointer > TractParcellationFilter< OutImageType >::GetBinarySplit(typename OutImageType::Pointer inImage) { + std::vector< typename itk::Image::Pointer > binary_maps; + for (unsigned int i=0; i::Pointer parcel_image = itk::Image::New(); + parcel_image->SetSpacing( inImage->GetSpacing() ); + parcel_image->SetOrigin( inImage->GetOrigin() ); + parcel_image->SetDirection( inImage->GetDirection() ); + parcel_image->SetRegions( inImage->GetLargestPossibleRegion() ); + parcel_image->Allocate(); + parcel_image->FillBuffer(0); + + binary_maps.push_back(parcel_image); + } + + itk::ImageRegionIterator< itk::Image > p_it(inImage, inImage->GetLargestPossibleRegion()); + while(!p_it.IsAtEnd()) + { + if (p_it.Get()>0) + { + binary_maps.at(p_it.Get()-1)->SetPixel(p_it.GetIndex(), 1); + } + ++p_it; + } + + return binary_maps; } template< class OutImageType > typename OutImageType::Pointer TractParcellationFilter< OutImageType >::PostprocessParcellation(typename OutImageType::Pointer inImage) { itk::ImageRegionConstIterator< OutImageType > in_it(inImage, inImage->GetLargestPossibleRegion()); typename OutImageType::Pointer outImage = OutImageType::New(); outImage->SetSpacing( inImage->GetSpacing() ); outImage->SetOrigin( inImage->GetOrigin() ); outImage->SetDirection( inImage->GetDirection() ); outImage->SetRegions( inImage->GetLargestPossibleRegion() ); outImage->Allocate(); outImage->FillBuffer(0); itk::ImageRegionIterator< OutImageType > out_it(outImage, outImage->GetLargestPossibleRegion()); MITK_INFO << "Postprocessing parcellation"; while( !in_it.IsAtEnd() ) { if (in_it.Get()>0) { std::vector< OutPixelType > vote; vote.resize(m_NumParcels + 1, 0); typename OutImageType::SizeType regionSize; regionSize[0] = 3; regionSize[1] = 3; regionSize[2] = 3; typename OutImageType::IndexType regionIndex = in_it.GetIndex(); regionIndex[0] -= 1; regionIndex[1] -= 1; regionIndex[2] -= 1; typename OutImageType::RegionType region; region.SetSize(regionSize); region.SetIndex(regionIndex); itk::ImageRegionConstIterator rit(inImage, region); while( !rit.IsAtEnd() ) { if (rit.GetIndex()!=regionIndex) vote[rit.Get()]++; ++rit; } OutPixelType max = 0; unsigned int max_parcel = -1; for (unsigned int i=1; imax) { max = vote[i]; max_parcel = i; } } out_it.Set(max_parcel); } ++out_it; ++in_it; } MITK_INFO << "DONE"; return outImage; } template< class OutImageType > void TractParcellationFilter< OutImageType >::StaticResampleParcelVoting(typename OutImageType::Pointer outImage) { typename itk::TractDensityImageFilter< OutImageType >::Pointer generator = itk::TractDensityImageFilter< OutImageType >::New(); generator->SetFiberBundle(m_InputTract); generator->SetMode(TDI_MODE::BINARY); generator->SetInputImage(outImage); generator->SetUseImageGeometry(true); generator->Update(); auto envelope = generator->GetOutput(); m_WorkingTract = GetWorkingFib(); vtkSmartPointer< vtkPolyData > polydata = m_WorkingTract->GetFiberPolyData(); float maxd = m_InputTract->GetMeanFiberLength()/(2*m_NumParcels); itk::ImageRegionIterator< OutImageType > it(outImage, outImage->GetLargestPossibleRegion()); itk::ImageRegionIterator< OutImageType > it_mask(envelope, envelope->GetLargestPossibleRegion()); vtkSmartPointer< vtkPolyData > reference_polydata = nullptr; if (m_ReferenceTract.IsNotNull()) reference_polydata = m_ReferenceTract->GetFiberPolyData(); unsigned long num_vox = 0; while( !it_mask.IsAtEnd() ) { if (it_mask.Get()>0) ++num_vox; ++it_mask; } it_mask.GoToBegin(); MITK_INFO << "Parcellating tract"; boost::progress_display disp(num_vox); while( !it.IsAtEnd() ) { if (it_mask.Get()>0) { int final_seg_id = -1; int mult = 1; while(final_seg_id<0) { std::vector seg_vote; seg_vote.resize(m_NumParcels, 0); typename OutImageType::PointType image_point; envelope->TransformIndexToPhysicalPoint(it.GetIndex(), image_point); for (unsigned int i=0; iGetNumFibers(); ++i) { vtkCell* cell = polydata->GetCell(i); auto numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); bool flip = Flip(polydata, i, reference_polydata); float local_d = 99999999; int local_closest_seg = -1; for (int j=0; jGetPoint(segment_id); } else { p = points->GetPoint(j); segment_id = j; } float d = std::fabs( (p[0]-image_point[0]) ) + std::fabs( (p[1]-image_point[1]) ) + std::fabs( (p[2]-image_point[2]) ); if (dmax_count) { final_seg_id = i; max_count = seg_vote.at(i); } } if (final_seg_id>=0) it.Set(final_seg_id + 1); ++mult; } ++disp; } ++it; ++it_mask; } MITK_INFO << "DONE"; } template< class OutImageType > void TractParcellationFilter< OutImageType >::GenerateData() { // generate upsampled image mitk::BaseGeometry::Pointer geometry = m_InputTract->GetGeometry(); // calculate new image parameters itk::Vector newSpacing; mitk::Point3D newOrigin; itk::Matrix newDirection; ImageRegion<3> upsampledRegion; if (!m_InputImage.IsNull()) { newSpacing = m_InputImage->GetSpacing()/m_UpsamplingFactor; upsampledRegion = m_InputImage->GetLargestPossibleRegion(); newOrigin = m_InputImage->GetOrigin(); typename OutImageType::RegionType::SizeType size = upsampledRegion.GetSize(); size[0] *= m_UpsamplingFactor; size[1] *= m_UpsamplingFactor; size[2] *= m_UpsamplingFactor; upsampledRegion.SetSize(size); newDirection = m_InputImage->GetDirection(); } else { newSpacing = geometry->GetSpacing()/m_UpsamplingFactor; newOrigin = geometry->GetOrigin(); mitk::Geometry3D::BoundsArrayType bounds = geometry->GetBounds(); newOrigin[0] += bounds.GetElement(0); newOrigin[1] += bounds.GetElement(2); newOrigin[2] += bounds.GetElement(4); for (int i=0; i<3; i++) for (int j=0; j<3; j++) newDirection[j][i] = geometry->GetMatrixColumn(i)[j]; upsampledRegion.SetSize(0, geometry->GetExtent(0)*m_UpsamplingFactor); upsampledRegion.SetSize(1, geometry->GetExtent(1)*m_UpsamplingFactor); upsampledRegion.SetSize(2, geometry->GetExtent(2)*m_UpsamplingFactor); } // apply new image parameters typename OutImageType::Pointer outImage = OutImageType::New(); outImage->SetSpacing( newSpacing ); outImage->SetOrigin( newOrigin ); outImage->SetDirection( newDirection ); outImage->SetRegions( upsampledRegion ); outImage->Allocate(); outImage->FillBuffer(0); this->SetNthOutput(0, outImage); StaticResampleParcelVoting(outImage); auto outImage_pp = PostprocessParcellation(outImage); outImage_pp = PostprocessParcellation(outImage_pp); outImage_pp = PostprocessParcellation(outImage_pp); this->SetNthOutput(1, outImage_pp); } } diff --git a/Modules/FiberTracking/Algorithms/itkTractParcellationFilter.h b/Modules/FiberTracking/Algorithms/itkTractParcellationFilter.h index 7d7d8d0..9bfba01 100644 --- a/Modules/FiberTracking/Algorithms/itkTractParcellationFilter.h +++ b/Modules/FiberTracking/Algorithms/itkTractParcellationFilter.h @@ -1,96 +1,96 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center. 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 __itkTractParcellationFilter_h__ #define __itkTractParcellationFilter_h__ #include #include #include namespace itk{ /** * \brief Generates image where the pixel values are set according to the position along a fiber bundle. */ template< class OutImageType=itk::Image > class TractParcellationFilter : public ImageSource< OutImageType > { public: typedef TractParcellationFilter Self; typedef ProcessObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; typedef typename OutImageType::PixelType OutPixelType; itkFactorylessNewMacro(Self) itkCloneMacro(Self) itkTypeMacro( TractParcellationFilter, ImageSource ) itkSetMacro( UpsamplingFactor, float) itkGetMacro( UpsamplingFactor, float) itkSetMacro( NumParcels, unsigned int ) itkGetMacro( NumParcels, unsigned int ) itkSetMacro( NumCentroids, unsigned int ) itkGetMacro( NumCentroids, unsigned int ) itkSetMacro( StartClusterSize, float ) itkGetMacro( StartClusterSize, float ) itkSetMacro( InputTract, mitk::FiberBundle::Pointer) itkSetMacro( ReferenceTract, mitk::FiberBundle::Pointer) itkGetMacro( WorkingTract, mitk::FiberBundle::Pointer) itkSetMacro( InputImage, typename OutImageType::Pointer) + std::vector< typename itk::Image::Pointer > GetBinarySplit(typename OutImageType::Pointer inImage); + void GenerateData() override; protected: TractParcellationFilter(); virtual ~TractParcellationFilter(); bool Flip(vtkSmartPointer< vtkPolyData > polydata1, int i, vtkSmartPointer< vtkPolyData > ref_poly=nullptr, int ref_i=0); mitk::FiberBundle::Pointer GetWorkingFib(); typename OutImageType::Pointer PostprocessParcellation(typename OutImageType::Pointer outImage); void StaticResampleParcelVoting(typename OutImageType::Pointer outImage); - std::vector< typename OutImageType::Pointer > GetBinarySplit(typename OutImageType::Pointer inImage); - mitk::FiberBundle::Pointer m_ReferenceTract; mitk::FiberBundle::Pointer m_InputTract; ///< input fiber bundle mitk::FiberBundle::Pointer m_WorkingTract; float m_UpsamplingFactor; ///< use higher resolution for ouput image unsigned int m_NumParcels; unsigned int m_NumCentroids; float m_StartClusterSize; typename OutImageType::Pointer m_InputImage; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkTractParcellationFilter.cpp" #endif #endif // __itkTractParcellationFilter_h__