双线性插值算法进行图像缩放及性能效果优化

一)转自http://handspeaker.iteye.com/blog/1545126html

最近在编程时用到了双线性插值算法,对图像进行缩放。网上有不少这方面的资料,介绍的也算明白。可是,这些文章只介绍了算法,并无具体说怎么实现以及怎么实现最好,举个例子,你能够按照网上文章的算法本身写一个双线性插值程序,用它对一张图片进行处理,而后再用matlab或者openCV的resize函数对同一张图片进行处理,获得的结果是不同的,若是源图片较小,效果差距就更大。如下是对于双线性插值的讲解以及上述现象的解释:算法

 

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)。ide

若图像为灰度图像,那么(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)为各点相应权值。关于权值的计算,在维基百科和百度百科上写的很明白。spa

 

2.存在的问题code

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

那这个到底是怎么回事呢?

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

按照网上一些博客上写的,源图像和目标图像的原点(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

 

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


总结:

总结一下,我获得的教训有这么几条。

1.网上的一些资料有的时候并不靠谱,本身仍是要多作实验。

2.不要小瞧一些简单的、基本的算法,让你写你未必会写,并且其中可能还藏着一些玄妙。

3.要多动手编程,多体会算法,多看大牛写的源码(虽然有的时候很吃力,可是要坚持看)。

二)转自http://www.cnblogs.com/Imageshop/archive/2011/11/12/2246808.html

 在图像处理中,双线性插值算法的使用频率至关高,好比在图像的缩放中,在全部的扭曲算法中,均可以利用该算法改进处理的视觉效果。首先,咱们看看该算法的简介。

     在数学上,双线性插值算法能够当作是两个变量间的线性插值的延伸。执行该过程的关键思路是先在一个方向上执行线性插值,而后再在另一个方向上插值。下图示意出这个过程的大概意思。

     用一个简单的数学表达式能够表示以下:

     f(x,y)=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)=(f(0,0)(1-x)+f(1,0)x) (1-y)+(f(0,1)(1-x)+f(1,1)x)y

     由上式能够看出,这个过程存在着大量的浮点数运算,对于图像这样大的计算用户来讲,是一个较为耗时的过程。

     考虑到图像的特殊性,他的像素值的计算结果须要落在0到255之间,最多只有256种结果,由上式能够看出,通常状况下,计算出的f(x,y)是个浮点数,咱们还须要对该浮点数进行取整。所以,咱们能够考虑将该过程当中的全部相似于1-x、1-y的变量放大合适的倍数,获得对应的整数,最后再除以一个合适的整数做为插值的结果。

      如何取这个合适的放大倍数呢,要从三个方面考虑,第一:精度问题,若是这个数取得太小,那么通过计算后可能会致使结果出现较大的偏差。第二,这个数不能太大,太大会致使计算过程超过长整形所能表达的范围。第三:速度考虑。假如放大倍数取为12,那么算式在最后的结果中应该须要除以12*12=144,可是若是取为16,则最后的除数为16*16=256,这个数字好,咱们能够用右移来实现,而右移要比普通的整除快多了。 

      综合考虑上述三条,咱们选择2048这个数比较合适。

      下面咱们假定某个算法获得了咱们要取样的坐标分别PosX以及PosY,其中PosX=25.489,PosY=58.698。则这个过程的相似代码片断以下:

复制代码
 1 NewX =  Int(PosX)                         ' 向下取整,NewX=25
 2 NewY =  Int(PosY)                         ' 向下取整,NewY=58
 3 PartX = (PosX - NewX) *  2048             ' 对应表达式中的X
 4 PartY = (PosY - NewY) *  2048             ' 对应表达式中的Y
 5 InvX =  2048 - PartX                      ' 对应表达式中的1-X
 6 InvY =  2048 - PartY                      ' 对应表达式中的1-Y
 7 
 8 Index1 = SamStride * NewY + NewX *  3     ' 计算取样点左上角邻近的那个像素点的内存地址
 9 Index2 = Index1 + SamStride           ' 左下角像素点地址
10 ImageData(Speed +  2) = ((Sample(Index1 +  2) * InvX + Sample(Index1 +  5) * PartX) * InvY + (Sample(Index2 +  2) * InvX + 
                          Sample(Index2 +  5) * PartX) * PartY) \  4194304       '处理红色份量
11 ImageData(Speed +  1) = ((Sample(Index1 +  1) * InvX + Sample(Index1 +  4) * PartX) * InvY + (Sample(Index2 +  1) * InvX +
                          Sample(Index2 +  4) * PartX) * PartY) \  4194304       '处理绿色份量
12 ImageData(Speed) = ((Sample(Index1) * InvX + Sample(Index1 +  3) * PartX) * InvY + (Sample(Index2) * InvX +
                      Sample(Index2 +  3) * PartX) * PartY) \  4194304           '处理蓝色份量
复制代码

      以上代码中涉及到的变量都为整型(PosX及PosY固然为浮点型)。

      代码中Sample数组保存了从中取样的图像数据,SamStride为该图像的扫描行大小。

      观察上述代码,除了有2句涉及到了浮点计算,其余都是整数之间的运算。

      在Basic语言中,编译时若是勾选全部的高级优化,则\ 4194304会被编译为>>22,即右移22位,若是使用的是C语言,则直接写为>>22。

      须要注意的是,在进行这代代码前,须要保证PosX以及PosY在合理的范围内,即不能超出取样图像的宽度和高度范围。

      经过这样的改进,速度较直接用浮点类型快至少100%以上,而处理后的效果基本没有什么区别。

相关文章
相关标签/搜索