Skip to content

Notebook: folder pictures with a 1:1 crop, tag like the saved group, colour mask evidence (PR 17c) - #250

Merged
guijacquemet merged 3 commits into
mainfrom
claude/notebook-17c
Nov 9, 2026
Merged

guijacquemet merged 3 commits into
mainfrom
claude/notebook-17c

Conversation

@guijacquemet

Copy link
Copy Markdown
Contributor

PR 17c of the notebook UX re-evaluation (owner decisions 2026-11-09). It is based on main at 45241f1, which has 17a #244, 17b-1 #247, 17b-2 #248 and #246. The diff is 24 files, +1263/−40, so it is one PR and not split.

Summary (by module)

  • header_preview (new, io): a picture of a folder's first image by path, drawn from its header before the axes are confirmed. It is display only: nothing it reads feeds an answer, an axis role, the draft or the run.
    • It is drawn only when the header names Y and X (metadata.named_axes). A plain stack (QYX, IYX) gets "preview after Axes (step 2)", and no axis is guessed.
    • Every other axis is max-projected, and the caption names it with the header's letter and length: "max projection over Z (12): 8 of 12 planes, evenly spread (a preview here reads at most 8 planes and 16,777,216 pixels)". An RGB header (rgb_samples, S = 3) is drawn in colour with the existing colour_composite.
    • Bounded reads:
      • at most PLANE_CAP = 8 planes per picture, evenly spread, and at most PIXEL_CAP = 4096 × 4096 pixels per plane and per picture;
      • each plane is read whole within preview.READ_BUDGET (64 MiB) and is never read in blocks here;
      • a larger plane, or one that only reads in blocks (a big OME-Zarr level 0 included), is not read. Its cell states the cap and says "preview after Axes (step 2)".
    • A read failure is a sentence in the cell ("cannot be read for a preview: …") and never stops the check.
    • np.maximum keeps a non-finite pixel non-finite, so it is shown magenta as in the existing pictures.
    • The reading uses planes.read_plane with a layout made from the header letters. Its Z/T/C slots only say which dimension to step through: all of them are projected, and no slot is shown or used as a role.
  • preview_crop (new, pure): tile_pictures returns the thumbnail and, when the downscale factor is over 2, a crop of 128 native pixels, drawn 2×2 with nearest neighbour.
    • Location: the 128-pixel square of the native grid (whole squares from the top left) whose thumbnail pixels have the highest standard deviation, on the displayed thumbnail. For colour it uses the mean of R, G and B.
    • Tie-break: the first maximum in row-major order, which is the topmost square, then the leftmost.
    • The crop is drawn with the thumbnail's contrast: display_panel gained an optional limits. Its caption gives the x/y range.
    • The planes are only read, never changed.
  • notebook_thumb_tile (new, ui): one shared ThumbTile holds the thumbnail, the crop beside it (image-rendering: pixelated), the captions and Enlarge. show_detail is the enlarged view with Close, and zoom_button is now shared with the tag grid's existing Enlarge.
    • Used on Images found, on Replicates (the pairing pictures) and on Check and save (the replicate blocks).
    • For the last two, Thumbnails.tile reads the same plane at native pixels, through the confirmed roles. That read is not cached, and only the PNGs are kept. A plane that read_plane would reduce gets no crop, and the caption says why. Thumbnails.enlarged puts the 1024 px picture in the enlarged view.
  • notebook_folder_previews (new, ui), the labels table with a Preview column on Images found. This is the described run only, so embedders' folder_step is unchanged.
    • The caption says "from the file header, axes not confirmed yet". Enlarge reads the picture again at 1024 px.
    • The check draws the first 12 folders. The others are one HTML block with Draw the pictures of the other N folders under it, which keeps Check folder quick (timings below).
  • notebook_channels_grid.TagGrid, Tag like the saved group (…):
    • When it is offered: on a reopen, to a block of new, untagged folders whose header is exactly a saved block's. The block key rule is the same as Notebook: tags per header block, colour pictures, one-click axes (PR 17b-1) #247: signature, colour flag and no split.
    • The saved block: its folders are unchanged since the save and fully tagged.
    • Never offered:
      • for a header without every channel name;
      • for a colour block;
      • for a folder taken out with Tag this folder on its own.
    • The click: tag_like applies with_tags to every folder of the block and saves once, which writes the same draft bytes as choosing the tags by hand. It only ever happens on the click.
  • Masks: MaskEvidence.in_colour() draws the evidence of an RGB folder (answered RGB, or pre-ticked from the header) on its colour picture. The overlay and the mask are unchanged. The caption says "in colour (channel 1 red, 2 green, 3 blue)". A plane that cannot be drawn in colour stays on channel 1, and the caption says why, "(not in colour: …)", so it never falls back silently.
  • Docs: CHANGELOG, the AGENTS.md map and the user guide. pyproject declares header_preview (io), preview_crop (pure), notebook_thumb_tile (ui) and notebook_folder_previews (ui). tools/code_rules.py and the baseline are untouched.

Decisions / for the owner

  • Clicking the picture itself is not possible with an ipywidgets Image, which has no click event in 7.7 or 8. The tile uses the existing Enlarge button under the picture, as the tag grid does.
  • The check draws only the first 12 folders' pictures. A bigger experiment gets one button for the rest. It is stated in the UI.
  • Max projection over C: for a multi-channel fluorescence header, every channel is max-projected into one grey picture, following "other axes are max-projected". It mixes the stains, and the caption says "max projection over C (3)".
  • The crop is chosen on the displayed thumbnail (8-bit, after each channel's contrast), not on the raw values. This makes colour and grey use the same rule.

Notes for review

  • Not verified in a live JupyterLab or Colab. The pages were rendered with embed_minimal_html in Playwright chromium (1180 px, deviceScaleFactor 2). Standalone renders lack JupyterLab styles and icons.
  • There is no pyramid-level read: a large OME-Zarr image falls back to the stated "too large … preview after Axes (step 2)".
  • The rows of the HTML block for undrawn folders are about 2 px off the widget rows' alignment.

Timings (Check folder, described run, median of 5, this container)

Synthetic experiment Without pictures With pictures Draw the others (click)
200 folders × 5 images, 256 × 256 CYX 1.24 s 1.46 s (+0.22 s) 1.44 s
32 folders × 2 images, 2048 × 2048 0.47 s 0.64 s (+0.16 s) 0.21 s

Before the cap, drawing every folder added 1.3 s to the 200-folder check, about 4.7 ms of reading and 3.8 ms of widgets per folder. That is why the cap is there.

Tests

  • tests/test_notebook_thumbnails.py (11):

    • the crop is the busiest square at native pixels, nearest-neighbour, with the thumbnail's contrast, and the source array is unchanged;
    • the tie-break (topmost, then leftmost) is the same on every run;
    • no crop at factor 2, and a colour crop;
    • a picture is drawn only with Y/X in the header;
    • the plane cap (8 of 20, evenly spread, a read spy) leaves the file bytes unchanged;
    • the pixel cap: no read, and the cap is stated;
    • RGB in colour, and a truncated file gives a sentence;
    • Images found rows, Enlarge, "preview after Axes";
    • the pictures feed nothing (mapping and draft equal with and without them);
    • the first N folders are drawn, and the rest on a click.
  • tests/test_notebook_tag_like.py (4):

    • draft bytes equal for "Tag like the saved group" and for tagging by hand, and nothing is copied before the click;
    • a nameless header is never offered;
    • dapi ≠ DAPI is not offered;
    • the mask evidence of an RGB folder is in colour (PNG pixel check), and so is its overlay.
  • Updated: test_notebook_check_save.py (the picture is now a tile). tests/channel_images.py gains large() (fine fibres on broad cells).

  • python tools/check.py (Python 3.13, .[dev,notebook,zarr]): ruff, format and code rules pass; 5003 passed, 4 skipped. The skips are environment-dependent: a case-sensitive file system ×3 and running as root ×1.

  • Screenshots (15) from tests/channel_images.py images:

    • a 2048 × 2048 image, with a crop;
    • an RGB set with masks;
    • a plain stack without Y/X;
    • a Z stack;
    • a reopen with two new same-header folders;
    • 16 folders.

    Each one was looked at, and two problems were fixed after looking: Enlarge buttons collapsed by the scrolling table, and tiles stretching across Check and save. The crop shows the one-pixel fibres that the 8× thumbnail loses.


Generated by Claude Code

…colour mask evidence (PR 17c)

- Images found: a picture of each folder's first image from its header (Y and X named only,
  other axes max-projected and named, colour for RGB, capped reads, display only); the first
  12 folders drawn by the check, the others on a click.
- A shared thumbnail tile with a crop at native pixels where the thumbnail varies most
  (deterministic), and Enlarge; used on Images found, Replicates and Check and save.
- Reopen: "Tag like the saved group" for new folders whose header is exactly a saved block's;
  the click writes the draft bytes of tagging by hand.
- Masks: the evidence of an RGB folder is drawn in colour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
- The crop says why it is missing for an image under 128 pixels on a side
  or a thumbnail with too few pixels per square, instead of "varies too
  little"; a thumbnail that varies nowhere names the first square rather
  than "the square that varies most".
- "Draw the pictures of the other 1 folder" (plural helper).
- Regression tests: the same channel names in another order and a folder
  taken out on its own are not offered "Tag like the saved group"; a new
  Check folder of another folder shows no picture of the last one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
@guijacquemet
guijacquemet merged commit bf733ee into main Nov 9, 2026
7 checks passed
@guijacquemet
guijacquemet deleted the claude/notebook-17c branch November 9, 2026 12:29
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