napari-tmidas 0.2.2__py3-none-any.whl → 0.2.5__py3-none-any.whl
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/__init__.py +35 -5
- napari_tmidas/_crop_anything.py +1520 -609
- napari_tmidas/_env_manager.py +76 -0
- napari_tmidas/_file_conversion.py +1646 -1131
- napari_tmidas/_file_selector.py +1455 -216
- napari_tmidas/_label_inspection.py +83 -8
- napari_tmidas/_processing_worker.py +309 -0
- napari_tmidas/_reader.py +6 -10
- napari_tmidas/_registry.py +2 -2
- napari_tmidas/_roi_colocalization.py +1221 -84
- napari_tmidas/_tests/test_crop_anything.py +123 -0
- napari_tmidas/_tests/test_env_manager.py +89 -0
- napari_tmidas/_tests/test_grid_view_overlay.py +193 -0
- napari_tmidas/_tests/test_init.py +98 -0
- napari_tmidas/_tests/test_intensity_label_filter.py +222 -0
- napari_tmidas/_tests/test_label_inspection.py +86 -0
- napari_tmidas/_tests/test_processing_basic.py +500 -0
- napari_tmidas/_tests/test_processing_worker.py +142 -0
- napari_tmidas/_tests/test_regionprops_analysis.py +547 -0
- napari_tmidas/_tests/test_registry.py +70 -2
- napari_tmidas/_tests/test_scipy_filters.py +168 -0
- napari_tmidas/_tests/test_skimage_filters.py +259 -0
- napari_tmidas/_tests/test_split_channels.py +217 -0
- napari_tmidas/_tests/test_spotiflow.py +87 -0
- napari_tmidas/_tests/test_tyx_display_fix.py +142 -0
- napari_tmidas/_tests/test_ui_utils.py +68 -0
- napari_tmidas/_tests/test_widget.py +30 -0
- napari_tmidas/_tests/test_windows_basic.py +66 -0
- napari_tmidas/_ui_utils.py +57 -0
- napari_tmidas/_version.py +16 -3
- napari_tmidas/_widget.py +41 -4
- napari_tmidas/processing_functions/basic.py +557 -20
- napari_tmidas/processing_functions/careamics_env_manager.py +72 -99
- napari_tmidas/processing_functions/cellpose_env_manager.py +415 -112
- napari_tmidas/processing_functions/cellpose_segmentation.py +132 -191
- napari_tmidas/processing_functions/colocalization.py +513 -56
- napari_tmidas/processing_functions/grid_view_overlay.py +703 -0
- napari_tmidas/processing_functions/intensity_label_filter.py +422 -0
- napari_tmidas/processing_functions/regionprops_analysis.py +1280 -0
- napari_tmidas/processing_functions/sam2_env_manager.py +53 -69
- napari_tmidas/processing_functions/sam2_mp4.py +274 -195
- napari_tmidas/processing_functions/scipy_filters.py +403 -8
- napari_tmidas/processing_functions/skimage_filters.py +424 -212
- napari_tmidas/processing_functions/spotiflow_detection.py +949 -0
- napari_tmidas/processing_functions/spotiflow_env_manager.py +591 -0
- napari_tmidas/processing_functions/timepoint_merger.py +334 -86
- {napari_tmidas-0.2.2.dist-info → napari_tmidas-0.2.5.dist-info}/METADATA +71 -30
- napari_tmidas-0.2.5.dist-info/RECORD +63 -0
- napari_tmidas/_tests/__init__.py +0 -0
- napari_tmidas-0.2.2.dist-info/RECORD +0 -40
- {napari_tmidas-0.2.2.dist-info → napari_tmidas-0.2.5.dist-info}/WHEEL +0 -0
- {napari_tmidas-0.2.2.dist-info → napari_tmidas-0.2.5.dist-info}/entry_points.txt +0 -0
- {napari_tmidas-0.2.2.dist-info → napari_tmidas-0.2.5.dist-info}/licenses/LICENSE +0 -0
- {napari_tmidas-0.2.2.dist-info → napari_tmidas-0.2.5.dist-info}/top_level.txt +0 -0
|
@@ -5,152 +5,439 @@ Updated to support Cellpose 4 (Cellpose-SAM) installation.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import os
|
|
8
|
-
import platform
|
|
9
|
-
import shutil
|
|
10
8
|
import subprocess
|
|
9
|
+
import sys
|
|
11
10
|
import tempfile
|
|
12
|
-
import
|
|
11
|
+
import threading
|
|
12
|
+
from contextlib import suppress
|
|
13
13
|
|
|
14
14
|
import tifffile
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
from napari_tmidas._env_manager import BaseEnvironmentManager
|
|
17
|
+
|
|
18
|
+
# Global variable to track running processes for cancellation
|
|
19
|
+
_running_processes = []
|
|
20
|
+
_process_lock = threading.Lock()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def cancel_all_processes():
|
|
24
|
+
"""Cancel all running cellpose processes."""
|
|
25
|
+
with _process_lock:
|
|
26
|
+
for process in _running_processes[
|
|
27
|
+
:
|
|
28
|
+
]: # Copy list to avoid modification during iteration
|
|
29
|
+
try:
|
|
30
|
+
if process.poll() is None: # Process is still running
|
|
31
|
+
process.terminate()
|
|
32
|
+
# Give it a moment to terminate gracefully
|
|
33
|
+
try:
|
|
34
|
+
process.wait(timeout=5)
|
|
35
|
+
except subprocess.TimeoutExpired:
|
|
36
|
+
# Force kill if it doesn't terminate gracefully
|
|
37
|
+
process.kill()
|
|
38
|
+
process.wait()
|
|
39
|
+
_running_processes.remove(process)
|
|
40
|
+
except (OSError, subprocess.SubprocessError) as e:
|
|
41
|
+
print(f"Error terminating process: {e}")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _add_process(process):
|
|
45
|
+
"""Add a process to the tracking list."""
|
|
46
|
+
with _process_lock:
|
|
47
|
+
_running_processes.append(process)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _remove_process(process):
|
|
51
|
+
"""Remove a process from the tracking list."""
|
|
52
|
+
with _process_lock:
|
|
53
|
+
if process in _running_processes:
|
|
54
|
+
_running_processes.remove(process)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class CellposeEnvironmentManager(BaseEnvironmentManager):
|
|
58
|
+
"""Environment manager for Cellpose."""
|
|
59
|
+
|
|
60
|
+
def __init__(self):
|
|
61
|
+
super().__init__("cellpose")
|
|
62
|
+
|
|
63
|
+
def _install_dependencies(self, env_python: str) -> None:
|
|
64
|
+
"""Install Cellpose-specific dependencies."""
|
|
65
|
+
# Install cellpose 4 and other dependencies
|
|
66
|
+
print(
|
|
67
|
+
"Installing Cellpose 4 (Cellpose-SAM) in the dedicated environment..."
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Install packages one by one with error checking
|
|
71
|
+
packages = ["cellpose", "zarr", "tifffile"]
|
|
72
|
+
for package in packages:
|
|
73
|
+
print(f"Installing {package}...")
|
|
74
|
+
try:
|
|
75
|
+
subprocess.check_call(
|
|
76
|
+
[env_python, "-m", "pip", "install", package]
|
|
77
|
+
)
|
|
78
|
+
print(f"✓ {package} installed successfully")
|
|
79
|
+
except subprocess.CalledProcessError as e:
|
|
80
|
+
print(f"✗ Failed to install {package}: {e}")
|
|
81
|
+
raise
|
|
82
|
+
|
|
83
|
+
# Verify installations
|
|
84
|
+
print("Verifying installations...")
|
|
85
|
+
|
|
86
|
+
# Check cellpose
|
|
87
|
+
try:
|
|
88
|
+
result = subprocess.run(
|
|
89
|
+
[
|
|
90
|
+
env_python,
|
|
91
|
+
"-c",
|
|
92
|
+
"from cellpose import core; print(f'GPU available: {core.use_gpu()}')",
|
|
93
|
+
],
|
|
94
|
+
capture_output=True,
|
|
95
|
+
text=True,
|
|
96
|
+
check=True,
|
|
97
|
+
)
|
|
98
|
+
print("✓ Cellpose installation verified:")
|
|
99
|
+
print(result.stdout)
|
|
100
|
+
except subprocess.CalledProcessError as e:
|
|
101
|
+
print(f"✗ Cellpose verification failed: {e}")
|
|
102
|
+
raise
|
|
103
|
+
|
|
104
|
+
# Check zarr
|
|
105
|
+
try:
|
|
106
|
+
result = subprocess.run(
|
|
107
|
+
[
|
|
108
|
+
env_python,
|
|
109
|
+
"-c",
|
|
110
|
+
"import zarr; print(f'Zarr version: {zarr.__version__}')",
|
|
111
|
+
],
|
|
112
|
+
capture_output=True,
|
|
113
|
+
text=True,
|
|
114
|
+
check=True,
|
|
115
|
+
)
|
|
116
|
+
print("✓ Zarr installation verified:")
|
|
117
|
+
print(result.stdout)
|
|
118
|
+
except subprocess.CalledProcessError as e:
|
|
119
|
+
print(f"✗ Zarr verification failed: {e}")
|
|
120
|
+
raise
|
|
121
|
+
|
|
122
|
+
# Check tifffile
|
|
123
|
+
try:
|
|
124
|
+
result = subprocess.run(
|
|
125
|
+
[
|
|
126
|
+
env_python,
|
|
127
|
+
"-c",
|
|
128
|
+
"import tifffile; print(f'Tifffile version: {tifffile.__version__}')",
|
|
129
|
+
],
|
|
130
|
+
capture_output=True,
|
|
131
|
+
text=True,
|
|
132
|
+
check=True,
|
|
133
|
+
)
|
|
134
|
+
print("✓ Tifffile installation verified:")
|
|
135
|
+
print(result.stdout)
|
|
136
|
+
except subprocess.CalledProcessError as e:
|
|
137
|
+
print(f"✗ Tifffile verification failed: {e}")
|
|
138
|
+
raise
|
|
139
|
+
|
|
140
|
+
def is_package_installed(self) -> bool:
|
|
141
|
+
"""Check if cellpose is installed in the current environment."""
|
|
142
|
+
try:
|
|
143
|
+
import importlib.util
|
|
144
|
+
|
|
145
|
+
return importlib.util.find_spec("cellpose") is not None
|
|
146
|
+
except ImportError:
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
def are_all_packages_installed(self) -> bool:
|
|
150
|
+
"""Check if all required packages are installed in the dedicated environment."""
|
|
151
|
+
if not self.is_env_created():
|
|
152
|
+
return False
|
|
153
|
+
|
|
154
|
+
env_python = self.get_env_python_path()
|
|
155
|
+
required_packages = ["cellpose", "zarr", "tifffile"]
|
|
156
|
+
|
|
157
|
+
for package in required_packages:
|
|
158
|
+
try:
|
|
159
|
+
subprocess.run(
|
|
160
|
+
[env_python, "-c", f"import {package}"],
|
|
161
|
+
capture_output=True,
|
|
162
|
+
text=True,
|
|
163
|
+
check=True,
|
|
164
|
+
)
|
|
165
|
+
except subprocess.CalledProcessError:
|
|
166
|
+
print(f"Missing package in cellpose environment: {package}")
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
return True
|
|
170
|
+
|
|
171
|
+
def reinstall_packages(self) -> None:
|
|
172
|
+
"""Force reinstall all packages in the dedicated environment."""
|
|
173
|
+
if not self.is_env_created():
|
|
174
|
+
print("Environment not created. Creating new environment...")
|
|
175
|
+
self.create_env()
|
|
176
|
+
return
|
|
177
|
+
|
|
178
|
+
env_python = self.get_env_python_path()
|
|
179
|
+
print("Force reinstalling packages in cellpose environment...")
|
|
180
|
+
self._install_dependencies(env_python)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
# Global instance for backward compatibility
|
|
184
|
+
manager = CellposeEnvironmentManager()
|
|
20
185
|
|
|
21
186
|
|
|
22
187
|
def is_cellpose_installed():
|
|
23
188
|
"""Check if cellpose is installed in the current environment."""
|
|
24
|
-
|
|
25
|
-
import importlib.util
|
|
26
|
-
|
|
27
|
-
return importlib.util.find_spec("cellpose") is not None
|
|
28
|
-
except ImportError:
|
|
29
|
-
return False
|
|
189
|
+
return manager.is_package_installed()
|
|
30
190
|
|
|
31
191
|
|
|
32
192
|
def is_env_created():
|
|
33
193
|
"""Check if the dedicated environment exists."""
|
|
34
|
-
|
|
35
|
-
return os.path.exists(env_python)
|
|
194
|
+
return manager.is_env_created()
|
|
36
195
|
|
|
37
196
|
|
|
38
197
|
def get_env_python_path():
|
|
39
198
|
"""Get the path to the Python executable in the environment."""
|
|
40
|
-
|
|
41
|
-
return os.path.join(ENV_DIR, "Scripts", "python.exe")
|
|
42
|
-
else:
|
|
43
|
-
return os.path.join(ENV_DIR, "bin", "python")
|
|
199
|
+
return manager.get_env_python_path()
|
|
44
200
|
|
|
45
201
|
|
|
46
202
|
def create_cellpose_env():
|
|
47
203
|
"""Create a dedicated virtual environment for Cellpose."""
|
|
48
|
-
|
|
49
|
-
os.makedirs(os.path.dirname(ENV_DIR), exist_ok=True)
|
|
50
|
-
|
|
51
|
-
# Remove existing environment if it exists
|
|
52
|
-
if os.path.exists(ENV_DIR):
|
|
53
|
-
shutil.rmtree(ENV_DIR)
|
|
54
|
-
|
|
55
|
-
print(f"Creating Cellpose environment at {ENV_DIR}...")
|
|
204
|
+
return manager.create_env()
|
|
56
205
|
|
|
57
|
-
# Create a new virtual environment
|
|
58
|
-
venv.create(ENV_DIR, with_pip=True)
|
|
59
206
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
# # Install numpy first to ensure correct version
|
|
64
|
-
# print("Installing NumPy...")
|
|
65
|
-
# subprocess.check_call(
|
|
66
|
-
# [env_python, "-m", "pip", "install", "--upgrade", "pip"]
|
|
67
|
-
# )
|
|
68
|
-
# subprocess.check_call(
|
|
69
|
-
# [env_python, "-m", "pip", "install", "numpy>=1.24,<1.25"]
|
|
70
|
-
# )
|
|
71
|
-
|
|
72
|
-
# Install cellpose 4 and other dependencies
|
|
73
|
-
print(
|
|
74
|
-
"Installing Cellpose 4 (Cellpose-SAM) in the dedicated environment..."
|
|
75
|
-
)
|
|
76
|
-
subprocess.check_call([env_python, "-m", "pip", "install", "cellpose"])
|
|
207
|
+
def check_cellpose_packages():
|
|
208
|
+
"""Check if all required packages are installed in the cellpose environment."""
|
|
209
|
+
return manager.are_all_packages_installed()
|
|
77
210
|
|
|
78
|
-
# # Install PyTorch - needed for GPU acceleration
|
|
79
|
-
# print("Installing PyTorch for GPU acceleration...")
|
|
80
|
-
# subprocess.check_call([
|
|
81
|
-
# env_python, "-m", "pip", "install",
|
|
82
|
-
# "torch", "torchvision"
|
|
83
|
-
# ])
|
|
84
211
|
|
|
85
|
-
|
|
86
|
-
|
|
212
|
+
def reinstall_cellpose_packages():
|
|
213
|
+
"""Force reinstall all packages in the cellpose environment."""
|
|
214
|
+
return manager.reinstall_packages()
|
|
87
215
|
|
|
88
|
-
# Check if installation was successful
|
|
89
|
-
try:
|
|
90
|
-
# Run a command to check if cellpose can be imported and GPU is available
|
|
91
|
-
result = subprocess.run(
|
|
92
|
-
[
|
|
93
|
-
env_python,
|
|
94
|
-
"-c",
|
|
95
|
-
"from cellpose import core; print(f'GPU available: {core.use_gpu()}')",
|
|
96
|
-
],
|
|
97
|
-
capture_output=True,
|
|
98
|
-
text=True,
|
|
99
|
-
check=True,
|
|
100
|
-
)
|
|
101
|
-
print(f"Cellpose environment check: {result.stdout.strip()}")
|
|
102
|
-
except subprocess.CalledProcessError as e:
|
|
103
|
-
print(f"Warning: Cellpose installation check failed: {e.stderr}")
|
|
104
216
|
|
|
105
|
-
|
|
106
|
-
|
|
217
|
+
def cancel_cellpose_processing():
|
|
218
|
+
"""Cancel all running cellpose processes."""
|
|
219
|
+
cancel_all_processes()
|
|
107
220
|
|
|
108
221
|
|
|
109
222
|
def run_cellpose_in_env(func_name, args_dict):
|
|
110
223
|
"""
|
|
111
|
-
Run Cellpose in a dedicated environment with
|
|
112
|
-
|
|
113
|
-
Parameters:
|
|
114
|
-
-----------
|
|
115
|
-
func_name : str
|
|
116
|
-
Name of the Cellpose function to run (currently unused)
|
|
117
|
-
args_dict : dict
|
|
118
|
-
Dictionary of arguments for Cellpose segmentation
|
|
119
|
-
|
|
120
|
-
Returns:
|
|
121
|
-
--------
|
|
122
|
-
numpy.ndarray
|
|
123
|
-
Segmentation masks
|
|
224
|
+
Run Cellpose in a dedicated environment with optimized zarr support.
|
|
124
225
|
"""
|
|
125
226
|
# Ensure the environment exists
|
|
126
227
|
if not is_env_created():
|
|
127
228
|
create_cellpose_env()
|
|
128
229
|
|
|
129
|
-
#
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
230
|
+
# Check if all required packages are installed
|
|
231
|
+
if not manager.are_all_packages_installed():
|
|
232
|
+
print("Missing packages detected. Reinstalling...")
|
|
233
|
+
manager.reinstall_packages()
|
|
234
|
+
|
|
235
|
+
# Check for zarr optimization
|
|
236
|
+
use_zarr_direct = "zarr_path" in args_dict
|
|
237
|
+
|
|
238
|
+
if use_zarr_direct:
|
|
239
|
+
zarr_path = args_dict["zarr_path"]
|
|
240
|
+
print(f"Using optimized zarr processing for: {zarr_path}")
|
|
241
|
+
return run_zarr_processing(zarr_path, args_dict)
|
|
242
|
+
else:
|
|
243
|
+
return run_legacy_processing(args_dict)
|
|
137
244
|
|
|
138
|
-
# Save input image
|
|
139
|
-
tifffile.imwrite(input_file.name, args_dict["image"])
|
|
140
245
|
|
|
141
|
-
|
|
142
|
-
|
|
246
|
+
def run_zarr_processing(zarr_path, args_dict):
|
|
247
|
+
"""Process zarr files directly without temporary input files."""
|
|
248
|
+
|
|
249
|
+
with (
|
|
250
|
+
tempfile.NamedTemporaryFile(
|
|
251
|
+
suffix=".tif", delete=False
|
|
252
|
+
) as output_file,
|
|
253
|
+
tempfile.NamedTemporaryFile(
|
|
254
|
+
mode="w", suffix=".py", delete=False
|
|
255
|
+
) as script_file,
|
|
256
|
+
):
|
|
257
|
+
|
|
258
|
+
# Create zarr processing script (similar to working TIFF script)
|
|
143
259
|
script = f"""
|
|
144
260
|
import numpy as np
|
|
261
|
+
import sys
|
|
145
262
|
from cellpose import models, core
|
|
146
263
|
import tifffile
|
|
264
|
+
import zarr
|
|
265
|
+
|
|
266
|
+
# Force output to flush immediately for real-time progress
|
|
267
|
+
import sys
|
|
268
|
+
sys.stdout.flush()
|
|
269
|
+
|
|
270
|
+
print("=== Cellpose Environment Info ===")
|
|
271
|
+
print(f"GPU available in dedicated environment: {{core.use_gpu()}}")
|
|
272
|
+
sys.stdout.flush()
|
|
273
|
+
|
|
274
|
+
def process_volume(image, name=""):
|
|
275
|
+
print(f"\\nProcessing {{name}}: shape={{image.shape}}, range={{np.min(image):.1f}}-{{np.max(image):.1f}}")
|
|
276
|
+
sys.stdout.flush()
|
|
277
|
+
|
|
278
|
+
# GPU detection IN THE DEDICATED ENVIRONMENT (not main environment)
|
|
279
|
+
gpu_available = core.use_gpu()
|
|
280
|
+
use_gpu_requested = {str(args_dict.get('use_gpu', True))} # Convert to string for f-string
|
|
281
|
+
actual_use_gpu = use_gpu_requested and gpu_available
|
|
282
|
+
|
|
283
|
+
print(f"GPU: requested={{use_gpu_requested}}, available={{gpu_available}}, using={{actual_use_gpu}}")
|
|
284
|
+
sys.stdout.flush()
|
|
285
|
+
|
|
286
|
+
# Create model with explicit GPU setting
|
|
287
|
+
print("Creating Cellpose model...")
|
|
288
|
+
sys.stdout.flush()
|
|
289
|
+
model = models.CellposeModel(gpu=actual_use_gpu)
|
|
290
|
+
print(f"Model created (GPU={{model.gpu}})")
|
|
291
|
+
sys.stdout.flush()
|
|
292
|
+
|
|
293
|
+
print("Running segmentation...")
|
|
294
|
+
sys.stdout.flush()
|
|
295
|
+
|
|
296
|
+
# Remove deprecated channels parameter for Cellpose v4.0.1+
|
|
297
|
+
masks, flows, styles = model.eval(
|
|
298
|
+
image,
|
|
299
|
+
# channels parameter removed - deprecated in v4.0.1+
|
|
300
|
+
flow_threshold={args_dict.get('flow_threshold', 0.4)},
|
|
301
|
+
cellprob_threshold={args_dict.get('cellprob_threshold', 0.0)},
|
|
302
|
+
batch_size={args_dict.get('batch_size', 32)},
|
|
303
|
+
normalize={args_dict.get('normalize', {'tile_norm_blocksize': 128})},
|
|
304
|
+
do_3D={args_dict.get('do_3D', False)},
|
|
305
|
+
flow3D_smooth={args_dict.get('flow3D_smooth', 0)},
|
|
306
|
+
anisotropy={args_dict.get('anisotropy', None)},
|
|
307
|
+
z_axis={args_dict.get('z_axis', 0)} if {args_dict.get('do_3D', False)} else None,
|
|
308
|
+
channel_axis={args_dict.get('channel_axis', None)}
|
|
309
|
+
)
|
|
147
310
|
|
|
311
|
+
object_count = np.max(masks)
|
|
312
|
+
print(f"Found {{object_count}} objects in {{name}}")
|
|
313
|
+
sys.stdout.flush()
|
|
314
|
+
return masks
|
|
315
|
+
|
|
316
|
+
print("Opening zarr: {zarr_path}")
|
|
317
|
+
sys.stdout.flush()
|
|
318
|
+
zarr_root = zarr.open('{zarr_path}', mode='r')
|
|
319
|
+
|
|
320
|
+
if hasattr(zarr_root, 'shape'):
|
|
321
|
+
image = np.array(zarr_root)
|
|
322
|
+
result = process_volume(image, "zarr")
|
|
323
|
+
else:
|
|
324
|
+
arrays = list(zarr_root.array_keys())
|
|
325
|
+
print(f"Arrays: {{arrays}}")
|
|
326
|
+
sys.stdout.flush()
|
|
327
|
+
zarr_array = zarr_root[arrays[0]]
|
|
328
|
+
print(f"Selected: {{arrays[0]}}, shape={{zarr_array.shape}}")
|
|
329
|
+
sys.stdout.flush()
|
|
330
|
+
|
|
331
|
+
if len(zarr_array.shape) == 5: # TCZYX
|
|
332
|
+
T, C, Z, Y, X = zarr_array.shape
|
|
333
|
+
print(f"5D TCZYX: T={{T}}, C={{C}}, Z={{Z}}, Y={{Y}}, X={{X}}")
|
|
334
|
+
print(f"Will process {{T*C}} T,C combinations")
|
|
335
|
+
sys.stdout.flush()
|
|
336
|
+
result = np.zeros((T, C, Z, Y, X), dtype=np.uint32)
|
|
337
|
+
|
|
338
|
+
for t in range(T):
|
|
339
|
+
for c in range(C):
|
|
340
|
+
print(f"\\n=== T={{t+1}}/{{T}}, C={{c+1}}/{{C}} ===")
|
|
341
|
+
sys.stdout.flush()
|
|
342
|
+
image = np.array(zarr_array[t, c, :, :, :])
|
|
343
|
+
masks = process_volume(image, f"T{{t+1}}C{{c+1}}")
|
|
344
|
+
result[t, c] = masks
|
|
345
|
+
|
|
346
|
+
elif len(zarr_array.shape) == 4: # 4D
|
|
347
|
+
dim1, Z, Y, X = zarr_array.shape
|
|
348
|
+
print(f"4D array: dim1={{dim1}}, Z={{Z}}, Y={{Y}}, X={{X}}")
|
|
349
|
+
sys.stdout.flush()
|
|
350
|
+
result = np.zeros((dim1, Z, Y, X), dtype=np.uint32)
|
|
351
|
+
|
|
352
|
+
for i in range(dim1):
|
|
353
|
+
print(f"\\n=== Volume {{i+1}}/{{dim1}} ===")
|
|
354
|
+
sys.stdout.flush()
|
|
355
|
+
image = np.array(zarr_array[i, :, :, :])
|
|
356
|
+
masks = process_volume(image, f"Vol{{i+1}}")
|
|
357
|
+
result[i] = masks
|
|
358
|
+
else:
|
|
359
|
+
image = np.array(zarr_array)
|
|
360
|
+
result = process_volume(image, "3D")
|
|
361
|
+
|
|
362
|
+
print(f"Saving results: shape={{result.shape}}, total objects={{np.max(result)}}")
|
|
363
|
+
sys.stdout.flush()
|
|
364
|
+
tifffile.imwrite('{output_file.name}', result.astype(np.uint32))
|
|
365
|
+
print("Complete!")
|
|
366
|
+
sys.stdout.flush()
|
|
367
|
+
"""
|
|
148
368
|
|
|
369
|
+
script_file.write(script)
|
|
370
|
+
script_file.flush()
|
|
371
|
+
|
|
372
|
+
try:
|
|
373
|
+
# Run with REAL-TIME output (don't capture, let it stream)
|
|
374
|
+
env_python = get_env_python_path()
|
|
375
|
+
print("=== Starting Cellpose Processing ===")
|
|
376
|
+
|
|
377
|
+
# Use Popen for real-time progress and cancellation support
|
|
378
|
+
process = subprocess.Popen(
|
|
379
|
+
[env_python, script_file.name],
|
|
380
|
+
cwd=os.getcwd(),
|
|
381
|
+
stdout=sys.stdout,
|
|
382
|
+
stderr=sys.stderr,
|
|
383
|
+
text=True,
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
# Track the process for cancellation
|
|
387
|
+
_add_process(process)
|
|
388
|
+
|
|
389
|
+
try:
|
|
390
|
+
# Wait for completion
|
|
391
|
+
returncode = process.wait()
|
|
392
|
+
|
|
393
|
+
if returncode != 0:
|
|
394
|
+
raise RuntimeError(
|
|
395
|
+
f"Cellpose failed with return code {returncode}"
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
finally:
|
|
399
|
+
# Remove from tracking regardless of outcome
|
|
400
|
+
_remove_process(process)
|
|
401
|
+
|
|
402
|
+
# Check if output file was created
|
|
403
|
+
if not os.path.exists(output_file.name):
|
|
404
|
+
raise RuntimeError("Output file was not created")
|
|
405
|
+
|
|
406
|
+
return tifffile.imread(output_file.name)
|
|
407
|
+
|
|
408
|
+
finally:
|
|
409
|
+
# Cleanup
|
|
410
|
+
for fname in [output_file.name, script_file.name]:
|
|
411
|
+
with suppress(OSError, FileNotFoundError):
|
|
412
|
+
os.unlink(fname)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def run_legacy_processing(args_dict):
|
|
416
|
+
"""Legacy processing for numpy arrays (original working TIFF approach)."""
|
|
417
|
+
|
|
418
|
+
with (
|
|
419
|
+
tempfile.NamedTemporaryFile(suffix=".tif", delete=False) as input_file,
|
|
420
|
+
tempfile.NamedTemporaryFile(
|
|
421
|
+
suffix=".tif", delete=False
|
|
422
|
+
) as output_file,
|
|
423
|
+
tempfile.NamedTemporaryFile(
|
|
424
|
+
mode="w", suffix=".py", delete=False
|
|
425
|
+
) as script_file,
|
|
426
|
+
):
|
|
427
|
+
|
|
428
|
+
# Save input image (exactly like original working code)
|
|
429
|
+
tifffile.imwrite(input_file.name, args_dict["image"])
|
|
430
|
+
|
|
431
|
+
# Create script (exactly like original working code)
|
|
432
|
+
script = f"""
|
|
433
|
+
import numpy as np
|
|
434
|
+
from cellpose import models, core
|
|
435
|
+
import tifffile
|
|
149
436
|
|
|
150
437
|
# Load image
|
|
151
438
|
image = tifffile.imread('{input_file.name}')
|
|
152
439
|
|
|
153
|
-
# Create and run model
|
|
440
|
+
# Create and run model (exactly like original working code)
|
|
154
441
|
model = models.CellposeModel(
|
|
155
442
|
gpu={args_dict.get('use_gpu', True)})
|
|
156
443
|
|
|
@@ -166,7 +453,10 @@ masks, flows, styles = model.eval(
|
|
|
166
453
|
batch_size={args_dict.get('batch_size', 32)},
|
|
167
454
|
normalize=normalize,
|
|
168
455
|
do_3D={args_dict.get('do_3D', False)},
|
|
169
|
-
|
|
456
|
+
flow3D_smooth={args_dict.get('flow3D_smooth', 0)},
|
|
457
|
+
anisotropy={args_dict.get('anisotropy', None)},
|
|
458
|
+
z_axis={args_dict.get('z_axis', 0)} if {args_dict.get('do_3D', False)} else None,
|
|
459
|
+
channel_axis={args_dict.get('channel_axis', None)}
|
|
170
460
|
)
|
|
171
461
|
|
|
172
462
|
# Save results
|
|
@@ -178,18 +468,33 @@ tifffile.imwrite('{output_file.name}', masks)
|
|
|
178
468
|
script_file.flush()
|
|
179
469
|
|
|
180
470
|
try:
|
|
181
|
-
# Run the script
|
|
471
|
+
# Run the script with cancellation support
|
|
182
472
|
env_python = get_env_python_path()
|
|
183
|
-
|
|
184
|
-
|
|
473
|
+
|
|
474
|
+
# Use Popen for cancellation support
|
|
475
|
+
process = subprocess.Popen(
|
|
476
|
+
[env_python, script_file.name],
|
|
477
|
+
stdout=subprocess.PIPE,
|
|
478
|
+
stderr=subprocess.PIPE,
|
|
479
|
+
text=True,
|
|
185
480
|
)
|
|
186
|
-
|
|
187
|
-
#
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
)
|
|
481
|
+
|
|
482
|
+
# Track the process for cancellation
|
|
483
|
+
_add_process(process)
|
|
484
|
+
|
|
485
|
+
try:
|
|
486
|
+
# Wait for completion and get output
|
|
487
|
+
stdout, stderr = process.communicate()
|
|
488
|
+
print("Stdout:", stdout)
|
|
489
|
+
|
|
490
|
+
# Check for errors
|
|
491
|
+
if process.returncode != 0:
|
|
492
|
+
print("Stderr:", stderr)
|
|
493
|
+
raise RuntimeError(f"Cellpose segmentation failed: {stderr}")
|
|
494
|
+
|
|
495
|
+
finally:
|
|
496
|
+
# Remove from tracking regardless of outcome
|
|
497
|
+
_remove_process(process)
|
|
193
498
|
|
|
194
499
|
# Read and return the results
|
|
195
500
|
return tifffile.imread(output_file.name)
|
|
@@ -199,9 +504,7 @@ tifffile.imwrite('{output_file.name}', masks)
|
|
|
199
504
|
raise
|
|
200
505
|
|
|
201
506
|
finally:
|
|
202
|
-
# Clean up temporary files
|
|
203
|
-
from contextlib import suppress
|
|
204
|
-
|
|
507
|
+
# Clean up temporary files
|
|
205
508
|
for fname in [input_file.name, output_file.name, script_file.name]:
|
|
206
509
|
with suppress(OSError, FileNotFoundError):
|
|
207
510
|
os.unlink(fname)
|