在开始学习YUV之前,先向大家介绍一款查看YUV图像的查看工具---YUV Eye,这个工具会非常方便调试
1.YUV 概念
YUV 是一种表示颜色的模型,跟RGB是同一个级别的概念,通常用YCbCr这样的形式表示,其中Y是指亮度分量,Cb指蓝色色度分量,而Cr指红色色度分量
2.YUV 和 RGB 比较
2.1 体积对比RGB更小
- 如果使用RGB
比如RGB888 (R、G、B、每个分量都是8bit),那么
一个像素占用24bit
- 如果使用YUV
1个像素可以减少至平均只占用12bit(1.5字节)
2.2 组成
- RGB 数据由R、G、B三个分量组成;
- YUV数据由Y、U、V三个分量组成,现在通常说的YUV指的是YCbCr
Y:表示亮度(Luminance、Luma),占8bit(1字节)
Cb、Cr:表示色度(Chrominance、Chroma)
- Cb(U):蓝色色度分量,占8bit(1字节)
- Cr(V):红色色度分量,占8bit(1字节)
3.计算公式
3.1 计算公式1
Y = 0.257R + 0.504G + 0.098B + 16
U = -0.148R - 0.291G + 0.439B + 128
V = 0.439R - 0.368G - 0.071B + 128
R = 1.164(Y - 16) + 2.018(U - 128)
G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128)
B = 1.164(Y - 16) + 1.596(V - 128)
- RGB的取值范围是[0,255]
- Y的取值范围是[16,235]
- UV的取值范围是[16,239]
3.2 计算公式2
Y = 0.299R + 0.587G + 0.114B
U = 0.564(B - Y) = -0.169R - 0.331G + 0.500B
V = 0.713(R - Y) = 0.500R - 0.419G - 0.081B
R = Y + 1.403V
G = Y - 0.344U - 0.714V
B = Y + 1.770U
- RGB的取值范围是[0, 1]
- Y的取值范围是[0, 1]
- UV的取值范围是[-0.5, 0.5]
3.3 计算公式3
Y = 0.299R + 0.587G + 0.114B
U = -0.169R - 0.331G + 0.500B + 128
V = 0.500R - 0.419G - 0.081B + 128
R = Y + 1.403(V - 128)
G = Y - 0.343(U - 128) - 0.714(V - 128)
B = Y + 1.770(U - 128)
- RGB的取值范围是[0, 255]
- YUV的取值范围是[0, 255]
4.YUV 格式
5.分类标准
我们可以将YUV格式按照数据大小分为三个格式,YUV420,YUV422,YUV444,另外因为人眼中有上亿个感光细胞,其中视杆细胞占了95%,而视锥细胞仅占5%。因此,人眼对亮度的敏感程度要高于对色度的敏感程度,人眼对于亮度的分辨要比对颜色的分辨精细一些。
如果把图像的色度分量减少一些,人眼也丝毫感觉不到变化和差异
- 视杆细胞
感知光线的强弱
没有色彩识别功能
负责夜间非彩色视觉
- 视锥细胞
感知颜色
负责白天彩色视觉
如果你的视锥细胞发育不正常,数量太少,那感知颜色就会受阻,可能会导致你色弱
5.1 采样格式
采样格式通常使用A:B:C的形式来表示,比如4:4:4、4:2:2、4:2:0 等
A:一块A*2个像素的概念区域,一般都是4
B:第1行的色度采样数目
C:第2行的色度采样数目
- C的值一般要么等于B,要么等于0
前面已经说过人眼对Y的敏感度远超过于对U和V的敏感,所以有时候可以多个Y分量共用一种UV,这样既可以极大得节省空间,又可以不太损失质量。
YUV 420,由4个Y分量共用一套UV分量
YUV 422,由2个分量共用一套UV分量
YUV 444,不共用,一个Y分量使用一套UV分量
下面进行图文解释一下:❌代表亮度,⭕️代表色度4:4:4
第一行采集4组CbCr分量,第二行采集4组CbCr分量
-
第一个像素都有自己独立的1组分量
- Y分量与CbCr分量的水平方向比例是1:1(每1列都有1组CbCr分量)
- Y分量与CbCr分量的垂直方向比例是1:1 (每1行都有1组CbCr分量)
- Y分量与CbCr分量的总比例是1:1
-
1个像素占用24bit(3字节),跟RGB888体积一样
-
24bpp(bits per pixel)
YUV444.png
-
4:2:2
第一行采集2组CbCr分量,第二行采集2组CbCr分量
-
水平方向相邻的2个像素(1行2列)共用1组CbCr分量
- Y分量与CbCr分量的水平方向比例是2:1(每2列就有1组CbCr分量)
- Y分量与CbCr分量的垂直方向比例是1:1(每1行都有1组CbCr分量)
- Y分量与CbCr分量的总比例是2:1
-
1个像素平均占用16bit(2字节)
- 16bpp
-
因为2个像素共占用32bit(4字节 = 2个Y分量 + 1个Cb分量 + 1个Cr分量)
YUV422.png
4:2:0
-
第一行采集2组CbCr分量,第二行共享第一行的CbCr分量
- Y分量与CbCr分量的水平方向比例是2:1(每2列就有1组CbCr分量)
- Y分量与CbCr分量的垂直方向比例是2:1(每2行就有1组CbCr分量)
- Y分量与CbCr分量的总比例是4:1
-
1个像素平均占用12bit(1.5 字节)
- 12bpp
-
因为4个像素共占用48bit(6字节 = 4个Y分量 + 1个Cb分量 + 1个Cr分量)
YUV420.png
按照多个Y分量共用一个UV的方式,我们可以把YUV分为420,422,444 三种类型,而在这三种类型之下,我们又可以按照YUV的排列存储顺序,将其细分为好多种格式
6.存储格式
存储格式,决定了YUV数量是如何排列和存储的
6.1 分类
YUV的存储格式可以分为3大类:
- Plannar 平面
Y、U、V分量单独存储
名称通常以字母p结尾
- Semi-Plannar(半平面)
Y分量单独存储,U、V分量交错存储
名称通常以字母sp结尾
- Packed(紧凑)
或者叫Interleaved,YUV分量交错存储
6.2 具体分类
4:4:4
- Plannar
- I444
Y Y Y Y
Y Y Y Y
U U U U
U U U U
V V V V
V V V V
- YV24
Y Y Y Y
Y Y Y Y
V V V V
V V V V
U U U U
U U U U
- Semi-Plannar
- NV24
Y Y Y Y
Y Y Y Y
U V U V U V U V
U V U V U V U V
- NV42
Y Y Y Y
Y Y Y Y
V U V U V U V U
V U V U V U V U
4:2:2
- Plannar
- I422
Y Y Y Y
Y Y Y Y
U U
U U
V V
- YV16
Y Y Y Y
Y Y Y Y
V V
V V
U U
U U
- Semi-Plannar
- NV16
Y Y Y Y
Y Y Y Y
U V U V
U V U V
- NV61
Y Y Y Y
Y Y Y Y
V U V U
V U V U
- Packed
- UYVY
U Y V Y U Y V Y
U Y V Y U Y V Y
- YUYV
Y U Y V Y U Y V
Y U Y V Y U Y V
- YVYU
Y V Y U Y V Y U
Y V Y U Y V Y U
4:2:0
- Plannar
- I420 属于YUV 420 Plannar 的一种,YUV分量分别存放,先是w * h 长度的Y,后面跟着w * h * 0.25 长度的U,最后是w * h * 0.25长度的V,总长度为w * h * 1.5;
Y Y Y Y
Y Y Y Y
U U
V V
- YV12
Y Y Y Y
Y Y Y Y
V V
U U
- Semi-Plannar
- NV12
Y Y Y Y
Y Y Y Y
U V U V
- NV21
Y Y Y Y
Y Y Y Y
V U V U
7 格式转换
71.其他图片格式转YUV
ffmpeg -i in.png -s width*height -pix_fmt yuv420p out.yuv
✘ songlin@feng-sl ~/audio/YUV master ± ffmpeg -i in.png -s 686x370 -pix_fmt yuv420p out.yuv
ffmpeg version 4.3.2 Copyright (c) 2000-2021 the FFmpeg developers
built with Apple clang version 12.0.0 (clang-1200.0.32.29)
configuration: --prefix=/usr/local/Cellar/ffmpeg/4.3.2_4 --enable-shared --enable-pthreads --enable-version3 --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox
libavutil 56. 51.100 / 56. 51.100
libavcodec 58. 91.100 / 58. 91.100
libavformat 58. 45.100 / 58. 45.100
libavdevice 58. 10.100 / 58. 10.100
libavfilter 7. 85.100 / 7. 85.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 7.100 / 5. 7.100
libswresample 3. 7.100 / 3. 7.100
libpostproc 55. 7.100 / 55. 7.100
Input #0, png_pipe, from 'in.png':
Duration: N/A, bitrate: N/A
Stream #0:0: Video: png, rgba(pc), 686x370, 25 tbr, 25 tbn, 25 tbc
Stream mapping:
Stream #0:0 -> #0:0 (png (native) -> rawvideo (native))
Press [q] to stop, [?] for help
Output #0, rawvideo, to 'out.yuv':
Metadata:
encoder : Lavf58.45.100
Stream #0:0: Video: rawvideo (I420 / 0x30323449), yuv420p, 686x370, q=2-31, 75480 kb/s, 25 fps, 25 tbn, 25 tbc
Metadata:
encoder : Lavc58.91.100 rawvideo
frame= 1 fps=0.0 q=-0.0 Lsize= 369kB time=00:00:00.04 bitrate=75480.0kbits/s speed=5.16x
video:369kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.000000%
上面命令生成的yuv文件大小是:imageSize = width * height = 686 * 370 * 1.5 = 380730 字节
- - s
- 设置图片的尺寸
- 可以使用一些固定字符串表示尺寸,比如hd720表示1280 * 720
- 如果不设置此项,默认会跟随输入图片的尺寸
- -pix_fmt
- 设置像素格式
- 可以通过ffmpeg -pix_fmts 查看FFmpeg支持的像素格式
- 如果不设置此选项,默认会跟随输入图片的像素格式
- 比如可能是rgb24,rgba8,pal8等
- 可以通过ffprobe查看某图片的像素格式,比如ffprobe in.png
7.2 YUV 转其他图片格式
ffmpeg -s 680x370 -pix_fmt yuv420p -i out.yuv out.jpg
songlin@feng-sl ~/audio/YUV master ± ffmpeg -s 686x370 -pix_fmt yuv420p -i out.yuv out.jpg
ffmpeg version 4.3.2 Copyright (c) 2000-2021 the FFmpeg developers
built with Apple clang version 12.0.0 (clang-1200.0.32.29)
configuration: --prefix=/usr/local/Cellar/ffmpeg/4.3.2_4 --enable-shared --enable-pthreads --enable-version3 --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox
libavutil 56. 51.100 / 56. 51.100
libavcodec 58. 91.100 / 58. 91.100
libavformat 58. 45.100 / 58. 45.100
libavdevice 58. 10.100 / 58. 10.100
libavfilter 7. 85.100 / 7. 85.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 7.100 / 5. 7.100
libswresample 3. 7.100 / 3. 7.100
libpostproc 55. 7.100 / 55. 7.100
[rawvideo @ 0x7f924b809600] Estimating duration from bitrate, this may be inaccurate
Input #0, rawvideo, from 'out.yuv':
Duration: 00:00:00.04, start: 0.000000, bitrate: 75480 kb/s
Stream #0:0: Video: rawvideo (I420 / 0x30323449), yuv420p, 686x370, 75480 kb/s, 25 tbr, 25 tbn, 25 tbc
Stream mapping:
Stream #0:0 -> #0:0 (rawvideo (native) -> mjpeg (native))
Press [q] to stop, [?] for help
[swscaler @ 0x7f924b086000] deprecated pixel format used, make sure you did set range correctly
[swscaler @ 0x7f924b086000] Warning: data is not aligned! This can lead to a speed loss
Output #0, image2, to 'out.jpg':
Metadata:
encoder : Lavf58.45.100
Stream #0:0: Video: mjpeg, yuvj420p(pc), 680x370, q=2-31, 200 kb/s, 25 fps, 25 tbn, 25 tbc
Metadata:
encoder : Lavc58.91.100 mjpeg
Side data:
cpb: bitrate max/min/avg: 0/0/200000 buffer size: 0 vbv_delay: N/A
frame= 1 fps=0.0 q=4.0 Lsize=N/A time=00:00:00.04 bitrate=N/A speed=8.71x
video:24kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
8 显示YUV
8.1 完整YUV
可以通过ffplay显示YUV数据
- YUV中直接存储的是所有像素的颜色信息,可以理解为原始数据
- 必须得设置YUV的尺寸(-s)、像素格式(-pix_fmt)才能正常显示,和音频pcm播放的时候需要设置采样率(-ar)、声道数(-ac)、采样格式(-f)类似
fplay -s 686x370 -pix_fmt yuv420p out.yuv
ffplay version 4.3.2 Copyright (c) 2003-2021 the FFmpeg developers
built with Apple clang version 12.0.0 (clang-1200.0.32.29)
configuration: --prefix=/usr/local/Cellar/ffmpeg/4.3.2_4 --enable-shared --enable-pthreads --enable-version3 --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox
libavutil 56. 51.100 / 56. 51.100
libavcodec 58. 91.100 / 58. 91.100
libavformat 58. 45.100 / 58. 45.100
libavdevice 58. 10.100 / 58. 10.100
libavfilter 7. 85.100 / 7. 85.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 7.100 / 5. 7.100
libswresample 3. 7.100 / 3. 7.100
libpostproc 55. 7.100 / 55. 7.100
Option -s is deprecated, use -video_size.
Option -pix_fmt is deprecated, use -pixel_format.
//可以看到我们上面输入的两个参数有一些已经过期了,建议我们使用新参数
ffplay -video_size 686x370 -pixel_format yuv420p out.yuv
8.2 单个分量
可以使用过滤器filter 显示其中的单个分量(r、g、b、y、u、v)
- 只显示r分量
ffplay -vf extractplanes=r in.png
- 只显示g分量
ffplay -vf extractplanes=g in.png
- 只显示b分量
ffplay -vf extractplanes=b in.png
- 只显示y分量
ffplay -video_size 686x370 -pixel_format yuv420p -vf extractplanes=y out.yuv
- 只显示u分量
ffplay -video_size 686x370 -pixel_format yuv420p -vf extractplanes=u out.yuv
- 只显示v分量
ffplay -video_size 686x370 -pixel_format yuv420p -vf extractplanes=v out.yuv
可以看到显示U分量和V分量的时候,图片的面积会变小,这个就是存储格式导致的,4:2:0的存储格式水平和垂直Y和UV分量比例都是2:1,所以整体就是4:1,所以面积会变小
好了YUV的介绍,到这里差不多了