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


一、问题

  已知3个点 g_centerg_p1g_p2。其中g_center是夹角的中心点,g_centerg_p1组成夹角的起始边 l 1 l_1 l1g_centerg_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;
}

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

运行结果:

猜你喜欢

转载自blog.csdn.net/qq_34801642/article/details/107058918