[Image-SIG] morphological functions

Robert Klimek klimek@grc.nasa.gov
Mon, 8 Jul 2002 17:32:08 -0400


On Monday 24 June 2002 05:27 pm, you wrote:
> I need to add some morphological function capability to my PIL based
> program, such as erosion, dilation, hole fill, border, and skeletonizin=
g.
> I'd rather not re-invent the wheel if I don't have to. So, does anybody
> have and this written and are willing to share the code?
>


For those interested, I've written a couple of PIL-based functions that=20
perform a 4-point erosion and dilation and I thought I'd share the code. =
Note=20
that these functions work only on thresholded images (not on general=20
grayscale). These functions are fairly fast., compared to my earlier atte=
mpts=20
where I looped through all the pixels in the image getting min of a 3x3=20
neighborhood.

Bob



def erode(image):
    paddedImage =3D createPaddedImage(image, 1)
    thresholdImg =3D paddedImage.point(lambda i, v=3D128: i > v and 255)
    filteredImg =3D thresholdImg.filter(ImageFilter.FIND_EDGES)
    thresholdImg =3D filteredImg.point(lambda i, v=3D128: i > v and 255)
    arithImg =3D ImageChops.subtract(paddedImage, thresholdImg)
    box =3D (1, 1, arithImg.size[0]-1, arithImg.size[1]-1)
    outImage =3D arithImg.crop(box)
    return outImage

def dilate(image):
    paddedImage =3D createPaddedImage(image, 1)
    thresholdImg =3D paddedImage.point(lambda i, v=3D128: i > v and 255)
    thresholdImg =3D ImageChops.invert(thresholdImg)
    filteredImg =3D thresholdImg.filter(ImageFilter.FIND_EDGES)
    thresholdImg =3D filteredImg.point(lambda i, v=3D128: i > v and 255)
    arithImg =3D ImageChops.add(paddedImage, thresholdImg)
    box =3D (1, 1, arithImg.size[0]-1, arithImg.size[1]-1)
    outImage =3D arithImg.crop(box)
    return outImage

def createPaddedImage(img, pad):
    '''Create an padded image - since it is created with resize() the bor=
der =20
       pixels will be same (almost) as the original edge pixels.'''
    sizeX, sizeY =3D img.size
    paddedImage =3D img.resize((sizeX+2*pad, sizeY+2*pad))
    # paste original image into the new big image with an offset
    paddedImage.paste(img, (pad, pad))
    return paddedImage