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.
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/PKG-INFO +3 -3
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/README.md +2 -2
- napari_tmidas-0.3.4/docs/batch_label_inspection.md +354 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_skimage_filters.py +19 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_version.py +3 -3
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/skimage_filters.py +49 -23
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas.egg-info/PKG-INFO +3 -3
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas.egg-info/SOURCES.txt +1 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/.github/dependabot.yml +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/.github/workflows/test_and_deploy.yml +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/.gitignore +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/.napari-hub/DESCRIPTION.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/.napari-hub/config.yml +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/.pre-commit-config.yaml +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/LICENSE +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/MANIFEST.in +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/advanced_processing.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/basic_processing.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/careamics_denoising.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/cellpose_segmentation.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/crop_anything.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/file_conversion.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/grid_view_overlay.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/intensity_label_filter.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/regionprops_analysis.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/regionprops_summary.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/spotiflow_detection.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/trackastra_tracking.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/docs/viscy_virtual_staining.md +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/examples/grid_overlay_example.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/examples/intensity_filter_example.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/examples/regionprops_example.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/pyproject.toml +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/setup.cfg +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/__init__.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_crop_anything.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_env_manager.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_file_conversion.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_file_selector.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_label_inspection.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_processing_worker.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_reader.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_registry.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_roi_colocalization.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_sample_data.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_crop_anything.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_env_manager.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_file_selector.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_grid_view_overlay.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_init.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_intensity_label_filter.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_label_inspection.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_processing_basic.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_processing_worker.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_reader.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_regionprops_analysis.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_registry.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_sample_data.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_scipy_filters.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_split_channels.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_spotiflow.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_tyx_display_fix.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_ui_utils.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_viscy_virtual_staining.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_widget.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_windows_basic.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_writer.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_ui_utils.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_widget.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_writer.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/napari.yaml +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/__init__.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/basic.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/careamics_denoising.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/careamics_env_manager.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/cellpose_env_manager.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/cellpose_segmentation.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/colocalization.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/file_compression.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/grid_view_overlay.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/intensity_label_filter.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/regionprops_analysis.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/sam2_env_manager.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/sam2_mp4.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/scipy_filters.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/spotiflow_detection.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/spotiflow_env_manager.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/timepoint_merger.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/trackastra_tracking.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/viscy_env_manager.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/viscy_virtual_staining.py +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas.egg-info/dependency_links.txt +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas.egg-info/entry_points.txt +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas.egg-info/requires.txt +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas.egg-info/top_level.txt +0 -0
- {napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/test_grid_overlay.py +0 -0
- {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.
|
|
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
|
-
- [
|
|
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
|
-
- [
|
|
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
|
+
```
|
{napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_skimage_filters.py
RENAMED
|
@@ -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.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 3,
|
|
31
|
+
__version__ = version = '0.3.4'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 3, 4)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
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,
|
|
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(
|
|
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(
|
|
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=
|
|
139
|
+
max_workers=max_workers
|
|
122
140
|
) as executor:
|
|
123
|
-
futures = [
|
|
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(
|
|
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=
|
|
167
|
+
max_workers=max_workers
|
|
145
168
|
) as executor:
|
|
146
|
-
futures = [
|
|
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.
|
|
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
|
-
- [
|
|
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
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_grid_view_overlay.py
RENAMED
|
File without changes
|
|
File without changes
|
{napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_intensity_label_filter.py
RENAMED
|
File without changes
|
{napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_label_inspection.py
RENAMED
|
File without changes
|
{napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_processing_basic.py
RENAMED
|
File without changes
|
{napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_processing_worker.py
RENAMED
|
File without changes
|
|
File without changes
|
{napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_regionprops_analysis.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_tyx_display_fix.py
RENAMED
|
File without changes
|
|
File without changes
|
{napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/_tests/test_viscy_virtual_staining.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/colocalization.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/sam2_mp4.py
RENAMED
|
File without changes
|
{napari_tmidas-0.3.2 → napari_tmidas-0.3.4}/src/napari_tmidas/processing_functions/scipy_filters.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|