一、問(wèn)題
??現(xiàn)有一條直線,給定箭頭角度
angle,長(zhǎng)度length,顏色color和厚度thickness,要求在畫出箭頭。
二、分析:

??已知角度angle和長(zhǎng)度length,可求出l1和l2:
double l1 = length * cos(angle * CV_PI / 180), l2 = length * sin(angle * CV_PI / 180);
??已知p1,p2和l1,可求出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);
??已知直線,
與
垂直,可計(jì)算出
相對(duì)
軸的角度
a2:
double a2 = 90 * CV_PI / 180 - a1; // 直線p3p4相對(duì)于x軸的角度
??已知p0、l2和a2,可計(jì)算出p3和p4:
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é)果:



