Python 找到使用 houghlines opencv 绘制的两条线的交点
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/46565975/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
find intersection point of two lines drawn using houghlines opencv
提问by Nauman Umer
How can I get the intersection points of lines down using opencv Hough lines algorithm?
如何使用 opencv 霍夫线算法得到线的交点?
Here is my code:
这是我的代码:
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()
Output:
输出:
I want to get all the points of intersection.
我想得到所有的交点。
回答by alkasm
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:
你不想得到平行线的交点;只有垂直线与水平线的交点。此外,由于您有垂直线,计算斜率可能会导致爆炸或 inf 斜率,因此您不应使用这些y = mx+b
方程。你需要做两件事:
- Segment your lines into two classes based on their angle.
- Calculate the intersections of each line in one class to the lines in the other classes.
- 根据它们的角度将您的线条分为两类。
- 计算一类中的每条线与其他类中的线的交点。
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.
使用HoughLines
,您已经获得了结果,rho, theta
因此您可以使用 轻松地将角度分割为两类theta
。您可以使用 for 例如cv2.kmeans()
withtheta
作为您要拆分的数据。
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. Edit: 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.
然后,要计算交点,您可以使用给定每条线的两个点来计算交点的公式。您已经从每条线上计算了两个点:(x1, y1), (x2, y2)
因此您可以简单地存储并使用它们。编辑:实际上,如下面我的代码所示,有一个公式可用于计算线与给出的rho, theta
形式的交点HoughLines
。
I have answered a similar questionbefore with some python code that you can check out; note this was using HoughLinesP
which gives you only line segments.
我之前用一些 python 代码回答了一个类似的问题,你可以看看;请注意,这是使用HoughLinesP
它只为您提供线段。
Code example
代码示例
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:
您没有提供原始图像,因此我无法使用它。相反,我将在他们的 Hough 变换和阈值教程中使用 OpenCV 使用的标准数独图像:
First, we'll just read this image and binarize it using adaptive thresholding like what's used in this OpenCV tutorial:
首先,我们将仅读取此图像并使用自适应阈值将其二值化,就像本 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)
Then we'll find the Hough lines with cv2.HoughLines()
:
然后我们会找到霍夫线cv2.HoughLines()
:
rho, theta, thresh = 2, np.pi/180, 400
lines = cv2.HoughLines(bin_img, rho, theta, thresh)
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()
.
现在,如果我们想找到交点,实际上我们只想找到垂直线的交点。我们不想要大部分平行线的交点。所以我们需要分割我们的线路。在这个特定的例子中,您可以根据简单的测试轻松地检查线是水平的还是垂直的;垂直线的 atheta
值约为 0 或 180 左右;水平线的 atheta
约为 90。但是,如果您想根据任意数量的角度自动分割它们,而无需定义这些角度,我认为最好的方法是使用cv2.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
.
有一件棘手的事情要做好。HoughLines
以rho, theta
表格形式返回线条(黑塞标准形式),theta
返回的角度在 0 到 180 度之间,180 度和 0 度附近的线条相似(它们都接近水平线),因此我们需要某种方法来获得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.
如果我们在单位圆上绘制角度,但将角度乘以2,那么最初大约 180 度的角度将变为接近 360 度,因此x, y
单位圆上的值对于 0 处的角度将接近相同。所以我们可以通过2*angle
在单位圆上绘制坐标,在这里获得一些不错的“接近度” 。然后我们可以cv2.kmeans()
在这些点上运行,并根据我们想要的任意数量自动分割。
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)
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).
这里的好处是我们可以通过指定可选参数来指定任意数量的组k
(默认情况下,k = 2
所以我没有在这里指定)。
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:
现在剩下的就是找到第一组中每条线的交点与第二组中每条线的交点。由于这些线是 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
Then to use it, it's simply:
然后使用它,它很简单:
intersections = segmented_intersections(segmented)
And plotting all the intersections, we get:
并绘制所有交叉点,我们得到:
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
:
如上所述,此代码也可以将线分割成两组以上的角度。这是它在手绘三角形上运行,并计算检测到的线的交点k=3
:
回答by Bhupen
If you already have the line segment, just substitute them in a line equation ...
如果您已经有了线段,只需将它们替换为线方程......
x = x1 + u * (x2-x1)
y = y1 + u * (y2-y1)
u can be found by using any of the following ...
可以使用以下任何一种方法找到您...
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))
回答by Wildhammer
First of all, you need to refine the output of Hough transform (I usually do this by k-means clustering based on some criteria, e.g. slope and/or centroids of segments). In your problem, for instance, it seems like the slope for all the lines is usually in the vicinity of 0, 180, 90 degrees so you can do clustering on this basis.
首先,您需要细化霍夫变换的输出(我通常通过基于某些标准的 k 均值聚类来完成此操作,例如段的斜率和/或质心)。例如,在您的问题中,似乎所有线条的斜率通常都在 0、180、90 度附近,因此您可以在此基础上进行聚类。
Next, there are two different ways you can get the intersecting points(which are technically the same):
接下来,有两种不同的方法可以获得相交点(技术上是相同的):
- The equations in Bhupen's answer.
- Using a geometry library like Shapelyor SymPy. The benefit of doing this with a geometry library is that you have access to a variety of tools you might need later on in development(intersection, interpolation, convex hull, etc. etc.)
P.S. Shapely is a wrapper around a powerful C++ geometry library but SymPy is pure Python. You may want to consider this in case your application is time critical.
PS Shapely 是一个强大的 C++ 几何库的包装器,但 SymPy 是纯 Python。如果您的应用程序对时间要求严格,您可能需要考虑这一点。
回答by Animesh Mandal
Here I have processed my image with some methods;
在这里,我用一些方法处理了我的图像;
1.Grayscale
1.灰度
2.Either bitwise conversion or edge detection, it depends on the image I guess,here I have gone with bitwise conversion. First carrying out all detected line into a list.
2.无论是按位转换还是边缘检测,这取决于我猜的图像,这里我已经进行了按位转换。首先将所有检测到的行放入一个列表中。
listOflines = cv2.HoughLines(mask_inv,1,np.pi/180,200)
We will be getting values of 'rho' and 'theta', What I am doing is here creating two empty list one for vertical lines one for the horizontal lines,and appending the values of both lines in respective list.
我们将获得 'rho' 和 'theta' 的值,我在这里做的是创建两个空列表,一个用于垂直线,一个用于水平线,并将两行的值附加到各自的列表中。
rowsValue = []
columnValue = []
Here is the logic for vertical and horizontal lines.
这是垂直线和水平线的逻辑。
for line in listOflines:
if line[0][1] == 0:
columnValue.append(line[0][0])
else:
rowsValue.append(line[0][0])
Now the important part is here, When every line passing through and intersecting one another it is intersecting that line on a particular pixel value. And we have that pixel value in terms of 'rho'.
现在重要的部分在这里,当每条线穿过并彼此相交时,它就是在特定像素值上与该线相交。我们有以“rho”表示的像素值。
Now lets create tuples to pass as a co-ordinate into 'cv2' function i.e. in the form of (x,y).
现在让我们创建元组作为坐标传递到 'cv2' 函数,即以 (x,y) 的形式。
tupsList = [(r,c) for r in rowsValue for c in columnValue]
for tups in tupsList:
cv2.circle(image, tups, 1,(0,0,255), 2)
cv2.imshow('image',image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Thats It!!Now here the images before and after.
就是这样!!现在这里是之前和之后的图像。