imgkit
Image processing, written out — 2D convolution, Gaussian & median filters, Sobel edges and Otsu threshold on canvas, before/after side by side.
Source image
Sample is a fixed-seed procedural scene (shapes + gradients) so every run is comparable — same idea as tools/make_images.py.
Apply a filter
Before / after
Original
Result
Pick a filter and press Apply — or click a filter button to run it immediately.
Convolution is not correlation
Almost every "convolution" in image code is correlation: the kernel slides in place, no flip. For symmetric kernels (box, Gaussian) they match. For Sobel they differ by sign — correlate(sobel) == -convolve(sobel).
The UI notes cover both mental models; edges use correlation like the Python correlate() path.
Separable Gaussian
A 2D Gaussian is rank-1: two 1D passes cost O(k) per pixel instead of O(k²). The kernel line counts above show the speed-up for the current σ.
Padding reflects (not zeros), so edges do not invent a black frame for the detector to find.
What each filter does
| Filter | Arithmetic |
|---|---|
| Grayscale | 0.2126R + 0.7152G + 0.0722B (Rec. 709, not average) |
| Gaussian blur | separable 1D passes, radius = ⌈3σ⌉ |
| Median 3×3 | neighbourhood median — kills salt & pepper |
| Sharpen | I − strength · Laplacian(I) |
| Sobel edges | ‖∇I‖ via 3×3 correlate with sobel-x / sobel-y |
| Canny-style | blur → gradients → non-max suppression → hysteresis |
| Otsu | maximise between-class variance; mid of tied range |
| Equalise | CDF stretch over 256 bins |