napari-tmidas 0.3.2__tar.gz → 0.3.4__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 (97) hide show
  1. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/PKG-INFO +3 -3
  2. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/README.md +2 -2
  3. napari_tmidas-0.3.4/docs/batch_label_inspection.md +354 -0
  4. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_skimage_filters.py +19 -0
  5. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_version.py +3 -3
  6. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/skimage_filters.py +49 -23
  7. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas.egg-info/PKG-INFO +3 -3
  8. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas.egg-info/SOURCES.txt +1 -0
  9. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/.github/dependabot.yml +0 -0
  10. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/.github/workflows/test_and_deploy.yml +0 -0
  11. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/.gitignore +0 -0
  12. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/.napari-hub/DESCRIPTION.md +0 -0
  13. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/.napari-hub/config.yml +0 -0
  14. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/.pre-commit-config.yaml +0 -0
  15. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/LICENSE +0 -0
  16. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/MANIFEST.in +0 -0
  17. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/advanced_processing.md +0 -0
  18. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/basic_processing.md +0 -0
  19. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/careamics_denoising.md +0 -0
  20. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/cellpose_segmentation.md +0 -0
  21. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/crop_anything.md +0 -0
  22. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/file_conversion.md +0 -0
  23. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/grid_view_overlay.md +0 -0
  24. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/intensity_label_filter.md +0 -0
  25. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/regionprops_analysis.md +0 -0
  26. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/regionprops_summary.md +0 -0
  27. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/spotiflow_detection.md +0 -0
  28. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/trackastra_tracking.md +0 -0
  29. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/viscy_virtual_staining.md +0 -0
  30. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/examples/grid_overlay_example.py +0 -0
  31. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/examples/intensity_filter_example.py +0 -0
  32. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/examples/regionprops_example.py +0 -0
  33. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/pyproject.toml +0 -0
  34. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/setup.cfg +0 -0
  35. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/__init__.py +0 -0
  36. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_crop_anything.py +0 -0
  37. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_env_manager.py +0 -0
  38. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_file_conversion.py +0 -0
  39. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_file_selector.py +0 -0
  40. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_label_inspection.py +0 -0
  41. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_processing_worker.py +0 -0
  42. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_reader.py +0 -0
  43. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_registry.py +0 -0
  44. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_roi_colocalization.py +0 -0
  45. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_sample_data.py +0 -0
  46. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_crop_anything.py +0 -0
  47. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_env_manager.py +0 -0
  48. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_file_selector.py +0 -0
  49. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_grid_view_overlay.py +0 -0
  50. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_init.py +0 -0
  51. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_intensity_label_filter.py +0 -0
  52. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_label_inspection.py +0 -0
  53. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_processing_basic.py +0 -0
  54. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_processing_worker.py +0 -0
  55. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_reader.py +0 -0
  56. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_regionprops_analysis.py +0 -0
  57. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_registry.py +0 -0
  58. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_sample_data.py +0 -0
  59. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_scipy_filters.py +0 -0
  60. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_split_channels.py +0 -0
  61. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_spotiflow.py +0 -0
  62. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_tyx_display_fix.py +0 -0
  63. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_ui_utils.py +0 -0
  64. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_viscy_virtual_staining.py +0 -0
  65. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_widget.py +0 -0
  66. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_windows_basic.py +0 -0
  67. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_writer.py +0 -0
  68. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_ui_utils.py +0 -0
  69. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_widget.py +0 -0
  70. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_writer.py +0 -0
  71. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/napari.yaml +0 -0
  72. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/__init__.py +0 -0
  73. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/basic.py +0 -0
  74. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/careamics_denoising.py +0 -0
  75. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/careamics_env_manager.py +0 -0
  76. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/cellpose_env_manager.py +0 -0
  77. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/cellpose_segmentation.py +0 -0
  78. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/colocalization.py +0 -0
  79. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/file_compression.py +0 -0
  80. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/grid_view_overlay.py +0 -0
  81. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/intensity_label_filter.py +0 -0
  82. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/regionprops_analysis.py +0 -0
  83. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/sam2_env_manager.py +0 -0
  84. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/sam2_mp4.py +0 -0
  85. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/scipy_filters.py +0 -0
  86. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/spotiflow_detection.py +0 -0
  87. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/spotiflow_env_manager.py +0 -0
  88. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/timepoint_merger.py +0 -0
  89. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/trackastra_tracking.py +0 -0
  90. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/viscy_env_manager.py +0 -0
  91. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/viscy_virtual_staining.py +0 -0
  92. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas.egg-info/dependency_links.txt +0 -0
  93. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas.egg-info/entry_points.txt +0 -0
  94. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas.egg-info/requires.txt +0 -0
  95. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas.egg-info/top_level.txt +0 -0
  96. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/test_grid_overlay.py +0 -0
  97. {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: napari-tmidas
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
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
@@ -159,9 +159,9 @@ Then find napari-tmidas in the **Plugins** menu. [Watch video tutorials →](htt
159
159
 
160
160
  ### Advanced Features
161
161
 
162
- - [SAM2 Crop Anything](docs/advanced_processing.md#sam2) - Interactive object cropping
162
+ - [Batch Crop Anything](docs/crop_anything.md) - Interactive object cropping with SAM2
163
+ - [Batch Label Inspection](docs/batch_label_inspection.md) - Manual label verification and editing
163
164
  - [Advanced Filters](docs/advanced_processing.md) - SciPy/scikit-image filters
164
- - [Batch Label Inspection](docs/basic_processing.md#label-inspection) - Manual correction workflow
165
165
 
166
166
  ## 💻 Installation
167
167
 
@@ -66,9 +66,9 @@ Then find napari-tmidas in the **Plugins** menu. [Watch video tutorials →](htt
66
66
 
67
67
  ### Advanced Features
68
68
 
69
- - [SAM2 Crop Anything](docs/advanced_processing.md#sam2) - Interactive object cropping
69
+ - [Batch Crop Anything](docs/crop_anything.md) - Interactive object cropping with SAM2
70
+ - [Batch Label Inspection](docs/batch_label_inspection.md) - Manual label verification and editing
70
71
  - [Advanced Filters](docs/advanced_processing.md) - SciPy/scikit-image filters
71
- - [Batch Label Inspection](docs/basic_processing.md#label-inspection) - Manual correction workflow
72
72
 
73
73
  ## 💻 Installation
74
74
 
@@ -0,0 +1,354 @@
1
+ # Batch Label Inspection
2
+
3
+ The Batch Label Inspection widget enables interactive verification, correction, and refinement of segmentation labels across entire image datasets. Inspect and manually edit label images while automatically saving changes back to disk.
4
+
5
+ ## Overview
6
+
7
+ Streamline the quality control workflow for segmentation results:
8
+
9
+ - **Side-by-side viewing**: Original image + label mask for easy comparison
10
+ - **Interactive editing**: Use napari's paint, eraser, and selection tools
11
+ - **Automatic saving**: Changes saved to disk as you proceed through pairs
12
+ - **Progress tracking**: Navigate through entire dataset with visual progress indicator
13
+ - **Batch workflow**: Process hundreds of images without manual file management
14
+ - **Format validation**: Automatic detection and validation of label image formats
15
+
16
+ ## Quick Start
17
+
18
+ 1. Open napari and navigate to **Plugins → napari-tmidas → Batch Label Inspection**
19
+ 2. Select folder containing your image-label pairs
20
+ 3. Specify label suffix (e.g., `_labels.tif`, `_segmentation.tif`)
21
+ 4. Click **Load** to index image-label pairs
22
+ 5. Edit labels in the viewer using napari's drawing tools
23
+ 6. Click **Save and Continue** to save changes and move to next pair
24
+ 7. Click **Previous** to revisit earlier pairs if needed
25
+
26
+ ## Workflow
27
+
28
+ ### Step 1: Prepare Your Files
29
+
30
+ Organize files with a consistent naming pattern:
31
+
32
+ ```
33
+ segmentation_results/
34
+ ├── sample1.tif (original image)
35
+ ├── sample1_labels.tif (segmentation labels)
36
+ ├── sample2.tif (original image)
37
+ ├── sample2_labels.tif (segmentation labels)
38
+ └── ...
39
+ ```
40
+
41
+ **File Requirements**:
42
+ - Label images must be integer type (8-bit, 16-bit, or 32-bit)
43
+ - Image and label must have matching spatial dimensions
44
+ - Any image format supported by scikit-image (TIF, PNG, etc.)
45
+
46
+ ### Step 2: Configure Inspection
47
+
48
+ **Folder Path**: Select directory containing image-label pairs
49
+
50
+ **Label Suffix**: Specify the suffix that identifies label files
51
+ - Examples: `_labels`, `_segmentation`, `_mask`, `_labels_filtered`
52
+ - The suffix is used to match labels with images
53
+ - File before suffix is treated as the base image name
54
+
55
+ **Example matching**:
56
+ ```
57
+ sample1.tif + sample1_labels.tif ✓ Match
58
+ sample1_labels.tif + sample1.tif ✓ Match (order doesn't matter)
59
+ sample1_seg.tif + sample1_seg_labels.tif ✗ No match (_labels not in sample1_seg.tif)
60
+ sample1_labels_filtered.tif ✓ Match (if suffix is "_labels")
61
+ ```
62
+
63
+ ### Step 3: Load Pairs
64
+
65
+ Click **Load** to scan the folder and create image-label pairs.
66
+
67
+ **Status Report**:
68
+ - Number of valid pairs found
69
+ - Any skipped files and reasons
70
+ - Format validation issues (if any)
71
+
72
+ ### Step 4: Edit and Review
73
+
74
+ For each pair displayed:
75
+
76
+ **Viewing**:
77
+ - Left panel: Original image
78
+ - Right panel: Label layer (editable)
79
+ - Status bar: Current pair number and filename
80
+
81
+ **Editing Tools** (napari built-in):
82
+ - **Paint**: Add new labels
83
+ - Select label ID from right panel
84
+ - Click and drag to paint
85
+ - **Eraser**: Remove labels
86
+ - Set label ID to 0 to erase
87
+ - **Selection tools**: Select and modify regions
88
+ - **Undo/Redo**: Ctrl+Z / Ctrl+Y
89
+
90
+ **Viewing Tips**:
91
+ - Adjust label opacity (right panel) to see image beneath
92
+ - Use different colormaps for better visibility
93
+ - Toggle layers on/off to compare
94
+
95
+ ### Step 5: Save Progress
96
+
97
+ **Save and Continue**:
98
+ - Saves current label edits to disk
99
+ - Moves to next image-label pair
100
+ - Shows confirmation status
101
+
102
+ **Previous**:
103
+ - Saves current edits
104
+ - Returns to previous pair (useful for refinement)
105
+
106
+ **Stop**:
107
+ - Saves final edits and closes widget
108
+
109
+ ## Features
110
+
111
+ ### Automatic Pair Matching
112
+
113
+ The widget intelligently matches images with their labels:
114
+
115
+ ```
116
+ Input: label suffix "_labels"
117
+
118
+ ✓ Correct matches:
119
+ image.tif ↔ image_labels.tif
120
+ sample_001.tif ↔ sample_001_labels.tif
121
+ data_ch1.tif ↔ data_ch1_labels.tif
122
+
123
+ ✗ No match:
124
+ image1.tif + image2_labels.tif (different base names)
125
+ file_labels.tif (no matching image found)
126
+ ```
127
+
128
+ ### Format Validation
129
+
130
+ Automatic checks ensure label integrity:
131
+
132
+ - **Integer type validation**: Labels must be integer (not float/RGB)
133
+ - **File format support**: TIF, PNG, etc. (any scikit-image format)
134
+ - **Dimension matching**: Labels must match image spatial dimensions
135
+ - **Error reporting**: Detailed messages for any validation issues
136
+
137
+ ### Progress Tracking
138
+
139
+ **Status Bar Display**:
140
+ ```
141
+ Viewing pair 5 of 47: sample_005.tif
142
+ ```
143
+
144
+ Shows:
145
+ - Current pair number
146
+ - Total number of pairs
147
+ - Current filename
148
+
149
+ Navigate using **Previous** / **Save and Continue** buttons
150
+
151
+ ### Automatic Saving
152
+
153
+ **When saving**:
154
+ - Current label layer written to disk
155
+ - Original filename preserved
156
+ - Data type preserved (8/16/32-bit as original)
157
+ - File overwritten (use backup if needed)
158
+ - Status confirmed in notification
159
+
160
+ ## Use Cases
161
+
162
+ ### Quality Control of Automated Segmentation
163
+
164
+ After running Cellpose or another segmenter:
165
+ 1. Load output label images
166
+ 2. Visually compare with original images
167
+ 3. Fix errors (merge split objects, remove false positives)
168
+ 4. Auto-saves corrections
169
+
170
+ ### Merging Split Objects
171
+
172
+ When segmentation over-splits cells:
173
+ 1. Select both object IDs
174
+ 2. Paint with same label to merge
175
+ 3. Erase artifacts/noise
176
+ 4. Save changes
177
+
178
+ ### Removing False Positives
179
+
180
+ When segmentation detects spurious objects:
181
+ 1. Use eraser (label = 0) to remove
182
+ 2. Paint background color where needed
183
+ 3. Save corrected labels
184
+
185
+ ### Refining Boundaries
186
+
187
+ For inaccurate object boundaries:
188
+ 1. Paint with same object ID to expand
189
+ 2. Use eraser to shrink
190
+ 3. Fine-tune label borders
191
+ 4. Save refined masks
192
+
193
+ ## Tips & Best Practices
194
+
195
+ ### Organization
196
+ - Keep consistent naming scheme across dataset
197
+ - Use descriptive suffix names (`_labels_v2`, not just `_v2`)
198
+ - Backup original labels before mass editing
199
+
200
+ ### Editing Efficiency
201
+ - Edit in 2D view for precise control
202
+ - Use opacity adjustment to see image beneath labels
203
+ - Zoom in for fine boundary adjustments
204
+ - Use selection tools for large regions
205
+
206
+ ### Data Management
207
+ - Check "Save and Continue" status confirms write
208
+ - Verify edits saved by reloading file
209
+ - Use version suffixes for multiple iterations (`_labels_v1`, `_labels_v2`)
210
+ - Keep audit trail of manual corrections
211
+
212
+ ### Performance
213
+ - For >100 pairs, consider processing in batches
214
+ - Verify label format before batch processing
215
+ - Use SSD storage for faster loading
216
+
217
+ ## Troubleshooting
218
+
219
+ ### "No Label Files Found"
220
+
221
+ **Cause**: Suffix doesn't match any files
222
+
223
+ **Solutions**:
224
+ - Check actual label filenames in folder
225
+ - Verify suffix spelling and case sensitivity
226
+ - Try shorter suffix (e.g., `_labels` instead of `_labels_filtered`)
227
+
228
+ ### "No Valid Image-Label Pairs"
229
+
230
+ **Cause**: Labels don't match images or format issues
231
+
232
+ **Solutions**:
233
+ - Verify image and label basenames match
234
+ - Check label images are integer type (not float/RGB)
235
+ - Ensure dimensions match between image and label
236
+
237
+ ### "Format Issues" Warning
238
+
239
+ **Cause**: Some label files not in expected format
240
+
241
+ **Possible Issues**:
242
+ - Label image is RGB/float instead of integer
243
+ - Label file corrupted or incompatible
244
+ - Dimension mismatch with image
245
+
246
+ **Solutions**:
247
+ - Convert labels to integer format if needed
248
+ - Regenerate problematic label files
249
+ - Verify with external tools (ImageJ, etc.)
250
+
251
+ ### Edits Not Saving
252
+
253
+ **Cause**: Wrong layer selected or permission issue
254
+
255
+ **Solutions**:
256
+ - Ensure "Labels" layer (right panel) is selected
257
+ - Check folder write permissions
258
+ - Verify label filename in confirmation message
259
+
260
+ ### Changes Lost After Clicking Previous
261
+
262
+ **Note**: Previous saves current edits first
263
+
264
+ If edits appear lost:
265
+ - Check file modification time
266
+ - Reload file to verify save
267
+ - Check for backup/version files
268
+
269
+ ## File Format Support
270
+
271
+ | Format | Input | Output | Status |
272
+ |--------|-------|--------|--------|
273
+ | TIF/TIFF | ✓ | ✓ | Full support |
274
+ | PNG | ✓ | ✓ | Full support |
275
+ | JPEG | ✓ (8-bit only) | ✗ | Read-only |
276
+ | Zarr | ✓ | Limited | Supported |
277
+ | HDF5 | ✗ | ✗ | Not supported |
278
+
279
+ ## Data Types Supported
280
+
281
+ | Type | Support |
282
+ |------|---------|
283
+ | uint8 | ✓ Full |
284
+ | uint16 | ✓ Full |
285
+ | uint32 | ✓ Full |
286
+ | int8, int16, int32 | ✓ Supported |
287
+ | float, RGB | ✗ Not supported (validation error) |
288
+
289
+ ## Related Features
290
+
291
+ - **[Cellpose Segmentation](cellpose_segmentation.md)** - Generate labels to inspect
292
+ - **[Batch Processing](basic_processing.md)** - Post-process labels
293
+ - **[Label Operations](basic_processing.md#label-image-operations)** - Filter/transform labels
294
+ - **[RegionProps Analysis](regionprops_analysis.md)** - Analyze edited labels
295
+
296
+ ## Technical Details
297
+
298
+ ### Workflow Architecture
299
+
300
+ ```
301
+ 1. User selects folder + suffix
302
+
303
+ 2. Widget scans folder
304
+
305
+ 3. Matches image-label pairs
306
+
307
+ 4. Validates formats
308
+
309
+ 5. Loads first pair into napari
310
+
311
+ 6. User edits labels
312
+
313
+ 7. Click "Save and Continue"
314
+
315
+ 8. Write label file to disk
316
+
317
+ 9. Load next pair (repeat from step 5)
318
+ ```
319
+
320
+ ### File Matching Logic
321
+
322
+ ```
323
+ Label suffix: "_labels"
324
+ Label file: sample1_labels.tif
325
+
326
+ 1. Extract base: "sample1"
327
+ 2. Find files starting with "sample1"
328
+ 3. Find files NOT equal to label file
329
+ 4. Find files with SAME extension (.tif)
330
+ 5. Match first found = Image file
331
+ ```
332
+
333
+ ### Format Validation
334
+
335
+ ```
336
+ For each label file:
337
+ 1. Read file (scikit-image imread)
338
+ 2. Check: Is dtype integer?
339
+ 3. Check: Does it load without error?
340
+ 4. Add to pairs list or report issue
341
+ ```
342
+
343
+ ## Citation
344
+
345
+ If you use Batch Label Inspection in your research, please cite:
346
+
347
+ ```bibtex
348
+ @software{napari_tmidas_2024,
349
+ title = {napari-tmidas: Batch Image Processing for Microscopy},
350
+ author = {Mercader Lab},
351
+ year = {2024},
352
+ url = {https://github.com/MercaderLabAnatomy/napari-tmidas}
353
+ }
354
+ ```
@@ -257,3 +257,22 @@ class TestCLAHE:
257
257
  assert result_float32.dtype == np.float32
258
258
  assert result_float32.max() <= 1.0
259
259
  assert result_float32.min() >= 0.0
260
+
261
+ def test_clahe_max_workers_parameter(self):
262
+ """Test that max_workers parameter is respected"""
263
+ # Create a 4D image that will trigger parallel processing
264
+ image_4d = np.random.rand(10, 50, 100, 100) * 0.5
265
+
266
+ # Test with different worker counts
267
+ result_1 = equalize_histogram(image_4d, max_workers=1)
268
+ result_4 = equalize_histogram(image_4d, max_workers=4)
269
+ result_8 = equalize_histogram(image_4d, max_workers=8)
270
+
271
+ # All should produce same shape
272
+ assert result_1.shape == image_4d.shape
273
+ assert result_4.shape == image_4d.shape
274
+ assert result_8.shape == image_4d.shape
275
+
276
+ # Results should be nearly identical (some floating point differences are OK)
277
+ np.testing.assert_allclose(result_1, result_4, rtol=1e-5)
278
+ np.testing.assert_allclose(result_1, result_8, rtol=1e-5)
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.3.2'
32
- __version_tuple__ = version_tuple = (0, 3, 2)
31
+ __version__ = version = '0.3.4'
32
+ __version_tuple__ = version_tuple = (0, 3, 4)
33
33
 
34
- __commit_id__ = commit_id = 'gfa06bb8df'
34
+ __commit_id__ = commit_id = 'g0775e99c0'
@@ -3,7 +3,6 @@
3
3
  Processing functions that depend on scikit-image.
4
4
  """
5
5
  import concurrent.futures
6
- import os
7
6
 
8
7
  import numpy as np
9
8
 
@@ -49,10 +48,18 @@ if SKIMAGE_AVAILABLE:
49
48
  "default": 0,
50
49
  "description": "Size of the local region (0 = auto-calculate based on image size). For small features use smaller values (e.g., 32), for large features use larger values (e.g., 128)",
51
50
  },
51
+ "max_workers": {
52
+ "type": int,
53
+ "default": 4,
54
+ "description": "Maximum number of parallel workers (default: 4). Lower values use less memory but are slower. Range: 1-16",
55
+ },
52
56
  },
53
57
  )
54
58
  def equalize_histogram(
55
- image: np.ndarray, clip_limit: float = 0.01, kernel_size: int = 0
59
+ image: np.ndarray,
60
+ clip_limit: float = 0.01,
61
+ kernel_size: int = 0,
62
+ max_workers: int = 4,
56
63
  ) -> np.ndarray:
57
64
  """
58
65
  Apply CLAHE (Contrast Limited Adaptive Histogram Equalization) to enhance local contrast.
@@ -70,23 +77,29 @@ if SKIMAGE_AVAILABLE:
70
77
  Higher values give more contrast but may amplify noise
71
78
  kernel_size : int
72
79
  Size of the contextual regions (0 = auto-calculate based on image size)
80
+ max_workers : int
81
+ Maximum number of parallel workers for processing large datasets (default: 4)
82
+ Lower values reduce memory usage, higher values increase speed
73
83
 
74
84
  Returns
75
85
  -------
76
86
  np.ndarray
77
87
  CLAHE-enhanced image with same dtype as input
78
-
88
+
79
89
  Notes
80
90
  -----
81
91
  For large multi-dimensional datasets (TZYX), processing is parallelized across
82
- the first dimension to utilize multiple CPU cores effectively.
92
+ the first dimension to utilize multiple CPU cores effectively. The max_workers
93
+ parameter controls memory usage vs speed tradeoff.
83
94
  """
84
95
  # Store original dtype to convert back later
85
96
  original_dtype = image.dtype
86
-
97
+
87
98
  # Print diagnostic info for multi-dimensional data
88
99
  if image.ndim > 2:
89
- print(f"Applying CLAHE to {image.ndim}D image with shape {image.shape}")
100
+ print(
101
+ f"Applying CLAHE to {image.ndim}D image with shape {image.shape}"
102
+ )
90
103
 
91
104
  # Auto-calculate kernel size if not specified
92
105
  if kernel_size <= 0:
@@ -99,14 +112,19 @@ if SKIMAGE_AVAILABLE:
99
112
  # Ensure kernel_size is odd
100
113
  if kernel_size % 2 == 0:
101
114
  kernel_size += 1
102
-
115
+
103
116
  if image.ndim > 2:
104
117
  print(f"Using kernel_size={kernel_size}, clip_limit={clip_limit}")
105
-
118
+
119
+ # Clamp max_workers to reasonable range
120
+ max_workers = max(1, min(max_workers, 16))
121
+
106
122
  # For 4D data (TZYX), parallelize across first dimension for better performance
107
123
  if image.ndim == 4 and image.shape[0] > 1:
108
- print(f"Parallelizing CLAHE across {image.shape[0]} timepoints/slices using {max(1, os.cpu_count() - 1)} workers...")
109
-
124
+ print(
125
+ f"Parallelizing CLAHE across {image.shape[0]} timepoints/slices using {max_workers} workers..."
126
+ )
127
+
110
128
  def process_slice(idx):
111
129
  """Process a single slice along first dimension"""
112
130
  result_slice = skimage.exposure.equalize_adapthist(
@@ -115,21 +133,26 @@ if SKIMAGE_AVAILABLE:
115
133
  if (idx + 1) % max(1, image.shape[0] // 10) == 0:
116
134
  print(f" Processed {idx + 1}/{image.shape[0]} slices")
117
135
  return result_slice
118
-
119
- # Process in parallel
136
+
137
+ # Process in parallel with limited workers to control memory usage
120
138
  with concurrent.futures.ThreadPoolExecutor(
121
- max_workers=max(1, os.cpu_count() - 1)
139
+ max_workers=max_workers
122
140
  ) as executor:
123
- futures = [executor.submit(process_slice, i) for i in range(image.shape[0])]
141
+ futures = [
142
+ executor.submit(process_slice, i)
143
+ for i in range(image.shape[0])
144
+ ]
124
145
  results = [future.result() for future in futures]
125
-
146
+
126
147
  result = np.stack(results, axis=0)
127
148
  print("CLAHE processing complete!")
128
-
149
+
129
150
  elif image.ndim == 3 and image.shape[0] > 5:
130
151
  # For 3D data with many slices, also parallelize
131
- print(f"Parallelizing CLAHE across {image.shape[0]} slices using {max(1, os.cpu_count() - 1)} workers...")
132
-
152
+ print(
153
+ f"Parallelizing CLAHE across {image.shape[0]} slices using {max_workers} workers..."
154
+ )
155
+
133
156
  def process_slice(idx):
134
157
  """Process a single 2D slice"""
135
158
  result_slice = skimage.exposure.equalize_adapthist(
@@ -138,14 +161,17 @@ if SKIMAGE_AVAILABLE:
138
161
  if (idx + 1) % max(1, image.shape[0] // 10) == 0:
139
162
  print(f" Processed {idx + 1}/{image.shape[0]} slices")
140
163
  return result_slice
141
-
142
- # Process in parallel
164
+
165
+ # Process in parallel with limited workers to control memory usage
143
166
  with concurrent.futures.ThreadPoolExecutor(
144
- max_workers=max(1, os.cpu_count() - 1)
167
+ max_workers=max_workers
145
168
  ) as executor:
146
- futures = [executor.submit(process_slice, i) for i in range(image.shape[0])]
169
+ futures = [
170
+ executor.submit(process_slice, i)
171
+ for i in range(image.shape[0])
172
+ ]
147
173
  results = [future.result() for future in futures]
148
-
174
+
149
175
  result = np.stack(results, axis=0)
150
176
  print("CLAHE processing complete!")
151
177
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: napari-tmidas
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
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
@@ -159,9 +159,9 @@ Then find napari-tmidas in the **Plugins** menu. [Watch video tutorials →](htt
159
159
 
160
160
  ### Advanced Features
161
161
 
162
- - [SAM2 Crop Anything](docs/advanced_processing.md#sam2) - Interactive object cropping
162
+ - [Batch Crop Anything](docs/crop_anything.md) - Interactive object cropping with SAM2
163
+ - [Batch Label Inspection](docs/batch_label_inspection.md) - Manual label verification and editing
163
164
  - [Advanced Filters](docs/advanced_processing.md) - SciPy/scikit-image filters
164
- - [Batch Label Inspection](docs/basic_processing.md#label-inspection) - Manual correction workflow
165
165
 
166
166
  ## 💻 Installation
167
167
 
@@ -12,6 +12,7 @@ tox.ini
12
12
  .napari-hub/config.yml
13
13
  docs/advanced_processing.md
14
14
  docs/basic_processing.md
15
+ docs/batch_label_inspection.md
15
16
  docs/careamics_denoising.md
16
17
  docs/cellpose_segmentation.md
17
18
  docs/crop_anything.md
File without changes
File without changes
File without changes
File without changes
File without changes