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

  1. Calculate a 2x2 patch variance weighting image
  2. 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)