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。

private static Bitmap YuvTansformJpeg(byte[] data, int width, int hight, int quality) {
    Log.i(TAG,"Yuv开始转换Jpeg");
    try {
        YuvImage image_jpeg =
                new YuvImage(data,ImageFormat.NV21,width,hight,null);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        image_jpeg.compressToJpeg(new Rect(0, 0,width, hight), quality, stream);
        Bitmap jpeg_bitmap =
                BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.toByteArray().length);
        Log.i(TAG,"Yuv转换Jpeg完成");

        return jpeg_bitmap;
    } catch (Exception e) {
        Log.e(TAG,"failed :"+e);
    }
    return null;
}

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

private static byte[] byteMergerAll(byte[]... values) {
    int length_byte = 0;
    for (int i = 0; i < values.length; i++) {
        length_byte += values[i].length;
    }
    byte[] all_byte = new byte[length_byte];
    int countLength = 0;
    for (int i = 0; i < values.length; i++) {
        byte[] b = values[i];
        System.arraycopy(b, 0, all_byte, countLength, b.length);
        countLength += b.length;
    }
    return all_byte;
}

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

private final ImageReader.OnImageAvailableListener imagereader_available
        = new ImageReader.OnImageAvailableListener() {

    @Override
    public void onImageAvailable(ImageReader imageReader) {
        Log.i(TAG,"开始回调图片数据!");
        Image image = imageReader.acquireLatestImage();

        int planes = image.getPlanes().length;
        Log.i(TAG,"length of getPlanes:"+planes);

        ByteBuffer[] buffers = new ByteBuffer[3];
        int[] number_buffers = new int[3];
        byte[][] bytes_plans = new byte[3][];
        for (int i = 0; i < planes; i++) {
            buffers[i] = image.getPlanes()[i].getBuffer();
            number_buffers[i] = buffers[i].remaining();
            Log.i(TAG,"测试相机 imagereader 回调数据大小 planes["+i+"]:"+number_buffers[i]);
            bytes_plans[i] = new byte[number_buffers[i]];
            buffers[i].get(bytes_plans[i]);
            //System.out.println(Arrays.toString(bytes_plans[i]));
        }
        image.close();

        if (planes > 1) {
            //创建多个yuv文件
            FileOutputStream Youtput = null;
            FileOutputStream UVoutput = null;
            FileOutputStream VUoutput = null;
            FileOutputStream YVUoutput = null;
            FileOutputStream YUVoutput = null;
            FileOutputStream Jpegoutput = null;
            try {
                // output = new FileOutputStream(new File(getExternalFilesDir("yuv"), "yout.yuv"));
                Youtput = new FileOutputStream("/sdcard/DCIM/Camera/y_out.yuv");
                UVoutput = new FileOutputStream("/sdcard/DCIM/Camera/uv_out.yuv");
                VUoutput = new FileOutputStream("/sdcard/DCIM/Camera/vu_out.yuv");
                YVUoutput = new FileOutputStream("/sdcard/DCIM/Camera/yvu_out.yuv");
                YUVoutput = new FileOutputStream("/sdcard/DCIM/Camera/yuv_out.yuv");
                Jpegoutput = new FileOutputStream("/sdcard/DCIM/Camera/yuvTansformJpeg.jpeg");

                //写入单个通道数据
                Youtput.write(bytes_plans[0]);
                UVoutput.write(bytes_plans[1]);
                VUoutput.write(bytes_plans[2]);

                //生成yuv数据 N12
                YUVoutput.write(bytes_plans[0]);
                YUVoutput.write(bytes_plans[1]);

                //生成yvu数据 N21
                YVUoutput.write(bytes_plans[0]);
                YVUoutput.write(bytes_plans[2]);

                byte[] yuv_buffer =byteMergerAll(bytes_plans[0],bytes_plans[2]);
                Log.i(TAG,"yuv_buffer size is :"+yuv_buffer.length);

                Youtput.close();
                UVoutput.close();
                VUoutput.close();
                YVUoutput.close();
                YUVoutput.close();

                //yuv420转换为jpeg格式
                if (yuv_buffer != null) {
                    Bitmap Jpeg_bitmap = YuvTansformJpeg(yuv_buffer,1920,1080,80);
                    if (Jpeg_bitmap != null) {
                        Log.i(TAG,"succeccful Jpeg_bitmap");
                        Jpeg_bitmap.compress(Bitmap.CompressFormat.JPEG,100,Jpegoutput);
                        image_view.setImageBitmap(Jpeg_bitmap);
                        Jpegoutput.flush();
                        Jpegoutput.close();
                        Log.i(TAG,"成功保存jpg文件");

                    }
                }

            } catch (FileNotFoundException e) {
                Log.w(TAG, "failed", e);
            } catch (IOException e) {
                Log.w(TAG, "failed 2", e);
            }

        }

        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes_plans[0], 0, number_buffers[0]);
        if (bitmap != null)
            image_view.setImageBitmap(bitmap);

        Log.i(TAG,"回调图片数据完成!");

    }
};

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

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值