以下是一个使用 OpenCV 进行霍夫线条检测的完整 Python 例程,并附带一个用于测试的合成图像生成代码。该例程同时演示了标准霍夫变换 (HoughLines)和概率霍夫变换 (HoughLinesP)两种方法,并显示检测结果。
bash
pip install opencv-python numpy matplotlib
python
import cv2
import numpy as np
import matplotlib.pyplot as plt
# ------------------------------
# 1. 生成测试图像(合成直线图)
# ------------------------------
def create_test_image():
# 创建一个黑色背景的图像 (400x400)
img = np.zeros((400, 400, 3), dtype=np.uint8)
# 绘制几条不同颜色、不同角度的直线
# 直线1: 白色,斜线 (起点(50,50) -> 终点(350,350))
cv2.line(img, (50, 50), (350, 350), (255, 255, 255), 2)
# 直线2: 蓝色,水平线 (y=200)
cv2.line(img, (50, 200), (350, 200), (255, 0, 0), 2)
# 直线3: 绿色,垂直线 (x=300)
cv2.line(img, (300, 50), (300, 350), (0, 255, 0), 2)
# 直线4: 红色,另一条斜线 (起点(350,50) -> 终点(50,350))
cv2.line(img, (350, 50), (50, 350), (0, 0, 255), 2)
return img
# 生成测试图像
test_img = create_test_image()
cv2.imwrite('test_lines.png', test_img) # 保存图像以供后续使用
print("测试图像已保存为 test_lines.png")
# ------------------------------
# 2. 读取图像并预处理
# ------------------------------
# 实际使用时,可以替换为 cv2.imread('your_image.jpg')
image = test_img.copy() # 使用生成的图像
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 转为灰度图
# Canny 边缘检测
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
# ------------------------------
# 3. 标准霍夫变换 (HoughLines)
# ------------------------------
# 参数: 边缘图, 距离分辨率(rho), 角度分辨率(theta), 阈值
lines = cv2.HoughLines(edges, rho=1, theta=np.pi/180, threshold=100)
# 复制原图用于绘制标准霍夫检测结果
img_hough = image.copy()
if lines is not None:
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))
y1 = int(y0 + 1000 * (a))
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * (a))
cv2.line(img_hough, (x1, y1), (x2, y2), (0, 0, 255), 2)
# ------------------------------
# 4. 概率霍夫变换 (HoughLinesP)
# ------------------------------
# 参数: 边缘图, rho, theta, 阈值, 最小线段长度, 最大间隔
linesP = cv2.HoughLinesP(edges, rho=1, theta=np.pi/180, threshold=50,
minLineLength=100, maxLineGap=10)
# 复制原图用于绘制概率霍夫检测结果
img_houghP = image.copy()
if linesP is not None:
for line in linesP:
x1, y1, x2, y2 = line[0]
cv2.line(img_houghP, (x1, y1), (x2, y2), (0, 0, 255), 2)
# ------------------------------
# 5. 显示结果
# ------------------------------
plt.figure(figsize=(12, 8))
plt.subplot(2, 2, 1)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title('Original Image')
plt.axis('off')
plt.subplot(2, 2, 2)
plt.imshow(edges, cmap='gray')
plt.title('Canny Edges')
plt.axis('off')
plt.subplot(2, 2, 3)
plt.imshow(cv2.cvtColor(img_hough, cv2.COLOR_BGR2RGB))
plt.title('Standard Hough Lines')
plt.axis('off')
plt.subplot(2, 2, 4)
plt.imshow(cv2.cvtColor(img_houghP, cv2.COLOR_BGR2RGB))
plt.title('Probabilistic Hough Lines')
plt.axis('off')
plt.tight_layout()
plt.show()

代码说明
-
生成测试图像
create_test_image()函数创建了一张 400×400 的黑色图像,并在上面绘制了四条不同颜色、不同方向的直线(白色斜线、蓝色水平线、绿色垂直线、红色斜线)。该图像被保存为test_lines.png,方便后续使用。你也可以用cv2.imread()替换为自己的图片。 -
预处理
-
将彩色图像转为灰度图(
cv2.cvtColor)。 -
使用 Canny 边缘检测提取边缘(参数可调)。
-
-
标准霍夫变换 (
cv2.HoughLines)-
输入:边缘图像,距离分辨率
rho=1(像素),角度分辨率theta=π/180(弧度),阈值threshold=100(至少需要多少条曲线交于一点才视为一条直线)。 -
输出:每条直线由
(ρ, θ)表示,然后通过极坐标公式转换为两点坐标,并在原图上绘制红色直线。
-
-
概率霍夫变换 (
cv2.HoughLinesP)-
输入:边缘图像,
rho=1,theta=π/180,阈值threshold=50,最小线段长度minLineLength=100,最大间隔maxLineGap=10。 -
输出:线段端点坐标,直接绘制红色线段。
-
-
结果显示
使用 Matplotlib 并排显示原图、Canny 边缘图、标准霍夫结果和概率霍夫结果。
常见参数调整建议
-
Canny 阈值 :较低的值会检测到更多边缘(可能包含噪声),较高的值会忽略弱边缘。建议使用
cv2.Canny(gray, 50, 150)作为起点,然后根据结果调整。 -
霍夫阈值:值越大,检测到的直线越少但越准确;值越小,检测到的直线越多但可能包含虚假直线。
-
概率霍夫的
minLineLength和maxLineGap:控制检测线段的最小长度和断裂线段的连接间隔。
标准霍夫变换 函数 cv2.HoughLines 中,参数 rho 和 theta 分别对应 距离分辨率 和 角度分辨率,它们是霍夫参数空间(霍夫空间)中两个坐标轴的离散化步长。
距离分辨率 (rho) 的含义
直线在极坐标下的表示为:
ρ=xcosθ+ysinθρ=xcosθ+ysinθ
其中:
-
ρρ 是原点到直线的垂直距离(有正负,通常取绝对值时代表距离);
-
θθ 是法线与 x 轴正方向的夹角(弧度)。
在霍夫变换中,我们将所有可能直线映射到 (ρ,θ)(ρ,θ) 参数空间。这个空间是连续的,但算法必须将其离散化成一个二维累加器(网格),以便对每个边缘点进行投票。
距离分辨率 rho 就是这个网格中 ρρ 轴的步长(间隔) 。例如,如果 rho = 1,那么参数空间中的 ρρ 值取为 0,1,2,...0,1,2,...(或负值),单位是像素。这意味着:
-
算法只考虑距离原点距离为整数像素的直线;
-
两个非常接近的直线(距离相差小于 1 像素)会被视为同一条直线而合并投票。
代码示例回顾
在之前的例程中:
lines = cv2.HoughLines(edges, rho=1, theta=np.pi/180, threshold=100)
这里 rho=1 表示距离分辨率为 1 像素,theta=np.pi/180 表示角度分辨率为 1 度(π/180 弧度)。这样参数空间共有 ρmax−ρminρmax−ρmin(约等于图像对角线长度)个格子,θθ 轴有 180 个格子(因为只考虑 0~π)。计算量适中,检测效果良好。