双线性缩放的公式都为人熟知,但是自己实现一遍却没有达到OpenCV的效果。仔细阅读了OpenCV的源码,将双线性缩放摘出来分享给大家一起学习
特别注意以下几点:
1、取整运算
直接使用int强制转换时截断取整,加上一个0.5矫正系数变为四舍五入取整
2、分段移位
猜测是因为cbufy[0] 为short类型,必须将dx右移4位也变成short类型进行乘积(dx为8+11=19位)
3、浮点精度
使用定点运算时将加权系数精度调节到11位。置于为什么不能使用更高的精度我觉得是因为两个11+8=30位,马上就达到int顶满了。
#define INTER_RESIZE_COEF_BITS 11
#define INTER_RESIZE_COEF_SCALE (1 << INTER_RESIZE_COEF_BITS)
#define INTER_RESIZE_COEF_BITS2 (INTER_RESIZE_COEF_BITS*2)
void bilinear_resize(U8 *dst, const U8 *src, const U32 in_width, const U32 in_height, const U32 out_width, const U32 out_height, const U32 channels)
{
F32 scale_x = (F32)in_width / out_width,
scale_y = (F32)in_height / out_height;
short cbufy[2], cbufx[2];
S32 in_area = in_width * in_height, in_area2 = in_area * 2;
S32 out_area = out_width * out_height, out_area2 = out_area * 2;
S32 sx, sy;
F32 fy, fx;
U32 index_dst, i, j;
for (j = 0; j < out_height; ++j)
{
fy = (F32)((j + 0.5) * scale_y - 0.5);
sy = (S32)(fy);
if (fy < 0) sy -= 1;
fy -= sy;
if (sy < 0) fy = 0, sy = 0;
if (sy + 1 >= (S32)in_height) fy = 0, sy = (S32)in_height - 1;
cbufy[0] = (S16)((1.f - fy) * INTER_RESIZE_COEF_SCALE + 0.5);
cbufy[1] = INTER_RESIZE_COEF_SCALE - cbufy[0];
for (i = 0; i < out_width; ++i)
{
fx = (F32)((i + 0.5) * scale_x - 0.5);
// cvfloor 不大于原值的整数
sx = (S32)(fx);
if (fx < 0) sx -= 1;
fx -= sx;
if (sx < 0) fx = 0, sx = 0;
if (sx + 1 >= (S32)in_width) fx = 0, sx = (S32)in_width - 1;
cbufx[0] = (S16)((1.f - fx) * INTER_RESIZE_COEF_SCALE + 0.5);
cbufx[1] = INTER_RESIZE_COEF_SCALE - cbufx[0];
index_dst = j * out_width + i;
S32 x00 = src[sy * in_width + sx] * cbufx[0],
x01 = src[sy * in_width + sx + 1] * cbufx[1],
dx = x00 + x01;
S32 y10 = src[(sy + 1) * in_width + sx] * cbufx[0],
y11 = src[(sy + 1) * in_width + sx + 1] * cbufx[1],
dy = y10 + y11;
// 这里分段移位猜测是因为cbufy[0] 为short类型,必须将dx右移4位也变成short类型进行乘积(dx为8+11=19位)
// 特别注意的是源码中使用SSE2加速,这里没有使用
dst[index_dst] = (U8)((((cbufy[0] * (dx >> 4)) >> 16) + ((cbufy[1] * (dy >> 4)) >> 16) + 2) >> 2);
if (channels == 3)
{
x00 = src[sy * in_width + sx + in_area] * cbufx[0];
x01 = src[sy * in_width + sx + 1 + in_area] * cbufx[1];
dx = x00 + x01;
y10 = src[(sy + 1) * in_width + sx + in_area] * cbufx[0];
y11 = src[(sy + 1) * in_width + sx + 1 + in_area] * cbufx[1];
dy = y10 + y11;
dst[index_dst + out_area] = (U8)((((cbufy[0] * (dx >> 4)) >> 16) + ((cbufy[1] * (dy >> 4)) >> 16) + 2) >> 2);
x00 = src[sy * in_width + sx + in_area2] * cbufx[0];
x01 = src[sy * in_width + sx + 1 + in_area2] * cbufx[1];
dx = x00 + x01;
y10 = src[(sy + 1) * in_width + sx + in_area2] * cbufx[0];
y11 = src[(sy + 1) * in_width + sx + 1 + in_area2] * cbufx[1];
dy = y10 + y11;
dst[index_dst + out_area2] = (U8)((((cbufy[0] * (dx >> 4)) >> 16) + ((cbufy[1] * (dy >> 4)) >> 16) + 2) >> 2);
}
}
}
}