# Bilinear resizing - I fail at it

This topic is 3416 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I wrote a function which purpose is to resize an using bilinear filtering/interpolation given its old dimensions and the new dimensions, regardless of the aspect ratio. I decided to try and get to same code working for both regular linear interpolation (upsizing) and linear filtering (when downsizing) for that reason, because while an axis can be upsized the other one can be simultaneously downsized. It works well for downsizing, looks great and all right, and for upsizing it works OK until you realise it really looks like a bit of pre-smoothing + nearest neighbour interpolation. That puzzles me, I have no idea how it can make it look like it does a tad bit of blurring on the original image and then upsamples it with nearest neighbour interpolation. I would blame it on the way that I calculate the 'weight' variable, which works by adding the sum of each weight used for each of the 4 original neighbouring pixels for the calculation of each new interpolated pixel, however when I used a fixed 'weight' variable things are 'way wronger' than when I do it like this. I'm kind of stumped that I would fail at doing something as simple as bilinear interpolation, but I just cannot see what's wrong with what I do, despite spending a whole day on trying to fix it, so I need help.. Thanks a lot in advance. EDIT : Also, my code is a tad slow. Any thoughts on that? I could move to fixed point airthmetic when things work I guess, but I'm going to reuse the code anything for some float image resizing..
uint32_t *resize_image(rgb32_t *in, int32_t old_w, int32_t old_h, int32_t new_w, int32_t new_h)
{
int32_t i, iy, ix, jy, jx;			// iterators
int32_t jy_start, jy_stop, jx_start, jx_stop;	// bounds for the iteration of pixels from the original image
rgb32_t *out;			// new image
float pos_in_x, pos_in_y;	// position of the new image pixel in the old image
float ratio_x, ratio_y;		// downsampling ratio (< 1.0 means enlargement)
float weight;			// global weight to apply to all weights, globally
float p_weight, p_weight_y;	// local pixel weight
int32_t *p_weight_p = (int32_t *) &p_weight;		// integer pointer to the float p_weight
int32_t *p_weight_yp = (int32_t *) &p_weight_y;		// integer pointer to the float p_weight_y
float r, g, b;

ratio_x = (float) (old_w-1) / (float) (new_w-1);	// resizing ratio
ratio_y = (float) (old_h-1) / (float) (new_h-1);
radius_x = ratio_x;					// how far from the new pixel should we look for old pixels to weight and add
radius_x = 1.f;					// if we upsample
if (radius_y<1.f)					// keep the radius to 1 so that we catch all the 4 neighbouring original pixels
weight = 1.f / (radius_x * radius_y);			// product by which to weight to final sum for a pixel

out = calloc (new_w * new_h, sizeof(rgb32_t));

for (iy=0; iy<new_h; iy++)
{
pos_in_y = (float) iy * ratio_y;			// position of the new pixel (iy) in the old array
jy_start = (int32_t) ceilf(pos_in_y - radius_y);	// only start at the first pixel that comes after the new pixel minus its radius
if (jy_start < 0)		// bounds checking
jy_start = 0;

jy_stop = (int32_t) ceilf(pos_in_y + radius_y);		// stop at the last pixel before the new pixel + its radius
if (jy_stop >= old_h)		// bounds checking
jy_stop = old_h - 1;

for (ix=0; ix<new_w; ix++)
{
pos_in_x = (float) ix * ratio_x;		// position of the new pixel (ix) in the old array
jx_start = (int32_t) ceilf(pos_in_x - radius_x);
if (jx_start < 0)
jx_start = 0;

jx_stop = (int32_t) ceilf(pos_in_x + radius_x);
if (jx_stop >= old_w)
jx_stop = old_w - 1;

r = 0;		// reset the values of the new interpolated pixels for each colour channel
g = 0;
b = 0;

weight = 0;

for (jy=jy_start; jy<jy_stop; jy++)	// iteration through all the neighbouring pixels from the new interpolated pixel
{
p_weight_y = ((float) jy - pos_in_y) * (1.f/radius_y);	// normalised distance of the original neighbouring pixel from the new pixel
*p_weight_yp &= 0x7FFFFFFF;	// absolute value by binary masking (sets the sign to 0)
p_weight_y = 1.f - p_weight_y;		// f(x) = 1 - x; <-- the formula for linear interpolation

for (jx=jx_start; jx<jx_stop; jx++)
{
p_weight = ((float) jx - pos_in_x) * (1.f/radius_x);	// same thing as above in the X axis
*p_weight_p &= 0x7FFFFFFF;		// absolute value by binary masking (sets the sign to 0)
p_weight = 1.f - p_weight;
p_weight *= p_weight_y;		// we multiply the weights of each axis to obtain the 2D weight

weight += p_weight;

r += (float) in[jy*old_w + jx].r * p_weight;	// we multiply each original pixel value by its weight
g += (float) in[jy*old_w + jx].g * p_weight;	// and add it to the new value,
b += (float) in[jy*old_w + jx].b * p_weight;	// for each channel
}
}

weight = 1.f/weight;			// we do that to avoid having to do 3 divisions (1 div + 3 muls is faster than 3 divs, right?)

out[iy*new_w + ix].r = r * weight;	// finally we weight the final sum and put it in the new image array
out[iy*new_w + ix].g = g * weight;
out[iy*new_w + ix].b = b * weight;
}
}

return (uint32_t *) out;
}
[Edited by - A_SN on October 13, 2008 9:06:42 PM]

##### Share on other sites
Nevermind I guess.. it looks like when I upsample in both dimensions I just need to set the 'weight' variable always to 1...

If anyone has any comments on the speed, they're still welcome :)