celldetective 1.1.1.post3__py3-none-any.whl → 1.1.1.post4__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.
@@ -48,14 +48,14 @@ def set_live_status(setA,setB,status, not_status_option):
48
48
 
49
49
  """
50
50
 
51
-
52
- if status is None:
51
+ print(f"Provided statuses: {status}...")
52
+ if status is None or status==["live_status","live_status"] or status==[None,None]:
53
53
  setA.loc[:,'live_status'] = 1
54
54
  setB.loc[:,'live_status'] = 1
55
55
  status = ['live_status', 'live_status']
56
56
  elif isinstance(status,list):
57
57
  assert len(status)==2,'Please provide only two columns to classify cells as alive or dead.'
58
- if status[0] is None:
58
+ if status[0] is None or status[0]=='live_status':
59
59
  setA.loc[:,'live_status'] = 1
60
60
  status[0] = 'live_status'
61
61
  elif status[0] is not None and isinstance(not_status_option,list):
@@ -63,7 +63,7 @@ def set_live_status(setA,setB,status, not_status_option):
63
63
  if not_status_option[0]:
64
64
  setA.loc[:,'not_'+status[0]] = [not a if a==0 or a==1 else np.nan for a in setA.loc[:,status[0]].values]
65
65
  status[0] = 'not_'+status[0]
66
- if status[1] is None:
66
+ if status[1] is None or status[1]=='live_status':
67
67
  setB.loc[:,'live_status'] = 1
68
68
  status[1] = 'live_status'
69
69
  elif status[1] is not None and isinstance(not_status_option,list):
@@ -380,16 +380,39 @@ def compute_neighborhood_at_position(pos, distance, population=['targets','effec
380
380
  if df_A_pkl is not None:
381
381
  pkl_columns = np.array(df_A_pkl.columns)
382
382
  neigh_columns = np.array([c.startswith('neighborhood') for c in pkl_columns])
383
- cols = list(pkl_columns[neigh_columns]) + ['TRACK_ID','FRAME']
383
+ cols = list(pkl_columns[neigh_columns]) + ['FRAME']
384
+
385
+ if 'TRACK_ID' in list(pkl_columns):
386
+ cols.append('TRACK_ID')
387
+ on_cols = ['TRACK_ID','FRAME']
388
+ else:
389
+ cols.append('ID')
390
+ on_cols = ['ID','FRAME']
391
+
384
392
  print(f'Recover {cols} from the pickle file...')
385
- df_A = pd.merge(df_A, df_A_pkl.loc[:,cols], how="outer", on=['TRACK_ID','FRAME'])
386
- print(df_A.columns)
393
+ try:
394
+ df_A = pd.merge(df_A, df_A_pkl.loc[:,cols], how="outer", on=on_cols)
395
+ print(df_A.columns)
396
+ except Exception as e:
397
+ print(f'Failure to merge pickle and csv files: {e}')
398
+
387
399
  if df_B_pkl is not None and df_B is not None:
388
400
  pkl_columns = np.array(df_B_pkl.columns)
389
401
  neigh_columns = np.array([c.startswith('neighborhood') for c in pkl_columns])
390
- cols = list(pkl_columns[neigh_columns]) + ['TRACK_ID','FRAME']
402
+ cols = list(pkl_columns[neigh_columns]) + ['FRAME']
403
+
404
+ if 'TRACK_ID' in list(pkl_columns):
405
+ cols.append('TRACK_ID')
406
+ on_cols = ['TRACK_ID','FRAME']
407
+ else:
408
+ cols.append('ID')
409
+ on_cols = ['ID','FRAME']
410
+
391
411
  print(f'Recover {cols} from the pickle file...')
392
- df_B = pd.merge(df_B, df_B_pkl.loc[:,cols], how="outer", on=['TRACK_ID','FRAME'])
412
+ try:
413
+ df_B = pd.merge(df_B, df_B_pkl.loc[:,cols], how="outer", on=on_cols)
414
+ except Exception as e:
415
+ print(f'Failure to merge pickle and csv files: {e}')
393
416
 
394
417
  if clear_neigh:
395
418
  unwanted = df_A.columns[df_A.columns.str.contains('neighborhood')]
@@ -408,17 +431,22 @@ def compute_neighborhood_at_position(pos, distance, population=['targets','effec
408
431
  elif neighborhood_kwargs['mode']=='self':
409
432
  neigh_col = f'neighborhood_self_circle_{d}_px'
410
433
 
411
- edge_filter_A = (df_A['POSITION_X'] > td)&(df_A['POSITION_Y'] > td)&(df_A['POSITION_Y'] < (img_shape[0] - td))&(df_A['POSITION_X'] < (img_shape[1] - td))
412
- edge_filter_B = (df_B['POSITION_X'] > td)&(df_B['POSITION_Y'] > td)&(df_B['POSITION_Y'] < (img_shape[0] - td))&(df_B['POSITION_X'] < (img_shape[1] - td))
413
- df_A.loc[~edge_filter_A, neigh_col] = np.nan
414
- df_B.loc[~edge_filter_B, neigh_col] = np.nan
434
+ # edge_filter_A = (df_A['POSITION_X'] > td)&(df_A['POSITION_Y'] > td)&(df_A['POSITION_Y'] < (img_shape[0] - td))&(df_A['POSITION_X'] < (img_shape[1] - td))
435
+ # edge_filter_B = (df_B['POSITION_X'] > td)&(df_B['POSITION_Y'] > td)&(df_B['POSITION_Y'] < (img_shape[0] - td))&(df_B['POSITION_X'] < (img_shape[1] - td))
436
+ # df_A.loc[~edge_filter_A, neigh_col] = np.nan
437
+ # df_B.loc[~edge_filter_B, neigh_col] = np.nan
415
438
 
439
+ print('Count neighborhood...')
416
440
  df_A = compute_neighborhood_metrics(df_A, neigh_col, metrics=['inclusive','exclusive','intermediate'], decompose_by_status=True)
417
441
  if neighborhood_kwargs['symmetrize']:
418
442
  df_B = compute_neighborhood_metrics(df_B, neigh_col, metrics=['inclusive','exclusive','intermediate'], decompose_by_status=True)
443
+ print('Done...')
419
444
 
420
- df_A = mean_neighborhood_before_event(df_A, neigh_col, event_time_col)
421
- df_A = mean_neighborhood_after_event(df_A, neigh_col, event_time_col)
445
+ if 'TRACK_ID' in list(df_A.columns):
446
+ print('Estimate average neighborhood before/after event...')
447
+ df_A = mean_neighborhood_before_event(df_A, neigh_col, event_time_col)
448
+ df_A = mean_neighborhood_after_event(df_A, neigh_col, event_time_col)
449
+ print('Done...')
422
450
 
423
451
  df_A.to_pickle(path_A.replace('.csv','.pkl'))
424
452
  if not population[0]==population[1]:
@@ -485,9 +513,14 @@ def compute_neighborhood_metrics(neigh_table, neigh_col, metrics=['inclusive','e
485
513
 
486
514
  neigh_table = neigh_table.reset_index(drop=True)
487
515
  if 'position' in list(neigh_table.columns):
488
- groupbycols = ['position','TRACK_ID']
516
+ groupbycols = ['position']
489
517
  else:
490
- groupbycols = ['TRACK_ID']
518
+ groupbycols = []
519
+ if 'TRACK_ID' in list(neigh_table.columns):
520
+ groupbycols.append('TRACK_ID')
521
+ else:
522
+ groupbycols.append('ID')
523
+
491
524
  neigh_table.sort_values(by=groupbycols+['FRAME'],inplace=True)
492
525
 
493
526
  for tid,group in neigh_table.groupby(groupbycols):
@@ -607,10 +640,16 @@ def mean_neighborhood_before_event(neigh_table, neigh_col, event_time_col, metri
607
640
  """
608
641
 
609
642
 
643
+ neigh_table = neigh_table.reset_index(drop=True)
610
644
  if 'position' in list(neigh_table.columns):
611
- groupbycols = ['position','TRACK_ID']
645
+ groupbycols = ['position']
612
646
  else:
613
- groupbycols = ['TRACK_ID']
647
+ groupbycols = []
648
+ if 'TRACK_ID' in list(neigh_table.columns):
649
+ groupbycols.append('TRACK_ID')
650
+ else:
651
+ groupbycols.append('ID')
652
+
614
653
  neigh_table.sort_values(by=groupbycols+['FRAME'],inplace=True)
615
654
  suffix = '_before_event'
616
655
 
@@ -681,10 +720,16 @@ def mean_neighborhood_after_event(neigh_table, neigh_col, event_time_col, metric
681
720
  """
682
721
 
683
722
 
723
+ neigh_table = neigh_table.reset_index(drop=True)
684
724
  if 'position' in list(neigh_table.columns):
685
- groupbycols = ['position','TRACK_ID']
725
+ groupbycols = ['position']
726
+ else:
727
+ groupbycols = []
728
+ if 'TRACK_ID' in list(neigh_table.columns):
729
+ groupbycols.append('TRACK_ID')
686
730
  else:
687
- groupbycols = ['TRACK_ID']
731
+ groupbycols.append('ID')
732
+
688
733
  neigh_table.sort_values(by=groupbycols+['FRAME'],inplace=True)
689
734
  suffix = '_after_event'
690
735
 
@@ -1096,6 +1141,8 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1096
1141
 
1097
1142
  df_A, path_A = get_position_table(pos, population=population[0], return_path=True)
1098
1143
  df_B, path_B = get_position_table(pos, population=population[1], return_path=True)
1144
+ if df_A is None or df_B is None:
1145
+ return None
1099
1146
 
1100
1147
  df_A_pkl = get_position_pickle(pos, population=population[0], return_path=False)
1101
1148
  df_B_pkl = get_position_pickle(pos, population=population[1], return_path=False)
@@ -1103,16 +1150,39 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1103
1150
  if df_A_pkl is not None:
1104
1151
  pkl_columns = np.array(df_A_pkl.columns)
1105
1152
  neigh_columns = np.array([c.startswith('neighborhood') for c in pkl_columns])
1106
- cols = list(pkl_columns[neigh_columns]) + ['TRACK_ID','FRAME']
1153
+ cols = list(pkl_columns[neigh_columns]) + ['FRAME']
1154
+
1155
+ if 'TRACK_ID' in list(pkl_columns):
1156
+ cols.append('TRACK_ID')
1157
+ on_cols = ['TRACK_ID','FRAME']
1158
+ else:
1159
+ cols.append('ID')
1160
+ on_cols = ['ID','FRAME']
1161
+
1107
1162
  print(f'Recover {cols} from the pickle file...')
1108
- df_A = pd.merge(df_A, df_A_pkl.loc[:,cols], how="outer", on=['TRACK_ID','FRAME'])
1109
- print(df_A.columns)
1163
+ try:
1164
+ df_A = pd.merge(df_A, df_A_pkl.loc[:,cols], how="outer", on=on_cols)
1165
+ print(df_A.columns)
1166
+ except Exception as e:
1167
+ print(f'Failure to merge pickle and csv files: {e}')
1168
+
1110
1169
  if df_B_pkl is not None and df_B is not None:
1111
1170
  pkl_columns = np.array(df_B_pkl.columns)
1112
1171
  neigh_columns = np.array([c.startswith('neighborhood') for c in pkl_columns])
1113
- cols = list(pkl_columns[neigh_columns]) + ['TRACK_ID','FRAME']
1172
+ cols = list(pkl_columns[neigh_columns]) + ['FRAME']
1173
+
1174
+ if 'TRACK_ID' in list(pkl_columns):
1175
+ cols.append('TRACK_ID')
1176
+ on_cols = ['TRACK_ID','FRAME']
1177
+ else:
1178
+ cols.append('ID')
1179
+ on_cols = ['ID','FRAME']
1180
+
1114
1181
  print(f'Recover {cols} from the pickle file...')
1115
- df_B = pd.merge(df_B, df_B_pkl.loc[:,cols], how="outer", on=['TRACK_ID','FRAME'])
1182
+ try:
1183
+ df_B = pd.merge(df_B, df_B_pkl.loc[:,cols], how="outer", on=on_cols)
1184
+ except Exception as e:
1185
+ print(f'Failure to merge pickle and csv files: {e}')
1116
1186
 
1117
1187
  labelsA = locate_labels(pos, population=population[0])
1118
1188
  if population[1]==population[0]:
@@ -15,8 +15,6 @@ from gc import collect
15
15
  from lmfit import Parameters, Model, models
16
16
  import tifffile.tifffile as tiff
17
17
 
18
- from tifffile import imwrite
19
-
20
18
  def estimate_background_per_condition(experiment, threshold_on_std=1, well_option='*', target_channel="channel_name", frame_range=[0,5], mode="timeseries", activation_protocol=[['gauss',2],['std',4]], show_progress_per_pos=False, show_progress_per_well=True):
21
19
 
22
20
  """
@@ -98,53 +96,65 @@ def estimate_background_per_condition(experiment, threshold_on_std=1, well_optio
98
96
  for l,pos_path in enumerate(tqdm(positions, disable=not show_progress_per_pos)):
99
97
 
100
98
  stack_path = get_position_movie_path(pos_path, prefix=movie_prefix)
99
+ if stack_path is not None:
100
+ len_movie_auto = auto_load_number_of_frames(stack_path)
101
+ if len_movie_auto is not None:
102
+ len_movie = len_movie_auto
103
+ img_num_channels = _get_img_num_per_channel(channel_indices, int(len_movie), nbr_channels)
101
104
 
102
- if mode=="timeseries":
103
-
104
- frames = load_frames(img_num_channels[0,frame_range[0]:frame_range[1]], stack_path, normalize_input=False)
105
- frames = np.moveaxis(frames, -1, 0).astype(float)
106
-
107
- for i in range(len(frames)):
108
- if np.all(frames[i].flatten()==0):
109
- frames[i,:,:] = np.nan
110
-
111
- frame_mean = np.nanmean(frames, axis=0)
112
-
113
- frame = frame_mean.copy().astype(float)
114
- std_frame = filter_image(frame.copy(),filters=activation_protocol)
115
- edge = estimate_unreliable_edge(activation_protocol)
116
- mask = threshold_image(std_frame, threshold_on_std, np.inf, foreground_value=1, edge_exclusion=edge)
117
- frame[np.where(mask.astype(int)==1)] = np.nan
118
-
119
- elif mode=="tiles":
105
+ if mode=="timeseries":
120
106
 
121
- frames = load_frames(img_num_channels[0,:], stack_path, normalize_input=False).astype(float)
122
- frames = np.moveaxis(frames, -1, 0).astype(float)
107
+ frames = load_frames(img_num_channels[0,frame_range[0]:frame_range[1]], stack_path, normalize_input=False)
108
+ frames = np.moveaxis(frames, -1, 0).astype(float)
123
109
 
124
- new_frames = []
125
- for i in range(len(frames)):
110
+ for i in range(len(frames)):
111
+ if np.all(frames[i].flatten()==0):
112
+ frames[i,:,:] = np.nan
126
113
 
127
- if np.all(frames[i].flatten()==0):
128
- empty_frame = np.zeros_like(frames[i])
129
- empty_frame[:,:] = np.nan
130
- new_frames.append(empty_frame)
131
- continue
132
-
133
- f = frames[i].copy()
134
- std_frame = filter_image(f.copy(),filters=activation_protocol)
114
+ frame_mean = np.nanmean(frames, axis=0)
115
+
116
+ frame = frame_mean.copy().astype(float)
117
+ std_frame = filter_image(frame.copy(),filters=activation_protocol)
135
118
  edge = estimate_unreliable_edge(activation_protocol)
136
119
  mask = threshold_image(std_frame, threshold_on_std, np.inf, foreground_value=1, edge_exclusion=edge)
137
- f[np.where(mask.astype(int)==1)] = np.nan
138
- new_frames.append(f.copy())
139
-
140
- frame = np.nanmedian(new_frames, axis=0)
120
+ frame[np.where(mask.astype(int)==1)] = np.nan
121
+
122
+ elif mode=="tiles":
123
+
124
+ frames = load_frames(img_num_channels[0,:], stack_path, normalize_input=False).astype(float)
125
+ frames = np.moveaxis(frames, -1, 0).astype(float)
126
+
127
+ new_frames = []
128
+ for i in range(len(frames)):
129
+
130
+ if np.all(frames[i].flatten()==0):
131
+ empty_frame = np.zeros_like(frames[i])
132
+ empty_frame[:,:] = np.nan
133
+ new_frames.append(empty_frame)
134
+ continue
135
+
136
+ f = frames[i].copy()
137
+ std_frame = filter_image(f.copy(),filters=activation_protocol)
138
+ edge = estimate_unreliable_edge(activation_protocol)
139
+ mask = threshold_image(std_frame, threshold_on_std, np.inf, foreground_value=1, edge_exclusion=edge)
140
+ f[np.where(mask.astype(int)==1)] = np.nan
141
+ new_frames.append(f.copy())
142
+
143
+ frame = np.nanmedian(new_frames, axis=0)
144
+ else:
145
+ print(f'Stack not found for position {pos_path}...')
146
+ frame = []
141
147
 
142
148
  # store
143
149
  frame_mean_per_position.append(frame)
144
150
 
145
- background = np.nanmedian(frame_mean_per_position,axis=0)
146
- backgrounds.append({"bg": background, "well": well_path})
147
- print(f"Background successfully computed for well {well_name}...")
151
+ try:
152
+ background = np.nanmedian(frame_mean_per_position,axis=0)
153
+ backgrounds.append({"bg": background, "well": well_path})
154
+ print(f"Background successfully computed for well {well_name}...")
155
+ except Exception as e:
156
+ print(e)
157
+ backgrounds.append(None)
148
158
 
149
159
  return backgrounds
150
160
 
@@ -266,28 +276,35 @@ def correct_background_model_free(
266
276
 
267
277
  stack_path = get_position_movie_path(pos_path, prefix=movie_prefix)
268
278
  print(f'Applying the correction to position {extract_position_name(pos_path)}...')
269
-
270
- corrected_stack = apply_background_to_stack(stack_path,
271
- background,
272
- target_channel_index=channel_indices[0],
273
- nbr_channels=nbr_channels,
274
- stack_length=len_movie,
275
- threshold_on_std=threshold_on_std,
276
- optimize_option=optimize_option,
277
- opt_coef_range=opt_coef_range,
278
- opt_coef_nbr=opt_coef_nbr,
279
- operation=operation,
280
- clip=clip,
281
- export=export,
282
- activation_protocol=activation_protocol,
283
- prefix=export_prefix,
284
- )
285
- print('Correction successful.')
286
- if return_stacks:
287
- stacks.append(corrected_stack)
279
+ if stack_path is not None:
280
+ len_movie_auto = auto_load_number_of_frames(stack_path)
281
+ if len_movie_auto is not None:
282
+ len_movie = len_movie_auto
283
+ img_num_channels = _get_img_num_per_channel(channel_indices, int(len_movie), nbr_channels)
284
+
285
+ corrected_stack = apply_background_to_stack(stack_path,
286
+ background,
287
+ target_channel_index=channel_indices[0],
288
+ nbr_channels=nbr_channels,
289
+ stack_length=len_movie,
290
+ threshold_on_std=threshold_on_std,
291
+ optimize_option=optimize_option,
292
+ opt_coef_range=opt_coef_range,
293
+ opt_coef_nbr=opt_coef_nbr,
294
+ operation=operation,
295
+ clip=clip,
296
+ export=export,
297
+ activation_protocol=activation_protocol,
298
+ prefix=export_prefix,
299
+ )
300
+ print('Correction successful.')
301
+ if return_stacks:
302
+ stacks.append(corrected_stack)
303
+ else:
304
+ del corrected_stack
305
+ collect()
288
306
  else:
289
- del corrected_stack
290
- collect()
307
+ stacks.append(None)
291
308
 
292
309
  if return_stacks:
293
310
  return stacks
@@ -770,7 +787,10 @@ def correct_background_model(
770
787
 
771
788
  stack_path = get_position_movie_path(pos_path, prefix=movie_prefix)
772
789
  print(f'Applying the correction to position {extract_position_name(pos_path)}...')
773
- print(stack_path)
790
+ len_movie_auto = auto_load_number_of_frames(stack_path)
791
+ if len_movie_auto is not None:
792
+ len_movie = len_movie_auto
793
+ img_num_channels = _get_img_num_per_channel(channel_indices, int(len_movie), nbr_channels)
774
794
 
775
795
  corrected_stack = fit_and_apply_model_background_to_stack(stack_path,
776
796
  target_channel_index=channel_indices[0],
@@ -20,6 +20,7 @@ from skimage.segmentation import watershed
20
20
  from skimage.feature import peak_local_max
21
21
  from skimage.measure import regionprops_table
22
22
  from skimage.exposure import match_histograms
23
+ from scipy.ndimage import zoom
23
24
  import pandas as pd
24
25
  import subprocess
25
26
 
@@ -27,7 +28,7 @@ import subprocess
27
28
  abs_path = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],'celldetective'])
28
29
 
29
30
  def segment(stack, model_name, channels=None, spatial_calibration=None, view_on_napari=False,
30
- use_gpu=True, time_flat_normalization=False, time_flat_percentiles=(0.0,99.99)):
31
+ use_gpu=True, channel_axis=-1):
31
32
 
32
33
  """
33
34
 
@@ -85,7 +86,10 @@ def segment(stack, model_name, channels=None, spatial_calibration=None, view_on_
85
86
  if not use_gpu:
86
87
  os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
87
88
  else:
88
- os.environ['CUDA_VISIBLE_DEVICES'] = '0'
89
+ os.environ['CUDA_VISIBLE_DEVICES'] = '0'
90
+
91
+ if channel_axis != -1:
92
+ stack = np.moveaxis(stack, channel_axis, -1)
89
93
 
90
94
  if channels is not None:
91
95
  assert len(channels)==stack.shape[-1],f'The channel names provided do not match with the expected number of channels in the stack: {stack.shape[-1]}.'
@@ -96,48 +100,83 @@ def segment(stack, model_name, channels=None, spatial_calibration=None, view_on_
96
100
  required_spatial_calibration = input_config['spatial_calibration']
97
101
  model_type = input_config['model_type']
98
102
 
99
- if 'normalize' in input_config:
100
- normalize = input_config['normalize']
101
- else:
102
- normalize = True
103
+ normalization_percentile = input_config['normalization_percentile']
104
+ normalization_clip = input_config['normalization_clip']
105
+ normalization_values = input_config['normalization_values']
103
106
 
104
107
  if model_type=='cellpose':
105
108
  diameter = input_config['diameter']
106
- if diameter!=30:
107
- required_spatial_calibration = None
109
+ # if diameter!=30:
110
+ # required_spatial_calibration = None
108
111
  cellprob_threshold = input_config['cellprob_threshold']
109
112
  flow_threshold = input_config['flow_threshold']
110
113
 
111
114
  scale = _estimate_scale_factor(spatial_calibration, required_spatial_calibration)
112
115
 
113
116
  if model_type=='stardist':
117
+
114
118
  model = StarDist2D(None, name=model_name, basedir=Path(model_path).parent)
115
- print(f"StarDist model {model_name} successfully loaded")
119
+ model.config.use_gpu = use_gpu
120
+ model.use_gpu = use_gpu
121
+ print(f"StarDist model {model_name} successfully loaded.")
122
+ scale_model = scale
116
123
 
117
124
  elif model_type=='cellpose':
118
- model = CellposeModel(gpu=use_gpu, pretrained_model=model_path+model_path.split('/')[-2], diam_mean=30.0)
125
+
126
+ import torch
127
+ if not use_gpu:
128
+ device = torch.device("cpu")
129
+ else:
130
+ device = torch.device("cuda")
131
+
132
+ model = CellposeModel(gpu=use_gpu, device=device, pretrained_model=model_path+model_path.split('/')[-2], model_type=None, nchan=len(required_channels))
119
133
  if scale is None:
120
134
  scale_model = model.diam_mean / model.diam_labels
121
135
  else:
122
136
  scale_model = scale * model.diam_mean / model.diam_labels
137
+ print(f"Diam mean: {model.diam_mean}; Diam labels: {model.diam_labels}; Final rescaling: {scale_model}...")
138
+ print(f'Cellpose model {model_name} successfully loaded.')
123
139
 
124
140
  labels = []
125
- if (time_flat_normalization)*normalize:
126
- normalization_values = get_stack_normalization_values(stack[:,:,:,channel_indices], percentiles=time_flat_percentiles)
127
- else:
128
- normalization_values = [None]*len(channel_indices)
129
141
 
130
142
  for t in tqdm(range(len(stack)),desc="frame"):
131
143
 
132
144
  # normalize
133
- frame = stack[t,:,:,np.array(channel_indices)]
134
- if np.argmin(frame.shape)!=(frame.ndim-1):
135
- frame = np.moveaxis(frame,np.argmin(frame.shape),-1)
136
- if normalize:
137
- frame = normalize_multichannel(frame, values=normalization_values)
138
-
139
- if scale is not None:
140
- frame = [ndi.zoom(frame[:,:,c].copy(), [scale_model,scale_model], order=3, prefilter=False) for c in range(frame.shape[-1])]
145
+ channel_indices = np.array(channel_indices)
146
+ none_channel_indices = np.where(channel_indices==None)[0]
147
+ channel_indices[channel_indices==None] = 0
148
+ print(channel_indices)
149
+
150
+ frame = stack[t,:,:,channel_indices.astype(int)].astype(float)
151
+ if frame.ndim==2:
152
+ frame = frame[:,:,np.newaxis]
153
+ if frame.ndim==3 and np.array(frame.shape).argmin()==0:
154
+ frame = np.moveaxis(frame,0,-1)
155
+ template = frame.copy()
156
+
157
+ values = []
158
+ percentiles = []
159
+ for k in range(len(normalization_percentile)):
160
+ if normalization_percentile[k]:
161
+ percentiles.append(normalization_values[k])
162
+ values.append(None)
163
+ else:
164
+ percentiles.append(None)
165
+ values.append(normalization_values[k])
166
+
167
+ frame = normalize_multichannel(frame, **{"percentiles": percentiles, 'values': values, 'clip': normalization_clip})
168
+
169
+ if scale_model is not None:
170
+ frame = [zoom(frame[:,:,c].copy(), [scale_model,scale_model], order=3, prefilter=False) for c in range(frame.shape[-1])]
171
+ frame = np.moveaxis(frame,0,-1)
172
+
173
+ for k in range(frame.shape[2]):
174
+ unique_values = np.unique(frame[:,:,k])
175
+ if len(unique_values)==1:
176
+ frame[0,0,k] += 1
177
+
178
+ frame = np.moveaxis([interpolate_nan(frame[:,:,c].copy()) for c in range(frame.shape[-1])],0,-1)
179
+ frame[:,:,none_channel_indices] = 0.
141
180
 
142
181
  if model_type=="stardist":
143
182
 
@@ -145,16 +184,15 @@ def segment(stack, model_name, channels=None, spatial_calibration=None, view_on_
145
184
  Y_pred = Y_pred.astype(np.uint16)
146
185
 
147
186
  elif model_type=="cellpose":
148
-
149
- Y_pred, _, _ = model.eval(frame, diameter = diameter, cellprob_threshold=cellprob_threshold, flow_threshold=flow_threshold, channels=None, normalize=False)
187
+
188
+ img = np.moveaxis(frame, -1, 0)
189
+ Y_pred, _, _ = model.eval(img, diameter = diameter, cellprob_threshold=cellprob_threshold, flow_threshold=flow_threshold, channels=None, normalize=False)
150
190
  Y_pred = Y_pred.astype(np.uint16)
151
191
 
152
- if scale is not None:
153
- Y_pred = ndi.zoom(Y_pred, [1./scale_model,1./scale_model],order=0)
154
-
155
-
156
192
  if Y_pred.shape != stack[0].shape[:2]:
157
- Y_pred = resize(Y_pred, stack[0].shape, order=0)
193
+ Y_pred = zoom(Y_pred, [1./scale_model,1./scale_model],order=0)
194
+ if Y_pred.shape != template.shape[:2]:
195
+ Y_pred = resize(Y_pred, template.shape[:2], order=0)
158
196
 
159
197
  labels.append(Y_pred)
160
198
 
celldetective/utils.py CHANGED
@@ -23,6 +23,7 @@ from tqdm import tqdm
23
23
  import shutil
24
24
  import tempfile
25
25
  from scipy.interpolate import griddata
26
+ import re
26
27
 
27
28
 
28
29
  def derivative(x, timeline, window, mode='bi'):
@@ -430,6 +431,31 @@ def mask_edges(binary_mask, border_size):
430
431
  return binary_mask
431
432
 
432
433
 
434
+ def extract_cols_from_query(query: str):
435
+
436
+ # Track variables in a dictionary to be used as a dictionary of globals. From: https://stackoverflow.com/questions/64576913/extract-pandas-dataframe-column-names-from-query-string
437
+
438
+ variables = {}
439
+
440
+ while True:
441
+ try:
442
+ # Try creating a Expr object with the query string and dictionary of globals.
443
+ # This will raise an error as long as the dictionary of globals is incomplete.
444
+ env = pd.core.computation.scope.ensure_scope(level=0, global_dict=variables)
445
+ pd.core.computation.eval.Expr(query, env=env)
446
+
447
+ # Exit the loop when evaluation is successful.
448
+ break
449
+ except pd.errors.UndefinedVariableError as e:
450
+ # This relies on the format defined here: https://github.com/pandas-dev/pandas/blob/965ceca9fd796940050d6fc817707bba1c4f9bff/pandas/errors/__init__.py#L401
451
+ name = re.findall("name '(.+?)' is not defined", str(e))[0]
452
+
453
+ # Add the name to the globals dictionary with a dummy value.
454
+ variables[name] = None
455
+
456
+ return list(variables.keys())
457
+
458
+
433
459
  def create_patch_mask(h, w, center=None, radius=None):
434
460
 
435
461
  """
@@ -1058,20 +1084,33 @@ def _extract_channel_indices(channels, required_channels):
1058
1084
  # [0, 1]
1059
1085
  """
1060
1086
 
1061
- if channels is not None:
1062
- channel_indices = []
1063
- for ch in required_channels:
1064
-
1087
+ channel_indices = []
1088
+ for c in required_channels:
1089
+ if c!='None' and c is not None:
1065
1090
  try:
1066
- idx = channels.index(ch)
1067
- except ValueError:
1068
- print('Mismatch between the channels required by the model and the provided channels.')
1069
- return None
1091
+ ch_idx = channels.index(c)
1092
+ channel_indices.append(ch_idx)
1093
+ except Exception as e:
1094
+ print(f"Error {e}. The channel required by the model is not available in your data... Check the configuration file.")
1095
+ channels = None
1096
+ break
1097
+ else:
1098
+ channel_indices.append(None)
1070
1099
 
1071
- channel_indices.append(idx)
1072
- channel_indices = np.array(channel_indices)
1073
- else:
1074
- channel_indices = np.arange(len(required_channels))
1100
+ # if channels is not None:
1101
+ # channel_indices = []
1102
+ # for ch in required_channels:
1103
+
1104
+ # try:
1105
+ # idx = channels.index(ch)
1106
+ # except ValueError:
1107
+ # print('Mismatch between the channels required by the model and the provided channels.')
1108
+ # return None
1109
+
1110
+ # channel_indices.append(idx)
1111
+ # channel_indices = np.array(channel_indices)
1112
+ # else:
1113
+ # channel_indices = np.arange(len(required_channels))
1075
1114
 
1076
1115
  return channel_indices
1077
1116
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: celldetective
3
- Version: 1.1.1.post3
3
+ Version: 1.1.1.post4
4
4
  Summary: description
5
5
  Home-page: http://github.com/remyeltorro/celldetective
6
6
  Author: Rémy Torro