Classical Image Processing
The math behind blur, sharpen, denoise & more — before AI existed. Upload an image and see each algorithm work live.
Drop an image here
JPG, PNG, WEBP — processed entirely in your browser, nothing is uploaded
Gaussian Blur
A Gaussian kernel weights nearby pixels more than distant ones (bell-curve falloff). Smooths noise while avoiding the blocky artefacts of a simple box average.
// kernel values follow e^(-(i²+j²)/2σ²) / (2πσ²)
// all values positive, sum = 1 → brightness preserved
// larger σ = wider blur = more noise removed = more detail lost
Noise Removal
Median filter: sorts neighbours, picks the middle value — outlier spikes (salt & pepper noise) end up at the sorted extremes and are discarded. Non-linear, so it can't be expressed as a convolution kernel.
// spike at 255 gets sorted to end, median stays at ~120
// non-linear: cannot be expressed as kernel convolution
// excellent edge preservation — unlike Gaussian blur
Unsharp Masking & Laplacian Sharpen
Unsharp masking: blur the image, subtract to get a "detail layer" of only edges, then add that detail back amplified. The paradox of using blur to create sharpness.
detail = original − blurred ← only edges remain
result = original + amount × detail
// flat areas: detail ≈ 0 → unchanged
// edges: detail is large → boosted → looks sharper
Interpolation Methods
To enlarge an image, new pixels must be invented. Each method uses different math to estimate those missing values from existing neighbours.
// A,B,C,D = 4 surrounding original pixels
// fx,fy = fractional position within that 1×1 cell
// bicubic: same idea but 4×4=16 neighbours + cubic weights
// none can invent detail — only interpolate existing values
Histogram Equalisation & Levels
Reshaping the distribution of brightness values. Stretching spreads a narrow range to fill 0–255. Equalisation redistributes pixel counts so every brightness level has equal representation.
new_value = round( CDF(old_value) × 255 )
// maps dense clusters → spreads them out
// sparse regions get compressed together
// CLAHE applies this per tile + clips to avoid noise amplification
Sobel, Prewitt & Canny
Edges are where brightness changes rapidly. Kernel-based detectors measure the brightness gradient. Canny adds non-maximum suppression and hysteresis thresholding for clean, thin edges.
Gy = convolve(image, sobel_y_kernel)
magnitude = √(Gx² + Gy²)
direction = atan2(Gy, Gx)
// flat region → magnitude ≈ 0
// edge → magnitude is large → bright pixel in output
Bilateral Filter — edge-preserving smooth
Like Gaussian blur, but the weight of each neighbour also depends on how similar its brightness is. Pixels across an edge have very different brightness → they get near-zero weight and are not blended in. Smooth areas are blurred, edges stay sharp.
// G_space = spatial Gaussian (nearby = higher weight)
// G_range = brightness Gaussian (similar = higher weight)
// across an edge: I_p ≠ I_q → G_range ≈ 0 → edge not blurred
Emboss & Relief Effects
An emboss kernel is an asymmetric edge detector — it finds edges but from one direction only, creating a raised 3D relief effect. Used heavily in vintage photo software and print effects.
// flat area → positives cancel negatives → output = 128 (grey)
// edge lit from NW → large positive output → bright pixel
// edge shaded → large negative → dark pixel
// result looks like the image was pressed into clay and lit
Pixelation & Mosaic
Divide the image into blocks. Replace every pixel in a block with the block's average colour. The extreme case of box blur — essentially nearest-neighbour downscale followed by nearest-neighbour upscale. Also the basis of privacy blurring (faces, license plates).
avg_r,g,b = mean(all pixel values in block)
Fill all pixels in block with avg_r,g,b
// equiv. to: downscale ÷B with box filter, upscale ×B nearest-neighbour
// used for face/plate anonymisation in news photography
Vignette & Radial Tonal Effects
A vignette darkens the image edges using a radial brightness falloff — originally an optical artefact of wide-aperture lenses where the corners received less light. Applied intentionally in darkroom printing as a compositional tool.
factor = smoothstep(inner_r, 1.0, dist)
pixel = lerp(pixel, 0, factor × strength)
// smoothstep gives S-curve falloff (no hard edge)
// cx,cy = image centre; rx,ry = half-width, half-height
Comments
Post a Comment