napari-tmidas 0.1.8__tar.gz → 0.1.9__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 → napari_tmidas-0.1.9}/PKG-INFO +39 -42
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/README.md +29 -40
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/pyproject.toml +9 -1
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_crop_anything.py +137 -5
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_file_conversion.py +40 -18
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_file_selector.py +120 -13
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_version.py +2 -2
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/processing_functions/basic.py +104 -0
- napari_tmidas-0.1.9/src/napari_tmidas/processing_functions/cellpose_env_manager.py +172 -0
- napari_tmidas-0.1.9/src/napari_tmidas/processing_functions/cellpose_segmentation.py +511 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/processing_functions/colocalization.py +17 -19
- napari_tmidas-0.1.9/src/napari_tmidas/processing_functions/file_compression.py +205 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/processing_functions/skimage_filters.py +25 -6
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas.egg-info/PKG-INFO +39 -42
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas.egg-info/SOURCES.txt +3 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas.egg-info/requires.txt +8 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/.github/dependabot.yml +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/.github/workflows/test_and_deploy.yml +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/.gitignore +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/.napari-hub/DESCRIPTION.md +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/.napari-hub/config.yml +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/.pre-commit-config.yaml +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/LICENSE +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/MANIFEST.in +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/setup.cfg +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/__init__.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_label_inspection.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_reader.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_registry.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_roi_colocalization.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_sample_data.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_tests/__init__.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_tests/test_reader.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_tests/test_sample_data.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_tests/test_widget.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_tests/test_writer.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_widget.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/_writer.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/napari.yaml +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/processing_functions/__init__.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas/processing_functions/scipy_filters.py +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas.egg-info/dependency_links.txt +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas.egg-info/entry_points.txt +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/src/napari_tmidas.egg-info/top_level.txt +0 -0
- {napari_tmidas-0.1.8 → napari_tmidas-0.1.9}/tox.ini +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: napari-tmidas
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.1.9
|
|
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:
|
|
@@ -58,6 +58,14 @@ Requires-Dist: magicgui
|
|
|
58
58
|
Requires-Dist: qtpy
|
|
59
59
|
Requires-Dist: scikit-image
|
|
60
60
|
Requires-Dist: pyqt5
|
|
61
|
+
Requires-Dist: tqdm
|
|
62
|
+
Requires-Dist: scikit-image
|
|
63
|
+
Requires-Dist: ome-zarr
|
|
64
|
+
Requires-Dist: napari-ome-zarr
|
|
65
|
+
Requires-Dist: torch
|
|
66
|
+
Requires-Dist: torchvision
|
|
67
|
+
Requires-Dist: timm
|
|
68
|
+
Requires-Dist: opencv-python
|
|
61
69
|
Provides-Extra: testing
|
|
62
70
|
Requires-Dist: tox; extra == "testing"
|
|
63
71
|
Requires-Dist: pytest; extra == "testing"
|
|
@@ -75,34 +83,14 @@ Dynamic: license-file
|
|
|
75
83
|
[](https://github.com/macromeer/napari-tmidas/actions)
|
|
76
84
|
[](https://napari-hub.org/plugins/napari-tmidas)
|
|
77
85
|
<!-- [](https://codecov.io/gh/macromeer/napari-tmidas) -->
|
|
78
|
-
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).
|
|
79
|
-
|
|
80
|
-
## Feature Overview
|
|
81
|
-
|
|
82
|
-
1. **Image Processing**
|
|
83
|
-
- Process image folders with: Gamma correction, Z-projection, channel splitting, Gaussian/median filters, thresholding (Otsu/manual), and label cleaning
|
|
84
|
-
|
|
85
|
-
2. **Label Inspection**
|
|
86
|
-
- Review and edit label images with auto-save
|
|
87
|
-
|
|
88
|
-
3. **Microscopy Image Conversion**
|
|
89
|
-
- Convert .nd2/.lif/.ndpi/.czi/acquifer → .tif/.zarr with metadata preservation
|
|
90
|
-
|
|
91
|
-
4. **Crop Anything**
|
|
92
|
-
- Interactive ROI selection via click interface
|
|
93
|
-
|
|
94
|
-
5. **ROI Colocalization**
|
|
95
|
-
- Count colocalized labels across multiple channels
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
### Coming Soon
|
|
100
|
-
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).
|
|
101
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).
|
|
102
90
|
|
|
103
91
|
## Installation
|
|
104
92
|
|
|
105
|
-
First install Napari in a virtual environment:
|
|
93
|
+
First, install Napari in a virtual environment:
|
|
106
94
|
|
|
107
95
|
mamba create -y -n napari-tmidas -c conda-forge python=3.11 tqdm
|
|
108
96
|
mamba activate napari-tmidas
|
|
@@ -112,19 +100,27 @@ Now you can install `napari-tmidas` via [pip]:
|
|
|
112
100
|
|
|
113
101
|
pip install napari-tmidas
|
|
114
102
|
|
|
115
|
-
|
|
103
|
+
It is recommended to install the latest development version:
|
|
116
104
|
|
|
117
105
|
pip install git+https://github.com/macromeer/napari-tmidas.git
|
|
118
106
|
|
|
119
107
|
### Dependencies
|
|
120
|
-
To use the Batch Microscopy Image Conversion pipeline, we need some libraries to read microscopy formats and to write ome-zarr:
|
|
121
108
|
|
|
122
|
-
|
|
109
|
+
To use the Batch Microscopy Image Conversion pipeline, we need some libraries to read microscopy formats:
|
|
110
|
+
|
|
111
|
+
pip install nd2 readlif tiffslide pylibCZIrw acquifer-napari
|
|
123
112
|
|
|
124
113
|
For the Batch Crop Anything pipeline, we need to install MobileSAM and its dependencies:
|
|
125
114
|
|
|
126
115
|
pip install git+https://github.com/ChaoningZhang/MobileSAM.git
|
|
127
|
-
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
If you want to batch compress images using [Zstandard](https://github.com/facebook/zstd), use the package manager of your operating system to install it:
|
|
119
|
+
|
|
120
|
+
sudo apt-get install zstd # for Linux
|
|
121
|
+
brew install zstd # for macOS
|
|
122
|
+
choco install zstandard # for Windows
|
|
123
|
+
|
|
128
124
|
|
|
129
125
|
## Usage
|
|
130
126
|
|
|
@@ -132,16 +128,15 @@ To use the plugin, start napari in the activated virtual environment with this t
|
|
|
132
128
|
|
|
133
129
|
mamba run -n napari-tmidas napari
|
|
134
130
|
|
|
135
|
-
You can find the installed plugin
|
|
136
|
-
|
|
137
|
-

|
|
138
|
-
|
|
131
|
+
You can then find the installed plugin in the Plugins tab.
|
|
139
132
|
|
|
140
133
|
### Microscopy Image Conversion
|
|
141
134
|
|
|
142
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.
|
|
143
136
|
|
|
144
|
-
|
|
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
|
+
|
|
145
140
|
|
|
146
141
|
### Image Processing
|
|
147
142
|
|
|
@@ -149,7 +144,7 @@ You can start this pipeline via `Plugins > T-MIDAS > Batch Microscopy Image Conv
|
|
|
149
144
|
|
|
150
145
|

|
|
151
146
|
|
|
152
|
-
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.
|
|
153
148
|
|
|
154
149
|

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

|
|
159
154
|
|
|
160
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;">
|
|
161
158
|
|
|
162
|
-

|
|
163
159
|
|
|
164
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.
|
|
165
161
|
|
|
166
162
|
### Batch Label Inspection
|
|
167
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).
|
|
168
164
|
|
|
169
|
-
|
|
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
|
+
|
|
170
167
|
|
|
171
168
|
### Crop Anything
|
|
172
|
-
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
|
|
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.
|
|
170
|
+
|
|
171
|
+
<img src="https://github.com/user-attachments/assets/6d72c2a2-1064-4a27-b398-a9b86fcbc443" alt="Crop Anything Widget" style="width:75%; height:auto;">
|
|
173
172
|
|
|
174
|
-

|
|
175
173
|
|
|
176
174
|
### ROI Colocalization
|
|
177
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.
|
|
178
176
|
|
|
179
|
-
|
|
180
|
-
|
|
177
|
+
<img src="https://github.com/user-attachments/assets/2f9022a0-7b88-4588-a448-250f07a634d7" alt="ROI Colocalization Widget" style="width:75%; height:auto;">
|
|
181
178
|
|
|
182
179
|
## Contributing
|
|
183
180
|
|
|
@@ -6,34 +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: Gamma correction, Z-projection, channel splitting, Gaussian/median filters, thresholding (Otsu/manual), and label cleaning
|
|
15
|
-
|
|
16
|
-
2. **Label Inspection**
|
|
17
|
-
- Review and edit label images with auto-save
|
|
18
|
-
|
|
19
|
-
3. **Microscopy Image Conversion**
|
|
20
|
-
- Convert .nd2/.lif/.ndpi/.czi/acquifer → .tif/.zarr with metadata preservation
|
|
21
|
-
|
|
22
|
-
4. **Crop Anything**
|
|
23
|
-
- Interactive ROI selection via click interface
|
|
24
|
-
|
|
25
|
-
5. **ROI Colocalization**
|
|
26
|
-
- Count colocalized labels across multiple channels
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
### Coming Soon
|
|
31
|
-
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).
|
|
32
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).
|
|
33
13
|
|
|
34
14
|
## Installation
|
|
35
15
|
|
|
36
|
-
First install Napari in a virtual environment:
|
|
16
|
+
First, install Napari in a virtual environment:
|
|
37
17
|
|
|
38
18
|
mamba create -y -n napari-tmidas -c conda-forge python=3.11 tqdm
|
|
39
19
|
mamba activate napari-tmidas
|
|
@@ -43,19 +23,27 @@ Now you can install `napari-tmidas` via [pip]:
|
|
|
43
23
|
|
|
44
24
|
pip install napari-tmidas
|
|
45
25
|
|
|
46
|
-
|
|
26
|
+
It is recommended to install the latest development version:
|
|
47
27
|
|
|
48
28
|
pip install git+https://github.com/macromeer/napari-tmidas.git
|
|
49
29
|
|
|
50
30
|
### Dependencies
|
|
51
|
-
To use the Batch Microscopy Image Conversion pipeline, we need some libraries to read microscopy formats and to write ome-zarr:
|
|
52
31
|
|
|
53
|
-
|
|
32
|
+
To use the Batch Microscopy Image Conversion pipeline, we need some libraries to read microscopy formats:
|
|
33
|
+
|
|
34
|
+
pip install nd2 readlif tiffslide pylibCZIrw acquifer-napari
|
|
54
35
|
|
|
55
36
|
For the Batch Crop Anything pipeline, we need to install MobileSAM and its dependencies:
|
|
56
37
|
|
|
57
38
|
pip install git+https://github.com/ChaoningZhang/MobileSAM.git
|
|
58
|
-
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
If you want to batch compress images using [Zstandard](https://github.com/facebook/zstd), use the package manager of your operating system to install it:
|
|
42
|
+
|
|
43
|
+
sudo apt-get install zstd # for Linux
|
|
44
|
+
brew install zstd # for macOS
|
|
45
|
+
choco install zstandard # for Windows
|
|
46
|
+
|
|
59
47
|
|
|
60
48
|
## Usage
|
|
61
49
|
|
|
@@ -63,16 +51,15 @@ To use the plugin, start napari in the activated virtual environment with this t
|
|
|
63
51
|
|
|
64
52
|
mamba run -n napari-tmidas napari
|
|
65
53
|
|
|
66
|
-
You can find the installed plugin
|
|
67
|
-
|
|
68
|
-

|
|
69
|
-
|
|
54
|
+
You can then find the installed plugin in the Plugins tab.
|
|
70
55
|
|
|
71
56
|
### Microscopy Image Conversion
|
|
72
57
|
|
|
73
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.
|
|
74
59
|
|
|
75
|
-
|
|
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
|
+
|
|
76
63
|
|
|
77
64
|
### Image Processing
|
|
78
65
|
|
|
@@ -80,7 +67,7 @@ You can start this pipeline via `Plugins > T-MIDAS > Batch Microscopy Image Conv
|
|
|
80
67
|
|
|
81
68
|

|
|
82
69
|
|
|
83
|
-
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.
|
|
84
71
|
|
|
85
72
|

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

|
|
90
77
|
|
|
91
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;">
|
|
92
81
|
|
|
93
|
-

|
|
94
82
|
|
|
95
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.
|
|
96
84
|
|
|
97
85
|
### Batch Label Inspection
|
|
98
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).
|
|
99
87
|
|
|
100
|
-
|
|
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
|
+
|
|
101
90
|
|
|
102
91
|
### Crop Anything
|
|
103
|
-
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
|
|
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.
|
|
93
|
+
|
|
94
|
+
<img src="https://github.com/user-attachments/assets/6d72c2a2-1064-4a27-b398-a9b86fcbc443" alt="Crop Anything Widget" style="width:75%; height:auto;">
|
|
104
95
|
|
|
105
|
-

|
|
106
96
|
|
|
107
97
|
### ROI Colocalization
|
|
108
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.
|
|
109
99
|
|
|
110
|
-
|
|
111
|
-
|
|
100
|
+
<img src="https://github.com/user-attachments/assets/2f9022a0-7b88-4588-a448-250f07a634d7" alt="ROI Colocalization Widget" style="width:75%; height:auto;">
|
|
112
101
|
|
|
113
102
|
## Contributing
|
|
114
103
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "napari-tmidas"
|
|
3
3
|
dynamic = ["version"]
|
|
4
|
-
description = "
|
|
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 = [
|
|
@@ -30,6 +30,14 @@ dependencies = [
|
|
|
30
30
|
"qtpy",
|
|
31
31
|
"scikit-image",
|
|
32
32
|
"pyqt5",
|
|
33
|
+
"tqdm",
|
|
34
|
+
"scikit-image",
|
|
35
|
+
"ome-zarr",
|
|
36
|
+
"napari-ome-zarr",
|
|
37
|
+
"torch",
|
|
38
|
+
"torchvision",
|
|
39
|
+
"timm",
|
|
40
|
+
"opencv-python",
|
|
33
41
|
]
|
|
34
42
|
|
|
35
43
|
[project.optional-dependencies]
|
|
@@ -29,6 +29,7 @@ from qtpy.QtWidgets import (
|
|
|
29
29
|
QWidget,
|
|
30
30
|
)
|
|
31
31
|
from skimage.io import imread
|
|
32
|
+
from skimage.transform import resize # Added import for resize function
|
|
32
33
|
from tifffile import imwrite
|
|
33
34
|
|
|
34
35
|
|
|
@@ -48,6 +49,7 @@ class BatchCropAnything:
|
|
|
48
49
|
self.original_image = None
|
|
49
50
|
self.segmentation_result = None
|
|
50
51
|
self.current_image_for_segmentation = None
|
|
52
|
+
self.current_scale_factor = 1.0 # Added scale factor tracking
|
|
51
53
|
|
|
52
54
|
# UI references
|
|
53
55
|
self.image_layer = None
|
|
@@ -356,10 +358,41 @@ class BatchCropAnything:
|
|
|
356
358
|
# Convert back to uint8
|
|
357
359
|
image_gamma = (image_gamma * 255).astype(np.uint8)
|
|
358
360
|
|
|
361
|
+
# Check if the image is very large and needs downscaling
|
|
362
|
+
orig_shape = image_gamma.shape[:2] # (height, width)
|
|
363
|
+
|
|
364
|
+
# Calculate image size in megapixels
|
|
365
|
+
image_mp = (orig_shape[0] * orig_shape[1]) / 1e6
|
|
366
|
+
|
|
367
|
+
# If image is larger than 2 megapixels, downscale it
|
|
368
|
+
max_mp = 2.0 # Maximum image size in megapixels
|
|
369
|
+
scale_factor = 1.0
|
|
370
|
+
|
|
371
|
+
if image_mp > max_mp:
|
|
372
|
+
scale_factor = np.sqrt(max_mp / image_mp)
|
|
373
|
+
new_height = int(orig_shape[0] * scale_factor)
|
|
374
|
+
new_width = int(orig_shape[1] * scale_factor)
|
|
375
|
+
|
|
376
|
+
self.viewer.status = f"Downscaling image from {orig_shape} to {(new_height, new_width)} for processing (scale: {scale_factor:.2f})"
|
|
377
|
+
|
|
378
|
+
# Resize the image for processing
|
|
379
|
+
image_gamma_resized = resize(
|
|
380
|
+
image_gamma,
|
|
381
|
+
(new_height, new_width),
|
|
382
|
+
anti_aliasing=True,
|
|
383
|
+
preserve_range=True,
|
|
384
|
+
).astype(np.uint8)
|
|
385
|
+
|
|
386
|
+
# Store scale factor for later use
|
|
387
|
+
self.current_scale_factor = scale_factor
|
|
388
|
+
else:
|
|
389
|
+
image_gamma_resized = image_gamma
|
|
390
|
+
self.current_scale_factor = 1.0
|
|
391
|
+
|
|
359
392
|
self.viewer.status = f"Generating segmentation with sensitivity {self.sensitivity} (gamma={gamma:.2f})..."
|
|
360
393
|
|
|
361
|
-
# Generate masks with gamma-corrected image
|
|
362
|
-
masks = self.mask_generator.generate(
|
|
394
|
+
# Generate masks with gamma-corrected and potentially resized image
|
|
395
|
+
masks = self.mask_generator.generate(image_gamma_resized)
|
|
363
396
|
self.viewer.status = f"Generated {len(masks)} masks"
|
|
364
397
|
|
|
365
398
|
if not masks:
|
|
@@ -390,9 +423,16 @@ class BatchCropAnything:
|
|
|
390
423
|
return
|
|
391
424
|
|
|
392
425
|
# Process segmentation masks
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
426
|
+
# If image was downscaled, we need to ensure masks are upscaled correctly
|
|
427
|
+
if self.current_scale_factor < 1.0:
|
|
428
|
+
# Upscale the segmentation masks to match the original image dimensions
|
|
429
|
+
self._process_segmentation_masks_with_scaling(
|
|
430
|
+
masks, self.current_image_for_segmentation.shape[:2]
|
|
431
|
+
)
|
|
432
|
+
else:
|
|
433
|
+
self._process_segmentation_masks(
|
|
434
|
+
masks, self.current_image_for_segmentation.shape[:2]
|
|
435
|
+
)
|
|
396
436
|
|
|
397
437
|
# Clear selected labels since segmentation has changed
|
|
398
438
|
self.selected_labels = set()
|
|
@@ -475,6 +515,98 @@ class BatchCropAnything:
|
|
|
475
515
|
# image_name = os.path.basename(self.images[self.current_index])
|
|
476
516
|
self.viewer.status = f"Loaded image {self.current_index + 1}/{len(self.images)} - Found {len(masks)} segments"
|
|
477
517
|
|
|
518
|
+
# New method for handling scaled segmentation masks
|
|
519
|
+
def _process_segmentation_masks_with_scaling(self, masks, original_shape):
|
|
520
|
+
"""Process segmentation masks with scaling to match the original image size."""
|
|
521
|
+
# Create label image from masks
|
|
522
|
+
# First determine the size of the mask predictions (which are at the downscaled resolution)
|
|
523
|
+
if not masks:
|
|
524
|
+
return
|
|
525
|
+
|
|
526
|
+
mask_shape = masks[0]["segmentation"].shape
|
|
527
|
+
|
|
528
|
+
# Create an empty label image at the downscaled resolution
|
|
529
|
+
downscaled_labels = np.zeros(mask_shape, dtype=np.uint32)
|
|
530
|
+
self.label_info = {} # Reset label info
|
|
531
|
+
|
|
532
|
+
# Fill in the downscaled labels
|
|
533
|
+
for i, mask_data in enumerate(masks):
|
|
534
|
+
mask = mask_data["segmentation"]
|
|
535
|
+
label_id = i + 1 # Start label IDs from 1
|
|
536
|
+
downscaled_labels[mask] = label_id
|
|
537
|
+
|
|
538
|
+
# Store basic label info
|
|
539
|
+
area = np.sum(mask)
|
|
540
|
+
y_indices, x_indices = np.where(mask)
|
|
541
|
+
center_y = np.mean(y_indices) if len(y_indices) > 0 else 0
|
|
542
|
+
center_x = np.mean(x_indices) if len(x_indices) > 0 else 0
|
|
543
|
+
|
|
544
|
+
# Scale centers to original image coordinates
|
|
545
|
+
center_y_orig = center_y / self.current_scale_factor
|
|
546
|
+
center_x_orig = center_x / self.current_scale_factor
|
|
547
|
+
|
|
548
|
+
# Store label info at original scale
|
|
549
|
+
self.label_info[label_id] = {
|
|
550
|
+
"area": area
|
|
551
|
+
/ (
|
|
552
|
+
self.current_scale_factor**2
|
|
553
|
+
), # Approximate area in original scale
|
|
554
|
+
"center_y": center_y_orig,
|
|
555
|
+
"center_x": center_x_orig,
|
|
556
|
+
"score": mask_data.get("stability_score", 0),
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
# Upscale the labels to the original image size
|
|
560
|
+
upscaled_labels = resize(
|
|
561
|
+
downscaled_labels,
|
|
562
|
+
original_shape,
|
|
563
|
+
order=0, # Nearest neighbor interpolation
|
|
564
|
+
preserve_range=True,
|
|
565
|
+
anti_aliasing=False,
|
|
566
|
+
).astype(np.uint32)
|
|
567
|
+
|
|
568
|
+
# Sort labels by area (largest first)
|
|
569
|
+
self.label_info = dict(
|
|
570
|
+
sorted(
|
|
571
|
+
self.label_info.items(),
|
|
572
|
+
key=lambda item: item[1]["area"],
|
|
573
|
+
reverse=True,
|
|
574
|
+
)
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
# Save segmentation result
|
|
578
|
+
self.segmentation_result = upscaled_labels
|
|
579
|
+
|
|
580
|
+
# Remove existing label layer if exists
|
|
581
|
+
for layer in list(self.viewer.layers):
|
|
582
|
+
if isinstance(layer, Labels) and "Segmentation" in layer.name:
|
|
583
|
+
self.viewer.layers.remove(layer)
|
|
584
|
+
|
|
585
|
+
# Add label layer to viewer
|
|
586
|
+
self.label_layer = self.viewer.add_labels(
|
|
587
|
+
upscaled_labels,
|
|
588
|
+
name=f"Segmentation ({os.path.basename(self.images[self.current_index])})",
|
|
589
|
+
opacity=0.7,
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
# Make the label layer active by default
|
|
593
|
+
self.viewer.layers.selection.active = self.label_layer
|
|
594
|
+
|
|
595
|
+
# Disconnect existing callbacks if any
|
|
596
|
+
if (
|
|
597
|
+
hasattr(self, "label_layer")
|
|
598
|
+
and self.label_layer is not None
|
|
599
|
+
and hasattr(self.label_layer, "mouse_drag_callbacks")
|
|
600
|
+
):
|
|
601
|
+
# Remove old callbacks
|
|
602
|
+
for callback in list(self.label_layer.mouse_drag_callbacks):
|
|
603
|
+
self.label_layer.mouse_drag_callbacks.remove(callback)
|
|
604
|
+
|
|
605
|
+
# Connect mouse click event to label selection
|
|
606
|
+
self.label_layer.mouse_drag_callbacks.append(self._on_label_clicked)
|
|
607
|
+
|
|
608
|
+
self.viewer.status = f"Loaded image {self.current_index + 1}/{len(self.images)} - Found {len(masks)} segments"
|
|
609
|
+
|
|
478
610
|
# --------------------------------------------------
|
|
479
611
|
# Label Selection and UI Elements
|
|
480
612
|
# --------------------------------------------------
|
|
@@ -1105,19 +1105,21 @@ class ConversionWorker(QThread):
|
|
|
1105
1105
|
)
|
|
1106
1106
|
file_size_GB = estimated_size_bytes / (1024**3)
|
|
1107
1107
|
|
|
1108
|
-
#
|
|
1108
|
+
# Determine format
|
|
1109
1109
|
use_zarr = self.use_zarr
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
)
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
)
|
|
1110
|
+
# If file is very large (>4GB) and user didn't explicitly choose TIF,
|
|
1111
|
+
# auto-switch to ZARR format
|
|
1112
|
+
if file_size_GB > 4 and not self.use_zarr:
|
|
1113
|
+
# Recommend ZARR format but respect user's choice by still allowing TIF
|
|
1114
|
+
print(
|
|
1115
|
+
f"File size ({file_size_GB:.2f}GB) exceeds 4GB, ZARR format is recommended but using TIF with BigTIFF format"
|
|
1116
|
+
)
|
|
1117
|
+
self.file_done.emit(
|
|
1118
|
+
filepath,
|
|
1119
|
+
True,
|
|
1120
|
+
f"File size ({file_size_GB:.2f}GB) exceeds 4GB, using TIF with BigTIFF format",
|
|
1121
|
+
)
|
|
1122
|
+
|
|
1121
1123
|
# Set up the output path
|
|
1122
1124
|
if use_zarr:
|
|
1123
1125
|
output_path = os.path.join(
|
|
@@ -1171,12 +1173,22 @@ class ConversionWorker(QThread):
|
|
|
1171
1173
|
def _save_tif(
|
|
1172
1174
|
self, image_data: np.ndarray, output_path: str, metadata: dict = None
|
|
1173
1175
|
):
|
|
1174
|
-
"""Enhanced TIF saving with proper dimension handling"""
|
|
1176
|
+
"""Enhanced TIF saving with proper dimension handling and BigTIFF support"""
|
|
1175
1177
|
import tifffile
|
|
1176
1178
|
|
|
1177
1179
|
print(f"Saving TIF file: {output_path}")
|
|
1178
1180
|
print(f"Image data shape: {image_data.shape}")
|
|
1179
1181
|
|
|
1182
|
+
# Check if this is a large file that needs BigTIFF
|
|
1183
|
+
estimated_size_bytes = np.prod(image_data.shape) * image_data.itemsize
|
|
1184
|
+
file_size_GB = estimated_size_bytes / (1024**3)
|
|
1185
|
+
use_bigtiff = file_size_GB > 4
|
|
1186
|
+
|
|
1187
|
+
if use_bigtiff:
|
|
1188
|
+
print(
|
|
1189
|
+
f"File size ({file_size_GB:.2f}GB) exceeds 4GB, using BigTIFF format"
|
|
1190
|
+
)
|
|
1191
|
+
|
|
1180
1192
|
if metadata:
|
|
1181
1193
|
print(f"Metadata keys: {list(metadata.keys())}")
|
|
1182
1194
|
if "axes" in metadata:
|
|
@@ -1198,7 +1210,12 @@ class ConversionWorker(QThread):
|
|
|
1198
1210
|
# Basic save if no metadata
|
|
1199
1211
|
if metadata is None:
|
|
1200
1212
|
print("No metadata provided, using basic save")
|
|
1201
|
-
tifffile.imwrite(
|
|
1213
|
+
tifffile.imwrite(
|
|
1214
|
+
output_path,
|
|
1215
|
+
image_data,
|
|
1216
|
+
compression="zlib",
|
|
1217
|
+
bigtiff=use_bigtiff,
|
|
1218
|
+
)
|
|
1202
1219
|
return
|
|
1203
1220
|
|
|
1204
1221
|
# Get image dimensions and axis order
|
|
@@ -1261,7 +1278,10 @@ class ConversionWorker(QThread):
|
|
|
1261
1278
|
print(f"Error reordering dimensions: {e}")
|
|
1262
1279
|
# Fall back to simple save without reordering
|
|
1263
1280
|
tifffile.imwrite(
|
|
1264
|
-
output_path,
|
|
1281
|
+
output_path,
|
|
1282
|
+
image_data,
|
|
1283
|
+
compression="zlib",
|
|
1284
|
+
bigtiff=use_bigtiff,
|
|
1265
1285
|
)
|
|
1266
1286
|
return
|
|
1267
1287
|
|
|
@@ -1287,7 +1307,8 @@ class ConversionWorker(QThread):
|
|
|
1287
1307
|
output_path,
|
|
1288
1308
|
image_data,
|
|
1289
1309
|
resolution=resolution,
|
|
1290
|
-
compression="
|
|
1310
|
+
compression="zlib",
|
|
1311
|
+
bigtiff=use_bigtiff,
|
|
1291
1312
|
)
|
|
1292
1313
|
else:
|
|
1293
1314
|
# Hyperstack case
|
|
@@ -1306,14 +1327,15 @@ class ConversionWorker(QThread):
|
|
|
1306
1327
|
imagej=True,
|
|
1307
1328
|
resolution=resolution,
|
|
1308
1329
|
metadata=imagej_metadata,
|
|
1309
|
-
compression="
|
|
1330
|
+
compression="zlib",
|
|
1331
|
+
bigtiff=use_bigtiff,
|
|
1310
1332
|
)
|
|
1311
1333
|
|
|
1312
1334
|
print(f"Successfully saved TIF file: {output_path}")
|
|
1313
1335
|
except (ValueError, FileNotFoundError) as e:
|
|
1314
1336
|
print(f"Error saving TIF file: {e}")
|
|
1315
1337
|
# Try simple save as fallback
|
|
1316
|
-
tifffile.imwrite(output_path, image_data)
|
|
1338
|
+
tifffile.imwrite(output_path, image_data, bigtiff=use_bigtiff)
|
|
1317
1339
|
|
|
1318
1340
|
def _save_zarr(
|
|
1319
1341
|
self, image_data: np.ndarray, output_path: str, metadata: dict = None
|