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

Question:

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.

Asked By: Ben123

||

Answers:

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:

  • this answer using PIL, or
  • this answer with scikit-image, or

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

Answered By: Mark Setchell