学习OpenCV3:判断两条直线平行,并计算平行距离

article/2025/8/31 16:11:37

一、问题

  已知两条直线 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间是否平行。若平行,计算出两条直线间的平行距离。

二、分析

y − y 1 y 2 − y 1 = x − x 1 x 2 − x 1 ⇒ { a x + b y + c = 0 a = − ( y 2 − y 1 ) b = x 2 − x 1 c = ( y 2 − y 1 ) x 1 − ( x 2 − x 1 ) y 1 k = − a b \frac{y-y_1}{y_2-y_1} = \frac{x-x_1}{x_2-x_1} \Rightarrow \begin{cases} ax+by+c=0 \\a=-(y_2-y_1) \\b = x_2-x_1 \\c = (y_2-y_1)x_1 - (x_2-x_1) y_1 \\ k = \frac{-a}{b}\end{cases} y2y1yy1=x2x1xx1ax+by+c=0a=(y2y1)b=x2x1c=(y2y1)x1(x2x1)y1k=ba

   l 1 l_1 l1的直线方程:
a 1 x + b 1 y + c 1 = 0 a_1x+b_1y+c_1=0 a1x+b1y+c1=0

   l 2 l_2 l2的直线方程:
a 2 x + b 2 y + c 2 = 0 a_2x+b_2y+c_2=0 a2x+b2y+c2=0

1、排除 l 1 l_1 l1 l 2 l_2 l2重合

   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上:

    !(p3!=p4 && a1*x3+b1*y3+c1==0 && a1*x4+b1*y4+c1==0)

2、 l 1 l_1 l1 l 2 l_2 l2都垂直 x x x

  当 l 1 l_1 l1 l 2 l_2 l2都垂直 x x x轴时, l 1 l_1 l1 l 2 l_2 l2平行:

    b2==0 && b1==0

   l 1 l_1 l1 l 2 l_2 l2的直线方程:
l 1 : a 1 x + c 1 = 0 l 2 : a 2 x + c 2 = 0 l_1:a_1x+c_1=0 \quad l_2:a_2x+c_2=0 l1:a1x+c1=0l2:a2x+c2=0

   l 1 l_1 l1 l 2 l_2 l2间的距离为:
d = ∣ x 2 − x 1 ∣ = ∣ c 1 a 1 − c 2 a 2 ∣ d= |x_2-x_1|= |\frac{c_1}{a_1} - \frac{c_2}{a_2} | d=x2x1=a1c1a2c2

3、 l 1 l_1 l1 l 2 l_2 l2都倾斜于 x x x轴,且斜率相同

  当 l 1 l_1 l1 l 2 l_2 l2斜率相同时, l 1 l_1 l1 l 2 l_2 l2平行:

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

   l 1 l_1 l1 l 2 l_2 l2的直线方程:
l 1 : a 1 x + b 1 y + c 1 = 0 l 2 : a 2 x + b 2 y + c 2 = 0 l_1:a_1x+b_1y+c_1=0 \quad l_2:a_2x+b_2y+c_2=0 l1:a1x+b1y+c1=0l2:a2x+b2y+c2=0

   l 2 l_2 l2 y y y轴的交点 p 0 p_0 p0
( x 0 , y 0 ) = ( 0 , − c 2 b 2 ) (x_0,y_0)=(0,\frac{-c_2}{b_2}) (x0,y0)=(0,b2c2)

   p 0 p_0 p0 l 1 l_1 l1距离:
d = ∣ a 1 x 0 + b 1 y 0 + c 1 ∣ a 1 2 + b 1 2 = ∣ c 1 − b 1 c 2 b 2 ∣ a 1 2 + b 1 2 d= \frac{|a_1x_0+b_1y_0+c_1|}{\sqrt{a_1^2+b_1^2}} = \frac{|c_1-\frac{b_1c_2}{b_2}|}{\sqrt{a_1^2+b_1^2}} d=a12+b12 a1x0+b1y0+c1=a12+b12 c1b2b1c2

三、实现

#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
using namespace cv;Vec4d g_line1(200, 200, 600, 200), g_line2(200, 400, 600, 400); // 平行的两条线// 判断两条线是否平行,若平行,则求出平行距离
double lines_parellel(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=0double x3 = l2[0], y3 = l2[1], x4 = l2[2], y4 = l2[3];                      // 两点式:(y-y3)/(y4-y3)=(x-x3)/(x4-x3)double a2 = -(y4 - y3), b2 = x4 - x3, c2 = (y4 - y3) * x3 - (x4 - x3) * y3; // 一般式:a2x+b2y1+c2=0Point2d p3(x3, y3), p4(x4, y4);double d = 0;if (!(p3 != p4 && a1 * x3 + b1 * y3 + c1 == 0 && a1 * x4 + b1 * y4 + c1 == 0)) // 排除重合的情况{if (b2 == 0 && b1 == 0) // l2,l1垂直x轴{d = abs(c1 / a1 - c2 / a2);}else if (b2 != 0 && b1 != 0 && a2 / b2 == a1 / b1) // l2,l1斜率相同{d = abs(c1 - b1 * c2 / b2) / sqrt(a1 * a1 + b1 * b1);}}return d;
}// 计算垂足的坐标
Point calculate_foot_point(Point2d p, Vec4d l)
{double x0 = p.x, y0 = p.y;double x1 = l[0], y1 = l[1], x2 = l[2], y2 = l[3];Point2d p1(0, 0); // 垂足if (x1 == x2)     // 线与x轴垂直{p1.x = x1;p1.y = y0;}else if (y1 == y2) // 线与x轴水平{p1.x = x0;p1.y = y1;}else // 线与x轴倾斜{int a = -(y2 - y1);int b = x2 - x1;int c = (y2 - y1) * x1 - (x2 - x1) * y1;p1.x = (b * b * x0 - a * b * y0 - a * c) / (a * a + b * b);p1.y = (a * a * y0 - a * b * x0 - b * c) / (a * a + b * b);}return p1;
}// 画虚线
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) // 与x轴水平: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轴垂直, 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 // 与x轴倾斜,直线方程的两点式:(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) // 与x轴倾斜{p1 = Point2d(0, -c / b);p2 = Point2d(img.cols, ((-a * img.cols - c) / b));}else // 与x轴垂直{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); // 画绿线double d = lines_parellel(l1, l2);if (d > 0) // 重合{putText(img, "yes d = " + to_string(d), Point2d(10, 25), cv::FONT_HERSHEY_COMPLEX, 0.5, Scalar(0, 255, 0));draw_extension_line(img, l1, Scalar(0, 255, 0));      // 画延长线draw_extension_line(img, l2, Scalar(0, 255, 0));      // 画延长线Point2d p1((l1[2] + l1[0]) / 2, (l1[3] + l1[1]) / 2); // 获取线段的中间点Point p2 = calculate_foot_point(p1, l2);              // 获取垂足draw_dotted_line(img, p1, p2, Scalar(0, 255, 0), 1);  // 画垂线}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(200, 200, 600, 200);g_line2 = Vec4d(200, 400, 600, 400);}else if (c == '2') // l1,l2与x轴垂直{g_line1 = Vec4d(300, 200, 300, 400);g_line2 = Vec4d(500, 200, 500, 400);}else if (c == '3') // l1,l2与x轴右倾斜{g_line1 = Vec4d(200, 450, 600, 150);g_line2 = Vec4d(200, 500, 600, 200);}else if (c == '4') // l1,l2与x轴左倾斜{g_line1 = Vec4d(100, 100, 500, 500);g_line2 = Vec4d(100, 150, 500, 550);}else if (c > 0 && (c < '1' || c > '4')) // 退出循环{break;}}return 0;
}

操作方法:
  鼠标点击两条直线的起点或终点并按住移动,由此可以修改直线。鼠标点击两条直线的中间点并按住移动,由此可以平移直线。
  键盘按住1234可选择对应的模式,画出不同角度下两条直线平行的情况。键盘按其它的键会退出程序。

两条直线不平行时:

两条直线都与 x x x轴水平(模式1)时:

两条直线都与 x x x轴垂直(模式2)时:

两条直线都与 x x x轴右倾斜(模式3)时:

两条直线都与 x x x轴左倾斜(模式4)时:


http://chatgpt.dhexx.cn/article/dcYAg90R.shtml

相关文章

数学空间向量--两条异面直线距离,以及相交并垂直与两条直线的直线。

1.两条异面直线的距离 &#xff08;1&#xff09;第一步先在两条直线上各取一点&#xff0c;&#xff08;如果为点向式方程或者参数方程&#xff0c;我们可以直接用t0的情况取点&#xff09;&#xff0c;然后求两点之间向量。 &#xff08;2&#xff09;第二步求出两条线的方…

空间两直线最近的两个点、距离

已知空间中有直线lineA和直线lineB&#xff0c;求两直线间最近的两点和距离。 using System.Collections; using System.Collections.Generic; using UnityEngine;public class TwoLineMinDis : MonoBehaviour {[SerializeField]private Vector3[] _lineA new Vector3[2];[Ser…

计算空间中两线段之间的距离

最近在建立气凝胶的有限元模型中需要计算每两根纤维&#xff08;线段&#xff09;之间的距离&#xff0c;最初参考的两篇文章确实提供了关于一些数值方法的计算思路&#xff08;文章1 && 文章2&#xff09;&#xff0c;但忽略了线段距离问题的理论推导&#xff0c;导致…

空间两个直线之间的距离和公垂线

已知 直线1&#xff1a;直线任意一点 P 1 P_1 P1​直线方向 V 1 V_1 V1​ (单位向量) 直线2&#xff1a;直线任意一点 P 2 P_2 P2​直线方向 V 2 V_2 V2​ (单位向量) 求解过程 需要用到点到直线的距离和垂足相关内容&#xff0c;参考 这里 下文中 ⋅ \centerdot ⋅代表点…

两点间距离、点到直线距离、点到线段距离、线段到线段距离

两点之间的距离 直接运用两点间距离公式 (x2-x1)^ 2(y2-y1)^ 2开根号 //两点间距离 double getDistancePP(Point a,Point b) { //这个代码是部分代码,有些逻辑没有展现完全,大家往下看!Point c(b.x-a.x,b.y-a.y);//返回一个新的点return c.abs();//取模 }点到直线距离 通常给出…

两平行平面间的距离

两平行平面方程为AxByCzD10&#xff0c;AxByCzD20 转载于:https://www.cnblogs.com/qiu-hua/p/8006040.html

两条平行线相交于一点

2019独角兽企业重金招聘Python工程师标准>>> 欧几里德几何说&#xff0c;两条平行的直线永远无法相交&#xff0c;爱因斯坦站在宇宙空间的角度猜测两条平行线有可能能相交&#xff0c;但到底如何相交&#xff0c;爱因斯坦也没有给出证明&#xff0c;科学家们至今也无…

【opencv】两条平行线之间的距离

问题&#xff1a;一张输入图片&#xff0c;图片上有两条平行线&#xff0c;求出这两条平行线之间的距离 解决思路&#xff1a; 1. 对图像中的直线进行细化 2. 提取直线的轮廓坐标 3. 对轮廓上的坐标进行直线集合&#xff0c;从而得到直线方程 4. 计算两条直线之间的距离 …

OpenCV计算两条平行线之间的距离

代码来自www.opencvchina.com #include "cv.h" #include "highgui.h" #include "cxcore.h" #include <stdlib.h> #include <stdio.h>#ifndef LINESDISHEADER#define LINESDISHEADER//对输入图像进行细化 void ThinImage(IplImage* s…

用vue实现tab切换

用vue实现tab切换 html代码 <div id"app"><ul class"tab-tilte"><li click"cur0" :class"{active:cur0}">html</li><li click"cur1" :class"{active:cur1}">css</li><li…

tab切换(用jQuery实现)?

页面中经常用到的tab栏&#xff0c;来分类展示内容 我认为掌握tab栏切换算是从静态页面到动态页面所迈出的第一步&#xff0c;并且在以后的工作中(jQuery框架开发)会作为jQuery中的常用事件和方法&#xff0c;反复的使用&#xff0c;所以掌握tab栏切换至关重要&#xff01;&am…

JQUERY实现TAB切换

博主是一枚前端小菜鸟&#xff0c;因为挺长时间没接触页面布局了&#xff0c;居然连tab栏切换都给忘了&#xff0c;后来博主看了一些前端资料还有书&#xff0c;发现网上的很多方法都杂乱无章&#xff0c;看的云里雾里的&#xff0c;冗余代码太多&#xff0c;这让新手小白会很苦…

React实现tab切换

下面来编写一个tab选项卡切换效果&#xff0c;效果如下图所示&#xff1a; 下面我放上该组件的代码&#xff1a; import React, { Component } from react; import { Link } from react-router; import ../scss/base.scss; import ../scss/tab.scss;class TabController exten…

vue实现Tab切换功能

在项目开发中&#xff0c;我们经常会碰到Tab切换的功能&#xff0c;而在Vue中想实现这样的功能也应该有很多种&#xff0c;常用的三种应该是 Tab路由切换、Tab动态组件切换、通过v-show设置Tab显示隐藏。每种方法实现起来其实都不难&#xff0c;看看官网介绍或看几篇博客应该就…

tab切换效果

1.效果图 2.分析步骤 1.首先写vue先引入&#xff1a;<script src"https://cdn.jsdelivr.net/npm/vue2.6.14/dist/vue.js"></script> 2.接着写静态布局 3.挂载dom 4.添加指令 5.肯定要储存数据 6. 最后效果实现 3.代码块部分 按步骤操作 1.首先&#xf…

vue中如何实现tab切换功能?

一、v-show控制内容切换 1&#xff09;简单版原理&#xff1a;用点击事件改变num值作为开关&#xff0c;控制tab样式和内容显示隐藏。 2&#xff09;数据渲染原理&#xff1a;主要利用v-for绑定的index来控制&#xff0c;跟上面差不多。 二、组件切换。 知识点主要是vue中is的…

点击tabs切换不同的内容

1.通过v-for遍历posts,然后渲染数据 2.定义currentTabs变量 3.运用computed计算属性 4.点击按钮时&#xff0c;切换下边的内容 5.点击切换tabs时&#xff0c;高亮当前tabs 4.将切换tabs的文件封装成组件&#xff0c;可以使用keep-alive进行缓存数据 5.使用keep-alive触发的生命…

Tab选项卡切换

Tab选项卡切换 基本代码 HTML代码&#xff1a; <div id"notice" class"notice"><!-- 标题--><div id"notice-tit" class"notice-tit"><ul><li><a href"#">公告</a></li>…

【JS实现tab切换】

JavaScript实现tab切换。 点击科技显示图一, 点击探索显示图二。 body部分&#xff1a; <div class"box"><ul><li class"active">科技</li><li>探索</li></ul><ol><li class"active">科…

Tab页面切换

页面效果如图 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta http-equiv"X-UA-Compatible" content"IEedge"> <meta name"viewport" content"widthdevice-…