What to build
Fix the omero.channels[].window block in every converted store to reflect the actual dtype range of the array, instead of the hardcoded {min: 0, max: 255, start: 0, end: 255} that leaks through from bioio-ome-zarr's Channel default.
Where the bug lives
bioio_ome_zarr.writers.Channel.__init__ sets:
self.window = window or {"min": 0, "max": 255, "start": 0, "end": 255}
Zarrmony constructs Channel(label=..., color=...) in two places without ever passing window=:
src/zarrmony/api.py::_lif_scene_channels (LIF path)
src/zarrmony/writers/scene.py::_default_channels (default path)
Every uint16 (or uint32, float32, …) store therefore ships with a display window clamped to the 8-bit range, which makes real-world microscopy data appear black on first open in napari/OMERO/any viewer that honors the omero block.
Fix
Compute (min, max) for the channel's array dtype and pass window= to Channel(...):
- Integer dtypes →
np.iinfo(dtype).min, np.iinfo(dtype).max
- Float dtypes →
0.0, 1.0 (OMERO convention for normalized floats; leave start/end matching)
Set start = min and end = max for this issue — data-driven start/end (percentile-based) is issue #TBD (linked separately) and can override them later without changing the shape of this fix.
Acceptance criteria
Blocked by
None — can start immediately.
What to build
Fix the
omero.channels[].windowblock in every converted store to reflect the actual dtype range of the array, instead of the hardcoded{min: 0, max: 255, start: 0, end: 255}that leaks through frombioio-ome-zarr'sChanneldefault.Where the bug lives
bioio_ome_zarr.writers.Channel.__init__sets:Zarrmony constructs
Channel(label=..., color=...)in two places without ever passingwindow=:src/zarrmony/api.py::_lif_scene_channels(LIF path)src/zarrmony/writers/scene.py::_default_channels(default path)Every uint16 (or uint32, float32, …) store therefore ships with a display window clamped to the 8-bit range, which makes real-world microscopy data appear black on first open in napari/OMERO/any viewer that honors the omero block.
Fix
Compute
(min, max)for the channel's array dtype and passwindow=toChannel(...):np.iinfo(dtype).min,np.iinfo(dtype).max0.0,1.0(OMERO convention for normalized floats; leavestart/endmatching)Set
start=minandend=maxfor this issue — data-drivenstart/end(percentile-based) is issue #TBD (linked separately) and can override them later without changing the shape of this fix.Acceptance criteria
omero.channels[i].windowreads{min: 0, max: 65535, start: 0, end: 65535}(verified on a real LIF conversion).{min: 0, max: 255, start: 0, end: 255}(regression guard)._lif_scene_channelsand_default_channels.zarr.json's omero window matches dtype range.Blocked by
None — can start immediately.