celldetective 1.1.1__py3-none-any.whl → 1.1.1.post3__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.
@@ -12,6 +12,8 @@ import matplotlib.cm as mcm
12
12
  import os
13
13
  from celldetective.gui import Styles
14
14
  from superqt import QColormapComboBox, QLabeledSlider, QSearchableComboBox
15
+ from superqt.fonticon import icon
16
+ from fonticon_mdi6 import MDI6
15
17
  from math import floor
16
18
 
17
19
  from matplotlib import colormaps
@@ -72,6 +74,92 @@ class QueryWidget(QWidget):
72
74
  print(e)
73
75
  return None
74
76
 
77
+
78
+ class MergeOneHotWidget(QWidget, Styles):
79
+
80
+ def __init__(self, parent_window, selected_columns=None):
81
+
82
+ super().__init__()
83
+ self.parent_window = parent_window
84
+ self.selected_columns = selected_columns
85
+
86
+ self.setWindowTitle("Merge one-hot encoded columns...")
87
+ # Create the QComboBox and add some items
88
+ center_window(self)
89
+
90
+ self.layout = QVBoxLayout(self)
91
+ self.layout.setContentsMargins(30,30,30,30)
92
+
93
+ if self.selected_columns is not None:
94
+ n_cols = len(self.selected_columns)
95
+ else:
96
+ n_cols = 2
97
+
98
+ name_hbox = QHBoxLayout()
99
+ name_hbox.addWidget(QLabel('New categorical column: '), 33)
100
+ self.new_col_le = QLineEdit()
101
+ self.new_col_le.setText('categorical_')
102
+ self.new_col_le.textChanged.connect(self.allow_merge)
103
+ name_hbox.addWidget(self.new_col_le, 66)
104
+ self.layout.addLayout(name_hbox)
105
+
106
+
107
+ self.layout.addWidget(QLabel('Source columns: '))
108
+
109
+ self.cbs = [QSearchableComboBox() for i in range(n_cols)]
110
+ self.cbs_layout = QVBoxLayout()
111
+
112
+ for i in range(n_cols):
113
+ lay = QHBoxLayout()
114
+ lay.addWidget(QLabel(f'column {i}: '), 33)
115
+ self.cbs[i].addItems(['--']+list(self.parent_window.data.columns))
116
+ if self.selected_columns is not None:
117
+ self.cbs[i].setCurrentText(self.selected_columns[i])
118
+ lay.addWidget(self.cbs[i], 66)
119
+ self.cbs_layout.addLayout(lay)
120
+
121
+ self.layout.addLayout(self.cbs_layout)
122
+
123
+ hbox = QHBoxLayout()
124
+ self.add_col_btn = QPushButton('Add column')
125
+ self.add_col_btn.clicked.connect(self.add_col)
126
+ self.add_col_btn.setStyleSheet(self.button_add)
127
+ self.add_col_btn.setIcon(icon(MDI6.plus,color="black"))
128
+
129
+ hbox.addWidget(QLabel(''), 50)
130
+ hbox.addWidget(self.add_col_btn, 50, alignment=Qt.AlignRight)
131
+ self.layout.addLayout(hbox)
132
+
133
+ self.submit_btn = QPushButton('Merge')
134
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
135
+ self.submit_btn.clicked.connect(self.merge_cols)
136
+ self.layout.addWidget(self.submit_btn, 30)
137
+
138
+ self.setAttribute(Qt.WA_DeleteOnClose)
139
+
140
+ def add_col(self):
141
+ self.cbs.append(QSearchableComboBox())
142
+ self.cbs[-1].addItems(['--']+list(self.parent_window.data.columns))
143
+ lay = QHBoxLayout()
144
+ lay.addWidget(QLabel(f'column {len(self.cbs)-1}: '), 33)
145
+ lay.addWidget(self.cbs[-1], 66)
146
+ self.cbs_layout.addLayout(lay)
147
+
148
+ def merge_cols(self):
149
+
150
+ self.parent_window.data[self.new_col_le.text()] = self.parent_window.data.loc[:,list(self.selected_columns)].idxmax(axis=1)
151
+ self.parent_window.model = PandasModel(self.parent_window.data)
152
+ self.parent_window.table_view.setModel(self.parent_window.model)
153
+ self.close()
154
+
155
+ def allow_merge(self):
156
+
157
+ if self.new_col_le.text()=='':
158
+ self.submit_btn.setEnabled(False)
159
+ else:
160
+ self.submit_btn.setEnabled(True)
161
+
162
+
75
163
  class DifferentiateColWidget(QWidget, Styles):
76
164
 
77
165
  def __init__(self, parent_window, column=None):
@@ -232,6 +320,11 @@ class TableUI(QMainWindow, Styles):
232
320
  self.plot_action.setShortcut("Ctrl+p")
233
321
  self.fileMenu.addAction(self.plot_action)
234
322
 
323
+ self.plot_inst_action = QAction("&Plot instantaneous...", self)
324
+ self.plot_inst_action.triggered.connect(self.plot_instantaneous)
325
+ self.plot_inst_action.setShortcut("Ctrl+i")
326
+ self.fileMenu.addAction(self.plot_inst_action)
327
+
235
328
  self.groupby_action = QAction("&Group by tracks...", self)
236
329
  self.groupby_action.triggered.connect(self.set_projection_mode_tracks)
237
330
  self.groupby_action.setShortcut("Ctrl+g")
@@ -259,7 +352,12 @@ class TableUI(QMainWindow, Styles):
259
352
  self.derivative_action = QAction('&Differentiate...', self)
260
353
  self.derivative_action.triggered.connect(self.differenciate_selected_feature)
261
354
  self.derivative_action.setShortcut("Ctrl+D")
262
- self.mathMenu.addAction(self.derivative_action)
355
+ self.mathMenu.addAction(self.derivative_action)
356
+
357
+ self.onehot_action = QAction('&One hot to categorical...', self)
358
+ self.onehot_action.triggered.connect(self.transform_one_hot_cols_to_categorical)
359
+ #self.onehot_action.setShortcut("Ctrl+D")
360
+ self.mathMenu.addAction(self.onehot_action)
263
361
 
264
362
  def delete_columns(self):
265
363
 
@@ -329,6 +427,19 @@ class TableUI(QMainWindow, Styles):
329
427
  self.diffWidget = DifferentiateColWidget(self, selected_col)
330
428
  self.diffWidget.show()
331
429
 
430
+ def transform_one_hot_cols_to_categorical(self):
431
+
432
+ x = self.table_view.selectedIndexes()
433
+ col_idx = np.unique(np.array([l.column() for l in x]))
434
+ if list(col_idx):
435
+ cols = np.array(list(self.data.columns))
436
+ selected_cols = cols[col_idx]
437
+ else:
438
+ selected_cols = None
439
+
440
+ self.mergewidget = MergeOneHotWidget(self, selected_columns=selected_cols)
441
+ self.mergewidget.show()
442
+
332
443
 
333
444
  def groupby_time_table(self):
334
445
 
@@ -408,7 +519,7 @@ class TableUI(QMainWindow, Styles):
408
519
  self.status_operation.setEnabled(False)
409
520
  self.status_operation.addItems(['mean','median','min','max', 'prod', 'sum'])
410
521
 
411
- status_cols = np.array([c.startswith('status_') for c in cols])
522
+ status_cols = np.array([c.startswith('status_') or c.startswith('group_') for c in cols])
412
523
  status_cols = list(cols[status_cols])
413
524
  if 'status' in list(self.data.columns):
414
525
  status_cols.append('status')
@@ -472,6 +583,7 @@ class TableUI(QMainWindow, Styles):
472
583
  layout.addWidget(QLabel('Representations: '))
473
584
  self.hist_check = QCheckBox('histogram')
474
585
  self.kde_check = QCheckBox('KDE plot')
586
+ self.count_check = QCheckBox('Countplot')
475
587
  self.ecdf_check = QCheckBox('ECDF plot')
476
588
  self.swarm_check = QCheckBox('swarm')
477
589
  self.violin_check = QCheckBox('violin')
@@ -481,6 +593,7 @@ class TableUI(QMainWindow, Styles):
481
593
 
482
594
  layout.addWidget(self.hist_check)
483
595
  layout.addWidget(self.kde_check)
596
+ layout.addWidget(self.count_check)
484
597
  layout.addWidget(self.ecdf_check)
485
598
  layout.addWidget(self.swarm_check)
486
599
  layout.addWidget(self.violin_check)
@@ -491,16 +604,38 @@ class TableUI(QMainWindow, Styles):
491
604
  self.x_cb = QSearchableComboBox()
492
605
  self.x_cb.addItems(['--']+list(self.data.columns))
493
606
 
607
+ self.y_cb = QSearchableComboBox()
608
+ self.y_cb.addItems(['--']+list(self.data.columns))
609
+
494
610
  self.hue_cb = QSearchableComboBox()
495
611
  self.hue_cb.addItems(['--']+list(self.data.columns))
496
612
  idx = self.hue_cb.findText('--')
613
+ self.hue_cb.setCurrentIndex(idx)
614
+
615
+ # Set selected column
616
+
617
+ try:
618
+ x = self.table_view.selectedIndexes()
619
+ col_idx = np.array([l.column() for l in x])
620
+ row_idx = np.array([l.row() for l in x])
621
+ column_names = self.data.columns
622
+ unique_cols = np.unique(col_idx)[0]
623
+ y = column_names[unique_cols]
624
+ idx = self.y_cb.findText(y)
625
+ self.y_cb.setCurrentIndex(idx)
626
+ except:
627
+ pass
497
628
 
498
- self.x_cb.findText('--')
499
629
  hbox = QHBoxLayout()
500
630
  hbox.addWidget(QLabel('x: '), 33)
501
631
  hbox.addWidget(self.x_cb, 66)
502
632
  layout.addLayout(hbox)
503
633
 
634
+ hbox = QHBoxLayout()
635
+ hbox.addWidget(QLabel('y: '), 33)
636
+ hbox.addWidget(self.y_cb, 66)
637
+ layout.addLayout(hbox)
638
+
504
639
  hbox = QHBoxLayout()
505
640
  hbox.addWidget(QLabel('hue: '), 33)
506
641
  hbox.addWidget(self.hue_cb, 66)
@@ -534,18 +669,10 @@ class TableUI(QMainWindow, Styles):
534
669
  self.x_option = True
535
670
  self.x = self.x_cb.currentText()
536
671
 
537
- x = self.table_view.selectedIndexes()
538
- col_idx = np.array([l.column() for l in x])
539
- row_idx = np.array([l.row() for l in x])
540
- column_names = self.data.columns
541
- unique_cols = np.unique(col_idx)[0]
542
-
543
672
  self.fig, self.ax = plt.subplots(1,1,figsize=(4,3))
544
673
  self.plot1dWindow = FigureCanvas(self.fig, title="scatter")
545
674
  self.ax.clear()
546
- row_idx_i = row_idx[np.where(col_idx==unique_cols)[0]]
547
- y = self.data.iloc[row_idx_i, unique_cols]
548
-
675
+
549
676
  cmap = getattr(mcm, self.cmap_cb.currentText())
550
677
 
551
678
  try:
@@ -557,58 +684,68 @@ class TableUI(QMainWindow, Styles):
557
684
  if self.hue_cb.currentText()=='--':
558
685
  hue_variable = None
559
686
 
560
- #for w,well_group in self.data.groupby('well_index'):
687
+ if self.y_cb.currentText()=='--':
688
+ self.y = None
689
+ else:
690
+ self.y = self.y_cb.currentText()
691
+
692
+ if self.x_cb.currentText()=='--':
693
+ self.x = None
694
+ else:
695
+ self.x = self.x_cb.currentText()
561
696
 
562
697
  legend=True
563
698
  if self.hist_check.isChecked():
564
- sns.histplot(data=self.data, x=column_names[unique_cols], hue=hue_variable, legend=legend, ax=self.ax, palette=colors, kde=True, common_norm=False, stat='density')
699
+ sns.histplot(data=self.data, x=self.x, hue=hue_variable, legend=legend, ax=self.ax, palette=colors, kde=True, common_norm=False, stat='density')
565
700
  legend = False
566
701
  if self.kde_check.isChecked():
567
- sns.kdeplot(data=self.data, x=column_names[unique_cols], hue=hue_variable, legend=legend, ax=self.ax, palette=colors, cut=0)
702
+ sns.kdeplot(data=self.data, x=self.x, hue=hue_variable, legend=legend, ax=self.ax, palette=colors, cut=0)
703
+ legend = False
704
+ if self.count_check.isChecked():
705
+ sns.countplot(data=self.data, x=self.x, hue=hue_variable, legend=legend, ax=self.ax, palette=colors)
568
706
  legend = False
569
-
570
707
  if self.ecdf_check.isChecked():
571
- sns.ecdfplot(data=self.data, x=column_names[unique_cols], hue=hue_variable, legend=legend, ax=self.ax, palette=colors)
708
+ sns.ecdfplot(data=self.data, x=self.x, hue=hue_variable, legend=legend, ax=self.ax, palette=colors)
572
709
  legend = False
573
710
 
574
711
  if self.swarm_check.isChecked():
575
712
  if self.x_option:
576
- sns.swarmplot(data=self.data, x=self.x,y=column_names[unique_cols],dodge=True, hue=hue_variable,legend=legend, ax=self.ax, palette=colors)
713
+ sns.swarmplot(data=self.data, x=self.x,y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, palette=colors)
577
714
  legend = False
578
715
  else:
579
- sns.swarmplot(data=self.data, y=column_names[unique_cols],dodge=True, hue=hue_variable,legend=legend, ax=self.ax, palette=colors)
716
+ sns.swarmplot(data=self.data, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, palette=colors)
580
717
  legend = False
581
718
 
582
719
  if self.violin_check.isChecked():
583
720
  if self.x_option:
584
- sns.stripplot(data=self.data,x=self.x, y=column_names[unique_cols],dodge=True, ax=self.ax, hue=hue_variable, legend=legend, palette=colors)
721
+ sns.stripplot(data=self.data,x=self.x, y=self.y,dodge=True, ax=self.ax, hue=hue_variable, legend=legend, palette=colors)
585
722
  legend = False
586
723
  else:
587
- sns.violinplot(data=self.data, y=column_names[unique_cols],dodge=True, hue=hue_variable,legend=legend, ax=self.ax, palette=colors, cut=0)
724
+ sns.violinplot(data=self.data, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, palette=colors, cut=0)
588
725
  legend = False
589
726
 
590
727
  if self.box_check.isChecked():
591
728
  if self.x_option:
592
- sns.boxplot(data=self.data, x=self.x, y=column_names[unique_cols],dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
729
+ sns.boxplot(data=self.data, x=self.x, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
593
730
  legend = False
594
731
  else:
595
- sns.boxplot(data=self.data, y=column_names[unique_cols],dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
732
+ sns.boxplot(data=self.data, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
596
733
  legend = False
597
734
 
598
735
  if self.boxenplot_check.isChecked():
599
736
  if self.x_option:
600
- sns.boxenplot(data=self.data, x = self.x, y=column_names[unique_cols],dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
737
+ sns.boxenplot(data=self.data, x=self.x, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
601
738
  legend = False
602
739
  else:
603
- sns.boxenplot(data=self.data, y=column_names[unique_cols],dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
740
+ sns.boxenplot(data=self.data, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
604
741
  legend = False
605
742
 
606
743
  if self.strip_check.isChecked():
607
744
  if self.x_option:
608
- sns.stripplot(data=self.data, x = self.x, y=column_names[unique_cols],dodge=True, ax=self.ax, hue=hue_variable, legend=legend, palette=colors)
745
+ sns.stripplot(data=self.data, x = self.x, y=self.y,dodge=True, ax=self.ax, hue=hue_variable, legend=legend, palette=colors)
609
746
  legend = False
610
747
  else:
611
- sns.stripplot(data=self.data, y=column_names[unique_cols],dodge=True, ax=self.ax, hue=hue_variable, legend=legend, palette=colors)
748
+ sns.stripplot(data=self.data, y=self.y,dodge=True, ax=self.ax, hue=hue_variable, legend=legend, palette=colors)
612
749
  legend = False
613
750
 
614
751
  plt.tight_layout()
@@ -739,6 +876,15 @@ class TableUI(QMainWindow, Styles):
739
876
  else:
740
877
  return array
741
878
 
879
+ def plot_instantaneous(self):
880
+
881
+ if self.plot_mode=='plot_track_signals':
882
+ self.plot_mode = 'static'
883
+ self.plot()
884
+ self.plot_mode = 'plot_track_signals'
885
+ elif self.plot_mode=="static":
886
+ self.plot()
887
+
742
888
  def plot(self):
743
889
  if self.plot_mode == "static":
744
890
 
@@ -748,42 +894,10 @@ class TableUI(QMainWindow, Styles):
748
894
  column_names = self.data.columns
749
895
  unique_cols = np.unique(col_idx)
750
896
 
751
- if len(unique_cols)==1:
752
- # 1D plot
753
- # Open widget to set 1D data representations
897
+ if len(unique_cols)==1 or len(unique_cols)==0:
754
898
  self.set_1D_plot_params()
755
899
 
756
-
757
-
758
- # x = self.table_view.selectedIndexes()
759
- # col_idx = np.array([l.column() for l in x])
760
- # row_idx = np.array([l.row() for l in x])
761
- # column_names = self.data.columns
762
- # unique_cols = np.unique(col_idx)[0]
763
-
764
- # self.fig, self.ax = plt.subplots(1,1,figsize=(4,3))
765
- # self.histogram_window = FigureCanvas(self.fig, title="scatter")
766
- # self.ax.clear()
767
- # row_idx_i = row_idx[np.where(col_idx==unique_cols)[0]]
768
- # y = self.data.iloc[row_idx_i, unique_cols]
769
-
770
- # colors = [viridis(i / len(self.data['well_index'].unique())) for i in range(len(self.data['well_index'].unique()))]
771
- # #for w,well_group in self.data.groupby('well_index'):
772
- # sns.boxplot(data=self.data, y=column_names[unique_cols],dodge=True, hue='well_index',legend=False, ax=self.ax, fill=False,palette=colors, linewidth=2,)
773
- # sns.stripplot(data=self.data, y=column_names[unique_cols],dodge=True, ax=self.ax, hue='well_index', legend=False, palette=colors)
774
- # # sns.kdeplot(data=self.data, x=column_names[unique_cols], hue='well_index', ax=self.ax, fill=False,common_norm=False, palette=colors, alpha=.5, linewidth=2,)
775
- # # for k,(w,well_group) in enumerate(self.data.groupby('well_index')):
776
- # # self.ax.hist(well_group[column_names[unique_cols]],label=w, density=True, alpha=0.5, color=colors[k])
777
- # #self.ax.legend()
778
- # self.ax.set_xlabel(column_names[unique_cols])
779
- # plt.tight_layout()
780
- # self.fig.set_facecolor('none') # or 'None'
781
- # self.fig.canvas.setStyleSheet("background-color: transparent;")
782
- # self.histogram_window.canvas.draw()
783
- # self.histogram_window.show()
784
-
785
-
786
- elif len(unique_cols) == 2:
900
+ if len(unique_cols) == 2:
787
901
 
788
902
  print("two columns, plot mode")
789
903
  x1 = self.test_bool(self.data.iloc[row_idx, unique_cols[0]])
@@ -2,7 +2,7 @@ import math
2
2
 
3
3
  import skimage
4
4
  from PyQt5.QtWidgets import QAction, QMenu, QMainWindow, QMessageBox, QLabel, QWidget, QFileDialog, QHBoxLayout, \
5
- QGridLayout, QLineEdit, QScrollArea, QVBoxLayout, QComboBox, QPushButton, QApplication, QPushButton
5
+ QGridLayout, QLineEdit, QScrollArea, QVBoxLayout, QComboBox, QPushButton, QApplication, QPushButton, QRadioButton, QButtonGroup
6
6
  from PyQt5.QtGui import QDoubleValidator, QIntValidator
7
7
  from matplotlib.backends.backend_qt import NavigationToolbar2QT
8
8
  from matplotlib.patches import Circle
@@ -17,6 +17,7 @@ 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
19
19
  from scipy.ndimage import binary_fill_holes
20
+ import scipy.ndimage as ndi
20
21
  from PyQt5.QtCore import Qt, QSize
21
22
  from glob import glob
22
23
  from superqt.fonticon import icon
@@ -263,10 +264,20 @@ class ThresholdConfigWizard(QMainWindow, Styles):
263
264
  marker_box = QVBoxLayout()
264
265
  marker_box.setContentsMargins(30, 30, 30, 30)
265
266
 
266
- marker_lbl = QLabel('Markers')
267
+ marker_lbl = QLabel('Objects')
267
268
  marker_lbl.setStyleSheet("font-weight: bold;")
268
269
  marker_box.addWidget(marker_lbl, alignment=Qt.AlignCenter)
269
270
 
271
+ object_option_hbox = QHBoxLayout()
272
+ self.marker_option = QRadioButton('markers')
273
+ self.all_objects_option = QRadioButton('all non-contiguous objects')
274
+ self.marker_option_group = QButtonGroup()
275
+ self.marker_option_group.addButton(self.marker_option)
276
+ self.marker_option_group.addButton(self.all_objects_option)
277
+ object_option_hbox.addWidget(self.marker_option, 50, alignment=Qt.AlignCenter)
278
+ object_option_hbox.addWidget(self.all_objects_option, 50, alignment=Qt.AlignCenter)
279
+ marker_box.addLayout(object_option_hbox)
280
+
270
281
  hbox_footprint = QHBoxLayout()
271
282
  hbox_footprint.addWidget(QLabel('Footprint: '), 20)
272
283
  self.footprint_slider = QLabeledSlider()
@@ -275,7 +286,8 @@ class ThresholdConfigWizard(QMainWindow, Styles):
275
286
  self.footprint_slider.setRange(1, self.binary.shape[0] // 4)
276
287
  self.footprint_slider.setValue(self.footprint)
277
288
  self.footprint_slider.valueChanged.connect(self.set_footprint)
278
- hbox_footprint.addWidget(self.footprint_slider, 80)
289
+ hbox_footprint.addWidget(self.footprint_slider, 30)
290
+ hbox_footprint.addWidget(QLabel(''), 50)
279
291
  marker_box.addLayout(hbox_footprint)
280
292
 
281
293
  hbox_distance = QHBoxLayout()
@@ -286,7 +298,8 @@ class ThresholdConfigWizard(QMainWindow, Styles):
286
298
  self.min_dist_slider.setRange(0, self.binary.shape[0] // 4)
287
299
  self.min_dist_slider.setValue(self.min_dist)
288
300
  self.min_dist_slider.valueChanged.connect(self.set_min_dist)
289
- hbox_distance.addWidget(self.min_dist_slider, 80)
301
+ hbox_distance.addWidget(self.min_dist_slider, 30)
302
+ hbox_distance.addWidget(QLabel(''), 50)
290
303
  marker_box.addLayout(hbox_distance)
291
304
 
292
305
  hbox_marker_btns = QHBoxLayout()
@@ -305,8 +318,23 @@ class ThresholdConfigWizard(QMainWindow, Styles):
305
318
  hbox_marker_btns.addWidget(self.watershed_btn)
306
319
  marker_box.addLayout(hbox_marker_btns)
307
320
 
321
+ self.marker_option.clicked.connect(self.enable_marker_options)
322
+ self.all_objects_option.clicked.connect(self.enable_marker_options)
323
+ self.marker_option.click()
324
+
308
325
  self.left_panel.addLayout(marker_box)
309
326
 
327
+ def enable_marker_options(self):
328
+ if self.marker_option.isChecked():
329
+ self.footprint_slider.setEnabled(True)
330
+ self.min_dist_slider.setEnabled(True)
331
+ self.markers_btn.setEnabled(True)
332
+ else:
333
+ self.footprint_slider.setEnabled(False)
334
+ self.min_dist_slider.setEnabled(False)
335
+ self.markers_btn.setEnabled(False)
336
+ self.watershed_btn.setEnabled(True)
337
+
310
338
  def generate_props_contents(self):
311
339
 
312
340
  properties_box = QVBoxLayout()
@@ -675,7 +703,10 @@ class ThresholdConfigWizard(QMainWindow, Styles):
675
703
 
676
704
  def apply_watershed_to_selection(self):
677
705
 
678
- self.labels = apply_watershed(self.binary, self.coords, self.edt_map)
706
+ if self.marker_option.isChecked():
707
+ self.labels = apply_watershed(self.binary, self.coords, self.edt_map)
708
+ else:
709
+ self.labels,_ = ndi.label(self.binary.astype(int))
679
710
 
680
711
  self.current_channel = self.channels_cb.currentIndex()
681
712
  t = int(self.frame_slider.value())
@@ -806,6 +837,7 @@ class ThresholdConfigWizard(QMainWindow, Styles):
806
837
  "marker_footprint_size": self.footprint,
807
838
  "feature_queries": [self.property_query_le.text()],
808
839
  "equalize_reference": [self.equalize_option, self.frame_slider.value()],
840
+ "do_watershed": self.marker_option.isChecked(),
809
841
  }
810
842
 
811
843
  print('The following instructions will be written: ', instructions)
@@ -873,6 +905,13 @@ class ThresholdConfigWizard(QMainWindow, Styles):
873
905
  self.property_query_le.setText(feature_queries[0])
874
906
  self.submit_query_btn.click()
875
907
 
908
+ if 'do_watershed' in threshold_instructions:
909
+ do_watershed = threshold_instructions['do_watershed']
910
+ if do_watershed:
911
+ self.marker_option.click()
912
+ else:
913
+ self.all_objects_option.click()
914
+
876
915
 
877
916
  class ThresholdNormalisation(ThresholdConfigWizard):
878
917
  def __init__(self, min_threshold, current_channel, parent_window=None):
@@ -396,7 +396,7 @@ class ThresholdedStackVisualizer(StackVisualizer):
396
396
  # Compute the mask based on the threshold value
397
397
  self.preprocess_image()
398
398
  edge = estimate_unreliable_edge(self.preprocessing)
399
- self.mask = threshold_image(self.processed_image, threshold_value, 1.0E06, foreground_value=1, edge_exclusion=edge).astype(int)
399
+ self.mask = threshold_image(self.processed_image, threshold_value, np.inf, foreground_value=1, edge_exclusion=edge).astype(int)
400
400
 
401
401
  def preprocess_image(self):
402
402
  # Preprocess the image before thresholding
celldetective/io.py CHANGED
@@ -1238,12 +1238,23 @@ def load_napari_data(position, prefix="Aligned", population="target", return_sta
1238
1238
  """
1239
1239
  position = position.replace('\\','/')
1240
1240
  if population.lower()=="target" or population.lower()=="targets":
1241
- napari_data = np.load(position+os.sep.join(['output','tables','napari_target_trajectories.npy']), allow_pickle=True)
1241
+ if os.path.exists(position+os.sep.join(['output','tables','napari_target_trajectories.npy'])):
1242
+ napari_data = np.load(position+os.sep.join(['output','tables','napari_target_trajectories.npy']), allow_pickle=True)
1243
+ else:
1244
+ napari_data = None
1242
1245
  elif population.lower()=="effector" or population.lower()=="effectors":
1243
- napari_data = np.load(position+os.sep.join(['output', 'tables', 'napari_effector_trajectories.npy']), allow_pickle=True)
1244
- data = napari_data.item()['data']
1245
- properties = napari_data.item()['properties']
1246
- graph = napari_data.item()['graph']
1246
+ if os.path.exists(position+os.sep.join(['output', 'tables', 'napari_effector_trajectories.npy'])):
1247
+ napari_data = np.load(position+os.sep.join(['output', 'tables', 'napari_effector_trajectories.npy']), allow_pickle=True)
1248
+ else:
1249
+ napari_data = None
1250
+ if napari_data is not None:
1251
+ data = napari_data.item()['data']
1252
+ properties = napari_data.item()['properties']
1253
+ graph = napari_data.item()['graph']
1254
+ else:
1255
+ data = None
1256
+ properties = None
1257
+ graph = None
1247
1258
  if return_stack:
1248
1259
  stack,labels = locate_stack_and_labels(position, prefix=prefix, population=population)
1249
1260
  else:
@@ -1968,15 +1979,19 @@ def normalize_multichannel(multichannel_frame, percentiles=None,
1968
1979
  v = values[c]
1969
1980
  else:
1970
1981
  v = None
1971
- norm = normalize(mf[:,:,c].copy(),
1972
- percentiles=percentiles[c],
1973
- values=v,
1974
- ignore_gray_value=ignore_gray_value,
1975
- clip=clip,
1976
- amplification=amplification,
1977
- dtype=dtype,
1978
- )
1979
- mf_new.append(norm)
1982
+
1983
+ if np.all(mf[:,:,c]==0.):
1984
+ mf_new.append(mf[:,:,c].copy())
1985
+ else:
1986
+ norm = normalize(mf[:,:,c].copy(),
1987
+ percentiles=percentiles[c],
1988
+ values=v,
1989
+ ignore_gray_value=ignore_gray_value,
1990
+ clip=clip,
1991
+ amplification=amplification,
1992
+ dtype=dtype,
1993
+ )
1994
+ mf_new.append(norm)
1980
1995
 
1981
1996
  return np.moveaxis(mf_new,0,-1)
1982
1997
 
@@ -15,6 +15,8 @@ from gc import collect
15
15
  from lmfit import Parameters, Model, models
16
16
  import tifffile.tifffile as tiff
17
17
 
18
+ from tifffile import imwrite
19
+
18
20
  def estimate_background_per_condition(experiment, threshold_on_std=1, well_option='*', target_channel="channel_name", frame_range=[0,5], mode="timeseries", activation_protocol=[['gauss',2],['std',4]], show_progress_per_pos=False, show_progress_per_well=True):
19
21
 
20
22
  """
@@ -69,6 +71,7 @@ def estimate_background_per_condition(experiment, threshold_on_std=1, well_optio
69
71
  ... print(bg["well"], bg["bg"].shape)
70
72
  """
71
73
 
74
+
72
75
  config = get_config(experiment)
73
76
  wells = get_experiment_wells(experiment)
74
77
  len_movie = float(ConfigSectionMap(config,"MovieSettings")["len_movie"])
@@ -79,7 +82,7 @@ def estimate_background_per_condition(experiment, threshold_on_std=1, well_optio
79
82
  channel_indices = _extract_channel_indices_from_config(config, [target_channel])
80
83
  nbr_channels = _extract_nbr_channels_from_config(config)
81
84
  img_num_channels = _get_img_num_per_channel(channel_indices, int(len_movie), nbr_channels)
82
-
85
+
83
86
  backgrounds = []
84
87
 
85
88
  for k, well_path in enumerate(tqdm(wells[well_indices], disable=not show_progress_per_well)):
@@ -110,7 +113,7 @@ def estimate_background_per_condition(experiment, threshold_on_std=1, well_optio
110
113
  frame = frame_mean.copy().astype(float)
111
114
  std_frame = filter_image(frame.copy(),filters=activation_protocol)
112
115
  edge = estimate_unreliable_edge(activation_protocol)
113
- mask = threshold_image(std_frame, threshold_on_std, 1.0E06, foreground_value=1, edge_exclusion=edge)
116
+ mask = threshold_image(std_frame, threshold_on_std, np.inf, foreground_value=1, edge_exclusion=edge)
114
117
  frame[np.where(mask.astype(int)==1)] = np.nan
115
118
 
116
119
  elif mode=="tiles":
@@ -118,21 +121,23 @@ def estimate_background_per_condition(experiment, threshold_on_std=1, well_optio
118
121
  frames = load_frames(img_num_channels[0,:], stack_path, normalize_input=False).astype(float)
119
122
  frames = np.moveaxis(frames, -1, 0).astype(float)
120
123
 
124
+ new_frames = []
121
125
  for i in range(len(frames)):
122
126
 
123
127
  if np.all(frames[i].flatten()==0):
124
- frames[i,:,:] = np.nan
128
+ empty_frame = np.zeros_like(frames[i])
129
+ empty_frame[:,:] = np.nan
130
+ new_frames.append(empty_frame)
125
131
  continue
126
132
 
127
133
  f = frames[i].copy()
128
134
  std_frame = filter_image(f.copy(),filters=activation_protocol)
129
135
  edge = estimate_unreliable_edge(activation_protocol)
130
- mask = threshold_image(std_frame, threshold_on_std, 1.0E06, foreground_value=1, edge_exclusion=edge)
136
+ mask = threshold_image(std_frame, threshold_on_std, np.inf, foreground_value=1, edge_exclusion=edge)
131
137
  f[np.where(mask.astype(int)==1)] = np.nan
132
-
133
- frames[i,:,:] = f
138
+ new_frames.append(f.copy())
134
139
 
135
- frame = np.nanmedian(frames, axis=0)
140
+ frame = np.nanmedian(new_frames, axis=0)
136
141
 
137
142
  # store
138
143
  frame_mean_per_position.append(frame)
@@ -371,18 +376,25 @@ def apply_background_to_stack(stack_path, background, target_channel_index=0, nb
371
376
 
372
377
  std_frame = filter_image(target_copy.copy(),filters=activation_protocol)
373
378
  edge = estimate_unreliable_edge(activation_protocol)
374
- mask = threshold_image(std_frame, threshold_on_std, 1.0E06, foreground_value=1, edge_exclusion=edge)
379
+ mask = threshold_image(std_frame, threshold_on_std, np.inf, foreground_value=1, edge_exclusion=edge)
375
380
  target_copy[np.where(mask.astype(int)==1)] = np.nan
376
381
 
377
382
  loss = []
378
383
 
379
384
  # brute-force regression, could do gradient descent instead
380
385
  for c in coefficients:
386
+
381
387
  target_crop = unpad(target_copy,edge)
382
388
  bg_crop = unpad(background, edge)
383
- diff = np.subtract(target_crop, c*bg_crop, where=target_crop==target_crop)
384
- s = np.sum(np.abs(diff, where=diff==diff), where=diff==diff)
389
+
390
+ roi = np.zeros_like(target_crop).astype(int)
391
+ roi[target_crop!=target_crop] = 1
392
+ roi[bg_crop!=bg_crop] = 1
393
+
394
+ diff = np.subtract(target_crop, c*bg_crop, where=roi==0)
395
+ s = np.sum(np.abs(diff, where=roi==0), where=roi==0)
385
396
  loss.append(s)
397
+
386
398
  c = coefficients[np.argmin(loss)]
387
399
  print(f"Frame: {i}; optimal coefficient: {c}...")
388
400
  # if c==min(coefficients) or c==max(coefficients):
@@ -954,7 +966,7 @@ def field_correction(img, threshold_on_std=1, operation='divide', model='parabol
954
966
 
955
967
  std_frame = filter_image(target_copy,filters=activation_protocol)
956
968
  edge = estimate_unreliable_edge(activation_protocol)
957
- mask = threshold_image(std_frame, threshold_on_std, 1.0E06, foreground_value=1, edge_exclusion=edge).astype(int)
969
+ mask = threshold_image(std_frame, threshold_on_std, np.inf, foreground_value=1, edge_exclusion=edge).astype(int)
958
970
  background = fit_background_model(img, cell_masks=mask, model=model, edge_exclusion=edge)
959
971
 
960
972
  if operation=="divide":