celldetective 1.1.0__py3-none-any.whl → 1.1.1__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 (32) hide show
  1. celldetective/__main__.py +5 -19
  2. celldetective/extra_properties.py +62 -52
  3. celldetective/gui/control_panel.py +5 -0
  4. celldetective/gui/layouts.py +1 -0
  5. celldetective/gui/measurement_options.py +13 -109
  6. celldetective/gui/plot_signals_ui.py +1 -0
  7. celldetective/gui/process_block.py +1 -1
  8. celldetective/gui/survival_ui.py +7 -1
  9. celldetective/gui/tableUI.py +280 -28
  10. celldetective/gui/thresholds_gui.py +7 -5
  11. celldetective/gui/viewers.py +169 -22
  12. celldetective/io.py +14 -5
  13. celldetective/measure.py +13 -238
  14. celldetective/models/segmentation_effectors/primNK_cfse/config_input.json +29 -0
  15. celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer +0 -0
  16. celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json +37 -0
  17. celldetective/neighborhood.py +4 -1
  18. celldetective/preprocessing.py +483 -143
  19. celldetective/scripts/segment_cells.py +15 -4
  20. celldetective/scripts/train_segmentation_model.py +35 -34
  21. celldetective/segmentation.py +14 -9
  22. celldetective/signals.py +13 -231
  23. celldetective/tracking.py +2 -1
  24. celldetective/utils.py +437 -26
  25. {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/METADATA +1 -1
  26. {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/RECORD +32 -28
  27. tests/test_preprocessing.py +37 -0
  28. tests/test_utils.py +48 -1
  29. {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/LICENSE +0 -0
  30. {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/WHEEL +0 -0
  31. {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/entry_points.txt +0 -0
  32. {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/top_level.txt +0 -0
celldetective/measure.py CHANGED
@@ -27,6 +27,7 @@ from skimage.draw import disk as dsk
27
27
  from celldetective.filters import std_filter, gauss_filter
28
28
  from celldetective.utils import rename_intensity_column, create_patch_mask, remove_redundant_features, \
29
29
  remove_trajectory_measurements
30
+ from celldetective.preprocessing import field_correction
30
31
  import celldetective.extra_properties as extra_properties
31
32
  from celldetective.extra_properties import *
32
33
  import cv2
@@ -771,7 +772,7 @@ def measure_isotropic_intensity(positions, # Dataframe of cell positions @ t
771
772
  projection = np.multiply(crop_temp, expanded_mask)
772
773
 
773
774
  projection[crop==epsilon] = epsilon
774
- projection[expanded_mask==0.] = epsilon
775
+ projection[expanded_mask[:,:,0]==0.,:] = epsilon
775
776
 
776
777
  for op in operations:
777
778
  func = eval('np.'+op)
@@ -861,7 +862,7 @@ def measure_at_position(pos, mode, return_measurements=False, threads=1):
861
862
  return None
862
863
 
863
864
 
864
- def local_normalisation(image, labels, background_intensity, model='median', operation='subtract', clip=False):
865
+ def local_normalisation(image, labels, background_intensity, measurement='intensity_median', operation='subtract', clip=False):
865
866
  """
866
867
  Perform local normalization on an image based on labels.
867
868
 
@@ -907,10 +908,10 @@ def local_normalisation(image, labels, background_intensity, model='median', ope
907
908
  continue
908
909
  if operation == 'subtract':
909
910
  image[np.where(labels == cell)] = image[np.where(labels == cell)].astype(float) - \
910
- background_intensity[f'intensity_{model.lower()}'][index-1].astype(float)
911
+ background_intensity[measurement][index-1].astype(float)
911
912
  elif operation == 'divide':
912
913
  image[np.where(labels == cell)] = image[np.where(labels == cell)].astype(float) / \
913
- background_intensity[f'intensity_{model.lower()}'][index-1].astype(float)
914
+ background_intensity[measurement][index-1].astype(float)
914
915
  if clip:
915
916
  image[image<=0.] = 0.
916
917
 
@@ -956,249 +957,23 @@ def normalise_by_cell(image, labels, distance=5, model='median', operation='subt
956
957
  """
957
958
  border = contour_of_instance_segmentation(label=labels, distance=distance * (-1))
958
959
  if model == 'mean':
960
+ measurement = 'intensity_nanmean'
961
+ extra_props = [getattr(extra_properties, measurement)]
959
962
  background_intensity = regionprops_table(intensity_image=image, label_image=border,
960
- properties=['intensity_mean'])
963
+ extra_properties=extra_props)
961
964
  elif model == 'median':
962
- median = []
963
- median.append(getattr(extra_properties, 'intensity_median'))
965
+ measurement = 'intensity_median'
966
+ extra_props = [getattr(extra_properties, measurement)]
964
967
  background_intensity = regionprops_table(intensity_image=image, label_image=border,
965
- extra_properties=median)
968
+ extra_properties=extra_props)
969
+
966
970
  normalised_frame = local_normalisation(image=image.astype(float).copy(),
967
- labels=labels, background_intensity=background_intensity, model=model,
971
+ labels=labels, background_intensity=background_intensity, measurement=measurement,
968
972
  operation=operation, clip=clip)
969
973
 
970
974
  return normalised_frame
971
975
 
972
976
 
973
- def paraboloid(x, y, a, b, c, d, e, g):
974
- return a * x ** 2 + b * y ** 2 + c * x * y + d * x + e * y + g
975
-
976
-
977
- def plane(x, y, a, b, c):
978
- return a * x + b * y + c
979
-
980
-
981
- def fit_plane(image, cell_masks=None):
982
- """
983
- Fit a plane to the given image data.
984
-
985
- Parameters:
986
- - image (numpy.ndarray): The input image data.
987
- - cell_masks (numpy.ndarray, optional): An array specifying cell masks. If provided, areas covered by
988
- cell masks will be excluded from the fitting process.
989
-
990
- Returns:
991
- - numpy.ndarray: The fitted plane.
992
-
993
- This function fits a plane to the given image data using least squares regression. It constructs a mesh
994
- grid based on the dimensions of the image and fits a plane model to the data points. If cell masks are
995
- provided, areas covered by cell masks will be excluded from the fitting process.
996
-
997
- Example:
998
- >>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
999
- >>> result = fit_plane(image)
1000
- >>> print(result)
1001
- [[1. 2. 3.]
1002
- [4. 5. 6.]
1003
- [7. 8. 9.]]
1004
-
1005
- Note:
1006
- - The 'cell_masks' parameter allows excluding areas covered by cell masks from the fitting process.
1007
- """
1008
- data = np.empty(image.shape)
1009
- x = np.arange(0, image.shape[1])
1010
- y = np.arange(0, image.shape[0])
1011
- xx, yy = np.meshgrid(x, y)
1012
- params = Parameters()
1013
- params.add('a', value=1)
1014
- params.add('b', value=1)
1015
- params.add('c', value=1)
1016
- model = Model(plane, independent_vars=['x', 'y'])
1017
- weights = np.ones_like(xx, dtype=float)
1018
- weights[np.where(cell_masks > 0)] = 0.
1019
- result = model.fit(image,
1020
- x=xx,
1021
- y=yy,
1022
- weights=weights,
1023
- params=params, max_nfev=3000)
1024
- a = result.params['a'].value
1025
- b = result.params['b'].value
1026
- c = result.params['c'].value
1027
- fitted_plane = plane(xx, yy, a, b, c)
1028
- return fitted_plane
1029
-
1030
-
1031
- def fit_paraboloid(image, cell_masks=None):
1032
- """
1033
- Fit a paraboloid to the given image data.
1034
-
1035
- Parameters:
1036
- - image (numpy.ndarray): The input image data.
1037
- - cell_masks (numpy.ndarray, optional): An array specifying cell masks. If provided, areas covered by
1038
- cell masks will be excluded from the fitting process.
1039
-
1040
- Returns:
1041
- - numpy.ndarray: The fitted paraboloid.
1042
-
1043
- This function fits a paraboloid to the given image data using least squares regression. It constructs
1044
- a mesh grid based on the dimensions of the image and fits a paraboloid model to the data points. If cell
1045
- masks are provided, areas covered by cell masks will be excluded from the fitting process.
1046
-
1047
- Example:
1048
- >>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
1049
- >>> result = fit_paraboloid(image)
1050
- >>> print(result)
1051
- [[1. 2. 3.]
1052
- [4. 5. 6.]
1053
- [7. 8. 9.]]
1054
-
1055
- Note:
1056
- - The 'cell_masks' parameter allows excluding areas covered by cell masks from the fitting process.
1057
- """
1058
- data = np.empty(image.shape)
1059
- x = np.arange(0, image.shape[1])
1060
- y = np.arange(0, image.shape[0])
1061
- xx, yy = np.meshgrid(x, y)
1062
- params = Parameters()
1063
- params.add('a', value=1)
1064
- params.add('b', value=1)
1065
- params.add('c', value=1)
1066
- params.add('d', value=1)
1067
- params.add('e', value=1)
1068
- params.add('g', value=1)
1069
- model = Model(paraboloid, independent_vars=['x', 'y'])
1070
- weights = np.ones_like(xx, dtype=float)
1071
- weights[np.where(cell_masks > 0)] = 0.
1072
-
1073
- result = model.fit(image,
1074
- x=xx,
1075
- y=yy,
1076
- weights=weights,
1077
- params=params, max_nfev=3000)
1078
- a = result.params['a'].value
1079
- b = result.params['b'].value
1080
- c = result.params['c'].value
1081
- d = result.params['d'].value
1082
- e = result.params['e'].value
1083
- g = result.params['g'].value
1084
- fitted_paraboloid = paraboloid(xx, yy, a, b, c, d, e, g)
1085
- return fitted_paraboloid
1086
-
1087
-
1088
- def correct_image(img, cell_masks=None, normalisation_operation=None, clip=False, mode=None):
1089
- """
1090
- Correct an image based on fitted models.
1091
-
1092
- Parameters:
1093
- - img (numpy.ndarray): The input image data.
1094
- - cell_masks (numpy.ndarray, optional): An array specifying cell masks. If provided, areas covered by
1095
- cell masks will be considered during correction.
1096
- - normalisation_operation (str, optional): The normalisation operation ('Subtract' or 'Divide') to apply
1097
- during correction.
1098
- - clip (bool, optional): Whether to clip corrected values below zero to a minimum value of 0.00001.
1099
- - mode (str, optional): The mode of correction ('Paraboloid' or 'Plane').
1100
-
1101
- Returns:
1102
- - tuple: A tuple containing the corrected image and the fitted model parameters.
1103
-
1104
- This function corrects an image based on fitted models such as paraboloid or plane. It first fits a model
1105
- to the image data based on the specified mode. Then, it performs correction by subtracting or dividing the
1106
- image by the fitted model. Optionally, it clips corrected values below zero to a minimum value of 0.00001.
1107
-
1108
- Example:
1109
- >>> img = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
1110
- >>> correction, para = correct_image(img, mode='Paraboloid', normalisation_operation='Subtract', clip=True)
1111
- >>> print(correction)
1112
- [[0. 0. 0.]
1113
- [0. 0. 0.]
1114
- [0. 0. 0.]]
1115
- >>> print(para)
1116
- [[1. 2. 3.]
1117
- [4. 5. 6.]
1118
- [7. 8. 9.]]
1119
-
1120
- Note:
1121
- - The 'cell_masks' parameter allows considering areas covered by cell masks during correction.
1122
- - The 'normalisation_operation' parameter specifies whether to subtract or divide the fitted model from
1123
- the image.
1124
- - If 'clip' is set to True, corrected values below zero will be clipped to a minimum value of 0.00001.
1125
- """
1126
- if mode == "Paraboloid":
1127
- para = fit_paraboloid(img.astype(float), cell_masks=cell_masks).astype(float)
1128
- elif mode == "Plane":
1129
- para = fit_plane(img.astype(float), cell_masks=cell_masks).astype(float)
1130
-
1131
- if para is not None:
1132
- para = np.array(para)
1133
- if normalisation_operation == 'Subtract':
1134
- correction = img.astype(float) - para.astype(float) # + 1000.
1135
- else:
1136
- correction = img.astype(float) / para.astype(float) # + 1000.
1137
- correction = np.array(correction, dtype=float)
1138
- if clip:
1139
- correction[correction <= 0] = 0.00001
1140
-
1141
- else:
1142
- correction = None
1143
-
1144
- return correction, para
1145
-
1146
-
1147
- def field_normalisation(img, threshold, normalisation_operation, clip, mode):
1148
- """
1149
- Perform field normalization on an image.
1150
-
1151
- Parameters:
1152
- - img (numpy.ndarray): The input image data.
1153
- - threshold (float): The threshold value for determining regions of interest.
1154
- - normalisation_operation (str): The normalization operation ('Subtract' or 'Divide') to apply during correction.
1155
- - clip (bool): Whether to clip corrected values below zero to a minimum value of 0.00001.
1156
- - mode (str): The mode of correction ('Paraboloid' or 'Plane').
1157
-
1158
- Returns:
1159
- - tuple: A tuple containing the normalized image and the fitted background model.
1160
-
1161
- This function performs field normalization on an image based on regions of interest determined by the
1162
- specified threshold. It identifies regions with standard deviation above the threshold and considers them
1163
- as areas of interest. Then, it corrects the image using the 'correct_image' function based on the specified
1164
- mode and normalization operation.
1165
-
1166
- Example:
1167
- >>> img = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
1168
- >>> fluo_max, bg_fit = field_normalisation(img, threshold=0.5, normalisation_operation='Subtract', clip=True, mode='Paraboloid')
1169
- >>> print(fluo_max)
1170
- [[0. 0. 0.]
1171
- [0. 0. 0.]
1172
- [0. 0. 0.]]
1173
- >>> print(bg_fit)
1174
- [[1. 2. 3.]
1175
- [4. 5. 6.]
1176
- [7. 8. 9.]]
1177
-
1178
- Note:
1179
- - The 'threshold' parameter determines regions of interest based on standard deviation.
1180
- - The 'normalisation_operation' parameter specifies whether to subtract or divide the fitted model from
1181
- the image during correction.
1182
- - If 'clip' is set to True, corrected values below zero will be clipped to a minimum value of 0.00001.
1183
- """
1184
- std_img = std_filter(gauss_filter(img, 2), 4)
1185
- mask = np.zeros_like(img)
1186
- if threshold=='':
1187
- pass
1188
- mask[np.where(std_img > float(threshold))] = 1.0
1189
- mask_int = mask.astype(int)
1190
- mask_int = binary_fill_holes(mask_int).astype(float)
1191
- # invert_mask = np.zeros_like(mask_int)
1192
- # invert_mask[mask_int == 0] = 1
1193
- # if isinstance(normalisation_operation,bool) and normalisation_operation:
1194
- # normalisation_operation = 'Subtract'
1195
- # else:
1196
- # normalisation_operation = 'Divide'
1197
- fluo_max, bg_fit = correct_image(img.astype(float), cell_masks=mask_int,
1198
- normalisation_operation=normalisation_operation, clip=clip, mode=mode)
1199
- return fluo_max, bg_fit
1200
-
1201
-
1202
977
  def blob_detection(image, label, threshold, diameter):
1203
978
  """
1204
979
  Perform blob detection on an image based on labeled regions.
@@ -0,0 +1,29 @@
1
+ {
2
+ "channels": [
3
+ "effector_fluo_channel",
4
+ "None"
5
+ ],
6
+ "diameter": 30.0,
7
+ "cellprob_threshold": 0.0,
8
+ "flow_threshold": 0.4,
9
+ "normalization_percentile": [
10
+ true,
11
+ true
12
+ ],
13
+ "normalization_clip": [
14
+ true,
15
+ true
16
+ ],
17
+ "normalization_values": [
18
+ [
19
+ 0.5,
20
+ 99.0
21
+ ],
22
+ [
23
+ 1.0,
24
+ 99.0
25
+ ]
26
+ ],
27
+ "model_type": "cellpose",
28
+ "spatial_calibration": 0.21783999999999998
29
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "model_name": "cp-cfse-transfer",
3
+ "model_type": "cellpose",
4
+ "pretrained": "/home/limozin/Documents/GitHub/celldetective/celldetective/models/segmentation_generic/CP_cyto2",
5
+ "spatial_calibration": 0.21783999999999998,
6
+ "channel_option": [
7
+ "effector_fluo_channel",
8
+ "None"
9
+ ],
10
+ "normalization_percentile": [
11
+ true,
12
+ true
13
+ ],
14
+ "normalization_clip": [
15
+ true,
16
+ true
17
+ ],
18
+ "normalization_values": [
19
+ [
20
+ 0.5,
21
+ 99.0
22
+ ],
23
+ [
24
+ 1.0,
25
+ 99.0
26
+ ]
27
+ ],
28
+ "ds": [
29
+ "/home/limozin/Desktop/primNK_w_MCF7/dataset"
30
+ ],
31
+ "augmentation_factor": 1.5,
32
+ "validation_split": 0.2,
33
+ "learning_rate": 0.001,
34
+ "batch_size": 8,
35
+ "epochs": 3000,
36
+ "target_directory": "/home/limozin/Documents/GitHub/celldetective/celldetective/models/segmentation_effectors"
37
+ }
@@ -916,7 +916,8 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
916
916
  idx_B = np.where(mask_ids_B==int(mask_B))[0][0]
917
917
  print(idx_A, idx_B)
918
918
  indices_to_keep.append([idx_A,idx_B])
919
- except:
919
+ except Exception as e:
920
+ print(f'{e} {t=} error something happened!!')
920
921
  pass
921
922
 
922
923
  print(f'Indices to keep: {indices_to_keep}...')
@@ -927,6 +928,8 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
927
928
  mask[indices_to_keep[:,1],indices_to_keep[:,0]] = False
928
929
  dist_map[mask] = 1.0E06
929
930
  plot_map=True
931
+ else:
932
+ dist_map[:,:] = 1.0E06
930
933
  else:
931
934
  dist_map[:,:] = 1.0E06
932
935