YUV图像格式学习与灰度处理
前言
- 刚入职不久,导师要我完成一个任务,就是将将彩色YUV图像数据处理成黑白色,由于对YUV并不了解,所以才有了下面的学习。
YUV概述
- YUV与RGB一样,都是像素数据的编码格式,一组YUV渲染屏幕上的一个像素,控制屏幕用色彩的形式将事物表现出来,其中Y表示像素中的亮度,U表示色度,V表示浓度或饱和度。它将亮度信息(Y)与色彩信息(UV)分离,没有UV信息一样可以显示完整的图像,只不过是黑白色。这是一种压缩后的颜色表示方法,占用更少的物理空间,且对颜色的表现失真不明显,所以现在YUV格式非常常用。
YUV存储方式
- YUV有两种存储方式:planar、packed。
- planar:从字面意思上来看,planar是平面的意思,对应到存储方式上就是把YUV三种分量分别存储,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。这种在解析时很方便。
- packed:从字面意思来看,packed是打包的意思,对应到存储方式上就是把YUV三种分量连续交叉存储,这种方式在解析时比较麻烦。
YUV的采样方式
- 采样方式即如何表现一个像素,现在主流的有三种采样方式,4:4:4、4:2:2、4:2:0,这三种比例是YUV三种分量的比例,前面提到一个像素应该包含一个Y、一个U、一个V,如果要完全存储,那一个像素点就要存储YUV三个分量,这种形式就是4:4:4。但是因为人的眼睛对色度和饱和度不是特别敏感,所以一定程度上丢失一部分UV并不影响我们分辨颜色,所以为了节省存储空间,在存储时就故意丢掉部分UV分量,用两个Y分量共用一组UV分量,这种形式就是4:2:2,或用四个Y分量共用一组UV,这种形式就是4:2:0。
YUV 4:4:4采样,每一个Y对应一组UV分量。 YUV 4:2:2采样,每两个Y共用一组UV分量。 YUV 4:2:0采样,每四个Y共用一组UV分量。
-
在存储时YUV各占一个字节Byte,如果4:4:4方式,那一个256X256分辨率的图片要占用256×256×3=196608Byte,4:2:2方式要占用256×256×2=131072Byte,4:2:0方式要占用256×256×2/3=43690.7Byte,可以看到采用4:2:0方式存储空间整整减少了一半。
-
以YUV420为例说明:
图像像素数据:[Y0 U0 V0]、[Y1 U1 V1]、 [Y2 U2 V2]、 [Y3 U3 V3]
[Y5 U5 V5]、[Y6 U6 V6]、 [Y7 U7 V7] 、[Y8 U8 V8]
采样的码流: Y0 U0 Y1 Y2 U2 Y3
Y5 V5 Y6 Y7 U7 Y8
映射出的像素点为:
[Y0 U0 V5]、[Y1 U0 V5]、[Y2 U2 V7]、[Y3 U2 V7]
[Y5 U0 V5]、[Y6 U0 V5]、[Y7 U2 V7]、[Y8 U2 V7]
YUV常用格式
YU12/I420,YV12格式(属于YUV420)(planar方式存储,简称420p)
- YU12:先把全部的Y分量存完,再存U分量,最后存V分量。
- YV12:先把全部的Y分量存完,再存V分量,最后存U分量,对应到上图把第五行和第六行位置互换一下就是了。
NV12,NV21 (属于YUV420) (planar和packed混合存储,简称420sp)
- NV12:该格式是先存储全部的Y分量,然后UV分量交叉存储。
- NV21:该格式与NV12唯一的区别只是UV分量交叉的顺序不同,NV12是U排前面,NV21是V排前面。
```java
I420: YYYYYYYY UU VV =>YUV420P
YV12: YYYYYYYY VV UU =>YUV420P
NV12: YYYYYYYY UVUV =>YUV420SP
NV21: YYYYYYYY VUVU =>YUV420SP //安卓Camera默认格式
```
YUV422P(属于YUV422)(planar方式存储)
- 该格式存储方式与YU12是一样的,唯一的区别是UV分量的数量不同,YU12中四个Y共用一组UV,而该格式中两个Y共用一组UV。
YUYV/YUY2, YVYU,UYVY(属于YUV422)(packed方式存储)
- YUYU/YUY2:每两个Y分量共用一组UV分量,存储顺序是YUYV。
- YVYU:该格式与YUYV相似,只是存储时UV分量顺序不同而已。
- UYVY:与上面两种方式并无大的不同。
YUV图像处理
- 这是使用YUV格式主要是NV12和NV21。
灰度处理
- 如果要把YUV格式像素变成灰度图像,只需要将U、V分别设置为128即可。因为U、V是图像经过偏置处理的色度分量,色度分量在偏置处理前的取值范围为-128至127,此时的无色对应的"0"值,经过偏置后色度分量取值变成了0至255,这时候无色对应的值就是128。
//读取YUV文件(格式是NV12或NV21)
bool read_yuv_one(const string path_in,const string path_out) {
//读取YUV文件
FILE *in, *out;
in = fopen(path_in.c_str(), "rb");
out = fopen(path_out.c_str(), "rb");
//获取YUV文件的大小
fseek(in, 0L, SEEK_END);
int len = ftell(in);
fseek(in, 0L, SEEK_SET);
//创建缓存YUV文件的Buffer
char* buff = new char[len];
//读取YUV文件到Buffer
fread(buff, sizeof(char), len, in);
memset(buff + len * 2 / 3, 128, len / 3);
fwrite(buff, sizeof(char), len, out);
fclose(in);
fclose(out);
return true;
}
亮度减半
- 只需要将YUV数据中的亮度分量Y的数值减半的方法,降低图像的亮度。
Linux查看YUV图像文件的方法
- ffplay -f rawvideo -video_size 高x宽 图像名称.yuv
参考链接
https://www.cnblogs.com/daner1257/p/10767570.html
https://blog.csdn.net/qingkongyeyue/article/details/52801396
https://blog.csdn.net/leixiaohua1020/article/details/50534150