How can I translate an image with subpixel accuracy?

Timulus picture Timulus · Sep 14, 2011 · Viewed 7.6k times · Source

I have a system that requires moving an image on the screen. I am currently using a png and just placing it at the desired screen coordinates.

Because of a combination of the screen resolution and the required frame rate, some frames are identical because the image has not yet moved a full pixel. Unfortunately, the resolution of the screen is not negotiable.

I have a general understanding of how sub-pixel rendering works to smooth out edges but I have been unable to find a resource (if it exists) as to how I can use shading to translate an image by less than a single pixel.

Ideally, this would be usable with any image but if it was only possible with a simple shape like a circle or a ring, that would also be acceptable.

Answer

Paul R picture Paul R · Sep 14, 2011

Sub-pixel interpolation is relatively simple. Typically you apply what amounts to an all-pass filter with a constant phase shift, where the phase shift corresponds to the required sub-pixel image shift. Depending on the required image quality you might use e.g. a 5 point Lanczos or other windowed sinc function and then apply this in one or both axes depending on whether you want an X shift or a Y shift or both.

E.g. for a 0.5 pixel shift the coefficients might be [ 0.06645, 0.18965, 0.27713, 0.27713, 0.18965 ]. (Note that the coefficients are normalised, i.e. their sum is equal to 1.0.)

To generate a horizontal shift you would convolve these coefficients with the pixels from x - 2 to x + 2, e.g.

const float kCoeffs[5] = { 0.06645f, 0.18965f, 0.27713f, 0.27713f, 0.18965f };

for (y = 0; y < height; ++y)         // for each row
    for (x = 2; x < width - 2; ++x)  // for each col (apart from 2 pixel border)
    {
        float p = 0.0f;              // convolve pixel with Lanczos coeffs

        for (dx = -2; dx <= 2; ++dx)
            p += in[y][x + dx] * kCoeffs[dx + 2];

        out[y][x] = p;               // store interpolated pixel
    }