Dilation in PIL
Recently I wanted to try and figure out how dilation works and if I could replicate it inside of python using nothing but the PIL library. It's a pretty straightforward bit of code.
I found a tutorial online by Ryan Brucks on how to do Dilation Inside of Unreal and applied that to Python.
First lets check out the code
dilate.py
from PIL import Image
OPERATIONS = (
lambda idx, width: idx - 1, # left pixel
lambda idx, width: idx - width, # top pixel
lambda idx, width: idx + 1, # right pixel
lambda idx, width: idx + width, # bottom pixel
lambda idx, width: idx - width - 1, # top left pixel
lambda idx, width: idx - width + 1, # top right pixel
lambda idx, width: idx + width - 1, # botom right pixel
lambda idx, width: idx + width + 1, # bottom left pixel
)
def dilate_texture(img: Image, iterations: int = 1):
"""
This function goes through each pixel and samples it's 8 neighbors and finds a non 0 neighbor to
replace the current pixels color with
:param img: Image to dilate
:param iterations: How many times to run the dilate filter
:return: Image the dilated image
"""
width = img.width
original_alpha = img.split()[-1]
for i in range(iterations):
pixels = img.getdata() # create the pixel map
data = list()
for idx, pixel in enumerate(pixels): # for every pixel:
# add our data in
data.append(pixel)
alpha = pixel[-1]
# if alpha is 0 we want to search for a neighbor with alpha
if alpha == 0:
for op in OPERATIONS:
try:
# find neighbor pixels
r, g, b, a = pixels[op(idx, width)]
# find the first non 0 alpha neighbor
if a != 0:
# replace the color we set earlier with neighbors
data[idx] = (r, g, b, a)
# bail out and move on to next pixel if we find a neighbor
break
except IndexError:
pass
img.putdata(data)
img.putalpha(original_alpha)
return img
img = Image.open("dilate_test_undilated.png")
img = dilate_texture(img, 10)
img.save("dilate_test_dilated.tga")
The undilated image on the left and the image dilated with one iteration on the right.
It should be noted that this is certainly not fast by any means. A roughly 128x128 image dilated 64 times takes around 33 seconds, but you know, it's python what can you do.
If you have ideas to make it faster please let me know. I ended up making this in c++ as well and it's much faster! Link here.