Variance Weighted Bilinear Scaling - GreycLab/gmic-community GitHub Wiki
A simple edge-smoothing adjustment to bilinear interpolation. This article aims to describe the algorithm in a plain language manner; clarity will be favoured over efficiency. Image coordinates are assumed to begin at zero.
Algorithm Overview
- Calculate a 2x2 patch variance weighting image
- Calculate a weighted bilinear scaling of the input image
Box Filter
For the method shown here, a 2x2 box filter algorithm is required. A short description of one possible implementation follows.
First pass:
- Iterate over all pixels p(x,y) to produce a new image q(x,y)
q(x,y) = 0.25 * p(x-1,y) + 0.5 * p(x,y) + 0.25 * p(x+1,y)
Second pass:
- Iterate over all pixels p(x,y)
q(x,y) = 0.25 * p(x,y-1) + 0.5 * p(x,y) + 0.25 * p(x,y+1)
This is also equivalent to a single convolution with a 3x3 grid of values:
0.0625 0.125 0.0625
0.125 0.25 0.125
0.0625 0.125 0.0625
Patch Variance Image
-
Start with two copies of the input image
-
Square the values of the first image:
p(x,y) = p(x,y) * p(x,y)
-
Apply a 2x2 box filter to both images (see above)
-
Square the values of the second image
-
Combine the images by difference:
p(x,y) = p(x,y) - q(x,y)
-
Add a small minimum to prevent infinities:
p(x,y) = p(x,y) + 0.000001
-
Compute the reciprocal of the image:
p(x,y) = 1 / p(x,y)
Note: if the input is a multi-channel image (e.g. RGB), then the values must be summed or averaged to produce a single channel output. It is suggested this is done immediately after combining by difference:
q(x,y) = R(x,y) + G(x,y) + B(x,y)
Scaled Image
Create a target output image of the desired new dimensions. In this example, p(x,y) is a pixel from the input (original) image, v(x,y) is a pixel from the variance image and q(x,y) is a pixel in the target image.
Iterate over all pixels in the target image:
scaled_X = x * (width_original - 1) / (width_new - 1)
scaled_Y = y * (height_original - 1) / (height_new - 1)
xA = floor(scaled_X)
xB = ceil(scaled_X)
yA = floor(scaled_Y)
yB = ceil(scaled_Y)
Lx = scaled_X - xA
Rx = 1 - Lx
Ly = scaled_Y - yA
Ry = 1 - Ly
wAA = Rx * Ry * v(xA,yA)
wAB = Rx * Ly * v(xA,yB)
wBA = Lx * Ry * v(xB,yA)
wBB = Lx * Ly * v(xB,yB)
sum_weights = wAA + wAB + wBA + wBB
sum_pixel = wAA * p(xA,yA) + wAB * p(xA,yB) + wBA * p(xB,yA) + wBB * p(xB,yB)
q(x,y) = sum_pixel / sum_weights
Alternative Scaling Method
The final scaling step can also be done using existing bilinear scaling routines:
- Multiply the original image by the variance image:
p(x,y) = p(x,y) * v(x,y)
- Scale both images using standard bilinear interpolation
- Combine the images by division:
p(x,y) = p(x,y) / v(x,y)