diff --git a/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h b/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h index d3bbcf0a67..1889ef0280 100644 --- a/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h +++ b/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h @@ -1,157 +1,167 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkConcentrationCurveGenerator_h #define mitkConcentrationCurveGenerator_h #include #include #include "mitkConvertToConcentrationAbsoluteFunctor.h" #include "mitkConvertToConcentrationRelativeFunctor.h" #include "MitkPharmacokineticsExports.h" namespace mitk { /** \class ConcentrationCurveGenerator * \brief Converts a given 4D mitk::Image with MR signal values into a 4D mitk::Image with corresponding contrast agent concentration values * * From a given 4D image, the Generator takes the 3D image of the first time point as baseline image. It then loops over all time steps, casts * the current 3D image to itk and passes it to the ConvertToconcentrationFunctor. The returned 3D image has now values of concentration type and is stored at its timepoint * in the return image. */ class MITKPHARMACOKINETICS_EXPORT ConcentrationCurveGenerator : public itk::Object { public: mitkClassMacroItkParent(ConcentrationCurveGenerator, itk::Object); itkNewMacro(Self); //typedef itk::Image ImageType; typedef itk::Image ConvertedImageType; /** Getter and Setter for 4D mitk::Image*/ itkSetConstObjectMacro(DynamicImage,Image); itkGetConstObjectMacro(DynamicImage,Image); /** Parameters Relevant for conversion Calculation; Have to be Set externally (Sequence Dependend)*/ itkSetMacro(RelaxationTime, double); itkGetConstReferenceMacro(RelaxationTime, double); itkSetMacro(Relaxivity, double); itkGetConstReferenceMacro(Relaxivity, double); itkSetMacro(RecoveryTime, double); itkGetConstReferenceMacro(RecoveryTime, double); + itkSetMacro(RepetitionTime, double); + itkGetConstReferenceMacro(RepetitionTime, double); + itkSetMacro(FlipAngle, double); itkGetConstReferenceMacro(FlipAngle, double); + itkSetMacro(FlipAnglePDW, double); + itkGetConstReferenceMacro(FlipAnglePDW, double); + itkSetMacro(Factor, double); itkGetConstReferenceMacro(Factor, double); - /** Getter and Setter for T10 Map image*/ - itkSetConstObjectMacro(T10Image,Image); - itkGetConstObjectMacro(T10Image,Image); + /** Getter and Setter for PDW Map image*/ + itkSetConstObjectMacro(PDWImage,Image); + itkGetConstObjectMacro(PDWImage,Image); itkSetMacro(T2Factor, double); itkGetConstReferenceMacro(T2Factor, double); itkSetMacro(T2EchoTime, double); itkGetConstReferenceMacro(T2EchoTime, double); /** @brief Calls Convert and returns the 4D mitk::image in Concentration units*/ itkSetMacro(BaselineStartTimeStep, unsigned int); itkGetConstReferenceMacro(BaselineStartTimeStep, unsigned int); itkSetMacro(BaselineEndTimeStep, unsigned int); itkGetConstReferenceMacro(BaselineEndTimeStep, unsigned int); itkSetMacro(isTurboFlashSequence,bool); itkGetConstReferenceMacro(isTurboFlashSequence,bool); itkSetMacro(AbsoluteSignalEnhancement,bool); itkGetConstReferenceMacro(AbsoluteSignalEnhancement,bool); itkSetMacro(RelativeSignalEnhancement,bool); itkGetConstReferenceMacro(RelativeSignalEnhancement,bool); itkSetMacro(UsingT1Map,bool); itkGetConstReferenceMacro(UsingT1Map,bool); itkSetMacro(isT2weightedImage,bool); itkGetConstReferenceMacro(isT2weightedImage,bool); Image::Pointer GetConvertedImage(); protected: ConcentrationCurveGenerator(); ~ConcentrationCurveGenerator() override; template mitk::Image::Pointer convertToConcentration(const itk::Image *itkInputImage, const itk::Image *itkBaselineImage); /** Calls ConvertToconcentrationFunctor for passed 3D itk::image*/ mitk::Image::Pointer ConvertSignalToConcentrationCurve(const mitk::Image* inputImage, const mitk::Image* baselineImage); /** @brief Takes the 3D image of the first timepoint to set as baseline image*/ void PrepareBaselineImage(); template void CalculateAverageBaselineImage(const itk::Image *itkBaselineImage); /** @brief loops over all timepoints, casts the current timepoint 3D mitk::image to itk and passes it to ConvertSignalToConcentrationCurve */ virtual void Convert(); private: Image::ConstPointer m_DynamicImage; Image::ConstPointer m_BaselineImage; - Image::ConstPointer m_T10Image; + Image::ConstPointer m_PDWImage; Image::Pointer m_ConvertSignalToConcentrationCurve_OutputImage; Image::Pointer m_ConvertedImage; bool m_isT2weightedImage; bool m_isTurboFlashSequence; bool m_AbsoluteSignalEnhancement; bool m_RelativeSignalEnhancement; bool m_UsingT1Map; double m_Factor; - //=Repetition Time TR + //=Recovery Time double m_RecoveryTime; + //=Repetition Time TR + double m_RepetitionTime; //= pre-CA T1 time double m_RelaxationTime; //= contrast agent relaxivity double m_Relaxivity; double m_FlipAngle; + double m_FlipAnglePDW; + double m_T2Factor; double m_T2EchoTime; // The baseline image is averaged from the signal within time step range [m_BaselineStartTimeStep, m_BaselineEndTimeStep]. // m_BaselineStartTimeStep is the first time frame, that is included into the baseline averaging (starting with 0). unsigned int m_BaselineStartTimeStep; // m_BaselinStopTimeStep is the last time frame, that is included into the baseline averaging. unsigned int m_BaselineEndTimeStep; }; } #endif diff --git a/Modules/Pharmacokinetics/include/mitkConvertToConcentrationViaT1Functor.h b/Modules/Pharmacokinetics/include/mitkConvertToConcentrationViaT1Functor.h index f39d68cbcc..a95b9e176d 100644 --- a/Modules/Pharmacokinetics/include/mitkConvertToConcentrationViaT1Functor.h +++ b/Modules/Pharmacokinetics/include/mitkConvertToConcentrationViaT1Functor.h @@ -1,79 +1,83 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkConvertToConcentrationViaT1Functor_h #define mitkConvertToConcentrationViaT1Functor_h #include "itkMath.h" #include "MitkPharmacokineticsExports.h" namespace mitk { template class MITKPHARMACOKINETICS_EXPORT ConvertToConcentrationViaT1CalcFunctor { public: - ConvertToConcentrationViaT1CalcFunctor(): m_relaxivity(0.0), m_TR(0.0), m_flipangle(0.0) {}; - ~ConvertToConcentrationViaT1CalcFunctor() {}; + ConvertToConcentrationViaT1CalcFunctor(): m_relaxivity(0.0), m_TR(0.0), m_flipangle(0.0), m_flipanglePDW(0.0) {}; + ~ConvertToConcentrationViaT1CalcFunctor() {}; - void initialize(double relaxivity, double TR, double flipangle) + void initialize(double relaxivity, double TR, double flipangle, double flipanglePDW) { - - m_relaxivity = relaxivity; - m_TR = TR; - m_flipangle = flipangle; + m_relaxivity = relaxivity; + m_TR = TR; + m_flipangle = flipangle; + m_flipanglePDW = flipanglePDW; } bool operator!=( const ConvertToConcentrationViaT1CalcFunctor & other) const { return !(*this == other); } bool operator==( const ConvertToConcentrationViaT1CalcFunctor & other) const { - return (this->m_relaxivity == other.m_relaxivity) && (this->m_TR == other.m_TR) && (this->m_flipangle == other.m_flipangle); + return (this->m_relaxivity == other.m_relaxivity) && (this->m_TR == other.m_TR) && (this->m_flipangle == other.m_flipangle) && (this->m_flipanglePDW == other.m_flipanglePDW); } - inline TOutputpixel operator()( const TInputPixel1 & value, const TInputPixel2 & baseline, const TInputPixel3 & nativeT1) + inline TOutputpixel operator()( const TInputPixel1 & value, const TInputPixel2 & baseline, const TInputPixel3 & pdw) { - TOutputpixel concentration(0); - double R10, R1; - if (baseline !=0 && nativeT1 != 0 && value != 0) - { - double s = (double) value/baseline; - R10 = (double) 1/nativeT1; - double tmp1 = log(1-s + s*exp(-R10*m_TR) - exp(-R10*m_TR)* cos(m_flipangle)); - double tmp2 = (1-s*cos(m_flipangle) + s*exp(-R10*m_TR)*cos(m_flipangle) - exp(-R10*m_TR)* cos(m_flipangle)); - - R1 = (double) -1/m_TR * tmp1/tmp2 ; - - concentration = (double) (R1 - R10)/ m_relaxivity; - } - else - { - concentration = 0; - } - - return concentration; + TOutputpixel concentration(0.0); + if (baseline != 0 && pdw != 0 && value != 0) + { + // calculate signal scaling factor S0 and pre-contrast T1 relaxation time T10 + const double a = pdw * sin(m_flipangle) / (baseline * sin(m_flipanglePDW)); + const double b = (a - 1.0) / (a * cos(m_flipanglePDW) - cos(m_flipangle)); + const double T10 = static_cast((-1.0) * m_TR / log(b)); + const double lambda = exp((-1.0) * m_TR / T10); + const double S0 = pdw * (1.0-lambda * cos(m_flipanglePDW)) / ((1.0 - lambda) * sin(m_flipanglePDW)); + + // calculate T1 + const double c = (value - S0 * sin(m_flipangle)) / (value * cos(m_flipangle) - S0 * sin(m_flipangle)); + const double T1 = static_cast((-1.0) * m_TR / log(c)); + + //calculate concentration + concentration = static_cast((1.0 / T1 - 1.0 / T10) / (m_relaxivity/1000.0)); + } + else + { + concentration = 0.0; + } + + return concentration; } private: - double m_relaxivity; - double m_TR; - double m_flipangle; - + double m_relaxivity; + double m_TR; + double m_flipangle; + double m_flipanglePDW; }; } #endif diff --git a/Modules/Pharmacokinetics/src/Common/mitkConcentrationCurveGenerator.cpp b/Modules/Pharmacokinetics/src/Common/mitkConcentrationCurveGenerator.cpp index f19254394b..f1c1198126 100644 --- a/Modules/Pharmacokinetics/src/Common/mitkConcentrationCurveGenerator.cpp +++ b/Modules/Pharmacokinetics/src/Common/mitkConcentrationCurveGenerator.cpp @@ -1,324 +1,394 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkConcentrationCurveGenerator.h" #include "mitkConvertToConcentrationTurboFlashFunctor.h" #include "mitkConvertT2ConcentrationFunctor.h" #include "mitkConvertToConcentrationViaT1Functor.h" #include "mitkImageTimeSelector.h" #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include "mitkModelBase.h" #include "mitkExtractTimeGrid.h" #include "mitkArbitraryTimeGeometry.h" #include "itkNaryAddImageFilter.h" #include "mitkImageAccessByItk.h" #include "itkImageIOBase.h" #include "itkBinaryFunctorImageFilter.h" #include "itkTernaryFunctorImageFilter.h" #include #include "itkMeanProjectionImageFilter.h" mitk::ConcentrationCurveGenerator::ConcentrationCurveGenerator() : m_isT2weightedImage(false), m_isTurboFlashSequence(false), - m_AbsoluteSignalEnhancement(false), m_RelativeSignalEnhancement(0.0), m_UsingT1Map(false), m_Factor(0.0), m_RecoveryTime(0.0), m_RelaxationTime(0.0), - m_Relaxivity(0.0), m_FlipAngle(0.0), m_T2Factor(0.0), m_T2EchoTime(0.0) + m_AbsoluteSignalEnhancement(false), m_RelativeSignalEnhancement(false), m_UsingT1Map(false), m_Factor(std::numeric_limits::quiet_NaN()), + m_RecoveryTime(std::numeric_limits::quiet_NaN()), m_RepetitionTime(std::numeric_limits::quiet_NaN()), + m_RelaxationTime(std::numeric_limits::quiet_NaN()), m_Relaxivity(std::numeric_limits::quiet_NaN()), + m_FlipAngle(std::numeric_limits::quiet_NaN()), m_FlipAnglePDW(std::numeric_limits::quiet_NaN()), + m_T2Factor(std::numeric_limits::quiet_NaN()), m_T2EchoTime(std::numeric_limits::quiet_NaN()) { } mitk::ConcentrationCurveGenerator::~ConcentrationCurveGenerator() { } mitk::Image::Pointer mitk::ConcentrationCurveGenerator::GetConvertedImage() { if(this->m_DynamicImage.IsNull()) { itkExceptionMacro( << "Dynamic Image not set!"); } else { Convert(); } return m_ConvertedImage; } void mitk::ConcentrationCurveGenerator::Convert() { mitk::Image::Pointer tempImage = mitk::Image::New(); mitk::PixelType pixeltype = mitk::MakeScalarPixelType(); tempImage->Initialize(pixeltype,*this->m_DynamicImage->GetTimeGeometry()); mitk::TimeGeometry::Pointer timeGeometry = (this->m_DynamicImage->GetTimeGeometry())->Clone(); tempImage->SetTimeGeometry(timeGeometry); PrepareBaselineImage(); mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(this->m_DynamicImage); for(unsigned int i = 0; i< this->m_DynamicImage->GetTimeSteps(); ++i) { imageTimeSelector->SetTimeNr(i); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkInputImage = imageTimeSelector->GetOutput(); mitk::Image::Pointer outputImage = mitk::Image::New(); outputImage = ConvertSignalToConcentrationCurve(mitkInputImage,this->m_BaselineImage); mitk::ImageReadAccessor accessor(outputImage); tempImage->SetVolume(accessor.GetData(), i); } this->m_ConvertedImage = tempImage; } void mitk::ConcentrationCurveGenerator::PrepareBaselineImage() { mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(this->m_DynamicImage); imageTimeSelector->SetTimeNr(0); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image::Pointer baselineImage; baselineImage = imageTimeSelector->GetOutput(); if (m_BaselineStartTimeStep == m_BaselineEndTimeStep) { this->m_BaselineImage = imageTimeSelector->GetOutput(); } else { try { AccessFixedDimensionByItk(this->m_DynamicImage, mitk::ConcentrationCurveGenerator::CalculateAverageBaselineImage, 4); } catch (itk::ExceptionObject & err) { std::cerr << "ExceptionObject in ConcentrationCurveGenerator::CalculateAverageBaselineImage caught!" << std::endl; std::cerr << err << std::endl; } } } template void mitk::ConcentrationCurveGenerator::CalculateAverageBaselineImage(const itk::Image *itkBaselineImage) { if (itkBaselineImage == NULL) { mitkThrow() << "Error in ConcentrationCurveGenerator::CalculateAverageBaselineImage. Input image is NULL."; } if (m_BaselineStartTimeStep > m_BaselineEndTimeStep) { mitkThrow() << "Error in ConcentrationCurveGenerator::CalculateAverageBaselineImage. End time point is before start time point."; } typedef itk::Image TPixel4DImageType; typedef itk::Image Double3DImageType; typedef itk::Image Double4DImageType; typedef itk::ExtractImageFilter ExtractImageFilterType; typedef itk::ExtractImageFilter Extract3DImageFilterType; typedef itk::MeanProjectionImageFilter MeanProjectionImageFilterType; typename MeanProjectionImageFilterType::Pointer MeanProjectionImageFilter = MeanProjectionImageFilterType::New(); typename Extract3DImageFilterType::Pointer Extract3DImageFilter = Extract3DImageFilterType::New(); typename TPixel4DImageType::RegionType region_input = itkBaselineImage->GetLargestPossibleRegion(); if (m_BaselineEndTimeStep > region_input.GetSize()[3]) { mitkThrow() << "Error in ConcentrationCurveGenerator::CalculateAverageBaselineImage. End time point is larger than total number of time points."; } typename ExtractImageFilterType::Pointer ExtractFilter = ExtractImageFilterType::New(); typename TPixel4DImageType::Pointer baselineTimeFrameImage = TPixel4DImageType::New(); typename TPixel4DImageType::RegionType extractionRegion; typename TPixel4DImageType::SizeType size_input_aux = region_input.GetSize(); size_input_aux[3] = m_BaselineEndTimeStep - m_BaselineStartTimeStep + 1; typename TPixel4DImageType::IndexType start_input_aux = region_input.GetIndex(); start_input_aux[3] = m_BaselineStartTimeStep; extractionRegion.SetSize(size_input_aux); extractionRegion.SetIndex(start_input_aux); ExtractFilter->SetExtractionRegion(extractionRegion); ExtractFilter->SetInput(itkBaselineImage); ExtractFilter->SetDirectionCollapseToSubmatrix(); try { ExtractFilter->Update(); } catch (itk::ExceptionObject & err) { std::cerr << "ExceptionObject caught!" << std::endl; std::cerr << err << std::endl; } baselineTimeFrameImage = ExtractFilter->GetOutput(); MeanProjectionImageFilter->SetProjectionDimension(3); MeanProjectionImageFilter->SetInput(baselineTimeFrameImage); try { MeanProjectionImageFilter->Update(); } catch (itk::ExceptionObject & err) { std::cerr << "ExceptionObject caught!" << std::endl; std::cerr << err << std::endl; } Extract3DImageFilter->SetInput(MeanProjectionImageFilter->GetOutput()); size_input_aux[3] = 0; start_input_aux[3] = 0; extractionRegion.SetSize(size_input_aux); extractionRegion.SetIndex(start_input_aux); Extract3DImageFilter->SetExtractionRegion(extractionRegion); Extract3DImageFilter->SetDirectionCollapseToSubmatrix(); try { Extract3DImageFilter->Update(); } catch (itk::ExceptionObject & err) { std::cerr << "ExceptionObject caught!" << std::endl; std::cerr << err << std::endl; } Image::Pointer mitkBaselineImage = Image::New(); CastToMitkImage(Extract3DImageFilter->GetOutput(), mitkBaselineImage); this->m_BaselineImage = mitkBaselineImage; } mitk::Image::Pointer mitk::ConcentrationCurveGenerator::ConvertSignalToConcentrationCurve(const mitk::Image* inputImage,const mitk::Image* baselineImage) { mitk::PixelType m_PixelType = inputImage->GetPixelType(); AccessTwoImagesFixedDimensionByItk(inputImage, baselineImage, mitk::ConcentrationCurveGenerator::convertToConcentration, 3); return m_ConvertSignalToConcentrationCurve_OutputImage; } template mitk::Image::Pointer mitk::ConcentrationCurveGenerator::convertToConcentration(const itk::Image *itkInputImage, const itk::Image *itkBaselineImage) { typedef itk::Image InputImageType; typedef itk::Image BaselineImageType; if (this->m_isT2weightedImage) { - typedef mitk::ConvertT2ConcentrationFunctor ConversionFunctorT2Type; - typedef itk::BinaryFunctorImageFilter FilterT2Type; + typedef mitk::ConvertT2ConcentrationFunctor ConversionFunctorT2Type; + typedef itk::BinaryFunctorImageFilter FilterT2Type; - ConversionFunctorT2Type ConversionT2Functor; + ConversionFunctorT2Type ConversionT2Functor; + + if (std::isnan(this->m_T2Factor)) + { + mitkThrow() << "The conversion factor k for T2-weighted images must be set."; + } + else if (std::isnan(this->m_T2EchoTime)) + { + mitkThrow() << "The echo time TE for T2-weighted images must be set."; + } + else + { ConversionT2Functor.initialize(this->m_T2Factor, this->m_T2EchoTime); + } - typename FilterT2Type::Pointer ConversionT2Filter = FilterT2Type::New(); + + + typename FilterT2Type::Pointer ConversionT2Filter = FilterT2Type::New(); ConversionT2Filter->SetFunctor(ConversionT2Functor); - ConversionT2Filter->SetInput1(itkInputImage); - ConversionT2Filter->SetInput2(itkBaselineImage); + ConversionT2Filter->SetInput1(itkInputImage); + ConversionT2Filter->SetInput2(itkBaselineImage); - ConversionT2Filter->Update(); + ConversionT2Filter->Update(); - m_ConvertSignalToConcentrationCurve_OutputImage = mitk::ImportItkImage(ConversionT2Filter->GetOutput())->Clone(); - } + m_ConvertSignalToConcentrationCurve_OutputImage = mitk::ImportItkImage(ConversionT2Filter->GetOutput())->Clone(); + } else { if(this->m_isTurboFlashSequence) { typedef mitk::ConvertToConcentrationTurboFlashFunctor ConversionFunctorTurboFlashType; typedef itk::BinaryFunctorImageFilter FilterTurboFlashType; ConversionFunctorTurboFlashType ConversionTurboFlashFunctor; - ConversionTurboFlashFunctor.initialize(this->m_RelaxationTime, this->m_Relaxivity, this->m_RecoveryTime); + + if (std::isnan(this->m_RelaxationTime)) + { + mitkThrow() << "The relaxation time must be set."; + } + else if (std::isnan(this->m_Relaxivity)) + { + mitkThrow() << "The relaxivity must be set."; + } + else if (std::isnan(this->m_RecoveryTime)) + { + mitkThrow() << "The recovery time must be set."; + } + else + { + ConversionTurboFlashFunctor.initialize(this->m_RelaxationTime, this->m_Relaxivity, this->m_RecoveryTime); + } + typename FilterTurboFlashType::Pointer ConversionTurboFlashFilter = FilterTurboFlashType::New(); ConversionTurboFlashFilter->SetFunctor(ConversionTurboFlashFunctor); ConversionTurboFlashFilter->SetInput1(itkInputImage); ConversionTurboFlashFilter->SetInput2(itkBaselineImage); ConversionTurboFlashFilter->Update(); m_ConvertSignalToConcentrationCurve_OutputImage = mitk::ImportItkImage(ConversionTurboFlashFilter->GetOutput())->Clone(); } else if(this->m_UsingT1Map) { - typename ConvertedImageType::Pointer itkT10Image = ConvertedImageType::New(); - mitk::CastToItkImage(m_T10Image, itkT10Image); + typename BaselineImageType::Pointer itkPDWImage = BaselineImageType::New(); + mitk::CastToItkImage(m_PDWImage, itkPDWImage); typedef mitk::ConvertToConcentrationViaT1CalcFunctor ConvertToConcentrationViaT1CalcFunctorType; - typedef itk::TernaryFunctorImageFilter FilterT1MapType; + typedef itk::TernaryFunctorImageFilter FilterT1MapType; ConvertToConcentrationViaT1CalcFunctorType ConversionT1MapFunctor; - ConversionT1MapFunctor.initialize(this->m_Relaxivity, this->m_RecoveryTime, this->m_FlipAngle); + + if (std::isnan(this->m_Relaxivity)) + { + mitkThrow() << "The relaxivity must be set."; + } + else if (std::isnan(this->m_RepetitionTime)) + { + mitkThrow() << "The repetition time must be set."; + } + else if (std::isnan(this->m_FlipAngle)) + { + mitkThrow() << "The flip angle must be set."; + } + else if (std::isnan(this->m_FlipAnglePDW)) + { + mitkThrow() << "The flip angle of the PDW image must be set."; + } + else + { + ConversionT1MapFunctor.initialize(this->m_Relaxivity, this->m_RepetitionTime, this->m_FlipAngle, this->m_FlipAnglePDW); + } typename FilterT1MapType::Pointer ConversionT1MapFilter = FilterT1MapType::New(); ConversionT1MapFilter->SetFunctor(ConversionT1MapFunctor); ConversionT1MapFilter->SetInput1(itkInputImage); ConversionT1MapFilter->SetInput2(itkBaselineImage); - ConversionT1MapFilter->SetInput3(itkT10Image); + ConversionT1MapFilter->SetInput3(itkPDWImage); ConversionT1MapFilter->Update(); m_ConvertSignalToConcentrationCurve_OutputImage = mitk::ImportItkImage(ConversionT1MapFilter->GetOutput())->Clone(); } else if(this->m_AbsoluteSignalEnhancement) { typedef mitk::ConvertToConcentrationAbsoluteFunctor ConversionFunctorAbsoluteType; typedef itk::BinaryFunctorImageFilter FilterAbsoluteType; ConversionFunctorAbsoluteType ConversionAbsoluteFunctor; - ConversionAbsoluteFunctor.initialize(this->m_Factor); + + if (std::isnan(this->m_Factor)) + { + mitkThrow() << "The conversion factor k must be set."; + } + else + { + ConversionAbsoluteFunctor.initialize(this->m_Factor); + } typename FilterAbsoluteType::Pointer ConversionAbsoluteFilter = FilterAbsoluteType::New(); ConversionAbsoluteFilter->SetFunctor(ConversionAbsoluteFunctor); ConversionAbsoluteFilter->SetInput1(itkInputImage); ConversionAbsoluteFilter->SetInput2(itkBaselineImage); ConversionAbsoluteFilter->Update(); m_ConvertSignalToConcentrationCurve_OutputImage = mitk::ImportItkImage(ConversionAbsoluteFilter->GetOutput())->Clone(); } else if(this->m_RelativeSignalEnhancement) { typedef mitk::ConvertToConcentrationRelativeFunctor ConversionFunctorRelativeType; typedef itk::BinaryFunctorImageFilter FilterRelativeType; ConversionFunctorRelativeType ConversionRelativeFunctor; - ConversionRelativeFunctor.initialize(this->m_Factor); + + if (std::isnan(this->m_Factor)) + { + mitkThrow() << "The conversion factor k must be set."; + } + else + { + ConversionRelativeFunctor.initialize(this->m_Factor); + } typename FilterRelativeType::Pointer ConversionRelativeFilter = FilterRelativeType::New(); ConversionRelativeFilter->SetFunctor(ConversionRelativeFunctor); ConversionRelativeFilter->SetInput1(itkInputImage); ConversionRelativeFilter->SetInput2(itkBaselineImage); ConversionRelativeFilter->Update(); m_ConvertSignalToConcentrationCurve_OutputImage = mitk::ImportItkImage(ConversionRelativeFilter->GetOutput())->Clone(); } } return m_ConvertSignalToConcentrationCurve_OutputImage; } diff --git a/Modules/Pharmacokinetics/test/mitkConvertSignalToConcentrationTest.cpp b/Modules/Pharmacokinetics/test/mitkConvertSignalToConcentrationTest.cpp index 771d25e57b..55115da524 100644 --- a/Modules/Pharmacokinetics/test/mitkConvertSignalToConcentrationTest.cpp +++ b/Modules/Pharmacokinetics/test/mitkConvertSignalToConcentrationTest.cpp @@ -1,217 +1,222 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Testing #include "mitkTestingMacros.h" #include "mitkTestFixture.h" //MITK includes #include "mitkConcentrationCurveGenerator.h" #include "mitkTestDynamicImageGenerator.h" #include "mitkImagePixelReadAccessor.h" +#include "boost/math/constants/constants.hpp" class mitkConvertSignalToConcentrationTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkConvertSignalToConcentrationTestSuite); MITK_TEST(GetConvertedImageAbsoluteEnhancementTest); MITK_TEST(GetConvertedImageAbsoluteEnhancementAveragedBaselineTest); MITK_TEST(GetConvertedImageRelativeEnhancementTest); MITK_TEST(GetConvertedImageturboFLASHTest); MITK_TEST(GetConvertedImageVFATest); MITK_TEST(GetConvertedImageT2Test); CPPUNIT_TEST_SUITE_END(); private: mitk::Image::Pointer m_dynamicImage; mitk::Image::Pointer m_convertedImage; mitk::ConcentrationCurveGenerator::Pointer m_concentrationGen; std::vector > m_testIndices; public: void setUp() override { m_dynamicImage = mitk::GenerateDynamicTestImageMITK(); m_concentrationGen = mitk::ConcentrationCurveGenerator::New(); m_concentrationGen->SetDynamicImage(m_dynamicImage); itk::Index<4> testIndex0 = { { 0, 0, 0, 0 } }; itk::Index<4> testIndex1 = { { 0, 2, 2, 3 } }; itk::Index<4> testIndex2 = { { 2, 2, 1, 9 } }; itk::Index<4> testIndex3 = { { 0, 3, 2, 1 } }; itk::Index<4> testIndex4 = { { 2, 2, 2, 4 } }; m_testIndices.push_back(testIndex0); m_testIndices.push_back(testIndex1); m_testIndices.push_back(testIndex2); m_testIndices.push_back(testIndex3); m_testIndices.push_back(testIndex4); } void tearDown() override { } void GetConvertedImageAbsoluteEnhancementTest() { m_concentrationGen->SetAbsoluteSignalEnhancement(true); m_concentrationGen->SetFactor(1.0); m_concentrationGen->SetBaselineStartTimeStep(0); m_concentrationGen->SetBaselineEndTimeStep(0); m_convertedImage = m_concentrationGen->GetConvertedImage(); mitk::ImagePixelReadAccessor readAccess(m_convertedImage, m_convertedImage->GetSliceData(4)); std::vector refValues; refValues.push_back(0.0); refValues.push_back(90.0); refValues.push_back(360.0); refValues.push_back(0.0); refValues.push_back(160.0); std::stringstream ss; for (long unsigned int i = 0; i < m_testIndices.size(); i++) { ss << "Checking value of image converted using absolute enhancement at test index " << i << "."; std::string message = ss.str(); CPPUNIT_ASSERT_MESSAGE(message, mitk::Equal(refValues.at(i), readAccess.GetPixelByIndex(m_testIndices.at(i)), 1e-6, true) == true); } } void GetConvertedImageAbsoluteEnhancementAveragedBaselineTest() { m_concentrationGen->SetAbsoluteSignalEnhancement(true); m_concentrationGen->SetFactor(1.0); m_concentrationGen->SetBaselineStartTimeStep(1); m_concentrationGen->SetBaselineEndTimeStep(3); m_convertedImage = m_concentrationGen->GetConvertedImage(); mitk::ImagePixelReadAccessor readAccess(m_convertedImage, m_convertedImage->GetSliceData(4)); std::vector refValues; refValues.push_back(0.0); refValues.push_back(30.0); refValues.push_back(280.0); refValues.push_back(0.0); refValues.push_back(80.0); std::stringstream ss; for (long unsigned int i = 0; i < m_testIndices.size(); i++) { ss << "Checking value of image converted using absolute enhancement with averaged baseline at test index " << i << "."; std::string message = ss.str(); CPPUNIT_ASSERT_MESSAGE(message, mitk::Equal(refValues.at(i), readAccess.GetPixelByIndex(m_testIndices.at(i)), 1e-6, true) == true); } } void GetConvertedImageRelativeEnhancementTest() { m_concentrationGen->SetRelativeSignalEnhancement(true); m_concentrationGen->SetFactor(1.0); m_concentrationGen->SetBaselineStartTimeStep(0); m_concentrationGen->SetBaselineEndTimeStep(0); m_convertedImage = m_concentrationGen->GetConvertedImage(); mitk::ImagePixelReadAccessor readAccess(m_convertedImage, m_convertedImage->GetSliceData(4)); std::vector refValues; refValues.push_back(0.0); refValues.push_back(3.461538); refValues.push_back(20.0); refValues.push_back(0.0); refValues.push_back(5.714286); std::stringstream ss; for (long unsigned int i = 0; i < m_testIndices.size(); i++) { ss << "Checking value of image converted using relative enhancement at test index " << i << "."; std::string message = ss.str(); CPPUNIT_ASSERT_MESSAGE(message, mitk::Equal(refValues.at(i), readAccess.GetPixelByIndex(m_testIndices.at(i)), 1e-6, true) == true); } } void GetConvertedImageturboFLASHTest() { m_concentrationGen->SetisTurboFlashSequence(true); m_concentrationGen->SetRecoveryTime(1.0); m_concentrationGen->SetRelaxationTime(1.0); m_concentrationGen->SetRelaxivity(1.0); m_concentrationGen->SetBaselineStartTimeStep(0); m_concentrationGen->SetBaselineEndTimeStep(0); m_convertedImage = m_concentrationGen->GetConvertedImage(); mitk::ImagePixelReadAccessor readAccess(m_convertedImage, m_convertedImage->GetSliceData(4)); std::vector refValues; refValues.push_back(0.0); refValues.push_back(0.0); refValues.push_back(0.0); refValues.push_back(0.0); refValues.push_back(0.0); std::stringstream ss; for (long unsigned int i = 0; i < m_testIndices.size(); i++) { ss << "Checking value of image converted using the turboFLASH model at test index " << i << "."; std::string message = ss.str(); CPPUNIT_ASSERT_MESSAGE(message, mitk::Equal(refValues.at(i), readAccess.GetPixelByIndex(m_testIndices.at(i)), 1e-6, true) == true); } } void GetConvertedImageVFATest() { mitk::Image::Pointer PDWImage; - PDWImage = mitk::GenerateTestFrame(1.0); + PDWImage = mitk::GenerateTestFrame(2.0); m_concentrationGen->SetUsingT1Map(true); - m_concentrationGen->SetRecoveryTime(1.0); - m_concentrationGen->SetRelaxivity(1.0); - m_concentrationGen->SetT10Image(PDWImage); - m_concentrationGen->SetFlipAngle(1.0); + m_concentrationGen->SetRepetitionTime(1.0); + m_concentrationGen->SetRelaxivity(1.0/1000.0); + m_concentrationGen->SetPDWImage(PDWImage); + m_concentrationGen->SetFlipAngle(2.0 / 360 * 2 * boost::math::constants::pi()); + m_concentrationGen->SetFlipAnglePDW(1.0 / 360 * 2 * boost::math::constants::pi()); m_concentrationGen->SetBaselineStartTimeStep(0); m_concentrationGen->SetBaselineEndTimeStep(0); m_convertedImage = m_concentrationGen->GetConvertedImage(); + + mitk::ImagePixelReadAccessor readAccessPDW(PDWImage, PDWImage->GetSliceData(3)); + mitk::ImagePixelReadAccessor readAccessDyn(m_dynamicImage, m_dynamicImage->GetSliceData(4)); mitk::ImagePixelReadAccessor readAccess(m_convertedImage, m_convertedImage->GetSliceData(4)); std::vector refValues; refValues.push_back(0.0); - refValues.push_back(2.956868); - refValues.push_back(0.0); + refValues.push_back(7871.5616001); + refValues.push_back(-1059.012343); refValues.push_back(0.0); - refValues.push_back(3.989374); + refValues.push_back(-3251.392613); std::stringstream ss; for (long unsigned int i = 0; i < m_testIndices.size(); i++) { ss << "Checking value of image converted using the VFA method at test index " << i << "."; std::string message = ss.str(); CPPUNIT_ASSERT_MESSAGE(message, mitk::Equal(refValues.at(i), readAccess.GetPixelByIndex(m_testIndices.at(i)), 1e-6, true) == true); } } void GetConvertedImageT2Test() { m_concentrationGen->SetisT2weightedImage(true); m_concentrationGen->SetT2Factor(1.0); m_concentrationGen->SetT2EchoTime(1.0); m_concentrationGen->SetBaselineStartTimeStep(0); m_concentrationGen->SetBaselineEndTimeStep(0); m_convertedImage = m_concentrationGen->GetConvertedImage(); mitk::ImagePixelReadAccessor readAccess(m_convertedImage, m_convertedImage->GetSliceData(4)); std::vector refValues; refValues.push_back(0.0); refValues.push_back(-1.495494); refValues.push_back(-3.044522); refValues.push_back(0.0); refValues.push_back(-1.904237); std::stringstream ss; for (long unsigned int i = 0; i < m_testIndices.size(); i++) { ss << "Checking value of converted T2 image at test index " << i << "."; std::string message = ss.str(); CPPUNIT_ASSERT_MESSAGE(message, mitk::Equal(refValues.at(i), readAccess.GetPixelByIndex(m_testIndices.at(i)), 1e-6, true) == true); } } }; MITK_TEST_SUITE_REGISTRATION(mitkConvertSignalToConcentration) diff --git a/Plugins/PluginList.cmake b/Plugins/PluginList.cmake index 4b71a33320..30109b8e31 100644 --- a/Plugins/PluginList.cmake +++ b/Plugins/PluginList.cmake @@ -1,86 +1,86 @@ # Plug-ins must be ordered according to their dependencies set(MITK_PLUGINS org.blueberry.core.runtime:ON org.blueberry.core.expressions:OFF org.blueberry.core.commands:OFF org.blueberry.core.jobs:OFF org.blueberry.ui.qt:OFF org.blueberry.ui.qt.help:ON org.blueberry.ui.qt.log:ON org.blueberry.ui.qt.objectinspector:OFF org.mitk.core.services:ON org.mitk.gui.common:ON org.mitk.planarfigure:ON org.mitk.core.jobs:OFF org.mitk.gui.qt.application:ON org.mitk.gui.qt.ext:OFF org.mitk.gui.qt.extapplication:OFF org.mitk.gui.qt.mitkworkbench.intro:OFF org.mitk.gui.qt.common:ON org.mitk.gui.qt.stdmultiwidgeteditor:ON org.mitk.gui.qt.mxnmultiwidgeteditor:OFF org.mitk.gui.qt.cmdlinemodules:OFF org.mitk.gui.qt.chartExample:OFF org.mitk.gui.qt.datamanager:ON org.mitk.gui.qt.datamanagerlight:OFF org.mitk.gui.qt.datastorageviewertest:OFF org.mitk.gui.qt.properties:ON org.mitk.gui.qt.basicimageprocessing:OFF org.mitk.gui.qt.dicombrowser:OFF org.mitk.gui.qt.dicominspector:OFF org.mitk.gui.qt.dosevisualization:OFF org.mitk.gui.qt.geometrytools:OFF org.mitk.gui.qt.igtexamples:OFF org.mitk.gui.qt.igttracking:OFF org.mitk.gui.qt.openigtlink:OFF org.mitk.gui.qt.imagecropper:OFF org.mitk.gui.qt.imagenavigator:ON org.mitk.gui.qt.viewnavigator:OFF org.mitk.gui.qt.materialeditor:OFF org.mitk.gui.qt.measurementtoolbox:OFF org.mitk.gui.qt.moviemaker:OFF org.mitk.gui.qt.pointsetinteraction:OFF org.mitk.gui.qt.pointsetinteractionmultispectrum:OFF org.mitk.gui.qt.python:OFF org.mitk.gui.qt.remeshing:OFF org.mitk.gui.qt.segmentation:OFF org.mitk.gui.qt.deformableclippingplane:OFF org.mitk.gui.qt.aicpregistration:OFF org.mitk.gui.qt.renderwindowmanager:OFF org.mitk.gui.qt.semanticrelations:OFF org.mitk.gui.qt.toftutorial:OFF org.mitk.gui.qt.tofutil:OFF org.mitk.gui.qt.tubegraph:OFF org.mitk.gui.qt.ugvisualization:OFF org.mitk.gui.qt.ultrasound:OFF org.mitk.gui.qt.volumevisualization:OFF org.mitk.gui.qt.eventrecorder:OFF org.mitk.gui.qt.xnat:OFF org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation:OFF org.mitk.gui.qt.overlaymanager:OFF org.mitk.gui.qt.igt.app.hummelprotocolmeasurements:OFF org.mitk.matchpoint.core.helper:OFF org.mitk.gui.qt.matchpoint.algorithm.browser:OFF org.mitk.gui.qt.matchpoint.algorithm.control:OFF org.mitk.gui.qt.matchpoint.mapper:OFF org.mitk.gui.qt.matchpoint.framereg:OFF org.mitk.gui.qt.matchpoint.visualizer:OFF org.mitk.gui.qt.matchpoint.evaluator:OFF org.mitk.gui.qt.matchpoint.manipulator:OFF org.mitk.gui.qt.preprocessing.resampling:OFF org.mitk.gui.qt.cest:OFF org.mitk.gui.qt.fit.demo:OFF org.mitk.gui.qt.fit.inspector:OFF org.mitk.gui.qt.fit.genericfitting:OFF + org.mitk.gui.qt.pharmacokinetics.concentration.mri:OFF + org.mitk.gui.qt.pharmacokinetics.curvedescriptor:OFF org.mitk.gui.qt.pharmacokinetics.mri:OFF org.mitk.gui.qt.pharmacokinetics.pet:OFF org.mitk.gui.qt.pharmacokinetics.simulation:OFF - org.mitk.gui.qt.pharmacokinetics.curvedescriptor:OFF - org.mitk.gui.qt.pharmacokinetics.concentration.mri:OFF org.mitk.gui.qt.flowapplication:OFF org.mitk.gui.qt.flow.segmentation:OFF org.mitk.gui.qt.pixelvalue:ON ) diff --git a/Plugins/org.mitk.gui.qt.fit.demo/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.fit.demo/manifest_headers.cmake index 03480fff78..c183994ae3 100644 --- a/Plugins/org.mitk.gui.qt.fit.demo/manifest_headers.cmake +++ b/Plugins/org.mitk.gui.qt.fit.demo/manifest_headers.cmake @@ -1,5 +1,5 @@ set(Plugin-Name "Fit Generator Demo") set(Plugin-Version "1.0") -set(Plugin-Vendor "DKFZ, Software Development For Integrated Diagnostics and Therapy") -set(Plugin-ContactAddress "r.floca@dkfz.de") +set(Plugin-Vendor "German Cancer Research Center (DKFZ) Heidelberg, Division of Medical Image Computing, Germany") +set(Plugin-ContactAddress "Contact via www.MITK.org") set(Require-Plugin org.mitk.gui.qt.common) diff --git a/Plugins/org.mitk.gui.qt.fit.genericfitting/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.fit.genericfitting/manifest_headers.cmake index 670a894787..2620d75de9 100644 --- a/Plugins/org.mitk.gui.qt.fit.genericfitting/manifest_headers.cmake +++ b/Plugins/org.mitk.gui.qt.fit.genericfitting/manifest_headers.cmake @@ -1,5 +1,5 @@ set(Plugin-Name "Modelfit Generic Data Fitting") set(Plugin-Version "1.0") -set(Plugin-Vendor "DKFZ, Software Development For Integrated Diagnostics and Therapy") -set(Plugin-ContactAddress "r.floca@dkfz.de") +set(Plugin-Vendor "German Cancer Research Center (DKFZ) Heidelberg, Division of Medical Image Computing, Germany") +set(Plugin-ContactAddress "Contact via www.MITK.org") set(Require-Plugin org.mitk.gui.qt.common) diff --git a/Plugins/org.mitk.gui.qt.fit.inspector/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.fit.inspector/manifest_headers.cmake index df38cd0082..7c5a41373c 100644 --- a/Plugins/org.mitk.gui.qt.fit.inspector/manifest_headers.cmake +++ b/Plugins/org.mitk.gui.qt.fit.inspector/manifest_headers.cmake @@ -1,5 +1,5 @@ set(Plugin-Name "Modelfit Inspector Plugin") set(Plugin-Version "1.0") -set(Plugin-Vendor "DKFZ, Software Development For Integrated Diagnostics and Therapy") -set(Plugin-ContactAddress "r.floca@dkfz.de") +set(Plugin-Vendor "German Cancer Research Center (DKFZ) Heidelberg, Division of Medical Image Computing, Germany") +set(Plugin-ContactAddress "Contact via www.MITK.org") set(Require-Plugin org.mitk.gui.qt.common) diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/documentation/UserManual/Manual.dox b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/documentation/UserManual/Manual.dox index 7c16b548b7..3fa2edaef1 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/documentation/UserManual/Manual.dox +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/documentation/UserManual/Manual.dox @@ -1,35 +1,53 @@ /** \page org_mitk_views_pharmacokinetics_concentration_mri The DCE Concentration Curve Converter View \imageMacro{pharmacokinetics_concentration_doc.svg,"Icon of the DCE Concentration Curve Converter View",3.0} \tableofcontents \section org_mitk_views_pharmacokinetics_concentration_mri_overview Overview This view offers a dedicated tool for the conversion of DCE MR image signal intensities to contrast agent (CA) concentration. -It contains a subset of the conversion tools for T1-weighted signal intensities, which are a part of the DCE MR Perfusion Datafit View (see \ref FIT_DCE_Settings_concentration). +It contains a subset of the conversion tools for T1-weighted signal intensities, which are also a part of the DCE MR Perfusion Datafit View. Additionally, it allows for the conversion between T2-weighted MR signal intensities and contrast agent concentration. \section org_mitk_views_pharmacokinetics_concentration_mri_Contact Contact information If you have any questions, need support, find a bug or have a feature request, feel free to contact us at www.mitk.org. \section org_mitk_views_pharmacokinetics_concentration_mri_T1_conversion Conversion of T1-weighted MRI data -\imageMacro{concentration_curve_converter_T1_weighted4D.png,"Example screenshot of the conversion of T1-weighted MR images",3.0} +\imageMacro{concentration_curve_converter_T1_weighted4D.png,"Example screenshot of the conversion of T1-weighted MR images",3.0 } The view offers the choice between a 3D Image and a 4D image. If a 4D image is selected, the Selected Time Series needs to be specified. -In this case, the baseline image is automatically extracted as the first time point image of the time series. +For 4D images, for all conversion methods which require a baseline value, the range of the time points which are part of the baseline can be specified. The baseline signal SBL will be averaged between the signal of the time points within this range. +If not specified, the baseline signal SBL is set as the signal of the first time point image of the time series. In case of a 3D image to be converted, additionally to the selected 3D image a Baseline Image (without CA) has to be specified.\n\n -In the configuration section, the following types of conversion can be chosen: -- Absolute Signal Enhancement: The contrast agent concentration c is calculated according to the formula: c = k*(S(t)-S(0)), where S(t) is the dynamic T1-weighted signal intensity, S(0) the baseline signal and k a user-defined conversion factor. -- Relative Signal Enhancement: The contrast agent concentration c is calculated according to the formula: c = k*(S(t)-S(0))/S(0), where S(t) is the dynamic T1-weighted signal intensity, S(0) the baseline signal and k a user-defined conversion factor. -- Turbo FLASH Sequence: The conversion from signal S(t) to contrast agent concentration c is calculated according to the turboFLASH (ultrafast gradient echo) sequence specific formula. +The following types of conversion can be chosen: +- Absolute Signal Enhancement: The dynamic contrast agent concentration C(t) is calculated according to the formula: C(t) = k*(S(t)-SBL), where S(t) is the dynamic T1-weighted signal intensity, SBL the baseline signal and k a user-defined conversion factor. +- Relative Signal Enhancement: The dynamic contrast agent concentration C(t) is calculated according to the formula: C(t) = k*(S(t)-SBL)/SBL, where S(t) is the dynamic T1-weighted signal intensity, SBL the baseline signal and k a user-defined conversion factor. +- Variable Flip Angle: This conversion uses the method described in \ref org_mitk_views_pharmacokinetics_concentration_mri_lit_ref1 "[1]". As additional input to the dynamic time series, +a proton density weighted (PDW) image is provided, which has been acquired pre-contrast with the same sequence parameters as for the +dynamic image but a smaller flip angle. Both flip angles are provided by the user. +Furthermore, the repetition time TR and the longitudinal relaxivity r1 are required as input.
+It is assumed that the MR data has been acquired according to the spoiled gradient recalled echo model. +The sequence formulas for the PDW image signal and for the baseline signal of the dynamic time series are two equations with two unknowns: +the pre-contrast R1-relaxation rate R10 and the signal scaling factor S0. These are calculated by solving the system of equations.
+With the knowledge of S0, the dynamic contrast-enhanced relaxation rate R1(t) is computed. +Finally, the concentration is calculated by inverting the linear model: +R1(t)=R10+r1*C(t). +- Turbo FLASH Sequence: The conversion from signal S(t) to contrast agent concentration C(t) is calculated according to the turboFLASH (ultrafast gradient echo) sequence specific formula. \section org_mitk_views_pharmacokinetics_concentration_mri_T2_conversion Conversion of T2-weighted MRI data \imageMacro{concentration_curve_converter_T2_weighted.png,"Example screenshot of the conversion of T2-weighted MR images",3.0} -The contrast agent concentration c is calculated according to the formula: c = -k/TE*ln(S(t)/S(0)), where S(t) is the dynamic T2-weighted signal intensity, S(0) the baseline signal, k a user-defined conversion factor and TE the echo time of the employed sequence. +The dynamic contrast agent concentration C(t) is calculated according to the formula: C(t) = -k/TE*ln(S(t)/SBL), where S(t) is the dynamic T2-weighted signal intensity, SBL the baseline signal, k a user-defined conversion factor and TE the echo time of the employed sequence. In practice, the factor k is often set to unity. +\section org_mitk_views_pharmacokinetics_concentration_mri_lit References/Literature +- \anchor org_mitk_views_pharmacokinetics_concentration_mri_lit_ref1 [1] Wang, H. Z., Riederer, S. J., and Lee, J. N. (1987). Optimization the +precision in T1 relaxation estimation using limited flip angles. Magn Reson Med, +5:399–416. + + + */ diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/documentation/UserManual/concentration_curve_converter_T1_weighted4D.png b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/documentation/UserManual/concentration_curve_converter_T1_weighted4D.png index ef521cc592..6de9b777c8 100644 Binary files a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/documentation/UserManual/concentration_curve_converter_T1_weighted4D.png and b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/documentation/UserManual/concentration_curve_converter_T1_weighted4D.png differ diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/documentation/UserManual/concentration_curve_converter_T2_weighted.png b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/documentation/UserManual/concentration_curve_converter_T2_weighted.png index cd4158f49d..91e1b6cdc8 100644 Binary files a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/documentation/UserManual/concentration_curve_converter_T2_weighted.png and b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/documentation/UserManual/concentration_curve_converter_T2_weighted.png differ diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/manifest_headers.cmake index 2266f968b7..4609994d87 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/manifest_headers.cmake +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/manifest_headers.cmake @@ -1,5 +1,5 @@ set(Plugin-Name "Concentration Curve Converter Plugin") set(Plugin-Version "1.0") -set(Plugin-Vendor "DKFZ, Software Development For Integrated Diagnostics and Therapy") -set(Plugin-ContactAddress "c.debus@dkfz.de") +set(Plugin-Vendor "German Cancer Research Center (DKFZ) Heidelberg, Division of Medical Image Computing, Germany") +set(Plugin-ContactAddress "Contact via www.MITK.org") set(Require-Plugin org.mitk.gui.qt.common) diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterView.cpp b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterView.cpp index 1ec7c9c470..05548ce5de 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterView.cpp +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterView.cpp @@ -1,551 +1,565 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include "mitkWorkbenchUtil.h" #include "ConcentrationCurveConverterView.h" #include "mitkConcentrationCurveGenerator.h" #include "mitkNodePredicateDataType.h" #include "mitkConvertToConcentrationTurboFlashFunctor.h" #include "mitkConvertToConcentrationAbsoluteFunctor.h" #include "mitkConvertToConcentrationRelativeFunctor.h" #include "itkBinaryFunctorImageFilter.h" #include "boost/math/constants/constants.hpp" #include #include #include #include #include #include #include "mitkNodePredicateFunction.h" #include #include // Includes for image casting between ITK and MITK #include "mitkImageTimeSelector.h" #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include #include const std::string ConcentrationCurveConverterView::VIEW_ID = "org.mitk.views.pharmacokinetics.concentration.mri"; void ConcentrationCurveConverterView::SetFocus() { m_Controls.btnConvertToConcentration->setFocus(); } void ConcentrationCurveConverterView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnConvertToConcentration->setEnabled(false); connect(m_Controls.btnConvertToConcentration, SIGNAL(clicked()), this, SLOT(OnConvertToConcentrationButtonClicked())); m_Controls.timeSeriesNodeSelector->SetNodePredicate(this->m_isValidTimeSeriesImagePredicate); m_Controls.timeSeriesNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.timeSeriesNodeSelector->SetSelectionIsOptional(false); m_Controls.timeSeriesNodeSelector->SetInvalidInfo("Please select time series."); m_Controls.image3DNodeSelector->SetNodePredicate(this->m_isValidPDWImagePredicate); m_Controls.image3DNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.image3DNodeSelector->SetSelectionIsOptional(false); m_Controls.image3DNodeSelector->SetInvalidInfo("Please select 3D image."); m_Controls.baselineImageNodeSelector->SetNodePredicate(this->m_isValidPDWImagePredicate); m_Controls.baselineImageNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.baselineImageNodeSelector->SetSelectionIsOptional(false); m_Controls.baselineImageNodeSelector->SetInvalidInfo("Please select baseline image."); m_Controls.t2TimeSeriesNodeSelector->SetNodePredicate(this->m_isValidTimeSeriesImagePredicate); m_Controls.t2TimeSeriesNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.t2TimeSeriesNodeSelector->SetSelectionIsOptional(false); m_Controls.t2TimeSeriesNodeSelector->SetInvalidInfo("Please select time series."); m_Controls.PDWImageNodeSelector->SetNodePredicate(m_isValidPDWImagePredicate); m_Controls.PDWImageNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.PDWImageNodeSelector->SetInvalidInfo("Please select PDW Image."); m_Controls.PDWImageNodeSelector->setEnabled(false); m_Controls.groupBox_T1->hide(); m_Controls.groupBox_T2->hide(); m_Controls.groupBox3D->hide(); m_Controls.groupBox4D->hide(); m_Controls.groupBoxTurboFlash->hide(); m_Controls.groupConcentration->hide(); + m_Controls.groupBox_baselineRangeSelection->hide(); + m_Controls.groupBox_BaselineRangeSelectionT2->hide(); connect(m_Controls.radioButton_T1, SIGNAL(toggled(bool)),this, SLOT(OnSettingChanged())); connect(m_Controls.radioButton_T2, SIGNAL(toggled(bool)),this, SLOT(OnSettingChanged())); connect(m_Controls.radioButton3D, SIGNAL(toggled(bool)),this, SLOT(OnSettingChanged())); connect(m_Controls.radioButton4D, SIGNAL(toggled(bool)),this, SLOT(OnSettingChanged())); //Concentration m_Controls.groupConcentration->hide(); m_Controls.groupBoxEnhancement->hide(); m_Controls.groupBoxTurboFlash->hide(); m_Controls.groupBox_T1MapviaVFA->hide(); m_Controls.spinBox_baselineStartTimeStep->setValue(0); m_Controls.spinBox_baselineEndTimeStep->setValue(0); m_Controls.spinBox_baselineEndTimeStep->setMinimum(0); m_Controls.spinBox_baselineStartTimeStep->setMinimum(0); m_Controls.spinBox_baselineStartTimeStepT2->setValue(0); m_Controls.spinBox_baselineEndTimeStepT2->setValue(0); m_Controls.spinBox_baselineEndTimeStepT2->setMinimum(0); m_Controls.spinBox_baselineStartTimeStepT2->setMinimum(0); + connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), m_Controls.groupBoxTurboFlash, SLOT(setVisible(bool))); + connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), m_Controls.groupBox_baselineRangeSelection, SLOT(setVisible(bool))); connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), this, SLOT(OnSettingChanged())); connect(m_Controls.relaxationtime, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); connect(m_Controls.recoverytime, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); connect(m_Controls.relaxivity, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); connect(m_Controls.timeSeriesNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ConcentrationCurveConverterView::OnNodeSelectionChanged); connect(m_Controls.image3DNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ConcentrationCurveConverterView::OnNodeSelectionChanged); connect(m_Controls.baselineImageNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ConcentrationCurveConverterView::OnNodeSelectionChanged); connect(m_Controls.t2TimeSeriesNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ConcentrationCurveConverterView::OnNodeSelectionChanged); connect(m_Controls.PDWImageNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ConcentrationCurveConverterView::OnSettingChanged); - connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), this, SLOT(OnSettingChanged())); - connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), this, SLOT(OnSettingChanged())); connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), m_Controls.groupBoxEnhancement, SLOT(setVisible(bool))); + connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), m_Controls.groupBox_baselineRangeSelection, SLOT(setVisible(bool))); + connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), this, SLOT(OnSettingChanged())); + connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), m_Controls.groupBoxEnhancement, SLOT(setVisible(bool))); + connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), m_Controls.groupBox_baselineRangeSelection, SLOT(setVisible(bool))); + connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), this, SLOT(OnSettingChanged())); connect(m_Controls.factorSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); connect(m_Controls.spinBox_baselineStartTimeStep, SIGNAL(valueChanged(int)), this, SLOT(OnSettingChanged())); connect(m_Controls.spinBox_baselineEndTimeStep, SIGNAL(valueChanged(int)), this, SLOT(OnSettingChanged())); connect(m_Controls.spinBox_baselineStartTimeStepT2, SIGNAL(valueChanged(int)), this, SLOT(OnSettingChanged())); connect(m_Controls.spinBox_baselineEndTimeStepT2, SIGNAL(valueChanged(int)), this, SLOT(OnSettingChanged())); connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), m_Controls.groupBox_T1MapviaVFA, SLOT(setVisible(bool))); + connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), m_Controls.groupBox_baselineRangeSelection, SLOT(setVisible(bool))); connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), this, SLOT(OnSettingChanged())); connect(m_Controls.FlipangleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); connect(m_Controls.RelaxivitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); connect(m_Controls.TRSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); connect(m_Controls.T2EchoTimeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); connect(m_Controls.T2FactorSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), m_Controls.PDWImageNodeSelector, SLOT(setEnabled(bool))); } void ConcentrationCurveConverterView::OnSettingChanged() { bool ok = false; m_Controls.groupBox_T1->setVisible(m_Controls.radioButton_T1->isChecked()); m_Controls.groupBox_T2->setVisible(m_Controls.radioButton_T2->isChecked()); + m_Controls.groupBox_BaselineRangeSelectionT2->setVisible(m_Controls.radioButton_T2->isChecked()); + if(m_Controls.radioButton_T1->isChecked()) { m_Controls.groupBox3D->setVisible(m_Controls.radioButton3D->isChecked()); m_Controls.groupBox4D->setVisible(m_Controls.radioButton4D->isChecked()); if(m_Controls.radioButton4D->isChecked()) { m_Controls.groupConcentration->setVisible(true); + if (m_Controls.radioButton_absoluteEnhancement->isChecked() || m_Controls.radioButton_relativeEnchancement->isChecked() || m_Controls.radioButtonUsingT1viaVFA->isChecked() || m_Controls.radioButtonTurboFlash->isChecked()) + m_Controls.groupBox_baselineRangeSelection->setVisible(true); ok = m_selectedImage.IsNotNull() && CheckSettings(); } else if(m_Controls.radioButton3D->isChecked()) { m_Controls.groupConcentration->setVisible(true); + m_Controls.groupBox_baselineRangeSelection->hide(); ok = m_selectedImage.IsNotNull() && m_selectedBaselineImage.IsNotNull() && CheckSettings(); - } } else if (m_Controls.radioButton_T2->isChecked()) { m_Controls.groupConcentration->setVisible(false); ok = m_selectedImage.IsNotNull() && CheckSettings(); } m_Controls.btnConvertToConcentration->setEnabled(ok); } bool ConcentrationCurveConverterView::CheckSettings() const { bool ok = true; if (m_Controls.radioButton_T1->isChecked()) { if (this->m_Controls.radioButtonTurboFlash->isChecked()) { ok = ok && (m_Controls.recoverytime->value() > 0); ok = ok && (m_Controls.relaxationtime->value() > 0); ok = ok && (m_Controls.relaxivity->value() > 0); ok = ok && (m_Controls.AifRecoverytime->value() > 0); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButton_absoluteEnhancement->isChecked() || this->m_Controls.radioButton_relativeEnchancement->isChecked()) { ok = ok && (m_Controls.factorSpinBox->value() > 0); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButtonUsingT1viaVFA->isChecked()) { ok = ok && (m_Controls.FlipangleSpinBox->value() > 0); ok = ok && (m_Controls.TRSpinBox->value() > 0); ok = ok && (m_Controls.RelaxivitySpinBox->value() > 0); ok = ok && (m_Controls.PDWImageNodeSelector->GetSelectedNode().IsNotNull()); ok = ok && CheckBaselineSelectionSettings(); } else { ok = false; } } else if (this->m_Controls.radioButton_T2->isChecked()) { ok = ok && m_Controls.T2EchoTimeSpinBox->value() > 0; ok = ok && m_Controls.T2FactorSpinBox->value() > 0; ok = ok && CheckBaselineSelectionSettings(); } else { ok = false; } return ok; } bool ConcentrationCurveConverterView::CheckBaselineSelectionSettings() const { if (this->m_Controls.radioButton_T1->isChecked()) { return m_Controls.spinBox_baselineStartTimeStep->value() <= m_Controls.spinBox_baselineEndTimeStep->value(); } else if (this->m_Controls.radioButton_T2->isChecked()) { return m_Controls.spinBox_baselineStartTimeStepT2->value() <= m_Controls.spinBox_baselineEndTimeStepT2->value(); } else { return 0; } } void ConcentrationCurveConverterView::OnConvertToConcentrationButtonClicked() { mitk::Image::Pointer concentrationImage; mitk::DataNode::Pointer concentrationNode; if(m_Controls.radioButton_T1->isChecked()) { if(m_Controls.radioButton4D->isChecked()) { concentrationImage = this->Convert4DConcentrationImage(this->m_selectedImage); } else if(m_Controls.radioButton3D->isChecked()) { concentrationImage = Convert3DConcentrationImage(this->m_selectedImage, this->m_selectedBaselineImage); } } else if(m_Controls.radioButton_T2->isChecked()) { concentrationImage = this->ConvertT2ConcentrationImgage(this->m_selectedImage); } std::string nameOfResultImage = m_selectedNode->GetName(); nameOfResultImage.append("_Concentration"); concentrationNode = AddConcentrationImage(concentrationImage,nameOfResultImage); } mitk::Image::Pointer ConcentrationCurveConverterView::Convert3DConcentrationImage(mitk::Image::Pointer inputImage,mitk::Image::Pointer baselineImage) { typedef itk::Image InputImageType; InputImageType::Pointer itkInputImage = InputImageType::New(); InputImageType::Pointer itkBaselineImage = InputImageType::New(); mitk::CastToItkImage(inputImage, itkInputImage ); mitk::CastToItkImage(baselineImage, itkBaselineImage ); mitk::Image::Pointer outputImage; if(this->m_Controls.radioButtonTurboFlash->isChecked()) { typedef mitk::ConvertToConcentrationTurboFlashFunctor ConversionFunctorTurboFlashType; typedef itk::BinaryFunctorImageFilter FilterTurboFlashType; ConversionFunctorTurboFlashType ConversionTurboFlashFunctor; ConversionTurboFlashFunctor.initialize(m_Controls.relaxationtime->value(), m_Controls.relaxivity->value(), m_Controls.recoverytime->value()); FilterTurboFlashType::Pointer ConversionTurboFlashFilter = FilterTurboFlashType::New(); ConversionTurboFlashFilter->SetFunctor(ConversionTurboFlashFunctor); ConversionTurboFlashFilter->SetInput1(itkInputImage); ConversionTurboFlashFilter->SetInput2(itkBaselineImage); ConversionTurboFlashFilter->Update(); outputImage = mitk::ImportItkImage(ConversionTurboFlashFilter->GetOutput())->Clone(); } else if(this->m_Controls.radioButton_absoluteEnhancement->isChecked()) { typedef mitk::ConvertToConcentrationAbsoluteFunctor ConversionFunctorAbsoluteType; typedef itk::BinaryFunctorImageFilter FilterAbsoluteType; ConversionFunctorAbsoluteType ConversionAbsoluteFunctor; ConversionAbsoluteFunctor.initialize(m_Controls.factorSpinBox->value()); FilterAbsoluteType::Pointer ConversionAbsoluteFilter = FilterAbsoluteType::New(); ConversionAbsoluteFilter->SetFunctor(ConversionAbsoluteFunctor); ConversionAbsoluteFilter->SetInput1(itkInputImage); ConversionAbsoluteFilter->SetInput2(itkBaselineImage); ConversionAbsoluteFilter->Update(); outputImage = mitk::ImportItkImage(ConversionAbsoluteFilter->GetOutput())->Clone(); } else if(m_Controls.radioButton_relativeEnchancement->isChecked()) { typedef mitk::ConvertToConcentrationRelativeFunctor ConversionFunctorRelativeType; typedef itk::BinaryFunctorImageFilter FilterRelativeType; ConversionFunctorRelativeType ConversionRelativeFunctor; ConversionRelativeFunctor.initialize(m_Controls.factorSpinBox->value()); FilterRelativeType::Pointer ConversionRelativeFilter = FilterRelativeType::New(); ConversionRelativeFilter->SetFunctor(ConversionRelativeFunctor); ConversionRelativeFilter->SetInput1(itkInputImage); ConversionRelativeFilter->SetInput2(itkBaselineImage); ConversionRelativeFilter->Update(); outputImage = mitk::ImportItkImage(ConversionRelativeFilter->GetOutput())->Clone(); } return outputImage; } mitk::DataNode::Pointer ConcentrationCurveConverterView::AddConcentrationImage(mitk::Image* image, std::string nodeName) const { if (!image) { mitkThrow() << "Cannot generate concentration node. Passed image is null. parameter name: "; } mitk::DataNode::Pointer result = mitk::DataNode::New(); result->SetData(image); result->SetName(nodeName); result->SetVisibility(true); this->GetDataStorage()->Add(result, m_selectedNode); return result; }; mitk::Image::Pointer ConcentrationCurveConverterView::Convert4DConcentrationImage(mitk::Image::Pointer inputImage) { //Compute Concentration image mitk::ConcentrationCurveGenerator::Pointer concentrationGen = mitk::ConcentrationCurveGenerator::New(); concentrationGen->SetDynamicImage(inputImage); concentrationGen->SetisTurboFlashSequence(m_Controls.radioButtonTurboFlash->isChecked()); concentrationGen->SetAbsoluteSignalEnhancement(m_Controls.radioButton_absoluteEnhancement->isChecked()); concentrationGen->SetRelativeSignalEnhancement(m_Controls.radioButton_relativeEnchancement->isChecked()); concentrationGen->SetUsingT1Map(m_Controls.radioButtonUsingT1viaVFA->isChecked()); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStepT2->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStepT2->value()); concentrationGen->SetisT2weightedImage(false); if (m_Controls.radioButtonTurboFlash->isChecked()) { concentrationGen->SetRecoveryTime(m_Controls.recoverytime->value()); concentrationGen->SetRelaxationTime(m_Controls.relaxationtime->value()); concentrationGen->SetRelaxivity(m_Controls.relaxivity->value()); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); } else if (this->m_Controls.radioButtonUsingT1viaVFA->isChecked()) { - concentrationGen->SetRecoveryTime(m_Controls.TRSpinBox->value()); + concentrationGen->SetRepetitionTime(m_Controls.TRSpinBox->value()); concentrationGen->SetRelaxivity(m_Controls.RelaxivitySpinBox->value()); - concentrationGen->SetT10Image(dynamic_cast(m_Controls.PDWImageNodeSelector->GetSelectedNode()->GetData())); + concentrationGen->SetPDWImage(dynamic_cast(m_Controls.PDWImageNodeSelector->GetSelectedNode()->GetData())); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); //Convert Flipangle from degree to radiant - double alpha = m_Controls.FlipangleSpinBox->value()/360*2* boost::math::constants::pi(); - concentrationGen->SetFlipAngle(alpha); + double alpha = m_Controls.FlipangleSpinBox->value()/360*2* boost::math::constants::pi(); + concentrationGen->SetFlipAngle(alpha); + double alphaPDW = m_Controls.FlipanglePDWSpinBox->value() / 360 * 2 * boost::math::constants::pi(); + concentrationGen->SetFlipAnglePDW(alphaPDW); } else { concentrationGen->SetFactor(m_Controls.factorSpinBox->value()); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); } mitk::Image::Pointer concentrationImage = concentrationGen->GetConvertedImage(); return concentrationImage; } mitk::Image::Pointer ConcentrationCurveConverterView::ConvertT2ConcentrationImgage(mitk::Image::Pointer inputImage) { //Compute Concentration image mitk::ConcentrationCurveGenerator::Pointer concentrationGen = mitk::ConcentrationCurveGenerator::New(); concentrationGen->SetDynamicImage(inputImage); concentrationGen->SetisTurboFlashSequence(false); concentrationGen->SetAbsoluteSignalEnhancement(false); concentrationGen->SetRelativeSignalEnhancement(false); concentrationGen->SetisT2weightedImage(true); concentrationGen->SetT2Factor(m_Controls.T2FactorSpinBox->value()); concentrationGen->SetT2EchoTime(m_Controls.T2EchoTimeSpinBox->value()); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); mitk::Image::Pointer concentrationImage = concentrationGen->GetConvertedImage(); return concentrationImage; } void ConcentrationCurveConverterView::OnNodeSelectionChanged(QList/*nodes*/) { m_selectedNode = nullptr; m_selectedImage = nullptr; m_selectedBaselineNode = nullptr; m_selectedBaselineImage = nullptr; if (m_Controls.radioButton_T1->isChecked()) { if (m_Controls.radioButton4D->isChecked()) { if (m_Controls.timeSeriesNodeSelector->GetSelectedNode().IsNotNull()) { this->m_selectedNode = m_Controls.timeSeriesNodeSelector->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); } else { this->m_selectedNode = nullptr; this->m_selectedImage = nullptr; } } else if (m_Controls.radioButton3D->isChecked()) { if (m_Controls.image3DNodeSelector->GetSelectedNode().IsNotNull() && m_Controls.baselineImageNodeSelector->GetSelectedNode().IsNotNull()) { this->m_selectedNode = m_Controls.image3DNodeSelector->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); this->m_selectedBaselineNode = m_Controls.baselineImageNodeSelector->GetSelectedNode(); m_selectedBaselineImage = dynamic_cast(m_selectedBaselineNode->GetData()); } else { this->m_selectedNode = nullptr; this->m_selectedImage = nullptr; m_selectedBaselineNode = nullptr; m_selectedBaselineImage = nullptr; } } if (this->m_selectedImage.IsNotNull()) { m_Controls.spinBox_baselineStartTimeStep->setMaximum((this->m_selectedImage->GetDimension(3)) - 1); m_Controls.spinBox_baselineEndTimeStep->setMaximum((this->m_selectedImage->GetDimension(3)) - 1); } } if (m_Controls.radioButton_T2->isChecked()) { if (m_Controls.t2TimeSeriesNodeSelector->GetSelectedNode().IsNotNull()) { this->m_selectedNode = m_Controls.t2TimeSeriesNodeSelector->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); } else { this->m_selectedNode = nullptr; this->m_selectedImage = nullptr; } if (this->m_selectedImage.IsNotNull()) { m_Controls.spinBox_baselineStartTimeStepT2->setMaximum((this->m_selectedImage->GetDimension(3)) - 1); m_Controls.spinBox_baselineEndTimeStepT2->setMaximum((this->m_selectedImage->GetDimension(3)) - 1); } } m_Controls.btnConvertToConcentration->setEnabled(m_selectedImage.IsNotNull() && CheckSettings()); } ConcentrationCurveConverterView::ConcentrationCurveConverterView() { mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isLegacyMask = mitk::NodePredicateAnd::New(isImage, isBinary); mitk::NodePredicateDimension::Pointer is3D = mitk::NodePredicateDimension::New(3); mitk::NodePredicateOr::Pointer isMask = mitk::NodePredicateOr::New(isLegacyMask, isLabelSet); mitk::NodePredicateAnd::Pointer isNoMask = mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isMask)); mitk::NodePredicateAnd::Pointer is3DImage = mitk::NodePredicateAnd::New(isImage, is3D, isNoMask); this->m_IsMaskPredicate = mitk::NodePredicateAnd::New(isMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); this->m_IsNoMaskImagePredicate = mitk::NodePredicateAnd::New(isNoMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); auto isDynamicData = mitk::NodePredicateFunction::New([](const mitk::DataNode* node) { return (node && node->GetData() && node->GetData()->GetTimeSteps() > 1); }); auto modelFitResultRelationRule = mitk::ModelFitResultRelationRule::New(); auto isNoModelFitNodePredicate = mitk::NodePredicateNot::New(modelFitResultRelationRule->GetConnectedSourcesDetector()); this->m_isValidPDWImagePredicate = mitk::NodePredicateAnd::New(is3DImage, isNoModelFitNodePredicate); this->m_isValidTimeSeriesImagePredicate = mitk::NodePredicateAnd::New(isDynamicData, isImage, isNoMask); } diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterViewControls.ui b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterViewControls.ui index ef67520b67..4f493e46c3 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterViewControls.ui +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterViewControls.ui @@ -1,469 +1,475 @@ ConcentrationCurveConverterViewControls 0 0 475 - 825 + 866 0 0 QmitkTemplate 10 T1 weighted MRI T2 weighted MRI T1 weighted images 10 10 10 3D Image 4D Image 4D Image Selected Time Series: 3D Image Selected 3D Image: QFrame::NoFrame QFrame::Plain Selected Baseline Image: + + QLayout::SetDefaultConstraint + - - - 10 + + + 4 Absolute Signal Enhancement Relative Signal Enhancement - Using T1 Map via Variable Flip Angle + Variable Flip Angle TurboFLASH Sequence Qt::Vertical 20 40 Enhancement Parameters: Conversion Factor k: - - - - Baseline Range Selection: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - - - - - Start Time Frame - - - - - - - End Time Frame - - - - - - - - - T1 Map via Variable Flip Angle Parameters: + Variable Flip Angle Parameters: - - + + - Flip Angle [ ° ] : + Repetition Time [ms] : - - + + + + + - Repetition Time TR [ms] : + Proton Density Weighted Image : - + + + + + + + Relaxivity [mM⁻¹ s⁻¹] : - - - - - - - - + + - Proton Density Weighted Image : + Flip Angle PDW Image [°] 10000.000000000000000 + + + + Flip Angle [ ° ] : + + + - + true Turbo FLASH Parameters: + + + Recovery Time [s]: - - + + + + Relaxation Time [s]: + + + + + AIF Recovery Time [s]: - - - - - + + - - + + - Relaxation Time [s]: + Relaxivity [ ]: - - + + + + + + + Baseline Range Selection: + + + + + + + + + + - Relaxivity [ ]: + Start Time Frame + + + + + + + End Time Frame Convert To Concentration 0 2 0 0 T2 weighted images + + + + Selected Time Series: + + + + + + + Conversion Factor k + + + + + + + Echo Time TE [ms] + + + - - - - - - - - - - - - Start Time Frame - - - - - - - End Time Frame - - - - - - - Baseline Range Selection: - - - - - - - + + + + + + + Baseline Range Selection: + + + + - Echo Time TE [ms] + Start Time Frame - + - Selected Time Series: + End Time Frame - - - - Conversion Factor k - - + + + + + Qt::Vertical 20 40 QmitkSingleNodeSelectionWidget QWidget
QmitkSingleNodeSelectionWidget.h
1
diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.curvedescriptor/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.pharmacokinetics.curvedescriptor/manifest_headers.cmake index 3323290acb..f57a1a3321 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.curvedescriptor/manifest_headers.cmake +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.curvedescriptor/manifest_headers.cmake @@ -1,5 +1,5 @@ set(Plugin-Name "Perfusion Curve Description Parameter Plugin") set(Plugin-Version "1.0") -set(Plugin-Vendor "DKFZ, Software Development For Integrated Diagnostics and Therapy") -set(Plugin-ContactAddress "c.debus@dkfz.de") -set(Require-Plugin org.mitk.gui.qt.common) +set(Plugin-Vendor "German Cancer Research Center (DKFZ) Heidelberg, Division of Medical Image Computing, Germany") +set(Plugin-ContactAddress "Contact via www.MITK.org") +set(Require-Plugin org.mitk.gui.qt.common org.mitk.gui.qt.pharmacokinetics.concentration.mri) diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/manifest_headers.cmake index e3fff30e06..c406d9776c 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/manifest_headers.cmake +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/manifest_headers.cmake @@ -1,5 +1,5 @@ set(Plugin-Name "DCE MR Perfusion Datafit Plugin") set(Plugin-Version "1.0") -set(Plugin-Vendor "DKFZ, Software Development For Integrated Diagnostics and Therapy") -set(Plugin-ContactAddress "c.debus@dkfz.de") -set(Require-Plugin org.mitk.gui.qt.common) +set(Plugin-Vendor "German Cancer Research Center (DKFZ) Heidelberg, Division of Medical Image Computing, Germany") +set(Plugin-ContactAddress "Contact via www.MITK.org") +set(Require-Plugin org.mitk.gui.qt.common org.mitk.gui.qt.pharmacokinetics.concentration.mri) diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp index 95b4584c78..b9e675e860 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp @@ -1,1362 +1,1372 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "MRPerfusionView.h" #include "boost/tokenizer.hpp" #include "boost/math/constants/constants.hpp" #include #include "mitkWorkbenchUtil.h" #include "mitkAterialInputFunctionGenerator.h" #include "mitkConcentrationCurveGenerator.h" #include #include #include #include #include #include #include #include "mitkTwoCompartmentExchangeModelFactory.h" #include "mitkTwoCompartmentExchangeModelParameterizer.h" #include #include #include #include #include #include #include #include "mitkNodePredicateFunction.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Includes for image casting between ITK and MITK #include #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include #include const std::string MRPerfusionView::VIEW_ID = "org.mitk.views.pharmacokinetics.mri"; inline double convertToDouble(const std::string& data) { std::istringstream stepStream(data); stepStream.imbue(std::locale("C")); double value = 0.0; if (!(stepStream >> value) || !(stepStream.eof())) { mitkThrow() << "Cannot convert string to double. String: " << data; } return value; } void MRPerfusionView::SetFocus() { m_Controls.btnModelling->setFocus(); } void MRPerfusionView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnModelling->setEnabled(false); this->InitModelComboBox(); m_Controls.timeSeriesNodeSelector->SetNodePredicate(this->m_isValidTimeSeriesImagePredicate); m_Controls.timeSeriesNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.timeSeriesNodeSelector->SetSelectionIsOptional(false); m_Controls.timeSeriesNodeSelector->SetInvalidInfo("Please select time series."); m_Controls.timeSeriesNodeSelector->SetAutoSelectNewNodes(true); m_Controls.maskNodeSelector->SetNodePredicate(this->m_IsMaskPredicate); m_Controls.maskNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.maskNodeSelector->SetSelectionIsOptional(true); m_Controls.maskNodeSelector->SetEmptyInfo("Please select (optional) mask."); connect(m_Controls.btnModelling, SIGNAL(clicked()), this, SLOT(OnModellingButtonClicked())); connect(m_Controls.comboModel, SIGNAL(currentIndexChanged(int)), this, SLOT(OnModellSet(int))); connect(m_Controls.radioPixelBased, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.timeSeriesNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &MRPerfusionView::OnNodeSelectionChanged); connect(m_Controls.maskNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &MRPerfusionView::OnNodeSelectionChanged); connect(m_Controls.AIFMaskNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &MRPerfusionView::UpdateGUIControls); connect(m_Controls.AIFImageNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &MRPerfusionView::UpdateGUIControls); //AIF setting m_Controls.groupAIF->hide(); m_Controls.btnAIFFile->setEnabled(false); m_Controls.btnAIFFile->setEnabled(false); m_Controls.radioAIFImage->setChecked(true); m_Controls.AIFMaskNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.AIFMaskNodeSelector->SetNodePredicate(m_IsMaskPredicate); m_Controls.AIFMaskNodeSelector->setVisible(true); m_Controls.AIFMaskNodeSelector->setEnabled(true); m_Controls.AIFMaskNodeSelector->SetAutoSelectNewNodes(true); m_Controls.AIFImageNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.AIFImageNodeSelector->SetNodePredicate(this->m_isValidTimeSeriesImagePredicate); m_Controls.AIFImageNodeSelector->setEnabled(false); m_Controls.checkDedicatedAIFImage->setEnabled(true); m_Controls.HCLSpinBox->setValue(mitk::AterialInputFunctionGenerator::DEFAULT_HEMATOCRIT_LEVEL); - m_Controls.spinBox_baselineEndTimeStep->setMinimum(0); - m_Controls.spinBox_baselineStartTimeStep->setMinimum(0); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.AIFMaskNodeSelector, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.labelAIFMask, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.AIFMaskNodeSelector, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.AIFImageNodeSelector, SLOT(setVisible(bool))); connect(m_Controls.checkDedicatedAIFImage, SIGNAL(toggled(bool)), m_Controls.AIFImageNodeSelector, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.btnAIFFile, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.aifFilePath, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.btnAIFFile, SIGNAL(clicked()), this, SLOT(LoadAIFfromFile())); //Brix setting m_Controls.groupDescBrix->hide(); connect(m_Controls.injectiontime, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); //Num2CX setting m_Controls.groupNum2CXM->hide(); connect(m_Controls.odeStepSize, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); //Model fit configuration m_Controls.groupBox_FitConfiguration->hide(); m_Controls.checkBox_Constraints->setEnabled(false); m_Controls.constraintManager->setEnabled(false); m_Controls.initialValuesManager->setEnabled(false); m_Controls.initialValuesManager->setDataStorage(this->GetDataStorage()); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.initialValuesManager, SIGNAL(initialValuesChanged(void)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), m_Controls.initialValuesManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setVisible(bool))); //Concentration m_Controls.groupConcentration->hide(); m_Controls.groupBoxEnhancement->hide(); m_Controls.groupBoxTurboFlash->hide(); m_Controls.radioButtonNoConversion->setChecked(true); m_Controls.groupBox_T1MapviaVFA->hide(); m_Controls.spinBox_baselineStartTimeStep->setValue(0); m_Controls.spinBox_baselineEndTimeStep->setValue(0); + m_Controls.spinBox_baselineEndTimeStep->setMinimum(0); + m_Controls.spinBox_baselineStartTimeStep->setMinimum(0); + m_Controls.groupBox_baselineRangeSelection->hide(); + + connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), m_Controls.groupBoxTurboFlash, SLOT(setVisible(bool))); + connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), m_Controls.groupBox_baselineRangeSelection, SLOT(setVisible(bool))); connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.relaxationtime, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.recoverytime, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.relaxivity, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), m_Controls.groupBoxEnhancement, SLOT(setVisible(bool))); + connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), m_Controls.groupBox_baselineRangeSelection, SLOT(setVisible(bool))); connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), m_Controls.groupBoxEnhancement, SLOT(setVisible(bool))); + connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), m_Controls.groupBox_baselineRangeSelection, SLOT(setVisible(bool))); connect(m_Controls.factorSpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.spinBox_baselineStartTimeStep, SIGNAL(valueChanged(int)), this, SLOT(UpdateGUIControls())); connect(m_Controls.spinBox_baselineEndTimeStep, SIGNAL(valueChanged(int)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), m_Controls.groupBox_T1MapviaVFA, SLOT(setVisible(bool))); + connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), m_Controls.groupBox_baselineRangeSelection, SLOT(setVisible(bool))); connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.FlipangleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.RelaxivitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.TRSpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); m_Controls.PDWImageNodeSelector->SetNodePredicate(m_isValidPDWImagePredicate); m_Controls.PDWImageNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.PDWImageNodeSelector->SetInvalidInfo("Please select PDW Image."); m_Controls.PDWImageNodeSelector->setEnabled(false); connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), m_Controls.PDWImageNodeSelector, SLOT(setEnabled(bool))); UpdateGUIControls(); } bool MRPerfusionView::IsTurboFlashSequenceFlag() const { return this->m_Controls.radioButtonTurboFlash->isChecked(); }; void MRPerfusionView::UpdateGUIControls() { m_Controls.lineFitName->setPlaceholderText(QString::fromStdString(this->GetDefaultFitName())); m_Controls.lineFitName->setEnabled(!m_FittingInProgress); m_Controls.checkBox_Constraints->setEnabled(m_modelConstraints.IsNotNull()); bool isDescBrixFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr || dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; m_Controls.groupAIF->setVisible(isToftsFactory || is2CXMFactory); m_Controls.groupDescBrix->setVisible(isDescBrixFactory); m_Controls.groupConcentration->setVisible(isToftsFactory || is2CXMFactory ); m_Controls.groupBox_FitConfiguration->setVisible(m_selectedModelFactory); m_Controls.groupBox->setEnabled(!m_FittingInProgress); m_Controls.comboModel->setEnabled(!m_FittingInProgress); m_Controls.groupAIF->setEnabled(!m_FittingInProgress); m_Controls.groupDescBrix->setEnabled(!m_FittingInProgress); m_Controls.groupNum2CXM->setEnabled(!m_FittingInProgress); m_Controls.groupConcentration->setEnabled(!m_FittingInProgress); m_Controls.groupBox_FitConfiguration->setEnabled(!m_FittingInProgress); m_Controls.radioROIbased->setEnabled(m_selectedMask.IsNotNull()); m_Controls.btnModelling->setEnabled(m_selectedImage.IsNotNull() && m_selectedModelFactory.IsNotNull() && !m_FittingInProgress && CheckModelSettings()); m_Controls.spinBox_baselineStartTimeStep->setEnabled(m_Controls.radioButtonTurboFlash->isChecked() || m_Controls.radioButton_absoluteEnhancement->isChecked() || m_Controls.radioButton_relativeEnchancement->isChecked() || m_Controls.radioButtonUsingT1viaVFA->isChecked()); m_Controls.spinBox_baselineEndTimeStep->setEnabled(m_Controls.radioButton_absoluteEnhancement->isChecked() || m_Controls.radioButton_relativeEnchancement->isChecked() || m_Controls.radioButtonUsingT1viaVFA->isChecked() || m_Controls.radioButtonTurboFlash->isChecked()); } void MRPerfusionView::OnModellSet(int index) { m_selectedModelFactory = nullptr; if (index > 0) { if (static_cast(index) <= m_FactoryStack.size() ) { m_selectedModelFactory = m_FactoryStack[index - 1]; } else { MITK_WARN << "Invalid model index. Index outside of the factory stack. Factory stack size: "<< m_FactoryStack.size() << "; invalid index: "<< index; } } if (m_selectedModelFactory) { this->m_modelConstraints = dynamic_cast (m_selectedModelFactory->CreateDefaultConstraints().GetPointer()); m_Controls.initialValuesManager->setInitialValues(m_selectedModelFactory->GetParameterNames(), m_selectedModelFactory->GetDefaultInitialParameterization()); if (this->m_modelConstraints.IsNull()) { this->m_modelConstraints = mitk::SimpleBarrierConstraintChecker::New(); } m_Controls.constraintManager->setChecker(this->m_modelConstraints, this->m_selectedModelFactory->GetParameterNames()); } UpdateGUIControls(); } std::string MRPerfusionView::GetFitName() const { std::string fitName = m_Controls.lineFitName->text().toStdString(); if (fitName.empty()) { fitName = m_Controls.lineFitName->placeholderText().toStdString(); } return fitName; } std::string MRPerfusionView::GetDefaultFitName() const { std::string defaultName = "undefined model"; if (this->m_selectedModelFactory.IsNotNull()) { defaultName = this->m_selectedModelFactory->GetClassID(); } if (this->m_Controls.radioPixelBased->isChecked()) { defaultName += "_pixel"; } else { defaultName += "_roi"; } return defaultName; } void MRPerfusionView::OnModellingButtonClicked() { //check if all static parameters set if (m_selectedModelFactory.IsNotNull() && CheckModelSettings()) { m_HasGeneratedNewInput = false; m_HasGeneratedNewInputAIF = false; mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr; mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr; bool isDescBrixFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isExtToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isStanToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; if (isDescBrixFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateDescriptiveBrixModel_PixelBased(fitSession, generator); } else { GenerateDescriptiveBrixModel_ROIBased(fitSession, generator); } } else if (isStanToftsFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } else if (isExtToftsFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } else if (is2CXMFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } //add other models with else if if (generator.IsNotNull() && fitSession.IsNotNull()) { m_FittingInProgress = true; UpdateGUIControls(); DoFit(fitSession, generator); } else { QMessageBox box; box.setText("Fitting error!"); box.setInformativeText("Could not establish fitting job. Error when setting ab generator, model parameterizer or session info."); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } else { QMessageBox box; box.setText("Static parameters for model are not set!"); box.setInformativeText("Some static parameters, that are needed for calculation are not set and equal to zero. Modeling not possible"); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } void MRPerfusionView::OnNodeSelectionChanged(QList/*nodes*/) { m_selectedMaskNode = nullptr; m_selectedMask = nullptr; if (m_Controls.timeSeriesNodeSelector->GetSelectedNode().IsNotNull()) { this->m_selectedNode = m_Controls.timeSeriesNodeSelector->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); if (m_selectedImage) { this->m_Controls.initialValuesManager->setReferenceImageGeometry(m_selectedImage->GetGeometry()); } else { this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr); } } else { this->m_selectedNode = nullptr; this->m_selectedImage = nullptr; this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr); } if (m_Controls.maskNodeSelector->GetSelectedNode().IsNotNull()) { this->m_selectedMaskNode = m_Controls.maskNodeSelector->GetSelectedNode(); this->m_selectedMask = dynamic_cast(m_selectedMaskNode->GetData()); if (this->m_selectedMask.IsNotNull() && this->m_selectedMask->GetTimeSteps() > 1) { MITK_INFO << "Selected mask has multiple timesteps. Only use first timestep to mask model fit. Mask name: " << m_Controls.maskNodeSelector->GetSelectedNode()->GetName(); mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedMask = maskedImageTimeSelector->GetOutput(); } } if (m_selectedMask.IsNull()) { this->m_Controls.radioPixelBased->setChecked(true); } if (this->m_selectedImage.IsNotNull()) { m_Controls.spinBox_baselineStartTimeStep->setMaximum((this->m_selectedImage->GetDimension(3))-1); m_Controls.spinBox_baselineEndTimeStep->setMaximum((this->m_selectedImage->GetDimension(3)) - 1); } UpdateGUIControls(); } bool MRPerfusionView::CheckModelSettings() const { bool ok = true; //check whether any model is set at all. Otherwise exit with false if (m_selectedModelFactory.IsNotNull()) { bool isDescBrixFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr|| dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; if (isDescBrixFactory) { //if all static parameters for this model are set, exit with true, Otherwise exit with false ok = m_Controls.injectiontime->value() > 0; } else if (isToftsFactory || is2CXMFactory) { if (this->m_Controls.radioAIFImage->isChecked()) { ok = ok && m_Controls.AIFMaskNodeSelector->GetSelectedNode().IsNotNull(); if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { ok = ok && m_Controls.AIFImageNodeSelector->GetSelectedNode().IsNotNull(); } } else if (this->m_Controls.radioAIFFile->isChecked()) { ok = ok && (this->AIFinputGrid.size() != 0) && (this->AIFinputFunction.size() != 0); } else { ok = false; } if (this->m_Controls.radioButtonTurboFlash->isChecked() ) { ok = ok && (m_Controls.recoverytime->value() > 0); ok = ok && (m_Controls.relaxationtime->value() > 0); ok = ok && (m_Controls.relaxivity->value() > 0); ok = ok && (m_Controls.AifRecoverytime->value() > 0); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButton_absoluteEnhancement->isChecked() || this->m_Controls.radioButton_relativeEnchancement->isChecked() ) { ok = ok && (m_Controls.factorSpinBox->value() > 0); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButtonUsingT1viaVFA->isChecked() ) { ok = ok && (m_Controls.FlipangleSpinBox->value() > 0); ok = ok && (m_Controls.TRSpinBox->value() > 0); ok = ok && (m_Controls.RelaxivitySpinBox->value() > 0); ok = ok && (m_Controls.PDWImageNodeSelector->GetSelectedNode().IsNotNull()); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButtonNoConversion->isChecked()) { ok = ok && true; } else { ok = false; } } //add other models as else if and check whether all needed static parameters are set else { ok = false; } if (this->m_Controls.radioButton_StartParameters->isChecked() && !this->m_Controls.initialValuesManager->hasValidInitialValues()) { std::string warning = "Warning. Invalid start parameters. At least one parameter as an invalid image setting as source."; MITK_ERROR << warning; m_Controls.infoBox->append(QString("") + QString::fromStdString(warning) + QString("")); ok = false; }; } else { ok = false; } return ok; } bool MRPerfusionView::CheckBaselineSelectionSettings() const { return m_Controls.spinBox_baselineStartTimeStep->value() <= m_Controls.spinBox_baselineEndTimeStep->value(); } void MRPerfusionView::ConfigureInitialParametersOfParameterizer(mitk::ModelParameterizerBase* parameterizer) const { if (m_Controls.radioButton_StartParameters->isChecked()) { //use user defined initial parameters mitk::InitialParameterizationDelegateBase::Pointer paramDelegate = m_Controls.initialValuesManager->getInitialParametrizationDelegate(); parameterizer->SetInitialParameterizationDelegate(paramDelegate); } } void MRPerfusionView::GenerateDescriptiveBrixModel_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); mitk::DescriptivePharmacokineticBrixModelParameterizer::Pointer modelParameterizer = mitk::DescriptivePharmacokineticBrixModelParameterizer::New(); //Model configuration (static parameters) can be done now modelParameterizer->SetTau(m_Controls.injectiontime->value()); mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(this->m_selectedImage); imageTimeSelector->SetTimeNr(0); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::DescriptivePharmacokineticBrixModelParameterizer::BaseImageType::Pointer baseImage; mitk::CastToItkImage(imageTimeSelector->GetOutput(), baseImage); modelParameterizer->SetBaseImage(baseImage); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); roiUID = m_selectedMask->GetUID(); } fitGenerator->SetDynamicImage(m_selectedImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); } void MRPerfusionView::GenerateDescriptiveBrixModel_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { if (m_selectedMask.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::Pointer modelParameterizer = mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::New(); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); signalGenerator->SetDynamicImage(m_selectedImage); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Model configuration (static parameters) can be done now modelParameterizer->SetTau(m_Controls.injectiontime->value()); modelParameterizer->SetBaseValue(roiSignal[0]); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(m_selectedImage)); generator = fitGenerator.GetPointer(); std::string roiUID = this->m_selectedMask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); } template void MRPerfusionView::GenerateLinearModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); roiUID = this->m_selectedMask->GetUID(); } fitGenerator->SetDynamicImage(m_selectedImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); } template void MRPerfusionView::GenerateLinearModelFit_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { if (m_selectedMask.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); signalGenerator->SetDynamicImage(m_selectedImage); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Model configuration (static parameters) can be done now this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(m_selectedImage)); generator = fitGenerator.GetPointer(); std::string roiUID = this->m_selectedMask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); } template void MRPerfusionView::GenerateAIFbasedModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); PrepareConcentrationImage(); mitk::AIFBasedModelBase::AterialInputFunctionType aif; mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid; GetAIF(aif, aifTimeGrid); modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); roiUID = this->m_selectedMask->GetUID(); } fitGenerator->SetDynamicImage(this->m_inputImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, this->m_inputImage, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin(); pos != aif.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("AIF", infoSignal); } template void MRPerfusionView::GenerateAIFbasedModelFit_ROIBased( mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { if (m_selectedMask.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); PrepareConcentrationImage(); mitk::AIFBasedModelBase::AterialInputFunctionType aif; mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid; GetAIF(aif, aifTimeGrid); modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); signalGenerator->SetDynamicImage(this->m_inputImage); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(this->m_inputImage)); generator = fitGenerator.GetPointer(); std::string roiUID = this->m_selectedMask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, this->m_inputImage, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); infoSignal.clear(); for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin(); pos != aif.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("AIF", infoSignal); } void MRPerfusionView::DoFit(const mitk::modelFit::ModelFitInfo* fitSession, mitk::ParameterFitImageGeneratorBase* generator) { this->m_Controls.infoBox->append(QString("" + QString("Fitting Data Set . . .") + QString (""))); ///////////////////////// //create job and put it into the thread pool mitk::modelFit::ModelFitResultNodeVectorType additionalNodes; if (m_HasGeneratedNewInput) { additionalNodes.push_back(m_inputNode); } if (m_HasGeneratedNewInputAIF) { additionalNodes.push_back(m_inputAIFNode); } ParameterFitBackgroundJob* pJob = new ParameterFitBackgroundJob(generator, fitSession, this->m_selectedNode, additionalNodes); pJob->setAutoDelete(true); connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnJobFinished())); connect(pJob, SIGNAL(ResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), this, SLOT(OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(JobProgress(double)), this, SLOT(OnJobProgress(double))); connect(pJob, SIGNAL(JobStatusChanged(QString)), this, SLOT(OnJobStatusChanged(QString))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } MRPerfusionView::MRPerfusionView() : m_FittingInProgress(false), m_HasGeneratedNewInput(false), m_HasGeneratedNewInputAIF(false) { m_selectedImage = nullptr; m_selectedMask = nullptr; mitk::ModelFactoryBase::Pointer factory = mitk::DescriptivePharmacokineticBrixModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::StandardToftsModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::ExtendedToftsModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::TwoCompartmentExchangeModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isLegacyMask = mitk::NodePredicateAnd::New(isImage, isBinary); mitk::NodePredicateDimension::Pointer is3D = mitk::NodePredicateDimension::New(3); mitk::NodePredicateOr::Pointer isMask = mitk::NodePredicateOr::New(isLegacyMask, isLabelSet); mitk::NodePredicateAnd::Pointer isNoMask = mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isMask)); mitk::NodePredicateAnd::Pointer is3DImage = mitk::NodePredicateAnd::New(isImage, is3D, isNoMask); this->m_IsMaskPredicate = mitk::NodePredicateAnd::New(isMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); this->m_IsNoMaskImagePredicate = mitk::NodePredicateAnd::New(isNoMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); auto isDynamicData = mitk::NodePredicateFunction::New([](const mitk::DataNode* node) { return (node && node->GetData() && node->GetData()->GetTimeSteps() > 1); }); auto modelFitResultRelationRule = mitk::ModelFitResultRelationRule::New(); auto isNoModelFitNodePredicate = mitk::NodePredicateNot::New(modelFitResultRelationRule->GetConnectedSourcesDetector()); this->m_isValidPDWImagePredicate = mitk::NodePredicateAnd::New(is3DImage, isNoModelFitNodePredicate); this->m_isValidTimeSeriesImagePredicate = mitk::NodePredicateAnd::New(isDynamicData, isImage, isNoMask); } void MRPerfusionView::OnJobFinished() { this->m_Controls.infoBox->append(QString("Fitting finished.")); this->m_FittingInProgress = false; this->UpdateGUIControls(); }; void MRPerfusionView::OnJobError(QString err) { MITK_ERROR << err.toStdString().c_str(); m_Controls.infoBox->append(QString("") + err + QString("")); }; void MRPerfusionView::OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType results, const ParameterFitBackgroundJob* pJob) { //Store the resulting parameter fit image via convenience helper function in data storage //(handles the correct generation of the nodes and their properties) mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), results, pJob->GetParentNode()); //this stores the concentration image and AIF concentration image, if generated for this fit in the storage. //if not generated for this fit, relevant nodes are empty. mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), pJob->GetAdditionalRelevantNodes(), pJob->GetParentNode()); }; void MRPerfusionView::OnJobProgress(double progress) { QString report = QString("Progress. ") + QString::number(progress); this->m_Controls.infoBox->append(report); }; void MRPerfusionView::OnJobStatusChanged(QString info) { this->m_Controls.infoBox->append(info); } void MRPerfusionView::InitModelComboBox() const { this->m_Controls.comboModel->clear(); this->m_Controls.comboModel->addItem(tr("No model selected")); for (ModelFactoryStackType::const_iterator pos = m_FactoryStack.begin(); pos != m_FactoryStack.end(); ++pos) { this->m_Controls.comboModel->addItem(QString::fromStdString((*pos)->GetClassID())); } this->m_Controls.comboModel->setCurrentIndex(0); }; mitk::DataNode::Pointer MRPerfusionView::GenerateConcentrationNode(mitk::Image* image, const std::string& nodeName) const { if (!image) { mitkThrow() << "Cannot generate concentration node. Passed image is null. parameter name: "; } mitk::DataNode::Pointer result = mitk::DataNode::New(); result->SetData(image); result->SetName(nodeName); result->SetVisibility(true); return result; }; mitk::Image::Pointer MRPerfusionView::ConvertConcentrationImage(bool AIFMode) { //Compute Concentration image mitk::ConcentrationCurveGenerator::Pointer concentrationGen = mitk::ConcentrationCurveGenerator::New(); if (m_Controls.checkDedicatedAIFImage->isChecked() && AIFMode) { concentrationGen->SetDynamicImage(this->m_selectedAIFImage); } else { concentrationGen->SetDynamicImage(this->m_selectedImage); } concentrationGen->SetisTurboFlashSequence(IsTurboFlashSequenceFlag()); concentrationGen->SetAbsoluteSignalEnhancement(m_Controls.radioButton_absoluteEnhancement->isChecked()); concentrationGen->SetRelativeSignalEnhancement(m_Controls.radioButton_relativeEnchancement->isChecked()); concentrationGen->SetUsingT1Map(m_Controls.radioButtonUsingT1viaVFA->isChecked()); + if (IsTurboFlashSequenceFlag()) { if (AIFMode) { concentrationGen->SetRecoveryTime(m_Controls.AifRecoverytime->value()); } else { concentrationGen->SetRecoveryTime(m_Controls.recoverytime->value()); } concentrationGen->SetRelaxationTime(m_Controls.relaxationtime->value()); concentrationGen->SetRelaxivity(m_Controls.relaxivity->value()); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); - } else if (this->m_Controls.radioButtonUsingT1viaVFA->isChecked()) { - concentrationGen->SetRecoveryTime(m_Controls.TRSpinBox->value()); + concentrationGen->SetRepetitionTime(m_Controls.TRSpinBox->value()); concentrationGen->SetRelaxivity(m_Controls.RelaxivitySpinBox->value()); - concentrationGen->SetT10Image(dynamic_cast(m_Controls.PDWImageNodeSelector->GetSelectedNode()->GetData())); + concentrationGen->SetPDWImage(dynamic_cast(m_Controls.PDWImageNodeSelector->GetSelectedNode()->GetData())); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); //Convert Flipangle from degree to radiant double alpha = m_Controls.FlipangleSpinBox->value()/360*2* boost::math::constants::pi(); concentrationGen->SetFlipAngle(alpha); + double alphaPDW = m_Controls.FlipanglePDWSpinBox->value() / 360 * 2 * boost::math::constants::pi(); + concentrationGen->SetFlipAnglePDW(alphaPDW); + } else { concentrationGen->SetFactor(m_Controls.factorSpinBox->value()); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); } mitk::Image::Pointer concentrationImage = concentrationGen->GetConvertedImage(); return concentrationImage; } void MRPerfusionView::GetAIF(mitk::AIFBasedModelBase::AterialInputFunctionType& aif, mitk::AIFBasedModelBase::AterialInputFunctionType& aifTimeGrid) { if (this->m_Controls.radioAIFFile->isChecked()) { aif.clear(); aifTimeGrid.clear(); aif.SetSize(AIFinputFunction.size()); aifTimeGrid.SetSize(AIFinputGrid.size()); aif.fill(0.0); aifTimeGrid.fill(0.0); itk::Array::iterator aifPos = aif.begin(); for (std::vector::const_iterator pos = AIFinputFunction.begin(); pos != AIFinputFunction.end(); ++pos, ++aifPos) { *aifPos = *pos; } itk::Array::iterator gridPos = aifTimeGrid.begin(); for (std::vector::const_iterator pos = AIFinputGrid.begin(); pos != AIFinputGrid.end(); ++pos, ++gridPos) { *gridPos = *pos; } } else if (this->m_Controls.radioAIFImage->isChecked()) { aif.clear(); aifTimeGrid.clear(); mitk::AterialInputFunctionGenerator::Pointer aifGenerator = mitk::AterialInputFunctionGenerator::New(); //Hematocrit level aifGenerator->SetHCL(this->m_Controls.HCLSpinBox->value()); //mask settings this->m_selectedAIFMaskNode = m_Controls.AIFMaskNodeSelector->GetSelectedNode(); this->m_selectedAIFMask = dynamic_cast(this->m_selectedAIFMaskNode->GetData()); if (this->m_selectedAIFMask->GetTimeSteps() > 1) { MITK_INFO << "Selected AIF mask has multiple timesteps. Only use first timestep to mask model fit. AIF Mask name: " << m_selectedAIFMaskNode->GetName() ; mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedAIFMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedAIFMask = maskedImageTimeSelector->GetOutput(); } if (this->m_selectedAIFMask.IsNotNull()) { aifGenerator->SetMask(this->m_selectedAIFMask); } //image settings if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { this->m_selectedAIFImageNode = m_Controls.AIFImageNodeSelector->GetSelectedNode(); this->m_selectedAIFImage = dynamic_cast(this->m_selectedAIFImageNode->GetData()); } else { this->m_selectedAIFImageNode = m_selectedNode; this->m_selectedAIFImage = m_selectedImage; } this->PrepareAIFConcentrationImage(); aifGenerator->SetDynamicImage(this->m_inputAIFImage); aif = aifGenerator->GetAterialInputFunction(); aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid(); } else { mitkThrow() << "Cannot generate AIF. View is in a invalid state. No AIF mode selected."; } } void MRPerfusionView::LoadAIFfromFile() { QFileDialog dialog; dialog.setNameFilter(tr("Images (*.csv")); QString fileName = dialog.getOpenFileName(); m_Controls.aifFilePath->setText(fileName); std::string m_aifFilePath = fileName.toStdString(); //Read Input typedef boost::tokenizer< boost::escaped_list_separator > Tokenizer; ///////////////////////////////////////////////////////////////////////////////////////////////// //AIF Data std::ifstream in1(m_aifFilePath.c_str()); if (!in1.is_open()) { this->m_Controls.infoBox->append(QString("Could not open AIF File!")); } std::vector< std::string > vec1; std::string line1; while (getline(in1, line1)) { Tokenizer tok(line1); vec1.assign(tok.begin(), tok.end()); this->AIFinputGrid.push_back(convertToDouble(vec1[0])); this->AIFinputFunction.push_back(convertToDouble(vec1[1])); } } void MRPerfusionView::PrepareConcentrationImage() { mitk::Image::Pointer concentrationImage = this->m_selectedImage; mitk::DataNode::Pointer concentrationNode = this->m_selectedNode; m_HasGeneratedNewInput = false; if (!this->m_Controls.radioButtonNoConversion->isChecked()) { concentrationImage = this->ConvertConcentrationImage(false); concentrationNode = GenerateConcentrationNode(concentrationImage, "Concentration"); m_HasGeneratedNewInput = true; } m_inputImage = concentrationImage; m_inputNode = concentrationNode; } void MRPerfusionView::PrepareAIFConcentrationImage() { mitk::Image::Pointer concentrationImage = this->m_selectedImage; mitk::DataNode::Pointer concentrationNode = this->m_selectedNode; m_HasGeneratedNewInputAIF = false; if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { concentrationImage = this->m_selectedAIFImage; concentrationNode = this->m_selectedAIFImageNode; } if (!this->m_Controls.radioButtonNoConversion->isChecked()) { if (!IsTurboFlashSequenceFlag() && !this->m_Controls.checkDedicatedAIFImage->isChecked()) { if (m_inputImage.IsNull()) { mitkThrow() << "Cannot get AIF concentration image. Invalid view state. Input image is not defined yet, but should be."; } //we can directly use the concentration input image/node (generated by GetConcentrationImage) also for the AIF concentrationImage = this->m_inputImage; concentrationNode = this->m_inputNode; } else { concentrationImage = this->ConvertConcentrationImage(true); concentrationNode = GenerateConcentrationNode(concentrationImage, "AIF Concentration"); m_HasGeneratedNewInputAIF = true; } } m_inputAIFImage = concentrationImage; m_inputAIFNode = concentrationNode; } mitk::ModelFitFunctorBase::Pointer MRPerfusionView::CreateDefaultFitFunctor( const mitk::ModelParameterizerBase* parameterizer) const { mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor = mitk::LevenbergMarquardtModelFitFunctor::New(); mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::Pointer chi2 = mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("Chi^2", chi2); if (m_Controls.checkBox_Constraints->isChecked()) { fitFunctor->SetConstraintChecker(m_modelConstraints); } mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel(); ::itk::LevenbergMarquardtOptimizer::ScalesType scales; scales.SetSize(refModel->GetNumberOfParameters()); scales.Fill(1.0); fitFunctor->SetScales(scales); fitFunctor->SetDebugParameterMaps(m_Controls.checkDebug->isChecked()); return fitFunctor.GetPointer(); } diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionViewControls.ui b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionViewControls.ui index b4e0cb396d..2ca927f7a8 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionViewControls.ui +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionViewControls.ui @@ -1,745 +1,750 @@ MRPerfusionViewControls 0 0 489 1124 0 0 0 0 QmitkTemplate QLayout::SetDefaultConstraint 4 QLayout::SetDefaultConstraint Selected Time Series: Selected Mask: 0 40 0 40 0 0 0 40 Fitting strategy 5 QLayout::SetMinAndMaxSize 5 5 5 5 0 0 Pixel based true 0 0 ROI based Select pharmacokinetic model... AIF Mask: Select AIF from Image: 20 AIF Mask: false Dedicated AIF Image: 0 40 false 0 40 0 Select AIF from File: false Browse 5 Hematocrit Level [ ]: 1.000000000000000 0.010000000000000 Descriptive Brix-Model Parameters: QFormLayout::AllNonFixedFieldsGrow Injection Time [min]: Numeric Two Compartment Exchange Model Parameters: ODE Int Step Size [s]: 3 0.001000000000000 0.050000000000000 0 0 Model Fit Configuration 0 0 0 0 459 84 Start parameter Enter Fit Starting Parameters 0 0 0 0 - 459 + 158 232 Constraints Enter Constraints for Fit Parameters 0 0 0 200 0 - 0 + -111 449 - 397 + 437 Conversion: Signal to Concentration 10 No Signal Conversion true Absolute Signal Enhancement Relative Signal Enhancement - Using T1 Map via Variable Flip Angle + Variable Flip Angle TurboFLASH Sequence Qt::Vertical 20 40 Enhancement Parameters: Conversion Factor k: - - - - Baseline Range Selection: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - - - - - Start Time Frame - - - - - - - End Time Frame - - - - - - - - - T1 Map via Variable Flip Angle Parameters: + Variable Flip Angle Parameters: + + + + Repetition Time TR [ms] : + + + + + + Flip Angle [ ° ] : - - + + - Repetition Time TR [ms] : + Proton Density Weighted Image : - + + + + Relaxivity [mM⁻¹ s⁻¹] : - - - - + - - - - Proton Density Weighted Image : - - - 10000.000000000000000 - - + + + + Flip Angle PDW Image [ ° ]: + + + + + true Turbo FLASH Parameters: + + + + Relaxivity [ ]: + + + Recovery Time [s]: + + + + + + + Relaxation Time [s]: + + + AIF Recovery Time [s]: + + + + + + + + + + Baseline Range Selection: + + - + - - + + + + + - Relaxation Time [s]: + Start Time Frame - - - - + - Relaxivity [ ]: + End Time Frame 5 Fitting name: <html><head/><body><p>Name/prefix that should be used for the fitting results.</p><p>May be explicitly defined by the user.</p></body></html> default fit name Start Modelling Generate debug parameter images 0 0 true Qt::Vertical QSizePolicy::Expanding 20 40 QmitkSimpleBarrierManagerWidget QWidget
QmitkSimpleBarrierManagerWidget.h
QmitkInitialValuesManagerWidget QWidget
QmitkInitialValuesManagerWidget.h
QmitkSingleNodeSelectionWidget QWidget
QmitkSingleNodeSelectionWidget.h
1
diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/manifest_headers.cmake index 87c3fac8ac..704eed6542 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/manifest_headers.cmake +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/manifest_headers.cmake @@ -1,5 +1,5 @@ set(Plugin-Name "PET Dynamic DataFit Plugin") set(Plugin-Version "1.0") -set(Plugin-Vendor "DKFZ, Software Development For Integrated Diagnostics and Therapy") -set(Plugin-ContactAddress "c.debus@dkfz.de") +set(Plugin-Vendor "German Cancer Research Center (DKFZ) Heidelberg, Division of Medical Image Computing, Germany") +set(Plugin-ContactAddress "Contact via www.MITK.org") set(Require-Plugin org.mitk.gui.qt.common) diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/manifest_headers.cmake index 4134f6923e..5cbce78577 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/manifest_headers.cmake +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/manifest_headers.cmake @@ -1,5 +1,5 @@ set(Plugin-Name "Perfusion Data Simulation Plugin") set(Plugin-Version "1.0") -set(Plugin-Vendor "DKFZ, Software Development For Integrated Diagnostics and Therapy") -set(Plugin-ContactAddress "c.debus@dkfz.de") +set(Plugin-Vendor "German Cancer Research Center (DKFZ) Heidelberg, Division of Medical Image Computing, Germany") +set(Plugin-ContactAddress "Contact via www.MITK.org") set(Require-Plugin org.mitk.gui.qt.common)