图像基础与色彩空间:RGB、LAB、灰度图的概念,以及OpenMV中图像表示方式
在OpenMV巡线机器人的开发中,图像处理是一切视觉算法的基石。巡线任务的核心是从摄像头捕获的画面中识别出线条与背景的差异,而这一过程高度依赖于对色彩空间的理解和运用。许多初学者在编写巡线代码时,常常因为对色彩空间理解不透彻,导致阈值设置不合理、线条识别不稳定。本章将系统讲解RGB、LAB、灰度图三种色彩空间的核心原理,并深入剖析OpenMV中图像数据的存储与表示方式,为后续的阈值分割和线段检测打下坚实基础。
3.1 RGB色彩空间:直观但脆弱的颜色模型
RGB(红绿蓝)色彩空间是最为人熟知的颜色表示方式,它基于人眼对三种锥状感光细胞的响应特性,通过红、绿、蓝三种基色的不同强度叠加来产生各种颜色。在OpenMV中,RGB图像通常以RGB565格式存储,即用16位二进制数表示一个像素:高5位为红色分量,中间6位为绿色分量,低5位为蓝色分量。这种格式之所以选择绿色6位,是因为人眼对绿色最为敏感,分配更多位数可以提高视觉感知的精度。
RGB色彩空间的核心优势在于其与显示设备的天然匹配。无论是OpenMV的LCD扩展屏还是电脑显示器,都直接使用RGB信号驱动。当我们需要将图像显示出来时,RGB格式无需任何转换。然而,RGB空间在机器视觉领域存在一个致命弱点:它对光照变化极其敏感。同一个物体在不同色温、不同强度的光照下,其RGB值会发生剧烈变化。例如,一张白色纸张在暖黄色灯光下会呈现偏红的RGB值,而在阴天日光下则偏蓝。这意味着,如果直接使用RGB阈值进行颜色识别,巡线机器人从室内移动到窗边时,原本设置好的阈值就可能完全失效。
在OpenMV的IDE中,当我们使用"帧缓冲区"工具查看图像时,将鼠标悬停在任意像素上,状态栏会显示该像素的RGB值。例如,一个纯红色像素显示为(255, 0, 0),纯绿色为(0, 255, 0),纯蓝色为(0, 0, 255)。但实际从摄像头获取的RGB565数据中,红色分量只有5位精度,即取值范围为0-31;绿色6位精度,范围0-63;蓝色5位精度,范围0-31。当我们在OpenMV IDE中看到(255, 0, 0)这样的数值时,实际上是IDE将RGB565格式转换成了8位每通道的显示格式。
3.2 LAB色彩空间:感知均匀的工业级选择
LAB色彩空间由国际照明委员会(CIE)于1976年制定,其设计目标是为了模拟人眼对颜色的感知方式。LAB空间包含三个通道:L通道表示亮度(Lightness),取值范围0-100,其中0为纯黑,100为纯白;A通道表示从绿色到红色的色彩对立轴,取值范围-128到127;B通道表示从蓝色到黄色的色彩对立轴,取值范围同样为-128到127。这种设计的精妙之处在于,它将亮度信息与色彩信息完全分离,使得颜色识别可以独立于光照强度进行。
LAB空间的"感知均匀"特性意味着,两个颜色在LAB空间中的欧几里得距离与人眼感知到的颜色差异成正比。例如,深红色与浅红色在LAB空间中距离较近,而红色与绿色距离较远。这一特性使得LAB空间非常适合作为颜色阈值分割的基础。在OpenMV巡线任务中,如果我们使用LAB空间来识别红色线条,只需关注A通道的数值范围,而L通道的变化(即光照明暗)不会对识别结果产生决定性影响。
OpenMV的sensor模块提供了RGB到LAB的自动转换功能。当我们调用img.to_lab()方法时,OpenMV会将当前图像从RGB565格式转换为LAB格式。转换后的图像中,每个像素的L、A、B分量分别存储在三个字节中。需要注意的是,OpenMV中的LAB值范围与标准CIE LAB略有不同:L通道范围0-100,A和B通道范围-128到127,但OpenMV内部将其偏移为0-255的无符号整数存储,以便于阈值设置。因此,在OpenMV IDE的阈值编辑器中,LAB阈值范围显示为L(0-100)、A(0-255)、B(0-255),其中A和B的128对应标准LAB中的0。
| 色彩空间 | 通道组成 | OpenMV存储格式 | 光照敏感性 | 典型应用场景 |
|---|---|---|---|---|
| RGB | R、G、B三通道 | RGB565(16位/像素) | 高 | 图像显示、简单颜色识别 |
| LAB | L、A、B三通道 | 每通道8位(24位/像素) | 低(L通道独立) | 阈值分割、颜色分类、巡线 |
| 灰度 | 单通道亮度 | 8位/像素 | 中 | 黑白巡线、边缘检测 |
3.3 灰度图:化繁为简的亮度表达
灰度图是色彩空间中最简化的形式,它仅保留亮度信息,丢弃所有色彩信息。在OpenMV中,灰度图每个像素占用1字节,取值范围0-255,其中0表示纯黑,255表示纯白。灰度图可以通过两种方式获得:一是直接设置摄像头传感器为灰度模式,二是将RGB图像通过to_grayscale()方法转换。灰度转换的经典公式为:Gray = 0.299R + 0.587G + 0.114B,这个权重系数基于人眼对不同颜色的敏感度差异——绿色贡献最大,蓝色最小。
在巡线机器人的实际应用中,灰度图具有不可替代的优势。首先,它的数据量仅为RGB图像的三分之一,这意味着更快的处理速度和更低的存储占用。对于OpenMV Cam H7 Plus这样配备32MB内存的硬件来说,320×240分辨率的灰度图仅需75KB,而RGB565格式需要150KB。其次,许多经典的图像处理算法,如Canny边缘检测、霍夫变换、二值化等,都是基于灰度图设计的。当巡线场景中的线条与背景具有明显亮度差异时(例如黑色线条在白色跑道上),灰度图配合二值化操作可以非常高效地提取线条信息。
然而,灰度图也有其局限性。当线条颜色与背景颜色亮度相近时(例如红色线条在橙色背景上),灰度图将无法有效区分两者。此时必须使用彩色信息,LAB空间就成为了更好的选择。在实际巡线项目中,我们通常会在初始化阶段判断赛道颜色与背景的亮度对比度,如果对比度足够高,优先使用灰度图以获取更快的处理速度;如果对比度不足,则切换到LAB空间进行颜色阈值分割。
3.4 OpenMV图像表示方式与像素操作
在OpenMV的MicroPython环境中,图像以Image对象的形式存在。每次调用sensor.snapshot()方法,摄像头都会捕获一帧图像并返回一个新的Image对象。这个对象封装了图像的像素数据、尺寸、格式等信息,并提供了一系列图像处理方法。理解Image对象的内部表示方式,对于编写高效的巡线代码至关重要。
OpenMV的图像数据在内存中是以连续字节数组的形式存储的。对于RGB565格式,每个像素占用2字节,按照"低字节在前,高字节在后"的小端序排列。例如,一个RGB值为(31, 63, 31)的像素(RGB565格式),其存储为两个字节:0x1F 0xFF。当我们需要访问某个特定像素时,可以使用get_pixel(x, y)方法,该方法返回一个元组。在RGB模式下返回(r, g, b),在灰度模式下返回一个整数,在LAB模式下返回(l, a, b)。
除了像素访问,Image对象还提供了丰富的图像处理方法。以下是一个典型的巡线初始化代码示例:
import sensor, image, time
# 初始化摄像头
sensor.reset()
sensor.set_pixformat(sensor.RGB565) # 设置RGB565格式
sensor.set_framesize(sensor.QVGA) # 设置320×240分辨率
sensor.skip_frames(time=2000) # 等待传感器稳定
# 创建图像对象
img = sensor.snapshot() # 捕获一帧图像
# 获取特定像素值
pixel = img.get_pixel(160, 120) # 获取中心像素
print("RGB值:", pixel) # 输出如 (120, 45, 200)
# 转换为灰度图
gray_img = img.to_grayscale()
gray_pixel = gray_img.get_pixel(160, 120)
print("灰度值:", gray_pixel) # 输出如 98
# 转换为LAB空间
lab_img = img.to_lab()
lab_pixel = lab_img.get_pixel(160, 120)
print("LAB值:", lab_pixel) # 输出如 (45, 128, 135)
在巡线算法的实际开发中,我们通常不会逐像素访问图像数据,而是使用OpenMV提供的高级图像处理函数。例如,find_blobs()函数可以在指定颜色阈值下查找色块,find_lines()函数可以检测线段。这些函数内部已经对图像数据进行了高效处理,我们只需要正确设置阈值参数即可。理解图像表示方式的意义在于:当算法效果不理想时,我们能够通过检查像素值来诊断问题所在,而不是盲目调整参数。
3.5 案例拆解:从RGB到LAB的阈值优化实战
让我们通过一个具体的巡线场景来理解色彩空间选择的实际影响。假设我们正在开发一台巡线机器人,赛道为白色背景上的红色线条。初始阶段,我们使用RGB色彩空间进行阈值设置,在OpenMV IDE的阈值编辑器中,通过框选红色线条区域,得到了以下RGB阈值范围:R(150-255)、G(0-80)、B(0-80)。在室内恒定光照下,这个阈值表现良好,机器人能够稳定识别红色线条。
然而,当我们将机器人移动到靠近窗户的位置时,自然光的变化导致红色线条的RGB值发生了偏移。原本红色的线条在阳光直射下,R值仍然较高,但G和B值也相应增加,导致部分像素不再满足阈值条件。此时,巡线机器人开始出现丢线现象,表现为频繁转向或停止。我们尝试调整阈值,但发现无论怎么调整,都无法同时适应直射光和阴影区域的线条。
解决方案是切换到LAB色彩空间。我们使用img.to_lab()将图像转换后,重新框选红色线条区域,得到LAB阈值:L(30-80)、A(150-200)、B(120-160)。关键在于,A通道表示绿色到红色的对立轴,红色线条的A值显著高于背景。即使光照变化导致L通道(亮度)波动,A通道的数值仍然保持相对稳定。经过测试,在直射光、阴影、阴天三种条件下,A通道的阈值范围仅需微调即可保持稳定识别。
这个案例揭示了色彩空间选择的工程意义:RGB空间适合显示,LAB空间适合分析。在巡线任务中,我们追求的是在不同光照条件下都能稳定工作的算法,因此LAB空间是更优的选择。当然,如果巡线场景是纯黑线条在白色背景上,灰度图配合二值化操作(阈值约128)就能完美解决问题,此时无需引入彩色信息,处理速度也更快。