diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.cpp index 85284a11f0..e73eff0f05 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.cpp @@ -1,268 +1,380 @@ /*=================================================================== 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 __itkFiberExtractionFilter_cpp #define __itkFiberExtractionFilter_cpp #include "itkFiberExtractionFilter.h" #define _USE_MATH_DEFINES #include #include #include namespace itk{ template< class PixelType > FiberExtractionFilter< PixelType >::FiberExtractionFilter() : m_DontResampleFibers(false) , m_Mode(MODE::OVERLAP) , m_InputType(INPUT::SCALAR_MAP) , m_BothEnds(true) , m_OverlapFraction(0.8) , m_NoNegatives(false) , m_NoPositives(false) , m_Interpolate(false) , m_Threshold(0.5) , m_Labels({1}) + , m_SkipSelfConnections(false) + , m_SplitLabels(false) + , m_MinFibersPerTract(0) { m_Interpolator = itk::LinearInterpolateImageFunction< itk::Image< PixelType, 3 >, float >::New(); } template< class PixelType > FiberExtractionFilter< PixelType >::~FiberExtractionFilter() { } template< class PixelType > void FiberExtractionFilter< PixelType >::SetRoiImages(const std::vector &rois) { for (auto roi : rois) { if (roi==nullptr) { MITK_INFO << "ROI image is NULL!"; return; } } m_RoiImages = rois; } template< class PixelType > mitk::FiberBundle::Pointer FiberExtractionFilter< PixelType >::CreateFib(std::vector< long >& ids) { vtkSmartPointer weights = vtkSmartPointer::New(); vtkSmartPointer pTmp = m_InputFiberBundle->GeneratePolyDataByIds(ids, weights); mitk::FiberBundle::Pointer fib = mitk::FiberBundle::New(pTmp); fib->SetFiberWeights(weights); return fib; } template< class PixelType > -bool FiberExtractionFilter< PixelType >::IsPositive(const itk::Point& itkP, itk::Image* image) +bool FiberExtractionFilter< PixelType >::IsPositive(const itk::Point& itkP) { - m_Interpolator->SetInputImage(image); if( m_InputType == INPUT::SCALAR_MAP ) return mitk::imv::IsInsideMask(itkP, m_Interpolate, m_Interpolator, m_Threshold); else if( m_InputType == INPUT::LABEL_MAP ) { auto val = mitk::imv::GetImageValue(itkP, m_Interpolate, m_Interpolator); for (auto l : m_Labels) if (l==val) return true; } else mitkThrow() << "No valid input type selected!"; return false; } +template< class PixelType > +std::vector< std::pair > FiberExtractionFilter< PixelType >::GetPositiveLabels() const +{ + return m_PositiveLabels; +} + template< class PixelType > void FiberExtractionFilter< PixelType >::ExtractOverlap(mitk::FiberBundle::Pointer fib) { MITK_INFO << "Extracting fibers (min. overlap " << m_OverlapFraction << ")"; vtkSmartPointer polydata = fib->GetFiberPolyData(); std::vector< std::vector< long > > positive_ids; positive_ids.resize(m_RoiImages.size()); std::vector< long > negative_ids; // fibers not overlapping with ANY mask boost::progress_display disp(m_InputFiberBundle->GetNumFibers()); for (int i=0; iGetNumFibers(); i++) { ++disp; vtkCell* cell = polydata->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); bool positive = false; for (unsigned int m=0; mSetInputImage(roi); int inside = 0; int outside = 0; for (int j=0; jGetPoint(j); itk::Point itkP; itkP[0] = p[0]; itkP[1] = p[1]; itkP[2] = p[2]; - if ( IsPositive(itkP, roi) ) + if ( IsPositive(itkP) ) inside++; else outside++; if ((float)inside/numPoints > m_OverlapFraction) { positive = true; positive_ids[m].push_back(i); break; } } } if (!positive) negative_ids.push_back(i); } if (!m_NoNegatives) m_Negatives.push_back(CreateFib(negative_ids)); if (!m_NoPositives) for (auto ids : positive_ids) - m_Positives.push_back(CreateFib(ids)); + if (ids.size()>=m_MinFibersPerTract) + m_Positives.push_back(CreateFib(ids)); } template< class PixelType > void FiberExtractionFilter< PixelType >::ExtractEndpoints(mitk::FiberBundle::Pointer fib) { MITK_INFO << "Extracting fibers (endpoints in mask)"; vtkSmartPointer polydata = fib->GetFiberPolyData(); std::vector< std::vector< long > > positive_ids; positive_ids.resize(m_RoiImages.size()); std::vector< long > negative_ids; // fibers not overlapping with ANY mask boost::progress_display disp(m_InputFiberBundle->GetNumFibers()); for (int i=0; iGetNumFibers(); i++) { ++disp; vtkCell* cell = polydata->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); bool positive = false; if (numPoints>1) for (unsigned int m=0; mSetInputImage(roi); int inside = 0; // check first fiber point { double* p = points->GetPoint(0); itk::Point itkP; itkP[0] = p[0]; itkP[1] = p[1]; itkP[2] = p[2]; - if ( IsPositive(itkP, roi) ) + if ( IsPositive(itkP) ) inside++; } // check second fiber point { double* p = points->GetPoint(numPoints-1); itk::Point itkP; itkP[0] = p[0]; itkP[1] = p[1]; itkP[2] = p[2]; - itk::Index<3> idx; - roi->TransformPhysicalPointToIndex(itkP, idx); - if ( IsPositive(itkP, roi) ) + + if ( IsPositive(itkP) ) inside++; } if (inside==2 || (inside==1 && !m_BothEnds)) { positive = true; positive_ids[m].push_back(i); } } if (!positive) negative_ids.push_back(i); } if (!m_NoNegatives) m_Negatives.push_back(CreateFib(negative_ids)); if (!m_NoPositives) for (auto ids : positive_ids) - m_Positives.push_back(CreateFib(ids)); + if (ids.size()>=m_MinFibersPerTract) + m_Positives.push_back(CreateFib(ids)); +} + +template< class PixelType > +void FiberExtractionFilter< PixelType >::ExtractLabels(mitk::FiberBundle::Pointer fib) +{ + MITK_INFO << "Extracting fibers by labels"; + vtkSmartPointer polydata = fib->GetFiberPolyData(); + + std::vector< std::map< unsigned int, std::vector< long > > > positive_ids; + positive_ids.resize(m_RoiImages.size()); + + std::vector< long > negative_ids; // fibers not overlapping with ANY label + + boost::progress_display disp(m_InputFiberBundle->GetNumFibers()); + for (int i=0; iGetNumFibers(); i++) + { + ++disp; + vtkCell* cell = polydata->GetCell(i); + int numPoints = cell->GetNumberOfPoints(); + vtkPoints* points = cell->GetPoints(); + + bool positive = false; + if (numPoints>1) + for (unsigned int m=0; mSetInputImage(roi); + + int inside = 0; + + // check first fiber point + short label1 = -1; + { + double* p = points->GetPoint(0); + itk::Point itkP; + itkP[0] = p[0]; itkP[1] = p[1]; itkP[2] = p[2]; + + label1 = mitk::imv::GetImageValue(itkP, m_Interpolate, m_Interpolator); + for (auto l : m_Labels) + { + if (l==label1) + { + inside++; + break; + } + } + } + + // check second fiber point + short label2 = -1; + { + double* p = points->GetPoint(numPoints-1); + itk::Point itkP; + itkP[0] = p[0]; itkP[1] = p[1]; itkP[2] = p[2]; + + label2 = mitk::imv::GetImageValue(itkP, m_Interpolate, m_Interpolator); + for (auto l : m_Labels) + { + if (l==label2) + { + inside++; + break; + } + } + } + + if ( inside==2 && (!m_SkipSelfConnections || label1!=label2) ) + { + unsigned int key; + if (label1 >( key, {i}) ); + else + positive_ids[m][key].push_back(i); + } + } + if (!positive) + negative_ids.push_back(i); + } + + if (!m_NoNegatives) + m_Negatives.push_back(CreateFib(negative_ids)); + if (!m_NoPositives) + for (auto labels : positive_ids) + for (auto tuple : labels) + { + if (tuple.second.size()>=m_MinFibersPerTract) + { + m_Positives.push_back(CreateFib(tuple.second)); + m_PositiveLabels.push_back(std::pair< unsigned int, unsigned int >( tuple.first/m_Labels.size(), tuple.first%m_Labels.size() )); + } + } } template< class PixelType > void FiberExtractionFilter< PixelType >::SetLabels(const std::vector &Labels) { m_Labels = Labels; } template< class PixelType > std::vector FiberExtractionFilter< PixelType >::GetNegatives() const { return m_Negatives; } template< class PixelType > std::vector FiberExtractionFilter< PixelType >::GetPositives() const { return m_Positives; } template< class PixelType > void FiberExtractionFilter< PixelType >::GenerateData() { mitk::FiberBundle::Pointer fib = m_InputFiberBundle; if (fib->GetNumFibers()<=0) { MITK_INFO << "No fibers in tractogram!"; return; } if (m_Mode==MODE::OVERLAP && !m_DontResampleFibers) { float minSpacing = 1; for (auto mask : m_RoiImages) { for (int i=0; i<3; ++i) if(mask->GetSpacing()[i]GetSpacing()[i]; } fib = m_InputFiberBundle->GetDeepCopy(); fib->ResampleLinear(minSpacing/5); } if (m_Mode == MODE::OVERLAP) ExtractOverlap(fib); else if (m_Mode == MODE::ENDPOINTS) - ExtractEndpoints(fib); + { + if (m_InputType==INPUT::LABEL_MAP && m_BothEnds && m_SplitLabels) + ExtractLabels(fib); + else + ExtractEndpoints(fib); + } } } #endif // __itkFiberExtractionFilter_cpp diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.h b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.h index f3b729f238..243c359e63 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.h +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.h @@ -1,112 +1,121 @@ /*=================================================================== 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 itkFiberExtractionFilter_h #define itkFiberExtractionFilter_h // MITK #include // ITK #include #include namespace itk{ /** -* \brief Extract streamlines from tractograms using binary images */ +* \brief Extract streamlines from tractograms using ROI images */ template< class PixelType > class FiberExtractionFilter : public ProcessObject { public: enum MODE { OVERLAP, ENDPOINTS }; enum INPUT { SCALAR_MAP, ///< In this case, positive means roi image vlaue > threshold LABEL_MAP ///< In this case, positive means roi image value in labels vector }; typedef FiberExtractionFilter Self; typedef ProcessObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; typedef itk::Image< PixelType , 3> ItkInputImgType; itkFactorylessNewMacro(Self) itkCloneMacro(Self) itkTypeMacro( FiberExtractionFilter, ProcessObject ) virtual void Update() override{ this->GenerateData(); } itkSetMacro( InputFiberBundle, mitk::FiberBundle::Pointer ) - itkSetMacro( Mode, MODE ) - itkSetMacro( InputType, INPUT ) - itkSetMacro( BothEnds, bool ) - itkSetMacro( OverlapFraction, float ) - itkSetMacro( DontResampleFibers, bool ) - itkSetMacro( NoNegatives, bool ) - itkSetMacro( NoPositives, bool ) - itkSetMacro( Interpolate, bool ) - itkSetMacro( Threshold, float ) + itkSetMacro( Mode, MODE ) ///< Overlap or endpoints + itkSetMacro( InputType, INPUT ) ///< Scalar map or label image + itkSetMacro( BothEnds, bool ) ///< Both streamline ends need to be inside of the ROI for the streamline to be considered positive + itkSetMacro( OverlapFraction, float ) ///< Necessary fraction of streamline points inside the ROI for the streamline to be considered positive + itkSetMacro( DontResampleFibers, bool ) ///< Don't resample input fibers to ensure coverage + itkSetMacro( NoNegatives, bool ) ///< Don't create output tractograms from negative streamlines (save the computation) + itkSetMacro( NoPositives, bool ) ///< Don't create output tractograms from positive streamlines (save the computation) + itkSetMacro( Interpolate, bool ) ///< Interpolate input ROI image + itkSetMacro( Threshold, float ) ///< Threshold on input ROI image value to determine positives/negatives + itkSetMacro( SkipSelfConnections, bool ) ///< Ignore streamlines between two identical labels + itkSetMacro( SplitLabels, bool ) ///< Output a separate tractogram for each label-->label tract + itkSetMacro( MinFibersPerTract, unsigned int ) ///< Discard positives with less fibers void SetRoiImages(const std::vector< ItkInputImgType* > &rois); void SetLabels(const std::vector &Labels); - std::vector GetPositives() const; - std::vector GetNegatives() const; + std::vector GetPositives() const; ///< Get positive tracts (filtered by the input ROIs) + std::vector GetNegatives() const; ///< Get negative tracts (not filtered by the ROIs) + std::vector > GetPositiveLabels() const; ///< In case of label extraction, this vector contains the labels corresponding to the positive tracts protected: void GenerateData() override; FiberExtractionFilter(); virtual ~FiberExtractionFilter(); mitk::FiberBundle::Pointer CreateFib(std::vector< long >& ids); void ExtractOverlap(mitk::FiberBundle::Pointer fib); void ExtractEndpoints(mitk::FiberBundle::Pointer fib); - bool IsPositive(const itk::Point& itkP, itk::Image* image); + void ExtractLabels(mitk::FiberBundle::Pointer fib); + bool IsPositive(const itk::Point& itkP); mitk::FiberBundle::Pointer m_InputFiberBundle; std::vector< mitk::FiberBundle::Pointer > m_Positives; std::vector< mitk::FiberBundle::Pointer > m_Negatives; std::vector< ItkInputImgType* > m_RoiImages; bool m_DontResampleFibers; MODE m_Mode; INPUT m_InputType; bool m_BothEnds; float m_OverlapFraction; bool m_NoNegatives; bool m_NoPositives; bool m_Interpolate; float m_Threshold; std::vector< unsigned short > m_Labels; + bool m_SkipSelfConnections; + bool m_SplitLabels; + unsigned int m_MinFibersPerTract; + std::vector< std::pair< unsigned int, unsigned int > > m_PositiveLabels; typename itk::LinearInterpolateImageFunction< itk::Image< PixelType, 3 >, float >::Pointer m_Interpolator; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkFiberExtractionFilter.cpp" #endif #endif diff --git a/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberExtractionRoi.cpp b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberExtractionRoi.cpp index 75fbad7cec..bd12dfa03e 100755 --- a/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberExtractionRoi.cpp +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberExtractionRoi.cpp @@ -1,166 +1,198 @@ /*=================================================================== 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 "mitkCommandLineParser.h" #include #include #include #include #include #include #include #include #include #include #include #include #define _USE_MATH_DEFINES #include typedef itksys::SystemTools ist; typedef itk::Image ItkFloatImgType; ItkFloatImgType::Pointer LoadItkImage(const std::string& filename) { mitk::Image::Pointer img = dynamic_cast(mitk::IOUtil::Load(filename)[0].GetPointer()); ItkFloatImgType::Pointer itk_image = ItkFloatImgType::New(); mitk::CastToItkImage(img, itk_image); return itk_image; } /*! \brief Extract fibers from a tractogram using binary image ROIs */ int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Fiber Extraction With ROI Image"); parser.setCategory("Fiber Tracking and Processing Methods"); parser.setContributor("MIC"); parser.setDescription("Extract fibers from a tractogram using binary image ROIs"); parser.setArgumentPrefix("--", "-"); parser.addArgument("input", "i", mitkCommandLineParser::String, "Input:", "input tractogram (.fib/.trk/.tck/.dcm)", us::Any(), false); parser.addArgument("out", "o", mitkCommandLineParser::String, "Output:", "output tractogram", us::Any(), false); - parser.addArgument("rois", "", mitkCommandLineParser::StringList, "ROI images:", "Images with binary ROIs", us::Any(), false); + parser.addArgument("rois", "", mitkCommandLineParser::StringList, "ROI images:", "ROI images", us::Any(), false); parser.addArgument("both_ends", "", mitkCommandLineParser::Bool, "Both ends:", "Fibers are extracted if both endpoints are located in the ROI.", false); parser.addArgument("overlap_fraction", "", mitkCommandLineParser::Float, "Overlap fraction:", "Extract by overlap, not by endpoints. Extract fibers that overlap to at least the provided factor (0-1) with the ROI.", -1); parser.addArgument("invert", "", mitkCommandLineParser::Bool, "Invert:", "get streamlines not positive for any of the ROI images", false); parser.addArgument("interpolate", "", mitkCommandLineParser::Bool, "Interpolate:", "interpolate ROI images", false); parser.addArgument("threshold", "", mitkCommandLineParser::Float, "Threshold:", "positive means ROI image value threshold", false, 0.5); parser.addArgument("labels", "", mitkCommandLineParser::StringList, "Labels:", "positive means roi image value in labels vector", false); + parser.addArgument("split_labels", "", mitkCommandLineParser::Bool, "Split labels:", "output a separate tractogram for each label-->label tract", false); + parser.addArgument("skip_self_connections", "", mitkCommandLineParser::Bool, "Skip self connections:", "ignore streamlines between two identical labels", false); + parser.addArgument("min_fibers", "", mitkCommandLineParser::Int, "Min. num. fibers:", "discard positive tracts with less fibers", 0); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) return EXIT_FAILURE; std::string inFib = us::any_cast(parsedArgs["input"]); std::string outFib = us::any_cast(parsedArgs["out"]); mitkCommandLineParser::StringContainerType roi_files = us::any_cast(parsedArgs["rois"]); bool both_ends = false; if (parsedArgs.count("both_ends")) both_ends = us::any_cast(parsedArgs["both_ends"]); bool invert = false; if (parsedArgs.count("invert")) invert = us::any_cast(parsedArgs["invert"]); + unsigned int min_fibers = 0; + if (parsedArgs.count("min_fibers")) + min_fibers = us::any_cast(parsedArgs["min_fibers"]); + + bool split_labels = false; + if (parsedArgs.count("split_labels")) + split_labels = us::any_cast(parsedArgs["split_labels"]); + + bool skip_self_connections = false; + if (parsedArgs.count("skip_self_connections")) + skip_self_connections = us::any_cast(parsedArgs["skip_self_connections"]); + float overlap_fraction = -1; if (parsedArgs.count("overlap_fraction")) overlap_fraction = us::any_cast(parsedArgs["overlap_fraction"]); bool any_point = false; if (overlap_fraction>=0) any_point = true; bool interpolate = false; if (parsedArgs.count("interpolate")) interpolate = us::any_cast(parsedArgs["interpolate"]); float threshold = 0.5; if (parsedArgs.count("threshold")) threshold = us::any_cast(parsedArgs["threshold"]); mitkCommandLineParser::StringContainerType labels; if (parsedArgs.count("labels")) labels = us::any_cast(parsedArgs["labels"]); try { // load fiber bundle mitk::FiberBundle::Pointer inputTractogram = dynamic_cast(mitk::IOUtil::Load(inFib)[0].GetPointer()); std::vector< ItkFloatImgType::Pointer > roi_images; for (std::size_t i=0; i roi_images2; for (auto roi : roi_images) roi_images2.push_back(roi); std::vector< unsigned short > short_labels; for (auto l : labels) short_labels.push_back(boost::lexical_cast(l)); itk::FiberExtractionFilter::Pointer extractor = itk::FiberExtractionFilter::New(); extractor->SetInputFiberBundle(inputTractogram); extractor->SetRoiImages(roi_images2); extractor->SetOverlapFraction(overlap_fraction); extractor->SetBothEnds(both_ends); extractor->SetInterpolate(interpolate); extractor->SetThreshold(threshold); extractor->SetLabels(short_labels); + extractor->SetSplitLabels(split_labels); + extractor->SetMinFibersPerTract(min_fibers); + extractor->SetSkipSelfConnections(skip_self_connections); if (invert) extractor->SetNoPositives(true); else extractor->SetNoNegatives(true); if (!any_point) extractor->SetMode(itk::FiberExtractionFilter::MODE::ENDPOINTS); if (short_labels.size()>0) extractor->SetInputType(itk::FiberExtractionFilter::INPUT::LABEL_MAP); extractor->Update(); mitk::FiberBundle::Pointer newFib = mitk::FiberBundle::New(nullptr); if (invert) mitk::IOUtil::Save(extractor->GetNegatives().at(0), outFib); else { - newFib = newFib->AddBundles(extractor->GetPositives()); - mitk::IOUtil::Save(newFib, outFib); + if (!split_labels) + { + newFib = newFib->AddBundles(extractor->GetPositives()); + mitk::IOUtil::Save(newFib, outFib); + } + else + { + int c = 0; + std::vector< std::pair< unsigned int, unsigned int > > positive_labels = extractor->GetPositiveLabels(); + for (auto fib : extractor->GetPositives()) + { + std::pair< unsigned int, unsigned int > l = positive_labels.at(c); + mitk::IOUtil::Save(fib, outFib + "_" + boost::lexical_cast(l.first) + "-" + boost::lexical_cast(l.second) + ".trk"); + ++c; + } + } } } 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/TractographyEvaluation/ExtractAnchorTracts.cpp b/Modules/DiffusionImaging/FiberTracking/cmdapps/TractographyEvaluation/ExtractAnchorTracts.cpp index 2a09a200e7..0d3480e294 100755 --- a/Modules/DiffusionImaging/FiberTracking/cmdapps/TractographyEvaluation/ExtractAnchorTracts.cpp +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/TractographyEvaluation/ExtractAnchorTracts.cpp @@ -1,285 +1,284 @@ /*=================================================================== 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 #include #include #include typedef itksys::SystemTools ist; typedef itk::Image ItkFloatImgType; typedef std::tuple< ItkFloatImgType::Pointer, std::string > MaskType; void CreateFolderStructure(const std::string& path) { if (ist::PathExists(path)) ist::RemoveADirectory(path); ist::MakeDirectory(path); ist::MakeDirectory(path + "/anchor_tracts/"); ist::MakeDirectory(path + "/candidate_tracts/"); ist::MakeDirectory(path + "/implausible_tracts/"); ist::MakeDirectory(path + "/skipped_masks/"); } ItkFloatImgType::Pointer LoadItkImage(const std::string& filename) { mitk::Image::Pointer img = dynamic_cast(mitk::IOUtil::Load(filename)[0].GetPointer()); ItkFloatImgType::Pointer itkMask = ItkFloatImgType::New(); mitk::CastToItkImage(img, itkMask); return itkMask; } std::vector< MaskType > get_file_list(const std::string& path, float anchor_fraction, const std::string& skipped_path, int random_seed) { if (anchor_fraction<0) anchor_fraction = 0; else if (anchor_fraction>1.0) anchor_fraction = 1.0; std::chrono::milliseconds ms = std::chrono::duration_cast< std::chrono::milliseconds >(std::chrono::system_clock::now().time_since_epoch()); if (random_seed<0) std::srand(ms.count()); else std::srand(random_seed); MITK_INFO << "random_seed: " << random_seed; std::vector< MaskType > mask_list; itk::Directory::Pointer dir = itk::Directory::New(); int skipped = 0; if (dir->Load(path.c_str())) { int n = dir->GetNumberOfFiles(); int num_images = 0; std::vector< int > im_indices; for (int r = 0; r < n; r++) { const char *filename = dir->GetFile(r); std::string ext = ist::GetFilenameExtension(filename); if (ext==".nii" || ext==".nii.gz" || ext==".nrrd") { ++num_images; im_indices.push_back(r); } } int skipping_num = num_images * (1.0 - anchor_fraction); std::random_shuffle(im_indices.begin(), im_indices.end()); MITK_INFO << "Skipping " << skipping_num << " images"; MITK_INFO << "Number of anchors: " << num_images-skipping_num; int c = -1; for (int r : im_indices) { c++; const char *filename = dir->GetFile(r); if (c matrix = vtkSmartPointer< vtkMatrix4x4 >::New(); matrix->Identity(); matrix->SetElement(0,0,-matrix->GetElement(0,0)); matrix->SetElement(1,1,-matrix->GetElement(1,1)); geometry->SetIndexToWorldTransformByVtkMatrix(matrix); vtkSmartPointer transformFilter = vtkSmartPointer::New(); transformFilter->SetInputData(fib->GetFiberPolyData()); transformFilter->SetTransform(geometry->GetVtkTransform()); transformFilter->Update(); mitk::FiberBundle::Pointer transformed_fib = mitk::FiberBundle::New(transformFilter->GetOutput()); return transformed_fib; } /*! \brief */ int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Extract Overlapping Tracts"); parser.setCategory("Fiber Tracking Evaluation"); parser.setDescription(""); parser.setContributor("MIC"); parser.setArgumentPrefix("--", "-"); parser.addArgument("input", "i", mitkCommandLineParser::InputFile, "Input:", "input tractogram (.fib, vtk ascii file format)", us::Any(), false); parser.addArgument("out", "o", mitkCommandLineParser::OutputDirectory, "Output:", "output folder", us::Any(), false); parser.addArgument("reference_mask_folder", "m", mitkCommandLineParser::String, "Reference Mask Folder:", "reference masks of known bundles", us::Any(), false); parser.addArgument("gray_matter_mask", "gm", mitkCommandLineParser::String, "GM mask:", "remove fibers not ending in the gray matter"); parser.addArgument("anchor_fraction", "", mitkCommandLineParser::Float, "Anchor fraction:", "Fraction of tracts used as anchors", 0.5); parser.addArgument("overlap", "", mitkCommandLineParser::Float, "Overlap threshold:", "Overlap threshold used to identify the anchor tracts", 0.8); parser.addArgument("subsample", "", mitkCommandLineParser::Float, "Subsampling factor:", "Only use specified fraction of input fibers for the analysis", 1.0); parser.addArgument("random_seed", "", mitkCommandLineParser::Int, ":", "", -1); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) return EXIT_FAILURE; std::string fibFile = us::any_cast(parsedArgs["input"]); std::string reference_mask_folder = us::any_cast(parsedArgs["reference_mask_folder"]); std::string out_folder = us::any_cast(parsedArgs["out"]); std::string gray_matter_mask = ""; if (parsedArgs.count("gray_matter_mask")) gray_matter_mask = us::any_cast(parsedArgs["gray_matter_mask"]); float anchor_fraction = 0.5; if (parsedArgs.count("anchor_fraction")) anchor_fraction = us::any_cast(parsedArgs["anchor_fraction"]); int random_seed = -1; if (parsedArgs.count("random_seed")) random_seed = us::any_cast(parsedArgs["random_seed"]); float overlap = 0.8; if (parsedArgs.count("overlap")) overlap = us::any_cast(parsedArgs["overlap"]); float subsample = 1.0; if (parsedArgs.count("subsample")) subsample = us::any_cast(parsedArgs["subsample"]); try { CreateFolderStructure(out_folder); std::vector< MaskType > known_tract_masks = get_file_list(reference_mask_folder, anchor_fraction, out_folder + "/skipped_masks/", random_seed); mitk::FiberBundle::Pointer inputTractogram = dynamic_cast(mitk::IOUtil::Load(fibFile)[0].GetPointer()); MITK_INFO << "Removing fibers not ending inside of GM"; if (gray_matter_mask.compare("")!=0) { std::streambuf *old = cout.rdbuf(); // <-- save std::stringstream ss; std::cout.rdbuf (ss.rdbuf()); // <-- redirect ItkFloatImgType::Pointer gm_image = LoadItkImage(gray_matter_mask); std::cout.rdbuf (old); // <-- restore itk::FiberExtractionFilter::Pointer extractor = itk::FiberExtractionFilter::New(); extractor->SetInputFiberBundle(inputTractogram); extractor->SetRoiImages({gm_image}); extractor->SetBothEnds(true); extractor->SetMode(itk::FiberExtractionFilter::MODE::ENDPOINTS); extractor->Update(); mitk::FiberBundle::Pointer not_gm_fibers = extractor->GetNegatives().at(0); old = cout.rdbuf(); // <-- save std::cout.rdbuf (ss.rdbuf()); // <-- redirect mitk::IOUtil::Save(not_gm_fibers, out_folder + "/implausible_tracts/no_gm_endings.trk"); inputTractogram = extractor->GetPositives().at(0); std::cout.rdbuf (old); // <-- restore } std::srand(0); if (subsample<1.0) inputTractogram = inputTractogram->SubsampleFibers(subsample); mitk::FiberBundle::Pointer anchor_tractogram = mitk::FiberBundle::New(nullptr); mitk::FiberBundle::Pointer candidate_tractogram = mitk::FiberBundle::New(nullptr); if (!known_tract_masks.empty()) { MITK_INFO << "Find known tracts via overlap match"; std::vector< ItkFloatImgType::Pointer > mask_images; for (auto mask : known_tract_masks) mask_images.push_back(std::get<0>(mask)); std::vector< ItkFloatImgType* > roi_images2; for (auto roi : mask_images) roi_images2.push_back(roi); itk::FiberExtractionFilter::Pointer extractor = itk::FiberExtractionFilter::New(); extractor->SetInputFiberBundle(inputTractogram); extractor->SetRoiImages(roi_images2); extractor->SetOverlapFraction(overlap); - extractor->SetDontResampleFibers(true); extractor->SetMode(itk::FiberExtractionFilter::MODE::OVERLAP); extractor->Update(); std::vector< mitk::FiberBundle::Pointer > positives = extractor->GetPositives(); candidate_tractogram = extractor->GetNegatives().at(0); for ( unsigned int i=0; i(known_tract_masks.at(i)); mitk::IOUtil::Save(positives.at(i), out_folder + "/anchor_tracts/" + mask_name + ".trk"); std::cout.rdbuf (old); // <-- restore } anchor_tractogram = anchor_tractogram->AddBundles(positives); } mitk::IOUtil::Save(anchor_tractogram, out_folder + "/anchor_tracts/anchor_tractogram.trk"); mitk::IOUtil::Save(candidate_tractogram, out_folder + "/candidate_tracts/candidate_tractogram.trk"); mitk::IOUtil::Save(inputTractogram, out_folder + "/filtered_tractogram.trk"); } 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; }