學(xué)習(xí)OpenCV3:畫帶箭頭的直線


一、問(wèn)題

??現(xiàn)有一條直線p_1p_2,給定箭頭角度angle,長(zhǎng)度length,顏色color和厚度thickness,要求在p_2畫出箭頭。

二、分析:

??已知角度angle和長(zhǎng)度length,可求出l1l2

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

??已知p1,p2l1,可求出p0

    int i = (p2.x > p1.x) ? 1 : -1; // i,j代表p2、p3、p4相對(duì)于p0的正負(fù)
    int j = (p2.y > p1.y) ? 1 : -1;
    double a1 = abs(atan((p2.y - p1.y) / (p2.x - p1.x))); // 直線p1p2相對(duì)于x軸的角度,取正值
    double w1 = l1 * cos(a1), h1 = l1 * sin(a1);          // 用于計(jì)算p2相對(duì)于p0的寬高
    Point2d p0(p2.x - w1 * i, p2.y - h1 * j);

??已知直線p_1p_2,p_3p_4p_1p_2垂直,可計(jì)算出p_3p_4相對(duì)x軸的角度a2

    double a2 = 90 * CV_PI / 180 - a1;           // 直線p3p4相對(duì)于x軸的角度

??已知p0、l2a2,可計(jì)算出p3p4

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

三、實(shí)現(xiàn)

#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:     直線的起點(diǎn)和終點(diǎn)
// angle:     箭頭相對(duì)直線角度
// length:    箭頭的長(zhǎng)度
// 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相對(duì)于p0的正負(fù)
    int j = (p2.y > p1.y) ? 1 : -1;
    double a1 = abs(atan((p2.y - p1.y) / (p2.x - p1.x))); // 直線p1p2相對(duì)于x軸的角度,取正值
    double w1 = l1 * cos(a1), h1 = l1 * sin(a1);          // 用于計(jì)算p2相對(duì)于p0的寬高
    Point2d p0(p2.x - w1 * i, p2.y - h1 * j);
    double a2 = 90 * CV_PI / 180 - a1;           // 直線p3p4相對(duì)于x軸的角度
    double w2 = l2 * cos(a2), h2 = l2 * sin(a2); // 用于計(jì)算p3和p4相對(duì)于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);
}

// 鼠標(biāo)回調(diào)函數(shù)
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: // 鼠標(biāo)左鍵點(diǎn)擊
    {
        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)) // 鼠標(biāo)落在g_p1
        {
            n = 1;
            p = g_p1;
        }
        else if (r2.contains(p1)) // 鼠標(biāo)落在g_p2
        {
            n = 2;
            p = g_p2;
        }
        break;
    }
    case cv::EVENT_MOUSEMOVE: // 鼠標(biāo)移動(dòng)
        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: // 鼠標(biāo)左鍵釋放
        p1 = Point2d(0, 0);
        p2 = Point2d(0, 0);
        n = -1;
        break;
    default:
        break;
    }
}

// 主函數(shù)
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); // 調(diào)用鼠標(biāo)回調(diào)函數(shù)
    while (true)
    {
        Mat img = image_original.clone();                          // 拷貝空白圖片,方便重復(fù)畫圖
        line(img, g_p1, g_p2, Scalar(255, 0, 0), 2);               // 畫藍(lán)線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;
}

操作方法:
??鼠標(biāo)點(diǎn)擊直線起點(diǎn)或終點(diǎn)并按住移動(dòng),可改變直線。在鍵盤按任意的鍵可退出程序。

運(yùn)行結(jié)果:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容