napari-nninteractive 2.3.2__tar.gz → 2.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {napari_nninteractive-2.3.2/src/napari_nninteractive.egg-info → napari_nninteractive-2.4.2}/PKG-INFO +63 -29
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/README.md +59 -22
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/pyproject.toml +18 -8
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/layers/scribble_layer.py +12 -1
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/widget_controls.py +13 -11
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/widget_gui.py +142 -9
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/widget_main.py +219 -45
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2/src/napari_nninteractive.egg-info}/PKG-INFO +63 -29
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive.egg-info/requires.txt +3 -6
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/LICENSE +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/MANIFEST.in +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/setup.cfg +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/__init__.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/_version_check.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/controls/__init__.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/controls/bbox_controls.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/controls/lasso_controls.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/controls/point_controls.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/controls/scribble_controls.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/layers/__init__.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/layers/abstract_layer.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/layers/bbox_layer.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/layers/lasso_layer.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/layers/point_layer.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/napari.yaml +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/utils/__init__.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/utils/affine.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/utils/utils.py +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive.egg-info/SOURCES.txt +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive.egg-info/dependency_links.txt +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive.egg-info/entry_points.txt +0 -0
- {napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive.egg-info/top_level.txt +0 -0
{napari_nninteractive-2.3.2/src/napari_nninteractive.egg-info → napari_nninteractive-2.4.2}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: napari-nninteractive
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.2
|
|
4
4
|
Summary: nnInteractive plugin for Napari
|
|
5
5
|
Author: Lars Krämer, Fabian Isensee, Maximilian Rokuss
|
|
6
6
|
Author-email: lars.kraemer@dkfz-heidelberg.de, f.isensee@dkfz-heidelberg.de, maximilian.rokuss@dkfz-heidelberg.de
|
|
@@ -225,14 +225,13 @@ Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
|
225
225
|
Requires-Python: >=3.10
|
|
226
226
|
Description-Content-Type: text/markdown
|
|
227
227
|
License-File: LICENSE
|
|
228
|
-
Requires-Dist: torch
|
|
229
228
|
Requires-Dist: numpy
|
|
230
229
|
Requires-Dist: qtpy
|
|
231
230
|
Requires-Dist: napari-nifti
|
|
232
|
-
Requires-Dist: huggingface_hub
|
|
233
|
-
Requires-Dist: hf_transfer
|
|
234
|
-
Requires-Dist: nnInteractive>=2.4.0
|
|
235
231
|
Requires-Dist: napari_toolkit
|
|
232
|
+
Requires-Dist: nninteractive-client>=2.5.0
|
|
233
|
+
Provides-Extra: local
|
|
234
|
+
Requires-Dist: nnInteractive>=2.5.0; extra == "local"
|
|
236
235
|
Provides-Extra: testing
|
|
237
236
|
Requires-Dist: tox; extra == "testing"
|
|
238
237
|
Requires-Dist: pytest; extra == "testing"
|
|
@@ -240,8 +239,6 @@ Requires-Dist: pytest-cov; extra == "testing"
|
|
|
240
239
|
Requires-Dist: pytest-qt; extra == "testing"
|
|
241
240
|
Requires-Dist: napari; extra == "testing"
|
|
242
241
|
Requires-Dist: pyqt5; extra == "testing"
|
|
243
|
-
Provides-Extra: remote
|
|
244
|
-
Requires-Dist: nnInteractive[client]>=2.4.0; extra == "remote"
|
|
245
242
|
Dynamic: license-file
|
|
246
243
|
|
|
247
244
|
<img src="https://github.com/MIC-DKFZ/napari-nninteractive/raw/main/imgs/nnInteractive_header.png" width="1200">
|
|
@@ -286,57 +283,68 @@ Extensive benchmarking demonstrates that nnInteractive far surpasses existing me
|
|
|
286
283
|
|
|
287
284
|
## Installation
|
|
288
285
|
|
|
289
|
-
|
|
286
|
+
`napari-nninteractive` installs as a **lightweight, torch-free remote client by default** — it can drive a remote `nninteractive-server` straight away, on any machine. **Local (in-process) inference is an opt-in extra** (`[local]`) that adds the full nnInteractive backend (PyTorch + nnU-Net) and needs an Nvidia GPU.
|
|
290
287
|
|
|
291
|
-
|
|
288
|
+
| Mode | Install | PyTorch? | Hardware |
|
|
289
|
+
|------|---------|----------|----------|
|
|
290
|
+
| **Remote client** (default) | `pip install napari-nninteractive` | **No** (lightweight, torch-free) | anything — laptop, Mac, no GPU |
|
|
291
|
+
| **Local inference** | `pip install "napari-nninteractive[local]"` (+ PyTorch) | **Yes** (large download) | Linux/Windows + Nvidia GPU — 10 GB VRAM recommended, \<6 GB works for small objects |
|
|
292
292
|
|
|
293
293
|
> [!TIP]
|
|
294
|
-
> **On a Mac, or without a capable Nvidia GPU?**
|
|
294
|
+
> **On a Mac, or without a capable Nvidia GPU?** The default install is already exactly what you want — stop after Step 2. nnInteractive relies heavily on 3D convolutions, which are prohibitively slow on Apple Silicon (MPS) and CPU hardware, so running the model on a remote GPU and driving it from napari is the recommended, and often only practical, way to use it on these machines. See [Remote inference (server / client)](#remote-inference-server--client) for how to set up and connect to the server.
|
|
295
295
|
|
|
296
|
-
|
|
296
|
+
### Step 1 — Create a virtual environment
|
|
297
297
|
|
|
298
|
-
nnInteractive supports Python 3.10+ and works with Conda, pip, or any other virtual environment. Here
|
|
298
|
+
nnInteractive supports Python 3.10+ and works with Conda, pip, or any other virtual environment. Here's an example using Conda:
|
|
299
299
|
|
|
300
|
-
```
|
|
300
|
+
```bash
|
|
301
301
|
conda create -n nnInteractive python=3.12
|
|
302
302
|
conda activate nnInteractive
|
|
303
303
|
```
|
|
304
304
|
|
|
305
|
-
|
|
305
|
+
Install napari if you don't already have it:
|
|
306
306
|
|
|
307
|
-
|
|
308
|
-
|
|
307
|
+
```bash
|
|
308
|
+
pip install napari[all]
|
|
309
|
+
```
|
|
309
310
|
|
|
310
|
-
|
|
311
|
+
### Step 2 — Install the plugin (remote client, torch-free)
|
|
311
312
|
|
|
312
|
-
```
|
|
313
|
-
|
|
313
|
+
```bash
|
|
314
|
+
pip install napari-nninteractive
|
|
314
315
|
```
|
|
315
316
|
|
|
316
|
-
|
|
317
|
+
This is the **entire** install if you only drive a remote `nninteractive-server`: no PyTorch, no GPU required. The plugin starts in Remote mode — see [Remote inference (server / client)](#remote-inference-server--client) to connect.
|
|
317
318
|
|
|
318
|
-
|
|
319
|
+
### Step 3 — (Optional) Enable local inference
|
|
320
|
+
|
|
321
|
+
Only needed if **this** machine runs the model itself. Requires a Linux or Windows computer with an Nvidia GPU.
|
|
322
|
+
|
|
323
|
+
**1. Install the correct PyTorch for your system.** Go to the [PyTorch homepage](https://pytorch.org/get-started/locally/) and pick the right configuration. PyTorch must be installed via pip nowadays, which is fine inside a conda environment. For Ubuntu with an Nvidia GPU, pick 'stable', 'Linux', 'Pip', 'Python', CUDA version does not matter (ensure your driver is up to date!). Then execute the command that is displayed, for example:
|
|
319
324
|
|
|
320
325
|
```bash
|
|
321
|
-
|
|
326
|
+
pip3 install torch torchvision --index-url https://download.pytorch.org/whl/cu126
|
|
322
327
|
```
|
|
323
328
|
|
|
324
|
-
|
|
329
|
+
**2. Add the `[local]` extra** (pulls the full nnInteractive backend + nnU-Net):
|
|
325
330
|
|
|
326
331
|
```bash
|
|
327
|
-
pip install napari-nninteractive
|
|
332
|
+
pip install "napari-nninteractive[local]"
|
|
328
333
|
```
|
|
329
334
|
|
|
330
|
-
|
|
335
|
+
Quote the brackets so your shell does not treat them as a glob (required in zsh / macOS). Model weights are downloaded automatically on first use; this can take a couple of minutes depending on your connection.
|
|
336
|
+
|
|
337
|
+
You can do this at **any** time: if you started with the default client-only install and the **Local** switch in the plugin is greyed out, just run the command above (after installing PyTorch) and restart napari to unlock local inference.
|
|
338
|
+
|
|
339
|
+
### Install from source (optional)
|
|
331
340
|
|
|
332
341
|
```bash
|
|
333
342
|
git clone https://github.com/MIC-DKFZ/napari-nninteractive
|
|
334
343
|
cd napari-nninteractive
|
|
335
|
-
pip install -e .
|
|
344
|
+
pip install -e . # remote client (torch-free) — the default
|
|
345
|
+
pip install -e ".[local]" # local inference — also install PyTorch first (see Step 3)
|
|
336
346
|
```
|
|
337
347
|
|
|
338
|
-
**Note:** Model weights are automatically downloaded on first use. This can take up to a couple of minutes depending on your internet connection
|
|
339
|
-
|
|
340
348
|
## Getting Started
|
|
341
349
|
|
|
342
350
|
Use one of these three options to start napari and activate the plugin.
|
|
@@ -401,6 +409,32 @@ Share the printed API key with your users via whatever channel you use for share
|
|
|
401
409
|
|
|
402
410
|
The model checkpoint is fixed by the server at startup, so the local checkpoint selector is hidden in Remote mode.
|
|
403
411
|
|
|
412
|
+
> [!NOTE]
|
|
413
|
+
> The **default install is the torch-free client**, so unless you added the `[local]` extra
|
|
414
|
+
> the plugin starts directly in Remote mode and the **Local** switch is greyed out. Hover the
|
|
415
|
+
> switch (or read the one-line notice printed on start-up) for the reason and the exact
|
|
416
|
+
> command. To unlock local inference, install PyTorch and the extra
|
|
417
|
+
> (`pip install "napari-nninteractive[local]"`; see Step 3 of [Installation](#installation) above),
|
|
418
|
+
> then restart napari.
|
|
419
|
+
|
|
420
|
+
### …or run the server in Docker
|
|
421
|
+
|
|
422
|
+
If you'd rather not install the backend on the GPU box, the server is also published as a Docker
|
|
423
|
+
image with the model **baked in** (a GPU host with the
|
|
424
|
+
[NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)
|
|
425
|
+
is required):
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
docker run --gpus all -p 1527:1527 \
|
|
429
|
+
-e NN_INTERACTIVE_API_KEY="$(openssl rand -hex 32)" \
|
|
430
|
+
ghcr.io/mic-dkfz/nninteractive-server:latest
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
A `lite` tag is also available if you'd rather mount your own checkpoint folder at `/model` (or
|
|
434
|
+
fetch a model by id at startup). See the backend
|
|
435
|
+
[DOCKER.md](https://github.com/MIC-DKFZ/nnInteractive/blob/master/nnInteractive/inference/server/DOCKER.md)
|
|
436
|
+
for both flavours and all configuration options.
|
|
437
|
+
|
|
404
438
|
### Things to be aware of
|
|
405
439
|
|
|
406
440
|
- **Lost connection or idle timeout.** Server-side sessions are reaped after 10 minutes of inactivity (configurable on the server), and a server that goes away (restart, crash, network drop) ends the session too. When this happens the plugin resets the **Connect** button and asks you to reconnect. **Your current segmentation is preserved** on the client: after you reconnect and click **Initialize** again, the image is re-uploaded and the segmentation is restored so you can keep refining where you left off. Only the in-progress prompt markers (the individual points/boxes/scribbles) need to be redone.
|
|
@@ -421,7 +455,7 @@ Link: [](https
|
|
|
421
455
|
|
|
422
456
|
# License
|
|
423
457
|
|
|
424
|
-
Note that while this repository is available under Apache-2.0 license (see [LICENSE](./LICENSE)), the [model checkpoint](https://huggingface.co/
|
|
458
|
+
Note that while this repository is available under Apache-2.0 license (see [LICENSE](./LICENSE)), the [model checkpoint](https://huggingface.co/MIC-DKFZ/nnInteractive) is `Creative Commons Attribution Non Commercial Share Alike 4.0`!
|
|
425
459
|
|
|
426
460
|
______________________________________________________________________
|
|
427
461
|
|
|
@@ -40,57 +40,68 @@ Extensive benchmarking demonstrates that nnInteractive far surpasses existing me
|
|
|
40
40
|
|
|
41
41
|
## Installation
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
`napari-nninteractive` installs as a **lightweight, torch-free remote client by default** — it can drive a remote `nninteractive-server` straight away, on any machine. **Local (in-process) inference is an opt-in extra** (`[local]`) that adds the full nnInteractive backend (PyTorch + nnU-Net) and needs an Nvidia GPU.
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
| Mode | Install | PyTorch? | Hardware |
|
|
46
|
+
|------|---------|----------|----------|
|
|
47
|
+
| **Remote client** (default) | `pip install napari-nninteractive` | **No** (lightweight, torch-free) | anything — laptop, Mac, no GPU |
|
|
48
|
+
| **Local inference** | `pip install "napari-nninteractive[local]"` (+ PyTorch) | **Yes** (large download) | Linux/Windows + Nvidia GPU — 10 GB VRAM recommended, \<6 GB works for small objects |
|
|
46
49
|
|
|
47
50
|
> [!TIP]
|
|
48
|
-
> **On a Mac, or without a capable Nvidia GPU?**
|
|
51
|
+
> **On a Mac, or without a capable Nvidia GPU?** The default install is already exactly what you want — stop after Step 2. nnInteractive relies heavily on 3D convolutions, which are prohibitively slow on Apple Silicon (MPS) and CPU hardware, so running the model on a remote GPU and driving it from napari is the recommended, and often only practical, way to use it on these machines. See [Remote inference (server / client)](#remote-inference-server--client) for how to set up and connect to the server.
|
|
49
52
|
|
|
50
|
-
|
|
53
|
+
### Step 1 — Create a virtual environment
|
|
51
54
|
|
|
52
|
-
nnInteractive supports Python 3.10+ and works with Conda, pip, or any other virtual environment. Here
|
|
55
|
+
nnInteractive supports Python 3.10+ and works with Conda, pip, or any other virtual environment. Here's an example using Conda:
|
|
53
56
|
|
|
54
|
-
```
|
|
57
|
+
```bash
|
|
55
58
|
conda create -n nnInteractive python=3.12
|
|
56
59
|
conda activate nnInteractive
|
|
57
60
|
```
|
|
58
61
|
|
|
59
|
-
|
|
62
|
+
Install napari if you don't already have it:
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
```bash
|
|
65
|
+
pip install napari[all]
|
|
66
|
+
```
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
### Step 2 — Install the plugin (remote client, torch-free)
|
|
65
69
|
|
|
66
|
-
```
|
|
67
|
-
|
|
70
|
+
```bash
|
|
71
|
+
pip install napari-nninteractive
|
|
68
72
|
```
|
|
69
73
|
|
|
70
|
-
|
|
74
|
+
This is the **entire** install if you only drive a remote `nninteractive-server`: no PyTorch, no GPU required. The plugin starts in Remote mode — see [Remote inference (server / client)](#remote-inference-server--client) to connect.
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
### Step 3 — (Optional) Enable local inference
|
|
77
|
+
|
|
78
|
+
Only needed if **this** machine runs the model itself. Requires a Linux or Windows computer with an Nvidia GPU.
|
|
79
|
+
|
|
80
|
+
**1. Install the correct PyTorch for your system.** Go to the [PyTorch homepage](https://pytorch.org/get-started/locally/) and pick the right configuration. PyTorch must be installed via pip nowadays, which is fine inside a conda environment. For Ubuntu with an Nvidia GPU, pick 'stable', 'Linux', 'Pip', 'Python', CUDA version does not matter (ensure your driver is up to date!). Then execute the command that is displayed, for example:
|
|
73
81
|
|
|
74
82
|
```bash
|
|
75
|
-
|
|
83
|
+
pip3 install torch torchvision --index-url https://download.pytorch.org/whl/cu126
|
|
76
84
|
```
|
|
77
85
|
|
|
78
|
-
|
|
86
|
+
**2. Add the `[local]` extra** (pulls the full nnInteractive backend + nnU-Net):
|
|
79
87
|
|
|
80
88
|
```bash
|
|
81
|
-
pip install napari-nninteractive
|
|
89
|
+
pip install "napari-nninteractive[local]"
|
|
82
90
|
```
|
|
83
91
|
|
|
84
|
-
|
|
92
|
+
Quote the brackets so your shell does not treat them as a glob (required in zsh / macOS). Model weights are downloaded automatically on first use; this can take a couple of minutes depending on your connection.
|
|
93
|
+
|
|
94
|
+
You can do this at **any** time: if you started with the default client-only install and the **Local** switch in the plugin is greyed out, just run the command above (after installing PyTorch) and restart napari to unlock local inference.
|
|
95
|
+
|
|
96
|
+
### Install from source (optional)
|
|
85
97
|
|
|
86
98
|
```bash
|
|
87
99
|
git clone https://github.com/MIC-DKFZ/napari-nninteractive
|
|
88
100
|
cd napari-nninteractive
|
|
89
|
-
pip install -e .
|
|
101
|
+
pip install -e . # remote client (torch-free) — the default
|
|
102
|
+
pip install -e ".[local]" # local inference — also install PyTorch first (see Step 3)
|
|
90
103
|
```
|
|
91
104
|
|
|
92
|
-
**Note:** Model weights are automatically downloaded on first use. This can take up to a couple of minutes depending on your internet connection
|
|
93
|
-
|
|
94
105
|
## Getting Started
|
|
95
106
|
|
|
96
107
|
Use one of these three options to start napari and activate the plugin.
|
|
@@ -155,6 +166,32 @@ Share the printed API key with your users via whatever channel you use for share
|
|
|
155
166
|
|
|
156
167
|
The model checkpoint is fixed by the server at startup, so the local checkpoint selector is hidden in Remote mode.
|
|
157
168
|
|
|
169
|
+
> [!NOTE]
|
|
170
|
+
> The **default install is the torch-free client**, so unless you added the `[local]` extra
|
|
171
|
+
> the plugin starts directly in Remote mode and the **Local** switch is greyed out. Hover the
|
|
172
|
+
> switch (or read the one-line notice printed on start-up) for the reason and the exact
|
|
173
|
+
> command. To unlock local inference, install PyTorch and the extra
|
|
174
|
+
> (`pip install "napari-nninteractive[local]"`; see Step 3 of [Installation](#installation) above),
|
|
175
|
+
> then restart napari.
|
|
176
|
+
|
|
177
|
+
### …or run the server in Docker
|
|
178
|
+
|
|
179
|
+
If you'd rather not install the backend on the GPU box, the server is also published as a Docker
|
|
180
|
+
image with the model **baked in** (a GPU host with the
|
|
181
|
+
[NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)
|
|
182
|
+
is required):
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
docker run --gpus all -p 1527:1527 \
|
|
186
|
+
-e NN_INTERACTIVE_API_KEY="$(openssl rand -hex 32)" \
|
|
187
|
+
ghcr.io/mic-dkfz/nninteractive-server:latest
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
A `lite` tag is also available if you'd rather mount your own checkpoint folder at `/model` (or
|
|
191
|
+
fetch a model by id at startup). See the backend
|
|
192
|
+
[DOCKER.md](https://github.com/MIC-DKFZ/nnInteractive/blob/master/nnInteractive/inference/server/DOCKER.md)
|
|
193
|
+
for both flavours and all configuration options.
|
|
194
|
+
|
|
158
195
|
### Things to be aware of
|
|
159
196
|
|
|
160
197
|
- **Lost connection or idle timeout.** Server-side sessions are reaped after 10 minutes of inactivity (configurable on the server), and a server that goes away (restart, crash, network drop) ends the session too. When this happens the plugin resets the **Connect** button and asks you to reconnect. **Your current segmentation is preserved** on the client: after you reconnect and click **Initialize** again, the image is re-uploaded and the segmentation is restored so you can keep refining where you left off. Only the in-progress prompt markers (the individual points/boxes/scribbles) need to be redone.
|
|
@@ -175,7 +212,7 @@ Link: [](https
|
|
|
175
212
|
|
|
176
213
|
# License
|
|
177
214
|
|
|
178
|
-
Note that while this repository is available under Apache-2.0 license (see [LICENSE](./LICENSE)), the [model checkpoint](https://huggingface.co/
|
|
215
|
+
Note that while this repository is available under Apache-2.0 license (see [LICENSE](./LICENSE)), the [model checkpoint](https://huggingface.co/MIC-DKFZ/nnInteractive) is `Creative Commons Attribution Non Commercial Share Alike 4.0`!
|
|
179
216
|
|
|
180
217
|
______________________________________________________________________
|
|
181
218
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "napari-nninteractive"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.4.2"
|
|
4
4
|
description = "nnInteractive plugin for Napari"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = {file = "LICENSE"}
|
|
@@ -26,17 +26,30 @@ classifiers = [
|
|
|
26
26
|
]
|
|
27
27
|
requires-python = ">=3.10"
|
|
28
28
|
dependencies = [
|
|
29
|
-
|
|
29
|
+
# The default install is the lightweight, torch-free REMOTE client: it can drive
|
|
30
|
+
# a remote nninteractive-server out of the box and pulls no PyTorch. For local
|
|
31
|
+
# inference install the [local] extra (see below / the README), which adds the
|
|
32
|
+
# full nnInteractive backend (torch + nnU-Net). pip extras can only ADD
|
|
33
|
+
# dependencies, never subtract, so local is the opt-in extra rather than
|
|
34
|
+
# something a remote-only user has to strip out of a torch-heavy base.
|
|
30
35
|
"numpy",
|
|
31
36
|
"qtpy",
|
|
32
37
|
"napari-nifti",
|
|
33
|
-
"huggingface_hub",
|
|
34
|
-
"hf_transfer",
|
|
35
|
-
"nnInteractive>=2.4.0",
|
|
36
38
|
"napari_toolkit",
|
|
39
|
+
# Torch-free remote client + shared wire protocol (pulls numpy/httpx/blosc2).
|
|
40
|
+
# Provides nnInteractive.inference.remote; the full nnInteractive package (added
|
|
41
|
+
# by [local]) depends on this same client, so the two coexist cleanly.
|
|
42
|
+
"nninteractive-client>=2.5.0",
|
|
37
43
|
]
|
|
38
44
|
|
|
39
45
|
[project.optional-dependencies]
|
|
46
|
+
# Local (in-process) inference. Adds the full nnInteractive backend, which pulls
|
|
47
|
+
# PyTorch + nnU-Net and provides model discovery/download. On a GPU machine install
|
|
48
|
+
# the matching CUDA build of torch first (see the README), then:
|
|
49
|
+
# pip install "napari-nninteractive[local]"
|
|
50
|
+
local = [
|
|
51
|
+
"nnInteractive>=2.5.0",
|
|
52
|
+
]
|
|
40
53
|
testing = [
|
|
41
54
|
"tox",
|
|
42
55
|
"pytest", # https://docs.pytest.org/en/latest/contents.html
|
|
@@ -45,9 +58,6 @@ testing = [
|
|
|
45
58
|
"napari",
|
|
46
59
|
"pyqt5",
|
|
47
60
|
]
|
|
48
|
-
remote = [
|
|
49
|
-
"nnInteractive[client]>=2.4.0",
|
|
50
|
-
]
|
|
51
61
|
|
|
52
62
|
[project.entry-points."napari.manifest"]
|
|
53
63
|
napari-nninteractive = "napari_nninteractive:napari.yaml"
|
|
@@ -48,7 +48,18 @@ class ScribbleLayer(BaseLayerClass, Labels):
|
|
|
48
48
|
"""
|
|
49
49
|
Finalizes the current scribble interaction, updating the label index and marking the layer as free.
|
|
50
50
|
"""
|
|
51
|
-
|
|
51
|
+
# Recolor the just-drawn stroke (value 1 -> committed prompt value) on the
|
|
52
|
+
# painted slice only. The stroke never spans more than the last painted slice
|
|
53
|
+
# (on_draw undoes any pending stroke before a new one, and _commit_staged_history
|
|
54
|
+
# records that slice), so this avoids scanning/writing the whole D×H×W volume.
|
|
55
|
+
if self._last_slice_id is not None:
|
|
56
|
+
idx = [slice(None)] * 3
|
|
57
|
+
idx[self._last_dim_not_displayed] = self._last_slice_id
|
|
58
|
+
slice_view = self.data[tuple(idx)] # integer index on one axis -> a view
|
|
59
|
+
slice_view[slice_view == 1] = self.prompt_index + 2 # in-place, writes back to self.data
|
|
60
|
+
else:
|
|
61
|
+
# Defensive fallback: run is normally called only after a commit recorded a slice.
|
|
62
|
+
self.data[self.data == 1] = self.prompt_index + 2
|
|
52
63
|
self._is_free = True
|
|
53
64
|
self.refresh()
|
|
54
65
|
|
|
@@ -4,7 +4,6 @@ from pathlib import Path
|
|
|
4
4
|
from typing import Any, Optional
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
|
-
from huggingface_hub import snapshot_download
|
|
8
7
|
from napari._qt.layer_controls.qt_layer_controls_container import layer_to_controls
|
|
9
8
|
from napari.layers import Labels
|
|
10
9
|
from napari.layers.base._base_constants import ActionType
|
|
@@ -233,22 +232,25 @@ class LayerControls(BaseGUI):
|
|
|
233
232
|
self.checkpoint_path = None
|
|
234
233
|
print(f"Using remote model at: {self.server_url_edit.text().strip()}")
|
|
235
234
|
else:
|
|
236
|
-
model_name = self.model_selection.currentText()
|
|
237
235
|
model_name_local = self.model_selection_local.text()
|
|
238
236
|
if model_name_local != "" and Path(model_name_local).exists():
|
|
239
237
|
# Use Local Checkpoint
|
|
240
238
|
model_name = Path(model_name_local).name
|
|
241
239
|
self.checkpoint_path = model_name_local
|
|
242
240
|
else:
|
|
243
|
-
#
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
241
|
+
# Resolve the selected official model through the nnInteractive backend.
|
|
242
|
+
# It downloads the model on first use, reuses it afterwards, and works
|
|
243
|
+
# offline once a model has been downloaded.
|
|
244
|
+
from nnInteractive.model_management import ensure_model_available
|
|
245
|
+
|
|
246
|
+
idx = self.model_selection.currentIndex()
|
|
247
|
+
if idx < 0 or idx >= len(self._model_ids):
|
|
248
|
+
raise ValueError(
|
|
249
|
+
"No model selected. Pick a model from the dropdown or set a local "
|
|
250
|
+
"checkpoint path."
|
|
251
|
+
)
|
|
252
|
+
model_name = self._model_ids[idx]
|
|
253
|
+
self.checkpoint_path = ensure_model_available(model_name)
|
|
252
254
|
print(f"Using Model {model_name} at : {self.checkpoint_path}")
|
|
253
255
|
|
|
254
256
|
# --- DATA HANDLING --- #
|
{napari_nninteractive-2.3.2 → napari_nninteractive-2.4.2}/src/napari_nninteractive/widget_gui.py
RENAMED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import warnings
|
|
1
3
|
from typing import Optional
|
|
2
4
|
|
|
3
5
|
from napari.layers import Image, Labels
|
|
@@ -24,6 +26,7 @@ from qtpy.QtWidgets import (
|
|
|
24
26
|
QHBoxLayout,
|
|
25
27
|
QLabel,
|
|
26
28
|
QLineEdit,
|
|
29
|
+
QMessageBox,
|
|
27
30
|
QShortcut,
|
|
28
31
|
QSizePolicy,
|
|
29
32
|
QVBoxLayout,
|
|
@@ -32,6 +35,47 @@ from qtpy.QtWidgets import (
|
|
|
32
35
|
|
|
33
36
|
from napari_nninteractive._version_check import VersionChecker, _is_outdated
|
|
34
37
|
|
|
38
|
+
# Shared wording for the Local tooltip, the start-up notice, and the dialog shown
|
|
39
|
+
# when the user clicks the (greyed) Local switch. Kept as a reason + a how-to so the
|
|
40
|
+
# dialog can present them as headline + details without repeating itself; the tooltip
|
|
41
|
+
# and console print use the two joined. The lightweight ``nninteractive-client``
|
|
42
|
+
# distribution is remote-only and torch-free; local inference lives in the full
|
|
43
|
+
# ``nnInteractive`` package.
|
|
44
|
+
_REMOTE_ONLY_REASON = (
|
|
45
|
+
"Local inference is unavailable: this is a remote-only (client-only) install "
|
|
46
|
+
"without the local nnInteractive backend, so only a remote nninteractive-server "
|
|
47
|
+
"can be used."
|
|
48
|
+
)
|
|
49
|
+
_ENABLE_LOCAL_STEPS = (
|
|
50
|
+
"To enable local inference (needs an Nvidia GPU):\n"
|
|
51
|
+
" 1. Install PyTorch yourself — it is NOT installed automatically. Pick the build "
|
|
52
|
+
"matching your GPU/CUDA from https://pytorch.org/get-started/locally/\n"
|
|
53
|
+
' 2. pip install "napari-nninteractive[local]"\n'
|
|
54
|
+
"then restart napari. See the README for details."
|
|
55
|
+
)
|
|
56
|
+
_REMOTE_ONLY_HINT = f"{_REMOTE_ONLY_REASON}\n{_ENABLE_LOCAL_STEPS}"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _local_inference_available() -> bool:
|
|
60
|
+
"""Return True if local inference is installed.
|
|
61
|
+
|
|
62
|
+
The lightweight ``nninteractive-client`` ships only the remote client; the
|
|
63
|
+
local engine and its torch / nnU-Net stack live in the full ``nnInteractive``
|
|
64
|
+
package. We probe for the local inference module *without* importing torch:
|
|
65
|
+
``find_spec`` only locates the module. In a client-only environment the full
|
|
66
|
+
package's last-resort meta-path finder raises ``ModuleNotFoundError`` here
|
|
67
|
+
(see ``nnInteractive.inference.remote._full_required``), which we treat as
|
|
68
|
+
"not available".
|
|
69
|
+
"""
|
|
70
|
+
try:
|
|
71
|
+
return (
|
|
72
|
+
importlib.util.find_spec("nnInteractive.inference.inference_session") is not None
|
|
73
|
+
)
|
|
74
|
+
except ModuleNotFoundError:
|
|
75
|
+
return False
|
|
76
|
+
except Exception: # noqa: BLE001 - never block the GUI on a capability probe
|
|
77
|
+
return False
|
|
78
|
+
|
|
35
79
|
|
|
36
80
|
class BaseGUI(QWidget):
|
|
37
81
|
"""
|
|
@@ -49,9 +93,18 @@ class BaseGUI(QWidget):
|
|
|
49
93
|
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
|
50
94
|
self._viewer = viewer
|
|
51
95
|
self.session_cfg = None
|
|
52
|
-
|
|
96
|
+
# Whether local inference is installed. A remote-only
|
|
97
|
+
# 'nninteractive-client' install cannot run it, so we start in Remote
|
|
98
|
+
# mode and disable the Local controls (see _init_model_selection).
|
|
99
|
+
self._local_available = _local_inference_available()
|
|
100
|
+
self._remote_mode = not self._local_available
|
|
53
101
|
self._settings = QSettings("MIC-DKFZ", "napari-nninteractive")
|
|
54
102
|
|
|
103
|
+
# Be transparent on start-up: tell remote-only users why local inference
|
|
104
|
+
# is off and how to enable it, so a missing Local button is never a mystery.
|
|
105
|
+
if not self._local_available:
|
|
106
|
+
print(f"[napari-nninteractive] {_REMOTE_ONLY_HINT}")
|
|
107
|
+
|
|
55
108
|
_main_layout = QVBoxLayout()
|
|
56
109
|
self.setLayout(_main_layout)
|
|
57
110
|
|
|
@@ -114,9 +167,14 @@ class BaseGUI(QWidget):
|
|
|
114
167
|
|
|
115
168
|
self.version_status_label.setVisible(True)
|
|
116
169
|
if outdated:
|
|
170
|
+
# Name only the package(s) actually behind PyPI, and build the upgrade
|
|
171
|
+
# command from those alone. A package whose installed version is newer
|
|
172
|
+
# than the latest release (e.g. a dev/unreleased build) is never listed,
|
|
173
|
+
# so we don't wrongly tell the user to "update" something already ahead.
|
|
174
|
+
names = ", ".join(outdated)
|
|
117
175
|
self.version_status_label.setText(
|
|
118
|
-
"Update available. Please run:\n"
|
|
119
|
-
"pip install -U
|
|
176
|
+
f"Update available for {names}. Please run:\n"
|
|
177
|
+
f"pip install -U {' '.join(outdated)}"
|
|
120
178
|
)
|
|
121
179
|
self.version_status_label.setStyleSheet("color: #e8830c; font-weight: bold;") # orange
|
|
122
180
|
else:
|
|
@@ -193,16 +251,29 @@ class BaseGUI(QWidget):
|
|
|
193
251
|
"""Initializes the model selection as a combo box."""
|
|
194
252
|
_group_box, _layout = setup_vgroupbox(text="Model Selection:")
|
|
195
253
|
|
|
196
|
-
# Local | Remote mode switch
|
|
254
|
+
# Local | Remote mode switch. Start on Remote when local inference is not
|
|
255
|
+
# installed, since Local cannot be used in that case.
|
|
197
256
|
self.mode_switch = setup_hswitch(
|
|
198
257
|
_layout,
|
|
199
258
|
options=["Local", "Remote"],
|
|
200
259
|
function=self.on_mode_switched,
|
|
201
|
-
default=0,
|
|
260
|
+
default=0 if self._local_available else 1,
|
|
202
261
|
fixed_color="rgb(0,100, 167)",
|
|
203
262
|
tooltips="Run inference locally or on a remote nninteractive-server",
|
|
204
263
|
)
|
|
205
264
|
|
|
265
|
+
# Remote-only install: local inference is not installed. Keep the Local
|
|
266
|
+
# button enabled — a *disabled* button swallows clicks and can show no
|
|
267
|
+
# feedback — but selecting it pops an explanatory dialog and snaps the
|
|
268
|
+
# switch back to Remote (handled in on_mode_switched). The tooltip explains
|
|
269
|
+
# why local is unavailable and how to enable it.
|
|
270
|
+
if not self._local_available:
|
|
271
|
+
self.mode_switch.buttons[0].setToolTip(_REMOTE_ONLY_HINT)
|
|
272
|
+
self.mode_switch.buttons[1].setToolTip(
|
|
273
|
+
"Run inference on a remote nninteractive-server"
|
|
274
|
+
)
|
|
275
|
+
self._grey_local_switch_button()
|
|
276
|
+
|
|
206
277
|
# --- Local container --- #
|
|
207
278
|
self.local_container = QWidget()
|
|
208
279
|
_local_layout = QVBoxLayout()
|
|
@@ -210,11 +281,43 @@ class BaseGUI(QWidget):
|
|
|
210
281
|
self.local_container.setLayout(_local_layout)
|
|
211
282
|
_layout.addWidget(self.local_container)
|
|
212
283
|
|
|
213
|
-
|
|
284
|
+
# Populate the model dropdown from the nnInteractive backend manifest — the
|
|
285
|
+
# authoritative list of selectable official models, fetched from Hugging Face
|
|
286
|
+
# (remote-first) with an offline cache fallback. The dropdown shows display
|
|
287
|
+
# names; self._model_ids keeps the matching manifest ids by position. If the
|
|
288
|
+
# list can't be loaded at all, the dropdown is left empty and the user can
|
|
289
|
+
# still point to a local checkpoint below or switch to Remote mode.
|
|
290
|
+
self._model_ids: list[str] = []
|
|
291
|
+
model_display_names: list[str] = []
|
|
292
|
+
default_index = 0
|
|
293
|
+
# The model dropdown only drives local inference (in Remote mode the server
|
|
294
|
+
# picks the model), and model discovery lives in the full package. A
|
|
295
|
+
# remote-only install therefore skips loading the list entirely rather than
|
|
296
|
+
# triggering the full-package-required error.
|
|
297
|
+
if self._local_available:
|
|
298
|
+
try:
|
|
299
|
+
from nnInteractive.model_management import get_default_model_id, list_models
|
|
300
|
+
|
|
301
|
+
models = list_models()
|
|
302
|
+
self._model_ids = [m["id"] for m in models]
|
|
303
|
+
model_display_names = [m.get("display_name", m["id"]) for m in models]
|
|
304
|
+
default_id = get_default_model_id()
|
|
305
|
+
if default_id in self._model_ids:
|
|
306
|
+
default_index = self._model_ids.index(default_id)
|
|
307
|
+
except Exception as exc: # noqa: BLE001 - never block the GUI on model discovery
|
|
308
|
+
warnings.warn(
|
|
309
|
+
f"Could not load the nnInteractive model list: {exc}", stacklevel=2
|
|
310
|
+
)
|
|
214
311
|
|
|
215
312
|
self.model_selection = setup_combobox(
|
|
216
|
-
_local_layout, options=
|
|
313
|
+
_local_layout, options=model_display_names, function=self.on_model_selected
|
|
217
314
|
)
|
|
315
|
+
if model_display_names:
|
|
316
|
+
# Select the manifest default without firing on_model_selected during init
|
|
317
|
+
# (the handler touches subclass state that isn't built yet).
|
|
318
|
+
self.model_selection.blockSignals(True)
|
|
319
|
+
self.model_selection.setCurrentIndex(default_index)
|
|
320
|
+
self.model_selection.blockSignals(False)
|
|
218
321
|
|
|
219
322
|
_boxlayout = QHBoxLayout()
|
|
220
323
|
_local_layout.addLayout(_boxlayout)
|
|
@@ -300,8 +403,13 @@ class BaseGUI(QWidget):
|
|
|
300
403
|
self.remote_status_label.setWordWrap(True)
|
|
301
404
|
_remote_layout.addWidget(self.remote_status_label)
|
|
302
405
|
|
|
303
|
-
#
|
|
304
|
-
|
|
406
|
+
# Show the container for the active mode. A remote-only install starts in
|
|
407
|
+
# Remote mode; its Local controls are hidden and disabled (the menus that
|
|
408
|
+
# belong to the now-greyed Local button).
|
|
409
|
+
self.local_container.setVisible(not self._remote_mode)
|
|
410
|
+
self.remote_container.setVisible(self._remote_mode)
|
|
411
|
+
if not self._local_available:
|
|
412
|
+
self.local_container.setEnabled(False)
|
|
305
413
|
|
|
306
414
|
# Restore last-used values (blocking signals so we don't trigger
|
|
307
415
|
# on_model_selected / on_remote_settings_changed before the rest of
|
|
@@ -349,6 +457,31 @@ class BaseGUI(QWidget):
|
|
|
349
457
|
_group_box.setLayout(_layout)
|
|
350
458
|
return _group_box
|
|
351
459
|
|
|
460
|
+
def _grey_local_switch_button(self) -> None:
|
|
461
|
+
"""Lightly grey the Local switch button so it reads as unavailable while
|
|
462
|
+
staying clickable (clicking it explains how to enable local inference).
|
|
463
|
+
|
|
464
|
+
The switch resets each button's stylesheet whenever it toggles, so this is
|
|
465
|
+
re-applied after switch changes (see on_mode_switched).
|
|
466
|
+
"""
|
|
467
|
+
self.mode_switch.buttons[0].setStyleSheet("color: gray;")
|
|
468
|
+
|
|
469
|
+
def _show_local_unavailable_dialog(self) -> None:
|
|
470
|
+
"""Explain why Local is unavailable and how to enable local inference.
|
|
471
|
+
|
|
472
|
+
Shown when the user clicks the greyed (but still clickable) Local switch.
|
|
473
|
+
Deliberately offers no one-click install: the correct PyTorch build depends
|
|
474
|
+
on the user's GPU/CUDA and must be installed manually first (see the steps).
|
|
475
|
+
"""
|
|
476
|
+
box = QMessageBox(self)
|
|
477
|
+
box.setIcon(QMessageBox.Information)
|
|
478
|
+
box.setWindowTitle("Local inference not installed")
|
|
479
|
+
box.setText(_REMOTE_ONLY_REASON)
|
|
480
|
+
box.setInformativeText(_ENABLE_LOCAL_STEPS)
|
|
481
|
+
box.addButton(QMessageBox.Close)
|
|
482
|
+
box.setDefaultButton(QMessageBox.Close)
|
|
483
|
+
box.exec()
|
|
484
|
+
|
|
352
485
|
def _init_image_selection(self) -> QGroupBox:
|
|
353
486
|
"""Initializes the image selection combo box in a group box."""
|
|
354
487
|
_group_box, _layout = setup_vgroupbox(text="Image Selection:")
|