原理
霍夫变换是一种流行的检测任何形状的技术,前提是您可以用数学形式表示该形状。即使它被破坏或扭曲一点,它也可以检测到形状。我们将看到它对一条线是如何工作的。
一条直线可以表示为 y=mx+c 或以参数形式表示,如 ρ=xcosθ+ysinθ 其中 ρ 是原点到直线的垂直距离,θ 是这条垂直线与水平轴形成的夹角以逆时针方向测量(该方向因您表示坐标系的方式而异。OpenCV 中使用此表示)。参考下面的图像:
所以如果线在原点以下通过,它会有一个正的 rho 并且角度小于 180。如果它在原点之上,而不是取大于 180 的角度,而是取小于 180 的角度,并且 rho被取为负。任何垂直线将具有 0 度,水平线将具有 90 度。
变换步骤参考Hough transform
1. OpenCV 中的霍夫变换
上面解释的所有内容都封装在 OpenCV 函数 cv.HoughLines()中。它只是返回一组 :math:(rho, theta) 值。 ρ 以像素为单位,θ 以弧度为单位。第一个参数,输入图像应该是一个二值图像,所以在应用霍夫变换之前应用阈值或使用Canny边缘检测。第二个和第三个参数分别是 ρ 和 θ 精度。第四个参数是阈值,这意味着它应该被视为一条线的最低投票数。请记住,投票数取决于线上的点数。所以它代表了应该检测的最小线长。
img = cv.imread("../../file/photos/sudoku.jpg")
img_c = img.copy()
gray = cv.cvtColor(img_c, cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray, 50, 150, 3) # 进行边缘检测
lines = cv.HoughLines(edges, 1, np.pi / 180, 150)
# 取出每条线单元
for line in lines:
rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + 1000 * (-b)) # 延伸长该线段的x坐标
y1 = int(y0 + 1000 * a) # 延伸该线段的y坐标
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * a)
# 两点确定一条直线,画出检测到的直线
cv.line(img_c, (x1, y1), (x2, y2), (255, 0, 0), thickness=2)
plt.imshow(img_c, )
plt.axis("off")
plt.show()
结果如下:
2. OpenCV中的概率霍夫变换
在霍夫变换中,您可以看到,即使对于具有两个参数的行,也需要进行大量计算。概率霍夫变换是我们看到的霍夫变换的优化。它没有考虑所有的点。相反,它只需要一个足以进行线检测的随机点子集。我们只需要降低阈值。请参见下图,它比较了霍夫空间中的霍夫变换和概率霍夫变换。 (图片提供:Franck Bettinger 的主页)
OpenCV 实现基于 Matas, J. 和 Galambos, C. 和 Kittler, J.V. 使用渐进式概率霍夫变换对线条进行鲁棒检测 。使用的函数是 cv.HoughLinesP()。它有两个新参数。
- minLineLength - 线的最小长度。比这短的线段被拒绝。
- maxLineGap - 线段之间的最大允许间隙,将它们视为一条线。
最重要的是,它直接返回直线的两个端点。在以前的情况下,你只能得到线的参数,而且你必须找到所有的点。在这里,一切都很直接和简单。
img = cv.imread(cv.samples.findFile('sudoku.png'))
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray, 100, 150, apertureSize=3)
lines = cv.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength=50, maxLineGap=20)
for line in lines:
x1, y1, x2, y2 = line[0] #得到的每条线的四个点
cv.line(img, (x1, y1), (x2, y2), (0, 255, 0), thickness=2)
img = cv.cvtColor(img, cv.COLOR_BGRA2RGB)
plt.subplot(121), plt.imshow(gray, cmap='gray'), plt.title("Input image")
plt.axis("off")
plt.subplot(122), plt.imshow(img), plt.title("houghliens image")
plt.axis("off")
plt.show()