I am working with 3D CT images and trying to resize the binary segmentation mask from (564,359,359) into (128,128,128) as follows:
from skimage.transform import resize
mask_resized= resize(binary_mask, (128, 128, 128), order=0)
Binary mask before resizing looks as follows:
The resulting output is not binary (yields a series of values between 0 & 1) and the output is distorted as follows:
I tried image_resized_seg = np.rint(image_resized_seg)
, but this yields full black images for some slices containing the segmentation mask.
I tried the following as well, which also gives distorted images and some slices containing mask is missing in the output:
from scipy import ndimage
def resize_volume_mask(img):
"""Resize across z-axis"""
# Set the desired depth
desired_depth = 128
desired_width = 128
desired_height = 128
# Get current depth
current_depth = img.shape[0] #-1
current_width = img.shape[1] #0
current_height = img.shape[2] #1
# Compute depth factor
depth = current_depth / desired_depth
width = current_width / desired_width
height = current_height / desired_height
depth_factor = 1 / depth
width_factor = 1 / width
height_factor = 1 / height
# Rotate
#img = ndimage.rotate(img, 90, reshape=False)
# Resize across z-axis
img = ndimage.zoom(img, (depth_factor, width_factor, height_factor), order=0)
return img
Could someone please advise on how to resize the segmentation mask without loss of information, while keeping it binary?
The resulting output is not binary
This is normal. When an image is resized, an interpolation is performed. This is especially required when the target image is smaller than the source because the amount of information in the target image is smaller. When the target image is bigger, the interpolation scheme enable to get something that looks "nice" for us.
There are many interpolation method. The most basic one is nearest. It does not really interpolate the pixels. Instead, it find the nearest one in the source image matching with the location of the target image. Typical interpolation methods are bilinear and cubic interpolation (using basic polynomials). By default, skimage.transform.resize
uses a Gaussian filter for a downsampling since anti_aliasing
is not set and the input datatype is not bool
:
Whether to apply a Gaussian filter to smooth the image prior to downsampling. It is crucial to filter when downsampling the image to avoid aliasing artifacts. If not specified, it is set to True when downsampling an image whose data type is not bool.
Thus you need to use a boolean mask, not a floating-point one, or to set anti_aliasing
to false
.
this yields full black images for some slices containing the segmentation mask.
This is certainly because the floating-point value in the image have a range between 0 and 1 (this is the standard for images defined as an array of floating-point values). np.rint
will result in 0-1 integers that skimage will interpret as standard integer components of pixels defined in the 0-255 range. As a result, the whole image seems black but it is not completely black. You should multiply the result by 255 so to see the 1
value.
which also gives distorted images
resize the segmentation mask without loss of information
A small distortion is normal. It is due to the loss of information. Nearly all downscale will cause a loss of information (it is not the case only if the source image is an upscaled version of the downscaled target image).
Using a threshold after the downscale gives the following image:
It looks quite good compared to the initial image and the fact that skimage use a Gaussian filter before. Using anti_aliasing=false
certainly give a better result.
To summary, the best solution is certainly simply to call resize(binary_mask, (128, 128, 128), anti_aliasing=false, order=0)
or convert the mask first using resize(binary_mask.astype(bool), (128, 128, 128), order=0)
. You may need to convert the result back to floating point regarding the next operations in your code. If so, be careful to the range of the value.