学习OpenCV3:画出代表夹角的圆弧

    技术2022-07-10  143


    一、问题

      已知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; }

    操作方法:   鼠标左键点击直线的起点或终点并按住移动,由此可以修改直线。夹角是从绿线顺时针到蓝线。在键盘输入任意的键,可退出程序。

    运行结果:

    Processed: 0.014, SQL: 9