Skip to content

RGB header override: rgb_header_override (Spec amendment RH1, P36) - #295

Draft
guijacquemet wants to merge 2 commits into
mainfrom
ccr-52991b66-5vnbgy
Draft

guijacquemet wants to merge 2 commits into
mainfrom
ccr-52991b66-5vnbgy

Conversation

@guijacquemet

Copy link
Copy Markdown
Contributor

Implements MorphoBricks-Spec amendment RH1 (Spec PR #37, commit 2cbebf8; owner decision of 2026-11-13, MorphoFeature #220 audit gap 1).

Why

Real scanner files store interleaved RGB under a header that does not say RGB. The Zenodo 4550300 brightfield cores are TIFF, photometric MINISBLACK, 3 samples per pixel, contiguous. A unit declared image_type = "rgb" was refused with R_IMAGE_TYPE_CONTRADICTS_HEADER, so such data could not be analysed without rewriting its pixels.

What changes

  • Description key rgb_header_override (boolean, on a technical-repeat unit):
    • a non-boolean value is E_TYPE (phase 3);
    • the key on a unit without image_type = "rgb" is the new E_RGB_OVERRIDE_WITHOUT_RGB (phase 5);
    • the canonical writer keeps it;
    • the notebook draft keeps it on re-save, and switching a unit to separate channels drops it.
  • Physical check (physical_check.py): for a unit declared rgb with the key, a header that says "not RGB" is accepted only when the image has exactly three interleaved samples (header only; pixels never examined). Samples 0, 1, 2 become R, G, B.
    • An image without three interleaved samples is the new R_RGB_OVERRIDE_NOT_THREE_SAMPLES. It is reported first for that unit, so a mixed unit gives this code rather than R_UNIT_NOT_HOMOGENEOUS.
    • A unit with the key whose header already says RGB is accepted as header-verified.
    • Without the key, the contradiction is still R_IMAGE_TYPE_CONTRADICTS_HEADER, and for three interleaved samples the message says to declare rgb_header_override = true.
    • OME-Zarr is unchanged.
  • Header reading: the new io module tiff_colour.py reads rgb_samples, photometric and interleaved_samples; _rgb_samples moved there from metadata.py. TiffMetadata gains photometric and interleaved_samples.
  • Manifest: an overridden unit's record gains image_type_header_overridden: true and header_photometric (e.g. "minisblack"; several values sorted and joined), and image_type_header_verified is false for it. Every other unit's record keeps exactly its keys.
  • Reconciliation: ObservedUnit.three_samples keeps an accepted override from counting as a changed unit. Adding or removing the key reopens no repeat confirmation, because it changes no channel ID.
  • Test fixtures: testing_described writes overridden units as MINISBLACK 3-sample images.
  • Docs: AGENTS.md, CHANGELOG, docs/contract.md, docs/user_guide.md.

Not changed / deliberate

  • Spec pin not moved: Differing channel names across images are only a warning (channels pooled by index) #37 is unmerged. Fixtures v40/i140/i141 are rebuilt inline in tests/test_rgb_header_override.py: v40 and i141 are the vendored v28 plus exactly the Spec's added lines, and i140 is copied.
    • test_spec_rules_table.py now allows exactly the new field and code ahead of the pin. That allowance fails once the pin contains them, which forces removing it at the re-pin.
  • Notebook: no control for the key in this PR; the key is kept on re-save.

Tests and checks

  • New tests: tests/test_rgb_header_override.py (23) cover P36 (a)–(d) on real tifffile-written files, plus E_TYPE, E_RGB_OVERRIDE_WITHOUT_RGB, the message naming the key, the manifest keys, and channels R, G, B in stored order through RunInputs.
  • Full check: python tools/check.py: Ruff and format pass, code rules pass (one baseline entry lowered, 649 → 646), 5516 passed, 4 skipped. The 4 skips are the same environment skips as on unmodified main (5493 passed, 4 skipped).
  • Manual check on a real Zenodo 4550300 file (1B_A3_BF.tif, not committed):
    • without the key: refused, and the message names the key;
    • with the key: the run is written, RunInputs gives channels ('R','G','B'), and read_plane equals stored samples 0, 1, 2 at (3, 2048, 2048).

Merge after Spec #37. The bricks re-pin the Spec and preprocessing in the next pin wave. The owner reviews before merge.


Generated by Claude Code

Scanner files store interleaved RGB under a header that is not RGB (TIFF
photometric MINISBLACK, SamplesPerPixel 3, contiguous), so a unit declared
rgb was refused with R_IMAGE_TYPE_CONTRADICTS_HEADER. Implements
MorphoBricks-Spec PR #37 (commit 2cbebf8, experiment-v1 4.4b):

- Reader: optional unit key rgb_header_override (boolean; E_TYPE otherwise,
  phase 3); present without image_type = "rgb" is E_RGB_OVERRIDE_WITHOUT_RGB
  (phase 5). Model (Unit.rgb_header_override), canonical writer and round
  trip keep it as written.
- Headers: TiffMetadata.photometric and interleaved_samples (new
  tiff_colour.py; _rgb_samples moved there from metadata.py). Pixels are
  never examined.
- Physical check: with the key, a header that does not say RGB is accepted
  only when every image has exactly three interleaved samples, else
  R_RGB_OVERRIDE_NOT_THREE_SAMPLES (reported first for the unit). A header
  that already says RGB stays header-verified. Without the key the refusal
  stays and names rgb_header_override when the images have three
  interleaved samples. The reverse direction and OME-Zarr are unchanged.
- Manifest: an overridden unit's record adds image_type_header_overridden:
  true and header_photometric (e.g. "minisblack"); its
  image_type_header_verified is false. Other units' records are unchanged.
- Reconciliation: an accepted override is not a changed unit.
- Notebook: no control for the key yet; the draft (format, answers,
  published answers) keeps the key when a description is saved again, and
  changing a unit to separate channels drops it.
- testing_described writes MINISBLACK three-sample images for overridden
  units.

The Spec pin is not moved (2cbebf8 is not merged): fixtures v40, i140,
i141 are not vendored; tests/test_rgb_header_override.py holds equivalent
descriptions inline (v40, i141 built from the vendored v28 with exactly the
Spec's added lines; i140 copied). test_spec_rules_table.py lists the RH1
field and code as ahead of the pin and fails once the pin holds them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VbytvF3Gsa85ix8Ne2yMDW
…rs B1-B5)

Implements MorphoBricks-Spec PR #37 as amended at commit 179dc5c (experiment-v1
4.4b, 8, 10; conformance P36 (d)-(h); transitions t14, t15):

- B2 homogeneity on the effective type: after the three-sample check, every
  image the override lifts (a TIFF header that does not say RGB, three
  interleaved samples) is read as RGB (physical_check.layout_of(rgb=...),
  _check_unit), so a unit whose headers differ only in colour interpretation
  is homogeneous. reconcile_observe marks such a unit uniform_as_rgb and
  description_reconcile accepts it under a declared override, as the run does.
- B3: the three-sample requirement applies to the lifted images only; a header
  already RGB or a format without colour interpretation (OME-Zarr) leaves the
  key declared but unused. R_RGB_OVERRIDE_NOT_THREE_SAMPLES stays reported
  before R_UNIT_NOT_HOMOGENEOUS.
- New pure module rgb_override.py (declares_override, lifted, effectively_rgb,
  override_uses, header_overridden).
- B1: description_transition.repeat_layout and published_answers._layout
  include rgb_header_override as written: adding, removing or changing it
  reopens the N35 confirmation, as the Spec reference validator compares it.
- B5: unit records of units declared rgb add rgb_header_override (declared,
  true/false); units declaring it true add rgb_header_override_images (per
  image: override used/unused, header_photometric, unused_reason
  header_already_rgb or no_colour_interpretation). Existing effective fields
  are kept; the new keys are additive, checked when read
  (recorded_description); manifest version stays 5 (precedent #225).
- B4: tests assert the literal codes; the inline-description test is renamed
  for what it checks, i140 is now the Spec file's text, and new tests cover
  P36 (d)-(h) and t14/t15 built locally (the Spec is not re-vendored).

Rebased onto main 3aa3b25 (v0.5.3); the RH1 changelog entry moved to
Unreleased.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VbytvF3Gsa85ix8Ne2yMDW
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.

2 participants