时间:2025-04-02 16:33
人气:
作者:admin
原文作者:aircraft
原文链接: halcon 入门教程(三) 边缘检测
有兴趣可以多看其他的halcon教程
本篇讲一下边缘检测(边缘提取),因为这个我发现也是比较常用的,放在入门教程(三)会比较好,在入门教程(一)(二)学完了形态学,和Blob分析,再来学边缘检测并且结合案例感觉会掌握学习的很快。跟openCV一样主流的还是那几个检测算子Sobel、Canny、Laplacian等等。
一.边缘检测简介
边缘是图像中灰度、颜色或纹理发生显著变化的区域,通常对应物体的边界。边缘检测的目标是定位这些变化的区域,方法可分为:

halcon示例:
sobel_amp(Image, EdgeAmplitude, 'sum_abs', 3) // Sobel算子,3x3卷积核
edges_image(Image, ImaAmp, ImaDir, 'canny', 1, 'nms') // 综合梯度计算
LowThreshold, HighThreshold)连接边缘。halcon示例:
edges_sub_pix(Image, Edges, 'canny', 1.5, 20, 40) // Canny算法,亚像素精度
halcon 示例:
edges_sub_pix(Image, Edges, 'canny', 1.5, 20, 40) // 直接输出亚像素边缘轮廓
Sigma参数控制边缘检测的灵敏度。示例:
edges_sub_pix(Image, Edges, 'canny', Sigma, LowThresh, HighThresh)
edges_image(Image, ImaAmp, ImaDir, 'canny', 1, 'nms') // 使用NMS
edges_sub_pix(Image, Edges, 'canny', 1.5, 20, 40) // Low=20, High=40
Halcon的边缘检测通过梯度计算、非极大值抑制、双阈值分割和亚像素优化,实现了高精度和强鲁棒性的边缘提取。其核心在于平衡噪声抑制与细节保留,广泛应用于工业检测、自动驾驶、医学成像等领域。实际使用时需根据场景特点调整算法参数,并结合形态学操作(如膨胀、填充)优化结果。
二.算子与案例学习
这里正常我是想让大家先学习相关的算子函数的,但是因为我自己也学习过,我们学习的心态都是不见兔子不撒鹰,没有看到一些效果之前,我们都难以产生比较大的学习热情,所以这里先上一个车道线提取的案例学习,注释都会详细打清楚。在大一上学期的时候自己买了本单片机来学,发现学的太枯燥了,后来就放弃的硬件的路,后面偶然得到了个c语言的学习视频,然后跟着视频学,大一就把c语言c++,MFC那些东西都自学完了。对于初学的人没有能直观看到效果的学习,都是没有太大兴趣的。书本和文字冷冰冰的,怎么让初学者去学!!!
1.案例:车道线检测(autobahn.hdev)

老规矩,先上图,我们的目的是把一定距离内的车道的车道线给提取出来

实例代码:autobahn.hdev
* autobahn.hdev: Fast detection of lane markers
*
dev_update_window ('off')
* 关闭窗口自动更新,避免频繁刷新提升执行速度
dev_close_window ()
* 关闭所有已打开的图形窗口
dev_open_window (0, 0, 768, 575, 'black', WindowID)
* 打开新窗口,位置(0,0),尺寸768x575,背景黑色
MinSize := 30
* 定义形态学膨胀的核大小(30x30矩形)
get_system ('init_new_image', Information)
* 获取系统默认的'init_new_image'参数值
set_system ('init_new_image', 'false')
* 禁止系统自动初始化新图像,避免覆盖已有图像
* 生成网格区域,用于限制后续处理的ROI(感兴趣区域) 注意这里只是提取宽度为30像素的网格线区域出来
gen_grid_region (Grid, MinSize, MinSize, 'lines', 512, 512)
* 参数说明:
* Grid : 输出网格区域
* MinSize : 网格线间距(水平和垂直均为30像素)
* 'lines' : 生成线型网格(非矩形块)
* 512, 512 : 网格覆盖的原始图像尺寸(此处可能与实际图像尺寸不一致,后续通过裁剪修正)
* 裁剪网格区域,仅保留道路部分(坐标范围:行130~450,列10~502)
clip_region (Grid, StreetGrid, 130, 10, 450, 502)
dev_set_line_width (3)
* 设置显示线宽为3(用于网格和边缘的高亮显示)
dev_set_color ('green')
* 设置显示颜色为绿色(用于网格)
read_image (ActualImage, 'autobahn/scene_00')
* 读取第一帧图像(scene_00)
dev_display (ActualImage)
* 显示图像
stop ()
* 暂停程序,等待用户按F5继续
dev_display (StreetGrid)
* 在图像上叠加显示裁剪后的网格区域(绿色)
stop ()
* 再次暂停
for i := 0 to 28 by 1
* 读取当前帧图像(例如:scene_00, scene_01,..., scene_28)
read_image (ActualImage, 'autobahn/scene_' + (i$'02'))
* i$'02'表示两位数字补零
* 将处理区域限制到StreetGrid网格内
reduce_domain (ActualImage, StreetGrid, Mask)
* 作用:生成一个掩膜图像Mask,仅保留StreetGrid区域内的像素
* Sobel边缘检测(梯度幅值计算)
sobel_amp (Mask, Gradient, 'sum_abs', 3)
* 参数说明:
* 'sum_abs' : 梯度计算方法(水平与垂直方向绝对值之和)
* 3 : Sobel算子尺寸(3x3核)
* 初次阈值分割提取边缘点 Gradient这个是得到的梯度图,只有在边缘区域(也就是道路和道路线交汇的区域)才会有较高的灰度值存在
threshold (Gradient, Points, 20, 255)
* 提取梯度值在[20, 255]之间的区域(初步筛选车道线边缘)
* 形态学膨胀(连接离散边缘点)
dilation_rectangle1 (Points, RegionDilation, MinSize, MinSize)
* 使用30x30矩形核对边缘点进行膨胀,连接相邻点形成连续区域
* 限制处理区域到膨胀后的区域
reduce_domain (ActualImage, RegionDilation, StripGray)
* StripGray为仅包含RegionDilation区域的灰度图像
* 高亮度区域提取(车道线通常为白色/黄色)
threshold (StripGray, Strip, 190, 255)
* 提取灰度值在[190, 255]之间的区域(高亮度车道线)
* 填充区域内的孔洞
fill_up (Strip, RegionFillUp)
* 确保车道线区域连续无断裂
* 显示处理结果
dev_display (ActualImage)
* 显示原始图像
dev_display (RegionFillUp)
* 叠加显示检测到的车道线区域(默认颜色)
endfor
dev_set_line_width (1)
* 恢复默认线宽为1
dev_update_window ('on')
* 重新启用窗口自动更新
set_system ('init_new_image', Information)
* 恢复系统参数'init_new_image'的默认值
clip_region) 和域缩减 (reduce_domain) 聚焦道路区域,减少计算量。
效果图:

第一个案例有我的注释在理解起来应该还是比较简单的,也发现了里面开始运用到了一些边缘检测的算子,接下来我们就学习一下几个常用的边缘检测的算子。
1.sobel算子sobel_amp(Image : EdgeAmplitude : FilterType, Size : )详解:
函数原型:
sobel_amp(Image : EdgeAmplitude : FilterType, Size : )
Image:输入图像(单通道灰度图像)。EdgeAmplitude:输出图像,表示梯度幅值(边缘强度)。FilterType:梯度计算方法(如 'sum_abs')。Size:Sobel 核的大小(3, 5, 7, 9, 11 等奇数)。FilterType参数(梯度计算方式)**控制梯度幅值的计算方法,常见选项如下:
**'sum_abs'**(默认):

**'thin_sum_abs'**:
'sum_abs',但使用更小的卷积核(仅适用于 Size=3)。**'x'** 或 **'y'**:
'x')或垂直方向('y')的梯度。**'frei_chen'**:
**'sobel'**:

Size(卷积核大小)**Size=3。Size=5 或 7。
read_image(Image, 'part.png')
sobel_amp(Image, EdgeAmplitude, 'sum_abs', 3)
threshold(EdgeAmplitude, Edges, 20, 255) // 阈值分割提取边缘
工业检测:
FilterType='sum_abs', Size=3。车道线检测(如 autobahn.hdev):
FilterType='sum_abs', Size=3。医学图像处理:
FilterType='frei_chen', Size=5(增强复杂边缘)。
| 场景需求 | 推荐参数 |
|---|---|
| 实时性要求高 | FilterType='sum_abs', Size=3 |
| 高精度边缘定位 | FilterType='sobel', Size=3 |
| 抗噪需求强 | Size=5 或 7 |
| 检测对角边缘 | FilterType='frei_chen' |
| 算子 | 特点 | 适用场景 |
|---|---|---|
sobel_amp |
灵活调节核大小,多种梯度计算方式 | 通用边缘检测 |
edges_image |
集成非极大值抑制(NMS)和亚像素精度 | 高精度边缘(如测量) |
canny |
双阈值和NMS,抗噪能力强但计算量大 | 复杂背景下的弱边缘检测 |
2.亚像素精度边缘提取算子(常用)edges_sub_pix(Image : Edges : Filter, Alpha, Low, High : )详解:
函数原型:
edges_sub_pix(Image : Edges : Filter, Alpha, Low, High : )
Image:输入图像(单通道灰度图像)。Edges:输出的亚像素边缘轮廓(XLD对象)。Filter:边缘检测滤波器类型(如 'canny', 'lanser2', 'deriche1')。Alpha:滤波器的平滑参数(控制边缘锐度与抗噪性)。Low, High:滞后阈值(用于边缘连接)。Filter(滤波器类型)**不同滤波器对应不同的边缘检测算法:
**'canny'**(默认):
**'lanser2'**:
**'deriche1'** 和 **'deriche2'**:
Alpha(平滑参数)**'canny':1.0~3.0。'lanser2':0.3~0.7。
Low 和 High(滞后阈值)**High ≈ 2 * Low。
Low=10, High=20。Low=30, High=60。
read_image(Image, 'part.png')
edges_sub_pix(Image, Edges, 'canny', 1.5, 25, 50)
dev_display(Edges) * 显示亚像素边缘
edges_sub_pix(Image, Edges, 'lanser2', 0.5, 20, 40)
工业测量:
Filter='lanser2', Alpha=0.5, Low=20, High=40。车道线检测:
Filter='canny', Alpha=1.5, Low=15, High=30。医学图像分析:
Filter='canny', Alpha=2.0, Low=10, High=20。| 问题现象 | 解决方案 |
|---|---|
| 边缘断裂 | 降低 Low 或增大 Alpha |
| 噪声过多 | 增大 Alpha 或提高 Low/High |
| 边缘模糊 | 减小 Alpha |
| 漏检弱边缘 | 降低 High 或 Low |
| 算子 | 精度 | 抗噪性 | 速度 | 适用场景 |
|---|---|---|---|---|
edges_sub_pix |
亚像素 | 高 | 中 | 高精度测量 |
sobel_amp |
像素级 | 中 | 快 | 快速边缘检测 |
canny |
像素级 | 高 | 慢 | 复杂背景下的边缘 |
edges_sub_pix 是 Halcon 中实现亚像素边缘检测的核心算子,通过合理选择滤波器类型(Filter)、平滑参数(Alpha)和阈值(Low, High),可在噪声抑制与细节保留之间取得平衡。典型场景包括工业零件测量、车道线识别和医学图像分析。实际应用中需结合后处理操作(如边缘连接和拟合)以提升结果质量。
3.像素精度边缘提取算子edges_image(Image : ImaAmp, ImaDir : Filter, Alpha, NMS, Low, High : )详解:
函数原型:
edges_image(Image : ImaAmp, ImaDir : Filter, Alpha, NMS, Low, High : )
ImaAmp)和方向图像(ImaDir),支持多种滤波器和非极大值抑制(NMS)。Image:输入图像(单通道灰度图像)。ImaAmp:输出梯度幅值图像(灰度图,高值对应边缘)。ImaDir:输出梯度方向图像(角度图,范围0~180°)。Filter:边缘检测滤波器类型(如 'canny', 'sobel_fast')。Alpha:滤波器平滑参数。NMS:非极大值抑制模式('none', 'nms', 'thin')。Low, High:滞后阈值(用于边缘连接)。Filter(滤波器类型)****'canny'**:
基于高斯导数的Canny算法,支持亚像素级精度。
**'sobel_fast'**:
优化的Sobel算子,计算速度快。
**'lanser2'** 或 **'deriche2'**:
高精度滤波器,适合测量任务。
Alpha(平滑参数)**NMS(非极大值抑制模式)**'none'**:不进行非极大值抑制,输出宽边缘。'nms'**:标准非极大值抑制,细化边缘至单像素宽。'thin'**:优化细化模式,适合高精度测量。Low 和 High(滞后阈值)**High ≈ 2 * Low。
High:边缘强度的最低阈值,高于此值的像素被保留为强边缘。Low:低于此值的像素被忽略;介于两者之间的像素需与强边缘连接。Low=10, High=20。Low=30, High=60。read_image(Image, 'part.png')
edges_image(Image, Amp, Dir, 'canny', 1.5, 'nms', 20, 40)
threshold(Amp, Edges, 1, 255) // 二值化边缘
edges_image(Image, Amp, Dir, 'sobel_fast', 0, 'none', 10, 20)
工业零件检测:
Filter='canny', Alpha=1.5, NMS='nms', Low=20, High=40。实时视频处理(如车道线检测):
Filter='sobel_fast', NMS='none', Low=15, High=30。医学图像分析(如血管分割):
Filter='lanser2', Alpha=0.5, NMS='thin', Low=10, High=20。| 问题现象 | 解决方案 |
|---|---|
| 边缘过宽 | 启用 NMS='nms' 或 NMS='thin' |
| 噪声过多 | 增大 Alpha 或提高 Low/High |
| 弱边缘漏检 | 降低 Low 和 High |
| 计算速度慢 | 改用 Filter='sobel_fast' |
| 算子 | 精度 | 抗噪性 | 输出类型 | 适用场景 |
|---|---|---|---|---|
edges_image |
像素级 | 高 | 梯度幅值+方向 | 通用边缘检测 |
edges_sub_pix |
亚像素 | 高 | XLD轮廓 | 高精度测量 |
sobel_amp |
像素级 | 中 | 梯度幅值 | 快速边缘检测 |
edges_image 是 Halcon 中灵活的边缘检测算子,支持多种滤波器和非极大值抑制模式。其核心优势在于:
Filter 和 NMS 适配不同场景(速度、精度、抗噪性)。Alpha 和阈值平衡噪声抑制与细节保留。典型应用包括工业检测、医学图像处理和实时视频分析。实际使用中需根据具体需求调整参数,并配合阈值分割或形态学操作优化结果。
写到这里我想的是边缘检测都学了,霍夫变换也可以了解学习一下,反正基本都是可以配合起来一起使用的:Halcon 中的霍夫变换(Hough Transform)是一种强大的工具,主要用于从图像中检测几何形状(如直线、圆、椭圆等)
霍夫变换通过将图像空间中的点映射到参数空间(极坐标系),利用投票机制检测几何形状。
对于直线检测,每个边缘点 (x,y) 对应极坐标中的一条正弦曲线:

关键步骤:
* 读取图像并提取边缘
read_image(Image, 'road.png')
edges_sub_pix(Image, Edges, 'canny', 1.5, 20, 40)
threshold(Edges, RegionEdges, 1, 255)
* 霍夫变换检测直线
hough_lines(RegionEdges, 0.02, 50, 0.1, 10, Angle, Dist)
* 绘制检测到的直线
gen_region_hline(RegionLines, Angle, Dist)
dev_display(Image)
dev_display(RegionLines)
函数原型:
hough_lines(RegionIn : : AngleResolution, Threshold, AngleGap, DistGap : Angle, Dist)
RegionIn:输入区域(通常为边缘检测后的二值图像)。AngleResolution:角度分辨率(控制角度检测精度)。Threshold:累加器阈值(决定直线的最小支持点数)。AngleGap:角度合并阈值(合并相近角度的直线)。DistGap:距离合并阈值(合并相近距离的直线)。Angle:检测到的直线的角度(弧度制,范围:-π/2 ~ π/2)。Dist:直线到原点的距离(像素单位,基于极坐标公式:r = x*cosθ + y*sinθ)。AngleResolution(角度分辨率)**0.01 ~ 1.0(弧度)。0.01):角度划分精细,检测精度高,但计算量大。0.1):角度划分粗糙,计算速度快,可能漏检细节。Threshold(累加器阈值)**Threshold=50:直线至少需要50个边缘点支持。AngleGap(角度合并阈值)**AngleGap=0.05(约2.86°):若两条直线角度差小于0.05弧度,视为同一方向。DistGap(距离合并阈值)**DistGap=10:若两条直线距离差小于10像素,视为同一位置。* 合并角度差<5°、距离差<20像素的直线
AngleGap := radians(5) * 5度转弧度
DistGap := 20
hough_lines(RegionEdges, 0.02, 30, AngleGap, DistGap, Angle, Dist)
车道线检测(如 autobahn.hdev):
AngleResolution=0.02, Threshold=50, AngleGap=0.1, DistGap=15。工业零件几何检测:
AngleResolution=0.01, Threshold=100, AngleGap=0.05, DistGap=5。文档表格线提取:
AngleResolution=0.05, Threshold=30, AngleGap=0.2, DistGap=10。| 问题现象 | 解决方案 |
|---|---|
| 检测到过多短线段 | 提高 Threshold,减少噪声影响 |
| 漏检长直线 | 降低 Threshold,增大 AngleGap |
| 直线断裂 | 合并参数(AngleGap和DistGap)设置过小,适当增大 |
| 计算速度慢 | 增大 AngleResolution,降低精度以换取速度 |
输入区域预处理:
RegionIn 应为二值化的边缘区域,建议先用 edges_sub_pix 或 sobel_amp 提取边缘。极坐标系原点:
Dist 是直线到原点的极坐标距离。角度范围:
Angle 范围为 -π/2 到 π/2,对应直线方向(与x轴夹角)。| 方法 | 优点 | 缺点 |
|---|---|---|
hough_lines |
直接输出参数,支持合并相近直线 | 计算量较大,需精细调参 |
fit_line_contour_xld |
基于轮廓拟合,精度高 | 需先提取边缘轮廓,无法合并直线 |
| 深度学习(如HoughNet) | 鲁棒性强,适应复杂场景 | 需要大量训练数据和算力 |
hough_lines 是 Halcon 中基于霍夫变换的经典直线检测算子,通过调节角度分辨率、阈值和合并参数,可适应不同场景的直线检测需求。其核心优势在于直接输出直线的极坐标参数,便于后续几何分析。实际应用中需结合预处理(边缘检测)和后处理(参数合并)以优化结果,适用于工业检测、自动驾驶和文档分析等领域。
OK,接下来在看个案例道路图像的边缘提取edge_segments.hdev:

图像:

效果图:

实例代码:
* 关闭窗口自动更新以提升执行速度
dev_update_off ()
* 关闭所有已打开的图形窗口
dev_close_window ()
* ****************************
* 步骤1: 读取图像并初始化窗口
* ****************************
* 读取图像文件'mreut'
read_image (Image, 'mreut')
* 获取图像尺寸
get_image_size (Image, Width, Height)
* 根据图像尺寸自适应打开显示窗口
dev_open_window_fit_image (Image, 0, 0, Width, Height, WindowID)
* 设置窗口字体为等宽字体,字号12
set_display_font (WindowID, 12, 'mono', 'true', 'false')
* 设置绘图模式为仅绘制区域边界(不填充)
dev_set_draw ('margin')
* 设置绘图线宽为3像素
dev_set_line_width (3)
* 显示原始图像
dev_display (Image)
* 显示继续提示信息(黑底白字)
disp_continue_message (WindowID, 'black', 'true')
* 暂停执行,等待用户按键
stop ()
* ****************************
* 步骤2: 边缘检测与滤波处理
* ****************************
* 使用Lanser2滤波器进行边缘检测
* 参数说明:
* 'lanser2' : 滤波器类型(平衡精度与抗噪性)
* 0.5 : 平滑系数(Alpha值)
* 'nms' : 非极大值抑制
* 20, 40 : 滞后阈值(Low=20, High=40)
edges_image (Image, ImaAmp, ImaDir, 'lanser2', 0.5, 'nms', 20, 40)
* 显示梯度幅值图像
dev_display (ImaAmp)
* 显示继续提示信息
disp_continue_message (WindowID, 'black', 'true')
* 暂停执行
stop ()
* ****************************
* 步骤3: 阈值分割与连通域提取
* ****************************
* 对梯度幅值图像进行阈值分割(保留所有非零像素)
threshold (ImaAmp, Region, 1, 255)
* 分割连通域(每个边缘段为一个独立区域)
connection (Region, ConnectedRegions)
* 清空窗口并设置彩色显示模式
dev_clear_window ()
dev_set_colored (12)
* 显示所有连通域(不同颜色区分)
dev_display (ConnectedRegions)
* 显示继续提示信息
disp_continue_message (WindowID, 'black', 'true')
* 暂停执行
stop ()
* ****************************
* 步骤4: 边缘分段处理与XLD生成
* ****************************
* 清空窗口准备显示最终结果
dev_clear_window ()
* 计算连通域总数
count_obj (ConnectedRegions, Number)
* 创建空对象容器存储XLD轮廓
gen_empty_obj (XLDContours)
* 遍历所有连通域
for i := 1 to Number by 1
* 选择第i个连通域
select_obj (ConnectedRegions, SingleEdgeObject, i)
* 将骨架线分割为独立线段
* 参数说明:
* 2 : 最小线段长度(短于2像素的线段被丢弃)
split_skeleton_lines (SingleEdgeObject, 2, BeginRow, BeginCol, EndRow, EndCol)
* 遍历当前连通域分割出的所有线段
for k := 0 to |BeginRow| - 1 by 1
* 根据起点和终点坐标生成XLD线段
gen_contour_polygon_xld (Contour, [BeginRow[k],EndRow[k]], [BeginCol[k],EndCol[k]])
* 将线段添加到容器中
concat_obj (XLDContours, Contour, XLDContours)
endfor
endfor
* 显示最终生成的XLD轮廓集合
dev_display (XLDContours)
上面传统的主要的边缘提取都在halcon的官方示例里了,有兴趣的话还可以学习一下halcon深度学习示例里面的边缘提取segment_edges_deep_learning.hdev(把ROI区域设置好,提取参数设置好的话,提取起来也不错的):

原图提取边缘:


添加噪声干扰图后再提取边缘:


转发和使用本文,请注明作者信息和原文地址---本文原作者为aircraft ---大家好我是徐飞机,有没有大佬们的公司招c++开发/图像处理/opengl/opencv/halcon实习的啊,带上我一个呗QAQ。。。hhhhhh 想要免费获取前端,后端,c/c++,matlab,Python,opencv,机器学习,深度学习,安卓,java,等等全套视频教程请关注机器视觉开发公众号,转发集赞28即可百度云获得hhhhhhhh