celldetective 1.3.9.post5__py3-none-any.whl → 1.4.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 (94) hide show
  1. celldetective/__init__.py +0 -3
  2. celldetective/_version.py +1 -1
  3. celldetective/events.py +2 -4
  4. celldetective/exceptions.py +11 -0
  5. celldetective/extra_properties.py +132 -0
  6. celldetective/filters.py +7 -1
  7. celldetective/gui/InitWindow.py +37 -46
  8. celldetective/gui/__init__.py +3 -9
  9. celldetective/gui/about.py +19 -15
  10. celldetective/gui/analyze_block.py +34 -19
  11. celldetective/gui/base_annotator.py +786 -0
  12. celldetective/gui/base_components.py +23 -0
  13. celldetective/gui/classifier_widget.py +86 -94
  14. celldetective/gui/configure_new_exp.py +163 -46
  15. celldetective/gui/control_panel.py +76 -146
  16. celldetective/gui/{signal_annotator.py → event_annotator.py} +533 -1438
  17. celldetective/gui/generic_signal_plot.py +11 -13
  18. celldetective/gui/gui_utils.py +54 -23
  19. celldetective/gui/help/neighborhood.json +2 -2
  20. celldetective/gui/json_readers.py +5 -4
  21. celldetective/gui/layouts.py +265 -31
  22. celldetective/gui/{signal_annotator2.py → pair_event_annotator.py} +433 -635
  23. celldetective/gui/plot_measurements.py +21 -17
  24. celldetective/gui/plot_signals_ui.py +125 -72
  25. celldetective/gui/process_block.py +283 -188
  26. celldetective/gui/processes/compute_neighborhood.py +594 -0
  27. celldetective/gui/processes/downloader.py +37 -34
  28. celldetective/gui/processes/measure_cells.py +19 -8
  29. celldetective/gui/processes/segment_cells.py +47 -11
  30. celldetective/gui/processes/track_cells.py +18 -13
  31. celldetective/gui/seg_model_loader.py +21 -62
  32. celldetective/gui/settings/__init__.py +7 -0
  33. celldetective/gui/settings/_settings_base.py +70 -0
  34. celldetective/gui/{retrain_signal_model_options.py → settings/_settings_event_model_training.py} +54 -109
  35. celldetective/gui/{measurement_options.py → settings/_settings_measurements.py} +54 -92
  36. celldetective/gui/{neighborhood_options.py → settings/_settings_neighborhood.py} +10 -13
  37. celldetective/gui/settings/_settings_segmentation.py +49 -0
  38. celldetective/gui/{retrain_segmentation_model_options.py → settings/_settings_segmentation_model_training.py} +38 -92
  39. celldetective/gui/{signal_annotator_options.py → settings/_settings_signal_annotator.py} +78 -103
  40. celldetective/gui/{btrack_options.py → settings/_settings_tracking.py} +85 -116
  41. celldetective/gui/styles.py +2 -1
  42. celldetective/gui/survival_ui.py +49 -95
  43. celldetective/gui/tableUI.py +53 -25
  44. celldetective/gui/table_ops/__init__.py +0 -0
  45. celldetective/gui/table_ops/merge_groups.py +118 -0
  46. celldetective/gui/thresholds_gui.py +617 -1221
  47. celldetective/gui/viewers.py +107 -42
  48. celldetective/gui/workers.py +8 -4
  49. celldetective/io.py +137 -57
  50. celldetective/links/zenodo.json +145 -144
  51. celldetective/measure.py +94 -53
  52. celldetective/neighborhood.py +342 -268
  53. celldetective/preprocessing.py +56 -35
  54. celldetective/regionprops/_regionprops.py +16 -5
  55. celldetective/relative_measurements.py +50 -29
  56. celldetective/scripts/analyze_signals.py +4 -1
  57. celldetective/scripts/measure_cells.py +5 -5
  58. celldetective/scripts/measure_relative.py +20 -12
  59. celldetective/scripts/segment_cells.py +4 -10
  60. celldetective/scripts/segment_cells_thresholds.py +3 -3
  61. celldetective/scripts/track_cells.py +10 -8
  62. celldetective/scripts/train_segmentation_model.py +18 -6
  63. celldetective/signals.py +29 -14
  64. celldetective/tracking.py +14 -3
  65. celldetective/utils.py +91 -62
  66. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/METADATA +24 -16
  67. celldetective-1.4.1.dist-info/RECORD +123 -0
  68. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/WHEEL +1 -1
  69. tests/gui/__init__.py +0 -0
  70. tests/gui/test_new_project.py +228 -0
  71. tests/gui/test_project.py +99 -0
  72. tests/test_preprocessing.py +2 -2
  73. celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -79
  74. celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
  75. celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -37
  76. celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -39
  77. celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
  78. celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
  79. celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
  80. celldetective/models/signal_detection/NucCond/config_input.json +0 -1
  81. celldetective/models/signal_detection/NucCond/log_classifier.csv +0 -126
  82. celldetective/models/signal_detection/NucCond/log_regressor.csv +0 -282
  83. celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
  84. celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
  85. celldetective/models/signal_detection/NucCond/scores.npy +0 -0
  86. celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
  87. celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
  88. celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
  89. celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
  90. celldetective-1.3.9.post5.dist-info/RECORD +0 -129
  91. tests/test_qt.py +0 -103
  92. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/entry_points.txt +0 -0
  93. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info/licenses}/LICENSE +0 -0
  94. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/top_level.txt +0 -0
celldetective/measure.py CHANGED
@@ -17,16 +17,25 @@ from math import ceil
17
17
  from skimage.draw import disk as dsk
18
18
  from skimage.feature import blob_dog, blob_log
19
19
 
20
+ from celldetective.exceptions import EmptyQueryError, MissingColumnsError, QueryError
20
21
  from celldetective.utils import rename_intensity_column, create_patch_mask, remove_redundant_features, \
21
22
  remove_trajectory_measurements, contour_of_instance_segmentation, extract_cols_from_query, step_function, interpolate_nan, _remove_invalid_cols
22
23
  from celldetective.preprocessing import field_correction
23
- from celldetective.extra_properties import *
24
+
25
+ # try:
26
+ # from celldetective.extra_properties import *
27
+ # extra_props = True
28
+ # except Exception as e:
29
+ # print(f"The module extra_properties seems corrupted: {e}... Skip...")
30
+ # extra_props = False
31
+
24
32
  from inspect import getmembers, isfunction
25
33
  from skimage.morphology import disk
26
34
  from scipy.signal import find_peaks, peak_widths
27
35
 
28
36
  from celldetective.segmentation import filter_image
29
37
  from celldetective.regionprops import regionprops_table
38
+ from celldetective.utils import pretty_table
30
39
 
31
40
  abs_path = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], 'celldetective'])
32
41
 
@@ -211,7 +220,7 @@ def measure(stack=None, labels=None, trajectories=None, channel_names=None,
211
220
  measurements = measurements.sort_values(by=[column_labels['track'],column_labels['time']])
212
221
  measurements = measurements.dropna(subset=[column_labels['track']])
213
222
  else:
214
- measurements['ID'] = np.arange(len(df))
223
+ measurements['ID'] = np.arange(len(measurements))
215
224
 
216
225
  measurements = measurements.reset_index(drop=True)
217
226
  measurements = _remove_invalid_cols(measurements)
@@ -365,26 +374,35 @@ def measure_features(img, label, features=['area', 'intensity_mean'], channels=N
365
374
  corrected_image = field_correction(img[:,:,ind].copy(), threshold_on_std=norm['threshold_on_std'], operation=norm['operation'], model=norm['model'], clip=norm['clip'])
366
375
  img[:, :, ind] = corrected_image
367
376
 
368
- import celldetective.extra_properties as extra_props
369
-
370
- extra = getmembers(extra_props, isfunction)
371
- extra = [extra[i][0] for i in range(len(extra))]
372
-
373
- extra_props_list = []
374
- feats = features.copy()
375
- for f in features:
376
- if f in extra:
377
- feats.remove(f)
378
- extra_props_list.append(getattr(extra_props, f))
379
-
380
- # Add intensity nan mean if need to measure mean intensities
381
- if measure_mean_intensities:
382
- extra_props_list.append(getattr(extra_props, 'intensity_nanmean'))
383
-
384
- if len(extra_props_list) == 0:
385
- extra_props_list = None
377
+ try:
378
+ import celldetective.extra_properties as extra_props
379
+ extraprops = True
380
+ except Exception as e:
381
+ print(f"The module extra_properties seems corrupted: {e}... Skip...")
382
+ extraprops = False
383
+
384
+ if extraprops:
385
+ extra = getmembers(extra_props, isfunction)
386
+ extra = [extra[i][0] for i in range(len(extra))]
387
+
388
+ extra_props_list = []
389
+ feats = features.copy()
390
+ for f in features:
391
+ if f in extra:
392
+ feats.remove(f)
393
+ extra_props_list.append(getattr(extra_props, f))
394
+
395
+ # Add intensity nan mean if need to measure mean intensities
396
+ if measure_mean_intensities:
397
+ extra_props_list.append(getattr(extra_props, 'intensity_nanmean'))
398
+
399
+ if len(extra_props_list) == 0:
400
+ extra_props_list = None
401
+ else:
402
+ extra_props_list = tuple(extra_props_list)
386
403
  else:
387
- extra_props_list = tuple(extra_props_list)
404
+ extra_props_list = []
405
+ feats = features.copy()
388
406
 
389
407
  props = regionprops_table(label, intensity_image=img, properties=feats, extra_properties=extra_props_list, channel_names=channels)
390
408
  df_props = pd.DataFrame(props)
@@ -837,17 +855,32 @@ def local_normalisation(image, labels, background_intensity, measurement='intens
837
855
 
838
856
  def normalise_by_cell(image, labels, distance=5, model='median', operation='subtract', clip=False):
839
857
 
840
- import celldetective.extra_properties as extra_props
858
+ try:
859
+ import celldetective.extra_properties as extra_props
860
+ extraprops = True
861
+ except Exception as e:
862
+ print(f"The module extra_properties seems corrupted: {e}... Skip...")
863
+ extraprops = False
841
864
 
842
865
  border = contour_of_instance_segmentation(label=labels, distance=distance * (-1))
843
866
  if model == 'mean':
867
+
844
868
  measurement = 'intensity_nanmean'
845
- extra_props = [getattr(extra_props, measurement)]
869
+ if extraprops:
870
+ extra_props = [getattr(extra_props, measurement)]
871
+ else:
872
+ extra_props = []
873
+
846
874
  background_intensity = regionprops_table(intensity_image=image, label_image=border,
847
875
  extra_properties=extra_props)
848
876
  elif model == 'median':
877
+
849
878
  measurement = 'intensity_median'
850
- extra_props = [getattr(extra_props, measurement)]
879
+ if extraprops:
880
+ extra_props = [getattr(extra_props, measurement)]
881
+ else:
882
+ extra_props = []
883
+
851
884
  background_intensity = regionprops_table(intensity_image=image, label_image=border,
852
885
  extra_properties=extra_props)
853
886
 
@@ -1250,16 +1283,20 @@ def classify_irreversible_events(data, class_attr, r2_threshold=0.5, percentile_
1250
1283
  # ambiguity, possible transition, use `unique_state` technique after
1251
1284
  df.loc[indices, class_attr] = 2
1252
1285
 
1253
- print("Classes after initial pass: ",df.loc[df['FRAME']==0,class_attr].value_counts())
1286
+ print("Number of cells per class after the initial pass: ")
1287
+ pretty_table(df.loc[df['FRAME']==0,class_attr].value_counts().to_dict())
1254
1288
 
1255
1289
  df.loc[df[class_attr]!=2, class_attr.replace('class', 't')] = -1
1256
1290
  # Try to fit time on class 2 cells (ambiguous)
1257
1291
  df = estimate_time(df, class_attr, model='step_function', class_of_interest=[2], r2_threshold=r2_threshold)
1258
- print("Classes after fit: ", df.loc[df['FRAME']==0,class_attr].value_counts())
1292
+
1293
+ print("Number of cells per class after conditional signal fit: ")
1294
+ pretty_table(df.loc[df['FRAME']==0,class_attr].value_counts().to_dict())
1259
1295
 
1260
1296
  # Revisit class 2 cells to classify as neg/pos with percentile tolerance
1261
1297
  df.loc[df[class_attr]==2,:] = classify_unique_states(df.loc[df[class_attr]==2,:].copy(), class_attr, percentile_recovery)
1262
- print("Classes after unique state recovery: ",df.loc[df['FRAME']==0,class_attr].value_counts())
1298
+ print("Number of cells per class after recovery pass (median state): ")
1299
+ pretty_table(df.loc[df['FRAME']==0,class_attr].value_counts().to_dict())
1263
1300
 
1264
1301
  return df
1265
1302
 
@@ -1396,35 +1433,39 @@ def classify_cells_from_query(df, status_attr, query):
1396
1433
  If the query is invalid or if there are issues with the DataFrame or query syntax, an error message is printed, and `None` is returned.
1397
1434
 
1398
1435
  """
1399
-
1400
-
1401
- # Initialize all states to 0
1402
- if not status_attr.startswith('status_'):
1403
- status_attr = 'status_'+status_attr
1404
-
1436
+
1437
+ if not status_attr.startswith("status_"):
1438
+ status_attr = "status_" + status_attr
1439
+
1405
1440
  df = df.copy()
1406
- df.loc[:,status_attr] = 0
1441
+ df = df.replace([np.inf, -np.inf, None], np.nan)
1442
+ #df = df.convert_dtypes()
1443
+
1444
+ df.loc[:, status_attr] = 0
1407
1445
  df[status_attr] = df[status_attr].astype(float)
1408
-
1446
+
1409
1447
  cols = extract_cols_from_query(query)
1410
- print(f"{cols=}")
1411
-
1412
- cols_in_df = np.all([c in list(df.columns) for c in cols], axis=0)
1413
- if query=='':
1414
- print('The provided query is empty...')
1415
- else:
1416
- try:
1417
- if cols_in_df:
1418
- selection = df.dropna(subset=cols).query(query).index
1419
- null_selection = df[df.loc[:,cols].isna().any(axis=1)].index
1420
- # Set NaN to invalid cells, 1 otherwise
1421
- df.loc[null_selection, status_attr] = np.nan
1422
- df.loc[selection, status_attr] = 1
1423
- else:
1424
- df.loc[:, status_attr] = np.nan
1425
- except Exception as e:
1426
- print(f"The query could not be understood. No filtering was applied. {e}...")
1427
- return None
1448
+ print(f"The following DataFrame measurements were identified in the query: {cols=}...")
1449
+
1450
+ if query.strip() == "":
1451
+ raise EmptyQueryError("The provided query is empty.")
1452
+
1453
+ missing_cols = [c for c in cols if c not in df.columns]
1454
+ if missing_cols:
1455
+ raise MissingColumnsError(missing_cols)
1456
+
1457
+ try:
1458
+ sub_df = df.dropna(subset=cols)
1459
+ if len(sub_df) > 0:
1460
+ selection = sub_df.query(query).index
1461
+ null_selection = df[df.loc[:, cols].isna().any(axis=1)].index
1462
+ df.loc[null_selection, status_attr] = np.nan
1463
+ df.loc[selection, status_attr] = 1
1464
+ else:
1465
+ df.loc[:, status_attr] = np.nan
1466
+ except Exception as e:
1467
+ raise QueryError(f"The query could not be understood: {e}")
1468
+
1428
1469
  return df.copy()
1429
1470
 
1430
1471
  def classify_tracks_from_query(df, event_name, query, irreversible_event=True, unique_state=False, r2_threshold=0.5, percentile_recovery=50):