图像缩放算法
- 格式:docx
- 大小:208.84 KB
- 文档页数:56
图像缩放算法
摘要:首先给出一个基本的图像缩放算法,然后一步一步的优化其速度和缩放质量;
高质量的快速的图像缩放 全文 分为:
上篇 近邻取样插值和其速度优化
中篇 二次线性插值和三次卷积插值
下篇 三次线性插值和MipMap链
正文:
为了便于讨论,这里只处理32bit的ARGB颜色;
代码使用C++;涉及到汇编优化的时候假定为x86平台;使用的编译器为vc2005;
为了代码的可读性,没有加入异常处理代码;
测试使用的CPU为AMD64x2 4200+(2.37G) 和 Intel Core2 4400(2.00G);
速度测试说明:
只测试内存数据到内存数据的缩放
测试图片都是800*600缩放到1024*768; fps表示每秒钟的帧数,值越大表示函数越快
////////////////////////////////////////////////////////////////////////////////
//Windows GDI相关函数参考速度:
//==============================================================================
// BitBlt 544.7 fps //is copy 800*600 to 800*600
// BitBlt 331.6 fps //is copy 1024*1024 to 1024*1024
// StretchBlt 232.7 fps //is zoom 800*600 to 1024*1024
////////////////////////////////////////////////////////////////////////////////
A: 首先定义图像数据结构:
#define asm __asm
typedef unsigned char TUInt8; // [0..255]
struct TARGB32 //32 bit color
{
TUInt8 B,G,R,A; // A is alpha
};
struct TPicRegion //一块颜色数据区的描述,便于参数传递
{
TARGB32* pdata; //颜色数据首地址
long byte_width; //一行数据的物理宽度(字节宽度);
//abs(byte_width)有可能大于等于width*sizeof(TARGB32);
long width; //像素宽度
long height; //像素高度
};
//那么访问一个点的函数可以写为:
inline TARGB32& Pixels(const TPicRegion& pic,const long x,const long y)
{
return ( (TARGB32*)((TUInt8*)pic.pdata+pic.byte_width*y) )[x];
}
B: 缩放原理和公式图示:
缩放后图片 原图片
(宽DW,高DH) (宽SW,高SH) (Sx-0)/(SW-0)=(Dx-0)/(DW-0) (Sy-0)/(SH-0)=(Dy-0)/(DH-0)
=> Sx=Dx*SW/DW Sy=Dy*SH/DH
C: 缩放算法的一个参考实现
//给出一个最简单的缩放函数(插值方式为近邻取样,而且我“尽力”把它写得慢一些了:D)
//Src.PColorData指向源数据区,Dst.PColorData指向目的数据区
//函数将大小为Src.Width*Src.Height的图片缩放到Dst.Width*Dst.Height的区域中
void PicZoom0(const TPicRegion& Dst,const TPicRegion& Src)
{
if ( (0==Dst.width)||(0==Dst.height)
||(0==Src.width)||(0==Src.height)) return;
for (long x=0;x { for (long y=0;y { long srcx=(x*Src.width/Dst.width); long srcy=(y*Src.height/Dst.height); Pixels(Dst,x,y)=Pixels(Src,srcx,srcy); } } } //////////////////////////////////////////////////////////////////////////////// //速度测试: //============================================================================== // PicZoom0 19.4 fps //////////////////////////////////////////////////////////////////////////////// D: 优化PicZoom0函数 a.PicZoom0函数并没有按照颜色数据在内存中的排列顺序读写(内部循环递增y行 索引),将造成CPU缓存预读失败和内存颠簸导致巨大的性能损失,(很多硬件都有这种特性, 包括缓存、内存、显存、硬盘等,优化顺序访问,随机访问时会造成巨大的性能损失) 所以先交换x,y循环的顺序: void PicZoom1(const TPicRegion& Dst,const TPicRegion& Src) { if ( (0==Dst.width)||(0==Dst.height) ||(0==Src.width)||(0==Src.height)) return; for (long y=0;y { for (long x=0;x { long srcx=(x*Src.width/Dst.width); long srcy=(y*Src.height/Dst.height); Pixels(Dst,x,y)=Pixels(Src,srcx,srcy); } } } //////////////////////////////////////////////////////////////////////////////// //速度测试: //============================================================================== // PicZoom1 30.1 fps //////////////////////////////////////////////////////////////////////////////// b.“(x*Src.Width/Dst.Width)”表达式中有一个除法运算,它属于很慢的操作(比一般 的加减运算慢几十倍!),使用定点数的方法来优化它; void PicZoom2(const TPicRegion& Dst,const TPicRegion& Src) { if ( (0==Dst.width)||(0==Dst.height) ||(0==Src.width)||(0==Src.height)) return; //函数能够处理的最大图片尺寸65536*65536 unsigned long xrIntFloat_16=(Src.width<<16)/Dst.width+1; //16.16格式定点数 unsigned long yrIntFloat_16=(Src.height<<16)/Dst.height+1; //16.16格式定点数 //可证明: (Dst.width-1)*xrIntFloat_16 for (unsigned long y=0;y { for (unsigned long x=0;x { unsigned long srcx=(x*xrIntFloat_16)>>16; unsigned long srcy=(y*yrIntFloat_16)>>16; Pixels(Dst,x,y)=Pixels(Src,srcx,srcy); } } } //////////////////////////////////////////////////////////////////////////////// //速度测试: //============================================================================== // PicZoom2 185.8 fps //////////////////////////////////////////////////////////////////////////////// c. 在x的循环中y一直不变,那么可以提前计算与y相关的值; 1.可以发现srcy的值和x变量无关,可以提前到x轴循环之前;2.展开Pixels函数,优化与y相关的指针计算; void PicZoom3(const TPicRegion& Dst,const TPicRegion& Src) { if ( (0==Dst.width)||(0==Dst.height) ||(0==Src.width)||(0==Src.height)) return; unsigned long xrIntFloat_16=(Src.width<<16)/Dst.width+1; unsigned long yrIntFloat_16=(Src.height<<16)/Dst.height+1; unsigned long dst_width=Dst.width; TARGB32* pDstLine=Dst.pdata; unsigned long srcy_16=0; for (unsigned long y=0;y { TARGB32* pSrcLine=((TARGB32*)((TUInt8*)Src.pdata+Src.byte_width*(srcy_16>>16))); unsigned long srcx_16=0; for (unsigned long x=0;x { pDstLine[x]=pSrcLine[srcx_16>>16]; srcx_16+=xrIntFloat_16; } srcy_16+=yrIntFloat_16; ((TUInt8*&)pDstLine)+=Dst.byte_width; } } //////////////////////////////////////////////////////////////////////////////// //速度测试: