Bootstrap

Android中的图像格式

一、前言

在音视频开发过程中,熟悉图像格式(图像编码)是十分有帮助的。这属于音视频开发者的基本功,就像计算机组成原理、数据结构之于普通程序员。

本文主要面向在Android平台进行音视频开发的人员。当然,如果您是iOS、嵌入式设备或者其他平台的音视频开发者,本文也能起到一定的参考借鉴作用。

二、背景知识

2.1 图像编码和存储

我们所看到的图像,实际是光经过反射而来。根据光学理论,各种颜色的光是由红(R)、绿(G)、蓝(B)以不同的比例组成。我们知道,计算机世界是数字的、抽象的,那它是如何来描述一张图像,或者再具体点,如何描述一个像素的呢?

以RGB颜色模型为例,我们可以用分别占1字节大小的R、G、B组合起来描述一种颜色,这样一个像素需要花3字节的空间来存储;我们也可以用2字节大小的R、G、B来描述一种颜色,这样一个像素需要6字节存储。显然,不难推断:描述R、G、B用的比特位越多,所能描述的颜色也就越多,存储空间占用的也就越多

考虑到实际的需求与资源限制,通常需要做trade-off,即找到一个平衡点。这就诞生了形形色色的编码、采样、存储方式。

这里本文以问答形式,对本文即将用的一些知识点作简单介绍,其余部分不作深入。感兴趣的读者可以与笔者交流或自行搜索资料。

Q:YUV和YCbCr是什么关系?

A:U = CbV = Cr

Q:注意到部分YUV格式有后缀,比如YUV_420_888、YUV_422_888、YUV_444_888,它们有什么不同呢?

A:采样方式的不同。最末尾的888表示三个分量各自占据的宽度,即Y、U、V分别用一个长度为8的二进制序列表示;两个"_"中间的三个数字表示由Raw转换到Yuv时的一个采样方式,比如:

  • :每四个像素点,共用4个Y分量、1个U分量、1个V分量表示。

  • :每四个像素点,共用4个Y分量、2个U分量、2个V分量表示。

  • :每四个像素点,共用4个Y分量、4个U分量、4个V分量表示。

注意到,其实只有UV分量的数量不同,这样做是基于人眼对色度信息不敏感的特性,通过降低UV分量数量,达到减少图像体积的目的。让我们用一张图来阐述它们:

Q:听说过planar、packed,semi-planar,这些又是什么?有什么不同。

A:这些是存储方式。以YUV格式为例,一个像素点由Y、U、V三个分量构成,

  • planar:就是把整张图像单个分量各自存储在一个数组中,每组分量紧挨在一起。

  • packed:就是把一个像素的Y、U、V分量“打包”放在一个数组,然后一个像素一个像素挨着存储。

  • semi-planar:是前两者的混合,一部分分量用planar方式存储,另一部分分量用packed分量存储。

2.2 图像传感器(Image Sensor)

图像传感器上分布了大量的感光单元,将接收到的光信号转换为电信号。众多感光单元一起,组成了色彩滤波阵列(Color Filter Array,简称)。

常见的感光单元可分为两大类:能够同时输出RGB三个分量的RGB感光单元、只能输出单个分量的感光单元。我们当然希望用前者作为Image Sensor的主材料,但遗憾的是,前者成本高,工艺复杂,所以在实际的市场上,后者流通更为主流。

如果一个像素点只能输出一种颜色的信息,那我们怎么做才能还原出完成的图像呢?比较容易想到的是邻近差值策略,即一个像素点输出一种颜色分量信息,它附近的其他像素点输出其他分量的信息,再将这些信息以某种策略汇合,从而得到某一点“完整”的所有分量信息。

这里比较出名的是拜耳滤波阵列(Bayer Filter),它的CFA排列如下:

这种CFA以nxn个像素矩阵为一个单元,每个单元由50%的绿25%的红25%的蓝组成。

为什么绿色占的比重高?这源自一个理论:人眼对绿色更敏感。具体原因还请自行查找相关资料。

比较常见的是以2x2像素矩阵为一个单元,即RGGB,也叫BGGR、RGBG、GRBG,它们指的是同一个东西的不同表现。

关于CFA,还有两个概念感兴趣的读者可以搜索下:QuadCFA和九合一CFA。(见参考资料2、3)

2.3 Android图像格式的两个类

Android上,表示图像格式的,主要有两个类:和它们都位于包内,有一些重叠之处,前者用的更多一些,后者中的许多值已经被为废弃(Deprecated)。本文主要介绍中的图像格式。

下面将会大致按类别和使用频率介绍主要图像编码格式,每个格式尽可能的会用图来展示它的存储方式。

注:如果没有特别说明,单个分量默认为占用8bit(即一字节)的宽度。

三、Android中的图像格式之

个人习惯将格式划分为:Raw、Yuv和其他格式(Jpeg、PNG等),这三大类。前两者主要用于图像传输与算法处理,后者更多用作呈现。

3.1 YUV

3.1.1

420采样,semi-planar方式存储的一种格式。Y分量单独用一个planar存储,UV分量以V分量在前,U分量在后的方式交错存储

Android Camera API1相机预览的默认格式(引证:Android CDD,7.5.2节,C-0-1和C-0-2条目)。

注:如果使用Camera API2,需要自己实例化一个ImageReader类来获取YUV Callback,设置其宽高和格式。切不可认为API2的预览就一定是NV21!!!(尽管大部分情况下是)

3.1.2

类似于,却别在于UV分量存储的先后顺序

3.1.3

420采样,可以以planarsemi-planar方式存储的一种格式。一般而言,应用无需考虑它具体的存储方式,因为通过接口,总能保证返回的第一个plane是Y分量,第二个plane是U分量,第三个plane是V分量

值得注意的是,不同的安卓机器上,大概率会存在字节对齐现象,这点在处理时尤其要注意。

stride信息可以通过和获取到。

和和它类似,却别在于取样方式。关于取样方式,前文2.1节中已经介绍过。

3.1.4

420采样,semi-planar方式存储的一种格式,单个分量用16bit小端的数值表示(低6位始终为0)。是Android S(Android 12)上新增的一个图像格式。

3.2 Raw

3.2.1

像素紧密排列,每个占据10bit的宽度,通常表示为未处理的Bayer格式数据。

这里的紧密排列注意一下。所谓紧密,是指像素与像素之间没有多余的元素。我们知道,计算机是以字节为基本存储单位,而每个像素又是10bit的,故而它是5字节存储了4个像素的信息,且遵循如下规则:

  • 前4字节存储了每个像素的高8位信息。

  • 第5个字节存储了这四个像素的低2位信息。

所以实际上是下面这样排列:

3.2.2

与类似,区别在于它的每个像素以12bit表示。其存储排列遵循如下规则:

  • 3个字节存储2个像素的信息。

  • 前两个字节存储了每个像素的高8位

  • 第3个字节存储了每个像素的低4位

3.2.3

跟Sensor密切相关,一般不会拿来使用(除了手机厂商或和手机厂商有相关合作),如果确实需要使用,则需要通过拿到相关属性后才能正确解析数据。操作十分复杂,且不具有通用性,不推荐使用。

格式的每个像素需要用2字节存储。

3.3 Others

3.3.1

是私有格式,和平台实现有关,主要在App层以下使用。安卓定义此格式,其目的是给厂商开放拓展能力,借助平台能力,在设备的frameworks/HAL之间能够高效、低功耗的共享和处理数据。

Camera API2的预览格式一般默认就是这种。

像高通会将这个实现为他们的私有格式,有的手机厂商出于某些目的和需求,又会将它的实现改为或者。

总而言之,对音视频开发人员来说,需要记住:Camera API2的预览格式不一定默认是NV21

3.3.2

类似与,也是一种私有格式,厂商可能会用到,一般应用开发者用不到这个。

注意区别于,前者是无法通过现有公共API接口获取到的数据,去解析数据的一种格式;而后者,则是可以通过framework API拿到相关信息,进而做解析的一种图像格式。

四、结束语

各式各样的图像格式是进行图像处理所避不开的一道关卡,有时碰到的一些玄学问题可能正是因为其中的一两个大意导致的,例如未考虑某些格式的内存对齐导致图像显示异常。

当我们熟稔常见格式的特点后,后期进行编码设计时,遇到的问题、代码中留的隐患会更少。能够熟练、详尽地描述出各个格式的采样方式、存储方式、应用场景等特点,应是身为音视频开发者的一个基本功。

参考资料: