celldetective 1.3.8.post1__py3-none-any.whl → 1.3.9.post1__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 (31) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/extra_properties.py +113 -17
  3. celldetective/filters.py +12 -12
  4. celldetective/gui/btrack_options.py +1 -1
  5. celldetective/gui/control_panel.py +1 -1
  6. celldetective/gui/gui_utils.py +4 -4
  7. celldetective/gui/measurement_options.py +1 -1
  8. celldetective/gui/plot_signals_ui.py +23 -6
  9. celldetective/gui/process_block.py +1 -1
  10. celldetective/gui/processes/measure_cells.py +4 -4
  11. celldetective/gui/processes/segment_cells.py +3 -3
  12. celldetective/gui/processes/track_cells.py +4 -4
  13. celldetective/gui/signal_annotator.py +26 -6
  14. celldetective/gui/signal_annotator2.py +1 -1
  15. celldetective/gui/signal_annotator_options.py +1 -1
  16. celldetective/gui/survival_ui.py +4 -1
  17. celldetective/gui/thresholds_gui.py +6 -5
  18. celldetective/io.py +1 -44
  19. celldetective/measure.py +22 -16
  20. celldetective/regionprops/__init__.py +1 -0
  21. celldetective/regionprops/_regionprops.py +310 -0
  22. celldetective/regionprops/props.json +63 -0
  23. celldetective/scripts/measure_relative.py +2 -20
  24. celldetective/segmentation.py +14 -4
  25. celldetective/utils.py +182 -171
  26. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.post1.dist-info}/METADATA +1 -1
  27. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.post1.dist-info}/RECORD +31 -28
  28. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.post1.dist-info}/LICENSE +0 -0
  29. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.post1.dist-info}/WHEEL +0 -0
  30. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.post1.dist-info}/entry_points.txt +0 -0
  31. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.post1.dist-info}/top_level.txt +0 -0
celldetective/measure.py CHANGED
@@ -6,7 +6,7 @@ from sklearn.metrics import r2_score
6
6
  from scipy.optimize import curve_fit
7
7
  from scipy import ndimage
8
8
  from tqdm import tqdm
9
- from skimage.measure import regionprops_table
9
+ #from skimage.measure import regionprops_table
10
10
  from functools import reduce
11
11
  from mahotas.features import haralick
12
12
  from scipy.ndimage import zoom
@@ -18,18 +18,20 @@ from skimage.draw import disk as dsk
18
18
  from skimage.feature import blob_dog, blob_log
19
19
 
20
20
  from celldetective.utils import rename_intensity_column, create_patch_mask, remove_redundant_features, \
21
- remove_trajectory_measurements, contour_of_instance_segmentation, extract_cols_from_query, step_function, interpolate_nan
21
+ remove_trajectory_measurements, contour_of_instance_segmentation, extract_cols_from_query, step_function, interpolate_nan, _remove_invalid_cols
22
22
  from celldetective.preprocessing import field_correction
23
- import celldetective.extra_properties as extra_properties
24
23
  from celldetective.extra_properties import *
25
24
  from inspect import getmembers, isfunction
26
25
  from skimage.morphology import disk
27
26
  from scipy.signal import find_peaks, peak_widths
28
27
 
29
28
  from celldetective.segmentation import filter_image
29
+ from celldetective.regionprops import regionprops_table
30
30
 
31
31
  abs_path = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], 'celldetective'])
32
32
 
33
+
34
+
33
35
  def measure(stack=None, labels=None, trajectories=None, channel_names=None,
34
36
  features=None, intensity_measurement_radii=None, isotropic_operations=['mean'], border_distances=None,
35
37
  haralick_options=None, column_labels={'track': "TRACK_ID", 'time': 'FRAME', 'x': 'POSITION_X', 'y': 'POSITION_Y'}, clear_previous=False):
@@ -212,6 +214,7 @@ def measure(stack=None, labels=None, trajectories=None, channel_names=None,
212
214
  measurements['ID'] = np.arange(len(df))
213
215
 
214
216
  measurements = measurements.reset_index(drop=True)
217
+ measurements = _remove_invalid_cols(measurements)
215
218
 
216
219
  return measurements
217
220
 
@@ -361,27 +364,29 @@ def measure_features(img, label, features=['area', 'intensity_mean'], channels=N
361
364
  else:
362
365
  corrected_image = field_correction(img[:,:,ind].copy(), threshold_on_std=norm['threshold_on_std'], operation=norm['operation'], model=norm['model'], clip=norm['clip'])
363
366
  img[:, :, ind] = corrected_image
367
+
368
+ import celldetective.extra_properties as extra_props
364
369
 
365
- extra_props = getmembers(extra_properties, isfunction)
366
- extra_props = [extra_props[i][0] for i in range(len(extra_props))]
370
+ extra = getmembers(extra_props, isfunction)
371
+ extra = [extra[i][0] for i in range(len(extra))]
367
372
 
368
373
  extra_props_list = []
369
374
  feats = features.copy()
370
375
  for f in features:
371
- if f in extra_props:
376
+ if f in extra:
372
377
  feats.remove(f)
373
- extra_props_list.append(getattr(extra_properties, f))
378
+ extra_props_list.append(getattr(extra_props, f))
374
379
 
375
380
  # Add intensity nan mean if need to measure mean intensities
376
381
  if measure_mean_intensities:
377
- extra_props_list.append(getattr(extra_properties, 'intensity_nanmean'))
382
+ extra_props_list.append(getattr(extra_props, 'intensity_nanmean'))
378
383
 
379
384
  if len(extra_props_list) == 0:
380
385
  extra_props_list = None
381
386
  else:
382
387
  extra_props_list = tuple(extra_props_list)
383
388
 
384
- props = regionprops_table(label, intensity_image=img, properties=feats, extra_properties=extra_props_list)
389
+ props = regionprops_table(label, intensity_image=img, properties=feats, extra_properties=extra_props_list, channel_names=channels)
385
390
  df_props = pd.DataFrame(props)
386
391
  if spot_detection is not None:
387
392
  if df_spots is not None:
@@ -395,8 +400,8 @@ def measure_features(img, label, features=['area', 'intensity_mean'], channels=N
395
400
  intensity_features = list(np.array(features)[np.array(intensity_features_test)])
396
401
  intensity_extra = []
397
402
  for s in intensity_features:
398
- if s in extra_props:
399
- intensity_extra.append(getattr(extra_properties, s))
403
+ if s in extra:
404
+ intensity_extra.append(getattr(extra_props, s))
400
405
  intensity_features.remove(s)
401
406
 
402
407
  if len(intensity_features) == 0:
@@ -407,13 +412,13 @@ def measure_features(img, label, features=['area', 'intensity_mean'], channels=N
407
412
 
408
413
  new_intensity_features = intensity_features.copy()
409
414
  for int_feat in intensity_features:
410
- if int_feat in extra_props:
415
+ if int_feat in extra:
411
416
  new_intensity_features.remove(int_feat)
412
417
  intensity_features = new_intensity_features
413
418
 
414
419
  if (isinstance(border_dist, int) or isinstance(border_dist, float)):
415
420
  border_label = contour_of_instance_segmentation(label, border_dist)
416
- props_border = regionprops_table(border_label, intensity_image=img, properties=intensity_features)
421
+ props_border = regionprops_table(border_label, intensity_image=img, properties=intensity_features, channel_names=channels)
417
422
  df_props_border = pd.DataFrame(props_border)
418
423
  for c in df_props_border.columns:
419
424
  if 'intensity' in c:
@@ -423,7 +428,7 @@ def measure_features(img, label, features=['area', 'intensity_mean'], channels=N
423
428
  df_props_border_list = []
424
429
  for d in border_dist:
425
430
  border_label = contour_of_instance_segmentation(label, d)
426
- props_border = regionprops_table(border_label, intensity_image=img, properties=intensity_features)
431
+ props_border = regionprops_table(border_label, intensity_image=img, properties=intensity_features, channel_names=channels)
427
432
  df_props_border_d = pd.DataFrame(props_border)
428
433
  for c in df_props_border_d.columns:
429
434
  if 'intensity' in c:
@@ -832,16 +837,17 @@ def local_normalisation(image, labels, background_intensity, measurement='intens
832
837
 
833
838
  def normalise_by_cell(image, labels, distance=5, model='median', operation='subtract', clip=False):
834
839
 
840
+ import celldetective.extra_properties as extra_props
835
841
 
836
842
  border = contour_of_instance_segmentation(label=labels, distance=distance * (-1))
837
843
  if model == 'mean':
838
844
  measurement = 'intensity_nanmean'
839
- extra_props = [getattr(extra_properties, measurement)]
845
+ extra_props = [getattr(extra_props, measurement)]
840
846
  background_intensity = regionprops_table(intensity_image=image, label_image=border,
841
847
  extra_properties=extra_props)
842
848
  elif model == 'median':
843
849
  measurement = 'intensity_median'
844
- extra_props = [getattr(extra_properties, measurement)]
850
+ extra_props = [getattr(extra_props, measurement)]
845
851
  background_intensity = regionprops_table(intensity_image=image, label_image=border,
846
852
  extra_properties=extra_props)
847
853
 
@@ -0,0 +1 @@
1
+ from ._regionprops import regionprops_table
@@ -0,0 +1,310 @@
1
+ from skimage.measure._regionprops import RegionProperties, regionprops, _cached, _props_to_dict, _infer_number_of_required_args
2
+ import numpy as np
3
+ import inspect
4
+ import json
5
+ import os
6
+ from scipy.ndimage import find_objects
7
+
8
+ abs_path = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]])
9
+
10
+ with open(os.sep.join([abs_path, 'regionprops', 'props.json'])) as f:
11
+ PROPS = json.load(f)
12
+
13
+ COL_DTYPES = {
14
+ 'area': float,
15
+ 'area_bbox': float,
16
+ 'area_convex': float,
17
+ 'area_filled': float,
18
+ 'axis_major_length': float,
19
+ 'axis_minor_length': float,
20
+ 'bbox': int,
21
+ 'centroid': float,
22
+ 'centroid_local': float,
23
+ 'centroid_weighted': float,
24
+ 'centroid_weighted_local': float,
25
+ 'coords': object,
26
+ 'coords_scaled': object,
27
+ 'eccentricity': float,
28
+ 'equivalent_diameter_area': float,
29
+ 'euler_number': int,
30
+ 'extent': float,
31
+ 'feret_diameter_max': float,
32
+ 'image': object,
33
+ 'image_convex': object,
34
+ 'image_filled': object,
35
+ 'image_intensity': object,
36
+ 'inertia_tensor': float,
37
+ 'inertia_tensor_eigvals': float,
38
+ 'intensity_max': float,
39
+ 'intensity_mean': float,
40
+ 'intensity_min': float,
41
+ 'intensity_std': float,
42
+ 'label': int,
43
+ 'moments': float,
44
+ 'moments_central': float,
45
+ 'moments_hu': float,
46
+ 'moments_normalized': float,
47
+ 'moments_weighted': float,
48
+ 'moments_weighted_central': float,
49
+ 'moments_weighted_hu': float,
50
+ 'moments_weighted_normalized': float,
51
+ 'num_pixels': int,
52
+ 'orientation': float,
53
+ 'perimeter': float,
54
+ 'perimeter_crofton': float,
55
+ 'slice': object,
56
+ 'solidity': float,
57
+ }
58
+
59
+ OBJECT_COLUMNS = [col for col, dtype in COL_DTYPES.items() if dtype == object]
60
+ PROP_VALS = set(PROPS.values())
61
+
62
+ class CustomRegionProps(RegionProperties):
63
+
64
+ """
65
+ From https://github.com/scikit-image/scikit-image/blob/main/skimage/measure/_regionprops.py with a modification to not mask the intensity image itself before measurements
66
+ """
67
+
68
+ def __init__(self, channel_names, *args, **kwargs):
69
+
70
+ self.channel_names = channel_names
71
+ if isinstance(self.channel_names, np.ndarray):
72
+ self.channel_names = list(self.channel_names)
73
+ super().__init__(*args, **kwargs)
74
+
75
+
76
+ def __getattr__(self, attr):
77
+
78
+ if self.channel_names is not None and self._multichannel:
79
+ assert len(self.channel_names)==self._intensity_image.shape[-1],'Mismatch between provided channel names and the number of channels in the image...'
80
+
81
+ if attr == "__setstate__":
82
+ # When deserializing this object with pickle, `__setstate__`
83
+ # is accessed before any other attributes like `self._intensity_image`
84
+ # are available which leads to a RecursionError when trying to
85
+ # access them later on in this function. So guard against this by
86
+ # provoking the default AttributeError (gh-6465).
87
+ return self.__getattribute__(attr)
88
+
89
+ if self._intensity_image is None and attr in _require_intensity_image:
90
+ raise AttributeError(
91
+ f"Attribute '{attr}' unavailable when `intensity_image` "
92
+ f"has not been specified."
93
+ )
94
+ if attr in self._extra_properties:
95
+ func = self._extra_properties[attr]
96
+ n_args = _infer_number_of_required_args(func)
97
+ # determine whether func requires intensity image
98
+ if n_args == 2:
99
+ if self._intensity_image is not None:
100
+ if self._multichannel:
101
+ arg_dict = dict(inspect.signature(func).parameters)
102
+ if self.channel_names is not None and 'target_channel' in arg_dict:
103
+ multichannel_list = [np.nan for i in range(self.image_intensity.shape[-1])]
104
+ default_channel = arg_dict['target_channel']._default
105
+ if default_channel in self.channel_names:
106
+ idx = self.channel_names.index(default_channel)
107
+ multichannel_list[idx] = func(self.image, self.image_intensity[..., idx])
108
+ else:
109
+ print(f'Warning... Channel required by custom measurement ({default_channel}) could not be found in your data...')
110
+ return np.stack(multichannel_list, axis=-1)
111
+ else:
112
+ multichannel_list = [
113
+ func(self.image, self.image_intensity[..., i])
114
+ for i in range(self.image_intensity.shape[-1])
115
+ ]
116
+ return np.stack(multichannel_list, axis=-1)
117
+ else:
118
+ return func(self.image, self.image_intensity)
119
+ else:
120
+ raise AttributeError(
121
+ f'intensity image required to calculate {attr}'
122
+ )
123
+ elif n_args == 1:
124
+ return func(self.image)
125
+ else:
126
+ raise AttributeError(
127
+ f'Custom regionprop function\'s number of arguments must '
128
+ f'be 1 or 2, but {attr} takes {n_args} arguments.'
129
+ )
130
+ elif attr in PROPS and attr.lower() == attr:
131
+ if (
132
+ self._intensity_image is None
133
+ and PROPS[attr] in _require_intensity_image
134
+ ):
135
+ raise AttributeError(
136
+ f"Attribute '{attr}' unavailable when `intensity_image` "
137
+ f"has not been specified."
138
+ )
139
+ # retrieve deprecated property (excluding old CamelCase ones)
140
+ return getattr(self, PROPS[attr])
141
+ else:
142
+ raise AttributeError(f"'{type(self)}' object has no attribute '{attr}'")
143
+
144
+ @property
145
+ @_cached
146
+ def image_intensity(self):
147
+ if self._intensity_image is None:
148
+ raise AttributeError('No intensity image specified.')
149
+ image = (
150
+ self.image
151
+ if not self._multichannel
152
+ else np.expand_dims(self.image, self._ndim)
153
+ )
154
+ return self._intensity_image[self.slice]
155
+
156
+ def regionprops(label_image, intensity_image=None,cache=True,channel_names=None,*,extra_properties=None,spacing=None,offset=None):
157
+
158
+ """
159
+ From https://github.com/scikit-image/scikit-image/blob/main/skimage/measure/_regionprops.py with a modification to use CustomRegionProps
160
+ """
161
+
162
+ if label_image.ndim not in (2, 3):
163
+ raise TypeError('Only 2-D and 3-D images supported.')
164
+
165
+ if not np.issubdtype(label_image.dtype, np.integer):
166
+ if np.issubdtype(label_image.dtype, bool):
167
+ raise TypeError(
168
+ 'Non-integer image types are ambiguous: '
169
+ 'use skimage.measure.label to label the connected '
170
+ 'components of label_image, '
171
+ 'or label_image.astype(np.uint8) to interpret '
172
+ 'the True values as a single label.'
173
+ )
174
+ else:
175
+ raise TypeError('Non-integer label_image types are ambiguous')
176
+
177
+ if offset is None:
178
+ offset_arr = np.zeros((label_image.ndim,), dtype=int)
179
+ else:
180
+ offset_arr = np.asarray(offset)
181
+ if offset_arr.ndim != 1 or offset_arr.size != label_image.ndim:
182
+ raise ValueError(
183
+ 'Offset should be an array-like of integers '
184
+ 'of shape (label_image.ndim,); '
185
+ f'{offset} was provided.'
186
+ )
187
+
188
+ regions = []
189
+
190
+ objects = find_objects(label_image)
191
+ for i, sl in enumerate(objects):
192
+ if sl is None:
193
+ continue
194
+
195
+ label = i + 1
196
+
197
+ props = CustomRegionProps(
198
+ channel_names,
199
+ sl,
200
+ label,
201
+ label_image,
202
+ intensity_image,
203
+ cache,
204
+ spacing=spacing,
205
+ extra_properties=extra_properties,
206
+ offset=offset_arr,
207
+ )
208
+ regions.append(props)
209
+
210
+ return regions
211
+
212
+
213
+ def _props_to_dict(regions, properties=('label', 'bbox'), separator='-'):
214
+
215
+
216
+ out = {}
217
+ n = len(regions)
218
+ for prop in properties:
219
+ r = regions[0]
220
+ # Copy the original property name so the output will have the
221
+ # user-provided property name in the case of deprecated names.
222
+ orig_prop = prop
223
+ # determine the current property name for any deprecated property.
224
+ prop = PROPS.get(prop, prop)
225
+ rp = getattr(r, prop)
226
+ if prop in COL_DTYPES:
227
+ dtype = COL_DTYPES[prop]
228
+ else:
229
+ func = r._extra_properties[prop]
230
+ # dtype = _infer_regionprop_dtype(
231
+ # func,
232
+ # intensity=r._intensity_image is not None,
233
+ # ndim=r.image.ndim,
234
+ # )
235
+ dtype = np.float64
236
+
237
+ # scalars and objects are dedicated one column per prop
238
+ # array properties are raveled into multiple columns
239
+ # for more info, refer to notes 1
240
+ if np.isscalar(rp) or prop in OBJECT_COLUMNS or dtype is np.object_:
241
+ column_buffer = np.empty(n, dtype=dtype)
242
+ for i in range(n):
243
+ column_buffer[i] = regions[i][prop]
244
+ out[orig_prop] = np.copy(column_buffer)
245
+ else:
246
+ # precompute property column names and locations
247
+ modified_props = []
248
+ locs = []
249
+ for ind in np.ndindex(np.shape(rp)):
250
+ modified_props.append(separator.join(map(str, (orig_prop,) + ind)))
251
+ locs.append(ind if len(ind) > 1 else ind[0])
252
+
253
+ # fill temporary column data_array
254
+ n_columns = len(locs)
255
+ column_data = np.empty((n, n_columns), dtype=dtype)
256
+ for k in range(n):
257
+ # we coerce to a numpy array to ensure structures like
258
+ # tuple-of-arrays expand correctly into columns
259
+ rp = np.asarray(regions[k][prop])
260
+ for i, loc in enumerate(locs):
261
+ column_data[k, i] = rp[loc]
262
+
263
+ # add the columns to the output dictionary
264
+ for i, modified_prop in enumerate(modified_props):
265
+ out[modified_prop] = column_data[:, i]
266
+ return out
267
+
268
+
269
+ def regionprops_table(label_image,intensity_image=None,properties=('label', 'bbox'),*,cache=True,separator='-',extra_properties=None,spacing=None,channel_names=None):
270
+
271
+ """
272
+ From https://github.com/scikit-image/scikit-image/blob/main/skimage/measure/_regionprops.py
273
+ """
274
+ regions = regionprops(
275
+ label_image,
276
+ intensity_image=intensity_image,
277
+ cache=cache,
278
+ extra_properties=extra_properties,
279
+ spacing=spacing,
280
+ channel_names=channel_names,
281
+ )
282
+ if extra_properties is not None:
283
+ properties = list(properties) + [prop.__name__ for prop in extra_properties]
284
+ if len(regions) == 0:
285
+ ndim = label_image.ndim
286
+ label_image = np.zeros((3,) * ndim, dtype=int)
287
+ label_image[(1,) * ndim] = 1
288
+ if intensity_image is not None:
289
+ intensity_image = np.zeros(
290
+ label_image.shape + intensity_image.shape[ndim:],
291
+ dtype=intensity_image.dtype,
292
+ )
293
+ regions = regionprops(
294
+ label_image,
295
+ intensity_image=intensity_image,
296
+ cache=cache,
297
+ extra_properties=extra_properties,
298
+ spacing=spacing,
299
+ channel_names=channel_names,
300
+ )
301
+ out_d = _props_to_dict(regions, properties=properties, separator=separator)
302
+ return {k: v[:0] for k, v in out_d.items()}
303
+
304
+ good_props = []
305
+ for prop in properties:
306
+ nan_test = [np.isnan(getattr(r,prop)) for r in regions]
307
+ if not np.all(nan_test):
308
+ good_props.append(prop)
309
+
310
+ return _props_to_dict(regions, properties=good_props, separator=separator)
@@ -0,0 +1,63 @@
1
+ {
2
+ "Area": "area",
3
+ "BoundingBox": "bbox",
4
+ "BoundingBoxArea": "area_bbox",
5
+ "bbox_area": "area_bbox",
6
+ "CentralMoments": "moments_central",
7
+ "Centroid": "centroid",
8
+ "ConvexArea": "area_convex",
9
+ "convex_area": "area_convex",
10
+ "ConvexImage": "image_convex",
11
+ "convex_image": "image_convex",
12
+ "Coordinates": "coords",
13
+ "Eccentricity": "eccentricity",
14
+ "EquivDiameter": "equivalent_diameter_area",
15
+ "equivalent_diameter": "equivalent_diameter_area",
16
+ "EulerNumber": "euler_number",
17
+ "Extent": "extent",
18
+ "FeretDiameter": "feret_diameter_max",
19
+ "FeretDiameterMax": "feret_diameter_max",
20
+ "FilledArea": "area_filled",
21
+ "filled_area": "area_filled",
22
+ "FilledImage": "image_filled",
23
+ "filled_image": "image_filled",
24
+ "HuMoments": "moments_hu",
25
+ "Image": "image",
26
+ "InertiaTensor": "inertia_tensor",
27
+ "InertiaTensorEigvals": "inertia_tensor_eigvals",
28
+ "IntensityImage": "image_intensity",
29
+ "intensity_image": "image_intensity",
30
+ "Label": "label",
31
+ "LocalCentroid": "centroid_local",
32
+ "local_centroid": "centroid_local",
33
+ "MajorAxisLength": "axis_major_length",
34
+ "major_axis_length": "axis_major_length",
35
+ "MaxIntensity": "intensity_max",
36
+ "max_intensity": "intensity_max",
37
+ "MeanIntensity": "intensity_mean",
38
+ "mean_intensity": "intensity_mean",
39
+ "MinIntensity": "intensity_min",
40
+ "min_intensity": "intensity_min",
41
+ "std_intensity": "intensity_std",
42
+ "MinorAxisLength": "axis_minor_length",
43
+ "minor_axis_length": "axis_minor_length",
44
+ "Moments": "moments",
45
+ "NormalizedMoments": "moments_normalized",
46
+ "Orientation": "orientation",
47
+ "Perimeter": "perimeter",
48
+ "CroftonPerimeter": "perimeter_crofton",
49
+ "Slice": "slice",
50
+ "Solidity": "solidity",
51
+ "WeightedCentralMoments": "moments_weighted_central",
52
+ "weighted_moments_central": "moments_weighted_central",
53
+ "WeightedCentroid": "centroid_weighted",
54
+ "weighted_centroid": "centroid_weighted",
55
+ "WeightedHuMoments": "moments_weighted_hu",
56
+ "weighted_moments_hu": "moments_weighted_hu",
57
+ "WeightedLocalCentroid": "centroid_weighted_local",
58
+ "weighted_local_centroid": "centroid_weighted_local",
59
+ "WeightedMoments": "moments_weighted",
60
+ "weighted_moments": "moments_weighted",
61
+ "WeightedNormalizedMoments": "moments_weighted_normalized",
62
+ "weighted_moments_normalized": "moments_weighted_normalized"
63
+ }
@@ -34,36 +34,18 @@ movie_prefix = ConfigSectionMap(config, "MovieSettings")["movie_prefix"]
34
34
  spatial_calibration = float(ConfigSectionMap(config, "MovieSettings")["pxtoum"])
35
35
  time_calibration = float(ConfigSectionMap(config, "MovieSettings")["frametomin"])
36
36
  len_movie = float(ConfigSectionMap(config, "MovieSettings")["len_movie"])
37
- channel_names, channel_inneigh_protocoles = extract_experiment_channels(config)
37
+ channel_names, channel_indices = extract_experiment_channels(expfolder)
38
38
  nbr_channels = len(channel_names)
39
39
 
40
40
  # from tracking instructions, fetch btrack config, features, haralick, clean_traj, idea: fetch custom timeline?
41
41
  instr_path = PurePath(expfolder, Path(f"{instruction_file}"))
42
42
  previous_pair_table_path = pos + os.sep.join(['output', 'tables', 'trajectories_pairs.csv'])
43
43
 
44
- # if os.path.exists(instr_path):
45
- # print(f"Neighborhood instructions has been successfully located.")
46
- # with open(instr_path, 'r') as f:
47
- # instructions = json.load(f)
48
- # print("Reading the following instructions: ", instructions)
49
-
50
- # if 'distance' in instructions:
51
- # distance = instructions['distance'][0]
52
- # else:
53
- # distance = None
54
- # else:
55
- # print('No measurement instructions found')
56
- # os.abort()
57
44
 
58
45
  previous_neighborhoods = []
59
46
  associated_reference_population = []
60
47
 
61
- # if distance is None:
62
- # print('No measurement could be performed. Check your inputs.')
63
- # print('Done.')
64
- # os.abort()
65
- # #distance = 0
66
- # else:
48
+
67
49
  neighborhoods_to_measure = extract_neighborhoods_from_pickles(pos)
68
50
  all_df_pairs = []
69
51
  if os.path.exists(previous_pair_table_path):
@@ -229,7 +229,7 @@ def segment_from_thresholds(stack, target_channel=0, thresholds=None, view_on_na
229
229
  return masks
230
230
 
231
231
  def segment_frame_from_thresholds(frame, target_channel=0, thresholds=None, equalize_reference=None,
232
- filters=None, marker_min_distance=30, marker_footprint_size=20, marker_footprint=None, feature_queries=None, channel_names=None, do_watershed=True):
232
+ filters=None, marker_min_distance=30, marker_footprint_size=20, marker_footprint=None, feature_queries=None, channel_names=None, do_watershed=True, edge_exclusion=True, fill_holes=True):
233
233
 
234
234
  """
235
235
  Segments objects within a single frame based on intensity thresholds and optional image processing steps.
@@ -269,14 +269,24 @@ def segment_frame_from_thresholds(frame, target_channel=0, thresholds=None, equa
269
269
 
270
270
  """
271
271
 
272
+ if frame.ndim==2:
273
+ frame = frame[:,:,np.newaxis]
272
274
  img = frame[:,:,target_channel]
273
- img = interpolate_nan(img)
275
+
276
+ if np.any(img!=img):
277
+ img = interpolate_nan(img)
278
+
274
279
  if equalize_reference is not None:
275
280
  img = match_histograms(img, equalize_reference)
281
+
276
282
  img_mc = frame.copy()
277
283
  img = filter_image(img, filters=filters)
278
- edge = estimate_unreliable_edge(filters)
279
- binary_image = threshold_image(img, thresholds[0], thresholds[1], fill_holes=True, edge_exclusion=edge)
284
+ if edge_exclusion:
285
+ edge = estimate_unreliable_edge(filters)
286
+ else:
287
+ edge = None
288
+
289
+ binary_image = threshold_image(img, thresholds[0], thresholds[1], fill_holes=fill_holes, edge_exclusion=edge)
280
290
 
281
291
  if do_watershed:
282
292
  coords,distance = identify_markers_from_binary(binary_image, marker_min_distance, footprint_size=marker_footprint_size, footprint=marker_footprint, return_edt=True)