图像缩放算法(中篇)

=================================

转载别人的,但是这篇文章写得确实太好了,所以想分享出来。

原文地址:http://blog.chinaunix.net/uid-22915173-id-2185545.html

摘要:首先给出一个基本的图像缩放算法,然后一步一步的优化其速度和缩放质量;

高质量的快速的图像缩放 全文 分为:
上篇 近邻取样插值和其速度优化
中篇 二次线性插值和三次卷积插值
下篇 三次线性插值和MipMap链

正文:
为了便于讨论,这里只处理32bit的ARGB颜色;
代码使用C++;涉及到汇编优化的时候假定为x86平台;使用的编译器为vc2005;
为了代码的可读性,没有加入异常处理代码;
测试使用的CPU为AMD64x2 4200+(2.37G) 和 Intel Core2 4400(2.00G);

速度测试说明:
只测试内存数据到内存数据的缩放
测试图片都是800600缩放到1024768; fps表示每秒钟的帧数,值越大表示函数越快

A:近邻取样插值、二次线性插值、三次卷积插值 缩放效果对比

原图 近邻取样缩放到0.6倍 近邻取样缩放到1.6倍

二次线性插值缩放到0.6倍 二次线性插值缩放到1.6倍

三次卷积插值缩放到0.6倍 三次卷积插值缩放到1.6倍

原图 近邻取样缩放到8倍 二次线性插值缩放到8倍 三次卷积插值缩放到8倍 二次线性插值(近似公式)

近邻取样插值缩放简单、速度快,但很多时候缩放出的图片质量比较差(特别是对于人物、景色等),
图片的缩放有比较明显的锯齿;使用二次或更高次插值有利于改善缩放效果;

B: 首先定义图像数据结构:

#defineasm __asm

typedef unsignedcharTUInt8;// [0…255]
structTARGB32//32 bit color
{
TUInt8 b,g,r,a;//a is alpha
};

structTPicRegion//一块颜色数据区的描述,便于参数传递
{
TARGB32* pdata;//颜色数据首地址
longbyte_width;//一行数据的物理宽度(字节宽度);
//abs(byte_width)有可能大于等于width*sizeof(TARGB32);
longwidth;//像素宽度
longheight;//像素高度
};

//那么访问一个点的函数可以写为:
inline TARGB32& Pixels(constTPicRegion& pic,constlongx,constlongy)
{
return( (TARGB32*)((TUInt8*)pic.pdata+pic.byte_width*y) )[x];
}

二次线性插值缩放:

C: 二次线性插值缩放原理和公式图示:

缩放后图片 原图片
(宽DW,高DH) (宽SW,高SH)

缩放映射原理:
(Sx-0)/(SW-0)=(Dx-0)/(DW-0) (Sy-0)/(SH-0)=(Dy-0)/(DH-0)
=> Sx=DxSW/DW Sy=DySH/DH

聚焦看看(Sx,Sy)坐标点(Sx,Sy为浮点数)附近的情况;

对于近邻取样插值的缩放算法,直接取Color0颜色作为缩放后点的颜色;
二次线性插值需要考虑(Sx,Sy)坐标点周围的4个颜色值Color0\Color1\Color2\Color3,
把(Sx,Sy)到A\B\C\D坐标点的距离作为系数来把4个颜色混合出缩放后点的颜色;
( u=Sx-floor(Sx); v=Sy-floor(Sy); 说明:floor函数的返回值为小于等于参数的最大整数 )
二次线性插值公式为:
tmpColor0=Color0*(1-u) + Color2u;
tmpColor1=Color1
(1-u) + Color3u;
DstColor =tmpColor0
(1-v) + tmpColor2*v;

展开公式为:
pm0=(1-u)(1-v);
pm1=v
(1-u);
pm2=u*(1-v);
pm3=uv;
则颜色混合公式为:
DstColor = Color0
pm0 + Color1pm1 + Color2pm2 + Color3*pm3;

参数函数图示:

二次线性插值函数图示

对于上面的公式,它将图片向右下各移动了半个像素,需要对此做一个修正;
=> Sx=(Dx+0.5)*SW/DW-0.5; Sy=(Dy+0.5)*SH/DH-0.5;
而实际的程序,还需要考虑到边界(访问源图片可能超界)对于算法的影响,边界的处理可能有各种
方案(不处理边界或边界回绕或边界饱和或边界映射或用背景颜色混合等;文章中默认使用边界饱和来处理超界);
比如:边界饱和函数:

//访问一个点的函数,(x,y)坐标可能超出图片边界; //边界处理模式:边界饱和
inline TARGB32 Pixels_Bound(constTPicRegion& pic,longx,longy)
{
//assert((pic.width>0)&&(pic.height>0));
boolIsInPic=true;
if(x<0) {x=0; IsInPic=false; }elseif(x>=pic.width ) {x=pic.width -1; IsInPic=false; }
if(y<0) {y=0; IsInPic=false; }elseif(y>=pic.height) {y=pic.height-1; IsInPic=false; }
TARGB32 result=Pixels(pic,x,y);
if(!IsInPic) result.a=0;
returnresult;
}

D: 二次线性插值缩放算法的一个参考实现:PicZoom_BilInear0
该函数并没有做什么优化,只是一个简单的浮点实现版本;

inlinevoidBilinear0(constTPicRegion& pic,floatfx,floatfy,TARGB32* result)
{
longx=(long)fx;if(x>fx) --x;//x=floor(fx);
longy=(long)fy;if(y>fy) --y;//y=floor(fy);

TARGB32 Color0=Pixels_Bound(pic,x,y);
TARGB32 Color2=Pixels_Bound(pic,x+1,y);
TARGB32 Color1=Pixels_Bound(pic,x,y+1);
TARGB32 Color3=Pixels_Bound(pic,x+1,y+1);

floatu=fx-x;
floatv=fy-y;
floatpm3=uv;
floatpm2=u
(1-v);
floatpm1=v*(1-u);
floatpm0=(1-u)*(1-v);

result->a=(pm0Color0.a+pm1Color1.a+pm2Color2.a+pm3Color3.a);
result->r=(pm0Color0.r+pm1Color1.r+pm2Color2.r+pm3Color3.r);
result->g=(pm0Color0.g+pm1Color1.g+pm2Color2.g+pm3Color3.g);
result->b=(pm0Color0.b+pm1Color1.b+pm2Color2.b+pm3Color3.b);
}

voidPicZoom_Bilinear0(constTPicRegion& Dst,constTPicRegion& Src)
{
if( (0 == Dst.width)||(0 == Dst.height)
||(0 == Src.width)||(0 == Src.height))return;

unsignedlongdst_width=Dst.width;
TARGB32* pDstLine=Dst.pdata;
for(unsignedlongy=0;y
{

floatsrcy=(y+0.4999999)*Src.height/Dst.height-0.5;
for(unsignedlongx=0;x
{

floatsrcx=(x+0.4999999)Src.width/Dst.width-0.5;
Bilinear0(Src,srcx,srcy,&pDstLine[x]);
}
((TUInt8
&)pDstLine)+=Dst.byte_width;
}
}

//速度测试:
//==============================================================================
// PicZoom_BilInear0 8.3 fps

E: 把PicZoom_BilInear0的浮点计算改写为定点数实现:PicZoom_BilInear1

inlinevoidBilinear1(constTPicRegion& pic,constlongx_16,constlongy_16,TARGB32* result)
{
longx=x_16>>16;
longy=y_16>>16;
TARGB32 Color0=Pixels_Bound(pic,x,y);
TARGB32 Color2=Pixels_Bound(pic,x+1,y);
TARGB32 Color1=Pixels_Bound(pic,x,y+1);
TARGB32 Color3=Pixels_Bound(pic,x+1,y+1);

unsignedlongu_8=(x_16 & 0xFFFF)>>8;
unsignedlongv_8=(y_16 & 0xFFFF)>>8;
unsignedlongpm3_16=(u_8v_8);
unsignedlongpm2_16=(u_8
(unsignedlong)(256-v_8));
unsignedlongpm1_16=(v_8*(unsignedlong)(256-u_8));
unsignedlongpm0_16=((256-u_8)*(256-v_8));

result->a=((pm0_16Color0.a+pm1_16Color1.a+pm2_16Color2.a+pm3_16Color3.a)>>16);
result->r=((pm0_16Color0.r+pm1_16Color1.r+pm2_16Color2.r+pm3_16Color3.r)>>16);
result->g=((pm0_16Color0.g+pm1_16Color1.g+pm2_16Color2.g+pm3_16Color3.g)>>16);
result->b=((pm0_16Color0.b+pm1_16Color1.b+pm2_16Color2.b+pm3_16Color3.b)>>16);
}

voidPicZoom_Bilinear1(constTPicRegion& Dst,constTPicRegion& Src)
{
if( (0 == Dst.width)||(0 == Dst.height)
||(0 == Src.width)||(0 == Src.height))return;

longxrIntFloat_16=((Src.width)<<16)/Dst.width+1;
longyrIntFloat_16=((Src.height)<<16)/Dst.height+1;
constlongcsDErrorX=-(1<<15)+(xrIntFloat_16>>1);
constlongcsDErrorY=-(1<<15)+(yrIntFloat_16>>1);

unsignedlongdst_width=Dst.width;

TARGB32* pDstLine=Dst.pdata;
longsrcy_16=csDErrorY;
longy;
for(y=0;y
{

longsrcx_16=csDErrorX;
for(unsignedlongx=0;x
{
Bilinear1(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
}

//速度测试:
//==============================================================================
// PicZoom_BilInear1 17.7 fps

F: 二次线性插值需要考略边界访问超界的问题,我们可以将边界区域和内部区域分开处理,这样就可以优化内部的插值实现函数了:比如不需要判断访问超界、减少颜色数据复制、减少一些不必要的重复坐标计算等等

inlinevoidBilinear2_Fast(TARGB32* PColor0,TARGB32* PColor1,unsignedlongu_8,unsignedlongv_8,TARGB32* result)
{
unsignedlongpm3_16=u_8*v_8;
unsignedlongpm2_16=(u_8<<8)-pm3_16;
unsignedlongpm1_16=(v_8<<8)-pm3_16;
unsignedlongpm0_16=(1<<16)-pm1_16-pm2_16-pm3_16;

result->a=((pm0_16PColor0[0].a+pm2_16PColor0[1].a+pm1_16PColor1[0].a+pm3_16PColor1[1].a)>>16);
result->r=((pm0_16PColor0[0].r+pm2_16PColor0[1].r+pm1_16PColor1[0].r+pm3_16PColor1[1].r)>>16);
result->g=((pm0_16PColor0[0].g+pm2_16PColor0[1].g+pm1_16PColor1[0].g+pm3_16PColor1[1].g)>>16);
result->b=((pm0_16PColor0[0].b+pm2_16PColor0[1].b+pm1_16PColor1[0].b+pm3_16PColor1[1].b)>>16);
}

inlinevoidBilinear2_Border(constTPicRegion& pic,constlongx_16,constlongy_16,TARGB32* result)
{
longx=(x_16>>16);
longy=(y_16>>16);
unsignedlongu_16=((unsignedshort)(x_16));
unsignedlongv_16=((unsignedshort)(y_16));

TARGB32 pixel[4];
pixel[0]=Pixels_Bound(pic,x,y);
pixel[1]=Pixels_Bound(pic,x+1,y);
pixel[2]=Pixels_Bound(pic,x,y+1);
pixel[3]=Pixels_Bound(pic,x+1,y+1);

Bilinear2_Fast(&pixel[0],&pixel[2],u_16>>8,v_16>>8,result);
}

voidPicZoom_Bilinear2(constTPicRegion& Dst,constTPicRegion& Src)
{
if( (0 == Dst.width)||(0 == Dst.height)
||(0 == Src.width)||(0 == Src.height))return;

longxrIntFloat_16=((Src.width)<<16)/Dst.width+1;
longyrIntFloat_16=((Src.height)<<16)/Dst.height+1;
constlongcsDErrorX=-(1<<15)+(xrIntFloat_16>>1);
constlongcsDErrorY=-(1<<15)+(yrIntFloat_16>>1);

unsignedlongdst_width=Dst.width;

//计算出需要特殊处理的边界
longborder_y0=-csDErrorY/yrIntFloat_16+1;//y0+yyr>=0; y0=csDErrorY => y>=-csDErrorY/yr
if(border_y0>=Dst.height) border_y0=Dst.height;
longborder_x0=-csDErrorX/xrIntFloat_16+1;
if(border_x0>=Dst.width ) border_x0=Dst.width;
longborder_y1=(((Src.height-2)<<16)-csDErrorY)/yrIntFloat_16+1;//y0+y
yr<=(height-2) => y<=(height-2-csDErrorY)/yr
if(border_y1 longborder_x1=(((Src.width-2)<<16)-csDErrorX)/xrIntFloat_16+1;
if(border_x1
TARGB32* pDstLine=Dst.pdata;
longSrc_byte_width=Src.byte_width;
longsrcy_16=csDErrorY;
longy;
for(y=0;y
{

longsrcx_16=csDErrorX;
for(unsignedlongx=0;x
{
Bilinear2_Border(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
for(y=border_y0;y
{

longsrcx_16=csDErrorX;
longx;
for(x=0;x
{
Bilinear2_Border(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}

{
unsignedlongv_8=(srcy_16 & 0xFFFF)>>8;
TARGB32* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
for(unsignedlongx=border_x0;x
{
TARGB32* PColor0=&PSrcLineColor[srcx_16>>16];
TARGB32* PColor1=(TARGB32*)((TUInt8*)(PColor0)+Src_byte_width);
Bilinear2_Fast(PColor0,PColor1,(srcx_16 & 0xFFFF)>>8,v_8,&pDstLine[x]);
srcx_16+=xrIntFloat_16;
}
}

for(x=border_x1;x
{
Bilinear2_Border(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
for(y=border_y1;y
{

longsrcx_16=csDErrorX;
for(unsignedlongx=0;x
{
Bilinear2_Border(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
}

//速度测试:
//==============================================================================
// PicZoom_BilInear2 43.4 fps

(F’补充:
如果不想处理边界访问超界问题,可以考虑扩大源图片的尺寸,加一个边框 (“哨兵”优化);
这样插值算法就不用考虑边界问题了,程序写起来也简单很多!
如果对缩放结果的边界像素级精度要求不是太高,我还有一个方案,一个稍微改变的缩放公式:
Sx=Dx*(SW-1)/DW; Sy=Dy*(SH-1)/DH; (源图片宽和高:SW>=2;SH>=2)
证明这个公式不会造成内存访问超界:
要求Dx=DW-1时: sx+1=int( (dw-1)/dw*(dw-1) ) +1 <= (sw-1)
有: int( (sw-1)(dw-1)/dw ) <=sw-2
(sw-1)
(dw-1)/dw <(sw-1)
(dw-1) /dw<1
(dw-1)
比如,按这个公式的一个简单实现: (缩放效果见前面的"二次线性插值(近似公式)"图示)

voidPicZoom_ftBilinear_Common(constTPicRegion& Dst,constTPicRegion& Src)
{
if( (0 == Dst.width)||(0 == Dst.height)
||(2>Src.width)||(2>Src.height))return;

longxrIntFloat_16=((Src.width-1)<<16)/Dst.width;
longyrIntFloat_16=((Src.height-1)<<16)/Dst.height;

unsignedlongdst_width=Dst.width;
longSrc_byte_width=Src.byte_width;
TARGB32* pDstLine=Dst.pdata;
longsrcy_16=0;
for(unsignedlongy=0;y
{
unsigned
longv_8=(srcy_16 & 0xFFFF)>>8;
TARGB32* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
longsrcx_16=0;
for(unsignedlongx=0;x
{
TARGB32* PColor0=&PSrcLineColor[srcx_16>>16];
Bilinear_Fast_Common(PColor0,(TARGB32*)((TUInt8*)(PColor0)+Src_byte_width),(srcx_16 & 0xFFFF)>>8,v_8,&pDstLine[x]);
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
}

)

G:利用单指令多数据处理的MMX指令一般都可以加快颜色的运算;在使用MMX改写之前,利用
32bit寄存器(或变量)来模拟单指令多数据处理;
数据储存原理:一个颜色数据分量只有一个字节,用2个字节来储存单个颜色分量的计算结果,
对于很多颜色计算来说精度就够了;那么一个32bit寄存器(或变量)就可以储存2个计算出的
临时颜色分量;从而达到了单个指令两路数据处理的目的;
单个指令两路数据处理的计算:
乘法: ((0x00AAa)<<16) | (0x00BBa) = 0x00AA00BB * a
可见只要保证0x00AAa和0x00BBa都小于(1<<16)那么乘法可以直接使用无符号数乘法了
加法: ((0x00AA+0x00CC)<<16) | (0x00BB+0x00DD) = 0x00AA00BB + 0x00CC00DD
可见只要0x00AA+0x00CC和0x00BB+0x00DD小于(1<<16)那么加法可以直接使用无符号数加法了
(移位、减法等稍微复杂一点,因为这里没有用到就不推倒运算公式了)

inlinevoidBilinear_Fast_Common(TARGB32* PColor0,TARGB32* PColor1,unsignedlongu_8,unsignedlongv_8,TARGB32* result)
{
unsignedlongpm3_8=(u_8*v_8)>>8;
unsignedlongpm2_8=u_8-pm3_8;
unsignedlongpm1_8=v_8-pm3_8;
unsignedlongpm0_8=256-pm1_8-pm2_8-pm3_8;

unsignedlongColor=(unsignedlong)(PColor0);
unsignedlongBR=(Color & 0x00FF00FF)*pm0_8;
unsignedlongGA=((Color & 0xFF00FF00)>>8)pm0_8;
Color=((unsignedlong
)(PColor0))[1];
GA+=((Color & 0xFF00FF00)>>8)pm2_8;
BR+=(Color & 0x00FF00FF)
pm2_8;
Color=(unsignedlong)(PColor1);
GA+=((Color & 0xFF00FF00)>>8)*pm1_8;
BR+=(Color & 0x00FF00FF)pm1_8;
Color=((unsignedlong
)(PColor1))[1];
GA+=((Color & 0xFF00FF00)>>8)*pm3_8;
BR+=(Color & 0x00FF00FF)*pm3_8;

(unsignedlong)(result)=(GA & 0xFF00FF00)|((BR & 0xFF00FF00)>>8);
}

inlinevoidBilinear_Border_Common(constTPicRegion& pic,constlongx_16,constlongy_16,TARGB32* result)
{
longx=(x_16>>16);
longy=(y_16>>16);
unsignedlongu_16=((unsignedshort)(x_16));
unsignedlongv_16=((unsignedshort)(y_16));

TARGB32 pixel[4];
pixel[0]=Pixels_Bound(pic,x,y);
pixel[1]=Pixels_Bound(pic,x+1,y);
pixel[2]=Pixels_Bound(pic,x,y+1);
pixel[3]=Pixels_Bound(pic,x+1,y+1);

Bilinear_Fast_Common(&pixel[0],&pixel[2],u_16>>8,v_16>>8,result);
}

voidPicZoom_Bilinear_Common(constTPicRegion& Dst,constTPicRegion& Src)
{
if( (0 == Dst.width)||(0 == Dst.height)
||(0 == Src.width)||(0 == Src.height))return;

longxrIntFloat_16=((Src.width)<<16)/Dst.width+1;
longyrIntFloat_16=((Src.height)<<16)/Dst.height+1;
constlongcsDErrorX=-(1<<15)+(xrIntFloat_16>>1);
constlongcsDErrorY=-(1<<15)+(yrIntFloat_16>>1);

unsignedlongdst_width=Dst.width;

//计算出需要特殊处理的边界
longborder_y0=-csDErrorY/yrIntFloat_16+1;//y0+yyr>=0; y0=csDErrorY => y>=-csDErrorY/yr
if(border_y0>=Dst.height) border_y0=Dst.height;
longborder_x0=-csDErrorX/xrIntFloat_16+1;
if(border_x0>=Dst.width ) border_x0=Dst.width;
longborder_y1=(((Src.height-2)<<16)-csDErrorY)/yrIntFloat_16+1;//y0+y
yr<=(height-2) => y<=(height-2-csDErrorY)/yr
if(border_y1 longborder_x1=(((Src.width-2)<<16)-csDErrorX)/xrIntFloat_16+1;
if(border_x1
TARGB32* pDstLine=Dst.pdata;
longSrc_byte_width=Src.byte_width;
longsrcy_16=csDErrorY;
longy;
for(y=0;y
{

longsrcx_16=csDErrorX;
for(unsignedlongx=0;x
{
Bilinear_Border_Common(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
for(y=border_y0;y
{

longsrcx_16=csDErrorX;
longx;
for(x=0;x
{
Bilinear_Border_Common(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}

{
unsignedlongv_8=(srcy_16 & 0xFFFF)>>8;
TARGB32* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
for(unsignedlongx=border_x0;x
{
TARGB32* PColor0=&PSrcLineColor[srcx_16>>16];
TARGB32* PColor1=(TARGB32*)((TUInt8*)(PColor0)+Src_byte_width);
Bilinear_Fast_Common(PColor0,PColor1,(srcx_16 & 0xFFFF)>>8,v_8,&pDstLine[x]);
srcx_16+=xrIntFloat_16;
}
}

for(x=border_x1;x
{
Bilinear_Border_Common(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
for(y=border_y1;y
{

longsrcx_16=csDErrorX;
for(unsignedlongx=0;x
{
Bilinear_Border_Common(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
}

//速度测试:
//==============================================================================
// PicZoom_BilInear_Common 65.3 fps

H:使用MMX指令改写:PicZoom_Bilinear_MMX

inlinevoidBilinear_Fast_MMX(TARGB32* PColor0,TARGB32* PColor1,unsignedlongu_8,unsignedlongv_8,TARGB32* result)
{
asm
{
MOVD MM6,v_8
MOVD MM5,u_8
mov edx,PColor0
mov eax,PColor1
PXOR mm7,mm7

MOVD MM2,dword ptr [eax]
MOVD MM0,dword ptr [eax+4]
PUNPCKLWD MM5,MM5
PUNPCKLWD MM6,MM6
MOVD MM3,dword ptr [edx]
MOVD MM1,dword ptr [edx+4]
PUNPCKLDQ MM5,MM5
PUNPCKLBW MM0,MM7
PUNPCKLBW MM1,MM7
PUNPCKLBW MM2,MM7
PUNPCKLBW MM3,MM7
PSUBw MM0,MM2
PSUBw MM1,MM3
PSLLw MM2,8
PSLLw MM3,8
PMULlw MM0,MM5
PMULlw MM1,MM5
PUNPCKLDQ MM6,MM6
PADDw MM0,MM2
PADDw MM1,MM3

PSRLw MM0,8
PSRLw MM1,8
PSUBw MM0,MM1
PSLLw MM1,8
PMULlw MM0,MM6
mov eax,result
PADDw MM0,MM1

PSRLw MM0,8
PACKUSwb MM0,MM7
movd [eax],MM0
//emms
}
}

voidBilinear_Border_MMX(constTPicRegion& pic,constlongx_16,constlongy_16,TARGB32* result)
{
longx=(x_16>>16);
longy=(y_16>>16);
unsignedlongu_16=((unsignedshort)(x_16));
unsignedlongv_16=((unsignedshort)(y_16));

TARGB32 pixel[4];
pixel[0]=Pixels_Bound(pic,x,y);
pixel[1]=Pixels_Bound(pic,x+1,y);
pixel[2]=Pixels_Bound(pic,x,y+1);
pixel[3]=Pixels_Bound(pic,x+1,y+1);

Bilinear_Fast_MMX(&pixel[0],&pixel[2],u_16>>8,v_16>>8,result);
}

voidPicZoom_Bilinear_MMX(constTPicRegion& Dst,constTPicRegion& Src)
{
if( (0 == Dst.width)||(0 == Dst.height)
||(0 == Src.width)||(0 == Src.height))return;

longxrIntFloat_16=((Src.width)<<16)/Dst.width+1;
longyrIntFloat_16=((Src.height)<<16)/Dst.height+1;
constlongcsDErrorX=-(1<<15)+(xrIntFloat_16>>1);
constlongcsDErrorY=-(1<<15)+(yrIntFloat_16>>1);

unsignedlongdst_width=Dst.width;

//计算出需要特殊处理的边界
longborder_y0=-csDErrorY/yrIntFloat_16+1;//y0+yyr>=0; y0=csDErrorY => y>=-csDErrorY/yr
if(border_y0>=Dst.height) border_y0=Dst.height;
longborder_x0=-csDErrorX/xrIntFloat_16+1;
if(border_x0>=Dst.width ) border_x0=Dst.width;
longborder_y1=(((Src.height-2)<<16)-csDErrorY)/yrIntFloat_16+1;//y0+y
yr<=(height-2) => y<=(height-2-csDErrorY)/yr
if(border_y1 longborder_x1=(((Src.width-2)<<16)-csDErrorX)/xrIntFloat_16+1;
if(border_x1
TARGB32* pDstLine=Dst.pdata;
longSrc_byte_width=Src.byte_width;
longsrcy_16=csDErrorY;
longy;
for(y=0;y
{

longsrcx_16=csDErrorX;
for(unsignedlongx=0;x
{
Bilinear_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
for(y=border_y0;y
{

longsrcx_16=csDErrorX;
longx;
for(x=0;x
{
Bilinear_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}

{
unsignedlongv_8=(srcy_16 & 0xFFFF)>>8;
TARGB32* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
for(unsignedlongx=border_x0;x
{
TARGB32* PColor0=&PSrcLineColor[srcx_16>>16];
TARGB32* PColor1=(TARGB32*)((TUInt8*)(PColor0)+Src_byte_width);
Bilinear_Fast_MMX(PColor0,PColor1,(srcx_16 & 0xFFFF)>>8,v_8,&pDstLine[x]);
srcx_16+=xrIntFloat_16;
}
}

for(x=border_x1;x
{
Bilinear_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
for(y=border_y1;y
{

longsrcx_16=csDErrorX;
for(unsignedlongx=0;x
{
Bilinear_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
asm emms
}

//速度测试:
//==============================================================================
// PicZoom_BilInear_MMX 132.9 fps

H’ 对BilInear_MMX简单改进:PicZoom_Bilinear_MMX_Ex

voidPicZoom_Bilinear_MMX_Ex(constTPicRegion& Dst,constTPicRegion& Src)
{
if( (0 == Dst.width)||(0 == Dst.height)
||(0 == Src.width)||(0 == Src.height))return;

longxrIntFloat_16=((Src.width)<<16)/Dst.width+1;
longyrIntFloat_16=((Src.height)<<16)/Dst.height+1;
constlongcsDErrorX=-(1<<15)+(xrIntFloat_16>>1);
constlongcsDErrorY=-(1<<15)+(yrIntFloat_16>>1);

unsignedlongdst_width=Dst.width;

//计算出需要特殊处理的边界
longborder_y0=-csDErrorY/yrIntFloat_16+1;//y0+yyr>=0; y0=csDErrorY => y>=-csDErrorY/yr
if(border_y0>=Dst.height) border_y0=Dst.height;
longborder_x0=-csDErrorX/xrIntFloat_16+1;
if(border_x0>=Dst.width ) border_x0=Dst.width;
longborder_y1=(((Src.height-2)<<16)-csDErrorY)/yrIntFloat_16+1;//y0+y
yr<=(height-2) => y<=(height-2-csDErrorY)/yr
if(border_y1 longborder_x1=(((Src.width-2)<<16)-csDErrorX)/xrIntFloat_16+1;
if(border_x1
TARGB32* pDstLine=Dst.pdata;
longSrc_byte_width=Src.byte_width;
longsrcy_16=csDErrorY;
longy;
for(y=0;y
{

longsrcx_16=csDErrorX;
for(unsignedlongx=0;x
{
Bilinear_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}

for(y=border_y0;y
{

longsrcx_16=csDErrorX;
longx;
for(x=0;x
{
Bilinear_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}

{
longdst_width_fast=border_x1-border_x0;
if(dst_width_fast>0)
{
unsignedlongv_8=(srcy_16 & 0xFFFF)>>8;
TARGB32* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
TARGB32* PSrcLineColorNext= (TARGB32*)((TUInt8*)(PSrcLineColor)+Src_byte_width) ;
TARGB32* pDstLine_Fast=&pDstLine[border_x0];
asm
{
movd mm6,v_8
pxor mm7,mm7//mm7=0
PUNPCKLWD MM6,MM6
PUNPCKLDQ MM6,MM6//mm6=v_8

mov esi,PSrcLineColor
mov ecx,PSrcLineColorNext
mov edx,srcx_16
mov ebx,dst_width_fast
mov edi,pDstLine_Fast
lea edi,[edi+ebx*4]
push ebp
mov ebp,xrIntFloat_16
neg ebx

loop_start:

mov eax,edx
shl eax,16
shr eax,24
//== movzx eax,dh //eax=u_8
MOVD MM5,eax
mov eax,edx
shr eax,16//srcx_16>>16

MOVD MM2,dword ptr [ecx+eax4]
MOVD MM0,dword ptr [ecx+eax
4+4]
PUNPCKLWD MM5,MM5
MOVD MM3,dword ptr [esi+eax4]
MOVD MM1,dword ptr [esi+eax
4+4]
PUNPCKLDQ MM5,MM5//mm5=u_8
PUNPCKLBW MM0,MM7
PUNPCKLBW MM1,MM7
PUNPCKLBW MM2,MM7
PUNPCKLBW MM3,MM7
PSUBw MM0,MM2
PSUBw MM1,MM3
PSLLw MM2,8
PSLLw MM3,8
PMULlw MM0,MM5
PMULlw MM1,MM5
PADDw MM0,MM2
PADDw MM1,MM3

PSRLw MM0,8
PSRLw MM1,8
PSUBw MM0,MM1
PSLLw MM1,8
PMULlw MM0,MM6
PADDw MM0,MM1

PSRLw MM0,8
PACKUSwb MM0,MM7
MOVd dword ptr [edi+ebx*4],MM0//write DstColor

add edx,ebp//srcx_16+=xrIntFloat_16
inc ebx
jnz loop_start

pop ebp
mov srcx_16,edx
}
}
}

for(x=border_x1;x
{
Bilinear_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
for(y=border_y1;y
{

longsrcx_16=csDErrorX;
for(unsignedlongx=0;x
{
Bilinear_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
asm emms
}

//速度测试:
//==============================================================================
// PicZoom_Bilinear_MMX_Ex 157.0 fps

I: 把测试成绩放在一起:

//CPU: AMD64x2 4200+(2.37G) zoom 800600 to 1024768
//==============================================================================
// StretchBlt 232.7 fps
// PicZoom3_SSE 711.7 fps
//
// PicZoom_BilInear0 8.3 fps
// PicZoom_BilInear1 17.7 fps
// PicZoom_BilInear2 43.4 fps
// PicZoom_BilInear_Common 65.3 fps
// PicZoom_BilInear_MMX 132.9 fps
// PicZoom_BilInear_MMX_Ex 157.0 fps

补充Intel Core2 4400上的测试成绩:

//CPU: Intel Core2 4400(2.00G) zoom 800600 to 1024768
//==============================================================================
// PicZoom3_SSE 1099.7 fps
//
// PicZoom_BilInear0 10.7 fps
// PicZoom_BilInear1 24.2 fps
// PicZoom_BilInear2 54.3 fps
// PicZoom_BilInear_Common 59.8 fps
// PicZoom_BilInear_MMX 118.4 fps
// PicZoom_BilInear_MMX_Ex 142.9 fps

三次卷积插值:

J: 二次线性插值缩放出的图片很多时候让人感觉变得模糊(术语叫低通滤波),特别是在放大
的时候;使用三次卷积插值来改善插值结果;三次卷积插值考虑映射点周围16个点(4x4)的颜色来
计算最终的混合颜色,如图;

P(0,0)所在像素为映射的点,加上它周围的15个点,按一定系数混合得到最终输出结果;

混合公式参见PicZoom_ThreeOrder0的实现;

插值曲线公式sin(xPI)/(xPI),如图:

三次卷积插值曲线sin(xPI)/(xPI) (其中PI=3.1415926…)

K:三次卷积插值缩放算法的一个参考实现:PicZoom_ThreeOrder0
该函数并没有做过多的优化,只是一个简单的浮点实现版本;

inlinedoubleSinXDivX(doublex)
{
//该函数计算插值曲线sin(xPI)/(xPI)的值 //PI=3.1415926535897932385;
//下面是它的近似拟合表达式
constfloata = -1;//a还可以取 a=-2,-1,-0.75,-0.5等等,起到调节锐化或模糊程度的作用

if(x<0) x=-x;//x=abs(x);
doublex2=xx;
doublex3=x2
x;
if(x<=1)
return(a+2)x3 - (a+3)x2 + 1;
elseif(x<=2)
returnax3 - (5a)x2 + (8a)x - (4a);
else
return0;
}

inline TUInt8 border_color(longColor)
{
if(Color<=0)
return0;
elseif(Color>=255)
return255;
else
returnColor;
}

voidThreeOrder0(constTPicRegion& pic,constfloatfx,constfloatfy,TARGB32* result)
{
longx0=(long)fx;if(x0>fx) --x0;//x0=floor(fx);
longy0=(long)fy;if(y0>fy) --y0;//y0=floor(fy);
floatfu=fx-x0;
floatfv=fy-y0;

TARGB32 pixel[16];
longi,j;

for(i=0;i<4;++i)
{
for(j=0;j<4;++j)
{
longx=x0-1+j;
longy=y0-1+i;
pixel[i*4+j]=Pixels_Bound(pic,x,y);
}
}

floatafu[4],afv[4];
//
afu[0]=SinXDivX(1+fu);
afu[1]=SinXDivX(fu);
afu[2]=SinXDivX(1-fu);
afu[3]=SinXDivX(2-fu);
afv[0]=SinXDivX(1+fv);
afv[1]=SinXDivX(fv);
afv[2]=SinXDivX(1-fv);
afv[3]=SinXDivX(2-fv);

floatsR=0,sG=0,sB=0,sA=0;
for(i=0;i<4;++i)
{
floataR=0,aG=0,aB=0,aA=0;
for(longj=0;j<4;++j)
{
aA+=afu[j]pixel[i4+j].a;
aR+=afu[j]pixel[i4+j].r;
aG+=afu[j]pixel[i4+j].g;
aB+=afu[j]pixel[i4+j].b;
}
sA+=aAafv[i];
sR+=aR
afv[i];
sG+=aGafv[i];
sB+=aB
afv[i];
}

result->a=border_color((long)(sA+0.5));
result->r=border_color((long)(sR+0.5));
result->g=border_color((long)(sG+0.5));
result->b=border_color((long)(sB+0.5));
}

voidPicZoom_ThreeOrder0(constTPicRegion& Dst,constTPicRegion& Src)
{
if( (0 == Dst.width)||(0 == Dst.height)
||(0 == Src.width)||(0 == Src.height))return;

unsignedlongdst_width=Dst.width;
TARGB32* pDstLine=Dst.pdata;
for(unsignedlongy=0;y
{

floatsrcy=(y+0.4999999)*Src.height/Dst.height-0.5;
for(unsignedlongx=0;x
{

floatsrcx=(x+0.4999999)Src.width/Dst.width-0.5;
ThreeOrder0(Src,srcx,srcy,&pDstLine[x]);
}
((TUInt8
&)pDstLine)+=Dst.byte_width;
}
}

//速度测试:
//==============================================================================
// PicZoom_ThreeOrder0 3.6 fps

L: 使用定点数来优化缩放函数;边界和内部分开处理;对SinXDivX做一个查找表;对border_color做一个查找表;

staticlongSinXDivX_Table_8[(2<<8)+1];
class_CAutoInti_SinXDivX_Table {
private:
void_Inti_SinXDivX_Table()
{
for(longi=0;i<=(2<<8);++i)
SinXDivX_Table_8[i]=long(0.5+256SinXDivX(i(1.0/(256))))*1;
};
public:
_CAutoInti_SinXDivX_Table() { _Inti_SinXDivX_Table(); }
};
static_CAutoInti_SinXDivX_Table __tmp_CAutoInti_SinXDivX_Table;

//颜色查表
staticTUInt8 _color_table[2563];
staticconstTUInt8
color_table=&_color_table[256];
class_CAuto_inti_color_table
{
public:
_CAuto_inti_color_table() {
for(inti=0;i<256*3;++i)
_color_table[i]=border_color(i-256);
}
};
static_CAuto_inti_color_table _Auto_inti_color_table;

voidThreeOrder_Fast_Common(constTPicRegion& pic,constlongx_16,constlongy_16,TARGB32* result)
{
unsignedlongu_8=(unsignedchar)((x_16)>>8);
unsignedlongv_8=(unsignedchar)((y_16)>>8);
constTARGB32* pixel=&Pixels(pic,(x_16>>16)-1,(y_16>>16)-1);
longpic_byte_width=pic.byte_width;

longau_8[4],av_8[4];
//
au_8[0]=SinXDivX_Table_8[(1<<8)+u_8];
au_8[1]=SinXDivX_Table_8[u_8];
au_8[2]=SinXDivX_Table_8[(1<<8)-u_8];
au_8[3]=SinXDivX_Table_8[(2<<8)-u_8];
av_8[0]=SinXDivX_Table_8[(1<<8)+v_8];
av_8[1]=SinXDivX_Table_8[v_8];
av_8[2]=SinXDivX_Table_8[(1<<8)-v_8];
av_8[3]=SinXDivX_Table_8[(2<<8)-v_8];

longsR=0,sG=0,sB=0,sA=0;
for(longi=0;i<4;++i)
{
longaA=au_8[0]*pixel[0].a + au_8[1]*pixel[1].a + au_8[2]*pixel[2].a + au_8[3]*pixel[3].a;
longaR=au_8[0]*pixel[0].r + au_8[1]*pixel[1].r + au_8[2]*pixel[2].r + au_8[3]*pixel[3].r;
longaG=au_8[0]*pixel[0].g + au_8[1]*pixel[1].g + au_8[2]*pixel[2].g + au_8[3]pixel[3].g;
longaB=au_8[0]
pixel[0].b + au_8[1]pixel[1].b + au_8[2]pixel[2].b + au_8[3]pixel[3].b;
sA+=aA
av_8[i];
sR+=aRav_8[i];
sG+=aGav_8[i];
sB+=aBav_8[i];
((TUInt8&)pixel)+=pic_byte_width;
}

result->a=color_table[sA>>16];
result->r=color_table[sR>>16];
result->g=color_table[sG>>16];
result->b=color_table[sB>>16];
}

voidThreeOrder_Border_Common(constTPicRegion& pic,constlongx_16,constlongy_16,TARGB32* result)
{
longx0_sub1=(x_16>>16)-1;
longy0_sub1=(y_16>>16)-1;
unsignedlongu_16_add1=((unsignedshort)(x_16))+(1<<16);
unsignedlongv_16_add1=((unsignedshort)(y_16))+(1<<16);

TARGB32 pixel[16];
longi;

for(i=0;i<4;++i)
{
longy=y0_sub1+i;
pixel[i4+0]=Pixels_Bound(pic,x0_sub1+0,y);
pixel[i
4+1]=Pixels_Bound(pic,x0_sub1+1,y);
pixel[i4+2]=Pixels_Bound(pic,x0_sub1+2,y);
pixel[i
4+3]=Pixels_Bound(pic,x0_sub1+3,y);
}

TPicRegion npic;
npic.pdata =&pixel[0];
npic.byte_width=4*sizeof(TARGB32);
//npic.width =4;
//npic.height =4;
ThreeOrder_Fast_Common(npic,u_16_add1,v_16_add1,result);
}

voidPicZoom_ThreeOrder_Common(constTPicRegion& Dst,constTPicRegion& Src)
{
if( (0 == Dst.width)||(0 == Dst.height)
||(0 == Src.width)||(0 == Src.height))return;

longxrIntFloat_16=((Src.width)<<16)/Dst.width+1;
longyrIntFloat_16=((Src.height)<<16)/Dst.height+1;
constlongcsDErrorX=-(1<<15)+(xrIntFloat_16>>1);
constlongcsDErrorY=-(1<<15)+(yrIntFloat_16>>1);

unsignedlongdst_width=Dst.width;

//计算出需要特殊处理的边界
longborder_y0=((1<<16)-csDErrorY)/yrIntFloat_16+1;//y0+yyr>=1; y0=csDErrorY => y>=(1-csDErrorY)/yr
if(border_y0>=Dst.height) border_y0=Dst.height;
longborder_x0=((1<<16)-csDErrorX)/xrIntFloat_16+1;
if(border_x0>=Dst.width ) border_x0=Dst.width;
longborder_y1=(((Src.height-3)<<16)-csDErrorY)/yrIntFloat_16+1;//y0+y
yr<=(height-3) => y<=(height-3-csDErrorY)/yr
if(border_y1 longborder_x1=(((Src.width-3)<<16)-csDErrorX)/xrIntFloat_16+1;;
if(border_x1
TARGB32* pDstLine=Dst.pdata;
longsrcy_16=csDErrorY;
longy;
for(y=0;y
{

longsrcx_16=csDErrorX;
for(unsignedlongx=0;x
{
ThreeOrder_Border_Common(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
for(y=border_y0;y
{

longsrcx_16=csDErrorX;
longx;
for(x=0;x
{
ThreeOrder_Border_Common(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
for(x=border_x0;x
{
ThreeOrder_Fast_Common(Src,srcx_16,srcy_16,&pDstLine[x]);
//fast !
srcx_16+=xrIntFloat_16;
}
for(x=border_x1;x
{
ThreeOrder_Border_Common(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
for(y=border_y1;y
{

longsrcx_16=csDErrorX;
for(unsignedlongx=0;x
{
ThreeOrder_Border_Common(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
}

//速度测试:
//==============================================================================
// PicZoom_ThreeOrder_Common 16.9 fps

M: 用MMX来优化ThreeOrder_Common函数:ThreeOrder_MMX

typedef unsignedlongTMMXData32;
staticTMMXData32 SinXDivX_Table_MMX[(2<<8)+1];
class_CAutoInti_SinXDivX_Table_MMX {
private:
void_Inti_SinXDivX_Table_MMX()
{
for(longi=0;i<=(2<<8);++i)
{
unsignedshortt=long(0.5+(1<<14)SinXDivX(i(1.0/(256))));
unsignedlongtl=t | (((unsignedlong)t)<<16);
SinXDivX_Table_MMX[i]=tl;
}
};
public:
_CAutoInti_SinXDivX_Table_MMX() { _Inti_SinXDivX_Table_MMX(); }
};
static_CAutoInti_SinXDivX_Table_MMX __tmp_CAutoInti_SinXDivX_Table_MMX;

void__declspec(naked) _private_ThreeOrder_Fast_MMX()
{
asm
{
movd mm1,dword ptr [edx]
movd mm2,dword ptr [edx+4]
movd mm3,dword ptr [edx+8]
movd mm4,dword ptr [edx+12]
movd mm5,dword ptr [(offset SinXDivX_Table_MMX)+2564+eax4]
movd mm6,dword ptr [(offset SinXDivX_Table_MMX)+eax4]
punpcklbw mm1,mm7
punpcklbw mm2,mm7
punpcklwd mm5,mm5
punpcklwd mm6,mm6
psllw mm1,7
psllw mm2,7
pmulhw mm1,mm5
pmulhw mm2,mm6
punpcklbw mm3,mm7
punpcklbw mm4,mm7
movd mm5,dword ptr [(offset SinXDivX_Table_MMX)+256
4+ecx4]
movd mm6,dword ptr [(offset SinXDivX_Table_MMX)+512
4+ecx*4]
punpcklwd mm5,mm5
punpcklwd mm6,mm6
psllw mm3,7
psllw mm4,7
pmulhw mm3,mm5
pmulhw mm4,mm6
paddsw mm1,mm2
paddsw mm3,mm4
movd mm6,dword ptr [ebx]//v
paddsw mm1,mm3
punpcklwd mm6,mm6

pmulhw mm1,mm6
add edx,esi//+pic.byte_width
paddsw mm0,mm1

ret
}
}

inlinevoidThreeOrder_Fast_MMX(constTPicRegion& pic,constlongx_16,constlongy_16,TARGB32* result)
{
asm
{
mov ecx,pic
mov eax,y_16
mov ebx,x_16
movzx edi,ah//v_8
mov edx,[ecx+TPicRegion::pdata]
shr eax,16
mov esi,[ecx+TPicRegion::byte_width]
dec eax
movzx ecx,bh//u_8
shr ebx,16
imul eax,esi
lea edx,[edx+ebx*4-4]
add edx,eax//pixel

mov eax,ecx
neg ecx

pxor mm7,mm7//0
//mov edx,pixel
pxor mm0,mm0//result=0
//lea eax,auv_7

lea ebx,[(offset SinXDivX_Table_MMX)+2564+edi4]
call _private_ThreeOrder_Fast_MMX
lea ebx,[(offset SinXDivX_Table_MMX)+edi4]
call _private_ThreeOrder_Fast_MMX
neg edi
lea ebx,[(offset SinXDivX_Table_MMX)+256
4+edi4]
call _private_ThreeOrder_Fast_MMX
lea ebx,[(offset SinXDivX_Table_MMX)+512
4+edi*4]
call _private_ThreeOrder_Fast_MMX

psraw mm0,3
mov eax,result
packuswb mm0,mm7
movd [eax],mm0
//emms
}
}

voidThreeOrder_Border_MMX(constTPicRegion& pic,constlongx_16,constlongy_16,TARGB32* result)
{
unsignedlongx0_sub1=(x_16>>16)-1;
unsignedlongy0_sub1=(y_16>>16)-1;
longu_16_add1=((unsignedshort)(x_16))+(1<<16);
longv_16_add1=((unsignedshort)(y_16))+(1<<16);

TARGB32 pixel[16];

for(longi=0;i<4;++i)
{
longy=y0_sub1+i;
pixel[i4+0]=Pixels_Bound(pic,x0_sub1 ,y);
pixel[i
4+1]=Pixels_Bound(pic,x0_sub1+1,y);
pixel[i4+2]=Pixels_Bound(pic,x0_sub1+2,y);
pixel[i
4+3]=Pixels_Bound(pic,x0_sub1+3,y);
}

TPicRegion npic;
npic.pdata =&pixel[0];
npic.byte_width=4*sizeof(TARGB32);
//npic.width =4;
//npic.height =4;
ThreeOrder_Fast_MMX(npic,u_16_add1,v_16_add1,result);
}

voidPicZoom_ThreeOrder_MMX(constTPicRegion& Dst,constTPicRegion& Src)
{
if( (0 == Dst.width)||(0 == Dst.height)
||(0 == Src.width)||(0 == Src.height))return;

longxrIntFloat_16=((Src.width)<<16)/Dst.width+1;
longyrIntFloat_16=((Src.height)<<16)/Dst.height+1;
constlongcsDErrorX=-(1<<15)+(xrIntFloat_16>>1);
constlongcsDErrorY=-(1<<15)+(yrIntFloat_16>>1);

unsignedlongdst_width=Dst.width;

//计算出需要特殊处理的边界
longborder_y0=((1<<16)-csDErrorY)/yrIntFloat_16+1;//y0+yyr>=1; y0=csDErrorY => y>=(1-csDErrorY)/yr
if(border_y0>=Dst.height) border_y0=Dst.height;
longborder_x0=((1<<16)-csDErrorX)/xrIntFloat_16+1;
if(border_x0>=Dst.width ) border_x0=Dst.width;
longborder_y1=(((Src.height-3)<<16)-csDErrorY)/yrIntFloat_16+1;//y0+y
yr<=(height-3) => y<=(height-3-csDErrorY)/yr
if(border_y1 longborder_x1=(((Src.width-3)<<16)-csDErrorX)/xrIntFloat_16+1;;
if(border_x1
TARGB32* pDstLine=Dst.pdata;
longsrcy_16=csDErrorY;
longy;
for(y=0;y
{

longsrcx_16=csDErrorX;
for(unsignedlongx=0;x
{
ThreeOrder_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
for(y=border_y0;y
{

longsrcx_16=csDErrorX;
longx;
for(x=0;x
{
ThreeOrder_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
for(x=border_x0;x
{
ThreeOrder_Fast_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);
//fast MMX !
srcx_16+=xrIntFloat_16;
}
for(x=border_x1;x
{
ThreeOrder_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
for(y=border_y1;y
{

longsrcx_16=csDErrorX;
for(unsignedlongx=0;x
{
ThreeOrder_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);
//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
asm emms
}

//速度测试:
//==============================================================================
// PicZoom_ThreeOrder_MMX 34.3 fps

N:将测试结果放到一起:

//CPU: AMD64x2 4200+(2.37G) zoom 800600 to 1024768
//==============================================================================
// StretchBlt 232.7 fps
// PicZoom3_SSE 711.7 fps
// PicZoom_BilInear_MMX_Ex 157.0 fps
//
// PicZoom_ThreeOrder0 3.6 fps
// PicZoom_ThreeOrder_Common 16.9 fps
// PicZoom_ThreeOrder_MMX 34.3 fps

补充Intel Core2 4400上的测试成绩:

//CPU: Intel Core2 4400(2.00G) zoom 800600 to 1024768
//==============================================================================
// PicZoom3_SSE 1099.7 fps
// PicZoom_BilInear_MMX_Ex 142.9 fps
//
// PicZoom_ThreeOrder0 4.2 fps
// PicZoom_ThreeOrder_Common 17.6 fps
// PicZoom_ThreeOrder_MMX 34.4 fps

图像缩放算法(中篇)相关推荐

  1. 图形图像处理 —— 图像缩放算法

    转自:http://blog.chinaunix.net/space.php?uid=22915173&do=blog&id=2185545 摘要:首先给出一个基本的图像缩放算法,然后 ...

  2. 图像缩放算法(下篇)

    图像缩放算法(下篇) ================================= 转载别人的,但是这篇文章写得确实太好了,所以想分享出来. 原文地址:http://blog.chinaunix ...

  3. 几个图像缩放算法的比较

    几个图像缩放算法的比较 前段时间由于项目的需求,需要实现图像的缩放功能,期间查找了不少关于图像缩放算法的资料,现把自己的心得整理一下. 由于研究生期间没有选修过图像处理方面的课程,所以对图像缩放的原理 ...

  4. 计算机视觉-图像缩放算法-cuda实现

    一.CUDA CUDA是显卡厂商NVIDIA(英伟达)推出的运算平台,能够将数据数据复制到GPU,在GPU中进行计算,然后再返回给CPU端.CUDA将GPU称为设备侧或者Device,将CPU称为Ho ...

  5. [图像]图像缩放算法-双线性内插法

    原创文章,欢迎转载.转载请注明:转载自 祥的博客 原文链接:http://blog.csdn.net/humanking7/article/details/45014879 简介: 图像缩放算法–双线 ...

  6. 图像缩放算法及速度优化

    原文来自:博客园 小欣子 图像缩放算法及速度优化--(一)最近邻插值 图像缩放算法及速度优化--(二)双线性插值 --------------------以下为原文------------------ ...

  7. 基于FPGA 的图像缩放算法设计

    介绍双线性插值算法来实现图像缩放,FPGA 硬件实现方法,包括图像数据缓冲单元.插值系数生成单元以及插值计算单元等. 图像是人类感知世界的视觉基础,是人类获取信息.表达信息的重要手段.现在研究较多的是 ...

  8. 图像缩放算法_技术专栏|基于无人机LK光流算法的适用性及其优化方法探究

    点击上方蓝字关注我们 问题描述 ◆ ◆ ◆ 一般的LK光流算法存在一个比较明显的局限性.我们知道,一般的LK光流算法必须包含三个假设: (1)亮度恒定: (2)时间连续或者运动是小运动: (3)空间一 ...

  9. 图像缩放算法_opencv缩放算法

    1.opencv插值介绍 opencv提供resize函数用来做图像缩放,该函数有6个参数: (1)输入图像,Mat型 (2)输出图像,Mat型 (3)输出图像大小,可用cv::Size(out_im ...

最新文章

  1. Toast 位置的改变 和 Toast的简单用法
  2. @ControllerAdvice注解(全局异常捕获)
  3. Python可视化库Matplotlib绘图入门详解
  4. arcgis报错常用解决方法
  5. c语言socket段错误,(Qtcpsocket)退出程序时提示段错误的解决
  6. java调用notify方法_Java线程,下面程序不知怎么调用notify方法唤醒test1,请指正
  7. IE8的样式兼容性适应方法【转】
  8. 大白话理解LSTM神经网络(附实例讲解)
  9. RSA密钥对生成过程具体实践
  10. 配置和google浏览器版本一直的webdriver
  11. __raw_writel, writel_relaxed 和 writel的区别
  12. win10打开程序响应很慢_win10优化设置最全教程(上)。
  13. 阿里前端面经(笔试+3轮技术面+HR面)
  14. 个人期货交割日(国内期货交割日)
  15. java 访问网址并将返回结果的json数组转换为List<String>
  16. mysql 查询 select_mysql 查询select语句汇总
  17. SpringCloud分布式微服务搭建(一)
  18. 第4代计算机的cpu,Haswell四核才强悍!第四代高端i7本推荐
  19. python pptx_python-pptx
  20. SEO人员,别再东施效颦了!

热门文章

  1. 铁路计算机应用期刊级别,铁路单位评审高级职称可以发表哪些期刊呢?
  2. OTP语音芯片ic的工作原理,以及目前的现状和技术发展路线是什么?flash型
  3. 【Druid】(八)Apache Druid 核心插件 Kafka Indexing Service SLS Indexing Service
  4. 解决打开CHM文件后,右侧显示空白
  5. VMware虚拟机拷贝mac冲突解决方法(及软件安装)
  6. Jetson-Xavier-NX刷机+pytorch环境配置+yolov5运行
  7. 成为一名合格的java工程师
  8. 0 Java语言简介
  9. 虚拟机给openwrt添加硬盘并且配置smb
  10. 【转载】没有urllib2怎么办?安装不了urllib2怎么办?