small-fish-gui 1.3.5__py3-none-any.whl → 1.5.0__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 (35) hide show
  1. small_fish_gui/.readthedocs.yaml +21 -0
  2. small_fish_gui/README.md +2 -3
  3. small_fish_gui/__init__.py +1 -1
  4. small_fish_gui/batch/__init__.py +5 -0
  5. small_fish_gui/batch/input.py +62 -0
  6. small_fish_gui/batch/integrity.py +158 -0
  7. small_fish_gui/batch/output.py +0 -0
  8. small_fish_gui/batch/pipeline.py +218 -0
  9. small_fish_gui/batch/prompt.py +428 -0
  10. small_fish_gui/batch/test.py +10 -0
  11. small_fish_gui/batch/update.py +132 -0
  12. small_fish_gui/batch/utils.py +66 -0
  13. small_fish_gui/batch/values.py +3 -0
  14. small_fish_gui/batch/values.txt +65 -0
  15. small_fish_gui/docs/conf.py +1 -0
  16. small_fish_gui/gui/animation.py +24 -15
  17. small_fish_gui/gui/layout.py +28 -7
  18. small_fish_gui/gui/prompts.py +11 -9
  19. small_fish_gui/interface/output.py +8 -4
  20. small_fish_gui/pipeline/__init__.py +21 -0
  21. small_fish_gui/pipeline/_napari_wrapper.py +63 -90
  22. small_fish_gui/pipeline/_preprocess.py +86 -28
  23. small_fish_gui/pipeline/_segmentation.py +165 -16
  24. small_fish_gui/pipeline/actions.py +6 -1
  25. small_fish_gui/pipeline/detection.py +75 -16
  26. small_fish_gui/pipeline/main.py +12 -5
  27. small_fish_gui/pipeline/utils.py +16 -0
  28. small_fish_gui/utils.py +6 -1
  29. {small_fish_gui-1.3.5.dist-info → small_fish_gui-1.5.0.dist-info}/METADATA +1 -1
  30. small_fish_gui-1.5.0.dist-info/RECORD +52 -0
  31. small_fish_gui/gui/batch.py +0 -312
  32. small_fish_gui/gui/test.py +0 -5
  33. small_fish_gui-1.3.5.dist-info/RECORD +0 -39
  34. {small_fish_gui-1.3.5.dist-info → small_fish_gui-1.5.0.dist-info}/WHEEL +0 -0
  35. {small_fish_gui-1.3.5.dist-info → small_fish_gui-1.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -67,9 +67,9 @@ def map_channels(user_parameters) :
67
67
  multichannel = user_parameters['multichannel']
68
68
 
69
69
  try :
70
- map = _auto_map_channels(image, is_3D_stack, is_time_stack, multichannel)
70
+ map = _auto_map_channels(is_3D_stack, is_time_stack, multichannel, image=image)
71
71
  except MappingError as e :
72
- sg.popup("Automatic dimension mapping went wrong. Please indicate manually dimensions positions in the array.")
72
+ sg.popup("Automatic dimension mapping went wrong. Please indicate dimensions positions in the array.")
73
73
  map = _ask_channel_map(image.shape, is_3D_stack, is_time_stack, multichannel, preset_map= e.get_map())
74
74
 
75
75
  else :
@@ -77,8 +77,9 @@ def map_channels(user_parameters) :
77
77
 
78
78
  return map
79
79
 
80
- def _auto_map_channels(image: np.ndarray, is_3D_stack, is_time_stack, multichannel) :
81
- shape = image.shape
80
+ def _auto_map_channels(is_3D_stack, is_time_stack, multichannel, image: np.ndarray=None, shape=None) :
81
+ if type(shape) == type(None) :
82
+ shape = image.shape
82
83
  reducing_list = list(shape)
83
84
 
84
85
  #Set the biggest dimension to y
@@ -124,14 +125,16 @@ def _auto_map_channels(image: np.ndarray, is_3D_stack, is_time_stack, multichann
124
125
  def _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map: dict= {}) :
125
126
  while True :
126
127
  relaunch = False
128
+ save_preset = preset_map.copy()
127
129
  x = preset_map.setdefault('x',0)
128
130
  y = preset_map.setdefault('y',0)
129
131
  z = preset_map.setdefault('z',0)
130
132
  c = preset_map.setdefault('c',0)
131
133
  t = preset_map.setdefault('t',0)
132
134
 
135
+
133
136
  layout = [
134
- add_header("Dimensions mapping", [sg.Text("Image shape : {0}".format(shape))])
137
+ add_header("Dimensions mapping") + [sg.Text("Image shape : {0}".format(shape))]
135
138
  ]
136
139
  layout += [parameters_layout(['x','y'], default_values=[x,y])]
137
140
  if is_3D_stack : layout += [parameters_layout(['z'], default_values=[z])]
@@ -139,7 +142,7 @@ def _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map
139
142
  if is_time_stack : layout += [parameters_layout(['t'], default_values=[t])]
140
143
 
141
144
  event, preset_map = prompt_with_help(layout,help= 'mapping', add_scrollbar=False)
142
- if event == 'Cancel' : quit()
145
+ if event == 'Cancel' : return save_preset
143
146
 
144
147
  #Check integrity
145
148
  channels_values = np.array(list(preset_map.values()), dtype= int)
@@ -156,24 +159,26 @@ def _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map
156
159
  return preset_map
157
160
 
158
161
  def _show_mapping(shape, map, is_3D_stack, is_time_stack, multichannel) :
159
- layout = [
160
- [sg.Text("Image shape : {0}".format(shape))],
161
- [sg.Text('Dimensions mapping was set to :')],
162
- [sg.Text('x : {0} \ny : {1} \nz : {2} \nc : {3} \nt : {4}'.format(
163
- map['x'], map['y'], map.get('z'), map.get("c"), map.get('t')
164
- ))],
165
- [sg.Button('Change mapping')]
166
- ]
167
-
168
- event, values = prompt_with_help(layout, help='mapping', add_scrollbar=False)
169
-
170
- if event == 'Ok' :
171
- return map
172
- elif event == 'Change mapping' or event == 'Cancel':
173
- map = _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map=map)
174
- else : raise AssertionError('Unforseen event')
162
+ while True :
163
+ layout = [
164
+ [sg.Text("Image shape : {0}".format(shape))],
165
+ [sg.Text('Dimensions mapping was set to :')],
166
+ [sg.Text('x : {0} \ny : {1} \nz : {2} \nc : {3} \nt : {4}'.format(
167
+ map['x'], map['y'], map.get('z'), map.get("c"), map.get('t')
168
+ ))],
169
+ [sg.Button('Change mapping')]
170
+ ]
171
+
172
+ event, values = prompt_with_help(layout, help='mapping', add_scrollbar=False)
173
+
174
+ if event == 'Ok' :
175
+ return map
176
+ elif event == 'Change mapping':
177
+ map = _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map=map)
178
+ elif event == 'Cancel' :
179
+ return None
180
+ else : raise AssertionError('Unforseen event')
175
181
 
176
- return map
177
182
 
178
183
  def convert_parameters_types(values:dict) :
179
184
  """
@@ -194,8 +199,8 @@ def convert_parameters_types(values:dict) :
194
199
  else : values[tuple_parameter] = tuple_values
195
200
 
196
201
  #Parameters
197
- int_list = ['threshold', 'channel_to_compute', 'min number of spots', 'cluster size','nucleus channel signal']
198
- float_list = ['time_step', 'alpha', 'beta', 'gamma', 'threshold penalty']
202
+ int_list = ['threshold', 'channel_to_compute', 'channel to compute', 'min number of spots', 'cluster size','nucleus channel signal']
203
+ float_list = ['alpha', 'beta', 'gamma', 'threshold penalty']
199
204
 
200
205
  for parameter in int_list :
201
206
  try :
@@ -213,7 +218,15 @@ def convert_parameters_types(values:dict) :
213
218
 
214
219
  return values
215
220
 
216
- def check_integrity(values: dict, do_dense_region_deconvolution, multichannel,segmentation_done, map, shape):
221
+ def check_integrity(
222
+ values: dict,
223
+ do_dense_region_deconvolution,
224
+ do_clustering,
225
+ multichannel,
226
+ segmentation_done,
227
+ map,
228
+ shape
229
+ ):
217
230
  """
218
231
  Checks that parameters given in input by user are fit to be used for bigfish detection.
219
232
  """
@@ -233,10 +246,22 @@ def check_integrity(values: dict, do_dense_region_deconvolution, multichannel,se
233
246
  _warning_popup('No gamma found; image will not be denoised before deconvolution.')
234
247
  values['gamma'] = 0
235
248
 
249
+ if values['alpha'] > 1 or values['alpha'] < 0 :
250
+ raise ParameterInputError("alpha must be set between 0 and 1.")
251
+
252
+ if do_clustering :
253
+ if not isinstance(values['min number of spots'], (int)) :
254
+ raise ParameterInputError("Incorrect min spot number parameter.")
255
+ if not isinstance(values['cluster size'], (int)) :
256
+ raise ParameterInputError("Incorrect cluster size parameter.")
257
+
236
258
  #channel
237
259
  if multichannel :
238
260
  ch_len = shape[int(map['c'])]
239
- if segmentation_done :
261
+
262
+ if type(segmentation_done) == type(None) :
263
+ pass
264
+ elif segmentation_done :
240
265
  try : nuc_signal_ch = int(values['nucleus channel signal'])
241
266
  except Exception :
242
267
  raise ParameterInputError("Incorrect channel for nucleus signal measure.")
@@ -259,7 +284,6 @@ def check_integrity(values: dict, do_dense_region_deconvolution, multichannel,se
259
284
 
260
285
  return values
261
286
 
262
-
263
287
  def reorder_shape(shape, map) :
264
288
  x = [int(map['x']),]
265
289
  y = [int(map['y']),]
@@ -275,6 +299,40 @@ def reorder_shape(shape, map) :
275
299
 
276
300
  return new_shape
277
301
 
302
+ def _check_segmentation_parameters(
303
+ user_parameters,
304
+ shape,
305
+ is_multichannel,
306
+ ) :
307
+
308
+ available_channels = list(range(len(shape)))
309
+ do_only_nuc = user_parameters['Segment only nuclei']
310
+ cyto_model_name = user_parameters['cyto_model_name']
311
+ cyto_size = user_parameters['cytoplasm diameter']
312
+ cytoplasm_channel = user_parameters['cytoplasm channel']
313
+ nucleus_model_name = user_parameters['nucleus_model_name']
314
+ nucleus_size = user_parameters['nucleus diameter']
315
+ nucleus_channel = user_parameters['nucleus channel']
316
+
317
+
318
+ if type(cyto_model_name) != str and not do_only_nuc:
319
+ raise ParameterInputError('Invalid cytoplasm model name.')
320
+ if cytoplasm_channel not in available_channels and not do_only_nuc and is_multichannel:
321
+ raise ParameterInputError('For given input image please select channel in {0}\ncytoplasm channel : {1}'.format(available_channels, cytoplasm_channel))
322
+
323
+ if type(cyto_size) not in [int, float] and not do_only_nuc:
324
+ raise ParameterInputError("Incorrect cytoplasm size.")
325
+
326
+ if type(nucleus_model_name) != str :
327
+ raise ParameterInputError('Invalid nucleus model name.')
328
+
329
+ if nucleus_channel not in available_channels and is_multichannel:
330
+ raise ParameterInputError('For given input image please select channel in {0}\nnucleus channel : {1}'.format(available_channels, nucleus_channel))
331
+
332
+ if type(nucleus_size) not in [int, float] :
333
+ raise ParameterInputError("Incorrect nucleus size.")
334
+
335
+
278
336
  def clean_unused_parameters_cache(user_parameters: dict) :
279
337
  """
280
338
  Clean unused parameters that were set to None in previous run.
@@ -6,14 +6,19 @@ from cellpose.core import use_gpu
6
6
  from skimage.measure import label
7
7
  from ..gui.layout import _segmentation_layout
8
8
  from ..gui import prompt, prompt_with_help, ask_cancel_segmentation
9
+ from ..interface import open_image
9
10
  from ._napari_wrapper import show_segmentation as napari_show_segmentation
11
+ from .utils import from_label_get_centeroidscoords
12
+ from matplotlib.colors import ListedColormap
10
13
 
14
+ import matplotlib as mpl
11
15
  import cellpose.models as models
12
16
  import numpy as np
13
17
  import bigfish.multistack as multistack
14
18
  import bigfish.stack as stack
15
19
  import bigfish.plot as plot
16
20
  import PySimpleGUI as sg
21
+ import matplotlib.pyplot as plt
17
22
  import os
18
23
 
19
24
  def launch_segmentation(image: np.ndarray, user_parameters: dict) :
@@ -38,6 +43,7 @@ def launch_segmentation(image: np.ndarray, user_parameters: dict) :
38
43
  nucleus_model_name = user_parameters.setdefault('nucleus_model_name', 'nuclei')
39
44
  nucleus_size = user_parameters.setdefault('nucleus diameter', 130)
40
45
  nucleus_channel = user_parameters.setdefault('nucleus channel', 0)
46
+ other_nucleus_image = user_parameters.setdefault('other_nucleus_image',None)
41
47
  path = os.getcwd()
42
48
  show_segmentation = user_parameters.setdefault('show segmentation', False)
43
49
  segment_only_nuclei = user_parameters.setdefault('Segment only nuclei', False)
@@ -56,6 +62,7 @@ def launch_segmentation(image: np.ndarray, user_parameters: dict) :
56
62
  nucleus_channel_preset= nucleus_channel,
57
63
  cyto_diameter_preset= cyto_size,
58
64
  nucleus_diameter_preset= nucleus_size,
65
+ other_nucleus_image_preset=other_nucleus_image,
59
66
  saving_path_preset= path,
60
67
  show_segmentation_preset=show_segmentation,
61
68
  segment_only_nuclei_preset=segment_only_nuclei,
@@ -81,10 +88,11 @@ def launch_segmentation(image: np.ndarray, user_parameters: dict) :
81
88
  nucleus_model_name = values['nucleus_model_name']
82
89
  nucleus_size = values['nucleus diameter']
83
90
  nucleus_channel = values['nucleus channel']
91
+ other_nucleus_image = values['other_nucleus_image']
84
92
  path = values['saving path'] if values['saving path'] != '' else None
85
93
  show_segmentation = values['show segmentation']
86
94
  filename = values['filename'] if type(path) != type(None) else None
87
- channels = [cytoplasm_channel, nucleus_channel]
95
+ channels = [cytoplasm_channel, nucleus_channel] if multichannel else [...,...]
88
96
 
89
97
  relaunch= False
90
98
  #Checking integrity of parameters
@@ -92,10 +100,13 @@ def launch_segmentation(image: np.ndarray, user_parameters: dict) :
92
100
  sg.popup('Invalid cytoplasm model name.')
93
101
  values['cyto_model_name'] = user_parameters.setdefault('cyto_model_name', 'cyto2')
94
102
  relaunch= True
95
- if cytoplasm_channel not in available_channels and not do_only_nuc:
96
- sg.popup('For given input image please select channel in {0}\ncytoplasm channel : {1}'.format(available_channels, cytoplasm_channel))
97
- relaunch= True
98
- values['cytoplasm channel'] = user_parameters.setdefault('cytoplasm channel',0)
103
+ if multichannel :
104
+ if cytoplasm_channel not in available_channels and not do_only_nuc:
105
+ sg.popup('For given input image please select channel in {0}\ncytoplasm channel : {1}'.format(available_channels, cytoplasm_channel))
106
+ relaunch= True
107
+ values['cytoplasm channel'] = user_parameters.setdefault('cytoplasm channel',0)
108
+ else :
109
+ cytoplasm_channel = ...
99
110
 
100
111
  if type(cyto_size) not in [int, float] and not do_only_nuc:
101
112
  sg.popup("Incorrect cytoplasm size.")
@@ -106,14 +117,46 @@ def launch_segmentation(image: np.ndarray, user_parameters: dict) :
106
117
  sg.popup('Invalid nucleus model name.')
107
118
  values['nucleus_model_name'] = user_parameters.setdefault('nucleus_model_name', 'nuclei')
108
119
  relaunch= True
109
- if nucleus_channel not in available_channels :
110
- sg.popup('For given input image please select channel in {0}\nnucleus channel : {1}'.format(available_channels, nucleus_channel))
111
- relaunch= True
112
- values['nucleus channel'] = user_parameters.setdefault('nucleus_channel', 0)
120
+
121
+ if multichannel :
122
+ if nucleus_channel not in available_channels :
123
+ sg.popup('For given input image please select channel in {0}\nnucleus channel : {1}'.format(available_channels, nucleus_channel))
124
+ relaunch= True
125
+ values['nucleus channel'] = user_parameters.setdefault('nucleus_channel', 0)
126
+ else :
127
+ nucleus_channel = ...
128
+
113
129
  if type(nucleus_size) not in [int, float] :
114
130
  sg.popup("Incorrect nucleus size.")
115
131
  relaunch= True
116
132
  values['nucleus diameter'] = user_parameters.setdefault('nucleus diameter', 30)
133
+ if other_nucleus_image != '' :
134
+ if not os.path.isfile(other_nucleus_image) :
135
+ sg.popup("Nucleus image is not a file.")
136
+ relaunch=True
137
+ values['other_nucleus_image'] = None
138
+ else :
139
+ try :
140
+ nucleus_image = open_image(other_nucleus_image)
141
+ except Exception as e :
142
+ sg.popup("Could not open image.\n{0}".format(e))
143
+ relaunch=True
144
+ values['other_nucleus_image'] = user_parameters.setdefault('other_nucleus_image', None)
145
+ else :
146
+ if nucleus_image.ndim != image.ndim - multichannel :
147
+ sg.popup("Nucleus image dimension missmatched. Expected same dimension as cytoplasm_image for monochannel or same dimension as cytoplasm_image -1 for multichannel\ncytoplasm dimension : {0}, nucleus dimension : {1}".format(image.ndim, nucleus_image.ndim))
148
+ nucleus_image = None
149
+ relaunch=True
150
+ values['other_nucleus_image'] = user_parameters.setdefault('other_nucleus_image', None)
151
+
152
+ elif nucleus_image.shape != image[cytoplasm_channel] :
153
+ sg.popup("Nucleus image shape missmatched. Expected same shape as cytoplasm_image \ncytoplasm shape : {0}, nucleus shape : {1}".format(image[cytoplasm_channel].shape, nucleus_image.shape))
154
+ nucleus_image = None
155
+ relaunch=True
156
+ values['other_nucleus_image'] = user_parameters.setdefault('other_nucleus_image', None)
157
+
158
+ else :
159
+ nucleus_image = None
117
160
 
118
161
  user_parameters.update(values)
119
162
  if not relaunch : break
@@ -149,14 +192,15 @@ def launch_segmentation(image: np.ndarray, user_parameters: dict) :
149
192
  nucleus_model_name= nucleus_model_name,
150
193
  nucleus_diameter= nucleus_size,
151
194
  channels=channels,
152
- do_only_nuc=do_only_nuc
195
+ do_only_nuc=do_only_nuc,
196
+ external_nucleus_image = nucleus_image,
153
197
  )
154
198
 
155
199
  finally : window.close()
156
200
 
157
201
  if show_segmentation :
158
202
  nucleus_label, cytoplasm_label = napari_show_segmentation(
159
- nuc_image=image[nucleus_channel],
203
+ nuc_image=image[nucleus_channel] if type(nucleus_image) == type(None) else nucleus_image,
160
204
  nuc_label= nucleus_label,
161
205
  cyto_image=image[cytoplasm_channel],
162
206
  cyto_label=cytoplasm_label,
@@ -175,16 +219,30 @@ def launch_segmentation(image: np.ndarray, user_parameters: dict) :
175
219
  continue
176
220
 
177
221
  if type(output_path) != type(None) :
222
+
223
+ #Get backgrounds
178
224
  nuc_proj = image[nucleus_channel]
179
225
  im_proj = image[cytoplasm_channel]
180
226
  if im_proj.ndim == 3 :
181
227
  im_proj = stack.maximum_projection(im_proj)
182
228
  if nuc_proj.ndim == 3 :
183
229
  nuc_proj = stack.maximum_projection(nuc_proj)
230
+
231
+ #Call plots
184
232
  plot.plot_segmentation_boundary(nuc_proj, cytoplasm_label, nucleus_label, boundary_size=2, contrast=True, show=False, path_output=nuc_path, title= "Nucleus segmentation (blue)", remove_frame=True,)
185
233
  if not do_only_nuc :
186
234
  plot.plot_segmentation_boundary(im_proj, cytoplasm_label, nucleus_label, boundary_size=2, contrast=True, show=False, path_output=cyto_path, title="Cytoplasm Segmentation (red)", remove_frame=True)
187
-
235
+ plot_labels(
236
+ nucleus_label,
237
+ path_output=output_path + "_nucleus_label_map.png",
238
+ show=False
239
+ )
240
+ if not do_only_nuc :
241
+ plot_labels(
242
+ cytoplasm_label,
243
+ path_output=output_path + "_cytoplasm_label_map.png",
244
+ show=False
245
+ )
188
246
 
189
247
 
190
248
 
@@ -204,7 +262,14 @@ def launch_segmentation(image: np.ndarray, user_parameters: dict) :
204
262
  user_parameters.update(values)
205
263
  return cytoplasm_label, nucleus_label, user_parameters
206
264
 
207
- def cell_segmentation(image, cyto_model_name, nucleus_model_name, channels, cyto_diameter, nucleus_diameter, do_only_nuc=False) :
265
+ def cell_segmentation(
266
+ image, cyto_model_name,
267
+ nucleus_model_name,
268
+ channels, cyto_diameter,
269
+ nucleus_diameter,
270
+ do_only_nuc=False,
271
+ external_nucleus_image = None,
272
+ ) :
208
273
 
209
274
  nuc_channel = channels[1]
210
275
  if not do_only_nuc :
@@ -214,10 +279,13 @@ def cell_segmentation(image, cyto_model_name, nucleus_model_name, channels, cyto
214
279
  else :
215
280
  cyto = image[cyto_channel]
216
281
 
217
- if image[nuc_channel].ndim >= 3 :
218
- nuc = stack.maximum_projection(image[nuc_channel])
219
- else :
282
+ if type(external_nucleus_image) != type(None) :
283
+ nuc = external_nucleus_image
284
+ else :
220
285
  nuc = image[nuc_channel]
286
+
287
+ if nuc.ndim >= 3 :
288
+ nuc = stack.maximum_projection(nuc)
221
289
 
222
290
  if not do_only_nuc :
223
291
  image = np.zeros(shape=(2,) + cyto.shape)
@@ -351,3 +419,84 @@ def remove_disjoint(image):
351
419
  image_cleaned = image_cleaned.astype(bool)
352
420
 
353
421
  return image_cleaned
422
+
423
+ def plot_segmentation(
424
+ cyto_image : np.ndarray,
425
+ cyto_label : np.ndarray,
426
+ nuc_image : np.ndarray,
427
+ nuc_label : np.ndarray,
428
+ path :str,
429
+ do_only_nuc=False
430
+ ) :
431
+
432
+ if nuc_image.ndim == 3 :
433
+ nuc_image = np.max(nuc_image,axis=0)
434
+
435
+ plot.plot_segmentation_boundary(
436
+ image=nuc_image,
437
+ nuc_label= nuc_label,
438
+ boundary_size= 3,
439
+ contrast=True,
440
+ path_output=path + "_nuclei_segmentation.png",
441
+ show=False,
442
+ )
443
+
444
+
445
+ if not do_only_nuc :
446
+ if cyto_image.ndim == 3 :
447
+ cyto_image = np.max(cyto_image,axis=0)
448
+
449
+ plot.plot_segmentation_boundary(
450
+ image=cyto_image,
451
+ cell_label= cyto_label,
452
+ nuc_label= nuc_label,
453
+ boundary_size= 3,
454
+ contrast=True,
455
+ path_output=path + "_cytoplasm_segmentation.png",
456
+ show=False,
457
+ )
458
+
459
+ def plot_labels(labelled_image: np.ndarray, path_output:str = None, show= True, axis= False, close= True):
460
+ """
461
+ Plot a labelled image and indicate the label number at the center of each region.
462
+ """
463
+ stack.check_parameter(labelled_image = (np.ndarray, list), show = (bool))
464
+ if isinstance(labelled_image, np.ndarray) :
465
+ stack.check_array(labelled_image, ndim= 2)
466
+ labelled_image = [labelled_image]
467
+
468
+ #Setting a colormap with background to white so all cells can be visible
469
+ viridis = mpl.colormaps['viridis'].resampled(256)
470
+ newcolors = viridis(np.linspace(0, 1, 256))
471
+ white = np.array([1, 1, 1, 1])
472
+ newcolors[0, :] = white
473
+ newcmp = ListedColormap(newcolors)
474
+
475
+ plt.figure(figsize= (10,10))
476
+ rescaled_image = stack.rescale(np.array(labelled_image[0], dtype= np.int32), channel_to_stretch= 0)
477
+ rescaled_image[rescaled_image == 0] = -100
478
+ plot = plt.imshow(rescaled_image, cmap=newcmp)
479
+ plot.axes.get_xaxis().set_visible(axis)
480
+ plot.axes.get_yaxis().set_visible(axis)
481
+ plt.tight_layout()
482
+
483
+ for index in range(0, len(labelled_image)) :
484
+ centroid_dict = from_label_get_centeroidscoords(labelled_image[index])
485
+ labels = centroid_dict["label"]
486
+ Y = centroid_dict["centroid-0"]
487
+ X = centroid_dict["centroid-1"]
488
+ centroids = zip(Y,X)
489
+
490
+ for label in labels :
491
+ y,x = next(centroids)
492
+ y,x = round(y), round(x)
493
+ an = plt.annotate(str(label), [round(x), round(y)])
494
+
495
+ if not axis : plt.cla
496
+ if show : plt.show()
497
+ if path_output != None :
498
+ stack.check_parameter(path_output = (str))
499
+ plt.savefig(path_output)
500
+ if close : plt.close()
501
+
502
+ return plot
@@ -1,3 +1,7 @@
1
+ """
2
+ This submodule groups all the possible actions of the user in the main windows. It is the start of each action the user can do.
3
+ """
4
+
1
5
  from ..gui.prompts import output_image_prompt, ask_detection_confirmation, ask_cancel_detection
2
6
  from ..interface.output import write_results
3
7
  from ._preprocess import map_channels, prepare_image_detection, reorder_shape, reorder_image_stack
@@ -25,6 +29,8 @@ def add_detection(user_parameters, segmentation_done, acquisition_id, cytoplasm_
25
29
  user_parameters.update(new_parameters)
26
30
 
27
31
  map = map_channels(user_parameters)
32
+ if type(map) == type(None) : #User clicks Cancel
33
+ return new_results_df, new_cell_results_df, acquisition_id, user_parameters, segmentation_done, cytoplasm_label, nucleus_label
28
34
  user_parameters['reordered_shape'] = reorder_shape(user_parameters['shape'], map)
29
35
 
30
36
 
@@ -89,7 +95,6 @@ def add_detection(user_parameters, segmentation_done, acquisition_id, cytoplasm_
89
95
  if user_parameters['spots_extraction_folder'] != '' and type(user_parameters['spots_extraction_folder']) != type(None) :
90
96
  if user_parameters['spots_filename'] != '' and type(user_parameters['spots_filename']) != type(None) :
91
97
  if any((user_parameters['do_spots_excel'], user_parameters['do_spots_csv'], user_parameters['do_spots_feather'])) :
92
- print((user_parameters['do_spots_excel'], user_parameters['do_spots_csv'], user_parameters['do_spots_feather']))
93
98
  launch_spots_extraction(
94
99
  acquisition_id=acquisition_id,
95
100
  user_parameters=user_parameters,
@@ -1,7 +1,6 @@
1
1
  """
2
2
  Contains code to handle detection as well as bigfish wrappers related to spot detection.
3
3
  """
4
-
5
4
  from ._preprocess import ParameterInputError
6
5
  from ._preprocess import check_integrity, convert_parameters_types
7
6
  from ._signaltonoise import compute_snr_spots
@@ -26,6 +25,7 @@ import bigfish.classification as classification
26
25
  from bigfish.detection.spot_detection import get_object_radius_pixel
27
26
  from types import GeneratorType
28
27
  from skimage.measure import regionprops
28
+ from scipy.ndimage import binary_dilation
29
29
 
30
30
 
31
31
  def ask_input_parameters(ask_for_segmentation=True) :
@@ -278,11 +278,18 @@ def initiate_detection(user_parameters, segmentation_done, map, shape) :
278
278
  segmentation_done= segmentation_done,
279
279
  default_dict=user_parameters
280
280
  )
281
-
282
281
  if type(user_parameters) == type(None) : return user_parameters
283
282
  try :
284
283
  user_parameters = convert_parameters_types(user_parameters)
285
- user_parameters = check_integrity(user_parameters, do_dense_region_deconvolution, is_multichannel, segmentation_done, map, shape)
284
+ user_parameters = check_integrity(
285
+ user_parameters,
286
+ do_dense_region_deconvolution,
287
+ do_clustering,
288
+ is_multichannel,
289
+ segmentation_done,
290
+ map,
291
+ shape
292
+ )
286
293
  except ParameterInputError as error:
287
294
  sg.popup(error)
288
295
  else :
@@ -306,7 +313,7 @@ def _launch_detection(image, image_input_values: dict) :
306
313
  threshold_user_selection = image_input_values.get('Interactive threshold selector')
307
314
 
308
315
  if type(threshold) == type(None) :
309
- threshold = compute_auto_threshold(image, voxel_size=voxel_size, spot_radius=spot_size, log_kernel_size=log_kernel_size, minimum_distance=minimum_distance) * threshold_penalty
316
+ threshold = threshold_penalty * compute_auto_threshold(image, voxel_size=voxel_size, spot_radius=spot_size, log_kernel_size=log_kernel_size, minimum_distance=minimum_distance)
310
317
 
311
318
  filtered_image = _apply_log_filter(
312
319
  image=image,
@@ -403,7 +410,7 @@ def launch_post_detection(image, spots, image_input_values: dict,) :
403
410
  #appending results
404
411
  fov_res.update(snr_res)
405
412
 
406
- return spots, fov_res
413
+ return fov_res
407
414
 
408
415
  def _compute_cell_snr(image: np.ndarray, bbox, spots, voxel_size, spot_size) :
409
416
 
@@ -592,7 +599,8 @@ def launch_detection(
592
599
  other_image,
593
600
  user_parameters,
594
601
  cell_label= None,
595
- nucleus_label = None
602
+ nucleus_label = None,
603
+ hide_loading=False,
596
604
  ) :
597
605
  """
598
606
  Main call for features computation :
@@ -618,18 +626,17 @@ def launch_detection(
618
626
  do_dense_region_deconvolution = user_parameters['Dense regions deconvolution']
619
627
  do_clustering = user_parameters['Cluster computation']
620
628
 
621
- spots, threshold = _launch_detection(image, user_parameters)
629
+ spots, threshold = _launch_detection(image, user_parameters, hide_loading = hide_loading)
622
630
 
623
631
  if do_dense_region_deconvolution :
624
- spots = launch_dense_region_deconvolution(image, spots, user_parameters)
632
+ spots = launch_dense_region_deconvolution(image, spots, user_parameters, hide_loading = hide_loading)
625
633
 
626
634
  if do_clustering :
627
- clusters = launch_clustering(spots, user_parameters) #012 are coordinates #3 is number of spots per cluster, #4 is cluster index
635
+ clusters = launch_clustering(spots, user_parameters, hide_loading = hide_loading) #012 are coordinates #3 is number of spots per cluster, #4 is cluster index
628
636
  clusters = _update_clusters(clusters, spots, voxel_size=user_parameters['voxel_size'], cluster_size=user_parameters['cluster size'], min_spot_number= user_parameters['min number of spots'], shape=image.shape)
629
637
 
630
638
  else : clusters = None
631
639
 
632
- spots, post_detection_dict = launch_post_detection(image, spots, user_parameters)
633
640
  user_parameters['threshold'] = threshold
634
641
 
635
642
  if user_parameters['Napari correction'] :
@@ -645,7 +652,7 @@ def launch_detection(
645
652
  nucleus_label=nucleus_label,
646
653
  other_images=other_image
647
654
  )
648
-
655
+ post_detection_dict = launch_post_detection(image, spots, user_parameters, hide_loading = hide_loading)
649
656
  fov_result.update(post_detection_dict)
650
657
 
651
658
  return user_parameters, fov_result, spots, clusters
@@ -773,12 +780,13 @@ def _create_threshold_slider(
773
780
  'size': 5,
774
781
  'scale' : scale,
775
782
  'face_color' : 'transparent',
776
- 'edge_color' : 'blue',
777
- 'symbol' : 'ring',
783
+ 'edge_color' : 'red',
784
+ 'symbol' : 'disc',
778
785
  'opacity' : 0.7,
779
- 'blending' : 'additive',
786
+ 'blending' : 'translucent',
780
787
  'name': 'single spots',
781
- 'features' : {'threshold' : threshold}
788
+ 'features' : {'threshold' : threshold},
789
+ 'visible' : True,
782
790
  }
783
791
  return (spots, layer_args , 'points')
784
792
  return threshold_slider
@@ -825,4 +833,55 @@ def _local_maxima_mask(
825
833
  ndim=ndim)
826
834
  mask_local_max = detection.local_maximum_detection(image_filtered, minimum_distance)
827
835
 
828
- return mask_local_max.astype(bool)
836
+ return mask_local_max.astype(bool)
837
+
838
+ def output_spot_tiffvisual(channel,spots_list, path_output, dot_size = 3, rescale = True):
839
+
840
+ """
841
+ Outputs a tiff image with one channel being {channel} and the other a mask containing dots where sports are located.
842
+
843
+ Parameters
844
+ ----------
845
+ channel : np.ndarray
846
+ 3D monochannel image
847
+ spots : list[np.ndarray] or np.ndarray
848
+ Spots arrays are ndarray where each element corresponds is a tuple(z,y,x) corresponding to 3D coordinate of a spot
849
+ To plot different spots on different channels a list of spots ndarray can be passed.
850
+ path_output : str
851
+ dot_size : int
852
+ in pixels
853
+ """
854
+
855
+ stack.check_parameter(channel = (np.ndarray), spots_list= (list, np.ndarray), path_output = (str), dot_size = (int))
856
+ stack.check_array(channel, ndim= [2,3])
857
+ if isinstance(spots_list, np.ndarray) : spots_list = [spots_list]
858
+
859
+ if channel.ndim == 3 :
860
+ channel = stack.maximum_projection(channel)
861
+
862
+ im = np.zeros([1 + len(spots_list)] + list(channel.shape))
863
+ im[0,:,:] = channel
864
+
865
+ for level in range(len(spots_list)) :
866
+ if len(spots_list[level]) == 0 : continue
867
+ else :
868
+ spots_mask = np.zeros_like(channel)
869
+
870
+ #Unpacking spots
871
+ if len(spots_list[level][0]) == 2 :
872
+ Y,X = zip(*spots_list[level])
873
+ elif len(spots_list[level][0]) == 3 :
874
+ Z,Y,X = zip(*spots_list[level])
875
+ del Z
876
+ else :
877
+ Z,Y,X,*_ = zip(*spots_list[level])
878
+ del Z,_
879
+
880
+ #Reconstructing signal
881
+ spots_mask[Y,X] = 1
882
+ if dot_size > 1 : spots_mask = binary_dilation(spots_mask, iterations= dot_size-1)
883
+ spots_mask = stack.rescale(np.array(spots_mask, dtype = channel.dtype))
884
+ im[level + 1] = spots_mask
885
+
886
+ if rescale : channel = stack.rescale(channel, channel_to_stretch= 0)
887
+ stack.save_image(im, path_output, extension= 'tif')