17 #include <itkImageRegionIterator.h> 19 #include <itkImageDuplicator.h> 20 #include <itkFFTConvolutionImageFilter.h> 26 m_HotspotRadiusinMM(6.2035049089940),
27 m_HotspotMustBeCompletelyInsideImage(true),
32 m_InternalMaskUpdateTime = 0;
40 m_ConvolutionImageMaxIndex.set_size(inputImage->GetDimension());
41 m_ConvolutionImageMinIndex.set_size(inputImage->GetDimension());
61 if(radiusInMillimeter != m_HotspotRadiusinMM)
63 m_HotspotRadiusinMM = radiusInMillimeter;
70 return m_HotspotRadiusinMM;
75 return m_HotspotMustBeCompletelyInsideImage;
80 if (m_HotspotMustBeCompletelyInsideImage != mustBeCompletelyInImage)
82 m_HotspotMustBeCompletelyInsideImage = mustBeCompletelyInImage;
90 if (IsUpdateRequired())
94 throw std::runtime_error(
"Error: image empty!" );
99 throw std::runtime_error(
"Error: invalid time step!" );
105 imageTimeSelector->UpdateLargestPossibleRegion();
108 m_internalImage = timeSliceImage;
109 m_internalMask2D =
nullptr;
110 m_internalMask3D =
nullptr;
112 if ( m_Mask !=
nullptr )
117 if ( m_internalImage->GetDimension() == 3 )
122 else if ( m_internalImage->GetDimension() == 2 )
129 throw std::runtime_error(
"Error: invalid image dimension" );
135 if ( m_internalImage->GetDimension() == 3 )
139 else if ( m_internalImage->GetDimension() == 2 )
145 throw std::runtime_error(
"Error: invalid image dimension" );
165 if (label != m_Label)
175 return m_ConvolutionImageMinIndex;
181 return m_ConvolutionImageMaxIndex;
184 template <
typename TPixel,
unsigned int VImageDimension >
186 HotspotMaskGenerator::CalculateExtremaWorld(
const itk::Image<TPixel, VImageDimension>* inputImage,
187 typename itk::Image<unsigned short, VImageDimension>::Pointer maskImage,
188 double neccessaryDistanceToImageBorderInMM,
191 typedef itk::Image< TPixel, VImageDimension >
ImageType;
192 typedef itk::Image< unsigned short, VImageDimension >
MaskImageType;
194 typedef itk::ImageRegionConstIteratorWithIndex<MaskImageType> MaskImageIteratorType;
195 typedef itk::ImageRegionConstIteratorWithIndex<ImageType> InputImageIndexIteratorType;
197 typename ImageType::SpacingType spacing = inputImage->GetSpacing();
201 minMax.
MaxIndex.set_size(VImageDimension);
202 minMax.
MaxIndex.set_size(VImageDimension);
204 typename ImageType::RegionType allowedExtremaRegion = inputImage->GetLargestPossibleRegion();
206 bool keepDistanceToImageBorders( neccessaryDistanceToImageBorderInMM > 0 );
207 if (keepDistanceToImageBorders)
209 itk::IndexValueType distanceInPixels[VImageDimension];
210 for(
unsigned short dimension = 0; dimension < VImageDimension; ++dimension)
216 distanceInPixels[dimension] = int( neccessaryDistanceToImageBorderInMM / spacing[dimension] + 0.5);
219 allowedExtremaRegion.ShrinkByRadius(distanceInPixels);
222 InputImageIndexIteratorType imageIndexIt(inputImage, allowedExtremaRegion);
227 typename ImageType::IndexType maxIndex;
228 typename ImageType::IndexType minIndex;
230 for(
unsigned short i = 0; i < VImageDimension; ++i)
236 if (maskImage !=
nullptr)
238 MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion());
239 typename ImageType::IndexType imageIndex;
240 typename ImageType::PointType worldPosition;
241 typename ImageType::IndexType maskIndex;
243 for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
245 imageIndex = maskIndex = maskIt.GetIndex();
247 if(maskIt.Get() == label)
249 if( allowedExtremaRegion.IsInside(imageIndex) )
251 imageIndexIt.SetIndex( imageIndex );
252 double value = imageIndexIt.Get();
256 if( value > maxValue )
258 maxIndex = imageIndexIt.GetIndex();
262 if(value < minValue )
264 minIndex = imageIndexIt.GetIndex();
273 for(imageIndexIt.GoToBegin(); !imageIndexIt.IsAtEnd(); ++imageIndexIt)
275 double value = imageIndexIt.Get();
279 if( value > maxValue )
281 maxIndex = imageIndexIt.GetIndex();
285 if(value < minValue )
287 minIndex = imageIndexIt.GetIndex();
293 minMax.
MaxIndex.set_size(VImageDimension);
294 minMax.
MinIndex.set_size(VImageDimension);
296 for(
unsigned int i = 0; i < minMax.
MaxIndex.size(); ++i)
301 for(
unsigned int i = 0; i < minMax.
MinIndex.size(); ++i)
306 minMax.
Max = maxValue;
307 minMax.
Min = minValue;
312 template <
unsigned int VImageDimension>
313 itk::Size<VImageDimension>
314 HotspotMaskGenerator::CalculateConvolutionKernelSize(
double spacing[VImageDimension],
317 typedef itk::Image< float, VImageDimension > KernelImageType;
318 typedef typename KernelImageType::SizeType SizeType;
321 for(
unsigned int i = 0; i < VImageDimension; ++i)
323 maskSize[i] =
static_cast<int>( 2 * radiusInMM / spacing[i]);
326 if(maskSize[i] % 2 == 0 )
334 template <
unsigned int VImageDimension>
336 HotspotMaskGenerator::GenerateHotspotSearchConvolutionKernel(
double mmPerPixel[VImageDimension],
339 std::stringstream ss;
340 for (
unsigned int i = 0; i < VImageDimension; ++i)
343 if (i < VImageDimension -1)
346 MITK_DEBUG <<
"Update convolution kernel for spacing (" << ss.str() <<
") and radius " << radiusInMM <<
"mm";
349 double radiusInMMSquared = radiusInMM * radiusInMM;
350 typedef itk::Image< float, VImageDimension > KernelImageType;
351 typename KernelImageType::Pointer convolutionKernel = KernelImageType::New();
354 typedef typename KernelImageType::SizeType SizeType;
355 SizeType maskSize = this->CalculateConvolutionKernelSize<VImageDimension>(mmPerPixel, radiusInMM);
358 convolutionMaskCenterIndex.Fill(0.0);
359 for(
unsigned int i = 0; i < VImageDimension; ++i)
361 convolutionMaskCenterIndex[i] = 0.5 * (double)(maskSize[i]-1);
364 typedef typename KernelImageType::IndexType IndexType;
368 typedef typename KernelImageType::RegionType RegionType;
369 RegionType maskRegion;
370 maskRegion.SetSize(maskSize);
371 maskRegion.SetIndex(maskIndex);
373 convolutionKernel->SetRegions(maskRegion);
374 convolutionKernel->SetSpacing(mmPerPixel);
375 convolutionKernel->Allocate();
378 typedef itk::ImageRegionIteratorWithIndex<KernelImageType> MaskIteratorType;
379 MaskIteratorType maskIt(convolutionKernel,maskRegion);
381 int numberOfSubVoxelsPerDimension = 2;
382 int numberOfSubVoxels = ::pow( static_cast<float>(numberOfSubVoxelsPerDimension), static_cast<float>(VImageDimension) );
383 double subVoxelSizeInPixels = 1.0 / (double)numberOfSubVoxelsPerDimension;
384 double valueOfOneSubVoxel = 1.0 / (double)numberOfSubVoxels;
386 double distanceSquared = 0.0;
388 typedef itk::ContinuousIndex<double, VImageDimension> ContinuousIndexType;
389 for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
391 ContinuousIndexType indexPoint(maskIt.GetIndex());
393 for (
unsigned int dimension = 0; dimension < VImageDimension; ++dimension)
395 voxelPosition[dimension] = indexPoint[dimension];
398 double maskValue = 0.0;
401 for (subVoxelOffset[0] = -0.5 + subVoxelSizeInPixels / 2.0;
402 subVoxelOffset[0] < +0.5;
403 subVoxelOffset[0] += subVoxelSizeInPixels)
405 for (subVoxelOffset[1] = -0.5 + subVoxelSizeInPixels / 2.0;
406 subVoxelOffset[1] < +0.5;
407 subVoxelOffset[1] += subVoxelSizeInPixels)
409 for (subVoxelOffset[2] = -0.5 + subVoxelSizeInPixels / 2.0;
410 subVoxelOffset[2] < +0.5;
411 subVoxelOffset[2] += subVoxelSizeInPixels)
413 subVoxelIndexPosition = voxelPosition + subVoxelOffset;
415 (subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0] * (subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0]
416 + (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1] * (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1]
417 + (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2] * (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2];
419 if (distanceSquared <= radiusInMMSquared)
421 maskValue += valueOfOneSubVoxel;
426 maskIt.Set( maskValue );
429 return convolutionKernel;
432 template <
typename TPixel,
unsigned int VImageDimension>
434 HotspotMaskGenerator::GenerateConvolutionImage(
const itk::Image<TPixel, VImageDimension>* inputImage )
436 double mmPerPixel[VImageDimension];
437 for (
unsigned int dimension = 0; dimension < VImageDimension; ++dimension)
439 mmPerPixel[dimension] = inputImage->GetSpacing()[dimension];
443 typedef itk::Image< float, VImageDimension > KernelImageType;
444 typename KernelImageType::Pointer convolutionKernel = this->GenerateHotspotSearchConvolutionKernel<VImageDimension>(mmPerPixel, m_HotspotRadiusinMM);
448 typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType;
451 ConvolutionImageType> ConvolutionFilterType;
453 typename ConvolutionFilterType::Pointer convolutionFilter = ConvolutionFilterType::New();
454 typedef itk::ConstantBoundaryCondition<InputImageType, InputImageType> BoundaryConditionType;
455 BoundaryConditionType boundaryCondition;
456 boundaryCondition.SetConstant(0.0);
458 if (m_HotspotMustBeCompletelyInsideImage)
461 convolutionFilter->SetBoundaryCondition(&boundaryCondition);
464 convolutionFilter->SetInput(inputImage);
465 convolutionFilter->SetKernelImage(convolutionKernel);
466 convolutionFilter->SetNormalize(
true);
467 MITK_DEBUG <<
"Update Convolution image for hotspot search";
468 convolutionFilter->UpdateLargestPossibleRegion();
470 typename ConvolutionImageType::Pointer convolutionImage = convolutionFilter->GetOutput();
471 convolutionImage->SetSpacing( inputImage->GetSpacing() );
473 return convolutionImage;
476 template <
typename TPixel,
unsigned int VImageDimension>
478 HotspotMaskGenerator::FillHotspotMaskPixels( itk::Image<TPixel, VImageDimension>* maskImage,
479 itk::Point<double, VImageDimension> sphereCenter,
480 double sphereRadiusInMM )
483 typedef itk::ImageRegionIteratorWithIndex<MaskImageType> MaskImageIteratorType;
485 MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion());
487 typename MaskImageType::IndexType maskIndex;
488 typename MaskImageType::PointType worldPosition;
491 for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
493 maskIndex = maskIt.GetIndex();
494 maskImage->TransformIndexToPhysicalPoint(maskIndex, worldPosition);
495 maskIt.Set( worldPosition.EuclideanDistanceTo(sphereCenter) <= sphereRadiusInMM ? 1 : 0 );
499 template <
typename TPixel,
unsigned int VImageDimension>
501 HotspotMaskGenerator::CalculateHotspotMask(itk::Image<TPixel, VImageDimension>* inputImage,
502 typename itk::Image<unsigned short, VImageDimension>::Pointer maskImage,
506 typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType;
507 typedef itk::Image< unsigned short, VImageDimension >
MaskImageType;
509 typename ConvolutionImageType::Pointer convolutionImage = this->GenerateConvolutionImage(inputImage);
511 if (convolutionImage.IsNull())
513 MITK_ERROR <<
"Empty convolution image in CalculateHotspotStatistics(). We should never reach this state (logic error).";
514 throw std::logic_error(
"Empty convolution image in CalculateHotspotStatistics()");
519 if (maskImage ==
nullptr)
521 maskImage = MaskImageType::New();
522 typename MaskImageType::RegionType maskRegion = inputImage->GetLargestPossibleRegion();
523 typename MaskImageType::SpacingType maskSpacing = inputImage->GetSpacing();
524 typename MaskImageType::PointType maskOrigin = inputImage->GetOrigin();
525 typename MaskImageType::DirectionType maskDirection = inputImage->GetDirection();
526 maskImage->SetRegions(maskRegion);
527 maskImage->Allocate();
528 maskImage->SetOrigin(maskOrigin);
529 maskImage->SetSpacing(maskSpacing);
530 maskImage->SetDirection(maskDirection);
532 maskImage->FillBuffer(1);
538 double requiredDistanceToBorder = m_HotspotMustBeCompletelyInsideImage ? m_HotspotRadiusinMM : -1.0;
539 ImageExtrema convolutionImageInformation = CalculateExtremaWorld(convolutionImage.GetPointer(), maskImage, requiredDistanceToBorder, label);
541 bool isHotspotDefined = convolutionImageInformation.
Defined;
543 if (!isHotspotDefined)
545 MITK_ERROR <<
"No origin of hotspot-sphere was calculated!";
558 typename MaskImageType::Pointer hotspotMaskITK = MaskImageType::New();
559 hotspotMaskITK->SetOrigin(inputImage->GetOrigin());
560 hotspotMaskITK->SetSpacing(inputImage->GetSpacing());
561 hotspotMaskITK->SetLargestPossibleRegion(inputImage->GetLargestPossibleRegion());
562 hotspotMaskITK->SetBufferedRegion(inputImage->GetBufferedRegion());
563 hotspotMaskITK->SetDirection(inputImage->GetDirection());
564 hotspotMaskITK->SetNumberOfComponentsPerPixel(inputImage->GetNumberOfComponentsPerPixel());
565 hotspotMaskITK->Allocate();
566 hotspotMaskITK->FillBuffer(1);
568 typedef typename InputImageType::IndexType IndexType;
569 IndexType maskCenterIndex;
570 for (
unsigned int d =0; d< VImageDimension;++d)
572 maskCenterIndex[d]=convolutionImageInformation.
MaxIndex[d];
575 typename ConvolutionImageType::PointType maskCenter;
576 inputImage->TransformIndexToPhysicalPoint(maskCenterIndex,maskCenter);
578 FillHotspotMaskPixels(hotspotMaskITK.GetPointer(), maskCenter, m_HotspotRadiusinMM);
584 m_ConvolutionImageMaxIndex = convolutionImageInformation.
MaxIndex;
585 m_ConvolutionImageMinIndex = convolutionImageInformation.
MinIndex;
589 bool HotspotMaskGenerator::IsUpdateRequired()
const 591 unsigned long thisClassTimeStamp = this->GetMTime();
593 unsigned long maskGeneratorTimeStamp = m_Mask->GetMTime();
594 unsigned long inputImageTimeStamp =
m_inputImage->GetMTime();
596 if (thisClassTimeStamp > m_InternalMaskUpdateTime)
601 if (m_InternalMaskUpdateTime < maskGeneratorTimeStamp || m_InternalMaskUpdateTime < inputImageTimeStamp)
606 if (internalMaskTimeStamp > m_InternalMaskUpdateTime)
vnl_vector< int > MaxIndex
vnl_vector< int > GetHotspotIndex()
Returns the image index where the hotspot is located.
void SetHotspotRadiusInMM(double radiusInMillimeter)
Set the radius of the hotspot (in MM)
mitk::Image::ConstPointer m_inputImage
itk::Image< unsigned char, 3 > ImageType
::mitk::Image InputImageType
DataCollection - Class to facilitate loading/accessing structured data.
void SetLabel(unsigned short label)
If a maskGenerator is set, this detemines which mask value is used.
void SetInputImage(mitk::Image::Pointer inputImage)
Set the input image. Required for this class.
void SetTimeStep(unsigned int timeStep) override
SetTimeStep is used to set the time step for which the mask is to be generated.
~HotspotMaskGenerator() override
Image::Pointer GrabItkImageMemory(itk::SmartPointer< ItkOutputImageType > &itkimage, mitk::Image *mitkImage=nullptr, const BaseGeometry *geometry=nullptr, bool update=true)
Grabs the memory of an itk::Image (with a specific type) and puts it into an mitk::Image.The memory is managed by the mitk::Image after calling this function. The itk::Image remains valid until the mitk::Image decides to free the memory.
void SetMask(MaskGenerator::Pointer mask)
Set a mask (can be nullptr if no mask is desired)
#define AccessFixedDimensionByItk_2(mitkImage, itkImageTypeFunction, dimension, arg1, arg2)
vnl_vector< int > MinIndex
itk::Image< unsigned char, 3 > MaskImageType
mitk::Image::Pointer m_InternalMask
vnl_vector< int > GetConvolutionImageMinIndex()
Returns the index where the convolution image is minimal (darkest spot in image)
void SetHotspotMustBeCompletelyInsideImage(bool hotspotCompletelyInsideImage)
Define whether the hotspot must be completely inside the image. Default is true.
mitk::Image::Pointer GetMask() override
Computes and returns the hotspot mask. The hotspot mask has the same size as the input image...
void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer< ItkOutputImageType > &itkOutputImage)
Cast an mitk::Image to an itk::Image with a specific type.
bool GetHotspotMustBeCompletelyInsideImage() const
mitk::Image::Pointer mask
const double & GetHotspotRadiusinMM() const