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总是有相同的rowStridepixelStride()(即有: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交错顺序相反):

结论

  1. plane[0] + plane[1] 可得NV12

  2. 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相关推荐

  1. 安卓camera2 API获取YUV420_888格式详解

    安卓音视频开发中的一个环节是摄像头采集数据,Android平台上摄像头采集的API有两套,camera1和camera2.本文主要讲的是camera2这套API采集数据,并指明YUV420_888格式 ...

  2. android Camera2 API适配百度人脸识别SDK

    Camera2 API替换Camera API之后的问题 camera和camera2的最主要区别之一就是camera2不再支持nv21的输出,通常我们为了使视频预览更加的流畅,会采用YUV_420_ ...

  3. 简单的Android Camera2与BoofCV

    目录 介绍 自动聚焦算法 源代码亮点 第一步:在Android Studio中创建一个新项目 第二步:依赖性 第三步:Android权限 第四步:MainActivity:OnCreate() 第五步 ...

  4. 史上最强的YUV转换RenderScript,支持转换成RGBA,BGRA,并且同时支持旋转,翻转,YUV数据格式支持NV21, YV12, YUV420_888, 分别对应Android Camer

    个人觉得libyuv性能更好,建议使用libyuv,地址:https://www.raoyunsoft.com/wordpress/index.php/2020/05/25/androidlibyuv ...

  5. android camera2拍照图像输出过慢,华为手机比较明显

    最近在camera2自定义相机拍照,在点击拍照按钮回调,在处理图像流的时候总是卡主 尤其是华为手机,几乎所有手机都会拍照后拿不到imageReader读取的image 然后我加了个300ms的延迟后, ...

  6. android 自定义录像机,android-camera2 - 将自定义捕获请求构建器选项设置为使用Camera2 API捕获图像以使用OpenCV库进行摄像机校准 - 堆栈内存溢出...

    我们正在使用Camera2 API捕获N张图像,而未设置任何自定义捕获请求构建器选项. 我们正在使用这些图像通过OpenCV Android库411进行相机校准. 然后,我们正在使用OpenCV An ...

  7. android camera2获取摄像头支持的分辨率

    android camera2 获取摄像头支持的分辨率 41的for循环我注释了,代码是获取最匹配的分辨率. private Size getMatchingSize2(){Size selectSi ...

  8. android Camera2

    控件: import android.view.TextureView; 这个是代码: https://github.com/plumcot/Camera2Demo/blob/master/src/c ...

  9. android.hardware.camera2使用指南

    API 21中将原来的camera API弃用转而推荐使用新增的camera2 API,这是一个大的动作,因为新API换了架构,让开发者用起来更难了.  先来看看camera2包架构示意图:    这 ...

最新文章

  1. apollo mqtt linux qt,MQTT第5版更新,以及如何应用到Qt MQTT模块中
  2. git与eclipse集成之代码冲突与解决
  3. mysql图片路径varchar大小_Mysqlvarchar大小长度问题_MySQL
  4. JUC多线程:Atomic原子类与CAS原理
  5. 九、多表模型创建,一对一,一对多,基于对像的多表模型等
  6. XCode 单独debug area窗口
  7. ORA-12505, TNS:listener does not currently know of SID given in connect descriptor异常
  8. 网络批量后修改服务器,企业网络批量安装服务器搭建案例
  9. php 测试数据整数,PHP中将字符串转化为整数(int) intval() printf() 性能测试
  10. 男生是学计算机网络还是应用,男生适合读计算机网络技术专业吗
  11. python怎样定义font_无法在matplotlib中使用自定义字体
  12. SWUST OJ 190: 游程编码
  13. matlab实现对图像的简单幻方置乱
  14. 矩阵与行列式计算注意点
  15. 互联网晚报 | 10月31日 星期日 | 网易云音乐旗下首家酒吧落地上海;B站上线童年动画专区;英特尔发布第12代酷睿芯片...
  16. 【abaqus demo】6.1平板拉伸-弹塑性分析实例
  17. bt 下载工具 deluge 配置 优化 使用
  18. App测试中IOS和安卓测试的区别
  19. js实现上下左右移动小方块
  20. SWFObject.js入门

热门文章

  1. 多张图片合成一张jpg工具_简单实用!3个手机拼图APP,让多张图片变为1张!
  2. dostyle 东格TJ3401 显示器 固件升级
  3. 服务器系统盘如何克隆,如果把云服务器系统盘克隆
  4. linux给文件夹加密码,如何使用linux命令给文件上锁?linux命令文件加密方法
  5. 我参加NVIDIA Sky Hackathon 训练文件的路径设置
  6. 计算机输入法切换用户,输入法切换不出来电脑输入法不见了的最佳解决方案
  7. uniapp h5 微信打开双标题处理
  8. 《自然语言处理实战入门》 深度学习组件TensorFlow2.0 ---- 文本数据建模流程
  9. 0004-1-模型好坏评判标准
  10. [图像识别]相似图片搜索的原理