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.
Files changed (47) hide show
  1. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/PKG-INFO +18 -45
  2. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/README.md +16 -43
  3. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/pyproject.toml +1 -1
  4. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_label_inspection.py +94 -47
  5. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_version.py +2 -2
  6. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/basic.py +60 -0
  7. napari_tmidas-0.2.0/src/napari_tmidas/processing_functions/sam2_env_manager.py +111 -0
  8. napari_tmidas-0.2.0/src/napari_tmidas/processing_functions/skimage_filters.py +427 -0
  9. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas.egg-info/PKG-INFO +18 -45
  10. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas.egg-info/SOURCES.txt +1 -0
  11. napari_tmidas-0.1.8.5/src/napari_tmidas/processing_functions/skimage_filters.py +0 -134
  12. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/.github/dependabot.yml +0 -0
  13. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/.github/workflows/test_and_deploy.yml +0 -0
  14. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/.gitignore +0 -0
  15. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/.napari-hub/DESCRIPTION.md +0 -0
  16. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/.napari-hub/config.yml +0 -0
  17. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/.pre-commit-config.yaml +0 -0
  18. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/LICENSE +0 -0
  19. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/MANIFEST.in +0 -0
  20. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/setup.cfg +0 -0
  21. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/__init__.py +0 -0
  22. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_crop_anything.py +0 -0
  23. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_file_conversion.py +0 -0
  24. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_file_selector.py +0 -0
  25. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_reader.py +0 -0
  26. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_registry.py +0 -0
  27. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_roi_colocalization.py +0 -0
  28. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_sample_data.py +0 -0
  29. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_tests/__init__.py +0 -0
  30. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_tests/test_reader.py +0 -0
  31. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_tests/test_sample_data.py +0 -0
  32. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_tests/test_widget.py +0 -0
  33. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_tests/test_writer.py +0 -0
  34. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_widget.py +0 -0
  35. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/_writer.py +0 -0
  36. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/napari.yaml +0 -0
  37. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/__init__.py +0 -0
  38. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/cellpose_env_manager.py +0 -0
  39. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/cellpose_segmentation.py +0 -0
  40. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/colocalization.py +0 -0
  41. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/file_compression.py +0 -0
  42. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas/processing_functions/scipy_filters.py +0 -0
  43. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas.egg-info/dependency_links.txt +0 -0
  44. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas.egg-info/entry_points.txt +0 -0
  45. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas.egg-info/requires.txt +0 -0
  46. {napari_tmidas-0.1.8.5 → napari_tmidas-0.2.0}/src/napari_tmidas.egg-info/top_level.txt +0 -0
  47. {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.1.8.5
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
  [![tests](https://github.com/macromeer/napari-tmidas/workflows/tests/badge.svg)](https://github.com/macromeer/napari-tmidas/actions)
84
84
  [![napari hub](https://img.shields.io/endpoint?url=https://api.napari-hub.org/shields/napari-tmidas)](https://napari-hub.org/plugins/napari-tmidas)
85
85
  <!-- [![codecov](https://codecov.io/gh/macromeer/napari-tmidas/branch/main/graph/badge.svg)](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 here:
160
-
161
- ![image](https://github.com/user-attachments/assets/504db09a-d66e-49eb-90cd-3237024d9d7a)
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
- ![image](https://github.com/user-attachments/assets/e377ca71-2f30-447d-825e-d2feebf7061b)
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
  ![image](https://github.com/user-attachments/assets/41ecb689-9abe-4371-83b5-9c5eb37069f9)
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
  ![image](https://github.com/user-attachments/assets/8360942a-be8f-49ec-bc25-385ee43bd601)
179
150
 
@@ -182,26 +153,28 @@ You can start this pipeline via `Plugins > T-MIDAS > Batch Microscopy Image Conv
182
153
  ![image](https://github.com/user-attachments/assets/05929660-6672-4f76-89da-4f17749ccfad)
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
- ![image](https://github.com/user-attachments/assets/cfe84828-c1cc-4196-9a53-5dfb82d5bfce)
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
- ![image](https://github.com/user-attachments/assets/0bf8c6ae-4212-449d-8183-e91b23ba740e)
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
- [![image](https://github.com/user-attachments/assets/6d72c2a2-1064-4a27-b398-a9b86fcbc443)](https://youtu.be/xPh0dRD_FbE)
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
- ![napari-tmidas_coloc_pipeline](https://github.com/user-attachments/assets/2f9022a0-7b88-4588-a448-250f07a634d7)
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
  [![tests](https://github.com/macromeer/napari-tmidas/workflows/tests/badge.svg)](https://github.com/macromeer/napari-tmidas/actions)
7
7
  [![napari hub](https://img.shields.io/endpoint?url=https://api.napari-hub.org/shields/napari-tmidas)](https://napari-hub.org/plugins/napari-tmidas)
8
8
  <!-- [![codecov](https://codecov.io/gh/macromeer/napari-tmidas/branch/main/graph/badge.svg)](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 here:
83
-
84
- ![image](https://github.com/user-attachments/assets/504db09a-d66e-49eb-90cd-3237024d9d7a)
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
- ![image](https://github.com/user-attachments/assets/e377ca71-2f30-447d-825e-d2feebf7061b)
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
  ![image](https://github.com/user-attachments/assets/41ecb689-9abe-4371-83b5-9c5eb37069f9)
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
  ![image](https://github.com/user-attachments/assets/8360942a-be8f-49ec-bc25-385ee43bd601)
102
73
 
@@ -105,26 +76,28 @@ You can start this pipeline via `Plugins > T-MIDAS > Batch Microscopy Image Conv
105
76
  ![image](https://github.com/user-attachments/assets/05929660-6672-4f76-89da-4f17749ccfad)
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
- ![image](https://github.com/user-attachments/assets/cfe84828-c1cc-4196-9a53-5dfb82d5bfce)
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
- ![image](https://github.com/user-attachments/assets/0bf8c6ae-4212-449d-8183-e91b23ba740e)
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
- [![image](https://github.com/user-attachments/assets/6d72c2a2-1064-4a27-b398-a9b86fcbc443)](https://youtu.be/xPh0dRD_FbE)
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
- ![napari-tmidas_coloc_pipeline](https://github.com/user-attachments/assets/2f9022a0-7b88-4588-a448-250f07a634d7)
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 label files with the given suffix and matches them with their corresponding image files.
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
- # Extract the file extension (e.g., .tif)
38
- file_extension = (
39
- os.path.splitext(label_suffix)[-1] if "." in label_suffix else ""
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
- # Modified matching logic
57
+ # Process all potential label files
43
58
  self.image_label_pairs = []
44
- for lbl in label_files:
45
- # Remove the label suffix to get the base name
46
- label_prefix = lbl[: -len(label_suffix)]
47
-
48
- # Potential corresponding image file
49
- img = f"{label_prefix}{file_extension}"
50
- img_path = os.path.join(folder_path, img)
51
-
52
- # Check if the image file exists
53
- if os.path.exists(img_path):
54
- self.image_label_pairs.append(
55
- (
56
- img_path,
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(label_prefix)
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
- # Use the first matching image
73
- self.image_label_pairs.append(
74
- (
75
- os.path.join(folder_path, potential_images[0]),
76
- os.path.join(folder_path, lbl),
77
- )
78
- )
79
-
80
- if not self.image_label_pairs:
81
- self.viewer.status = "No matching image-label pairs found."
82
- return
83
-
84
- self.viewer.status = (
85
- f"Found {len(self.image_label_pairs)} image-label pairs."
86
- )
87
- self.current_index = 0
88
- self._load_current_pair()
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., _otsu_labels.tif)"},
222
+ label_suffix={"label": "Label Suffix (e.g., _labels.tif)"},
176
223
  )
177
224
  def label_inspector(
178
225
  folder_path: str,
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.1.8.5'
21
- __version_tuple__ = version_tuple = (0, 1, 8, 5)
20
+ __version__ = version = '0.2.0'
21
+ __version_tuple__ = version_tuple = (0, 2, 0)
@@ -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
+ """