celldetective 1.0.2.post1__py3-none-any.whl → 1.1.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/__main__.py +2 -2
- celldetective/events.py +2 -44
- celldetective/filters.py +4 -5
- celldetective/gui/__init__.py +1 -1
- celldetective/gui/analyze_block.py +37 -10
- celldetective/gui/btrack_options.py +24 -23
- celldetective/gui/classifier_widget.py +62 -19
- celldetective/gui/configure_new_exp.py +32 -35
- celldetective/gui/control_panel.py +115 -81
- celldetective/gui/gui_utils.py +674 -396
- celldetective/gui/json_readers.py +7 -6
- celldetective/gui/layouts.py +755 -0
- celldetective/gui/measurement_options.py +168 -487
- celldetective/gui/neighborhood_options.py +322 -270
- celldetective/gui/plot_measurements.py +1114 -0
- celldetective/gui/plot_signals_ui.py +20 -20
- celldetective/gui/process_block.py +449 -169
- celldetective/gui/retrain_segmentation_model_options.py +27 -26
- celldetective/gui/retrain_signal_model_options.py +25 -24
- celldetective/gui/seg_model_loader.py +31 -27
- celldetective/gui/signal_annotator.py +2326 -2295
- celldetective/gui/signal_annotator_options.py +18 -16
- celldetective/gui/styles.py +16 -1
- celldetective/gui/survival_ui.py +61 -39
- celldetective/gui/tableUI.py +60 -23
- celldetective/gui/thresholds_gui.py +68 -66
- celldetective/gui/viewers.py +596 -0
- celldetective/io.py +234 -23
- celldetective/measure.py +37 -32
- celldetective/neighborhood.py +495 -27
- celldetective/preprocessing.py +683 -0
- celldetective/scripts/analyze_signals.py +7 -0
- celldetective/scripts/measure_cells.py +12 -0
- celldetective/scripts/segment_cells.py +5 -0
- celldetective/scripts/track_cells.py +11 -0
- celldetective/signals.py +221 -98
- celldetective/tracking.py +0 -1
- celldetective/utils.py +178 -36
- celldetective-1.1.0.dist-info/METADATA +305 -0
- celldetective-1.1.0.dist-info/RECORD +80 -0
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/top_level.txt +1 -0
- tests/__init__.py +0 -0
- tests/test_events.py +28 -0
- tests/test_filters.py +24 -0
- tests/test_io.py +70 -0
- tests/test_measure.py +141 -0
- tests/test_neighborhood.py +70 -0
- tests/test_segmentation.py +93 -0
- tests/test_signals.py +135 -0
- tests/test_tracking.py +164 -0
- tests/test_utils.py +71 -0
- celldetective-1.0.2.post1.dist-info/METADATA +0 -221
- celldetective-1.0.2.post1.dist-info/RECORD +0 -66
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/LICENSE +0 -0
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/WHEEL +0 -0
- {celldetective-1.0.2.post1.dist-info → celldetective-1.1.0.dist-info}/entry_points.txt +0 -0
celldetective/__main__.py
CHANGED
|
@@ -15,10 +15,10 @@ class AppInitWindow(QMainWindow):
|
|
|
15
15
|
Initial window to set the experiment folder or create a new one.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
def __init__(self,
|
|
18
|
+
def __init__(self, parent_window=None):
|
|
19
19
|
super().__init__()
|
|
20
20
|
|
|
21
|
-
self.
|
|
21
|
+
self.parent_window = parent_window
|
|
22
22
|
self.Styles = Styles()
|
|
23
23
|
self.init_styles()
|
|
24
24
|
self.setWindowTitle("celldetective")
|
celldetective/events.py
CHANGED
|
@@ -1,49 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
|
|
3
|
-
def switch_to_events(classes, times, max_times, first_detections=None, left_censored=False, FrameToMin=None):
|
|
4
|
-
|
|
5
|
-
events = []
|
|
6
|
-
survival_times = []
|
|
7
|
-
if first_detections is None:
|
|
8
|
-
first_detections = np.zeros_like(max_times)
|
|
9
|
-
|
|
10
|
-
for c,t,mt,ft in zip(classes, times, max_times, first_detections):
|
|
11
|
-
|
|
12
|
-
if left_censored:
|
|
13
|
-
#print('left censored is True: exclude cells that exist in first frame')
|
|
14
|
-
if ft>0.:
|
|
15
|
-
if c==0:
|
|
16
|
-
if t>0:
|
|
17
|
-
dt = t - ft
|
|
18
|
-
#print('event: dt = ',dt, t, ft)
|
|
19
|
-
if dt>0:
|
|
20
|
-
events.append(1)
|
|
21
|
-
survival_times.append(dt)
|
|
22
|
-
elif c==1:
|
|
23
|
-
dt = mt - ft
|
|
24
|
-
if dt>0:
|
|
25
|
-
events.append(0)
|
|
26
|
-
survival_times.append(dt)
|
|
27
|
-
else:
|
|
28
|
-
pass
|
|
29
|
-
else:
|
|
30
|
-
if c==0:
|
|
31
|
-
if t>0:
|
|
32
|
-
events.append(1)
|
|
33
|
-
survival_times.append(t - ft)
|
|
34
|
-
elif c==1:
|
|
35
|
-
events.append(0)
|
|
36
|
-
survival_times.append(mt - ft)
|
|
37
|
-
else:
|
|
38
|
-
pass
|
|
39
3
|
|
|
40
|
-
|
|
41
|
-
print('convert to minutes!', FrameToMin)
|
|
42
|
-
survival_times = [s*FrameToMin for s in survival_times]
|
|
43
|
-
return events, survival_times
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def switch_to_events_v2(classes, event_times, max_times, origin_times=None, left_censored=True, FrameToMin=None):
|
|
4
|
+
def switch_to_events(classes, event_times, max_times, origin_times=None, left_censored=True, FrameToMin=None):
|
|
47
5
|
|
|
48
6
|
|
|
49
7
|
"""
|
|
@@ -144,6 +102,6 @@ def switch_to_events_v2(classes, event_times, max_times, origin_times=None, left
|
|
|
144
102
|
pass
|
|
145
103
|
|
|
146
104
|
if FrameToMin is not None:
|
|
147
|
-
print('convert to minutes!', FrameToMin)
|
|
105
|
+
#print('convert to minutes!', FrameToMin)
|
|
148
106
|
survival_times = [s*FrameToMin for s in survival_times]
|
|
149
107
|
return events, survival_times
|
celldetective/filters.py
CHANGED
|
@@ -35,8 +35,8 @@ def variance_filter(img, size):
|
|
|
35
35
|
|
|
36
36
|
size = int(size)
|
|
37
37
|
img = img.astype(float)
|
|
38
|
-
win_mean = snd.uniform_filter(img, (size,size))
|
|
39
|
-
win_sqr_mean = snd.uniform_filter(img**2, (size,size))
|
|
38
|
+
win_mean = snd.uniform_filter(img, (size,size), mode='wrap')
|
|
39
|
+
win_sqr_mean = snd.uniform_filter(img**2, (size,size), mode='wrap')
|
|
40
40
|
img = win_sqr_mean - win_mean**2
|
|
41
41
|
|
|
42
42
|
return img
|
|
@@ -45,8 +45,8 @@ def std_filter(img, size):
|
|
|
45
45
|
|
|
46
46
|
size = int(size)
|
|
47
47
|
img = img.astype(float)
|
|
48
|
-
win_mean = snd.uniform_filter(img, (size,size))
|
|
49
|
-
win_sqr_mean = snd.uniform_filter(img**2, (size, size))
|
|
48
|
+
win_mean = snd.uniform_filter(img, (size,size), mode='wrap')
|
|
49
|
+
win_sqr_mean = snd.uniform_filter(img**2, (size, size), mode='wrap')
|
|
50
50
|
win_sqr_mean[win_sqr_mean!=win_sqr_mean] = 0.
|
|
51
51
|
win_sqr_mean[win_sqr_mean<=0.] = 0. # add this to prevent sqrt from breaking
|
|
52
52
|
img = np.sqrt(win_sqr_mean - win_mean**2)
|
|
@@ -65,7 +65,6 @@ def otsu_filter(img, *kwargs):
|
|
|
65
65
|
return binary.astype(float)
|
|
66
66
|
|
|
67
67
|
def local_filter(img, *kwargs):
|
|
68
|
-
print(*kwargs)
|
|
69
68
|
thresh = threshold_local(img.astype(float), *kwargs)
|
|
70
69
|
binary = img >= thresh
|
|
71
70
|
return binary.astype(float)
|
celldetective/gui/__init__.py
CHANGED
|
@@ -13,7 +13,7 @@ from .retrain_signal_model_options import ConfigSignalModelTraining
|
|
|
13
13
|
from .retrain_segmentation_model_options import ConfigSegmentationModelTraining
|
|
14
14
|
from .thresholds_gui import ThresholdConfigWizard
|
|
15
15
|
from .seg_model_loader import SegmentationModelLoader
|
|
16
|
-
from .process_block import ProcessPanel, NeighPanel
|
|
16
|
+
from .process_block import ProcessPanel, NeighPanel, PreprocessingPanel
|
|
17
17
|
from .analyze_block import AnalysisPanel
|
|
18
18
|
from .control_panel import ControlPanel
|
|
19
19
|
from .configure_new_exp import ConfigNewExperiment
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import QFrame, QGridLayout, QComboBox, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, QMessageBox
|
|
1
|
+
from PyQt5.QtWidgets import QFrame, QGridLayout, QComboBox, QSizePolicy, QSpacerItem, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, QMessageBox
|
|
2
2
|
from PyQt5.QtCore import Qt, QSize
|
|
3
3
|
from PyQt5.QtGui import QIcon
|
|
4
4
|
|
|
5
5
|
from superqt.fonticon import icon
|
|
6
6
|
from fonticon_mdi6 import MDI6
|
|
7
7
|
import gc
|
|
8
|
+
|
|
9
|
+
from celldetective.gui.plot_measurements import ConfigMeasurementsPlot
|
|
8
10
|
from celldetective.io import get_segmentation_models_list, control_segmentation_napari, get_signal_models_list, control_tracking_btrack
|
|
9
11
|
from celldetective.gui import ConfigSurvival, ConfigSignalPlot
|
|
10
12
|
from celldetective.gui.gui_utils import QHSeperationLine
|
|
@@ -17,21 +19,23 @@ from glob import glob
|
|
|
17
19
|
from natsort import natsorted
|
|
18
20
|
import os
|
|
19
21
|
import pandas as pd
|
|
22
|
+
from celldetective.gui import Styles
|
|
20
23
|
|
|
21
|
-
class AnalysisPanel(QFrame):
|
|
22
|
-
def __init__(self,
|
|
24
|
+
class AnalysisPanel(QFrame, Styles):
|
|
25
|
+
def __init__(self, parent_window, title=None):
|
|
23
26
|
|
|
24
27
|
super().__init__()
|
|
25
|
-
self.
|
|
28
|
+
self.parent_window = parent_window
|
|
26
29
|
self.title = title
|
|
27
30
|
if self.title is None:
|
|
28
31
|
self.title=''
|
|
29
|
-
self.exp_channels = self.
|
|
30
|
-
self.exp_dir = self.
|
|
31
|
-
self.soft_path = self.
|
|
32
|
+
self.exp_channels = self.parent_window.exp_channels
|
|
33
|
+
self.exp_dir = self.parent_window.exp_dir
|
|
34
|
+
self.soft_path = self.parent_window.parent_window.soft_path
|
|
32
35
|
|
|
33
36
|
self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
34
37
|
self.grid = QVBoxLayout(self)
|
|
38
|
+
self.grid.setSpacing(20)
|
|
35
39
|
self.generate_header()
|
|
36
40
|
|
|
37
41
|
def generate_header(self):
|
|
@@ -41,7 +45,7 @@ class AnalysisPanel(QFrame):
|
|
|
41
45
|
|
|
42
46
|
"""
|
|
43
47
|
|
|
44
|
-
panel_title = QLabel(
|
|
48
|
+
panel_title = QLabel("Survival")
|
|
45
49
|
panel_title.setStyleSheet("""
|
|
46
50
|
font-weight: bold;
|
|
47
51
|
padding: 0px;
|
|
@@ -51,19 +55,37 @@ class AnalysisPanel(QFrame):
|
|
|
51
55
|
|
|
52
56
|
self.survival_btn = QPushButton("plot survival")
|
|
53
57
|
self.survival_btn.setIcon(QIcon(QIcon(os.sep.join([self.soft_path,'celldetective','icons','survival2.png']))))
|
|
54
|
-
self.survival_btn.setStyleSheet(self.
|
|
58
|
+
self.survival_btn.setStyleSheet(self.button_style_sheet_2)
|
|
55
59
|
self.survival_btn.setIconSize(QSize(35, 35))
|
|
56
60
|
self.survival_btn.clicked.connect(self.configure_survival)
|
|
57
61
|
self.grid.addWidget(self.survival_btn)
|
|
58
62
|
|
|
63
|
+
signal_lbl = QLabel("Single-cell signals")
|
|
64
|
+
signal_lbl.setStyleSheet("""
|
|
65
|
+
font-weight: bold;
|
|
66
|
+
padding: 0px;
|
|
67
|
+
""")
|
|
68
|
+
|
|
69
|
+
self.grid.addWidget(signal_lbl, alignment=Qt.AlignCenter)
|
|
59
70
|
|
|
60
71
|
self.plot_signal_btn = QPushButton("plot signals")
|
|
61
72
|
self.plot_signal_btn.setIcon(QIcon(QIcon(os.sep.join([self.soft_path,'celldetective','icons','signals_icon.png']))))
|
|
62
|
-
self.plot_signal_btn.setStyleSheet(self.
|
|
73
|
+
self.plot_signal_btn.setStyleSheet(self.button_style_sheet_2)
|
|
63
74
|
self.plot_signal_btn.setIconSize(QSize(35, 35))
|
|
64
75
|
self.plot_signal_btn.clicked.connect(self.configure_plot_signals)
|
|
65
76
|
self.grid.addWidget(self.plot_signal_btn)
|
|
66
77
|
|
|
78
|
+
# self.plot_measurements_btn = QPushButton("plot measurements")
|
|
79
|
+
# self.plot_measurements_btn.setIcon(icon(MDI6.chart_waterfall, color='black'))
|
|
80
|
+
# #self.plot_measurements_btn.setIcon(QIcon(QIcon(os.sep.join([self.soft_path,'celldetective','icons','signals_icon.png']))))
|
|
81
|
+
# self.plot_measurements_btn.setStyleSheet(self.button_style_sheet_2)
|
|
82
|
+
# self.plot_measurements_btn.setIconSize(QSize(35, 35))
|
|
83
|
+
# self.plot_measurements_btn.clicked.connect(self.configure_plot_measurements)
|
|
84
|
+
# self.grid.addWidget(self.plot_measurements_btn)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
|
88
|
+
self.grid.addItem(verticalSpacer)
|
|
67
89
|
|
|
68
90
|
def configure_survival(self):
|
|
69
91
|
print('survival analysis starting!!!')
|
|
@@ -75,6 +97,11 @@ class AnalysisPanel(QFrame):
|
|
|
75
97
|
self.ConfigSignalPlot = ConfigSignalPlot(self)
|
|
76
98
|
self.ConfigSignalPlot.show()
|
|
77
99
|
|
|
100
|
+
def configure_plot_measurements(self):
|
|
101
|
+
print('plot measurements analysis starting!!!')
|
|
102
|
+
self.ConfigMeasurementsPLot = ConfigMeasurementsPlot(self)
|
|
103
|
+
self.ConfigMeasurementsPLot.show()
|
|
104
|
+
|
|
78
105
|
|
|
79
106
|
# self.select_all_btn = QPushButton()
|
|
80
107
|
# self.select_all_btn.setIcon(icon(MDI6.checkbox_blank_outline,color="black"))
|
|
@@ -14,27 +14,28 @@ import os
|
|
|
14
14
|
import matplotlib.pyplot as plt
|
|
15
15
|
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
16
16
|
from glob import glob
|
|
17
|
+
from celldetective.gui import Styles
|
|
17
18
|
|
|
18
|
-
class ConfigTracking(QMainWindow):
|
|
19
|
+
class ConfigTracking(QMainWindow, Styles):
|
|
19
20
|
|
|
20
21
|
"""
|
|
21
22
|
UI to set tracking parameters for bTrack.
|
|
22
23
|
|
|
23
24
|
"""
|
|
24
25
|
|
|
25
|
-
def __init__(self,
|
|
26
|
+
def __init__(self, parent_window=None):
|
|
26
27
|
|
|
27
28
|
super().__init__()
|
|
28
|
-
self.
|
|
29
|
+
self.parent_window = parent_window
|
|
29
30
|
self.setWindowTitle("Configure tracking")
|
|
30
|
-
self.mode = self.
|
|
31
|
-
self.exp_dir = self.
|
|
31
|
+
self.mode = self.parent_window.mode
|
|
32
|
+
self.exp_dir = self.parent_window.exp_dir
|
|
32
33
|
if self.mode=="targets":
|
|
33
34
|
self.config_name = os.sep.join(["configs", "btrack_config_targets.json"])
|
|
34
|
-
self.track_instructions_write_path = self.
|
|
35
|
+
self.track_instructions_write_path = self.parent_window.exp_dir + os.sep.join(["configs","tracking_instructions_targets.json"])
|
|
35
36
|
elif self.mode=="effectors":
|
|
36
37
|
self.config_name = os.sep.join(["configs","btrack_config_effectors.json"])
|
|
37
|
-
self.track_instructions_write_path = self.
|
|
38
|
+
self.track_instructions_write_path = self.parent_window.exp_dir + os.sep.join(["configs", "tracking_instructions_effectors.json"])
|
|
38
39
|
self.soft_path = get_software_location()
|
|
39
40
|
|
|
40
41
|
exp_config = self.exp_dir +"config.ini"
|
|
@@ -42,7 +43,7 @@ class ConfigTracking(QMainWindow):
|
|
|
42
43
|
self.channel_names, self.channels = extract_experiment_channels(exp_config)
|
|
43
44
|
self.channel_names = np.array(self.channel_names)
|
|
44
45
|
self.channels = np.array(self.channels)
|
|
45
|
-
self.screen_height = self.
|
|
46
|
+
self.screen_height = self.parent_window.parent_window.parent_window.screen_height
|
|
46
47
|
|
|
47
48
|
center_window(self)
|
|
48
49
|
self.setMinimumWidth(540)
|
|
@@ -84,7 +85,7 @@ class ConfigTracking(QMainWindow):
|
|
|
84
85
|
main_layout.addWidget(self.post_proc_frame)
|
|
85
86
|
|
|
86
87
|
self.submit_btn = QPushButton('Save')
|
|
87
|
-
self.submit_btn.setStyleSheet(self.
|
|
88
|
+
self.submit_btn.setStyleSheet(self.parent_window.parent_window.parent_window.button_style_sheet)
|
|
88
89
|
self.submit_btn.clicked.connect(self.write_instructions)
|
|
89
90
|
main_layout.addWidget(self.submit_btn)
|
|
90
91
|
|
|
@@ -114,7 +115,7 @@ class ConfigTracking(QMainWindow):
|
|
|
114
115
|
|
|
115
116
|
self.select_post_proc_btn = QPushButton()
|
|
116
117
|
self.select_post_proc_btn.clicked.connect(self.activate_post_proc_options)
|
|
117
|
-
self.select_post_proc_btn.setStyleSheet(self.
|
|
118
|
+
self.select_post_proc_btn.setStyleSheet(self.button_select_all)
|
|
118
119
|
grid.addWidget(self.select_post_proc_btn, 0,0,1,4,alignment=Qt.AlignLeft)
|
|
119
120
|
|
|
120
121
|
self.post_proc_lbl = QLabel("POST-PROCESSING")
|
|
@@ -127,7 +128,7 @@ class ConfigTracking(QMainWindow):
|
|
|
127
128
|
self.collapse_post_proc_btn = QPushButton()
|
|
128
129
|
self.collapse_post_proc_btn.setIcon(icon(MDI6.chevron_down,color="black"))
|
|
129
130
|
self.collapse_post_proc_btn.setIconSize(QSize(20, 20))
|
|
130
|
-
self.collapse_post_proc_btn.setStyleSheet(self.
|
|
131
|
+
self.collapse_post_proc_btn.setStyleSheet(self.button_select_all)
|
|
131
132
|
grid.addWidget(self.collapse_post_proc_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
|
|
132
133
|
|
|
133
134
|
self.generate_post_proc_panel_contents()
|
|
@@ -148,7 +149,7 @@ class ConfigTracking(QMainWindow):
|
|
|
148
149
|
|
|
149
150
|
self.select_features_btn = QPushButton()
|
|
150
151
|
self.select_features_btn.clicked.connect(self.activate_feature_options)
|
|
151
|
-
self.select_features_btn.setStyleSheet(self.
|
|
152
|
+
self.select_features_btn.setStyleSheet(self.button_select_all)
|
|
152
153
|
grid.addWidget(self.select_features_btn, 0,0,1,4,alignment=Qt.AlignLeft)
|
|
153
154
|
|
|
154
155
|
|
|
@@ -162,7 +163,7 @@ class ConfigTracking(QMainWindow):
|
|
|
162
163
|
self.collapse_features_btn = QPushButton()
|
|
163
164
|
self.collapse_features_btn.setIcon(icon(MDI6.chevron_down,color="black"))
|
|
164
165
|
self.collapse_features_btn.setIconSize(QSize(20, 20))
|
|
165
|
-
self.collapse_features_btn.setStyleSheet(self.
|
|
166
|
+
self.collapse_features_btn.setStyleSheet(self.button_select_all)
|
|
166
167
|
grid.addWidget(self.collapse_features_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
|
|
167
168
|
|
|
168
169
|
self.generate_feature_panel_contents()
|
|
@@ -209,7 +210,7 @@ class ConfigTracking(QMainWindow):
|
|
|
209
210
|
self.min_tracklength_slider.setTickInterval(1)
|
|
210
211
|
self.min_tracklength_slider.setSingleStep(1)
|
|
211
212
|
self.min_tracklength_slider.setOrientation(1)
|
|
212
|
-
self.min_tracklength_slider.setRange(0,self.
|
|
213
|
+
self.min_tracklength_slider.setRange(0,self.parent_window.parent_window.len_movie)
|
|
213
214
|
self.min_tracklength_slider.setValue(0)
|
|
214
215
|
tracklength_layout.addWidget(QLabel('Min. tracklength: '),40)
|
|
215
216
|
tracklength_layout.addWidget(self.min_tracklength_slider, 60)
|
|
@@ -261,13 +262,13 @@ class ConfigTracking(QMainWindow):
|
|
|
261
262
|
|
|
262
263
|
self.feature_lbl = QLabel("Add features:")
|
|
263
264
|
self.del_feature_btn = QPushButton("")
|
|
264
|
-
self.del_feature_btn.setStyleSheet(self.
|
|
265
|
+
self.del_feature_btn.setStyleSheet(self.button_select_all)
|
|
265
266
|
self.del_feature_btn.setIcon(icon(MDI6.trash_can,color="black"))
|
|
266
267
|
self.del_feature_btn.setToolTip("Remove feature")
|
|
267
268
|
self.del_feature_btn.setIconSize(QSize(20, 20))
|
|
268
269
|
|
|
269
270
|
self.add_feature_btn = QPushButton("")
|
|
270
|
-
self.add_feature_btn.setStyleSheet(self.
|
|
271
|
+
self.add_feature_btn.setStyleSheet(self.button_select_all)
|
|
271
272
|
self.add_feature_btn.setIcon(icon(MDI6.filter_plus,color="black"))
|
|
272
273
|
self.add_feature_btn.setToolTip("Add feature")
|
|
273
274
|
self.add_feature_btn.setIconSize(QSize(20, 20))
|
|
@@ -324,7 +325,7 @@ class ConfigTracking(QMainWindow):
|
|
|
324
325
|
self.haralick_percentile_min_le = QLineEdit('0.01')
|
|
325
326
|
self.haralick_percentile_max_le = QLineEdit('99.9')
|
|
326
327
|
self.haralick_normalization_mode_btn = QPushButton()
|
|
327
|
-
self.haralick_normalization_mode_btn.setStyleSheet(self.
|
|
328
|
+
self.haralick_normalization_mode_btn.setStyleSheet(self.button_select_all)
|
|
328
329
|
self.haralick_normalization_mode_btn.setIcon(icon(MDI6.percent_circle,color="black"))
|
|
329
330
|
self.haralick_normalization_mode_btn.setIconSize(QSize(20, 20))
|
|
330
331
|
self.haralick_normalization_mode_btn.setToolTip("Switch to absolute normalization values.")
|
|
@@ -336,12 +337,12 @@ class ConfigTracking(QMainWindow):
|
|
|
336
337
|
self.haralick_hist_btn = QPushButton()
|
|
337
338
|
self.haralick_hist_btn.clicked.connect(self.control_haralick_intensity_histogram)
|
|
338
339
|
self.haralick_hist_btn.setIcon(icon(MDI6.poll,color="k"))
|
|
339
|
-
self.haralick_hist_btn.setStyleSheet(self.
|
|
340
|
+
self.haralick_hist_btn.setStyleSheet(self.button_select_all)
|
|
340
341
|
|
|
341
342
|
self.haralick_digit_btn = QPushButton()
|
|
342
343
|
self.haralick_digit_btn.clicked.connect(self.control_haralick_digitalization)
|
|
343
344
|
self.haralick_digit_btn.setIcon(icon(MDI6.image_check,color="k"))
|
|
344
|
-
self.haralick_digit_btn.setStyleSheet(self.
|
|
345
|
+
self.haralick_digit_btn.setStyleSheet(self.button_select_all)
|
|
345
346
|
|
|
346
347
|
self.haralick_layout = QVBoxLayout()
|
|
347
348
|
self.haralick_layout.setContentsMargins(20,20,20,20)
|
|
@@ -431,7 +432,7 @@ class ConfigTracking(QMainWindow):
|
|
|
431
432
|
self.collapse_config_btn = QPushButton()
|
|
432
433
|
self.collapse_config_btn.setIcon(icon(MDI6.chevron_down,color="black"))
|
|
433
434
|
self.collapse_config_btn.setIconSize(QSize(20, 20))
|
|
434
|
-
self.collapse_config_btn.setStyleSheet(self.
|
|
435
|
+
self.collapse_config_btn.setStyleSheet(self.button_select_all)
|
|
435
436
|
grid.addWidget(self.collapse_config_btn, 0, 0, 1, 4, alignment=Qt.AlignRight)
|
|
436
437
|
|
|
437
438
|
self.generate_config_panel_contents()
|
|
@@ -471,7 +472,7 @@ class ConfigTracking(QMainWindow):
|
|
|
471
472
|
self.upload_btrack_config_btn.setIcon(icon(MDI6.plus,color="black"))
|
|
472
473
|
self.upload_btrack_config_btn.setIconSize(QSize(20, 20))
|
|
473
474
|
self.upload_btrack_config_btn.setToolTip("Upload a new bTrack configuration.")
|
|
474
|
-
self.upload_btrack_config_btn.setStyleSheet(self.
|
|
475
|
+
self.upload_btrack_config_btn.setStyleSheet(self.button_select_all)
|
|
475
476
|
self.upload_btrack_config_btn.clicked.connect(self.upload_btrack_config)
|
|
476
477
|
btrack_config_layout.addWidget(self.upload_btrack_config_btn, 5) #4,3,1,1, alignment=Qt.AlignLeft
|
|
477
478
|
|
|
@@ -479,7 +480,7 @@ class ConfigTracking(QMainWindow):
|
|
|
479
480
|
self.reset_config_btn.setIcon(icon(MDI6.arrow_u_right_top,color="black"))
|
|
480
481
|
self.reset_config_btn.setIconSize(QSize(20, 20))
|
|
481
482
|
self.reset_config_btn.setToolTip("Reset the configuration to the default bTrack config.")
|
|
482
|
-
self.reset_config_btn.setStyleSheet(self.
|
|
483
|
+
self.reset_config_btn.setStyleSheet(self.button_select_all)
|
|
483
484
|
self.reset_config_btn.clicked.connect(self.reset_btrack_config)
|
|
484
485
|
btrack_config_layout.addWidget(self.reset_config_btn, 5) #4,3,1,1, alignment=Qt.AlignLeft
|
|
485
486
|
|
|
@@ -817,7 +818,7 @@ class ConfigTracking(QMainWindow):
|
|
|
817
818
|
Load the first frame of the first movie found in the experiment folder as a sample.
|
|
818
819
|
"""
|
|
819
820
|
|
|
820
|
-
movies = glob(self.
|
|
821
|
+
movies = glob(self.parent_window.parent_window.exp_dir + os.sep.join(["*","*","movie",self.parent_window.parent_window.movie_prefix+"*.tif"]))
|
|
821
822
|
if len(movies)==0:
|
|
822
823
|
msgBox = QMessageBox()
|
|
823
824
|
msgBox.setIcon(QMessageBox.Warning)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from PyQt5.QtWidgets import QWidget, QLineEdit, QMessageBox, QHBoxLayout, QVBoxLayout, QPushButton, QLabel, QComboBox, \
|
|
2
|
-
QCheckBox
|
|
2
|
+
QCheckBox, QRadioButton, QButtonGroup
|
|
3
3
|
from celldetective.gui.gui_utils import FigureCanvas, center_window, color_from_class
|
|
4
4
|
import numpy as np
|
|
5
5
|
import matplotlib.pyplot as plt
|
|
@@ -10,25 +10,27 @@ from PyQt5.QtCore import Qt, QSize
|
|
|
10
10
|
import os
|
|
11
11
|
from sklearn.metrics import r2_score
|
|
12
12
|
from scipy.optimize import curve_fit
|
|
13
|
+
from celldetective.gui import Styles
|
|
14
|
+
from math import ceil
|
|
13
15
|
|
|
14
16
|
def step_function(t, t_shift, dt):
|
|
15
17
|
return 1/(1+np.exp(-(t-t_shift)/dt))
|
|
16
18
|
|
|
17
|
-
class ClassifierWidget(QWidget):
|
|
19
|
+
class ClassifierWidget(QWidget, Styles):
|
|
18
20
|
|
|
19
|
-
def __init__(self,
|
|
21
|
+
def __init__(self, parent_window):
|
|
20
22
|
|
|
21
23
|
super().__init__()
|
|
22
24
|
|
|
23
|
-
self.
|
|
24
|
-
self.screen_height = self.
|
|
25
|
-
self.screen_width = self.
|
|
25
|
+
self.parent_window = parent_window
|
|
26
|
+
self.screen_height = self.parent_window.parent_window.parent_window.screen_height
|
|
27
|
+
self.screen_width = self.parent_window.parent_window.parent_window.screen_width
|
|
26
28
|
self.currentAlpha = 1.0
|
|
27
29
|
|
|
28
30
|
self.setWindowTitle("Custom classification")
|
|
29
31
|
|
|
30
|
-
self.mode = self.
|
|
31
|
-
self.df = self.
|
|
32
|
+
self.mode = self.parent_window.mode
|
|
33
|
+
self.df = self.parent_window.df
|
|
32
34
|
|
|
33
35
|
is_number = np.vectorize(lambda x: np.issubdtype(x, np.number))
|
|
34
36
|
is_number_test = is_number(self.df.dtypes)
|
|
@@ -53,7 +55,7 @@ class ClassifierWidget(QWidget):
|
|
|
53
55
|
fig_btn_hbox = QHBoxLayout()
|
|
54
56
|
fig_btn_hbox.addWidget(QLabel(''), 95)
|
|
55
57
|
self.project_times_btn = QPushButton('')
|
|
56
|
-
self.project_times_btn.setStyleSheet(self.
|
|
58
|
+
self.project_times_btn.setStyleSheet(self.parent_window.parent_window.parent_window.button_select_all)
|
|
57
59
|
self.project_times_btn.setIcon(icon(MDI6.math_integral,color="black"))
|
|
58
60
|
self.project_times_btn.setToolTip("Project measurements at all times.")
|
|
59
61
|
self.project_times_btn.setIconSize(QSize(20, 20))
|
|
@@ -111,7 +113,7 @@ class ClassifierWidget(QWidget):
|
|
|
111
113
|
self.features_cb[i].setCurrentIndex(i)
|
|
112
114
|
|
|
113
115
|
self.log_btns[i].setIcon(icon(MDI6.math_log,color="black"))
|
|
114
|
-
self.log_btns[i].setStyleSheet(self.
|
|
116
|
+
self.log_btns[i].setStyleSheet(self.button_select_all)
|
|
115
117
|
self.log_btns[i].clicked.connect(lambda ch, i=i: self.switch_to_log(i))
|
|
116
118
|
|
|
117
119
|
hbox_classify = QHBoxLayout()
|
|
@@ -124,19 +126,47 @@ class ClassifierWidget(QWidget):
|
|
|
124
126
|
hbox_classify.addWidget(self.submit_query_btn, 20)
|
|
125
127
|
layout.addLayout(hbox_classify)
|
|
126
128
|
|
|
127
|
-
self.time_corr = QCheckBox('Time correlated
|
|
129
|
+
self.time_corr = QCheckBox('Time correlated')
|
|
130
|
+
self.time_corr.toggled.connect(self.activate_time_corr_options)
|
|
128
131
|
if "TRACK_ID" in self.df.columns:
|
|
129
132
|
self.time_corr.setEnabled(True)
|
|
130
133
|
else:
|
|
131
134
|
self.time_corr.setEnabled(False)
|
|
132
135
|
layout.addWidget(self.time_corr,alignment=Qt.AlignCenter)
|
|
136
|
+
|
|
137
|
+
self.irreversible_event_btn = QRadioButton('irreversible event')
|
|
138
|
+
self.unique_state_btn = QRadioButton('unique state')
|
|
139
|
+
self.time_corr_options = [self.irreversible_event_btn, self.unique_state_btn]
|
|
140
|
+
time_corr_btn_group = QButtonGroup()
|
|
141
|
+
self.unique_state_btn.click()
|
|
142
|
+
for btn in self.time_corr_options:
|
|
143
|
+
time_corr_btn_group.addButton(btn)
|
|
144
|
+
btn.setEnabled(False)
|
|
145
|
+
|
|
146
|
+
time_corr_layout = QHBoxLayout()
|
|
147
|
+
time_corr_layout.addWidget(self.unique_state_btn, 50, alignment=Qt.AlignCenter)
|
|
148
|
+
time_corr_layout.addWidget(self.irreversible_event_btn, 50,alignment=Qt.AlignCenter)
|
|
149
|
+
layout.addLayout(time_corr_layout)
|
|
150
|
+
|
|
151
|
+
layout.addWidget(QLabel())
|
|
152
|
+
|
|
133
153
|
self.submit_btn = QPushButton('apply')
|
|
154
|
+
self.submit_btn.setStyleSheet(self.button_style_sheet)
|
|
134
155
|
self.submit_btn.clicked.connect(self.submit_classification)
|
|
135
156
|
layout.addWidget(self.submit_btn, 30)
|
|
136
157
|
|
|
137
158
|
self.frame_slider.valueChanged.connect(self.set_frame)
|
|
138
159
|
self.alpha_slider.valueChanged.connect(self.set_transparency)
|
|
139
160
|
|
|
161
|
+
def activate_time_corr_options(self):
|
|
162
|
+
|
|
163
|
+
if self.time_corr.isChecked():
|
|
164
|
+
for btn in self.time_corr_options:
|
|
165
|
+
btn.setEnabled(True)
|
|
166
|
+
else:
|
|
167
|
+
for btn in self.time_corr_options:
|
|
168
|
+
btn.setEnabled(False)
|
|
169
|
+
|
|
140
170
|
def init_class(self):
|
|
141
171
|
|
|
142
172
|
self.class_name = 'custom'
|
|
@@ -277,14 +307,27 @@ class ClassifierWidget(QWidget):
|
|
|
277
307
|
for tid,track in self.df.groupby(['position','TRACK_ID']):
|
|
278
308
|
indices = track[self.class_name_user].index
|
|
279
309
|
status_values = track[stat_col].to_numpy()
|
|
280
|
-
if
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
310
|
+
if self.irreversible_event_btn.isChecked():
|
|
311
|
+
if np.all([s==0 for s in status_values]):
|
|
312
|
+
self.df.loc[indices, self.class_name_user] = 1
|
|
313
|
+
elif np.all([s==1 for s in status_values]):
|
|
314
|
+
self.df.loc[indices, self.class_name_user] = 2
|
|
315
|
+
self.df.loc[indices, self.class_name_user.replace('class','status')] = 2
|
|
316
|
+
else:
|
|
317
|
+
self.df.loc[indices, self.class_name_user] = 2
|
|
318
|
+
elif self.unique_state_btn.isChecked():
|
|
319
|
+
frames = track['FRAME'].to_numpy()
|
|
320
|
+
t_first = track['t_firstdetection'].to_numpy()[0]
|
|
321
|
+
median_status = np.nanmedian(status_values[frames>=t_first])
|
|
322
|
+
c = ceil(median_status)
|
|
323
|
+
if c==0:
|
|
324
|
+
self.df.loc[indices, self.class_name_user] = 1
|
|
325
|
+
self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
|
|
326
|
+
elif c==1:
|
|
327
|
+
self.df.loc[indices, self.class_name_user] = 2
|
|
328
|
+
self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
|
|
329
|
+
if self.irreversible_event_btn.isChecked():
|
|
330
|
+
self.estimate_time()
|
|
288
331
|
else:
|
|
289
332
|
self.group_name_user = 'group_' + self.name_le.text()
|
|
290
333
|
print(f'User defined characteristic group name: {self.group_name_user}.')
|