diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractClusteringFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractClusteringFilter.cpp index 92f343adc7..3c6a9a446d 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractClusteringFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractClusteringFilter.cpp @@ -1,499 +1,556 @@ /*=================================================================== 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. ===================================================================*/ #include "itkTractClusteringFilter.h" #define _USE_MATH_DEFINES #include #include #include namespace itk{ TractClusteringFilter::TractClusteringFilter() : m_NumPoints(12) , m_InCentroids(nullptr) , m_MinClusterSize(1) , m_MaxClusters(0) , m_MergeDuplicateThreshold(-1) , m_DoResampling(true) , m_FilterMask(nullptr) , m_OverlapThreshold(0.0) { } TractClusteringFilter::~TractClusteringFilter() { for (auto m : m_Metrics) delete m; } std::vector > TractClusteringFilter::GetOutFiberIndices() const { return m_OutFiberIndices; } void TractClusteringFilter::SetMetrics(const std::vector &Metrics) { m_Metrics = Metrics; } std::vector TractClusteringFilter::GetOutClusters() const { return m_OutClusters; } std::vector TractClusteringFilter::GetOutCentroids() const { return m_OutCentroids; } std::vector TractClusteringFilter::GetOutTractograms() const { return m_OutTractograms; } void TractClusteringFilter::SetDistances(const std::vector &Distances) { m_Distances = Distances; } float TractClusteringFilter::CalcOverlap(vnl_matrix& t) { float overlap = 0; if (m_FilterMask.IsNotNull()) { for (unsigned int i=0; i p = t.get_column(i); itk::Point point; point[0] = p[0]; point[1] = p[1]; point[2] = p[2]; itk::Index<3> idx; m_FilterMask->TransformPhysicalPointToIndex(point, idx); if (m_FilterMask->GetLargestPossibleRegion().IsInside(idx) && m_FilterMask->GetPixel(idx)>0) overlap += 1; } overlap /= m_NumPoints; } else return 1.0; return overlap; } std::vector > TractClusteringFilter::ResampleFibers(mitk::FiberBundle::Pointer tractogram) { mitk::FiberBundle::Pointer temp_fib = tractogram->GetDeepCopy(); if (m_DoResampling) temp_fib->ResampleToNumPoints(m_NumPoints); std::vector< vnl_matrix > out_fib; for (int i=0; iGetFiberPolyData()->GetNumberOfCells(); i++) { vtkCell* cell = temp_fib->GetFiberPolyData()->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); vnl_matrix streamline; streamline.set_size(3, m_NumPoints); streamline.fill(0.0); for (int j=0; jGetPoint(j, cand); vnl_vector_fixed< float, 3 > candV; candV[0]=cand[0]; candV[1]=cand[1]; candV[2]=cand[2]; streamline.set_column(j, candV); } out_fib.push_back(streamline); } return out_fib; } std::vector< TractClusteringFilter::Cluster > TractClusteringFilter::ClusterStep(std::vector< long > f_indices, std::vector distances) { float dist_thres = distances.back(); distances.pop_back(); std::vector< Cluster > C; int N = f_indices.size(); Cluster c1; c1.I.push_back(f_indices.at(0)); c1.h = T[f_indices.at(0)]; c1.n = 1; C.push_back(c1); if (f_indices.size()==1) return C; for (int i=1; i t = T.at(f_indices.at(i)); int min_cluster_index = -1; float min_cluster_distance = 99999; bool flip = false; for (unsigned int k=0; k v = C.at(k).h / C.at(k).n; bool f = false; float d = 0; for (auto m : m_Metrics) d += m->CalculateDistance(t, v, f); d /= m_Metrics.size(); if (d=0 && min_cluster_distance outC; #pragma omp parallel for for (int c=0; c<(int)C.size(); c++) { std::vector< Cluster > tempC = ClusterStep(C.at(c).I, distances); #pragma omp critical AppendCluster(outC, tempC); } return outC; } else return C; } void TractClusteringFilter::AppendCluster(std::vector< Cluster >& a, std::vector< Cluster >&b) { for (auto c : b) a.push_back(c); } +std::vector< TractClusteringFilter::Cluster > TractClusteringFilter::MergeDuplicateClusters2(std::vector< TractClusteringFilter::Cluster >& clusters) +{ + if (m_MergeDuplicateThreshold<0) + m_MergeDuplicateThreshold = m_Distances.at(0)/2; + + MITK_INFO << "Merging duplicate clusters with distance threshold " << m_MergeDuplicateThreshold; + + std::vector< TractClusteringFilter::Cluster > new_clusters; + for (Cluster c1 : clusters) + { + vnl_matrix t = c1.h / c1.n; + + int min_idx = -1; + float min_d = 99999; + bool flip = false; + +#pragma omp parallel for + for (unsigned int k2=0; k2 v = c2.h / c2.n; + + bool f = false; + float d = 0; + for (auto m : m_Metrics) + d += m->CalculateDistance(t, v, f); + d /= m_Metrics.size(); + +#pragma omp critical + if (d& clusters) { if (m_MergeDuplicateThreshold<0) m_MergeDuplicateThreshold = m_Distances.at(0)/2; bool found = true; MITK_INFO << "Merging duplicate clusters with distance threshold " << m_MergeDuplicateThreshold; int start = 0; while (found && m_MergeDuplicateThreshold>mitk::eps) { std::cout << " \r"; std::cout << "Number of clusters: " << clusters.size() << '\r'; cout.flush(); found = false; for (int k1=start; k1<(int)clusters.size(); ++k1) { Cluster c1 = clusters.at(k1); vnl_matrix t = c1.h / c1.n; std::vector< int > merge_indices; std::vector< bool > flip_indices; #pragma omp parallel for for (int k2=start+1; k2<(int)clusters.size(); ++k2) { if (k1!=k2) { Cluster c2 = clusters.at(k2); vnl_matrix v = c2.h / c2.n; bool f = false; float d = 0; for (auto m : m_Metrics) d += m->CalculateDistance(t, v, f); d /= m_Metrics.size(); #pragma omp critical if (d TractClusteringFilter::AddToKnownClusters(std::vector< long > f_indices, std::vector >& centroids) { float dist_thres = m_Distances.at(0); int N = f_indices.size(); std::vector< Cluster > C; vnl_matrix zero_h; zero_h.set_size(T.at(0).rows(), T.at(0).cols()); zero_h.fill(0.0); Cluster no_fit; no_fit.h = zero_h; for (unsigned int i=0; i t = T.at(f_indices.at(i)); int min_cluster_index = -1; float min_cluster_distance = 99999; bool flip = false; if (CalcOverlap(t)>=m_OverlapThreshold) { int c_idx = 0; for (vnl_matrix centroid : centroids) { bool f = false; float d = 0; for (auto m : m_Metrics) d += m->CalculateDistance(t, centroid, f); d /= m_Metrics.size(); if (d=0 && min_cluster_distance f_indices; for (unsigned int i=0; i clusters; if (m_InCentroids.IsNull()) { MITK_INFO << "Clustering fibers"; clusters = ClusterStep(f_indices, m_Distances); MITK_INFO << "Number of clusters: " << clusters.size(); - MergeDuplicateClusters(clusters); + clusters = MergeDuplicateClusters2(clusters); std::sort(clusters.begin(),clusters.end()); } else { std::vector > centroids = ResampleFibers(m_InCentroids); if (centroids.empty()) { MITK_INFO << "No fibers in centroid tractogram!"; return; } MITK_INFO << "Clustering with input centroids"; clusters = AddToKnownClusters(f_indices, centroids); no_match = clusters.back(); clusters.pop_back(); MITK_INFO << "Number of clusters: " << clusters.size(); - MergeDuplicateClusters(clusters); + clusters = MergeDuplicateClusters2(clusters); } MITK_INFO << "Clustering finished"; int max = clusters.size()-1; if (m_MaxClusters>0 && clusters.size()-1>m_MaxClusters) max = m_MaxClusters; int skipped = 0; for (int i=clusters.size()-1; i>=0; --i) { Cluster c = clusters.at(i); if ( c.n>=(int)m_MinClusterSize && !(m_MaxClusters>0 && clusters.size()-i>m_MaxClusters) ) { m_OutClusters.push_back(c); vtkSmartPointer weights = vtkSmartPointer::New(); vtkSmartPointer pTmp = m_Tractogram->GeneratePolyDataByIds(c.I, weights); mitk::FiberBundle::Pointer fib = mitk::FiberBundle::New(pTmp); if (max>0) fib->SetFiberWeights((float)i/max); m_OutTractograms.push_back(fib); m_OutFiberIndices.push_back(c.I); // create centroid vtkSmartPointer vtkNewPoints = vtkSmartPointer::New(); vtkSmartPointer vtkNewCells = vtkSmartPointer::New(); vtkSmartPointer polyData = vtkSmartPointer::New(); vtkSmartPointer container = vtkSmartPointer::New(); vnl_matrix centroid_points = c.h / c.n; for (unsigned int j=0; jInsertNextPoint(p); container->GetPointIds()->InsertNextId(id); } vtkNewCells->InsertNextCell(container); polyData->SetPoints(vtkNewPoints); polyData->SetLines(vtkNewCells); mitk::FiberBundle::Pointer centroid = mitk::FiberBundle::New(polyData); centroid->SetFiberColors(255, 255, 255); m_OutCentroids.push_back(centroid); } else { skipped++; } } MITK_INFO << "Final number of clusters: " << m_OutTractograms.size() << " (discarded " << skipped << " clusters)"; int w = 0; for (auto fib : m_OutTractograms) { if (m_OutTractograms.size()>1) { fib->SetFiberWeights((float)w/(m_OutTractograms.size()-1)); m_OutCentroids.at(w)->SetFiberWeights((float)w/(m_OutTractograms.size()-1)); } else { fib->SetFiberWeights(1); m_OutCentroids.at(w)->SetFiberWeights(1); } fib->ColorFibersByFiberWeights(false, false); ++w; } if (no_match.n>0) { vtkSmartPointer weights = vtkSmartPointer::New(); vtkSmartPointer pTmp = m_Tractogram->GeneratePolyDataByIds(no_match.I, weights); mitk::FiberBundle::Pointer fib = mitk::FiberBundle::New(pTmp); fib->SetFiberColors(0, 0, 0); m_OutFiberIndices.push_back(no_match.I); m_OutTractograms.push_back(fib); } } } diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractClusteringFilter.h b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractClusteringFilter.h index bf03459e68..116c2f12a1 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractClusteringFilter.h +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractClusteringFilter.h @@ -1,139 +1,140 @@ /*=================================================================== 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 itkTractClusteringFilter_h #define itkTractClusteringFilter_h // MITK #include #include #include #include // ITK #include // VTK #include #include #include #include #include namespace itk{ /** * \brief */ class TractClusteringFilter : public ProcessObject { public: struct Cluster { Cluster() : n(0), f_id(-1) {} vnl_matrix h; std::vector< long > I; int n; int f_id; bool operator <(Cluster const& b) const { return this->n < b.n; } }; typedef TractClusteringFilter Self; typedef ProcessObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; typedef itk::Image< float, 3 > FloatImageType; typedef itk::Image< unsigned char, 3 > UcharImageType; itkFactorylessNewMacro(Self) itkCloneMacro(Self) itkTypeMacro( TractClusteringFilter, ProcessObject ) itkSetMacro(NumPoints, unsigned int) ///< Fibers are resampled to the specified number of points. If scalar maps are used, a larger number of points is recommended. itkGetMacro(NumPoints, unsigned int) ///< Fibers are resampled to the specified number of points. If scalar maps are used, a larger number of points is recommended. itkSetMacro(MinClusterSize, unsigned int) ///< Clusters with too few fibers are discarded itkGetMacro(MinClusterSize, unsigned int) ///< Clusters with too few fibers are discarded itkSetMacro(MaxClusters, unsigned int) ///< Only the N largest clusters are kept itkGetMacro(MaxClusters, unsigned int) ///< Only the N largest clusters are kept itkSetMacro(MergeDuplicateThreshold, float) ///< Clusters with centroids very close to each other are merged. Set to 0 to avoid merging and to -1 to use the original cluster size. itkGetMacro(MergeDuplicateThreshold, float) ///< Clusters with centroids very close to each other are merged. Set to 0 to avoid merging and to -1 to use the original cluster size. itkSetMacro(DoResampling, bool) ///< Resample fibers to equal number of points. This is mandatory, but can be performed outside of the filter if desired. itkGetMacro(DoResampling, bool) ///< Resample fibers to equal number of points. This is mandatory, but can be performed outside of the filter if desired. itkSetMacro(OverlapThreshold, float) ///< Overlap threshold used in conjunction with the filter mask when clustering around known centroids. itkGetMacro(OverlapThreshold, float) ///< Overlap threshold used in conjunction with the filter mask when clustering around known centroids. itkSetMacro(Tractogram, mitk::FiberBundle::Pointer) ///< The streamlines to be clustered itkSetMacro(InCentroids, mitk::FiberBundle::Pointer) ///< If a tractogram containing known tract centroids is set, the input fibers are assigned to the closest centroid. If no centroid is found within the specified smallest clustering distance, the fiber is assigned to the no-fit cluster. itkSetMacro(FilterMask, UcharImageType::Pointer) ///< If fibers are clustered around the nearest input centroids (see SetInCentroids), the complete input tractogram can additionally be pre-filtered with this binary mask and a given overlap threshold (see SetOverlapThreshold). virtual void Update() override{ this->GenerateData(); } void SetDistances(const std::vector &Distances); ///< Set clustering distances that are traversed recoursively. The distances have to be sorted in an ascending manner. The actual cluster size is determined by the smallest entry in the distance-list (index 0). std::vector GetOutTractograms() const; std::vector GetOutCentroids() const; std::vector GetOutClusters() const; void SetMetrics(const std::vector &Metrics); std::vector > GetOutFiberIndices() const; protected: void GenerateData() override; std::vector< vnl_matrix > ResampleFibers(FiberBundle::Pointer tractogram); float CalcOverlap(vnl_matrix& t); std::vector< Cluster > ClusterStep(std::vector< long > f_indices, std::vector< float > distances); + std::vector< TractClusteringFilter::Cluster > MergeDuplicateClusters2(std::vector< TractClusteringFilter::Cluster >& clusters); void MergeDuplicateClusters(std::vector< TractClusteringFilter::Cluster >& clusters); std::vector< Cluster > AddToKnownClusters(std::vector< long > f_indices, std::vector > ¢roids); void AppendCluster(std::vector< Cluster >& a, std::vector< Cluster >&b); TractClusteringFilter(); virtual ~TractClusteringFilter(); unsigned int m_NumPoints; std::vector< float > m_Distances; mitk::FiberBundle::Pointer m_Tractogram; mitk::FiberBundle::Pointer m_InCentroids; std::vector< mitk::FiberBundle::Pointer > m_OutTractograms; std::vector< mitk::FiberBundle::Pointer > m_OutCentroids; std::vector > T; unsigned int m_MinClusterSize; unsigned int m_MaxClusters; float m_MergeDuplicateThreshold; std::vector< Cluster > m_OutClusters; bool m_DoResampling; UcharImageType::Pointer m_FilterMask; float m_OverlapThreshold; std::vector< mitk::ClusteringMetric* > m_Metrics; std::vector< std::vector< long > > m_OutFiberIndices; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkTractClusteringFilter.cpp" #endif #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractDistanceFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractDistanceFilter.cpp new file mode 100644 index 0000000000..6245c6bb4e --- /dev/null +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkTractDistanceFilter.cpp @@ -0,0 +1,181 @@ +/*=================================================================== + +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. + +===================================================================*/ +#include "itkTractDistanceFilter.h" + +#define _USE_MATH_DEFINES +#include +#include + +namespace itk{ + +TractDistanceFilter::TractDistanceFilter() + : m_NumPoints(12) + , disp(0) +{ + +} + +TractDistanceFilter::~TractDistanceFilter() +{ + for (auto m : m_Metrics) + delete m; +} + +std::vector TractDistanceFilter::GetIndices() const +{ + return m_Indices; +} + +std::vector TractDistanceFilter::GetDistances() const +{ + return m_Distances; +} + +void TractDistanceFilter::SetTracts2(const std::vector &Tracts2) +{ + m_Tracts2 = Tracts2; +} + +void TractDistanceFilter::SetTracts1(const std::vector &Tracts1) +{ + m_Tracts1 = Tracts1; +} + +void TractDistanceFilter::SetMetrics(const std::vector &Metrics) +{ + m_Metrics = Metrics; +} + +std::vector > TractDistanceFilter::ResampleFibers(mitk::FiberBundle::Pointer tractogram) +{ + std::streambuf *old = cout.rdbuf(); // <-- save + std::stringstream ss; + std::cout.rdbuf (ss.rdbuf()); // <-- redirect + mitk::FiberBundle::Pointer temp_fib = tractogram->GetDeepCopy(); + temp_fib->ResampleToNumPoints(m_NumPoints); + + std::vector< vnl_matrix > out_fib; + + for (int i=0; iGetFiberPolyData()->GetNumberOfCells(); i++) + { + vtkCell* cell = temp_fib->GetFiberPolyData()->GetCell(i); + int numPoints = cell->GetNumberOfPoints(); + vtkPoints* points = cell->GetPoints(); + + vnl_matrix streamline; + streamline.set_size(3, m_NumPoints); + streamline.fill(0.0); + + for (int j=0; jGetPoint(j, cand); + + vnl_vector_fixed< float, 3 > candV; + candV[0]=cand[0]; candV[1]=cand[1]; candV[2]=cand[2]; + streamline.set_column(j, candV); + } + + out_fib.push_back(streamline); + } + + std::cout.rdbuf (old); // <-- restore + return out_fib; +} + +float TractDistanceFilter::calc_distance(const std::vector >& T1, const std::vector >& T2) +{ + float distance = 0; + for (auto metric : m_Metrics) + { + unsigned int r = 0; + for (auto f1 : T1) + { + unsigned int c = 0; + float min_d = 99999; + for (auto f2 : T2) + { +#pragma omp critical + ++disp; + bool flipped = false; + float d = metric->CalculateDistance(f1, f2, flipped); + if (d < min_d) + min_d = d; + ++c; + } + distance += min_d; + ++r; + } + } + distance /= (T1.size() * m_Metrics.size()); + return distance; +} + +void TractDistanceFilter::GenerateData() +{ + if (m_Metrics.empty()) + { + mitkThrow() << "No metric selected!"; + return; + } + + m_Indices.clear(); + m_Distances.clear(); + std::vector>> T1; + std::vector>> T2; + + unsigned int num_fibs1 = 0; + unsigned int num_fibs2 = 0; + for (auto t : m_Tracts1) + { + T1.push_back(ResampleFibers(t)); + num_fibs1 += T1.back().size(); + } + for (auto t : m_Tracts2) + { + T2.push_back(ResampleFibers(t)); + num_fibs2 += T2.back().size(); + } + + disp.restart(m_Metrics.size() * num_fibs1 * num_fibs2); + + m_Indices.resize(T1.size()); + m_Distances.resize(T1.size()); +#pragma omp parallel for + for (unsigned int i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace itk{ + +/** +* \brief */ + +class TractDistanceFilter : public ProcessObject +{ +public: + + typedef TractDistanceFilter Self; + typedef ProcessObject Superclass; + typedef SmartPointer< Self > Pointer; + typedef SmartPointer< const Self > ConstPointer; + typedef itk::Image< float, 3 > FloatImageType; + typedef itk::Image< unsigned char, 3 > UcharImageType; + + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + itkTypeMacro( TractDistanceFilter, ProcessObject ) + + itkSetMacro(NumPoints, unsigned int) ///< Fibers are resampled to the specified number of points. If scalar maps are used, a larger number of points is recommended. + + virtual void Update() override{ + this->GenerateData(); + } + + void SetMetrics(const std::vector &Metrics); + + void SetTracts1(const std::vector &Tracts1); + + void SetTracts2(const std::vector &Tracts2); + + std::vector GetDistances() const; + + std::vector GetIndices() const; + +protected: + + void GenerateData() override; + std::vector< vnl_matrix > ResampleFibers(FiberBundle::Pointer tractogram); + float calc_distance(const std::vector > &T1, const std::vector > &T2); + + TractDistanceFilter(); + virtual ~TractDistanceFilter(); + + std::vector< FiberBundle::Pointer > m_Tracts1; + std::vector< FiberBundle::Pointer > m_Tracts2; + unsigned int m_NumPoints; + std::vector< mitk::ClusteringMetric* > m_Metrics; + std::vector< float > m_Distances; + std::vector< int > m_Indices; + boost::progress_display disp; +}; +} + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkTractDistanceFilter.cpp" +#endif + +#endif diff --git a/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/CMakeLists.txt b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/CMakeLists.txt index 6404d2892b..f3d6510284 100755 --- a/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/CMakeLists.txt +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/CMakeLists.txt @@ -1,48 +1,49 @@ 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^^MitkFiberTracking Sift2WeightCopy^^MitkFiberTracking FiberExtraction^^MitkFiberTracking FiberExtractionRoi^^MitkFiberTracking FiberProcessing^^MitkFiberTracking FitFibersToImage^^MitkFiberTracking FiberDirectionExtraction^^MitkFiberTracking FiberJoin^^MitkFiberTracking FiberClustering^^MitkFiberTracking GetOverlappingTracts^^MitkFiberTracking TractDensityFilter^^MitkFiberTracking + TractDistance^^MitkFiberTracking ) 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 MitkCore MitkDiffusionCore ${dependencies_list} PACKAGE_DEPENDS ITK ) endforeach() if(EXECUTABLE_IS_ENABLED) MITK_INSTALL_TARGETS(EXECUTABLES ${EXECUTABLE_TARGET}) endif() endif() diff --git a/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberClustering.cpp b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberClustering.cpp index af51228ca2..bf5106f096 100644 --- a/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberClustering.cpp +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberClustering.cpp @@ -1,251 +1,273 @@ /*=================================================================== 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. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include +#include + +typedef itksys::SystemTools ist; 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 Spatially cluster fibers */ int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Fiber Clustering"); parser.setCategory("Fiber Processing"); parser.setContributor("MIC"); parser.setArgumentPrefix("--", "-"); parser.addArgument("", "i", mitkCommandLineParser::InputFile, "Input:", "input fiber bundle (.fib, .trk, .tck)", us::Any(), false); parser.addArgument("", "o", mitkCommandLineParser::OutputDirectory, "Output:", "output root", us::Any(), false); parser.addArgument("cluster_size", "", mitkCommandLineParser::Int, "Cluster size:", "", 10); parser.addArgument("fiber_points", "", mitkCommandLineParser::Int, "Fiber points:", "", 12); parser.addArgument("min_fibers", "", mitkCommandLineParser::Int, "Min. fibers per cluster:", "", 1); parser.addArgument("max_clusters", "", mitkCommandLineParser::Int, "Max. clusters:", ""); parser.addArgument("merge_clusters", "", mitkCommandLineParser::Float, "Merge clusters:", "", -1.0); parser.addArgument("output_centroids", "", mitkCommandLineParser::Bool, "Output centroids:", ""); + parser.addArgument("only_centroids", "", mitkCommandLineParser::Bool, "Output only centroids:", ""); + parser.addArgument("merge_centroids", "", mitkCommandLineParser::Bool, "Merge centroids:", ""); parser.addArgument("metrics", "", mitkCommandLineParser::StringList, "Metrics:", "EU_MEAN (default), EU_STD, EU_MAX, ANAT, MAP, LENGTH"); parser.addArgument("metric_weights", "", mitkCommandLineParser::StringList, "Metric weights:", "add one float weight for each used metric"); parser.addArgument("input_centroids", "", mitkCommandLineParser::String, "Input centroids:", ""); parser.addArgument("scalar_map", "", mitkCommandLineParser::String, "Scalar map:", ""); parser.addArgument("parcellation", "", mitkCommandLineParser::String, "Parcellation:", ""); parser.addArgument("file_ending", "", mitkCommandLineParser::String, "File ending:", ""); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) return EXIT_FAILURE; std::string inFileName = us::any_cast(parsedArgs["i"]); std::string out_root = us::any_cast(parsedArgs["o"]); + bool only_centroids = false; + if (parsedArgs.count("only_centroids")) + only_centroids = us::any_cast(parsedArgs["only_centroids"]); + + bool merge_centroids = false; + if (parsedArgs.count("merge_centroids")) + merge_centroids = us::any_cast(parsedArgs["merge_centroids"]); + int cluster_size = 10; if (parsedArgs.count("cluster_size")) cluster_size = us::any_cast(parsedArgs["cluster_size"]); int fiber_points = 12; if (parsedArgs.count("fiber_points")) fiber_points = us::any_cast(parsedArgs["fiber_points"]); int min_fibers = 1; if (parsedArgs.count("min_fibers")) min_fibers = us::any_cast(parsedArgs["min_fibers"]); int max_clusters = 0; if (parsedArgs.count("max_clusters")) max_clusters = us::any_cast(parsedArgs["max_clusters"]); float merge_clusters = -1.0; if (parsedArgs.count("merge_clusters")) merge_clusters = us::any_cast(parsedArgs["merge_clusters"]); bool output_centroids = false; if (parsedArgs.count("output_centroids")) output_centroids = us::any_cast(parsedArgs["output_centroids"]); std::vector< std::string > metric_strings = {"EU_MEAN"}; if (parsedArgs.count("metrics")) metric_strings = us::any_cast(parsedArgs["metrics"]); std::vector< std::string > metric_weights = {"1.0"}; if (parsedArgs.count("metric_weights")) metric_weights = us::any_cast(parsedArgs["metric_weights"]); std::string input_centroids = ""; if (parsedArgs.count("input_centroids")) input_centroids = us::any_cast(parsedArgs["input_centroids"]); std::string scalar_map = ""; if (parsedArgs.count("scalar_map")) scalar_map = us::any_cast(parsedArgs["scalar_map"]); std::string parcellation = ""; if (parsedArgs.count("parcellation")) parcellation = us::any_cast(parsedArgs["parcellation"]); std::string file_ending = ".fib"; if (parsedArgs.count("file_ending")) file_ending = us::any_cast(parsedArgs["file_ending"]); if (metric_strings.size()!=metric_weights.size()) { MITK_INFO << "Each metric needs an associated metric weight!"; return EXIT_FAILURE; } try { typedef itk::Image< float, 3 > FloatImageType; typedef itk::Image< short, 3 > ShortImageType; mitk::FiberBundle::Pointer fib = LoadFib(inFileName); float max_d = 0; int i=1; std::vector< float > distances; while (max_d < fib->GetGeometry()->GetDiagonalLength()/2) { distances.push_back(cluster_size*i); max_d = cluster_size*i; ++i; } itk::TractClusteringFilter::Pointer clusterer = itk::TractClusteringFilter::New(); clusterer->SetDistances(distances); clusterer->SetTractogram(fib); if (input_centroids!="") { mitk::FiberBundle::Pointer in_centroids = LoadFib(input_centroids); clusterer->SetInCentroids(in_centroids); } std::vector< mitk::ClusteringMetric* > metrics; int mc = 0; for (auto m : metric_strings) { float w = boost::lexical_cast(metric_weights.at(mc)); MITK_INFO << "Metric: " << m << " (w=" << w << ")"; if (m=="EU_MEAN") metrics.push_back({new mitk::ClusteringMetricEuclideanMean()}); else if (m=="EU_STD") metrics.push_back({new mitk::ClusteringMetricEuclideanStd()}); else if (m=="EU_MAX") metrics.push_back({new mitk::ClusteringMetricEuclideanMax()}); else if (m=="ANGLES") metrics.push_back({new mitk::ClusteringMetricInnerAngles()}); else if (m=="LENGTH") metrics.push_back({new mitk::ClusteringMetricLength()}); else if (m=="MAP" && scalar_map!="") { mitk::Image::Pointer mitk_map = mitk::IOUtil::Load(scalar_map); if (mitk_map->GetDimension()==3) { FloatImageType::Pointer itk_map = FloatImageType::New(); mitk::CastToItkImage(mitk_map, itk_map); mitk::ClusteringMetricScalarMap* metric = new mitk::ClusteringMetricScalarMap(); metric->SetImages({itk_map}); metric->SetScale(distances.at(0)); metrics.push_back(metric); } } else if (m=="ANAT" && parcellation!="") { mitk::Image::Pointer mitk_map = mitk::IOUtil::Load(parcellation); if (mitk_map->GetDimension()==3) { ShortImageType::Pointer itk_map = ShortImageType::New(); mitk::CastToItkImage(mitk_map, itk_map); mitk::ClusteringMetricAnatomic* metric = new mitk::ClusteringMetricAnatomic(); metric->SetParcellations({itk_map}); metrics.push_back(metric); } } metrics.back()->SetScale(w); mc++; } if (metrics.empty()) { MITK_INFO << "No metric selected!"; return EXIT_FAILURE; } clusterer->SetMetrics(metrics); clusterer->SetMergeDuplicateThreshold(merge_clusters); clusterer->SetNumPoints(fiber_points); clusterer->SetMaxClusters(max_clusters); clusterer->SetMinClusterSize(min_fibers); clusterer->Update(); std::vector tracts = clusterer->GetOutTractograms(); std::vector centroids = clusterer->GetOutCentroids(); MITK_INFO << "Saving clusters"; std::streambuf *old = cout.rdbuf(); // <-- save std::stringstream ss; std::cout.rdbuf (ss.rdbuf()); // <-- redirect - unsigned int c = 0; - for (auto f : tracts) - { - mitk::IOUtil::Save(f, out_root + "Cluster_" + boost::lexical_cast(c) + file_ending); - if (output_centroids) - mitk::IOUtil::Save(centroids.at(c), out_root + "Centroid_" + boost::lexical_cast(c) + file_ending); - ++c; + if (!only_centroids) + for (unsigned int i=0; i(i) + file_ending); + + + if (output_centroids && !merge_centroids) + { + for (unsigned int i=0; i(i) + file_ending); } + else if (output_centroids) + { + mitk::FiberBundle::Pointer centroid = mitk::FiberBundle::New(); + centroid = centroid->AddBundles(centroids); + mitk::IOUtil::Save(centroid, out_root + ist::GetFilenameWithoutExtension(inFileName) + "_Centroids" + file_ending); + } + std::cout.rdbuf (old); // <-- restore } catch (itk::ExceptionObject e) { std::cout << e; 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/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/TractDistance.cpp b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/TractDistance.cpp new file mode 100644 index 0000000000..a82028753d --- /dev/null +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/TractDistance.cpp @@ -0,0 +1,182 @@ +/*=================================================================== + +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. + +===================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef itksys::SystemTools ist; + +std::vector< std::string > get_file_list(const std::string& path, std::vector< std::string > extensions={".fib", ".trk"}) +{ + std::vector< std::string > file_list; + itk::Directory::Pointer dir = itk::Directory::New(); + + if (dir->Load(path.c_str())) + { + int n = dir->GetNumberOfFiles(); + for (int r = 0; r < n; r++) + { + const char *filename = dir->GetFile(r); + std::string ext = ist::GetFilenameExtension(filename); + for (auto e : extensions) + { + if (ext==e) + { + file_list.push_back(path + '/' + filename); + break; + } + } + } + } + std::sort(file_list.begin(), file_list.end()); + return file_list; +} + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + + parser.setTitle("Tract Distance"); + parser.setCategory("Fiber Processing"); + parser.setContributor("MIC"); + + parser.setArgumentPrefix("--", "-"); + parser.addArgument("", "i1", mitkCommandLineParser::String, "Input Folder 1:", "input tracts folder 1", us::Any(), false); + parser.addArgument("", "i2", mitkCommandLineParser::String, "Input Folder 2:", "input tracts folder 2", us::Any(), false); + parser.addArgument("", "o", mitkCommandLineParser::String, "Output:", "output logfile", us::Any(), false); + + parser.addArgument("fiber_points", "", mitkCommandLineParser::Int, "Fiber points:", "", 12); + parser.addArgument("metrics", "", mitkCommandLineParser::StringList, "Metrics:", "EU_MEAN (default), EU_STD, EU_MAX"); + parser.addArgument("metric_weights", "", mitkCommandLineParser::StringList, "Metric weights:", "add one float weight for each used metric"); + + std::map parsedArgs = parser.parseArguments(argc, argv); + if (parsedArgs.size()==0) + return EXIT_FAILURE; + + std::string t1_folder = us::any_cast(parsedArgs["i1"]); + std::string t2_folder = us::any_cast(parsedArgs["i2"]); + std::string out_file = us::any_cast(parsedArgs["o"]); + + int fiber_points = 12; + if (parsedArgs.count("fiber_points")) + fiber_points = us::any_cast(parsedArgs["fiber_points"]); + + std::vector< std::string > metric_strings = {"EU_MEAN"}; + if (parsedArgs.count("metrics")) + metric_strings = us::any_cast(parsedArgs["metrics"]); + + std::vector< std::string > metric_weights = {"1.0"}; + if (parsedArgs.count("metric_weights")) + metric_weights = us::any_cast(parsedArgs["metric_weights"]); + + if (metric_strings.size()!=metric_weights.size()) + { + MITK_INFO << "Each metric needs an associated metric weight!"; + return EXIT_FAILURE; + } + + try + { + std::streambuf *old = cout.rdbuf(); // <-- save + std::stringstream ss; + std::cout.rdbuf (ss.rdbuf()); // <-- redirect + std::vector< mitk::FiberBundle::Pointer > tractograms1; + std::vector< mitk::FiberBundle::Pointer > tractograms2; + + auto t1_files = get_file_list(t1_folder, {".fib",".trk",".tck"}); + for (auto f : t1_files) + { + mitk::FiberBundle::Pointer fib = mitk::IOUtil::Load(f); + tractograms1.push_back(fib); + } + auto t2_files = get_file_list(t2_folder, {".fib",".trk",".tck"}); + for (auto f : t2_files) + { + mitk::FiberBundle::Pointer fib = mitk::IOUtil::Load(f); + tractograms2.push_back(fib); + } + + std::cout.rdbuf (old); // <-- restore + MITK_INFO << "Loaded " << tractograms1.size() << " source tractograms."; + MITK_INFO << "Loaded " << tractograms2.size() << " target tractograms."; + + itk::TractDistanceFilter::Pointer distance_calculator = itk::TractDistanceFilter::New(); + distance_calculator->SetNumPoints(fiber_points); + distance_calculator->SetTracts1(tractograms1); + distance_calculator->SetTracts2(tractograms2); + + std::vector< mitk::ClusteringMetric* > metrics; + int mc = 0; + for (auto m : metric_strings) + { + float w = boost::lexical_cast(metric_weights.at(mc)); + MITK_INFO << "Metric: " << m << " (w=" << w << ")"; + if (m=="EU_MEAN") + metrics.push_back({new mitk::ClusteringMetricEuclideanMean()}); + else if (m=="EU_STD") + metrics.push_back({new mitk::ClusteringMetricEuclideanStd()}); + else if (m=="EU_MAX") + metrics.push_back({new mitk::ClusteringMetricEuclideanMax()}); + metrics.back()->SetScale(w); + mc++; + } + + if (metrics.empty()) + { + MITK_INFO << "No metric selected!"; + return EXIT_FAILURE; + } + + distance_calculator->SetMetrics(metrics); + distance_calculator->Update(); + MITK_INFO << "Distances:"; + auto distances = distance_calculator->GetDistances(); + auto indices = distance_calculator->GetIndices(); + + ofstream logfile; + logfile.open (out_file); + for (unsigned int i=0; i FiberBundle IODataStructures/FiberBundle/mitkFiberBundle.h IODataStructures/FiberBundle/mitkTrackvis.h IODataStructures/mitkFiberfoxParameters.h IODataStructures/mitkTractographyForest.h # Algorithms Algorithms/itkTractDensityImageFilter.h Algorithms/itkTractsToFiberEndingsImageFilter.h Algorithms/itkTractsToRgbaImageFilter.h Algorithms/itkTractsToVectorImageFilter.h Algorithms/itkEvaluateDirectionImagesFilter.h Algorithms/itkEvaluateTractogramDirectionsFilter.h Algorithms/itkFiberCurvatureFilter.h Algorithms/itkFitFibersToImageFilter.h Algorithms/itkTractClusteringFilter.h + Algorithms/itkTractDistanceFilter.h Algorithms/itkFiberExtractionFilter.h Algorithms/itkTdiToVolumeFractionFilter.h # Tractography Algorithms/TrackingHandlers/mitkTrackingDataHandler.h Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.h Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.h Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.h Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.h Algorithms/itkGibbsTrackingFilter.h Algorithms/itkStochasticTractographyFilter.h Algorithms/GibbsTracking/mitkParticle.h Algorithms/GibbsTracking/mitkParticleGrid.h Algorithms/GibbsTracking/mitkMetropolisHastingsSampler.h Algorithms/GibbsTracking/mitkSimpSamp.h Algorithms/GibbsTracking/mitkEnergyComputer.h Algorithms/GibbsTracking/mitkGibbsEnergyComputer.h Algorithms/GibbsTracking/mitkSphereInterpolator.h Algorithms/GibbsTracking/mitkFiberBuilder.h Algorithms/itkStreamlineTrackingFilter.h # Clustering Algorithms/ClusteringMetrics/mitkClusteringMetric.h Algorithms/ClusteringMetrics/mitkClusteringMetricEuclideanMean.h Algorithms/ClusteringMetrics/mitkClusteringMetricEuclideanMax.h Algorithms/ClusteringMetrics/mitkClusteringMetricEuclideanStd.h Algorithms/ClusteringMetrics/mitkClusteringMetricAnatomic.h Algorithms/ClusteringMetrics/mitkClusteringMetricScalarMap.h Algorithms/ClusteringMetrics/mitkClusteringMetricInnerAngles.h Algorithms/ClusteringMetrics/mitkClusteringMetricLength.h # Fiberfox Fiberfox/itkFibersFromPlanarFiguresFilter.h Fiberfox/itkTractsToDWIImageFilter.h Fiberfox/itkKspaceImageFilter.h Fiberfox/itkDftImageFilter.h Fiberfox/itkFieldmapGeneratorFilter.h Fiberfox/SignalModels/mitkDiffusionSignalModel.h Fiberfox/SignalModels/mitkTensorModel.h Fiberfox/SignalModels/mitkBallModel.h Fiberfox/SignalModels/mitkDotModel.h Fiberfox/SignalModels/mitkAstroStickModel.h Fiberfox/SignalModels/mitkStickModel.h Fiberfox/SignalModels/mitkRawShModel.h Fiberfox/SignalModels/mitkDiffusionNoiseModel.h Fiberfox/SignalModels/mitkRicianNoiseModel.h Fiberfox/SignalModels/mitkChiSquareNoiseModel.h Fiberfox/Sequences/mitkAcquisitionType.h Fiberfox/Sequences/mitkSingleShotEpi.h Fiberfox/Sequences/mitkCartesianReadout.h ) set(RESOURCE_FILES # Binary directory resources FiberTrackingLUTBaryCoords.bin FiberTrackingLUTIndices.bin ) diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberClusteringViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberClusteringViewControls.ui index 22d11ae3eb..b9edec8bfc 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberClusteringViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberClusteringViewControls.ui @@ -1,524 +1,524 @@ - + QmitkFiberClusteringViewControls 0 0 474 - 683 + 695 Form QCommandLinkButton:disabled { border: none; } QGroupBox { background-color: transparent; } 25 Qt::Vertical 20 40 Metrics 6 6 6 6 Weighting factor for metric values. 1 999.000000000000000 1.000000000000000 Euclidean true Weighting factor for metric values. 1 999.000000000000000 1.000000000000000 Euclidean STDEV Weighting factor for metric values. 1 999.000000000000000 1.000000000000000 Euclidean Maximum Weighting factor for metric values. 1 999.000000000000000 1.000000000000000 Weighting factor for metric values. 1 999.000000000000000 1.000000000000000 Inner Angles Weighting factor for metric values. 1 999.000000000000000 1.000000000000000 QFrame::NoFrame QFrame::Raised 0 0 0 0 6 Distance is based on the selected parcellation. Anatomical Distance is based on the selected parcellation. Weighting factor for metric values. 1 999.000000000000000 30.000000000000000 QFrame::NoFrame QFrame::Raised 0 0 0 0 Distance is based on the scalar map values along the tract. Scalar Map Distance is based on the scalar map values along the tract. Streamline Length Input Data 6 6 6 6 Input Centroids: If set, the input tractogram is clustered around the input centroids and no new clusters are created. false 0 0 200 16777215 11 - + Start Tractogram: - + Parameters Only output clusters with ate least the specified number of fibers. 1 9999999 50 Only output the N largest clusters. Zero means no limit. 99999999 10 Min. Fibers per Cluster: Max. Clusters: Fiber Points: Merge duplicate clusters withthe specified distance threshold. If threshold is < 0, the threshold is set to half of the specified cluster size. -1.000000000000000 99999.000000000000000 - 0.000000000000000 + -1.000000000000000 Cluster Size: Cluster size in mm. 1 9999999 20 Fibers are resampled to the desired number of points for clustering. Smaller is faster but less accurate. 2 9999999 12 Merge Duplicate Clusters: Output Centroids: - + QmitkDataStorageComboBox QComboBox
QmitkDataStorageComboBox.h
QmitkDataStorageComboBoxWithSelectNone QComboBox
QmitkDataStorageComboBoxWithSelectNone.h
- - -
\ No newline at end of file + + +