前言
我们开发平常图片的数据都是RGB,但是涉及视频相关的都会使用到YUV格式,为什么呢?
- YUV是电视信号的格式,为了同时兼容黑白和彩色,只有Y就是黑白,加上UV就是彩色。
- YUV采样可以大大降低传输数据的大小,以YUV420为例就是RGB的一半。
换算
1byte就是1个字节,等于8位(bits)。
常用的值
1 | 2^24=16777216 |
RGB/BGR
每一个点都是由三个byte组成,分别存储R、G、B,值范围是[0-255]。
需要注意的是在C#中我们获取到的是按BGR排序的。
如果要包含透明通道,就有RGBA、BGRA、ARGB或者ABGR这四种方式,所以要注意我们所需的格式。
图像的格式RGB565,RGB555,RGB888,BMP24的区别
- RGB565:16位格式,5位红色,6位绿色,5位蓝色,颜色深度较浅,文件大小较小
- RGB555:16位格式,5位红色,5位绿色,5位蓝色,最后1位未使用,颜色深度比RGB565略浅
- RGB888:24位格式,8位红色,8位绿色,8位蓝色,真彩色格式,颜色深度高,文件大小大
- BMP24:也是24位格式,和RGB888一致,真彩色,文件较大
YUV
YUV的值是怎么来的呢?
直接给公式 :
(请不要使用其他博客中的浮点数类型的公式,会严重影响精度)
1 | y = (( 66 * r + 129 * g + 25 * b + 128) >> 8) + 16 ; |
这时大家可能就有疑问了,我们明明已经有了RGB可以表示这个像素点了,为什么还需要再使用YUV来进行表示。
说白一点就是RGB三个分量一个都不可少才能表示出一个像素点。
而YUV可以通过不同的采样方式来减少一些U、V分量,从而减小所需的存储空间。而恢复为RGB的时候可以几个Y分量共用U、V分量来恢复为RGB。
这样全采样的YUV其实跟RGB所需存储空间一样了,而这种采样方式就是 YUV 4:4:4 。
采样方式
YUV 4:4:4
这种方式也就是上文所说的,YUV分量全部进行采样
YUV 4:2:2
在所有像素上,Y分量全部采样。
在同行的像素上, U 和 V 分量分别 交替 进行采样;
YUV 4:2:0【重点】
在所有像素上,Y分量全部采样。
在(偶数行), U 分量 间隔 进行采样,而不采样V分量。
在(奇数行), V 分量 间隔 进行采样,而不采样U分量。
以上面的8个像素为例,那么我们采集到的数组长度则分别为:
YUV 4:4:4
8 + 8 + 8 长度为24
YUV 4:2:2
8 + 4 + 4 长度为16,是第一种的 三分之二
YUV 4:2:0
8 + 2 + 2 长度为12,是第一种的 二分之一
所以使用420的采样方式,所需的存储空间会大大减小。
存储方式
我们4x2的图片为例,共8个像素,使用YUV420存储的话,对应的数组就会是这样:
1 | Y数组: [Y, Y, Y, Y, Y, Y, Y, Y] |
存储方式分为
- planar(平面方式) 现存Y,在存UV
- packed(打包方式)YUV交替存储
整体
平面模式
顺序可以是 先存Y,再存U,最后存V
。也可以是先存Y,再存V,再存U
。我们这里把前者称为 YU的存储方式,把后者称为 YV的存储方式
420采样方式 + YU存储方式 = YU12(又叫 I420 )
1 | YYYYYYYY UU VV |
420采样方式 + YV存储方式 = YV12
1 | YYYYYYYY VV UU |
这两种存储格式呢,又统称为 YUV420P 格式。
UV交替存储
UV交替存储的,还有VU交替存储的,那么我们就把前者称为UV存储,把后者称为VU存储,那么总结来了:
420采样方式 + UV存储方式 = NV12
1 | YYYYYYYY UV UV |
420采样方式 + VU存储方式 = NV21
1 | YYYYYYYY VU VU |
上面这两种特殊的平面方式呢,又叫 Semi-Splanar ,所以以上两种格式又称为 YUV420SP 格式。
422采样也可以使用平面存储的方式,如下:
1 | Y数组: [Y, Y, Y, Y, Y, Y, Y, Y] |
存储方式
1 | YYYYYYYY UUUU VVVV |
422采样方式 + 平面存储方式 = YUV422P(属于YUV422)
打包方式
一般我们使用422采样方式的时候会采用这种存储方式,这种方式就不像上面那种那么直白了,先用数组表示吧,注意是422采样模式,所以U、V数组长度也变化了
1 | YUYV YUYV YUYV YUYV |
如上所示,因为YUV的比例是2:1:1 ,所以取两个Y元素就需要分别取一个U和V元素,后面同理。所以根据上面这种格式:
422采样方式 + YUYV打包存储方式 = YUYV
1 | YUYV YUYV YUYV YUYV |
422采样方式 + UYVY打包存储方式 = UYVY
1 | UYVY UYVY UYVY UYVY |
图片数据
1 | using (var bmp = new Bitmap(image)) |
C#封装的libyuv
https://github.com/jlennox/LibYuvSharp
https://www.nuget.org/packages/Lennox.LibYuvSharp/1.1.2?_src=template
安装
1 | Install-Package Lennox.LibYuvSharp -Version 1.1.2 |
注意
要求.Net Framework版本net461及以上。
改库是64位的。
官网
https://www.nuget.org/packages/Lennox.LibYuvSharp/1.1.2?_src=template
推荐程序使用.Net Framework 4.7.2版本
加载BMP
1 | private void LoadBmp() |
Image加载I420数据
I420 => RGB24 => Bitmap => BitmapImage => 加载
I420转RGB24
这里使用了libyuv库
1 | int width = obj.Width; |
RGB24转Bitmap
1 | public static Bitmap RgbToBitmap |
Image加载Bitmap
1 | Bitmap bitmap = ...; |
加载图片转RGB/ARGB
1 | private void LoadBmp() |
WWebrtcSharp回显
1 | var videoSource = new FrameVideoSource(); |