napari-tmidas 0.1.8.5__tar.gz → 0.2.0__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_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/PKG-INFO +18 -45
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/README.md +16 -43
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/pyproject.toml +1 -1
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_label_inspection.py +94 -47
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_version.py +2 -2
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/basic.py +60 -0
- napari_tmidas-0.2.0/src/napari_tmidas/processing_functions/sam2_env_manager.py +111 -0
- napari_tmidas-0.2.0/src/napari_tmidas/processing_functions/skimage_filters.py +427 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas.egg-info/PKG-INFO +18 -45
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas.egg-info/SOURCES.txt +1 -0
- napari_tmidas-0.1.8.5/src/napari_tmidas/processing_functions/skimage_filters.py +0 -134
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/.github/dependabot.yml +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/.github/workflows/test_and_deploy.yml +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/.gitignore +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/.napari-hub/DESCRIPTION.md +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/.napari-hub/config.yml +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/.pre-commit-config.yaml +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/LICENSE +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/MANIFEST.in +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/setup.cfg +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/__init__.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_crop_anything.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_file_conversion.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_file_selector.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_reader.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_registry.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_roi_colocalization.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_sample_data.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_tests/__init__.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_tests/test_reader.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_tests/test_sample_data.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_tests/test_widget.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_tests/test_writer.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_widget.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_writer.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/napari.yaml +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/__init__.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/cellpose_env_manager.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/cellpose_segmentation.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/colocalization.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/file_compression.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/scipy_filters.py +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas.egg-info/dependency_links.txt +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas.egg-info/entry_points.txt +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas.egg-info/requires.txt +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas.egg-info/top_level.txt +0 -0
- {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/tox.ini +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: napari-tmidas
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: A plugin for batch processing of confocal microscopy images
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A plugin for batch processing of confocal and whole-slide microscopy images of biological tissues
|
|
5
5
|
Author: Marco Meer
|
|
6
6
|
Author-email: marco.meer@pm.me
|
|
7
7
|
License:
|
|
@@ -83,42 +83,14 @@ Dynamic: license-file
|
|
|
83
83
|
[](https://github.com/macromeer/napari-tmidas/actions)
|
|
84
84
|
[](https://napari-hub.org/plugins/napari-tmidas)
|
|
85
85
|
<!-- [](https://codecov.io/gh/macromeer/napari-tmidas) -->
|
|
86
|
-
The `napari-tmidas` plugin consists of a growing collection of pipelines for fast batch processing of microscopy images. This is a WIP and based on the CLI version of [T-MIDAS](https://github.com/MercaderLabAnatomy/T-MIDAS).
|
|
87
|
-
|
|
88
|
-
## Feature Overview
|
|
89
|
-
|
|
90
|
-
1. **Image Processing**
|
|
91
|
-
- Process image folders with:
|
|
92
|
-
- Gamma correction & histogram equalization
|
|
93
|
-
- Z-projection and channel splitting
|
|
94
|
-
- Gaussian & median filters
|
|
95
|
-
- Thresholding (Otsu/manual)
|
|
96
|
-
- Label cleaning & binary conversion
|
|
97
|
-
- RGB to labels conversion
|
|
98
|
-
- Cellpose 3.0 automated segmentation
|
|
99
|
-
- File compression (Zstandard)
|
|
100
|
-
|
|
101
|
-
2. **Label Inspection**
|
|
102
|
-
- Review and edit label images with auto-save
|
|
103
|
-
|
|
104
|
-
3. **Microscopy Image Conversion**
|
|
105
|
-
- Convert .nd2/.lif/.ndpi/.czi/acquifer → .tif/.zarr with metadata preservation
|
|
106
|
-
|
|
107
|
-
4. **Crop Anything**
|
|
108
|
-
- Interactive ROI selection via click interface
|
|
109
|
-
|
|
110
|
-
5. **ROI Colocalization**
|
|
111
|
-
- Count colocalized labels across multiple channels
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
### Coming Soon
|
|
116
|
-
New features arriving April 2025
|
|
86
|
+
The `napari-tmidas` plugin consists of a growing collection of pipelines for fast batch processing of confocal and whole slide microscopy images of biological tissues. This is a WIP and based on the CLI version of [T-MIDAS](https://github.com/MercaderLabAnatomy/T-MIDAS).
|
|
117
87
|
|
|
88
|
+
## Features
|
|
89
|
+
Currently, napari-tmidas provides pipelines as widgets for batch image conversion / cropping / processing, ROI colocalization and label inspection (cf. [Usage](#usage) below).
|
|
118
90
|
|
|
119
91
|
## Installation
|
|
120
92
|
|
|
121
|
-
First install Napari in a virtual environment:
|
|
93
|
+
First, install Napari in a virtual environment:
|
|
122
94
|
|
|
123
95
|
mamba create -y -n napari-tmidas -c conda-forge python=3.11 tqdm
|
|
124
96
|
mamba activate napari-tmidas
|
|
@@ -156,16 +128,15 @@ To use the plugin, start napari in the activated virtual environment with this t
|
|
|
156
128
|
|
|
157
129
|
mamba run -n napari-tmidas napari
|
|
158
130
|
|
|
159
|
-
You can find the installed plugin
|
|
160
|
-
|
|
161
|
-

|
|
162
|
-
|
|
131
|
+
You can then find the installed plugin in the Plugins tab.
|
|
163
132
|
|
|
164
133
|
### Microscopy Image Conversion
|
|
165
134
|
|
|
166
135
|
You can start this pipeline via `Plugins > T-MIDAS > Batch Microscopy Image Conversion`. Currently, this pipeline supports the conversion of `.nd2, .lif, .ndpi, .czi` and acquifer data. After scanning a folder of your choice for microscopy image data, select a file in the first column of the table and preview and export any image data it contains.
|
|
167
136
|
|
|
168
|
-
|
|
137
|
+
|
|
138
|
+
<img src="https://github.com/user-attachments/assets/e377ca71-2f30-447d-825e-d2feebf7061b" alt="Microscopy Image Conversion Widget" style="width:75%; height:auto;">
|
|
139
|
+
|
|
169
140
|
|
|
170
141
|
### Image Processing
|
|
171
142
|
|
|
@@ -173,7 +144,7 @@ You can start this pipeline via `Plugins > T-MIDAS > Batch Microscopy Image Conv
|
|
|
173
144
|
|
|
174
145
|

|
|
175
146
|
|
|
176
|
-
2. As a result, a table appears with the found images.
|
|
147
|
+
2. As a result, a table appears with the found images. You can click on them to inspect them in the viewer.
|
|
177
148
|
|
|
178
149
|

|
|
179
150
|
|
|
@@ -182,26 +153,28 @@ You can start this pipeline via `Plugins > T-MIDAS > Batch Microscopy Image Conv
|
|
|
182
153
|

|
|
183
154
|
|
|
184
155
|
4. You can click on the images in the table to show them in the viewer. For example first click on one of the `Original Files`, and then the corresponding `Processed File` to see an overlay.
|
|
156
|
+
|
|
157
|
+
<img src="https://github.com/user-attachments/assets/cfe84828-c1cc-4196-9a53-5dfb82d5bfce" alt="Image Processing Widget" style="width:75%; height:auto;">
|
|
185
158
|
|
|
186
|
-

|
|
187
159
|
|
|
188
160
|
Note that whenever you click on an `Original File` or `Processed File` in the table, it will replace the one that is currently shown in the viewer. So naturally, you'd first select the original image, and then the processed image to correctly see the image pair that you want to inspect.
|
|
189
161
|
|
|
190
162
|
### Batch Label Inspection
|
|
191
163
|
If you have already segmented a folder full of images and now you want to maybe inspect and edit each label image, you can use the `Plugins > T-MIDAS > Batch Label Inspection`, which automatically saves your changes to the existing label image once you click the `Save Changes and Continue` button (bottom right).
|
|
192
164
|
|
|
193
|
-
|
|
165
|
+
<img src="https://github.com/user-attachments/assets/0bf8c6ae-4212-449d-8183-e91b23ba740e" alt="Batch Label Inspection Widget" style="width:75%; height:auto;">
|
|
166
|
+
|
|
194
167
|
|
|
195
168
|
### Crop Anything
|
|
196
169
|
This pipeline combines the Segment Anything Model (SAM) for automatic object detection with an interactive interface for selecting and cropping multiple objects from images. To launch the widget, open `Plugins > T-MIDAS > Batch Crop Anything`. Click the image below to see a video demo.
|
|
197
170
|
|
|
198
|
-
|
|
171
|
+
<img src="https://github.com/user-attachments/assets/6d72c2a2-1064-4a27-b398-a9b86fcbc443" alt="Crop Anything Widget" style="width:75%; height:auto;">
|
|
172
|
+
|
|
199
173
|
|
|
200
174
|
### ROI Colocalization
|
|
201
175
|
This pipeline quantifies colocalization between labeled regions of interest (ROIs) across multiple image channels. It determines the extent of overlap between ROIs in a reference channel and those in one or two other channels. The output is a table of colocalization counts. Optionally, the size of reference channel ROIs, as well as the total or median size of colocalizing ROIs in the other channels, can be included. Colocalization is determined using Boolean masking. The number of colocalizing instances is determined by counting unique label IDs within the overlapping regions. Typically, the reference channel contains larger structures, while other channels contain smaller, potentially nested, structures. For example, the reference channel might contain cell bodies, with the second and third channels containing nuclei and sub-nuclear objects, respectively.
|
|
202
176
|
|
|
203
|
-
|
|
204
|
-
|
|
177
|
+
<img src="https://github.com/user-attachments/assets/2f9022a0-7b88-4588-a448-250f07a634d7" alt="ROI Colocalization Widget" style="width:75%; height:auto;">
|
|
205
178
|
|
|
206
179
|
## Contributing
|
|
207
180
|
|
|
@@ -6,42 +6,14 @@
|
|
|
6
6
|
[](https://github.com/macromeer/napari-tmidas/actions)
|
|
7
7
|
[](https://napari-hub.org/plugins/napari-tmidas)
|
|
8
8
|
<!-- [](https://codecov.io/gh/macromeer/napari-tmidas) -->
|
|
9
|
-
The `napari-tmidas` plugin consists of a growing collection of pipelines for fast batch processing of microscopy images. This is a WIP and based on the CLI version of [T-MIDAS](https://github.com/MercaderLabAnatomy/T-MIDAS).
|
|
10
|
-
|
|
11
|
-
## Feature Overview
|
|
12
|
-
|
|
13
|
-
1. **Image Processing**
|
|
14
|
-
- Process image folders with:
|
|
15
|
-
- Gamma correction & histogram equalization
|
|
16
|
-
- Z-projection and channel splitting
|
|
17
|
-
- Gaussian & median filters
|
|
18
|
-
- Thresholding (Otsu/manual)
|
|
19
|
-
- Label cleaning & binary conversion
|
|
20
|
-
- RGB to labels conversion
|
|
21
|
-
- Cellpose 3.0 automated segmentation
|
|
22
|
-
- File compression (Zstandard)
|
|
23
|
-
|
|
24
|
-
2. **Label Inspection**
|
|
25
|
-
- Review and edit label images with auto-save
|
|
26
|
-
|
|
27
|
-
3. **Microscopy Image Conversion**
|
|
28
|
-
- Convert .nd2/.lif/.ndpi/.czi/acquifer → .tif/.zarr with metadata preservation
|
|
29
|
-
|
|
30
|
-
4. **Crop Anything**
|
|
31
|
-
- Interactive ROI selection via click interface
|
|
32
|
-
|
|
33
|
-
5. **ROI Colocalization**
|
|
34
|
-
- Count colocalized labels across multiple channels
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
### Coming Soon
|
|
39
|
-
New features arriving April 2025
|
|
9
|
+
The `napari-tmidas` plugin consists of a growing collection of pipelines for fast batch processing of confocal and whole slide microscopy images of biological tissues. This is a WIP and based on the CLI version of [T-MIDAS](https://github.com/MercaderLabAnatomy/T-MIDAS).
|
|
40
10
|
|
|
11
|
+
## Features
|
|
12
|
+
Currently, napari-tmidas provides pipelines as widgets for batch image conversion / cropping / processing, ROI colocalization and label inspection (cf. [Usage](#usage) below).
|
|
41
13
|
|
|
42
14
|
## Installation
|
|
43
15
|
|
|
44
|
-
First install Napari in a virtual environment:
|
|
16
|
+
First, install Napari in a virtual environment:
|
|
45
17
|
|
|
46
18
|
mamba create -y -n napari-tmidas -c conda-forge python=3.11 tqdm
|
|
47
19
|
mamba activate napari-tmidas
|
|
@@ -79,16 +51,15 @@ To use the plugin, start napari in the activated virtual environment with this t
|
|
|
79
51
|
|
|
80
52
|
mamba run -n napari-tmidas napari
|
|
81
53
|
|
|
82
|
-
You can find the installed plugin
|
|
83
|
-
|
|
84
|
-

|
|
85
|
-
|
|
54
|
+
You can then find the installed plugin in the Plugins tab.
|
|
86
55
|
|
|
87
56
|
### Microscopy Image Conversion
|
|
88
57
|
|
|
89
58
|
You can start this pipeline via `Plugins > T-MIDAS > Batch Microscopy Image Conversion`. Currently, this pipeline supports the conversion of `.nd2, .lif, .ndpi, .czi` and acquifer data. After scanning a folder of your choice for microscopy image data, select a file in the first column of the table and preview and export any image data it contains.
|
|
90
59
|
|
|
91
|
-
|
|
60
|
+
|
|
61
|
+
<img src="https://github.com/user-attachments/assets/e377ca71-2f30-447d-825e-d2feebf7061b" alt="Microscopy Image Conversion Widget" style="width:75%; height:auto;">
|
|
62
|
+
|
|
92
63
|
|
|
93
64
|
### Image Processing
|
|
94
65
|
|
|
@@ -96,7 +67,7 @@ You can start this pipeline via `Plugins > T-MIDAS > Batch Microscopy Image Conv
|
|
|
96
67
|
|
|
97
68
|

|
|
98
69
|
|
|
99
|
-
2. As a result, a table appears with the found images.
|
|
70
|
+
2. As a result, a table appears with the found images. You can click on them to inspect them in the viewer.
|
|
100
71
|
|
|
101
72
|

|
|
102
73
|
|
|
@@ -105,26 +76,28 @@ You can start this pipeline via `Plugins > T-MIDAS > Batch Microscopy Image Conv
|
|
|
105
76
|

|
|
106
77
|
|
|
107
78
|
4. You can click on the images in the table to show them in the viewer. For example first click on one of the `Original Files`, and then the corresponding `Processed File` to see an overlay.
|
|
79
|
+
|
|
80
|
+
<img src="https://github.com/user-attachments/assets/cfe84828-c1cc-4196-9a53-5dfb82d5bfce" alt="Image Processing Widget" style="width:75%; height:auto;">
|
|
108
81
|
|
|
109
|
-

|
|
110
82
|
|
|
111
83
|
Note that whenever you click on an `Original File` or `Processed File` in the table, it will replace the one that is currently shown in the viewer. So naturally, you'd first select the original image, and then the processed image to correctly see the image pair that you want to inspect.
|
|
112
84
|
|
|
113
85
|
### Batch Label Inspection
|
|
114
86
|
If you have already segmented a folder full of images and now you want to maybe inspect and edit each label image, you can use the `Plugins > T-MIDAS > Batch Label Inspection`, which automatically saves your changes to the existing label image once you click the `Save Changes and Continue` button (bottom right).
|
|
115
87
|
|
|
116
|
-
|
|
88
|
+
<img src="https://github.com/user-attachments/assets/0bf8c6ae-4212-449d-8183-e91b23ba740e" alt="Batch Label Inspection Widget" style="width:75%; height:auto;">
|
|
89
|
+
|
|
117
90
|
|
|
118
91
|
### Crop Anything
|
|
119
92
|
This pipeline combines the Segment Anything Model (SAM) for automatic object detection with an interactive interface for selecting and cropping multiple objects from images. To launch the widget, open `Plugins > T-MIDAS > Batch Crop Anything`. Click the image below to see a video demo.
|
|
120
93
|
|
|
121
|
-
|
|
94
|
+
<img src="https://github.com/user-attachments/assets/6d72c2a2-1064-4a27-b398-a9b86fcbc443" alt="Crop Anything Widget" style="width:75%; height:auto;">
|
|
95
|
+
|
|
122
96
|
|
|
123
97
|
### ROI Colocalization
|
|
124
98
|
This pipeline quantifies colocalization between labeled regions of interest (ROIs) across multiple image channels. It determines the extent of overlap between ROIs in a reference channel and those in one or two other channels. The output is a table of colocalization counts. Optionally, the size of reference channel ROIs, as well as the total or median size of colocalizing ROIs in the other channels, can be included. Colocalization is determined using Boolean masking. The number of colocalizing instances is determined by counting unique label IDs within the overlapping regions. Typically, the reference channel contains larger structures, while other channels contain smaller, potentially nested, structures. For example, the reference channel might contain cell bodies, with the second and third channels containing nuclei and sub-nuclear objects, respectively.
|
|
125
99
|
|
|
126
|
-
|
|
127
|
-
|
|
100
|
+
<img src="https://github.com/user-attachments/assets/2f9022a0-7b88-4588-a448-250f07a634d7" alt="ROI Colocalization Widget" style="width:75%; height:auto;">
|
|
128
101
|
|
|
129
102
|
## Contributing
|
|
130
103
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "napari-tmidas"
|
|
3
3
|
dynamic = ["version"]
|
|
4
|
-
description = "A plugin for batch processing of confocal microscopy images"
|
|
4
|
+
description = "A plugin for batch processing of confocal and whole-slide microscopy images of biological tissues"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = {file = "LICENSE"}
|
|
7
7
|
authors = [
|
|
@@ -11,10 +11,11 @@ Users can make and save changes to the labels, and proceed to the next pair.
|
|
|
11
11
|
import os
|
|
12
12
|
import sys
|
|
13
13
|
|
|
14
|
+
import numpy as np
|
|
14
15
|
from magicgui import magicgui
|
|
15
16
|
from napari.layers import Labels
|
|
16
17
|
from napari.viewer import Viewer
|
|
17
|
-
from qtpy.QtWidgets import QFileDialog, QPushButton
|
|
18
|
+
from qtpy.QtWidgets import QFileDialog, QMessageBox, QPushButton
|
|
18
19
|
from skimage.io import imread # , imsave
|
|
19
20
|
|
|
20
21
|
sys.path.append("src/napari_tmidas")
|
|
@@ -29,63 +30,105 @@ class LabelInspector:
|
|
|
29
30
|
def load_image_label_pairs(self, folder_path: str, label_suffix: str):
|
|
30
31
|
"""
|
|
31
32
|
Load image-label pairs from a folder.
|
|
32
|
-
Finds
|
|
33
|
+
Finds all files with the given suffix and matches them with their corresponding image files.
|
|
34
|
+
Validates that label files are in the correct format.
|
|
33
35
|
"""
|
|
36
|
+
if not os.path.exists(folder_path) or not os.path.isdir(folder_path):
|
|
37
|
+
self.viewer.status = f"Folder path does not exist: {folder_path}"
|
|
38
|
+
return
|
|
39
|
+
|
|
34
40
|
files = os.listdir(folder_path)
|
|
35
|
-
label_files = [file for file in files if file.endswith(label_suffix)]
|
|
36
41
|
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
# Find all files that contain the label suffix
|
|
43
|
+
# Using "in" instead of "endswith" for more flexibility
|
|
44
|
+
potential_label_files = [
|
|
45
|
+
file for file in files if label_suffix in file
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
if not potential_label_files:
|
|
49
|
+
self.viewer.status = f"No files found with suffix '{label_suffix}'"
|
|
50
|
+
QMessageBox.warning(
|
|
51
|
+
None,
|
|
52
|
+
"No Label Files Found",
|
|
53
|
+
f"No files containing '{label_suffix}' were found in {folder_path}.",
|
|
54
|
+
)
|
|
55
|
+
return
|
|
41
56
|
|
|
42
|
-
#
|
|
57
|
+
# Process all potential label files
|
|
43
58
|
self.image_label_pairs = []
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
os.path.join(folder_path, lbl),
|
|
58
|
-
)
|
|
59
|
-
)
|
|
60
|
-
continue
|
|
61
|
-
|
|
62
|
-
# If not found, try finding any file that starts with the base name
|
|
59
|
+
skipped_files = []
|
|
60
|
+
format_issues = []
|
|
61
|
+
|
|
62
|
+
for label_file in potential_label_files:
|
|
63
|
+
label_path = os.path.join(folder_path, label_file)
|
|
64
|
+
|
|
65
|
+
# Get file extension
|
|
66
|
+
_, file_extension = os.path.splitext(label_file)
|
|
67
|
+
|
|
68
|
+
# Try to find a matching image file (everything before the label suffix)
|
|
69
|
+
base_name = label_file.split(label_suffix)[0]
|
|
70
|
+
|
|
71
|
+
# Look for potential images matching the base name
|
|
63
72
|
potential_images = [
|
|
64
73
|
file
|
|
65
74
|
for file in files
|
|
66
|
-
if file.startswith(
|
|
75
|
+
if file.startswith(base_name)
|
|
76
|
+
and file != label_file
|
|
67
77
|
and file.endswith(file_extension)
|
|
68
|
-
and file != lbl
|
|
69
78
|
]
|
|
70
79
|
|
|
80
|
+
# If we found at least one potential image
|
|
71
81
|
if potential_images:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
image_path = os.path.join(folder_path, potential_images[0])
|
|
83
|
+
|
|
84
|
+
# Validate label file format
|
|
85
|
+
try:
|
|
86
|
+
label_data = imread(label_path)
|
|
87
|
+
|
|
88
|
+
# Check if it looks like a label image (integer type)
|
|
89
|
+
if not np.issubdtype(label_data.dtype, np.integer):
|
|
90
|
+
format_issues.append(
|
|
91
|
+
(label_file, "not an integer type")
|
|
92
|
+
)
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
# Add valid pair
|
|
96
|
+
self.image_label_pairs.append((image_path, label_path))
|
|
97
|
+
|
|
98
|
+
except (
|
|
99
|
+
FileNotFoundError,
|
|
100
|
+
OSError,
|
|
101
|
+
ValueError,
|
|
102
|
+
Exception,
|
|
103
|
+
) as e:
|
|
104
|
+
skipped_files.append((label_file, str(e)))
|
|
105
|
+
else:
|
|
106
|
+
skipped_files.append((label_file, "no matching image found"))
|
|
107
|
+
|
|
108
|
+
# Report results
|
|
109
|
+
if self.image_label_pairs:
|
|
110
|
+
self.viewer.status = (
|
|
111
|
+
f"Found {len(self.image_label_pairs)} valid image-label pairs."
|
|
112
|
+
)
|
|
113
|
+
self.current_index = 0
|
|
114
|
+
self._load_current_pair()
|
|
115
|
+
else:
|
|
116
|
+
self.viewer.status = "No valid image-label pairs found."
|
|
117
|
+
|
|
118
|
+
# Show detailed report if there were issues
|
|
119
|
+
if skipped_files or format_issues:
|
|
120
|
+
msg = ""
|
|
121
|
+
if skipped_files:
|
|
122
|
+
msg += "Skipped files:\n"
|
|
123
|
+
for file, reason in skipped_files:
|
|
124
|
+
msg += f"- {file}: {reason}\n"
|
|
125
|
+
|
|
126
|
+
if format_issues:
|
|
127
|
+
msg += "\nFormat issues:\n"
|
|
128
|
+
for file, issue in format_issues:
|
|
129
|
+
msg += f"- {file}: {issue}\n"
|
|
130
|
+
|
|
131
|
+
QMessageBox.information(None, "Loading Report", msg)
|
|
89
132
|
|
|
90
133
|
def _load_current_pair(self):
|
|
91
134
|
"""
|
|
@@ -110,6 +153,10 @@ class LabelInspector:
|
|
|
110
153
|
label_image, name=f"Labels ({os.path.basename(label_path)})"
|
|
111
154
|
)
|
|
112
155
|
|
|
156
|
+
# Show progress
|
|
157
|
+
total = len(self.image_label_pairs)
|
|
158
|
+
self.viewer.status = f"Viewing pair {self.current_index + 1} of {total}: {os.path.basename(image_path)}"
|
|
159
|
+
|
|
113
160
|
def save_current_labels(self):
|
|
114
161
|
"""
|
|
115
162
|
Save the current labels back to the original file.
|
|
@@ -172,7 +219,7 @@ class LabelInspector:
|
|
|
172
219
|
@magicgui(
|
|
173
220
|
call_button="Start Label Inspection",
|
|
174
221
|
folder_path={"label": "Folder Path", "widget_type": "LineEdit"},
|
|
175
|
-
label_suffix={"label": "Label Suffix (e.g.,
|
|
222
|
+
label_suffix={"label": "Label Suffix (e.g., _labels.tif)"},
|
|
176
223
|
)
|
|
177
224
|
def label_inspector(
|
|
178
225
|
folder_path: str,
|
{napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/basic.py
RENAMED
|
@@ -100,6 +100,66 @@ def max_z_projection(image: np.ndarray) -> np.ndarray:
|
|
|
100
100
|
return (projection * max_val).clip(0, max_val).astype(image.dtype)
|
|
101
101
|
|
|
102
102
|
|
|
103
|
+
@BatchProcessingRegistry.register(
|
|
104
|
+
name="Max Z Projection (TZYX)",
|
|
105
|
+
suffix="_maxZ_tzyx",
|
|
106
|
+
description="Maximum intensity projection along the Z-axis for TZYX data",
|
|
107
|
+
parameters={}, # No parameters needed - fully automatic
|
|
108
|
+
)
|
|
109
|
+
def max_z_projection_tzyx(image: np.ndarray) -> np.ndarray:
|
|
110
|
+
"""
|
|
111
|
+
Memory-efficient maximum intensity projection along the Z-axis for TZYX data.
|
|
112
|
+
|
|
113
|
+
This function intelligently chooses the most memory-efficient approach
|
|
114
|
+
based on the input data size and available system memory.
|
|
115
|
+
|
|
116
|
+
Parameters:
|
|
117
|
+
-----------
|
|
118
|
+
image : numpy.ndarray
|
|
119
|
+
Input 4D image with TZYX dimensions
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
--------
|
|
123
|
+
numpy.ndarray
|
|
124
|
+
3D image with TYX dimensions after max projection
|
|
125
|
+
"""
|
|
126
|
+
# Validate input dimensions
|
|
127
|
+
if image.ndim != 4:
|
|
128
|
+
raise ValueError(f"Expected 4D image (TZYX), got {image.ndim}D image")
|
|
129
|
+
|
|
130
|
+
# Get dimensions
|
|
131
|
+
t_size, z_size, y_size, x_size = image.shape
|
|
132
|
+
|
|
133
|
+
# For Z projection, we only need one Z plane in memory at a time
|
|
134
|
+
# so we can process this plane by plane to minimize memory usage
|
|
135
|
+
|
|
136
|
+
# Create output array with appropriate dimensions and same dtype
|
|
137
|
+
result = np.zeros((t_size, y_size, x_size), dtype=image.dtype)
|
|
138
|
+
|
|
139
|
+
# Process each time point separately to minimize memory usage
|
|
140
|
+
for t in range(t_size):
|
|
141
|
+
# If data type allows direct max, use it
|
|
142
|
+
if np.issubdtype(image.dtype, np.integer) or np.issubdtype(
|
|
143
|
+
image.dtype, np.floating
|
|
144
|
+
):
|
|
145
|
+
# Process Z planes efficiently
|
|
146
|
+
# Start with the first Z plane
|
|
147
|
+
z_max = image[t, 0].copy()
|
|
148
|
+
|
|
149
|
+
# Compare with each subsequent Z plane
|
|
150
|
+
for z in range(1, z_size):
|
|
151
|
+
# Use numpy's maximum function to update max values in-place
|
|
152
|
+
np.maximum(z_max, image[t, z], out=z_max)
|
|
153
|
+
|
|
154
|
+
# Store result for this time point
|
|
155
|
+
result[t] = z_max
|
|
156
|
+
else:
|
|
157
|
+
# For unusual data types, fall back to numpy's max function
|
|
158
|
+
result[t] = np.max(image[t], axis=0)
|
|
159
|
+
|
|
160
|
+
return result
|
|
161
|
+
|
|
162
|
+
|
|
103
163
|
@BatchProcessingRegistry.register(
|
|
104
164
|
name="Split Channels",
|
|
105
165
|
suffix="_split_channels",
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""
|
|
2
|
+
processing_functions/sam2_env_manager.py
|
|
3
|
+
|
|
4
|
+
This module manages a dedicated virtual environment for SAM2.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import platform
|
|
9
|
+
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
import venv
|
|
12
|
+
|
|
13
|
+
# Define the environment directory in user's home folder
|
|
14
|
+
ENV_DIR = os.path.join(
|
|
15
|
+
os.path.expanduser("~"), ".napari-tmidas", "envs", "sam2-env"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def is_sam2_installed():
|
|
20
|
+
"""Check if SAM2 is installed in the current environment."""
|
|
21
|
+
try:
|
|
22
|
+
import importlib.util
|
|
23
|
+
|
|
24
|
+
return importlib.util.find_spec("sam2-env") is not None
|
|
25
|
+
except ImportError:
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def is_env_created():
|
|
30
|
+
"""Check if the dedicated environment exists."""
|
|
31
|
+
env_python = get_env_python_path()
|
|
32
|
+
return os.path.exists(env_python)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_env_python_path():
|
|
36
|
+
"""Get the path to the Python executable in the environment."""
|
|
37
|
+
if platform.system() == "Windows":
|
|
38
|
+
return os.path.join(ENV_DIR, "Scripts", "python.exe")
|
|
39
|
+
else:
|
|
40
|
+
return os.path.join(ENV_DIR, "bin", "python")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def create_sam2_env():
|
|
44
|
+
"""Create a dedicated virtual environment for SAM2."""
|
|
45
|
+
# Ensure the environment directory exists
|
|
46
|
+
os.makedirs(os.path.dirname(ENV_DIR), exist_ok=True)
|
|
47
|
+
|
|
48
|
+
# Remove existing environment if it exists
|
|
49
|
+
if os.path.exists(ENV_DIR):
|
|
50
|
+
shutil.rmtree(ENV_DIR)
|
|
51
|
+
|
|
52
|
+
print(f"Creating SAM2 environment at {ENV_DIR}...")
|
|
53
|
+
|
|
54
|
+
# Create a new virtual environment
|
|
55
|
+
venv.create(ENV_DIR, with_pip=True)
|
|
56
|
+
|
|
57
|
+
# Path to the Python executable in the new environment
|
|
58
|
+
env_python = get_env_python_path()
|
|
59
|
+
|
|
60
|
+
# Upgrade pip
|
|
61
|
+
print("Upgrading pip...")
|
|
62
|
+
subprocess.check_call(
|
|
63
|
+
[env_python, "-m", "pip", "install", "--upgrade", "pip"]
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Install numpy and torch first for compatibility
|
|
67
|
+
print("Installing torch and torchvision...")
|
|
68
|
+
subprocess.check_call(
|
|
69
|
+
[env_python, "-m", "pip", "install", "torch", "torchvision"]
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Install sam2 from GitHub
|
|
73
|
+
print("Installing SAM2 from GitHub...")
|
|
74
|
+
subprocess.check_call(
|
|
75
|
+
[
|
|
76
|
+
env_python,
|
|
77
|
+
"-m",
|
|
78
|
+
"pip",
|
|
79
|
+
"install",
|
|
80
|
+
"git+https://github.com/facebookresearch/sam2.git",
|
|
81
|
+
]
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
subprocess.run(
|
|
85
|
+
[
|
|
86
|
+
env_python,
|
|
87
|
+
"-c",
|
|
88
|
+
"import torch; import torchvision; print('PyTorch version:', torch.__version__); print('Torchvision version:', torchvision.__version__); print('CUDA is available:', torch.cuda.is_available())",
|
|
89
|
+
]
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
print("SAM2 environment created successfully.")
|
|
93
|
+
return env_python
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def run_sam2_in_env(func_name, args_dict):
|
|
97
|
+
"""
|
|
98
|
+
Run SAM2 in a dedicated environment with minimal complexity.
|
|
99
|
+
|
|
100
|
+
Parameters:
|
|
101
|
+
-----------
|
|
102
|
+
func_name : str
|
|
103
|
+
Name of the SAM2 function to run (currently unused)
|
|
104
|
+
args_dict : dict
|
|
105
|
+
Dictionary of arguments for SAM2
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
--------
|
|
109
|
+
numpy.ndarray
|
|
110
|
+
Segmentation masks
|
|
111
|
+
"""
|