学习OpenCV3:判断两条直线重合 小咪咪 2023-02-20 12:29 81阅读 0赞 -------------------- # 一、问题 # 已知两条直线 l 1 ( x 1 , y 1 , x 2 , y 2 ) l\_1(x\_1,y\_1,x\_2,y\_2) l1(x1,y1,x2,y2)和 l 2 ( x 3 , y 3 , x 4 , y 4 ) l\_2(x\_3,y\_3,x\_4,y\_4) l2(x3,y3,x4,y4),现希望判断 l 1 l\_1 l1与 l 2 l\_2 l2间是否重合。 # 二、分析 # l 1 l\_1 l1的直线方程: y − y 1 y 2 − y 1 = x − x 1 x 2 − x 1 ⇒ \{ a 1 x + b 1 y + c 1 = 0 a 1 = − ( y 2 − y 1 ) b 1 = x 2 − x 1 c 1 = ( y 2 − y 1 ) x 1 − ( x 2 − x 1 ) y 1 \\frac\{y-y\_1\}\{y\_2-y\_1\} = \\frac\{x-x\_1\}\{x\_2-x\_1\} \\Rightarrow \\begin\{cases\} a\_1x+b\_1y+c\_1=0 \\\\a\_1=-(y\_2-y\_1) \\\\b\_1 = x\_2-x\_1 \\\\c\_1 = (y\_2-y\_1)x\_1 - (x\_2-x\_1) y\_1\\end\{cases\} y2−y1y−y1=x2−x1x−x1⇒⎩⎪⎪⎪⎨⎪⎪⎪⎧a1x\+b1y\+c1=0a1=−(y2−y1)b1=x2−x1c1=(y2−y1)x1−(x2−x1)y1 若 l 2 l\_2 l2的起点 p 3 ( x 3 , y 3 ) p\_\{3\}(x\_\{3\},y\_\{3\}) p3(x3,y3)和终点 p 4 ( x 4 , y 4 ) p\_\{4\}(x\_\{4\},y\_\{4\}) p4(x4,y4)不是同一个点,且都在直线 l 1 l\_1 l1上,则 l 2 l\_2 l2与 l 1 l\_1 l1重合。 判断条件:`p3!=p4 && a1*x3+b1*y3+c1==0 && a1*x4+b1*y4+c1==0` # 三、实现 # #include <opencv2/opencv.hpp> #include <iostream> #include <string> #include <cmath> using namespace std; using namespace cv; Vec4d g_line1(100, 300, 300, 300), g_line2(500, 300, 700, 300); // 重合的两条线 // 判断两条线是否重合 bool lines_coincidence(const Vec4d l1, const Vec4d l2) { double x1 = l1[0], y1 = l1[1], x2 = l1[2], y2 = l1[3]; // 两点式:(y-y1)/(y2-y1)=(x-x1)/(x2-x1) 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]; Point2d p3(x3, y3), p4(x4, y4); if (p3 != p4 && a1 * x3 + b1 * y3 + c1 == 0 && a1 * x4 + b1 * y4 + c1 == 0) // 判断条件 return true; else return false; } // 画虚线 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(const Mat img, const Vec4d l1, const Vec4d l2) { line(img, Point2d(l1[0], l1[1]), Point2d(l1[2], l1[3]), Scalar(0, 255, 0), 2); // 画绿线 line(img, Point2d(l2[0], l2[1]), Point2d(l2[2], l2[3]), Scalar(0, 255, 0), 2); // 画绿线 if (lines_coincidence(l1, l2)) // 重合 { putText(img, "yes", Point2d(10, 25), cv::FONT_HERSHEY_COMPLEX, 0.5, Scalar(0, 255, 0)); draw_extension_line(img, l1, Scalar(0, 255, 0)); // 画延长线 } 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); char c = waitKey(3); if (c == '1') // l1,l2与x轴平行 { g_line1 = Vec4d(100, 300, 300, 300); g_line2 = Vec4d(500, 300, 700, 300); } else if (c == '2') // l1,l2与x轴垂直 { g_line1 = Vec4d(400, 50, 400, 250); g_line2 = Vec4d(400, 350, 400, 550); } else if (c == '3') // l1,l2与x轴右倾斜 { g_line1 = Vec4d(100, 500, 300, 400); g_line2 = Vec4d(500, 300, 700, 200); } else if (c == '4') // l1,l2与x轴左倾斜 { g_line1 = Vec4d(100, 200, 300, 300); g_line2 = Vec4d(500, 400, 700, 500); } else if (c > 0 && (c < '1' || c > '4')) // 退出循环 { break; } } return 0; } **操作方法:** 鼠标点击两条直线的起点或终点并按住移动,由此可以修改直线。鼠标点击两条直线的中间点并按住移动,由此可以平移直线。 键盘按住`1`、`2`、`3`或`4`可选择对应的模式,画出不同角度下两条直线重合的情况。键盘按其它的键会退出程序。 **两条直线不重合时:** ![两条直线不重合][format_png_pie_center] **模式1:** ![模式1][1] **模式2:** ![模式2][2] **模式3:** ![模式3][3] **模式4:** ![模式4][4] [format_png_pie_center]: https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8yMDMwMzQzNC1mY2M1YzdiMWY4ZmZlODM0LnBuZw?x-oss-process=image/format,png#pie_center [1]: https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8yMDMwMzQzNC1hZTc0YmFlZDVhMjUzMjE4LnBuZw?x-oss-process=image/format,png#pie_center [2]: https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8yMDMwMzQzNC0zNjJmMzZiOWY3YjZkZDMwLnBuZw?x-oss-process=image/format,png#pie_center [3]: https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8yMDMwMzQzNC1mMzc5YzdkY2VkYzAwZjY1LnBuZw?x-oss-process=image/format,png#pie_center [4]: https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8yMDMwMzQzNC1iMzk4ZDczNTQ3YzZiOTMyLnBuZw?x-oss-process=image/format,png#pie_center
还没有评论,来说两句吧...