A_SN 122 Report post Posted October 13, 2008 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 radius_x, radius_y; // radius of the triangle from the projected point in original units 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_y = ratio_y; if (radius_x<1.f) 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 radius_y = 1.f; 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] 0 Share this post Link to post Share on other sites
A_SN 122 Report post Posted October 14, 2008 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 :) 0 Share this post Link to post Share on other sites