diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.cpp index 768604ed93..dc28e08540 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.cpp @@ -1,391 +1,446 @@ /*=================================================================== 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 +#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_OnlySelfConnections(false) , m_SplitLabels(false) , m_MinFibersPerTract(0) + , m_PairedStartEndLabels(false) { 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 > +void FiberExtractionFilter< PixelType >::SetRoiImageNames(const std::vector< std::string > roi_names) +{ + m_RoiImageNames = roi_names; +} + 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) { 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 +std::vector< std::string > 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) ) 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) 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) ) 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]; 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) 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< std::pair< unsigned int, unsigned int >, std::vector< long > > > positive_ids; + std::vector< std::map< std::string, 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]; + double* p1 = points->GetPoint(0); + itk::Point itkP1; + itkP1[0] = p1[0]; itkP1[1] = p1[1]; itkP1[2] = p1[2]; + short label1 = mitk::imv::GetImageValue(itkP1, m_Interpolate, m_Interpolator); - label1 = mitk::imv::GetImageValue(itkP, m_Interpolate, m_Interpolator); + double* p2 = points->GetPoint(numPoints-1); + itk::Point itkP2; + itkP2[0] = p2[0]; itkP2[1] = p2[1]; itkP2[2] = p2[2]; + short label2 = mitk::imv::GetImageValue(itkP2, m_Interpolate, m_Interpolator); + + if (!m_Labels.empty()) // extract fibers from all pairwise label combinations + { for (auto l : m_Labels) { if (l==label1) - { inside++; + if (l==label2) + inside++; + if (inside==2) break; - } } } - - // check second fiber point - short label2 = -1; + else // extract fibers between start and end labels { - 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) + m_BothEnds = true; // if we have start and end labels it does not make sense to not use both endpoints + if (m_PairedStartEndLabels) { - if (l==label2) + if (m_StartLabels.size()!=m_EndLabels.size()) + mitkThrow() << "Start and end label lists must have same size if paired labels are used"; + for (unsigned int ii=0; ii(label1) + "-" + boost::lexical_cast(label2); + else + key = boost::lexical_cast(label2) + "-" + boost::lexical_cast(label1); + if (m key; - if (label1(label1, label2); - else - key = std::pair< unsigned int, unsigned int >(label2, label1); - positive = true; - if ( positive_ids[m].count(key)==0 ) - positive_ids[m].insert( std::pair< std::pair< unsigned int, unsigned int >, std::vector< long > >( key, {i}) ); - else - positive_ids[m][key].push_back(i); + if ( (inside==2 && (!m_SkipSelfConnections || label1!=label2)) || (inside==2 && m_OnlySelfConnections && label1==label2) ) + { + positive = true; + if ( positive_ids[m].count(key)==0 ) + positive_ids[m].insert( std::pair< std::string, std::vector< long > >( key, {i}) ); + else + positive_ids[m][key].push_back(i); + } + } + else + { + if ( (inside>=1 && (!m_SkipSelfConnections || label1!=label2)) || (inside==2 && m_OnlySelfConnections && label1==label2) ) + { + positive = true; + if ( positive_ids[m].count(key)==0 ) + positive_ids[m].insert( std::pair< std::string, std::vector< long > >( 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(tuple.first); } } if (!m_SplitLabels) { mitk::FiberBundle::Pointer output = mitk::FiberBundle::New(nullptr); output = output->AddBundles(m_Positives); m_Positives.clear(); m_Positives.push_back(output); m_PositiveLabels.clear(); } } } template< class PixelType > void FiberExtractionFilter< PixelType >::SetLabels(const std::vector &Labels) { m_Labels = Labels; } +template< class PixelType > +void FiberExtractionFilter< PixelType >::SetStartLabels(const std::vector &Labels) +{ + m_StartLabels = Labels; +} + +template< class PixelType > +void FiberExtractionFilter< PixelType >::SetEndLabels(const std::vector &Labels) +{ + m_EndLabels = 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) { - if (m_InputType==INPUT::LABEL_MAP && m_BothEnds) + if (m_InputType==INPUT::LABEL_MAP) 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 88bc3a9a09..24d2f1bbf2 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.h +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkFiberExtractionFilter.h @@ -1,123 +1,131 @@ /*=================================================================== 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 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 ) void Update() override{ this->GenerateData(); } itkSetMacro( InputFiberBundle, mitk::FiberBundle::Pointer ) 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( OnlySelfConnections, bool ) ///< Only keep 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 + itkSetMacro( PairedStartEndLabels, bool ) void SetRoiImages(const std::vector< ItkInputImgType* > &rois); + void SetRoiImageNames(const std::vector< std::string > roi_names); void SetLabels(const std::vector &Labels); + void SetStartLabels(const std::vector &Labels); + void SetEndLabels(const std::vector &Labels); 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 + std::vector< std::string > GetPositiveLabels() const; ///< In case of label extraction, this vector contains the labels corresponding to the positive tracts protected: void GenerateData() override; FiberExtractionFilter(); ~FiberExtractionFilter() override; mitk::FiberBundle::Pointer CreateFib(std::vector< long >& ids); void ExtractOverlap(mitk::FiberBundle::Pointer fib); void ExtractEndpoints(mitk::FiberBundle::Pointer fib); 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; + std::vector< std::string > m_RoiImageNames; 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_OnlySelfConnections; bool m_SplitLabels; unsigned int m_MinFibersPerTract; - std::vector< std::pair< unsigned int, unsigned int > > m_PositiveLabels; + std::vector< unsigned short > m_StartLabels; + std::vector< unsigned short > m_EndLabels; + bool m_PairedStartEndLabels; + std::vector< std::string > 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 7c63ae10b0..86c7ec8a3f 100755 --- a/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberExtractionRoi.cpp +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberExtractionRoi.cpp @@ -1,198 +1,265 @@ /*=================================================================== 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 = mitk::IOUtil::Load(filename); 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.beginGroup("1. Mandatory arguments:"); 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("out", "o", mitkCommandLineParser::String, "Output:", "output root", us::Any(), false); parser.addArgument("rois", "", mitkCommandLineParser::StringList, "ROI images:", "ROI images", us::Any(), false); + parser.endGroup(); + + parser.beginGroup("2. Label based extraction:"); + parser.addArgument("labels", "", mitkCommandLineParser::StringList, "Labels:", "positive means roi image value in labels vector", us::Any()); + 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("start_labels", "", mitkCommandLineParser::StringList, "Start Labels:", "use separate start and end labels instead of one mixed set", us::Any()); + parser.addArgument("end_labels", "", mitkCommandLineParser::StringList, "End Labels:", "use separate start and end labels instead of one mixed set", us::Any()); + parser.addArgument("paired", "", mitkCommandLineParser::Bool, "Paired:", "start and end label list are paired", false); + parser.endGroup(); + parser.beginGroup("3. Misc:"); 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("output_negatives", "", mitkCommandLineParser::Bool, "Negatives:", "output negatives", false); parser.addArgument("interpolate", "", mitkCommandLineParser::Bool, "Interpolate:", "interpolate ROI images", false); parser.addArgument("threshold", "", mitkCommandLineParser::Float, "Threshold:", "positive means ROI image value threshold", 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); + parser.endGroup(); + 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"]); + bool output_negatives = false; + if (parsedArgs.count("output_negatives")) + output_negatives = us::any_cast(parsedArgs["output_negatives"]); + 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"]); + mitkCommandLineParser::StringContainerType start_labels; + if (parsedArgs.count("start_labels")) + start_labels = us::any_cast(parsedArgs["start_labels"]); + + mitkCommandLineParser::StringContainerType end_labels; + if (parsedArgs.count("end_labels")) + end_labels = us::any_cast(parsedArgs["end_labels"]); + + bool paired = false; + if (parsedArgs.count("paired")) + paired = us::any_cast(parsedArgs["paired"]); + try { // load fiber bundle mitk::FiberBundle::Pointer inputTractogram = mitk::IOUtil::Load(inFib); std::vector< ItkFloatImgType::Pointer > roi_images; + std::vector< std::string > roi_names; 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)); + std::vector< unsigned short > short_start_labels; + for (auto l : start_labels) + short_start_labels.push_back(boost::lexical_cast(l)); + + std::vector< unsigned short > short_end_labels; + for (auto l : end_labels) + short_end_labels.push_back(boost::lexical_cast(l)); + itk::FiberExtractionFilter::Pointer extractor = itk::FiberExtractionFilter::New(); extractor->SetInputFiberBundle(inputTractogram); extractor->SetRoiImages(roi_images2); + extractor->SetRoiImageNames(roi_names); extractor->SetOverlapFraction(overlap_fraction); extractor->SetBothEnds(both_ends); extractor->SetInterpolate(interpolate); extractor->SetThreshold(threshold); extractor->SetLabels(short_labels); + extractor->SetStartLabels(short_start_labels); + extractor->SetEndLabels(short_end_labels); extractor->SetSplitLabels(split_labels); extractor->SetMinFibersPerTract(min_fibers); extractor->SetSkipSelfConnections(skip_self_connections); - if (invert) - extractor->SetNoPositives(true); - else - extractor->SetNoNegatives(true); + extractor->SetPairedStartEndLabels(paired); if (!any_point) extractor->SetMode(itk::FiberExtractionFilter::MODE::ENDPOINTS); - if (short_labels.size()>0) + if (short_labels.size()>0 || short_start_labels.size()>0 || short_end_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); + mitk::IOUtil::Save(extractor->GetNegatives().at(0), outFib + ".trk"); else { if (!split_labels) { newFib = newFib->AddBundles(extractor->GetPositives()); - mitk::IOUtil::Save(newFib, outFib); + mitk::IOUtil::Save(newFib, outFib + ".trk"); } else { int c = 0; - std::vector< std::pair< unsigned int, unsigned int > > positive_labels = extractor->GetPositiveLabels(); + std::vector< std::string > 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"); + std::string l = positive_labels.at(c); + mitk::IOUtil::Save(fib, outFib + "_" + l + ".trk"); ++c; } } } + + if (output_negatives) + { + invert = !invert; + if (invert) + mitk::IOUtil::Save(extractor->GetNegatives().at(0), outFib + "_negatives.trk"); + else + { + if (!split_labels) + { + newFib = newFib->AddBundles(extractor->GetPositives()); + mitk::IOUtil::Save(newFib, outFib + "_negatives.trk"); + } + else + { + int c = 0; + std::vector< std::string > positive_labels = extractor->GetPositiveLabels(); + for (auto fib : extractor->GetPositives()) + { + std::string l = positive_labels.at(c); + mitk::IOUtil::Save(fib, outFib + "_" + l + "_negatives.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/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/documentation/UserManual/QmitkFiberFitViewUserManual.dox b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/documentation/UserManual/QmitkFiberFitViewUserManual.dox index 641f2fb84d..7c7d344aca 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/documentation/UserManual/QmitkFiberFitViewUserManual.dox +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/documentation/UserManual/QmitkFiberFitViewUserManual.dox @@ -1,30 +1,30 @@ /** \page org_mitk_views_fiberfit Fiber Fit Linearly fits a scalar weight for each streamline in order to optimally explain the input image. Corrseponding command line tool is MitkFitFibersToImage. -\section SecParams Input Data and Parameters +\section SecFiberFitParams Input Data and Parameters - Image: The image data used to fit the fiber weights. Possible input types: - Peak images. The input peak magnitudes are approximated using the voxel-wise fixel magnitudes obtained from the input tractogram. - Raw diffusion-weighted images. The dMRI signal is approximated using a tensor model with fixed diffusion parameters. Tensor orientations are determined by the input tractogram. - Sclar valued images (e.g. FA, axon density maps, ...). - Tractogram: The method fits a weight for each streamline in the input tractogram. In the command line tool, a list of separate bundles can be used as input. - Regularization: - Voxel-wise Variance: Constrain the fiber weights to be similar in one voxe (similar to [1]). Command line tool string "VoxelVariance". - Variance: Constrain the fiber weights to be globally similar (mean squared deaviation of weights from mean weight). Command line tool string "Variance". - Mean-squared magnitude: Enforce small weights using the mean-squared magnitude of the streamlines as regularization factor. Command line tool string "MSM". - Lasso: L1 regularization of the streamline weights. Enforces a sparse weight vector. Command line tool string "Lasso". - Group Lasso: Useful if individual bundles are used as input (command-line tool only). This regularization tries to explain the signal with as few bundles as possible penalizing the bundle-wise root mean squared magnitude of the weighs (see [2]). Command line tool string "GroupLasso". - Group Variance: Constrain the fiber weights to be similar for each bundle individually (command-line tool only). Command line tool string "GrouplVariance". - No regularization. Command line tool string "NONE". - Suppress Outliers: Perform second optimization run with an upper weight bound based on the first weight estimation (99% quantile). - Output Residuals: Add residual images to the data manager. [1] Smith, Robert E., Jacques-Donald Tournier, Fernando Calamante, and Alan Connelly. “SIFT2: Enabling Dense Quantitative Assessment of Brain White Matter Connectivity Using Streamlines Tractography.” NeuroImage 119, no. Supplement C (October 1, 2015): 338–51. https://doi.org/10.1016/j.neuroimage.2015.06.092. [2] Yuan, Ming, and Yi Lin. “Model Selection and Estimation in Regression with Grouped Variables.” Journal of the Royal Statistical Society: Series B (Statistical Methodology) 68, no. 1 (February 1, 2006): 49–67. https://doi.org/10.1111/j.1467-9868.2005.00532.x. [3] Pestilli, Franco, Jason D. Yeatman, Ariel Rokem, Kendrick N. Kay, and Brian A. Wandell. “Evaluation and Statistical Inference for Human Connectomes.” Nature Methods 11, no. 10 (October 2014): 1058–63. https://doi.org/10.1038/nmeth.3098. */ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/documentation/UserManual/QmitkFiberQuantificationViewUserManual.dox b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/documentation/UserManual/QmitkFiberQuantificationViewUserManual.dox index 5c80693604..fea03bd0bb 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/documentation/UserManual/QmitkFiberQuantificationViewUserManual.dox +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/documentation/UserManual/QmitkFiberQuantificationViewUserManual.dox @@ -1,32 +1,32 @@ /** \page org_mitk_views_fiberquantification Fiber Quantification This view provides tools to derive additional information (such as tract density images and principal fiber direction maps) from tractograms. -\section SecInput Input Data +\section SecFiberQuantInput Input Data - Tractogram: The input streamlines. - Reference Image: The output images will have the same geometry as this reference image (optional). If a reference image with DICOM tags is used, the resulting tract envelope can be saved as DICOM Segmentation Object. \section SecFDI Fiber-derived Images - Tract density image: Generate a 2D heatmap from a fiber bundle. - Normalized TDI: 0-1 normalized version of the TDI. - Binary envelope: Generate a binary segmentation from the input tractogram. - Fiber bundle image: Generate a 2D rgba image representation of the fiber bundle. - Fiber endings image: Generate a 2D image showing the locations of fiber endpoints. - Fiber endings pointset: Generate a poinset containing the locations of fiber endpoints (not recommended for large tractograms). \section SecPD Principal Fiber Directions Calculate the voxel-wise principal fiber directions (fixels) from a tractogram. - Max. Peaks: Maximum number of output directions per voxel. - Angular Threshold: Cluster directions that are close together using the specified threshold (in degree). - Size Threshold: Discard principal directions with a magnitude smaller than the specified threshold. This value is the vector magnitude raltive to the largest vector in the voxel. - Normalization: Normalize the principal fiber directions by the global maximum, the voxel-wise maximum or each direction individually. - Output #Directions per Voxel: Generate an image that contains the number of principal directions per voxel as values. \imageMacro{DirectionExtractionFib.png, "Input fiber bundle",10} \imageMacro{DirectionExtractionPeaks.png, "Output principal fiber directions",10} */