Search code examples
pythonnumpymatplotlibpython-imaging-library

How to overlay Grayscale Mask on top of RGB image using Numpy and Matplotlib ( opencv or scikit image in case not possible)


I have 2 images from Carvana Image Dataset where image is jpg and mask is gif. I have converted the mask as grayscale as 0 or 1 and now want to overlay it over the image to see these 3 original, mask, superimposed side by side using matplotlib. What is the right way to do this?

from PIL import Image

def get_pair(image_path, mask_path):
    image = np.array(Image.open(image_path).convert('RGB'))
    mask = np.array(Image.open(mask_path).convert('L'), dtype = np.float32) # Mask should be Grayscale so each value is either 0 or 255
    mask[mask == 255.0] = 1.0 # whereever there is 255, convert it to 1: (1 == 255 == White)
    return image, mask 

One way could be:


image, mask = data[0]
image = image / 255
mask = np.stack((mask,)*3, axis=-1)

blended = image * mask
plt.imshow(blended)

but it shows only the car and everything else as black

Below are the 2 images

enter image description here enter image description here

and I want to plot these 3 as:

enter image description here


Solution

  • There might be a misconception in what you expect.

    ... but it shows only the car and everything else as black

    This is how a binary mask usually operates.

    The following selfcontained code (with the images from above saved accordingly) might explain what happens. Note the comments near blended1 = ...

    
    from PIL import Image
    import numpy as np
    from matplotlib import pyplot as plt
    
    def get_pair(image_path, mask_path):
        image = np.array(Image.open(image_path).convert('RGB'))
        mask = np.array(Image.open(mask_path).convert('L'), dtype = np.float32) # Mask should be Grayscale so each value is either 0 or 255
        mask[mask == 255.0] = 1.0 # whereever there is 255, convert it to 1: (1 == 255 == White)
        return image, mask 
    
    img, mask = get_pair("img.jpg", "mask.gif")
    print(f"{img.shape=}")  #      -> img.shape=(1280, 1918, 3)
    print(f"{mask.shape=}")  #     -> img.shape=(1280, 1918)
    
    mask2 = np.stack((mask,)*3, axis=-1)
    print(f"{mask2.shape=}")  # -> img.shape=(1280, 1918, 3)
    
    # rescale image
    img = img /255
    
    # set every pixel to (0, 0, 0) (black) where mask is 0 and
    # keep every pixel unchanged where mask is 1
    # this is how a mask is usually applied
    blended1 = img*mask2
    
    # set every pixel to (1, 1, 1) (white) where mask is 1 and
    # keep every pixel unchanged where mask is 0
    
    
    blended2 = np.clip(img+mask2, 0, 1)
    
    fig, axx = plt.subplots(1, 4, figsize=(8, 18))
    
    for ax, arr, title in zip(axx,
                            [img, mask, blended1, blended2],
                            ["original", "mask", "blended1", "blended2"]):
        ax.imshow(arr)
        ax.axis("off")
        ax.set_title(title)
    
    plt.show()
    

    The resulting image:

    figure with 4 subfigures car inside environment, mask, only car, only environment