celldetective 1.3.9.post4__py3-none-any.whl → 1.4.0__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 (57) hide show
  1. celldetective/__init__.py +0 -3
  2. celldetective/_version.py +1 -1
  3. celldetective/events.py +2 -4
  4. celldetective/extra_properties.py +320 -24
  5. celldetective/gui/InitWindow.py +33 -45
  6. celldetective/gui/__init__.py +1 -0
  7. celldetective/gui/about.py +19 -15
  8. celldetective/gui/analyze_block.py +34 -19
  9. celldetective/gui/base_components.py +23 -0
  10. celldetective/gui/btrack_options.py +26 -34
  11. celldetective/gui/classifier_widget.py +71 -80
  12. celldetective/gui/configure_new_exp.py +113 -17
  13. celldetective/gui/control_panel.py +68 -141
  14. celldetective/gui/generic_signal_plot.py +9 -12
  15. celldetective/gui/gui_utils.py +49 -21
  16. celldetective/gui/json_readers.py +5 -4
  17. celldetective/gui/layouts.py +246 -22
  18. celldetective/gui/measurement_options.py +32 -17
  19. celldetective/gui/neighborhood_options.py +10 -13
  20. celldetective/gui/plot_measurements.py +21 -17
  21. celldetective/gui/plot_signals_ui.py +131 -75
  22. celldetective/gui/process_block.py +180 -123
  23. celldetective/gui/processes/compute_neighborhood.py +594 -0
  24. celldetective/gui/processes/measure_cells.py +5 -0
  25. celldetective/gui/processes/segment_cells.py +27 -6
  26. celldetective/gui/processes/track_cells.py +6 -0
  27. celldetective/gui/retrain_segmentation_model_options.py +12 -20
  28. celldetective/gui/retrain_signal_model_options.py +57 -56
  29. celldetective/gui/seg_model_loader.py +21 -62
  30. celldetective/gui/signal_annotator.py +139 -72
  31. celldetective/gui/signal_annotator2.py +431 -635
  32. celldetective/gui/signal_annotator_options.py +8 -11
  33. celldetective/gui/survival_ui.py +49 -95
  34. celldetective/gui/tableUI.py +28 -25
  35. celldetective/gui/thresholds_gui.py +617 -1221
  36. celldetective/gui/viewers.py +106 -39
  37. celldetective/gui/workers.py +9 -3
  38. celldetective/io.py +73 -27
  39. celldetective/measure.py +63 -27
  40. celldetective/neighborhood.py +342 -268
  41. celldetective/preprocessing.py +25 -17
  42. celldetective/relative_measurements.py +50 -29
  43. celldetective/scripts/analyze_signals.py +4 -1
  44. celldetective/scripts/measure_relative.py +4 -1
  45. celldetective/scripts/segment_cells.py +0 -6
  46. celldetective/scripts/track_cells.py +3 -1
  47. celldetective/scripts/train_segmentation_model.py +7 -4
  48. celldetective/signals.py +29 -14
  49. celldetective/tracking.py +7 -2
  50. celldetective/utils.py +36 -8
  51. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/METADATA +24 -16
  52. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/RECORD +57 -55
  53. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/WHEEL +1 -1
  54. tests/test_qt.py +21 -21
  55. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/entry_points.txt +0 -0
  56. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info/licenses}/LICENSE +0 -0
  57. {celldetective-1.3.9.post4.dist-info → celldetective-1.4.0.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
- from PyQt5.QtWidgets import QMainWindow, QComboBox, QLabel, QRadioButton, QLineEdit, QFileDialog, QApplication, \
2
- QPushButton, QWidget, QVBoxLayout, QHBoxLayout, QMessageBox, QShortcut, QLineEdit, QSlider, QCheckBox
1
+ from PyQt5.QtWidgets import QComboBox, QLabel, QRadioButton, QLineEdit, QFileDialog, QApplication, \
2
+ QPushButton, QVBoxLayout, QHBoxLayout, QMessageBox, QShortcut, QLineEdit, QSlider, QCheckBox
3
3
  from PyQt5.QtCore import Qt, QSize
4
4
  from PyQt5.QtGui import QKeySequence, QIntValidator
5
5
 
@@ -23,10 +23,11 @@ from matplotlib.animation import FuncAnimation
23
23
  from matplotlib.cm import tab10
24
24
  import pandas as pd
25
25
  from sklearn.preprocessing import MinMaxScaler
26
- from celldetective.gui import Styles
26
+ from celldetective.gui import Styles, CelldetectiveWidget, CelldetectiveMainWindow
27
27
  from celldetective.measure import contour_of_instance_segmentation
28
+ from celldetective.utils import pretty_table
28
29
 
29
- class SignalAnnotator(QMainWindow, Styles):
30
+ class SignalAnnotator(CelldetectiveMainWindow):
30
31
  """
31
32
  UI to set tracking parameters for bTrack.
32
33
 
@@ -38,7 +39,6 @@ class SignalAnnotator(QMainWindow, Styles):
38
39
 
39
40
  center_window(self)
40
41
  self.proceed = True
41
- self.setAttribute(Qt.WA_DeleteOnClose)
42
42
 
43
43
  self.parent_window = parent_window
44
44
  self.setWindowTitle("Signal annotator")
@@ -51,12 +51,8 @@ class SignalAnnotator(QMainWindow, Styles):
51
51
  self.recently_modified = False
52
52
  self.selection = []
53
53
 
54
- if self.mode == "targets":
55
- self.instructions_path = self.exp_dir + os.sep.join(['configs', 'signal_annotator_config_targets.json'])
56
- self.trajectories_path = self.pos + os.sep.join(['output','tables','trajectories_targets.csv'])
57
- elif self.mode == "effectors":
58
- self.instructions_path = self.exp_dir + os.sep.join(['configs', 'signal_annotator_config_effectors.json'])
59
- self.trajectories_path = self.pos + os.sep.join(['output','tables','trajectories_effectors.csv'])
54
+ self.instructions_path = self.exp_dir + os.sep.join(['configs', f'signal_annotator_config_{self.mode}.json'])
55
+ self.trajectories_path = self.pos + os.sep.join(['output','tables',f'trajectories_{self.mode}.csv'])
60
56
 
61
57
  self.screen_height = self.parent_window.parent_window.parent_window.screen_height
62
58
  self.screen_width = self.parent_window.parent_window.parent_window.screen_width
@@ -99,7 +95,7 @@ class SignalAnnotator(QMainWindow, Styles):
99
95
 
100
96
  """
101
97
 
102
- self.button_widget = QWidget()
98
+ self.button_widget = CelldetectiveWidget()
103
99
  main_layout = QHBoxLayout()
104
100
  self.button_widget.setLayout(main_layout)
105
101
 
@@ -330,7 +326,7 @@ class SignalAnnotator(QMainWindow, Styles):
330
326
  self.contrast_slider = QLabeledDoubleRangeSlider()
331
327
  self.contrast_slider.setSingleStep(0.001)
332
328
  self.contrast_slider.setTickInterval(0.001)
333
- self.contrast_slider.setOrientation(1)
329
+ self.contrast_slider.setOrientation(Qt.Horizontal)
334
330
  self.contrast_slider.setRange(
335
331
  *[np.nanpercentile(self.stack, 0.001), np.nanpercentile(self.stack, 99.999)])
336
332
  self.contrast_slider.setValue(
@@ -345,7 +341,7 @@ class SignalAnnotator(QMainWindow, Styles):
345
341
  # self.interval_slider = QLabeledSlider()
346
342
  # self.interval_slider.setSingleStep(1)
347
343
  # self.interval_slider.setTickInterval(1)
348
- # self.interval_slider.setOrientation(1)
344
+ # self.interval_slider.setOrientation(Qt.Horizontal)
349
345
  # self.interval_slider.setRange(1, 10000)
350
346
  # self.interval_slider.setValue(self.speed)
351
347
  # self.interval_slider.valueChanged.connect(self.interval_slider_action)
@@ -394,7 +390,7 @@ class SignalAnnotator(QMainWindow, Styles):
394
390
  def create_new_event_class(self):
395
391
 
396
392
  # display qwidget to name the event
397
- self.newClassWidget = QWidget()
393
+ self.newClassWidget = CelldetectiveWidget()
398
394
  self.newClassWidget.setWindowTitle('Create new event class')
399
395
 
400
396
  layout = QVBoxLayout()
@@ -787,10 +783,12 @@ class SignalAnnotator(QMainWindow, Styles):
787
783
  # self.columns_to_rescale = [col for t,col in zip(is_number_test,self.df_tracks.columns) if t]
788
784
  # print(self.columns_to_rescale)
789
785
 
790
- cols_to_remove = ['status', 'status_color', 'class_color', 'TRACK_ID', 'FRAME', 'x_anim', 'y_anim', 't',
786
+ cols_to_remove = ['group', 'group_color', 'status', 'status_color', 'class_color', 'TRACK_ID', 'FRAME',
787
+ 'x_anim', 'y_anim', 't','dummy','group_color',
791
788
  'state', 'generation', 'root', 'parent', 'class_id', 'class', 't0', 'POSITION_X',
792
- 'POSITION_Y', 'position', 'well', 'well_index', 'well_name', 'pos_name', 'index',] + self.class_cols
793
-
789
+ 'POSITION_Y', 'position', 'well', 'well_index', 'well_name', 'pos_name', 'index',
790
+ 'concentration', 'cell_type', 'antibody', 'pharmaceutical_agent', 'ID'] + self.class_cols
791
+
794
792
  meta = get_experiment_metadata(self.exp_dir)
795
793
  if meta is not None:
796
794
  keys = list(meta.keys())
@@ -807,8 +805,6 @@ class SignalAnnotator(QMainWindow, Styles):
807
805
  cols_to_remove += time_cols
808
806
  #cols_to_remove.extend(self.df_tracks.select_dtypes(include=['object']).columns)
809
807
 
810
- print(f"{cols_to_remove=}")
811
-
812
808
  for tr in cols_to_remove:
813
809
  try:
814
810
  self.columns_to_rescale.remove(tr)
@@ -831,12 +827,14 @@ class SignalAnnotator(QMainWindow, Styles):
831
827
  cclass = group[self.class_name].to_numpy()[0]
832
828
  timeline = group['FRAME'].to_numpy()
833
829
  status = np.zeros_like(timeline)
830
+
834
831
  if t0 > 0:
835
- status[timeline >= t0] = 1.
836
- if cclass == 2:
837
- status[:] = 2
832
+ status[timeline >= t0] = 1.
833
+ # if cclass == 2:
834
+ # status[:] = 1.
838
835
  if cclass > 2:
839
836
  status[:] = 42
837
+
840
838
  status_color = [color_from_status(s) for s in status]
841
839
  class_color = [color_from_class(cclass) for i in range(len(status))]
842
840
 
@@ -854,7 +852,17 @@ class SignalAnnotator(QMainWindow, Styles):
854
852
 
855
853
  to_remove = ['TRACK_ID', 'FRAME', 'x_anim', 'y_anim', 't', 'state', 'generation', 'root', 'parent', 'class_id',
856
854
  'class', 't0', 'POSITION_X', 'POSITION_Y', 'position', 'well', 'well_index', 'well_name',
857
- 'pos_name', 'index','class_color','status_color']
855
+ 'pos_name', 'index','class_color','status_color','dummy','group_color']
856
+
857
+ meta = get_experiment_metadata(self.exp_dir)
858
+ if meta is not None:
859
+ keys = list(meta.keys())
860
+ to_remove.extend(keys)
861
+
862
+ labels = get_experiment_labels(self.exp_dir)
863
+ if labels is not None:
864
+ keys = list(labels.keys())
865
+ to_remove.extend(labels)
858
866
 
859
867
  for c in to_remove:
860
868
  if c in signals:
@@ -874,7 +882,11 @@ class SignalAnnotator(QMainWindow, Styles):
874
882
  for i in range(len(self.signal_choice_cb)):
875
883
 
876
884
  signal_choice = self.signal_choice_cb[i].currentText()
877
- self.lines[i].set_label(signal_choice)
885
+ lbl = signal_choice
886
+ n_cut = 35
887
+ if len(lbl)>n_cut:
888
+ lbl = lbl[:(n_cut-3)]+'...'
889
+ self.lines[i].set_label(lbl)
878
890
 
879
891
  if signal_choice == "--":
880
892
  self.lines[i].set_xdata([])
@@ -903,9 +915,10 @@ class SignalAnnotator(QMainWindow, Styles):
903
915
  self.line_dt.set_xdata([t0, t0])
904
916
  self.line_dt.set_ydata([min_val, max_val])
905
917
 
906
- self.cell_ax.legend()
918
+ self.cell_ax.legend(fontsize=8)
907
919
  self.cell_fcanvas.canvas.draw()
908
920
  except Exception as e:
921
+ print(e)
909
922
  pass
910
923
 
911
924
  if len(range_values)>0:
@@ -1093,7 +1106,7 @@ class SignalAnnotator(QMainWindow, Styles):
1093
1106
  self.line_dt, = self.cell_ax.plot([-1, -1], [min_val, max_val], c="k", linestyle="--")
1094
1107
 
1095
1108
  self.cell_ax.set_xlim(0, self.len_movie)
1096
- self.cell_ax.legend()
1109
+ self.cell_ax.legend(fontsize=8)
1097
1110
  self.cell_fcanvas.canvas.draw()
1098
1111
 
1099
1112
  self.plot_signals()
@@ -1391,7 +1404,7 @@ class MeasureAnnotator(SignalAnnotator):
1391
1404
 
1392
1405
  def __init__(self, parent_window=None):
1393
1406
 
1394
- QMainWindow.__init__(self)
1407
+ SignalAnnotator.__init__(self)
1395
1408
  self.parent_window = parent_window
1396
1409
  self.setWindowTitle("Signal annotator")
1397
1410
  self.mode = self.parent_window.mode
@@ -1403,12 +1416,15 @@ class MeasureAnnotator(SignalAnnotator):
1403
1416
  self.selection = []
1404
1417
  self.int_validator = QIntValidator()
1405
1418
  self.current_alpha=0.5
1406
- if self.mode == "targets":
1407
- self.instructions_path = self.exp_dir + os.sep.join(['configs','signal_annotator_config_targets.json'])
1408
- self.trajectories_path = self.pos + os.sep.join(['output','tables','trajectories_targets.csv'])
1409
- elif self.mode == "effectors":
1410
- self.instructions_path = self.exp_dir + os.sep.join(['configs','signal_annotator_config_effectors.json'])
1411
- self.trajectories_path = self.pos + os.sep.join(['output','tables','trajectories_effectors.csv'])
1419
+ self.value_magnitude = 1
1420
+
1421
+ epsilon = 0.01
1422
+ self.observed_min_intensity = 0
1423
+ self.observed_max_intensity = 0 + epsilon
1424
+
1425
+
1426
+ self.instructions_path = self.exp_dir + os.sep.join(['configs',f'signal_annotator_config_{self.mode}.json'])
1427
+ self.trajectories_path = self.pos + os.sep.join(['output','tables',f'trajectories_{self.mode}.csv'])
1412
1428
 
1413
1429
  self.screen_height = self.parent_window.parent_window.parent_window.screen_height
1414
1430
  self.screen_width = self.parent_window.parent_window.parent_window.screen_width
@@ -1446,6 +1462,7 @@ class MeasureAnnotator(SignalAnnotator):
1446
1462
  self.setAttribute(Qt.WA_DeleteOnClose)
1447
1463
  self.previous_index = None
1448
1464
 
1465
+
1449
1466
  def static_image(self):
1450
1467
 
1451
1468
  """
@@ -1515,6 +1532,7 @@ class MeasureAnnotator(SignalAnnotator):
1515
1532
  current_yvalues = []
1516
1533
  all_median_values = []
1517
1534
  labels = []
1535
+ range_values = []
1518
1536
 
1519
1537
  for i in range(len(self.signal_choice_cb)):
1520
1538
 
@@ -1531,6 +1549,7 @@ class MeasureAnnotator(SignalAnnotator):
1531
1549
  all_ydata = self.df_tracks.loc[:, signal_choice].to_numpy()
1532
1550
  ydataNaN = ydata
1533
1551
  ydata = ydata[ydata == ydata] # remove nan
1552
+
1534
1553
  current_ydata = self.df_tracks.loc[
1535
1554
  (self.df_tracks['FRAME'] == current_frame), signal_choice].to_numpy()
1536
1555
  current_ydata = current_ydata[current_ydata == current_ydata]
@@ -1538,12 +1557,16 @@ class MeasureAnnotator(SignalAnnotator):
1538
1557
  yvalues.extend(ydataNaN)
1539
1558
  current_yvalues.append(current_ydata)
1540
1559
  all_yvalues.append(all_ydata)
1560
+ range_values.extend(all_ydata)
1541
1561
  labels.append(signal_choice)
1542
1562
 
1543
1563
  self.cell_ax.clear()
1544
1564
 
1545
1565
  if len(yvalues) > 0:
1546
- self.cell_ax.boxplot(all_yvalues, showfliers=self.show_fliers)
1566
+ try:
1567
+ self.cell_ax.boxplot(all_yvalues, showfliers=self.show_fliers)
1568
+ except Exception as e:
1569
+ print(f"{e=}")
1547
1570
  ylim = self.cell_ax.get_ylim()
1548
1571
  self.cell_ax.set_ylim(ylim)
1549
1572
  x_pos = np.arange(len(all_yvalues)) + 1
@@ -1556,6 +1579,20 @@ class MeasureAnnotator(SignalAnnotator):
1556
1579
 
1557
1580
  self.cell_ax.plot(x_pos, yvalues, marker='H', linestyle='None', color=tab10.colors[3], alpha=1)
1558
1581
 
1582
+ range_values = np.array(range_values)
1583
+ if len(range_values[range_values==range_values])>0:
1584
+
1585
+ if len(range_values[range_values>0])>0:
1586
+ self.value_magnitude = np.nanmin(range_values[range_values>0]) - 0.03*(np.nanmax(range_values[range_values>0]) - np.nanmin(range_values[range_values>0]))
1587
+ else:
1588
+ self.value_magnitude = 1
1589
+
1590
+ self.non_log_ymin = np.nanmin(range_values) - 0.03*(np.nanmax(range_values) - np.nanmin(range_values))
1591
+ self.non_log_ymax = np.nanmax(range_values) + 0.03*(np.nanmax(range_values) - np.nanmin(range_values))
1592
+ if self.cell_ax.get_yscale()=='linear':
1593
+ self.cell_ax.set_ylim(self.non_log_ymin, self.non_log_ymax)
1594
+ else:
1595
+ self.cell_ax.set_ylim(self.value_magnitude, self.non_log_ymax)
1559
1596
 
1560
1597
  else:
1561
1598
  self.cell_ax.text(0.5, 0.5, "No data available", horizontalalignment='center',
@@ -1661,7 +1698,7 @@ class MeasureAnnotator(SignalAnnotator):
1661
1698
 
1662
1699
  """
1663
1700
 
1664
- self.button_widget = QWidget()
1701
+ self.button_widget = CelldetectiveWidget()
1665
1702
  main_layout = QHBoxLayout()
1666
1703
  self.button_widget.setLayout(main_layout)
1667
1704
 
@@ -1684,14 +1721,12 @@ class MeasureAnnotator(SignalAnnotator):
1684
1721
  self.class_cols = np.array([c.startswith('group') or c.startswith('status') for c in list(self.df_tracks.columns)])
1685
1722
  self.class_cols = list(cols[self.class_cols])
1686
1723
 
1687
- try:
1688
- self.class_cols.remove('group_id')
1689
- except Exception:
1690
- pass
1691
- try:
1692
- self.class_cols.remove('group_color')
1693
- except Exception:
1694
- pass
1724
+ to_remove = ['group_id','group_color','class_id','class_color']
1725
+ for col in to_remove:
1726
+ try:
1727
+ self.class_cols.remove(col)
1728
+ except Exception:
1729
+ pass
1695
1730
 
1696
1731
  self.class_choice_cb.addItems(self.class_cols)
1697
1732
  self.class_choice_cb.currentIndexChanged.connect(self.changed_class)
@@ -1878,7 +1913,7 @@ class MeasureAnnotator(SignalAnnotator):
1878
1913
 
1879
1914
  self.contrast_slider.setSingleStep(0.001)
1880
1915
  self.contrast_slider.setTickInterval(0.001)
1881
- self.contrast_slider.setOrientation(1)
1916
+ self.contrast_slider.setOrientation(Qt.Horizontal)
1882
1917
  self.contrast_slider.setRange(
1883
1918
  *[np.nanpercentile(self.img, 0.001), np.nanpercentile(self.img, 99.999)])
1884
1919
  self.contrast_slider.setValue(
@@ -1889,7 +1924,7 @@ class MeasureAnnotator(SignalAnnotator):
1889
1924
  self.right_panel.addLayout(contrast_hbox, 5)
1890
1925
  self.alpha_slider = QLabeledDoubleSlider()
1891
1926
  self.alpha_slider.setSingleStep(0.001)
1892
- self.alpha_slider.setOrientation(1)
1927
+ self.alpha_slider.setOrientation(Qt.Horizontal)
1893
1928
  self.alpha_slider.setRange(0, 1)
1894
1929
  self.alpha_slider.setValue(self.current_alpha)
1895
1930
  self.alpha_slider.setDecimals(3)
@@ -1949,7 +1984,7 @@ class MeasureAnnotator(SignalAnnotator):
1949
1984
  print(f"Error {e}...")
1950
1985
 
1951
1986
  def set_next_frame(self):
1952
-
1987
+
1953
1988
  self.current_frame = self.current_frame + 1
1954
1989
  if self.current_frame > self.len_movie - 1:
1955
1990
  self.current_frame == self.len_movie - 1
@@ -1958,7 +1993,7 @@ class MeasureAnnotator(SignalAnnotator):
1958
1993
  self.start_btn.setShortcut(QKeySequence("f"))
1959
1994
 
1960
1995
  def set_previous_frame(self):
1961
-
1996
+
1962
1997
  self.current_frame = self.current_frame - 1
1963
1998
  if self.current_frame < 0:
1964
1999
  self.current_frame == 0
@@ -1990,8 +2025,10 @@ class MeasureAnnotator(SignalAnnotator):
1990
2025
  self.class_choice_cb.clear()
1991
2026
  cols = np.array(self.df_tracks.columns)
1992
2027
  self.class_cols = np.array([c.startswith('group') for c in list(self.df_tracks.columns)])
2028
+
1993
2029
  self.class_cols = list(cols[self.class_cols])
1994
2030
  self.class_cols.remove('group_color')
2031
+
1995
2032
  self.class_choice_cb.addItems(self.class_cols)
1996
2033
  idx = self.class_choice_cb.findText(self.target_class)
1997
2034
  self.status_name = self.target_class
@@ -2038,7 +2075,7 @@ class MeasureAnnotator(SignalAnnotator):
2038
2075
  def create_new_event_class(self):
2039
2076
 
2040
2077
  # display qwidget to name the event
2041
- self.newClassWidget = QWidget()
2078
+ self.newClassWidget = CelldetectiveWidget()
2042
2079
  self.newClassWidget.setWindowTitle('Create new characteristic group')
2043
2080
 
2044
2081
  layout = QVBoxLayout()
@@ -2105,6 +2142,7 @@ class MeasureAnnotator(SignalAnnotator):
2105
2142
  self.fcanvas.canvas.draw()
2106
2143
 
2107
2144
  def assign_color_state(self, state):
2145
+
2108
2146
  if np.isnan(state):
2109
2147
  state = "nan"
2110
2148
  return self.state_color_map[state]
@@ -2133,23 +2171,23 @@ class MeasureAnnotator(SignalAnnotator):
2133
2171
  return (self.im, self.status_scatter,self.im_mask,)
2134
2172
 
2135
2173
  def compute_status_and_colors(self):
2136
- print('compute status and colors!')
2174
+
2137
2175
  if self.class_choice_cb.currentText() == '':
2138
2176
  self.status_name=self.target_class
2139
2177
  else:
2140
2178
  self.status_name = self.class_choice_cb.currentText()
2141
2179
 
2142
- print(f'{self.status_name=}')
2143
2180
  if self.status_name not in self.df_tracks.columns:
2144
- print('not in df, make column')
2181
+ print('Creating a new status for visualization...')
2145
2182
  self.make_status_column()
2146
2183
  else:
2184
+ print(f'Generating per-state colors for the status "{self.status_name}"...')
2147
2185
  all_states = self.df_tracks.loc[:, self.status_name].tolist()
2148
2186
  all_states = np.array(all_states)
2149
2187
  self.state_color_map = color_from_state(all_states, recently_modified=False)
2150
- print(f'{self.state_color_map=}')
2188
+ print(f'Color mapping for "{self.status_name}":')
2189
+ pretty_table(self.state_color_map)
2151
2190
  self.df_tracks['group_color'] = self.df_tracks[self.status_name].apply(self.assign_color_state)
2152
- print(self.df_tracks['group_color'])
2153
2191
 
2154
2192
  def del_event_class(self):
2155
2193
 
@@ -2209,16 +2247,23 @@ class MeasureAnnotator(SignalAnnotator):
2209
2247
  self.df_tracks = self.df_tracks.sort_values(by=['ID', 'FRAME'])
2210
2248
 
2211
2249
  cols = np.array(self.df_tracks.columns)
2212
- self.class_cols = np.array([c.startswith('group') for c in list(self.df_tracks.columns)])
2250
+ self.class_cols = np.array([c.startswith('group') or c.startswith('class') for c in list(self.df_tracks.columns)])
2213
2251
  self.class_cols = list(cols[self.class_cols])
2214
- try:
2215
- self.class_cols.remove('class_id')
2216
- except:
2217
- pass
2218
- try:
2219
- self.class_cols.remove('group_color')
2220
- except:
2221
- pass
2252
+
2253
+ to_remove = ['class_id','group_color','class_color']
2254
+ for col in to_remove:
2255
+ try:
2256
+ self.class_cols.remove(col)
2257
+ except:
2258
+ pass
2259
+ # try:
2260
+ # self.class_cols.remove('class_id')
2261
+ # except:
2262
+ # pass
2263
+ # try:
2264
+ # self.class_cols.remove('group_color')
2265
+ # except:
2266
+ # pass
2222
2267
  if len(self.class_cols) > 0:
2223
2268
  self.status = self.class_cols[0]
2224
2269
 
@@ -2278,7 +2323,7 @@ class MeasureAnnotator(SignalAnnotator):
2278
2323
  # print(self.columns_to_rescale)
2279
2324
 
2280
2325
  cols_to_remove = ['group', 'group_color', 'status', 'status_color', 'class_color', 'TRACK_ID', 'FRAME',
2281
- 'x_anim', 'y_anim', 't',
2326
+ 'x_anim', 'y_anim', 't','dummy','group_color',
2282
2327
  'state', 'generation', 'root', 'parent', 'class_id', 'class', 't0', 'POSITION_X',
2283
2328
  'POSITION_Y', 'position', 'well', 'well_index', 'well_name', 'pos_name', 'index',
2284
2329
  'concentration', 'cell_type', 'antibody', 'pharmaceutical_agent', 'ID'] + self.class_cols
@@ -2340,6 +2385,10 @@ class MeasureAnnotator(SignalAnnotator):
2340
2385
  self.modify()
2341
2386
 
2342
2387
  self.draw_frame(self.current_frame)
2388
+ self.vmin = self.contrast_slider.value()[0]
2389
+ self.vmax = self.contrast_slider.value()[1]
2390
+ self.im.set_clim(vmin=self.vmin, vmax=self.vmax)
2391
+
2343
2392
  self.fcanvas.canvas.draw()
2344
2393
  self.plot_signals()
2345
2394
 
@@ -2435,7 +2484,6 @@ class MeasureAnnotator(SignalAnnotator):
2435
2484
  all_states = self.df_tracks.loc[:, self.status_name].tolist()
2436
2485
  all_states = np.array(all_states)
2437
2486
  self.state_color_map = color_from_state(all_states, recently_modified=False)
2438
- print(f'{self.state_color_map=}')
2439
2487
 
2440
2488
  self.df_tracks['group_color'] = self.df_tracks[self.status_name].apply(self.assign_color_state)
2441
2489
 
@@ -2530,13 +2578,26 @@ class MeasureAnnotator(SignalAnnotator):
2530
2578
  """
2531
2579
 
2532
2580
  # self.clear_post_threshold_options()
2533
-
2581
+ self.previous_channel = self.current_channel
2534
2582
  self.current_channel = self.choose_channel.currentIndex()
2535
2583
 
2536
2584
  t = int(self.frame_slider.value())
2537
2585
  idx = t * self.nbr_channels + self.current_channel
2538
2586
  self.img = load_frames(idx, self.stack_path, normalize_input=False)
2587
+
2588
+ if self.previous_channel != self.current_channel:
2589
+ # reinitialize intensity bounds
2590
+ epsilon = 0.01
2591
+ self.observed_min_intensity = 0
2592
+ self.observed_max_intensity = 0 + epsilon
2593
+
2539
2594
  if self.img is not None:
2595
+ max_img = np.nanmax(self.img)
2596
+ min_img = np.nanmin(self.img)
2597
+ if max_img > self.observed_max_intensity:
2598
+ self.observed_max_intensity = max_img
2599
+ if min_img < self.observed_min_intensity:
2600
+ self.observed_min_intensity = min_img
2540
2601
  self.refresh_imshow()
2541
2602
  # self.redo_histogram()
2542
2603
  else:
@@ -2550,13 +2611,19 @@ class MeasureAnnotator(SignalAnnotator):
2550
2611
 
2551
2612
  """
2552
2613
 
2553
- self.vmin = np.nanpercentile(self.img.flatten(), 1)
2554
- self.vmax = np.nanpercentile(self.img.flatten(), 99.)
2614
+ if self.previous_channel != self.current_channel:
2555
2615
 
2556
- self.contrast_slider.disconnect()
2557
- self.contrast_slider.setRange(np.nanmin(self.img), np.nanmax(self.img))
2558
- self.contrast_slider.setValue([self.vmin, self.vmax])
2559
- self.contrast_slider.valueChanged.connect(self.contrast_slider_action)
2616
+ self.vmin = np.nanpercentile(self.img.flatten(), 1)
2617
+ self.vmax = np.nanpercentile(self.img.flatten(), 99.)
2618
+
2619
+ self.contrast_slider.disconnect()
2620
+ self.contrast_slider.setRange(np.nanmin(self.img), np.nanmax(self.img))
2621
+ self.contrast_slider.setValue([self.vmin, self.vmax])
2622
+ self.contrast_slider.valueChanged.connect(self.contrast_slider_action)
2623
+ else:
2624
+ #self.contrast_slider.disconnect()
2625
+ self.contrast_slider.setRange(self.observed_min_intensity, self.observed_max_intensity)
2626
+ #self.contrast_slider.valueChanged.connect(self.contrast_slider_action)
2560
2627
 
2561
2628
  self.im.set_data(self.img)
2562
2629