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

Nau*_*mer 15 python opencv numpy hough-transform

如何使用opencv Hough线算法获得线的交点?

这是我的代码:

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()
Run Code Online (Sandbox Code Playgroud)

输出:

产量

我想得到所有的交点.

alk*_*asm 38

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

  1. 根据角度将线条划分为两个类别.
  2. 计算一个类中每一行与其他类中的行的交叉点.

有了HoughLines,你已经有了结果,rho, theta所以你可以很容易地分成两类角度theta.您可以使用例如cv2.kmeans()theta作为你的数据,你要分割.

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

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


代码示例

您没有提供原始图像,因此我无法使用它.相反,我将使用OpenCV在其Hough变换和阈值教程中使用的标准数独图像:

数独图像

首先,我们将只读取此图像并使用自适应阈值进行二值化,就像在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)
Run Code Online (Sandbox Code Playgroud)

数独图像二值化

然后我们会找到Hough线cv2.HoughLines():

rho, theta, thresh = 2, np.pi/180, 400
lines = cv2.HoughLines(bin_img, rho, theta, thresh)
Run Code Online (Sandbox Code Playgroud)

与霍夫线的数独图像

现在,如果我们想找到交叉点,我们真的想找到垂直线的交点.我们不希望大多数平行线的交叉点.所以我们需要分割我们的线.在这个特定的例子中,您可以根据简单的测试轻松检查线条是水平线还是垂直线; 垂直线theta的大约为0或大约180; 水平线theta的大小约为90.但是,如果你想根据任意数量的角度自动分割它们,而不是你定义这些角度,我认为最好的想法是使用cv2.kmeans().

要做对,有一件棘手的事情.HoughLines返回线rho, theta形式(黑森范式),而theta返回的是0到180度之间,并围绕180线和0度是相似的(他们都是接近水平线),所以我们需要一些方法来得到这个周期中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 zip(range(len(lines)), lines):
        segmented[labels[i]].append(line)
    segmented = list(segmented.values())
    return segmented
Run Code Online (Sandbox Code Playgroud)

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

segmented = segment_by_angle_kmeans(lines)
Run Code Online (Sandbox Code Playgroud)

有什么好处在这里我们可以通过指定可选参数来指定任意数量的组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
Run Code Online (Sandbox Code Playgroud)

然后使用它,它只是:

intersections = segmented_intersections(segmented)
Run Code Online (Sandbox Code Playgroud)

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

交叉口


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

三角交叉口

  • @mattsmith5 具体什么对你不起作用?使用 Python 3.9.1 和 OpenCV 4.5.1,给定的代码按原样运行。您是否注意到,orangepips 指出,所提供的答案使用 OpenCV 3.2(!),并且在 OpenCV 2.4(!)中使用所提供的代码涉及一些“黑客攻击”? (2认同)

Bhu*_*pen 5

如果您已经有了线段,只需将它们替换为线方程......

x = x1 + u * (x2-x1)
y = y1 + u * (y2-y1)
Run Code Online (Sandbox Code Playgroud)

可以使用以下任何一种方法找到您...

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))
Run Code Online (Sandbox Code Playgroud)


tbr*_*ere 5

这是一个更直接的解决方案,改编了这个答案。它应该比 Bhupen\xe2\x80\x99s 答案在数值上更稳定

\n

首先,您应该对线进行聚类,这样您就不会尝试找到平行线的交点,如其他答案中所述(否则,您将得到不一致的结果和/或计算错误)

\n

然后你可以找到一对线的交点:

\n
def hough_inter(theta1, rho1, theta2, rho2):\n    A = np.array([[cos(theta1), sin(theta1)], \n                  [cos(theta2), sin(theta2)]])\n    b = np.array([rho1, rho2])\n    return np.linalg.lstsq(A, b)[0] # use lstsq to solve Ax = b, not inv() which is unstable\n
Run Code Online (Sandbox Code Playgroud)\n

我的数据结果:

\n

线 交叉点

\n

解释:

\n

霍夫 (rho/theta) 空间中的线在 xy 空间中表示如下:

\n
rho = x cos\xce\xb8 + y sin\xce\xb8\n
Run Code Online (Sandbox Code Playgroud)\n

因此交集 (x, y) 必然求解

\n
x cos \xce\xb81 + y sin \xce\xb81 = r1\nx cos \xce\xb82 + y sin \xce\xb82 = r2\n
Run Code Online (Sandbox Code Playgroud)\n

即 AX = b,其中

\n
A = [cos \xce\xb81  sin \xce\xb81]   b = |r1|   X = |x|\n    [cos \xce\xb82  sin \xce\xb82]       |r2|       |y|\n
Run Code Online (Sandbox Code Playgroud)\n

因此,如果你在Python中有两条线,你可以像这样找到它们的交集。

\n