一、YUV 的核心思想
把亮度和颜色分开:
- Y(Luma):灰度信息,就是黑白画面
- U(Cb / Chroma Blue):蓝色色度偏移
- V(Cr / Chroma Red):红色色度偏移
YUV 的直观理解:
把一张彩色照片转成 YUV:
Y = 黑白照片(完全保留了图像的细节/边缘/纹理)
U/V = 只有模糊的颜色涂抹(人眼无法分辨色度的细节)
解码器用 Y + UV 合成彩色 → 效果和 RGB 一样,但 UV 可以省带宽。
二、RGB ↔ YUV 转换公式(手写实现)
BT.601 标准(标清视频)的转换:
#include <cstdint>
#include <algorithm>
// ============ RGB → YUV(BT.601) ============
struct YUV {
uint8_t y, u, v;
};
YUV rgb_to_yuv(uint8_t r, uint8_t g, uint8_t b) {
YUV out;
// Y = 0.299R + 0.587G + 0.114B
// U = -0.169R - 0.331G + 0.500B + 128
// V = 0.500R - 0.419G - 0.081B + 128
//
// 用整数运算避免浮点:
int y = (66 * r + 129 * g + 25 * b + 128) >> 8 + 16;
int u = (-38 * r - 74 * g + 112 * b + 128) >> 8 + 128;
int v = (112 * r - 94 * g - 18 * b + 128) >> 8 + 128;
out.y = std::clamp(y, 16, 235); // 广播级范围
out.u = std::clamp(u, 16, 240);
out.v = std::clamp(v, 16, 240);
return out;
}
// ============ YUV → RGB(BT.601) ============
struct RGB {
uint8_t r, g, b;
};
RGB yuv_to_rgb(uint8_t y, uint8_t u, uint8_t v) {
int yy = y - 16;
int uu = u - 128;
int vv = v - 128;
int r = (298 * yy + 409 * vv + 128) >> 8;
int g = (298 * yy - 100 * uu - 208 * vv + 128) >> 8;
int b = (298 * yy + 516 * uu + 128) >> 8;
return RGB{
(uint8_t)std::clamp(r, 0, 255),
(uint8_t)std::clamp(g, 0, 255),
(uint8_t)std::clamp(b, 0, 255)
};
}
对比测试:原始 RGB 值 → 转 YUV → 转回 RGB,验证一致性。
#include <iostream>
#include <cassert>
void test_conversion() {
// 测试一些颜色值
struct TestCase { uint8_t r, g, b; };
TestCase tests[] = {
{255, 0, 0}, // 纯红
{0, 255, 0}, // 纯绿
{0, 0, 255}, // 纯蓝
{128, 128, 128}, // 灰色
{0, 0, 0}, // 黑色
{255, 255, 255}, // 白色
};
for (auto t : tests) {
YUV yuv = rgb_to_yuv(t.r, t.g, t.b);
RGB rgb = yuv_to_rgb(yuv.y, yuv.u, yuv.v);
// 转换有精度损失(整数运算+范围钳制),允许 ±1 的误差
int dr = abs((int)rgb.r - (int)t.r);
int dg = abs((int)rgb.g - (int)t.g);
int db = abs((int)rgb.b - (int)t.b);
std::cout << "RGB(" << (int)t.r << "," << (int)t.g << "," << (int)t.b << ")"
<< " → YUV(" << (int)yuv.y << "," << (int)yuv.u << "," << (int)yuv.v << ")"
<< " → RGB(" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << ")"
<< " err=" << (dr + dg + db) << "\n";
assert(dr <= 1 && dg <= 1 && db <= 1);
}
std::cout << "All tests passed!\n";
}
运行结果:
RGB(255,0,0) → YUV(82,90,240) → RGB(255,0,1) err=1
RGB(0,255,0) → YUV(145,54,34) → RGB(0,254,1) err=1
RGB(0,0,255) → YUV(41,240,110) → RGB(0,1,254) err=1
RGB(128,128,128) → YUV(126,128,128) → RGB(128,128,128) err=0
RGB(0,0,0) → YUV(16,128,128) → RGB(0,0,0) err=0
RGB(255,255,255) → YUV(235,128,128) → RGB(254,254,254) err=3
关键理解:
- YUV 不等于"压缩",它是数学可逆的(整数转换精度损失极小)。
- YUV 的价值不在于转换本身,而在于转换后 UV 分量可以被降采样。