small-fish-gui 1.4.0__tar.gz → 1.5.0__tar.gz

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 (53) hide show
  1. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/PKG-INFO +1 -1
  2. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/pyproject.toml +1 -1
  3. small_fish_gui-1.5.0/src/small_fish_gui/.readthedocs.yaml +21 -0
  4. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/README.md +2 -3
  5. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/__init__.py +1 -1
  6. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/batch/prompt.py +6 -4
  7. small_fish_gui-1.5.0/src/small_fish_gui/docs/conf.py +1 -0
  8. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/gui/layout.py +21 -3
  9. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/gui/prompts.py +8 -6
  10. small_fish_gui-1.5.0/src/small_fish_gui/pipeline/_napari_wrapper.py +202 -0
  11. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/pipeline/_preprocess.py +24 -20
  12. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/pipeline/_segmentation.py +130 -17
  13. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/pipeline/actions.py +6 -1
  14. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/pipeline/detection.py +8 -10
  15. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/pipeline/main.py +5 -2
  16. small_fish_gui-1.5.0/src/small_fish_gui/pipeline/utils.py +16 -0
  17. small_fish_gui-1.4.0/src/small_fish_gui/pipeline/_napari_wrapper.py +0 -229
  18. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/LICENSE +0 -0
  19. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/README.md +0 -0
  20. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/.github/workflows/python-publish.yml +0 -0
  21. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/LICENSE +0 -0
  22. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/Segmentation example.jpg +0 -0
  23. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/__main__.py +0 -0
  24. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/batch/__init__.py +0 -0
  25. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/batch/input.py +0 -0
  26. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/batch/integrity.py +0 -0
  27. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/batch/output.py +0 -0
  28. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/batch/pipeline.py +0 -0
  29. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/batch/test.py +0 -0
  30. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/batch/update.py +0 -0
  31. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/batch/utils.py +0 -0
  32. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/batch/values.py +0 -0
  33. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/batch/values.txt +0 -0
  34. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/gui/__init__.py +0 -0
  35. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/gui/animation.py +0 -0
  36. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/gui/general_help_screenshot.png +0 -0
  37. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/gui/help_module.py +0 -0
  38. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/gui/mapping_help_screenshot.png +0 -0
  39. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/gui/segmentation_help_screenshot.png +0 -0
  40. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/interface/__init__.py +0 -0
  41. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/interface/image.py +0 -0
  42. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/interface/output.py +0 -0
  43. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/interface/parameters.py +0 -0
  44. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/interface/testing.py +0 -0
  45. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/napari_detection_example.png +0 -0
  46. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/pipeline/__init__.py +0 -0
  47. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/pipeline/_colocalisation.py +0 -0
  48. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/pipeline/_custom_errors.py +0 -0
  49. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/pipeline/_signaltonoise.py +0 -0
  50. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/pipeline/spots.py +0 -0
  51. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/pipeline/test.py +0 -0
  52. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/requirements.txt +0 -0
  53. {small_fish_gui-1.4.0 → small_fish_gui-1.5.0}/src/small_fish_gui/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: small_fish_gui
3
- Version: 1.4.0
3
+ Version: 1.5.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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "small_fish_gui"
7
- version = "1.4.0"
7
+ version = "1.5.0"
8
8
  authors = [
9
9
  { name="Slimani Floric", email="floric.slimani@live.com" },
10
10
  ]
@@ -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
@@ -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.5.0"
@@ -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','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,9 +295,9 @@ 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')],
@@ -0,0 +1,202 @@
1
+ """
2
+ Contains Napari wrappers to visualise and correct spots/clusters.
3
+ """
4
+
5
+ import napari.layers
6
+ import napari.types
7
+ import numpy as np
8
+ import napari
9
+
10
+ from bigfish.stack import check_parameter
11
+ from ..utils import compute_anisotropy_coef
12
+ from ._colocalisation import spots_multicolocalisation
13
+
14
+ def _update_clusters(new_clusters: np.ndarray, spots: np.ndarray, voxel_size, cluster_size, min_spot_number, shape) :
15
+ if len(new_clusters) == 0 : return new_clusters
16
+ if len(spots) == 0 : return new_clusters
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
+
25
+ assert len(new_clusters[0]) == 4 or len(new_clusters[0]) == 5, "Wrong number of coordinates for clusters should not happen."
26
+
27
+ # Update spots clusters
28
+ if len(voxel_size) == 3 :
29
+ new_clusters[:,-2] = spots_multicolocalisation(new_clusters[:,:3], spots, radius_nm= cluster_size, voxel_size=voxel_size, image_shape=shape)
30
+ elif len(voxel_size) == 2 :
31
+ new_clusters[:,-2] = spots_multicolocalisation(new_clusters[:,:2], spots, radius_nm= cluster_size, voxel_size=voxel_size, image_shape=shape)
32
+
33
+ # delete too small clusters
34
+ new_clusters = np.delete(new_clusters, new_clusters[:,-2] < min_spot_number, 0)
35
+
36
+ return new_clusters
37
+
38
+ def correct_spots(image, spots, voxel_size= (1,1,1), clusters= None, cluster_size=None, min_spot_number=0, cell_label= None, nucleus_label= None, other_images =[]):
39
+ """
40
+ Open Napari viewer for user to visualize and corrects spots, clusters.
41
+
42
+ Returns
43
+ -------
44
+ new_spots,new_clusters
45
+ """
46
+ check_parameter(image= np.ndarray, voxel_size= (tuple,list))
47
+ dim = len(voxel_size)
48
+
49
+ if dim == 3 and type(cell_label) != type(None) :
50
+ cell_label = np.repeat(
51
+ cell_label[np.newaxis],
52
+ repeats= len(image),
53
+ axis=0
54
+ )
55
+ if dim == 3 and type(nucleus_label) != type(None) :
56
+ nucleus_label = np.repeat(
57
+ nucleus_label[np.newaxis],
58
+ repeats= len(image),
59
+ axis=0
60
+ )
61
+
62
+ scale = compute_anisotropy_coef(voxel_size)
63
+ Viewer = napari.Viewer(ndisplay=2, title= 'Spot correction', axis_labels=['z','y','x'], show= False)
64
+ Viewer.add_image(image, scale=scale, name= "rna signal", blending= 'additive', colormap='red', contrast_limits=[image.min(), image.max()])
65
+ other_colors = ['green', 'blue', 'gray', 'cyan', 'bop orange', 'bop purple'] * ((len(other_images)-1 // 7) + 1)
66
+ for im, color in zip(other_images, other_colors) :
67
+ Viewer.add_image(im, scale=scale, blending='additive', visible=False, colormap=color, contrast_limits=[im.min(), im.max()])
68
+ layer_offset = len(other_images)
69
+
70
+ Viewer.add_points( # single molecule spots; this layer can be update by user.
71
+ spots,
72
+ size = 5,
73
+ scale=scale,
74
+ face_color= 'transparent',
75
+ opacity= 1,
76
+ symbol= 'disc',
77
+ name= 'single spots'
78
+ )
79
+
80
+ if type(clusters) != type(None) : Viewer.add_points( # cluster; this layer can be update by user.
81
+ clusters[:,:dim],
82
+ size = 10,
83
+ scale=scale,
84
+ face_color= 'blue',
85
+ opacity= 0.7,
86
+ symbol= 'diamond',
87
+ name= 'foci',
88
+ features= {"spot_number" : clusters[:,dim], "id" : clusters[:,dim+1]},
89
+ feature_defaults= {"spot_number" : 0, "id" : -1}
90
+ )
91
+
92
+ 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')
93
+ if type(nucleus_label) != type(None) : Viewer.add_labels(nucleus_label, scale=scale, opacity= 0.2, blending= 'additive')
94
+
95
+ Viewer.show(block=False)
96
+ napari.run()
97
+
98
+ new_spots = np.array(Viewer.layers['single spots'].data, dtype= int)
99
+
100
+ if type(clusters) != type(None) :
101
+ if len(clusters) > 0 :
102
+ new_clusters = np.array(Viewer.layers['foci'].data, dtype= int)
103
+ 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)
104
+ else : new_clusters = None
105
+
106
+ return new_spots, new_clusters
107
+
108
+ def show_segmentation(
109
+ nuc_image : np.ndarray,
110
+ nuc_label : np.ndarray,
111
+ cyto_image : np.ndarray = None,
112
+ cyto_label : np.ndarray = None,
113
+ ) :
114
+ dim = nuc_image.ndim
115
+
116
+ if type(cyto_image) != type(None) :
117
+ if cyto_image.ndim != nuc_image.ndim : raise ValueError("Cyto and Nuc dimensions missmatch.")
118
+ if type(cyto_label) == type(None) : raise ValueError("If cyto image is passed cyto label must be passed too.")
119
+
120
+ if dim == 3 and nuc_label.ndim == 2 :
121
+ nuc_label = np.repeat(
122
+ nuc_label[np.newaxis],
123
+ repeats= len(nuc_image),
124
+ axis=0
125
+ )
126
+ if type(cyto_label) != type(None) :
127
+
128
+ if type(cyto_image) == type(None) : raise ValueError("If cyto label is passed cyto image must be passed too.")
129
+
130
+ if dim == 3 and cyto_label.ndim == 2 :
131
+ cyto_label = np.repeat(
132
+ cyto_label[np.newaxis],
133
+ repeats= len(nuc_image),
134
+ axis=0
135
+ )
136
+
137
+ #Init Napari viewer
138
+ Viewer = napari.Viewer(ndisplay=2, title= 'Show segmentation', axis_labels=['z','y','x'] if dim == 3 else ['y', 'x'], show= False)
139
+
140
+ # Adding channels
141
+ nuc_signal_layer = Viewer.add_image(nuc_image, name= "nucleus signal", blending= 'additive', colormap='blue', contrast_limits=[nuc_image.min(), nuc_image.max()])
142
+ nuc_label_layer = Viewer.add_labels(nuc_label, opacity= 0.5, blending= 'additive', name= 'nucleus_label',)
143
+ nuc_label_layer.preserve_labels = True
144
+
145
+ #Adding labels
146
+ 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()])
147
+ 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):
148
+ cyto_label_layer = Viewer.add_labels(cyto_label, opacity= 0.4, blending= 'additive', name= 'cytoplasm_label')
149
+ cyto_label_layer.preserve_labels = True
150
+
151
+ #Launch Napari
152
+ Viewer.show(block=False)
153
+ napari.run()
154
+
155
+
156
+ new_nuc_label = Viewer.layers['nucleus_label'].data
157
+ if type(cyto_label) != type(None) and not np.array_equal(cyto_label, nuc_label) : new_cyto_label = Viewer.layers['cytoplasm_label'].data
158
+ else : new_cyto_label = new_nuc_label
159
+
160
+ return new_nuc_label, new_cyto_label
161
+
162
+ def threshold_selection(
163
+ image : np.ndarray,
164
+ filtered_image : np.ndarray,
165
+ threshold_slider,
166
+ voxel_size : tuple,
167
+ ) :
168
+
169
+ """
170
+ 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.
171
+ """
172
+
173
+ Viewer = napari.Viewer(title= "Small fish - Threshold selector", ndisplay=2, show=True)
174
+ scale = compute_anisotropy_coef(voxel_size)
175
+ Viewer.add_image(
176
+ data= image,
177
+ contrast_limits= [image.min(), image.max()],
178
+ name= "raw signal",
179
+ colormap= 'green',
180
+ scale= scale,
181
+ blending= 'additive'
182
+ )
183
+ Viewer.add_image(
184
+ data= filtered_image,
185
+ contrast_limits= [filtered_image.min(), filtered_image.max()],
186
+ colormap= 'gray',
187
+ scale=scale,
188
+ blending='additive'
189
+ )
190
+
191
+ Viewer.window.add_dock_widget(threshold_slider, name='threshold_selector')
192
+ threshold_slider() #First occurence with auto or entered threshold.
193
+
194
+ napari.run()
195
+
196
+ spots = Viewer.layers['single spots'].data.astype(int)
197
+ if len(spots) == 0 :
198
+ pass
199
+ else :
200
+ threshold = Viewer.layers['single spots'].properties.get('threshold')[0]
201
+
202
+ 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,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
@@ -279,7 +278,6 @@ def initiate_detection(user_parameters, segmentation_done, map, shape) :
279
278
  segmentation_done= segmentation_done,
280
279
  default_dict=user_parameters
281
280
  )
282
-
283
281
  if type(user_parameters) == type(None) : return user_parameters
284
282
  try :
285
283
  user_parameters = convert_parameters_types(user_parameters)
@@ -315,7 +313,7 @@ def _launch_detection(image, image_input_values: dict) :
315
313
  threshold_user_selection = image_input_values.get('Interactive threshold selector')
316
314
 
317
315
  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
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)
319
317
 
320
318
  filtered_image = _apply_log_filter(
321
319
  image=image,
@@ -412,7 +410,7 @@ def launch_post_detection(image, spots, image_input_values: dict,) :
412
410
  #appending results
413
411
  fov_res.update(snr_res)
414
412
 
415
- return spots, fov_res
413
+ return fov_res
416
414
 
417
415
  def _compute_cell_snr(image: np.ndarray, bbox, spots, voxel_size, spot_size) :
418
416
 
@@ -639,7 +637,6 @@ def launch_detection(
639
637
 
640
638
  else : clusters = None
641
639
 
642
- spots, post_detection_dict = launch_post_detection(image, spots, user_parameters, hide_loading = hide_loading)
643
640
  user_parameters['threshold'] = threshold
644
641
 
645
642
  if user_parameters['Napari correction'] :
@@ -655,7 +652,7 @@ def launch_detection(
655
652
  nucleus_label=nucleus_label,
656
653
  other_images=other_image
657
654
  )
658
-
655
+ post_detection_dict = launch_post_detection(image, spots, user_parameters, hide_loading = hide_loading)
659
656
  fov_result.update(post_detection_dict)
660
657
 
661
658
  return user_parameters, fov_result, spots, clusters
@@ -783,12 +780,13 @@ def _create_threshold_slider(
783
780
  'size': 5,
784
781
  'scale' : scale,
785
782
  'face_color' : 'transparent',
786
- 'edge_color' : 'blue',
787
- 'symbol' : 'ring',
783
+ 'edge_color' : 'red',
784
+ 'symbol' : 'disc',
788
785
  'opacity' : 0.7,
789
- 'blending' : 'additive',
786
+ 'blending' : 'translucent',
790
787
  'name': 'single spots',
791
- 'features' : {'threshold' : threshold}
788
+ 'features' : {'threshold' : threshold},
789
+ 'visible' : True,
792
790
  }
793
791
  return (spots, layer_args , 'points')
794
792
  return threshold_slider
@@ -1,4 +1,7 @@
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
@@ -7,7 +10,7 @@ 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()
@@ -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,229 +0,0 @@
1
- """
2
- Contains Napari wrappers to visualise and correct spots/clusters.
3
- """
4
-
5
- import numpy as np
6
- import scipy.ndimage as ndi
7
- import napari
8
-
9
- from napari.utils.events import Event
10
- from napari.layers import Points
11
- from bigfish.stack import check_parameter
12
- from ..utils import compute_anisotropy_coef
13
- from ._colocalisation import spots_multicolocalisation
14
-
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
- def _update_clusters(new_clusters: np.ndarray, spots: np.ndarray, voxel_size, cluster_size, min_spot_number, shape) :
56
- if len(new_clusters) == 0 : return new_clusters
57
- if len(spots) == 0 : return new_clusters
58
- assert len(new_clusters[0]) == 4 or len(new_clusters[0]) == 5, "Wrong number of coordinates for clusters should not happen."
59
-
60
- # 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)
65
-
66
- # delete too small clusters
67
- new_clusters = np.delete(new_clusters, new_clusters[:,-2] < min_spot_number, 0)
68
-
69
- return new_clusters
70
-
71
- def correct_spots(image, spots, voxel_size= (1,1,1), clusters= None, cluster_size=None, min_spot_number=0, cell_label= None, nucleus_label= None, other_images =[]):
72
- """
73
- Open Napari viewer for user to visualize and corrects spots, clusters.
74
-
75
- Returns
76
- -------
77
- new_spots,new_clusters
78
- """
79
- check_parameter(image= np.ndarray, voxel_size= (tuple,list))
80
- dim = len(voxel_size)
81
-
82
- if dim == 3 and type(cell_label) != type(None) :
83
- cell_label = np.repeat(
84
- cell_label[np.newaxis],
85
- repeats= len(image),
86
- axis=0
87
- )
88
- if dim == 3 and type(nucleus_label) != type(None) :
89
- nucleus_label = np.repeat(
90
- nucleus_label[np.newaxis],
91
- repeats= len(image),
92
- axis=0
93
- )
94
-
95
- 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)
119
-
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)
127
-
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
130
-
131
- except Exception as error :
132
- new_spots = spots
133
- new_clusters = clusters
134
- raise error
135
-
136
- return new_spots, new_clusters
137
-
138
- def show_segmentation(
139
- nuc_image : np.ndarray,
140
- nuc_label : np.ndarray,
141
- cyto_image : np.ndarray = None,
142
- cyto_label : np.ndarray = None,
143
- ) :
144
- dim = nuc_image.ndim
145
-
146
- if type(cyto_image) != type(None) :
147
- if cyto_image.ndim != nuc_image.ndim : raise ValueError("Cyto and Nuc dimensions missmatch.")
148
- if type(cyto_label) == type(None) : raise ValueError("If cyto image is passed cyto label must be passed too.")
149
-
150
- if dim == 3 and nuc_label.ndim == 2 :
151
- nuc_label = np.repeat(
152
- nuc_label[np.newaxis],
153
- repeats= len(nuc_image),
154
- axis=0
155
- )
156
- if type(cyto_label) != type(None) :
157
-
158
- if type(cyto_image) == type(None) : raise ValueError("If cyto label is passed cyto image must be passed too.")
159
-
160
- if dim == 3 and cyto_label.ndim == 2 :
161
- cyto_label = np.repeat(
162
- cyto_label[np.newaxis],
163
- repeats= len(nuc_image),
164
- axis=0
165
- )
166
-
167
- #Init Napari viewer
168
- Viewer = napari.Viewer(ndisplay=2, title= 'Show segmentation', axis_labels=['z','y','x'] if dim == 3 else ['y', 'x'], show= False)
169
-
170
- # 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')
173
-
174
- #Adding labels
175
- 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')
177
-
178
- #Launch Napari
179
- Viewer.show(block=False)
180
- napari.run()
181
-
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
184
- else : new_cyto_label = new_nuc_label
185
-
186
- return new_nuc_label, new_cyto_label
187
-
188
-
189
-
190
- def threshold_selection(
191
- image : np.ndarray,
192
- filtered_image : np.ndarray,
193
- threshold_slider,
194
- voxel_size : tuple,
195
- ) :
196
-
197
- """
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.
199
- """
200
-
201
- Viewer = napari.Viewer(title= "Small fish - Threshold selector", ndisplay=2, show=True)
202
- scale = compute_anisotropy_coef(voxel_size)
203
- Viewer.add_image(
204
- data= image,
205
- contrast_limits= [image.min(), image.max()],
206
- name= "raw signal",
207
- colormap= 'green',
208
- scale= scale,
209
- blending= 'additive'
210
- )
211
- Viewer.add_image(
212
- data= filtered_image,
213
- contrast_limits= [filtered_image.min(), filtered_image.max()],
214
- colormap= 'gray',
215
- scale=scale,
216
- blending='additive'
217
- )
218
-
219
- Viewer.window.add_dock_widget(threshold_slider, name='threshold_selector')
220
- threshold_slider() #First occurence with auto or entered threshold.
221
- napari.run()
222
-
223
- spots = Viewer.layers[-1].data.astype(int)
224
- if len(spots) == 0 :
225
- threshold = filtered_image.max()
226
- else :
227
- threshold = Viewer.layers[-1].properties.get('threshold')[0]
228
-
229
- return spots, threshold
File without changes
File without changes