Interpolation Methods
Resizing an image asks for values between the pixels you have. Interpolation blends the neighbours by distance, so close points count more than far ones.
Why Does This Exist?
Resizing an image, warping it, or sampling a feature map at a fractional coordinate all ask the same impossible question: what is the value between the grid points you actually stored? There is no measured answer, so every renderer and every vision model must manufacture a plausible one from the neighbours.
The three standard answers form a ladder. Nearest neighbour copies the closest pixel, which is fast but blocky. Bilinear blends the 4 surrounding pixels by distance, which smooths edges. Bicubic fits a cubic surface through the 16 surrounding pixels, which preserves sharpness better but can overshoot. This page works the bilinear arithmetic by hand, because once its weights click, the other two are variations on the same idea. The warping that consumes these samples is in image transformations, and the filtering sibling that smooths grids instead of resampling them is convolution.
The one idea you need first
A weighted average blends values by weights that sum to one. At the centre of a square each corner is equally far, so each gets weight . Near one corner, that corner dominates. Bilinear interpolation is two weighted averages in sequence: blend along the top edge, blend along the bottom edge, then blend those two results vertically. The grid reasoning is the same one behind finite differences.
Think of It Like This
Blending paint by how close you stand
Four paint cans sit at the corners of a square, labelled 10, 20, 30 and 40. You stand somewhere inside and mix a colour from all four, pouring more from the cans nearest you. Dead centre you pour equally and get 25.0, the plain mean. One step from a corner and that can dominates the cup.
The analogy maps part for part: cans are pixels, distance sets the weights, the cup is the sampled value. Where it stops: paint mixes physically while interpolation is pure arithmetic, and no blend invents a colour no can holds, just as interpolation never recovers detail the grid never recorded.
How It Actually Works
Nearest copies the closest grid value: . One memory read, zero blending, visible steps at edges.
Bilinear blends the neighbourhood. With corners (top left), (top right), (bottom left), (bottom right) and fractional position inside the square, first blend horizontally along each row, then vertically:
In plain words: is the distance from the left edge, the distance from the top, and each blend gives nearby corners proportionally more say. The surface between four corners is quadratic, not flat, which is why diagonal gradients look slightly curved.
Bicubic fits a cubic polynomial through the neighbourhood (16 pixels) instead. Cubic pieces join with matching slopes, so edges stay crisper than bilinear, at roughly four times the reads and a real risk of ringing past sharp steps.
Worked example
Sample the grid above at its centre . Top edge: . Bottom edge: . Vertical: . At the centre every corner carries weight , and .
As a check, sample : top is , bottom is , and the result is . Closer to the heavy 30 and 40 corners, the answer leans their way, exactly as distance weighting promises.
Code
The point is the check: run the blend and confirm both hand computations land exactly.
def bilinear(grid: list[list[float]], x: float, y: float) -> float: # grid: [[top_left, top_right], [bottom_left, bottom_right]] top = grid[0][0] * (1 - x) + grid[0][1] * x bot = grid[1][0] * (1 - x) + grid[1][1] * x return top * (1 - y) + bot * y
grid = [[10.0, 20.0], [30.0, 40.0]]print(bilinear(grid, 0.5, 0.5)) # -> 25.0print(bilinear(grid, 0.25, 0.75)) # -> 27.5Both outputs match the hand arithmetic, including the off-centre 27.5 that leans toward the larger corners.
Watch Out For
Expecting upsampling to restore lost detail
The symptom is a pipeline that downsamples aggressively and trusts bicubic to rebuild texture later: small text stays mush, thin lines stay broken, and no interpolation order fixes it. Interpolation blends recorded neighbours; it cannot recover frequencies the grid never captured. That is aliasing, not a settings bug.
Downsample less, capture at the working resolution, or reach for a learned super-resolution model whose prior supplies the missing texture. If edges look smooth but fine structure is gone, the information is gone, not the method.
Half pixel coordinate conventions going wrong
The symptom is a resized image shifted by half a pixel, or a warp that is inexplicably blurry on one side: corner-aligned versus centre-aligned coordinates disagree about where pixel zero lives. Libraries split on this (the classic align corners flag), and mixing conventions across resize, warp and loss silently offsets every sample.
Fix the convention once per pipeline and test it with an asymmetric grid like the one above: sample must return exactly , and exactly . If either endpoint drifts, the grid origin is off by half a step somewhere upstream.
The Quick Version
- Interpolation manufactures values between stored grid points by blending neighbours.
- Nearest copies one pixel; bilinear blends 4 by distance; bicubic fits 16 with cubics.
- Corners 10, 20, 30, 40 read 25.0 at the centre and 27.5 at (0.25, 0.75).
- Bilinear weights always sum to one, so flat regions pass through unchanged.
- Upsampling smooths but never restores detail the grid never recorded.
- Pin the pixel coordinate convention with exact endpoint samples before trusting a resize.