Android Yuv图像转jpg方法

来源:互联网 发布:开淘宝店货源怎么弄 编辑:程序博客网 时间:2024/06/04 20:01
首先视频硬编码MediaRecorder类并不适合做实时发送(不知道为什么博主的机器测试出来的mdat后面紧接着并不是网上所说的一个Int表示的场长度,而是连续8个3F预留字节位,努力很久后彻底放弃了MediaRecorder,改为实现Camera的onPreviewFrame方法)

Camera的onPreviewFrame会在每一帧取回一个字节数组,格式为YUV422(ImageFormat.NV21)
这种无压缩的图片格式是相当占用空间的(480x320分辨率可以达到200KB每张图的体积,10FPS的话就有2MB每秒的传输速率,如果要网络发送的话压力是非常大的),因此需要压缩。

在网上找到的方法基本是Yuv到RGB的方式,把Yuv的数据通过数学运算得到每个像素点的RGB编码,存入Bitmap对象,再调用Bitmap对象自带的压缩方法压为jpg图片。这种方法效率极低,一张480x320分辨率的图片有20万个字节,因此运算需要经过20万次循环,在博主的小米1上测试的结果是一张图需要400毫秒的处理时间,帧速被强行降低到了2.5FPS(onPreviewFrame在执行完成之前得到的数据会自动丢弃),单张图片还好说,如果是连续图片构成的视频流是不可能完成这种需求的。

在我一筹莫展之际发现了android.graphics包下面已经存在一个YuvImage类,可以将数据直接导入:
YuvImage image = new YuvImage(data,ImageFormat.NV21,IMG_WIDTH,IMG_HEIGHT,null);

这个构造方法有5个参数,第一个就是字节数组,第二个是格式信息,和相机配置的时候设置的previewFormat一致即可。第三、第四个不需要解释,就是图片的长宽信息(YUV格式没有储存图片的尺寸信息,最后一个博主也不知道是什么,总之给个null就可以了)。
然后很碰巧的是YuvImage有一个compressToJPEG()方法。
需要传入的参数有3个:一个Rect对象,包含图片的显示范围,一般是(0,0)到(width,height),也可以利用这个参数对图片进行切割;
第二个是质量因数,1-100内的整数;
第三个是一个OutputStream对象,会把结果输出到这个Stream。

实际测试80质量的480x320尺寸图片压缩只用了50ms,效率大大提高。

不过这种jpg图片似乎和传统jpg编码格式有差异,打开是可以正常打开的,而用FFMpeg压缩的时候显示是jpg-yuv的格式,也就是说直接将yuv以jpg格式压缩的。如果需要进一步转换可能依然需要转为RGB格式。





  @Override        protected Void doInBackground(Void... params) {//            Size size = myCamera.getParameters().getPreviewSize(); //获取预览大小//            final int w = size.width;  //宽度//            final int h = size.height;            final YuvImage image = new YuvImage(mData, ImageFormat.NV21, mCameraView.mPicWidth, mCameraView.mPicHeight, null);            ByteArrayOutputStream os = new ByteArrayOutputStream(mData.length);            if (!image.compressToJpeg(new Rect(0, 0, mCameraView.mPicWidth, mCameraView.mPicHeight), 100, os)) {                return null;            }            byte[] tmp = os.toByteArray();          
            Bitmap mapLbb = BitmapFactory.decodeByteArray(tmp, 0, tmp.length);
// detBmp = Bitmap.createBitmap(mapLbb, 0, 0, mCameraView.mPicWidth, mCameraView.mPicHeight, m, true);// Bitmap mapLbb = BitmapFactory.decodeResource(getResources(), R.drawable.id2);// 识别人脸,画出人脸框的位置 publishProgress(drawFaceRect(mapLbb)); mapLbb.recycle(); return null; }

0 0