重现image resize-双线性插值

Opencv水太深,代码看的头疼。
从库一层一层往下看,发现opencv采用了许多代码加速,例如并行计算,SSE等。这几天做的是resize的线性插值的方法。

SSE(Streaming SIMD Extensions)是Intel在3D Now!发布一年之后,在PIII中引入的指令集,是MMX的超集。AMD后来在Athlon XP中加入了对这个指令集的支持。这个指令集增加了对8个128位寄存器XMM0-XMM7的支持,每个寄存器可以存储4个单精度浮点数。使用这些寄存器的程序必须使用FXSAVE和FXRSTR指令来保持和恢复状态。但是在PIII对SSE的实现中,浮点数寄存器又一次被新的指令集占用了,但是这一次切换运算模式不是必要的了,只是SSE和浮点数指令不能同时进入CPU的处理线而已。
SSE2是Intel在P4的最初版本中引入的,但是AMD后来在Opteron 和Athlon 64中也加入了对它的支持。这个指令集添加了对64位双精度浮点数的支持,以及对整型数据的支持,也就是说这个指令集中所有的MMX指令都是多余的了,同时也避免了占用浮点数寄存器。这个指令集还增加了对CPU的缓存的控制指令。AMD对它的扩展增加了8个XMM寄存器,但是需要切换到64位模式(AMD64)才可以使用这些寄存器。Intel后来在其EM64T架构中也增加了对AMD64的支持。
SSE3是Intel在P4的Prescott版中引入的指令集,AMD在Athlon 64的第五个版本中也添加了对它的支持。这个指令集扩展的指令包含寄存器的局部位之间的运算,例如高位和低位之间的加减运算;浮点数到整数的转换,以及对超线程技术的支持。
双线性内插值算法描述如下:
  对于一个目的像素,设置坐标通过反向变换得到的浮点坐标为(i+u,j+v) (其中i、j均为浮点坐标的整数部分,u、v为浮点坐标的小数部分,是取值[0,1)区间的浮点数),则这个像素得值 f(i+u,j+v) 可由原图像中坐标为 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所对应的周围四个像素的值决定,即:
  f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1) 公式1
其中f(i,j)表示源图像(i,j)处的的像素值,以此类推。
比如,象刚才的例子,现在假如目标图的象素坐标为(1,1),那么反推得到的对应于源图的坐标是(0.75 , 0.75), 这其实只是一个概念上的虚拟象素,实际在源图中并不存在这样一个象素,那么目标图的象素(1,1)的取值不能够由这个虚拟象素来决定,而只能由源图的这四个象素共同决定:(0,0)(0,1)(1,0)(1,1),而由于(0.75,0.75)离(1,1)要更近一些,那么(1,1)所起的决定作用更大一些,这从公式1中的系数uv=0.75×0.75就可以体现出来,而(0.75,0.75)离(0,0)最远,所以(0,0)所起的决定作用就要小一些,公式中系数为(1-u)(1-v)=0.25×0.25也体现出了这一特点。
双线性内插法是利用待求象素四个邻象素的灰度在两个方向上作线性内插,如下图所示:

image.png
对于 (i, j+v),f(i, j) 到 f(i, j+1) 的灰度变化为线性关系,则有:

      f(i, j+v) = [f(i, j+1) - f(i, j)] * v + f(i, j)

同理对于 (i+1, j+v) 则有:

                  f(i+1, j+v) = [f(i+1, j+1) - f(i+1, j)] * v + f(i+1, j)

从f(i, j+v) 到 f(i+1, j+v) 的灰度变化也为线性关系,由此可推导出待求象素灰度的计算式如下:

                  f(i+u, j+v) = (1-u) * (1-v) * f(i, j) + (1-u) * v * f(i, j+1) + u * (1-v) * f(i+1, j) + u * v * f(i+1, j+1)

双线性内插法的计算比最邻近点法复杂,计算量较大,但没有灰度不连续的缺点,结果基本令人满意。它具有低通滤波性质,使高频分量受损,图像轮廓可能会有一点模糊。
基于此原理写代码

/*********************Resize_INTER_LINEAR******************************************/
//功能:利用线性插值修改图像尺寸
//参数:原图像,目标图像,目标长,目标宽
void mcv::Resize_INTER_LINEAR(mcv::Image &src, mcv::Image &dst, unsigned int width, unsigned int heigth)
{
    matDst1 = Mat(Size(width, heigth), matSrc.type(), Scalar::all(0));//为了显示使用opencv,Size(width,heigth)
    dst.height = matDst1.rows;
    dst.width = matDst1.cols;
    dst.channel = matDst1.channels();
    dst.data = matDst1.data;

    //缩放倍数
    double scale_x = (double)src.width / dst.width;
    double scale_y = (double)src.height / dst.height;

    for (int j = 0; j < dst.height; ++j)//f( sy+fy )
    {
        float fy = (float)((j + 0.5) * scale_y - 0.5);
        int sy = floor(fy);//整数部分
        fy -= sy;//小数部分
        sy = min(sy, src.height - 2);//因为有小数部分所以-2
        sy = max(0, sy);

        //由于cbufx[0]和cbufy[1]都放大了2048(2^11)倍,这里右移是为了还原,
        //这样先乘后右移的操作在图像处理中很常见,主要是为了简化计算
        short cbufy[2];
        if ((1.f - fy) * 2048>32767)
            cbufy[0] = 32767;
        else if ((1.f - fy) * 2048 < -32768)
            cbufy[0] = -32768;
        else
            cbufy[0] = (1.f - fy) * 2048;
        //cbufy[0] = cv::saturate_cast<short>((1.f - fy)*2048);
        cbufy[1] = 2048 - cbufy[0];
        //float cbufy[2];
        //cbufy[0] = cv::saturate_cast<float>((1.f - fy));
        //cbufy[1] = 1 - cbufy[0];
        for (int i = 0; i < dst.width; ++i)
        {
            float fx = (float)((i + 0.5) * scale_x - 0.5);
            int sx = floor(fx);
            fx -= sx;

            if (sx < 0) {
                fx = 0, sx = 0;
            }
            if (sx >= src.width - 1) {
                fx = 0, sx = src.width - 2;
            }

            //float cbufx[2];
            //cbufx[0] = cv::saturate_cast<float>((1.f - fx));
            //cbufx[1] = 1 - cbufx[0];
            short cbufx[2];
            if ((1.f - fx) * 2048>32767)
                cbufx[0] = 32767;
            else if ((1.f - fx) * 2048 < -32768)
                cbufx[0] = -32768;
            else
                cbufx[0] = (1.f - fx) * 2048;
            //cbufx[0] = cv::saturate_cast<short>((1.f - fx) * 2048);
            cbufx[1] = 2048 - cbufx[0];

            for (int k = 0; k < src.channel; ++k)
            {
                *(dst.data + j*dst.width*dst.channel + dst.channel * i + k) = (*(src.data + sy*src.width*src.channel + src.channel * sx + k) * cbufx[0] * cbufy[0] +
                    *(src.data + (sy + 1)*src.width*src.channel + src.channel * sx + k) * cbufx[0] * cbufy[1] +
                    *(src.data + sy*src.width*src.channel + src.channel * (sx + 1) + k) * cbufx[1] * cbufy[0] +
                    *(src.data + (sy + 1)*src.width*src.channel + src.channel * (sx + 1) + k) * cbufx[1] * cbufy[1]) >> 22;
            }
        }
    }
}

这样能够实现图像尺寸的线性插值

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容