YuvImage和Bitmap压缩为Jpeg时,质量和耗时的关系

前言

如果查看CameraX库的拍照功能ImageCapture类,可以发现两个常量:

 private static final byte JPEG_QUALITY_MAXIMIZE_QUALITY_MODE = 100;
 private static final byte JPEG_QUALITY_MINIMIZE_LATENCY_MODE = 95;

这两个常量用于CameraX的拍照时,设置保存JPEG图片的质量:

  • 最大质量
  • 减少延迟

其中,最大质量模式的值为100,减少延迟模式的值为95。最终用在compressToJpeg的quality参数中:

YuvImage yuvImage = new YuvImage(yuvBytes, ImageFormat.NV21, imageProxy.getWidth(),
                    imageProxy.getHeight(), null);
...
...
yuvImage.compressToJpeg(imageRect, mQuality, os);

而CameraX默认使用减少延迟模式进行拍照:

@CaptureMode
private static final int DEFAULT_CAPTURE_MODE = CAPTURE_MODE_MINIMIZE_LATENCY;

既然YUV数据压缩为JPEG数据时,选择牺牲一点质量,换取更少的延迟,说明当质量为95时,相对于最大质量的100,消耗的时间有显著的减少。

测试YUV压缩为JPEG

实测代码如下:

	public void testYuvImageCompressSpeed() {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.big_size_photo);
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int[] pixels = new int[width * height];
        bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
        byte[] yuv = rgb2YCbCr420(pixels, width, height);
        //
        for (int i = 1; i <= 10; i++) {
            String[] qualities = new String[21];
            String[] costTimes = new String[21];
            for (int quality = 100; quality >= 80; quality--) {
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                long startTime = System.currentTimeMillis();

                YuvImage image = new YuvImage(yuv, ImageFormat.NV21, width, height, null);   //将NV21 data保存成YuvImage
                //图像压缩
                image.compressToJpeg(
                        new Rect(0, 0, image.getWidth(), image.getHeight()),
                        quality, stream);   // 将NV21格式图片,以质量 quality 压缩成Jpeg,并得到JPEG数据流

                qualities[100 - quality] = String.valueOf(quality);
                costTimes[100 - quality] = String.valueOf(System.currentTimeMillis() - startTime);
            }
            Log.d(TAG, "qualities:" + TextUtils.join(",", qualities));
            Log.d(TAG, "costTimes:" + TextUtils.join(",", costTimes));
        }
    }

    public byte[] rgb2YCbCr420(int[] pixels, int width, int height) {
        int len = width * height;
        //yuv格式数组大小,y亮度占len长度,u,v各占len/4长度。
        byte[] yuv = new byte[len * 3 / 2];
        int y, u, v;
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                //屏蔽ARGB的透明度值
                int rgb = pixels[i * width + j] & 0x00FFFFFF;
                //像素的颜色顺序为bgr,移位运算。
                int r = rgb & 0xFF;
                int g = (rgb >> 8) & 0xFF;
                int b = (rgb >> 16) & 0xFF;
                //套用公式
                y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
                u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
                v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
                //调整
                y = y < 16 ? 16 : (Math.min(y, 255));
                u = u < 0 ? 0 : (Math.min(u, 255));
                v = v < 0 ? 0 : (Math.min(v, 255));
                //赋值
                yuv[i * width + j] = (byte) y;
                yuv[len + (i >> 1) * width + (j & ~1) + 0] = (byte) u;
                yuv[len + +(i >> 1) * width + (j & ~1) + 1] = (byte) v;
            }
        }
        return yuv;
    }

使用手机拍摄了一个分辨率3456 x 4608,大小为8.3 MB的图片,就是代码中的R.drawable.big_size_photo文件。

解码转换为Bitmap文件,然后读取像素数据,转换为YUV格式,最后使用YuvImage的compressToJpeg函数压缩为Jpeg(压缩质量为100~80)。

得到测试数据,转换为图表如下:
YuvImage和Bitmap压缩为Jpeg时,质量和耗时的关系_第1张图片
其中,横轴为压缩质量(100~80),纵轴为时间(单位毫秒)。

可以看出,选项“减少延迟”的质量选取 95 这个值是有一定的道理:保证图片的质量同时,消耗的时间显著减少。

测试Bitmap压缩为JPEG

既然YUV压缩为JPEG时,消耗时间有这种关系,那么进一步拓展,Bitmap压缩为JPEG时,消耗时间是不是也这样的,下面进行测试:

public void testBitmapCompressSpeed() {
        for (int i = 1; i <= 10; i++) {
            String[] qualities = new String[21];
            String[] costTimes = new String[21];
            for (int quality = 100; quality >= 80; quality--) {
                Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.big_size_photo);
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                long startTime = System.currentTimeMillis();
                bitmap.compress(Bitmap.CompressFormat.JPEG, quality, stream);
                bitmap.recycle();
                qualities[100 - quality] = String.valueOf(quality);
                costTimes[100 - quality] = String.valueOf(System.currentTimeMillis() - startTime);
            }
            Log.d(TAG, "qualities:" + TextUtils.join(",", qualities));
            Log.d(TAG, "costTimes:" + TextUtils.join(",", costTimes));
        }
    }

同样使用R.drawable.big_size_photo这个图片。

解码转换为Bitmap文件,使用Bitmap的compress函数压缩为Jpeg(压缩质量为100~80)。

得到测试数据,转换为图表如下:
YuvImage和Bitmap压缩为Jpeg时,质量和耗时的关系_第2张图片
其中,横轴为压缩质量(100~80),纵轴为时间(单位毫秒)。

可以看出,Bitmap压缩为JPEG时,质量和耗时依然有类似的关系,如果只是想要减少延迟,图片的质量不想有太大的下降,依然可以选择 95 这个值。

PS:压缩高分辨率的图片时,这种关系比较明显,如果压缩分辨率比较低的普通图片,质量和耗时就没有这种明显的关系了。

你可能感兴趣的:(android,android,安卓)