3、图像处理基础:像素、颜色空间(RGB/HSV/LAB)、感兴趣区域(ROI)、二值化与阈值概念
在OpenMV智能车项目中,摄像头采集到的原始图像只是一堆离散的亮度与颜色数据。要让智能车识别出赛道上的黑线、路标颜色以及前方障碍物,必须理解图像处理的基本概念。本章将围绕像素、颜色空间、感兴趣区域(ROI)以及二值化与阈值展开,这些是后续巡线、路标识别和避障算法的基础。只有掌握了这些底层逻辑,才能在实际调试中快速定位问题,而不是盲目修改代码参数。
3.1 像素:图像的最小构成单元
像素是数字图像中最基本的概念。OpenMV Cam默认输出QVGA分辨率(320×240),意味着整幅图像由320列、240行共76800个像素点组成。每个像素点存储了该位置的颜色或亮度信息。在灰度模式下,每个像素用一个0~255的整数表示亮度,0为纯黑,255为纯白;在RGB模式下,每个像素由R、G、B三个分量组成,每个分量同样取值0~255。
理解像素的坐标系统至关重要。OpenMV中图像左上角为原点(0,0),x轴向右增加,y轴向下增加。例如,像素(160, 120)恰好位于图像中心。在巡线任务中,我们通常关注图像下半部分的像素行,因为那是车体前方最近的地面区域。通过遍历特定行或列的像素值,可以判断黑线的位置偏移量。
img.get_pixel(x, y)可以获取某点像素值,但实际项目中很少逐像素操作,而是通过区域统计函数(如img.get_statistics())批量处理ROI内的像素,以提高效率。
3.2 颜色空间:RGB、HSV与LAB的对比与选择
颜色空间是描述颜色的数学模型。OpenMV支持RGB、HSV、LAB三种主要颜色空间,不同空间对光照变化的敏感度差异显著。
| 颜色空间 | 分量含义 | 光照敏感性 | 适用场景 |
|---|---|---|---|
| RGB | 红/绿/蓝三通道 | 高(亮度变化直接影响三通道值) | 屏幕显示、简单颜色识别 |
| HSV | 色调/饱和度/明度 | 中(色调分量相对稳定) | 彩色路标识别、颜色分割 |
| LAB | 亮度/绿红轴/蓝黄轴 | 低(L与颜色分离,A/B通道受光照影响小) | 巡线、复杂光照环境下的颜色识别 |
在OpenMV智能车项目中,LAB颜色空间是首选。原因在于:L通道独立表示亮度,而A和B通道分别表示颜色对立轴。当环境光照变化时,L值会波动,但A和B值相对稳定。例如,红色路标在强光和弱光下,其A值始终显著高于B值。相比之下,RGB空间中R、G、B三个通道会同时受光照影响,导致颜色判断阈值难以设定。
HSV空间中的H(色调)分量对光照也有一定鲁棒性,但H值在低饱和度区域(如灰色、白色)会变得不稳定。对于赛道上的白色引导线和黑色赛道,LAB空间的A、B通道接近0,而L通道差异明显,非常适合用于二值化分割。
3.3 感兴趣区域(ROI):聚焦关键区域
ROI(Region of Interest)是指在图像中划定的矩形子区域,格式为(x, y, w, h),其中(x,y)为左上角坐标,w为宽度,h为高度。OpenMV中几乎所有图像处理函数都支持ROI参数,例如img.find_blobs(thresholds, roi=...)。
合理设置ROI能带来三大好处:减少计算量(只处理关键区域,帧率更高)、排除干扰(忽略背景中的无关物体)、提高稳定性(固定ROI内检测,减少误判)。
在巡线场景中,典型的ROI设置是图像底部中央的带状区域。例如,对于320×240的图像,可以设置ROI为(60, 180, 200, 50),即从x=60到x=260,y=180到y=230的区域。这个区域覆盖了车体前方约20~30cm的地面,既能提前感知弯道,又不会因远处透视变形导致误判。
对于路标识别,ROI通常设置在图像上半部分,因为路标悬挂在较高位置。而避障任务中,ROI则根据超声波或摄像头安装角度动态调整,一般设置在图像中线附近。
img.draw_rectangle(roi)将ROI可视化,观察是否覆盖了所有可能出现目标的区域。
3.4 二值化与阈值:从灰度到黑白
二值化是将灰度图像转换为只有黑(0)和白(255)两种像素值的过程。核心是设定一个阈值T:像素值大于等于T则设为255(白),小于T则设为0(黑)。在OpenMV中,二值化通常通过img.binary([thresholds])实现,其中thresholds是一个阈值列表。
阈值的选择直接决定二值化效果。以巡线为例,假设赛道为白色地板、黑色引导线,在LAB空间下L通道中黑线的L值约为20~40,白底的L值约为180~220。设定阈值(0, 100)即可将黑线分割为黑色区域,白底变为白色区域。但若光照不均,部分阴影区域L值可能降到100以下,导致误分割。
OpenMV提供了两种阈值设定方式:固定阈值和自适应阈值。固定阈值简单高效,但需要针对不同光照环境手动调整;自适应阈值(如img.adaptive_threshold())根据局部邻域像素动态计算阈值,能适应光照渐变,但计算量较大,可能影响帧率。
# 巡线二值化示例(LAB空间)
import sensor, image, time
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(10)
# 阈值编辑器获取的黑线LAB阈值
black_threshold = (0, 40, -20, 20, -20, 20) # L, A, B范围
while True:
img = sensor.snapshot()
# 设置ROI为底部中央区域
roi = (60, 180, 200, 50)
# 复制ROI区域并二值化
img.binary([black_threshold], roi=roi)
# 统计黑色像素的质心
blobs = img.find_blobs([black_threshold], roi=roi, pixels_threshold=100)
if blobs:
for b in blobs:
img.draw_cross(b.cx(), b.cy(), color=(255,0,0))
time.sleep_ms(50)
上述代码中,black_threshold的六个数值分别对应LAB空间的L、A、B通道的最小和最大值。通过限制A和B的范围,可以排除与黑色相近的深灰色干扰,只保留真正的黑色引导线。
3.5 案例拆解:光照变化下的巡线阈值调优
场景背景:某高校实验室的OpenMV智能车在上午10点阳光直射环境下巡线正常,但下午3点太阳西斜,部分赛道被阴影覆盖,导致黑线丢失或误检。
初始现象:使用固定阈值(0, 40, -20, 20, -20, 20)时,阴影区域的白色地板L值降至50左右,被误判为黑色,导致二值化图像中出现大面积黑色噪点,巡线算法计算出错误的质心位置。
分析过程:通过阈值编辑器观察阴影区域的像素值,发现L通道在45~60之间,A、B通道接近0。而真正的黑线L值在15~30之间。两者在L通道上有重叠,但A、B通道差异不大。因此单纯调整L阈值无法完全分离。
解决方案:采用两步策略。第一步,将ROI缩小至车体正前方30cm范围,减少远处阴影的影响;第二步,将阈值调整为(0, 35, -15, 15, -15, 15),同时增加pixels_threshold=200过滤小面积噪点。此外,在代码中加入光照补偿逻辑:每100帧重新采样图像左上角区域的L值,动态调整阈值上限。
结果对比:调整前,阴影区域误检率约30%,巡线偏移量抖动达±20像素;调整后,误检率降至2%以内,偏移量稳定在±5像素。代价是ROI缩小后,高速弯道中黑线偶尔超出ROI范围,需要结合转向提前量弥补。
复盘经验:固定阈值适合实验室稳定光照,实际比赛或户外场景必须考虑动态阈值或自适应方法。ROI的缩小虽然减少了干扰,但也牺牲了远距离感知能力,需要根据赛道曲率半径权衡。
3.6 常见误区与总结
- 误区一:在RGB空间直接设定颜色阈值。RGB三个通道高度相关,光照变化时三个值同时漂移,阈值范围难以覆盖。应优先使用LAB或HSV。
- 误区二:ROI设置后不验证。很多开发者凭感觉设定ROI,导致目标物体部分在ROI外。务必用
draw_rectangle可视化确认。 - 误区三:二值化后不进行形态学滤波。二值化图像中常有孤立噪点,使用
img.erode()和img.dilate()可以去除小噪点、连接断裂区域。 - 误区四:忽略帧率影响。复杂的图像处理(如全图自适应阈值)会降低帧率,导致控制延迟。在保证效果的前提下,尽量缩小ROI和简化算法。