上一篇文章介绍了图像的几何旋转,是计算原图中每个像素点旋转后的位置,将原图中的像素点的值赋给旋转后对应位置的像素点。这样存在的一个问题就是,原图中有些像素点旋转后的坐标取整后会映射到同一个点,这就造成了旋转后的图像中会出现一些黑点,也就是图像范围中有一些点是映射不到的。
这篇文章介绍的出发点是解决图像旋转后出现黑点的问题,所使用的方法是利用插值方法。其思想是,将旋转后图像的像素点映射回原图像,找到它的采样点,就是旋转的逆变换,将变换后的图像旋转到原图。映射的结果不会都是整数像素点,那么旋转后的点的像素值由与采样点最邻近的像素值表示,这是最近邻插值。
如下图,P0(x0,y0)是原图像中的点,P1(x1,y1)是旋转后的点,P1映射到原图中得到的P0附近的红点,近似地用P0表示红点的像素值;
点P0旋转θ角度到P1点。由P1映射到P0的方法是,令
由P1到P0,有,
于是有,
先给出利用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);
变换结果如下图,可以看出,图像的边缘处有锯齿状,这是由于采样点的近似值与原始值有偏差引起的,合适的插值方法可以解决这一问题。
改变尺寸的图像旋转
这种旋转是将旋转后的图像内容完全显示出来,所以要先确定新的图像的尺寸,在上一篇文章中有关于新图像尺寸计算的介绍。
新图像中每一个像素点都参与逆变换过程,而目标像素点是经过逆变换落在原图像区域的点,对这些点进行相应的赋值。代码如下:
//改变尺寸插值图像旋转
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);
变换结果如下,同样的,图像的边缘处有锯齿状。