microlive 1.0.11__py3-none-any.whl → 1.0.19__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.
@@ -1,48 +1,237 @@
1
- """Pipeline module for MicroLive.
1
+ """Particle tracking pipeline for live-cell microscopy analysis.
2
2
 
3
- This module is part of the microlive package.
3
+ This module provides a complete pipeline for automated particle detection,
4
+ tracking, and quantification in live-cell microscopy images. It integrates
5
+ segmentation, spot detection, trajectory linking, MSD analysis, and
6
+ correlation analysis.
7
+
8
+ The pipeline supports:
9
+ - Leica LIF file format with automatic metadata extraction
10
+ - Multi-channel spot detection and tracking
11
+ - Cell segmentation via Cellpose or watershed
12
+ - Photobleaching correction
13
+ - Mean Squared Displacement (MSD) analysis
14
+ - Auto- and cross-correlation analysis
15
+
16
+ Example:
17
+ >>> import microlive.microscopy as mi
18
+ >>> from microlive.pipelines import pipeline_particle_tracking
19
+ >>>
20
+ >>> results = pipeline_particle_tracking(
21
+ ... data_folder_path=pathlib.Path("experiment.lif"),
22
+ ... selected_image=0,
23
+ ... channels_spots=[0],
24
+ ... channels_cytosol=[1],
25
+ ... )
26
+
27
+ Authors:
28
+ Luis U. Aguilera, Ning Zhao
29
+
30
+ License:
31
+ GPL v3
4
32
  """
5
33
  from microlive.imports import *
6
34
 
7
- def pipeline_particle_tracking(data_folder_path, selected_image, channels_spots, max_spots_for_threshold=100000,
8
- show_plot=True, channels_cytosol=None, channels_nucleus=None,memory=1,
9
- min_length_trajectory=5, yx_spot_size_in_px=5, z_spot_size_in_px=2, maximum_spots_cluster=4,cluster_radius_nm =500,
10
- MINIMAL_SNR=0.5, diameter_cytosol=300, diameter_nucleus=200,
11
- segmentation_selection_metric='max_area',recalculate_mask=False,optimization_segmentation_method='diameter_segmentation',
12
- pretrained_model_cyto_segmentation=None,use_watershed=False,list_images_to_process=None,save_3d_visualization=False,
13
- max_percentage_empty_data_in_trajectory=0.1,particle_detection_threshold=None,save_croparray=False,
14
- apply_photobleaching_correction=False,photobleaching_mode='inside_cell',use_maximum_projection=False,
15
- max_lag_for_MSD=30,step_size_in_sec=1,separate_clusters_and_spots=False,maximum_range_search_pixels=10,results_folder_path=None,
16
- calculate_MSD=True,calculate_correlations=True):
17
- # Read images and metadata
18
- # detect if the data is a lif file
19
- list_images, list_names, pixel_xy_um, voxel_z_um, channel_names, number_color_channels, list_time_intervals, bit_depth = \
20
- mi.ReadLif(data_folder_path, show_metadata=False, save_tif=False, save_png=False, format='TZYXC').read()
21
- # Prepare full list of images and set up indices to process
35
+ # =============================================================================
36
+ # Constants
37
+ # =============================================================================
38
+
39
+ # Default percentile values for image visualization
40
+ DEFAULT_MIN_PERCENTILE = 0.5
41
+ DEFAULT_MAX_PERCENTILE = 99.9
42
+ DEFAULT_MIN_PERCENTILE_TRACKING = 0.05
43
+ DEFAULT_MAX_PERCENTILE_TRACKING = 99.95
44
+
45
+ # Default quality thresholds
46
+ DEFAULT_MINIMAL_SNR = 0.5
47
+ DEFAULT_MAX_CROP_PERCENTILE = 99
48
+
49
+
50
+ def pipeline_particle_tracking(
51
+ data_folder_path,
52
+ selected_image,
53
+ channels_spots,
54
+ max_spots_for_threshold=100000,
55
+ show_plot=True,
56
+ channels_cytosol=None,
57
+ channels_nucleus=None,
58
+ memory=1,
59
+ min_length_trajectory=5,
60
+ yx_spot_size_in_px=5,
61
+ z_spot_size_in_px=2,
62
+ maximum_spots_cluster=4,
63
+ cluster_radius_nm=500,
64
+ MINIMAL_SNR=0.5,
65
+ diameter_cytosol=300,
66
+ diameter_nucleus=200,
67
+ segmentation_selection_metric='max_area',
68
+ recalculate_mask=False,
69
+ optimization_segmentation_method='diameter_segmentation',
70
+ pretrained_model_cyto_segmentation=None,
71
+ use_watershed=False,
72
+ list_images_to_process=None,
73
+ save_3d_visualization=False,
74
+ max_percentage_empty_data_in_trajectory=0.1,
75
+ particle_detection_threshold=None,
76
+ save_croparray=False,
77
+ apply_photobleaching_correction=False,
78
+ photobleaching_mode='inside_cell',
79
+ use_maximum_projection=False,
80
+ max_lag_for_MSD=30,
81
+ step_size_in_sec=1,
82
+ separate_clusters_and_spots=False,
83
+ maximum_range_search_pixels=10,
84
+ results_folder_path=None,
85
+ calculate_MSD=True,
86
+ calculate_correlations=True,
87
+ link_particles=True
88
+ ):
89
+ """Execute the complete particle tracking pipeline on LIF microscopy data.
90
+
91
+ This function orchestrates the full analysis workflow: loading images,
92
+ segmentation, spot detection, tracking, and downstream analysis (MSD,
93
+ correlations). It can process a single image or batch process multiple
94
+ images from a LIF file.
95
+
96
+ Args:
97
+ data_folder_path: Path to the LIF file or data folder.
98
+ selected_image: Index of the image to process, or None for batch mode.
99
+ channels_spots: List of channel indices for spot detection.
100
+ max_spots_for_threshold: Maximum spots to consider for threshold
101
+ calculation. Defaults to 100000.
102
+ show_plot: Whether to display plots during processing. Defaults to True.
103
+ channels_cytosol: List of channel indices for cytosol segmentation.
104
+ Defaults to None.
105
+ channels_nucleus: List of channel indices for nucleus segmentation.
106
+ Defaults to None.
107
+ memory: Number of frames to remember for trajectory linking.
108
+ Defaults to 1.
109
+ min_length_trajectory: Minimum trajectory length in frames.
110
+ Defaults to 5.
111
+ yx_spot_size_in_px: Expected spot size in XY (pixels). Defaults to 5.
112
+ z_spot_size_in_px: Expected spot size in Z (pixels). Defaults to 2.
113
+ maximum_spots_cluster: Maximum spots per cluster. Defaults to 4.
114
+ cluster_radius_nm: Cluster radius in nanometers. Defaults to 500.
115
+ MINIMAL_SNR: Minimum signal-to-noise ratio for trajectories.
116
+ Defaults to 0.5.
117
+ diameter_cytosol: Expected cytosol diameter for Cellpose (pixels).
118
+ Defaults to 300.
119
+ diameter_nucleus: Expected nucleus diameter for Cellpose (pixels).
120
+ Defaults to 200.
121
+ segmentation_selection_metric: Metric for mask selection
122
+ ('max_area', 'center', etc.). Defaults to 'max_area'.
123
+ recalculate_mask: Force mask recalculation even if cached.
124
+ Defaults to False.
125
+ optimization_segmentation_method: Segmentation optimization method.
126
+ Defaults to 'diameter_segmentation'.
127
+ pretrained_model_cyto_segmentation: Path to pretrained Cellpose model.
128
+ Defaults to None.
129
+ use_watershed: Use watershed instead of Cellpose. Defaults to False.
130
+ list_images_to_process: List of image names to process in batch mode.
131
+ Defaults to None (all images).
132
+ save_3d_visualization: Save 3D Napari visualization. Defaults to False.
133
+ max_percentage_empty_data_in_trajectory: Maximum fraction of NaN values
134
+ allowed in trajectories. Defaults to 0.1.
135
+ particle_detection_threshold: Manual threshold for spot detection.
136
+ Defaults to None (auto-calculate).
137
+ save_croparray: Save crop array visualization. Defaults to False.
138
+ apply_photobleaching_correction: Apply photobleaching correction.
139
+ Defaults to False.
140
+ photobleaching_mode: Mode for photobleaching correction
141
+ ('inside_cell', 'whole_image'). Defaults to 'inside_cell'.
142
+ use_maximum_projection: Use Z maximum projection. Defaults to False.
143
+ max_lag_for_MSD: Maximum lag time for MSD calculation.
144
+ Defaults to 30.
145
+ step_size_in_sec: Time step between frames in seconds.
146
+ Defaults to 1.
147
+ separate_clusters_and_spots: Separate clustered spots during detection.
148
+ Defaults to False.
149
+ maximum_range_search_pixels: Maximum search range for linking (pixels).
150
+ Defaults to 10.
151
+ results_folder_path: Custom path for results. Defaults to None.
152
+ calculate_MSD: Whether to calculate MSD. Defaults to True.
153
+ calculate_correlations: Whether to calculate correlations.
154
+ Defaults to True.
155
+ link_particles: Whether to link detected spots into trajectories.
156
+ If False, performs detection-only. Defaults to True.
157
+
158
+ Returns:
159
+ tuple: A tuple containing:
160
+ - final_df (pd.DataFrame): Combined tracking results for all images.
161
+ - list_df (list): List of DataFrames, one per processed image.
162
+ - list_masks (list): List of segmentation masks.
163
+ - list_images_tested (list): List of processed images.
164
+ - list_diffusion_coefficient (list): List of diffusion coefficients.
165
+
166
+ Raises:
167
+ FileNotFoundError: If the data_folder_path does not exist.
168
+ ValueError: If channels_spots is empty or invalid.
169
+
170
+ Example:
171
+ >>> # Process a single image
172
+ >>> df, dfs, masks, images, D = pipeline_particle_tracking(
173
+ ... data_folder_path=pathlib.Path("data.lif"),
174
+ ... selected_image=0,
175
+ ... channels_spots=[0],
176
+ ... channels_cytosol=[1],
177
+ ... min_length_trajectory=10,
178
+ ... calculate_MSD=True
179
+ ... )
180
+ >>> print(f"Found {len(df)} spots, D = {D[0]:.4f} um²/s")
181
+ """
182
+ # Read images and metadata from LIF file
183
+ (
184
+ list_images,
185
+ list_names,
186
+ pixel_xy_um,
187
+ voxel_z_um,
188
+ channel_names,
189
+ number_color_channels,
190
+ list_time_intervals,
191
+ bit_depth
192
+ ) = mi.ReadLif(
193
+ data_folder_path,
194
+ show_metadata=False,
195
+ save_tif=False,
196
+ save_png=False,
197
+ format='TZYXC'
198
+ ).read()
199
+
200
+ # Keep a complete copy for reference
22
201
  list_images_complete = list_images.copy()
202
+
203
+ # Filter images if a subset is specified
23
204
  if list_images_to_process is not None:
24
- selected_indices = [i for i in range(len(list_names)) if list_names[i] in list_images_to_process]
205
+ selected_indices = [
206
+ i for i in range(len(list_names))
207
+ if list_names[i] in list_images_to_process
208
+ ]
25
209
  if use_maximum_projection:
26
- # use the maximum projection in Z. but keep the image shape with only one Z slice
27
- list_images = [np.max(list_images[i], axis=1, keepdims=True) for i in selected_indices]
210
+ list_images = [
211
+ np.max(list_images[i], axis=1, keepdims=True)
212
+ for i in selected_indices
213
+ ]
28
214
  else:
29
215
  list_images = [list_images[i] for i in selected_indices]
30
216
  else:
31
217
  selected_indices = range(len(list_names))
32
218
  if use_maximum_projection:
33
- list_images = [np.max(img, axis=1, keepdims=True) for img in list_images]
219
+ list_images = [
220
+ np.max(img, axis=1, keepdims=True) for img in list_images
221
+ ]
34
222
 
35
-
36
-
37
- # If selected_image is None, process all images
223
+ # Batch processing: process all images if selected_image is None
38
224
  if selected_image is None:
39
- list_df, list_masks, list_images_tested, list_diffusion_coefficient = [], [], [], []
40
-
41
- #for idx in range(len(list_images)):
225
+ list_df = []
226
+ list_masks = []
227
+ list_images_tested = []
228
+ list_diffusion_coefficient = []
229
+
42
230
  for idx in range(len(list_images_complete)):
43
231
  if idx not in selected_indices:
44
232
  continue
45
- df, masks,image,diffusion_coefficient = process_single_image(
233
+
234
+ df, masks, image, diffusion_coefficient = process_single_image(
46
235
  data_folder_path=data_folder_path,
47
236
  selected_image=idx,
48
237
  channels_spots=channels_spots,
@@ -52,7 +241,7 @@ def pipeline_particle_tracking(data_folder_path, selected_image, channels_spots,
52
241
  channels_nucleus=channels_nucleus,
53
242
  min_length_trajectory=min_length_trajectory,
54
243
  yx_spot_size_in_px=yx_spot_size_in_px,
55
- z_spot_size_in_px = z_spot_size_in_px,
244
+ z_spot_size_in_px=z_spot_size_in_px,
56
245
  maximum_spots_cluster=maximum_spots_cluster,
57
246
  cluster_radius_nm=cluster_radius_nm,
58
247
  MINIMAL_SNR=MINIMAL_SNR,
@@ -73,7 +262,7 @@ def pipeline_particle_tracking(data_folder_path, selected_image, channels_spots,
73
262
  apply_photobleaching_correction=apply_photobleaching_correction,
74
263
  photobleaching_mode=photobleaching_mode,
75
264
  use_maximum_projection=use_maximum_projection,
76
- max_lag_for_MSD = max_lag_for_MSD,
265
+ max_lag_for_MSD=max_lag_for_MSD,
77
266
  step_size_in_sec=step_size_in_sec,
78
267
  separate_clusters_and_spots=separate_clusters_and_spots,
79
268
  maximum_range_search_pixels=maximum_range_search_pixels,
@@ -84,26 +273,33 @@ def pipeline_particle_tracking(data_folder_path, selected_image, channels_spots,
84
273
  calculate_MSD=calculate_MSD,
85
274
  calculate_correlations=calculate_correlations,
86
275
  save_croparray=save_croparray,
276
+ link_particles=link_particles,
87
277
  )
88
- #if df is None:
89
- # if the df is None or empty, continue to the next image
278
+
279
+ # Skip empty results
90
280
  if df.empty:
91
281
  continue
92
- # rename the field image_id to idx
282
+
283
+ # Update image_id to reflect the original index
93
284
  df['image_id'] = idx
94
285
  list_df.append(df)
95
286
  list_masks.append(masks)
96
287
  list_images_tested.append(image)
97
288
  list_diffusion_coefficient.append(diffusion_coefficient)
98
289
 
99
- if len(list_df) >1 :
290
+ # Combine all DataFrames
291
+ if len(list_df) > 1:
100
292
  final_df = pd.concat(list_df, ignore_index=True)
293
+ elif len(list_df) == 1:
294
+ final_df = list_df[0]
101
295
  else:
102
- final_df = df
296
+ final_df = pd.DataFrame()
297
+
103
298
  return final_df, list_df, list_masks, list_images_tested, list_diffusion_coefficient
299
+
104
300
  else:
105
- # Process single image
106
- df,masks,image, diffusion_coefficient= process_single_image(
301
+ # Single image processing
302
+ df, masks, image, diffusion_coefficient = process_single_image(
107
303
  data_folder_path=data_folder_path,
108
304
  selected_image=selected_image,
109
305
  channels_spots=channels_spots,
@@ -113,7 +309,7 @@ def pipeline_particle_tracking(data_folder_path, selected_image, channels_spots,
113
309
  channels_nucleus=channels_nucleus,
114
310
  min_length_trajectory=min_length_trajectory,
115
311
  yx_spot_size_in_px=yx_spot_size_in_px,
116
- z_spot_size_in_px = z_spot_size_in_px,
312
+ z_spot_size_in_px=z_spot_size_in_px,
117
313
  maximum_spots_cluster=maximum_spots_cluster,
118
314
  cluster_radius_nm=cluster_radius_nm,
119
315
  MINIMAL_SNR=MINIMAL_SNR,
@@ -121,11 +317,11 @@ def pipeline_particle_tracking(data_folder_path, selected_image, channels_spots,
121
317
  diameter_nucleus=diameter_nucleus,
122
318
  segmentation_selection_metric=segmentation_selection_metric,
123
319
  list_images=list_images,
124
- list_names=list_names[idx],
320
+ list_names=list_names,
125
321
  pixel_xy_um=pixel_xy_um,
126
322
  voxel_z_um=voxel_z_um,
127
323
  channel_names=channel_names,
128
- image_time_interval = list_time_intervals[selected_image],
324
+ image_time_interval=list_time_intervals[selected_image],
129
325
  recalculate_mask=recalculate_mask,
130
326
  optimization_segmentation_method=optimization_segmentation_method,
131
327
  pretrained_model_cyto_segmentation=pretrained_model_cyto_segmentation,
@@ -134,7 +330,7 @@ def pipeline_particle_tracking(data_folder_path, selected_image, channels_spots,
134
330
  apply_photobleaching_correction=apply_photobleaching_correction,
135
331
  photobleaching_mode=photobleaching_mode,
136
332
  use_maximum_projection=use_maximum_projection,
137
- max_lag_for_MSD = max_lag_for_MSD,
333
+ max_lag_for_MSD=max_lag_for_MSD,
138
334
  step_size_in_sec=step_size_in_sec,
139
335
  separate_clusters_and_spots=separate_clusters_and_spots,
140
336
  maximum_range_search_pixels=maximum_range_search_pixels,
@@ -145,25 +341,122 @@ def pipeline_particle_tracking(data_folder_path, selected_image, channels_spots,
145
341
  calculate_MSD=calculate_MSD,
146
342
  calculate_correlations=calculate_correlations,
147
343
  save_croparray=save_croparray,
344
+ link_particles=link_particles,
148
345
  )
346
+
149
347
  return df, [df], [masks], [image], [diffusion_coefficient]
150
348
 
151
349
 
350
+ @mi.Utilities().metadata_decorator(
351
+ metadata_folder_func=mi.Utilities().get_metadata_folder,
352
+ exclude_args=['list_images']
353
+ )
354
+ def process_single_image(
355
+ data_folder_path,
356
+ selected_image,
357
+ channels_spots,
358
+ max_spots_for_threshold=100000,
359
+ show_plot=True,
360
+ channels_cytosol=None,
361
+ channels_nucleus=None,
362
+ memory=1,
363
+ min_length_trajectory=5,
364
+ yx_spot_size_in_px=5,
365
+ z_spot_size_in_px=2,
366
+ maximum_spots_cluster=4,
367
+ cluster_radius_nm=500,
368
+ MINIMAL_SNR=0.5,
369
+ diameter_cytosol=300,
370
+ diameter_nucleus=200,
371
+ segmentation_selection_metric='area',
372
+ list_images=None,
373
+ list_names=None,
374
+ pixel_xy_um=None,
375
+ voxel_z_um=None,
376
+ channel_names=None,
377
+ optimization_segmentation_method='diameter_segmentation',
378
+ recalculate_mask=False,
379
+ use_watershed=False,
380
+ pretrained_model_cyto_segmentation=None,
381
+ particle_detection_threshold=None,
382
+ save_croparray=False,
383
+ image_time_interval=None,
384
+ save_3d_visualization=False,
385
+ apply_photobleaching_correction=False,
386
+ photobleaching_mode='inside_cell',
387
+ max_percentage_empty_data_in_trajectory=0.1,
388
+ use_maximum_projection=False,
389
+ max_lag_for_MSD=30,
390
+ step_size_in_sec=1,
391
+ separate_clusters_and_spots=False,
392
+ maximum_range_search_pixels=10,
393
+ results_folder_path=None,
394
+ calculate_MSD=True,
395
+ calculate_correlations=True,
396
+ link_particles=True
397
+ ):
398
+ """Process a single image through the complete particle tracking workflow.
152
399
 
400
+ This function handles the core analysis for one image: segmentation,
401
+ spot detection, trajectory linking, quality filtering, and optional
402
+ MSD and correlation analysis.
153
403
 
404
+ Args:
405
+ data_folder_path: Path to the data folder or LIF file.
406
+ selected_image: Index of the image to process.
407
+ channels_spots: List of channel indices for spot detection.
408
+ max_spots_for_threshold: Maximum spots for threshold calculation.
409
+ show_plot: Whether to display plots.
410
+ channels_cytosol: List of channels for cytosol segmentation.
411
+ channels_nucleus: List of channels for nucleus segmentation.
412
+ memory: Frames to remember for trajectory linking.
413
+ min_length_trajectory: Minimum trajectory length.
414
+ yx_spot_size_in_px: Expected spot size in XY (pixels).
415
+ z_spot_size_in_px: Expected spot size in Z (pixels).
416
+ maximum_spots_cluster: Maximum spots per cluster.
417
+ cluster_radius_nm: Cluster radius in nanometers.
418
+ MINIMAL_SNR: Minimum SNR threshold for quality filtering.
419
+ diameter_cytosol: Expected cytosol diameter (pixels).
420
+ diameter_nucleus: Expected nucleus diameter (pixels).
421
+ segmentation_selection_metric: Metric for mask selection.
422
+ list_images: Pre-loaded list of images.
423
+ list_names: List of image names.
424
+ pixel_xy_um: Pixel size in XY (micrometers).
425
+ voxel_z_um: Voxel size in Z (micrometers).
426
+ channel_names: List of channel names.
427
+ optimization_segmentation_method: Segmentation optimization method.
428
+ recalculate_mask: Force mask recalculation.
429
+ use_watershed: Use watershed segmentation.
430
+ pretrained_model_cyto_segmentation: Path to pretrained model.
431
+ particle_detection_threshold: Manual detection threshold.
432
+ save_croparray: Save crop array visualization.
433
+ image_time_interval: Time interval between frames (seconds).
434
+ save_3d_visualization: Save 3D visualization.
435
+ apply_photobleaching_correction: Apply photobleaching correction.
436
+ photobleaching_mode: Photobleaching correction mode.
437
+ max_percentage_empty_data_in_trajectory: Maximum NaN fraction allowed.
438
+ use_maximum_projection: Use Z maximum projection.
439
+ max_lag_for_MSD: Maximum lag for MSD calculation.
440
+ step_size_in_sec: Time step for MSD (seconds).
441
+ separate_clusters_and_spots: Separate clusters during detection.
442
+ maximum_range_search_pixels: Maximum search range for linking.
443
+ results_folder_path: Custom results folder path.
444
+ calculate_MSD: Whether to calculate MSD.
445
+ calculate_correlations: Whether to calculate correlations.
446
+ link_particles: Whether to link detected spots into trajectories.
447
+ If False, performs detection-only. Defaults to True.
154
448
 
449
+ Returns:
450
+ tuple: A tuple containing:
451
+ - df_tracking (pd.DataFrame): Tracking results with spot positions,
452
+ intensities, and trajectory IDs.
453
+ - masks (np.ndarray): Cell segmentation mask.
454
+ - original_tested_image (np.ndarray): Original image data.
455
+ - diffusion_coefficient (float or None): Calculated D value.
155
456
 
156
- @mi.Utilities().metadata_decorator(metadata_folder_func=mi.Utilities().get_metadata_folder,exclude_args=['list_images',]) # exclude_args=['list_images', 'list_names' ]
157
- def process_single_image(data_folder_path, selected_image, channels_spots, max_spots_for_threshold=100000,
158
- show_plot=True, channels_cytosol=None, channels_nucleus=None,memory=1,
159
- min_length_trajectory=5, yx_spot_size_in_px=5, z_spot_size_in_px=2 , maximum_spots_cluster=4,cluster_radius_nm=500,
160
- MINIMAL_SNR=0.5, diameter_cytosol=300, diameter_nucleus=200, segmentation_selection_metric='area',
161
- list_images=None, list_names=None, pixel_xy_um=None, voxel_z_um=None,
162
- channel_names=None, optimization_segmentation_method='diameter_segmentation',
163
- recalculate_mask=False,use_watershed=False, pretrained_model_cyto_segmentation=None,particle_detection_threshold=None,save_croparray=False,
164
- image_time_interval=None,save_3d_visualization=False,apply_photobleaching_correction=False,photobleaching_mode='inside_cell',max_percentage_empty_data_in_trajectory=0.1,
165
- use_maximum_projection=False,max_lag_for_MSD=30,step_size_in_sec=1,separate_clusters_and_spots=False,maximum_range_search_pixels=10,results_folder_path=None,
166
- calculate_MSD=True,calculate_correlations=True):
457
+ Raises:
458
+ ValueError: If list_images or list_names is None.
459
+ """
167
460
  # Ensure lists are properly formatted
168
461
  channels_spots = [channels_spots] if not isinstance(channels_spots, list) else channels_spots
169
462
  channels_cytosol = [channels_cytosol] if not isinstance(channels_cytosol, list) else channels_cytosol
@@ -174,30 +467,35 @@ def process_single_image(data_folder_path, selected_image, channels_spots, max_s
174
467
  voxel_z_nm = int(voxel_z_um * 1000)
175
468
  list_voxels = [voxel_z_nm, pixel_xy_nm]
176
469
  list_spot_size_px = [z_spot_size_in_px, yx_spot_size_in_px]
177
- # print a line
470
+
471
+ # Log progress
178
472
  print('--------------------------------------------------')
179
473
  print(f'Processing image: {list_names[selected_image]}')
180
- tested_image = list_images[selected_image] # TZYXC
474
+
475
+ tested_image = list_images[selected_image] # TZYXC format
181
476
  original_tested_image = tested_image.copy()
182
- # Creating the results folder
183
- results_name = 'results_' + data_folder_path.stem + '_cell_id_' + str(selected_image)
477
+
478
+ # Create results folder
479
+ results_name = f'results_{data_folder_path.stem}_cell_id_{selected_image}'
184
480
  current_dir = pathlib.Path().absolute()
185
-
186
-
481
+
187
482
  if results_folder_path is not None:
188
- # ensure that results_folder_path is a Path object
189
483
  if not isinstance(results_folder_path, pathlib.Path):
190
484
  results_folder_path = pathlib.Path(results_folder_path)
191
485
  results_folder = results_folder_path.joinpath(results_name)
192
486
  else:
193
487
  results_folder = current_dir.joinpath('results_live_cell', results_name)
488
+
194
489
  results_folder.mkdir(parents=True, exist_ok=True)
195
490
  mi.Utilities().clear_folder_except_substring(results_folder, 'mask')
491
+
196
492
  # Plot the original image
197
493
  plot_name_original = results_folder.joinpath('original_image.png')
198
- suptitle=f'Image: {data_folder_path.stem[:16]} - {list_names[selected_image]} - Cell_ID: {selected_image}'
199
-
200
-
494
+ suptitle = (
495
+ f'Image: {data_folder_path.stem[:16]} - '
496
+ f'{list_names[selected_image]} - Cell_ID: {selected_image}'
497
+ )
498
+
201
499
  mi.Plots().plot_images(
202
500
  image_ZYXC=tested_image[0],
203
501
  figsize=(12, 5),
@@ -205,76 +503,97 @@ def process_single_image(data_folder_path, selected_image, channels_spots, max_s
205
503
  use_maximum_projection=True,
206
504
  use_gaussian_filter=True,
207
505
  cmap='binary',
208
- min_max_percentile=[0.5, 99.9],
506
+ min_max_percentile=[DEFAULT_MIN_PERCENTILE, DEFAULT_MAX_PERCENTILE],
209
507
  show_gird=False,
210
508
  save_plots=True,
211
509
  plot_name=plot_name_original,
212
510
  suptitle=suptitle
213
511
  )
512
+
214
513
  # Read or create masks
215
- mask_file_name = 'mask_' + data_folder_path.stem + '_image_' + str(selected_image) + '.tif'
514
+ mask_file_name = f'mask_{data_folder_path.stem}_image_{selected_image}.tif'
216
515
  mask_file_path = results_folder.joinpath(mask_file_name)
217
516
  path_mask_exist = os.path.exists(str(mask_file_path))
218
- if path_mask_exist and recalculate_mask is False:
517
+
518
+ if path_mask_exist and not recalculate_mask:
219
519
  masks = imread(str(mask_file_path)).astype(bool)
220
520
  else:
221
- # Use Cellpose to create masks
521
+ # Use Cellpose or watershed to create masks
222
522
  if use_watershed:
223
- masks_complete_cells = mi.CellSegmentationWatershed(np.max(tested_image[:,:,:,:,channels_cytosol[0]],
224
- axis=(0,1)), footprint_size=2, ).apply_watershed()
523
+ masks_complete_cells = mi.CellSegmentationWatershed(
524
+ np.max(tested_image[:, :, :, :, channels_cytosol[0]], axis=(0, 1)),
525
+ footprint_size=2
526
+ ).apply_watershed()
225
527
  else:
226
528
  masks_complete_cells, _, _ = mi.CellSegmentation(
227
- tested_image[0],
228
- channels_cytosol=channels_cytosol,
229
- channels_nucleus=channels_nucleus,
230
- diameter_cytosol=diameter_cytosol,
231
- diameter_nucleus=diameter_nucleus,
232
- optimization_segmentation_method=optimization_segmentation_method,
233
- remove_fragmented_cells=False,
234
- show_plot=show_plot,
235
- image_name=None,
236
- NUMBER_OF_CORES=1,
237
- selection_metric=segmentation_selection_metric,
238
- pretrained_model_cyto_segmentation = pretrained_model_cyto_segmentation
239
- ).calculate_masks()
240
- # Selecting the mask that is in the center of the image
529
+ tested_image[0],
530
+ channels_cytosol=channels_cytosol,
531
+ channels_nucleus=channels_nucleus,
532
+ diameter_cytosol=diameter_cytosol,
533
+ diameter_nucleus=diameter_nucleus,
534
+ optimization_segmentation_method=optimization_segmentation_method,
535
+ remove_fragmented_cells=False,
536
+ show_plot=show_plot,
537
+ image_name=None,
538
+ NUMBER_OF_CORES=1,
539
+ selection_metric=segmentation_selection_metric,
540
+ pretrained_model_cyto_segmentation=pretrained_model_cyto_segmentation
541
+ ).calculate_masks()
542
+
543
+ # Select the mask at the center of the image
241
544
  center_y = masks_complete_cells.shape[0] // 2
242
545
  center_x = masks_complete_cells.shape[1] // 2
243
546
  selected_mask_id = masks_complete_cells[center_y, center_x]
547
+
244
548
  if selected_mask_id > 0:
245
549
  masks = masks_complete_cells == selected_mask_id
246
550
  else:
247
- # Select the largest mask that is not the background mask (0)
551
+ # Fall back to the largest mask
248
552
  mask_labels = np.unique(masks_complete_cells)
249
- mask_sizes = [(label, np.sum(masks_complete_cells == label)) for label in mask_labels if label != 0]
553
+ mask_sizes = [
554
+ (label, np.sum(masks_complete_cells == label))
555
+ for label in mask_labels if label != 0
556
+ ]
250
557
  if mask_sizes:
251
558
  selected_mask_id = max(mask_sizes, key=lambda x: x[1])[0]
252
559
  masks = masks_complete_cells == selected_mask_id
253
560
  else:
254
561
  masks = np.zeros_like(masks_complete_cells, dtype=bool)
562
+
255
563
  # Save the mask
256
564
  masks = masks.astype(np.uint8)
257
565
  tifffile.imwrite(str(mask_file_path), masks, dtype='uint8')
258
566
 
567
+ # Apply photobleaching correction if requested
259
568
  if apply_photobleaching_correction:
260
- file_path_photobleacing = results_folder.joinpath('photobleaching.png')
261
- corrected_image = mi.Photobleaching(image_TZYXC=tested_image,mask_YX=masks, show_plot=False, mode= photobleaching_mode,plot_name=file_path_photobleacing).apply_photobleaching_correction() #mi.PhotobleachingCorrection(tested_image).apply_correction()
262
-
569
+ file_path_photobleaching = results_folder.joinpath('photobleaching.png')
570
+ corrected_image = mi.Photobleaching(
571
+ image_TZYXC=tested_image,
572
+ mask_YX=masks,
573
+ show_plot=False,
574
+ mode=photobleaching_mode,
575
+ plot_name=file_path_photobleaching
576
+ ).apply_photobleaching_correction()
263
577
  else:
264
578
  corrected_image = tested_image
579
+
265
580
  # Calculate the threshold for spot detection
266
581
  plot_name_threshold = results_folder.joinpath('threshold_spot_detection.png')
267
-
582
+
268
583
  if particle_detection_threshold is None:
269
584
  starting_threshold = mi.Utilities().calculate_threshold_for_spot_detection(
270
- corrected_image, list_spot_size_px, list_voxels, channels_spots,
585
+ corrected_image,
586
+ list_spot_size_px,
587
+ list_voxels,
588
+ channels_spots,
271
589
  max_spots_for_threshold=max_spots_for_threshold,
272
- show_plot=True,plot_name=plot_name_threshold
590
+ show_plot=True,
591
+ plot_name=plot_name_threshold
273
592
  )
274
593
  else:
275
- starting_threshold = [particle_detection_threshold]*len(channels_spots)
276
-
277
- # Run the particle tracking
594
+ starting_threshold = [particle_detection_threshold] * len(channels_spots)
595
+
596
+ # Run particle tracking
278
597
  try:
279
598
  list_dataframes_trajectories, _ = mi.ParticleTracking(
280
599
  image=corrected_image,
@@ -289,28 +608,32 @@ def process_single_image(data_folder_path, selected_image, channels_spots, max_s
289
608
  yx_spot_size_in_px=yx_spot_size_in_px,
290
609
  z_spot_size_in_px=z_spot_size_in_px,
291
610
  maximum_spots_cluster=maximum_spots_cluster,
292
- cluster_radius_nm = cluster_radius_nm,
611
+ cluster_radius_nm=cluster_radius_nm,
293
612
  separate_clusters_and_spots=separate_clusters_and_spots,
294
613
  maximum_range_search_pixels=maximum_range_search_pixels,
614
+ link_particles=link_particles,
295
615
  ).run()
296
616
  except Exception as e:
297
- print(f'Error: {e}')
617
+ print(f'Error during particle tracking: {e}')
298
618
  return pd.DataFrame(), masks, original_tested_image, None
299
- #df_tracking = list_dataframes_trajectories[0]
619
+
300
620
  df_tracking = list_dataframes_trajectories[0]
301
621
 
302
- if len(df_tracking)==0:
622
+ if len(df_tracking) == 0:
303
623
  return pd.DataFrame(), masks, original_tested_image, None
304
624
 
305
-
625
+ # Combine trajectories from multiple channels if present
306
626
  if len(list_dataframes_trajectories) > 1:
307
627
  for i in range(1, len(list_dataframes_trajectories)):
308
- df_tracking = pd.concat([df_tracking, list_dataframes_trajectories[i]], ignore_index=True)
309
- df_tracking = df_tracking.reset_index(drop=True)
310
- #print(df_tracking)
311
- # Plot histograms for the SNR
312
- selected_field = 'snr' # options are: psf_sigma, snr, 'spot_int'
313
- plot_name_snr = results_folder.joinpath('spots_' + selected_field + '.png')
628
+ df_tracking = pd.concat(
629
+ [df_tracking, list_dataframes_trajectories[i]],
630
+ ignore_index=True
631
+ )
632
+ df_tracking = df_tracking.reset_index(drop=True)
633
+
634
+ # Plot histograms for SNR
635
+ selected_field = 'snr'
636
+ plot_name_snr = results_folder.joinpath(f'spots_{selected_field}.png')
314
637
  mean_snr = mi.Plots().plot_histograms_from_df(
315
638
  df_tracking,
316
639
  selected_field=selected_field,
@@ -321,9 +644,10 @@ def process_single_image(data_folder_path, selected_image, channels_spots, max_s
321
644
  list_colors=channel_names,
322
645
  remove_outliers=True
323
646
  )
324
- # Plot histograms for the spot intensity
647
+
648
+ # Plot histograms for spot intensity
325
649
  selected_field = 'spot_int'
326
- plot_name_int = results_folder.joinpath('spots_' + selected_field + '.png')
650
+ plot_name_int = results_folder.joinpath(f'spots_{selected_field}.png')
327
651
  mean_int = mi.Plots().plot_histograms_from_df(
328
652
  df_tracking,
329
653
  selected_field=selected_field,
@@ -334,11 +658,12 @@ def process_single_image(data_folder_path, selected_image, channels_spots, max_s
334
658
  list_colors=channel_names,
335
659
  remove_outliers=True
336
660
  )
661
+
337
662
  # Remove tracks with low SNR in the tracking channel
338
663
  if MINIMAL_SNR is not None:
339
664
  array_selected_field = mi.Utilities().df_trajectories_to_array(
340
665
  dataframe=df_tracking,
341
- selected_field=selected_field + '_ch_' + str(channels_spots[0]),
666
+ selected_field=f'{selected_field}_ch_{channels_spots[0]}',
342
667
  fill_value='nans'
343
668
  )
344
669
  mean_snr = np.nanmean(array_selected_field, axis=1)
@@ -346,12 +671,13 @@ def process_single_image(data_folder_path, selected_image, channels_spots, max_s
346
671
  df_tracking = df_tracking[~df_tracking['particle'].isin(indices_low_quality_tracks)]
347
672
  df_tracking = df_tracking.reset_index(drop=True)
348
673
  df_tracking['particle'] = df_tracking.groupby('particle').ngroup()
674
+
349
675
  # Plot image intensity histogram
350
-
351
676
  masked_data = corrected_image * masks[np.newaxis, np.newaxis, :, :, np.newaxis].astype(float)
352
- for i in range(len(channels_spots)):
353
- #plot_name_histogram = results_folder.joinpath('pixel_histogram_in_cell.png')
354
- plot_name_histogram = results_folder.joinpath('pixel_histogram_in_cell_'+str(channels_spots[i])+'.png')
677
+ for i in range(len(channels_spots)):
678
+ plot_name_histogram = results_folder.joinpath(
679
+ f'pixel_histogram_in_cell_{channels_spots[i]}.png'
680
+ )
355
681
  mi.Plots().plot_image_pixel_intensity_distribution(
356
682
  image=np.mean(masked_data, axis=(0, 1)),
357
683
  figsize=(8, 2),
@@ -365,8 +691,12 @@ def process_single_image(data_folder_path, selected_image, channels_spots, max_s
365
691
  tracking_channel=channels_spots[0],
366
692
  threshold_tracking=starting_threshold[i]
367
693
  )
694
+
368
695
  # Plot original image and tracks
369
- suptitle = f'Image: {data_folder_path.stem[:16]} - {list_names[selected_image]} - Cell_ID: {selected_image}'
696
+ suptitle = (
697
+ f'Image: {data_folder_path.stem[:16]} - '
698
+ f'{list_names[selected_image]} - Cell_ID: {selected_image}'
699
+ )
370
700
  plot_name_original_image_and_tracks = results_folder.joinpath('original_image_tracking.png')
371
701
  mi.Plots().plot_images(
372
702
  image_ZYXC=corrected_image[0],
@@ -380,110 +710,220 @@ def process_single_image(data_folder_path, selected_image, channels_spots, max_s
380
710
  use_maximum_projection=True,
381
711
  use_gaussian_filter=True,
382
712
  cmap='binary',
383
- min_max_percentile=[0.05, 99.95],
713
+ min_max_percentile=[DEFAULT_MIN_PERCENTILE_TRACKING, DEFAULT_MAX_PERCENTILE_TRACKING],
384
714
  show_gird=False,
385
715
  save_plots=True,
386
716
  plot_name=plot_name_original_image_and_tracks
387
717
  )
388
-
718
+
389
719
  # Combine the original image and the image with tracks
390
720
  plot_name_complete_image = results_folder.joinpath('complete_image_tracking.png')
391
- mi.Utilities().combine_images_vertically([plot_name_original, plot_name_original_image_and_tracks], plot_name_complete_image, delete_originals=True)
721
+ mi.Utilities().combine_images_vertically(
722
+ [plot_name_original, plot_name_original_image_and_tracks],
723
+ plot_name_complete_image,
724
+ delete_originals=True
725
+ )
392
726
 
393
727
  # Save the DataFrame
394
728
  df_tracking.to_csv(results_folder.joinpath('tracking_results.csv'), index=False)
395
- PLOT_FILTERED_IMAGES = True
396
- normalize_each_particle = True
397
- crop_size = yx_spot_size_in_px + 5 # 3 pixels for the border
398
- # add 5 pixels to crop_size, check if the crop_size is odd, if not, add 1
729
+
730
+ # Prepare crop arrays for visualization
731
+ normalize_each_particle = True
732
+ crop_size = yx_spot_size_in_px + 5
399
733
  if crop_size % 2 == 0:
400
734
  crop_size += 1
401
735
  selected_time_point = None
402
- #if PLOT_FILTERED_IMAGES:
403
- filtered_image = mi.Utilities().gaussian_laplace_filter_image(corrected_image, list_spot_size_px, list_voxels)
404
- croparray_filtered, mean_crop_filtered, first_appearance, crop_size = mi.CropArray(image=filtered_image, df_crops=df_tracking, crop_size=crop_size, remove_outliers=False, max_percentile=99.95,selected_time_point=selected_time_point,normalize_each_particle=normalize_each_particle).run()
405
- #else:
406
- # croparray_filtered, mean_crop_filtered, first_appearance, crop_size = mi.CropArray(image=tested_image, df_crops=df_tracking, crop_size=crop_size, remove_outliers=False, max_percentile=99.9,selected_time_point=selected_time_point,normalize_each_particle=normalize_each_particle).run()
407
- # Plot all crops
408
- if save_croparray:
736
+
737
+ filtered_image = mi.Utilities().gaussian_laplace_filter_image(
738
+ corrected_image, list_spot_size_px, list_voxels
739
+ )
740
+ croparray_filtered, mean_crop_filtered, first_appearance, crop_size = mi.CropArray(
741
+ image=filtered_image,
742
+ df_crops=df_tracking,
743
+ crop_size=crop_size,
744
+ remove_outliers=False,
745
+ max_percentile=DEFAULT_MAX_PERCENTILE_TRACKING,
746
+ selected_time_point=selected_time_point,
747
+ normalize_each_particle=normalize_each_particle
748
+ ).run()
749
+
750
+ # Save crop array if requested
751
+ if save_croparray:
409
752
  path_crop_array = results_folder.joinpath('crop_array.png')
410
- mi.Plots().plot_croparray(croparray_filtered, crop_size, save_plots=True,plot_name= path_crop_array,suptitle=None,show_particle_labels=True, cmap='binary_r',max_percentile = 99) # flag_vector=flag_vector
411
- # plot pair of crops
753
+ mi.Plots().plot_croparray(
754
+ croparray_filtered,
755
+ crop_size,
756
+ save_plots=True,
757
+ plot_name=path_crop_array,
758
+ suptitle=None,
759
+ show_particle_labels=True,
760
+ cmap='binary_r',
761
+ max_percentile=DEFAULT_MAX_CROP_PERCENTILE
762
+ )
763
+
764
+ # Plot pair of crops
412
765
  plot_name_crops_filter = results_folder.joinpath('crops.png')
413
- mi.Plots().plot_matrix_pair_crops (mean_crop_filtered, crop_size,save_plots=True,plot_name=plot_name_crops_filter) # flag_vector=flag_vector
766
+ mi.Plots().plot_matrix_pair_crops(
767
+ mean_crop_filtered,
768
+ crop_size,
769
+ save_plots=True,
770
+ plot_name=plot_name_crops_filter
771
+ )
772
+
414
773
  # Calculate the Mean Squared Displacement
415
774
  plot_name_MSD = results_folder.joinpath('MSD_plot.png')
416
-
417
- #max_lag_for_MSD = 30
775
+
418
776
  if image_time_interval is None:
419
777
  image_time_interval = step_size_in_sec
420
- print(f'Warning: The image_time_interval was not provided. Using the step_size_in_sec as the image_time_interval: {step_size_in_sec} seconds.')
778
+ print(
779
+ f'Warning: image_time_interval not provided. '
780
+ f'Using step_size_in_sec: {step_size_in_sec} seconds.'
781
+ )
421
782
  else:
422
783
  image_time_interval = float(image_time_interval)
423
- # print a warning message indicating that we are using the step_size_in_sec as the image_time_interval
424
-
425
- if calculate_MSD:
426
- diffusion_coefficient, em, time_range, model_fit, trackpy_df = mi.ParticleMotion(df_tracking,
427
- microns_per_pixel=pixel_xy_um,
428
- step_size_in_sec=image_time_interval,
429
- max_lagtime=max_lag_for_MSD,
430
- show_plot=True,
431
- remove_drift=False,
432
- plot_name=plot_name_MSD).calculate_msd()
784
+
785
+ # MSD and correlations require linked trajectories
786
+ if not link_particles:
787
+ if calculate_MSD or calculate_correlations:
788
+ print(
789
+ 'Warning: MSD and correlation calculations require linked trajectories. '
790
+ 'Skipping because link_particles=False.'
791
+ )
792
+ diffusion_coefficient = None
793
+ elif calculate_MSD:
794
+ # calculate_msd() returns: D_um2_s, D_px2_s, em_um2, em_px2, fit_times, fit_line_msd, trackpy_df
795
+ diffusion_coefficient, _, _, _, _, _, _ = mi.ParticleMotion(
796
+ df_tracking,
797
+ microns_per_pixel=pixel_xy_um,
798
+ step_size_in_sec=image_time_interval,
799
+ max_lagtime=max_lag_for_MSD,
800
+ show_plot=True,
801
+ remove_drift=False,
802
+ plot_name=plot_name_MSD
803
+ ).calculate_msd()
433
804
  else:
434
805
  diffusion_coefficient = None
435
806
 
436
-
437
- if calculate_correlations:
438
- # calculate and plot the autocorrelation
439
- array_ch0= mi.Utilities().df_trajectories_to_array(dataframe=df_tracking, selected_field='spot_int_ch_0', fill_value='nans')
440
-
807
+ # Calculate correlations (requires linked trajectories)
808
+ if calculate_correlations and link_particles:
809
+ array_ch0 = mi.Utilities().df_trajectories_to_array(
810
+ dataframe=df_tracking,
811
+ selected_field='spot_int_ch_0',
812
+ fill_value='nans'
813
+ )
814
+
441
815
  if 'spot_int_ch_1' in df_tracking.columns:
442
- array_ch1= mi.Utilities().df_trajectories_to_array(dataframe=df_tracking, selected_field='spot_int_ch_1', fill_value='nans')
443
- intensity_array_ch0_short, intensity_array_ch1_short = mi.Utilities().shift_trajectories(array_ch0, array_ch1,max_percentage_empty_data_in_trajectory=max_percentage_empty_data_in_trajectory)
816
+ array_ch1 = mi.Utilities().df_trajectories_to_array(
817
+ dataframe=df_tracking,
818
+ selected_field='spot_int_ch_1',
819
+ fill_value='nans'
820
+ )
821
+ intensity_array_ch0_short, intensity_array_ch1_short = mi.Utilities().shift_trajectories(
822
+ array_ch0,
823
+ array_ch1,
824
+ max_percentage_empty_data_in_trajectory=max_percentage_empty_data_in_trajectory
825
+ )
444
826
  else:
445
827
  array_ch1 = None
446
- intensity_array_ch0_short = mi.Utilities().shift_trajectories(array_ch0,max_percentage_empty_data_in_trajectory=max_percentage_empty_data_in_trajectory)
828
+ intensity_array_ch0_short = mi.Utilities().shift_trajectories(
829
+ array_ch0,
830
+ max_percentage_empty_data_in_trajectory=max_percentage_empty_data_in_trajectory
831
+ )
447
832
  intensity_array_ch1_short = None
448
833
 
449
834
  plot_name_intensity_matrix = results_folder.joinpath('intensity_matrix.png')
450
- mi.Plots().plot_matrix_sample_time(intensity_array_ch0_short, intensity_array_ch1_short,plot_name=plot_name_intensity_matrix)
835
+ mi.Plots().plot_matrix_sample_time(
836
+ intensity_array_ch0_short,
837
+ intensity_array_ch1_short,
838
+ plot_name=plot_name_intensity_matrix
839
+ )
451
840
 
452
841
  plot_name_AC_ch0 = results_folder.joinpath('AC_plot_ch0.png')
453
- mean_correlation_ch0, std_correlation_ch0, lags_ch0, correlations_array_ch0,dwell_time_ch0 = mi.Correlation(primary_data=intensity_array_ch0_short, max_lag=None,
454
- nan_handling='ignore',shift_data=True,return_full=False,
455
- time_interval_between_frames_in_seconds=image_time_interval,
456
- show_plot=True,start_lag=1,fit_type='exponential',
457
- use_linear_projection_for_lag_0=True,save_plots=True,plot_name=plot_name_AC_ch0).run()
842
+ (
843
+ mean_correlation_ch0,
844
+ std_correlation_ch0,
845
+ lags_ch0,
846
+ correlations_array_ch0,
847
+ dwell_time_ch0
848
+ ) = mi.Correlation(
849
+ primary_data=intensity_array_ch0_short,
850
+ max_lag=None,
851
+ nan_handling='ignore',
852
+ shift_data=True,
853
+ return_full=False,
854
+ time_interval_between_frames_in_seconds=image_time_interval,
855
+ show_plot=True,
856
+ start_lag=1,
857
+ fit_type='exponential',
858
+ use_linear_projection_for_lag_0=True,
859
+ save_plots=True,
860
+ plot_name=plot_name_AC_ch0
861
+ ).run()
862
+
458
863
  if array_ch1 is not None:
459
864
  plot_name_AC_ch1 = results_folder.joinpath('AC_plot_ch1.png')
460
- mean_correlation_ch1, std_correlation_ch1, lags_ch1, correlations_array_ch1,dwell_time_ch1 = mi.Correlation(primary_data=intensity_array_ch1_short, max_lag=None,
461
- nan_handling='ignore',shift_data=True,return_full=False,
462
- time_interval_between_frames_in_seconds=image_time_interval,
463
- show_plot=True,start_lag=1,fit_type='exponential',
464
- use_linear_projection_for_lag_0=True,save_plots=True,plot_name=plot_name_AC_ch1).run()
465
-
865
+ (
866
+ mean_correlation_ch1,
867
+ std_correlation_ch1,
868
+ lags_ch1,
869
+ correlations_array_ch1,
870
+ dwell_time_ch1
871
+ ) = mi.Correlation(
872
+ primary_data=intensity_array_ch1_short,
873
+ max_lag=None,
874
+ nan_handling='ignore',
875
+ shift_data=True,
876
+ return_full=False,
877
+ time_interval_between_frames_in_seconds=image_time_interval,
878
+ show_plot=True,
879
+ start_lag=1,
880
+ fit_type='exponential',
881
+ use_linear_projection_for_lag_0=True,
882
+ save_plots=True,
883
+ plot_name=plot_name_AC_ch1
884
+ ).run()
885
+
466
886
  # Plot cross-correlation
467
887
  plot_name_cross_correlation = results_folder.joinpath('cross_correlation.png')
468
- mean_cross_correlation, std_cross_correlation, lags_cross_correlation, cross_correlations_array, max_lag = mi.Correlation(primary_data=intensity_array_ch0_short, secondary_data=intensity_array_ch1_short,
469
- max_lag=None, nan_handling='ignore', shift_data=False, return_full=True,
470
- time_interval_between_frames_in_seconds=image_time_interval,show_plot=True,
471
- save_plots=True,plot_name=plot_name_cross_correlation).run()
472
-
888
+ (
889
+ mean_cross_correlation,
890
+ std_cross_correlation,
891
+ lags_cross_correlation,
892
+ cross_correlations_array,
893
+ max_lag
894
+ ) = mi.Correlation(
895
+ primary_data=intensity_array_ch0_short,
896
+ secondary_data=intensity_array_ch1_short,
897
+ max_lag=None,
898
+ nan_handling='ignore',
899
+ shift_data=False,
900
+ return_full=True,
901
+ time_interval_between_frames_in_seconds=image_time_interval,
902
+ show_plot=True,
903
+ save_plots=True,
904
+ plot_name=plot_name_cross_correlation
905
+ ).run()
473
906
 
474
- # plot napari visualizer
907
+ # Plot 3D visualization if requested
475
908
  if save_3d_visualization:
476
909
  mask_expanded = masks[np.newaxis, np.newaxis, :, :, np.newaxis]
477
910
  masked_image_TZYXC = filtered_image * mask_expanded
478
- # Apply Gaussian filter to reduce background noise
479
- #from scipy.ndimage import gaussian_filter
480
911
  masked_image_TZYXC = gaussian_filter(masked_image_TZYXC, sigma=1)
481
- # Remove extreme values from the image
482
- masked_image_TZYXC = mi.RemoveExtrema(masked_image_TZYXC, min_percentile=0.001, max_percentile=99.995).remove_outliers()
912
+ masked_image_TZYXC = mi.RemoveExtrema(
913
+ masked_image_TZYXC,
914
+ min_percentile=0.001,
915
+ max_percentile=99.995
916
+ ).remove_outliers()
483
917
  plot_name_3d_visualizer = str(results_folder.joinpath('image_3d.gif'))
484
- mi.Plots().Napari_Visualizer(masked_image_TZYXC, df_tracking, z_correction=7, channels_spots=0, plot_name=plot_name_3d_visualizer)
485
-
486
- # print the process has finished for the selected image
918
+ mi.Plots().Napari_Visualizer(
919
+ masked_image_TZYXC,
920
+ df_tracking,
921
+ z_correction=7,
922
+ channels_spots=0,
923
+ plot_name=plot_name_3d_visualizer
924
+ )
925
+
926
+ # Log completion
487
927
  print(f'Image {list_names[selected_image]} has been processed.')
488
-
928
+
489
929
  return df_tracking, masks, original_tested_image, diffusion_coefficient