Camera2 YUV420_888
Camera2 YUV420_888转RGB
官网文档介绍
Android PAI 对 YUV420_888的介绍 ,大致意思如下:
它是YCbCr的泛化格式,能够表示任何4:2:0的平面和半平面格式,每个分量用8 bits 表示。带有这种格式的图像使用3个独立的Buffer表示,每一个Buffer表示一个颜色平面(Plane),除了Buffer外,它还提供rowStride、pixelStride来描述对应的Plane。
使用Image的getPlanes()获取plane数组:
Image.Plane[] planes = image.getPlanes();
它保证planes[0]
总是Y ,planes[1]
总是U(Cb),planes[2]
总是V(Cr)。并保证Y-Plane永远不会和U/V交叉(yPlane.getPixelStride()
总是返回 1 )。U/V-Plane总是有相同的rowStride
和pixelStride()
(即有:uPlane.getRowStride() == vPlane.getRowStride() 和 uPlane.getPixelStride() == vPlane.getPixelStride();
)。
U/V的平(Planar)面和半平面(Semi-Planar)
U/V的Planar存储(YUV420P)
我测试几个设备没有找到存储格式是Planar的设备,这里使用参考2的例子简单说一下:
Log.i(TAG,"image format: " +image.getFormat());// 从image里获取三个planeImage.Plane[] planes = image.getPlanes();for (int i = 0; i < planes.length; i++) {ByteBuffer iBuffer = planes[i].getBuffer();int iSize = iBuffer.remaining();Log.i(TAG, "pixelStride " + planes[i].getPixelStride());Log.i(TAG, "rowStride " + planes[i].getRowStride());Log.i(TAG, "width " + image.getWidth());Log.i(TAG, "height " + image.getHeight());Log.i(TAG, "Finished reading data from plane " + i);}
getPixelStride()
获取行内连续两个颜色值之间的距离(步长)。
getRowStride()
获取行间像素之间的距离。
输出如下:
image format: 35
pixelStride 1
rowStride 1920
width 1920
height 1080
buffer size 2073600
Finished reading data from plane 0
pixelStride 1
rowStride 960
width 1920
height 1080
buffer size 518400
Finished reading data from plane 1
pixelStride 1
rowStride 960
width 1920
height 1080
buffer size 518400
Finished reading data from plane 2
在ImageFormat中,YUV_420_888
格式的数值是35,如上所示,可知当前Preview格式是YUV_420_888
,根据image的分辨率是 1920 x 1080 ,像素点个数是2073600 。下面分别对plane[0]、plane[1]、plane[2]作分析。
- plane[0]表示Y,
rowStride
是1920 ,其pixelStride
是1 ,说明Y存储时中间无间隔,每行1920个像素全是Y值,buffer size 是 plane[0]的1/4 ,buffer size / rowStride= 1080
可知Y有1080行。 - plane[1]表示U,
rowStride
是960 ,其pixelStride
也是1,说明连续的U之间没有间隔,每行只存储了960个数据,buffer size 是 plane[0]的1/4 ,buffer size / rowStride = 540
可知U有540行,对于U来说横纵都是1/2采样。 - pane[2]和plane[1]相同。、
此时,YUV三个量分离,每一块数据单独存储在独立的plane里。此时的YUV420叫做YUV420P或I420,以分辨率8 x 4 为例其存储结构:
U/V的Semi-Planar存储 (YUV420SP)
Nexus 6P和东东那部三星机都属于此类,采用相同的代码输出如下:
image format: 35
pixelStride 1
rowStride 1920
width 1920
height 1080
buffer size 2073600
Finished reading data from plane 0
pixelStride 2
rowStride 1920
width 1920
height 1080
buffer size 1036800
Finished reading data from plane 1
pixelStride 2
rowStride 1920
width 1920
height 1080
buffer size 1036800
Finished reading data from plane 2
image格式依然是YUV_420_888
,分辨率是1920 x 1080 。
- plane[0] 是Y数据,从rowStride是1920和 pixelStride是1,可知每行1920个像素且Y数据之间无间隔,从
buffer size / rowStride = 1080
Y数据有1080行。 - plane[1] 是U数据,rowStride 是1920, rowStride是2 ,说明每行1920个像素中每两个连续的U之间隔了一个像素,buffer中索引为: 0 , 2 , 4, 6, 8 … 是U数据,即步长为2。 每行实际的U数据只占1/2 ,
buffer size / rowStride = 540
只有540行,说明纵向采样也是1/2 ,但buffer size 是 plane[0]的 1/2而不是1/4, 连续的U之间到底存储了什么数据,才使得buffer size 变为plane[0]的1/2了? - 同plane[1]。
通过如下方法分别打印Y、U、V三个buffer 到文件中(十六进制格式),来看一下plane[1]和plane[2]中存储数据的特点:
// Y-bufferByteBuffer yBuffer = planes[0].getBuffer();int ySize = yBuffer.remaining();byte[] yBytes = new byte[ySize];yBuffer.get(yBytes);// U-bufferByteBuffer uBuffer = planes[1].getBuffer();int uSize = uBuffer.remaining();byte[] uBytes = new byte[uSize];uBuffer.get(uBytes);// V-bufferByteBuffer vBuffer = planes[2].getBuffer();int vSize = vBuffer.remaining();byte[] vBytes = new byte[vSize];vBuffer.get(vBytes);String yFileName = "Y";String uFileName = "U";String vFileName = "V";// 保存目录File dir = new File(mRootDir + File.separator + "YUVV");if (!dir.exists()) {dir.mkdir();}// 文件名File yFile = new File(dir.getAbsolutePath() + File.separator + yFileName + ".yuv");File uFile = new File(dir.getAbsolutePath() + File.separator + uFileName + ".yuv");File vFile = new File(dir.getAbsolutePath() + File.separator + vFileName + ".yuv");try {// 以字符方式书写Writer yW = new FileWriter(yFile);Writer uW = new FileWriter(uFile);Writer vW = new FileWriter(vFile);for (int i = 0; i < ySize; i++) {String preValue = Integer.toHexString(yBytes[i]); // 转为16进制// 因为byte[] 元素是一个字节,这里只取16进制的最后一个字节String lastValue = preValue.length() > 2 ? preValue.substring(preValue.length() - 2) : preValue;yW.write(" " + lastValue + " "); // 写入文件if ((i + 1) % 20 == 0) { // 每行20个yW.write("\n");}}yW.close();for (int i = 0; i < uSize; i++) {String preValue = Integer.toHexString(uBytes[i]);String lastValue = preValue.length() > 2 ? preValue.substring(preValue.length() - 2) : preValue;uW.write(" " + lastValue + " ");if ((i + 1) % 20 == 0) {uW.write("\n");}}uW.close();for (int i = 0; i < vSize; i++) {String preValue = Integer.toHexString(vBytes[i]);String lastValue = preValue.length() > 2 ? preValue.substring(preValue.length() - 2) : preValue;vW.write(" " + lastValue + " ");if ((i + 1) % 20 == 0) {vW.write("\n");}}vW.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}
打开U.yuv和V.yuv :
U.yuv文件 :
80 7b 80 7b 80 7b 80 7b 80 7b 80 7b 80 7b 80 7c 80 7c 80 7c ...
V.yuv文件:
7b 80 7b 80 7b 80 7b 80 7b 80 7b 80 7b 80 7b 80 7c 80 7c 80 ...
将V.yuv错开一位 :
U : 80 7b 80 7b 80 7b 80 7b 80 7b 80 7b 80 7b 80 7c 80 7c 80 7c ...
V : 7b 80 7b 80 7b 80 7b 80 7b 80 7b 80 7b 80 7b 80 7c 80 7c 80 ...
可以发现U和V错开一位后,对应位相等,实际上:
plane[1] : UVUVUVUVUVUVUVUV...
plane[2] : VUVUVUVUVUVUVUVU...
这就是为什么plane[1]和plane[2]的buffer size 是plane[0]的1/2而不是1/4的原因。
看8 x 4的NV12存储结构(NV21只是UV交错顺序相反):
结论
plane[0] + plane[1] 可得NV12
plane[0] + plane[2] 可得NV21
参考3中获取I420和NV21的方法是:先从plane[0]中提取出Y数据,然后在plane[1]中提取U数据,最后在plane[2]中提取V数据。
两种方法通过Shader解码后都已得到正确的预览图像。
注意
如上两个工具不能打开以上所得的NV12和NV21图像,但若是以plane[0] + plane[1] + plane[2] 顺序写入文件,在上述工具中选择NV12格式可以打开。
参考
参考1 : ImageFormat#YUV_420_888
参考2 : Android: Image类浅析(结合YUV_420_888)
参考3 : Android: YUV_420_888编码Image转换为I420和NV21格式byte数组
Camera2 YUV420_888相关推荐
- 安卓camera2 API获取YUV420_888格式详解
安卓音视频开发中的一个环节是摄像头采集数据,Android平台上摄像头采集的API有两套,camera1和camera2.本文主要讲的是camera2这套API采集数据,并指明YUV420_888格式 ...
- android Camera2 API适配百度人脸识别SDK
Camera2 API替换Camera API之后的问题 camera和camera2的最主要区别之一就是camera2不再支持nv21的输出,通常我们为了使视频预览更加的流畅,会采用YUV_420_ ...
- 简单的Android Camera2与BoofCV
目录 介绍 自动聚焦算法 源代码亮点 第一步:在Android Studio中创建一个新项目 第二步:依赖性 第三步:Android权限 第四步:MainActivity:OnCreate() 第五步 ...
- 史上最强的YUV转换RenderScript,支持转换成RGBA,BGRA,并且同时支持旋转,翻转,YUV数据格式支持NV21, YV12, YUV420_888, 分别对应Android Camer
个人觉得libyuv性能更好,建议使用libyuv,地址:https://www.raoyunsoft.com/wordpress/index.php/2020/05/25/androidlibyuv ...
- android camera2拍照图像输出过慢,华为手机比较明显
最近在camera2自定义相机拍照,在点击拍照按钮回调,在处理图像流的时候总是卡主 尤其是华为手机,几乎所有手机都会拍照后拿不到imageReader读取的image 然后我加了个300ms的延迟后, ...
- android 自定义录像机,android-camera2 - 将自定义捕获请求构建器选项设置为使用Camera2 API捕获图像以使用OpenCV库进行摄像机校准 - 堆栈内存溢出...
我们正在使用Camera2 API捕获N张图像,而未设置任何自定义捕获请求构建器选项. 我们正在使用这些图像通过OpenCV Android库411进行相机校准. 然后,我们正在使用OpenCV An ...
- android camera2获取摄像头支持的分辨率
android camera2 获取摄像头支持的分辨率 41的for循环我注释了,代码是获取最匹配的分辨率. private Size getMatchingSize2(){Size selectSi ...
- android Camera2
控件: import android.view.TextureView; 这个是代码: https://github.com/plumcot/Camera2Demo/blob/master/src/c ...
- android.hardware.camera2使用指南
API 21中将原来的camera API弃用转而推荐使用新增的camera2 API,这是一个大的动作,因为新API换了架构,让开发者用起来更难了. 先来看看camera2包架构示意图: 这 ...
最新文章
- apollo mqtt linux qt,MQTT第5版更新,以及如何应用到Qt MQTT模块中
- git与eclipse集成之代码冲突与解决
- mysql图片路径varchar大小_Mysqlvarchar大小长度问题_MySQL
- JUC多线程:Atomic原子类与CAS原理
- 九、多表模型创建,一对一,一对多,基于对像的多表模型等
- XCode 单独debug area窗口
- ORA-12505, TNS:listener does not currently know of SID given in connect descriptor异常
- 网络批量后修改服务器,企业网络批量安装服务器搭建案例
- php 测试数据整数,PHP中将字符串转化为整数(int) intval() printf() 性能测试
- 男生是学计算机网络还是应用,男生适合读计算机网络技术专业吗
- python怎样定义font_无法在matplotlib中使用自定义字体
- SWUST OJ 190: 游程编码
- matlab实现对图像的简单幻方置乱
- 矩阵与行列式计算注意点
- 互联网晚报 | 10月31日 星期日 | 网易云音乐旗下首家酒吧落地上海;B站上线童年动画专区;英特尔发布第12代酷睿芯片...
- 【abaqus demo】6.1平板拉伸-弹塑性分析实例
- bt 下载工具 deluge 配置 优化 使用
- App测试中IOS和安卓测试的区别
- js实现上下左右移动小方块
- SWFObject.js入门
热门文章
- 多张图片合成一张jpg工具_简单实用!3个手机拼图APP,让多张图片变为1张!
- dostyle 东格TJ3401 显示器 固件升级
- 服务器系统盘如何克隆,如果把云服务器系统盘克隆
- linux给文件夹加密码,如何使用linux命令给文件上锁?linux命令文件加密方法
- 我参加NVIDIA Sky Hackathon 训练文件的路径设置
- 计算机输入法切换用户,输入法切换不出来电脑输入法不见了的最佳解决方案
- uniapp h5 微信打开双标题处理
- 《自然语言处理实战入门》 深度学习组件TensorFlow2.0 ---- 文本数据建模流程
- 0004-1-模型好坏评判标准
- [图像识别]相似图片搜索的原理