Search code examples
pythonimage-processingmaskscikit-imagenifti

How to extract labelled masks in PNG format correctly from an array?


I have the following file in Nifti format, which contains the masks, I have written the following code to extract the images inside as PNG images, the problem is that:

This is a multiclass masks labelled with [0. 1. 2. 3.], after saving the mask as PNG the values inside mask image are distributed among the range [0 --> 255] and they are not just 4 labels as before!

How can I solve this issue please?

The only important thing is to have 4 unique labels inside.

Thanks in Advance.

import nibabel as nib
import os
import glob
import numpy as np
from skimage.io import imread, imsave
#-------------------------------------------
# Multi Class Masks
path = 'Masks'
Dataset = glob.glob( os.path.join(path, '*.gz') )
ctr = 0
for image in Dataset:
    # Load masks voxel
    images = nib.load(image).get_fdata()
    print(np.unique(images))
    # Save it as PNG
    ctr+=1
    if(not os.path.exists('Dataset/masks/Case_'+str(ctr))):
        os.mkdir('Dataset/masks/Case_'+str(ctr))
    for _id in range(images.shape[2]):
        imsave(os.path.join('Dataset/masks','Case_'+
            str(ctr),str(ctr)+'_'+str(_id+1)+'.png'),
             resize(images[:,:,_id],(256,256)))
#-------------------------------------------
        imag = imread(os.path.join('Dataset/masks','Case_'+
             str(ctr),str(ctr)+'_'+str(_id+1)+'.png'))
        print(np.unique(imag))

Solution

  • Just to warn you, I know nothing about Nifti data and may be completely wrong here... but, I think the problem is that when you do:

    fdata = nib.load(image).get_fdata()
    

    you actually get a float64 datatype:

    print(fdata.dtype)
    

    and PNG format only handles uint8 or uint16, but not float64.

    I assume that, as the shape of data is (630, 630, 45), it means there are 45 slices of something, each 630x630 - but again, I may be wrong. So, I took a fairly central slice on the basis that there is more likely to be something in the middle and I converted it to uint8 and saved it as a PNG.

    imsave('result.png', fdata[...,22].astype(np.uint8))
    

    The PNG is low contrast, because all values are less than 4 on a scale of 0..255, so I increased the contrast with ImageMagick just so you can see it:

    magick result.png -auto-level visible.png
    

    enter image description here

    You can get a histogram and see the frequency of occurrence of each of the 4 class labels with ImageMagick in Terminal:

    magick result.png -format "%c" histogram:info
    

    Sample Output

    335942: (0,0,0) #000000 gray(0)      <--- there are 335,942 pixels=0
     26367: (1,1,1) #010101 gray(1)      <--- there are  26,367 pixels=1
     29419: (2,2,2) #020202 gray(2)
      5172: (3,3,3) #030303 gray(3)