celldetective 1.3.0__py3-none-any.whl → 1.3.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.
@@ -30,10 +30,10 @@ import json
30
30
  import psutil
31
31
  from celldetective.neighborhood import compute_neighborhood_at_position, compute_contact_neighborhood_at_position
32
32
  from celldetective.gui.gui_utils import FigureCanvas
33
- from celldetective.preprocessing import correct_background_model_free, correct_background_model
33
+ from celldetective.preprocessing import correct_background_model_free, correct_background_model, correct_channel_offset
34
34
  from celldetective.utils import _estimate_scale_factor, _extract_channel_indices_from_config, _extract_channel_indices, ConfigSectionMap, _extract_nbr_channels_from_config, _get_img_num_per_channel, normalize_per_channel
35
35
  from celldetective.gui.gui_utils import ThresholdLineEdit, QuickSliderLayout, help_generic
36
- from celldetective.gui.layouts import CellposeParamsWidget, StarDistParamsWidget, BackgroundModelFreeCorrectionLayout, ProtocolDesignerLayout, BackgroundFitCorrectionLayout
36
+ from celldetective.gui.layouts import CellposeParamsWidget, StarDistParamsWidget, BackgroundModelFreeCorrectionLayout, ProtocolDesignerLayout, BackgroundFitCorrectionLayout, ChannelOffsetOptionsLayout
37
37
  from celldetective.gui import Styles
38
38
  from celldetective.utils import get_software_location
39
39
 
@@ -166,10 +166,10 @@ class ProcessPanel(QFrame, Styles):
166
166
  self.generate_signal_analysis_options()
167
167
 
168
168
  self.grid_contents.addWidget(QHSeperationLine(), 9, 0, 1, 4)
169
- self.view_tab_btn = QPushButton("View table")
169
+ self.view_tab_btn = QPushButton("Explore table")
170
170
  self.view_tab_btn.setStyleSheet(self.button_style_sheet_2)
171
171
  self.view_tab_btn.clicked.connect(self.view_table_ui)
172
- self.view_tab_btn.setToolTip('View table')
172
+ self.view_tab_btn.setToolTip('Explore table')
173
173
  self.view_tab_btn.setIcon(icon(MDI6.table,color="#1565c0"))
174
174
  self.view_tab_btn.setIconSize(QSize(20, 20))
175
175
  #self.view_tab_btn.setEnabled(False)
@@ -177,7 +177,7 @@ class ProcessPanel(QFrame, Styles):
177
177
 
178
178
  self.grid_contents.addWidget(QHSeperationLine(), 9, 0, 1, 4)
179
179
  self.submit_btn = QPushButton("Submit")
180
- self.submit_btn.setStyleSheet(self.button_style_sheet_2)
180
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
181
181
  self.submit_btn.clicked.connect(self.process_population)
182
182
  self.grid_contents.addWidget(self.submit_btn, 11, 0, 1, 4)
183
183
 
@@ -228,7 +228,7 @@ class ProcessPanel(QFrame, Styles):
228
228
 
229
229
  signal_layout = QVBoxLayout()
230
230
  signal_hlayout = QHBoxLayout()
231
- self.signal_analysis_action = QCheckBox("SIGNAL ANALYSIS")
231
+ self.signal_analysis_action = QCheckBox("DETECT EVENTS")
232
232
  self.signal_analysis_action.setStyleSheet("""
233
233
  font-size: 10px;
234
234
  padding-left: 10px;
@@ -1283,10 +1283,10 @@ class NeighPanel(QFrame, Styles):
1283
1283
  self.grid_contents.addLayout(signal_layout, 7, 0, 1, 4)
1284
1284
  self.grid_contents.addWidget(QHSeperationLine(), 11, 0, 1, 4)
1285
1285
 
1286
- self.view_tab_btn = QPushButton("View table")
1286
+ self.view_tab_btn = QPushButton("Explore table")
1287
1287
  self.view_tab_btn.setStyleSheet(self.button_style_sheet_2)
1288
1288
  self.view_tab_btn.clicked.connect(self.view_table_ui)
1289
- self.view_tab_btn.setToolTip('View table')
1289
+ self.view_tab_btn.setToolTip('Explore table')
1290
1290
  self.view_tab_btn.setIcon(icon(MDI6.table,color="#1565c0"))
1291
1291
  self.view_tab_btn.setIconSize(QSize(20, 20))
1292
1292
  #self.view_tab_btn.setEnabled(False)
@@ -1295,7 +1295,7 @@ class NeighPanel(QFrame, Styles):
1295
1295
  #self.grid_contents.addWidget(QLabel(''), 12, 0, 1, 4)
1296
1296
 
1297
1297
  self.submit_btn = QPushButton("Submit")
1298
- self.submit_btn.setStyleSheet(self.button_style_sheet_2)
1298
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
1299
1299
  self.submit_btn.setToolTip("Compute the neighborhoods of the selected positions.")
1300
1300
  self.submit_btn.clicked.connect(self.process_neighborhood)
1301
1301
  self.grid_contents.addWidget(self.submit_btn, 14, 0, 1, 4)
@@ -1657,12 +1657,33 @@ class PreprocessingPanel(QFrame, Styles):
1657
1657
 
1658
1658
  self.protocol_layout.title_layout.addWidget(self.help_background_btn, 5, alignment=Qt.AlignRight)
1659
1659
 
1660
+
1661
+ self.channel_offset_correction_layout = QVBoxLayout()
1662
+
1663
+ self.channel_shift_lbl = QLabel("CHANNEL OFFSET CORRECTION")
1664
+ self.channel_shift_lbl.setStyleSheet("""
1665
+ font-weight: bold;
1666
+ padding: 0px;
1667
+ """)
1668
+ self.channel_offset_correction_layout.addWidget(self.channel_shift_lbl, alignment=Qt.AlignCenter)
1669
+
1670
+ self.channel_offset_options_layout = ChannelOffsetOptionsLayout(self)
1671
+ self.channel_offset_correction_layout.addLayout(self.channel_offset_options_layout)
1672
+
1673
+ self.protocol_layout.correction_layout.addWidget(QLabel(''))
1674
+ self.protocol_layout.correction_layout.addLayout(self.channel_offset_correction_layout)
1675
+
1660
1676
  self.grid_contents.addLayout(self.protocol_layout,0,0,1,4)
1677
+
1661
1678
  self.submit_preprocessing_btn = QPushButton("Submit")
1662
- self.submit_preprocessing_btn.setStyleSheet(self.button_style_sheet_2)
1679
+ self.submit_preprocessing_btn.setStyleSheet(self.button_style_sheet)
1663
1680
  self.submit_preprocessing_btn.clicked.connect(self.launch_preprocessing)
1681
+
1664
1682
  self.grid_contents.addWidget(self.submit_preprocessing_btn, 1,0,1,4)
1665
1683
 
1684
+ def add_offset_instructions_to_parent_list(self):
1685
+ print('adding instructions')
1686
+
1666
1687
 
1667
1688
  def launch_preprocessing(self):
1668
1689
 
@@ -1712,7 +1733,7 @@ class PreprocessingPanel(QFrame, Styles):
1712
1733
  export_prefix = None
1713
1734
 
1714
1735
  if correction_protocol['correction_type']=='model-free':
1715
-
1736
+ print(f'Model-free correction; {movie_prefix=} {export_prefix=}')
1716
1737
  correct_background_model_free(self.exp_dir,
1717
1738
  well_option=well_option,
1718
1739
  position_option=pos_option,
@@ -1726,7 +1747,7 @@ class PreprocessingPanel(QFrame, Styles):
1726
1747
  )
1727
1748
 
1728
1749
  elif correction_protocol['correction_type']=='fit':
1729
-
1750
+ print(f'Fit correction; {movie_prefix=} {export_prefix=} {correction_protocol=}')
1730
1751
  correct_background_model(self.exp_dir,
1731
1752
  well_option=well_option,
1732
1753
  position_option=pos_option,
@@ -1738,6 +1759,19 @@ class PreprocessingPanel(QFrame, Styles):
1738
1759
  export_prefix = export_prefix,
1739
1760
  **correction_protocol,
1740
1761
  )
1762
+ elif correction_protocol['correction_type']=='offset':
1763
+ print(f'Offset correction; {movie_prefix=} {export_prefix=} {correction_protocol=}')
1764
+ correct_channel_offset(self.exp_dir,
1765
+ well_option=well_option,
1766
+ position_option=pos_option,
1767
+ export= True,
1768
+ return_stacks=False,
1769
+ show_progress_per_well = True,
1770
+ show_progress_per_pos = True,
1771
+ movie_prefix = movie_prefix,
1772
+ export_prefix = export_prefix,
1773
+ **correction_protocol,
1774
+ )
1741
1775
  print('Done.')
1742
1776
 
1743
1777
 
@@ -8,7 +8,7 @@ from superqt import QLabeledDoubleSlider,QLabeledSlider
8
8
  from superqt.fonticon import icon
9
9
  from fonticon_mdi6 import MDI6
10
10
  from celldetective.utils import get_software_location
11
- from celldetective.io import get_segmentation_datasets_list, locate_segmentation_dataset
11
+ from celldetective.io import get_segmentation_datasets_list, locate_segmentation_dataset, get_segmentation_models_list
12
12
  from celldetective.segmentation import train_segmentation_model
13
13
  from celldetective.gui.layouts import CellposeParamsWidget
14
14
  import numpy as np
@@ -85,8 +85,11 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
85
85
  self.submit_btn.clicked.connect(self.prep_model)
86
86
  self.main_layout.addWidget(self.submit_btn)
87
87
  self.submit_btn.setEnabled(False)
88
+ self.submit_warning = QLabel('')
89
+ self.main_layout.addWidget(self.submit_warning)
88
90
 
89
91
  self.spatial_calib_le.textChanged.connect(self.activate_train_btn)
92
+ self.modelname_le.setText(f"Untitled_model_{datetime.today().strftime('%Y-%m-%d')}")
90
93
 
91
94
  #self.populate_left_panel()
92
95
  #grid.addLayout(self.left_side, 0, 0, 1, 1)
@@ -273,6 +276,7 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
273
276
  modelname_layout.addWidget(QLabel('Model name: '), 30)
274
277
  self.modelname_le = QLineEdit()
275
278
  self.modelname_le.setText(f"Untitled_model_{datetime.today().strftime('%Y-%m-%d')}")
279
+ self.modelname_le.textChanged.connect(self.activate_train_btn)
276
280
  modelname_layout.addWidget(self.modelname_le, 70)
277
281
  layout.addLayout(modelname_layout)
278
282
 
@@ -317,13 +321,21 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
317
321
  spatial_calib_layout.addWidget(self.spatial_calib_le, 70)
318
322
  layout.addLayout(spatial_calib_layout)
319
323
 
320
-
321
-
322
324
  def activate_train_btn(self):
323
- if self.spatial_calib_le.text()=='':
324
- self.submit_btn.setEnabled(False)
325
- elif not np.all([cb.currentText()=='--' for cb in self.ch_norm.channel_cbs]):
325
+
326
+ current_name = self.modelname_le.text()
327
+ models = get_segmentation_models_list(mode=self.mode, return_path=False)
328
+ if not current_name in models and not self.spatial_calib_le.text()=='' and not np.all([cb.currentText()=='--' for cb in self.ch_norm.channel_cbs]):
326
329
  self.submit_btn.setEnabled(True)
330
+ self.submit_warning.setText('')
331
+ else:
332
+ self.submit_btn.setEnabled(False)
333
+ if current_name in models:
334
+ self.submit_warning.setText('A model with this name already exists... Please pick another.')
335
+ elif self.spatial_calib_le.text()=='':
336
+ self.submit_warning.setText('Please provide a valid spatial calibration...')
337
+ elif np.all([cb.currentText()=='--' for cb in self.ch_norm.channel_cbs]):
338
+ self.submit_warning.setText('Please provide valid channels...')
327
339
 
328
340
  def rescale_slider(self):
329
341
  if self.stardist_model.isChecked():
@@ -336,11 +348,13 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
336
348
 
337
349
  def showDialog_pretrained(self):
338
350
 
339
- try:
340
- self.cancel_pretrained.click()
341
- except:
342
- pass
351
+ # try:
352
+ # self.cancel_pretrained.click()
353
+ # except Exception as e:
354
+ # print(e)
355
+ # pass
343
356
 
357
+ self.clear_pretrained()
344
358
  self.pretrained_model = None
345
359
  self.pretrained_model = QFileDialog.getExistingDirectory(
346
360
  self, "Open Directory",
@@ -125,6 +125,13 @@ class ConfigSurvival(QWidget, Styles):
125
125
  select_layout.addWidget(self.query_le, 66)
126
126
  main_layout.addLayout(select_layout)
127
127
 
128
+ time_cut_layout = QHBoxLayout()
129
+ time_cut_layout.addWidget(QLabel('cut observation time [min]: '), 33)
130
+ self.query_time_cut = QLineEdit()
131
+ self.query_time_cut.setValidator(self.float_validator)
132
+ time_cut_layout.addWidget(self.query_time_cut, 66)
133
+ main_layout.addLayout(time_cut_layout)
134
+
128
135
  self.cbs[0].setCurrentIndex(0)
129
136
  self.cbs[1].setCurrentText('t_firstdetection')
130
137
 
@@ -259,17 +266,27 @@ class ConfigSurvival(QWidget, Styles):
259
266
 
260
267
  def compute_survival_functions(self):
261
268
 
269
+ cut_observation_time = None
270
+ try:
271
+ cut_observation_time = float(self.query_time_cut.text()) / self.FrameToMin
272
+ if not 0<cut_observation_time<=(self.df['FRAME'].max()):
273
+ print('Invalid cut time (larger than movie length)... Not applied.')
274
+ cut_observation_time = None
275
+ except Exception as e:
276
+ pass
277
+ print(f"{cut_observation_time=}")
278
+
262
279
  # Per position survival
263
280
  for block,movie_group in self.df.groupby(['well','position']):
264
281
 
265
- ks = compute_survival(movie_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin)
282
+ ks = compute_survival(movie_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin, cut_observation_time=cut_observation_time)
266
283
  if ks is not None:
267
284
  self.df_pos_info.loc[self.df_pos_info['pos_path']==block[1],'survival_fit'] = ks
268
285
 
269
286
  # Per well survival
270
287
  for well,well_group in self.df.groupby('well'):
271
288
 
272
- ks = compute_survival(well_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin)
289
+ ks = compute_survival(well_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin, cut_observation_time=cut_observation_time)
273
290
  if ks is not None:
274
291
  self.df_well_info.loc[self.df_well_info['well_path']==well,'survival_fit'] = ks
275
292
 
@@ -11,9 +11,10 @@ from natsort import natsorted
11
11
  from glob import glob
12
12
  import os
13
13
 
14
- from PyQt5.QtWidgets import QWidget, QHBoxLayout, QPushButton, QLabel, QComboBox, QLineEdit, QListWidget
14
+ from PyQt5.QtWidgets import QWidget, QHBoxLayout, QPushButton, QLabel, QComboBox, QLineEdit, QListWidget, QShortcut
15
15
  from PyQt5.QtCore import Qt, QSize
16
- from celldetective.gui.gui_utils import FigureCanvas, center_window, QuickSliderLayout
16
+ from PyQt5.QtGui import QKeySequence
17
+ from celldetective.gui.gui_utils import FigureCanvas, center_window, QuickSliderLayout, QHSeperationLine, ThresholdLineEdit
17
18
  from celldetective.gui import Styles
18
19
  from superqt import QLabeledDoubleSlider, QLabeledSlider, QLabeledDoubleRangeSlider
19
20
  from superqt.fonticon import icon
@@ -21,6 +22,7 @@ from fonticon_mdi6 import MDI6
21
22
  from matplotlib_scalebar.scalebar import ScaleBar
22
23
  import gc
23
24
  from celldetective.utils import mask_edges
25
+ from scipy.ndimage import shift
24
26
 
25
27
  class StackVisualizer(QWidget, Styles):
26
28
 
@@ -739,4 +741,262 @@ class CellSizeViewer(StackVisualizer):
739
741
 
740
742
  self.diameter = value
741
743
  self.circ.set_radius(self.diameter//2)
742
- self.canvas.canvas.draw_idle()
744
+ self.canvas.canvas.draw_idle()
745
+
746
+
747
+ class ChannelOffsetViewer(StackVisualizer):
748
+
749
+ def __init__(self, parent_window=None, *args, **kwargs):
750
+
751
+ self.parent_window = parent_window
752
+ self.overlay_target_channel = -1
753
+ self.shift_vertical = 0
754
+ self.shift_horizontal = 0
755
+ super().__init__(*args, **kwargs)
756
+
757
+ self.load_stack()
758
+ self.canvas.layout.addWidget(QHSeperationLine())
759
+
760
+ self.generate_overlay_channel_cb()
761
+ self.generate_overlay_imshow()
762
+
763
+ self.generate_overlay_alpha_slider()
764
+ self.generate_overlay_contrast_slider()
765
+
766
+ self.generate_overlay_shift()
767
+ self.generate_add_to_parent_btn()
768
+
769
+ if self.overlay_target_channel==-1:
770
+ index = len(self.channel_names) -1
771
+ else:
772
+ index = self.overlay_target_channel
773
+ self.channels_overlay_cb.setCurrentIndex(index)
774
+ self.frame_slider.valueChanged.connect(self.change_overlay_frame)
775
+
776
+ self.define_keyboard_shortcuts()
777
+
778
+ self.channels_overlay_cb.setCurrentIndex(self.parent_window.channels_cb.currentIndex())
779
+ self.set_channel_index(0)
780
+
781
+ self.setAttribute(Qt.WA_DeleteOnClose)
782
+
783
+ def generate_overlay_imshow(self):
784
+ self.im_overlay = self.ax.imshow(self.overlay_init_frame, cmap='Blues', interpolation='none',alpha=0.5, **self.imshow_kwargs)
785
+
786
+ def generate_overlay_alpha_slider(self):
787
+ # Generate the contrast slider if enabled
788
+
789
+ self.overlay_alpha_slider = QLabeledDoubleSlider()
790
+ alpha_layout = QuickSliderLayout(
791
+ label='Overlay\ntransparency: ',
792
+ slider=self.overlay_alpha_slider,
793
+ slider_initial_value=0.5,
794
+ slider_range=(0,1.0),
795
+ decimal_option=True,
796
+ precision=1.0E-05,
797
+ )
798
+ alpha_layout.setContentsMargins(15,0,15,0)
799
+ self.overlay_alpha_slider.valueChanged.connect(self.change_alpha_overlay)
800
+ self.canvas.layout.addLayout(alpha_layout)
801
+
802
+
803
+ def generate_overlay_contrast_slider(self):
804
+ # Generate the contrast slider if enabled
805
+
806
+ self.overlay_contrast_slider = QLabeledDoubleRangeSlider()
807
+ contrast_layout = QuickSliderLayout(
808
+ label='Overlay contrast: ',
809
+ slider=self.overlay_contrast_slider,
810
+ slider_initial_value=[np.nanpercentile(self.overlay_init_frame, 0.1),np.nanpercentile(self.overlay_init_frame, 99.99)],
811
+ slider_range=(np.nanmin(self.overlay_init_frame),np.nanmax(self.overlay_init_frame)),
812
+ decimal_option=True,
813
+ precision=1.0E-05,
814
+ )
815
+ contrast_layout.setContentsMargins(15,0,15,0)
816
+ self.im_overlay.set_clim(vmin=np.nanpercentile(self.overlay_init_frame, 0.1),vmax=np.nanpercentile(self.overlay_init_frame, 99.99))
817
+ self.overlay_contrast_slider.valueChanged.connect(self.change_contrast_overlay)
818
+ self.canvas.layout.addLayout(contrast_layout)
819
+
820
+ def set_overlay_channel_index(self, value):
821
+ # Set the channel index based on dropdown value
822
+
823
+ self.overlay_target_channel = value
824
+ self.overlay_init_contrast = True
825
+ if self.mode == 'direct':
826
+ self.overlay_last_frame = self.stack[-1,:,:,self.overlay_target_channel]
827
+ elif self.mode == 'virtual':
828
+ self.overlay_last_frame = load_frames(self.img_num_per_channel[self.overlay_target_channel, self.stack_length-1],
829
+ self.stack_path,
830
+ normalize_input=False).astype(float)[:,:,0]
831
+ self.change_overlay_frame(self.frame_slider.value())
832
+ self.overlay_init_contrast = False
833
+
834
+ def generate_overlay_channel_cb(self):
835
+
836
+ assert self.channel_names is not None
837
+ assert len(self.channel_names)==self.n_channels
838
+
839
+ channel_layout = QHBoxLayout()
840
+ channel_layout.setContentsMargins(15,0,15,0)
841
+ channel_layout.addWidget(QLabel('Overlay channel: '), 25)
842
+
843
+ self.channels_overlay_cb = QComboBox()
844
+ self.channels_overlay_cb.addItems(self.channel_names)
845
+ self.channels_overlay_cb.currentIndexChanged.connect(self.set_overlay_channel_index)
846
+ channel_layout.addWidget(self.channels_overlay_cb, 75)
847
+ self.canvas.layout.addLayout(channel_layout)
848
+
849
+ def generate_overlay_shift(self):
850
+
851
+ shift_layout = QHBoxLayout()
852
+ shift_layout.setContentsMargins(15,0,15,0)
853
+ shift_layout.addWidget(QLabel('shift (h): '), 20, alignment=Qt.AlignRight)
854
+
855
+ self.apply_shift_btn = QPushButton('Apply')
856
+ self.apply_shift_btn.setStyleSheet(self.button_style_sheet_2)
857
+ self.apply_shift_btn.setToolTip('Apply the shift to the overlay channel.')
858
+ self.apply_shift_btn.clicked.connect(self.shift_generic)
859
+
860
+ self.set_shift_btn = QPushButton('Set')
861
+
862
+ self.horizontal_shift_le = ThresholdLineEdit(init_value=self.shift_horizontal, connected_buttons=[self.apply_shift_btn, self.set_shift_btn],placeholder='horizontal shift [pixels]', value_type='float')
863
+ shift_layout.addWidget(self.horizontal_shift_le, 20)
864
+
865
+ shift_layout.addWidget(QLabel('shift (v): '), 20, alignment=Qt.AlignRight)
866
+
867
+ self.vertical_shift_le = ThresholdLineEdit(init_value=self.shift_vertical, connected_buttons=[self.apply_shift_btn, self.set_shift_btn],placeholder='vertical shift [pixels]', value_type='float')
868
+ shift_layout.addWidget(self.vertical_shift_le, 20)
869
+
870
+ shift_layout.addWidget(self.apply_shift_btn, 20)
871
+
872
+ self.canvas.layout.addLayout(shift_layout)
873
+
874
+
875
+ def change_overlay_frame(self, value):
876
+ # Change the displayed frame based on slider value
877
+
878
+ if self.mode=='virtual':
879
+
880
+ self.overlay_init_frame = load_frames(self.img_num_per_channel[self.overlay_target_channel, value],
881
+ self.stack_path,
882
+ normalize_input=False
883
+ ).astype(float)[:,:,0]
884
+ elif self.mode=='direct':
885
+ self.overlay_init_frame = self.stack[value,:,:,self.overlay_target_channel].copy()
886
+
887
+ self.im_overlay.set_data(self.overlay_init_frame)
888
+
889
+ if self.overlay_init_contrast:
890
+ self.im_overlay.autoscale()
891
+ I_min, I_max = self.im_overlay.get_clim()
892
+ self.overlay_contrast_slider.setRange(np.nanmin([self.overlay_init_frame,self.overlay_last_frame]),np.nanmax([self.overlay_init_frame,self.overlay_last_frame]))
893
+ self.overlay_contrast_slider.setValue((I_min,I_max))
894
+
895
+ if self.create_contrast_slider:
896
+ self.change_contrast_overlay(self.overlay_contrast_slider.value())
897
+
898
+ def locate_image_virtual(self):
899
+ # Locate the stack of images if provided as a file
900
+ self.stack_length = auto_load_number_of_frames(self.stack_path)
901
+ if self.stack_length is None:
902
+ stack = imread(self.stack_path)
903
+ self.stack_length = len(stack)
904
+ del stack
905
+ gc.collect()
906
+
907
+ self.mid_time = self.stack_length // 2
908
+ self.img_num_per_channel = _get_img_num_per_channel(np.arange(self.n_channels), self.stack_length, self.n_channels)
909
+
910
+ self.init_frame = load_frames(self.img_num_per_channel[self.target_channel, self.mid_time],
911
+ self.stack_path,
912
+ normalize_input=False).astype(float)[:,:,0]
913
+ self.last_frame = load_frames(self.img_num_per_channel[self.target_channel, self.stack_length-1],
914
+ self.stack_path,
915
+ normalize_input=False).astype(float)[:,:,0]
916
+ self.overlay_init_frame = load_frames(self.img_num_per_channel[self.overlay_target_channel, self.mid_time],
917
+ self.stack_path,
918
+ normalize_input=False).astype(float)[:,:,0]
919
+ self.overlay_last_frame = load_frames(self.img_num_per_channel[self.overlay_target_channel, self.stack_length-1],
920
+ self.stack_path,
921
+ normalize_input=False).astype(float)[:,:,0]
922
+
923
+ def change_contrast_overlay(self, value):
924
+ # Change contrast based on slider value
925
+
926
+ vmin = value[0]
927
+ vmax = value[1]
928
+ self.im_overlay.set_clim(vmin=vmin, vmax=vmax)
929
+ self.fig.canvas.draw_idle()
930
+
931
+ def change_alpha_overlay(self, value):
932
+ # Change contrast based on slider value
933
+
934
+ alpha = value
935
+ self.im_overlay.set_alpha(alpha)
936
+ self.fig.canvas.draw_idle()
937
+
938
+
939
+ def define_keyboard_shortcuts(self):
940
+
941
+ self.shift_up_shortcut = QShortcut(QKeySequence(Qt.Key_Up), self.canvas)
942
+ self.shift_up_shortcut.activated.connect(self.shift_overlay_up)
943
+
944
+ self.shift_down_shortcut = QShortcut(QKeySequence(Qt.Key_Down), self.canvas)
945
+ self.shift_down_shortcut.activated.connect(self.shift_overlay_down)
946
+
947
+ self.shift_left_shortcut = QShortcut(QKeySequence(Qt.Key_Left), self.canvas)
948
+ self.shift_left_shortcut.activated.connect(self.shift_overlay_left)
949
+
950
+ self.shift_right_shortcut = QShortcut(QKeySequence(Qt.Key_Right), self.canvas)
951
+ self.shift_right_shortcut.activated.connect(self.shift_overlay_right)
952
+
953
+
954
+ def shift_overlay_up(self):
955
+ self.shift_vertical -= 2
956
+ self.vertical_shift_le.set_threshold(self.shift_vertical)
957
+ #self.shift_generic()
958
+ self.apply_shift_btn.click()
959
+
960
+ def shift_overlay_down(self):
961
+ self.shift_vertical += 2
962
+ self.vertical_shift_le.set_threshold(self.shift_vertical)
963
+ #self.shift_generic()
964
+ self.apply_shift_btn.click()
965
+
966
+ def shift_overlay_left(self):
967
+ self.shift_horizontal -= 2
968
+ self.horizontal_shift_le.set_threshold(self.shift_horizontal)
969
+ #self.shift_generic()
970
+ self.apply_shift_btn.click()
971
+
972
+ def shift_overlay_right(self):
973
+ self.shift_horizontal += 2
974
+ self.horizontal_shift_le.set_threshold(self.shift_horizontal)
975
+ #self.shift_generic()
976
+ self.apply_shift_btn.click()
977
+
978
+ def shift_generic(self):
979
+ self.shift_vertical = self.vertical_shift_le.get_threshold()
980
+ self.shift_horizontal = self.horizontal_shift_le.get_threshold()
981
+ self.shifted_frame = shift(self.overlay_init_frame, [self.shift_vertical, self.shift_horizontal],prefilter=False)
982
+ self.im_overlay.set_data(self.shifted_frame)
983
+ self.fig.canvas.draw_idle()
984
+
985
+ def generate_add_to_parent_btn(self):
986
+
987
+ add_hbox = QHBoxLayout()
988
+ add_hbox.setContentsMargins(0,5,0,5)
989
+ self.set_shift_btn.clicked.connect(self.set_parent_attributes)
990
+ self.set_shift_btn.setStyleSheet(self.button_style_sheet)
991
+ add_hbox.addWidget(QLabel(''),33)
992
+ add_hbox.addWidget(self.set_shift_btn, 33)
993
+ add_hbox.addWidget(QLabel(''),33)
994
+ self.canvas.layout.addLayout(add_hbox)
995
+
996
+ def set_parent_attributes(self):
997
+
998
+ idx = self.channels_overlay_cb.currentIndex()
999
+ self.parent_window.channels_cb.setCurrentIndex(idx)
1000
+ self.parent_window.vertical_shift_le.set_threshold(self.vertical_shift_le.get_threshold())
1001
+ self.parent_window.horizontal_shift_le.set_threshold(self.horizontal_shift_le.get_threshold())
1002
+ self.close()
celldetective/io.py CHANGED
@@ -766,6 +766,9 @@ def locate_stack_and_labels(position, prefix='Aligned', population="target"):
766
766
  position = position.replace('\\', '/')
767
767
  labels = locate_labels(position, population=population)
768
768
  stack = locate_stack(position, prefix=prefix)
769
+ if len(labels) < len(stack):
770
+ fix_missing_labels(position, population=population, prefix=prefix)
771
+ labels = locate_labels(position, population=population)
769
772
  assert len(stack) == len(
770
773
  labels), f"The shape of the stack {stack.shape} does not match with the shape of the labels {labels.shape}"
771
774
 
@@ -1272,7 +1275,7 @@ def view_on_napari_btrack(data, properties, graph, stack=None, labels=None, rela
1272
1275
  if stack is not None:
1273
1276
  viewer.add_image(stack, channel_axis=-1, colormap=["gray"] * stack.shape[-1])
1274
1277
  if labels is not None:
1275
- viewer.add_labels(labels, name='segmentation', opacity=0.4)
1278
+ viewer.add_labels(labels.astype(int), name='segmentation', opacity=0.4)
1276
1279
  viewer.add_points(vertices, size=4, name='points', opacity=0.3)
1277
1280
  if data.shape[1]==4:
1278
1281
  viewer.add_tracks(data, properties=properties, graph=graph, name='tracks')