diff --git a/CMake/BuildConfigurations/DiffusionAll.cmake b/CMake/BuildConfigurations/DiffusionAll.cmake index 703d668914..c1ca6adcf2 100644 --- a/CMake/BuildConfigurations/DiffusionAll.cmake +++ b/CMake/BuildConfigurations/DiffusionAll.cmake @@ -1,35 +1,37 @@ message(STATUS "Configuring MITK Diffusion with all Plugins") # Enable non-optional external dependencies set(MITK_USE_Vigra ON CACHE BOOL "MITK Use Vigra Library" FORCE) set(MITK_USE_HDF5 ON CACHE BOOL "MITK Use HDF5 Library" FORCE) set(MITK_USE_MatchPoint ON CACHE BOOL "" FORCE) +set(MITK_USE_DCMTK ON CACHE BOOL "" FORCE) +set(MITK_USE_DCMQI ON CACHE BOOL "" FORCE) set(MITK_USE_Python ON CACHE BOOL "" FORCE) set(MITK_USE_SYSTEM_PYTHON ON CACHE BOOL "" FORCE) # Disable all apps but MITK Diffusion set(MITK_BUILD_ALL_APPS OFF CACHE BOOL "Build all MITK applications" FORCE) set(MITK_BUILD_APP_CoreApp OFF CACHE BOOL "Build the MITK CoreApp" FORCE) set(MITK_BUILD_APP_Diffusion ON CACHE BOOL "Build MITK Diffusion" FORCE) # Activate Diffusion Mini Apps set(BUILD_DiffusionCoreCmdApps ON CACHE BOOL "Build commandline tools for diffusion" FORCE) set(BUILD_DiffusionFiberProcessingCmdApps ON CACHE BOOL "Build commandline tools for diffusion fiber processing" FORCE) set(BUILD_DiffusionFiberfoxCmdApps ON CACHE BOOL "Build commandline tools for diffusion data simulation (Fiberfox)" FORCE) set(BUILD_DiffusionMiscCmdApps ON CACHE BOOL "Build miscellaneous commandline tools for diffusion" FORCE) set(BUILD_DiffusionQuantificationCmdApps ON CACHE BOOL "Build commandline tools for diffusion quantification (IVIM, ADC, ...)" FORCE) set(BUILD_DiffusionTractographyCmdApps ON CACHE BOOL "Build commandline tools for diffusion fiber tractography" FORCE) set(BUILD_DiffusionTractographyEvaluationCmdApps ON CACHE BOOL "Build commandline tools for diffusion fiber tractography evaluation" FORCE) set(BUILD_DiffusionConnectomicsCmdApps ON CACHE BOOL "Build commandline tools for diffusion connectomics" FORCE) # Build neither all plugins nor examples set(MITK_BUILD_ALL_PLUGINS OFF CACHE BOOL "Build all MITK plugins" FORCE) set(MITK_BUILD_EXAMPLES OFF CACHE BOOL "Build the MITK examples" FORCE) set(BUILD_TESTING ON CACHE BOOL "Build the MITK tests" FORCE) # Activate in-application help generation set(MITK_DOXYGEN_GENERATE_QCH_FILES ON CACHE BOOL "Use doxygen to generate Qt compressed help files for MITK docs" FORCE) set(BLUEBERRY_USE_QT_HELP ON CACHE BOOL "Enable support for integrating bundle documentation into Qt Help" FORCE) # Disable console window set(MITK_SHOW_CONSOLE_WINDOW ON CACHE BOOL "Use this to enable or disable the console window when starting MITK GUI Applications" FORCE) diff --git a/CMake/BuildConfigurations/DiffusionRelease.cmake b/CMake/BuildConfigurations/DiffusionRelease.cmake index a95181552d..9c7164d6da 100644 --- a/CMake/BuildConfigurations/DiffusionRelease.cmake +++ b/CMake/BuildConfigurations/DiffusionRelease.cmake @@ -1,38 +1,40 @@ message(STATUS "Configuring MITK Diffusion Release Build") # Enable non-optional external dependencies set(MITK_USE_Vigra ON CACHE BOOL "MITK Use Vigra Library" FORCE) set(MITK_USE_HDF5 ON CACHE BOOL "MITK Use HDF5 Library" FORCE) set(MITK_USE_MatchPoint ON CACHE BOOL "" FORCE) +set(MITK_USE_DCMTK ON CACHE BOOL "" FORCE) +set(MITK_USE_DCMQI ON CACHE BOOL "" FORCE) set(MITK_USE_Python ON CACHE BOOL "" FORCE) set(MITK_USE_SYSTEM_PYTHON ON CACHE BOOL "" FORCE) # Disable all apps but MITK Diffusion set(MITK_BUILD_ALL_APPS OFF CACHE BOOL "Build all MITK applications" FORCE) set(MITK_BUILD_APP_CoreApp OFF CACHE BOOL "Build the MITK CoreApp" FORCE) set(MITK_BUILD_APP_Workbench OFF CACHE BOOL "Build the MITK Workbench" FORCE) set(MITK_BUILD_APP_Diffusion ON CACHE BOOL "Build MITK Diffusion" FORCE) # Activate Diffusion Mini Apps set(BUILD_DiffusionCoreCmdApps OFF CACHE BOOL "Build commandline tools for diffusion" FORCE) set(BUILD_DiffusionFiberProcessingCmdApps ON CACHE BOOL "Build commandline tools for diffusion fiber processing" FORCE) set(BUILD_DiffusionFiberfoxCmdApps ON CACHE BOOL "Build commandline tools for diffusion data simulation (Fiberfox)" FORCE) set(BUILD_DiffusionMiscCmdApps OFF CACHE BOOL "Build miscellaneous commandline tools for diffusion" FORCE) set(BUILD_DiffusionQuantificationCmdApps OFF CACHE BOOL "Build commandline tools for diffusion quantification (IVIM, ADC, ...)" FORCE) set(BUILD_DiffusionTractographyCmdApps ON CACHE BOOL "Build commandline tools for diffusion fiber tractography" FORCE) set(BUILD_DiffusionTractographyEvaluationCmdApps OFF CACHE BOOL "Build commandline tools for diffusion fiber tractography evaluation" FORCE) set(BUILD_DiffusionConnectomicsCmdApps ON CACHE BOOL "Build commandline tools for diffusion connectomics" FORCE) # Build neither all plugins nor examples set(MITK_BUILD_ALL_PLUGINS OFF CACHE BOOL "Build all MITK plugins" FORCE) set(MITK_BUILD_EXAMPLES OFF CACHE BOOL "Build the MITK examples" FORCE) set(BUILD_TESTING OFF CACHE BOOL "Build the MITK tests" FORCE) # Activate in-application help generation set(MITK_DOXYGEN_GENERATE_QCH_FILES ON CACHE BOOL "Use doxygen to generate Qt compressed help files for MITK docs" FORCE) set(BLUEBERRY_USE_QT_HELP ON CACHE BOOL "Enable support for integrating bundle documentation into Qt Help" FORCE) # Enable console window set(MITK_SHOW_CONSOLE_WINDOW ON CACHE BOOL "Use this to enable or disable the console window when starting MITK GUI Applications" FORCE) set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE) 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/CMakeLists.txt b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/CMakeLists.txt index 642b53de57..6404d2892b 100755 --- a/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/CMakeLists.txt +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/CMakeLists.txt @@ -1,47 +1,48 @@ 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 ) 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/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/FiberProcessing/FiberJoin.cpp b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberJoin.cpp index df912dcb30..62f5da6516 100755 --- a/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberJoin.cpp +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/FiberJoin.cpp @@ -1,100 +1,100 @@ /*=================================================================== 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 "mitkCommandLineParser.h" #include #include #include #include #include #include #include #define _USE_MATH_DEFINES #include mitk::FiberBundle::Pointer LoadFib(std::string filename) { std::vector fibInfile = mitk::IOUtil::Load(filename); if( fibInfile.empty() ) std::cout << "File " << filename << " could not be read!"; mitk::BaseData::Pointer baseData = fibInfile.at(0); return dynamic_cast(baseData.GetPointer()); } /*! \brief Join multiple tractograms */ int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Fiber Join"); parser.setCategory("Fiber Tracking and Processing Methods"); parser.setContributor("MIC"); parser.setDescription("Join multiple tractograms"); parser.setArgumentPrefix("--", "-"); parser.addArgument("input", "i", mitkCommandLineParser::StringList, "Input:", "input tractograms", us::Any(), false); parser.addArgument("out", "o", mitkCommandLineParser::String, "Output:", "output tractogram", us::Any(), false); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) return EXIT_FAILURE; mitkCommandLineParser::StringContainerType inFibs = us::any_cast(parsedArgs["input"]); std::string outFib = us::any_cast(parsedArgs["out"]); if (inFibs.size()<=1) { std::cout << "More than one input tractogram required!"; return EXIT_FAILURE; } try { std::vector< mitk::FiberBundle::Pointer > tractograms; - mitk::FiberBundle::Pointer result = mitk::FiberBundle::New(nullptr); + mitk::FiberBundle::Pointer result = LoadFib(inFibs.at(0)); for (std::size_t i=1; iAddBundles(tractograms); mitk::IOUtil::Save(result, outFib); } 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/TractDensityFilter.cpp b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/TractDensityFilter.cpp new file mode 100755 index 0000000000..c99c28c7ef --- /dev/null +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/FiberProcessing/TractDensityFilter.cpp @@ -0,0 +1,111 @@ +/*=================================================================== + +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 + +#define _USE_MATH_DEFINES +#include + +typedef itksys::SystemTools ist; +typedef itk::Image ItkFloatImgType; + +/*! +\brief Extract fibers from a tractogram using binary image ROIs +*/ +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + + parser.setTitle("Filter Outliers by Tract Density"); + parser.setCategory("Fiber Tracking and Processing Methods"); + parser.setContributor("MIC"); + + 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("threshold", "", mitkCommandLineParser::Float, "Threshold:", "positive means ROI image value threshold", false, 0.05); + parser.addArgument("overlap", "", mitkCommandLineParser::Float, "Overlap:", "positive means ROI image value threshold", false, 0.5); + 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"]); + + int min_fibers = 0; + if (parsedArgs.count("min_fibers")) + min_fibers = us::any_cast(parsedArgs["min_fibers"]); + + float overlap = 0.5; + if (parsedArgs.count("overlap")) + overlap = us::any_cast(parsedArgs["overlap_fraction"]); + + float threshold = 0.05; + if (parsedArgs.count("threshold")) + threshold = us::any_cast(parsedArgs["threshold"]); + + try + { + mitk::FiberBundle::Pointer inputTractogram = dynamic_cast(mitk::IOUtil::Load(inFib)[0].GetPointer()); + + itk::TractDensityImageFilter< ItkFloatImgType >::Pointer generator = itk::TractDensityImageFilter< ItkFloatImgType >::New(); + generator->SetFiberBundle(inputTractogram); + generator->SetBinaryOutput(false); + generator->SetOutputAbsoluteValues(false); + generator->SetWorkOnFiberCopy(true); + generator->Update(); + + itk::FiberExtractionFilter::Pointer extractor = itk::FiberExtractionFilter::New(); + extractor->SetRoiImages({generator->GetOutput()}); + extractor->SetInputFiberBundle(inputTractogram); + extractor->SetOverlapFraction(overlap); + extractor->SetInterpolate(true); + extractor->SetThreshold(threshold); + extractor->SetNoNegatives(true); + extractor->Update(); + if (extractor->GetPositives().at(0)->GetNumFibers()>=min_fibers) + mitk::IOUtil::Save(extractor->GetPositives().at(0), outFib); + } + 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..81440ae729 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) { + MITK_INFO << "Removing fibers not ending inside of GM"; 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; } diff --git a/Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMSegmentationIO.cpp b/Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMSegmentationIO.cpp index ef10baed96..d6f76a23e6 100644 --- a/Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMSegmentationIO.cpp +++ b/Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMSegmentationIO.cpp @@ -1,700 +1,700 @@ /*=================================================================== 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 __mitkDICOMSegmentationIO__cpp #define __mitkDICOMSegmentationIO__cpp #include "mitkDICOMSegmentationIO.h" #include "mitkDICOMQIIOMimeTypes.h" #include "mitkDICOMSegmentationConstants.h" #include #include #include #include #include #include #include #include // itk #include // dcmqi #include // us #include #include namespace mitk { DICOMSegmentationIO::DICOMSegmentationIO() : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), mitk::MitkDICOMQIIOMimeTypes::DICOMQI_MIMETYPE_NAME(), "DICOM Segmentation") { AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); this->AddDICOMTagsToService(); } void DICOMSegmentationIO::AddDICOMTagsToService() { IDICOMTagsOfInterest *toiService = GetDicomTagsOfInterestService(); if (toiService != nullptr) { toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()); } } IFileIO::ConfidenceLevel DICOMSegmentationIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const LabelSetImage *input = static_cast(this->GetInput()); if (input) return Supported; else return Unsupported; } void DICOMSegmentationIO::Write() { ValidateOutputLocation(); mitk::LocaleSwitch localeSwitch("C"); LocalFile localFile(this); const std::string path = localFile.GetFileName(); auto input = dynamic_cast(this->GetInput()); if (input == nullptr) mitkThrow() << "Cannot write non-image data"; // Get DICOM information from referenced image vector dcmDatasets; DcmFileFormat *readFileFormat = new DcmFileFormat(); try { // TODO: Generate dcmdataset witk DICOM tags from property list; ATM the source are the filepaths from the // property list mitk::StringLookupTableProperty::Pointer filesProp = dynamic_cast(input->GetProperty("referenceFiles").GetPointer()); if (filesProp.IsNull()) { mitkThrow() << "No property with dicom file path."; return; } StringLookupTable filesLut = filesProp->GetValue(); const StringLookupTable::LookupTableType &lookUpTableMap = filesLut.GetLookupTable(); for (auto it : lookUpTableMap) { const char *fileName = (it.second).c_str(); if (readFileFormat->loadFile(fileName, EXS_Unknown).good()) dcmDatasets.push_back(readFileFormat->getAndRemoveDataset()); } } catch (const std::exception &e) { MITK_ERROR << "An error occurred while getting the dicom informations: " << e.what() << endl; return; } // Iterate over all layers. For each a dcm file will be generated for (unsigned int layer = 0; layer < input->GetNumberOfLayers(); ++layer) { vector segmentations; try { // Hack: Remove the const attribute to switch between the layer images. Normally you could get the different // layer images by input->GetLayerImage(layer) mitk::LabelSetImage *mitkLayerImage = const_cast(input); mitkLayerImage->SetActiveLayer(layer); // Cast mitk layer image to itk ImageToItk::Pointer imageToItkFilter = ImageToItk::New(); imageToItkFilter->SetInput(mitkLayerImage); imageToItkFilter->Update(); // Cast from original itk type to dcmqi input itk image type typedef itk::CastImageFilter castItkImageFilterType; castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New(); castFilter->SetInput(imageToItkFilter->GetOutput()); castFilter->Update(); itkInternalImageType::Pointer itkLabelImage = castFilter->GetOutput(); itkLabelImage->DisconnectPipeline(); // Iterate over all labels. For each label a segmentation image will be created const LabelSet *labelSet = input->GetLabelSet(layer); auto labelIter = labelSet->IteratorConstBegin(); // Ignore background label ++labelIter; for (; labelIter != labelSet->IteratorConstEnd(); ++labelIter) { // Thresold over the image with the given label value itk::ThresholdImageFilter::Pointer thresholdFilter = itk::ThresholdImageFilter::New(); thresholdFilter->SetInput(itkLabelImage); thresholdFilter->ThresholdOutside(labelIter->first, labelIter->first); thresholdFilter->SetOutsideValue(0); thresholdFilter->Update(); itkInternalImageType::Pointer segmentImage = thresholdFilter->GetOutput(); segmentImage->DisconnectPipeline(); segmentations.push_back(segmentImage); } } catch (const itk::ExceptionObject &e) { MITK_ERROR << e.GetDescription() << endl; return; } // Create segmentation meta information const std::string &tmpMetaInfoFile = this->CreateMetaDataJsonFile(layer); MITK_INFO << "Writing image: " << path << std::endl; try { // Convert itk segmentation images to dicom image dcmqi::ImageSEGConverter *converter = new dcmqi::ImageSEGConverter(); DcmDataset *result = converter->itkimage2dcmSegmentation(dcmDatasets, segmentations, tmpMetaInfoFile); // Write dicom file DcmFileFormat dcmFileFormat(result); std::string filePath = path.substr(0, path.find_last_of(".")); // If there is more than one layer, we have to write more than 1 dicom file if (input->GetNumberOfLayers() != 1) filePath = filePath + std::to_string(layer) + ".dcm"; else filePath = filePath + ".dcm"; dcmFileFormat.saveFile(filePath.c_str(), EXS_LittleEndianExplicit); // Clean up if (converter != nullptr) delete converter; if (result != nullptr) delete result; } catch (const std::exception &e) { MITK_ERROR << "An error occurred during writing the DICOM Seg: " << e.what() << endl; return; } } // Write a dcm file for the next layer // End of image writing; clean up if (readFileFormat) delete readFileFormat; for (auto obj : dcmDatasets) delete obj; dcmDatasets.clear(); } IFileIO::ConfidenceLevel DICOMSegmentationIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; const std::string fileName = this->GetLocalFileName(); DcmFileFormat dcmFileFormat; OFCondition status = dcmFileFormat.loadFile(fileName.c_str()); if (status.bad()) return Unsupported; OFString modality; if (dcmFileFormat.getDataset()->findAndGetOFString(DCM_Modality, modality).good()) { if (modality.compare("SEG") == 0) return Supported; else return Unsupported; } return Unsupported; } std::vector DICOMSegmentationIO::Read() { mitk::LocaleSwitch localeSwitch("C"); LabelSetImage::Pointer labelSetImage; std::vector result; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << std::endl; if (path.empty()) mitkThrow() << "Empty filename in mitk::ItkImageIO "; try { // Get the dcm data set from file path DcmFileFormat dcmFileFormat; OFCondition status = dcmFileFormat.loadFile(path.c_str()); if (status.bad()) mitkThrow() << "Can't read the input file!"; DcmDataset *dataSet = dcmFileFormat.getDataset(); if (dataSet == nullptr) mitkThrow() << "Can't read data from input file!"; //=============================== dcmqi part ==================================== // Read the DICOM SEG images (segItkImages) and DICOM tags (metaInfo) dcmqi::ImageSEGConverter *converter = new dcmqi::ImageSEGConverter(); pair, string> dcmqiOutput = converter->dcmSegmentation2itkimage(dataSet); map segItkImages = dcmqiOutput.first; dcmqi::JSONSegmentationMetaInformationHandler metaInfo(dcmqiOutput.second.c_str()); metaInfo.read(); MITK_INFO << "Input " << metaInfo.getJSONOutputAsString(); //=============================================================================== // Get the label information from segment attributes for each itk image vector>::const_iterator segmentIter = metaInfo.segmentsAttributesMappingList.begin(); // For each itk image add a layer to the LabelSetImage output for (auto &element : segItkImages) { // Get the labeled image and cast it to mitkImage typedef itk::CastImageFilter castItkImageFilterType; castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New(); castFilter->SetInput(element.second); castFilter->Update(); Image::Pointer layerImage; CastToMitkImage(castFilter->GetOutput(), layerImage); // Get pixel value of the label itkInternalImageType::ValueType segValue = 1; typedef itk::ImageRegionIterator IteratorType; // Iterate over the image to find the pixel value of the label IteratorType iter(element.second, element.second->GetLargestPossibleRegion()); iter.GoToBegin(); while (!iter.IsAtEnd()) { itkInputImageType::PixelType value = iter.Get(); if (value != 0) { segValue = value; break; } ++iter; } // Get Segment information map map segmentMap = (*segmentIter); map::const_iterator segmentMapIter = (*segmentIter).begin(); dcmqi::SegmentAttributes *segmentAttribute = (*segmentMapIter).second; OFString labelName; if (segmentAttribute->getSegmentedPropertyTypeCodeSequence() != nullptr) { segmentAttribute->getSegmentedPropertyTypeCodeSequence()->getCodeMeaning(labelName); if (segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence() != nullptr) { OFString modifier; segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence()->getCodeMeaning(modifier); labelName.append(" (").append(modifier).append(")"); } } else { labelName = std::to_string(segmentAttribute->getLabelID()).c_str(); if (labelName.empty()) labelName = "Unnamed"; } float tmp[3] = {0.0, 0.0, 0.0}; if (segmentAttribute->getRecommendedDisplayRGBValue() != nullptr) { tmp[0] = segmentAttribute->getRecommendedDisplayRGBValue()[0] / 255.0; tmp[1] = segmentAttribute->getRecommendedDisplayRGBValue()[1] / 255.0; tmp[2] = segmentAttribute->getRecommendedDisplayRGBValue()[2] / 255.0; } Label *newLabel = nullptr; // If labelSetImage do not exists (first image) if (labelSetImage.IsNull()) { // Initialize the labelSetImage with the read image labelSetImage = LabelSetImage::New(); labelSetImage->InitializeByLabeledImage(layerImage); // Already a label was generated, so set the information to this newLabel = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer()); newLabel->SetName(labelName.c_str()); newLabel->SetColor(Color(tmp)); newLabel->SetValue(segValue); } else { // Add a new layer to the labelSetImage. Background label is set automatically labelSetImage->AddLayer(layerImage); // Add new label newLabel = new Label; newLabel->SetName(labelName.c_str()); newLabel->SetColor(Color(tmp)); newLabel->SetValue(segValue); labelSetImage->GetLabelSet(labelSetImage->GetActiveLayer())->AddLabel(newLabel); } // Add some more label properties this->SetLabelProperties(newLabel, segmentAttribute); ++segmentIter; } // Add some general DICOM Segmentation properties mitk::IDICOMTagsOfInterest *toiSrv = GetDicomTagsOfInterestService(); auto tagsOfInterest = toiSrv->GetTagsOfInterest(); DICOMTagPathList tagsOfInterestList; for (const auto &tag : tagsOfInterest) { tagsOfInterestList.push_back(tag.first); } mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); scanner->SetInputFiles({GetInputLocation()}); scanner->AddTagPaths(tagsOfInterestList); scanner->Scan(); mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); if (frames.empty()) { MITK_ERROR << "Error reading the DICOM Seg file" << std::endl; return result; } auto findings = ExtractPathsOfInterest(tagsOfInterestList, frames); SetProperties(labelSetImage, findings); // Set active layer to the first layer of the labelset image if (labelSetImage->GetNumberOfLayers() > 1 && labelSetImage->GetActiveLayer() != 0) labelSetImage->SetActiveLayer(0); // Clean up if (converter != nullptr) delete converter; } catch (const std::exception &e) { MITK_ERROR << "An error occurred while reading the DICOM Seg file: " << e.what(); return result; } result.push_back(labelSetImage.GetPointer()); return result; } const std::string mitk::DICOMSegmentationIO::CreateMetaDataJsonFile(int layer) { const mitk::LabelSetImage *image = dynamic_cast(this->GetInput()); const std::string output; dcmqi::JSONSegmentationMetaInformationHandler handler; // 1. Metadata attributes that will be listed in the resulting DICOM SEG object std::string contentCreatorName; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0070, 0x0084).c_str(), contentCreatorName)) contentCreatorName = "MITK"; handler.setContentCreatorName(contentCreatorName); std::string clinicalTrailSeriesId; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0071).c_str(), clinicalTrailSeriesId)) clinicalTrailSeriesId = "Session 1"; handler.setClinicalTrialSeriesID(clinicalTrailSeriesId); std::string clinicalTrialTimePointID; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0050).c_str(), clinicalTrialTimePointID)) clinicalTrialTimePointID = "0"; handler.setClinicalTrialTimePointID(clinicalTrialTimePointID); std::string clinicalTrialCoordinatingCenterName = ""; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0060).c_str(), clinicalTrialCoordinatingCenterName)) clinicalTrialCoordinatingCenterName = "Unknown"; handler.setClinicalTrialCoordinatingCenterName(clinicalTrialCoordinatingCenterName); std::string seriesDescription; if (!image->GetPropertyList()->GetStringProperty("name", seriesDescription)) seriesDescription = "MITK Segmentation"; handler.setSeriesDescription(seriesDescription); handler.setSeriesNumber("0" + std::to_string(layer)); handler.setInstanceNumber("1"); handler.setBodyPartExamined(""); const LabelSet *labelSet = image->GetLabelSet(layer); auto labelIter = labelSet->IteratorConstBegin(); // Ignore background label ++labelIter; for (; labelIter != labelSet->IteratorConstEnd(); ++labelIter) { const Label *label = labelIter->second; if (label != nullptr) { - StringProperty *segmentNumberProp = dynamic_cast(label->GetProperty( + TemporoSpatialStringProperty *segmentNumberProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str())); - StringProperty *segmentLabelProp = dynamic_cast(label->GetProperty( + TemporoSpatialStringProperty *segmentLabelProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str())); - StringProperty *algorithmTypeProp = dynamic_cast(label->GetProperty( + TemporoSpatialStringProperty *algorithmTypeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str())); - StringProperty *segmentCategoryCodeValueProp = dynamic_cast(label->GetProperty( + TemporoSpatialStringProperty *segmentCategoryCodeValueProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str())); - StringProperty *segmentCategoryCodeSchemeProp = dynamic_cast(label->GetProperty( + TemporoSpatialStringProperty *segmentCategoryCodeSchemeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str())); - StringProperty *segmentCategoryCodeMeaningProp = dynamic_cast(label->GetProperty( + TemporoSpatialStringProperty *segmentCategoryCodeMeaningProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str())); - StringProperty *segmentTypeCodeValueProp = dynamic_cast(label->GetProperty( + TemporoSpatialStringProperty *segmentTypeCodeValueProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str())); - StringProperty *segmentTypeCodeSchemeProp = dynamic_cast(label->GetProperty( + TemporoSpatialStringProperty *segmentTypeCodeSchemeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str())); - StringProperty *segmentTypeCodeMeaningProp = dynamic_cast(label->GetProperty( + TemporoSpatialStringProperty *segmentTypeCodeMeaningProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str())); - StringProperty *segmentModifierCodeValueProp = dynamic_cast(label->GetProperty( + TemporoSpatialStringProperty *segmentModifierCodeValueProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str())); - StringProperty *segmentModifierCodeSchemeProp = dynamic_cast(label->GetProperty( + TemporoSpatialStringProperty *segmentModifierCodeSchemeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str())); - StringProperty *segmentModifierCodeMeaningProp = dynamic_cast(label->GetProperty( + TemporoSpatialStringProperty *segmentModifierCodeMeaningProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str())); dcmqi::SegmentAttributes *segmentAttribute = nullptr; - if (segmentNumberProp->GetValue() == nullptr) + if (segmentNumberProp->GetValue() == "") { MITK_ERROR << "Something went wrong with the label ID."; } else { int labelId = std::stoi(segmentNumberProp->GetValue()); segmentAttribute = handler.createAndGetNewSegment(labelId); } if (segmentAttribute != nullptr) { segmentAttribute->setSegmentDescription(segmentLabelProp->GetValueAsString()); segmentAttribute->setSegmentAlgorithmType(algorithmTypeProp->GetValueAsString()); segmentAttribute->setSegmentAlgorithmName("MITK Segmentation"); if (segmentCategoryCodeValueProp != nullptr && segmentCategoryCodeSchemeProp != nullptr && segmentCategoryCodeMeaningProp != nullptr) segmentAttribute->setSegmentedPropertyCategoryCodeSequence( segmentCategoryCodeValueProp->GetValueAsString(), segmentCategoryCodeSchemeProp->GetValueAsString(), segmentCategoryCodeMeaningProp->GetValueAsString()); else // some default values segmentAttribute->setSegmentedPropertyCategoryCodeSequence( "M-01000", "SRT", "Morphologically Altered Structure"); if (segmentTypeCodeValueProp != nullptr && segmentTypeCodeSchemeProp != nullptr && segmentTypeCodeMeaningProp != nullptr) { segmentAttribute->setSegmentedPropertyTypeCodeSequence(segmentTypeCodeValueProp->GetValueAsString(), segmentTypeCodeSchemeProp->GetValueAsString(), segmentTypeCodeMeaningProp->GetValueAsString()); handler.setBodyPartExamined(segmentTypeCodeMeaningProp->GetValueAsString()); } else { // some default values segmentAttribute->setSegmentedPropertyTypeCodeSequence("M-03000", "SRT", "Mass"); handler.setBodyPartExamined("Mass"); } if (segmentModifierCodeValueProp != nullptr && segmentModifierCodeSchemeProp != nullptr && segmentModifierCodeMeaningProp != nullptr) segmentAttribute->setSegmentedPropertyTypeModifierCodeSequence( segmentModifierCodeValueProp->GetValueAsString(), segmentModifierCodeSchemeProp->GetValueAsString(), segmentModifierCodeMeaningProp->GetValueAsString()); Color color = label->GetColor(); segmentAttribute->setRecommendedDisplayRGBValue(color[0] * 255, color[1] * 255, color[2] * 255); } } } return handler.getJSONOutputAsString(); } void mitk::DICOMSegmentationIO::SetLabelProperties(mitk::Label *label, dcmqi::SegmentAttributes *segmentAttribute) { // Segment Number:Identification number of the segment.The value of Segment Number(0062, 0004) shall be unique // within the Segmentation instance in which it is created label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str(), - StringProperty::New(std::to_string(label->GetValue()))); + TemporoSpatialStringProperty::New(std::to_string(label->GetValue()))); // Segment Label: User-defined label identifying this segment. label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str(), - StringProperty::New(label->GetName())); + TemporoSpatialStringProperty::New(label->GetName())); // Segment Algorithm Type: Type of algorithm used to generate the segment. if (!segmentAttribute->getSegmentAlgorithmType().empty()) label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str(), - StringProperty::New(segmentAttribute->getSegmentAlgorithmType())); + TemporoSpatialStringProperty::New(segmentAttribute->getSegmentAlgorithmType())); // Add Segmented Property Category Code Sequence tags auto categoryCodeSequence = segmentAttribute->getSegmentedPropertyCategoryCodeSequence(); if (categoryCodeSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value categoryCodeSequence->getCodeValue(codeValue); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str(), - StringProperty::New(codeValue.c_str())); + TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator categoryCodeSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str(), - StringProperty::New(codeScheme.c_str())); + TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning categoryCodeSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str(), - StringProperty::New(codeMeaning.c_str())); + TemporoSpatialStringProperty::New(codeMeaning.c_str())); } // Add Segmented Property Type Code Sequence tags auto typeCodeSequence = segmentAttribute->getSegmentedPropertyTypeCodeSequence(); if (typeCodeSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value typeCodeSequence->getCodeValue(codeValue); label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str(), - StringProperty::New(codeValue.c_str())); + TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator typeCodeSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str(), - StringProperty::New(codeScheme.c_str())); + TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning typeCodeSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str(), - StringProperty::New(codeMeaning.c_str())); + TemporoSpatialStringProperty::New(codeMeaning.c_str())); } // Add Segmented Property Type Modifier Code Sequence tags auto modifierCodeSequence = segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence(); if (modifierCodeSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value modifierCodeSequence->getCodeValue(codeValue); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str(), - StringProperty::New(codeValue.c_str())); + TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator modifierCodeSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str(), - StringProperty::New(codeScheme.c_str())); + TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning modifierCodeSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str(), - StringProperty::New(codeMeaning.c_str())); + TemporoSpatialStringProperty::New(codeMeaning.c_str())); } // Add Atomic RegionSequence tags auto atomicRegionSequence = segmentAttribute->getAnatomicRegionSequence(); if (atomicRegionSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value atomicRegionSequence->getCodeValue(codeValue); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH()).c_str(), - StringProperty::New(codeValue.c_str())); + TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator atomicRegionSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH()).c_str(), - StringProperty::New(codeScheme.c_str())); + TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning atomicRegionSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH()).c_str(), - StringProperty::New(codeMeaning.c_str())); + TemporoSpatialStringProperty::New(codeMeaning.c_str())); } } DICOMSegmentationIO *DICOMSegmentationIO::IOClone() const { return new DICOMSegmentationIO(*this); } } // namespace #endif //__mitkDICOMSegmentationIO__cpp diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/src/QmitkFreeSurferParcellationHandler.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/src/QmitkFreeSurferParcellationHandler.cpp index 2eff6caac7..804dbc768b 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/src/QmitkFreeSurferParcellationHandler.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/src/QmitkFreeSurferParcellationHandler.cpp @@ -1,123 +1,138 @@ /*=================================================================== 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 "QmitkFreeSurferParcellationHandler.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include US_USE_NAMESPACE QmitkFreeSurferParcellationHandler::QmitkFreeSurferParcellationHandler() : m_LastPixelvalue( 0 ), m_Translator( mitk::FreeSurferParcellationTranslator::New() ) { us::ServiceProperties props; props["name"] = std::string("ParcellationHandler"); us::ModuleContext* context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); m_ServiceRegistration = context->RegisterService( this, props); } QmitkFreeSurferParcellationHandler::~QmitkFreeSurferParcellationHandler() { m_ServiceRegistration.Unregister(); } void QmitkFreeSurferParcellationHandler::Notify(InteractionEvent *interactionEvent, bool isHandled) { Q_UNUSED( isHandled ) BaseRenderer* sender = interactionEvent->GetSender(); InteractionPositionEvent* positionEvent = static_cast(interactionEvent); TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); DataStorage::SetOfObjects::ConstPointer nodes = sender->GetDataStorage()->GetSubset(isImageData).GetPointer(); if(nodes.IsNull() || nodes->size() <= 0) return; Point3D worldposition = positionEvent->GetPositionInWorld(); for (unsigned int x = 0; x < nodes->size(); x++) { DataNode::Pointer node = static_cast( nodes->at(x) ); if(node.IsNotNull()) { Image::Pointer image = dynamic_cast( node->GetData() ); if( image.IsNotNull() && image->GetGeometry()->IsInside(worldposition) ) { std::string typeStr = image->GetPixelType().GetComponentTypeAsString(); int value = 0; try { if( typeStr == "int" ) { ImagePixelReadAccessor readAccess( image ); value = static_cast( readAccess.GetPixelByWorldCoordinates( worldposition ) ); } + else if( typeStr == "unsigned_int" ) + { + ImagePixelReadAccessor readAccess( image ); + value = static_cast( readAccess.GetPixelByWorldCoordinates( worldposition ) ); + } else if( typeStr == "unsigned_char" ) { ImagePixelReadAccessor readAccess( image ); value = static_cast( readAccess.GetPixelByWorldCoordinates( worldposition ) ); } else if( typeStr == "short" ) { ImagePixelReadAccessor readAccess( image ); value = static_cast( readAccess.GetPixelByWorldCoordinates( worldposition ) ); } else if( typeStr == "float" ) { ImagePixelReadAccessor readAccess( image ); value = static_cast( readAccess.GetPixelByWorldCoordinates( worldposition ) ); } + else if( typeStr == "double" ) + { + ImagePixelReadAccessor readAccess( image ); + value = static_cast( readAccess.GetPixelByWorldCoordinates( worldposition ) ); + } + else if( typeStr == "unsigned_short" ) + { + ImagePixelReadAccessor readAccess( image ); + value = static_cast( readAccess.GetPixelByWorldCoordinates( worldposition ) ); + } else { MITK_WARN("QmitkFreeSurferParcellationHandler") << "Pixeltype '" << typeStr << "' is not implemented yet."; return; } emit this->changed( value ); QString name( QString::fromStdString( this->m_Translator->GetName( value ) ) ); emit this->changed( name ); MousePressEvent::Pointer mouseEvent = dynamic_cast( interactionEvent ); MouseWheelEvent::Pointer wheelEvent = dynamic_cast( interactionEvent ); if(mouseEvent.IsNotNull()) { emit this->clicked( value ); emit this->clicked( name ); } if( wheelEvent.IsNotNull() ) { emit this->scrolled( value ); emit this->scrolled( name ); } return; // exit loop } catch( const Exception& ex ) { MITK_WARN("QmitkFreeSurferParcellationHandler") << "Could not access image for reading pixelvalue due to: " << ex.GetDescription(); } catch(...) { MITK_WARN("QmitkFreeSurferParcellationHandler") << "Could not access image for reading pixelvalue."; } } } } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/CMakeLists.txt b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/CMakeLists.txt index 20294539db..b46289df16 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/CMakeLists.txt @@ -1,9 +1,9 @@ # The project name must correspond to the directory name of your plug-in # and must not contain periods. project(org_mitk_gui_qt_diffusionimaging_fiberprocessing) mitk_create_plugin( EXPORT_DIRECTIVE DIFFUSIONIMAGING_FIBERPROCESSING_EXPORT EXPORTED_INCLUDE_SUFFIXES src - MODULE_DEPENDS MitkFiberTracking MitkChart + MODULE_DEPENDS MitkFiberTracking MitkChart MitkMultilabel ) diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingView.cpp index 25ba19ad35..be248475d3 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingView.cpp @@ -1,1596 +1,1597 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include #include // Qmitk #include "QmitkFiberProcessingView.h" // Qt #include // MITK #include #include #include #include #include #include #include #include #include #include #include "usModuleRegistry.h" #include #include "mitkNodePredicateDataType.h" #include #include #include #include // ITK #include #include #include #include #include #include #include #include #define _USE_MATH_DEFINES #include const std::string QmitkFiberProcessingView::VIEW_ID = "org.mitk.views.fiberprocessing"; const std::string id_DataManager = "org.mitk.views.datamanager"; using namespace mitk; QmitkFiberProcessingView::QmitkFiberProcessingView() : QmitkAbstractView() , m_Controls( 0 ) , m_CircleCounter(0) , m_PolygonCounter(0) , m_UpsamplingFactor(1) { } // Destructor QmitkFiberProcessingView::~QmitkFiberProcessingView() { RemoveObservers(); } void QmitkFiberProcessingView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkFiberProcessingViewControls; m_Controls->setupUi( parent ); connect( m_Controls->m_CircleButton, SIGNAL( clicked() ), this, SLOT( OnDrawCircle() ) ); connect( m_Controls->m_PolygonButton, SIGNAL( clicked() ), this, SLOT( OnDrawPolygon() ) ); connect(m_Controls->PFCompoANDButton, SIGNAL(clicked()), this, SLOT(GenerateAndComposite()) ); connect(m_Controls->PFCompoORButton, SIGNAL(clicked()), this, SLOT(GenerateOrComposite()) ); connect(m_Controls->PFCompoNOTButton, SIGNAL(clicked()), this, SLOT(GenerateNotComposite()) ); connect(m_Controls->m_GenerateRoiImage, SIGNAL(clicked()), this, SLOT(GenerateRoiImage()) ); connect(m_Controls->m_JoinBundles, SIGNAL(clicked()), this, SLOT(JoinBundles()) ); connect(m_Controls->m_SubstractBundles, SIGNAL(clicked()), this, SLOT(SubstractBundles()) ); connect(m_Controls->m_CopyBundle, SIGNAL(clicked()), this, SLOT(CopyBundles()) ); connect(m_Controls->m_ExtractFibersButton, SIGNAL(clicked()), this, SLOT(Extract())); connect(m_Controls->m_RemoveButton, SIGNAL(clicked()), this, SLOT(Remove())); connect(m_Controls->m_ModifyButton, SIGNAL(clicked()), this, SLOT(Modify())); connect(m_Controls->m_ExtractionMethodBox, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGui())); connect(m_Controls->m_RemovalMethodBox, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGui())); connect(m_Controls->m_ModificationMethodBox, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGui())); connect(m_Controls->m_ExtractionBoxMask, SIGNAL(currentIndexChanged(int)), this, SLOT(OnMaskExtractionChanged())); m_Controls->m_ColorMapBox->SetDataStorage(this->GetDataStorage()); mitk::TNodePredicateDataType::Pointer isMitkImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateDataType::Pointer isDwi = mitk::NodePredicateDataType::New("DiffusionImage"); mitk::NodePredicateDataType::Pointer isDti = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isOdf = mitk::NodePredicateDataType::New("OdfImage"); mitk::NodePredicateOr::Pointer isDiffusionImage = mitk::NodePredicateOr::New(isDwi, isDti); isDiffusionImage = mitk::NodePredicateOr::New(isDiffusionImage, isOdf); mitk::NodePredicateNot::Pointer noDiffusionImage = mitk::NodePredicateNot::New(isDiffusionImage); mitk::NodePredicateAnd::Pointer finalPredicate = mitk::NodePredicateAnd::New(isMitkImage, noDiffusionImage); m_Controls->m_ColorMapBox->SetPredicate(finalPredicate); m_Controls->label_17->setVisible(false); m_Controls->m_FiberExtractionFractionBox->setVisible(false); } UpdateGui(); } void QmitkFiberProcessingView::OnMaskExtractionChanged() { if (m_Controls->m_ExtractionBoxMask->currentIndex() == 2 || m_Controls->m_ExtractionBoxMask->currentIndex() == 3) { m_Controls->label_17->setVisible(true); m_Controls->m_FiberExtractionFractionBox->setVisible(true); m_Controls->m_BothEnds->setVisible(false); } else { m_Controls->label_17->setVisible(false); m_Controls->m_FiberExtractionFractionBox->setVisible(false); if (m_Controls->m_ExtractionBoxMask->currentIndex() != 3) m_Controls->m_BothEnds->setVisible(true); } } void QmitkFiberProcessingView::SetFocus() { m_Controls->toolBoxx->setFocus(); } void QmitkFiberProcessingView::Modify() { switch (m_Controls->m_ModificationMethodBox->currentIndex()) { case 0: { ResampleSelectedBundlesSpline(); break; } case 1: { ResampleSelectedBundlesLinear(); break; } case 2: { CompressSelectedBundles(); break; } case 3: { DoImageColorCoding(); break; } case 4: { MirrorFibers(); break; } case 5: { WeightFibers(); break; } case 6: { DoCurvatureColorCoding(); break; } case 7: { DoWeightColorCoding(); break; } } } void QmitkFiberProcessingView::WeightFibers() { float weight = this->m_Controls->m_BundleWeightBox->value(); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->SetFiberWeights(weight); } } void QmitkFiberProcessingView::Remove() { switch (m_Controls->m_RemovalMethodBox->currentIndex()) { case 0: { RemoveDir(); break; } case 1: { PruneBundle(); break; } case 2: { ApplyCurvatureThreshold(); break; } case 3: { RemoveWithMask(false); break; } case 4: { RemoveWithMask(true); break; } case 5: { ApplyWeightThreshold(); break; } } } void QmitkFiberProcessingView::Extract() { switch (m_Controls->m_ExtractionMethodBox->currentIndex()) { case 0: { ExtractWithPlanarFigure(); break; } case 1: { switch (m_Controls->m_ExtractionBoxMask->currentIndex()) { { case 0: ExtractWithMask(true, false); break; } { case 1: ExtractWithMask(true, true); break; } { case 2: ExtractWithMask(false, false); break; } { case 3: ExtractWithMask(false, true); break; } } break; } } } void QmitkFiberProcessingView::PruneBundle() { int minLength = this->m_Controls->m_PruneFibersMinBox->value(); int maxLength = this->m_Controls->m_PruneFibersMaxBox->value(); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); if (!fib->RemoveShortFibers(minLength)) QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); else if (!fib->RemoveLongFibers(maxLength)) QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::ApplyWeightThreshold() { float thr = this->m_Controls->m_WeightThresholdBox->value(); std::vector< DataNode::Pointer > nodes = m_SelectedFB; for (auto node : nodes) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); mitk::FiberBundle::Pointer newFib = fib->FilterByWeights(thr); if (newFib->GetNumFibers()>0) { newFib->ColorFibersByFiberWeights(false, true); node->SetData(newFib); } else QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::ApplyCurvatureThreshold() { int angle = this->m_Controls->m_CurvSpinBox->value(); int dist = this->m_Controls->m_CurvDistanceSpinBox->value(); std::vector< DataNode::Pointer > nodes = m_SelectedFB; for (auto node : nodes) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); itk::FiberCurvatureFilter::Pointer filter = itk::FiberCurvatureFilter::New(); filter->SetInputFiberBundle(fib); filter->SetAngularDeviation(angle); filter->SetDistance(dist); filter->SetRemoveFibers(m_Controls->m_RemoveCurvedFibersBox->isChecked()); filter->Update(); mitk::FiberBundle::Pointer newFib = filter->GetOutputFiberBundle(); if (newFib->GetNumFibers()>0) { newFib->ColorFibersByOrientation(); node->SetData(newFib); } else QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::RemoveDir() { for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); vnl_vector_fixed dir; dir[0] = m_Controls->m_ExtractDirX->value(); dir[1] = m_Controls->m_ExtractDirY->value(); dir[2] = m_Controls->m_ExtractDirZ->value(); fib->RemoveDir(dir,cos((float)m_Controls->m_ExtractAngle->value()*M_PI/180)); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::RemoveWithMask(bool removeInside) { if (m_RoiImageNode.IsNull()) return; mitk::Image::Pointer mitkMask = dynamic_cast(m_RoiImageNode->GetData()); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); ItkUCharImageType::Pointer mask = ItkUCharImageType::New(); mitk::CastToItkImage(mitkMask, mask); mitk::FiberBundle::Pointer newFib = fib->RemoveFibersOutside(mask, removeInside); if (newFib->GetNumFibers()<=0) { QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); continue; } node->SetData(newFib); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::ExtractWithMask(bool onlyEnds, bool invert) { if (m_RoiImageNode.IsNull()) return; mitk::Image::Pointer mitkMask = dynamic_cast(m_RoiImageNode->GetData()); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); QString name(node->GetName().c_str()); ItkFloatImageType::Pointer mask = ItkFloatImageType::New(); mitk::CastToItkImage(mitkMask, mask); itk::FiberExtractionFilter::Pointer extractor = itk::FiberExtractionFilter::New(); extractor->SetInputFiberBundle(fib); extractor->SetRoiImages({mask}); + extractor->SetThreshold(m_Controls->m_FiberExtractionThresholdBox->value()); extractor->SetOverlapFraction(m_Controls->m_FiberExtractionFractionBox->value()); extractor->SetBothEnds(m_Controls->m_BothEnds->isChecked()); extractor->SetInterpolate(m_Controls->m_InterpolateRoiBox->isChecked()); if (invert) extractor->SetNoPositives(true); else extractor->SetNoNegatives(true); if (onlyEnds) extractor->SetMode(itk::FiberExtractionFilter::MODE::ENDPOINTS); extractor->Update(); mitk::FiberBundle::Pointer newFib; if (invert) newFib = extractor->GetNegatives().at(0); else newFib = extractor->GetPositives().at(0); if (newFib.IsNull() || newFib->GetNumFibers()<=0) { QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); continue; } DataNode::Pointer newNode = DataNode::New(); newNode->SetData(newFib); if (invert) { name += "_not"; if (onlyEnds) name += "-ending-in-mask"; else name += "-passing-mask"; } else { if (onlyEnds) name += "_ending-in-mask"; else name += "_passing-mask"; } newNode->SetName(name.toStdString()); GetDataStorage()->Add(newNode); node->SetVisibility(false); } } void QmitkFiberProcessingView::GenerateRoiImage() { if (m_SelectedPF.empty()) return; mitk::BaseGeometry::Pointer geometry; if (!m_SelectedFB.empty()) { mitk::FiberBundle::Pointer fib = dynamic_cast(m_SelectedFB.front()->GetData()); geometry = fib->GetGeometry(); } else if (m_SelectedImage) geometry = m_SelectedImage->GetGeometry(); else return; itk::Vector spacing = geometry->GetSpacing(); spacing /= m_UpsamplingFactor; mitk::Point3D newOrigin = geometry->GetOrigin(); mitk::Geometry3D::BoundsArrayType bounds = geometry->GetBounds(); newOrigin[0] += bounds.GetElement(0); newOrigin[1] += bounds.GetElement(2); newOrigin[2] += bounds.GetElement(4); itk::Matrix direction; itk::ImageRegion<3> imageRegion; for (int i=0; i<3; i++) for (int j=0; j<3; j++) direction[j][i] = geometry->GetMatrixColumn(i)[j]/spacing[j]; imageRegion.SetSize(0, geometry->GetExtent(0)*m_UpsamplingFactor); imageRegion.SetSize(1, geometry->GetExtent(1)*m_UpsamplingFactor); imageRegion.SetSize(2, geometry->GetExtent(2)*m_UpsamplingFactor); m_PlanarFigureImage = ItkUCharImageType::New(); m_PlanarFigureImage->SetSpacing( spacing ); // Set the image spacing m_PlanarFigureImage->SetOrigin( newOrigin ); // Set the image origin m_PlanarFigureImage->SetDirection( direction ); // Set the image direction m_PlanarFigureImage->SetRegions( imageRegion ); m_PlanarFigureImage->Allocate(); m_PlanarFigureImage->FillBuffer( 0 ); Image::Pointer tmpImage = Image::New(); tmpImage->InitializeByItk(m_PlanarFigureImage.GetPointer()); tmpImage->SetVolume(m_PlanarFigureImage->GetBufferPointer()); std::string name = m_SelectedPF.at(0)->GetName(); WritePfToImage(m_SelectedPF.at(0), tmpImage); for (unsigned int i=1; iGetName(); WritePfToImage(m_SelectedPF.at(i), tmpImage); } DataNode::Pointer node = DataNode::New(); tmpImage = Image::New(); tmpImage->InitializeByItk(m_PlanarFigureImage.GetPointer()); tmpImage->SetVolume(m_PlanarFigureImage->GetBufferPointer()); node->SetData(tmpImage); node->SetName(name); this->GetDataStorage()->Add(node); } void QmitkFiberProcessingView::WritePfToImage(mitk::DataNode::Pointer node, mitk::Image* image) { if (dynamic_cast(node->GetData())) { m_PlanarFigure = dynamic_cast(node->GetData()); AccessFixedDimensionByItk_2( image, InternalReorientImagePlane, 3, m_PlanarFigure->GetGeometry(), -1); AccessFixedDimensionByItk_2( m_InternalImage, InternalCalculateMaskFromPlanarFigure, 3, 2, node->GetName() ); } else if (dynamic_cast(node->GetData())) { DataStorage::SetOfObjects::ConstPointer children = GetDataStorage()->GetDerivations(node); for (unsigned int i=0; iSize(); i++) { WritePfToImage(children->at(i), image); } } } template < typename TPixel, unsigned int VImageDimension > void QmitkFiberProcessingView::InternalReorientImagePlane( const itk::Image< TPixel, VImageDimension > *image, mitk::BaseGeometry* planegeo3D, int additionalIndex ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< float, VImageDimension > FloatImageType; typedef itk::ResampleImageFilter ResamplerType; typename ResamplerType::Pointer resampler = ResamplerType::New(); mitk::PlaneGeometry* planegeo = dynamic_cast(planegeo3D); float upsamp = m_UpsamplingFactor; float gausssigma = 0.5; // Spacing typename ResamplerType::SpacingType spacing = planegeo->GetSpacing(); spacing[0] = image->GetSpacing()[0] / upsamp; spacing[1] = image->GetSpacing()[1] / upsamp; spacing[2] = image->GetSpacing()[2]; resampler->SetOutputSpacing( spacing ); // Size typename ResamplerType::SizeType size; size[0] = planegeo->GetExtentInMM(0) / spacing[0]; size[1] = planegeo->GetExtentInMM(1) / spacing[1]; size[2] = 1; resampler->SetSize( size ); // Origin typename mitk::Point3D orig = planegeo->GetOrigin(); typename mitk::Point3D corrorig; planegeo3D->WorldToIndex(orig,corrorig); corrorig[0] += 0.5/upsamp; corrorig[1] += 0.5/upsamp; corrorig[2] += 0; planegeo3D->IndexToWorld(corrorig,corrorig); resampler->SetOutputOrigin(corrorig ); // Direction typename ResamplerType::DirectionType direction; typename mitk::AffineTransform3D::MatrixType matrix = planegeo->GetIndexToWorldTransform()->GetMatrix(); for(unsigned int c=0; cSetOutputDirection( direction ); // Gaussian interpolation if(gausssigma != 0) { double sigma[3]; for( unsigned int d = 0; d < 3; d++ ) sigma[d] = gausssigma * image->GetSpacing()[d]; double alpha = 2.0; typedef itk::GaussianInterpolateImageFunction GaussianInterpolatorType; typename GaussianInterpolatorType::Pointer interpolator = GaussianInterpolatorType::New(); interpolator->SetInputImage( image ); interpolator->SetParameters( sigma, alpha ); resampler->SetInterpolator( interpolator ); } else { typedef typename itk::LinearInterpolateImageFunction InterpolatorType; typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); interpolator->SetInputImage( image ); resampler->SetInterpolator( interpolator ); } resampler->SetInput( image ); resampler->SetDefaultPixelValue(0); resampler->Update(); if(additionalIndex < 0) { this->m_InternalImage = mitk::Image::New(); this->m_InternalImage->InitializeByItk( resampler->GetOutput() ); this->m_InternalImage->SetVolume( resampler->GetOutput()->GetBufferPointer() ); } } template < typename TPixel, unsigned int VImageDimension > void QmitkFiberProcessingView::InternalCalculateMaskFromPlanarFigure( itk::Image< TPixel, VImageDimension > *image, unsigned int axis, std::string ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::CastImageFilter< ImageType, ItkUCharImageType > CastFilterType; // Generate mask image as new image with same header as input image and // initialize with "1". ItkUCharImageType::Pointer newMaskImage = ItkUCharImageType::New(); newMaskImage->SetSpacing( image->GetSpacing() ); // Set the image spacing newMaskImage->SetOrigin( image->GetOrigin() ); // Set the image origin newMaskImage->SetDirection( image->GetDirection() ); // Set the image direction newMaskImage->SetRegions( image->GetLargestPossibleRegion() ); newMaskImage->Allocate(); newMaskImage->FillBuffer( 1 ); // Generate VTK polygon from (closed) PlanarFigure polyline // (The polyline points are shifted by -0.5 in z-direction to make sure // that the extrusion filter, which afterwards elevates all points by +0.5 // in z-direction, creates a 3D object which is cut by the the plane z=0) const PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const BaseGeometry *imageGeometry3D = m_InternalImage->GetGeometry( 0 ); vtkPolyData *polyline = vtkPolyData::New(); polyline->Allocate( 1, 1 ); // Determine x- and y-dimensions depending on principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } // Create VTK polydata object of polyline contour vtkPoints *points = vtkPoints::New(); PlanarFigure::PolyLineType::const_iterator it; unsigned int numberOfPoints = 0; for ( it = planarFigurePolyline.begin(); it != planarFigurePolyline.end(); ++it ) { Point3D point3D; // Convert 2D point back to the local index coordinates of the selected image Point2D point2D = *it; planarFigurePlaneGeometry->WorldToIndex(point2D, point2D); point2D[0] -= 0.5/m_UpsamplingFactor; point2D[1] -= 0.5/m_UpsamplingFactor; planarFigurePlaneGeometry->IndexToWorld(point2D, point2D); planarFigurePlaneGeometry->Map( point2D, point3D ); // Polygons (partially) outside of the image bounds can not be processed further due to a bug in vtkPolyDataToImageStencil if ( !imageGeometry3D->IsInside( point3D ) ) { float bounds[2] = {0,0}; bounds[0] = this->m_InternalImage->GetLargestPossibleRegion().GetSize().GetElement(i0); bounds[1] = this->m_InternalImage->GetLargestPossibleRegion().GetSize().GetElement(i1); imageGeometry3D->WorldToIndex( point3D, point3D ); if (point3D[i0]<0) point3D[i0] = 0.0; else if (point3D[i0]>bounds[0]) point3D[i0] = bounds[0]-0.001; if (point3D[i1]<0) point3D[i1] = 0.0; else if (point3D[i1]>bounds[1]) point3D[i1] = bounds[1]-0.001; points->InsertNextPoint( point3D[i0], point3D[i1], -0.5 ); numberOfPoints++; } else { imageGeometry3D->WorldToIndex( point3D, point3D ); // Add point to polyline array points->InsertNextPoint( point3D[i0], point3D[i1], -0.5 ); numberOfPoints++; } } polyline->SetPoints( points ); points->Delete(); vtkIdType *ptIds = new vtkIdType[numberOfPoints]; for ( vtkIdType i = 0; i < numberOfPoints; ++i ) ptIds[i] = i; polyline->InsertNextCell( VTK_POLY_LINE, numberOfPoints, ptIds ); // Extrude the generated contour polygon vtkLinearExtrusionFilter *extrudeFilter = vtkLinearExtrusionFilter::New(); extrudeFilter->SetInputData( polyline ); extrudeFilter->SetScaleFactor( 1 ); extrudeFilter->SetExtrusionTypeToNormalExtrusion(); extrudeFilter->SetVector( 0.0, 0.0, 1.0 ); // Make a stencil from the extruded polygon vtkPolyDataToImageStencil *polyDataToImageStencil = vtkPolyDataToImageStencil::New(); polyDataToImageStencil->SetInputConnection( extrudeFilter->GetOutputPort() ); // Export from ITK to VTK (to use a VTK filter) typedef itk::VTKImageImport< ItkUCharImageType > ImageImportType; typedef itk::VTKImageExport< ItkUCharImageType > ImageExportType; typename ImageExportType::Pointer itkExporter = ImageExportType::New(); itkExporter->SetInput( newMaskImage ); vtkImageImport *vtkImporter = vtkImageImport::New(); this->ConnectPipelines( itkExporter, vtkImporter ); vtkImporter->Update(); // Apply the generated image stencil to the input image vtkImageStencil *imageStencilFilter = vtkImageStencil::New(); imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() ); imageStencilFilter->SetStencilConnection(polyDataToImageStencil->GetOutputPort() ); imageStencilFilter->ReverseStencilOff(); imageStencilFilter->SetBackgroundValue( 0 ); imageStencilFilter->Update(); // Export from VTK back to ITK vtkImageExport *vtkExporter = vtkImageExport::New(); vtkExporter->SetInputConnection( imageStencilFilter->GetOutputPort() ); vtkExporter->Update(); typename ImageImportType::Pointer itkImporter = ImageImportType::New(); this->ConnectPipelines( vtkExporter, itkImporter ); itkImporter->Update(); // calculate cropping bounding box m_InternalImageMask3D = itkImporter->GetOutput(); m_InternalImageMask3D->SetDirection(image->GetDirection()); itk::ImageRegionConstIterator itmask(m_InternalImageMask3D, m_InternalImageMask3D->GetLargestPossibleRegion()); itk::ImageRegionIterator itimage(image, image->GetLargestPossibleRegion()); itmask.GoToBegin(); itimage.GoToBegin(); typename ImageType::SizeType lowersize = {{itk::NumericTraits::max(),itk::NumericTraits::max(),itk::NumericTraits::max()}}; typename ImageType::SizeType uppersize = {{0,0,0}}; while( !itmask.IsAtEnd() ) { if(itmask.Get() == 0) itimage.Set(0); else { typename ImageType::IndexType index = itimage.GetIndex(); typename ImageType::SizeType signedindex; signedindex[0] = index[0]; signedindex[1] = index[1]; signedindex[2] = index[2]; lowersize[0] = signedindex[0] < lowersize[0] ? signedindex[0] : lowersize[0]; lowersize[1] = signedindex[1] < lowersize[1] ? signedindex[1] : lowersize[1]; lowersize[2] = signedindex[2] < lowersize[2] ? signedindex[2] : lowersize[2]; uppersize[0] = signedindex[0] > uppersize[0] ? signedindex[0] : uppersize[0]; uppersize[1] = signedindex[1] > uppersize[1] ? signedindex[1] : uppersize[1]; uppersize[2] = signedindex[2] > uppersize[2] ? signedindex[2] : uppersize[2]; } ++itmask; ++itimage; } typename ImageType::IndexType index; index[0] = lowersize[0]; index[1] = lowersize[1]; index[2] = lowersize[2]; typename ImageType::SizeType size; size[0] = uppersize[0] - lowersize[0] + 1; size[1] = uppersize[1] - lowersize[1] + 1; size[2] = uppersize[2] - lowersize[2] + 1; itk::ImageRegion<3> cropRegion = itk::ImageRegion<3>(index, size); // crop internal mask typedef itk::RegionOfInterestImageFilter< ItkUCharImageType, ItkUCharImageType > ROIMaskFilterType; typename ROIMaskFilterType::Pointer roi2 = ROIMaskFilterType::New(); roi2->SetRegionOfInterest(cropRegion); roi2->SetInput(m_InternalImageMask3D); roi2->Update(); m_InternalImageMask3D = roi2->GetOutput(); Image::Pointer tmpImage = Image::New(); tmpImage->InitializeByItk(m_InternalImageMask3D.GetPointer()); tmpImage->SetVolume(m_InternalImageMask3D->GetBufferPointer()); Image::Pointer tmpImage2 = Image::New(); tmpImage2->InitializeByItk(m_PlanarFigureImage.GetPointer()); const BaseGeometry *pfImageGeometry3D = tmpImage2->GetGeometry( 0 ); const BaseGeometry *intImageGeometry3D = tmpImage->GetGeometry( 0 ); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType imageIterator (m_InternalImageMask3D, m_InternalImageMask3D->GetRequestedRegion()); imageIterator.GoToBegin(); while ( !imageIterator.IsAtEnd() ) { unsigned char val = imageIterator.Value(); if (val>0) { itk::Index<3> index = imageIterator.GetIndex(); Point3D point; point[0] = index[0]; point[1] = index[1]; point[2] = index[2]; intImageGeometry3D->IndexToWorld(point, point); pfImageGeometry3D->WorldToIndex(point, point); point[i0] += 0.5; point[i1] += 0.5; index[0] = point[0]; index[1] = point[1]; index[2] = point[2]; if (pfImageGeometry3D->IsIndexInside(index)) m_PlanarFigureImage->SetPixel(index, 1); } ++imageIterator; } // Clean up VTK objects polyline->Delete(); extrudeFilter->Delete(); polyDataToImageStencil->Delete(); vtkImporter->Delete(); imageStencilFilter->Delete(); //vtkExporter->Delete(); // TODO: crashes when outcommented; memory leak?? delete[] ptIds; } void QmitkFiberProcessingView::UpdateGui() { m_Controls->m_FibLabel->setText("mandatory"); m_Controls->m_PfLabel->setText("needed for extraction"); m_Controls->m_InputData->setTitle("Please Select Input Data"); m_Controls->m_RemoveButton->setEnabled(false); m_Controls->m_PlanarFigureButtonsFrame->setEnabled(false); m_Controls->PFCompoANDButton->setEnabled(false); m_Controls->PFCompoORButton->setEnabled(false); m_Controls->PFCompoNOTButton->setEnabled(false); m_Controls->m_GenerateRoiImage->setEnabled(false); m_Controls->m_ExtractFibersButton->setEnabled(false); m_Controls->m_ModifyButton->setEnabled(false); m_Controls->m_CopyBundle->setEnabled(false); m_Controls->m_JoinBundles->setEnabled(false); m_Controls->m_SubstractBundles->setEnabled(false); // disable alle frames m_Controls->m_BundleWeightFrame->setVisible(false); m_Controls->m_ExtactionFramePF->setVisible(false); m_Controls->m_RemoveDirectionFrame->setVisible(false); m_Controls->m_RemoveLengthFrame->setVisible(false); m_Controls->m_RemoveCurvatureFrame->setVisible(false); m_Controls->m_RemoveByWeightFrame->setVisible(false); m_Controls->m_SmoothFibersFrame->setVisible(false); m_Controls->m_CompressFibersFrame->setVisible(false); m_Controls->m_ColorFibersFrame->setVisible(false); m_Controls->m_MirrorFibersFrame->setVisible(false); m_Controls->m_MaskExtractionFrame->setVisible(false); m_Controls->m_ColorMapBox->setVisible(false); bool pfSelected = !m_SelectedPF.empty(); bool fibSelected = !m_SelectedFB.empty(); bool multipleFibsSelected = (m_SelectedFB.size()>1); bool maskSelected = m_RoiImageNode.IsNotNull(); bool imageSelected = m_SelectedImage.IsNotNull(); // toggle visibility of elements according to selected method switch ( m_Controls->m_ExtractionMethodBox->currentIndex() ) { case 0: m_Controls->m_ExtactionFramePF->setVisible(true); break; case 1: m_Controls->m_MaskExtractionFrame->setVisible(true); break; } switch ( m_Controls->m_RemovalMethodBox->currentIndex() ) { case 0: m_Controls->m_RemoveDirectionFrame->setVisible(true); if ( fibSelected ) m_Controls->m_RemoveButton->setEnabled(true); break; case 1: m_Controls->m_RemoveLengthFrame->setVisible(true); if ( fibSelected ) m_Controls->m_RemoveButton->setEnabled(true); break; case 2: m_Controls->m_RemoveCurvatureFrame->setVisible(true); if ( fibSelected ) m_Controls->m_RemoveButton->setEnabled(true); break; case 3: break; case 4: break; case 5: m_Controls->m_RemoveByWeightFrame->setVisible(true); if ( fibSelected ) m_Controls->m_RemoveButton->setEnabled(true); break; } switch ( m_Controls->m_ModificationMethodBox->currentIndex() ) { case 0: m_Controls->m_SmoothFibersFrame->setVisible(true); break; case 1: m_Controls->m_SmoothFibersFrame->setVisible(true); break; case 2: m_Controls->m_CompressFibersFrame->setVisible(true); break; case 3: m_Controls->m_ColorFibersFrame->setVisible(true); m_Controls->m_ColorMapBox->setVisible(true); break; case 4: m_Controls->m_MirrorFibersFrame->setVisible(true); if (m_SelectedSurfaces.size()>0) m_Controls->m_ModifyButton->setEnabled(true); break; case 5: m_Controls->m_BundleWeightFrame->setVisible(true); break; case 6: m_Controls->m_ColorFibersFrame->setVisible(true); break; case 7: m_Controls->m_ColorFibersFrame->setVisible(true); break; } // are fiber bundles selected? if ( fibSelected ) { m_Controls->m_CopyBundle->setEnabled(true); m_Controls->m_ModifyButton->setEnabled(true); m_Controls->m_PlanarFigureButtonsFrame->setEnabled(true); m_Controls->m_FibLabel->setText(QString(m_SelectedFB.at(0)->GetName().c_str())); // one bundle and one planar figure needed to extract fibers if (pfSelected && m_Controls->m_ExtractionMethodBox->currentIndex()==0) { m_Controls->m_InputData->setTitle("Input Data"); m_Controls->m_PfLabel->setText(QString(m_SelectedPF.at(0)->GetName().c_str())); m_Controls->m_ExtractFibersButton->setEnabled(true); } // more than two bundles needed to join/subtract if (multipleFibsSelected) { m_Controls->m_FibLabel->setText("multiple bundles selected"); m_Controls->m_JoinBundles->setEnabled(true); m_Controls->m_SubstractBundles->setEnabled(true); } if (maskSelected && m_Controls->m_ExtractionMethodBox->currentIndex()==1) { m_Controls->m_InputData->setTitle("Input Data"); m_Controls->m_PfLabel->setText(QString(m_RoiImageNode->GetName().c_str())); m_Controls->m_ExtractFibersButton->setEnabled(true); } if (maskSelected && (m_Controls->m_RemovalMethodBox->currentIndex()==3 || m_Controls->m_RemovalMethodBox->currentIndex()==4) ) { m_Controls->m_InputData->setTitle("Input Data"); m_Controls->m_PfLabel->setText(QString(m_RoiImageNode->GetName().c_str())); m_Controls->m_RemoveButton->setEnabled(true); } } // are planar figures selected? if (pfSelected) { if ( fibSelected || m_SelectedImage.IsNotNull()) m_Controls->m_GenerateRoiImage->setEnabled(true); if (m_SelectedPF.size() > 1) { m_Controls->PFCompoANDButton->setEnabled(true); m_Controls->PFCompoORButton->setEnabled(true); } else m_Controls->PFCompoNOTButton->setEnabled(true); } // is image selected if (imageSelected || maskSelected) { m_Controls->m_PlanarFigureButtonsFrame->setEnabled(true); } } void QmitkFiberProcessingView::NodeRemoved(const mitk::DataNode* node ) { for (auto fnode: m_SelectedFB) if (node == fnode) { m_SelectedFB.clear(); break; } berry::IWorkbenchPart::Pointer nullPart; QList nodes; OnSelectionChanged(nullPart, nodes); } void QmitkFiberProcessingView::NodeAdded(const mitk::DataNode* ) { if (!m_Controls->m_InteractiveBox->isChecked()) { berry::IWorkbenchPart::Pointer nullPart; QList nodes; OnSelectionChanged(nullPart, nodes); } } void QmitkFiberProcessingView::OnEndInteraction() { if (m_Controls->m_InteractiveBox->isChecked()) ExtractWithPlanarFigure(true); } void QmitkFiberProcessingView::AddObservers() { typedef itk::SimpleMemberCommand< QmitkFiberProcessingView > SimpleCommandType; for (auto node : m_SelectedPF) { mitk::PlanarFigure* figure = dynamic_cast(node->GetData()); if (figure!=nullptr) { figure->RemoveAllObservers(); // add observer for event when interaction with figure starts SimpleCommandType::Pointer endInteractionCommand = SimpleCommandType::New(); endInteractionCommand->SetCallbackFunction( this, &QmitkFiberProcessingView::OnEndInteraction); m_EndInteractionObserverTag = figure->AddObserver( mitk::EndInteractionPlanarFigureEvent(), endInteractionCommand ); } } } void QmitkFiberProcessingView::RemoveObservers() { for (auto node : m_SelectedPF) { mitk::PlanarFigure* figure = dynamic_cast(node->GetData()); if (figure!=nullptr) figure->RemoveAllObservers(); } } void QmitkFiberProcessingView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& nodes) { RemoveObservers(); //reset existing Vectors containing FiberBundles and PlanarFigures from a previous selection std::vector lastSelectedFB = m_SelectedFB; m_SelectedFB.clear(); m_SelectedPF.clear(); m_SelectedSurfaces.clear(); m_SelectedImage = nullptr; m_RoiImageNode = nullptr; for (auto node: nodes) { if ( dynamic_cast(node->GetData()) ) m_SelectedFB.push_back(node); else if (dynamic_cast(node->GetData()) || dynamic_cast(node->GetData()) || dynamic_cast(node->GetData())) m_SelectedPF.push_back(node); else if (dynamic_cast(node->GetData())) { m_SelectedImage = dynamic_cast(node->GetData()); if (m_SelectedImage->GetDimension()==3) m_RoiImageNode = node; } else if (dynamic_cast(node->GetData())) m_SelectedSurfaces.push_back(dynamic_cast(node->GetData())); } // if we perform interactive fiber extraction, we want to avoid auto-selection of the extracted bundle if (m_SelectedFB.empty() && m_Controls->m_InteractiveBox->isChecked()) m_SelectedFB = lastSelectedFB; // if no fibers or surfaces are selected, select topmost if (m_SelectedFB.empty() && m_SelectedSurfaces.empty()) { int maxLayer = 0; itk::VectorContainer::ConstPointer nodes = this->GetDataStorage()->GetAll(); for (unsigned int i=0; iSize(); i++) if (dynamic_cast(nodes->at(i)->GetData())) { mitk::DataStorage::SetOfObjects::ConstPointer sources = GetDataStorage()->GetSources(nodes->at(i)); if (sources->Size()>0) continue; int layer = 0; nodes->at(i)->GetPropertyValue("layer", layer); if (layer>=maxLayer) { maxLayer = layer; m_SelectedFB.clear(); m_SelectedFB.push_back(nodes->at(i)); } } } // if no plar figure is selected, select topmost if (m_SelectedPF.empty()) { int maxLayer = 0; itk::VectorContainer::ConstPointer nodes = this->GetDataStorage()->GetAll(); for (unsigned int i=0; iSize(); i++) if (dynamic_cast(nodes->at(i)->GetData()) || dynamic_cast(nodes->at(i)->GetData()) || dynamic_cast(nodes->at(i)->GetData())) { mitk::DataStorage::SetOfObjects::ConstPointer sources = GetDataStorage()->GetSources(nodes->at(i)); if (sources->Size()>0) continue; int layer = 0; nodes->at(i)->GetPropertyValue("layer", layer); if (layer>=maxLayer) { maxLayer = layer; m_SelectedPF.clear(); m_SelectedPF.push_back(nodes->at(i)); } } } AddObservers(); UpdateGui(); } void QmitkFiberProcessingView::OnDrawPolygon() { mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); figure->ClosedOn(); this->AddFigureToDataStorage(figure, QString("Polygon%1").arg(++m_PolygonCounter)); } void QmitkFiberProcessingView::OnDrawCircle() { mitk::PlanarCircle::Pointer figure = mitk::PlanarCircle::New(); this->AddFigureToDataStorage(figure, QString("Circle%1").arg(++m_CircleCounter)); } void QmitkFiberProcessingView::AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name, const char *, mitk::BaseProperty* ) { // initialize figure's geometry with empty geometry mitk::PlaneGeometry::Pointer emptygeometry = mitk::PlaneGeometry::New(); figure->SetPlaneGeometry( emptygeometry ); //set desired data to DataNode where Planarfigure is stored mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetName(name.toStdString()); newNode->SetData(figure); newNode->SetBoolProperty("planarfigure.3drendering", true); newNode->SetBoolProperty("planarfigure.3drendering.fill", true); mitk::PlanarFigureInteractor::Pointer figureInteractor = dynamic_cast(newNode->GetDataInteractor().GetPointer()); if(figureInteractor.IsNull()) { figureInteractor = mitk::PlanarFigureInteractor::New(); us::Module* planarFigureModule = us::ModuleRegistry::GetModule( "MitkPlanarFigure" ); figureInteractor->LoadStateMachine("PlanarFigureInteraction.xml", planarFigureModule ); figureInteractor->SetEventConfig( "PlanarFigureConfig.xml", planarFigureModule ); figureInteractor->SetDataNode(newNode); } // figure drawn on the topmost layer / image GetDataStorage()->Add(newNode ); RemoveObservers(); for(unsigned int i = 0; i < m_SelectedPF.size(); i++) m_SelectedPF[i]->SetSelected(false); newNode->SetSelected(true); m_SelectedPF.clear(); m_SelectedPF.push_back(newNode); AddObservers(); UpdateGui(); } void QmitkFiberProcessingView::ExtractWithPlanarFigure(bool interactive) { if ( m_SelectedFB.empty() || m_SelectedPF.empty() ){ QMessageBox::information( nullptr, "Warning", "No fibe bundle selected!"); return; } try { std::vector fiberBundles = m_SelectedFB; mitk::DataNode::Pointer planarFigure = m_SelectedPF.at(0); for (unsigned int i=0; i(fiberBundles.at(i)->GetData()); mitk::FiberBundle::Pointer extFB = fib->ExtractFiberSubset(planarFigure, GetDataStorage()); if (interactive && m_Controls->m_InteractiveBox->isChecked()) { if (m_InteractiveNode.IsNull()) { m_InteractiveNode = mitk::DataNode::New(); QString name("Interactive"); m_InteractiveNode->SetName(name.toStdString()); GetDataStorage()->Add(m_InteractiveNode); } float op = 5.0/sqrt(fib->GetNumFibers()); float currentOp = 0; fiberBundles.at(i)->GetFloatProperty("opacity", currentOp); if (currentOp!=op) { fib->SetFiberColors(255, 255, 255); fiberBundles.at(i)->SetFloatProperty("opacity", op); fiberBundles.at(i)->SetBoolProperty("Fiber2DfadeEFX", false); } m_InteractiveNode->SetData(extFB); } else { if (extFB->GetNumFibers()<=0) { QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers."); continue; } mitk::DataNode::Pointer node; node = mitk::DataNode::New(); node->SetData(extFB); QString name(fiberBundles.at(i)->GetName().c_str()); name += "*"; node->SetName(name.toStdString()); fiberBundles.at(i)->SetVisibility(false); GetDataStorage()->Add(node); } } } catch(const std::out_of_range& ) { QMessageBox::warning( nullptr, "Fiber extraction failed", "Did you only create the planar figure, using the circle or polygon button, but forgot to actually place it in the image afterwards? \nAfter creating a planar figure, simply left-click at the desired position in the image or on the tractogram to place it."); } } void QmitkFiberProcessingView::GenerateAndComposite() { mitk::PlanarFigureComposite::Pointer PFCAnd = mitk::PlanarFigureComposite::New(); PFCAnd->setOperationType(mitk::PlanarFigureComposite::AND); mitk::DataNode::Pointer newPFCNode; newPFCNode = mitk::DataNode::New(); newPFCNode->SetName("AND"); newPFCNode->SetData(PFCAnd); AddCompositeToDatastorage(newPFCNode, m_SelectedPF); RemoveObservers(); m_SelectedPF.clear(); m_SelectedPF.push_back(newPFCNode); AddObservers(); UpdateGui(); } void QmitkFiberProcessingView::GenerateOrComposite() { mitk::PlanarFigureComposite::Pointer PFCOr = mitk::PlanarFigureComposite::New(); PFCOr->setOperationType(mitk::PlanarFigureComposite::OR); mitk::DataNode::Pointer newPFCNode; newPFCNode = mitk::DataNode::New(); newPFCNode->SetName("OR"); newPFCNode->SetData(PFCOr); RemoveObservers(); AddCompositeToDatastorage(newPFCNode, m_SelectedPF); m_SelectedPF.clear(); m_SelectedPF.push_back(newPFCNode); UpdateGui(); } void QmitkFiberProcessingView::GenerateNotComposite() { mitk::PlanarFigureComposite::Pointer PFCNot = mitk::PlanarFigureComposite::New(); PFCNot->setOperationType(mitk::PlanarFigureComposite::NOT); mitk::DataNode::Pointer newPFCNode; newPFCNode = mitk::DataNode::New(); newPFCNode->SetName("NOT"); newPFCNode->SetData(PFCNot); RemoveObservers(); AddCompositeToDatastorage(newPFCNode, m_SelectedPF); m_SelectedPF.clear(); m_SelectedPF.push_back(newPFCNode); AddObservers(); UpdateGui(); } void QmitkFiberProcessingView::AddCompositeToDatastorage(mitk::DataNode::Pointer pfc, std::vector children, mitk::DataNode::Pointer parentNode ) { pfc->SetSelected(true); if (parentNode.IsNotNull()) GetDataStorage()->Add(pfc, parentNode); else GetDataStorage()->Add(pfc); for (auto child : children) { if (dynamic_cast(child->GetData())) { mitk::DataNode::Pointer newChild; newChild = mitk::DataNode::New(); newChild->SetData(dynamic_cast(child->GetData())); newChild->SetName( child->GetName() ); newChild->SetBoolProperty("planarfigure.3drendering", true); newChild->SetBoolProperty("planarfigure.3drendering.fill", true); GetDataStorage()->Add(newChild, pfc); GetDataStorage()->Remove(child); } else if (dynamic_cast(child->GetData())) { mitk::DataNode::Pointer newChild; newChild = mitk::DataNode::New(); newChild->SetData(dynamic_cast(child->GetData())); newChild->SetName( child->GetName() ); std::vector< mitk::DataNode::Pointer > grandChildVector; mitk::DataStorage::SetOfObjects::ConstPointer grandchildren = GetDataStorage()->GetDerivations(child); for( mitk::DataStorage::SetOfObjects::const_iterator it = grandchildren->begin(); it != grandchildren->end(); ++it ) grandChildVector.push_back(*it); AddCompositeToDatastorage(newChild, grandChildVector, pfc); GetDataStorage()->Remove(child); } } UpdateGui(); } void QmitkFiberProcessingView::CopyBundles() { if ( m_SelectedFB.empty() ){ QMessageBox::information( nullptr, "Warning", "Select at least one fiber bundle!"); MITK_WARN("QmitkFiberProcessingView") << "Select at least one fiber bundle!"; return; } for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); mitk::FiberBundle::Pointer newFib = fib->GetDeepCopy(); node->SetVisibility(false); QString name(""); name += QString(m_SelectedFB.at(0)->GetName().c_str()); name += "_copy"; mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); fbNode->SetData(newFib); fbNode->SetName(name.toStdString()); fbNode->SetVisibility(true); GetDataStorage()->Add(fbNode); } UpdateGui(); } void QmitkFiberProcessingView::JoinBundles() { if ( m_SelectedFB.size()<2 ){ QMessageBox::information( nullptr, "Warning", "Select at least two fiber bundles!"); MITK_WARN("QmitkFiberProcessingView") << "Select at least two fiber bundles!"; return; } m_SelectedFB.at(0)->SetVisibility(false); mitk::FiberBundle::Pointer newBundle = dynamic_cast(m_SelectedFB.at(0)->GetData()); std::vector< mitk::FiberBundle::Pointer > tractograms; for (unsigned int i=1; iSetVisibility(false); tractograms.push_back(dynamic_cast(m_SelectedFB.at(i)->GetData())); } newBundle = newBundle->AddBundles(tractograms); mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); fbNode->SetData(newBundle); fbNode->SetName("Joined_Tractograms"); fbNode->SetVisibility(true); GetDataStorage()->Add(fbNode); UpdateGui(); } void QmitkFiberProcessingView::SubstractBundles() { if ( m_SelectedFB.size()<2 ){ QMessageBox::information( nullptr, "Warning", "Select at least two fiber bundles!"); MITK_WARN("QmitkFiberProcessingView") << "Select at least two fiber bundles!"; return; } mitk::FiberBundle::Pointer newBundle = dynamic_cast(m_SelectedFB.at(0)->GetData()); m_SelectedFB.at(0)->SetVisibility(false); QString name(""); name += QString(m_SelectedFB.at(0)->GetName().c_str()); for (unsigned int i=1; iSubtractBundle(dynamic_cast(m_SelectedFB.at(i)->GetData())); if (newBundle.IsNull()) break; name += "-"+QString(m_SelectedFB.at(i)->GetName().c_str()); m_SelectedFB.at(i)->SetVisibility(false); } if (newBundle.IsNull()) { QMessageBox::information(nullptr, "No output generated:", "The resulting fiber bundle contains no fibers. Did you select the fiber bundles in the correct order? X-Y is not equal to Y-X!"); return; } mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); fbNode->SetData(newBundle); fbNode->SetName(name.toStdString()); fbNode->SetVisibility(true); GetDataStorage()->Add(fbNode); UpdateGui(); } void QmitkFiberProcessingView::ResampleSelectedBundlesSpline() { double factor = this->m_Controls->m_SmoothFibersBox->value(); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->ResampleSpline(factor); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::ResampleSelectedBundlesLinear() { double factor = this->m_Controls->m_SmoothFibersBox->value(); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->ResampleLinear(factor); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::CompressSelectedBundles() { double factor = this->m_Controls->m_ErrorThresholdBox->value(); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->Compress(factor); fib->ColorFibersByOrientation(); } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::DoImageColorCoding() { if (m_Controls->m_ColorMapBox->GetSelectedNode().IsNull()) { QMessageBox::information(nullptr, "Bundle coloring aborted:", "No image providing the scalar values for coloring the selected bundle available."); return; } for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->ColorFibersByScalarMap(dynamic_cast(m_Controls->m_ColorMapBox->GetSelectedNode()->GetData()), m_Controls->m_FiberOpacityBox->isChecked(), m_Controls->m_NormalizeColorValues->isChecked()); } if (auto renderWindowPart = this->GetRenderWindowPart()) { renderWindowPart->RequestUpdate(); } } void QmitkFiberProcessingView::DoCurvatureColorCoding() { for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->ColorFibersByCurvature(m_Controls->m_FiberOpacityBox->isChecked(), m_Controls->m_NormalizeColorValues->isChecked()); } if (auto renderWindowPart = this->GetRenderWindowPart()) { renderWindowPart->RequestUpdate(); } } void QmitkFiberProcessingView::DoWeightColorCoding() { for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); fib->ColorFibersByFiberWeights(m_Controls->m_FiberOpacityBox->isChecked(), m_Controls->m_NormalizeColorValues->isChecked()); } if (auto renderWindowPart = this->GetRenderWindowPart()) { renderWindowPart->RequestUpdate(); } } void QmitkFiberProcessingView::MirrorFibers() { unsigned int axis = this->m_Controls->m_MirrorFibersBox->currentIndex(); for (auto node : m_SelectedFB) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); if (m_SelectedImage.IsNotNull()) fib->SetReferenceGeometry(m_SelectedImage->GetGeometry()); fib->MirrorFibers(axis); } for (auto surf : m_SelectedSurfaces) { vtkSmartPointer poly = surf->GetVtkPolyData(); vtkSmartPointer vtkNewPoints = vtkSmartPointer::New(); for (int i=0; iGetNumberOfPoints(); i++) { double* point = poly->GetPoint(i); point[axis] *= -1; vtkNewPoints->InsertNextPoint(point); } poly->SetPoints(vtkNewPoints); surf->CalculateBoundingBox(); } if (auto renderWindowPart = this->GetRenderWindowPart()) { renderWindowPart->RequestUpdate(); } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingViewControls.ui index 3a3acd8ae6..b9734618b3 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingViewControls.ui @@ -1,1515 +1,1538 @@ QmitkFiberProcessingViewControls 0 0 385 684 Form 0 5 0 0 353 - 411 + 443 Fiber Extraction Extract a fiber subset from the selected fiber bundle using manually placed planar figures as waypoints or binary regions of interest. false 0 0 200 16777215 11 Extract fibers passing through selected ROI or composite ROI. Select ROI and fiber bundle to execute. Extract Qt::Vertical 20 40 QFrame::NoFrame QFrame::Raised 9 9 9 9 0 0 0 200 0 16777215 60 QFrame::NoFrame QFrame::Raised 0 0 0 0 false 60 16777215 Create OR composition with selected ROIs. OR Qt::Horizontal 40 20 false 60 16777215 Create NOT composition from selected ROI. NOT false 60 16777215 Create AND composition with selected ROIs. AND 0 0 200 0 16777215 60 QFrame::NoFrame QFrame::Raised 0 0 0 0 30 30 Draw circular ROI. Select reference fiber bundle to execute. :/QmitkDiffusionImaging/circle.png:/QmitkDiffusionImaging/circle.png 32 32 false true Qt::Horizontal 40 20 30 30 Draw polygonal ROI. Select reference fiber bundle to execute. :/QmitkDiffusionImaging/polygon.png:/QmitkDiffusionImaging/polygon.png 32 32 true true false 0 0 16777215 16777215 11 Generate a binary image containing all selected ROIs. Select at least one ROI (planar figure) and a reference fiber bundle or image. Generate ROI Image Interactive Extraction 0 0 Extract using planar figures Extract using ROI image QFrame::NoFrame QFrame::Raised 0 0 0 0 - - + + - Both ends - - - true + Min. overlap: Extract fibers: + + + + Both ends + + + true + + + 1.000000000000000 0.100000000000000 - - - - Min. overlap: - - - 0 0 Ending in ROI Not ending in ROI Passing ROI Not passing ROI - + Interpolate ROI - false + true + + + + + + + Threshold: + + + + + + + Threshold on ROI image for positions to be considered as positive. + + + 9999.000000000000000 + + + 0.100000000000000 + + + 0.500000000000000 0 0 367 408 Fiber Removal Remove fibers that satisfy certain criteria from the selected bundle. QFrame::NoFrame QFrame::Raised 0 0 0 0 If unchecked, the fiber exceeding the threshold will be split in two instead of removed. Remove Fiber false QFrame::NoFrame QFrame::Raised 0 0 0 0 0 Max. Angular Deviation: Qt::Horizontal 40 20 Maximum angular deviation in degree 180.000000000000000 0.100000000000000 30.000000000000000 Distance: Distance in mm 1 999.000000000000000 1.000000000000000 10.000000000000000 QFrame::NoFrame QFrame::Raised 0 0 0 0 0 X: Y: Z: Angle: Angular deviation threshold in degree 1 90.000000000000000 1.000000000000000 25.000000000000000 QFrame::NoFrame QFrame::Raised 0 0 0 0 Qt::Horizontal 40 20 Minimum fiber length in mm 0 999999999 20 Max. Length: Min. Length: Maximum fiber length in mm 0 999999999 300 false 0 0 200 16777215 11 Remove Qt::Vertical 20 40 0 0 Remove fibers in direction Remove fibers by length Remove fibers by curvature Remove fiber parts outside mask Remove fiber parts inside mask Remove fibers by weight QFrame::NoFrame QFrame::Raised 0 0 0 0 0 Weight threshold: Only fibers with weight larger than this threshold are kept. 5 99999.000000000000000 0.100000000000000 0 0 367 408 Bundle Modification Modify the selected bundle with operations such as fiber resampling, FA coloring, etc. QFrame::NoFrame QFrame::Raised 0 0 0 0 0 Error threshold in mm: 999999999.000000000000000 0.100000000000000 0.100000000000000 QFrame::NoFrame QFrame::Raised 0 0 0 0 0 Sagittal Coronal Axial Select direction: QFrame::NoFrame QFrame::Raised 0 0 0 0 0 If checked, the image values are not only used to color the fibers but are also used as opaxity values. Values as opacity false Scalar map: The values used to color the fibers are min-max normalized. If not checked, the values should be between 0 and 1. Normalize values true 0 0 Resample fibers (spline) Resample fibers (linear) Compress fibers Color fibers by scalar map (e.g. FA) Mirror fibers Weight bundle Color fibers by curvature Color fibers by fiber weights QFrame::NoFrame QFrame::Raised 0 0 0 0 0 0.010000000000000 999999999.000000000000000 0.100000000000000 1.000000000000000 Point distance in mm: Qt::Vertical 20 40 false 0 0 200 16777215 11 Execute QFrame::NoFrame QFrame::Raised 0 0 0 0 0 Weight: 7 999999999.000000000000000 0.100000000000000 1.000000000000000 0 0 367 165 Bundle Operations Join, subtract or copy bundles. false 0 0 200 16777215 11 Returns all fibers contained in bundle X that are not contained in bundle Y (not commutative!). Select at least two fiber bundles to execute. Substract Qt::Vertical 20 40 false 0 0 200 16777215 11 Merge selected fiber bundles. Select at least two fiber bundles to execute. Join false 0 0 200 16777215 11 Merge selected fiber bundles. Select at least two fiber bundles to execute. Copy Please Select Input Data <html><head/><body><p><span style=" color:#ff0000;">mandatory</span></p></body></html> true <html><head/><body><p><span style=" color:#969696;">needed for extraction</span></p></body></html> true Input DTI Fiber Bundle: Binary seed ROI. If not specified, the whole image area is seeded. ROI: Qt::Vertical 20 40 QmitkDataStorageComboBox QComboBox
QmitkDataStorageComboBox.h
diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationView.cpp index b3e6baca3d..c79c81ddab 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberQuantificationView.cpp @@ -1,435 +1,464 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkFiberQuantificationView.h" // Qt #include // MITK #include #include #include +#include +#include +#include // ITK #include #include #include #include #include #include const std::string QmitkFiberQuantificationView::VIEW_ID = "org.mitk.views.fiberquantification"; using namespace mitk; QmitkFiberQuantificationView::QmitkFiberQuantificationView() : QmitkAbstractView() , m_Controls( 0 ) , m_UpsamplingFactor(5) { } // Destructor QmitkFiberQuantificationView::~QmitkFiberQuantificationView() { } void QmitkFiberQuantificationView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkFiberQuantificationViewControls; m_Controls->setupUi( parent ); connect( m_Controls->m_ProcessFiberBundleButton, SIGNAL(clicked()), this, SLOT(ProcessSelectedBundles()) ); connect( m_Controls->m_ExtractFiberPeaks, SIGNAL(clicked()), this, SLOT(CalculateFiberDirections()) ); } } void QmitkFiberQuantificationView::SetFocus() { m_Controls->m_ProcessFiberBundleButton->setFocus(); } void QmitkFiberQuantificationView::CalculateFiberDirections() { typedef itk::Image ItkUcharImgType; // load fiber bundle mitk::FiberBundle::Pointer inputTractogram = dynamic_cast(m_SelectedFB.back()->GetData()); itk::TractsToVectorImageFilter::Pointer fOdfFilter = itk::TractsToVectorImageFilter::New(); if (m_SelectedImage.IsNotNull()) { ItkUcharImgType::Pointer itkMaskImage = ItkUcharImgType::New(); mitk::CastToItkImage(m_SelectedImage, itkMaskImage); fOdfFilter->SetMaskImage(itkMaskImage); } // extract directions from fiber bundle fOdfFilter->SetFiberBundle(inputTractogram); fOdfFilter->SetAngularThreshold(cos(m_Controls->m_AngularThreshold->value()*M_PI/180)); switch (m_Controls->m_FiberDirNormBox->currentIndex()) { case 0: fOdfFilter->SetNormalizationMethod(itk::TractsToVectorImageFilter::NormalizationMethods::GLOBAL_MAX); break; case 1: fOdfFilter->SetNormalizationMethod(itk::TractsToVectorImageFilter::NormalizationMethods::SINGLE_VEC_NORM); break; case 2: fOdfFilter->SetNormalizationMethod(itk::TractsToVectorImageFilter::NormalizationMethods::MAX_VEC_NORM); break; } fOdfFilter->SetUseWorkingCopy(true); fOdfFilter->SetSizeThreshold(m_Controls->m_PeakThreshold->value()); fOdfFilter->SetMaxNumDirections(m_Controls->m_MaxNumDirections->value()); fOdfFilter->Update(); QString name = m_SelectedFB.back()->GetName().c_str(); if (m_Controls->m_NumDirectionsBox->isChecked()) { mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk( fOdfFilter->GetNumDirectionsImage().GetPointer() ); mitkImage->SetVolume( fOdfFilter->GetNumDirectionsImage()->GetBufferPointer() ); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(mitkImage); node->SetName((name+"_NUM_DIRECTIONS").toStdString().c_str()); GetDataStorage()->Add(node, m_SelectedFB.back()); } Image::Pointer mitkImage = dynamic_cast(PeakImage::New().GetPointer()); mitk::CastToMitkImage(fOdfFilter->GetDirectionImage(), mitkImage); mitkImage->SetVolume(fOdfFilter->GetDirectionImage()->GetBufferPointer()); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(mitkImage); node->SetName( (name+"_DIRECTIONS").toStdString().c_str()); GetDataStorage()->Add(node, m_SelectedFB.back()); } void QmitkFiberQuantificationView::UpdateGui() { m_Controls->m_ProcessFiberBundleButton->setEnabled(!m_SelectedFB.empty()); m_Controls->m_ExtractFiberPeaks->setEnabled(!m_SelectedFB.empty()); } void QmitkFiberQuantificationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& nodes) { //reset existing Vectors containing FiberBundles and PlanarFigures from a previous selection m_SelectedFB.clear(); m_SelectedImage = nullptr; for (mitk::DataNode::Pointer node: nodes) { if ( dynamic_cast(node->GetData()) ) { m_SelectedFB.push_back(node); } else if (dynamic_cast(node->GetData())) m_SelectedImage = dynamic_cast(node->GetData()); } UpdateGui(); GenerateStats(); } void QmitkFiberQuantificationView::GenerateStats() { if ( m_SelectedFB.empty() ) return; QString stats(""); for( unsigned int i=0; i(node->GetData())) { if (i>0) stats += "\n-----------------------------\n"; stats += QString(node->GetName().c_str()) + "\n"; mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); stats += "Number of fibers: "+ QString::number(fib->GetNumFibers()) + "\n"; stats += "Number of points: "+ QString::number(fib->GetNumberOfPoints()) + "\n"; stats += "Min. length: "+ QString::number(fib->GetMinFiberLength(),'f',1) + " mm\n"; stats += "Max. length: "+ QString::number(fib->GetMaxFiberLength(),'f',1) + " mm\n"; stats += "Mean length: "+ QString::number(fib->GetMeanFiberLength(),'f',1) + " mm\n"; stats += "Median length: "+ QString::number(fib->GetMedianFiberLength(),'f',1) + " mm\n"; stats += "Standard deviation: "+ QString::number(fib->GetLengthStDev(),'f',1) + " mm\n"; vtkSmartPointer weights = fib->GetFiberWeights(); if (weights!=nullptr) { std::vector< float > weights2; for (int i=0; iGetSize(); i++) weights2.push_back(weights->GetValue(i)); std::sort(weights2.begin(), weights2.end()); stats += "\nFiber weight statistics\n"; stats += "Min: " + QString::number(weights2.front()) + "\n"; stats += "1% quantile: " + QString::number(weights2.at(weights2.size()*0.01)) + "\n"; stats += "5% quantile: " + QString::number(weights2.at(weights2.size()*0.05)) + "\n"; stats += "25% quantile: " + QString::number(weights2.at(weights2.size()*0.25)) + "\n"; stats += "Median: " + QString::number(weights2.at(weights2.size()*0.5)) + "\n"; stats += "75% quantile: " + QString::number(weights2.at(weights2.size()*0.75)) + "\n"; stats += "95% quantile: " + QString::number(weights2.at(weights2.size()*0.95)) + "\n"; stats += "99% quantile: " + QString::number(weights2.at(weights2.size()*0.99)) + "\n"; stats += "Max: " + QString::number(weights2.back()) + "\n"; } else stats += "No fiber weight array found.\n"; } } this->m_Controls->m_StatsTextEdit->setText(stats); } void QmitkFiberQuantificationView::ProcessSelectedBundles() { if ( m_SelectedFB.empty() ){ QMessageBox::information( nullptr, "Warning", "No fibe bundle selected!"); MITK_WARN("QmitkFiberQuantificationView") << "no fibe bundle selected"; return; } int generationMethod = m_Controls->m_GenerationBox->currentIndex(); for( unsigned int i=0; i(node->GetData())) { mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); QString name(node->GetName().c_str()); DataNode::Pointer newNode = nullptr; switch(generationMethod){ case 0: newNode = GenerateTractDensityImage(fib, false, true); name += "_TDI"; break; case 1: newNode = GenerateTractDensityImage(fib, false, false); name += "_TDI"; break; case 2: newNode = GenerateTractDensityImage(fib, true, false); name += "_envelope"; break; case 3: newNode = GenerateColorHeatmap(fib); break; case 4: newNode = GenerateFiberEndingsImage(fib); name += "_fiber_endings"; break; case 5: newNode = GenerateFiberEndingsPointSet(fib); name += "_fiber_endings"; break; } if (newNode.IsNotNull()) { newNode->SetName(name.toStdString()); GetDataStorage()->Add(newNode); } } } } // generate pointset displaying the fiber endings mitk::DataNode::Pointer QmitkFiberQuantificationView::GenerateFiberEndingsPointSet(mitk::FiberBundle::Pointer fib) { mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); vtkSmartPointer fiberPolyData = fib->GetFiberPolyData(); int count = 0; int numFibers = fib->GetNumFibers(); for( int i=0; iGetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); if (numPoints>0) { double* point = points->GetPoint(0); itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; pointSet->InsertPoint(count, itkPoint); count++; } if (numPoints>2) { double* point = points->GetPoint(numPoints-1); itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; pointSet->InsertPoint(count, itkPoint); count++; } } mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( pointSet ); return node; } // generate image displaying the fiber endings mitk::DataNode::Pointer QmitkFiberQuantificationView::GenerateFiberEndingsImage(mitk::FiberBundle::Pointer fib) { typedef unsigned int OutPixType; typedef itk::Image OutImageType; typedef itk::TractsToFiberEndingsImageFilter< OutImageType > ImageGeneratorType; ImageGeneratorType::Pointer generator = ImageGeneratorType::New(); generator->SetFiberBundle(fib); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { OutImageType::Pointer itkImage = OutImageType::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } generator->Update(); // get output image OutImageType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // init data node mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); return node; } // generate rgba heatmap from fiber bundle mitk::DataNode::Pointer QmitkFiberQuantificationView::GenerateColorHeatmap(mitk::FiberBundle::Pointer fib) { typedef itk::RGBAPixel OutPixType; typedef itk::Image OutImageType; typedef itk::TractsToRgbaImageFilter< OutImageType > ImageGeneratorType; ImageGeneratorType::Pointer generator = ImageGeneratorType::New(); generator->SetFiberBundle(fib); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { itk::Image::Pointer itkImage = itk::Image::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } generator->Update(); // get output image typedef itk::Image OutType; OutType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // init data node mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); return node; } // generate tract density image from fiber bundle mitk::DataNode::Pointer QmitkFiberQuantificationView::GenerateTractDensityImage(mitk::FiberBundle::Pointer fib, bool binary, bool absolute) { mitk::DataNode::Pointer node = mitk::DataNode::New(); if (binary) { typedef unsigned char OutPixType; typedef itk::Image OutImageType; itk::TractDensityImageFilter< OutImageType >::Pointer generator = itk::TractDensityImageFilter< OutImageType >::New(); generator->SetFiberBundle(fib); generator->SetBinaryOutput(binary); generator->SetOutputAbsoluteValues(absolute); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { OutImageType::Pointer itkImage = OutImageType::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } generator->Update(); // get output image typedef itk::Image OutType; OutType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); - // init data node - node->SetData(img); + + if (m_SelectedImage.IsNotNull()) + { + mitk::LabelSetImage::Pointer multilabelImage = mitk::LabelSetImage::New(); + multilabelImage->InitializeByLabeledImage(img); + + + + mitk::Label::Pointer label = multilabelImage->GetActiveLabel(); + label->SetName("Tractogram"); +// label->SetColor(color); + label->SetValue(1); +// multilabelImage->GetActiveLabelSet()->AddLabel(label); + multilabelImage->GetActiveLabelSet()->SetActiveLabel(1); + + PropertyList::Pointer dicomSegPropertyList = mitk::DICOMSegmentationPropertyHandler::GetDICOMSegmentationProperties(m_SelectedImage->GetPropertyList()); + + multilabelImage->GetPropertyList()->ConcatenatePropertyList(dicomSegPropertyList); + mitk::DICOMSegmentationPropertyHandler::GetDICOMSegmentProperties(multilabelImage->GetActiveLabel(multilabelImage->GetActiveLayer())); + // init data node + node->SetData(multilabelImage); + } + else + { + // init data node + node->SetData(img); + } + } else { typedef float OutPixType; typedef itk::Image OutImageType; itk::TractDensityImageFilter< OutImageType >::Pointer generator = itk::TractDensityImageFilter< OutImageType >::New(); generator->SetFiberBundle(fib); generator->SetBinaryOutput(binary); generator->SetOutputAbsoluteValues(absolute); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { OutImageType::Pointer itkImage = OutImageType::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } //generator->SetDoFiberResampling(false); generator->Update(); // get output image typedef itk::Image OutType; OutType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // init data node node->SetData(img); } return node; }