常见视频原始数据格式分析 — YUV

android multimedia 专栏收录该内容
11 篇文章 1 订阅

1. YUV 简介

YUV 是比较常用的原始视频数据数据格式,视频采集芯片输出的码流大部分都是 YUV 数据流形式,而视频处理(如 H264、H265编码等),也是在原始 YUV 码流进行编码和解析。所以,了解熟悉 YUV 数据流对于做视频领域的人而言,至关重要。

YUV,分为三个分量,Y:表示明亮度(Luminance 或 Luma),也就是灰度值;而 U 和 V :表示的则是色度(Chrominance 或 Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。这样设计分开的主要原因是,人眼对色度的敏感程度要低于对亮度的敏感程度。

与我们熟知的 RGB 类似,YUV 也是一种颜色编码方法,主要用于电视系统以及模拟视频领域,它将亮度信息(Y)与色彩信息(UV)分离,没有 UV 信息一样可以显示完整的图像,只不过是黑白的,这样的设计很好地解决了彩色电视机与黑白电视的兼容问题。并且,YUV 不像 RGB 那样要求三个独立的视频信号同时传输,所以用 YUV 方式传送占用极少的频宽。

2. YUV 采样方式

YUV 码流有多种不同的格式,要分析 YUV 码流,就必须搞清楚你面对的到底是哪一种格式,并且必须搞清楚这种格式的 YUV 采样和分布情况。

大分类:packed & planar

YUV 按像素分布规律被分为两大类,packed 打包格式和 plannar 平面格式,

  1. – 将 YUV 分量存放在同一数组中,通常是几个相邻的像素组成一个宏像素(macro-pixel)
  2. – 将 YUV 分量分别存储在三个数组中,先连续存储所有像素点的 Y,紧接着存储所有像素点的 U,随后是所有像素点的 V

小分类:YUV444 & YUV422 & YUV420

目前比较主流的采样方式有:YUV444、YUV422、YUV420,下面会对这三种格式存储方式加以介绍,并说明如何根据其采样格式来从码流中还原每个点的 YUV 值。

常讲的的 YUV A:B:C 的意思一般是指基于 4 个象素来讲,其中 Y 采样了 A 次,U 采样了 B 次,V 采样了 C 次

下面的三个图可以直观的表示采集的方式,以黑点表示像素点的 Y 分量,以空心圆圈表示该像素点的 UV 分量。

image

先记住下面这句话,以后提取每个像素的 YUV 分量会用到。

YUV 4:4:4 采样,每一个 Y 对应一组 UV 分量,每像素 24 位

YUV 4:2:2 采样,每两个 Y 共用一组 UV 分量,每像素 16 位

YUV 4:2:0 采样,每四个 Y 共用一组 UV 分量,每像素 12 位

3. 存储方式

YUV 的优点之一是基于人眼对色度的敏感程度要低于对亮度的敏感程度,可以适当降低色度的采样率,同时不会明显降低视觉质量。因此,常见的 YUV422、YUV420 都适当降低了色度分量。

下面用图的形式给出常见的 YUV 码流的存储方式,其中,Cb、Cr 的含义等同于 U、V。

1) YUVY 格式(属于 YUV422)

image

YUYV 为 YUV422 采样方式的存储格式之一,相邻的两个 Y 分量共用其相邻的两个Cb、Cr,因此,对于像素点 Y’00、Y’01 而言,其 Cb、Cr 的值均为 Cb00、Cr00,其他的像素点的 YUV 取值依次类推。

2) UYVY 格式 (属于YUV422)

image

UYVY 格式也是 YUV422 采样方式的存储格式之一,只不过与 YUYV 不同的是 UV 的排列顺序不一样而已,还原其每个像素点的YUV值的方法与上面一样。

3) YUV422P(属于YUV422)

image

YUV422P 是一种 plannar 模式,即平面模式。与前面其他 YUV422 采访方式不同,它并不是将 YUV 数据交错存储,而是先存放所有的 Y 分量,然后存储所有的 U分量, 最后存储所有的 V 分量。

如上图所示。其每一个像素点的 YUV 值提取方法也是遵循 YUV422 格式的最基本提取方法,即两个 Y 共用一个 UV。比如, 对于像素点 Y’00、Y’01 而言,其 Cb、Cr 的值均为 Cb00、Cr00。

4) YV12,YU12格式(属于YUV420)

image

YU12 和 YV12 属于 YUV420 采样格 式之一,也是一种 plannar
模式,将 Y、U、V 分量分别打包,依次存储。

其每一个像素点的 YUV 数据提取遵循 YUV420 格式的提取方式,即 4 个 Y 分量共用一 组 UV,即上图中,Y’00、Y’01、Y’10、Y’11 共用 Cr00、Cb00,其他依次类推。

5) NV12、NV21(属于YUV420)

image

NV12 和 NV21 属于 YUV420 采样格式之一,也是一种 plannar 模式,但是与 YV12 不同的是,将 Y 和 UV 分为两个 plane,Y 连续存储,但是 UV 交错存储。

其提取方式与上一种类似,即 Y’00、Y’01、Y’10、Y’11 共用 Cr00、Cb00。

这种采样存储方式也被称为 YUV420SP,即 UV 交错存储,作为区分,上文 4) 介绍的存储方式被称为 YUV420P。

下面用一种表说明几种常见存储方式所占用的内存,以 w*h 大小的图像为例:

采样方式占用
YUV 4:4:4Y(w * h) + U(w * h) + V(W * h) = 3 * w * h
YUV 4:2:2Y(w * h) + U(w * h * 1/2) + V(W * h * 1/2) = 2 * w * h
YUV 4:2:0Y(w * h) + U(w * h * 1/4) + V(W * h * 1/4) = 1.5 * w * h

4. 几种常见 YUV 视频像素处理

4.1 分离 YUV420P 像素数据中的 Y、U、V 分量

由于 YUV420P 各分量的连续存储性质,可以很方便快捷的直接提取出三个分量。

下面的程序将 YUV420P 中的 Y、U、V 三个分量分离出来。代码如下:

/** 
 * Split Y, U, V planes in YUV420P file. 
 * @param url  Location of Input YUV file. 
 * @param w    Width of Input YUV file. 
 * @param h    Height of Input YUV file. 
 * @param num  Number of frames to process. 
 * 
 */  
void simplest_yuv420_split(char *url, int w, int h)
{  
    FILE *fp = fopen(url, "rb+");  
    FILE *fp1 = fopen("output_420_y.y", "wb+");  
    FILE *fp2 = fopen("output_420_u.y", "wb+");  
    FILE *fp3=fopen("output_420_v.y", "wb+");  

    unsigned char *pic = (unsigned char*) malloc(w * h * 3 / 2);  

    fread(pic, 1, w * h * 3 / 2, fp);  
    //Y  
    fwrite(pic, 1, w * h, fp1);  
    //U  
    fwrite(pic + w * h, 1, w * h / 4, fp2);  
    //V  
    fwrite(pic + w * h * 5 / 4, 1, w * h / 4, fp3);  

    free(pic);  
    
    fclose(fp);  
    fclose(fp1);  
    fclose(fp2);  
    fclose(fp3);  
}  

从代码可以看出, 如果视频帧的宽和高分别为 w 和 h,那么一帧 YUV420P 像素数据一共占用 (w * h * 3 / 2) Byte 的数据。其中前 (w * h) Byte存储 Y,接着的 (w * h * 1 / 4) Byte 存储 U,最后 (w * h * 1 / 4) Byte 存储V。

上述调用函数的代码运行后,将会把一张分辨率为 256x256 分辨率 的 YUV420P 格式的像素数据文件分离成为三个文件:

output_420_y.y:纯 Y 数据,分辨率为 256x256 
output_420_u.y:纯 U 数据,分辨率为 128x128 
output_420_v.y:纯 V 数据,分辨率为 128x128

下面是一个运行实例:

输出原图:

image

程序输出三个分量:

1). output_420_y.y

image

2). output_420_u.y

image

3). output_420_v.y

image

4.2 将 YUV420P 像素数据的亮度减半

本程序中的函数可以通过将 YUV 数据中的亮度分量 Y 的数值减半的方法,降低图像的亮度。函数代码如下所示

/** 
 * Halve Y value of YUV420P file 
 * @param url     Location of Input YUV file. 
 * @param w       Width of Input YUV file. 
 * @param h       Height of Input YUV file. 
 * @param num     Number of frames to process. 
 */  
int simplest_yuv420_halfy(char *url, int w, int h)
{  
    FILE *fp = fopen(url,"rb+");  
    FILE *fp1 = fopen("output_half.yuv", "wb+");  

    unsigned char *pic = (unsigned char*)malloc(w * h * 3 / 2);  

    fread(pic, 1, w * h * 3 / 2, fp);  
    //Half  
    for (int j = 0; j < w * h; j++) {  
        unsigned char temp = pic[j] / 2;  
        pic[j] = temp;  
    }  
    fwrite(pic, 1, w * h * 3 / 2, fp1);  

    free(pic);  
    fclose(fp);  
    fclose(fp1);  

    return 0;  
} 

从代码可以看出,如果打算将图像的亮度减半,只要将图像的每个像素的 Y 值取出来分别进行除以 2 的工作就可以了。

输入原图:

image

处理后图片:

image

4.3 将 YUV420SP 图片旋转 90 度

void YUV420spRotate90(uchar *des, uchar *src, int width, int height)
{
    int wh = width * height;
    //旋转Y
    int k = 0;
    for (int i = 0; i < width; i++) {
        for (int j = 0; j < height; j++) {
            des[k] = src[width * j + i];
            k++;
        }
    }

    for (int i = 0; i < width; i += 2) {
        for (int j = 0; j < height / 2; j++) {
            des[k] = src[wh+ width * j + i];
            des[k+1] = src[wh + width * j + i + 1];
            k += 2;
        }
    }
}

其他待添加…

  • 2
    点赞
  • 1
    评论
  • 9
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值