small-fish-gui 1.4.0__py3-none-any.whl → 1.6.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.
@@ -0,0 +1,21 @@
1
+ # .readthedocs.yaml
2
+ # Read the Docs configuration file
3
+ # See for details
4
+
5
+ # Required
6
+ version: 2
7
+
8
+ # Set the OS, Python version and other tools you might need
9
+ build:
10
+ os: ubuntu-22.04
11
+ tools:
12
+ python: "3.8"
13
+
14
+ # Build documentation in the "docs/" directory with Sphinx
15
+ sphinx:
16
+ configuration: docs/conf.py
17
+
18
+ # Optionally build your docs in additional formats such as PDF and ePub
19
+ formats:
20
+ - pdf
21
+ - epub
small_fish_gui/README.md CHANGED
@@ -5,7 +5,7 @@ Cell segmentation (**2D**) is peformed using *cellpose* (published work) : https
5
5
 
6
6
  Spot detection is performed via *big-fish* (published work) : https://github.com/fish-quant/big-fish
7
7
 
8
- Time stacks are not yet supported.
8
+ **Time stacks are not supported.**
9
9
 
10
10
  ## What can you do with small fish ?
11
11
 
@@ -77,7 +77,7 @@ If you run into any problems I would recommend following the official cellpose i
77
77
 
78
78
 
79
79
  ### Training cellpose
80
- If you want to train your own cellpose model or import custom model from exterior source I recommend doing so from the cellpose GUI
80
+ If you want to train your own cellpose model or import custom model from exterior source I recommend doing so from the cellpose GUI. Note that Small fish uses mean projection to segment images in 2D, if you want to retrain a cellpose model to fit your data it is recommended to do so on mean or max projection of your data.
81
81
 
82
82
  To install the GUI run :
83
83
 
@@ -93,6 +93,5 @@ Note that for training it is recommended to first set up your GPU as training co
93
93
  ## Developpement
94
94
 
95
95
  Optional features to include in future versions :
96
- - batch processing
97
96
  - time stack (which would include cell tracking)
98
97
  - 3D segmentation
@@ -38,4 +38,4 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38
38
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
39
 
40
40
  """
41
- __version__ = "1.4.0"
41
+ __version__ = "1.6.0"
@@ -1,6 +1,8 @@
1
1
  import sys, subprocess
2
2
  import PySimpleGUI as sg
3
3
 
4
+ from small_fish_gui import __version__
5
+
4
6
  def main():
5
7
  import small_fish_gui.pipeline.main
6
8
 
@@ -10,9 +12,7 @@ def is_last_version() :
10
12
  latest_version = latest_version[:latest_version.find(')')]
11
13
  latest_version = latest_version.replace(' ','').split(',')[-1]
12
14
 
13
- current_version = str(subprocess.run([sys.executable, '-m', 'pip', 'show', '{}'.format('small_fish_gui')], capture_output=True, text=True))
14
- current_version = current_version[current_version.find('Version:')+8:]
15
- current_version = current_version[:current_version.find('\\n')].replace(' ','')
15
+ current_version = __version__
16
16
 
17
17
  return current_version == latest_version
18
18
 
@@ -35,7 +35,7 @@ def batch_promp(
35
35
  sanity_header = sg.Text("Dimension sanity", font=('bold',15), pad=(0,10))
36
36
  dimension_number_text = sg.Text("Dimension number : unknown")
37
37
 
38
-
38
+ #LAYOUT INIT
39
39
  #########################################
40
40
  ##### COLUMNS
41
41
  #########################################
@@ -182,8 +182,6 @@ def batch_promp(
182
182
  [tab_col, launch_col],
183
183
  [stream_output],
184
184
  ]
185
- stream_output.restore_stderr()
186
- stream_output.restore_stdout()
187
185
 
188
186
  window = sg.Window("small fish", layout=layout, size= (800,800), auto_size_buttons=True, auto_size_text=True, resizable=True)
189
187
 
@@ -332,7 +330,11 @@ def batch_promp(
332
330
  batch_name_input.update(value=values.get('batch_name'))
333
331
 
334
332
  elif event == "Cancel" :
335
- raise InterruptedError("cancel")
333
+ stream_output.restore_stderr()
334
+ stream_output.restore_stdout()
335
+ window.close()
336
+ return results_df, cell_results_df, acquisition_id, preset, False, None,None #Segmentation done : False, cell label : None, Nucleus label : None
337
+
336
338
 
337
339
  elif event == None :
338
340
  raise InterruptedError("closed")
@@ -0,0 +1 @@
1
+ #init conf file
@@ -95,7 +95,7 @@ def path_layout(keys= [],look_for_dir = False, header=None, preset=os.getcwd())
95
95
 
96
96
  max_length = len(max(keys, key=len))
97
97
  layout = [
98
- [sg.Text(pad_right(name, max_length, ' ')), Browse(key= name, initial_folder= preset)] for name in keys
98
+ [sg.Text(pad_right(name, max_length, ' ')), Browse(key= name, target=(555666777,2), initial_folder= preset), sg.Text('')] for name in keys
99
99
  ]
100
100
  if isinstance(header, str) :
101
101
  layout = [add_header(header)] + layout
@@ -152,7 +152,20 @@ def radio_layout(values, header=None) :
152
152
  layout = [add_header(header)] + layout
153
153
  return layout
154
154
 
155
- def _segmentation_layout(multichannel, cytoplasm_model_preset= 'cyto2', nucleus_model_preset= 'nuclei', cytoplasm_channel_preset=0, nucleus_channel_preset=0, cyto_diameter_preset=30, nucleus_diameter_preset= 30, show_segmentation_preset= False, segment_only_nuclei_preset=False, saving_path_preset=os.getcwd(), filename_preset='cell_segmentation.png',) :
155
+ def _segmentation_layout(
156
+ multichannel,
157
+ cytoplasm_model_preset= 'cyto3',
158
+ nucleus_model_preset= 'nuclei',
159
+ cytoplasm_channel_preset=0,
160
+ nucleus_channel_preset=0,
161
+ other_nucleus_image_preset = None,
162
+ cyto_diameter_preset=30,
163
+ nucleus_diameter_preset= 30,
164
+ show_segmentation_preset= False,
165
+ segment_only_nuclei_preset=False,
166
+ saving_path_preset=os.getcwd(),
167
+ filename_preset='cell_segmentation.png',
168
+ ) :
156
169
 
157
170
  USE_GPU = use_gpu()
158
171
 
@@ -160,7 +173,9 @@ def _segmentation_layout(multichannel, cytoplasm_model_preset= 'cyto2', nucleus_
160
173
  if len(models_list) == 0 : models_list = ['no model found']
161
174
 
162
175
  #Header : GPU availabality
163
- layout = [[sg.Text("GPU is currently "), sg.Text('ON', text_color= 'green') if USE_GPU else sg.Text('OFF', text_color= 'red')]]
176
+ layout = [
177
+ [sg.Text("GPU is currently "), sg.Text('ON', text_color= 'green') if USE_GPU else sg.Text('OFF', text_color= 'red')]
178
+ ]
164
179
 
165
180
  #cytoplasm parameters
166
181
  layout += [
@@ -170,6 +185,8 @@ def _segmentation_layout(multichannel, cytoplasm_model_preset= 'cyto2', nucleus_
170
185
  ]
171
186
 
172
187
  if multichannel : layout += parameters_layout(['cytoplasm channel'],default_values= [cytoplasm_channel_preset])
188
+
189
+
173
190
  layout += parameters_layout(['cytoplasm diameter'], unit= "px", default_values= [cyto_diameter_preset])
174
191
  #Nucleus parameters
175
192
  layout += [
@@ -179,6 +196,7 @@ def _segmentation_layout(multichannel, cytoplasm_model_preset= 'cyto2', nucleus_
179
196
  ]
180
197
 
181
198
  if multichannel : layout += parameters_layout(['nucleus channel'], default_values= [nucleus_channel_preset])
199
+ layout += path_layout(['other_nucleus_image'], preset=other_nucleus_image_preset)
182
200
  layout += parameters_layout([ 'nucleus diameter'],unit= "px", default_values= [nucleus_diameter_preset])
183
201
  layout += bool_layout(["Segment only nuclei"], preset=segment_only_nuclei_preset)
184
202
 
@@ -32,14 +32,14 @@ def prompt(layout, add_ok_cancel=True, timeout=None, timeout_key='TIMEOUT_KEY',
32
32
  window.close()
33
33
  return event, values
34
34
 
35
- def prompt_with_help(layout, help =None, add_scrollbar=True) :
35
+ def prompt_with_help(layout, help =None, add_scrollbar=True, vertical_scroll_only=True) :
36
36
  layout += [[]]
37
37
  layout += [[sg.Button('Help')]]
38
38
  layout += [[sg.Button('Ok'), sg.Button('Cancel')]]
39
39
 
40
40
  if add_scrollbar :
41
41
  size = (400,500)
42
- col_elmt = sg.Column(layout, scrollable=True, vertical_scroll_only=True, size=size)
42
+ col_elmt = sg.Column(layout, scrollable=True, vertical_scroll_only=vertical_scroll_only, size=size)
43
43
  layout = [[col_elmt]]
44
44
  else :
45
45
  size = (None,None)
@@ -280,11 +280,13 @@ def _warning_popup(warning:str) :
280
280
 
281
281
  def _sumup_df(results: pd.DataFrame) :
282
282
 
283
+ COLUMNS = ['acquisition_id','name','threshold', 'spot_number', 'cell_number', 'filename', 'channel to compute']
284
+
283
285
  if len(results) > 0 :
284
286
  if 'channel to compute' not in results : results['channel to compute'] = np.NaN
285
- res = results.loc[:,['acquisition_id', 'spot_number', 'cell_number', 'filename', 'channel to compute']]
287
+ res = results.loc[:,COLUMNS]
286
288
  else :
287
- res = pd.DataFrame(columns= ['acquisition_id', 'spot_number', 'cell_number', 'filename', 'channel to compute'])
289
+ res = pd.DataFrame(columns= COLUMNS)
288
290
 
289
291
  return res
290
292
 
@@ -293,15 +295,15 @@ def hub_prompt(fov_results, do_segmentation=False) :
293
295
  sumup_df = _sumup_df(fov_results)
294
296
 
295
297
  if do_segmentation :
296
- segmentation_object = sg.Text('Segmentation was performed', font='8', text_color= 'green')
298
+ segmentation_object = sg.Text('Segmentation in memory', font='8', text_color= 'green')
297
299
  else :
298
- segmentation_object = sg.Text('Segmentation was not performed', font='8', text_color= 'red')
300
+ segmentation_object = sg.Text('No segmentation in memory', font='8', text_color= 'red')
299
301
 
300
302
  layout = [
301
303
  [sg.Text('RESULTS', font= 'bold 13')],
302
304
  [sg.Table(values= list(sumup_df.values), headings= list(sumup_df.columns), row_height=20, num_rows= 5, vertical_scroll_only=False, key= "result_table"), segmentation_object],
303
305
  [sg.Button('Add detection'), sg.Button('Compute colocalisation'), sg.Button('Batch detection')],
304
- [sg.Button('Save results', button_color= 'green'), sg.Button('Delete acquisitions',button_color= 'gray'), sg.Button('Reset segmentation',button_color= 'gray'), sg.Button('Reset results',button_color= 'gray')]
306
+ [sg.Button('Rename acquisition', button_color= 'green'), sg.Button('Save results', button_color= 'green'), sg.Button('Delete acquisitions',button_color= 'gray'), sg.Button('Reset segmentation',button_color= 'gray'), sg.Button('Reset results',button_color= 'gray')]
305
307
  # [sg.Button('Save results', button_color= 'green'), sg.Button('Reset results',button_color= 'gray')]
306
308
  ]
307
309
 
@@ -316,16 +318,20 @@ def hub_prompt(fov_results, do_segmentation=False) :
316
318
  return event, values
317
319
 
318
320
  def coloc_prompt() :
319
- layout = [
320
- [parameters_layout(['colocalisation distance'], header= 'Colocalisation', default_values= 0)]
321
- ]
322
-
321
+ layout = parameters_layout(['colocalisation distance'], unit= 'nm', header= 'Colocalisation', default_values= 0)
323
322
  event, values = prompt_with_help(layout)
324
323
 
325
324
  if event == 'Ok' :
326
325
  return values['colocalisation distance']
327
326
  else : return False
328
327
 
328
+ def rename_prompt() :
329
+ layout = parameters_layout(['name'], header= "Rename acquisitions", size=12)
330
+ event, values = prompt_with_help(layout)
331
+ if event == 'Ok' :
332
+ return values['name']
333
+ else : return False
334
+
329
335
  def ask_detection_confirmation(used_threshold) :
330
336
  layout = [
331
337
  [sg.Text("Proceed with current detection ?", font= 'bold 10')],
@@ -263,4 +263,13 @@ def launch_colocalisation(result_tables, result_dataframe, colocalisation_distan
263
263
  'fraction_spots1_coloc_cluster2' : [fraction_spots1_coloc_cluster2],
264
264
  })
265
265
 
266
+ coloc_df['fraction_spots1_coloc_free2'] = coloc_df['fraction_spots1_coloc_spots2'] - coloc_df['fraction_spots1_coloc_cluster2']
267
+ coloc_df['fraction_spots2_coloc_free1'] = coloc_df['fraction_spots2_coloc_spots1'] - coloc_df['fraction_spots2_coloc_cluster1']
268
+
269
+ #Add names
270
+ coloc_df_col = list(coloc_df.columns)
271
+ coloc_df['name1'] = acquisition1.at['name']
272
+ coloc_df['name2'] = acquisition2.at['name']
273
+ coloc_df = coloc_df.loc[:,['name1','name2'] + coloc_df_col]
274
+
266
275
  return coloc_df
@@ -2,69 +2,33 @@
2
2
  Contains Napari wrappers to visualise and correct spots/clusters.
3
3
  """
4
4
 
5
+ import napari.layers
6
+ import napari.types
5
7
  import numpy as np
6
- import scipy.ndimage as ndi
7
8
  import napari
8
9
 
9
- from napari.utils.events import Event
10
- from napari.layers import Points
11
10
  from bigfish.stack import check_parameter
12
11
  from ..utils import compute_anisotropy_coef
13
12
  from ._colocalisation import spots_multicolocalisation
14
13
 
15
- class Points_callback :
16
- """
17
- Custom class to handle points number evolution during Napari run.
18
- """
19
-
20
- def __init__(self, points, next_id) -> None:
21
- self.points = points
22
- self.next_id = next_id
23
- self._set_callback()
24
-
25
- def __str__(self) -> str:
26
- string = 'Points_callback object state :\ncurrent_points_number : {0}\ncurrnet_id : {1}'.format(self.current_points_number, self.next_id)
27
- return string
28
-
29
- def get_points(self) :
30
- return self.points
31
-
32
- def get_next_id(self) :
33
- return self.next_id
34
-
35
- def _set_callback(self) :
36
- def callback(event:Event) :
37
-
38
- old_points = self.get_points()
39
- new_points:Points = event.source.data
40
- features = event.source.features
41
-
42
- current_point_number = len(old_points)
43
- next_id = self.get_next_id()
44
- new_points_number = len(new_points)
45
-
46
- if new_points_number > current_point_number :
47
- features.at[new_points_number - 1, "id"] = next_id
48
- self.next_id += 1
49
-
50
- #preparing next callback
51
- self.points = new_points
52
- self._set_callback()
53
- self.callback = callback
54
-
55
14
  def _update_clusters(new_clusters: np.ndarray, spots: np.ndarray, voxel_size, cluster_size, min_spot_number, shape) :
56
15
  if len(new_clusters) == 0 : return new_clusters
57
- if len(spots) == 0 : return new_clusters
16
+ if len(spots) == 0 : return np.empty(shape=(0,2+len(voxel_size)))
17
+
18
+ if len(new_clusters[0]) in [2,3] :
19
+ new_clusters = np.concatenate([
20
+ new_clusters,
21
+ np.zeros(shape=(len(new_clusters),1), dtype=int),
22
+ np.arange(len(new_clusters), dtype=int).reshape(len(new_clusters),1)
23
+ ],axis=1, dtype=int)
24
+
58
25
  assert len(new_clusters[0]) == 4 or len(new_clusters[0]) == 5, "Wrong number of coordinates for clusters should not happen."
59
26
 
60
27
  # Update spots clusters
61
- if len(voxel_size) == 3 :
62
- new_clusters[:,-2] = spots_multicolocalisation(new_clusters[:,:3], spots, radius_nm= cluster_size, voxel_size=voxel_size, image_shape=shape)
63
- elif len(voxel_size) == 2 :
64
- new_clusters[:,-2] = spots_multicolocalisation(new_clusters[:,:2], spots, radius_nm= cluster_size, voxel_size=voxel_size, image_shape=shape)
28
+ new_clusters[:,-2] = spots_multicolocalisation(new_clusters[:,:-2], spots, radius_nm= cluster_size, voxel_size=voxel_size, image_shape=shape)
65
29
 
66
30
  # delete too small clusters
67
- new_clusters = np.delete(new_clusters, new_clusters[:,-2] < min_spot_number, 0)
31
+ new_clusters = np.delete(new_clusters, new_clusters[:,-2] < min_spot_number, 0)
68
32
 
69
33
  return new_clusters
70
34
 
@@ -93,45 +57,47 @@ def correct_spots(image, spots, voxel_size= (1,1,1), clusters= None, cluster_siz
93
57
  )
94
58
 
95
59
  scale = compute_anisotropy_coef(voxel_size)
96
- try :
97
- Viewer = napari.Viewer(ndisplay=2, title= 'Spot correction', axis_labels=['z','y','x'], show= False)
98
- Viewer.add_image(image, scale=scale, name= "rna signal", blending= 'additive', colormap='red', contrast_limits=[image.min(), image.max()])
99
- other_colors = ['green', 'blue', 'gray', 'cyan', 'bop orange', 'bop purple'] * ((len(other_images)-1 // 7) + 1)
100
- for im, color in zip(other_images, other_colors) :
101
- Viewer.add_image(im, scale=scale, blending='additive', visible=False, colormap=color, contrast_limits=[im.min(), im.max()])
102
- layer_offset = len(other_images)
103
-
104
- Viewer.add_points(spots, size = 5, scale=scale, face_color= 'green', opacity= 1, symbol= 'ring', name= 'single spots') # spots
105
- if type(clusters) != type(None) : Viewer.add_points(clusters[:,:dim], size = 10, scale=scale, face_color= 'blue', opacity= 0.7, symbol= 'diamond', name= 'foci', features= {"spot_number" : clusters[:,dim], "id" : clusters[:,dim+1]}, feature_defaults= {"spot_number" : 0, "id" : -1}) # cluster
106
- if type(cell_label) != type(None) and not np.array_equal(nucleus_label, cell_label) : Viewer.add_labels(cell_label, scale=scale, opacity= 0.2, blending= 'additive')
107
- if type(nucleus_label) != type(None) : Viewer.add_labels(nucleus_label, scale=scale, opacity= 0.2, blending= 'additive')
108
-
109
- #prepare cluster update
110
- if type(clusters) != type(None) :
111
- next_cluster_id = clusters[-1,-1] + 1 if len(clusters) > 0 else 1
112
- _callback = Points_callback(points=clusters[:dim], next_id=next_cluster_id)
113
- points_callback = Viewer.layers[2 + layer_offset].events.data.connect((_callback, 'callback'))
114
- Viewer.show(block=False)
115
- napari.run()
116
-
117
-
118
- new_spots = np.array(Viewer.layers[1 + layer_offset].data, dtype= int)
60
+ Viewer = napari.Viewer(ndisplay=2, title= 'Spot correction', axis_labels=['z','y','x'], show= False)
61
+ Viewer.add_image(image, scale=scale, name= "rna signal", blending= 'additive', colormap='red', contrast_limits=[image.min(), image.max()])
62
+ other_colors = ['green', 'blue', 'gray', 'cyan', 'bop orange', 'bop purple'] * ((len(other_images)-1 // 7) + 1)
63
+ for im, color in zip(other_images, other_colors) :
64
+ Viewer.add_image(im, scale=scale, blending='additive', visible=False, colormap=color, contrast_limits=[im.min(), im.max()])
65
+ layer_offset = len(other_images)
66
+
67
+ Viewer.add_points( # single molecule spots; this layer can be update by user.
68
+ spots,
69
+ size = 5,
70
+ scale=scale,
71
+ face_color= 'transparent',
72
+ opacity= 1,
73
+ symbol= 'disc',
74
+ name= 'single spots'
75
+ )
76
+
77
+ if type(clusters) != type(None) : Viewer.add_points( # cluster; this layer can be update by user.
78
+ clusters[:,:dim],
79
+ size = 10,
80
+ scale=scale,
81
+ face_color= 'blue',
82
+ opacity= 0.7,
83
+ symbol= 'diamond',
84
+ name= 'foci',
85
+ features= {"spot_number" : clusters[:,dim], "id" : clusters[:,dim+1]},
86
+ feature_defaults= {"spot_number" : 0, "id" : -1}
87
+ )
119
88
 
120
- if type(clusters) != type(None) :
121
- if len(clusters) > 0 :
122
- new_clusters = np.concatenate([
123
- np.array(Viewer.layers[2 + layer_offset].data, dtype= int),
124
- np.array(Viewer.layers[2 + layer_offset].features, dtype= int)
125
- ],
126
- axis= 1)
89
+ if type(cell_label) != type(None) and not np.array_equal(nucleus_label, cell_label) : Viewer.add_labels(cell_label, scale=scale, opacity= 0.2, blending= 'additive')
90
+ if type(nucleus_label) != type(None) : Viewer.add_labels(nucleus_label, scale=scale, opacity= 0.2, blending= 'additive')
91
+
92
+ Viewer.show(block=False)
93
+ napari.run()
127
94
 
128
- new_clusters = _update_clusters(new_clusters, new_spots, voxel_size=voxel_size, cluster_size=cluster_size, min_spot_number=min_spot_number, shape=image.shape)
129
- else : new_clusters = None
95
+ new_spots = np.array(Viewer.layers['single spots'].data, dtype= int)
130
96
 
131
- except Exception as error :
132
- new_spots = spots
133
- new_clusters = clusters
134
- raise error
97
+ if type(clusters) != type(None) :
98
+ new_clusters = np.array(Viewer.layers['foci'].data, dtype= int)
99
+ new_clusters = _update_clusters(new_clusters, new_spots, voxel_size=voxel_size, cluster_size=cluster_size, min_spot_number=min_spot_number, shape=image.shape)
100
+ else : new_clusters = None
135
101
 
136
102
  return new_spots, new_clusters
137
103
 
@@ -168,25 +134,27 @@ def show_segmentation(
168
134
  Viewer = napari.Viewer(ndisplay=2, title= 'Show segmentation', axis_labels=['z','y','x'] if dim == 3 else ['y', 'x'], show= False)
169
135
 
170
136
  # Adding channels
171
- Viewer.add_image(nuc_image, name= "nucleus signal", blending= 'additive', colormap='blue', contrast_limits=[nuc_image.min(), nuc_image.max()])
172
- Viewer.add_labels(nuc_label, opacity= 0.5, blending= 'additive')
137
+ nuc_signal_layer = Viewer.add_image(nuc_image, name= "nucleus signal", blending= 'additive', colormap='blue', contrast_limits=[nuc_image.min(), nuc_image.max()])
138
+ nuc_label_layer = Viewer.add_labels(nuc_label, opacity= 0.5, blending= 'additive', name= 'nucleus_label',)
139
+ nuc_label_layer.preserve_labels = True
173
140
 
174
141
  #Adding labels
175
142
  if type(cyto_image) != type(None) : Viewer.add_image(cyto_image, name= "cytoplasm signal", blending= 'additive', colormap='red', contrast_limits=[cyto_image.min(), cyto_image.max()])
176
- if type(cyto_label) != type(None) and not np.array_equal(cyto_label, nuc_label): Viewer.add_labels(cyto_label, opacity= 0.4, blending= 'additive')
143
+ 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):
144
+ cyto_label_layer = Viewer.add_labels(cyto_label, opacity= 0.4, blending= 'additive', name= 'cytoplasm_label')
145
+ cyto_label_layer.preserve_labels = True
177
146
 
178
147
  #Launch Napari
179
148
  Viewer.show(block=False)
180
149
  napari.run()
181
150
 
182
- new_nuc_label = Viewer.layers[1].data
183
- if type(cyto_label) != type(None) and not np.array_equal(cyto_label, nuc_label) : new_cyto_label = Viewer.layers[3].data
151
+ new_nuc_label = Viewer.layers['nucleus_label'].data
152
+ if 'cytoplasm_label' in Viewer.layers :
153
+ new_cyto_label = Viewer.layers['cytoplasm_label'].data
184
154
  else : new_cyto_label = new_nuc_label
185
155
 
186
156
  return new_nuc_label, new_cyto_label
187
157
 
188
-
189
-
190
158
  def threshold_selection(
191
159
  image : np.ndarray,
192
160
  filtered_image : np.ndarray,
@@ -195,7 +163,7 @@ def threshold_selection(
195
163
  ) :
196
164
 
197
165
  """
198
- To view code for spot selection please have a look at magicgui instance created with `detection._create_threshold_slider` which is then passed to this napari wrapper as 'threshold_slider' argument.
166
+ To view code for spot selection have a look at magicgui instance created with `detection._create_threshold_slider` which is then passed to this napari wrapper as 'threshold_slider' argument.
199
167
  """
200
168
 
201
169
  Viewer = napari.Viewer(title= "Small fish - Threshold selector", ndisplay=2, show=True)
@@ -218,12 +186,13 @@ def threshold_selection(
218
186
 
219
187
  Viewer.window.add_dock_widget(threshold_slider, name='threshold_selector')
220
188
  threshold_slider() #First occurence with auto or entered threshold.
189
+
221
190
  napari.run()
222
191
 
223
- spots = Viewer.layers[-1].data.astype(int)
192
+ spots = Viewer.layers['single spots'].data.astype(int)
224
193
  if len(spots) == 0 :
225
- threshold = filtered_image.max()
194
+ pass
226
195
  else :
227
- threshold = Viewer.layers[-1].properties.get('threshold')[0]
196
+ threshold = Viewer.layers['single spots'].properties.get('threshold')[0]
228
197
 
229
198
  return spots, threshold
@@ -69,7 +69,7 @@ def map_channels(user_parameters) :
69
69
  try :
70
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 :
@@ -125,14 +125,16 @@ def _auto_map_channels(is_3D_stack, is_time_stack, multichannel, image: np.ndarr
125
125
  def _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map: dict= {}) :
126
126
  while True :
127
127
  relaunch = False
128
+ save_preset = preset_map.copy()
128
129
  x = preset_map.setdefault('x',0)
129
130
  y = preset_map.setdefault('y',0)
130
131
  z = preset_map.setdefault('z',0)
131
132
  c = preset_map.setdefault('c',0)
132
133
  t = preset_map.setdefault('t',0)
133
134
 
135
+
134
136
  layout = [
135
- add_header("Dimensions mapping", [sg.Text("Image shape : {0}".format(shape))])
137
+ add_header("Dimensions mapping") + [sg.Text("Image shape : {0}".format(shape))]
136
138
  ]
137
139
  layout += [parameters_layout(['x','y'], default_values=[x,y])]
138
140
  if is_3D_stack : layout += [parameters_layout(['z'], default_values=[z])]
@@ -140,7 +142,7 @@ def _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map
140
142
  if is_time_stack : layout += [parameters_layout(['t'], default_values=[t])]
141
143
 
142
144
  event, preset_map = prompt_with_help(layout,help= 'mapping', add_scrollbar=False)
143
- if event == 'Cancel' : quit()
145
+ if event == 'Cancel' : return save_preset
144
146
 
145
147
  #Check integrity
146
148
  channels_values = np.array(list(preset_map.values()), dtype= int)
@@ -157,24 +159,26 @@ def _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map
157
159
  return preset_map
158
160
 
159
161
  def _show_mapping(shape, map, is_3D_stack, is_time_stack, multichannel) :
160
- layout = [
161
- [sg.Text("Image shape : {0}".format(shape))],
162
- [sg.Text('Dimensions mapping was set to :')],
163
- [sg.Text('x : {0} \ny : {1} \nz : {2} \nc : {3} \nt : {4}'.format(
164
- map['x'], map['y'], map.get('z'), map.get("c"), map.get('t')
165
- ))],
166
- [sg.Button('Change mapping')]
167
- ]
168
-
169
- event, values = prompt_with_help(layout, help='mapping', add_scrollbar=False)
170
-
171
- if event == 'Ok' :
172
- return map
173
- elif event == 'Change mapping' or event == 'Cancel':
174
- map = _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map=map)
175
- 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')
176
181
 
177
- return map
178
182
 
179
183
  def convert_parameters_types(values:dict) :
180
184
  """
@@ -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)
@@ -386,4 +454,49 @@ def plot_segmentation(
386
454
  contrast=True,
387
455
  path_output=path + "_cytoplasm_segmentation.png",
388
456
  show=False,
389
- )
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,4 +1,8 @@
1
- from ..gui.prompts import output_image_prompt, ask_detection_confirmation, ask_cancel_detection
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
+
5
+ from ..gui.prompts import output_image_prompt, ask_detection_confirmation, ask_cancel_detection, rename_prompt
2
6
  from ..interface.output import write_results
3
7
  from ._preprocess import map_channels, prepare_image_detection, reorder_shape, reorder_image_stack
4
8
  from .detection import ask_input_parameters, initiate_detection, launch_detection, launch_features_computation, get_nucleus_signal
@@ -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,
@@ -172,3 +177,32 @@ def delete_acquisitions(selected_acquisitions : pd.DataFrame,
172
177
  result_df = result_df.drop(result_drop_idx, axis=0)
173
178
 
174
179
  return result_df, cell_result_df, coloc_df
180
+
181
+ def rename_acquisitions(
182
+ selected_acquisitions : pd.DataFrame,
183
+ result_df : pd.DataFrame,
184
+ cell_result_df : pd.DataFrame,
185
+ coloc_df : pd.DataFrame
186
+ ) :
187
+
188
+ if len(result_df) == 0 :
189
+ sg.popup("No acquisition to rename.")
190
+ return result_df, cell_result_df, coloc_df
191
+
192
+ if len(selected_acquisitions) == 0 :
193
+ sg.popup("Please select the acquisitions you would like to rename.")
194
+
195
+ else :
196
+ name = rename_prompt()
197
+ print("entered : ",name)
198
+ if not name : return result_df, cell_result_df, coloc_df #User didn't put a name or canceled
199
+ name : str = name.replace(' ','_')
200
+ acquisition_ids = list(result_df.iloc[list(selected_acquisitions)]['acquisition_id'])
201
+
202
+ result_df.loc[result_df['acquisition_id'].isin(acquisition_ids),['name']] = name
203
+ if len(cell_result_df) > 0 : cell_result_df.loc[cell_result_df['acquisition_id'].isin(acquisition_ids),['name']] = name
204
+ if len(coloc_df) > 0 :
205
+ coloc_df.loc[coloc_df['acquisition_id_1'].isin(acquisition_ids), ['name1']] = name
206
+ coloc_df.loc[coloc_df['acquisition_id_2'].isin(acquisition_ids), ['name2']] = name
207
+
208
+ return result_df, cell_result_df, coloc_df
@@ -279,7 +279,6 @@ def initiate_detection(user_parameters, segmentation_done, map, shape) :
279
279
  segmentation_done= segmentation_done,
280
280
  default_dict=user_parameters
281
281
  )
282
-
283
282
  if type(user_parameters) == type(None) : return user_parameters
284
283
  try :
285
284
  user_parameters = convert_parameters_types(user_parameters)
@@ -315,7 +314,7 @@ def _launch_detection(image, image_input_values: dict) :
315
314
  threshold_user_selection = image_input_values.get('Interactive threshold selector')
316
315
 
317
316
  if type(threshold) == type(None) :
318
- 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
317
+ 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)
319
318
 
320
319
  filtered_image = _apply_log_filter(
321
320
  image=image,
@@ -412,7 +411,7 @@ def launch_post_detection(image, spots, image_input_values: dict,) :
412
411
  #appending results
413
412
  fov_res.update(snr_res)
414
413
 
415
- return spots, fov_res
414
+ return fov_res
416
415
 
417
416
  def _compute_cell_snr(image: np.ndarray, bbox, spots, voxel_size, spot_size) :
418
417
 
@@ -577,8 +576,6 @@ def launch_cell_extraction(acquisition_id, spots, clusters, image, nucleus_signa
577
576
 
578
577
  return result_frame
579
578
 
580
-
581
-
582
579
  @add_default_loading
583
580
  def launch_clustering(spots, user_parameters):
584
581
 
@@ -639,7 +636,6 @@ def launch_detection(
639
636
 
640
637
  else : clusters = None
641
638
 
642
- spots, post_detection_dict = launch_post_detection(image, spots, user_parameters, hide_loading = hide_loading)
643
639
  user_parameters['threshold'] = threshold
644
640
 
645
641
  if user_parameters['Napari correction'] :
@@ -655,7 +651,7 @@ def launch_detection(
655
651
  nucleus_label=nucleus_label,
656
652
  other_images=other_image
657
653
  )
658
-
654
+ post_detection_dict = launch_post_detection(image, spots, user_parameters, hide_loading = hide_loading)
659
655
  fov_result.update(post_detection_dict)
660
656
 
661
657
  return user_parameters, fov_result, spots, clusters
@@ -697,6 +693,15 @@ def launch_features_computation(acquisition_id, image, nucleus_signal, spots, cl
697
693
  frame_results['threshold'] = user_parameters['threshold']
698
694
 
699
695
  frame_results = pd.DataFrame(columns= frame_results.keys(), data= (frame_results.values(),))
696
+
697
+ #Adding name column
698
+ result_col = list(frame_results.columns)
699
+ cell_result_col = list(cell_result_dframe.columns)
700
+ name = "acquisition_{0}".format(acquisition_id)
701
+ frame_results['name'] = name
702
+ cell_result_dframe['name'] = name
703
+ frame_results = frame_results.loc[:,['name'] + result_col]
704
+ cell_result_dframe = cell_result_dframe.loc[:,['name'] + cell_result_col]
700
705
 
701
706
  return frame_results, cell_result_dframe
702
707
 
@@ -783,12 +788,13 @@ def _create_threshold_slider(
783
788
  'size': 5,
784
789
  'scale' : scale,
785
790
  'face_color' : 'transparent',
786
- 'edge_color' : 'blue',
787
- 'symbol' : 'ring',
791
+ 'edge_color' : 'red',
792
+ 'symbol' : 'disc',
788
793
  'opacity' : 0.7,
789
- 'blending' : 'additive',
794
+ 'blending' : 'translucent',
790
795
  'name': 'single spots',
791
- 'features' : {'threshold' : threshold}
796
+ 'features' : {'threshold' : threshold},
797
+ 'visible' : True,
792
798
  }
793
799
  return (spots, layer_args , 'points')
794
800
  return threshold_slider
@@ -1,13 +1,16 @@
1
- #### New version for main.py
1
+ """
2
+ This script is called when software starts; it is the main loop.
3
+ """
4
+
2
5
  import pandas as pd
3
6
  import PySimpleGUI as sg
4
7
  from ..gui import hub_prompt
5
- from .actions import add_detection, save_results, compute_colocalisation, delete_acquisitions
8
+ from .actions import add_detection, save_results, compute_colocalisation, delete_acquisitions, rename_acquisitions
6
9
  from ._preprocess import clean_unused_parameters_cache
7
10
  from ..batch import batch_promp
8
11
 
9
12
  #'Global' parameters
10
- user_parameters = dict() # Very important object containg all choice from user that will influence the behavior of the main loop.
13
+ user_parameters = dict() # Very important instance containg all choice from user that will influence the behavior of the actions loops.
11
14
  acquisition_id = -1
12
15
  result_df = pd.DataFrame()
13
16
  cell_result_df = pd.DataFrame()
@@ -81,6 +84,10 @@ while True : #Break this loop to close small_fish
81
84
  preset=user_parameters,
82
85
  )
83
86
 
87
+ elif event == "Rename acquisition" :
88
+ selected_acquisitions = values.setdefault('result_table', []) #Contains the lines selected by the user on the sum-up array.
89
+ result_df, cell_result_df, coloc_df = rename_acquisitions(selected_acquisitions, result_df, cell_result_df, coloc_df)
90
+
84
91
  else :
85
92
  break
86
93
 
@@ -0,0 +1,16 @@
1
+ import numpy as np
2
+
3
+ from math import ceil
4
+ from itertools import zip_longest
5
+ from skimage.measure import regionprops_table
6
+
7
+
8
+ def from_label_get_centeroidscoords(label: np.ndarray):
9
+ """
10
+ Returns
11
+ --------
12
+ centroid : dict{"label": list, "centroid-n": list}
13
+ n should be replace with 1,2.. according to the axe you wish to access."""
14
+
15
+ centroid = regionprops_table(label, properties= ["label","centroid"])
16
+ return centroid
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: small_fish_gui
3
- Version: 1.4.0
3
+ Version: 1.6.0
4
4
  Summary: Small Fish is a python application for the analysis of smFish images. It provides a ready to use graphical interface to combine famous python packages for cell analysis without any need for coding.
5
5
  Project-URL: Homepage, https://github.com/2Echoes/small_fish
6
6
  Project-URL: Issues, https://github.com/2Echoes/small_fish/issues
@@ -1,8 +1,9 @@
1
+ small_fish_gui/.readthedocs.yaml,sha256=r2T0e_In8X8l0_ZwgPvuoWQ9c0PE9bSpFzV2W6EzW3g,409
1
2
  small_fish_gui/LICENSE,sha256=-iFy8VGBYs5VsHglKpk4D-hxqQ2jMJaqmfq_ulIzDks,1303
2
- small_fish_gui/README.md,sha256=2c_homYDJXX6VsBiEs5obhBh3HpcTSMdyjLo-35WzE4,4062
3
+ small_fish_gui/README.md,sha256=4RpEXKZW5vH6sUWeZb88yr1TLLPi20PqOk7KdA9O9Hk,4234
3
4
  small_fish_gui/Segmentation example.jpg,sha256=opfiSbjmfF6z8kBs08sg_FNR2Om0AcMPU5sSwSLHdoQ,215038
4
- small_fish_gui/__init__.py,sha256=bAy5AbCcDOo7LXgavAnPpRWrr5bCcI8KVqQZX1nb1L0,1941
5
- small_fish_gui/__main__.py,sha256=EzSCoJ7jpSdK-QbzUwQLGZeQWjybNeq8VnCBucA8MZw,1372
5
+ small_fish_gui/__init__.py,sha256=5p-W0XXzvadf_q5TZCbYLXaSLIBLOBlhwt7rTtg760w,1941
6
+ small_fish_gui/__main__.py,sha256=jjFNnf-l4jCJI16epq2KOaKmgtUAe9lSNdPj5fpxrDk,1143
6
7
  small_fish_gui/napari_detection_example.png,sha256=l5EZlrbXemLiGqb5inSVsD6Kko1Opz528-go-fBfrw8,977350
7
8
  small_fish_gui/requirements.txt,sha256=9OMfUAnLdHevq6w_fVoDmVmkSMJeFofkOK_86_fu9C0,321
8
9
  small_fish_gui/utils.py,sha256=LM6QW2ono_LIRv7JXIIq7ZxxbDXqBtZ5uR9gjKJfwM8,1903
@@ -12,19 +13,20 @@ small_fish_gui/batch/input.py,sha256=mqnP8LBhyNbtlcqjVlUiVeuHw4YxOX3GgzJbq03isKE
12
13
  small_fish_gui/batch/integrity.py,sha256=yzVWBwm4Mxftd1sDziQwKc7d3ALdgWOhkqQrU5-p430,4849
13
14
  small_fish_gui/batch/output.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
15
  small_fish_gui/batch/pipeline.py,sha256=uZCwkoiSnTHWAf7C4XPNXg4ldIjbpRiULpzZWq7R1i0,8798
15
- small_fish_gui/batch/prompt.py,sha256=K3Vb1ebcNe6JieXPDL1btrZR-4ASjs_HIklXx5B9lYU,18608
16
+ small_fish_gui/batch/prompt.py,sha256=ZfOyWjmG2AZeB_wwbmqchEp6JM1duXM4GXj12r_eDZk,18788
16
17
  small_fish_gui/batch/test.py,sha256=q04a1YstnDsxy2Bi5563BfcOU-O3VPE9c5WSJjvFjMg,211
17
18
  small_fish_gui/batch/update.py,sha256=hR4kZ7tP2tvn1tmDa4oJb2e7-SUqN1Lf8JR6OCIOMS8,5037
18
19
  small_fish_gui/batch/utils.py,sha256=HgfPwfhqWXOGtCny_nTdGs8csWB1BQp7-hYgrNVLB70,1774
19
20
  small_fish_gui/batch/values.py,sha256=C1hRlCpTIDsg89DMKIIW5NUxeK876ODRUuJ2D-mJv6o,1519
20
21
  small_fish_gui/batch/values.txt,sha256=PVxzIaaF6DGFRx_CMaStXZI6OrbjNub1-jR3pklXVjc,991
22
+ small_fish_gui/docs/conf.py,sha256=6YU8UEpTenKGMiz7H4aG42Of72_n4uLadDfHJvziqRk,16
21
23
  small_fish_gui/gui/__init__.py,sha256=xQ_BfYcnQmKZtx_0leO4OmbkLNLv49ZPqEu_UXMgmDc,867
22
24
  small_fish_gui/gui/animation.py,sha256=rnNP5FPp06Hu-R33c4AVTCknALBbxT2YlsKFCXHAp9k,981
23
25
  small_fish_gui/gui/general_help_screenshot.png,sha256=X4E6Td5f04K-pBUPDaBJRAE3D5b8fuEdiAUKhkIDr-0,54210
24
26
  small_fish_gui/gui/help_module.py,sha256=PmgkkDs7bZ2-po83A_PK9uldQcHjehYmqre21nYb6DQ,9600
25
- small_fish_gui/gui/layout.py,sha256=k_ATlpkzqzo7UotXwq6WkuqnevKT6UgFjfDH0UzHAOM,13563
27
+ small_fish_gui/gui/layout.py,sha256=oB8Kg6s0rCA8yB4WM8JQY8BpjoPiBqTGb6YoOKDqEA8,13855
26
28
  small_fish_gui/gui/mapping_help_screenshot.png,sha256=HcuRh5TYciUogUasza5vZ_QSshaiHsskQK23mh9vQS8,34735
27
- small_fish_gui/gui/prompts.py,sha256=WFxXLx-M3TM685iiQcNNNVX4Tpttn5JXPm6yM9eBRV4,13358
29
+ small_fish_gui/gui/prompts.py,sha256=rdO2X_whpBgMzCrysFoJEUWfZwbC7mZOp_DYctftNT0,13634
28
30
  small_fish_gui/gui/segmentation_help_screenshot.png,sha256=rbSgIydT0gZtfMh1qk4mdMbEIyCaakvHmxa2eOrLwO0,118944
29
31
  small_fish_gui/interface/__init__.py,sha256=PB86R4Y9kV80aGZ-vP0ZW2KeaCwGbBbCtFCmbN2yl28,275
30
32
  small_fish_gui/interface/image.py,sha256=X1L7S5svxUwdoDcI3QM1PbN-c4Nz5w30hixq3IgqSn8,1130
@@ -32,18 +34,19 @@ small_fish_gui/interface/output.py,sha256=5jC37tobgXgsiVJYx3RWaES09I-YFmbXKk65lH
32
34
  small_fish_gui/interface/parameters.py,sha256=lUugD-4W2TZyJF3TH1q70TlktEYhhPtcPCrvxm5Dk50,36
33
35
  small_fish_gui/interface/testing.py,sha256=MY5-GcPOUHagcrwR8A7QOjAmjZIDVC8Wz3NibLe3KQw,321
34
36
  small_fish_gui/pipeline/__init__.py,sha256=_Ey20GG8fJtqZvixbXNNYX6wTWMnCUArmARPqsNEhuQ,743
35
- small_fish_gui/pipeline/_colocalisation.py,sha256=peBw2Qz5m6wSejDkDz240UgvWl8ohNelrnmEgznbEsw,9635
37
+ small_fish_gui/pipeline/_colocalisation.py,sha256=pWyObNoACWNW04OYzKf7bgq2OezWpEWh7Vl5DShTI1A,10118
36
38
  small_fish_gui/pipeline/_custom_errors.py,sha256=tQ-AUhgzIFpK30AZiQQrtHCHyGVRDdAoIjzL0Fk-1pA,43
37
- small_fish_gui/pipeline/_napari_wrapper.py,sha256=_FkkY7IBKn1QgNvyea-x6XPjw_AwsLEVzdRKzg65oCE,9290
38
- small_fish_gui/pipeline/_preprocess.py,sha256=9Ns0el109qeRD1I7HmKpuljyKZfYLwPapvrKYl9ebdc,12943
39
- small_fish_gui/pipeline/_segmentation.py,sha256=jcNf_GxNov_O16Xt6XyDIfYC7JsdiAxGeqLMJCwEy5I,13925
39
+ small_fish_gui/pipeline/_napari_wrapper.py,sha256=Yjpo-uXQxLfLESsWr4kIBZgQNVXJtcTFcrsvS9sk4No,7832
40
+ small_fish_gui/pipeline/_preprocess.py,sha256=ddocTXwc0vYq2VGUbWYaN9eUiHPyfiCuBpYQ2p6rQ8g,13084
41
+ small_fish_gui/pipeline/_segmentation.py,sha256=gcanidUOC9nULF6UffWLFmfIup-EOMxeckBz7Xldp3I,18852
40
42
  small_fish_gui/pipeline/_signaltonoise.py,sha256=7A9t7xu7zghI6cr201Ldm-LjJ5NOuP56VSeJ8KIzcUo,8497
41
- small_fish_gui/pipeline/actions.py,sha256=EIGIOlwJ_DADX1NcLWwrTP_AidDX-4f4ggZV0gkIb58,7988
42
- small_fish_gui/pipeline/detection.py,sha256=La8hCO2WvZgHjvgUIjwQbqKYhDHI5ZtLhYrxFc62qbs,34429
43
- small_fish_gui/pipeline/main.py,sha256=QamUbM4pfLLWGAC8AkJ-vTcUNHpWeu9bBB1g5pBBoNY,3433
43
+ small_fish_gui/pipeline/actions.py,sha256=YGW7IFwtRksf0nktl5C2uVLP-Z-7vVznIBxIdJGZJZw,9447
44
+ small_fish_gui/pipeline/detection.py,sha256=rnA0qMqr0dNUAUPd1ANYZ7dyfllsDQrihO_gjEpenvA,34817
45
+ small_fish_gui/pipeline/main.py,sha256=7iBHO8xNEvdONaUo78jkShBkHFuwgGryMoI4YwP4coI,3817
44
46
  small_fish_gui/pipeline/spots.py,sha256=yHvqf1eD25UltELpzcouYXhLkxiXI_mOL1ANSzXK5pw,1907
45
47
  small_fish_gui/pipeline/test.py,sha256=w4ZMGDmUDXxVgWTlZ2TKw19W8q5gcE9gLMKe0SWnRrw,2827
46
- small_fish_gui-1.4.0.dist-info/METADATA,sha256=ga-XNAKMcj2t0XMTxUoj3aIMuIRE9DuiBPKQbmuadMk,2567
47
- small_fish_gui-1.4.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
48
- small_fish_gui-1.4.0.dist-info/licenses/LICENSE,sha256=-iFy8VGBYs5VsHglKpk4D-hxqQ2jMJaqmfq_ulIzDks,1303
49
- small_fish_gui-1.4.0.dist-info/RECORD,,
48
+ small_fish_gui/pipeline/utils.py,sha256=run6qtqCAe_mFnE3o1CnmF1xBBmK3ydgc8-jOV9P-_w,448
49
+ small_fish_gui-1.6.0.dist-info/METADATA,sha256=lNNHwO008d4nmzHaYpeNKi9DFptJ56O0GxfmxWiRxI4,2567
50
+ small_fish_gui-1.6.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
51
+ small_fish_gui-1.6.0.dist-info/licenses/LICENSE,sha256=-iFy8VGBYs5VsHglKpk4D-hxqQ2jMJaqmfq_ulIzDks,1303
52
+ small_fish_gui-1.6.0.dist-info/RECORD,,