「识别」的核心不是「看形状」,而是「数学参数的最优匹配」与「断点的逻辑缝合」。 本文档将识别原理(五个层次)与对应经典示例、可直接运行的 Python 代码整合为一, 每个示例都配有由脚本生成的演示图。

所有示例图由 code/demo*.py 脚本自动生成(合成测试图 → 运行算法 → 出图), 完整可运行代码见 code/ 目录,运行方式见文末 附录:运行演示脚本。


第一层:参数空间投票(霍夫变换)——解决「断裂」问题

当图形因扫描质量差而断成几截时,靠「找连通域」会失效。此时最经典的武器是霍夫变换(Hough Transform)。

原理

  • 直线识别:将图像中无数个像素点,映射到「斜率-截距」参数空间进行累加投票。即使一条直线断成 10 段,这些碎片对应的参数值仍然会投射到参数空间的同一个峰值点上,系统通过检测峰值就能「强行」将断线连成一条完整的直线。
  • 圆/圆弧识别:升级为霍夫梯度法。算法遍历所有边缘点,计算其梯度方向,将梯度方向映射到圆心坐标空间。只要断点位于同一圆周上,它们衍生出的圆心候选点就会高度聚集,从而识别出完整的圆。

经典示例一:霍夫变换——检测图像中的直线与圆

cv2.HoughLinesP() 用于检测线段,cv2.HoughCircles() 用于检测圆。它们都要求输入是边缘图(如 Canny 的结果)。

Python
import cv2
import numpy as np

# ---------- 1. 直线检测 (HoughLinesP) ----------
def detect_lines(image_path):
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 50, 150, apertureSize=3)  # 提取边缘

    # 概率霍夫变换检测线段
    lines = cv2.HoughLinesP(edges,
                            rho=1,                   # 距离精度(像素)
                            theta=np.pi/180,         # 角度精度(弧度)
                            threshold=100,           # 投票阈值
                            minLineLength=50,        # 最小线段长度
                            maxLineGap=10)           # 允许的最大线段间隔

    if lines is not None:
        for line in lines:
            x1, y1, x2, y2 = line[0]
            cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)  # 画红线

    cv2.imshow('Line Detection', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# ---------- 2. 圆检测 (HoughCircles) ----------
def detect_circles(image_path):
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    gray = cv2.medianBlur(gray, 5)  # 中值滤波去噪,对圆检测很重要

    # 霍夫梯度法检测圆
    circles = cv2.HoughCircles(gray,
                               cv2.HOUGH_GRADIENT,
                               dp=1,                 # 累加器分辨率与图像分辨率的反比
                               minDist=50,           # 圆心之间的最小距离
                               param1=100,           # Canny高阈值
                               param2=30,            # 圆心检测的累加器阈值
                               minRadius=10,         # 最小半径
                               maxRadius=100)        # 最大半径

    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, (x, y), r, (0, 255, 0), 2)   # 画绿色圆
            cv2.circle(img, (x, y), 2, (0, 0, 255), 3)   # 画红色圆心

    cv2.imshow('Circle Detection', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# 使用示例
# detect_lines('path_to_your_image.jpg')
# detect_circles('path_to_your_image.jpg')

注意:threshold、param2 等参数对结果影响很大,需要根据具体图像调整。

演示效果图(由 code/demo1_hough_lines.py 生成)

霍夫变换检测断裂直线
霍夫变换检测断裂直线

图中左栏为故意打断成多段并加入椒盐噪声的合成直线图像,中栏为 Canny 边缘图, 右栏为霍夫变换检测结果——断裂的线段被重新「缝合」成完整直线。

演示效果图(圆检测,由 code/demo2_hough_circles.py 生成)

霍夫梯度法检测断裂圆
霍夫梯度法检测断裂圆

图中左栏为圆周被切成多段圆弧的合成图像(含一个内圈干扰圆与噪点), 中栏为 Canny 边缘图,右栏为霍夫圆检测结果——断开的圆弧被还原为完整圆。


第二层:随机采样与一致性检验(RANSAC)——解决「离群杂点」干扰

现实图纸中常有无关的交叉线或噪点(离群点)。识别算法采用 RANSAC(随机抽样一致性) 策略:

  1. 从杂乱的点集中随机抽取最少点数(直线取 2 点,圆取 3 点)拟合出一个候选图形;
  2. 遍历所有其他点,统计有多少点落在该图形的容差带(如像素距离 < 2)内;
  3. 保留「内点」数量最多的那个候选模型。

通过反复迭代,算法能无视大量干扰点,只针对「最主流」的几何特征进行精准识别。

经典示例二:RANSAC——从含噪点中稳健拟合直线

RANSAC 的核心是随机采样和一致性检验。它能有效排除离群点(Outliers)的干扰,找到最能代表主流数据的模型。sklearn 库提供了现成的实现。

Python
import numpy as np
from sklearn import linear_model, datasets
import matplotlib.pyplot as plt

# 生成示例数据:1000个样本,其中50个为离群点
n_samples = 1000
n_outliers = 50
X, y, coef = datasets.make_regression(n_samples=n_samples,
                                      n_features=1,
                                      n_informative=1,
                                      noise=10,
                                      coef=True,
                                      random_state=0)

# 添加离群点(outlier data)
np.random.seed(0)
X[:n_outliers] = 3 + 0.5 * np.random.normal(size=(n_outliers, 1))
y[:n_outliers] = -3 + 10 * np.random.normal(size=n_outliers)

# ---------- 使用RANSAC稳健拟合 ----------
ransac = linear_model.RANSACRegressor()
ransac.fit(X, y)

# 获取内点(Inliers)和外点(Outliers)的掩码
inlier_mask = ransac.inlier_mask_
outlier_mask = np.logical_not(inlier_mask)

# ---------- 可视化对比 ----------
line_X = np.arange(X.min(), X.max())[:, np.newaxis]
line_y_ransac = ransac.predict(line_X)

plt.scatter(X[inlier_mask], y[inlier_mask], color='yellowgreen', marker='.', label='Inliers')
plt.scatter(X[outlier_mask], y[outlier_mask], color='gold', marker='.', label='Outliers')
plt.plot(line_X, line_y_ransac, color='navy', linewidth=2, label='RANSAC Regressor')
plt.legend()
plt.xlabel("Input")
plt.ylabel("Response")
plt.show()

print(f"拟合系数: {ransac.estimator_.coef_[0]:.2f}")  # 应接近真实系数

说明:RANSACRegressor 会自动将数据分为「内点」和「外点」,并仅用内点进行最终拟合,从而得到稳健的结果。

演示效果图(由 code/demo3_ransac_line.py 生成)

RANSAC 稳健拟合直线
RANSAC 稳健拟合直线

左图为数据分布与拟合对比:黄色点是离群点,绿色点是内点。红色虚线为普通最小二乘对全部点 拟合的结果(被离群点严重带偏),蓝色实线为 RANSAC 拟合结果(正确逼近主流数据)。 右图为仅显示内点与 RANSAC 拟合直线的结果。

经典示例三:RANSAC——从角点中拟合圆

这个例子展示了如何从检测到的角点(Corners)中,使用 RANSAC 算法找到一个圆。

Python
import numpy as np
import matplotlib.pyplot as plt
from skimage.measure import CircleModel  # 使用CircleModel作为拟合模型

# 1. 生成一个带噪声的圆
np.random.seed(0)
# 真实圆的参数:圆心(100, 100),半径50
true_center = (100, 100)
true_radius = 50

# 生成圆周上的点,并加入噪声
theta = np.linspace(0, 2*np.pi, 100)
points = np.array([[true_center[0] + true_radius * np.cos(t) + np.random.normal(0, 5),
                    true_center[1] + true_radius * np.sin(t) + np.random.normal(0, 5)] for t in theta])

# 添加一些随机离群点
outliers = np.random.rand(30, 2) * 200
points = np.vstack([points, outliers])

# 2. 使用RANSAC拟合圆模型
# 定义模型:CircleModel(),需要至少3个点来拟合一个圆
model = CircleModel()

# RANSAC拟合
from skimage.measure import ransac
model_robust, inliers = ransac(points, CircleModel, 3,  # 最少3个点拟合一个圆
                               residual_threshold=10,   # 内点阈值
                               max_trials=1000)         # 最大迭代次数

# 3. 结果可视化
inlier_points = points[inliers]
outlier_points = points[~inliers]

# 绘制结果
plt.plot(inlier_points[:, 0], inlier_points[:, 1], '.', color='green', label='Inliers')
plt.plot(outlier_points[:, 0], outlier_points[:, 1], '.', color='red', label='Outliers')

# 绘制拟合出的圆
cy, cx = model_robust.params  # CircleModel的参数是 (cy, cx, radius)
r = model_robust.params[2]
circle = plt.Circle((cx, cy), radius=r, color='blue', fill=False, linewidth=2, label='Fitted Circle')
plt.gca().add_patch(circle)

plt.legend()
plt.axis('equal')
plt.show()

print(f"拟合圆心: ({cx:.2f}, {cy:.2f}), 拟合半径: {r:.2f}")

说明:CircleModel 是 scikit-image 库提供的圆形模型。ransac 函数会反复随机采样 3 个点来生成候选圆,并选择内点最多的那个作为最终结果。

演示效果图(由 code/demo4_ransac_circle.py 生成)

RANSAC 稳健拟合圆
RANSAC 稳健拟合圆

绿色点为落在圆周附近的内点,红色点为随机离群点。蓝色圆为 RANSAC 拟合结果, 其圆心与半径接近真实值(圆心 (100, 100),半径 50),误差通常在 1 个像素以内。


第三层:拓扑图搜索与子图同构——解决「组合图形」识别

对于复杂的嵌套图形(如法兰盘、回字形零件),单靠基本几何不够。此时将识别转化为「图匹配」问题:

  1. 算法先提取所有角点(顶点)和线段(边),构建一个属性邻接图(记录每条边的长度、角度、以及顶点间的连接关系)。
  2. 系统内置一个标准图元库(如「矩形内接圆」、「同心矩形」)。
  3. 识别时,在待测图中搜索与模板图同构的子图,并校验夹角、长度比例是否一致。一旦匹配上,即判定为特定零件轮廓。

无独立代码示例:该层通常基于 networkx(子图同构 networkx.algorithms.isomorphism)配合自建的属性邻接图实现,属于对第一、二层提取结果的二次推理。


第四层:断点缝合与虚拟求交——解决「残缺与遮挡」

这是工业识别中最见功力的部分。当图形缺角或被标注线遮挡时:

  • 端点延伸预测:对于断开的线段端点,算法计算其切线方向,若两条断线的延长线距离阈值小于设定值(如 5 个像素),且夹角接近 0° 或 90°,系统会主动「脑补」出虚拟交点,将两条线缝合。
  • 投影正交补全:识别矩形时,若只捕捉到三条边,算法会根据平行与垂直约束,自动计算缺失的第四条边所在位置,从而完成全图识别。

无独立代码示例:该层通常以第一层(霍夫)检测出的线段为输入,做线段延长、求交与几何约束补全。


第五层:现代深度学习方法(端到端补充)

尽管传统几何法仍是精度主流,但近年也开始引入深度学习做「粗定位」:

  1. 使用目标检测网络(如 YOLO)先在整张大图中快速框选出「圆形区域」或「矩形区域」;
  2. 然后将该局部区域裁切出来,再交由传统的 RANSAC 或霍夫变换进行亚像素级精定位。

这种「粗检测 + 精拟合」的混合模式,大幅提升了在复杂背景下的识别速度。

无独立代码示例:YOLO 部分可参考 ultralytics,裁切后精定位部分复用本文档第一、二层的代码。


总结:识别与分类、拆线的协作关系

环节输入核心任务核心算法
拆线二值图像像素提取顶点坐标Douglas-Peucker(拆分-合并)
分类完整的顶点序列判定几何类型计算边数、夹角、曲率
识别含噪、断裂的像素/点集从乱局中检出目标并还原完整参数霍夫投票、RANSAC、子图同构、断点缝合

识别方法对比

方法核心思想优点缺点最佳适用场景
霍夫变换民主投票:每个像素都为所有可能的图形投票,得票高者胜出。对断裂、轻微变形鲁棒;实现简单。参数空间随图形复杂度指数增长,计算量大;参数调优依赖经验。图像背景干净,需要检测标准几何形状(直线、圆)的场景。
RANSAC精英选拔:反复随机抽样,选出能获得最多拥护者(内点)的模型。对大量离群噪声极其鲁棒;可灵活应用于各种数学模型。需要设定较多阈值(如距离阈值、迭代次数);当内点占比过低时可能失效。数据包含大量噪声或离群点,需要稳健拟合的场景。

在实际的工业应用中,通常会将两者结合:先用霍夫变换进行粗检测,定位出候选图形区域;再用 RANSAC 对候选区域内的点进行精拟合,以获得亚像素级别的精确参数。


附录:运行演示脚本

所有演示脚本位于 code/ 目录,各自独立可运行(合成测试图 → 运行算法 → 输出 figures/*.png)。

Bash
# 进入 code 目录后依次运行(依赖: numpy, matplotlib, opencv-python, scikit-learn, scikit-image)
python demo1_hough_lines.py    # 示例一: 霍夫变换检测断裂直线
python demo2_hough_circles.py  # 示例一(续): 霍夫梯度法检测断裂圆
python demo3_ransac_line.py    # 示例二: RANSAC 稳健拟合直线
python demo4_ransac_circle.py  # 示例三: RANSAC 稳健拟合圆
脚本对应章节输出图
code/demo1_hough_lines.py第一层 示例一figures/fig_hough_lines.png
code/demo2_hough_circles.py第一层 示例一figures/fig_hough_circles.png
code/demo3_ransac_line.py第二层 示例二figures/fig_ransac_line.png
code/demo4_ransac_circle.py第二层 示例三figures/fig_ransac_circle.png
code/figure_style.py—中文字体与绘图公共配置(被各脚本引用)