• 最近在做视频拼接的项目,里面用到了图像的单应矩阵变换,在最后的图像重映射,由于目标图像的坐标是非整数的,所以需要用到插值的方法,用的就是双线性插值,下面的博文主要是查看了前辈的博客对双线性插值算法...

    最近在做视频拼接的项目,里面用到了图像的单应性矩阵变换,在最后的图像重映射,由于目标图像的坐标是非整数的,所以需要用到插值的方法,用的就是双线性插值,下面的博文主要是查看了前辈的博客对双线性插值算法原理进行了一个总结,在这里也感谢一些大牛的博文。

    http://www.cnblogs.com/linkr/p/3630902.html

    http://www.cnblogs.com/funny-world/p/3162003.html

    双线性插值

          假设源图像大小为mxn,目标图像为axb。那么两幅图像的边长比分别为:m/a和n/b。注意,通常这个比例不是整数,编程存储的时候要用浮点型。目标图像的第(i,j)个像素点(i行j列)可以通过边长比对应回源图像。其对应坐标为(i*m/a,j*n/b)。显然,这个对应坐标一般来说不是整数,而非整数的坐标是无法在图像这种离散数据上使用的。双线性插值通过寻找距离这个对应坐标最近的四个像素点,来计算该点的值(灰度值或者RGB值)。

      若图像为灰度图像,那么(i,j)点的灰度值的数学计算模型是:

    f(x,y)=b1+b2x+b3y+b4xy

    其中b1,b2,b3,b4是相关的系数。关于其的计算过程如下如下:

          如图,已知Q12,Q22,Q11,Q21,但是要插值的点为P点,这就要用双线性插值了,首先在x轴方向上,对R1和R2两个点进行插值,这个很简单,然后根据R1和R2对P点进行插值,这就是所谓的双线性插值。

    clip_image001

     

    附:维基百科--双线性插值:

          双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。

    假如我们想得到未知函数 f 在点 P=\left( x, y\right) 的值,假设我们已知函数 f 在 Q_{11} = \left( x_1, y_1 \right)Q_{12} = \left( x_1, y_2 \right)Q_{21} = \left( x_2, y_1 \right), 及 Q_{22} = \left( x_2, y_2 \right) 四个点的值。

    首先在 x 方向进行线性插值,得到

    f(R_1) \approx \frac{x_2-x}{x_2-x_1} f(Q_{11}) + \frac{x-x_1}{x_2-x_1} f(Q_{21}) \quad\mbox{Where}\quad R_1 = (x,y_1),
    f(R_2) \approx \frac{x_2-x}{x_2-x_1} f(Q_{12}) + \frac{x-x_1}{x_2-x_1} f(Q_{22}) \quad\mbox{Where}\quad R_2 = (x,y_2).

    然后在 y 方向进行线性插值,得到

    f(P) \approx \frac{y_2-y}{y_2-y_1} f(R_1) + \frac{y-y_1}{y_2-y_1} f(R_2).

    这样就得到所要的结果 f \left( x, y \right),

    f(x,y) \approx \frac{f(Q_{11})}{(x_2-x_1)(y_2-y_1)} (x_2-x)(y_2-y) + \frac{f(Q_{21})}{(x_2-x_1)(y_2-y_1)} (x-x_1)(y_2-y)
    + \frac{f(Q_{12})}{(x_2-x_1)(y_2-y_1)} (x_2-x)(y-y_1) + \frac{f(Q_{22})}{(x_2-x_1)(y_2-y_1)} (x-x_1)(y-y_1).

    如果选择一个坐标系统使得 f 的四个已知点坐标分别为 (0, 0)、(0, 1)、(1, 0) 和 (1, 1),那么插值公式就可以化简为

    f(x,y) \approx f(0,0) \, (1-x)(1-y) + f(1,0) \, x(1-y) + f(0,1) \, (1-x)y + f(1,1) xy.

    或者用矩阵运算表示为

    f(x,y) \approx \begin{bmatrix}1-x & x \end{bmatrix} \begin{bmatrix}f(0,0) & f(0,1) \\f(1,0) & f(1,1) \end{bmatrix} \begin{bmatrix}1-y \\y \end{bmatrix}

    这种插值方法的结果通常不是线性的,线性插值的结果与插值的顺序无关。首先进行 y 方向的插值,然后进行 x 方向的插值,所得到的结果是一样的。

    opencv和Matlab中的双线性插值

       这部分的前提是,你已经明白什么是双线性插值并且在给定源图像和目标图像尺寸的情况下,可以用笔计算出目标图像某个像素点的值。当然,最好的情况是你已经用某种语言实现了网上一大堆博客上原创或转载的双线性插值算法,然后发现计算出来的结果和matlab、openCV对应的resize()函数得到的结果完全不一样。

    那这个究竟是怎么回事呢?

    其实答案很简单,就是坐标系的选择问题,或者说源图像和目标图像之间的对应问题。

    按照网上一些博客上写的,源图像和目标图像的原点(0,0)均选择左上角,然后根据插值公式计算目标图像每点像素,假设你需要将一幅5x5的图像缩小成3x3,那么源图像和目标图像各个像素之间的对应关系如下:

    只画了一行,用做示意,从图中可以很明显的看到,如果选择右上角为原点(0,0),那么最右边和最下边的像素实际上并没有参与计算,而且目标图像的每个像素点计算出的灰度值也相对于源图像偏左偏上。

    那么,让坐标加1或者选择右下角为原点怎么样呢?很不幸,还是一样的效果,不过这次得到的图像将偏右偏下。

    最好的方法就是,两个图像的几何中心重合,并且目标图像的每个像素之间都是等间隔的,并且都和两边有一定的边距,这也是matlab和openCV的做法。如下图:

    如果你不懂我上面说的什么,没关系,只要在计算对应坐标的时候改为以下公式即可,

     

    int x=(i+0.5)*m/a-0.5

    int y=(j+0.5)*n/b-0.5

    代替

    int x=i*m/a

    int y=j*n/b

     

    利用上述公式,将得到正确的双线性插值结果


    原文地址:http://blog.csdn.net/xjz18298268521/article/details/51220576

    展开全文
  • 1、线性插值的解释 ...1.双线性插值 2.存在的问题 3、又是另一位讲的通俗易懂 1,原理 2,计算方法 3,加速以及优化策略 3.1 源图像和目标图像几何中心的对齐  3.2 将浮点运算转换成整数运算 4,代码 ...

    1、线性插值的解释

    双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。一下来自于[1]

    单线性插值法

    已知数据 (x0, y0) 与 (x1, y1),要计算 [x0, x1] 区间内某一位置 x 在直线上的y值。
    这里写图片描述
    这里写图片描述
    上面比较好理解吧,仔细看就是用x和x0,x1的距离作为一个权重,用于y0和y1的加权。双线性插值本质上就是在两个方向上做线性插值。

    双线性插值法

    在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值[1]。见下图:
    这里写图片描述

    假如我们想得到未知函数 f 在点 P = (x, y) 的值,假设我们已知函数 f 在 Q11 = (x1, y1)、Q12 = (x1, y2), Q21 = (x2, y1) 以及 Q22 = (x2, y2) 四个点的值。最常见的情况,f就是一个像素点的像素值。首先在 x 方向进行线性插值,得到
    这里写图片描述
    然后在 y 方向进行线性插值,得到
    这里写图片描述
    综合起来就是双线性插值最后的结果:
    这里写图片描述

    由于图像双线性插值只会用相邻的4个点,因此上述公式的分母都是1。opencv中的源码如下,用了一些优化手段,比如用整数计算代替float(下面代码中的*2048就是变11位小数为整数,最后有两个连乘,因此>>22位),以及源图像和目标图像几何中心的对齐
    - SrcX=(dstX+0.5)* (srcWidth/dstWidth) -0.5
    - SrcY=(dstY+0.5) * (srcHeight/dstHeight)-0.5
    这个要重点说一下,源图像和目标图像的原点(0,0)均选择左上角,然后根据插值公式计算目标图像每点像素,假设你需要将一幅5x5的图像缩小成3x3,那么源图像和目标图像各个像素之间的对应关系如下。如果没有这个中心对齐,根据基本公式去算,就会得到左边这样的结果;而用了对齐,就会得到右边的结果:
    这里写图片描述

    2、另一位牛人讲的比较易懂

    这博文来自文件[2]

    1.双线性插值

    假设源图像大小为mxn,目标图像为axb。那么两幅图像的边长比分别为:m/a和n/b。注意,通常这个比例不是整数,编程存储的时候要用浮点型。目标图像的第(i,j)个像素点(i行j列)可以通过边长比对应回源图像。其对应坐标为(i*m/a,j*n/b)。
    显然,这个对应坐标一般来说不是整数,而非整数的坐标是无法在图像这种离散数据上使用的。双线性插值通过寻找距离这个对应坐标最近的四个像素点,来计算该点的值(灰度值或者RGB值)。如果你的对应坐标是(2.5,4.5),那么最近的四个像素是(2,4)、(2,5)、(3,4),(3,5)。
    若图像为灰度图像,那么(i,j)点的灰度值可以通过一下公式计算:
    f(i,j)=w1*p1+w2*p2+w3*p3+w4*p4;
    其中,pi(i=1,2,3,4)为最近的四个像素点,wi(i=1,2,3,4)为各点相应权值。关于权值的计算,在维基百科和百度百科上写的很明白。

    2.存在的问题

    这部分的前提是,你已经明白什么是双线性插值并且在给定源图像和目标图像尺寸的情况下,可以用笔计算出目标图像某个像素点的值。当然,最好的情况是你已经用某种语言实现了网上一大堆博客上原创或转载的双线性插值算法,然后发现计算出来的结果和matlab、openCV对应的resize()函数得到的结果完全不一样。
    那这个究竟是怎么回事呢?
    其实答案很简单,就是坐标系的选择问题,或者说源图像和目标图像之间的对应问题。
    按照网上一些博客上写的,源图像和目标图像的原点(0,0)均选择左上角,然后根据插值公式计算目标图像每点像素,假设你需要将一幅5x5的图像缩小成3x3,那么源图像和目标图像各个像素之间的对应关系如下:
    这里写图片描述
    只画了一行,用做示意,从图中可以很明显的看到,如果选择右上角为原点(0,0),那么最右边和最下边的像素实际上并没有参与计算,而且目标图像的每个像素点计算出的灰度值也相对于源图像偏左偏上。
    那么,让坐标加1或者选择右下角为原点怎么样呢?很不幸,还是一样的效果,不过这次得到的图像将偏右偏下。
    最好的方法就是,两个图像的几何中心重合,并且目标图像的每个像素之间都是等间隔的,并且都和两边有一定的边距,这也是matlab和openCV的做法。如下图:
    这里写图片描述
    如果你不懂我上面说的什么,没关系,只要在计算对应坐标的时候改为以下公式即可,

    int x=(i+0.5)*m/a-0.5
    int y=(j+0.5)*n/b-0.5

    instead of

    int x=i*m/a
    int y=j*n/b

    利用上述公式,将得到正确的双线性插值结果

    3、又是另一位讲的通俗易懂

    这位大神讲openCV ——双线性插值(Bilinear interpolation)[3]

    1,原理

      在图像的仿射变换中,很多地方需要用到插值运算,常见的插值运算包括最邻近插值,双线性插值,双三次插值,兰索思插值等方法,OpenCV提供了很多方法,其中,双线性插值由于折中的插值效果和运算速度,运用比较广泛。
      越是简单的模型越适合用来举例子,我们就举个简单的图像:3*3 的256级灰度图。假如图像的象素矩阵如下图所示(这个原始图把它叫做源图,Source):
    234 38 22
    67 44 12
    89 65 63
      这 个矩阵中,元素坐标(x,y)是这样确定的,x从左到右,从0开始,y从上到下,也是从零开始,这是图象处理中最常用的坐标系。
      如果想把这副图放大为 4*4大小的图像,那么该怎么做呢?那么第一步肯定想到的是先把4*4的矩阵先画出来再说,好了矩阵画出来了,如下所示,当然,矩阵的每个像素都是未知数,等待着我们去填充(这个将要被填充的图的叫做目标图,Destination):
      ? ? ? ?
      ? ? ? ?
      ? ? ? ?
      ? ? ? ?
      然后要往这个空的矩阵里面填值了,要填的值从哪里来来呢?是从源图中来,好,先填写目标图最左上角的象素,坐标为(0,0),那么该坐标对应源图中的坐标可以由如下公式得出srcX=dstX* (srcWidth/dstWidth) , srcY = dstY * (srcHeight/dstHeight)
      好了,套用公式,就可以找到对应的原图的坐标了(0*(3/4),0*(3/4))=>(0*0.75,0*0.75)=>(0,0),找到了源图的对应坐标,就可以把源图中坐标为(0,0)处的234象素值填进去目标图的(0,0)这个位置了。
      接下来,如法炮制,寻找目标图中坐标为(1,0)的象素对应源图中的坐标,套用公式:
    (1*0.75,0*0.75)=>(0.75,0) 结果发现,得到的坐标里面竟然有小数,这可怎么办?计算机里的图像可是数字图像,象素就是最小单位了,象素的坐标都是整数,从来没有小数坐标。这时候采用的一种策略就是采用四舍五入的方法(也可以采用直接舍掉小数位的方法),把非整数坐标转换成整数,好,那么按照四舍五入的方法就得到坐标(1,0),完整的运算过程就是这样的:(1*0.75,0*0.75)=>(0.75,0)=>(1,0) 那么就可以再填一个象素到目标矩阵中了,同样是把源图中坐标为(1,0)处的像素值38填入目标图中的坐标。
      依次填完每个象素,一幅放大后的图像就诞生了,像素矩阵如下所示:
      234 38 22 22
      67 44 12 12
      89 65 63 63
      89 65 63 63
      这种放大图像的方法叫做最临近插值算法,这是一种最基本、最简单的图像缩放算法,效果也是最不好的,放大后的图像有很严重的马赛克,缩小后的图像有很严重的失真;效果不好的根源就是其简单的最临近插值方法引入了严重的图像失真,比如,当由目标图的坐标反推得到的源图的的坐标是一个浮点数的时候,采用了四舍五入的方法,直接采用了和这个浮点数最接近的象素的值,这种方法是很不科学的,当推得坐标值为 0.75的时候,不应该就简单的取为1,既然是0.75,比1要小0.25 ,比0要大0.75 ,那么目标象素值其实应该根据这个源图中虚拟的点四周的四个真实的点来按照一定的规律计算出来的,这样才能达到更好的缩放效果。
      双线型内插值算法就是一种比较好的图像缩放算法,它充分的利用了源图中虚拟点四周的四个真实存在的像素值来共同决定目标图中的一个像素值,因此缩放效果比简单的最邻近插值要好很多。
    双线性内插值算法描述如下:
      对于一个目的像素,设置坐标通过反向变换得到的浮点坐标为(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)
    其中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也体现出了这一特点。

    2,计算方法

    这里写图片描述
     首先,在X方向上进行两次线性插值计算,然后在Y方向上进行一次插值计算。
     这里写图片描述
    在图像处理的时候,我们先根据
      srcX=dstX* (srcWidth/dstWidth),
      srcY = dstY * (srcHeight/dstHeight)
    来计算目标像素在源图像中的位置,这里计算的srcX和srcY一般都是浮点数,比如f(1.2, 3.4)这个像素点是虚拟存在的,先找到与它临近的四个实际存在的像素点
      (1,3) (2,3)
      (1,4) (2,4)
    写成f(i+u,j+v)的形式,则u=0.2,v=0.4, i=1, j=3
    在沿着X方向差插值时,f(R1)=u(f(Q21)-f(Q11))+f(Q11)
    沿着Y方向同理计算。
    或者,直接整理一步计算,
    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)

    3,加速以及优化策略

     单纯按照上文实现的插值算法只能勉强完成插值的功能,速度和效果都不会理想,在具体代码实现的时候有些小技巧。参考OpenCV源码以及网上博客整理如下两点:
    源图像和目标图像几何中心的对齐
    将浮点运算转换成整数运算

    3.1 源图像和目标图像几何中心的对齐  

     方法:在计算源图像的虚拟浮点坐标的时候,一般情况:
      srcX=dstX* (srcWidth/dstWidth) ,
      srcY = dstY * (srcHeight/dstHeight)
      中心对齐(OpenCV也是如此):
      SrcX=(dstX+0.5)* (srcWidth/dstWidth) -0.5
      SrcY=(dstY+0.5) * (srcHeight/dstHeight)-0.5
      原理:
    双线性插值算法及需要注意事项这篇博客解释说“如果选择右上角为原点(0,0),那么最右边和最下边的像素实际上并没有参与计算,而且目标图像的每个像素点计算出的灰度值也相对于源图像偏左偏上。”我有点保持疑问。
      将公式变形:
      srcX=dstX* (srcWidth/dstWidth)+0.5*(srcWidth/dstWidth-1)
      相当于我们在原始的浮点坐标上加上了0.5*(srcWidth/dstWidth-1)这样一个控制因子,这项的符号可正可负,与srcWidth/dstWidth的比值也就是当前插值是扩大还是缩小图像有关,有什么作用呢?看一个例子:假设源图像是3*3,中心点坐标(1,1)目标图像是9*9,中心点坐标(4,4),我们在进行插值映射的时候,尽可能希望均匀的用到源图像的像素信息,最直观的就是(4,4)映射到(1,1)现在直接计算srcX=4*3/9=1.3333!=1,也就是我们在插值的时候所利用的像素集中在图像的右下方,而不是均匀分布整个图像。现在考虑中心点对齐,srcX=(4+0.5)*3/9-0.5=1,刚好满足我们的要求。

    3.2 将浮点运算转换成整数运算

      参考图像处理界双线性插值算法的优化
      直接进行计算的话,由于计算的srcX和srcY 都是浮点数,后续会进行大量的乘法,而图像数据量又大,速度不会理想,解决思路是:
      浮点运算→→整数运算→→”<<左右移按位运算”
      放大的主要对象是u,v这些浮点数,OpenCV选择的放大倍数是2048“如何取这个合适的放大倍数呢,要从三个方面考虑,
      第一:精度问题,如果这个数取得过小,那么经过计算后可能会导致结果出现较大的误差。
      第二,这个数不能太大,太大会导致计算过程超过长整形所能表达的范围。
      第三:速度考虑。假如放大倍数取为12,那么算式在最后的结果中应该需要除以12*12=144,但是如果取为16,则最后的除数为16*16=256,这个数字好,我们可以用右移来实现,而右移要比普通的整除快多了。”我们利用左移11位操作就可以达到放大目的。

    4,代码

    uchar* dataDst = matDst1.data;
        int stepDst = matDst1.step;
        uchar* dataSrc = matSrc.data;
        int stepSrc = matSrc.step;
        int iWidthSrc = matSrc.cols;
        int iHiehgtSrc = matSrc.rows;
    
        for (int j = 0; j < matDst1.rows; ++j)
        {
            float fy = (float)((j + 0.5) * scale_y - 0.5);
            int sy = cvFloor(fy);
            fy -= sy;
            sy = std::min(sy, iHiehgtSrc - 2);
            sy = std::max(0, sy);
    
            short cbufy[2];
            cbufy[0] = cv::saturate_cast<short>((1.f - fy) * 2048);
            cbufy[1] = 2048 - cbufy[0];
    
            for (int i = 0; i < matDst1.cols; ++i)
            {
                float fx = (float)((i + 0.5) * scale_x - 0.5);
                int sx = cvFloor(fx);
                fx -= sx;
    
                if (sx < 0) {
                    fx = 0, sx = 0;
                }
                if (sx >= iWidthSrc - 1) {
                    fx = 0, sx = iWidthSrc - 2;
                }
    
                short cbufx[2];
                cbufx[0] = cv::saturate_cast<short>((1.f - fx) * 2048);
                cbufx[1] = 2048 - cbufx[0];
    
                for (int k = 0; k < matSrc.channels(); ++k)
                {
                    *(dataDst+ j*stepDst + 3*i + k) = (*(dataSrc + sy*stepSrc + 3*sx + k) * cbufx[0] * cbufy[0] + 
                        *(dataSrc + (sy+1)*stepSrc + 3*sx + k) * cbufx[0] * cbufy[1] + 
                        *(dataSrc + sy*stepSrc + 3*(sx+1) + k) * cbufx[1] * cbufy[0] + 
                        *(dataSrc + (sy+1)*stepSrc + 3*(sx+1) + k) * cbufx[1] * cbufy[1]) >> 22;
                }
            }
        }
        cv::imwrite("linear_1.jpg", matDst1);
    
        cv::resize(matSrc, matDst2, matDst1.size(), 0, 0, 1);
        cv::imwrite("linear_2.jpg", matDst2);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    参考文件:
    [1]三十分钟理解:线性插值,双线性插值Bilinear Interpolation算法
    [2] 双线性插值算法及需要注意事项
    [3]OpenCV ——双线性插值(Bilinear interpolation)
    [4]FPGA双线性插值算法(图像的缩放



    转载自:https://blog.csdn.net/lovexlsforever/article/details/79508602

    展开全文
  • 数字图像处理--放大和收缩数字图像(双线性插值法)前言放大和收缩数字图像步骤计算新的像素在原图的对应位置为这些对应位置赋灰度值最近邻法赋灰度值双线性插值法赋灰度值一维线性插值双线性插值代码最近邻法 ...

    前言

    大家好!在学习数字图像处理这门课程中,进行到了放大和收缩数字图像环节。当然处理的方法有很多(最近邻法、双线性插值法、更多邻点插值法),小编重点介绍双线性插值法,并结合最近邻法的效果进行对比分析。

    放大和收缩数字图像步骤

    1.计算新的像素在原图的对应位置。
    2.为这些对应位置赋灰度值。

    计算新的像素在原图的对应位置

    f(x,y)表示输出图像,g(u,v)表示输入图像。图像放大或缩小的几何运算可定义为:
    在这里插入图片描述
    如果令:u0=a(x,y)=x/c;v0=b(x,y)=y/d。那么会使图像在x轴方向放大c倍,在y轴方向放大d倍。
    例如: 将一副200×200的图像g(u,v)放大1.5倍,那么将得到300×300的新图像f(x,y)。产生新图像的过程,实际就是为300×300的像素赋值的过程。
    假如为f(150,150)赋值:
    f(150,150)=g(150/1.5,150/1.5)=g(100,100);
    假如为f(100,100)赋值:
    f(100,100)=g(100/1.5,100/1.5)=g(66.7,66.7).

    为这些对应位置赋灰度值

    对于g(100,100),坐标值为整数,可直接赋值。而对于g(66.7,66.7),坐标值为小数,也即(u0,v0)不一定在坐标点上,这种情况就需要用到前言中所提到的方法(最近邻法、双线性插值法、更多邻点插值法),当然这里只介绍前两种,我们分别来看。

    最近邻法赋灰度值

    这种方法比较简单,取点(u0,v0)最近的整数坐标(u,v)赋值即可,不过这种方法做出来的图效果不是很好,模糊成都较高。稍后我们结合代码和效果图具体分析。

    双线性插值法赋灰度值

    根据4个邻点的灰度值通过插值计算g(u0,v0)。

    一维线性插值

    假设我们已知坐标(x0,y0)与(x1,y1),要得到[x0,x1]区间内某一位置x在直线上的值。根据图中所示可得:
    Alt
    在这里插入图片描述
    如果令:
    在这里插入图片描述
    则:
    在这里插入图片描述

    双线性插值

    Alt
    如图所示:所谓双线性插值,也就是连续使用三次一维线性插值,最终求得g(u0,v0)。
    第一次:由g(u’,v’)和g(u’+1,v’)一维线性插值求g(u0,v’).
    第二次:由g(u’,v’+1)和g(u’+1,v’+1)一维线性插值求g(u0,v’+1).
    第三次:由g(u0,v’)和g(u0,v’+1)一维线性插值求g(u0,v0).
    计算过程
    Alt

    代码

    最近邻法

    MATLAB程序,读入灰度图1.jpg,缩放倍数为n。为了之后作对比,这里取n=2。

    %采用最近邻插值对图像进行缩放处理
    %参数n表示缩放倍数
    function []=scale1(n)
    ima=imread('F:\1.jpg');
    imshow(ima);
    title('输入图像');
    n=2;
    ima=double(ima);
    swh=size(ima); %得到原图像的宽高
    sw=swh(:,2); %得到原图像的宽
    sh=swh(:,1); %得到原图像的高
    dw=ceil(sw * n); %得到
    dh=ceil(sh * n);
    resIma=zeros(dh,dw);
    for i=1:dh
        for j=1:dw
            tx=round(i/n); %缩放后的图像坐标在原图像处的位置
            ty=round(j/n);
            
            if(tx < 1) %如果越界,则进行调整
                tx = 1;
            end
            if(tx > sh)
                tx = swh;
            end
            if(ty < 1)
                ty = 1;
            end
            if(ty > sw)
                ty = sw;
            end
            resIma(i,j)=ima(tx,ty); %将缩放后的图像坐标在原图像处的位置的灰度值赋值给缩放后的图像
        end
    end
    
    resIma=uint8(resIma);
    figure;
    imshow(resIma);
    title('输出图像');
    imwrite(resIma,'F:\z.jpg');%保存缩放后的图片
    end
    

    双线性插值

    MATLAB程序,读入灰度图1.jpg,缩放倍数为n。为了之后作对比,这里取n=2。

    %采用双线性内插值对图像进行缩放处理
    %参数n表示缩放的倍数
    function []=scale2(n)
    ima=imread('F:\1.jpg'); %读取原图像
    ima=double(ima); %二维矩阵转为双精度类型
    swh=size(ima); %获取原图像的宽高
    sh=swh(:,1); %获取原图像的高
    sw=swh(:,2); %获取原图像的宽
     
    %"加墙"
    ima2=zeros(sh+2,sw+2);
    ima2(1,2:sw+1)=ima(1,:); %原图像上边加墙,灰度值与边界一致
    ima2(sh+2,2:sw+1)=ima(sh,:); %原图像下边加墙,灰度值与边界一致
    ima2(2:sh+1,2:sw+1)=ima; %将原图像赋值给中心部分
    ima2(:,1)=ima2(:,2); %原图像左边加墙,灰度值与边界一致
    ima2(:,sw+2)=ima2(:,sw+1); %原图像右边加墙,灰度值与边界一致
    n=2;
    dw=sw*n; %计算缩放后的图像的宽
    dh=sh*n; %计算缩放后的图像的高
     
    dw1=round((sw+2)*n); %计算加墙后缩放的图像的宽
    dh1=round((sh+2)*n); %计算加墙后缩放的图像的高
     
    resIma1=zeros(dh1,dw1); %创建原图像的矩阵
     
    %从不是“墙”的位置开始计算缩放后的图像的各点灰度值
    %考虑缩小图像时,输入的缩放倍数是小数,需进行取整
    start=round(n+1);
    endI=round(dh+n);
    endJ=round(dw+n);
     
    for i=start:endI
        for j=start:endJ
            tx=i/n; %缩放后的图像坐标在原图像处的位置
            ty=j/n;
            tdx=tx-floor(tx); %得到小数坐标
            tdy=ty-floor(ty);
            %确定临近四个角的坐标
            %Q11点
            Q11x=tx-tdx;
            Q11y=ty-tdy;
            %Q12点
            Q12x=tx-tdx;
            Q12y=Q11y+1;
            %Q21点
            Q21x=Q11x+1;
            Q21y=Q11y;
            %Q22点
            Q22x=Q11x+1;
            Q22y=Q11y+1;
            %根据双线性内插算法,算出缩放后的图像在(i,j)点处的灰度值
            resIma1(i,j)=tdx*tdy*ima2(Q11x,Q11y)+(1-tdx)*tdy*ima2(Q12x,Q12y)+tdx*(1-tdy)*ima2(Q21x,Q21y)+(1-tdy)*(1-tdx)*ima2(Q22x,Q22y);
        end
    end
    resIma=resIma1(n+1:dh+n,n+1:dw+n); %截取除墙外的中心部分
    resIma=uint8(resIma);
    imshow(resIma); %显示缩放后的图像
    imwrite(resIma,'F:\s.jpg');%保存缩放后的图片
    end
    

    实验结果及对比分析

    原图(为了吸引更多人关注此博客,小编特意奉上易炀千玺图片镇楼):
    在这里插入图片描述
    最近邻法
    在这里插入图片描述
    双线性插值
    在这里插入图片描述
    对比以上两种方法呈现出来的结果,双线性插值法要好一些。大家可以用程序实验一下自己的图片,有什么问题及时与我交流。需要注意的是,以上理论只针对灰度图,暂不能运行RGB图片,但原理相通,大家可以继续探索。

    后记

    特别感谢电子科技大学-李庆嵘老师精彩的数字图像处理课程,以下为大家提供课程链接。
    本文代码是改编而来,以下为大家提供原代码链接。
    课程链接:https://www.bilibili.com/video/av22153795?from=search&seid=7751552597133815606
    原代码链接:https://blog.csdn.net/qq_15096707/article/details/49227449

    展开全文
  • 单线性插值法 双线性插值法 2、另一位牛人讲的比较易懂 1.双线性插值 2.存在的问题 3、又是另一位讲的通俗易懂 1,原理 2,计算方法 3,加速以及优化策略 3.1 源图像和目标图像几何中心的对齐  3.2 将浮点运算...

    1、线性插值的解释

    双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。一下来自于[1]

    单线性插值法

    已知数据 (x0, y0) 与 (x1, y1),要计算 [x0, x1] 区间内某一位置 x 在直线上的y值。
    这里写图片描述
    这里写图片描述
    上面比较好理解吧,仔细看就是用x和x0,x1的距离作为一个权重,用于y0和y1的加权。双线性插值本质上就是在两个方向上做线性插值。

    双线性插值法

    在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值[1]。见下图:
    这里写图片描述

    假如我们想得到未知函数 f 在点 P = (x, y) 的值,假设我们已知函数 f 在 Q11 = (x1, y1)、Q12 = (x1, y2), Q21 = (x2, y1) 以及 Q22 = (x2, y2) 四个点的值。最常见的情况,f就是一个像素点的像素值。首先在 x 方向进行线性插值,得到
    这里写图片描述
    然后在 y 方向进行线性插值,得到
    这里写图片描述
    综合起来就是双线性插值最后的结果:
    这里写图片描述

    由于图像双线性插值只会用相邻的4个点,因此上述公式的分母都是1。opencv中的源码如下,用了一些优化手段,比如用整数计算代替float(下面代码中的*2048就是变11位小数为整数,最后有两个连乘,因此>>22位),以及源图像和目标图像几何中心的对齐
    - SrcX=(dstX+0.5)* (srcWidth/dstWidth) -0.5
    - SrcY=(dstY+0.5) * (srcHeight/dstHeight)-0.5
    这个要重点说一下,源图像和目标图像的原点(0,0)均选择左上角,然后根据插值公式计算目标图像每点像素,假设你需要将一幅5x5的图像缩小成3x3,那么源图像和目标图像各个像素之间的对应关系如下。如果没有这个中心对齐,根据基本公式去算,就会得到左边这样的结果;而用了对齐,就会得到右边的结果:
    这里写图片描述

    2、另一位牛人讲的比较易懂

    这博文来自文件[2]

    1.双线性插值

    假设源图像大小为mxn,目标图像为axb。那么两幅图像的边长比分别为:m/a和n/b。注意,通常这个比例不是整数,编程存储的时候要用浮点型。目标图像的第(i,j)个像素点(i行j列)可以通过边长比对应回源图像。其对应坐标为(i*m/a,j*n/b)。
    显然,这个对应坐标一般来说不是整数,而非整数的坐标是无法在图像这种离散数据上使用的。双线性插值通过寻找距离这个对应坐标最近的四个像素点,来计算该点的值(灰度值或者RGB值)。如果你的对应坐标是(2.5,4.5),那么最近的四个像素是(2,4)、(2,5)、(3,4),(3,5)。
    若图像为灰度图像,那么(i,j)点的灰度值可以通过一下公式计算:
    f(i,j)=w1*p1+w2*p2+w3*p3+w4*p4;
    其中,pi(i=1,2,3,4)为最近的四个像素点,wi(i=1,2,3,4)为各点相应权值。关于权值的计算,在维基百科和百度百科上写的很明白。

    2.存在的问题

    这部分的前提是,你已经明白什么是双线性插值并且在给定源图像和目标图像尺寸的情况下,可以用笔计算出目标图像某个像素点的值。当然,最好的情况是你已经用某种语言实现了网上一大堆博客上原创或转载的双线性插值算法,然后发现计算出来的结果和matlab、openCV对应的resize()函数得到的结果完全不一样。
    那这个究竟是怎么回事呢?
    其实答案很简单,就是坐标系的选择问题,或者说源图像和目标图像之间的对应问题。
    按照网上一些博客上写的,源图像和目标图像的原点(0,0)均选择左上角,然后根据插值公式计算目标图像每点像素,假设你需要将一幅5x5的图像缩小成3x3,那么源图像和目标图像各个像素之间的对应关系如下:
    这里写图片描述
    只画了一行,用做示意,从图中可以很明显的看到,如果选择右上角为原点(0,0),那么最右边和最下边的像素实际上并没有参与计算,而且目标图像的每个像素点计算出的灰度值也相对于源图像偏左偏上。
    那么,让坐标加1或者选择右下角为原点怎么样呢?很不幸,还是一样的效果,不过这次得到的图像将偏右偏下。
    最好的方法就是,两个图像的几何中心重合,并且目标图像的每个像素之间都是等间隔的,并且都和两边有一定的边距,这也是matlab和openCV的做法。如下图:
    这里写图片描述
    如果你不懂我上面说的什么,没关系,只要在计算对应坐标的时候改为以下公式即可,

    int x=(i+0.5)*m/a-0.5
    int y=(j+0.5)*n/b-0.5

    instead of

    int x=i*m/a
    int y=j*n/b

    利用上述公式,将得到正确的双线性插值结果

    3、又是另一位讲的通俗易懂

    这位大神讲openCV ——双线性插值(Bilinear interpolation)[3]

    1,原理

      在图像的仿射变换中,很多地方需要用到插值运算,常见的插值运算包括最邻近插值,双线性插值,双三次插值,兰索思插值等方法,OpenCV提供了很多方法,其中,双线性插值由于折中的插值效果和运算速度,运用比较广泛。
      越是简单的模型越适合用来举例子,我们就举个简单的图像:3*3 的256级灰度图。假如图像的象素矩阵如下图所示(这个原始图把它叫做源图,Source):
    234 38 22
    67 44 12
    89 65 63
      这 个矩阵中,元素坐标(x,y)是这样确定的,x从左到右,从0开始,y从上到下,也是从零开始,这是图象处理中最常用的坐标系。
      如果想把这副图放大为 4*4大小的图像,那么该怎么做呢?那么第一步肯定想到的是先把4*4的矩阵先画出来再说,好了矩阵画出来了,如下所示,当然,矩阵的每个像素都是未知数,等待着我们去填充(这个将要被填充的图的叫做目标图,Destination):
      ? ? ? ?
      ? ? ? ?
      ? ? ? ?
      ? ? ? ?
      然后要往这个空的矩阵里面填值了,要填的值从哪里来来呢?是从源图中来,好,先填写目标图最左上角的象素,坐标为(0,0),那么该坐标对应源图中的坐标可以由如下公式得出srcX=dstX* (srcWidth/dstWidth) , srcY = dstY * (srcHeight/dstHeight)
      好了,套用公式,就可以找到对应的原图的坐标了(0*(3/4),0*(3/4))=>(0*0.75,0*0.75)=>(0,0),找到了源图的对应坐标,就可以把源图中坐标为(0,0)处的234象素值填进去目标图的(0,0)这个位置了。
      接下来,如法炮制,寻找目标图中坐标为(1,0)的象素对应源图中的坐标,套用公式:
    (1*0.75,0*0.75)=>(0.75,0) 结果发现,得到的坐标里面竟然有小数,这可怎么办?计算机里的图像可是数字图像,象素就是最小单位了,象素的坐标都是整数,从来没有小数坐标。这时候采用的一种策略就是采用四舍五入的方法(也可以采用直接舍掉小数位的方法),把非整数坐标转换成整数,好,那么按照四舍五入的方法就得到坐标(1,0),完整的运算过程就是这样的:(1*0.75,0*0.75)=>(0.75,0)=>(1,0) 那么就可以再填一个象素到目标矩阵中了,同样是把源图中坐标为(1,0)处的像素值38填入目标图中的坐标。
      依次填完每个象素,一幅放大后的图像就诞生了,像素矩阵如下所示:
      234 38 22 22
      67 44 12 12
      89 65 63 63
      89 65 63 63
      这种放大图像的方法叫做最临近插值算法,这是一种最基本、最简单的图像缩放算法,效果也是最不好的,放大后的图像有很严重的马赛克,缩小后的图像有很严重的失真;效果不好的根源就是其简单的最临近插值方法引入了严重的图像失真,比如,当由目标图的坐标反推得到的源图的的坐标是一个浮点数的时候,采用了四舍五入的方法,直接采用了和这个浮点数最接近的象素的值,这种方法是很不科学的,当推得坐标值为 0.75的时候,不应该就简单的取为1,既然是0.75,比1要小0.25 ,比0要大0.75 ,那么目标象素值其实应该根据这个源图中虚拟的点四周的四个真实的点来按照一定的规律计算出来的,这样才能达到更好的缩放效果。
      双线型内插值算法就是一种比较好的图像缩放算法,它充分的利用了源图中虚拟点四周的四个真实存在的像素值来共同决定目标图中的一个像素值,因此缩放效果比简单的最邻近插值要好很多。
    双线性内插值算法描述如下:
      对于一个目的像素,设置坐标通过反向变换得到的浮点坐标为(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)
    其中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也体现出了这一特点。

    2,计算方法

    这里写图片描述
     首先,在X方向上进行两次线性插值计算,然后在Y方向上进行一次插值计算。
     这里写图片描述
    在图像处理的时候,我们先根据
      srcX=dstX* (srcWidth/dstWidth),
      srcY = dstY * (srcHeight/dstHeight)
    来计算目标像素在源图像中的位置,这里计算的srcX和srcY一般都是浮点数,比如f(1.2, 3.4)这个像素点是虚拟存在的,先找到与它临近的四个实际存在的像素点
      (1,3) (2,3)
      (1,4) (2,4)
    写成f(i+u,j+v)的形式,则u=0.2,v=0.4, i=1, j=3
    在沿着X方向差插值时,f(R1)=u(f(Q21)-f(Q11))+f(Q11)
    沿着Y方向同理计算。
    或者,直接整理一步计算,
    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)

    3,加速以及优化策略

     单纯按照上文实现的插值算法只能勉强完成插值的功能,速度和效果都不会理想,在具体代码实现的时候有些小技巧。参考OpenCV源码以及网上博客整理如下两点:
    源图像和目标图像几何中心的对齐
    将浮点运算转换成整数运算

    3.1 源图像和目标图像几何中心的对齐  

     方法:在计算源图像的虚拟浮点坐标的时候,一般情况:
      srcX=dstX* (srcWidth/dstWidth) ,
      srcY = dstY * (srcHeight/dstHeight)
      中心对齐(OpenCV也是如此):
      SrcX=(dstX+0.5)* (srcWidth/dstWidth) -0.5
      SrcY=(dstY+0.5) * (srcHeight/dstHeight)-0.5
      原理:
    双线性插值算法及需要注意事项这篇博客解释说“如果选择右上角为原点(0,0),那么最右边和最下边的像素实际上并没有参与计算,而且目标图像的每个像素点计算出的灰度值也相对于源图像偏左偏上。”我有点保持疑问。
      将公式变形:
      srcX=dstX* (srcWidth/dstWidth)+0.5*(srcWidth/dstWidth-1)
      相当于我们在原始的浮点坐标上加上了0.5*(srcWidth/dstWidth-1)这样一个控制因子,这项的符号可正可负,与srcWidth/dstWidth的比值也就是当前插值是扩大还是缩小图像有关,有什么作用呢?看一个例子:假设源图像是3*3,中心点坐标(1,1)目标图像是9*9,中心点坐标(4,4),我们在进行插值映射的时候,尽可能希望均匀的用到源图像的像素信息,最直观的就是(4,4)映射到(1,1)现在直接计算srcX=4*3/9=1.3333!=1,也就是我们在插值的时候所利用的像素集中在图像的右下方,而不是均匀分布整个图像。现在考虑中心点对齐,srcX=(4+0.5)*3/9-0.5=1,刚好满足我们的要求。

    3.2 将浮点运算转换成整数运算

      参考图像处理界双线性插值算法的优化
      直接进行计算的话,由于计算的srcX和srcY 都是浮点数,后续会进行大量的乘法,而图像数据量又大,速度不会理想,解决思路是:
      浮点运算→→整数运算→→”<<左右移按位运算”
      放大的主要对象是u,v这些浮点数,OpenCV选择的放大倍数是2048“如何取这个合适的放大倍数呢,要从三个方面考虑,
      第一:精度问题,如果这个数取得过小,那么经过计算后可能会导致结果出现较大的误差。
      第二,这个数不能太大,太大会导致计算过程超过长整形所能表达的范围。
      第三:速度考虑。假如放大倍数取为12,那么算式在最后的结果中应该需要除以12*12=144,但是如果取为16,则最后的除数为16*16=256,这个数字好,我们可以用右移来实现,而右移要比普通的整除快多了。”我们利用左移11位操作就可以达到放大目的。

    4,代码

    uchar* dataDst = matDst1.data;
        int stepDst = matDst1.step;
        uchar* dataSrc = matSrc.data;
        int stepSrc = matSrc.step;
        int iWidthSrc = matSrc.cols;
        int iHiehgtSrc = matSrc.rows;
    
        for (int j = 0; j < matDst1.rows; ++j)
        {
            float fy = (float)((j + 0.5) * scale_y - 0.5);
            int sy = cvFloor(fy);
            fy -= sy;
            sy = std::min(sy, iHiehgtSrc - 2);
            sy = std::max(0, sy);
    
            short cbufy[2];
            cbufy[0] = cv::saturate_cast<short>((1.f - fy) * 2048);
            cbufy[1] = 2048 - cbufy[0];
    
            for (int i = 0; i < matDst1.cols; ++i)
            {
                float fx = (float)((i + 0.5) * scale_x - 0.5);
                int sx = cvFloor(fx);
                fx -= sx;
    
                if (sx < 0) {
                    fx = 0, sx = 0;
                }
                if (sx >= iWidthSrc - 1) {
                    fx = 0, sx = iWidthSrc - 2;
                }
    
                short cbufx[2];
                cbufx[0] = cv::saturate_cast<short>((1.f - fx) * 2048);
                cbufx[1] = 2048 - cbufx[0];
    
                for (int k = 0; k < matSrc.channels(); ++k)
                {
                    *(dataDst+ j*stepDst + 3*i + k) = (*(dataSrc + sy*stepSrc + 3*sx + k) * cbufx[0] * cbufy[0] + 
                        *(dataSrc + (sy+1)*stepSrc + 3*sx + k) * cbufx[0] * cbufy[1] + 
                        *(dataSrc + sy*stepSrc + 3*(sx+1) + k) * cbufx[1] * cbufy[0] + 
                        *(dataSrc + (sy+1)*stepSrc + 3*(sx+1) + k) * cbufx[1] * cbufy[1]) >> 22;
                }
            }
        }
        cv::imwrite("linear_1.jpg", matDst1);
    
        cv::resize(matSrc, matDst2, matDst1.size(), 0, 0, 1);
        cv::imwrite("linear_2.jpg", matDst2);

    参考文件:
    [1]三十分钟理解:线性插值,双线性插值Bilinear Interpolation算法
    [2] 双线性插值算法及需要注意事项
    [3]OpenCV ——双线性插值(Bilinear interpolation)
    [4]FPGA双线性插值算法(图像的缩放

    展开全文
  • 算法原理简介双线性插值是一阶插值,常用于图像的旋转、缩放处理。它利用原图中对应的四个点的像素值来确定目标图像中的像素值。为了便于理解,我们来看两张尺寸不一样的图片:原图变换图假设原图图片的宽度为yw,...

    算法原理简介

    双线性插值是一阶插值,常用于图像的旋转、缩放处理。

    它利用原图中对应的四个点的像素值来确定目标图像中的像素值。

    为了便于理解,我们来看两张尺寸不一样的图片:

    原图


    变换图

    假设原图图片的宽度为yw,高度为xh

    变换图的宽度为jw,高度为ih

    于是对于变换图中任意一个像素点(j’, i’)我们可以用以下的方法映射到原图中去:

    y' = yw/jw * j'
    x' = xh/ih * i'
    

    通常情况下,y’和x’不为整数。

    例如,原图尺寸:

    yw = 1000
    xh = 800
    

    变换图尺寸:

    jw = 700
    ih = 700
    

    对于变换图中的(400, 400)像素点:

    y' = 1000/700 * 400 = 571.42857
    x' = 800/700 * 400 = 457.14286
    

    我们将变换图中的(400, 400)像素点映射到原图中的(571.42857, 457.14286)。

    于是我们就用原图中对应的4点(571,457),(572,457),(571,458),(572,458)来确定变换图像中的(400,400)点像素值。

    t = 571.42857 - 571 = 0.42857
    u = 457.14286 - 457 = 0.14286
    

    我们认为距离(y’, x’)点距离越近,其对目标像素影响的权重应该越大,距离越远,影响权重越小。

    (571,457)点权重为s4面积,(572,457)点权重为s3面积,(571,458)点权重为s2面积,(572,458)点权重为s1面积。

    于是我们得到:

    (400,400)目标图 = (571,457)xs4 + (572,457)xs3 + (571,458)xs2 + (572,458)xs1

    其中,s1+s2+s3+s4 = 1

    以上就是双线性插值法目标像素计算公式。

    算法的c代码实现

    /*
     * param: 
     * Mat src 原始图片
     * Mat dst 目标图片
     */
    void BGRBilinearScale(const Mat src, Mat dst) {
    
        double dstH = dst.rows;  //目标图片高度
        double dstW = dst.cols;  //目标图片宽度
        double srcW = src.cols;  //原始图片宽度,如果用int可能会导致(srcH - 1)/(dstH - 1)恒为零
        double srcH = src.rows;  //原始图片高度
        double xm = 0;      //映射的x
        double ym = 0;      //映射的y
        int xi = 0;         //映射x整数部分
        int yi = 0;         //映射y整数部分
        int xl = 0;         //xi + 1
        int yl = 0;         //yi + 1
        double xs = 0;   
        double ys = 0;
    
        /* 为目标图片每个像素点赋值 */
        for(int i = 0; i < dstH; i ++) {
            for(int j = 0; j < dstW; j ++) {
                //求出目标图像(i,j)点到原图像中的映射坐标(mapx,mapy)
                xm = (srcH - 1)/(dstH - 1) * i;
                ym = (srcW - 1)/(dstW - 1) * j;
                /* 取映射到原图的xm的整数部分 */
                xi = (int)xm;
                yi = (int)ym;
                /* 取偏移量 */
                xs = xm - xi;
                ys = ym - yi;
    
                xl = xi + 1;
                yl = yi + 1;
                //边缘点
                if((xi+1) > (srcH-1)) xl = xi-1;
                if((yi+1) > (srcW-1)) yl = yi-1;
    
                //b
                dst.at<Vec3b>(i,j)[0] = (int)(src.at<Vec3b>(xi,yi)[0]*(1-xs)*(1-ys) + 
                        src.at<Vec3b>(xi,yl)[0]*(1-xs)*ys +
                        src.at<Vec3b>(xl,yi)[0]*xs*(1-ys) +
                        src.at<Vec3b>(xl,yl)[0]*xs*ys);
                //g
                dst.at<Vec3b>(i,j)[1] = (int)(src.at<Vec3b>(xi,yi)[1]*(1-xs)*(1-ys) + 
                        src.at<Vec3b>(xi,yl)[1]*(1-xs)*ys +
                        src.at<Vec3b>(xl,yi)[1]*xs*(1-ys) +
                        src.at<Vec3b>(xl,yl)[1]*xs*ys);
                //r
                dst.at<Vec3b>(i,j)[2] = (int)(src.at<Vec3b>(xi,yi)[2]*(1-xs)*(1-ys) + 
                        src.at<Vec3b>(xi,yl)[2]*(1-xs)*ys +
                        src.at<Vec3b>(xl,yi)[2]*xs*(1-ys) +
                        src.at<Vec3b>(xl,yl)[2]*xs*ys);
    
            }
        }
    }

    放缩效果预览

    原图为1024 x 640图片,变换目标图片为800 x 800图片

    原图

    目标图

    展开全文
  • 上一篇中,对于倾斜的表格图像,通过拉冬变换得到了图像的倾斜角,下面探讨如何利用这个倾斜角进行图像变换在论文《社区选举系统选票中的表格识别算法》中使用的是双线性插值法。下面我们研究一下如何使用双线性插值...
  • 双线性插值法及python实现 插值法是一种根据原图(source)图片信息构造目标图像(destination)的方法。而其中的双线性插值法是一种目前使用较多的插值方法,他在效果和计算量之间有较好的权衡,所以使用较为广泛。...
  • 准备写双线性插值的图像缩放,先从算法原理开始。 查阅资料和一点自己的理解。...因为这里是用作图像处理,所以仅代入当一种计算规律使用。 首先来了解一下算法过程: 假如我们想得到未知函...
  • 双线性插值实现图像放大
  • 在讲双线性插值之前,先说一下线性插值,因为双线性插值法的核心思想是在两个方向分别进行一次线性插值。 如图所示: 使用斜率的关系,有:(这是非常重要的,第一个式子是斜率关系,第二个式子由第一个式子推出)...
  • 最近在做视频拼接的项目,里面用到了图像的单应矩阵变换,在最后的图像重映射,由于目标图像的坐标是非整数的,所以需要用到插值的方法,用的就是双线性插值,下面的博文主要是查看了前辈的博客对双线性插值算法...
  • 一、引言 数字图像处理的对象因其涉及到社会的各个领域,倍受到越来越多的关注,而图像缩放作为数字图像处理中的基本操作尤为重要,在社会的...本文提出了一种双线性插值算法,用以改进图像的缩放质量。 二、空间变换
  • 用Visual C++实现图像双线性插值法等比例缩放 在数字图像处理中,图像的缩放是基本的操作。下面介绍利用Visual C++ MFC多文档应用程序对话框来,利用双线性插值法来实现图像缩放的功能,本实验采用Visual Studio ...
  • 图像处理中的双线性插值 1.为什么要用图像的插值? 在图像的放大和缩小的过程中,需要计算新图像像素点在原图的位置,如果计算的位置不是整数,就需要用到图像的内插,我们需要寻找在原图中最近得像素点赋值给新的...
  • 双线性插值法原理: ① 何为线性插值? 插值就是在两个数之间插入一个数,线性插值原理图如下: 在位置 x 进行线性插值,插入的值为f(x) ↑ ② 各种插值法插值法的第一步都是相同的,计算目标图...
  • 图像处理双线性插值算法的优化  该博文转载于Imageshop的博文  在图像处理中,双线性插值算法的使用频率相当高,比如在图像的缩放中,在所有的扭曲算法中,都可以利用该算法改进处理的视觉效果。首先,...
  • 双线性插值法 定义: 又称双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向上分别进行一次线性插值。 对于一个目的像素,设置坐标通过反向变换得到的浮点坐标为...
  • 双线性插值-图像旋转

    2015-02-04 15:36:43
    首先,什么是双线性插值?具体的说明大家可参照各种百科,我在这里从图像角度来简单讲下,大体相似。 看下面这个图 图像旋转后,像素点的整数坐标必然会得到小数,显然在图像的像素点里面是没有小数坐标的,那...
  • 图片的旋转方法算法以及双线性插值,最邻近插值法 图片的旋转方法算法以及双线性插值,最邻近插值法
  • 自己编写整理的双线性插值算法原理,内含原理和实例源码,给大家分享。
1 2 3 4 5 ... 20
收藏数 2,627
精华内容 1,050
关键字:

双线性插值法图像处理