opencv图像旋转--矩阵旋转和仿射变换相比较

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liumangmao1314/article/details/53367387

opencv图像旋转--矩阵旋转和仿射变换相比较

这段时间在用opencv实现一些图像基本功能,旋转啊,对比度,亮度增强等,在该篇文章做的实验时图像旋转时,发现有两种方法(我这里都会介绍),我将两种方法处理结果情况作对比,对比发现仿射变换做的旋转效果比点位移旋转要好很多。
点位移基本思路就是旋转后图像每个点坐标以及像素对应旋转之前点的坐标以及像素。我给个关于点位移链接,之前我也是不明白该算法思路,后来看到这篇文章,写的很仔细,理解透了。点击打开链接,这篇文章关于图像矩阵旋转写的很详细,我就命名为点位移,也是我导师命名的。
图像仿射变换就是利用opencv2库函数warpAffine(),具体实现方法不详,有兴趣可以参考其他资料。图像仿射变换有个缺点,就是按opencv2库函数getRotationMatrix2D()的话,得到图像左上角的图像显示不了。就像如下图这样:


然后只能上网找资料,发现了解决方案,具体那篇文章也找不到链接了,发现大神很多,哈哈。
话不多说,进入代码阶段:
第一部分是矩阵旋转,就是点位移。
Mat AngleRotate(Mat srcimage,float angle)//普通旋转
{
	/*
	旋转过程:
	1、将原始图像的坐标系换成数学坐标系
	2、通过旋转公式对图像坐标进行旋转
	3、将旋转后的数学坐标系转换成图像坐标系
	                  |1       0  0| |cos -sin 0| |1     0     0|
	[x,y,1]=[x0,y0,1]*|0      -1  0|*|sin  cos 0|*|0     -1    0|
	                  |-0.5w 0.5h 1| | 0    0  1| |0.5w' 0.5h' 1|
	但是这是旋转过程,接着要反转,就是新图像每个坐标的像素对应之前图像坐标的像素
						  |1       0  0| |cos  sin 0| |1     0     0|  
    就是[x0,y0,1]=[x,y,1]*|0      -1  0|*|-sin cos 0|*|0     -1    0| 
						  |-0.5w'0.5h'1| | 0    0  1| |0.5w  0.5h  1|
	最终得到   x0=xcos+ysin-0.5w'cos-0.5h,sin+0.5w;
	           y0=-xsin+ycos+0.5w'sin-0.5h'cos+0.5h;
	*/

	float alpha=angle*CV_PI/180.0;
	float cs=cos(alpha);
	float sn=sin(alpha);
	int nrows=srcimage.rows;  
	int ncols=srcimage.cols;

	// 旋转后四个角的坐标(以图像中心为坐标系原点)
	float ao=-0.5*ncols*cs+0.5*nrows*sn;
	float bo=-0.5*ncols*(-sn)+0.5*nrows*cs;
	float a1=0.5*ncols*cs+0.5*nrows*sn;
	float b1=0.5*ncols*(-sn)+0.5*nrows*cs;
	float a2=-0.5*ncols*cs-0.5*nrows*sn;
	float b2=-0.5*ncols*(-sn)-0.5*nrows*cs;
	float a3=0.5*ncols*cs-0.5*nrows*sn;
	float b3=0.5*ncols*(-sn)-0.5*nrows*cs;
	int Rows=max(fabs(b3-bo),fabs(b2-b1));
	int Cols=max(fabs(a3-ao),fabs(a2-a1));

	cv::Mat dstimage(Rows,Cols,srcimage.type());
	for(int i=0;i<Rows;i++)
	{
		for(int j=0;j<Cols;j++)
		{
			//新图像坐标对应老图像坐标
			int x=j*cs+i*sn-0.5*Cols*cs-0.5*Rows*sn+0.5*ncols;
			int y=-j*sn+i*cs+0.5*Cols*sn-0.5*Rows*cs+0.5*nrows;
			if(x>=0 && x<ncols && y>=0 && y<nrows)
			{
				if(srcimage.channels()==3)//彩色图像channel通道是3
					dstimage.at<cv::Vec3b>(i,j)=srcimage.at<cv::Vec3b>(y,x);
				else if(srcimage.channels()==1)//灰度图像channel通道是1
					dstimage.at<uchar>(i,j)=srcimage.at<uchar>(y,x);
			}
			else
			{
				if(srcimage.channels()==3)
					dstimage.at<cv::Vec3b>(i,j)=cv::Vec3b(0,0,0);
				else if(srcimage.channels()==1)
					dstimage.at<uchar>(i,j)=0;
			}
		}
	}
	return dstimage;
}

两部分代码的具体思想很类似,先确定旋转后图像尺寸,点位移需要找到旋转后图像的中心点,而这里使用图像仿射变换不需要找图像中心点,通过函数bound()找出旋转后图像的xmin,xmax,ymin,ymax,再通过位移将刚才所说的那种情况将像素点都往右下移,就是代码的矩阵M赋值了。
第二部分,是图像仿射变换:

void bound(int x, int y, float ca, float sa, int *xmin, int *xmax, int *ymin, int *ymax)
/* int x,y;
 float ca,sa;
 int *xmin,*xmax,*ymin,*ymax;*/
{   
    int rx,ry;
    // 顺时针旋转
    rx = (int)floor(ca*(float)x+sa*(float)y);
    ry = (int)floor(-sa*(float)x+ca*(float)y);
    if (rx<*xmin) *xmin=rx; if (rx>*xmax) *xmax=rx;
    if (ry<*ymin) *ymin=ry; if (ry>*ymax) *ymax=ry;
}
cv::Mat  AngleAffinis(Mat srcimage,float angle)//仿射旋转
{
	int  width,high; 
    float  ca,sa;
    int  xmin,xmax,ymin,ymax,newwidth,newhigh;
    ca = (float)cos((double)(angle)*CV_PI/180.0);
    sa = (float)sin((double)(angle)*CV_PI/180.0);
    width = srcimage.cols;
    high=srcimage.rows;
    xmin = xmax = ymin = ymax = 0;
	/*
	除原点其他三个点经过旋转之后判断点坐标,确定 xmin,xmax,ymin,ymax。
	*/
    bound(width-1,0,ca,sa,&xmin,&xmax,&ymin,&ymax);
    bound(0,high-1,ca,sa,&xmin,&xmax,&ymin,&ymax);
    bound(width-1,high-1,ca,sa,&xmin,&xmax,&ymin,&ymax);
    newwidth = xmax-xmin+1;
    newhigh = ymax-ymin+1;
    cv::Mat dstimage=cv::Mat::zeros(cv::Size(newwidth,newhigh),srcimage.type());
	Mat M( 2, 3, CV_64F);
	double* m=(double*)M.data;
	/*平移的值,就是将左上方的图像点,平移值图像原点。
	如果调用getRotationMatrix2D函数,则结果是左上部分不能显示出来,
	而如下所示则是将整幅图像向左平移xmin,向下平移ymin。
	*/
    m[0] = ca;
    m[1] = sa;
    m[2] =-(float)xmin; 
    m[3] =-m[1];
    m[4] = m[0];
    m[5] =-(float)ymin;
	warpAffine(srcimage, dstimage,M,dstimage.size());
	return dstimage;
}

两种旋转结果的对比图如下:


你看出不一样的地方么?发现仿射变换几乎看不出锯齿感,虽然还是有一点,哈哈,但是比点位移好很多。
文章可能有些不专业的地方,还请大家批评指正。

猜你喜欢

转载自blog.csdn.net/liumangmao1314/article/details/53367387
今日推荐