diff --git a/README.md b/README.md index bf50067..505f0ab 100644 --- a/README.md +++ b/README.md @@ -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 `:` (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= ./build.sh # QuPath 0.7 is built with Java 25 - QUPATH=/path/to/QuPath LC_HOME= 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). diff --git a/docs/USING_QUPATH.md b/docs/USING_QUPATH.md new file mode 100644 index 0000000..0c97210 --- /dev/null +++ b/docs/USING_QUPATH.md @@ -0,0 +1,75 @@ +# Using LabConstrictor in QuPath + +LabConstrictor reads the tool manifest of any registered scientific application. The same application can be accessed from other hosts; in QuPath, the useful workflows are generally those involving image regions, annotations and spatial results. + +This guide describes the current QuPath 0.7 prototype. For installation, build instructions and application links, start with the [README](../README.md). + +## Run a tool + +1. Open an image in QuPath. +2. Select one or more annotations if you want to work on a smaller area. +3. Choose **Extensions > LabConstrictor tools...**, select an application and tool. +4. Select **Current image** for the image input. Set **Image area** to **Selected annotation(s)** to export their bounding rectangle, or choose another area. +5. Review parameters, especially pixel size, then run the tool. +6. Inspect the result window and any annotations added to the project. + +An annotation selection does not automatically restrict the analysis to pixels inside its shapes: the **Image area** setting exports their bounding box. If a tool declares a separate `RegionOf(...)` labels input, the selected annotations can also be sent as a mask. + +## Image inputs and areas + +| Image source | Available area | +|---|---| +| Current image | Whole image, selected annotations or current viewport | +| Another project image | Whole image | +| File | File passed directly, unless channel extraction requires QuPath to export it | + +**Whole image** exports the full-resolution image, using the first Z/T plane in the current implementation. + +**Selected annotation(s)** exports the bounding rectangle of the chosen annotations, rounded to pixel boundaries and clipped to the image. The exported Z/T plane follows the viewer. Selections spanning different Z/T planes require particular care: the bounding-box calculation does not filter the selected annotations by plane. + +**Current viewport** exports its visible bounds, clipped to the image, at full resolution. The viewer's Z/T plane is used. + +The default export guard is **100 million pixels per plane**. This limits host-side image exports, not image files passed as paths without export. The system property `lc.qupath.max_export_pixels` can change the budget; higher limits require more memory. There is no automatic downsampling. + +### Multiple image inputs + +A tool can receive multiple images. When using cropped areas, the current bridge requires their spatial areas to agree, since it supports a single coordinate offset for outputs. A mixed cropped/whole-image run is refused. Check the result's `apply_to` association before interpreting spatial data. + +### Calibration + +A tool may declare `PixelSizeOf(...)` to prefill its pixel-size parameter from the chosen image. **Check the value after changing source**, particularly when switching to a file or an image without usable calibration. Until [issue #15](https://github.com/CellMigrationLab/LabConstrictor-QuPath/issues/15) is fixed, the field may retain an earlier value. + +## Results and coordinates + +The bridge stores the selected area's origin and uses it to map local pixel coordinates from results back to full-resolution slide coordinates. + +| Output | Current behavior | +|---|---| +| Points | Point annotation on the open image, when associated with it; otherwise a table | +| Shapes | Polygon annotations, including holes and numeric feature measurements, when associated with the open image | +| Image and labels | Preview, path and an option to open the image | +| Table | View in the results window, with a display row limit | +| Affine | Matrix display (not an applied image transform) | +| Values and messages | Results panel or status | + +A label-image preview is **not** an imported QuPath detection or annotation, and tabular outputs are **not** copied into measurements automatically. Points and shapes are persistent objects only when added to the image hierarchy. + +**Do not change the active QuPath image or selected annotations while a tool is running.** Currently the result import and some input lookups access live QuPath state rather than the exact image/selection used to start the run. See [issue #16](https://github.com/CellMigrationLab/LabConstrictor-QuPath/issues/16). + +### Checking placement + +For spatial tools, inspect the original image and confirm that annotations are positioned where expected. A useful regression case places an annotation away from the origin, crops around it, then checks the restored coordinates. + +## Troubleshooting + +If no application appears, check its registration with `labconstrictor-tools list` and `labconstrictor-tools doctor`. + +If export fails, check the area size and whether annotations are selected. If a worker fails, inspect **Details** and the shared LabConstrictor log. If a table is malformed, preserve the source CSV for an issue report: QuPath currently reads CSV by physical lines and [does not support multiline quoted cells correctly](https://github.com/CellMigrationLab/LabConstrictor-QuPath/issues/17). + +The **Copy as command** feature cannot reproduce a selected annotation, viewport crop or channel extraction by itself; it adds explanatory notes and uses a whole-file path where possible. + +## Maintainer regression checks + +Verify the areas, pixel calibration, selected region masks, point and polygon offsets, Z/T planes, multiple image inputs, output types, cancellation and background worker state. Include a long-running test that switches active images before the result returns, and a multiline CSV case. Record QuPath, Java and operating-system versions used. + +The test harness is documented in the [README](../README.md). It modifies the QuPath installation selected for testing, so use a disposable copy.