Skip to main content
GameDev.net gamedev.net
🔒 Locked

Bicubic Interpolation for Image Resizing

Started by soconne Jun 3, 2004 at 12:29 PM 17 replies 10.2k views
Original Post
soconne
soconne
Does anybody have any good urls showing code for resizing image s using Bicubic Interpolation? Or at least a site that explains it well?
Author Freeworld3Dhttp://www.freeworld3d.org
soconne
soconne
Well I found an explanation for bicubic interpolation here http://astronomy.swin.edu.au/~pbourke/colour/bicubic/

and I''ve implemented it, but it does not seem to be working. I tried it on a single 2x2 image, with the 4 pixel values of Red,Blue,Yellow, and Green, and after enlarging the 2x2 image to an 8x8 image, it simply had a faded dark red in the upper left and then diagnolly down to the right was a darker red, then every other pixel was black.

Can anybody help me with my code?

code
Author Freeworld3Dhttp://www.freeworld3d.org
outRider
outRider


uint Interpolate_Value(uint v0, uint v1, uint v2, uint v3, float t)
{
int p = (v3 - v2) - (v0 - v1);
int q = (v0 - v1) - p;
int r = v2 - v0;
int s = v1;
float tSqrd = t * t;
//
return (p * (tSqrd * t)) + (q * tSqrd) + (r * t) + s;
//return (p * (tSqrd * t) + 0.5f) + (q * tSqrd + 0.5f) + (r * t + 0.5f) + s; // Use this one for nicer interpolation, it rounds instead of truncates.
}
//
// To interpolate a pixel we need to interpolate each color component seperately
//
ARGB Interpolate_Pixel(ARGB p0, ARGB p1, ARGB p2, ARGB p3, float t)
{
return InterpolateValue(p0.a, p1.a, p2.a, p3.a, t) << 24 |
InterpolateValue(p0.r, p1.r, p2.r, p3.r, t) << 16 |
InterpolateValue(p0.g, p1.g, p2.g, p3.g, t) << 08 |
InterpolateValue(p0.b, p1.b, p2.b, p3.b, t);
}
//
//
//
Resize_Image(ARGB *Image, uint Width, uint Height, uint NewWidth, uint NewWidth, ARGB *NewImage)
{
uint OldImageX, OldImageY;
float dx = Width / NewWidth, dy = Height / NewHeight;
ARGB Temp[4];
//
//
for (y = 0; y < NewHeight; ++y)
{
OldImageY = dy * y;
ty = dy * y - OldImageY;
//
for (x = 0; x < NewWidth; ++x)
{
OldImageX = dx * x;
tx = dx * x - OldImageX;
//
// I don't know how image scaling works with regards to edges
// e.g. when OldImageX is 0 and you need pixels from behind (OldImageX - 1) you can either wrap around and get pixels to sample from the other side of the image (OldImageX = Width - 1)
// or you can just clamp to the edge (if (OldImageX - 1 < 0) OldImageX = 0), your choice, see what looks best.
// Likewise for when OldImageX == Width - 1 and you need to get OldImageX + 1
//
// To interpolate 2D data correctly we first interpolate 4 pixels horizontally for each of 4 scanlines, which leaves us with 4 new pixels
//
Temp[0] = Interpolate_Pixel(Image[(OldImageY - 1) * Width + OldImageX - 1],
Image[(OldImageY - 1) * Width + OldImageX],
Image[(OldImageY - 1) * Width + OldImageX + 1],
Image[(OldImageY - 1) * Width + OldImageX + 2],
tx);
Temp[1] = Interpolate_Pixel(Image[OldImageY * Width + OldImageX - 1],
Image[OldImageY * Width + OldImageX],
Image[OldImageY * Width + OldImageX + 1],
Image[OldImageY * Width + OldImageX + 2],
tx);
Temp[2] = Interpolate_Pixel(Image[(OldImageY + 1) * Width + OldImageX - 1],
Image[(OldImageY + 1) * Width + OldImageX],
Image[(OldImageY + 1) * Width + OldImageX + 1],
Image[(OldImageY + 1) * Width + OldImageX + 2],
tx);
Temp[3] = Interpolate_Pixel(Image[(OldImageY + 2) * Width + OldImageX - 1],
Image[(OldImageY + 2) * Width + OldImageX],
Image[(OldImageY + 2) * Width + OldImageX + 1],
Image[(OldImageY + 2) * Width + OldImageX + 2],
tx);
//
// Then we interpolate those 4 pixels to get a single pixel that is a composite of 4 * 4 pixels, 16 pixels
NewImage[y * NewWidth + x] = Interpolate_Pixel(Temp[0], Temp[1], Temp[2], Temp[3], ty);
}
}
}



Note that it contains a bit of pseudo-code, but I hope you understand. Note the caveats. You can do a lot of optimization, but for clarity I skipped most of it, you can obviously reuse some of the values instead of recalculating them. I haven't tested it, but if anyone spots something amiss feel free to jump in and correct it.

Arghhh.. better code parsing plz. x_x

------------
- outRider -

[edited by - outRider on June 5, 2004 4:26:54 PM]
soconne
soconne
This is for bicubic interpolation? or bilinear ?
Author Freeworld3Dhttp://www.freeworld3d.org
outRider
outRider
Bicubic. Bilinear would sample the nearest 4 pixels, bicubic samples the nearest 16.

------------
- outRider -

[edited by - outRider on June 5, 2004 4:28:05 PM]
soconne
soconne
Ah, okay. Well thanks a lot for the code :-)
Author Freeworld3Dhttp://www.freeworld3d.org
soconne
soconne
I tried implementing your code, but I still got very odd results. Here's the 8x8 image I was enlarging to a 32x32, and the result of what happened after enlarging.

8x8 image


32x32 image



I pretty much tried to copy your code piece by piece, although I had to convert it over to using char instead of int for the pixel data. But other than that, it is identical.

CODE

Any ideas ?

[edited by - oconnellseanm on June 5, 2004 5:55:49 PM]
Author Freeworld3Dhttp://www.freeworld3d.org
outRider
outRider
I''ll test it and get back to you later.

------------
- outRider -
soconne
soconne
Oh I got it to work, thanks again. I updated the the code with the working version.

CODE
Author Freeworld3Dhttp://www.freeworld3d.org
outRider
outRider
Yeah, I just tested it myself and noticed I forgot to clamp the values in the 0-255 range in InterpolateValue(). Cubic interpolation can overflow or underflow, so you have to either clamp or rescale all your values.
outRider
outRider
Just a comment on your code.

First, you have a massive memory leak, or rather lots of little memory leaks.

In InterpolatePixel() do unsigned char ret[3]; instead of *ret = ...; Cast it to unsigned char* if it complains on the return.

Also, to pick up speed you only have to calculate PosX1/2/3 once per loop since OldImageX only changes once per loop. No need to keep doing it. And for both PosX and PosY if you do

PosX = something - 1;
if (PosX < 0) {...}
if (PoxX > somevalue) {...} <--- there's no need for this, you subtracted and PosX is signed, there's no way it will get larger (in this case)

Same for when you do + 1 and + 2, don't check if it's less than 0, it can't happen. Remove those useless checks for both PosX and PosY.

Glad it worked for you though.

------------
- outRider -

[edited by - outRider on June 5, 2004 10:15:42 PM]
soconne
soconne
I don''t know why I didn''t notice this before, but tx and ty are always 0....you see what I mean?
Author Freeworld3Dhttp://www.freeworld3d.org
outRider
outRider
Only when minifying. For magnification it''s a value between 0 and 1.

Truthfully I''ve never used this code for minifying so I can''t be 100% sure it''s correct. I didn''t realize you were using it for mipmaps or something like that.

In fact I''m pretty sure it''s not correct, I''ll look into it tomorrow.

------------
- outRider -
soconne
soconne
I think its 0 always.

OldImageY = dy * y;
ty = dy * y - OldImageY = dy*y - dy*y = 0;

See what I mean ?
Author Freeworld3Dhttp://www.freeworld3d.org
outRider
outRider
No, that's on purpose.

dy * y produces a float
OldImageY = dy * y; stores the result as an int, disregarding the part after the decimal
ty = dy * y; stores the result as a float, so you retain the entire number;
ty = dy * y - OldImageY; stores the number minus the integer part, so you only get the part after the decimal, which is the intent.

------------
- outRider -

[edited by - outRider on June 6, 2004 9:03:10 AM]
soconne
soconne
LOL, my mistake :-p
Author Freeworld3Dhttp://www.freeworld3d.org
Bourla
Bourla
Is this code right ?

InterpolateValue does not look like the R function defined at http://astronomy.swin.edu.au/~pbourke/colour/bicubic/

Morover this peace of code in InterpolateValue() seems odd :

float p = (v3 - v2) - (v0 - v1);
float q = (v0 - v1) - p;

why not write q = v2 - v3 ?






okonomiyaki
okonomiyaki
Quote:
Original post by Bourla

float p = (v3 - v2) - (v0 - v1);
float q = (v0 - v1) - p;

why not write q = v2 - v3 ?


Because they are not the same.

float p = (v3 - v2) - (v0 - v1);

is the same as

float p = v3 - v2 - v0 + v1;

so q would be

float q = v0 - v1 - (v3 - v2 - v0 + v1);

which would be

float q = v0 - v1 - v3 + v2 + v0 - v1;

which would be

float q = 2v0 - 2v1 - v3 + v2;
Bourla
Bourla
yes, my mistake...

But it still is not the same formula than pbourke site !?


Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.