ENH: Add SimpleITK <-> ITK image conversion to the Python interface (supersedes #6021) - #6811
Conversation
|
@greptileai review |
This comment was marked as resolved.
This comment was marked as resolved.
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>
769fa94 to
46afb95
Compare
|
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! |
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 |
Adds
(@blowekamp), which stalled on the axis-order conflict that
has since resolved.
itk.image_from_simpleitk()anditk.simpleitk_from_image(), and teaches the filter decorator to accept a SimpleITK image wherever it accepts a NumPy array. SupersedesNo 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.
Why the bare
spacingkey cannot be usedimage['spacing']means (z, y, x) on anitk.Imageand (x, y, z) on a SimpleITKImage. 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_xyzkeys from #6710, falling back toGetSpacing()/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
GetSpacing()/GetOrigin()/GetDirection(); SimpleITK exposes methods, not dict keys. Vector images treated as scalar, geometry left at ITK defaults_spatial_from_order_explicit()tries the_xyzkey, then the accessor. Components come fromGetNumberOfComponentsPerPixel();dimfromGetDimension(), notarray.ndimITK_original_indexand restored by the inverse. SimpleITK images always start at 0, so it cannot be represented directlyImageRegion.m_Indexas metadataMetadata: 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.
PythonSimpleITKProtocolTest— Passed via ctest against this commit. Runs with SimpleITK present and with it blocked, confirming no hard dependency.extras.py/helpers.py. Causes are a broken VTK@rpathand a wrapping predating002eebce7c4, neither related to this change.pre-commit run --all-filespasses on the branch tip.Not validated on Linux or Windows.