diff --git a/Modules/DiffusionImaging/Algorithms/itkResidualImageFilter.h b/Modules/DiffusionImaging/Algorithms/itkResidualImageFilter.h new file mode 100644 index 0000000000..d9f3f3c36e --- /dev/null +++ b/Modules/DiffusionImaging/Algorithms/itkResidualImageFilter.h @@ -0,0 +1,161 @@ +/*========================================================================= + +Program: Tensor ToolKit - TTK +Module: $URL: svn://scm.gforge.inria.fr/svn/ttk/trunk/Algorithms/itkTensorImageToDiffusionImageFilter.h $ +Language: C++ +Date: $Date: 2010-06-07 13:39:13 +0200 (Mo, 07 Jun 2010) $ +Version: $Revision: 68 $ + +Copyright (c) INRIA 2010. All rights reserved. +See LICENSE.txt for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef _itk_ResidualImageFilter_h_ +#define _itk_ResidualImageFilter_h_ + +#include "itkImageToImageFilter.h" +#include + + +namespace itk +{ + + template + class ResidualImageFilter + : public ImageToImageFilter, itk::Image > + { + + public: + + typedef TInputScalarType InputScalarType; + typedef itk::VectorImage InputImageType; + typedef typename InputImageType::PixelType InputPixelType; + typedef typename InputImageType::RegionType InputImageRegionType; + + typedef TOutputScalarType OutputScalarType; + typedef itk::Image OutputImageType; + typedef typename OutputImageType::PixelType OutputPixelType; + typedef typename OutputImageType::RegionType OutputImageRegionType; + + typedef ResidualImageFilter Self; + typedef ImageToImageFilter Superclass; + + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + + typedef vnl_vector_fixed< double, 3 > GradientDirectionType; + typedef itk::VectorContainer< unsigned int, + GradientDirectionType > GradientDirectionContainerType; + + + typedef itk::Image BaselineImageType; + + itkTypeMacro (ResidualImageFilter, ImageToImageFilter); + + itkStaticConstMacro (ImageDimension, unsigned int, + OutputImageType::ImageDimension); + + itkNewMacro (Self); + + void SetSecondDiffusionImage(typename InputImageType::Pointer diffImage) + { + m_SecondDiffusionImage = diffImage; + } + + + std::vector GetQ1() + { + return m_Q1; + } + + std::vector GetQ3() + { + return m_Q3; + } + + std::vector GetMeans() + { + return m_Means; + } + + std::vector GetPercentagesOfOutliers() + { + return m_PercentagesOfOutliers; + } + + std::vector< std::vector > GetOutliersPerSlice() + { + return m_OutliersPerSlice; + } + + void SetGradients(GradientDirectionContainerType* grads) + { + m_Gradients = grads; + } + + void SetBaseLineImage(BaselineImageType* baseline) + { + m_BaseLineImage = baseline; + } + + void SetB0Threshold(InputScalarType threshold) + { + m_B0Threshold = threshold; + } + + itkSetMacro(B0Index, int) + + + protected: + ResidualImageFilter() + { + m_B0Threshold = 30.0; // default value. allow user to redefine + }; + ~ResidualImageFilter(){}; + + void PrintSelf (std::ostream& os, Indent indent) const + { + Superclass::PrintSelf (os, indent); + } + + + void GenerateData(); + + private: + + ResidualImageFilter (const Self&); + void operator=(const Self&); + + typename InputImageType::Pointer m_SecondDiffusionImage; + + std::vector m_Means, m_Q1, m_Q3, m_PercentagesOfOutliers; + + + + // 'Outer' vector: slices, 'Inner' volumes + std::vector< std::vector > m_OutliersPerSlice; + + GradientDirectionContainerType* m_Gradients; + + typename BaselineImageType::Pointer m_BaseLineImage; + + InputScalarType m_B0Threshold; + + int m_B0Index; + + }; + + +} // end of namespace + + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkResidualImageFilter.txx" +#endif + + +#endif diff --git a/Modules/DiffusionImaging/Algorithms/itkResidualImageFilter.txx b/Modules/DiffusionImaging/Algorithms/itkResidualImageFilter.txx new file mode 100644 index 0000000000..19c0a5646e --- /dev/null +++ b/Modules/DiffusionImaging/Algorithms/itkResidualImageFilter.txx @@ -0,0 +1,274 @@ +/*========================================================================= + +Program: Tensor ToolKit - TTK +Module: $URL: svn://scm.gforge.inria.fr/svn/ttk/trunk/Algorithms/itkTensorImageToDiffusionImageFilter.txx $ +Language: C++ +Date: $Date: 2010-06-07 13:39:13 +0200 (Mo, 07 Jun 2010) $ +Version: $Revision: 68 $ + +Copyright (c) INRIA 2010. All rights reserved. +See LICENSE.txt for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef _itk_ResidualImageFilter_txx_ +#define _itk_ResidualImageFilter_txx_ +#endif + +#include "itkResidualImageFilter.h" +#include +#include + +namespace itk +{ + + + + + template + void + ResidualImageFilter + ::GenerateData() + { + typename InputImageType::SizeType size = this->GetInput()->GetLargestPossibleRegion().GetSize(); + typename InputImageType::SizeType size2 = m_SecondDiffusionImage->GetLargestPossibleRegion().GetSize(); + + if(size != size2) + { + MITK_ERROR << "Sizes do not match"; + return; + } + + // Initialize output image + typename OutputImageType::Pointer outputImage = + static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); + + outputImage->SetSpacing( this->GetInput()->GetSpacing() ); // Set the image spacing + outputImage->SetOrigin( this->GetInput()->GetOrigin() ); // Set the image origin + outputImage->SetDirection( this->GetInput()->GetDirection() ); // Set the image direction + outputImage->SetRegions( this->GetInput()->GetLargestPossibleRegion() ); + outputImage->Allocate(); + outputImage->FillBuffer(0.0); + + + + + + std::vector< std::vector > residuals; + + // per slice, per volume + std::vector< std::vector > > residualsPerSlice; + + // Detrmine number of B0 images + int numberB0=0; + for(int i=0; iSize(); i++) + { + GradientDirectionType grad = m_Gradients->ElementAt(i); + + if(fabs(grad[0]) < 0.001 && fabs(grad[1]) < 0.001 && fabs(grad[2]) < 0.001) + { + numberB0++; + } + } + + residuals.resize(this->GetInput()->GetVectorLength()-numberB0); + + // Calculate the standard residual image and for each volume put all residuals in a vector + for(int z=0; z > sliceResiduals; // residuals per volume for this slice + sliceResiduals.resize(this->GetInput()->GetVectorLength()-numberB0); + + for(int y=0; y ix; + ix[0] = x; + ix[1] = y; + ix[2] = z; + + + typename InputImageType::PixelType p1 = this->GetInput()->GetPixel(ix); + typename InputImageType::PixelType p2 = m_SecondDiffusionImage->GetPixel(ix); + + + int s1 = p1.GetSize(); + int s2 = p2.GetSize(); + + if(p1.GetSize() != p2.GetSize()) + { + MITK_ERROR << "Vector sizes do not match"; + return; + } + + + if(p1.GetElement(m_B0Index) <= m_B0Threshold) + { + continue; + } + + + + double res = 0; + int shift = 0; // correction for the skipped B0 images + + for(int i = 0; iElementAt(i); + if(!(fabs(grad[0]) < 0.001 && fabs(grad[1]) < 0.001 && fabs(grad[2]) < 0.001)) + { + double val1 = (double)p1.GetElement(i); + double val2 = (double)p2.GetElement(i); + + res += abs(val1-val2); + + residuals[i-shift].push_back(val1-val2); + sliceResiduals[i-shift].push_back(val1-val2); + + } + else + { + shift++; + } + + + + } + + + res = res/p1.GetSize(); + + outputImage->SetPixel(ix, res); + + } + } + + residualsPerSlice.push_back(sliceResiduals); + } + + + + + + // for each dw volume: sort the the measured residuals (for each voxel) to enable determining Q1 and Q3; calculate means + // determine percentage of errors as described in QUALITY ASSESSMENT THROUGH ANALYSIS OF RESIDUALS OF DIFFUSION IMAGE FITTING + // Leemans et al 2008 + + double q1,q3, median; + std::vector< std::vector >::iterator it = residuals.begin(); + while(it != residuals.end()) + { + std::vector res = *it; + + // sort + std::sort(res.begin(), res.end()); + + q1 = res[0.25*res.size()]; + m_Q1.push_back(q1); + q3 = res[0.75*res.size()]; + m_Q3.push_back(q3); + + + + median = res[0.5*res.size()]; + double iqr = q3-q1; + double outlierThreshold = median + 1.5*iqr; + double numberOfOutliers = 0.0; + + std::vector::iterator resIt = res.begin(); + double mean = 0; + while(resIt != res.end()) + { + double f = *resIt; + if(f>outlierThreshold) + { + numberOfOutliers++; + } + mean += f; + ++resIt; + } + + double percOfOutliers = 100 * numberOfOutliers / res.size(); + m_PercentagesOfOutliers.push_back(percOfOutliers); + + mean /= res.size(); + m_Means.push_back(mean); + + ++it; + } + + + + // Calculate for each slice the number of outliers per volume(dw volume) + std::vector< std::vector > >::iterator sliceIt = residualsPerSlice.begin(); + + while(sliceIt != residualsPerSlice.end()) + { + std::vector< std::vector > currentSlice = *sliceIt; + std::vector percentages; + + std::vector< std::vector >::iterator volIt = currentSlice.begin(); + while(volIt != currentSlice.end()) + { + + + std::vector currentVolume = *volIt; + + //sort + std::sort(currentVolume.begin(), currentVolume.end()); + + + + q1 = currentVolume[0.25*currentVolume.size()]; + q3 = currentVolume[0.75*currentVolume.size()]; + median = currentVolume[0.5*currentVolume.size()]; + double iqr = q3-q1; + double outlierThreshold = median + 1.5*iqr; + double numberOfOutliers = 0.0; + + std::vector::iterator resIt = currentVolume.begin(); + double mean; + while(resIt != currentVolume.end()) + { + double f = *resIt; + if(f>outlierThreshold) + { + numberOfOutliers++; + } + mean += f; + ++resIt; + } + + double percOfOutliers = 100 * numberOfOutliers / currentVolume.size(); + percentages.push_back(percOfOutliers); + + ++volIt; + } + + m_OutliersPerSlice.push_back(percentages); + + + + ++sliceIt; + } + + + + + } + + + + + + +} // end of namespace diff --git a/Modules/DiffusionImaging/Algorithms/itkTensorImageToDiffusionImageFilter.h b/Modules/DiffusionImaging/Algorithms/itkTensorImageToDiffusionImageFilter.h index e8890d6b2b..8f1aaabe36 100644 --- a/Modules/DiffusionImaging/Algorithms/itkTensorImageToDiffusionImageFilter.h +++ b/Modules/DiffusionImaging/Algorithms/itkTensorImageToDiffusionImageFilter.h @@ -1,117 +1,128 @@ /*========================================================================= Program: Tensor ToolKit - TTK Module: $URL: svn://scm.gforge.inria.fr/svn/ttk/trunk/Algorithms/itkTensorImageToDiffusionImageFilter.h $ Language: C++ Date: $Date: 2010-06-07 13:39:13 +0200 (Mo, 07 Jun 2010) $ Version: $Revision: 68 $ Copyright (c) INRIA 2010. All rights reserved. See LICENSE.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _itk_TensorImageToDiffusionImageFilter_h_ #define _itk_TensorImageToDiffusionImageFilter_h_ #include "itkImageToImageFilter.h" #include namespace itk { template class TensorImageToDiffusionImageFilter : public ImageToImageFilter,3>, itk::VectorImage > { public: typedef TInputScalarType InputScalarType; typedef itk::DiffusionTensor3D InputPixelType; typedef itk::Image InputImageType; typedef typename InputImageType::RegionType InputImageRegionType; typedef TOutputScalarType OutputScalarType; typedef itk::VectorImage OutputImageType; typedef typename OutputImageType::PixelType OutputPixelType; typedef typename OutputImageType::RegionType OutputImageRegionType; typedef OutputScalarType BaselineScalarType; typedef BaselineScalarType BaselinePixelType; typedef typename itk::Image BaselineImageType; typedef typename BaselineImageType::RegionType BaselineImageRegionType; typedef TensorImageToDiffusionImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; itkTypeMacro (TensorImageToDiffusionImageFilter, ImageToImageFilter); itkStaticConstMacro (ImageDimension, unsigned int, OutputImageType::ImageDimension); itkNewMacro (Self); typedef Vector GradientType; typedef std::vector GradientListType; /** Manually Set/Get a list of gradients */ void SetGradientList(const GradientListType list) { m_GradientList = list; this->Modified(); } GradientListType GetGradientList(void) const {return m_GradientList;} void SetBValue( const double& bval) { m_BValue = bval; } + itkSetMacro(Min, OutputScalarType); + itkSetMacro(Max, OutputScalarType); + + protected: TensorImageToDiffusionImageFilter() { m_BValue = 1.0; m_BaselineImage = 0; + m_Min = 0.0; + m_Max = 10000.0; }; ~TensorImageToDiffusionImageFilter(){}; void PrintSelf (std::ostream& os, Indent indent) const { Superclass::PrintSelf (os, indent); } void BeforeThreadedGenerateData( void ); void ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, int); //void GenerateData(); + + private: TensorImageToDiffusionImageFilter (const Self&); void operator=(const Self&); GradientListType m_GradientList; double m_BValue; typename BaselineImageType::Pointer m_BaselineImage; + OutputScalarType m_Min; + OutputScalarType m_Max; + }; } // end of namespace #ifndef ITK_MANUAL_INSTANTIATION #include "itkTensorImageToDiffusionImageFilter.txx" #endif #endif diff --git a/Modules/DiffusionImaging/Algorithms/itkTensorImageToDiffusionImageFilter.txx b/Modules/DiffusionImaging/Algorithms/itkTensorImageToDiffusionImageFilter.txx index 8df15ec81d..19ef4242ed 100644 --- a/Modules/DiffusionImaging/Algorithms/itkTensorImageToDiffusionImageFilter.txx +++ b/Modules/DiffusionImaging/Algorithms/itkTensorImageToDiffusionImageFilter.txx @@ -1,214 +1,214 @@ /*========================================================================= - -Program: Tensor ToolKit - TTK -Module: $URL: svn://scm.gforge.inria.fr/svn/ttk/trunk/Algorithms/itkTensorImageToDiffusionImageFilter.txx $ -Language: C++ -Date: $Date: 2010-06-07 13:39:13 +0200 (Mo, 07 Jun 2010) $ -Version: $Revision: 68 $ - -Copyright (c) INRIA 2010. All rights reserved. -See LICENSE.txt for details. - -This software is distributed WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the above copyright notices for more information. - -=========================================================================*/ + 2 + 3 Program: Tensor ToolKit - TTK + 4 Module: $URL: svn://scm.gforge.inria.fr/svn/ttk/trunk/Algorithms/itkTensorImageToDiffusionImageFilter.txx $ + 5 Language: C++ + 6 Date: $Date: 2010-06-07 13:39:13 +0200 (Mo, 07 Jun 2010) $ + 7 Version: $Revision: 68 $ + 8 + 9 Copyright (c) INRIA 2010. All rights reserved. + 10 See LICENSE.txt for details. + 11 + 12 This software is distributed WITHOUT ANY WARRANTY; without even + 13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + 14 PURPOSE. See the above copyright notices for more information. + 15 + 16 =========================================================================*/ #ifndef _itk_TensorImageToDiffusionImageFilter_txx_ #define _itk_TensorImageToDiffusionImageFilter_txx_ #endif #include "itkTensorImageToDiffusionImageFilter.h" #include "itkTensorToL2NormImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include #include namespace itk { //template //void // TensorImageToDiffusionImageFilter // ::GenerateData() //{ // // Call a method that can be overriden by a subclass to allocate // // memory for the filter's outputs // this->AllocateOutputs(); // // Call a method that can be overridden by a subclass to perform // // some calculations prior to splitting the main computations into // // separate threads // this->BeforeThreadedGenerateData(); // // Set up the multithreaded processing // ThreadStruct str; // str.Filter = this; // this->GetMultiThreader()->SetNumberOfThreads(this->GetNumberOfThreads()); // this->GetMultiThreader()->SetSingleMethod(this->ThreaderCallback, &str); // // multithread the execution // this->GetMultiThreader()->SingleMethodExecute(); // // Call a method that can be overridden by a subclass to perform // // some calculations after all the threads have completed // this->AfterThreadedGenerateData(); //} template void TensorImageToDiffusionImageFilter ::BeforeThreadedGenerateData() { if( m_GradientList.size()==0 ) { throw itk::ExceptionObject (__FILE__,__LINE__,"Error: gradient list is empty, cannot generate DWI."); } // create a B0 image by taking the norm of the tensor field * scale: typedef itk::TensorToL2NormImageFilter > TensorToL2NormFilterType; typename TensorToL2NormFilterType::Pointer myFilter1 = TensorToL2NormFilterType::New(); myFilter1->SetInput (this->GetInput()); try { myFilter1->Update(); } catch (itk::ExceptionObject &e) { std::cerr << e; return; } typename itk::RescaleIntensityImageFilter< itk::Image, BaselineImageType>::Pointer rescaler= itk::RescaleIntensityImageFilter, BaselineImageType>::New(); - rescaler->SetOutputMinimum ( 0 ); - rescaler->SetOutputMaximum ( 10000 ); + rescaler->SetOutputMinimum ( m_Min ); + rescaler->SetOutputMaximum ( m_Max ); rescaler->SetInput ( myFilter1->GetOutput() ); try { rescaler->Update(); } catch (itk::ExceptionObject &e) { std::cerr << e; return; } m_BaselineImage = rescaler->GetOutput(); typename OutputImageType::Pointer outImage = OutputImageType::New(); outImage->SetSpacing( this->GetInput()->GetSpacing() ); // Set the image spacing outImage->SetOrigin( this->GetInput()->GetOrigin() ); // Set the image origin outImage->SetDirection( this->GetInput()->GetDirection() ); // Set the image direction outImage->SetLargestPossibleRegion( this->GetInput()->GetLargestPossibleRegion()); outImage->SetBufferedRegion( this->GetInput()->GetLargestPossibleRegion() ); outImage->SetRequestedRegion( this->GetInput()->GetLargestPossibleRegion() ); - outImage->SetVectorLength(m_GradientList.size()+1); + outImage->SetVectorLength(m_GradientList.size()); outImage->Allocate(); this->SetNumberOfRequiredOutputs (1); this->SetNthOutput (0, outImage); } template void TensorImageToDiffusionImageFilter ::ThreadedGenerateData ( const OutputImageRegionType &outputRegionForThread, int threadId ) { typedef ImageRegionIterator IteratorOutputType; typedef ImageRegionConstIterator IteratorInputType; typedef ImageRegionConstIterator IteratorBaselineType; unsigned long numPixels = outputRegionForThread.GetNumberOfPixels(); unsigned long step = numPixels/100; unsigned long progress = 0; IteratorOutputType itOut (this->GetOutput(0), outputRegionForThread); IteratorInputType itIn (this->GetInput(), outputRegionForThread); IteratorBaselineType itB0 (m_BaselineImage, outputRegionForThread); if( threadId==0 ) { this->UpdateProgress (0.0); } while(!itIn.IsAtEnd()) { if( this->GetAbortGenerateData() ) { throw itk::ProcessAborted(__FILE__,__LINE__); } InputPixelType T = itIn.Get(); BaselinePixelType b0 = itB0.Get(); OutputPixelType out; - out.SetSize(m_GradientList.size()+1); + out.SetSize(m_GradientList.size()); out.Fill(0); if( b0 > 0) { - for( unsigned int i=0; i=0) out[i] = static_cast( 1.0*b0*exp ( -1.0 * m_BValue * res ) ); } } - out[m_GradientList.size()] = b0; + out[m_GradientList.size()-1] = b0; itOut.Set(out); if( threadId==0 && step>0) { if( (progress%step)==0 ) { this->UpdateProgress ( double(progress)/double(numPixels) ); } } ++progress; ++itB0; ++itIn; ++itOut; } if( threadId==0 ) { this->UpdateProgress (1.0); } } } // end of namespace diff --git a/Modules/DiffusionImaging/files.cmake b/Modules/DiffusionImaging/files.cmake index e22043c204..4b62ccfe5c 100644 --- a/Modules/DiffusionImaging/files.cmake +++ b/Modules/DiffusionImaging/files.cmake @@ -1,167 +1,168 @@ SET(CPP_FILES # DicomImport DicomImport/mitkDicomDiffusionImageReader.cpp DicomImport/mitkGroupDiffusionHeadersFilter.cpp DicomImport/mitkDicomDiffusionImageHeaderReader.cpp DicomImport/mitkGEDicomDiffusionImageHeaderReader.cpp DicomImport/mitkPhilipsDicomDiffusionImageHeaderReader.cpp DicomImport/mitkSiemensDicomDiffusionImageHeaderReader.cpp DicomImport/mitkSiemensMosaicDicomDiffusionImageHeaderReader.cpp # DataStructures IODataStructures/mitkDiffusionImagingObjectFactory.cpp # DataStructures -> DWI IODataStructures/DiffusionWeightedImages/mitkDiffusionImageHeaderInformation.cpp IODataStructures/DiffusionWeightedImages/mitkDiffusionImageSource.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageReader.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageWriter.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageIOFactory.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageWriterFactory.cpp IODataStructures/DiffusionWeightedImages/mitkDiffusionImageSerializer.cpp # DataStructures -> QBall IODataStructures/QBallImages/mitkQBallImageSource.cpp IODataStructures/QBallImages/mitkNrrdQBallImageReader.cpp IODataStructures/QBallImages/mitkNrrdQBallImageWriter.cpp IODataStructures/QBallImages/mitkNrrdQBallImageIOFactory.cpp IODataStructures/QBallImages/mitkNrrdQBallImageWriterFactory.cpp IODataStructures/QBallImages/mitkQBallImage.cpp IODataStructures/QBallImages/mitkQBallImageSerializer.cpp # DataStructures -> Tensor IODataStructures/TensorImages/mitkTensorImageSource.cpp IODataStructures/TensorImages/mitkNrrdTensorImageReader.cpp IODataStructures/TensorImages/mitkNrrdTensorImageWriter.cpp IODataStructures/TensorImages/mitkNrrdTensorImageIOFactory.cpp IODataStructures/TensorImages/mitkNrrdTensorImageWriterFactory.cpp IODataStructures/TensorImages/mitkTensorImage.cpp IODataStructures/TensorImages/mitkTensorImageSerializer.cpp IODataStructures/mitkParticle.cpp IODataStructures/mitkParticleGrid.cpp # DataStructures -> FiberBundleX IODataStructures/FiberBundleX/mitkFiberBundleX.cpp IODataStructures/FiberBundleX/mitkFiberBundleXWriter.cpp IODataStructures/FiberBundleX/mitkFiberBundleXReader.cpp IODataStructures/FiberBundleX/mitkFiberBundleXIOFactory.cpp IODataStructures/FiberBundleX/mitkFiberBundleXWriterFactory.cpp IODataStructures/FiberBundleX/mitkFiberBundleXSerializer.cpp IODataStructures/FiberBundleX/mitkFiberBundleXThreadMonitor.cpp # DataStructures -> PlanarFigureComposite IODataStructures/PlanarFigureComposite/mitkPlanarFigureComposite.cpp # DataStructures -> Tbss IODataStructures/TbssImages/mitkTbssImageSource.cpp IODataStructures/TbssImages/mitkTbssRoiImageSource.cpp IODataStructures/TbssImages/mitkNrrdTbssImageReader.cpp IODataStructures/TbssImages/mitkNrrdTbssImageIOFactory.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageReader.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageIOFactory.cpp IODataStructures/TbssImages/mitkTbssImage.cpp IODataStructures/TbssImages/mitkTbssRoiImage.cpp IODataStructures/TbssImages/mitkNrrdTbssImageWriter.cpp IODataStructures/TbssImages/mitkNrrdTbssImageWriterFactory.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageWriter.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageWriterFactory.cpp IODataStructures/TbssImages/mitkTbssImporter.cpp # Rendering Rendering/vtkMaskedProgrammableGlyphFilter.cpp Rendering/mitkCompositeMapper.cpp Rendering/mitkVectorImageVtkGlyphMapper3D.cpp Rendering/vtkOdfSource.cxx Rendering/vtkThickPlane.cxx Rendering/mitkOdfNormalizationMethodProperty.cpp Rendering/mitkOdfScaleByProperty.cpp Rendering/mitkFiberBundleXMapper2D.cpp Rendering/mitkFiberBundleXMapper3D.cpp Rendering/mitkFiberBundleXThreadMonitorMapper3D.cpp Rendering/mitkTbssImageMapper.cpp Rendering/mitkPlanarCircleMapper3D.cpp Rendering/mitkPlanarPolygonMapper3D.cpp # Interactions Interactions/mitkFiberBundleInteractor.cpp # Algorithms Algorithms/mitkPartialVolumeAnalysisHistogramCalculator.cpp Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.cpp Algorithms/mitkTractAnalyzer.cpp # Tractography Tractography/itkStochasticTractographyFilter.h ) SET(H_FILES # Rendering Rendering/mitkDiffusionImageMapper.h Rendering/mitkTbssImageMapper.h Rendering/mitkOdfVtkMapper2D.h Rendering/mitkFiberBundleXMapper3D.h Rendering/mitkFiberBundleXMapper2D.h Rendering/mitkFiberBundleXThreadMonitorMapper3D.h Rendering/mitkPlanarCircleMapper3D.h Rendering/mitkPlanarPolygonMapper3D.h # Reconstruction Reconstruction/itkDiffusionQballReconstructionImageFilter.h Reconstruction/mitkTeemDiffusionTensor3DReconstructionImageFilter.h Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.h Reconstruction/itkPointShell.h Reconstruction/itkOrientationDistributionFunction.h Reconstruction/itkDiffusionIntravoxelIncoherentMotionReconstructionImageFilter.h Reconstruction/itkRegularizedIVIMLocalVariationImageFilter.h Reconstruction/itkRegularizedIVIMReconstructionFilter.h Reconstruction/itkRegularizedIVIMReconstructionSingleIteration.h # IO Datastructures IODataStructures/DiffusionWeightedImages/mitkDiffusionImage.h IODataStructures/TbssImages/mitkTbssImporter.h # DataStructures -> FiberBundleX IODataStructures/FiberBundleX/mitkFiberBundleX.h IODataStructures/FiberBundleX/mitkFiberBundleXWriter.h IODataStructures/FiberBundleX/mitkFiberBundleXReader.h IODataStructures/FiberBundleX/mitkFiberBundleXIOFactory.h IODataStructures/FiberBundleX/mitkFiberBundleXWriterFactory.h IODataStructures/FiberBundleX/mitkFiberBundleXSerializer.h IODataStructures/FiberBundleX/mitkFiberBundleXThreadMonitor.h # Tractography Tractography/itkGibbsTrackingFilter.h Tractography/itkStochasticTractographyFilter.h # Algorithms Algorithms/itkDiffusionQballGeneralizedFaImageFilter.h Algorithms/itkDiffusionQballPrepareVisualizationImageFilter.h Algorithms/itkTensorDerivedMeasurementsFilter.h Algorithms/itkBrainMaskExtractionImageFilter.h Algorithms/itkB0ImageExtractionImageFilter.h Algorithms/itkTensorImageToDiffusionImageFilter.h Algorithms/itkTensorToL2NormImageFilter.h Algorithms/itkTractDensityImageFilter.h Algorithms/itkTractsToFiberEndingsImageFilter.h Algorithms/itkTractsToRgbaImageFilter.h Algorithms/itkGaussianInterpolateImageFunction.h Algorithms/mitkPartialVolumeAnalysisHistogramCalculator.h Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.h Algorithms/itkDiffusionTensorPrincipleDirectionImageFilter.h Algorithms/itkCartesianToPolarVectorImageFilter.h Algorithms/itkPolarToCartesianVectorImageFilter.h Algorithms/itkDistanceMapFilter.h Algorithms/itkProjectionFilter.h Algorithms/itkSkeletonizationFilter.h Algorithms/itkReduceDirectionGradientsFilter.h + Algorithms/itkResidualImageFilter.h ) SET( TOOL_FILES ) IF(WIN32) ENDIF(WIN32) #MITK_MULTIPLEX_PICTYPE( Algorithms/mitkImageRegistrationMethod-TYPE.cpp ) diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake b/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake index 273bd24879..50676e1a87 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake @@ -1,110 +1,114 @@ SET(SRC_CPP_FILES QmitkODFDetailsWidget.cpp QmitkODFRenderWidget.cpp QmitkPartialVolumeAnalysisWidget.cpp QmitkIVIMWidget.cpp QmitkTbssRoiAnalysisWidget.cpp + QmitkResidualAnalysisWidget.cpp + QmitkResidualViewWidget.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 QmitkFiberProcessingView.cpp QmitkFiberBundleDeveloperView.cpp QmitkPartialVolumeAnalysisView.cpp QmitkIVIMView.cpp QmitkTractbasedSpatialStatisticsView.cpp QmitkTbssTableModel.cpp QmitkTbssMetaTableModel.cpp QmitkTbssSkeletonizationView.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/QmitkFiberProcessingViewControls.ui src/internal/QmitkFiberBundleDeveloperViewControls.ui src/internal/QmitkPartialVolumeAnalysisViewControls.ui src/internal/QmitkIVIMViewControls.ui src/internal/QmitkTractbasedSpatialStatisticsViewControls.ui src/internal/QmitkTbssSkeletonizationViewControls.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/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 ) 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 ) 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/src/QmitkResidualAnalysisWidget.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkResidualAnalysisWidget.cpp new file mode 100644 index 0000000000..40298ee687 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkResidualAnalysisWidget.cpp @@ -0,0 +1,125 @@ +/*========================================================================= +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#include "QmitkResidualAnalysisWidget.h" + + +#include +#include +#include + +#include +#include +#include +#include +#include + + +#include +#include + + +QmitkResidualAnalysisWidget::QmitkResidualAnalysisWidget( QWidget * parent ) + : QmitkPlotWidget(parent) +{ + m_PlotPicker = new QwtPlotPicker(m_Plot->canvas()); + m_PlotPicker->setSelectionFlags(QwtPicker::PointSelection | QwtPicker::ClickSelection | QwtPicker::DragSelection); + m_PlotPicker->setTrackerMode(QwtPicker::ActiveOnly); +} + + + +QmitkResidualAnalysisWidget::~QmitkResidualAnalysisWidget() +{ + delete m_PlotPicker; +} + + +void QmitkResidualAnalysisWidget::DrawMeans() +{ + this->Clear(); + this->SetPlotTitle("mean residual per volume"); + QPen pen( Qt::SolidLine ); + pen.setWidth(1); + + + // Create values for x-axis + std::vector xAxis; + for(int i=0; iInsertCurve( "Mean" ); + this->SetCurveData( curveId, xAxis, m_Means ); + this->SetCurvePen( curveId, pen ); + this->SetCurveStyle( curveId, QwtPlotCurve::Dots); + + pen.setColor(Qt::blue); + curveId = this->InsertCurve( "Q1" ); + this->SetCurveData( curveId, xAxis, m_Q1 ); + this->SetCurvePen( curveId, pen ); + this->SetCurveStyle( curveId, QwtPlotCurve::Dots); + + pen.setColor(Qt::red); + curveId = this->InsertCurve( "Q3" ); + this->SetCurveData( curveId, xAxis, m_Q3 ); + this->SetCurvePen( curveId, pen ); + this->SetCurveStyle( curveId, QwtPlotCurve::Dots); + + + this->m_Plot->setAxisTitle(0, "Residual"); + this->m_Plot->setAxisTitle(3, "DWI Volume"); + + + QwtLegend *legend = new QwtLegend; + this->SetLegend(legend, QwtPlot::RightLegend, 0.5); + + this->Replot(); + +} + +void QmitkResidualAnalysisWidget::DrawPercentagesOfOutliers() +{ + this->Clear(); + + + this->SetPlotTitle("Percentage of outliers"); + QPen pen( Qt::SolidLine ); + pen.setWidth(1); + + + // Create values for x-axis + std::vector xAxis; + for(int i=0; iInsertCurve( "Outliers" ); + this->SetCurveData( curveId, xAxis, m_PercentagesOfOutliers ); + this->SetCurvePen( curveId, pen ); + //this->SetCurveStyle( curveId, QwtPlotCurve::Fitted); + + this->m_Plot->setAxisTitle(0, "Percentage of outliers"); + this->m_Plot->setAxisTitle(3, "DWI Volume"); + + QwtLegend *legend = new QwtLegend; + this->SetLegend(legend, QwtPlot::RightLegend, 0.5); + + this->Replot(); +} + + + + diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkResidualAnalysisWidget.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkResidualAnalysisWidget.h new file mode 100644 index 0000000000..29669ca28f --- /dev/null +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkResidualAnalysisWidget.h @@ -0,0 +1,98 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date: 2009-05-15 18:09:46 +0200 (Fr, 15 Mai 2009) $ +Version: $Revision: 1.12 $ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#ifndef QmitkResidualAnalysisWidget_H_ +#define QmitkResidualAnalysisWidget_H_ + +#include "QmitkPlotWidget.h" + +#include + +//#include "QmitkHistogram.h" +#include "QmitkExtExports.h" +#include "mitkImage.h" +#include "mitkPlanarFigure.h" +#include "itkVectorImage.h" + + +//#include + + +#include +#include + + + +/** + * \brief Widget for displaying boxplots + * framework + */ +class DIFFUSIONIMAGING_EXPORT QmitkResidualAnalysisWidget : public QmitkPlotWidget +{ + +Q_OBJECT + +public: + + + QmitkResidualAnalysisWidget( QWidget * parent); + virtual ~QmitkResidualAnalysisWidget(); + + + + + QwtPlot* GetPlot() + { + return m_Plot; + } + + QwtPlotPicker* m_PlotPicker; + + + void SetMeans(std::vector< double > means) + { + m_Means = means; + } + + void SetQ1(std::vector< double > q1) + { + m_Q1 = q1; + } + + void SetQ3(std::vector< double > q3) + { + m_Q3 = q3; + } + + void SetPercentagesOfOutliers(std::vector< double > perc) + { + m_PercentagesOfOutliers = perc; + } + + + void DrawMeans(); + void DrawPercentagesOfOutliers(); + + +protected: + std::vector< double > m_Means; + std::vector< double > m_Q1; + std::vector< double > m_Q3; + std::vector< double > m_PercentagesOfOutliers; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkResidualViewWidget.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkResidualViewWidget.cpp new file mode 100644 index 0000000000..48a770e22e --- /dev/null +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkResidualViewWidget.cpp @@ -0,0 +1,165 @@ +/*========================================================================= +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#include "QmitkResidualViewWidget.h" +#include +#include + + +QmitkResidualViewWidget::QmitkResidualViewWidget( QWidget * parent ) + : QGraphicsView(parent) +{ + + +} + + +QmitkResidualViewWidget::QmitkResidualViewWidget(QGraphicsScene *scene, QWidget *parent) + : QGraphicsView(scene, parent) +{ + +} + + +void QmitkResidualViewWidget::mousePressEvent(QMouseEvent* event) { + // Panning + m_LastPanPoint = event->pos(); + setCursor(Qt::ClosedHandCursor); + + + + QGraphicsItem *item = this->itemAt(event->pos()); + if(item == m_ResidualPixmapItem) + { + QPointF sceneCoord(mapToScene(event->pos())); + QPointF imageCoord(item->mapFromParent(sceneCoord)); + + int volume = (int)imageCoord.y(); + int slice = (int)imageCoord.x(); + + emit pointSelected(slice, volume); + + } +} + + +void QmitkResidualViewWidget::mouseReleaseEvent(QMouseEvent* event) { + setCursor(Qt::OpenHandCursor); + m_LastPanPoint = QPoint(); +} + +void QmitkResidualViewWidget::mouseMoveEvent(QMouseEvent *event) +{ + if(!m_LastPanPoint.isNull()) { + QPointF delta = mapToScene(m_LastPanPoint) - mapToScene(event->pos()); + m_LastPanPoint = event->pos(); + SetCenter(m_CurrentCenterPoint + delta); + } +} + +void QmitkResidualViewWidget::wheelEvent(QWheelEvent *event) +{ + + // Position of the mouse in scene coordinates + QPointF before(mapToScene(event->pos())); + + QPointF screenCenter = m_CurrentCenterPoint; + + double factor = 1.15; + if(event->delta() > 0) + { + scale(factor, factor); + } + else + { + scale(1.0 / factor, 1.0 / factor); + } + + //Get the position after scaling, in scene coords + QPointF after(mapToScene(event->pos())); + + //Get the offset of how the screen moved + QPointF offset = before - after; + + //Adjust to the new center for correct zooming + QPointF newCenter = screenCenter + offset; + SetCenter(newCenter); + +} + + +/** + * Sets the current centerpoint. Also updates the scene's center point. + * Unlike centerOn, which has no way of getting the floating point center + * back, SetCenter() stores the center point. It also handles the special + * sidebar case. This function will claim the centerPoint to sceneRec ie. + * the centerPoint must be within the sceneRec. + */ + +void QmitkResidualViewWidget::SetCenter(const QPointF& center) { + + QRectF visibleArea = mapToScene(rect()).boundingRect(); + QRectF sceneBounds = sceneRect(); + + double boundX = visibleArea.width() / 2.0 ; + double boundY = visibleArea.height() / 2.0; + double boundWidth = sceneBounds.width() -2.0 * boundX; + double boundHeight = sceneBounds.height() - 2.0 * boundY; + + //The max boundary that the centerPoint can be to + QRectF bounds(boundX, boundY, boundWidth, boundHeight); + + if(bounds.contains(center)) + { + m_CurrentCenterPoint = center; + } + else + { + //We need to clamp or use the center of the screen + if(visibleArea.contains(sceneBounds)) + { + //Use the center of scene ie. we can see the whole scene + m_CurrentCenterPoint = sceneBounds.center(); + } + else{ + + m_CurrentCenterPoint = center; + + //We need to clamp the center. The centerPoint is too large + if(center.x() > bounds.x() + bounds.width()) + { + m_CurrentCenterPoint.setX(bounds.x() + bounds.width()); + } + else if(center.x() < bounds.x()) { + m_CurrentCenterPoint.setX(bounds.x()); + } + + if(center.y() > bounds.y() + bounds.height()) + { + m_CurrentCenterPoint.setY(bounds.y() + bounds.height()); + } + else if(center.y() < bounds.y()) + { + m_CurrentCenterPoint.setY(bounds.y()); + } + + } + } + + // Update the scrollbars + centerOn(m_CurrentCenterPoint); +} + + + + + + diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkResidualViewWidget.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkResidualViewWidget.h new file mode 100644 index 0000000000..d5034a7293 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkResidualViewWidget.h @@ -0,0 +1,84 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date: 2009-05-15 18:09:46 +0200 (Fr, 15 Mai 2009) $ +Version: $Revision: 1.12 $ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#ifndef QmitkResidualViewWidget_H_ +#define QmitkResidualViewWidget_H_ + +//#include +#include +#include +#include +#include + +#include + + +#include "QmitkExtExports.h" + + +/** + * \brief Widget for displaying the residual between an original dwi image and the dwi estimated from a tensor image + * + */ +class DIFFUSIONIMAGING_EXPORT QmitkResidualViewWidget : public QGraphicsView +{ + +Q_OBJECT + +public: + + + QmitkResidualViewWidget(QWidget *parent = 0); + + QmitkResidualViewWidget(QGraphicsScene *scene, QWidget *parent = 0); + + + // ~QmitkResidualViewWidget(); + + + void SetResidualPixmapItem(QGraphicsPixmapItem* item) + { + m_ResidualPixmapItem = item; + } + +signals: + + void pointSelected(int slice, int volume); + +protected: + + void wheelEvent(QWheelEvent *event); + + void mouseMoveEvent(QMouseEvent* event); + + void mousePressEvent(QMouseEvent* event); + + void mouseReleaseEvent(QMouseEvent* event); + + QPointF m_CurrentCenterPoint; + + QGraphicsPixmapItem* m_ResidualPixmapItem; + + QPoint m_LastPanPoint; + + void SetCenter(const QPointF& centerPoint); + + + +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.cpp index 026df14322..00b21ae1a9 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.cpp @@ -1,847 +1,1351 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: 2009-05-28 17:19:30 +0200 (Do, 28 Mai 2009) $ Version: $Revision: 17495 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "QmitkTensorReconstructionView.h" #include "mitkDiffusionImagingConfigure.h" // qt includes #include +#include +#include +#include +#include + // itk includes #include "itkTimeProbe.h" //#include "itkTensor.h" // mitk includes #include "mitkProgressBar.h" #include "mitkStatusBar.h" #include "mitkNodePredicateDataType.h" #include "QmitkDataStorageComboBox.h" #include "QmitkStdMultiWidget.h" #include "mitkTeemDiffusionTensor3DReconstructionImageFilter.h" #include "itkDiffusionTensor3DReconstructionImageFilter.h" #include "itkTensorImageToDiffusionImageFilter.h" #include "itkPointShell.h" #include "itkVector.h" +#include "itkB0ImageExtractionImageFilter.h" #include "mitkProperties.h" #include "mitkDataNodeObject.h" #include "mitkOdfNormalizationMethodProperty.h" #include "mitkOdfScaleByProperty.h" #include "mitkDiffusionImageMapper.h" +#include "mitkLookupTableProperty.h" +#include "mitkLookupTable.h" +#include "mitkImageStatisticsHolder.h" #include "berryIStructuredSelection.h" #include "berryIWorkbenchWindow.h" #include "berryISelectionService.h" #include +#include + const std::string QmitkTensorReconstructionView::VIEW_ID = "org.mitk.views.tensorreconstruction"; #define DI_INFO MITK_INFO("DiffusionImaging") -typedef float TTensorPixelType; +typedef float TTensorPixelType; +typedef itk::DiffusionTensor3D< TTensorPixelType > TensorPixelType; +typedef itk::Image< TensorPixelType, 3 > TensorImageType; + using namespace berry; struct TrSelListener : ISelectionListener { berryObjectMacro(TrSelListener); TrSelListener(QmitkTensorReconstructionView* view) { m_View = view; } void DoSelectionChanged(ISelection::ConstPointer selection) { // if(!m_View->IsVisible()) // return; // save current selection in member variable m_View->m_CurrentSelection = selection.Cast(); // do something with the selected items if(m_View->m_CurrentSelection) { bool foundDwiVolume = false; bool foundTensorVolume = false; // iterate selection for (IStructuredSelection::iterator i = m_View->m_CurrentSelection->Begin(); i != m_View->m_CurrentSelection->End(); ++i) { // extract datatree node if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) { mitk::DataNode::Pointer node = nodeObj->GetDataNode(); // only look at interesting types if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) { foundDwiVolume = true; } // only look at interesting types if(QString("TensorImage").compare(node->GetData()->GetNameOfClass())==0) { foundTensorVolume = true; } } } m_View->m_Controls->m_ItkReconstruction->setEnabled(foundDwiVolume); m_View->m_Controls->m_TeemReconstruction->setEnabled(foundDwiVolume); m_View->m_Controls->m_TensorsToDWIButton->setEnabled(foundTensorVolume); m_View->m_Controls->m_TensorsToQbiButton->setEnabled(foundTensorVolume); + m_View->m_Controls->m_ResidualButton->setEnabled(foundDwiVolume && foundTensorVolume); + m_View->m_Controls->m_PercentagesOfOutliers->setEnabled(foundDwiVolume && foundTensorVolume); + m_View->m_Controls->m_PerSliceView->setEnabled(foundDwiVolume && foundTensorVolume); + + + } } void SelectionChanged(IWorkbenchPart::Pointer part, ISelection::ConstPointer selection) { // check, if selection comes from datamanager if (part) { QString partname(part->GetPartName().c_str()); if(partname.compare("Datamanager")==0) { // apply selection DoSelectionChanged(selection); } } } QmitkTensorReconstructionView* m_View; }; QmitkTensorReconstructionView::QmitkTensorReconstructionView() : QmitkFunctionality(), m_Controls(NULL), m_MultiWidget(NULL) { + + if(m_CurrentSelection) + { + mitk::DataStorage::SetOfObjects::Pointer set = + mitk::DataStorage::SetOfObjects::New(); + + mitk::DiffusionImage::Pointer diffImage + = mitk::DiffusionImage::New(); + + + TensorImageType::Pointer tensorImage; + + std::string nodename; + + + for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); + i != m_CurrentSelection->End(); + ++i) + { + + if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) + { + mitk::DataNode::Pointer node = nodeObj->GetDataNode(); + if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) + { + diffImage = static_cast*>((node)->GetData()); + } + else if((QString("TensorImage").compare(node->GetData()->GetNameOfClass())==0)) + { + mitk::TensorImage* mitkVol; + mitkVol = static_cast((node)->GetData()); + mitk::CastToItkImage(mitkVol, tensorImage); + node->GetStringProperty("name", nodename); + } + } + } + + } + + } QmitkTensorReconstructionView::QmitkTensorReconstructionView(const QmitkTensorReconstructionView& other) { Q_UNUSED(other) throw std::runtime_error("Copy constructor not implemented"); } QmitkTensorReconstructionView::~QmitkTensorReconstructionView() { this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->RemovePostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); } void QmitkTensorReconstructionView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkTensorReconstructionViewControls; m_Controls->setupUi(parent); this->CreateConnections(); QStringList items; items << "LLS (Linear Least Squares)" << "MLE (Maximum Likelihood)" << "NLS (Nonlinear Least Squares)" << "WLS (Weighted Least Squares)"; m_Controls->m_TensorEstimationTeemEstimationMethodCombo->addItems(items); m_Controls->m_TensorEstimationTeemEstimationMethodCombo->setCurrentIndex(0); m_Controls->m_TensorEstimationManualThreashold->setChecked(false); m_Controls->m_TensorEstimationTeemSigmaEdit->setText("NaN"); m_Controls->m_TensorEstimationTeemNumItsSpin->setValue(1); m_Controls->m_TensorEstimationTeemFuzzyEdit->setText("0.0"); m_Controls->m_TensorEstimationTeemMinValEdit->setText("1.0"); m_Controls->m_TensorEstimationTeemNumItsLabel_2->setEnabled(true); m_Controls->m_TensorEstimationTeemNumItsSpin->setEnabled(true); m_Controls->m_TensorsToDWIBValueEdit->setText("1000"); Advanced1CheckboxClicked(); Advanced2CheckboxClicked(); TeemCheckboxClicked(); #ifndef DIFFUSION_IMAGING_EXTENDED m_Controls->m_TeemToggle->setVisible(false); #endif // define data type for combobox //m_Controls->m_ImageSelector->SetDataStorage( this->GetDefaultDataStorage() ); //m_Controls->m_ImageSelector->SetPredicate( mitk::NodePredicateDataType::New("DiffusionImage") ); } m_SelListener = berry::ISelectionListener::Pointer(new TrSelListener(this)); this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddPostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); berry::ISelection::ConstPointer sel( this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); m_CurrentSelection = sel.Cast(); m_SelListener.Cast()->DoSelectionChanged(sel); + + + + + + } void QmitkTensorReconstructionView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { berry::ISelection::ConstPointer sel( this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); m_CurrentSelection = sel.Cast(); m_SelListener.Cast()->DoSelectionChanged(sel); m_MultiWidget = &stdMultiWidget; } void QmitkTensorReconstructionView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkTensorReconstructionView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_TeemToggle), SIGNAL(clicked()), this, SLOT(TeemCheckboxClicked()) ); connect( (QObject*)(m_Controls->m_ItkReconstruction), SIGNAL(clicked()), this, SLOT(ItkReconstruction()) ); connect( (QObject*)(m_Controls->m_TeemReconstruction), SIGNAL(clicked()), this, SLOT(TeemReconstruction()) ); connect( (QObject*)(m_Controls->m_TensorEstimationTeemEstimationMethodCombo), SIGNAL(currentIndexChanged(int)), this, SLOT(MethodChoosen(int)) ); connect( (QObject*)(m_Controls->m_Advanced1), SIGNAL(clicked()), this, SLOT(Advanced1CheckboxClicked()) ); connect( (QObject*)(m_Controls->m_Advanced2), SIGNAL(clicked()), this, SLOT(Advanced2CheckboxClicked()) ); connect( (QObject*)(m_Controls->m_TensorEstimationManualThreashold), SIGNAL(clicked()), this, SLOT(ManualThresholdClicked()) ); connect( (QObject*)(m_Controls->m_TensorsToDWIButton), SIGNAL(clicked()), this, SLOT(TensorsToDWI()) ); connect( (QObject*)(m_Controls->m_TensorsToQbiButton), SIGNAL(clicked()), this, SLOT(TensorsToQbi()) ); + connect( (QObject*)(m_Controls->m_ResidualButton), SIGNAL(clicked()), this, SLOT(ResidualCalculation()) ); + connect( (QObject*)(m_Controls->m_PerSliceView), SIGNAL(pointSelected(int, int)), this, SLOT(ResidualClicked(int, int)) ); } } +void QmitkTensorReconstructionView::ResidualClicked(int slice, int volume) +{ + // Use image coord to reset crosshair + + // Find currently selected diffusion image + + // Update Label + + + // to do: This position should be modified in order to skip B0 volumes that are not taken into account + // when calculating residuals + + // Find the diffusion image + mitk::DiffusionImage* diffImage; + + mitk::DataNodeObject::Pointer nodeObj; + mitk::DataNode::Pointer correctNode; + mitk::Geometry3D* geometry; + + for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); + i != m_CurrentSelection->End(); + ++i) + { + if (nodeObj = i->Cast()) + { + mitk::DataNode::Pointer node = nodeObj->GetDataNode(); + if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) + { + diffImage = static_cast*>((node)->GetData()); + + geometry = diffImage->GetGeometry(); + + // Remember the node whose display index must be updated + correctNode = mitk::DataNode::New(); + correctNode = node; + } + } + } + + if(diffImage != NULL) + { + typedef vnl_vector_fixed< double, 3 > GradientDirectionType; + typedef itk::VectorContainer< unsigned int, + GradientDirectionType > GradientDirectionContainerType; + + GradientDirectionContainerType::Pointer dirs = diffImage->GetDirections(); + + + + for(int i=0; iSize() && i<=volume; i++) + { + GradientDirectionType grad = dirs->ElementAt(i); + + // check if image is b0 weighted + if(fabs(grad[0]) < 0.001 && fabs(grad[1]) < 0.001 && fabs(grad[2]) < 0.001) + { + volume++; + } + } + + + QString pos = "Volume: "; + pos.append(QString::number(volume)); + pos.append(", Slice: "); + pos.append(QString::number(slice)); + m_Controls->m_PositionLabel->setText(pos); + + + + + if(correctNode) + { + + int oldDisplayVal; + correctNode->GetIntProperty("DisplayChannel", oldDisplayVal); + std::string oldVal = QString::number(oldDisplayVal).toStdString(); + std::string newVal = QString::number(volume).toStdString(); + + correctNode->SetIntProperty("DisplayChannel",volume); + + correctNode->SetSelected(true); + + this->FirePropertyChanged("DisplayChannel", oldVal, newVal); + + + correctNode->UpdateOutputInformation(); + + + mitk::Point3D p3 = m_MultiWidget->GetCrossPosition(); + itk::Index<3> ix; + geometry->WorldToIndex(p3, ix); + // ix[2] = slice; + + mitk::Vector3D vec; + vec[0] = ix[0]; + vec[1] = ix[1]; + vec[2] = slice; + + + mitk::Vector3D v3New; + geometry->IndexToWorld(vec, v3New); + + + mitk::Point3D origin = geometry->GetOrigin(); + + mitk::Point3D p3New; + p3New[0] = v3New[0] + origin[0]; + p3New[1] = v3New[1] + origin[1]; + p3New[2] = v3New[2] + origin[2]; + + + + m_MultiWidget->MoveCrossToPosition(p3New); + + + m_MultiWidget->RequestUpdate(); + + + } + + + + } + +} + void QmitkTensorReconstructionView::TeemCheckboxClicked() { m_Controls->groupBox_3->setVisible(m_Controls-> m_TeemToggle->isChecked()); } void QmitkTensorReconstructionView::Advanced1CheckboxClicked() { bool check = m_Controls-> m_Advanced1->isChecked(); m_Controls->frame->setVisible(check); } void QmitkTensorReconstructionView::Advanced2CheckboxClicked() { bool check = m_Controls-> m_Advanced2->isChecked(); m_Controls->frame_2->setVisible(check); } void QmitkTensorReconstructionView::ManualThresholdClicked() { m_Controls->m_TensorReconstructionThreasholdEdit_2->setEnabled( m_Controls->m_TensorEstimationManualThreashold->isChecked()); } void QmitkTensorReconstructionView::Activated() { QmitkFunctionality::Activated(); } void QmitkTensorReconstructionView::Deactivated() { QmitkFunctionality::Deactivated(); } void QmitkTensorReconstructionView::MethodChoosen(int method) { m_Controls->m_TensorEstimationTeemNumItsLabel_2->setEnabled(method==3); m_Controls->m_TensorEstimationTeemNumItsSpin->setEnabled(method==3); } +void QmitkTensorReconstructionView::ResidualCalculation() +{ + // Extract dwi and dti from current selection + // In case of multiple selections, take the first one, since taking all combinations is not meaningful + + + + if(m_CurrentSelection) + { + mitk::DataStorage::SetOfObjects::Pointer set = + mitk::DataStorage::SetOfObjects::New(); + + mitk::DiffusionImage::Pointer diffImage + = mitk::DiffusionImage::New(); + + TensorImageType::Pointer tensorImage; + + std::string nodename; + + + for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); + i != m_CurrentSelection->End(); + ++i) + { + + if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) + { + mitk::DataNode::Pointer node = nodeObj->GetDataNode(); + if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) + { + diffImage = static_cast*>((node)->GetData()); + } + else if((QString("TensorImage").compare(node->GetData()->GetNameOfClass())==0)) + { + mitk::TensorImage* mitkVol; + mitkVol = static_cast((node)->GetData()); + mitk::CastToItkImage(mitkVol, tensorImage); + node->GetStringProperty("name", nodename); + } + } + } + + + + typedef itk::TensorImageToDiffusionImageFilter< + TTensorPixelType, DiffusionPixelType > FilterType; + + FilterType::GradientListType gradientList; + mitk::DiffusionImage::GradientDirectionContainerType* gradients + = diffImage->GetDirections(); + + // Copy gradients vectors from gradients to gradientList + for(int i=0; iSize(); i++) + { + mitk::DiffusionImage::GradientDirectionType vec = gradients->at(i); + itk::Vector grad; + + grad[0] = vec[0]; + grad[1] = vec[1]; + grad[2] = vec[2]; + + gradientList.push_back(grad); + } + + // Find the min and the max values from a baseline image + mitk::ImageStatisticsHolder *stats = diffImage->GetStatistics(); + + //Initialize filter that calculates the modeled diffusion weighted signals + FilterType::Pointer filter = FilterType::New(); + filter->SetInput( tensorImage ); + filter->SetBValue(diffImage->GetB_Value()); + filter->SetGradientList(gradientList); + filter->SetMin(stats->GetScalarValueMin()); + filter->SetMax(500); + filter->Update(); + + + // TENSORS TO DATATREE + mitk::DiffusionImage::Pointer image = mitk::DiffusionImage::New(); + image->SetVectorImage( filter->GetOutput() ); + image->SetB_Value(diffImage->GetB_Value()); + image->SetDirections(gradientList); + image->SetOriginalDirections(gradientList); + image->InitializeFromVectorImage(); + mitk::DataNode::Pointer node = mitk::DataNode::New(); + node->SetData( image ); + mitk::DiffusionImageMapper::SetDefaultProperties(node); + + QString newname; + newname = newname.append(nodename.c_str()); + newname = newname.append("_dwi"); + node->SetName(newname.toAscii()); + + + GetDefaultDataStorage()->Add(node); + + + std::vector b0Indices = image->GetB0Indices(); + + + + typedef itk::ResidualImageFilter ResidualImageFilterType; + + ResidualImageFilterType::Pointer residualFilter = ResidualImageFilterType::New(); + residualFilter->SetInput(diffImage->GetVectorImage()); + residualFilter->SetSecondDiffusionImage(image->GetVectorImage()); + residualFilter->SetGradients(gradients); + residualFilter->SetB0Index(b0Indices[0]); + residualFilter->SetB0Threshold(30); + residualFilter->Update(); + + itk::Image::Pointer residualImage = itk::Image::New(); + residualImage = residualFilter->GetOutput(); + + mitk::Image::Pointer mitkResImg = mitk::Image::New(); + + mitk::CastToMitkImage(residualImage, mitkResImg); + + stats = mitkResImg->GetStatistics(); + float min = stats->GetScalarValueMin(); + float max = stats->GetScalarValueMax(); + + mitk::LookupTableProperty::Pointer lutProp = mitk::LookupTableProperty::New(); + mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); + + + vtkSmartPointer lookupTable = + vtkSmartPointer::New(); + + lookupTable->SetTableRange(min, max); + + + // If you don't want to use the whole color range, you can use + // SetValueRange, SetHueRange, and SetSaturationRange + lookupTable->Build(); + + int size = lookupTable->GetTable()->GetSize(); + + vtkSmartPointer reversedlookupTable = + vtkSmartPointer::New(); + reversedlookupTable->SetTableRange(min+1, max); + reversedlookupTable->Build(); + + for(int i=0; i<256; i++) + { + double* rgba = reversedlookupTable->GetTableValue(255-i); + + lookupTable->SetTableValue(i, rgba[0], rgba[1], rgba[2], rgba[3]); + } + + lut->SetVtkLookupTable(lookupTable); + lutProp->SetLookupTable(lut); + + // Create lookuptable + + mitk::DataNode::Pointer resNode=mitk::DataNode::New(); + resNode->SetData( mitkResImg ); + resNode->SetName("Residual Image"); + + resNode->SetProperty("LookupTable", lutProp); + + bool b; + resNode->GetBoolProperty("use color", b); + resNode->SetBoolProperty("use color", false); + + GetDefaultDataStorage()->Add(resNode); + + m_MultiWidget->RequestUpdate(); + + + + // Draw Graph + std::vector means = residualFilter->GetMeans(); + std::vector q1s = residualFilter->GetQ1(); + std::vector q3s = residualFilter->GetQ3(); + std::vector percentagesOfOUtliers = residualFilter->GetPercentagesOfOutliers(); + + m_Controls->m_ResidualAnalysis->SetMeans(means); + m_Controls->m_ResidualAnalysis->SetQ1(q1s); + m_Controls->m_ResidualAnalysis->SetQ3(q3s); + m_Controls->m_ResidualAnalysis->SetPercentagesOfOutliers(percentagesOfOUtliers); + + if(m_Controls->m_PercentagesOfOutliers->isChecked()) + { + m_Controls->m_ResidualAnalysis->DrawPercentagesOfOutliers(); + } + else + { + m_Controls->m_ResidualAnalysis->DrawMeans(); + } + + + + // Draw Graph for volumes per slice in the QGraphicsView + std::vector< std::vector > outliersPerSlice = residualFilter->GetOutliersPerSlice(); + int xSize = outliersPerSlice.size(); + if(xSize == 0) + { + return; + } + int ySize = outliersPerSlice[0].size(); + + + // Find maximum in outliersPerSlice + double maxOutlier= 0.0; + for(int i=0; imaxOutlier) + { + maxOutlier = outliersPerSlice[i][j]; + } + } + } + + + // Create some QImage + QImage qImage(xSize, ySize, QImage::Format_RGB32); + QImage legend(1, 256, QImage::Format_RGB32); + QRgb value; + + vtkSmartPointer lookup = + vtkSmartPointer::New(); + + lookup->SetTableRange(0.0, maxOutlier); + lookup->Build(); + + reversedlookupTable->SetTableRange(0, maxOutlier); + reversedlookupTable->Build(); + + for(int i=0; i<256; i++) + { + double* rgba = reversedlookupTable->GetTableValue(255-i); + lookup->SetTableValue(i, rgba[0], rgba[1], rgba[2], rgba[3]); + } + + + // Fill qImage + for(int i=0; iMapValue(out); + int r, g, b; + r = _rgba[0]; + g = _rgba[1]; + b = _rgba[2]; + + value = qRgb(r, g, b); + + qImage.setPixel(i,j,value); + + } + } + + for(int i=0; i<256; i++) + { + double* rgba = lookup->GetTableValue(i); + int r, g, b; + r = rgba[0]*255; + g = rgba[1]*255; + b = rgba[2]*255; + value = qRgb(r, g, b); + legend.setPixel(0,255-i,value); + } + + QString upper = QString::number(maxOutlier, 'g', 3); + upper.append(" %"); + QString lower = QString::number(0.0); + lower.append(" %"); + m_Controls->m_UpperLabel->setText(upper); + m_Controls->m_LowerLabel->setText(lower); + + QGraphicsScene* scene = new QGraphicsScene; + QGraphicsScene* scene2 = new QGraphicsScene; + + + QPixmap pixmap(QPixmap::fromImage(qImage)); + QGraphicsPixmapItem *item = new QGraphicsPixmapItem( pixmap, 0, scene); + item->scale(10.0, 3.0); + + QPixmap pixmap2(QPixmap::fromImage(legend)); + QGraphicsPixmapItem *item2 = new QGraphicsPixmapItem( pixmap2, 0, scene2); + item2->scale(20.0, 1.0); + + m_Controls->m_PerSliceView->SetResidualPixmapItem(item); + + + + m_Controls->m_PerSliceView->setScene(scene); + m_Controls->m_LegendView->setScene(scene2); + m_Controls->m_PerSliceView->show(); + m_Controls->m_PerSliceView->repaint(); + + m_Controls->m_LegendView->setHorizontalScrollBarPolicy ( Qt::ScrollBarAlwaysOff ); + m_Controls->m_LegendView->setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOff ); + m_Controls->m_LegendView->show(); + m_Controls->m_LegendView->repaint(); + + } + + +} + void QmitkTensorReconstructionView::ItkReconstruction() { Reconstruct(0); } void QmitkTensorReconstructionView::TeemReconstruction() { Reconstruct(1); } void QmitkTensorReconstructionView::Reconstruct(int method) { if (m_CurrentSelection) { mitk::DataStorage::SetOfObjects::Pointer set = mitk::DataStorage::SetOfObjects::New(); int at = 0; for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); i != m_CurrentSelection->End(); ++i) { if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) { mitk::DataNode::Pointer node = nodeObj->GetDataNode(); if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) { set->InsertElement(at++, node); } } } if(method == 0) { ItkTensorReconstruction(set); } if(method == 1) { TeemTensorReconstruction(set); } } } void QmitkTensorReconstructionView::ItkTensorReconstruction (mitk::DataStorage::SetOfObjects::Pointer inImages) { try { itk::TimeProbe clock; int nrFiles = inImages->size(); if (!nrFiles) return; QString status; mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles); mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { mitk::DiffusionImage* vols = static_cast*>( (*itemiter)->GetData()); std::string nodename; (*itemiter)->GetStringProperty("name", nodename); ++itemiter; // TENSOR RECONSTRUCTION clock.Start(); MBI_INFO << "Tensor reconstruction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "Tensor reconstruction for %s", nodename.c_str()).toAscii()); typedef itk::DiffusionTensor3DReconstructionImageFilter< DiffusionPixelType, DiffusionPixelType, TTensorPixelType > TensorReconstructionImageFilterType; TensorReconstructionImageFilterType::Pointer tensorReconstructionFilter = TensorReconstructionImageFilterType::New(); tensorReconstructionFilter->SetGradientImage( vols->GetDirections(), vols->GetVectorImage() ); tensorReconstructionFilter->SetBValue(vols->GetB_Value()); tensorReconstructionFilter->SetThreshold( m_Controls->m_TensorReconstructionThreasholdEdit->text().toFloat() ); tensorReconstructionFilter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s."; // TENSORS TO DATATREE mitk::TensorImage::Pointer image = mitk::TensorImage::New(); typedef itk::Image, 3> TensorImageType; TensorImageType::Pointer tensorImage; tensorImage = tensorReconstructionFilter->GetOutput(); - - // Check the tensor for negative eigenvalues if(m_Controls->m_CheckNegativeEigenvalues->isChecked()) { typedef itk::ImageRegionIterator TensorImageIteratorType; TensorImageIteratorType tensorIt(tensorImage, tensorImage->GetRequestedRegion()); tensorIt.GoToBegin(); while(!tensorIt.IsAtEnd()) { typedef itk::DiffusionTensor3D TensorType; //typedef itk::Tensor TensorType2; TensorType tensor = tensorIt.Get(); // TensorType2 tensor2; /* for(int i=0; i SymEigenSystemType; SymEigenSystemType eig (tensor2.GetVnlMatrix()); for(unsigned int i=0; iInitializeByItk( tensorImage.GetPointer() ); image->SetVolume( tensorReconstructionFilter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); QString newname; newname = newname.append(nodename.c_str()); newname = newname.append("_dti"); SetDefaultNodeProperties(node, newname.toStdString()); nodes.push_back(node); mitk::ProgressBar::GetInstance()->Progress(); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) GetDefaultDataStorage()->Add(*nodeIt); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles).toAscii()); m_MultiWidget->RequestUpdate(); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex ; return ; } } void QmitkTensorReconstructionView::TeemTensorReconstruction (mitk::DataStorage::SetOfObjects::Pointer inImages) { try { itk::TimeProbe clock; int nrFiles = inImages->size(); if (!nrFiles) return; QString status; mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles); mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { mitk::DiffusionImage* vols = static_cast*>( (*itemiter)->GetData()); std::string nodename; (*itemiter)->GetStringProperty("name", nodename); ++itemiter; // TENSOR RECONSTRUCTION clock.Start(); MBI_INFO << "Teem Tensor reconstruction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "Teem Tensor reconstruction for %s", nodename.c_str()).toAscii()); typedef mitk::TeemDiffusionTensor3DReconstructionImageFilter< DiffusionPixelType, TTensorPixelType > TensorReconstructionImageFilterType; TensorReconstructionImageFilterType::Pointer tensorReconstructionFilter = TensorReconstructionImageFilterType::New(); tensorReconstructionFilter->SetInput( vols ); if(!m_Controls->m_TensorEstimationTeemSigmaEdit->text().contains(QString("NaN"))) tensorReconstructionFilter->SetSigma( m_Controls->m_TensorEstimationTeemSigmaEdit->text().toFloat() ); switch(m_Controls->m_TensorEstimationTeemEstimationMethodCombo->currentIndex()) { // items << "LLS (Linear Least Squares)" //<< "MLE (Maximum Likelihood)" //<< "NLS (Nonlinear Least Squares)" //<< "WLS (Weighted Least Squares)"; case 0: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsLLS); break; case 1: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsMLE); break; case 2: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsNLS); break; case 3: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsWLS); break; default: tensorReconstructionFilter->SetEstimationMethod(mitk::TeemTensorEstimationMethodsLLS); } tensorReconstructionFilter->SetNumIterations( m_Controls->m_TensorEstimationTeemNumItsSpin->value() ); if(m_Controls->m_TensorEstimationManualThreashold->isChecked()) tensorReconstructionFilter->SetConfidenceThreshold( m_Controls->m_TensorReconstructionThreasholdEdit_2->text().toDouble() ); tensorReconstructionFilter->SetConfidenceFuzzyness( m_Controls->m_TensorEstimationTeemFuzzyEdit->text().toFloat() ); tensorReconstructionFilter->SetMinPlausibleValue( m_Controls->m_TensorEstimationTeemMinValEdit->text().toDouble() ); tensorReconstructionFilter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s." ; // TENSORS TO DATATREE mitk::DataNode::Pointer node2=mitk::DataNode::New(); node2->SetData( tensorReconstructionFilter->GetOutputItk() ); QString newname; newname = newname.append(nodename.c_str()); newname = newname.append("_dtix"); SetDefaultNodeProperties(node2, newname.toStdString()); nodes.push_back(node2); mitk::ProgressBar::GetInstance()->Progress(); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) GetDefaultDataStorage()->Add(*nodeIt); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles).toAscii()); m_MultiWidget->RequestUpdate(); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex ; return ; } } void QmitkTensorReconstructionView::SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name) { node->SetProperty( "ShowMaxNumber", mitk::IntProperty::New( 500 ) ); node->SetProperty( "Scaling", mitk::FloatProperty::New( 1.0 ) ); node->SetProperty( "Normalization", mitk::OdfNormalizationMethodProperty::New()); node->SetProperty( "ScaleBy", mitk::OdfScaleByProperty::New()); node->SetProperty( "IndexParam1", mitk::FloatProperty::New(2)); node->SetProperty( "IndexParam2", mitk::FloatProperty::New(1)); node->SetProperty( "visible", mitk::BoolProperty::New( true ) ); node->SetProperty( "VisibleOdfs", mitk::BoolProperty::New( false ) ); node->SetProperty ("layer", mitk::IntProperty::New(100)); node->SetProperty( "DoRefresh", mitk::BoolProperty::New( true ) ); //node->SetProperty( "opacity", mitk::FloatProperty::New(1.0f) ); node->SetProperty( "name", mitk::StringProperty::New(name) ); } //node->SetProperty( "volumerendering", mitk::BoolProperty::New( false ) ); //node->SetProperty( "use color", mitk::BoolProperty::New( true ) ); //node->SetProperty( "texture interpolation", mitk::BoolProperty::New( true ) ); //node->SetProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ); //node->SetProperty( "layer", mitk::IntProperty::New(0)); //node->SetProperty( "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ); //node->SetOpacity(1.0f); //node->SetColor(1.0,1.0,1.0); //node->SetVisibility(true); //node->SetProperty( "IsTensorVolume", mitk::BoolProperty::New( true ) ); //mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); //mitk::LevelWindow levelwindow; //// levelwindow.SetAuto( image ); //levWinProp->SetLevelWindow( levelwindow ); //node->GetPropertyList()->SetProperty( "levelwindow", levWinProp ); //// add a default rainbow lookup table for color mapping //if(!node->GetProperty("LookupTable")) //{ // mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); // vtkLookupTable* vtkLut = mitkLut->GetVtkLookupTable(); // vtkLut->SetHueRange(0.6667, 0.0); // vtkLut->SetTableRange(0.0, 20.0); // vtkLut->Build(); // mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); // mitkLutProp->SetLookupTable(mitkLut); // node->SetProperty( "LookupTable", mitkLutProp ); //} //if(!node->GetProperty("binary")) // node->SetProperty( "binary", mitk::BoolProperty::New( false ) ); //// add a default transfer function //mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); //node->SetProperty ( "TransferFunction", mitk::TransferFunctionProperty::New ( tf.GetPointer() ) ); //// set foldername as string property //mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New( name ); //node->SetProperty( "name", nameProp ); void QmitkTensorReconstructionView::TensorsToDWI() { if (m_CurrentSelection) { mitk::DataStorage::SetOfObjects::Pointer set = mitk::DataStorage::SetOfObjects::New(); int at = 0; for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); i != m_CurrentSelection->End(); ++i) { if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) { mitk::DataNode::Pointer node = nodeObj->GetDataNode(); if(QString("TensorImage").compare(node->GetData()->GetNameOfClass())==0) { set->InsertElement(at++, node); } } } DoTensorsToDWI(set); } } void QmitkTensorReconstructionView::TensorsToQbi() { std::vector nodes = this->GetDataManagerSelection(); for (int i=0; i TensorPixelType; typedef itk::Image< TensorPixelType, 3 > TensorImageType; TensorImageType::Pointer itkvol = TensorImageType::New(); mitk::CastToItkImage(dynamic_cast(tensorImageNode->GetData()), itkvol); typedef itk::TensorImageToQBallImageFilter< TTensorPixelType, TTensorPixelType > FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput( itkvol ); filter->Update(); typedef itk::Vector OutputPixelType; typedef itk::Image OutputImageType; mitk::QBallImage::Pointer image = mitk::QBallImage::New(); OutputImageType::Pointer outimg = filter->GetOutput(); image->InitializeByItk( outimg.GetPointer() ); image->SetVolume( outimg->GetBufferPointer() ); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); QString newname; newname = newname.append(tensorImageNode->GetName().c_str()); newname = newname.append("_qbi"); node->SetName(newname.toAscii()); GetDefaultDataStorage()->Add(node); } } void QmitkTensorReconstructionView::OnSelectionChanged( std::vector nodes ) { if ( !this->IsVisible() ) return; } template std::vector > QmitkTensorReconstructionView::MakeGradientList() { std::vector > retval; vnl_matrix_fixed* U = itk::PointShell >::DistributePointShell(); for(int i=0; i v; v[0] = U->get(0,i); v[1] = U->get(1,i); v[2] = U->get(2,i); retval.push_back(v); } + // Add 0 vector for B0 + itk::Vector v; + v.Fill(0.0); + retval.push_back(v); + return retval; } void QmitkTensorReconstructionView::DoTensorsToDWI (mitk::DataStorage::SetOfObjects::Pointer inImages) { try { itk::TimeProbe clock; int nrFiles = inImages->size(); if (!nrFiles) return; QString status; mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles); mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { std::string nodename; (*itemiter)->GetStringProperty("name", nodename); mitk::TensorImage* vol = static_cast((*itemiter)->GetData()); ++itemiter; typedef float TTensorPixelType; typedef itk::DiffusionTensor3D< TTensorPixelType > TensorPixelType; typedef itk::Image< TensorPixelType, 3 > TensorImageType; TensorImageType::Pointer itkvol = TensorImageType::New(); mitk::CastToItkImage(vol, itkvol); typedef itk::TensorImageToDiffusionImageFilter< TTensorPixelType, DiffusionPixelType > FilterType; FilterType::GradientListType gradientList; switch(m_Controls->m_TensorsToDWINumDirsSelect->currentIndex()) { case 0: gradientList = MakeGradientList<12>(); break; case 1: gradientList = MakeGradientList<42>(); break; case 2: gradientList = MakeGradientList<92>(); break; case 3: gradientList = MakeGradientList<162>(); break; case 4: gradientList = MakeGradientList<252>(); break; case 5: gradientList = MakeGradientList<362>(); break; case 6: gradientList = MakeGradientList<492>(); break; case 7: gradientList = MakeGradientList<642>(); break; case 8: gradientList = MakeGradientList<812>(); break; case 9: gradientList = MakeGradientList<1002>(); break; default: gradientList = MakeGradientList<92>(); } double bVal = m_Controls->m_TensorsToDWIBValueEdit->text().toDouble(); // DWI ESTIMATION clock.Start(); MBI_INFO << "DWI Estimation "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( "DWI Estimation for %s", nodename.c_str()).toAscii()); FilterType::Pointer filter = FilterType::New(); filter->SetInput( itkvol ); filter->SetBValue(bVal); filter->SetGradientList(gradientList); //filter->SetNumberOfThreads(1); filter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s."; itk::Vector v; v[0] = 0; v[1] = 0; v[2] = 0; gradientList.push_back(v); // TENSORS TO DATATREE mitk::DiffusionImage::Pointer image = mitk::DiffusionImage::New(); image->SetVectorImage( filter->GetOutput() ); image->SetB_Value(bVal); image->SetDirections(gradientList); image->SetOriginalDirections(gradientList); image->InitializeFromVectorImage(); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); mitk::DiffusionImageMapper::SetDefaultProperties(node); QString newname; newname = newname.append(nodename.c_str()); newname = newname.append("_dwi"); node->SetName(newname.toAscii()); nodes.push_back(node); mitk::ProgressBar::GetInstance()->Progress(); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) GetDefaultDataStorage()->Add(*nodeIt); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles).toAscii()); m_MultiWidget->RequestUpdate(); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex ; return ; } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.h index 24deb57a32..8afe39952d 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionView.h @@ -1,123 +1,125 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date: 2009-05-28 17:19:30 +0200 (Do, 28 Mai 2009) $ Version: $Revision: 17495 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _QMITKTENSORRECONSTRUCTIONVIEW_H_INCLUDED #define _QMITKTENSORRECONSTRUCTIONVIEW_H_INCLUDED #include #include #include "ui_QmitkTensorReconstructionViewControls.h" #include #include #include #include #include typedef short DiffusionPixelType; struct TrSelListener; /*! * \ingroup org_mitk_gui_qt_tensorreconstruction_internal * * \brief QmitkTensorReconstructionView * * Document your class here. * * \sa QmitkFunctionality */ class QmitkTensorReconstructionView : public QmitkFunctionality { friend struct TrSelListener; // this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) Q_OBJECT public: static const std::string VIEW_ID; QmitkTensorReconstructionView(); QmitkTensorReconstructionView(const QmitkTensorReconstructionView& other); virtual ~QmitkTensorReconstructionView(); virtual void CreateQtPartControl(QWidget *parent); /// \brief Creation of the connections of main and control widget virtual void CreateConnections(); /// \brief Called when the functionality is activated virtual void Activated(); virtual void Deactivated(); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); static const int nrconvkernels; protected slots: void TensorsToQbi(); void TensorsToDWI(); void DoTensorsToDWI(mitk::DataStorage::SetOfObjects::Pointer inImages); void TeemCheckboxClicked(); void Advanced1CheckboxClicked(); void Advanced2CheckboxClicked(); void ManualThresholdClicked(); void MethodChoosen(int method); void Reconstruct(int method); void TeemReconstruction(); void ItkReconstruction(); + void ResidualCalculation(); + void ResidualClicked(int slice, int volume); + +protected: + void ItkTensorReconstruction (mitk::DataStorage::SetOfObjects::Pointer inImages); void TeemTensorReconstruction (mitk::DataStorage::SetOfObjects::Pointer inImages); - -protected: - void OnSelectionChanged( std::vector nodes ); Ui::QmitkTensorReconstructionViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; template std::vector > MakeGradientList() ; template void TemplatedAnalyticalTensorReconstruction(mitk::DiffusionImage* vols, float lambda, std::string nodename, std::vector* nodes, int normalization); void SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name); berry::ISelectionListener::Pointer m_SelListener; berry::IStructuredSelection::ConstPointer m_CurrentSelection; }; #endif // _QMITKTENSORRECONSTRUCTIONVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionViewControls.ui index 7feacc3f22..23f2ebb31f 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTensorReconstructionViewControls.ui @@ -1,524 +1,696 @@ QmitkTensorReconstructionViewControls 0 0 345 - 836 + 1303 0 0 true QmitkTensorReconstructionViewControls ITK Reconstruction Advanced Settings false QFrame::StyledPanel QFrame::Raised QFormLayout::AllNonFixedFieldsGrow B0 Threshold false 0 Check for negative eigenvalues QFrame::StyledPanel QFrame::Raised false ITK Tensor Reconstruction Estimate Diffusion Image from Tensors QFrame::StyledPanel QFrame::Raised QFormLayout::AllNonFixedFieldsGrow 6 6 9 how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" B-Value false 0 0 how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" # Gradient Directions 3 12 42 92 162 252 362 492 642 812 1002 false Diffusion Image Estimation Estimate Q-Ball Image from Tensors false Calculate ODF value as tensor value in the according direction Q-Ball Image Estimation true Teem Reconstruction true Teem Reconstruction Advanced Settings QFrame::StyledPanel QFrame::Raised QFormLayout::AllNonFixedFieldsGrow important in case of method wls # Iterations false how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" Fuzzy confidence false 0 0 how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" how fuzzy the confidence boundary should be. By default, confidence boundary is perfectly sharp (float); default: "0" minimum plausible value (especially important for linear least squares estimation) (double); default: "1.0" minimum plausible value (especially important for linear least squares estimation) (double); default: "1.0" minimum plausible value (especially important for linear least squares estimation) (double); default: "1.0" Min plausible value false Rician noise parameter (float) Rician noise parameter (float) Rician noise parameter (float) Sigma false 0 0 minimum plausible value (especially important for linear least squares estimation) (double); default: "1.0" minimum plausible value (especially important for linear least squares estimation) (double); default: "1.0" minimum plausible value (especially important for linear least squares estimation) (double); default: "1.0" 0 0 Rician noise parameter (float) Rician noise parameter (float) Rician noise parameter (float) Method B0-Threshold false 0 false Teem Tensor Reconstruction + + + + Residuals + + + false + + + false + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + false + + + Calculate the residual from a dti and a dwi iimage + + + + + + + + + Residual Image Calculation + + + + + + + 1 + + + + Per volume + + + + + + + 200 + 300 + + + + + + + + + Per slice + + + + + + outliers per slice + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 300 + 400 + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + + + + + + 20 + 255 + + + + + + + + + + + + + + + + + + + + + Volume: .., Slice:.. + + + + + + + + + + + false + + + percentages of error + + + + + + + + + Qt::Vertical 20 1150 + + + QmitkResidualAnalysisWidget + QWidget +
QmitkResidualAnalysisWidget.h
+ 1 +
+ + QmitkResidualViewWidget + QGraphicsView +
QmitkResidualViewWidget.h
+
+