RGB与YUV像素格式详解

RGB与YUV像素格式详解

摄像头预览发绿、H.264 编码前不知道用 NV12 还是 I420、RGB 转 YUV 后整体偏色------多数不是「公式背错」,而是 像素格式名没对齐:采样方式、平面排布、色域矩阵、量化范围、行跨距里少对齐一项就会踩坑。

显示器与图像处理常用 RGB ;摄像头采集、视频编解码、硬件加速缓冲里主流是 YCbCr(口语常叫 YUV)。一条链路可以记成:

text 复制代码
场景选择 RGB 或 YCbCr → 色度采样(4:2:0 等) → 平面/半平面/打包存储(I420/NV12...) → 字节写入内存/文件

下文按这条链展开:先 RGB 与 YCbCr 各自干什么 ,再讲 4:2:0 采样与 I420/NV12 排布 ,然后是 RGB↔YCbCr 转换、色域与范围、stride ,最后给 FFmpeg 像素名与 ffplay 验证


目录

一、RGB 与 YUV 为什么共存

  • [1. 为什么视频链路偏爱 YCbCr](#1. 为什么视频链路偏爱 YCbCr)
  • [2. RGB:显示侧的像素打包](#2. RGB:显示侧的像素打包)
  • [3. YCbCr:亮度与色度分离](#3. YCbCr:亮度与色度分离)

二、YUV 采样与存储

  • [4. 色度采样:4:4:4 到 4:2:0](#4. 色度采样:4:4:4 到 4:2:0)
  • [5. 存储方式:Planar、Semi-Planar、Packed](#5. 存储方式:Planar、Semi-Planar、Packed)
  • [6. 帧大小怎么算](#6. 帧大小怎么算)
  • [7. 4:2:0 采样与 I420、NV12 内存布局](#7. 4:2:0 采样与 I420、NV12 内存布局)
  • [8. 名字别混:YUV420、I420、NV12](#8. 名字别混:YUV420、I420、NV12)

三、RGB↔YCbCr 转换

  • [9. RGB 与 YCbCr 的相互转换](#9. RGB 与 YCbCr 的相互转换)
  • [10. 色域矩阵:BT.601、BT.709、BT.2020](#10. 色域矩阵:BT.601、BT.709、BT.2020)
  • [11. Limited Range 与 Full Range](#11. Limited Range 与 Full Range)

四、工程实践

  • [12. Stride(行跨距)](#12. Stride(行跨距))
  • [13. FFmpeg 像素格式与 ffplay 验证](#13. FFmpeg 像素格式与 ffplay 验证)
  • [14. 场景与格式速查](#14. 场景与格式速查)
  • [15. 工程里常见现象](#15. 工程里常见现象)
  • [16. 延伸阅读](#16. 延伸阅读)

1. 为什么视频链路偏爱 YCbCr

维度 RGB YCbCr(常说 YUV)
信息组织 R、G、B 捆绑,亮度与颜色难分离 Y 表亮度,Cb/Cr(或 U/V)表色度
数据量 每像素常 3~4 字节,难再减 可对色度 降采样 ,4:2:0 约 1.5 字节/像素
典型场景 屏幕、OpenGL 纹理、未压缩 BMP 摄像头、MediaCodec、x264/x265、多数 raw .yuv

人眼对 亮度变化 更敏感、对 色度细节 较弱,因此视频在保持观感的前提下 少传 U/V ,带宽和存储都更划算。RGB 仍用于 显示最后一跳(GPU 合成、窗口上屏),中间处理与编码几乎总是 YCbCr 家族。
摄像头 / 解码输出

NV12 / I420
H.264 / H.265
解码
转 RGB

显示 / OpenCV 绘图

更上层的 编码、封装、播放流程从播放流程到技术演进-音视频编解码与封装格式详解Android 侧硬解缓冲Android_MediaCodec架构与实现解析


2. RGB:显示侧的像素打包

RGB 用红、绿、蓝三通道比例表示颜色,亮度和色度绑在一起,适合做显示,不适合再做大幅度色度压缩。

2.1 常见 RGB 存储

格式 位宽 布局(概念) 说明
RGB565 16 bit R5 G6 B5 嵌入式屏、游戏纹理常见
RGB555 16 bit 1 保留 + R5 G5 B5 较少见
RGB24 24 bit R8 G8 B8 无 Alpha;width×height×3 字节
ARGB8888 / RGBA8888 32 bit A8 R8 G8 B8 带透明通道
ARGB4444 16 bit A4 R4 G4 B4 省内存 UI

2.2 字节序:RGBA 与 BGRA

小端 CPU 上,源码里的 0xAARRGGBB 写入内存后,低地址往往先出现 B、G、R、A

text 复制代码
uint32_t pixel = 0xFF112233;  // 常写成 ARGB
内存低地址 → 33 22 11 FF     // 实际字节顺序多为 BGRA
生态 常见顺序
Windows GDI、部分 OpenCV 默认 BGR / BGRA
OpenGL、PNG、Web Canvas RGB / RGBA

对接 API 时以 文档与实测 为准,不要只看结构体字段名。


3. YCbCr:亮度与色度分离

数字视频标准里写的是 YCbCr (ITU-R BT.601 / BT.709 等),YUV 来自模拟电视术语,工程口语混用,下文在涉及矩阵与范围时写 YCbCr

分量 含义
Y 亮度(Luma),可看作「灰度强弱」
Cb / Cr 色度(Chroma);也常记 U、V 指代两个色差方向

亮度与色度分开后,可以:

  • 只对 Y 做锐化、降噪;
  • Cb/Cr4:2:0 等降采样,在几乎不损观感的前提下 减半以上色度数据

4. 色度采样:4:4:4 到 4:2:0

4:2:0 」描述的是 色度在水平、垂直方向相对亮度的采样密度,不是文件里字节怎么排(那是下一节的事)。

采样 含义(直观) 约字节/像素
4:4:4 每个像素都有完整 Y、U、V 3
4:2:2 水平方向色度减半 2
4:1:1 水平每 4 像素共用一组 UV 1.5(较少见)
4:2:0 水平、垂直各减半 → 2×2 块共用一组 UV 1.5

4:2:0H.264/H.265、摄像头、软编 里最常遇到的采样;4:2:2 见于部分采集卡、YUY2 等 packed 格式。

4.1 4×4 块上的 4:2:0(示意)

text 复制代码
像素网格(每格一个亮度采样点):
┌───┬───┬───┬───┐
│   │   │   │   │  每个格子都有 Y(共 16 个 Y)
├───┼───┼───┼───┤
│   │   │   │   │
├───┼───┼───┼───┤
│   │   │   │   │
├───┼───┼───┼───┤
│   │   │   │   │
└───┴───┴───┴───┘

色度 U/V:每 2×2 块只保留 1 组 (U,V) → 共 4 组 UV
块0 覆盖左上 2×2,块1 右上,块2 左下,块3 右下

UV 平面 2×2 = 4 组
每 2×2 共享 1 组 U,V
Y 平面 4×4 = 16 采样
每像素 1 个 Y


5. 存储方式:Planar、Semi-Planar、Packed

同一套 4:2:0 采样,字节在内存里还有三种常见排法:

类型 排布方式 典型名字
Planar(平面) 先整块 Y,再整块 U,再整块 V I420 (YU12)、YV12
Semi-Planar(半平面) 先整块 Y,再 UV 交错 NV12 (UVUV...)、NV21(VUVU...)
Packed(打包) 像素级交错,如 Y0 U0 Y1 V0... YUY2(4:2:2 packed)
平台/组件 常见格式
Android Camera / MediaCodec NV12NV21
FFmpeg 软解默认 planar YUV420P(I420)
部分 Windows 采集 YUY2

完整像素格式名 = 采样 + 存储 。例如 I420 = 4:2:0 采样 + PlanarNV12 = 4:2:0 + Semi-Planar


6. 帧大小怎么算

设宽 W、高 H(像素):

格式 大小(字节)
RGB24 W × H × 3
RGBA8888 W × H × 4
YUV 4:2:0(任意 planar/sp,采样相同) W × H × 3 / 2W × H × 1.5

例:1280×720

  • RGB24:1280 × 720 × 32.64 MB
  • YUV420:1280 × 720 × 1.51.38 MB

未编码的 raw 帧按上式估算;H.264 码流 还要再乘编码压缩比,与 raw 未压缩像素布局不是同一层概念。


7. 4:2:0 采样与 I420、NV12 内存布局

4×4 示例上,设 Y 为 Y00...Y33,UV 为 U0 V0 ... U3 V3

7.1 I420(Planar,FFmpeg 称 yuv420p

text 复制代码
[ Y00 Y01 Y02 Y03 Y10 ... Y33 ]   ← 16 字节 Y
[ U0  U1  U2  U3  ]             ← 4 字节 U
[ V0  V1  V2  V3  ]             ← 4 字节 V

7.2 NV12(Semi-Planar)

text 复制代码
[ Y00 ... Y33 ]
[ U0 V0  U1 V1  U2 V2  U3 V3 ]

7.3 YV12

与 I420 相同采样,但 V 平面在 U 前面YVU)。对接解码器、硬编输入时要 逐格式区分,不能只看「都是 420」。


8. 名字别混:YUV420、I420、NV12

说法 实际指什么
YUV420 / 4:2:0 只说明 色度采样比例
I420 / YU12 4:2:0 + Planar(Y、U、V 分平面)
NV12 / NV21 4:2:0 + Semi-Planar(UV 或 VU 交错)
YUY2 多为 4:2:2 Packed,不是 4:2:0

排查日志时,应问:采样是 420 还是 422?存储是 I420 还是 NV12? 两项都对了,再谈转换矩阵。

域选型(实践) :采集 → 解码 → 滤镜/编码尽量全程留在 YUV/YCbCr (NV12、I420),少做 RGB↔YUV 往返;只在 上屏、OpenCV 绘图、UI 合成 等必须走显示管线时再转 RGB。每多一次转换,就多一次矩阵、range、stride 对齐风险。


9. RGB 与 YCbCr 的相互转换

显示前常要把解码出的 YUV420 转成 RGB24 ;自绘 OSD 或算法在 RGB 域做完,再喂给编码器时要做反向转换。矩阵与偏移必须和 整条链路约定一致(见 §10、§11)。

下面给出 8 bit、BT.601、Limited Range(电视范围) 的整数实现,与许多教材、入门博文一致;HD 内容 更常用 BT.709 系数(结构相同,系数不同,需查 ITU-R 表)。

9.1 RGB → YCbCr(Limited,BT.601)

cpp 复制代码
void RGBtoYCbCr601Limited(uint8_t r, uint8_t g, uint8_t b,
                          uint8_t* y, uint8_t* cb, uint8_t* cr)
{
    *y  = static_cast<uint8_t>((( 66 * r + 129 * g +  25 * b + 128) >> 8) + 16);
    *cb = static_cast<uint8_t>(((-38 * r -  74 * g + 112 * b + 128) >> 8) + 128);
    *cr = static_cast<uint8_t>(((112 * r -  94 * g -  18 * b + 128) >> 8) + 128);
}

9.2 YCbCr → RGB(Limited,BT.601)

cpp 复制代码
void YCbCr601LimitedToRGB(uint8_t y, uint8_t cb, uint8_t cr,
                          uint8_t* r, uint8_t* g, uint8_t* b)
{
    int C = y - 16;
    int D = cb - 128;
    int E = cr - 128;

    int R = (298 * C + 409 * E + 128) >> 8;
    int G = (298 * C - 100 * D - 208 * E + 128) >> 8;
    int B = (298 * C + 516 * D + 128) >> 8;

    auto clip = [](int v) -> uint8_t {
        if (v < 0) return 0;
        if (v > 255) return 255;
        return static_cast<uint8_t>(v);
    };
    *r = clip(R);
    *g = clip(G);
    *b = clip(B);
}

JPEG 图像常用 Full Range(0~255) 的 YCbCr,偏移与系数不同;把 JPEG 的 YCbCr 按 Limited 公式转 RGB,会 发灰或对比度异常


10. 色域矩阵:BT.601、BT.709、BT.2020

RGB → YCbCr 的系数矩阵随 标准 变化:

标准 典型用途 场景举例
BT.601 SD 老标清、部分摄像头默认
BT.709 HD 720p/1080p 视频
BT.2020 UHD / HDR 4K/HDR;色域更大,与 HDR_Vivid技术介绍 等专题相关

编码器 VUI 里可声明矩阵与范围;播放器按声明还原。若 用 601 矩阵编码、按 709 矩阵显示 (或相反),画面会 整体偏绿或偏红,且难以用简单亮度调节修回来。


11. Limited Range 与 Full Range

范围 Y 常见区间 Cb/Cr 常见区间 常见场景
Limited(TV) 16~235 16~240 视频编解码、摄像头默认
Full(PC) 0~255 0~255 JPEG、部分截图/游戏纹理

RGB 帧缓冲多为 Full ;视频 YCbCr 多为 Limited 。转换时 矩阵和范围要成对 ;x264/x265 等可用参数指定 input-range / range(以具体版本文档为准)。


12. Stride(行跨距)

width 是有效像素宽;stride (FFmpeg 里常叫 linesize)是 相邻两行起始地址的字节差 ,常 ≥ 有效行宽

12.1 为何存在 stride

摄像头、GPU、DMA 常要求 每行 16/32/64 字节对齐,于是:

text 复制代码
stride_y   >= width
stride_uv  >= width / 2   (4:2:0 时)

只按 width 连续读 width×height 字节,会 把每行尾部 padding 当成下一行开头 ,出现 画面倾斜、绿条、错位

12.2 按行拷贝(I420,伪代码)

cpp 复制代码
// 拷贝 Y 平面
for (int row = 0; row < height; ++row) {
    memcpy(dst_y + row * dst_stride_y,
           src_y + row * src_stride_y,
           width);
}
// U/V 平面高度为 height/2,宽度 width/2,同理按 stride_uv 逐行拷

MediaCodec Image / FFmpeg AVFrame 取数据时,永远用 linesize[i] 做行进,不要假设 linesize == width


13. FFmpeg 像素格式与 ffplay 验证

13.1 常用 AVPixelFormat

FFmpeg 名称 含义
AV_PIX_FMT_RGB24 packed RGB
AV_PIX_FMT_BGRA packed BGRA
AV_PIX_FMT_YUV420P I420 planar
AV_PIX_FMT_YUVJ420P I420 + Full Range(旧接口,新项目注意弃用路径)
AV_PIX_FMT_NV12 Y + 交错 UV
AV_PIX_FMT_NV21 Y + 交错 VU

软编滤镜、x264 输入多为 yuv420p ;Android 硬解表面多为 NV12

13.2 用 ffplay 看 raw 文件

生成或截取一帧 raw 后,可直接预览(尺寸与格式必须一致):

bash 复制代码
# I420,1280x720
ffplay -f rawvideo -video_size 1280x720 -pixel_format yuv420p frame.yuv

# RGB24
ffplay -f rawvideo -video_size 1280x720 -pixel_format rgb24 frame.rgb

画面正常说明 采样、存储、宽高 声明正确;异常条纹优先查 格式名是否写错、stride 是否忽略


14. 场景与格式速查

场景 常见格式 备注
手机摄像头 / MediaCodec NV12NV21 以机型与 API 文档为准
x264/x265、FFmpeg 软编滤镜 I420yuv420p Planar 4:2:0
FFmpeg 硬解 / 部分 GPU 路径 NV12 与 I420 采样相同,存储不同
Windows 部分采集卡 YUY2 多为 4:2:2 Packed
OpenGL / 窗口上屏 RGBABGRA 注意字节序
JPEG / 截图算法 Full Range YCbCr 勿按视频 Limited 公式硬转
raw 文件自测 yuv420p / rgb24 + ffplay 宽高、格式名必须一致

硬编输入若要求 NV12,而手里是 I420,应做 格式转换(或让解码/采集直接输出目标格式),不要只改枚举名。


15. 工程里常见现象

现象 优先排查
整体偏绿/偏红 BT.601 vs BT.709 矩阵;Limited vs Full 混用
右侧斜纹、周期性色块 stride 按 width 算,未用 linesize
NV12 当 I420 解 U/V 平面顺序或交错方式错误
RGB 正常、送编码器后色怪 送入的是 RGB 却声明为 YUV,或矩阵不对
只有部分机型异常 该机 Camera 输出 NV21 而非 NV12

16. 延伸阅读

  • 从播放流程到技术演进-音视频编解码与封装格式详解 --- 编解码、封装与播放链路。
  • Android_MediaCodec架构与实现解析 --- 硬解缓冲与 Surface 格式。
  • ITU-R BT.601 / BT.709 / BT.2020 与 FFmpeg pixel formats 官方说明。

矩阵系数、FFmpeg 枚举名与 range 标志以所用 FFmpeg / 硬件 SDK 版本 文档为准;升级库后应回归 ffplay + 单帧 raw 做一次颜色与布局核对。


一句话 :搞清 RGB/YUV,本质是搞清 谁在用、怎么采、怎么存、怎么转、行跨距有没有对齐

相关推荐
水上冰石4 小时前
v1-5-pruned-emaonly.safetensors 搭配mm_sd_v15_v2.ckpt 生成视频,具体操作步骤
stable diffusion·音视频·文生视频
searchforAI4 小时前
我用这款本土NotebookLM平替重构了知识库
人工智能·笔记·gpt·ai·音视频·知识图谱
美狐美颜SDK开放平台5 小时前
美颜SDK开发详解:如何优化美颜SDK在低端安卓机上的性能?
android·ios·音视频·直播美颜sdk·视频美颜sdk
wj30558537819 小时前
课程 6:图生视频首次运行流程
人工智能·音视频
runafterhit19 小时前
显示调研专题-OLED 终端市场分析报告
音视频
天上路人19 小时前
A-59F所有应用模式说明
人工智能·硬件架构·音视频·语音识别·实时音视频
Likeadust1 天前
企业级融媒体生产管理平台/智能会议管理系统EasyDSS构建一体化应急视频指挥体系
音视频·媒体
OsDepK1 天前
AudioSplit音频多轨免费分离工具即将发布
ide·git·python·音视频·集成学习
Highcharts.js1 天前
数学函数双曲线音频图表(y=1/x 双曲线)|图表代码示例
前端·react.js·实时音视频·highcharts·音频图表·双曲线图表