Skip to content

Commit

Permalink
PERF: Add SpatialObject IsInsideInWorldSpace(const PointType &) overload
Browse files Browse the repository at this point in the history
A performance improvement of more than 35% was observed, from more than 0.62
sec. to less than 0.38 sec. when calling IsInsideInWorldSpace(point) 2^25 times,
using VS2022 Release.
  • Loading branch information
N-Dekker authored and dzenanz committed Feb 1, 2024
1 parent 8aab642 commit 2ca2351
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 1 deletion.
7 changes: 6 additions & 1 deletion Modules/Core/SpatialObjects/include/itkSpatialObject.h
Expand Up @@ -332,7 +332,12 @@ class ITK_TEMPLATE_EXPORT SpatialObject : public DataObject
* transform may be updated explicitly by calling `GetObjectToWorldTransformInverse()`, `Update()`, or
* `SetObjectToWorldTransform(transform)` */
virtual bool
IsInsideInWorldSpace(const PointType & point, unsigned int depth = 0, const std::string & name = "") const;
IsInsideInWorldSpace(const PointType & point, unsigned int depth, const std::string & name = "") const;

/** Overload, optimized for depth = 0 and name = "": `spatialObject.IsInsideInWorldSpace(point)` is equivalent to
* `spatialObject.IsInsideInWorldSpace(point, 0, "")`, but much faster. */
bool
IsInsideInWorldSpace(const PointType & point) const;

/** World space equivalent to IsEvaluableAtInObjectSpace
* \note This member function assumes that the internal `ObjectToWorldTransformInverse` transform is up-to-date. This
Expand Down
8 changes: 8 additions & 0 deletions Modules/Core/SpatialObjects/include/itkSpatialObject.hxx
Expand Up @@ -184,6 +184,14 @@ SpatialObject<TDimension>::IsInsideInWorldSpace(const PointType & point,
return IsInsideInObjectSpace(pnt, depth, name);
}

template <unsigned int TDimension>
bool
SpatialObject<TDimension>::IsInsideInWorldSpace(const PointType & point) const
{
const PointType pnt = m_ObjectToWorldTransformInverse->TransformPoint(point);
return IsInsideInObjectSpace(pnt);
}

template <unsigned int TDimension>
bool
SpatialObject<TDimension>::IsInsideChildrenInObjectSpace(const PointType & point,
Expand Down
Expand Up @@ -323,3 +323,31 @@ TEST(ImageMaskSpatialObject, CornerPointIsNotInsideMaskOfZeroValues)
const double cornerPoint[] = { 1.5, 1.5 };
ASSERT_FALSE(imageMaskSpatialObject->IsInsideInObjectSpace(cornerPoint));
}

// Check that the IsInsideInWorldSpace overloads yield the same result, when depth = 0 and name = "".
TEST(ImageMaskSpatialObject, IsInsideInWorldSpaceOverloads)
{
constexpr auto imageDimension = 2U;
using ImageMaskSpatialObjectType = itk::ImageMaskSpatialObject<imageDimension>;
using MaskImageType = ImageMaskSpatialObjectType::ImageType;
using MaskPixelType = MaskImageType::PixelType;
using PointType = MaskImageType::PointType;

// Create a mask image.
const auto maskImage = MaskImageType::New();
maskImage->SetRegions(itk::Size<imageDimension>::Filled(2));
maskImage->Allocate(true);
maskImage->SetPixel({}, MaskPixelType{ 1 });
maskImage->SetSpacing(itk::MakeFilled<MaskImageType::SpacingType>(0.5));

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

for (const double pointValue : { -1.0, 0.0, 0.5, 1.0 })
{
const PointType point(pointValue);

EXPECT_EQ(imageMaskSpatialObject->IsInsideInWorldSpace(point),
imageMaskSpatialObject->IsInsideInWorldSpace(point, 0, ""));
}
}

0 comments on commit 2ca2351

Please sign in to comment.