Fraser Fraser -4 years ago 152
Java Question

Bilinear Interpolation Byte Rollover Java

The issue in short

So I've been working on a 3d software renderer and needed a bilinear interpolation method. I'm working with a 3-Byte BGR raster for speed and I can't figure out how my code is functionally different from something working I scraped together directly accessing the image itself. I'll lay out the code first before the results.

float lerp(float x1, float x2, float a) {
return x1 * (1 - a) + x2 * a;
}


Basic linear interpolation algorithem

void berp(float sourceX, float sourceY, int destPos, byte[] source, byte[] dest, int sourceWidth) {
float ax = sourceX - 0.5f;
float bx = sourceX + 0.5f;
float xa = ax - (int) (ax);
float ay = sourceY - 0.5f;
float by = sourceY + 0.5f;
float ya = ay - (int) (ay);
int pos1 = (((int) ay) * sourceWidth + ((int) ax)) * 3;
int pos2 = (((int) ay) * sourceWidth + ((int) bx)) * 3;
int pos3 = (((int) by) * sourceWidth + ((int) ax)) * 3;
int pos4 = (((int) by) * sourceWidth + ((int) bx)) * 3;


Just set it up so that pos1 through pos4 the positions in the source array of the 4 nearest pixels to the coards (sourceX & sourceY) given. xa and ya are the distance from the coards to the center of the pixel to the top and pixel to left respectably, so basically just how much to sample from each pixel.

dest[destPos] = (byte) lerp(lerp(source[pos1], source[pos2], xa), lerp(source[pos3], source[pos4], xa), ya);
dest[destPos + 1] = (byte) lerp(lerp(source[pos1 + 1], source[pos2 + 1], xa), lerp(source[pos3 + 1], source[pos4 + 1], xa), ya);
dest[destPos + 2] = (byte) lerp(lerp(source[pos1 + 2], source[pos2 + 2], xa), lerp(source[pos3 + 2], source[pos4 + 2], xa), ya);
}


Here I interpolation between rgb1 and rgb2 then rgb3 and rgb4 using xa. Then I take the results of those and interpolate between them using ya. Repeat for Green and Red and it should work out golden.

EXCEPT:

Just look at this pic, there is no way to describe it using words.

I'm sure you see my predicament.

Any help at all at this point would be welcome, I've tried for hours to no avail, it looks like it's rollover with the float-byte cast but I don't really see how it can be given that lerp will only ever give a value between x1 and x2 and only bytes go in so...

*

*

*

*

*

Just occurred to me that it might be helpful to add the code that produced the correct image. Some names and stuff might be different but the gist of it's all there :)

private int getColorBilinear (float x, float y, BufferedImage s) {
float nx = x - 0.5f;
float xa = nx - (int) (nx);
float ny = y - 0.5f;
float ya = ny - (int) (ny);
int rgb1 = s.getRGB((int) (x - 0.5), (int) (y - 0.5));
int rgb2 = s.getRGB((int) (x + 0.5), (int) (y - 0.5));
int rgb3 = s.getRGB((int) (x - 0.5), (int) (y + 0.5));
int rgb4 = s.getRGB((int) (x + 0.5), (int) (y + 0.5));

return (int) (interpolate(
interpolate(((rgb1 >> 16) & 0x000000FF), ((rgb2 >> 16) & 0x000000FF), xa),
interpolate(((rgb3 >> 16) & 0x000000FF), ((rgb4 >> 16) & 0x000000FF), xa),
ya)) << 16
| (int) (interpolate(
interpolate(((rgb1 >> 8) & 0x000000FF), ((rgb2 >> 8) & 0x000000FF), xa),
interpolate(((rgb3 >> 8) & 0x000000FF), ((rgb4 >> 8) & 0x000000FF), xa),
ya)) << 8
| (int) (interpolate(interpolate((rgb1 & 0x000000FF), (rgb2 & 0x000000FF), xa),
interpolate((rgb3 & 0x000000FF), (rgb4 & 0x000000FF), xa), ya));
}

Answer Source

The actual process is this:

  • The pixel value is sampled as an unsigned byte
  • The bytes (assumed to be signed) are interpolated
  • The interpolated value is than used in the destination image as an unsigned byte

Now shockingly this actually works for most cases. The problem arises when one of the source values is above 127 and the other is below. For example the interpolation method would see (129,125) as (-127,125) and tries to interpolate as such. One way of combating this problem would be to first convert to an int, then interpolating; an implementation of which is given below.

float lerp(byte x1, byte x2, float a) {

return unsignedByteToInt(x1) * (1 - a) + unsignedByteToInt(x2) * a;

}

int unsignedByteToInt(byte x) {return (((int) x) + 255) % 255;}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download