Skip to content

Reviewing and correcting results

A segmentation of a whole tissue has tens of thousands of objects, and some of them are wrong. Scrolling through the volume hoping to spot them doesn't work. patchworks review shows you the objects most likely to be wrong, one at a time, and lets you fix each one with a single key. Your corrections go straight into the result tables and workbooks, and no voxel is rewritten unless you ask.

pip install "patchworks[napari]"      # on the machine you review on
patchworks review results/image.zarr

The review panel

A cilium only 25% inside its cell. The view jumps to it, shows it with its cell outlined, and hides everything else. Press G if it is right, H to give it the right cell, W if it isn't a cilium at all.

What gets flagged

Each object in the queue shows why it is there:

Flag Means Typical cause
not inside any cell the object overlaps no parent object debris, a missed cell, a cilium on the lumen side
only 30% inside cell #88 less than min_overlap of it is inside its parent wrong cell picked at a boundary, or a merge of two objects
0 nuclei (expected 1) a parent holds an unexpected number of children a cell split in two, two cells merged, a missed nucleus
meets #412 exactly at a tile seam (z) two objects touch face to face on a tile boundary one object the tiling cut in two, or two neighbours; press J to join them
unusually large (6.2x the median) the size is far from the rest (robust z-score > 3.5) two objects merged, or a fragment
outside cell #12, 0.8 µm away touches no parent, but one is within max_distance_um a cilium beside its cell (worth a look, lower priority)
position unclear: its cell has no nucleus to orient it a position rule needs the nucleus, and the cell has none a missed nucleus, or a cell cut by the image edge

The most suspicious objects come first. "Children" and "parents" come from the relations of a multi run (e.g. cilia_labels → cyto_labels); the size and seam flags apply to every label image.

Expected counts are yours to state, since only you know that a cell has one nucleus and zero to two cilia. Put them in multi.yaml, and the workflow checks them before it starts and stores them with the results:

review:
  expect:
    cyto_labels:
      nuclei_labels: 1         # exactly one
      cilia_labels: [0, 2]     # zero to two
  min_overlap: 0.5             # or per pair: {cilia_labels: {cyto_labels: 0.3}}

Or pass them when you open the review: patchworks review image.zarr --expect cyto_labels:nuclei_labels=1 cyto_labels:cilia_labels=0-2.

Deciding

Key Decision Effect on the results
G correct as it is kept; marked ok
W wrong: not a real object dropped from every table and count
H, then click belongs to another parent: click the right one (the background means "none") its parent id changes; both parents' counts follow
J join with the suggested object (a seam split) the two become one object: sizes added, centroid and intensities weighted
Shift+J, then click join with the object you click same
N / Shift+N skip / go back nothing
U undo the decision about this object back to unreviewed

Every decision is saved immediately, in the store, next to the object table. Close napari whenever you like: the next patchworks review continues where you stopped, and the queue leaves out what has been decided. Corrections chain as you would expect. For example, joining two halves of a cell moves the cilia of both halves to the joined cell.

Seeing what belongs to what

Outside the queue, you can look at any object:

  • Click any object in the image: the panel switches to it and shows it with its parent (outlined) and its children. Click a cell to see its nuclei and cilia, a cilium to see its cell. A click reads the image at full resolution, and a thin object is hit even a couple of voxels off, so cilia are easy to pick.
  • Go to #: type an object's id and press Enter.
  • Hover over any object: napari's status bar shows its parent, its number of children, its position and its review status.

Options in the panel change the view:

  • Show only this object and its relatives (on by default) hides everything else. Untick it to see the neighbourhood.
  • Colour these objects by their parent adds a layer where every child has its parent's colour: all cilia of one cell share a colour, so an assignment error stands out as a different colour.
  • Colour these objects by position colours each cilium by its class: apical, basal, lateral or central.
  • Side view shows z against x, with z up, through the object. Apical and basal are then seen at a glance.

Orange rings mark the flagged objects still open. They stay visible in 3D, where napari's coarse 3D level hides small objects.

Where a cilium sits: apical, basal, lateral, central

A cilium classified as apical, in side view

Side view, coloured by position: an apical cilium (green) standing out of the top of its cell, the nucleus at the bottom; next door, a basal cilium (blue).

Each cilium is classified by the cell surface its base is nearest to:

Class The base is nearest to
apical the top of the cell
basal the bottom of the cell
lateral the side wall
central none of them: deeper than half-way from every surface

"Top" needs an apical direction per cell. It can be a fixed direction: +z if apical is up the stack, as for a monolayer imaged from below. Or it can point away from the nucleus, for epithelia whose nuclei sit basally; then each cell gets its own axis, whatever its tilt. The base of a cilium is its end nearer the cell's centre, since a cilium grows out from its base.

# multi.yaml
review:
  position:
    cilia_labels:
      parent: cyto_labels
      apical: nuclei_labels     # away from the nucleus; or "+z", "-z", ...
      central_depth: 0.5        # optional

Or when opening the review: patchworks review image.zarr --position cilia_labels:cyto_labels=nuclei_labels.

The corrected tables get the class (position), where the base sits (position_axial: -1 basal … +1 apical; position_radial: 0 on the axis … 1 at the side) and the cilium's angle to the apical axis (angle_to_axis_deg: 0 along it, 90 across it). Each cell gets its counts per class (n_cilia_labels_apical, …), as does the relation workbook.

The cell's shape comes from its moments, i.e. an equivalent cylinder, so this is a classification, not a surface distance. It is reliable for columnar and cuboidal cells, and less so for very irregular ones. Check it the usual way: Colour these objects by position plus Side view. If one is wrong, the Position is: buttons correct it. A correction counts as a classification fix, not a segmentation error, so it does not enter the error rate. A cell without a nucleus cannot be oriented; its cilia are unknown and flagged.

Cilia next to their cell, not on it

A cilium lying against its cell without overlapping it belongs to no cell. With max_distance_um: 1.0 on a relation (or --max-distance 1 for patchworks tables), it gets the nearest cell within that distance, recorded in cyto_labels_distance_um, and is flagged with a lower priority.

How good is the segmentation?

Pick the queue Random sample (error rate) and review objects in the order it gives. The panel reports the fraction found wrong, with a 95% confidence interval, e.g. Error rate 3.0% (95% CI 1.0–8.5%) from 100 random objects. Stop when the interval is narrow enough for what you need. The estimate is unbiased because the order is random and fixed. Objects you already decided from the flagged queue count too: a decision about an object is true however it came up.

Using the corrections

The workbooks and tables are always read with the decisions applied:

  • The relation workbooks of a multi run: patchworks review image.zarr --workbooks results/ writes every one (<child>_to_<parent>.xlsx) from the corrected tables, with a qc column (ok, fixed, or blank if not reviewed). Re-running the same run_multi command does it too: a workbook older than your decisions is rewritten, straight from the tables, without re-reading any labels.
  • Export the corrected tables, one file per label image, from the panel or with patchworks review image.zarr --export results/tables --format xlsx (or csv, parquet). A csv loads straight into napari-chunked-regionprops.
  • Write corrected labels (panel button, or --write-labels nuclei_labels) writes labels/nuclei_labels_reviewed, the label image with the decisions applied to the voxels. You only need this for figures, or for tools that read label images only.

patchworks review image.zarr --summary prints the counts and the error estimate without opening napari.

In Python

from patchworks import Review

rv = Review("image.zarr", expect={"cyto_labels": {"nuclei_labels": 1}})
rv.flags("cyto_labels")[:5]                 # the worst five
rv.decide("nuclei_labels", 17, "wrong")
rv.effective("cyto_labels")                 # the table with corrections

The tables themselves: Tables and relations.

Where to review

On any machine with a screen and napari, reading the store the cluster wrote:

  • Copy the store to your computer (tables and decisions live in it), or open it on a mounted cluster filesystem. napari reads only what is on screen, so a network mount works.
  • The store must be writable, since that is where the decisions are saved. A .zip bundle is read-only, so unzip it first.
  • The decisions are small. If you reviewed a copy, --workbooks and --export work on that copy directly; there is no need to copy anything back.