Skip to main content

Image Math Studio

Classical Image Processing Lab — Creators Quest
Creators Quest — Interactive Lab

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

⏳ processing…
Original
Processed
Original histogram
Processed histogram
Convolution

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.

Box blur (uniform)
1/9
1/9
1/9
1/9
1/9
1/9
1/9
1/9
1/9
Gaussian (σ≈1)
.0625
.125
.0625
.125
.25
.125
.0625
.125
.0625
3
How it works output[x,y] = Σ kernel[i,j] × image[x+i, y+j]
// kernel values follow e^(-(i²+j²)/2σ²) / (2πσ²)
// all values positive, sum = 1 → brightness preserved
// larger σ = wider blur = more noise removed = more detail lost
Denoising

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.

3×3
Median filter logic neighbours = collect all pixels in window → sort → pick middle
// 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
Sharpening

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.

Laplacian sharpen
0
-1
0
-1
+5
-1
0
-1
0
Strong sharpen
-1
-1
-1
-1
+9
-1
-1
-1
-1
0.8×
2
Unsharp masking formula blurred = gaussian_blur(original, σ)
detail = original − blurred ← only edges remain
result = original + amount × detail
// flat areas: detail ≈ 0 → unchanged
// edges: detail is large → boosted → looks sharper
Upscaling

Interpolation Methods

To enlarge an image, new pixels must be invented. Each method uses different math to estimate those missing values from existing neighbours.

2×
Bilinear interpolation P = (1-fx)(1-fy)·A + fx(1-fy)·B + (1-fx)fy·C + fx·fy·D
// 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
Tonal

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.

Histogram equalisation CDF(v) = (pixels with brightness ≤ v) / total_pixels
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
Edge Detection

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.

Sobel X (vertical edges)
-1
0
+1
-2
0
+2
-1
0
+1
Sobel Y (horiz. edges)
-1
-2
-1
0
0
0
+1
+2
+1
50
Sobel gradient magnitude Gx = convolve(image, sobel_x_kernel)
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

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.

4
25
1
Bilateral weight formula w(i,j) = G_space(||p−q||) × G_range(|I_p − I_q|)
// 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

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.

Emboss NW→SE
-2
-1
0
-1
1
+1
0
+1
+2
2
Emboss kernel principle Asymmetric: negative values NW side, positive values SE side
// 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
Pixelate

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).

10px
Mosaic / privacy blur For each block of size B×B:
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

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.

50%
60%
Radial falloff formula dist = √( (x-cx)²/rx² + (y-cy)²/ry² ) ← normalised 0..1
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