biomedisa 24.5.23__py3-none-any.whl → 24.7.1__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/deeplearning.py CHANGED
@@ -65,11 +65,11 @@ def deep_learning(img_data, label_data=None, val_img_data=None, val_label_data=N
65
65
  path_to_images=None, path_to_labels=None, val_images=None, val_labels=None,
66
66
  path_to_model=None, predict=False, train=False, header_file=None,
67
67
  balance=False, crop_data=False, flip_x=False, flip_y=False, flip_z=False,
68
- swapaxes=False, train_dice=False, val_dice=True, no_compression=False, ignore='none', only='all',
68
+ swapaxes=False, train_dice=False, val_dice=True, compression=True, ignore='none', only='all',
69
69
  network_filters='32-64-128-256-512', resnet=False, debug_cropping=False,
70
- save_cropped=False, epochs=100, no_normalization=False, rotate=0.0, validation_split=0.0,
70
+ save_cropped=False, epochs=100, normalization=True, rotate=0.0, validation_split=0.0,
71
71
  learning_rate=0.01, stride_size=32, validation_stride_size=32, validation_freq=1,
72
- batch_size=None, x_scale=256, y_scale=256, z_scale=256, no_scaling=False, early_stopping=0,
72
+ batch_size=None, x_scale=256, y_scale=256, z_scale=256, scaling=True, early_stopping=0,
73
73
  pretrained_model=None, fine_tune=False, workers=1, cropping_epochs=50,
74
74
  x_range=None, y_range=None, z_range=None, header=None, extension='.tif',
75
75
  img_header=None, img_extension='.tif', average_dice=False, django_env=False,
@@ -91,17 +91,13 @@ def deep_learning(img_data, label_data=None, val_img_data=None, val_label_data=N
91
91
  for arg in key_copy:
92
92
  bm.__dict__[arg] = locals()[arg]
93
93
 
94
- # compression
95
- if bm.no_compression:
96
- bm.compression = False
97
- else:
98
- bm.compression = True
99
-
100
94
  # normalization
101
- if bm.no_normalization:
95
+ bm.normalize = 1 if bm.normalization else 0
96
+
97
+ # use patch normalization instead of normalizing the entire volume
98
+ if not bm.scaling:
102
99
  bm.normalize = 0
103
- else:
104
- bm.normalize = 1
100
+ bm.patch_normalization = True
105
101
 
106
102
  # django environment
107
103
  if bm.django_env:
@@ -217,14 +213,19 @@ def deep_learning(img_data, label_data=None, val_img_data=None, val_label_data=N
217
213
  hf = h5py.File(bm.path_to_model, 'r')
218
214
  meta = hf.get('meta')
219
215
  configuration = meta.get('configuration')
220
- channels, bm.x_scale, bm.y_scale, bm.z_scale, normalize, mu, sig = np.array(configuration)[:]
221
- channels, bm.x_scale, bm.y_scale, bm.z_scale, normalize, mu, sig = int(channels), int(bm.x_scale), \
222
- int(bm.y_scale), int(bm.z_scale), int(normalize), float(mu), float(sig)
223
- if '/meta/normalization' in hf:
224
- normalization_parameters = np.array(meta.get('normalization'), dtype=float)
216
+ channels, bm.x_scale, bm.y_scale, bm.z_scale, bm.normalize, mu, sig = np.array(configuration)[:]
217
+ channels, bm.x_scale, bm.y_scale, bm.z_scale, bm.normalize, mu, sig = int(channels), int(bm.x_scale), \
218
+ int(bm.y_scale), int(bm.z_scale), int(bm.normalize), float(mu), float(sig)
219
+ if 'normalization' in meta:
220
+ normalization_parameters = np.array(meta['normalization'], dtype=float)
225
221
  else:
226
222
  normalization_parameters = np.array([[mu],[sig]])
227
223
  allLabels = np.array(meta.get('labels'))
224
+ if 'patch_normalization' in meta:
225
+ bm.patch_normalization = bool(meta['patch_normalization'][()])
226
+ if 'scaling' in meta:
227
+ bm.scaling = bool(meta['scaling'][()])
228
+
228
229
  # check if amira header is available in the network
229
230
  if header is None and meta.get('header') is not None:
230
231
  header = [np.array(meta.get('header'))]
@@ -290,16 +291,11 @@ def deep_learning(img_data, label_data=None, val_img_data=None, val_label_data=N
290
291
  region_of_interest, cropped_volume = ch.crop_data(bm.path_to_image, bm.path_to_model, bm.path_to_cropped_image,
291
292
  bm.batch_size, bm.debug_cropping, bm.save_cropped, img_data, bm.x_range, bm.y_range, bm.z_range)
292
293
 
293
- # load prediction data
294
- img, img_header, z_shape, y_shape, x_shape, region_of_interest, img_data = load_prediction_data(bm.path_to_image,
295
- channels, bm.x_scale, bm.y_scale, bm.z_scale, bm.no_scaling, normalize, normalization_parameters,
296
- region_of_interest, img_data, img_header)
297
-
298
294
  # make prediction
299
- results, bm = predict_semantic_segmentation(bm, img, bm.path_to_model,
300
- bm.z_patch, bm.y_patch, bm.x_patch, z_shape, y_shape, x_shape, bm.compression, header,
301
- img_header, bm.stride_size, allLabels, bm.batch_size, region_of_interest,
302
- bm.no_scaling, extension, img_data)
295
+ results, bm = predict_semantic_segmentation(bm,
296
+ header, img_header, allLabels,
297
+ region_of_interest, extension, img_data,
298
+ channels, normalization_parameters)
303
299
 
304
300
  # results
305
301
  if cropped_volume is not None:
@@ -403,7 +399,7 @@ if __name__ == '__main__':
403
399
  help='Dice loss function')
404
400
  parser.add_argument('-ad','--average_dice', action='store_true', default=False,
405
401
  help='Use averaged dice score of each label')
406
- parser.add_argument('-nc', '--no_compression', action='store_true', default=False,
402
+ parser.add_argument('-nc', '--no-compression', dest='compression', action='store_false',
407
403
  help='Disable compression of segmentation results')
408
404
  parser.add_argument('-i', '--ignore', type=str, default='none',
409
405
  help='Ignore specific label(s), e.g. 2,5,6')
@@ -421,12 +417,12 @@ if __name__ == '__main__':
421
417
  help='Epochs the network is trained')
422
418
  parser.add_argument('-ce','--cropping_epochs', type=int, default=50,
423
419
  help='Epochs the network for auto-cropping is trained')
424
- parser.add_argument('-nn','--no_normalization', action='store_true', default=False,
425
- help='Disable image normalization')
420
+ parser.add_argument('-nn','--no-normalization', dest='normalization', action='store_false',
421
+ help='Disable normalization of 3D image volumes')
426
422
  parser.add_argument('-r','--rotate', type=float, default=0.0,
427
423
  help='Randomly rotate during training')
428
424
  parser.add_argument('-vs','--validation_split', type=float, default=0.0,
429
- help='Percentage of data used for validation')
425
+ help='Percentage of data used for training')
430
426
  parser.add_argument('-lr','--learning_rate', type=float, default=0.01,
431
427
  help='Learning rate')
432
428
  parser.add_argument('-ss','--stride_size', metavar="[1-64]", type=int, choices=range(1,65), default=32,
@@ -447,7 +443,7 @@ if __name__ == '__main__':
447
443
  help='Images and labels are scaled at y-axis to this size before training')
448
444
  parser.add_argument('-zs','--z_scale', type=int, default=256,
449
445
  help='Images and labels are scaled at z-axis to this size before training')
450
- parser.add_argument('-ns','--no_scaling', action='store_true', default=False,
446
+ parser.add_argument('-ns','--no-scaling', dest='scaling', action='store_false',
451
447
  help='Do not resize image and label data')
452
448
  parser.add_argument('-es','--early_stopping', type=int, default=0,
453
449
  help='Training is terminated when the accuracy has not increased in the epochs defined by this')
@@ -484,8 +480,15 @@ if __name__ == '__main__':
484
480
  parser.add_argument('-hf','--header_file', type=str, metavar='PATH', default=None,
485
481
  help='Location of header file')
486
482
  bm = parser.parse_args()
487
-
488
483
  bm.success = True
484
+
485
+ # prediction or training
486
+ if not any([bm.train, bm.predict]):
487
+ bm.predict = False
488
+ bm.train = True
489
+ if os.path.splitext(bm.path)[1] == '.h5':
490
+ bm.predict = True
491
+ bm.train = False
489
492
  if bm.predict:
490
493
  bm.path_to_labels = None
491
494
  bm.path_to_model = bm.path
@@ -286,7 +286,7 @@ class DataGenerator(tf.keras.utils.Sequence):
286
286
 
287
287
  # patch normalization
288
288
  if self.patch_normalization:
289
- tmp_X = np.copy(tmp_X, order='C')
289
+ tmp_X = tmp_X.copy()
290
290
  for c in range(self.n_channels):
291
291
  tmp_X[:,:,:,c] -= np.mean(tmp_X[:,:,:,c])
292
292
  tmp_X[:,:,:,c] /= max(np.std(tmp_X[:,:,:,c]), 1e-6)
@@ -106,7 +106,7 @@ def reduce_blocksize(raw, slices):
106
106
  return raw, slices, argmin_z, argmax_z, argmin_y, argmax_y, argmin_x, argmax_x
107
107
 
108
108
  def activeContour(data, labelData, alpha=1.0, smooth=1, steps=3,
109
- path_to_data=None, path_to_labels=None, no_compression=False,
109
+ path_to_data=None, path_to_labels=None, compression=True,
110
110
  ignore='none', only='all', simple=False,
111
111
  img_id=None, friend_id=None, remote=False):
112
112
 
@@ -126,12 +126,6 @@ def activeContour(data, labelData, alpha=1.0, smooth=1, steps=3,
126
126
  else:
127
127
  bm.django_env = False
128
128
 
129
- # compression
130
- if bm.no_compression:
131
- bm.compression = False
132
- else:
133
- bm.compression = True
134
-
135
129
  # disable file saving when called as a function
136
130
  if bm.data is not None:
137
131
  bm.path_to_data = None
@@ -374,8 +368,7 @@ def init_active_contour(image_id, friend_id, label_id, simple=False):
374
368
  else:
375
369
  try:
376
370
  activeContour(None, None, path_to_data=image.pic.path, path_to_labels=friend.pic.path,
377
- alpha=label.ac_alpha, smooth=label.ac_smooth, steps=label.ac_steps,
378
- no_compression=(False if label.compression else True),
371
+ alpha=label.ac_alpha, smooth=label.ac_smooth, steps=label.ac_steps, compression=label.compression,
379
372
  simple=simple, img_id=image_id, friend_id=friend_id, remote=False)
380
373
  except Exception as e:
381
374
  print(traceback.format_exc())
@@ -407,7 +400,7 @@ if __name__ == '__main__':
407
400
  help='Number of smoothing steps')
408
401
  parser.add_argument('-st', '--steps', type=int, default=3,
409
402
  help='Number of iterations')
410
- parser.add_argument('-nc', '--no_compression', action='store_true', default=False,
403
+ parser.add_argument('-nc', '--no-compression', dest='compression', action='store_false',
411
404
  help='Disable compression of segmentation results')
412
405
  parser.add_argument('-i', '--ignore', type=str, default='none',
413
406
  help='Ignore specific label(s), e.g. 2,5,6')
@@ -317,19 +317,23 @@ def load_data(path_to_data, process='None', return_extension=False):
317
317
  data, header = None, None
318
318
  else:
319
319
  try:
320
- # remove unreadable files or directories
321
- for name in files:
322
- if os.path.isfile(name):
320
+ # load data slice by slice
321
+ file_names = []
322
+ img_slices = []
323
+ header = []
324
+ files.sort()
325
+ for file_name in files:
326
+ if os.path.isfile(file_name):
323
327
  try:
324
- img, _ = load(name)
328
+ img, img_header = load(file_name)
329
+ file_names.append(file_name)
330
+ img_slices.append(img)
331
+ header.append(img_header)
325
332
  except:
326
- files.remove(name)
327
- else:
328
- files.remove(name)
329
- files.sort()
333
+ pass
330
334
 
331
335
  # get data size
332
- img, _ = load(files[0])
336
+ img = img_slices[0]
333
337
  if len(img.shape)==3:
334
338
  ysh, xsh, csh = img.shape[0], img.shape[1], img.shape[2]
335
339
  channel = 'last'
@@ -340,11 +344,9 @@ def load_data(path_to_data, process='None', return_extension=False):
340
344
  ysh, xsh = img.shape[0], img.shape[1]
341
345
  csh, channel = 0, None
342
346
 
343
- # load data slice by slice
344
- data = np.empty((len(files), ysh, xsh), dtype=img.dtype)
345
- header, image_data_shape = [], []
346
- for k, file_name in enumerate(files):
347
- img, img_header = load(file_name)
347
+ # create 3D volume
348
+ data = np.empty((len(file_names), ysh, xsh), dtype=img.dtype)
349
+ for k, img in enumerate(img_slices):
348
350
  if csh==3:
349
351
  img = rgb2gray(img, channel)
350
352
  elif csh==1 and channel=='last':
@@ -352,8 +354,7 @@ def load_data(path_to_data, process='None', return_extension=False):
352
354
  elif csh==1 and channel=='first':
353
355
  img = img[0,:,:]
354
356
  data[k] = img
355
- header.append(img_header)
356
- header = [header, files, data.dtype]
357
+ header = [header, file_names, data.dtype]
357
358
  data = np.swapaxes(data, 1, 2)
358
359
  data = np.copy(data, order='C')
359
360
  except Exception as e:
@@ -151,6 +151,7 @@ def create_slices(path_to_data, path_to_label, on_site=False):
151
151
  # increase contrast
152
152
  raw = img_to_uint8(raw)
153
153
  raw = contrast(raw)
154
+ zsh, ysh, xsh = raw.shape
154
155
 
155
156
  # create slices for slice viewer
156
157
  if not os.path.isdir(path_to_slices):
@@ -160,9 +161,9 @@ def create_slices(path_to_data, path_to_label, on_site=False):
160
161
  os.chmod(path_to_slices, 0o770)
161
162
 
162
163
  # save slices
163
- for k in range(raw.shape[0]):
164
+ for k in range(zsh):
164
165
  im = Image.fromarray(raw[k])
165
- im.save(path_to_slices + f'/{k}.png')
166
+ im.save(path_to_slices + '/slice_' + str(k).zfill(len(str(zsh-1))) + '.png')
166
167
 
167
168
  if path_to_label and not os.path.isdir(path_to_label_slices):
168
169
 
@@ -263,7 +264,7 @@ def create_slices(path_to_data, path_to_label, on_site=False):
263
264
 
264
265
  # save slice
265
266
  im = Image.fromarray(out)
266
- im.save(path_to_label_slices + f'/{k}.png')
267
+ im.save(path_to_label_slices + '/slice_' + str(k).zfill(len(str(zsh-1))) + '.png')
267
268
 
268
269
  except Exception as e:
269
270
  print(e)
@@ -45,6 +45,7 @@ from biomedisa.features.biomedisa_helper import (
45
45
  img_resize, load_data, save_data, set_labels_to_zero, id_generator, unique_file_path)
46
46
  from biomedisa.features.remove_outlier import clean, fill
47
47
  from biomedisa.features.active_contour import activeContour
48
+ from tifffile import TiffFile, imread
48
49
  import matplotlib.pyplot as plt
49
50
  import SimpleITK as sitk
50
51
  import tensorflow as tf
@@ -100,7 +101,7 @@ def save_history(history, path_to_model, val_dice, train_dice):
100
101
  # save history dictonary
101
102
  np.save(path_to_model.replace('.h5','.npy'), history)
102
103
 
103
- def predict_blocksize(labelData, x_puffer, y_puffer, z_puffer):
104
+ def predict_blocksize(labelData, x_puffer=25, y_puffer=25, z_puffer=25):
104
105
  zsh, ysh, xsh = labelData.shape
105
106
  argmin_z, argmax_z, argmin_y, argmax_y, argmin_x, argmax_x = zsh, 0, ysh, 0, xsh, 0
106
107
  for k in range(zsh):
@@ -121,7 +122,7 @@ def predict_blocksize(labelData, x_puffer, y_puffer, z_puffer):
121
122
  argmax_z = argmax_z + z_puffer if argmax_z + z_puffer < zsh else zsh
122
123
  return argmin_z,argmax_z,argmin_y,argmax_y,argmin_x,argmax_x
123
124
 
124
- def get_image_dimensions(header, data):
125
+ def set_image_dimensions(header, data):
125
126
 
126
127
  # read header as string
127
128
  b = header.tobytes()
@@ -148,7 +149,7 @@ def get_image_dimensions(header, data):
148
149
  new_header = np.frombuffer(b2, dtype=header.dtype)
149
150
  return new_header
150
151
 
151
- def get_physical_size(header, img_header):
152
+ def set_physical_size(header, img_header):
152
153
 
153
154
  # read img_header as string
154
155
  b = img_header.tobytes()
@@ -354,7 +355,7 @@ def read_img_list(img_list, label_list, temp_img_dir, temp_label_dir):
354
355
  label_names.append(label_name)
355
356
  return img_names, label_names
356
357
 
357
- def load_training_data(normalize, img_list, label_list, channels, x_scale, y_scale, z_scale, no_scaling,
358
+ def load_training_data(normalize, img_list, label_list, channels, x_scale, y_scale, z_scale, scaling,
358
359
  crop_data, labels_to_compute, labels_to_remove, img_in=None, label_in=None,
359
360
  normalization_parameters=None, allLabels=None, header=None, extension='.tif',
360
361
  x_puffer=25, y_puffer=25, z_puffer=25):
@@ -386,7 +387,7 @@ def load_training_data(normalize, img_list, label_list, channels, x_scale, y_sca
386
387
  if crop_data:
387
388
  argmin_z,argmax_z,argmin_y,argmax_y,argmin_x,argmax_x = predict_blocksize(label, x_puffer, y_puffer, z_puffer)
388
389
  label = np.copy(label[argmin_z:argmax_z,argmin_y:argmax_y,argmin_x:argmax_x], order='C')
389
- if not no_scaling:
390
+ if scaling:
390
391
  label = img_resize(label, z_scale, y_scale, x_scale, labels=True)
391
392
 
392
393
  # if header is not single data stream Amira Mesh falling back to Multi-TIFF
@@ -412,7 +413,7 @@ def load_training_data(normalize, img_list, label_list, channels, x_scale, y_sca
412
413
  else:
413
414
  img = img_in
414
415
  img_names = ['img_1']
415
- if label_dim != img.shape:
416
+ if label_dim != img.shape[:3]:
416
417
  InputError.message = f'Dimensions of "{os.path.basename(img_names[0])}" and "{os.path.basename(label_names[0])}" do not match'
417
418
  raise InputError()
418
419
 
@@ -432,7 +433,7 @@ def load_training_data(normalize, img_list, label_list, channels, x_scale, y_sca
432
433
 
433
434
  # scale/resize image data
434
435
  img = img.astype(np.float32)
435
- if not no_scaling:
436
+ if scaling:
436
437
  img = img_resize(img, z_scale, y_scale, x_scale)
437
438
 
438
439
  # normalize image data
@@ -469,7 +470,7 @@ def load_training_data(normalize, img_list, label_list, channels, x_scale, y_sca
469
470
  if crop_data:
470
471
  argmin_z,argmax_z,argmin_y,argmax_y,argmin_x,argmax_x = predict_blocksize(a, x_puffer, y_puffer, z_puffer)
471
472
  a = np.copy(a[argmin_z:argmax_z,argmin_y:argmax_y,argmin_x:argmax_x], order='C')
472
- if not no_scaling:
473
+ if scaling:
473
474
  a = img_resize(a, z_scale, y_scale, x_scale, labels=True)
474
475
  label = np.append(label, a, axis=0)
475
476
 
@@ -481,7 +482,7 @@ def load_training_data(normalize, img_list, label_list, channels, x_scale, y_sca
481
482
  raise InputError()
482
483
  else:
483
484
  a = img_in[k]
484
- if label_dim != a.shape:
485
+ if label_dim != a.shape[:3]:
485
486
  InputError.message = f'Dimensions of "{os.path.basename(img_names[k])}" and "{os.path.basename(label_names[k])}" do not match'
486
487
  raise InputError()
487
488
  if len(a.shape)==3:
@@ -493,7 +494,7 @@ def load_training_data(normalize, img_list, label_list, channels, x_scale, y_sca
493
494
  if crop_data:
494
495
  a = np.copy(a[argmin_z:argmax_z,argmin_y:argmax_y,argmin_x:argmax_x], order='C')
495
496
  a = a.astype(np.float32)
496
- if not no_scaling:
497
+ if scaling:
497
498
  a = img_resize(a, z_scale, y_scale, x_scale)
498
499
  for c in range(channels):
499
500
  a[:,:,:,c] -= np.amin(a[:,:,:,c])
@@ -541,18 +542,17 @@ class CustomCallback(Callback):
541
542
  time_remaining = str(t // 60) + 'min'
542
543
  else:
543
544
  time_remaining = str(t // 3600) + 'h ' + str((t % 3600) // 60) + 'min'
544
- try:
545
- val_accuracy = round(float(logs["val_accuracy"])*100,1)
546
- image.message = 'Progress {}%, {} remaining, {}% accuracy'.format(percentage,time_remaining,val_accuracy)
547
- except KeyError:
548
- image.message = 'Progress {}%, {} remaining'.format(percentage,time_remaining)
545
+ image.message = 'Progress {}%, {} remaining'.format(percentage,time_remaining)
546
+ if 'best_val_dice' in logs:
547
+ best_val_dice = round(float(logs['best_val_dice'])*100,1)
548
+ image.message += f', {best_val_dice}% accuracy'
549
549
  image.save()
550
550
  print("Start epoch {} of training; got log keys: {}".format(epoch, keys))
551
551
 
552
552
  class MetaData(Callback):
553
553
  def __init__(self, path_to_model, configuration_data, allLabels,
554
554
  extension, header, crop_data, cropping_weights, cropping_config,
555
- normalization_parameters, cropping_norm):
555
+ normalization_parameters, cropping_norm, patch_normalization, scaling):
556
556
 
557
557
  self.path_to_model = path_to_model
558
558
  self.configuration_data = configuration_data
@@ -564,6 +564,8 @@ class MetaData(Callback):
564
564
  self.cropping_weights = cropping_weights
565
565
  self.cropping_config = cropping_config
566
566
  self.cropping_norm = cropping_norm
567
+ self.patch_normalization = patch_normalization
568
+ self.scaling = scaling
567
569
 
568
570
  def on_epoch_end(self, epoch, logs={}):
569
571
  hf = h5py.File(self.path_to_model, 'r')
@@ -574,6 +576,8 @@ class MetaData(Callback):
574
576
  group.create_dataset('configuration', data=self.configuration_data)
575
577
  group.create_dataset('normalization', data=self.normalization_parameters)
576
578
  group.create_dataset('labels', data=self.allLabels)
579
+ group.create_dataset('patch_normalization', data=int(self.patch_normalization))
580
+ group.create_dataset('scaling', data=int(self.scaling))
577
581
  if self.extension == '.am':
578
582
  group.create_dataset('extension', data=self.extension)
579
583
  group.create_dataset('header', data=self.header)
@@ -758,8 +762,8 @@ def dice_coef_loss(nb_labels):
758
762
  for index in range(1,nb_labels):
759
763
  dice += dice_coef(y_true[:,:,:,:,index], y_pred[:,:,:,:,index])
760
764
  dice = dice / (nb_labels-1)
761
- loss = -K.log(dice)
762
- #loss = 1 - dice
765
+ #loss = -K.log(dice)
766
+ loss = 1 - dice
763
767
  return loss
764
768
  return loss_fn
765
769
 
@@ -772,7 +776,7 @@ def train_semantic_segmentation(bm,
772
776
 
773
777
  # training data
774
778
  img, label, allLabels, normalization_parameters, header, extension, bm.channels = load_training_data(bm.normalize,
775
- img_list, label_list, None, bm.x_scale, bm.y_scale, bm.z_scale, bm.no_scaling, bm.crop_data,
779
+ img_list, label_list, None, bm.x_scale, bm.y_scale, bm.z_scale, bm.scaling, bm.crop_data,
776
780
  bm.only, bm.ignore, img, label, None, None, header, extension)
777
781
 
778
782
  # configuration data
@@ -784,7 +788,7 @@ def train_semantic_segmentation(bm,
784
788
  # validation data
785
789
  if any(val_img_list) or img_val is not None:
786
790
  img_val, label_val, _, _, _, _, _ = load_training_data(bm.normalize,
787
- val_img_list, val_label_list, bm.channels, bm.x_scale, bm.y_scale, bm.z_scale, bm.no_scaling, bm.crop_data,
791
+ val_img_list, val_label_list, bm.channels, bm.x_scale, bm.y_scale, bm.z_scale, bm.scaling, bm.crop_data,
788
792
  bm.only, bm.ignore, img_val, label_val, normalization_parameters, allLabels)
789
793
 
790
794
  elif bm.validation_split:
@@ -908,7 +912,7 @@ def train_semantic_segmentation(bm,
908
912
  # save meta data
909
913
  meta_data = MetaData(bm.path_to_model, configuration_data, allLabels,
910
914
  extension, header, bm.crop_data, bm.cropping_weights, bm.cropping_config,
911
- normalization_parameters, bm.cropping_norm)
915
+ normalization_parameters, bm.cropping_norm, bm.patch_normalization, bm.scaling)
912
916
 
913
917
  # model checkpoint
914
918
  if img_val is not None:
@@ -946,22 +950,29 @@ def train_semantic_segmentation(bm,
946
950
  if img_val is not None and not bm.val_dice:
947
951
  save_history(history.history, bm.path_to_model, False, bm.train_dice)
948
952
 
949
- def load_prediction_data(path_to_img, channels, x_scale, y_scale, z_scale,
950
- no_scaling, normalize, normalization_parameters, region_of_interest,
951
- img, img_header):
953
+ def load_prediction_data(bm, channels, normalize, normalization_parameters,
954
+ region_of_interest, img, img_header, load_blockwise=False, z=None):
955
+
952
956
  # read image data
953
957
  if img is None:
954
- img, img_header = load_data(path_to_img, 'first_queue')
958
+ if load_blockwise:
959
+ img_header = None
960
+ tif = TiffFile(bm.path_to_image)
961
+ img = imread(bm.path_to_image, key=range(z,min(len(tif.pages),z+bm.z_patch)))
962
+ else:
963
+ img, img_header = load_data(bm.path_to_image, 'first_queue')
955
964
 
956
965
  # verify validity
957
966
  if img is None:
958
- InputError.message = f'Invalid image data: {os.path.basename(path_to_img)}.'
967
+ InputError.message = f'Invalid image data: {os.path.basename(bm.path_to_image)}.'
959
968
  raise InputError()
960
969
 
961
- # preserve original image data
962
- img_data = img.copy()
970
+ # preserve original image data for post-processing
971
+ img_data = None
972
+ if bm.acwe:
973
+ img_data = img.copy()
963
974
 
964
- # handle all images having channels >=1
975
+ # handle all images using number of channels >=1
965
976
  if len(img.shape)==3:
966
977
  z_shape, y_shape, x_shape = img.shape
967
978
  img = img.reshape(z_shape, y_shape, x_shape, 1)
@@ -969,7 +980,7 @@ def load_prediction_data(path_to_img, channels, x_scale, y_scale, z_scale,
969
980
  InputError.message = f'Number of channels must be {channels}.'
970
981
  raise InputError()
971
982
 
972
- # image shape
983
+ # original image shape
973
984
  z_shape, y_shape, x_shape, _ = img.shape
974
985
 
975
986
  # automatic cropping of image to region of interest
@@ -981,8 +992,8 @@ def load_prediction_data(path_to_img, channels, x_scale, y_scale, z_scale,
981
992
 
982
993
  # scale/resize image data
983
994
  img = img.astype(np.float32)
984
- if not no_scaling:
985
- img = img_resize(img, z_scale, y_scale, x_scale)
995
+ if bm.scaling:
996
+ img = img_resize(img, bm.z_scale, bm.y_scale, bm.x_scale)
986
997
 
987
998
  # normalize image data
988
999
  for c in range(channels):
@@ -1000,94 +1011,255 @@ def load_prediction_data(path_to_img, channels, x_scale, y_scale, z_scale,
1000
1011
 
1001
1012
  return img, img_header, z_shape, y_shape, x_shape, region_of_interest, img_data
1002
1013
 
1003
- def predict_semantic_segmentation(bm, img, path_to_model,
1004
- z_patch, y_patch, x_patch, z_shape, y_shape, x_shape, compress, header,
1005
- img_header, stride_size, allLabels, batch_size, region_of_interest,
1006
- no_scaling, extension, img_data):
1014
+ def append_ghost_areas(bm, img):
1015
+ # append ghost areas to make image dimensions divisible by patch size (mirror edge areas)
1016
+ zsh, ysh, xsh, _ = img.shape
1017
+ z_rest = bm.z_patch - (zsh % bm.z_patch)
1018
+ if z_rest == bm.z_patch:
1019
+ z_rest = -zsh
1020
+ else:
1021
+ img = np.append(img, img[-z_rest:][::-1], axis=0)
1022
+ y_rest = bm.y_patch - (ysh % bm.y_patch)
1023
+ if y_rest == bm.y_patch:
1024
+ y_rest = -ysh
1025
+ else:
1026
+ img = np.append(img, img[:,-y_rest:][:,::-1], axis=1)
1027
+ x_rest = bm.x_patch - (xsh % bm.x_patch)
1028
+ if x_rest == bm.x_patch:
1029
+ x_rest = -xsh
1030
+ else:
1031
+ img = np.append(img, img[:,:,-x_rest:][:,:,::-1], axis=2)
1032
+ return img, z_rest, y_rest, x_rest
1007
1033
 
1008
- results = {}
1034
+ def predict_semantic_segmentation(bm,
1035
+ header, img_header, allLabels,
1036
+ region_of_interest, extension, img_data,
1037
+ channels, normalization_parameters):
1009
1038
 
1010
- # img shape
1011
- zsh, ysh, xsh, csh = img.shape
1039
+ # initialize results
1040
+ results = {}
1012
1041
 
1013
1042
  # number of labels
1014
1043
  nb_labels = len(allLabels)
1015
1044
 
1016
- # list of IDs
1017
- list_IDs = []
1045
+ # load model
1046
+ if bm.dice_loss:
1047
+ def loss_fn(y_true, y_pred):
1048
+ dice = 0
1049
+ for index in range(1, nb_labels):
1050
+ dice += dice_coef(y_true[:,:,:,:,index], y_pred[:,:,:,:,index])
1051
+ dice = dice / (nb_labels-1)
1052
+ #loss = -K.log(dice)
1053
+ loss = 1 - dice
1054
+ return loss
1055
+ custom_objects = {'dice_coef_loss': dice_coef_loss,'loss_fn': loss_fn}
1056
+ model = load_model(bm.path_to_model, custom_objects=custom_objects)
1057
+ else:
1058
+ model = load_model(bm.path_to_model)
1059
+
1060
+ # check if data can be loaded blockwise to save host memory
1061
+ load_blockwise = False
1062
+ if not bm.scaling and not bm.normalize and bm.path_to_image and not np.any(region_of_interest) and \
1063
+ os.path.splitext(bm.path_to_image)[1] in ['.tif', '.tiff'] and not bm.acwe:
1064
+ tif = TiffFile(bm.path_to_image)
1065
+ zsh = len(tif.pages)
1066
+ ysh, xsh = tif.pages[0].shape
1067
+
1068
+ # determine new image size after appending ghost areas to make image dimensions divisible by patch size
1069
+ z_rest = bm.z_patch - (zsh % bm.z_patch)
1070
+ if z_rest == bm.z_patch:
1071
+ z_rest = -zsh
1072
+ else:
1073
+ zsh += z_rest
1074
+ y_rest = bm.y_patch - (ysh % bm.y_patch)
1075
+ if y_rest == bm.y_patch:
1076
+ y_rest = -ysh
1077
+ else:
1078
+ ysh += y_rest
1079
+ x_rest = bm.x_patch - (xsh % bm.x_patch)
1080
+ if x_rest == bm.x_patch:
1081
+ x_rest = -xsh
1082
+ else:
1083
+ xsh += x_rest
1018
1084
 
1019
- # get Ids of patches
1020
- for k in range(0, zsh-z_patch+1, stride_size):
1021
- for l in range(0, ysh-y_patch+1, stride_size):
1022
- for m in range(0, xsh-x_patch+1, stride_size):
1023
- list_IDs.append(k*ysh*xsh+l*xsh+m)
1085
+ # get Ids of patches
1086
+ list_IDs = []
1087
+ for k in range(0, zsh-bm.z_patch+1, bm.stride_size):
1088
+ for l in range(0, ysh-bm.y_patch+1, bm.stride_size):
1089
+ for m in range(0, xsh-bm.x_patch+1, bm.stride_size):
1090
+ list_IDs.append(k*ysh*xsh+l*xsh+m)
1024
1091
 
1025
- # make length of list divisible by batch size
1026
- rest = batch_size - (len(list_IDs) % batch_size)
1027
- list_IDs = list_IDs + list_IDs[:rest]
1092
+ # make length of list divisible by batch size
1093
+ rest = bm.batch_size - (len(list_IDs) % bm.batch_size)
1094
+ list_IDs = list_IDs + list_IDs[:rest]
1028
1095
 
1029
- # number of patches
1030
- nb_patches = len(list_IDs)
1096
+ # prediction
1097
+ if len(list_IDs) > 400:
1098
+ load_blockwise = True
1031
1099
 
1032
- # parameters
1033
- params = {'dim': (z_patch, y_patch, x_patch),
1034
- 'dim_img': (zsh, ysh, xsh),
1035
- 'batch_size': batch_size,
1036
- 'n_channels': csh,
1037
- 'patch_normalization': bm.patch_normalization}
1100
+ # load image data and calculate patch IDs
1101
+ if not load_blockwise:
1038
1102
 
1039
- # data generator
1040
- predict_generator = PredictDataGenerator(img, list_IDs, **params)
1103
+ # load prediction data
1104
+ img, img_header, z_shape, y_shape, x_shape, region_of_interest, img_data = load_prediction_data(
1105
+ bm, channels, bm.normalize, normalization_parameters, region_of_interest, img_data, img_header)
1041
1106
 
1042
- # load model
1043
- model = load_model(str(path_to_model))
1107
+ # append ghost areas
1108
+ img, z_rest, y_rest, x_rest = append_ghost_areas(bm, img)
1109
+
1110
+ # img shape
1111
+ zsh, ysh, xsh, _ = img.shape
1112
+
1113
+ # list of IDs
1114
+ list_IDs = []
1115
+
1116
+ # get Ids of patches
1117
+ for k in range(0, zsh-bm.z_patch+1, bm.stride_size):
1118
+ for l in range(0, ysh-bm.y_patch+1, bm.stride_size):
1119
+ for m in range(0, xsh-bm.x_patch+1, bm.stride_size):
1120
+ list_IDs.append(k*ysh*xsh+l*xsh+m)
1121
+
1122
+ # make length of list divisible by batch size
1123
+ rest = bm.batch_size - (len(list_IDs) % bm.batch_size)
1124
+ list_IDs = list_IDs + list_IDs[:rest]
1125
+
1126
+ # number of patches
1127
+ nb_patches = len(list_IDs)
1128
+
1129
+ # load all patches on GPU memory
1130
+ if not load_blockwise and nb_patches < 400:
1131
+
1132
+ # parameters
1133
+ params = {'dim': (bm.z_patch, bm.y_patch, bm.x_patch),
1134
+ 'dim_img': (zsh, ysh, xsh),
1135
+ 'batch_size': bm.batch_size,
1136
+ 'n_channels': channels,
1137
+ 'patch_normalization': bm.patch_normalization}
1044
1138
 
1045
- # predict
1046
- if nb_patches < 400:
1139
+ # data generator
1140
+ predict_generator = PredictDataGenerator(img, list_IDs, **params)
1141
+
1142
+ # predict probabilities
1047
1143
  probabilities = model.predict(predict_generator, verbose=0, steps=None)
1144
+
1145
+ # create final
1146
+ final = np.zeros((zsh, ysh, xsh, nb_labels), dtype=np.float32)
1147
+ nb = 0
1148
+ for k in range(0, zsh-bm.z_patch+1, bm.stride_size):
1149
+ for l in range(0, ysh-bm.y_patch+1, bm.stride_size):
1150
+ for m in range(0, xsh-bm.x_patch+1, bm.stride_size):
1151
+ final[k:k+bm.z_patch, l:l+bm.y_patch, m:m+bm.x_patch] += probabilities[nb]
1152
+ nb += 1
1153
+
1154
+ # calculate result
1155
+ label = np.argmax(final, axis=-1).astype(np.uint8)
1156
+
1048
1157
  else:
1049
- X = np.empty((batch_size, z_patch, y_patch, x_patch, csh), dtype=np.float32)
1050
- probabilities = np.zeros((nb_patches, z_patch, y_patch, x_patch, nb_labels), dtype=np.float32)
1051
-
1052
- # get image patches
1053
- for step in range(nb_patches//batch_size):
1054
- for i, ID in enumerate(list_IDs[step*batch_size:(step+1)*batch_size]):
1055
-
1056
- # get patch indices
1057
- k = ID // (ysh*xsh)
1058
- rest = ID % (ysh*xsh)
1059
- l = rest // xsh
1060
- m = rest % xsh
1061
-
1062
- # get patch
1063
- tmp_X = img[k:k+z_patch,l:l+y_patch,m:m+x_patch]
1064
- if bm.patch_normalization:
1065
- tmp_X = np.copy(tmp_X, order='C')
1066
- for c in range(csh):
1067
- tmp_X[:,:,:,c] -= np.mean(tmp_X[:,:,:,c])
1068
- tmp_X[:,:,:,c] /= max(np.std(tmp_X[:,:,:,c]), 1e-6)
1069
- X[i] = tmp_X
1070
-
1071
- probabilities[step*batch_size:(step+1)*batch_size] = model.predict(X, verbose=0, steps=None, batch_size=batch_size)
1072
-
1073
- # create final
1074
- final = np.zeros((zsh, ysh, xsh, nb_labels), dtype=np.float32)
1075
- if bm.return_probs:
1076
- counter = np.zeros((zsh, ysh, xsh, nb_labels), dtype=np.float32)
1077
- nb = 0
1078
- for k in range(0, zsh-z_patch+1, stride_size):
1079
- for l in range(0, ysh-y_patch+1, stride_size):
1080
- for m in range(0, xsh-x_patch+1, stride_size):
1081
- final[k:k+z_patch, l:l+y_patch, m:m+x_patch] += probabilities[nb]
1158
+ # stream data batchwise to GPU to reduce memory usage
1159
+ X = np.empty((bm.batch_size, bm.z_patch, bm.y_patch, bm.x_patch, channels), dtype=np.float32)
1160
+
1161
+ # allocate final array
1162
+ if bm.return_probs:
1163
+ final = np.zeros((zsh, ysh, xsh, nb_labels), dtype=np.float32)
1164
+
1165
+ # allocate result array
1166
+ label = np.zeros((zsh, ysh, xsh), dtype=np.uint8)
1167
+
1168
+ # predict segmentation block by block
1169
+ z_indices = range(0, zsh-bm.z_patch+1, bm.stride_size)
1170
+ for j, z in enumerate(z_indices):
1171
+
1172
+ # load blockwise
1173
+ if load_blockwise:
1174
+ img, _, _, _, _, _, _ = load_prediction_data(bm,
1175
+ channels, bm.normalize, normalization_parameters,
1176
+ region_of_interest, img_data, img_header, load_blockwise, z)
1177
+ img, _, _, _ = append_ghost_areas(bm, img)
1178
+
1179
+ # list of IDs
1180
+ list_IDs = []
1181
+
1182
+ # get Ids of patches
1183
+ for l in range(0, ysh-bm.y_patch+1, bm.stride_size):
1184
+ for m in range(0, xsh-bm.x_patch+1, bm.stride_size):
1185
+ list_IDs.append(z*ysh*xsh+l*xsh+m)
1186
+
1187
+ # make length of list divisible by batch size
1188
+ max_i = len(list_IDs)
1189
+ rest = bm.batch_size - (len(list_IDs) % bm.batch_size)
1190
+ list_IDs = list_IDs + list_IDs[:rest]
1191
+
1192
+ # number of patches
1193
+ nb_patches = len(list_IDs)
1194
+
1195
+ # allocate tmp probabilities array
1196
+ probs = np.zeros((bm.z_patch, ysh, xsh, nb_labels), dtype=np.float32)
1197
+
1198
+ # get one batch of image patches
1199
+ for step in range(nb_patches//bm.batch_size):
1200
+ for i, ID in enumerate(list_IDs[step*bm.batch_size:(step+1)*bm.batch_size]):
1201
+
1202
+ # get patch indices
1203
+ k=0 if load_blockwise else ID // (ysh*xsh)
1204
+ rest = ID % (ysh*xsh)
1205
+ l = rest // xsh
1206
+ m = rest % xsh
1207
+
1208
+ # get patch
1209
+ tmp_X = img[k:k+bm.z_patch,l:l+bm.y_patch,m:m+bm.x_patch]
1210
+ if bm.patch_normalization:
1211
+ tmp_X = np.copy(tmp_X, order='C')
1212
+ for c in range(channels):
1213
+ tmp_X[:,:,:,c] -= np.mean(tmp_X[:,:,:,c])
1214
+ tmp_X[:,:,:,c] /= max(np.std(tmp_X[:,:,:,c]), 1e-6)
1215
+ X[i] = tmp_X
1216
+
1217
+ # predict batch
1218
+ Y = model.predict(X, verbose=0, steps=None, batch_size=bm.batch_size)
1219
+
1220
+ # loop over result patches
1221
+ for i, ID in enumerate(list_IDs[step*bm.batch_size:(step+1)*bm.batch_size]):
1222
+ rest = ID % (ysh*xsh)
1223
+ l = rest // xsh
1224
+ m = rest % xsh
1225
+ if i < max_i:
1226
+ probs[:,l:l+bm.y_patch,m:m+bm.x_patch] += Y[i]
1227
+
1228
+ # overlap in z direction
1229
+ if bm.stride_size < bm.z_patch:
1230
+ if j>0:
1231
+ probs[:bm.stride_size] += overlap
1232
+ overlap = probs[bm.stride_size:].copy()
1233
+
1234
+ # calculate result
1235
+ if z==z_indices[-1]:
1236
+ label[z:z+bm.z_patch] = np.argmax(probs, axis=-1).astype(np.uint8)
1082
1237
  if bm.return_probs:
1083
- counter[k:k+z_patch, l:l+y_patch, m:m+x_patch] += 1
1084
- nb += 1
1238
+ final[z:z+bm.z_patch] = probs
1239
+ else:
1240
+ block_zsh = min(bm.stride_size, bm.z_patch)
1241
+ label[z:z+block_zsh] = np.argmax(probs[:block_zsh], axis=-1).astype(np.uint8)
1242
+ if bm.return_probs:
1243
+ final[z:z+block_zsh] = probs[:block_zsh]
1244
+
1245
+ # remove appendix
1246
+ if bm.return_probs:
1247
+ final = final[:-z_rest,:-y_rest,:-x_rest]
1248
+ label = label[:-z_rest,:-y_rest,:-x_rest]
1249
+ zsh, ysh, xsh = label.shape
1085
1250
 
1086
1251
  # return probabilities
1087
1252
  if bm.return_probs:
1253
+ counter = np.zeros((zsh, ysh, xsh, nb_labels), dtype=np.float32)
1254
+ nb = 0
1255
+ for k in range(0, zsh-bm.z_patch+1, bm.stride_size):
1256
+ for l in range(0, ysh-bm.y_patch+1, bm.stride_size):
1257
+ for m in range(0, xsh-bm.x_patch+1, bm.stride_size):
1258
+ counter[k:k+bm.z_patch, l:l+bm.y_patch, m:m+bm.x_patch] += 1
1259
+ nb += 1
1088
1260
  counter[counter==0] = 1
1089
1261
  probabilities = final / counter
1090
- if not no_scaling:
1262
+ if bm.scaling:
1091
1263
  probabilities = img_resize(probabilities, z_shape, y_shape, x_shape)
1092
1264
  if np.any(region_of_interest):
1093
1265
  min_z,max_z,min_y,max_y,min_x,max_x,original_zsh,original_ysh,original_xsh = region_of_interest[:]
@@ -1096,12 +1268,8 @@ def predict_semantic_segmentation(bm, img, path_to_model,
1096
1268
  probabilities = np.copy(tmp, order='C')
1097
1269
  results['probs'] = probabilities
1098
1270
 
1099
- # get final
1100
- label = np.argmax(final, axis=3)
1101
- label = label.astype(np.uint8)
1102
-
1103
1271
  # rescale final to input size
1104
- if not no_scaling:
1272
+ if bm.scaling:
1105
1273
  label = img_resize(label, z_shape, y_shape, x_shape, labels=True)
1106
1274
 
1107
1275
  # revert automatic cropping
@@ -1130,10 +1298,10 @@ def predict_semantic_segmentation(bm, img, path_to_model,
1130
1298
  # handle amira header
1131
1299
  if header is not None:
1132
1300
  if extension == '.am':
1133
- header = get_image_dimensions(header[0], label)
1301
+ header = set_image_dimensions(header[0], label)
1134
1302
  if img_header is not None:
1135
1303
  try:
1136
- header = get_physical_size(header, img_header[0])
1304
+ header = set_physical_size(header, img_header[0])
1137
1305
  except:
1138
1306
  pass
1139
1307
  header = [header]
@@ -1151,7 +1319,7 @@ def predict_semantic_segmentation(bm, img, path_to_model,
1151
1319
 
1152
1320
  # save result
1153
1321
  if bm.path_to_image:
1154
- save_data(bm.path_to_final, label, header=header, compress=compress)
1322
+ save_data(bm.path_to_final, label, header=header, compress=bm.compression)
1155
1323
 
1156
1324
  # paths to optional results
1157
1325
  filename, extension = os.path.splitext(bm.path_to_final)
@@ -1169,17 +1337,17 @@ def predict_semantic_segmentation(bm, img, path_to_model,
1169
1337
  cleaned_result = clean(label, bm.clean)
1170
1338
  results['cleaned'] = cleaned_result
1171
1339
  if bm.path_to_image:
1172
- save_data(path_to_cleaned, cleaned_result, header=header, compress=compress)
1340
+ save_data(path_to_cleaned, cleaned_result, header=header, compress=bm.compression)
1173
1341
  if bm.fill:
1174
1342
  filled_result = clean(label, bm.fill)
1175
1343
  results['filled'] = filled_result
1176
1344
  if bm.path_to_image:
1177
- save_data(path_to_filled, filled_result, header=header, compress=compress)
1345
+ save_data(path_to_filled, filled_result, header=header, compress=bm.compression)
1178
1346
  if bm.clean and bm.fill:
1179
1347
  cleaned_filled_result = cleaned_result + (filled_result - label)
1180
1348
  results['cleaned_filled'] = cleaned_filled_result
1181
1349
  if bm.path_to_image:
1182
- save_data(path_to_cleaned_filled, cleaned_filled_result, header=header, compress=compress)
1350
+ save_data(path_to_cleaned_filled, cleaned_filled_result, header=header, compress=bm.compression)
1183
1351
 
1184
1352
  # post-processing with active contour
1185
1353
  if bm.acwe:
@@ -1188,8 +1356,8 @@ def predict_semantic_segmentation(bm, img, path_to_model,
1188
1356
  results['acwe'] = acwe_result
1189
1357
  results['refined'] = refined_result
1190
1358
  if bm.path_to_image:
1191
- save_data(path_to_acwe, acwe_result, header=header, compress=compress)
1192
- save_data(path_to_refined, refined_result, header=header, compress=compress)
1359
+ save_data(path_to_acwe, acwe_result, header=header, compress=bm.compression)
1360
+ save_data(path_to_refined, refined_result, header=header, compress=bm.compression)
1193
1361
 
1194
1362
  return results, bm
1195
1363
 
@@ -143,7 +143,7 @@ def fill(image, threshold=0.9):
143
143
  return image_i
144
144
 
145
145
  def main_helper(path_to_labels, img_id=None, friend_id=None, fill_holes=True,
146
- clean_threshold=0.1, fill_threshold=0.9, remote=False, no_compression=False):
146
+ clean_threshold=0.1, fill_threshold=0.9, remote=False, compression=True):
147
147
 
148
148
  # django environment
149
149
  if img_id is not None:
@@ -151,12 +151,6 @@ def main_helper(path_to_labels, img_id=None, friend_id=None, fill_holes=True,
151
151
  else:
152
152
  django_env = False
153
153
 
154
- # compression
155
- if no_compression:
156
- compression = False
157
- else:
158
- compression = True
159
-
160
154
  # final filenames
161
155
  filename, extension = os.path.splitext(path_to_labels)
162
156
  if extension == '.gz':
@@ -350,7 +344,7 @@ def init_remove_outlier(image_id, final_id, label_id, fill_holes=True):
350
344
  try:
351
345
  main_helper(final.pic.path, img_id=image_id, friend_id=final.friend,
352
346
  fill_holes=fill_holes, clean_threshold=label.delete_outliers, fill_threshold=label.fill_holes, remote=False,
353
- no_compression=(False if label.compression else True))
347
+ compression=label.compression)
354
348
  except Exception as e:
355
349
  print(traceback.format_exc())
356
350
 
@@ -377,7 +371,7 @@ if __name__ == '__main__':
377
371
  help='Remove outliers, e.g. 0.5 means that objects smaller than 50 percent of the size of the largest object will be removed')
378
372
  parser.add_argument('-f', '--fill_threshold', type=float, default=0.9,
379
373
  help='Fill holes, e.g. 0.5 means that all holes smaller than 50 percent of the entire label will be filled')
380
- parser.add_argument('-nc', '--no_compression', action='store_true', default=False,
374
+ parser.add_argument('-nc', '--no-compression', dest='compression', action='store_false',
381
375
  help='Disable compression of segmentation results')
382
376
  parser.add_argument('-iid','--img_id', type=str, default=None,
383
377
  help='Image ID within django environment/browser version')
@@ -42,7 +42,7 @@ class Biomedisa(object):
42
42
 
43
43
  def smart_interpolation(data, labelData, nbrw=10, sorw=4000, acwe=False, acwe_alpha=1.0, acwe_smooth=1, acwe_steps=3,
44
44
  path_to_data=None, path_to_labels=None, denoise=False, uncertainty=False, platform=None,
45
- allaxis=False, ignore='none', only='all', smooth=0, no_compression=False, return_hits=False,
45
+ allaxis=False, ignore='none', only='all', smooth=0, compression=True, return_hits=False,
46
46
  img_id=None, label_id=None, remote=False, queue=0, clean=None, fill=None):
47
47
 
48
48
  freeze_support()
@@ -73,12 +73,6 @@ def smart_interpolation(data, labelData, nbrw=10, sorw=4000, acwe=False, acwe_al
73
73
  else:
74
74
  bm.django_env = False
75
75
 
76
- # compression
77
- if no_compression:
78
- bm.compression = False
79
- else:
80
- bm.compression = True
81
-
82
76
  # disable file saving when called as a function
83
77
  if bm.data is not None:
84
78
  bm.path_to_data = None
@@ -149,19 +143,18 @@ def smart_interpolation(data, labelData, nbrw=10, sorw=4000, acwe=False, acwe_al
149
143
  bm.path_to_acwe = filename + '.acwe' + bm.final_image_type
150
144
  bm.path_to_uq = filename + '.uncertainty.tif'
151
145
 
152
- # data type
153
- if bm.data.dtype == 'uint8':
154
- pass
155
- elif bm.data.dtype == 'int8':
146
+ # data types
147
+ if bm.data.dtype == 'int8':
156
148
  bm.data = bm.data.astype(np.int16)
157
149
  bm.data += 128
158
150
  bm.data = bm.data.astype(np.uint8)
159
- else:
160
- bm.data = bm.data.astype(float)
151
+ elif bm.data.dtype != 'uint8':
152
+ bm.data = bm.data.astype(np.float32)
161
153
  bm.data -= np.amin(bm.data)
162
154
  bm.data /= np.amax(bm.data)
163
155
  bm.data *= 255.0
164
- bm.data = bm.data.astype(np.float32)
156
+ if bm.labelData.dtype in ['uint32','int64','uint64']:
157
+ bm.labelData = bm.labelData.astype(np.int32)
165
158
 
166
159
  # denoise image data
167
160
  if bm.denoise:
@@ -332,7 +325,7 @@ if __name__ == '__main__':
332
325
  help='Smoothing steps of active contour')
333
326
  parser.add_argument('--acwe_steps', metavar='STEPS', type=int, default=3,
334
327
  help='Iterations of active contour')
335
- parser.add_argument('-nc', '--no_compression', action='store_true', default=False,
328
+ parser.add_argument('-nc', '--no-compression', dest='compression', action='store_false',
336
329
  help='Disable compression of segmentation results')
337
330
  parser.add_argument('-allx', '--allaxis', action='store_true', default=False,
338
331
  help='If pre-segmentation is not exlusively in xy-plane')
biomedisa/mesh.py CHANGED
@@ -29,6 +29,7 @@
29
29
 
30
30
  import os
31
31
  import numpy as np
32
+ import biomedisa
32
33
  from biomedisa.features.biomedisa_helper import load_data, unique_file_path
33
34
  from biomedisa.features.django_env import create_pid_object
34
35
  from vtk.util.numpy_support import vtk_to_numpy, numpy_to_vtk
@@ -167,8 +168,7 @@ def save_mesh(path_to_result, labels, x_res=1, y_res=1, z_res=1,
167
168
  mesh_final.attr[start:stop,0] = i+1
168
169
  mesh_final.save(path_to_result)
169
170
 
170
- def get_voxel_spacing(header, data, extension):
171
-
171
+ def get_voxel_spacing(header, extension):
172
172
  if extension == '.am':
173
173
  # read header as string
174
174
  b = header[0].tobytes()
@@ -176,21 +176,23 @@ def get_voxel_spacing(header, data, extension):
176
176
  s = b.decode("utf-8")
177
177
  except:
178
178
  s = b.decode("latin1")
179
-
180
179
  # get physical size from image header
180
+ lattice = re.search('define Lattice (.*)\n', s)
181
181
  bounding_box = re.search('BoundingBox (.*),\n', s)
182
- if bounding_box:
182
+ if bounding_box and lattice:
183
+ # get number of voxels
184
+ lattice = lattice.group(1)
185
+ xsh, ysh, zsh = lattice.split(' ')
186
+ xsh, ysh, zsh = float(xsh), float(ysh), float(zsh)
187
+ # get bounding box
183
188
  bounding_box = bounding_box.group(1)
184
189
  i0, i1, i2, i3, i4, i5 = bounding_box.split(' ')
185
-
186
- # voxel spacing
187
- zsh, ysh, xsh = data.shape
190
+ # calculate voxel spacing
188
191
  xres = (float(i1)-float(i0)) / xsh
189
192
  yres = (float(i3)-float(i2)) / ysh
190
193
  zres = (float(i5)-float(i4)) / zsh
191
194
  else:
192
195
  xres, yres, zres = 1, 1, 1
193
-
194
196
  elif extension in ['.hdr', '.mhd', '.mha', '.nrrd', '.nii', '.nii.gz']:
195
197
  xres, yres, zres = header.GetSpacing()
196
198
  elif extension == '.zip':
@@ -203,7 +205,6 @@ def get_voxel_spacing(header, data, extension):
203
205
  else:
204
206
  print('Warning: could not get voxel spacing. Using x_spacing, y_spacing, z_spacing = 1, 1, 1 instead.')
205
207
  xres, yres, zres = 1, 1, 1
206
-
207
208
  return xres, yres, zres
208
209
 
209
210
  def init_create_mesh(id):
@@ -324,7 +325,7 @@ def init_create_mesh(id):
324
325
  log=1, imageType=None, shortfilename='Invalid label data.')
325
326
  else:
326
327
  # get voxel spacing
327
- xres, yres, zres = get_voxel_spacing(header, data, extension)
328
+ xres, yres, zres = get_voxel_spacing(header, extension)
328
329
  print(f'Voxel spacing: x_spacing, y_spacing, z_spacing = {xres}, {yres}, {zres}')
329
330
 
330
331
  # create stl file
@@ -386,7 +387,7 @@ if __name__ == "__main__":
386
387
 
387
388
  # get voxel spacing
388
389
  if not all([bm.x_res, bm.y_res, bm.z_res]):
389
- x_res, y_res, z_res = get_voxel_spacing(header, bm.labels, extension)
390
+ x_res, y_res, z_res = get_voxel_spacing(header, extension)
390
391
  if not bm.x_res:
391
392
  bm.x_res = x_res
392
393
  if not bm.y_res:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: biomedisa
3
- Version: 24.5.23
3
+ Version: 24.7.1
4
4
  Summary: Segmentation of 3D volumetric image data
5
5
  Author: Philipp Lösel
6
6
  Author-email: philipp.loesel@anu.edu.au
@@ -33,18 +33,21 @@ License-File: LICENSE
33
33
  - [License](#license)
34
34
 
35
35
  ## Overview
36
- Biomedisa (https://biomedisa.info) is a free and easy-to-use open-source application for segmenting large volumetric images, e.g. CT and MRI scans, developed at [The Australian National University CTLab](https://ctlab.anu.edu.au/). Biomedisa's semi-automated segmentation is based on a smart interpolation of sparsely pre-segmented slices, taking into account the complete underlying image data. In addition, Biomedisa enables deep learning for the fully automated segmentation of series of similar samples. It can be used in combination with segmentation tools such as Amira/Avizo, ImageJ/Fiji and 3D Slicer. If you are using Biomedisa or the data for your research please cite: Lösel, P.D. et al. [Introducing Biomedisa as an open-source online platform for biomedical image segmentation.](https://www.nature.com/articles/s41467-020-19303-w) *Nat. Commun.* **11**, 5577 (2020).
36
+ Biomedisa (https://biomedisa.info) is a free and easy-to-use open-source application for segmenting large 3D volumetric images such as CT and MRI scans, developed at [The Australian National University CTLab](https://ctlab.anu.edu.au/). Biomedisa's smart interpolation of sparsely pre-segmented slices enables accurate semi-automated segmentation by considering the complete underlying image data. Additionally, Biomedisa enables deep learning for fully automated segmentation across similar samples and structures. It is compatible with segmentation tools like Amira/Avizo, ImageJ/Fiji and 3D Slicer. If you are using Biomedisa or the data for your research please cite: Lösel, P.D. et al. [Introducing Biomedisa as an open-source online platform for biomedical image segmentation.](https://www.nature.com/articles/s41467-020-19303-w) *Nat. Commun.* **11**, 5577 (2020).
37
37
 
38
38
  ## Hardware Requirements
39
39
  + One or more NVIDIA GPUs with compute capability 3.0 or higher or an Intel CPU
40
40
 
41
41
  ## Installation (command-line based)
42
42
  + [Ubuntu 22.04 + CUDA + GPU (recommended)](https://github.com/biomedisa/biomedisa/blob/master/README/ubuntu2204_cuda11.8_gpu_cli.md)
43
- + [Ubuntu 22.04 + OpenCL + CPU (smart interpolation only and very slow)](https://github.com/biomedisa/biomedisa/blob/master/README/ubuntu2204_opencl_cpu_cli.md)
43
+ + [Ubuntu 22.04 + OpenCL + CPU (very slow)](https://github.com/biomedisa/biomedisa/blob/master/README/ubuntu2204_opencl_cpu_cli.md)
44
44
  + [Windows 10 + CUDA + GPU (recommended)](https://github.com/biomedisa/biomedisa/blob/master/README/windows10_cuda_gpu_cli.md)
45
45
  + [Windows 10 + OpenCL + GPU (easy to install but lacks features like allaxis, smoothing, uncertainty, optimized GPU memory usage)](https://github.com/biomedisa/biomedisa/blob/master/README/windows10_opencl_gpu_cli.md)
46
46
  + [Windows 10 + OpenCL + CPU (very slow)](https://github.com/biomedisa/biomedisa/blob/master/README/windows10_opencl_cpu_cli.md)
47
47
 
48
+ ## Installation (3D Slicer extension)
49
+ + [Ubuntu 22.04 + CUDA + GPU](https://github.com/biomedisa/biomedisa/blob/master/README/ubuntu2204_cuda11.8_gpu_slicer.md)
50
+
48
51
  ## Installation (browser based)
49
52
  + [Ubuntu 22.04](https://github.com/biomedisa/biomedisa/blob/master/README/ubuntu2204_cuda11.8.md)
50
53
 
@@ -52,6 +55,9 @@ Biomedisa (https://biomedisa.info) is a free and easy-to-use open-source applica
52
55
  + Download test data from our [gallery](https://biomedisa.info/gallery/)
53
56
 
54
57
  ## Revisions
58
+ 24.7.1
59
+ + 3D Slicer extension
60
+ + Prediction of large data block by block
55
61
  24.5.22
56
62
  + Pip is the preferred installation method
57
63
  + Commands, module names and imports have been changed to conform to the Pip standard
@@ -131,15 +137,14 @@ deep_learning(img_data, label_data, train=True, batch_size=12,
131
137
  ```
132
138
 
133
139
  #### Command-line based (training)
134
- Start training with a batch size of 12:
135
140
  ```
136
- python -m biomedisa.deeplearning C:\Users\%USERNAME%\Downloads\training_heart C:\Users\%USERNAME%\Downloads\training_heart_labels -t -bs=12
141
+ python -m biomedisa.deeplearning C:\Users\%USERNAME%\Downloads\training_heart C:\Users\%USERNAME%\Downloads\training_heart_labels -t
137
142
  ```
138
143
  Monitor training progress using validation data:
139
144
  ```
140
145
  python -m biomedisa.deeplearning C:\Users\%USERNAME%\Downloads\training_heart C:\Users\%USERNAME%\Downloads\training_heart_labels -t -vi=C:\Users\%USERNAME%\Downloads\val_img -vl=C:\Users\%USERNAME%\Downloads\val_labels
141
146
  ```
142
- If running into ResourceExhaustedError due to out of memory (OOM), try to use a smaller batch size.
147
+ If running into ResourceExhaustedError due to out of memory (OOM), try to use a smaller batch size (e.g. -bs=12).
143
148
 
144
149
  #### Python example (prediction)
145
150
  ```python
@@ -175,7 +180,7 @@ from biomedisa.mesh import get_voxel_spacing, save_mesh
175
180
  data, header, extension = load_data('final.Head5.am', return_extension=True)
176
181
 
177
182
  # get voxel spacing
178
- x_res, y_res, z_res = get_voxel_spacing(header, data, extension)
183
+ x_res, y_res, z_res = get_voxel_spacing(header, extension)
179
184
  print(f'Voxel spacing: x_spacing, y_spacing, z_spacing = {x_res}, {y_res}, {z_res}')
180
185
 
181
186
  # save stl file
@@ -1,26 +1,26 @@
1
1
  biomedisa/__init__.py,sha256=hw4mzEjGFXm-vxus2DBfKFW0nKoG0ibL5SH6ShfchrY,1526
2
2
  biomedisa/__main__.py,sha256=a1--8vhtztWEloHVtbM43FZLCfrFo4BELgdsgtWE8ls,536
3
- biomedisa/deeplearning.py,sha256=UG5baX58CrO8YXe9pU6_Bp2OOvbC74LQw4S33HqM2iA,27828
4
- biomedisa/interpolation.py,sha256=R8UbBWt7vOuiQCPSeNIpEY0_yfQUg1oBfhAjXi91Hl4,17253
5
- biomedisa/mesh.py,sha256=glvpTN0PPByb5j2IbLCdWQtc5O4VT-Pwu3en8EaYyTo,15819
6
- biomedisa/features/DataGenerator.py,sha256=bGys6UZ0bnKb_k1Y3Spo6MNPk_goSAmptdZnI39smaw,12770
3
+ biomedisa/deeplearning.py,sha256=eVFoy3FsSb8h2hqFBdI8kz63tATLUvuMfSPzSa_wX5Q,27813
4
+ biomedisa/interpolation.py,sha256=tehhMhm3WEVzUpfAemujMixJMK6BMNLQ6uw7Z9TR9Go,17163
5
+ biomedisa/mesh.py,sha256=8-iuVsrfW5JovaMrAez7qSxv1LCU3eiqOdik0s0DV1w,16062
6
+ biomedisa/features/DataGenerator.py,sha256=MYPSY9-ssMwPx9UOI_ZfE7_5rddOSn4aHbVQ0HtYQVA,12757
7
7
  biomedisa/features/DataGeneratorCrop.py,sha256=23R4Z-8tB1CsjYTYfhHGovlJpAny_q9OV9hq8kc2GJg,5454
8
8
  biomedisa/features/PredictDataGenerator.py,sha256=JH8SPGQm-Y7_Drec2fw3jBUupvpIkQ1FvkDXP7mUjDY,4074
9
9
  biomedisa/features/PredictDataGeneratorCrop.py,sha256=HF5tJbGtlJMHr7lMT9IiIdLG2CTjXstbKoOjlZJ93Is,3431
10
10
  biomedisa/features/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- biomedisa/features/active_contour.py,sha256=n5_vAD8jvQjU6fQ6A9hxjSmtkLLo_1fl0S5q1H2pmVg,18096
11
+ biomedisa/features/active_contour.py,sha256=FnPuvYck_KL4xYRFqwzm4Grdm288EdlcLFt88E0OtqA,17938
12
12
  biomedisa/features/assd.py,sha256=q9NUQXEoA4Pi3d8b5fmys615CWu06Sm0N9-OGwJOFnw,6537
13
- biomedisa/features/biomedisa_helper.py,sha256=iLbt4RpCU3EK51uiMefkM0321AgBdeLgrT-X4d--YJY,32250
14
- biomedisa/features/create_slices.py,sha256=tLDJmuJFN8teTiCYvMauExfVzT2ZUF28VcPXpo4sOsE,13001
13
+ biomedisa/features/biomedisa_helper.py,sha256=d5wCtiP8pRTUbXv-jrN-b7cNStm8T1B_FtQR2Msd5OY,32286
14
+ biomedisa/features/create_slices.py,sha256=uSDH1OcEYc5BFPZHSy3UpS4P2DuoVnxOZ-l7wmyT_Po,13108
15
15
  biomedisa/features/crop_helper.py,sha256=si72n9Q-C7U0cXYOD9Ux2UqIbZdXbZSOARBYDeqRggI,24533
16
16
  biomedisa/features/curvop_numba.py,sha256=AjKQJcUBoURTB8pq1HmugQYpBwBELthhcEu51_r_xPI,7049
17
17
  biomedisa/features/django_env.py,sha256=pdiPcBpqu1BWuyvh-palIGVwHFaY-leQ4Gatlbm8hIg,8942
18
- biomedisa/features/keras_helper.py,sha256=muPwb_W7O63exzWACJ4oLdAc-AQitcxLeNwgHvxyhuE,50488
18
+ biomedisa/features/keras_helper.py,sha256=0rgMLD3mvLuV9PFbsOwypH4fr3IhPkQveCtUiqTCZdc,57270
19
19
  biomedisa/features/nc_reader.py,sha256=RoRMwu3ELSNfoV3qZtaT2OWACnXb2EhNFu_DAF1T93o,7406
20
20
  biomedisa/features/pid.py,sha256=Jmn1VIp0fBlgBrqZ-yUIQVVb5-NAxNBdibXALVr2PPI,2545
21
21
  biomedisa/features/process_image.py,sha256=VtS3fGDvglqJiiJLPK1toe76J58j914NJ8XQKg3CRwo,11091
22
22
  biomedisa/features/pycuda_test.py,sha256=UGAGIz_dgcCJkzjyCqnMlflp-WJPzpCtFQmE9C5DwIo,3275
23
- biomedisa/features/remove_outlier.py,sha256=XhbFPkazMmEUZiP0FERdCkrXaLhwO095x4wcn-B3SdU,16756
23
+ biomedisa/features/remove_outlier.py,sha256=C2m-Wsw-n3q8Ft21SGNwEd3wDU_T1ghqQ5hldwU_rqI,16627
24
24
  biomedisa/features/split_volume.py,sha256=UgMpHhZPvH90xFo-mJ0Oc0tBXbrf8FQF0kzVySAlO8g,8917
25
25
  biomedisa/features/amira_to_np/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  biomedisa/features/amira_to_np/amira_data_stream.py,sha256=JrZTyKP01CKDFB5d9BlGtSFwBgoAo0AJeAmn3pADH88,32618
@@ -37,8 +37,8 @@ biomedisa/features/random_walk/pyopencl_large.py,sha256=q79AxG3p3qFjxfiAZfUK9I5B
37
37
  biomedisa/features/random_walk/pyopencl_small.py,sha256=opNlS-qzOa9qWafBNJdvf6r1aRAFf7_JXf6ISDnkdXE,17068
38
38
  biomedisa/features/random_walk/rw_large.py,sha256=ZnITvk00Y11ZZlGuBRaJO1EwU0wYBdEwdpj9vvXCqF4,19805
39
39
  biomedisa/features/random_walk/rw_small.py,sha256=RPzZe24YrEwYelJukDjvqaoD_SyhgdriEi7uV3kZGXI,14881
40
- biomedisa-24.5.23.dist-info/LICENSE,sha256=sehayP6UhydNnmstfL4yFR3genMRdpuUh6uZVWJN1H0,14152
41
- biomedisa-24.5.23.dist-info/METADATA,sha256=XIEKdnTY54QTALYPtNambyN7NP7_e76S2Mo99Ubifv8,10475
42
- biomedisa-24.5.23.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
43
- biomedisa-24.5.23.dist-info/top_level.txt,sha256=opsf1Eb4vCguPSxev4HHSeiUKCccT_C_RcUCdAYbHWQ,10
44
- biomedisa-24.5.23.dist-info/RECORD,,
40
+ biomedisa-24.7.1.dist-info/LICENSE,sha256=sehayP6UhydNnmstfL4yFR3genMRdpuUh6uZVWJN1H0,14152
41
+ biomedisa-24.7.1.dist-info/METADATA,sha256=4ziBlBpd6JbYp9YSmxs0ptZpm_XX3J4pERQAL6Kg118,10631
42
+ biomedisa-24.7.1.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
43
+ biomedisa-24.7.1.dist-info/top_level.txt,sha256=opsf1Eb4vCguPSxev4HHSeiUKCccT_C_RcUCdAYbHWQ,10
44
+ biomedisa-24.7.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (71.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5