一、问题
已知3个点 g_center
、g_p1
和g_p2
。其中g_center
是夹角的中心点,g_center
和g_p1
组成夹角的起始边 l 1 l_1 l1,g_center
和g_p2
组成夹角的结束边 l 2 l_2 l2,现希望画出从 l 1 l_1 l1到 l 2 l_2 l2的夹角。
二、分析
OpenCV中cv::ellipse()不止可以画椭圆,还可以画圆弧。
bool ellipse(
cv::Mat& img, // 画图的图片
cv::Point center, // 椭圆的中心位置
cv::Size axes, // 椭圆长轴和短轴的长度
double angle, // 椭圆主轴的角度
double startAngle, // 圆弧起始的角度
double endAngle, // 圆弧结束的角度
const cv::Scalar& color, // 圆弧的颜色
int thickness=1, // 圆弧的厚度
int lineTyppe=8, // 连通性,可以为4、8
int shift=0 // 作为分数处理的半径位
);
数学上常用的坐标系是以右上角作为正坐标,而OpenCV的图片是以右下角作为正坐标。
三、实现
#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
using namespace cv;
Point2d g_center(400, 300), g_p1(400, 100), g_p2(600, 300);
// 画夹角
void draw_angle(Mat img, Point2d p0, Point2d p1, Point2d p2, double radius)
{
// 计算直线的角度
double angle1 = atan2(-(p1.y - p0.y), (p1.x - p0.x)) * 180 / CV_PI;
double angle2 = atan2(-(p2.y - p0.y), (p2.x - p0.x)) * 180 / CV_PI;
// 计算主轴的角度
double angle = angle1 <= 0 ? -angle1 : 360 - angle1;
// 计算圆弧的结束角度
double end_angle = (angle2 < angle1) ? (angle1 - angle2) : (360 - (angle2 - angle1));
// 画圆弧
ellipse(img, p0, Size(radius, radius), angle, 0, end_angle, Scalar(0, 0, 255), 2);
}
// 鼠标回调函数
void mouse_callback(int event, int x, int y, int flags, void *param)
{
static Point2d p(0, 0), p1(0, 0), p2(0, 0);
static int n = -1;
switch (event)
{
case cv::EVENT_LBUTTONDOWN: // 鼠标左键点击
{
p1 = Point2d(x, y);
int w = 15, h = 15;
Rect r0(g_center.x - w, g_center.y - h, 2 * w, 2 * h);
Rect r1(g_p1.x - w, g_p1.y - h, 2 * w, 2 * h);
Rect r2(g_p2.x - w, g_p2.y - h, 2 * w, 2 * h);
if (r0.contains(p1)) // 鼠标落在中心点g_center
{
n = 0;
p = g_center;
}
else if (r1.contains(p1)) // 鼠标落在g_p1
{
n = 1;
p = g_p1;
}
else if (r2.contains(p1)) // 鼠标落在g_p2
{
n = 2;
p = g_p2;
}
break;
}
case cv::EVENT_MOUSEMOVE: // 鼠标移动
p2 = Point2d(x, y);
if (n == 0)
{
g_center = p + p2 - p1;
}
else if (n == 1)
{
g_p1 = p + p2 - p1;
}
else if (n == 2)
{
g_p2 = p + p2 - p1;
}
break;
case cv::EVENT_LBUTTONUP: // 鼠标左键释放
p1 = Point2d(0, 0);
p2 = Point2d(0, 0);
n = -1;
break;
default:
break;
}
}
// 主函数
int main()
{
string window_name = "image";
namedWindow(window_name, WINDOW_AUTOSIZE);
int w = 800, h = 600;
Mat image_original = Mat(h, w, CV_8UC3, Scalar(255, 255, 255));
cv::setMouseCallback(window_name, mouse_callback); // 调用鼠标回调函数
while (true)
{
Mat img = image_original.clone(); // 拷贝空白图片,方便重复画图
line(img, g_center, g_p1, Scalar(0, 255, 0), 2); // 画绿线l1
line(img, g_center, g_p2, Scalar(255, 0, 0), 2); // 画蓝线l2
draw_angle(img, g_center, g_p1, g_p2, 15); // 画红色夹角
imshow(window_name, img);
if (waitKey(3) > 0)
break;
}
return 0;
}
操作方法:
鼠标左键点击直线的起点或终点并按住移动,由此可以修改直线。夹角是从绿线顺时针到蓝线。在键盘输入任意的键,可退出程序。
运行结果: