找到使用 houghlines opencv 绘制的两条线的交点

新手上路,请多包涵

如何使用 opencv 霍夫线算法获取线的交点?

这是我的代码:

 import cv2
import numpy as np
import imutils

im = cv2.imread('../data/test1.jpg')
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 60, 150, apertureSize=3)

img = im.copy()
lines = cv2.HoughLines(edges,1,np.pi/180,200)

for line in lines:
    for rho,theta in line:
        a = np.cos(theta)
        b = np.sin(theta)
        x0 = a*rho
        y0 = b*rho
        x1 = int(x0 + 3000*(-b))
        y1 = int(y0 + 3000*(a))
        x2 = int(x0 - 3000*(-b))
        y2 = int(y0 - 3000*(a))
        cv2.line(img,(x1,y1),(x2,y2),(0,255,0),10)

cv2.imshow('houghlines',imutils.resize(img, height=650))
cv2.waitKey(0)
cv2.destroyAllWindows()

输出:

输出

我想得到所有的交点。

原文由 Nauman Umer 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

你不想得到平行线的交点;只有垂直线与水平线的交点。此外,由于您有垂直线,计算斜率可能会导致斜率爆炸或 inf 斜率,因此您不应使用 y = mx+b 方程式。你需要做两件事:

  1. 根据线的角度将线分成两类。
  2. 计算一个类中的每条线与其他类中的线的交点。

使用 HoughLines ,您已经得到了 rho, theta 的结果,因此您可以轻松地将角度分为两类 theta 您可以使用例如 cv2.kmeans()theta 作为您要拆分的数据。

然后,要计算交点,可以使用 给定每条线的两个点计算交点 的公式。您已经从每行计算了两个点: (x1, y1), (x2, y2) 因此您可以简单地存储并使用它们。编辑:实际上,如下面我的代码所示,有一个公式可用于计算线与 rho, theta 形式的交点 HoughLines 给出。

我之前用一些 python 代码回答 了类似的问题,你可以查看;请注意,这是使用 HoughLinesP 给你线段。


代码示例

你没有提供你的原始图像,所以我不能使用它。相反,我将使用 OpenCV 在其霍夫变换和阈值教程中使用的标准数独图像:

数独图像

首先,我们将读取此图像并使用自适应阈值将其二值化,就像 本 OpenCV 教程 中使用的那样:

 import cv2
import numpy as np

img = cv2.imread('sudoku.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.medianBlur(gray, 5)
adapt_type = cv2.ADAPTIVE_THRESH_GAUSSIAN_C
thresh_type = cv2.THRESH_BINARY_INV
bin_img = cv2.adaptiveThreshold(blur, 255, adapt_type, thresh_type, 11, 2)

数独图像二值化

然后我们将找到带有 cv2.HoughLines() 的 Hough 线:

 rho, theta, thresh = 2, np.pi/180, 400
lines = cv2.HoughLines(bin_img, rho, theta, thresh)

带有霍夫线的数独图像

现在,如果我们想找到交点,实际上我们只想找到垂直线的交点。我们不想要大部分平行线的交点。所以我们需要分割我们的线。在这个特定的例子中,你可以很容易地根据一个简单的测试来检查这条线是水平的还是垂直的;垂直线的 theta 大约为 0 或大约 180;水平线将有一个 theta 大约 90。但是,如果你想根据任意数量的角度自动分割它们,而不定义这些角度,我认为最好的主意是使用 cv2.kmeans()

要做好一件棘手的事情。 HoughLines 返回线 rho, theta 形式( Hesse范式),和 theta 之间0和线18018 (它们都接近水平线),所以我们需要一些方法来获得 kmeans 中的这种周期性。

如果我们在单位圆上绘制角度,但将角度乘以 2 ,则原来约 180 度的角度将变为接近 360 度,因此单位圆上的值将接近 x, y 角度为 0。因此,我们可以通过绘制 2*angle 与单位圆上的坐标来获得一些不错的“接近度”。然后我们可以在这些点上运行 cv2.kmeans() ,并自动分割我们想要的任何部分。

因此,让我们构建一个函数来进行分割:

 from collections import defaultdict
def segment_by_angle_kmeans(lines, k=2, **kwargs):
    """Groups lines based on angle with k-means.

    Uses k-means on the coordinates of the angle on the unit circle
    to segment `k` angles inside `lines`.
    """

    # Define criteria = (type, max_iter, epsilon)
    default_criteria_type = cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER
    criteria = kwargs.get('criteria', (default_criteria_type, 10, 1.0))
    flags = kwargs.get('flags', cv2.KMEANS_RANDOM_CENTERS)
    attempts = kwargs.get('attempts', 10)

    # returns angles in [0, pi] in radians
    angles = np.array([line[0][1] for line in lines])
    # multiply the angles by two and find coordinates of that angle
    pts = np.array([[np.cos(2*angle), np.sin(2*angle)]
                    for angle in angles], dtype=np.float32)

    # run kmeans on the coords
    labels, centers = cv2.kmeans(pts, k, None, criteria, attempts, flags)[1:]
    labels = labels.reshape(-1)  # transpose to row vec

    # segment lines based on their kmeans label
    segmented = defaultdict(list)
    for i, line in enumerate(lines):
        segmented[labels[i]].append(line)
    segmented = list(segmented.values())
    return segmented

现在要使用它,我们可以简单地调用:

 segmented = segment_by_angle_kmeans(lines)

这里的好处是我们可以通过指定可选参数 k 来指定任意数量的组(默认情况下, k = 2 所以我没有在这里指定它)。

如果我们用不同的颜色绘制每组的线条:

分段线

现在剩下的就是找到第一组中每条线与第二组中每条线的交点。由于这些线是 Hesse 范式,因此有一个很好的线性代数公式用于计算这种形式的线的交点。看 这里。让我们在这里创建两个函数;一个只找到两条线的交点,一个函数循环遍历组中的所有线并将该更简单的函数用于两条线:

 def intersection(line1, line2):
    """Finds the intersection of two lines given in Hesse normal form.

    Returns closest integer pixel locations.
    See https://stackoverflow.com/a/383527/5087436
    """
    rho1, theta1 = line1[0]
    rho2, theta2 = line2[0]
    A = np.array([
        [np.cos(theta1), np.sin(theta1)],
        [np.cos(theta2), np.sin(theta2)]
    ])
    b = np.array([[rho1], [rho2]])
    x0, y0 = np.linalg.solve(A, b)
    x0, y0 = int(np.round(x0)), int(np.round(y0))
    return [[x0, y0]]

def segmented_intersections(lines):
    """Finds the intersections between groups of lines."""

    intersections = []
    for i, group in enumerate(lines[:-1]):
        for next_group in lines[i+1:]:
            for line1 in group:
                for line2 in next_group:
                    intersections.append(intersection(line1, line2))

    return intersections

然后使用它,它很简单:

 intersections = segmented_intersections(segmented)

并绘制所有交叉点,我们得到:

十字路口


如上所述,此代码也可以将线分割成两组以上的角度。这是它在手绘三角形上运行,并计算检测到的线与 k=3 的交点:

三角交点

原文由 alkasm 发布,翻译遵循 CC BY-SA 4.0 许可协议

如果您已经有了线段,只需将它们代入线方程…

 x = x1 + u * (x2-x1)
y = y1 + u * (y2-y1)

可以使用以下任何一种方式找到您…

 u = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1))
u = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1))

原文由 Bhupen 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏