计算机视觉-利用霍夫变换检测直线

霍夫变换(Hough Transform)是一种用于检测图像中特定形状(如直线、圆等)的经典算法,其检测直线的原理如下:

直线的参数表示

在笛卡尔坐标系中,直线可以用方程\(y = kx + b\)来表示,其中k是斜率,b是截距。但这种表示方法存在一些问题,例如当直线垂直于x轴时,斜率k会趋于无穷大,导致数值计算上的困难。

为了避免这个问题,在霍夫变换中通常使用极坐标来表示直线。对于平面上的任意一点\((x,y)\),从原点到该点的向量可以用极坐标\((\rho,\theta)\)来表示,其中\(\rho\)是向量的长度,\(\theta\)是向量与x轴正方向的夹角。那么,直线在极坐标下的方程可以表示为\(\rho = x\cos\theta + y\sin\theta\)。

霍夫空间与投票机制

  • 霍夫空间:对于图像中的每一个点\((x,y)\),将其代入极坐标方程\(\rho = x\cos\theta + y\sin\theta\),可以得到一系列的\((\rho,\theta)\)组合,这些组合构成了该点在霍夫空间中的一条曲线。实际上,图像中同一条直线上的所有点,在霍夫空间中对应的曲线会相交于一点,这个交点的坐标\((\rho,\theta)\)就代表了图像中的这条直线。
  • 投票机制:为了找到图像中的直线,需要在霍夫空间中进行 “投票”。首先创建一个二维数组作为霍夫累加器,其维度通常根据\(\rho\)和\(\theta\)的取值范围来确定。对于图像中的每一个边缘点(通过边缘检测算法得到),在霍夫空间中计算其对应的\((\rho,\theta)\)曲线,并在累加器中相应的位置进行投票(通常是将该位置的数值加 1)。投票结束后,累加器中数值较高的点就对应着图像中可能存在的直线。

阈值处理与峰值检测

  • 阈值处理:为了确定哪些点是真正的直线,需要设置一个阈值。只有当累加器中的某个点的投票数超过阈值时,才认为该点对应着图像中的一条直线。
  • 峰值检测:在超过阈值的点中,通常还需要进行峰值检测,以找到真正的直线参数。这是因为可能存在一些相邻的点都超过了阈值,但它们实际上代表的是同一条直线。通过峰值检测算法(如非极大值抑制等),可以找到霍夫空间中的局部最大值,这些最大值对应的\((\rho,\theta)\)就是最终检测到的直线的参数。

霍夫变换(Hough Transform)是一种用于检测图像中特定形状(如直线、圆等)的经典算法,其检测直线的原理如下:

直线的参数表示

在笛卡尔坐标系中,直线可以用方程\(y = kx + b\)来表示,其中k是斜率,b是截距。但这种表示方法存在一些问题,例如当直线垂直于x轴时,斜率k会趋于无穷大,导致数值计算上的困难。

为了避免这个问题,在霍夫变换中通常使用极坐标来表示直线。对于平面上的任意一点\((x,y)\),从原点到该点的向量可以用极坐标\((\rho,\theta)\)来表示,其中\(\rho\)是向量的长度,\(\theta\)是向量与x轴正方向的夹角。那么,直线在极坐标下的方程可以表示为\(\rho = x\cos\theta + y\sin\theta\)。

霍夫空间与投票机制

  • 霍夫空间:对于图像中的每一个点\((x,y)\),将其代入极坐标方程\(\rho = x\cos\theta + y\sin\theta\),可以得到一系列的\((\rho,\theta)\)组合,这些组合构成了该点在霍夫空间中的一条曲线。实际上,图像中同一条直线上的所有点,在霍夫空间中对应的曲线会相交于一点,这个交点的坐标\((\rho,\theta)\)就代表了图像中的这条直线。
  • 投票机制:为了找到图像中的直线,需要在霍夫空间中进行 “投票”。首先创建一个二维数组作为霍夫累加器,其维度通常根据\(\rho\)和\(\theta\)的取值范围来确定。对于图像中的每一个边缘点(通过边缘检测算法得到),在霍夫空间中计算其对应的\((\rho,\theta)\)曲线,并在累加器中相应的位置进行投票(通常是将该位置的数值加 1)。投票结束后,累加器中数值较高的点就对应着图像中可能存在的直线。

阈值处理与峰值检测

  • 阈值处理:为了确定哪些点是真正的直线,需要设置一个阈值。只有当累加器中的某个点的投票数超过阈值时,才认为该点对应着图像中的一条直线。
  • 峰值检测:在超过阈值的点中,通常还需要进行峰值检测,以找到真正的直线参数。这是因为可能存在一些相邻的点都超过了阈值,但它们实际上代表的是同一条直线。通过峰值检测算法(如非极大值抑制等),可以找到霍夫空间中的局部最大值,这些最大值对应的\((\rho,\theta)\)就是最终检测到的直线的参数。

霍夫变换(Hough Transform)是一种用于检测图像中特定形状(如直线、圆等)的经典算法,其检测直线的原理如下:

直线的参数表示

在笛卡尔坐标系中,直线可以用方程\(y = kx + b\)来表示,其中k是斜率,b是截距。但这种表示方法存在一些问题,例如当直线垂直于x轴时,斜率k会趋于无穷大,导致数值计算上的困难。

为了避免这个问题,在霍夫变换中通常使用极坐标来表示直线。对于平面上的任意一点\((x,y)\),从原点到该点的向量可以用极坐标\((\rho,\theta)\)来表示,其中\(\rho\)是向量的长度,\(\theta\)是向量与x轴正方向的夹角。那么,直线在极坐标下的方程可以表示为\(\rho = x\cos\theta + y\sin\theta\)。

霍夫空间与投票机制

  • 霍夫空间:对于图像中的每一个点\((x,y)\),将其代入极坐标方程\(\rho = x\cos\theta + y\sin\theta\),可以得到一系列的\((\rho,\theta)\)组合,这些组合构成了该点在霍夫空间中的一条曲线。实际上,图像中同一条直线上的所有点,在霍夫空间中对应的曲线会相交于一点,这个交点的坐标\((\rho,\theta)\)就代表了图像中的这条直线。
  • 投票机制:为了找到图像中的直线,需要在霍夫空间中进行 “投票”。首先创建一个二维数组作为霍夫累加器,其维度通常根据\(\rho\)和\(\theta\)的取值范围来确定。对于图像中的每一个边缘点(通过边缘检测算法得到),在霍夫空间中计算其对应的\((\rho,\theta)\)曲线,并在累加器中相应的位置进行投票(通常是将该位置的数值加 1)。投票结束后,累加器中数值较高的点就对应着图像中可能存在的直线。

阈值处理与峰值检测

  • 阈值处理:为了确定哪些点是真正的直线,需要设置一个阈值。只有当累加器中的某个点的投票数超过阈值时,才认为该点对应着图像中的一条直线。
  • 峰值检测:在超过阈值的点中,通常还需要进行峰值检测,以找到真正的直线参数。这是因为可能存在一些相邻的点都超过了阈值,但它们实际上代表的是同一条直线。通过峰值检测算法(如非极大值抑制等),可以找到霍夫空间中的局部最大值,这些最大值对应的\((\rho,\theta)\)就是最终检测到的直线的参数。

 

 

---------------------------------------------------------------------------------------------------------------------------------

检测流程如下: 

image_path = r'D:\test1.jpg'
image = cv2.imread(image_path)
if image is None:
    print(f"[ERROR] 无法读取图像,请检查路径:{image_path}")
    exit()
original = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
  • 读取指定路径的图像,若读取失败则输出错误信息并退出程序。
  • 将图像从 BGR 颜色空间转换为 RGB 颜色空间,方便后续使用 Matplotlib 显示。
  • 将图像转换为灰度图像。
  • 对灰度图像进行高斯模糊,以减少噪声。

edges = cv2.Canny(blurred, 50, 150)
plt.figure(figsize=(8, 4))
plt.subplot(121), plt.imshow(blurred, cmap='gray'), plt.title('去噪图像')
plt.subplot(122), plt.imshow(edges, cmap='gray'), plt.title('Canny边缘检测结果')
plt.show()
  • 对去噪后的图像进行 Canny 边缘检测,阈值分别设置为 50 和 150。
  • 显示去噪后的图像和 Canny 边缘检测结果。

 

 

 

h, w = edges.shape
diag_len = int(np.hypot(h, w))
thetas = np.deg2rad(np.arange(-90, 90))
num_thetas = len(thetas)
H = np.zeros((2 * diag_len + 1, num_thetas), dtype=np.uint64)
y_idxs, x_idxs = np.nonzero(edges)
for i in range(num_thetas):
    theta = thetas[i]
    cos_theta = np.cos(theta)
    sin_theta = np.sin(theta)
    rho_values = x_idxs * cos_theta + y_idxs * sin_theta
    rho_indices = np.round(rho_values).astype(int) + diag_len
    valid = (rho_indices >= 0) & (rho_indices < H.shape[0])
    np.add.at(H[:, i], rho_indices[valid], 1)

 

  • 计算图像的对角线长度,作为rho的最大值。
  • 定义theta的取值范围为 - 90 到 89 度,并将其转换为弧度。
  • 初始化 Hough 累加器H,用于记录每个(rho, theta)组合的投票数。
  • 遍历边缘图像中的每个非零像素,计算其对应的rho值,并在累加器中相应位置进行投票。

 

plt.figure(figsize=(10, 6))
plt.imshow(H, cmap='gray', aspect='auto',
           extent=[-90, 89, -diag_len, diag_len])
plt.xlabel('Theta (度)')
plt.ylabel('Rho (像素)')
plt.title('Hough空间投票结果')
plt.colorbar(label='投票数')
plt.show()

 

 使用 Matplotlib 显示 Hough 空间的投票结果,其中颜色表示投票数。

 

peaks = peak_local_max(H, min_distance=20, threshold_abs=50)
rho_idxs, theta_idxs = peaks[:, 0], peaks[:, 1]
rhos_peak = rho_idxs - diag_len
thetas_peak_deg = np.rad2deg(thetas[theta_idxs])

 

  • 使用peak_local_max函数检测 Hough 空间中的局部最大值,最小间隔为 20 像素,阈值为 50。
  • 将峰值的索引转换为rhotheta的值。

 

plt.figure(figsize=(10, 6))
plt.imshow(H, cmap='gray', aspect='auto',
           extent=[-90, 89, -diag_len, diag_len])
plt.scatter(np.rad2deg(thetas[theta_idxs]), rhos_peak,
            c='red', s=40, edgecolors='white')
plt.xlabel('Theta (度)')
plt.ylabel('Rho (像素)')
plt.title('Hough空间峰值点标记')
plt.colorbar(label='投票数')
plt.show()

在 Hough 空间中标记检测到的峰值点。 

 

result = original.copy()
for rho, theta_deg in zip(rhos_peak, thetas_peak_deg):
    theta = np.deg2rad(theta_deg)
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    pt1 = (int(x0 + 1000*(-b)), int(y0 + 1000*a))
    pt2 = (int(x0 - 1000*(-b)), int(y0 - 1000*a))
    cv2.line(result, pt1, pt2, (0, 255, 0), 2)

 

  • 遍历检测到的(rho, theta)组合,将其转换为直线的两个端点坐标。
  • 在原始图像的副本上绘制检测到的直线。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值