cellects 0.1.0.dev1__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.
Files changed (46) hide show
  1. cellects/__init__.py +0 -0
  2. cellects/__main__.py +49 -0
  3. cellects/config/__init__.py +0 -0
  4. cellects/config/all_vars_dict.py +154 -0
  5. cellects/core/__init__.py +0 -0
  6. cellects/core/cellects_paths.py +30 -0
  7. cellects/core/cellects_threads.py +1464 -0
  8. cellects/core/motion_analysis.py +1931 -0
  9. cellects/core/one_image_analysis.py +1065 -0
  10. cellects/core/one_video_per_blob.py +679 -0
  11. cellects/core/program_organizer.py +1347 -0
  12. cellects/core/script_based_run.py +154 -0
  13. cellects/gui/__init__.py +0 -0
  14. cellects/gui/advanced_parameters.py +1258 -0
  15. cellects/gui/cellects.py +189 -0
  16. cellects/gui/custom_widgets.py +789 -0
  17. cellects/gui/first_window.py +449 -0
  18. cellects/gui/if_several_folders_window.py +239 -0
  19. cellects/gui/image_analysis_window.py +1909 -0
  20. cellects/gui/required_output.py +232 -0
  21. cellects/gui/video_analysis_window.py +656 -0
  22. cellects/icons/__init__.py +0 -0
  23. cellects/icons/cellects_icon.icns +0 -0
  24. cellects/icons/cellects_icon.ico +0 -0
  25. cellects/image_analysis/__init__.py +0 -0
  26. cellects/image_analysis/cell_leaving_detection.py +54 -0
  27. cellects/image_analysis/cluster_flux_study.py +102 -0
  28. cellects/image_analysis/extract_exif.py +61 -0
  29. cellects/image_analysis/fractal_analysis.py +184 -0
  30. cellects/image_analysis/fractal_functions.py +108 -0
  31. cellects/image_analysis/image_segmentation.py +272 -0
  32. cellects/image_analysis/morphological_operations.py +867 -0
  33. cellects/image_analysis/network_functions.py +1244 -0
  34. cellects/image_analysis/one_image_analysis_threads.py +289 -0
  35. cellects/image_analysis/progressively_add_distant_shapes.py +246 -0
  36. cellects/image_analysis/shape_descriptors.py +981 -0
  37. cellects/utils/__init__.py +0 -0
  38. cellects/utils/formulas.py +881 -0
  39. cellects/utils/load_display_save.py +1016 -0
  40. cellects/utils/utilitarian.py +516 -0
  41. cellects-0.1.0.dev1.dist-info/LICENSE.odt +0 -0
  42. cellects-0.1.0.dev1.dist-info/METADATA +131 -0
  43. cellects-0.1.0.dev1.dist-info/RECORD +46 -0
  44. cellects-0.1.0.dev1.dist-info/WHEEL +5 -0
  45. cellects-0.1.0.dev1.dist-info/entry_points.txt +2 -0
  46. cellects-0.1.0.dev1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1909 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Genereate the Image analysis window of the user interface of Cellects
4
+
5
+ Cellects transforms the color images into grayscale images in a way that maximizes the contrast between the specimens and the background.
6
+ It has an automatic procedure, processed by the one_image_analysis class. If this automatic procedure does not produce good enough results, the user can manually label some areas of the picture as “cell” or “background” to help find a better color space combination. This is particularly useful when the background is heterogeneous, and Cellects can use this information in two ways: First; it can simply ignore the parts labeled as background (e.g. objects or manual writings). Second, it can use the manual annotation to train a more sophisticated segmentation method: A k-means algorithm to split the image into as many categories as necessary and use the “Cell” labelling to infer to what category the specimens are related to.
7
+ Then, Cellects will take into account the user’s input as follows: For each of the segmentations created in the previous steps, it will count the amount of pixels labeled as specimens by the user that were correctly labeled as cell in the segmentation, and will select the segmentation that achieves the highest number. Then, it will do the same thing for the pixels labeled as background. Then, it will use the AND operator between the two results having the best match with the areas labeled as specimens, the AND operator between the two results having the best match with the areas labeled as background, and the OR operator between the result having the best match with the areas labeled as specimens and the result having the best match with the areas labeled as background. Therefore, this optional labeling adds three new segmentations that take into account the user-labeled regions. If the results are still unsatisfactory, the user can continue labeling more areas until one of the segmentations matches their expectations.
8
+
9
+ """
10
+ import logging
11
+ import time
12
+ from copy import deepcopy
13
+ import numpy as np
14
+ from PySide6 import QtWidgets, QtCore, QtGui
15
+
16
+ from cellects.core.cellects_threads import (
17
+ GetFirstImThread, GetLastImThread, FirstImageAnalysisThread,
18
+ CropScaleSubtractDelineateThread, UpdateImageThread,
19
+ LastImageAnalysisThread, SaveManualDelineationThread, FinalizeImageAnalysisThread)
20
+ from cellects.gui.custom_widgets import (
21
+ MainTabsType, InsertImage, FullScreenImage, PButton, Spinbox,
22
+ Combobox, Checkbox, FixedText)
23
+ from cellects.core.one_image_analysis import OneImageAnalysis
24
+
25
+
26
+ class ImageAnalysisWindow(MainTabsType):
27
+ def __init__(self, parent, night_mode):
28
+ super().__init__(parent, night_mode)
29
+ self.setParent(parent)
30
+ self.csc_dict = self.parent().po.vars['convert_for_origin'] # To change
31
+ self.manual_delineation_flag: bool = False
32
+
33
+ def true_init(self):
34
+
35
+ logging.info("Initialize ImageAnalysisWindow")
36
+ self.data_tab.set_not_in_use()
37
+ self.image_tab.set_in_use()
38
+ self.video_tab.set_not_usable()
39
+ self.hold_click_flag: bool = False
40
+ self.is_first_image_flag: bool = True
41
+ self.is_image_analysis_running: bool = False
42
+ self.is_image_analysis_display_running: bool = False
43
+ self.asking_first_im_parameters_flag: bool = True
44
+ self.first_im_parameters_answered: bool = False
45
+ self.auto_delineation_flag: bool = False
46
+ self.delineation_done: bool = False
47
+ self.asking_delineation_flag: bool = False
48
+ self.asking_slower_or_manual_delineation_flag: bool = False
49
+ self.slower_delineation_flag: bool = False
50
+ self.asking_last_image_flag: bool = False
51
+ self.step = 0
52
+ self.temporary_mask_coord = []
53
+ self.saved_coord = []
54
+ self.back1_bio2 = 0
55
+ self.bio_masks_number = 0
56
+ self.back_masks_number = 0
57
+ self.arena_masks_number = 0
58
+ self.available_bio_names = np.arange(1, 1000, dtype=np.uint16)
59
+ self.available_back_names = np.arange(1, 1000, dtype=np.uint16)
60
+ self.parent().po.current_combination_id = 0
61
+ greyscale = len(self.parent().po.first_im.shape) == 2
62
+
63
+ self.display_image = np.zeros((self.parent().im_max_width, self.parent().im_max_width, 3), np.uint8)
64
+ self.display_image = InsertImage(self.display_image, self.parent().im_max_height, self.parent().im_max_width)
65
+ self.display_image.mousePressEvent = self.get_click_coordinates
66
+ self.display_image.mouseMoveEvent = self.get_mouse_move_coordinates
67
+ self.display_image.mouseReleaseEvent = self.get_mouse_release_coordinates
68
+
69
+ ## Title
70
+ # self.title_label = FixedText('One Image Analysis', police=30, night_mode=self.parent().po.all['night_mode'])
71
+ # self.title_label.setAlignment(QtCore.Qt.AlignHCenter)
72
+ self.image_number_label = FixedText('Image number',
73
+ tip="Change this number if cells are invisible on the first image, never otherwise\nIf they cannot be seen on the first image, increase this number and read until all cells have appeared.",
74
+ night_mode=self.parent().po.all['night_mode'])
75
+ self.image_number_label.setAlignment(QtCore.Qt.AlignVCenter)
76
+ self.image_number = Spinbox(min=1, max=self.parent().po.vars['img_number'], val=self.parent().po.all['first_detection_frame'], night_mode=self.parent().po.all['night_mode'])
77
+ self.read = PButton("Read", night_mode=self.parent().po.all['night_mode'])
78
+ self.read.clicked.connect(self.read_is_clicked)
79
+
80
+ self.one_blob_per_arena = Checkbox(not self.parent().po.vars['several_blob_per_arena'])
81
+ self.one_blob_per_arena.stateChanged.connect(self.several_blob_per_arena_check)
82
+ self.one_blob_per_arena_label = FixedText("One cell/colony per arena", valign="c",
83
+ tip="Check if there is always only one cell/colony per arena.\nUncheck if each experimental arena can contain several disconnected cells/colonies.",
84
+ night_mode=self.parent().po.all['night_mode'])
85
+
86
+
87
+ self.scale_with_label = FixedText('Scale with:', valign="c",
88
+ tip="What, on the image, should be considered to calculate pixel size in mm",
89
+ night_mode=self.parent().po.all['night_mode'])
90
+ self.scale_with = Combobox(["Image horizontal size", "Cell(s) horizontal size"], night_mode=self.parent().po.all['night_mode'])
91
+ self.scale_with.setFixedWidth(280)
92
+ self.scale_with.setCurrentIndex(self.parent().po.all['scale_with_image_or_cells'])
93
+ self.scale_size_label = FixedText('Scale size:', valign="c",
94
+ tip="True size (in mm) of the item(s) used for scaling",
95
+ night_mode=self.parent().po.all['night_mode'])
96
+ if self.parent().po.all['scale_with_image_or_cells'] == 0:
97
+ self.horizontal_size = Spinbox(min=0, max=100000,
98
+ val=self.parent().po.all['image_horizontal_size_in_mm'],
99
+ night_mode=self.parent().po.all['night_mode'])
100
+ else:
101
+ self.horizontal_size = Spinbox(min=0, max=100000,
102
+ val=self.parent().po.all['starting_blob_hsize_in_mm'],
103
+ night_mode=self.parent().po.all['night_mode'])
104
+ self.horizontal_size.valueChanged.connect(self.horizontal_size_changed)
105
+ self.scale_with.currentTextChanged.connect(self.scale_with_changed)
106
+ self.scale_unit_label = FixedText(' mm', night_mode=self.parent().po.all['night_mode'])
107
+
108
+ # 1) Open the first row layout
109
+ self.row1_widget = QtWidgets.QWidget()
110
+ self.row1_layout = QtWidgets.QHBoxLayout()
111
+ self.row1_layout.addWidget(self.image_number_label)
112
+ self.row1_layout.addWidget(self.image_number)
113
+ self.row1_layout.addWidget(self.read)
114
+ self.row1_layout.addItem(self.horizontal_space)
115
+ self.row1_layout.addWidget(self.one_blob_per_arena_label)
116
+ self.row1_layout.addWidget(self.one_blob_per_arena)
117
+ self.row1_layout.addItem(self.horizontal_space)
118
+ self.row1_layout.addWidget(self.scale_with_label)
119
+ self.row1_layout.addWidget(self.scale_with)
120
+ self.row1_layout.addItem(self.horizontal_space)
121
+ self.row1_layout.addWidget(self.scale_size_label)
122
+ self.row1_layout.addWidget(self.horizontal_size)
123
+
124
+ # self.row1_widget = QtWidgets.QWidget()
125
+ # self.row1_layout = QtWidgets.QHBoxLayout()
126
+ # self.row1_col1_widget = QtWidgets.QWidget()
127
+ # self.row1_col1_layout = QtWidgets.QVBoxLayout()
128
+ # self.row1_col2_widget = QtWidgets.QWidget()
129
+ # self.row1_col2_layout = QtWidgets.QVBoxLayout()
130
+ #
131
+ # self.im_number_widget = QtWidgets.QWidget()
132
+ # self.im_number_layout = QtWidgets.QHBoxLayout()
133
+ # self.im_number_layout.addWidget(self.image_number_label)
134
+ # self.im_number_layout.addWidget(self.image_number)
135
+ # self.im_number_layout.addWidget(self.read)
136
+ # self.im_number_widget.setLayout(self.im_number_layout)
137
+ # self.row1_col1_layout.addWidget(self.im_number_widget)
138
+ #
139
+ # self.specimen_number_widget = QtWidgets.QWidget()
140
+ # self.specimen_number_layout = QtWidgets.QHBoxLayout()
141
+ # self.specimen_number_layout.addWidget(self.one_blob_per_arena)
142
+ # self.specimen_number_layout.addWidget(self.one_blob_per_arena_label)
143
+ # self.specimen_number_widget.setLayout(self.specimen_number_layout)
144
+ # self.row1_col1_layout.addWidget(self.specimen_number_widget)
145
+ # self.row1_col1_widget.setLayout(self.row1_col1_layout)
146
+ # self.row1_layout.addWidget(self.row1_col1_widget)
147
+ #
148
+ # # self.row1_layout.addItem(self.horizontal_space)
149
+ # # self.row1_layout.addWidget(self.title_label)
150
+ # self.row1_layout.addItem(self.horizontal_space)
151
+ #
152
+ # self.scale_with_widget = QtWidgets.QWidget()
153
+ # self.scale_with_layout = QtWidgets.QHBoxLayout()
154
+ # self.scale_with_layout.addWidget(self.scale_with_label)
155
+ # self.scale_with_layout.addWidget(self.scale_with)
156
+ # self.scale_with_widget.setLayout(self.scale_with_layout)
157
+ # self.row1_col2_layout.addWidget(self.scale_with_widget)
158
+ #
159
+ # self.scale_size_widget = QtWidgets.QWidget()
160
+ # self.scale_size_layout = QtWidgets.QHBoxLayout()
161
+ # self.scale_size_layout.addWidget(self.scale_size_label)
162
+ # self.scale_size_layout.addWidget(self.horizontal_size)
163
+ # self.scale_size_widget.setLayout(self.scale_size_layout)
164
+ # self.row1_col2_layout.addWidget(self.scale_size_widget)
165
+ # self.row1_col2_widget.setLayout(self.row1_col2_layout)
166
+ # self.row1_layout.addWidget(self.row1_col2_widget)
167
+
168
+ self.row1_widget.setLayout(self.row1_layout)
169
+ self.Vlayout.addItem(self.vertical_space)
170
+ self.Vlayout.addWidget(self.row1_widget)
171
+ self.Vlayout.addItem(self.vertical_space)
172
+ self.Vlayout.setSpacing(0)
173
+
174
+ # 2) Open the central row layout
175
+ self.central_row_widget = QtWidgets.QWidget()
176
+ self.central_row_layout = QtWidgets.QGridLayout()
177
+ # self.central_row_widget.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
178
+
179
+ # it will contain a) the user drawn lines, b) the image, c) the csc
180
+ # 2)a) the user drawn lines
181
+ self.user_drawn_lines_widget = QtWidgets.QWidget()
182
+ self.user_drawn_lines_layout = QtWidgets.QVBoxLayout()
183
+ self.user_drawn_lines_label = FixedText("Select and draw:",
184
+ tip='By holding down mouse button on the image',
185
+ night_mode=self.parent().po.all['night_mode'])
186
+ self.user_drawn_lines_label.setAlignment(QtCore.Qt.AlignHCenter)
187
+ self.user_drawn_lines_layout.addWidget(self.user_drawn_lines_label)
188
+ self.pbuttons_widget = QtWidgets.QWidget()
189
+ self.pbuttons_layout = QtWidgets.QHBoxLayout()
190
+ self.cell = PButton("Cell", False, night_mode=self.parent().po.all['night_mode'])
191
+ self.cell.setFixedWidth(150)
192
+ self.background = PButton("Back", False, night_mode=self.parent().po.all['night_mode'])
193
+ self.background.setFixedWidth(150)
194
+ self.cell.clicked.connect(self.cell_is_clicked)
195
+ self.background.clicked.connect(self.background_is_clicked)
196
+ self.pbuttons_layout.addWidget(self.cell)
197
+ self.pbuttons_layout.addWidget(self.background)
198
+ self.pbuttons_widget.setLayout(self.pbuttons_layout)
199
+ self.user_drawn_lines_layout.addWidget(self.pbuttons_widget)
200
+
201
+ self.pbuttons_tables_widget = QtWidgets.QWidget()
202
+ self.pbuttons_tables_layout = QtWidgets.QHBoxLayout()
203
+ self.pbuttons_tables_layout.setAlignment(QtCore.Qt.AlignHCenter)
204
+ self.bio_pbuttons_table = QtWidgets.QScrollArea()#QTableWidget() # Scroll Area which contains the widgets, set as the centralWidget
205
+ self.bio_pbuttons_table.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
206
+ # self.bio_pbuttons_table.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
207
+ self.bio_pbuttons_table.setMinimumHeight(self.parent().im_max_height // 2)
208
+ self.bio_pbuttons_table.setFrameShape(QtWidgets.QFrame.NoFrame)
209
+ self.bio_pbuttons_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
210
+ # self.bio_pbuttons_table.setColumnCount(1)
211
+ # self.bio_pbuttons_table.verticalHeader().hide()
212
+ # self.bio_pbuttons_table.horizontalHeader().hide()
213
+ self.back_pbuttons_table = QtWidgets.QScrollArea()#QTableWidget() # Scroll Area which contains the widgets, set as the centralWidget
214
+ self.back_pbuttons_table.setMinimumHeight(self.parent().im_max_height // 2)
215
+ self.back_pbuttons_table.setFrameShape(QtWidgets.QFrame.NoFrame)
216
+ # self.back_pbuttons_table.setShowGrid(False)
217
+ self.back_pbuttons_table.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
218
+ # self.back_pbuttons_table.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
219
+ self.back_pbuttons_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
220
+ # self.back_pbuttons_table.setColumnCount(1)
221
+ # self.back_pbuttons_table.verticalHeader().hide()
222
+ # self.back_pbuttons_table.horizontalHeader().hide()
223
+
224
+ self.bio_added_lines_widget = QtWidgets.QWidget()
225
+ self.back_added_lines_widget = QtWidgets.QWidget()
226
+ self.bio_added_lines_layout = QtWidgets.QVBoxLayout()
227
+ self.back_added_lines_layout = QtWidgets.QVBoxLayout()
228
+ self.back_added_lines_widget.setLayout(self.back_added_lines_layout)
229
+ self.bio_added_lines_widget.setLayout(self.bio_added_lines_layout)
230
+ self.bio_pbuttons_table.setWidget(self.bio_added_lines_widget)
231
+ self.back_pbuttons_table.setWidget(self.back_added_lines_widget)
232
+ self.bio_pbuttons_table.setWidgetResizable(True)
233
+ self.back_pbuttons_table.setWidgetResizable(True)
234
+
235
+ self.pbuttons_tables_layout.addWidget(self.bio_pbuttons_table)
236
+ self.pbuttons_tables_layout.addWidget(self.back_pbuttons_table)
237
+ self.pbuttons_tables_widget.setLayout(self.pbuttons_tables_layout)
238
+ self.user_drawn_lines_layout.addWidget(self.pbuttons_tables_widget)
239
+
240
+ # self.added_lines_widget = QtWidgets.QWidget()
241
+ # self.added_lines_layout = QtWidgets.QHBoxLayout()
242
+ # self.bio_added_lines_widget = QtWidgets.QWidget()
243
+ # self.bio_added_lines_layout = QtWidgets.QVBoxLayout()
244
+ # self.back_added_lines_widget = QtWidgets.QWidget()
245
+ # self.back_added_lines_layout = QtWidgets.QVBoxLayout()
246
+ # # Dynamically add the lines
247
+ self.bio_lines = {}
248
+ self.back_lines = {}
249
+ self.arena_lines = {}
250
+ # self.bio_added_lines_widget.setLayout(self.bio_added_lines_layout)
251
+ # self.back_added_lines_widget.setLayout(self.back_added_lines_layout)
252
+ # self.added_lines_layout.addWidget(self.bio_added_lines_widget)
253
+ # self.added_lines_layout.addWidget(self.back_added_lines_widget)
254
+ # self.added_lines_widget.setLayout(self.added_lines_layout)
255
+ # self.user_drawn_lines_layout.addWidget(self.added_lines_widget)
256
+ # self.user_drawn_lines_layout.addItem(self.vertical_space)
257
+
258
+ self.user_drawn_lines_widget.setLayout(self.user_drawn_lines_layout)
259
+ # self.user_drawn_lines_widget.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
260
+ # self.user_drawn_lines_widget.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
261
+ self.user_drawn_lines_widget.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
262
+ # self.user_drawn_lines_widget.setFixedWidth(450)
263
+ self.central_row_layout.addWidget(self.user_drawn_lines_widget, 0, 0)
264
+
265
+ # 2)b) the image
266
+ # self.central_row_layout.columnStretch(1)
267
+ self.central_row_layout.addWidget(self.display_image, 0, 1)
268
+ # self.central_row_layout.columnStretch(2)
269
+
270
+ # Need to create this before self.generate_csc_editing()
271
+ self.message = FixedText("", halign="r", night_mode=self.parent().po.all['night_mode'])
272
+ self.message.setStyleSheet("color: rgb(230, 145, 18)")
273
+
274
+ # 2)c) The csc editing
275
+ self.central_right_widget = QtWidgets.QWidget()
276
+ self.central_right_layout = QtWidgets.QVBoxLayout()
277
+ self.generate_csc_editing()
278
+ self.central_right_layout.addWidget(self.edit_widget)
279
+ self.central_right_widget.setLayout(self.central_right_layout)
280
+ # self.central_right_widget.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
281
+ # self.central_right_widget.setFixedWidth(450)
282
+
283
+ self.central_row_layout.addWidget(self.central_right_widget, 0, 2)
284
+ self.central_row_layout.setAlignment(QtCore.Qt.AlignLeft)
285
+ self.central_row_layout.setAlignment(QtCore.Qt.AlignHCenter)
286
+ # 2) Close the central row layout
287
+ self.central_row_widget.setLayout(self.central_row_layout)
288
+ # self.central_row_widget.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
289
+ # self.central_row_widget.setFixedHeight(self.parent().im_max_height)
290
+ self.Vlayout.addWidget(self.central_row_widget)
291
+ # self.Vlayout.setSpacing(0)
292
+ self.Vlayout.addItem(self.vertical_space)
293
+
294
+ # 3) Add Set supplementary parameters row 1
295
+ self.sup_param_row1_widget = QtWidgets.QWidget()
296
+ self.sup_param_row1_layout = QtWidgets.QHBoxLayout()
297
+ # self.sample_number = Spinbox(min=0, max=255, val=self.parent().po.all['first_folder_sample_number'],
298
+ # decimals=0, night_mode=self.parent().po.all['night_mode'])
299
+ # self.sample_number_label = FixedText("Arena per image", night_mode=self.parent().po.all['night_mode'])
300
+ # self.sample_number.valueChanged.connect(self.sample_number_changed)
301
+
302
+ #HERE
303
+
304
+ # 4) Add Set supplementary parameters row2
305
+ self.sup_param_row2_widget = QtWidgets.QWidget()
306
+ self.sup_param_row2_layout = QtWidgets.QHBoxLayout()
307
+
308
+ self.arena_shape_label = FixedText("Arena shape", night_mode=self.parent().po.all['night_mode'])
309
+ self.arena_shape = Combobox(['circle', 'rectangle'], night_mode=self.parent().po.all['night_mode'])
310
+ self.arena_shape.setFixedWidth(160)
311
+ self.arena_shape.setCurrentText(self.parent().po.vars['arena_shape'])
312
+ self.arena_shape.currentTextChanged.connect(self.arena_shape_changed)
313
+ self.set_spot_shape = Checkbox(self.parent().po.all['set_spot_shape'])
314
+ self.set_spot_shape.stateChanged.connect(self.set_spot_shape_check)
315
+ self.spot_shape_label = FixedText("Set spot shape", tip="horizontal size in mm", night_mode=self.parent().po.all['night_mode'])
316
+ self.spot_shape = Combobox(['circle', 'rectangle'], night_mode=self.parent().po.all['night_mode'])
317
+ self.spot_shape.setFixedWidth(160)
318
+ if self.parent().po.all['starting_blob_shape'] is None:
319
+ self.spot_shape.setCurrentText('circle')
320
+ else:
321
+ self.spot_shape.setCurrentText(self.parent().po.all['starting_blob_shape'])
322
+ self.spot_shape.currentTextChanged.connect(self.spot_shape_changed)
323
+ self.set_spot_size = Checkbox(self.parent().po.all['set_spot_size'])
324
+ self.set_spot_size.stateChanged.connect(self.set_spot_size_check)
325
+ self.spot_size_label = FixedText("Set spot size", night_mode=self.parent().po.all['night_mode'])
326
+ self.spot_size = Spinbox(min=0, max=100000, val=self.parent().po.all['starting_blob_hsize_in_mm'], decimals=2,
327
+ night_mode=self.parent().po.all['night_mode'])
328
+ self.spot_size.valueChanged.connect(self.spot_size_changed)
329
+ self.sup_param_row2_layout.addItem(self.horizontal_space)
330
+ self.sup_param_row2_layout.addWidget(self.arena_shape_label)
331
+ self.sup_param_row2_layout.addWidget(self.arena_shape)
332
+ self.sup_param_row2_layout.addWidget(self.set_spot_shape)
333
+ self.sup_param_row2_layout.addWidget(self.spot_shape_label)
334
+ self.sup_param_row2_layout.addWidget(self.spot_shape)
335
+ self.sup_param_row2_layout.addWidget(self.set_spot_size)
336
+ self.sup_param_row2_layout.addWidget(self.spot_size_label)
337
+ self.sup_param_row2_layout.addWidget(self.spot_size)
338
+ self.sup_param_row2_widget.setLayout(self.sup_param_row2_layout)
339
+ self.sup_param_row2_layout.addItem(self.horizontal_space)
340
+ self.Vlayout.addWidget(self.sup_param_row2_widget)
341
+ self.Vlayout.setSpacing(0)
342
+
343
+ # self.sample_number.setVisible(False)
344
+ # self.sample_number_label.setVisible(False)
345
+ self.one_blob_per_arena.setVisible(True)
346
+ self.one_blob_per_arena_label.setVisible(True)
347
+ self.set_spot_shape.setVisible(False)
348
+ self.spot_shape_label.setVisible(False)
349
+ self.spot_shape.setVisible(False)
350
+ self.arena_shape_label.setVisible(False)
351
+ self.arena_shape.setVisible(False)
352
+ self.set_spot_size.setVisible(False)
353
+ self.spot_size_label.setVisible(False)
354
+ self.spot_size.setVisible(False)
355
+
356
+ # 5) Add the generate option row
357
+ self.generate_analysis_options = FixedText("Generate analysis options: ", night_mode=self.parent().po.all['night_mode'])
358
+ self.quickly = PButton("Quickly", night_mode=self.parent().po.all['night_mode'])
359
+ self.carefully = PButton("Carefully", night_mode=self.parent().po.all['night_mode'])
360
+ self.quickly.clicked.connect(self.quickly_is_clicked)
361
+ self.carefully.clicked.connect(self.carefully_is_clicked)
362
+ self.visualize = PButton('Visualize', night_mode=self.parent().po.all['night_mode'])
363
+ self.visualize.clicked.connect(self.visualize_is_clicked)
364
+ if self.parent().po.vars['already_greyscale']:
365
+ self.visualize_label = FixedText("Directly: ", night_mode=self.parent().po.all['night_mode'])
366
+ else:
367
+ self.visualize_label = FixedText("Or directly: ", night_mode=self.parent().po.all['night_mode'])
368
+
369
+ self.sup_param_row1_layout.addWidget(self.generate_analysis_options)
370
+ self.sup_param_row1_layout.addWidget(self.quickly)
371
+ self.sup_param_row1_layout.addWidget(self.carefully)
372
+ self.sup_param_row1_layout.addItem(self.horizontal_space)
373
+ # self.sup_param_row1_layout.addWidget(self.sample_number)
374
+ # self.sup_param_row1_layout.addWidget(self.sample_number_label)
375
+ self.sup_param_row1_layout.addItem(self.horizontal_space)
376
+ self.sup_param_row1_layout.addWidget(self.visualize_label)
377
+ self.sup_param_row1_layout.addWidget(self.visualize)
378
+
379
+ self.sup_param_row1_widget.setLayout(self.sup_param_row1_layout)
380
+ self.Vlayout.addWidget(self.sup_param_row1_widget)
381
+ self.Vlayout.setSpacing(0)
382
+
383
+ # 6) Open the choose best option row layout
384
+ self.options_row_widget = QtWidgets.QWidget()
385
+ self.options_row_layout = QtWidgets.QHBoxLayout()
386
+ self.select_option_label = FixedText('Select option to read', tip='Select the option allowing the best segmentation between the cell and the background',
387
+ night_mode=self.parent().po.all['night_mode'])
388
+ self.select_option = Combobox([], night_mode=self.parent().po.all['night_mode'])
389
+ if self.parent().po.vars['color_number'] == 2:
390
+ self.select_option.setCurrentIndex(self.parent().po.all['video_option'])
391
+ self.select_option.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
392
+ # self.select_option.setFixedWidth(120)
393
+ self.select_option.setMinimumWidth(145)
394
+ self.select_option.currentTextChanged.connect(self.option_changed)
395
+ self.n_shapes_detected = FixedText(f'', night_mode=self.parent().po.all['night_mode'])
396
+ self.select_option_label.setVisible(False)
397
+ self.select_option.setVisible(False)
398
+ self.n_shapes_detected.setVisible(False)
399
+ self.options_row_layout.addItem(self.horizontal_space)
400
+ self.options_row_layout.addWidget(self.select_option_label)
401
+ self.options_row_layout.addWidget(self.select_option)
402
+ self.options_row_layout.addWidget(self.n_shapes_detected)
403
+ self.options_row_layout.addItem(self.horizontal_space)
404
+ self.options_row_widget.setLayout(self.options_row_layout)
405
+ self.Vlayout.addWidget(self.options_row_widget)
406
+ self.Vlayout.setSpacing(0)
407
+
408
+ # 7) Open decision row layout
409
+ self.decision_row_widget = QtWidgets.QWidget()
410
+ self.decision_row_layout = QtWidgets.QHBoxLayout()
411
+ self.decision_label = FixedText("", night_mode=self.parent().po.all['night_mode'])
412
+ self.yes = PButton("Yes", night_mode=self.parent().po.all['night_mode'])
413
+ self.yes.clicked.connect(self.when_yes_is_clicked)
414
+ self.no = PButton("No", night_mode=self.parent().po.all['night_mode'])
415
+ self.no.clicked.connect(self.when_no_is_clicked)
416
+
417
+ self.decision_label.setVisible(False)
418
+ self.yes.setVisible(False)
419
+ self.no.setVisible(False)
420
+ self.decision_row_layout.addItem(self.horizontal_space)
421
+ self.decision_row_layout.addWidget(self.decision_label)
422
+ self.decision_row_layout.addWidget(self.yes)
423
+ self.decision_row_layout.addWidget(self.no)
424
+ self.decision_row_layout.addItem(self.horizontal_space)
425
+ self.decision_row_widget.setLayout(self.decision_row_layout)
426
+ self.Vlayout.addWidget(self.decision_row_widget)
427
+
428
+ # 8) Open the special cases layout
429
+ self.special_cases_widget = QtWidgets.QWidget()
430
+ self.special_cases_layout = QtWidgets.QHBoxLayout()
431
+ self.starting_differs_from_growing_cb = Checkbox(self.parent().po.vars['origin_state'] == 'constant')
432
+ self.starting_differs_from_growing_cb.stateChanged.connect(self.starting_differs_from_growing_check)
433
+ self.starting_differs_from_growing_label = FixedText("Check if the starting area differs from the growing area", tip="This option is only relevant for experiments in which the medium\n(e.g. agar) on which the cells grow is heterogeneous.\nMore precisely when the exploration areas on which the cells will grow and/or move\nare not the same color as the one they were initially on.", night_mode=self.parent().po.all['night_mode'])
434
+ self.starting_differs_from_growing_cb.setVisible(False)
435
+ self.starting_differs_from_growing_label.setVisible(False)
436
+ self.special_cases_layout.addWidget(self.starting_differs_from_growing_cb)
437
+ self.special_cases_layout.addWidget(self.starting_differs_from_growing_label)
438
+ self.special_cases_widget.setLayout(self.special_cases_layout)
439
+ self.Vlayout.addWidget(self.special_cases_widget)
440
+
441
+ self.Vlayout.setSpacing(0)
442
+
443
+ # 9) Open the last row layout
444
+ self.last_row_widget = QtWidgets.QWidget()
445
+ self.last_row_layout = QtWidgets.QHBoxLayout()
446
+ self.previous = PButton('Previous', night_mode=self.parent().po.all['night_mode'])
447
+ self.previous.clicked.connect(self.previous_is_clicked)
448
+ self.data_tab.clicked.connect(self.data_is_clicked)
449
+ self.video_tab.clicked.connect(self.video_is_clicked)
450
+ self.next = PButton("Next", night_mode=self.parent().po.all['night_mode'])
451
+ self.next.setVisible(False)
452
+ self.next.clicked.connect(self.go_to_next_widget)
453
+ self.last_row_layout.addWidget(self.previous)
454
+ self.last_row_layout.addWidget(self.message)
455
+ self.last_row_layout.addItem(self.horizontal_space)
456
+ self.last_row_layout.addWidget(self.next)
457
+ self.last_row_widget.setLayout(self.last_row_layout)
458
+ self.Vlayout.addWidget(self.last_row_widget)
459
+ self.setLayout(self.Vlayout)
460
+ self.Vlayout.setSpacing(0)
461
+
462
+ self.advanced_mode_check()
463
+
464
+ self.thread = {}
465
+ self.thread["GetFirstIm"] = GetFirstImThread(self.parent())
466
+ # self.thread["GetFirstIm"].start()
467
+ # self.thread["GetFirstIm"].message_when_thread_finished.connect(self.first_im_read)
468
+ self.reinitialize_image_and_masks(self.parent().po.first_im)
469
+ self.thread["GetLastIm"] = GetLastImThread(self.parent())
470
+ self.thread["GetLastIm"].start()
471
+ self.parent().po.first_image = OneImageAnalysis(self.parent().po.first_im)
472
+ self.thread["FirstImageAnalysis"] = FirstImageAnalysisThread(self.parent())
473
+ self.thread["LastImageAnalysis"] = LastImageAnalysisThread(self.parent())
474
+ self.thread['UpdateImage'] = UpdateImageThread(self.parent())
475
+ self.thread['CropScaleSubtractDelineate'] = CropScaleSubtractDelineateThread(self.parent())
476
+ self.thread['SaveManualDelineation'] = SaveManualDelineationThread(self.parent())
477
+ self.thread['FinalizeImageAnalysis'] = FinalizeImageAnalysisThread(self.parent())
478
+
479
+ def previous_is_clicked(self):
480
+ if self.is_image_analysis_running:
481
+ self.message.setText("Wait for the analysis to end, or restart Cellects")
482
+ else:
483
+ self.parent().firstwindow.instantiate = True
484
+ self.hold_click_flag: bool = False
485
+ self.is_first_image_flag: bool = True
486
+ self.is_image_analysis_running: bool = False
487
+ self.is_image_analysis_display_running: bool = False
488
+ self.asking_first_im_parameters_flag: bool = True
489
+ self.first_im_parameters_answered: bool = False
490
+ self.auto_delineation_flag: bool = False
491
+ self.delineation_done: bool = False
492
+ self.asking_delineation_flag: bool = False
493
+ self.asking_slower_or_manual_delineation_flag: bool = False
494
+ self.slower_delineation_flag: bool = False
495
+ self.asking_last_image_flag: bool = False
496
+ self.step = 0
497
+ self.temporary_mask_coord = []
498
+ self.saved_coord = []
499
+ self.back1_bio2 = 0
500
+ self.bio_masks_number = 0
501
+ self.back_masks_number = 0
502
+ self.arena_masks_number = 0
503
+ self.available_bio_names = np.arange(1, 1000, dtype=np.uint16)
504
+ self.available_back_names = np.arange(1, 1000, dtype=np.uint16)
505
+ self.parent().po.current_combination_id = 0
506
+ self.parent().last_tab = "data_specifications"
507
+ self.parent().change_widget(0) # First
508
+
509
+ def data_is_clicked(self):
510
+ if self.is_image_analysis_running:
511
+ self.message.setText("Wait for the analysis to end, or restart Cellects")
512
+ else:
513
+ self.parent().last_tab = "data_specifications"
514
+ self.parent().change_widget(0) # First
515
+
516
+ def video_is_clicked(self):
517
+
518
+ if self.video_tab.state != "not_usable":
519
+ if self.is_image_analysis_running:
520
+ self.message.setText("Wait for the analysis to end, or restart Cellects")
521
+ else:
522
+ self.parent().last_tab = "image_analysis"
523
+ self.parent().change_widget(3)
524
+
525
+ def read_is_clicked(self):
526
+ if not self.thread["GetFirstIm"].isRunning():
527
+ self.parent().po.all['first_detection_frame'] = int(self.image_number.value())
528
+ self.message.setText(f"Reading image n°{self.parent().po.all['first_detection_frame']}")
529
+ self.thread["GetFirstIm"].start()
530
+ # self.thread["GetFirstIm"].message_when_thread_finished.connect(self.reinitialize_image_and_masks)
531
+ self.reinitialize_bio_and_back_legend()
532
+ self.reinitialize_image_and_masks(self.parent().po.first_im)
533
+
534
+
535
+ def several_blob_per_arena_check(self):
536
+ is_checked = self.one_blob_per_arena.isChecked()
537
+ self.parent().po.vars['several_blob_per_arena'] = not is_checked
538
+ self.set_spot_size.setVisible(is_checked)
539
+ self.spot_size_label.setVisible(is_checked)
540
+ self.spot_size.setVisible(is_checked and self.set_spot_size.isChecked())
541
+
542
+ def set_spot_size_check(self):
543
+ is_checked = self.set_spot_size.isChecked()
544
+ if self.step == 1:
545
+ self.spot_size.setVisible(is_checked)
546
+ self.parent().po.all['set_spot_size'] = is_checked
547
+
548
+ def spot_size_changed(self):
549
+ self.parent().po.all['starting_blob_hsize_in_mm'] = self.spot_size.value()
550
+ if self.parent().po.all['scale_with_image_or_cells'] == 1:
551
+ self.horizontal_size.setValue(self.parent().po.all['starting_blob_hsize_in_mm'])
552
+ self.set_spot_size_check()
553
+
554
+ def set_spot_shape_check(self):
555
+ is_checked = self.set_spot_shape.isChecked()
556
+ self.spot_shape.setVisible(is_checked)
557
+ self.parent().po.all['set_spot_shape'] = is_checked
558
+ if not is_checked:
559
+ self.parent().po.all['starting_blob_shape'] = None
560
+
561
+ def spot_shape_changed(self):
562
+ self.parent().po.all['starting_blob_shape'] = self.spot_shape.currentText()
563
+ self.set_spot_shape_check()
564
+
565
+ def arena_shape_changed(self):
566
+ self.parent().po.vars['arena_shape'] = self.arena_shape.currentText()
567
+ if self.asking_delineation_flag:
568
+ if self.thread['CropScaleSubtractDelineate'].isRunning():
569
+ self.thread['CropScaleSubtractDelineate'].wait()
570
+ if self.thread['UpdateImage'].isRunning():
571
+ self.thread['UpdateImage'].wait()
572
+ self.message.setText("Display updating...")
573
+ self.decision_label.setVisible(False)
574
+ self.yes.setVisible(False)
575
+ self.no.setVisible(False)
576
+ self.reinitialize_bio_and_back_legend()
577
+ self.reinitialize_image_and_masks(self.parent().po.first_image.bgr)
578
+ self.delineation_done = True
579
+ if self.thread["UpdateImage"].isRunning():
580
+ self.thread["UpdateImage"].wait()
581
+ self.thread["UpdateImage"].start()
582
+ self.thread["UpdateImage"].message_when_thread_finished.connect(self.automatic_delineation_display_done)
583
+
584
+ # self.start_crop_scale_subtract_delineate()
585
+
586
+ def reinitialize_bio_and_back_legend(self):
587
+ lines_names_to_remove = []
588
+ for line_number, back_line_dict in self.back_lines.items():
589
+ line_name = u"\u00D7" + " Back" + str(line_number)
590
+ self.back_added_lines_layout.removeWidget(back_line_dict[line_name])
591
+ back_line_dict[line_name].deleteLater()
592
+ lines_names_to_remove.append(line_number)
593
+ for line_number in lines_names_to_remove:
594
+ self.back_lines.pop(line_number)
595
+ lines_names_to_remove = []
596
+ for line_number, bio_line_dict in self.bio_lines.items():
597
+ line_name = u"\u00D7" + " Cell" + str(line_number)
598
+ self.bio_added_lines_layout.removeWidget(bio_line_dict[line_name])
599
+ bio_line_dict[line_name].deleteLater()
600
+ lines_names_to_remove.append(line_number)
601
+ for line_number in lines_names_to_remove:
602
+ self.bio_lines.pop(line_number)
603
+ if len(self.arena_lines) > 0:
604
+ lines_names_to_remove = []
605
+ for i, (line_number, arena_line_dict) in enumerate(self.arena_lines.items()):
606
+ line_name = u"\u00D7" + " Arena" + str(line_number)
607
+ if i % 2 == 0:
608
+ self.bio_added_lines_layout.removeWidget(arena_line_dict[line_name])
609
+ else:
610
+ self.back_added_lines_layout.removeWidget(arena_line_dict[line_name])
611
+ arena_line_dict[line_name].deleteLater()
612
+ lines_names_to_remove.append(line_number)
613
+ for line_number in lines_names_to_remove:
614
+ self.arena_lines.pop(line_number)
615
+ self.bio_masks_number = 0
616
+ self.back_masks_number = 0
617
+
618
+ def reinitialize_image_and_masks(self, image):
619
+ if len(image.shape) == 2:
620
+ self.parent().po.current_image = np.stack((image, image, image), axis=2)
621
+ # self.sample_number.setVisible(True)
622
+ # self.sample_number_label.setVisible(True)
623
+ # self.one_blob_per_arena.setVisible(True)
624
+ # self.one_blob_per_arena_label.setVisible(True)
625
+ # self.set_spot_shape.setVisible(True)
626
+ # self.spot_shape_label.setVisible(True)
627
+ # self.spot_shape.setVisible(True)
628
+ # self.arena_shape.setVisible(True)
629
+ # self.arena_shape_label.setVisible(True)
630
+
631
+ self.generate_analysis_options.setVisible(False)
632
+ self.quickly.setVisible(False)
633
+ self.carefully.setVisible(False)
634
+ self.select_option.setVisible(False)
635
+ self.select_option_label.setVisible(False)
636
+
637
+ # self.parent().po.all['expert_mode'] = True
638
+ # self.advanced_mode_cb.setChecked(False)
639
+ # self.advanced_mode_cb.setVisible(False)
640
+ # self.advanced_mode_label.setVisible(False)
641
+
642
+ # self.more_than_two_colors.setVisible(True)
643
+ # self.more_than_two_colors_label.setVisible(True)
644
+
645
+ self.visualize.setVisible(True)
646
+ self.visualize_label.setVisible(True)
647
+ else:
648
+ self.parent().po.current_image = deepcopy(image)
649
+ self.drawn_image = deepcopy(self.parent().po.current_image)
650
+ self.display_image.update_image(self.parent().po.current_image)
651
+
652
+ self.arena_mask = None
653
+ self.bio_mask = np.zeros(self.parent().po.current_image.shape[:2], dtype=np.uint16)
654
+ self.back_mask = np.zeros(self.parent().po.current_image.shape[:2], dtype=np.uint16)
655
+
656
+ def scale_with_changed(self):
657
+ self.parent().po.all['scale_with_image_or_cells'] = self.scale_with.currentIndex()
658
+ if self.parent().po.all['scale_with_image_or_cells'] == 0:
659
+ self.horizontal_size.setValue(self.parent().po.all['image_horizontal_size_in_mm'])
660
+ else:
661
+ self.horizontal_size.setValue(self.parent().po.all['starting_blob_hsize_in_mm'])
662
+
663
+ def horizontal_size_changed(self):
664
+ if self.parent().po.all['scale_with_image_or_cells'] == 0:
665
+ self.parent().po.all['image_horizontal_size_in_mm'] = self.horizontal_size.value()
666
+ else:
667
+ self.parent().po.all['starting_blob_hsize_in_mm'] = self.horizontal_size.value()
668
+ self.spot_size.setValue(self.parent().po.all['starting_blob_hsize_in_mm'])
669
+
670
+ def advanced_mode_check(self):
671
+ is_checked = self.advanced_mode_cb.isChecked()
672
+ color_analysis = is_checked and not self.parent().po.vars['already_greyscale']
673
+ self.parent().po.all['expert_mode'] = is_checked
674
+
675
+ if is_checked and (self.asking_first_im_parameters_flag or self.auto_delineation_flag):
676
+ self.arena_shape_label.setVisible(True)
677
+ self.arena_shape.setVisible(True)
678
+ self.set_spot_shape.setVisible(True)
679
+ self.spot_shape_label.setVisible(True)
680
+ self.spot_shape.setVisible(self.set_spot_shape.isChecked())
681
+ self.set_spot_size.setVisible(self.one_blob_per_arena.isChecked())
682
+ self.spot_size_label.setVisible(self.one_blob_per_arena.isChecked())
683
+ self.spot_size.setVisible(
684
+ self.one_blob_per_arena.isChecked() and self.set_spot_size.isChecked())
685
+ self.first_im_parameters_answered = True
686
+
687
+ self.space_label.setVisible(color_analysis)
688
+ self.c1.setVisible(color_analysis)
689
+ self.c2.setVisible(color_analysis)
690
+ self.c3.setVisible(color_analysis)
691
+ display_logical = self.logical_operator_between_combination_result.currentText() != 'None'
692
+ self.logical_operator_between_combination_result.setVisible(color_analysis and display_logical)
693
+ self.logical_operator_label.setVisible(color_analysis and display_logical)
694
+
695
+ # if not self.parent().po.vars['already_greyscale']:
696
+ # self.visualize.setVisible(is_checked)
697
+ # self.visualize_label.setVisible(is_checked)
698
+ at_least_one_line_drawn = self.bio_masks_number > 0
699
+ self.more_than_two_colors.setVisible(is_checked and at_least_one_line_drawn)
700
+ self.more_than_two_colors_label.setVisible(is_checked and at_least_one_line_drawn)
701
+ self.distinct_colors_number.setVisible(is_checked and at_least_one_line_drawn and self.parent().po.all["more_than_two_colors"])
702
+ self.grid_segmentation.setVisible(is_checked)
703
+ self.grid_segmentation_label.setVisible(is_checked)
704
+
705
+ for i in range(5):
706
+ self.row1[i].setVisible(color_analysis and self.row1[0].currentText() != "None")
707
+ self.row21[i].setVisible(color_analysis and self.row21[0].currentText() != "None")
708
+ self.row2[i].setVisible(color_analysis and self.row2[0].currentText() != "None")
709
+ self.row22[i].setVisible(color_analysis and self.row22[0].currentText() != "None")
710
+ if i < 4:
711
+ self.row3[i].setVisible(color_analysis and self.row3[0].currentText() != "None")
712
+ self.row23[i].setVisible(color_analysis and self.row23[0].currentText() != "None")
713
+ if color_analysis:
714
+ if self.row1[0].currentText() != "None":
715
+ if self.row2[0].currentText() == "None":
716
+ self.row1[4].setVisible(True)
717
+ else:
718
+ self.row2[4].setVisible(True)
719
+ if self.row21[0].currentText() != "None":
720
+ if self.row22[0].currentText() == "None":
721
+ self.row21[4].setVisible(True)
722
+ else:
723
+ self.row22[4].setVisible(True)
724
+ else:
725
+ self.row1[4].setVisible(False)
726
+ self.row2[4].setVisible(False)
727
+ self.row21[4].setVisible(False)
728
+ self.row22[4].setVisible(False)
729
+
730
+ def cell_is_clicked(self):
731
+ if self.back1_bio2 == 2:
732
+ self.cell.night_mode_switch(night_mode=self.parent().po.all['night_mode'])
733
+ self.back1_bio2 = 0
734
+ else:
735
+ self.cell.color("rgb(230, 145, 18)")
736
+ self.background.night_mode_switch(night_mode=self.parent().po.all['night_mode'])
737
+ self.back1_bio2 = 2
738
+ self.saved_coord = []
739
+
740
+ def background_is_clicked(self):
741
+ if self.back1_bio2 == 1:
742
+ self.background.night_mode_switch(night_mode=self.parent().po.all['night_mode'])
743
+ self.back1_bio2 = 0
744
+ else:
745
+ self.background.color("rgb(81, 160, 224)")
746
+ self.cell.night_mode_switch(night_mode=self.parent().po.all['night_mode'])
747
+ self.back1_bio2 = 1
748
+ self.saved_coord = []
749
+
750
+ def get_click_coordinates(self, event):
751
+ if self.back1_bio2 > 0 or self.manual_delineation_flag:
752
+ if not self.is_image_analysis_display_running and not self.thread["UpdateImage"].isRunning():
753
+ self.hold_click_flag = True
754
+ self.saved_coord.append([event.pos().y(), event.pos().x()])
755
+ else:
756
+ self.popup_img = FullScreenImage(self.drawn_image, self.parent().screen_width, self.parent().screen_height)
757
+ self.popup_img.show()
758
+ # img = resize(self.drawn_image, (self.parent().screen_width, self.parent().screen_height))
759
+ # cv2.imshow("Full screen image display", img)
760
+ # waitKey(0)
761
+ # destroyAllWindows()
762
+
763
+ def get_mouse_move_coordinates(self, event):
764
+ # if not self.is_image_analysis_display_running:
765
+ # if self.back1_bio2 > 0 or self.manual_delineation_flag:
766
+ # if not self.thread["UpdateImage"].isRunning() and len(self.saved_coord) > 0:
767
+ # if self.saved_coord[0][0] != event.pos().y() and self.saved_coord[0][1] != event.pos().x():
768
+ # self.temporary_mask_coord = [self.saved_coord[0], [event.pos().y(), event.pos().x()]]
769
+ # self.thread["UpdateImage"].start()
770
+ if self.hold_click_flag:
771
+ if not self.thread["UpdateImage"].isRunning():
772
+ if self.saved_coord[0][0] != event.pos().y() and self.saved_coord[0][1] != event.pos().x():
773
+ self.temporary_mask_coord = [self.saved_coord[0], [event.pos().y(), event.pos().x()]]
774
+ self.thread["UpdateImage"].start()
775
+
776
+ def get_mouse_release_coordinates(self, event):
777
+ if self.hold_click_flag:
778
+ if self.thread["UpdateImage"].isRunning():
779
+ self.thread["UpdateImage"].wait()
780
+ # self.saved_coord = []
781
+ # self.background.night_mode_switch(night_mode=self.parent().po.all['night_mode'])
782
+ # self.background.night_mode_switch(night_mode=self.parent().po.all['night_mode'])
783
+ # self.back1_bio2 = 0
784
+ self.temporary_mask_coord = []
785
+ if self.manual_delineation_flag and len(self.parent().imageanalysiswindow.available_arena_names) == 0:
786
+ self.message.setText(f"The total number of arenas are already drawn ({self.parent().po.sample_number})")
787
+ self.saved_coord = []
788
+ else:
789
+ self.saved_coord.append([event.pos().y(), event.pos().x()])
790
+ self.thread["UpdateImage"].start()
791
+ self.thread["UpdateImage"].message_when_thread_finished.connect(self.user_defined_shape_displayed)
792
+ self.hold_click_flag = False
793
+
794
+
795
+ # if not self.is_image_analysis_display_running:
796
+ # if self.back1_bio2 > 0 or self.manual_delineation_flag:
797
+ # if len(self.saved_coord) > 0 and self.saved_coord[0][0] != event.pos().y() and self.saved_coord[0][1] != event.pos().x():
798
+ # if self.thread["UpdateImage"].isRunning():
799
+ # self.thread["UpdateImage"].wait()
800
+ # self.temporary_mask_coord = []
801
+ # if self.manual_delineation_flag and len(self.parent().imageanalysiswindow.available_arena_names) == 0:
802
+ # self.message.setText(f"The total number of arenas are already drawn ({self.parent().po.sample_number})")
803
+ # self.saved_coord = []
804
+ # else:
805
+ # self.saved_coord.append([event.pos().y(), event.pos().x()])
806
+ # self.thread["UpdateImage"].start()
807
+ # self.thread["UpdateImage"].message_when_thread_finished.connect(self.user_defined_shape_displayed)
808
+
809
+ def user_defined_shape_displayed(self, boole):
810
+ if self.back1_bio2 == 1:
811
+ back_name = self.parent().imageanalysiswindow.available_back_names[0]
812
+ self.back_lines[back_name] = {}
813
+ pbutton_name = u"\u00D7" + " Back" + str(back_name)
814
+ self.back_lines[back_name][pbutton_name] = self.new_pbutton_on_the_left(pbutton_name)
815
+ self.back_added_lines_layout.addWidget(self.back_lines[back_name][pbutton_name])
816
+ self.background.night_mode_switch(night_mode=self.parent().po.all['night_mode'])
817
+ self.available_back_names = self.available_back_names[1:]
818
+ elif self.back1_bio2 == 2:
819
+ bio_name = self.parent().imageanalysiswindow.available_bio_names[0]
820
+ self.bio_lines[bio_name] = {}
821
+ pbutton_name = u"\u00D7" + " Cell" + str(bio_name)
822
+ self.bio_lines[bio_name][pbutton_name] = self.new_pbutton_on_the_left(pbutton_name)
823
+ self.bio_added_lines_layout.addWidget(self.bio_lines[bio_name][pbutton_name])
824
+ self.cell.night_mode_switch(night_mode=self.parent().po.all['night_mode'])
825
+ self.available_bio_names = self.available_bio_names[1:]
826
+ if self.bio_masks_number == 0:
827
+ self.display_more_than_two_colors_option()
828
+
829
+ self.more_than_two_colors.setVisible(self.advanced_mode_cb.isChecked())
830
+ self.more_than_two_colors_label.setVisible(self.advanced_mode_cb.isChecked())
831
+ self.distinct_colors_number.setVisible(self.advanced_mode_cb.isChecked() and self.more_than_two_colors.isChecked())
832
+ elif self.manual_delineation_flag:
833
+ arena_name = self.parent().imageanalysiswindow.available_arena_names[0]
834
+ self.arena_lines[arena_name] = {}
835
+ pbutton_name = u"\u00D7" + " Arena" + str(arena_name)
836
+ self.arena_lines[arena_name][pbutton_name] = self.new_pbutton_on_the_left(pbutton_name)
837
+ if self.arena_masks_number % 2 == 1:
838
+ self.bio_added_lines_layout.addWidget(self.arena_lines[arena_name][pbutton_name])
839
+ else:
840
+ self.back_added_lines_layout.addWidget(self.arena_lines[arena_name][pbutton_name])
841
+ self.available_arena_names = self.available_arena_names[1:]
842
+ self.saved_coord = []
843
+ self.back1_bio2 = 0
844
+ try:
845
+ self.thread["UpdateImage"].message_when_thread_finished.disconnect()
846
+ except RuntimeError:
847
+ pass
848
+
849
+ def new_pbutton_on_the_left(self, pbutton_name):
850
+ pbutton = PButton(pbutton_name, False, night_mode=self.parent().po.all['night_mode'])
851
+ pbutton.setFixedHeight(20)
852
+ pbutton.setFixedWidth(100)
853
+ pbutton.setFont(QtGui.QFont("Segoe UI Semibold", 8, QtGui.QFont.Thin))
854
+ pbutton.textcolor("rgb(0, 0, 0)")
855
+ pbutton.border("0px")
856
+ pbutton.angles("10px")
857
+ if self.back1_bio2 == 1:
858
+ pbutton.color("rgb(81, 160, 224)")
859
+ elif self.back1_bio2 == 2:
860
+ pbutton.color("rgb(230, 145, 18)")
861
+ else:
862
+ pbutton.color("rgb(126, 126, 126)")
863
+ pbutton.clicked.connect(self.remove_line)
864
+ return pbutton
865
+
866
+ def remove_line(self):
867
+ if not self.is_image_analysis_display_running and not self.thread["UpdateImage"].isRunning() and hasattr(self.sender(), 'text'):
868
+ pbutton_name = self.sender().text()
869
+ if pbutton_name[2:6] == "Back":
870
+ line_name = np.uint8(pbutton_name[6:])
871
+ self.back_mask[self.back_mask == line_name] = 0
872
+ self.back_added_lines_layout.removeWidget(self.back_lines[line_name][pbutton_name])
873
+ self.back_lines[line_name][pbutton_name].deleteLater()
874
+ self.back_lines.pop(line_name)
875
+ self.back_masks_number -= 1
876
+ self.available_back_names = np.sort(np.concatenate(([line_name], self.available_back_names)))
877
+ # self.back_pbuttons_table.removeRow(line_name - 1)
878
+ elif pbutton_name[2:6] == "Cell":
879
+ line_name = np.uint8(pbutton_name[6:])
880
+ self.bio_mask[self.bio_mask == line_name] = 0
881
+ self.bio_added_lines_layout.removeWidget(self.bio_lines[line_name][pbutton_name])
882
+ self.bio_lines[line_name][pbutton_name].deleteLater()
883
+ self.bio_lines.pop(line_name)
884
+ self.bio_masks_number -= 1
885
+ self.available_bio_names = np.sort(np.concatenate(([line_name], self.available_bio_names)))
886
+ # self.bio_pbuttons_table.removeRow(line_name - 1)
887
+ self.display_more_than_two_colors_option()
888
+ else:
889
+ line_name = np.uint8(pbutton_name[7:])
890
+ self.arena_mask[self.arena_mask == line_name] = 0
891
+ if line_name % 2 == 1:
892
+ self.bio_added_lines_layout.removeWidget(self.arena_lines[line_name][pbutton_name])
893
+ else:
894
+ self.back_added_lines_layout.removeWidget(self.arena_lines[line_name][pbutton_name])
895
+ self.arena_lines[line_name][pbutton_name].deleteLater()
896
+ self.arena_lines.pop(line_name)
897
+
898
+ self.arena_masks_number -= 1
899
+ self.available_arena_names = np.sort(np.concatenate(([line_name], self.available_arena_names)))
900
+ # if line_name % 2 == 1:
901
+ # self.bio_pbuttons_table.removeRow((line_name + 1) // 2)
902
+ # else:
903
+ # self.back_pbuttons_table.removeRow((line_name + 1) // 2)
904
+ # if self.parent().po.first_image.im_combinations is not None:
905
+ # if self.thread["UpdateImage"].isRunning():
906
+ # self.thread["UpdateImage"].wait()
907
+ self.thread["UpdateImage"].start()
908
+
909
+ def quickly_is_clicked(self):
910
+ if not self.is_image_analysis_running:
911
+ self.is_image_analysis_running = True
912
+ self.message.setText('Loading, wait...')
913
+ self.parent().po.carefully = False
914
+ self.parent().po.visualize = False
915
+ if self.is_first_image_flag:
916
+ self.run_first_image_analysis()
917
+ else:
918
+ self.run_last_image_analysis()
919
+
920
+ def carefully_is_clicked(self):
921
+ if not self.is_image_analysis_running:
922
+ self.is_image_analysis_running = True
923
+ self.message.setText('Loading, wait...')
924
+ self.parent().po.carefully = True
925
+ self.parent().po.visualize = False
926
+ if self.is_first_image_flag:
927
+ self.run_first_image_analysis()
928
+ else:
929
+ self.run_last_image_analysis()
930
+
931
+ def visualize_is_clicked(self):
932
+ if not self.is_image_analysis_running:
933
+ self.is_image_analysis_running = True
934
+ self.message.setText('Loading, wait...')
935
+ self.parent().po.visualize = True
936
+ # if self.step == 0:
937
+ # self.select_option_label.setVisible(False)
938
+ # self.select_option.setVisible(False)
939
+ if self.is_first_image_flag:
940
+ self.run_first_image_analysis()
941
+ else:
942
+ self.run_last_image_analysis()
943
+
944
+ def run_first_image_analysis(self):
945
+ # logging.info('runfim' +str(self.parent().po.sample_number))
946
+ if self.first_im_parameters_answered:
947
+ # self.sample_number_changed()
948
+ self.several_blob_per_arena_check()
949
+ self.horizontal_size_changed()
950
+ self.spot_shape_changed()
951
+ self.arena_shape_changed()
952
+ logging.info(self.parent().po.sample_number)
953
+ logging.info(self.parent().po.vars['several_blob_per_arena'])
954
+ logging.info(self.parent().po.all['starting_blob_shape'])
955
+ logging.info(self.parent().po.vars['arena_shape'])
956
+
957
+ if self.parent().po.visualize:
958
+ self.save_user_defined_csc()
959
+ self.parent().po.vars["color_number"] = int(self.distinct_colors_number.value())
960
+ if self.csc_dict_is_empty:
961
+ self.message.setText('Choose a color space, modify a channel and visualize')
962
+ self.message.setStyleSheet("color: rgb(230, 145, 18)")
963
+ if not self.parent().po.visualize or not self.csc_dict_is_empty:
964
+ self.parent().po.vars['convert_for_origin'] = deepcopy(self.csc_dict)
965
+ self.thread["FirstImageAnalysis"].start()
966
+ self.thread["FirstImageAnalysis"].message_from_thread.connect(self.display_message_from_thread)
967
+ self.thread["FirstImageAnalysis"].message_when_thread_finished.connect(self.when_image_analysis_finishes)
968
+
969
+ def run_last_image_analysis(self):
970
+ logging.info('runlim')
971
+ if self.parent().po.visualize:
972
+ self.save_user_defined_csc()
973
+ self.parent().po.vars["color_number"] = int(self.distinct_colors_number.value())
974
+ if self.csc_dict_is_empty:
975
+ self.message.setText('Choose a color space, increase a channel and visualize')
976
+ self.message.setStyleSheet("color: rgb(230, 145, 18)")
977
+ else:
978
+ self.parent().po.vars['convert_for_motion'] = deepcopy(self.csc_dict)
979
+ self.thread["LastImageAnalysis"].start()
980
+ self.thread["LastImageAnalysis"].message_from_thread.connect(self.display_message_from_thread)
981
+ self.thread["LastImageAnalysis"].message_when_thread_finished.connect(
982
+ self.when_image_analysis_finishes)
983
+ else:
984
+ self.thread["LastImageAnalysis"].start()
985
+ self.thread["LastImageAnalysis"].message_from_thread.connect(self.display_message_from_thread)
986
+ self.thread["LastImageAnalysis"].message_when_thread_finished.connect(self.when_image_analysis_finishes)
987
+
988
+ def when_image_analysis_finishes(self):
989
+ logging.info('im_finish' + str(self.parent().po.sample_number))
990
+
991
+ if self.parent().po.visualize:
992
+ if self.parent().po.current_combination_id != self.select_option.currentIndex():
993
+ self.select_option.setCurrentIndex(self.parent().po.current_combination_id)
994
+ else:
995
+ self.parent().po.current_combination_id = 0
996
+ if self.is_first_image_flag:
997
+ im_combinations = self.parent().po.first_image.im_combinations
998
+ else:
999
+ im_combinations = self.parent().po.last_image.im_combinations
1000
+ if len(im_combinations) > 0:
1001
+ self.csc_dict = im_combinations[self.parent().po.current_combination_id]["csc"]
1002
+
1003
+ if self.is_first_image_flag:
1004
+ self.parent().po.vars['convert_for_origin'] = deepcopy(self.csc_dict)
1005
+ else:
1006
+ self.parent().po.vars['convert_for_motion'] = deepcopy(self.csc_dict)
1007
+ option_number = len(im_combinations)
1008
+
1009
+ if option_number > 1:
1010
+ # Update the available options of the scrolling menu
1011
+ self.select_option.clear()
1012
+ for option in range(option_number):
1013
+ self.select_option.addItem(f"Option {option + 1}")
1014
+ self.update_csc_editing_display()
1015
+ else:
1016
+ self.message.setText("No options could be generated automatically, use the advanced mode")
1017
+
1018
+ if self.parent().po.visualize or len(im_combinations) > 0:
1019
+ self.is_image_analysis_display_running = True
1020
+ # Update image display
1021
+ if self.thread["UpdateImage"].isRunning():
1022
+ self.thread["UpdateImage"].wait()
1023
+ self.thread["UpdateImage"].start()
1024
+ self.thread["UpdateImage"].message_when_thread_finished.connect(self.image_analysis_displayed)
1025
+
1026
+ def image_analysis_displayed(self):
1027
+ color_analysis = not self.parent().po.vars['already_greyscale']
1028
+ self.message.setText("")
1029
+
1030
+
1031
+ if self.step < 2:
1032
+ detected_shape_nb = self.parent().po.first_image.im_combinations[self.parent().po.current_combination_id][
1033
+ 'shape_number']
1034
+ if detected_shape_nb == self.parent().po.sample_number or self.parent().po.vars['several_blob_per_arena']:
1035
+ self.decision_label.setText(
1036
+ f"{detected_shape_nb} distinct spots detected in {self.parent().po.sample_number} arena(s). Does the color match the cell(s)?")
1037
+ if self.step == 1:
1038
+ self.yes.setVisible(True)
1039
+ self.message.setText("If not, draw more Cell and Back ellipses on the image and retry")
1040
+ else:
1041
+ if self.no.isVisible():
1042
+ self.decision_label.setText(
1043
+ f"{detected_shape_nb} distinct spots detected in {self.parent().po.sample_number} arena(s). Click Yes when satisfied, Click No to fill in more parameters")
1044
+ self.yes.setVisible(True)
1045
+ self.no.setVisible(True)
1046
+ else:
1047
+ self.decision_label.setText(
1048
+ f"{detected_shape_nb} distinct spots detected in {self.parent().po.sample_number} arena(s). Click Yes when satisfied")
1049
+ self.yes.setVisible(True)
1050
+
1051
+ if self.parent().po.vars['several_blob_per_arena'] and (detected_shape_nb == self.parent().po.sample_number):
1052
+ self.message.setText("Beware: Contrary to what has been checked, there is one spot per arena")
1053
+
1054
+ if not self.parent().po.visualize:
1055
+ self.select_option.setVisible(color_analysis)
1056
+ self.select_option_label.setVisible(color_analysis)
1057
+ if self.step == 0:
1058
+ # self.decision_label.setText(f"Does the color correctly cover the cells? And, is {detected_shape_nb} the number of distinct arenas?")
1059
+ if self.parent().po.first_image.im_combinations[self.parent().po.current_combination_id]['shape_number'] == 0:
1060
+ self.message.setText("Make sure that scaling metric and spot size are correct")
1061
+ self.decision_label.setVisible(True)
1062
+ self.yes.setVisible(True)
1063
+ self.no.setVisible(True)
1064
+ self.arena_shape.setVisible(True)
1065
+ self.arena_shape_label.setVisible(True)
1066
+ # self.select_option_label.setVisible(color_analysis)
1067
+ # self.select_option.setVisible(color_analysis)
1068
+ self.n_shapes_detected.setVisible(True)
1069
+
1070
+ elif self.step == 2:
1071
+ self.generate_analysis_options.setVisible(color_analysis)
1072
+ self.quickly.setVisible(color_analysis)
1073
+ self.carefully.setVisible(color_analysis)
1074
+ self.visualize.setVisible(True)
1075
+
1076
+ self.decision_label.setText("Click next when color delimits the cell(s) correctly")
1077
+ self.yes.setVisible(False)
1078
+ self.no.setVisible(False)
1079
+ self.message.setText('When the resulting segmentation of the last image seems good, click next.')
1080
+ self.next.setVisible(True)
1081
+
1082
+ try:
1083
+ self.thread["UpdateImage"].message_when_thread_finished.disconnect()
1084
+ except RuntimeError:
1085
+ pass
1086
+ self.is_image_analysis_running = False
1087
+ self.is_image_analysis_display_running = False
1088
+
1089
+ def option_changed(self):
1090
+ """
1091
+ Save the csc, change the image displayed, the csc editing
1092
+ :return:
1093
+ """
1094
+ # Update the current image
1095
+ if self.is_first_image_flag:
1096
+ im_combinations = self.parent().po.first_image.im_combinations
1097
+ else:
1098
+ im_combinations = self.parent().po.last_image.im_combinations
1099
+ self.parent().po.current_combination_id = self.select_option.currentIndex()
1100
+ logging.info(im_combinations is None)
1101
+ if im_combinations is not None and len(im_combinations) > 0:
1102
+ if self.parent().po.current_combination_id + 1 > len(im_combinations):
1103
+ self.parent().po.current_combination_id = 0
1104
+ self.csc_dict = im_combinations[self.parent().po.current_combination_id]["csc"]
1105
+ self.parent().po.current_image = np.stack((im_combinations[self.parent().po.current_combination_id]['converted_image'],
1106
+ im_combinations[self.parent().po.current_combination_id]['converted_image'],
1107
+ im_combinations[self.parent().po.current_combination_id]['converted_image']), axis=2)
1108
+ self.drawn_image = deepcopy(self.parent().po.current_image)
1109
+
1110
+ # Update image display
1111
+ if self.thread["UpdateImage"].isRunning():
1112
+ self.thread["UpdateImage"].wait()
1113
+ self.thread["UpdateImage"].start()
1114
+ # Update csc editing
1115
+ self.update_csc_editing_display()
1116
+
1117
+ # Update the detected shape number
1118
+ if self.is_first_image_flag:
1119
+ self.parent().po.vars['convert_for_origin'] = im_combinations[self.parent().po.current_combination_id]["csc"]
1120
+ detected_shape_nb = \
1121
+ self.parent().po.first_image.im_combinations[self.parent().po.current_combination_id]['shape_number']
1122
+ if self.parent().po.vars['several_blob_per_arena']:
1123
+ if detected_shape_nb == self.parent().po.sample_number:
1124
+ self.message.setText("Beware: Contrary to what has been checked, there is one spot per arena")
1125
+ else:
1126
+ if detected_shape_nb == self.parent().po.sample_number:
1127
+ self.decision_label.setText(
1128
+ f"{detected_shape_nb} distinct spots detected in {self.parent().po.sample_number} arena(s). Does the color match the cell(s)?")
1129
+ self.yes.setVisible(True)
1130
+ else:
1131
+ self.decision_label.setText(
1132
+ f"{detected_shape_nb} distinct spots detected in {self.parent().po.sample_number} arena(s). Adjust settings, draw more cells and background, and try again")
1133
+ self.yes.setVisible(False)
1134
+ # self.decision_label.setText(f"Does the color correctly cover the cells?")
1135
+ # detected_shape_nb = \
1136
+ # self.parent().po.first_image.im_combinations[self.parent().po.current_combination_id]['shape_number']
1137
+ # self.decision_label.setText(
1138
+ # f"Does the color correctly cover the cells? And, is {detected_shape_nb} the number of distinct arenas?")
1139
+ if self.parent().po.first_image.im_combinations[self.parent().po.current_combination_id]['shape_number'] == 0:
1140
+ self.message.setText("Make sure that scaling metric and spot size are correct")
1141
+ else:
1142
+ self.parent().po.vars['convert_for_motion'] = im_combinations[self.parent().po.current_combination_id]["csc"]
1143
+ self.decision_label.setText("Do colored contours correctly match cell(s) contours?")
1144
+
1145
+ def generate_csc_editing(self):
1146
+ # self.edit_layout = QtWidgets.QGridLayout()
1147
+ self.edit_widget = QtWidgets.QWidget()
1148
+ self.edit_layout = QtWidgets.QVBoxLayout()
1149
+
1150
+ # 1) Advanced mode option
1151
+ self.advanced_mode_widget = QtWidgets.QWidget()
1152
+ self.advanced_mode_layout = QtWidgets.QHBoxLayout()
1153
+ self.advanced_mode_cb = Checkbox(self.parent().po.all['expert_mode'])
1154
+ self.advanced_mode_cb.setStyleSheet("margin-left:0%; margin-right:0%;")
1155
+ self.advanced_mode_cb.stateChanged.connect(self.advanced_mode_check)
1156
+ self.advanced_mode_label = FixedText('Advanced mode', halign='l',
1157
+ tip="Display the color space combination corresponding to the selected option",
1158
+ night_mode=self.parent().po.all['night_mode'])
1159
+ self.advanced_mode_label.setAlignment(QtCore.Qt.AlignTop)
1160
+ self.advanced_mode_layout.addWidget(self.advanced_mode_cb)
1161
+ self.advanced_mode_layout.addWidget(self.advanced_mode_label)
1162
+ self.advanced_mode_layout.addItem(self.horizontal_space)
1163
+ self.advanced_mode_widget.setLayout(self.advanced_mode_layout)
1164
+ self.edit_layout.addWidget(self.advanced_mode_widget)
1165
+
1166
+ self.csc_scroll_table = QtWidgets.QScrollArea() # QTableWidget() # Scroll Area which contains the widgets, set as the centralWidget
1167
+ # self.csc_scroll_table.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
1168
+ self.csc_scroll_table.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
1169
+ self.csc_scroll_table.setMinimumHeight(self.parent().im_max_height - 100)
1170
+ # self.csc_scroll_table.setMinimumWidth(300)
1171
+ self.csc_scroll_table.setFrameShape(QtWidgets.QFrame.NoFrame)
1172
+ self.csc_scroll_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
1173
+ self.csc_scroll_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
1174
+ self.csc_table_widget = QtWidgets.QWidget()
1175
+ self.csc_table_layout = QtWidgets.QVBoxLayout()
1176
+
1177
+ # 2) Titles
1178
+ self.edit_labels_widget = QtWidgets.QWidget()
1179
+ self.edit_labels_layout = QtWidgets.QHBoxLayout()
1180
+ self.space_label = FixedText('Space', halign='l',
1181
+ tip="Color spaces are transformations of the original BGR (Blue Green Red) image\nInstead of defining an image by 3 colors,\n they transform it into 3 different visual properties\n - hsv: hue (color), saturation, value (lightness)\n - hls: hue (color), lightness, saturation\n - lab: Lightness, Red/Green, Blue/Yellow\n - luv and yuv: l and y are Lightness, u and v are related to colors\n",
1182
+ night_mode=self.parent().po.all['night_mode'])
1183
+ self.c1 = FixedText(' C1', halign='c', tip="Increase if it increase cell detection", night_mode=self.parent().po.all['night_mode'])
1184
+ self.c2 = FixedText(' C2', halign='c', tip="Increase if it increase cell detection", night_mode=self.parent().po.all['night_mode'])
1185
+ self.c3 = FixedText(' C3', halign='c', tip="Increase if it increase cell detection", night_mode=self.parent().po.all['night_mode'])
1186
+
1187
+ self.edit_labels_layout.addWidget(self.space_label)
1188
+ self.edit_labels_layout.addWidget(self.c1)
1189
+ self.edit_labels_layout.addWidget(self.c2)
1190
+ self.edit_labels_layout.addWidget(self.c3)
1191
+ self.edit_labels_layout.addItem(self.horizontal_space)
1192
+ self.space_label.setVisible(False)
1193
+ self.c1.setVisible(False)
1194
+ self.c2.setVisible(False)
1195
+ self.c3.setVisible(False)
1196
+ self.edit_labels_widget.setLayout(self.edit_labels_layout)
1197
+ # self.edit_layout.addWidget(self.edit_labels_widget)
1198
+ self.csc_table_layout.addWidget(self.edit_labels_widget)
1199
+
1200
+ # 3) First CSC
1201
+ self.first_csc_widget = QtWidgets.QWidget()
1202
+ self.first_csc_layout = QtWidgets.QGridLayout()
1203
+ self.row1 = self.one_csc_editing()
1204
+ self.row1[4].clicked.connect(self.display_row2)
1205
+ self.row2 = self.one_csc_editing()
1206
+ self.row2[4].clicked.connect(self.display_row3)
1207
+ self.row3 = self.one_csc_editing()# Second CSC
1208
+ self.logical_operator_between_combination_result = Combobox(["None", "Or", "And", "Xor"],
1209
+ night_mode=self.parent().po.all['night_mode'])
1210
+ self.logical_operator_between_combination_result.setCurrentText(self.parent().po.vars['convert_for_motion']['logical'])
1211
+ self.logical_operator_between_combination_result.currentTextChanged.connect(self.logical_op_changed)
1212
+ self.logical_operator_between_combination_result.setFixedWidth(100)
1213
+ # self.logical_operator_between_combination_result.cha
1214
+ self.logical_operator_label = FixedText("Logical operator", tip="Between selected color space combinations",
1215
+ night_mode=self.parent().po.all['night_mode'])
1216
+
1217
+ self.row21 = self.one_csc_editing()
1218
+ self.row21[4].clicked.connect(self.display_row22)
1219
+ self.row22 = self.one_csc_editing()
1220
+ self.row22[4].clicked.connect(self.display_row23)
1221
+ self.row23 = self.one_csc_editing()
1222
+ if self.csc_dict is not None:
1223
+ self.update_csc_editing_display()
1224
+ else:
1225
+ self.row1[0].setCurrentIndex(4)
1226
+ self.row1[3].setValue(1)
1227
+ self.row21[0].setCurrentIndex(0)
1228
+ self.row21[3].setValue(0)
1229
+
1230
+ for i in range(5):
1231
+ self.first_csc_layout.addWidget(self.row1[i], 0, i, 1, 1)
1232
+ self.first_csc_layout.addWidget(self.row2[i], 1, i, 1, 1)
1233
+ self.first_csc_layout.addWidget(self.row3[i], 2, i, 1, 1)
1234
+ self.row1[i].setVisible(False)
1235
+ self.row2[i].setVisible(False)
1236
+ self.row3[i].setVisible(False)
1237
+ self.first_csc_layout.setHorizontalSpacing(0)
1238
+ self.first_csc_layout.addItem(self.horizontal_space, 0, 5, 3, 1)
1239
+ self.first_csc_widget.setLayout(self.first_csc_layout)
1240
+ self.csc_table_layout.addWidget(self.first_csc_widget)
1241
+ # self.edit_layout.addWidget(self.first_csc_widget)
1242
+
1243
+ # 4) logical_operator
1244
+ self.logical_op_widget = QtWidgets.QWidget()
1245
+ self.logical_op_layout = QtWidgets.QHBoxLayout()
1246
+ self.logical_op_layout.addWidget(self.logical_operator_label)
1247
+ self.logical_op_layout.addWidget(self.logical_operator_between_combination_result)
1248
+ self.logical_op_layout.addItem(self.horizontal_space)
1249
+ self.logical_operator_between_combination_result.setVisible(False)
1250
+ self.logical_operator_label.setVisible(False)
1251
+ self.logical_op_widget.setLayout(self.logical_op_layout)
1252
+ self.csc_table_layout.addWidget(self.logical_op_widget)
1253
+ # self.edit_layout.addWidget(self.logical_op_widget)
1254
+
1255
+ # 5) Second CSC
1256
+ self.second_csc_widget = QtWidgets.QWidget()
1257
+ self.second_csc_layout = QtWidgets.QGridLayout()
1258
+ for i in range(5):
1259
+ self.second_csc_layout.addWidget(self.row21[i], 0, i, 1, 1)
1260
+ self.second_csc_layout.addWidget(self.row22[i], 1, i, 1, 1)
1261
+ self.second_csc_layout.addWidget(self.row23[i], 2, i, 1, 1)
1262
+ self.row21[i].setVisible(False)
1263
+ self.row22[i].setVisible(False)
1264
+ self.row23[i].setVisible(False)
1265
+ self.second_csc_layout.setHorizontalSpacing(0)
1266
+ self.second_csc_layout.addItem(self.horizontal_space, 0, 5, 3, 1)
1267
+ self.second_csc_widget.setLayout(self.second_csc_layout)
1268
+ self.csc_table_layout.addWidget(self.second_csc_widget)
1269
+
1270
+ self.csc_table_widget.setLayout(self.csc_table_layout)
1271
+ self.csc_scroll_table.setWidget(self.csc_table_widget)
1272
+ self.csc_scroll_table.setWidgetResizable(True)
1273
+ # self.edit_layout.addWidget(self.second_csc_widget)
1274
+ self.edit_layout.addWidget(self.csc_scroll_table)
1275
+ self.edit_layout.addItem(self.vertical_space)
1276
+
1277
+ # 6) Open the grid_segmentation row layout
1278
+ self.grid_segmentation_widget = QtWidgets.QWidget()
1279
+ self.grid_segmentation_layout = QtWidgets.QHBoxLayout()
1280
+ try:
1281
+ self.parent().po.vars["grid_segmentation"]
1282
+ except KeyError:
1283
+ self.parent().po.vars["grid_segmentation"] = False
1284
+ self.grid_segmentation = Checkbox(self.parent().po.vars["grid_segmentation"])
1285
+ self.grid_segmentation.setStyleSheet("margin-left:0%; margin-right:-10%;")
1286
+ self.grid_segmentation.stateChanged.connect(self.grid_segmentation_option)
1287
+
1288
+ self.grid_segmentation_label = FixedText("Grid segmentation",
1289
+ tip="Segment small squares of the images to detect local intensity valleys\nThis method segment the image locally using otsu thresholding on a rolling window", night_mode=self.parent().po.all['night_mode'])
1290
+ self.grid_segmentation_label.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
1291
+ # self.more_than_two_colors_label.setFixedWidth(300)
1292
+ self.grid_segmentation_label.setAlignment(QtCore.Qt.AlignLeft)
1293
+
1294
+ self.grid_segmentation_layout.addWidget(self.grid_segmentation)
1295
+ self.grid_segmentation_layout.addWidget(self.grid_segmentation_label)
1296
+ self.grid_segmentation_layout.addItem(self.horizontal_space)
1297
+ self.grid_segmentation_widget.setLayout(self.grid_segmentation_layout)
1298
+ self.edit_layout.addWidget(self.grid_segmentation_widget)
1299
+
1300
+ # 6) Open the more_than_2_colors row layout
1301
+ self.more_than_2_colors_widget = QtWidgets.QWidget()
1302
+ self.more_than_2_colors_layout = QtWidgets.QHBoxLayout()
1303
+ self.more_than_two_colors = Checkbox(self.parent().po.all["more_than_two_colors"])
1304
+ self.more_than_two_colors.setStyleSheet("margin-left:0%; margin-right:-10%;")
1305
+ self.more_than_two_colors.stateChanged.connect(self.display_more_than_two_colors_option)
1306
+
1307
+ self.more_than_two_colors_label = FixedText("More than two colors",
1308
+ tip="The program will split the image into categories\nand find the one corresponding to the cell(s)", night_mode=self.parent().po.all['night_mode'])
1309
+ self.more_than_two_colors_label.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
1310
+ # self.more_than_two_colors_label.setFixedWidth(300)
1311
+ self.more_than_two_colors_label.setAlignment(QtCore.Qt.AlignLeft)
1312
+ self.distinct_colors_number = Spinbox(min=2, max=5, val=self.parent().po.vars["color_number"], night_mode=self.parent().po.all['night_mode'])
1313
+
1314
+ self.distinct_colors_number.valueChanged.connect(self.distinct_colors_number_changed)
1315
+ self.display_more_than_two_colors_option()
1316
+ self.more_than_two_colors.setVisible(False)
1317
+ self.more_than_two_colors_label.setVisible(False)
1318
+ self.distinct_colors_number.setVisible(False)
1319
+ self.grid_segmentation.setVisible(False)
1320
+ self.grid_segmentation_label.setVisible(False)
1321
+
1322
+ self.more_than_2_colors_layout.addWidget(self.more_than_two_colors)
1323
+ self.more_than_2_colors_layout.addWidget(self.more_than_two_colors_label)
1324
+ self.more_than_2_colors_layout.addWidget(self.distinct_colors_number)
1325
+ self.more_than_2_colors_layout.addItem(self.horizontal_space)
1326
+ self.more_than_2_colors_widget.setLayout(self.more_than_2_colors_layout)
1327
+ self.edit_layout.addWidget(self.more_than_2_colors_widget)
1328
+
1329
+ self.edit_widget.setLayout(self.edit_layout)
1330
+
1331
+ def one_csc_editing(self):
1332
+ widget_list = []
1333
+ widget_list.insert(0, Combobox(["None", "bgr", "hsv", "hls", "lab", "luv", "yuv"],
1334
+ night_mode=self.parent().po.all['night_mode']))
1335
+ widget_list[0].setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
1336
+ widget_list[0].setFixedWidth(100)
1337
+ for i in [1, 2, 3]:
1338
+ widget_list.insert(i, Spinbox(min=-126, max=126, val=0, night_mode=self.parent().po.all['night_mode']))
1339
+ widget_list[i].setFixedWidth(45)
1340
+ widget_list.insert(i + 1, PButton("+", night_mode=self.parent().po.all['night_mode']))
1341
+
1342
+ return widget_list
1343
+
1344
+ def logical_op_changed(self):
1345
+ # show = self.logical_operator_between_combination_result.currentText() != 'None'
1346
+ if self.logical_operator_between_combination_result.currentText() == 'None':
1347
+ self.row21[0].setVisible(False)
1348
+ self.row21[0].setCurrentIndex(0)
1349
+ for i1 in [1, 2, 3]:
1350
+ self.row21[i1].setVisible(False)
1351
+ self.row21[i1].setValue(0)
1352
+ self.row21[i1 + 1].setVisible(False)
1353
+
1354
+ self.row22[0].setVisible(False)
1355
+ self.row22[0].setCurrentIndex(0)
1356
+ for i1 in [1, 2, 3]:
1357
+ self.row22[i1].setVisible(False)
1358
+ self.row22[i1].setValue(0)
1359
+ self.row22[i1 + 1].setVisible(False)
1360
+
1361
+ self.row23[0].setVisible(False)
1362
+ self.row23[0].setCurrentIndex(0)
1363
+ for i1 in [1, 2, 3]:
1364
+ self.row23[i1].setVisible(False)
1365
+ self.row23[i1].setValue(0)
1366
+ self.row23[i1 + 1].setVisible(False)
1367
+ else:
1368
+ self.row21[0].setVisible(self.parent().po.all['expert_mode'])
1369
+ for i1 in [1, 2, 3]:
1370
+ self.row21[i1].setVisible(self.parent().po.all['expert_mode'])
1371
+ self.row21[i1 + 1].setVisible(self.parent().po.all['expert_mode'])
1372
+
1373
+ def display_logical_operator(self):
1374
+ self.logical_operator_between_combination_result.setVisible(self.parent().po.all['expert_mode'])
1375
+ self.logical_operator_label.setVisible(self.parent().po.all['expert_mode'])
1376
+
1377
+ def display_row2(self):
1378
+ self.row1[4].setVisible(False)
1379
+ for i in range(5):
1380
+ self.row2[i].setVisible(self.parent().po.all['expert_mode'])
1381
+ self.display_logical_operator()
1382
+
1383
+ def display_row3(self):
1384
+ self.row2[4].setVisible(False)
1385
+ for i in range(4):
1386
+ self.row3[i].setVisible(self.parent().po.all['expert_mode'])
1387
+ self.display_logical_operator()
1388
+
1389
+ def display_row22(self):
1390
+ self.row21[4].setVisible(False)
1391
+ for i in range(5):
1392
+ self.row22[i].setVisible(self.parent().po.all['expert_mode'])
1393
+ self.display_logical_operator()
1394
+
1395
+ def display_row23(self):
1396
+ self.row22[4].setVisible(False)
1397
+ for i in range(4):
1398
+ self.row23[i].setVisible(self.parent().po.all['expert_mode'])
1399
+ self.display_logical_operator()
1400
+
1401
+ def update_csc_editing_display(self):
1402
+ c_space_order = ["None", "bgr", "hsv", "hls", "lab", "luv", "yuv"]
1403
+ remaining_c_spaces = []
1404
+ row_number1 = 0
1405
+ row_number2 = 0
1406
+ for i, (k, v) in enumerate(self.csc_dict.items()):
1407
+ if k != "logical":
1408
+ if k[-1] != "2":
1409
+ if row_number1 == 0:
1410
+ row_to_change = self.row1
1411
+ elif row_number1 == 1:
1412
+ row_to_change = self.row2
1413
+ elif row_number1 == 2:
1414
+ row_to_change = self.row3
1415
+ else:
1416
+ remaining_c_spaces.append(k + " " + str(v))
1417
+ row_number1 += 1
1418
+ current_row_number = row_number1
1419
+ else:
1420
+ if row_number2 == 0:
1421
+ row_to_change = self.row21
1422
+ elif row_number2 == 1:
1423
+ row_to_change = self.row22
1424
+ elif row_number2 == 2:
1425
+ row_to_change = self.row23
1426
+ else:
1427
+ remaining_c_spaces.append(k + " " + str(v))
1428
+ row_number2 += 1
1429
+ current_row_number = row_number2
1430
+ k = k[:-1]
1431
+ if current_row_number <= 3:
1432
+ row_to_change[0].setCurrentIndex(np.nonzero(np.isin(c_space_order, k))[0][0])
1433
+ row_to_change[0].setVisible(self.parent().po.all['expert_mode'])
1434
+ for i1, i2 in zip([1, 2, 3], [0, 1, 2]):
1435
+ row_to_change[i1].setValue(v[i2])
1436
+ row_to_change[i1].setVisible(self.parent().po.all['expert_mode'])
1437
+ if current_row_number < 3:
1438
+ row_to_change[i1 + 1].setVisible(self.parent().po.all['expert_mode'])
1439
+
1440
+ # If not all color space combinations are filled, put None and 0 in boxes
1441
+ if row_number1 < 3:
1442
+ self.row3[0].setVisible(False)
1443
+ self.row3[0].setCurrentIndex(0)
1444
+ for i1 in [1, 2, 3]:
1445
+ self.row3[i1].setVisible(False)
1446
+ self.row3[i1].setValue(0)
1447
+ if row_number1 < 2:
1448
+ self.row2[0].setVisible(False)
1449
+ self.row2[0].setCurrentIndex(0)
1450
+ for i1 in [1, 2, 3]:
1451
+ self.row2[i1].setVisible(False)
1452
+ self.row2[i1].setValue(0)
1453
+ self.row2[i1 + 1].setVisible(False)
1454
+
1455
+ self.row1[4].setVisible(self.parent().po.all['expert_mode'] and row_number1 == 1)
1456
+ self.row2[4].setVisible(self.parent().po.all['expert_mode'] and row_number1 == 2)
1457
+ self.row21[4].setVisible(self.parent().po.all['expert_mode'] and row_number2 == 1)
1458
+ self.row22[4].setVisible(self.parent().po.all['expert_mode'] and row_number2 == 2)
1459
+ if row_number2 > 0:
1460
+ self.logical_operator_between_combination_result.setCurrentText(self.csc_dict['logical'])
1461
+ if row_number2 == 0:
1462
+ self.logical_operator_between_combination_result.setCurrentText('None')
1463
+ self.logical_operator_between_combination_result.setVisible(False)
1464
+ self.logical_operator_label.setVisible(False)
1465
+ self.row21[0].setVisible(False)
1466
+ self.row21[0].setCurrentIndex(0)
1467
+ for i1 in [1, 2, 3]:
1468
+ self.row21[i1].setVisible(False)
1469
+ self.row21[i1].setValue(0)
1470
+ self.row21[i1 + 1].setVisible(False)
1471
+
1472
+ self.logical_operator_between_combination_result.setVisible((row_number2 > 0) and self.parent().po.all['expert_mode'])
1473
+ self.logical_operator_label.setVisible((row_number2 > 0) and self.parent().po.all['expert_mode'])
1474
+
1475
+ if row_number2 < 3:
1476
+ self.row23[0].setVisible(False)
1477
+ self.row23[0].setCurrentIndex(0)
1478
+ for i1 in [1, 2, 3]:
1479
+ self.row23[i1].setVisible(False)
1480
+ self.row23[i1].setValue(0)
1481
+ self.row23[i1 + 1].setVisible(False)
1482
+ self.row22[4].setVisible(False)
1483
+ if row_number2 < 2:
1484
+ self.row22[0].setVisible(False)
1485
+ self.row22[0].setCurrentIndex(0)
1486
+ for i1 in [1, 2, 3]:
1487
+ self.row22[i1].setVisible(False)
1488
+ self.row22[i1].setValue(0)
1489
+ self.row22[i1 + 1].setVisible(False)
1490
+
1491
+ if self.advanced_mode_cb.isChecked():
1492
+ if len(remaining_c_spaces) > 0:
1493
+ self.message.setText(f'Combination also includes {remaining_c_spaces}')
1494
+ self.message.setStyleSheet("color: rgb(230, 145, 18)")
1495
+ else:
1496
+ self.message.setText(f'')
1497
+
1498
+ def save_user_defined_csc(self):
1499
+ self.csc_dict = {}
1500
+ spaces = np.array((self.row1[0].currentText(), self.row2[0].currentText(), self.row3[0].currentText()))
1501
+ channels = np.array(
1502
+ ((self.row1[1].value(), self.row1[2].value(), self.row1[3].value()),
1503
+ (self.row2[1].value(), self.row2[2].value(), self.row2[3].value()),
1504
+ (self.row3[1].value(), self.row3[2].value(), self.row3[3].value()),
1505
+ (self.row21[1].value(), self.row21[2].value(), self.row21[3].value()),
1506
+ (self.row22[1].value(), self.row22[2].value(), self.row22[3].value()),
1507
+ (self.row23[1].value(), self.row23[2].value(), self.row23[3].value())),
1508
+ dtype=np.int8)
1509
+ if self.logical_operator_between_combination_result.currentText() != 'None':
1510
+ spaces = np.concatenate((spaces, np.array((
1511
+ self.row21[0].currentText() + "2", self.row22[0].currentText() + "2",
1512
+ self.row23[0].currentText() + "2"))))
1513
+ channels = np.concatenate((channels, np.array(((self.row21[1].value(), self.row21[2].value(), self.row21[3].value()),
1514
+ (self.row22[1].value(), self.row22[2].value(), self.row22[3].value()),
1515
+ (self.row23[1].value(), self.row23[2].value(), self.row23[3].value())),
1516
+ dtype=np.int8)))
1517
+ self.csc_dict['logical'] = self.logical_operator_between_combination_result.currentText()
1518
+ else:
1519
+ self.csc_dict['logical'] = 'None'
1520
+ if not np.all(spaces == "None"):
1521
+ for i, space in enumerate(spaces):
1522
+ if space != "None" and space != "None2":
1523
+ self.csc_dict[space] = channels[i, :]
1524
+ if len(self.csc_dict) == 1 or channels.sum() == 0:
1525
+ self.csc_dict_is_empty = True
1526
+ else:
1527
+ self.csc_dict_is_empty = False
1528
+
1529
+ def grid_segmentation_option(self):
1530
+ self.parent().po.vars["grid_segmentation"] = self.grid_segmentation.isChecked()
1531
+
1532
+ def display_more_than_two_colors_option(self):
1533
+ """ should not do
1534
+
1535
+ self.parent().po.all["more_than_two_colors"] = self.more_than_two_colors.isChecked()
1536
+ when init
1537
+ """
1538
+ if self.bio_masks_number > 0 and self.advanced_mode_cb.isChecked():
1539
+ self.more_than_two_colors.setVisible(True)
1540
+ self.more_than_two_colors_label.setVisible(True)
1541
+ if self.more_than_two_colors.isChecked():
1542
+ self.distinct_colors_number.setVisible(True)
1543
+ self.more_than_two_colors_label.setText("How many distinct colors?")
1544
+ self.distinct_colors_number.setValue(3)
1545
+ else:
1546
+ self.more_than_two_colors_label.setText("Heterogeneous background")
1547
+ self.distinct_colors_number.setVisible(False)
1548
+ self.distinct_colors_number.setValue(2)
1549
+ self.parent().po.all["more_than_two_colors"] = self.more_than_two_colors.isChecked()
1550
+ else:
1551
+ self.more_than_two_colors.setChecked(False)
1552
+ self.more_than_two_colors.setVisible(False)
1553
+ self.more_than_two_colors_label.setVisible(False)
1554
+ self.distinct_colors_number.setVisible(False)
1555
+ self.distinct_colors_number.setValue(2)
1556
+ # self.parent().po.vars["color_number"] = 2
1557
+
1558
+ def distinct_colors_number_changed(self):
1559
+ self.parent().po.vars["color_number"] = int(self.distinct_colors_number.value())
1560
+
1561
+ def start_crop_scale_subtract_delineate(self):
1562
+ if not self.thread['CropScaleSubtractDelineate'].isRunning():
1563
+ self.message.setText("Looking for each arena contour, wait...")
1564
+ self.thread['CropScaleSubtractDelineate'].start()
1565
+ self.thread['CropScaleSubtractDelineate'].message_from_thread.connect(self.display_message_from_thread)
1566
+ self.thread['CropScaleSubtractDelineate'].message_when_thread_finished.connect(self.delineate_is_done)
1567
+
1568
+ self.yes.setVisible(False)
1569
+ self.no.setVisible(False)
1570
+ # self.times_clicked_yes += 1
1571
+ self.reinitialize_bio_and_back_legend()
1572
+ self.user_drawn_lines_label.setVisible(False)
1573
+ self.cell.setVisible(False)
1574
+ self.background.setVisible(False)
1575
+ # self.sample_number.setVisible(False)
1576
+ # self.sample_number_label.setVisible(False)
1577
+ self.one_blob_per_arena.setVisible(False)
1578
+ self.one_blob_per_arena_label.setVisible(False)
1579
+ self.set_spot_shape.setVisible(False)
1580
+ self.spot_shape.setVisible(False)
1581
+ self.spot_shape_label.setVisible(False)
1582
+ self.set_spot_size.setVisible(False)
1583
+ self.spot_size.setVisible(False)
1584
+ self.spot_size_label.setVisible(False)
1585
+ self.advanced_mode_cb.setChecked(False)
1586
+ self.advanced_mode_cb.setVisible(False)
1587
+ self.advanced_mode_label.setVisible(False)
1588
+ self.generate_analysis_options.setVisible(False)
1589
+ self.quickly.setVisible(False)
1590
+ self.carefully.setVisible(False)
1591
+ self.visualize.setVisible(False)
1592
+ self.visualize_label.setVisible(False)
1593
+ self.select_option.setVisible(False)
1594
+ self.select_option_label.setVisible(False)
1595
+
1596
+ def delineate_is_done(self, message):
1597
+ logging.info("Delineation is done, update GUI")
1598
+ self.message.setText(message)
1599
+ self.arena_shape_label.setVisible(False)
1600
+ self.arena_shape.setVisible(False)
1601
+ self.reinitialize_bio_and_back_legend()
1602
+ self.reinitialize_image_and_masks(self.parent().po.first_image.bgr)
1603
+ self.delineation_done = True
1604
+ if self.thread["UpdateImage"].isRunning():
1605
+ self.thread["UpdateImage"].wait()
1606
+ self.thread["UpdateImage"].start()
1607
+ self.thread["UpdateImage"].message_when_thread_finished.connect(self.automatic_delineation_display_done)
1608
+
1609
+ try:
1610
+ self.thread['CropScaleSubtractDelineate'].message_from_thread.disconnect()
1611
+ self.thread['CropScaleSubtractDelineate'].message_when_thread_finished.disconnect()
1612
+ except RuntimeError:
1613
+ pass
1614
+ if not self.slower_delineation_flag:
1615
+ self.asking_delineation_flag = True
1616
+
1617
+ def automatic_delineation_display_done(self, boole):
1618
+ # Remove this flag to not draw it again next time UpdateImage runs for another reason
1619
+ self.delineation_done = False
1620
+ self.auto_delineation_flag = False
1621
+ self.select_option_label.setVisible(False)
1622
+ self.select_option.setVisible(False)
1623
+
1624
+ self.arena_shape_label.setVisible(True)
1625
+ self.arena_shape.setVisible(True)
1626
+
1627
+ self.decision_label.setText('Is video delineation correct?')
1628
+ self.decision_label.setVisible(True)
1629
+ # self.message.setText('If not, restart the analysis (Previous) or manually draw each arena (No)')
1630
+ self.user_drawn_lines_label.setText('Draw each arena on the image')
1631
+ self.yes.setVisible(True)
1632
+ self.no.setVisible(True)
1633
+ try:
1634
+ self.thread["UpdateImage"].message_when_thread_finished.disconnect()
1635
+ except RuntimeError:
1636
+ pass
1637
+
1638
+ def display_message_from_thread(self, text_from_thread):
1639
+ self.message.setText(text_from_thread)
1640
+
1641
+ def starting_differs_from_growing_check(self):
1642
+ if self.parent().po.all['first_detection_frame'] > 1:
1643
+ self.parent().po.vars['origin_state'] = 'invisible'
1644
+ else:
1645
+ if self.starting_differs_from_growing_cb.isChecked():
1646
+ self.parent().po.vars['origin_state'] = 'constant'
1647
+ else:
1648
+ self.parent().po.vars['origin_state'] = 'fluctuating'
1649
+
1650
+ def when_yes_is_clicked(self):
1651
+ if not self.is_image_analysis_running:
1652
+ # self.message.setText('Loading, wait...')
1653
+ self.decision_tree(True)
1654
+
1655
+ def when_no_is_clicked(self):
1656
+ if not self.is_image_analysis_running:
1657
+ # self.message.setText('Loading, wait...')
1658
+ self.decision_tree(False)
1659
+
1660
+ def decision_tree(self, is_yes):
1661
+ color_analysis = not self.parent().po.vars['already_greyscale']
1662
+ if self.is_first_image_flag:
1663
+ if self.asking_first_im_parameters_flag:
1664
+ # Ask for the right number of distinct arenas, if not add parameters
1665
+ if not is_yes:
1666
+ self.first_im_parameters()
1667
+ else:
1668
+ self.auto_delineation()
1669
+ self.asking_first_im_parameters_flag = False
1670
+
1671
+ elif self.auto_delineation_flag:
1672
+ self.auto_delineation()
1673
+
1674
+ # Is automatic Video delineation correct?
1675
+ elif self.asking_delineation_flag:
1676
+ if not is_yes:
1677
+ self.asking_slower_or_manual_delineation()
1678
+ else:
1679
+ self.last_image_question()
1680
+ self.asking_delineation_flag = False
1681
+
1682
+ # Slower or manual delineation?
1683
+ elif self.asking_slower_or_manual_delineation_flag:
1684
+ if not is_yes:
1685
+ self.manual_delineation()
1686
+ else:
1687
+ self.slower_delineation_flag = True
1688
+ self.slower_delineation()
1689
+ self.asking_slower_or_manual_delineation_flag = False
1690
+
1691
+ # Is slower delineation correct?
1692
+ elif self.slower_delineation_flag:
1693
+ self.yes.setText("Yes")
1694
+ self.no.setText("No")
1695
+ if not is_yes:
1696
+ self.manual_delineation()
1697
+ else:
1698
+ self.last_image_question()
1699
+ self.slower_delineation_flag = False
1700
+
1701
+ elif self.manual_delineation_flag:
1702
+ if is_yes:
1703
+ if self.parent().po.sample_number == self.arena_masks_number:
1704
+ self.thread['SaveManualDelineation'].start()
1705
+ self.last_image_question()
1706
+ self.manual_delineation_flag = False
1707
+ else:
1708
+ self.message.setText(
1709
+ f"{self.arena_masks_number} arenas are drawn over the {self.parent().po.sample_number} expected")
1710
+
1711
+ elif self.asking_last_image_flag:
1712
+ self.parent().po.first_image.im_combinations = None
1713
+ self.select_option.clear()
1714
+ self.arena_shape.setVisible(False)
1715
+ self.arena_shape_label.setVisible(False)
1716
+ if is_yes:
1717
+ self.start_last_image()
1718
+ # if self.parent().po.vars['origin_state'] != 'invisible':
1719
+ # self.parent().po.vars['origin_state'] = "constant"
1720
+ else:
1721
+ # if self.parent().po.vars['origin_state'] != 'invisible':
1722
+ # self.parent().po.vars['origin_state'] = "fluctuating"
1723
+ self.parent().po.vars['convert_for_origin'] = deepcopy(self.csc_dict)
1724
+ self.parent().po.vars['convert_for_motion'] = deepcopy(self.csc_dict)
1725
+ self.go_to_next_widget()
1726
+ self.asking_last_image_flag = False
1727
+ else:
1728
+ if is_yes:
1729
+ self.parent().po.vars['convert_for_motion'] = deepcopy(self.csc_dict)
1730
+ self.go_to_next_widget()
1731
+
1732
+ def first_im_parameters(self):
1733
+ """ Method called in the decision tree"""
1734
+ self.step = 1
1735
+ self.decision_label.setText("Adjust settings, draw more cells and background, and try again")
1736
+ self.yes.setVisible(False)
1737
+ self.no.setVisible(False)
1738
+ # self.one_blob_per_arena.setVisible(True)
1739
+ # self.one_blob_per_arena_label.setVisible(True)
1740
+ self.set_spot_shape.setVisible(True)
1741
+ self.spot_shape_label.setVisible(True)
1742
+ self.spot_shape.setVisible(self.parent().po.all['set_spot_shape'])
1743
+ self.set_spot_size.setVisible(self.one_blob_per_arena.isChecked())
1744
+ self.spot_size_label.setVisible(self.one_blob_per_arena.isChecked())
1745
+ self.spot_size.setVisible(
1746
+ self.one_blob_per_arena.isChecked() and self.set_spot_size.isChecked())
1747
+ # self.arena_shape.setVisible(True)
1748
+ # self.arena_shape_label.setVisible(True)
1749
+ self.auto_delineation_flag = True
1750
+ self.first_im_parameters_answered = True
1751
+
1752
+ def auto_delineation(self):
1753
+ """ Method called in the decision tree"""
1754
+ # Do not proceed automatic delineation if there are more than one arena containing distinct spots
1755
+ # The automatic delineation algorithm cannot handle this situation
1756
+ if self.parent().po.vars['several_blob_per_arena'] and self.parent().po.sample_number > 1:
1757
+ self.manual_delineation()
1758
+ else:
1759
+ self.decision_label.setText(f"")
1760
+ # Save the current mask, its stats, remove useless memory and start delineation
1761
+ self.parent().po.first_image.update_current_images(self.parent().po.current_combination_id)
1762
+ self.parent().po.get_average_pixel_size()
1763
+ self.parent().po.all['are_gravity_centers_moving'] = 0
1764
+ self.start_crop_scale_subtract_delineate()
1765
+ self.visualize_label.setVisible(False)
1766
+ self.visualize.setVisible(False)
1767
+
1768
+ def asking_slower_or_manual_delineation(self):
1769
+ self.asking_slower_or_manual_delineation_flag = True
1770
+ self.decision_label.setText(f"Click yes to try a slower but more efficient delineation algorithm, no to do it manually")
1771
+ self.message.setText(f"Clicking no will allow you to draw each arena manually")
1772
+
1773
+ def slower_delineation(self):
1774
+ self.decision_label.setText(f"")
1775
+ self.arena_shape.setVisible(False)
1776
+ self.arena_shape_label.setVisible(False)
1777
+ # Save the current mask, its stats, remove useless memory and start delineation
1778
+ self.parent().po.first_image.update_current_images(self.parent().po.current_combination_id)
1779
+ self.parent().po.all['are_gravity_centers_moving'] = 1
1780
+ self.start_crop_scale_subtract_delineate()
1781
+
1782
+ def manual_delineation(self):
1783
+ """ Method called in the decision tree"""
1784
+ self.manual_delineation_flag = True
1785
+ self.parent().po.cropping(is_first_image=True)
1786
+ self.parent().po.get_average_pixel_size()
1787
+ self.reinitialize_image_and_masks(self.parent().po.first_image.bgr)
1788
+ self.reinitialize_bio_and_back_legend()
1789
+ self.available_arena_names = np.arange(1, self.parent().po.sample_number + 1)
1790
+ self.saved_coord = []
1791
+ self.arena_mask = np.zeros(self.parent().po.current_image.shape[:2], dtype=np.uint16)
1792
+ # self.next.setVisible(True)
1793
+ self.decision_label.setVisible(True)
1794
+ self.yes.setVisible(True)
1795
+ self.cell.setVisible(False)
1796
+ self.background.setVisible(False)
1797
+ self.arena_shape_label.setVisible(False)
1798
+ self.arena_shape.setVisible(False)
1799
+ self.no.setVisible(False)
1800
+ self.one_blob_per_arena.setVisible(False)
1801
+ self.one_blob_per_arena_label.setVisible(False)
1802
+ self.generate_analysis_options.setVisible(False)
1803
+ self.quickly.setVisible(False)
1804
+ self.carefully.setVisible(False)
1805
+ self.visualize.setVisible(False)
1806
+ self.visualize_label.setVisible(False)
1807
+ self.select_option.setVisible(False)
1808
+ self.select_option_label.setVisible(False)
1809
+ self.user_drawn_lines_label.setText("Draw each arena")
1810
+ self.user_drawn_lines_label.setVisible(True)
1811
+ self.decision_label.setText(
1812
+ f"Hold click to draw {self.parent().po.sample_number} arenas on the image")
1813
+ self.message.setText('Click Yes when it is done')
1814
+
1815
+ def last_image_question(self):
1816
+ """ Method called in the decision tree"""
1817
+ self.decision_label.setText(
1818
+ 'Do you want to check if the current parameters work for the last image:')
1819
+ self.message.setText('Click Yes if the cell color may change during the analysis.')
1820
+ self.yes.setVisible(True)
1821
+ self.no.setVisible(True)
1822
+ self.starting_differs_from_growing_cb.setVisible(True)
1823
+ self.starting_differs_from_growing_label.setVisible(True)
1824
+ self.image_number.setVisible(False)
1825
+ self.image_number_label.setVisible(False)
1826
+ self.read.setVisible(False)
1827
+ self.asking_last_image_flag = True
1828
+ # self.title_label.setVisible(False)
1829
+ self.step = 2
1830
+
1831
+ def start_last_image(self):
1832
+ self.is_first_image_flag = False
1833
+ # self.parent().po.vars["color_number"] = 2
1834
+ self.decision_label.setText('')
1835
+ self.yes.setVisible(False)
1836
+ self.no.setVisible(False)
1837
+ self.spot_size.setVisible(False)
1838
+ self.starting_differs_from_growing_cb.setVisible(False)
1839
+ self.starting_differs_from_growing_label.setVisible(False)
1840
+ self.message.setText('Gathering data and visualizing last image analysis result')
1841
+ self.parent().po.get_last_image()
1842
+ if self.thread['SaveManualDelineation'].isRunning():
1843
+ self.thread['SaveManualDelineation'].wait()
1844
+ self.parent().po.cropping(is_first_image=False)
1845
+ # self.parent().po.last_image = OneImageAnalysis(self.parent().po.last_im)
1846
+ self.reinitialize_image_and_masks(self.parent().po.last_image.bgr)
1847
+ self.reinitialize_bio_and_back_legend()
1848
+ self.parent().po.current_combination_id = 0
1849
+ # self.advanced_mode_cb.setChecked(True)
1850
+ self.visualize_is_clicked()
1851
+ self.user_drawn_lines_label.setText('Select and draw')
1852
+ self.user_drawn_lines_label.setVisible(True)
1853
+ self.cell.setVisible(True)
1854
+ self.background.setVisible(True)
1855
+ self.advanced_mode_cb.setVisible(True)
1856
+ self.advanced_mode_label.setVisible(True)
1857
+ self.visualize_label.setVisible(True)
1858
+ self.visualize.setVisible(True)
1859
+ self.row1_widget.setVisible(False)
1860
+ # self.title_label.setVisible(True)
1861
+ # self.row1_col1_widget.setVisible(False)
1862
+ # self.row1_col2_widget.setVisible(False)
1863
+
1864
+ def go_to_next_widget(self):
1865
+ if not self.thread['SaveManualDelineation'].isRunning() or not self.thread['FinalizeImageAnalysis'].isRunning() or not self.thread['SaveData'].isRunning():
1866
+
1867
+ self.popup = QtWidgets.QMessageBox()
1868
+ self.popup.setWindowTitle("Info")
1869
+ self.popup.setText("Final checks...")
1870
+ self.popup.setInformativeText("Close and wait until the video tracking window appears.")
1871
+ self.popup.setStandardButtons(QtWidgets.QMessageBox.Close)
1872
+ x = self.popup.exec_()
1873
+ self.decision_label.setVisible(False)
1874
+ self.yes.setVisible(False)
1875
+ self.no.setVisible(False)
1876
+ self.next.setVisible(True)
1877
+
1878
+
1879
+ self.message.setText(f"Final checks, wait... ")
1880
+ self.parent().last_tab = "image_analysis"
1881
+ self.thread['FinalizeImageAnalysis'].start()
1882
+ if self.parent().po.vars["color_number"] > 2:
1883
+ self.parent().videoanalysiswindow.select_option.clear()
1884
+ self.parent().videoanalysiswindow.select_option.addItem(f"1) Kmeans")
1885
+ self.parent().videoanalysiswindow.select_option.setCurrentIndex(0)
1886
+ self.parent().po.all['video_option'] = 0
1887
+ time.sleep(1 / 10)
1888
+ self.thread['FinalizeImageAnalysis'].wait()
1889
+ self.message.setText(f"")
1890
+
1891
+ self.video_tab.set_not_in_use()
1892
+ self.parent().last_tab = "image_analysis"
1893
+ self.parent().change_widget(3) # VideoAnalysisWindow
1894
+
1895
+ self.popup.close()
1896
+
1897
+ def closeEvent(self, event):
1898
+ event.accept
1899
+
1900
+
1901
+ # if __name__ == "__main__":
1902
+ # from cellects.gui.cellects import CellectsMainWidget
1903
+ # import sys
1904
+ # app = QtWidgets.QApplication([])
1905
+ # parent = CellectsMainWidget()
1906
+ # session = ImageAnalysisWindow(parent, False)
1907
+ # parent.insertWidget(0, session)
1908
+ # parent.show()
1909
+ # sys.exit(app.exec())