YUV420格式详解

以下从原理到实现逐步详解YUV420格式,结合Mermaid图表与C++代码,为音视频开发者提供系统指南。


1. YUV420核心原理

1.1 采样结构与数据量
原始像素
Y分量全采样
UV分量2x2降采样
  • Y(亮度):全分辨率存储(每个像素独立)
  • U/V(色度):每2x2像素共享一组UV值,水平和垂直分辨率减半
  • 数据量计算(8位深度):
    // 计算YUV420图像字节数
    int y_size = width * height;          // Y分量
    int uv_size = (width/2) * (height/2); // U或V分量
    int total_size = y_size + uv_size * 2; // Y + U + V
    
    示例:1920x1080图像 → Y占1.97MB,UV共0.98MB,总计2.95MB(仅为RGB的50%)
1.2 与RGB转换公式

标准ITU-R BT.601转换公式:

// RGB转YUV分量(范围:Y∈[0,255], UV∈[0,255])
void rgb_to_yuv(uint8_t r, uint8_t g, uint8_t b, uint8_t& y, uint8_t& u, uint8_t& v) {
    y = 0.299*r + 0.587*g + 0.114*b;
    u = -0.1687*r - 0.3313*g + 0.5*b + 128;  // 加128避免负值
    v = 0.5*r - 0.4187*g - 0.0813*b + 128;
}

2. 存储格式详解

2.1 平面格式(YUV420P)
连续存储
降采样
降采样
Y平面
height * width
U平面
height/2 * width/2
V平面
height/2 * width/2
  • 内存布局
    [YYYY...][UUU...][VVV...]
    
  • 示例:4x4图像数据流:
    Y0 Y1 Y2 Y3 Y4 ... Y15   // 16字节
    U0 U1 U2 U3             // 4字节(2x2块均值)
    V0 V1 V2 V3             // 4字节
    
2.2 半平面格式(YUV420SP/NV12)
连续存储
降采样
Y平面
height * width
UV交织
height/2 * width/2 * 2
  • 内存布局
    [YYYY...][U0 V0 U1 V1 ...]
    
  • 应用场景:手机摄像头(MIPI CSI-2)、GPU纹理处理

3. C++代码实现

3.1 RGB转YUV420P
void rgb_to_yuv420p(uint8_t* rgb, uint8_t* yuv, int width, int height) {
    uint8_t* y = yuv;                        // Y分量起始位置
    uint8_t* u = yuv + width * height;       // U分量起始位置
    uint8_t* v = u + (width/2) * (height/2); // V分量起始位置

    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            int idx = (i * width + j) * 3;   // RGB像素偏移
            uint8_t r = rgb[idx];
            uint8_t g = rgb[idx + 1];
            uint8_t b = rgb[idx + 2];
            
            // 计算当前像素Y值
            y[i * width + j] = 0.299*r + 0.587*g + 0.114*b;
            
            // 每2x2块计算一次UV
            if (i % 2 == 0 && j % 2 == 0) {
                int uv_idx = (i/2) * (width/2) + (j/2);
                u[uv_idx] = -0.1687*r - 0.3313*g + 0.5*b + 128;
                v[uv_idx] = 0.5*r - 0.4187*g - 0.0813*b + 128;
            }
        }
    }
}
3.2 YUV420P转RGB(OpenCV显示)
#include 
using namespace cv;

void yuv420p_to_mat(uint8_t* yuv, Mat& rgb, int width, int height) {
    Mat yuv_mat(height * 3/2, width, CV_8UC1, yuv); // 创建YUV矩阵
    cvtColor(yuv_mat, rgb, COLOR_YUV2BGR_I420);      // 内置转换
}

int main() {
    int width = 640, height = 480;
    uint8_t yuv_data[width * height * 3/2]; // 读取YUV文件
    Mat rgb;
    yuv420p_to_mat(yuv_data, rgb, width, height);
    imshow("YUV420P", rgb);
    waitKey(0);
    return 0;
}

4. 性能优化技巧

4.1 SIMD加速(AVX2示例)
#include 

// 批量计算8个像素的Y值
void simd_calc_y(const uint8_t* rgb, uint8_t* y, int n) {
    __m256 coef_y = _mm256_set_ps(0.114, 0.587, 0.299, ...); // 重复系数
    for (int i = 0; i < n; i += 8) {
        __m256 r = _mm256_loadu_ps(rgb + i*3);
        __m256 g = _mm256_loadu_ps(rgb + i*3 + 8);
        __m256 b = _mm256_loadu_ps(rgb + i*3 + 16);
        __m256 y_val = _mm256_fmadd_ps(r, coef_y, 
                       _mm256_fmadd_ps(g, coef_y, 
                       _mm256_mul_ps(b, coef_y)));
        _mm256_storeu_ps(y + i, y_val);
    }
}
4.2 零拷贝传输
  • GPU优化:通过DMA将YUV420数据直传GPU显存
    cudaMemcpyAsync(dev_yuv, host_yuv, size, cudaMemcpyHostToDevice);
    
  • 内存对齐:UV分量按32字节对齐,提升缓存命中率
    uint8_t* uv = (uint8_t*)_aligned_malloc(uv_size, 32);
    

5. 典型问题与调试

问题现象 原因 解决方案
图像绿色偏色 UV分量顺序颠倒 交换U/V平面位置
水平条纹 UV未降采样 检查2x2块采样逻辑
内存访问越界 未计算YUV420准确大小 使用 width*height*3/2 预分配
转换速度慢 未启用并行计算 使用OpenMP多线程

6. 应用场景

MIPI CSI-2
H.264编码
FFmpeg解码
OpenGL渲染
摄像头Sensor
YUV420SP
视频流
YUV420P
显示器
  • 视频编码:H.264/265默认输入格式(节省带宽)
  • 实时通信:WebRTC优先使用YUV420传输
  • AI推理:移动端模型输入常为YUV420直接处理

完整工程参考:

  • GitHub YUV处理库
  • FFmpeg swscale源码
    通过理解采样原理、内存布局与硬件优化,YUV420成为平衡画质与效率的核心多媒体格式。

你可能感兴趣的:(嵌入式,Linux,C/C++,linux)