• ImageReader回调YUV数据转换成JPEG图片


    WHO

            YUV数据百度一搜到处都是,在这里不在赘述,不不,还是赘述一下,毕竟下文还是需要一些理论,抛砖引玉一下:YUV是一种色彩空间,Y通道是指亮度,UV通道代表颜色差,没有UV图片数据就是黑白。衍生的YUV数据格式有很多,不同的采样和存储方式都会造成格式的差异。

            在这里分享一篇很有启迪性的YUV数据介绍博客:

            https://zhuanlan.zhihu.com/p/384455058 (谢谢大兄弟)

    按照采样方式可以分为:

            YUV 420,由 4 个 Y 分量共用一套 UV 分量(有损)

            YUV 422,由 2 个 Y 分量共用一套 UV 分量(有损)

            YUV 444,不共用,一个 Y 分量使用一套 UV 分量(无损)

    按照存储通道可以分为:

            Planar YUV 三个分量分开存放(Y、U、V三通道)

            Semi-Planar Y 分量单独存放,UV 分量交错存放(Y、UV或VU两通道)

            Packed YUV 三个分量全部交错存放(YUV或者YVU单通道)

            本文主要就是介绍在imageReader回到YUV420_888数据,这个格式的数据使用的存储方式是Semi-Planar方式,也就是UV交错存放在同一个通道中。Image回到的数据实际上也是有三个planes,其中image.getPlanes()[0]是Y通道数据,image.getPlanes()[1]是UV交错数据,image.getPlanes()[2]是VU交错数据。看我们后期怎么搭配,如果planes 0和planes 1搭配就形成yuv420类型中的NV12格式数据,如果planes 0和planes 2搭配就是yuv420类型中的NV21格式数据。

    WHY

            为什么会有YUV数据转换为JPEG,如果你有camera开发基础的同仁,你一定会想要获取JPEG数据可以在实例化imagereader时就设置format为JPEG格式,然后就能在回调中直接获取JPEG,我的回答:我也是这样想的。没有必要还需要这样转一下,增加开发周期。但是当我们有实际需求场景时我们就不会这样想了,我之所以这样做就是开发过程发现获取JPEG会让预览有短暂卡顿,通过这种当时很好的改善了卡顿。当然这只是这个应用场景之一,我相信产品的需求会让这个功能发光发彩的。

    WHRER

            Imagereader回调中:public void onImageAvailable(ImageReader imageReader),当然你可以是任何yuv数据环境中;

    WHAT/HOW

            1、转换接口函数,实例化YuvImage,然后利用其compressToJpeg方法实现bitmap的输出。唯一遗憾的是YuvImage目前仅仅支持一种格式NV21。

    1. private static Bitmap YuvTansformJpeg(byte[] data, int width, int hight, int quality) {
    2. Log.i(TAG,"Yuv开始转换Jpeg");
    3. try {
    4. YuvImage image_jpeg =
    5. new YuvImage(data,ImageFormat.NV21,width,hight,null);
    6. ByteArrayOutputStream stream = new ByteArrayOutputStream();
    7. image_jpeg.compressToJpeg(new Rect(0, 0,width, hight), quality, stream);
    8. Bitmap jpeg_bitmap =
    9. BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.toByteArray().length);
    10. Log.i(TAG,"Yuv转换Jpeg完成");
    11. return jpeg_bitmap;
    12. } catch (Exception e) {
    13. Log.e(TAG,"failed :"+e);
    14. }
    15. return null;
    16. }

            2、数据组合函数,目的合并多个byte[ ],前面已经说过,需要构建NV12格式或者NV21格式数据,需要将不同通道数据合并,即多个byte[ ]数据合并。

    1. private static byte[] byteMergerAll(byte[]... values) {
    2. int length_byte = 0;
    3. for (int i = 0; i < values.length; i++) {
    4. length_byte += values[i].length;
    5. }
    6. byte[] all_byte = new byte[length_byte];
    7. int countLength = 0;
    8. for (int i = 0; i < values.length; i++) {
    9. byte[] b = values[i];
    10. System.arraycopy(b, 0, all_byte, countLength, b.length);
    11. countLength += b.length;
    12. }
    13. return all_byte;
    14. }

            3、imagereader回调函数实现整体函数,该函数可以直接移植,支持YUV_420_888和JPEG格式回调,各位同仁可以根据需求修改。

    1. private final ImageReader.OnImageAvailableListener imagereader_available
    2. = new ImageReader.OnImageAvailableListener() {
    3. @Override
    4. public void onImageAvailable(ImageReader imageReader) {
    5. Log.i(TAG,"开始回调图片数据!");
    6. Image image = imageReader.acquireLatestImage();
    7. int planes = image.getPlanes().length;
    8. Log.i(TAG,"length of getPlanes:"+planes);
    9. ByteBuffer[] buffers = new ByteBuffer[3];
    10. int[] number_buffers = new int[3];
    11. byte[][] bytes_plans = new byte[3][];
    12. for (int i = 0; i < planes; i++) {
    13. buffers[i] = image.getPlanes()[i].getBuffer();
    14. number_buffers[i] = buffers[i].remaining();
    15. Log.i(TAG,"测试相机 imagereader 回调数据大小 planes["+i+"]:"+number_buffers[i]);
    16. bytes_plans[i] = new byte[number_buffers[i]];
    17. buffers[i].get(bytes_plans[i]);
    18. //System.out.println(Arrays.toString(bytes_plans[i]));
    19. }
    20. image.close();
    21. if (planes > 1) {
    22. //创建多个yuv文件
    23. FileOutputStream Youtput = null;
    24. FileOutputStream UVoutput = null;
    25. FileOutputStream VUoutput = null;
    26. FileOutputStream YVUoutput = null;
    27. FileOutputStream YUVoutput = null;
    28. FileOutputStream Jpegoutput = null;
    29. try {
    30. // output = new FileOutputStream(new File(getExternalFilesDir("yuv"), "yout.yuv"));
    31. Youtput = new FileOutputStream("/sdcard/DCIM/Camera/y_out.yuv");
    32. UVoutput = new FileOutputStream("/sdcard/DCIM/Camera/uv_out.yuv");
    33. VUoutput = new FileOutputStream("/sdcard/DCIM/Camera/vu_out.yuv");
    34. YVUoutput = new FileOutputStream("/sdcard/DCIM/Camera/yvu_out.yuv");
    35. YUVoutput = new FileOutputStream("/sdcard/DCIM/Camera/yuv_out.yuv");
    36. Jpegoutput = new FileOutputStream("/sdcard/DCIM/Camera/yuvTansformJpeg.jpeg");
    37. //写入单个通道数据
    38. Youtput.write(bytes_plans[0]);
    39. UVoutput.write(bytes_plans[1]);
    40. VUoutput.write(bytes_plans[2]);
    41. //生成yuv数据 N12
    42. YUVoutput.write(bytes_plans[0]);
    43. YUVoutput.write(bytes_plans[1]);
    44. //生成yvu数据 N21
    45. YVUoutput.write(bytes_plans[0]);
    46. YVUoutput.write(bytes_plans[2]);
    47. byte[] yuv_buffer =byteMergerAll(bytes_plans[0],bytes_plans[2]);
    48. Log.i(TAG,"yuv_buffer size is :"+yuv_buffer.length);
    49. Youtput.close();
    50. UVoutput.close();
    51. VUoutput.close();
    52. YVUoutput.close();
    53. YUVoutput.close();
    54. //yuv420转换为jpeg格式
    55. if (yuv_buffer != null) {
    56. Bitmap Jpeg_bitmap = YuvTansformJpeg(yuv_buffer,1920,1080,80);
    57. if (Jpeg_bitmap != null) {
    58. Log.i(TAG,"succeccful Jpeg_bitmap");
    59. Jpeg_bitmap.compress(Bitmap.CompressFormat.JPEG,100,Jpegoutput);
    60. image_view.setImageBitmap(Jpeg_bitmap);
    61. Jpegoutput.flush();
    62. Jpegoutput.close();
    63. Log.i(TAG,"成功保存jpg文件");
    64. }
    65. }
    66. } catch (FileNotFoundException e) {
    67. Log.w(TAG, "failed", e);
    68. } catch (IOException e) {
    69. Log.w(TAG, "failed 2", e);
    70. }
    71. }
    72. Bitmap bitmap = BitmapFactory.decodeByteArray(bytes_plans[0], 0, number_buffers[0]);
    73. if (bitmap != null)
    74. image_view.setImageBitmap(bitmap);
    75. Log.i(TAG,"回调图片数据完成!");
    76. }
    77. };

    (谢谢,一起学习,欢迎交流)

  • 相关阅读:
    初级算法_排序和搜索 --- 合并两个有序数组
    “闭关修炼”这么久,吃透这些“微服务”笔记,足够面试涨10K
    java项目进度跟踪管理系统
    VScode配置Jupyter
    【面试】C/C++面试八股
    【虹科ELPRO - EMS系统】实现苏州某医药仓库温湿度自动监测 - 100% GxP合规(下)
    理解内存,让Android性能没有问题
    16、学习MySQL 正则表达式
    智能汽车进入HPC时代,这家本土芯片厂商如何领跑市场
    SpringBoot原理简介说明
  • 原文地址:https://blog.csdn.net/weixin_42023797/article/details/126234751