Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ ruff format .
pytest
```

Coding conventions are described in [docs/development.md](docs/development.md).
Coding conventions are described in [docs/development.md](docs/development.md). Planned work after v0.4.0 is in [docs/roadmap.md](docs/roadmap.md).

## License

Expand Down
117 changes: 117 additions & 0 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Preprocessing roadmap

Planned work after v0.4.0. The v0.4.0 work itself (PP-0 to PP-13) is in
[plan.md](plan.md) §10 and in the frozen design (`preprocessing-design-v0.4.md` on
`CellMigrationLab/MorphoFeature`, branch `claude/restart-doc`).

## Quick quality checks before analysis (owner, 2026-11-02)

**Goal:** preprocessing stays a *quick* step, but it points the user to fields of view (FOVs)
worth a look before any analysis. Two checks are planned:
- outlier images, from whole-FOV embeddings;
- possibly wrong masks, from quick per-object measurements.

Both only **flag**. The user looks at the flagged FOVs and decides. Nothing is excluded,
corrected or re-annotated automatically.

### Q1. Outlier FOVs from whole-FOV embeddings (MorphoEmbed)

- **What it does:**
- embeds each FOV as a whole, with no patching, through MorphoEmbed;
- flags FOVs whose embedding lies far from the other FOVs of the same condition and
biological repeat. Typical causes are out-of-focus fields, debris, bubbles, empty fields,
or other acquisition settings.
- **What it complements:** the intensity checks, which already flag images whose median or
high percentile lies far from their group. The embedding check catches what intensities
miss, such as texture, focus and content.

### Q2. Quick mask check (MorphoObjects)

- **What it measures:** a small, fast set of per-object measurements through MorphoObjects,
such as area, solidity, eccentricity, the share of objects touching the border, the
object count per FOV, and objects per parent (nuclei per cell). They are summarised per FOV
and mask type.
- **What it flags:** FOVs whose masks look unlike the others of the same condition and
biological repeat (out of distribution), for example:
- many tiny or merged objects;
- holes;
- over- or under-segmentation;
- cells without a nucleus or with several nuclei.
- **What the user does:** checks the annotation quality of those FOVs.
- **What it complements:** the `masks` step, which already counts objects and checks
containment between mask types; this extends it with shape statistics and outliers.

### Rules for both

- **Fast by design.** A time budget per FOV is set and benchmarked on the real datasets
(fiberfeatures `experiment_001`, MorphoRelate HPMEC) before release:
- Q1: one embedding per FOV from a downscaled whole field; CPU must be usable, and GPU is
optional and explicit;
- Q2: only cheap region properties, with no texture features.
- **Optional.** These are extras; `check` and `run` never need them.
- **Same rules as every brick:**
- no guessed axes or channels;
- no implicit model download, model swap, or CPU/GPU fallback; the model and its weights
are explicit and recorded;
- the channels used are stated;
- every FOV that could not be scored is reported, never treated as normal.
- **Comparisons only within a group.** A FOV is compared with the FOVs of its own condition
and biological repeat, as in the intensity checks. That way a real difference between
conditions is not flagged as an error. A group too small to compare (below the
intensity checks' minimum of 5 images) is reported as "not checked", not as passed.
- **Outlier statistic.** Robust deviations from the group median, reusing the intensity
checks' statistic and thresholds where they fit (no second copy, §9). For embeddings, a
robust distance in the embedding space; which one is decided with a benchmark.
- **Output.** The flags go to the run's report and to a QC table in the output folder, with
the model, its weights' identity, the measurement set, thresholds and versions, so a rerun
can tell whether they are still valid.

### Where the code lives (owner, 2026-11-02)

**Decision:**
- each check runs when its brick is installed: Q1 when MorphoEmbed is installed, Q2 when
MorphoObjects is installed;
- a check whose brick is missing is skipped;
- neither brick is ever needed for `check` or `run`.

**Design (to keep the dependency one-way):**
- **The bricks register their checks.** MorphoEmbed and MorphoObjects depend on preprocessing,
so preprocessing never imports them by name. Each brick declares its check through a Python
entry point, for example group `morphobricks_preprocessing.quick_checks` with names
`embedding_outliers` and `mask_outliers`. Preprocessing finds the installed ones with
`importlib.metadata.entry_points`.
- **No extra of preprocessing installs the bricks,** since that would be a circular
install. Users install the bricks they want.
- **The algorithms stay in the bricks:**
- MorphoEmbed: the whole-FOV embedding;
- MorphoObjects: the quick per-object set.
Both read the images and masks through `RunInputs`.
- **Preprocessing owns the rest:**
- the hook's interface (a versioned protocol: inputs, the result table, the provenance it
must return);
- the outlier statistic, shared with the intensity checks;
- the QC table's format;
- showing the flags in the report, the CLI and the notebook.
- **A skipped check is reported, never silent.** For example: "embedding check not run:
MorphoEmbed is not installed (`pip install …`)". A skipped check is never read as passed,
so the report keeps three outcomes apart: flagged, checked with nothing found, and not
run.
- **A hook that fails, or returns an incompatible protocol version, is reported as not run,
with the reason.** Preprocessing does not fall back to another method.
- **When the checks run:**
- in the notebook, after the run, as an optional step that lists which checks are
available;
- on the command line, through a `quick-checks` command (or `run --quick-checks`), so
the default run stays as fast as today.
- **Model and device for Q1 come from MorphoEmbed's own explicit settings.** Its weights must
already be present; a missing weight file is "not run", never a download.

**Still to decide:**
- the default model for Q1;
- the per-FOV time budget;
- whether MorphoFeature also offers the same checks as a "quick QC" preset.

### When

After v0.4.0 and after the MorphoEmbed and MorphoObjects migrations to `RunInputs` (phase 1
of the MorphoBricks sequence). In the phase 2 triage, the owner places it in a bucket.
Loading