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.
Files changed (54) hide show
  1. napari_tmidas/__init__.py +35 -5
  2. napari_tmidas/_crop_anything.py +1520 -609
  3. napari_tmidas/_env_manager.py +76 -0
  4. napari_tmidas/_file_conversion.py +1646 -1131
  5. napari_tmidas/_file_selector.py +1455 -216
  6. napari_tmidas/_label_inspection.py +83 -8
  7. napari_tmidas/_processing_worker.py +309 -0
  8. napari_tmidas/_reader.py +6 -10
  9. napari_tmidas/_registry.py +2 -2
  10. napari_tmidas/_roi_colocalization.py +1221 -84
  11. napari_tmidas/_tests/test_crop_anything.py +123 -0
  12. napari_tmidas/_tests/test_env_manager.py +89 -0
  13. napari_tmidas/_tests/test_grid_view_overlay.py +193 -0
  14. napari_tmidas/_tests/test_init.py +98 -0
  15. napari_tmidas/_tests/test_intensity_label_filter.py +222 -0
  16. napari_tmidas/_tests/test_label_inspection.py +86 -0
  17. napari_tmidas/_tests/test_processing_basic.py +500 -0
  18. napari_tmidas/_tests/test_processing_worker.py +142 -0
  19. napari_tmidas/_tests/test_regionprops_analysis.py +547 -0
  20. napari_tmidas/_tests/test_registry.py +70 -2
  21. napari_tmidas/_tests/test_scipy_filters.py +168 -0
  22. napari_tmidas/_tests/test_skimage_filters.py +259 -0
  23. napari_tmidas/_tests/test_split_channels.py +217 -0
  24. napari_tmidas/_tests/test_spotiflow.py +87 -0
  25. napari_tmidas/_tests/test_tyx_display_fix.py +142 -0
  26. napari_tmidas/_tests/test_ui_utils.py +68 -0
  27. napari_tmidas/_tests/test_widget.py +30 -0
  28. napari_tmidas/_tests/test_windows_basic.py +66 -0
  29. napari_tmidas/_ui_utils.py +57 -0
  30. napari_tmidas/_version.py +16 -3
  31. napari_tmidas/_widget.py +41 -4
  32. napari_tmidas/processing_functions/basic.py +557 -20
  33. napari_tmidas/processing_functions/careamics_env_manager.py +72 -99
  34. napari_tmidas/processing_functions/cellpose_env_manager.py +415 -112
  35. napari_tmidas/processing_functions/cellpose_segmentation.py +132 -191
  36. napari_tmidas/processing_functions/colocalization.py +513 -56
  37. napari_tmidas/processing_functions/grid_view_overlay.py +703 -0
  38. napari_tmidas/processing_functions/intensity_label_filter.py +422 -0
  39. napari_tmidas/processing_functions/regionprops_analysis.py +1280 -0
  40. napari_tmidas/processing_functions/sam2_env_manager.py +53 -69
  41. napari_tmidas/processing_functions/sam2_mp4.py +274 -195
  42. napari_tmidas/processing_functions/scipy_filters.py +403 -8
  43. napari_tmidas/processing_functions/skimage_filters.py +424 -212
  44. napari_tmidas/processing_functions/spotiflow_detection.py +949 -0
  45. napari_tmidas/processing_functions/spotiflow_env_manager.py +591 -0
  46. napari_tmidas/processing_functions/timepoint_merger.py +334 -86
  47. {napari_tmidas-0.2.2.dist-info → napari_tmidas-0.2.5.dist-info}/METADATA +71 -30
  48. napari_tmidas-0.2.5.dist-info/RECORD +63 -0
  49. napari_tmidas/_tests/__init__.py +0 -0
  50. napari_tmidas-0.2.2.dist-info/RECORD +0 -40
  51. {napari_tmidas-0.2.2.dist-info → napari_tmidas-0.2.5.dist-info}/WHEEL +0 -0
  52. {napari_tmidas-0.2.2.dist-info → napari_tmidas-0.2.5.dist-info}/entry_points.txt +0 -0
  53. {napari_tmidas-0.2.2.dist-info → napari_tmidas-0.2.5.dist-info}/licenses/LICENSE +0 -0
  54. {napari_tmidas-0.2.2.dist-info → napari_tmidas-0.2.5.dist-info}/top_level.txt +0 -0
@@ -11,6 +11,7 @@ for cellular segmentation without requiring diameter parameter.
11
11
 
12
12
  Note: This requires the cellpose library to be installed.
13
13
  """
14
+ from typing import Union
14
15
 
15
16
  import numpy as np
16
17
 
@@ -23,16 +24,15 @@ from napari_tmidas.processing_functions.cellpose_env_manager import (
23
24
 
24
25
  # Check if cellpose is directly available in this environment
25
26
  try:
26
- from cellpose import core, models
27
+ import cellpose # noqa: F401
27
28
 
28
29
  CELLPOSE_AVAILABLE = True
29
- USE_GPU = core.use_gpu()
30
- USE_DEDICATED_ENV = False
30
+ # Don't evaluate USE_GPU here - it should be evaluated in the cellpose environment
31
+
31
32
  print("Cellpose found in current environment. Using native import.")
32
33
  except ImportError:
33
34
  CELLPOSE_AVAILABLE = False
34
- USE_GPU = False
35
- USE_DEDICATED_ENV = True
35
+
36
36
  print(
37
37
  "Cellpose not found in current environment. Will use dedicated environment."
38
38
  )
@@ -88,102 +88,6 @@ def transpose_dimensions(img, dim_order):
88
88
  return img_transposed, new_dim_order, is_3d
89
89
 
90
90
 
91
- def run_cellpose(
92
- img,
93
- model,
94
- channels,
95
- flow_threshold=0.4,
96
- cellprob_threshold=0.0,
97
- dim_order="ZYX",
98
- max_pixels=4000000,
99
- tile_norm_blocksize=128,
100
- batch_size=32,
101
- ):
102
- """
103
- Run Cellpose segmentation on an image using Cellpose 4 (Cellpose-SAM).
104
-
105
- Parameters:
106
- -----------
107
- img : numpy.ndarray
108
- Input image
109
- model : cellpose.models.Cellpose
110
- Cellpose model to use
111
- channels : list
112
- Channels to use for segmentation [0,0] = grayscale, [1,0] = green channel, [2,0] = red channel
113
- flow_threshold : float
114
- Flow threshold for Cellpose
115
- cellprob_threshold : float
116
- Cell probability threshold
117
- dim_order : str
118
- Dimension order of the input image
119
- max_pixels : int
120
- Maximum number of pixels to process (for 2D images)
121
- tile_norm_blocksize : int
122
- Block size for tile normalization (new parameter in Cellpose 4)
123
- batch_size : int
124
- Batch size for processing multiple images or 3D slices at once
125
-
126
- Returns:
127
- --------
128
- numpy.ndarray
129
- Segmented image with labels
130
- """
131
- # First check if the image is too large (for 2D images)
132
- if len(img.shape) == 2 or (len(img.shape) == 3 and "C" in dim_order):
133
- # For 2D images (potentially with channels)
134
- height, width = img.shape[:2]
135
- total_pixels = height * width
136
- if total_pixels > max_pixels:
137
- raise ValueError(
138
- f"Image size ({height}x{width}={total_pixels} pixels) exceeds the "
139
- f"maximum size of {max_pixels} pixels. Consider downsampling."
140
- )
141
-
142
- # Transpose to expected dimension order
143
- img_transposed, new_dim_order, is_3d = transpose_dimensions(img, dim_order)
144
-
145
- # Check if we have a time series
146
- has_time = "T" in new_dim_order
147
-
148
- # Set up normalization with tile_norm_blocksize (Cellpose 4 parameter)
149
- normalize = {"tile_norm_blocksize": tile_norm_blocksize}
150
-
151
- if has_time:
152
- # Handle time series - process each time point
153
- n_timepoints = img_transposed.shape[0]
154
- result = np.zeros(img_transposed.shape, dtype=np.uint32)
155
-
156
- # Process each time point
157
- for t in range(n_timepoints):
158
- img_t = img_transposed[t]
159
- masks, _, _ = model.eval(
160
- img_t,
161
- channels=channels,
162
- flow_threshold=flow_threshold,
163
- cellprob_threshold=cellprob_threshold,
164
- normalize=normalize,
165
- z_axis=0 if is_3d else None,
166
- do_3D=is_3d,
167
- batch_size=batch_size,
168
- )
169
- result[t] = masks
170
- else:
171
- # Process single time point
172
- masks, _, _ = model.eval(
173
- img_transposed,
174
- channels=channels,
175
- flow_threshold=flow_threshold,
176
- cellprob_threshold=cellprob_threshold,
177
- normalize=normalize,
178
- z_axis=0 if is_3d else None,
179
- do_3D=is_3d,
180
- batch_size=batch_size,
181
- )
182
- result = masks
183
-
184
- return result.astype(np.uint32)
185
-
186
-
187
91
  @BatchProcessingRegistry.register(
188
92
  name="Cellpose-SAM Segmentation",
189
93
  suffix="_labels",
@@ -208,19 +112,40 @@ def run_cellpose(
208
112
  # "max": 3,
209
113
  # "description": "Second channel: 0=none, 1=green, 2=red, 3=blue",
210
114
  # },
115
+ "diameter": {
116
+ "type": float,
117
+ "default": 0.0,
118
+ "min": 0.0,
119
+ "max": 200.0,
120
+ "description": "Optional. Only required if your ROI diameter is outside the range 7.5–120. Set to 0 to leave unset (recommended for most users). Cellpose-SAM is trained for diameters in this range.",
121
+ },
211
122
  "flow_threshold": {
212
123
  "type": float,
213
124
  "default": 0.4,
214
125
  "min": 0.1,
215
126
  "max": 0.9,
216
- "description": "Flow threshold for Cellpose segmentation",
127
+ "description": "Flow threshold (cell detection sensitivity)",
217
128
  },
218
129
  "cellprob_threshold": {
219
130
  "type": float,
220
131
  "default": 0.0,
221
132
  "min": -6.0,
222
133
  "max": 6.0,
223
- "description": "Cell probability threshold (Cellpose 4 parameter)",
134
+ "description": "Cell probability threshold (increase to reduce splits)",
135
+ },
136
+ "anisotropy": {
137
+ "type": float,
138
+ "default": 1.0,
139
+ "min": 0.1,
140
+ "max": 10.0,
141
+ "description": "Optional rescaling factor (3D; Z step[um] / X pixel res [um])",
142
+ },
143
+ "flow3D_smooth": {
144
+ "type": int,
145
+ "default": 0,
146
+ "min": 0,
147
+ "max": 10,
148
+ "description": "Smooth flow with gaussian filter (stdev)",
224
149
  },
225
150
  "tile_norm_blocksize": {
226
151
  "type": int,
@@ -236,11 +161,6 @@ def run_cellpose(
236
161
  "max": 128,
237
162
  "description": "Batch size for processing multiple images/slices at once",
238
163
  },
239
- "force_dedicated_env": {
240
- "type": bool,
241
- "default": False,
242
- "description": "Force using dedicated environment even if Cellpose is available",
243
- },
244
164
  },
245
165
  )
246
166
  def cellpose_segmentation(
@@ -250,9 +170,12 @@ def cellpose_segmentation(
250
170
  channel_2: int = 0,
251
171
  flow_threshold: float = 0.4,
252
172
  cellprob_threshold: float = 0.0,
173
+ anisotropy: Union[float, None] = None,
174
+ flow3D_smooth: int = 0,
253
175
  tile_norm_blocksize: int = 128,
254
176
  batch_size: int = 32,
255
- force_dedicated_env: bool = False,
177
+ diameter: float = 0.0,
178
+ _source_filepath: str = None, # Hidden parameter for zarr optimization
256
179
  ) -> np.ndarray:
257
180
  """
258
181
  Run Cellpose 4 (Cellpose-SAM) segmentation on an image.
@@ -282,110 +205,128 @@ def cellpose_segmentation(
282
205
  Block size for tile normalization (Cellpose 4 parameter) (default: 128)
283
206
  batch_size : int
284
207
  Batch size for processing multiple images/slices at once (default: 32)
285
- force_dedicated_env : bool
286
- Force using dedicated environment even if Cellpose is available (default: False)
287
208
 
288
209
  Returns:
289
210
  --------
290
211
  numpy.ndarray
291
212
  Segmented image with instance labels
292
213
  """
214
+ # Diameter parameter guidance:
215
+ # Cellpose-SAM is trained for ROI diameters 7.5–120. Only set diameter if your images have objects outside this range (e.g., diameter <7.5 or >120). Otherwise, leave as None.
216
+ # Cellpose 4 handles normalization internally via percentile-based normalization
217
+ # It accepts uint8, uint16, float32, float64 - no pre-conversion needed!
218
+ # The normalize=True parameter (default) will convert to float and normalize
219
+ # to 1st-99th percentile range internally
220
+
221
+ # Handle TZYX data by processing each timepoint separately
222
+ if "T" in dim_order and image.ndim == 4:
223
+ print(
224
+ f"Detected TZYX data with shape {image.shape}. Processing each timepoint separately..."
225
+ )
226
+ t_axis = dim_order.index("T")
227
+ num_timepoints = image.shape[t_axis]
228
+
229
+ # Move T axis to front if not already there
230
+ if t_axis != 0:
231
+ axes_order = list(range(image.ndim))
232
+ axes_order.insert(0, axes_order.pop(t_axis))
233
+ image = np.transpose(image, axes_order)
234
+
235
+ # Process each timepoint
236
+ results = []
237
+ for t in range(num_timepoints):
238
+ print(f"Processing timepoint {t+1}/{num_timepoints}...")
239
+ timepoint_image = image[t]
240
+ # Remove 'T' from dim_order for single timepoint
241
+ timepoint_dim_order = dim_order.replace("T", "")
242
+
243
+ # Recursively call this function for the single timepoint
244
+ timepoint_result = cellpose_segmentation(
245
+ timepoint_image,
246
+ dim_order=timepoint_dim_order,
247
+ channel_1=channel_1,
248
+ channel_2=channel_2,
249
+ flow_threshold=flow_threshold,
250
+ cellprob_threshold=cellprob_threshold,
251
+ anisotropy=anisotropy,
252
+ flow3D_smooth=flow3D_smooth,
253
+ tile_norm_blocksize=tile_norm_blocksize,
254
+ batch_size=batch_size,
255
+ _source_filepath=None, # Don't use zarr optimization for timepoints
256
+ )
257
+ results.append(timepoint_result)
258
+
259
+ # Stack results back together
260
+ result = np.stack(results, axis=0)
261
+ print(f"Completed processing all {num_timepoints} timepoints.")
262
+ return result
263
+
293
264
  # Convert channel parameters to Cellpose channels list
294
265
  # channels = [channel_1, channel_2]
295
266
  channels = [0, 0] # limit script to single channel
296
- # Determine whether to use dedicated environment
297
- use_env = force_dedicated_env or USE_DEDICATED_ENV
298
267
 
299
- if use_env:
300
- print("Using dedicated Cellpose environment...")
268
+ # First check if the environment exists, create if not
269
+ if not is_env_created():
270
+ print(
271
+ "Creating dedicated Cellpose environment (this may take a few minutes)..."
272
+ )
273
+ create_cellpose_env()
274
+ print("Environment created successfully.")
301
275
 
302
- # First check if the environment exists, create if not
303
- if not is_env_created():
304
- print(
305
- "Creating dedicated Cellpose environment (this may take a few minutes)..."
306
- )
307
- create_cellpose_env()
308
- print("Environment created successfully.")
276
+ # Check if we can use zarr optimization
277
+ use_zarr_direct = _source_filepath and _source_filepath.lower().endswith(
278
+ ".zarr"
279
+ )
309
280
 
310
- # Prepare arguments for the Cellpose function
281
+ if use_zarr_direct:
282
+ print(f"Using optimized zarr processing for: {_source_filepath}")
283
+ # Prepare arguments for direct zarr processing
284
+ is_3d = "Z" in dim_order
311
285
  args = {
312
- "image": image,
286
+ "zarr_path": _source_filepath,
287
+ "zarr_key": None, # Could be enhanced to support specific keys
313
288
  "channels": channels,
314
289
  "flow_threshold": flow_threshold,
315
290
  "cellprob_threshold": cellprob_threshold,
291
+ "flow3D_smooth": flow3D_smooth,
292
+ "anisotropy": anisotropy,
316
293
  "normalize": {"tile_norm_blocksize": tile_norm_blocksize},
317
294
  "batch_size": batch_size,
318
- "use_gpu": USE_GPU,
319
- "do_3D": "Z" in dim_order,
320
- "z_axis": 0 if "Z" in dim_order else None,
295
+ "diameter": diameter,
296
+ "use_gpu": True, # Let cellpose environment detect GPU
297
+ "do_3D": is_3d,
298
+ "z_axis": 0 if is_3d else None,
299
+ "channel_axis": None, # No channel axis for single-channel grayscale images
321
300
  }
322
-
323
- # Run Cellpose in the dedicated environment
324
- print("Running Cellpose model in dedicated environment...")
325
- result = run_cellpose_in_env("eval", args)
326
- print(f"Segmentation complete. Found {np.max(result)} objects.")
327
- return result
328
-
329
301
  else:
330
- print("Running Cellpose model in current environment...")
331
- # Initialize Cellpose model in current environment
332
- model = models.CellposeModel(gpu=USE_GPU)
333
-
334
- # Print status information
335
- gpu_status = "GPU" if USE_GPU else "CPU"
336
- print(f"Using Cellpose on {gpu_status}")
337
- print(
338
- f"Processing image with shape {image.shape}, dimension order: {dim_order}"
339
- )
340
-
341
- # Run segmentation
342
- try:
343
- result = run_cellpose(
344
- image,
345
- model,
346
- channels,
347
- flow_threshold,
348
- cellprob_threshold,
349
- dim_order,
350
- tile_norm_blocksize=tile_norm_blocksize,
351
- batch_size=batch_size,
352
- )
302
+ # Prepare arguments for the Cellpose function (legacy numpy array mode)
303
+ is_3d = "Z" in dim_order
304
+ args = {
305
+ "image": image,
306
+ "channels": channels,
307
+ "flow_threshold": flow_threshold,
308
+ "cellprob_threshold": cellprob_threshold,
309
+ "flow3D_smooth": flow3D_smooth,
310
+ "anisotropy": anisotropy,
311
+ "normalize": {"tile_norm_blocksize": tile_norm_blocksize},
312
+ "batch_size": batch_size,
313
+ "diameter": diameter,
314
+ "use_gpu": True, # Let cellpose environment detect GPU
315
+ "do_3D": is_3d,
316
+ "z_axis": 0 if is_3d else None,
317
+ "channel_axis": None, # No channel axis for single-channel grayscale images
318
+ }
319
+ # Run Cellpose in the dedicated environment
320
+ print("Running Cellpose model in dedicated environment...")
321
+ result = run_cellpose_in_env("eval", args)
322
+ print(f"Segmentation complete. Found {np.max(result)} objects.")
353
323
 
354
- print(f"Segmentation complete. Found {np.max(result)} objects.")
355
- return result
324
+ # Ensure result is uint32 for proper label detection in napari
325
+ # Cellpose typically returns int32, but uint32 is preferred for labels
326
+ if result.dtype != np.uint32:
327
+ result = result.astype(np.uint32)
356
328
 
357
- except (Exception, MemoryError) as e:
358
- print(f"Error during segmentation in current environment: {str(e)}")
359
-
360
- # If we haven't already tried using the dedicated environment, try that as a fallback
361
- if not USE_DEDICATED_ENV and not force_dedicated_env:
362
- print("Attempting fallback to dedicated Cellpose environment...")
363
- try:
364
- args = {
365
- "image": image,
366
- "channels": channels,
367
- "flow_threshold": flow_threshold,
368
- "cellprob_threshold": cellprob_threshold,
369
- "normalize": {"tile_norm_blocksize": tile_norm_blocksize},
370
- "batch_size": batch_size,
371
- "use_gpu": USE_GPU,
372
- "do_3D": "Z" in dim_order,
373
- "z_axis": 0 if "Z" in dim_order else None,
374
- }
375
-
376
- if not is_env_created():
377
- create_cellpose_env()
378
-
379
- result = run_cellpose_in_env("eval", args)
380
- print(f"Fallback successful. Found {np.max(result)} objects.")
381
- return result
382
- except (Exception, MemoryError) as fallback_e:
383
- print(f"Fallback also failed: {str(fallback_e)}")
384
- raise Exception(
385
- f"Both direct and dedicated environment approaches failed: {str(e)} | {str(fallback_e)}"
386
- ) from fallback_e
387
- else:
388
- raise
329
+ return result
389
330
 
390
331
 
391
332
  # Update cellpose_env_manager.py to install Cellpose 4