I am trying to implement a bilinear interpolation function, but for some reason I am getting bad output. I cant seem to figure out what's wrong, any help getting on the right track will be appreciated.
double lerp(double c1, double c2, double v1, double v2, double x)
{
if( (v1==v2) ) return c1;
double inc = ((c2-c1)/(v2 - v1)) * (x - v1);
double val = c1 + inc;
return val;
};
void bilinearInterpolate(int width, int height)
{
// if the current size is the same, do nothing
if(width == GetWidth() && height == GetHeight())
return;
//Create a new image
std::unique_ptr<Image2D> image(new Image2D(width, height));
// x and y ratios
double rx = (double)(GetWidth()) / (double)(image->GetWidth()); // oldWidth / newWidth
double ry = (double)(GetHeight()) / (double)(image->GetHeight()); // oldWidth / newWidth
// loop through destination image
for(int y=0; y<height; ++y)
{
for(int x=0; x<width; ++x)
{
double sx = x * rx;
double sy = y * ry;
uint xl = std::floor(sx);
uint xr = std::floor(sx + 1);
uint yt = std::floor(sy);
uint yb = std::floor(sy + 1);
for (uint d = 0; d < image->GetDepth(); ++d)
{
uchar tl = GetData(xl, yt, d);
uchar tr = GetData(xr, yt, d);
uchar bl = GetData(xl, yb, d);
uchar br = GetData(xr, yb, d);
double t = lerp(tl, tr, xl, xr, sx);
double b = lerp(bl, br, xl, xr, sx);
double m = lerp(t, b, yt, yb, sy);
uchar val = std::floor(m + 0.5);
image->SetData(x,y,d,val);
}
}
}
//Cleanup
mWidth = width; mHeight = height;
std::swap(image->mData, mData);
}
Input Image (4 pixels wide and high)
My Output
Expected Output (Photoshop's Bilinear Interpolation)
Photoshop's algorithm assumes that each source pixel's color is in the center of the pixel, while your algorithm assumes that the color is in its topleft. This causes your results to be shifted half a pixel up and left compared to Photoshop.
Another way to look at it is that your algorithm maps the x coordinate range (0, srcWidth)
to (0, dstWidth)
, while Photoshop maps (-0.5, srcWidth-0.5)
to (-0.5, dstWidth-0.5)
, and the same in y coordinate.
Instead of:
double sx = x * rx;
double sy = y * ry;
You can use:
double sx = (x + 0.5) * rx - 0.5;
double sy = (y + 0.5) * ry - 0.5;
to get similar results. Note that this can give you a negative value for sx
and sy
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With