Android Camera2数据处理小结

本文总结了Android Camera2中YUV_420_888格式的数据处理,包括PixelStride与RowStride的概念,YV12和NV21的存储方式,并探讨了从YUV_420_888转换到I420的优化策略,通过C层处理显著提升了转换速度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android Camera2数据处理小结

由于项目需求变更,需要使用Camera2以支持跨页面访问相机,在这里记录一下对其数据处理和速度优化的思路

数据格式-YUV_420_888

相较于原本的Camera,Camera2的各方面都变得复杂,同样在数据这一块,相对于原本的NV21和YV12(这里记个坑,MX3的Camera1对YV12实际是不支持的,绿屏警告)等YUV420格式,它提供了一个混合型格式,即YUV_420_888。

先来看看API描述(详见ImageFormat.YUV_420_888):

   Multi-plane Android YUV 420 format
   This format is a generic YCbCr format, capable of describing any 4:2:0
   chroma-subsampled planar or semiplanar buffer (but not fully interleaved),
   with 8 bits per color sample.
   
   Images in this format are always represented by three separate buffers
   of data, one for each color plane. Additional information always
   accompanies the buffers, describing the row stride and the pixel stride
   for each plane.
   
   The order of planes in the array returned by
   {@link android.media.Image#getPlanes() Image#getPlanes()} is guaranteed such that
   plane #0 is always Y, plane #1 is always U (Cb), and plane #2 is always V (Cr).
   
   The Y-plane is guaranteed not to be interleaved with the U/V planes
   (in particular, pixel stride is always 1 in
   {@link android.media.Image.Plane#getPixelStride() yPlane.getPixelStride()}).
   
   The U/V planes are guaranteed to have the same row stride and pixel stride
   (in particular,
   {@link android.media.Image.Plane#getRowStride() uPlane.getRowStride()}
   == {@link android.media.Image.Plane#getRowStride() vPlane.getRowStride()} and
   {@link android.media.Image.Plane#getPixelStride() uPlane.getPixelStride()}
   == {@link android.media.Image.Plane#getPixelStride() vPlane.getPixelStride()};
   ).

简单来说,这就是一个混合的YCbCr格式,无论是何种YUV420格式,他都用三个平面将Y U V分量分开存储,具体到Image类中,即Image#getPlanes()获得的平面数组,其中plane[0]存放Y分量,plane[[1]]存放U分量,plane[[2]]存放V分量,可以看出,这样的设计对Java层的数据处理是非常友好的,我们不需要关心其具体数据格式,只需要根据参数考虑如何获取三个分量的数据。

话是这么说,但实际处理过程并不是那么友好,根据相机返回的具体格式的不同,实际在Image中存储的方式也是不同的,我们以YV12和NV21这两个Camera1中泛用的格式举例:

PixelStride&RowStride

这里先了解一下Plane中的两个相关属性:
PixelStride顾名思义,指像素跨度,即在一个平面中,U/V分量的取值间隔,这里我们可以知道即使UV分量分别存储在不同平面中,他们也不一定是连续存储的(即PixelStride不总是为1)

RowStride则是行跨度,说白了就是每行存放的数据量。

YV12

YV12是平面(Planar)存储的YUV420格式,即Y U V三个分量分开且连续的存储(PixelStride = 1),以Camera1的回调数据为例,我们可以理解为在一个byte[]数组中,分量以YYYYYYYYVVUU的形式存储,三个分量间没有交叉。在Image的Plane中,同样体现为此,以1920x1080的YV12数据为例
Y分量----- PixelStride = 1 RowStride = 1920 size = 2073600 YYYYYYYY
V分量----- PixelStride = 1 RowStride = 960 size = 518400 VV
U分量----- PixelStride = 1 RowStride = 960 size = 518400 UU
注:size 为 buffer.remaining()

根据我本人项目的需求,是需要数据转为I420格式,这种情况再为简单不过,直接拷贝数据即可(这里我只使用libyuv的I420Copy尝试过,java中直接拷贝没有试过)

NV21

NV21是半平面(Semi-Planar)存储的YUV420格式,即Y分量单独连续存储,UV分量交叉存储(PixelStride = 2),以Camera1的回调数据为例,我们可以理解为在一个byte[]数组中,分量以YYYYYYYYVUVU的形式存储,而在Image中,由于YUV_420_888格式强制分离了UV分量,我们可以分别从plane[1]和plane[2]中获取U、V分量,这时候的平面属性如下
Y分量----- PixelStride = 1 RowStride = 1920 size = 2073600 YYYYYYYY
V分量----- PixelStride = 2 RowStride = 1920 size = 1036800 V0V0
U分量----- PixelStride = 2 RowStride = 1920 size = 1036800 U0U0

注:这里的0只是占位符,不表示具体数据
可以看到这里的UV分量并不是连续存储的,因此取值不能直接拷贝,以U平面为例大致为如下逻辑:

//注意我这里没有考虑行跨度,只是举个栗子
int pixelStride = planes[2].getPixelStride();                //U平面步长
ByteBuffer buffer = planes[2].getBuffer();
if (null == buffer) return null;
byte[] uPlane = new byte[buffer.
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值