判断两条直线是否相交c语言,学习OpenCV3:判断两条直线相交,并计算交点和夹角...

一、问题

已知两条直线

,现希望判断

间是否相交。若相交,计算出两条直线的交点和夹角。

二、分析

1、直线方程

的直线方程:

的直线方程:

提示:

不能同时为0。若

同时为0,起点和终点重合,该直线实际上是一个点。

2、判断相交

垂直于

轴,

倾斜于

轴时,

相交:

b1==0 && b2!=0

倾斜于

轴,

垂直于

轴时,

相交:

b1!=0 && b2==0

都倾斜于

轴,且斜率不同时,

相交:

b1!=0 && b2!=0 && a1/b1!=a2/b2

3、计算交点

4、计算夹角

交点

的终点

的终点

组成一个三角形,则其各边边长为:

由余弦定理得:

三、实现

#include

#include

#include

#include

using namespace std;

using namespace cv;

Vec4d g_line1(200, 300, 600, 300), g_line2(400, 500, 400, 100); // 垂直的两条线

// 判断两条线是否相交,若相交则求出交点和夹角

Vec4d lines_intersection(const Vec4d l1, const Vec4d l2)

{

double x1 = l1[0], y1 = l1[1], x2 = l1[2], y2 = l1[3];

double a1 = -(y2 - y1), b1 = x2 - x1, c1 = (y2 - y1) * x1 - (x2 - x1) * y1; // 一般式:a1x+b1y1+c1=0

double x3 = l2[0], y3 = l2[1], x4 = l2[2], y4 = l2[3];

double a2 = -(y4 - y3), b2 = x4 - x3, c2 = (y4 - y3) * x3 - (x4 - x3) * y3; // 一般式:a2x+b2y1+c2=0

bool r = false; // 判断结果

double x0 = 0, y0 = 0; // 交点

double angle = 0; // 夹角

// 判断相交

if (b1 == 0 && b2 != 0) // l1垂直于x轴,l2倾斜于x轴

r = true;

else if (b1 != 0 && b2 == 0) // l1倾斜于x轴,l2垂直于x轴

r = true;

else if (b1 != 0 && b2 != 0 && a1 / b1 != a2 / b2)

r = true;

if (r)

{

//计算交点

x0 = (b1 * c2 - b2 * c1) / (a1 * b2 - a2 * b1);

y0 = (a1 * c2 - a2 * c1) / (a2 * b1 - a1 * b2);

// 计算夹角

double a = sqrt(pow(x4 - x2, 2) + pow(y4 - y2, 2));

double b = sqrt(pow(x4 - x0, 2) + pow(y4 - y0, 2));

double c = sqrt(pow(x2 - x0, 2) + pow(y2 - y0, 2));

angle = acos((b * b + c * c - a * a) / (2 * b * c)) * 180 / CV_PI;

}

return Vec4d(r, x0, y0, angle);

}

// 画夹角

void draw_angle(Mat img, const Point2d p0, const Point2d p1, const Point2d p2, const double radius, const Scalar color, const int thickness)

{

// 计算直线的角度

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));

if (end_angle > 180)

{

angle = angle2 <= 0 ? -angle2 : 360 - angle2;

end_angle = 360 - end_angle;

}

// 画圆弧

ellipse(img, p0, Size(radius, radius), angle, 0, end_angle, 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 draw_dotted_line(Mat img, const Point2d p1, const Point2d p2, const Scalar color, const int thickness)

{

double n = 15; // 小线段的长度

double w = p2.x - p1.x, h = p2.y - p1.y;

double l = sqrtl(w * w + h * h);

// 矫正小线段长度,使小线段个数为奇数

int m = l / n;

m = m % 2 ? m : m + 1;

n = l / m;

circle(img, p1, 1, color, thickness); // 画起点

circle(img, p2, 1, color, thickness); // 画终点

// 画中间的小线段

if (p1.y == p2.y) //水平线:y = m

{

double x1 = min(p1.x, p2.x);

double x2 = max(p1.x, p2.x);

for (double x = x1, n1 = 2 * n; x < x2; x = x + n1)

line(img, Point2d(x, p1.y), Point2d(x + n, p1.y), color, thickness);

}

else if (p1.x == p2.x) //垂直线, x = m

{

double y1 = min(p1.y, p2.y);

double y2 = max(p1.y, p2.y);

for (double y = y1, n1 = 2 * n; y < y2; y = y + n1)

line(img, Point2d(p1.x, y), Point2d(p1.x, y + n), color, thickness);

}

else

{

// 直线方程的两点式:(y-y1)/(y2-y1)=(x-x1)/(x2-x1) -> y = (y2-y1)*(x-x1)/(x2-x1)+y1

double n1 = n * abs(w) / l;

double k = h / w;

double x1 = min(p1.x, p2.x);

double x2 = max(p1.x, p2.x);

for (double x = x1, n2 = 2 * n1; x < x2; x = x + n2)

{

Point p3 = Point2d(x, k * (x - p1.x) + p1.y);

Point p4 = Point2d(x + n1, k * (x + n1 - p1.x) + p1.y);

line(img, p3, p4, color, thickness);

}

}

}

// 画延长线

void draw_extension_line(Mat img, const Vec4d l, Scalar color)

{

double x1 = l[0], y1 = l[1], x2 = l[2], y2 = l[3];

double a = -(y2 - y1), b = x2 - x1, c = (y2 - y1) * x1 - (x2 - x1) * y1;

Point2d p1(0, 0), p2(0, 0);

if (b != 0)

{

p1 = Point2d(0, -c / b);

p2 = Point2d(img.cols, ((-a * img.cols - c) / b));

}

else

{

p1 = Point2d(-c / a, 0);

p2 = Point2d(-c / a, img.rows);

}

draw_dotted_line(img, p1, p2, color, 1);

}

// 画图

void draw(Mat img, const Vec4d l1, const Vec4d l2)

{

Vec4d v = lines_intersection(l1, l2); // 判断是否相交,并计算交点和夹角

line(img, Point2d(l1[0], l1[1]), Point2d(l1[2], l1[3]), Scalar(255, 0, 0), 2); // 画蓝线

draw_arrow(img, Point2d(l1[0], l1[1]), Point2d(l1[2], l1[3]), 20, 20, Scalar(255, 0, 0), 2); // 画箭头

line(img, Point2d(l2[0], l2[1]), Point2d(l2[2], l2[3]), Scalar(0, 255, 0), 2); // 画绿线

draw_arrow(img, Point2d(l2[0], l2[1]), Point2d(l2[2], l2[3]), 20, 20, Scalar(0, 255, 0), 2); // 画箭头

draw_extension_line(img, l1, Scalar(255, 0, 0)); // 画延长线

draw_extension_line(img, l2, Scalar(0, 255, 0)); // 画延长线

draw_angle(img, Point2d(v[1], v[2]), Point2d(l1[2], l1[3]), Point2d(l2[2], l2[3]), 15, Scalar(0, 0, 255), 1); // 画夹角

if (v[0])

{

string s = "(" + to_string(v[1]) + ", " + to_string(v[2]) + ") " + to_string(v[3]);

putText(img, s, Point2d(10, 25), cv::FONT_HERSHEY_COMPLEX, 0.5, Scalar(0, 255, 0));

circle(img, Point2d(v[1], v[2]), 2, Scalar(0, 0, 255), 2); // 画交点

}

else

{

putText(img, "no", Point2d(10, 25), cv::FONT_HERSHEY_COMPLEX, 0.5, Scalar(0, 0, 255));

}

}

// 确定鼠标左键点击在两条直线的那个点上

Vec6d define_area(const Vec4d l1, const Vec4d l2, const Point2d p)

{

Vec6d v(-1, 0, 0, 0, 0, 0);

double w = 20, h = 20;

double x1 = l1[0], y1 = l1[1], x2 = l1[2], y2 = l1[3];

double x3 = l2[0], y3 = l2[1], x4 = l2[2], y4 = l2[3];

Rect r0(x1 - w, y1 - h, 2 * w, 2 * h); // l1的起点

Point2d p1((x2 + x1) / 2, (y1 + y2) / 2); // l1的中间点

Rect r1(p1.x - w, p1.y - h, 2 * w, 2 * h);

Rect r2(x2 - w, y2 - h, 2 * w, 2 * h); // l1的终点

Rect r3(x3 - w, y3 - h, 2 * w, 2 * h); // l2的起点

Point2d p2((x3 + x4) / 2, (y3 + y4) / 2); // l2的中间点

Rect r4(p2.x - w, p2.y - h, 2 * w, 2 * h);

Rect r5(x4 - w, y4 - h, 2 * w, 2 * h); // l2的终点

if (r0.contains(p)) // 判断点是否在矩形中

{

v = Vec6d(0, x1, y1, 0, 0, 0);

}

else if (r1.contains(p))

{

v = Vec6d(1, x1, y1, x2, y2, 0);

}

else if (r2.contains(p))

{

v = Vec6d(2, x2, y2, 0, 0, 0);

}

else if (r3.contains(p))

{

v = Vec6d(3, x3, y3, 0, 0, 0);

}

else if (r4.contains(p))

{

v = Vec6d(4, x3, y3, x4, y4, 0);

}

else if (r5.contains(p))

{

v = Vec6d(5, x4, y4, 0, 0, 0);

}

return v;

}

// 根据鼠标移动相应的修改直线的起点和终点

void modify_line(Vec4d &l1, Vec4d &l2, const Vec6d area, const double w, const double h)

{

if (area[0] == 0)

{

l1[0] = area[1] + w;

l1[1] = area[2] + h;

}

else if (area[0] == 1)

{

l1[0] = area[1] + w;

l1[1] = area[2] + h;

l1[2] = area[3] + w;

l1[3] = area[4] + h;

}

else if (area[0] == 2)

{

l1[2] = area[1] + w;

l1[3] = area[2] + h;

}

else if (area[0] == 3)

{

l2[0] = area[1] + w;

l2[1] = area[2] + h;

}

else if (area[0] == 4)

{

l2[0] = area[1] + w;

l2[1] = area[2] + h;

l2[2] = area[3] + w;

l2[3] = area[4] + h;

}

else if (area[0] == 5)

{

l2[2] = area[1] + w;

l2[3] = area[2] + h;

}

}

// 鼠标回调函数

void mouse_callback(int event, int x, int y, int flags, void *param)

{

static Point2d p1(0, 0), p2(0, 0);

static Vec6d area(-1, 0, 0, 0, 0, 0);

switch (event)

{

case cv::EVENT_LBUTTONDOWN: // 鼠标左键点击

p1 = Point2d(x, y);

area = define_area(g_line1, g_line2, p1); // 确定鼠标所要移动的区域

break;

case cv::EVENT_MOUSEMOVE: // 鼠标移动

if (area[0] > -1) // 移动直线

{

p2 = Point2d(x, y);

double w = p2.x - p1.x, h = p2.y - p1.y;

modify_line(g_line1, g_line2, area, w, h); // 根据鼠标移动相应的修改直线的起点和终点

}

break;

case cv::EVENT_LBUTTONUP: // 鼠标左键释放

p1 = Point2d(0, 0);

p2 = Point2d(0, 0);

area = Vec6d(-1, 0, 0, 0, 0, 0);

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(); // 拷贝空白图片,方便重复画图

draw(img, g_line1, g_line2); // 画图

imshow(window_name, img);

if (waitKey(3) > 0) // 退出循环

break;

}

return 0;

}

操作方法:

鼠标点击直线起点或终点并按住移动,可改变直线。鼠标点击直线的中间点并按住移动,可平移直线。在键盘按任意的键可退出程序。

运行结果:

你可能感兴趣的:(判断两条直线是否相交c语言)