900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > c/c++实现图像旋转任意角度

c/c++实现图像旋转任意角度

时间:2021-12-04 06:33:52

相关推荐

c/c++实现图像旋转任意角度

旋转原理和旋转公式:

文章出处

/wonengguwozai/article/details/52049092

推导旋转公式:

旋转示意图

有: tg(b)=y/x ----(1)

tg(a+b)=y’/x’ ----(2)

xx + yy = x’x’ + y’y’ ----(3)

有公式:tg(a+b) = ( tg(a)+tg(b) ) / ( 1-tg(a)tg(b) ) ----(4)

把(1)代入(4)从而消除参数b;

tg(a)+y/x = y’/x’( 1-tg(a)y/x ) ----(5)

由(5)可以得x’=y’(x-ytg(a))/( xtg(a)+y ) ----(6)

把(6)代入(3)从而消除参数x’,化简后求得:

y’=xsin(a)+ycos(a); ----(7)

把(7)代入(6),有:

x’=xcos(a)-ysin(a); ----(8)

OK,旋转公式有了,那么来看看在图片旋转中的应用;

假设对图片上任意点(x,y),绕一个坐标点(rx0,ry0)逆时针旋转RotaryAngle角度后的新的坐标设为(x’, y’),有公式:

(x平移rx0,y平移ry0,角度a对应-RotaryAngle , 带入方程(7)、(8)后有: )

x’= (x - rx0)*cos(RotaryAngle) + (y - ry0)*sin(RotaryAngle) + rx0 ;

y’=-(x - rx0)*sin(RotaryAngle) + (y - ry0)*cos(RotaryAngle) + ry0 ;

那么,根据新的坐标点求源坐标点的公式为:

x=(x’- rx0)*cos(RotaryAngle) - (y’- ry0)*sin(RotaryAngle) + rx0 ;

y=(x’- rx0)*sin(RotaryAngle) + (y’- ry0)*cos(RotaryAngle) + ry0 ;

旋转的时候还可以顺便加入x轴和y轴的缩放和平移,而不影响速度,那么完整的公式为:

x=(x’- move_x-rx0)/ZoomXcos(RotaryAngle) - (y’- move_y-ry0)/ZoomYsin(RotaryAngle) + rx0 ;

y=(x’- move_x-rx0)/ZoomXsin(RotaryAngle) + (y’- move_y-ry0)/ZoomYcos(RotaryAngle) + ry0 ;

其中: RotaryAngle为逆时针旋转的角度;

ZoomX,ZoomY为x轴y轴的缩放系数(支持负的系数,相当于图像翻转);

move_x,move_y为x轴y轴的平移量;

一些颜色和图片的数据定义:

#define asm __asmtypedef 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];}//判断一个点是否在图片中inline bool PixelsIsInPic(const TPicRegion& pic,const long x,const long y){return ( (x>=0)&&(x<pic.width) && (y>=0)&&(y< pic.height) );}

B:一个简单的浮点实现版本

//函数假设以原图片的中心点坐标为旋转和缩放的中心void PicRotary0(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double move_y){if ( (fabs(ZoomX*Src.width)<1.0e-4) || (fabs(ZoomY*Src.height)<1.0e-4) ) return; //太小的缩放比例认为已经不可见double rx0=Src.width*0.5; //(rx0,ry0)为旋转中心 double ry0=Src.height*0.5 ; for (long y=0;y<Dst.height;++ y){for (long x=0;x<Dst.width;++ x){long srcx=(long)((x- move_x-rx0)/ZoomX*cos(RotaryAngle) - (y- move_y-ry0)/ZoomY*sin(RotaryAngle) + rx0) ;long srcy=(long)((x- move_x-rx0)/ZoomX*sin(RotaryAngle) + (y- move_y-ry0)/ZoomY*cos(RotaryAngle) + ry0) ;if (PixelsIsInPic(Src,srcx,srcy))Pixels(Dst,x,y)= Pixels(Src,srcx,srcy);}}}

(调用方法比如:

PicRotary0(ppicDst,ppicSrc,PI/6,0.9,0.9,(dst_wh-ppicSrc.width)*0.5,(dst_wh-ppicSrc.height)*0.5);

//作用:将图片ppicSrc按0.9的缩放比例旋转PI/6幅度后绘制到图片ppicDst的中心

)

//注:测试图片都是800600的图片旋转到10041004的图片中心 测试成绩取各个旋转角度的平均速度值

速度测试:

优化循环内部,化简系数

1.sin和cos函数是很慢的计算函数,可以在循环前预先计算好sin(RotaryAngle)和cos(RotaryAngle)的值:

double sinA=sin(RotaryAngle);

double cosA=cos(RotaryAngle);

2.可以将除以ZoomX、ZoomY改成乘法,预先计算出倒数:

double rZoomX=1.0/ZoomX;

double rZoomY=1.0/ZoomY;

3.优化内部的旋转公式,将能够预先计算的部分提到循环外(即:拆解公式):

原: long srcx=(long)((x- move_x-rx0)/ZoomXcos(RotaryAngle) - (y- move_y-ry0)/ZoomYsin(RotaryAngle) + rx0) ;

long srcy=(long)((x- move_x-rx0)/ZoomXsin(RotaryAngle) + (y- move_y-ry0)/ZoomYcos(RotaryAngle) + ry0) ;

变形为:

long srcx=(long)( Axx + Bxy +Cx ) ;

long srcy=(long)( Ayx + Byy +Cy ) ;

其中: Ax=(rZoomXcosA); Bx=(-rZoomYsinA); Cx=(-(rx0+move_x)rZoomXcosA+(ry0+move_y)rZoomYsinA+rx0);

Ay=(rZoomXsinA); By=(rZoomYcosA); Cy=(-(rx0+move_x)rZoomXsinA-(ry0+move_y)rZoomYcosA+ry0);

(提示: Ax,Bx,Cx,Ay,By,Cy都可以在旋转之前预先计算出来)

改进后的函数为:

void PicRotary1(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double move_y){if ( (fabs(ZoomX*Src.width)<1.0e-4) || (fabs(ZoomY*Src.height)<1.0e-4) ) return; //太小的缩放比例认为已经不可见double rZoomX=1.0/ ZoomX;double rZoomY=1.0/ ZoomY;double sinA= sin(RotaryAngle);double cosA= cos(RotaryAngle);double Ax=(rZoomX* cosA); double Ay=(rZoomX* sinA); double Bx=(-rZoomY* sinA); double By=(rZoomY* cosA); double rx0=Src.width*0.5; //(rx0,ry0)为旋转中心 double ry0=Src.height*0.5 ; double Cx=(-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+ rx0);double Cy=(-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ ry0); TARGB32* pDstLine= Dst.pdata;double srcx0_f= (Cx);double srcy0_f= (Cy);for (long y=0;y<Dst.height;++ y){double srcx_f= srcx0_f;double srcy_f= srcy0_f;for (long x=0;x<Dst.width;++ x){long srcx=(long )(srcx_f);long srcy=(long )(srcy_f);if (PixelsIsInPic(Src,srcx,srcy))pDstLine[x]= Pixels(Src,srcx,srcy);srcx_f+= Ax;srcy_f+= Ay;}srcx0_f+= Bx;srcy0_f+= By;((TUInt8*&)pDstLine)+= Dst.byte_width;}}

//速度测试:

//==============================================================================

// PicRotary1 62.0 fps

( 在AMD64x2 4200+和VC编译下PicRotary1(51.8fps)比PicRotary0(27.1fps)快90%;

在AMD64x2 4200+和VC6编译下PicRotary1(20.3fps)比PicRotary0(16.1fps)快26%;

以前在赛扬2G和VC6编译下PicRotary1(8.4fps)反而比PicRotary0(12.7fps)慢50%! 😦 )

D:更深入的优化、定点数优化

(浮点数到整数的转化也是应该优化的一个地方,这里不再处理,可以参见

<图形图像处理-之-高质量的快速的图像缩放 上篇 近邻取样插值和其速度优化>中的PicZoom3_float函数)

1.优化除法:

原: double rZoomX=1.0/ZoomX;

double rZoomY=1.0/ZoomY;

改写为(优化掉了一次除法):

double tmprZoomXY=1.0/(ZoomXZoomY);

double rZoomX=tmprZoomXYZoomY;

double rZoomY=tmprZoomXY*ZoomX;

2.x86的浮点计算单元(FPU)有一条指令"fsincos"可以同时计算出sin和cos值

//定义SinCos函数: 同时计算sin(Angle)和cos(Angle)的内嵌x86汇编函数

void __declspec(naked) __stdcall SinCos(const double Angle,double& sina,double& cosa)

{

asm

{

fld qword ptr [esp+4]//Angle

mov eax,[esp+12]//&sina

mov edx,[esp+16]//&cosa

fsincos

fstp qword ptr [edx]

fstp qword ptr [eax]

ret 16

}

}

用定点数代替浮点计算

void PicRotary2(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double move_y){if ( (fabs(ZoomX*Src.width)<1.0e-4) || (fabs(ZoomY*Src.height)<1.0e-4) ) return; //太小的缩放比例认为已经不可见double tmprZoomXY=1.0/(ZoomX* ZoomY); double rZoomX=tmprZoomXY* ZoomY;double rZoomY=tmprZoomXY* ZoomX;double sinA,cosA;SinCos(RotaryAngle,sinA,cosA);long Ax_16=(long)(rZoomX*cosA*(1<<16 )); long Ay_16=(long)(rZoomX*sinA*(1<<16 )); long Bx_16=(long)(-rZoomY*sinA*(1<<16 )); long By_16=(long)(rZoomY*cosA*(1<<16 )); double rx0=Src.width*0.5; //(rx0,ry0)为旋转中心 double ry0=Src.height*0.5 ; long Cx_16=(long)((-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0)*(1<<16 ));long Cy_16=(long)((-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0)*(1<<16 )); TARGB32* pDstLine= Dst.pdata;long srcx0_16= (Cx_16);long srcy0_16= (Cy_16);for (long y=0;y<Dst.height;++ y){long srcx_16= srcx0_16;long srcy_16= srcy0_16;for (long x=0;x<Dst.width;++ x){long srcx=(srcx_16>>16 );long srcy=(srcy_16>>16 );if (PixelsIsInPic(Src,srcx,srcy))pDstLine[x]= Pixels(Src,srcx,srcy);srcx_16+= Ax_16;srcy_16+= Ay_16;}srcx0_16+= Bx_16;srcy0_16+= By_16;((TUInt8*&)pDstLine)+= Dst.byte_width;}}

//速度测试:

//==============================================================================

// PicRotary2 134.2 fps

E:优化内部循环的判断函数PixelsIsInPic,将判断展开到内部循环之外,跳过不需要处理的目标像素;

TRotaryClipData类用于寻找旋转需要处理的边界范围;算法思路是首先寻找原图片中心点对应的;

那条扫描线,然后依次向上和向下寻找边界; 如果想要更快速的边界寻找算法,可以考虑利用像素的直线

绘制原理来自动生成边界(有机会的时候再来实现它);

边界寻找算法图示

struct TRotaryClipData{public :long src_width;long src_height;long dst_width;long dst_height;long Ax_16; long Ay_16; long Bx_16; long By_16; long Cx_16;long Cy_16; public :long out_dst_up_y;long out_dst_down_y;long out_src_x0_16;long out_src_y0_16;private :long out_dst_up_x0;long out_dst_up_x1;long out_dst_down_x0;long out_dst_down_x1;public :inline long get_up_x0(){if (out_dst_up_x0<0) return 0; else return out_dst_up_x0; }inline long get_up_x1(){if (out_dst_up_x1>=dst_width) return dst_width; else return out_dst_up_x1; }inline long get_down_x0(){if (out_dst_down_x0<0) return 0; else return out_dst_down_x0; }inline long get_down_x1(){if (out_dst_down_x1>=dst_width) return dst_width; else return out_dst_down_x1; }inline bool is_in_src(long src_x_16,long src_y_16){return ( ( (src_x_16>=0)&&((src_x_16>>16)< src_width) )&& ( (src_y_16>=0)&&((src_y_16>>16)< src_height) ) );}void find_begin_in(long dst_y,long& out_dst_x,long& src_x_16,long& src_y_16){src_x_16-= Ax_16;src_y_16-= Ay_16;while (is_in_src(src_x_16,src_y_16)){-- out_dst_x;src_x_16-= Ax_16;src_y_16-= Ay_16;}src_x_16+= Ax_16;src_y_16+= Ay_16;}bool find_begin(long dst_y,long& out_dst_x0,long dst_x1){long test_dst_x0=out_dst_x0-1 ;long src_x_16=Ax_16*test_dst_x0 + Bx_16*dst_y + Cx_16;long src_y_16=Ay_16*test_dst_x0 + By_16*dst_y + Cy_16;for (long i=test_dst_x0;i<=dst_x1;++ i){if (is_in_src(src_x_16,src_y_16)){out_dst_x0= i;if (i== test_dst_x0)find_begin_in(dst_y,out_dst_x0,src_x_16,src_y_16);if (out_dst_x0<0 ){src_x_16-=(Ax_16* out_dst_x0);src_y_16-=(Ay_16* out_dst_x0);}out_src_x0_16= src_x_16;out_src_y0_16= src_y_16;return true ;}else{src_x_16 += Ax_16;src_y_16+= Ay_16;}}return false ;}void find_end(long dst_y,long dst_x0,long& out_dst_x1){long test_dst_x1= out_dst_x1;if (test_dst_x1<dst_x0) test_dst_x1= dst_x0;long src_x_16=Ax_16*test_dst_x1 + Bx_16*dst_y + Cx_16;long src_y_16=Ay_16*test_dst_x1 + By_16*dst_y + Cy_16;if (is_in_src(src_x_16,src_y_16)){++ test_dst_x1;src_x_16+= Ax_16;src_y_16+= Ay_16;while (is_in_src(src_x_16,src_y_16)){++ test_dst_x1;src_x_16+= Ax_16;src_y_16+= Ay_16;}out_dst_x1= test_dst_x1;}else{src_x_16 -= Ax_16;src_y_16-= Ay_16;while (! is_in_src(src_x_16,src_y_16)){-- test_dst_x1;src_x_16-= Ax_16;src_y_16-= Ay_16;}out_dst_x1= test_dst_x1;}}bool inti_clip(double move_x,double move_y){//计算src中心点映射到dst后的坐标out_dst_down_y=(long)(src_height*0.5+ move_y);out_dst_down_x0=(long)(src_width*0.5+ move_x);out_dst_down_x1= out_dst_down_x0;//得到初始out_???if (find_begin(out_dst_down_y,out_dst_down_x0,out_dst_down_x1))find_end(out_dst_down_y,out_dst_down_x0,out_dst_down_x1);out_dst_up_y= out_dst_down_y;out_dst_up_x0= out_dst_down_x0;out_dst_up_x1= out_dst_down_x1;return (out_dst_down_x0< out_dst_down_x1);}bool next_clip_line_down(){++ out_dst_down_y;if (!find_begin(out_dst_down_y,out_dst_down_x0,out_dst_down_x1)) return false ;find_end(out_dst_down_y,out_dst_down_x0,out_dst_down_x1);return (out_dst_down_x0< out_dst_down_x1);}bool next_clip_line_up(){-- out_dst_up_y;if (!find_begin(out_dst_up_y,out_dst_up_x0,out_dst_up_x1)) return false ;find_end(out_dst_up_y,out_dst_up_x0,out_dst_up_x1);return (out_dst_up_x0< out_dst_up_x1);}};

void PicRotary3_CopyLine(TARGB32* pDstLine,long dstCount,long Ax_16,long Ay_16,long srcx0_16,long srcy0_16,TARGB32* pSrcLine,long src_byte_width){for (long x=0;x<dstCount;++ x){pDstLine[x]=Pixels(pSrcLine,src_byte_width,srcx0_16>>16,srcy0_16>>16 );srcx0_16+= Ax_16;srcy0_16+= Ay_16;}}void PicRotary3(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double move_y){if ( (fabs(ZoomX*Src.width)<1.0e-4) || (fabs(ZoomY*Src.height)<1.0e-4) ) return; //太小的缩放比例认为已经不可见double tmprZoomXY=1.0/(ZoomX* ZoomY); double rZoomX=tmprZoomXY* ZoomY;double rZoomY=tmprZoomXY* ZoomX;double sinA,cosA;SinCos(RotaryAngle,sinA,cosA);long Ax_16=(long)(rZoomX*cosA*(1<<16 )); long Ay_16=(long)(rZoomX*sinA*(1<<16 )); long Bx_16=(long)(-rZoomY*sinA*(1<<16 )); long By_16=(long)(rZoomY*cosA*(1<<16 )); double rx0=Src.width*0.5; //(rx0,ry0)为旋转中心 double ry0=Src.height*0.5 ; long Cx_16=(long)((-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0)*(1<<16 ));long Cy_16=(long)((-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0)*(1<<16 )); TRotaryClipData rcData;rcData.Ax_16= Ax_16;rcData.Bx_16= Bx_16;rcData.Cx_16= Cx_16;rcData.Ay_16= Ay_16;rcData.By_16= By_16;rcData.Cy_16= Cy_16;rcData.dst_width= Dst.width;rcData.dst_height= Dst.height;rcData.src_width= Src.width;rcData.src_height= Src.height;if (!rcData.inti_clip(move_x,move_y)) return ;TARGB32* pDstLine= Dst.pdata;((TUInt8*&)pDstLine)+=(Dst.byte_width* rcData.out_dst_down_y);while (true) //to down{long y= rcData.out_dst_down_y;if (y>=Dst.height) break ;if (y>=0 ){long x0= rcData.get_down_x0();PicRotary3_CopyLine(&pDstLine[x0],rcData.get_down_x1()- x0,Ax_16,Ay_16,rcData.out_src_x0_16,rcData.out_src_y0_16,Src.pdata,Src.byte_width);}if (!rcData.next_clip_line_down()) break ;((TUInt8*&)pDstLine)+= Dst.byte_width;}pDstLine= Dst.pdata;((TUInt8*&)pDstLine)+=(Dst.byte_width* rcData.out_dst_up_y);while (rcData.next_clip_line_up()) //to up{long y= rcData.out_dst_up_y;if (y<0) break ;((TUInt8*&)pDstLine)-= Dst.byte_width;if (y< Dst.height){long x0= rcData.get_up_x0();PicRotary3_CopyLine(&pDstLine[x0],rcData.get_up_x1()- x0,Ax_16,Ay_16,rcData.out_src_x0_16,rcData.out_src_y0_16,Src.pdata,Src.byte_width);}}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。