学习OpenCV3:画带箭头的直线


一、问题

  现有一条直线 p 1 p 2 p_1p_2 p1p2,给定箭头角度angle,长度length,颜色color和厚度thickness,要求在 p 2 p_2 p2画出箭头。

二、分析:

  已知角度angle和长度length,可求出l1l2

    double l1 = length * cos(angle * CV_PI / 180), l2 = length * sin(angle * CV_PI / 180);

  已知p1p2l1,可求出p0

    int i = (p2.x > p1.x) ? 1 : -1; // i,j代表p2、p3、p4相对于p0的正负
    int j = (p2.y > p1.y) ? 1 : -1;
    double a1 = abs(atan((p2.y - p1.y) / (p2.x - p1.x))); // 直线p1p2相对于x轴的角度,取正值
    double w1 = l1 * cos(a1), h1 = l1 * sin(a1);          // 用于计算p2相对于p0的宽高
    Point2d p0(p2.x - w1 * i, p2.y - h1 * j);

  已知直线 p 1 p 2 p_1p_2 p1p2 p 3 p 4 p_3p_4 p3p4 p 1 p 2 p_1p_2 p1p2垂直,可计算出 p 3 p 4 p_3p_4 p3p4相对 x x x轴的角度a2

    double a2 = 90 * CV_PI / 180 - a1;           // 直线p3p4相对于x轴的角度

  已知p0l2a2,可计算出p3p4

    double w2 = l2 * cos(a2), h2 = l2 * sin(a2); // 用于计算p3和p4相对于p0的宽高
    p3 = Point2d(p0.x - w2 * i, p0.y + h2 * j);
    p4 = Point2d(p0.x + w2 * i, p0.y - h2 * j);

三、实现

#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
using namespace cv;

Point2d g_p1(400, 300), g_p2(400, 500); // 直线

// 画箭头
// img:       所画的图片
// p1,p2:     直线的起点和终点
// angle:     箭头相对直线角度
// length:    箭头的长度
// color:     箭头的颜色
// thickness: 箭头的厚度
void draw_arrow(Mat img, const Point2d p1, const Point2d p2, const double angle, const double length, const Scalar color, const int thickness)
{
    double l1 = length * cos(angle * CV_PI / 180), l2 = length * sin(angle * CV_PI / 180);
    Point2d p3(0, 0), p4(0, 0);
    int i = (p2.x > p1.x) ? 1 : -1; // i,j代表p2、p3、p4相对于p0的正负
    int j = (p2.y > p1.y) ? 1 : -1;
    double a1 = abs(atan((p2.y - p1.y) / (p2.x - p1.x))); // 直线p1p2相对于x轴的角度,取正值
    double w1 = l1 * cos(a1), h1 = l1 * sin(a1);          // 用于计算p2相对于p0的宽高
    Point2d p0(p2.x - w1 * i, p2.y - h1 * j);
    double a2 = 90 * CV_PI / 180 - a1;           // 直线p3p4相对于x轴的角度
    double w2 = l2 * cos(a2), h2 = l2 * sin(a2); // 用于计算p3和p4相对于p0的宽高
    p3 = Point2d(p0.x - w2 * i, p0.y + h2 * j);
    p4 = Point2d(p0.x + w2 * i, p0.y - h2 * j);
    line(img, p2, p3, color, 2); //画箭头
    line(img, p2, p4, color, 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 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 (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 == 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_p1, g_p2, Scalar(255, 0, 0), 2);               // 画蓝线a
        draw_arrow(img, g_p1, g_p2, 20, 20, Scalar(255, 0, 0), 2); // 画箭头
        imshow(window_name, img);
        if (waitKey(3) > 0)
            break;
    }
    return 0;
}

操作方法:
  鼠标点击直线起点或终点并按住移动,可改变直线。在键盘按任意的键可退出程序。

运行结果:

猜你喜欢

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