diff --git a/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkNonLocalMeansDenoisingFilter.h b/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkNonLocalMeansDenoisingFilter.h index 4b05697352..878443938b 100644 --- a/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkNonLocalMeansDenoisingFilter.h +++ b/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkNonLocalMeansDenoisingFilter.h @@ -1,110 +1,147 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -/*=================================================================== - -This file is based heavily on a corresponding ITK filter. - -===================================================================*/ #ifndef __itkNonLocalMeansDenoisingFilter_h_ #define __itkNonLocalMeansDenoisingFilter_h_ #include "itkImageToImageFilter.h" #include "itkVectorImage.h" #include -#include -#include -#include -#include -#include -#include -#include namespace itk{ - /** \class NonLocalMeansDenoisingFilter - * \brief This class denoises a vectorimage according to the non local means procedure. + /** @class NonLocalMeansDenoisingFilter + * @brief This class denoises a vectorimage according to the non-local means procedure. * - * This Filter needs as an input the diffusion weigthed image and a related brainmask. - * Search- and comparisonradius need to be set! + * This Filter needs as an input a diffusion weigthed image, which will be denoised unsing the non-local means principle. + * An input mask is optional to denoise only inside the mask range. All other voxels will be set to 0. */ template< class TPixelType > class NonLocalMeansDenoisingFilter : public ImageToImageFilter< VectorImage < TPixelType, 3 >, VectorImage < TPixelType, 3 > > { public: /** Typedefs */ - typedef NonLocalMeansDenoisingFilter Self; - typedef SmartPointer Pointer; - typedef SmartPointer ConstPointer; - typedef ImageToImageFilter< VectorImage < TPixelType, 3 >, VectorImage < TPixelType, 3 > > Superclass; - typedef typename Superclass::InputImageType InputImageType; - typedef typename Superclass::OutputImageType OutputImageType; - typedef typename Superclass::OutputImageRegionType OutputImageRegionType; - typedef Image MaskImageType; - typedef VectorImageToImageFilter < TPixelType > ImageExtractorType; - typedef ChangeInformationImageFilter < MaskImageType > ChangeInformationType; - typedef ExtractImageFilter < MaskImageType, MaskImageType > ExtractImageFilterType; - typedef LabelStatisticsImageFilter < MaskImageType, MaskImageType > LabelStatisticsFilterType; - typedef InvertIntensityImageFilter < MaskImageType, MaskImageType > InvertImageFilterType; - typedef StatisticsImageFilter < MaskImageType > StatisticsFilterType; + typedef NonLocalMeansDenoisingFilter Self; + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + typedef ImageToImageFilter< VectorImage < TPixelType, 3 >, VectorImage < TPixelType, 3 > > Superclass; + typedef typename Superclass::InputImageType InputImageType; + typedef typename Superclass::OutputImageType OutputImageType; + typedef typename Superclass::OutputImageRegionType OutputImageRegionType; + typedef Image MaskImageType; /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Runtime information support. */ itkTypeMacro(NonLocalMeansDenoisingFilter, ImageToImageFilter) - /** Set/Get Macros */ + /** + * @brief Set flag to use joint information + */ itkSetMacro(UseJointInformation, bool) - itkSetMacro(ChannelRadius, int) + /** + * @brief Set the searchradius + * + * The searchradius generates a neighborhood of size (2 * searchradius + 1)³. + * Default is 4. + */ itkSetMacro(SearchRadius, int) + /** + * @brief Set the comparisonradius + * + * The comparisonradius generates neighborhoods of size (2 * comparisonradius +1)³. + * Default is 1. + */ itkSetMacro(ComparisonRadius, int) + /** + * @brief Set the variance of the noise + * + * The variance of the noise needs to be estimated to use this filter properly. + * Default is 1. + */ + itkSetMacro(Variance, double) + /** + * @brief Set flag to use a rician adaption + * + * If this flag is true the filter uses a method which is optimized for Rician distributed noise. + */ + itkSetMacro(UseRicianAdaption, bool) + /** + * @brief Get the amount of calculated Voxels + * + * @return the number of calculated Voxels until yet, useful for the use of a progressbars. + */ itkGetMacro(CurrentVoxelCount, unsigned int) + + /** @brief Set the input image. **/ void SetInputImage(const InputImageType* image); - void SetInputMask(const MaskImageType* mask); + /** + * @brief Set a denoising mask + * + * optional + * + * Set a mask to denoise only the masked area, all voxel outside this area will be set to 0. + */ + void SetInputMask(MaskImageType* mask); protected: NonLocalMeansDenoisingFilter(); ~NonLocalMeansDenoisingFilter() {} - void PrintSelf(std::ostream& os, Indent indent) const; + /** + * @brief Calculations which need to be done before the denoising starts + * + * This method is called before the denoising starts. It calculates the ROI if a mask is used + * and sets the number of processed voxels to zero. + */ void BeforeThreadedGenerateData(); + + /** + * @brief Denoising procedure + * + * This method calculates the denoised voxelvalue for each voxel in the image in multiple threads. + * If a mask is used, voxels outside the masked area will be set to 0. + * + * @param outputRegionForThread Region to denoise for each thread. + */ void ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, ThreadIdType); private: - int m_SearchRadius; - int m_ComparisonRadius; - int m_ChannelRadius; - VariableLengthVector< double > m_Deviations; - bool m_UseJointInformation; - unsigned int m_CurrentVoxelCount; + int m_SearchRadius; ///< Radius of the searchblock. + int m_ComparisonRadius; ///< Radius of the comparisonblock. + bool m_UseJointInformation; ///< Flag to use joint information. + bool m_UseRicianAdaption; ///< Flag to use rician adaption. + unsigned int m_CurrentVoxelCount; ///< Amount of processed voxels. + double m_Variance; ///< Estimated noise variance. + typename MaskImageType::Pointer m_Mask; ///< Pointer to the mask image. }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkNonLocalMeansDenoisingFilter.txx" #endif #endif //__itkNonLocalMeansDenoisingFilter_h_ diff --git a/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkNonLocalMeansDenoisingFilter.txx b/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkNonLocalMeansDenoisingFilter.txx index 59ad9d019a..c14261b757 100644 --- a/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkNonLocalMeansDenoisingFilter.txx +++ b/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkNonLocalMeansDenoisingFilter.txx @@ -1,321 +1,376 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __itkNonLocalMeansDenoisingFilter_txx #define __itkNonLocalMeansDenoisingFilter_txx #include #include #include #define _USE_MATH_DEFINES #include #include "itkImageRegionIterator.h" #include "itkNeighborhoodIterator.h" #include #include namespace itk { template< class TPixelType > NonLocalMeansDenoisingFilter< TPixelType > ::NonLocalMeansDenoisingFilter() + : m_SearchRadius(4), + m_ComparisonRadius(1), + m_UseJointInformation(false), + m_UseRicianAdaption(false), + m_Variance(1), + m_Mask(NULL) { - this->SetNumberOfRequiredInputs( 2 ); + this->SetNumberOfRequiredInputs( 1 ); } template< class TPixelType > void NonLocalMeansDenoisingFilter< TPixelType > ::BeforeThreadedGenerateData() { - typename OutputImageType::Pointer outputImage = - static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); - typename OutputImageType::PixelType px; - px.SetSize(1); - px.SetElement(0,0); - outputImage->FillBuffer(px); - - typename InputImageType::Pointer inImage = static_cast< InputImageType* >(this->ProcessObject::GetInput(0)); - typename MaskImageType::Pointer mask = static_cast< MaskImageType* >(this->ProcessObject::GetInput(1)); - int size = inImage->GetVectorLength(); - m_Deviations.SetSize(size); - typename ImageExtractorType::Pointer extractor = ImageExtractorType::New(); - extractor->SetInput(inImage); - - // calculate max value of mask, for correct inversion - typename StatisticsFilterType::Pointer statisticsFilter = StatisticsFilterType::New(); - statisticsFilter->SetInput(mask); - statisticsFilter->Update(); - - // invert mask, to mask the backround - typename InvertImageFilterType::Pointer inverter = InvertImageFilterType::New(); - inverter->SetInput(mask); - inverter->SetMaximum(statisticsFilter->GetMaximum()); - inverter->Update(); - - // make sure inverted mask has same origin is the brainmask - typename ChangeInformationType::Pointer changeMaskFilter = ChangeInformationType::New(); - changeMaskFilter->ChangeOriginOn(); - changeMaskFilter->SetInput(inverter->GetOutput()); - changeMaskFilter->SetOutputOrigin(mask->GetOrigin()); - changeMaskFilter->Update(); - typename MaskImageType::Pointer invertedMask = changeMaskFilter->GetOutput(); - typename MaskImageType::PointType imageOrigin = inImage->GetOrigin(); - typename MaskImageType::PointType maskOrigin = invertedMask->GetOrigin(); - long offset[3]; - - typedef itk::ContinuousIndex ContinousIndexType; - ContinousIndexType maskOriginContinousIndex, imageOriginContinousIndex; - - inImage->TransformPhysicalPointToContinuousIndex(maskOrigin, maskOriginContinousIndex); - inImage->TransformPhysicalPointToContinuousIndex(imageOrigin, imageOriginContinousIndex); - - // make sure there is no misalignment between mask and image - for ( unsigned int i = 0; i < 3; ++i ) + + MITK_INFO << "SearchRadius: " << m_SearchRadius; + MITK_INFO << "ComparisonRadius: " << m_ComparisonRadius; + MITK_INFO << "Noisevariance: " << m_Variance; + MITK_INFO << "Use Rician Adaption: " << std::boolalpha << m_UseRicianAdaption; + MITK_INFO << "Use Joint Information: " << std::boolalpha << m_UseJointInformation; + + + typename InputImageType::Pointer inputImagePointer = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); + if (m_Mask.IsNull()) { - double misalignment = maskOriginContinousIndex[i] - floor( maskOriginContinousIndex[i] + 0.5 ); - if ( fabs( misalignment ) > mitk::eps ) + // If no mask is used generate a mask of the complete image + + m_Mask = MaskImageType::New(); + m_Mask->SetRegions(inputImagePointer->GetLargestPossibleRegion()); + m_Mask->Allocate(); + m_Mask->FillBuffer(1); + } + else + { + // Calculation of the smallest masked region + + ImageRegionIterator< MaskImageType > mit(m_Mask, m_Mask->GetLargestPossibleRegion()); + mit.GoToBegin(); + typename MaskImageType::IndexType minIndex; + typename MaskImageType::IndexType maxIndex; + minIndex.Fill(10000); + maxIndex.Fill(0); + while (!mit.IsAtEnd()) { - itkExceptionMacro( << "Pixels/voxels of mask and image are not sufficiently aligned! (Misalignment: " << misalignment << ")" ); + + if (mit.Get()) + { + // calculation of the start & end index of the smallest masked region + minIndex[0] = minIndex[0] < mit.GetIndex()[0] ? minIndex[0] : mit.GetIndex()[0]; + minIndex[1] = minIndex[1] < mit.GetIndex()[1] ? minIndex[1] : mit.GetIndex()[1]; + minIndex[2] = minIndex[2] < mit.GetIndex()[2] ? minIndex[2] : mit.GetIndex()[2]; + + maxIndex[0] = maxIndex[0] > mit.GetIndex()[0] ? maxIndex[0] : mit.GetIndex()[0]; + maxIndex[1] = maxIndex[1] > mit.GetIndex()[1] ? maxIndex[1] : mit.GetIndex()[1]; + maxIndex[2] = maxIndex[2] > mit.GetIndex()[2] ? maxIndex[2] : mit.GetIndex()[2]; + } + ++mit; } - double indexCoordDistance = maskOriginContinousIndex[i] - imageOriginContinousIndex[i]; - offset[i] = (int) indexCoordDistance + inImage->GetBufferedRegion().GetIndex()[i]; - } + // calculation of the masked region + typename OutputImageType::SizeType size; + size[0] = maxIndex[0] - minIndex[0]; + size[1] = maxIndex[1] - minIndex[1]; + size[2] = maxIndex[2] - minIndex[2]; - // calculate for each channel the stddev - for ( int i = 0; i < size; ++i) - { - /// extract channel i of the input - extractor->SetIndex(i); - extractor->Update(); - - // adapt mask to the image - typename ChangeInformationType::Pointer adaptMaskFilter; - adaptMaskFilter = ChangeInformationType::New(); - adaptMaskFilter->ChangeOriginOn(); - adaptMaskFilter->ChangeRegionOn(); - adaptMaskFilter->SetInput( invertedMask ); - adaptMaskFilter->SetOutputOrigin( extractor->GetOutput()->GetOrigin() /*image->GetOrigin()*/ ); - adaptMaskFilter->SetOutputOffset( offset ); - adaptMaskFilter->Update(); - - // extract backround as the ROI - typename MaskImageType::Pointer adaptedMaskImage = adaptMaskFilter->GetOutput(); - typename ExtractImageFilterType::Pointer extractImageFilter = ExtractImageFilterType::New(); - extractImageFilter->SetInput( extractor->GetOutput() ); - extractImageFilter->SetExtractionRegion( adaptedMaskImage->GetBufferedRegion() ); - extractImageFilter->Update(); - - // calculate statistics of ROI - typename MaskImageType::Pointer adaptedImage = extractImageFilter->GetOutput(); - typename LabelStatisticsFilterType::Pointer labelStatisticsFilter = LabelStatisticsFilterType::New(); - labelStatisticsFilter->SetInput(adaptedImage); - labelStatisticsFilter->SetLabelInput(adaptedMaskImage); - labelStatisticsFilter->UseHistogramsOff(); - labelStatisticsFilter->GetOutput()->SetRequestedRegion( adaptedMaskImage->GetLargestPossibleRegion() ); - labelStatisticsFilter->Update(); - - // save the stddev of each channel - m_Deviations.SetElement(i, labelStatisticsFilter->GetSigma(1)); + typename OutputImageType::RegionType region (minIndex, size); + typename OutputImageType::Pointer outputImage = + static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); + outputImage->SetRequestedRegion(region); } m_CurrentVoxelCount = 0; } template< class TPixelType > void NonLocalMeansDenoisingFilter< TPixelType > ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType ) { + + // initialize iterators typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); ImageRegionIterator< OutputImageType > oit(outputImage, outputRegionForThread); oit.GoToBegin(); + ImageRegionIterator< MaskImageType > mit(m_Mask, outputRegionForThread); + mit.GoToBegin(); + + + typedef ImageRegionIteratorWithIndex InputIteratorType; typename InputImageType::Pointer inputImagePointer = NULL; inputImagePointer = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); InputIteratorType git(inputImagePointer, outputRegionForThread ); - InputIteratorType njit(inputImagePointer, outputRegionForThread ); - InputIteratorType niit(inputImagePointer, outputRegionForThread ); - InputIteratorType hit(inputImagePointer, outputRegionForThread); git.GoToBegin(); // iterate over complete image region while( !git.IsAtEnd() ) { - - typename OutputImageType::PixelType outpix; outpix.SetSize (inputImagePointer->GetVectorLength()); - for (int i = 0; i < (int)inputImagePointer->GetVectorLength(); ++i) + if (mit.Get() != 0 && !this->GetAbortGenerateData()) { - double Z = 0; - double sumj = 0; - double w = 0; - double deviation = m_Deviations.GetElement(i); - std::vector wj; - std::vector p; - - for (int x = git.GetIndex().GetElement(0) - m_SearchRadius; x <= git.GetIndex().GetElement(0) + m_SearchRadius; ++x) + if(!m_UseJointInformation) { - for (int y = git.GetIndex().GetElement(1) - m_SearchRadius; y <= git.GetIndex().GetElement(1) + m_SearchRadius; ++y) + for (int i = 0; i < (int)inputImagePointer->GetVectorLength(); ++i) { - for (int z = git.GetIndex().GetElement(2) - m_SearchRadius; z <= git.GetIndex().GetElement(2) + m_SearchRadius; ++z) + double summw = 0; + double sumj = 0; + double w = 0; + std::vector wj; + std::vector p; + typename InputIteratorType::IndexType index; + index = git.GetIndex(); + + for (int x = index.GetElement(0) - m_SearchRadius; x <= index.GetElement(0) + m_SearchRadius; ++x) { - typename InputIteratorType::IndexType idx; - idx.SetElement(0, x); - idx.SetElement(1, y); - idx.SetElement(2, z); - if (inputImagePointer->GetLargestPossibleRegion().IsInside(idx)) + for (int y = index.GetElement(1) - m_SearchRadius; y <= index.GetElement(1) + m_SearchRadius; ++y) { - hit.SetIndex(idx); - TPixelType pixelJ = hit.Get()[i]; - double sumk = 0; - double size = 0; - for (int xi = git.GetIndex().GetElement(0) - m_ComparisonRadius, xj = hit.GetIndex().GetElement(0) - m_ComparisonRadius; xi <= git.GetIndex().GetElement(0) + m_ComparisonRadius; ++xi, ++xj) + for (int z = index.GetElement(2) - m_SearchRadius; z <= index.GetElement(2) + m_SearchRadius; ++z) { - for (int yi = git.GetIndex().GetElement(1) - m_ComparisonRadius, yj = hit.GetIndex().GetElement(1) - m_ComparisonRadius; yi <= git.GetIndex().GetElement(1) + m_ComparisonRadius; ++yi, ++yj) + typename InputIteratorType::IndexType indexV; + indexV.SetElement(0, x); + indexV.SetElement(1, y); + indexV.SetElement(2, z); + if (inputImagePointer->GetLargestPossibleRegion().IsInside(indexV)) { - for (int zi = git.GetIndex().GetElement(2) - m_ComparisonRadius, zj = hit.GetIndex().GetElement(2) - m_ComparisonRadius; zi <= git.GetIndex().GetElement(2) + m_ComparisonRadius; ++zi, ++zj) + TPixelType pixelJ = inputImagePointer->GetPixel(indexV)[i]; + double sumk = 0; + double size = 0; + for (int xi = index.GetElement(0) - m_ComparisonRadius, xj = x - m_ComparisonRadius; xi <= index.GetElement(0) + m_ComparisonRadius; ++xi, ++xj) { - typename InputIteratorType::IndexType indexI, indexJ; - indexI.SetElement(0, xi); - indexI.SetElement(1, yi); - indexI.SetElement(2, zi); - indexJ.SetElement(0, xj); - indexJ.SetElement(1, yj); - indexJ.SetElement(2, zj); - // Count amount of used neighbors - if (inputImagePointer->GetLargestPossibleRegion().IsInside(indexI) && inputImagePointer->GetLargestPossibleRegion().IsInside(indexJ)) + for (int yi = index.GetElement(1) - m_ComparisonRadius, yj = y - m_ComparisonRadius; yi <= index.GetElement(1) + m_ComparisonRadius; ++yi, ++yj) { - if (m_UseJointInformation) + for (int zi = index.GetElement(2) - m_ComparisonRadius, zj = z - m_ComparisonRadius; zi <= index.GetElement(2) + m_ComparisonRadius; ++zi, ++zj) { - for (int d = i - m_ChannelRadius; d <= i + m_ChannelRadius; ++d) + typename InputIteratorType::IndexType indexI, indexJ; + indexI.SetElement(0, xi); + indexI.SetElement(1, yi); + indexI.SetElement(2, zi); + indexJ.SetElement(0, xj); + indexJ.SetElement(1, yj); + indexJ.SetElement(2, zj); + + + // Compare neighborhoods ni & nj + if (inputImagePointer->GetLargestPossibleRegion().IsInside(indexI) && inputImagePointer->GetLargestPossibleRegion().IsInside(indexJ)) { - if (d >= 0 && d < (int)inputImagePointer->GetVectorLength()) - { - size++; - } + int diff = inputImagePointer->GetPixel(indexI)[i] - inputImagePointer->GetPixel(indexJ)[i]; + sumk += (double)(diff*diff); + ++size; } } - else - { - size++; - } } } + // weight all neighborhoods + w = std::exp( - sumk / size / m_Variance); + wj.push_back(w); + if (m_UseRicianAdaption) + { + p.push_back((double)(pixelJ*pixelJ)); + } + else + { + p.push_back((double)(pixelJ)); + } + summw += w; } } - for (int xi = git.GetIndex().GetElement(0) - m_ComparisonRadius, xj = hit.GetIndex().GetElement(0) - m_ComparisonRadius; xi <= git.GetIndex().GetElement(0) + m_ComparisonRadius; ++xi, ++xj) + } + } + for (unsigned int n = 0; n < wj.size(); ++n) + { + sumj += (wj[n]/summw) * p[n]; + } + if (m_UseRicianAdaption) + { + sumj -=2 * m_Variance; + } + + if (sumj < 0) + { + sumj = 0; + } + + TPixelType outval; + if (m_UseRicianAdaption) + { + outval = std::floor(std::sqrt(sumj) + 0.5); + } + else + { + outval = std::floor(sumj + 0.5); + } + outpix.SetElement(i, outval); + } + } + + else + { + // same procedure for vektoranalysis + + double Z = 0; + itk::VariableLengthVector sumj; + sumj.SetSize(inputImagePointer->GetVectorLength()); + sumj.Fill(0.0); + double w = 0; + std::vector wj; + std::vector > p; + typename InputIteratorType::IndexType index; + index = git.GetIndex(); + + for (int x = index.GetElement(0) - m_SearchRadius; x <= index.GetElement(0) + m_SearchRadius; ++x) + { + for (int y = index.GetElement(1) - m_SearchRadius; y <= index.GetElement(1) + m_SearchRadius; ++y) + { + for (int z = index.GetElement(2) - m_SearchRadius; z <= index.GetElement(2) + m_SearchRadius; ++z) + { + typename InputIteratorType::IndexType indexV; + indexV.SetElement(0, x); + indexV.SetElement(1, y); + indexV.SetElement(2, z); + if (inputImagePointer->GetLargestPossibleRegion().IsInside(indexV)) { - for (int yi = git.GetIndex().GetElement(1) - m_ComparisonRadius, yj = hit.GetIndex().GetElement(1) - m_ComparisonRadius; yi <= git.GetIndex().GetElement(1) + m_ComparisonRadius; ++yi, ++yj) + typename InputImageType::PixelType pixelJ = inputImagePointer->GetPixel(indexV); + double sumk = 0; + double size = 0; + for (int xi = index.GetElement(0) - m_ComparisonRadius, xj = x - m_ComparisonRadius; xi <= index.GetElement(0) + m_ComparisonRadius; ++xi, ++xj) { - for (int zi = git.GetIndex().GetElement(2) - m_ComparisonRadius, zj = hit.GetIndex().GetElement(2) - m_ComparisonRadius; zi <= git.GetIndex().GetElement(2) + m_ComparisonRadius; ++zi, ++zj) + for (int yi = index.GetElement(1) - m_ComparisonRadius, yj = y - m_ComparisonRadius; yi <= index.GetElement(1) + m_ComparisonRadius; ++yi, ++yj) { - typename InputIteratorType::IndexType indexI, indexJ; - indexI.SetElement(0, xi); - indexI.SetElement(1, yi); - indexI.SetElement(2, zi); - indexJ.SetElement(0, xj); - indexJ.SetElement(1, yj); - indexJ.SetElement(2, zj); - // Compare neighborhoods ni & nj - if (inputImagePointer->GetLargestPossibleRegion().IsInside(indexI) && inputImagePointer->GetLargestPossibleRegion().IsInside(indexJ)) + for (int zi = index.GetElement(2) - m_ComparisonRadius, zj = z - m_ComparisonRadius; zi <= index.GetElement(2) + m_ComparisonRadius; ++zi, ++zj) { - niit.SetIndex(indexI); - njit.SetIndex(indexJ); - if (m_UseJointInformation) - { - // if filter should use joint information. A 4d Neighborhood including surrounding channels is used. - for (int d = i - m_ChannelRadius; d <= i + m_ChannelRadius; ++d) - { - if (d >= 0 && d < (int)inputImagePointer->GetVectorLength()) - { - int diff = niit.Get()[d] - njit.Get()[d]; - sumk += (double)(diff*diff); - } - } - } - else + typename InputIteratorType::IndexType indexI, indexJ; + indexI.SetElement(0, xi); + indexI.SetElement(1, yi); + indexI.SetElement(2, zi); + indexJ.SetElement(0, xj); + indexJ.SetElement(1, yj); + indexJ.SetElement(2, zj); + // Compare neighborhoods ni & nj + if (inputImagePointer->GetLargestPossibleRegion().IsInside(indexI) && inputImagePointer->GetLargestPossibleRegion().IsInside(indexJ)) { - int diff = niit.Get()[i] - njit.Get()[i]; - sumk += (double)(diff*diff); + typename InputImageType::PixelType diff = inputImagePointer->GetPixel(indexI) - inputImagePointer->GetPixel(indexJ); + sumk += (double)(diff.GetSquaredNorm()); + ++size; } } } } + // weight all neighborhoods + size *= inputImagePointer->GetVectorLength() + 1; + w = std::exp( - (sumk / size) / m_Variance); + wj.push_back(w); + if (m_UseRicianAdaption) + { + itk::VariableLengthVector m; + m.SetSize(inputImagePointer->GetVectorLength()); + for (unsigned int i = 0; i < inputImagePointer->GetVectorLength(); ++i) + { + double sp = (double)(pixelJ.GetElement(i) * pixelJ.GetElement(i)); + m.SetElement(i,sp); + } + p.push_back(m); + } + else + ++size; + { + p.push_back(pixelJ); + } + Z += w; } - // weight all neighborhoods - w = std::exp( - (std::sqrt((sumk / size)) / (deviation * deviation))); - wj.push_back(w); - p.push_back((double)(pixelJ*pixelJ)); - Z += w; } } } - } - for (unsigned int n = 0; n < wj.size(); ++n) - { - sumj += (wj[n]/Z) * p[n]; - } - double df = sumj - (2 * deviation * deviation); - if (df < 0) - { - df = 0; - } + for (unsigned int n = 0; n < wj.size(); ++n) + { + sumj = sumj + ((wj[n]/Z) * p[n]); + } + if (m_UseRicianAdaption) + { + sumj = sumj - (2 * m_Variance); + } - TPixelType outval = std::floor(std::sqrt(df) + 0.5); - outpix.SetElement(i, outval); + for (unsigned int i = 0; i < inputImagePointer->GetVectorLength(); ++i) + { + double a = sumj.GetElement(i); + if (a < 0) + { + a = 0; + } + TPixelType outval; + if (m_UseRicianAdaption) + { + outval = std::floor(std::sqrt(a) + 0.5); + } + else + { + outval = std::floor(a + 0.5); + } + outpix.SetElement(i, outval); + } + } + } + else + { + outpix.Fill(0); } oit.Set(outpix); ++oit; ++m_CurrentVoxelCount; ++git; + ++mit; } MITK_INFO << "One Thread finished calculation"; } -template< class TPixelType > -void NonLocalMeansDenoisingFilter< TPixelType >::PrintSelf(std::ostream& os, Indent indent) const -{ - Superclass::PrintSelf(os,indent); -} - template< class TPixelType > void NonLocalMeansDenoisingFilter< TPixelType >::SetInputImage(const InputImageType* image) { this->SetNthInput(0, const_cast< InputImageType* >(image)); } template< class TPixelType > -void NonLocalMeansDenoisingFilter< TPixelType >::SetInputMask(const MaskImageType* mask) +void NonLocalMeansDenoisingFilter< TPixelType >::SetInputMask(MaskImageType* mask) { - this->SetNthInput(1, const_cast< MaskImageType* >(mask)); + m_Mask = mask; } } #endif // __itkNonLocalMeansDenoisingFilter_txx diff --git a/Modules/DiffusionImaging/DiffusionCore/Testing/mitkNonLocalMeansDenoisingTest.cpp b/Modules/DiffusionImaging/DiffusionCore/Testing/mitkNonLocalMeansDenoisingTest.cpp index e3c70f3b87..680b5c6295 100644 --- a/Modules/DiffusionImaging/DiffusionCore/Testing/mitkNonLocalMeansDenoisingTest.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/Testing/mitkNonLocalMeansDenoisingTest.cpp @@ -1,124 +1,176 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDiffusionImage.h" #include "mitkIOUtil.h" #include "mitkTestingMacros.h" #include "mitkTestFixture.h" #include "itkNonLocalMeansDenoisingFilter.h" class mitkNonLocalMeansDenoisingTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkNonLocalMeansDenoisingTestSuite); - MITK_TEST(Denoise_NLMr_1_1_shouldReturnTrue); - MITK_TEST(Denoise_NLMv_1_1_1_shouldReturnTrue); + MITK_TEST(Denoise_NLMg_shouldReturnTrue); + MITK_TEST(Denoise_NLMr_shouldReturnTrue); + MITK_TEST(Denoise_NLMv_shouldReturnTrue); + MITK_TEST(Denoise_NLMvr_shouldReturnTrue); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different (sub-)tests. All members are initialized via setUp().*/ mitk::DiffusionImage::Pointer m_Image; mitk::DiffusionImage::Pointer m_ReferenceImage; mitk::DiffusionImage::Pointer m_DenoisedImage; itk::Image::Pointer m_ImageMask; itk::NonLocalMeansDenoisingFilter::Pointer m_DenoisingFilter; public: /** * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() { //generate test images std::string imagePath = GetTestDataFilePath("DiffusionImaging/Denoising/test_multi.dwi"); - std::string maskPath = GetTestDataFilePath("DiffusionImaging/Denoising/denoising_mask.nrrd"); +// std::string maskPath = GetTestDataFilePath("DiffusionImaging/Denoising/denoising_mask.nrrd"); m_Image = static_cast*>( mitk::IOUtil::LoadImage(imagePath).GetPointer()); - mitk::Image::Pointer imageMask = static_cast( mitk::IOUtil::LoadImage(maskPath).GetPointer()); - mitk::CastToItkImage(imageMask, m_ImageMask); +// mitk::Image::Pointer imageMask = static_cast( mitk::IOUtil::LoadImage(maskPath).GetPointer()); +// mitk::CastToItkImage(imageMask, m_ImageMask); m_ReferenceImage = NULL; m_DenoisedImage = mitk::DiffusionImage::New(); //initialise Filter m_DenoisingFilter = itk::NonLocalMeansDenoisingFilter::New(); m_DenoisingFilter->SetInputImage(m_Image->GetVectorImage()); - m_DenoisingFilter->SetInputMask(m_ImageMask); +// m_DenoisingFilter->SetInputMask(m_ImageMask); m_DenoisingFilter->SetNumberOfThreads(1); m_DenoisingFilter->SetComparisonRadius(1); m_DenoisingFilter->SetSearchRadius(1); + m_DenoisingFilter->SetVariance(500); } void tearDown() { m_Image = NULL; m_ImageMask = NULL; m_ReferenceImage = NULL; m_DenoisingFilter = NULL; m_DenoisedImage = NULL; } - void Denoise_NLMr_1_1_shouldReturnTrue() + void Denoise_NLMg_shouldReturnTrue() { - std::string referenceImagePath = GetTestDataFilePath("DiffusionImaging/Denoising/test_multi_NLMr_1-1.dwi"); + std::string referenceImagePath = GetTestDataFilePath("DiffusionImaging/Denoising/test_multi_NLMg.dwi"); m_ReferenceImage = static_cast*>( mitk::IOUtil::LoadImage(referenceImagePath).GetPointer()); + m_DenoisingFilter->SetUseRicianAdaption(false); + m_DenoisingFilter->SetUseJointInformation(false); + try + { + m_DenoisingFilter->Update(); + } + catch(std::exception& e) + { + MITK_ERROR << e.what(); + } + + m_DenoisedImage->SetVectorImage(m_DenoisingFilter->GetOutput()); + m_DenoisedImage->SetReferenceBValue(m_Image->GetReferenceBValue()); + m_DenoisedImage->SetDirections(m_Image->GetDirections()); + m_DenoisedImage->InitializeFromVectorImage(); + + MITK_ASSERT_EQUAL( m_DenoisedImage, m_ReferenceImage, "NLMg should always return the same result."); + } + + void Denoise_NLMr_shouldReturnTrue() + { + std::string referenceImagePath = GetTestDataFilePath("DiffusionImaging/Denoising/test_multi_NLMr.dwi"); + m_ReferenceImage = static_cast*>( mitk::IOUtil::LoadImage(referenceImagePath).GetPointer()); + + m_DenoisingFilter->SetUseRicianAdaption(true); m_DenoisingFilter->SetUseJointInformation(false); try { m_DenoisingFilter->Update(); } catch(std::exception& e) { MITK_ERROR << e.what(); } m_DenoisedImage->SetVectorImage(m_DenoisingFilter->GetOutput()); m_DenoisedImage->SetReferenceBValue(m_Image->GetReferenceBValue()); m_DenoisedImage->SetDirections(m_Image->GetDirections()); m_DenoisedImage->InitializeFromVectorImage(); MITK_ASSERT_EQUAL( m_DenoisedImage, m_ReferenceImage, "NLMr should always return the same result."); } - void Denoise_NLMv_1_1_1_shouldReturnTrue() + void Denoise_NLMv_shouldReturnTrue() { - std::string referenceImagePath = GetTestDataFilePath("DiffusionImaging/Denoising/test_multi_NLMv_1-1-1.dwi"); + std::string referenceImagePath = GetTestDataFilePath("DiffusionImaging/Denoising/test_multi_NLMv.dwi"); m_ReferenceImage = static_cast*>( mitk::IOUtil::LoadImage(referenceImagePath).GetPointer()); - m_DenoisingFilter->SetChannelRadius(1); + m_DenoisingFilter->SetUseRicianAdaption(false); m_DenoisingFilter->SetUseJointInformation(true); try { m_DenoisingFilter->Update(); } catch(std::exception& e) { MITK_ERROR << e.what(); } m_DenoisedImage->SetVectorImage(m_DenoisingFilter->GetOutput()); m_DenoisedImage->SetReferenceBValue(m_Image->GetReferenceBValue()); m_DenoisedImage->SetDirections(m_Image->GetDirections()); m_DenoisedImage->InitializeFromVectorImage(); MITK_ASSERT_EQUAL( m_DenoisedImage, m_ReferenceImage, "NLMv should always return the same result."); } + void Denoise_NLMvr_shouldReturnTrue() + { + std::string referenceImagePath = GetTestDataFilePath("DiffusionImaging/Denoising/test_multi_NLMvr.dwi"); + m_ReferenceImage = static_cast*>( mitk::IOUtil::LoadImage(referenceImagePath).GetPointer()); + + m_DenoisingFilter->SetUseRicianAdaption(true); + m_DenoisingFilter->SetUseJointInformation(true); + try + { + m_DenoisingFilter->Update(); + } + catch(std::exception& e) + { + MITK_ERROR << e.what(); + } + + m_DenoisedImage->SetVectorImage(m_DenoisingFilter->GetOutput()); + m_DenoisedImage->SetReferenceBValue(m_Image->GetReferenceBValue()); + m_DenoisedImage->SetDirections(m_Image->GetDirections()); + m_DenoisedImage->InitializeFromVectorImage(); + + MITK_ASSERT_EQUAL( m_DenoisedImage, m_ReferenceImage, "NLMvr should always return the same result."); + } + }; MITK_TEST_SUITE_REGISTRATION(mitkNonLocalMeansDenoising) diff --git a/Modules/DiffusionImaging/MiniApps/DwiDenoising.cpp b/Modules/DiffusionImaging/MiniApps/DwiDenoising.cpp index 0bdac8ead8..9d9e63f1b4 100644 --- a/Modules/DiffusionImaging/MiniApps/DwiDenoising.cpp +++ b/Modules/DiffusionImaging/MiniApps/DwiDenoising.cpp @@ -1,172 +1,161 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "MiniAppManager.h" #include #include #include "ctkCommandLineParser.h" #include #include #include #include #include typedef mitk::DiffusionImage DiffusionImageType; typedef itk::Image ImageType; mitk::BaseData::Pointer LoadFile(std::string filename) { if( filename.empty() ) return NULL; const std::string s1="", s2=""; std::vector infile = mitk::BaseDataIO::LoadBaseDataFromFile( filename, s1, s2, false ); if( infile.empty() ) { MITK_INFO << "File " << filename << " could not be read!"; return NULL; } mitk::BaseData::Pointer baseData = infile.at(0); return baseData; } /** * Denoises DWI using the Nonlocal - Means algorithm */ int DwiDenoising(int argc, char* argv[]) { ctkCommandLineParser parser; parser.setArgumentPrefix("--", "-"); parser.addArgument("input", "i", ctkCommandLineParser::String, "input image (DWI)", us::Any(), false); - parser.addArgument("mask", "m", ctkCommandLineParser::String, "brainmask for input image", us::Any(), false); - parser.addArgument("search", "s", ctkCommandLineParser::Int, "search radius", us::Any(), false); - parser.addArgument("compare", "c", ctkCommandLineParser::Int, "compare radius", us::Any(), false); - parser.addArgument("channels", "ch", ctkCommandLineParser::Int, "radius of used channels", us::Any(), true); + parser.addArgument("variance", "v", ctkCommandLineParser::Float, "noise variance", us::Any(), false); + parser.addArgument("mask", "m", ctkCommandLineParser::String, "brainmask for input image", us::Any(), true); + parser.addArgument("search", "s", ctkCommandLineParser::Int, "search radius", us::Any(), true); + parser.addArgument("compare", "c", ctkCommandLineParser::Int, "compare radius", us::Any(), true); + parser.addArgument("joint", "j", ctkCommandLineParser::Bool, "use joint information"); + parser.addArgument("rician", "r", ctkCommandLineParser::Bool, "use rician adaption"); map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) return EXIT_FAILURE; string inFileName = us::any_cast(parsedArgs["input"]); - string maskName = us::any_cast(parsedArgs["mask"]); + double variance = static_cast(us::any_cast(parsedArgs["variance"])); + string maskName; + if (parsedArgs.count("mask")) + maskName = us::any_cast(parsedArgs["mask"]); string outFileName = inFileName; boost::algorithm::erase_all(outFileName, ".dwi"); - int search = us::any_cast(parsedArgs["search"]); - int compare = us::any_cast(parsedArgs["compare"]); - int channels = 0; - if (parsedArgs.count("channels")) - channels = us::any_cast(parsedArgs["channels"]); + int search = 4; + if (parsedArgs.count("search")) + search = us::any_cast(parsedArgs["search"]); + int compare = 1; + if (parsedArgs.count("compare")) + compare = us::any_cast(parsedArgs["compare"]); + bool joint = false; + if (parsedArgs.count("joint")) + joint = true; + bool rician = false; + if (parsedArgs.count("rician")) + rician = true; try { if( boost::algorithm::ends_with(inFileName, ".dwi")) { DiffusionImageType::Pointer dwi = dynamic_cast(LoadFile(inFileName).GetPointer()); - mitk::Image::Pointer mask = dynamic_cast(LoadFile(maskName).GetPointer()); - ImageType::Pointer itkMask = ImageType::New(); - mitk::CastToItkImage(mask, itkMask); itk::NonLocalMeansDenoisingFilter::Pointer filter = itk::NonLocalMeansDenoisingFilter::New(); filter->SetNumberOfThreads(12); filter->SetInputImage(dwi->GetVectorImage()); - filter->SetInputMask(itkMask); - if (channels == 0) + if (!maskName.empty()) { - MITK_INFO << "Denoising with: s = " << search << "; c = " << compare; - - filter->SetUseJointInformation(false); - filter->SetSearchRadius(search); - filter->SetComparisonRadius(compare); - filter->Update(); + mitk::Image::Pointer mask = dynamic_cast(LoadFile(maskName).GetPointer()); + ImageType::Pointer itkMask = ImageType::New(); + mitk::CastToItkImage(mask, itkMask); + filter->SetInputMask(itkMask); + } - DiffusionImageType::Pointer output = DiffusionImageType::New(); - output->SetVectorImage(filter->GetOutput()); - output->SetReferenceBValue(dwi->GetReferenceBValue()); - output->SetDirections(dwi->GetDirections()); - output->InitializeFromVectorImage(); - std::stringstream name; - name << outFileName << "_NLMr_" << search << "-" << compare << ".dwi"; + filter->SetUseJointInformation(joint); + filter->SetUseRicianAdaption(rician); + filter->SetSearchRadius(search); + filter->SetComparisonRadius(compare); + filter->SetVariance(variance); + filter->Update(); - MITK_INFO << "Writing: " << name.str(); + DiffusionImageType::Pointer output = DiffusionImageType::New(); + output->SetVectorImage(filter->GetOutput()); + output->SetReferenceBValue(dwi->GetReferenceBValue()); + output->SetDirections(dwi->GetDirections()); + output->InitializeFromVectorImage(); - mitk::NrrdDiffusionImageWriter::Pointer writer = mitk::NrrdDiffusionImageWriter::New(); - writer->SetInput(output); - writer->SetFileName(name.str()); - writer->Update(); - } + std::stringstream name; + name << outFileName << "_NLM_" << search << "-" << compare << "-" << variance << ".dwi"; - else - { - MITK_INFO << "Denoising with: s = " << search << "; c = " << compare << "; ch = " << channels; - filter->SetUseJointInformation(true); - filter->SetSearchRadius(search); - filter->SetComparisonRadius(compare); - filter->SetChannelRadius(channels); - filter->Update(); + MITK_INFO << "Writing: " << name.str(); - DiffusionImageType::Pointer output = DiffusionImageType::New(); - output->SetVectorImage(filter->GetOutput()); - output->SetReferenceBValue(dwi->GetReferenceBValue()); - output->SetDirections(dwi->GetDirections()); - output->InitializeFromVectorImage(); + mitk::NrrdDiffusionImageWriter::Pointer writer = mitk::NrrdDiffusionImageWriter::New(); + writer->SetInput(output); + writer->SetFileName(name.str()); + writer->Update(); - std::stringstream name; - name << outFileName << "_NLMv_" << search << "-" << compare << "-" << channels << ".dwi"; - - MITK_INFO << "Writing " << name.str(); - - mitk::NrrdDiffusionImageWriter::Pointer writer = mitk::NrrdDiffusionImageWriter::New(); - writer->SetInput(output); - writer->SetFileName(name.str()); - writer->Update(); - } MITK_INFO << "Finish!"; } else { MITK_INFO << "Only supported for .dwi!"; } } catch (itk::ExceptionObject e) { MITK_INFO << e; return EXIT_FAILURE; } catch (std::exception e) { MITK_INFO << e.what(); return EXIT_FAILURE; } catch (...) { MITK_INFO << "ERROR!?!"; return EXIT_FAILURE; } return EXIT_SUCCESS; } RegisterDiffusionMiniApp(DwiDenoising); diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/GaussianFilter.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/GaussianFilter.png new file mode 100644 index 0000000000..f3dc46087c Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/GaussianFilter.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/NLM.png b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/NLM.png new file mode 100644 index 0000000000..94b564d252 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/NLM.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkDenoisingViewUserManual.dox b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkDenoisingViewUserManual.dox new file mode 100644 index 0000000000..7adb65fd1a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkDenoisingViewUserManual.dox @@ -0,0 +1,62 @@ +/** +\page org_mitk_views_denoisingview DWI Denoising + +This view provides the user interface to denoise a diffusion weighted image (DWI) with several methods. + +Available sections: + - \ref DwiDenoisingUserManualInputData + - \ref DwiDenoisingUserManualOutputData + - \ref DwiDenoisingUserManualNonLocalMeans + - \ref DwiDenoisingUserManualDiscreteGaussian + - \ref DwiDenoisingUserManualReferences + +\section DwiDenoisingUserManualInputData Input Data + +Mandatory Input: + +\li Diffusion weigthed image + +Optional Input: + +\li Binary mask to define a denoising area. + +\section DwiDenoisingUserManualOutputData Output Data + +\li Denoised DWI: if a mask is set for denoising, all voxel excluding the masked area are set to 0. + +\section DwiDenoisingUserManualNonLocalMeans Non-local means filter + +Denoise the input DWI using a non local means algorithm. For more details refer to [1] and [2]. + +Parameters: + +\li Searchradius: defines the size of the neighborhood V (Fig. 1 b)) in which the voxels will be weighted to reconstruct the center voxel. The resulting neighborhood size is defined as 2x searchradius + 1 (e.g. a searchradius of 1 generates a neighborhood cube with size 3x3x3). + +\li Comparisonradius: defines the size of the compared neighborhoods N (Fig. 1 b)) around each voxel. The resulting neighborhood size is defined as 2x comaprisonradius + 1 (e.g. a comparisonradius of 1 generates a neighborhood cube with size 3x3x3). + +\li Noise variance: the variance of the noise need to be set for filtering. An estimation of the noise varinace will be implemented soon. + +\li Rician adaption: if checked the non-local means uses an adaption for Rician distributed noise. + +\li Joint information: if checked the whole DWI is seen as a vector image, weighting each voxels complete vector, instead of weighting each channel seperate. (This might be a bit faster, but is less accurate) + +\image html NLM.png Fig. 1: a) View using the Non-local means filter b) 2D illustration of the Non-local means principle [1] + +\section DwiDenoisingUserManualDiscreteGaussian Discrete gaussian filter + +Denoise the input DWI using a discrete gaussian filter. + +Parameters: + +\li Variance: defines the varinance of the gaussian distribution to denoise the image. + +\image html GaussianFilter.png Fig. 2: View using the discrete gaussian filter + + +\section DwiDenoisingUserManualReferences References + +[1] Wiest-Daesslé et al. Non-Local Means Variants for Denoising of Diffusion-Weigthed and Diffusion Tensor MRI. MICCAI 2007, Part II, LNCS 4792, pp- 344-351. + +[2] Wiest-Daesslé et al. Rician Noise Removal by Non-Local Means Filtering for Low Signal-to-Noise Ratio MRI: Applications to DT-MRI. MICCAI 2008, Part II, LNCS 5242, pp. 171-179. + +*/ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkDiffusionImagingUserManual.dox b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkDiffusionImagingUserManual.dox index 2b6bd56cf8..0b01f058f6 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkDiffusionImagingUserManual.dox +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/documentation/UserManual/QmitkDiffusionImagingUserManual.dox @@ -1,132 +1,133 @@ /** \page org_mitk_gui_qt_diffusionimaging MITK Diffusion Imaging (MITK-DI) \tableofcontents This module provides means to diffusion weighted image reconstruction, visualization and quantification. Diffusion tensors as well as different q-ball reconstruction schemes are supported. Q-ball imaging aims at recovering more detailed information about the orientations of fibers from diffusion MRI measurements and, in particular, to resolve the orientations of crossing fibers. \section QmitkDiffusionImagingUserManualIssues Known Issues \li Dicom Import: The dicom import has so far only been implemented for Siemens dicom images. MITK-DI is capable of reading the nrrd format, which is documented elsewhere [1, 2]. These files can be created by combining the raw image data with a corresponding textual header file. The file extension should be changed from *.nrrd to *.dwi or from *.nhdr to *.hdwi respectively in order to let MITK-DI recognize the diffusion related header information provided in the files. \section QmitkDiffusionImagingUserManualPreprocessing Preprocessing The preprocessing view gives an overview over the important features of a diffusion weighted image like the number of gradient directions, b-value and the measurement frame. Additionally it allows the extraction of the B0 image, reduction of gradient directions and the generation of a binary brain mask. The image volume can be modified by applying a new mesurement frame, which is useful if the measurement frame is not set correctly in the image header, or by averaging redundant gradient directions. \image html prepro1.png Preprocessing \section QmitkDiffusionImagingUserManualTensorReconstruction Tensor Reconstruction The tensor reconstruction view allows ITK based tensor reconstruction [3]. The advanced settings for ITK reconstruction let you configure a manual threshold on the non-diffusion weighted image. All voxels below this threshold will not be reconstructed and left blank. It is also possible to check for negative eigenvalues. The according voxels are also left blank. \image html tensor1.png ITK tensor reconstruction A few seconds (depending on the image size) after the reconstruction button is hit, a colored image should appear in the main window. \image html tensor4.png Tensor image after reconstruction To assess the quality of the tensor fit it has been proposed to calculate the model residual [9]. This calculates the residual between the measured signal and the signal predicted by the model. Large residuals indicate an inadequacy of the model or the presence of artefacts in the signal intensity (noise, head motion, etc.). To use this option: Select a DWI dataset, estimate a tensor, select both the DWI node and the tensor node in the datamanager and press Residual Image Calculation. MITK-Diffusion can show the residual for every voxel averaged over all volumes or (in the plot widget) summarized per volume or for every slice in every volume. Clicking in the widget where the residual is shown per slice will automatically let the cross-hair jump to that position in the DWI dataset. If Percentage of outliers is checked, the per volume plot will show the percentage of outliers per volume. Otherwise it will show the mean together with the first and third quantile of residuals. See [9] for more information. \image html residuals.png The residual widget The view also allows the generation of artificial diffusion weighted or Q-Ball images from the selected tensor image. The ODFs of the Q-Ball image are directly initialized from the tensor values and afterwards normalized. The diffusion weighted image is estimated using the l2-norm image of the tensor image as B0. The gradient images are afterwards generated using the standard tensor equation. \section QmitkDiffusionImagingUserManualQBallReconstruction Q-Ball Reconstruction The q-ball reonstruction view implements a variety of reconstruction methods. The different reconstruction methods are described in the following: \li Numerical: The original, numerical q-ball reconstruction presented by Tuch et al. [5] \li Standard (SH): Descoteaux's reconstruction based on spherical harmonic basis functions [6] \li Solid Angle (SH): Aganj's reconstruction with solid angle consideration [7] \li ADC-profile only: The ADC-profile reconstructed with spherical harmonic basis functions \li Raw signal only: The raw signal reconstructed with spherical harmonic basis functions \image html qballs1.png The q-ball resonstruction view B0 threshold works the same as in tensor reconstruction. The maximum l-level configures the size of the spherical harmonics basis. Larger l-values (e.g. l=8) allow higher levels of detail, lower levels are more stable against noise (e.g. l=4). Lambda is a regularisation parameter. Set it to 0 for no regularisation. lambda = 0.006 has proven to be a stable choice under various settings. \image html qballs2.png Advanced q-ball reconstruction settings This is how a q-ball image should initially look after reconstruction. Standard q-balls feature a relatively low GFA and thus appear rather dark. Adjust the level-window to solve this. \image html qballs3.png q-ball image after reconstruction \section QmitkDiffusionImagingUserManualDicomImport Dicom Import The dicom import does not cover all hardware manufacturers but only Siemens dicom images. MITK-DI is also capable of reading the nrrd format, which is documented elsewhere [1, 2]. These files can be created by combining the raw image data with a corresponding textual header file. The file extension should be changed from *.nrrd to *.dwi or from *.nhdr to *.hdwi respectively in order to let MITK-DI recognize the diffusion related header information provided in the files. In case your dicom images are readable by MITK-DI, select one or more input dicom folders and click import. Each input folder must only contain DICOM-images that can be combined into one vector-valued 3D output volume. Different patients must be loaded from different input-folders. The folders must not contain other acquisitions (e.g. T1,T2,localizer). In case many imports are performed at once, it is recommended to set the the optional output folder argument. This prevents the images from being kept in memory. \image html dicom1.png Dicom import The option "Average duplicate gradients" accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "blur radius" > 0 is configured. \section QmitkDiffusionImagingUserManualFslImport FSL Import FSL diffusion data can be imported with MITK Diffusion. FSL diffusion datasets consist of 3 files: a nifty file (filename.nii.gz or filename.nii), a bvecs file (filename.bvecs), which is a text file containing the gradient vectors, and a bvals file (filename.bvecs), containing the b-values. Due to the system that selects suitable file readers, MITK will not recognize these files as diffusion datasets. In order to make MITK recognize it as diffusion, the extension must be changed from .nii.gz to .fslgz (so the new name is filename.fslgz) or from filename.nii to filename.fsl. The bvecs and bvals files have to be renamed as well(to filename.fsl.bvecs/filenames.fsl.bvecs or to filename.fslgz.bvecs/filename.fslgz.bvals). MITK can also save diffusion weighted images in FSL format. To do this the extension of the new file should be changed to .fsl or .fslgz upon saving the file. \image html fslsave.png Save a dwi dataset as fsl \section QmitkDiffusionImagingUserManualQuantification Quantification The quantification view allows the derivation of different scalar anisotropy measures for the reconstructed tensors (Fractional Anisotropy, Relative Anisotropy, Axial Diffusivity, Radial Diffusivity) or q-balls (Generalized Fractional Anisotropy). \image html quantification.png Anisotropy quantification \section QmitkDiffusionImagingUserManualVisualizationSettings ODF Visualization Setting In this small view, the visualization of ODFs and diffusion images can be configured. Depending on the selected image in the data storage, different options are shown here. For tensor or q-ball images, the visibility of glyphs in the different render windows (T)ransversal, (S)agittal, and (C)oronal can be configured here. The maximal number of glyphs to display can also be configured here for. This is usefull to keep the system response time during rendering feasible. The other options configure normalization and scaling of the glyphs. In diffusion images, a slider lets you choose the desired image channel from the vector of images (each gradient direction one image) for rendering. Furthermore reinit can be performed and texture interpolation toggled. This is how a visualization with activated glyphs should look like: \image html visualization3.png Q-ball image with ODF glyph visibility toggled ON \section QmitkDiffusionImagingUserManualReferences References 1. http://teem.sourceforge.net/nrrd/format.html 2. http://www.cmake.org/Wiki/Getting_Started_with_the_NRRD_Format 3. C.F.Westin, S.E.Maier, H.Mamata, A.Nabavi, F.A.Jolesz, R.Kikinis, "Processing and visualization for Diffusion tensor MRI", Medical image Analysis, 2002, pp 93-108 5. Tuch, D.S., 2004. Q-ball imaging. Magn Reson Med 52, 1358-1372. 6. Descoteaux, M., Angelino, E., Fitzgibbons, S., Deriche, R., 2007. Regularized, fast, and robust analytical Q-ball imaging. Magn Reson Med 58, 497-510. 7. Aganj, I., Lenglet, C., Sapiro, G., 2009. ODF reconstruction in q-ball imaging with solid angle consideration. Proceedings of the Sixth IEEE International Symposium on Biomedical Imaging Boston, MA. 8. Goh, A., Lenglet, C., Thompson, P.M., Vidal, R., 2009. Estimating Orientation Distribution Functions with Probability Density Constraints and Spatial Regularity. Med Image Comput Comput Assist Interv Int Conf Med Image Comput Comput Assist Interv LNCS 5761, 877 ff. 9. J.-D. Tournier, S. Mori, A. Leemans., 2011. Diffusion Tensor Imaging and Beyond. Magn Reson Med 65, 1532-1556. \section QmitkDiffusionImagingUserManualTechnicalDetail Technical Information for Developers The diffusion imaging module uses additional properties beside the ones in use in other modules, for further information see \ref DiffusionImagingPropertiesPage . \section QmitkDiffusionImagingUserManualSubManuals Manuals of componentes The MITK Diffusion tools consist of further components, which have their own documentation, see: \li \subpage org_mitk_views_fiberprocessing \li \subpage org_mitk_views_gibbstracking \li \subpage org_mitk_views_odfdetails \li \subpage org_mitk_views_partialvolumeanalysisview \li \subpage org_mitk_views_screenshotmaker \li \subpage org_mitk_views_stochasticfibertracking \li \subpage org_mitk_views_ivim \li \subpage org_mitk_diffusionimagingapp_perspectives_connectomics \li \subpage org_mitk_views_tractbasedspatialstatistics \li \subpage org_mitk_views_fiberextraction \li \subpage org_mitk_views_fiberprocessing \li \subpage org_mitk_views_odfmaximaextraction \li \subpage org_mitk_views_streamlinetracking \li \subpage org_mitk_views_fiberfoxview \li \subpage org_mitk_views_fieldmapgenerator + \li \subpage org_mitk_views_denoisingview */ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake b/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake index e422eff8ae..12233dbd7d 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake @@ -1,180 +1,181 @@ set(SRC_CPP_FILES QmitkODFDetailsWidget.cpp QmitkODFRenderWidget.cpp QmitkPartialVolumeAnalysisWidget.cpp QmitkIVIMWidget.cpp QmitkTbssRoiAnalysisWidget.cpp QmitkResidualAnalysisWidget.cpp QmitkResidualViewWidget.cpp QmitkTensorModelParametersWidget.cpp QmitkZeppelinModelParametersWidget.cpp QmitkStickModelParametersWidget.cpp QmitkDotModelParametersWidget.cpp QmitkBallModelParametersWidget.cpp QmitkAstrosticksModelParametersWidget.cpp ) set(INTERNAL_CPP_FILES mitkPluginActivator.cpp QmitkQBallReconstructionView.cpp QmitkPreprocessingView.cpp QmitkDiffusionDicomImportView.cpp QmitkDiffusionQuantificationView.cpp QmitkTensorReconstructionView.cpp QmitkDiffusionImagingPublicPerspective.cpp QmitkControlVisualizationPropertiesView.cpp QmitkODFDetailsView.cpp QmitkGibbsTrackingView.cpp QmitkStochasticFiberTrackingView.cpp QmitkStreamlineTrackingView.cpp QmitkFiberProcessingView.cpp # QmitkFiberBundleDeveloperView.cpp QmitkPartialVolumeAnalysisView.cpp QmitkIVIMView.cpp QmitkTractbasedSpatialStatisticsView.cpp QmitkTbssTableModel.cpp QmitkTbssMetaTableModel.cpp QmitkTbssSkeletonizationView.cpp Connectomics/QmitkConnectomicsDataView.cpp Connectomics/QmitkConnectomicsNetworkOperationsView.cpp Connectomics/QmitkConnectomicsStatisticsView.cpp Connectomics/QmitkNetworkHistogramCanvas.cpp QmitkDwiSoftwarePhantomView.cpp QmitkOdfMaximaExtractionView.cpp QmitkFiberfoxView.cpp QmitkFiberExtractionView.cpp QmitkFieldmapGeneratorView.cpp QmitkDiffusionRegistrationView.cpp QmitkDenoisingView.cpp ) set(UI_FILES src/internal/QmitkQBallReconstructionViewControls.ui src/internal/QmitkPreprocessingViewControls.ui src/internal/QmitkDiffusionDicomImportViewControls.ui src/internal/QmitkDiffusionQuantificationViewControls.ui src/internal/QmitkTensorReconstructionViewControls.ui src/internal/QmitkControlVisualizationPropertiesViewControls.ui src/internal/QmitkODFDetailsViewControls.ui src/internal/QmitkGibbsTrackingViewControls.ui src/internal/QmitkStochasticFiberTrackingViewControls.ui src/internal/QmitkStreamlineTrackingViewControls.ui src/internal/QmitkFiberProcessingViewControls.ui # src/internal/QmitkFiberBundleDeveloperViewControls.ui src/internal/QmitkPartialVolumeAnalysisViewControls.ui src/internal/QmitkIVIMViewControls.ui src/internal/QmitkTractbasedSpatialStatisticsViewControls.ui src/internal/QmitkTbssSkeletonizationViewControls.ui src/internal/Connectomics/QmitkConnectomicsDataViewControls.ui src/internal/Connectomics/QmitkConnectomicsNetworkOperationsViewControls.ui src/internal/Connectomics/QmitkConnectomicsStatisticsViewControls.ui src/internal/QmitkDwiSoftwarePhantomViewControls.ui src/internal/QmitkOdfMaximaExtractionViewControls.ui src/internal/QmitkFiberfoxViewControls.ui src/internal/QmitkFiberExtractionViewControls.ui src/QmitkTensorModelParametersWidgetControls.ui src/QmitkZeppelinModelParametersWidgetControls.ui src/QmitkStickModelParametersWidgetControls.ui src/QmitkDotModelParametersWidgetControls.ui src/QmitkBallModelParametersWidgetControls.ui src/QmitkAstrosticksModelParametersWidgetControls.ui src/internal/QmitkFieldmapGeneratorViewControls.ui src/internal/QmitkDiffusionRegistrationViewControls.ui src/internal/QmitkDenoisingViewControls.ui ) set(MOC_H_FILES src/internal/mitkPluginActivator.h src/internal/QmitkQBallReconstructionView.h src/internal/QmitkPreprocessingView.h src/internal/QmitkDiffusionDicomImportView.h src/internal/QmitkDiffusionImagingPublicPerspective.h src/internal/QmitkDiffusionQuantificationView.h src/internal/QmitkTensorReconstructionView.h src/internal/QmitkControlVisualizationPropertiesView.h src/internal/QmitkODFDetailsView.h src/QmitkODFRenderWidget.h src/QmitkODFDetailsWidget.h src/internal/QmitkGibbsTrackingView.h src/internal/QmitkStochasticFiberTrackingView.h src/internal/QmitkStreamlineTrackingView.h src/internal/QmitkFiberProcessingView.h # src/internal/QmitkFiberBundleDeveloperView.h src/internal/QmitkPartialVolumeAnalysisView.h src/QmitkPartialVolumeAnalysisWidget.h src/internal/QmitkIVIMView.h src/internal/QmitkTractbasedSpatialStatisticsView.h src/internal/QmitkTbssSkeletonizationView.h src/QmitkTbssRoiAnalysisWidget.h src/QmitkResidualAnalysisWidget.h src/QmitkResidualViewWidget.h src/internal/Connectomics/QmitkConnectomicsDataView.h src/internal/Connectomics/QmitkConnectomicsNetworkOperationsView.h src/internal/Connectomics/QmitkConnectomicsStatisticsView.h src/internal/Connectomics/QmitkNetworkHistogramCanvas.h src/internal/QmitkDwiSoftwarePhantomView.h src/internal/QmitkOdfMaximaExtractionView.h src/internal/QmitkFiberfoxView.h src/internal/QmitkFiberExtractionView.h src/QmitkTensorModelParametersWidget.h src/QmitkZeppelinModelParametersWidget.h src/QmitkStickModelParametersWidget.h src/QmitkDotModelParametersWidget.h src/QmitkBallModelParametersWidget.h src/QmitkAstrosticksModelParametersWidget.h src/internal/QmitkFieldmapGeneratorView.h src/internal/QmitkDiffusionRegistrationView.h src/internal/QmitkDenoisingView.h ) set(CACHED_RESOURCE_FILES # list of resource files which can be used by the plug-in # system without loading the plug-ins shared library, # for example the icon used in the menu and tabs for the # plug-in views in the workbench plugin.xml resources/preprocessing.png resources/dwiimport.png resources/quantification.png resources/reconodf.png resources/recontensor.png resources/vizControls.png resources/OdfDetails.png resources/GibbsTracking.png resources/FiberBundleOperations.png resources/PartialVolumeAnalysis_24.png resources/IVIM_48.png resources/stochFB.png resources/tbss.png resources/connectomics/QmitkConnectomicsDataViewIcon_48.png resources/connectomics/QmitkConnectomicsNetworkOperationsViewIcon_48.png resources/connectomics/QmitkConnectomicsStatisticsViewIcon_48.png resources/arrow.png resources/qball_peaks.png resources/phantom.png resources/tensor.png resources/qball.png resources/StreamlineTracking.png resources/dwi2.png resources/odf.png resources/refresh.xpm resources/diffusionregistration.png + resources/denoisingicon.png ) set(QRC_FILES # uncomment the following line if you want to use Qt resources resources/QmitkDiffusionImaging.qrc #resources/QmitkTractbasedSpatialStatisticsView.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/plugin.xml b/Plugins/org.mitk.gui.qt.diffusionimaging/plugin.xml index 5d045e7c0d..db8942459c 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/plugin.xml +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/plugin.xml @@ -1,188 +1,188 @@ + icon="resources/denoisingicon.png" /> diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/QmitkDiffusionImaging.qrc b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/QmitkDiffusionImaging.qrc index d4cc60c09a..03fff7a423 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/QmitkDiffusionImaging.qrc +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/QmitkDiffusionImaging.qrc @@ -1,62 +1,63 @@ qball.png tensor.png dwi.png dwiimport.png quantification.png reconodf.png recontensor.png texIntONIcon.png texIntOFFIcon.png vizControls.png Refresh_48.png QBallData24.png glyphsoff_C.png glyphsoff_S.png glyphsoff_T.png glyphson_C.png glyphson_S.png glyphson_T.png FiberBundle.png FiberBundleX.png connectomics/ConnectomicsNetwork.png rectangle.png circle.png polygon.png color24.gif color48.gif color64.gif crosshair.png paint2.png IVIM_48.png reset.png MapperEfx2D.png refresh.xpm odf.png general_icons/download.ico general_icons/play.ico general_icons/plus.ico general_icons/refresh.ico general_icons/right.ico general_icons/save.ico general_icons/undo.ico general_icons/upload.ico general_icons/abort.ico general_icons/copy1.ico general_icons/copy2.ico general_icons/cut.ico general_icons/deny1.ico general_icons/deny2.ico general_icons/down.ico general_icons/left.ico general_icons/magn_minus.ico general_icons/magn_plus.ico general_icons/search.ico general_icons/stop.ico general_icons/up.ico general_icons/help.ico general_icons/pencil.ico general_icons/edit.ico + denoisingicon.png diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/denoisingicon.png b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/denoisingicon.png new file mode 100644 index 0000000000..265983f535 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/denoisingicon.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDenoisingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDenoisingView.cpp index e91810749f..5a81abc463 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDenoisingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDenoisingView.cpp @@ -1,500 +1,515 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkDenoisingView.h" #include #include QmitkDenoisingWorker::QmitkDenoisingWorker(QmitkDenoisingView *view) : m_View(view) { } void QmitkDenoisingWorker::run() { - if (m_View->m_ImageNode.IsNotNull() && m_View->m_BrainMaskNode.IsNotNull()) + if (m_View->m_ImageNode.IsNotNull()) { switch (m_View->m_SelectedFilter) { - case 0: - case 3: + case QmitkDenoisingView::NOFILTERSELECTED: + case QmitkDenoisingView::GAUSS: { break; } - case 1: - case 2: + case QmitkDenoisingView::NLM: { try { - m_View->m_NoExceptionThrown = true; + m_View->m_CompletedCalculation = true; m_View->m_NonLocalMeansFilter->Update(); } catch (itk::ExceptionObject& e) { - m_View->m_NoExceptionThrown = false; + m_View->m_CompletedCalculation = false; MITK_ERROR << e.what(); } break; } } m_View->m_DenoisingThread.quit(); } } const std::string QmitkDenoisingView::VIEW_ID = "org.mitk.views.denoisingview"; QmitkDenoisingView::QmitkDenoisingView() : QmitkFunctionality() , m_Controls( 0 ) , m_ImageNode(NULL) , m_BrainMaskNode(NULL) , m_DenoisingWorker(this) , m_ThreadIsRunning(false) , m_NonLocalMeansFilter(NULL) , m_InputImage(NULL) , m_LastProgressCount(0) , m_MaxProgressCount(0) + , m_SelectedFilter(NOFILTERSELECTED) { m_DenoisingWorker.moveToThread(&m_DenoisingThread); connect(&m_DenoisingThread, SIGNAL(started()), this, SLOT(BeforeThread())); connect(&m_DenoisingThread, SIGNAL(started()), &m_DenoisingWorker, SLOT(run())); connect(&m_DenoisingThread, SIGNAL(finished()), this, SLOT(AfterThread())); connect(&m_DenoisingThread, SIGNAL(terminated()), this, SLOT(AfterThread())); m_DenoisingTimer = new QTimer(this); } QmitkDenoisingView::~QmitkDenoisingView() { delete m_DenoisingTimer; } void QmitkDenoisingView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkDenoisingViewControls; m_Controls->setupUi( parent ); CreateConnections(); ResetParameterPanel(); } } void QmitkDenoisingView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_ApplyButton), SIGNAL(clicked()), this, SLOT(StartDenoising())); connect( (QObject*)(m_Controls->m_SelectFilterComboBox), SIGNAL(activated(int)), this, SLOT(SelectFilter(int))); connect( m_DenoisingTimer, SIGNAL(timeout()), this, SLOT(UpdateProgress())); } } void QmitkDenoisingView::Activated() { QmitkFunctionality::Activated(); m_Controls->m_SelectFilterComboBox->clear(); m_Controls->m_SelectFilterComboBox->insertItem(NOFILTERSELECTED, QString( QApplication::translate("QmitkDenoisingView", "Please select a filter", 0, QApplication::UnicodeUTF8) )); - m_Controls->m_SelectFilterComboBox->insertItem(NLMR, QString( QApplication::translate("QmitkDenoisingView", "Non local means filter", 0, QApplication::UnicodeUTF8) )); - m_Controls->m_SelectFilterComboBox->insertItem(NLMV, QString( QApplication::translate("QmitkDenoisingView", "Non local means filter with joint information", 0, QApplication::UnicodeUTF8) )); + m_Controls->m_SelectFilterComboBox->insertItem(NLM, QString( QApplication::translate("QmitkDenoisingView", "Non-local means filter", 0, QApplication::UnicodeUTF8) )); m_Controls->m_SelectFilterComboBox->insertItem(GAUSS, QString( QApplication::translate("QmitkDenoisingView", "Discrete gaussian filter", 0, QApplication::UnicodeUTF8) )); } void QmitkDenoisingView::OnSelectionChanged( std::vector nodes ) { if (m_ThreadIsRunning) return; if (m_SelectedFilter != NOFILTERSELECTED) { m_Controls->m_InputImageLabel->setText("mandatory"); - m_Controls->m_InputBrainMaskLabel->setText("mandatory"); } else { m_Controls->m_InputImageLabel->setText("mandatory"); } + m_Controls->m_InputBrainMaskLabel->setText("optional"); m_Controls->m_ApplyButton->setEnabled(false); m_ImageNode = NULL; m_BrainMaskNode = NULL; // iterate selection for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; if( node.IsNotNull() && dynamic_cast(node->GetData())) { m_Controls->m_InputImageLabel->setText(node->GetName().c_str()); m_ImageNode = node; } bool isBinary = false; node->GetBoolProperty("binary", isBinary); // look for a brainmask in selection if( node.IsNotNull() && static_cast(node->GetData()) && isBinary) { m_Controls->m_InputBrainMaskLabel->setText(node->GetName().c_str()); m_BrainMaskNode = node; } } // Preparation of GUI to start denoising if a filter is selected - if (m_ImageNode.IsNotNull() && m_BrainMaskNode.IsNotNull()) - { - if(m_SelectedFilter != NOFILTERSELECTED) - { - m_Controls->m_ApplyButton->setEnabled(true); - } - } - else if (m_ImageNode.IsNotNull() && m_SelectedFilter == GAUSS) + if (m_ImageNode.IsNotNull() && m_SelectedFilter != NOFILTERSELECTED) { m_Controls->m_ApplyButton->setEnabled(true); } } void QmitkDenoisingView::StartDenoising() { - if (m_ImageNode.IsNotNull() && m_BrainMaskNode.IsNotNull() && m_SelectedFilter != GAUSS) + if (!m_ThreadIsRunning) { - m_LastProgressCount = 0; - switch (m_SelectedFilter) + if (m_ImageNode.IsNotNull()) { - case NOFILTERSELECTED: - case GAUSS: - { - break; - } - case NLMR: + m_LastProgressCount = 0; + switch (m_SelectedFilter) { - // initialize NLMr - m_InputImage = dynamic_cast (m_ImageNode->GetData()); - m_ImageMask = dynamic_cast(m_BrainMaskNode->GetData()); - itk::Image::Pointer itkMask = itk::Image::New(); - mitk::CastToItkImage(m_ImageMask, itkMask); - - m_NonLocalMeansFilter = NonLocalMeansDenoisingFilterType::New(); - m_NonLocalMeansFilter->SetNumberOfThreads(12); - m_NonLocalMeansFilter->SetInputImage(m_InputImage->GetVectorImage()); - m_NonLocalMeansFilter->SetInputMask(itkMask); - m_NonLocalMeansFilter->SetUseJointInformation(false); - m_NonLocalMeansFilter->SetSearchRadius(m_Controls->m_SpinBoxParameter1->value()); - m_NonLocalMeansFilter->SetComparisonRadius(m_Controls->m_SpinBoxParameter2->value()); - - // initialize the progressbar - m_MaxProgressCount = m_InputImage->GetDimension(0) * m_InputImage->GetDimension(1) * m_InputImage->GetDimension(2); - mitk::ProgressBar::GetInstance()->AddStepsToDo(m_MaxProgressCount); - - // start denoising in detached thread - m_DenoisingThread.start(QThread::HighestPriority); + case NOFILTERSELECTED: + { + break; + } + case NLM: + { + // initialize NLM + m_InputImage = dynamic_cast (m_ImageNode->GetData()); + m_NonLocalMeansFilter = NonLocalMeansDenoisingFilterType::New(); - break; - } - case NLMV: - { - // initialize NLMv - m_InputImage = dynamic_cast (m_ImageNode->GetData()); - m_ImageMask = dynamic_cast(m_BrainMaskNode->GetData()); - itk::Image::Pointer itkMask = itk::Image::New(); - mitk::CastToItkImage(m_ImageMask, itkMask); - - m_NonLocalMeansFilter = NonLocalMeansDenoisingFilterType::New(); - m_NonLocalMeansFilter->SetNumberOfThreads(12); - m_NonLocalMeansFilter->SetInputImage(m_InputImage->GetVectorImage()); - m_NonLocalMeansFilter->SetInputMask(itkMask); - m_NonLocalMeansFilter->SetUseJointInformation(true); - m_NonLocalMeansFilter->SetSearchRadius(m_Controls->m_SpinBoxParameter1->value()); - m_NonLocalMeansFilter->SetComparisonRadius(m_Controls->m_SpinBoxParameter2->value()); - m_NonLocalMeansFilter->SetChannelRadius(m_Controls->m_SpinBoxParameter3->value()); - - // initialize the progressbar - m_MaxProgressCount = m_InputImage->GetDimension(0) * m_InputImage->GetDimension(1) * m_InputImage->GetDimension(2); - mitk::ProgressBar::GetInstance()->AddStepsToDo(m_MaxProgressCount); - - // start denoising in detached thread - m_DenoisingThread.start(QThread::HighestPriority); + if (m_BrainMaskNode.IsNotNull()) + { + // use brainmask if set + m_ImageMask = dynamic_cast(m_BrainMaskNode->GetData()); + itk::Image::Pointer itkMask = itk::Image::New(); + mitk::CastToItkImage(m_ImageMask, itkMask); + m_NonLocalMeansFilter->SetInputMask(itkMask); - break; - } - } - } - else if(m_SelectedFilter == GAUSS && m_ImageNode.IsNotNull()) - { - // initialize GAUSS - m_InputImage = dynamic_cast (m_ImageNode->GetData()); + itk::ImageRegionIterator< itk::Image > mit(itkMask, itkMask->GetLargestPossibleRegion()); + mit.GoToBegin(); + itk::Image::IndexType minIndex; + itk::Image::IndexType maxIndex; + minIndex.Fill(10000); + maxIndex.Fill(0); + while (!mit.IsAtEnd()) + { - ExtractFilterType::Pointer extractor = ExtractFilterType::New(); - extractor->SetInput(m_InputImage->GetVectorImage()); - ComposeFilterType::Pointer composer = ComposeFilterType::New(); + if (mit.Get()) + { + // calculation of the start & end index of the smallest masked region + minIndex[0] = minIndex[0] < mit.GetIndex()[0] ? minIndex[0] : mit.GetIndex()[0]; + minIndex[1] = minIndex[1] < mit.GetIndex()[1] ? minIndex[1] : mit.GetIndex()[1]; + minIndex[2] = minIndex[2] < mit.GetIndex()[2] ? minIndex[2] : mit.GetIndex()[2]; - for (unsigned int i = 0; i < m_InputImage->GetVectorImage()->GetVectorLength(); ++i) - { - extractor->SetIndex(i); - extractor->Update(); + maxIndex[0] = maxIndex[0] > mit.GetIndex()[0] ? maxIndex[0] : mit.GetIndex()[0]; + maxIndex[1] = maxIndex[1] > mit.GetIndex()[1] ? maxIndex[1] : mit.GetIndex()[1]; + maxIndex[2] = maxIndex[2] > mit.GetIndex()[2] ? maxIndex[2] : mit.GetIndex()[2]; + } + ++mit; + } + typename itk::Image::SizeType size; + size[0] = maxIndex[0] - minIndex[0]; + size[1] = maxIndex[1] - minIndex[1]; + size[2] = maxIndex[2] - minIndex[2]; - m_GaussianFilter = GaussianFilterType::New(); - m_GaussianFilter->SetInput(extractor->GetOutput()); - m_GaussianFilter->SetVariance(m_Controls->m_SpinBoxParameter1->value()); - m_GaussianFilter->Update(); + m_MaxProgressCount = size[0] * size[1] * size[2]; + } + else + { + // initialize the progressbar + m_MaxProgressCount = m_InputImage->GetDimension(0) * m_InputImage->GetDimension(1) * m_InputImage->GetDimension(2); + + } + + + mitk::ProgressBar::GetInstance()->AddStepsToDo(m_MaxProgressCount); + + + m_NonLocalMeansFilter->SetNumberOfThreads(12); + m_NonLocalMeansFilter->SetInputImage(m_InputImage->GetVectorImage()); + m_NonLocalMeansFilter->SetUseRicianAdaption(m_Controls->m_RicianCheckbox->isChecked()); + m_NonLocalMeansFilter->SetUseJointInformation(m_Controls->m_JointInformationCheckbox->isChecked()); + m_NonLocalMeansFilter->SetSearchRadius(m_Controls->m_SpinBoxParameter1->value()); + m_NonLocalMeansFilter->SetComparisonRadius(m_Controls->m_SpinBoxParameter2->value()); + m_NonLocalMeansFilter->SetVariance(m_Controls->m_DoubleSpinBoxParameter3->value()); - composer->SetInput(i, m_GaussianFilter->GetOutput()); - } - composer->Update(); - DiffusionImageType::Pointer image = DiffusionImageType::New(); - image->SetVectorImage(composer->GetOutput()); - image->SetReferenceBValue(m_InputImage->GetReferenceBValue()); - image->SetDirections(m_InputImage->GetDirections()); - image->InitializeFromVectorImage(); - mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); - imageNode->SetData( image ); - QString name = m_ImageNode->GetName().c_str(); - imageNode->SetName((name+"_gauss_"+QString::number(m_Controls->m_SpinBoxParameter1->value())).toStdString().c_str()); - GetDefaultDataStorage()->Add(imageNode); + + // start denoising in detached thread + m_DenoisingThread.start(QThread::HighestPriority); + + break; + } + case GAUSS: + { + // initialize GAUSS and run + m_InputImage = dynamic_cast (m_ImageNode->GetData()); + + ExtractFilterType::Pointer extractor = ExtractFilterType::New(); + extractor->SetInput(m_InputImage->GetVectorImage()); + ComposeFilterType::Pointer composer = ComposeFilterType::New(); + + for (unsigned int i = 0; i < m_InputImage->GetVectorImage()->GetVectorLength(); ++i) + { + extractor->SetIndex(i); + extractor->Update(); + + m_GaussianFilter = GaussianFilterType::New(); + m_GaussianFilter->SetVariance(m_Controls->m_SpinBoxParameter1->value()); + + if (m_BrainMaskNode.IsNotNull()) + { + m_ImageMask = dynamic_cast(m_BrainMaskNode->GetData()); + itk::Image::Pointer itkMask = itk::Image::New(); + mitk::CastToItkImage(m_ImageMask, itkMask); + + itk::MaskImageFilter , itk::Image >::Pointer maskImageFilter = itk::MaskImageFilter , itk::Image >::New(); + maskImageFilter->SetInput(extractor->GetOutput()); + maskImageFilter->SetMaskImage(itkMask); + maskImageFilter->Update(); + m_GaussianFilter->SetInput(maskImageFilter->GetOutput()); + } + else + { + m_GaussianFilter->SetInput(extractor->GetOutput()); + } + m_GaussianFilter->Update(); + + composer->SetInput(i, m_GaussianFilter->GetOutput()); + } + composer->Update(); + + + DiffusionImageType::Pointer image = DiffusionImageType::New(); + image->SetVectorImage(composer->GetOutput()); + image->SetReferenceBValue(m_InputImage->GetReferenceBValue()); + image->SetDirections(m_InputImage->GetDirections()); + image->InitializeFromVectorImage(); + mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); + imageNode->SetData( image ); + QString name = m_ImageNode->GetName().c_str(); + + imageNode->SetName((name+"_gauss_"+QString::number(m_Controls->m_SpinBoxParameter1->value())).toStdString().c_str()); + GetDefaultDataStorage()->Add(imageNode); + + break; + } + } + } + } + + else + { + m_NonLocalMeansFilter->AbortGenerateDataOn(); + m_CompletedCalculation = false; } } void QmitkDenoisingView::ResetParameterPanel() { m_Controls->m_DwiLabel->setEnabled(false); m_Controls->m_InputImageLabel->setEnabled(false); - m_Controls->m_BrainMaskLabel->hide(); - m_Controls->m_InputBrainMaskLabel->hide(); + m_Controls->m_BrainMaskLabel->setEnabled(false); + m_Controls->m_InputBrainMaskLabel->setEnabled(false); m_Controls->m_ParameterBox->hide(); m_Controls->m_LabelParameter_1->hide(); m_Controls->m_LabelParameter_2->hide(); m_Controls->m_LabelParameter_3->hide(); m_Controls->m_SpinBoxParameter1->hide(); m_Controls->m_SpinBoxParameter2->hide(); - m_Controls->m_SpinBoxParameter3->hide(); + m_Controls->m_DoubleSpinBoxParameter3->hide(); + m_Controls->m_RicianLabel->hide(); + m_Controls->m_RicianCheckbox->hide(); + m_Controls->m_JointInformationLabel->hide(); + m_Controls->m_JointInformationCheckbox->hide(); + m_Controls->m_ApplyButton->setEnabled(false); } void QmitkDenoisingView::SelectFilter(int filter) { if (m_ThreadIsRunning) return; //Prepare GUI this->ResetParameterPanel(); switch (filter) { case 0: { m_SelectedFilter = NOFILTERSELECTED; break; } - case 1: - { - m_SelectedFilter = NLMR; - m_Controls->m_DwiLabel->setEnabled(true); - m_Controls->m_InputImageLabel->setEnabled(true); - m_Controls->m_BrainMaskLabel->show(); - m_Controls->m_InputBrainMaskLabel->show(); - m_Controls->m_ParameterBox->show(); - m_Controls->m_LabelParameter_1->show(); - m_Controls->m_LabelParameter_1->setText("Search Radius:"); - m_Controls->m_LabelParameter_2->show(); - m_Controls->m_LabelParameter_2->setText("Comparision Radius:"); - m_Controls->m_SpinBoxParameter1->show(); - m_Controls->m_SpinBoxParameter1->setValue(1); - m_Controls->m_SpinBoxParameter2->show(); - m_Controls->m_SpinBoxParameter2->setValue(1); - break; - } - case 2: + case 1: { - m_SelectedFilter = NLMV; + m_SelectedFilter = NLM; m_Controls->m_DwiLabel->setEnabled(true); m_Controls->m_InputImageLabel->setEnabled(true); - m_Controls->m_BrainMaskLabel->show(); - m_Controls->m_InputBrainMaskLabel->show(); + m_Controls->m_BrainMaskLabel->setEnabled(true); + m_Controls->m_InputBrainMaskLabel->setEnabled(true); m_Controls->m_ParameterBox->show(); m_Controls->m_LabelParameter_1->show(); m_Controls->m_LabelParameter_1->setText("Search Radius:"); m_Controls->m_LabelParameter_2->show(); m_Controls->m_LabelParameter_2->setText("Comparision Radius:"); m_Controls->m_LabelParameter_3->show(); - m_Controls->m_LabelParameter_3->setText("Number of neighboring gradients:"); + m_Controls->m_LabelParameter_3->setText("Noise variance:"); m_Controls->m_SpinBoxParameter1->show(); - m_Controls->m_SpinBoxParameter1->setValue(1); + m_Controls->m_SpinBoxParameter1->setValue(4); m_Controls->m_SpinBoxParameter2->show(); m_Controls->m_SpinBoxParameter2->setValue(1); - m_Controls->m_SpinBoxParameter3->show(); - m_Controls->m_SpinBoxParameter3->setValue(1); + m_Controls->m_DoubleSpinBoxParameter3->show(); + m_Controls->m_DoubleSpinBoxParameter3->setValue(1.0); + m_Controls->m_RicianLabel->show(); + m_Controls->m_RicianCheckbox->show(); + m_Controls->m_RicianCheckbox->setChecked(true); + m_Controls->m_JointInformationLabel->show(); + m_Controls->m_JointInformationCheckbox->show(); + m_Controls->m_JointInformationCheckbox->setChecked(false); break; } - case 3: + + case 2: { m_SelectedFilter = GAUSS; m_Controls->m_DwiLabel->setEnabled(true); m_Controls->m_InputImageLabel->setEnabled(true); + m_Controls->m_BrainMaskLabel->setEnabled(true); + m_Controls->m_InputBrainMaskLabel->setEnabled(true); m_Controls->m_ParameterBox->show(); m_Controls->m_LabelParameter_1->show(); m_Controls->m_LabelParameter_1->setText("Variance:"); m_Controls->m_SpinBoxParameter1->show(); m_Controls->m_SpinBoxParameter1->setValue(2); - m_Controls->m_LabelParameter_2->hide(); - m_Controls->m_SpinBoxParameter2->hide(); + + break; } } if (m_ImageNode.IsNull()) { if (m_SelectedFilter != NOFILTERSELECTED) m_Controls->m_InputImageLabel->setText("mandatory"); else m_Controls->m_InputImageLabel->setText("mandatory"); } if (m_ImageNode.IsNotNull()) { m_Controls->m_ApplyButton->setEnabled(false); switch(filter) { case NOFILTERSELECTED: { break; } - case NLMR: - case NLMV: - { - if (m_BrainMaskNode.IsNotNull()) - m_Controls->m_ApplyButton->setEnabled(true); - break; - } + case NLM: case GAUSS: { m_Controls->m_ApplyButton->setEnabled(true); break; } } } } void QmitkDenoisingView::BeforeThread() { m_ThreadIsRunning = true; + // initialize timer to update the progressbar at each timestep m_DenoisingTimer->start(500); - m_Controls->m_LabelParameter_1->setEnabled(false); - m_Controls->m_LabelParameter_2->setEnabled(false); - m_Controls->m_LabelParameter_3->setEnabled(false); - m_Controls->m_SpinBoxParameter1->setEnabled(false); - m_Controls->m_SpinBoxParameter2->setEnabled(false); - m_Controls->m_SpinBoxParameter3->setEnabled(false); - m_Controls->m_SelectFilterComboBox->setEnabled(false); - m_Controls->m_ApplyButton->setEnabled(false); + m_Controls->m_ParameterBox->setEnabled(false); + m_Controls->m_ApplyButton->setText("Abort"); } void QmitkDenoisingView::AfterThread() { m_ThreadIsRunning = false; // stop timer to stop updates of progressbar m_DenoisingTimer->stop(); // make sure progressbar is finished mitk::ProgressBar::GetInstance()->Progress(m_MaxProgressCount); - if (m_NoExceptionThrown) + if (m_CompletedCalculation) { switch (m_SelectedFilter) { case NOFILTERSELECTED: case GAUSS: { break; } - case NLMR: + case NLM: { DiffusionImageType::Pointer image = DiffusionImageType::New(); image->SetVectorImage(m_NonLocalMeansFilter->GetOutput()); image->SetReferenceBValue(m_InputImage->GetReferenceBValue()); image->SetDirections(m_InputImage->GetDirections()); image->InitializeFromVectorImage(); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( image ); QString name = m_ImageNode->GetName().c_str(); - imageNode->SetName((name+"_NLMr_"+QString::number(m_Controls->m_SpinBoxParameter1->value())+"-"+QString::number(m_Controls->m_SpinBoxParameter2->value())).toStdString().c_str()); - GetDefaultDataStorage()->Add(imageNode); - break; - } - - case NLMV: - { - DiffusionImageType::Pointer image = DiffusionImageType::New(); - image->SetVectorImage(m_NonLocalMeansFilter->GetOutput()); - image->SetReferenceBValue(m_InputImage->GetReferenceBValue()); - image->SetDirections(m_InputImage->GetDirections()); - image->InitializeFromVectorImage(); - mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); - imageNode->SetData( image ); - QString name = m_ImageNode->GetName().c_str(); - - imageNode->SetName((name+"_NLMv_"+QString::number(m_Controls->m_SpinBoxParameter1->value())+"-"+QString::number(m_Controls->m_SpinBoxParameter2->value())+"-"+QString::number(m_Controls->m_SpinBoxParameter3->value())).toStdString().c_str()); + //TODO: Rician adaption & joint information in name + if (m_Controls->m_RicianCheckbox->isChecked() && !m_Controls->m_JointInformationCheckbox->isChecked()) + { + imageNode->SetName((name+"_NLMr_"+QString::number(m_Controls->m_SpinBoxParameter1->value())+"-"+QString::number(m_Controls->m_SpinBoxParameter2->value())).toStdString().c_str()); + } + else if(!m_Controls->m_RicianCheckbox->isChecked() && m_Controls->m_JointInformationCheckbox->isChecked()) + { + imageNode->SetName((name+"_NLMv_"+QString::number(m_Controls->m_SpinBoxParameter1->value())+"-"+QString::number(m_Controls->m_SpinBoxParameter2->value())).toStdString().c_str()); + } + else if(m_Controls->m_RicianCheckbox->isChecked() && m_Controls->m_JointInformationCheckbox->isChecked()) + { + imageNode->SetName((name+"_NLMvr_"+QString::number(m_Controls->m_SpinBoxParameter1->value())+"-"+QString::number(m_Controls->m_SpinBoxParameter2->value())).toStdString().c_str()); + } + else + { + imageNode->SetName((name+"_NLM_"+QString::number(m_Controls->m_SpinBoxParameter1->value())+"-"+QString::number(m_Controls->m_SpinBoxParameter2->value())).toStdString().c_str()); + } GetDefaultDataStorage()->Add(imageNode); - - m_Controls->m_LabelParameter_3->setEnabled(true); - m_Controls->m_SpinBoxParameter3->setEnabled(true); break; } } } - m_Controls->m_LabelParameter_1->setEnabled(true); - m_Controls->m_LabelParameter_2->setEnabled(true); - m_Controls->m_SpinBoxParameter1->setEnabled(true); - m_Controls->m_SpinBoxParameter2->setEnabled(true); - m_Controls->m_SelectFilterComboBox->setEnabled(true); - m_Controls->m_ApplyButton->setEnabled(true); + m_Controls->m_ParameterBox->setEnabled(true); + m_Controls->m_ApplyButton->setText("Apply"); } void QmitkDenoisingView::UpdateProgress() { switch (m_SelectedFilter) { case NOFILTERSELECTED: case GAUSS: { break; } - case NLMR: - case NLMV: + case NLM: { unsigned int currentProgressCount = m_NonLocalMeansFilter->GetCurrentVoxelCount(); mitk::ProgressBar::GetInstance()->Progress(currentProgressCount-m_LastProgressCount); m_LastProgressCount = currentProgressCount; break; } } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDenoisingView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDenoisingView.h index d804268379..ba63e1a8b3 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDenoisingView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDenoisingView.h @@ -1,131 +1,133 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _QMITKQmitkDenoisingView_H_INCLUDED #define _QMITKQmitkDenoisingView_H_INCLUDED #include #include #include "ui_QmitkDenoisingViewControls.h" #include #include #include #include +#include #include #include #include #include #include +/** + * \class QmitkDenoisingView + * \brief View displaying details to denoise diffusionweighted images. + * + * \sa QmitkFunctionality + * \ingroup Functionalities + */ class QmitkDenoisingView; class QmitkDenoisingWorker : public QObject { Q_OBJECT public: QmitkDenoisingWorker(QmitkDenoisingView* view); public slots: void run(); private: QmitkDenoisingView* m_View; }; -/*! - \brief View displaying details to denoise diffusionweighted images. - \sa QmitkFunctionality - \ingroup Functionalities -*/ class QmitkDenoisingView : public QmitkFunctionality { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; QmitkDenoisingView(); virtual ~QmitkDenoisingView(); /** Typedefs */ - typedef short DiffusionPixelType; - typedef mitk::DiffusionImage< DiffusionPixelType > DiffusionImageType; - typedef mitk::Image MaskImageType; - typedef itk::NonLocalMeansDenoisingFilter< DiffusionPixelType > NonLocalMeansDenoisingFilterType; - typedef itk::DiscreteGaussianImageFilter < itk::Image< DiffusionPixelType, 3>, itk::Image< DiffusionPixelType, 3> > GaussianFilterType; - typedef itk::VectorImageToImageFilter < DiffusionPixelType > ExtractFilterType; - typedef itk::ComposeImageFilter < itk::Image > ComposeFilterType; + typedef short DiffusionPixelType; + typedef mitk::DiffusionImage< DiffusionPixelType > DiffusionImageType; + typedef mitk::Image MaskImageType; + typedef itk::NonLocalMeansDenoisingFilter< DiffusionPixelType > NonLocalMeansDenoisingFilterType; + typedef itk::DiscreteGaussianImageFilter < itk::Image< DiffusionPixelType, 3>, itk::Image< DiffusionPixelType, 3> > GaussianFilterType; + typedef itk::VectorImageToImageFilter < DiffusionPixelType > ExtractFilterType; + typedef itk::ComposeImageFilter < itk::Image > ComposeFilterType; virtual void CreateQtPartControl(QWidget *parent); /// \brief Creation of the connections of main and control widget virtual void CreateConnections(); /// \brief Creation of the connections of the FilterComboBox virtual void Activated(); -protected slots: +private slots: - void StartDenoising(); ///< prepares filter condition and starts thread for denoising - void SelectFilter(int filter); ///< updates which filter is selected - void BeforeThread(); ///< starts timer & disables all buttons while denoising - void AfterThread(); ///< stops timer & creates a new datanode of the denoised image - void UpdateProgress(); ///< updates the progressbar each timestep + void StartDenoising(); //< prepares filter condition and starts thread for denoising + void SelectFilter(int filter); //< updates which filter is selected + void BeforeThread(); //< starts timer & disables all buttons while denoising + void AfterThread(); //< stops timer & creates a new datanode of the denoised image + void UpdateProgress(); //< updates the progressbar each timestep private: /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged( std::vector nodes ); void ResetParameterPanel(); Ui::QmitkDenoisingViewControls* m_Controls; mitk::DataNode::Pointer m_ImageNode; mitk::DataNode::Pointer m_BrainMaskNode; QmitkDenoisingWorker m_DenoisingWorker; QThread m_DenoisingThread; bool m_ThreadIsRunning; - bool m_NoExceptionThrown; + bool m_CompletedCalculation; NonLocalMeansDenoisingFilterType::Pointer m_NonLocalMeansFilter; GaussianFilterType::Pointer m_GaussianFilter; DiffusionImageType::Pointer m_InputImage; MaskImageType::Pointer m_ImageMask; QTimer* m_DenoisingTimer; unsigned int m_LastProgressCount; unsigned int m_MaxProgressCount; enum FilterType { NOFILTERSELECTED, - NLMR, - NLMV, + NLM, GAUSS }m_SelectedFilter; friend class QmitkDenoisingWorker; }; #endif // _QmitkDenoisingView_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDenoisingViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDenoisingViewControls.ui index a6d8bf918b..d14560f2b3 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDenoisingViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkDenoisingViewControls.ui @@ -1,191 +1,237 @@ QmitkDenoisingViewControls 0 0 351 734 0 0 QmitkTemplate 6 - + + 9 + + + 9 + + + 9 + + 9 Denoisingfilter - + + 9 + + + 9 + + + 9 + + 9 Input Data DWI: <html><head/><body><p><span style=" color:#ff0000;">mandatory</span></p></body></html> true - Brainmask: + Mask: - <html><head/><body><p><span style=" color:#ff0000;">mandatory</span></p></body></html> + <html><head/><body><p><span style=" color:#ff0000;">optional</span></p></body></html> Parameters - - - - 1 + + + + Parameter 3 - - 3 + + + + + + Parameter 2 + + + + + + + + + + + + + + Use joint information: + + + + + + + Use rician adaption: + + + + + + + 1 7 - - - - Parameter 3 - - - Parameter 1 - + + + + 1.000000000000000 + + + 99999.990000000005239 + + + + + + + 1 + + + 3 + + + + + + + + + + + + + Qt::Horizontal 40 20 - - - - Parameter 2 - - - - + Apply - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - + Qt::Vertical - - QSizePolicy::Expanding - 20 - 220 + 40