biomedisa 2024.5.14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- biomedisa/__init__.py +53 -0
- biomedisa/__main__.py +18 -0
- biomedisa/biomedisa_features/DataGenerator.py +299 -0
- biomedisa/biomedisa_features/DataGeneratorCrop.py +121 -0
- biomedisa/biomedisa_features/PredictDataGenerator.py +87 -0
- biomedisa/biomedisa_features/PredictDataGeneratorCrop.py +74 -0
- biomedisa/biomedisa_features/__init__.py +0 -0
- biomedisa/biomedisa_features/active_contour.py +434 -0
- biomedisa/biomedisa_features/amira_to_np/__init__.py +0 -0
- biomedisa/biomedisa_features/amira_to_np/amira_data_stream.py +980 -0
- biomedisa/biomedisa_features/amira_to_np/amira_grammar.py +369 -0
- biomedisa/biomedisa_features/amira_to_np/amira_header.py +290 -0
- biomedisa/biomedisa_features/amira_to_np/amira_helper.py +72 -0
- biomedisa/biomedisa_features/assd.py +167 -0
- biomedisa/biomedisa_features/biomedisa_helper.py +801 -0
- biomedisa/biomedisa_features/create_slices.py +286 -0
- biomedisa/biomedisa_features/crop_helper.py +586 -0
- biomedisa/biomedisa_features/curvop_numba.py +149 -0
- biomedisa/biomedisa_features/django_env.py +172 -0
- biomedisa/biomedisa_features/keras_helper.py +1219 -0
- biomedisa/biomedisa_features/nc_reader.py +179 -0
- biomedisa/biomedisa_features/pid.py +52 -0
- biomedisa/biomedisa_features/process_image.py +253 -0
- biomedisa/biomedisa_features/pycuda_test.py +84 -0
- biomedisa/biomedisa_features/random_walk/__init__.py +0 -0
- biomedisa/biomedisa_features/random_walk/gpu_kernels.py +183 -0
- biomedisa/biomedisa_features/random_walk/pycuda_large.py +826 -0
- biomedisa/biomedisa_features/random_walk/pycuda_large_allx.py +806 -0
- biomedisa/biomedisa_features/random_walk/pycuda_small.py +414 -0
- biomedisa/biomedisa_features/random_walk/pycuda_small_allx.py +493 -0
- biomedisa/biomedisa_features/random_walk/pyopencl_large.py +760 -0
- biomedisa/biomedisa_features/random_walk/pyopencl_small.py +441 -0
- biomedisa/biomedisa_features/random_walk/rw_large.py +390 -0
- biomedisa/biomedisa_features/random_walk/rw_small.py +310 -0
- biomedisa/biomedisa_features/remove_outlier.py +399 -0
- biomedisa/biomedisa_features/split_volume.py +274 -0
- biomedisa/deeplearning.py +519 -0
- biomedisa/interpolation.py +371 -0
- biomedisa/mesh.py +406 -0
- biomedisa-2024.5.14.dist-info/LICENSE +191 -0
- biomedisa-2024.5.14.dist-info/METADATA +306 -0
- biomedisa-2024.5.14.dist-info/RECORD +44 -0
- biomedisa-2024.5.14.dist-info/WHEEL +5 -0
- biomedisa-2024.5.14.dist-info/top_level.txt +1 -0
@@ -0,0 +1,399 @@
|
|
1
|
+
#!/usr/bin/python3
|
2
|
+
##########################################################################
|
3
|
+
## ##
|
4
|
+
## Copyright (c) 2024 Philipp Lösel. All rights reserved. ##
|
5
|
+
## ##
|
6
|
+
## This file is part of the open source project biomedisa. ##
|
7
|
+
## ##
|
8
|
+
## Licensed under the European Union Public Licence (EUPL) ##
|
9
|
+
## v1.2, or - as soon as they will be approved by the ##
|
10
|
+
## European Commission - subsequent versions of the EUPL; ##
|
11
|
+
## ##
|
12
|
+
## You may redistribute it and/or modify it under the terms ##
|
13
|
+
## of the EUPL v1.2. You may not use this work except in ##
|
14
|
+
## compliance with this Licence. ##
|
15
|
+
## ##
|
16
|
+
## You can obtain a copy of the Licence at: ##
|
17
|
+
## ##
|
18
|
+
## https://joinup.ec.europa.eu/page/eupl-text-11-12 ##
|
19
|
+
## ##
|
20
|
+
## Unless required by applicable law or agreed to in ##
|
21
|
+
## writing, software distributed under the Licence is ##
|
22
|
+
## distributed on an "AS IS" basis, WITHOUT WARRANTIES ##
|
23
|
+
## OR CONDITIONS OF ANY KIND, either express or implied. ##
|
24
|
+
## ##
|
25
|
+
## See the Licence for the specific language governing ##
|
26
|
+
## permissions and limitations under the Licence. ##
|
27
|
+
## ##
|
28
|
+
##########################################################################
|
29
|
+
|
30
|
+
import sys, os
|
31
|
+
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
32
|
+
if not BASE_DIR in sys.path:
|
33
|
+
sys.path.append(BASE_DIR)
|
34
|
+
import biomedisa
|
35
|
+
from biomedisa_features.biomedisa_helper import load_data, save_data, silent_remove
|
36
|
+
import numpy as np
|
37
|
+
from scipy import ndimage
|
38
|
+
import argparse
|
39
|
+
import traceback
|
40
|
+
import subprocess
|
41
|
+
|
42
|
+
def reduce_blocksize(data):
|
43
|
+
zsh, ysh, xsh = data.shape
|
44
|
+
argmin_z, argmax_z, argmin_y, argmax_y, argmin_x, argmax_x = zsh, 0, ysh, 0, xsh, 0
|
45
|
+
for k in range(zsh):
|
46
|
+
y, x = np.nonzero(data[k])
|
47
|
+
if x.any():
|
48
|
+
argmin_x = min(argmin_x, np.amin(x))
|
49
|
+
argmax_x = max(argmax_x, np.amax(x))
|
50
|
+
argmin_y = min(argmin_y, np.amin(y))
|
51
|
+
argmax_y = max(argmax_y, np.amax(y))
|
52
|
+
argmin_z = min(argmin_z, k)
|
53
|
+
argmax_z = max(argmax_z, k)
|
54
|
+
argmin_x = max(argmin_x - 1, 0)
|
55
|
+
argmax_x = min(argmax_x + 1, xsh-1) + 1
|
56
|
+
argmin_y = max(argmin_y - 1, 0)
|
57
|
+
argmax_y = min(argmax_y + 1, ysh-1) + 1
|
58
|
+
argmin_z = max(argmin_z - 1, 0)
|
59
|
+
argmax_z = min(argmax_z + 1, zsh-1) + 1
|
60
|
+
data = np.copy(data[argmin_z:argmax_z, argmin_y:argmax_y, argmin_x:argmax_x], order='C')
|
61
|
+
return data, argmin_z, argmax_z, argmin_y, argmax_y, argmin_x, argmax_x
|
62
|
+
|
63
|
+
def clean(image, threshold=0.1):
|
64
|
+
image_i = np.copy(image, order='C')
|
65
|
+
allLabels = np.unique(image_i)
|
66
|
+
mask = np.empty_like(image_i)
|
67
|
+
s = [[[0,0,0], [0,1,0], [0,0,0]], [[0,1,0], [1,1,1], [0,1,0]], [[0,0,0], [0,1,0], [0,0,0]]]
|
68
|
+
for k in allLabels[1:]:
|
69
|
+
|
70
|
+
# get mask
|
71
|
+
label = image_i==k
|
72
|
+
mask.fill(0)
|
73
|
+
mask[label] = 1
|
74
|
+
|
75
|
+
# reduce size
|
76
|
+
reduced, argmin_z, argmax_z, argmin_y, argmax_y, argmin_x, argmax_x = reduce_blocksize(mask)
|
77
|
+
|
78
|
+
# get clusters
|
79
|
+
labeled_array, _ = ndimage.label(reduced, structure=s)
|
80
|
+
size = np.bincount(labeled_array.ravel())
|
81
|
+
|
82
|
+
# get reference size
|
83
|
+
biggest_label = np.argmax(size[1:]) + 1
|
84
|
+
label_size = size[biggest_label]
|
85
|
+
|
86
|
+
# preserve large segments
|
87
|
+
reduced.fill(0)
|
88
|
+
for l, m in enumerate(size[1:]):
|
89
|
+
if m > threshold * label_size:
|
90
|
+
reduced[labeled_array==l+1] = 1
|
91
|
+
|
92
|
+
# get original size
|
93
|
+
mask.fill(0)
|
94
|
+
mask[argmin_z:argmax_z, argmin_y:argmax_y, argmin_x:argmax_x] = reduced
|
95
|
+
|
96
|
+
# write cleaned label to array
|
97
|
+
image_i[label] = 0
|
98
|
+
image_i[mask==1] = k
|
99
|
+
|
100
|
+
return image_i
|
101
|
+
|
102
|
+
def fill(image, threshold=0.9):
|
103
|
+
image_i = np.copy(image, order='C')
|
104
|
+
allLabels = np.unique(image_i)
|
105
|
+
mask = np.empty_like(image_i)
|
106
|
+
s = [[[0,0,0], [0,1,0], [0,0,0]], [[0,1,0], [1,1,1], [0,1,0]], [[0,0,0], [0,1,0], [0,0,0]]]
|
107
|
+
for k in allLabels[1:]:
|
108
|
+
|
109
|
+
# get mask
|
110
|
+
label = image_i==k
|
111
|
+
mask.fill(0)
|
112
|
+
mask[label] = 1
|
113
|
+
|
114
|
+
# reduce size
|
115
|
+
reduced, argmin_z, argmax_z, argmin_y, argmax_y, argmin_x, argmax_x = reduce_blocksize(mask)
|
116
|
+
|
117
|
+
# reference size
|
118
|
+
label_size = np.sum(reduced)
|
119
|
+
|
120
|
+
# invert
|
121
|
+
reduced = 1 - reduced # background and holes of object
|
122
|
+
|
123
|
+
# get clusters
|
124
|
+
labeled_array, _ = ndimage.label(reduced, structure=s)
|
125
|
+
size = np.bincount(labeled_array.ravel())
|
126
|
+
biggest_label = np.argmax(size)
|
127
|
+
|
128
|
+
# get label with all holes filled
|
129
|
+
reduced.fill(1)
|
130
|
+
reduced[labeled_array==biggest_label] = 0
|
131
|
+
|
132
|
+
# preserve large holes
|
133
|
+
for l, m in enumerate(size[1:]):
|
134
|
+
if m > threshold * label_size and l+1 != biggest_label:
|
135
|
+
reduced[labeled_array==l+1] = 0
|
136
|
+
|
137
|
+
# get original size
|
138
|
+
mask.fill(0)
|
139
|
+
mask[argmin_z:argmax_z, argmin_y:argmax_y, argmin_x:argmax_x] = reduced
|
140
|
+
|
141
|
+
# write filled label to array
|
142
|
+
image_i[label] = 0
|
143
|
+
image_i[mask==1] = k
|
144
|
+
|
145
|
+
return image_i
|
146
|
+
|
147
|
+
def main_helper(path_to_labels, img_id=None, friend_id=None, fill_holes=True,
|
148
|
+
clean_threshold=0.1, fill_threshold=0.9, remote=False, no_compression=False):
|
149
|
+
|
150
|
+
# django environment
|
151
|
+
if img_id is not None:
|
152
|
+
django_env = True
|
153
|
+
else:
|
154
|
+
django_env = False
|
155
|
+
|
156
|
+
# compression
|
157
|
+
if no_compression:
|
158
|
+
compression = False
|
159
|
+
else:
|
160
|
+
compression = True
|
161
|
+
|
162
|
+
# final filenames
|
163
|
+
filename, extension = os.path.splitext(path_to_labels)
|
164
|
+
if extension == '.gz':
|
165
|
+
extension = '.nii.gz'
|
166
|
+
filename = filename[:-4]
|
167
|
+
path_to_cleaned = filename + '.cleaned' + extension
|
168
|
+
path_to_filled = filename + '.filled' + extension
|
169
|
+
path_to_cleaned_filled = filename + '.cleaned.filled' + extension
|
170
|
+
|
171
|
+
# load data
|
172
|
+
final, header = load_data(path_to_labels, 'cleanup')
|
173
|
+
|
174
|
+
# process data
|
175
|
+
final_cleaned = clean(final, clean_threshold)
|
176
|
+
if fill_holes:
|
177
|
+
final_filled = fill(final, fill_threshold)
|
178
|
+
final_cleaned_filled = final_cleaned + (final_filled - final)
|
179
|
+
|
180
|
+
# unique_file_paths
|
181
|
+
if django_env and not remote:
|
182
|
+
from biomedisa_app.views import unique_file_path
|
183
|
+
path_to_cleaned = unique_file_path(path_to_cleaned)
|
184
|
+
path_to_filled = unique_file_path(path_to_filled)
|
185
|
+
path_to_cleaned_filled = unique_file_path(path_to_cleaned_filled)
|
186
|
+
|
187
|
+
# save results
|
188
|
+
save_data(path_to_cleaned, final_cleaned, header, extension, compression)
|
189
|
+
if fill_holes:
|
190
|
+
save_data(path_to_filled, final_filled, header, extension, compression)
|
191
|
+
save_data(path_to_cleaned_filled, final_cleaned_filled, header, extension, compression)
|
192
|
+
|
193
|
+
# post processing
|
194
|
+
post_processing(path_to_cleaned, path_to_filled, path_to_cleaned_filled, img_id, friend_id, fill_holes, remote)
|
195
|
+
|
196
|
+
def post_processing(path_to_cleaned, path_to_filled, path_to_cleaned_filled, img_id=None, friend_id=None, fill_holes=False, remote=False):
|
197
|
+
if remote:
|
198
|
+
with open(BASE_DIR + '/log/config_6', 'w') as configfile:
|
199
|
+
print(path_to_cleaned, path_to_filled, path_to_cleaned_filled, file=configfile)
|
200
|
+
else:
|
201
|
+
import django
|
202
|
+
django.setup()
|
203
|
+
from biomedisa_app.models import Upload
|
204
|
+
from biomedisa_features.create_slices import create_slices
|
205
|
+
from redis import Redis
|
206
|
+
from rq import Queue
|
207
|
+
|
208
|
+
# check if reference data still exists
|
209
|
+
image = Upload.objects.filter(pk=img_id)
|
210
|
+
friend = Upload.objects.filter(pk=friend_id)
|
211
|
+
if len(friend)>0:
|
212
|
+
friend = friend[0]
|
213
|
+
|
214
|
+
# save django object
|
215
|
+
shortfilename = os.path.basename(path_to_cleaned)
|
216
|
+
pic_path = 'images/' + friend.user.username + '/' + shortfilename
|
217
|
+
Upload.objects.create(pic=pic_path, user=friend.user, project=friend.project, final=(2 if fill_holes else 6), imageType=3, shortfilename=shortfilename, friend=friend_id)
|
218
|
+
|
219
|
+
# create slices for sliceviewer
|
220
|
+
if len(image)>0:
|
221
|
+
q_slices = Queue('slices', connection=Redis())
|
222
|
+
job = q_slices.enqueue_call(create_slices, args=(image[0].pic.path, path_to_cleaned,), timeout=-1)
|
223
|
+
|
224
|
+
# fill holes
|
225
|
+
if fill_holes:
|
226
|
+
# save django object
|
227
|
+
shortfilename = os.path.basename(path_to_cleaned_filled)
|
228
|
+
pic_path = 'images/' + friend.user.username + '/' + shortfilename
|
229
|
+
Upload.objects.create(pic=pic_path, user=friend.user, project=friend.project, final=8, imageType=3, shortfilename=shortfilename, friend=friend_id)
|
230
|
+
shortfilename = os.path.basename(path_to_filled)
|
231
|
+
pic_path = 'images/' + friend.user.username + '/' + shortfilename
|
232
|
+
Upload.objects.create(pic=pic_path, user=friend.user, project=friend.project, final=7, imageType=3, shortfilename=shortfilename, friend=friend_id)
|
233
|
+
|
234
|
+
# create slices for sliceviewer
|
235
|
+
if len(image)>0:
|
236
|
+
q_slices = Queue('slices', connection=Redis())
|
237
|
+
job = q_slices.enqueue_call(create_slices, args=(image[0].pic.path, path_to_filled,), timeout=-1)
|
238
|
+
job = q_slices.enqueue_call(create_slices, args=(image[0].pic.path, path_to_cleaned_filled,), timeout=-1)
|
239
|
+
else:
|
240
|
+
silent_remove(path_to_cleaned)
|
241
|
+
silent_remove(path_to_filled)
|
242
|
+
silent_remove(path_to_cleaned_filled)
|
243
|
+
|
244
|
+
def init_remove_outlier(image_id, final_id, label_id, fill_holes=True):
|
245
|
+
'''
|
246
|
+
Runs clean() and fill() within django environment/webbrowser version
|
247
|
+
|
248
|
+
Parameters
|
249
|
+
---------
|
250
|
+
image_id: int
|
251
|
+
Django id of image data used for creating slice preview
|
252
|
+
final_id: int
|
253
|
+
Django id of result data to be processed
|
254
|
+
label_id: int
|
255
|
+
Django id of label data used for configuration parameters
|
256
|
+
fill_holes: bool
|
257
|
+
Fill holes and save as an optional result
|
258
|
+
|
259
|
+
Returns
|
260
|
+
-------
|
261
|
+
No returns
|
262
|
+
Fails silently
|
263
|
+
'''
|
264
|
+
|
265
|
+
import django
|
266
|
+
django.setup()
|
267
|
+
from biomedisa_app.models import Upload
|
268
|
+
from biomedisa_app.config import config
|
269
|
+
from biomedisa_app.views import send_data_to_host, qsub_start, qsub_stop, unique_file_path
|
270
|
+
|
271
|
+
# get objects
|
272
|
+
try:
|
273
|
+
image = Upload.objects.get(pk=image_id)
|
274
|
+
final = Upload.objects.get(pk=final_id)
|
275
|
+
label = Upload.objects.get(pk=label_id)
|
276
|
+
success = True
|
277
|
+
except Upload.DoesNotExist:
|
278
|
+
success = False
|
279
|
+
|
280
|
+
# get host information
|
281
|
+
host = ''
|
282
|
+
host_base = BASE_DIR
|
283
|
+
subhost, qsub_pid = None, None
|
284
|
+
if 'REMOTE_QUEUE_HOST' in config:
|
285
|
+
host = config['REMOTE_QUEUE_HOST']
|
286
|
+
if host and 'REMOTE_QUEUE_BASE_DIR' in config:
|
287
|
+
host_base = config['REMOTE_QUEUE_BASE_DIR']
|
288
|
+
|
289
|
+
if success:
|
290
|
+
|
291
|
+
# remote server
|
292
|
+
if host:
|
293
|
+
|
294
|
+
# command
|
295
|
+
cmd = ['python3', host_base+'/biomedisa_features/remove_outlier.py', final.pic.path.replace(BASE_DIR,host_base)]
|
296
|
+
cmd += [f'-iid={image.id}', f'-fid={final.friend}', '-r']
|
297
|
+
|
298
|
+
# command (append only on demand)
|
299
|
+
if fill_holes:
|
300
|
+
cmd += ['-fh']
|
301
|
+
if not label.compression:
|
302
|
+
cmd += ['-nc']
|
303
|
+
if label.delete_outliers != 0.1:
|
304
|
+
cmd += [f'-c={label.delete_outliers}']
|
305
|
+
if label.fill_holes != 0.9:
|
306
|
+
cmd += [f'-f={label.fill_holes}']
|
307
|
+
|
308
|
+
# create user directory
|
309
|
+
subprocess.Popen(['ssh', host, 'mkdir', '-p', host_base+'/private_storage/images/'+image.user.username]).wait()
|
310
|
+
|
311
|
+
# send data to host
|
312
|
+
success = send_data_to_host(final.pic.path, host+':'+final.pic.path.replace(BASE_DIR,host_base))
|
313
|
+
|
314
|
+
if success==0:
|
315
|
+
|
316
|
+
# qsub start
|
317
|
+
if 'REMOTE_QUEUE_QSUB' in config and config['REMOTE_QUEUE_QSUB']:
|
318
|
+
subhost, qsub_pid = qsub_start(host, host_base, 6)
|
319
|
+
|
320
|
+
# start removing outliers
|
321
|
+
if subhost:
|
322
|
+
cmd = ['ssh', '-t', host, 'ssh', subhost] + cmd
|
323
|
+
else:
|
324
|
+
cmd = ['ssh', host] + cmd
|
325
|
+
subprocess.Popen(cmd).wait()
|
326
|
+
|
327
|
+
# config
|
328
|
+
success = subprocess.Popen(['scp', host+':'+host_base+'/log/config_6', BASE_DIR+'/log/config_6']).wait()
|
329
|
+
|
330
|
+
if success==0:
|
331
|
+
with open(BASE_DIR + '/log/config_6', 'r') as configfile:
|
332
|
+
cleaned_on_host, filled_on_host, cleaned_filled_on_host = configfile.read().split()
|
333
|
+
|
334
|
+
# local file names
|
335
|
+
path_to_cleaned = unique_file_path(cleaned_on_host.replace(host_base,BASE_DIR))
|
336
|
+
path_to_filled = unique_file_path(filled_on_host.replace(host_base,BASE_DIR))
|
337
|
+
path_to_cleaned_filled = unique_file_path(cleaned_filled_on_host.replace(host_base,BASE_DIR))
|
338
|
+
|
339
|
+
# get results
|
340
|
+
subprocess.Popen(['scp', host+':'+cleaned_on_host, path_to_cleaned]).wait()
|
341
|
+
if fill_holes:
|
342
|
+
subprocess.Popen(['scp', host+':'+filled_on_host, path_to_filled]).wait()
|
343
|
+
subprocess.Popen(['scp', host+':'+cleaned_filled_on_host, path_to_cleaned_filled]).wait()
|
344
|
+
|
345
|
+
# post processing
|
346
|
+
post_processing(path_to_cleaned, path_to_filled, path_to_cleaned_filled, image_id, final.friend, fill_holes)
|
347
|
+
|
348
|
+
# remove config file
|
349
|
+
subprocess.Popen(['ssh', host, 'rm', host_base + '/log/config_6']).wait()
|
350
|
+
|
351
|
+
# local server
|
352
|
+
else:
|
353
|
+
try:
|
354
|
+
main_helper(final.pic.path, img_id=image_id, friend_id=final.friend,
|
355
|
+
fill_holes=fill_holes, clean_threshold=label.delete_outliers, fill_threshold=label.fill_holes, remote=False,
|
356
|
+
no_compression=(False if label.compression else True))
|
357
|
+
except Exception as e:
|
358
|
+
print(traceback.format_exc())
|
359
|
+
|
360
|
+
# qsub stop
|
361
|
+
if 'REMOTE_QUEUE_QSUB' in config and config['REMOTE_QUEUE_QSUB']:
|
362
|
+
qsub_stop(host, host_base, 6, 'cleanup', subhost, qsub_pid)
|
363
|
+
|
364
|
+
if __name__ == '__main__':
|
365
|
+
|
366
|
+
# initialize arguments
|
367
|
+
parser = argparse.ArgumentParser(description='Biomedisa remove outliers.',
|
368
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
369
|
+
|
370
|
+
# required arguments
|
371
|
+
parser.add_argument('path_to_labels', type=str, metavar='PATH_TO_LABELS',
|
372
|
+
help='Location of label data')
|
373
|
+
|
374
|
+
# optional arguments
|
375
|
+
parser.add_argument('-v', '--version', action='version', version=f'{biomedisa.__version__}',
|
376
|
+
help='Biomedisa version')
|
377
|
+
parser.add_argument('-fh','--fill_holes', action='store_true', default=False,
|
378
|
+
help='Fill holes and save as an optional result')
|
379
|
+
parser.add_argument('-c', '--clean_threshold', type=float, default=0.1,
|
380
|
+
help='Remove outliers, e.g. 0.5 means that objects smaller than 50 percent of the size of the largest object will be removed')
|
381
|
+
parser.add_argument('-f', '--fill_threshold', type=float, default=0.9,
|
382
|
+
help='Fill holes, e.g. 0.5 means that all holes smaller than 50 percent of the entire label will be filled')
|
383
|
+
parser.add_argument('-nc', '--no_compression', action='store_true', default=False,
|
384
|
+
help='Disable compression of segmentation results')
|
385
|
+
parser.add_argument('-iid','--img_id', type=str, default=None,
|
386
|
+
help='Image ID within django environment/browser version')
|
387
|
+
parser.add_argument('-fid','--friend_id', type=str, default=None,
|
388
|
+
help='Label ID within django environment/browser version')
|
389
|
+
parser.add_argument('-r','--remote', action='store_true', default=False,
|
390
|
+
help='Process is carried out on a remote server. Must be set up in config.py')
|
391
|
+
|
392
|
+
kwargs = vars(parser.parse_args())
|
393
|
+
|
394
|
+
# main function
|
395
|
+
try:
|
396
|
+
main_helper(**kwargs)
|
397
|
+
except Exception as e:
|
398
|
+
print(traceback.format_exc())
|
399
|
+
|
@@ -0,0 +1,274 @@
|
|
1
|
+
##########################################################################
|
2
|
+
## ##
|
3
|
+
## Copyright (c) 2024 Philipp Lösel. All rights reserved. ##
|
4
|
+
## ##
|
5
|
+
## This file is part of the open source project biomedisa. ##
|
6
|
+
## ##
|
7
|
+
## Licensed under the European Union Public Licence (EUPL) ##
|
8
|
+
## v1.2, or - as soon as they will be approved by the ##
|
9
|
+
## European Commission - subsequent versions of the EUPL; ##
|
10
|
+
## ##
|
11
|
+
## You may redistribute it and/or modify it under the terms ##
|
12
|
+
## of the EUPL v1.2. You may not use this work except in ##
|
13
|
+
## compliance with this Licence. ##
|
14
|
+
## ##
|
15
|
+
## You can obtain a copy of the Licence at: ##
|
16
|
+
## ##
|
17
|
+
## https://joinup.ec.europa.eu/page/eupl-text-11-12 ##
|
18
|
+
## ##
|
19
|
+
## Unless required by applicable law or agreed to in ##
|
20
|
+
## writing, software distributed under the Licence is ##
|
21
|
+
## distributed on an "AS IS" basis, WITHOUT WARRANTIES ##
|
22
|
+
## OR CONDITIONS OF ANY KIND, either express or implied. ##
|
23
|
+
## ##
|
24
|
+
## See the Licence for the specific language governing ##
|
25
|
+
## permissions and limitations under the Licence. ##
|
26
|
+
## ##
|
27
|
+
##########################################################################
|
28
|
+
|
29
|
+
import sys, os
|
30
|
+
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
31
|
+
sys.path.append(BASE_DIR)
|
32
|
+
from biomedisa_features.biomedisa_helper import load_data, save_data
|
33
|
+
import numpy as np
|
34
|
+
import subprocess
|
35
|
+
import platform
|
36
|
+
import glob
|
37
|
+
|
38
|
+
if __name__ == '__main__':
|
39
|
+
|
40
|
+
# path to data
|
41
|
+
path_to_data = sys.argv[1]
|
42
|
+
path_to_labels = sys.argv[2]
|
43
|
+
|
44
|
+
# get arguments
|
45
|
+
nump = 1
|
46
|
+
smooth = 0
|
47
|
+
overlap = 100
|
48
|
+
sub_z, sub_y, sub_x = 1, 1, 1
|
49
|
+
for i, val in enumerate(sys.argv):
|
50
|
+
if val in ['--split_z','-sz']:
|
51
|
+
sub_z = max(int(sys.argv[i+1]), 1)
|
52
|
+
if val in ['--split_y','-sy']:
|
53
|
+
sub_y = max(int(sys.argv[i+1]), 1)
|
54
|
+
if val in ['--split_x','-sx']:
|
55
|
+
sub_x = max(int(sys.argv[i+1]), 1)
|
56
|
+
if val in ['--overlap','-ol']:
|
57
|
+
overlap = max(int(sys.argv[i+1]), 0)
|
58
|
+
if val in ['-n','-np']:
|
59
|
+
nump = max(int(sys.argv[i+1]), 1)
|
60
|
+
if val in ['--smooth','-s']:
|
61
|
+
smooth = int(sys.argv[i+1])
|
62
|
+
uq = True if any(x in sys.argv for x in ['--uncertainty','-uq']) else False
|
63
|
+
allx = 1 if '-allx' in sys.argv else 0
|
64
|
+
|
65
|
+
# base directory
|
66
|
+
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
67
|
+
|
68
|
+
# clean tmp folder
|
69
|
+
filelist = glob.glob(BASE_DIR+'/tmp/*.tif')
|
70
|
+
for f in filelist:
|
71
|
+
os.remove(f)
|
72
|
+
|
73
|
+
# data shape
|
74
|
+
data, _ = load_data(path_to_data, 'split_volume')
|
75
|
+
shape = np.copy(np.array(data.shape), order='C')
|
76
|
+
zsh, ysh, xsh = shape
|
77
|
+
del data
|
78
|
+
|
79
|
+
# split volume
|
80
|
+
sub_size_z = np.ceil(zsh / sub_z)
|
81
|
+
sub_size_y = np.ceil(ysh / sub_y)
|
82
|
+
sub_size_x = np.ceil(xsh / sub_x)
|
83
|
+
|
84
|
+
# iterate over subvolumes
|
85
|
+
for sub_z_i in range(sub_z):
|
86
|
+
for sub_y_i in range(sub_y):
|
87
|
+
for sub_x_i in range(sub_x):
|
88
|
+
subvolume = sub_z_i*sub_y*sub_x + sub_y_i*sub_x + sub_x_i + 1
|
89
|
+
print('Subvolume:', subvolume, '/', sub_z*sub_y*sub_x)
|
90
|
+
|
91
|
+
# determine z subvolume
|
92
|
+
blockmin_z = int(sub_z_i * sub_size_z)
|
93
|
+
blockmax_z = int((sub_z_i+1) * sub_size_z)
|
94
|
+
datamin_z = max(blockmin_z - overlap, 0)
|
95
|
+
datamax_z = min(blockmax_z + overlap, zsh)
|
96
|
+
|
97
|
+
# determine y subvolume
|
98
|
+
blockmin_y = int(sub_y_i * sub_size_y)
|
99
|
+
blockmax_y = int((sub_y_i+1) * sub_size_y)
|
100
|
+
datamin_y = max(blockmin_y - overlap, 0)
|
101
|
+
datamax_y = min(blockmax_y + overlap, ysh)
|
102
|
+
|
103
|
+
# determine x subvolume
|
104
|
+
blockmin_x = int(sub_x_i * sub_size_x)
|
105
|
+
blockmax_x = int((sub_x_i+1) * sub_size_x)
|
106
|
+
datamin_x = max(blockmin_x - overlap, 0)
|
107
|
+
datamax_x = min(blockmax_x + overlap, xsh)
|
108
|
+
|
109
|
+
# extract image subvolume
|
110
|
+
data, _ = load_data(path_to_data, 'split_volume')
|
111
|
+
save_data(BASE_DIR+f'/tmp/sub_volume_{subvolume}.tif', data[datamin_z:datamax_z,datamin_y:datamax_y,datamin_x:datamax_x], False)
|
112
|
+
del data
|
113
|
+
|
114
|
+
# extract label subvolume
|
115
|
+
labelData, header, final_image_type = load_data(path_to_labels, 'split_volume', True)
|
116
|
+
save_data(BASE_DIR+'/tmp/labels.sub_volume.tif', labelData[datamin_z:datamax_z,datamin_y:datamax_y,datamin_x:datamax_x])
|
117
|
+
del labelData
|
118
|
+
|
119
|
+
# configure command
|
120
|
+
cmd = ['mpiexec', '-np', f'{nump}', 'python3', 'biomedisa_interpolation.py', BASE_DIR+f'/tmp/sub_volume_{subvolume}.tif', BASE_DIR+'/tmp/labels.sub_volume.tif', '-s', f'{smooth}']
|
121
|
+
if uq:
|
122
|
+
cmd.append('-uq')
|
123
|
+
if allx:
|
124
|
+
cmd.append('-allx')
|
125
|
+
cwd = BASE_DIR + '/demo/'
|
126
|
+
|
127
|
+
# run segmentation
|
128
|
+
if platform.system() == 'Windows':
|
129
|
+
cmd[3] = 'python'
|
130
|
+
cmd.insert(4, '-u')
|
131
|
+
p = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE)
|
132
|
+
for line in iter(p.stdout.readline, b''):
|
133
|
+
line = str(line,'utf-8')
|
134
|
+
print(line.rstrip())
|
135
|
+
p.stdout.close()
|
136
|
+
else:
|
137
|
+
p = subprocess.Popen(cmd, cwd=cwd)
|
138
|
+
p.wait()
|
139
|
+
|
140
|
+
# remove tmp files
|
141
|
+
os.remove(BASE_DIR+f'/tmp/sub_volume_{subvolume}.tif')
|
142
|
+
os.remove(BASE_DIR+'/tmp/labels.sub_volume.tif')
|
143
|
+
|
144
|
+
# create path_to_final
|
145
|
+
filename, extension = os.path.splitext(os.path.basename(path_to_data))
|
146
|
+
if extension == '.gz':
|
147
|
+
filename = filename[:-4]
|
148
|
+
filename = 'final.' + filename
|
149
|
+
path_to_final = path_to_data.replace(os.path.basename(path_to_data), filename + final_image_type)
|
150
|
+
|
151
|
+
# path_to_uq and path_to_smooth
|
152
|
+
filename, extension = os.path.splitext(path_to_final)
|
153
|
+
if extension == '.gz':
|
154
|
+
filename = filename[:-4]
|
155
|
+
path_to_smooth = filename + '.smooth' + final_image_type
|
156
|
+
path_to_uq = filename + '.uncertainty.tif'
|
157
|
+
|
158
|
+
# iterate over subvolumes
|
159
|
+
final = np.zeros((zsh, ysh, xsh), dtype=np.uint8)
|
160
|
+
for sub_z_i in range(sub_z):
|
161
|
+
for sub_y_i in range(sub_y):
|
162
|
+
for sub_x_i in range(sub_x):
|
163
|
+
subvolume = sub_z_i*sub_y*sub_x + sub_y_i*sub_x + sub_x_i + 1
|
164
|
+
print('Subvolume:', subvolume, '/', sub_z*sub_y*sub_x)
|
165
|
+
|
166
|
+
# determine z subvolume
|
167
|
+
blockmin_z = int(sub_z_i * sub_size_z)
|
168
|
+
blockmax_z = int((sub_z_i+1) * sub_size_z)
|
169
|
+
datamin_z = max(blockmin_z - overlap, 0)
|
170
|
+
datamax_z = min(blockmax_z + overlap, zsh)
|
171
|
+
|
172
|
+
# determine y subvolume
|
173
|
+
blockmin_y = int(sub_y_i * sub_size_y)
|
174
|
+
blockmax_y = int((sub_y_i+1) * sub_size_y)
|
175
|
+
datamin_y = max(blockmin_y - overlap, 0)
|
176
|
+
datamax_y = min(blockmax_y + overlap, ysh)
|
177
|
+
|
178
|
+
# determine x subvolume
|
179
|
+
blockmin_x = int(sub_x_i * sub_size_x)
|
180
|
+
blockmax_x = int((sub_x_i+1) * sub_size_x)
|
181
|
+
datamin_x = max(blockmin_x - overlap, 0)
|
182
|
+
datamax_x = min(blockmax_x + overlap, xsh)
|
183
|
+
|
184
|
+
# load subvolume
|
185
|
+
path_to_subvolume = BASE_DIR+f'/tmp/final.sub_volume_{subvolume}.tif'
|
186
|
+
if os.path.isfile(path_to_subvolume):
|
187
|
+
tmp, _ = load_data(path_to_subvolume)
|
188
|
+
final[blockmin_z:blockmax_z,blockmin_y:blockmax_y,blockmin_x:blockmax_x] \
|
189
|
+
= tmp[blockmin_z-datamin_z:blockmax_z-datamin_z,blockmin_y-datamin_y:blockmax_y-datamin_y,blockmin_x-datamin_x:blockmax_x-datamin_x]
|
190
|
+
os.remove(path_to_subvolume)
|
191
|
+
|
192
|
+
# save result
|
193
|
+
save_data(path_to_final, final, header)
|
194
|
+
|
195
|
+
# iterate over subvolumes (smooth)
|
196
|
+
smooth = 0
|
197
|
+
final.fill(0)
|
198
|
+
for sub_z_i in range(sub_z):
|
199
|
+
for sub_y_i in range(sub_y):
|
200
|
+
for sub_x_i in range(sub_x):
|
201
|
+
subvolume = sub_z_i*sub_y*sub_x + sub_y_i*sub_x + sub_x_i + 1
|
202
|
+
print('Subvolume:', subvolume, '/', sub_z*sub_y*sub_x)
|
203
|
+
|
204
|
+
# determine z subvolume
|
205
|
+
blockmin_z = int(sub_z_i * sub_size_z)
|
206
|
+
blockmax_z = int((sub_z_i+1) * sub_size_z)
|
207
|
+
datamin_z = max(blockmin_z - overlap, 0)
|
208
|
+
datamax_z = min(blockmax_z + overlap, zsh)
|
209
|
+
|
210
|
+
# determine y subvolume
|
211
|
+
blockmin_y = int(sub_y_i * sub_size_y)
|
212
|
+
blockmax_y = int((sub_y_i+1) * sub_size_y)
|
213
|
+
datamin_y = max(blockmin_y - overlap, 0)
|
214
|
+
datamax_y = min(blockmax_y + overlap, ysh)
|
215
|
+
|
216
|
+
# determine x subvolume
|
217
|
+
blockmin_x = int(sub_x_i * sub_size_x)
|
218
|
+
blockmax_x = int((sub_x_i+1) * sub_size_x)
|
219
|
+
datamin_x = max(blockmin_x - overlap, 0)
|
220
|
+
datamax_x = min(blockmax_x + overlap, xsh)
|
221
|
+
|
222
|
+
# load subvolume
|
223
|
+
path_to_subvolume = BASE_DIR+f'/tmp/final.sub_volume_{subvolume}.smooth.tif'
|
224
|
+
if os.path.isfile(path_to_subvolume):
|
225
|
+
tmp, _ = load_data(path_to_subvolume)
|
226
|
+
final[blockmin_z:blockmax_z,blockmin_y:blockmax_y,blockmin_x:blockmax_x] \
|
227
|
+
= tmp[blockmin_z-datamin_z:blockmax_z-datamin_z,blockmin_y-datamin_y:blockmax_y-datamin_y,blockmin_x-datamin_x:blockmax_x-datamin_x]
|
228
|
+
os.remove(path_to_subvolume)
|
229
|
+
smooth = 1
|
230
|
+
|
231
|
+
# save result
|
232
|
+
if smooth:
|
233
|
+
save_data(path_to_smooth, final, header)
|
234
|
+
|
235
|
+
# iterate over subvolumes (uncertainty)
|
236
|
+
uncertainty = 0
|
237
|
+
final.fill(0)
|
238
|
+
for sub_z_i in range(sub_z):
|
239
|
+
for sub_y_i in range(sub_y):
|
240
|
+
for sub_x_i in range(sub_x):
|
241
|
+
subvolume = sub_z_i*sub_y*sub_x + sub_y_i*sub_x + sub_x_i + 1
|
242
|
+
print('Subvolume:', subvolume, '/', sub_z*sub_y*sub_x)
|
243
|
+
|
244
|
+
# determine z subvolume
|
245
|
+
blockmin_z = int(sub_z_i * sub_size_z)
|
246
|
+
blockmax_z = int((sub_z_i+1) * sub_size_z)
|
247
|
+
datamin_z = max(blockmin_z - overlap, 0)
|
248
|
+
datamax_z = min(blockmax_z + overlap, zsh)
|
249
|
+
|
250
|
+
# determine y subvolume
|
251
|
+
blockmin_y = int(sub_y_i * sub_size_y)
|
252
|
+
blockmax_y = int((sub_y_i+1) * sub_size_y)
|
253
|
+
datamin_y = max(blockmin_y - overlap, 0)
|
254
|
+
datamax_y = min(blockmax_y + overlap, ysh)
|
255
|
+
|
256
|
+
# determine x subvolume
|
257
|
+
blockmin_x = int(sub_x_i * sub_size_x)
|
258
|
+
blockmax_x = int((sub_x_i+1) * sub_size_x)
|
259
|
+
datamin_x = max(blockmin_x - overlap, 0)
|
260
|
+
datamax_x = min(blockmax_x + overlap, xsh)
|
261
|
+
|
262
|
+
# load subvolume
|
263
|
+
path_to_subvolume = BASE_DIR+f'/tmp/final.sub_volume_{subvolume}.uncertainty.tif'
|
264
|
+
if os.path.isfile(path_to_subvolume):
|
265
|
+
tmp, _ = load_data(path_to_subvolume)
|
266
|
+
final[blockmin_z:blockmax_z,blockmin_y:blockmax_y,blockmin_x:blockmax_x] \
|
267
|
+
= tmp[blockmin_z-datamin_z:blockmax_z-datamin_z,blockmin_y-datamin_y:blockmax_y-datamin_y,blockmin_x-datamin_x:blockmax_x-datamin_x]
|
268
|
+
os.remove(path_to_subvolume)
|
269
|
+
uncertainty = 1
|
270
|
+
|
271
|
+
# save result
|
272
|
+
if uncertainty:
|
273
|
+
save_data(path_to_uq, final, header)
|
274
|
+
|