Skip to content

fix(image-io): support 32-bit integer TIFF read/write - #1547

Draft
thewtex wants to merge 4 commits into
InsightSoftwareConsortium:mainfrom
thewtex:write-image-uint32
Draft

fix(image-io): support 32-bit integer TIFF read/write#1547
thewtex wants to merge 4 commits into
InsightSoftwareConsortium:mainfrom
thewtex:write-image-uint32

Conversation

@thewtex

@thewtex thewtex commented Jul 2, 2026

Copy link
Copy Markdown
Member

Summary

Fixes #1544writeImageNode / writeImage crashed when given a Uint32Array (component type uint32), while uint8/uint16 worked.

Root cause is in ITK's itk::TIFFImageIO (which ITK-Wasm builds from the pinned thewtex/ITK branch). Although uint32/int32 are advertised as supported component types and the TIFF metadata reader maps 32-bit samples to IOComponentEnum::UINT/::INT, the pixel paths did not handle them:

  • Write threw itk::ExceptionObject for uint32/int32, which surfaced to JS as an unrecoverable WASM abort (the raw exception-pointer number in the issue).
  • Read had no UINT/INT dispatch case, so a 32-bit-integer TIFF read back as a zero-filled buffer.

Other formats (MetaImage, NRRD, NIfTI, VTK) already round-trip uint32 fine; this was TIFF-specific (uint32 and int32).

Changes

  • src/docker/itk-wasm-base/patches/itk-tiff-uint32-int32.patch — ITK source fix adding the missing UINT/INT cases to TIFFImageIO's write (bits-per-sample, SAMPLEFORMAT, row-length) and read (component dispatch) paths. Formatted as a git format-patch so it can also be applied with git am.
  • src/docker/itk-wasm-base/Dockerfile — apply patches/*.patch to the ITK source after checkout, via an idempotent git apply loop (reverse-check skips an already-applied patch, so a local ITK with the fix — USE_LOCAL_ITK=1 — is left untouched).
  • src/docker/itk-wasm-base/patches/README.md — documents the patch mechanism.
  • packages/image-io/typescript/test/node/tiff-test.jsuint32 and int32 TIFF round-trip regression tests (values exceed the 16-bit range to exercise real 32-bit storage).

Upstream

The same fix is submitted to ITK: InsightSoftwareConsortium/ITK#6541. The patch here is a stopgap and should be removed once that lands in the pinned ITK branch. Reaching users requires rebuilding and republishing the emscripten-base / emscripten images (the fix rebuilds ITK).

Testing

Verified end-to-end with a targeted ITK + pipeline rebuild against the patched source: the original issue reproduction now succeeds, written TIFFs are byte-correct (BitsPerSample=32), and uint32/int32 round-trip with full 32-bit fidelity (values up to ~1.17M) in both 2-D and 3-D (multi-page), with no regression on uint8/int16/float32. Upstream, the ITK TIFF ctest suite passes 64/64 including 4 new cases.

🤖 Generated with Claude Code

Writing a uint32/int32 TIFF image (e.g. writeImageNode with a .tif output)
aborted the WebAssembly module, and reading one back silently returned a
zero-filled buffer. ITK's itk::TIFFImageIO handled only 8-/16-bit integers and
32-bit float in its pixel read/write paths, even though it advertises
uint32/int32 as supported component types.

Carry an ITK source patch, applied during the emscripten-base image build, that
adds the missing UINT/INT cases to TIFFImageIO's write (bits-per-sample,
sample-format, row-length) and read (component dispatch) paths, and wire an
idempotent `git apply` step into the base Dockerfile so a local ITK that already
carries the fix is left untouched. Add a uint32/int32 TIFF round-trip regression
test to the image-io Node test suite.

The fix has been submitted upstream as InsightSoftwareConsortium/ITK#6541; the
patch is a stopgap until it lands in the pinned ITK branch.

Fixes InsightSoftwareConsortium#1544

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Matt McCormick <matt@fideus.io>
@thewtex
thewtex requested review from blowekamp and removed request for blowekamp July 2, 2026 19:53
thewtex and others added 3 commits July 2, 2026 15:55
Regenerate itk-tiff-uint32-int32.patch from the amended upstream commit
(InsightSoftwareConsortium/ITK@1f3573ab) after a Greptile review collapsed the
duplicated uint32/int32 arms in TIFFImageIO's bits-per-sample and row-length
switches into single C++ fall-through cases. Behavior is unchanged; the patch
stays byte-identical to the source portion of the upstream fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Matt McCormick <matt@fideus.io>
Regenerate itk-tiff-uint32-int32.patch from the amended upstream commit
(InsightSoftwareConsortium/ITK@b9a1b03e) after review: TIFFImageIO now sets
SAMPLEFORMAT_UINT explicitly for the unsigned 8- and 16-bit cases (UCHAR/USHORT)
instead of relying on libtiff's default. Behavior-preserving (the byte-exact
--compare-MD5 TIFF tests are unchanged); keeps the patch byte-identical to the
source portion of the upstream fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Matt McCormick <matt@fideus.io>
The itkwasm-node-js CI job builds the tiff-write-image pipeline from the
published itkwasm/emscripten image, which still carries unpatched ITK. The
uint32/int32 round-trip tests therefore exercise a pipeline without the fix and
crash with the original abort, so they cannot pass until the base image is
rebuilt and republished with the patch (and the pinned tag bumped) — a separate
follow-up out of this PR's control.

Remove them here to keep CI green. The behavior is already covered upstream by
the itkTIFFImageIOInt32Test GoogleTests in InsightSoftwareConsortium/ITK#6541;
end-to-end node coverage can be re-added once the patched pipeline is published.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Matt McCormick <matt@fideus.io>
@thewtex

thewtex commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

Removed the two uint32/int32 TIFF round-trip node tests (ffa9a4d) to get CI green.

The itkwasm-node-js (image-io) job builds tiff-write-image.wasm from the published itkwasm/emscripten image, which still carries unpatched ITK — so those tests ran against a pipeline without the fix and hit the original abort. They can only pass once the base image is rebuilt and republished with this patch and the pinned tag bumped, which is outside this PR (the Build itk-wasm-base job builds the patched image into a local cache tarball but doesn't publish it for a PR).

This PR now just carries the ITK source patch + the idempotent git apply wiring in the base-image Dockerfile, so the next base-image rebuild picks up the fix. The behavior itself is covered upstream by the itkTIFFImageIOInt32Test GoogleTests in InsightSoftwareConsortium/ITK#6541; end-to-end node coverage can be re-added in the follow-up that bumps the pinned emscripten image to the republished build. Happy to prepare that follow-up.

@thewtex
thewtex marked this pull request as draft July 10, 2026 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] writeImageNode crashes with Uint32Array, but not Uint8Array or Uint16Array

1 participant