Change the numbers, see what happens to the picture, take the Pillow code away.
Nothing is uploaded. The convolution runs in your browser.
Every output pixel is a weighted sum of the pixels around it. The grid holds the weights. The middle cell is the pixel itself, the cells around it are its neighbours. Put 1 in the middle and 0 everywhere else and nothing changes, which is why that one is called the identity.
After the sum, Pillow divides by scale and adds offset. Leave scale empty and it uses the sum of the weights, which keeps the overall brightness the same. That is why a box blur of nine ones does not come out nine times too bright.
Offset matters for kernels that sum to zero, such as edge detectors and emboss. Without it, everything that is not an edge comes out black. An offset of 128 puts the flat areas back in the middle grey.
| Kernel | Does |
|---|---|
0 -1 0 / -1 5 -1 / 0 -1 0 | Sharpen. The centre is boosted, the neighbours subtracted |
1 1 1 / 1 1 1 / 1 1 1 | Box blur, with scale 9 |
-1 -1 -1 / -1 8 -1 / -1 -1 -1 | Edge detect. Sums to zero |
-2 -1 0 / -1 1 1 / 0 1 2 | Emboss. Asymmetric, so it looks lit from one side |
-1 0 1 / -2 0 2 / -1 0 1 | Sobel, finds vertical edges |
from PIL import Image, ImageFilter
kernel = ImageFilter.Kernel(
(3, 3),
[0, -1, 0,
-1, 5, -1,
0, -1, 0],
scale=1,
)
im = Image.open("photo.jpg")
im.filter(kernel).save("out.png")
L and RGB images. Convert a palette image to RGB first.Yes. The same arithmetic, in JavaScript. Checked against Pillow 12 on a 360 by 240 image with both a sharpen and a blur kernel: every interior pixel came out identical, no difference at all. The edges differ slightly, because this tool repeats the border pixel while Pillow leaves the outermost rows untouched.
See the filter gallery for the ready made filters.