From a0a6c9efad3d2ed4db83d630ed9313d5bfe4211d Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Thu, 3 Sep 2026 21:03:32 +0000 Subject: [PATCH] ENH: Fix buffer ownership and a redundant copy in SimpleITK interop image_from_simpleitk used itk.image_view_from_array, giving the itk::Image a pixel container that does not own its memory -- unsafe for Graft() and in-place filters. It now deep-copies via itk.image_from_array instead. simpleitk_from_image read the ITK image with itk.array_from_image (a deep copy) only to hand it to sitk.GetImageFromArray, which deep-copies again. It now reads a view instead, dropping the redundant copy. --- Wrapping/Generators/Python/itk/support/extras.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Wrapping/Generators/Python/itk/support/extras.py b/Wrapping/Generators/Python/itk/support/extras.py index 1ce7abc8a38..3460e8a57b7 100644 --- a/Wrapping/Generators/Python/itk/support/extras.py +++ b/Wrapping/Generators/Python/itk/support/extras.py @@ -815,9 +815,11 @@ def image_from_simpleitk(sitk_image) -> itkt.ImageBase: Geometry is read in ITK (x, y, z) order, via the order-explicit ``spacing_xyz``/``origin_xyz``/``direction_xyz`` keys when the object provides them and otherwise via ``GetSpacing()``/``GetOrigin()``/ - ``GetDirection()``. Pixels are copied through SimpleITK's array API. - Multi-component images become an itk.VectorImage. Entries reported by - ``GetMetaDataKeys()`` are copied into the MetaDataDictionary. + ``GetDirection()``. Pixels are deep-copied into a buffer the returned + image owns, so it is independent of ``sitk_image`` and safe for + grafting or in-place filters. Multi-component images become an + itk.VectorImage. Entries reported by ``GetMetaDataKeys()`` are copied + into the MetaDataDictionary. Parameters ---------- @@ -836,9 +838,9 @@ def image_from_simpleitk(sitk_image) -> itkt.ImageBase: number_of_components = sitk_image.GetNumberOfComponentsPerPixel() is_vector = number_of_components != 1 - array = sitk.GetArrayFromImage(sitk_image) + array = sitk.GetArrayViewFromImage(sitk_image) - l_image = itk.image_view_from_array(array, is_vector=is_vector) + l_image = itk.image_from_array(array, is_vector=is_vector) spacing = _spatial_from_order_explicit(sitk_image, "spacing") if spacing is not None: @@ -879,8 +881,8 @@ def simpleitk_from_image(image: itkt.ImageOrImageSource): image = itk.output(image) - # Updates the image, so the regions compared below are the ones just read. - array = itk.array_from_image(image) + # Updates the image; a view is safe since GetImageFromArray deep-copies. + array = itk.array_view_from_image(image) buffered_region = image.GetBufferedRegion() largest_region = image.GetLargestPossibleRegion()