ASCII-Filter/sobel.py
2024-09-06 10:44:50 -05:00

214 lines
5.0 KiB
Python

import scipy.ndimage as nd
import imageio.v3 as iio
import numpy as np
import math
from PIL import Image, ImageFilter
IMAGE_PATH = 'engine.png'
BLUR_STRENGTH_1 = 1
BLUR_STRENGTH_2 = 3
DOG_THRESHOLD = 8
sobel_x_kernel = ImageFilter.Kernel((3, 3),
(
1, 0, -1,
2, 0, -2,
1, 0, -1
))
sobel_y_kernel = ImageFilter.Kernel((3, 3),
(
1, 2, 1,
0, 0, 0,
-1, -2, -1
))
def main():
image = Image.open(IMAGE_PATH)
# image = image.convert('L')
blur_1 = image.filter(ImageFilter.GaussianBlur(BLUR_STRENGTH_1))
blur_2 = image.filter(ImageFilter.GaussianBlur(BLUR_STRENGTH_2))
dog: Image = difference_of_gaussians(blur_1, blur_2)
sb = sobel(image)
sb.save('sobel-pil.png')
sb = sobel(dog)
sb.save('sobel-dog.png')
# dog.save('dog.png')
# sb_x = dog.filter(sobel_x_kernel)
# sb_x.save('pil-sobel_x.png')
# image_np = np.array(dog)
# sb_x = nd.sobel(image_np, 1)
# sb_y = nd.sobel(image_np, 0)
# combined_sobel = np.sqrt(np.square(sb_x) + np.square(sb_y))
# i = Image.fromarray(combined_sobel, 'L')
# i.save('sobel.png')
# iio.imwrite('sobel_x.png', sb_x)
# iio.imwrite('sobel_y.png', sb_y)
def subtract_colors(t1: tuple, t2: tuple) -> tuple:
if type(t1) != tuple:
if (t2 - t1) >= DOG_THRESHOLD:
return t2 - t1
else:
return 0
if len(t1) != len(t2):
print('Len of first tuple should equal second tuple probably')
exit(1)
ans = []
for i, n in enumerate(t1):
ans.append(t2[i] - n)
return tuple(ans)
def difference_of_gaussians(blur_1: Image, blur_2: Image) -> Image:
w, h = blur_1.size
dog = Image.new(blur_1.mode, blur_1.size)
for pixel_y in range(0, h):
for pixel_x in range(0, w):
coords = (pixel_x, pixel_y)
dog.putpixel(coords, subtract_colors(blur_2.getpixel(coords), blur_1.getpixel(coords)))
return dog
# Taken from
# https://enzoftware.github.io/posts/image-filter-python
def sobel(img: Image) -> Image:
width, height = img.size
newimg = Image.new("RGB", (width, height), "white")
for x in range(1, width-1): # ignore the edge pixels for simplicity (1 to width-1)
for y in range(1, height-1): # ignore edge pixels for simplicity (1 to height-1)
# initialise Gx to 0 and Gy to 0 for every pixel
Gx = 0
Gy = 0
# top left pixel
p = img.getpixel((x-1, y-1))
r = p[0]
g = p[1]
b = p[2]
# intensity ranges from 0 to 765 (255 * 3)
intensity = r + g + b
# accumulate the value into Gx, and Gy
Gx += -intensity
Gy += -intensity
# remaining left column
p = img.getpixel((x-1, y))
r = p[0]
g = p[1]
b = p[2]
Gx += -2 * (r + g + b)
p = img.getpixel((x-1, y+1))
r = p[0]
g = p[1]
b = p[2]
Gx += -(r + g + b)
Gy += (r + g + b)
# middle pixels
p = img.getpixel((x, y-1))
r = p[0]
g = p[1]
b = p[2]
Gy += -2 * (r + g + b)
p = img.getpixel((x, y+1))
r = p[0]
g = p[1]
b = p[2]
Gy += 2 * (r + g + b)
# right column
p = img.getpixel((x+1, y-1))
r = p[0]
g = p[1]
b = p[2]
Gx += (r + g + b)
Gy += -(r + g + b)
p = img.getpixel((x+1, y))
r = p[0]
g = p[1]
b = p[2]
Gx += 2 * (r + g + b)
p = img.getpixel((x+1, y+1))
r = p[0]
g = p[1]
b = p[2]
Gx += (r + g + b)
Gy += (r + g + b)
# calculate the length of the gradient (Pythagorean theorem)
length = math.sqrt((Gx * Gx) + (Gy * Gy))
# normalise the length of gradient to the range 0 to 255
length = length / 4328 * 255
length = int(length)
# draw the length in the edge image
#newpixel = img.putpixel((length,length,length))
newimg.putpixel((x,y),(length,length,length))
return newimg
if __name__ == '__main__':
main()
# image = iio.imread(IMAGE_PATH)
# sb_x = nd.sobel(image, 1)
# sb_y = nd.sobel(image, 0)
# iio.imwrite('sobel_x.png', sb_x)
# iio.imwrite('sobel_y.png', sb_y)