在图片处理中,经常会遇到点的旋转和坐标系的旋转问题,这里对一个点(x, y)绕另一个点(x0, y0)旋转的问题做一个总结。
一、在介绍之前,先总结一下编程的问题,因为在实现的过程中碰到了一些坑:
(1)c++中,三角函数的输入和反三角函数的输出都是:弧度。
(2)opencv中涉及的都是角度,比如旋转矩形的角度、仿射变换时旋转图片的角度。
二、假设有两个点,一个点为p(x, y),另一个点为p0(x0, y0)。让点p绕着点p0旋转α(角度或者弧度都可以),求旋转之后点的坐标p1(x1, y1)。我们可以分成三个步骤来完成:(1)先将原点移动到旋转中心。(2)让p0围绕原点旋转α。(3)移回坐标原点。
1、将原点移动到旋转中心p0处。那么点p0在新坐标系中的坐标为:(0, 0),点p1在新坐标系中的坐标为:(x - x0, y- y0)。
2、将点(x - x0, y- y0)绕着原点旋转角度α,将旋转之后点的坐标记为p2(x2, y2)。那么:
x2 = (x - x0) * cosα + (y - y0) * sinα, y2 = -(x - x0) * sinα + (y - y0) * cosα
3、将坐标原点移回到原来的位置,那么可以根据点p2得到我们最终的结果p1(x1, y1)。即:
x2 = (x - x0) * cosα + (y - y0) * sinα + x0, y2 = -(x - x0) * sinα + (y - y0) * cosα + y0
三、应用举例
图片中有一条线段,线段L的两个顶点分别为:p1(x1, y1),p2(x2, y3)。我们的目标是将L旋转为水平,旋转之后的线段记为L',L'的两个顶点分别为p3(x3, y3),p4(x4, y4),其中p3和p1对应,p4和p2对应,我们需要计算旋转之后的点p3和p4的坐标。
实现代码如下:
void point_rotate() {
// 得到一张空白的灰度图
Mat image = Mat::zeros(Size(500, 500), CV_8UC1);
float x0 = image.cols / 2;
float y0 = image.rows / 2;
// 初始化两个点, p1和p2
Point p1 = Point(100, 100);
Point p2 = Point(200, 120);
//分别得到两个点的坐标
float x1 = p1.x;
float y1 = p1.y;
float x2 = p2.x;
float y2 = p2.y;
//在图上画出两个点,以及两个点连成的线L
circle(image, p1, 3, Scalar(255), -1);
circle(image, p2, 3, Scalar(255), -1);
line(image, p1, p2, Scalar(255));
// 计算L的偏转角度
float angle = atan((y2 - y1) / (x2 - x1)); //注意:这里得到的是弧度
//以rotate_angle角度来旋转图片m,注意,opencv中的都是角度,所以这里需要把弧度转化为角度
// 旋转的时候也需要注意,opencv中,负角度指的是顺时针
Mat rotate_img;
float rotate_angle = angle * 180 / 3.14159265358979323846;
Mat m = getRotationMatrix2D(Point2f(x0, y0), rotate_angle, 1);
warpAffine(image, rotate_img, m, Size(image.cols, image.rows), 1, 0, 255);
//计算p1在新图片中对应的坐标p3
float x3 = (x1 - x0) * cos(angle) + (y1 - y0) * sin(angle) + x0;
float y3 = -(x1 - x0) * sin(angle) + (y1 - y0) * cos(angle) + y0;
Point p3 = Point(int(x3), int(y3));
//计算p2在新图片中对应的坐标p4
float x4 = (x2 - x0) * cos(angle) + (y2 - y0) * sin(angle) + x0;
float y4 = -(x2 - x0) * sin(angle) + (y2 - y0) * cos(angle) + y0;
Point p4 = Point(int(x4), int(y4));
//在旋转之后的图片中画出点p1, p2, p3, p4, 以及p1和p2的连线L,p3和p4的连线L'
circle(rotate_img, p1, 3, Scalar(255), -1);
circle(rotate_img, p2, 3, Scalar(255), -1);
line(rotate_img, p1, p2, Scalar(255));
circle(rotate_img, p3, 3, Scalar(255), -1);
circle(rotate_img, p4, 3, Scalar(255), -1);
line(rotate_img, p3, p4, Scalar(255));
//标识每个点的位置
putText(rotate_img, "p1", p1, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
putText(rotate_img, "p2", p2, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
putText(rotate_img, "p3", p3, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
putText(rotate_img, "p4", p4, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
//画出旋转中心,以及p1,p3分别和旋转中心的连线
Point center = Point(x0, y0);
circle(rotate_img, center, 3, Scalar(255), -1);
line(rotate_img, p1, center, Scalar(255));
line(rotate_img, p3, center, Scalar(255));
//沿着p1点画一条水平基准线
line(rotate_img, Point(0, 100), Point(rotate_img.cols, 100), Scalar(255));
// 保存图片image, rotate_img
imwrite("E:\\image.bmp", image);
imwrite("E:\\rotate_img.bmp", rotate_img);
}
结果如下图所示:
在上图的角度标识中,角度1为:计算出的线段L的角度;角度2为:图片旋转的角度;角度3为:点p1旋转的角度。从视觉效果上来看,这三个角度应该是一致的。
网友评论