celldetective 1.3.9.post5__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.
- celldetective/__init__.py +0 -3
- celldetective/_version.py +1 -1
- celldetective/events.py +2 -4
- celldetective/extra_properties.py +132 -0
- celldetective/gui/InitWindow.py +33 -45
- celldetective/gui/__init__.py +1 -0
- celldetective/gui/about.py +19 -15
- celldetective/gui/analyze_block.py +34 -19
- celldetective/gui/base_components.py +23 -0
- celldetective/gui/btrack_options.py +26 -34
- celldetective/gui/classifier_widget.py +68 -81
- celldetective/gui/configure_new_exp.py +113 -17
- celldetective/gui/control_panel.py +68 -141
- celldetective/gui/generic_signal_plot.py +9 -12
- celldetective/gui/gui_utils.py +49 -21
- celldetective/gui/json_readers.py +5 -4
- celldetective/gui/layouts.py +246 -22
- celldetective/gui/measurement_options.py +32 -17
- celldetective/gui/neighborhood_options.py +10 -13
- celldetective/gui/plot_measurements.py +21 -17
- celldetective/gui/plot_signals_ui.py +125 -72
- celldetective/gui/process_block.py +180 -123
- celldetective/gui/processes/compute_neighborhood.py +594 -0
- celldetective/gui/processes/measure_cells.py +5 -0
- celldetective/gui/processes/segment_cells.py +27 -6
- celldetective/gui/processes/track_cells.py +6 -0
- celldetective/gui/retrain_segmentation_model_options.py +12 -20
- celldetective/gui/retrain_signal_model_options.py +57 -56
- celldetective/gui/seg_model_loader.py +21 -62
- celldetective/gui/signal_annotator.py +129 -70
- celldetective/gui/signal_annotator2.py +431 -635
- celldetective/gui/signal_annotator_options.py +8 -11
- celldetective/gui/survival_ui.py +49 -95
- celldetective/gui/tableUI.py +28 -25
- celldetective/gui/thresholds_gui.py +617 -1221
- celldetective/gui/viewers.py +106 -39
- celldetective/gui/workers.py +9 -3
- celldetective/io.py +57 -20
- celldetective/measure.py +63 -27
- celldetective/neighborhood.py +342 -268
- celldetective/preprocessing.py +25 -17
- celldetective/relative_measurements.py +50 -29
- celldetective/scripts/analyze_signals.py +4 -1
- celldetective/scripts/measure_relative.py +4 -1
- celldetective/scripts/segment_cells.py +0 -6
- celldetective/scripts/track_cells.py +3 -1
- celldetective/scripts/train_segmentation_model.py +7 -4
- celldetective/signals.py +29 -14
- celldetective/tracking.py +7 -2
- celldetective/utils.py +36 -8
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/METADATA +24 -16
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/RECORD +57 -55
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/WHEEL +1 -1
- tests/test_qt.py +21 -21
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/entry_points.txt +0 -0
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info/licenses}/LICENSE +0 -0
- {celldetective-1.3.9.post5.dist-info → celldetective-1.4.0.dist-info}/top_level.txt +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import
|
|
2
|
-
QPushButton,
|
|
1
|
+
from PyQt5.QtWidgets import QComboBox, QLabel, QRadioButton, QLineEdit, QFileDialog, QApplication, \
|
|
2
|
+
QPushButton, QVBoxLayout, QHBoxLayout, QMessageBox, QShortcut, QLineEdit, \
|
|
3
3
|
QButtonGroup
|
|
4
4
|
from PyQt5.QtCore import Qt, QSize
|
|
5
5
|
from PyQt5.QtGui import QKeySequence
|
|
6
|
-
from celldetective.gui import Styles
|
|
6
|
+
from celldetective.gui import Styles, CelldetectiveMainWindow, CelldetectiveWidget
|
|
7
7
|
from celldetective.gui.gui_utils import center_window
|
|
8
8
|
from superqt import QLabeledDoubleRangeSlider, QSearchableComboBox
|
|
9
9
|
from celldetective.utils import extract_experiment_channels, get_software_location, _get_img_num_per_channel
|
|
10
|
-
from celldetective.io import auto_load_number_of_frames, load_frames, get_experiment_metadata
|
|
10
|
+
from celldetective.io import auto_load_number_of_frames, load_frames, get_experiment_metadata, get_experiment_labels
|
|
11
11
|
from celldetective.gui.gui_utils import FigureCanvas, color_from_status, color_from_class
|
|
12
12
|
import json
|
|
13
13
|
import numpy as np
|
|
@@ -26,7 +26,7 @@ from sklearn.preprocessing import MinMaxScaler
|
|
|
26
26
|
from functools import partial
|
|
27
27
|
from pandas.api.types import is_numeric_dtype
|
|
28
28
|
|
|
29
|
-
class SignalAnnotator2(
|
|
29
|
+
class SignalAnnotator2(CelldetectiveMainWindow):
|
|
30
30
|
|
|
31
31
|
"""
|
|
32
32
|
UI to set tracking parameters for bTrack.
|
|
@@ -41,14 +41,12 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
41
41
|
|
|
42
42
|
self.pos = self.parent_window.parent_window.pos
|
|
43
43
|
self.exp_dir = self.parent_window.exp_dir
|
|
44
|
-
|
|
44
|
+
self.populations = self.parent_window.parent_window.populations
|
|
45
45
|
|
|
46
46
|
self.soft_path = get_software_location()
|
|
47
47
|
self.recently_modified = False
|
|
48
48
|
self.n_signals = 3
|
|
49
|
-
|
|
50
|
-
self.effector_selection = []
|
|
51
|
-
|
|
49
|
+
|
|
52
50
|
self.reference_selection = []
|
|
53
51
|
self.neighbor_selection = []
|
|
54
52
|
self.pair_selection = []
|
|
@@ -61,25 +59,27 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
61
59
|
self.neighbor_track_of_interest = None
|
|
62
60
|
self.value_magnitude = 1
|
|
63
61
|
|
|
64
|
-
self.cols_to_remove = ['
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
self.cols_to_remove = ['group', 'group_color', 'status', 'status_color', 'class_color', 'TRACK_ID', 'FRAME',
|
|
63
|
+
'x_anim', 'y_anim', 't','dummy','group_color',
|
|
64
|
+
'state', 'generation', 'root', 'parent', 'class_id', 'class', 't0', 'POSITION_X',
|
|
65
|
+
'POSITION_Y', 'position', 'well', 'well_index', 'well_name', 'pos_name', 'index',
|
|
66
|
+
'concentration', 'cell_type', 'antibody', 'pharmaceutical_agent', 'ID', "REFERENCE_ID", "NEIGHBOR_ID", "reference_population", "neighbor_population"]
|
|
67
|
+
|
|
69
68
|
meta = get_experiment_metadata(self.exp_dir)
|
|
70
69
|
if meta is not None:
|
|
71
70
|
keys = list(meta.keys())
|
|
72
|
-
self.cols_to_remove.extend(keys)
|
|
71
|
+
self.cols_to_remove.extend(keys)
|
|
72
|
+
|
|
73
|
+
labels = get_experiment_labels(self.exp_dir)
|
|
74
|
+
if labels is not None:
|
|
75
|
+
keys = list(labels.keys())
|
|
76
|
+
self.cols_to_remove.extend(labels)
|
|
77
|
+
|
|
73
78
|
|
|
74
79
|
# Read instructions from target block for now...
|
|
75
80
|
self.mode = "neighborhood"
|
|
76
81
|
self.instructions_path = self.exp_dir + os.sep.join(['configs', 'signal_annotator_config_neighborhood.json'])
|
|
77
82
|
|
|
78
|
-
# default params
|
|
79
|
-
self.target_class_name = 'class'
|
|
80
|
-
self.target_time_name = 't0'
|
|
81
|
-
self.target_status_name = 'status'
|
|
82
|
-
|
|
83
83
|
center_window(self)
|
|
84
84
|
|
|
85
85
|
# Locate stack
|
|
@@ -87,21 +87,15 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
87
87
|
self.load_annotator_config()
|
|
88
88
|
|
|
89
89
|
# Locate tracks
|
|
90
|
-
self.
|
|
91
|
-
self.
|
|
92
|
-
|
|
93
|
-
self.dataframes = {
|
|
94
|
-
'targets': self.df_targets,
|
|
95
|
-
'effectors': self.df_effectors,
|
|
96
|
-
}
|
|
90
|
+
self.locate_all_tracks()
|
|
91
|
+
self.extract_scatter_from_trajectories()
|
|
97
92
|
|
|
98
93
|
self.neighborhood_cols = []
|
|
99
|
-
|
|
100
|
-
self.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
print(f"The following neighborhoods were detected: {self.neighborhood_cols=}")
|
|
94
|
+
for pop in self.dataframes.keys():
|
|
95
|
+
if self.dataframes[pop] is not None:
|
|
96
|
+
self.neighborhood_cols.extend([f'{pop}_ref_'+c for c in list(self.dataframes[pop].columns) if c.startswith('neighborhood')])
|
|
97
|
+
print(f"The following neighborhoods were detected: {self.neighborhood_cols=}...")
|
|
98
|
+
|
|
105
99
|
self.locate_relative_tracks()
|
|
106
100
|
|
|
107
101
|
# Prepare stack
|
|
@@ -132,8 +126,6 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
132
126
|
|
|
133
127
|
#self.cell_fcanvas.setMinimumHeight(int(0.3*self.screen_height))
|
|
134
128
|
|
|
135
|
-
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
136
|
-
|
|
137
129
|
def resizeEvent(self, event):
|
|
138
130
|
|
|
139
131
|
super().resizeEvent(event)
|
|
@@ -149,7 +141,7 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
149
141
|
|
|
150
142
|
"""
|
|
151
143
|
|
|
152
|
-
self.button_widget =
|
|
144
|
+
self.button_widget = CelldetectiveWidget()
|
|
153
145
|
main_layout = QHBoxLayout()
|
|
154
146
|
main_layout.setSpacing(30)
|
|
155
147
|
|
|
@@ -237,11 +229,11 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
237
229
|
reference_layout.addLayout(self.cell_events_hbox)
|
|
238
230
|
|
|
239
231
|
neighbor_layout = QVBoxLayout()
|
|
240
|
-
neighbor_layout.addWidget(self.neighbor_cell_info)
|
|
232
|
+
neighbor_layout.addWidget(self.neighbor_cell_info, alignment=Qt.AlignRight)
|
|
241
233
|
neighbor_layout.addLayout(self.neigh_cell_events_hbox)
|
|
242
234
|
|
|
243
235
|
self.cell_info_hbox.addLayout(reference_layout, 33)
|
|
244
|
-
self.cell_info_hbox.addWidget(self.pair_info, 33)
|
|
236
|
+
self.cell_info_hbox.addWidget(self.pair_info, 33, alignment=Qt.AlignCenter)
|
|
245
237
|
self.cell_info_hbox.addLayout(neighbor_layout, 33)
|
|
246
238
|
|
|
247
239
|
self.left_panel.addLayout(self.cell_info_hbox)
|
|
@@ -419,8 +411,6 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
419
411
|
|
|
420
412
|
|
|
421
413
|
self.right_panel.addLayout(animation_buttons_box, 5)
|
|
422
|
-
|
|
423
|
-
|
|
424
414
|
self.right_panel.addWidget(self.fcanvas, 90)
|
|
425
415
|
|
|
426
416
|
if not self.rgb_mode:
|
|
@@ -429,7 +419,7 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
429
419
|
self.contrast_slider = QLabeledDoubleRangeSlider()
|
|
430
420
|
# self.contrast_slider.setSingleStep(0.001)
|
|
431
421
|
# self.contrast_slider.setTickInterval(0.001)
|
|
432
|
-
self.contrast_slider.setOrientation(
|
|
422
|
+
self.contrast_slider.setOrientation(Qt.Horizontal)
|
|
433
423
|
print('range: ', [np.nanpercentile(self.stack.flatten(), 0.001), np.nanpercentile(self.stack.flatten(), 99.999)])
|
|
434
424
|
self.contrast_slider.setRange(
|
|
435
425
|
*[np.nanpercentile(self.stack, 0.001), np.nanpercentile(self.stack, 99.999)])
|
|
@@ -444,6 +434,7 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
444
434
|
main_layout.addLayout(self.right_panel, 65)
|
|
445
435
|
self.button_widget.adjustSize()
|
|
446
436
|
self.compute_status_and_colors_reference()
|
|
437
|
+
self.compute_status_and_colors_neighbor()
|
|
447
438
|
|
|
448
439
|
self.extract_relevant_events()
|
|
449
440
|
|
|
@@ -486,53 +477,6 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
486
477
|
self.neighbor_event_choice_cb.addItems(neighbor_class_cols)
|
|
487
478
|
self.neighbor_event_choice_cb.currentIndexChanged.connect(self.compute_status_and_colors_neighbor)
|
|
488
479
|
|
|
489
|
-
|
|
490
|
-
def del_target_event_class(self):
|
|
491
|
-
|
|
492
|
-
msgBox = QMessageBox()
|
|
493
|
-
msgBox.setIcon(QMessageBox.Warning)
|
|
494
|
-
msgBox.setText(f"You are about to delete event class {self.target_class_choice_cb.currentText()}. The associated time and\nstatus will also be deleted. Do you still want to proceed?")
|
|
495
|
-
msgBox.setWindowTitle("Warning")
|
|
496
|
-
msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
|
|
497
|
-
returnValue = msgBox.exec()
|
|
498
|
-
if returnValue == QMessageBox.No:
|
|
499
|
-
return None
|
|
500
|
-
else:
|
|
501
|
-
class_to_delete = self.target_class_choice_cb.currentText()
|
|
502
|
-
time_to_delete = class_to_delete.replace('class','t')
|
|
503
|
-
status_to_delete = class_to_delete.replace('class', 'status')
|
|
504
|
-
cols_to_delete = [class_to_delete, time_to_delete, status_to_delete]
|
|
505
|
-
for c in cols_to_delete:
|
|
506
|
-
try:
|
|
507
|
-
self.df_targets = self.df_targets.drop([c], axis=1)
|
|
508
|
-
except Exception as e:
|
|
509
|
-
print(e)
|
|
510
|
-
item_idx = self.target_class_choice_cb.findText(class_to_delete)
|
|
511
|
-
self.target_class_choice_cb.removeItem(item_idx)
|
|
512
|
-
|
|
513
|
-
def del_effector_event_class(self):
|
|
514
|
-
|
|
515
|
-
msgBox = QMessageBox()
|
|
516
|
-
msgBox.setIcon(QMessageBox.Warning)
|
|
517
|
-
msgBox.setText(f"You are about to delete event class {self.effector_class_choice_cb.currentText()}. The associated time and\nstatus will also be deleted. Do you still want to proceed?")
|
|
518
|
-
msgBox.setWindowTitle("Warning")
|
|
519
|
-
msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
|
|
520
|
-
returnValue = msgBox.exec()
|
|
521
|
-
if returnValue == QMessageBox.No:
|
|
522
|
-
return None
|
|
523
|
-
else:
|
|
524
|
-
class_to_delete = self.effector_class_choice_cb.currentText()
|
|
525
|
-
time_to_delete = class_to_delete.replace('class','t')
|
|
526
|
-
status_to_delete = class_to_delete.replace('class', 'status')
|
|
527
|
-
cols_to_delete = [class_to_delete, time_to_delete, status_to_delete]
|
|
528
|
-
for c in cols_to_delete:
|
|
529
|
-
try:
|
|
530
|
-
self.df_effectors = self.df_effectors.drop([c], axis=1)
|
|
531
|
-
except Exception as e:
|
|
532
|
-
print(e)
|
|
533
|
-
item_idx = self.effector_class_choice_cb.findText(class_to_delete)
|
|
534
|
-
self.effector_class_choice_cb.removeItem(item_idx)
|
|
535
|
-
|
|
536
480
|
def del_relative_event_class(self):
|
|
537
481
|
|
|
538
482
|
msgBox = QMessageBox()
|
|
@@ -557,55 +501,30 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
557
501
|
self.relative_class_choice_cb.removeItem(item_idx)
|
|
558
502
|
|
|
559
503
|
def update_cell_events(self):
|
|
560
|
-
if 'self' in self.current_neighborhood:
|
|
561
|
-
try:
|
|
562
|
-
self.neighbor_event_choice_cb.hide()
|
|
563
|
-
self.neigh_lab.hide()
|
|
564
|
-
except:
|
|
565
|
-
pass
|
|
566
|
-
self.reference_event_choice_cb.disconnect()
|
|
567
|
-
self.reference_event_choice_cb.clear()
|
|
568
|
-
if self.reference_population=='targets':
|
|
569
|
-
self.reference_event_choice_cb.addItems(self.target_class_cols)
|
|
570
|
-
self.reference_event_choice_cb.currentIndexChanged.connect(self.compute_status_and_colors_reference)
|
|
571
|
-
else:
|
|
572
|
-
self.reference_event_choice_cb.addItems(self.effector_class_cols)
|
|
573
|
-
self.reference_event_choice_cb.currentIndexChanged.connect(self.compute_status_and_colors_neighbor)
|
|
574
|
-
|
|
575
|
-
else:
|
|
576
|
-
try:
|
|
577
|
-
self.neighbor_event_choice_cb.show()
|
|
578
|
-
self.neigh_lab.show()
|
|
579
|
-
except:
|
|
580
|
-
pass
|
|
581
|
-
self.reference_event_choice_cb.disconnect()
|
|
582
|
-
self.reference_event_choice_cb.clear()
|
|
583
504
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
505
|
+
try:
|
|
506
|
+
self.neighbor_event_choice_cb.show()
|
|
507
|
+
self.neigh_lab.show()
|
|
508
|
+
except:
|
|
509
|
+
pass
|
|
587
510
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
511
|
+
self.reference_event_choice_cb.disconnect()
|
|
512
|
+
self.reference_event_choice_cb.clear()
|
|
513
|
+
self.reference_event_choice_cb.addItems(self.class_cols_per_pop[self.reference_population])
|
|
514
|
+
self.reference_event_choice_cb.currentIndexChanged.connect(self.compute_status_and_colors_reference)
|
|
591
515
|
|
|
516
|
+
if not 'self' in self.current_neighborhood:
|
|
592
517
|
self.neighbor_event_choice_cb.disconnect()
|
|
593
518
|
self.neighbor_event_choice_cb.clear()
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
self.neighbor_event_choice_cb.addItems(self.target_class_cols)
|
|
597
|
-
self.neighbor_event_choice_cb.currentIndexChanged.connect(self.compute_status_and_colors_reference)
|
|
598
|
-
|
|
599
|
-
else:
|
|
600
|
-
self.neighbor_event_choice_cb.addItems(self.effector_class_cols)
|
|
601
|
-
self.neighbor_event_choice_cb.currentIndexChanged.connect(self.compute_status_and_colors_neighbor)
|
|
519
|
+
self.neighbor_event_choice_cb.addItems(self.class_cols_per_pop[self.neighbor_population])
|
|
520
|
+
self.neighbor_event_choice_cb.currentIndexChanged.connect(self.compute_status_and_colors_reference)
|
|
602
521
|
|
|
603
522
|
|
|
604
523
|
|
|
605
524
|
def create_new_relative_event_class(self):
|
|
606
525
|
|
|
607
526
|
# display qwidget to name the event
|
|
608
|
-
self.newClassWidget =
|
|
527
|
+
self.newClassWidget = CelldetectiveWidget()
|
|
609
528
|
self.newClassWidget.setWindowTitle('Create new event class')
|
|
610
529
|
|
|
611
530
|
layout = QVBoxLayout()
|
|
@@ -723,12 +642,18 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
723
642
|
df_reference['status_color'] = [color_from_status(i) for i in df_reference[self.reference_status_name].to_numpy()]
|
|
724
643
|
df_reference['class_color'] = [color_from_class(i) for i in df_reference[self.reference_class_name].to_numpy()]
|
|
725
644
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
645
|
+
self.refresh_scatter_from_trajectories(self.reference_population, clear_selection=False)
|
|
646
|
+
|
|
647
|
+
if self.reference_track_of_interest is not None:
|
|
648
|
+
t_reference = df_reference.loc[
|
|
649
|
+
df_reference['TRACK_ID'] == self.reference_track_of_interest, self.reference_time_name].values
|
|
650
|
+
if len(t_reference) > 0:
|
|
651
|
+
t_reference = t_reference[0]
|
|
652
|
+
ymin, ymax = self.cell_ax.get_ylim()
|
|
653
|
+
self.line_dt_reference.set_xdata([t_reference, t_reference])
|
|
654
|
+
self.line_dt_reference.set_ydata([ymin,ymax])
|
|
655
|
+
self.cell_fcanvas.canvas.draw()
|
|
656
|
+
|
|
732
657
|
def compute_status_and_colors_neighbor(self):
|
|
733
658
|
|
|
734
659
|
df_neighbors = self.dataframes[self.neighbor_population]
|
|
@@ -767,10 +692,8 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
767
692
|
df_neighbors['status_color'] = [color_from_status(i) for i in df_neighbors[self.neighbor_status_name].to_numpy()]
|
|
768
693
|
df_neighbors['class_color'] = [color_from_class(i) for i in df_neighbors[self.neighbor_class_name].to_numpy()]
|
|
769
694
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
else:
|
|
773
|
-
self.extract_scatter_from_effector_trajectories()
|
|
695
|
+
self.refresh_scatter_from_trajectories(self.neighbor_population)
|
|
696
|
+
|
|
774
697
|
|
|
775
698
|
def compute_status_and_colors_pair(self):
|
|
776
699
|
|
|
@@ -845,13 +768,8 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
845
768
|
if len(self.pair_selection) > 0:
|
|
846
769
|
self.cancel_pair_selection()
|
|
847
770
|
|
|
848
|
-
if self.df_targets is not None:
|
|
849
|
-
self.target_selection = []
|
|
850
|
-
if self.df_effectors is not None:
|
|
851
|
-
self.effector_selection = []
|
|
852
|
-
|
|
853
771
|
_, _, neighbor_colors, initial_neighbor_colors = self.get_neighbor_sets()
|
|
854
|
-
_, _, reference_colors, initial_reference_colors = self.get_reference_sets()
|
|
772
|
+
_, _, _, reference_colors, initial_reference_colors = self.get_reference_sets()
|
|
855
773
|
|
|
856
774
|
for k, (t,idx) in enumerate(zip(self.neighbor_loc_t, self.neighbor_loc_idx)):
|
|
857
775
|
neighbor_colors[t][idx,0] = initial_neighbor_colors[k][0]
|
|
@@ -936,8 +854,17 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
936
854
|
if option!=0:
|
|
937
855
|
self.lines[i].set_xdata([])
|
|
938
856
|
self.lines[i].set_ydata([])
|
|
857
|
+
|
|
939
858
|
self.line_dt.set_xdata([])
|
|
940
859
|
self.line_dt.set_ydata([])
|
|
860
|
+
|
|
861
|
+
if self.reference_track_of_interest is None:
|
|
862
|
+
self.line_dt_reference.set_xdata([])
|
|
863
|
+
self.line_dt_reference.set_ydata([])
|
|
864
|
+
|
|
865
|
+
self.line_dt_neighbor.set_xdata([])
|
|
866
|
+
self.line_dt_neighbor.set_ydata([])
|
|
867
|
+
|
|
941
868
|
self.lines[i].set_label('')
|
|
942
869
|
|
|
943
870
|
self.correct_btn.setEnabled(False)
|
|
@@ -949,15 +876,15 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
949
876
|
# Plot the new time
|
|
950
877
|
t0 = -1
|
|
951
878
|
if self.event_btn.isChecked():
|
|
952
|
-
try:
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
except Exception as e:
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
879
|
+
# try:
|
|
880
|
+
cclass = 0
|
|
881
|
+
t0 = float(self.time_of_interest_le.text().replace(',', '.'))
|
|
882
|
+
self.line_dt.set_xdata([t0, t0])
|
|
883
|
+
self.cell_fcanvas.canvas.draw_idle()
|
|
884
|
+
# except Exception as e:
|
|
885
|
+
# print(e)
|
|
886
|
+
# t0 = -1
|
|
887
|
+
# cclass = 2
|
|
961
888
|
|
|
962
889
|
elif self.no_event_btn.isChecked():
|
|
963
890
|
cclass = 1
|
|
@@ -1012,8 +939,8 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1012
939
|
# but keep reference
|
|
1013
940
|
|
|
1014
941
|
#self.make_status_column()
|
|
1015
|
-
self.
|
|
1016
|
-
self.
|
|
942
|
+
self.refresh_scatter_from_trajectories(self.reference_population)
|
|
943
|
+
self.refresh_scatter_from_trajectories(self.neighbor_population)
|
|
1017
944
|
|
|
1018
945
|
self.recolor_selection()
|
|
1019
946
|
self.trace_neighbors()
|
|
@@ -1048,234 +975,128 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1048
975
|
self.channels = np.array(self.channels)
|
|
1049
976
|
self.nbr_channels = len(self.channels)
|
|
1050
977
|
|
|
1051
|
-
def
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
self.
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
978
|
+
def locate_all_tracks(self):
|
|
979
|
+
|
|
980
|
+
self.dataframes = {}
|
|
981
|
+
self.class_cols_per_pop = {}
|
|
982
|
+
self.population_columns = {}
|
|
983
|
+
self.MinMaxScaler_per_pop = {}
|
|
984
|
+
|
|
985
|
+
for population in self.populations:
|
|
986
|
+
|
|
987
|
+
population_trajectories_path = self.pos + os.sep.join(['output','tables', f'trajectories_{population}.pkl'])
|
|
988
|
+
if not os.path.exists(population_trajectories_path):
|
|
989
|
+
population_trajectories_path = population_trajectories_path.replace('.pkl','.csv')
|
|
990
|
+
|
|
991
|
+
if not os.path.exists(population_trajectories_path):
|
|
992
|
+
|
|
993
|
+
msgBox = QMessageBox()
|
|
994
|
+
msgBox.setIcon(QMessageBox.Warning)
|
|
995
|
+
msgBox.setText(f"The trajectories for the {population} population cannot be detected...")
|
|
996
|
+
msgBox.setWindowTitle("Warning")
|
|
997
|
+
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
998
|
+
returnValue = msgBox.exec()
|
|
999
|
+
df_population = None
|
|
1073
1000
|
else:
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
self.target_class_cols = [c for c in list(self.df_targets.columns) if c.startswith('class')]
|
|
1080
|
-
|
|
1081
|
-
try:
|
|
1082
|
-
self.target_class_cols.remove('class_id')
|
|
1083
|
-
except:
|
|
1084
|
-
pass
|
|
1085
|
-
try:
|
|
1086
|
-
self.target_class_cols.remove('class_color')
|
|
1087
|
-
except:
|
|
1088
|
-
pass
|
|
1001
|
+
# Load and prep tracks
|
|
1002
|
+
if population_trajectories_path.endswith('.pkl'):
|
|
1003
|
+
df_population = np.load(population_trajectories_path, allow_pickle=True)
|
|
1004
|
+
else:
|
|
1005
|
+
df_population = pd.read_csv(population_trajectories_path)
|
|
1089
1006
|
|
|
1090
|
-
|
|
1007
|
+
df_population = df_population.sort_values(by=['TRACK_ID', 'FRAME'])
|
|
1091
1008
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
suffix = self.target_class_name.replace('class','').replace('_','')
|
|
1095
|
-
if suffix!='':
|
|
1096
|
-
self.target_expected_status+='_'+suffix
|
|
1097
|
-
self.target_expected_time = 't_'+suffix
|
|
1098
|
-
else:
|
|
1099
|
-
self.target_expected_time = 't0'
|
|
1100
|
-
self.target_time_name = self.target_expected_time
|
|
1101
|
-
self.target_status_name = self.target_expected_status
|
|
1102
|
-
else:
|
|
1103
|
-
self.target_class_name = 'class'
|
|
1104
|
-
self.target_time_name = 't0'
|
|
1105
|
-
self.target_status_name = 'status'
|
|
1009
|
+
cols = np.array(df_population.columns)
|
|
1010
|
+
class_cols = [c for c in list(df_population.columns) if c.startswith('class')]
|
|
1106
1011
|
|
|
1107
|
-
if self.target_time_name in self.df_targets.columns and self.target_class_name in self.df_targets.columns and not self.target_status_name in self.df_targets.columns:
|
|
1108
|
-
# only create the status column if it does not exist to not erase static classification results
|
|
1109
|
-
pass
|
|
1110
|
-
#self.make_target_status_column()
|
|
1111
|
-
elif self.target_time_name in self.df_targets.columns and self.target_class_name in self.df_targets.columns:
|
|
1112
|
-
# all good, do nothing
|
|
1113
|
-
pass
|
|
1114
|
-
else:
|
|
1115
|
-
if not self.target_status_name in self.df_targets.columns:
|
|
1116
|
-
self.df_targets[self.target_status_name] = 0
|
|
1117
|
-
self.df_targets['status_color'] = color_from_status(0)
|
|
1118
|
-
self.df_targets['class_color'] = color_from_class(1)
|
|
1119
|
-
|
|
1120
|
-
if not self.target_class_name in self.df_targets.columns:
|
|
1121
|
-
self.df_targets[self.target_class_name] = 1
|
|
1122
|
-
if not self.target_time_name in self.df_targets.columns:
|
|
1123
|
-
self.df_targets[self.target_time_name] = -1
|
|
1124
|
-
|
|
1125
|
-
self.df_targets['status_color'] = color_from_status(2) #[color_from_status(i) for i in self.df_targets[self.target_status_name].to_numpy()]
|
|
1126
|
-
self.df_targets['class_color'] = color_from_status(2) #[color_from_class(i) for i in self.df_targets[self.target_class_name].to_numpy()]
|
|
1127
|
-
|
|
1128
|
-
self.df_targets = self.df_targets.dropna(subset=['POSITION_X', 'POSITION_Y'])
|
|
1129
|
-
self.df_targets['x_anim'] = self.df_targets['POSITION_X'] * self.fraction
|
|
1130
|
-
self.df_targets['y_anim'] = self.df_targets['POSITION_Y'] * self.fraction
|
|
1131
|
-
self.df_targets['x_anim'] = self.df_targets['x_anim'].astype(int)
|
|
1132
|
-
self.df_targets['y_anim'] = self.df_targets['y_anim'].astype(int)
|
|
1133
|
-
|
|
1134
|
-
self.extract_scatter_from_target_trajectories()
|
|
1135
|
-
self.target_track_of_interest = self.df_targets['TRACK_ID'].min()
|
|
1136
|
-
|
|
1137
|
-
self.loc_t = []
|
|
1138
|
-
self.loc_idx = []
|
|
1139
|
-
for t in range(len(self.target_tracks)):
|
|
1140
|
-
indices = np.where(self.target_tracks[t]==self.target_track_of_interest)[0]
|
|
1141
|
-
if len(indices)>0:
|
|
1142
|
-
self.loc_t.append(t)
|
|
1143
|
-
self.loc_idx.append(indices[0])
|
|
1144
|
-
|
|
1145
|
-
self.MinMaxScaler_targets = MinMaxScaler()
|
|
1146
|
-
self.target_columns = list(self.df_targets.columns)
|
|
1147
|
-
cols_to_remove = [c for c in self.cols_to_remove if c in self.target_columns] + self.target_class_cols
|
|
1148
|
-
time_cols = [c for c in self.target_columns if c.startswith('t_')]
|
|
1149
|
-
cols_to_remove += time_cols
|
|
1150
|
-
neigh_cols = [c for c in self.target_columns if c.startswith('neighborhood_')]
|
|
1151
|
-
cols_to_remove += neigh_cols
|
|
1152
|
-
|
|
1153
|
-
for col in cols_to_remove:
|
|
1154
1012
|
try:
|
|
1155
|
-
|
|
1013
|
+
class_cols.remove('class_id')
|
|
1014
|
+
except:
|
|
1015
|
+
pass
|
|
1016
|
+
try:
|
|
1017
|
+
class_cols.remove('class_color')
|
|
1156
1018
|
except:
|
|
1157
1019
|
pass
|
|
1158
1020
|
|
|
1159
|
-
|
|
1160
|
-
self.MinMaxScaler_targets.fit(x)
|
|
1161
|
-
|
|
1162
|
-
def locate_effector_tracks(self):
|
|
1163
|
-
|
|
1164
|
-
population = 'effectors'
|
|
1165
|
-
self.effector_trajectories_path = self.pos + os.sep.join(['output','tables',f'trajectories_{population}.pkl'])
|
|
1166
|
-
if not os.path.exists(self.effector_trajectories_path):
|
|
1167
|
-
self.effector_trajectories_path = self.effector_trajectories_path.replace('.pkl','.csv')
|
|
1168
|
-
|
|
1169
|
-
if not os.path.exists(self.effector_trajectories_path):
|
|
1170
|
-
|
|
1171
|
-
msgBox = QMessageBox()
|
|
1172
|
-
msgBox.setIcon(QMessageBox.Warning)
|
|
1173
|
-
msgBox.setText("The effector trajectories cannot be detected...")
|
|
1174
|
-
msgBox.setWindowTitle("Warning")
|
|
1175
|
-
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
1176
|
-
returnValue = msgBox.exec()
|
|
1177
|
-
self.df_effectors = None
|
|
1178
|
-
else:
|
|
1179
|
-
# Load and prep tracks
|
|
1180
|
-
if self.effector_trajectories_path.endswith('.pkl'):
|
|
1181
|
-
self.df_effectors = np.load(self.effector_trajectories_path, allow_pickle=True)
|
|
1182
|
-
else:
|
|
1183
|
-
self.df_effectors = pd.read_csv(self.effector_trajectories_path)
|
|
1184
|
-
|
|
1185
|
-
try:
|
|
1186
|
-
self.df_effectors = self.df_effectors.sort_values(by=['TRACK_ID', 'FRAME'])
|
|
1187
|
-
except:
|
|
1188
|
-
self.df_effectors = self.df_effectors.sort_values(by=['ID', 'FRAME'])
|
|
1189
|
-
|
|
1021
|
+
if len(class_cols)>0:
|
|
1190
1022
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
pass
|
|
1202
|
-
if len(self.effector_class_cols)>0:
|
|
1203
|
-
self.effector_class_name = self.effector_class_cols[0]
|
|
1204
|
-
self.effector_expected_status = 'status'
|
|
1205
|
-
suffix = self.effector_class_name.replace('class','').replace('_','')
|
|
1206
|
-
if suffix!='':
|
|
1207
|
-
self.effector_expected_status+='_'+suffix
|
|
1208
|
-
self.effector_expected_time = 't_'+suffix
|
|
1023
|
+
pop_class_name = class_cols[0]
|
|
1024
|
+
pop_expected_status = 'status'
|
|
1025
|
+
suffix = pop_class_name.replace('class','').replace('_','')
|
|
1026
|
+
if suffix!='':
|
|
1027
|
+
pop_expected_status+='_'+suffix
|
|
1028
|
+
pop_expected_time = 't_'+suffix
|
|
1029
|
+
else:
|
|
1030
|
+
pop_expected_time = 't0'
|
|
1031
|
+
pop_time_name = pop_expected_time
|
|
1032
|
+
pop_status_name = pop_expected_status
|
|
1209
1033
|
else:
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
else:
|
|
1214
|
-
self.effector_class_name = 'class'
|
|
1215
|
-
self.effector_time_name = 't0'
|
|
1216
|
-
self.effector_status_name = 'status'
|
|
1034
|
+
pop_class_name = 'class'
|
|
1035
|
+
pop_time_name = 't0'
|
|
1036
|
+
pop_status_name = 'status'
|
|
1217
1037
|
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
pass
|
|
1225
|
-
else:
|
|
1226
|
-
if not self.effector_status_name in self.df_effectors.columns:
|
|
1227
|
-
self.df_effectors[self.effector_status_name] = 0
|
|
1228
|
-
self.df_effectors['status_color'] = color_from_status(0)
|
|
1229
|
-
self.df_effectors['class_color'] = color_from_class(1)
|
|
1230
|
-
|
|
1231
|
-
if not self.effector_class_name in self.df_effectors.columns:
|
|
1232
|
-
self.df_effectors[self.effector_class_name] = 1
|
|
1233
|
-
if not self.effector_time_name in self.df_effectors.columns:
|
|
1234
|
-
self.df_effectors[self.effector_time_name] = -1
|
|
1235
|
-
|
|
1236
|
-
self.df_effectors['status_color'] = color_from_status(2) #[color_from_status(i) for i in self.df_effectors[self.effector_status_name].to_numpy()]
|
|
1237
|
-
self.df_effectors['class_color'] = color_from_status(2) #[color_from_class(i) for i in self.df_effectors[self.effector_class_name].to_numpy()]
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
self.df_effectors = self.df_effectors.dropna(subset=['POSITION_X', 'POSITION_Y'])
|
|
1241
|
-
self.df_effectors['x_anim'] = self.df_effectors['POSITION_X'] * self.fraction
|
|
1242
|
-
self.df_effectors['y_anim'] = self.df_effectors['POSITION_Y'] * self.fraction
|
|
1243
|
-
self.df_effectors['x_anim'] = self.df_effectors['x_anim'].astype(int)
|
|
1244
|
-
self.df_effectors['y_anim'] = self.df_effectors['y_anim'].astype(int)
|
|
1245
|
-
|
|
1246
|
-
self.extract_scatter_from_effector_trajectories()
|
|
1247
|
-
try:
|
|
1248
|
-
self.effector_track_of_interest = self.df_effectors['TRACK_ID'].min()
|
|
1249
|
-
except:
|
|
1250
|
-
self.effector_track_of_interest = self.df_effectors['ID'].min()
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
self.loc_t = []
|
|
1254
|
-
self.loc_idx = []
|
|
1255
|
-
for t in range(len(self.effector_tracks)):
|
|
1256
|
-
indices = np.where(self.effector_tracks[t]==self.effector_track_of_interest)[0]
|
|
1257
|
-
if len(indices)>0:
|
|
1258
|
-
self.loc_t.append(t)
|
|
1259
|
-
self.loc_idx.append(indices[0])
|
|
1260
|
-
|
|
1261
|
-
self.MinMaxScaler_effectors = MinMaxScaler()
|
|
1262
|
-
self.effector_columns = list(self.df_effectors.columns)
|
|
1263
|
-
cols_to_remove = [c for c in self.cols_to_remove if c in self.effector_columns] + self.effector_class_cols
|
|
1264
|
-
time_cols = [c for c in self.effector_columns if c.startswith('t_')]
|
|
1265
|
-
cols_to_remove += time_cols
|
|
1266
|
-
neigh_cols = [c for c in self.effector_columns if c.startswith('neighborhood_')]
|
|
1267
|
-
cols_to_remove += neigh_cols
|
|
1268
|
-
|
|
1269
|
-
for col in cols_to_remove:
|
|
1270
|
-
try:
|
|
1271
|
-
self.effector_columns.remove(col)
|
|
1272
|
-
except:
|
|
1038
|
+
if pop_time_name in df_population.columns and pop_class_name in df_population.columns and not pop_status_name in df_population.columns:
|
|
1039
|
+
# only create the status column if it does not exist to not erase static classification results
|
|
1040
|
+
pass
|
|
1041
|
+
#self.make_target_status_column()
|
|
1042
|
+
elif pop_time_name in df_population.columns and pop_class_name in df_population.columns:
|
|
1043
|
+
# all good, do nothing
|
|
1273
1044
|
pass
|
|
1045
|
+
else:
|
|
1046
|
+
if not pop_status_name in df_population.columns:
|
|
1047
|
+
df_population[pop_status_name] = 0
|
|
1048
|
+
df_population['status_color'] = color_from_status(0)
|
|
1049
|
+
df_population['class_color'] = color_from_class(1)
|
|
1050
|
+
|
|
1051
|
+
if not pop_class_name in df_population.columns:
|
|
1052
|
+
df_population[pop_class_name] = 1
|
|
1053
|
+
if not pop_time_name in df_population.columns:
|
|
1054
|
+
df_population[pop_time_name] = -1
|
|
1055
|
+
|
|
1056
|
+
df_population['status_color'] = color_from_status(2)
|
|
1057
|
+
df_population['class_color'] = color_from_status(2)
|
|
1058
|
+
|
|
1059
|
+
df_population = df_population.dropna(subset=['POSITION_X', 'POSITION_Y'])
|
|
1060
|
+
df_population['x_anim'] = df_population['POSITION_X'] * self.fraction
|
|
1061
|
+
df_population['y_anim'] = df_population['POSITION_Y'] * self.fraction
|
|
1062
|
+
df_population['x_anim'] = df_population['x_anim'].astype(int)
|
|
1063
|
+
df_population['y_anim'] = df_population['y_anim'].astype(int)
|
|
1064
|
+
|
|
1065
|
+
#self.reference_positions, self.reference_tracks, self.reference_colors, self.reference_initial_colors = self.extract_scatter_from_trajectories(self.reference_population)
|
|
1066
|
+
#self.extract_scatter_from_target_trajectories()
|
|
1067
|
+
|
|
1068
|
+
tracks = df_population['TRACK_ID'].unique()
|
|
1069
|
+
self.target_track_of_interest = df_population['TRACK_ID'].min()
|
|
1070
|
+
|
|
1071
|
+
self.loc_t = []
|
|
1072
|
+
self.loc_idx = []
|
|
1073
|
+
for t in range(len(tracks)):
|
|
1074
|
+
indices = np.where(tracks[t]==self.target_track_of_interest)[0]
|
|
1075
|
+
if len(indices)>0:
|
|
1076
|
+
self.loc_t.append(t)
|
|
1077
|
+
self.loc_idx.append(indices[0])
|
|
1078
|
+
|
|
1079
|
+
minmax = MinMaxScaler()
|
|
1080
|
+
pop_cols = list(df_population.columns)
|
|
1081
|
+
cols_to_remove = [c for c in self.cols_to_remove if c in pop_cols] + class_cols
|
|
1082
|
+
time_cols = [c for c in pop_cols if c.startswith('t_')]
|
|
1083
|
+
cols_to_remove += time_cols
|
|
1084
|
+
neigh_cols = [c for c in pop_cols if c.startswith('neighborhood_')]
|
|
1085
|
+
cols_to_remove += neigh_cols
|
|
1086
|
+
|
|
1087
|
+
for col in cols_to_remove:
|
|
1088
|
+
try:
|
|
1089
|
+
pop_cols.remove(col)
|
|
1090
|
+
except:
|
|
1091
|
+
pass
|
|
1274
1092
|
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
self.MinMaxScaler_effectors.fit(x)
|
|
1093
|
+
x = df_population[pop_cols].values
|
|
1094
|
+
minmax.fit(x)
|
|
1278
1095
|
|
|
1096
|
+
self.dataframes.update({population: df_population})
|
|
1097
|
+
self.class_cols_per_pop.update({population: class_cols})
|
|
1098
|
+
self.population_columns.update({population: pop_cols})
|
|
1099
|
+
self.MinMaxScaler_per_pop.update({population: minmax})
|
|
1279
1100
|
|
|
1280
1101
|
def locate_relative_tracks(self):
|
|
1281
1102
|
|
|
@@ -1294,7 +1115,8 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1294
1115
|
else:
|
|
1295
1116
|
# Load and prep tracks
|
|
1296
1117
|
self.df_relative = pd.read_csv(self.relative_trajectories_path)
|
|
1297
|
-
|
|
1118
|
+
self.df_relative = self.df_relative.astype({"reference_population": str, "neighbor_population": str})
|
|
1119
|
+
|
|
1298
1120
|
self.df_relative= self.df_relative.sort_values(by=['REFERENCE_ID','NEIGHBOR_ID','reference_population','neighbor_population','FRAME'])
|
|
1299
1121
|
self.relative_cols = np.array(self.df_relative.columns)
|
|
1300
1122
|
|
|
@@ -1341,10 +1163,21 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1341
1163
|
def set_reference_and_neighbor_populations(self):
|
|
1342
1164
|
|
|
1343
1165
|
neigh = self.neighborhood_choice_cb.currentText()
|
|
1344
|
-
|
|
1345
|
-
self.
|
|
1346
|
-
|
|
1166
|
+
|
|
1167
|
+
self.current_neighborhood = neigh
|
|
1168
|
+
for pop in self.dataframes.keys():
|
|
1169
|
+
self.current_neighborhood = self.current_neighborhood.replace(f'{pop}_ref_','')
|
|
1347
1170
|
|
|
1171
|
+
self.reference_population = self.neighborhood_choice_cb.currentText().split('_')[0]
|
|
1172
|
+
|
|
1173
|
+
if '_(' in self.current_neighborhood and ')_' in self.current_neighborhood:
|
|
1174
|
+
self.neighbor_population = self.current_neighborhood.split('_(')[-1].split(')_')[0].split('-')[-1]
|
|
1175
|
+
self.reference_population = self.current_neighborhood.split('_(')[-1].split(')_')[0].split('-')[0]
|
|
1176
|
+
else:
|
|
1177
|
+
if 'self' in self.current_neighborhood:
|
|
1178
|
+
self.neighbor_population = self.reference_population
|
|
1179
|
+
|
|
1180
|
+
|
|
1348
1181
|
print(f'Current neighborhood: {self.current_neighborhood}')
|
|
1349
1182
|
print(f'New reference population: {self.reference_population}')
|
|
1350
1183
|
print(f'New neighbor population: {self.neighbor_population}')
|
|
@@ -1395,7 +1228,6 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1395
1228
|
status[:] = 2
|
|
1396
1229
|
if cclass>2:
|
|
1397
1230
|
status[:] = 42
|
|
1398
|
-
print(t0, status)
|
|
1399
1231
|
status_color = [color_from_status(s) for s in status]
|
|
1400
1232
|
class_color = [color_from_class(cclass) for i in range(len(status))]
|
|
1401
1233
|
|
|
@@ -1514,8 +1346,16 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1514
1346
|
self.lines[i].set_xdata([])
|
|
1515
1347
|
self.lines[i].set_ydata([])
|
|
1516
1348
|
self.lines[i].set_label('')
|
|
1349
|
+
|
|
1517
1350
|
self.line_dt.set_xdata([])
|
|
1518
1351
|
self.line_dt.set_ydata([])
|
|
1352
|
+
|
|
1353
|
+
self.line_dt_reference.set_xdata([])
|
|
1354
|
+
self.line_dt_reference.set_ydata([])
|
|
1355
|
+
|
|
1356
|
+
self.line_dt_neighbor.set_xdata([])
|
|
1357
|
+
self.line_dt_neighbor.set_ydata([])
|
|
1358
|
+
|
|
1519
1359
|
self.cell_fcanvas.canvas.draw()
|
|
1520
1360
|
return None
|
|
1521
1361
|
else:
|
|
@@ -1528,11 +1368,18 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1528
1368
|
signal = []; timeline = [];
|
|
1529
1369
|
signal_txt = self.signal_choices[i].currentText()
|
|
1530
1370
|
option = self.signal_pop_button_groups[i].checkedId()
|
|
1371
|
+
|
|
1372
|
+
n_cut = 25
|
|
1373
|
+
if len(signal_txt) > n_cut:
|
|
1374
|
+
signal_txt_to_show = signal_txt[:(n_cut - 3)] + '...'
|
|
1375
|
+
else:
|
|
1376
|
+
signal_txt_to_show = signal_txt
|
|
1531
1377
|
|
|
1532
1378
|
if option==0 and self.reference_track_of_interest is not None and signal_txt!='--' and signal_txt!='':
|
|
1533
1379
|
|
|
1534
1380
|
df_reference = self.dataframes[self.reference_population]
|
|
1535
|
-
|
|
1381
|
+
|
|
1382
|
+
self.lines[i].set_label(f'reference ({self.reference_population}) '+ signal_txt_to_show)
|
|
1536
1383
|
|
|
1537
1384
|
signal = df_reference.loc[df_reference['TRACK_ID']==self.reference_track_of_interest, signal_txt].to_numpy()
|
|
1538
1385
|
timeline = df_reference.loc[df_reference['TRACK_ID']==self.reference_track_of_interest, 'FRAME'].to_numpy()
|
|
@@ -1541,15 +1388,14 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1541
1388
|
elif option==1 and self.neighbor_track_of_interest is not None and signal_txt!='--' and signal_txt!='':
|
|
1542
1389
|
|
|
1543
1390
|
df_neighbor = self.dataframes[self.neighbor_population]
|
|
1544
|
-
self.lines[i].set_label(f'neighbor ({self.neighbor_population}) '+
|
|
1391
|
+
self.lines[i].set_label(f'neighbor ({self.neighbor_population}) '+ signal_txt_to_show)
|
|
1545
1392
|
|
|
1546
1393
|
signal = df_neighbor.loc[df_neighbor['TRACK_ID']==self.neighbor_track_of_interest, signal_txt].to_numpy()
|
|
1547
1394
|
timeline = df_neighbor.loc[df_neighbor['TRACK_ID']==self.neighbor_track_of_interest, 'FRAME'].to_numpy()
|
|
1548
1395
|
range_values.extend(df_neighbor.loc[:,signal_txt].values)
|
|
1549
1396
|
|
|
1550
1397
|
elif option==2 and self.reference_track_of_interest is not None and self.neighbor_track_of_interest is not None and signal_txt!='--' and signal_txt!='':
|
|
1551
|
-
|
|
1552
|
-
self.lines[i].set_label(f'pair '+signal_txt)
|
|
1398
|
+
self.lines[i].set_label(f'pair '+signal_txt_to_show)
|
|
1553
1399
|
signal = self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.reference_track_of_interest)&(self.df_relative['NEIGHBOR_ID']==self.neighbor_track_of_interest)&(self.df_relative['reference_population']==self.reference_population)&(self.df_relative['neighbor_population']==self.neighbor_population), signal_txt].to_numpy()
|
|
1554
1400
|
timeline = self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.reference_track_of_interest)&(self.df_relative['NEIGHBOR_ID']==self.neighbor_track_of_interest)&(self.df_relative['reference_population']==self.reference_population)&(self.df_relative['neighbor_population']==self.neighbor_population), 'FRAME'].to_numpy()
|
|
1555
1401
|
range_values.extend(self.df_relative.loc[(self.df_relative['reference_population']==self.reference_population)&(self.df_relative['neighbor_population']==self.neighbor_population), signal_txt].values)
|
|
@@ -1560,7 +1406,7 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1560
1406
|
self.lines[i].set_xdata(timeline)
|
|
1561
1407
|
self.lines[i].set_ydata(signal)
|
|
1562
1408
|
self.lines[i].set_color(tab10(i / float(self.n_signals)))
|
|
1563
|
-
|
|
1409
|
+
|
|
1564
1410
|
#self.configure_ylims()
|
|
1565
1411
|
if len(range_values)>0:
|
|
1566
1412
|
range_values = np.array(range_values)
|
|
@@ -1576,18 +1422,36 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1576
1422
|
else:
|
|
1577
1423
|
self.cell_ax.set_ylim(self.value_magnitude, self.non_log_ymax)
|
|
1578
1424
|
|
|
1425
|
+
df_reference = self.dataframes[self.reference_population]
|
|
1426
|
+
t_reference = df_reference.loc[df_reference['TRACK_ID']==self.reference_track_of_interest, self.reference_time_name].values
|
|
1427
|
+
|
|
1428
|
+
if len(t_reference)>0:
|
|
1429
|
+
t_reference=t_reference[0]
|
|
1430
|
+
ymin,ymax = self.cell_ax.get_ylim()
|
|
1431
|
+
self.line_dt_reference.set_xdata([t_reference, t_reference])
|
|
1432
|
+
self.line_dt_reference.set_ydata([ymin,ymax])
|
|
1433
|
+
|
|
1434
|
+
|
|
1579
1435
|
if self.reference_track_of_interest is not None and self.neighbor_track_of_interest is not None:
|
|
1580
1436
|
t0 = self.df_relative.loc[(self.df_relative['REFERENCE_ID'] == self.reference_track_of_interest)&(self.df_relative['NEIGHBOR_ID'] == self.neighbor_track_of_interest)&(self.df_relative['reference_population'] == self.reference_population)&(self.df_relative['neighbor_population'] == self.neighbor_population), self.pair_time_name].dropna().values
|
|
1581
|
-
try:
|
|
1582
|
-
if t0!=[]:
|
|
1583
|
-
t0=t0[0]
|
|
1584
|
-
ymin,ymax = self.cell_ax.get_ylim()
|
|
1585
|
-
self.line_dt.set_xdata([t0, t0])
|
|
1586
|
-
self.line_dt.set_ydata([ymin,ymax])
|
|
1587
|
-
except Exception as e:
|
|
1588
|
-
print(e)
|
|
1589
1437
|
|
|
1590
|
-
|
|
1438
|
+
df_neighbor = self.dataframes[self.neighbor_population]
|
|
1439
|
+
t_neighbor = df_neighbor.loc[
|
|
1440
|
+
df_neighbor['TRACK_ID'] == self.neighbor_track_of_interest, self.neighbor_time_name].values
|
|
1441
|
+
|
|
1442
|
+
if len(t0)>0:
|
|
1443
|
+
t0=t0[0]
|
|
1444
|
+
ymin,ymax = self.cell_ax.get_ylim()
|
|
1445
|
+
self.line_dt.set_xdata([t0, t0])
|
|
1446
|
+
self.line_dt.set_ydata([ymin,ymax])
|
|
1447
|
+
|
|
1448
|
+
if len(t_neighbor)>0:
|
|
1449
|
+
t_neighbor=t_neighbor[0]
|
|
1450
|
+
ymin,ymax = self.cell_ax.get_ylim()
|
|
1451
|
+
self.line_dt_neighbor.set_xdata([t_neighbor, t_neighbor])
|
|
1452
|
+
self.line_dt_neighbor.set_ydata([ymin,ymax])
|
|
1453
|
+
|
|
1454
|
+
self.cell_ax.legend(fontsize=8)
|
|
1591
1455
|
self.cell_fcanvas.canvas.draw()
|
|
1592
1456
|
|
|
1593
1457
|
|
|
@@ -1603,55 +1467,63 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1603
1467
|
for t in np.arange(self.len_movie):
|
|
1604
1468
|
|
|
1605
1469
|
# Append frame_positions to self.line_positions
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
self.
|
|
1609
|
-
|
|
1610
|
-
self.
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
self.target_positions = []
|
|
1617
|
-
self.target_colors = []
|
|
1618
|
-
self.target_tracks = []
|
|
1619
|
-
self.initial_target_colors = []
|
|
1620
|
-
|
|
1621
|
-
for t in np.arange(self.len_movie):
|
|
1622
|
-
|
|
1623
|
-
if self.df_targets is not None:
|
|
1624
|
-
self.target_positions.append(self.df_targets.loc[self.df_targets['FRAME']==t,['x_anim', 'y_anim']].to_numpy())
|
|
1625
|
-
self.target_colors.append(self.df_targets.loc[self.df_targets['FRAME']==t,['class_color', 'status_color']].to_numpy())
|
|
1626
|
-
self.initial_target_colors.append(
|
|
1627
|
-
self.df_targets.loc[self.df_targets['FRAME'] == t, ['class_color', 'status_color']].to_numpy())
|
|
1628
|
-
try:
|
|
1629
|
-
self.target_tracks.append(self.df_targets.loc[self.df_targets['FRAME']==t, 'TRACK_ID'].to_numpy())
|
|
1630
|
-
except:
|
|
1631
|
-
self.target_tracks.append(
|
|
1632
|
-
self.df_targets.loc[self.df_targets['FRAME'] == t, 'ID'].to_numpy())
|
|
1470
|
+
neigh_at_time_filter = (self.df_relative['FRAME'] == t)&(~self.df_relative['status_'+self.current_neighborhood].isnull())&(self.df_relative['reference_population']==self.reference_population)
|
|
1471
|
+
data = self.df_relative.loc[neigh_at_time_filter, ['REFERENCE_ID', 'NEIGHBOR_ID','status_color','class_color']].to_numpy()
|
|
1472
|
+
self.lines_tracks.append(data[:,[0,1]])
|
|
1473
|
+
|
|
1474
|
+
self.initial_lines_colors_status.append(data[:,[0,1,2]])
|
|
1475
|
+
self.lines_colors_status.append(data[:,[0,1,2]])
|
|
1476
|
+
|
|
1477
|
+
self.initial_lines_colors_class.append(data[:,[0,1,3]])
|
|
1478
|
+
self.lines_colors_class.append(data[:,[0,1,3]])
|
|
1633
1479
|
|
|
1480
|
+
def extract_scatter_from_trajectories(self):
|
|
1634
1481
|
|
|
1635
|
-
|
|
1482
|
+
self.tracks = {}
|
|
1483
|
+
self.positions = {}
|
|
1484
|
+
self.colors = {}
|
|
1485
|
+
self.initial_colors = {}
|
|
1486
|
+
self.timeline = {}
|
|
1487
|
+
|
|
1488
|
+
for population in self.dataframes.keys():
|
|
1489
|
+
self.refresh_scatter_from_trajectories(population)
|
|
1636
1490
|
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
self.
|
|
1640
|
-
|
|
1491
|
+
def refresh_scatter_from_trajectories(self, population, clear_selection=True):
|
|
1492
|
+
|
|
1493
|
+
df = self.dataframes[population]
|
|
1494
|
+
positions = []
|
|
1495
|
+
tracks = []
|
|
1496
|
+
timeline = []
|
|
1497
|
+
colors = []
|
|
1498
|
+
initial_colors = []
|
|
1641
1499
|
|
|
1642
|
-
|
|
1500
|
+
if df is not None:
|
|
1643
1501
|
|
|
1644
|
-
|
|
1502
|
+
for t in np.arange(self.len_movie):
|
|
1645
1503
|
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1504
|
+
positions.append(df.loc[df['FRAME']==t,['x_anim', 'y_anim']].to_numpy())
|
|
1505
|
+
colors.append(df.loc[df['FRAME']==t,['class_color', 'status_color']].to_numpy())
|
|
1506
|
+
initial_colors.append(
|
|
1507
|
+
df.loc[df['FRAME'] == t, ['class_color', 'status_color']].to_numpy())
|
|
1649
1508
|
try:
|
|
1650
|
-
|
|
1509
|
+
tracks.append(df.loc[df['FRAME']==t, 'TRACK_ID'].to_numpy())
|
|
1510
|
+
timeline.append(t)
|
|
1651
1511
|
except:
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1512
|
+
tracks.append(
|
|
1513
|
+
df.loc[df['FRAME'] == t, 'ID'].to_numpy())
|
|
1514
|
+
timeline.append(t)
|
|
1515
|
+
|
|
1516
|
+
self.tracks.update({population: tracks})
|
|
1517
|
+
self.timeline.update({population: timeline})
|
|
1518
|
+
self.positions.update({population: positions})
|
|
1519
|
+
self.colors.update({population: colors})
|
|
1520
|
+
self.initial_colors.update({population: initial_colors})
|
|
1521
|
+
|
|
1522
|
+
if self.reference_track_of_interest is not None:
|
|
1523
|
+
#self.recolor_selection()
|
|
1524
|
+
#self.trace_neighbors()
|
|
1525
|
+
self.highlight_the_pair()
|
|
1526
|
+
|
|
1655
1527
|
def load_annotator_config(self):
|
|
1656
1528
|
|
|
1657
1529
|
"""
|
|
@@ -1793,7 +1665,11 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1793
1665
|
# "Are you sure you want to exit ?",
|
|
1794
1666
|
# QMessageBox.Yes| QMessageBox.No,
|
|
1795
1667
|
# )
|
|
1796
|
-
|
|
1668
|
+
try:
|
|
1669
|
+
del self.stack
|
|
1670
|
+
except:
|
|
1671
|
+
pass
|
|
1672
|
+
|
|
1797
1673
|
gc.collect()
|
|
1798
1674
|
|
|
1799
1675
|
def looped_animation(self):
|
|
@@ -1815,21 +1691,26 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1815
1691
|
self.im = self.ax.imshow(self.stack[0], cmap='gray', vmin=np.nanpercentile(self.stack, 1), vmax=np.nanpercentile(self.stack, 99.99))
|
|
1816
1692
|
|
|
1817
1693
|
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1694
|
+
self.status_scatter = {}
|
|
1695
|
+
self.class_scatter = {}
|
|
1696
|
+
|
|
1697
|
+
n_pops = len(list(self.dataframes.keys()))
|
|
1698
|
+
markers = ['x','+','*','.','X']
|
|
1699
|
+
while len(markers) < n_pops:
|
|
1700
|
+
markers = markers*2
|
|
1701
|
+
|
|
1702
|
+
for i,pop in enumerate(self.dataframes.keys()):
|
|
1703
|
+
df = self.dataframes[pop]
|
|
1704
|
+
if df is not None:
|
|
1705
|
+
status_scatter = self.ax.scatter(self.positions[pop][0][:,0], self.positions[pop][0][:,1], c=self.colors[pop][0][:,1], s=50, picker=True, pickradius=150, zorder=10, marker=markers[i]) #marker="x",
|
|
1706
|
+
class_scatter = self.ax.scatter(self.positions[pop][0][:,0], self.positions[pop][0][:,1], marker='o', facecolors='none',edgecolors=self.colors[pop][0][:,0], s=200, zorder=10)
|
|
1707
|
+
else:
|
|
1708
|
+
status_scatter = self.ax.scatter([], [], s=50, picker=True, pickradius=10, marker=markers[i]) #marker="x",
|
|
1709
|
+
class_scatter = self.ax.scatter([], [], marker='o', facecolors='none', s=200)
|
|
1710
|
+
self.status_scatter.update({pop: status_scatter})
|
|
1711
|
+
self.class_scatter.update({pop: class_scatter})
|
|
1831
1712
|
|
|
1832
|
-
self.points=self.ax.scatter([], [], marker="
|
|
1713
|
+
self.points=self.ax.scatter([], [], marker="$\\Join$", s=100, picker=True, pickradius=10, zorder=10) #picker=True, pickradius=10
|
|
1833
1714
|
|
|
1834
1715
|
self.ax.set_xticks([])
|
|
1835
1716
|
self.ax.set_yticks([])
|
|
@@ -1869,13 +1750,17 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1869
1750
|
|
|
1870
1751
|
self.lines = [self.cell_ax.plot([np.linspace(0,self.len_movie-1,self.len_movie)],[np.zeros((self.len_movie))])[0] for i in range(len(self.signal_choices))]
|
|
1871
1752
|
for i in range(len(self.lines)):
|
|
1753
|
+
|
|
1872
1754
|
self.lines[i].set_label(f'signal {i}')
|
|
1873
1755
|
|
|
1874
1756
|
min_val,max_val = self.cell_ax.get_ylim()
|
|
1875
1757
|
self.line_dt, = self.cell_ax.plot([-1,-1],[min_val,max_val],c="k",linestyle="--")
|
|
1758
|
+
self.line_dt_reference, = self.cell_ax.plot([-1,-1],[min_val,max_val],c="tab:blue",linestyle="--")
|
|
1759
|
+
self.line_dt_neighbor, = self.cell_ax.plot([-1,-1],[min_val,max_val],c="tab:red",linestyle="--")
|
|
1760
|
+
|
|
1876
1761
|
|
|
1877
1762
|
self.cell_ax.set_xlim(0,self.len_movie)
|
|
1878
|
-
self.cell_ax.legend()
|
|
1763
|
+
self.cell_ax.legend(fontsize=8)
|
|
1879
1764
|
self.cell_fcanvas.canvas.draw()
|
|
1880
1765
|
|
|
1881
1766
|
#self.plot_signals()
|
|
@@ -1884,9 +1769,8 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1884
1769
|
def on_scatter_pick(self, event):
|
|
1885
1770
|
|
|
1886
1771
|
self.identify_closest_marker(event)
|
|
1887
|
-
print(self.pair_selected, self.reference_selection)
|
|
1888
1772
|
|
|
1889
|
-
_, tracks, _, _ = self.get_reference_sets()
|
|
1773
|
+
_, tracks, _, _, _ = self.get_reference_sets()
|
|
1890
1774
|
|
|
1891
1775
|
if self.selected_population == self.reference_population:
|
|
1892
1776
|
|
|
@@ -1949,12 +1833,15 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1949
1833
|
|
|
1950
1834
|
print('You selected a pair...')
|
|
1951
1835
|
artist = event.artist
|
|
1952
|
-
print(self.index)
|
|
1953
1836
|
|
|
1954
1837
|
if self.index is not None and len(self.pair_selection)==0:
|
|
1955
1838
|
|
|
1956
|
-
|
|
1957
|
-
|
|
1839
|
+
try:
|
|
1840
|
+
selected_point = artist.get_offsets()[self.index]
|
|
1841
|
+
except Exception as e:
|
|
1842
|
+
print(f"L1788 {e}")
|
|
1843
|
+
return
|
|
1844
|
+
|
|
1958
1845
|
if len(self.pair_selection) == 0 and ((selected_point[0],selected_point[1]) in self.connections.keys()):
|
|
1959
1846
|
|
|
1960
1847
|
connect = self.connections[(selected_point[0], selected_point[1])]
|
|
@@ -2029,24 +1916,10 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
2029
1916
|
|
|
2030
1917
|
|
|
2031
1918
|
def get_neighbor_sets(self):
|
|
2032
|
-
|
|
2033
|
-
if self.reference_population != self.neighbor_population:
|
|
2034
|
-
if self.reference_population=='effectors':
|
|
2035
|
-
return self.target_positions, self.target_tracks, self.target_colors, self.initial_target_colors
|
|
2036
|
-
elif self.reference_population=='targets':
|
|
2037
|
-
return self.effector_positions, self.effector_tracks, self.effector_colors, self.initial_effector_colors
|
|
2038
|
-
else:
|
|
2039
|
-
if self.reference_population=='effectors':
|
|
2040
|
-
return self.effector_positions, self.effector_tracks, self.effector_colors, self.initial_effector_colors
|
|
2041
|
-
elif self.reference_population=='targets':
|
|
2042
|
-
return self.target_positions, self.target_tracks, self.target_colors, self.initial_target_colors
|
|
1919
|
+
return self.positions[self.neighbor_population], self.tracks[self.neighbor_population], self.colors[self.neighbor_population], self.initial_colors[self.neighbor_population]
|
|
2043
1920
|
|
|
2044
1921
|
def get_reference_sets(self):
|
|
2045
|
-
|
|
2046
|
-
if self.reference_population == 'effectors':
|
|
2047
|
-
return self.effector_positions, self.effector_tracks, self.effector_colors, self.initial_effector_colors
|
|
2048
|
-
elif self.reference_population == 'targets':
|
|
2049
|
-
return self.target_positions, self.target_tracks, self.target_colors, self.initial_target_colors
|
|
1922
|
+
return self.positions[self.reference_population], self.tracks[self.reference_population], self.timeline[self.reference_population], self.colors[self.reference_population], self.initial_colors[self.reference_population]
|
|
2050
1923
|
|
|
2051
1924
|
def trace_neighbors(self):
|
|
2052
1925
|
|
|
@@ -2056,7 +1929,8 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
2056
1929
|
self.line_connections={}
|
|
2057
1930
|
|
|
2058
1931
|
positions, tracks, colors, _ = self.get_neighbor_sets()
|
|
2059
|
-
|
|
1932
|
+
_, tracks_reference, _, _, _ = self.get_reference_sets()
|
|
1933
|
+
|
|
2060
1934
|
# Look for neighbors
|
|
2061
1935
|
for neigh in self.neighbors:
|
|
2062
1936
|
|
|
@@ -2065,46 +1939,39 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
2065
1939
|
|
|
2066
1940
|
for t in range(len(tracks)):
|
|
2067
1941
|
indices = np.where(tracks[t]==neigh)[0]
|
|
2068
|
-
|
|
1942
|
+
indices_reference = np.where(tracks_reference[t]==self.reference_track_of_interest)[0]
|
|
1943
|
+
if len(indices)>0 and len(indices_reference)>0:
|
|
2069
1944
|
self.neighbor_loc_t.append(t)
|
|
2070
1945
|
self.neighbor_loc_idx.append(indices[0])
|
|
2071
|
-
|
|
1946
|
+
|
|
2072
1947
|
self.neighbor_previous_color = []
|
|
2073
1948
|
for t, idx in zip(self.neighbor_loc_t, self.neighbor_loc_idx):
|
|
2074
|
-
|
|
2075
|
-
|
|
1949
|
+
|
|
1950
|
+
t_ref_idx = np.where(self.reference_timeline == t)[0]
|
|
1951
|
+
if t_ref_idx:
|
|
1952
|
+
t_ref_idx = t_ref_idx[0]
|
|
2076
1953
|
|
|
2077
1954
|
neigh_x = positions[t][idx, 0]
|
|
2078
1955
|
neigh_y = positions[t][idx, 1]
|
|
2079
|
-
x_m_point = (self.reference_x[
|
|
2080
|
-
y_m_point = (self.reference_y[
|
|
2081
|
-
|
|
1956
|
+
x_m_point = (self.reference_x[t_ref_idx] + neigh_x) / 2
|
|
1957
|
+
y_m_point = (self.reference_y[t_ref_idx] + neigh_y) / 2
|
|
1958
|
+
|
|
2082
1959
|
if t not in self.lines_data.keys():
|
|
2083
|
-
self.lines_data[t]=[([self.reference_x[
|
|
1960
|
+
self.lines_data[t]=[([self.reference_x[t_ref_idx], neigh_x], [self.reference_y[t_ref_idx], neigh_y])]
|
|
2084
1961
|
self.points_data[t]=[(x_m_point, y_m_point)]
|
|
2085
1962
|
else:
|
|
2086
|
-
self.lines_data[t].append(([self.reference_x[
|
|
1963
|
+
self.lines_data[t].append(([self.reference_x[t_ref_idx], neigh_x], [self.reference_y[t_ref_idx], neigh_y]))
|
|
2087
1964
|
self.points_data[t].append((x_m_point, y_m_point))
|
|
2088
|
-
|
|
1965
|
+
|
|
2089
1966
|
self.connections[(x_m_point, y_m_point)] = [(self.reference_track_of_interest, neigh)]
|
|
2090
|
-
self.line_connections[(self.reference_x[
|
|
2091
|
-
|
|
1967
|
+
self.line_connections[(self.reference_x[t_ref_idx], neigh_x, self.reference_y[t_ref_idx], neigh_y)]=[(self.reference_track_of_interest, neigh)]
|
|
1968
|
+
|
|
2092
1969
|
self.neighbor_previous_color.append(colors[t][idx].copy())
|
|
2093
|
-
except Exception as e:
|
|
2094
|
-
print(e)
|
|
2095
|
-
pass
|
|
2096
|
-
#colors[t][idx] = 'salmon'
|
|
2097
1970
|
|
|
2098
|
-
# for t in range(len(colors)):
|
|
2099
|
-
# for idx in range(len(colors[t])):
|
|
2100
|
-
# if colors[t][idx].any() != 'salmon':
|
|
2101
|
-
# if colors[t][idx].any() != 'magenta':
|
|
2102
|
-
# #init_color[t][idx] = colors[t][idx].copy()
|
|
2103
|
-
# colors[t][idx] = 'black'
|
|
2104
1971
|
|
|
2105
1972
|
def recolor_selection(self):
|
|
2106
1973
|
|
|
2107
|
-
positions, tracks, colors, init_colors = self.get_reference_sets()
|
|
1974
|
+
positions, tracks, timelines, colors, init_colors = self.get_reference_sets()
|
|
2108
1975
|
|
|
2109
1976
|
self.reference_loc_t = []
|
|
2110
1977
|
self.reference_loc_idx = []
|
|
@@ -2125,13 +1992,16 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
2125
1992
|
self.reference_not_picked_initial_colors=[]
|
|
2126
1993
|
self.reference_x = []
|
|
2127
1994
|
self.reference_y = []
|
|
1995
|
+
self.reference_timeline = []
|
|
2128
1996
|
|
|
2129
1997
|
# Recolor selected cell
|
|
2130
1998
|
for t,idx in zip(self.reference_loc_t,self.reference_loc_idx):
|
|
2131
1999
|
self.reference_x.append(positions[t][idx, 0])
|
|
2132
2000
|
self.reference_y.append(positions[t][idx, 1])
|
|
2001
|
+
self.reference_timeline.append(timelines[t])
|
|
2133
2002
|
self.reference_previous_color.append(colors[t][idx].copy())
|
|
2134
2003
|
colors[t][idx] = 'lime'
|
|
2004
|
+
self.reference_timeline = np.array(self.reference_timeline)
|
|
2135
2005
|
|
|
2136
2006
|
# Recolor all other cells in black
|
|
2137
2007
|
for t, idx in zip(self.reference_loc_t_not_picked, self.reference_loc_idx_not_picked):
|
|
@@ -2144,6 +2014,7 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
2144
2014
|
|
|
2145
2015
|
self.neighbors = self.df_relative.loc[(self.df_relative['REFERENCE_ID'] == selected_cell)&(~self.df_relative['status_'+self.current_neighborhood].isnull())&(self.df_relative['reference_population']==self.reference_population),'NEIGHBOR_ID']
|
|
2146
2016
|
self.neighbors = np.unique(self.neighbors)
|
|
2017
|
+
|
|
2147
2018
|
# if len(self.neighbors)>0:
|
|
2148
2019
|
# first_neighbor = np.min(self.neighbors)
|
|
2149
2020
|
# self.neighbor_track_of_interest = first_neighbor
|
|
@@ -2154,38 +2025,37 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
2154
2025
|
|
|
2155
2026
|
ind = event.ind
|
|
2156
2027
|
label = event.artist.get_label()
|
|
2157
|
-
print(f'{label=}')
|
|
2158
2028
|
|
|
2159
2029
|
# Identify the nature of the selected object (target/effector/pair)
|
|
2160
2030
|
self.pair_selected = False
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
if self.selected_population=='effectors':
|
|
2172
|
-
positions = self.effector_positions
|
|
2173
|
-
elif self.selected_population=='targets':
|
|
2174
|
-
positions = self.target_positions
|
|
2175
|
-
|
|
2176
|
-
if len(ind)==1:
|
|
2177
|
-
self.index = ind[0]
|
|
2178
|
-
elif len(ind)>1:
|
|
2179
|
-
# More than one point in vicinity
|
|
2180
|
-
try:
|
|
2181
|
-
datax,datay = [positions[self.framedata][i,0] for i in ind],[positions[self.framedata][i,1] for i in ind]
|
|
2182
|
-
msx, msy = event.mouseevent.xdata, event.mouseevent.ydata
|
|
2183
|
-
dist = np.sqrt((np.array(datax)-msx)**2+(np.array(datay)-msy)**2)
|
|
2184
|
-
self.index = ind[np.argmin(dist)]
|
|
2185
|
-
except Exception as e:
|
|
2186
|
-
print(f"{e=}")
|
|
2031
|
+
|
|
2032
|
+
populations = list(self.dataframes.keys())
|
|
2033
|
+
|
|
2034
|
+
number = int(label.split('_child')[1])
|
|
2035
|
+
|
|
2036
|
+
if number>len(populations)*2:
|
|
2037
|
+
print('A pair is selected...')
|
|
2038
|
+
self.pair_selected = True
|
|
2039
|
+
self.selected_population = None
|
|
2187
2040
|
else:
|
|
2188
|
-
self.
|
|
2041
|
+
self.selected_population = populations[(number-1)//2]
|
|
2042
|
+
|
|
2043
|
+
if self.selected_population is not None:
|
|
2044
|
+
positions = self.positions[self.selected_population]
|
|
2045
|
+
|
|
2046
|
+
if len(ind)==1:
|
|
2047
|
+
self.index = ind[0]
|
|
2048
|
+
elif len(ind)>1:
|
|
2049
|
+
# More than one point in vicinity
|
|
2050
|
+
try:
|
|
2051
|
+
datax,datay = [positions[self.framedata][i,0] for i in ind],[positions[self.framedata][i,1] for i in ind]
|
|
2052
|
+
msx, msy = event.mouseevent.xdata, event.mouseevent.ydata
|
|
2053
|
+
dist = np.sqrt((np.array(datax)-msx)**2+(np.array(datay)-msy)**2)
|
|
2054
|
+
self.index = ind[np.argmin(dist)]
|
|
2055
|
+
except Exception as e:
|
|
2056
|
+
print(f"Exception L2090 to find closest marker: {e=}")
|
|
2057
|
+
else:
|
|
2058
|
+
self.index = None
|
|
2189
2059
|
|
|
2190
2060
|
|
|
2191
2061
|
def show_annotation_buttons(self):
|
|
@@ -2304,58 +2174,46 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
2304
2174
|
self.framedata = framedata
|
|
2305
2175
|
self.frame_lbl.setText(f'frame: {self.framedata}')
|
|
2306
2176
|
self.im.set_array(self.stack[self.framedata])
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
if self.df_targets is not None:
|
|
2320
|
-
self.target_status_scatter.set_visible(True)
|
|
2321
|
-
self.target_status_scatter.set_picker(True)
|
|
2322
|
-
self.target_class_scatter.set_visible(True)
|
|
2323
|
-
self.target_status_scatter.set_offsets(self.target_positions[self.framedata])
|
|
2324
|
-
self.target_status_scatter.set_color(self.target_colors[self.framedata][:, 1])
|
|
2325
|
-
self.target_class_scatter.set_offsets(self.target_positions[self.framedata])
|
|
2326
|
-
self.target_class_scatter.set_edgecolor(self.target_colors[self.framedata][:, 0])
|
|
2177
|
+
|
|
2178
|
+
for pop in self.dataframes.keys():
|
|
2179
|
+
df = self.dataframes[pop]
|
|
2180
|
+
if df is not None:
|
|
2181
|
+
self.status_scatter[pop].set_visible(True)
|
|
2182
|
+
self.status_scatter[pop].set_picker(True)
|
|
2183
|
+
self.status_scatter[pop].set_offsets(self.positions[pop][self.framedata])
|
|
2184
|
+
self.status_scatter[pop].set_color(self.colors[pop][self.framedata][:,1])
|
|
2185
|
+
|
|
2186
|
+
self.class_scatter[pop].set_visible(True)
|
|
2187
|
+
self.class_scatter[pop].set_offsets(self.positions[pop][self.framedata])
|
|
2188
|
+
self.class_scatter[pop].set_edgecolor(self.colors[pop][self.framedata][:,0])
|
|
2327
2189
|
|
|
2328
2190
|
self.lines_list=[]
|
|
2329
2191
|
|
|
2330
2192
|
for key in self.lines_data:
|
|
2193
|
+
|
|
2331
2194
|
if key==self.framedata:
|
|
2195
|
+
|
|
2332
2196
|
for line in self.lines_data[key]:
|
|
2333
|
-
x_coords, y_coords = line
|
|
2334
|
-
pair=self.line_connections[x_coords[0],x_coords[1],y_coords[0],y_coords[1]]
|
|
2335
2197
|
|
|
2336
|
-
|
|
2198
|
+
x_coords, y_coords = line
|
|
2199
|
+
pair = self.line_connections[x_coords[0],x_coords[1],y_coords[0],y_coords[1]]
|
|
2200
|
+
this_frame = self.lines_colors_class[self.framedata]
|
|
2337
2201
|
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2202
|
+
this_pair = this_frame[(this_frame[:, 0] == pair[0][0]) & (this_frame[:, 1] == pair[0][1])]
|
|
2203
|
+
if len(this_pair)>0:
|
|
2204
|
+
c = this_pair[0][2]
|
|
2205
|
+
self.lines_plot = self.ax.plot(x_coords, y_coords, alpha=1, linewidth=2, color=c)
|
|
2341
2206
|
self.lines_list.append(self.lines_plot[0])
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
self.points.set_offsets(self.points_data[key])
|
|
2348
|
-
colors_at_this_frame = self.lines_colors_status[self.framedata]
|
|
2349
|
-
colors = [colors_at_this_frame[(colors_at_this_frame[:, 0] == self.connections[point[0],point[1]][0][0]) & (colors_at_this_frame[:, 1] == self.connections[point[0],point[1]][0][1])][0][2] for point in self.points_data[key]]
|
|
2350
|
-
self.points.set_color(colors)
|
|
2351
|
-
except Exception as e:
|
|
2352
|
-
print(e)
|
|
2207
|
+
|
|
2208
|
+
self.points.set_offsets(self.points_data[key])
|
|
2209
|
+
colors_at_this_frame = self.lines_colors_status[self.framedata]
|
|
2210
|
+
colors = [colors_at_this_frame[(colors_at_this_frame[:, 0] == self.connections[point[0],point[1]][0][0]) & (colors_at_this_frame[:, 1] == self.connections[point[0],point[1]][0][1])][0][2] for point in self.points_data[key]]
|
|
2211
|
+
self.points.set_color(colors)
|
|
2353
2212
|
|
|
2354
2213
|
if self.lines_list!=[]:
|
|
2355
|
-
return [self.im
|
|
2214
|
+
return [self.im] + [self.status_scatter[p] for p in self.status_scatter.keys()] + [self.class_scatter[p] for p in self.status_scatter.keys()] + self.lines_list + [self.points]
|
|
2356
2215
|
else:
|
|
2357
|
-
return [self.im, self.
|
|
2358
|
-
self.effector_class_scatter,]
|
|
2216
|
+
return [self.im,] + [self.status_scatter[p] for p in self.status_scatter.keys()] + [self.class_scatter[p] for p in self.status_scatter.keys()]
|
|
2359
2217
|
|
|
2360
2218
|
def stop(self):
|
|
2361
2219
|
# # On stop we disconnect all of our events.
|
|
@@ -2435,63 +2293,6 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
2435
2293
|
|
|
2436
2294
|
self.target_cell_info.setText('')
|
|
2437
2295
|
|
|
2438
|
-
# def give_effector_cell_information(self):
|
|
2439
|
-
# self.effector_cell_info.setSpacing(0)
|
|
2440
|
-
# self.effector_cell_info.setContentsMargins(0, 20, 0, 30)
|
|
2441
|
-
# self.neigh_eff_combo=QComboBox()
|
|
2442
|
-
# #self.neighb_eff_combo.addItems(self.df_relative.loc[(self.df_relative['target']==self.target_track_of_interest),'effecor'])
|
|
2443
|
-
# neighs=self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.target_track_of_interest),'NEIGHBOR_ID'].to_numpy()
|
|
2444
|
-
# neighs=np.unique(neighs)
|
|
2445
|
-
# for effector in neighs:
|
|
2446
|
-
# self.neigh_eff_combo.addItem(str(effector))
|
|
2447
|
-
# if self.effector_track_of_interest not in neighs:
|
|
2448
|
-
# self.neigh_eff_combo.addItem(str(self.effector_track_of_interest))
|
|
2449
|
-
# self.neigh_eff_combo.setCurrentText(str(self.effector_track_of_interest))
|
|
2450
|
-
# self.eff_cell_sel=QHBoxLayout()
|
|
2451
|
-
# #effector_cell_selected = f"effector cell: {self.effector_track_of_interest}"
|
|
2452
|
-
# self.effector_cell_selected = f"effector cell: "
|
|
2453
|
-
# self.eff_cell = QLabel(self.effector_cell_selected)
|
|
2454
|
-
# # self.eff_cell_sel.removeWidget(self.eff_cell)
|
|
2455
|
-
# # self.eff_cell_sel.removeWidget(self.neigh_eff_combo)
|
|
2456
|
-
# self.eff_cell_sel.addWidget(self.eff_cell)
|
|
2457
|
-
# self.eff_cell_sel.addWidget(self.neigh_eff_combo, alignment=Qt.AlignLeft)
|
|
2458
|
-
# try:
|
|
2459
|
-
# self.effector_cell_class = f"class: {self.df_effectors.loc[self.df_effectors['TRACK_ID']==self.effector_track_of_interest, self.effector_class_name].to_numpy()[0]}"
|
|
2460
|
-
# except:
|
|
2461
|
-
# self.effector_cell_class = f"class: {self.df_effectors.loc[self.df_effectors['ID'] == self.effector_track_of_interest, self.effector_class_name].to_numpy()[0]}"
|
|
2462
|
-
|
|
2463
|
-
# self.eff_cls = QLabel(self.effector_cell_class)
|
|
2464
|
-
# try:
|
|
2465
|
-
# self.effector_cell_time = f"time of interest: {self.df_effectors.loc[self.df_effectors['TRACK_ID']==self.effector_track_of_interest, self.effector_time_name].to_numpy()[0]}"
|
|
2466
|
-
# except:
|
|
2467
|
-
# self.effector_cell_time = f"time of interest: {self.df_effectors.loc[self.df_effectors['ID']==self.effector_track_of_interest, self.effector_time_name].to_numpy()[0]}"
|
|
2468
|
-
|
|
2469
|
-
# self.eff_tm=QLabel(self.effector_cell_time)
|
|
2470
|
-
# # try:
|
|
2471
|
-
# # self.effector_probabilty = f"probability: {self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.target_track_of_interest)&(self.df_relative['NEIGHBOR_ID']==self.effector_track_of_interest),'probability'].to_numpy()[0]}"
|
|
2472
|
-
# # except:
|
|
2473
|
-
# # self.effector_probabilty=f"probability: 0"
|
|
2474
|
-
# # self.eff_prb=QLabel(self.effector_probabilty)
|
|
2475
|
-
# #self.effector_cell_info.setText(effector_cell_selected+effector_cell_class+effector_cell_time+effector_probabilty)
|
|
2476
|
-
# # self.effector_cell_info.removeWidget(self.eff_cls)
|
|
2477
|
-
# # self.effector_cell_info.removeWidget(self.eff_tm)
|
|
2478
|
-
# # self.effector_cell_info.removeWidget(self.eff_prb)
|
|
2479
|
-
# self.effector_cell_info.addLayout(self.eff_cell_sel)
|
|
2480
|
-
# self.effector_cell_info.addWidget(self.eff_cls)
|
|
2481
|
-
# self.effector_cell_info.addWidget(self.eff_tm)
|
|
2482
|
-
# #self.effector_cell_info.addWidget(self.eff_prb)
|
|
2483
|
-
# self.neigh_eff_combo.currentIndexChanged.connect(self.update_effector_info)
|
|
2484
|
-
# self.eff_info_to_hide=[self.eff_cell,self.neigh_eff_combo,self.eff_cls,self.eff_tm]#self.eff_prb
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
# def hide_effector_cell_info(self):
|
|
2488
|
-
# self.eff_cls.clear()
|
|
2489
|
-
# self.eff_tm.clear()
|
|
2490
|
-
# #self.eff_prb.clear()
|
|
2491
|
-
|
|
2492
|
-
# for info in self.eff_info_to_hide:
|
|
2493
|
-
# info.hide()
|
|
2494
|
-
|
|
2495
2296
|
|
|
2496
2297
|
def save_trajectories(self):
|
|
2497
2298
|
|
|
@@ -2602,39 +2403,34 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
2602
2403
|
|
|
2603
2404
|
def normalize_features(self):
|
|
2604
2405
|
|
|
2605
|
-
if self.df_effectors is not None:
|
|
2606
|
-
x_effectors = self.df_effectors[self.effector_columns].values
|
|
2607
|
-
if self.df_targets is not None:
|
|
2608
|
-
x_targets = self.df_targets[self.target_columns].values
|
|
2609
|
-
if self.df_relative is not None:
|
|
2610
|
-
x_pairs = self.df_relative[self.pair_columns].values
|
|
2611
|
-
|
|
2612
2406
|
if not self.normalized_signals:
|
|
2407
|
+
for pop in self.dataframes.keys():
|
|
2408
|
+
df_pop = self.dataframes[pop]
|
|
2409
|
+
cols = self.population_columns[pop]
|
|
2410
|
+
if df_pop is not None:
|
|
2411
|
+
df_pop[cols] = self.MinMaxScaler_per_pop[pop].transform(df_pop[cols].values)
|
|
2412
|
+
self.dataframes.update({pop: df_pop})
|
|
2613
2413
|
|
|
2614
|
-
if self.df_effectors is not None:
|
|
2615
|
-
self.df_effectors[self.effector_columns] = self.MinMaxScaler_effectors.transform(x_effectors)
|
|
2616
|
-
if self.df_targets is not None:
|
|
2617
|
-
self.df_targets[self.target_columns] = self.MinMaxScaler_targets.transform(x_targets)
|
|
2618
2414
|
if self.df_relative is not None:
|
|
2619
|
-
self.df_relative[self.pair_columns] = self.MinMaxScaler_pairs.transform(
|
|
2415
|
+
self.df_relative[self.pair_columns] = self.MinMaxScaler_pairs.transform(self.df_relative[self.pair_columns].values)
|
|
2620
2416
|
|
|
2621
|
-
self.plot_signals()
|
|
2622
2417
|
self.normalized_signals = True
|
|
2623
2418
|
self.normalize_features_btn.setIcon(icon(MDI6.arrow_collapse_vertical, color="#1565c0"))
|
|
2624
|
-
self.normalize_features_btn.setIconSize(QSize(25, 25))
|
|
2625
2419
|
else:
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2420
|
+
for pop in self.dataframes.keys():
|
|
2421
|
+
df_pop = self.dataframes[pop]
|
|
2422
|
+
cols = self.population_columns[pop]
|
|
2423
|
+
if df_pop is not None:
|
|
2424
|
+
df_pop[cols] = self.MinMaxScaler_per_pop[pop].inverse_transform(df_pop[cols].values)
|
|
2425
|
+
self.dataframes.update({pop: df_pop})
|
|
2631
2426
|
if self.df_relative is not None:
|
|
2632
|
-
self.df_relative[self.pair_columns] = self.MinMaxScaler_pairs.inverse_transform(
|
|
2427
|
+
self.df_relative[self.pair_columns] = self.MinMaxScaler_pairs.inverse_transform(self.df_relative[self.pair_columns].values)
|
|
2633
2428
|
|
|
2634
|
-
self.plot_signals()
|
|
2635
2429
|
self.normalized_signals = False
|
|
2636
2430
|
self.normalize_features_btn.setIcon(icon(MDI6.arrow_collapse_vertical, color="black"))
|
|
2637
|
-
|
|
2431
|
+
|
|
2432
|
+
self.plot_signals()
|
|
2433
|
+
self.normalize_features_btn.setIconSize(QSize(25, 25))
|
|
2638
2434
|
|
|
2639
2435
|
def switch_to_log(self):
|
|
2640
2436
|
|