4、图像阈值入门:什么是阈值、二值化原理、如何通过阈值分离目标与背景
在OpenMV巡线机器人的视觉处理流程中,图像阈值是最基础也最关键的预处理步骤。巡线任务的核心是让机器人“看懂”地面上的线条——通常是一条黑色或白色的胶带——并将其与周围环境区分开来。然而,摄像头采集到的原始图像是包含大量亮度、颜色和噪声信息的灰度或彩色数据,直接对这些数据进行线段检测或运动控制,计算量巨大且极易受环境干扰。阈值处理正是为了解决“如何将感兴趣的目标从复杂背景中干净地分离出来”这一问题而存在的。本章将深入剖析阈值的数学本质、二值化的工作原理,并通过OpenMV的实际操作,帮助你掌握利用阈值分离目标与背景的完整方法论。
上图展示了从原始图像到二值图像的完整处理链路。摄像头采集的原始图像中,每个像素点都携带灰度或颜色信息;阈值处理的核心是设定一个或多个分割点(阈值T),将像素空间划分为“目标”和“背景”两个子集。在OpenMV中,这一过程通常通过 image.binary() 或 image.threshold() 方法实现。理解这一链路,是后续进行线段检测和运动控制的基础——因为只有获得了干净的二值图像,才能准确提取线条的坐标、角度和宽度等特征。
4.1 核心原理:灰度图像与阈值分割的数学本质
图像阈值处理本质上是一个像素级的分类问题。对于灰度图像,每个像素的亮度值范围是0到255,其中0代表纯黑,255代表纯白。阈值分割的目标是找到一个合适的阈值T,使得所有满足条件 pixel >= T 的像素被归类为目标(通常赋值为255),其余像素被归类为背景(赋值为0)。这一过程可以用数学表达式简洁描述:
if gray_pixel >= T:
binary_pixel = 255
else:
binary_pixel = 0
在OpenMV中,灰度图像的二值化可以直接调用 sensor.set_pixformat(sensor.GRAYSCALE) 后使用 img.binary([(threshold_min, threshold_max)]) 方法。这里的 threshold_min 和 threshold_max 定义了一个灰度区间,落在该区间内的像素被置为白色(255),区间外的像素被置为黑色(0)。值得注意的是,OpenMV的 binary() 方法支持多阈值区间,这意味着你可以同时提取多个灰度范围的目标,这在处理复杂背景时非常有用。
对于彩色图像,阈值处理扩展到了多通道空间。OpenMV默认使用LAB色彩空间进行颜色阈值分割,其中L代表亮度(0-100),A代表绿色到品红色的分量(-128到127),B代表蓝色到黄色的分量(-128到127)。通过设定每个通道的最小值和最大值,可以构建一个三维的“颜色盒子”,只有同时满足三个通道条件的像素才会被识别为目标。这种方法的优势在于,它能够将颜色信息与亮度信息分离,从而在光照变化剧烈的环境下保持较好的鲁棒性。
4.2 判断方法与操作流程:如何确定最佳阈值
确定最佳阈值是阈值处理的核心难点。阈值设置过高,目标像素可能被误判为背景;阈值设置过低,背景噪声则会被误判为目标。以下是三种常用的阈值确定方法,以及它们在OpenMV中的具体操作流程:
| 方法名称 | 适用场景 | OpenMV实现方式 | 优缺点 |
|---|---|---|---|
| 固定阈值法 | 光照稳定、目标与背景灰度差异明显的环境 | 手动调整 threshold_min 和 threshold_max,通过IDE的阈值编辑器实时预览 |
简单直观,但环境变化时需要重新调参 |
| 自适应阈值法(Otsu) | 光照不均匀、目标与背景灰度差异不固定的场景 | 使用 image.threshold() 配合 auto_threshold=True 参数,或调用 image.get_histogram() 分析灰度分布 |
自动计算最优阈值,适应性强,但计算量稍大 |
| 颜色阈值法(LAB) | 彩色线条(如红色胶带)在彩色背景(如灰色地板)上的场景 | 使用 sensor.set_pixformat(sensor.RGB565),通过IDE的阈值编辑器分别调整L、A、B三个通道的上下限 |
抗光照干扰能力强,但需要精确标定颜色范围 |
在OpenMV IDE中,阈值调试的推荐流程如下:
- 采集样本图像:将摄像头对准实际巡线场景,点击IDE右下角的“快照”按钮,冻结一帧包含目标线条和背景的图像。
- 打开阈值编辑器:在IDE的“工具”菜单中选择“机器视觉”->“阈值编辑器”,加载刚才的快照图像。
- 调整阈值区间:在编辑器右侧的直方图面板中,拖动滑块调整阈值上下限。观察左侧图像预览,确保目标线条被高亮为白色,背景被置为黑色。
- 验证鲁棒性:在不同光照条件下(如开灯、关灯、侧光)重复上述步骤,记录多组阈值参数,取交集或平均值作为最终配置。
- 写入代码:将调试好的阈值参数复制到代码中,例如
img.binary([(80, 180)])表示灰度值在80到180之间的像素被识别为目标。
4.3 案例拆解:从灰度直方图到稳定阈值
下面通过一个具体的巡线场景,完整演示阈值分离目标与背景的实战过程。假设我们使用OpenMV Cam H7 Plus,在室内环境(光照约500 lux)下追踪一条宽度为2cm的黑色电工胶带,地面为浅灰色瓷砖。
初始现象与目标:摄像头采集到的原始灰度图像中,黑色线条的灰度值约为30-60,而浅灰色地板的灰度值约为150-200。目标是通过阈值处理,将黑色线条完整提取出来,同时消除地板上的细小纹理和反光噪声。
关键输入数据:使用 img.get_histogram() 获取灰度直方图,观察到明显的双峰分布——第一个峰集中在灰度值40附近(对应黑色线条),第二个峰集中在灰度值170附近(对应地板)。双峰之间的谷底位于灰度值100左右,这个谷底就是理想的阈值分割点。
分析判断过程:如果选择固定阈值T=100,则灰度值低于100的像素(包括黑色线条和部分阴影)被识别为目标,灰度值高于100的像素(包括地板和反光区域)被识别为背景。但实际测试发现,当机器人经过瓷砖接缝处时,接缝的暗色阴影灰度值约为90,会被误判为目标,导致线段检测出现“伪线条”。
实际操作步骤:
- 将阈值区间调整为
(0, 80),排除灰度值在80-100之间的阴影干扰。 - 在代码中加入形态学滤波操作
img.erode(1)和img.dilate(1),去除二值图像中的孤立噪点。 - 使用
img.find_blobs()找到所有白色区域,并筛选出面积最大的区域作为目标线条。
结果对比:调整前,使用T=100时,每帧图像平均检测到3-5个“伪线条”区域;调整后,使用T=80并配合形态学滤波,伪线条数量降为0,且黑色线条的完整性保持在95%以上(线条中部的细小断裂可通过膨胀操作修复)。
风险与代价:将阈值从100降低到80,虽然消除了阴影干扰,但也可能丢失部分灰度值在80-100之间的线条边缘像素。如果线条本身颜色较浅(如灰色胶带),这种阈值设置会导致线条变细甚至断裂。因此,阈值的选择需要在“抗干扰”和“目标完整性”之间取得平衡。
复盘经验:在实际巡线中,光照变化是阈值失效的主要原因。建议在代码中实现“动态阈值”功能——每隔一定时间重新采集一帧图像,自动计算直方图谷底位置并更新阈值参数。OpenMV的 image.threshold() 方法支持 auto_threshold=True 参数,可以自动执行这一过程。
4.4 常见误区与总结复盘
在图像阈值处理的学习和实践中,初学者常犯以下错误,需要特别注意规避:
- 误区一:阈值是固定不变的。很多初学者在实验室调好一组阈值后,就直接部署到实际场地,结果发现机器人一换位置就“失明”。实际上,环境光照、地面材质、摄像头角度都会影响灰度分布,阈值必须根据实际场景动态调整。
- 误区二:二值化后图像就“干净”了。二值化只是完成了像素分类,但分类结果中仍然可能包含噪声、孤立点、断裂区域等。必须配合形态学操作(腐蚀、膨胀、开运算、闭运算)来优化二值图像的质量。
- 误区三:颜色阈值比灰度阈值“高级”。颜色阈值确实能提供更多信息,但它的调试复杂度也更高。在OpenMV的LAB阈值编辑器中,L、A、B三个通道的微小变化都可能导致分割结果剧烈波动。对于黑白线条识别,灰度阈值往往更稳定、更高效。
- 误区四:忽略ROI(感兴趣区域)的设置。在巡线场景中,线条通常只出现在图像的下半部分。如果对整个图像进行阈值处理,不仅计算量大,而且容易将图像上方的无关物体(如墙壁、天花板)误判为目标。通过
img.binary(thresholds, roi=(x, y, w, h))指定ROI,可以显著提高处理速度和准确性。
总结复盘:图像阈值入门的关键在于理解“分割”的本质——通过设定合适的阈值,将像素空间划分为目标与背景两个子集。在OpenMV巡线机器人中,这一过程需要结合灰度直方图分析、阈值编辑器调试、形态学优化和ROI设置等多个环节。掌握从“原始图像”到“干净二值图像”的完整链路,是后续实现线段检测和运动控制的必要前提。建议读者在完成本章学习后,自行采集不同光照条件下的图像样本,反复练习阈值调试流程,直到能够快速、准确地为任意场景设定最优阈值。