diff --git a/Modules/Core/src/DataManagement/mitkImageCastPart3.cpp b/Modules/Core/src/DataManagement/mitkImageCastPart3.cpp index 29c10f2bd6..c0555d02af 100644 --- a/Modules/Core/src/DataManagement/mitkImageCastPart3.cpp +++ b/Modules/Core/src/DataManagement/mitkImageCastPart3.cpp @@ -1,104 +1,108 @@ /*=================================================================== 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 namespace mitk { typedef itk::Image, 2> itkImageRGBUC2; typedef itk::Image, 2> itkImageDTIF2; typedef itk::Image, 2> itkImageDTID2; template void MITKCORE_EXPORT _CastToItkImage2Access(const itkImageRGBUC2 *, itk::SmartPointer &); template void MITKCORE_EXPORT _CastToItkImage2Access(const itkImageDTIF2 *, itk::SmartPointer &); template void MITKCORE_EXPORT _CastToItkImage2Access(const itkImageDTID2 *, itk::SmartPointer &); typedef itk::Image, 3> itkImageRGBUC3; typedef itk::Image, 3> itkImageDTIF3; typedef itk::Image, 3> itkImageDTID3; template void MITKCORE_EXPORT _CastToItkImage2Access(const itkImageRGBUC3 *, itk::SmartPointer &); template void MITKCORE_EXPORT _CastToItkImage2Access(const itkImageDTIF3 *, itk::SmartPointer &); template void MITKCORE_EXPORT _CastToItkImage2Access(const itkImageDTID3 *, itk::SmartPointer &); #define CAST_HUNDRED_VECS(HUN) \ CAST_TEN_VECS(HUN) \ CAST_TEN_VECS(HUN + 10) \ CAST_TEN_VECS(HUN + 20) \ CAST_TEN_VECS(HUN + 30) \ CAST_TEN_VECS(HUN + 40) \ CAST_TEN_VECS(HUN + 50) \ CAST_TEN_VECS(HUN + 60) \ CAST_TEN_VECS(HUN + 70) \ CAST_TEN_VECS(HUN + 80) \ CAST_TEN_VECS(HUN + 90) #define CAST_TEN_VECS(TEN) \ CAST_N_VEC(TEN + 1) \ CAST_N_VEC(TEN + 2) \ CAST_N_VEC(TEN + 3) \ CAST_N_VEC(TEN + 4) \ CAST_N_VEC(TEN + 5) \ CAST_N_VEC(TEN + 6) \ CAST_N_VEC(TEN + 7) \ CAST_N_VEC(TEN + 8) \ CAST_N_VEC(TEN + 9) \ CAST_N_VEC(TEN + 10) #define CAST_N_VEC(N_DIRS) \ _CAST_N_VEC(N_DIRS, double) \ _CAST_N_VEC(N_DIRS, float) \ _CAST_N_VEC(N_DIRS, short) #define _CAST_N_VEC(N_DIRS, PIXTYPE) \ template void MITKCORE_EXPORT _CastToItkImage2Access( \ const itk::Image, 2> *, \ itk::SmartPointer, 2>> &); \ template void MITKCORE_EXPORT _CastToItkImage2Access( \ const itk::Image, 3> *, \ itk::SmartPointer, 3>> &); // the following lines allow for fixed-size vector images up to a certain size limit // (commented out for shorter compile times) // CAST_HUNDRED_VECS(0) // CAST_HUNDRED_VECS(100) // CAST_HUNDRED_VECS(200) // CAST_HUNDRED_VECS(300) // allow for fixed-size vectors of specific length // (inspired by itkPointshell.cpp, precompiled ODF configs) // CAST_TEN_VECS(0) // CAST_N_VEC(11) // CAST_N_VEC(12) CAST_N_VEC(2) CAST_N_VEC(3) CAST_N_VEC(6) + CAST_N_VEC(15) + CAST_N_VEC(28) CAST_N_VEC(42) + CAST_N_VEC(45) + CAST_N_VEC(91) CAST_N_VEC(92) CAST_N_VEC(162) CAST_N_VEC(252) CAST_N_VEC(362) CAST_N_VEC(492) CAST_N_VEC(642) CAST_N_VEC(812) CAST_N_VEC(1002) } diff --git a/Modules/Core/src/DataManagement/mitkImageCastPart4.cpp b/Modules/Core/src/DataManagement/mitkImageCastPart4.cpp index 67ef149589..168c0f2801 100644 --- a/Modules/Core/src/DataManagement/mitkImageCastPart4.cpp +++ b/Modules/Core/src/DataManagement/mitkImageCastPart4.cpp @@ -1,179 +1,183 @@ /*=================================================================== 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 namespace mitk { typedef itk::Image, 2> itkImageRGBUC2; typedef itk::Image, 2> itkImageDTIF2; typedef itk::Image, 2> itkImageDTID2; template <> void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer &itkOutputImage) { typedef itkImageRGBUC2 ItkOutputImageType; AccessFixedTypeByItk_1(mitkImage, _CastToItkImage2Access, (itk::RGBPixel), (ItkOutputImageType::ImageDimension), itkOutputImage); } template <> void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer &itkOutputImage) { typedef itkImageDTIF2 ItkOutputImageType; AccessFixedTypeByItk_1(mitkImage, _CastToItkImage2Access, (itk::DiffusionTensor3D), (ItkOutputImageType::ImageDimension), itkOutputImage); } template <> void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer &itkOutputImage) { typedef itkImageDTID2 ItkOutputImageType; AccessFixedTypeByItk_1(mitkImage, _CastToItkImage2Access, (itk::DiffusionTensor3D), (ItkOutputImageType::ImageDimension), itkOutputImage); } typedef itk::Image, 3> itkImageRGBUC3; typedef itk::Image, 3> itkImageDTIF3; typedef itk::Image, 3> itkImageDTID3; template <> void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer &itkOutputImage) { typedef itkImageRGBUC3 ItkOutputImageType; AccessFixedTypeByItk_1(mitkImage, _CastToItkImage2Access, (itk::RGBPixel), (ItkOutputImageType::ImageDimension), itkOutputImage); } template <> void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer &itkOutputImage) { typedef itkImageDTIF3 ItkOutputImageType; AccessFixedTypeByItk_1(mitkImage, _CastToItkImage2Access, (itk::DiffusionTensor3D), (ItkOutputImageType::ImageDimension), itkOutputImage); } template <> void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer &itkOutputImage) { typedef itkImageDTID3 ItkOutputImageType; AccessFixedTypeByItk_1(mitkImage, _CastToItkImage2Access, (itk::DiffusionTensor3D), (ItkOutputImageType::ImageDimension), itkOutputImage); } #define TYPE_VECS(HUN) \ TYPE_TEN_VECS(HUN) \ TYPE_TEN_VECS(HUN + 10) \ TYPE_TEN_VECS(HUN + 20) \ TYPE_TEN_VECS(HUN + 30) \ TYPE_TEN_VECS(HUN + 40) \ TYPE_TEN_VECS(HUN + 50) \ TYPE_TEN_VECS(HUN + 60) \ TYPE_TEN_VECS(HUN + 70) \ TYPE_TEN_VECS(HUN + 80) \ TYPE_TEN_VECS(HUN + 90) #define TYPE_TEN_VECS(HUN) \ TYPE_N_VEC(HUN + 1) \ TYPE_N_VEC(HUN + 2) \ TYPE_N_VEC(HUN + 3) \ TYPE_N_VEC(HUN + 4) \ TYPE_N_VEC(HUN + 5) \ TYPE_N_VEC(HUN + 6) \ TYPE_N_VEC(HUN + 7) \ TYPE_N_VEC(HUN + 8) \ TYPE_N_VEC(HUN + 9) \ TYPE_N_VEC(HUN + 10) #define TYPE_N_VEC(N_DIRS) \ _TYPE_N_VEC(N_DIRS, double) \ _TYPE_N_VEC(N_DIRS, float) \ _TYPE_N_VEC(N_DIRS, short) #define _TYPE_N_VEC(N_DIRS, PIXTYPE) \ template <> \ void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, \ itk::SmartPointer, 2>> &itkOutputImage) \ \ { \ typedef itk::Vector VECTORTYPE; \ typedef itk::Image ItkOutputImageType2; \ AccessFixedTypeByItk_1( \ mitkImage, _CastToItkImage2Access, (VECTORTYPE), (ItkOutputImageType2::ImageDimension), itkOutputImage); \ } \ template <> \ void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, \ itk::SmartPointer, 3>> &itkOutputImage) \ \ { \ typedef itk::Vector VECTORTYPE; \ typedef itk::Image ItkOutputImageType3; \ AccessFixedTypeByItk_1( \ mitkImage, _CastToItkImage2Access, (VECTORTYPE), (ItkOutputImageType3::ImageDimension), itkOutputImage); \ } // the following lines allow for fixed-size vector images up to a certain size limit // (commented out for shorter compile times) // TYPE_VECS(000) // TYPE_VECS(100) // TYPE_VECS(200) // TYPE_VECS(300) // TYPE_VECS(400) // TYPE_VECS(500) // TYPE_VECS(600) // TYPE_VECS(700) // allow for fixed-size vectors of specific length // (inspired by itkPointshell.cpp, precompiled ODF configs) - // TYPE_TEN_VECS(0) // TYPE_N_VEC(11) // TYPE_N_VEC(12) TYPE_N_VEC(2) TYPE_N_VEC(3) TYPE_N_VEC(6) + TYPE_N_VEC(15) + TYPE_N_VEC(28) TYPE_N_VEC(42) + TYPE_N_VEC(45) + TYPE_N_VEC(66) + TYPE_N_VEC(91) TYPE_N_VEC(92) TYPE_N_VEC(162) TYPE_N_VEC(252) TYPE_N_VEC(362) TYPE_N_VEC(492) TYPE_N_VEC(642) TYPE_N_VEC(812) TYPE_N_VEC(1002) #ifndef DOXYGEN_SKIP #endif // DOXYGEN_SKIP } diff --git a/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp b/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp index 4e352b644e..fffc5876b3 100644 --- a/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp +++ b/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp @@ -1,389 +1,390 @@ /*=================================================================== 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 "mitkImageStatisticsHolder.h" #include "mitkHistogramGenerator.h" //#include "mitkImageTimeSelector.h" #include mitk::ImageStatisticsHolder::ImageStatisticsHolder(mitk::Image *image) : m_Image(image) /*, m_TimeSelectorForExtremaObject(nullptr)*/ { m_CountOfMinValuedVoxels.resize(1, 0); m_CountOfMaxValuedVoxels.resize(1, 0); m_ScalarMin.resize(1, itk::NumericTraits::max()); m_ScalarMax.resize(1, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.resize(1, itk::NumericTraits::max()); m_Scalar2ndMax.resize(1, itk::NumericTraits::NonpositiveMin()); mitk::HistogramGenerator::Pointer generator = mitk::HistogramGenerator::New(); m_HistogramGeneratorObject = generator; // m_Image = image; // create time selector // this->GetTimeSelector(); } mitk::ImageStatisticsHolder::~ImageStatisticsHolder() { m_HistogramGeneratorObject = nullptr; // m_TimeSelectorForExtremaObject = nullptr; // m_Image = nullptr; } const mitk::ImageStatisticsHolder::HistogramType *mitk::ImageStatisticsHolder::GetScalarHistogram( int t, unsigned int /*component*/) { mitk::ImageTimeSelector *timeSelector = this->GetTimeSelector(); if (timeSelector != nullptr) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); auto *generator = static_cast(m_HistogramGeneratorObject.GetPointer()); generator->SetImage(timeSelector->GetOutput()); generator->ComputeHistogram(); return static_cast(generator->GetHistogram()); } return nullptr; } bool mitk::ImageStatisticsHolder::IsValidTimeStep(int t) const { return m_Image->IsValidTimeStep(t); } mitk::ImageTimeSelector::Pointer mitk::ImageStatisticsHolder::GetTimeSelector() { // if(m_TimeSelectorForExtremaObject.IsNull()) //{ // m_TimeSelectorForExtremaObject = ImageTimeSelector::New(); ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); // static_cast( m_TimeSelectorForExtremaObject.GetPointer() ); timeSelector->SetInput(m_Image); //} return timeSelector; // static_cast( m_TimeSelectorForExtremaObject.GetPointer() ); } void mitk::ImageStatisticsHolder::Expand(unsigned int timeSteps) { if (!m_Image->IsValidTimeStep(timeSteps - 1)) return; // The BaseData needs to be expanded, call the mitk::Image::Expand() method m_Image->Expand(timeSteps); if (timeSteps > m_ScalarMin.size()) { m_ScalarMin.resize(timeSteps, itk::NumericTraits::max()); m_ScalarMax.resize(timeSteps, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.resize(timeSteps, itk::NumericTraits::max()); m_Scalar2ndMax.resize(timeSteps, itk::NumericTraits::NonpositiveMin()); m_CountOfMinValuedVoxels.resize(timeSteps, 0); m_CountOfMaxValuedVoxels.resize(timeSteps, 0); } } void mitk::ImageStatisticsHolder::ResetImageStatistics() { m_ScalarMin.assign(1, itk::NumericTraits::max()); m_ScalarMax.assign(1, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.assign(1, itk::NumericTraits::max()); m_Scalar2ndMax.assign(1, itk::NumericTraits::NonpositiveMin()); m_CountOfMinValuedVoxels.assign(1, 0); m_CountOfMaxValuedVoxels.assign(1, 0); } #include "mitkImageAccessByItk.h" //#define BOUNDINGOBJECT_IGNORE template void mitk::_ComputeExtremaInItkImage(const ItkImageType *itkImage, mitk::ImageStatisticsHolder *statisticsHolder, int t) { typename ItkImageType::RegionType region; region = itkImage->GetBufferedRegion(); if (region.Crop(itkImage->GetRequestedRegion()) == false) return; if (region != itkImage->GetRequestedRegion()) return; itk::ImageRegionConstIterator it(itkImage, region); typedef typename ItkImageType::PixelType TPixel; TPixel value = 0; if (statisticsHolder == nullptr || !statisticsHolder->IsValidTimeStep(t)) return; statisticsHolder->Expand(t + 1); // make sure we have initialized all arrays statisticsHolder->m_CountOfMinValuedVoxels[t] = 0; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 0; statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t] = itk::NumericTraits::max(); statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t] = itk::NumericTraits::NonpositiveMin(); while (!it.IsAtEnd()) { value = it.Get(); // if ( (value > mitkImage->m_ScalarMin) && (value < mitkImage->m_Scalar2ndMin) ) mitkImage->m_Scalar2ndMin = // value; // else if ( (value < mitkImage->m_ScalarMax) && (value > mitkImage->m_Scalar2ndMax) ) mitkImage->m_Scalar2ndMax = // value; // else if (value > mitkImage->m_ScalarMax) mitkImage->m_ScalarMax = // value; // else if (value < mitkImage->m_ScalarMin) mitkImage->m_ScalarMin = // value; // if numbers start with 2ndMin or 2ndMax and never have that value again, the previous above logic failed #ifdef BOUNDINGOBJECT_IGNORE if (value > -32765) { #endif // update min if (value < statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t]; statisticsHolder->m_ScalarMin[t] = value; statisticsHolder->m_CountOfMinValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMin[t]) { ++statisticsHolder->m_CountOfMinValuedVoxels[t]; } else if (value < statisticsHolder->m_Scalar2ndMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = value; } // update max if (value > statisticsHolder->m_ScalarMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t]; statisticsHolder->m_ScalarMax[t] = value; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMax[t]) { ++statisticsHolder->m_CountOfMaxValuedVoxels[t]; } else if (value > statisticsHolder->m_Scalar2ndMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = value; } #ifdef BOUNDINGOBJECT_IGNORE } #endif ++it; } //// guard for wrong 2dMin/Max on single constant value images if (statisticsHolder->m_ScalarMax[t] == statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMax[t]; } statisticsHolder->m_LastRecomputeTimeStamp.Modified(); // MITK_DEBUG <<"extrema "<::NonpositiveMin()<<" "<m_ScalarMin<<" // "<m_Scalar2ndMin<<" "<m_Scalar2ndMax<<" "<m_ScalarMax<<" // "<::max(); } template void mitk::_ComputeExtremaInItkVectorImage(const ItkImageType *itkImage, mitk::ImageStatisticsHolder *statisticsHolder, int t, unsigned int component) { typename ItkImageType::RegionType region; region = itkImage->GetBufferedRegion(); if (region.Crop(itkImage->GetRequestedRegion()) == false) return; if (region != itkImage->GetRequestedRegion()) return; itk::ImageRegionConstIterator it(itkImage, region); if (statisticsHolder == nullptr || !statisticsHolder->IsValidTimeStep(t)) return; statisticsHolder->Expand(t + 1); // make sure we have initialized all arrays statisticsHolder->m_CountOfMinValuedVoxels[t] = 0; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 0; statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t] = itk::NumericTraits::max(); statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t] = itk::NumericTraits::NonpositiveMin(); while (!it.IsAtEnd()) { double value = it.Get()[component]; // if ( (value > mitkImage->m_ScalarMin) && (value < mitkImage->m_Scalar2ndMin) ) mitkImage->m_Scalar2ndMin = // value; // else if ( (value < mitkImage->m_ScalarMax) && (value > mitkImage->m_Scalar2ndMax) ) mitkImage->m_Scalar2ndMax = // value; // else if (value > mitkImage->m_ScalarMax) mitkImage->m_ScalarMax = // value; // else if (value < mitkImage->m_ScalarMin) mitkImage->m_ScalarMin = // value; // if numbers start with 2ndMin or 2ndMax and never have that value again, the previous above logic failed #ifdef BOUNDINGOBJECT_IGNORE if (value > -32765) { #endif // update min if (value < statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t]; statisticsHolder->m_ScalarMin[t] = value; statisticsHolder->m_CountOfMinValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMin[t]) { ++statisticsHolder->m_CountOfMinValuedVoxels[t]; } else if (value < statisticsHolder->m_Scalar2ndMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = value; } // update max if (value > statisticsHolder->m_ScalarMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t]; statisticsHolder->m_ScalarMax[t] = value; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMax[t]) { ++statisticsHolder->m_CountOfMaxValuedVoxels[t]; } else if (value > statisticsHolder->m_Scalar2ndMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = value; } #ifdef BOUNDINGOBJECT_IGNORE } #endif ++it; } //// guard for wrong 2dMin/Max on single constant value images if (statisticsHolder->m_ScalarMax[t] == statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMax[t]; } statisticsHolder->m_LastRecomputeTimeStamp.Modified(); // MITK_DEBUG <<"extrema "<::NonpositiveMin()<<" "<m_ScalarMin<<" // "<m_Scalar2ndMin<<" "<m_Scalar2ndMax<<" "<m_ScalarMax<<" // "<::max(); } void mitk::ImageStatisticsHolder::ComputeImageStatistics(int t, unsigned int component) { // timestep valid? if (!m_Image->IsValidTimeStep(t)) return; // image modified? if (this->m_Image->GetMTime() > m_LastRecomputeTimeStamp.GetMTime()) this->ResetImageStatistics(); Expand(t + 1); // do we have valid information already? if (m_ScalarMin[t] != itk::NumericTraits::max() || m_Scalar2ndMin[t] != itk::NumericTraits::max()) return; // Values already calculated before... // used to avoid statistics calculation on Odf images. property will be replaced as soons as bug 17928 is merged and // the diffusion image refactoring is complete. + mitk::BoolProperty *isSh = dynamic_cast(m_Image->GetProperty("IsShImage").GetPointer()); mitk::BoolProperty *isOdf = dynamic_cast(m_Image->GetProperty("IsOdfImage").GetPointer()); const mitk::PixelType pType = m_Image->GetPixelType(0); if (pType.GetNumberOfComponents() == 1 && (pType.GetPixelType() != itk::ImageIOBase::UNKNOWNPIXELTYPE) && (pType.GetPixelType() != itk::ImageIOBase::VECTOR)) { // recompute mitk::ImageTimeSelector::Pointer timeSelector = this->GetTimeSelector(); if (timeSelector.IsNotNull()) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); const mitk::Image *image = timeSelector->GetOutput(); AccessByItk_2(image, _ComputeExtremaInItkImage, this, t); } } else if (pType.GetPixelType() == itk::ImageIOBase::VECTOR && - (!isOdf || !isOdf->GetValue())) // we have a vector image + (!isOdf || !isOdf->GetValue()) && (!isSh || !isSh->GetValue())) // we have a vector image { // recompute mitk::ImageTimeSelector::Pointer timeSelector = this->GetTimeSelector(); if (timeSelector.IsNotNull()) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); const mitk::Image *image = timeSelector->GetOutput(); AccessVectorPixelTypeByItk_n(image, _ComputeExtremaInItkVectorImage, (this, t, component)); } } else { m_ScalarMin[t] = 0; m_ScalarMax[t] = 255; m_Scalar2ndMin[t] = 0; m_Scalar2ndMax[t] = 255; } } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValueMin(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_ScalarMin[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValueMax(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_ScalarMax[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValue2ndMin(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_Scalar2ndMin[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValue2ndMax(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_Scalar2ndMax[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetCountOfMinValuedVoxels(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_CountOfMinValuedVoxels[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetCountOfMaxValuedVoxels(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_CountOfMaxValuedVoxels[t]; } diff --git a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp index 6d8667287a..08c201f25f 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp @@ -1,622 +1,624 @@ /*=================================================================== 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 "mitkLevelWindowManager.h" #include "mitkDataStorage.h" #include "mitkImage.h" #include "mitkMessage.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include "mitkRenderingModeProperty.h" #include mitk::LevelWindowManager::LevelWindowManager() : m_DataStorage(nullptr), m_LevelWindowProperty(nullptr), m_AutoTopMost(true), m_IsObserverTagSet(false), m_CurrentImage(nullptr), m_IsPropertyModifiedTagSet(false), m_SettingImgForLvlWinProp(false) { } mitk::LevelWindowManager::~LevelWindowManager() { if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); m_DataStorage = nullptr; } if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } // clear both observer maps this->ClearPropObserverLists(); } void mitk::LevelWindowManager::SetDataStorage(mitk::DataStorage *ds) { if (ds == nullptr) return; /* remove listeners of old DataStorage */ if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); } /* register listener for new DataStorage */ m_DataStorage = ds; // register m_DataStorage->AddNodeEvent.AddListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.AddListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); this->DataStorageAddedNode(); // update us with new DataStorage } void mitk::LevelWindowManager::OnPropertyModified(const itk::EventObject &) { Modified(); } void mitk::LevelWindowManager::SetAutoTopMostImage(bool autoTopMost, const mitk::DataNode *removedNode) { m_AutoTopMost = autoTopMost; if (m_AutoTopMost == false) return; if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } /* search topmost image */ if (m_DataStorage.IsNull()) { itkExceptionMacro("DataStorage not set"); } int maxLayer = itk::NumericTraits::min(); m_LevelWindowProperty = nullptr; mitk::DataNode::Pointer topLevelNode; mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if (node.IsNull() || (removedNode != nullptr && node == removedNode)) continue; m_SettingImgForLvlWinProp = true; node->SetBoolProperty("imageForLevelWindow", false); m_SettingImgForLvlWinProp = false; if (node->IsVisible(nullptr) == false) continue; int layer = 0; node->GetIntProperty("layer", layer); if (layer < maxLayer) continue; mitk::LevelWindowProperty::Pointer levelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); if (levelWindowProperty.IsNull()) continue; int nonLvlWinMode1 = mitk::RenderingModeProperty::LOOKUPTABLE_COLOR; int nonLvlWinMode2 = mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(node->GetProperty("Image Rendering.Mode")); if (mode.IsNotNull()) { int currMode = mode->GetRenderingMode(); if (currMode == nonLvlWinMode1 || currMode == nonLvlWinMode2) { continue; } } else continue; m_LevelWindowProperty = levelWindowProperty; m_CurrentImage = dynamic_cast(node->GetData()); topLevelNode = node; maxLayer = layer; } if (topLevelNode.IsNotNull()) { m_SettingImgForLvlWinProp = true; topLevelNode->SetBoolProperty("imageForLevelWindow", true); m_SettingImgForLvlWinProp = false; } this->SetLevelWindowProperty(m_LevelWindowProperty); if (m_LevelWindowProperty.IsNull()) { Modified(); } // else SetLevelWindowProperty will call Modified(); } // sets an specific LevelWindowProperty, all changes will affect the image belonging to this property. void mitk::LevelWindowManager::SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty) { if (levelWindowProperty.IsNull()) return; /* search image than belongs to the property */ typedef mitk::DataStorage::SetOfObjects NodeSetType; NodeSetType::ConstPointer nodes = m_DataStorage->GetAll(); NodeSetType::ConstIterator it = nodes->Begin(); mitk::DataNode::Pointer propNode = nullptr; while (it != nodes->End()) { mitk::DataNode::Pointer node = it.Value(); mitk::LevelWindowProperty::Pointer prop = dynamic_cast(node->GetProperty("levelwindow")); if (prop == levelWindowProperty) { propNode = node; } else { m_SettingImgForLvlWinProp = true; node->SetBoolProperty("imageForLevelWindow", false); m_SettingImgForLvlWinProp = false; } ++it; } if (propNode.IsNull()) { mitkThrow() << "No Image in DataStorage that belongs to LevelWindow property" << m_LevelWindowProperty; } if (m_IsPropertyModifiedTagSet) // remove listener for old property { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } m_LevelWindowProperty = levelWindowProperty; itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); // register listener for new property command->SetCallbackFunction(this, &LevelWindowManager::OnPropertyModified); m_PropertyModifiedTag = m_LevelWindowProperty->AddObserver(itk::ModifiedEvent(), command); m_IsPropertyModifiedTagSet = true; m_CurrentImage = dynamic_cast(propNode->GetData()); m_SettingImgForLvlWinProp = true; propNode->SetBoolProperty("imageForLevelWindow", true); m_SettingImgForLvlWinProp = false; this->Modified(); } // returns the current mitkLevelWindowProperty object from the image that is affected by changes mitk::LevelWindowProperty::Pointer mitk::LevelWindowManager::GetLevelWindowProperty() { return m_LevelWindowProperty; } // returns Level/Window values for the current image const mitk::LevelWindow &mitk::LevelWindowManager::GetLevelWindow() { if (m_LevelWindowProperty.IsNotNull()) { return m_LevelWindowProperty->GetLevelWindow(); } else { itkExceptionMacro("No LevelWindow available!"); } } // sets new Level/Window values and informs all listeners about changes void mitk::LevelWindowManager::SetLevelWindow(const mitk::LevelWindow &levelWindow) { if (m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->SetLevelWindow(levelWindow); } this->Modified(); } void mitk::LevelWindowManager::DataStorageAddedNode(const mitk::DataNode *) { // update observers with new data storage UpdateObservers(); // Initialize LevelWindowsManager to new image SetAutoTopMostImage(true); // check if everything is still ok if ((m_PropObserverToNode.size() != m_PropObserverToNode2.size()) || (m_PropObserverToNode2.size() != this->GetRelevantNodes()->size())) { mitkThrow() << "Wrong number of observers in Level Window Manager!"; } } void mitk::LevelWindowManager::DataStorageRemovedNode(const mitk::DataNode *removedNode) { // first: check if deleted node is part of relevant nodes. If not, abort method because there is no need change // anything. if ((this->GetRelevantNodes()->size() == 0)) return; bool removedNodeIsRelevant = false; /* Iterator code: is crashing, don't know why... so using for loop for (mitk::DataStorage::SetOfObjects::ConstIterator it = this->GetRelevantNodes()->Begin(); it != this->GetRelevantNodes()->End(); ++it) {if (it->Value() == removedNode) {removedNodeIsRelevant=true;}}*/ for (unsigned int i = 0; i < this->GetRelevantNodes()->size(); i++) { if (this->GetRelevantNodes()->at(i) == removedNode) { removedNodeIsRelevant = true; } } if (!removedNodeIsRelevant) return; // remember node which will be removed m_NodeMarkedToDelete = removedNode; // update observers UpdateObservers(); /* search image than belongs to the property */ if (m_LevelWindowProperty.IsNull()) { SetAutoTopMostImage(true, removedNode); } else { mitk::NodePredicateProperty::Pointer p2 = mitk::NodePredicateProperty::New("levelwindow", m_LevelWindowProperty); mitk::DataNode *n = m_DataStorage->GetNode(p2); if (n == nullptr || m_AutoTopMost) // if node was deleted, change our behaviour to AutoTopMost, if AutoTopMost is true // change level window to topmost node { SetAutoTopMostImage(true, removedNode); } } // reset variable m_NodeMarkedToDelete = nullptr; // check if everything is still ok if ((m_PropObserverToNode.size() != m_PropObserverToNode2.size()) || (m_PropObserverToNode2.size() != (this->GetRelevantNodes()->size() - 1))) { mitkThrow() << "Wrong number of observers in Level Window Manager!"; } } void mitk::LevelWindowManager::UpdateObservers() { this->ClearPropObserverLists(); // remove old observers CreatePropObserverLists(); // create new observer lists } int mitk::LevelWindowManager::GetNumberOfObservers() { return m_PropObserverToNode.size(); } mitk::DataStorage *mitk::LevelWindowManager::GetDataStorage() { return m_DataStorage.GetPointer(); } // true if changes on slider or line-edits will affect always the topmost layer image bool mitk::LevelWindowManager::isAutoTopMost() { return m_AutoTopMost; } void mitk::LevelWindowManager::RecaluclateLevelWindowForSelectedComponent(const itk::EventObject &event) { mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if (node.IsNull()) continue; bool isSelected = false; node->GetBoolProperty("selected", isSelected); if (isSelected) { mitk::LevelWindow selectedLevelWindow; node->GetLevelWindow(selectedLevelWindow); // node is an image node because of predicates auto *image = dynamic_cast(node->GetData()); int displayedComponent = 0; if (image && (node->GetIntProperty("Image.Displayed Component", displayedComponent))) { // we found a selected image with a displayed component // let's recalculate the levelwindow for this. selectedLevelWindow.SetAuto(image, true, true, static_cast(displayedComponent)); node->SetLevelWindow(selectedLevelWindow); } } mitk::LevelWindow levelWindow; node->GetLevelWindow(levelWindow); } this->Update(event); } void mitk::LevelWindowManager::Update(const itk::EventObject &) // visible property of a image has changed { if (m_SettingImgForLvlWinProp) // no mutex, should still help { return; } if (m_AutoTopMost) { SetAutoTopMostImage(true); return; } int maxVisibleLayer = itk::NumericTraits::min(); mitk::DataNode::Pointer highestVisible = nullptr; std::vector visProbNodes; mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if (node.IsNull()) { continue; } bool visible = node->IsVisible(nullptr); if (node->IsVisible(nullptr)) { int layer = -1; node->GetIntProperty("layer", layer); if (layer > maxVisibleLayer) { maxVisibleLayer = layer; highestVisible = node; } } bool prop = false; node->GetBoolProperty("imageForLevelWindow", prop); if (prop && visible) { visProbNodes.push_back(node); } } int numVisProbNodes = visProbNodes.size(); if (numVisProbNodes > 2) { MITK_ERROR << "Error: not more than two visible nodes are expected to have the imageForLevelWindow property set at " "any point."; } else if (numVisProbNodes == 2) { for (std::vector::const_iterator it = visProbNodes.begin(); it != visProbNodes.end(); ++it) { mitk::LevelWindowProperty::Pointer newProp = dynamic_cast((*it)->GetProperty("levelwindow")); if (newProp != m_LevelWindowProperty) { this->SetLevelWindowProperty(newProp); return; } } } else if (numVisProbNodes == 1) { mitk::LevelWindowProperty::Pointer newProp = dynamic_cast(visProbNodes[0]->GetProperty("levelwindow")); if (newProp != m_LevelWindowProperty) { this->SetLevelWindowProperty(newProp); return; } } else if (highestVisible) { mitk::LevelWindowProperty::Pointer lvlProp = dynamic_cast(highestVisible->GetProperty("levelwindow")); this->SetLevelWindowProperty(lvlProp); } else { Modified(); } } mitk::DataStorage::SetOfObjects::ConstPointer mitk::LevelWindowManager::GetRelevantNodes() { if (m_DataStorage.IsNull()) return mitk::DataStorage::SetOfObjects::ConstPointer(mitk::DataStorage::SetOfObjects::New()); // return empty set mitk::NodePredicateProperty::Pointer notBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(false)); mitk::NodePredicateProperty::Pointer hasLevelWindow = mitk::NodePredicateProperty::New("levelwindow", nullptr); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateDataType::Pointer isDImage = mitk::NodePredicateDataType::New("DiffusionImage"); mitk::NodePredicateDataType::Pointer isTImage = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isOdfImage = mitk::NodePredicateDataType::New("OdfImage"); + mitk::NodePredicateDataType::Pointer isShImage = mitk::NodePredicateDataType::New("ShImage"); mitk::NodePredicateOr::Pointer predicateTypes = mitk::NodePredicateOr::New(); predicateTypes->AddPredicate(isImage); predicateTypes->AddPredicate(isDImage); predicateTypes->AddPredicate(isTImage); predicateTypes->AddPredicate(isOdfImage); + predicateTypes->AddPredicate(isShImage); mitk::NodePredicateAnd::Pointer predicate = mitk::NodePredicateAnd::New(); predicate->AddPredicate(notBinary); predicate->AddPredicate(hasLevelWindow); predicate->AddPredicate(predicateTypes); mitk::DataStorage::SetOfObjects::ConstPointer relevantNodes = m_DataStorage->GetSubset(predicate); return relevantNodes; } mitk::Image *mitk::LevelWindowManager::GetCurrentImage() { return m_CurrentImage; } void mitk::LevelWindowManager::ClearPropObserverLists() { for (auto iter = m_PropObserverToNode.begin(); iter != m_PropObserverToNode.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_PropObserverToNode.clear(); for (auto iter = m_PropObserverToNode2.begin(); iter != m_PropObserverToNode2.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_PropObserverToNode2.clear(); for (auto iter = m_PropObserverToNode3.begin(); iter != m_PropObserverToNode3.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_PropObserverToNode3.clear(); for (auto iter = m_PropObserverToNode4.begin(); iter != m_PropObserverToNode4.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_PropObserverToNode4.clear(); for (auto iter = m_PropObserverToNode5.begin(); iter != m_PropObserverToNode5.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_PropObserverToNode5.clear(); } void mitk::LevelWindowManager::CreatePropObserverLists() { if (m_DataStorage.IsNull()) // check if data storage is set { itkExceptionMacro("DataStorage not set"); } /* add observers for all relevant nodes */ mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { if ((it->Value().IsNull()) || (it->Value() == m_NodeMarkedToDelete)) { continue; } /* register listener for changes in visible property */ itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long visIdx = it->Value()->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command); m_PropObserverToNode[PropDataPair(visIdx, it->Value())] = it->Value()->GetProperty("visible"); /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long layerIdx = it->Value()->GetProperty("layer")->AddObserver(itk::ModifiedEvent(), command2); m_PropObserverToNode2[PropDataPair(layerIdx, it->Value())] = it->Value()->GetProperty("layer"); /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command3 = itk::ReceptorMemberCommand::New(); command3->SetCallbackFunction(this, &LevelWindowManager::Update); mitk::BaseProperty::Pointer imageRenderingMode = it->Value()->GetProperty("Image Rendering.Mode"); if (imageRenderingMode.IsNotNull()) { unsigned long rendIdx = imageRenderingMode->AddObserver(itk::ModifiedEvent(), command3); m_PropObserverToNode3[PropDataPair(rendIdx, it->Value())] = imageRenderingMode.GetPointer(); } itk::ReceptorMemberCommand::Pointer command4 = itk::ReceptorMemberCommand::New(); command4->SetCallbackFunction(this, &LevelWindowManager::RecaluclateLevelWindowForSelectedComponent); mitk::BaseProperty::Pointer displayedImageComponent = it->Value()->GetProperty("Image.Displayed Component"); if (displayedImageComponent.IsNotNull()) { unsigned long dispIdx = displayedImageComponent->AddObserver(itk::ModifiedEvent(), command4); m_PropObserverToNode4[PropDataPair(dispIdx, it->Value())] = displayedImageComponent.GetPointer(); } itk::ReceptorMemberCommand::Pointer command5 = itk::ReceptorMemberCommand::New(); command5->SetCallbackFunction(this, &LevelWindowManager::Update); mitk::BaseProperty::Pointer imgForLvlWin = it->Value()->GetProperty("imageForLevelWindow"); if (imgForLvlWin.IsNull()) { it->Value()->SetBoolProperty("imageForLevelWindow", false); imgForLvlWin = it->Value()->GetProperty("imageForLevelWindow"); } unsigned long lvlWinIdx = imgForLvlWin->AddObserver(itk::ModifiedEvent(), command5); m_PropObserverToNode5[PropDataPair(lvlWinIdx, it->Value())] = it->Value()->GetProperty("imageForLevelWindow"); } } diff --git a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp index 853cd1a06c..9c27887369 100644 --- a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp @@ -1,1130 +1,1130 @@ /*=================================================================== 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. ===================================================================*/ // MITK #include #include #include #include #include #include #include #include #include #include #include //#include #include "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" #include // MITK Rendering #include "mitkImageVtkMapper2D.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkNeverTranslucentTexture.h" // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ITK #include #include mitk::ImageVtkMapper2D::ImageVtkMapper2D() { } mitk::ImageVtkMapper2D::~ImageVtkMapper2D() { // The 3D RW Mapper (PlaneGeometryDataVtkMapper3D) is listening to this event, // in order to delete the images from the 3D RW. this->InvokeEvent(itk::DeleteEvent()); } // set the two points defining the textured plane according to the dimension and spacing void mitk::ImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float depth = this->CalculateLayerDepth(renderer); // Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct // plane size in crosshair rotation and swivel mode. localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth); // These two points define the axes of the plane in combination with the origin. // Point 1 is the x-axis and point 2 the y-axis. // Each plane is transformed according to the view (axial, coronal and saggital) afterwards. localStorage->m_Plane->SetPoint1(planeBounds[1], planeBounds[2], depth); // P1: (xMax, yMin, depth) localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); // P2: (xMin, yMax, depth) } float mitk::ImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer *renderer) { // get the clipping range to check how deep into z direction we can render images double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1]; // Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined float depth = -maxRange * 0.01; // divide by 100 int layer = 0; GetDataNode()->GetIntProperty("layer", layer, renderer); // add the layer property for each image to render images with a higher layer on top of the others depth += layer * 10; //*10: keep some room for each image (e.g. for ODFs in between) if (depth > 0.0f) { depth = 0.0f; MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead."; } return depth; } const mitk::Image *mitk::ImageVtkMapper2D::GetInput(void) { return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::ImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } void mitk::ImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); SetVtkMapperImmediateModeRendering(localStorage->m_Mapper); auto *image = const_cast(this->GetInput()); mitk::DataNode *datanode = this->GetDataNode(); if (nullptr == image || !image->IsInitialized()) { return; } // check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (nullptr == worldGeometry || !worldGeometry->IsValid() || !worldGeometry->HasReferenceGeometry()) { return; } image->Update(); // early out if there is no intersection of the current rendering geometry // and the geometry of the image that is to be rendered. if (!RenderingGeometryIntersectsImage(worldGeometry, image->GetSlicedGeometry())) { // set image to nullptr, to clear the texture in 3D, because // the latest image is used there if the plane is out of the geometry // see bug-13275 localStorage->m_ReslicedImage = nullptr; localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData); return; } // set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(image); localStorage->m_Reslicer->SetWorldGeometry(worldGeometry); localStorage->m_Reslicer->SetTimeStep(this->GetTimestep()); // set the transformation of the image to adapt reslice axis localStorage->m_Reslicer->SetResliceTransformByGeometry( image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())); // is the geometry of the slice based on the input image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ((image->GetDimension() >= 3) && (image->GetDimension(2) > 1)) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation", renderer); int interpolationMode = VTK_RESLICE_NEAREST; if (resliceInterpolationProperty != nullptr) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch (interpolationMode) { case VTK_RESLICE_NEAREST: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); break; case VTK_RESLICE_LINEAR: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR); break; case VTK_RESLICE_CUBIC: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC); break; } } else { localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); } // set the vtk output property to true, makes sure that no unneeded mitk image convertion // is done. localStorage->m_Reslicer->SetVtkOutputRequest(true); // Thickslicing int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if (image->GetPixelType().GetNumberOfComponents() == 1) // for now only single component are allowed { DataNode *dn = renderer->GetCurrentWorldPlaneGeometryNode(); if (dn) { ResliceMethodProperty *resliceMethodEnumProperty = nullptr; if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices", renderer) && resliceMethodEnumProperty) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty = nullptr; if (dn->GetProperty(intProperty, "reslice.thickslices.num", renderer) && intProperty) { thickSlicesNum = intProperty->GetValue(); if (thickSlicesNum < 1) thickSlicesNum = 1; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } const auto *planeGeometry = dynamic_cast(worldGeometry); if (thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; const auto *abstractGeometry = dynamic_cast(worldGeometry); if (abstractGeometry != nullptr) normal = abstractGeometry->GetPlane()->GetNormal(); else { if (planeGeometry != nullptr) { normal = planeGeometry->GetNormal(); } else return; // no fitting geometry set } normal.Normalize(); image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())->WorldToIndex(normal, normInIndex); dataZSpacing = 1.0 / normInIndex.GetNorm(); localStorage->m_Reslicer->SetOutputDimensionality(3); localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing); localStorage->m_Reslicer->SetOutputExtentZDirection(-thickSlicesNum, 0 + thickSlicesNum); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. localStorage->m_TSFilter->SetThickSliceMode(thickSlicesMode - 1); localStorage->m_TSFilter->SetInputData(localStorage->m_Reslicer->GetVtkOutput()); // vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually localStorage->m_Reslicer->Modified(); localStorage->m_Reslicer->Update(); localStorage->m_TSFilter->Modified(); localStorage->m_TSFilter->Update(); localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput(); } else { // this is needed when thick mode was enable bevore. These variable have to be reset to default values localStorage->m_Reslicer->SetOutputDimensionality(2); localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0); localStorage->m_Reslicer->SetOutputExtentZDirection(0, 0); localStorage->m_Reslicer->Modified(); // start the pipeline with updating the largest possible, needed if the geometry of the input has changed localStorage->m_Reslicer->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput(); } // Bounds information for reslicing (only reuqired if reference geometry // is present) // this used for generating a vtkPLaneSource with the right size double sliceBounds[6]; for (auto &sliceBound : sliceBounds) { sliceBound = 0.0; } localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds); // get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing(); // calculate minimum bounding rect of IMAGE in texture { double textureClippingBounds[6]; for (auto &textureClippingBound : textureClippingBounds) { textureClippingBound = 0.0; } // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. mitk::PlaneClipping::CalculateClippedPlaneBounds(image->GetGeometry(), planeGeometry, textureClippingBounds); textureClippingBounds[0] = static_cast(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[1] = static_cast(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[2] = static_cast(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5); textureClippingBounds[3] = static_cast(textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5); // clipping bounds for cutting the image localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds); } // get the number of scalar components to distinguish between different image types int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents(); // get the binary property bool binary = false; bool binaryOutline = false; datanode->GetBoolProperty("binary", binary, renderer); if (binary) // binary image { datanode->GetBoolProperty("outline binary", binaryOutline, renderer); if (binaryOutline) // contour rendering { // get pixel type of vtk image itk::ImageIOBase::IOComponentType componentType = static_cast(image->GetPixelType().GetComponentType()); switch (componentType) { case itk::ImageIOBase::UCHAR: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; case itk::ImageIOBase::USHORT: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; default: binaryOutline = false; this->ApplyLookuptable(renderer); MITK_WARN << "Type of all binary images should be unsigned char or unsigned short. Outline does not work on other pixel types!"; } if (binaryOutline) // binary outline is still true --> add outline { float binaryOutlineWidth = 1.0; if (datanode->GetFloatProperty("outline width", binaryOutlineWidth, renderer)) { if (localStorage->m_Actors->GetNumberOfPaths() > 1) { float binaryOutlineShadowWidth = 1.5; datanode->GetFloatProperty("outline shadow width", binaryOutlineShadowWidth, renderer); dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty() ->SetLineWidth(binaryOutlineWidth * binaryOutlineShadowWidth); } localStorage->m_Actor->GetProperty()->SetLineWidth(binaryOutlineWidth); } } } else // standard binary image { if (numberOfComponents != 1) { MITK_ERROR << "Rendering Error: Binary Images with more then 1 component are not supported!"; } } } this->ApplyOpacity(renderer); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->MapColorScalarsThroughLookupTableOff(); int displayedComponent = 0; if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1) { localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent); localStorage->m_VectorComponentExtractor->SetInputData(localStorage->m_ReslicedImage); localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0)); } else { // connect the input with the levelwindow filter localStorage->m_LevelWindowFilter->SetInputData(localStorage->m_ReslicedImage); } // check for texture interpolation property bool textureInterpolation = false; GetDataNode()->GetBoolProperty("texture interpolation", textureInterpolation, renderer); // set the interpolation modus according to the property localStorage->m_Texture->SetInterpolate(textureInterpolation); // connect the texture with the output of the levelwindow filter localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); this->TransformActor(renderer); auto *contourShadowActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); if (binary && binaryOutline) // connect the mapper with the polyData which contains the lines { // We need the contour for the binary outline property as actor localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData); localStorage->m_Actor->SetTexture(nullptr); // no texture for contours bool binaryOutlineShadow = false; datanode->GetBoolProperty("outline binary shadow", binaryOutlineShadow, renderer); if (binaryOutlineShadow) { contourShadowActor->SetVisibility(true); } else { contourShadowActor->SetVisibility(false); } } else { // Connect the mapper with the input texture. This is the standard case. // setup the textured plane this->GeneratePlane(renderer, sliceBounds); // set the plane as input for the mapper localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort()); // set the texture for the actor localStorage->m_Actor->SetTexture(localStorage->m_Texture); contourShadowActor->SetVisibility(false); } // We have been modified => save this for next Update() localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); LevelWindow levelWindow; this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelwindow"); localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); mitk::LevelWindow opacLevelWindow; if (this->GetDataNode()->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow")) { // pass the opaque level window to the filter localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { // no opaque level window localStorage->m_LevelWindowFilter->SetMinOpacity(0.0); localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0); } } void mitk::ImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float rgb[3] = {1.0f, 1.0f, 1.0f}; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection bool hover = false; bool selected = false; bool binary = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary && hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.hoveringcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (binary && selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.selectedcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (!binary || (!hover && !selected)) { GetDataNode()->GetColor(rgb, renderer, "color"); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); localStorage->m_Actor->GetProperty()->SetColor(rgbConv); if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1) { float rgb[3] = {1.0f, 1.0f, 1.0f}; mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("outline binary shadow color", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); } } void mitk::ImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float opacity = 1.0f; // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(opacity, renderer, "opacity"); // set the opacity according to the properties localStorage->m_Actor->GetProperty()->SetOpacity(opacity); if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1) { dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty() ->SetOpacity(opacity); } } void mitk::ImageVtkMapper2D::ApplyRenderingMode(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); bool binary = false; this->GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary) // is it a binary image? { // for binary images, we always use our default LuT and map every value to (0,1) // the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window. localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable); } else { // all other image types can make use of the rendering mode int renderingMode = mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(this->GetDataNode()->GetProperty("Image Rendering.Mode", renderer)); if (mode.IsNotNull()) { renderingMode = mode->GetRenderingMode(); } switch (renderingMode) { case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color"; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color"; this->ApplyLookuptable(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); break; default: MITK_ERROR << "No valid 'Image Rendering.Mode' set. Using LOOKUPTABLE_LEVELWINDOW_COLOR instead."; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; } } // we apply color for all images (including binaries). this->ApplyColor(renderer); } void mitk::ImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); vtkLookupTable *usedLookupTable = localStorage->m_ColorLookupTable; // If lookup table or transferfunction use is requested... mitk::LookupTableProperty::Pointer lookupTableProp = dynamic_cast(this->GetDataNode()->GetProperty("LookupTable")); if (lookupTableProp.IsNotNull()) // is a lookuptable set? { usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); } else { //"Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'. // A default (rainbow) lookup table will be used. // Here have to do nothing. Warning for the user has been removed, due to unwanted console output // in every interation of the rendering. } localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable); } void mitk::ImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer) { mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast( this->GetDataNode()->GetProperty("Image Rendering.Transfer Function", renderer)); if (transferFunctionProp.IsNull()) { MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image " "Rendering.Transfer Function'. Nothing will be done."; return; } LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // pass the transfer function to our level window filter localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction()); localStorage->m_LevelWindowFilter->SetOpacityPiecewiseFunction( transferFunctionProp->GetValue()->GetScalarOpacityFunction()); } void mitk::ImageVtkMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } auto *data = const_cast(this->GetInput()); if (data == nullptr) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(this->GetTimestep()))) { return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) // was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) // Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty("depthOffset", mitk::FloatProperty::New(0.0), renderer, overwrite); node->AddProperty("outline binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline width", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty("outline binary shadow", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline binary shadow color", ColorProperty::New(0.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("outline shadow width", mitk::FloatProperty::New(1.5), renderer, overwrite); if (image->IsRotated()) node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC)); else node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New()); node->AddProperty("texture interpolation", mitk::BoolProperty::New(false)); node->AddProperty("in plane resample extent by geometry", mitk::BoolProperty::New(false)); node->AddProperty("bounding box", mitk::BoolProperty::New(false)); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(); node->AddProperty("Image Rendering.Mode", renderingModeProperty); // Set default grayscale look-up table mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); mitkLut->SetType(mitk::LookupTable::GRAYSCALE); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed if (node->GetStringProperty("dicom.pixel.PhotometricInterpretation", photometricInterpretation)) { // modality provided by DICOM or other reader if (photometricInterpretation.find("MONOCHROME1") != std::string::npos) // meaning: display MINIMUM pixels as WHITE { // Set inverse grayscale look-up table mitkLut->SetType(mitk::LookupTable::INVERSE_GRAYSCALE); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); renderingModeProperty->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); // USE lookuptable } // Otherwise do nothing - the default grayscale look-up table has already been set } bool isBinaryImage(false); if (!node->GetBoolProperty("binary", isBinaryImage) && image->GetPixelType().GetNumberOfComponents() == 1) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2) / 2); sliceSelector->SetTimeNr(image->GetDimension(3) / 2); sliceSelector->SetChannelNr(image->GetDimension(4) / 2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); if (centralSliceImage.IsNotNull() && centralSliceImage->IsInitialized()) { minValue = centralSliceImage->GetStatistics()->GetScalarValueMin(); maxValue = centralSliceImage->GetStatistics()->GetScalarValueMax(); min2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMin(); max2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMax(); } if ((maxValue == min2ndValue && minValue == max2ndValue) || minValue == maxValue) { // centralSlice is strange, lets look at all data minValue = image->GetStatistics()->GetScalarValueMin(); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } isBinaryImage = (maxValue == min2ndValue && minValue == max2ndValue); } std::string className = image->GetNameOfClass(); - if (className != "TensorImage" && className != "OdfImage") + if (className != "TensorImage" && className != "OdfImage" && className != "ShImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); if ((pixelType.GetPixelType() == itk::ImageIOBase::VECTOR && numComponents > 1) || numComponents == 2 || numComponents > 4) { node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite); } } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty("opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 1.0, 1.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); } if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr)) { /* initialize level/window from DICOM tags */ std::string sLevel = ""; std::string sWindow = ""; if (GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1050, "dicom.voilut.WindowCenter", image->GetPropertyList(), sLevel) && GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1051, "dicom.voilut.WindowWidth", image->GetPropertyList(), sWindow)) { float level = atof(sLevel.c_str()); float window = atof(sWindow.c_str()); mitk::LevelWindow contrast; std::string sSmallestPixelValueInSeries; std::string sLargestPixelValueInSeries; if (GetBackwardsCompatibleDICOMProperty(0x0028, 0x0108, "dicom.series.SmallestPixelValueInSeries", image->GetPropertyList(), sSmallestPixelValueInSeries) && GetBackwardsCompatibleDICOMProperty(0x0028, 0x0109, "dicom.series.LargestPixelValueInSeries", image->GetPropertyList(), sLargestPixelValueInSeries)) { float smallestPixelValueInSeries = atof(sSmallestPixelValueInSeries.c_str()); float largestPixelValueInSeries = atof(sLargestPixelValueInSeries.c_str()); contrast.SetRangeMinMax(smallestPixelValueInSeries - 1, largestPixelValueInSeries + 1); // why not a little buffer? // might remedy some l/w widget challenges } else { contrast.SetAuto(static_cast(node->GetData()), false, true); // we need this as a fallback } contrast.SetLevelWindow(level, window, true); node->SetProperty("levelwindow", LevelWindowProperty::New(contrast), renderer); } } if (((overwrite) || (node->GetProperty("opaclevelwindow", renderer) == nullptr)) && (image->GetPixelType().GetPixelType() == itk::ImageIOBase::RGBA) && (image->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR)) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0, 255); opaclevwin.SetWindowBounds(0, 255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty("opaclevelwindow", prop, renderer); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::ImageVtkMapper2D::LocalStorage *mitk::ImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } template vtkSmartPointer mitk::ImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); // get the min and max index values of each direction int *extent = localStorage->m_ReslicedImage->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int *dims = localStorage->m_ReslicedImage->GetDimensions(); // dimensions of the image int line = dims[0]; // how many pixels per line? int x = xMin; // pixel index x int y = yMin; // pixel index y // get the depth for each contour float depth = CalculateLayerDepth(renderer); vtkSmartPointer points = vtkSmartPointer::New(); // the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); // the lines to connect the points // We take the pointer to the first pixel of the image auto* currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer()); while (y <= yMax) { // if the current pixel value is set to something if ((currentPixel) && (*currentPixel != 0)) { // check in which direction a line is necessary // a line is added if the neighbor of the current pixel has the value 0 // and if the pixel is located at the edge of the image // if vvvvv not the first line vvvvv if (y > yMin && *(currentPixel - line) == 0) { // x direction - bottom edge of the pixel // add the 2 points vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); // add the line between both points lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel + line) == 0) { // x direction - top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the first pixel vvvvv if ((x > xMin || y > yMin) && *(currentPixel - 1) == 0) { // y direction - left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last pixel vvvvv if ((y < yMax || (x < xMax)) && *(currentPixel + 1) == 0) { // y direction - right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } /* now consider pixels at the edge of the image */ // if vvvvv left edge of image vvvvv if (x == xMin) { // draw left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv right edge of image vvvvv if (x == xMax) { // draw right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv bottom edge of image vvvvv if (y == yMin) { // draw bottom edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv top edge of image vvvvv if (y == yMax) { // draw top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } } // end if currentpixel is set x++; if (x > xMax) { // reached end of line x = xMin; y++; } // Increase the pointer-position to the next pixel. // This is safe, as the while-loop and the x-reset logic above makes // sure we do not exceed the bounds of the image currentPixel++; } // end of while // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); return polyData; } void mitk::ImageVtkMapper2D::TransformActor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // get the transformation matrix of the reslicer in order to render the slice as axial, coronal or saggital vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = localStorage->m_Reslicer->GetResliceAxes(); trans->SetMatrix(matrix); // transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital) localStorage->m_Actor->SetUserTransform(trans); // transform the origin to center based coordinates, because MITK is center based. localStorage->m_Actor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); if (localStorage->m_Actors->GetNumberOfPaths() > 1) { auto *secondaryActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); secondaryActor->SetUserTransform(trans); secondaryActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } } bool mitk::ImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, SlicedGeometry3D *imageGeometry) { // if either one of the two geometries is nullptr we return true // for safety reasons if (renderingGeometry == nullptr || imageGeometry == nullptr) return true; // get the distance for the first cornerpoint ScalarType initialDistance = renderingGeometry->SignedDistance(imageGeometry->GetCornerPoint(0)); for (int i = 1; i < 8; i++) { mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint(i); // get the distance to the other cornerpoints ScalarType distance = renderingGeometry->SignedDistance(cornerPoint); // if it has not the same signing as the distance of the first point if (initialDistance * distance < 0) { // we have an intersection and return true return true; } } // all distances have the same sign, no intersection and we return false return false; } mitk::ImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::ImageVtkMapper2D::LocalStorage::LocalStorage() : m_VectorComponentExtractor(vtkSmartPointer::New()) { m_LevelWindowFilter = vtkSmartPointer::New(); // Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_BinaryLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_TSFilter = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_ReslicedImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); // the following actions are always the same and thus can be performed // in the constructor for each image (i.e. the image-corresponding local storage) m_TSFilter->ReleaseDataFlagOn(); mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New(); // built a default lookuptable mitkLUT->SetType(mitk::LookupTable::GRAYSCALE); m_DefaultLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_BINARY); m_BinaryLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_RAINBOW_COLOR); m_ColorLookupTable = mitkLUT->GetVtkLookupTable(); // do not repeat the texture (the image) m_Texture->RepeatOff(); // set the mapper for the actor m_Actor->SetMapper(m_Mapper); vtkSmartPointer outlineShadowActor = vtkSmartPointer::New(); outlineShadowActor->SetMapper(m_Mapper); m_Actors->AddPart(outlineShadowActor); m_Actors->AddPart(m_Actor); } diff --git a/Modules/DicomRT/src/mitkDoseImageVtkMapper2D.cpp b/Modules/DicomRT/src/mitkDoseImageVtkMapper2D.cpp index 7d570a3a34..f99d059910 100644 --- a/Modules/DicomRT/src/mitkDoseImageVtkMapper2D.cpp +++ b/Modules/DicomRT/src/mitkDoseImageVtkMapper2D.cpp @@ -1,1163 +1,1163 @@ /*=================================================================== 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. ===================================================================*/ // MITK #include "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" #include "mitkPropertyNameHelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // MITK Rendering #include "mitkDoseImageVtkMapper2D.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkNeverTranslucentTexture.h" // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ITK #include mitk::DoseImageVtkMapper2D::DoseImageVtkMapper2D() { } mitk::DoseImageVtkMapper2D::~DoseImageVtkMapper2D() { // The 3D RW Mapper (PlaneGeometryDataVtkMapper3D) is listening to this event, // in order to delete the images from the 3D RW. this->InvokeEvent(itk::DeleteEvent()); } // set the two points defining the textured plane according to the dimension and spacing void mitk::DoseImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float depth = this->CalculateLayerDepth(renderer); // Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct // plane size in crosshair rotation and swivel mode. localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth); // These two points define the axes of the plane in combination with the origin. // Point 1 is the x-axis and point 2 the y-axis. // Each plane is transformed according to the view (axial, coronal and saggital) afterwards. localStorage->m_Plane->SetPoint1(planeBounds[1], planeBounds[2], depth); // P1: (xMax, yMin, depth) localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); // P2: (xMin, yMax, depth) } float mitk::DoseImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer *renderer) { // get the clipping range to check how deep into z direction we can render images double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1]; // Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined float depth = -maxRange * 0.01; // divide by 100 int layer = 0; GetDataNode()->GetIntProperty("layer", layer, renderer); // add the layer property for each image to render images with a higher layer on top of the others depth += layer * 10; //*10: keep some room for each image (e.g. for ODFs in between) if (depth > 0.0f) { depth = 0.0f; MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead."; } return depth; } const mitk::Image *mitk::DoseImageVtkMapper2D::GetInput(void) { return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::DoseImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } void mitk::DoseImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::Image *input = const_cast(this->GetInput()); mitk::DataNode *datanode = this->GetDataNode(); if (input == NULL || input->IsInitialized() == false) { return; } // check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if ((worldGeometry == NULL) || (!worldGeometry->IsValid()) || (!worldGeometry->HasReferenceGeometry())) { return; } input->Update(); // early out if there is no intersection of the current rendering geometry // and the geometry of the image that is to be rendered. if (!RenderingGeometryIntersectsImage(worldGeometry, input->GetSlicedGeometry())) { // set image to nullptr, to clear the texture in 3D, because // the latest image is used there if the plane is out of the geometry // see bug-13275 localStorage->m_ReslicedImage = NULL; localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData); return; } // set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(input); localStorage->m_Reslicer->SetWorldGeometry(worldGeometry); localStorage->m_Reslicer->SetTimeStep(this->GetTimestep()); // set the transformation of the image to adapt reslice axis localStorage->m_Reslicer->SetResliceTransformByGeometry( input->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())); // is the geometry of the slice based on the input image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ((input->GetDimension() >= 3) && (input->GetDimension(2) > 1)) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation"); int interpolationMode = VTK_RESLICE_NEAREST; if (resliceInterpolationProperty != NULL) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch (interpolationMode) { case VTK_RESLICE_NEAREST: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); break; case VTK_RESLICE_LINEAR: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR); break; case VTK_RESLICE_CUBIC: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC); break; } } else { localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); } // set the vtk output property to true, makes sure that no unneeded mitk image convertion // is done. localStorage->m_Reslicer->SetVtkOutputRequest(true); // Thickslicing int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if (input->GetPixelType().GetNumberOfComponents() == 1) // for now only single component are allowed { DataNode *dn = renderer->GetCurrentWorldPlaneGeometryNode(); if (dn) { ResliceMethodProperty *resliceMethodEnumProperty = 0; if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices") && resliceMethodEnumProperty) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty = 0; if (dn->GetProperty(intProperty, "reslice.thickslices.num") && intProperty) { thickSlicesNum = intProperty->GetValue(); if (thickSlicesNum < 1) thickSlicesNum = 1; if (thickSlicesNum > 10) thickSlicesNum = 10; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } const PlaneGeometry *planeGeometry = dynamic_cast(worldGeometry); if (thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; if (planeGeometry != NULL) { normal = planeGeometry->GetNormal(); } else { const mitk::AbstractTransformGeometry *abstractGeometry = dynamic_cast(worldGeometry); if (abstractGeometry != NULL) normal = abstractGeometry->GetPlane()->GetNormal(); else return; // no fitting geometry set } normal.Normalize(); input->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())->WorldToIndex(normal, normInIndex); dataZSpacing = 1.0 / normInIndex.GetNorm(); localStorage->m_Reslicer->SetOutputDimensionality(3); localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing); localStorage->m_Reslicer->SetOutputExtentZDirection(-thickSlicesNum, 0 + thickSlicesNum); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. localStorage->m_TSFilter->SetThickSliceMode(thickSlicesMode - 1); localStorage->m_TSFilter->SetInputData(localStorage->m_Reslicer->GetVtkOutput()); // vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually localStorage->m_Reslicer->Modified(); localStorage->m_Reslicer->Update(); localStorage->m_TSFilter->Modified(); localStorage->m_TSFilter->Update(); localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput(); } else { // this is needed when thick mode was enable bevore. These variable have to be reset to default values localStorage->m_Reslicer->SetOutputDimensionality(2); localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0); localStorage->m_Reslicer->SetOutputExtentZDirection(0, 0); localStorage->m_Reslicer->Modified(); // start the pipeline with updating the largest possible, needed if the geometry of the input has changed localStorage->m_Reslicer->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput(); } // Bounds information for reslicing (only reuqired if reference geometry // is present) // this used for generating a vtkPLaneSource with the right size double sliceBounds[6]; for (int i = 0; i < 6; ++i) { sliceBounds[i] = 0.0; } localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds); // get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing(); // calculate minimum bounding rect of IMAGE in texture { double textureClippingBounds[6]; for (int i = 0; i < 6; ++i) { textureClippingBounds[i] = 0.0; } // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. mitk::PlaneClipping::CalculateClippedPlaneBounds(input->GetGeometry(), planeGeometry, textureClippingBounds); textureClippingBounds[0] = static_cast(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[1] = static_cast(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[2] = static_cast(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5); textureClippingBounds[3] = static_cast(textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5); // clipping bounds for cutting the image localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds); } // get the number of scalar components to distinguish between different image types int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents(); // get the showIsoLines property bool showIsoLines = false; datanode->GetBoolProperty("dose.showIsoLines", showIsoLines, renderer); if (showIsoLines) // contour rendering { // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); float binaryOutlineWidth(1.0); if (datanode->GetFloatProperty("outline width", binaryOutlineWidth, renderer)) { if (localStorage->m_Actors->GetNumberOfPaths() > 1) { float binaryOutlineShadowWidth(1.5); datanode->GetFloatProperty("outline shadow width", binaryOutlineShadowWidth, renderer); dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty() ->SetLineWidth(binaryOutlineWidth * binaryOutlineShadowWidth); } localStorage->m_Actor->GetProperty()->SetLineWidth(binaryOutlineWidth); } } else { localStorage->m_ReslicedImage = NULL; localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData); return; } this->ApplyOpacity(renderer); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->MapColorScalarsThroughLookupTableOff(); int displayedComponent = 0; if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1) { localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent); localStorage->m_VectorComponentExtractor->SetInputData(localStorage->m_ReslicedImage); localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0)); } else { // connect the input with the levelwindow filter localStorage->m_LevelWindowFilter->SetInputData(localStorage->m_ReslicedImage); } // check for texture interpolation property bool textureInterpolation = false; GetDataNode()->GetBoolProperty("texture interpolation", textureInterpolation, renderer); // set the interpolation modus according to the property localStorage->m_Texture->SetInterpolate(textureInterpolation); // connect the texture with the output of the levelwindow filter localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); this->TransformActor(renderer); vtkActor *contourShadowActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); if (showIsoLines) // connect the mapper with the polyData which contains the lines { // We need the contour for the binary outline property as actor localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData); localStorage->m_Actor->SetTexture(NULL); // no texture for contours bool binaryOutlineShadow(false); datanode->GetBoolProperty("outline binary shadow", binaryOutlineShadow, renderer); if (binaryOutlineShadow) contourShadowActor->SetVisibility(true); else contourShadowActor->SetVisibility(false); } else { // Connect the mapper with the input texture. This is the standard case. // setup the textured plane this->GeneratePlane(renderer, sliceBounds); // set the plane as input for the mapper localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort()); // set the texture for the actor localStorage->m_Actor->SetTexture(localStorage->m_Texture); contourShadowActor->SetVisibility(false); } // We have been modified => save this for next Update() localStorage->m_LastUpdateTime.Modified(); } void mitk::DoseImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); LevelWindow levelWindow; this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelwindow"); localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); mitk::LevelWindow opacLevelWindow; if (this->GetDataNode()->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow")) { // pass the opaque level window to the filter localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { // no opaque level window localStorage->m_LevelWindowFilter->SetMinOpacity(0.0); localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0); } } void mitk::DoseImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float rgb[3] = {1.0f, 1.0f, 1.0f}; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection bool hover = false; bool selected = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); if (hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.hoveringcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.selectedcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (!hover && !selected) { GetDataNode()->GetColor(rgb, renderer, "color"); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); localStorage->m_Actor->GetProperty()->SetColor(rgbConv); if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1) { float rgb[3] = {1.0f, 1.0f, 1.0f}; mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("outline binary shadow color", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); } } void mitk::DoseImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float opacity = 1.0f; // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(opacity, renderer, "opacity"); // set the opacity according to the properties localStorage->m_Actor->GetProperty()->SetOpacity(opacity); if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1) { dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty() ->SetOpacity(opacity); } } void mitk::DoseImageVtkMapper2D::ApplyRenderingMode(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); bool binary = false; this->GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary) // is it a binary image? { // for binary images, we always use our default LuT and map every value to (0,1) // the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window. localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable); } else { // all other image types can make use of the rendering mode int renderingMode = mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(this->GetDataNode()->GetProperty("Image Rendering.Mode", renderer)); if (mode.IsNotNull()) { renderingMode = mode->GetRenderingMode(); } switch (renderingMode) { case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color"; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color"; this->ApplyLookuptable(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); break; default: MITK_ERROR << "No valid 'Image Rendering.Mode' set. Using LOOKUPTABLE_LEVELWINDOW_COLOR instead."; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; } } // we apply color for all images (including binaries). this->ApplyColor(renderer); } void mitk::DoseImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); vtkLookupTable *usedLookupTable = localStorage->m_ColorLookupTable; // If lookup table or transferfunction use is requested... mitk::LookupTableProperty::Pointer lookupTableProp = dynamic_cast(this->GetDataNode()->GetProperty("LookupTable")); if (lookupTableProp.IsNotNull()) // is a lookuptable set? { usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); } else { //"Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'. // A default (rainbow) lookup table will be used. // Here have to do nothing. Warning for the user has been removed, due to unwanted console output // in every interation of the rendering. } localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable); } void mitk::DoseImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer) { mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast( this->GetDataNode()->GetProperty("Image Rendering.Transfer Function", renderer)); if (transferFunctionProp.IsNull()) { MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image " "Rendering.Transfer Function'. Nothing will be done."; return; } LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // pass the transfer function to our level window filter localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction()); } void mitk::DoseImageVtkMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } mitk::Image *data = const_cast(this->GetInput()); if (data == NULL) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == NULL) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(this->GetTimestep()))) { return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) // was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) // Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::DoseImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty("depthOffset", mitk::FloatProperty::New(0.0), renderer, overwrite); node->AddProperty("outline binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline width", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty("outline binary shadow", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline binary shadow color", ColorProperty::New(0.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("outline shadow width", mitk::FloatProperty::New(1.5), renderer, overwrite); if (image->IsRotated()) node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC)); else node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New()); node->AddProperty("texture interpolation", mitk::BoolProperty::New(false)); // default value node->AddProperty("in plane resample extent by geometry", mitk::BoolProperty::New(false)); node->AddProperty("bounding box", mitk::BoolProperty::New(false)); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(); node->AddProperty("Image Rendering.Mode", renderingModeProperty); // Set default grayscale look-up table mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); mitkLut->SetType(mitk::LookupTable::GRAYSCALE); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed if (node->GetStringProperty("dicom.pixel.PhotometricInterpretation", photometricInterpretation)) { // modality provided by DICOM or other reader if (photometricInterpretation.find("MONOCHROME1") != std::string::npos) // meaning: display MINIMUM pixels as WHITE { // Set inverse grayscale look-up table mitkLut->SetType(mitk::LookupTable::INVERSE_GRAYSCALE); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); } // Otherwise do nothing - the default grayscale look-up table has already been set } bool isBinaryImage(false); if (!node->GetBoolProperty("binary", isBinaryImage)) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2) / 2); sliceSelector->SetTimeNr(image->GetDimension(3) / 2); sliceSelector->SetChannelNr(image->GetDimension(4) / 2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); if (centralSliceImage.IsNotNull() && centralSliceImage->IsInitialized()) { minValue = centralSliceImage->GetStatistics()->GetScalarValueMin(); maxValue = centralSliceImage->GetStatistics()->GetScalarValueMax(); min2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMin(); max2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMax(); } if ((maxValue == min2ndValue && minValue == max2ndValue) || minValue == maxValue) { // centralSlice is strange, lets look at all data minValue = image->GetStatistics()->GetScalarValueMin(); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } isBinaryImage = (maxValue == min2ndValue && minValue == max2ndValue); } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty("opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 1.0, 1.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); std::string className = image->GetNameOfClass(); - if (className != "TensorImage" && className != "OdfImage") + if (className != "TensorImage" && className != "OdfImage" && className != "ShImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); if ((pixelType.GetPixelTypeAsString() == "vector" && numComponents > 1) || numComponents == 2 || numComponents > 4) node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite); } } if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("levelwindow", renderer) == NULL)) { /* initialize level/window from DICOM tags */ std::string sLevel; std::string sWindow; if (GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1050, "dicom.voilut.WindowCenter", image->GetPropertyList(), sLevel) && GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1051, "dicom.voilut.WindowWidth", image->GetPropertyList(), sWindow)) { float level = atof(sLevel.c_str()); float window = atof(sWindow.c_str()); mitk::LevelWindow contrast; std::string sSmallestPixelValueInSeries; std::string sLargestPixelValueInSeries; if (GetBackwardsCompatibleDICOMProperty(0x0028, 0x0108, "dicom.series.SmallestPixelValueInSeries", image->GetPropertyList(), sSmallestPixelValueInSeries) && GetBackwardsCompatibleDICOMProperty(0x0028, 0x0109, "dicom.series.LargestPixelValueInSeries", image->GetPropertyList(), sLargestPixelValueInSeries)) { float smallestPixelValueInSeries = atof(sSmallestPixelValueInSeries.c_str()); float largestPixelValueInSeries = atof(sLargestPixelValueInSeries.c_str()); contrast.SetRangeMinMax(smallestPixelValueInSeries - 1, largestPixelValueInSeries + 1); // why not a little buffer? // might remedy some l/w widget challenges } else { contrast.SetAuto(static_cast(node->GetData()), false, true); // we need this as a fallback } contrast.SetLevelWindow(level, window, true); node->SetProperty("levelwindow", LevelWindowProperty::New(contrast), renderer); } } if (((overwrite) || (node->GetProperty("opaclevelwindow", renderer) == NULL)) && (image->GetPixelType().GetPixelType() == itk::ImageIOBase::RGBA) && (image->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR)) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0, 255); opaclevwin.SetWindowBounds(0, 255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty("opaclevelwindow", prop, renderer); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::DoseImageVtkMapper2D::LocalStorage *mitk::DoseImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } vtkSmartPointer mitk::DoseImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer) { vtkSmartPointer points = vtkSmartPointer::New(); // the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); // the lines to connect the points vtkSmartPointer colors = vtkSmartPointer::New(); colors->SetNumberOfComponents(3); colors->SetName("Colors"); float pref; this->GetDataNode()->GetFloatProperty(mitk::RTConstants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), pref); mitk::IsoDoseLevelSetProperty::Pointer propIsoSet = dynamic_cast( GetDataNode()->GetProperty(mitk::RTConstants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str())); mitk::IsoDoseLevelSet::Pointer isoDoseLevelSet = propIsoSet->GetValue(); for (mitk::IsoDoseLevelSet::ConstIterator doseIT = isoDoseLevelSet->Begin(); doseIT != isoDoseLevelSet->End(); ++doseIT) { if (doseIT->GetVisibleIsoLine()) { this->CreateLevelOutline(renderer, &(doseIT.Value()), pref, points, lines, colors); } // end of if visible dose value } // end of loop over all does values mitk::IsoDoseLevelVectorProperty::Pointer propfreeIsoVec = dynamic_cast( GetDataNode()->GetProperty(mitk::RTConstants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str())); mitk::IsoDoseLevelVector::Pointer frereIsoDoseLevelVec = propfreeIsoVec->GetValue(); for (mitk::IsoDoseLevelVector::ConstIterator freeDoseIT = frereIsoDoseLevelVec->Begin(); freeDoseIT != frereIsoDoseLevelVec->End(); ++freeDoseIT) { if (freeDoseIT->Value()->GetVisibleIsoLine()) { this->CreateLevelOutline(renderer, freeDoseIT->Value(), pref, points, lines, colors); } // end of if visible dose value } // end of loop over all does values // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); polyData->GetCellData()->SetScalars(colors); return polyData; } void mitk::DoseImageVtkMapper2D::CreateLevelOutline(mitk::BaseRenderer *renderer, const mitk::IsoDoseLevel *level, float pref, vtkSmartPointer points, vtkSmartPointer lines, vtkSmartPointer colors) { LocalStorage *localStorage = this->GetLocalStorage(renderer); // get the min and max index values of each direction int *extent = localStorage->m_ReslicedImage->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int *dims = localStorage->m_ReslicedImage->GetDimensions(); // dimensions of the image int line = dims[0]; // how many pixels per line? // get the depth for each contour float depth = CalculateLayerDepth(renderer); double doseValue = level->GetDoseValue() * pref; mitk::IsoDoseLevel::ColorType isoColor = level->GetColor(); unsigned char colorLine[3] = {static_cast(isoColor.GetRed() * 255), static_cast(isoColor.GetGreen() * 255), static_cast(isoColor.GetBlue() * 255)}; int x = xMin; // pixel index x int y = yMin; // pixel index y float *currentPixel; // We take the pointer to the first pixel of the image currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer()); while (y <= yMax) { // if the current pixel value is set to something if ((currentPixel) && (*currentPixel >= doseValue)) { // check in which direction a line is necessary // a line is added if the neighbor of the current pixel has the value 0 // and if the pixel is located at the edge of the image // if vvvvv not the first line vvvvv if (y > yMin && *(currentPixel - line) < doseValue) { // x direction - bottom edge of the pixel // add the 2 points vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); // add the line between both points lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } // if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel + line) < doseValue) { // x direction - top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } // if vvvvv not the first pixel vvvvv if ((x > xMin || y > yMin) && *(currentPixel - 1) < doseValue) { // y direction - left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } // if vvvvv not the last pixel vvvvv if ((y < yMax || (x < xMax)) && *(currentPixel + 1) < doseValue) { // y direction - right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } /* now consider pixels at the edge of the image */ // if vvvvv left edge of image vvvvv if (x == xMin) { // draw left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } // if vvvvv right edge of image vvvvv if (x == xMax) { // draw right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } // if vvvvv bottom edge of image vvvvv if (y == yMin) { // draw bottom edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } // if vvvvv top edge of image vvvvv if (y == yMax) { // draw top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } } // end if currentpixel is set x++; if (x > xMax) { // reached end of line x = xMin; y++; } // Increase the pointer-position to the next pixel. // This is safe, as the while-loop and the x-reset logic above makes // sure we do not exceed the bounds of the image currentPixel++; } // end of while } void mitk::DoseImageVtkMapper2D::TransformActor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // get the transformation matrix of the reslicer in order to render the slice as axial, coronal or saggital vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = localStorage->m_Reslicer->GetResliceAxes(); trans->SetMatrix(matrix); // transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital) localStorage->m_Actor->SetUserTransform(trans); // transform the origin to center based coordinates, because MITK is center based. localStorage->m_Actor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); if (localStorage->m_Actors->GetNumberOfPaths() > 1) { vtkActor *secondaryActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); secondaryActor->SetUserTransform(trans); secondaryActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } } bool mitk::DoseImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, SlicedGeometry3D *imageGeometry) { // if either one of the two geometries is nullptr we return true // for safety reasons if (renderingGeometry == NULL || imageGeometry == NULL) return true; // get the distance for the first cornerpoint ScalarType initialDistance = renderingGeometry->SignedDistance(imageGeometry->GetCornerPoint(0)); for (int i = 1; i < 8; i++) { mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint(i); // get the distance to the other cornerpoints ScalarType distance = renderingGeometry->SignedDistance(cornerPoint); // if it has not the same signing as the distance of the first point if (initialDistance * distance < 0) { // we have an intersection and return true return true; } } // all distances have the same sign, no intersection and we return false return false; } mitk::DoseImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::DoseImageVtkMapper2D::LocalStorage::LocalStorage() : m_VectorComponentExtractor(vtkSmartPointer::New()) { m_LevelWindowFilter = vtkSmartPointer::New(); // Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_BinaryLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_TSFilter = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_ReslicedImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); // the following actions are always the same and thus can be performed // in the constructor for each image (i.e. the image-corresponding local storage) m_TSFilter->ReleaseDataFlagOn(); mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New(); // built a default lookuptable mitkLUT->SetType(mitk::LookupTable::GRAYSCALE); m_DefaultLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_BINARY); m_BinaryLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_RAINBOW_COLOR); m_ColorLookupTable = mitkLUT->GetVtkLookupTable(); // do not repeat the texture (the image) m_Texture->RepeatOff(); // set the mapper for the actor m_Actor->SetMapper(m_Mapper); vtkSmartPointer outlineShadowActor = vtkSmartPointer::New(); outlineShadowActor->SetMapper(m_Mapper); m_Actors->AddPart(outlineShadowActor); m_Actors->AddPart(m_Actor); } diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/files.cmake b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/files.cmake index 4bc431ffff..a7f7003a49 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/files.cmake +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/files.cmake @@ -1,25 +1,28 @@ set(CPP_FILES mitkDiffusionCoreIOActivator.cpp mitkPeakImageReader.cpp mitkNrrdTensorImageReader.cpp mitkNrrdTensorImageWriter.cpp mitkTensorImageSerializer.cpp mitkTensorImageSource.cpp mitkDiffusionCoreObjectFactory.cpp mitkDiffusionCoreIOMimeTypes.cpp mitkDiffusionImageDicomReaderService.cpp mitkDiffusionImageNrrdReaderService.cpp mitkDiffusionImageNrrdWriterService.cpp mitkDiffusionImageNiftiReaderService.cpp mitkDiffusionImageNiftiWriterService.cpp mitkNrrdOdfImageReader.cpp mitkNrrdOdfImageWriter.cpp mitkOdfImageSerializer.cpp mitkPeakImageSerializer.cpp + mitkShImageReader.cpp + mitkShImageWriter.cpp + mitkShImageSerializer.cpp mitkCompositeMapper.cpp ) diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreIOActivator.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreIOActivator.cpp index 2c2a187d37..dba0107ae4 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreIOActivator.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreIOActivator.cpp @@ -1,140 +1,148 @@ /*=================================================================== 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 #include #include #include "mitkDiffusionCoreIOMimeTypes.h" namespace mitk { /** \brief Registers services for segmentation module. */ class DiffusionCoreIOActivator : public us::ModuleActivator { public: void Load(us::ModuleContext* context) override { us::ServiceProperties props; props[ us::ServiceConstants::SERVICE_RANKING() ] = 10; m_MimeTypes = mitk::DiffusionCoreIOMimeTypes::Get(); for (std::vector::const_iterator mimeTypeIter = m_MimeTypes.begin(), iterEnd = m_MimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { context->RegisterService(*mimeTypeIter, props); } m_DiffusionImageNrrdReaderService = new DiffusionImageNrrdReaderService(); m_DiffusionImageNiftiReaderService = new DiffusionImageNiftiReaderService( CustomMimeType( mitk::DiffusionCoreIOMimeTypes::DWI_NIFTI_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::DWI_NIFTI_MIMETYPE_DESCRIPTION() ); m_DiffusionImageFslNiftiReaderService = new DiffusionImageNiftiReaderService( CustomMimeType( mitk::DiffusionCoreIOMimeTypes::DWI_FSL_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::DWI_FSL_MIMETYPE_DESCRIPTION() ); m_DiffusionImageDicomReaderService = new DiffusionImageDicomReaderService(); m_NrrdTensorImageReader = new NrrdTensorImageReader(); m_NrrdOdfImageReader = new NrrdOdfImageReader(); m_PeakImageReader = new PeakImageReader(); + m_ShImageReader = new ShImageReader(); m_DiffusionImageNrrdWriterService = new DiffusionImageNrrdWriterService(); m_DiffusionImageNiftiWriterService = new DiffusionImageNiftiWriterService(); m_NrrdTensorImageWriter = new NrrdTensorImageWriter(); m_NrrdOdfImageWriter = new NrrdOdfImageWriter(); + m_ShImageWriter = new ShImageWriter(); //register relevant properties //non-persistent properties mitk::CoreServices::GetPropertyDescriptions()->AddDescription(mitk::DiffusionPropertyHelper::BVALUEMAPPROPERTYNAME, "This map stores which b values belong to which gradients."); mitk::CoreServices::GetPropertyDescriptions()->AddDescription(mitk::DiffusionPropertyHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME, "The original gradients used during acquisition. This property may be empty."); //persistent properties mitk::CoreServices::GetPropertyDescriptions()->AddDescription(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME, "The reference b value the gradients are normalized to."); mitk::CoreServices::GetPropertyDescriptions()->AddDescription(mitk::DiffusionPropertyHelper::MEASUREMENTFRAMEPROPERTYNAME, "The measurment frame used during acquisition."); mitk::CoreServices::GetPropertyDescriptions()->AddDescription(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME, "The gradients after applying measurement frame and image matrix."); mitk::CoreServices::GetPropertyDescriptions()->AddDescription(mitk::DiffusionPropertyHelper::MODALITY, "Defines the modality used for acquisition. DWMRI signifies diffusion weighted images."); mitk::PropertyPersistenceInfo::Pointer PPI_referenceBValue = mitk::PropertyPersistenceInfo::New(); PPI_referenceBValue->SetNameAndKey(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME, "DWMRI_b-value"); mitk::PropertyPersistenceInfo::Pointer PPI_measurementFrame = mitk::PropertyPersistenceInfo::New(); PPI_measurementFrame->SetNameAndKey(mitk::DiffusionPropertyHelper::MEASUREMENTFRAMEPROPERTYNAME, "measurement frame"); mitk::PropertyPersistenceInfo::Pointer PPI_gradientContainer = mitk::PropertyPersistenceInfo::New(); PPI_gradientContainer->SetNameAndKey(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME, "DWMRI_gradient"); mitk::PropertyPersistenceInfo::Pointer PPI_modality = mitk::PropertyPersistenceInfo::New(); PPI_modality->SetNameAndKey(mitk::DiffusionPropertyHelper::MODALITY, "modality"); mitk::CoreServices::GetPropertyPersistence()->AddInfo(PPI_referenceBValue.GetPointer() , true); mitk::CoreServices::GetPropertyPersistence()->AddInfo(PPI_measurementFrame.GetPointer(), true); mitk::CoreServices::GetPropertyPersistence()->AddInfo(PPI_gradientContainer.GetPointer(), true); mitk::CoreServices::GetPropertyPersistence()->AddInfo(PPI_modality.GetPointer(), true); } void Unload(us::ModuleContext*) override { for (unsigned int loop(0); loop < m_MimeTypes.size(); ++loop) { delete m_MimeTypes.at(loop); } delete m_DiffusionImageNrrdReaderService; delete m_DiffusionImageNiftiReaderService; delete m_DiffusionImageFslNiftiReaderService; delete m_DiffusionImageDicomReaderService; delete m_NrrdTensorImageReader; delete m_NrrdOdfImageReader; delete m_PeakImageReader; + delete m_ShImageReader; delete m_DiffusionImageNrrdWriterService; delete m_DiffusionImageNiftiWriterService; delete m_NrrdTensorImageWriter; delete m_NrrdOdfImageWriter; + delete m_ShImageWriter; } private: DiffusionImageNrrdReaderService * m_DiffusionImageNrrdReaderService; DiffusionImageNiftiReaderService * m_DiffusionImageNiftiReaderService; DiffusionImageNiftiReaderService * m_DiffusionImageFslNiftiReaderService; DiffusionImageDicomReaderService * m_DiffusionImageDicomReaderService; NrrdTensorImageReader * m_NrrdTensorImageReader; NrrdOdfImageReader * m_NrrdOdfImageReader; PeakImageReader * m_PeakImageReader; + ShImageReader * m_ShImageReader; DiffusionImageNrrdWriterService * m_DiffusionImageNrrdWriterService; DiffusionImageNiftiWriterService * m_DiffusionImageNiftiWriterService; NrrdTensorImageWriter * m_NrrdTensorImageWriter; NrrdOdfImageWriter * m_NrrdOdfImageWriter; + ShImageWriter * m_ShImageWriter; std::vector m_MimeTypes; }; } US_EXPORT_MODULE_ACTIVATOR(mitk::DiffusionCoreIOActivator) diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreIOMimeTypes.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreIOMimeTypes.cpp index 2f8fd6eeb3..5763b03118 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreIOMimeTypes.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreIOMimeTypes.cpp @@ -1,517 +1,630 @@ /*=================================================================== 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 "mitkDiffusionCoreIOMimeTypes.h" #include "mitkIOMimeTypes.h" #include #include #include #include #include #include #include #include #include namespace mitk { std::vector DiffusionCoreIOMimeTypes::Get() { std::vector mimeTypes; // order matters here (descending rank for mime types) mimeTypes.push_back(DWI_NRRD_MIMETYPE().Clone()); mimeTypes.push_back(DWI_NIFTI_MIMETYPE().Clone()); mimeTypes.push_back(DWI_FSL_MIMETYPE().Clone()); mimeTypes.push_back(DWI_DICOM_MIMETYPE().Clone()); mimeTypes.push_back(DTI_MIMETYPE().Clone()); mimeTypes.push_back(ODF_MIMETYPE().Clone()); mimeTypes.push_back(PEAK_MIMETYPE().Clone()); + mimeTypes.push_back(SH_MIMETYPE().Clone()); return mimeTypes; } // Mime Types DiffusionCoreIOMimeTypes::DiffusionImageNrrdMimeType::DiffusionImageNrrdMimeType() : CustomMimeType(DWI_NRRD_MIMETYPE_NAME()) { std::string category = "Diffusion Weighted Images"; this->SetCategory(category); this->SetComment("Diffusion Weighted Images"); this->AddExtension("dwi"); //this->AddExtension("hdwi"); // saving with detached header does not work out of the box this->AddExtension("nrrd"); } bool DiffusionCoreIOMimeTypes::DiffusionImageNrrdMimeType::AppliesTo(const std::string &path) const { bool canRead( CustomMimeType::AppliesTo(path) ); // fix for bug 18572 // Currently this function is called for writing as well as reading, in that case // the image information can of course not be read // This is a bug, this function should only be called for reading. if( ! itksys::SystemTools::FileExists( path.c_str() ) ) { return canRead; } //end fix for bug 18572 std::string ext = this->GetExtension( path ); ext = itksys::SystemTools::LowerCase( ext ); // Simple NRRD files should only be considered for this mime type if they contain // corresponding tags if( ext == ".nrrd" ) { itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); io->SetFileName(path); try { io->ReadImageInformation(); itk::MetaDataDictionary imgMetaDictionary = io->GetMetaDataDictionary(); std::vector imgMetaKeys = imgMetaDictionary.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString; for (; itKey != imgMetaKeys.end(); itKey ++) { itk::ExposeMetaData (imgMetaDictionary, *itKey, metaString); if (itKey->find("modality") != std::string::npos) { if (metaString.find("DWMRI") != std::string::npos) { return canRead; } } } } catch( const itk::ExceptionObject &e ) { MITK_ERROR << "ITK Exception: " << e.what(); } canRead = false; } return canRead; } DiffusionCoreIOMimeTypes::DiffusionImageNrrdMimeType* DiffusionCoreIOMimeTypes::DiffusionImageNrrdMimeType::Clone() const { return new DiffusionImageNrrdMimeType(*this); } DiffusionCoreIOMimeTypes::DiffusionImageNrrdMimeType DiffusionCoreIOMimeTypes::DWI_NRRD_MIMETYPE() { return DiffusionImageNrrdMimeType(); } DiffusionCoreIOMimeTypes::DiffusionImageNiftiMimeType::DiffusionImageNiftiMimeType() : CustomMimeType(DWI_NIFTI_MIMETYPE_NAME()) { std::string category = "Diffusion Weighted Images"; this->SetCategory(category); this->SetComment("Diffusion Weighted Images"); this->AddExtension("nii.gz"); this->AddExtension("nii"); } bool DiffusionCoreIOMimeTypes::DiffusionImageNiftiMimeType::AppliesTo(const std::string &path) const { bool canRead(CustomMimeType::AppliesTo(path)); // fix for bug 18572 // Currently this function is called for writing as well as reading, in that case // the image information can of course not be read // This is a bug, this function should only be called for reading. if (!itksys::SystemTools::FileExists(path.c_str())) { return canRead; } //end fix for bug 18572 std::string ext = this->GetExtension(path); ext = itksys::SystemTools::LowerCase(ext); // Nifti files should only be considered for this mime type if they are // accompanied by bvecs and bvals files defining the diffusion information if (ext == ".nii" || ext == ".nii.gz") { std::string base_path = itksys::SystemTools::GetFilenamePath(path); std::string base = this->GetFilenameWithoutExtension(path); std::string filename = base; if (!base_path.empty()) { base = base_path + "/" + base; base_path += "/"; } if (itksys::SystemTools::FileExists(std::string(base + ".bvec").c_str()) && itksys::SystemTools::FileExists(std::string(base + ".bval").c_str()) ) { return canRead; } if (itksys::SystemTools::FileExists(std::string(base + ".bvecs").c_str()) && itksys::SystemTools::FileExists(std::string(base + ".bvals").c_str()) ) { return canRead; } // hack for HCP data if ( filename=="data" && itksys::SystemTools::FileExists(std::string(base_path + "bvec").c_str()) && itksys::SystemTools::FileExists(std::string(base_path + "bval").c_str()) ) { return canRead; } if ( filename=="data" && itksys::SystemTools::FileExists(std::string(base_path + "bvecs").c_str()) && itksys::SystemTools::FileExists(std::string(base_path + "bvals").c_str()) ) { return canRead; } canRead = false; } return canRead; } DiffusionCoreIOMimeTypes::DiffusionImageNiftiMimeType* DiffusionCoreIOMimeTypes::DiffusionImageNiftiMimeType::Clone() const { return new DiffusionImageNiftiMimeType(*this); } DiffusionCoreIOMimeTypes::DiffusionImageNiftiMimeType DiffusionCoreIOMimeTypes::DWI_NIFTI_MIMETYPE() { return DiffusionImageNiftiMimeType(); } DiffusionCoreIOMimeTypes::DiffusionImageFslMimeType::DiffusionImageFslMimeType() : CustomMimeType(DWI_FSL_MIMETYPE_NAME()) { - std::string category = "Diffusion Weighted Image"; + std::string category = "Diffusion Weighted Images"; this->SetCategory(category); this->SetComment("Diffusion Weighted Images"); this->AddExtension("fslgz"); this->AddExtension("fsl"); } bool DiffusionCoreIOMimeTypes::DiffusionImageFslMimeType::AppliesTo(const std::string &path) const { bool canRead(CustomMimeType::AppliesTo(path)); // fix for bug 18572 // Currently this function is called for writing as well as reading, in that case // the image information can of course not be read // This is a bug, this function should only be called for reading. if (!itksys::SystemTools::FileExists(path.c_str())) { return canRead; } //end fix for bug 18572 std::string ext = this->GetExtension(path); ext = itksys::SystemTools::LowerCase(ext); // Nifti files should only be considered for this mime type if they are // accompanied by bvecs and bvals files defining the diffusion information if (ext == ".fsl" || ext == ".fslgz") { std::string base_path = itksys::SystemTools::GetFilenamePath(path); std::string base = this->GetFilenameWithoutExtension(path); if (!base_path.empty()) base = base_path + "/" + base; if (itksys::SystemTools::FileExists(std::string(base + ".bvec").c_str()) && itksys::SystemTools::FileExists(std::string(base + ".bval").c_str()) ) { return canRead; } if (itksys::SystemTools::FileExists(std::string(base + ".bvecs").c_str()) && itksys::SystemTools::FileExists(std::string(base + ".bvals").c_str()) ) { return canRead; } if (itksys::SystemTools::FileExists(std::string(base + ext + ".bvec").c_str()) && itksys::SystemTools::FileExists(std::string(base + ext + ".bval").c_str()) ) { return canRead; } if (itksys::SystemTools::FileExists(std::string(base + ext + ".bvecs").c_str()) && itksys::SystemTools::FileExists(std::string(base + ext + ".bvals").c_str()) ) { return canRead; } canRead = false; } return canRead; } DiffusionCoreIOMimeTypes::DiffusionImageFslMimeType* DiffusionCoreIOMimeTypes::DiffusionImageFslMimeType::Clone() const { return new DiffusionImageFslMimeType(*this); } DiffusionCoreIOMimeTypes::DiffusionImageFslMimeType DiffusionCoreIOMimeTypes::DWI_FSL_MIMETYPE() { return DiffusionImageFslMimeType(); } DiffusionCoreIOMimeTypes::DiffusionImageDicomMimeType::DiffusionImageDicomMimeType() : CustomMimeType(DWI_DICOM_MIMETYPE_NAME()) { std::string category = "Diffusion Weighted Images"; this->SetCategory(category); this->SetComment("Diffusion Weighted Images"); this->AddExtension("gdcm"); this->AddExtension("dcm"); this->AddExtension("DCM"); this->AddExtension("dc3"); this->AddExtension("DC3"); this->AddExtension("ima"); this->AddExtension("img"); } bool DiffusionCoreIOMimeTypes::DiffusionImageDicomMimeType::AppliesTo(const std::string &path) const { itk::GDCMImageIO::Pointer gdcmIO = itk::GDCMImageIO::New(); bool canRead = gdcmIO->CanReadFile(path.c_str()); if (!canRead) return canRead; mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); mitk::DICOMTag ImageTypeTag(0x0008, 0x0008); mitk::DICOMTag SeriesDescriptionTag(0x0008, 0x103E); mitk::StringList relevantFiles; relevantFiles.push_back(path); scanner->AddTag(ImageTypeTag); scanner->AddTag(SeriesDescriptionTag); scanner->SetInputFiles(relevantFiles); scanner->Scan(); mitk::DICOMTagCache::Pointer tagCache = scanner->GetScanCache(); mitk::DICOMImageFrameList imageFrameList = mitk::ConvertToDICOMImageFrameList(tagCache->GetFrameInfoList()); mitk::DICOMImageFrameInfo *firstFrame = imageFrameList.begin()->GetPointer(); std::string byteString = tagCache->GetTagValue(firstFrame, ImageTypeTag).value; if (byteString.empty()) return false; std::string byteString2 = tagCache->GetTagValue(firstFrame, SeriesDescriptionTag).value; if (byteString2.empty()) return false; if (byteString.find("DIFFUSION")==std::string::npos && byteString2.find("diff")==std::string::npos) return false; // if (byteString.find("NONE")==std::string::npos) // return false; return canRead; } DiffusionCoreIOMimeTypes::DiffusionImageDicomMimeType* DiffusionCoreIOMimeTypes::DiffusionImageDicomMimeType::Clone() const { return new DiffusionImageDicomMimeType(*this); } DiffusionCoreIOMimeTypes::DiffusionImageDicomMimeType DiffusionCoreIOMimeTypes::DWI_DICOM_MIMETYPE() { return DiffusionImageDicomMimeType(); } DiffusionCoreIOMimeTypes::PeakImageMimeType::PeakImageMimeType() : CustomMimeType(PEAK_MIMETYPE_NAME()) { std::string category = "Peak Image"; this->SetCategory(category); this->SetComment("Peak Image"); this->AddExtension("nrrd"); this->AddExtension("nii"); this->AddExtension("nii.gz"); } bool DiffusionCoreIOMimeTypes::PeakImageMimeType::AppliesTo(const std::string &path) const { { itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); if ( io->CanReadFile( path.c_str() ) ) { io->SetFileName( path.c_str() ); io->ReadImageInformation(); if ( io->GetPixelType() == itk::ImageIOBase::SCALAR && io->GetNumberOfDimensions()==4 && io->GetDimensions(3)%3==0) return true; } } { itk::NiftiImageIO::Pointer io = itk::NiftiImageIO::New(); if ( io->CanReadFile( path.c_str() ) ) { io->SetFileName( path.c_str() ); io->ReadImageInformation(); if ( io->GetPixelType() == itk::ImageIOBase::SCALAR && io->GetNumberOfDimensions()==4 && io->GetDimensions(3)%3==0) return true; } } return false; } DiffusionCoreIOMimeTypes::PeakImageMimeType* DiffusionCoreIOMimeTypes::PeakImageMimeType::Clone() const { return new PeakImageMimeType(*this); } DiffusionCoreIOMimeTypes::PeakImageMimeType DiffusionCoreIOMimeTypes::PEAK_MIMETYPE() { return PeakImageMimeType(); } +DiffusionCoreIOMimeTypes::SHImageMimeType::SHImageMimeType() : CustomMimeType(SH_MIMETYPE_NAME()) +{ + std::string category = "SH Image"; + this->SetCategory(category); + this->SetComment("SH Image"); + + this->AddExtension("nii.gz"); + this->AddExtension("nii"); + this->AddExtension("nrrd"); + this->AddExtension("shi"); +} + +bool DiffusionCoreIOMimeTypes::SHImageMimeType::AppliesTo(const std::string &path) const +{ + { + itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); + try + { + io->SetFileName( path.c_str() ); + io->ReadImageInformation(); + if ( io->GetPixelType() == itk::ImageIOBase::SCALAR && io->GetNumberOfDimensions()==4) + { + switch (io->GetDimensions(3)) + { + case 6: + return true; + break; + case 15: + return true; + break; + case 28: + return true; + break; + case 45: + return true; + break; + case 66: + return true; + break; + case 91: + return true; + break; + default : + return false; + } + } + } + catch(...) + {} + } + + { + itk::NiftiImageIO::Pointer io = itk::NiftiImageIO::New(); + if ( io->CanReadFile( path.c_str() ) ) + { + io->SetFileName( path.c_str() ); + io->ReadImageInformation(); + if ( io->GetPixelType() == itk::ImageIOBase::SCALAR && io->GetNumberOfDimensions()==4) + { + switch (io->GetDimensions(3)) + { + case 6: + return true; + break; + case 15: + return true; + break; + case 28: + return true; + break; + case 45: + return true; + break; + case 66: + return true; + break; + case 91: + return true; + break; + default : + return false; + } + } + } + } + + return false; +} + +DiffusionCoreIOMimeTypes::SHImageMimeType* DiffusionCoreIOMimeTypes::SHImageMimeType::Clone() const +{ + return new SHImageMimeType(*this); +} + + +DiffusionCoreIOMimeTypes::SHImageMimeType DiffusionCoreIOMimeTypes::SH_MIMETYPE() +{ + return SHImageMimeType(); +} + CustomMimeType DiffusionCoreIOMimeTypes::DTI_MIMETYPE() { CustomMimeType mimeType(DTI_MIMETYPE_NAME()); - std::string category = "Tensor Images"; - mimeType.SetComment("Diffusion Tensor Images"); + std::string category = "Tensor Image"; + mimeType.SetComment("Diffusion Tensor Image"); mimeType.SetCategory(category); mimeType.AddExtension("dti"); return mimeType; } CustomMimeType DiffusionCoreIOMimeTypes::ODF_MIMETYPE() { CustomMimeType mimeType(ODF_MIMETYPE_NAME()); - std::string category = "ODF Images"; - mimeType.SetComment("Diffusion ODF Images"); + std::string category = "ODF Image"; + mimeType.SetComment("Diffusion ODF Image"); mimeType.SetCategory(category); mimeType.AddExtension("odf"); mimeType.AddExtension("qbi"); // legacy support return mimeType; } // Names std::string DiffusionCoreIOMimeTypes::PEAK_MIMETYPE_NAME() { - static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + ".nrrd"; + static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + "_PEAKS"; return name; } std::string DiffusionCoreIOMimeTypes::DWI_NRRD_MIMETYPE_NAME() { static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + ".dwi"; return name; } std::string DiffusionCoreIOMimeTypes::DWI_NIFTI_MIMETYPE_NAME() { static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + ".nii.gz"; return name; } std::string DiffusionCoreIOMimeTypes::DWI_FSL_MIMETYPE_NAME() { static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + ".fslgz"; return name; } std::string DiffusionCoreIOMimeTypes::DWI_DICOM_MIMETYPE_NAME() { static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + ".IMA"; return name; } std::string DiffusionCoreIOMimeTypes::DTI_MIMETYPE_NAME() { static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + ".dti"; return name; } std::string DiffusionCoreIOMimeTypes::ODF_MIMETYPE_NAME() { static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + ".odf"; return name; } +std::string DiffusionCoreIOMimeTypes::SH_MIMETYPE_NAME() +{ + static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + "_SH"; + return name; +} + // Descriptions std::string DiffusionCoreIOMimeTypes::PEAK_MIMETYPE_DESCRIPTION() { static std::string description = "Peak Image"; return description; } std::string DiffusionCoreIOMimeTypes::DWI_NRRD_MIMETYPE_DESCRIPTION() { static std::string description = "Diffusion Weighted Images"; return description; } std::string DiffusionCoreIOMimeTypes::DWI_NIFTI_MIMETYPE_DESCRIPTION() { static std::string description = "Diffusion Weighted Images"; return description; } std::string DiffusionCoreIOMimeTypes::DWI_FSL_MIMETYPE_DESCRIPTION() { static std::string description = "Diffusion Weighted Images"; return description; } std::string DiffusionCoreIOMimeTypes::DWI_DICOM_MIMETYPE_DESCRIPTION() { static std::string description = "Diffusion Weighted Images"; return description; } std::string DiffusionCoreIOMimeTypes::DTI_MIMETYPE_DESCRIPTION() { - static std::string description = "Diffusion Tensor Images"; + static std::string description = "Diffusion Tensor Image"; return description; } std::string DiffusionCoreIOMimeTypes::ODF_MIMETYPE_DESCRIPTION() { - static std::string description = "ODF Images"; + static std::string description = "ODF Image"; + return description; +} + +std::string DiffusionCoreIOMimeTypes::SH_MIMETYPE_DESCRIPTION() +{ + static std::string description = "SH Image"; return description; } } diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreIOMimeTypes.h b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreIOMimeTypes.h index 01876a94fd..03c28ad213 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreIOMimeTypes.h +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreIOMimeTypes.h @@ -1,108 +1,119 @@ /*=================================================================== 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 MITKDiffusionCoreIOMimeTypes_H #define MITKDiffusionCoreIOMimeTypes_H #include "mitkCustomMimeType.h" #include namespace mitk { class DiffusionCoreIOMimeTypes { public: class DiffusionImageNrrdMimeType : public CustomMimeType { public: DiffusionImageNrrdMimeType(); virtual bool AppliesTo(const std::string &path) const override; virtual DiffusionImageNrrdMimeType* Clone() const override; }; class DiffusionImageNiftiMimeType : public CustomMimeType { public: DiffusionImageNiftiMimeType(); virtual bool AppliesTo(const std::string &path) const override; virtual DiffusionImageNiftiMimeType* Clone() const override; }; class DiffusionImageFslMimeType : public CustomMimeType { public: DiffusionImageFslMimeType(); virtual bool AppliesTo(const std::string &path) const override; virtual DiffusionImageFslMimeType* Clone() const override; }; class DiffusionImageDicomMimeType : public CustomMimeType { public: DiffusionImageDicomMimeType(); virtual bool AppliesTo(const std::string &path) const override; virtual DiffusionImageDicomMimeType* Clone() const override; }; class PeakImageMimeType : public CustomMimeType { public: PeakImageMimeType(); virtual bool AppliesTo(const std::string &path) const override; virtual PeakImageMimeType* Clone() const override; }; + class SHImageMimeType : public CustomMimeType + { + public: + SHImageMimeType(); + virtual bool AppliesTo(const std::string &path) const override; + virtual SHImageMimeType* Clone() const override; + }; + // Get all Diffusion Mime Types static std::vector Get(); // ------------------------- Image formats (ITK based) -------------------------- static DiffusionImageNrrdMimeType DWI_NRRD_MIMETYPE(); static DiffusionImageNiftiMimeType DWI_NIFTI_MIMETYPE(); static DiffusionImageFslMimeType DWI_FSL_MIMETYPE(); static DiffusionImageDicomMimeType DWI_DICOM_MIMETYPE(); static PeakImageMimeType PEAK_MIMETYPE(); static CustomMimeType DTI_MIMETYPE(); // dti static CustomMimeType ODF_MIMETYPE(); // odf, qbi + static SHImageMimeType SH_MIMETYPE(); // spherical harmonics coefficients static std::string PEAK_MIMETYPE_NAME(); static std::string DWI_NRRD_MIMETYPE_NAME(); static std::string DWI_NIFTI_MIMETYPE_NAME(); static std::string DWI_FSL_MIMETYPE_NAME(); static std::string DWI_DICOM_MIMETYPE_NAME(); static std::string DTI_MIMETYPE_NAME(); static std::string ODF_MIMETYPE_NAME(); + static std::string SH_MIMETYPE_NAME(); static std::string PEAK_MIMETYPE_DESCRIPTION(); static std::string DWI_NRRD_MIMETYPE_DESCRIPTION(); static std::string DWI_NIFTI_MIMETYPE_DESCRIPTION(); static std::string DWI_FSL_MIMETYPE_DESCRIPTION(); static std::string DWI_DICOM_MIMETYPE_DESCRIPTION(); static std::string DTI_MIMETYPE_DESCRIPTION(); static std::string ODF_MIMETYPE_DESCRIPTION(); + static std::string SH_MIMETYPE_DESCRIPTION(); private: // purposely not implemented DiffusionCoreIOMimeTypes(); DiffusionCoreIOMimeTypes(const DiffusionCoreIOMimeTypes&); }; } #endif // MITKDiffusionCoreIOMimeTypes_H diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreObjectFactory.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreObjectFactory.cpp index f36cec14e4..2a40757906 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreObjectFactory.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionCoreObjectFactory.cpp @@ -1,156 +1,175 @@ /*=================================================================== 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 "mitkDiffusionCoreObjectFactory.h" #include "mitkProperties.h" #include "mitkBaseRenderer.h" #include "mitkDataNode.h" #include "mitkCompositeMapper.h" #include "mitkGPUVolumeMapper3D.h" typedef short DiffusionPixelType; typedef std::multimap MultimapType; mitk::DiffusionCoreObjectFactory::DiffusionCoreObjectFactory() : CoreObjectFactoryBase() { static bool alreadyDone = false; if (!alreadyDone) { MITK_DEBUG << "DiffusionCoreObjectFactory c'tor" << std::endl; CreateFileExtensionsMap(); alreadyDone = true; } } mitk::DiffusionCoreObjectFactory::~DiffusionCoreObjectFactory() { } mitk::Mapper::Pointer mitk::DiffusionCoreObjectFactory::CreateMapper(mitk::DataNode* node, MapperSlotId id) { mitk::Mapper::Pointer newMapper=nullptr; if ( id == mitk::BaseRenderer::Standard2D ) { std::string classname("OdfImage"); if(node->GetData() && classname.compare(node->GetData()->GetNameOfClass())==0) { newMapper = mitk::CompositeMapper::New(); newMapper->SetDataNode(node); node->SetMapper(3, ((CompositeMapper*)newMapper.GetPointer())->GetImageMapper()); } classname = "TensorImage"; if(node->GetData() && classname.compare(node->GetData()->GetNameOfClass())==0) { newMapper = mitk::CompositeMapper::New(); newMapper->SetDataNode(node); node->SetMapper(3, ((CompositeMapper*)newMapper.GetPointer())->GetImageMapper()); } + classname = "ShImage"; + if(node->GetData() && classname.compare(node->GetData()->GetNameOfClass())==0) + { + newMapper = mitk::CompositeMapper::New(); + newMapper->SetDataNode(node); + node->SetMapper(3, ((CompositeMapper*)newMapper.GetPointer())->GetImageMapper()); + } } else if ( id == mitk::BaseRenderer::Standard3D ) { std::string classname("OdfImage"); if(node->GetData() && classname.compare(node->GetData()->GetNameOfClass())==0) { newMapper = mitk::GPUVolumeMapper3D::New(); newMapper->SetDataNode(node); } classname = "TensorImage"; if(node->GetData() && classname.compare(node->GetData()->GetNameOfClass())==0) { newMapper = mitk::GPUVolumeMapper3D::New(); newMapper->SetDataNode(node); } + classname = "ShImage"; + if(node->GetData() && classname.compare(node->GetData()->GetNameOfClass())==0) + { + newMapper = mitk::GPUVolumeMapper3D::New(); + newMapper->SetDataNode(node); + } } return newMapper; } void mitk::DiffusionCoreObjectFactory::SetDefaultProperties(mitk::DataNode* node) { std::string classname = "OdfImage"; if(node->GetData() && classname.compare(node->GetData()->GetNameOfClass())==0) { mitk::CompositeMapper::SetDefaultProperties(node); mitk::GPUVolumeMapper3D::SetDefaultProperties(node); } classname = "TensorImage"; if(node->GetData() && classname.compare(node->GetData()->GetNameOfClass())==0) { mitk::CompositeMapper::SetDefaultProperties(node); mitk::GPUVolumeMapper3D::SetDefaultProperties(node); } + classname = "ShImage"; + if(node->GetData() && classname.compare(node->GetData()->GetNameOfClass())==0) + { + mitk::CompositeMapper::SetDefaultProperties(node); + mitk::GPUVolumeMapper3D::SetDefaultProperties(node); + } } const char* mitk::DiffusionCoreObjectFactory::GetFileExtensions() { std::string fileExtension; this->CreateFileExtensions(m_FileExtensionsMap, fileExtension); return fileExtension.c_str(); } mitk::CoreObjectFactoryBase::MultimapType mitk::DiffusionCoreObjectFactory::GetFileExtensionsMap() { return m_FileExtensionsMap; } const char* mitk::DiffusionCoreObjectFactory::GetSaveFileExtensions() { std::string fileExtension; this->CreateFileExtensions(m_SaveFileExtensionsMap, fileExtension); return fileExtension.c_str(); } mitk::CoreObjectFactoryBase::MultimapType mitk::DiffusionCoreObjectFactory::GetSaveFileExtensionsMap() { return m_SaveFileExtensionsMap; } void mitk::DiffusionCoreObjectFactory::CreateFileExtensionsMap() { } struct RegisterDiffusionCoreObjectFactory{ RegisterDiffusionCoreObjectFactory() : m_Factory( mitk::DiffusionCoreObjectFactory::New() ) { mitk::CoreObjectFactory::GetInstance()->RegisterExtraFactory( m_Factory ); } ~RegisterDiffusionCoreObjectFactory() { mitk::CoreObjectFactory::GetInstance()->UnRegisterExtraFactory( m_Factory ); } mitk::DiffusionCoreObjectFactory::Pointer m_Factory; }; static RegisterDiffusionCoreObjectFactory registerDiffusionCoreObjectFactory; diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdOdfImageReader.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdOdfImageReader.cpp index 18d96d8ff0..8faf43b592 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdOdfImageReader.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdOdfImageReader.cpp @@ -1,118 +1,118 @@ /*=================================================================== 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 "mitkNrrdOdfImageReader.h" #include #include "mitkDiffusionCoreIOMimeTypes.h" #include "itkImageFileReader.h" #include "itkImageRegionIterator.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "mitkITKImageImport.h" #include "mitkImageDataItem.h" #include namespace mitk { NrrdOdfImageReader::NrrdOdfImageReader(const NrrdOdfImageReader& other) : mitk::AbstractFileReader(other) { } NrrdOdfImageReader::NrrdOdfImageReader() : mitk::AbstractFileReader( CustomMimeType( mitk::DiffusionCoreIOMimeTypes::ODF_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::ODF_MIMETYPE_DESCRIPTION() ) { m_ServiceReg = this->RegisterService(); } NrrdOdfImageReader::~NrrdOdfImageReader() { } std::vector > NrrdOdfImageReader::Read() { std::vector > result; std::string location = GetInputLocation(); if ( location == "") { - throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, the filename of the vessel tree to be read is empty!"); + throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, the filename of the ODF image to be read is empty!"); } else { try { mitk::LocaleSwitch localeSwitch("C"); typedef itk::VectorImage ImageType; itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetImageIO(io); reader->SetFileName(location); reader->Update(); ImageType::Pointer img = reader->GetOutput(); typedef itk::Image,3> VecImgType; VecImgType::Pointer vecImg = VecImgType::New(); vecImg->SetSpacing( img->GetSpacing() ); // Set the image spacing vecImg->SetOrigin( img->GetOrigin() ); // Set the image origin vecImg->SetDirection( img->GetDirection() ); // Set the image direction vecImg->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); vecImg->SetBufferedRegion( img->GetLargestPossibleRegion() ); vecImg->Allocate(); itk::ImageRegionIterator ot (vecImg, vecImg->GetLargestPossibleRegion() ); ot.GoToBegin(); itk::ImageRegionIterator it (img, img->GetLargestPossibleRegion() ); typedef ImageType::PixelType VarPixType; typedef VecImgType::PixelType FixPixType; for (it.GoToBegin(); !it.IsAtEnd(); ++it) { VarPixType vec = it.Get(); FixPixType fixVec(vec.GetDataPointer()); ot.Set(fixVec); ++ot; } OutputType::Pointer resultImage = OutputType::New(); resultImage->InitializeByItk( vecImg.GetPointer() ); resultImage->SetVolume( vecImg->GetBufferPointer() ); result.push_back( resultImage.GetPointer() ); } catch(std::exception& e) { throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); } catch(...) { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, an error occurred while reading the requested vessel tree file!"); } } return result; } } //namespace MITK mitk::NrrdOdfImageReader* mitk::NrrdOdfImageReader::Clone() const { return new NrrdOdfImageReader(*this); } diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageReader.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageReader.cpp new file mode 100644 index 0000000000..58e3a45f00 --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageReader.cpp @@ -0,0 +1,130 @@ +/*=================================================================== + +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 "mitkShImageReader.h" +#include +#include "mitkDiffusionCoreIOMimeTypes.h" + +#include "itkImageFileReader.h" +#include "itkImageRegionIterator.h" +#include "itkMetaDataObject.h" +#include "itkNrrdImageIO.h" +#include "itkNiftiImageIO.h" +#include "mitkITKImageImport.h" +#include "mitkImageDataItem.h" +#include +#include +#include + +namespace mitk +{ + ShImageReader::ShImageReader(const ShImageReader& other) + : mitk::AbstractFileReader(other) + { + } + + ShImageReader::ShImageReader() + : mitk::AbstractFileReader( CustomMimeType( mitk::DiffusionCoreIOMimeTypes::SH_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::SH_MIMETYPE_DESCRIPTION() ) + { + m_ServiceReg = this->RegisterService(); + } + + ShImageReader::~ShImageReader() + { + } + + template + mitk::Image::Pointer ShImageReader::ConvertShImage(ShImage::ShOnDiskType::Pointer img) + { + typedef itk::ShCoefficientImageImporter< float, shOrder > ImporterType; + typename ImporterType::Pointer importer = ImporterType::New(); + importer->SetInputImage(img); + importer->GenerateData(); + + mitk::ShImage::Pointer shImage = mitk::ShImage::New(); + mitk::Image::Pointer resultImage = dynamic_cast(shImage.GetPointer()); + mitk::CastToMitkImage(importer->GetCoefficientImage(), resultImage); + resultImage->SetVolume(importer->GetCoefficientImage()->GetBufferPointer()); + return resultImage; + } + + std::vector > ShImageReader::Read() + { + std::vector > result; + std::string location = GetInputLocation(); + + if ( location == "") + { + throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, the filename is empty!"); + } + else + { + try + { + std::string ext = itksys::SystemTools::GetFilenameExtension(location); + typedef itk::ImageFileReader< ShImage::ShOnDiskType > FileReaderType; + FileReaderType::Pointer reader = FileReaderType::New(); + reader->SetFileName(location); + if (ext==".shi") + { + itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); + reader->SetImageIO(io); + } + reader->Update(); + ShImage::ShOnDiskType::Pointer img = reader->GetOutput(); + + switch (img->GetLargestPossibleRegion().GetSize()[3]) + { + case 6: + result.push_back( ConvertShImage<2>(img).GetPointer() ); + break; + case 15: + result.push_back( ConvertShImage<4>(img).GetPointer() ); + break; + case 28: + result.push_back( ConvertShImage<6>(img).GetPointer() ); + break; + case 45: + result.push_back( ConvertShImage<8>(img).GetPointer() ); + break; + case 66: + result.push_back( ConvertShImage<10>(img).GetPointer() ); + break; + case 91: + result.push_back( ConvertShImage<12>(img).GetPointer() ); + break; + default : + mitkThrow() << "SH order larger 12 not supported"; + } + } + catch(std::exception& e) + { + throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); + } + catch(...) + { + throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, an error occurred while reading the requested vessel tree file!"); + } + } + return result; + } + +} //namespace MITK + +mitk::ShImageReader* mitk::ShImageReader::Clone() const +{ + return new ShImageReader(*this); +} diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageReader.h b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageReader.h new file mode 100644 index 0000000000..673fa6a7ca --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageReader.h @@ -0,0 +1,63 @@ +/*=================================================================== + +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 __mitkNrrdShImageReader_h +#define __mitkNrrdShImageReader_h + +#include "mitkCommon.h" +#include "itkVectorContainer.h" +#include "vnl/vnl_vector_fixed.h" +#include "mitkShImage.h" +#include "itkVectorImage.h" +#include +#include +#include + +namespace mitk +{ + + /** \brief + */ + + class ShImageReader : public mitk::AbstractFileReader + { + public: + + typedef mitk::ShImage OutputType; + typedef itk::VectorImage VectorImageType; + + ShImageReader(const ShImageReader& other); + ShImageReader(); + virtual ~ShImageReader(); + + using AbstractFileReader::Read; + virtual std::vector > Read() override; + + protected: + + + private: + ShImageReader* Clone() const override; + + template + mitk::Image::Pointer ConvertShImage(ShImage::ShOnDiskType::Pointer img); + + us::ServiceRegistration m_ServiceReg; + }; + +} //namespace MITK + +#endif // __mitkNrrdShImageReader_h diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageSerializer.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageSerializer.cpp new file mode 100644 index 0000000000..2a47d38bfb --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageSerializer.cpp @@ -0,0 +1,74 @@ +/*=================================================================== + +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 "mitkShImageSerializer.h" +#include "mitkShImage.h" +#include "mitkShImageWriter.h" + +#include + + +MITK_REGISTER_SERIALIZER(ShImageSerializer) + + +mitk::ShImageSerializer::ShImageSerializer() +{ +} + + +mitk::ShImageSerializer::~ShImageSerializer() +{ +} + + +std::string mitk::ShImageSerializer::Serialize() +{ + const ShImage* image = dynamic_cast( m_Data.GetPointer() ); + if (image == nullptr) + { + MITK_ERROR << " Object at " << (const void*) this->m_Data + << " is not an mitk::NrrdShImage. Cannot serialize as NrrdShImage."; + return ""; + } + + std::string filename( this->GetUniqueFilenameInWorkingDirectory() ); + filename += "_"; + filename += m_FilenameHint; + filename += ".shi"; + + std::string fullname(m_WorkingDirectory); + fullname += "/"; + fullname += itksys::SystemTools::ConvertToOutputPath(filename.c_str()); + + try + { + mitk::ShImageWriter writer; + writer.SetOutputLocation(fullname); + writer.SetInput(image); + writer.Write(); + } + catch (std::exception& e) + { + MITK_ERROR << " Error serializing object at " << (const void*) this->m_Data + << " to " + << fullname + << ": " + << e.what(); + return ""; + } + return filename; +} + diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageSerializer.h b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageSerializer.h new file mode 100644 index 0000000000..4fefb9b71f --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageSerializer.h @@ -0,0 +1,39 @@ +/*=================================================================== + +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 mitkShImageSerializer_h_included +#define mitkShImageSerializer_h_included + +#include "mitkBaseDataSerializer.h" + +namespace mitk +{ +/** + \brief Serializes mitk::Surface for mitk::SceneIO +*/ +class ShImageSerializer : public BaseDataSerializer +{ + public: + mitkClassMacro( ShImageSerializer, BaseDataSerializer ); + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + virtual std::string Serialize() override; + protected: + ShImageSerializer(); + virtual ~ShImageSerializer(); +}; +} // namespace +#endif diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageWriter.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageWriter.cpp new file mode 100644 index 0000000000..6552b4514a --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageWriter.cpp @@ -0,0 +1,136 @@ +/*=================================================================== + +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 "mitkShImageWriter.h" +#include "itkMetaDataDictionary.h" +#include "itkMetaDataObject.h" +#include "itkImageFileWriter.h" +#include "mitkImageCast.h" +#include "mitkIOMimeTypes.h" +#include "mitkDiffusionCoreIOMimeTypes.h" +#include +#include +#include +#include + +mitk::ShImageWriter::ShImageWriter() + : AbstractFileWriter(mitk::ShImage::GetStaticNameOfClass(), CustomMimeType( mitk::DiffusionCoreIOMimeTypes::SH_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::SH_MIMETYPE_DESCRIPTION()) +{ + RegisterService(); +} + +mitk::ShImageWriter::ShImageWriter(const mitk::ShImageWriter& other) + : AbstractFileWriter(other) +{ +} + +mitk::ShImageWriter::~ShImageWriter() +{} + +template +void mitk::ShImageWriter::WriteShImage(InputType::ConstPointer input) +{ + typename itk::ShCoefficientImageExporter< float, shOrder >::InputImageType::Pointer itk_image = itk::ShCoefficientImageExporter< float, shOrder >::InputImageType::New(); + CastToItkImage(input, itk_image); + + typedef itk::ShCoefficientImageExporter< float, shOrder > ExporterType; + typename ExporterType::Pointer exporter = ExporterType::New(); + exporter->SetInputImage(itk_image); + exporter->GenerateData(); + + std::string ext = itksys::SystemTools::GetFilenameExtension(this->GetOutputLocation()); + + typedef itk::ImageFileWriter WriterType; + WriterType::Pointer writer = WriterType::New(); + writer->SetInput( exporter->GetOutputImage() ); + + if (ext==".shi") + { + itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); + io->SetFileType( itk::ImageIOBase::Binary ); + io->UseCompressionOn(); + writer->SetImageIO(io); + } + + writer->SetFileName(this->GetOutputLocation().c_str()); + writer->SetUseCompression(true); + + try + { + writer->Update(); + } + catch (itk::ExceptionObject e) + { + std::cout << e << std::endl; + } +} + +void mitk::ShImageWriter::Write() +{ + InputType::ConstPointer input = dynamic_cast(this->GetInput()); + if (input.IsNull()) + { + MITK_ERROR <<"Sorry, input to ShImageWriter is nullptr!"; + return; + } + if ( this->GetOutputLocation().empty() ) + { + MITK_ERROR << "Sorry, filename has not been set!"; + return ; + } + + switch (input->GetImageDescriptor()->GetChannelTypeById(0).GetNumberOfComponents()) + { + case 6: + WriteShImage<2>(input); + break; + case 15: + WriteShImage<4>(input); + break; + case 28: + WriteShImage<6>(input); + break; + case 45: + WriteShImage<8>(input); + break; + case 66: + WriteShImage<10>(input); + break; + case 91: + WriteShImage<12>(input); + break; + default : + mitkThrow() << "SH order larger 12 not supported"; + } +} + +mitk::ShImageWriter* mitk::ShImageWriter::Clone() const +{ + return new ShImageWriter(*this); +} + +mitk::IFileWriter::ConfidenceLevel mitk::ShImageWriter::GetConfidenceLevel() const +{ + InputType::ConstPointer input = dynamic_cast(this->GetInput()); + if (input.IsNull() ) + { + return Unsupported; + } + else + { + return Supported; + } +} diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageWriter.h b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageWriter.h new file mode 100644 index 0000000000..d3bf186cd3 --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkShImageWriter.h @@ -0,0 +1,58 @@ +/*=================================================================== + +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 _MITK_SH_WRITER__H_ +#define _MITK_SH_WRITER__H_ + +#include +#include + + +namespace mitk +{ + +/** + * Writes diffusion volumes to a file + * @ingroup Process + */ + class ShImageWriter : public mitk::AbstractFileWriter + { + public: + + typedef itk::VectorImage VecImgType; + typedef mitk::ShImage InputType; + + ShImageWriter(); + virtual ~ShImageWriter(); + + using AbstractFileWriter::Write; + virtual void Write() override; + + virtual ConfidenceLevel GetConfidenceLevel() const override; + + protected: + + ShImageWriter(const ShImageWriter& other); + virtual mitk::ShImageWriter* Clone() const override; + + template + void WriteShImage(InputType::ConstPointer input); + }; + + +} // end of namespace mitk + +#endif //_MITK_NRRDODF_WRITER__H_ diff --git a/Modules/DiffusionImaging/DiffusionCore/cmdapps/CMakeLists.txt b/Modules/DiffusionImaging/DiffusionCore/cmdapps/CMakeLists.txt index a70ed5f074..c94d269842 100644 --- a/Modules/DiffusionImaging/DiffusionCore/cmdapps/CMakeLists.txt +++ b/Modules/DiffusionImaging/DiffusionCore/cmdapps/CMakeLists.txt @@ -1,45 +1,46 @@ option(BUILD_DiffusionCoreCmdApps "Build commandline tools for diffusion" OFF) if(BUILD_DiffusionCoreCmdApps 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( diffusioncorecmdapps ImageResampler^^ CopyGeometry^^ Registration^^ DiffusionDICOMLoader^^ ResampleGradients^^ + ShToOdfImage^^ DImp^^ DReg^^ ) foreach(diffusioncorecmdapp ${diffusioncorecmdapps}) # extract cmd app name and dependencies string(REPLACE "^^" "\\;" cmdapp_info ${diffusioncorecmdapp}) 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() endif() mitkFunctionCreateCommandLineApp( NAME Dicom2Nrrd DEPENDS MitkCore ${dependencies_list} ) diff --git a/Modules/DiffusionImaging/DiffusionCore/cmdapps/ShToOdfImage.cpp b/Modules/DiffusionImaging/DiffusionCore/cmdapps/ShToOdfImage.cpp new file mode 100644 index 0000000000..95e1bde010 --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/cmdapps/ShToOdfImage.cpp @@ -0,0 +1,110 @@ +/*=================================================================== + +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 "mitkCommandLineParser.h" +#include +#include +#include + +template +mitk::OdfImage::Pointer TemplatedConvertShImage(mitk::ShImage::Pointer mitkImage) +{ + typedef itk::ShToOdfImageFilter< float, ShOrder > ShConverterType; + typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New(); + mitk::CastToItkImage(mitkImage, itkvol); + + typename ShConverterType::Pointer converter = ShConverterType::New(); + converter->SetInput(itkvol); + converter->Update(); + + mitk::OdfImage::Pointer image = mitk::OdfImage::New(); + image->InitializeByItk( converter->GetOutput() ); + image->SetVolume( converter->GetOutput()->GetBufferPointer() ); + return image; +} + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + + parser.setTitle("ShToOdfImage"); + parser.setCategory("Preprocessing Tools"); + parser.setDescription("Calculate discrete ODF image from SH coefficient image"); + parser.setContributor("MIC"); + + parser.setArgumentPrefix("--", "-"); + parser.addArgument("in", "i", mitkCommandLineParser::InputFile, "Input:", "input image", us::Any(), false); + parser.addArgument("out", "o", mitkCommandLineParser::OutputFile, "Output:", "output image", us::Any(), false); + + std::map parsedArgs = parser.parseArguments(argc, argv); + if (parsedArgs.size()==0) + return EXIT_FAILURE; + + // mandatory arguments + std::string imageName = us::any_cast(parsedArgs["in"]); + std::string outImage = us::any_cast(parsedArgs["out"]); + + try + { + mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor({"SH Image"}, {}); + mitk::ShImage::Pointer source = dynamic_cast(mitk::IOUtil::Load(imageName, &functor)[0].GetPointer()); + + mitk::OdfImage::Pointer out_image = nullptr; + switch (source->ShOrder()) + { + case 2: + out_image = TemplatedConvertShImage<2>(source); + break; + case 4: + out_image = TemplatedConvertShImage<4>(source); + break; + case 6: + out_image = TemplatedConvertShImage<6>(source); + break; + case 8: + out_image = TemplatedConvertShImage<8>(source); + break; + case 10: + out_image = TemplatedConvertShImage<10>(source); + break; + case 12: + out_image = TemplatedConvertShImage<12>(source); + break; + } + + if (out_image.IsNotNull()) + mitk::IOUtil::Save(out_image, outImage); + } + 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/DiffusionCore/files.cmake b/Modules/DiffusionImaging/DiffusionCore/files.cmake index b45035cfef..d76b1fbe9c 100644 --- a/Modules/DiffusionImaging/DiffusionCore/files.cmake +++ b/Modules/DiffusionImaging/DiffusionCore/files.cmake @@ -1,160 +1,163 @@ set(CPP_FILES # DicomImport # DicomImport/mitkGroupDiffusionHeadersFilter.cpp DicomImport/mitkDicomDiffusionImageHeaderReader.cpp DicomImport/mitkGEDicomDiffusionImageHeaderReader.cpp DicomImport/mitkPhilipsDicomDiffusionImageHeaderReader.cpp DicomImport/mitkSiemensDicomDiffusionImageHeaderReader.cpp DicomImport/mitkSiemensMosaicDicomDiffusionImageHeaderReader.cpp DicomImport/mitkDiffusionDICOMFileReader.cpp DicomImport/mitkDiffusionHeaderDICOMFileReader.cpp DicomImport/mitkDiffusionHeaderSiemensDICOMFileReader.cpp DicomImport/mitkDiffusionHeaderSiemensDICOMFileHelper.cpp DicomImport/mitkDiffusionHeaderSiemensMosaicDICOMFileReader.cpp DicomImport/mitkDiffusionHeaderGEDICOMFileReader.cpp DicomImport/mitkDiffusionHeaderPhilipsDICOMFileReader.cpp # DataStructures -> DWI IODataStructures/DiffusionWeightedImages/mitkDiffusionImageHeaderInformation.cpp IODataStructures/DiffusionWeightedImages/mitkDiffusionImageCorrectionFilter.cpp IODataStructures/DiffusionWeightedImages/mitkDiffusionImageCreationFilter.cpp # Properties IODataStructures/Properties/mitkBValueMapProperty.cpp IODataStructures/Properties/mitkGradientDirectionsProperty.cpp IODataStructures/Properties/mitkMeasurementFrameProperty.cpp IODataStructures/Properties/mitkDiffusionPropertyHelper.cpp IODataStructures/Properties/mitkNodePredicateIsDWI.cpp # Serializer IODataStructures/Properties/mitkBValueMapPropertySerializer.cpp IODataStructures/Properties/mitkGradientDirectionsPropertySerializer.cpp IODataStructures/Properties/mitkMeasurementFramePropertySerializer.cpp # DataStructures -> Odf IODataStructures/OdfImages/mitkOdfImageSource.cpp IODataStructures/OdfImages/mitkOdfImage.cpp + IODataStructures/mitkShImage.cpp + IODataStructures/mitkShImageSource.cpp # DataStructures -> Tensor IODataStructures/TensorImages/mitkTensorImage.cpp # DataStructures -> Peaks IODataStructures/mitkPeakImage.cpp Rendering/vtkMaskedProgrammableGlyphFilter.cpp Rendering/mitkVectorImageVtkGlyphMapper3D.cpp Rendering/vtkOdfSource.cxx Rendering/vtkThickPlane.cxx Rendering/mitkOdfNormalizationMethodProperty.cpp Rendering/mitkOdfScaleByProperty.cpp # Algorithms Algorithms/mitkPartialVolumeAnalysisHistogramCalculator.cpp Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.cpp Algorithms/itkDwiGradientLengthCorrectionFilter.cpp # Registration Algorithms & Co. Algorithms/Registration/mitkRegistrationWrapper.cpp Algorithms/Registration/mitkPyramidImageRegistrationMethod.cpp # Algorithms/Registration/mitkRegistrationMethodITK4.cpp Algorithms/Registration/mitkDWIHeadMotionCorrectionFilter.cpp # MultishellProcessing Algorithms/Reconstruction/MultishellProcessing/itkADCAverageFunctor.cpp Algorithms/Reconstruction/MultishellProcessing/itkADCFitFunctor.cpp Algorithms/Reconstruction/MultishellProcessing/itkKurtosisFitFunctor.cpp Algorithms/Reconstruction/MultishellProcessing/itkBiExpFitFunctor.cpp # Function Collection mitkDiffusionFunctionCollection.cpp ) set(H_FILES # function Collection include/mitkDiffusionFunctionCollection.h # Rendering include/Rendering/mitkOdfVtkMapper2D.h # Reconstruction include/Algorithms/Reconstruction/itkDiffusionQballReconstructionImageFilter.h include/Algorithms/Reconstruction/mitkTeemDiffusionTensor3DReconstructionImageFilter.h include/Algorithms/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.h include/Algorithms/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.h include/Algorithms/Reconstruction/itkPointShell.h include/Algorithms/Reconstruction/itkOrientationDistributionFunction.h include/Algorithms/Reconstruction/itkDiffusionIntravoxelIncoherentMotionReconstructionImageFilter.h include/Algorithms/Reconstruction/itkDiffusionKurtosisReconstructionImageFilter.h include/Algorithms/Reconstruction/itkBallAndSticksImageFilter.h include/Algorithms/Reconstruction/itkMultiTensorImageFilter.h # Fitting functions include/Algorithms/Reconstruction/FittingFunctions/mitkAbstractFitter.h include/Algorithms/Reconstruction/FittingFunctions/mitkMultiTensorFitter.h include/Algorithms/Reconstruction/FittingFunctions/mitkBallStickFitter.h # MultishellProcessing include/Algorithms/Reconstruction/MultishellProcessing/itkRadialMultishellToSingleshellImageFilter.h include/Algorithms/Reconstruction/MultishellProcessing/itkDWIVoxelFunctor.h include/Algorithms/Reconstruction/MultishellProcessing/itkADCAverageFunctor.h include/Algorithms/Reconstruction/MultishellProcessing/itkKurtosisFitFunctor.h include/Algorithms/Reconstruction/MultishellProcessing/itkBiExpFitFunctor.h include/Algorithms/Reconstruction/MultishellProcessing/itkADCFitFunctor.h # Properties include/IODataStructures/Properties/mitkBValueMapProperty.h include/IODataStructures/Properties/mitkGradientDirectionsProperty.h include/IODataStructures/Properties/mitkMeasurementFrameProperty.h include/IODataStructures/Properties/mitkDiffusionPropertyHelper.h include/IODataStructures/DiffusionWeightedImages/mitkDiffusionImageTransformedCreationFilter.h # Algorithms include/Algorithms/itkDiffusionOdfGeneralizedFaImageFilter.h include/Algorithms/itkDiffusionOdfPrepareVisualizationImageFilter.h include/Algorithms/itkElectrostaticRepulsionDiffusionGradientReductionFilter.h include/Algorithms/itkTensorDerivedMeasurementsFilter.h include/Algorithms/itkBrainMaskExtractionImageFilter.h include/Algorithms/itkB0ImageExtractionImageFilter.h include/Algorithms/itkB0ImageExtractionToSeparateImageFilter.h include/Algorithms/itkTensorImageToDiffusionImageFilter.h include/Algorithms/itkTensorToL2NormImageFilter.h include/Algorithms/itkGaussianInterpolateImageFunction.h include/Algorithms/mitkPartialVolumeAnalysisHistogramCalculator.h include/Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.h include/Algorithms/itkDiffusionTensorPrincipalDirectionImageFilter.h include/Algorithms/itkCartesianToPolarVectorImageFilter.h include/Algorithms/itkPolarToCartesianVectorImageFilter.h include/Algorithms/itkDistanceMapFilter.h include/Algorithms/itkProjectionFilter.h include/Algorithms/itkResidualImageFilter.h include/Algorithms/itkExtractChannelFromRgbaImageFilter.h include/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.h include/Algorithms/itkMergeDiffusionImagesFilter.h include/Algorithms/itkFiniteDiffOdfMaximaExtractionFilter.h include/Algorithms/itkShCoefficientImageImporter.h include/Algorithms/itkShCoefficientImageExporter.h include/Algorithms/itkOdfMaximaExtractionFilter.h include/Algorithms/itkResampleDwiImageFilter.h include/Algorithms/itkDwiGradientLengthCorrectionFilter.h include/Algorithms/itkAdcImageFilter.h include/Algorithms/itkDwiNormilzationFilter.h include/Algorithms/itkSplitDWImageFilter.h include/Algorithms/itkRemoveDwiChannelFilter.h include/Algorithms/itkExtractDwiChannelFilter.h include/Algorithms/itkFlipPeaksFilter.h + include/Algorithms/itkShToOdfImageFilter.h include/Algorithms/Registration/mitkDWIHeadMotionCorrectionFilter.h include/Algorithms/itkNonLocalMeansDenoisingFilter.h include/Algorithms/itkVectorImageToImageFilter.h ) set( TOOL_FILES ) diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.cpp b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.cpp index 5714634f89..c436d2bae6 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.cpp @@ -1,752 +1,657 @@ /*=================================================================== 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 __itkAnalyticalDiffusionQballReconstructionImageFilter_cpp #define __itkAnalyticalDiffusionQballReconstructionImageFilter_cpp #include #include #include #include #include #include +#include -#include #include #include #include - #define _USE_MATH_DEFINES #include - -#include - #include "itkPointShell.h" -using namespace boost::math; - namespace itk { template< class T, class TG, class TO, int ShOrder, int NrOdfDirections> AnalyticalDiffusionQballReconstructionImageFilter::AnalyticalDiffusionQballReconstructionImageFilter() : m_GradientDirectionContainer(nullptr), m_NumberOfGradientDirections(0), m_NumberOfBaselineImages(1), m_Threshold(NumericTraits< ReferencePixelType >::NonpositiveMin()), m_BValue(-1), m_Lambda(0.0), m_DirectionsDuplicated(false), m_Delta1(0.001), m_Delta2(0.001), m_UseMrtrixBasis(false) { // At least 1 inputs is necessary for a vector image. // For images added one at a time we need at least six this->SetNumberOfRequiredInputs( 1 ); } template< class TReferenceImagePixelType, class TGradientImagePixelType, class TOdfPixelType, int ShOrder, int NrOdfDirections> typename itk::AnalyticalDiffusionQballReconstructionImageFilter< TReferenceImagePixelType,TGradientImagePixelType,TOdfPixelType, ShOrder,NrOdfDirections>::OdfPixelType itk::AnalyticalDiffusionQballReconstructionImageFilter::Normalize( OdfPixelType odf, typename NumericTraits::AccumulateType b0 ) { switch( m_NormalizationMethod ) { case QBAR_STANDARD: { TOdfPixelType sum = 0; for(int i=0; i0) odf /= sum; return odf; break; } case QBAR_B_ZERO_B_VALUE: { for(int i=0; i vnl_vector itk::AnalyticalDiffusionQballReconstructionImageFilter::PreNormalize( vnl_vector vec, typename NumericTraits::AccumulateType b0 ) { switch( m_NormalizationMethod ) { case QBAR_STANDARD: { int n = vec.size(); double b0f = (double)b0; for(int i=0; i=1) vec[i] = 1-m_Delta2/2; else if (vec[i]>=1-m_Delta2) vec[i] = 1-m_Delta2/2-(1-vec[i])*(1-vec[i])/(2*m_Delta2); vec[i] = log(-log(vec[i])); } return vec; break; } } return vec; } template< class T, class TG, class TO, int ShOrder, int NrOdfDirections> void AnalyticalDiffusionQballReconstructionImageFilter::BeforeThreadedGenerateData() { // If we have more than 2 inputs, then each input, except the first is a // gradient image. The number of gradient images must match the number of // gradient directions. //const unsigned int numberOfInputs = this->GetNumberOfInputs(); // There need to be at least 6 gradient directions to be able to compute the // tensor basis if( m_NumberOfGradientDirections < (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder ) { itkExceptionMacro( << "Not enough gradient directions supplied (" << m_NumberOfGradientDirections << "). At least " << (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder << " needed for SH-order " << ShOrder); } // Input must be an itk::VectorImage. std::string gradientImageClassName( this->ProcessObject::GetInput(0)->GetNameOfClass()); if ( strcmp(gradientImageClassName.c_str(),"VectorImage") != 0 ) { itkExceptionMacro( << "There is only one Gradient image. I expect that to be a VectorImage. " << "But its of type: " << gradientImageClassName ); } this->ComputeReconstructionMatrix(); typename GradientImagesType::Pointer img = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); m_BZeroImage = BZeroImageType::New(); m_BZeroImage->SetSpacing( img->GetSpacing() ); // Set the image spacing m_BZeroImage->SetOrigin( img->GetOrigin() ); // Set the image origin m_BZeroImage->SetDirection( img->GetDirection() ); // Set the image direction m_BZeroImage->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); m_BZeroImage->SetBufferedRegion( img->GetLargestPossibleRegion() ); m_BZeroImage->Allocate(); m_ODFSumImage = BZeroImageType::New(); m_ODFSumImage->SetSpacing( img->GetSpacing() ); // Set the image spacing m_ODFSumImage->SetOrigin( img->GetOrigin() ); // Set the image origin m_ODFSumImage->SetDirection( img->GetDirection() ); // Set the image direction m_ODFSumImage->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); m_ODFSumImage->SetBufferedRegion( img->GetLargestPossibleRegion() ); m_ODFSumImage->Allocate(); m_CoefficientImage = CoefficientImageType::New(); m_CoefficientImage->SetSpacing( img->GetSpacing() ); // Set the image spacing m_CoefficientImage->SetOrigin( img->GetOrigin() ); // Set the image origin m_CoefficientImage->SetDirection( img->GetDirection() ); // Set the image direction m_CoefficientImage->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); m_CoefficientImage->SetBufferedRegion( img->GetLargestPossibleRegion() ); m_CoefficientImage->Allocate(); if(m_NormalizationMethod == QBAR_SOLID_ANGLE || m_NormalizationMethod == QBAR_NONNEG_SOLID_ANGLE) m_Lambda = 0.0; } template< class T, class TG, class TO, int ShOrder, int NrOdfDirections> void AnalyticalDiffusionQballReconstructionImageFilter ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType ) { typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetPrimaryOutput()); ImageRegionIterator< OutputImageType > oit(outputImage, outputRegionForThread); oit.GoToBegin(); ImageRegionIterator< BZeroImageType > oit2(m_BZeroImage, outputRegionForThread); oit2.GoToBegin(); ImageRegionIterator< FloatImageType > oit3(m_ODFSumImage, outputRegionForThread); oit3.GoToBegin(); ImageRegionIterator< CoefficientImageType > oit4(m_CoefficientImage, outputRegionForThread); oit4.GoToBegin(); typedef ImageRegionConstIterator< GradientImagesType > GradientIteratorType; typedef typename GradientImagesType::PixelType GradientVectorType; typename GradientImagesType::Pointer gradientImagePointer = nullptr; // Would have liked a dynamic_cast here, but seems SGI doesn't like it // The enum will ensure that an inappropriate cast is not done gradientImagePointer = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); GradientIteratorType git(gradientImagePointer, outputRegionForThread ); git.GoToBegin(); // Compute the indicies of the baseline images and gradient images std::vector baselineind; // contains the indicies of // the baseline images std::vector gradientind; // contains the indicies of // the gradient images for(GradientDirectionContainerType::ConstIterator gdcit = this->m_GradientDirectionContainer->Begin(); gdcit != this->m_GradientDirectionContainer->End(); ++gdcit) { float bval = gdcit.Value().two_norm(); bval = bval*bval*m_BValue; if(bval < 100) baselineind.push_back(gdcit.Index()); else gradientind.push_back(gdcit.Index()); } if( m_DirectionsDuplicated ) { int gradIndSize = gradientind.size(); for(int i=0; i::AccumulateType b0 = NumericTraits::Zero; // Average the baseline image pixels for(unsigned int i = 0; i < baselineind.size(); ++i) { b0 += b[baselineind[i]]; } b0 /= this->m_NumberOfBaselineImages; OdfPixelType odf(0.0); typename CoefficientImageType::PixelType coeffPixel(0.0); vnl_vector B(m_NumberOfGradientDirections); if( (b0 != 0) && (b0 >= m_Threshold) ) { for( unsigned int i = 0; i< m_NumberOfGradientDirections; i++ ) { B[i] = static_cast(b[gradientind[i]]); } B = PreNormalize(B, b0); if(m_NormalizationMethod == QBAR_SOLID_ANGLE) { vnl_vector coeffs(m_NumberCoefficients); coeffs = ( m_CoeffReconstructionMatrix * B ); coeffs[0] += 1.0/(2.0*sqrt(M_PI)); odf = ( m_SphericalHarmonicBasisMatrix * coeffs ).data_block(); coeffPixel = coeffs.data_block(); } else if(m_NormalizationMethod == QBAR_NONNEG_SOLID_ANGLE) { /** this would be the place to implement a non-negative * solver for quadratic programming problem: * min .5*|| Bc-s ||^2 subject to -CLPc <= 4*pi*ones * (refer to MICCAI 2009 Goh et al. "Estimating ODFs with PDF constraints") * .5*|| Bc-s ||^2 == .5*c'B'Bc - x'B's + .5*s's */ itkExceptionMacro( << "Nonnegative Solid Angle not yet implemented"); } else { vnl_vector coeffs(m_NumberCoefficients); coeffs = ( m_CoeffReconstructionMatrix * B ); coeffs[0] += 1.0/(2.0*sqrt(M_PI)); coeffPixel = coeffs.data_block(); odf = ( m_ReconstructionMatrix * B ).data_block(); } odf = Normalize(odf, b0); } oit.Set( odf ); oit2.Set( b0 ); float sum = 0; for (unsigned int k=0; k void AnalyticalDiffusionQballReconstructionImageFilter ::tofile2(vnl_matrix *pA, std::string fname) { vnl_matrix A = (*pA); std::ofstream myfile; std::locale C("C"); std::locale originalLocale = myfile.getloc(); myfile.imbue(C); myfile.open (fname.c_str()); myfile << "A1=["; for(unsigned int i=0; i -void AnalyticalDiffusionQballReconstructionImageFilter::Cart2Sph(double x, double y, double z, double *spherical) -{ - double phi, theta, r; - r = sqrt(x*x+y*y+z*z); - - if( r -double AnalyticalDiffusionQballReconstructionImageFilter::Yj(int m, int l, double theta, double phi, bool useMRtrixBasis) -{ - if (!useMRtrixBasis) - { - if (m<0) - return sqrt(2.0)*spherical_harmonic_r(l, -m, theta, phi); - else if (m==0) - return spherical_harmonic_r(l, m, theta, phi); - else - return pow(-1.0,m)*sqrt(2.0)*spherical_harmonic_i(l, m, theta, phi); - } - else - { - double plm = legendre_p(l,abs(m),-cos(theta)); - double mag = sqrt((double)(2*l+1)/(4.0*M_PI)*factorial(l-abs(m))/factorial(l+abs(m)))*plm; - if (m>0) - return mag*cos(m*phi); - else if (m==0) - return mag; - else - return mag*sin(-m*phi); - } - - return 0; -} - -template< class T, class TG, class TO, int ShOrder, int NrOdfDirections> -double AnalyticalDiffusionQballReconstructionImageFilter::Legendre0(int l) -{ - if( l%2 != 0 ) - { - return 0; - } - else - { - double prod1 = 1.0; - for(int i=1;i void AnalyticalDiffusionQballReconstructionImageFilter::ComputeReconstructionMatrix() { m_NumberCoefficients = (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder; if( m_NumberOfGradientDirections < m_NumberCoefficients ) { itkExceptionMacro( << "Not enough gradient directions supplied (" << m_NumberOfGradientDirections << "). At least " << (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder << " needed for SH-order " << ShOrder); } // Gradient preprocessing { // check for duplicate diffusion gradients bool warning = false; for(GradientDirectionContainerType::ConstIterator gdcit1 = this->m_GradientDirectionContainer->Begin(); gdcit1 != this->m_GradientDirectionContainer->End(); ++gdcit1) { for(GradientDirectionContainerType::ConstIterator gdcit2 = this->m_GradientDirectionContainer->Begin(); gdcit2 != this->m_GradientDirectionContainer->End(); ++gdcit2) { if(gdcit1.Value() == gdcit2.Value() && gdcit1.Index() != gdcit2.Index()) { itkWarningMacro( << "Some of the Diffusion Gradients equal each other. Corresponding image data should be averaged before calling this filter." ); warning = true; break; } } if (warning) break; } // handle acquisition schemes where only half of the spherical // shell is sampled by the gradient directions. In this case, // each gradient direction is duplicated in negative direction. vnl_vector centerMass(3); centerMass.fill(0.0); int count = 0; for(GradientDirectionContainerType::ConstIterator gdcit1 = this->m_GradientDirectionContainer->Begin(); gdcit1 != this->m_GradientDirectionContainer->End(); ++gdcit1) { float bval = gdcit1.Value().two_norm(); bval = bval*bval*m_BValue; if(bval > 100) { centerMass += gdcit1.Value(); count ++; } } centerMass /= count; if(centerMass.two_norm() > 0.1) { m_DirectionsDuplicated = true; m_NumberOfGradientDirections *= 2; } } // Create 3xM matrix Q that contains the gradient vectors in polar coordinates vnl_matrix Q(3, m_NumberOfGradientDirections); { int i = 0; for(GradientDirectionContainerType::ConstIterator gdcit = this->m_GradientDirectionContainer->Begin(); gdcit != this->m_GradientDirectionContainer->End(); ++gdcit) { float bval = gdcit.Value().two_norm(); bval = bval*bval*m_BValue; if(bval > 100) { double x = gdcit.Value().get(0); double y = gdcit.Value().get(1); double z = gdcit.Value().get(2); double cart[3]; - Cart2Sph(x,y,z,cart); + mitk::sh::Cart2Sph(x,y,z,cart); Q(0,i) = cart[0]; Q(1,i) = cart[1]; Q(2,i++) = cart[2]; } } if(m_DirectionsDuplicated) { for(GradientDirectionContainerType::ConstIterator gdcit = this->m_GradientDirectionContainer->Begin(); gdcit != this->m_GradientDirectionContainer->End(); ++gdcit) { float bval = gdcit.Value().two_norm(); bval = bval*bval*m_BValue; if(bval > 100) { double x = gdcit.Value().get(0); double y = gdcit.Value().get(1); double z = gdcit.Value().get(2); double cart[3]; - Cart2Sph(x,y,z,cart); + mitk::sh::Cart2Sph(x,y,z,cart); Q(0,i) = cart[0]; Q(1,i) = cart[1]; Q(2,i++) = cart[2]; } } } } // Calcualte SH basis B vnl_matrix L(m_NumberCoefficients,m_NumberCoefficients, 0); vnl_vector lj(m_NumberCoefficients); vnl_matrix B(m_NumberOfGradientDirections,m_NumberCoefficients); for(unsigned int i=0; i P(m_NumberCoefficients,m_NumberCoefficients, 0); for(unsigned int i=0; i B_transpose(B.transpose()); vnl_matrix_inverse* pseudoInverse = new vnl_matrix_inverse( B_transpose * B + L*L*m_Lambda ); m_CoeffReconstructionMatrix = pseudoInverse->pinverse() * B_transpose; switch(m_NormalizationMethod) { case QBAR_ADC_ONLY: case QBAR_RAW_SIGNAL: break; case QBAR_STANDARD: case QBAR_B_ZERO_B_VALUE: case QBAR_B_ZERO: case QBAR_NONE: { m_CoeffReconstructionMatrix = P * m_CoeffReconstructionMatrix; break; } case QBAR_SOLID_ANGLE: { m_CoeffReconstructionMatrix = (float)(1.0/(8.0*M_PI)) * P * L * m_CoeffReconstructionMatrix; break; } case QBAR_NONNEG_SOLID_ANGLE: break; } - // this code goes to the image adapter coeffs->odfs later + // needed to calculate the ODF values from the SH coefficients vnl_matrix_fixed* U = itk::PointShell >::DistributePointShell(); - m_SphericalHarmonicBasisMatrix = vnl_matrix(NrOdfDirections,m_NumberCoefficients); - for(int i=0; ias_matrix()); m_ReconstructionMatrix = m_SphericalHarmonicBasisMatrix * m_CoeffReconstructionMatrix; } template< class T, class TG, class TO, int ShOrder, int NrOdfDirections> void AnalyticalDiffusionQballReconstructionImageFilter ::SetGradientImage(const GradientDirectionContainerType *gradientDirection, const GradientImagesType *gradientImage ) { // Copy Gradient Direction Container this->m_GradientDirectionContainer = GradientDirectionContainerType::New(); for(GradientDirectionContainerType::ConstIterator it = gradientDirection->Begin(); it != gradientDirection->End(); it++) { this->m_GradientDirectionContainer->push_back(it.Value()); } if (m_BValue<0) itkExceptionMacro("B-value needs to best before gradient image!"); unsigned int numImages = gradientDirection->Size(); this->m_NumberOfBaselineImages = 0; for(GradientDirectionContainerType::Iterator it = this->m_GradientDirectionContainer->Begin(); it != this->m_GradientDirectionContainer->End(); it++) { float bval = it.Value().two_norm(); bval = bval*bval*m_BValue; if(bval < 100) { this->m_NumberOfBaselineImages++; } else // Normalize non-zero gradient directions { it.Value() = it.Value() / it.Value().two_norm(); } } if (this->m_NumberOfBaselineImages==0) itkExceptionMacro("No baseline image detected (no b-zero image)"); this->m_NumberOfGradientDirections = numImages - this->m_NumberOfBaselineImages; // ensure that the gradient image we received has as many components as // the number of gradient directions if( gradientImage->GetVectorLength() != this->m_NumberOfBaselineImages + m_NumberOfGradientDirections ) { itkExceptionMacro( << m_NumberOfGradientDirections << " gradients + " << this->m_NumberOfBaselineImages << "baselines = " << m_NumberOfGradientDirections + this->m_NumberOfBaselineImages << " directions specified but image has " << gradientImage->GetVectorLength() << " components."); } this->ProcessObject::SetNthInput( 0, const_cast< GradientImagesType* >(gradientImage) ); } template< class T, class TG, class TO, int ShOrder, int NrOdfDirections> void AnalyticalDiffusionQballReconstructionImageFilter ::PrintSelf(std::ostream& os, Indent indent) const { std::locale C("C"); std::locale originalLocale = os.getloc(); os.imbue(C); Superclass::PrintSelf(os,indent); os << indent << "OdfReconstructionMatrix: " << m_ReconstructionMatrix << std::endl; if ( m_GradientDirectionContainer ) os << indent << "GradientDirectionContainer: " << m_GradientDirectionContainer << std::endl; else os << indent << "GradientDirectionContainer: (Gradient directions not set)" << std::endl; os << indent << "NumberOfGradientDirections: " << m_NumberOfGradientDirections << std::endl; os << indent << "NumberOfBaselineImages: " << m_NumberOfBaselineImages << std::endl; os << indent << "Threshold for reference B0 image: " << m_Threshold << std::endl; os << indent << "BValue: " << m_BValue << std::endl; os.imbue( originalLocale ); } } #endif // __itkAnalyticalDiffusionQballReconstructionImageFilter_cpp diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.h b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.h index e5753bc571..f6bdc34dcb 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.h +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.h @@ -1,278 +1,269 @@ /*=================================================================== 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 __itkAnalyticalDiffusionQballReconstructionImageFilter_h_ #define __itkAnalyticalDiffusionQballReconstructionImageFilter_h_ #include "itkImageToImageFilter.h" #include "vnl/vnl_vector_fixed.h" #include "vnl/vnl_matrix.h" #include "vnl/algo/vnl_svd.h" #include "itkVectorContainer.h" #include "itkVectorImage.h" namespace itk{ /** \class AnalyticalDiffusionQballReconstructionImageFilter * \brief This class takes as input one or more reference image (acquired in the * absence of diffusion sensitizing gradients) and 'n' diffusion * weighted images and their gradient directions and computes an image of * orientation distribution function coefficients in a spherical harmonic basis. * * \par Inputs and Usage * \par * When you have the 'n' gradient and one or more reference images in a single * multi-component image (VectorImage), you can specify the images as * \code * filter->SetGradientImage( directionsContainer, vectorImage ); * \endcode * Note that this method is used to specify both the reference and gradient images. * This is convenient when the DWI images are read in using the * NRRD * format. Like the Nrrd format, the reference images are those components of the * vectorImage whose gradient direction is (0,0,0). If more than one reference image * is present, they are averaged prior to the reconstruction. * * \par Outputs * The output image is an image of vectors that must be understood as ODFs: * \code * Image< Vector< TPixelType, OdfNrDirections >, 3 > * \endcode * * \par Parameters * \li Threshold - Threshold on the reference image data. The output ODF will * be a null pdf for pixels in the reference image that have a value less * than this. * \li BValue - See the documentation of SetBValue(). * \li At least 6 gradient images must be specified for the filter to be able * to run. If the input gradient directions g_i are majorly sampled on one half * of the sqhere, then each input image I_i will be duplicated and assign -g_i * in order to guarantee stability of the algorithm. * \li OdfDirections - directions of resulting orientation distribution function * \li EquatorNrSamplingPoints - number of sampling points on equator when * performing Funk Radeon Transform (FRT) * \li BasisFunctionCenters - the centers of the basis functions are used for * the sRBF (spherical radial basis functions interpolation). If not set, they * will be defaulted to equal m_EquatorNrSamplingPoints * * \par Template parameters * The class is templated over * \li the pixel type of the reference and gradient images * (expected to be scalar data types) * \li the internal representation of the ODF pixels (double, float etc). * \li the number of OdfDirections * \li the number of basis function centers for the sRBF * * \par References: * \li[1] * Tuch DS, * "Q-ball imaging", Magn Reson Med. 2004 Dec;52(6):1358-72. * */ -template< class TReferenceImagePixelType, - class TGradientImagePixelType, - class TOdfPixelType, - int ShOrder, - int NrOdfDirections> -class AnalyticalDiffusionQballReconstructionImageFilter : - public ImageToImageFilter< Image< TReferenceImagePixelType, 3 >, - Image< Vector< TOdfPixelType, NrOdfDirections >, 3 > > +template< class TReferenceImagePixelType, class TGradientImagePixelType, class TOdfPixelType, int ShOrder, int NrOdfDirections> +class AnalyticalDiffusionQballReconstructionImageFilter : public ImageToImageFilter< Image< TReferenceImagePixelType, 3 >, Image< Vector< TOdfPixelType, NrOdfDirections >, 3 > > { public: enum Normalization { QBAR_STANDARD, QBAR_B_ZERO_B_VALUE, QBAR_B_ZERO, QBAR_NONE, QBAR_ADC_ONLY, QBAR_RAW_SIGNAL, QBAR_SOLID_ANGLE, QBAR_NONNEG_SOLID_ANGLE }; typedef AnalyticalDiffusionQballReconstructionImageFilter Self; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef ImageToImageFilter< Image< TReferenceImagePixelType, 3>, Image< Vector< TOdfPixelType, NrOdfDirections >, 3 > > Superclass; /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Runtime information support. */ itkTypeMacro(AnalyticalDiffusionQballReconstructionImageFilter, ImageToImageFilter) typedef TReferenceImagePixelType ReferencePixelType; typedef TGradientImagePixelType GradientPixelType; typedef Vector< TOdfPixelType, NrOdfDirections > OdfPixelType; typedef TOdfPixelType BZeroPixelType; /** Reference image data, This image is aquired in the absence * of a diffusion sensitizing field gradient */ typedef typename Superclass::InputImageType ReferenceImageType; typedef Image< OdfPixelType, 3 > OdfImageType; typedef OdfImageType OutputImageType; typedef Image< Vector< TOdfPixelType, (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder >, 3 > CoefficientImageType; typedef Image< BZeroPixelType, 3 > BZeroImageType; typedef typename Superclass::OutputImageRegionType OutputImageRegionType; /** Typedef defining one (of the many) gradient images. */ typedef Image< GradientPixelType, 3 > GradientImageType; /** An alternative typedef defining one (of the many) gradient images. * It will be assumed that the vectorImage has the same dimension as the * Reference image and a vector length parameter of \c n (number of * gradient directions)*/ typedef VectorImage< GradientPixelType, 3 > GradientImagesType; /** Holds each magnetic field gradient used to acquire one DWImage */ typedef vnl_vector_fixed< double, 3 > GradientDirectionType; /** Container to hold gradient directions of the 'n' DW measurements */ typedef VectorContainer< unsigned int, GradientDirectionType > GradientDirectionContainerType; /** set method to add gradient directions and its corresponding * image. The image here is a VectorImage. The user is expected to pass the * gradient directions in a container. The ith element of the container * corresponds to the gradient direction of the ith component image the * VectorImage. For the baseline image, a vector of all zeros * should be set.*/ void SetGradientImage( const GradientDirectionContainerType *, const GradientImagesType *image); /** Get reference image */ virtual ReferenceImageType * GetReferenceImage() { return ( static_cast< ReferenceImageType *>(this->ProcessObject::GetInput(0)) ); } /** Return the gradient direction. idx is 0 based */ virtual GradientDirectionType GetGradientDirection( unsigned int idx) const { if( idx >= m_NumberOfGradientDirections ) itkExceptionMacro( << "Gradient direction " << idx << "does not exist" ); return m_GradientDirectionContainer->ElementAt( idx+1 ); } static void tofile2(vnl_matrix *A, std::string fname); - static void Cart2Sph(double x, double y, double z, double* cart); - static double Yj(int m, int k, double theta, double phi, bool useMRtrixBasis = false); - double Legendre0(int l); OdfPixelType Normalize(OdfPixelType odf, typename NumericTraits::AccumulateType b0 ); vnl_vector PreNormalize( vnl_vector vec, typename NumericTraits::AccumulateType b0 ); /** Threshold on the reference image data. The output ODF will be a null * pdf for pixels in the reference image that have a value less than this * threshold. */ itkSetMacro( Threshold, ReferencePixelType ) itkGetMacro( Threshold, ReferencePixelType ) itkSetMacro( NormalizationMethod, Normalization) itkGetMacro( NormalizationMethod, Normalization ) typedef Image FloatImageType; itkGetMacro( BZeroImage, typename BZeroImageType::Pointer) itkGetMacro( ODFSumImage, typename FloatImageType::Pointer) itkGetMacro( CoefficientImage, typename CoefficientImageType::Pointer) itkSetMacro( BValue, TOdfPixelType) #ifdef GetBValue #undef GetBValue #endif itkGetConstReferenceMacro( BValue, TOdfPixelType) itkSetMacro( Lambda, double ) itkGetMacro( Lambda, double ) itkSetMacro( UseMrtrixBasis, bool ) #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(ReferenceEqualityComparableCheck, (Concept::EqualityComparable)); itkConceptMacro(TensorEqualityComparableCheck, (Concept::EqualityComparable)); itkConceptMacro(GradientConvertibleToDoubleCheck, (Concept::Convertible)); itkConceptMacro(DoubleConvertibleToTensorCheck, (Concept::Convertible)); itkConceptMacro(GradientReferenceAdditiveOperatorsCheck, (Concept::AdditiveOperators)); itkConceptMacro(ReferenceOStreamWritableCheck, (Concept::OStreamWritable)); itkConceptMacro(TensorOStreamWritableCheck, (Concept::OStreamWritable)); /** End concept checking */ #endif protected: AnalyticalDiffusionQballReconstructionImageFilter(); ~AnalyticalDiffusionQballReconstructionImageFilter() {}; void PrintSelf(std::ostream& os, Indent indent) const; void ComputeReconstructionMatrix(); void BeforeThreadedGenerateData(); void ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, ThreadIdType); private: vnl_matrix< float > m_ReconstructionMatrix; vnl_matrix< float > m_CoeffReconstructionMatrix; vnl_matrix< float > m_SphericalHarmonicBasisMatrix; /** container to hold gradient directions */ GradientDirectionContainerType::Pointer m_GradientDirectionContainer; /** Number of gradient measurements */ unsigned int m_NumberOfGradientDirections; /** Number of baseline images */ unsigned int m_NumberOfBaselineImages; /** Threshold on the reference image data */ ReferencePixelType m_Threshold; /** LeBihan's b-value for normalizing tensors */ TOdfPixelType m_BValue; typename BZeroImageType::Pointer m_BZeroImage; double m_Lambda; bool m_DirectionsDuplicated; Normalization m_NormalizationMethod; unsigned int m_NumberCoefficients; FloatImageType::Pointer m_ODFSumImage; typename CoefficientImageType::Pointer m_CoefficientImage; TOdfPixelType m_Delta1; TOdfPixelType m_Delta2; bool m_UseMrtrixBasis; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkAnalyticalDiffusionQballReconstructionImageFilter.cpp" #endif #endif //__itkAnalyticalDiffusionQballReconstructionImageFilter_h_ diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkFiniteDiffOdfMaximaExtractionFilter.cpp b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkFiniteDiffOdfMaximaExtractionFilter.cpp index 0b0fe5064c..7bec5289de 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkFiniteDiffOdfMaximaExtractionFilter.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkFiniteDiffOdfMaximaExtractionFilter.cpp @@ -1,480 +1,430 @@ /*=================================================================== 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 __itkFiniteDiffOdfMaximaExtractionFilter_cpp #define __itkFiniteDiffOdfMaximaExtractionFilter_cpp #include "itkFiniteDiffOdfMaximaExtractionFilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include - +#include using namespace boost::math; namespace itk { static bool CompareVectors(const vnl_vector_fixed< double, 3 >& v1, const vnl_vector_fixed< double, 3 >& v2) { return (v1.magnitude()>v2.magnitude()); } template< class PixelType, int ShOrder, int NrOdfDirections > FiniteDiffOdfMaximaExtractionFilter< PixelType, ShOrder, NrOdfDirections> ::FiniteDiffOdfMaximaExtractionFilter() : m_NormalizationMethod(MAX_VEC_NORM) , m_MaxNumPeaks(2) , m_PeakThreshold(0.4) , m_AbsolutePeakThreshold(0) , m_ClusteringThreshold(0.9) , m_AngularThreshold(0.7) , m_NumCoeffs((ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder) - , m_Toolkit(FSL) + , m_Toolkit(MRTRIX) , m_ApplyDirectionMatrix(false) { this->SetNumberOfRequiredInputs(1); } template< class PixelType, int ShOrder, int NrOdfDirections > void FiniteDiffOdfMaximaExtractionFilter< PixelType, ShOrder, NrOdfDirections> ::FindCandidatePeaks(OdfType& odf, double thr, std::vector< DirectionType >& container) { double gfa = odf.GetGeneralizedFractionalAnisotropy(); //Find the peaks using a finite difference method bool flag = true; vnl_vector_fixed< bool, NrOdfDirections > used; used.fill(false); //Find the peaks for (int i=0; ithr && val*gfa>m_AbsolutePeakThreshold) // limit to one hemisphere ??? { flag = true; std::vector< int > neighbours = odf.GetNeighbors(i); for (unsigned int j=0; j std::vector< vnl_vector_fixed< double, 3 > > FiniteDiffOdfMaximaExtractionFilter< PixelType, ShOrder, NrOdfDirections>::MeanShiftClustering(std::vector< DirectionType >& inDirs) { std::vector< DirectionType > outDirs; if (inDirs.empty()) return inDirs; DirectionType oldMean, currentMean, workingMean; std::vector< int > touched; // initialize touched.resize(inDirs.size(), 0); bool free = true; currentMean = inDirs[0]; // initialize first seed while (free) { oldMean.fill(0.0); // start mean-shift clustering float angle = 0.0; int counter = 0; while ((currentMean-oldMean).magnitude()>0.0001) { counter = 0; oldMean = currentMean; workingMean = oldMean; workingMean.normalize(); currentMean.fill(0.0); for (unsigned int i=0; i=m_ClusteringThreshold) { currentMean += inDirs[i]; touched[i] = 1; counter++; } else if (-angle>=m_ClusteringThreshold) { currentMean -= inDirs[i]; touched[i] = 1; counter++; } } } // found stable mean if (counter>0) { float mag = currentMean.magnitude(); if (mag>0) { currentMean /= mag; outDirs.push_back(currentMean); } } // find next unused seed free = false; for (unsigned int i=0; i void FiniteDiffOdfMaximaExtractionFilter< PixelType, ShOrder, NrOdfDirections> ::BeforeThreadedGenerateData() { typename CoefficientImageType::Pointer ShCoeffImage = static_cast< CoefficientImageType* >( this->ProcessObject::GetInput(0) ); itk::Vector spacing = ShCoeffImage->GetSpacing(); double minSpacing = spacing[0]; if (spacing[1]GetOrigin(); itk::Matrix direction = ShCoeffImage->GetDirection(); ImageRegion<3> imageRegion = ShCoeffImage->GetLargestPossibleRegion(); if (m_MaskImage.IsNotNull()) { origin = m_MaskImage->GetOrigin(); direction = m_MaskImage->GetDirection(); imageRegion = m_MaskImage->GetLargestPossibleRegion(); } itk::Vector spacing3 = ShCoeffImage->GetSpacing(); itk::Point origin3 = ShCoeffImage->GetOrigin(); itk::Matrix direction3 = ShCoeffImage->GetDirection(); itk::ImageRegion<3> imageRegion3 = ShCoeffImage->GetLargestPossibleRegion(); itk::Vector spacing4; itk::Point origin4; itk::Matrix direction4; itk::ImageRegion<4> imageRegion4; spacing4[0] = spacing3[0]; spacing4[1] = spacing3[1]; spacing4[2] = spacing3[2]; spacing4[3] = 1; origin4[0] = origin3[0]; origin4[1] = origin3[1]; origin4[2] = origin3[2]; origin4[3] = 0; for (int r=0; r<3; r++) for (int c=0; c<3; c++) direction4[r][c] = direction3[r][c]; direction4[3][3] = 1; imageRegion4.SetSize(0, imageRegion3.GetSize()[0]); imageRegion4.SetSize(1, imageRegion3.GetSize()[1]); imageRegion4.SetSize(2, imageRegion3.GetSize()[2]); imageRegion4.SetSize(3, m_MaxNumPeaks*3); m_PeakImage = PeakImageType::New(); m_PeakImage->SetSpacing( spacing4 ); m_PeakImage->SetOrigin( origin4 ); m_PeakImage->SetDirection( direction4 ); m_PeakImage->SetRegions( imageRegion4 ); m_PeakImage->Allocate(); m_PeakImage->FillBuffer(0.0); if (m_MaskImage.IsNull()) { m_MaskImage = ItkUcharImgType::New(); m_MaskImage->SetSpacing( spacing ); m_MaskImage->SetOrigin( origin ); m_MaskImage->SetDirection( direction ); m_MaskImage->SetRegions( imageRegion ); m_MaskImage->Allocate(); m_MaskImage->FillBuffer(1); } m_NumDirectionsImage = ItkUcharImgType::New(); m_NumDirectionsImage->SetSpacing( spacing ); m_NumDirectionsImage->SetOrigin( origin ); m_NumDirectionsImage->SetDirection( direction ); m_NumDirectionsImage->SetRegions( imageRegion ); m_NumDirectionsImage->Allocate(); m_NumDirectionsImage->FillBuffer(0); // calculate SH basis OdfType odf; vnl_matrix< double > sphCoords; std::vector< DirectionType > dirs; for (int i=0; i void FiniteDiffOdfMaximaExtractionFilter< PixelType, ShOrder, NrOdfDirections> ::AfterThreadedGenerateData() { } template< class PixelType, int ShOrder, int NrOdfDirections > void FiniteDiffOdfMaximaExtractionFilter< PixelType, ShOrder, NrOdfDirections> ::ThreadedGenerateData( const OutputImageRegionType& outputRegionForThread, ThreadIdType threadID ) { typename CoefficientImageType::Pointer ShCoeffImage = static_cast< CoefficientImageType* >( this->ProcessObject::GetInput(0) ); ImageRegionConstIterator< CoefficientImageType > cit(ShCoeffImage, outputRegionForThread ); OdfType odf; while( !cit.IsAtEnd() ) { typename CoefficientImageType::IndexType idx3 = cit.GetIndex(); if (m_MaskImage->GetPixel(idx3)==0) { ++cit; continue; } CoefficientPixelType c = cit.Get(); // calculate ODF double max = 0; odf.Fill(0.0); for (int i=0; imax) max = odf[i]; } if (max<0.0001) { ++cit; continue; } std::vector< DirectionType > candidates, peaks, temp; peaks.clear(); max *= m_PeakThreshold; // relative threshold FindCandidatePeaks(odf, max, candidates); // find all local maxima candidates = MeanShiftClustering(candidates); // cluster maxima - vnl_matrix< double > shBasis, sphCoords; - Cart2Sph(candidates, sphCoords); // convert candidate peaks to spherical angles - shBasis = CalcShBasis(sphCoords); // evaluate spherical harmonics at each peak + vnl_matrix sphCoords; + CreateDirMatrix(candidates, sphCoords); // convert candidate peaks to spherical angles + vnl_matrix< float > shBasis; + if (m_Toolkit==Toolkit::MRTRIX) + shBasis = mitk::sh::CalcShBasisForDirections(ShOrder, sphCoords); + else + shBasis = mitk::sh::CalcShBasisForDirections(ShOrder, sphCoords, false); + max = 0.0; for (unsigned int i=0; imax) max = val; peaks.push_back(candidates[i]*val); } std::sort( peaks.begin(), peaks.end(), CompareVectors ); // sort peaks // kick out directions to close to a larger direction (too far away to cluster but too close to keep) unsigned int m = peaks.size(); if ( m>m_MaxNumPeaks ) m = m_MaxNumPeaks; for (unsigned int i=0; im_AngularThreshold && val idx4; idx4[0] = idx3[0]; idx4[1] = idx3[1]; idx4[2] = idx3[2]; // fill output image unsigned int num = peaks.size(); if ( num>m_MaxNumPeaks ) num = m_MaxNumPeaks; for (unsigned int i=0; iGetDirection()*dir; if (m_FlipX) dir[0] = -dir[0]; if (m_FlipY) dir[1] = -dir[1]; if (m_FlipZ) dir[2] = -dir[2]; for (unsigned int j = 0; j<3; j++) { idx4[3] = i*3 + j; m_PeakImage->SetPixel(idx4, dir[j]); } } m_NumDirectionsImage->SetPixel(idx3, num); ++cit; } MITK_INFO << "Thread " << threadID << " finished extraction"; } // convert cartesian to spherical coordinates template< class PixelType, int ShOrder, int NrOdfDirections > void FiniteDiffOdfMaximaExtractionFilter< PixelType, ShOrder, NrOdfDirections> -::Cart2Sph(const std::vector< DirectionType >& dir, vnl_matrix& sphCoords) +::CreateDirMatrix(const std::vector< DirectionType >& dir, vnl_matrix& sphCoords) { - sphCoords.set_size(dir.size(), 2); - + sphCoords.set_size(3, dir.size()); for (unsigned int i=0; i -vnl_matrix FiniteDiffOdfMaximaExtractionFilter< PixelType, ShOrder, NrOdfDirections> -::CalcShBasis(vnl_matrix& sphCoords) -{ - int M = sphCoords.rows(); - int j, m; double mag, plm; - vnl_matrix shBasis; - shBasis.set_size(M, m_NumCoeffs); - - for (int p=0; p(l,abs(m),cos(sphCoords(p,0))); - mag = sqrt((double)(2*l+1)/(4.0*M_PI)*factorial(l-abs(m))/factorial(l+abs(m)))*plm; - - if (m<0) - shBasis(p,j) = sqrt(2.0)*mag*cos(fabs((double)m)*sphCoords(p,1)); - else if (m==0) - shBasis(p,j) = mag; - else - shBasis(p,j) = pow(-1.0, m)*sqrt(2.0)*mag*sin(m*sphCoords(p,1)); - break; - case MRTRIX: - - plm = legendre_p(l,abs(m),-cos(sphCoords(p,0))); - mag = sqrt((double)(2*l+1)/(4.0*M_PI)*factorial(l-abs(m))/factorial(l+abs(m)))*plm; - if (m>0) - shBasis(p,j) = mag*cos(m*sphCoords(p,1)); - else if (m==0) - shBasis(p,j) = mag; - else - shBasis(p,j) = mag*sin(-m*sphCoords(p,1)); - break; - } - - j++; - } + sphCoords(0, i) = dir[i](0); + sphCoords(1, i) = dir[i](1); + sphCoords(2, i) = dir[i](2); } - return shBasis; } } #endif // __itkFiniteDiffOdfMaximaExtractionFilter_cpp diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkFiniteDiffOdfMaximaExtractionFilter.h b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkFiniteDiffOdfMaximaExtractionFilter.h index a11ed68a6b..d624e3b916 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkFiniteDiffOdfMaximaExtractionFilter.h +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkFiniteDiffOdfMaximaExtractionFilter.h @@ -1,149 +1,146 @@ /*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: itkDiffusionTensor3DReconstructionImageFilter.h,v $ Language: C++ Date: $Date: 2006-03-27 17:01:06 $ Version: $Revision: 1.12 $ Copyright (c) Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef __itkFiniteDiffOdfMaximaExtractionFilter_h_ #define __itkFiniteDiffOdfMaximaExtractionFilter_h_ #include "itkImageToImageFilter.h" #include "vnl/vnl_vector_fixed.h" #include "vnl/vnl_matrix.h" #include "vnl/algo/vnl_svd.h" #include "itkVectorContainer.h" #include "itkVectorImage.h" #include #include namespace itk{ /** * \brief Extract ODF peaks by searching all local maxima on a densely sampled ODF und clustering these maxima to get the underlying fiber direction. * NrOdfDirections: number of sampling points on the ODF surface (about 20000 is a good value) */ template< class PixelType, int ShOrder, int NrOdfDirections > class FiniteDiffOdfMaximaExtractionFilter : public ImageToImageFilter< Image< Vector< PixelType, (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder >, 3 >, Image< unsigned char, 3 > > { public: enum Toolkit { ///< SH coefficient convention (depends on toolkit) FSL, MRTRIX }; enum NormalizationMethods { NO_NORM, ///< no length normalization of the output peaks SINGLE_VEC_NORM, ///< normalize the single peaks to length 1 MAX_VEC_NORM ///< normalize all peaks according to their length in comparison to the largest peak in the respective voxel (0-1) }; typedef FiniteDiffOdfMaximaExtractionFilter Self; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef ImageToImageFilter< Image< Vector< PixelType, (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder >, 3 >, Image< unsigned char, 3 > > Superclass; /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Runtime information support. */ itkTypeMacro(FiniteDiffOdfMaximaExtractionFilter, ImageToImageFilter) typedef typename Superclass::InputImageType CoefficientImageType; typedef typename CoefficientImageType::RegionType CoefficientImageRegionType; typedef typename CoefficientImageType::PixelType CoefficientPixelType; typedef typename Superclass::OutputImageType OutputImageType; typedef typename Superclass::OutputImageRegionType OutputImageRegionType; typedef typename Superclass::InputImageRegionType InputImageRegionType; typedef Image< float, 4 > PeakImageType; typedef OrientationDistributionFunction OdfType; typedef itk::Image ItkUcharImgType; typedef vnl_vector_fixed< double, 3 > DirectionType; // input itkSetMacro( MaxNumPeaks, unsigned int) ///< maximum number of peaks per voxel. if more peaks are detected, only the largest are kept. itkSetMacro( PeakThreshold, double) ///< threshold on the peak length relative to the largest peak inside the current voxel itkSetMacro( AbsolutePeakThreshold, double) ///< hard threshold on the peak length of all local maxima itkSetMacro( ClusteringThreshold, double) ///< directions closer together than the specified angular threshold will be clustered (in rad) itkSetMacro( AngularThreshold, double) ///< directions closer together than the specified threshold that remain after clustering are discarded (largest is kept) (in rad) itkSetMacro( MaskImage, ItkUcharImgType::Pointer) ///< only voxels inside the binary mask are processed itkSetMacro( NormalizationMethod, NormalizationMethods) ///< normalization method of ODF peaks itkSetMacro( FlipX, bool) ///< flip peaks in x direction itkSetMacro( FlipY, bool) ///< flip peaks in y direction itkSetMacro( FlipZ, bool) ///< flip peaks in z direction itkSetMacro( ApplyDirectionMatrix, bool) // output itkGetMacro( NumDirectionsImage, ItkUcharImgType::Pointer ) itkGetMacro( PeakImage, PeakImageType::Pointer ) itkSetMacro( Toolkit, Toolkit) ///< define SH coefficient convention (depends on toolkit) itkGetMacro( Toolkit, Toolkit) ///< SH coefficient convention (depends on toolkit) protected: FiniteDiffOdfMaximaExtractionFilter(); ~FiniteDiffOdfMaximaExtractionFilter(){} void BeforeThreadedGenerateData(); void ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, ThreadIdType threadID ); void AfterThreadedGenerateData(); /** Extract all local maxima from the densely sampled ODF surface. Thresholding possible. **/ void FindCandidatePeaks(OdfType& odf, double odfMax, std::vector< DirectionType >& inDirs); /** Cluster input directions within a certain angular threshold **/ std::vector< DirectionType > MeanShiftClustering(std::vector< DirectionType >& inDirs); - /** Convert cartesian to spherical coordinates **/ - void Cart2Sph(const std::vector< DirectionType >& dir, vnl_matrix& sphCoords); - - /** Calculate spherical harmonic basis of the defined order **/ - vnl_matrix CalcShBasis(vnl_matrix& sphCoords); + /** Convert direction vector to matrix **/ + void CreateDirMatrix(const std::vector< DirectionType >& dir, vnl_matrix& sphCoords); private: NormalizationMethods m_NormalizationMethod; ///< normalization method of ODF peaks unsigned int m_MaxNumPeaks; ///< maximum number of peaks per voxel. if more peaks are detected, only the largest are kept. double m_PeakThreshold; ///< threshold on the peak length relative to the largest peak inside the current voxel double m_AbsolutePeakThreshold;///< hard threshold on the peak length of all local maxima - vnl_matrix< double > m_ShBasis; ///< container for evaluated SH base functions + vnl_matrix< float > m_ShBasis; ///< container for evaluated SH base functions double m_ClusteringThreshold; ///< directions closer together than the specified angular threshold will be clustered (in rad) double m_AngularThreshold; ///< directions closer together than the specified threshold that remain after clustering are discarded (largest is kept) (in rad) const int m_NumCoeffs; ///< number of spherical harmonics coefficients PeakImageType::Pointer m_PeakImage; ItkUcharImgType::Pointer m_NumDirectionsImage; ///< number of peaks per voxel ItkUcharImgType::Pointer m_MaskImage; ///< only voxels inside the binary mask are processed Toolkit m_Toolkit; bool m_FlipX; bool m_FlipY; bool m_FlipZ; bool m_ApplyDirectionMatrix; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkFiniteDiffOdfMaximaExtractionFilter.cpp" #endif #endif //__itkFiniteDiffOdfMaximaExtractionFilter_h_ diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShCoefficientImageExporter.cpp b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShCoefficientImageExporter.cpp index 05f6dc8fc0..9711456a4c 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShCoefficientImageExporter.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShCoefficientImageExporter.cpp @@ -1,96 +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. ===================================================================*/ #ifndef __itkShCoefficientImageExporter_cpp #define __itkShCoefficientImageExporter_cpp #include #include #include #include "itkShCoefficientImageExporter.h" #include #include using namespace boost::math; namespace itk { template< class PixelType, int ShOrder > ShCoefficientImageExporter< PixelType, ShOrder >::ShCoefficientImageExporter() { } template< class PixelType, int ShOrder > void ShCoefficientImageExporter< PixelType, ShOrder > ::GenerateData() { if (m_InputImage.IsNull()) return; Vector spacing4; Point origin4; Matrix direction4; direction4.SetIdentity(); ImageRegion<4> imageRegion4; Vector spacing3 = m_InputImage->GetSpacing(); Point origin3 = m_InputImage->GetOrigin(); Matrix direction3 = m_InputImage->GetDirection(); ImageRegion<3> imageRegion3 = m_InputImage->GetLargestPossibleRegion(); spacing4[0] = spacing3[0]; spacing4[1] = spacing3[1]; spacing4[2] = spacing3[2]; spacing4[3] = 1; origin4[0] = origin3[0]; origin4[1] = origin3[1]; origin4[2] = origin3[2]; origin4[3] = 0; for (int r=0; r<3; r++) for (int c=0; c<3; c++) direction4[r][c] = direction3[r][c]; imageRegion4.SetSize(0, imageRegion3.GetSize()[0]); imageRegion4.SetSize(1, imageRegion3.GetSize()[1]); imageRegion4.SetSize(2, imageRegion3.GetSize()[2]); imageRegion4.SetSize(3, (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder ); m_OutputImage = CoefficientImageType::New(); m_OutputImage->SetSpacing( spacing4 ); m_OutputImage->SetOrigin( origin4 ); m_OutputImage->SetDirection( direction4 ); m_OutputImage->SetRegions( imageRegion4 ); m_OutputImage->Allocate(); m_OutputImage->FillBuffer(0.0); typedef ImageRegionConstIterator< InputImageType > InputIteratorType; InputIteratorType it(m_InputImage, m_InputImage->GetLargestPossibleRegion()); int numCoeffs = imageRegion4.GetSize(3); - while(!it.IsAtEnd()) - { - CoefficientImageType::IndexType index; - index[0] = it.GetIndex()[0]; - index[1] = it.GetIndex()[1]; - index[2] = it.GetIndex()[2]; - + int x = imageRegion3.GetSize(0); + int y = imageRegion3.GetSize(1); + int z = imageRegion3.GetSize(2); + +#ifdef WIN32 +#pragma omp parallel for +#else +#pragma omp parallel for collapse(3) +#endif + for (int a=0; aGetPixel(index3); for (int i=0; iSetPixel(index, it.Get()[i]); + index4[3] = i; + m_OutputImage->SetPixel(index4, pix[i]); } - - ++it; } } } #endif // __itkShCoefficientImageExporter_cpp diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShCoefficientImageImporter.cpp b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShCoefficientImageImporter.cpp index 6714de9597..4e1da02349 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShCoefficientImageImporter.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShCoefficientImageImporter.cpp @@ -1,192 +1,104 @@ /*=================================================================== 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 __itkShCoefficientImageImporter_cpp #define __itkShCoefficientImageImporter_cpp #include #include #include #include "itkShCoefficientImageImporter.h" #include -#include - -using namespace boost::math; +#include namespace itk { template< class PixelType, int ShOrder > ShCoefficientImageImporter< PixelType, ShOrder >::ShCoefficientImageImporter() - : m_Toolkit(FSL) { - m_ShBasis.set_size(ODF_SAMPLING_SIZE, (ShOrder+1)*(ShOrder+2)/2); -} -template< class PixelType, int ShOrder > -void ShCoefficientImageImporter< PixelType, ShOrder > -::GenerateData() -{ - CalcShBasis(); - if (m_InputImage.IsNull()) - return; - - Vector spacing4 = m_InputImage->GetSpacing(); - Point origin4 = m_InputImage->GetOrigin(); - Matrix direction4 = m_InputImage->GetDirection(); - ImageRegion<4> imageRegion4 = m_InputImage->GetLargestPossibleRegion(); - - Vector spacing3; - Point origin3; - Matrix direction3; - ImageRegion<3> imageRegion3; - - spacing3[0] = spacing4[0]; spacing3[1] = spacing4[1]; spacing3[2] = spacing4[2]; - origin3[0] = origin4[0]; origin3[1] = origin4[1]; origin3[2] = origin4[2]; - for (int r=0; r<3; r++) - for (int c=0; c<3; c++) - direction3[r][c] = direction4[r][c]; - imageRegion3.SetSize(0, imageRegion4.GetSize()[0]); - imageRegion3.SetSize(1, imageRegion4.GetSize()[1]); - imageRegion3.SetSize(2, imageRegion4.GetSize()[2]); - - m_OdfImage = OdfImageType::New(); - m_OdfImage->SetSpacing( spacing3 ); - m_OdfImage->SetOrigin( origin3 ); - m_OdfImage->SetDirection( direction3 ); - m_OdfImage->SetRegions( imageRegion3 ); - m_OdfImage->Allocate(); - Vector< PixelType, ODF_SAMPLING_SIZE > nullVec1; nullVec1.Fill(0.0); - m_OdfImage->FillBuffer(nullVec1); - - m_CoefficientImage = CoefficientImageType::New(); - m_CoefficientImage->SetSpacing( spacing3 ); - m_CoefficientImage->SetOrigin( origin3 ); - m_CoefficientImage->SetDirection( direction3 ); - m_CoefficientImage->SetRegions( imageRegion3 ); - m_CoefficientImage->Allocate(); - Vector< PixelType, (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder > nullVec2; nullVec2.Fill(0.0); - m_CoefficientImage->FillBuffer(nullVec2); - - int x = imageRegion4.GetSize(0); - int y = imageRegion4.GetSize(1); - int z = imageRegion4.GetSize(2); - int numCoeffs = imageRegion4.GetSize(3); - - for (int a=0; a coeffs((ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder,1); - - typename InputImageType::IndexType index; - index.SetElement(0,a); - index.SetElement(1,b); - index.SetElement(2,c); - typename CoefficientImageType::PixelType pix; - for (int d=0; dGetPixel(index); - coeffs[d][0] = pix[d]; - } - typename CoefficientImageType::IndexType index2; - index2.SetElement(0,a); - index2.SetElement(1,b); - index2.SetElement(2,c); - m_CoefficientImage->SetPixel(index2, pix); - - typename OdfImageType::PixelType pix2; - vnl_matrix odf = m_ShBasis*coeffs; - for (int d=0; dSetPixel(index2,pix2); - } } -// generate spherical harmonic values of the desired order for each input direction template< class PixelType, int ShOrder > void ShCoefficientImageImporter< PixelType, ShOrder > -::CalcShBasis() -{ - vnl_matrix_fixed sphCoords = GetSphericalOdfDirections(); - int j, m; double mag, plm; - - for (int p=0; p(l,abs(m),cos(sphCoords(0,p))); - mag = sqrt((double)(2*l+1)/(4.0*M_PI)*factorial(l-abs(m))/factorial(l+abs(m)))*plm; - if (m<0) - m_ShBasis(p,j) = sqrt(2.0)*mag*cos(-m*sphCoords(1,p)); - else if (m==0) - m_ShBasis(p,j) = mag; - else - m_ShBasis(p,j) = pow(-1.0, m)*sqrt(2.0)*mag*sin(m*sphCoords(1,p)); - break; - case MRTRIX: - plm = legendre_p(l,abs(m),-cos(sphCoords(0,p))); - mag = sqrt((double)(2*l+1)/(4.0*M_PI)*factorial(l-abs(m))/factorial(l+abs(m)))*plm; - if (m>0) - m_ShBasis(p,j) = mag*cos(m*sphCoords(1,p)); - else if (m==0) - m_ShBasis(p,j) = mag; - else - m_ShBasis(p,j) = mag*sin(-m*sphCoords(1,p)); - break; - } - - j++; - } - } -} - -// convert cartesian to spherical coordinates -template< class PixelType, int ShOrder > -vnl_matrix_fixed ShCoefficientImageImporter< PixelType, ShOrder > -::GetSphericalOdfDirections() +::GenerateData() { - itk::OrientationDistributionFunction< PixelType, ODF_SAMPLING_SIZE > odf; - vnl_matrix_fixed* dir = odf.GetDirections(); - vnl_matrix_fixed sphCoords; - - for (int i=0; iget_column(i).magnitude(); - - if( mag spacing4 = m_InputImage->GetSpacing(); + Point origin4 = m_InputImage->GetOrigin(); + Matrix direction4 = m_InputImage->GetDirection(); + ImageRegion<4> imageRegion4 = m_InputImage->GetLargestPossibleRegion(); + + Vector spacing3; + Point origin3; + Matrix direction3; + ImageRegion<3> imageRegion3; + + spacing3[0] = spacing4[0]; spacing3[1] = spacing4[1]; spacing3[2] = spacing4[2]; + origin3[0] = origin4[0]; origin3[1] = origin4[1]; origin3[2] = origin4[2]; + for (int r=0; r<3; r++) + for (int c=0; c<3; c++) + direction3[r][c] = direction4[r][c]; + imageRegion3.SetSize(0, imageRegion4.GetSize()[0]); + imageRegion3.SetSize(1, imageRegion4.GetSize()[1]); + imageRegion3.SetSize(2, imageRegion4.GetSize()[2]); + + m_CoefficientImage = CoefficientImageType::New(); + m_CoefficientImage->SetSpacing( spacing3 ); + m_CoefficientImage->SetOrigin( origin3 ); + m_CoefficientImage->SetDirection( direction3 ); + m_CoefficientImage->SetRegions( imageRegion3 ); + m_CoefficientImage->Allocate(); + Vector< PixelType, (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder > nullVec2; nullVec2.Fill(0.0); + m_CoefficientImage->FillBuffer(nullVec2); + + int x = imageRegion4.GetSize(0); + int y = imageRegion4.GetSize(1); + int z = imageRegion4.GetSize(2); + int numCoeffs = imageRegion4.GetSize(3); + +#ifdef WIN32 +#pragma omp parallel for +#else +#pragma omp parallel for collapse(3) +#endif + for (int a=0; aget(2,i)/mag); // theta - sphCoords(1,i) = atan2(dir->get(1,i), dir->get(0,i)); // phi + index.SetElement(3,d); + pix[d] = m_InputImage->GetPixel(index); } - } - return sphCoords; + typename CoefficientImageType::IndexType index2; + index2.SetElement(0,a); + index2.SetElement(1,b); + index2.SetElement(2,c); + m_CoefficientImage->SetPixel(index2, pix); + } } } #endif // __itkShCoefficientImageImporter_cpp diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShCoefficientImageImporter.h b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShCoefficientImageImporter.h index ad6022ac16..dd02af0f3f 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShCoefficientImageImporter.h +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShCoefficientImageImporter.h @@ -1,95 +1,74 @@ /*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: itkDiffusionTensor3DReconstructionImageFilter.h,v $ Language: C++ Date: $Date: 2006-03-27 17:01:06 $ Version: $Revision: 1.12 $ Copyright (c) Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef __itkShCoefficientImageImporter_h_ #define __itkShCoefficientImageImporter_h_ #include namespace itk{ /** \class ShCoefficientImageImporter - Converts FSL reconstructions of diffusionweighted images (4D images containing the sh coefficients) to Odf images or 3D sh-coefficient images. + Converts converts 4D SH coefficient images (MRtrix) to 3D vector images containing the SH coefficients */ template< class PixelType, int ShOrder > class ShCoefficientImageImporter : public ProcessObject { public: - enum NormalizationMethods { - NO_NORM, - SINGLE_VEC_NORM, - SPACING_COMPENSATION - }; - - enum Toolkit { ///< SH coefficient convention (depends on toolkit) - FSL, - MRTRIX - }; - typedef ShCoefficientImageImporter Self; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef ProcessObject Superclass; typedef itk::Image< float, 4 > InputImageType; typedef Image< Vector< PixelType, (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder >, 3 > CoefficientImageType; typedef Image< Vector< PixelType, ODF_SAMPLING_SIZE >, 3 > OdfImageType; /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Runtime information support. */ itkTypeMacro(ShCoefficientImageImporter, ProcessObject) // input itkSetMacro( InputImage, InputImageType::Pointer) ///< sh coefficient image in FSL file format // output itkGetMacro( CoefficientImage, typename CoefficientImageType::Pointer) ///< mitk style image containing the SH coefficients - itkGetMacro( OdfImage, typename OdfImageType::Pointer) ///< mitk ODF image generated from the coefficients - - itkSetMacro( Toolkit, Toolkit) ///< define SH coefficient convention (depends on toolkit) - itkGetMacro( Toolkit, Toolkit) ///< SH coefficient convention (depends on toolkit) void GenerateData() override; protected: ShCoefficientImageImporter(); ~ShCoefficientImageImporter(){} - void CalcShBasis(); - vnl_matrix_fixed GetSphericalOdfDirections(); - InputImageType::Pointer m_InputImage; typename CoefficientImageType::Pointer m_CoefficientImage; ///< mitk style image containing the SH coefficients - typename OdfImageType::Pointer m_OdfImage; ///< mitk ODF image generated from the coefficients - vnl_matrix m_ShBasis; - Toolkit m_Toolkit; private: }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkShCoefficientImageImporter.cpp" #endif #endif //__itkShCoefficientImageImporter_h_ diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShToOdfImageFilter.cpp b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShToOdfImageFilter.cpp new file mode 100644 index 0000000000..8981cdf042 --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShToOdfImageFilter.cpp @@ -0,0 +1,76 @@ +/*=================================================================== + +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 __itkShToOdfImageFilter_cpp +#define __itkShToOdfImageFilter_cpp + +#include +#include +#include + +#include "itkShToOdfImageFilter.h" +#include +#include + +namespace itk { + +template< class PixelType, int ShOrder > +ShToOdfImageFilter< PixelType, ShOrder >::ShToOdfImageFilter() + : m_Toolkit(Toolkit::MRTRIX) +{ + +} + +template< class PixelType, int ShOrder > +void ShToOdfImageFilter< PixelType, ShOrder >::ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, ThreadIdType) +{ + CalcShBasis(); + + typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetPrimaryOutput()); + + typename InputImageType::Pointer inputImage = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); + + typedef ImageRegionConstIterator< InputImageType > InputIteratorType; + InputIteratorType it(inputImage, outputRegionForThread); + + typedef ImageRegionIterator< OutputImageType > OutputIteratorType; + OutputIteratorType oit(outputImage, outputRegionForThread); + + while(!it.IsAtEnd()) + { + auto pix = it.Get(); + vnl_vector coeffs = pix.GetVnlVector(); + OutputPixelType odf = (m_ShBasis * coeffs).data_block(); + oit.Set(odf); + + ++it; + ++oit; + } +} + +// generate spherical harmonic values of the desired order for each input direction +template< class PixelType, int ShOrder > +void ShToOdfImageFilter< PixelType, ShOrder >::CalcShBasis() +{ + vnl_matrix_fixed* U = itk::PointShell >::DistributePointShell(); + if (m_Toolkit==Toolkit::MRTRIX) + m_ShBasis = mitk::sh::CalcShBasisForDirections(ShOrder, U->as_matrix()); + else + m_ShBasis = mitk::sh::CalcShBasisForDirections(ShOrder, U->as_matrix(), false); +} + +} + +#endif // __itkShToOdfImageFilter_cpp diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShToOdfImageFilter.h b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShToOdfImageFilter.h new file mode 100644 index 0000000000..e5b9d72494 --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShToOdfImageFilter.h @@ -0,0 +1,86 @@ +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: itkDiffusionTensor3DReconstructionImageFilter.h,v $ + Language: C++ + Date: $Date: 2006-03-27 17:01:06 $ + Version: $Revision: 1.12 $ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef __itkShToOdfImageFilter_h_ +#define __itkShToOdfImageFilter_h_ + +#include +#include + +namespace itk{ +/** \class ShToOdfImageFilter + +*/ + +template< class PixelType, int ShOrder > +class ShToOdfImageFilter : public + ImageToImageFilter< itk::Image< itk::Vector< PixelType, (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder >, 3 >, itk::Image< itk::Vector,3> > +{ + +public: + + enum Toolkit { ///< SH coefficient convention (depends on toolkit) + FSL, + MRTRIX + }; + + typedef itk::Vector< PixelType, (ShOrder*ShOrder + ShOrder + 2)/2 + ShOrder > InputPixelType; + typedef itk::Image InputImageType; + typedef typename InputImageType::RegionType InputImageRegionType; + + typedef itk::Vector OutputPixelType; + typedef itk::Image OutputImageType; + typedef typename OutputImageType::RegionType OutputImageRegionType; + + typedef ShToOdfImageFilter Self; + typedef itk::ImageToImageFilter Superclass; + + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + /** Runtime information support. */ + itkTypeMacro(ShToOdfImageFilter, ImageToImageFilter) + + itkSetMacro( Toolkit, Toolkit) ///< define SH coefficient convention (depends on toolkit) + itkGetMacro( Toolkit, Toolkit) ///< SH coefficient convention (depends on toolkit) + + void ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, ThreadIdType); + +protected: + ShToOdfImageFilter(); + ~ShToOdfImageFilter(){} + + void CalcShBasis(); + + vnl_matrix m_ShBasis; + Toolkit m_Toolkit; + +private: + +}; + +} + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkShToOdfImageFilter.cpp" +#endif + +#endif //__itkShToOdfImageFilter_h_ + diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShToRgbImageFilter.h b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShToRgbImageFilter.h new file mode 100644 index 0000000000..2931890c35 --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/include/Algorithms/itkShToRgbImageFilter.h @@ -0,0 +1,154 @@ +/*=================================================================== + +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 __itkShToRgbImageFilter_h +#define __itkShToRgbImageFilter_h + +#include "itkUnaryFunctorImageFilter.h" +#include "itkOrientationDistributionFunction.h" +#include "itkRGBAPixel.h" +#include "itkImageRegionConstIterator.h" +#include "itkImageRegionIterator.h" +#include + +namespace itk +{ + + #define __IMG_DAT_ITEM__CEIL_ZERO_ONE__(val) (val) = \ + ( (val) < 0 ) ? ( 0 ) : ( ( (val)>1 ) ? ( 1 ) : ( (val) ) ); + + +/** \class ShToRgbImageFilter +* +*/ +template ,3> > + class ShToRgbImageFilter : + public ImageToImageFilter + { + public: + /** Standard class typedefs. */ + typedef ShToRgbImageFilter Self; + typedef ImageToImageFilter + Superclass; + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + + typedef typename Superclass::InputImageType InputImageType; + typedef typename Superclass::OutputImageType OutputImageType; + typedef typename OutputImageType::PixelType OutputPixelType; + typedef typename TInputImage::PixelType InputPixelType; + typedef typename InputPixelType::ValueType InputValueType; + + /** Run-time type information (and related methods). */ + itkTypeMacro( ShToRgbImageFilter, ImageToImageFilter ) + + /** Method for creation through the object factory. */ + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + /** Print internal ivars */ + void PrintSelf(std::ostream& os, Indent indent) const + { this->Superclass::PrintSelf( os, indent ); } + +#ifdef ITK_USE_CONCEPT_CHECKING + /** Begin concept checking */ + itkConceptMacro(InputHasNumericTraitsCheck, + (Concept::HasNumericTraits)); + /** End concept checking */ +#endif + +protected: + ShToRgbImageFilter(){} + virtual ~ShToRgbImageFilter(){} + + void ThreadedGenerateData( const typename OutputImageType::RegionType &outputRegionForThread, ThreadIdType) + { + typename InputImageType::Pointer coeff_image = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); + typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetPrimaryOutput()); + + outputImage->SetSpacing( coeff_image->GetSpacing() ); // Set the image spacing + outputImage->SetOrigin( coeff_image->GetOrigin() ); // Set the image origin + outputImage->SetDirection( coeff_image->GetDirection() ); // Set the image direction + outputImage->SetRegions( coeff_image->GetLargestPossibleRegion()); + outputImage->Allocate(); + + typedef ImageRegionConstIterator< InputImageType > OdfImageIteratorType; + OdfImageIteratorType it(coeff_image, outputRegionForThread); + + typedef ImageRegionIterator< OutputImageType > OutputImageIteratorType; + OutputImageIteratorType oit(outputImage, outputRegionForThread); + + it.GoToBegin(); + oit.GoToBegin(); + + typedef itk::OrientationDistributionFunction OdfType; + vnl_matrix sh2Basis = mitk::sh::CalcShBasisForDirections(ShOrder, itk::PointShell >::DistributePointShell()->as_matrix()); + + while(!it.IsAtEnd() && !oit.IsAtEnd()) + { + InputPixelType x = it.Get(); + + Vector< float, ODF_SAMPLING_SIZE > odf_vals; + vnl_vector< float > coeffs(x.GetNumberOfComponents()); + for(unsigned int i=0; i dir; + int pd = odf.GetPrincipalDiffusionDirectionIndex(); + if (pd==-1) + dir.fill(0); + else + dir = OdfType::GetDirection(pd); + + const float fa = odf.GetGeneralizedFractionalAnisotropy(); + float r = fabs(dir[0]) * fa; + float g = fabs(dir[1]) * fa; + float b = fabs(dir[2]) * fa; + float a = fa; + + __IMG_DAT_ITEM__CEIL_ZERO_ONE__(r); + __IMG_DAT_ITEM__CEIL_ZERO_ONE__(g); + __IMG_DAT_ITEM__CEIL_ZERO_ONE__(b); + __IMG_DAT_ITEM__CEIL_ZERO_ONE__(a); + + OutputPixelType out; + out.SetRed( r * 255.0f); + out.SetGreen( g * 255.0f); + out.SetBlue( b * 255.0f); + out.SetAlpha( a * 255.0f); + + oit.Set(out); + + ++it; + ++oit; + } + + } + +private: + ShToRgbImageFilter(const Self&); //purposely not implemented + void operator=(const Self&); //purposely not implemented +}; + +} // end namespace itk + +#endif diff --git a/Modules/DiffusionImaging/DiffusionCore/include/IODataStructures/mitkShImage.h b/Modules/DiffusionImaging/DiffusionCore/include/IODataStructures/mitkShImage.h new file mode 100644 index 0000000000..2ddcc0bdf2 --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/include/IODataStructures/mitkShImage.h @@ -0,0 +1,71 @@ +/*=================================================================== + +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 __mitkShImage__h +#define __mitkShImage__h + +#include "mitkImage.h" +#include "itkVectorImage.h" +#include "mitkImageVtkAccessor.h" + +#include + +namespace mitk +{ + + /** + * \brief this class encapsulates spherical harmonics coefficients + */ + class MITKDIFFUSIONCORE_EXPORT ShImage : public Image + { + + public: + + typedef itk::Image ShOnDiskType; // we store the sh images in MRtrix 4D float format and convert the on load to 3D multi-component images + + mitkClassMacro( ShImage, Image ) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + virtual const vtkImageData* GetNonRgbVtkImageData(int t = 0, int n = 0) const; + virtual vtkImageData* GetNonRgbVtkImageData(int t = 0, int n = 0); + + virtual const vtkImageData* GetVtkImageData(int t = 0, int n = 0) const override; + virtual vtkImageData* GetVtkImageData(int t = 0, int n = 0) override; + + virtual void ConstructRgbImage() const; + + int ShOrder(); + int NumCoefficients(); + + protected: + ShImage(); + virtual ~ShImage(); + + template + void Construct() const; + + virtual void PrintSelf(std::ostream &os, itk::Indent indent) const override; + + mutable mitk::Image::Pointer m_RgbImage; + int m_ShOrder; + int m_NumCoefficients; + }; + +} // namespace mitk + +#endif /* __mitkShImage__h */ diff --git a/Modules/DiffusionImaging/DiffusionCore/include/IODataStructures/mitkShImageSource.h b/Modules/DiffusionImaging/DiffusionCore/include/IODataStructures/mitkShImageSource.h new file mode 100644 index 0000000000..a9d36e1a9b --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/include/IODataStructures/mitkShImageSource.h @@ -0,0 +1,64 @@ +/*=================================================================== + +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 ShImageSource_H_HEADER_INCLUDED_C1E7D6EC +#define ShImageSource_H_HEADER_INCLUDED_C1E7D6EC + +#include "mitkImageSource.h" +#include "mitkShImage.h" +#include + +namespace mitk { + +class MITKDIFFUSIONCORE_EXPORT ShImageSource : public ImageSource +{ +public: + + typedef mitk::ShImage OutputImageType; + typedef OutputImageType::Pointer OutputImagePointer; + typedef SlicedData::RegionType OutputImageRegionType; + typedef itk::DataObject::Pointer DataObjectPointer; + + mitkClassMacro(ShImageSource,ImageSource); + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + /** + * Allocates a new output object and returns it. Currently the + * index idx is not evaluated. + * @param idx the index of the output for which an object should be created + * @returns the new object + */ + virtual itk::DataObject::Pointer MakeOutput ( DataObjectPointerArraySizeType idx ) override; + + /** + * This is a default implementation to make sure we have something. + * Once all the subclasses of ProcessObject provide an appopriate + * MakeOutput(), then ProcessObject::MakeOutput() can be made pure + * virtual. + */ + virtual itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override; + +protected: + ShImageSource(); + virtual ~ShImageSource() {} + +}; + +} // namespace mitk + +#endif /* ShImageSource_H_HEADER_INCLUDED_C1E7D6EC */ diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Rendering/mitkOdfVtkMapper2D.h b/Modules/DiffusionImaging/DiffusionCore/include/Rendering/mitkOdfVtkMapper2D.h index a31294afb0..7c55480a14 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Rendering/mitkOdfVtkMapper2D.h +++ b/Modules/DiffusionImaging/DiffusionCore/include/Rendering/mitkOdfVtkMapper2D.h @@ -1,152 +1,161 @@ /*=================================================================== 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 ODFVTKMAPPER2D_H_HEADER_INCLUDED #define ODFVTKMAPPER2D_H_HEADER_INCLUDED #include "mitkVtkMapper.h" #include "vtkPropAssembly.h" #include "vtkAppendPolyData.h" #include "vtkActor.h" #include "vtkPolyDataMapper.h" #include "vtkPlane.h" #include "vtkCutter.h" #include "vtkClipPolyData.h" #include "vtkTransform.h" #include "vtkDataArrayTemplate.h" #include "vtkSmartPointer.h" #include "vtkOdfSource.h" #include "vtkThickPlane.h" +#include namespace mitk { //##Documentation //## @brief Mapper for spherical object densitiy function representations //## template class OdfVtkMapper2D : public VtkMapper { struct OdfDisplayGeometry { double vp[ 3 ], vnormal[ 3 ]; Vector3D normal; double d, d1, d2; mitk::Point3D M3D, L3D, O3D; double vp_original[ 3 ], vnormal_original[ 3 ]; mitk::Vector2D size, origin; bool Equals(OdfDisplayGeometry other) { return other.vp_original[0] == vp[0] && other.vp_original[1] == vp[1] && other.vp_original[2] == vp[2] && other.vnormal_original[0] == vnormal[0] && other.vnormal_original[1] == vnormal[1] && other.vnormal_original[2] == vnormal[2] && other.size[0] == size[0] && other.size[1] == size[1] && other.origin[0] == origin[0] && other.origin[1] == origin[1]; } }; public: mitkClassMacro(OdfVtkMapper2D,VtkMapper) itkFactorylessNewMacro(Self) itkCloneMacro(Self) virtual vtkProp* GetVtkProp(mitk::BaseRenderer* renderer) override; bool IsVisibleOdfs(mitk::BaseRenderer* renderer); virtual void MitkRenderOverlay(mitk::BaseRenderer* renderer) override; virtual void MitkRenderOpaqueGeometry(mitk::BaseRenderer* renderer) override; virtual void MitkRenderTranslucentGeometry(mitk::BaseRenderer* renderer) override; virtual void MitkRenderVolumetricGeometry(mitk::BaseRenderer* /*renderer*/) override{} OdfDisplayGeometry MeasureDisplayedGeometry(mitk::BaseRenderer* renderer); double GetMinImageSpacing( int index ); void ApplyPropertySettings(); virtual void Slice(mitk::BaseRenderer* renderer, OdfDisplayGeometry dispGeo); virtual int GetIndex(mitk::BaseRenderer* renderer); static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = nullptr, bool overwrite = false); virtual void Update(mitk::BaseRenderer * renderer) override; virtual void GenerateDataForRenderer(mitk::BaseRenderer* renderer) override; virtual bool IsLODEnabled( BaseRenderer * /*renderer*/ ) const override { return true; } class LocalStorage : public mitk::Mapper::BaseLocalStorage { public: std::vector< vtkSmartPointer > m_PropAssemblies; std::vector< vtkSmartPointer > m_OdfsPlanes; std::vector< vtkSmartPointer > m_OdfsActors; std::vector< vtkSmartPointer > m_OdfsMappers; vtkSmartPointer< vtkPolyData > m_TemplateOdf; itk::TimeStamp m_LastUpdateTime; /** \brief Default constructor of the local storage. */ LocalStorage(); /** \brief Default deconstructor of the local storage. */ ~LocalStorage() { } }; protected: OdfVtkMapper2D(); virtual ~OdfVtkMapper2D(); static void GlyphMethod(void *arg); bool IsPlaneRotated(mitk::BaseRenderer* renderer); static bool m_ToggleTensorEllipsoidView; static bool m_ToggleColourisationMode; static bool m_ToggleGlyphPlacementMode; + typedef vnl_matrix_fixed DirectionsType; private: mitk::Image* GetInput(); static vtkSmartPointer m_OdfTransform; static vtkSmartPointer m_OdfSource; static float m_Scaling; static int m_Normalization; static int m_ScaleBy; static float m_IndexParam1; static float m_IndexParam2; static vtkSmartPointer m_ColourScalars; int m_ShowMaxNumber; std::vector< vtkSmartPointer > m_Planes; std::vector< vtkSmartPointer > m_Cutters; std::vector< vtkSmartPointer > m_ThickPlanes1; std::vector< vtkSmartPointer > m_Clippers1; std::vector< vtkSmartPointer > m_ThickPlanes2; std::vector< vtkSmartPointer > m_Clippers2; vtkImageData* m_VtkImage ; std::vector< OdfDisplayGeometry > m_LastDisplayGeometry; mitk::LocalStorageHandler m_LSH; + + static vnl_matrix m_Sh2Basis; + static vnl_matrix m_Sh4Basis; + static vnl_matrix m_Sh6Basis; + static vnl_matrix m_Sh8Basis; + static vnl_matrix m_Sh10Basis; + static vnl_matrix m_Sh12Basis; }; } // namespace mitk #include "mitkOdfVtkMapper2D.txx" #endif /* ODFVTKMAPPER2D_H_HEADER_INCLUDED */ diff --git a/Modules/DiffusionImaging/DiffusionCore/include/Rendering/mitkOdfVtkMapper2D.txx b/Modules/DiffusionImaging/DiffusionCore/include/Rendering/mitkOdfVtkMapper2D.txx index 745ec9ef03..af8ee667e3 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/Rendering/mitkOdfVtkMapper2D.txx +++ b/Modules/DiffusionImaging/DiffusionCore/include/Rendering/mitkOdfVtkMapper2D.txx @@ -1,938 +1,984 @@ /*=================================================================== 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 __mitkOdfVtkMapper2D_txx__ #define __mitkOdfVtkMapper2D_txx__ #include "mitkOdfVtkMapper2D.h" #include "mitkDataNode.h" #include "mitkBaseRenderer.h" #include "mitkMatrixConvert.h" #include "mitkGeometry3D.h" #include "mitkTimeGeometry.h" #include "mitkOdfNormalizationMethodProperty.h" #include "mitkOdfScaleByProperty.h" #include "mitkProperties.h" #include "mitkTensorImage.h" +#include "mitkShImage.h" #include "vtkSphereSource.h" #include "vtkPropCollection.h" #include "vtkMaskedGlyph3D.h" #include "vtkGlyph2D.h" #include "vtkGlyph3D.h" #include "vtkMaskedProgrammableGlyphFilter.h" #include "vtkImageData.h" #include "vtkLinearTransform.h" #include "vtkCamera.h" #include "vtkPointData.h" #include "vtkTransformPolyDataFilter.h" #include "vtkTransform.h" #include "vtkOdfSource.h" #include "vtkDoubleArray.h" #include "vtkLookupTable.h" #include "vtkProperty.h" #include "vtkPolyDataNormals.h" #include "vtkLight.h" #include "vtkLightCollection.h" #include "vtkMath.h" #include "vtkFloatArray.h" #include "vtkDelaunay2D.h" #include "vtkMapper.h" #include #include #include "vtkRenderer.h" #include "itkOrientationDistributionFunction.h" #include "itkFixedArray.h" #include #include "vtkOpenGLRenderer.h" #define _USE_MATH_DEFINES #include #include template vtkSmartPointer mitk::OdfVtkMapper2D::m_OdfTransform = vtkSmartPointer::New(); template vtkSmartPointer mitk::OdfVtkMapper2D::m_OdfSource = vtkSmartPointer::New(); template float mitk::OdfVtkMapper2D::m_Scaling; template int mitk::OdfVtkMapper2D::m_Normalization; template int mitk::OdfVtkMapper2D::m_ScaleBy; template float mitk::OdfVtkMapper2D::m_IndexParam1; template float mitk::OdfVtkMapper2D::m_IndexParam2; template bool mitk::OdfVtkMapper2D::m_ToggleTensorEllipsoidView = false; template bool mitk::OdfVtkMapper2D::m_ToggleColourisationMode = false; template bool mitk::OdfVtkMapper2D::m_ToggleGlyphPlacementMode = true; template vtkSmartPointer mitk::OdfVtkMapper2D::m_ColourScalars = nullptr; -#define ODF_MAPPER_PI M_PI + +template +vnl_matrix mitk::OdfVtkMapper2D::m_Sh2Basis = mitk::sh::CalcShBasisForDirections(2, itk::PointShell >::DistributePointShell()->as_matrix()); +template +vnl_matrix mitk::OdfVtkMapper2D::m_Sh4Basis = mitk::sh::CalcShBasisForDirections(4, itk::PointShell >::DistributePointShell()->as_matrix()); +template +vnl_matrix mitk::OdfVtkMapper2D::m_Sh6Basis = mitk::sh::CalcShBasisForDirections(6, itk::PointShell >::DistributePointShell()->as_matrix()); +template +vnl_matrix mitk::OdfVtkMapper2D::m_Sh8Basis = mitk::sh::CalcShBasisForDirections(8, itk::PointShell >::DistributePointShell()->as_matrix()); +template +vnl_matrix mitk::OdfVtkMapper2D::m_Sh10Basis = mitk::sh::CalcShBasisForDirections(10, itk::PointShell >::DistributePointShell()->as_matrix()); +template +vnl_matrix mitk::OdfVtkMapper2D::m_Sh12Basis = mitk::sh::CalcShBasisForDirections(12, itk::PointShell >::DistributePointShell()->as_matrix()); template mitk::OdfVtkMapper2D::LocalStorage::LocalStorage() { m_PropAssemblies.push_back(vtkPropAssembly::New()); m_PropAssemblies.push_back(vtkPropAssembly::New()); m_PropAssemblies.push_back(vtkPropAssembly::New()); m_OdfsPlanes.push_back(vtkAppendPolyData::New()); m_OdfsPlanes.push_back(vtkAppendPolyData::New()); m_OdfsPlanes.push_back(vtkAppendPolyData::New()); m_OdfsPlanes[0]->AddInputData(vtkPolyData::New()); m_OdfsPlanes[1]->AddInputData(vtkPolyData::New()); m_OdfsPlanes[2]->AddInputData(vtkPolyData::New()); m_OdfsActors.push_back(vtkActor::New()); m_OdfsActors.push_back(vtkActor::New()); m_OdfsActors.push_back(vtkActor::New()); m_OdfsActors[0]->GetProperty()->SetInterpolationToGouraud(); m_OdfsActors[1]->GetProperty()->SetInterpolationToGouraud(); m_OdfsActors[2]->GetProperty()->SetInterpolationToGouraud(); m_OdfsMappers.push_back(vtkPolyDataMapper::New()); m_OdfsMappers.push_back(vtkPolyDataMapper::New()); m_OdfsMappers.push_back(vtkPolyDataMapper::New()); vtkLookupTable *lut = vtkLookupTable::New(); m_OdfsMappers[0]->SetLookupTable(lut); m_OdfsMappers[1]->SetLookupTable(lut); m_OdfsMappers[2]->SetLookupTable(lut); m_OdfsActors[0]->SetMapper(m_OdfsMappers[0]); m_OdfsActors[1]->SetMapper(m_OdfsMappers[1]); m_OdfsActors[2]->SetMapper(m_OdfsMappers[2]); } template mitk::OdfVtkMapper2D ::OdfVtkMapper2D() { m_LastDisplayGeometry.push_back(OdfDisplayGeometry()); m_LastDisplayGeometry.push_back(OdfDisplayGeometry()); m_LastDisplayGeometry.push_back(OdfDisplayGeometry()); m_Planes.push_back(vtkPlane::New()); m_Planes.push_back(vtkPlane::New()); m_Planes.push_back(vtkPlane::New()); m_Cutters.push_back(vtkCutter::New()); m_Cutters.push_back(vtkCutter::New()); m_Cutters.push_back(vtkCutter::New()); m_Cutters[0]->SetCutFunction( m_Planes[0] ); m_Cutters[0]->GenerateValues( 1, 0, 1 ); m_Cutters[1]->SetCutFunction( m_Planes[1] ); m_Cutters[1]->GenerateValues( 1, 0, 1 ); m_Cutters[2]->SetCutFunction( m_Planes[2] ); m_Cutters[2]->GenerateValues( 1, 0, 1 ); // Windowing the cutted planes in direction 1 m_ThickPlanes1.push_back(vtkThickPlane::New()); m_ThickPlanes1.push_back(vtkThickPlane::New()); m_ThickPlanes1.push_back(vtkThickPlane::New()); m_Clippers1.push_back(vtkClipPolyData::New()); m_Clippers1.push_back(vtkClipPolyData::New()); m_Clippers1.push_back(vtkClipPolyData::New()); m_Clippers1[0]->SetClipFunction( m_ThickPlanes1[0] ); m_Clippers1[1]->SetClipFunction( m_ThickPlanes1[1] ); m_Clippers1[2]->SetClipFunction( m_ThickPlanes1[2] ); // Windowing the cutted planes in direction 2 m_ThickPlanes2.push_back(vtkThickPlane::New()); m_ThickPlanes2.push_back(vtkThickPlane::New()); m_ThickPlanes2.push_back(vtkThickPlane::New()); m_Clippers2.push_back(vtkClipPolyData::New()); m_Clippers2.push_back(vtkClipPolyData::New()); m_Clippers2.push_back(vtkClipPolyData::New()); m_Clippers2[0]->SetClipFunction( m_ThickPlanes2[0] ); m_Clippers2[1]->SetClipFunction( m_ThickPlanes2[1] ); m_Clippers2[2]->SetClipFunction( m_ThickPlanes2[2] ); m_ShowMaxNumber = 500; } template mitk::OdfVtkMapper2D ::~OdfVtkMapper2D() { } template mitk::Image* mitk::OdfVtkMapper2D ::GetInput() { return static_cast ( m_DataNode->GetData() ); } template vtkProp* mitk::OdfVtkMapper2D ::GetVtkProp(mitk::BaseRenderer* renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); return localStorage->m_PropAssemblies[GetIndex(renderer)]; } template int mitk::OdfVtkMapper2D ::GetIndex(mitk::BaseRenderer* renderer) { if(!strcmp(renderer->GetName(),"stdmulti.widget1")) return 0; if(!strcmp(renderer->GetName(),"stdmulti.widget2")) return 1; if(!strcmp(renderer->GetName(),"stdmulti.widget3")) return 2; return 0; } template void mitk::OdfVtkMapper2D ::GlyphMethod(void *arg) { vtkMaskedProgrammableGlyphFilter* pfilter=(vtkMaskedProgrammableGlyphFilter*)arg; double point[3]; double debugpoint[3]; pfilter->GetPoint(point); pfilter->GetPoint(debugpoint); itk::Point p(point); Vector3D spacing = pfilter->GetGeometry()->GetSpacing(); p[0] /= spacing[0]; p[1] /= spacing[1]; p[2] /= spacing[2]; mitk::Point3D p2; pfilter->GetGeometry()->IndexToWorld( p, p2 ); point[0] = p2[0]; point[1] = p2[1]; point[2] = p2[2]; vtkPointData* data = pfilter->GetPointData(); - vtkDataArray* odfvals = data->GetArray("vector"); + vtkDataArray* image_vals = data->GetArray("vector"); vtkIdType id = pfilter->GetPointId(); m_OdfTransform->Identity(); m_OdfTransform->Translate(point[0],point[1],point[2]); typedef itk::OrientationDistributionFunction OdfType; OdfType odf; - if( odfvals->GetNumberOfComponents()==6 and not m_ToggleTensorEllipsoidView ) + if( image_vals->GetNumberOfComponents()==6 && !m_ToggleTensorEllipsoidView ) { float tensorelems[6] = { - (float)odfvals->GetComponent(id,0), - (float)odfvals->GetComponent(id,1), - (float)odfvals->GetComponent(id,2), - (float)odfvals->GetComponent(id,3), - (float)odfvals->GetComponent(id,4), - (float)odfvals->GetComponent(id,5), + (float)image_vals->GetComponent(id,0), + (float)image_vals->GetComponent(id,1), + (float)image_vals->GetComponent(id,2), + (float)image_vals->GetComponent(id,3), + (float)image_vals->GetComponent(id,4), + (float)image_vals->GetComponent(id,5), }; itk::DiffusionTensor3D tensor(tensorelems); odf.InitFromTensor(tensor); } - else if( odfvals->GetNumberOfComponents()==6 and m_ToggleTensorEllipsoidView ) + else if( image_vals->GetNumberOfComponents()==6 && m_ToggleTensorEllipsoidView ) { float tensorelems[6] = { - (float)odfvals->GetComponent(id,0), - (float)odfvals->GetComponent(id,1), - (float)odfvals->GetComponent(id,2), - (float)odfvals->GetComponent(id,3), - (float)odfvals->GetComponent(id,4), - (float)odfvals->GetComponent(id,5), + (float)image_vals->GetComponent(id,0), + (float)image_vals->GetComponent(id,1), + (float)image_vals->GetComponent(id,2), + (float)image_vals->GetComponent(id,3), + (float)image_vals->GetComponent(id,4), + (float)image_vals->GetComponent(id,5), }; itk::DiffusionTensor3D tensor( tensorelems ); odf.InitFromEllipsoid( tensor ); } + else if (image_vals->GetNumberOfComponents() == ODF_SAMPLING_SIZE) + { + for(int i=0; iGetComponent(id,i); + } else { + int nrCoeffs = image_vals->GetNumberOfComponents(); + Vector< float, N > odf_vals; + vnl_vector< float > coeffs(nrCoeffs); + for(int i=0; iGetComponent(id,i); + + switch (nrCoeffs) + { + case 6: + odf_vals = ( m_Sh2Basis * coeffs ).data_block(); + break; + case 15: + odf_vals = ( m_Sh4Basis * coeffs ).data_block(); + break; + case 28: + odf_vals = ( m_Sh6Basis * coeffs ).data_block(); + break; + case 45: + odf_vals = ( m_Sh8Basis * coeffs ).data_block(); + break; + case 66: + odf_vals = ( m_Sh10Basis * coeffs ).data_block(); + break; + case 91: + odf_vals = ( m_Sh12Basis * coeffs ).data_block(); + break; + default : + mitkThrow() << "SH order larger 12 not supported in current ODF mapper version"; + } + for(int i=0; iGetComponent(id,i); + odf[i] = odf_vals[i]; } if (m_ToggleColourisationMode) { m_OdfSource->SetUseCustomColor(true); vnl_vector_fixed d = odf.GetPrincipalDiffusionDirection(); m_OdfSource->SetColor(fabs(d[0])*255,fabs(d[1])*255,fabs(d[2])*255); } else { m_OdfSource->SetUseCustomColor(false); } switch(m_ScaleBy) { case ODFSB_NONE: m_OdfSource->SetScale(m_Scaling); break; case ODFSB_GFA: m_OdfSource->SetScale(m_Scaling*odf.GetGeneralizedFractionalAnisotropy()); break; case ODFSB_PC: m_OdfSource->SetScale(m_Scaling*odf.GetPrincipleCurvature(m_IndexParam1, m_IndexParam2, 0)); break; } m_OdfSource->SetNormalization(m_Normalization); m_OdfSource->SetOdf(odf); m_OdfSource->Modified(); } template typename mitk::OdfVtkMapper2D::OdfDisplayGeometry mitk::OdfVtkMapper2D ::MeasureDisplayedGeometry(mitk::BaseRenderer* renderer) { PlaneGeometry::ConstPointer worldPlaneGeometry = renderer->GetCurrentWorldPlaneGeometry(); // set up the cutter orientation according to the current geometry of // the renderers plane double vp[ 3 ], vnormal[ 3 ]; Point3D point = worldPlaneGeometry->GetOrigin(); Vector3D normal = worldPlaneGeometry->GetNormal(); normal.Normalize(); vnl2vtk( point.GetVnlVector(), vp ); vnl2vtk( normal.GetVnlVector(), vnormal ); Point2D dispSizeInMM = renderer->GetViewportSizeInMM(); Point2D displayGeometryOriginInMM = renderer->GetOriginInMM(); mitk::Vector2D size = dispSizeInMM.GetVectorFromOrigin(); mitk::Vector2D origin = displayGeometryOriginInMM.GetVectorFromOrigin(); // // |------O------| // | d2 | // L d1 M | // | | // |-------------| // mitk::Vector2D M; mitk::Vector2D L; mitk::Vector2D O; M[0] = origin[0] + size[0]/2; M[1] = origin[1] + size[1]/2; L[0] = origin[0]; L[1] = origin[1] + size[1]/2; O[0] = origin[0] + size[0]/2; O[1] = origin[1] + size[1]; mitk::Point2D point1; point1[0] = M[0]; point1[1] = M[1]; mitk::Point3D M3D; renderer->GetCurrentWorldPlaneGeometry()->Map(point1, M3D); point1[0] = L[0]; point1[1] = L[1]; mitk::Point3D L3D; renderer->GetCurrentWorldPlaneGeometry()->Map(point1, L3D); point1[0] = O[0]; point1[1] = O[1]; mitk::Point3D O3D; renderer->GetCurrentWorldPlaneGeometry()->Map(point1, O3D); double d1 = sqrt((M3D[0]-L3D[0])*(M3D[0]-L3D[0]) + (M3D[1]-L3D[1])*(M3D[1]-L3D[1]) + (M3D[2]-L3D[2])*(M3D[2]-L3D[2])); double d2 = sqrt((M3D[0]-O3D[0])*(M3D[0]-O3D[0]) + (M3D[1]-O3D[1])*(M3D[1]-O3D[1]) + (M3D[2]-O3D[2])*(M3D[2]-O3D[2])); double d = d1>d2 ? d1 : d2; d = d2; OdfDisplayGeometry retval; retval.vp[0] = vp[0]; retval.vp[1] = vp[1]; retval.vp[2] = vp[2]; retval.vnormal[0] = vnormal[0]; retval.vnormal[1] = vnormal[1]; retval.vnormal[2] = vnormal[2]; retval.normal[0] = normal[0]; retval.normal[1] = normal[1]; retval.normal[2] = normal[2]; retval.d = d; retval.d1 = d1; retval.d2 = d2; retval.M3D[0] = M3D[0]; retval.M3D[1] = M3D[1]; retval.M3D[2] = M3D[2]; retval.L3D[0] = L3D[0]; retval.L3D[1] = L3D[1]; retval.L3D[2] = L3D[2]; retval.O3D[0] = O3D[0]; retval.O3D[1] = O3D[1]; retval.O3D[2] = O3D[2]; retval.vp_original[0] = vp[0]; retval.vp_original[1] = vp[1]; retval.vp_original[2] = vp[2]; retval.vnormal_original[0] = vnormal[0]; retval.vnormal_original[1] = vnormal[1]; retval.vnormal_original[2] = vnormal[2]; retval.size[0] = size[0]; retval.size[1] = size[1]; retval.origin[0] = origin[0]; retval.origin[1] = origin[1]; return retval; } template void mitk::OdfVtkMapper2D ::Slice(mitk::BaseRenderer* renderer, OdfDisplayGeometry dispGeo) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); vtkLinearTransform * vtktransform = this->GetDataNode()->GetVtkTransform(this->GetTimestep()); int index = GetIndex(renderer); vtkSmartPointer inversetransform = vtkSmartPointer::New(); inversetransform->Identity(); inversetransform->Concatenate(vtktransform->GetLinearInverse()); double myscale[3]; ((vtkTransform*)vtktransform)->GetScale(myscale); myscale[0] = fabs(myscale[0]); myscale[1] = fabs(myscale[1]); myscale[2] = fabs(myscale[2]); inversetransform->PostMultiply(); inversetransform->Scale(myscale[0],myscale[1],myscale[2]); inversetransform->TransformPoint( dispGeo.vp, dispGeo.vp ); inversetransform->TransformNormalAtPoint( dispGeo.vp, dispGeo.vnormal, dispGeo.vnormal ); // vtk works in axis align coords // thus the normal also must be axis align, since // we do not allow arbitrary cutting through volume // // vnormal should already be axis align, but in order // to get rid of precision effects, we set the two smaller // components to zero here int dims[3]; m_VtkImage->GetDimensions(dims); double spac[3]; m_VtkImage->GetSpacing(spac); if(fabs(dispGeo.vnormal[0]) > fabs(dispGeo.vnormal[1]) && fabs(dispGeo.vnormal[0]) > fabs(dispGeo.vnormal[2]) ) { if(fabs(dispGeo.vp[0]/spac[0]) < 0.4) dispGeo.vp[0] = 0.4*spac[0]; if(fabs(dispGeo.vp[0]/spac[0]) > (dims[0]-1)-0.4) dispGeo.vp[0] = ((dims[0]-1)-0.4)*spac[0]; dispGeo.vnormal[1] = 0; dispGeo.vnormal[2] = 0; } if(fabs(dispGeo.vnormal[1]) > fabs(dispGeo.vnormal[0]) && fabs(dispGeo.vnormal[1]) > fabs(dispGeo.vnormal[2]) ) { if(fabs(dispGeo.vp[1]/spac[1]) < 0.4) dispGeo.vp[1] = 0.4*spac[1]; if(fabs(dispGeo.vp[1]/spac[1]) > (dims[1]-1)-0.4) dispGeo.vp[1] = ((dims[1]-1)-0.4)*spac[1]; dispGeo.vnormal[0] = 0; dispGeo.vnormal[2] = 0; } if(fabs(dispGeo.vnormal[2]) > fabs(dispGeo.vnormal[1]) && fabs(dispGeo.vnormal[2]) > fabs(dispGeo.vnormal[0]) ) { if(fabs(dispGeo.vp[2]/spac[2]) < 0.4) dispGeo.vp[2] = 0.4*spac[2]; if(fabs(dispGeo.vp[2]/spac[2]) > (dims[2]-1)-0.4) dispGeo.vp[2] = ((dims[2]-1)-0.4)*spac[2]; dispGeo.vnormal[0] = 0; dispGeo.vnormal[1] = 0; } m_Planes[index]->SetTransform( (vtkAbstractTransform*)NULL ); m_Planes[index]->SetOrigin( dispGeo.vp ); m_Planes[index]->SetNormal( dispGeo.vnormal ); vtkSmartPointer points; vtkSmartPointer tmppoints; vtkSmartPointer polydata; vtkSmartPointer pointdata; vtkSmartPointer delaunay; vtkSmartPointer cuttedPlane; // the cutter only works if we do not have a 2D-image // or if we have a 2D-image and want to see the whole image. // // for side views of 2D-images, we need some special treatment if(!( (dims[0] == 1 && dispGeo.vnormal[0] != 0) || (dims[1] == 1 && dispGeo.vnormal[1] != 0) || (dims[2] == 1 && dispGeo.vnormal[2] != 0) )) { m_Cutters[index]->SetCutFunction( m_Planes[index] ); m_Cutters[index]->SetInputData( m_VtkImage ); m_Cutters[index]->Update(); cuttedPlane = m_Cutters[index]->GetOutput(); } else { // cutting of a 2D-Volume does not work, // so we have to build up our own polydata object cuttedPlane = vtkPolyData::New(); points = vtkPoints::New(); points->SetNumberOfPoints(m_VtkImage->GetNumberOfPoints()); for(int i=0; iGetNumberOfPoints(); i++) { points->SetPoint(i, m_VtkImage->GetPoint(i)); } cuttedPlane->SetPoints(points); int nZero1, nZero2; if(dims[0]==1) { nZero1 = 1; nZero2 = 2; } else if(dims[1]==1) { nZero1 = 0; nZero2 = 2; } else { nZero1 = 0; nZero2 = 1; } tmppoints = vtkPoints::New(); for(int j=0; jGetNumberOfPoints(); j++){ double pt[3]; m_VtkImage->GetPoint(j,pt); tmppoints->InsertNextPoint(pt[nZero1],pt[nZero2],0); } polydata = vtkPolyData::New(); polydata->SetPoints( tmppoints ); delaunay = vtkDelaunay2D::New(); delaunay->SetInputData( polydata ); delaunay->Update(); vtkCellArray* polys = delaunay->GetOutput()->GetPolys(); cuttedPlane->SetPolys(polys); } if(cuttedPlane->GetNumberOfPoints()) { // WINDOWING HERE dispGeo.vnormal[0] = dispGeo.M3D[0]-dispGeo.O3D[0]; dispGeo.vnormal[1] = dispGeo.M3D[1]-dispGeo.O3D[1]; dispGeo.vnormal[2] = dispGeo.M3D[2]-dispGeo.O3D[2]; vtkMath::Normalize(dispGeo.vnormal); dispGeo.vp[0] = dispGeo.M3D[0]; dispGeo.vp[1] = dispGeo.M3D[1]; dispGeo.vp[2] = dispGeo.M3D[2]; inversetransform->TransformPoint( dispGeo.vp, dispGeo.vp ); inversetransform->TransformNormalAtPoint( dispGeo.vp, dispGeo.vnormal, dispGeo.vnormal ); m_ThickPlanes1[index]->count = 0; m_ThickPlanes1[index]->SetTransform((vtkAbstractTransform*)NULL ); m_ThickPlanes1[index]->SetPose( dispGeo.vnormal, dispGeo.vp ); m_ThickPlanes1[index]->SetThickness(dispGeo.d2); m_Clippers1[index]->SetClipFunction( m_ThickPlanes1[index] ); m_Clippers1[index]->SetInputData( cuttedPlane ); m_Clippers1[index]->SetInsideOut(1); m_Clippers1[index]->Update(); dispGeo.vnormal[0] = dispGeo.M3D[0]-dispGeo.L3D[0]; dispGeo.vnormal[1] = dispGeo.M3D[1]-dispGeo.L3D[1]; dispGeo.vnormal[2] = dispGeo.M3D[2]-dispGeo.L3D[2]; vtkMath::Normalize(dispGeo.vnormal); dispGeo.vp[0] = dispGeo.M3D[0]; dispGeo.vp[1] = dispGeo.M3D[1]; dispGeo.vp[2] = dispGeo.M3D[2]; inversetransform->TransformPoint( dispGeo.vp, dispGeo.vp ); inversetransform->TransformNormalAtPoint( dispGeo.vp, dispGeo.vnormal, dispGeo.vnormal ); m_ThickPlanes2[index]->count = 0; m_ThickPlanes2[index]->SetTransform((vtkAbstractTransform*)NULL ); m_ThickPlanes2[index]->SetPose( dispGeo.vnormal, dispGeo.vp ); m_ThickPlanes2[index]->SetThickness(dispGeo.d1); m_Clippers2[index]->SetClipFunction( m_ThickPlanes2[index] ); m_Clippers2[index]->SetInputData( m_Clippers1[index]->GetOutput() ); m_Clippers2[index]->SetInsideOut(1); m_Clippers2[index]->Update(); cuttedPlane = m_Clippers2[index]->GetOutput (); if(cuttedPlane->GetNumberOfPoints()) { localStorage->m_OdfsPlanes[index]->RemoveAllInputs(); vtkSmartPointer normals = vtkSmartPointer::New(); normals->SetInputConnection( m_OdfSource->GetOutputPort() ); normals->SplittingOff(); normals->ConsistencyOff(); normals->AutoOrientNormalsOff(); normals->ComputePointNormalsOn(); normals->ComputeCellNormalsOff(); normals->FlipNormalsOff(); normals->NonManifoldTraversalOff(); vtkSmartPointer trans = vtkSmartPointer::New(); trans->SetInputConnection( normals->GetOutputPort() ); trans->SetTransform(m_OdfTransform); vtkSmartPointer glyphGenerator = vtkSmartPointer::New(); glyphGenerator->SetMaximumNumberOfPoints(std::min(m_ShowMaxNumber,(int)cuttedPlane->GetNumberOfPoints())); glyphGenerator->SetRandomMode( m_ToggleGlyphPlacementMode ); glyphGenerator->SetUseMaskPoints(1); glyphGenerator->SetSourceConnection(trans->GetOutputPort() ); glyphGenerator->SetInput(cuttedPlane); glyphGenerator->SetColorModeToColorBySource(); glyphGenerator->SetGeometry(this->GetDataNode()->GetData()->GetGeometry()); glyphGenerator->SetGlyphMethod(&(GlyphMethod),(void *)glyphGenerator); try { glyphGenerator->Update(); } catch( itk::ExceptionObject& err ) { std::cout << err << std::endl; } localStorage->m_OdfsPlanes[index]->AddInputConnection(glyphGenerator->GetOutputPort()); localStorage->m_OdfsPlanes[index]->Update(); } } localStorage->m_OdfsMappers[index]->ScalarVisibilityOn(); localStorage->m_OdfsMappers[index]->SetScalarModeToUsePointFieldData(); localStorage->m_OdfsMappers[index]->SelectColorArray("ODF_COLORS"); localStorage->m_PropAssemblies[index]->VisibilityOn(); if(localStorage->m_PropAssemblies[index]->GetParts()->IsItemPresent(localStorage->m_OdfsActors[index])) { localStorage->m_PropAssemblies[index]->RemovePart(localStorage->m_OdfsActors[index]); } localStorage->m_OdfsMappers[index]->SetInputData(localStorage->m_OdfsPlanes[index]->GetOutput()); localStorage->m_PropAssemblies[index]->AddPart(localStorage->m_OdfsActors[index]); } template bool mitk::OdfVtkMapper2D ::IsVisibleOdfs(mitk::BaseRenderer* renderer) { mitk::Image::Pointer input = const_cast(this->GetInput()); const TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); if(inputTimeGeometry==NULL || inputTimeGeometry->CountTimeSteps()==0 || !inputTimeGeometry->IsValidTimeStep(this->GetTimestep())) return false; if(this->IsPlaneRotated(renderer)) return false; bool retval = false; switch(GetIndex(renderer)) { case 0: GetDataNode()->GetVisibility(retval, renderer, "VisibleOdfs_T"); break; case 1: GetDataNode()->GetVisibility(retval, renderer, "VisibleOdfs_S"); break; case 2: GetDataNode()->GetVisibility(retval, renderer, "VisibleOdfs_C"); break; } return retval; } template void mitk::OdfVtkMapper2D ::MitkRenderOverlay(mitk::BaseRenderer* renderer) { if ( this->IsVisibleOdfs(renderer)==false ) return; if ( this->GetVtkProp(renderer)->GetVisibility() ) this->GetVtkProp(renderer)->RenderOverlay(renderer->GetVtkRenderer()); } template void mitk::OdfVtkMapper2D ::MitkRenderOpaqueGeometry(mitk::BaseRenderer* renderer) { if ( this->IsVisibleOdfs( renderer )==false ) return; if ( this->GetVtkProp(renderer)->GetVisibility() ) { // adapt cam pos this->GetVtkProp(renderer)->RenderOpaqueGeometry( renderer->GetVtkRenderer() ); } } template void mitk::OdfVtkMapper2D ::MitkRenderTranslucentGeometry(mitk::BaseRenderer* renderer) { if ( this->IsVisibleOdfs(renderer)==false ) return; if ( this->GetVtkProp(renderer)->GetVisibility() ) this->GetVtkProp(renderer)->RenderTranslucentPolygonalGeometry(renderer->GetVtkRenderer()); } template void mitk::OdfVtkMapper2D ::Update(mitk::BaseRenderer* renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) return; mitk::Image::Pointer input = const_cast( this->GetInput() ); if ( input.IsNull() ) return ; std::string classname("TensorImage"); if(classname.compare(input->GetNameOfClass())==0) m_VtkImage = dynamic_cast( this->GetInput() )->GetNonRgbVtkImageData(); std::string qclassname("OdfImage"); if(qclassname.compare(input->GetNameOfClass())==0) m_VtkImage = dynamic_cast( this->GetInput() )->GetNonRgbVtkImageData(); + std::string shclassname("ShImage"); + if(shclassname.compare(input->GetNameOfClass())==0) + m_VtkImage = dynamic_cast( this->GetInput() )->GetNonRgbVtkImageData(); + if( m_VtkImage ) { // make sure, that we have point data with more than 1 component (as vectors) vtkPointData* pointData = m_VtkImage->GetPointData(); if ( pointData == NULL ) { itkWarningMacro( << "m_VtkImage->GetPointData() returns NULL!" ); return ; } if ( pointData->GetNumberOfArrays() == 0 ) { itkWarningMacro( << "m_VtkImage->GetPointData()->GetNumberOfArrays() is 0!" ); return ; } - else if ( pointData->GetArray(0)->GetNumberOfComponents() != N - && pointData->GetArray(0)->GetNumberOfComponents() != 6 /*for tensor visualization*/) - { - itkWarningMacro( << "number of components != number of directions in ODF!" ); - return; - } else if ( pointData->GetArrayName( 0 ) == NULL ) { m_VtkImage->GetPointData()->GetArray(0)->SetName("vector"); } GenerateDataForRenderer(renderer); } else { itkWarningMacro( << "m_VtkImage is NULL!" ); return ; } } template void mitk::OdfVtkMapper2D ::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); OdfDisplayGeometry dispGeo = MeasureDisplayedGeometry( renderer); if ((localStorage->m_LastUpdateTime >= m_DataNode->GetMTime()) //was the node modified? && (localStorage->m_LastUpdateTime >= m_DataNode->GetPropertyList()->GetMTime()) //was a property modified? && (localStorage->m_LastUpdateTime >= m_DataNode->GetPropertyList(renderer)->GetMTime()) && dispGeo.Equals(m_LastDisplayGeometry.at(GetIndex(renderer)))) { return; } localStorage->m_LastUpdateTime.Modified(); if(!IsVisibleOdfs(renderer)) { localStorage->m_OdfsActors[0]->VisibilityOff(); localStorage->m_OdfsActors[1]->VisibilityOff(); localStorage->m_OdfsActors[2]->VisibilityOff(); } else { localStorage->m_OdfsActors[0]->VisibilityOn(); localStorage->m_OdfsActors[1]->VisibilityOn(); localStorage->m_OdfsActors[2]->VisibilityOn(); m_OdfSource->SetAdditionalScale(GetMinImageSpacing(GetIndex(renderer))); ApplyPropertySettings(); Slice(renderer, dispGeo); m_LastDisplayGeometry[GetIndex(renderer)] = dispGeo; } } template double mitk::OdfVtkMapper2D::GetMinImageSpacing( int index ) { // Spacing adapted scaling double spacing[3]; m_VtkImage->GetSpacing(spacing); double min = spacing[0]; if(index==0) { min = spacing[0]; min = min > spacing[1] ? spacing[1] : min; } if(index==1) { min = spacing[1]; min = min > spacing[2] ? spacing[2] : min; } if(index==2) { min = spacing[0]; min = min > spacing[2] ? spacing[2] : min; } return min; } template void mitk::OdfVtkMapper2D ::ApplyPropertySettings() { this->GetDataNode()->GetFloatProperty( "Scaling", m_Scaling ); this->GetDataNode()->GetIntProperty( "ShowMaxNumber", m_ShowMaxNumber ); OdfNormalizationMethodProperty* nmp = dynamic_cast(this->GetDataNode()->GetProperty( "Normalization" )); if(nmp) m_Normalization = nmp->GetNormalization(); OdfScaleByProperty* sbp = dynamic_cast(this->GetDataNode()->GetProperty( "ScaleBy" )); if(sbp) m_ScaleBy = sbp->GetScaleBy(); this->GetDataNode()->GetFloatProperty( "IndexParam1", m_IndexParam1); this->GetDataNode()->GetFloatProperty( "IndexParam2", m_IndexParam2); this->GetDataNode()->GetBoolProperty( "DiffusionCore.Rendering.OdfVtkMapper.SwitchTensorView", m_ToggleTensorEllipsoidView ); this->GetDataNode()->GetBoolProperty( "DiffusionCore.Rendering.OdfVtkMapper.ColourisationModeBit", m_ToggleColourisationMode ); this->GetDataNode()->GetBoolProperty( "DiffusionCore.Rendering.OdfVtkMapper.RandomModeBit", m_ToggleGlyphPlacementMode); } template bool mitk::OdfVtkMapper2D ::IsPlaneRotated(mitk::BaseRenderer* renderer) { PlaneGeometry::ConstPointer worldPlaneGeometry = renderer->GetCurrentWorldPlaneGeometry(); double vnormal[ 3 ]; Vector3D normal = worldPlaneGeometry->GetNormal(); normal.Normalize(); vnl2vtk( normal.GetVnlVector(), vnormal ); mitk::Image* currentImage = dynamic_cast( this->GetDataNode()->GetData() ); if( currentImage == NULL ) return false; mitk::Vector3D imageNormal0 = currentImage->GetSlicedGeometry()->GetAxisVector(0); mitk::Vector3D imageNormal1 = currentImage->GetSlicedGeometry()->GetAxisVector(1); mitk::Vector3D imageNormal2 = currentImage->GetSlicedGeometry()->GetAxisVector(2); imageNormal0.Normalize(); imageNormal1.Normalize(); imageNormal2.Normalize(); double eps = 0.000001; // Did you mean: std::numeric_limits::epsilon(); ? int test = 0; if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal0.GetVnlVector()))-1) > eps ) test++; if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal1.GetVnlVector()))-1) > eps ) test++; if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal2.GetVnlVector()))-1) > eps ) test++; if (test==3) return true; return false; } template void mitk::OdfVtkMapper2D ::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* /*renderer*/, bool /*overwrite*/) { node->SetProperty( "ShowMaxNumber", mitk::IntProperty::New( 500 ) ); node->SetProperty( "Normalization", mitk::OdfNormalizationMethodProperty::New()); mitk::OdfScaleByProperty::Pointer prop = mitk::OdfScaleByProperty::New(); prop->SetScaleByGFA(); node->SetProperty( "ScaleBy", prop); if (dynamic_cast(node->GetData())) { node->AddProperty( "DiffusionCore.Rendering.OdfVtkMapper.ColourisationModeBit", mitk::BoolProperty::New( true ) ); node->SetProperty( "Scaling", mitk::FloatProperty::New( 3.0 ) ); } else { node->AddProperty( "DiffusionCore.Rendering.OdfVtkMapper.ColourisationModeBit", mitk::BoolProperty::New( false ) ); node->SetProperty( "Scaling", mitk::FloatProperty::New( 1.5 ) ); } node->SetProperty( "IndexParam1", mitk::FloatProperty::New(2)); node->SetProperty( "IndexParam2", mitk::FloatProperty::New(1)); node->SetProperty( "visible", mitk::BoolProperty::New( true ) ); node->SetProperty( "VisibleOdfs_T", mitk::BoolProperty::New( false ) ); node->SetProperty( "VisibleOdfs_C", mitk::BoolProperty::New( false ) ); node->SetProperty( "VisibleOdfs_S", mitk::BoolProperty::New( false ) ); node->SetProperty( "layer", mitk::IntProperty::New(100)); node->SetProperty( "DoRefresh", mitk::BoolProperty::New( true ) ); node->AddProperty( "DiffusionCore.Rendering.OdfVtkMapper.SwitchTensorView", mitk::BoolProperty::New( true) ); node->AddProperty( "DiffusionCore.Rendering.OdfVtkMapper.RandomModeBit", mitk::BoolProperty::New( true ) ); } #endif // __mitkOdfVtkMapper2D_txx__ diff --git a/Modules/DiffusionImaging/DiffusionCore/include/mitkDiffusionFunctionCollection.h b/Modules/DiffusionImaging/DiffusionCore/include/mitkDiffusionFunctionCollection.h index d11532fc2e..ab1930a3ba 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/mitkDiffusionFunctionCollection.h +++ b/Modules/DiffusionImaging/DiffusionCore/include/mitkDiffusionFunctionCollection.h @@ -1,118 +1,119 @@ /*=================================================================== 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 __mitkDiffusionFunctionCollection_h_ #define __mitkDiffusionFunctionCollection_h_ #include #include #include #include #include #include #include namespace mitk{ class imv { public: template< class TPixelType, class TOutPixelType=TPixelType > static TOutPixelType GetImageValue(const itk::Point& itkP, bool interpolate, typename itk::LinearInterpolateImageFunction< itk::Image< TPixelType, 3 >, float >::Pointer interpolator) { if (interpolator==nullptr) return 0.0; itk::ContinuousIndex< float, 3> cIdx; interpolator->ConvertPointToContinuousIndex(itkP, cIdx); if (interpolator->IsInsideBuffer(cIdx)) { if (interpolate) return interpolator->EvaluateAtContinuousIndex(cIdx); else { itk::Index<3> idx; interpolator->ConvertContinuousIndexToNearestIndex(cIdx, idx); return interpolator->EvaluateAtIndex(idx); } } else return 0.0; } template< class TPixelType=unsigned char > static bool IsInsideMask(const itk::Point& itkP, bool interpolate, typename itk::LinearInterpolateImageFunction< itk::Image< TPixelType, 3 >, float >::Pointer interpolator, float threshold=0.5) { if (interpolator==nullptr) return false; itk::ContinuousIndex< float, 3> cIdx; interpolator->ConvertPointToContinuousIndex(itkP, cIdx); if (interpolator->IsInsideBuffer(cIdx)) { double value = 0.0; if (interpolate) value = interpolator->EvaluateAtContinuousIndex(cIdx); else { itk::Index<3> idx; interpolator->ConvertContinuousIndexToNearestIndex(cIdx, idx); value = interpolator->EvaluateAtIndex(idx); } if (value>=threshold) return true; } return false; } }; class MITKDIFFUSIONCORE_EXPORT sh { public: static double factorial(int number); - static void Cart2Sph(double x, double y, double z, double* cart); + static void Cart2Sph(double x, double y, double z, double* spherical); static double legendre0(int l); static double spherical_harmonic(int m,int l,double theta,double phi, bool complexPart); - static double Yj(int m, int k, double theta, double phi); + static double Yj(int m, int k, float theta, float phi, bool mrtrix=true); + static vnl_matrix CalcShBasisForDirections(int sh_order, vnl_matrix U, bool mrtrix=true); }; class MITKDIFFUSIONCORE_EXPORT gradients { private: typedef std::vector IndiciesVector; typedef std::map BValueMap; typedef itk::VectorContainer< unsigned int, vnl_vector_fixed< double, 3 > > GradientDirectionContainerType; typedef vnl_vector_fixed GradientDirectionType; public: static std::vector GetAllUniqueDirections(const BValueMap &bValueMap, GradientDirectionContainerType *refGradientsContainer ); static bool CheckForDifferingShellDirections(const BValueMap &bValueMap, GradientDirectionContainerType::ConstPointer refGradientsContainer); static vnl_matrix ComputeSphericalHarmonicsBasis(const vnl_matrix & QBallReference, const unsigned int & LOrder); static vnl_matrix ComputeSphericalFromCartesian(const IndiciesVector & refShell, const GradientDirectionContainerType * refGradientsContainer); static mitk::gradients::GradientDirectionContainerType::Pointer CreateNormalizedUniqueGradientDirectionContainer(const BValueMap &bValueMap, const GradientDirectionContainerType * origninalGradentcontainer); }; } #endif //__mitkDiffusionFunctionCollection_h_ diff --git a/Modules/DiffusionImaging/DiffusionCore/src/IODataStructures/mitkShImage.cpp b/Modules/DiffusionImaging/DiffusionCore/src/IODataStructures/mitkShImage.cpp new file mode 100644 index 0000000000..ad46da3454 --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/src/IODataStructures/mitkShImage.cpp @@ -0,0 +1,157 @@ +/*=================================================================== + +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 "mitkShImage.h" +#include "mitkImageCast.h" +#include "itkImage.h" +#include "mitkImageVtkAccessor.h" +#include +#include "itkShToRgbImageFilter.h" +#include + +mitk::ShImage::ShImage() : Image() + , m_ShOrder(0) + , m_NumCoefficients(0) +{ + m_RgbImage = nullptr; + // not needed anymore as soon as all diffusion images are identified via properties anyway + this->SetProperty("IsShImage", mitk::BoolProperty::New(true)); +} + +mitk::ShImage::~ShImage() +{ + +} + +int mitk::ShImage::NumCoefficients() +{ + if (m_NumCoefficients==0 || m_ShOrder==0) + { + m_NumCoefficients = this->m_ImageDescriptor->GetChannelTypeById(0).GetNumberOfComponents(); + int c=3, d=2-2*m_NumCoefficients; + double D = c*c-4*d; + if (D>0) + { + m_ShOrder = (-c+sqrt(D))/2.0; + if (m_ShOrder<0) + m_ShOrder = (-c-sqrt(D))/2.0; + } + else if (D==0) + m_ShOrder = -c/2.0; + } + return m_NumCoefficients; +} + +int mitk::ShImage::ShOrder() +{ + if (m_NumCoefficients==0 || m_ShOrder==0) + { + m_NumCoefficients = this->m_ImageDescriptor->GetChannelTypeById(0).GetNumberOfComponents(); + int c=3, d=2-2*m_NumCoefficients; + double D = c*c-4*d; + if (D>0) + { + m_ShOrder = (-c+sqrt(D))/2.0; + if (m_ShOrder<0) + m_ShOrder = (-c-sqrt(D))/2.0; + } + else if (D==0) + m_ShOrder = -c/2.0; + } + return m_ShOrder; +} + +vtkImageData* mitk::ShImage::GetVtkImageData(int t, int n) +{ + if(m_RgbImage.IsNull()) + { + ShOrder(); + NumCoefficients(); + ConstructRgbImage(); + } + return m_RgbImage->GetVtkImageData(t,n); +} + +const vtkImageData*mitk::ShImage::GetVtkImageData(int t, int n) const +{ + if(m_RgbImage.IsNull()) + { + ConstructRgbImage(); + } + return m_RgbImage->GetVtkImageData(t,n); +} + +template +void mitk::ShImage::Construct() const +{ + typedef itk::Image,3> ImageType; + typedef itk::ShToRgbImageFilter FilterType; + typename FilterType::Pointer filter = FilterType::New(); + + typename ImageType::Pointer itkvol = ImageType::New(); + mitk::CastToItkImage(this, itkvol); + filter->SetInput(itkvol); + filter->Update(); + + itk::Image,3>::Pointer tmp = filter->GetOutput(); + m_RgbImage = mitk::Image::New(); + m_RgbImage->InitializeByItk( tmp.GetPointer() ); + m_RgbImage->SetVolume( tmp->GetBufferPointer() ); +} + +void mitk::ShImage::ConstructRgbImage() const +{ + switch (this->m_ImageDescriptor->GetChannelTypeById(0).GetNumberOfComponents()) + { + case 6: + Construct<2>(); + break; + case 15: + Construct<4>(); + break; + case 28: + Construct<6>(); + break; + case 45: + Construct<8>(); + break; + case 66: + Construct<10>(); + break; + case 91: + Construct<12>(); + break; + default : + mitkThrow() << "SH order larger 12 not supported"; + } +} + +const vtkImageData* mitk::ShImage::GetNonRgbVtkImageData(int t, int n) const +{ + return Superclass::GetVtkImageData(t,n); +} + +vtkImageData* mitk::ShImage::GetNonRgbVtkImageData(int t, int n) +{ + return Superclass::GetVtkImageData(t,n); +} + +void mitk::ShImage::PrintSelf(std::ostream &os, itk::Indent indent) const +{ + os << indent << "Spherical harmonics order: " << m_ShOrder << std::endl; + os << indent << "Number of coefficients: " << m_NumCoefficients << std::endl; + Superclass::PrintSelf(os, indent); +} diff --git a/Modules/DiffusionImaging/DiffusionCore/src/IODataStructures/mitkShImageSource.cpp b/Modules/DiffusionImaging/DiffusionCore/src/IODataStructures/mitkShImageSource.cpp new file mode 100644 index 0000000000..c3bc718496 --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/src/IODataStructures/mitkShImageSource.cpp @@ -0,0 +1,45 @@ +/*=================================================================== + +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 "mitkShImageSource.h" + + +mitk::ShImageSource::ShImageSource() +{ + // Create the output. We use static_cast<> here because we know the default + // output must be of type TOutputImage + OutputImageType::Pointer output + = static_cast(this->MakeOutput(0).GetPointer()); + Superclass::SetNumberOfRequiredOutputs(1); + Superclass::SetNthOutput(0, output.GetPointer()); +} + +itk::DataObject::Pointer mitk::ShImageSource::MakeOutput ( DataObjectPointerArraySizeType /*idx*/ ) +{ + return OutputImageType::New().GetPointer(); +} + + +itk::DataObject::Pointer mitk::ShImageSource::MakeOutput( const DataObjectIdentifierType & name ) +{ + itkDebugMacro("MakeOutput(" << name << ")"); + if( this->IsIndexedOutputName(name) ) + { + return this->MakeOutput( this->MakeIndexFromOutputName(name) ); + } + return static_cast(OutputImageType::New().GetPointer()); +} diff --git a/Modules/DiffusionImaging/DiffusionCore/src/mitkDiffusionFunctionCollection.cpp b/Modules/DiffusionImaging/DiffusionCore/src/mitkDiffusionFunctionCollection.cpp index f0a064d50e..4c613beb2c 100644 --- a/Modules/DiffusionImaging/DiffusionCore/src/mitkDiffusionFunctionCollection.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/src/mitkDiffusionFunctionCollection.cpp @@ -1,245 +1,288 @@ /*=================================================================== 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 "mitkDiffusionFunctionCollection.h" #include #include "mitkNumericTypes.h" // for Windows #ifndef M_PI #define M_PI 3.14159265358979323846 #endif // Namespace ::SH #include #include #include - +#include // Namespace ::Gradients #include "itkVectorContainer.h" #include "vnl/vnl_vector.h" //------------------------- SH-function ------------------------------------ double mitk::sh::factorial(int number) { if(number <= 1) return 1; double result = 1.0; for(int i=1; i<=number; i++) result *= i; return result; } -void mitk::sh::Cart2Sph(double x, double y, double z, double *cart) +void mitk::sh::Cart2Sph(double x, double y, double z, double *spherical) { double phi, th, rad; rad = sqrt(x*x+y*y+z*z); if( rad < mitk::eps ) { th = M_PI/2; phi = M_PI/2; } else { th = acos(z/rad); phi = atan2(y, x); } - cart[0] = phi; - cart[1] = th; - cart[2] = rad; + spherical[0] = phi; + spherical[1] = th; + spherical[2] = rad; } double mitk::sh::legendre0(int l) { if( l%2 != 0 ) { return 0; } else { double prod1 = 1.0; for(int i=1;i(l,abs(m),-cos(theta)); + double mag = sqrt((double)(2*l+1)/(4.0*M_PI)*::boost::math::factorial(l-abs(m))/::boost::math::factorial(l+abs(m)))*plm; + if (m>0) + return mag*cos(m*phi); + else if (m==0) + return mag; + else + return mag*sin(-m*phi); + } return 0; } +vnl_matrix mitk::sh::CalcShBasisForDirections(int sh_order, vnl_matrix U, bool mrtrix) +{ + vnl_matrix sh_basis = vnl_matrix(U.cols(), (sh_order*sh_order + sh_order + 2)/2 + sh_order ); + for(unsigned int i=0; i mitk::gradients::GetAllUniqueDirections(const BValueMap & refBValueMap, GradientDirectionContainerType *refGradientsContainer ) { IndiciesVector directioncontainer; auto mapIterator = refBValueMap.begin(); if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1) mapIterator++; //skip bzero Values for( ; mapIterator != refBValueMap.end(); mapIterator++){ IndiciesVector currentShell = mapIterator->second; while(currentShell.size()>0) { unsigned int wntIndex = currentShell.back(); currentShell.pop_back(); auto containerIt = directioncontainer.begin(); bool directionExist = false; while(containerIt != directioncontainer.end()) { if (fabs(dot_product(refGradientsContainer->ElementAt(*containerIt), refGradientsContainer->ElementAt(wntIndex))) > 0.9998) { directionExist = true; break; } containerIt++; } if(!directionExist) { directioncontainer.push_back(wntIndex); } } } return directioncontainer; } bool mitk::gradients::CheckForDifferingShellDirections(const BValueMap & refBValueMap, GradientDirectionContainerType::ConstPointer refGradientsContainer) { auto mapIterator = refBValueMap.begin(); if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1) mapIterator++; //skip bzero Values for( ; mapIterator != refBValueMap.end(); mapIterator++){ auto mapIterator_2 = refBValueMap.begin(); if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1) mapIterator_2++; //skip bzero Values for( ; mapIterator_2 != refBValueMap.end(); mapIterator_2++){ if(mapIterator_2 == mapIterator) continue; IndiciesVector currentShell = mapIterator->second; IndiciesVector testShell = mapIterator_2->second; for (unsigned int i = 0; i< currentShell.size(); i++) if (fabs(dot_product(refGradientsContainer->ElementAt(currentShell[i]), refGradientsContainer->ElementAt(testShell[i]))) <= 0.9998) { return true; } } } return false; } vnl_matrix mitk::gradients::ComputeSphericalFromCartesian(const IndiciesVector & refShell, const GradientDirectionContainerType * refGradientsContainer) { vnl_matrix Q(3, refShell.size()); Q.fill(0.0); for(unsigned int i = 0; i < refShell.size(); i++) { GradientDirectionType dir = refGradientsContainer->ElementAt(refShell[i]); double x = dir.normalize().get(0); double y = dir.normalize().get(1); double z = dir.normalize().get(2); double cart[3]; mitk::sh::Cart2Sph(x,y,z,cart); Q(0,i) = cart[0]; Q(1,i) = cart[1]; Q(2,i) = cart[2]; } return Q; } vnl_matrix mitk::gradients::ComputeSphericalHarmonicsBasis(const vnl_matrix & QBallReference, const unsigned int & LOrder) { vnl_matrix SHBasisOutput(QBallReference.cols(), (LOrder+1)*(LOrder+2)*0.5); SHBasisOutput.fill(0.0); for(int i=0; i< (int)SHBasisOutput.rows(); i++) for(int k = 0; k <= (int)LOrder; k += 2) for(int m =- k; m <= k; m++) { int j = ( k * k + k + 2 ) / 2.0 + m - 1; double phi = QBallReference(0,i); double th = QBallReference(1,i); double val = mitk::sh::Yj(m,k,th,phi); SHBasisOutput(i,j) = val; } return SHBasisOutput; } mitk::gradients::GradientDirectionContainerType::Pointer mitk::gradients::CreateNormalizedUniqueGradientDirectionContainer(const mitk::gradients::BValueMap & bValueMap, const GradientDirectionContainerType *origninalGradentcontainer) { mitk::gradients::GradientDirectionContainerType::Pointer directioncontainer = mitk::gradients::GradientDirectionContainerType::New(); auto mapIterator = bValueMap.begin(); if(bValueMap.find(0) != bValueMap.end() && bValueMap.size() > 1){ mapIterator++; //skip bzero Values vnl_vector_fixed vec; vec.fill(0.0); directioncontainer->push_back(vec); } for( ; mapIterator != bValueMap.end(); mapIterator++){ IndiciesVector currentShell = mapIterator->second; while(currentShell.size()>0) { unsigned int wntIndex = currentShell.back(); currentShell.pop_back(); mitk::gradients::GradientDirectionContainerType::Iterator containerIt = directioncontainer->Begin(); bool directionExist = false; while(containerIt != directioncontainer->End()) { if (fabs(dot_product(containerIt.Value(), origninalGradentcontainer->ElementAt(wntIndex))) > 0.9998) { directionExist = true; break; } containerIt++; } if(!directionExist) { GradientDirectionType dir(origninalGradentcontainer->ElementAt(wntIndex)); directioncontainer->push_back(dir.normalize()); } } } return directioncontainer; } diff --git a/Modules/DiffusionImaging/FiberTracking/cmdapps/Misc/PeakExtraction.cpp b/Modules/DiffusionImaging/FiberTracking/cmdapps/Misc/PeakExtraction.cpp index f81f293629..c0505da2e7 100755 --- a/Modules/DiffusionImaging/FiberTracking/cmdapps/Misc/PeakExtraction.cpp +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/Misc/PeakExtraction.cpp @@ -1,360 +1,357 @@ /*=================================================================== 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 "mitkCommandLineParser.h" #include #include #include #include #include template int StartPeakExtraction(int argc, char* argv[]) { - mitkCommandLineParser parser; - parser.setArgumentPrefix("--", "-"); - parser.addArgument("image", "i", mitkCommandLineParser::InputFile, "Input image", "sh coefficient image", us::Any(), false); - parser.addArgument("outroot", "o", mitkCommandLineParser::OutputDirectory, "Output directory", "output root", us::Any(), false); - parser.addArgument("mask", "m", mitkCommandLineParser::InputFile, "Mask", "mask image"); - parser.addArgument("normalization", "n", mitkCommandLineParser::Int, "Normalization", "0=no norm, 1=max norm, 2=single vec norm", 1, true); - parser.addArgument("numpeaks", "p", mitkCommandLineParser::Int, "Max. number of peaks", "maximum number of extracted peaks", 2, true); - parser.addArgument("peakthres", "r", mitkCommandLineParser::Float, "Peak threshold", "peak threshold relative to largest peak", 0.4, true); - parser.addArgument("abspeakthres", "a", mitkCommandLineParser::Float, "Absolute peak threshold", "absolute peak threshold weighted with local GFA value", 0.06, true); - parser.addArgument("shConvention", "s", mitkCommandLineParser::String, "Use specified SH-basis", "use specified SH-basis (MITK, FSL, MRtrix)", std::string("MITK"), true); - parser.addArgument("noFlip", "f", mitkCommandLineParser::Bool, "No flip", "do not flip input image to match MITK coordinate convention"); - parser.addArgument("clusterThres", "c", mitkCommandLineParser::Float, "Clustering threshold", "directions closer together than the specified angular threshold will be clustered (in rad)", 0.9); - parser.addArgument("flipX", "fx", mitkCommandLineParser::Bool, "Flip X", "Flip peaks in x direction"); - parser.addArgument("flipY", "fy", mitkCommandLineParser::Bool, "Flip Y", "Flip peaks in y direction"); - parser.addArgument("flipZ", "fz", mitkCommandLineParser::Bool, "Flip Z", "Flip peaks in z direction"); - - - parser.setCategory("Preprocessing Tools"); - parser.setTitle("Peak Extraction"); - parser.setDescription(""); - parser.setContributor("MIC"); - - std::map parsedArgs = parser.parseArguments(argc, argv); - if (parsedArgs.size()==0) - return EXIT_FAILURE; - - // mandatory arguments - std::string imageName = us::any_cast(parsedArgs["image"]); - std::string outRoot = us::any_cast(parsedArgs["outroot"]); - - // optional arguments - std::string maskImageName(""); - if (parsedArgs.count("mask")) - maskImageName = us::any_cast(parsedArgs["mask"]); - - int normalization = 1; - if (parsedArgs.count("normalization")) - normalization = us::any_cast(parsedArgs["normalization"]); - - int numPeaks = 2; - if (parsedArgs.count("numpeaks")) - numPeaks = us::any_cast(parsedArgs["numpeaks"]); - - float peakThres = 0.4; - if (parsedArgs.count("peakthres")) - peakThres = us::any_cast(parsedArgs["peakthres"]); - - float absPeakThres = 0.06; - if (parsedArgs.count("abspeakthres")) - absPeakThres = us::any_cast(parsedArgs["abspeakthres"]); - - float clusterThres = 0.9; - if (parsedArgs.count("clusterThres")) - clusterThres = us::any_cast(parsedArgs["clusterThres"]); - - bool noFlip = false; - if (parsedArgs.count("noFlip")) - noFlip = us::any_cast(parsedArgs["noFlip"]); - - bool flipX = false; - if (parsedArgs.count("flipX")) - flipX = us::any_cast(parsedArgs["flipX"]); - - bool flipY = false; - if (parsedArgs.count("flipY")) - flipY = us::any_cast(parsedArgs["flipY"]); - - bool flipZ = false; - if (parsedArgs.count("flipZ")) - flipZ = us::any_cast(parsedArgs["flipZ"]); + mitkCommandLineParser parser; + parser.setArgumentPrefix("--", "-"); + parser.addArgument("image", "i", mitkCommandLineParser::InputFile, "Input image", "sh coefficient image", us::Any(), false); + parser.addArgument("outroot", "o", mitkCommandLineParser::OutputDirectory, "Output directory", "output root", us::Any(), false); + parser.addArgument("mask", "m", mitkCommandLineParser::InputFile, "Mask", "mask image"); + parser.addArgument("normalization", "n", mitkCommandLineParser::Int, "Normalization", "0=no norm, 1=max norm, 2=single vec norm", 1, true); + parser.addArgument("numpeaks", "p", mitkCommandLineParser::Int, "Max. number of peaks", "maximum number of extracted peaks", 2, true); + parser.addArgument("peakthres", "r", mitkCommandLineParser::Float, "Peak threshold", "peak threshold relative to largest peak", 0.4, true); + parser.addArgument("abspeakthres", "a", mitkCommandLineParser::Float, "Absolute peak threshold", "absolute peak threshold weighted with local GFA value", 0.06, true); + parser.addArgument("shConvention", "s", mitkCommandLineParser::String, "Use specified SH-basis", "use specified SH-basis (MITK, FSL, MRtrix)", std::string("MITK"), true); + parser.addArgument("noFlip", "f", mitkCommandLineParser::Bool, "No flip", "do not flip input image to match MITK coordinate convention"); + parser.addArgument("clusterThres", "c", mitkCommandLineParser::Float, "Clustering threshold", "directions closer together than the specified angular threshold will be clustered (in rad)", 0.9); + parser.addArgument("flipX", "fx", mitkCommandLineParser::Bool, "Flip X", "Flip peaks in x direction"); + parser.addArgument("flipY", "fy", mitkCommandLineParser::Bool, "Flip Y", "Flip peaks in y direction"); + parser.addArgument("flipZ", "fz", mitkCommandLineParser::Bool, "Flip Z", "Flip peaks in z direction"); + + + parser.setCategory("Preprocessing Tools"); + parser.setTitle("Peak Extraction"); + parser.setDescription(""); + parser.setContributor("MIC"); + + std::map parsedArgs = parser.parseArguments(argc, argv); + if (parsedArgs.size()==0) + return EXIT_FAILURE; - std::cout << "image: " << imageName; - std::cout << "outroot: " << outRoot; - if (!maskImageName.empty()) - std::cout << "mask: " << maskImageName; + // mandatory arguments + std::string imageName = us::any_cast(parsedArgs["image"]); + std::string outRoot = us::any_cast(parsedArgs["outroot"]); + + // optional arguments + std::string maskImageName(""); + if (parsedArgs.count("mask")) + maskImageName = us::any_cast(parsedArgs["mask"]); + + int normalization = 1; + if (parsedArgs.count("normalization")) + normalization = us::any_cast(parsedArgs["normalization"]); + + int numPeaks = 2; + if (parsedArgs.count("numpeaks")) + numPeaks = us::any_cast(parsedArgs["numpeaks"]); + + float peakThres = 0.4; + if (parsedArgs.count("peakthres")) + peakThres = us::any_cast(parsedArgs["peakthres"]); + + float absPeakThres = 0.06; + if (parsedArgs.count("abspeakthres")) + absPeakThres = us::any_cast(parsedArgs["abspeakthres"]); + + float clusterThres = 0.9; + if (parsedArgs.count("clusterThres")) + clusterThres = us::any_cast(parsedArgs["clusterThres"]); + + bool noFlip = false; + if (parsedArgs.count("noFlip")) + noFlip = us::any_cast(parsedArgs["noFlip"]); + + bool flipX = false; + if (parsedArgs.count("flipX")) + flipX = us::any_cast(parsedArgs["flipX"]); + + bool flipY = false; + if (parsedArgs.count("flipY")) + flipY = us::any_cast(parsedArgs["flipY"]); + + bool flipZ = false; + if (parsedArgs.count("flipZ")) + flipZ = us::any_cast(parsedArgs["flipZ"]); + + std::cout << "image: " << imageName; + std::cout << "outroot: " << outRoot; + if (!maskImageName.empty()) + std::cout << "mask: " << maskImageName; + else + std::cout << "no mask image selected"; + std::cout << "numpeaks: " << numPeaks; + std::cout << "peakthres: " << peakThres; + std::cout << "abspeakthres: " << absPeakThres; + std::cout << "shOrder: " << shOrder; + + try + { + mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(imageName)[0].GetPointer()); + mitk::Image::Pointer mask = dynamic_cast(mitk::IOUtil::Load(maskImageName)[0].GetPointer()); + + typedef itk::Image ItkUcharImgType; + typedef itk::FiniteDiffOdfMaximaExtractionFilter< float, shOrder, 20242 > MaximaExtractionFilterType; + typename MaximaExtractionFilterType::Pointer peak_extraction_filter = MaximaExtractionFilterType::New(); + + int toolkitConvention = 0; + + if (parsedArgs.count("shConvention")) + { + std::string convention = us::any_cast(parsedArgs["shConvention"]).c_str(); + if ( boost::algorithm::equals(convention, "FSL") ) + { + toolkitConvention = 1; + std::cout << "Using FSL SH-basis"; + } + else if ( boost::algorithm::equals(convention, "MRtrix") ) + { + toolkitConvention = 2; + std::cout << "Using MRtrix SH-basis"; + } + else + std::cout << "Using MITK SH-basis"; + } else - std::cout << "no mask image selected"; - std::cout << "numpeaks: " << numPeaks; - std::cout << "peakthres: " << peakThres; - std::cout << "abspeakthres: " << absPeakThres; - std::cout << "shOrder: " << shOrder; + std::cout << "Using MITK SH-basis"; - try + ItkUcharImgType::Pointer itkMaskImage = nullptr; + if (mask.IsNotNull()) { - mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(imageName)[0].GetPointer()); - mitk::Image::Pointer mask = dynamic_cast(mitk::IOUtil::Load(maskImageName)[0].GetPointer()); - - typedef itk::Image ItkUcharImgType; - typedef itk::FiniteDiffOdfMaximaExtractionFilter< float, shOrder, 20242 > MaximaExtractionFilterType; - typename MaximaExtractionFilterType::Pointer filter = MaximaExtractionFilterType::New(); - - int toolkitConvention = 0; - - if (parsedArgs.count("shConvention")) - { - std::string convention = us::any_cast(parsedArgs["shConvention"]).c_str(); - if ( boost::algorithm::equals(convention, "FSL") ) - { - toolkitConvention = 1; - std::cout << "Using FSL SH-basis"; - } - else if ( boost::algorithm::equals(convention, "MRtrix") ) - { - toolkitConvention = 2; - std::cout << "Using MRtrix SH-basis"; - } - else - std::cout << "Using MITK SH-basis"; - } - else - std::cout << "Using MITK SH-basis"; - - ItkUcharImgType::Pointer itkMaskImage = nullptr; - if (mask.IsNotNull()) - { - try{ - itkMaskImage = ItkUcharImgType::New(); - mitk::CastToItkImage(mask, itkMaskImage); - filter->SetMaskImage(itkMaskImage); - } - catch(...) - { - - } - } - - if (toolkitConvention>0) - { - std::cout << "Converting coefficient image to MITK format"; - typedef itk::ShCoefficientImageImporter< float, shOrder > ConverterType; - typedef mitk::ImageToItk< itk::Image< float, 4 > > CasterType; - CasterType::Pointer caster = CasterType::New(); - caster->SetInput(image); - caster->Update(); - itk::Image< float, 4 >::Pointer itkImage = caster->GetOutput(); - - typename ConverterType::Pointer converter = ConverterType::New(); - - if (noFlip) - { - converter->SetInputImage(itkImage); - } - else - { - std::cout << "Flipping image"; - itk::FixedArray flipAxes; - flipAxes[0] = true; - flipAxes[1] = true; - flipAxes[2] = false; - flipAxes[3] = false; - itk::FlipImageFilter< itk::Image< float, 4 > >::Pointer flipper = itk::FlipImageFilter< itk::Image< float, 4 > >::New(); - flipper->SetInput(itkImage); - flipper->SetFlipAxes(flipAxes); - flipper->Update(); - itk::Image< float, 4 >::Pointer flipped = flipper->GetOutput(); - itk::Matrix< double,4,4 > m = itkImage->GetDirection(); m[0][0] *= -1; m[1][1] *= -1; - flipped->SetDirection(m); - - itk::Point< float, 4 > o = itkImage->GetOrigin(); - o[0] -= (flipped->GetLargestPossibleRegion().GetSize(0)-1); - o[1] -= (flipped->GetLargestPossibleRegion().GetSize(1)-1); - flipped->SetOrigin(o); - converter->SetInputImage(flipped); - } - - std::cout << "Starting conversion"; - switch (toolkitConvention) - { - case 1: - converter->SetToolkit(ConverterType::FSL); - filter->SetToolkit(MaximaExtractionFilterType::FSL); - break; - case 2: - converter->SetToolkit(ConverterType::MRTRIX); - filter->SetToolkit(MaximaExtractionFilterType::MRTRIX); - break; - default: - converter->SetToolkit(ConverterType::FSL); - filter->SetToolkit(MaximaExtractionFilterType::FSL); - break; - } - converter->GenerateData(); - filter->SetInput(converter->GetCoefficientImage()); - } - else - { - try{ - typedef mitk::ImageToItk< typename MaximaExtractionFilterType::CoefficientImageType > CasterType; - typename CasterType::Pointer caster = CasterType::New(); - caster->SetInput(image); - caster->Update(); - filter->SetInput(caster->GetOutput()); - } - catch(...) - { - std::cout << "wrong image type"; - return EXIT_FAILURE; - } - } - - filter->SetMaxNumPeaks(numPeaks); - filter->SetPeakThreshold(peakThres); - filter->SetAbsolutePeakThreshold(absPeakThres); - filter->SetAngularThreshold(1); - filter->SetClusteringThreshold(clusterThres); - filter->SetFlipX(flipX); - filter->SetFlipY(flipY); - filter->SetFlipZ(flipZ); - - switch (normalization) - { - case 0: - filter->SetNormalizationMethod(MaximaExtractionFilterType::NO_NORM); - break; - case 1: - filter->SetNormalizationMethod(MaximaExtractionFilterType::MAX_VEC_NORM); - break; - case 2: - filter->SetNormalizationMethod(MaximaExtractionFilterType::SINGLE_VEC_NORM); - break; - } - - std::cout << "Starting extraction"; - filter->Update(); - - // write direction image - { - typename MaximaExtractionFilterType::PeakImageType::Pointer itkImg = filter->GetPeakImage(); - std::string outfilename = outRoot; - outfilename.append("_PEAKS.nrrd"); - - typedef itk::ImageFileWriter< typename MaximaExtractionFilterType::PeakImageType > WriterType; - typename WriterType::Pointer writer = WriterType::New(); - writer->SetFileName(outfilename); - writer->SetInput(itkImg); - writer->Update(); - } - - // write num directions image - { - ItkUcharImgType::Pointer numDirImage = filter->GetNumDirectionsImage(); - - if (itkMaskImage.IsNotNull()) - { - numDirImage->SetDirection(itkMaskImage->GetDirection()); - numDirImage->SetOrigin(itkMaskImage->GetOrigin()); - } - - std::string outfilename = outRoot.c_str(); - outfilename.append("_NUM_PEAKS.nrrd"); - typedef itk::ImageFileWriter< ItkUcharImgType > WriterType; - WriterType::Pointer writer = WriterType::New(); - writer->SetFileName(outfilename); - writer->SetInput(numDirImage); - writer->Update(); - } + try{ + itkMaskImage = ItkUcharImgType::New(); + mitk::CastToItkImage(mask, itkMaskImage); + peak_extraction_filter->SetMaskImage(itkMaskImage); + } + catch(...) + { + + } } - catch (itk::ExceptionObject e) + + if (toolkitConvention>0) { - std::cout << e; - return EXIT_FAILURE; + std::cout << "Converting coefficient image to MITK format"; + typedef itk::ShCoefficientImageImporter< float, shOrder > ConverterType; + typedef mitk::ImageToItk< itk::Image< float, 4 > > CasterType; + CasterType::Pointer caster = CasterType::New(); + caster->SetInput(image); + caster->Update(); + itk::Image< float, 4 >::Pointer itkImage = caster->GetOutput(); + + typename ConverterType::Pointer converter = ConverterType::New(); + + if (noFlip) + { + converter->SetInputImage(itkImage); + } + else + { + std::cout << "Flipping image"; + itk::FixedArray flipAxes; + flipAxes[0] = true; + flipAxes[1] = true; + flipAxes[2] = false; + flipAxes[3] = false; + itk::FlipImageFilter< itk::Image< float, 4 > >::Pointer flipper = itk::FlipImageFilter< itk::Image< float, 4 > >::New(); + flipper->SetInput(itkImage); + flipper->SetFlipAxes(flipAxes); + flipper->Update(); + itk::Image< float, 4 >::Pointer flipped = flipper->GetOutput(); + itk::Matrix< double,4,4 > m = itkImage->GetDirection(); m[0][0] *= -1; m[1][1] *= -1; + flipped->SetDirection(m); + + itk::Point< float, 4 > o = itkImage->GetOrigin(); + o[0] -= (flipped->GetLargestPossibleRegion().GetSize(0)-1); + o[1] -= (flipped->GetLargestPossibleRegion().GetSize(1)-1); + flipped->SetOrigin(o); + converter->SetInputImage(flipped); + } + + std::cout << "Starting conversion"; + switch (toolkitConvention) + { + case 1: + peak_extraction_filter->SetToolkit(MaximaExtractionFilterType::FSL); + break; + case 2: + peak_extraction_filter->SetToolkit(MaximaExtractionFilterType::MRTRIX); + break; + default: + peak_extraction_filter->SetToolkit(MaximaExtractionFilterType::FSL); + break; + } + converter->GenerateData(); + peak_extraction_filter->SetInput(converter->GetCoefficientImage()); } - catch (std::exception e) + else { - std::cout << e.what(); + try{ + typedef mitk::ImageToItk< typename MaximaExtractionFilterType::CoefficientImageType > CasterType; + typename CasterType::Pointer caster = CasterType::New(); + caster->SetInput(image); + caster->Update(); + peak_extraction_filter->SetInput(caster->GetOutput()); + } + catch(...) + { + std::cout << "wrong image type"; return EXIT_FAILURE; + } } - catch (...) + + peak_extraction_filter->SetMaxNumPeaks(numPeaks); + peak_extraction_filter->SetPeakThreshold(peakThres); + peak_extraction_filter->SetAbsolutePeakThreshold(absPeakThres); + peak_extraction_filter->SetAngularThreshold(1); + peak_extraction_filter->SetClusteringThreshold(clusterThres); + peak_extraction_filter->SetFlipX(flipX); + peak_extraction_filter->SetFlipY(flipY); + peak_extraction_filter->SetFlipZ(flipZ); + + switch (normalization) { - std::cout << "ERROR!?!"; - return EXIT_FAILURE; + case 0: + peak_extraction_filter->SetNormalizationMethod(MaximaExtractionFilterType::NO_NORM); + break; + case 1: + peak_extraction_filter->SetNormalizationMethod(MaximaExtractionFilterType::MAX_VEC_NORM); + break; + case 2: + peak_extraction_filter->SetNormalizationMethod(MaximaExtractionFilterType::SINGLE_VEC_NORM); + break; + } + + std::cout << "Starting extraction"; + peak_extraction_filter->Update(); + + // write direction image + { + typename MaximaExtractionFilterType::PeakImageType::Pointer itkImg = peak_extraction_filter->GetPeakImage(); + std::string outfilename = outRoot; + outfilename.append("_PEAKS.nrrd"); + + typedef itk::ImageFileWriter< typename MaximaExtractionFilterType::PeakImageType > WriterType; + typename WriterType::Pointer writer = WriterType::New(); + writer->SetFileName(outfilename); + writer->SetInput(itkImg); + writer->Update(); + } + + // write num directions image + { + ItkUcharImgType::Pointer numDirImage = peak_extraction_filter->GetNumDirectionsImage(); + + if (itkMaskImage.IsNotNull()) + { + numDirImage->SetDirection(itkMaskImage->GetDirection()); + numDirImage->SetOrigin(itkMaskImage->GetOrigin()); + } + + std::string outfilename = outRoot.c_str(); + outfilename.append("_NUM_PEAKS.nrrd"); + typedef itk::ImageFileWriter< ItkUcharImgType > WriterType; + WriterType::Pointer writer = WriterType::New(); + writer->SetFileName(outfilename); + writer->SetInput(numDirImage); + writer->Update(); } - return EXIT_SUCCESS; + } + 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; } /*! \brief Extract maxima in the input spherical harmonics image. */ int main(int argc, char* argv[]) { - mitkCommandLineParser parser; - parser.setArgumentPrefix("--", "-"); - parser.addArgument("image", "i", mitkCommandLineParser::InputFile, "Input image", "sh coefficient image", us::Any(), false); - parser.addArgument("shOrder", "sh", mitkCommandLineParser::Int, "Spherical harmonics order", "spherical harmonics order"); - parser.addArgument("outroot", "o", mitkCommandLineParser::OutputDirectory, "Output directory", "output root", us::Any(), false); - parser.addArgument("mask", "m", mitkCommandLineParser::InputFile, "Mask", "mask image"); - parser.addArgument("normalization", "n", mitkCommandLineParser::Int, "Normalization", "0=no norm, 1=max norm, 2=single vec norm", 1, true); - parser.addArgument("numpeaks", "p", mitkCommandLineParser::Int, "Max. number of peaks", "maximum number of extracted peaks", 2, true); - parser.addArgument("peakthres", "r", mitkCommandLineParser::Float, "Peak threshold", "peak threshold relative to largest peak", 0.4, true); - parser.addArgument("abspeakthres", "a", mitkCommandLineParser::Float, "Absolute peak threshold", "absolute peak threshold weighted with local GFA value", 0.06, true); - parser.addArgument("shConvention", "s", mitkCommandLineParser::String, "Use specified SH-basis", "use specified SH-basis (MITK, FSL, MRtrix)", std::string("MITK"), true); - parser.addArgument("noFlip", "f", mitkCommandLineParser::Bool, "No flip", "do not flip input image to match MITK coordinate convention"); - - parser.setCategory("Preprocessing Tools"); - parser.setTitle("Peak Extraction"); - parser.setDescription("Extract maxima in the input spherical harmonics image."); - parser.setContributor("MIC"); - - std::map parsedArgs = parser.parseArguments(argc, argv); - if (parsedArgs.size()==0) - return EXIT_FAILURE; - + mitkCommandLineParser parser; + parser.setArgumentPrefix("--", "-"); + parser.addArgument("image", "i", mitkCommandLineParser::InputFile, "Input image", "sh coefficient image", us::Any(), false); + parser.addArgument("shOrder", "sh", mitkCommandLineParser::Int, "Spherical harmonics order", "spherical harmonics order"); + parser.addArgument("outroot", "o", mitkCommandLineParser::OutputDirectory, "Output directory", "output root", us::Any(), false); + parser.addArgument("mask", "m", mitkCommandLineParser::InputFile, "Mask", "mask image"); + parser.addArgument("normalization", "n", mitkCommandLineParser::Int, "Normalization", "0=no norm, 1=max norm, 2=single vec norm", 1, true); + parser.addArgument("numpeaks", "p", mitkCommandLineParser::Int, "Max. number of peaks", "maximum number of extracted peaks", 2, true); + parser.addArgument("peakthres", "r", mitkCommandLineParser::Float, "Peak threshold", "peak threshold relative to largest peak", 0.4, true); + parser.addArgument("abspeakthres", "a", mitkCommandLineParser::Float, "Absolute peak threshold", "absolute peak threshold weighted with local GFA value", 0.06, true); + parser.addArgument("shConvention", "s", mitkCommandLineParser::String, "Use specified SH-basis", "use specified SH-basis (MITK, FSL, MRtrix)", std::string("MITK"), true); + parser.addArgument("noFlip", "f", mitkCommandLineParser::Bool, "No flip", "do not flip input image to match MITK coordinate convention"); + + parser.setCategory("Preprocessing Tools"); + parser.setTitle("Peak Extraction"); + parser.setDescription("Extract maxima in the input spherical harmonics image."); + parser.setContributor("MIC"); + + std::map parsedArgs = parser.parseArguments(argc, argv); + if (parsedArgs.size()==0) + return EXIT_FAILURE; - int shOrder = -1; - if (parsedArgs.count("shOrder")) - shOrder = us::any_cast(parsedArgs["shOrder"]); - switch (shOrder) - { - case 4: - return StartPeakExtraction<4>(argc, argv); - case 6: - return StartPeakExtraction<6>(argc, argv); - case 8: - return StartPeakExtraction<8>(argc, argv); - case 10: - return StartPeakExtraction<10>(argc, argv); - case 12: - return StartPeakExtraction<12>(argc, argv); - } - return EXIT_FAILURE; + int shOrder = -1; + if (parsedArgs.count("shOrder")) + shOrder = us::any_cast(parsedArgs["shOrder"]); + + switch (shOrder) + { + case 4: + return StartPeakExtraction<4>(argc, argv); + case 6: + return StartPeakExtraction<6>(argc, argv); + case 8: + return StartPeakExtraction<8>(argc, argv); + case 10: + return StartPeakExtraction<10>(argc, argv); + case 12: + return StartPeakExtraction<12>(argc, argv); + } + return EXIT_FAILURE; } diff --git a/Modules/DiffusionImaging/FiberTracking/cmdapps/Tractography/GlobalTractography.cpp b/Modules/DiffusionImaging/FiberTracking/cmdapps/Tractography/GlobalTractography.cpp index f6c7a11c7e..e65cea49b6 100755 --- a/Modules/DiffusionImaging/FiberTracking/cmdapps/Tractography/GlobalTractography.cpp +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/Tractography/GlobalTractography.cpp @@ -1,240 +1,170 @@ /*=================================================================== 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 "mitkCommandLineParser.h" #include #include #include +#include +#include template -typename itk::ShCoefficientImageImporter< float, shOrder >::OdfImageType::Pointer TemplatedConvertShCoeffs(mitk::Image* mitkImg, int toolkit, bool noFlip = false) +typename itk::ShCoefficientImageImporter< float, shOrder >::OdfImageType::Pointer TemplatedConvertShCoeffs(mitk::Image::Pointer mitkImg) { - typedef itk::ShCoefficientImageImporter< float, shOrder > FilterType; - typedef mitk::ImageToItk< itk::Image< float, 4 > > CasterType; - CasterType::Pointer caster = CasterType::New(); - caster->SetInput(mitkImg); - caster->Update(); - itk::Image< float, 4 >::Pointer itkImage = caster->GetOutput(); - typename FilterType::Pointer filter = FilterType::New(); - - if (noFlip) - { - filter->SetInputImage(itkImage); - } - else - { - std::cout << "Flipping image"; - itk::FixedArray flipAxes; - flipAxes[0] = true; - flipAxes[1] = true; - flipAxes[2] = false; - flipAxes[3] = false; - itk::FlipImageFilter< itk::Image< float, 4 > >::Pointer flipper = itk::FlipImageFilter< itk::Image< float, 4 > >::New(); - flipper->SetInput(itkImage); - flipper->SetFlipAxes(flipAxes); - flipper->Update(); - itk::Image< float, 4 >::Pointer flipped = flipper->GetOutput(); - itk::Matrix< double,4,4 > m = itkImage->GetDirection(); m[0][0] *= -1; m[1][1] *= -1; - flipped->SetDirection(m); - - itk::Point< float, 4 > o = itkImage->GetOrigin(); - o[0] -= (flipped->GetLargestPossibleRegion().GetSize(0)-1); - o[1] -= (flipped->GetLargestPossibleRegion().GetSize(1)-1); - flipped->SetOrigin(o); - filter->SetInputImage(flipped); - } + typedef itk::ShToOdfImageFilter< float, shOrder > ShConverterType; - switch (toolkit) - { - case 0: - filter->SetToolkit(FilterType::FSL); - break; - case 1: - filter->SetToolkit(FilterType::MRTRIX); - break; - default: - filter->SetToolkit(FilterType::FSL); - } - filter->GenerateData(); - return filter->GetOdfImage(); + typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New(); + mitk::CastToItkImage(mitkImg, itkvol); + + typename ShConverterType::Pointer converter = ShConverterType::New(); + converter->SetInput(itkvol); + converter->Update(); + + return converter->GetOutput(); } /*! \brief Perform global fiber tractography (Gibbs tractography) */ int main(int argc, char* argv[]) { - mitkCommandLineParser parser; - - parser.setTitle("Gibbs Tracking"); - parser.setCategory("Fiber Tracking and Processing Methods"); - parser.setDescription("Perform global fiber tractography (Gibbs tractography)"); - parser.setContributor("MIC"); - - parser.setArgumentPrefix("--", "-"); - parser.addArgument("input", "i", mitkCommandLineParser::InputFile, "Input:", "input image (tensor, ODF or FSL/MRTrix SH-coefficient image)", us::Any(), false); - parser.addArgument("parameters", "p", mitkCommandLineParser::InputFile, "Parameters:", "parameter file (.gtp)", us::Any(), false); - parser.addArgument("mask", "m", mitkCommandLineParser::InputFile, "Mask:", "binary mask image"); - parser.addArgument("shConvention", "s", mitkCommandLineParser::String, "SH coefficient:", "sh coefficient convention (FSL, MRtrix)", std::string("FSL"), true); - parser.addArgument("outFile", "o", mitkCommandLineParser::OutputFile, "Output:", "output fiber bundle (.fib)", us::Any(), false); - parser.addArgument("noFlip", "f", mitkCommandLineParser::Bool, "No flip:", "do not flip input image to match MITK coordinate convention"); - - std::map parsedArgs = parser.parseArguments(argc, argv); - if (parsedArgs.size()==0) - return EXIT_FAILURE; - - std::string inFileName = us::any_cast(parsedArgs["input"]); - std::string paramFileName = us::any_cast(parsedArgs["parameters"]); - std::string outFileName = us::any_cast(parsedArgs["outFile"]); - - bool noFlip = false; - if (parsedArgs.count("noFlip")) - noFlip = us::any_cast(parsedArgs["noFlip"]); - - try + mitkCommandLineParser parser; + + parser.setTitle("Gibbs Tracking"); + parser.setCategory("Fiber Tracking and Processing Methods"); + parser.setDescription("Perform global fiber tractography (Gibbs tractography)"); + parser.setContributor("MIC"); + + parser.setArgumentPrefix("--", "-"); + parser.addArgument("input", "i", mitkCommandLineParser::InputFile, "Input:", "input image (tensor, ODF or SH-coefficient image)", us::Any(), false); + parser.addArgument("parameters", "p", mitkCommandLineParser::InputFile, "Parameters:", "parameter file (.gtp)", us::Any(), false); + parser.addArgument("mask", "m", mitkCommandLineParser::InputFile, "Mask:", "binary mask image"); + parser.addArgument("outFile", "o", mitkCommandLineParser::OutputFile, "Output:", "output fiber bundle (.fib)", us::Any(), false); + + std::map parsedArgs = parser.parseArguments(argc, argv); + if (parsedArgs.size()==0) + return EXIT_FAILURE; + + std::string inFileName = us::any_cast(parsedArgs["input"]); + std::string paramFileName = us::any_cast(parsedArgs["parameters"]); + std::string outFileName = us::any_cast(parsedArgs["outFile"]); + + try + { + // instantiate gibbs tracker + typedef itk::Vector OdfVectorType; + typedef itk::Image ItkOdfImageType; + typedef itk::GibbsTrackingFilter GibbsTrackingFilterType; + GibbsTrackingFilterType::Pointer gibbsTracker = GibbsTrackingFilterType::New(); + + // load input image + mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor({"SH Image"}, {}); + mitk::Image::Pointer mitkImage = dynamic_cast(mitk::IOUtil::Load(inFileName, &functor)[0].GetPointer()); + + // try to cast to Odf image + if( dynamic_cast(mitkImage.GetPointer()) ) { - // instantiate gibbs tracker - typedef itk::Vector OdfVectorType; - typedef itk::Image ItkOdfImageType; - typedef itk::GibbsTrackingFilter GibbsTrackingFilterType; - GibbsTrackingFilterType::Pointer gibbsTracker = GibbsTrackingFilterType::New(); - - // load input image - mitk::Image::Pointer mitkImage = dynamic_cast(mitk::IOUtil::Load(inFileName)[0].GetPointer()); - - // try to cast to Odf image - if( boost::algorithm::ends_with(inFileName, ".odf") || boost::algorithm::ends_with(inFileName, ".qbi") ) - { - std::cout << "Loading Odf image ..."; - mitk::OdfImage::Pointer mitkOdfImage = dynamic_cast(mitkImage.GetPointer()); - ItkOdfImageType::Pointer itk_odf = ItkOdfImageType::New(); - mitk::CastToItkImage(mitkOdfImage, itk_odf); - gibbsTracker->SetOdfImage(itk_odf.GetPointer()); - } - else if( boost::algorithm::ends_with(inFileName, ".dti") ) - { - std::cout << "Loading tensor image ..."; - typedef itk::Image< itk::DiffusionTensor3D, 3 > ItkTensorImage; - mitk::TensorImage::Pointer mitkTensorImage = dynamic_cast(mitkImage.GetPointer()); - ItkTensorImage::Pointer itk_dti = ItkTensorImage::New(); - mitk::CastToItkImage(mitkTensorImage, itk_dti); - gibbsTracker->SetTensorImage(itk_dti); - } - else if ( boost::algorithm::ends_with(inFileName, ".nii") ) - { - std::cout << "Loading sh-coefficient image ..."; - int nrCoeffs = mitkImage->GetLargestPossibleRegion().GetSize()[3]; - int c=3, d=2-2*nrCoeffs; - double D = c*c-4*d; - int shOrder = 0; - if (D>0) - { - shOrder = (-c+sqrt(D))/2.0; - if (shOrder<0) - shOrder = (-c-sqrt(D))/2.0; - } - else if (D==0) - shOrder = -c/2.0; - - std::cout << "using SH-order " << shOrder; - - int toolkitConvention = 0; - - if (parsedArgs.count("shConvention")) - { - std::string convention = us::any_cast(parsedArgs["shConvention"]).c_str(); - - if ( boost::algorithm::equals(convention, "MRtrix") ) - { - toolkitConvention = 1; - std::cout << "Using MRtrix style sh-coefficient convention"; - } - else - std::cout << "Using FSL style sh-coefficient convention"; - } - else - std::cout << "Using FSL style sh-coefficient convention"; - - switch (shOrder) - { - case 4: - gibbsTracker->SetOdfImage(TemplatedConvertShCoeffs<4>(mitkImage, toolkitConvention, noFlip)); - break; - case 6: - gibbsTracker->SetOdfImage(TemplatedConvertShCoeffs<6>(mitkImage, toolkitConvention, noFlip)); - break; - case 8: - gibbsTracker->SetOdfImage(TemplatedConvertShCoeffs<8>(mitkImage, toolkitConvention, noFlip)); - break; - case 10: - gibbsTracker->SetOdfImage(TemplatedConvertShCoeffs<10>(mitkImage, toolkitConvention, noFlip)); - break; - case 12: - gibbsTracker->SetOdfImage(TemplatedConvertShCoeffs<12>(mitkImage, toolkitConvention, noFlip)); - break; - default: - std::cout << "SH-order " << shOrder << " not supported"; - } - } - else - return EXIT_FAILURE; - - // global tracking - if (parsedArgs.count("mask")) - { - typedef itk::Image MaskImgType; - mitk::Image::Pointer mitkMaskImage = dynamic_cast(mitk::IOUtil::Load(us::any_cast(parsedArgs["mask"]))[0].GetPointer()); - MaskImgType::Pointer itk_mask = MaskImgType::New(); - mitk::CastToItkImage(mitkMaskImage, itk_mask); - gibbsTracker->SetMaskImage(itk_mask); - } - - gibbsTracker->SetDuplicateImage(false); - gibbsTracker->SetLoadParameterFile( paramFileName ); -// gibbsTracker->SetLutPath( "" ); - gibbsTracker->Update(); - - mitk::FiberBundle::Pointer mitkFiberBundle = mitk::FiberBundle::New(gibbsTracker->GetFiberBundle()); - mitkFiberBundle->SetReferenceGeometry(mitkImage->GetGeometry()); - - mitk::IOUtil::Save(mitkFiberBundle, outFileName ); + mitk::OdfImage::Pointer mitkOdfImage = dynamic_cast(mitkImage.GetPointer()); + ItkOdfImageType::Pointer itk_odf = ItkOdfImageType::New(); + mitk::CastToItkImage(mitkOdfImage, itk_odf); + gibbsTracker->SetOdfImage(itk_odf.GetPointer()); } - catch (itk::ExceptionObject e) + else if( dynamic_cast(mitkImage.GetPointer()) ) { - std::cout << e; - return EXIT_FAILURE; + typedef itk::Image< itk::DiffusionTensor3D, 3 > ItkTensorImage; + mitk::TensorImage::Pointer mitkTensorImage = dynamic_cast(mitkImage.GetPointer()); + ItkTensorImage::Pointer itk_dti = ItkTensorImage::New(); + mitk::CastToItkImage(mitkTensorImage, itk_dti); + gibbsTracker->SetTensorImage(itk_dti); } - catch (std::exception e) + else if ( dynamic_cast(mitkImage.GetPointer()) ) { - std::cout << e.what(); - return EXIT_FAILURE; + mitk::ShImage::Pointer shImage = dynamic_cast(mitkImage.GetPointer()); + + switch (shImage->ShOrder()) + { + case 2: + gibbsTracker->SetOdfImage(TemplatedConvertShCoeffs<2>(mitkImage)); + break; + case 4: + gibbsTracker->SetOdfImage(TemplatedConvertShCoeffs<4>(mitkImage)); + break; + case 6: + gibbsTracker->SetOdfImage(TemplatedConvertShCoeffs<6>(mitkImage)); + break; + case 8: + gibbsTracker->SetOdfImage(TemplatedConvertShCoeffs<8>(mitkImage)); + break; + case 10: + gibbsTracker->SetOdfImage(TemplatedConvertShCoeffs<10>(mitkImage)); + break; + case 12: + gibbsTracker->SetOdfImage(TemplatedConvertShCoeffs<12>(mitkImage)); + break; + default: + std::cout << "SH-order " << shImage->ShOrder() << " not supported"; + } } - catch (...) + else + return EXIT_FAILURE; + + // global tracking + if (parsedArgs.count("mask")) { - std::cout << "ERROR!?!"; - return EXIT_FAILURE; + typedef itk::Image MaskImgType; + mitk::Image::Pointer mitkMaskImage = dynamic_cast(mitk::IOUtil::Load(us::any_cast(parsedArgs["mask"]))[0].GetPointer()); + MaskImgType::Pointer itk_mask = MaskImgType::New(); + mitk::CastToItkImage(mitkMaskImage, itk_mask); + gibbsTracker->SetMaskImage(itk_mask); } - return EXIT_SUCCESS; + + gibbsTracker->SetDuplicateImage(false); + gibbsTracker->SetLoadParameterFile( paramFileName ); + // gibbsTracker->SetLutPath( "" ); + gibbsTracker->Update(); + + mitk::FiberBundle::Pointer mitkFiberBundle = mitk::FiberBundle::New(gibbsTracker->GetFiberBundle()); + mitkFiberBundle->SetReferenceGeometry(mitkImage->GetGeometry()); + + mitk::IOUtil::Save(mitkFiberBundle, outFileName ); + } + catch (itk::ExceptionObject e) + { + std::cout << e; + return EXIT_FAILURE; + } + catch (std::exception e) + { + std::cout << e.what(); + return EXIT_FAILURE; + } + catch (...) + { + std::cout << "ERROR!?!"; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.odfpeaks/src/internal/QmitkOdfMaximaExtractionView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.odfpeaks/src/internal/QmitkOdfMaximaExtractionView.cpp index ace95e55ae..e7a473ad33 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.odfpeaks/src/internal/QmitkOdfMaximaExtractionView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.odfpeaks/src/internal/QmitkOdfMaximaExtractionView.cpp @@ -1,424 +1,382 @@ /*=================================================================== 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. ===================================================================*/ //misc #define _USE_MATH_DEFINES #include #include // Blueberry #include #include // Qmitk #include "QmitkOdfMaximaExtractionView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include // Qt #include const std::string QmitkOdfMaximaExtractionView::VIEW_ID = "org.mitk.views.odfmaximaextractionview"; using namespace mitk; QmitkOdfMaximaExtractionView::QmitkOdfMaximaExtractionView() : m_Controls(nullptr) { } // Destructor QmitkOdfMaximaExtractionView::~QmitkOdfMaximaExtractionView() { } void QmitkOdfMaximaExtractionView::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::QmitkOdfMaximaExtractionViewControls; m_Controls->setupUi(parent); connect((QObject*)m_Controls->m_StartPeakExtractionButton, SIGNAL(clicked()), (QObject*) this, SLOT(StartPeakExtraction())); connect((QObject*)m_Controls->m_ImportShCoeffs, SIGNAL(clicked()), (QObject*) this, SLOT(ConvertShCoeffs())); m_Controls->m_MaskBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_ImageBox->SetDataStorage(this->GetDataStorage()); mitk::TNodePredicateDataType::Pointer isMitkImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateNot::Pointer isDwi = mitk::NodePredicateNot::New(mitk::NodePredicateIsDWI::New()); mitk::NodePredicateNot::Pointer isOdf = mitk::NodePredicateNot::New(mitk::NodePredicateDataType::New("OdfImage")); mitk::NodePredicateAnd::Pointer unwanted = mitk::NodePredicateAnd::New(isOdf, isDwi); mitk::NodePredicateDimension::Pointer dim3 = mitk::NodePredicateDimension::New(3); mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); m_Controls->m_MaskBox->SetPredicate(mitk::NodePredicateAnd::New(mitk::NodePredicateAnd::New(unwanted, dim3), isBinaryPredicate)); m_Controls->m_ImageBox->SetPredicate(mitk::NodePredicateAnd::New(mitk::NodePredicateAnd::New(unwanted, isMitkImage), mitk::NodePredicateNot::New(isBinaryPredicate))); m_Controls->m_MaskBox->SetZeroEntryText("--"); - m_Controls->m_ImageBox->SetZeroEntryText("--"); connect( (QObject*)(m_Controls->m_ImageBox), SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnImageSelectionChanged()) ); m_Controls->m_StartPeakExtractionButton->setVisible(false); m_Controls->m_ImportShCoeffs->setVisible(false); } } void QmitkOdfMaximaExtractionView::SetFocus() { } void QmitkOdfMaximaExtractionView::StartPeakExtraction() { if (dynamic_cast(m_Controls->m_ImageBox->GetSelectedNode()->GetData()) != nullptr) { StartTensorPeakExtraction(dynamic_cast(m_Controls->m_ImageBox->GetSelectedNode()->GetData())); } else { StartMaximaExtraction(dynamic_cast(m_Controls->m_ImageBox->GetSelectedNode()->GetData())); } } template void QmitkOdfMaximaExtractionView::TemplatedConvertShCoeffs(mitk::Image* mitkImg) { typedef itk::ShCoefficientImageImporter< float, shOrder > FilterType; typedef mitk::ImageToItk< itk::Image< float, 4 > > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(mitkImg); caster->Update(); typename FilterType::Pointer filter = FilterType::New(); - - switch (m_Controls->m_ToolkitBox->currentIndex()) - { - case 0: - filter->SetToolkit(FilterType::FSL); - break; - case 1: - filter->SetToolkit(FilterType::MRTRIX); - break; - default: - filter->SetToolkit(FilterType::FSL); - } - filter->SetInputImage(caster->GetOutput()); filter->GenerateData(); - typename FilterType::OdfImageType::Pointer itkodf = filter->GetOdfImage(); typename FilterType::CoefficientImageType::Pointer itkCi = filter->GetCoefficientImage(); { - mitk::Image::Pointer img = mitk::Image::New(); + mitk::Image::Pointer img = dynamic_cast(mitk::ShImage::New().GetPointer()); img->InitializeByItk(itkCi.GetPointer()); img->SetVolume(itkCi->GetBufferPointer()); DataNode::Pointer node = DataNode::New(); node->SetData(img); QString name(m_Controls->m_ImageBox->GetSelectedNode()->GetName().c_str()); - name += "_ShCoefficientImage_Imported"; - node->SetName(name.toStdString().c_str()); - - GetDataStorage()->Add(node, m_Controls->m_ImageBox->GetSelectedNode()); - } - - { - mitk::OdfImage::Pointer img = mitk::OdfImage::New(); - img->InitializeByItk(itkodf.GetPointer()); - img->SetVolume(itkodf->GetBufferPointer()); - DataNode::Pointer node = DataNode::New(); - node->SetData(img); - - QString name(m_Controls->m_ImageBox->GetSelectedNode()->GetName().c_str()); - name += "_OdfImage_Imported"; + name += "_ShImage_Imported"; node->SetName(name.toStdString().c_str()); GetDataStorage()->Add(node, m_Controls->m_ImageBox->GetSelectedNode()); } } void QmitkOdfMaximaExtractionView::ConvertShCoeffs() { if (m_Controls->m_ImageBox->GetSelectedNode().IsNull()) return; Image::Pointer mitkImg = dynamic_cast(m_Controls->m_ImageBox->GetSelectedNode()->GetData()); if (mitkImg->GetDimension() != 4 && mitkImg->GetLargestPossibleRegion().GetSize()[3]<6) { MITK_INFO << "wrong image type (need 4 dimensions)"; return; } int nrCoeffs = mitkImg->GetLargestPossibleRegion().GetSize()[3]; - - // // solve bx² + cx + d = 0 = shOrder² + 2*shOrder + 2-2*neededCoeffs; - // int c = 3, d = 2 - 2 * nrCoeffs; - // double D = c*c - 4 * d; - // int shOrder; - // if (D>0) - // { - // shOrder = (-c + sqrt(D)) / 2.0; - // if (shOrder<0) - // shOrder = (-c - sqrt(D)) / 2.0; - // } - // else if (D == 0) - // shOrder = -c / 2.0; - switch (nrCoeffs) { case 6: TemplatedConvertShCoeffs<2>(mitkImg); break; case 15: TemplatedConvertShCoeffs<4>(mitkImg); break; case 28: TemplatedConvertShCoeffs<6>(mitkImg); break; case 45: TemplatedConvertShCoeffs<8>(mitkImg); break; case 66: TemplatedConvertShCoeffs<10>(mitkImg); break; case 91: TemplatedConvertShCoeffs<12>(mitkImg); break; default : QMessageBox::warning(nullptr, "Error", "Only spherical harmonics orders 2-12 are supported.", QMessageBox::Ok); } } void QmitkOdfMaximaExtractionView::StartTensorPeakExtraction(mitk::TensorImage* img) { typedef itk::DiffusionTensorPrincipalDirectionImageFilter< float > MaximaExtractionFilterType; MaximaExtractionFilterType::Pointer filter = MaximaExtractionFilterType::New(); filter->SetUsePolarCoordinates(false); mitk::BaseGeometry::Pointer geometry; try{ ItkTensorImage::Pointer itkImage = ItkTensorImage::New(); CastToItkImage(img, itkImage); filter->SetInput(itkImage); geometry = img->GetGeometry(); } catch (itk::ExceptionObject &e) { MITK_INFO << "wrong image type: " << e.what(); QMessageBox::warning(nullptr, "Wrong pixel type", "Could not perform Tensor Principal Direction Extraction due to Image has wrong pixel type.", QMessageBox::Ok); return; } if (m_Controls->m_MaskBox->GetSelectedNode().IsNotNull()) { ItkUcharImgType::Pointer itkMaskImage = ItkUcharImgType::New(); Image::Pointer mitkMaskImg = dynamic_cast(m_Controls->m_MaskBox->GetSelectedNode()->GetData()); CastToItkImage(mitkMaskImg, itkMaskImage); filter->SetMaskImage(itkMaskImage); } if (m_Controls->m_NormalizationBox->currentIndex() == 0) filter->SetNormalizeVectors(false); filter->SetFaThreshold(m_Controls->m_AbsoluteThresholdBox->value()); filter->Update(); MaximaExtractionFilterType::PeakImageType::Pointer itkImg = filter->GetPeakImage(); mitk::Image::Pointer mitkPeakImage = dynamic_cast(PeakImage::New().GetPointer()); CastToMitkImage(itkImg, mitkPeakImage); DataNode::Pointer node = DataNode::New(); node->SetData(mitkPeakImage); QString name(m_Controls->m_ImageBox->GetSelectedNode()->GetName().c_str()); name += "_PrincipalDirection"; node->SetName(name.toStdString().c_str()); GetDataStorage()->Add(node, m_Controls->m_ImageBox->GetSelectedNode()); if (m_Controls->m_OutputNumDirectionsBox->isChecked()) { ItkUcharImgType::Pointer numDirImage = filter->GetOutput(); mitk::Image::Pointer image2 = mitk::Image::New(); image2->InitializeByItk(numDirImage.GetPointer()); image2->SetVolume(numDirImage->GetBufferPointer()); DataNode::Pointer node2 = DataNode::New(); node2->SetData(image2); QString name(m_Controls->m_ImageBox->GetSelectedNode()->GetName().c_str()); name += "_NumDirections"; node2->SetName(name.toStdString().c_str()); GetDataStorage()->Add(node2, m_Controls->m_ImageBox->GetSelectedNode()); } } template void QmitkOdfMaximaExtractionView::StartMaximaExtraction(Image *image) { - typedef itk::FiniteDiffOdfMaximaExtractionFilter< float, shOrder, 20242 > MaximaExtractionFilterType; + typedef itk::FiniteDiffOdfMaximaExtractionFilter< float, shOrder, 10000 > MaximaExtractionFilterType; typename MaximaExtractionFilterType::Pointer filter = MaximaExtractionFilterType::New(); switch (m_Controls->m_ToolkitBox->currentIndex()) { case 0: - filter->SetToolkit(MaximaExtractionFilterType::FSL); + filter->SetToolkit(MaximaExtractionFilterType::MRTRIX); break; case 1: - filter->SetToolkit(MaximaExtractionFilterType::MRTRIX); + filter->SetToolkit(MaximaExtractionFilterType::FSL); break; default: - filter->SetToolkit(MaximaExtractionFilterType::FSL); + filter->SetToolkit(MaximaExtractionFilterType::MRTRIX); } mitk::BaseGeometry::Pointer geometry; try{ typedef ImageToItk< typename MaximaExtractionFilterType::CoefficientImageType > CasterType; typename CasterType::Pointer caster = CasterType::New(); caster->SetInput(image); caster->Update(); filter->SetInput(caster->GetOutput()); geometry = image->GetGeometry(); } catch (itk::ExceptionObject &e) { MITK_INFO << "wrong image type: " << e.what(); QMessageBox::warning(nullptr, "Wrong pixel type", "Could not perform Finite Differences Extraction due to Image has wrong pixel type.", QMessageBox::Ok); return; } filter->SetAngularThreshold(cos((float)m_Controls->m_AngularThreshold->value()*M_PI / 180)); filter->SetClusteringThreshold(cos((float)m_Controls->m_ClusteringAngleBox->value()*M_PI / 180)); filter->SetMaxNumPeaks(m_Controls->m_MaxNumPeaksBox->value()); filter->SetPeakThreshold(m_Controls->m_PeakThresholdBox->value()); filter->SetAbsolutePeakThreshold(m_Controls->m_AbsoluteThresholdBox->value()); if (m_Controls->m_MaskBox->GetSelectedNode().IsNotNull()) { ItkUcharImgType::Pointer itkMaskImage = ItkUcharImgType::New(); Image::Pointer mitkMaskImg = dynamic_cast(m_Controls->m_MaskBox->GetSelectedNode()->GetData()); CastToItkImage(mitkMaskImg, itkMaskImage); filter->SetMaskImage(itkMaskImage); } switch (m_Controls->m_NormalizationBox->currentIndex()) { case 0: filter->SetNormalizationMethod(MaximaExtractionFilterType::NO_NORM); break; case 1: filter->SetNormalizationMethod(MaximaExtractionFilterType::MAX_VEC_NORM); break; case 2: filter->SetNormalizationMethod(MaximaExtractionFilterType::SINGLE_VEC_NORM); break; } filter->Update(); typename MaximaExtractionFilterType::PeakImageType::Pointer itkImg = filter->GetPeakImage(); mitk::Image::Pointer img = dynamic_cast(PeakImage::New().GetPointer()); CastToMitkImage(itkImg, img); DataNode::Pointer node = DataNode::New(); node->SetData(img); QString name(m_Controls->m_ImageBox->GetSelectedNode()->GetName().c_str()); name += "_PEAKS"; node->SetName(name.toStdString().c_str()); GetDataStorage()->Add(node, m_Controls->m_ImageBox->GetSelectedNode()); if (m_Controls->m_OutputNumDirectionsBox->isChecked()) { ItkUcharImgType::Pointer numDirImage = filter->GetNumDirectionsImage(); mitk::Image::Pointer image2 = mitk::Image::New(); CastToMitkImage(numDirImage, image2); DataNode::Pointer node2 = DataNode::New(); node2->SetData(image2); QString name(m_Controls->m_ImageBox->GetSelectedNode()->GetName().c_str()); name += "_NUM_DIRECTIONS"; node2->SetName(name.toStdString().c_str()); GetDataStorage()->Add(node2, m_Controls->m_ImageBox->GetSelectedNode()); } } void QmitkOdfMaximaExtractionView::StartMaximaExtraction(Image* img) { mitk::PixelType pixT = img->GetPixelType(); switch (pixT.GetNumberOfComponents()) { case 6: StartMaximaExtraction<2>(img); break; case 15: StartMaximaExtraction<4>(img); break; case 28: StartMaximaExtraction<6>(img); break; case 45: StartMaximaExtraction<8>(img); break; case 66: StartMaximaExtraction<10>(img); break; case 91: StartMaximaExtraction<12>(img); break; default : QMessageBox::warning(nullptr, "Error", "Only spherical harmonics orders 2-12 are supported.", QMessageBox::Ok); } } void QmitkOdfMaximaExtractionView::OnSelectionChanged(berry::IWorkbenchPart::Pointer , const QList& nodes) { (void) nodes; this->OnImageSelectionChanged(); } void QmitkOdfMaximaExtractionView::OnImageSelectionChanged() { m_Controls->m_StartPeakExtractionButton->setVisible(false); m_Controls->m_ImportShCoeffs->setVisible(false); mitk::DataNode::Pointer node = m_Controls->m_ImageBox->GetSelectedNode(); if (node.IsNull()) return; Image::Pointer img = dynamic_cast(node->GetData()); if (img->GetDimension()==4) m_Controls->m_ImportShCoeffs->setVisible(true); else m_Controls->m_StartPeakExtractionButton->setVisible(true); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.odfpeaks/src/internal/QmitkOdfMaximaExtractionViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.odfpeaks/src/internal/QmitkOdfMaximaExtractionViewControls.ui index 4a404eae71..8ca105cd2b 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.odfpeaks/src/internal/QmitkOdfMaximaExtractionViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.odfpeaks/src/internal/QmitkOdfMaximaExtractionViewControls.ui @@ -1,462 +1,467 @@ QmitkOdfMaximaExtractionViewControls 0 0 397 848 Form 25 QFrame::NoFrame QFrame::Raised 0 0 0 0 true Generate ODF image and MITK compatible SH coefficient from other toolkits. Start SH Coefficient Import true Extract ODF peaks using finite differences on the densely sampled ODF surface. Start Peak Extraction Please Select Input Data 0 0 0 0 Select a tensor image or a SH coefficient image (generate using Q-Ball reconstruction view). ShCoeff/DTI: Mask Image: - + Additional Output QFormLayout::AllNonFixedFieldsGrow 0 0 0 0 Output unsigned char image containing the number of directions per voxel. #Peaks per Voxel false Parameters 0 0 0 0 QFrame::NoFrame QFrame::Raised 0 0 0 0 6 Vector Normalization: <html><head/><body><p>The vector fields are always coorected for image spacing and using the lagest eigenvalue in case of the tensor peak extraction. This is done for visualizytion purposes. The output direction images are not affected.</p></body></html> 1 No Normalization MAX Normalize Single Vec Normalization true QFrame::NoFrame QFrame::Raised 0 0 0 0 6 true Max. Peaks: Relative Threshold: true Peak threshold relative to the largest peak per voxel. 3 0.000000000000000 1.000000000000000 0.100000000000000 0.500000000000000 true Absolute peak threshold (only used for the finite differences method). The value is additionally scaled by 1/GFA. 3 0.000000000000000 1.000000000000000 0.010000000000000 0.030000000000000 true Maximum number of peaks to extract. 1 1000 3 Clustering Angle: Cluster close directions. Define "close" here. 90 30 Absolute Threshold: Angular Threshold: Discard smaller peaks in the defined angle around the maximum peaks that were too far away to be clustered. 0 90 0 - + + + + Qt::Vertical + + + + 20 + 259 + + + + + Spherical Harmonic Convention 0 0 0 0 Define SH coefficient convention (depends on toolkit) 0 - MITK/FSL + MITK/MRtrix - MRtrix + FSL - - - - Qt::Vertical - - - - 20 - 259 - - - - + + QmitkDataStorageComboBox + QComboBox +
QmitkDataStorageComboBox.h
+
QmitkDataStorageComboBoxWithSelectNone QComboBox
QmitkDataStorageComboBoxWithSelectNone.h
diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/src/internal/QmitkQBallReconstructionView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/src/internal/QmitkQBallReconstructionView.cpp index 891554ac48..e64a90ab03 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/src/internal/QmitkQBallReconstructionView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/src/internal/QmitkQBallReconstructionView.cpp @@ -1,825 +1,875 @@ /*=================================================================== 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. ===================================================================*/ //#define MBILOG_ENABLE_DEBUG #include "QmitkQBallReconstructionView.h" // qt includes #include // itk includes #include "itkTimeProbe.h" // mitk includes #include "mitkProgressBar.h" #include "mitkStatusBar.h" #include "mitkNodePredicateDataType.h" #include "QmitkDataStorageComboBox.h" #include "itkDiffusionQballReconstructionImageFilter.h" #include "itkAnalyticalDiffusionQballReconstructionImageFilter.h" #include "itkDiffusionMultiShellQballReconstructionImageFilter.h" #include "itkVectorContainer.h" #include "itkB0ImageExtractionImageFilter.h" #include #include "mitkOdfImage.h" #include "mitkProperties.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkTransferFunction.h" #include "mitkTransferFunctionProperty.h" #include "mitkDataNodeObject.h" #include "mitkOdfNormalizationMethodProperty.h" #include "mitkOdfScaleByProperty.h" #include #include "mitkDiffusionImagingConfigure.h" #include #include "berryIStructuredSelection.h" #include "berryIWorkbenchWindow.h" #include "berryISelectionService.h" - +#include #include +#include +#include const std::string QmitkQBallReconstructionView::VIEW_ID = "org.mitk.views.qballreconstruction"; typedef float TTensorPixelType; const int QmitkQBallReconstructionView::nrconvkernels = 252; struct QbrShellSelection { typedef mitk::DiffusionPropertyHelper::GradientDirectionType GradientDirectionType; typedef mitk::DiffusionPropertyHelper::GradientDirectionsContainerType GradientDirectionContainerType; typedef mitk::DiffusionPropertyHelper::BValueMapType BValueMapType; typedef itk::VectorImage< DiffusionPixelType, 3 > ITKDiffusionImageType; QmitkQBallReconstructionView* m_View; mitk::DataNode * m_Node; std::string m_NodeName; std::vector m_CheckBoxes; QLabel * m_Label; mitk::Image * m_Image; QbrShellSelection(QmitkQBallReconstructionView* view, mitk::DataNode * node) : m_View(view), m_Node(node), m_NodeName(node->GetName()) { m_Image = dynamic_cast (node->GetData()); if(!m_Image) { MITK_ERROR << "QmitkQBallReconstructionView::QbrShellSelection : no image selected"; return; } bool isDiffusionImage( mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(m_Node->GetData())) ); if( !isDiffusionImage ) { MITK_ERROR << "QmitkQBallReconstructionView::QbrShellSelection : selected image contains no diffusion information"; return; } GenerateCheckboxes(); } void GenerateCheckboxes() { BValueMapType origMap = static_cast(m_Image->GetProperty(mitk::DiffusionPropertyHelper::BVALUEMAPPROPERTYNAME.c_str()).GetPointer() )->GetBValueMap(); BValueMapType::iterator itStart = origMap.begin(); itStart++; BValueMapType::iterator itEnd = origMap.end(); m_Label = new QLabel(m_NodeName.c_str()); m_Label->setVisible(true); m_View->m_Controls->m_QBallSelectionBox->layout()->addWidget(m_Label); for(BValueMapType::iterator it = itStart ; it!= itEnd; it++) { QCheckBox * box = new QCheckBox(QString::number(it->first)); m_View->m_Controls->m_QBallSelectionBox->layout()->addWidget(box); box->setChecked(true); box->setCheckable(true); // box->setVisible(true); m_CheckBoxes.push_back(box); } } void SetVisible(bool vis) { foreach(QCheckBox * box, m_CheckBoxes) { box->setVisible(vis); } } BValueMapType GetBValueSelctionMap() { BValueMapType inputMap = static_cast(m_Image->GetProperty(mitk::DiffusionPropertyHelper::BVALUEMAPPROPERTYNAME.c_str()).GetPointer() )->GetBValueMap(); BValueMapType outputMap; unsigned int val = 0; if(inputMap.find(0) == inputMap.end()){ MITK_INFO << "QbrShellSelection: return empty BValueMap from GUI Selection"; return outputMap; }else{ outputMap[val] = inputMap[val]; MITK_INFO << val; } foreach(QCheckBox * box, m_CheckBoxes) { if(box->isChecked()){ val = box->text().toDouble(); outputMap[val] = inputMap[val]; MITK_INFO << val; } } return outputMap; } ~QbrShellSelection() { m_View->m_Controls->m_QBallSelectionBox->layout()->removeWidget(m_Label); delete m_Label; for(std::vector::iterator it = m_CheckBoxes.begin() ; it!= m_CheckBoxes.end(); it++) { m_View->m_Controls->m_QBallSelectionBox->layout()->removeWidget((*it)); delete (*it); } m_CheckBoxes.clear(); } }; QmitkQBallReconstructionView::QmitkQBallReconstructionView() : QmitkAbstractView(), m_Controls(nullptr) { } QmitkQBallReconstructionView::~QmitkQBallReconstructionView() { } void QmitkQBallReconstructionView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkQBallReconstructionViewControls; m_Controls->setupUi(parent); this->CreateConnections(); QStringList items; items << "2" << "4" << "6" << "8" << "10" << "12"; m_Controls->m_QBallReconstructionMaxLLevelComboBox->addItems(items); m_Controls->m_QBallReconstructionMaxLLevelComboBox->setCurrentIndex(1); MethodChoosen(m_Controls->m_QBallReconstructionMethodComboBox->currentIndex()); #ifndef DIFFUSION_IMAGING_EXTENDED m_Controls->m_QBallReconstructionMethodComboBox->removeItem(3); #endif } } void QmitkQBallReconstructionView::SetFocus() { } void QmitkQBallReconstructionView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_ButtonStandard), SIGNAL(clicked()), this, SLOT(ReconstructStandard()) ); connect( (QObject*)(m_Controls->m_QBallReconstructionMethodComboBox), SIGNAL(currentIndexChanged(int)), this, SLOT(MethodChoosen(int)) ); connect( (QObject*)(m_Controls->m_QBallReconstructionThreasholdEdit), SIGNAL(valueChanged(int)), this, SLOT(PreviewThreshold(int)) ); + connect( (QObject*)(m_Controls->m_ConvertButton), SIGNAL(clicked()), this, SLOT(ConvertShImage()) ); m_Controls->m_ImageBox->SetDataStorage(this->GetDataStorage()); mitk::NodePredicateIsDWI::Pointer isDwi = mitk::NodePredicateIsDWI::New(); m_Controls->m_ImageBox->SetPredicate( isDwi ); + m_Controls->m_ShImageBox->SetDataStorage(this->GetDataStorage()); + mitk::TNodePredicateDataType::Pointer isSh = mitk::TNodePredicateDataType::New(); + m_Controls->m_ShImageBox->SetPredicate( isSh ); + connect( (QObject*)(m_Controls->m_ImageBox), SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGui())); + connect( (QObject*)(m_Controls->m_ShImageBox), SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGui())); + + UpdateGui(); + } +} + +template +void QmitkQBallReconstructionView::TemplatedConvertShImage(mitk::ShImage::Pointer mitkImage) +{ + typedef itk::ShToOdfImageFilter< float, ShOrder > ShConverterType; + + typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New(); + mitk::CastToItkImage(mitkImage, itkvol); + + typename ShConverterType::Pointer converter = ShConverterType::New(); + converter->SetInput(itkvol); + converter->Update(); + + mitk::OdfImage::Pointer image = mitk::OdfImage::New(); + image->InitializeByItk( converter->GetOutput() ); + image->SetVolume( converter->GetOutput()->GetBufferPointer() ); + mitk::DataNode::Pointer node=mitk::DataNode::New(); + node->SetData( image ); + node->SetName(m_Controls->m_ShImageBox->GetSelectedNode()->GetName()); + + GetDataStorage()->Add(node, m_Controls->m_ShImageBox->GetSelectedNode()); +} + +void QmitkQBallReconstructionView::ConvertShImage() +{ + if (m_Controls->m_ShImageBox->GetSelectedNode().IsNotNull()) + { + mitk::ShImage::Pointer mitkImg = dynamic_cast(m_Controls->m_ShImageBox->GetSelectedNode()->GetData()); + switch (mitkImg->ShOrder()) + { + case 2: + TemplatedConvertShImage<2>(mitkImg); + break; + case 4: + TemplatedConvertShImage<4>(mitkImg); + break; + case 6: + TemplatedConvertShImage<6>(mitkImg); + break; + case 8: + TemplatedConvertShImage<8>(mitkImg); + break; + case 10: + TemplatedConvertShImage<10>(mitkImg); + break; + case 12: + TemplatedConvertShImage<12>(mitkImg); + break; + default : + QMessageBox::warning(nullptr, "Error", "Only spherical harmonics orders 2-12 are supported.", QMessageBox::Ok); + } } } void QmitkQBallReconstructionView::UpdateGui() { m_Controls->m_ButtonStandard->setEnabled(false); if (m_Controls->m_ImageBox->GetSelectedNode().IsNotNull()) { m_Controls->m_ButtonStandard->setEnabled(true); GenerateShellSelectionUI(m_Controls->m_ImageBox->GetSelectedNode()); } + + m_Controls->m_ConvertButton->setEnabled(m_Controls->m_ShImageBox->GetSelectedNode().IsNotNull()); } void QmitkQBallReconstructionView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& /*nodes*/) { UpdateGui(); } void QmitkQBallReconstructionView::Activated() { } void QmitkQBallReconstructionView::Deactivated() { } void QmitkQBallReconstructionView::Visible() { } void QmitkQBallReconstructionView::Hidden() { } void QmitkQBallReconstructionView::ReconstructStandard() { int index = m_Controls->m_QBallReconstructionMethodComboBox->currentIndex(); #ifndef DIFFUSION_IMAGING_EXTENDED if(index>=3) { index = index + 1; } #endif switch(index) { case 0: { // Numerical Reconstruct(0,0); break; } case 1: { // Standard Reconstruct(1,0); break; } case 2: { // Solid Angle Reconstruct(1,6); break; } case 3: { // Constrained Solid Angle Reconstruct(1,7); break; } case 4: { // ADC Reconstruct(1,4); break; } case 5: { // Raw Signal Reconstruct(1,5); break; } case 6: { // Q-Ball reconstruction Reconstruct(2,0); break; } } } void QmitkQBallReconstructionView::MethodChoosen(int method) { #ifndef DIFFUSION_IMAGING_EXTENDED if(method>=3) { method = method + 1; } #endif m_Controls->m_QBallSelectionBox->setHidden(true); m_Controls->m_OutputCoeffsImage->setHidden(true); if (method==0) m_Controls->m_ShFrame->setVisible(false); else m_Controls->m_ShFrame->setVisible(true); switch(method) { case 0: m_Controls->m_Description->setText("Numerical recon. (Tuch 2004)"); break; case 1: m_Controls->m_Description->setText("Spherical harmonics recon. (Descoteaux 2007)"); m_Controls->m_OutputCoeffsImage->setHidden(false); break; case 2: m_Controls->m_Description->setText("SH recon. with solid angle consideration (Aganj 2009)"); m_Controls->m_OutputCoeffsImage->setHidden(false); break; case 3: m_Controls->m_Description->setText("SH solid angle with non-neg. constraint (Goh 2009)"); m_Controls->m_OutputCoeffsImage->setHidden(false); break; case 4: m_Controls->m_Description->setText("SH recon. of the plain ADC-profiles"); m_Controls->m_OutputCoeffsImage->setHidden(false); break; case 5: m_Controls->m_Description->setText("SH recon. of the raw diffusion signal"); m_Controls->m_OutputCoeffsImage->setHidden(false); break; case 6: m_Controls->m_Description->setText("SH recon. of the multi shell diffusion signal (Aganj 2010)"); m_Controls->m_QBallSelectionBox->setHidden(false); m_Controls->m_OutputCoeffsImage->setHidden(false); break; } } void QmitkQBallReconstructionView::Reconstruct(int method, int normalization) { if (m_Controls->m_ImageBox->GetSelectedNode().IsNotNull()) { if(method == 0) { NumericalQBallReconstruction(m_Controls->m_ImageBox->GetSelectedNode(), normalization); } else if(method == 1) { AnalyticalQBallReconstruction(m_Controls->m_ImageBox->GetSelectedNode(), normalization); } else if(method == 2) { MultiQBallReconstruction(m_Controls->m_ImageBox->GetSelectedNode()); } } } void QmitkQBallReconstructionView::NumericalQBallReconstruction(mitk::DataNode::Pointer node, int normalization) { try { mitk::Image* vols = static_cast(node->GetData()); std::string nodename = node->GetName(); typedef itk::DiffusionQballReconstructionImageFilter QballReconstructionImageFilterType; ITKDiffusionImageType::Pointer itkVectorImagePointer = ITKDiffusionImageType::New(); mitk::CastToItkImage(vols, itkVectorImagePointer); QballReconstructionImageFilterType::Pointer filter = QballReconstructionImageFilterType::New(); filter->SetGradientImage( static_cast( vols->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer(), itkVectorImagePointer ); filter->SetBValue( static_cast(vols->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() )->GetValue() ); filter->SetThreshold( m_Controls->m_QBallReconstructionThreasholdEdit->value() ); std::string nodePostfix; switch(normalization) { case 0: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_STANDARD); nodePostfix = "_Numerical_Qball"; break; } case 1: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_B_ZERO_B_VALUE); nodePostfix = "_Numerical_ZeroBvalueNormalization_Qball"; break; } case 2: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_B_ZERO); nodePostfix = "_NumericalQball_ZeroNormalization_Qball"; break; } case 3: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_NONE); nodePostfix = "_NumericalQball_NoNormalization_Qball"; break; } default: { filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_STANDARD); nodePostfix = "_NumericalQball_Qball"; } } filter->Update(); // ODFs TO DATATREE mitk::OdfImage::Pointer image = mitk::OdfImage::New(); image->InitializeByItk( filter->GetOutput() ); image->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer new_node = mitk::DataNode::New(); new_node->SetData( image ); - SetDefaultNodeProperties(new_node, nodename+nodePostfix); + new_node->SetName(nodename+nodePostfix); mitk::ProgressBar::GetInstance()->Progress(); GetDataStorage()->Add(new_node, node); this->GetRenderWindowPart()->RequestUpdate(); } catch (itk::ExceptionObject &ex) { MITK_INFO << ex ; QMessageBox::information(0, "Reconstruction not possible:", ex.GetDescription()); return ; } } void QmitkQBallReconstructionView::AnalyticalQBallReconstruction( mitk::DataNode::Pointer node, int normalization) { try { float lambda = m_Controls->m_QBallReconstructionLambdaLineEdit->value(); switch(m_Controls->m_QBallReconstructionMaxLLevelComboBox->currentIndex()) { case 0: { TemplatedAnalyticalQBallReconstruction<2>(node, lambda, normalization); break; } case 1: { TemplatedAnalyticalQBallReconstruction<4>(node, lambda, normalization); break; } case 2: { TemplatedAnalyticalQBallReconstruction<6>(node, lambda, normalization); break; } case 3: { TemplatedAnalyticalQBallReconstruction<8>(node, lambda, normalization); break; } case 4: { TemplatedAnalyticalQBallReconstruction<10>(node, lambda, normalization); break; } case 5: { TemplatedAnalyticalQBallReconstruction<12>(node, lambda, normalization); break; } } this->GetRenderWindowPart()->RequestUpdate(); } catch (itk::ExceptionObject &ex) { MITK_INFO << ex; QMessageBox::information(0, "Reconstruction not possible:", ex.GetDescription()); return; } } template void QmitkQBallReconstructionView::TemplatedAnalyticalQBallReconstruction(mitk::DataNode* dataNodePointer, float lambda, int normalization) { typedef itk::AnalyticalDiffusionQballReconstructionImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); mitk::Image* vols = dynamic_cast(dataNodePointer->GetData()); ITKDiffusionImageType::Pointer itkVectorImagePointer = ITKDiffusionImageType::New(); mitk::CastToItkImage(vols, itkVectorImagePointer); filter->SetBValue( static_cast(vols->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() )->GetValue() ); filter->SetGradientImage( static_cast( vols->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer(), itkVectorImagePointer ); filter->SetThreshold( m_Controls->m_QBallReconstructionThreasholdEdit->value() ); filter->SetLambda(lambda); std::string nodePostfix; switch(normalization) { case 0: { filter->SetNormalizationMethod(FilterType::QBAR_STANDARD); nodePostfix = "_SH_Qball"; break; } case 1: { filter->SetNormalizationMethod(FilterType::QBAR_B_ZERO_B_VALUE); nodePostfix = "_SH_1_Qball"; break; } case 2: { filter->SetNormalizationMethod(FilterType::QBAR_B_ZERO); nodePostfix = "_SH_2_Qball"; break; } case 3: { filter->SetNormalizationMethod(FilterType::QBAR_NONE); nodePostfix = "_SH_3_Qball"; break; } case 4: { filter->SetNormalizationMethod(FilterType::QBAR_ADC_ONLY); nodePostfix = "_AdcProfile"; break; } case 5: { filter->SetNormalizationMethod(FilterType::QBAR_RAW_SIGNAL); nodePostfix = "_RawSignal"; break; } case 6: { filter->SetNormalizationMethod(FilterType::QBAR_SOLID_ANGLE); nodePostfix = "_SH_CSA_Qball"; break; } case 7: { filter->SetNormalizationMethod(FilterType::QBAR_NONNEG_SOLID_ANGLE); nodePostfix = "_SH_NonNegCSA_Qball"; break; } default: { filter->SetNormalizationMethod(FilterType::QBAR_STANDARD); } } filter->Update(); // ODFs TO DATATREE mitk::OdfImage::Pointer image = mitk::OdfImage::New(); image->InitializeByItk( filter->GetOutput() ); image->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); - SetDefaultNodeProperties(node, dataNodePointer->GetName()+nodePostfix); + node->SetName(dataNodePointer->GetName()+nodePostfix); GetDataStorage()->Add(node, dataNodePointer); if(m_Controls->m_OutputCoeffsImage->isChecked()) { - mitk::Image::Pointer coeffsImage = mitk::Image::New(); + mitk::Image::Pointer coeffsImage = dynamic_cast(mitk::ShImage::New().GetPointer()); coeffsImage->InitializeByItk( filter->GetCoefficientImage().GetPointer() ); coeffsImage->SetVolume( filter->GetCoefficientImage()->GetBufferPointer() ); + mitk::DataNode::Pointer coeffsNode=mitk::DataNode::New(); coeffsNode->SetData( coeffsImage ); coeffsNode->SetProperty( "name", mitk::StringProperty::New(dataNodePointer->GetName()+"_SH-Coeffs") ); - coeffsNode->SetVisibility(false); GetDataStorage()->Add(coeffsNode, node); } } void QmitkQBallReconstructionView::MultiQBallReconstruction(mitk::DataNode::Pointer node) { try { float lambda = m_Controls->m_QBallReconstructionLambdaLineEdit->value(); switch(m_Controls->m_QBallReconstructionMaxLLevelComboBox->currentIndex()) { case 0: { TemplatedMultiQBallReconstruction<2>(lambda, node); break; } case 1: { TemplatedMultiQBallReconstruction<4>(lambda, node); break; } case 2: { TemplatedMultiQBallReconstruction<6>(lambda, node); break; } case 3: { TemplatedMultiQBallReconstruction<8>(lambda, node); break; } case 4: { TemplatedMultiQBallReconstruction<10>(lambda, node); break; } case 5: { TemplatedMultiQBallReconstruction<12>(lambda, node); break; } } } catch (itk::ExceptionObject &ex) { MITK_INFO << ex ; QMessageBox::information(0, "Reconstruction not possible:", ex.GetDescription()); return ; } } template void QmitkQBallReconstructionView::TemplatedMultiQBallReconstruction(float lambda, mitk::DataNode* dataNodePointer) { typedef itk::DiffusionMultiShellQballReconstructionImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); std::string nodename; dataNodePointer->GetStringProperty("name",nodename); mitk::Image* dwi = dynamic_cast(dataNodePointer->GetData()); BValueMapType currSelectionMap = m_ShellSelectorMap[dataNodePointer]->GetBValueSelctionMap(); if(currSelectionMap.size() != 4)// || currSelectionMap.find(0) != currSelectionMap.end()) { QMessageBox::information(0, "Reconstruction not possible:" ,QString("Only three equidistant shells are supported. (ImageName: " + QString(nodename.c_str()) + ")")); return; } BValueMapType::reverse_iterator it1 = currSelectionMap.rbegin(); BValueMapType::reverse_iterator it2 = currSelectionMap.rbegin(); ++it2; // Get average distance int avdistance = 0; for(; it2 != currSelectionMap.rend(); ++it1,++it2) avdistance += (int)it1->first - (int)it2->first; avdistance /= currSelectionMap.size()-1; // Check if all shells are using the same averae distance it1 = currSelectionMap.rbegin(); it2 = currSelectionMap.rbegin(); ++it2; for(; it2 != currSelectionMap.rend(); ++it1,++it2) { if(avdistance != (int)it1->first - (int)it2->first) { QMessageBox::information(0, "Reconstruction not possible:" ,QString("Selected Shells are not in a equidistant configuration. (ImageName: " + QString(nodename.c_str()) + ")")); return; } } ITKDiffusionImageType::Pointer itkVectorImagePointer = ITKDiffusionImageType::New(); mitk::CastToItkImage(dwi, itkVectorImagePointer); filter->SetBValueMap(m_ShellSelectorMap[dataNodePointer]->GetBValueSelctionMap()); filter->SetGradientImage( static_cast( dwi->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer(), itkVectorImagePointer, static_cast(dwi->GetProperty(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() )->GetValue() ); filter->SetThreshold( m_Controls->m_QBallReconstructionThreasholdEdit->value() ); filter->SetLambda(lambda); filter->Update(); if(m_Controls->m_OutputCoeffsImage->isChecked()) { mitk::Image::Pointer coeffsImage = mitk::Image::New(); coeffsImage->InitializeByItk( filter->GetCoefficientImage().GetPointer() ); coeffsImage->SetVolume( filter->GetCoefficientImage()->GetBufferPointer() ); mitk::DataNode::Pointer coeffsNode=mitk::DataNode::New(); coeffsNode->SetData( coeffsImage ); coeffsNode->SetProperty( "name", mitk::StringProperty::New( QString(nodename.c_str()).append("_SH-Coefficients").toStdString()) ); GetDataStorage()->Add(coeffsNode, dataNodePointer); } // ODFs TO DATATREE mitk::OdfImage::Pointer image = mitk::OdfImage::New(); image->InitializeByItk( filter->GetOutput() ); image->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); - SetDefaultNodeProperties(node, nodename+"_SH_MultiShell_Qball"); + node->SetName(nodename+"_SH_MultiShell_Qball"); GetDataStorage()->Add(node, dataNodePointer); } -void QmitkQBallReconstructionView::SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name) -{ - node->SetProperty( "ShowMaxNumber", mitk::IntProperty::New( 500 ) ); - node->SetProperty( "Scaling", mitk::FloatProperty::New( 1.0 ) ); - node->SetProperty( "Normalization", mitk::OdfNormalizationMethodProperty::New()); - node->SetProperty( "ScaleBy", mitk::OdfScaleByProperty::New()); - node->SetProperty( "IndexParam1", mitk::FloatProperty::New(2)); - node->SetProperty( "IndexParam2", mitk::FloatProperty::New(1)); - node->SetProperty( "visible", mitk::BoolProperty::New( true ) ); - node->SetProperty( "VisibleOdfs", mitk::BoolProperty::New( false ) ); - node->SetProperty ("layer", mitk::IntProperty::New(100)); - node->SetProperty( "DoRefresh", mitk::BoolProperty::New( true ) ); - node->SetProperty( "name", mitk::StringProperty::New(name) ); -} - void QmitkQBallReconstructionView::GenerateShellSelectionUI(mitk::DataNode::Pointer node) { std::map tempMap; if(m_ShellSelectorMap.find( node.GetPointer() ) != m_ShellSelectorMap.end()) { tempMap[node.GetPointer()] = m_ShellSelectorMap[node.GetPointer()]; m_ShellSelectorMap.erase(node.GetPointer()); } else { tempMap[node.GetPointer()] = new QbrShellSelection(this, node ); tempMap[node.GetPointer()]->SetVisible(true); } for(std::map::iterator it = m_ShellSelectorMap.begin(); it != m_ShellSelectorMap.end();it ++) { delete it->second; } m_ShellSelectorMap.clear(); m_ShellSelectorMap = tempMap; } void QmitkQBallReconstructionView::PreviewThreshold(int threshold) { if (m_Controls->m_ImageBox->GetSelectedNode().IsNotNull()) { mitk::Image* vols = static_cast(m_Controls->m_ImageBox->GetSelectedNode()->GetData()); // Extract b0 image ITKDiffusionImageType::Pointer itkVectorImagePointer = ITKDiffusionImageType::New(); mitk::CastToItkImage(vols, itkVectorImagePointer); typedef itk::B0ImageExtractionImageFilter FilterType; FilterType::Pointer filterB0 = FilterType::New(); filterB0->SetInput( itkVectorImagePointer ); filterB0->SetDirections( static_cast( vols->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() )->GetGradientDirectionsContainer() ); filterB0->Update(); mitk::Image::Pointer mitkImage = mitk::Image::New(); typedef itk::Image ImageType; typedef itk::Image SegmentationType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; // apply threshold ThresholdFilterType::Pointer filterThreshold = ThresholdFilterType::New(); filterThreshold->SetInput(filterB0->GetOutput()); filterThreshold->SetLowerThreshold(threshold); filterThreshold->SetInsideValue(0); filterThreshold->SetOutsideValue(1); // mark cut off values red filterThreshold->Update(); mitkImage->InitializeByItk( filterThreshold->GetOutput() ); mitkImage->SetVolume( filterThreshold->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node; if (this->GetDataStorage()->GetNamedDerivedNode("ThresholdOverlay", m_Controls->m_ImageBox->GetSelectedNode())) { node = this->GetDataStorage()->GetNamedDerivedNode("ThresholdOverlay", m_Controls->m_ImageBox->GetSelectedNode()); } else { // create a new node, to show thresholded values node = mitk::DataNode::New(); GetDataStorage()->Add( node, m_Controls->m_ImageBox->GetSelectedNode() ); node->SetProperty( "name", mitk::StringProperty::New("ThresholdOverlay")); node->SetBoolProperty("helper object", true); } node->SetData( mitkImage ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/src/internal/QmitkQBallReconstructionView.h b/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/src/internal/QmitkQBallReconstructionView.h index b5c559dbce..e8a73bebc4 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/src/internal/QmitkQBallReconstructionView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/src/internal/QmitkQBallReconstructionView.h @@ -1,133 +1,136 @@ /*=================================================================== 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 _QMITKQBALLRECONSTRUCTIONVIEW_H_INCLUDED #define _QMITKQBALLRECONSTRUCTIONVIEW_H_INCLUDED #include #include #include #include "ui_QmitkQBallReconstructionViewControls.h" #include #include #include +#include typedef short DiffusionPixelType; struct QbrSelListener; struct QbrShellSelection; /*! * \ingroup org_mitk_gui_qt_qballreconstruction_internal * * \brief QmitkQBallReconstructionView * * Document your class here. */ class QmitkQBallReconstructionView : public QmitkAbstractView, public mitk::ILifecycleAwarePart { friend struct QbrSelListener; friend struct QbrShellSelection; typedef mitk::DiffusionPropertyHelper::GradientDirectionType GradientDirectionType; typedef mitk::DiffusionPropertyHelper::GradientDirectionsContainerType GradientDirectionContainerType; typedef mitk::DiffusionPropertyHelper::BValueMapType BValueMapType; typedef itk::VectorImage< DiffusionPixelType, 3 > ITKDiffusionImageType; // this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) Q_OBJECT public: static const std::string VIEW_ID; QmitkQBallReconstructionView(); virtual ~QmitkQBallReconstructionView(); virtual void CreateQtPartControl(QWidget *parent) override; /// \brief Creation of the connections of main and control widget virtual void CreateConnections(); /// /// Sets the focus to an internal widget. /// virtual void SetFocus() override; /// \brief Called when the view gets activated virtual void Activated() override; /// \brief Called when the view gets deactivated virtual void Deactivated() override; /// \brief Called when the view becomes visible virtual void Visible() override; /// \brief Called when the view becomes hidden virtual void Hidden() override; static const int nrconvkernels; protected slots: void UpdateGui(); void ReconstructStandard(); + void ConvertShImage(); void MethodChoosen(int method); void Reconstruct(int method, int normalization); void NumericalQBallReconstruction(mitk::DataNode::Pointer node, int normalization); void AnalyticalQBallReconstruction(mitk::DataNode::Pointer node, int normalization); void MultiQBallReconstruction(mitk::DataNode::Pointer node); /** * @brief PreviewThreshold Generates a preview of the values that are cut off by the thresholds * @param threshold */ void PreviewThreshold(int threshold); protected: + template + void TemplatedConvertShImage(mitk::ShImage::Pointer mitkImage); + /// \brief called by QmitkAbstractView when DataManager's selection has changed virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList& nodes) override; Ui::QmitkQBallReconstructionViewControls* m_Controls; template void TemplatedAnalyticalQBallReconstruction(mitk::DataNode* dataNodePointer, float lambda, int normalization); template void TemplatedMultiQBallReconstruction(float lambda, mitk::DataNode*); - void SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name); - private: std::map< const mitk::DataNode *, QbrShellSelection * > m_ShellSelectorMap; void GenerateShellSelectionUI(mitk::DataNode::Pointer node); }; #endif // _QMITKQBALLRECONSTRUCTIONVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/src/internal/QmitkQBallReconstructionViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/src/internal/QmitkQBallReconstructionViewControls.ui index 1210791dc4..dcad1e284d 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/src/internal/QmitkQBallReconstructionViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/src/internal/QmitkQBallReconstructionViewControls.ui @@ -1,343 +1,420 @@ QmitkQBallReconstructionViewControls 0 0 372 844 0 0 true QmitkQBallReconstructionViewControls 25 - - - - Input Data - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Input for Q-Ball reconstruction. - - - Raw DWI: - - - - - - - - - Parameters 0 0 0 0 2 Numerical Standard Solid Angle Constraint Solid Angle ADC-Profile only Raw Signal only Multi-Shell TextLabel QFrame::NoFrame QFrame::Raised 0 0 0 0 QFrame::NoFrame QFrame::Raised 0 0 0 0 true Regularization Parameter Lambda: false Regularization factor 3 1.000000000000000 0.001000000000000 0.006000000000000 true SH-Order: false true -1 true B0 Threshold false 10000 Output SH-Coefficient Image false false Start Reconstruction + + + + Input Data + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Input for Q-Ball reconstruction. + + + Raw DWI: + + + + + + + + + true Qt::LeftToRight false Multi-Shell Reconstruction 0 0 0 0 - + Qt::Vertical 20 0 + + + + Convert SH to sampled ODF image + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + false + + + + + + + + + + + + Convert + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Input for Q-Ball reconstruction. + + + SH Image: + + + + + + + + + + + + QmitkDataStorageComboBox QComboBox
QmitkDataStorageComboBox.h
diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake b/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake index f8797c5460..3ccd164796 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake @@ -1,65 +1,66 @@ set(SRC_CPP_FILES ) set(INTERNAL_CPP_FILES mitkPluginActivator.cpp QmitkDiffusionDicomImportView.cpp QmitkControlVisualizationPropertiesView.cpp QmitkDicomTractogramTagEditorView.cpp Perspectives/QmitkDiffusionDefaultPerspective.cpp Perspectives/QmitkSegmentationPerspective.cpp ) set(UI_FILES src/internal/QmitkDiffusionDicomImportViewControls.ui src/internal/QmitkControlVisualizationPropertiesViewControls.ui src/internal/QmitkDicomTractogramTagEditorViewControls.ui ) set(MOC_H_FILES src/internal/mitkPluginActivator.h src/internal/QmitkDiffusionDicomImportView.h src/internal/QmitkControlVisualizationPropertiesView.h src/internal/QmitkDicomTractogramTagEditorView.h src/internal/Perspectives/QmitkDiffusionDefaultPerspective.h src/internal/Perspectives/QmitkSegmentationPerspective.h ) set(CACHED_RESOURCE_FILES # list of resource files which can be used by the plug-in # system without loading the plug-ins shared library, # for example the icon used in the menu and tabs for the # plug-in views in the workbench plugin.xml resources/dwiimport.png resources/vizControls.png resources/arrow.png resources/dwi.png resources/odf.png resources/tensor.png resources/tractogram.png resources/odf_peaks.png resources/ml_tractogram.png + resources/sh.png resources/refresh.xpm resources/segmentation.svg ) set(QRC_FILES # uncomment the following line if you want to use Qt resources resources/QmitkDiffusionImaging.qrc #resources/QmitkTractbasedSpatialStatisticsView.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/QmitkDiffusionImaging.qrc b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/QmitkDiffusionImaging.qrc index edee859a05..9ce535c184 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/QmitkDiffusionImaging.qrc +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/QmitkDiffusionImaging.qrc @@ -1,32 +1,33 @@ dwi.png dwiimport.png texIntONIcon.png texIntOFFIcon.png vizControls.png Refresh_48.png glyphsoff_C.png glyphsoff_S.png glyphsoff_T.png glyphson_C.png glyphson_S.png glyphson_T.png color24.gif color48.gif color64.gif crosshair.png paint2.png reset.png MapperEfx2D.png refresh.xpm tensor.png tractogram.png ml_tractogram.png ConnectomicsNetwork.png odf.png DiffData24.png odf_peaks.png segmentation.svg + sh.png diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/sh.png b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/sh.png new file mode 100644 index 0000000000..450945a4c9 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/sh.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp index 3af7534c2d..7c54d0dbbf 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp @@ -1,1291 +1,1293 @@ /*=================================================================== 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 "QmitkControlVisualizationPropertiesView.h" #include "mitkNodePredicateDataType.h" #include "mitkDataNodeObject.h" #include "mitkOdfNormalizationMethodProperty.h" #include "mitkOdfScaleByProperty.h" #include "mitkResliceMethodProperty.h" #include "mitkRenderingManager.h" -#include "mitkTbssImage.h" +#include "mitkImageCast.h" +#include "mitkShImage.h" #include "mitkPlanarFigure.h" #include "mitkFiberBundle.h" #include "QmitkDataStorageComboBox.h" #include "mitkPlanarFigureInteractor.h" #include #include #include #include #include #include "usModuleRegistry.h" #include #include #include "mitkPlaneGeometry.h" #include #include #include #include "berryIWorkbenchWindow.h" #include "berryIWorkbenchPage.h" #include "berryISelectionService.h" #include "berryConstants.h" #include "berryPlatformUI.h" #include "itkRGBAPixel.h" #include #include "qwidgetaction.h" #include "qcolordialog.h" #include #include #include #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) const std::string QmitkControlVisualizationPropertiesView::VIEW_ID = "org.mitk.views.controlvisualizationpropertiesview"; using namespace berry; QmitkControlVisualizationPropertiesView::QmitkControlVisualizationPropertiesView() : QmitkAbstractView(), m_Controls(nullptr), m_CurrentSelection(0), m_IconTexOFF(new QIcon(":/QmitkDiffusionImaging/texIntOFFIcon.png")), m_IconTexON(new QIcon(":/QmitkDiffusionImaging/texIntONIcon.png")), m_IconGlyOFF_T(new QIcon(":/QmitkDiffusionImaging/glyphsoff_T.png")), m_IconGlyON_T(new QIcon(":/QmitkDiffusionImaging/glyphson_T.png")), m_IconGlyOFF_C(new QIcon(":/QmitkDiffusionImaging/glyphsoff_C.png")), m_IconGlyON_C(new QIcon(":/QmitkDiffusionImaging/glyphson_C.png")), m_IconGlyOFF_S(new QIcon(":/QmitkDiffusionImaging/glyphsoff_S.png")), m_IconGlyON_S(new QIcon(":/QmitkDiffusionImaging/glyphson_S.png")), m_GlyIsOn_T(false), m_GlyIsOn_C(false), m_GlyIsOn_S(false), m_CurrentPickingNode(0), m_ColorPropertyObserverTag(0), m_OpacityPropertyObserverTag(0) { currentThickSlicesMode = 1; m_MyMenu = nullptr; int numThread = itk::MultiThreader::GetGlobalMaximumNumberOfThreads(); if (numThread > 12) numThread = 12; itk::MultiThreader::SetGlobalDefaultNumberOfThreads(numThread); } QmitkControlVisualizationPropertiesView::~QmitkControlVisualizationPropertiesView() { } void QmitkControlVisualizationPropertiesView::OnThickSlicesModeSelected( QAction* action ) { currentThickSlicesMode = action->data().toInt(); switch( currentThickSlicesMode ) { case 0: // toInt() returns 0 'otherwise'. return; // dummy code/todo: implement stuff. case 1: this->m_Controls->m_TSMenu->setText("MIP"); break; case 2: this->m_Controls->m_TSMenu->setText("SUM"); break; case 3: this->m_Controls->m_TSMenu->setText("WEIGH"); break; default: return; // dummy code/todo: implement stuff. } if (auto renderWindowPart = this->GetRenderWindowPart(OPEN)) { /// TODO There is no way to access the individual crosshair planes through the render window part API. /// There could be a new 'mitk::DataNode* mitk::ILinkedRenderWindowPart::GetSlicingPlane(const std::string& name) const' /// function for this purpose. For the time being, I comment out the lines below, but they are valid /// and they have to be re-enabled after the crosshair planes can be accessed again. // mitk::DataNode* n; // n = renderWindowPart->GetSlicingPlane("axial"); // if (n) { n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( currentThickSlicesMode ) ); } // n = renderWindowPart->GetSlicingPlane("sagittal"); // if (n) { n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( currentThickSlicesMode ) ); } // n = renderWindowPart->GetSlicingPlane("coronal"); // if (n) { n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( currentThickSlicesMode ) ); } mitk::BaseRenderer::Pointer renderer; renderer = renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer(); if (renderer.IsNotNull()) { renderer->SendUpdateSlice(); } renderer = renderWindowPart->GetQmitkRenderWindow("sagittal")->GetRenderer(); if (renderer.IsNotNull()) { renderer->SendUpdateSlice(); } renderer = renderWindowPart->GetQmitkRenderWindow("coronal")->GetRenderer(); if (renderer.IsNotNull()) { renderer->SendUpdateSlice(); } renderer->GetRenderingManager()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::OnTSNumChanged( int num ) { if (auto renderWindowPart = this->GetRenderWindowPart(OPEN)) { /// TODO There is no way to access the individual crosshair planes through the render window part API. /// There could be a new 'mitk::DataNode* mitk::ILinkedRenderWindowPart::GetSlicingPlane(const std::string& name) const' /// function for this purpose. For the time being, I comment out the lines below, but they are valid /// and they have to be re-enabled after the crosshair planes can be accessed again. // if(num==0) // { // mitk::DataNode* n; // n = renderWindowPart->GetSlicingPlane("axial"); // if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( 0 ) ); // if(n) n->SetProperty( "reslice.thickslices.num", mitk::IntProperty::New( num ) ); // if(n) n->SetProperty( "reslice.thickslices.showarea", mitk::BoolProperty::New( false ) ); // // n = renderWindowPart->GetSlicingPlane("sagittal"); // if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( 0 ) ); // if(n) n->SetProperty( "reslice.thickslices.num", mitk::IntProperty::New( num ) ); // if(n) n->SetProperty( "reslice.thickslices.showarea", mitk::BoolProperty::New( false ) ); // // n = renderWindowPart->GetSlicingPlane("coronal"); // if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( 0 ) ); // if(n) n->SetProperty( "reslice.thickslices.num", mitk::IntProperty::New( num ) ); // if(n) n->SetProperty( "reslice.thickslices.showarea", mitk::BoolProperty::New( false ) ); // } // else // { // mitk::DataNode* n; // n = renderWindowPart->GetSlicingPlane("axial"); // if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( currentThickSlicesMode ) ); // if(n) n->SetProperty( "reslice.thickslices.num", mitk::IntProperty::New( num ) ); // if(n) n->SetProperty( "reslice.thickslices.showarea", mitk::BoolProperty::New( (num>0) ) ); // // n = renderWindowPart->GetSlicingPlane("sagittal"); // if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( currentThickSlicesMode ) ); // if(n) n->SetProperty( "reslice.thickslices.num", mitk::IntProperty::New( num ) ); // if(n) n->SetProperty( "reslice.thickslices.showarea", mitk::BoolProperty::New( (num>0) ) ); // // n = renderWindowPart->GetSlicingPlane("coronal"); // if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( currentThickSlicesMode ) ); // if(n) n->SetProperty( "reslice.thickslices.num", mitk::IntProperty::New( num ) ); // if(n) n->SetProperty( "reslice.thickslices.showarea", mitk::BoolProperty::New( (num>0) ) ); // } m_TSLabel->setText(QString::number( num*2 + 1 )); mitk::BaseRenderer::Pointer renderer; renderer = renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer(); if(renderer.IsNotNull()) { renderer->SendUpdateSlice(); } renderer = nullptr; renderer = renderWindowPart->GetQmitkRenderWindow("sagittal")->GetRenderer(); if(renderer.IsNotNull()) { renderer->SendUpdateSlice(); } renderer = nullptr; renderer = renderWindowPart->GetQmitkRenderWindow("coronal")->GetRenderer(); if(renderer.IsNotNull()) { renderer->SendUpdateSlice(); } renderer->GetRenderingManager()->RequestUpdateAll(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); } } void QmitkControlVisualizationPropertiesView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkControlVisualizationPropertiesViewControls; m_Controls->setupUi(parent); this->CreateConnections(); // hide warning (ODFs in rotated planes) m_Controls->m_lblRotatedPlanesWarning->hide(); m_MyMenu = new QMenu(parent); m_Controls->m_TSMenu->setMenu( m_MyMenu ); QIcon iconFiberFade(":/QmitkDiffusionImaging/MapperEfx2D.png"); m_Controls->m_FiberFading2D->setIcon(iconFiberFade); #ifndef DIFFUSION_IMAGING_EXTENDED int size = m_Controls->m_AdditionalScaling->count(); for(int t=0; tm_AdditionalScaling->itemText(t).toStdString() == "Scale by ASR") { m_Controls->m_AdditionalScaling->removeItem(t); } } #endif m_Controls->m_NormalizationFrame->setVisible(false); m_Controls->m_Crosshair->setVisible(false); mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow) { m_SliceChangeListener.RenderWindowPartActivated(renderWindow); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } connect(m_Controls->m_SetColor1, SIGNAL(clicked()), this, SLOT(SetColor())); connect(m_Controls->m_SetColor2, SIGNAL(clicked()), this, SLOT(SetColor())); } } void QmitkControlVisualizationPropertiesView::SetColor() { if(m_SelectedNode) { QColor c = QColorDialog::getColor(); float rgb[3]; rgb[0] = c.redF(); rgb[1] = c.greenF(); rgb[2] = c.blueF(); m_SelectedNode->SetColor(rgb); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::SetFocus() { m_Controls->m_TSMenu->setFocus(); } void QmitkControlVisualizationPropertiesView::SliceRotation(const itk::EventObject&) { // test if plane rotated if( m_GlyIsOn_T || m_GlyIsOn_C || m_GlyIsOn_S ) { if( this->IsPlaneRotated() ) { // show label m_Controls->m_lblRotatedPlanesWarning->show(); } else { //hide label m_Controls->m_lblRotatedPlanesWarning->hide(); } } } void QmitkControlVisualizationPropertiesView::NodeRemoved(const mitk::DataNode* /*node*/) { } #include void QmitkControlVisualizationPropertiesView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_VisibleOdfsON_T), SIGNAL(clicked()), this, SLOT(VisibleOdfsON_T()) ); connect( (QObject*)(m_Controls->m_VisibleOdfsON_S), SIGNAL(clicked()), this, SLOT(VisibleOdfsON_S()) ); connect( (QObject*)(m_Controls->m_VisibleOdfsON_C), SIGNAL(clicked()), this, SLOT(VisibleOdfsON_C()) ); connect( (QObject*)(m_Controls->m_ShowMaxNumber), SIGNAL(editingFinished()), this, SLOT(ShowMaxNumberChanged()) ); connect( (QObject*)(m_Controls->m_NormalizationDropdown), SIGNAL(currentIndexChanged(int)), this, SLOT(NormalizationDropdownChanged(int)) ); connect( (QObject*)(m_Controls->m_ScalingFactor), SIGNAL(valueChanged(double)), this, SLOT(ScalingFactorChanged(double)) ); connect( (QObject*)(m_Controls->m_AdditionalScaling), SIGNAL(currentIndexChanged(int)), this, SLOT(AdditionalScaling(int)) ); connect((QObject*) m_Controls->m_ResetColoring, SIGNAL(clicked()), (QObject*) this, SLOT(ResetColoring())); connect((QObject*) m_Controls->m_ResetColoring2, SIGNAL(clicked()), (QObject*) this, SLOT(ResetColoring())); connect((QObject*) m_Controls->m_FiberFading2D, SIGNAL(clicked()), (QObject*) this, SLOT( Fiber2DfadingEFX() ) ); connect((QObject*) m_Controls->m_FiberThicknessSlider, SIGNAL(sliderReleased()), (QObject*) this, SLOT( FiberSlicingThickness2D() ) ); connect((QObject*) m_Controls->m_FiberThicknessSlider, SIGNAL(valueChanged(int)), (QObject*) this, SLOT( FiberSlicingUpdateLabel(int) )); connect((QObject*) m_Controls->m_Crosshair, SIGNAL(clicked()), (QObject*) this, SLOT(SetInteractor())); connect((QObject*) m_Controls->m_LineWidth, SIGNAL(editingFinished()), (QObject*) this, SLOT(LineWidthChanged())); connect((QObject*) m_Controls->m_TubeWidth, SIGNAL(editingFinished()), (QObject*) this, SLOT(TubeRadiusChanged())); connect( (QObject*) m_Controls->m_OdfColorBox, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(OnColourisationModeChanged() ) ); connect((QObject*) m_Controls->m_Clip0, SIGNAL(toggled(bool)), (QObject*) this, SLOT(Toggle3DClipping(bool))); connect((QObject*) m_Controls->m_Clip1, SIGNAL(toggled(bool)), (QObject*) this, SLOT(Toggle3DClipping(bool))); connect((QObject*) m_Controls->m_Clip2, SIGNAL(toggled(bool)), (QObject*) this, SLOT(Toggle3DClipping(bool))); connect((QObject*) m_Controls->m_Clip3, SIGNAL(toggled(bool)), (QObject*) this, SLOT(Toggle3DClipping(bool))); connect((QObject*) m_Controls->m_FlipClipBox, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(Toggle3DClipping())); connect((QObject*) m_Controls->m_FlipPeaksButton, SIGNAL(clicked()), (QObject*) this, SLOT(FlipPeaks())); } } // set diffusion image channel to b0 volume void QmitkControlVisualizationPropertiesView::NodeAdded(const mitk::DataNode *node) { mitk::DataNode* notConst = const_cast(node); bool isDiffusionImage( mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(node->GetData())) ); if (isDiffusionImage) { mitk::Image::Pointer dimg = dynamic_cast(notConst->GetData()); // if there is no b0 image in the dataset, the GetB0Indices() returns a vector of size 0 // and hence we cannot set the Property directly to .front() int displayChannelPropertyValue = 0; mitk::BValueMapProperty* bmapproperty = static_cast (dimg->GetProperty(mitk::DiffusionPropertyHelper::BVALUEMAPPROPERTYNAME.c_str()).GetPointer() ); mitk::DiffusionPropertyHelper::BValueMapType map = bmapproperty->GetBValueMap(); if( map[0].size() > 0) { displayChannelPropertyValue = map[0].front(); } notConst->SetIntProperty("DisplayChannel", displayChannelPropertyValue ); } } /* OnSelectionChanged is registered to SelectionService, therefore no need to implement SelectionService Listener explicitly */ void QmitkControlVisualizationPropertiesView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& nodes) { m_Controls->m_BundleControlsFrame->setVisible(false); m_Controls->m_ImageControlsFrame->setVisible(false); m_Controls->m_PeakImageFrame->setVisible(false); if (nodes.size()>1) // only do stuff if one node is selected return; m_Controls->m_NumberGlyphsFrame->setVisible(false); m_Controls->m_GlyphFrame->setVisible(false); m_Controls->m_TSMenu->setVisible(false); m_SelectedNode = nullptr; int numOdfImages = 0; for (mitk::DataNode::Pointer node: nodes) { if(node.IsNull()) continue; mitk::BaseData* nodeData = node->GetData(); if(nodeData == nullptr) continue; m_SelectedNode = node; if (dynamic_cast(nodeData)) { m_Controls->m_PeakImageFrame->setVisible(true); if (m_Color.IsNotNull()) m_Color->RemoveObserver(m_ColorPropertyObserverTag); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkControlVisualizationPropertiesView::SetCustomColor ); m_Color = dynamic_cast(node->GetProperty("color", nullptr)); if (m_Color.IsNotNull()) m_ColorPropertyObserverTag = m_Color->AddObserver( itk::ModifiedEvent(), command ); } else if (dynamic_cast(nodeData)) { int Fiber3DClippingPlaneId = -1; m_SelectedNode->GetPropertyValue("Fiber3DClippingPlaneId",Fiber3DClippingPlaneId); switch(Fiber3DClippingPlaneId) { case 0: m_Controls->m_Clip0->setChecked(1); break; case 1: m_Controls->m_Clip1->setChecked(1); break; case 2: m_Controls->m_Clip2->setChecked(1); break; case 3: m_Controls->m_Clip3->setChecked(1); break; default : m_Controls->m_Clip0->setChecked(1); } // handle fiber property observers if (m_Color.IsNotNull()) m_Color->RemoveObserver(m_ColorPropertyObserverTag); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkControlVisualizationPropertiesView::SetCustomColor ); m_Color = dynamic_cast(node->GetProperty("color", nullptr)); if (m_Color.IsNotNull()) m_ColorPropertyObserverTag = m_Color->AddObserver( itk::ModifiedEvent(), command ); m_Controls->m_BundleControlsFrame->setVisible(true); if(m_CurrentPickingNode != 0 && node.GetPointer() != m_CurrentPickingNode) { m_Controls->m_Crosshair->setEnabled(false); } else { m_Controls->m_Crosshair->setEnabled(true); } int width; node->GetIntProperty("shape.linewidth", width); m_Controls->m_LineWidth->setValue(width); float radius; node->GetFloatProperty("shape.tuberadius", radius); m_Controls->m_TubeWidth->setValue(radius); float range; node->GetFloatProperty("Fiber2DSliceThickness",range); mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); mitk::BaseGeometry::Pointer geo = fib->GetGeometry(); mitk::ScalarType max = geo->GetExtentInMM(0); max = std::max(max, geo->GetExtentInMM(1)); max = std::max(max, geo->GetExtentInMM(2)); m_Controls->m_FiberThicknessSlider->setMaximum(max * 10); m_Controls->m_FiberThicknessSlider->setValue(range * 10); } - else if(dynamic_cast(nodeData) || dynamic_cast(nodeData)) + else if(dynamic_cast(nodeData) || dynamic_cast(nodeData) || dynamic_cast(nodeData)) { m_Controls->m_ImageControlsFrame->setVisible(true); m_Controls->m_NumberGlyphsFrame->setVisible(true); m_Controls->m_GlyphFrame->setVisible(true); m_Controls->m_NormalizationFrame->setVisible(true); if(m_NodeUsedForOdfVisualization.IsNotNull()) { m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_S", false); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_C", false); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_T", false); } m_NodeUsedForOdfVisualization = node; m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_S", m_GlyIsOn_S); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_C", m_GlyIsOn_C); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_T", m_GlyIsOn_T); if (dynamic_cast(nodeData)) { m_Controls->m_NormalizationDropdown->setVisible(false); m_Controls->m_NormalizationLabel->setVisible(false); } else { m_Controls->m_NormalizationDropdown->setVisible(true); m_Controls->m_NormalizationLabel->setVisible(true); } int val; node->GetIntProperty("ShowMaxNumber", val); m_Controls->m_ShowMaxNumber->setValue(val); - m_Controls->m_NormalizationDropdown - ->setCurrentIndex(dynamic_cast(node->GetProperty("Normalization")) - ->GetValueAsId()); + m_Controls->m_NormalizationDropdown->setCurrentIndex(dynamic_cast(node->GetProperty("Normalization"))->GetValueAsId()); float fval; node->GetFloatProperty("Scaling",fval); m_Controls->m_ScalingFactor->setValue(fval); - m_Controls->m_AdditionalScaling - ->setCurrentIndex(dynamic_cast(node->GetProperty("ScaleBy"))->GetValueAsId()); + m_Controls->m_AdditionalScaling->setCurrentIndex(dynamic_cast(node->GetProperty("ScaleBy"))->GetValueAsId()); bool switchTensorViewValue = false; node->GetBoolProperty( "DiffusionCore.Rendering.OdfVtkMapper.SwitchTensorView", switchTensorViewValue ); bool colourisationModeBit = false; node->GetBoolProperty("DiffusionCore.Rendering.OdfVtkMapper.ColourisationModeBit", colourisationModeBit ); m_Controls->m_OdfColorBox->setCurrentIndex(colourisationModeBit); numOdfImages++; } else if(dynamic_cast(nodeData)) { PlanarFigureFocus(); } else if( dynamic_cast(nodeData) ) { m_Controls->m_ImageControlsFrame->setVisible(true); m_Controls->m_TSMenu->setVisible(true); } } if( nodes.empty() ) { return; } mitk::DataNode::Pointer node = nodes.at(0); if( node.IsNull() ) { return; } QMenu *myMenu = m_MyMenu; myMenu->clear(); QActionGroup* thickSlicesActionGroup = new QActionGroup(myMenu); thickSlicesActionGroup->setExclusive(true); int currentTSMode = 0; { mitk::ResliceMethodProperty::Pointer m = dynamic_cast(node->GetProperty( "reslice.thickslices" )); if( m.IsNotNull() ) currentTSMode = m->GetValueAsId(); } int maxTS = 30; for (auto node: nodes) { mitk::Image* image = dynamic_cast(node->GetData()); if (image) { int size = std::max(image->GetDimension(0), std::max(image->GetDimension(1), image->GetDimension(2))); if (size>maxTS) { maxTS=size; } } } maxTS /= 2; int currentNum = 0; { mitk::IntProperty::Pointer m = dynamic_cast(node->GetProperty( "reslice.thickslices.num" )); if( m.IsNotNull() ) { currentNum = m->GetValue(); if(currentNum < 0) { currentNum = 0; } if(currentNum > maxTS) { currentNum = maxTS; } } } if(currentTSMode==0) { currentNum=0; } QSlider *m_TSSlider = new QSlider(myMenu); m_TSSlider->setMinimum(0); m_TSSlider->setMaximum(maxTS-1); m_TSSlider->setValue(currentNum); m_TSSlider->setOrientation(Qt::Horizontal); connect( m_TSSlider, SIGNAL( valueChanged(int) ), this, SLOT( OnTSNumChanged(int) ) ); QHBoxLayout* _TSLayout = new QHBoxLayout; _TSLayout->setContentsMargins(4,4,4,4); _TSLayout->addWidget(m_TSSlider); _TSLayout->addWidget(m_TSLabel=new QLabel(QString::number(currentNum*2+1),myMenu)); QWidget* _TSWidget = new QWidget; _TSWidget->setLayout(_TSLayout); QActionGroup* thickSliceModeActionGroup = new QActionGroup(myMenu); thickSliceModeActionGroup->setExclusive(true); QWidgetAction *m_TSSliderAction = new QWidgetAction(myMenu); m_TSSliderAction->setDefaultWidget(_TSWidget); myMenu->addAction(m_TSSliderAction); QAction* mipThickSlicesAction = new QAction(myMenu); mipThickSlicesAction->setActionGroup(thickSliceModeActionGroup); mipThickSlicesAction->setText("MIP (max. intensity proj.)"); mipThickSlicesAction->setCheckable(true); mipThickSlicesAction->setChecked(currentThickSlicesMode==1); mipThickSlicesAction->setData(1); myMenu->addAction( mipThickSlicesAction ); QAction* sumThickSlicesAction = new QAction(myMenu); sumThickSlicesAction->setActionGroup(thickSliceModeActionGroup); sumThickSlicesAction->setText("SUM (sum intensity proj.)"); sumThickSlicesAction->setCheckable(true); sumThickSlicesAction->setChecked(currentThickSlicesMode==2); sumThickSlicesAction->setData(2); myMenu->addAction( sumThickSlicesAction ); QAction* weightedThickSlicesAction = new QAction(myMenu); weightedThickSlicesAction->setActionGroup(thickSliceModeActionGroup); weightedThickSlicesAction->setText("WEIGHTED (gaussian proj.)"); weightedThickSlicesAction->setCheckable(true); weightedThickSlicesAction->setChecked(currentThickSlicesMode==3); weightedThickSlicesAction->setData(3); myMenu->addAction( weightedThickSlicesAction ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); connect( thickSliceModeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(OnThickSlicesModeSelected(QAction*)) ); } void QmitkControlVisualizationPropertiesView::VisibleOdfsON_S() { m_GlyIsOn_S = m_Controls->m_VisibleOdfsON_S->isChecked(); if (m_NodeUsedForOdfVisualization.IsNull()) { MITK_WARN << "ODF visualization activated but m_NodeUsedForOdfVisualization is nullptr"; return; } m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_S", m_GlyIsOn_S); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Visible() { mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow) { m_SliceChangeListener.RenderWindowPartActivated(renderWindow); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } } void QmitkControlVisualizationPropertiesView::Hidden() { } void QmitkControlVisualizationPropertiesView::Activated() { mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow) { m_SliceChangeListener.RenderWindowPartActivated(renderWindow); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } } void QmitkControlVisualizationPropertiesView::Deactivated() { } void QmitkControlVisualizationPropertiesView::FlipPeaks() { if (m_SelectedNode.IsNull() || dynamic_cast(m_SelectedNode->GetData())==nullptr) return; std::string name = m_SelectedNode->GetName(); mitk::Image::Pointer image = dynamic_cast(m_SelectedNode->GetData()); typedef mitk::ImageToItk< mitk::PeakImage::ItkPeakImageType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(image); caster->Update(); mitk::PeakImage::ItkPeakImageType::Pointer itkImg = caster->GetOutput(); itk::FlipPeaksFilter< float >::Pointer flipper = itk::FlipPeaksFilter< float >::New(); flipper->SetInput(itkImg); flipper->SetFlipX(m_Controls->m_FlipPeaksX->isChecked()); flipper->SetFlipY(m_Controls->m_FlipPeaksY->isChecked()); flipper->SetFlipZ(m_Controls->m_FlipPeaksZ->isChecked()); flipper->Update(); mitk::Image::Pointer resultImage = dynamic_cast(mitk::PeakImage::New().GetPointer()); mitk::CastToMitkImage(flipper->GetOutput(), resultImage); resultImage->SetVolume(flipper->GetOutput()->GetBufferPointer()); m_SelectedNode->SetData(resultImage); m_SelectedNode->SetName(name); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Toggle3DClipping(bool enabled) { if (!enabled || m_SelectedNode.IsNull() || dynamic_cast(m_SelectedNode->GetData())==nullptr) return; m_SelectedNode->SetBoolProperty( "Fiber3DClippingPlaneFlip", m_Controls->m_FlipClipBox->isChecked() ); if (m_Controls->m_Clip0->isChecked()) { m_SelectedNode->SetIntProperty( "Fiber3DClippingPlaneId", 0 ); Set3DClippingPlane(true, m_SelectedNode, ""); } else if (m_Controls->m_Clip1->isChecked()) { m_SelectedNode->SetIntProperty( "Fiber3DClippingPlaneId", 1 ); Set3DClippingPlane(false, m_SelectedNode, "axial"); } else if (m_Controls->m_Clip2->isChecked()) { m_SelectedNode->SetIntProperty( "Fiber3DClippingPlaneId", 2 ); Set3DClippingPlane(false, m_SelectedNode, "sagittal"); } else if (m_Controls->m_Clip3->isChecked()) { m_SelectedNode->SetIntProperty( "Fiber3DClippingPlaneId", 3 ); Set3DClippingPlane(false, m_SelectedNode, "coronal"); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::OnSliceChanged() { mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetDataStorage()->GetAll(); for (unsigned int i=0; iSize(); ++i) { mitk::DataNode::Pointer node = nodes->GetElement(i); int plane_id = -1; node->GetIntProperty("Fiber3DClippingPlaneId", plane_id); if (plane_id==1) Set3DClippingPlane(false, node, "axial"); else if (plane_id==2) Set3DClippingPlane(false, node, "sagittal"); else if (plane_id==3) Set3DClippingPlane(false, node, "coronal"); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Set3DClippingPlane(bool disable, mitk::DataNode* node, std::string plane) { mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow && node && dynamic_cast(node->GetData())) { mitk::Vector3D planeNormal; planeNormal.Fill(0.0); if (!disable) { mitk::SliceNavigationController* slicer = renderWindow->GetQmitkRenderWindow(QString(plane.c_str()))->GetSliceNavigationController(); mitk::PlaneGeometry::ConstPointer planeGeo = slicer->GetCurrentPlaneGeometry(); //generate according cutting planes based on the view position planeNormal = planeGeo->GetNormal(); float tmp1 = planeGeo->GetOrigin()[0] * planeNormal[0]; float tmp2 = planeGeo->GetOrigin()[1] * planeNormal[1]; float tmp3 = planeGeo->GetOrigin()[2] * planeNormal[2]; float distance = tmp1 + tmp2 + tmp3; //attention, correct normalvector planeNormal *= distance; if (distance<0) node->SetBoolProperty( "Fiber3DClippingPlaneSecondFlip", true ); else node->SetBoolProperty( "Fiber3DClippingPlaneSecondFlip", false ); } node->SetProperty( "Fiber3DClippingPlane", mitk::Vector3DProperty::New( planeNormal ) ); dynamic_cast(node->GetData())->RequestUpdate(); } } void QmitkControlVisualizationPropertiesView::VisibleOdfsON_T() { m_GlyIsOn_T = m_Controls->m_VisibleOdfsON_T->isChecked(); if (m_NodeUsedForOdfVisualization.IsNull()) { MITK_WARN << "ODF visualization activated but m_NodeUsedForOdfVisualization is nullptr"; return; } m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_T", m_GlyIsOn_T); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::VisibleOdfsON_C() { m_GlyIsOn_C = m_Controls->m_VisibleOdfsON_C->isChecked(); if (m_NodeUsedForOdfVisualization.IsNull()) { MITK_WARN << "ODF visualization activated but m_NodeUsedForOdfVisualization is nullptr"; return; } m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_C", m_GlyIsOn_C); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } bool QmitkControlVisualizationPropertiesView::IsPlaneRotated() { mitk::Image* currentImage = dynamic_cast( m_NodeUsedForOdfVisualization->GetData() ); if( currentImage == nullptr ) { MITK_ERROR << " Casting problems. Returning false"; return false; } mitk::Vector3D imageNormal0 = currentImage->GetSlicedGeometry()->GetAxisVector(0); mitk::Vector3D imageNormal1 = currentImage->GetSlicedGeometry()->GetAxisVector(1); mitk::Vector3D imageNormal2 = currentImage->GetSlicedGeometry()->GetAxisVector(2); imageNormal0.Normalize(); imageNormal1.Normalize(); imageNormal2.Normalize(); auto renderWindowPart = this->GetRenderWindowPart(); double eps = 0.000001; // for all 2D renderwindows of the render window part check alignment { mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast ( renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer()->GetCurrentWorldPlaneGeometry() ); if (displayPlane.IsNull()) { return false; } mitk::Vector3D normal = displayPlane->GetNormal(); normal.Normalize(); int test = 0; if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal0.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal1.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal2.GetVnlVector()))-1) > eps ) { test++; } if (test==3) { return true; } } { mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast ( renderWindowPart->GetQmitkRenderWindow("sagittal")->GetRenderer()->GetCurrentWorldPlaneGeometry() ); if (displayPlane.IsNull()) { return false; } mitk::Vector3D normal = displayPlane->GetNormal(); normal.Normalize(); int test = 0; if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal0.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal1.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal2.GetVnlVector()))-1) > eps ) { test++; } if (test==3) { return true; } } { mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast ( renderWindowPart->GetQmitkRenderWindow("coronal")->GetRenderer()->GetCurrentWorldPlaneGeometry() ); if (displayPlane.IsNull()) { return false; } mitk::Vector3D normal = displayPlane->GetNormal(); normal.Normalize(); int test = 0; if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal0.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal1.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal2.GetVnlVector()))-1) > eps ) { test++; } if (test==3) { return true; } } return false; } void QmitkControlVisualizationPropertiesView::ShowMaxNumberChanged() { int maxNr = m_Controls->m_ShowMaxNumber->value(); if ( maxNr < 1 ) { m_Controls->m_ShowMaxNumber->setValue( 1 ); maxNr = 1; } if ( dynamic_cast(m_SelectedNode->GetData()) - || dynamic_cast(m_SelectedNode->GetData()) ) + || dynamic_cast(m_SelectedNode->GetData()) + || dynamic_cast(m_SelectedNode->GetData()) ) { m_SelectedNode->SetIntProperty("ShowMaxNumber", maxNr); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::NormalizationDropdownChanged(int normDropdown) { typedef mitk::OdfNormalizationMethodProperty PropType; PropType::Pointer normMeth = PropType::New(); switch(normDropdown) { case 0: normMeth->SetNormalizationToMinMax(); break; case 1: normMeth->SetNormalizationToMax(); break; case 2: normMeth->SetNormalizationToNone(); break; case 3: normMeth->SetNormalizationToGlobalMax(); break; default: normMeth->SetNormalizationToMinMax(); } if ( dynamic_cast(m_SelectedNode->GetData()) - || dynamic_cast(m_SelectedNode->GetData()) ) + || dynamic_cast(m_SelectedNode->GetData()) + || dynamic_cast(m_SelectedNode->GetData()) ) { m_SelectedNode->SetProperty("Normalization", normMeth.GetPointer()); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::ScalingFactorChanged(double scalingFactor) { if ( dynamic_cast(m_SelectedNode->GetData()) - || dynamic_cast(m_SelectedNode->GetData()) ) + || dynamic_cast(m_SelectedNode->GetData()) + || dynamic_cast(m_SelectedNode->GetData()) ) { m_SelectedNode->SetFloatProperty("Scaling", scalingFactor); } if (auto renderWindowPart = this->GetRenderWindowPart()) { renderWindowPart->RequestUpdate(); } } void QmitkControlVisualizationPropertiesView::AdditionalScaling(int additionalScaling) { typedef mitk::OdfScaleByProperty PropType; PropType::Pointer scaleBy = PropType::New(); switch(additionalScaling) { case 0: scaleBy->SetScaleByNothing(); break; case 1: scaleBy->SetScaleByGFA(); //m_Controls->params_frame->setVisible(true); break; #ifdef DIFFUSION_IMAGING_EXTENDED case 2: scaleBy->SetScaleByPrincipalCurvature(); // commented in for SPIE paper, Principle curvature scaling //m_Controls->params_frame->setVisible(true); break; #endif default: scaleBy->SetScaleByNothing(); } if ( dynamic_cast(m_SelectedNode->GetData()) - || dynamic_cast(m_SelectedNode->GetData()) ) + || dynamic_cast(m_SelectedNode->GetData()) + || dynamic_cast(m_SelectedNode->GetData()) ) { m_SelectedNode->SetProperty("ScaleBy", scaleBy.GetPointer()); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Fiber2DfadingEFX() { if (m_SelectedNode && dynamic_cast(m_SelectedNode->GetData()) ) { bool currentMode; m_SelectedNode->GetBoolProperty("Fiber2DfadeEFX", currentMode); m_SelectedNode->SetProperty("Fiber2DfadeEFX", mitk::BoolProperty::New(!currentMode)); dynamic_cast(m_SelectedNode->GetData())->RequestUpdate2D(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::FiberSlicingThickness2D() { if (m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { float fibThickness = m_Controls->m_FiberThicknessSlider->value() * 0.1; float currentThickness = 0; m_SelectedNode->GetFloatProperty("Fiber2DSliceThickness", currentThickness); if ( fabs(fibThickness-currentThickness) < 0.001 ) { return; } m_SelectedNode->SetProperty("Fiber2DSliceThickness", mitk::FloatProperty::New(fibThickness)); dynamic_cast(m_SelectedNode->GetData())->RequestUpdate2D(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::FiberSlicingUpdateLabel(int value) { QString label = "Range %1 mm"; label = label.arg(value * 0.1); m_Controls->label_range->setText(label); FiberSlicingThickness2D(); } void QmitkControlVisualizationPropertiesView::SetCustomColor(const itk::EventObject& /*e*/) { if(m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { float color[3]; m_SelectedNode->GetColor(color); mitk::FiberBundle::Pointer fib = dynamic_cast(m_SelectedNode->GetData()); fib->SetFiberColors(color[0]*255, color[1]*255, color[2]*255); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if (m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { float color[3]; m_SelectedNode->GetColor(color); mitk::PeakImage::Pointer img = dynamic_cast(m_SelectedNode->GetData()); img->SetCustomColor(color[0]*255, color[1]*255, color[2]*255); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::ResetColoring() { if(m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { mitk::FiberBundle::Pointer fib = dynamic_cast(m_SelectedNode->GetData()); fib->ColorFibersByOrientation(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if(m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { mitk::PeakImage::Pointer fib = dynamic_cast(m_SelectedNode->GetData()); fib->ColorByOrientation(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::PlanarFigureFocus() { if(m_SelectedNode) { mitk::PlanarFigure* _PlanarFigure = 0; _PlanarFigure = dynamic_cast (m_SelectedNode->GetData()); if (_PlanarFigure && _PlanarFigure->GetPlaneGeometry()) { QmitkRenderWindow* selectedRenderWindow = 0; bool PlanarFigureInitializedWindow = false; auto renderWindowPart = this->GetRenderWindowPart(OPEN); QmitkRenderWindow* axialRenderWindow = renderWindowPart->GetQmitkRenderWindow("axial"); if (m_SelectedNode->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, axialRenderWindow->GetRenderer())) { selectedRenderWindow = axialRenderWindow; } QmitkRenderWindow* sagittalRenderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal"); if (!selectedRenderWindow && m_SelectedNode->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, sagittalRenderWindow->GetRenderer())) { selectedRenderWindow = sagittalRenderWindow; } QmitkRenderWindow* coronalRenderWindow = renderWindowPart->GetQmitkRenderWindow("coronal"); if (!selectedRenderWindow && m_SelectedNode->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, coronalRenderWindow->GetRenderer())) { selectedRenderWindow = coronalRenderWindow; } QmitkRenderWindow* _3DRenderWindow = renderWindowPart->GetQmitkRenderWindow("3d"); if (!selectedRenderWindow && m_SelectedNode->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, _3DRenderWindow->GetRenderer())) { selectedRenderWindow = _3DRenderWindow; } const mitk::PlaneGeometry* _PlaneGeometry = _PlanarFigure->GetPlaneGeometry(); mitk::VnlVector normal = _PlaneGeometry->GetNormalVnl(); mitk::PlaneGeometry::ConstPointer worldGeometry1 = axialRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); mitk::PlaneGeometry::ConstPointer _Plane1 = dynamic_cast( worldGeometry1.GetPointer() ); mitk::VnlVector normal1 = _Plane1->GetNormalVnl(); mitk::PlaneGeometry::ConstPointer worldGeometry2 = sagittalRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); mitk::PlaneGeometry::ConstPointer _Plane2 = dynamic_cast( worldGeometry2.GetPointer() ); mitk::VnlVector normal2 = _Plane2->GetNormalVnl(); mitk::PlaneGeometry::ConstPointer worldGeometry3 = coronalRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); mitk::PlaneGeometry::ConstPointer _Plane3 = dynamic_cast( worldGeometry3.GetPointer() ); mitk::VnlVector normal3 = _Plane3->GetNormalVnl(); normal[0] = fabs(normal[0]); normal[1] = fabs(normal[1]); normal[2] = fabs(normal[2]); normal1[0] = fabs(normal1[0]); normal1[1] = fabs(normal1[1]); normal1[2] = fabs(normal1[2]); normal2[0] = fabs(normal2[0]); normal2[1] = fabs(normal2[1]); normal2[2] = fabs(normal2[2]); normal3[0] = fabs(normal3[0]); normal3[1] = fabs(normal3[1]); normal3[2] = fabs(normal3[2]); double ang1 = angle(normal, normal1); double ang2 = angle(normal, normal2); double ang3 = angle(normal, normal3); if(ang1 < ang2 && ang1 < ang3) { selectedRenderWindow = axialRenderWindow; } else { if(ang2 < ang3) { selectedRenderWindow = sagittalRenderWindow; } else { selectedRenderWindow = coronalRenderWindow; } } // make node visible if (selectedRenderWindow) { const mitk::Point3D& centerP = _PlaneGeometry->GetOrigin(); selectedRenderWindow->GetSliceNavigationController()->ReorientSlices( centerP, _PlaneGeometry->GetNormal()); } } // set interactor for new node (if not already set) mitk::PlanarFigureInteractor::Pointer figureInteractor = dynamic_cast(m_SelectedNode->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( m_SelectedNode ); } m_SelectedNode->SetProperty("planarfigure.iseditable",mitk::BoolProperty::New(true)); } } void QmitkControlVisualizationPropertiesView::SetInteractor() { // BUG 19179 // typedef std::vector Container; // Container _NodeSet = this->GetDataManagerSelection(); // mitk::DataNode* node = 0; // mitk::FiberBundle* bundle = 0; // mitk::FiberBundleInteractor::Pointer bundleInteractor = 0; // // finally add all nodes to the model // for(Container::const_iterator it=_NodeSet.begin(); it!=_NodeSet.end() // ; it++) // { // node = const_cast(*it); // bundle = dynamic_cast(node->GetData()); // if(bundle) // { // bundleInteractor = dynamic_cast(node->GetInteractor()); // if(bundleInteractor.IsNotNull()) // mitk::GlobalInteraction::GetInstance()->RemoveInteractor(bundleInteractor); // if(!m_Controls->m_Crosshair->isChecked()) // { // m_Controls->m_Crosshair->setChecked(false); // this->GetActiveStdMultiWidget()->GetRenderWindow4()->setCursor(Qt::ArrowCursor); // m_CurrentPickingNode = 0; // } // else // { // m_Controls->m_Crosshair->setChecked(true); // bundleInteractor = mitk::FiberBundleInteractor::New("FiberBundleInteractor", node); // mitk::GlobalInteraction::GetInstance()->AddInteractor(bundleInteractor); // this->GetActiveStdMultiWidget()->GetRenderWindow4()->setCursor(Qt::CrossCursor); // m_CurrentPickingNode = node; // } // } // } } void QmitkControlVisualizationPropertiesView::TubeRadiusChanged() { if(m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { float newRadius = m_Controls->m_TubeWidth->value(); if (newRadius>0) m_SelectedNode->SetBoolProperty( "light.enable_light", true); else m_SelectedNode->SetBoolProperty( "light.enable_light", false); m_SelectedNode->SetFloatProperty("shape.tuberadius", newRadius); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::LineWidthChanged() { if(m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { int newWidth = m_Controls->m_LineWidth->value(); int currentWidth = 0; m_SelectedNode->GetIntProperty("shape.linewidth", currentWidth); if (currentWidth==newWidth) return; m_SelectedNode->SetIntProperty("shape.linewidth", newWidth); dynamic_cast(m_SelectedNode->GetData())->RequestUpdate(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::Welcome() { berry::PlatformUI::GetWorkbench()->GetIntroManager() ->ShowIntro(GetSite()->GetWorkbenchWindow(), false); } void QmitkControlVisualizationPropertiesView::OnColourisationModeChanged() { if( m_SelectedNode && m_NodeUsedForOdfVisualization.IsNotNull() ) { m_SelectedNode->SetProperty( "DiffusionCore.Rendering.OdfVtkMapper.ColourisationModeBit", mitk::BoolProperty::New( m_Controls->m_OdfColorBox->currentIndex() ) ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else { MITK_DEBUG << "QmitkControlVisualizationPropertiesView::OnColourisationModeChanged() was called but m_NodeUsedForOdfVisualization was Null."; } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/mitkPluginActivator.cpp index fa8f61b734..48fd9d067e 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/mitkPluginActivator.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/mitkPluginActivator.cpp @@ -1,75 +1,78 @@ /*=================================================================== 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 "mitkPluginActivator.h" #include "src/internal/Perspectives/QmitkDiffusionDefaultPerspective.h" #include "src/internal/Perspectives/QmitkSegmentationPerspective.h" #include "src/internal/QmitkDiffusionDicomImportView.h" #include "src/internal/QmitkDicomTractogramTagEditorView.h" #include "src/internal/QmitkControlVisualizationPropertiesView.h" #include "QmitkNodeDescriptorManager.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateProperty.h" #include "mitkNodePredicateIsDWI.h" #include namespace mitk { void PluginActivator::start(ctkPluginContext* context) { BERRY_REGISTER_EXTENSION_CLASS(QmitkDiffusionDefaultPerspective, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkSegmentationPerspective, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkDiffusionDicomImport, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkDicomTractogramTagEditorView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkControlVisualizationPropertiesView, context) // Q_UNUSED(context) QmitkNodeDescriptorManager* manager = QmitkNodeDescriptorManager::GetInstance(); mitk::NodePredicateIsDWI::Pointer isDiffusionImage = mitk::NodePredicateIsDWI::New(); QmitkNodeDescriptor* desc = new QmitkNodeDescriptor(QObject::tr("DiffusionImage"), QString(":/QmitkDiffusionImaging/DiffData24.png"), isDiffusionImage, manager); manager->AddDescriptor(desc); mitk::NodePredicateDataType::Pointer isTensorImage = mitk::NodePredicateDataType::New("TensorImage"); manager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("TensorImage"), QString(":/QmitkDiffusionImaging/tensor.png"), isTensorImage, manager)); mitk::NodePredicateDataType::Pointer isOdfImage = mitk::NodePredicateDataType::New("OdfImage"); manager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("OdfImage"), QString(":/QmitkDiffusionImaging/odf.png"), isOdfImage, manager)); + mitk::NodePredicateDataType::Pointer isShImage = mitk::NodePredicateDataType::New("ShImage"); + manager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("ShImage"), QString(":/QmitkDiffusionImaging/sh.png"), isShImage, manager)); + mitk::NodePredicateDataType::Pointer isFiberBundle = mitk::NodePredicateDataType::New("FiberBundle"); manager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("FiberBundle"), QString(":/QmitkDiffusionImaging/tractogram.png"), isFiberBundle, manager)); mitk::NodePredicateDataType::Pointer isPeakImage = mitk::NodePredicateDataType::New("PeakImage"); manager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("PeakImage"), QString(":/QmitkDiffusionImaging/odf_peaks.png"), isPeakImage, manager)); mitk::NodePredicateDataType::Pointer isConnectomicsNetwork = mitk::NodePredicateDataType::New("ConnectomicsNetwork"); manager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("ConnectomicsNetwork"), QString(":/QmitkDiffusionImaging/ConnectomicsNetwork.png"), isConnectomicsNetwork, manager)); mitk::NodePredicateDataType::Pointer isTractographyForestNetwork = mitk::NodePredicateDataType::New("TractographyForest"); manager->AddDescriptor(new QmitkNodeDescriptor(QObject::tr("TractographyForest"), QString(":/QmitkDiffusionImaging/ml_tractogram.png"), isTractographyForestNetwork, manager)); } void PluginActivator::stop(ctkPluginContext* context) { Q_UNUSED(context) } }