查找使用houghlines opencv绘制的两条线的交点 [英] find intersection point of two lines drawn using houghlines opencv

查看:91
本文介绍了查找使用houghlines opencv绘制的两条线的交点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使用opencv霍夫线算法向下求线的交点?

How can I get the intersection points of lines down using opencv Hough lines algorithm?

这是我的代码:

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

输出:

我想得到所有的交点.

推荐答案

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

You don't want to get the intersections of the parallel lines; only the intersections of the vertical lines with those of the horizontal lines. Also, since you have vertical lines, calculating the slope will likely result in exploding or inf slopes, so you shouldn't use the y = mx+b equations. You need to do two things:

  1. 根据角度将线段分为两类.
  2. 计算一类中每条线与另一类中的线的交点.

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

With HoughLines, you already have the result as rho, theta so you can easily segment into two classes of angle with theta. You can use for e.g. cv2.kmeans() with theta as your data you want to split.

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

Then, to calculate the intersections, you can use the formula for calculating intersections given two points from each line. You are already calculating two points from each line: (x1, y1), (x2, y2) so you can simply just store those and use them. Actually, as seen below in my code, there's a formula you can use for calculating the intersections of lines with the rho, theta form that HoughLines gives.

我已经回答类似的问题带有一些您可以签出的python代码;请注意,这是使用HoughLinesP进行的,它只为您提供线段.

I have answered a similar question before with some python code that you can check out; note this was using HoughLinesP which gives you only line segments.

您没有提供原始图片,所以我不能使用它.相反,我将在他们的Hough变换和阈值化教程中使用OpenCV使用的标准数独图像:

You didn't provide your original image so I can't use that. Instead I'll use the standard sudoku image used by OpenCV on their Hough transform and thresholding tutorials:

首先,我们将像使用此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()中找到霍夫线:

Then we'll find the Hough lines with cv2.HoughLines():

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

现在,如果我们要查找相交,实际上我们只想查找垂直线的相交.我们不希望大多数平行线的交点.因此,我们需要细分我们的线路.在这个特定的示例中,您可以轻松地通过简单的测试来检查线条是水平的还是垂直的.垂直线的theta大约为0或大约180;水平线的theta大约为90.但是,如果要基于任意数量的角度将它们分段,而无需定义这些角度,则我认为最好的方法是使用cv2.kmeans().

Now, if we want to find the intersections, really we want to find the intersections only of the perpendicular lines. We don't want the intersections of mostly parallel lines. So we need to segment our lines. In this particular example you could easily just check whether the line is horizontal or vertical based on a simple test; the vertical lines will have a theta of around 0 or around 180; the horizontal lines will have a theta of around 90. However, if you want to segment them based on an arbitrary number of angles, automatically, without you defining those angles, I think the best idea is to use cv2.kmeans().

有一件棘手的事情要纠正. HoughLinesrho, theta格式返回行(黑森常规格式),以及theta返回值介于0到180度之间,并且180到0度附近的线是相似的(它们都接近水平线),因此我们需要某种方法来获得kmeans中的这种周期性.

There is one tricky thing to get right. HoughLines returns lines in rho, theta form (Hesse normal form), and the theta returned is between 0 and 180 degrees, and lines around 180 and 0 degrees are similar (they are both close to horizontal lines), so we need some way to get this periodicity in kmeans.

如果我们在单位圆上绘制角度,但将角度乘以 2 ,则最初大约180度的角度将接近360度,因此在坐标轴上将具有x, y值.单位圆接近相同的角度(角度为0).因此,我们可以通过在单位圆上绘制2*angle坐标来获得一些不错的紧密度".然后,我们可以在这些点上运行cv2.kmeans(),并根据需要的任意段自动进行分段.

If we plot the angle on the unit circle, but multiply the angle by two, then the angles originally around 180 degrees will become close to 360 degrees and thus will have x, y values on the unit circle near the same for angles at 0. So we can get some nice "closeness" here by plotting 2*angle with the coordinates on the unit circle. Then we can run cv2.kmeans() on those points, and segment automatically with however many pieces we want.

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

So let's build a function to do the segmentation:

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

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

Now to use it, we can simply call:

segmented = segment_by_angle_kmeans(lines)

这里很好,我们可以通过指定可选参数k(默认情况下为k = 2,因此在此未指定)来指定任意数量的组.

What's nice is here we can specify an arbitrary number of groups by specifying the optional argument k (by default, k = 2 so I didn't specify it here).

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

If we plot the lines from each group with a different color:

现在剩下的就是找到第一组中每条线的交点与第二组中每条线的交点.由于直线采用黑森标准形式,因此有一个很好的线性代数公式,可以根据这种形式计算直线的交点.请参见此处.让我们在这里创建两个函数;一个只找到两条线的交点,一个函数在组中的所有线之间循环,并对两条线使用更简单的函数:

And now all that's left is to find the intersections of each line in the first group with the intersection of each line in the second group. Since the lines are in Hesse normal form, there's a nice linear algebra formula for calculating the intersection of lines from this form. See here. Let's create two functions here; one that finds the intersection of just two lines, and one function that loops through all the lines in the groups and uses that simpler function for two lines:

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)

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

And plotting all the intersections, we get:

如上所述,此代码也可以将线分成多于两组角度.它在一个手绘三角形上运行,并使用k=3计算检测到的线的交点:

As mentioned above, this code can segment lines into more than two groups of angles as well. Here's it running on a hand drawn triangle, and calculating the intersection points of the detected lines with k=3:

这篇关于查找使用houghlines opencv绘制的两条线的交点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆