注意
转到末尾下载完整的示例代码。或通过 Binder 在浏览器中运行此示例
直线霍夫变换#
霍夫变换在其最简单的形式中是一种检测直线的方法[1]。
在以下示例中,我们构建了一个带有直线交叉的图像。然后我们使用霍夫变换。探索可能穿过图像的直线的参数空间。
算法概述#
通常,直线参数化为\(y = mx + c\),其中\(m\) 为梯度,c 为 y 截距。但是,这意味着对于垂直线,\(m\) 将趋于无穷大。因此,我们改为构建一条垂直于直线且指向原点的线段。直线由该线段的长度\(r\) 和它与 x 轴的夹角\(\theta\) 表示。
霍夫变换构建一个直方图数组来表示参数空间(即一个\(M \times N\) 矩阵,其中\(M\) 是半径的不同值,\(N\) 是\(\theta\) 的不同值)。对于每个参数组合\(r\) 和\(\theta\),我们找到输入图像中非零像素的数量,这些像素会落在相应的直线附近,并相应地递增位置\((r, \theta)\) 处的数组。
我们可以认为每个非零像素都“投票”支持潜在的直线候选。结果直方图中的局部最大值指示最可能直线的参数。在我们的示例中,最大值出现在 45 度和 135 度,对应于每条直线的法向量角度。
另一种方法是渐进概率霍夫变换[2]。它基于这样的假设:使用投票点的随机子集可以很好地近似实际结果,并且可以通过沿连通分量行走在投票过程中提取直线。这将返回每条线段的起点和终点,这很有用。
probabilistic_hough 函数有三个参数:应用于霍夫累加器的通用阈值、最小线长和影响线合并的线间隙。在下面的示例中,我们找到长度大于 10 且间隙小于 3 个像素的直线。
参考文献#
直线霍夫变换#
import numpy as np
from skimage.transform import hough_line, hough_line_peaks
from skimage.feature import canny
from skimage.draw import line as draw_line
from skimage import data
import matplotlib.pyplot as plt
from matplotlib import cm
# Constructing test image
image = np.zeros((200, 200))
idx = np.arange(25, 175)
image[idx, idx] = 255
image[draw_line(45, 25, 25, 175)] = 255
image[draw_line(25, 135, 175, 155)] = 255
# Classic straight-line Hough transform
# Set a precision of 0.5 degree.
tested_angles = np.linspace(-np.pi / 2, np.pi / 2, 360, endpoint=False)
h, theta, d = hough_line(image, theta=tested_angles)
# Generating figure 1
fig, axes = plt.subplots(1, 3, figsize=(15, 6))
ax = axes.ravel()
ax[0].imshow(image, cmap=cm.gray)
ax[0].set_title('Input image')
ax[0].set_axis_off()
angle_step = 0.5 * np.diff(theta).mean()
d_step = 0.5 * np.diff(d).mean()
bounds = [
np.rad2deg(theta[0] - angle_step),
np.rad2deg(theta[-1] + angle_step),
d[-1] + d_step,
d[0] - d_step,
]
ax[1].imshow(np.log(1 + h), extent=bounds, cmap=cm.gray, aspect=1 / 1.5)
ax[1].set_title('Hough transform')
ax[1].set_xlabel('Angles (degrees)')
ax[1].set_ylabel('Distance (pixels)')
ax[1].axis('image')
ax[2].imshow(image, cmap=cm.gray)
ax[2].set_ylim((image.shape[0], 0))
ax[2].set_axis_off()
ax[2].set_title('Detected lines')
for _, angle, dist in zip(*hough_line_peaks(h, theta, d)):
(x0, y0) = dist * np.array([np.cos(angle), np.sin(angle)])
ax[2].axline((x0, y0), slope=np.tan(angle + np.pi / 2))
plt.tight_layout()
plt.show()
概率霍夫变换#
from skimage.transform import probabilistic_hough_line
# Line finding using the Probabilistic Hough Transform
image = data.camera()
edges = canny(image, 2, 1, 25)
lines = probabilistic_hough_line(edges, threshold=10, line_length=5, line_gap=3)
# Generating figure 2
fig, axes = plt.subplots(1, 3, figsize=(15, 5), sharex=True, sharey=True)
ax = axes.ravel()
ax[0].imshow(image, cmap=cm.gray)
ax[0].set_title('Input image')
ax[1].imshow(edges, cmap=cm.gray)
ax[1].set_title('Canny edges')
ax[2].imshow(edges * 0)
for line in lines:
p0, p1 = line
ax[2].plot((p0[0], p1[0]), (p0[1], p1[1]))
ax[2].set_xlim((0, image.shape[1]))
ax[2].set_ylim((image.shape[0], 0))
ax[2].set_title('Probabilistic Hough')
for a in ax:
a.set_axis_off()
plt.tight_layout()
plt.show()
脚本的总运行时间:(0 分钟 2.331 秒)