small-fish-gui 1.10.4__py3-none-any.whl → 2.0.2__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 (39) hide show
  1. small_fish_gui/README.md +54 -69
  2. small_fish_gui/__init__.py +9 -3
  3. small_fish_gui/__main__.py +1 -1
  4. small_fish_gui/batch/integrity.py +8 -1
  5. small_fish_gui/batch/output.py +16 -0
  6. small_fish_gui/batch/pipeline.py +43 -24
  7. small_fish_gui/batch/prompt.py +44 -26
  8. small_fish_gui/batch/update.py +19 -3
  9. small_fish_gui/batch/utils.py +2 -0
  10. small_fish_gui/batch/values.txt +5 -5
  11. small_fish_gui/default_values.py +51 -0
  12. small_fish_gui/gui/__init__.py +3 -1
  13. small_fish_gui/gui/_napari_widgets.py +15 -4
  14. small_fish_gui/gui/layout.py +123 -54
  15. small_fish_gui/gui/napari_visualiser.py +12 -8
  16. small_fish_gui/gui/prompts.py +61 -74
  17. small_fish_gui/gui/tooltips.py +15 -0
  18. small_fish_gui/hints.py +23 -4
  19. small_fish_gui/illustrations/DetectionVitrine_filtre.png +0 -0
  20. small_fish_gui/illustrations/DetectionVitrine_signal.png +0 -0
  21. small_fish_gui/illustrations/FocciVitrine.png +0 -0
  22. small_fish_gui/illustrations/FocciVitrine_no_spots.png +0 -0
  23. small_fish_gui/illustrations/Segmentation2D.png +0 -0
  24. small_fish_gui/illustrations/Segmentation2D_with_labels.png +0 -0
  25. small_fish_gui/logo.png +0 -0
  26. small_fish_gui/{pipeline/main.py → main_menu.py} +16 -15
  27. small_fish_gui/pipeline/_colocalisation.py +60 -41
  28. small_fish_gui/pipeline/_preprocess.py +13 -12
  29. small_fish_gui/pipeline/actions.py +102 -14
  30. small_fish_gui/pipeline/detection.py +5 -1
  31. small_fish_gui/pipeline/segmentation.py +206 -73
  32. small_fish_gui/pipeline/spots.py +129 -5
  33. small_fish_gui/pipeline/testing.ipynb +1571 -2
  34. small_fish_gui/requirements.txt +4 -4
  35. {small_fish_gui-1.10.4.dist-info → small_fish_gui-2.0.2.dist-info}/METADATA +14 -16
  36. small_fish_gui-2.0.2.dist-info/RECORD +59 -0
  37. small_fish_gui-1.10.4.dist-info/RECORD +0 -49
  38. {small_fish_gui-1.10.4.dist-info → small_fish_gui-2.0.2.dist-info}/WHEEL +0 -0
  39. {small_fish_gui-1.10.4.dist-info → small_fish_gui-2.0.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,51 @@
1
+ """"
2
+ Constant submodule to have a common reference for parameters default values
3
+ """
4
+ import os
5
+
6
+ #Image
7
+ IS_MULTICHANNEL = False
8
+ IS_3D_STACK = False
9
+ CHANNEL = 0
10
+ NUC_CHANNEL = 1
11
+
12
+ #Segmentation
13
+ FLOW_THRESHOLD = 0.4
14
+ CELLPROB_THRESHOD = 0.
15
+ CYTO_MODEL = "cpsam"
16
+ NUC_MODEL = "cpsam"
17
+ CYTO_DIAMETER = 90
18
+ NUC_DIAMETER = 60
19
+ ANISOTROPY = 1.
20
+ SHOW_SEGMENTATION = True
21
+ SEGMENT_ONLY_NUCLEI = False
22
+ DO_3D_SEMGENTATION = False
23
+ VISUAL_PATH = os.getcwd()
24
+ SAVE_SEGMENTATION_VISUAL = False
25
+
26
+ #Detection
27
+ THRESHOLD = None
28
+ THRESHOLD_PENALTY = 1
29
+ DO_DENSE_REGIONS_DECONVOLUTION = False
30
+ DO_CLUSTER_COMPUTATION = False
31
+ DO_CLUSTER_COMPUTATION = False
32
+ SHOW_NAPARI_CORRECTOR = True
33
+ INTERACTIVE_THRESHOLD = False
34
+
35
+ #Deconvolution
36
+ ALPHA = 0.5
37
+ BETA = 1.
38
+ GAMMA = 3.
39
+
40
+ #Clustering
41
+ CLUSTER_SIZE = 400
42
+ MIN_NUMBER_SPOTS = 5
43
+
44
+ #Coloc
45
+ COLOC_RANGE = 400
46
+ COLOC_VOXEL_SIZE = (1,2,3)
47
+
48
+ #Spots Extraction
49
+ DO_CSV = False
50
+ DO_EXCEL = False
51
+ SPOT_EXTRACTION_FOLDER = os.getcwd()
@@ -24,4 +24,6 @@ from .layout import tuple_layout
24
24
  from .layout import radio_layout
25
25
  from .layout import add_header
26
26
 
27
- from .animation import add_default_loading
27
+ from .animation import add_default_loading
28
+
29
+ from .theme import default_theme
@@ -158,21 +158,32 @@ class ChangesPropagater(NapariWidget) :
158
158
  """
159
159
  def __init__(self, label_list):
160
160
  self.label_list = label_list
161
+ for label_layer in self.label_list :
162
+ label_layer.events.selected_label.connect((self, 'update'))
161
163
  super().__init__()
162
164
 
163
165
  def _create_widget(self) :
164
166
  @magicgui(
165
- call_button='Apply changes',
167
+ call_button='Expand label',
166
168
  auto_call=False,
167
169
  )
168
- def apply_changes() -> None:
170
+ def apply_changes(label_number : int) -> None:
169
171
  for layer in self.label_list :
170
172
  slices = layer.data.shape[0]
171
- layer_2D = np.max(layer.data, axis=0)
172
- layer.data = np.repeat(layer_2D[np.newaxis], slices, axis=0)
173
+ masked_layer = layer.data.copy()
174
+ masked_layer[masked_layer != label_number] = 0
175
+ masked_layer = np.max(masked_layer, axis=0)
176
+ expanded_layer = np.repeat(masked_layer[np.newaxis], slices, axis=0)
177
+ layer.data[expanded_layer == label_number] = expanded_layer[expanded_layer == label_number]
173
178
  layer.refresh()
174
179
  return apply_changes
175
180
 
181
+ def update(self, event) :
182
+ layer : Labels = event.source
183
+ new_label = layer.selected_label
184
+ self.widget.label_number.value = new_label
185
+ self.widget.update()
186
+
176
187
  class ClusterIDSetter(ClusterWidget) :
177
188
  """
178
189
  Allow user to set selected single spots to chosen cluster_id
@@ -1,12 +1,13 @@
1
1
  import FreeSimpleGUI as sg
2
2
  import os
3
- from ..utils import check_parameter
4
- from ..hints import pipeline_parameters
5
- from typing import Optional, Union
6
3
  import cellpose.models as models
7
- from cellpose.core import use_gpu
4
+ import small_fish_gui.default_values as default
5
+ from typing import Optional, Union
8
6
 
9
- sg.theme('DarkAmber')
7
+ from cellpose.core import use_gpu
8
+ from .tooltips import FLOW_THRESHOLD_TOOLTIP,CELLPROB_TOOLTIP
9
+ from ..hints import pipeline_parameters
10
+ from ..utils import check_parameter
10
11
 
11
12
 
12
13
  def add_header(header_text) :
@@ -20,13 +21,29 @@ def pad_right(string, length, pad_char) :
20
21
  else : return string + pad_char* (length - len(string))
21
22
 
22
23
 
23
- def parameters_layout(parameters:'list[str]' = [], unit=None, header= None, default_values=None, size=5, opt:list=None) :
24
+ def parameters_layout(
25
+ parameters:'list[str]' = [],
26
+ unit=None,
27
+ header= None,
28
+ default_values=None,
29
+ keys = None,
30
+ tooltips = None,
31
+ size=5,
32
+ opt:list=None
33
+ ) :
24
34
 
25
35
  if len(parameters) == 0 : return []
26
36
  check_parameter(parameters= list, header = (str, type(None)))
27
37
  for key in parameters : check_parameter(key = str)
28
38
  max_length = len(max(parameters, key=len))
29
39
 
40
+ if keys is None : keys = [None] * len(parameters)
41
+ else :
42
+ if len(keys) != len(parameters) : raise ValueError("keys length must be equal with parameters length or set to None.")
43
+ if tooltips is None : tooltips = [None] * len(parameters)
44
+ else :
45
+ if len(tooltips) != len(parameters) : raise ValueError("tooltips length must be equal with parameters length or set to None.")
46
+
30
47
  if type(opt) == type(None) :
31
48
  opt = [False] * len(parameters)
32
49
  else :
@@ -35,10 +52,11 @@ def parameters_layout(parameters:'list[str]' = [], unit=None, header= None, defa
35
52
  if isinstance(default_values, (list, tuple)) :
36
53
  if len(default_values) != len(parameters) : raise ValueError("if default values specified it must be of equal length as parameters.")
37
54
  layout= [
38
- [sg.Text("{0}".format(pad_right(parameter, max_length, ' ')), text_color= 'green' if option else None),
39
- sg.InputText(size= size, key= parameter, default_text= value)
40
-
41
- ] for parameter,value, option in zip(parameters,default_values, opt)
55
+ [
56
+ sg.Text("{0}".format(pad_right(parameter, max_length, ' ')), text_color= 'green' if option else None),
57
+ sg.InputText(size= size, key= parameter if key is None else key, default_text= value, tooltip=tooltip)
58
+
59
+ ] for parameter,value, option, key, tooltip in zip(parameters,default_values, opt, keys, tooltips)
42
60
  ]
43
61
  else :
44
62
  layout= [
@@ -56,7 +74,7 @@ def parameters_layout(parameters:'list[str]' = [], unit=None, header= None, defa
56
74
  layout = [add_header(header)] + layout
57
75
  return layout
58
76
 
59
- def tuple_layout(opt=None, default_dict={}, unit:dict={}, **tuples) :
77
+ def tuple_layout(opt=None, default_dict={}, unit:dict={}, names : dict = {}, **tuples) :
60
78
  """
61
79
  tuples example : voxel_size = ['z','y','x']; will ask a tuple with 3 element default to 'z', 'y' 'x'.
62
80
  """
@@ -77,9 +95,9 @@ def tuple_layout(opt=None, default_dict={}, unit:dict={}, **tuples) :
77
95
  max_size = len(max(tuples.keys(), key=len))
78
96
 
79
97
  layout = [
80
- [sg.Text(pad_right(tup, max_size, ' '), text_color= 'green' if opt[option] else None)]
98
+ [sg.Text(pad_right(names[tup] if tup in names.keys() else tup, max_size, ' '), text_color= 'green' if opt[option] else None)]
81
99
  + [sg.InputText(default_text=default_dict.setdefault('{0}_{1}'.format(tup,elmnt), elmnt),key= '{0}_{1}'.format(tup, elmnt), size= 5) for elmnt in tuples[tup]]
82
- + [sg.Text(unit.setdefault(tup,''))]
100
+ + [sg.Text(unit.setdefault(tup,''))]
83
101
  for tup,option, in zip(tuples,opt)
84
102
  ]
85
103
 
@@ -96,9 +114,13 @@ def path_layout(keys= [],look_for_dir = False, header=None, preset=os.getcwd())
96
114
  else : Browse = sg.FileBrowse
97
115
 
98
116
  max_length = len(max(keys, key=len))
99
- layout = [
100
- [sg.Text(pad_right(name, max_length, ' ')), Browse(key= name, target=(555666777,2), initial_folder= preset), sg.Text('')] for name in keys
101
- ]
117
+
118
+ layout = []
119
+ for name in keys :
120
+ layout += [
121
+ [sg.Text(pad_right(name, max_length, ' '))],
122
+ [sg.InputText(key= name, expand_x=True, default_text=preset), Browse(key= name + "_browse", initial_folder= preset), sg.Text('')],
123
+ ]
102
124
  if isinstance(header, str) :
103
125
  layout = [add_header(header)] + layout
104
126
  return layout
@@ -163,18 +185,25 @@ def radio_layout(values, header=None, key=None) :
163
185
  return layout
164
186
 
165
187
  def _segmentation_layout(
166
- multichannel,
167
- cytoplasm_model_preset= 'cyto3',
168
- nucleus_model_preset= 'nuclei',
169
- cytoplasm_channel_preset=0,
170
- nucleus_channel_preset=0,
188
+ multichannel : bool,
189
+ is_3D_stack : bool,
190
+ cytoplasm_model_preset= default.CYTO_MODEL,
191
+ nucleus_model_preset= default.NUC_MODEL,
192
+ cytoplasm_channel_preset=default.CHANNEL,
193
+ nucleus_channel_preset=default.NUC_CHANNEL,
171
194
  other_nucleus_image_preset = None,
172
- cyto_diameter_preset=30,
173
- nucleus_diameter_preset= 30,
174
- show_segmentation_preset= False,
175
- segment_only_nuclei_preset=False,
176
- saving_path_preset=os.getcwd(),
195
+ cyto_diameter_preset=default.CYTO_DIAMETER,
196
+ nucleus_diameter_preset= default.NUC_DIAMETER,
197
+ show_segmentation_preset= default.SHOW_SEGMENTATION,
198
+ save_segmentation_visual_preset = default.SAVE_SEGMENTATION_VISUAL,
199
+ segment_only_nuclei_preset=default.SEGMENT_ONLY_NUCLEI,
200
+ saving_path_preset=default.VISUAL_PATH,
177
201
  filename_preset='cell_segmentation.png',
202
+ cytoplasm_segmentation_3D = default.DO_3D_SEMGENTATION,
203
+ nucleus_segmentation_3D = default.DO_3D_SEMGENTATION,
204
+ cellprob_threshold = default.CELLPROB_THRESHOD,
205
+ flow_threshold = default.FLOW_THRESHOLD,
206
+ anisotropy= default.ANISOTROPY,
178
207
  ) :
179
208
 
180
209
  USE_GPU = use_gpu()
@@ -189,30 +218,51 @@ def _segmentation_layout(
189
218
 
190
219
  #cytoplasm parameters
191
220
  layout += [
192
- add_header("Cell Segmentation"),
193
- [sg.Text("Choose cellpose model for cytoplasm: \n")],
194
- combo_elmt(models_list, key='cyto_model_name', default_value= cytoplasm_model_preset)
221
+ add_header("Cytoplasm Segmentation"),
222
+ [sg.Text("Choose parameters for cytoplasm segmentation: \n")],
195
223
  ]
196
224
 
197
- if multichannel : layout += parameters_layout(['cytoplasm channel'],default_values= [cytoplasm_channel_preset])
225
+ if is_3D_stack : layout += bool_layout(['3D segmentation'], preset=[cytoplasm_segmentation_3D], keys=['cytoplasm_segmentation_3D'],)
226
+ if multichannel : layout += parameters_layout(['Cytoplasm channel'],default_values= [cytoplasm_channel_preset], keys = ["cytoplasm_channel"])
198
227
 
228
+ layout += [[sg.Text("Cellpose model : ")] + combo_elmt(models_list, key='cyto_model_name', default_value= cytoplasm_model_preset)]
229
+ layout += parameters_layout(['Cytoplasm diameter'], unit= "px", default_values= [cyto_diameter_preset], keys=['cytoplasm_diameter'])
230
+ layout += parameters_layout(
231
+ ["Flow threshold", "Cellprob threshold"],
232
+ default_values=[flow_threshold, cellprob_threshold],
233
+ keys=["flow_threshold_cyto","cellprob_threshold_cyto"],
234
+ tooltips= [FLOW_THRESHOLD_TOOLTIP, CELLPROB_TOOLTIP]
235
+ )
199
236
 
200
- layout += parameters_layout(['cytoplasm diameter'], unit= "px", default_values= [cyto_diameter_preset])
201
237
  #Nucleus parameters
202
238
  layout += [
203
- add_header("Nucleus segmentation"),
204
- [sg.Text("Choose cellpose model for nucleus: \n")],
205
- combo_elmt(models_list, key='nucleus_model_name', default_value= nucleus_model_preset)
239
+ add_header("Nuclei segmentation"),
240
+ [sg.Text("Choose parameters for nuclei segmentation: \n")],
206
241
  ]
207
242
 
208
- if multichannel : layout += parameters_layout(['nucleus channel'], default_values= [nucleus_channel_preset])
209
243
  layout += path_layout(['other_nucleus_image'], preset=other_nucleus_image_preset)
210
- layout += parameters_layout([ 'nucleus diameter'],unit= "px", default_values= [nucleus_diameter_preset])
211
- layout += bool_layout(["Segment only nuclei"], preset=segment_only_nuclei_preset)
244
+ if is_3D_stack : layout += bool_layout(['3D segmentation'], preset=[nucleus_segmentation_3D], keys=['nucleus_segmentation_3D'],)
245
+ if multichannel : layout += parameters_layout(['nucleus_channel'], default_values= [nucleus_channel_preset])
246
+ layout += bool_layout(["Segment only nuclei"], preset=segment_only_nuclei_preset, keys=["segment_only_nuclei"])
212
247
 
248
+ layout += [[sg.Text("Cellpose model : ")] + combo_elmt(models_list, key='nucleus_model_name', default_value= nucleus_model_preset)]
249
+ layout += parameters_layout(['Nucleus diameter'],unit= "px", default_values= [nucleus_diameter_preset], keys=['nucleus_diameter'])
250
+ layout += parameters_layout(
251
+ ["Flow threshold", "Cellprob threshold"],
252
+ default_values=[flow_threshold, cellprob_threshold],
253
+ keys=["flow_threshold_nuc","cellprob_threshold_nuc"],
254
+ tooltips= [FLOW_THRESHOLD_TOOLTIP, CELLPROB_TOOLTIP]
255
+ )
256
+ if is_3D_stack : layout += parameters_layout(
257
+ ['anisotropy'],
258
+ header = "Model parameters",
259
+ default_values = [anisotropy]
260
+ )
261
+
213
262
  #Control plots
214
- layout += bool_layout(['show segmentation'], header= 'Segmentation plots', preset= show_segmentation_preset)
215
- layout += path_layout(['saving path'], look_for_dir=True, preset=saving_path_preset)
263
+ layout += bool_layout(['Show_segmentation'],keys=['show_segmentation'], header= 'Segmentation plots', preset= show_segmentation_preset)
264
+ layout += bool_layout(['Save segmentation visual'], preset= save_segmentation_visual_preset, keys=['save_segmentation_visual'])
265
+ layout += path_layout(keys=['saving path'], look_for_dir=True, preset=saving_path_preset)
216
266
  layout += parameters_layout(['filename'], default_values=[filename_preset], size= 25)
217
267
 
218
268
  return layout
@@ -254,7 +304,7 @@ def _detection_layout(
254
304
 
255
305
  #Detection
256
306
  detection_parameters = ['threshold', 'threshold penalty']
257
- default_detection = [default_dict.setdefault('threshold',''), default_dict.setdefault('threshold penalty', '1')]
307
+ default_detection = [default_dict.setdefault('threshold',default.THRESHOLD), default_dict.setdefault('threshold penalty', default.THRESHOLD_PENALTY)]
258
308
  opt= [True, True]
259
309
  if is_multichannel :
260
310
  detection_parameters += ['channel_to_compute']
@@ -268,34 +318,31 @@ def _detection_layout(
268
318
  else : tuple_shape = ('z','y','x')
269
319
  opt = {'voxel_size' : False, 'spot_size' : False, 'log_kernel_size' : True, 'minimum_distance' : True}
270
320
  unit = {'voxel_size' : 'nm', 'minimum_distance' : 'nm', 'spot_size' : 'radius(nm)', 'log_kernel_size' : 'px'}
321
+ names = {'voxel_size' : 'Voxel size', 'spot_size' : 'Spot size', 'log_kernel_size' : 'LoG kernel size', 'minimum_distance' : 'Minimum distance between spots'}
271
322
 
272
- layout += tuple_layout(opt=opt, unit=unit, default_dict=default_dict, voxel_size= tuple_shape, spot_size= tuple_shape, log_kernel_size= tuple_shape, minimum_distance= tuple_shape)
323
+ layout += tuple_layout(opt=opt, unit=unit, default_dict=default_dict, names=names, voxel_size= tuple_shape, spot_size= tuple_shape, log_kernel_size= tuple_shape, minimum_distance= tuple_shape)
273
324
 
274
325
  if (do_segmentation and is_multichannel) or (is_multichannel and segmentation_done):
275
- default_segmentation = [default_dict.setdefault('nucleus channel signal', default_dict.setdefault('nucleus channel',0))]
276
- layout += [[sg.Text("nucleus channel signal "), sg.InputText(default_text=default_segmentation, key= "nucleus channel signal", size= 5, tooltip= "Channel from which signal will be measured for nucleus features, \nallowing you to measure signal from a different channel than the one used for segmentation.")]]
277
- # layout += parameters_layout(['nucleus channel signal'], default_values=default_segmentation) + [[sg.Text("Channel from which signal will be measured for nucleus features, allowing you to measure signal from a different channel than the one used for segmentation.")]]
326
+ layout += [[sg.Text("nucleus channel signal "), sg.InputText(default_text=default_dict.setdefault('nucleus_channel',default.NUC_CHANNEL), key= "nucleus channel signal", size= 5, tooltip= "Channel from which signal will be measured for nucleus features, \nallowing you to measure signal from a different channel than the one used for segmentation.")]]
278
327
 
279
328
  #Deconvolution
280
329
  if do_dense_region_deconvolution :
281
- default_dense_regions_deconvolution = [default_dict.setdefault('alpha',0.5), default_dict.setdefault('beta',1)]
330
+ default_dense_regions_deconvolution = [default_dict.setdefault('alpha',default.ALPHA), default_dict.setdefault('beta',default.BETA)]
282
331
  layout += parameters_layout(['alpha', 'beta',], default_values= default_dense_regions_deconvolution, header= 'do_dense_regions_deconvolution')
283
- layout += parameters_layout(['gamma'], unit= 'px', default_values= [default_dict.setdefault('gamma',5)])
332
+ layout += parameters_layout(['gamma'], unit= 'px', default_values= [default_dict.setdefault('gamma',default.GAMMA)])
284
333
  layout += tuple_layout(opt= {"deconvolution_kernel" : True}, unit= {"deconvolution_kernel" : 'px'}, default_dict=default_dict, deconvolution_kernel = tuple_shape)
285
334
 
286
335
  #Clustering
287
336
  if do_clustering :
288
- layout += parameters_layout(['cluster_size'], unit="radius(nm)", default_values=[default_dict.setdefault('cluster_size',400)])
289
- layout += parameters_layout(['min_number_of_spots'], default_values=[default_dict.setdefault('min_number_of_spots', 5)])
337
+ layout += parameters_layout(['Cluster radius'],keys=['cluster_size'], unit="radius(nm)", default_values=[default_dict.setdefault('cluster_size',default.CLUSTER_SIZE)])
338
+ layout += parameters_layout(['Min nb spots per cluster'],keys=['min_number_of_spots'], default_values=[default_dict.setdefault('min_number_of_spots', default.MIN_NUMBER_SPOTS)])
290
339
 
291
-
292
-
293
- layout += bool_layout(['Interactive threshold selector'],keys = ['show_interactive_threshold_selector'], preset=[False])
340
+ layout += bool_layout(['Interactive threshold selector'],keys = ['show_interactive_threshold_selector'], preset=[default.INTERACTIVE_THRESHOLD])
294
341
  layout += path_layout(
295
342
  keys=['spots_extraction_folder'],
296
343
  look_for_dir=True,
297
344
  header= "Individual spot extraction",
298
- preset= default_dict.setdefault('spots_extraction_folder', '')
345
+ preset= default_dict.setdefault('spots_extraction_folder', default.SPOT_EXTRACTION_FOLDER)
299
346
  )
300
347
  default_filename = default_dict.setdefault("filename","") + "_spot_extraction"
301
348
  layout += parameters_layout(
@@ -304,12 +351,34 @@ def _detection_layout(
304
351
  size= 13
305
352
  )
306
353
  layout += bool_layout(
307
- parameters= ['do_spots_csv', 'do_spots_excel', 'do_spots_feather'],
308
- preset= [default_dict.setdefault('do_spots_csv',False), default_dict.setdefault('do_spots_excel',False),default_dict.setdefault('do_spots_feather',False)]
354
+ ['.csv','.excel',],
355
+ keys= ['do_spots_csv', 'do_spots_excel'],
356
+ preset= [default_dict.setdefault('do_spots_csv',default.DO_CSV), default_dict.setdefault('do_spots_excel',default.DO_EXCEL),]
309
357
  )
310
358
 
311
359
  return layout
312
360
 
361
+ def colocalization_layout(spot_list : list) :
362
+ layout = [
363
+ [sg.Push(), sg.Text("Co-localization", size=25, font="bold"), sg.Push()],
364
+ [sg.VPush()],
365
+ [sg.Text("Spots 1", size = 10)],
366
+ [sg.DropDown(values=[""] + spot_list, key="spots1_dropdown"), sg.Input(disabled=True, text_color="black"),sg.FileBrowse("Load spot extraction", key="spots1_browse")],
367
+ [sg.Text("Spots 2", size = 10)],
368
+ [sg.DropDown(values=[""] + spot_list, key="spots2_dropdown"), sg.Input(disabled=True, text_color="black"),sg.FileBrowse("Load spot extraction", key="spots2_browse")],
369
+ ]
370
+
371
+ layout += parameters_layout(['colocalisation distance'], unit= 'nm', default_values= default.COLOC_RANGE,)
372
+
373
+ layout += tuple_layout(opt={'voxel_size' : False},unit={'voxel_size' : "nm"}, voxel_size = ['z','y','x'], names={'voxel_size' : 'Voxel size'}, default_dict={'voxel_size' : default.COLOC_VOXEL_SIZE},)
374
+ layout += [[sg.Text(" 'voxel size' is used only for loaded spot lists.")]]
375
+
376
+ layout += [[sg.Push(),sg.Button("Ok", bind_return_key=True),sg.Button("Cancel"),sg.Push(),],
377
+ [sg.VPush()]
378
+ ]
379
+
380
+ return layout
381
+
313
382
  def _ask_channel_map_layout(
314
383
  shape,
315
384
  is_3D_stack,
@@ -134,7 +134,7 @@ def correct_spots(
134
134
  new_clusters = np.concatenate([
135
135
  cluster_layer.data,
136
136
  cluster_layer.features.loc[:,["spot_number","cluster_id"]].to_numpy()
137
- ],axis=1)
137
+ ],axis=1).astype(int)
138
138
 
139
139
  new_spots = np.concatenate([
140
140
  single_layer.data,
@@ -145,7 +145,7 @@ def correct_spots(
145
145
  new_min_spot_number = widget_cluster_updater.min_spot
146
146
 
147
147
  else :
148
- new_spots = single_layer.data
148
+ new_spots = single_layer.data.astype(int)
149
149
  new_clusters = None
150
150
  new_cluster_radius = None
151
151
  new_min_spot_number = None
@@ -159,6 +159,7 @@ def show_segmentation(
159
159
  nuc_label : np.ndarray,
160
160
  cyto_image : np.ndarray = None,
161
161
  cyto_label : np.ndarray = None,
162
+ anisotrpy : float = 1,
162
163
  ) :
163
164
  dim = nuc_image.ndim
164
165
 
@@ -186,16 +187,18 @@ def show_segmentation(
186
187
  #Init Napari viewer
187
188
  Viewer = napari.Viewer(ndisplay=2, title= 'Show segmentation', axis_labels=['z','y','x'] if dim == 3 else ['y', 'x'])
188
189
 
190
+ scale = (anisotrpy, 1, 1)
191
+
189
192
  # Adding nuclei
190
- nuc_signal_layer = Viewer.add_image(nuc_image, name= "nucleus signal", blending= 'additive', colormap='blue', contrast_limits=[nuc_image.min(), nuc_image.max()])
191
- nuc_label_layer = Viewer.add_labels(nuc_label, opacity= 0.6, name= 'nucleus_label',)
193
+ nuc_signal_layer = Viewer.add_image(nuc_image, name= "nucleus signal", blending= 'additive', colormap='blue', contrast_limits=[nuc_image.min(), nuc_image.max()], scale=scale)
194
+ nuc_label_layer = Viewer.add_labels(nuc_label, opacity= 0.6, name= 'nucleus_label', scale=scale)
192
195
  nuc_label_layer.preserve_labels = True
193
196
  labels_layer_list = [nuc_label_layer]
194
197
 
195
198
  #Adding cytoplasm
196
199
  if (type(cyto_label) != type(None) and not np.array_equal(cyto_label, nuc_label) ) or (type(cyto_label) != type(None) and cyto_label.max() == 0):
197
- Viewer.add_image(cyto_image, name= "cytoplasm signal", blending= 'additive', colormap='red', contrast_limits=[cyto_image.min(), cyto_image.max()])
198
- cyto_label_layer = Viewer.add_labels(cyto_label, opacity= 0.6, name= 'cytoplasm_label')
200
+ Viewer.add_image(cyto_image, name= "cytoplasm signal", blending= 'additive', colormap='red', contrast_limits=[cyto_image.min(), cyto_image.max()], scale=scale)
201
+ cyto_label_layer = Viewer.add_labels(cyto_label, opacity= 0.6, name= 'cytoplasm_label', scale=scale)
199
202
  cyto_label_layer.preserve_labels = True
200
203
  labels_layer_list += [cyto_label_layer]
201
204
 
@@ -205,9 +208,10 @@ def show_segmentation(
205
208
  label_reseter = SegmentationReseter(labels_layer_list)
206
209
  changes_applier = ChangesPropagater(labels_layer_list)
207
210
 
208
- buttons_container = widgets.Container(widgets=[label_picker.widget, changes_applier.widget, label_reseter.widget], labels=False, layout='horizontal')
211
+ buttons_container = widgets.Container(widgets=[label_picker.widget, label_reseter.widget], labels=False, layout='horizontal')
212
+ changes_applier = widgets.Container(widgets=[changes_applier.widget], labels=False, layout='horizontal')
209
213
  tools_container = widgets.Container(
210
- widgets = [buttons_container, label_eraser.widget],
214
+ widgets = [buttons_container, changes_applier, label_eraser.widget],
211
215
  labels=False,
212
216
  )
213
217
  Viewer.window.add_dock_widget(tools_container, name='SmallFish', area='left')
@@ -2,8 +2,19 @@ import FreeSimpleGUI as sg
2
2
  import pandas as pd
3
3
  import os
4
4
  import numpy as np
5
- from typing import Literal, Union, Any
6
- from .layout import path_layout, parameters_layout, bool_layout, tuple_layout, combo_elmt, add_header, path_layout, radio_layout
5
+ import small_fish_gui.default_values as default
6
+
7
+ from typing import Literal, Union
8
+ from .layout import (
9
+ path_layout,
10
+ parameters_layout,
11
+ bool_layout,
12
+ path_layout,
13
+ radio_layout,
14
+ colocalization_layout,
15
+ tuple_layout,
16
+ _detection_layout
17
+ )
7
18
  from ..interface import open_image, check_format, FormatError
8
19
 
9
20
 
@@ -13,12 +24,9 @@ def prompt(layout, add_ok_cancel=True, timeout=None, timeout_key='TIMEOUT_KEY',
13
24
  """
14
25
  if add_ok_cancel : layout += [[sg.Button('Ok', bind_return_key=True), sg.Button('Cancel')]]
15
26
 
16
- if add_scrollbar :
17
- size = (400,500)
18
- col_elmt = sg.Column(layout, scrollable=True, vertical_scroll_only=True, size=size)
19
- layout = [[col_elmt]]
20
- else :
21
- size = (None,None)
27
+ size = (400,500)
28
+ col_elmt = sg.Column(layout, scrollable=True, vertical_scroll_only=True, size=size, expand_x=True, expand_y=True)
29
+ layout = [[col_elmt]]
22
30
 
23
31
  window = sg.Window('small fish', layout=layout, margins=(10,10), size=size, resizable=True, location=None)
24
32
  event, values = window.read(timeout=timeout, timeout_key=timeout_key)
@@ -33,8 +41,6 @@ def prompt(layout, add_ok_cancel=True, timeout=None, timeout_key='TIMEOUT_KEY',
33
41
  window.close()
34
42
  return event, values
35
43
 
36
-
37
-
38
44
  def input_image_prompt(
39
45
  is_3D_stack_preset=False,
40
46
  multichannel_preset = False,
@@ -90,15 +96,16 @@ def output_image_prompt(filename) :
90
96
  layout = path_layout(['folder'], look_for_dir= True, header= "Output parameters :")
91
97
  layout += parameters_layout(["filename"], default_values= [filename + "_quantification"], size=25)
92
98
  layout += bool_layout(['csv','Excel', 'Feather'])
93
- layout.append([sg.Button('Cancel')])
94
99
 
95
100
  event,values= prompt(layout)
101
+ if event == ('Cancel') : return None
96
102
 
97
103
  values['filename'] = values['filename'].replace(".xlsx","")
98
104
  values['filename'] = values['filename'].replace(".feather","")
99
105
  excel_filename = values['filename'] + ".xlsx"
100
106
  feather_filename = values['filename'] + ".feather"
101
107
 
108
+
102
109
  if not values['Excel'] and not values['Feather'] and not values['csv'] :
103
110
  sg.popup("Please check at least one box : Excel/Feather/csv")
104
111
  relaunch = True
@@ -118,9 +125,7 @@ def output_image_prompt(filename) :
118
125
 
119
126
  if not relaunch : break
120
127
 
121
- if event == ('Cancel') : return None
122
-
123
- else : return values
128
+ return values
124
129
 
125
130
  def detection_parameters_promt(
126
131
  is_3D_stack,
@@ -135,60 +140,15 @@ def detection_parameters_promt(
135
140
  Returns Values
136
141
 
137
142
  """
138
- if is_3D_stack : dim = 3
139
- else : dim = 2
140
-
141
- #Detection
142
- detection_parameters = ['threshold', 'threshold penalty']
143
- default_detection = [default_dict.setdefault('threshold',''), default_dict.setdefault('threshold penalty', '1')]
144
- opt= [True, True]
145
- if is_multichannel :
146
- detection_parameters += ['channel_to_compute']
147
- opt += [False]
148
- default_detection += [default_dict.setdefault('channel_to_compute', '')]
149
- layout = [[sg.Text("Green parameters", text_color= 'green'), sg.Text(" are optional parameters.")]]
150
- layout += parameters_layout(detection_parameters, header= 'Detection', opt=opt, default_values=default_detection)
151
-
152
- if dim == 2 : tuple_shape = ('y','x')
153
- else : tuple_shape = ('z','y','x')
154
- opt = {'voxel_size' : False, 'spot_size' : False, 'log_kernel_size' : True, 'minimum_distance' : True}
155
- unit = {'voxel_size' : 'nm', 'minimum_distance' : 'nm', 'spot_size' : 'radius(nm)', 'log_kernel_size' : 'px'}
156
-
157
- layout += tuple_layout(opt=opt, unit=unit, default_dict=default_dict, voxel_size= tuple_shape, spot_size= tuple_shape, log_kernel_size= tuple_shape, minimum_distance= tuple_shape)
158
-
159
- #Deconvolution
160
- if do_dense_region_deconvolution :
161
- default_dense_regions_deconvolution = [default_dict.setdefault('alpha',0.5), default_dict.setdefault('beta',1)]
162
- layout += parameters_layout(['alpha', 'beta',], default_values= default_dense_regions_deconvolution, header= 'do_dense_regions_deconvolution')
163
- layout += parameters_layout(['gamma'], unit= 'px', default_values= [default_dict.setdefault('gamma',5)])
164
- layout += tuple_layout(opt= {"deconvolution_kernel" : True}, unit= {"deconvolution_kernel" : 'px'}, default_dict=default_dict, deconvolution_kernel = tuple_shape)
165
143
 
166
- #Clustering
167
- if do_clustering :
168
- layout += parameters_layout(['cluster_size'], unit="radius(nm)", default_values=[default_dict.setdefault('cluster_size',400)])
169
- layout += parameters_layout(['min_number_of_spots'], default_values=[default_dict.setdefault('min_number_of_spots', 5)])
170
-
171
- if is_multichannel and segmentation_done :
172
- default_segmentation = [default_dict.setdefault('nucleus channel signal', default_dict.setdefault('nucleus channel',0))]
173
- layout += parameters_layout(['nucleus channel signal'], default_values=default_segmentation) + [[sg.Text(" channel from which signal will be measured for nucleus features.")]]
174
-
175
- layout += bool_layout(['Interactive threshold selector'], keys=['show_interactive_threshold_selector'], preset=[False])
176
- layout += path_layout(
177
- keys=['spots_extraction_folder'],
178
- look_for_dir=True,
179
- header= "Individual spot extraction",
180
- preset= default_dict.setdefault('spots_extraction_folder', '')
181
- )
182
- default_filename = default_dict.setdefault("filename","") + "_spot_extraction"
183
- layout += parameters_layout(
184
- parameters=['spots_filename'],
185
- default_values=[default_filename],
186
- size= 13
187
- )
188
- layout += bool_layout(
189
- ['.csv','.excel','.feather'],
190
- keys= ['do_spots_csv', 'do_spots_excel', 'do_spots_feather'],
191
- preset= [default_dict.setdefault('do_spots_csv',False), default_dict.setdefault('do_spots_excel',False),default_dict.setdefault('do_spots_feather',False)]
144
+ layout = _detection_layout(
145
+ is_3D_stack=is_3D_stack,
146
+ is_multichannel=is_multichannel,
147
+ do_dense_region_deconvolution=do_dense_region_deconvolution,
148
+ do_clustering=do_clustering,
149
+ segmentation_done=segmentation_done,
150
+ default_dict=default_dict,
151
+ do_segmentation=False,
192
152
  )
193
153
 
194
154
  event, values = prompt(layout)
@@ -268,18 +228,45 @@ def hub_prompt(fov_results : pd.DataFrame, do_segmentation=False) -> 'Union[Lite
268
228
  while True :
269
229
  event, values = window.read()
270
230
  if event == None : quit()
271
- elif event == 'Help' : pass
272
231
  else :
273
232
  window.close()
274
233
  return event, values
275
234
 
276
- def coloc_prompt() :
277
- layout = parameters_layout(['colocalisation distance'], unit= 'nm', header= 'Colocalisation', default_values= 0)
278
- event, values = prompt(layout)
235
+ def coloc_prompt(spot_list : list) :
236
+ layout = colocalization_layout(spot_list)
279
237
 
280
- if event == 'Ok' :
281
- return values['colocalisation distance']
282
- else : return False
238
+ layout = [[sg.Col(
239
+ layout,
240
+ expand_x=True,
241
+ expand_y=True,
242
+ vertical_scroll_only=True,
243
+ scrollable=True
244
+ )
245
+ ]]
246
+ window = sg.Window('small fish', layout=layout, margins=(10,10), resizable=True, location=None, auto_size_buttons=True)
247
+ while True :
248
+ event, values = window.read(timeout=100, timeout_key="timeout")
249
+
250
+ if event == None : quit()
251
+ elif event == "timeout" : pass
252
+ elif event == 'Ok' :
253
+
254
+ if values["spots1_dropdown"] =="" :
255
+ spots1 = values["spots1_browse"]
256
+ else :
257
+ spots1 = values["spots1_dropdown"]
258
+
259
+ if values["spots2_dropdown"] =="" :
260
+ spots2 = values["spots2_browse"]
261
+ else :
262
+ spots2 = values["spots2_dropdown"]
263
+
264
+
265
+ window.close()
266
+ return values['colocalisation distance'], [values['voxel_size_z'], values['voxel_size_y'], values['voxel_size_x']], spots1, spots2
267
+ else :
268
+ window.close()
269
+ return None,None,None,None
283
270
 
284
271
  def rename_prompt() :
285
272
  layout = parameters_layout(['name'], header= "Rename acquisitions", size=12)