接触前端音视频之后,需要掌握大量音视频和多媒体相关的基础知识。在使用 FFmpeg + WASM 进行视频帧提取时,涉及到视频帧和颜色编码等相关概念。本文将对视频帧中的颜色空间进行介绍。
对于视频,我们都知道是由一系列的画面在一个较短的时间内(通常是 1/24 或 1/30 秒)不停地下一个画面替换上一个画面形成连贯的画面变化。这些画面称之为视频帧。
对于视频帧,在现代视频技术里面,通常都是用 RGB 颜色空间或者 YUV 颜色空间的像素矩阵来表示。在 ffmpeg 里面,我们可以看到源码 libavutil/pixfmt.h 中定义了一系列像素格式,绝大部分都是 RGB 和 YUV 颜色空间类型。
enum AVPixelFormat {
// ... 省略部分不怎么重要的类型
///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
AV_PIX_FMT_YUV420P,
///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr
AV_PIX_FMT_YUYV422,
///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
AV_PIX_FMT_YUV422P,
///< packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1
AV_PIX_FMT_UYVY422,
///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
AV_PIX_FMT_YUV444P,
///< planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
AV_PIX_FMT_YUV440P,
///< packed RGB 8:8:8, 24bpp, RGBRGB...
AV_PIX_FMT_RGB24,
///< packed RGB 8:8:8, 24bpp, BGRBGR...
AV_PIX_FMT_BGR24,
///< packed ARGB 8:8:8:8, 32bpp, ARGBARGB...
AV_PIX_FMT_ARGB,
///< packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
AV_PIX_FMT_RGBA,
///< packed ABGR 8:8:8:8, 32bpp, ABGRABGR...
AV_PIX_FMT_ABGR,
///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
AV_PIX_FMT_BGRA,
///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), big-endian
AV_PIX_FMT_RGB565BE,
///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), little-endian
AV_PIX_FMT_RGB565LE,
///< packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), big-endian , X=unused/undefined
AV_PIX_FMT_RGB555BE,
///< packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), little-endian, X=unused/undefined
AV_PIX_FMT_RGB555LE,
///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), big-endian
AV_PIX_FMT_BGR565BE,
///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), little-endian
AV_PIX_FMT_BGR565LE,
///< packed BGR 5:5:5, 16bpp, (msb)1X 5B 5G 5R(lsb), big-endian , X=unused/undefined
AV_PIX_FMT_BGR555BE,
///< packed BGR 5:5:5, 16bpp, (msb)1X 5B 5G 5R(lsb), little-endian, X=unused/undefined
AV_PIX_FMT_BGR555LE,
}
每个类型的注释开头要么是 packed
要么是 planar
,YUV 类型后跟着三个数字 4:2:0、4:2:2、4:4:4 等等,这些都表示什么?带着这些疑问,开始搜索资料研究学习 RGB 和 YUV 颜色空间相关和像素格式的概念。
RGB 和 YUV 都是颜色空间的一种。RGB 是目前运用最广的颜色系统之一,在现代显示器上基本都是采用 RGB 颜色标准。RGB 的原理是把颜色分为红、绿、蓝三个通道,每个通道按照不同比例混合来描述一个颜色。YUV 是用一个 亮度 分量和两个 色度 分量来描述一个颜色,Y 表示亮度,U 和 V 表示色度。YUV 的最大特点是将亮度信息和色彩信息分离,没有了色彩信息依旧可以显示一张完整的黑白图片。
对于前端开发者来说,在 CSS 中经常会用到 RGB 或 RGBA 的颜色数值,RGB 格式非常好理解,R、G、B 分别表示红绿蓝三个通道的值。RGB 格式根据存储的位数可以分为 16 位格式 、 24 位格式 和 32 位格式。在 FFmpeg 的源码中也可以看到 16bpp、24bpp 和 32bpp 的注释说明。(因为内存的 字节顺序 有大端序和小端序区别,RGB 可能被表达为 BGR 顺序,本质上是一样的)
16 位格式主要是 RGB555 和 RGB565 两种表达方式。RGB555 是每个通道分量占 5 位,空出一位不用。RGB565 则顾名思义,R 和 B 通道占 5 位,G 通道占 6 位。
# RGB555
XRRR RRGG GGGB BBBB
# RGB565
RRRR RGGG GGGB BBBB
24 位格式和 32 位格式我们最常用到,RGB24 表示每个颜色通道分量占 8 位,共 24 位。RGB32 表示除了每个颜色通道分量占 8 位外,还有 8 位用于表示透明通道,又称 RGBA 或 ARGB 等。
# RGB24
RRRR RRRR GGGG GGGG BBBB BBBB
# RGB32
RRRR RRRR GGGG GGGG BBBB BBBB AAAA AAAA
YUV 是一种彩色编码系统,主要用在视频、图形处理流水线中 (pipeline)。相对于 RGB 颜色空间,设计 YUV 的目的就是为了编码、传输的方便,减少带宽占用和信息出错。
YUV 编码系统是 Y’UV、YUV、YCbCr、YPbPr 等色彩空间的统称。由于历史关系,Y’UV、YUV 主要用在彩色电视中,用于模拟信号表示。YCbCr 则用于数字视频、图像的压缩和传输,如 MPEG、JPEG。由于数字信号的普及,目前 YUV 大多数时候指的是 YCbCr。
对于显示器来说,显示图像都是用 RGB 格式,所以需要先把 YUV 格式转换成 RGB。
从 YUV 转换到 RGB 有公式:
R = Y + 1.13983 * V
G = Y - 0.39465 * U - 0.58060 * V
B = Y + 2.03211 * U
从 RGB 转换到 YUV 的公式:
Y = 0.299 * R + 0.587 * G + 0.114 * B
U = -0.14713 * R - 0.28886 * G + 0.436 * B
V = 0.615 * R - 0.51499 * G - 0.10001 * B
对于单个像素来说,像素数据都是由 Y/U/V 三个通道的数据来组成。但对于一整张图片来说,数据存储不一定是每个像素数据按顺序排列,在电视信号传播过程中,由于存储和发送的限制,信号处理中会减少部分信息来降低负荷。基于前提人眼对色度的敏感度不及亮度的敏感度,因此可以压缩色度同时可以极小化对图像表达的影响。YUV444、YUV422、YUV420 这些 YUV 后面跟数字的表示 YUV 的采样方式。YUV 格式主流的采样方式主要有 YUV 4:4:4 、YUV 4:2:2 、YUV 4:2:0。(这里的采样可以简单理解为从原始 RGB 图像转换成 YUV 图像的过程)
视频系统的抽样系统中通常用一个三分比值表示:J:A:B(例如4:2:2),形容一个以J个像素宽及两个像素高的概念上区域。
YUV 444 采样又称全采样,意思是每个Y分量使用一个UV分量,得到的图像和原始RGB图像的大小是一样的。
YUV 4:2:2 的意思是 Y 分量和 UV 分量按 2:1 的比例采样,每两个 Y 分量共享一个 UV 分量。这么就有一半的像素点的数据大小是原来的 1/3,则整个图像的大小就会是原图像大小的 2/3。
YUV 4:2:0 是目前比较常用的视频帧采用的格式。字面理解就是对第一行像素,Y 分量和 UV 分量按 2:1 的比例进行采样,第二行像素不采样 UV 分量。采样示意图如下:
在上述代码注释中,开头不是 planar 就是 packed。planar 和 packed 表示的是图片数据的存储格式。
Packed 格式简单理解就是每个通道分量连续交替存储。RGB 格式基本都是 Packed 格式,因为数据排列都是 RGBRGBRGBRGB... 。YUV 中常见的 packed 方式存储的格式有 YUYV 格式 和 UYVY 格式,这两种都是基于 YUV 4:2:2 采样的格式。
YUYV
排列顺序举例 Y0U0Y1V0 Y2U2Y3V2,Y0 和 Y1 共享 U0V0 分量,Y2 和 Y3 共享 U2V2 分量。
UYVU
排列顺序举例 U0Y0V0Y1 U2Y2V2Y3,跟 YUYV 差异在于 UV 分量放在前面。
Planar 平面格式,指先连续存储所有像素点的 Y 分量,再存储 U 分量,最后才是 V 分量。典型的例子有 I420(视频中最常用),基于 YUV 4:2:0 采样格式。以 4 * 4 像素为例,排列方式如下:
每四个 Y 分量共享一个 UV 分量,共享关系如图所示。
在查阅资料 YUV 相关资料的时候,发现有太多的格式类型,但原理都差不多一样。可想而知在数字信号发展过程没有统一标准各种方案满天飞的时代是多么的混乱。
FFmpeg 提供了 YUV 转换成 RGB 的方法,但查阅源码发现是基于 CPU 运算的。YUV 和 RGB 的转换公式可以表达成矩阵相乘的形式
根据一切可以写成矩阵相乘的运算都可以利用 GPU 来加速原则,后续继续研究使用 GPU 加速 YUV 转换成 RGB 的方法,提高在业务侧落地时的性能。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8