Source code for niftynet.layer.binary_masking
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, division
import numpy as np
import scipy.ndimage as ndimg
from scipy.ndimage.morphology import binary_fill_holes as fill_holes
from niftynet.layer.base_layer import Layer
from niftynet.utilities.util_common import look_up_operations
from niftynet.utilities.util_common import otsu_threshold
"""
This class defines methods to generate a binary image from an input image.
The binary image can be used as an automatic foreground selector, so that later
processing layers can only operate on the `True` locations within the image.
"""
SUPPORTED_MASK_TYPES = set(['threshold_plus', 'threshold_minus',
'otsu_plus', 'otsu_minus', 'mean_plus'])
SUPPORTED_MULTIMOD_MASK_TYPES = set(['or', 'and', 'multi'])
[docs]class BinaryMaskingLayer(Layer):
def __init__(self,
type_str='otsu_plus',
multimod_fusion='or',
threshold=0.0):
super(BinaryMaskingLayer, self).__init__(name='binary_masking')
self.type_str = look_up_operations(
type_str.lower(), SUPPORTED_MASK_TYPES)
self.multimod_fusion = look_up_operations(
multimod_fusion.lower(), SUPPORTED_MULTIMOD_MASK_TYPES)
self.threshold = threshold
def __make_mask_3d(self, image):
assert image.ndim == 3
image_shape = image.shape
image = image.reshape(-1)
mask = np.zeros_like(image, dtype=np.bool)
thr = self.threshold
if self.type_str == 'threshold_plus':
mask[image > thr] = True
elif self.type_str == 'threshold_minus':
mask[image < thr] = True
elif self.type_str == 'otsu_plus':
thr = otsu_threshold(image) if np.any(image) else thr
mask[image > thr] = True
elif self.type_str == 'otsu_minus':
thr = otsu_threshold(image) if np.any(image) else thr
mask[image < thr] = True
elif self.type_str == 'mean_plus':
thr = np.mean(image)
mask[image > thr] = True
mask = mask.reshape(image_shape)
mask = ndimg.binary_dilation(mask, iterations=2)
mask = fill_holes(mask)
# foreground should not be empty
assert np.any(mask == True), \
"no foreground based on the specified combination parameters, " \
"please change choose another `mask_type` or double-check all " \
"input images"
return mask
[docs] def layer_op(self, image):
if image.ndim == 3:
return self.__make_mask_3d(image)
if image.ndim == 5:
mod_to_mask = [m for m in range(image.shape[4])
if np.any(image[..., :, m])]
mask = np.zeros_like(image, dtype=bool)
mod_mask = None
for mod in mod_to_mask:
for t in range(image.shape[3]):
mask[..., t, mod] = self.__make_mask_3d(image[..., t, mod])
# combine masks across the modalities dim
if self.multimod_fusion == 'or':
if mod_mask is None:
mod_mask = np.zeros(image.shape[:4], dtype=bool)
mod_mask = np.logical_or(mod_mask, mask[..., mod])
elif self.multimod_fusion == 'and':
if mod_mask is None:
mod_mask = np.ones(image.shape[:4], dtype=bool)
mod_mask = np.logical_and(mod_mask, mask[..., mod])
for mod in mod_to_mask:
mask[..., mod] = mod_mask
return mask
else:
raise ValueError("unknown input format")