napari-tmidas 0.2.0__py3-none-any.whl → 0.2.2__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 +1942 -607
- napari_tmidas/_file_selector.py +99 -16
- napari_tmidas/_registry.py +15 -14
- napari_tmidas/_tests/test_file_selector.py +90 -0
- napari_tmidas/_tests/test_registry.py +67 -0
- napari_tmidas/_version.py +2 -2
- napari_tmidas/processing_functions/basic.py +494 -23
- napari_tmidas/processing_functions/careamics_denoising.py +324 -0
- napari_tmidas/processing_functions/careamics_env_manager.py +339 -0
- napari_tmidas/processing_functions/cellpose_env_manager.py +55 -20
- napari_tmidas/processing_functions/cellpose_segmentation.py +105 -218
- napari_tmidas/processing_functions/sam2_mp4.py +283 -0
- napari_tmidas/processing_functions/skimage_filters.py +31 -1
- napari_tmidas/processing_functions/timepoint_merger.py +490 -0
- napari_tmidas/processing_functions/trackastra_tracking.py +322 -0
- {napari_tmidas-0.2.0.dist-info → napari_tmidas-0.2.2.dist-info}/METADATA +37 -17
- {napari_tmidas-0.2.0.dist-info → napari_tmidas-0.2.2.dist-info}/RECORD +21 -14
- {napari_tmidas-0.2.0.dist-info → napari_tmidas-0.2.2.dist-info}/WHEEL +1 -1
- {napari_tmidas-0.2.0.dist-info → napari_tmidas-0.2.2.dist-info}/entry_points.txt +0 -0
- {napari_tmidas-0.2.0.dist-info → napari_tmidas-0.2.2.dist-info}/licenses/LICENSE +0 -0
- {napari_tmidas-0.2.0.dist-info → napari_tmidas-0.2.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# processing_functions/careamics_denoising.py
|
|
2
|
+
"""
|
|
3
|
+
Processing functions for denoising images using CAREamics.
|
|
4
|
+
|
|
5
|
+
This module provides functionality to denoise images using various models from CAREamics,
|
|
6
|
+
including Noise2Void (N2V) and CARE models. The functions support both 2D and 3D data.
|
|
7
|
+
|
|
8
|
+
The functions will automatically create and manage a dedicated environment for CAREamics
|
|
9
|
+
if it's not already installed in the main environment.
|
|
10
|
+
"""
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from napari_tmidas._registry import BatchProcessingRegistry
|
|
16
|
+
|
|
17
|
+
# Import the environment manager for CAREamics
|
|
18
|
+
from napari_tmidas.processing_functions.careamics_env_manager import (
|
|
19
|
+
create_careamics_env,
|
|
20
|
+
is_env_created,
|
|
21
|
+
run_careamics_in_env,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Check if CAREamics is directly available in current environment
|
|
25
|
+
try:
|
|
26
|
+
from careamics import CAREamist
|
|
27
|
+
|
|
28
|
+
CAREAMICS_AVAILABLE = True
|
|
29
|
+
USE_DEDICATED_ENV = False
|
|
30
|
+
print("CAREamics found in current environment, using direct import")
|
|
31
|
+
except ImportError:
|
|
32
|
+
CAREAMICS_AVAILABLE = False
|
|
33
|
+
USE_DEDICATED_ENV = True
|
|
34
|
+
print(
|
|
35
|
+
"CAREamics not found in current environment, will use dedicated environment"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@BatchProcessingRegistry.register(
|
|
40
|
+
name="CAREamics Denoise (N2V/CARE)",
|
|
41
|
+
suffix="_denoised",
|
|
42
|
+
description="Denoise images using CAREamics (Noise2Void or CARE model)",
|
|
43
|
+
parameters={
|
|
44
|
+
"checkpoint_path": {
|
|
45
|
+
"type": str,
|
|
46
|
+
"default": "",
|
|
47
|
+
"description": "Path to the CAREamics model checkpoint file (.ckpt)",
|
|
48
|
+
},
|
|
49
|
+
"tile_size_x": {
|
|
50
|
+
"type": int,
|
|
51
|
+
"default": 32,
|
|
52
|
+
"min": 16,
|
|
53
|
+
"max": 512,
|
|
54
|
+
"description": "Tile size in X dimension",
|
|
55
|
+
},
|
|
56
|
+
"tile_size_y": {
|
|
57
|
+
"type": int,
|
|
58
|
+
"default": 32,
|
|
59
|
+
"min": 16,
|
|
60
|
+
"max": 512,
|
|
61
|
+
"description": "Tile size in Y dimension",
|
|
62
|
+
},
|
|
63
|
+
"tile_size_z": {
|
|
64
|
+
"type": int,
|
|
65
|
+
"default": 0,
|
|
66
|
+
"min": 0,
|
|
67
|
+
"max": 256,
|
|
68
|
+
"description": "Tile size in Z dimension (for 3D data)",
|
|
69
|
+
},
|
|
70
|
+
"batch_size": {
|
|
71
|
+
"type": int,
|
|
72
|
+
"default": 1,
|
|
73
|
+
"min": 1,
|
|
74
|
+
"max": 16,
|
|
75
|
+
"description": "Batch size for prediction",
|
|
76
|
+
},
|
|
77
|
+
"use_tta": {
|
|
78
|
+
"type": bool,
|
|
79
|
+
"default": True,
|
|
80
|
+
"description": "Use test-time augmentation for better results",
|
|
81
|
+
},
|
|
82
|
+
"force_dedicated_env": {
|
|
83
|
+
"type": bool,
|
|
84
|
+
"default": False,
|
|
85
|
+
"description": "Force using dedicated environment even if CAREamics is available",
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
)
|
|
89
|
+
def careamics_denoise(
|
|
90
|
+
image: np.ndarray,
|
|
91
|
+
checkpoint_path: str = "",
|
|
92
|
+
tile_size_z: int = 64,
|
|
93
|
+
tile_size_y: int = 64,
|
|
94
|
+
tile_size_x: int = 64,
|
|
95
|
+
tile_overlap_z: int = 8,
|
|
96
|
+
tile_overlap_y: int = 8,
|
|
97
|
+
tile_overlap_x: int = 8,
|
|
98
|
+
batch_size: int = 1,
|
|
99
|
+
use_tta: bool = True,
|
|
100
|
+
force_dedicated_env: bool = False,
|
|
101
|
+
) -> np.ndarray:
|
|
102
|
+
"""
|
|
103
|
+
Denoise images using CAREamics models.
|
|
104
|
+
|
|
105
|
+
This function loads a CAREamics model from a checkpoint file and uses it to denoise
|
|
106
|
+
the input image. The function supports both 2D and 3D data and handles tiling for
|
|
107
|
+
processing large images efficiently.
|
|
108
|
+
|
|
109
|
+
If CAREamics is not installed in the main environment, a dedicated virtual environment
|
|
110
|
+
will be automatically created and managed.
|
|
111
|
+
|
|
112
|
+
Parameters:
|
|
113
|
+
-----------
|
|
114
|
+
image : numpy.ndarray
|
|
115
|
+
Input image to denoise
|
|
116
|
+
checkpoint_path : str
|
|
117
|
+
Path to the CAREamics model checkpoint file (.ckpt)
|
|
118
|
+
tile_size_z : int
|
|
119
|
+
Tile size in Z dimension (for 3D data)
|
|
120
|
+
tile_size_y : int
|
|
121
|
+
Tile size in Y dimension
|
|
122
|
+
tile_size_x : int
|
|
123
|
+
Tile size in X dimension
|
|
124
|
+
tile_overlap_z : int
|
|
125
|
+
Tile overlap in Z dimension (for 3D data)
|
|
126
|
+
tile_overlap_y : int
|
|
127
|
+
Tile overlap in Y dimension
|
|
128
|
+
tile_overlap_x : int
|
|
129
|
+
Tile overlap in X dimension
|
|
130
|
+
batch_size : int
|
|
131
|
+
Batch size for prediction
|
|
132
|
+
use_tta : bool
|
|
133
|
+
Use test-time augmentation for better results
|
|
134
|
+
force_dedicated_env : bool
|
|
135
|
+
Force using dedicated environment even if CAREamics is available
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
--------
|
|
139
|
+
numpy.ndarray
|
|
140
|
+
Denoised image with the same dimensions as the input
|
|
141
|
+
"""
|
|
142
|
+
# Verify checkpoint path
|
|
143
|
+
if not checkpoint_path or not os.path.exists(checkpoint_path):
|
|
144
|
+
print(f"Checkpoint file not found: {checkpoint_path}")
|
|
145
|
+
print("Please provide a valid checkpoint file path.")
|
|
146
|
+
return image
|
|
147
|
+
|
|
148
|
+
# Determine whether to use dedicated environment
|
|
149
|
+
use_env = force_dedicated_env or USE_DEDICATED_ENV
|
|
150
|
+
|
|
151
|
+
careamics_denoise.thread_safe = False
|
|
152
|
+
|
|
153
|
+
if use_env:
|
|
154
|
+
print("Using dedicated CAREamics environment...")
|
|
155
|
+
|
|
156
|
+
# First check if the environment exists, create if not
|
|
157
|
+
if not is_env_created():
|
|
158
|
+
print(
|
|
159
|
+
"Creating dedicated CAREamics environment (this may take a few minutes)..."
|
|
160
|
+
)
|
|
161
|
+
create_careamics_env()
|
|
162
|
+
print("Environment created successfully.")
|
|
163
|
+
|
|
164
|
+
# Prepare arguments for the CAREamics function
|
|
165
|
+
args = {
|
|
166
|
+
"image": image,
|
|
167
|
+
"checkpoint_path": checkpoint_path,
|
|
168
|
+
"tile_size_z": tile_size_z,
|
|
169
|
+
"tile_size_y": tile_size_y,
|
|
170
|
+
"tile_size_x": tile_size_x,
|
|
171
|
+
"tile_overlap_z": tile_overlap_z,
|
|
172
|
+
"tile_overlap_y": tile_overlap_y,
|
|
173
|
+
"tile_overlap_x": tile_overlap_x,
|
|
174
|
+
"batch_size": batch_size,
|
|
175
|
+
"use_tta": use_tta,
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# Calculate tile overlap automatically (e.g., 25% of tile size)
|
|
179
|
+
def compute_overlap(tile_size, fraction=0.25):
|
|
180
|
+
# Ensure overlap is at least 1 and less than tile size
|
|
181
|
+
overlap = max(1, int(tile_size * fraction))
|
|
182
|
+
return min(overlap, tile_size - 1)
|
|
183
|
+
|
|
184
|
+
# Inside careamics_denoise, after parsing tile sizes:
|
|
185
|
+
tile_overlap_z = (
|
|
186
|
+
compute_overlap(tile_size_z) if len(image.shape) >= 3 else None
|
|
187
|
+
)
|
|
188
|
+
tile_overlap_y = compute_overlap(tile_size_y)
|
|
189
|
+
tile_overlap_x = compute_overlap(tile_size_x)
|
|
190
|
+
|
|
191
|
+
# Run CAREamics in the dedicated environment
|
|
192
|
+
print("Running CAREamics in dedicated environment...")
|
|
193
|
+
return run_careamics_in_env("predict", args)
|
|
194
|
+
|
|
195
|
+
else:
|
|
196
|
+
print("Running CAREamics in current environment...")
|
|
197
|
+
# Use CAREamics directly in the current environment
|
|
198
|
+
try:
|
|
199
|
+
print(f"Loading CAREamics model from: {checkpoint_path}")
|
|
200
|
+
# Initialize the CAREamist model
|
|
201
|
+
careamist = CAREamist(
|
|
202
|
+
checkpoint_path, os.path.dirname(checkpoint_path)
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# Determine dimensionality
|
|
206
|
+
is_3d = len(image.shape) >= 3
|
|
207
|
+
|
|
208
|
+
if is_3d:
|
|
209
|
+
print(f"Processing 3D data with shape: {image.shape}")
|
|
210
|
+
# Determine axes based on dimensionality
|
|
211
|
+
if len(image.shape) == 3:
|
|
212
|
+
# ZYX format
|
|
213
|
+
axes = "ZYX"
|
|
214
|
+
tile_size = (tile_size_z, tile_size_y, tile_size_x)
|
|
215
|
+
tile_overlap = (
|
|
216
|
+
tile_overlap_z,
|
|
217
|
+
tile_overlap_y,
|
|
218
|
+
tile_overlap_x,
|
|
219
|
+
)
|
|
220
|
+
print(f"Using axes configuration: {axes}")
|
|
221
|
+
elif len(image.shape) == 4:
|
|
222
|
+
# Assuming TZYX format
|
|
223
|
+
axes = "TZYX"
|
|
224
|
+
tile_size = (tile_size_z, tile_size_y, tile_size_x)
|
|
225
|
+
tile_overlap = (
|
|
226
|
+
tile_overlap_z,
|
|
227
|
+
tile_overlap_y,
|
|
228
|
+
tile_overlap_x,
|
|
229
|
+
)
|
|
230
|
+
print(f"Using axes configuration: {axes}")
|
|
231
|
+
else:
|
|
232
|
+
# Unknown format, try to handle it
|
|
233
|
+
print(
|
|
234
|
+
f"Warning: Unusual data shape: {image.shape}. Defaulting to 'TZYX'"
|
|
235
|
+
)
|
|
236
|
+
axes = "TZYX"
|
|
237
|
+
tile_size = (tile_size_z, tile_size_y, tile_size_x)
|
|
238
|
+
tile_overlap = (
|
|
239
|
+
tile_overlap_z,
|
|
240
|
+
tile_overlap_y,
|
|
241
|
+
tile_overlap_x,
|
|
242
|
+
)
|
|
243
|
+
else:
|
|
244
|
+
print(f"Processing 2D data with shape: {image.shape}")
|
|
245
|
+
# 2D data
|
|
246
|
+
if len(image.shape) == 2:
|
|
247
|
+
# YX format
|
|
248
|
+
axes = "YX"
|
|
249
|
+
tile_size = (tile_size_y, tile_size_x)
|
|
250
|
+
tile_overlap = (tile_overlap_y, tile_overlap_x)
|
|
251
|
+
print(f"Using axes configuration: {axes}")
|
|
252
|
+
else:
|
|
253
|
+
# Unknown format, try to handle it
|
|
254
|
+
print(
|
|
255
|
+
f"Warning: Unusual data shape: {image.shape}. Defaulting to 'YX'"
|
|
256
|
+
)
|
|
257
|
+
axes = "YX"
|
|
258
|
+
tile_size = (tile_size_y, tile_size_x)
|
|
259
|
+
tile_overlap = (tile_overlap_y, tile_overlap_x)
|
|
260
|
+
|
|
261
|
+
# Run the prediction
|
|
262
|
+
print(
|
|
263
|
+
f"Running prediction with tile size: {tile_size}, overlap: {tile_overlap}"
|
|
264
|
+
)
|
|
265
|
+
print(f"Using batch size: {batch_size}, TTA: {use_tta}")
|
|
266
|
+
|
|
267
|
+
prediction = careamist.predict(
|
|
268
|
+
source=image,
|
|
269
|
+
tile_size=tile_size,
|
|
270
|
+
tile_overlap=tile_overlap,
|
|
271
|
+
axes=axes,
|
|
272
|
+
batch_size=batch_size,
|
|
273
|
+
tta=use_tta,
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
# # Handle output shape
|
|
277
|
+
# if prediction.shape != image.shape:
|
|
278
|
+
# print(f"Warning: Prediction shape {prediction.shape} differs from input shape {image.shape}")
|
|
279
|
+
# prediction = np.squeeze(prediction)
|
|
280
|
+
|
|
281
|
+
# # If shapes still don't match, try to reshape
|
|
282
|
+
# if prediction.shape != image.shape:
|
|
283
|
+
# print(f"Warning: Shapes still don't match after squeezing. Using original dimensions.")
|
|
284
|
+
# try:
|
|
285
|
+
# prediction = prediction.reshape(image.shape)
|
|
286
|
+
# except ValueError:
|
|
287
|
+
# print("Error: Could not reshape prediction to match input shape.")
|
|
288
|
+
# return image
|
|
289
|
+
|
|
290
|
+
# print(f"Denoising completed. Output shape: {prediction.shape}")
|
|
291
|
+
return prediction
|
|
292
|
+
|
|
293
|
+
except (RuntimeError, ValueError, ImportError) as e:
|
|
294
|
+
import traceback
|
|
295
|
+
|
|
296
|
+
print(
|
|
297
|
+
f"Error during CAREamics denoising in current environment: {str(e)}"
|
|
298
|
+
)
|
|
299
|
+
traceback.print_exc()
|
|
300
|
+
|
|
301
|
+
# If we haven't already tried using the dedicated environment, try that as a fallback
|
|
302
|
+
if not force_dedicated_env:
|
|
303
|
+
print(
|
|
304
|
+
"Attempting fallback to dedicated CAREamics environment..."
|
|
305
|
+
)
|
|
306
|
+
args = {
|
|
307
|
+
"image": image,
|
|
308
|
+
"checkpoint_path": checkpoint_path,
|
|
309
|
+
"tile_size_z": tile_size_z,
|
|
310
|
+
"tile_size_y": tile_size_y,
|
|
311
|
+
"tile_size_x": tile_size_x,
|
|
312
|
+
"tile_overlap_z": tile_overlap_z,
|
|
313
|
+
"tile_overlap_y": tile_overlap_y,
|
|
314
|
+
"tile_overlap_x": tile_overlap_x,
|
|
315
|
+
"batch_size": batch_size,
|
|
316
|
+
"use_tta": use_tta,
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if not is_env_created():
|
|
320
|
+
create_careamics_env()
|
|
321
|
+
|
|
322
|
+
return run_careamics_in_env("predict", args)
|
|
323
|
+
|
|
324
|
+
return None
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# processing_functions/careamics_env_manager.py
|
|
2
|
+
"""
|
|
3
|
+
This module manages a dedicated virtual environment for CAREamics.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import contextlib
|
|
7
|
+
import os
|
|
8
|
+
import platform
|
|
9
|
+
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
import tempfile
|
|
12
|
+
import venv
|
|
13
|
+
|
|
14
|
+
import tifffile
|
|
15
|
+
|
|
16
|
+
# Define the environment directory in user's home folder
|
|
17
|
+
ENV_DIR = os.path.join(
|
|
18
|
+
os.path.expanduser("~"), ".napari-tmidas", "envs", "careamics"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def is_careamics_installed():
|
|
23
|
+
"""Check if CAREamics is installed in the current environment."""
|
|
24
|
+
try:
|
|
25
|
+
import importlib.util
|
|
26
|
+
|
|
27
|
+
return importlib.util.find_spec("careamics") is not None
|
|
28
|
+
except ImportError:
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def is_env_created():
|
|
33
|
+
"""Check if the dedicated environment exists."""
|
|
34
|
+
env_python = get_env_python_path()
|
|
35
|
+
return os.path.exists(env_python)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_env_python_path():
|
|
39
|
+
"""Get the path to the Python executable in the environment."""
|
|
40
|
+
if platform.system() == "Windows":
|
|
41
|
+
return os.path.join(ENV_DIR, "Scripts", "python.exe")
|
|
42
|
+
else:
|
|
43
|
+
return os.path.join(ENV_DIR, "bin", "python")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def create_careamics_env():
|
|
47
|
+
"""Create a dedicated virtual environment for CAREamics."""
|
|
48
|
+
# Ensure the environment directory exists
|
|
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 CAREamics environment at {ENV_DIR}...")
|
|
56
|
+
|
|
57
|
+
# Create a new virtual environment
|
|
58
|
+
venv.create(ENV_DIR, with_pip=True)
|
|
59
|
+
|
|
60
|
+
# Path to the Python executable in the new environment
|
|
61
|
+
env_python = get_env_python_path()
|
|
62
|
+
|
|
63
|
+
# Upgrade pip first
|
|
64
|
+
print("Upgrading pip...")
|
|
65
|
+
subprocess.check_call(
|
|
66
|
+
[env_python, "-m", "pip", "install", "--upgrade", "pip"]
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Install PyTorch first for compatibility
|
|
70
|
+
# Try to detect if CUDA is available
|
|
71
|
+
cuda_available = False
|
|
72
|
+
try:
|
|
73
|
+
import torch
|
|
74
|
+
|
|
75
|
+
cuda_available = torch.cuda.is_available()
|
|
76
|
+
print(f"CUDA is {'available' if cuda_available else 'not available'}")
|
|
77
|
+
except ImportError:
|
|
78
|
+
print("PyTorch not detected in main environment")
|
|
79
|
+
|
|
80
|
+
if cuda_available:
|
|
81
|
+
# Install PyTorch with CUDA support
|
|
82
|
+
print("Installing PyTorch with CUDA support...")
|
|
83
|
+
subprocess.check_call(
|
|
84
|
+
[env_python, "-m", "pip", "install", "torch", "torchvision"]
|
|
85
|
+
)
|
|
86
|
+
else:
|
|
87
|
+
# Install PyTorch without CUDA
|
|
88
|
+
print("Installing PyTorch without CUDA support...")
|
|
89
|
+
subprocess.check_call(
|
|
90
|
+
[env_python, "-m", "pip", "install", "torch", "torchvision"]
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Install CAREamics and dependencies
|
|
94
|
+
print("Installing CAREamics in the dedicated environment...")
|
|
95
|
+
subprocess.check_call(
|
|
96
|
+
[env_python, "-m", "pip", "install", "careamics[tensorboard]"]
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Install tifffile for image handling
|
|
100
|
+
subprocess.check_call(
|
|
101
|
+
[env_python, "-m", "pip", "install", "tifffile", "numpy"]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Check if installation was successful
|
|
105
|
+
try:
|
|
106
|
+
# Run a simple script to verify CAREamics is installed and working
|
|
107
|
+
check_script = """
|
|
108
|
+
import sys
|
|
109
|
+
try:
|
|
110
|
+
import careamics
|
|
111
|
+
print(f"CAREamics version: {careamics.__version__}")
|
|
112
|
+
from careamics import CAREamist
|
|
113
|
+
print("CAREamist imported successfully")
|
|
114
|
+
import torch
|
|
115
|
+
print(f"PyTorch version: {torch.__version__}")
|
|
116
|
+
print(f"CUDA available: {torch.cuda.is_available()}")
|
|
117
|
+
if torch.cuda.is_available():
|
|
118
|
+
print(f"CUDA version: {torch.version.cuda}")
|
|
119
|
+
print(f"GPU: {torch.cuda.get_device_name(0)}")
|
|
120
|
+
print("SUCCESS: CAREamics environment is working correctly")
|
|
121
|
+
except Exception as e:
|
|
122
|
+
print(f"ERROR: {str(e)}")
|
|
123
|
+
sys.exit(1)
|
|
124
|
+
"""
|
|
125
|
+
with tempfile.NamedTemporaryFile(
|
|
126
|
+
mode="w", suffix=".py", delete=False
|
|
127
|
+
) as temp:
|
|
128
|
+
temp.write(check_script)
|
|
129
|
+
temp_path = temp.name
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
result = subprocess.run(
|
|
133
|
+
[env_python, temp_path],
|
|
134
|
+
check=True,
|
|
135
|
+
capture_output=True,
|
|
136
|
+
text=True,
|
|
137
|
+
)
|
|
138
|
+
print(result.stdout)
|
|
139
|
+
if "SUCCESS" in result.stdout:
|
|
140
|
+
print(
|
|
141
|
+
"CAREamics environment created and verified successfully."
|
|
142
|
+
)
|
|
143
|
+
else:
|
|
144
|
+
print(
|
|
145
|
+
"WARNING: CAREamics environment created but verification uncertain."
|
|
146
|
+
)
|
|
147
|
+
finally:
|
|
148
|
+
os.unlink(temp_path)
|
|
149
|
+
|
|
150
|
+
except subprocess.CalledProcessError as e:
|
|
151
|
+
print(f"Error during CAREamics installation: {e}")
|
|
152
|
+
print(f"Output: {e.output}")
|
|
153
|
+
print(f"Errors: {e.stderr}")
|
|
154
|
+
raise RuntimeError("Failed to create CAREamics environment") from e
|
|
155
|
+
|
|
156
|
+
return env_python
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def run_careamics_in_env(func_name, args_dict):
|
|
160
|
+
"""
|
|
161
|
+
Run CAREamics in a dedicated environment.
|
|
162
|
+
|
|
163
|
+
Parameters:
|
|
164
|
+
-----------
|
|
165
|
+
func_name : str
|
|
166
|
+
Name of the CAREamics function to run (e.g., 'predict')
|
|
167
|
+
args_dict : dict
|
|
168
|
+
Dictionary of arguments for CAREamics function
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
--------
|
|
172
|
+
numpy.ndarray
|
|
173
|
+
Denoised image
|
|
174
|
+
"""
|
|
175
|
+
# Ensure the environment exists
|
|
176
|
+
if not is_env_created():
|
|
177
|
+
create_careamics_env()
|
|
178
|
+
|
|
179
|
+
# Prepare temporary files
|
|
180
|
+
with tempfile.NamedTemporaryFile(
|
|
181
|
+
suffix=".tif", delete=False
|
|
182
|
+
) as input_file, tempfile.NamedTemporaryFile(
|
|
183
|
+
suffix=".tif", delete=False
|
|
184
|
+
) as output_file, tempfile.NamedTemporaryFile(
|
|
185
|
+
mode="w", suffix=".py", delete=False
|
|
186
|
+
) as script_file:
|
|
187
|
+
|
|
188
|
+
# Save input image to temp file
|
|
189
|
+
tifffile.imwrite(
|
|
190
|
+
input_file.name, args_dict["image"], compression="zlib"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Create a temporary script to run CAREamics
|
|
194
|
+
script = f"""
|
|
195
|
+
import sys
|
|
196
|
+
import os
|
|
197
|
+
import numpy as np
|
|
198
|
+
import tifffile
|
|
199
|
+
from pathlib import Path
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
from careamics import CAREamist
|
|
203
|
+
|
|
204
|
+
# Load input image
|
|
205
|
+
print(f"Loading image from {{os.path.basename('{input_file.name}')}}")
|
|
206
|
+
image = tifffile.imread('{input_file.name}')
|
|
207
|
+
print(f"Input image shape: {{image.shape}}, dtype: {{image.dtype}}")
|
|
208
|
+
|
|
209
|
+
# Load model
|
|
210
|
+
print(f"Loading model from {{os.path.basename('{args_dict['checkpoint_path']}')}}")
|
|
211
|
+
model = CAREamist('{args_dict['checkpoint_path']}',os.path.dirname('{args_dict['checkpoint_path']}'))
|
|
212
|
+
|
|
213
|
+
# Determine dimensionality
|
|
214
|
+
dims = len(image.shape)
|
|
215
|
+
is_3d = dims >= 3
|
|
216
|
+
|
|
217
|
+
if is_3d:
|
|
218
|
+
print(f"Processing 3D data with shape: {{image.shape}}")
|
|
219
|
+
# Determine axes based on dimensionality
|
|
220
|
+
if dims == 3:
|
|
221
|
+
# ZYX format
|
|
222
|
+
axes = "ZYX"
|
|
223
|
+
tile_size = ({args_dict.get('tile_size_z', 64)},
|
|
224
|
+
{args_dict.get('tile_size_y', 64)},
|
|
225
|
+
{args_dict.get('tile_size_x', 64)})
|
|
226
|
+
tile_overlap = ({args_dict.get('tile_overlap_z', 32)},
|
|
227
|
+
{args_dict.get('tile_overlap_y', 32)},
|
|
228
|
+
{args_dict.get('tile_overlap_x', 32)})
|
|
229
|
+
elif dims == 4:
|
|
230
|
+
# Assuming TZYX format
|
|
231
|
+
axes = "TZYX"
|
|
232
|
+
tile_size = ({args_dict.get('tile_size_z', 64)},
|
|
233
|
+
{args_dict.get('tile_size_y', 64)},
|
|
234
|
+
{args_dict.get('tile_size_x', 64)})
|
|
235
|
+
tile_overlap = ({args_dict.get('tile_overlap_z', 32)},
|
|
236
|
+
{args_dict.get('tile_overlap_y', 32)},
|
|
237
|
+
{args_dict.get('tile_overlap_x', 32)})
|
|
238
|
+
else:
|
|
239
|
+
print(f"Warning: Unusual data shape: {{image.shape}}. Defaulting to 'TZYX'")
|
|
240
|
+
axes = "TZYX"
|
|
241
|
+
tile_size = ({args_dict.get('tile_size_z', 64)},
|
|
242
|
+
{args_dict.get('tile_size_y', 64)},
|
|
243
|
+
{args_dict.get('tile_size_x', 64)})
|
|
244
|
+
tile_overlap = ({args_dict.get('tile_overlap_z', 32)},
|
|
245
|
+
{args_dict.get('tile_overlap_y', 32)},
|
|
246
|
+
{args_dict.get('tile_overlap_x', 32)})
|
|
247
|
+
else:
|
|
248
|
+
print(f"Processing 2D data with shape: {{image.shape}}")
|
|
249
|
+
# 2D data
|
|
250
|
+
if dims == 2:
|
|
251
|
+
# YX format
|
|
252
|
+
axes = "YX"
|
|
253
|
+
tile_size = ({args_dict.get('tile_size_y', 64)},
|
|
254
|
+
{args_dict.get('tile_size_x', 64)})
|
|
255
|
+
tile_overlap = ({args_dict.get('tile_overlap_y', 32)},
|
|
256
|
+
{args_dict.get('tile_overlap_x', 32)})
|
|
257
|
+
else:
|
|
258
|
+
print(f"Warning: Unusual data shape: {{image.shape}}. Defaulting to 'YX'")
|
|
259
|
+
axes = "YX"
|
|
260
|
+
tile_size = ({args_dict.get('tile_size_y', 64)},
|
|
261
|
+
{args_dict.get('tile_size_x', 64)})
|
|
262
|
+
tile_overlap = ({args_dict.get('tile_overlap_y', 32)},
|
|
263
|
+
{args_dict.get('tile_overlap_x', 32)})
|
|
264
|
+
|
|
265
|
+
print(f"Using axes: {{axes}}, tile_size: {{tile_size}}, tile_overlap: {{tile_overlap}}")
|
|
266
|
+
|
|
267
|
+
# Run the prediction
|
|
268
|
+
print("Starting CAREamics prediction...")
|
|
269
|
+
prediction = model.predict(
|
|
270
|
+
source=image,
|
|
271
|
+
tile_size=tile_size,
|
|
272
|
+
tile_overlap=tile_overlap,
|
|
273
|
+
axes=axes,
|
|
274
|
+
batch_size={args_dict.get('batch_size', 1)},
|
|
275
|
+
tta={args_dict.get('use_tta', True)},
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
# # Handle output shape
|
|
279
|
+
# if prediction.shape != image.shape:
|
|
280
|
+
# print(f"Warning: Prediction shape {{prediction.shape}} differs from input shape {{image.shape}}")
|
|
281
|
+
# prediction = np.squeeze(prediction)
|
|
282
|
+
|
|
283
|
+
# # If shapes still don't match, try to reshape
|
|
284
|
+
# if prediction.shape != image.shape:
|
|
285
|
+
# print(f"Warning: Shapes still don't match after squeezing. Using original dimensions.")
|
|
286
|
+
|
|
287
|
+
# print(f"Denoising completed. Output shape: {{prediction.shape}}")
|
|
288
|
+
|
|
289
|
+
# Save result
|
|
290
|
+
print(f"Saving result to {{os.path.basename('{output_file.name}')}}")
|
|
291
|
+
tifffile.imwrite('{output_file.name}', prediction, compression="zlib")
|
|
292
|
+
print("Done!")
|
|
293
|
+
|
|
294
|
+
# Exit with success code
|
|
295
|
+
sys.exit(0)
|
|
296
|
+
|
|
297
|
+
except Exception as e:
|
|
298
|
+
import traceback
|
|
299
|
+
print(f"Error in CAREamics processing: {{e}}")
|
|
300
|
+
traceback.print_exc()
|
|
301
|
+
sys.exit(1)
|
|
302
|
+
"""
|
|
303
|
+
|
|
304
|
+
# Write script
|
|
305
|
+
script_file.write(script)
|
|
306
|
+
script_file.flush()
|
|
307
|
+
|
|
308
|
+
try:
|
|
309
|
+
# Run the script in the dedicated environment
|
|
310
|
+
env_python = get_env_python_path()
|
|
311
|
+
result = subprocess.run(
|
|
312
|
+
[env_python, script_file.name], capture_output=True, text=True
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
# Print output
|
|
316
|
+
print(result.stdout)
|
|
317
|
+
|
|
318
|
+
# Check for errors
|
|
319
|
+
if result.returncode != 0:
|
|
320
|
+
print("Error in CAREamics processing:")
|
|
321
|
+
print(result.stderr)
|
|
322
|
+
raise RuntimeError(
|
|
323
|
+
f"CAREamics denoising failed with error code {result.returncode}"
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
# Read and return the results
|
|
327
|
+
denoised_image = tifffile.imread(output_file.name)
|
|
328
|
+
return denoised_image
|
|
329
|
+
|
|
330
|
+
except (RuntimeError, subprocess.CalledProcessError) as e:
|
|
331
|
+
print(f"Error in CAREamics processing: {e}")
|
|
332
|
+
# Return original image in case of error
|
|
333
|
+
return args_dict["image"]
|
|
334
|
+
|
|
335
|
+
finally:
|
|
336
|
+
# Clean up temporary files
|
|
337
|
+
for file_path in [input_file.name, output_file.name, script_file.name]:
|
|
338
|
+
with contextlib.suppress(FileNotFoundError, PermissionError):
|
|
339
|
+
os.unlink(file_path)
|