1. 图像预处理在整个视觉方案里的位置
1.1 预处理不只是“美化图像”
读《HALCON机器视觉与算法原理编程实践》第5章之前,我一直把图像预处理当成“让画面更好看”的步骤,直到自己上手做工业检测项目才意识到这个理解偏差得有多远。书上那一句话点醒了我:预处理的目的从来不是改善人眼观看效果,而是为了让后续的阈值分割、边缘提取、Blob分析、匹配定位等算法拿到更稳定、更干净的输入。说白了,预处理是给算法打工的,不是给工程师养眼的。
实际项目里,一条产线的光照条件不可能永远恒定,工件表面会有油污、反光、氧化色差,相机传感器本身也会引入噪声。如果不做预处理,后面无论是用threshold做分割、用edges_sub_pix提亚像素边缘,还是用shape_model做模板匹配,结果都会飘。同一个工件,上午拍和下午拍,灰度分布可能差一大截,算法参数上午能跑通下午就崩。预处理的作用就是把这种“环境不确定性”尽量压平,让算法面对的输入尽量接近理想状态。
这也是为什么HALCON官方文档里几乎每一个视觉案例都要先走一遍预处理流程。你去看那些示例程序,filter、scale、equ_histo这几个词出现频率极高。第5章就是这个思路的系统化总结:先讲清楚图像预处理的数学基础,再给出常用算子的适用场景,最后串联出一套完整流程。理解这一章,后面学阈值分割、形态学、特征提取才会有根基,不然就像建房子不打地基,参数全靠试,换个工件就翻车。
1.2 一套典型的预处理流程该长什么样
学完第5章,我总结了一套适用于大多数工业视觉场景的预处理流水线,按顺序一般是这样:先是采集原始图像,然后判断图像质量、噪声水平、光照均匀性;接着做灰度化(如果是彩色相机且需要灰度图);然后滤波去噪,常用的有均值滤波、高斯滤波、中值滤波;再做灰度变换,比如对比度拉伸、直方图均衡化;最后根据需要做背景校正或边缘增强。
这个顺序不是随便排的。滤波在前,是因为噪声会干扰灰度统计和边缘定位;灰度变换在后,是因为它需要基于相对干净的灰度分布来计算参数。如果反过来,先做对比度增强再滤波,噪声会被同步放大,后续处理更麻烦。书中虽然没有用“流水线”这个词直接描述,但各节的排列逻辑其实就是这个顺序。
当然不是每个项目都要走完全流程。背光检测的PIN针轮廓,光源均匀、背景单一,可能只需要一次简单的阈值分割,连滤波都不用。而表面划伤检测这种,纹理复杂、光照不均,就得组合滤波、差分、增强多步操作。学预处理最关键的能力,就是知道什么场景该做到哪一步、用哪个算子、参数取多少——这需要原理和经验的共同支撑。
2. 滤波算法:从原理到算子选择
2.1 高斯滤波到底在滤什么
高斯滤波是HALCON里最常见的平滑算子,对应的函数是gauss_filter,它用一个高斯核和图像做卷积。书中给出了高斯核公式,但初看很容易犯晕——为什么要用这么复杂的权重分配方式?
我的理解是这样:图像里的噪声,本质上是一个像素点的灰度值跟周围“应有的值”发生了突变。如果简单粗暴地把邻域像素求平均(均值滤波),确实能把突变抹平,但代价是图像里真正的边缘也一起变糊了。高斯滤波的高明之处在于,它的权重是离中心越近贡献越大,离中心越远贡献越小,近似模拟了自然成像中的像素相关性。实际效果就是:噪声被平滑了,但边缘的锐利度损失比均值滤波小得多。
HALCON里gauss_filter的参数很关键,一个Size、一个Sigma。我踩过坑的地方在于,很多人以为Sigma是决定滤波强度的唯一参数,其实Size决定了核覆盖的邻域范围,Sigma决定权重分布的陡峭程度。经验上的关系是Size要取Sigma的4到5倍再加1(保证核覆盖足够范围),比如Sigma=3,Size就是15到16左右。如果Size太小,核被截断得厉害,实际效果会和理论高斯滤波差距很大,甚至出现奇怪的振铃。
对于不同噪声类型,高斯滤波的效果也不同。高斯噪声(传感器热噪声)用高斯滤波最对症;椒盐噪声用高斯滤波效果就很差,因为椒盐点是极亮或极暗的孤立点,高斯加权平均之后它依然会严重拉偏邻域的结果。这种选择性是书里重点强调的:滤波不是越狠越好,而是要对准噪声类型选武器。
2.2 中值滤波为何是去椒盐噪声的首选
椒盐噪声在工业现场出镜率非常高,比如电磁干扰导致个别像素随机跳变,或者灰尘颗粒落在传感器上造成亮点暗点。第5章里专门列出了中值滤波算法:取当前像素邻域内所有像素灰度值,排序后取中间值作为输出。
这个原理看起来简单,实际效果却有一种“外科手术式”的精准:椒盐噪声点是极端值,不会出现在邻域灰度排序的中间位置,所以取中值可以彻底甩掉它。同时,因为中值不是平均,而是选取某个真实存在的灰度值,所以对边缘的破坏程度远小于均值滤波。我自己做过对比实验:对加了一定比例椒盐噪声的工件图,中值滤波后目标边缘的亚像素定位结果几乎不受影响,而均值滤波后边缘半径方向会有几个微米的系统性偏移——在精密测量里这是致命的。
HALCON里对应算子是median_image,它有一个可以选的参数Margin,控制边缘像素的填充方式。我习惯把Margin设为0,即边缘填充0值,但随后必须配合crop_domain把边框裁掉,否则图像边界会出现一圈黑边,灰度统计时会被误判为极暗区域。另一个参数是Radius,表示滤波半径,工业上如果噪声颗粒不大,Radius=1(3x3邻域)就够用;噪声颗粒超过两三个像素时,再考虑提高到2或3。不要一上来就开大半径,因为中值滤波的代价是丢失细节,半径越大图像越“石化”,后续要提微小特征就难了。
书里也给了几个变种,比如带权中值、多级中值,实际工程里我们用的不多,但理解它们存在是有必要的,因为当物体表面既有噪声又必须保留细微纹理时,普通中值扛不住,这些变种能救场。
2.3 均值滤波与预平滑的注意事项
均值滤波在HALCON里的实现是mean_image,原理就是简单的邻域平均。它在学术上看没什么“高级感”,但工程上我发现它的定位不是主力去噪,而是“预平滑”——在大尺度分析(比如找大块缺陷区域、做背景估计)之前,先用它把细碎纹理抹匀,让后续计算更稳定。
但这恰恰是新手最容易翻车的地方。均值滤波的卷积核一旦大了,边缘位移和模糊会非常明显。尤其是后面要做像素级边缘检测的项目,如果预处理阶段用了大核均值滤波,边缘会被“摊开”几个像素,基于梯度的检测算子会认为边缘变得更宽了,导致测量结果偏大。我见过有人为了去噪把mean_image的Size开到9x9,结果本来很锐利的工件边缘变成了“缓坡”,后面一维测量直接偏出公差。
如果一个项目中不得不使用大核均值滤波,我的建议是:后续必须在边缘定位环节做补偿,或者在滤波前先用crop_domain把感兴趣区域单独切出来,滤波只作用于该区域,减少对边界信息的影响。另外,均值滤波核尺寸尽量选奇数且成对递增(3、5、7),这样卷积锚点始终在正中心,不容易引入左右不对称的系统偏差。这些细节书里没有专门列,但都是实践中真金白银换来的教训。
3. 灰度变换与对比度增强
3.1 直方图均衡化背后的原理
书中对灰度直方图均衡化(equalization)的数学描述比较精简,核心思路是把图像的灰度分布重新映射,使输出图像的各灰度级像素数量尽量均匀。为什么要这么做?因为很多算法对灰度分布是敏感的,比如全局阈值分割,如果图像所有像素都挤在低灰度区间,一个阈值很难把前景和背景切开;直方图均衡化之后灰度铺开,峰谷分明,阈值的“可选择性”就大了。
HALCON里最直接的算子是equ_histo_image,它做的事情就是直方图均衡化。但我必须提醒:均衡化不是任何时候都该做。它会把噪声也一并“拉伸”到很宽的灰度范围,原本轻微的传感器噪声经过均衡化后可能被放大成视觉可见的颗粒。工业方案里,如果后续做的是高精度灰度测量(比如透过率检测),盲目均衡化反而会毁掉测量线性度。
我的经验是:直方图均衡化适合两类场景,一是待检测目标与环境背景的灰度差异较小,二是光照有明显渐晕导致图像周边暗、中心亮。要是图像本身对比度还行,只是整体偏暗,那优先用灰度拉伸(后面讲),而不是均衡化。因为均衡化的映射是非线性的,它改变的不只是对比度,还有灰度间的相对关系,对依赖灰度绝对值的测量类任务很不友好。
书中还给了一个有意思的判断技巧:先画直方图,如果直方图形状像一个“尖塔”集中在窄区间,均衡化收益最大;如果直方图本身就是多峰分布的,说明图像已经分得比较开,均衡化反而可能把峰拉变形,这时候更该做的是局部分割而非全局增强。这个“先看直方图再决定”的习惯,我现在每个项目都会保留。
3.2 灰度拉伸的参数经验值
灰度拉伸在HALCON里常用scale_image配合Offset和Mult参数实现,公式是:输出灰度 = 输入灰度 × Mult + Offset。这是一种线性映射,不会改变灰度的相对分布,只是整体放大对比度和平移亮度,所以对后续区域分割的影响是可预测的。
实际做工程时,我习惯先调用min_max_gray在感兴趣区域内拿到灰度最小值和最大值,然后计算拉伸系数。比如某工件表面灰度分布在80到160之间,我们想把它的低端拉到30以下,高端抬到220以上,那么相应地就可以算出Mult和Offset的取值。这里有个细腻的小技巧:做拉伸时不要拿全图的灰度极值来计算,因为极值往往被噪声或反光点污染。用灰度直方图的5%分位和95%分位估算区间更稳,能避开那些孤立的亮暗点干扰。
另一个要注意的是数据类型:scale_image是对灰度值做乘加运算,在uint2图像里容易溢出或截断。HALCON里如果原始图像是byte类型,我通常先用convert_image_type转成real类型做运算,算完再用convert_image_type转回byte,这样能避免中间过程的截断误差。书里虽然提了算子用法,但对数据类型的细节讲得很简略,而这恰恰是实操中影响结果稳定的核心点。
灰阶拉伸也不是拉伸得越狠越好。工业图像通常保留80到220的灰度范围就够用了,过度拉伸到0到255会把环境微小的亮度波纹也放大成明暗差异,后续做Blob分析时反而多出一堆伪区域。适度的“留白”其实是在给抗干扰留余地。
3.3 对比度增强与打光的配合
学第5章的时候我一度以为预处理能搞定所有光照问题,实际做项目才知道预处理和打光是互补关系,而且预处理的“能力上限”由打光质量决定。你不可能指望一张完全过曝、高光区域完全饱和的图像,通过灰度变换把过曝区的细节恢复出来——灰度值都到255了,信息已经被截断,任何映射都变不回来。
这里要特别区分两个层次:均匀的偏暗或偏亮可以通过灰度变换纠正;局部的过曝或死黑则必须靠打光改善,或者改用HDR多曝光融合等更重的手段。图像预处理能做的是“锦上添花”,不是“无中生有”。书里虽然没有专门对比这两类光照问题,但工程经验告诉我们:如果预处理阶段发现需要靠很激进的参数才能勉强出图,基本可以判断是打光需要调整,而不是修改程序继续硬撑。
以我经手的某次实验为例,检测一个弧形高光表面,刚开始用条形光低角度照射,表面反光带面积太大,预处理里试遍了高斯滤波、灰度拉伸和局部自适应增强,还是无法稳定分割。后来把光源换成同轴光加偏光片,反光大幅削弱,程序里的预处理参数反而变得很简单,识别效果立刻稳定。这个项目给我的启发是:预处理方案和打光方案要放在一起设计,而不能等图像采出来再补救。
4. 图像预处理实操:算子和流程串一遍
4.1 读取图像与基础信息检查
在HALCON里写预处理程序,第一个动作永远是read_image把图像载入,但很多人拿到图像就开始调滤波,忽略了基础信息检查。我习惯先做三件事:读取图像尺寸与类型、计算灰度直方图、观察感兴趣区域的灰度分布。
图像尺寸和类型会直接影响算子选择。byte类型的灰度图,绝大部分滤波算子都支持;如果是uint2类型(常见于高动态范围的工业相机),某些算子会有限制,直接跑可能报错,就需要先用convert_image_type转换。另一个极其容易被忽略的坑是图像是否带Domain。HALCON的图像不只是一块矩形像素数组,还带一个“感兴趣区域”的概念,很多算子是只处理Domain内部的。如果之前某一步用了reduce_domain裁剪了区域,后面预处理操作只作用于裁剪后的区域。我看过有人调试半天,滤波怎么都不生效,最后才发现是Domain被无意裁到了一个小矩形上。
直方图检查也很有用。用gray_histo统计感兴趣区域的灰度分布,一眼就能看出图像是否存在偏暗、偏亮、对比度不足、双峰不明显等问题。这个步骤相当于给图像做“体检”,再决定要不要滤波、要不要拉伸、要不要均衡化。没有这一步,直接盲目调参,效果好坏全凭运气,换个批次产品往往又得重调。
4.2 按场景组合滤波与增强算子
预处理算子要按场景组合,我目前总结出三套比较成熟的组合方案,它们足够覆盖大多数需求。
第一套是“洁净场景”组合:背景均匀、光照稳定、目标对比度强,像引脚检测、字符识别在背光下的图像。这种场景不需要滤波,直接做一次灰度拉伸或直方图均衡化增强对比度就好。顺序上用scale_image进行调整,再配合threshold做分割。这套组合的关键是算得快,省下滤波时间,整个处理周期可以控制在几个毫秒内。
第二套是“噪声场景”组合:光照有波动、传感器噪声明显,像常规面阵相机在工厂环境里拍的图像。先做中值滤波或高斯滤波去噪,然后做灰度拉伸和对比度增强,必要时再用illuminate算子消除光照不均(illuminate的作用是估计背景照度并减掉,让图像中心与边缘亮度趋于一致)。这套组合的重点是滤波参数宁小勿大,先用中值半径1,再用高斯Sigma=1到2,一步不到位再逐级调整,切忌一次拉满。
第三套是“低对比度/纹理复杂”场景:目标表面与背景灰度差异微小,比如浅划痕检测、透明薄膜缺陷检测。这时候单靠线性拉伸不够,我习惯用emphasize做边缘对比度增强,它通过计算局部均值与原始图像差分来突出细节;或者在频域角度来看,相当于把高频分量适度放大。emphasize的InputMaxMin和Contrast参数需要反复试,Contrast太大会导致光晕和噪声放大,配合中值滤波或高斯滤波能压住一部分副作用。
这三套不是定死的公式,遇到特殊项目还是得灵活拆解重组,但作为起点,比零散试参数要高效得多。
4.3 预处理参数标定与效果评估
预处理调参不能靠肉眼“看着顺眼”,要能量化。我常用的评估指标有几个:灰度方差是否增大、目标边缘梯度幅值是否增强、后续阈值分割能否稳定分离目标与背景。这其实是一个“逆向验证”的思路:预处理调得好不好,直接看下游算法的输出是否稳定,而不是看中间图像顺不顺眼。
实操方法是在HALCON的HDevelop里把整套流程连起来,预处理后直接跑阈值分割和Blob分析,然后用region_features输出目标区域面积、圆度、位置这几个指标。对同一组测试图不断微调预处理参数,观察这些指标的变化幅度。如果指标在参数小范围扰动下基本不变,说明预处理参数在“稳定平台”上,鲁棒性好;如果稍微动一下参数指标就剧烈跳动,说明目前参数处在“悬崖边”,产线上稍微有点光照波动就会翻车。
书里没有明说这个概念,但做工程的人必须懂。在这个思路下,我每套方案里预处理参数最后都要经过“扰动测试”:把曝光时间上下浮动10%,光源亮度上下浮动10%,产品位置稍微偏移,看算法结果是否还在容忍范围内。如果不在,就回到预处理环节继续调,或者是换更鲁棒的后端算法。
4.4 图像差分与背景校正的工程用法
第5章里还有一个处理手段我单独拿出来说,就是图像差分多用于检测表面缺陷。基本思路是先采集一张没有缺陷的“标准背景图”,再用当前图像减去背景图,差值图像中非零区域就是疑似缺陷区域。
听上去很简单,做起来坑很多。主要坑是背景图必须有代表性,如果产品批次之间正常纹理本身就有些差异,直接用固定背景图做差分,会把正常纹理差异误判成缺陷。一个缓解方法是采多张不同产品的背景图做平均,得到一张“统计背景图”;但更稳妥的方案是用HALCON的estimate_noise和reduce_mean_noise这类算子做自适应背景估计,或者在预处理阶段用大窗口均值滤波模拟出背景,再与原图差分。比如用mean_image配合一个比缺陷尺寸大5到10倍的核,算出来的结果就近似背景照度分布。
背景校正和差分结合使用在光学检测里很常见。它本质上是用“空间滤波”的方式把低频背景变化去掉、把高频缺陷突出来。预处理到这一步,图像已经不只是“变好看”,而是变成了“只突出异常”的映射图。后面再配一个简单阈值就能锁定缺陷,计算量小而且稳定。
5. 常见问题与排错实录
5.1 处理后图像整体变暗或偏色,问题出在哪
做灰度拉伸或者直方图均衡化之后,图像整体变暗是个很常见的现象,尤其是用了直方图均衡化之后。因为均衡化追求的是灰度分布均匀,假如原始图像大部分区域处于中高亮度、少数区域很暗,均衡化后中高亮度被压缩到更暗的区间去填补暗区,看起来整幅图就变暗了,因此,这个结果实际上是算法在“拉平”分布导致的,不一定是错误。
解决办法随目标而定:如果是测量类任务,用线性灰度拉伸而非均衡化;如果必须用均衡化增强局部对比度,可以考虑对感兴趣区域局部做均衡而不是全图均衡。偏色问题通常只在彩色图像里出现,HALCON里可以用decompose3分离成RGB三个通道,分别做预处理再合并;如果偏色是照明色温变化引起的,更推荐在采集端做白平衡校正,软件端处理要么效果有限要么耗时高。
排查这类问题,我的流程是先关掉所有预处理算子,单独检查原始图像本身是否偏暗或偏色;然后逐个算子叠加,每叠加一个就查看一次中间输出。这样定位是哪个环节引入的问题,比一次性把全流程跑完再猜原因要快得多。
5.2 滤波过度导致边缘被削没,如何平衡
滤波和保边缘永远是一对矛盾。最常见的翻车场景是:为了去噪把高斯滤波Sigma调到4甚至5以上,噪声确实没了,但目标边缘梯度也下降到和背景噪声差不多水平,边缘检测算子根本找不出边缘轮廓。或者中值滤波Radius开太大,小尺寸目标直接被“吃”掉,Blob分析找不到完整区域。
解决思路有三个方向。第一个是换保边滤波算子,HALCON里像bilateral_filter这类双边滤波能够在平滑平坦区域的同时保留边缘,但代价是计算量大,实时项目慎用。第二个是用“差分还原”的方式:先做强滤波得到背景平滑图,再用原图减背景图,得到的差值图既去掉了背景纹理又保留了边缘细节,这在我前面说的差分处理中很常用。第三个是分级处理:先用弱滤波保证边缘信息不丢,然后在后端算法面对噪声时增加形态学开运算或闭运算来抑制噪点,而不是孤注一掷在预处理阶段把所有噪声全部滤掉。
这背后的工程理念是:把“去噪”这个任务拆给不同环节,各承担一部分,避免某一个环节出现极端参数。
5.3 预处理耗时过长,怎么优化
工业视觉项目走到部署阶段,预处理耗时往往成为瓶颈。一个1920x1080的图像,如果预处理串联了四五个大核滤波,一帧轻松超过几十毫秒,流水线节拍直接受不了。遇到这种情况,有几个优化经验值得分享。
第一,尽量裁剪Region。预处理只针对感兴趣区域做,比如检测一个工件可能只需要画面中央的一小块,先用reduce_domain把区域缩小,滤波算子的计算量会大幅下降。第二,能用小核就不用大核。很多用高斯滤波的场合Sigma=1.5就够,非要去到3,图像更糊还更慢。第三,考虑下采样。如果目标尺寸在图像里占比较大,先把图像用zoom_factor缩小到原来的一半或四分之三,预处理和后续算法都会快很多,代价是亚像素精度下降,所以高精度测量场景慎用。第四,并行化处理。图像分成四块并做并行计算,但要注意分块重叠区域,避免滤波时边界信息缺失。
HALCON的算子底层很多是多线程优化过的,但有些组合方式会阻塞流水线。比如多次读取图像盘上的缓存,或者频繁做数据类型转换。把这些操作提前做完,把预处理链路的中间结果缓存下来,重复实验时能节省大量时间。
5.4 有关预处理参数迁移的经验
最后一个高频问题是:一套预处理参数,在这个批次产品上表现很好,换个批次、换条产线就失效。我经历过的最典型的例子是检测某个金属工件,抛光工艺调整后表面粗糙度变了,反射特性不同了,图像里的灰度分布直接变了。
这种情况下参数迁移要注意什么?关键一步是建立“参数与图像特征”之间的映射关系,而不是记住具体数值。比如通过灰度直方图的均值、标准差来自动调整拉伸系数,用频域里的噪声能量来调整滤波强度。这其实就是一个简易的自动标定,HALCON里支持动态计算参数,你可以写一段逻辑实时计算最合适的参数,虽然不可能完全自动适应所有场景,但至少比参数写死要耐用得多。
我的经验是:在交付方案时,一定要预留参数自适应逻辑。哪怕只是根据每个产品的平均灰度自动调整Offset,也能让方案在产线波动时多撑很久。
6. 预处理与下游算法的衔接要点
6.1 阈值分割前最后一道把关
第5章的预处理内容学完,如果不把它和后续算法串起来理解,就容易学成孤立知识点。以最常用的阈值分割为例,预处理做得好不好,直接决定了全局阈值能不能用。很多场景下,目标与背景的直方图本来就是轻微交叠的,如果预处理阶段通过灰度拉伸把两峰推得更开,一个固定的灰度阈值就能切得很干净;要是拉伸过度或者均衡化把峰的形态搞变形了,反而会让阈值无法取值。
我自己的习惯是:预处理之后先跑一遍灰度直方图,看目标峰和背景峰是否呈现清晰的双峰形态,峰间谷底是否明显。如果双峰明显,直接用threshold选谷底附近的灰度值即可;如果双峰仍然交叠,那就该考虑用动态阈值或局部阈值,比如使用dyn_threshold算子来辅助。这种判断思路能减少“盲目试阈值”的时间。
书里的图像预处理章节其实悄悄埋下了这条逻辑线:预处理的参数选择可以完全以“让直方图双峰更清晰”为导向。有了这个目标,滤波选什么、拉伸到什么程度,都有了参考方向,而不是为预处理而预处理。
6.2 边缘检测与亚像素精度对预处理的要求
边缘检测是HALCON里很多测量方案的命门。edges_sub_pix这类亚像素边缘算子的输入质量要求很高:不仅要求边缘清晰,还要求边缘附近没有太多噪声伪边缘,更不能有模糊过渡带。
预处理会影响亚像素边缘定位精度,这是一个非常值得展开的点。均值滤波或偏大的高斯滤波会让边缘附近的灰度过渡被拉宽,亚像素插值算出来的边缘位置会发生系统性偏移。比如一个直径10mm的轴类零件,如果边缘被滤波抹宽两三个像素,视觉测量直径可能会偏大或偏小几十微米,在精密测量项目里这完全就是废品量产和合格品之间的区别。
因此在测量类项目中,我对预处理的建议是“宁可少滤,不可过滤”。如果噪声不大,就跳过滤波步骤,直接用尺度空间或梯度算子做边缘检测;如果必须滤波,优选对边缘位移影响小的中值滤波或小Sigma高斯滤波。同时,滤波核尺寸要记录在方案设计文档里,产出测量数据时如果精度出现微小偏移,先回头检查是不是滤波参数变了。
6.3 从学习笔记到可部署方案:一个完整案例串讲
第5章内容最好的消化方式,其实是完完整整做一个案例。我之前做过一个模拟项目X,任务是检测某金属垫片表面的异物区域。原始图像光照有明显渐变,表面还有金属拉丝纹理产生的方向性灰度变化,异物本身和背景对比度很低。
我的处理思路是:第一步,读取图像后先裁剪出检测区域,排除背景干扰;第二步,用中值滤波半径1去掉传感器随机噪声;第三步,用大窗口均值滤波估计背景照度(模拟金属表面的低频灰度变化),然后原图与背景相减得到差值图,这一步相当于做了背景校正;第四步,对差值图做轻微灰度拉伸,增强异物和正常区域的差异;第五步,用dyn_threshold做局部阈值分割,把疑似异物区域提取出来;第六步,用形态学开运算去掉孤立噪声点,再用select_shape过滤掉面积过小的区域。整套流程做完,异物区域能够稳定检出。
这个案例串起来就是预处理和后续算法的经典配合。如果我在第三步直接把差分图交给阈值分割,效果也不会差,但加一步轻微拉伸能显著降低阈值选择的敏感度,提升产线稳定性。从第5章的理论角度看,这个方案里的每一步都有依据:中值滤波去椒盐噪声、背景差分去低频变化、灰度拉伸提高对比度。
6.4 彩色图像预处理的原始理解
第5章的图像预处理内容以灰度图像为主,但实际项目中彩色相机不少,所以有必要简单聊一下彩色图像的预处理,作为书后延伸。彩色图像在HALCON里通常由三个通道组成,预处理有两个方向:一个是拆成三个通道分别处理再合并,另一个是转成HSV等色彩空间处理亮度通道。
我的经验是:机器视觉里大部分检测逻辑不依赖颜色,只依赖亮度差异,这时直接用rgb1_to_gray把彩色图转灰度,然后走这套灰度预处理流程就够了。只有到色差检测、颜色分类这种场景,才需要保留颜色信息。这时候我通常把RGB转换到HSV,对V通道做亮度预处理,保留H和S通道不动,因为H和S代表色调与饱和度,用亮度增强去修改它们会破坏颜色语义。
另外,彩色图像里各通道噪声水平不同,常见的是B通道噪声偏大,因为传感器对蓝光敏感度低。如果三个通道统一用同一套滤波参数,噪声大的通道滤波效果不足、噪声小的通道又可能被滤过头。处理时最好先估算各通道的噪声水平,再分别设置参数。这些内容书里没有详细展开,但在彩色视觉项目里能省去很多排查时间。
实操过程中我的额外体会
把第5章反复读了几遍,又结合手上几个项目沉淀下来的经验,我最想分享的一点是:图像预处理最忌讳“背算子”。HALCON里的算子看起来很多,但每种算子背后都是特定的数学假设。gauss_filter假设噪声符合高斯分布,median_image假设噪声是孤立脉冲,equ_histo_image假设对比度不足来源于灰度分布过窄,背景差分假设异常和正常的差异体现在灰度差值上。理解这些假设,才能在新项目里快速挑选出正确算子组合。
最后再分享一个小技巧:在HDevelop里调试预处理流程时,不要只用一张图作为测试样本,最好准备一组至少10张的图,覆盖光照强弱变化、工件位置偏移、表面状态差异等情况。每调一轮参数,就在这组图上批量跑一遍,观察结果是否整体稳定。只在一张图上调得再漂亮,拿到产线批量数据时往往一秒破功。预处理的目标不是在某张图上最好,而是在一批图上都足够好。这个思路贯穿我所有视觉方案开发的始终,希望对你也有帮助。