Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
80a1d30
docs: explain QuPath region analysis and result coordinates
guijacquemet Oct 8, 2026
1bb3dee
docs: narrow QuPath claims and clarify native result limits
guijacquemet Oct 8, 2026
a2e0891
docs: define maintainable documentation structure
guijacquemet Oct 8, 2026
615eb4b
docs: remove planning map; create actual documentation pages instead
guijacquemet Oct 8, 2026
c5c517e
docs: add actual user guide with workflow, results and maintainer checks
guijacquemet Oct 8, 2026
6e3171a
docs: link README to real user guide
guijacquemet Oct 8, 2026
09fc1f9
docs: link real scientific applications and qualify bridge availability
guijacquemet Oct 8, 2026
ca76e8e
docs: link installable scientific applications from user guide
guijacquemet Oct 8, 2026
866d4cd
docs: add Guess the Condition to available applications
guijacquemet Oct 8, 2026
6be3e47
docs: link Guess the Condition from host guide
guijacquemet Oct 8, 2026
9c9a117
docs: replace duplicated app catalog with relevant verified workflows
guijacquemet Oct 8, 2026
3aec0f5
docs: fix awkward links and state app compatibility by host
guijacquemet Oct 8, 2026
5ba22c6
docs: avoid linking to README heading that is not yet merged
guijacquemet Oct 8, 2026
36b9566
docs: avoid linking to README heading that is not yet merged
guijacquemet Oct 8, 2026
621924b
docs: clarify manifest-driven support and focus on host usefulness
guijacquemet Oct 8, 2026
d2cc04d
docs: explain generic manifest connection in host guide
guijacquemet Oct 8, 2026
4f5958e
docs: correct QuPath first run, scope and test-installation warning
guijacquemet Oct 9, 2026
c163c43
docs: correct QuPath image-area behavior and result limitations in guide
guijacquemet Oct 9, 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
94 changes: 36 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,80 +1,58 @@
# LabConstrictor-QuPath (prototype)
# LabConstrictor for QuPath

Runs the tools of every installed LabConstrictor app (NucleiSky, CellTracksColab, ...) from **Extensions > LabConstrictor tools...** in
QuPath 0.7. The form is generated from the tool's declared schema ([LabConstrictor-Tools](https://github.com/CellMigrationLab/LabConstrictor-Tools),
`docs/PROTOCOL.md`); the tool runs in the app's own Python environment through the LabConstrictor worker (JSON lines on stdin/stdout),
so QuPath never imports the app's packages and needs no Python of its own. Same idea as the Napari and Fiji front-ends.
Run tools from any **registered LabConstrictor application** on QuPath images. QuPath reads the application's tool manifest, builds a form and launches the tool in its own Python environment. Applications do not need separate QuPath plugins.

## Install
1. Install the LabConstrictor app(s) (the installer registers them; check with `labconstrictor-tools list`).
2. Drop `labconstrictor-qupath-0.1.0.jar` on the QuPath window (or into the QuPath extensions folder) and restart QuPath.
3. **Extensions > LabConstrictor tools...**
This is a **prototype targeting QuPath 0.7**. See [Using LabConstrictor in QuPath](docs/USING_QUPATH.md) for image-area choices, coordinates, results and limitations.

Without the jar: open `src/main/resources/org/cellmigrationlab/labconstrictor/qupath/LabConstrictorTools.groovy` in QuPath's Script Editor and run it.
## Start here

## What it does
* Pick an app and a tool. Numbers (range, unit), choices, checkboxes, text, files and folders become fields; parameters of a group sit under a heading, advanced ones in a collapsed section, optional ones without a default have a "set" box (unticked = the tool gets nothing), and `enabled_when` greys fields out.
* **Image inputs**: the image open in QuPath, any image of the open project, or a file. QuPath's image is exported losslessly as TIFF (the whole image, or the part chosen under **Image area**, see below). The pixel size of the chosen image fills the tool's pixel-size field (micrometres).
* Results open in a window: values, tables (as a table), images (a preview, the path and an "Open in QuPath" button), alignment matrices, files, messages (also shown under the status line) and points.
* **Interaction hints** of the manifest: a `choices_from` parameter is a dropdown filled by another tool of the app (a text field when that tool cannot answer), `clear_after_run` parameters are reset after a successful run, a `Collapsed` group is a folded section, and an output with `Replace()` reuses the results window of the tool instead of opening a new one.
* **Channel selector**: an image input declared with `PickChannel()` gets a Channel chooser (the channel names of the chosen image, or of a file); the tool receives only that channel.
* **Points** found in the image that is open in QuPath become a point annotation named `<app>:<output>` (replaced by the next run when the output declares `Replace()`); points of any other image are shown as a table.
* "No match" is a message, not an error. Progress bar, Cancel (a tool that ignores it is killed after 3 s), worker kept between runs (or not), details of the last run, no worker left behind when the window closes.
* Results are kept in `~/.labconstrictor/results/` (the newest 20), like the command line.
1. Install a [LabConstrictor application](#applications) with registered tools. Check it using `labconstrictor-tools list` and `labconstrictor-tools doctor`.
2. Build the QuPath extension (see below), or obtain its JAR if available. Install it by dragging the JAR onto QuPath's main window or copying it to the QuPath user extensions directory. Restart QuPath.
3. Open an image and choose **Extensions > LabConstrictor tools...**.
4. Select an application and tool. For a region-based run, select an annotation, choose **Current image**, and set **Image area** to **Selected annotation(s)**.
5. Inspect the result window and any annotations created in the image hierarchy.

## Large images: the Image area and the size guard
Every image or labels input that uses the image open in QuPath ("Current image: ...") has an **Image area** chooser beside the image chooser:
**Selected annotation(s)** exports the **bounding rectangle** of the selected objects, not just pixels inside their shapes. A separate `RegionOf(...)` input can supply a labelled selection mask if the tool declares one. The extension exports at full resolution, with a default limit of **100 million pixels per plane**.

* **Whole image** (default): as before.
* **Selected annotation(s)**: the bounding box of the selected annotations (points do not count), widened to whole pixels and clamped to the image; z and t are those of the viewer.
* **Current viewport**: the bounds of the region the viewer shows, clamped to the image.
Points and outlines associated with the image open in QuPath can become annotations in slide coordinates. Image and label results have previews, but **label images are not converted to native detections**, and result tables do not automatically become object measurements.

A project image or a file is always sent whole (the chooser is disabled and its tooltip says why). A missing selection or viewer is reported, never guessed around.
Do not switch the active QuPath image or change its selection during a running analysis until [run-state handling](https://github.com/CellMigrationLab/LabConstrictor-QuPath/issues/16) is corrected. Check the pixel-size field whenever you change image sources ([calibration issue](https://github.com/CellMigrationLab/LabConstrictor-QuPath/issues/15)).

The tool receives only the area; **results come back on the right place of the slide**: outlines (annotations) and points are moved by the area's origin (x0, y0). The offset comes from the image named by the output's `apply_to` (a RegionOf parameter counts as the image it belongs to), else from the first image input. Because results have only one offset, several image inputs must use the same area; a run that mixes different areas (or an area with a whole image or a file) is refused with a message. A RegionOf selection mask is painted over the area only (same crop and offset), so its 100 M pixel cap applies to the cropped size and a selection on a big image works.
## Install or build

**Size guard.** Before an image is exported through QuPath (a path to a file is passed on untouched and is not checked) the number of pixels of one plane of the exported area (width x height) is compared with a budget of 100 000 000 pixels. Above it the run is refused with a message that names the image and its size and gives the two ways out: "choose Image area: Selected annotation(s)", or use a smaller image or a file. Nothing is downsampled or cropped silently. Change the budget with the system property `lc.qupath.max_export_pixels` (a positive whole number of pixels), e.g. `-Dlc.qupath.max_export_pixels=400000000` in QuPath's JVM options; the tests set a tiny value to trigger the guard on a small image. An invalid value is ignored (logged once) and the default is used.
The extension is packaged as `labconstrictor-qupath-0.1.0.jar`. For a development build against an installed QuPath 0.7 and JDK 25:

The downsample of an export is 1 for now and is kept next to the offset (`downsample`, always 1.0), so the later resolution step changes one place.
```bash
QUPATH=/path/to/QuPath JAVA_HOME=/path/to/jdk25 ./build.sh
```

## Fallbacks (intended)
Each of these is deliberate and is written once to QuPath's log (logger `labconstrictor`) when it happens. Every other failure is logged with its stack trace and shown to the person (status line or Details).
The JAR is written to `target/labconstrictor-qupath-0.1.0.jar`. Follow [QuPath's manual extension installation instructions](https://qupath.readthedocs.io/en/stable/docs/intro/extensions.html#installing-extensions-manually) to install it. Alternatively, developers can open `src/main/resources/org/cellmigrationlab/labconstrictor/qupath/LabConstrictorTools.groovy` in QuPath's Script Editor.

| Where | What happens | What the person sees |
|---|---|---|
| `choices_from` parameter (dropdown twin) | The source tool is not ready (a value it depends on is empty), failed, or did not return a list: the text field stays | The plain text field; after a failure the status line says "The choices for '...' could not be loaded (type the value): reason" (first time only) |
| `PickChannel` image input | The chosen image has one channel (or none can be read): the channel chooser stays hidden and the tool gets the whole image | No Channel chooser; an unreadable file also shows "Channels of '...' could not be read" in the status line |
| Image area other than the whole image, in Copy as command | A terminal line cannot name a selection or a viewport: the line sends the whole file | A note line "# image: QuPath sent only the Image area '...' (the command line has no area); the command sends the whole file" before the command |
| Points / outlines output | The image searched was not the image open in QuPath (a file, or another project image): nothing is added to the open image | The table (points) or the line "Not placed on an image (...)" in the results window |
For troubleshooting, check the extension's **Details** window and the LabConstrictor log under `~/.labconstrictor/logs/` (or the configured `LC_HOME`). See the [user guide](docs/USING_QUPATH.md).

## Not done yet
* Images are exported at full resolution only (no downsample or pixel-size choice yet); RGB / brightfield images are exported as RGB TIFF.
* Label images are shown as a preview, not converted to QuPath annotations/detections, and tables are not added to the measurements.
* No headless/script API, no Windows/macOS testing, no menu entry per tool.
## Applications

## Build and test
QUPATH=/path/to/QuPath JAVA_HOME=<JDK 25> ./build.sh # QuPath 0.7 is built with Java 25
QUPATH=/path/to/QuPath LC_HOME=<registry with NucleiSky and CellTracksColab> tests/run_gui_test.sh # needs xvfb; 21 checks
The manifest makes the bridge **application-independent**; the list below is a set of examples, not a compatibility allowlist. Choose tools according to how useful their inputs and outputs are in QuPath.

`BODY=gui_test_interactions_body.groovy tests/run_gui_test.sh` runs the interaction hints, messages and points against the example app of labconstrictor-tools (`labconstrictor_tools.examples.interactions`, registered in `LC_HOME`; 9 checks).
- [LabConstrictor Playground](https://github.com/CellMigrationLab/LabConstrictor-Playground) — test images and bridge diagnostics
- [NucleiSky](https://github.com/CellMigrationLab/NucleiSky) — microscopy-image registration
- [VLab4Mic desktop](https://github.com/CellMigrationLab/LabConstrictor-VLab4Mic) — fluorescence-image simulation
- [CellTracksColab desktop](https://github.com/CellMigrationLab/CellTracksColab_LabConstrictor) — cell-track analysis
- [Guess the Condition](https://github.com/CellMigrationLab/GuessTheCondition) — blinded microscopy-image classification

`BODY=gui_test_playground_body.groovy tests/run_gui_test.sh` runs the tools of the LabConstrictor Playground app (check, device dropdown, big image, crash, no memory, 200 000 points; 8 checks; register the Playground in `LC_HOME` first).
Each application must be installed and its tool module registered before its tools appear. The [LabConstrictor Toolkit](https://github.com/CellMigrationLab/LabConstrictor-Tools) documents the shared manifest and registration.

`BODY=gui_test_channel_body.groovy tests/run_gui_test.sh` checks the channel chooser of `PickChannel` inputs with a 3-channel TIFF (the tool receives only the chosen channel; 4 checks).
## Development and tests

`BODY=gui_test_adversarial_body.groovy tests/run_gui_test.sh` throws odd input at the hints (a broken or malformed source tool, 5000 unicode options, a default the source does not list, empty and 50 000 points, a very long message, a failing run); register `tests/adversarial_app` as `adv` first (12 checks).
The QuPath implementation is in `src/main/resources/org/cellmigrationlab/labconstrictor/qupath/LabConstrictorTools.groovy`. The extension entry point is `src/main/java/org/cellmigrationlab/labconstrictor/qupath/LabConstrictorExtension.java`.

``BODY=gui_test_fallbacks_body.groovy tests/run_gui_test.sh` checks that failures that used to be silent are reported (bad number typed into a field, unreadable value in Copy as command, host failure with stack trace in Details, unreadable channel file, intended fallbacks logged; 12 checks; needs the `interactions` example app in `LC_HOME`).
`BODY=gui_test_area_body.groovy tests/run_gui_test.sh` checks the Image area and the size guard against the `interactions` example app and the blobs fixture (guard at and one pixel over the budget, the exported area equals the server region, outlines and points land at the blobs' full-image coordinates, the viewport area, RegionOf over an area, differing areas refused, Copy as command note; 48 checks).
The GUI test harness requires QuPath, Java and (on Linux) `xvfb-run`. **It alters the selected QuPath installation** by copying the JAR into `lib/app` and modifying `lib/app/QuPath.cfg`; use a disposable test installation:

`BODY=gui_test_buttons_body.groovy tests/run_gui_test.sh` clicks "Rescan apps" and "Restart worker" (3 checks; on the version before the split the Rescan button threw `MissingMethodException` because a local variable named `rescan` shadowed the method).
```bash
QUPATH=/path/to/disposable-QuPath JAVA_HOME=/path/to/jdk25 ./build.sh
QUPATH=/path/to/disposable-QuPath LC_HOME=/path/to/test-registry tests/run_gui_test.sh
```

### Lint (runs in CI) and what CI does not run
See `tests/` for test bodies and fixtures. The GUI tests were not run for this documentation change. The extension's [open issues](https://github.com/CellMigrationLab/LabConstrictor-QuPath/issues) record implementation work.

`.github/workflows/lint.yml` lints `LabConstrictorTools.groovy` with `npm-groovy-lint` (pinned version, CodeNarc rules, config in `.groovylintrc.json`; each disabled rule has its reason there). It fails on any finding, including info. Run it locally (needs Node and Java):

npx --yes npm-groovy-lint@18.0.0 --path src/main/resources/org/cellmigrationlab/labconstrictor/qupath --files "**/*.groovy" --failon info

The GUI tests above are **not** run in CI: they need a real QuPath (0.7, Java 25), a virtual display and registered LabConstrictor apps, which a GitHub runner does not have. Run them by hand with `tests/run_gui_test.sh` before merging a change to the script.

`tests/gui_test_body.groovy` drives the real dialog in QuPath on a virtual screen (project with two calibrated images, both apps) and writes screenshots.
Related hosts: [Fiji](https://github.com/CellMigrationLab/LabConstrictor-Fiji) · [Napari](https://github.com/CellMigrationLab/napari-labconstrictor).
Loading
Loading