Skip to content

Commit 263f49c

Browse files
committed
Add resumable timepoint processing and RAM-safe ConvPaint+Cellpose tuning
1 parent 10bd347 commit 263f49c

20 files changed

Lines changed: 3784 additions & 238 deletions

.github/copilot-instructions.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
# GitHub Copilot Instructions for napari-tmidas
3+
4+
## Mandatory Environment Activation
5+
6+
Before running any repository analysis, tests, scripts, or Python commands, always activate the `napari-tmidas` mamba environment first.
7+
8+
Use this exact command:
9+
`source ~/miniforge3/etc/profile.d/conda.sh && mamba activate napari-tmidas`
10+
11+
After activation, verify that the active environment is `napari-tmidas`.
12+
If activation fails, stop immediately and report the failure instead of continuing in a different environment.
13+
14+
## Zarr processing functions
15+
16+
When writing or modifying any function that reads a zarr and writes a processed zarr:
17+
18+
- **Preserve the source pyramid structure.** The output zarr must have the same number of pyramid levels as the source. Do not add levels that do not exist in the source; do not remove levels that do.
19+
- If the source has only one level (no pyramid), write the output with `max_layer=0` (i.e. a single level).
20+
- The general rule: **copy everything from the source except the data dimensions/values that the function explicitly modifies.** This includes number of pyramid levels, axes metadata, coordinate transforms, chunk layout, omero channel metadata, and data type.
21+
- When using `ome_zarr.writer.write_image`, always derive `max_layer` from the source: `max_layer = len(source_levels) - 1`.
22+
- Never hard-code `max_layer=4` or any fixed pyramid depth.

docs/multichannel_processing.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# Multichannel Processing with Channel Selection
2+
3+
## Overview
4+
5+
The napari-tmidas batch processing system now supports automatic detection of multichannel images (especially from zarr files) and allows users to select which channel(s) to process.
6+
7+
## Features
8+
9+
### 1. Automatic Channel Detection
10+
When loading multichannel zarr files or other image formats, the system automatically detects:
11+
- Number of channels in the image
12+
- The axis where channels are located (C axis)
13+
- Common patterns: CYX, CZYX, TCYX, TCZYX
14+
15+
### 2. Channel Selection UI
16+
Processing functions can add a `channel` parameter with `widget_type: "channel_selector"` to enable channel selection in the UI:
17+
18+
```python
19+
@BatchProcessingRegistry.register(
20+
name="My Processing Function",
21+
suffix="_processed",
22+
description="Process images with channel selection",
23+
parameters={
24+
"channel": {
25+
"type": str,
26+
"default": "all",
27+
"widget_type": "channel_selector",
28+
"description": "Select which channel to process",
29+
},
30+
# ... other parameters
31+
},
32+
)
33+
def my_function(image: np.ndarray, channel: str = "all") -> np.ndarray:
34+
# The processing worker handles channel extraction automatically
35+
# You receive the selected channel as input
36+
return processed_image
37+
```
38+
39+
### 3. Channel Selection Options
40+
Users can select:
41+
- **All channels**: Process all channels separately, creating one output file per channel
42+
- **Channel 0, 1, 2, ...**: Process only a specific channel
43+
44+
## How It Works
45+
46+
### Detection Phase
47+
1. When files are selected, the first file is loaded
48+
2. `detect_channels_in_image()` analyzes the image shape
49+
3. Channels are detected based on common patterns (small dimension: 2-10 values)
50+
51+
### Processing Phase
52+
1. User selects which channel(s) to process via the dropdown
53+
2. `ProcessingWorker` extracts the selected channel(s) before processing
54+
3. The processing function receives the extracted channel data
55+
4. Results are saved with appropriate channel suffixes
56+
57+
### Output Files
58+
- **Single channel selected**: `filename_suffix.tif`
59+
- **All channels selected**: `filename_ch0_suffix.tif`, `filename_ch1_suffix.tif`, etc.
60+
61+
## Example Usage
62+
63+
### Adding Channel Selection to Your Function
64+
65+
```python
66+
from napari_tmidas._registry import BatchProcessingRegistry
67+
68+
@BatchProcessingRegistry.register(
69+
name="Gaussian Blur with Channel Selection",
70+
suffix="_blurred",
71+
description="Apply Gaussian blur to selected channel(s)",
72+
parameters={
73+
"sigma": {
74+
"type": float,
75+
"default": 1.0,
76+
"min": 0.1,
77+
"max": 10.0,
78+
"description": "Blur strength",
79+
},
80+
"channel": {
81+
"type": str,
82+
"default": "all",
83+
"widget_type": "channel_selector",
84+
"description": "Select channel to process",
85+
},
86+
},
87+
)
88+
def gaussian_blur_multichannel(image: np.ndarray, sigma: float = 1.0, channel: str = "all") -> np.ndarray:
89+
from scipy import ndimage
90+
# Note: channel parameter is handled by the processing worker
91+
# You receive the already-extracted channel data
92+
return ndimage.gaussian_filter(image, sigma=sigma)
93+
```
94+
95+
## Implementation Details
96+
97+
### Key Components
98+
99+
1. **`detect_channels_in_image()`** ([_file_selector.py](../src/napari_tmidas/_file_selector.py))
100+
- Detects channels in numpy arrays or OME-Zarr layer data
101+
- Returns (num_channels, channel_axis)
102+
- Handles common patterns: CYX, CZYX, TCYX, TCZYX
103+
104+
2. **`ParameterWidget.update_channel_selector()`** ([_file_selector.py](../src/napari_tmidas/_file_selector.py))
105+
- Populates the channel selector dropdown
106+
- Called when processing function is selected
107+
- Analyzes first file in the batch
108+
109+
3. **`ProcessingWorker.process_file()`** ([_processing_worker.py](../src/napari_tmidas/_processing_worker.py))
110+
- Extracts selected channel(s) before processing
111+
- Handles "all" channels by processing each separately
112+
- Manages output file naming with channel suffixes
113+
114+
### Supported Image Formats
115+
- **Zarr files** (`.zarr`): Full OME-Zarr support with metadata
116+
- **TIFF files** (`.tif`, `.tiff`): Multichannel TIFF stacks
117+
- **Other formats**: Any format loadable by napari
118+
119+
### Channel Detection Patterns
120+
The system detects channels when:
121+
- First dimension is 2-10 in size (CYX, CZYX patterns)
122+
- Second dimension is 2-10 in size (TCYX, TCZYX patterns)
123+
- Dimension is clearly smaller than spatial dimensions (Y, X)
124+
125+
## Benefits
126+
127+
1. **Flexibility**: Process all channels or just one
128+
2. **Efficiency**: Avoid processing unwanted channels
129+
3. **Clarity**: Clear output files with channel information
130+
4. **Automation**: No manual channel splitting needed
131+
132+
## Example Functions Using Channel Selection
133+
134+
- **Gaussian Blur**: Blur specific channels in multichannel images
135+
- _(Add your functions here)_
136+
137+
## Future Enhancements
138+
139+
Potential improvements:
140+
- Support for selecting multiple specific channels (e.g., channels 0 and 2)
141+
- Channel merging after processing
142+
- Preview of channel selection before batch processing
143+
- Custom channel naming/labeling

src/napari_tmidas/_file_conversion.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1979,13 +1979,17 @@ def _save_zarr(
19791979
metadata, axes, image_data.shape
19801980
)
19811981
pyramid_factors = (2, 4, 8, 16)
1982-
coordinate_transformations = (
1983-
self._build_pyramid_coordinate_transformations(
1984-
scale_transform,
1985-
axes or "zyx",
1986-
pyramid_factors,
1987-
)
1988-
)
1982+
1983+
# Build base-level coordinate transformation only
1984+
# When using scale_factors, OME-Zarr will automatically generate
1985+
# coordinate transformations for pyramid levels
1986+
base_coordinate_transform = None
1987+
if scale_transform:
1988+
base_scales = scale_transform.get("scale")
1989+
if base_scales and isinstance(base_scales, list):
1990+
base_coordinate_transform = [
1991+
[{"type": "scale", "scale": base_scales}]
1992+
]
19891993

19901994
# Save with OME-ZARR including physical metadata
19911995
with ProgressBar():
@@ -1995,12 +1999,12 @@ def _save_zarr(
19951999
root.attrs["name"] = layer_name
19962000

19972001
# Write the image with proper OME-ZARR structure and physical metadata
1998-
# coordinate_transformations expects a list of lists (one per resolution level)
2002+
# Pass scale_factors to generate pyramid and base coordinate transformation
19992003
write_image(
20002004
image_data,
20012005
group=root,
20022006
axes=axes or "zyx",
2003-
coordinate_transformations=coordinate_transformations,
2007+
coordinate_transformations=base_coordinate_transform,
20042008
scale_factors=pyramid_factors,
20052009
scaler=None,
20062010
storage_options={"compression": "zstd"},

0 commit comments

Comments
 (0)