from:http://blog.csdn.net/housisong/article/details/1452249
图形图像处理-之-高质量的快速的图像缩放 中篇 二次线性插值和三次卷积插值
HouSisong@GMail.com 2006255.12.13
(2015.08.15 PicZoom_ftBilinear_Common更精确的边界公式推导)
(2009.03.07 可以到这里下载缩放算法的完整的可以编译的项目源代码: http://blog.csdn.net/housisong/archive/2009/03/07/3967270.aspx )
(2007.11.12 替换了二次线性插值的实现(以前偷懒使用了一个近似公式),改进后在图片边缘的插值效果更好(包括三次卷积插值的边界也更精确);
(2007.09.14 修正三次卷积的MMX版本中表的精度太低(7bit),造成卷积结果误差较大的问题,该版本提高了插值质量,并且速度加快12-25%)
(2007.09.07 PicZoom_ThreeOrder2和PicZoom_ThreeOrder_MMX在缩放的图片宽或高
小于3个像素的时候有一个Bug(边界计算错误);将unsigned long xrIntFloat_16,
yrIntFloat_16的定义改成long xrIntFloat_16,yrIntFloat_16就可以了)
(2007.07.02 ThreeOrder2_Fast一点小的改进,加快14%)
(2007.06.18 优化PicZoom_BilInear_MMX的实现(由138.5fps提高到147.9fps),
并添加更快的两路展开的实现版本BilInear_MMX_expand2函数;
补充新的SSE2的实现PicZoom_BilInear_SSE2函数)
(2007.06.06 更新测试数据,编译器由vc6改为vc2005,CPU由赛扬2G改为AMD64x2 4200+(2.1G) )
(2007.03.06 更新)
tag:图像缩放,速度优化,定点数优化,近邻取样插值,二次线性插值,三次线性插值,
MipMap链,三次卷积插值,MMX,SSE,SSE2,CPU缓存优化
摘要:首先给出一个基本的图像缩放算法,然后一步一步的优化其速度和缩放质量;
高质量的快速的图像缩放 全文 分为:
上篇 近邻取样插值和其速度优化
中篇 二次线性插值和三次卷积插值
下篇 三次线性插值和MipMap链
补充 使用SSE2优化
正文:
为了便于讨论,这里只处理32bit的ARGB颜色;
代码使用C++;涉及到汇编优化的时候假定为x86平台;使用的编译器为vc2005;
为了代码的可读性,没有加入异常处理代码;
测试使用的CPU为AMD64x2 4200+(2.37G) 和 Intel Core2 4400(2.00G);
速度测试说明:
只测试内存数据到内存数据的缩放
测试图片都是800*600缩放到1024*768; fps表示每秒钟的帧数,值越大表示函数越快
A:近邻取样插值、二次线性插值、三次卷积插值 缩放效果对比
原图 近邻取样缩放到0.6倍 近邻取样缩放到1.6倍
二次线性插值缩放到0.6倍 二次线性插值缩放到1.6倍
三次卷积插值缩放到0.6倍 三次卷积插值缩放到1.6倍
原图 近邻取样缩放到8倍 二次线性插值缩放到8倍 三次卷积插值缩放到8倍 二次线性插值(近似公式)
近邻取样插值缩放简单、速度快,但很多时候缩放出的图片质量比较差(特别是对于人物、景色等),
图片的缩放有比较明显的锯齿;使用二次或更高次插值有利于改善缩放效果;
B: 首先定义图像数据结构:
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];
}
二次线性插值缩放:
C: 二次线性插值缩放原理和公式图示:
缩放后图片 原图片
(宽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
聚焦看看(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;
而实际的程序,还需要考虑到边界(访问源图片可能超界)对于算法的影响,边界的处理可能有各种
方案(不处理边界或边界回绕或边界饱和或边界映射或用背景颜色混合等;文章中默认使用边界饱和来处理超界);
比如:边界饱和函数:
inline TARGB32 Pixels_Bound(const TPicRegion& pic,long x,long y)
{
//assert((pic.width>0)&&(pic.height>0));
bool IsInPic=true;
if (x<0) {x=0; IsInPic=false; } else if (x>=pic.width ) {x=pic.width -1; IsInPic=false; }
if (y<0) {y=0; IsInPic=false; } else if (y>=pic.height) {y=pic.height-1; IsInPic=false; }
TARGB32 result=Pixels(pic,x,y);
if (!IsInPic) result.a=0;
return result;
}
D: 二次线性插值缩放算法的一个参考实现:PicZoom_BilInear0
该函数并没有做什么优化,只是一个简单的浮点实现版本;
{
long x=(long)fx; if (x>fx) --x; //x=floor(fx);
long y=(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);
float u=fx-x;
float v=fy-y;
float pm3=u*v;
float pm2=u*(1-v);
float pm1=v*(1-u);
float pm0=(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);
}
void PicZoom_Bilinear0(const TPicRegion& Dst,const TPicRegion& Src)
{
if ( (0==Dst.width)||(0==Dst.height)
||(0==Src.width)||(0==Src.height)) return;
unsigned long dst_width=Dst.width;
TARGB32* pDstLine=Dst.pdata;
for (unsigned long y=0;y<Dst.height;++y)
{
float srcy=(y+0.4999999)*Src.height/Dst.height-0.5;
for (unsigned long x=0;x<dst_width;++x)
{
float srcx=(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
{
long x=x_16>>16;
long y=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);
unsigned long u_8=(x_16 & 0xFFFF)>>8;
unsigned long v_8=(y_16 & 0xFFFF)>>8;
unsigned long pm3_16=(u_8*v_8);
unsigned long pm2_16=(u_8*(unsigned long)(256-v_8));
unsigned long pm1_16=(v_8*(unsigned long)(256-u_8));
unsigned long pm0_16=((256-u_8)*(256-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);
}
void PicZoom_Bilinear1(const TPicRegion& Dst,const TPicRegion& Src)
{
if ( (0==Dst.width)||(0==Dst.height)
||(0==Src.width)||(0==Src.height)) return;
long xrIntFloat_16=((Src.width)<<16)/Dst.width;
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);
unsigned long dst_width=Dst.width;
TARGB32* pDstLine=Dst.pdata;
long srcy_16=csDErrorY;
long y;
for (y=0;y<Dst.height;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++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: 二次线性插值需要考略边界访问超界的问题,我们可以将边界区域和内部区域分开处理,这样就可以优化内部的插值实现函数了:比如不需要判断访问超界、减少颜色数据复制、减少一些不必要的重复坐标计算等等
{
unsigned long pm3_16=u_8*v_8;
unsigned long pm2_16=(u_8<<8)-pm3_16;
unsigned long pm1_16=(v_8<<8)-pm3_16;
unsigned long pm0_16=(1<<16)-pm1_16-pm2_16-pm3_16;
result->a=((pm0_16*PColor0[0].a+pm2_16*PColor0[1].a+pm1_16*PColor1[0].a+pm3_16*PColor1[1].a)>>16);
result->r=((pm0_16*PColor0[0].r+pm2_16*PColor0[1].r+pm1_16*PColor1[0].r+pm3_16*PColor1[1].r)>>16);
result->g=((pm0_16*PColor0[0].g+pm2_16*PColor0[1].g+pm1_16*PColor1[0].g+pm3_16*PColor1[1].g)>>16);
result->b=((pm0_16*PColor0[0].b+pm2_16*PColor0[1].b+pm1_16*PColor1[0].b+pm3_16*PColor1[1].b)>>16);
}
inline void Bilinear2_Border(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
{
long x=(x_16>>16);
long y=(y_16>>16);
unsigned long u_16=((unsigned short)(x_16));
unsigned long v_16=((unsigned short)(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);
}
void PicZoom_Bilinear2(const TPicRegion& Dst,const TPicRegion& Src)
{
if ( (0==Dst.width)||(0==Dst.height)
||(0==Src.width)||(0==Src.height)) return;
long xrIntFloat_16=((Src.width)<<16)/Dst.width;
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);
unsigned long dst_width=Dst.width;
//计算出需要特殊处理的边界
long border_y0=-csDErrorY/yrIntFloat_16+1; //y0+y*yr>=0; y0=csDErrorY => y>=-csDErrorY/yr
if (border_y0>=Dst.height) border_y0=Dst.height;
long border_x0=-csDErrorX/xrIntFloat_16+1;
if (border_x0>=Dst.width ) border_x0=Dst.width;
long border_y1=(((Src.height-2)<<16)-csDErrorY)/yrIntFloat_16+1; //y0+y*yr<=(height-2) => y<=(height-2-csDErrorY)/yr
if (border_y1<border_y0) border_y1=border_y0;
long border_x1=(((Src.width-2)<<16)-csDErrorX)/xrIntFloat_16+1;
if (border_x1<border_x0) border_x1=border_x0;
TARGB32* pDstLine=Dst.pdata;
long Src_byte_width=Src.byte_width;
long srcy_16=csDErrorY;
long y;
for (y=0;y<border_y0;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++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<border_y1;++y)
{
long srcx_16=csDErrorX;
long x;
for (x=0;x<border_x0;++x)
{
Bilinear2_Border(Src,srcx_16,srcy_16,&pDstLine[x]);//border
srcx_16+=xrIntFloat_16;
}
{
unsigned long v_8=(srcy_16 & 0xFFFF)>>8;
TARGB32* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
for (unsigned long x=border_x0;x<border_x1;++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<dst_width;++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<Dst.height;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++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)
证明这个公式不会造成内存访问超界:
//fit的基本想法,不超界情况下尽量增大inc的值
//fit算法推导://寻址不超界要求 (inc*(D-1)>>16)+1 <= S-1
//即: inc*(D-1)/(1<<16) <= (S-2)
// inc*(D-1) <= (S-2)*(1<<16) +((1<<16)-1)
// inc*(D-1) <= ((S-1)<<16) -1
// inc <= (((S-1)<<16) -1)/(D-1)
比如,按这个公式的一个简单实现: (缩放效果见前面的"二次线性插值(近似公式)"图示)
{
if ( (0==Dst.width)||(0==Dst.height)
||(2>Src.width)||(2>Src.height)) return;
long xrIntFloat_16=((Src.width-1)<<16)/Dst.width;
long yrIntFloat_16=((Src.height-1)<<16)/Dst.height;
unsigned long dst_width=Dst.width;
long Src_byte_width=Src.byte_width;
TARGB32* pDstLine=Dst.pdata;
long srcy_16=0;
for (unsigned long y=0;y<Dst.height;++y)
{
unsigned long v_8=(srcy_16 & 0xFFFF)>>8;
TARGB32* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
long srcx_16=0;
for (unsigned long x=0;x<dst_width;++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个计算出的
临时颜色分量;从而达到了单个指令两路数据处理的目的;
单个指令两路数据处理的计算:
乘法: ((0x00AA*a)<<16) | (0x00BB*a) = 0x00AA00BB * a
可见只要保证0x00AA*a和0x00BB*a都小于(1<<16)那么乘法可以直接使用无符号数乘法了
加法: ((0x00AA+0x00CC)<<16) | (0x00BB+0x00DD) = 0x00AA00BB + 0x00CC00DD
可见只要0x00AA+0x00CC和0x00BB+0x00DD小于(1<<16)那么加法可以直接使用无符号数加法了
(移位、减法等稍微复杂一点,因为这里没有用到就不推倒运算公式了)
{
unsigned long pm3_8=(u_8*v_8)>>8;
unsigned long pm2_8=u_8-pm3_8;
unsigned long pm1_8=v_8-pm3_8;
unsigned long pm0_8=256-pm1_8-pm2_8-pm3_8;
unsigned long Color=*(unsigned long*)(PColor0);
unsigned long BR=(Color & 0x00FF00FF)*pm0_8;
unsigned long GA=((Color & 0xFF00FF00)>>8)*pm0_8;
Color=((unsigned long*)(PColor0))[1];
GA+=((Color & 0xFF00FF00)>>8)*pm2_8;
BR+=(Color & 0x00FF00FF)*pm2_8;
Color=*(unsigned long*)(PColor1);
GA+=((Color & 0xFF00FF00)>>8)*pm1_8;
BR+=(Color & 0x00FF00FF)*pm1_8;
Color=((unsigned long*)(PColor1))[1];
GA+=((Color & 0xFF00FF00)>>8)*pm3_8;
BR+=(Color & 0x00FF00FF)*pm3_8;
*(unsigned long*)(result)=(GA & 0xFF00FF00)|((BR & 0xFF00FF00)>>8);
}
inline void Bilinear_Border_Common(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
{
long x=(x_16>>16);
long y=(y_16>>16);
unsigned long u_16=((unsigned short)(x_16));
unsigned long v_16=((unsigned short)(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);
}
void PicZoom_Bilinear_Common(const TPicRegion& Dst,const TPicRegion& Src)
{
if ( (0==Dst.width)||(0==Dst.height)
||(0==Src.width)||(0==Src.height)) return;
long xrIntFloat_16=((Src.width)<<16)/Dst.width;
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);
unsigned long dst_width=Dst.width;
//计算出需要特殊处理的边界
long border_y0=-csDErrorY/yrIntFloat_16+1; //y0+y*yr>=0; y0=csDErrorY => y>=-csDErrorY/yr
if (border_y0>=Dst.height) border_y0=Dst.height;
long border_x0=-csDErrorX/xrIntFloat_16+1;
if (border_x0>=Dst.width ) border_x0=Dst.width;
long border_y1=(((Src.height-2)<<16)-csDErrorY)/yrIntFloat_16+1; //y0+y*yr<=(height-2) => y<=(height-2-csDErrorY)/yr
if (border_y1<border_y0) border_y1=border_y0;
long border_x1=(((Src.width-2)<<16)-csDErrorX)/xrIntFloat_16+1;
if (border_x1<border_x0) border_x1=border_x0;
TARGB32* pDstLine=Dst.pdata;
long Src_byte_width=Src.byte_width;
long srcy_16=csDErrorY;
long y;
for (y=0;y<border_y0;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++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<border_y1;++y)
{
long srcx_16=csDErrorX;
long x;
for (x=0;x<border_x0;++x)
{
Bilinear_Border_Common(Src,srcx_16,srcy_16,&pDstLine[x]);//border
srcx_16+=xrIntFloat_16;
}
{
unsigned long v_8=(srcy_16 & 0xFFFF)>>8;
TARGB32* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
for (unsigned long x=border_x0;x<border_x1;++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<dst_width;++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<Dst.height;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++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
{
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
}
}
void Bilinear_Border_MMX(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
{
long x=(x_16>>16);
long y=(y_16>>16);
unsigned long u_16=((unsigned short)(x_16));
unsigned long v_16=((unsigned short)(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);
}
void PicZoom_Bilinear_MMX(const TPicRegion& Dst,const TPicRegion& Src)
{
if ( (0==Dst.width)||(0==Dst.height)
||(0==Src.width)||(0==Src.height)) return;
long xrIntFloat_16=((Src.width)<<16)/Dst.width;
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);
unsigned long dst_width=Dst.width;
//计算出需要特殊处理的边界
long border_y0=-csDErrorY/yrIntFloat_16+1; //y0+y*yr>=0; y0=csDErrorY => y>=-csDErrorY/yr
if (border_y0>=Dst.height) border_y0=Dst.height;
long border_x0=-csDErrorX/xrIntFloat_16+1;
if (border_x0>=Dst.width ) border_x0=Dst.width;
long border_y1=(((Src.height-2)<<16)-csDErrorY)/yrIntFloat_16+1; //y0+y*yr<=(height-2) => y<=(height-2-csDErrorY)/yr
if (border_y1<border_y0) border_y1=border_y0;
long border_x1=(((Src.width-2)<<16)-csDErrorX)/xrIntFloat_16+1;
if (border_x1<border_x0) border_x1=border_x0;
TARGB32* pDstLine=Dst.pdata;
long Src_byte_width=Src.byte_width;
long srcy_16=csDErrorY;
long y;
for (y=0;y<border_y0;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++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<border_y1;++y)
{
long srcx_16=csDErrorX;
long x;
for (x=0;x<border_x0;++x)
{
Bilinear_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);//border
srcx_16+=xrIntFloat_16;
}
{
unsigned long v_8=(srcy_16 & 0xFFFF)>>8;
TARGB32* PSrcLineColor= (TARGB32*)((TUInt8*)(Src.pdata)+Src_byte_width*(srcy_16>>16)) ;
for (unsigned long x=border_x0;x<border_x1;++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<dst_width;++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<Dst.height;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++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
{
if ( (0==Dst.width)||(0==Dst.height)
||(0==Src.width)||(0==Src.height)) return;
long xrIntFloat_16=((Src.width)<<16)/Dst.width;
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);
unsigned long dst_width=Dst.width;
//计算出需要特殊处理的边界
long border_y0=-csDErrorY/yrIntFloat_16+1; //y0+y*yr>=0; y0=csDErrorY => y>=-csDErrorY/yr
if (border_y0>=Dst.height) border_y0=Dst.height;
long border_x0=-csDErrorX/xrIntFloat_16+1;
if (border_x0>=Dst.width ) border_x0=Dst.width;
long border_y1=(((Src.height-2)<<16)-csDErrorY)/yrIntFloat_16+1; //y0+y*yr<=(height-2) => y<=(height-2-csDErrorY)/yr
if (border_y1<border_y0) border_y1=border_y0;
long border_x1=(((Src.width-2)<<16)-csDErrorX)/xrIntFloat_16+1;
if (border_x1<border_x0) border_x1=border_x0;
TARGB32* pDstLine=Dst.pdata;
long Src_byte_width=Src.byte_width;
long srcy_16=csDErrorY;
long y;
for (y=0;y<border_y0;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++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<border_y1;++y)
{
long srcx_16=csDErrorX;
long x;
for (x=0;x<border_x0;++x)
{
Bilinear_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);//border
srcx_16+=xrIntFloat_16;
}
{
long dst_width_fast=border_x1-border_x0;
if (dst_width_fast>0)
{
unsigned long v_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+eax*4]
MOVD MM0,dword ptr [ecx+eax*4+4]
PUNPCKLWD MM5,MM5
MOVD MM3,dword ptr [esi+eax*4]
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<dst_width;++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<Dst.height;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++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 800*600 to 1024*768
//==============================================================================
// 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 800*600 to 1024*768
//==============================================================================
// 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(x*PI)/(x*PI),如图:
三次卷积插值曲线sin(x*PI)/(x*PI) (其中PI=3.1415926...)
K:三次卷积插值缩放算法的一个参考实现:PicZoom_ThreeOrder0
该函数并没有做过多的优化,只是一个简单的浮点实现版本;
inline double SinXDivX(double x)
{
//该函数计算插值曲线sin(x*PI)/(x*PI)的值 //PI=3.1415926535897932385;
//下面是它的近似拟合表达式
const float a = -1; //a还可以取 a=-2,-1,-0.75,-0.5等等,起到调节锐化或模糊程度的作用
if (x<0) x=-x; //x=abs(x);
double x2=x*x;
double x3=x2*x;
if (x<=1)
return (a+2)*x3 - (a+3)*x2 + 1;
else if (x<=2)
return a*x3 - (5*a)*x2 + (8*a)*x - (4*a);
else
return 0;
}
inline TUInt8 border_color(long Color)
{
if (Color<=0)
return 0;
else if (Color>=255)
return 255;
else
return Color;
}
void ThreeOrder0(const TPicRegion& pic,const float fx,const float fy,TARGB32* result)
{
long x0=(long)fx; if (x0>fx) --x0; //x0=floor(fx);
long y0=(long)fy; if (y0>fy) --y0; //y0=floor(fy);
float fu=fx-x0;
float fv=fy-y0;
TARGB32 pixel[16];
long i,j;
for (i=0;i<4;++i)
{
for (j=0;j<4;++j)
{
long x=x0-1+j;
long y=y0-1+i;
pixel[i*4+j]=Pixels_Bound(pic,x,y);
}
}
float afu[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);
float sR=0,sG=0,sB=0,sA=0;
for (i=0;i<4;++i)
{
float aR=0,aG=0,aB=0,aA=0;
for (long j=0;j<4;++j)
{
aA+=afu[j]*pixel[i*4+j].a;
aR+=afu[j]*pixel[i*4+j].r;
aG+=afu[j]*pixel[i*4+j].g;
aB+=afu[j]*pixel[i*4+j].b;
}
sA+=aA*afv[i];
sR+=aR*afv[i];
sG+=aG*afv[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));
}
void PicZoom_ThreeOrder0(const TPicRegion& Dst,const TPicRegion& Src)
{
if ( (0==Dst.width)||(0==Dst.height)
||(0==Src.width)||(0==Src.height)) return;
unsigned long dst_width=Dst.width;
TARGB32* pDstLine=Dst.pdata;
for (unsigned long y=0;y<Dst.height;++y)
{
float srcy=(y+0.4999999)*Src.height/Dst.height-0.5;
for (unsigned long x=0;x<dst_width;++x)
{
float srcx=(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做一个查找表;
class _CAutoInti_SinXDivX_Table {
private:
void _Inti_SinXDivX_Table()
{
for (long i=0;i<=(2<<8);++i)
SinXDivX_Table_8[i]=long(0.5+256*SinXDivX(i*(1.0/(256))))*1;
};
public:
_CAutoInti_SinXDivX_Table() { _Inti_SinXDivX_Table(); }
};
static _CAutoInti_SinXDivX_Table __tmp_CAutoInti_SinXDivX_Table;
//颜色查表
static TUInt8 _color_table[256*3];
static const TUInt8* color_table=&_color_table[256];
class _CAuto_inti_color_table
{
public:
_CAuto_inti_color_table() {
for (int i=0;i<256*3;++i)
_color_table[i]=border_color(i-256);
}
};
static _CAuto_inti_color_table _Auto_inti_color_table;
void ThreeOrder_Fast_Common(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
{
unsigned long u_8=(unsigned char)((x_16)>>8);
unsigned long v_8=(unsigned char)((y_16)>>8);
const TARGB32* pixel=&Pixels(pic,(x_16>>16)-1,(y_16>>16)-1);
long pic_byte_width=pic.byte_width;
long au_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];
long sR=0,sG=0,sB=0,sA=0;
for (long i=0;i<4;++i)
{
long aA=au_8[0]*pixel[0].a + au_8[1]*pixel[1].a + au_8[2]*pixel[2].a + au_8[3]*pixel[3].a;
long aR=au_8[0]*pixel[0].r + au_8[1]*pixel[1].r + au_8[2]*pixel[2].r + au_8[3]*pixel[3].r;
long aG=au_8[0]*pixel[0].g + au_8[1]*pixel[1].g + au_8[2]*pixel[2].g + au_8[3]*pixel[3].g;
long aB=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+=aR*av_8[i];
sG+=aG*av_8[i];
sB+=aB*av_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];
}
void ThreeOrder_Border_Common(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
{
long x0_sub1=(x_16>>16)-1;
long y0_sub1=(y_16>>16)-1;
unsigned long u_16_add1=((unsigned short)(x_16))+(1<<16);
unsigned long v_16_add1=((unsigned short)(y_16))+(1<<16);
TARGB32 pixel[16];
long i;
for (i=0;i<4;++i)
{
long y=y0_sub1+i;
pixel[i*4+0]=Pixels_Bound(pic,x0_sub1+0,y);
pixel[i*4+1]=Pixels_Bound(pic,x0_sub1+1,y);
pixel[i*4+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);
}
void PicZoom_ThreeOrder_Common(const TPicRegion& Dst,const TPicRegion& Src)
{
if ( (0==Dst.width)||(0==Dst.height)
||(0==Src.width)||(0==Src.height)) return;
long xrIntFloat_16=((Src.width)<<16)/Dst.width;
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);
unsigned long dst_width=Dst.width;
//计算出需要特殊处理的边界
long border_y0=((1<<16)-csDErrorY)/yrIntFloat_16+1;//y0+y*yr>=1; y0=csDErrorY => y>=(1-csDErrorY)/yr
if (border_y0>=Dst.height) border_y0=Dst.height;
long border_x0=((1<<16)-csDErrorX)/xrIntFloat_16+1;
if (border_x0>=Dst.width ) border_x0=Dst.width;
long border_y1=(((Src.height-3)<<16)-csDErrorY)/yrIntFloat_16+1; //y0+y*yr<=(height-3) => y<=(height-3-csDErrorY)/yr
if (border_y1<border_y0) border_y1=border_y0;
long border_x1=(((Src.width-3)<<16)-csDErrorX)/xrIntFloat_16+1;;
if (border_x1<border_x0) border_x1=border_x0;
TARGB32* pDstLine=Dst.pdata;
long srcy_16=csDErrorY;
long y;
for (y=0;y<border_y0;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++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<border_y1;++y)
{
long srcx_16=csDErrorX;
long x;
for (x=0;x<border_x0;++x)
{
ThreeOrder_Border_Common(Src,srcx_16,srcy_16,&pDstLine[x]);//border
srcx_16+=xrIntFloat_16;
}
for (x=border_x0;x<border_x1;++x)
{
ThreeOrder_Fast_Common(Src,srcx_16,srcy_16,&pDstLine[x]);//fast !
srcx_16+=xrIntFloat_16;
}
for (x=border_x1;x<dst_width;++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<Dst.height;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++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
static TMMXData32 SinXDivX_Table_MMX[(2<<8)+1];
class _CAutoInti_SinXDivX_Table_MMX {
private:
void _Inti_SinXDivX_Table_MMX()
{
for (long i=0;i<=(2<<8);++i)
{
unsigned short t=long(0.5+(1<<14)*SinXDivX(i*(1.0/(256))));
unsigned long tl=t | (((unsigned long)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)+256*4+eax*4]
movd mm6,dword ptr [(offset SinXDivX_Table_MMX)+eax*4]
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+ecx*4]
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
}
}
inline void ThreeOrder_Fast_MMX(const TPicRegion& pic,const long x_16,const long y_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)+256*4+edi*4]
call _private_ThreeOrder_Fast_MMX
lea ebx,[(offset SinXDivX_Table_MMX)+edi*4]
call _private_ThreeOrder_Fast_MMX
neg edi
lea ebx,[(offset SinXDivX_Table_MMX)+256*4+edi*4]
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
}
}
void ThreeOrder_Border_MMX(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
{
unsigned long x0_sub1=(x_16>>16)-1;
unsigned long y0_sub1=(y_16>>16)-1;
long u_16_add1=((unsigned short)(x_16))+(1<<16);
long v_16_add1=((unsigned short)(y_16))+(1<<16);
TARGB32 pixel[16];
for (long i=0;i<4;++i)
{
long y=y0_sub1+i;
pixel[i*4+0]=Pixels_Bound(pic,x0_sub1 ,y);
pixel[i*4+1]=Pixels_Bound(pic,x0_sub1+1,y);
pixel[i*4+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);
}
void PicZoom_ThreeOrder_MMX(const TPicRegion& Dst,const TPicRegion& Src)
{
if ( (0==Dst.width)||(0==Dst.height)
||(0==Src.width)||(0==Src.height)) return;
long xrIntFloat_16=((Src.width)<<16)/Dst.width;
long yrIntFloat_16=((Src.height)<<16)/Dst.height;
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);
unsigned long dst_width=Dst.width;
//计算出需要特殊处理的边界
long border_y0=((1<<16)-csDErrorY)/yrIntFloat_16+1;//y0+y*yr>=1; y0=csDErrorY => y>=(1-csDErrorY)/yr
if (border_y0>=Dst.height) border_y0=Dst.height;
long border_x0=((1<<16)-csDErrorX)/xrIntFloat_16+1;
if (border_x0>=Dst.width ) border_x0=Dst.width;
long border_y1=(((Src.height-3)<<16)-csDErrorY)/yrIntFloat_16+1; //y0+y*yr<=(height-3) => y<=(height-3-csDErrorY)/yr
if (border_y1<border_y0) border_y1=border_y0;
long border_x1=(((Src.width-3)<<16)-csDErrorX)/xrIntFloat_16+1;;
if (border_x1<border_x0) border_x1=border_x0;
TARGB32* pDstLine=Dst.pdata;
long srcy_16=csDErrorY;
long y;
for (y=0;y<border_y0;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++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<border_y1;++y)
{
long srcx_16=csDErrorX;
long x;
for (x=0;x<border_x0;++x)
{
ThreeOrder_Border_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);//border
srcx_16+=xrIntFloat_16;
}
for (x=border_x0;x<border_x1;++x)
{
ThreeOrder_Fast_MMX(Src,srcx_16,srcy_16,&pDstLine[x]);//fast MMX !
srcx_16+=xrIntFloat_16;
}
for (x=border_x1;x<dst_width;++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<Dst.height;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++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 800*600 to 1024*768
//==============================================================================
// 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 800*600 to 1024*768
//==============================================================================
// 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
版权声明:本文为博主原创文章,未经博主允许不得转载。
- 上一篇图形图像处理-之-高质量的快速的图像缩放 上篇 近邻取样插值和其速度优化
- 下一篇并行计算简介和多核CPU编程Demo
- 顶
- 0
- 踩
- 0
- 主题推荐
- 图像处理 图形
- 猜你在找
- 有趣的算法(数据结构)
- 数据结构和算法
- 移动APP测试基础到进阶
- Java经典算法讲解
- Flash实战技能应用从入门到精通
97楼 33184777 2015-01-01 11:28发表 [回复]-
- MMX之类的 优化 只对32位图像有效, 24位或者灰度图很难用了。 32位一个像素四个字节正好满足MMX内部的一些需求。
96楼 nancy_princess 2014-11-07 15:35发表 [回复]-
- 对于上面的公式,它将图片向右下各移动了半个像素,需要对此做一个修正;
=> Sx=(Dx+0.5)*SW/DW-0.5; Sy=(Dy+0.5)*SH/DH-0.5;
没看出来哪向右下各移动了半个像素啊?
95楼 u013574852 2014-01-28 23:21发表 [回复]-
- 把这个用到全景图像变换中的双向性插值,速度没有提升,可能稍微再优化一下,能到40ms每帧,自己的优化代码能到47ms每帧,不过用mmx这个快速算法,结果显示有一丁点锯齿,感觉是其中移位操作出现次序问题
94楼 yankaiyutong6 2013-09-04 14:42发表 [回复]-
- *****************接上一段代码************************
*result=(int16_t)((GA & 0x000007E0)|((BR & 0x0000F81F)>>8));
}
Re: housisong 2013-09-13 06:27发表 [回复]-
- 回复yankaiyutong6:先理解我的代码如何处理各颜色分量的,再改写为你的代码,不要只是拷贝代码.
93楼 yankaiyutong6 2013-09-04 14:40发表 [回复]-
- void Bilinear_Fast_Common(int16_t* PColor0,int16_t* PColor1,unsigned long u_8,unsigned long v_8,int16_t* result)
{
unsigned long pm3_8=(u_8*v_8)>>8;
unsigned long pm2_8=u_8-pm3_8;
unsigned long pm1_8=v_8-pm3_8;
unsigned long pm0_8=256-pm1_8-pm2_8-pm3_8;
unsigned long Color=(unsigned long)(*PColor0);
unsigned long GA=((Color & 0x000007E0)>>8)*pm0_8;
unsigned long BR=(Color & 0x0000F81F)*pm0_8;
Color=(unsigned long)PColor0[1];
GA+=((Color & 0x000007E0)>>8)*pm2_8;
BR+=(Color & 0x0000F81F)*pm2_8;
Color=(unsigned long)*PColor1;
GA+=((Color & 0x000007E0)>>8)*pm1_8;
BR+=(Color & 0x0000F81F)*pm1_8;
Color=(unsigned long)PColor1[1];
GA+=((Color & 0x000007E0)>>8)*pm3_8;
BR+=(Color & 0x0000F81F)*pm3_8;
92楼 yankaiyutong6 2013-09-04 14:40发表 [回复]-
- 楼主太牛了。我在将Bilinear移植中,出现的问题,博主可否帮忙看一下。多谢。
我使用的是RGB565的方式,对Bilinear_Fast_Common()函数修改如下。
这样出现的情况是颜色不正确,博主可否帮忙看一下应该如何修改,多谢!
91楼 foggy32 2013-03-31 20:14发表 [回复]-
- lz你好,我想问一下那个读图像的load函数里的lockbuf何意?拿来干嘛的。。
Re: housisong 2013-04-01 14:05发表 [回复]-
- 回复foggy32:外面用来访问图像数据的内存区;这个时候数据区的拥有者也可以借此干点私活
90楼 kivi206 2013-01-26 20:08发表 [回复]-
- 为什么我的三次卷积差值比二次线性插值的效果差?
三次卷积差值的图片放大后有麦芒样的边缘,而且好像字都变粗了,试过改a的值从-2到+2.效果都不是很好;
二次线性差值的效果放大倍数比较大的时候比较模糊,有哪个参数可以调整一下效果吗?
89楼 CAS_wycherley 2012-09-13 10:02发表 [回复]-
- 赞一个
88楼 zhuzhubin 2011-11-14 10:09发表 [回复]-
- 请问有没有一种算法的时候 同时清晰度能提高上去 也就是二次线性插值和三次卷积插值折中的办法 二次线性插值图片清晰度不够 三次卷积插值太耗cpu了
87楼 yjx279742048 2011-10-26 19:54发表 [回复]-
- 你好啊,刚才向你问的那个问题已经解决了,是个小问题,我两边代码同时运行,比较一下就弄好了。
另外有个其他的问题向你请教,我现在也是在做算法方面的工作,代码写好后,也需要对代码进行优化,提高运算速度。这一方面以前没有接触过,不知道怎么下手好。请你指点下,给我介绍些资料,带我入门啊,谢谢。我的邮箱yjx_108@163.com。
Re: housisong 2011-10-27 11:15发表 [回复]-
- 回复yjx279742048:可以看看我的blog里关于优化的一些杂文,应该有些帮助: http://blog.csdn.net/housisong/article/category/232185 ;
86楼 yjx279742048 2011-10-26 17:01发表 [回复]-
- Hi,你好,我刚刚把PicZoom_Bilinear2移植了一下,主要是把32位ARGB图改成8位位图。发现一个问题,边界处理时会出现像素值跳变的问题,看缩放后的图会明显看见四个边界都会有不连续的地方。检查了一下,发现这个问题:Bilinear2_Fast函数中 ,在一个点,假设PColor0和PColor1所指的四个像素值分别是102,103, 138,135,那算出来result的值可能会是168;而在相临另一个点,四个像素值变化很小,算出来的result值又会变成70。这样图像中看起来就成跳变了。
我自己对定点运算不太熟悉,希望你抽空帮我看看这个原因。如果有时间,请回复我啊,这里回也行,回我邮箱也行,yjx_108@163.com。谢谢啊。
85楼 awgin 2011-08-24 09:25发表 [回复]-
- 数学学的不好,看起来有点吃力。
84楼 zistzh 2011-08-01 15:40发表 [回复]-
- 楼主高人!
以前也做过一些图像算法的优化,包括MMX的,累人啊。
谢谢楼主,学到了很多
优化无止境啊
83楼 chengxuyuanit 2011-07-28 10:26发表 [回复]-
- 有没有三次卷积缩放16位图片的啊,很期待
82楼 monokuro00 2011-06-29 23:10发表 [回复]-
- [e01]
81楼 ding_ 2011-06-09 16:57发表 [回复]-
- [e03]
80楼 gdutljg 2010-08-06 10:21发表 [回复]-
- 我的意思是,在Bilinear_Fast_MMX里用两个字节精度来运算,可以完成那个算法吗?
79楼 gdutljg 2010-08-06 10:04发表 [回复]-
- 楼主你好, 二次线性插值时, 在Bilinear_Fast_MMX这个函数里,你在计算完tmpColor0,tmpColor1 后,就把它右移8位,之后再计算。这样做会不会损失精度?我参照按Bilinear_Fast_MMX写了一个,图片有锯齿,不知是不是因为精度损失了?
78楼 gdutljg 2010-07-28 12:05发表 [回复]-
- 膜拜![e01]
77楼 kuanghong 2010-05-25 11:39发表 [回复]-
- 双线性里
pm1=v*(1-u);
pm2=u*(1-v);
pm1和pm2搞反了,需要交换一下。
Re: kuanghong 2010-05-25 12:43发表 [回复]-
- 回复 kuanghong:搞错了,是对的。自己吧c0,c1,c2,c3顺序搞错了。
76楼 ehoo_jack 2009-08-20 15:13发表 [回复]-
- 感谢楼主的无私,但是好多还是没有看的很明白,求此代码,bjec110@126.com 谢谢 !!!
75楼 jt_feelcool007 2009-07-27 21:35发表 [回复]-
- C#中好像不能直接写入汇编代码,郁闷了。。。。
74楼 jt_feelcool007 2009-07-27 20:57发表 [回复]-
- 博主您好,看了你的文章受益匪浅,在此求代码
jt_feelcool007@sina.com
73楼 yezijian_721 2009-06-11 18:03发表 [回复]-
- 楼主能否发份代码给我,下载不了,yezijian721@gmail.com
72楼 housisong 2009-06-05 20:31发表 [回复]-
- 你仔细看看代码; 那里传的是指针!
71楼 im_ym 2009-06-03 18:00发表 [回复]-
- void PicZoom_Bilinear2(const TPicRegion& Dst,const TPicRegion& Src) 和以后的函数中为什么都只用了color0和color1进行计算了? color2和color3都不需要用了吗?
70楼 gaoxuankang 2009-05-20 15:14发表 [回复]-
- 您好,您的代码我主要做了三次卷积这部分的,将32位改为16位的我做出来了,但是将32位的改成16位的MMX代码一直有问题,您能帮我指出问题在哪么?谢谢了。
我的邮箱是:gaoxuankang@gmail.com
能告诉我您的邮箱么?把我做的MMX三次卷积代码给您发过去。
69楼 neugls 2009-05-02 18:28发表 [回复]-
- 太强大了
68楼 housisong 2009-04-23 12:46发表 [回复]-
- en 用256来减更好些 我会更新一下文章; 你的代码不全而且格式丢失,可以用邮件联系;delphi的MMX版本你可以用Bilinear_Fast_MMX来改写,少量修改就可以了
67楼 阿发伯 2009-04-22 20:06发表 [回复]-
- 说明,上面过程中的[ebx].TImageOperate.FStride等是对我定义的类数据的引用,通过字面应该能明白意思;x,y扩大256倍;因原数据已经作了边界扩展,所以过程中没有边界检测代码。你的mmx比我用得好,看看可不可以继续改进?如果有疑问可发邮件给我(在你的好友名单中查)
Re: housisong 2009-04-22 23:13发表 [回复]-
- 你的代码不全,而且回车和格式也没有了,可以给我发邮件
我的MMX代码你可以借鉴Bilinear_Fast_MMX,改成delphi版的代码只需要很少的改动
66楼 阿发伯 2009-04-22 19:56发表 [回复]-
- 认识我吧!我按你的二次线形插值定点数算法写了几个版本,基本不错,但后来发现无论哪个版本,x,y无论是扩大65536还是256,计算出的ARGB都偏小,如Alpha原为255,返回值却为253,我仔细查了原因,与扩大倍数无关,问题在于v,u,255-v,255-u等都是以x,y余数的255倍,v,u相乘可能的最大值是65025,而最后计算结果时却用65536去除,所以结果偏小了。但如果改用65025,又会大大影响速度,所以我做了一点对结果影响不大的小改进,即255-v,255-u改为256-v,256-u,最后达到了要求,下面是我写的一个Delphi类内部使用的过程(其实是个函数,不过没标明参数和返回值):// in ebx = TImageOperate object, edx = x, ecx = y// mm7 = 00 00 00 00 00 00 00 00// out eax = ARGB colorprocedure GetBilinearColor;asmpushesi push edx push ecx// pxor mm7, mm7 mov esi, ecx mov eax, edx sar esi, 8 // y0 = y >> 8 sar eax, 8 // x0 = x >> 8 imul esi, [ebx].TImageOperate.FStride add esi, [ebx].TImageOperate.FScan0 shl eax, 2 add esi, eax // esi = Scan0 y0 * Stride x0 * 4 movq mm0, [esi] // mm0 = C2 (x0 1, y0) C0(x0, y0) movq mm1, mm0 add esi, [ebx].TImageOperate.FStride // [esi] = C3(x0 1, y0 1) C1(x0, y0 1) punpcklbw mm0, [esi] // mm0 = A1 A0 R1 R0 G1 G0 B1 B0 punpckhbw mm1, [esi] // mm1 = A3 A2 R3 R2 G3 G2 B3 B2 movq mm2, mm0 movq mm3, mm1 punpcklbw mm0, mm7 // mm0 = 00 G1 00 G0 00 B1 00 B0 punpcklbw mm1, mm7 // mm1 = 00 G3 00 G2 00 B3 00 B2 punpckhbw mm2, mm7 // mm2 = 00 A1 00 A0 00 R1 00 R0 punpckhbw mm3, mm7 // mm3 = 00 A3 00 A2 00 R3 00 R2 mov eax, 100hand edx, 255 // u = x
65楼 housisong 2008-12-01 11:33发表 [回复]-
- to: whdqpi
这里就是源代码了
关于定点数优化/查表优化/MMX优化等 要搞明白 建议看看我的其他优化文章
64楼 whdqpi 2008-11-30 19:34发表 [回复]-
- 您的这个网页我已经看过很多次了,对于三次插值算法,浮点的那个已经看懂了,但是对于后面两次优化的程序还是不太明白,希望您能将完整的三次插值的程序源码发给我一份,最好是能多写点注释的。谢谢!
whdqpi@126.com
63楼 hdjhdj 2008-09-28 11:19发表 [回复]-
- 有没有DELPHI的代码呢?能不能帮我发一个二线插值delphi MMX的算法到hdj5_cn@163.com
62楼 journ 2008-07-01 18:29发表 [回复]-
- 牛就一个字
61楼 housisong 2008-06-20 14:25发表 [回复]-
- to: wanruirui
你使用不是汇编优化的版本不就可以了吗:(
60楼 红烧大肠 2008-06-20 11:04发表 [回复]-
- 我想把你的代码用在CE上.不知道汇编那段代码会不会有影响????
59楼 红烧大肠 2008-06-20 11:02发表 [回复]-
- 代码啊代码。难度这么大留个代码啊,demo就更好了。想自己拷你代码却还有汇编。要命啊。dulton@gmail.com。谢谢。
58楼 xlkj 2008-06-07 19:22发表 [回复]-
- 谢谢,C to delphi的代码我已经调试好了。
57楼 xlkjsoft 2008-06-06 19:03发表 [回复]-
- 有没有DELPHI的代码呢?C的看不懂,能不能帮我发一个二线插值delphi MMX的算法到vod8844@163.com。看了文章,更深一步了解了二线插值算法,感谢了。
56楼 ivy 2008-06-06 16:56发表 [回复]-
- 楼主真厉害,刚开始学习图像编程很困惑,看见你对缩放算法的研究很深入很钦佩。获益匪浅,慢慢学习吧,以后在向您请教!
55楼 savior00 2008-06-05 16:29发表 [回复]-
- 太强了
学习一下
。。
为什么总是不对呢。
不知道能把源码发一分给我么..
zhaofenghai@gmail.com
54楼 iBug168 2008-06-03 13:42发表 [回复]-
- 主要是我自己写了个缩放算法...好像有内存越界的问题..
痛苦ing....
53楼 iBug168 2008-06-03 13:41发表 [回复]-
- 无语咯...
学习学习...
不知道能不能给份算法源码....
iidioter@gmail.com
52楼 zz 2008-05-29 08:54发表 [回复]-
- 楼主,你好,我不太明白CSDerrorx,您说是一个预设的偏移值,我还是不太懂它的作用,为什么要用这个呢?
51楼 micvenus 2008-05-26 00:10发表 [回复]-
- 高手,您好,您好厉害哈,能把3次卷积插值的源代码发给我看下噢?我现在也在写一个图像缩放的程序,用的是图像缩放的分片连续算法,希望你发3次卷积插值的代码,我想比较2个的效果,十分感激
我的邮箱是 micvenus@163.com
50楼 ericjwh 2008-05-24 01:34发表 [回复]-
- MMX的内存读写我还是不懂,头大阿,现在有缩放MMX的问题急于请教,已发到你邮箱,希望你帮我看看,需不需要用SSE(SSE2),万谢!
49楼 ericjwh 2008-05-21 21:46发表 [回复]-
- 谢谢 资料收到了,很有用。 我先好好看看,再向你请教。
48楼 housisong 2008-05-21 10:37发表 [回复]-
- to:ericjwh
1. 我也不知道为什么出错了,思路应该没有错; 你调试跟踪一下吧,看哪一步没有达到预想的功能(VC调试时可以看到MMX寄存器的值)
2.你看看可以利用hadd*指令; 另外,有时也不一定非要水平加(一个累加器),某些时候可以用多个累加器而避免水平加
3.我自己写MMX的时候,很少去细调指令位置的,一般都是适当调整,保持一定的结构和可读性也比较重要;
关于MMX/SSE等的更多资料,建议直接到intel和amd的官方主页去下载手册;
(收邮件)
47楼 ericjwh 2008-05-20 22:11发表 [回复]-
- to:housisong
谢谢你的耐心指导,几句话点醒梦中人。
第一个问题真惭愧,我基本的概念都搞错了
第二个问题你说的也是我看错的地方,pmulhw相当于右移16位,我当成8位了。
还想再问两个问题
1 一个16位(32位)的数在mmx寄存器里怎么符号扩展到32位(64位)。 我先pslld(mm0,16) 然后psrad(mm0,16) 结果出错了
2 一个mmx寄存器里4个16位的数的和怎么算方便(0-15,16-31,32-47,48-63的和),能比移出到内存中算快吗
由于新学,我现在编写mmx代码在微码阻塞,指令配对上存在很大问题,速度提不起来,有时改写还会发生数据的溢出。 你能发点这方面的资料到我邮箱吗,要是方便的话,提供几份你写的非商业性的mmx,sse代码让我多熟悉一下。 非常感谢
46楼 housisong 2008-05-19 23:52发表 [回复]-
- to:ericjwh
1. eax寄存器是32bit,ax是其低位的16bit,ah是ax的高位的8bit,al是ax的低8位bit;
movzx edi,ah 这句相当于 edi=(eax &0x0000FF00)>>8;
2.你应该计算错了:) 可能你要注意一下pmulhw的使用; 代码的效果我测试过的;
3.你说的SSE2,应该是指其提供的128bit的整数运算支持吧;在我的多次测试中,当前的CPU(包括intel/amd)的实现都不尽如人意,很多时候没有MMX的实现快;
45楼 ericjwh 2008-05-19 22:29发表 [回复]-
- 只恨没有早点拜读你的文章阿,受益很大,佩服阿
有几个问题,是关于三次卷积插值的MMX运算的
1 inline void ThreeOrder_Fast_MMX 的注释
movzx edi,ah //v_8
movzx ecx,bh //u_8
这里大小头赋值 我感觉是al和bl才对应 v_8 和u_8,一般是小头。
2 inline void ThreeOrder_Fast_MMX 这个函数中最后 psraw mm0,3 是不是应该改成
psraw mm0,13 前面左移了14位,7位,又右移了8位,这里是不是应该是13
3 希望楼主能提供sse2的三次卷积插值算法 或者二次线性的,非常期望
我是新人,希望楼主能够指点一下,万分感激。我也在深圳,邮箱ericjwh@163.com
44楼 jacky22 2008-03-21 17:04发表 [回复]-
- 你太牛了,我以前从来没会没事留言,不过,真的觉得你是个真正的技术人员,相对于你,我只是个枪手,惭愧。
43楼 housisong 2008-03-21 12:58发表 [回复]-
- to:nazio
CSDerrorx是一个预设的偏移值(X轴平移量);
"需要特殊处理边界"是通过离散数学计算得到的;
比如类似这样的缩放公式; s=(d*A+B)>>16; 由于 s>=0并且s<sW,那么可以有: 求d的最小值公式 ((d*A+B)>>16)>=0; 最大值值公式((d*A+B)>>16)<sW ......
42楼 nazio 2008-03-21 10:34发表 [回复]-
- lz我很困惑那个边界问题的CSDerrorx的物理意义是什么?
他是怎么样找到需要特殊处理边界的呢?
41楼 Beaning 2007-12-19 15:49发表 [回复]-
- to housisong
颜色现在改对了,是typedef struct TARGB16 //16 bit color
{
U8 b:4;
U8 g:4;
U8 r:4;
U8 a:4; //a is alpha
}TARGB16;
的问题,谢谢您的提醒:)
真是太感谢了^_^
还是想知道您的mail,您真的很强,很想认识您
40楼 Beaning 2007-12-19 15:22发表 [回复]-
- 我只知道BMP的文件格式,然后是每个像素是RGB,还是您告诉我吧,没看到您的mail地址,告诉您我的mail地址吧,是fean800910@163.com,您给我发个mail,我就知道您的mail了:)
39楼 housisong 2007-12-19 13:08发表 [回复]-
- 你知道16bit高彩色的颜色编码方式吗?
给我发邮件吧 blog上的谈论不适合贴代码
38楼 Beaning 2007-12-19 11:54发表 [回复]-
- for (x=border_x1;x<dst_width;++x)
{
Bilinear2_Border(Src,srcx_16,srcy_16,&pDstLine[x]);//border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((U8*)pDstLine)+=Dst->byte_width;
}
for (y=border_y1;y<(Dst->height)/2;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++x)
{
Bilinear2_Border(Src,srcx_16,srcy_16,&pDstLine[x]); //border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((U8*)pDstLine)+=Dst->byte_width;
}
}
//=============================================================
37楼 Beaning 2007-12-19 11:53发表 [回复]-
- TARGB16* pDstLine=Dst->pdata;
long Src_byte_width=Src->byte_width;
long srcy_16=csDErrorY;
long y;
for (y=0;y<border_y0;++y)
{
long srcx_16=csDErrorX;
for (unsigned long x=0;x<dst_width;++x)
{
Bilinear2_Border(Src,srcx_16,srcy_16,&pDstLine[x]); //border
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
((U8*)pDstLine)+=Dst->byte_width;
}
for (y=border_y0;y<border_y1;++y)
{
long srcx_16=csDErrorX;
long x;
for (x=0;x<border_x0;++x)
{
Bilinear2_Border(Src,srcx_16,srcy_16,&pDstLine[x]);//border
srcx_16+=xrIntFloat_16;
}
{
unsigned long v_8=(srcy_16 & 0xFFFF)>>8;
TARGB16* PSrcLineColor= (TARGB16*)((U8*)(Src->pdata)+Src_byte_width*(srcy_16>>16)) ;
for (unsigned long x=border_x0;x<border_x1;++x)
{
TARGB16* PColor0=&PSrcLineColor[srcx_16>>16];
TARGB16* PColor1=(TARGB16*)((U8*)(PColor0)+Src_byte_width);
Bilinear2_Fast(PColor0,PColor1,(srcx_16 & 0xFFFF)>>8,v_8,&pDstLine[x]);
srcx_16+=xrIntFloat_16;
}
}
for (x=borde
36楼 Beaning 2007-12-19 11:53发表 [回复]-
- pixel[3]=Pixels_Bound(pic,x+1,y+1);
Bilinear2_Fast(&pixel[0],&pixel[2],u_16>>8,v_16>>8,result);
}
//=============================================================
void PicZoom_Bilinear2(const TPicRegion *Dst,const TPicRegion *Src)
{
if ( (0==Dst->width)||(0==Dst->height)
||(0==Src->width)||(0==Src->height)) return;
long xrIntFloat_16=((Src->width)<<16)/Dst->width+1;
long yrIntFloat_16=((Src->height)<<16)/Dst->height+1;
const long csDErrorX=-(1<<15)+(xrIntFloat_16>>1);
const long csDErrorY=-(1<<15)+(yrIntFloat_16>>1);
unsigned long dst_width=Dst->width;
long border_y0=-csDErrorY/yrIntFloat_16+1; //y0+y*yr>=0; y0=csDErrorY => y>=-csDErrorY/yr
if (border_y0>=Dst->height) border_y0=Dst->height;
long border_x0=-csDErrorX/xrIntFloat_16+1;
if (border_x0>=Dst->width ) border_x0=Dst->width;
long border_y1=((((Src->height-2)<<16)-csDErrorY)/yrIntFloat_16+1)/2; //y0+y*yr<=(height-2) => y<=(height-2-csDErrorY)/yr
if (border_y1<border_y0) border_y1=border_y0;
long border_x1=(((Src->width-2)<<16)-csDErrorX)/xrIntFloat_16+1;
if (border_x1<border_x0) border_x1=border_x0;
35楼 Beaning 2007-12-19 11:52发表 [回复]-
- 接上面:
//=============================================================
void Bilinear2_Fast(TARGB16* PColor0,TARGB16* PColor1,unsigned long u_8,unsigned long v_8,TARGB16* result)
{
unsigned long pm3_16=u_8*v_8;
unsigned long pm2_16=(u_8<<8)-pm3_16;
unsigned long pm1_16=(v_8<<8)-pm3_16;
unsigned long pm0_16=(1<<16)-pm1_16-pm2_16-pm3_16;
// result->a=((pm0_16*PColor0[0].a+pm2_16*PColor0[1].a+pm1_16*PColor1[0].a+pm3_16*PColor1[1].a)>>16);
// result->r=((pm0_16*PColor0[0].r+pm2_16*PColor0[1].r+pm1_16*PColor1[0].r+pm3_16*PColor1[1].r)>>16);
result->g=((pm0_16*PColor0[0].g+pm2_16*PColor0[1].g+pm1_16*PColor1[0].g+pm3_16*PColor1[1].g)>>16);
result->b=((pm0_16*PColor0[0].b+pm2_16*PColor0[1].b+pm1_16*PColor1[0].b+pm3_16*PColor1[1].b)>>16);
}
//=============================================================
void Bilinear2_Border(const TPicRegion *pic,const long x_16,const long y_16,TARGB16* result)
{
long x=(x_16>>16);
long y=(y_16>>16);
unsigned long u_16=((unsigned short)(x_16));
unsigned long v_16=((unsigned short)(y_16));
TARGB16 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);
34楼 Beaning 2007-12-19 11:49发表 [回复]-
- to housisong,您好,以下是我从您的32bit的二次线性插值做的图像缩放的code 改成的16bit,但显示的颜色不太对,而且图像有亮暗的闪烁,请问我哪里改的不对呢,谢谢您
typedef struct TARGB16 //32 bit color
{
U8 b,g; //a is alpha
}TARGB16;
typedef struct TPicRegion
{
TARGB16* pdata;
long byte_width;
long width;
long height;
}TPicRegion;
TPicRegion *dst=NULL,*src=NULL;
TARGB16 Pixels(const TPicRegion *pic,const long x,const long y)
{
return ( (TARGB16*)((U8*)pic->pdata+pic->byte_width*y) )[x];
}
//============================================================
TARGB16 Pixels_Bound(const TPicRegion *pic,long x,long y)
{
//assert((pic.width>0)&&(pic.height>0));
FBOOL IsInPic=FTRUE;
if (x<0) {x=0; IsInPic=FFALSE; } else if (x>=pic->width ) {x=pic->width -1; IsInPic=FFALSE; }
if (y<0) {y=0; IsInPic=FFALSE; } else if (y>=pic->height) {y=pic->height-1; IsInPic=FFALSE; }
TARGB16 result=Pixels(pic,x,y);
if (!IsInPic) result.g=0;
return result;
}
//=============================================================
void Bilinear2_Fast(TARGB16* PColor0,TARGB16* PColor1,unsigned long u_8,unsigned long v_8,TARGB16* result)
{
unsigne
33楼 Beaning 2007-12-19 09:25发表 [回复]-
- 专家,您说的转换成16bit定点数,还是不清楚,大概要改动哪,发来参考呢,谢谢您了:)
32楼 housisong 2007-12-19 09:21发表 [回复]-
- to:Beaning
我的文章有旋转的实现:<图形图像处理-之-任意角度的高质量的快速的图像旋转>,分三篇; 难道你没有看到?!
关于不使用sin和cos的旋转,你可以建立一个表,预先保存好各个角度的sin值(cos的计算也可以用这个表);表的大小看你要求的精度,也可以考虑做一下sin小角度插值;
31楼 Beaning 2007-12-18 16:11发表 [回复]-
- 高手,请详细的指点呢,请问有不用sin,cos的处理图像旋转的代码吗
30楼 Beaning 2007-12-18 15:58发表 [回复]-
- 您有写有关图像旋转的代码吗
29楼 Beaning 2007-12-18 14:55发表 [回复]-
- 麻烦您,能具体指点下内存位置计算的代码和RGB颜色分量访问这两个地方吗,十分感谢
28楼 Beaning 2007-12-18 14:35发表 [回复]-
- 您好,我现在用的就是用的PicZoom_BilInear2,想先把这个弄好了,再试MMX的。这是我现在的调用您的PicZoom_BilInear2的情况,能详细的指教吗,或者能顺手改下或者提示下就更好了,我是个初学者:)
typedef struct TARGB32 //32 bit color
{
U8 b,g,r,a; //a is alpha
}TARGB32;
typedef struct TPicRegion
{
TARGB32* pdata;
long byte_width;
long width;
long height;
}TPicRegion;
U32 start_address= NULL;
TPicRegion *dst=NULL,*src=NULL;
src->pdata = (TARGB32*)(source_address+((imageInfo->width/imageInfo->scale/imageInfo->zoomIn)*imageInfo->position_x)+(((imageInfo->height/imageInfo->scale)/imageInfo->zoomIn- 1)*((720*imageInfo->position_y))));//(TARGB32*)(0x60dee0);//
src->byte_width = 720*4;
src->height = imageInfo->height/imageInfo->scale/imageInfo->zoomIn;
src->width = imageInfo->width/imageInfo->scale/imageInfo->zoomIn;
dst->pdata = (TARGB32*)address;
dst->byte_width = 720*4;
dst->width = 720;
dst->height = 576;
a = (float)dst->height/src->height;
b = (float)dst->width/src->width;
if(a<b)
dst->width = (U32)(src->width*a);
else
dst->height= (U32)(src->height*b);
FM_PrStr("zoom START TIME VALUE is ");
FM_PrInt(FD_TIMER_ReadTimerStamp());
PicZoom_Bilinear2(dst,src);
FM_PrSt
27楼 housisong 2007-12-18 12:29发表 [回复]-
- to:bean
需要注意内存位置计算的代码和RGB颜色分量访问这两个地方
如果你不熟悉MMX,建议用PicZoom_BilInear2作为基础来改写
26楼 bean 2007-12-18 12:17发表 [回复]-
- to housisong
我现在想缩放16bit的,不知道用您的函数都需要改什么呢,请指教,很急,谢谢
25楼 bean 2007-12-18 11:25发表 [回复]-
- 你好,我现在想缩放16bit的,请问上面的二次线性插值的函数只要把32的地方改成16是不是就可以用了呢
24楼 fz 2007-11-24 10:48发表 [回复]-
- 恩,没错,我粗心了。
23楼 housisong 2007-11-23 10:49发表 [回复]-
- to: fz
你在仔细算算:)
22楼 fz 2007-11-23 10:11发表 [回复]-
- unsigned long pm0_8=256-pm1_8-pm2_8-pm3_8;
中应该是+pm3_8。
21楼 fz 2007-11-22 16:10发表 [回复]-
- 我也写了几年程序了,但很少看到楼主这么详尽地把过程实现地讲解一个算法,受益匪浅。十分佩服。
20楼 housisong 2007-11-15 15:50发表 [回复]-
- to: wazdxm1980
你自己改一下吧 我把代码都贴出来了,把它改成8位灰度的很容易了吧
我也不可能把每种颜色的实现代码都单独最为例子实现出来吧
(为了一套算法代码,支持多种颜色 建议可以用模板来改写算法,再加上特化来做高级优化 我实际使用中的代码就是)
19楼 wazdxm1980 2007-11-15 13:44发表 [回复]-
- 谢谢。
能否有8位灰度图像缩放的实例,谢谢!
dx.zong@gmail.com
18楼 housisong 2007-11-13 00:07发表 [回复]-
- 二次线性插值使用的公式我替换了,边界处缩放像素值更精确了
17楼 housisong 2007-11-09 13:04发表 [回复]-
- to:deally
1. 对于bilinear我偷懒使用的是一个近似缩放算法,计算出的U/V当然和你修正后的系数不同;
2.这个公式确实有点问题(造成bug),我会在近期替换;缩放精确度会提高; 看来我的改进版本需要早点完成了,估计下周吧
3. :( 关于越界问题,我处理的是ARGB32颜色,当然是写4字节! 你处理的是8位的吧
谢谢deally
16楼 deally 2007-11-09 11:42发表 [回复]-
- MOVd dword ptr [edi+ebx*4],MM0 //write DstColor
这条语句就会造成内存越界的问题,你实际只需要mm0中的一个字节,但是却写了4个字节,考虑一下整个压缩后的图像的最后一个像素。
我觉得Sx=Dx*(SW-1)/DW; Sy=Dy*(SH-1)/DH;
可能会存在3问题,能解释下为什么这么处理么。
图片我发到你邮箱里面了。我这边有一些mmx优化后的固定的bilinear,bicubic算法,只能是1/2,1/4,2比例的。有机会可以帖出来。
15楼 deally 2007-11-09 11:28发表 [回复]-
- 我这边处理一个1/2固定缩放,用你原来的算法和我校正后的算法效果很不一样。我这边有图,你可以看一下。
如果你用Sx=Dx*(SW-1)/DW; Sy=Dy*(SH-1)/DH;
来处理边界后在那些固定的比如1/2算法后,u,v系数就会很不一样,按理说u,v 都=0.5,不信的话你可以去查一下。
我这边也处理了bicubic算法。
14楼 housisong 2007-11-06 16:19发表 [回复]-
- to: deally
你没有仔细看文章;
1. 从PicZoom_BilInear1开始,为了不处理边界访问超界的问题
我使用了一个变形了的公式:Sx=Dx*(SW-1)/DW; Sy=Dy*(SH-1)/DH;
所以后面的函数不需要修正0.5的问题,也没有内存访问超界的问题
2.你如果要使用原来的公式,就必须考虑边界处理问题,边界处理方法可以 参考PicZoom_ThreeOrderX的边界处理方式
3.关于使用MMX的问题,二次线性插值缩放的速度
从59.9fps提高到了136.1fps,你能说MMX优化没有作用?!
4.我会考虑提供一个按照原始缩放算法实现的二次线性插值缩放,可能要过一段时间吧
13楼 deally 2007-11-06 14:57发表 [回复]-
- 回帖怎么只能这么短,我在用楼主的算法时,发现两个问题,void PicZoom_BilInear3(const TPicRegion& Dst,const TPicRegion& Src)
没有校正0.5个像素的的偏移:unsigned long v_8=((srcy_16 & 0xFFFF)+0x8000)>>8;
pDstLine[x]=BilInear3(PColor0,(TARGB32*)((TUInt8*)(PColor0)+Src_byte_width),((srcx_16 & 0xFFFF)+0x8000)>>8,v_8);
同样相应的mmx也存在这样的问题。
还有更致命的就是如果在没有说明的情况下,调用bilinear_mmx算法,会造成内存溢出的问题。所以最好要求dst能多分配4个字节。
希望在文章中改进下。
个人感觉mmx优化在这个算法中作用不是很大,完全没有体现出单指令多数据的特点。
12楼 deally 2007-11-06 14:46发表 [回复]-
- H:使用MMX指令改写;(速度的瓶颈在于计算,内存读写的优化意义不大)
//BilInear_MMX(out [edi+ebx*4];mm6=v_8,mm7=0,edx=srcx_16,esi=PSrcLineColor,ecx=PSrcLineColorNext)
void __declspec(naked) BilInear_MMX()
{
asm
{
mov eax,edx
shl eax,16
add eax,0x80000000;//校正0.5个像素
shr eax,24
//== movzx eax,dh //eax=u_8
MOVD MM5,eax
mov eax,edx
shr eax,16 //srcx_16>>16
MOVD MM0,dword ptr [ecx+eax*4+4]//MM0=Color0
MOVD MM2,dword ptr [ecx+eax*4] //MM2=Color2
PUNPCKLWD MM5,MM5
MOVD MM1,dword ptr [esi+eax*4+4]//MM1=Color1
MOVD MM3,dword ptr [esi+eax*4] //MM3=Color3
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
11楼 zww 2007-10-10 18:35发表 [回复]-
- 你那有改写过的吗?我现在急着要用,要有麻烦发到我邮箱吧:zwwll520@163.com。谢谢了。
10楼 housisong 2007-10-10 11:29发表 [回复]-
- 对于24位RGB颜色,上面的函数你都需要修改一下,特别是内存访问的地方(3字节颜色的内存位置计算比较麻烦呵), 有人改写过的。
9楼 zww 2007-10-10 10:50发表 [回复]-
- 谢谢,我现在要对24位bmp进行放大,是不是只要把32位的改成24位的,其他的函数都可以继续使用吧。
8楼 housisong 2007-10-09 23:20发表 [回复]-
- to: zww
你可以使用windows里的DIB作为数据源;
Delphi或CB中你可以用TBitmap数据作为数据源;
实在不行,你自己建一个RGB32的数组,自己加载图片数据到数组,作为数据源;
7楼 zww 2007-10-09 22:19发表 [回复]-
- 真的谢谢你的大作,真是太强了,怎样得到颜色数据首地址?能把完整代码发给我吗。我的邮箱:zwwll520@163.com.谢谢了。对于其中的一些细节还不是很清楚。
6楼 Leaveye 2007-08-14 15:49发表 [回复]-
- 搜索过程中发现您的文章已经被转载至此:
http://school.ogdev.net/ArticleShow.asp?id=6136&categoryid=5
5楼 riverqh 2007-07-29 16:02发表 [回复]-
- 佩服得一塌糊涂,把楼主的BLOG URL加入收藏夹方便查看。
多谢楼主分享!
4楼 housisong 2007-01-14 09:19发表 [回复]-
- to:51357
实际应用中, pic.byte_width很有可能>=sizeof(TARGB32) * pic.width
比如该TPicRegion是一个更大的TPicRegion的一个子区域;或者TPicRegion为了每行的起始数据内存对齐;或者是DirectX的表面等
定义pic.byte_width也就是为了代码的通用性
3楼 Jackson_GZ 2007-01-13 15:59发表 [回复]-
- sorry, 是我看错了
pic.byte_width = sizeof(TARGB32) * pic.width
2楼 Jackson_GZ 2007-01-13 15:48发表 [回复] [引用] [举报]-
- //那么访问一个点的函数可以写为:
inline TARGB32& Pixels(const TPicRegion& pic,const long x,const long y)
{
return ( (TARGB32*)((TUInt8*)pic.pdata+pic.byte_width*y) )[x];
}
这时有误,是否漏了 pic.width ?
pic.byte_width*y 改成 pic.byte_width*y * pic.width