Metadata-Version: 2.4
Name: quai-workbench-napari
Version: 0.1.0
Summary: napari plug-in to create ground-truth data for quai.
Author: Aaron Ponti
Author-email: aaron@aaronponti.ch
License: Apache-2.0
Classifier: Framework :: napari
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: napari>=0.9.2
Requires-Dist: numpy>=1.25
Requires-Dist: pydantic!=2.11.*,<3,>=2.8
Requires-Dist: qtpy>=2.4
Requires-Dist: tifffile>=2022.7.28
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-qt; extra == "test"
Dynamic: license-file

# quai annotation workbench

[napari](https://napari.org/) plug-in to create ground-truth data for `quai`.

Annotations are kept in an **annotation project**: a folder that records every
sample, every saved version of its labels, and the source image each sample came
from, so annotation work is traceable and can be resumed at any time.

**Supported:** single-channel 2D images and 3D (ZYX) volumes in TIFF files, with
semantic labels (each pixel value is a class); random regions of the project's crop
size, from all source images or from images selected by `quai select-samples`;
predictions from quai to correct; exports that `quai storage import-annotations`
versions for training. **Planned later:** choosing regions on an overview of the
whole image (with OME-Zarr support), instance labels, and multichannel images.

For a standalone walkthrough with practice images and optional quai integration, see **Tutorial 14: quai annotation workbench** in the official quai tutorials.

# Install

Install [uv](https://docs.astral.sh/uv/getting-started/installation/) once.

### Install quai-workbench-napari

Create an environment (Python 3.12 or newer) and install the plug-in with its dependencies. Annotating does not need `quai` or a quai license. Working with quai's outputs (selections, predictions, `quai storage import-annotations`, and 3D in particular) needs quai 0.4.0b4 or newer.

The example uses Python 3.12; uv downloads it if needed.

```bash
uv venv --python 3.12 .venv
source .venv/bin/activate        # Windows (PowerShell): .venv\Scripts\Activate.ps1
uv pip install "napari[all]" quai-workbench-napari
```

# Annotating

Start **napari** and open **Plugins → Annotation workbench (quai)**.

```sh
$ napari
```

### Create or open a project

* **New project...** asks for:
  * a **project folder** (new or empty), where the project will be stored;
  * the **source images folder** (2D TIFF images, or ZYX stacks for a 3D project; subfolders are included). Source images are only read, never modified;
  * a **project name**;
  * the **classes**, comma-separated in label order, **background first** (label 0). For example `background, nucleus, border` gives labels 0, 1, and 2;
  * the **crop size** of the regions to annotate (default 512×512). Smaller regions are quicker to annotate, but more context may help the neural network learn;
  * the **maximum overlap** allowed between regions taken from the same image (default 20 %).
* **Open project...** opens an existing project folder. If the source images have moved, you are asked to locate them.
* A project can be edited by one napari session at a time. If it is open elsewhere, you are told where; take over only if that session has ended, otherwise its unsaved work may be lost.

Enter your name under **Annotator** to have it recorded with each saved version. The name is remembered for you on this computer (it is not stored in the project, which colleagues may share). Under the field, the panel shows who saved last and when, so you can tell whether the name is right before continuing someone else's work. Saving without a name works, but the status line points it out. "Completed regions" and "Saved drafts" count the current saved state of each region once, across the whole project. Unsaved edits do not change these counts until saved. Hover for contributors, historical revision counts, and dates. To list them without opening napari:

```sh
$ quai-wb-annotators /path/to/project          # or --json
```

### Annotate

* **Next random crop** picks a random region of the chosen size from a random source image, respecting the maximum overlap with regions already saved. The image and an empty **Annotation** layer are added to napari.
* Paint the labels in the **Annotation** layer. The legend in the panel shows which label value is which class.
* **Save draft** stores the labels as a **draft**. You can save as often as you like; each save is kept as a new version.
* **Complete** offers **Complete**, **Save draft**, **Discard changes**, and **Cancel** when labels differ from when the region was opened. Choosing Complete saves and marks the region as **fully annotated**. Only completed regions are used for training. If a region contains nothing of interest (only background), still complete it: the network also needs to see such areas.
* **Revert** discards edits made since the last save.
* Editing a completed region turns it back into a draft until you complete it again.
* The **sample list** shows every saved region and its state (draft or complete). Click a region to reopen it.
* Before you move to another region, open another project, or close napari or the panel with unsaved edits, you are asked whether to **Complete**, **Save draft**, **Discard changes**, or **Cancel**.

Labels with values that are not classes of the project are refused with a message; nothing is saved.

### 3D (volumetric) projects

Choose **3D (ZYX)** under **Images** in **New project...** to annotate single-channel ZYX stacks (TIFF; channel and time axes are not supported).

* Regions are **subvolumes**. Set their size per axis (default 16 × 96 × 96 voxels); it should cover the training patch of your quai campaign. A stack without axis metadata is read as ZYX.
* The voxel **spacing** is read from OME-TIFF or ImageJ metadata when it is complete for all three axes (in nm, µm, or mm). Otherwise you are asked for it the first time you save a region of that stack; known values are filled in. You can also set a project default for stacks without metadata, at creation or in the spacing dialog. While the project has no default, the first prompt offers the entered spacing as the default (untick it if your stacks differ), so stacks without metadata, including selected stacks already in the queue, are not asked one by one. **Spacing...** shows or changes the current stack's spacing; changed values are recorded as user-supplied. Use it only to correct a missing or wrong calibration: it changes the recorded spacing and the display scale, not the pixels. When stacks have different spacings, a note lists them: when a save or import first makes them differ, when the project is opened, and before an export that contains them. quai's default 3D campaign transforms work with one spacing per dataset; to train on several spacings, specialize them, otherwise annotate stacks of each spacing in a separate project. The Spacing dialog's checkbox shows whether the current spacing is also the project default.
* The subvolume opens at its middle slice, scaled by its spacing. Paint slice by slice in napari's 2D view, moving through Z with the slider. napari's 3D view shows the whole subvolume; there, napari only edits voxels that already have a label (repainting and erasing work, painting on background does nothing). **Save draft**, **Complete**, **Revert**, and **Discard changes** work on the whole subvolume. There is no per-slice completion: Complete means every slice of the subvolume is annotated.
* **Training in quai:** the export records each sample's spacing, but quai's 3D campaign transforms do not read it. Set the run's `voxel_size` to the recorded spacing and choose a `patch_size` (in the resampled space) that fits the subvolumes; see quai's annotation import guide. `quai storage import-annotations` reports a note when an export has several spacings.
* Selected stacks and their predictions work as for 2D images (see "Annotating images selected by quai"): **Next imported selection** draws a random subvolume of the next selected stack, prefilled with the matching part of its prediction. Whole stacks are never opened.
* If cells span many slices, most of them touch the top or bottom of a thin subvolume; a deeper subvolume (more Z slices) leaves more cells whole.

# Annotating images selected by quai

`quai select-samples` picks the images a model is most uncertain about. To annotate them:

* **Import selections** and choose the folder `quai select-samples` wrote (with `selection_manifest.json` and `selected_images.csv`). The selected images join the **selection queue**, in rank order; nothing is opened or copied yet.
* Images are matched to the project's sources folder by their absolute path. If they were selected from another folder (for example a training run's test subset), you are asked for the folder their relative paths start from.
* **Next imported selection** draws a **random region** of the next image in the queue, of the project's crop size (the whole extent along axes where the image is smaller), with the same overlap limit as **Next random crop**. Annotate, **Save draft**, and **Complete** as usual. Each region records the image's selection rank, score, and metric, and is recorded as randomly placed; both are carried into exports.
* The queue takes the images in turn and wraps around, so each image gives one region per pass; it continues where it left off after a restart. An image with no room for another region under the overlap limit is passed over and named in the status line.
* **Selection queue...** lists the selected images with their status and number of regions. **Done with this image** and **Skip** take an image out of the queue; **Put back in queue** returns it.
* Images already in the queue, images that changed since they were added, and images of the wrong dimension are reported and not queued.
* Regions are random on purpose: choosing them by hand favours easy regions. Showing the whole image to choose a region is planned with OME-Zarr support.
* **Projects from earlier versions:** selected images imported as whole-image samples stay whole-image samples. **Next imported selection** opens the unfinished ones first; they export as before.

### Starting from model predictions

Correcting a model's prediction is often faster than drawing from scratch. A selection is not needed: predictions of any of the project's source images can be imported, and **Next random crop** opens their regions with the prediction. To focus on the images the model is least sure about, use a selection as well (above).

* Predict the images with quai (`quai full-inference`, or a training run with `source_for_prediction` and `target_for_prediction` set), then **Import predictions** and choose that run's folder in the experiment's `runs/` (the one with `quai_lineage.json`). The prediction TIFFs are found through its configuration; if they have moved, you are asked to locate them.
* The model's classes must be the project's classes in the same order, or nothing is imported.
* The prediction of every image in the project's sources folder that has one is recorded (checked against the image's shape and the project's classes), whether or not the image was selected. A region drawn from such an image, with **Next random crop** or **Next imported selection**, then opens with the matching part of the prediction to correct; it is stored with the region when you first save it, so the prediction folder is not needed afterwards. If the prediction file was moved or changed, you can continue without it or locate the folder it was moved to.
* In projects from earlier versions, whole-image samples not annotated yet receive their prediction as a **draft**; samples you have already worked on keep their labels and are skipped; the import summary names them.
* For an ensemble, the fused prediction (`ensemble_<name>.tif`) is imported, not the individual models' predictions. Predictions never become ground truth on their own.
* Edit the labels and **Complete**. For labels changed since opening, the dialog offers **Complete**, **Save draft**, **Discard changes**, and **Cancel**. Complete keeps the labels and records an unchanged prediction as **accepted**, or changed labels as **corrected**, automatically. Discard changes restores the labels from when you opened the region without saving or completing it; earlier explicit saves remain in history, so the restored labels may be unsaved relative to the latest save. Cancel keeps editing without writing anything. **Revert** continues to restore the last saved labels. Existing `rejected` decisions remain readable and exportable; the dialog no longer creates them.

# Exporting for quai

**Export for quai...** writes the project's completed samples to a new, empty folder:

```sh
my-export/
├── annotation_manifest.json   # what was exported, from where, by whom
├── images/<sample>.tif
└── labels/<sample>.tif
```

* Each sample's **latest completed version** is exported; samples never completed are left out. If a sample was edited after it was last completed, you are asked whether to export its last completed version.
* Unsaved edits are not exported; you are offered to save them first.
* The export is built in a hidden folder, fully checked, and only then put in place, so an interrupted export never looks complete. An existing folder is never overwritten.
* Each export records the previous one as its parent, so successive exports form a history.
* `images/` and `labels/` use identical file names, which quai pairs correctly.

To check an export, or an `images/` + `labels/` folder made with another tool, before training:

```sh
$ quai-wb-validate /path/to/my-export
$ quai-wb-validate /path/to/other-folder --num-classes 3
$ quai-wb-validate /path/to/volume-folder --axes ZYX --num-classes 3
```

Plain folders are checked as 2D (`YX`) unless `--axes ZYX` is given; 3D images must not have channel or time axes. For an export, `--axes` states the layout you expect, and a different one is reported.

# Project folder

```sh
my-project/
├── annotation_project.json   # the project record: samples, versions, sources
└── blobs/                    # image and label data, one file per version
```

Do not edit these files by hand. The format is specified in `annotation-record-schema-v1.md`.

# Reviewing saved drafts

The sample list offers **All**, **Drafts**, and **Complete** filters with counts.
**Next draft** cycles through saved unfinished regions. **Next imported selection**
draws regions of the selected images.

When leaving an edited region or completing it, use the same choices:
**Complete** saves and makes it eligible for export; **Save draft** saves unfinished
work; **Discard changes** restores opening labels without writing; **Cancel** keeps
editing. Cancelling panel closure reopens the panel. Explicit earlier saves remain
in history. **Revert** still restores the most recently saved labels.

**Complete all drafts...** previews the affected samples and requires confirmation
that all are fully annotated, including imported predictions. Every draft passes
full pixel validation before one project-record replacement completes the batch;
if validation fails, none is completed.

**Discard all drafts...** previews how many samples will return to a completed
annotation and how many will become unannotated. Confirmation appends discard
events in one project-record replacement, preserving all revisions and blobs.
Never-completed samples reopen without labels and can receive predictions again.
New edits can be saved afterward, retaining the historical revision chain.
These discard events are an optional project-record extension; use the updated
plugin to reopen projects containing them. The export format is unchanged.

# Scope of the controls

- **Current region:** Save draft, Complete, Revert; Revert restores the last save.
- **Sample navigation:** Next random crop, Next imported selection, Next draft.
- **Saved drafts (whole project):** Complete all drafts and Discard all drafts.
- **Import:** Import selections, Import predictions, and Selection queue.
  Predictions open with the regions drawn from predicted images and never replace
  manual annotations.
- **Export (completed annotations):** Export for quai.

Controls are disabled when there is no eligible region, queue, draft, or completed
annotation. Edit controls update as labels change; all controls stay disabled
while a worker runs. Random crops and regions of selected images share one
project class mapping and the overlap limit. Each sample retains its own
geometry; prediction files must match the source shape before a region is cut
from them. In projects from earlier versions, selected whole images leave no
eligible crops in those sources.
