霍夫变换:几何特征检测与量化验证【计算机视觉】

霍夫变换:几何特征检测与量化验证【计算机视觉】

霍夫变换(Hough Transform)

霍夫变换是计算机视觉领域中经典的几何特征检测算法,核心解决了"从含噪声、遮挡、模糊的图像中精准提取直线/圆等规则几何形状"的问题,通过"图像空间→参数空间"的映射转换,实现对几何特征的鲁棒检测,是车道线检测、工业零件质检、文档边框矫正、相机标定等场景的核心技术支撑。

注意:本文所有代码均可导入Jupyter Notebook

完整代码仓库地址:🔗 GitHub:https://github.com/KnifeWen007/CV---StudyNotebook


Ⅰ、引言

在计算机视觉任务中,边缘检测仅能提取图像中离散的边缘像素,却无法将这些像素整合为完整的几何形状(如直线、圆)------ 而实际场景中,车道线、零件孔洞、文档边框等核心目标均以规则几何形态存在,离散的边缘像素无法直接支撑后续的识别与分析。

霍夫变换为解决这一痛点提供了核心思路:它放弃在图像空间中直接拟合几何形状,转而将每个边缘像素映射到参数空间,通过检测参数空间的峰值来定位几何特征的参数,从而实现"从离散像素到完整形状"的提取。这一方法具备极强的抗噪声、抗遮挡能力,衍生出霍夫直线变换、霍夫圆变换两大核心分支,分别适配不同几何特征的检测需求,是几何特征提取领域的"基石算法"。


Ⅱ、霍夫变换核心原理

霍夫变换的本质是**"空间转换"**:将图像空间中难以拟合的几何特征,转换为参数空间中易于检测的峰值特征。其中,霍夫直线变换是最基础、应用最广的分支,霍夫圆变换是其扩展形式,二者核心逻辑一致,仅参数空间维度不同。

一、霍夫直线变换:二维参数空间的峰值检测

在图像空间中,直线的描述方式决定了霍夫变换的实现逻辑------ 若采用笛卡尔坐标系 y = k x + b y=kx+b y=kx+b,垂直直线的斜率 k k k 会趋于无穷大,无法有效映射;因此霍夫变换采用极坐标方程 描述直线:
ρ = x cos ⁡ θ + y sin ⁡ θ \rho = x\cos\theta + y\sin\theta ρ=xcosθ+ysinθ

  • ρ \rho ρ:坐标原点到直线的垂直距离(单位:像素),范围为 [ − D , D ] [-D, D] [−D,D]( D D D 为图像对角线长度);
  • θ \theta θ:垂线与x轴正方向的夹角(单位:弧度),范围为 [ 0 , π ) [0, \pi) [0,π)(覆盖所有直线方向)。
(1)核心映射逻辑(3步拆解)
  1. 单点映射 :图像空间中一个边缘点 ( x , y ) (x,y) (x,y),对应参数空间( ρ \rho ρ- θ \theta θ)中一条连续的正弦曲线------ 这条曲线代表"所有经过该点的直线的参数组合";
  2. 多点交汇 :图像空间中同一条直线上的 N N N 个边缘点,在参数空间中的 N N N 条正弦曲线会交于同一个 ( ρ , θ ) (\rho, \theta) (ρ,θ) 点,交汇次数等于直线上的边缘点数;
  3. 峰值检测 :统计参数空间中每个 ( ρ , θ ) (\rho, \theta) (ρ,θ) 点的交汇次数,超过设定阈值的点即为"有效直线"------ 阈值越高,检测到的直线越"完整",抗噪声能力越强。

(2)数学例子

假设图像中有一条水平直线 y = 50 y=50 y=50,取其上3个点 P 1 ( 10 , 50 ) P_1(10,50) P1(10,50)、 P 2 ( 20 , 50 ) P_2(20,50) P2(20,50)、 P 3 ( 30 , 50 ) P_3(30,50) P3(30,50),我们通过霍夫映射逻辑,清晰说明这3个点共线的核心原因,贴合极坐标方程与映射规则:

  • 第一步:将3个点分别代入霍夫极坐标方程 ρ = x cos ⁡ θ + y sin ⁡ θ \rho = x\cos\theta + y\sin\theta ρ=xcosθ+ysinθ,得到3条对应曲线的表达式:
    1. 点 P 1 ( 10 , 50 ) P_1(10,50) P1(10,50): ρ 1 = 10 cos ⁡ θ + 50 sin ⁡ θ \rho_1 = 10\cos\theta + 50\sin\theta ρ1=10cosθ+50sinθ
    2. 点 P 2 ( 20 , 50 ) P_2(20,50) P2(20,50): ρ 2 = 20 cos ⁡ θ + 50 sin ⁡ θ \rho_2 = 20\cos\theta + 50\sin\theta ρ2=20cosθ+50sinθ
    3. 点 P 3 ( 30 , 50 ) P_3(30,50) P3(30,50): ρ 3 = 30 cos ⁡ θ + 50 sin ⁡ θ \rho_3 = 30\cos\theta + 50\sin\theta ρ3=30cosθ+50sinθ
  • 第二步:分析3条曲线的交汇点------ 我们知道,这条水平直线 y = 50 y=50 y=50 的本质特征是"与x轴平行,到原点的垂直距离为50",对应霍夫参数空间中唯一一组有效参数: θ = π / 2 \theta=\pi/2 θ=π/2(90°)、 ρ = 50 \rho=50 ρ=50。
    当 θ = π / 2 \theta=\pi/2 θ=π/2 时,根据三角函数性质: cos ⁡ θ = 0 \cos\theta=0 cosθ=0、 sin ⁡ θ = 1 \sin\theta=1 sinθ=1,将其分别代入3条曲线表达式:
    1. ρ 1 = 10 × 0 + 50 × 1 = 50 \rho_1 = 10\times0 + 50\times1 = 50 ρ1=10×0+50×1=50
    2. ρ 2 = 20 × 0 + 50 × 1 = 50 \rho_2 = 20\times0 + 50\times1 = 50 ρ2=20×0+50×1=50
    3. ρ 3 = 30 × 0 + 50 × 1 = 50 \rho_3 = 30\times0 + 50\times1 = 50 ρ3=30×0+50×1=50
  • 第三步:得出结论------ 此时3个点对应的3条正弦曲线,均精准交汇于参数空间的 ( ρ = 50 , θ = π / 2 ) (\rho=50, \theta=\pi/2) (ρ=50,θ=π/2) 点,交汇次数为3(等于直线上的边缘点数)。
  • 第四步:结合峰值检测规则------ 若我们设定的检测阈值≤3,则该交汇点会被判定为"有效峰值",对应图像空间中一条完整的直线,即3个点所在的水平直线 y = 50 y=50 y=50,这也从霍夫变换的角度,严格证明了这3个点共线。

反之,若3个点不在同一条直线上,它们在参数空间中的3条正弦曲线不会交于同一个 ( ρ , θ ) (\rho, \theta) (ρ,θ) 点,只会各自出现独立的低频次交点,无法达到检测阈值,因此不会被判定为有效直线------ 这也是霍夫变换区分"共线点"与"非共线点"的核心逻辑。


二、霍夫圆变换:三维参数空间的优化求解

圆的参数方程为 ( x − a ) 2 + ( y − b ) 2 = r 2 (x-a)^2 + (y-b)^2 = r^2 (x−a)2+(y−b)2=r2,其中 ( a , b ) (a,b) (a,b) 为圆心坐标, r r r 为半径------ 对应的参数空间是三维的 ( a , b , r ) (a,b,r) (a,b,r),直接遍历所有可能的 ( a , b , r ) (a,b,r) (a,b,r) 组合计算量极大(时间复杂度呈指数级增长):假设图像尺寸为 W × H W\times H W×H,半径范围为 [ 0 , R ] [0, R] [0,R],则需遍历 W × H × R W\times H\times R W×H×R 次,对分辨率稍高的图像(如 1000 × 1000 1000\times1000 1000×1000),仅半径遍历100次就需10亿次计算,完全不具备工程实用性。

工程中普遍采用霍夫梯度法(也叫"两步法")优化,核心是"降维求解",具体拆解为3个核心步骤:

  1. 第一步:边缘检测与梯度计算:对图像做Canny边缘检测得到边缘点,再计算每个边缘点的梯度方向(梯度是像素值变化的最快方向,垂直于边缘切线,因此梯度方向必然指向圆心------ 这是梯度法的核心几何依据);
  2. 第二步:二维投票找圆心 :以每个边缘点为起点,沿梯度方向绘制一条短线(或射线),统计图像中所有射线的交汇点,交汇频次最高的点即为圆心 ( a , b ) (a,b) (a,b)------ 这一步仅在二维 ( a , b ) (a,b) (a,b) 空间投票,计算量从三维降至二维;
  3. 第三步:一维统计找半径 :确定圆心 ( a , b ) (a,b) (a,b) 后,计算该圆心到所有边缘点的欧式距离 d = ( x − a ) 2 + ( y − b ) 2 d=\sqrt{(x-a)^2 + (y-b)^2} d=(x−a)2+(y−b)2 ,统计距离的出现频次,频次最高的距离值即为圆的半径 r r r------ 这一步仅需一维遍历,计算量进一步降低。

(1)数学例子

假设图像中有一个圆心为 ( 100 , 100 ) (100,100) (100,100)、半径为50的圆,取圆上3个边缘点 P 1 ( 100 , 50 ) P_1(100,50) P1(100,50)、 P 2 ( 150 , 100 ) P_2(150,100) P2(150,100)、 P 3 ( 100 , 150 ) P_3(100,150) P3(100,150):

  • 第一步:计算3个点的梯度方向:
    1. P 1 ( 100 , 50 ) P_1(100,50) P1(100,50) 在圆的正下方,边缘切线为水平方向,梯度方向垂直向上,指向 ( 100 , 100 ) (100,100) (100,100);
    2. P 2 ( 150 , 100 ) P_2(150,100) P2(150,100) 在圆的正右方,边缘切线为垂直方向,梯度方向水平向左,指向 ( 100 , 100 ) (100,100) (100,100);
    3. P 3 ( 100 , 150 ) P_3(100,150) P3(100,150) 在圆的正上方,边缘切线为水平方向,梯度方向垂直向下,指向 ( 100 , 100 ) (100,100) (100,100);
  • 第二步:沿3个点的梯度方向绘制射线,所有射线均交汇于 ( 100 , 100 ) (100,100) (100,100),该点投票频次最高,判定为圆心;
  • 第三步:计算圆心 ( 100 , 100 ) (100,100) (100,100) 到3个点的距离:
    1. 到 P 1 P_1 P1: ( 100 − 100 ) 2 + ( 50 − 100 ) 2 = 50 \sqrt{(100-100)^2 + (50-100)^2}=50 (100−100)2+(50−100)2 =50;
    2. 到 P 2 P_2 P2: ( 150 − 100 ) 2 + ( 100 − 100 ) 2 = 50 \sqrt{(150-100)^2 + (100-100)^2}=50 (150−100)2+(100−100)2 =50;
    3. 到 P 3 P_3 P3: ( 100 − 100 ) 2 + ( 150 − 100 ) 2 = 50 \sqrt{(100-100)^2 + (150-100)^2}=50 (100−100)2+(150−100)2 =50;
      距离的峰值为50,因此判定圆的半径为50。

(2)核心优势
  1. 计算量大幅降低 :将三维 ( a , b , r ) (a,b,r) (a,b,r) 空间的遍历拆解为"二维找圆心 + 一维找半径",计算复杂度从 O ( W × H × R ) O(W\times H\times R) O(W×H×R) 降至 O ( W × H + W × H ) O(W\times H + W\times H) O(W×H+W×H),工程可执行性显著提升;
  2. 鲁棒性更强:梯度方向天然指向圆心,即使圆存在部分遮挡(如仅保留1/4圆弧),剩余边缘点的梯度射线仍能交汇到正确圆心,抗遮挡、抗噪声能力远优于直接三维遍历;
  3. 参数约束更精准:通过梯度方向约束,避免了无关边缘点对圆心检测的干扰,相比无约束的三维投票,误检率大幅降低。

三、核心差异:霍夫变换 vs 直接拟合

对比维度 霍夫变换 直接几何拟合
抗噪声能力 强(阈值筛选峰值) 弱(单个噪声点影响拟合)
抗遮挡能力 强(部分边缘即可检测) 弱(需完整边缘)
计算复杂度 中(参数空间遍历) 低(仅拟合公式求解)
适用场景 噪声/遮挡/模糊图像 无噪声、完整边缘图像

Ⅲ、代码实现

一、霍夫直线检测

python 复制代码
import cv2
import numpy as np
import matplotlib.pyplot as plt

# Notebook配置:图片内嵌显示、支持中文
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

# 1. 读取图像
img = cv2.imread('road.png')
if img is None:
    raise FileNotFoundError("未找到road.png!检查文件名/路径")

# 2. 图像预处理:灰度+高斯模糊+Canny边缘检测
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转灰度图
blur = cv2.GaussianBlur(gray, (5, 5), 0)      # 高斯模糊降噪
edges = cv2.Canny(blur, 50, 150)              # 提取边缘

# 3. 霍夫直线检测(概率霍夫变换)
lines = cv2.HoughLinesP(
    edges,
    rho=1,                # 距离精度1像素
    theta=np.pi/180,      # 角度精度1度
    threshold=50,         # 投票阈值
    minLineLength=50,     # 最短直线长度
    maxLineGap=10         # 直线断点最大间距
)

# 4. 绘制检测到的直线
img_lines = img.copy()
if lines is not None:
    for line in lines:
        x1, y1, x2, y2 = line[0]
        cv2.line(img_lines, (x1, y1), (x2, y2), (0, 0, 255), 2)  # 红色直线,线宽2
else:
    print("未检测到有效直线!可降低threshold参数")

# 5. 可视化结果
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))

# 显示边缘检测结果
ax1.imshow(edges, cmap='gray')
ax1.set_title('Canny Edge Detection', fontsize=12)
ax1.axis('off')

# 显示直线检测结果
ax2.imshow(cv2.cvtColor(img_lines, cv2.COLOR_BGR2RGB))
ax2.set_title('Hough Line Detection', fontsize=12)
ax2.axis('off')

plt.tight_layout()
plt.show()

核心函数:cv2.HoughLinesP()

参数说明:

python 复制代码
lines = cv2.HoughLinesP(
    edges,                # 输入:Canny边缘检测后的边缘图
    rho=1,                # 距离精度:1像素(参数空间ρ的步长)
    theta=np.pi/180,      # 角度精度:π/180弧度(即1度,参数空间θ的步长)
    threshold=50,         # 投票阈值:只有投票数≥50的直线才会被检测出
    minLineLength=50,     # 最短直线长度:过滤短于50像素的噪声线
    maxLineGap=10         # 直线断点最大间距:10像素内的断点可合并为一条直线
)

cv2.HoughLinesP() 是概率霍夫变换的核心实现

本质是:

  1. 将图像空间的边缘点映射到ρ-θ参数空间;
  2. 统计参数空间中各(ρ,θ)点的交汇次数(投票数);
  3. 保留投票数≥阈值的(ρ,θ),还原为图像空间的直线坐标(x1,y1,x2,y2)。

二、霍夫圆变换

python 复制代码
import cv2
import numpy as np
import matplotlib.pyplot as plt

# 配置图片显示
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

# 读取本地图片
img = cv2.imread('circle.png')
if img is None:
    raise FileNotFoundError("未找到circle.png!请确认图片在代码同级目录")

# 图像预处理
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)

# 霍夫圆检测
circles = cv2.HoughCircles(
    blur,
    cv2.HOUGH_GRADIENT,
    dp=1,
    minDist=30,
    param1=50,
    param2=25,
    minRadius=5,
    maxRadius=0
)

# 绘制检测到的圆
img_circles = img.copy()
if circles is not None:
    circles = np.uint16(np.around(circles))
    for circle in circles[0, :]:
        x, y, r = circle[0], circle[1], circle[2]
        cv2.circle(img_circles, (x, y), 3, (0, 255, 0), 4)
        cv2.circle(img_circles, (x, y), r, (0, 0, 255), 3)
    print(f"共检测到 {len(circles[0, :])} 个圆,已全部绘制")
else:
    print("未检测到任何圆!可尝试降低param2至20或减小minDist至20")

# 显示结果
plt.figure(figsize=(10, 8))
plt.imshow(cv2.cvtColor(img_circles, cv2.COLOR_BGR2RGB))
plt.title('Hough Circle Detection')
plt.axis('off')
plt.show()

核心函数:cv2.HoughCircles()

参数说明:

python 复制代码
circles = cv2.HoughCircles(
    blur,                     # 输入:高斯模糊后的灰度图(降噪后检测更准确)
    method=cv2.HOUGH_GRADIENT,# 检测方法:霍夫梯度法(唯一实用的圆检测方法)
    dp=1,                     # 分辨率因子:1表示参数空间与图像空间分辨率一致
    minDist=20,               # 圆心最小距离:避免检测到重复的同心圆/邻近圆
    param1=50,                # Canny边缘检测高阈值:低阈值自动为其1/2,提取圆边缘
    param2=30,                # 圆心投票阈值:值越小检测圆越多,值越大检测越精准
    minRadius=0,              # 最小检测半径:0表示无下限
    maxRadius=0               # 最大检测半径:0表示无上限
)

cv2.HoughCircles() 基于霍夫梯度法实现

核心是"降维求解":

  1. 先通过Canny边缘检测提取圆边缘,计算每个边缘点的梯度方向(指向圆心);
  2. 在二维空间统计梯度方向的交汇点,投票数≥param2的点判定为圆心;
  3. 计算圆心到边缘点的距离,统计峰值作为圆的半径;
  4. 最终输出格式为(x, y, r)(圆心坐标+半径),完成圆检测。

Ⅳ、总结

综上,霍夫变换凭借「图像空间与参数空间对偶映射+累加投票」的核心逻辑,成为直线、圆这类规则几何形状检测的经典技术,也是本次分享的核心内容。

直线检测核心

通过极坐标方程( ρ = x cos ⁡ θ + y sin ⁡ θ ρ=x\cosθ+y\sinθ ρ=xcosθ+ysinθ)规避了直角坐标下垂直线斜率无穷大的缺陷,经「边缘预处理→空间映射→累加投票→峰值检测」四步,即可鲁棒提取图像中的直线;

圆检测核心

拓展至三维参数空间(圆心x、圆心y、半径r),通过对圆心和半径的投票统计,实现圆形目标的定位与识别。

二者均能有效应对图像噪声、局部遮挡、形状不完整等问题,是计算机视觉中几何特征提取的基础方法。

实战选型技巧

  • 直线检测:追求精度选「标准霍夫变换」,需实时性则用「概率霍夫变换」;
  • 圆检测:依赖霍夫圆变换(CHT),可通过优化参数空间量化精度、调整投票阈值,平衡检测效果与计算效率。

霍夫变换无需精准匹配目标完整轮廓,对复杂场景适应性强,也是入门计算机视觉几何检测的核心知识点。


上一章

图像去雾:从直方图增强到暗通道先验【计算机视觉】https://blog.csdn.net/R_Feynman_/article/details/158807328?spm=1001.2014.3001.5501

相关推荐
麦聪聊数据2 小时前
为什么 AI Agent 需要 RESTful API 而不是直接执行 SQL?
人工智能·sql·restful
计算机安禾2 小时前
【C语言程序设计】第30篇:指针与字符串
c语言·开发语言·c++·算法·visualstudio·visual studio code·visual studio
信奥胡老师2 小时前
GESP 2026年3月C++三级(二进制回文串)
开发语言·c++·算法
这张生成的图像能检测吗2 小时前
(论文速读)Mono3DVLT:基于单眼视频的3D视觉语言跟踪
深度学习·计算机视觉·视觉语言模型·3d目标追踪·单目视频
Oflycomm2 小时前
瑞昱亮相 AWE 2026:从 Wi-Fi 7 到 AIoT,全场景连接能力再升级
人工智能·wifi模组·qogrisys·awe·o8852pm·瑞昱芯片
小年糕是糕手2 小时前
【35天从0开始备战蓝桥杯 -- 刷题包】
c语言·jvm·数据结构·c++·算法·蓝桥杯
AI精钢2 小时前
NVIDIA 可以挑战中国 AI 在开源社区的统治地位吗?
人工智能·ai·开源·llm·nvidia·open source·open weight
小陈phd2 小时前
多模态大模型学习笔记(十八)——基于 DeepSeek-7B 的 LoRA 微调训练实战教程
人工智能·笔记·学习
GISer_Jing2 小时前
AI Agent技能Skills设计
前端·人工智能·aigc·状态模式