Skip to content

Commit

Permalink
ENH: Let ImageSpatialObject update the image regions of its base class
Browse files Browse the repository at this point in the history
`SpatialObject` stores the image regions of the spatial object. It appears
reasonable for its derived class `ImageSpatialObject` to keep them up-to-date,
in accordance with its image.
  • Loading branch information
N-Dekker authored and hjmjohnson committed Feb 7, 2024
1 parent 436423d commit 5e528ec
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Modules/Core/SpatialObjects/include/itkImageSpatialObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ class ITK_TEMPLATE_EXPORT ImageSpatialObject : public SpatialObject<TDimension>
SetInterpolator(InterpolatorType * interpolator);
itkGetConstMacro(Interpolator, InterpolatorType *);

/** Updates the regions of this spatial object in accordance with its current image, and calls its Superclass. */
void
Update() override;

protected:
/** Compute the boundaries of the image spatial object. */
void
Expand All @@ -149,6 +153,10 @@ class ITK_TEMPLATE_EXPORT ImageSpatialObject : public SpatialObject<TDimension>

typename InterpolatorType::Pointer m_Interpolator{};

/** Updates the regions of this spatial object in accordance with its current image. */
void
UpdateImageRegions();

#if !defined(ITK_LEGACY_REMOVE)
template <typename T>
void
Expand Down
28 changes: 28 additions & 0 deletions Modules/Core/SpatialObjects/include/itkImageSpatialObject.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ ImageSpatialObject<TDimension, PixelType>::SetImage(const ImageType * image)
}

m_Image = image;
UpdateImageRegions();

if (m_Interpolator)
{
m_Interpolator->SetInputImage(m_Image);
Expand Down Expand Up @@ -185,6 +187,32 @@ ImageSpatialObject<TDimension, PixelType>::InternalClone() const
return loPtr;
}


template <unsigned int TDimension, typename PixelType>
void
ImageSpatialObject<TDimension, PixelType>::Update()
{
UpdateImageRegions();

// Call the Superclass Update() after (not before) updating the image regions! The effect of Superclass::Update()
// may depend partially on the image regions.
Superclass::Update();
}


template <unsigned int TDimension, typename PixelType>
void
ImageSpatialObject<TDimension, PixelType>::UpdateImageRegions()
{
if (m_Image)
{
Superclass::SetLargestPossibleRegion(m_Image->GetLargestPossibleRegion());
Superclass::SetBufferedRegion(m_Image->GetBufferedRegion());
Superclass::SetRequestedRegion(m_Image->GetRequestedRegion());
}
}


template <unsigned int TDimension, typename PixelType>
void
ImageSpatialObject<TDimension, PixelType>::PrintSelf(std::ostream & os, Indent indent) const
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,47 @@ TEST(ImageMaskSpatialObject, IsInsideInWorldSpaceOverloads)
imageMaskSpatialObject->IsInsideInWorldSpace(point, 0, ""));
}
}


// Check that regions of the mask image are stored in the spatial object.
TEST(ImageMaskSpatialObject, StoresRegionsFromMaskImage)
{
using ImageMaskSpatialObjectType = itk::ImageMaskSpatialObject<>;
using MaskImageType = ImageMaskSpatialObjectType::ImageType;

// Test image regions of various indices and sizes:
for (const itk::IndexValueType indexValue : { -1, 0, 1 })
{
// Just test some small sizes, to make the test run fast:
for (itk::SizeValueType sizeValue{ 2 }; sizeValue < 4; ++sizeValue)
{
using RegionType = MaskImageType::RegionType;
using IndexType = MaskImageType::IndexType;
using SizeType = MaskImageType::SizeType;

// Create a mask image.
const auto maskImage = MaskImageType::New();
maskImage->SetRegions(RegionType{ IndexType::Filled(indexValue), SizeType::Filled(sizeValue) });
maskImage->Allocate(true);

const auto imageMaskSpatialObject = ImageMaskSpatialObjectType::New();
imageMaskSpatialObject->SetImage(maskImage);

EXPECT_EQ(imageMaskSpatialObject->GetLargestPossibleRegion(), maskImage->GetLargestPossibleRegion());
EXPECT_EQ(imageMaskSpatialObject->GetBufferedRegion(), maskImage->GetBufferedRegion());
EXPECT_EQ(imageMaskSpatialObject->GetRequestedRegion(), maskImage->GetRequestedRegion());

// Modify one of the image regions.
maskImage->SetRequestedRegion(RegionType{ IndexType::Filled(indexValue + 1), SizeType::Filled(sizeValue - 1) });

// Note: when an image region is modified _after_ calling SetImage, the user must call Update() to keep the
// regions of the spatial object up-to-date.
imageMaskSpatialObject->Update();

// Do the same checks after the Update():
EXPECT_EQ(imageMaskSpatialObject->GetLargestPossibleRegion(), maskImage->GetLargestPossibleRegion());
EXPECT_EQ(imageMaskSpatialObject->GetBufferedRegion(), maskImage->GetBufferedRegion());
EXPECT_EQ(imageMaskSpatialObject->GetRequestedRegion(), maskImage->GetRequestedRegion());
}
}
}

0 comments on commit 5e528ec

Please sign in to comment.