YUV转RGB的常见错误是直接套用公式却不处理采样格式,因YUV420P、NV12等格式内存布局和色度采样方式不同,误判会导致偏色或伪影;必须先确认格式,再按对应规则处理指针偏移、U/V坐标映射与边界对齐,并注意内存访问优化。

YUV转RGB的常见错误:直接套用公式却不处理采样格式
绝大多数人卡在第一步——以为YUV只有一种布局。实际上YUV420P、YUV422P、NV12、UYVY等格式的内存排布和色度分量采样方式完全不同,直接套用标准YUV→RGB系数会得到严重偏色或块状伪影。
关键判断点:先确认你的原始数据是哪种YUV变体。最常见的是YUV420P(又称I420),它把Y单独存为一个平面,U和V各自下采样为宽高各一半,再顺序存放;而NV12是Y平面后紧跟一个UV交织的平面(U/V交错,每行UV各占一半字节)。
- 用
ffprobe查原始数据:如果没封装,靠采集设备文档或协议说明确认格式 - 若不确定,可dump前几帧到文件,用
ffmpeg -f rawvideo -pix_fmt yuv420p -s:v 640x480 -i input.yuv -vframes 1 out.png验证是否能正确解码 - 误判
NV12为I420会导致U/V通道错位,绿色/紫色大面积溢出
手动实现YUV420P → RGB24:注意指针偏移与边界对齐
以YUV420P为例,Y平面大小为width * height,U和V平面各为(width/2) * (height/2)(需向下取整,且width/height必须为偶数)。RGB输出缓冲区应按width * height * 3分配,且必须逐行处理——因为U/V每两个像素共用一组值。
核心转换公式(ITU-R BT.601,适用于SD/多数采集设备):
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
int r = y + (140 * (v - 128)) / 100; int g = y - (34 * (u - 128)) / 100 - (71 * (v - 128)) / 100; int b = y + (177 * (u - 128)) / 100;
实际编码时必须做裁剪:r = std::clamp(r, 0, 255),否则溢出导致马赛克。
- U/V坐标对应关系:像素
(x,y)的U/V值来自u[y/2 * (width/2) + x/2],不是u[y * width + x] - 若width非2的倍数,
width/2向下取整后可能导致最后一列U/V缺失,需复制左侧值或补零 - 避免浮点运算:全部用整数移位近似,例如
(140 * (v - 128)) / 100可写为((140 * (v - 128) + 50) >> 7)(+50为四舍五入)
用OpenCV快速验证但别直接用于生产
cv::cvtColor()确实能一键转换,比如cv::cvtColor(yuv_mat, rgb_mat, cv::COLOR_YUV2RGB_I420),但它默认假设输入是连续内存、无padding、且Mat类型匹配。真实场景中,原始YUV数据常带stride(如GPU输出行末有填充字节),直接构造cv::Mat会读错后续行。
- 构造Mat时务必显式传入step参数:
cv::Mat yuv_mat(height + height/2, width, CV_8UC1, data_ptr, width)(I420总高为height*3/2,但step仍是width) -
cv::COLOR_YUV2RGB_NV12和cv::COLOR_YUV2RGB_I420不可混用,传错触发断言失败或静默错误 - OpenCV内部仍走查表或SIMD优化,但调试时方便;上线建议用
libyuv或手写SSE/NEON版本
性能瓶颈在哪?别只盯着转换公式
真正拖慢速度的往往不是乘法本身,而是内存访问模式。YUV420P中U/V平面比Y小四分之三,但每次取U/V都要跳转到不同内存区域,CPU cache频繁失效。实测在i7上,单纯公式计算只占耗时30%,70%花在cache miss和非对齐加载上。
- 优先用
libyuv:它针对不同架构做了汇编优化,且自动处理stride、padding、多线程分块 - 自己写时,把Y/U/V各读一行到L1缓存友好的临时buffer,再批量转RGB,比逐像素随机访问快2–3倍
- Android NDK开发必须用
libyuv或MediaCodec输出RGB,硬编解码器输出的YUV通常带额外stride,cv::Mat无法安全解析
真正麻烦的是跨平台兼容性——Windows DirectShow常用YUY2,iOS VideoToolbox默认BiPlanar(Y + UV两个plane),嵌入式sensor可能输出YV12(Y-V-U顺序)。不确认格式就动手,后面全白忙。

















