几个图像缩放算法的比较
前段时间由于项目的需求,需要实现图像的缩放功能,期间查找了不少关于图像缩放算法的资料,现把自己的心得整理一下。
由于研究生期间没有选修过图像处理方面的课程,所以对图像缩放的原理可谓一窍不通,当时开始编写代码的时候简直就是一头雾水。而且网上虽然介绍图像处理的代码很多,但涉及图像缩放的代码却很少,因为很多软件都直接使用了windows的GDI函数库的API函数:StretchBlt,或者VCL中TCanvas类的StretchDraw。无奈这两个函数都是直接对BMP图像进行缩放,而且StretchBlt是在CDC里面调用的,结果只是在显示的时候对图像进行缩放,不能够进行缩放的存储。那些天在GDI和GDIPLUS摸索了半天,都找不到合适的函数,某天却迸出个想法来:图像放大不就是把每个象素点再多弄几个出来,而缩小不就是去掉里面一些象素点。所以就按照自己的想法写了一个比较粗糙的放大函数:
BYTE *src,*dst,*ptr,*buffer,*next;
for(int i=0,n=0; i < this->Height(); i++,n=n+rate)
{
src = this->GetLinePtr(i);
dst = tempdib->GetLinePtr(n);
ptr = dst;
for(int j=0; j < this->Width(); j++,ptr=ptr+3*rate)
{
memcpy(ptr,src+j*3,3);
for(int m=1;m
memcpy(ptr+m*3,ptr,3);
}
for(int m=n+1;m
{
buffer = dst;
next = tempdib->GetLinePtr(m);
ptr = next;
memcpy(ptr,buffer,dstwidth*3);
}
}
这段代码的效果比较粗糙,但处理的办法比较有意思。首先是读取一行的图像数据,然后在每一行循环读取一个象素的RGB值并复制到新图像的内存空间,然后根据放大的比例再作一次循环,把这个RGB值按照比例复制进内存空间。当进行完一行的处理后,在新图像的内存空间进行一次循环处理,把这行数据按照比例复制给下面几行。这样就通过象素点的复制实现了图片的放大。不过放大的效果不是特别好,图像列方向上会出现很多的毛刺,放大4倍的话图像就很模糊了。
所以还是重新去查找资料,结果在网上搜到一篇不错的文章——用线性插值算法实现图像缩放。看了文章,才发现我原先的办法还真不是一般的原始,不过思路还跟GDI里面的StretchBlt差不多。StretchBlt采用的方法在图像处理领域称为最近邻域法,其基本原理就是先取出原图的相邻四个点,然后把新位置的点跟这四个点的位置做比较,把最近一个点的RGB值赋给新位置的点。所以在放大的时候,几乎就是像我那样把前一个点的象素赋给新位置的点。这样处理的结果就是导致图像不够平滑,因为点与点之间是一个过渡的过程,不是简单的复制,稍微好点的办法就是把新点附近几个点的颜色值取平均再赋给这个点。这种方法在数值计算方法叫做线性插值。但那篇文章提供了一个更好的方法,叫做二维线性插值,其原理也是对附近的点取平均,但它对各个点的颜色值加上不同的权数,这个权数就是各个点距离这个点的位置。其计算方法如下:
P = n*b*PA + n * ( 1 – b )*PB + ( 1 – n ) * b * PC + ( 1 – n ) * ( 1 – b ) * PD
其中:n为v(映射后相应点在源图像中的Y轴坐标,一般不是整数)下面最接
|
|