Skip to content

ENH: Add SimpleITK <-> ITK image conversion to the Python interface (supersedes #6021) - #6811

Open
hjmjohnson wants to merge 1 commit into
InsightSoftwareConsortium:mainfrom
hjmjohnson:enh-image-from-simpleitk
Open

ENH: Add SimpleITK <-> ITK image conversion to the Python interface (supersedes #6021)#6811
hjmjohnson wants to merge 1 commit into
InsightSoftwareConsortium:mainfrom
hjmjohnson:enh-image-from-simpleitk

Conversation

@hjmjohnson

Copy link
Copy Markdown
Member

Adds itk.image_from_simpleitk() and itk.simpleitk_from_image(), and teaches the filter decorator to accept a SimpleITK image wherever it accepts a NumPy array. Supersedes #6021 (@blowekamp), which stalled on the axis-order conflict that #6710 has since resolved.

No new dependency. SimpleITK is imported lazily inside the functions, so ITK builds and imports without it. The in-tree test uses a duck-typed stub and never imports SimpleITK — SimpleITK's superbuild builds ITK, so a test requiring it would close a cycle in the build graph, and SimpleITK is not present in ITK CI.

Related Status
Original PR this supersedes #6021
Axis-order conflict #6706
Order-explicit keys this builds on #6710
SimpleITK-side dual (open question) SimpleITK#2531
Why the bare spacing key cannot be used

image['spacing'] means (z, y, x) on an itk.Image and (x, y, z) on a SimpleITK Image. A converter reading it from one and writing it to the other silently reverses the spacing — the conflict reported in #6706, and what blocked #6021.

Geometry is therefore read through the order-explicit spacing_xyz / origin_xyz / direction_xyz keys from #6710, falling back to GetSpacing() / GetOrigin() / GetDirection(). Both are unambiguous.

Note that SimpleITK exposes no order-explicit keys today, so the accessor fallback carries every conversion; the key path is forward-looking, and starts working by itself if SimpleITK#2531 lands. A test asserts the two bare-key conventions still disagree, so this fails loudly rather than drifting if either toolkit changes.

Concerns from #6021, and how each is resolved
Concern Raised by Resolution
P1 — fallback never read GetSpacing()/GetOrigin()/GetDirection(); SimpleITK exposes methods, not dict keys. Vector images treated as scalar, geometry left at ITK defaults greptile _spatial_from_order_explicit() tries the _xyz key, then the accessor. Components come from GetNumberOfComponentsPerPixel(); dim from GetDimension(), not array.ndim
Missing image buffer start index @hjmjohnson (CHANGES_REQUESTED) A non-zero buffered-region index is carried as ITK_original_index and restored by the inverse. SimpleITK images always start at 0, so it cannot be represented directly
Store ImageRegion.m_Index as metadata @hjmjohnson / @blowekamp Implemented as above. The earlier "not needed" applied to sitk→itk, where SimpleITK enforces a zero index; this PR adds the itk→sitk direction, where it is needed

Metadata: every key is copied in both directions, and ITK_-prefixed entries are guaranteed to survive — a conversion failure on one of those raises rather than being silently dropped.

Testing

macOS 15 arm64, Release.

  • PythonSimpleITKProtocolTestPassed via ctest against this commit. Runs with SimpleITK present and with it blocked, confirming no hard dependency.
  • Full ITK Python suite — 167/176. The 9 failures are pre-existing: the identical 9 test numbers fail with pristine upstream extras.py/helpers.py. Causes are a broken VTK @rpath and a wrapping predating 002eebce7c4, neither related to this change.
  • Cross-toolkit round trip — 16/16 with ITK 6.0.0 and SimpleITK 3.0.0b1 built against that same ITK, in one interpreter. Covers geometry, vector, 2-D, pixels, metadata, index round trip, and physical-point agreement between the toolkits (which catches a transposed direction matrix that element-wise comparison misses). That suite lives outside both projects so neither gains a dependency.
  • pre-commit run --all-files passes on the branch tip.

Not validated on Linux or Windows.

@github-actions github-actions Bot added type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots type:Enhancement Improvement of existing methods or implementation area:Python wrapping Python bindings for a class type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct labels Aug 27, 2026
@hjmjohnson

Copy link
Copy Markdown
Member Author

@greptileai review

@greptile-apps

This comment was marked as resolved.

Comment thread Wrapping/Generators/Python/itk/support/extras.py Outdated
Comment thread Wrapping/Generators/Python/itk/support/extras.py
itk.image_from_simpleitk() and itk.simpleitk_from_image() convert between the
two toolkits, and the filter decorator accepts a SimpleITK image wherever it
accepts a NumPy array.

Geometry uses the order-explicit spatial keys from InsightSoftwareConsortium#6710, falling back to the
Get*() accessors. The bare 'spacing' key means (z,y,x) on an itk.Image and
(x,y,z) on a SimpleITK Image, so reading it would reverse the spacing (InsightSoftwareConsortium#6706).

SimpleITK images start at index 0, so the origin moves to the first stored
voxel and the index is carried as ITK_original_index; the inverse restores
both, keeping the pixels in the same physical location.

Supersedes InsightSoftwareConsortium#6021.

Co-Authored-By: Bradley Lowekamp <321061+blowekamp@users.noreply.github.com>
Co-Authored-By: Matt McCormick <25432+thewtex@users.noreply.github.com>
Co-Authored-By: Dzenan Zukic <1792121+dzenanz@users.noreply.github.com>
@hjmjohnson
hjmjohnson force-pushed the enh-image-from-simpleitk branch from 769fa94 to 46afb95 Compare August 28, 2026 00:52
@hjmjohnson
hjmjohnson marked this pull request as ready for review August 28, 2026 00:57

@thewtex thewtex left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@thewtex
thewtex requested a review from blowekamp August 28, 2026 17:52
@blowekamp

blowekamp commented Aug 31, 2026

Copy link
Copy Markdown
Member

Thank for working on this and moving it forward!

Do you have an active need to support the round trip origin that adds those special cases to this conversion code? Maybe this is not a requirement of these methods? [ EDIT: Additionally if the buffered region is not the largest possible region, then in this current form the round trip image has different region information.]

I am still mulling over adding the _zyx _zyx keys to the SimpleITK class. The extra specification and verbosity is nice and clarifies things. It is unfortunate that the keys between ITK and SimpleITK are not natively compatible. But for SimpleITK maybe it would be simpler to just have the one consistent representation. I am still mulling over the best corse of action here.

Thanks again!

@hjmjohnson

hjmjohnson commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

Thank for working on this and moving it forward!

Do you have an active need to support the round-trip origin that adds those special cases to this conversion code? Maybe this is not a requirement of these methods? [ EDIT: Additionally, if the buffered region is not the largest possible region, then in this current form the round trip image has different region information.]

@blowekamp ,

I do not have an active case because this was unusable previously :). I was trying to be complete and hoped for parity with C++ in the new features being added. I had missed considering the Buffer != Largest case, and I think we should throw a Python exception in that slightly unusual case for now (at least by default).

Hans

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Python wrapping Python bindings for a class type:Enhancement Improvement of existing methods or implementation type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants