small-fish-gui 1.3.4__tar.gz → 1.3.5__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.3.4 → small_fish_gui-1.3.5}/PKG-INFO +1 -1
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/pyproject.toml +1 -1
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/__init__.py +1 -1
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/gui/__init__.py +1 -2
- small_fish_gui-1.3.5/src/small_fish_gui/gui/batch.py +312 -0
- small_fish_gui-1.3.5/src/small_fish_gui/gui/layout.py +307 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/gui/prompts.py +21 -23
- small_fish_gui-1.3.5/src/small_fish_gui/gui/test.py +5 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/pipeline/_preprocess.py +10 -11
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/pipeline/_segmentation.py +1 -1
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/pipeline/detection.py +4 -1
- small_fish_gui-1.3.4/src/small_fish_gui/gui/layout.py +0 -184
- small_fish_gui-1.3.4/src/small_fish_gui/gui/test.py +0 -4
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/LICENSE +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/README.md +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/.github/workflows/python-publish.yml +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/LICENSE +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/README.md +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/Segmentation example.jpg +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/__main__.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/gui/animation.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/gui/general_help_screenshot.png +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/gui/help_module.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/gui/mapping_help_screenshot.png +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/gui/segmentation_help_screenshot.png +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/interface/__init__.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/interface/image.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/interface/output.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/interface/parameters.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/interface/testing.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/napari_detection_example.png +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/pipeline/_colocalisation.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/pipeline/_custom_errors.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/pipeline/_napari_wrapper.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/pipeline/_signaltonoise.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/pipeline/actions.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/pipeline/main.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/pipeline/spots.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/pipeline/test.py +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/requirements.txt +0 -0
- {small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/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.
|
|
3
|
+
Version: 1.3.5
|
|
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
|
|
@@ -10,7 +10,6 @@ from .prompts import input_image_prompt
|
|
|
10
10
|
from .prompts import hub_prompt
|
|
11
11
|
from .prompts import detection_parameters_promt
|
|
12
12
|
from .prompts import coloc_prompt
|
|
13
|
-
from .prompts import post_analysis_prompt
|
|
14
13
|
from .prompts import output_image_prompt
|
|
15
14
|
from .prompts import ask_cancel_detection
|
|
16
15
|
from .prompts import ask_cancel_segmentation
|
|
@@ -21,7 +20,7 @@ from .prompts import ask_detection_confirmation
|
|
|
21
20
|
from .layout import parameters_layout
|
|
22
21
|
from .layout import bool_layout
|
|
23
22
|
from .layout import path_layout
|
|
24
|
-
from .layout import
|
|
23
|
+
from .layout import combo_elmt
|
|
25
24
|
from .layout import tuple_layout
|
|
26
25
|
from .layout import radio_layout
|
|
27
26
|
from .layout import add_header
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import numpy as np
|
|
3
|
+
import PySimpleGUI as sg
|
|
4
|
+
import bigfish.stack as stack
|
|
5
|
+
import czifile as czi
|
|
6
|
+
|
|
7
|
+
from .layout import _segmentation_layout, _detection_layout, _input_parameters_layout, _ask_channel_map_layout
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
from time import sleep
|
|
11
|
+
|
|
12
|
+
def get_images(filename:str) :
|
|
13
|
+
"""returns filename if is image else return None"""
|
|
14
|
+
|
|
15
|
+
supported_types = ('.tiff', '.tif', '.png', '.czi')
|
|
16
|
+
if filename.endswith(supported_types) :
|
|
17
|
+
return [filename]
|
|
18
|
+
else :
|
|
19
|
+
return None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_files(path) :
|
|
23
|
+
|
|
24
|
+
filelist = os.listdir(path)
|
|
25
|
+
filelist = list(map(get_images,filelist))
|
|
26
|
+
|
|
27
|
+
while None in filelist : filelist.remove(None)
|
|
28
|
+
|
|
29
|
+
return filelist
|
|
30
|
+
|
|
31
|
+
def extract_files(filenames: list) :
|
|
32
|
+
return sum(filenames,[])
|
|
33
|
+
|
|
34
|
+
def check_file(filename:str) :
|
|
35
|
+
|
|
36
|
+
if filename.endswith('.czi') :
|
|
37
|
+
image = czi.imread(filename)
|
|
38
|
+
else :
|
|
39
|
+
image = stack.read_image(filename)
|
|
40
|
+
|
|
41
|
+
image = np.squeeze(image)
|
|
42
|
+
|
|
43
|
+
return image.shape
|
|
44
|
+
|
|
45
|
+
def sanity_check(
|
|
46
|
+
filename_list: list,
|
|
47
|
+
batch_folder : str,
|
|
48
|
+
window : sg.Window,
|
|
49
|
+
progress_bar: sg.ProgressBar,
|
|
50
|
+
) :
|
|
51
|
+
|
|
52
|
+
filenumber = len(filename_list)
|
|
53
|
+
if filenumber == 0 :
|
|
54
|
+
print("No file to check")
|
|
55
|
+
progress_bar.update(current_count= 0, bar_color=('gray','gray'))
|
|
56
|
+
return None
|
|
57
|
+
else :
|
|
58
|
+
print("{0} files to check".format(filenumber))
|
|
59
|
+
progress_bar.update(current_count=0, max= filenumber)
|
|
60
|
+
ref_shape = check_file(batch_folder + '/' + filename_list[0])
|
|
61
|
+
|
|
62
|
+
print("Starting sanity check. This could take some time...")
|
|
63
|
+
for i, file in enumerate(filename_list) :
|
|
64
|
+
progress_bar.update(current_count= i+1, bar_color=('green','gray'))
|
|
65
|
+
shape = check_file(batch_folder + '/' + file)
|
|
66
|
+
|
|
67
|
+
if len(shape) != len(ref_shape) : #then dimension missmatch
|
|
68
|
+
print("Different number of dimensions found : {0}, {1}".format(len(ref_shape), len(shape)))
|
|
69
|
+
progress_bar.update(current_count=filenumber, bar_color=('red','black'))
|
|
70
|
+
window= window.refresh()
|
|
71
|
+
break
|
|
72
|
+
|
|
73
|
+
window= window.refresh()
|
|
74
|
+
|
|
75
|
+
print("Sanity check completed.")
|
|
76
|
+
return None if len(shape) != len(ref_shape) else shape
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_elmt_from_key(Tab_elmt:sg.Tab, key) -> sg.Element:
|
|
80
|
+
elmt_list = sum(Tab_elmt.Rows,[])
|
|
81
|
+
for elmt in elmt_list :
|
|
82
|
+
if elmt.Key == key : return elmt
|
|
83
|
+
raise KeyError("{0} key not found amongst {1}.".format(key, [elmt.Key for elmt in elmt_list]))
|
|
84
|
+
|
|
85
|
+
def update_detection_tab(
|
|
86
|
+
tab_elmt:sg.Tab,
|
|
87
|
+
is_multichannel,
|
|
88
|
+
is_3D,
|
|
89
|
+
do_dense_region_deconvolution,
|
|
90
|
+
do_clustering
|
|
91
|
+
) :
|
|
92
|
+
|
|
93
|
+
#Acess elements
|
|
94
|
+
##Detection
|
|
95
|
+
channel_to_compute = get_elmt_from_key(tab_elmt, key= 'channel to compute')
|
|
96
|
+
voxel_size_z = get_elmt_from_key(tab_elmt, key= 'voxel_size_z')
|
|
97
|
+
spot_size_z = get_elmt_from_key(tab_elmt, key= 'spot_size_z')
|
|
98
|
+
log_kernel_size_z = get_elmt_from_key(tab_elmt, key= 'log_kernel_size_z')
|
|
99
|
+
minimum_distance_z = get_elmt_from_key(tab_elmt, key= 'minimum_distance_z')
|
|
100
|
+
|
|
101
|
+
##Dense regions deconvolution
|
|
102
|
+
alpha = get_elmt_from_key(tab_elmt, key= 'alpha')
|
|
103
|
+
beta = get_elmt_from_key(tab_elmt, key= 'beta')
|
|
104
|
+
gamma = get_elmt_from_key(tab_elmt, key= 'gamma')
|
|
105
|
+
deconvolution_kernel_z = get_elmt_from_key(tab_elmt, key= 'deconvolution_kernel_z')
|
|
106
|
+
cluster_size = get_elmt_from_key(tab_elmt, key= 'cluster size')
|
|
107
|
+
min_number_of_spot = get_elmt_from_key(tab_elmt, key= 'min number of spots')
|
|
108
|
+
nucleus_channel_signal = get_elmt_from_key(tab_elmt, key= 'nucleus channel signal')
|
|
109
|
+
interactive_threshold_selector = get_elmt_from_key(tab_elmt, key= 'Interactive threshold selector')
|
|
110
|
+
|
|
111
|
+
update_dict={
|
|
112
|
+
'is_3D' : is_3D,
|
|
113
|
+
'is_multichannel' : is_multichannel,
|
|
114
|
+
'do_dense_region_deconvolution' : do_dense_region_deconvolution,
|
|
115
|
+
'do_clustering' : do_clustering,
|
|
116
|
+
'always_hidden' : False
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
list_dict={
|
|
120
|
+
'is_3D' : [voxel_size_z, spot_size_z, log_kernel_size_z, minimum_distance_z, deconvolution_kernel_z],
|
|
121
|
+
'is_multichannel' : [channel_to_compute, nucleus_channel_signal],
|
|
122
|
+
'do_dense_region_deconvolution' : [alpha,beta,gamma],
|
|
123
|
+
'do_clustering' : [cluster_size, min_number_of_spot],
|
|
124
|
+
'always_hidden' : [interactive_threshold_selector]
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for key, enabled in update_dict.items() :
|
|
129
|
+
for elmt in list_dict.get(key) :
|
|
130
|
+
elmt.update(disabled=not enabled)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def update_segmentation_tab(tab_elmt : sg.Tab, do_segmentation, is_multichannel) : #TODO
|
|
135
|
+
|
|
136
|
+
#Access elements
|
|
137
|
+
cytoplasm_channel_elmt = get_elmt_from_key(tab_elmt, key= 'cytoplasm channel')
|
|
138
|
+
nucleus_channel_elmt = get_elmt_from_key(tab_elmt, key= 'nucleus channel')
|
|
139
|
+
|
|
140
|
+
#Update values
|
|
141
|
+
tab_elmt.update(visible=do_segmentation)
|
|
142
|
+
cytoplasm_channel_elmt.update(disabled = not is_multichannel)
|
|
143
|
+
nucleus_channel_elmt.update(disabled = not is_multichannel)
|
|
144
|
+
|
|
145
|
+
def update_map_tab() :
|
|
146
|
+
#TODO
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
def batch_promp() :
|
|
150
|
+
|
|
151
|
+
files_values = [[]]
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
#LOAD FILES
|
|
155
|
+
files_table = sg.Table(values=files_values, headings=['Filenames'], col_widths=100, max_col_width= 200, def_col_width=100, num_rows= 10, auto_size_columns=False)
|
|
156
|
+
|
|
157
|
+
#Start&Stop
|
|
158
|
+
start_button =sg.Button('Start', button_color= 'green', disabled= True)
|
|
159
|
+
stop_button = sg.Button('Cancel', button_color= 'red')
|
|
160
|
+
|
|
161
|
+
#DIMENSION SANITY
|
|
162
|
+
sanity_progress = sg.ProgressBar(10, size_px=(500,10))
|
|
163
|
+
sanity_check_button = sg.Button(
|
|
164
|
+
'Check',
|
|
165
|
+
tooltip= "Will check that all files loaded have the same dimension number and that small fish is able to open them.",
|
|
166
|
+
pad=(10,0))
|
|
167
|
+
sanity_header = sg.Text("Dimension sanity", font=('bold',15), pad=(0,10))
|
|
168
|
+
dimension_number_text = sg.Text("Dimension number : unknown")
|
|
169
|
+
|
|
170
|
+
#Input tab
|
|
171
|
+
input_layout = _input_parameters_layout(
|
|
172
|
+
ask_for_segmentation=True,
|
|
173
|
+
is_3D_stack_preset=False,
|
|
174
|
+
time_stack_preset=False,
|
|
175
|
+
multichannel_preset=False,
|
|
176
|
+
do_dense_regions_deconvolution_preset=False,
|
|
177
|
+
do_clustering_preset=False,
|
|
178
|
+
do_Napari_correction=False,
|
|
179
|
+
do_segmentation_preset=False,
|
|
180
|
+
)
|
|
181
|
+
input_layout += [[sg.Button('Ok')]]
|
|
182
|
+
input_tab = sg.Tab("Input", input_layout)
|
|
183
|
+
|
|
184
|
+
#Maptab
|
|
185
|
+
map_layout = _ask_channel_map_layout(
|
|
186
|
+
shape=(0,1,2,3,4),
|
|
187
|
+
is_3D_stack=True,
|
|
188
|
+
is_time_stack=True,
|
|
189
|
+
multichannel=True,
|
|
190
|
+
)
|
|
191
|
+
last_shape_read = sg.Text("Last shape read : None")
|
|
192
|
+
auto_map = sg.Button("auto-map", disabled=True, pad=(10,0))
|
|
193
|
+
map_layout += [[last_shape_read, auto_map]]
|
|
194
|
+
map_tab = sg.Tab("Map", map_layout)
|
|
195
|
+
|
|
196
|
+
#Segmentation tab
|
|
197
|
+
segmentation_layout = _segmentation_layout(multichannel=True, cytoplasm_model_preset='cyto3')
|
|
198
|
+
segmentation_tab = sg.Tab("Segmentation", segmentation_layout, visible=False)
|
|
199
|
+
|
|
200
|
+
#Detection tab
|
|
201
|
+
detection_layout = _detection_layout(
|
|
202
|
+
is_3D_stack=True,
|
|
203
|
+
is_multichannel=True,
|
|
204
|
+
do_clustering=True,
|
|
205
|
+
do_dense_region_deconvolution=True,
|
|
206
|
+
do_segmentation=True,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
detection_tab = sg.Tab("Detection", detection_layout)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
#TABS
|
|
215
|
+
_tab_group = sg.TabGroup([[input_tab, map_tab, segmentation_tab, detection_tab]], enable_events=True)
|
|
216
|
+
tab_group = sg.Column( #Allow the tab to be scrollable
|
|
217
|
+
[[_tab_group]],
|
|
218
|
+
scrollable=True,
|
|
219
|
+
vertical_scroll_only=True,
|
|
220
|
+
pad=(150,5)
|
|
221
|
+
)
|
|
222
|
+
tab_dict= {
|
|
223
|
+
"Input" : input_tab,
|
|
224
|
+
"Segmentation" : segmentation_tab,
|
|
225
|
+
"Detection" : detection_tab,
|
|
226
|
+
"Map" : map_tab,
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
layout = [
|
|
230
|
+
[sg.Text("Batch Processing", font=('bold',20), pad=((300,0),(0,2)))],
|
|
231
|
+
[sg.Text("Select a folder : "), sg.FolderBrowse(initial_folder=os.getcwd(), key='Batch_folder'), sg.Button('Load')],
|
|
232
|
+
[files_table],
|
|
233
|
+
[sanity_header, sanity_check_button, sanity_progress],
|
|
234
|
+
[dimension_number_text],
|
|
235
|
+
[tab_group],
|
|
236
|
+
[sg.Output(size=(100,10), pad=(30,10))],
|
|
237
|
+
[start_button, stop_button],
|
|
238
|
+
]
|
|
239
|
+
|
|
240
|
+
window = sg.Window("small fish", layout=layout, size= (800,800), auto_size_buttons=True, auto_size_text=True)
|
|
241
|
+
loop = 0
|
|
242
|
+
timeout = 1
|
|
243
|
+
while True :
|
|
244
|
+
loop +=1
|
|
245
|
+
window = window.refresh()
|
|
246
|
+
event, values = window.read(timeout=timeout)
|
|
247
|
+
|
|
248
|
+
#Welcome message
|
|
249
|
+
if loop == 1 :
|
|
250
|
+
timeout = None
|
|
251
|
+
print("Welcome to small fish batch analysis. Please start by loading some files and setting parameters.")
|
|
252
|
+
|
|
253
|
+
batch_folder = values.get('Batch_folder')
|
|
254
|
+
is_multichanel = values.get('multichannel')
|
|
255
|
+
is_3D = values.get('3D stack')
|
|
256
|
+
do_segmentation = values.get('Segmentation')
|
|
257
|
+
do_dense_regions_deconvolution = values.get('Dense regions deconvolution')
|
|
258
|
+
do_clustering = values.get('Cluster computation')
|
|
259
|
+
|
|
260
|
+
if type(batch_folder) != type(None) and event == 'Load':
|
|
261
|
+
if not os.path.isdir(batch_folder) :
|
|
262
|
+
print("Can't open {0}".format(batch_folder))
|
|
263
|
+
else :
|
|
264
|
+
files_values = get_files(batch_folder)
|
|
265
|
+
files_table.update(values=files_values)
|
|
266
|
+
|
|
267
|
+
elif event == 'Check' :
|
|
268
|
+
filename_list = extract_files(files_values)
|
|
269
|
+
last_shape = sanity_check(
|
|
270
|
+
filename_list=filename_list,
|
|
271
|
+
batch_folder=batch_folder,
|
|
272
|
+
window=window,
|
|
273
|
+
progress_bar=sanity_progress
|
|
274
|
+
)
|
|
275
|
+
if isinstance(last_shape,(tuple,list)) :
|
|
276
|
+
dim_number = len(last_shape)
|
|
277
|
+
dimension_number_text.update("Dimension number : {0}".format(dim_number))
|
|
278
|
+
auto_map.update(disabled=False)
|
|
279
|
+
else :
|
|
280
|
+
dimension_number_text.update("Dimension number : unknown")
|
|
281
|
+
auto_map.update(disabled=True)
|
|
282
|
+
|
|
283
|
+
last_shape_read.update("Last shape read : {0}".format(last_shape))
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
elif event == _tab_group.key or event == 'Ok': #Tab switch in parameters
|
|
287
|
+
update_segmentation_tab(
|
|
288
|
+
tab_elmt=tab_dict.get("Segmentation"),
|
|
289
|
+
do_segmentation=do_segmentation,
|
|
290
|
+
is_multichannel=is_multichanel,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
update_detection_tab(
|
|
294
|
+
tab_elmt=tab_dict.get("Detection"),
|
|
295
|
+
is_multichannel=is_multichanel,
|
|
296
|
+
is_3D=is_3D,
|
|
297
|
+
do_dense_region_deconvolution=do_dense_regions_deconvolution,
|
|
298
|
+
do_clustering=do_clustering,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
elif event == 'auto-map' :
|
|
302
|
+
#TODO
|
|
303
|
+
pass
|
|
304
|
+
|
|
305
|
+
# elif event == 'apply' (map) #TODO
|
|
306
|
+
|
|
307
|
+
# elif event == 'check parameters' -> un/lock start #TODO
|
|
308
|
+
|
|
309
|
+
elif event == "Cancel" :
|
|
310
|
+
print(values)
|
|
311
|
+
elif event == None :
|
|
312
|
+
quit()
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import PySimpleGUI as sg
|
|
2
|
+
import os
|
|
3
|
+
from ..utils import check_parameter
|
|
4
|
+
import cellpose.models as models
|
|
5
|
+
from cellpose.core import use_gpu
|
|
6
|
+
|
|
7
|
+
sg.theme('DarkAmber')
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def add_header(header_text) :
|
|
11
|
+
"""Returns [elmnt] not layout"""
|
|
12
|
+
header = [sg.Text('\n{0}'.format(header_text), size= (len(header_text),3), font= 'bold 15')]
|
|
13
|
+
return header
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def pad_right(string, length, pad_char) :
|
|
17
|
+
if len(string) >= length : return string
|
|
18
|
+
else : return string + pad_char* (length - len(string))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def parameters_layout(parameters:'list[str]' = [], unit=None, header= None, default_values=None, size=5, opt=None) :
|
|
22
|
+
|
|
23
|
+
if len(parameters) == 0 : return []
|
|
24
|
+
check_parameter(parameters= list, header = (str, type(None)))
|
|
25
|
+
for key in parameters : check_parameter(key = str)
|
|
26
|
+
max_length = len(max(parameters, key=len))
|
|
27
|
+
|
|
28
|
+
if type(opt) == type(None) :
|
|
29
|
+
opt = [False] * len(parameters)
|
|
30
|
+
else :
|
|
31
|
+
if len(opt) != len(parameters) : raise ValueError("Parameters and opt must be of same length.")
|
|
32
|
+
|
|
33
|
+
if isinstance(default_values, (list, tuple)) :
|
|
34
|
+
if len(default_values) != len(parameters) : raise ValueError("if default values specified it must be of equal length as parameters.")
|
|
35
|
+
layout= [
|
|
36
|
+
[sg.Text("{0}".format(pad_right(parameter, max_length, ' ')), text_color= 'green' if option else None),
|
|
37
|
+
sg.InputText(size= size, key= parameter, default_text= value)
|
|
38
|
+
|
|
39
|
+
] for parameter,value, option in zip(parameters,default_values, opt)
|
|
40
|
+
]
|
|
41
|
+
else :
|
|
42
|
+
layout= [
|
|
43
|
+
[sg.Text("{0}".format(pad_right(parameter, max_length, ' ')), text_color= 'green' if option else None),
|
|
44
|
+
sg.InputText(size= size, key= parameter)
|
|
45
|
+
|
|
46
|
+
] for parameter, option in zip(parameters, opt)
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
if type(unit) == str :
|
|
50
|
+
for line_id, line in enumerate(layout) :
|
|
51
|
+
layout[line_id] += [sg.Text('{0}'.format(unit))]
|
|
52
|
+
|
|
53
|
+
if isinstance(header, str) :
|
|
54
|
+
layout = [add_header(header)] + layout
|
|
55
|
+
return layout
|
|
56
|
+
|
|
57
|
+
def tuple_layout(opt=None, default_dict={}, unit:dict={}, **tuples) :
|
|
58
|
+
"""
|
|
59
|
+
tuples example : voxel_size = ['z','y','x']; will ask a tuple with 3 element default to 'z', 'y' 'x'.
|
|
60
|
+
"""
|
|
61
|
+
if type(tuples) == type(None) : return []
|
|
62
|
+
if len(tuples.keys()) == 0 : return []
|
|
63
|
+
|
|
64
|
+
if type(opt) != type(None) :
|
|
65
|
+
if not isinstance(opt, dict) : raise TypeError("opt parameter should be either None or dict type.")
|
|
66
|
+
if not opt.keys() == tuples.keys() : raise ValueError("If opt is passed it is expected to have same keys as tuples dict.")
|
|
67
|
+
else :
|
|
68
|
+
opt = tuples.copy()
|
|
69
|
+
for key in opt.keys() :
|
|
70
|
+
opt[key] = False
|
|
71
|
+
|
|
72
|
+
for tup in tuples :
|
|
73
|
+
if not isinstance(tuples[tup], (list,tuple)) : raise TypeError()
|
|
74
|
+
|
|
75
|
+
max_size = len(max(tuples.keys(), key=len))
|
|
76
|
+
|
|
77
|
+
layout = [
|
|
78
|
+
[sg.Text(pad_right(tup, max_size, ' '), text_color= 'green' if opt[option] else None)]
|
|
79
|
+
+ [sg.InputText(default_text=default_dict.setdefault('{0}_{1}'.format(tup,elmnt), elmnt),key= '{0}_{1}'.format(tup, elmnt), size= 5) for elmnt in tuples[tup]]
|
|
80
|
+
+ [sg.Text(unit.setdefault(tup,''))]
|
|
81
|
+
for tup,option, in zip(tuples,opt)
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
return layout
|
|
85
|
+
|
|
86
|
+
def path_layout(keys= [],look_for_dir = False, header=None, preset=os.getcwd()) :
|
|
87
|
+
"""
|
|
88
|
+
If not look for dir then looks for file.
|
|
89
|
+
"""
|
|
90
|
+
if len(keys) == 0 : return []
|
|
91
|
+
check_parameter(keys= list, header = (str, type(None)))
|
|
92
|
+
for key in keys : check_parameter(key = str)
|
|
93
|
+
if look_for_dir : Browse = sg.FolderBrowse
|
|
94
|
+
else : Browse = sg.FileBrowse
|
|
95
|
+
|
|
96
|
+
max_length = len(max(keys, key=len))
|
|
97
|
+
layout = [
|
|
98
|
+
[sg.Text(pad_right(name, max_length, ' ')), Browse(key= name, initial_folder= preset)] for name in keys
|
|
99
|
+
]
|
|
100
|
+
if isinstance(header, str) :
|
|
101
|
+
layout = [add_header(header)] + layout
|
|
102
|
+
return layout
|
|
103
|
+
|
|
104
|
+
def bool_layout(parameters= [], header=None, preset=None) :
|
|
105
|
+
if len(parameters) == 0 : return []
|
|
106
|
+
check_parameter(parameters= list, header= (str, type(None)), preset=(type(None), list, tuple, bool))
|
|
107
|
+
for key in parameters : check_parameter(key = str)
|
|
108
|
+
if type(preset) == type(None) :
|
|
109
|
+
preset = [False] * len(parameters)
|
|
110
|
+
elif type(preset) == bool :
|
|
111
|
+
preset = [preset] * len(parameters)
|
|
112
|
+
else :
|
|
113
|
+
for key in preset : check_parameter(key = bool)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
max_length = len(max(parameters, key=len))
|
|
118
|
+
layout = [
|
|
119
|
+
[sg.Checkbox(pad_right(name, max_length, ' '), key= name, default=box_preset)] for name, box_preset in zip(parameters,preset)
|
|
120
|
+
]
|
|
121
|
+
if isinstance(header, str) :
|
|
122
|
+
layout = [add_header(header)] + layout
|
|
123
|
+
return layout
|
|
124
|
+
|
|
125
|
+
def combo_elmt(values, key, header=None, read_only=True, default_value=None) :
|
|
126
|
+
"""
|
|
127
|
+
drop-down list
|
|
128
|
+
"""
|
|
129
|
+
if len(values) == 0 : return []
|
|
130
|
+
check_parameter(values= list, header= (str, type(None)))
|
|
131
|
+
if type(default_value) == type(None) :
|
|
132
|
+
default_value = values[0]
|
|
133
|
+
elif default_value not in values :
|
|
134
|
+
default_value = values[0]
|
|
135
|
+
layout = [
|
|
136
|
+
sg.Combo(values, default_value=default_value, readonly=read_only, key=key)
|
|
137
|
+
]
|
|
138
|
+
if isinstance(header, str) :
|
|
139
|
+
layout = add_header(header) + layout
|
|
140
|
+
return layout
|
|
141
|
+
|
|
142
|
+
def radio_layout(values, header=None) :
|
|
143
|
+
"""
|
|
144
|
+
Single choice buttons.
|
|
145
|
+
"""
|
|
146
|
+
if len(values) == 0 : return []
|
|
147
|
+
check_parameter(values= list, header= (str, type(None)))
|
|
148
|
+
layout = [
|
|
149
|
+
[sg.Radio(value, group_id= 0) for value in values]
|
|
150
|
+
]
|
|
151
|
+
if isinstance(header, str) :
|
|
152
|
+
layout = [add_header(header)] + layout
|
|
153
|
+
return layout
|
|
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',) :
|
|
156
|
+
|
|
157
|
+
USE_GPU = use_gpu()
|
|
158
|
+
|
|
159
|
+
models_list = models.get_user_models() + models.MODEL_NAMES
|
|
160
|
+
if len(models_list) == 0 : models_list = ['no model found']
|
|
161
|
+
|
|
162
|
+
#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')]]
|
|
164
|
+
|
|
165
|
+
#cytoplasm parameters
|
|
166
|
+
layout += [
|
|
167
|
+
add_header("Cell Segmentation"),
|
|
168
|
+
[sg.Text("Choose cellpose model for cytoplasm: \n")],
|
|
169
|
+
combo_elmt(models_list, key='cyto_model_name', default_value= cytoplasm_model_preset)
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
if multichannel : layout += parameters_layout(['cytoplasm channel'],default_values= [cytoplasm_channel_preset])
|
|
173
|
+
layout += parameters_layout(['cytoplasm diameter'], unit= "px", default_values= [cyto_diameter_preset])
|
|
174
|
+
#Nucleus parameters
|
|
175
|
+
layout += [
|
|
176
|
+
add_header("Nucleus segmentation"),
|
|
177
|
+
[sg.Text("Choose cellpose model for nucleus: \n")],
|
|
178
|
+
combo_elmt(models_list, key='nucleus_model_name', default_value= nucleus_model_preset)
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
if multichannel : layout += parameters_layout(['nucleus channel'], default_values= [nucleus_channel_preset])
|
|
182
|
+
layout += parameters_layout([ 'nucleus diameter'],unit= "px", default_values= [nucleus_diameter_preset])
|
|
183
|
+
layout += bool_layout(["Segment only nuclei"], preset=segment_only_nuclei_preset)
|
|
184
|
+
|
|
185
|
+
#Control plots
|
|
186
|
+
layout += bool_layout(['show segmentation'], header= 'Segmentation plots', preset= show_segmentation_preset)
|
|
187
|
+
layout += path_layout(['saving path'], look_for_dir=True, preset=saving_path_preset)
|
|
188
|
+
layout += parameters_layout(['filename'], default_values=[filename_preset], size= 25)
|
|
189
|
+
|
|
190
|
+
return layout
|
|
191
|
+
|
|
192
|
+
def _input_parameters_layout(
|
|
193
|
+
ask_for_segmentation,
|
|
194
|
+
is_3D_stack_preset,
|
|
195
|
+
time_stack_preset,
|
|
196
|
+
multichannel_preset,
|
|
197
|
+
do_dense_regions_deconvolution_preset,
|
|
198
|
+
do_clustering_preset,
|
|
199
|
+
do_segmentation_preset,
|
|
200
|
+
do_Napari_correction
|
|
201
|
+
|
|
202
|
+
) :
|
|
203
|
+
layout_image_path = path_layout(['image path'], header= "Image")
|
|
204
|
+
layout_image_path += bool_layout(['3D stack', 'multichannel'], preset= [is_3D_stack_preset, time_stack_preset, multichannel_preset])
|
|
205
|
+
|
|
206
|
+
if ask_for_segmentation :
|
|
207
|
+
layout_image_path += bool_layout(
|
|
208
|
+
['Dense regions deconvolution', 'Cluster computation', 'Segmentation', 'Napari correction'],
|
|
209
|
+
preset= [do_dense_regions_deconvolution_preset, do_clustering_preset, do_segmentation_preset, do_Napari_correction],
|
|
210
|
+
header= "Pipeline settings")
|
|
211
|
+
else :
|
|
212
|
+
layout_image_path += bool_layout(
|
|
213
|
+
['Dense regions deconvolution', 'Cluster computation', 'Napari correction'],
|
|
214
|
+
preset= [do_dense_regions_deconvolution_preset, do_clustering_preset, do_Napari_correction],
|
|
215
|
+
header= "Pipeline settings")
|
|
216
|
+
|
|
217
|
+
return layout_image_path
|
|
218
|
+
|
|
219
|
+
def _detection_layout(
|
|
220
|
+
is_3D_stack,
|
|
221
|
+
is_multichannel,
|
|
222
|
+
do_dense_region_deconvolution,
|
|
223
|
+
do_clustering,
|
|
224
|
+
do_segmentation,
|
|
225
|
+
segmentation_done=False,
|
|
226
|
+
default_dict={},
|
|
227
|
+
) :
|
|
228
|
+
if is_3D_stack : dim = 3
|
|
229
|
+
else : dim = 2
|
|
230
|
+
|
|
231
|
+
#Detection
|
|
232
|
+
detection_parameters = ['threshold', 'threshold penalty']
|
|
233
|
+
default_detection = [default_dict.setdefault('threshold',''), default_dict.setdefault('threshold penalty', '1')]
|
|
234
|
+
opt= [True, True]
|
|
235
|
+
if is_multichannel :
|
|
236
|
+
detection_parameters += ['channel to compute']
|
|
237
|
+
opt += [False]
|
|
238
|
+
default_detection += [default_dict.setdefault('channel to compute', '')]
|
|
239
|
+
|
|
240
|
+
layout = [[sg.Text("Green parameters", text_color= 'green'), sg.Text(" are optional parameters.")]]
|
|
241
|
+
layout += parameters_layout(detection_parameters, header= 'Detection', opt=opt, default_values=default_detection)
|
|
242
|
+
|
|
243
|
+
if dim == 2 : tuple_shape = ('y','x')
|
|
244
|
+
else : tuple_shape = ('z','y','x')
|
|
245
|
+
opt = {'voxel_size' : False, 'spot_size' : False, 'log_kernel_size' : True, 'minimum_distance' : True}
|
|
246
|
+
unit = {'voxel_size' : 'nm', 'minimum_distance' : 'nm', 'spot_size' : 'radius(nm)', 'log_kernel_size' : 'px'}
|
|
247
|
+
|
|
248
|
+
layout += tuple_layout(opt=opt, unit=unit, default_dict=default_dict, voxel_size= tuple_shape, spot_size= tuple_shape, log_kernel_size= tuple_shape, minimum_distance= tuple_shape)
|
|
249
|
+
|
|
250
|
+
#Deconvolution
|
|
251
|
+
if do_dense_region_deconvolution :
|
|
252
|
+
default_dense_regions_deconvolution = [default_dict.setdefault('alpha',0.5), default_dict.setdefault('beta',1)]
|
|
253
|
+
layout += parameters_layout(['alpha', 'beta',], default_values= default_dense_regions_deconvolution, header= 'Dense regions deconvolution')
|
|
254
|
+
layout += parameters_layout(['gamma'], unit= 'px', default_values= [default_dict.setdefault('gamma',5)])
|
|
255
|
+
layout += tuple_layout(opt= {"deconvolution_kernel" : True}, unit= {"deconvolution_kernel" : 'px'}, default_dict=default_dict, deconvolution_kernel = tuple_shape)
|
|
256
|
+
|
|
257
|
+
#Clustering
|
|
258
|
+
if do_clustering :
|
|
259
|
+
layout += parameters_layout(['cluster size'], unit="radius(nm)", default_values=[default_dict.setdefault('cluster size',400)])
|
|
260
|
+
layout += parameters_layout(['min number of spots'], default_values=[default_dict.setdefault('min number of spots', 5)])
|
|
261
|
+
|
|
262
|
+
if (do_segmentation and is_multichannel) or (is_multichannel and segmentation_done):
|
|
263
|
+
default_segmentation = [default_dict.setdefault('nucleus channel signal', default_dict.setdefault('nucleus channel',0))]
|
|
264
|
+
layout += parameters_layout(['nucleus channel signal'], default_values=default_segmentation) + [[sg.Text(" channel from which signal will be measured for nucleus features.")]]
|
|
265
|
+
|
|
266
|
+
layout += bool_layout(['Interactive threshold selector'], preset=[False])
|
|
267
|
+
layout += path_layout(
|
|
268
|
+
keys=['spots_extraction_folder'],
|
|
269
|
+
look_for_dir=True,
|
|
270
|
+
header= "Individual spot extraction",
|
|
271
|
+
preset= default_dict.setdefault('spots_extraction_folder', '')
|
|
272
|
+
)
|
|
273
|
+
layout += parameters_layout(
|
|
274
|
+
parameters=['spots_filename'],
|
|
275
|
+
default_values=[default_dict.setdefault('spots_filename','spots_extraction')],
|
|
276
|
+
size= 13
|
|
277
|
+
)
|
|
278
|
+
layout += bool_layout(
|
|
279
|
+
parameters= ['do_spots_csv', 'do_spots_excel', 'do_spots_feather'],
|
|
280
|
+
preset= [default_dict.setdefault('do_spots_csv',False), default_dict.setdefault('do_spots_excel',False),default_dict.setdefault('do_spots_feather',False)]
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
return layout
|
|
284
|
+
|
|
285
|
+
def _ask_channel_map_layout(
|
|
286
|
+
shape,
|
|
287
|
+
is_3D_stack,
|
|
288
|
+
multichannel,
|
|
289
|
+
is_time_stack,
|
|
290
|
+
preset_map={},
|
|
291
|
+
) :
|
|
292
|
+
|
|
293
|
+
x = preset_map.setdefault('x',0)
|
|
294
|
+
y = preset_map.setdefault('y',0)
|
|
295
|
+
z = preset_map.setdefault('z',0)
|
|
296
|
+
c = preset_map.setdefault('c',0)
|
|
297
|
+
t = preset_map.setdefault('t',0)
|
|
298
|
+
|
|
299
|
+
layout = [
|
|
300
|
+
add_header("Dimensions mapping"), [sg.Text("Image shape : {0}".format(shape))]
|
|
301
|
+
]
|
|
302
|
+
layout += parameters_layout(['x','y'], default_values=[x,y])
|
|
303
|
+
if is_3D_stack : layout += parameters_layout(['z'], default_values=[z])
|
|
304
|
+
if multichannel : layout += parameters_layout(['c'], default_values=[c])
|
|
305
|
+
if is_time_stack : layout += parameters_layout(['t'], default_values=[t])
|
|
306
|
+
|
|
307
|
+
return layout
|
|
@@ -2,18 +2,24 @@ import PySimpleGUI as sg
|
|
|
2
2
|
import pandas as pd
|
|
3
3
|
import os
|
|
4
4
|
import numpy as np
|
|
5
|
-
from .layout import path_layout, parameters_layout, bool_layout, tuple_layout,
|
|
5
|
+
from .layout import path_layout, parameters_layout, bool_layout, tuple_layout, combo_elmt, add_header, path_layout
|
|
6
6
|
from ..interface import open_image, check_format, FormatError
|
|
7
7
|
from .help_module import ask_help
|
|
8
8
|
|
|
9
|
-
def prompt(layout, add_ok_cancel=True, timeout=None, timeout_key='TIMEOUT_KEY') :
|
|
9
|
+
def prompt(layout, add_ok_cancel=True, timeout=None, timeout_key='TIMEOUT_KEY', add_scrollbar=True) :
|
|
10
10
|
"""
|
|
11
11
|
Default event : 'Ok', 'Cancel'
|
|
12
12
|
"""
|
|
13
13
|
if add_ok_cancel : layout += [[sg.Button('Ok'), sg.Button('Cancel')]]
|
|
14
14
|
|
|
15
|
+
if add_scrollbar :
|
|
16
|
+
size = (400,500)
|
|
17
|
+
col_elmt = sg.Column(layout, scrollable=True, vertical_scroll_only=True, size=size)
|
|
18
|
+
layout = [[col_elmt]]
|
|
19
|
+
else :
|
|
20
|
+
size = (None,None)
|
|
15
21
|
|
|
16
|
-
window = sg.Window('small fish', layout=layout, margins=(10,10))
|
|
22
|
+
window = sg.Window('small fish', layout=layout, margins=(10,10), size=size, resizable=True)
|
|
17
23
|
event, values = window.read(timeout=timeout, timeout_key=timeout_key)
|
|
18
24
|
if event == None :
|
|
19
25
|
window.close()
|
|
@@ -26,12 +32,19 @@ def prompt(layout, add_ok_cancel=True, timeout=None, timeout_key='TIMEOUT_KEY')
|
|
|
26
32
|
window.close()
|
|
27
33
|
return event, values
|
|
28
34
|
|
|
29
|
-
def prompt_with_help(layout, help =None) :
|
|
35
|
+
def prompt_with_help(layout, help =None, add_scrollbar=True) :
|
|
30
36
|
layout += [[]]
|
|
31
37
|
layout += [[sg.Button('Help')]]
|
|
32
38
|
layout += [[sg.Button('Ok'), sg.Button('Cancel')]]
|
|
33
39
|
|
|
34
|
-
|
|
40
|
+
if add_scrollbar :
|
|
41
|
+
size = (400,500)
|
|
42
|
+
col_elmt = sg.Column(layout, scrollable=True, vertical_scroll_only=True, size=size)
|
|
43
|
+
layout = [[col_elmt]]
|
|
44
|
+
else :
|
|
45
|
+
size = (None,None)
|
|
46
|
+
|
|
47
|
+
window = sg.Window('small fish', layout=layout, size=size, resizable=True)
|
|
35
48
|
while True :
|
|
36
49
|
event, values = window.read()
|
|
37
50
|
if event == None :
|
|
@@ -79,7 +92,7 @@ def input_image_prompt(
|
|
|
79
92
|
else :
|
|
80
93
|
layout_image_path += bool_layout(['Dense regions deconvolution', 'Cluster computation', 'Napari correction'], preset= [do_dense_regions_deconvolution_preset, do_clustering_preset, do_Napari_correction], header= "Pipeline settings")
|
|
81
94
|
|
|
82
|
-
event, values = prompt_with_help(layout_image_path, help= 'general')
|
|
95
|
+
event, values = prompt_with_help(layout_image_path, help= 'general', add_scrollbar=False)
|
|
83
96
|
|
|
84
97
|
if event == 'Cancel' :
|
|
85
98
|
return None
|
|
@@ -227,22 +240,6 @@ def detection_parameters_promt(is_3D_stack, is_multichannel, do_dense_region_dec
|
|
|
227
240
|
else : values['dim'] = 2
|
|
228
241
|
return values
|
|
229
242
|
|
|
230
|
-
def post_analysis_prompt() :
|
|
231
|
-
answer = events(['Save results','add_detection', 'colocalisation', 'open results in napari'])
|
|
232
|
-
|
|
233
|
-
return answer
|
|
234
|
-
|
|
235
|
-
def events(event_list) :
|
|
236
|
-
"""
|
|
237
|
-
Return event chose from user
|
|
238
|
-
"""
|
|
239
|
-
|
|
240
|
-
layout = [
|
|
241
|
-
[sg.Button(event) for event in event_list]
|
|
242
|
-
]
|
|
243
|
-
|
|
244
|
-
event, values = prompt(layout, add_ok_cancel= False)
|
|
245
|
-
return event
|
|
246
243
|
|
|
247
244
|
def ask_replace_file(filename:str) :
|
|
248
245
|
layout = [
|
|
@@ -354,4 +351,5 @@ def ask_cancel_detection() :
|
|
|
354
351
|
if event == 'No' :
|
|
355
352
|
return False
|
|
356
353
|
else :
|
|
357
|
-
return True
|
|
354
|
+
return True
|
|
355
|
+
|
|
@@ -122,14 +122,13 @@ def _auto_map_channels(image: np.ndarray, is_3D_stack, is_time_stack, multichann
|
|
|
122
122
|
return map
|
|
123
123
|
|
|
124
124
|
def _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map: dict= {}) :
|
|
125
|
-
map = preset_map
|
|
126
125
|
while True :
|
|
127
126
|
relaunch = False
|
|
128
|
-
x =
|
|
129
|
-
y =
|
|
130
|
-
z =
|
|
131
|
-
c =
|
|
132
|
-
t =
|
|
127
|
+
x = preset_map.setdefault('x',0)
|
|
128
|
+
y = preset_map.setdefault('y',0)
|
|
129
|
+
z = preset_map.setdefault('z',0)
|
|
130
|
+
c = preset_map.setdefault('c',0)
|
|
131
|
+
t = preset_map.setdefault('t',0)
|
|
133
132
|
|
|
134
133
|
layout = [
|
|
135
134
|
add_header("Dimensions mapping", [sg.Text("Image shape : {0}".format(shape))])
|
|
@@ -139,12 +138,12 @@ def _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map
|
|
|
139
138
|
if multichannel : layout += [parameters_layout(['c'], default_values=[c])]
|
|
140
139
|
if is_time_stack : layout += [parameters_layout(['t'], default_values=[t])]
|
|
141
140
|
|
|
142
|
-
event,
|
|
141
|
+
event, preset_map = prompt_with_help(layout,help= 'mapping', add_scrollbar=False)
|
|
143
142
|
if event == 'Cancel' : quit()
|
|
144
143
|
|
|
145
144
|
#Check integrity
|
|
146
|
-
channels_values = np.array(list(
|
|
147
|
-
total_channels = len(
|
|
145
|
+
channels_values = np.array(list(preset_map.values()), dtype= int)
|
|
146
|
+
total_channels = len(preset_map)
|
|
148
147
|
unique_channel = len(np.unique(channels_values))
|
|
149
148
|
if total_channels != unique_channel :
|
|
150
149
|
sg.popup("{0} channel(s) are not uniquely mapped.".format(total_channels - unique_channel))
|
|
@@ -154,7 +153,7 @@ def _ask_channel_map(shape, is_3D_stack, is_time_stack, multichannel, preset_map
|
|
|
154
153
|
relaunch= True
|
|
155
154
|
if not relaunch : break
|
|
156
155
|
|
|
157
|
-
return
|
|
156
|
+
return preset_map
|
|
158
157
|
|
|
159
158
|
def _show_mapping(shape, map, is_3D_stack, is_time_stack, multichannel) :
|
|
160
159
|
layout = [
|
|
@@ -166,7 +165,7 @@ def _show_mapping(shape, map, is_3D_stack, is_time_stack, multichannel) :
|
|
|
166
165
|
[sg.Button('Change mapping')]
|
|
167
166
|
]
|
|
168
167
|
|
|
169
|
-
event, values = prompt_with_help(layout, help='mapping')
|
|
168
|
+
event, values = prompt_with_help(layout, help='mapping', add_scrollbar=False)
|
|
170
169
|
|
|
171
170
|
if event == 'Ok' :
|
|
172
171
|
return map
|
|
@@ -170,7 +170,7 @@ def launch_segmentation(image: np.ndarray, user_parameters: dict) :
|
|
|
170
170
|
[sg.Button("Yes"), sg.Button("No")]
|
|
171
171
|
]
|
|
172
172
|
|
|
173
|
-
event, values = prompt(layout=layout, add_ok_cancel=False)
|
|
173
|
+
event, values = prompt(layout=layout, add_ok_cancel=False, add_scrollbar=False)
|
|
174
174
|
if event == "No" :
|
|
175
175
|
continue
|
|
176
176
|
|
|
@@ -8,6 +8,7 @@ from ._signaltonoise import compute_snr_spots
|
|
|
8
8
|
from ._napari_wrapper import correct_spots, _update_clusters, threshold_selection
|
|
9
9
|
from ..gui import add_default_loading
|
|
10
10
|
from ..gui import detection_parameters_promt, input_image_prompt
|
|
11
|
+
from ..utils import compute_anisotropy_coef
|
|
11
12
|
from .spots import compute_Spots
|
|
12
13
|
from magicgui import magicgui
|
|
13
14
|
from napari.layers import Image, Points
|
|
@@ -766,9 +767,11 @@ def _create_threshold_slider(
|
|
|
766
767
|
threshold=threshold
|
|
767
768
|
)[0]
|
|
768
769
|
|
|
770
|
+
scale = compute_anisotropy_coef(voxel_size)
|
|
771
|
+
|
|
769
772
|
layer_args = {
|
|
770
773
|
'size': 5,
|
|
771
|
-
'scale' :
|
|
774
|
+
'scale' : scale,
|
|
772
775
|
'face_color' : 'transparent',
|
|
773
776
|
'edge_color' : 'blue',
|
|
774
777
|
'symbol' : 'ring',
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import PySimpleGUI as sg
|
|
2
|
-
import os
|
|
3
|
-
from ..utils import check_parameter
|
|
4
|
-
import cellpose.models as models
|
|
5
|
-
from cellpose.core import use_gpu
|
|
6
|
-
|
|
7
|
-
sg.theme('DarkAmber')
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def add_header(header_text, layout) :
|
|
11
|
-
header = [[sg.Text('\n{0}'.format(header_text), size= (len(header_text),3), font= 'bold 15')]]
|
|
12
|
-
return header + layout
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def pad_right(string, length, pad_char) :
|
|
16
|
-
if len(string) >= length : return string
|
|
17
|
-
else : return string + pad_char* (length - len(string))
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def parameters_layout(parameters:'list[str]' = [], unit=None, header= None, default_values=None, size=5, opt=None) :
|
|
21
|
-
|
|
22
|
-
if len(parameters) == 0 : return []
|
|
23
|
-
check_parameter(parameters= list, header = (str, type(None)))
|
|
24
|
-
for key in parameters : check_parameter(key = str)
|
|
25
|
-
max_length = len(max(parameters, key=len))
|
|
26
|
-
|
|
27
|
-
if type(opt) == type(None) :
|
|
28
|
-
opt = [False] * len(parameters)
|
|
29
|
-
else :
|
|
30
|
-
if len(opt) != len(parameters) : raise ValueError("Parameters and opt must be of same length.")
|
|
31
|
-
|
|
32
|
-
if isinstance(default_values, (list, tuple)) :
|
|
33
|
-
if len(default_values) != len(parameters) : raise ValueError("if default values specified it must be of equal length as parameters.")
|
|
34
|
-
layout= [
|
|
35
|
-
[sg.Text("{0}".format(pad_right(parameter, max_length, ' ')), text_color= 'green' if option else None),
|
|
36
|
-
sg.InputText(size= size, key= parameter, default_text= value)
|
|
37
|
-
|
|
38
|
-
] for parameter,value, option in zip(parameters,default_values, opt)
|
|
39
|
-
]
|
|
40
|
-
else :
|
|
41
|
-
layout= [
|
|
42
|
-
[sg.Text("{0}".format(pad_right(parameter, max_length, ' ')), text_color= 'green' if option else None),
|
|
43
|
-
sg.InputText(size= size, key= parameter)
|
|
44
|
-
|
|
45
|
-
] for parameter, option in zip(parameters, opt)
|
|
46
|
-
]
|
|
47
|
-
|
|
48
|
-
if type(unit) == str :
|
|
49
|
-
for line_id, line in enumerate(layout) :
|
|
50
|
-
layout[line_id] += [sg.Text('{0}'.format(unit))]
|
|
51
|
-
|
|
52
|
-
if isinstance(header, str) :
|
|
53
|
-
layout = add_header(header, layout)
|
|
54
|
-
return layout
|
|
55
|
-
|
|
56
|
-
def tuple_layout(opt=None, default_dict={}, unit:dict={}, **tuples) :
|
|
57
|
-
"""
|
|
58
|
-
tuples example : voxel_size = ['z','y','x']; will ask a tuple with 3 element default to 'z', 'y' 'x'.
|
|
59
|
-
"""
|
|
60
|
-
if type(tuples) == type(None) : return []
|
|
61
|
-
if len(tuples.keys()) == 0 : return []
|
|
62
|
-
|
|
63
|
-
if type(opt) != type(None) :
|
|
64
|
-
if not isinstance(opt, dict) : raise TypeError("opt parameter should be either None or dict type.")
|
|
65
|
-
if not opt.keys() == tuples.keys() : raise ValueError("If opt is passed it is expected to have same keys as tuples dict.")
|
|
66
|
-
else :
|
|
67
|
-
opt = tuples.copy()
|
|
68
|
-
for key in opt.keys() :
|
|
69
|
-
opt[key] = False
|
|
70
|
-
|
|
71
|
-
for tup in tuples :
|
|
72
|
-
if not isinstance(tuples[tup], (list,tuple)) : raise TypeError()
|
|
73
|
-
|
|
74
|
-
max_size = len(max(tuples.keys(), key=len))
|
|
75
|
-
|
|
76
|
-
layout = [
|
|
77
|
-
[sg.Text(pad_right(tup, max_size, ' '), text_color= 'green' if opt[option] else None)]
|
|
78
|
-
+ [sg.InputText(default_text=default_dict.setdefault('{0}_{1}'.format(tup,elmnt), elmnt),key= '{0}_{1}'.format(tup, elmnt), size= 5) for elmnt in tuples[tup]]
|
|
79
|
-
+ [sg.Text(unit.setdefault(tup,''))]
|
|
80
|
-
for tup,option, in zip(tuples,opt)
|
|
81
|
-
]
|
|
82
|
-
|
|
83
|
-
return layout
|
|
84
|
-
|
|
85
|
-
def path_layout(keys= [],look_for_dir = False, header=None, preset=os.getcwd()) :
|
|
86
|
-
"""
|
|
87
|
-
If not look for dir then looks for file.
|
|
88
|
-
"""
|
|
89
|
-
if len(keys) == 0 : return []
|
|
90
|
-
check_parameter(keys= list, header = (str, type(None)))
|
|
91
|
-
for key in keys : check_parameter(key = str)
|
|
92
|
-
if look_for_dir : Browse = sg.FolderBrowse
|
|
93
|
-
else : Browse = sg.FileBrowse
|
|
94
|
-
|
|
95
|
-
max_length = len(max(keys, key=len))
|
|
96
|
-
layout = [
|
|
97
|
-
[sg.Text(pad_right(name, max_length, ' ')), Browse(key= name, initial_folder= preset)] for name in keys
|
|
98
|
-
]
|
|
99
|
-
if isinstance(header, str) :
|
|
100
|
-
layout = add_header(header, layout=layout)
|
|
101
|
-
return layout
|
|
102
|
-
|
|
103
|
-
def bool_layout(parameters= [], header=None, preset=None) :
|
|
104
|
-
if len(parameters) == 0 : return []
|
|
105
|
-
check_parameter(parameters= list, header= (str, type(None)), preset=(type(None), list, tuple, bool))
|
|
106
|
-
for key in parameters : check_parameter(key = str)
|
|
107
|
-
if type(preset) == type(None) :
|
|
108
|
-
preset = [False] * len(parameters)
|
|
109
|
-
elif type(preset) == bool :
|
|
110
|
-
preset = [preset] * len(parameters)
|
|
111
|
-
else :
|
|
112
|
-
for key in preset : check_parameter(key = bool)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
max_length = len(max(parameters, key=len))
|
|
117
|
-
layout = [
|
|
118
|
-
[sg.Checkbox(pad_right(name, max_length, ' '), key= name, default=box_preset)] for name, box_preset in zip(parameters,preset)
|
|
119
|
-
]
|
|
120
|
-
if isinstance(header, str) :
|
|
121
|
-
layout = add_header(header, layout=layout)
|
|
122
|
-
return layout
|
|
123
|
-
|
|
124
|
-
def combo_layout(values, key, header=None, read_only=True, default_value=None) :
|
|
125
|
-
"""
|
|
126
|
-
drop-down list
|
|
127
|
-
"""
|
|
128
|
-
if len(values) == 0 : return []
|
|
129
|
-
check_parameter(values= list, header= (str, type(None)))
|
|
130
|
-
if type(default_value) == type(None) :
|
|
131
|
-
default_value = values[0]
|
|
132
|
-
elif default_value not in values :
|
|
133
|
-
default_value = values[0]
|
|
134
|
-
layout = [
|
|
135
|
-
sg.Combo(values, default_value=default_value, readonly=read_only, key=key)
|
|
136
|
-
]
|
|
137
|
-
if isinstance(header, str) :
|
|
138
|
-
layout = add_header(header, layout=layout)
|
|
139
|
-
return layout
|
|
140
|
-
|
|
141
|
-
def radio_layout(values, header=None) :
|
|
142
|
-
"""
|
|
143
|
-
Single choice buttons.
|
|
144
|
-
"""
|
|
145
|
-
if len(values) == 0 : return []
|
|
146
|
-
check_parameter(values= list, header= (str, type(None)))
|
|
147
|
-
layout = [
|
|
148
|
-
[sg.Radio(value, group_id= 0) for value in values]
|
|
149
|
-
]
|
|
150
|
-
if isinstance(header, str) :
|
|
151
|
-
layout = add_header(header, layout=layout)
|
|
152
|
-
return layout
|
|
153
|
-
|
|
154
|
-
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
|
-
|
|
156
|
-
USE_GPU = use_gpu()
|
|
157
|
-
|
|
158
|
-
models_list = models.get_user_models() + models.MODEL_NAMES
|
|
159
|
-
if len(models_list) == 0 : models_list = ['no model found']
|
|
160
|
-
|
|
161
|
-
#Header : GPU availabality
|
|
162
|
-
layout = [[sg.Text("GPU is currently "), sg.Text('ON', text_color= 'green') if USE_GPU else sg.Text('OFF', text_color= 'red')]]
|
|
163
|
-
|
|
164
|
-
#cytoplasm parameters
|
|
165
|
-
layout += [add_header("Cell Segmentation", [sg.Text("Choose cellpose model for cytoplasm: \n")]),
|
|
166
|
-
[combo_layout(models_list, key='cyto_model_name', default_value= cytoplasm_model_preset)]
|
|
167
|
-
]
|
|
168
|
-
if multichannel : layout += [parameters_layout(['cytoplasm channel'],default_values= [cytoplasm_channel_preset])]
|
|
169
|
-
layout += [parameters_layout(['cytoplasm diameter'], unit= "px", default_values= [cyto_diameter_preset])]
|
|
170
|
-
#Nucleus parameters
|
|
171
|
-
layout += [
|
|
172
|
-
add_header("Nucleus segmentation",[sg.Text("Choose cellpose model for nucleus: \n")]),
|
|
173
|
-
combo_layout(models_list, key='nucleus_model_name', default_value= nucleus_model_preset)
|
|
174
|
-
]
|
|
175
|
-
if multichannel : layout += [parameters_layout(['nucleus channel'], default_values= [nucleus_channel_preset])]
|
|
176
|
-
layout += [parameters_layout([ 'nucleus diameter'],unit= "px", default_values= [nucleus_diameter_preset])]
|
|
177
|
-
layout += [bool_layout(["Segment only nuclei"], preset=segment_only_nuclei_preset)]
|
|
178
|
-
|
|
179
|
-
#Control plots
|
|
180
|
-
layout += [bool_layout(['show segmentation'], header= 'Segmentation plots', preset= show_segmentation_preset)]
|
|
181
|
-
layout += [path_layout(['saving path'], look_for_dir=True, preset=saving_path_preset)]
|
|
182
|
-
layout += [parameters_layout(['filename'], default_values=[filename_preset], size= 25)]
|
|
183
|
-
|
|
184
|
-
return layout
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/gui/general_help_screenshot.png
RENAMED
|
File without changes
|
|
File without changes
|
{small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/gui/mapping_help_screenshot.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/napari_detection_example.png
RENAMED
|
File without changes
|
{small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/pipeline/_colocalisation.py
RENAMED
|
File without changes
|
|
File without changes
|
{small_fish_gui-1.3.4 → small_fish_gui-1.3.5}/src/small_fish_gui/pipeline/_napari_wrapper.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|