pythonnumpypython-imaging-librarydilation

PIL/Numpy: enlarging white areas of black/white mask image


I want to produce a Python algorithm which takes in a 'mask' RGB image comprised exclusively of black and white pixels. Basically, each mask is a black image with one or more white shapes on it (see below).

enter image description here

I want to transform this image by enlarging the white areas by a factor x:

enter image description here

So far I have only got it to work by drawing rectangles around the shapes using PIL:

def add_padding_to_mask(mask, padding):
    # Create a copy of the original mask
    padded_mask = mask.copy()
    draw = ImageDraw.Draw(padded_mask)

    # Iterate over the pixels in the original mask
    for x in range(mask.width):
        for y in range(mask.height):
            # If the pixel is white, draw a white rectangle with the desired padding around it
            if mask.getpixel((x, y)) == (255, 255, 255):
                draw.rectangle((x-padding, y-padding, x+padding, y+padding), fill=(255, 255, 255))

    return padded_mask

This is suboptimal since I want to retain the original white shapes (only make them larger). I can't figure out an efficient way to approach this problem. Any help greatly appreciated.


Solution

  • If you want to enlarge a white object on a black background, you can use "morphological dilation". There are many tools/methods:

    The simplest is with ImageMagick on the command-line, e.g.:

    magick XlAiE.png -morphology dilate disk:3.5 result.png
    

    enter image description here

    There are lots of examples here. I'm showing you with ImageMagick not because it is better or anything, but so you can quickly experiment with different structuring elements and sizes without needing to code any (slightly more complicated) Python. Increase the 3.5 in the command for more dilation, or decrease it for less.


    If you want to use Python, see:


    Here's an OpenCV version:

    #!/usr/bin/env python3
    
    import cv2
    import numpy as np
    
    # Load image
    im = cv2.imread('XlAiE.png', cv2.IMREAD_GRAYSCALE)
    
    # Dilate with an elliptical/circular structuring element
    SE = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))
    res = cv2.morphologyEx(im, cv2.MORPH_DILATE, SE)
    cv2.imwrite('result.png', res)
    

    enter image description here

    Reduce the (9,9) to, say (3,3) for less dilation, or increase it to, say (20,20) for more dilation.


    If you just want to dilate vertically, use a tall thin structuring element:

    SE = np.ones((20,1), np.uint8)
    res = cv2.morphologyEx(im, cv2.MORPH_DILATE, SE)
    

    enter image description here

    If you just want to dilate horizontally, use a wide thin structuring element:

    SE = np.ones((1,20), np.uint8)
    res = cv2.morphologyEx(im, cv2.MORPH_DILATE, SE)
    

    enter image description here