napari-tmidas 0.1.7.1__py3-none-any.whl → 0.1.8.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/_crop_anything.py +137 -5
- napari_tmidas/_file_conversion.py +40 -18
- napari_tmidas/_file_selector.py +120 -13
- napari_tmidas/_version.py +2 -2
- napari_tmidas/processing_functions/basic.py +104 -0
- napari_tmidas/processing_functions/cellpose_env_manager.py +172 -0
- napari_tmidas/processing_functions/cellpose_segmentation.py +511 -0
- napari_tmidas/processing_functions/colocalization.py +17 -19
- napari_tmidas/processing_functions/file_compression.py +205 -0
- napari_tmidas/processing_functions/skimage_filters.py +25 -6
- {napari_tmidas-0.1.7.1.dist-info → napari_tmidas-0.1.8.5.dist-info}/METADATA +33 -9
- {napari_tmidas-0.1.7.1.dist-info → napari_tmidas-0.1.8.5.dist-info}/RECORD +16 -13
- {napari_tmidas-0.1.7.1.dist-info → napari_tmidas-0.1.8.5.dist-info}/WHEEL +1 -1
- {napari_tmidas-0.1.7.1.dist-info → napari_tmidas-0.1.8.5.dist-info}/entry_points.txt +0 -0
- {napari_tmidas-0.1.7.1.dist-info → napari_tmidas-0.1.8.5.dist-info}/licenses/LICENSE +0 -0
- {napari_tmidas-0.1.7.1.dist-info → napari_tmidas-0.1.8.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# processing_functions/cellpose_env_manager.py
|
|
2
|
+
"""
|
|
3
|
+
This module manages a dedicated virtual environment for Cellpose.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import platform
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import tempfile
|
|
11
|
+
import venv
|
|
12
|
+
|
|
13
|
+
import tifffile
|
|
14
|
+
|
|
15
|
+
# Define the environment directory in user's home folder
|
|
16
|
+
ENV_DIR = os.path.join(
|
|
17
|
+
os.path.expanduser("~"), ".napari-tmidas", "envs", "cellpose"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def is_cellpose_installed():
|
|
22
|
+
"""Check if cellpose is installed in the current environment."""
|
|
23
|
+
try:
|
|
24
|
+
import importlib.util
|
|
25
|
+
|
|
26
|
+
return importlib.util.find_spec("cellpose") is not None
|
|
27
|
+
except ImportError:
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def is_env_created():
|
|
32
|
+
"""Check if the dedicated environment exists."""
|
|
33
|
+
env_python = get_env_python_path()
|
|
34
|
+
return os.path.exists(env_python)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_env_python_path():
|
|
38
|
+
"""Get the path to the Python executable in the environment."""
|
|
39
|
+
if platform.system() == "Windows":
|
|
40
|
+
return os.path.join(ENV_DIR, "Scripts", "python.exe")
|
|
41
|
+
else:
|
|
42
|
+
return os.path.join(ENV_DIR, "bin", "python")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def create_cellpose_env():
|
|
46
|
+
"""Create a dedicated virtual environment for Cellpose."""
|
|
47
|
+
# Ensure the environment directory exists
|
|
48
|
+
os.makedirs(os.path.dirname(ENV_DIR), exist_ok=True)
|
|
49
|
+
|
|
50
|
+
# Remove existing environment if it exists
|
|
51
|
+
if os.path.exists(ENV_DIR):
|
|
52
|
+
shutil.rmtree(ENV_DIR)
|
|
53
|
+
|
|
54
|
+
print(f"Creating Cellpose environment at {ENV_DIR}...")
|
|
55
|
+
|
|
56
|
+
# Create a new virtual environment
|
|
57
|
+
venv.create(ENV_DIR, with_pip=True)
|
|
58
|
+
|
|
59
|
+
# Path to the Python executable in the new environment
|
|
60
|
+
env_python = get_env_python_path()
|
|
61
|
+
|
|
62
|
+
# Install numpy first to ensure correct version
|
|
63
|
+
print("Installing NumPy...")
|
|
64
|
+
subprocess.check_call(
|
|
65
|
+
[env_python, "-m", "pip", "install", "numpy>=1.24,<1.25"]
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Install cellpose and other dependencies
|
|
69
|
+
print("Installing Cellpose in the dedicated environment...")
|
|
70
|
+
subprocess.check_call([env_python, "-m", "pip", "install", "cellpose"])
|
|
71
|
+
|
|
72
|
+
# Install tifffile for image handling
|
|
73
|
+
subprocess.check_call([env_python, "-m", "pip", "install", "tifffile"])
|
|
74
|
+
|
|
75
|
+
print("Cellpose environment created successfully.")
|
|
76
|
+
return env_python
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def run_cellpose_in_env(func_name, args_dict):
|
|
80
|
+
"""
|
|
81
|
+
Run Cellpose in a dedicated environment with minimal complexity.
|
|
82
|
+
|
|
83
|
+
Parameters:
|
|
84
|
+
-----------
|
|
85
|
+
func_name : str
|
|
86
|
+
Name of the Cellpose function to run (currently unused)
|
|
87
|
+
args_dict : dict
|
|
88
|
+
Dictionary of arguments for Cellpose segmentation
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
--------
|
|
92
|
+
numpy.ndarray
|
|
93
|
+
Segmentation masks
|
|
94
|
+
"""
|
|
95
|
+
# Ensure the environment exists
|
|
96
|
+
if not is_env_created():
|
|
97
|
+
create_cellpose_env()
|
|
98
|
+
|
|
99
|
+
# Prepare temporary files
|
|
100
|
+
with tempfile.NamedTemporaryFile(
|
|
101
|
+
suffix=".tif", delete=False
|
|
102
|
+
) as input_file, tempfile.NamedTemporaryFile(
|
|
103
|
+
suffix=".tif", delete=False
|
|
104
|
+
) as output_file, tempfile.NamedTemporaryFile(
|
|
105
|
+
mode="w", suffix=".py", delete=False
|
|
106
|
+
) as script_file:
|
|
107
|
+
|
|
108
|
+
# Save input image
|
|
109
|
+
tifffile.imwrite(input_file.name, args_dict["image"])
|
|
110
|
+
|
|
111
|
+
# Prepare a temporary script to run Cellpose
|
|
112
|
+
script = f"""
|
|
113
|
+
import numpy as np
|
|
114
|
+
from cellpose import models
|
|
115
|
+
import tifffile
|
|
116
|
+
|
|
117
|
+
# Load image
|
|
118
|
+
image = tifffile.imread('{input_file.name}')
|
|
119
|
+
|
|
120
|
+
# Create and run model
|
|
121
|
+
model = models.Cellpose(
|
|
122
|
+
gpu={args_dict.get('use_gpu', True)},
|
|
123
|
+
model_type='{args_dict.get('model_type', 'cyto3')}'
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Perform segmentation
|
|
127
|
+
masks, *_ = model.eval(
|
|
128
|
+
image,
|
|
129
|
+
diameter={args_dict.get('diameter', 30.0)},
|
|
130
|
+
flow_threshold={args_dict.get('flow_threshold', 0.4)},
|
|
131
|
+
channels={args_dict.get('channels', [0, 0])},
|
|
132
|
+
do_3D={args_dict.get('do_3D', False)},
|
|
133
|
+
z_axis={args_dict.get('z_axis', 0)} if {args_dict.get('do_3D', False)} else None
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Save results
|
|
137
|
+
tifffile.imwrite('{output_file.name}', masks)
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
# Write script
|
|
141
|
+
script_file.write(script)
|
|
142
|
+
script_file.flush()
|
|
143
|
+
|
|
144
|
+
try:
|
|
145
|
+
# Run the script in the dedicated environment
|
|
146
|
+
env_python = get_env_python_path()
|
|
147
|
+
result = subprocess.run(
|
|
148
|
+
[env_python, script_file.name], capture_output=True, text=True
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Check for errors
|
|
152
|
+
if result.returncode != 0:
|
|
153
|
+
print("Stdout:", result.stdout)
|
|
154
|
+
print("Stderr:", result.stderr)
|
|
155
|
+
raise RuntimeError(
|
|
156
|
+
f"Cellpose segmentation failed: {result.stderr}"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Read and return the results
|
|
160
|
+
return tifffile.imread(output_file.name)
|
|
161
|
+
|
|
162
|
+
except (RuntimeError, subprocess.CalledProcessError) as e:
|
|
163
|
+
print(f"Error in Cellpose segmentation: {e}")
|
|
164
|
+
raise
|
|
165
|
+
|
|
166
|
+
finally:
|
|
167
|
+
# Clean up temporary files using contextlib.suppress
|
|
168
|
+
from contextlib import suppress
|
|
169
|
+
|
|
170
|
+
for fname in [input_file.name, output_file.name, script_file.name]:
|
|
171
|
+
with suppress(OSError, FileNotFoundError):
|
|
172
|
+
os.unlink(fname)
|
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
# processing_functions/cellpose_segmentation.py
|
|
2
|
+
"""
|
|
3
|
+
Processing functions for automatic instance segmentation using Cellpose.
|
|
4
|
+
|
|
5
|
+
This module provides functionality to automatically segment cells or nuclei in images
|
|
6
|
+
using the Cellpose deep learning-based segmentation toolkit. It supports both 2D and 3D images,
|
|
7
|
+
various dimension orders, and handles time series data.
|
|
8
|
+
|
|
9
|
+
Note: This requires the cellpose library to be installed.
|
|
10
|
+
"""
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
# Import the environment manager
|
|
17
|
+
from napari_tmidas.processing_functions.cellpose_env_manager import (
|
|
18
|
+
create_cellpose_env,
|
|
19
|
+
is_env_created,
|
|
20
|
+
run_cellpose_in_env,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Check if cellpose is directly available in this environment
|
|
24
|
+
try:
|
|
25
|
+
from cellpose import core, models
|
|
26
|
+
|
|
27
|
+
CELLPOSE_AVAILABLE = True
|
|
28
|
+
USE_GPU = core.use_gpu()
|
|
29
|
+
USE_DEDICATED_ENV = False
|
|
30
|
+
print("Cellpose found in current environment. Using native import.")
|
|
31
|
+
except ImportError:
|
|
32
|
+
CELLPOSE_AVAILABLE = False
|
|
33
|
+
USE_GPU = False
|
|
34
|
+
USE_DEDICATED_ENV = True
|
|
35
|
+
print(
|
|
36
|
+
"Cellpose not found in current environment. Will use dedicated environment."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
from napari_tmidas._registry import BatchProcessingRegistry
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def transpose_dimensions(img, dim_order):
|
|
43
|
+
"""
|
|
44
|
+
Transpose image dimensions to match expected Cellpose input.
|
|
45
|
+
|
|
46
|
+
Parameters:
|
|
47
|
+
-----------
|
|
48
|
+
img : numpy.ndarray
|
|
49
|
+
Input image
|
|
50
|
+
dim_order : str
|
|
51
|
+
Dimension order of the input image (e.g., 'ZYX', 'TZYX', 'YXC')
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
--------
|
|
55
|
+
numpy.ndarray
|
|
56
|
+
Transposed image
|
|
57
|
+
str
|
|
58
|
+
New dimension order
|
|
59
|
+
bool
|
|
60
|
+
Whether the image is 3D
|
|
61
|
+
"""
|
|
62
|
+
# Handle time dimension if present
|
|
63
|
+
has_time = "T" in dim_order
|
|
64
|
+
|
|
65
|
+
# Determine if the image is 3D (has Z dimension)
|
|
66
|
+
is_3d = "Z" in dim_order
|
|
67
|
+
|
|
68
|
+
# Standardize dimension order
|
|
69
|
+
if has_time:
|
|
70
|
+
# For time series, we want to end up with TZYX
|
|
71
|
+
target_dims = "TZYX"
|
|
72
|
+
transpose_order = [
|
|
73
|
+
dim_order.index(d) for d in target_dims if d in dim_order
|
|
74
|
+
]
|
|
75
|
+
new_dim_order = "".join([dim_order[i] for i in transpose_order])
|
|
76
|
+
else:
|
|
77
|
+
# For single timepoints, we want ZYX
|
|
78
|
+
target_dims = "ZYX"
|
|
79
|
+
transpose_order = [
|
|
80
|
+
dim_order.index(d) for d in target_dims if d in dim_order
|
|
81
|
+
]
|
|
82
|
+
new_dim_order = "".join([dim_order[i] for i in transpose_order])
|
|
83
|
+
|
|
84
|
+
# Perform the transpose
|
|
85
|
+
img_transposed = np.transpose(img, transpose_order)
|
|
86
|
+
|
|
87
|
+
return img_transposed, new_dim_order, is_3d
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def run_cellpose(
|
|
91
|
+
img,
|
|
92
|
+
model,
|
|
93
|
+
channels,
|
|
94
|
+
diameter,
|
|
95
|
+
flow_threshold=0.4,
|
|
96
|
+
dim_order="ZYX",
|
|
97
|
+
max_pixels=4000000,
|
|
98
|
+
):
|
|
99
|
+
"""
|
|
100
|
+
Run Cellpose segmentation on an image.
|
|
101
|
+
|
|
102
|
+
Parameters:
|
|
103
|
+
-----------
|
|
104
|
+
img : numpy.ndarray
|
|
105
|
+
Input image
|
|
106
|
+
model : cellpose.models.Cellpose
|
|
107
|
+
Cellpose model to use
|
|
108
|
+
channels : list
|
|
109
|
+
Channels to use for segmentation [0,0] = grayscale, [1,0] = green channel, [2,0] = red channel
|
|
110
|
+
diameter : float
|
|
111
|
+
Diameter of objects to segment
|
|
112
|
+
flow_threshold : float
|
|
113
|
+
Flow threshold for Cellpose
|
|
114
|
+
dim_order : str
|
|
115
|
+
Dimension order of the input image
|
|
116
|
+
max_pixels : int
|
|
117
|
+
Maximum number of pixels to process (for 2D images)
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
--------
|
|
121
|
+
numpy.ndarray
|
|
122
|
+
Segmented image with labels
|
|
123
|
+
"""
|
|
124
|
+
# First check if the image is too large (for 2D images)
|
|
125
|
+
if len(img.shape) == 2 or (len(img.shape) == 3 and "C" in dim_order):
|
|
126
|
+
# For 2D images (potentially with channels)
|
|
127
|
+
height, width = img.shape[:2]
|
|
128
|
+
total_pixels = height * width
|
|
129
|
+
if total_pixels > max_pixels:
|
|
130
|
+
raise ValueError(
|
|
131
|
+
f"Image size ({height}x{width}={total_pixels} pixels) exceeds the "
|
|
132
|
+
f"maximum size of {max_pixels} pixels. Consider downsampling."
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Transpose to expected dimension order
|
|
136
|
+
img_transposed, new_dim_order, is_3d = transpose_dimensions(img, dim_order)
|
|
137
|
+
|
|
138
|
+
# Check if we have a time series
|
|
139
|
+
has_time = "T" in new_dim_order
|
|
140
|
+
|
|
141
|
+
if has_time:
|
|
142
|
+
# Handle time series - process each time point
|
|
143
|
+
n_timepoints = img_transposed.shape[0]
|
|
144
|
+
result = np.zeros(img_transposed.shape, dtype=np.uint32)
|
|
145
|
+
|
|
146
|
+
# Process each time point
|
|
147
|
+
for t in range(n_timepoints):
|
|
148
|
+
img_t = img_transposed[t]
|
|
149
|
+
mask, _, _, _ = model.eval(
|
|
150
|
+
img_t,
|
|
151
|
+
diameter=diameter,
|
|
152
|
+
flow_threshold=flow_threshold,
|
|
153
|
+
channels=channels,
|
|
154
|
+
z_axis=0 if is_3d else None,
|
|
155
|
+
do_3D=is_3d,
|
|
156
|
+
niter=2000, # Maximum iterations
|
|
157
|
+
)
|
|
158
|
+
result[t] = mask
|
|
159
|
+
else:
|
|
160
|
+
# Process single time point
|
|
161
|
+
result, _, _, _ = model.eval(
|
|
162
|
+
img_transposed,
|
|
163
|
+
diameter=diameter,
|
|
164
|
+
flow_threshold=flow_threshold,
|
|
165
|
+
channels=channels,
|
|
166
|
+
z_axis=0 if is_3d else None,
|
|
167
|
+
do_3D=is_3d,
|
|
168
|
+
niter=2000, # Maximum iterations
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
return result.astype(np.uint32)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@BatchProcessingRegistry.register(
|
|
175
|
+
name="Segment cells or nuclei (Cellpose)",
|
|
176
|
+
suffix="_labels",
|
|
177
|
+
description="Automatic instance segmentation using Cellpose 3.0 (dedicated environment)",
|
|
178
|
+
parameters={
|
|
179
|
+
"model_type": {
|
|
180
|
+
"type": str,
|
|
181
|
+
"default": "cyto3",
|
|
182
|
+
"description": "Cellpose model type: 'cyto'/'cyto2'/'cyto3' for cells, 'nuclei' for nuclei",
|
|
183
|
+
},
|
|
184
|
+
"diameter": {
|
|
185
|
+
"type": float,
|
|
186
|
+
"default": 40.0,
|
|
187
|
+
"min": 5.0,
|
|
188
|
+
"max": 1000.0,
|
|
189
|
+
"description": "Approximate diameter of objects to segment (pixels)",
|
|
190
|
+
},
|
|
191
|
+
"dim_order": {
|
|
192
|
+
"type": str,
|
|
193
|
+
"default": "YX",
|
|
194
|
+
"description": "Dimension order of the input (e.g., 'YX', 'ZYX', 'TZYX')",
|
|
195
|
+
},
|
|
196
|
+
"channel_1": {
|
|
197
|
+
"type": int,
|
|
198
|
+
"default": 0,
|
|
199
|
+
"min": 0,
|
|
200
|
+
"max": 3,
|
|
201
|
+
"description": "First channel: 0=grayscale, 1=green, 2=red, 3=blue",
|
|
202
|
+
},
|
|
203
|
+
"channel_2": {
|
|
204
|
+
"type": int,
|
|
205
|
+
"default": 0,
|
|
206
|
+
"min": 0,
|
|
207
|
+
"max": 3,
|
|
208
|
+
"description": "Second channel: 0=none, 1=green, 2=red, 3=blue",
|
|
209
|
+
},
|
|
210
|
+
"flow_threshold": {
|
|
211
|
+
"type": float,
|
|
212
|
+
"default": 0.4,
|
|
213
|
+
"min": 0.1,
|
|
214
|
+
"max": 0.9,
|
|
215
|
+
"description": "Flow threshold for Cellpose segmentation",
|
|
216
|
+
},
|
|
217
|
+
"force_dedicated_env": {
|
|
218
|
+
"type": bool,
|
|
219
|
+
"default": False,
|
|
220
|
+
"description": "Force using dedicated environment even if Cellpose is available",
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
)
|
|
224
|
+
def cellpose_segmentation(
|
|
225
|
+
image: np.ndarray,
|
|
226
|
+
model_type: str = "cyto3",
|
|
227
|
+
diameter: float = 40.0,
|
|
228
|
+
dim_order: str = "YX",
|
|
229
|
+
channel_1: int = 0,
|
|
230
|
+
channel_2: int = 0,
|
|
231
|
+
flow_threshold: float = 0.4,
|
|
232
|
+
force_dedicated_env: bool = False,
|
|
233
|
+
) -> np.ndarray:
|
|
234
|
+
"""
|
|
235
|
+
Run Cellpose segmentation on an image.
|
|
236
|
+
|
|
237
|
+
This function takes an image and performs automatic instance segmentation using
|
|
238
|
+
Cellpose. It supports both 2D and 3D images, various dimension orders, and handles
|
|
239
|
+
time series data.
|
|
240
|
+
|
|
241
|
+
If Cellpose is not available in the current environment, a dedicated virtual
|
|
242
|
+
environment will be created to run Cellpose.
|
|
243
|
+
|
|
244
|
+
Parameters:
|
|
245
|
+
-----------
|
|
246
|
+
image : numpy.ndarray
|
|
247
|
+
Input image
|
|
248
|
+
model_type : str
|
|
249
|
+
Cellpose model type: 'cyto'/'cyto2'/'cyto3' for cells, 'nuclei' for nuclei (default: "cyto3")
|
|
250
|
+
diameter : float
|
|
251
|
+
Approximate diameter of objects to segment in pixels (default: 40.0)
|
|
252
|
+
dim_order : str
|
|
253
|
+
Dimension order of the input (e.g., 'YX', 'ZYX', 'TZYX') (default: "YX")
|
|
254
|
+
channel_1 : int
|
|
255
|
+
First channel: 0=grayscale, 1=green, 2=red, 3=blue (default: 0)
|
|
256
|
+
channel_2 : int
|
|
257
|
+
Second channel: 0=none, 1=green, 2=red, 3=blue (default: 0)
|
|
258
|
+
flow_threshold : float
|
|
259
|
+
Flow threshold for Cellpose segmentation (default: 0.4)
|
|
260
|
+
force_dedicated_env : bool
|
|
261
|
+
Force using dedicated environment even if Cellpose is available (default: False)
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
--------
|
|
265
|
+
numpy.ndarray
|
|
266
|
+
Segmented image with instance labels
|
|
267
|
+
"""
|
|
268
|
+
# Convert channel parameters to Cellpose channels list
|
|
269
|
+
channels = [channel_1, channel_2]
|
|
270
|
+
|
|
271
|
+
# Validate parameters
|
|
272
|
+
valid_models = ["cyto", "cyto2", "cyto3", "nuclei"]
|
|
273
|
+
if model_type not in valid_models:
|
|
274
|
+
raise ValueError(
|
|
275
|
+
f"Invalid model_type: {model_type}. "
|
|
276
|
+
f"Must be one of: {', '.join(valid_models)}"
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# Determine whether to use dedicated environment
|
|
280
|
+
use_env = force_dedicated_env or USE_DEDICATED_ENV
|
|
281
|
+
|
|
282
|
+
if use_env:
|
|
283
|
+
print("Using dedicated Cellpose environment...")
|
|
284
|
+
|
|
285
|
+
# First check if the environment exists, create if not
|
|
286
|
+
if not is_env_created():
|
|
287
|
+
print(
|
|
288
|
+
"Creating dedicated Cellpose environment (this may take a few minutes)..."
|
|
289
|
+
)
|
|
290
|
+
create_cellpose_env()
|
|
291
|
+
print("Environment created successfully.")
|
|
292
|
+
|
|
293
|
+
# Prepare arguments for the Cellpose function
|
|
294
|
+
args = {
|
|
295
|
+
"image": image,
|
|
296
|
+
"model_type": model_type,
|
|
297
|
+
"diameter": diameter,
|
|
298
|
+
"channels": channels,
|
|
299
|
+
"flow_threshold": flow_threshold,
|
|
300
|
+
"use_gpu": USE_GPU,
|
|
301
|
+
"do_3D": "Z" in dim_order,
|
|
302
|
+
"z_axis": 0 if "Z" in dim_order else None,
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
# Run Cellpose in the dedicated environment
|
|
306
|
+
print(f"Running Cellpose ({model_type}) in dedicated environment...")
|
|
307
|
+
result = run_cellpose_in_env("eval", args)
|
|
308
|
+
print(f"Segmentation complete. Found {np.max(result)} objects.")
|
|
309
|
+
return result
|
|
310
|
+
|
|
311
|
+
else:
|
|
312
|
+
print(f"Running Cellpose ({model_type}) in current environment...")
|
|
313
|
+
# Initialize Cellpose model in current environment
|
|
314
|
+
model = models.Cellpose(gpu=USE_GPU, model_type=model_type)
|
|
315
|
+
|
|
316
|
+
# Print status information
|
|
317
|
+
gpu_status = "GPU" if USE_GPU else "CPU"
|
|
318
|
+
print(f"Using Cellpose {model_type} model on {gpu_status}")
|
|
319
|
+
print(
|
|
320
|
+
f"Processing image with shape {image.shape}, dimension order: {dim_order}"
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# Run segmentation
|
|
324
|
+
try:
|
|
325
|
+
result = run_cellpose(
|
|
326
|
+
image, model, channels, diameter, flow_threshold, dim_order
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
print(f"Segmentation complete. Found {np.max(result)} objects.")
|
|
330
|
+
return result
|
|
331
|
+
|
|
332
|
+
except (Exception, MemoryError) as e:
|
|
333
|
+
print(f"Error during segmentation in current environment: {str(e)}")
|
|
334
|
+
|
|
335
|
+
# If we haven't already tried using the dedicated environment, try that as a fallback
|
|
336
|
+
if not USE_DEDICATED_ENV and not force_dedicated_env:
|
|
337
|
+
print("Attempting fallback to dedicated Cellpose environment...")
|
|
338
|
+
try:
|
|
339
|
+
args = {
|
|
340
|
+
"image": image,
|
|
341
|
+
"model_type": model_type,
|
|
342
|
+
"diameter": diameter,
|
|
343
|
+
"channels": channels,
|
|
344
|
+
"flow_threshold": flow_threshold,
|
|
345
|
+
"use_gpu": USE_GPU,
|
|
346
|
+
"do_3D": "Z" in dim_order,
|
|
347
|
+
"z_axis": 0 if "Z" in dim_order else None,
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if not is_env_created():
|
|
351
|
+
create_cellpose_env()
|
|
352
|
+
|
|
353
|
+
result = run_cellpose_in_env("eval", args)
|
|
354
|
+
print(f"Fallback successful. Found {np.max(result)} objects.")
|
|
355
|
+
return result
|
|
356
|
+
except (Exception, MemoryError) as fallback_e:
|
|
357
|
+
print(f"Fallback also failed: {str(fallback_e)}")
|
|
358
|
+
raise Exception(
|
|
359
|
+
f"Both direct and dedicated environment approaches failed: {str(e)} | {str(fallback_e)}"
|
|
360
|
+
) from fallback_e
|
|
361
|
+
else:
|
|
362
|
+
raise
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
# Add a command-line function to run cellpose segmentation
|
|
366
|
+
def run_cellpose_segmentation():
|
|
367
|
+
"""Run Cellpose segmentation from the command line."""
|
|
368
|
+
import argparse
|
|
369
|
+
|
|
370
|
+
from skimage.io import imread
|
|
371
|
+
from tifffile import imwrite
|
|
372
|
+
from tqdm import tqdm
|
|
373
|
+
|
|
374
|
+
# Parse arguments
|
|
375
|
+
parser = argparse.ArgumentParser(
|
|
376
|
+
description="Runs automatic mask generation on images using Cellpose."
|
|
377
|
+
)
|
|
378
|
+
parser.add_argument(
|
|
379
|
+
"--input", type=str, required=True, help="Path to input images."
|
|
380
|
+
)
|
|
381
|
+
parser.add_argument(
|
|
382
|
+
"--diameter", type=float, default=40.0, help="Diameter of objects."
|
|
383
|
+
)
|
|
384
|
+
parser.add_argument(
|
|
385
|
+
"--channels",
|
|
386
|
+
type=int,
|
|
387
|
+
nargs="+",
|
|
388
|
+
default=[0, 0],
|
|
389
|
+
help="Channels to use.",
|
|
390
|
+
)
|
|
391
|
+
parser.add_argument(
|
|
392
|
+
"--dim_order",
|
|
393
|
+
type=str,
|
|
394
|
+
default="ZYX",
|
|
395
|
+
help="Dimension order of the input images.",
|
|
396
|
+
)
|
|
397
|
+
parser.add_argument(
|
|
398
|
+
"--model_type",
|
|
399
|
+
type=str,
|
|
400
|
+
default="cyto3",
|
|
401
|
+
choices=["cyto", "cyto2", "cyto3", "nuclei"],
|
|
402
|
+
help="Model type: 'cyto'/'cyto2'/'cyto3' for cells, 'nuclei' for nuclei",
|
|
403
|
+
)
|
|
404
|
+
parser.add_argument(
|
|
405
|
+
"--flow_threshold",
|
|
406
|
+
type=float,
|
|
407
|
+
default=0.4,
|
|
408
|
+
help="Flow threshold for Cellpose (default: 0.4)",
|
|
409
|
+
)
|
|
410
|
+
parser.add_argument(
|
|
411
|
+
"--max_pixels",
|
|
412
|
+
type=int,
|
|
413
|
+
default=4000000,
|
|
414
|
+
help="Maximum number of pixels to process for 2D images (default: 4000000)",
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
args = parser.parse_args()
|
|
418
|
+
|
|
419
|
+
# Validate input folder
|
|
420
|
+
input_folder = args.input
|
|
421
|
+
if not os.path.isdir(input_folder):
|
|
422
|
+
print(
|
|
423
|
+
f"Error: The input folder '{input_folder}' does not exist or is not accessible."
|
|
424
|
+
)
|
|
425
|
+
return 1
|
|
426
|
+
|
|
427
|
+
# Find input files
|
|
428
|
+
input_files = [
|
|
429
|
+
f
|
|
430
|
+
for f in os.listdir(input_folder)
|
|
431
|
+
if f.endswith(".tif") and not f.endswith("_labels.tif")
|
|
432
|
+
]
|
|
433
|
+
|
|
434
|
+
if not input_files:
|
|
435
|
+
print(f"No .tif files found in {input_folder}")
|
|
436
|
+
return 1
|
|
437
|
+
|
|
438
|
+
print(f"Found {len(input_files)} files to process")
|
|
439
|
+
|
|
440
|
+
# Check if Cellpose is available
|
|
441
|
+
if not CELLPOSE_AVAILABLE:
|
|
442
|
+
print(
|
|
443
|
+
"Error: Cellpose is not installed. Please install it with: pip install cellpose"
|
|
444
|
+
)
|
|
445
|
+
return 1
|
|
446
|
+
|
|
447
|
+
# Initialize model
|
|
448
|
+
model = models.Cellpose(gpu=USE_GPU, model_type=args.model_type)
|
|
449
|
+
|
|
450
|
+
# Print status
|
|
451
|
+
gpu_status = "GPU" if USE_GPU else "CPU"
|
|
452
|
+
print(f"Using Cellpose {args.model_type} model on {gpu_status}")
|
|
453
|
+
|
|
454
|
+
# Process each file
|
|
455
|
+
for input_file in tqdm(input_files, desc="Processing images"):
|
|
456
|
+
try:
|
|
457
|
+
# Check image size
|
|
458
|
+
img = imread(os.path.join(input_folder, input_file))
|
|
459
|
+
|
|
460
|
+
if len(img.shape) == 2 or (
|
|
461
|
+
len(img.shape) == 3 and "C" in args.dim_order
|
|
462
|
+
):
|
|
463
|
+
# For 2D images (potentially with channels)
|
|
464
|
+
height, width = img.shape[:2]
|
|
465
|
+
total_pixels = height * width
|
|
466
|
+
if total_pixels > args.max_pixels:
|
|
467
|
+
print(
|
|
468
|
+
f"Skipping {input_file} as it exceeds the maximum size of {args.max_pixels} pixels."
|
|
469
|
+
)
|
|
470
|
+
continue
|
|
471
|
+
|
|
472
|
+
print(
|
|
473
|
+
"\nCheck if image shape corresponds to the dim order that you have given:"
|
|
474
|
+
)
|
|
475
|
+
print(
|
|
476
|
+
f"Image shape: {img.shape}, dimension order: {args.dim_order}\n"
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
# Run segmentation
|
|
480
|
+
result = run_cellpose(
|
|
481
|
+
img,
|
|
482
|
+
model,
|
|
483
|
+
args.channels,
|
|
484
|
+
args.diameter,
|
|
485
|
+
args.flow_threshold,
|
|
486
|
+
args.dim_order,
|
|
487
|
+
args.max_pixels,
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
# Save result
|
|
491
|
+
output_file = os.path.join(
|
|
492
|
+
input_folder, input_file.replace(".tif", "_labels.tif")
|
|
493
|
+
)
|
|
494
|
+
imwrite(output_file, result, compression="zlib")
|
|
495
|
+
|
|
496
|
+
print(
|
|
497
|
+
f"Saved segmentation with {np.max(result)} objects to {output_file}"
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
except (Exception, MemoryError) as e:
|
|
501
|
+
print(f"Error processing {input_file}: {str(e)}")
|
|
502
|
+
|
|
503
|
+
print("\nProcessing complete.")
|
|
504
|
+
return 0
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
# Run the command-line function if this script is run directly
|
|
508
|
+
if __name__ == "__main__":
|
|
509
|
+
import sys
|
|
510
|
+
|
|
511
|
+
sys.exit(run_cellpose_segmentation())
|