Repository navigation
feat(virtualize): sample the omero windows of TIFF and QPTIFF images from the coarsest level - #98
Merged
Merged
Conversation
…from the coarsest level The `omero` channel windows of the TIFF and QPTIFF profiles gave each integer channel its data type's whole range, which leaves a dim 12- or 16-bit image black, and gave a float image no `omero` (TIFF) or no window (QPTIFF float32 component data), which ome-zarr-py rejects. Each channel's window is now sampled from the data, as refkv's demo samples a contrast range: the middle chunk of the coarsest level, one per channel, is decoded when the core decodes its compression (none, Deflate, LZW; not JPEG, JPEG 2000 or zstd), and `start`/`end` are the 5th and 99.9th percentiles of the channel's values inside the array, leaving out the fill value 0 (and non-finite floats). `min`/`max` stay the integer type's range, or are the float values' extremes, so a float TIFF now has a valid `omero`. RGB samples (SVS, NDPI, RGB TIFF and QPTIFF) keep the type's full range, since a window per sample would tint them; an absent or undecodable chunk, or one without contrast, falls back to the type's range, or to no window (no `omero` for a float TIFF). The projections still read nothing: `project::reads` gives the TIFF and QPTIFF sampled chunks' source ranges (from the coarsest level's references alone), which the run reads after the parser as it reads NIfTI's and MRC's copied chunks, and project/window.rs decodes them, so the Python and wasm hosts give the same windows. lzw.rs gains a decoder that reads into the chunk's size, as the QPTIFF codec does. spec/virtualize/tiff.md (§3.1, §4.4) and spec/virtualize/qptiff.md (§4.3, §16) record the rule with dated notes, and the format READMEs describe it. The Python tests check every TIFF and QPTIFF fixture's windows against numpy over the coarsest level read through zarr-python, and each fallback on its own; every accepted TIFF, NDPI and QPTIFF fixture's `omero` validates with ome_zarr_models' Omero. Assisted-by: ClaudeCode:claude-opus-5-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 AI text below 🤖
The
omerochannel windows of the TIFF and QPTIFF profiles are now sampled from the middle chunk of each channel at the coarsest level, as refkv's demo samples a contrast range, instead of only covering the data type's whole range. Float images get a validomerotoo: a float TIFF used to have none, and QPTIFF float32 channels had no window, which ome-zarr-py rejects.Rule (spec/virtualize/tiff.md §4.4, spec/virtualize/qptiff.md §4.3, each with a dated change note)
miniz_oxide) or LZW (a new size-bounded decoder inlzw.rs). JPEG, JPEG 2000 and zstd are not decoded.t= 0, at the channel's chunk alongc, and at the chunk holdingfloor(n / 2)along every other axis. Only its values inside the array are used, so an edge tile's padding and a padded last strip are left out. The fill value 0 is dropped, and so are non-finite floats.start/endare the values at indicesfloor(m·5/100)andmin(m−1, floor(m·999/1000)). This is integer arithmetic, so it is the same on wasm32.min/maxare the integer type's range, or the extremes of the sampled float values.omeroat all for a float TIFF): an absent chunk, a chunk that doesn't decode, one over 2^26 bytes, and one with no contrast.How it reads: projections still read nothing themselves.
project::readsreturns the source ranges of the sampled chunks, taken from the references of the coarsest level only. The run reads them after the parser, the same way NIfTI and MRC edge chunks are read, andproject/window.rsdecodes them. The Python and wasm hosts therefore produce identical output, and conformance comparespywithwebentry for entry.Tests
test_omero_windows_sample_the_coarsest_levelintest_tiff.pyandtest_qptiff.pyruns over every fixture. It checks the windows against numpy applied to the coarsest level, read through zarr-python (ir_helpers.omero_and_expected_windows).omeroof all 43 accepted TIFF, NDPI and QPTIFF fixtures validates withome_zarr_models.common.omero.Omero.model_validate.Checks run
cargo test --release --manifest-path rust/refkv-ir/Cargo.tomluv run pytest -q python/tests: 2050 passed, 2 skippedjust js::test: 638 passedjust sectionsconformance/virtualize/compare.py <out> --fixtures fixtures/{tiff,ndpi,qptiff}: 0 divergencesStack notes
Builds on #95 (merged as 7d051e3), which added
type_windowandsample_channels.sample_channelsnow takes one window per sample. The frozen reference comparisons already ignoreomerofor tiff and ndpi (ADDED), so no reference changes are needed.🤖 Generated with Claude Code