视频基础(一):如何理解图像
视频图像的相关参数
像素
像素这一概念相信大家都知道,在计算机的世界中,图像正是由一个个像素点组成的。像素是图像的基本单位。直观点说像素就是一个带有颜色的小块。
分辨率
分辨率是图像的大小和尺寸。在计算机图像中,我们使用像素个数来表示图像的尺寸,所以分辨率就是像素的个数,我们一般使用宽x高来表示,如1920x1090分辨率,就表示宽度方向上由1920个像素点,高度方向上由1080个像素点。
分辨率越高,说明像素点就越多,那么图像的信息就越丰富,所以我们一般认为分辨率越高图像就越清晰。
不过这句话并不严谨,比如一张图片我们进行放大之后,分辨是很高的,但是我们看得却越模糊,这是因为放大的过程中图像会进行插值,很多像素都不是原始数据而是生成的。
所以我们可以这样理解,图像在成像时的分辨率越大,那么图像就越清晰。
位深
现在计算机中的图片都是彩色的,都有三个通道,为R、G、B通道,即红、绿、蓝三种颜色的通道。
每个像素的颜色就是通过这三个计算得到的。一般R、G、B各占8位,即1个字节,我们就称这种图像的位深位8位。8位可以表示256中颜色,256^3 = 1677万中颜色。
可以看到位深越大,能够表示的颜色值就越多。同样的图像所占的存储空间就越大。
Stride
stride是跨距,图像本身并没有这个概念,但是在将图像读取到内存的时候,为了能够快速读取一行像素,通常会对内存中的图像进行内存对齐,例如16字节对齐。
这就可以导致内存中的一行像素所占字节的大小可能要大于实际的像素宽度的,因此我们从内存中读取一行像素的时候,需要跳过填充的字节,否则就会导致图像花屏,出现一条条的斜线。
帧率
视频是由连续播放的视频组成。只要在1s内播放的图像数量达到10~12秒,人眼就会认为视频是流畅的。我们把1s内图像的数量叫做帧率,用来表示视频的流畅程度。
帧率越高,表示视频越流畅,同样地,表示每秒钟处理的图像数量会很高,从而对设备的性能就有所要求了。
码率
帧率越高,1秒内的图像数据量就会越大。所以为了节省空间,通常会在存储视频的时候先对图像进行压缩。
压缩后的视频文件的大小我们可以直接读取到,但是在实时通信或者直播的时候,视频是视频流的形式,没有固定大小的视频文件。因此我们就用视频流每秒的数据量x通信时长或者直播时长来计算出整个范围时间内视频流的数据大小。
每秒的数据量,即单位时间内数据量的大小,就是码率。码率越高,每秒传输的图像信息就越多,但是要注意这并不代表视频就越清晰,因为这个过程与采用的视频压缩算法也息息相关。
颜色空间
为了能够将现实的颜色在计算机中更方便的展示和处理,在不同的领域建立了多种不同的颜色空间。这里主要介绍RGB与YUV。
RGB
RGB相对来说比较简单。RGB图像的每一个像素都有R、G、B三个值。RGB图像也是我们平时遇到最多的一种图像颜色空间,摄像头采集的原始图形就是RGB、显示器显示的图像也是RGB图像。
RGB图像的三个值R、G、B依次排列存储,不过不一定是按照R、G、B的顺序,也可能是按照B、G、R的顺序排列。所以在存储和读取RGB图像的时候需要注意一下。
对于静态的图像来说,RGB是即简单也非常常用的颜色空间。但是在视频领域,更多地是使用YUV颜色空间来表示图像的。
YUV
RGB的三种颜色通道,每一个都和一个颜色有关。但是YUV是不同的,YUV图像将亮度信息Y与色彩信息UV分开了。
Y表示亮度,是图像的总体轮廓,U、V表示色度,用来描述图像的色彩信息。采用YUV的好处在于,一个Y分量就可以表示出一张图像,只不过是黑白图像。这是因为以前的电视是黑白电视,为了兼容于是有了YUV,黑白电视可以只使用Y分量。
YUV的类型主要有YUV4:4:4、YUV4:2:2、YUV4:2:0,其中最常用的是YUV4:2:0,因为这种类型占用的存储要少。
- YUV4:4:4。每1个Y共用一组UV。
- YUV4:2:2。每2个Y共用一组UV。
- YUV4:2:0。每4个Y共用一组UV。
YUV按照存储方式可以分为两大类,Planar和Packed两种。首先这两种方式都是先连续存储Y,然后Planar的U分量和V分量也是分别连续的;但是Packed是UV分量交错存储的。并且两种方式的UV先后顺序也是可以不同的,所以一个类型最多有4种存储类型。
不过YUV4:4:4比较特殊,只有Planar的两种方式。通过下面的表格总结一下。
YUV类型 | 存储类型 | |
YUV 4:4:4 | Planar(UV连续) | I444(uv) |
YV24(vu) | ||
YUV 4:2:2 | Packed(uv交错) | NV16(uv) |
NV61(vu) | ||
Planar(uv连续) | YU16(I422)(uv) | |
YV16(vu) | ||
YUV 4:2:0 | Packed(uv交错) | NV12(uv) |
NV21(vu) | ||
Planar(uv连续) | YU12(I420)(uv) | |
YV12(vu) |
RGB与YUV的转换
采集的原始图像和显示器选择的最终图像都是RGB,但是视频编码一般用的是YUV图像。那么中间过程一定发生了两者的转换。
在将转换之前,我们需要说明一下Color Range。对于一个8bit的RGB图像,理论上每个分量的取值为0~255。但是涉及到Color Range这个概念了。Color Range分为两种:
- Full Range:R、G、B的取值都是0~255.
- Limited Range:R、G、B的取值范围为16~235.
RGB和YUV的转换是有标准的,BT709(高清标准)和BT601(标清标准)定义了RGB和YUV互转的标准规范。所以在做RGB往YUV转换的时候我们需要告知接收方使用的哪种标准的那种Range才能正确地让接收方将YUV转为RGB。
两种标准的转换公式如下:
- BT601(Limited Range)
- RGB -> YUV
- Y = 0.299 * R + 0.587 * G + 0.114 * B
- U = -0.172 * R - 0.339 * G + 0.511 * B + 128
- V = 0.511 * R - 0.428 * G -0.083 * B + 128
- YUV -> RGB
- R = Y + 1.371 * (V-128)
- G = Y - 0.336 * (U-128) - 0.698 * (V-128)
- B = Y + 1.732 * (U-128)
- RGB -> YUV
- BT601(Full Range)
- RGB -> YUV
- Y = 16 + 0.257 * R + 0.504 * G + 0.098 * B
- U = 128 - 0.148 * R - 0.291 * G + 0.439 * B
- V = 128 + 0.439 * R - 0.368 * G - 0.071 * B
- YUN -> RGB
- R = 1.164 * (Y - 16) + 1.596 * (V - 128)
- G = 1.164 * (Y - 16) - 0.392 * (U -128) -0.812 * (V - 128)
- B = 1.164 * (Y - 16) + 2.016 * (U - 128)
- RGB -> YUV
- BT709(Limited Range)
- RGB -> YUV
- Y = 0.213 * R + 0.715 * G + 0.072 * B
- U = -0.117 * R - 0.394 * G + 0.511 * B + 128
- V = 0.511 * R - 0.464 * G - 0.047 * B + 128
- YUV -> RGB
- R = Y + 1.540 * (V - 128)
- G = Y - 0.183 * (U - 128) - 0.459 * (V - 128)
- B = Y + 1.816 * (U - 128)
- RGB -> YUV
- BT709(Full Range)
- RGB -> YUV
- Y = 16 + 0.183 * R + 0.614 * G + 0.062 * B
- U = 128 - 0.101 * R - 0.339 * G + 0.439 * B
- V = 128 + 0.439 * R - 0.339 * G - 0.040 * B
- YUV -> RGB
- R = 1.164 * (Y - 16) + 1.792 * (V - 128)
- G = 1.164 * (Y - 16) - 0.213 * (U - 128) - 0.534 * (V - 128)
- B = 1.164 * (Y - 16) + 2.114 * (U - 128)
- RGB -> YUV
要注意,YUV图像在内存中同样会stride的概念,所以一定要注意跳过填充字节,否则同样会出现花屏。
编码采用YUV的优点
前面我们已经介绍了YUV可以兼容黑白电视机的播放,但是对于现在几乎全部都是彩色影视,这个优点似乎并没有太大的优势。
采用YUV进行编码还有一个原因。那就是RGB具有相关性,即R、G、B的三个通道图像内容几乎一样,只是颜色不同而已,具有相关性,所以利用RGB进行编码的时候,三种图像同样重要,颜色又不同,因此编码没有什么好处。
而YUV是不具有相关性的,前面介绍YUV类型的时候就可以看出来,多个Y可以共用一组UV,这是因为Y分量已经具有了图像的整体轮廓,UV只是颜色分量而已,并且人眼对颜色不如轮廓敏感,因此我们可以缩少U、V的大小,这样编码的图像就小了很多,比如YUV420中,本身就比RGB图像小了一半,并且编码的时候Y、U、V可以独立编码,也非常方便。
图像缩放算法
在日常观看图像的时候我们经常会对图像进行放大和缩小,在网页观看视频的时候也是,我们在进入全屏或者退出全屏的时候,画面就会进行缩放。
在视频开发中,图像的缩放是非常重要的。列举一些场景:
- 播放窗口与原始图像分辨率不匹配时需要缩放。
- 观看视频时可以选择多种分辨率。
- RTC场景,根据网络状态来实时调节视频通话的分辨率。
如果没有进行图像缩放的话,那么视频的观看体验会是十分差劲的。同样的,采用的图像缩放算法的优劣也是十分重要的。
目前大部分图像缩放都是通过插值算法实现的,虽然具体的实现又很多中,但是基本原理都是差不多的。使用周围已有的像素值通过一定的加权运算得到插值像素值。主要包括:最近邻插值算法、双线性插值算法、双三次插值算法等。
假设原图像分辨率是w0 x h0,需要缩放到w1 x h1,那么目标图像中像素位置(x,y)映射到原图像就是(x * w0/w1, y * h0/h1),再插值得到这个像素值就是目标图像(x,y)位置处的像素值。
不同插值算法就是再计算插值时选择的周围像素值有所不同。
- 最近邻插值算法。找到原图像的映射位置之后,选择相邻四个点中距离最近的点作为目标像素。这种方式会导致相邻两个插值像素很大概率是相同的,放大图像会出现块效应,缩小图形容易出现锯齿。但是计算量小,速度快。
- 双线性插值。同样去相邻的四个像素,但是会将距离四个像素的元素作为权重,来计算出最终的插值。因为是在x,y两个方向上做线性插值,所以叫做双线性插值。
- 双三次插值。有了双线性插值,双三次插值就好理解了,就是在两个方向上做三次插值。不过选择的是周围的16个像素,并且采用的是一个特殊的BiCubic基函数来计算。
BiCubic基函数形式如下
通常将a取值为-0.5,带入上述公式之后就是
相邻16个点的x,y坐标的插值的绝对值绝大部分都在2以内,极少数会刚好为2。
目标像素在原图像的映射像素位置为p(u,v),相邻的16个点的像素都用p(x,y)表示,那么我们就将每一个相邻点的横纵方向上的插值都用上述公式计算出来开并相乘,就得到了这个相邻点的像素值在最终插值中所占的权重了,然后乘以该点的像素值,最后累加就得到了插值。
这三种插值的缩放效果是一个比一个好,但是同样的计算量也一个比一个高。速度一个比一个满。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!