celldetective 1.1.0__py3-none-any.whl → 1.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- celldetective/__main__.py +5 -19
- celldetective/extra_properties.py +62 -52
- celldetective/gui/control_panel.py +5 -0
- celldetective/gui/layouts.py +1 -0
- celldetective/gui/measurement_options.py +13 -109
- celldetective/gui/plot_signals_ui.py +1 -0
- celldetective/gui/process_block.py +1 -1
- celldetective/gui/survival_ui.py +7 -1
- celldetective/gui/tableUI.py +280 -28
- celldetective/gui/thresholds_gui.py +7 -5
- celldetective/gui/viewers.py +169 -22
- celldetective/io.py +14 -5
- celldetective/measure.py +13 -238
- celldetective/models/segmentation_effectors/primNK_cfse/config_input.json +29 -0
- celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer +0 -0
- celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json +37 -0
- celldetective/neighborhood.py +4 -1
- celldetective/preprocessing.py +483 -143
- celldetective/scripts/segment_cells.py +15 -4
- celldetective/scripts/train_segmentation_model.py +35 -34
- celldetective/segmentation.py +14 -9
- celldetective/signals.py +13 -231
- celldetective/tracking.py +2 -1
- celldetective/utils.py +437 -26
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/METADATA +1 -1
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/RECORD +32 -28
- tests/test_preprocessing.py +37 -0
- tests/test_utils.py +48 -1
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/LICENSE +0 -0
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/WHEEL +0 -0
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/top_level.txt +0 -0
celldetective/gui/tableUI.py
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import QMainWindow, QTableView, QAction, QMenu,QFileDialog, QLineEdit, QHBoxLayout, QWidget, QPushButton, QVBoxLayout, QComboBox, QLabel, QCheckBox, QMessageBox
|
|
1
|
+
from PyQt5.QtWidgets import QRadioButton, QButtonGroup, QMainWindow, QTableView, QAction, QMenu,QFileDialog, QLineEdit, QHBoxLayout, QWidget, QPushButton, QVBoxLayout, QComboBox, QLabel, QCheckBox, QMessageBox
|
|
2
2
|
from PyQt5.QtCore import Qt, QAbstractTableModel
|
|
3
3
|
import pandas as pd
|
|
4
4
|
import matplotlib.pyplot as plt
|
|
5
5
|
from matplotlib.cm import viridis
|
|
6
6
|
plt.rcParams['svg.fonttype'] = 'none'
|
|
7
7
|
from celldetective.gui.gui_utils import FigureCanvas, center_window
|
|
8
|
+
from celldetective.utils import differentiate_per_track
|
|
8
9
|
import numpy as np
|
|
9
10
|
import seaborn as sns
|
|
10
11
|
import matplotlib.cm as mcm
|
|
11
12
|
import os
|
|
12
13
|
from celldetective.gui import Styles
|
|
13
|
-
from superqt import QColormapComboBox
|
|
14
|
+
from superqt import QColormapComboBox, QLabeledSlider, QSearchableComboBox
|
|
15
|
+
from math import floor
|
|
16
|
+
|
|
17
|
+
from matplotlib import colormaps
|
|
14
18
|
|
|
15
19
|
class PandasModel(QAbstractTableModel):
|
|
16
20
|
|
|
@@ -55,6 +59,7 @@ class QueryWidget(QWidget):
|
|
|
55
59
|
self.submit_btn = QPushButton('submit')
|
|
56
60
|
self.submit_btn.clicked.connect(self.filter_table)
|
|
57
61
|
layout.addWidget(self.submit_btn, 30)
|
|
62
|
+
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
58
63
|
|
|
59
64
|
def filter_table(self):
|
|
60
65
|
try:
|
|
@@ -67,6 +72,84 @@ class QueryWidget(QWidget):
|
|
|
67
72
|
print(e)
|
|
68
73
|
return None
|
|
69
74
|
|
|
75
|
+
class DifferentiateColWidget(QWidget, Styles):
|
|
76
|
+
|
|
77
|
+
def __init__(self, parent_window, column=None):
|
|
78
|
+
|
|
79
|
+
super().__init__()
|
|
80
|
+
self.parent_window = parent_window
|
|
81
|
+
self.column = column
|
|
82
|
+
|
|
83
|
+
self.setWindowTitle("d/dt")
|
|
84
|
+
# Create the QComboBox and add some items
|
|
85
|
+
center_window(self)
|
|
86
|
+
|
|
87
|
+
layout = QVBoxLayout(self)
|
|
88
|
+
layout.setContentsMargins(30,30,30,30)
|
|
89
|
+
|
|
90
|
+
self.measurements_cb = QComboBox()
|
|
91
|
+
self.measurements_cb.addItems(list(self.parent_window.data.columns))
|
|
92
|
+
if self.column is not None:
|
|
93
|
+
idx = self.measurements_cb.findText(self.column)
|
|
94
|
+
self.measurements_cb.setCurrentIndex(idx)
|
|
95
|
+
|
|
96
|
+
measurement_layout = QHBoxLayout()
|
|
97
|
+
measurement_layout.addWidget(QLabel('measurements: '), 25)
|
|
98
|
+
measurement_layout.addWidget(self.measurements_cb, 75)
|
|
99
|
+
layout.addLayout(measurement_layout)
|
|
100
|
+
|
|
101
|
+
self.window_size_slider = QLabeledSlider()
|
|
102
|
+
self.window_size_slider.setRange(1,np.nanmax(self.parent_window.data.FRAME.to_numpy()))
|
|
103
|
+
self.window_size_slider.setValue(3)
|
|
104
|
+
window_layout = QHBoxLayout()
|
|
105
|
+
window_layout.addWidget(QLabel('window size: '), 25)
|
|
106
|
+
window_layout.addWidget(self.window_size_slider, 75)
|
|
107
|
+
layout.addLayout(window_layout)
|
|
108
|
+
|
|
109
|
+
self.backward_btn = QRadioButton('backward')
|
|
110
|
+
self.bi_btn = QRadioButton('bi')
|
|
111
|
+
self.bi_btn.click()
|
|
112
|
+
self.forward_btn = QRadioButton('forward')
|
|
113
|
+
self.mode_btn_group = QButtonGroup()
|
|
114
|
+
self.mode_btn_group.addButton(self.backward_btn)
|
|
115
|
+
self.mode_btn_group.addButton(self.bi_btn)
|
|
116
|
+
self.mode_btn_group.addButton(self.forward_btn)
|
|
117
|
+
|
|
118
|
+
mode_layout = QHBoxLayout()
|
|
119
|
+
mode_layout.addWidget(QLabel('mode: '),25)
|
|
120
|
+
mode_sublayout = QHBoxLayout()
|
|
121
|
+
mode_sublayout.addWidget(self.backward_btn, 33, alignment=Qt.AlignCenter)
|
|
122
|
+
mode_sublayout.addWidget(self.bi_btn, 33, alignment=Qt.AlignCenter)
|
|
123
|
+
mode_sublayout.addWidget(self.forward_btn, 33, alignment=Qt.AlignCenter)
|
|
124
|
+
mode_layout.addLayout(mode_sublayout, 75)
|
|
125
|
+
layout.addLayout(mode_layout)
|
|
126
|
+
|
|
127
|
+
self.submit_btn = QPushButton('Compute')
|
|
128
|
+
self.submit_btn.setStyleSheet(self.button_style_sheet)
|
|
129
|
+
self.submit_btn.clicked.connect(self.compute_derivative_and_add_new_column)
|
|
130
|
+
layout.addWidget(self.submit_btn, 30)
|
|
131
|
+
|
|
132
|
+
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def compute_derivative_and_add_new_column(self):
|
|
136
|
+
|
|
137
|
+
if self.bi_btn.isChecked():
|
|
138
|
+
mode = 'bi'
|
|
139
|
+
elif self.forward_btn.isChecked():
|
|
140
|
+
mode = 'forward'
|
|
141
|
+
elif self.backward_btn.isChecked():
|
|
142
|
+
mode = 'backward'
|
|
143
|
+
self.parent_window.data = differentiate_per_track(self.parent_window.data,
|
|
144
|
+
self.measurements_cb.currentText(),
|
|
145
|
+
window_size=self.window_size_slider.value(),
|
|
146
|
+
mode=mode)
|
|
147
|
+
self.parent_window.model = PandasModel(self.parent_window.data)
|
|
148
|
+
self.parent_window.table_view.setModel(self.parent_window.model)
|
|
149
|
+
self.close()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
70
153
|
class RenameColWidget(QWidget):
|
|
71
154
|
|
|
72
155
|
def __init__(self, parent_window, column=None):
|
|
@@ -90,6 +173,7 @@ class RenameColWidget(QWidget):
|
|
|
90
173
|
self.submit_btn = QPushButton('rename')
|
|
91
174
|
self.submit_btn.clicked.connect(self.rename_col)
|
|
92
175
|
layout.addWidget(self.submit_btn, 30)
|
|
176
|
+
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
93
177
|
|
|
94
178
|
def rename_col(self):
|
|
95
179
|
|
|
@@ -128,6 +212,8 @@ class TableUI(QMainWindow, Styles):
|
|
|
128
212
|
self.model = PandasModel(data)
|
|
129
213
|
self.table_view.setModel(self.model)
|
|
130
214
|
self.table_view.resizeColumnsToContents()
|
|
215
|
+
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
216
|
+
|
|
131
217
|
|
|
132
218
|
def _createActions(self):
|
|
133
219
|
|
|
@@ -231,8 +317,17 @@ class TableUI(QMainWindow, Styles):
|
|
|
231
317
|
# check only one col selected and assert is numerical
|
|
232
318
|
# open widget to select window parameters, directionality
|
|
233
319
|
# create new col
|
|
234
|
-
|
|
235
|
-
|
|
320
|
+
|
|
321
|
+
x = self.table_view.selectedIndexes()
|
|
322
|
+
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
323
|
+
if col_idx!=0:
|
|
324
|
+
cols = np.array(list(self.data.columns))
|
|
325
|
+
selected_col = str(cols[col_idx][0])
|
|
326
|
+
else:
|
|
327
|
+
selected_col = None
|
|
328
|
+
|
|
329
|
+
self.diffWidget = DifferentiateColWidget(self, selected_col)
|
|
330
|
+
self.diffWidget.show()
|
|
236
331
|
|
|
237
332
|
|
|
238
333
|
def groupby_time_table(self):
|
|
@@ -270,25 +365,102 @@ class TableUI(QMainWindow, Styles):
|
|
|
270
365
|
def set_projection_mode_tracks(self):
|
|
271
366
|
|
|
272
367
|
self.projectionWidget = QWidget()
|
|
368
|
+
self.projectionWidget.setMinimumWidth(500)
|
|
273
369
|
self.projectionWidget.setWindowTitle('Set projection mode')
|
|
274
370
|
|
|
275
371
|
layout = QVBoxLayout()
|
|
276
372
|
self.projectionWidget.setLayout(layout)
|
|
373
|
+
|
|
374
|
+
self.projection_option = QRadioButton('global operation: ')
|
|
375
|
+
self.projection_option.setToolTip('Collapse the cell track measurements with an operation over each track.')
|
|
376
|
+
self.projection_option.toggled.connect(self.enable_projection_options)
|
|
277
377
|
self.projection_op_cb = QComboBox()
|
|
278
378
|
self.projection_op_cb.addItems(['mean','median','min','max', 'prod', 'sum'])
|
|
279
|
-
hbox = QHBoxLayout()
|
|
280
|
-
hbox.addWidget(QLabel('operation: '), 33)
|
|
281
|
-
hbox.addWidget(self.projection_op_cb, 66)
|
|
282
|
-
layout.addLayout(hbox)
|
|
283
379
|
|
|
284
|
-
|
|
380
|
+
projection_layout = QHBoxLayout()
|
|
381
|
+
projection_layout.addWidget(self.projection_option, 33)
|
|
382
|
+
projection_layout.addWidget(self.projection_op_cb, 66)
|
|
383
|
+
layout.addLayout(projection_layout)
|
|
384
|
+
|
|
385
|
+
self.event_time_option = QRadioButton('@event time: ')
|
|
386
|
+
self.event_time_option.setToolTip('Pick the measurements at a specific event time.')
|
|
387
|
+
self.event_time_option.toggled.connect(self.enable_projection_options)
|
|
388
|
+
self.event_times_cb = QComboBox()
|
|
389
|
+
cols = np.array(self.data.columns)
|
|
390
|
+
time_cols = np.array([c.startswith('t_') for c in cols])
|
|
391
|
+
time_cols = list(cols[time_cols])
|
|
392
|
+
if 't0' in list(self.data.columns):
|
|
393
|
+
time_cols.append('t0')
|
|
394
|
+
self.event_times_cb.addItems(time_cols)
|
|
395
|
+
self.event_times_cb.setEnabled(False)
|
|
396
|
+
|
|
397
|
+
event_time_layout = QHBoxLayout()
|
|
398
|
+
event_time_layout.addWidget(self.event_time_option, 33)
|
|
399
|
+
event_time_layout.addWidget(self.event_times_cb, 66)
|
|
400
|
+
layout.addLayout(event_time_layout)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
self.per_status_option = QRadioButton('per status: ')
|
|
404
|
+
self.per_status_option.setToolTip('Collapse the cell track measurements independently for each of the cell state.')
|
|
405
|
+
self.per_status_option.toggled.connect(self.enable_projection_options)
|
|
406
|
+
self.per_status_cb = QComboBox()
|
|
407
|
+
self.status_operation = QComboBox()
|
|
408
|
+
self.status_operation.setEnabled(False)
|
|
409
|
+
self.status_operation.addItems(['mean','median','min','max', 'prod', 'sum'])
|
|
410
|
+
|
|
411
|
+
status_cols = np.array([c.startswith('status_') for c in cols])
|
|
412
|
+
status_cols = list(cols[status_cols])
|
|
413
|
+
if 'status' in list(self.data.columns):
|
|
414
|
+
status_cols.append('status')
|
|
415
|
+
self.per_status_cb.addItems(status_cols)
|
|
416
|
+
self.per_status_cb.setEnabled(False)
|
|
417
|
+
|
|
418
|
+
per_status_layout = QHBoxLayout()
|
|
419
|
+
per_status_layout.addWidget(self.per_status_option, 33)
|
|
420
|
+
per_status_layout.addWidget(self.per_status_cb, 66)
|
|
421
|
+
layout.addLayout(per_status_layout)
|
|
422
|
+
|
|
423
|
+
status_operation_layout = QHBoxLayout()
|
|
424
|
+
status_operation_layout.addWidget(QLabel('operation: '), 33, alignment=Qt.AlignRight)
|
|
425
|
+
status_operation_layout.addWidget(self.status_operation, 66)
|
|
426
|
+
layout.addLayout(status_operation_layout)
|
|
427
|
+
|
|
428
|
+
self.btn_projection_group = QButtonGroup()
|
|
429
|
+
self.btn_projection_group.addButton(self.projection_option)
|
|
430
|
+
self.btn_projection_group.addButton(self.event_time_option)
|
|
431
|
+
self.btn_projection_group.addButton(self.per_status_option)
|
|
432
|
+
|
|
433
|
+
apply_layout = QHBoxLayout()
|
|
434
|
+
|
|
435
|
+
self.set_projection_btn = QPushButton('Apply')
|
|
285
436
|
self.set_projection_btn.setStyleSheet(self.button_style_sheet)
|
|
286
437
|
self.set_projection_btn.clicked.connect(self.set_proj_mode)
|
|
287
|
-
|
|
438
|
+
apply_layout.addWidget(QLabel(''), 33)
|
|
439
|
+
apply_layout.addWidget(self.set_projection_btn,33)
|
|
440
|
+
apply_layout.addWidget(QLabel(''),33)
|
|
441
|
+
layout.addLayout(apply_layout)
|
|
288
442
|
|
|
289
443
|
self.projectionWidget.show()
|
|
290
444
|
center_window(self.projectionWidget)
|
|
291
445
|
|
|
446
|
+
def enable_projection_options(self):
|
|
447
|
+
|
|
448
|
+
if self.projection_option.isChecked():
|
|
449
|
+
self.projection_op_cb.setEnabled(True)
|
|
450
|
+
self.event_times_cb.setEnabled(False)
|
|
451
|
+
self.per_status_cb.setEnabled(False)
|
|
452
|
+
self.status_operation.setEnabled(False)
|
|
453
|
+
elif self.event_time_option.isChecked():
|
|
454
|
+
self.projection_op_cb.setEnabled(False)
|
|
455
|
+
self.event_times_cb.setEnabled(True)
|
|
456
|
+
self.per_status_cb.setEnabled(False)
|
|
457
|
+
self.status_operation.setEnabled(False)
|
|
458
|
+
elif self.per_status_option.isChecked():
|
|
459
|
+
self.projection_op_cb.setEnabled(False)
|
|
460
|
+
self.event_times_cb.setEnabled(False)
|
|
461
|
+
self.per_status_cb.setEnabled(True)
|
|
462
|
+
self.status_operation.setEnabled(True)
|
|
463
|
+
|
|
292
464
|
def set_1D_plot_params(self):
|
|
293
465
|
|
|
294
466
|
self.plot1Dparams = QWidget()
|
|
@@ -316,12 +488,12 @@ class TableUI(QMainWindow, Styles):
|
|
|
316
488
|
layout.addWidget(self.box_check)
|
|
317
489
|
layout.addWidget(self.boxenplot_check)
|
|
318
490
|
|
|
319
|
-
self.x_cb =
|
|
491
|
+
self.x_cb = QSearchableComboBox()
|
|
320
492
|
self.x_cb.addItems(['--']+list(self.data.columns))
|
|
321
493
|
|
|
322
|
-
self.hue_cb =
|
|
323
|
-
self.hue_cb.addItems(list(self.data.columns))
|
|
324
|
-
idx = self.hue_cb.findText('
|
|
494
|
+
self.hue_cb = QSearchableComboBox()
|
|
495
|
+
self.hue_cb.addItems(['--']+list(self.data.columns))
|
|
496
|
+
idx = self.hue_cb.findText('--')
|
|
325
497
|
|
|
326
498
|
self.x_cb.findText('--')
|
|
327
499
|
hbox = QHBoxLayout()
|
|
@@ -335,7 +507,12 @@ class TableUI(QMainWindow, Styles):
|
|
|
335
507
|
layout.addLayout(hbox)
|
|
336
508
|
|
|
337
509
|
self.cmap_cb = QColormapComboBox()
|
|
338
|
-
|
|
510
|
+
for cm in list(colormaps):
|
|
511
|
+
try:
|
|
512
|
+
self.cmap_cb.addColormap(cm)
|
|
513
|
+
except:
|
|
514
|
+
pass
|
|
515
|
+
|
|
339
516
|
hbox = QHBoxLayout()
|
|
340
517
|
hbox.addWidget(QLabel('colormap: '), 33)
|
|
341
518
|
hbox.addWidget(self.cmap_cb, 66)
|
|
@@ -370,9 +547,16 @@ class TableUI(QMainWindow, Styles):
|
|
|
370
547
|
y = self.data.iloc[row_idx_i, unique_cols]
|
|
371
548
|
|
|
372
549
|
cmap = getattr(mcm, self.cmap_cb.currentText())
|
|
373
|
-
hue_variable = self.hue_cb.currentText()
|
|
374
550
|
|
|
375
|
-
|
|
551
|
+
try:
|
|
552
|
+
hue_variable = self.hue_cb.currentText()
|
|
553
|
+
colors = [cmap(i / len(self.data[hue_variable].unique())) for i in range(len(self.data[hue_variable].unique()))]
|
|
554
|
+
except:
|
|
555
|
+
colors = None
|
|
556
|
+
|
|
557
|
+
if self.hue_cb.currentText()=='--':
|
|
558
|
+
hue_variable = None
|
|
559
|
+
|
|
376
560
|
#for w,well_group in self.data.groupby('well_index'):
|
|
377
561
|
|
|
378
562
|
legend=True
|
|
@@ -435,18 +619,86 @@ class TableUI(QMainWindow, Styles):
|
|
|
435
619
|
|
|
436
620
|
|
|
437
621
|
def set_proj_mode(self):
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
622
|
+
|
|
623
|
+
self.static_columns = ['well_index', 'well_name', 'pos_name', 'position', 'well', 'status', 't0', 'class','cell_type','concentration', 'antibody', 'pharmaceutical_agent','TRACK_ID','position']
|
|
624
|
+
|
|
625
|
+
if self.projection_option.isChecked():
|
|
626
|
+
|
|
627
|
+
self.projection_mode = self.projection_op_cb.currentText()
|
|
628
|
+
op = getattr(self.data.groupby(['position', 'TRACK_ID']), self.projection_mode)
|
|
629
|
+
group_table = op(self.data.groupby(['position', 'TRACK_ID']))
|
|
630
|
+
|
|
631
|
+
for c in self.static_columns:
|
|
632
|
+
try:
|
|
633
|
+
group_table[c] = self.data.groupby(['position','TRACK_ID'])[c].apply(lambda x: x.unique()[0])
|
|
634
|
+
except Exception as e:
|
|
635
|
+
print(e)
|
|
636
|
+
pass
|
|
637
|
+
|
|
638
|
+
for col in ['TRACK_ID']:
|
|
639
|
+
first_column = group_table.pop(col)
|
|
640
|
+
group_table.insert(0, col, first_column)
|
|
641
|
+
group_table.pop('FRAME')
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
elif self.event_time_option.isChecked():
|
|
645
|
+
time_of_interest = self.event_times_cb.currentText()
|
|
646
|
+
self.projection_mode = f"measurements at {time_of_interest}"
|
|
647
|
+
new_table = []
|
|
648
|
+
for tid,group in self.data.groupby(['position','TRACK_ID']):
|
|
649
|
+
time = group[time_of_interest].values[0]
|
|
650
|
+
if time==time:
|
|
651
|
+
time = floor(time) # floor for onset
|
|
652
|
+
else:
|
|
653
|
+
continue
|
|
654
|
+
frames = group['FRAME'].values
|
|
655
|
+
values = group.loc[group['FRAME']==time,:].to_numpy()
|
|
656
|
+
if len(values)>0:
|
|
657
|
+
values = dict(zip(list(self.data.columns), values[0]))
|
|
658
|
+
values.update({'TRACK_ID': tid[1]})
|
|
659
|
+
values.update({'position': tid[0]})
|
|
660
|
+
new_table.append(values)
|
|
661
|
+
|
|
662
|
+
group_table = pd.DataFrame(new_table)
|
|
663
|
+
for col in ['TRACK_ID']:
|
|
664
|
+
first_column = group_table.pop(col)
|
|
665
|
+
group_table.insert(0, col, first_column)
|
|
666
|
+
|
|
667
|
+
group_table = group_table.sort_values(by=['position','TRACK_ID','FRAME'],ignore_index=True)
|
|
668
|
+
group_table = group_table.reset_index(drop=True)
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
elif self.per_status_option.isChecked():
|
|
672
|
+
|
|
673
|
+
status_of_interest = self.per_status_cb.currentText()
|
|
674
|
+
self.projection_mode = f'{self.status_operation.currentText()} per {status_of_interest}'
|
|
675
|
+
self.data = self.data.dropna(subset=status_of_interest,ignore_index=True)
|
|
676
|
+
unique_statuses = np.unique(self.data[status_of_interest].to_numpy())
|
|
677
|
+
|
|
678
|
+
df_sections = []
|
|
679
|
+
for s in unique_statuses:
|
|
680
|
+
subtab = self.data.loc[self.data[status_of_interest]==s,:]
|
|
681
|
+
op = getattr(subtab.groupby(['position', 'TRACK_ID']), self.status_operation.currentText())
|
|
682
|
+
subtab_projected = op(subtab.groupby(['position', 'TRACK_ID']))
|
|
683
|
+
frame_duration = subtab.groupby(['position','TRACK_ID']).size().to_numpy()
|
|
684
|
+
for c in self.static_columns:
|
|
685
|
+
try:
|
|
686
|
+
subtab_projected[c] = subtab.groupby(['position', 'TRACK_ID'])[c].apply(lambda x: x.unique()[0])
|
|
687
|
+
except Exception as e:
|
|
688
|
+
print(e)
|
|
689
|
+
pass
|
|
690
|
+
subtab_projected['duration_in_state'] = frame_duration
|
|
691
|
+
df_sections.append(subtab_projected)
|
|
692
|
+
|
|
693
|
+
group_table = pd.concat(df_sections,axis=0,ignore_index=True)
|
|
694
|
+
for col in ['duration_in_state',status_of_interest,'TRACK_ID']:
|
|
695
|
+
first_column = group_table.pop(col)
|
|
696
|
+
group_table.insert(0, col, first_column)
|
|
697
|
+
group_table.pop('FRAME')
|
|
698
|
+
group_table = group_table.sort_values(by=['position','TRACK_ID',status_of_interest],ignore_index=True)
|
|
699
|
+
group_table = group_table.reset_index(drop=True)
|
|
700
|
+
|
|
442
701
|
|
|
443
|
-
self.static_columns = ['well_index', 'well_name', 'pos_name', 'position', 'well', 'status', 't0', 'class', 'concentration', 'antibody', 'pharmaceutical_agent']
|
|
444
|
-
for c in self.static_columns:
|
|
445
|
-
try:
|
|
446
|
-
group_table[c] = self.data.groupby(['position','TRACK_ID'])[c].apply(lambda x: x.unique()[0])
|
|
447
|
-
except Exception as e:
|
|
448
|
-
print(e)
|
|
449
|
-
pass
|
|
450
702
|
self.subtable = TableUI(group_table,f"Group by tracks: {self.projection_mode}", plot_mode="static")
|
|
451
703
|
self.subtable.show()
|
|
452
704
|
|
|
@@ -12,7 +12,7 @@ from skimage.morphology import disk
|
|
|
12
12
|
|
|
13
13
|
from celldetective.filters import std_filter, gauss_filter
|
|
14
14
|
from celldetective.gui.gui_utils import center_window, FigureCanvas, ListWidget, FilterChoice, color_from_class
|
|
15
|
-
from celldetective.utils import get_software_location, extract_experiment_channels, rename_intensity_column
|
|
15
|
+
from celldetective.utils import get_software_location, extract_experiment_channels, rename_intensity_column, estimate_unreliable_edge
|
|
16
16
|
from celldetective.io import auto_load_number_of_frames, load_frames
|
|
17
17
|
from celldetective.segmentation import threshold_image, identify_markers_from_binary, apply_watershed, \
|
|
18
18
|
segment_frame_from_thresholds
|
|
@@ -61,6 +61,7 @@ class ThresholdConfigWizard(QMainWindow, Styles):
|
|
|
61
61
|
self.onlyFloat = QDoubleValidator()
|
|
62
62
|
self.onlyInt = QIntValidator()
|
|
63
63
|
self.cell_properties = ['centroid', 'area', 'perimeter', 'eccentricity', 'intensity_mean', 'solidity']
|
|
64
|
+
self.edge = None
|
|
64
65
|
|
|
65
66
|
if self.mode == "targets":
|
|
66
67
|
self.config_out_name = "threshold_targets.json"
|
|
@@ -437,7 +438,7 @@ class ThresholdConfigWizard(QMainWindow, Styles):
|
|
|
437
438
|
self.im = self.ax.imshow(self.img, cmap='gray')
|
|
438
439
|
|
|
439
440
|
self.binary = threshold_image(self.img, self.threshold_slider.value()[0], self.threshold_slider.value()[1],
|
|
440
|
-
foreground_value=
|
|
441
|
+
foreground_value=1., fill_holes=True, edge_exclusion=None)
|
|
441
442
|
self.thresholded_image = np.ma.masked_where(self.binary == 0., self.binary)
|
|
442
443
|
self.image_thresholded = self.ax.imshow(self.thresholded_image, cmap="viridis", alpha=0.5, interpolation='none')
|
|
443
444
|
|
|
@@ -591,6 +592,7 @@ class ThresholdConfigWizard(QMainWindow, Styles):
|
|
|
591
592
|
|
|
592
593
|
self.reload_frame()
|
|
593
594
|
filters = self.filters_qlist.items
|
|
595
|
+
self.edge = estimate_unreliable_edge(filters)
|
|
594
596
|
self.img = filter_image(self.img, filters)
|
|
595
597
|
self.refresh_imshow()
|
|
596
598
|
self.update_histogram()
|
|
@@ -637,7 +639,7 @@ class ThresholdConfigWizard(QMainWindow, Styles):
|
|
|
637
639
|
"""
|
|
638
640
|
|
|
639
641
|
self.binary = threshold_image(self.img, self.threshold_slider.value()[0], self.threshold_slider.value()[1],
|
|
640
|
-
foreground_value=
|
|
642
|
+
foreground_value=1., fill_holes=True, edge_exclusion=self.edge)
|
|
641
643
|
self.thresholded_image = np.ma.masked_where(self.binary == 0., self.binary)
|
|
642
644
|
self.image_thresholded.set_data(self.thresholded_image)
|
|
643
645
|
self.fcanvas.canvas.draw_idle()
|
|
@@ -658,7 +660,7 @@ class ThresholdConfigWizard(QMainWindow, Styles):
|
|
|
658
660
|
|
|
659
661
|
if self.binary.ndim == 3:
|
|
660
662
|
self.binary = np.squeeze(self.binary)
|
|
661
|
-
self.binary = binary_fill_holes(self.binary)
|
|
663
|
+
#self.binary = binary_fill_holes(self.binary)
|
|
662
664
|
self.coords, self.edt_map = identify_markers_from_binary(self.binary, self.min_dist,
|
|
663
665
|
footprint_size=self.footprint, footprint=None,
|
|
664
666
|
return_edt=True)
|
|
@@ -785,7 +787,7 @@ class ThresholdConfigWizard(QMainWindow, Styles):
|
|
|
785
787
|
self.property_query_le.setText('')
|
|
786
788
|
|
|
787
789
|
self.binary = threshold_image(self.img, self.threshold_slider.value()[0], self.threshold_slider.value()[1],
|
|
788
|
-
foreground_value=
|
|
790
|
+
foreground_value=1., fill_holes=True, edge_exclusion=self.edge)
|
|
789
791
|
self.thresholded_image = np.ma.masked_where(self.binary == 0., self.binary)
|
|
790
792
|
|
|
791
793
|
self.scat_markers.set_color('tab:red')
|