高质量的快速的图像缩放——二次线性插值和三次卷积插值限制条件:为了便于讨论,这里只处理32bit的ARGB颜色;代码使用C++;涉及到汇编优化的时候假定为x86平台;使用的编译器为vc2005;为了代码的可读性,没有加入异常处理代码;测试使用的CPU为AMD64x24200+(2.37G)和IntelCore24400(2.00G);速度测试说明:只测试内存数据到内存数据的缩放测试图片都是800*600缩放到1024*768;fps表示每秒钟的帧数,值越大表示函数越快A:近邻取样插值、二次线性插值、三次卷积插值缩放效果对比原图近邻取样缩放到0.6倍近邻取样缩放到1.6倍二次线性插值缩放到0.6倍二次线性插值缩放到1.6倍三次卷积插值缩放到0.6倍三次卷积插值缩放到1.6倍原图近邻取样缩放到8倍二次线性插值缩放到8倍三次卷积插值缩放到8倍二次线性插值(近似公式)近邻取样插值缩放简单、速度快,但很多时候缩放出的图片质量比较差(特别是对于人物、景色等),图片的缩放有比较明显的锯齿;使用二次或更高次插值有利于改善缩放效果;B:首先定义图像数据结构:#defineasm__asmtypedefunsignedcharTUInt8;//[0..255]structTARGB32//32bitcolor{TUInt8b,g,r,a;//aisalpha};structTPicRegion//一块颜色数据区的描述,便于参数传递{TARGB32*pdata;//颜色数据首地址longbyte_width;//一行数据的物理宽度(字节宽度);//abs(byte_width)有可能大于等于width*sizeof(TARGB32);longwidth;//像素宽度longheight;//像素高度};//那么访问一个点的函数可以写为:inlineTARGB32&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=Dx*SW/DWSy=Dy*SH/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)+Color2*u;tmpColor1=Color1*(1-u)+Color3*u;DstColor=tmpColor0*(1-v)+tmpColor2*v;展开公式为:pm0=(1-u)*(1-v);pm1=v*(1-u);pm2=u*(1-v);pm3=u*v;则颜色混合公式为:DstColor=Color0*pm0+Color1*pm1+Color2*pm2+Color3*pm3;参数函数图示:二次线性插值函数图示对于上面的公式,它将图片向右下各移动了半个像素,需要对此做一个修正;=Sx=(Dx+0.5)*SW/DW-0.5;Sy=(Dy+0.5)*SH/DH-0.5;而实际的程序,还需要考虑到边界(访问源图片可能超界)对于算法的影响,边界的处理可能有各种方案(不处理边界或边界回绕或边界饱和或边界映射或用背景颜色混合等;文章中默认使用边界饱和来处理超界);比如:边界饱和函数://访问一个点的函数,(x,y)坐标可能超出图片边界;//边界处理模式:边界饱和inlineTARGB32Pixels_Bound(constTPicRegion&pic,longx,longy){//assert((pic.width0)&&(pic.height0));boolIsInPic=true;if(x0){x=0;IsInPic=false;}elseif(x=pic.width){x=pic.width-1;IsInPic=false;}if(y0){y=0;IsInPic=false;}elseif(y=pic.height){y=pic.height-1;IsInPic=false;}TARGB32result=Pixels(pic,x,y);if(!IsInPic)result.a=0;returnresult;}D:二次线性插值缩放算法的一个参考实现:PicZoom_BilInear0该函数并没有做什么优化,只是一个简单的浮点实现版本;inlinevoidBilinear0(constTPicRegion&pic,floatfx,floatfy,TARGB32*result){longx=(long)fx;if(xfx)--x;//x=floor(fx);longy=(long)fy;if(yfy)--y;//y=floor(fy);TARGB32Color0=Pixels_Bound(pic,x,y);TARGB32Color2=Pixels_Bound(pic,x+1,y);TARGB32Color1=Pixels_Bound(pic,x,y+1);TARGB32Color3=Pixels_Bound(pic,x+1,y+1);floatu=fx-x;floatv=fy-y;floatpm3=u*v;floatpm2=u*(1-v);floatpm1=v*(1-u);floatpm0=(1-u)*(1-v);result-a=(pm0*Color0.a+pm1*Color1.a+pm2*Color2.a+pm3*Color3.a);result-r=(pm0*Color0.r+pm1*Color1.r+pm2*Color2.r+pm3*Color3.r);result-g=(pm0*Color0.g+pm1*Color1.g+pm2*Color2.g+pm3*Color3.g);result-b=(pm0*Color0.b+pm1*Color1.b+pm2*Color2.b+pm3*Color3.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;yDst.height;++y){floatsrcy=(y+0.4999999)*Src.height/Dst.height-0.5;for(unsignedlongx=0;xdst_width;++x){floatsrcx=(x+0.4999999)*Src.width/Dst.width-0.5;Bilinear0(Src,srcx,srcy,&pDstLine[x]);}((TUInt8*&)pDstLine)+=Dst.byte_width;}}//////////////////////////////////////////////////////////////////////////////////速度测试://==============================================================================//PicZoom_BilInear08.3fps////////////////////////////////////////////////////////////////////////////////E:浮点计算改为定点数实现:PicZoom_BilInear1inlinevoidBilinear1(constTPicRegion&pic,constlongx_16,constlongy_16,TARGB32*result){longx=x_1616;longy=y_1616;TARGB32Color0=Pixels_Bound(pic,x,y);TARGB32Color2=Pixels_Bound(pic,x+1,y);TARGB32Color1=Pixels_Bound(pic,x,y+1);TARGB32Color3=Pixels_Bound(pic,x+1,y+1);unsignedlongu_8=(x_16&0xFFFF)8;unsignedlongv_8=(y_16&0xFFFF)8;unsignedlongpm3_16=(u_8*v_8);unsignedlongpm2_16=(u_8*(unsignedlong)(255-v_8));unsignedlongpm1_16=(v_8*(unsignedlong)(255-u_8));unsignedlongpm0_16=((255-u_8)*(255-v_8));result-a=((pm0_16*Color0.a+pm1_16*Color1.a+pm2_16*Color2.a+pm3_16*Color3.a)16);result-r=((pm0_16*Color0.r+pm1_16*Color1.r+pm2_16*Color2.r+pm3_16*Color3.r)16);result-g=((pm0_16*Color0.g+pm1_16*Color1.g+pm2_16*Color2.g+pm3_16*Color3.g)16);result-b=((pm0_16*Color0.b+pm1_16*Color1.b+pm2_16*Color2.b+pm3_16*Color3.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=-(115)+(xrIntFloat_161);constlongcsDErrorY=-(115)+(yrIntFloat_161);unsignedlongdst_width=Dst.width;TARGB32*pDstLine=Dst.pdata;longsrcy_16=csDErrorY;longy;for(y=0;yDst.height;++y){longsrcx_16=csDErrorX;for(unsignedlongx=0;xdst_width;++x){Bilinear1(Src,srcx_16,srcy_16,&pDstLine[x]);//bordersrcx_16+=xrIntFloat_16;}srcy_16+=yrIntFloat_16;((TUInt8*&)pDstLine)+=Dst.byte_width;}}//