OpenCV双线性缩放函数实现

双线性缩放的公式都为人熟知,但是自己实现一遍却没有达到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);
                
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值