当前位置:C++技术网 > 资讯 > opencv编程:15 基于近邻插值的图像旋转

opencv编程:15 基于近邻插值的图像旋转

更新时间:2016-05-30 22:18:31浏览次数:1+次

  上一篇文章介绍了图像的几何旋转,是计算原图中每个像素点旋转后的位置,将原图中的像素点的值赋给旋转后对应位置的像素点。这样存在的一个问题就是,原图中有些像素点旋转后的坐标取整后会映射到同一个点,这就造成了旋转后的图像中会出现一些黑点,也就是图像范围中有一些点是映射不到的。

  这篇文章介绍的出发点是解决图像旋转后出现黑点的问题,所使用的方法是利用插值方法。其思想是,将旋转后图像的像素点映射回原图像,找到它的采样点,就是旋转的逆变换,将变换后的图像旋转到原图。映射的结果不会都是整数像素点,那么旋转后的点的像素值由与采样点最邻近的像素值表示,这是最近邻插值。

  如下图,P0(x0,y0)是原图像中的点,P1(x1,y1)是旋转后的点,P1映射到原图中得到的P0附近的红点,近似地用P0表示红点的像素值;

P0旋转θ角度到P1点。由P1映射到P0的方法是,令

P1P0,有,

于是有,

先给出利用opencv自带的函数进行图像旋转的结果,旋转角度的问题在上一篇中有介绍,这里不再赘述,其代码如下:

//opencv函数旋转图像
Mat image=imread("chuli.jpg");
float map[6];
Mat map_matrix;
map_matrix=getRotationMatrix2D(Point(0,0),-15,1.0);  //旋转中心,旋转角度,缩放比例
Mat src(image.rows,image.cols,CV_8UC3);
warpAffine(image,src,map_matrix,Size(image.cols,image.rows));
namedWindow("src");
imshow("src",src);
waitKey(0);

处理结果如下,左边为原图像,右边为opencv函数旋转结果。

                   

不改变图像尺寸的插值旋转变换

  在原图上做旋转,不改变图像的尺寸,对内容进行旋转。根据旋转的角度,选择是按行进行变换还是按列进行变换。这里所利用的不是最近邻插值,而是直接将映射结果取整,如何实现最近邻插值,有兴趣的读者可以思考下。下面附上代码

//不改变尺寸
Mat image=imread("chuli.jpg");
int x0,y0,x1,y1;  //分别是映射回原图像的横坐标与纵坐标,旋转后的横坐标与纵坐标
double pi=3.14159265358979323846264;
double angle=pi/12;
//按列变换
for (x1=0;x1<image.cols;x1++)
{
	for (y1=0;y1<image.rows;y1++)
	{
		x0=x1*cos(angle) + y1*sin(angle);
		y0=y1*cos(angle) – x1*sin(angle);
		if (x0>=0 && x0<image.cols && y0>=0 && y0<image.rows)
		{
			image.at<Vec3b>(Point(x1,y1))=image.at<Vec3b>(Point(x0,y0));
		}
		else
			image.at<Vec3b>(Point(x1,y1))= 0;
	}
}
namedWindow("tatote");
imshow("tatote",image);
waitKey(0);

变换结果如下图,可以看出,图像的边缘处有锯齿状,这是由于采样点的近似值与原始值有偏差引起的,合适的插值方法可以解决这一问题。

                                   

改变尺寸的图像旋转

  这种旋转是将旋转后的图像内容完全显示出来,所以要先确定新的图像的尺寸,在上一篇文章中有关于新图像尺寸计算的介绍。

关键问题是如何将旋转后的图像逆变换后得到原图像。本篇文章所用的图像的轮廓示意图如下,黑色边框是新图像的轮廓,深红色轮廓是原图旋转后的轮廓。在新图像中原图的旋转可以看做是绕A点进行旋转,在做新图像向原图像映射的时候,是先把新图像左移|OA|的距离,绕点O进行逆旋转。

                              

 新图像中每一个像素点都参与逆变换过程,而目标像素点是经过逆变换落在原图像区域的点,对这些点进行相应的赋值。代码如下:

//改变尺寸插值图像旋转
Mat image1=imread("chuli.jpg");
int x0,,y0,x1,y1;
double pi=3.14159265358979323846264;
double angle=pi/12;
int dx = (int)(image1.cols*cos(angle) + image1.rows*sin(angle));
int dy = (int)(image1.cols*sin(angle) + image1.rows*cos(angle));
Mat dst(dy,dx,CV_8UC3,Scalar(0));  //创建新图像

for (x1=0;x1<dst.cols;x1++)
{
	for (y1=0;y1<dst.rows;y1++)
	{
		x0=(x1-image1.rows*sin(angle))*cos(angle)+y1*sin(angle);
    	y0=y1*cos(angle) - (x1-image1.rows*sin(angle))*sin(angle);

		if (x0>=0 && x0<image.cols && y0>=0 && y0<image.rows)
		{
			dst.at<Vec3b>(Point(x1,y1))=image1.at<Vec3b>(Point(x0,y0));
		}
		else
			dst.at<Vec3b>(Point(x1,y1))=0;
	}
}
namedWindow("dst");
imshow("dst",dst);
waitKey(0);

变换结果如下,同样的,图像的边缘处有锯齿状。