前言RGB565RGB24ARGB_8888I420 和 NV21灰阶值是什么?既然都是YUV420格式的,I420 和 NV21 到底有何区别?总结参考资料
前言
此篇文章针对
Android
中图像处理工具 ImageUtils 的解释篇,有兴趣的童鞋可以 点击此处 了解这款开源工具。
针对图像,存在各种各样的格式,每个格式都不一样,而在Android中,常用的图像格式无外乎也就几种,下面就Android中常用的图像格式做相关说明。
RGB565
Android中自带的一种图像格式,这个图像格式像素排布如下图:
RGB565
每个像素点都有红、绿、蓝三个原色,其中 R
原色占用 5 bit
,G
原色占用 6 bit
,B
原色占用 5 bit
,也就是说一个像素点总占用 5 + 6 + 5 = 16 bit
。
一张 640 * 480
分辨率的 RGB565
图像,其占用的内存大小为:640 * 480 * 16 / 8 = 614400 byte
通常在处理一个图像数据时会涉及到步长,步长计算一般为下面的公式:
步长 = 图像的宽 * 每个像素点总 bit 数 / 8
因此,对于 640 * 480
的 RGB565
图像而言,其步长为:640 * 16 / 8 = 1280
RGB24
如上图,RGB24
其真正的排序是 BGR BGR ...
这种排序的,而并非 RGB RGB ...
这种排序。
同样的,RGB24
每个像素点都有蓝、红、绿三个原色 B
、G
、R
三个原色各占用 8 bit
,也就是说一个像素点总占用 24 bit
。
一张 640 * 480
分辨率的 RGB24
图像,其占用的内存大小为:640 * 480 * 24 / 8 = 921600 byte
。
同样的,对于 640 * 480
的 RGB24
图像而言,其步长为:640 * 24 / 8 = 1920
ARGB_8888
如上图,ARGB_8888
作为在Android中我们最常用的一种图像格式,其实其真正的图像格式为 RGBA
。
ARGB_8888
除了包含 R
、G
、B
常用的三原色以外,还包含一个透明度 Alpha
,而且一个像素点中的每个通道各占 8 bit
,一个像素点总计: 8 * 4 = 32 bit
。
也就是说,对于一张 640 * 480
分辨率的 ARGB_8888
图像,其占用的内存大小为:640 * 480 * 32 / 8 = 1228800 byte
。
而其对应的步长为:640 * 32 / 8 = 2560
I420 和 NV21
I420
和 NV21
都是属于 YUV
格式的数据。
所谓的 YUV
格式指的是图像的颜色编码采用的是 明亮度 和 色度来指定像素的颜色,而不是通常使用的三原色来指定。
例如下图:
上图是一个 YUV444
的图像格式。
Y
代表明亮度,也即灰阶值UV
代表色度,是描述影像色彩及饱和度,用于指定像素的颜色- 关于
YUV
与RGB
是如何互相转换的,您可以 参见这里
灰阶值是什么?
当
R = B = G
时候,像素点的颜色就呈现黑白色调,这种像素点拼接起来的图像呈现为灰度图,R = B = G
的通道值就是灰阶。
通常情况下彩色像素点转灰阶值一般采用大致于R:G:B=3:6:1
的算法计算灰阶值,实际上RGB
转换成YUV
时,Y
分量的计算公式为:
Y = 0.299 * R + 0.587 * G + 0.114 * B
其R
、G
、B
比例基本与3 : 6 : 1
吻合【具体的可 点击参考此处 】。
继续说回 YUV,针对 YUV444
这种格式,其采样比例为 Y
: U
: V
= 4 : 4 : 4
,也即 1 : 1 : 1
。
例如上图,其采样流是这样的:
Y1
U1
V1
Y2
U2
V2
Y3
U3
V3
Y4
U4
V4
Y5
U5
V5
...
也就是说采样时候每个像素的 Y
U
V
三个分量都是完整的,而在一个像素点中,一个完整的分量占用 8 bit
大小,
因此针对一个 640 * 480
大小的 YUV444
的图像,其真实的占用大小为:
640 * 480 * 8 * 3 = 921600 bit
跟 RGB24
占用的大小是一致的;同样的针对 YUV444
,其图像的步长一样跟 RGB24
一样,为 640 * 3 = 1920
。
针对这种完全采样的 YUV
格式,并没有给图像进行压缩,因此业界也很少使用 YUV444
这种格式对图像进程存储。
实际上,目前使用最多的 YUV
图像采集格式主要包含 YUV422
和 YUV420
这两种采集方式,而 I420
和 NV21
恰好是这种数据采集方式,YUV420
数据采集大概是下面这种方式:
针对 YUV420
这种采用方式,有以下几个特点:
- 每个像素的
Y
分量必须采集 - 采集时,每一行只扫描一种分量【或
U
或V
】,而且采样比例为Y
:U【V】
=2
:1
- 因为存在隔行采样,存在上下行像素的共用情况,因此,一般而言此类格式的图像宽和高值均为偶数
如上图,你会发现,每个像素原本均包含 Y
U
V
三个分量,而在进行 YUV420
采样的时候,每个 Y
分量都进行了采集,而 U
和 V
分量并非每一行都采集,而是先采集一行 U
分量在采集一行 V
分量,而且是每隔一个像素点采集一次的。上图第一行 对 Y
分量都采集了,而 U
分量只采集了 Y
分量的一半,V
分量没有采集;到第二行,Y
分量还是全部采集,此时 V
分量采集了,采集的数量是 Y
分量的一半,U
分量未采集。
也就是说其采样流是这样的:
Y1
U1
Y2
Y3
U3
Y4
Y5
V5
Y6
Y7
V7
Y8
Y9
U9
Y10
Y11
U11
Y12
Y13
V13
Y14
Y15
V15
Y16
同时映射成的像素点如上图蓝色虚线框中的方式进行映射,也即下面这种:
[Y1 U1 V5]
[Y2 U1 V5]
[Y3 U3 V7]
[Y4 U3 V7]
[Y5 U1 V5]
[Y6 U1 V5]
[Y7 U3 V7]
[Y8 U3 V7]
[Y9 U9 V13]
[Y10 U9 V13]
[Y11 U11 V15]
[Y12 U11 V15]
[Y13 U9 V13]
[Y14 U9 V13]
[Y15 U11 V15]
[Y16 U11 V15]
针对 YUV420
这种采样的图像,你会发现这种方式对图片做了一定的压缩,我们可以根据上面的方式计算,
对于一个 640 * 480
大小的图像,其占用的内存为:Y
分量:640 * 480 * 8 = 2457600 bit
U
和 V
分量总占用为 :640 * 480 * 0.5 * 8 = 1228800 bit
因此总占用内存大小为:640 * 480 * 8 + 640 * 480 * 0.5 * 8 = 3686400 bit = 460800 byte
同时可以计算出对于的 YUV420
格式的大小为 640 * 480
图像的步长为:640 + 0.5 * 640 = 960
既然都是YUV420格式的,I420 和 NV21 到底有何区别?
针对基于 YUV 4:2:0
采样的格式,主要分为两种类型:
YUV420P
- YU12,即I420
- YV12
YUV420SP
- NV12
- NV21
也就是说 I420
是属于 YUV420P
这种类型的,这种类型的格式存储有以下特点:
- 都是基于
Planar
模式【平面模式】进行存储的 - 在存储时,先存储所有的
Y
分量,然后在存储U
或V
分量 - 如果先存储的是
U
分量,再存储的是V
分量,则这种格式即YU12
格式,即我们所说的I420
格式 - 如果先存储的是
V
分量,再存储的是U
分量,则这种格式即YV12
格式
如下图,即为 一个分辨率为 8 * 4
的I420
图像的存储格式图:
我们可以把这种存储方式理解成三层,其中第一层固定存储 Y
分量,第二和第三层根据实际格式分别存储 U
和 V
(或 V
和 U
)
同样的,对应的 NV21
图像格式,它是属于 YUV420SP
这种类型的,此类型的存储格式有以下特点:
- 同样都是属于
Planar
模式【平面模式】进行存储的,而严格上来说,是属于biplanar
模式【双平面模式】 - 在存储时,先存储所有的
Y
分量,然后再将U
V
两个分量交替连续存储 - 如果以
U
和V
的循序交替存储时,则为NV12
格式 - 如果以
V
和U
的循序交替存储时,则为NV21
格式
下图为一个分辨率为 8 * 4
的 NV21
格式图像存储图:
至此,关于 I420
和 NV21
这两种图像格式就介绍完了,有关更多的关于 YUV
的其他格式,可点击 此处 进行了解。
总结
- 图像所占内存大小跟图像的实际格式及其相关
- 每个格式的图像都有自己对应的步长【或步幅】值,从在存储器一行像素在存储器像素的下一行的字节数
- RGB_565 格式图像步幅为:宽 * 2
- RGB24 格式图像步幅为:宽 * 3
- ARGB_8888 格式图像步幅为:宽 * 4
- I420 和 NV21 格式图像步幅为: 宽 * 1.5
- 图像在内存中所占用的字节大小,通常为 图像的高 * 步长
- 对于 I420 和 NV21 两种格式的图像,因涉及到隔行采样,上下行像素共用问题,一般此类图像的宽和高均为偶数