他说:人的进步有两条:
一是,发展自己的特长
二是,改变自己的缺点
复现十个官方例程第一个:图像二值化与区域分割
功能:
如何使用 binary_threshold 算子对图像进行全局二值化分割具体展示了两种不同的分割方法:
'max_separability'(最大分离性方法)
'smooth_histo'(平滑直方图方法)
01-路径(打开文件目录)
D:\MVTec\docDATA\HALCON-24.11-Progress-Steady\examples\hdevelop\Segmentation\Threshold\binary_threshold.hdev
或者:文件里面
相关算子:read_image,threshold,binary_threshold
调试:
代码文本:binary_threshold.txt
02-实操:
参照:根据核心逻辑的关键定义,提取三个关键词:算子、逻辑控制、模块化。
080105_halcon的程序结构与调试
step1:看看现象
打开代码,直接按F5,观察F5每次到stop (),停止。
看完之后:对代码功能有了简单判断
step2:看看模块化
观察代码整体现象之后,看一看模块,整体注释如下:
- Init display 显示初始化
- 'max_separability' 最大可分性
- This method separates the histogram of the input image into two classes and performs a global thresholding operation to segment one class.
- 该方法将输入图像的直方图分为两个类,并执行全局阈值作以分割一个类。
- Display results
- It is also possible to segment dark areas
- 也可以分割黑暗区域
- Display results
- 'smooth_histo' 光滑柱状图,平滑处理
- This method separates the histogram of the input image into two classes by subsequently smoothing its histogram until there is only one minimum in the smoothed histogram. The position of this minimum determines the threshold.
- 该方法通过随后平滑其直方图直到平滑直方图中只有一个最小值,将输入图像的直方图分成两类。此最小值的位置决定了阈值。
- Display results
- Segment dark areas 分割暗区
- Display results
- The difference between both methods is small, but 'max_separability' is faster in most cases.
- 两种方法之间的差异很小,但在大多数情况下,“最大可分离性”更快。
- Display difference
- binary_threshold returns the threshold used for segmentation in UsedThreshold. The threshold operator with this value returns the same region as binary_threshold did.
- binary_threshold返回用于 UsedThreshold 中分段的阈值。具有此值的阈值运算符返回与binary_threshold相同的区域。
- As a consequence the difference between these two regions is empty.
- 因此,这两个区域之间的差异是空的。
看完之后应当得到:本段代码是,如何使用 binary_threshold 算子对图像进行全局二值化分割,具体展示了两种不同的分割方法:
'max_separability'(最大分离性方法)
'smooth_histo'(平滑直方图方法)
step3:看看逻辑
要看逻辑,需看控制。观察一个树木,首先看主干,再看枝干。
先看看程序里面有没有,
■ 条件分支:if...elseif...else...endif、switch...case...end。
■ 循环结构:for循环(指定步长)、while循环(条件判断),支持break和continue控制流程 。
该程序,第九行有一个for,for I := 0 to |ImageFiles| - 1 by 1,其他并无特殊分支语句。
看完之后:对整体代码有了初步感觉,逐行阅读,有一个循环结构。
step4:看看内容
- 初始化显示设置
dev_update_off () // 关闭HDevelop窗口的自动更新,提高程序运行速度
dev_close_window () // 关闭所有已打开的图像窗口,当然可能一个没打开。
dev_open_window (0, 0, 512, 512, 'black', WindowHandle) // 打开一个512x512的黑色背景窗口,窗口位置(0,0)返回窗口句柄WindowHandle
set_display_font (WindowHandle, 14, 'mono', 'true', 'false') // 设置显示文字的字体:大小14,等宽字体,抗锯齿,不斜体
🎯 作用: 初始化HDevelop的图形显示环境,关闭不必要的自动刷新,设置一个固定大小的黑色窗口用于显示图像和文字信息。
定义:多张要处理的文件
ImageFiles := ['dip_switch/dip_switch_01', 'circular_barcode', 'fin1']
hdevelop 默认的图片文件路径在:安装的文档目录的image文件下(非安装目录)。
D:<font style="color:#DF2A3F;">MVTec\docDATA\HALCON-24.11-Progress-Steady\examples\images
images文件下有一个文件夹想要进去只需要在列表里:'dip_switch/'
ImageFiles := ['dip_switch/dip_switch_01', 'circular_barcode', 'fin1']
由 := 赋值运算法知,ImageFiles 是一个字符串数组,包含三个元素(三个图像名称,不含扩展名),这些图像将要被处理。
循环处理每一张图像
for I := 0 to |ImageFiles| - 1 by 1
🎯 作用: 开始一个 for 循环,依次处理 ImageFiles
列表中的每一张图像。
先知:
for算子:启动一个循环块,该循环块通常在固定次数的迭代中执行。
for — Starts a loop block that is usually executed for a fixed number of iterations.
for( : : Start, End, Step : Index)
for Index := Start to End by Step
从0开始,到<font style="color:#DF2A3F;">ImageFiles</font>
列表最后一个结束,步长为1
|ImageFiles|
是数组长度。
读取图像并设置窗口
read_image (Image, ImageFiles[I]) // 读取当前索引对应的图像,存入到变量Image里面
dev_resize_window_fit_image (Image, 0, 0, -1, -1) // 根据图像尺寸,自动调整窗口大小以适应图像,左上角位置不变
dev_display (Image) // 在窗口中显示读取的图像
🎯 作用: 读取一张图片并调整窗口大小以适应该图像,然后显示它。
ImageFiles[0]='dip_switch/dip_switch_01'
显示提示信息并暂停程序(等待用户按键)
Message := 'Test image for binary_threshold' // 定义要显示的文字信息
disp_message (WindowHandle, Message, 'window', 12, 12, 'black', 'true') // 在窗口上显示提示文本
disp_continue_message (WindowHandle, 'black', 'true') // 显示“按任意键继续”的提示
stop () // 暂停程序执行,等待用户按键继续。这是调试和观察结果的重要手段!
🎯 作用: 在图像上显示提示文字,并暂停程序,让用户有时间观察当前处理的图像。这是 Halcon 开发中常用的交互调试技巧。
此段可跳过,无错。
分析一下:HALCON handle已经被清除了
问题描述:问题出现在 disp_message 函数中调用了 disp_text,而在这个调用中出现HALCON handle 已经被清除的错误。
2454的解释:“参数值超出有效范围”(Parameter value out of range)。
原因可能是:句柄无效
定位一下:返回看看在
disp_continue_message
的第一个参数是窗口句柄(WindowHandle
),若该句柄未正确初始化(如未调用dev_open_window
创建窗口,或窗口已被关闭),会导致disp_text
调用失败。
解决方法:可能是在循环体内的原因
disp_continue_message
的第一个参数是窗口句柄(WindowHandle
),若该句柄未正确初始化(如未调用dev_open_window
创建窗口,或窗口已被关闭),会导致disp_text
调用失败。
系统不建议使用disp_text
最终解决办法:dev_open_window (300, 300, 512, 512, 'black', WindowHandle)
循环的缘故,在前面加入这个就可以测试。
方法一:使用 'max_separability' 分割 亮背景 区域
binary_threshold (Image, RegionMaxSeparabilityLight, 'max_separability', 'light', UsedThreshold)
🎯 作用: 对输入图像 Image
使用 'max_separability'
方法进行二值化,目标是分割出亮背景区域(light),结果保存在 RegionMaxSeparabilityLight
,并返回实际使用的阈值到 UsedThreshold
。
🔍 参数解释:
'max_separability'
: 分割方法,基于直方图最大类间分离性自动计算一个全局阈值。'light'
: 表示我们希望提取的是图像中较亮的区域(即背景可能是亮的,我们要把亮的部分分割出来)。
dev_display (Image) // 再次显示原图dev_display (RegionMaxSeparabilityLight) // 在原图上叠加显示分割出的亮区域Message := 'Bright background segmented globally with' // 构造提示文本Message[1] := 'Method = ''max_separability''' // 第一行附加信息:使用的方法Message[2] := 'Used threshold: ' + UsedThreshold // 第二行附加信息:实际使用的阈值disp_message (WindowHandle, Message, 'window', 12, 12, 'black', 'true') // 显示这些信息disp_continue_message (WindowHandle, 'black', 'true') // 提示继续stop () // 暂停,等待用户按键
🎯 作用: 可视化显示用 'max_separability' 方法分割出的亮区域,并输出所用的阈值,便于理解与调试。
方法一: 使用 'max_separability' 分割 暗前景 区域****
binary_threshold (Image, RegionMaxSeparabilityDark, 'max_separability', 'dark', UsedThreshold)
🎯 作用: 同上,但这次是提取暗前景区域(dark),即我们希望找到图像中较暗的部分。
🔍 比如:如果你的目标物体是黑色或深色的,而背景较亮,那么你就应该选 <font style="color:rgba(0, 0, 0, 0.86);background-color:rgba(255, 255, 255, 0.9);">'dark'</font>
。
(代码结构同上,只是显示的是 <font style="color:rgba(0, 0, 0, 0.86);background-color:rgba(255, 255, 255, 0.9);">RegionMaxSeparabilityDark</font>
)
方法二:使用 'smooth_histo' 分割 亮背景 区域
binary_threshold (Image, RegionSmoothHistoLight, 'smooth_histo', 'light', UsedThreshold)
🎯 作用: 使用 'smooth_histo'
方法分割出亮区域。该方法是通过对直方图进行平滑处理,直到找到唯一的最小值,以此确定阈值。
🔍 与 <font style="color:rgba(0, 0, 0, 0.86);background-color:rgba(255, 255, 255, 0.9);">'max_separability'</font>
不同,这个方法更侧重于直方图形态分析。
直方图需要需要先验知识,篇幅太长不补充了。见参考
显示 'smooth_histo' 分割亮区域的结果
方法二:使用 'smooth_histo' 分割 暗前景 区域
binary_threshold (Image, RegionSmoothHistoDark, 'smooth_histo', 'dark', UsedThreshold)
🎯 作用: 使用 'smooth_histo'
方法分割出暗区域。
方法对比与差异分析
symm_difference (RegionMaxSeparabilityDark, RegionSmoothHistoDark, RegionDifference)
🎯 作用: 计算两种方法分割出的暗区域之间的对称差集,即两个区域不一致的部分,存入 RegionDifference
。
这个函数使用的好
显示两种方法的差异区域
dev_display (Image)
dev_display (RegionDifference)
Message := 'The difference of both methods is small'
... // 构造一段详细的文字说明,比较两种方法的优缺点
disp_message (WindowHandle, Message, 'window', 12, 12, 'black', 'true')
🎯 作用: 可视化两种分割方法的差异,并通过文字说明:
'max_separability' 通常更快
'max_separability' 对过曝/欠曝更鲁棒
'max_separability' 只支持 byte 和 uint2 图像类型,而 'smooth_histo' 还支持 real 类型图像
验证阈值一致性
threshold (Image, RegionThreshold, 0, UsedThreshold)
symm_difference (RegionThreshold, RegionSmoothHistoDark, RegionDifference)
用 threshold 算子复现 binary_threshold 的效果
🎯 作用: 使用标准的 threshold
算子,手动设置阈值(即 binary_threshold
返回的 UsedThreshold
),分割出低于该阈值的区域(暗区域)。然后再次计算与 smooth_histo
方法结果的差异,验证两者是否一致。
🧠 学习点:binary_threshold
内部也是基于阈值分割,只是阈值是通过某种算法自动计算出来的。你完全可以用 threshold
手动指定相同的阈值达到类似效果。
🧠 学习要点总结
知识点 | 说明 |
---|---|
binary_threshold | Halcon 提供的基于直方图分析的自动全局阈值分割算子 |
方法一:'max_separability' | 基于类间最大分离性计算阈值,速度快,效果好,但对图像类型有限制 |
方法二:'smooth_histo' | 基于直方图平滑找最小值计算阈值,支持更多图像类型,更稳定但略慢 |
'light' vs 'dark' | 指定你要分割的是图像中的亮区域还是暗区域,理解目标与背景的关系至关重要 |
UsedThreshold | 算子返回的自动计算出的阈值,你可以用这个值配合 <font style="color:rgba(0, 0, 0, 0.86);">threshold</font> 算子手动实现相同功能 |
stop() 与交互调试 | Halcon 开发中非常重要的调试手段,让你一步步观察结果 |
图像显示与窗口控制 | 如何打开窗口、调整大小、显示图像和文字 |