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格式数据。
为什么会有YUV数据转换为JPEG,如果你有camera开发基础的同仁,你一定会想要获取JPEG数据可以在实例化imagereader时就设置format为JPEG格式,然后就能在回调中直接获取JPEG,我的回答:我也是这样想的。没有必要还需要这样转一下,增加开发周期。但是当我们有实际需求场景时我们就不会这样想了,我之所以这样做就是开发过程发现获取JPEG会让预览有短暂卡顿,通过这种当时很好的改善了卡顿。当然这只是这个应用场景之一,我相信产品的需求会让这个功能发光发彩的。
Imagereader回调中:public void onImageAvailable(ImageReader imageReader),当然你可以是任何yuv数据环境中;
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,"回调图片数据完成!");
-
- }
- };
(谢谢,一起学习,欢迎交流)