Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
540ac52
feat(track): write labels and tracks.geff into the assembled plate
ieivanov Oct 8, 2026
d8e06a0
build(deps): repin iohub dev/dca-labels after #485 drops its legacy l…
ieivanov Oct 8, 2026
02e5f63
build(deps): repin iohub dev/dca-labels after iohub#486 label-default…
ieivanov Oct 8, 2026
f863ad2
feat(track): keep no Ultrack databases by default
ieivanov Oct 9, 2026
662e53f
build(deps): pin iohub to iohub#486 now that #485 is on main
ieivanov Oct 9, 2026
8d473e8
build(deps): repin iohub#486 after per-position label defaults fix
ieivanov Oct 9, 2026
0c35375
build(deps): repin iohub#486 after review fixes
ieivanov Oct 9, 2026
cee19fb
build(deps): pin ultrack to royerlab main now that #288 and #289 merged
ieivanov Oct 16, 2026
324ff4d
refactor(track): drop the tempfile redirect now that ultrack#289 merged
ieivanov Oct 16, 2026
cbeedd8
build(deps): pin iohub to main now that #486 merged
ieivanov Oct 16, 2026
a98a267
feat(project)!: biahub project CLI; segment and track no longer project
ieivanov Oct 17, 2026
1c82b7f
feat(nextflow): A549 project.yml projects every assembled channel; sk…
ieivanov Oct 19, 2026
a8c6d1b
fix(project): float32 plate for any max/mean/sum channel; drop float6…
ieivanov Oct 19, 2026
1b240c2
refactor(project): focus record keys are z and z_per_t
ieivanov Oct 19, 2026
67df18d
refactor(project)!: z selection is start/stop or center with below/above
ieivanov Oct 19, 2026
1acc892
docs(nextflow): cleanup never targets the projected plate
ieivanov Oct 19, 2026
bcac86d
feat(project): chunks_czyx and shards_ratio; A549 writes 16-timepoint…
ieivanov Oct 19, 2026
dde9e93
perf(project): find the focus of the timepoints in a process pool
ieivanov Oct 19, 2026
7cadafd
feat(nextflow): projected plate lives beside the assembled one as <ds…
ieivanov Oct 19, 2026
c8f8e78
Preserve focus metadata
ieivanov Oct 20, 2026
f06223b
Validate existing output layout before reuse
ieivanov Oct 20, 2026
6e5b5e4
Include channel order in settings fingerprint
ieivanov Oct 20, 2026
0e504d5
fix(project): size RAM by the channels read
ieivanov Oct 20, 2026
1bf0071
test(project): focus record carries the focus settings
ieivanov Oct 20, 2026
88f363e
fix(project): more RAM headroom per worker, calibrated on an A549 pos…
ieivanov Oct 20, 2026
5a35ef6
Merge feat/project-cli: labels and tracks.geff go into the plate trac…
ieivanov Oct 20, 2026
af5ec1f
build(deps): ultrack pin follows royerlab/ultrack#290's pyupgrade fix
ieivanov Oct 20, 2026
a4f5398
Merge remote-tracking branch 'origin/main' into feat/project-cli
ieivanov Oct 21, 2026
a4f4efa
Merge branch 'feat/project-cli' into feat/track-labels-geff
ieivanov Oct 21, 2026
108724a
feat(track)!: cellpose is the default segmentation method
ieivanov Oct 21, 2026
16063a1
feat(pyramid): pyramids on images and label images; mantis-v2 adds th…
ieivanov Oct 21, 2026
c8d2b60
feat(nextflow): QC is the last step; pyramids run after tracking and …
ieivanov Oct 21, 2026
936c147
build(deps): iohub pin follows the #488 review fixes (label-safe defa…
ieivanov Oct 21, 2026
098b16f
build(deps): iohub pin follows the second #488 review round
ieivanov Oct 21, 2026
dccb1f8
build(deps): iohub pin follows the third #488 review round
ieivanov Oct 21, 2026
1b5b175
feat(segment,track)!: segmentation moves out of track into the plate
ieivanov Oct 21, 2026
2666758
refactor(segment,track): scaffold labels with iohub's create_empty_la…
ieivanov Oct 21, 2026
8025a44
build(deps): iohub pin moves to the #461 branch (pyramid levels keep …
ieivanov Oct 21, 2026
f1df4fc
feat(settings)!: shards replaces shards_ratio in the concatenate and …
ieivanov Oct 21, 2026
640e7ba
Merge feat/track-labels-geff: segment writes the labels, track links …
ieivanov Oct 21, 2026
53b8f36
feat(track): the tracked label image takes the source label's dtype a…
ieivanov Oct 21, 2026
ed7be79
feat(segment,track)!: segment writes only into the plate; track's out…
ieivanov Oct 21, 2026
bebe628
build(configs): concatenate chunks 1 x 8 x 256 x 256; iohub pin keeps…
ieivanov Oct 21, 2026
7a4de6c
feat(track)!: the tracked label image is labels/tracks_<labels>
ieivanov Oct 21, 2026
0ccd0ee
Merge remote-tracking branch 'origin/main' into feat/project-cli
ieivanov Oct 21, 2026
0512e9f
Merge branch 'feat/project-cli' into feat/track-labels-geff
ieivanov Oct 21, 2026
8547550
Merge branch 'feat/track-labels-geff' into feat/pyramid-labels
ieivanov Oct 21, 2026
3d70068
fix(pyramid): size the task from the level-0 shard, measured on an A5…
ieivanov Oct 21, 2026
f1966b2
fix(pyramid): double a retried task's memory after an OOM kill
ieivanov Oct 22, 2026
a84a480
fix(project): double a retried task's memory after an OOM kill
ieivanov Oct 22, 2026
466d727
fix(pyramid): request 14x the shard and 0.3 min per volume, from v9's…
ieivanov Oct 23, 2026
c407462
fix(project): max keeps the input dtype; only mean/sum make the plate…
ieivanov Oct 23, 2026
3b689f9
Merge remote-tracking branch 'origin/main' into feat/project-cli
ieivanov Oct 23, 2026
fafdec8
Merge commit '3b689f99' into feat/track-labels-geff
ieivanov Oct 23, 2026
48e95a2
build(deps): pin iohub to main at the #491 merge (80b20a6)
ieivanov Oct 23, 2026
67e7156
Merge branch 'feat/track-labels-geff' into feat/pyramid-labels
ieivanov Oct 23, 2026
2214e79
Merge remote-tracking branch 'origin/main' into feat/track-labels-geff
ieivanov Oct 23, 2026
1be37b0
Merge branch 'feat/track-labels-geff' into feat/pyramid-labels
ieivanov Oct 23, 2026
d3cb469
feat(segment,track)!: label images are (T, 1, Z, Y, X)
ieivanov Oct 23, 2026
ee6f1a1
build(deps): pin iohub to czbiohub-sf/iohub#496 (labels are (T, 1, Z,…
ieivanov Oct 23, 2026
ca5fdee
build(deps): iohub pin follows czbiohub-sf/iohub#496's review fixes (…
ieivanov Oct 23, 2026
fe09c0d
build(deps): iohub pin follows czbiohub-sf/iohub#496 (eee7567)
ieivanov Oct 24, 2026
d570380
build(deps): pin iohub to main at the #496 merge (0cb3e35)
ieivanov Oct 24, 2026
0bc9f45
refactor(labels): size label shards with iohub's 200 MB target
ieivanov Oct 24, 2026
6d9d19a
build(deps): the iohub override follows the pin (0cb3e35)
ieivanov Oct 24, 2026
8a92abb
Merge feat/track-labels-geff: label images are (T, 1, Z, Y, X); iohub…
ieivanov Oct 24, 2026
d2b0992
style: ruff format
ieivanov Oct 24, 2026
04d6882
Merge remote-tracking branch 'origin/main' into feat/pyramid-labels
ieivanov Oct 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions .claude/skills/reconstruct-with-nextflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ name: reconstruct-with-nextflow
description: >-
Run a mantis-v2 microscope dataset through the biahub Nextflow reconstruction
pipeline (nextflow/mantis-v2.nf: flat-field → deskew → reconstruct →
virtual-stain → assemble → project → segment → track → QC) on the Bruno HPC
cluster. Assemble, project, segment, track and QC are optional and selected per
virtual-stain → assemble → project → segment → track → pyramid → QC) on the Bruno
HPC cluster. Assemble, project, segment, track and QC are optional and selected per
dataset family: A549 runs all of them, neuromast runs assemble + QC and no
segmentation or tracking. Locates the raw
segmentation or tracking; pyramids are on whenever assemble runs. Locates the raw
acquisition under /hpc/instruments/cm.mantis, picks the output project
directory, scaffolds configs, launches the run in a tmux session in the
foreground so it can be watched, monitors it unattended every 30 minutes
Expand Down Expand Up @@ -358,8 +358,14 @@ Do not run anything yet. Show the user:

| family | steps |
|---|---|
| A549 / cell-line / organelle | flat-field → deskew → reconstruct → virtual-stain → **assemble → project → segment → track → QC** |
| neuromast / zebrafish / dynatrack | flat-field → deskew → reconstruct → virtual-stain → **assemble → QC** (no projection, segmentation or tracking) |
| A549 / cell-line / organelle | flat-field → deskew → reconstruct → virtual-stain → **assemble → project → segment → track → pyramid → QC** |
| neuromast / zebrafish / dynatrack | flat-field → deskew → reconstruct → virtual-stain → **assemble → pyramid → QC** (no projection, segmentation or tracking) |

Pyramid is on by default whenever assemble runs: it adds two coarser levels
(3 in total, DCA v0.2) to every image and label image of the assembled and
projected plates, in place, once segmentation and tracking have finished
writing into them. QC is the very last step and waits for the pyramids.
`--pyramid_levels 1` skips them.

Write the list out in the plan rather than naming the family, and say which
optional steps are being skipped and why. If the user asks to skip anything
Expand Down
48 changes: 31 additions & 17 deletions .claude/skills/reconstruct-with-nextflow/references/caveats.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,30 +265,30 @@ Assemble is one large SLURM job — the single longest step.
`biahub concatenate --resume` (passed by the Nextflow task) makes a late kill
recoverable per (t, c) unit.

## 7. Shard geometry and `shards_ratio`

`shards_ratio` is a ratio over the **chunk** shape
(`shards = chunks * shards_ratio`, elementwise TCZYX), not an absolute shard
shape — changing T without recomputing ZYX silently changes the spatial extent.
Larger T shards mean a larger RAM request and a worse blast radius when one
tears; leave the T ratio alone without a measured reason. Channels cannot be
sharded — keep C at 1. The same two keys exist in `project.yml`: the A549
template uses one-frame `256 x 256` chunks (the assembled and tracking grid)
and `shards_ratio: [16, 1, 1, 7, 5]`, 16-timepoint full-frame shards; its YX
entries are `ceil(Y/256), ceil(X/256)` and must be recomputed for a different
frame size, exactly as for `concatenate.yml`. Auto-sizing is
[iohub#458](https://github.com/czbiohub-sf/iohub/issues/458); most of this
section goes away when it lands.

**A T ratio above 1 needs [iohub#460](https://github.com/czbiohub-sf/iohub/pull/460),
## 7. Shard geometry and `shards`

`shards` in `concatenate.yml` and `project.yml` is a **size target**
(`shards: 5GB`, `shards: 200MB`): iohub builds the largest chunk-aligned shard
under it, filling X, Y and Z first and then timepoints, and never spans
channels ([iohub#461](https://github.com/czbiohub-sf/iohub/pull/461)). Nothing
is recomputed per dataset: the same `5GB` gives 5 timepoints by the full volume
on the A549 plate and 9 on the dynatrack one. `"XY"` / `"XYZ"` (one shard per
plane / volume) and an explicit TCZYX shape in array elements are also accepted.
The old `shards_ratio` (a multiplier over the chunk shape) is refused with a
message naming the new key. A larger target means more timepoints per shard,
hence a larger RAM request (concatenate reads the T extent back from the plate it
initialised to size its workers) and a worse blast radius when one tears; leave
the templates' values alone without a measured reason.

**Shards spanning several timepoints need [iohub#460](https://github.com/czbiohub-sf/iohub/pull/460),
which shipped in iohub 0.3.11** — the floor `pyproject.toml` already requires, so a
synced checkout has it.

Before that fix, `concatenate` aborted with a message that points nowhere near the
cause — `numpy ... Unable to allocate 651. TiB for an array with shape
(1048576, 86, 1664, 1193)`, whose first axis is not T, not a shard, not
anything. It is **not** evidence that the shard buffer is too large, and
unsetting `shards_ratio` "fixes" it for the wrong reason. What happens: a
unsetting `shards` "fixes" it for the wrong reason. What happens: a
shard-aligned batch writes a whole shard's worth of timepoints in one call,
iohub drops the blank ones (`Skipping t=1, c=0 due to all zeros or nans` in the
step's `.command.out`), and the surviving indices are left with a *gap* — which
Expand Down Expand Up @@ -440,3 +440,17 @@ Consequences worth knowing:
differ" means nothing here; `corr` and max abs diff are the measures that do. A
real virtual-stain regression moves the distribution, not the last bit. Deskew,
reconstruct and assemble are fully deterministic.

## 15. Pyramids follow tracking and precede QC, and are not a resume unit

`biahub pyramid` rewrites each position's `zarr.json` (the multiscales list and
its `biahub-pyramid` record), as do tracking and imaging-qc, so the pipeline
runs it after tracking and holds QC, the very last step, until it has finished. A run that fails after the pyramid step
left the plates with 3 levels; a relaunch with `-resume` reuses the cached
pyramid tasks, and a relaunch that recomputes earlier steps rewrites level 0
only — rerun `biahub pyramid -i <plate>/*/*/* --levels 3` by hand in that
case, since the coarser levels are not recomputed by anything else. A plate
that already carries a pyramid of another depth is refused at init
(`already has a pyramid of a different depth`); the fix is iohub's
`delete_pyramid` on each position or matching `--pyramid_levels`.

Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ unset CLAUDECODE
# --qc_track_config was removed: qc_project.yaml counts the labels tracking writes
# into the projected plate, so passing the old flag errors at launch.
#
# Pyramids are not a flag here: the assembled and projected plates get 3
# resolution levels (images mean-pooled, labels max-pooled; DCA v0.2, biahub#359)
# after tracking by default, and QC runs last, after them. Add
# `--pyramid_levels 1` to skip them.
#
# TO SKIP A STEP, DELETE ITS LINE. Do not comment it out: `#` inside a
# backslash-continued command does not start a comment line — the continuation
# swallows it, every flag below it is dropped including `-resume`, and bash then
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ Some steps include an `estimate-*` command that writes a YAML of parameters you
| | |
| `flat-field` | Apply flat-field correction to selected channels |
| `flip` | Flip images in a dataset |
| `pyramid` | Create multiscale pyramid levels |
| `pyramid` | Compute multiscale pyramids of images and label images, in place |
| `process-with-config` | Apply arbitrary YAML-defined functions to a dataset |
| | |
| `estimate-deskew` | Estimate deskewing parameters |
Expand Down
2 changes: 1 addition & 1 deletion biahub/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def format_options(self, ctx, formatter):
{
"name": "pyramid",
"import_path": "biahub.pyramid.pyramid_cli",
"help": "Create pyramid levels for a dataset",
"help": "Compute multiscale pyramids of a plate's images and label images, in place",
},
{
"name": "apply-transform",
Expand Down
9 changes: 7 additions & 2 deletions biahub/concatenate.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ def _resolve_concatenate_inputs(
output_metadata = {
"shape": (len(input_time_indices), len(all_channel_names)) + tuple(cropped_shape_zyx),
"chunks": chunk_size,
"shards_ratio": settings.shards_ratio,
"shards": settings.shards,
"version": resolve_ome_zarr_version(
all_data_paths[0], settings.output_ome_zarr_version
),
Expand Down Expand Up @@ -793,7 +793,12 @@ def concatenate(
# 16-worker tasks took 7-26 min; 0.15 min/volume budgets 60 min here.
T_out, C_out, _, _, _ = prep["output_metadata"]["shape"]
_, _, Z, Y, X = prep["shape"]
batch_size = settings.shards_ratio[0] if settings.shards_ratio else 1
# iohub writes one shard's timepoints per unit, so the shard's T extent is the
# batch a worker holds. Read it back from the plate init just created rather than
# predicting it: the config may give the shards as a size target.
with open_ome_zarr(output_dirpath, layout="hcs", mode="r") as output_plate:
_, first_position = next(output_plate.positions())
batch_size = (first_position.data.shards or first_position.data.chunks)[0]
time_minutes, num_cpus, gb_ram_per_cpu = estimate_resources(
shape=(max(T_out // batch_size, 1), C_out, Z, Y, X),
ram_multiplier=8 * batch_size,
Expand Down
4 changes: 3 additions & 1 deletion biahub/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,9 @@ def _init_output_plate(
),
dtype=dtype,
chunks=(1, *settings.chunks_czyx) if settings.chunks_czyx else None,
shards_ratio=tuple(settings.shards_ratio) if settings.shards_ratio else None,
shards=tuple(settings.shards)
if isinstance(settings.shards, list)
else settings.shards,
metadata_sources=input_plate,
metadata_keys=PROVENANCE_METADATA_KEYS,
extra_metadata={PROVENANCE_KEY: settings.model_dump(mode="json")},
Expand Down
Loading
Loading