第三章 图像基础与色彩空间:RGB、BGR、YUV、HSV、灰度图转换原理、色彩空间转换的GPU加速思路
各位同学,今天我们来聊聊图像处理中最基础、也最绕不开的话题——色彩空间。
说实话,我刚入行那会儿,觉得色彩空间不就是RGB嘛,红绿蓝三个通道,有啥好学的?直到我在一个嵌入式项目中,把YUV数据当RGB直接显示,结果屏幕上全是诡异的紫色条纹……嗯,那次教训挺深刻的。
3.1 为什么需要不同的色彩空间?
你想想看,人眼看颜色和摄像头“看”颜色,完全是两码事。RGB适合显示器,但压缩效率低。YUV适合视频传输,因为人眼对亮度更敏感。HSV适合做图像分割,因为色调和饱和度更直观。
说白了,不同的色彩空间,就是为不同的任务准备的“方言”。
3.2 RGB与BGR:谁在前谁在后?
RGB是标准的红绿蓝顺序。但OpenCV默认用的是BGR,也就是蓝绿红。我第一次用OpenCV读图,然后直接传给一个RGB格式的显示库,结果图像偏蓝得离谱。排查了半天,才发现是通道顺序搞反了。
转换其实很简单:
// 伪代码:RGB ↔ BGR 转换
for (int i = 0; i < width * height; i++) {
unsigned char tmp = data[i * 3 + 0]; // R
data[i * 3 + 0] = data[i * 3 + 2]; // B
data[i * 3 + 2] = tmp; // R
}
在GPU上,这个操作可以用一个简单的kernel并行完成。每个线程处理一个像素,效率极高。
3.3 YUV:视频领域的“老大哥”
YUV把亮度(Y)和色度(U、V)分开。人眼对亮度变化敏感,对颜色变化不敏感。所以视频压缩时,可以降低U、V的分辨率,这就是所谓的“色度子采样”。
常见的格式有:
| 格式 | 描述 | 常见场景 |
|---|---|---|
| YUV420 | 每4个Y共享一组UV | H.264/H.265视频编码 |
| YUV422 | 每2个Y共享一组UV | 专业视频采集 |
| YUV444 | 每个像素都有独立的YUV | 高质量图像处理 |
YUV转RGB的公式,我建议你直接背下来:
R = Y + 1.402 * (V - 128)
G = Y - 0.344 * (U - 128) - 0.714 * (V - 128)
B = Y + 1.772 * (U - 128)
3.4 HSV:做图像分割的利器
HSV把颜色分解为色调(Hue)、饱和度(Saturation)和明度(Value)。
为什么说它适合做分割?因为你可以直接根据H值来筛选颜色范围。比如要检测红色物体,只需要判断H在0~10度或340~360度之间即可。这在RGB空间里很难做到,因为红色在RGB中可能是(255,0,0),也可能是(200,50,30)。
转换公式稍微复杂一点:
// RGB → HSV 核心逻辑
float r, g, b; // 归一化到[0,1]
float max = max(r, g, b);
float min = min(r, g, b);
float delta = max - min;
// 计算H
if (delta == 0) H = 0;
else if (max == r) H = 60 * ((g - b) / delta + 0);
else if (max == g) H = 60 * ((b - r) / delta + 2);
else H = 60 * ((r - g) / delta + 4);
if (H < 0) H += 360;
// 计算S
S = (max == 0) ? 0 : delta / max;
// 计算V
V = max;
在GPU上,这个转换需要处理分支语句。我建议用条件赋值代替if-else,避免线程束发散。
3.5 灰度图:最简单的降维
灰度图就是把彩色图像变成单通道的亮度图。常用的加权公式是:
Gray = 0.299 * R + 0.587 * G + 0.114 * B
为什么权重不一样?因为人眼对绿色最敏感,对蓝色最不敏感。这个权重是ITU-R BT.601标准规定的。
3.6 色彩空间转换的GPU加速思路
好了,前面讲了各种转换的原理。现在聊聊怎么在GPU上加速。
核心思路就一句话:每个像素独立计算,并行执行。
具体来说:
- 数据布局:确保输入输出数据在显存中连续排列。比如RGB格式,最好用平面格式(Planar)而不是交错格式(Interleaved),这样GPU的缓存命中率更高。
- 线程映射:每个线程处理一个像素。对于大图像,可以用二维线程块,每个块处理一个tile。
- 常量内存:把转换系数(比如YUV转RGB的矩阵)放到常量内存中,所有线程共享,减少寄存器压力。
- 向量化加载:用float4或uchar4一次加载4个像素,提高内存带宽利用率。
我曾经在一个项目中,把YUV420转RGB的CPU实现改成GPU实现,速度提升了30倍。关键就在于用向量化加载和共享内存来减少全局内存访问次数。
3.7 总结与建议
色彩空间转换是图像处理流水线的“第一关”。我的建议是:
- 先搞清楚你的数据来源是什么格式(摄像头、视频文件、网络流)
- 再确定目标格式(显示、编码、分析)
- 最后选择合适的转换算法和加速策略
在嵌入式GPU上,内存带宽往往是瓶颈。所以尽量用定点数代替浮点数,用向量化加载代替标量加载。这些细节,决定了你的流水线能不能跑满帧率。
下一章,我们会深入聊聊图像滤波与卷积操作。到时候你会发现,色彩空间转换只是热身运动。