celldetective 1.1.1.post1__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.
@@ -2,6 +2,7 @@ from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox, QScrollArea,
2
2
  from PyQt5.QtCore import Qt, QSize
3
3
  from PyQt5.QtGui import QDoubleValidator, QIntValidator, QIcon
4
4
  from celldetective.gui.gui_utils import center_window, FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, GeometryChoice, OperationChoice
5
+ from celldetective.gui.layouts import ChannelNormGenerator
5
6
  from superqt import QLabeledDoubleRangeSlider, QLabeledDoubleSlider,QLabeledSlider
6
7
  from superqt.fonticon import icon
7
8
  from fonticon_mdi6 import MDI6
@@ -306,103 +307,9 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
306
307
  recompile_layout.addWidget(self.recompile_option, 70)
307
308
  layout.addLayout(recompile_layout)
308
309
 
309
- #self.channel_cbs = [QComboBox() for i in range(4)]
310
-
311
310
  self.max_nbr_channels = 5
312
- self.channel_cbs = [QComboBox() for i in range(self.max_nbr_channels)]
313
- self.normalization_mode_btns = [QPushButton('') for i in range(self.max_nbr_channels)]
314
- self.normalization_mode = [True for i in range(self.max_nbr_channels)]
315
-
316
- self.normalization_clip_btns = [QPushButton('') for i in range(self.max_nbr_channels)]
317
- self.clip_option = [False for i in range(self.max_nbr_channels)]
318
-
319
- for i in range(self.max_nbr_channels):
320
-
321
- self.normalization_mode_btns[i].setIcon(icon(MDI6.percent_circle,color="#1565c0"))
322
- self.normalization_mode_btns[i].setIconSize(QSize(20, 20))
323
- self.normalization_mode_btns[i].setStyleSheet(self.button_select_all)
324
- self.normalization_mode_btns[i].setToolTip("Switch to absolute normalization values.")
325
- self.normalization_mode_btns[i].clicked.connect(partial(self.switch_normalization_mode, i))
326
-
327
- self.normalization_clip_btns[i].setIcon(icon(MDI6.content_cut,color="black"))
328
- self.normalization_clip_btns[i].setIconSize(QSize(20, 20))
329
- self.normalization_clip_btns[i].setStyleSheet(self.button_select_all)
330
- self.normalization_clip_btns[i].clicked.connect(partial(self.switch_clipping_mode, i))
331
- self.normalization_clip_btns[i].setToolTip('clip')
332
-
333
- self.normalization_min_value_lbl = [QLabel('Min %: ') for i in range(self.max_nbr_channels)]
334
- self.normalization_min_value_le = [QLineEdit('0.1') for i in range(self.max_nbr_channels)]
335
-
336
- self.normalization_max_value_lbl = [QLabel('Max %: ') for i in range(self.max_nbr_channels)]
337
- self.normalization_max_value_le = [QLineEdit('99.99') for i in range(self.max_nbr_channels)]
338
-
339
- tables = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_{self.mode}.csv']))
340
- print(tables)
341
- all_measurements = []
342
- for tab in tables:
343
- cols = pd.read_csv(tab, nrows=1).columns.tolist()
344
- all_measurements.extend(cols)
345
- all_measurements = np.unique(all_measurements)
346
- generic_measurements = ['brightfield_channel', 'live_nuclei_channel', 'dead_nuclei_channel',
347
- 'effector_fluo_channel', 'adhesion_channel', 'fluo_channel_1', 'fluo_channel_2',
348
- "area", "area_bbox","area_convex","area_filled","major_axis_length",
349
- "minor_axis_length",
350
- "eccentricity",
351
- "equivalent_diameter_area",
352
- "euler_number",
353
- "extent",
354
- "feret_diameter_max",
355
- "orientation",
356
- "perimeter",
357
- "perimeter_crofton",
358
- "solidity",
359
- "angular_second_moment",
360
- "contrast",
361
- "correlation",
362
- "sum_of_square_variance",
363
- "inverse_difference_moment",
364
- "sum_average",
365
- "sum_variance",
366
- "sum_entropy",
367
- "entropy",
368
- "difference_variance",
369
- "difference_entropy",
370
- "information_measure_of_correlation_1",
371
- "information_measure_of_correlation_2",
372
- "maximal_correlation_coefficient",
373
- "POSITION_X",
374
- "POSITION_Y",
375
- ]
376
-
377
- self.channel_items = np.unique(generic_measurements + list(all_measurements))
378
- self.channel_items = np.insert(self.channel_items, 0, '--')
379
-
380
- self.channel_option_layouts = []
381
- for i in range(len(self.channel_cbs)):
382
- ch_layout = QHBoxLayout()
383
- ch_layout.addWidget(QLabel(f'channel {i}: '), 30)
384
- self.channel_cbs[i].addItems(self.channel_items)
385
- self.channel_cbs[i].currentIndexChanged.connect(self.check_valid_channels)
386
- ch_layout.addWidget(self.channel_cbs[i], 70)
387
- layout.addLayout(ch_layout)
388
-
389
- channel_norm_options_layout = QHBoxLayout()
390
- channel_norm_options_layout.setContentsMargins(130,0,0,0)
391
- channel_norm_options_layout.addWidget(self.normalization_min_value_lbl[i])
392
- channel_norm_options_layout.addWidget(self.normalization_min_value_le[i])
393
- channel_norm_options_layout.addWidget(self.normalization_max_value_lbl[i])
394
- channel_norm_options_layout.addWidget(self.normalization_max_value_le[i])
395
- channel_norm_options_layout.addWidget(self.normalization_clip_btns[i])
396
- channel_norm_options_layout.addWidget(self.normalization_mode_btns[i])
397
- layout.addLayout(channel_norm_options_layout)
398
-
399
- # for i in range(len(self.channel_cbs)):
400
- # ch_layout = QHBoxLayout()
401
- # ch_layout.addWidget(QLabel(f'channel {i}: '), 30)
402
- # self.channel_cbs[i].addItems(self.channel_items)
403
- # self.channel_cbs[i].currentIndexChanged.connect(self.check_valid_channels)
404
- # ch_layout.addWidget(self.channel_cbs[i], 70)
405
- # layout.addLayout(ch_layout)
311
+ self.ch_norm = ChannelNormGenerator(self, mode='signals')
312
+ layout.addLayout(self.ch_norm)
406
313
 
407
314
  model_length_layout = QHBoxLayout()
408
315
  model_length_layout.addWidget(QLabel('Max signal length: '), 30)
@@ -465,8 +372,9 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
465
372
 
466
373
  self.pretrained_model = None
467
374
  self.pretrained_lbl.setText('No folder chosen')
468
- for cb in self.channel_cbs:
375
+ for cb in self.ch_norm.channel_cbs:
469
376
  cb.setEnabled(True)
377
+ self.ch_norm.add_col_btn.setEnabled(True)
470
378
  self.recompile_option.setEnabled(False)
471
379
  self.cancel_pretrained.setVisible(False)
472
380
  self.model_length_slider.setEnabled(True)
@@ -495,14 +403,15 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
495
403
  self.model_length_slider.setValue(int(signal_length))
496
404
  self.model_length_slider.setEnabled(False)
497
405
 
498
- for c,cb in zip(channels, self.channel_cbs):
406
+ for c,cb in zip(channels, self.ch_norm.channel_cbs):
499
407
  index = cb.findText(c)
500
408
  cb.setCurrentIndex(index)
501
409
 
502
- if len(channels)<len(self.channel_cbs):
503
- for k in range(len(self.channel_cbs)-len(channels)):
504
- self.channel_cbs[len(channels)+k].setCurrentIndex(0)
505
- self.channel_cbs[len(channels)+k].setEnabled(False)
410
+ if len(channels)<len(self.ch_norm.channel_cbs):
411
+ for k in range(len(self.ch_norm.channel_cbs)-len(channels)):
412
+ self.ch_norm.channel_cbs[len(channels)+k].setCurrentIndex(0)
413
+ self.ch_norm.channel_cbs[len(channels)+k].setEnabled(False)
414
+ self.ch_norm.add_col_btn.setEnabled(False)
506
415
 
507
416
 
508
417
  def adjustScrollArea(self):
@@ -524,23 +433,23 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
524
433
  recompile_op = self.recompile_option.isChecked()
525
434
 
526
435
  channels = []
527
- for i in range(len(self.channel_cbs)):
528
- channels.append(self.channel_cbs[i].currentText())
436
+ for i in range(len(self.ch_norm.channel_cbs)):
437
+ channels.append(self.ch_norm.channel_cbs[i].currentText())
529
438
 
530
439
  slots_to_keep = np.where(np.array(channels)!='--')[0]
531
440
  while '--' in channels:
532
441
  channels.remove('--')
533
442
 
534
- norm_values = np.array([[float(a.replace(',','.')),float(b.replace(',','.'))] for a,b in zip([l.text() for l in self.normalization_min_value_le],
535
- [l.text() for l in self.normalization_max_value_le])])
443
+ norm_values = np.array([[float(a.replace(',','.')),float(b.replace(',','.'))] for a,b in zip([l.text() for l in self.ch_norm.normalization_min_value_le],
444
+ [l.text() for l in self.ch_norm.normalization_max_value_le])])
536
445
  norm_values = norm_values[slots_to_keep]
537
446
  norm_values = [list(v) for v in norm_values]
538
447
 
539
- clip_values = np.array(self.clip_option)
448
+ clip_values = np.array(self.ch_norm.clip_option)
540
449
  clip_values = list(clip_values[slots_to_keep])
541
450
  clip_values = [bool(c) for c in clip_values]
542
451
 
543
- normalization_mode = np.array(self.normalization_mode)
452
+ normalization_mode = np.array(self.ch_norm.normalization_mode)
544
453
  normalization_mode = list(normalization_mode[slots_to_keep])
545
454
  normalization_mode = [bool(m) for m in normalization_mode]
546
455
 
@@ -586,59 +495,4 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
586
495
 
587
496
  train_signal_model(model_folder+"training_instructions.json")
588
497
 
589
- self.parent_window.refresh_signal_models()
590
-
591
-
592
- def check_valid_channels(self):
593
-
594
- if np.all([cb.currentText()=='--' for cb in self.channel_cbs]):
595
- self.submit_btn.setEnabled(False)
596
- else:
597
- self.submit_btn.setEnabled(True)
598
-
599
-
600
- def switch_normalization_mode(self, index):
601
-
602
- """
603
- Use absolute or percentile values for the normalization of each individual channel.
604
-
605
- """
606
-
607
- currentNormMode = self.normalization_mode[index]
608
- self.normalization_mode[index] = not currentNormMode
609
-
610
- if self.normalization_mode[index]:
611
- self.normalization_mode_btns[index].setIcon(icon(MDI6.percent_circle,color="#1565c0"))
612
- self.normalization_mode_btns[index].setIconSize(QSize(20, 20))
613
- self.normalization_mode_btns[index].setStyleSheet(self.button_select_all)
614
- self.normalization_mode_btns[index].setToolTip("Switch to absolute normalization values.")
615
- self.normalization_min_value_lbl[index].setText('Min %: ')
616
- self.normalization_max_value_lbl[index].setText('Max %: ')
617
- self.normalization_min_value_le[index].setText('0.1')
618
- self.normalization_max_value_le[index].setText('99.99')
619
-
620
- else:
621
- self.normalization_mode_btns[index].setIcon(icon(MDI6.percent_circle_outline,color="black"))
622
- self.normalization_mode_btns[index].setIconSize(QSize(20, 20))
623
- self.normalization_mode_btns[index].setStyleSheet(self.button_select_all)
624
- self.normalization_mode_btns[index].setToolTip("Switch to percentile normalization values.")
625
- self.normalization_min_value_lbl[index].setText('Min: ')
626
- self.normalization_min_value_le[index].setText('0')
627
- self.normalization_max_value_lbl[index].setText('Max: ')
628
- self.normalization_max_value_le[index].setText('1000')
629
-
630
- def switch_clipping_mode(self, index):
631
-
632
- currentClipMode = self.clip_option[index]
633
- self.clip_option[index] = not currentClipMode
634
-
635
- if self.clip_option[index]:
636
- self.normalization_clip_btns[index].setIcon(icon(MDI6.content_cut,color="#1565c0"))
637
- self.normalization_clip_btns[index].setIconSize(QSize(20, 20))
638
- self.normalization_clip_btns[index].setStyleSheet(self.button_select_all)
639
-
640
- else:
641
- self.normalization_clip_btns[index].setIcon(icon(MDI6.content_cut,color="black"))
642
- self.normalization_clip_btns[index].setIconSize(QSize(20, 20))
643
- self.normalization_clip_btns[index].setStyleSheet(self.button_select_all)
644
-
498
+ self.parent_window.refresh_signal_models()
@@ -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):
@@ -264,7 +352,12 @@ class TableUI(QMainWindow, Styles):
264
352
  self.derivative_action = QAction('&Differentiate...', self)
265
353
  self.derivative_action.triggered.connect(self.differenciate_selected_feature)
266
354
  self.derivative_action.setShortcut("Ctrl+D")
267
- 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)
268
361
 
269
362
  def delete_columns(self):
270
363
 
@@ -334,6 +427,19 @@ class TableUI(QMainWindow, Styles):
334
427
  self.diffWidget = DifferentiateColWidget(self, selected_col)
335
428
  self.diffWidget.show()
336
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
+
337
443
 
338
444
  def groupby_time_table(self):
339
445
 
@@ -477,6 +583,7 @@ class TableUI(QMainWindow, Styles):
477
583
  layout.addWidget(QLabel('Representations: '))
478
584
  self.hist_check = QCheckBox('histogram')
479
585
  self.kde_check = QCheckBox('KDE plot')
586
+ self.count_check = QCheckBox('Countplot')
480
587
  self.ecdf_check = QCheckBox('ECDF plot')
481
588
  self.swarm_check = QCheckBox('swarm')
482
589
  self.violin_check = QCheckBox('violin')
@@ -486,6 +593,7 @@ class TableUI(QMainWindow, Styles):
486
593
 
487
594
  layout.addWidget(self.hist_check)
488
595
  layout.addWidget(self.kde_check)
596
+ layout.addWidget(self.count_check)
489
597
  layout.addWidget(self.ecdf_check)
490
598
  layout.addWidget(self.swarm_check)
491
599
  layout.addWidget(self.violin_check)
@@ -496,16 +604,38 @@ class TableUI(QMainWindow, Styles):
496
604
  self.x_cb = QSearchableComboBox()
497
605
  self.x_cb.addItems(['--']+list(self.data.columns))
498
606
 
607
+ self.y_cb = QSearchableComboBox()
608
+ self.y_cb.addItems(['--']+list(self.data.columns))
609
+
499
610
  self.hue_cb = QSearchableComboBox()
500
611
  self.hue_cb.addItems(['--']+list(self.data.columns))
501
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
502
628
 
503
- self.x_cb.findText('--')
504
629
  hbox = QHBoxLayout()
505
630
  hbox.addWidget(QLabel('x: '), 33)
506
631
  hbox.addWidget(self.x_cb, 66)
507
632
  layout.addLayout(hbox)
508
633
 
634
+ hbox = QHBoxLayout()
635
+ hbox.addWidget(QLabel('y: '), 33)
636
+ hbox.addWidget(self.y_cb, 66)
637
+ layout.addLayout(hbox)
638
+
509
639
  hbox = QHBoxLayout()
510
640
  hbox.addWidget(QLabel('hue: '), 33)
511
641
  hbox.addWidget(self.hue_cb, 66)
@@ -539,18 +669,10 @@ class TableUI(QMainWindow, Styles):
539
669
  self.x_option = True
540
670
  self.x = self.x_cb.currentText()
541
671
 
542
- x = self.table_view.selectedIndexes()
543
- col_idx = np.array([l.column() for l in x])
544
- row_idx = np.array([l.row() for l in x])
545
- column_names = self.data.columns
546
- unique_cols = np.unique(col_idx)[0]
547
-
548
672
  self.fig, self.ax = plt.subplots(1,1,figsize=(4,3))
549
673
  self.plot1dWindow = FigureCanvas(self.fig, title="scatter")
550
674
  self.ax.clear()
551
- row_idx_i = row_idx[np.where(col_idx==unique_cols)[0]]
552
- y = self.data.iloc[row_idx_i, unique_cols]
553
-
675
+
554
676
  cmap = getattr(mcm, self.cmap_cb.currentText())
555
677
 
556
678
  try:
@@ -562,58 +684,68 @@ class TableUI(QMainWindow, Styles):
562
684
  if self.hue_cb.currentText()=='--':
563
685
  hue_variable = None
564
686
 
565
- #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()
566
696
 
567
697
  legend=True
568
698
  if self.hist_check.isChecked():
569
- 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')
570
700
  legend = False
571
701
  if self.kde_check.isChecked():
572
- 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)
573
706
  legend = False
574
-
575
707
  if self.ecdf_check.isChecked():
576
- 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)
577
709
  legend = False
578
710
 
579
711
  if self.swarm_check.isChecked():
580
712
  if self.x_option:
581
- 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)
582
714
  legend = False
583
715
  else:
584
- 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)
585
717
  legend = False
586
718
 
587
719
  if self.violin_check.isChecked():
588
720
  if self.x_option:
589
- 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)
590
722
  legend = False
591
723
  else:
592
- 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)
593
725
  legend = False
594
726
 
595
727
  if self.box_check.isChecked():
596
728
  if self.x_option:
597
- 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,)
598
730
  legend = False
599
731
  else:
600
- 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,)
601
733
  legend = False
602
734
 
603
735
  if self.boxenplot_check.isChecked():
604
736
  if self.x_option:
605
- 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,)
606
738
  legend = False
607
739
  else:
608
- 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,)
609
741
  legend = False
610
742
 
611
743
  if self.strip_check.isChecked():
612
744
  if self.x_option:
613
- 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)
614
746
  legend = False
615
747
  else:
616
- 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)
617
749
  legend = False
618
750
 
619
751
  plt.tight_layout()
@@ -762,42 +894,10 @@ class TableUI(QMainWindow, Styles):
762
894
  column_names = self.data.columns
763
895
  unique_cols = np.unique(col_idx)
764
896
 
765
- if len(unique_cols)==1:
766
- # 1D plot
767
- # Open widget to set 1D data representations
897
+ if len(unique_cols)==1 or len(unique_cols)==0:
768
898
  self.set_1D_plot_params()
769
899
 
770
-
771
-
772
- # x = self.table_view.selectedIndexes()
773
- # col_idx = np.array([l.column() for l in x])
774
- # row_idx = np.array([l.row() for l in x])
775
- # column_names = self.data.columns
776
- # unique_cols = np.unique(col_idx)[0]
777
-
778
- # self.fig, self.ax = plt.subplots(1,1,figsize=(4,3))
779
- # self.histogram_window = FigureCanvas(self.fig, title="scatter")
780
- # self.ax.clear()
781
- # row_idx_i = row_idx[np.where(col_idx==unique_cols)[0]]
782
- # y = self.data.iloc[row_idx_i, unique_cols]
783
-
784
- # colors = [viridis(i / len(self.data['well_index'].unique())) for i in range(len(self.data['well_index'].unique()))]
785
- # #for w,well_group in self.data.groupby('well_index'):
786
- # 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,)
787
- # sns.stripplot(data=self.data, y=column_names[unique_cols],dodge=True, ax=self.ax, hue='well_index', legend=False, palette=colors)
788
- # # 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,)
789
- # # for k,(w,well_group) in enumerate(self.data.groupby('well_index')):
790
- # # self.ax.hist(well_group[column_names[unique_cols]],label=w, density=True, alpha=0.5, color=colors[k])
791
- # #self.ax.legend()
792
- # self.ax.set_xlabel(column_names[unique_cols])
793
- # plt.tight_layout()
794
- # self.fig.set_facecolor('none') # or 'None'
795
- # self.fig.canvas.setStyleSheet("background-color: transparent;")
796
- # self.histogram_window.canvas.draw()
797
- # self.histogram_window.show()
798
-
799
-
800
- elif len(unique_cols) == 2:
900
+ if len(unique_cols) == 2:
801
901
 
802
902
  print("two columns, plot mode")
803
903
  x1 = self.test_bool(self.data.iloc[row_idx, unique_cols[0]])
@@ -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
@@ -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":