Android: Image类浅析(结合YUV_420_888)

转载自:Penguin

简介

Image类在API 19中引入,但真正开始发挥作用还是在API 21引入CameraDeviceMediaCodec的增强后。API 21引入了Camera2,deprecated掉了Camera,确立Image作为相机得到的原始帧数据的载体;硬件编解码的MediaCodec类加入了对ImageImage的封装ImageReader的全面支持。可以预见,Image将会用来统一Android内部混乱的中间图片数据(这里中间图片数据指如各式YUV格式数据,在处理过程中产生和销毁)管理。

本文主要介绍YUV_420_888格式的图片数据如何在Image中存储和管理。

从YUV420谈起

YUV即通过Y、U和V三个分量表示颜色空间,其中Y表示亮度,U和V表示色度。不同于RGB中每个像素点都有独立的R、G和B三个颜色分量值,YUV根据U和V采样数目的不同,分为如YUV444、YUV422和YUV420等,而YUV420表示的就是每个像素点有一个独立的亮度表示,即Y分量;而色度,即U和V分量则由每4个像素点共享一个。举例来说,对于4x4的图片,在YUV420下,有16个Y值,4个U值和4个V值。

YUV420根据颜色数据的存储顺序不同,又分为了多种不同的格式,如YUV420Planar、YUV420PackedPlanar、YUV420SemiPlanar和YUV420PackedSemiPlanar,这些格式实际存储的信息还是完全一致的。举例来说,对于4x4的图片,在YUV420下,任何格式都有16个Y值,4个U值和4个V值,不同格式只是Y、U和V的排列顺序变化。I420(YUV420Planar的一种)则为YYYYYYYYYYYYYYYYUUUUVVVV,NV21(YUV420SemiPlanar)则为YYYYYYYYYYYYYYYYUVUVUVUV。也就是说,YUV420是一类格式的集合,YUV420并不能完全确定颜色数据的存储顺序。

Image

这么多眼花缭乱的格式名字自然是不利于程序开发的,Image就这样横空出世了。

长和宽

对于YUV来说图片的宽和高是必不可少的,因为YUV本身只存储颜色信息,想要还原出图片,必须知道图片的长宽。Image保存有图片的宽和高,可以通过getWidth()getHeight()得到。

图片格式

每个Image当然有自己的格式,这个格式由ImageFormat确定。对于YUV420,ImageFormat在API 21中新加入了YUV_420_888类型,其表示YUV420格式的集合,888表示Y、U、V分量中每个颜色占8bit。既然只能指定YUV420这个格式集合,那怎么知道具体的格式呢?马上就来回答这个问题。

YUV分量

Y、U和V三个分量的数据分别保存在三个Plane类中,可以通过getPlanes()得到。Plane实际是对ByteBuffer的封装。Image保证了plane #0一定是Y,#1一定是U,#2一定是V。且对于plane #0,Y分量数据一定是连续存储的,中间不会有U或V数据穿插,也就是说我们一定能够一次性得到所有Y分量的值。

接下来看看U和V分量,我们考虑其中的三类格式:Planar,SemiPlanar和PackedSemiPlanar。

Planar

Planar下U和V分量是分开存放的,所以我们也应当能够一次性从plane #1和plane #2中获得所有的U和V分量值,事实也是如此。

下面是一段YUV420Planar格式Image解析的记录

image format: 35
get data from 3 planes
pixelStride 1
rowStride 1920
width 1920
height 1080
buffer size 2088960
Finished reading data from plane 0
pixelStride 1
rowStride 960
width 1920
height 1080
buffer size 522240
Finished reading data from plane 1
pixelStride 1
rowStride 960
width 1920
height 1080
buffer size 522240
Finished reading data from plane 2

Image中获得的图片格式是35,即YUV_420_888,一共有3个planes,图片分辨率为1920x1080,像素点个数为2073600;可以看到Y分量包含有全部的像素点,而U和V都只含有1/4的像素点,显然是YUV420。更为明显的是,Y分量中rowStride为1920,pixelStride代表行内颜色值间隔,取1表示无间隔,即对于一行1920个像素点每个都有独立的值,根据其buffer size可以得出共有1080行;而U分量中,一行1920个像素点只有960个值,即行内每两个像素点共用一个U值,根据其buffer size得出共有540行,即行间每两个像素点共用一个U值;这就是YUV420的采样了。

SemiPlanar

再来看看SemiPlanar,此格式下U和V分量交叉存储,Image并没有为我们将U和V分量分离出来

下面是一段YUV420SemiPlanar格式Image解析的记录

image format: 35
get data from 3 planes
pixelStride 1
rowStride 1920
width 1920
height 1080
buffer size 2088960
Finished reading data from plane 0
pixelStride 2
rowStride 1920
width 1920
height 1080
buffer size 1044479
Finished reading data from plane 1
pixelStride 2
rowStride 1920
width 1920
height 1080
buffer size 1044479
Finished reading data from plane 2

图片格式依然是YUV_420_888,Y分量与上述Planar中一样。但U和V分量出现了变化,buffer size是Y分量的1/2,如果说U分量只包含有U分量信息的话应当是1/4,多出来了1/4的内容,我们稍后再仔细看。注意到U中rowStride为1920,即U中每1920个数据代表一行,但pixelStride为2,代表行内颜色值间隔为1,就说是只有行内索引为0 2 4 6 ...才有U分量数据,这样来说还是行内每两个像素点共用一个U值,行间每两个像素点共用一个U值,即YUV420。

U和V的pixelStride都是2,我们从U和V中挑相同位置的20个byte值出来看看相互之间的关系。

124 -127 124 -127 123 -127 122 -127 122 -127 123 -127 123 -127 123 -127 122 -127 123 -127
-127 124 -127 123 -127 122 -127 122 -127 123 -127 123 -127 123 -127 122 -127 123 -127 123

上面一行来自U,下面一行来自V,最前面一个byte的索引值相同,且为偶数。可以明显发现U和V分量只是进行了一次移位,而这个移位就保证了从索引0开始间隔取值就一定能取到自己分量的值。所以可以简单来说U和V分量就是复制的UV交叉的数据。

这样想要获取U分量值的话只需要以pixelStride为间隔获取就好了,V分量也是一样。虽然你也可以只从U或V分量得到U和V分量的信息,但毕竟官方并没有保证这一点,多少有些风险。另外如果想要知道更多的细节,也可以去翻Android源码。

PackedSemiPlanar

这个简单点说,不知为何,在我的设备上PackedSemiPlanar和SemiPlanar的表现是一致的,也就是说,可能Android已经帮我们解决了Packed的问题,只有Semi留给我们自己解决。

综上,我们只需要根据pixelStride和rowStride就能在对应的plane中获取到相应的颜色数据,而不必知道具体的YUV420格式。

关于CropRect

根据官方文档的介绍是说,CropRect指定了图片内的一个矩形区域,只有这个区域内的像素才是有效的,但鉴于我目前还没碰到这个问题,也不好详细解释。不过有两点要注意,首先是坐标系的变换,一定要弄清楚Rect和图片的长和宽的关系;其次是U和V的偏移量问题,U和V中颜色点是Y的1/4,在Rect中要计算好U和V中数据的范围,避免发生错位等。

小结

本篇主要介绍YUV_420_888格式的图片数据如在Image中的存储和管理,其中重点又放在U和V分量的管理上。本篇算作对官方文档的一点补充,如果想要深入理解,还需从源码入手。

参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容