3、色彩空间基础:RGB、YUV、HSV色彩空间的定义与转换原理
做安防监控图像处理这几年,我越来越觉得色彩空间是绕不开的基础。说白了,摄像头拍下来的原始数据,和我们人眼感知到的颜色,中间差了好几个「翻译层」。你如果不懂这些空间怎么定义、怎么转换,那白平衡调优基本就是瞎调。
这一章,我就把 RGB、YUV、HSV 这三个最常用的色彩空间掰开揉碎讲清楚。嗯,咱们不搞纯数学推导,重点放在「为什么安防场景要用这个空间」以及「实际转换时有哪些坑」。
3.1 RGB 色彩空间:最原始,也最直接
RGB 就是红、绿、蓝三个通道。每个像素用三个数值表示,范围通常是 0-255(8bit)或 0-1023(10bit)。
为什么安防摄像头都用 RGB 作为原始输出? 因为 CMOS 传感器上的滤色片就是 Bayer 阵列,每个像素只采集一种颜色,然后通过插值算法补全成 RGB。所以 RGB 是「最接近硬件」的表达方式。
关键点: RGB 的三个通道是高度相关的。光照一变,三个值一起变。这就是为什么直接在 RGB 空间做白平衡很麻烦——你调一个通道,另外两个也得跟着调。
我在项目中遇到过一个问题:某款 IPC 在黄昏场景下,RGB 三个通道的数值分布完全乱掉,R 通道饱和、B 通道几乎为零。如果直接在 RGB 空间做白平衡,怎么调都偏色。后来我意识到,得换个空间处理。
3.2 YUV 色彩空间:亮度和颜色分开
YUV 的核心思想很简单:把亮度(Y)和色度(U、V)分开。Y 只代表明暗,U 和 V 代表颜色偏移。
为什么安防行业这么喜欢 YUV?两个原因:
- 压缩友好: 人眼对亮度敏感、对色度不敏感。所以 Y 可以保留高分辨率,U 和 V 可以降采样(比如 4:2:0),带宽直接省一半。
- 白平衡好做: 白平衡本质是调整色温,也就是调整 U、V 的偏移量。Y 通道基本不动,计算量小很多。
我记得第一次调 YUV 空间的 AWB 算法时,踩过一个坑:YUV 的转换矩阵是有标准版本的。BT.601 和 BT.709 的系数不一样。如果你用错矩阵,颜色会整体偏绿或偏紫。
| 标准 | 应用场景 | Y 计算公式(简化) |
|---|---|---|
| BT.601 | 标清视频(SD) | Y = 0.299R + 0.587G + 0.114B |
| BT.709 | 高清视频(HD) | Y = 0.2126R + 0.7152G + 0.0722B |
| BT.2020 | 超高清(4K/8K) | Y = 0.2627R + 0.6780G + 0.0593B |
避坑指南: 我曾经在移植算法时,把 BT.601 的系数用到了 4K 摄像头上。结果画面整体偏黄,查了两天才发现是标准不匹配。所以,拿到摄像头的第一件事,确认它用的是哪个色彩标准。
3.3 HSV 色彩空间:更接近人眼的直觉
HSV 把颜色拆成三个维度:色相(Hue)、饱和度(Saturation)、明度(Value)。
- H(色相): 0°-360°,决定是什么颜色(红、绿、蓝等)。
- S(饱和度): 0%-100%,决定颜色有多浓。
- V(明度): 0%-100%,决定颜色有多亮。
你想想看,人眼判断「这个红色偏不偏」时,其实是在看 H 值有没有偏移。而不是像 RGB 那样同时看 R、G、B 三个数。所以 HSV 在安防场景里特别适合做:
- 颜色识别: 比如车牌颜色、指示灯状态。
- 白平衡评估: 通过统计 H 通道的分布,判断是否有色偏。
我个人习惯在做 AWB 调优时,先用 HSV 空间做一次「色偏诊断」。如果 H 通道的直方图峰值不在预期范围内,那基本可以断定白平衡没调好。
3.4 三种空间的转换关系
实际项目中,我们经常需要在三个空间之间来回切换。下面这张图是我自己总结的转换流程,画出来方便你理解。
3.5 转换代码实战
光讲理论不够,我直接给你一段实际项目中用过的转换代码。这是 C 语言版本,安防嵌入式设备上跑得很快。
// RGB 转 YUV (BT.601 标准)
void rgb_to_yuv(uint8_t r, uint8_t g, uint8_t b,
uint8_t *y, uint8_t *u, uint8_t *v) {
*y = (uint8_t)(0.299f * r + 0.587f * g + 0.114f * b);
*u = (uint8_t)(-0.147f * r - 0.289f * g + 0.436f * b + 128);
*v = (uint8_t)(0.615f * r - 0.515f * g - 0.100f * b + 128);
}
// RGB 转 HSV
void rgb_to_hsv(uint8_t r, uint8_t g, uint8_t b,
float *h, float *s, float *v) {
float rf = r / 255.0f, gf = g / 255.0f, bf = b / 255.0f;
float max = fmaxf(rf, fmaxf(gf, bf));
float min = fminf(rf, fminf(gf, bf));
float delta = max - min;
*v = max;
*s = (max == 0) ? 0 : delta / max;
if (delta == 0) {
*h = 0;
} else if (max == rf) {
*h = 60.0f * fmodf((gf - bf) / delta, 6.0f);
} else if (max == gf) {
*h = 60.0f * ((bf - rf) / delta + 2.0f);
} else {
*h = 60.0f * ((rf - gf) / delta + 4.0f);
}
if (*h < 0) *h += 360.0f;
}
小技巧: 在嵌入式平台上,浮点运算很慢。我建议你把转换系数提前算成定点数,用移位和整数乘法代替浮点。比如 0.299 可以近似为 77/256,用 (r * 77) >> 8 代替。
3.6 安防场景下的选择建议
最后,我根据实际项目经验,给你一个简单的选择指南:
| 场景 | 推荐空间 | 原因 |
|---|---|---|
| 白平衡调整 | YUV | 亮度色度分离,调整 UV 不影响亮度 |
| 颜色偏色诊断 | HSV | H 通道直接反映色偏方向 |
| 视频编码传输 | YUV | 兼容 H.264/H.265 压缩标准 |
| 目标颜色识别 | HSV | 对光照变化不敏感,鲁棒性好 |
| 原始传感器数据 | RGB | 最接近硬件输出,信息完整 |