/*========================================================================= Program: vv http://www.creatis.insa-lyon.fr/rio/vv Authors belong to: - University of LYON http://www.universite-lyon.fr/ - Léon Bérard cancer center http://www.centreleonberard.fr - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the copyright notices for more information. It is distributed under dual licence - BSD See included LICENSE.txt file - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html ===========================================================================**/ #ifndef CLITKSUVPEAKGENERICFILTER_TXX #define CLITKSUVPEAKGENERICFILTER_TXX #include "clitkImageCommon.h" #include "clitkCropLikeImageFilter.h" #include "clitkResampleImageWithOptionsFilter.h" #include namespace clitk { //-------------------------------------------------------------------- template SUVPeakGenericFilter::SUVPeakGenericFilter() :ImageToImageGenericFilter("SUVPeakGenericFilter") { InitializeImageType<2>(); InitializeImageType<3>(); InitializeImageType<4>(); } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template template void SUVPeakGenericFilter::InitializeImageType() { ADD_DEFAULT_IMAGE_TYPES(Dim); } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void SUVPeakGenericFilter::SetArgsInfo(const args_info_type & a) { mArgsInfo=a; // Set value this->SetIOVerbose(mArgsInfo.verbose_flag); if (mArgsInfo.input_given) this->AddInputFilename(mArgsInfo.input_arg); if (mArgsInfo.mask_given) this->AddInputFilename(mArgsInfo.mask_arg); } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template template void SUVPeakGenericFilter::UpdateWithInputImageType() { // Read input typename ImageType::Pointer input = this->template GetInput(0); //Read mask typedef itk::Image MaskImageType; typename MaskImageType::Pointer mask; if(mArgsInfo.mask_given) { mask = this->template GetInput(1); // Check mask sampling/size if (!HaveSameSizeAndSpacing(mask, input)) { if (mArgsInfo.allow_resize_flag) { if (mArgsInfo.verbose_flag) { std::cout << "Resize mask image like input" << std::endl; } typedef clitk::ResampleImageWithOptionsFilter ResamplerType; typename ResamplerType::Pointer resampler = ResamplerType::New(); resampler->SetInput(mask); //By default the interpolation in NN, Ok for mask resampler->SetOutputSpacing(input->GetSpacing()); resampler->SetOutputOrigin(mask->GetOrigin()); resampler->SetGaussianFilteringEnabled(false); resampler->Update(); mask = resampler->GetOutput(); typedef clitk::CropLikeImageFilter FilterType; typename FilterType::Pointer crop = FilterType::New(); crop->SetInput(mask); crop->SetCropLikeImage(input); crop->Update(); mask = crop->GetOutput(); } else { std::cerr << "Mask image has a different size/spacing than input. Abort. (Use option --allow_resize)" << std::endl; exit(-1); } } } else { mask = MaskImageType::New(); mask->SetRegions(input->GetLargestPossibleRegion()); mask->SetOrigin(input->GetOrigin()); mask->SetSpacing(input->GetSpacing()); mask->Allocate(); mask->FillBuffer(1); } double volume = 1000; //1 cc into mc if (mArgsInfo.volume_given) volume *= mArgsInfo.volume_arg; const double PI = 3.141592653589793238463; double radius = std::pow(3*volume/(4*PI),1./3); typename ImageType::Pointer kernel = ComputeMeanFilterKernel(input->GetSpacing(), radius); // Perform the convolution typedef itk::ConvolutionImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput(input); filter->SetKernelImage(kernel); filter->Update(); typename ImageType::Pointer output = filter->GetOutput(); typedef itk::ImageRegionConstIteratorWithIndex IteratorType; typedef itk::ImageRegionConstIteratorWithIndex MIteratorType; IteratorType iters(output, output->GetLargestPossibleRegion()); MIteratorType iterm(mask, mask->GetLargestPossibleRegion()); iters.GoToBegin(); iterm.GoToBegin(); double max = 0.0; typename ImageType::IndexType index; while (!iters.IsAtEnd()) { if (iterm.Get() == 1) { // inside the mask if (iters.Get() > max) { max = iters.Get(); index = iters.GetIndex(); } } ++iters; ++iterm; } typename ImageType::PointType p; output->TransformIndexToPhysicalPoint(index, p); std::cout<<"SUV Peak found in "<< p << " mm with the value " << max << std::endl; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template template typename ImageType::Pointer SUVPeakGenericFilter::ComputeMeanFilterKernel(const typename ImageType::SpacingType & spacing, double radius) { // Some kind of cache to speed up a bit static std::map cache; if (cache.find(radius) != cache.end()) { return cache.find(radius)->second; } // Compute a kernel that corresponds to a sphere with 1 inside, 0 // outside and in between proportional to the intersection between // the pixel and the sphere. Computed by Monte-Carlo because I don't // know an equation that compute the intersection volume between a // box and a sphere ... //auto kernel = ImageType::New(); typename ImageType::Pointer kernel = ImageType::New(); // Size of the kernel in pixel (minimum 3 pixels) typename ImageType::SizeType size; size[0] = std::max((int)ceil(radius*2/spacing[0]), 3); size[1] = std::max((int)ceil(radius*2/spacing[1]), 3); size[2] = std::max((int)ceil(radius*2/spacing[2]), 3); // Compute the region, such as the origin is at the center typename ImageType::IndexType start; start.Fill(0); typename ImageType::RegionType region; region.SetSize(size); region.SetIndex(start); kernel->SetRegions(region); kernel->SetSpacing(spacing); typename ImageType::PointType origin; origin[0] = -(double)size[0]/2.0*spacing[0]+spacing[0]/2.0; origin[1] = -(double)size[1]/2.0*spacing[1]+spacing[1]/2.0; origin[2] = -(double)size[2]/2.0*spacing[2]+spacing[2]/2.0; kernel->SetOrigin(origin); kernel->Allocate(); // Fill the kernel itk::ImageRegionIteratorWithIndex iter(kernel, region); typename ImageType::PointType center; center.Fill(0.0); typename ImageType::PointType hh; // half a voxel hh[0] = spacing[0]/2.0; hh[1] = spacing[1]/2.0; hh[2] = spacing[2]/2.0; double h = hh.EuclideanDistanceTo(center); // distance of half a pixel to its center. std::srand(time(NULL)); double sum = 0.0; while (!iter.IsAtEnd()) { typename ImageType::IndexType index = iter.GetIndex(); typename ImageType::PointType p; kernel->TransformIndexToPhysicalPoint(index, p); double d = p.EuclideanDistanceTo(center) + h; if (d