small-fish-gui 1.4.0__tar.gz → 1.6.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.
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/PKG-INFO +1 -1
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/pyproject.toml +1 -1
- small_fish_gui-1.6.0/src/small_fish_gui/.readthedocs.yaml +21 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/README.md +2 -3
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/__init__.py +1 -1
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/__main__.py +3 -3
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/batch/prompt.py +6 -4
- small_fish_gui-1.6.0/src/small_fish_gui/docs/conf.py +1 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/gui/layout.py +21 -3
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/gui/prompts.py +17 -11
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/pipeline/_colocalisation.py +9 -0
- small_fish_gui-1.6.0/src/small_fish_gui/pipeline/_napari_wrapper.py +198 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/pipeline/_preprocess.py +24 -20
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/pipeline/_segmentation.py +130 -17
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/pipeline/actions.py +36 -2
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/pipeline/detection.py +17 -11
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/pipeline/main.py +10 -3
- small_fish_gui-1.6.0/src/small_fish_gui/pipeline/utils.py +16 -0
- small_fish_gui-1.4.0/src/small_fish_gui/pipeline/_napari_wrapper.py +0 -229
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/LICENSE +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/README.md +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/.github/workflows/python-publish.yml +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/LICENSE +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/Segmentation example.jpg +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/batch/__init__.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/batch/input.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/batch/integrity.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/batch/output.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/batch/pipeline.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/batch/test.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/batch/update.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/batch/utils.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/batch/values.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/batch/values.txt +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/gui/__init__.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/gui/animation.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/gui/general_help_screenshot.png +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/gui/help_module.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/gui/mapping_help_screenshot.png +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/gui/segmentation_help_screenshot.png +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/interface/__init__.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/interface/image.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/interface/output.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/interface/parameters.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/interface/testing.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/napari_detection_example.png +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/pipeline/__init__.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/pipeline/_custom_errors.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/pipeline/_signaltonoise.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/pipeline/spots.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/pipeline/test.py +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/requirements.txt +0 -0
- {small_fish_gui-1.4.0 → small_fish_gui-1.6.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.
|
|
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
|
|
@@ -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
|
|
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
|
|
@@ -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 =
|
|
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
|
-
|
|
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(
|
|
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 = [
|
|
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=
|
|
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[:,
|
|
287
|
+
res = results.loc[:,COLUMNS]
|
|
286
288
|
else :
|
|
287
|
-
res = pd.DataFrame(columns=
|
|
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
|
|
298
|
+
segmentation_object = sg.Text('Segmentation in memory', font='8', text_color= 'green')
|
|
297
299
|
else :
|
|
298
|
-
segmentation_object = sg.Text('
|
|
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')],
|
{small_fish_gui-1.4.0 → small_fish_gui-1.6.0}/src/small_fish_gui/pipeline/_colocalisation.py
RENAMED
|
@@ -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
|
|
@@ -0,0 +1,198 @@
|
|
|
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 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
|
+
|
|
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
|
+
new_clusters[:,-2] = spots_multicolocalisation(new_clusters[:,:-2], spots, radius_nm= cluster_size, voxel_size=voxel_size, image_shape=shape)
|
|
29
|
+
|
|
30
|
+
# delete too small clusters
|
|
31
|
+
new_clusters = np.delete(new_clusters, new_clusters[:,-2] < min_spot_number, 0)
|
|
32
|
+
|
|
33
|
+
return new_clusters
|
|
34
|
+
|
|
35
|
+
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 =[]):
|
|
36
|
+
"""
|
|
37
|
+
Open Napari viewer for user to visualize and corrects spots, clusters.
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
new_spots,new_clusters
|
|
42
|
+
"""
|
|
43
|
+
check_parameter(image= np.ndarray, voxel_size= (tuple,list))
|
|
44
|
+
dim = len(voxel_size)
|
|
45
|
+
|
|
46
|
+
if dim == 3 and type(cell_label) != type(None) :
|
|
47
|
+
cell_label = np.repeat(
|
|
48
|
+
cell_label[np.newaxis],
|
|
49
|
+
repeats= len(image),
|
|
50
|
+
axis=0
|
|
51
|
+
)
|
|
52
|
+
if dim == 3 and type(nucleus_label) != type(None) :
|
|
53
|
+
nucleus_label = np.repeat(
|
|
54
|
+
nucleus_label[np.newaxis],
|
|
55
|
+
repeats= len(image),
|
|
56
|
+
axis=0
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
scale = compute_anisotropy_coef(voxel_size)
|
|
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
|
+
)
|
|
88
|
+
|
|
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()
|
|
94
|
+
|
|
95
|
+
new_spots = np.array(Viewer.layers['single spots'].data, dtype= int)
|
|
96
|
+
|
|
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
|
|
101
|
+
|
|
102
|
+
return new_spots, new_clusters
|
|
103
|
+
|
|
104
|
+
def show_segmentation(
|
|
105
|
+
nuc_image : np.ndarray,
|
|
106
|
+
nuc_label : np.ndarray,
|
|
107
|
+
cyto_image : np.ndarray = None,
|
|
108
|
+
cyto_label : np.ndarray = None,
|
|
109
|
+
) :
|
|
110
|
+
dim = nuc_image.ndim
|
|
111
|
+
|
|
112
|
+
if type(cyto_image) != type(None) :
|
|
113
|
+
if cyto_image.ndim != nuc_image.ndim : raise ValueError("Cyto and Nuc dimensions missmatch.")
|
|
114
|
+
if type(cyto_label) == type(None) : raise ValueError("If cyto image is passed cyto label must be passed too.")
|
|
115
|
+
|
|
116
|
+
if dim == 3 and nuc_label.ndim == 2 :
|
|
117
|
+
nuc_label = np.repeat(
|
|
118
|
+
nuc_label[np.newaxis],
|
|
119
|
+
repeats= len(nuc_image),
|
|
120
|
+
axis=0
|
|
121
|
+
)
|
|
122
|
+
if type(cyto_label) != type(None) :
|
|
123
|
+
|
|
124
|
+
if type(cyto_image) == type(None) : raise ValueError("If cyto label is passed cyto image must be passed too.")
|
|
125
|
+
|
|
126
|
+
if dim == 3 and cyto_label.ndim == 2 :
|
|
127
|
+
cyto_label = np.repeat(
|
|
128
|
+
cyto_label[np.newaxis],
|
|
129
|
+
repeats= len(nuc_image),
|
|
130
|
+
axis=0
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
#Init Napari viewer
|
|
134
|
+
Viewer = napari.Viewer(ndisplay=2, title= 'Show segmentation', axis_labels=['z','y','x'] if dim == 3 else ['y', 'x'], show= False)
|
|
135
|
+
|
|
136
|
+
# Adding channels
|
|
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
|
|
140
|
+
|
|
141
|
+
#Adding labels
|
|
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()])
|
|
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
|
|
146
|
+
|
|
147
|
+
#Launch Napari
|
|
148
|
+
Viewer.show(block=False)
|
|
149
|
+
napari.run()
|
|
150
|
+
|
|
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
|
|
154
|
+
else : new_cyto_label = new_nuc_label
|
|
155
|
+
|
|
156
|
+
return new_nuc_label, new_cyto_label
|
|
157
|
+
|
|
158
|
+
def threshold_selection(
|
|
159
|
+
image : np.ndarray,
|
|
160
|
+
filtered_image : np.ndarray,
|
|
161
|
+
threshold_slider,
|
|
162
|
+
voxel_size : tuple,
|
|
163
|
+
) :
|
|
164
|
+
|
|
165
|
+
"""
|
|
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.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
Viewer = napari.Viewer(title= "Small fish - Threshold selector", ndisplay=2, show=True)
|
|
170
|
+
scale = compute_anisotropy_coef(voxel_size)
|
|
171
|
+
Viewer.add_image(
|
|
172
|
+
data= image,
|
|
173
|
+
contrast_limits= [image.min(), image.max()],
|
|
174
|
+
name= "raw signal",
|
|
175
|
+
colormap= 'green',
|
|
176
|
+
scale= scale,
|
|
177
|
+
blending= 'additive'
|
|
178
|
+
)
|
|
179
|
+
Viewer.add_image(
|
|
180
|
+
data= filtered_image,
|
|
181
|
+
contrast_limits= [filtered_image.min(), filtered_image.max()],
|
|
182
|
+
colormap= 'gray',
|
|
183
|
+
scale=scale,
|
|
184
|
+
blending='additive'
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
Viewer.window.add_dock_widget(threshold_slider, name='threshold_selector')
|
|
188
|
+
threshold_slider() #First occurence with auto or entered threshold.
|
|
189
|
+
|
|
190
|
+
napari.run()
|
|
191
|
+
|
|
192
|
+
spots = Viewer.layers['single spots'].data.astype(int)
|
|
193
|
+
if len(spots) == 0 :
|
|
194
|
+
pass
|
|
195
|
+
else :
|
|
196
|
+
threshold = Viewer.layers['single spots'].properties.get('threshold')[0]
|
|
197
|
+
|
|
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
|
|
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"
|
|
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' :
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
"""
|