feat(omero): data-driven contrast (min, 99.9th pct) fused into pyramid write#67
Merged
Merged
Conversation
…d write (#53) Compute per-channel (min, percentile) on the coarsest pyramid level and write them into omero.channels[i].window.start / .end so uint16/uint32/ float fluorescence stores open with sensible auto-contrast in napari / OMERO on first click. The extra ops fuse into the same da.compute call as the pyramid writes so raw data is read once — no second read pass over 80+ GB LIF inputs. - New keyword: convert(..., contrast_percentile=99.9) — default runs the pass; pass a different float in (0, 100) to shift the tail, or None to skip and keep the dtype-range placeholder from issue #50. - CLI mirrors: --contrast-percentile FLOAT and --no-contrast. - Audit records the resolved percentile under config.contrast_percentile plus per_scene[i].contrast.per_channel[] bounds and the approximation method (coarsest-pyramid-level). - Backfills plate.py's _channels_for_current_scene to pass window=_dtype_window(reader.dtype), closing the #50 gap on plate FOVs. Co-Authored-By: Claude Opus 4.7 (1M context) <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.
Closes #53.
Summary
(min, percentile)on the coarsest pyramid level and write them intoomero.channels[i].window.start/.endso uint16/uint32/float fluorescence stores open with sensible auto-contrast in napari / OMERO on first click.da.computecall as the pyramid writes — raw data is read once, no second read pass over 80+ GB LIF inputs.convert(..., contrast_percentile=99.9)— default runs the pass; pass a float in(0, 100)to shift the tail, orNoneto skip and keep issue fix(omero): default window to array dtype range instead of hardcoded 0–255 #50's dtype-range placeholder.--contrast-percentile FLOATand--no-contrast.config.contrast_percentileplus per-channel bounds underper_scene[i].contrast.per_channel[]and the approximation method (coarsest-pyramid-level).writers/plate.py::_channels_for_current_sceneto passwindow=_dtype_window(reader.dtype), closing the fix(omero): default window to array dtype range instead of hardcoded 0–255 #50 gap on plate FOVs (which otherwise still ship the bioio-ome-zarr 0–255 default).Approximation choice
Percentile is computed on the coarsest pyramid level, not the base:
min_size × min_sizeper channel per Z — trivially cheap to sort or quantile even for 80+ GB inputs.per_scene[i].contrast.method = "coarsest-pyramid-level") so downstream consumers can distinguish exact vs approximate.Wall-clock overhead
Measured on the FakeReader-driven test scenes (32×32 uint16 base,
pyramid_min_size=8, 2 pyramid levels): contrast pass adds a handful of ms — the coarse level is 64 pixels per channel. On real 80 GB LIF inputs the extra cost is expected to fall well under the 10% target because the coarsest level's read is already scheduled by the pyramid build; the added work is a per-chunkminreduction plus a percentile over ≤ min_size² values.contrast_percentile=Nonereturns wall-clock to the pre-change baseline (verified by test — the extras list is empty).Test plan
window.start/.endwithin tolerance of expected coarse-level quantile.config.contrast_percentileandper_scene[i].contrastrecord the resolved value and method.contrast_percentile=Noneleaves the dtype-range window unchanged (issue fix(omero): default window to array dtype range instead of hardcoded 0–255 #50 behavior).contrast_percentile=None(they now test just the placeholder shape).Follow-ups
v0.9.0tag once merged (this + feat(lif): extract objective lens metadata (magnification, NA, immersion) into audit + OME-XML #52 + feat(channels): emission-band colorblind palette as default; source-file opt-in #51 + fix(omero): default window to array dtype range instead of hardcoded 0–255 #50 are all in Unreleased).🤖 Generated with Claude Code