RGB与YUV像素格式详解
RGB与YUV像素格式详解摄像头预览发绿、H.264 编码前不知道用 NV12 还是 I420、RGB 转 YUV 后整体偏色——多数不是「公式背错」而是像素格式名没对齐采样方式、平面排布、色域矩阵、量化范围、行跨距里少对齐一项就会踩坑。显示器与图像处理常用RGB摄像头采集、视频编解码、硬件加速缓冲里主流是YCbCr口语常叫 YUV。一条链路可以记成场景选择 RGB 或 YCbCr → 色度采样(4:2:0 等) → 平面/半平面/打包存储(I420/NV12…) → 字节写入内存/文件下文按这条链展开先 RGB 与 YCbCr 各自干什么再讲4:2:0 采样与 I420/NV12 排布然后是RGB↔YCbCr 转换、色域与范围、stride最后给FFmpeg 像素名与 ffplay 验证。目录一、RGB 与 YUV 为什么共存1. 为什么视频链路偏爱 YCbCr2. RGB显示侧的像素打包3. YCbCr亮度与色度分离二、YUV 采样与存储4. 色度采样4:4:4 到 4:2:05. 存储方式Planar、Semi-Planar、Packed6. 帧大小怎么算7. 4:2:0 采样与 I420、NV12 内存布局8. 名字别混YUV420、I420、NV12三、RGB↔YCbCr 转换9. RGB 与 YCbCr 的相互转换10. 色域矩阵BT.601、BT.709、BT.202011. Limited Range 与 Full Range四、工程实践12. Stride行跨距13. FFmpeg 像素格式与 ffplay 验证14. 场景与格式速查15. 工程里常见现象16. 延伸阅读1. 为什么视频链路偏爱 YCbCr维度RGBYCbCr常说 YUV信息组织R、G、B 捆绑亮度与颜色难分离Y表亮度Cb/Cr或 U/V表色度数据量每像素常 34 字节难再减可对色度降采样4:2:0 约1.5 字节/像素典型场景屏幕、OpenGL 纹理、未压缩 BMP摄像头、MediaCodec、x264/x265、多数raw .yuv人眼对亮度变化更敏感、对色度细节较弱因此视频在保持观感的前提下少传 U/V带宽和存储都更划算。RGB 仍用于显示最后一跳GPU 合成、窗口上屏中间处理与编码几乎总是 YCbCr 家族。摄像头 / 解码输出NV12 / I420H.264 / H.265解码转 RGB显示 / OpenCV 绘图更上层的编码、封装、播放流程见从播放流程到技术演进-音视频编解码与封装格式详解Android 侧硬解缓冲见Android_MediaCodec架构与实现解析。2. RGB显示侧的像素打包RGB 用红、绿、蓝三通道比例表示颜色亮度和色度绑在一起适合做显示不适合再做大幅度色度压缩。2.1 常见 RGB 存储格式位宽布局概念说明RGB56516 bitR5 G6 B5嵌入式屏、游戏纹理常见RGB55516 bit1 保留 R5 G5 B5较少见RGB2424 bitR8 G8 B8无 Alphawidth×height×3字节ARGB8888 / RGBA888832 bitA8 R8 G8 B8带透明通道ARGB444416 bitA4 R4 G4 B4省内存 UI2.2 字节序RGBA 与 BGRA小端 CPU 上源码里的0xAARRGGBB写入内存后低地址往往先出现B、G、R、Auint32_t pixel 0xFF112233; // 常写成 ARGB 内存低地址 → 33 22 11 FF // 实际字节顺序多为 BGRA生态常见顺序Windows GDI、部分 OpenCV 默认BGR / BGRAOpenGL、PNG、Web CanvasRGB / RGBA对接 API 时以文档与实测为准不要只看结构体字段名。3. YCbCr亮度与色度分离数字视频标准里写的是YCbCrITU-R BT.601 / BT.709 等YUV来自模拟电视术语工程口语混用下文在涉及矩阵与范围时写YCbCr。分量含义Y亮度Luma可看作「灰度强弱」Cb / Cr色度Chroma也常记U、V指代两个色差方向亮度与色度分开后可以只对Y做锐化、降噪对Cb/Cr做4:2:0等降采样在几乎不损观感的前提下减半以上色度数据。4. 色度采样4:4:4 到 4:2:0「4:2:0」描述的是色度在水平、垂直方向相对亮度的采样密度不是文件里字节怎么排那是下一节的事。采样含义直观约字节/像素4:4:4每个像素都有完整 Y、U、V34:2:2水平方向色度减半24:1:1水平每 4 像素共用一组 UV1.5较少见4:2:0水平、垂直各减半 →2×2 块共用一组 UV1.54:2:0是H.264/H.265、摄像头、软编里最常遇到的采样4:2:2见于部分采集卡、YUY2等 packed 格式。4.1 4×4 块上的 4:2:0示意像素网格每格一个亮度采样点 ┌───┬───┬───┬───┐ │ │ │ │ │ 每个格子都有 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,VY 平面 4×4 16 采样每像素 1 个 Y5. 存储方式Planar、Semi-Planar、Packed同一套4:2:0 采样字节在内存里还有三种常见排法类型排布方式典型名字Planar平面先整块 Y再整块 U再整块 VI420YU12、YV12Semi-Planar半平面先整块 Y再UV 交错NV12UVUV…、NV21VUVU…Packed打包像素级交错如 Y0 U0 Y1 V0…YUY24:2:2 packed平台/组件常见格式Android Camera / MediaCodecNV12、NV21FFmpeg 软解默认 planarYUV420PI420部分 Windows 采集YUY2完整像素格式名 采样 存储。例如I420 4:2:0 采样 PlanarNV12 4:2:0 Semi-Planar。6. 帧大小怎么算设宽W、高H像素格式大小字节RGB24W × H × 3RGBA8888W × H × 4YUV 4:2:0任意 planar/sp采样相同W × H × 3 / 2或W × H × 1.5例1280×720RGB241280 × 720 × 3≈2.64 MBYUV4201280 × 720 × 1.5≈1.38 MB未编码的 raw 帧按上式估算H.264 码流还要再乘编码压缩比与 raw 未压缩像素布局不是同一层概念。7. 4:2:0 采样与 I420、NV12 内存布局在4×4示例上设 Y 为Y00…Y33UV 为U0 V0 … U3 V3。7.1 I420PlanarFFmpeg 称yuv420p[ Y00 Y01 Y02 Y03 Y10 … Y33 ] ← 16 字节 Y [ U0 U1 U2 U3 ] ← 4 字节 U [ V0 V1 V2 V3 ] ← 4 字节 V7.2 NV12Semi-Planar[ Y00 … Y33 ] [ U0 V0 U1 V1 U2 V2 U3 V3 ]7.3 YV12与 I420 相同采样但V 平面在 U 前面Y→V→U。对接解码器、硬编输入时要逐格式区分不能只看「都是 420」。8. 名字别混YUV420、I420、NV12说法实际指什么YUV420 / 4:2:0只说明色度采样比例I420 / YU124:2:0 PlanarY、U、V 分平面NV12 / NV214:2:0 Semi-PlanarUV 或 VU 交错YUY2多为4:2:2 Packed不是 4:2:0排查日志时应问采样是 420 还是 422存储是 I420 还是 NV12两项都对了再谈转换矩阵。域选型实践采集 → 解码 → 滤镜/编码尽量全程留在YUV/YCbCrNV12、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 → YCbCrLimitedBT.601voidRGBtoYCbCr601Limited(uint8_tr,uint8_tg,uint8_tb,uint8_t*y,uint8_t*cb,uint8_t*cr){*ystatic_castuint8_t(((66*r129*g25*b128)8)16);*cbstatic_castuint8_t(((-38*r-74*g112*b128)8)128);*crstatic_castuint8_t(((112*r-94*g-18*b128)8)128);}9.2 YCbCr → RGBLimitedBT.601voidYCbCr601LimitedToRGB(uint8_ty,uint8_tcb,uint8_tcr,uint8_t*r,uint8_t*g,uint8_t*b){intCy-16;intDcb-128;intEcr-128;intR(298*C409*E128)8;intG(298*C-100*D-208*E128)8;intB(298*C516*D128)8;autoclip[](intv)-uint8_t{if(v0)return0;if(v255)return255;returnstatic_castuint8_t(v);};*rclip(R);*gclip(G);*bclip(B);}JPEG图像常用Full Range0255的 YCbCr偏移与系数不同把 JPEG 的 YCbCr 按 Limited 公式转 RGB会发灰或对比度异常。10. 色域矩阵BT.601、BT.709、BT.2020RGB → YCbCr 的系数矩阵随标准变化标准典型用途场景举例BT.601SD老标清、部分摄像头默认BT.709HD720p/1080p 视频BT.2020UHD / HDR4K/HDR色域更大与HDR_Vivid技术介绍等专题相关编码器VUI里可声明矩阵与范围播放器按声明还原。若用 601 矩阵编码、按 709 矩阵显示或相反画面会整体偏绿或偏红且难以用简单亮度调节修回来。11. Limited Range 与 Full Range范围Y 常见区间Cb/Cr 常见区间常见场景LimitedTV1623516240视频编解码、摄像头默认FullPC02550255JPEG、部分截图/游戏纹理RGB 帧缓冲多为Full视频 YCbCr 多为Limited。转换时矩阵和范围要成对x264/x265 等可用参数指定input-range / range以具体版本文档为准。12. Stride行跨距width是有效像素宽strideFFmpeg 里常叫linesize是相邻两行起始地址的字节差常≥ 有效行宽。12.1 为何存在 stride摄像头、GPU、DMA 常要求每行 16/32/64 字节对齐于是stride_y width stride_uv width / 2 4:2:0 时只按width连续读width×height字节会把每行尾部 padding 当成下一行开头出现画面倾斜、绿条、错位。12.2 按行拷贝I420伪代码// 拷贝 Y 平面for(introw0;rowheight;row){memcpy(dst_yrow*dst_stride_y,src_yrow*src_stride_y,width);}// U/V 平面高度为 height/2宽度 width/2同理按 stride_uv 逐行拷从MediaCodecImage/ FFmpegAVFrame取数据时永远用linesize[i]做行进不要假设linesize width。13. FFmpeg 像素格式与 ffplay 验证13.1 常用AVPixelFormatFFmpeg 名称含义AV_PIX_FMT_RGB24packed RGBAV_PIX_FMT_BGRApacked BGRAAV_PIX_FMT_YUV420PI420planarAV_PIX_FMT_YUVJ420PI420 Full Range旧接口新项目注意弃用路径AV_PIX_FMT_NV12Y 交错 UVAV_PIX_FMT_NV21Y 交错 VU软编滤镜、x264 输入多为yuv420pAndroid 硬解表面多为NV12。13.2 用 ffplay 看 raw 文件生成或截取一帧 raw 后可直接预览尺寸与格式必须一致# I4201280x720ffplay-frawvideo-video_size1280x720-pixel_formatyuv420p frame.yuv# RGB24ffplay-frawvideo-video_size1280x720-pixel_formatrgb24 frame.rgb画面正常说明采样、存储、宽高声明正确异常条纹优先查格式名是否写错、stride 是否忽略。14. 场景与格式速查场景常见格式备注手机摄像头 / MediaCodecNV12、NV21以机型与 API 文档为准x264/x265、FFmpeg 软编滤镜I420yuv420pPlanar 4:2:0FFmpeg 硬解 / 部分 GPU 路径NV12与 I420 采样相同存储不同Windows 部分采集卡YUY2多为 4:2:2 PackedOpenGL / 窗口上屏RGBA、BGRA注意字节序JPEG / 截图算法Full Range YCbCr勿按视频 Limited 公式硬转raw 文件自测yuv420p/rgb24ffplay宽高、格式名必须一致硬编输入若要求 NV12而手里是 I420应做格式转换或让解码/采集直接输出目标格式不要只改枚举名。15. 工程里常见现象现象优先排查整体偏绿/偏红BT.601 vs BT.709 矩阵Limited vs Full 混用右侧斜纹、周期性色块stride按 width 算未用 linesizeNV12 当 I420 解U/V 平面顺序或交错方式错误RGB 正常、送编码器后色怪送入的是 RGB 却声明为 YUV或矩阵不对只有部分机型异常该机 Camera 输出NV21而非 NV1216. 延伸阅读从播放流程到技术演进-音视频编解码与封装格式详解— 编解码、封装与播放链路。Android_MediaCodec架构与实现解析— 硬解缓冲与 Surface 格式。ITU-RBT.601 / BT.709 / BT.2020与 FFmpegpixel formats官方说明。矩阵系数、FFmpeg 枚举名与range标志以所用FFmpeg / 硬件 SDK 版本文档为准升级库后应回归ffplay 单帧 raw做一次颜色与布局核对。一句话搞清 RGB/YUV本质是搞清谁在用、怎么采、怎么存、怎么转、行跨距有没有对齐。