将JPG图像压缩为给定大小(KB)?

时间:2017-12-08 15:25:06

标签: android bitmap byte jpeg

我有一个输入相机返回的字节数组。

我需要缩小图像以使其大小等于500KB。我试图使用Bitmap实现这一点,但我找不到如何获得正确的compression值。

public static byte[] compressCapture(byte[] capture) {

    // How to get the right compression value ?
    int compression = 50;

    Bitmap bitmap  = BitmapFactory.decodeByteArray(capture, 0, capture.length);
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, compression, outputStream);
    return outputStream.toByteArray();
}

问题是压缩与图像大小不成比例,这意味着compression等于50不会将图像大小除以2.

有没有办法在输出中获得不依赖于焦点或智能手机型号的固定图像尺寸?

编辑:

我不想使用文件(我使用机密数据)。

6 个答案:

答案 0 :(得分:2)

更新01/16/2018

最简单的方法是使用BitmapFactory.Options.inSampleSize解码字节数组并同时压缩它。 Here is the doc

 BitmapFactory.Options options = new BitmapFactory.Options();
 options.inSampleSize = 4; // If you want an image four times smaller than the original
 Bitmap decoded = BitmapFactory.decodeByteArray(data, 0, data.length, options);

老问题,请不要使用

由于显然无法一次性实现这一目标,因此我实施了迭代过程以达到KB的给定大小。

我从一个等于80的压缩系数开始,如果它不够,我会减小这个系数,然后重试,直到我的尺寸低于我的阈值。

static COMPRESSION_PERCENTAGE_START = 80;
static IMAGE_COMPRESSION_EXPECTED_MAX_ITERATIONS = 3;
static IMAGE_COMPRESSION_STEP_PERCENT = 5;

// For logging
static IMAGE_COMPRESSION_EXPECTED_MAX_ITERATIONS = 5;

static byte[] compressCapture(byte[] capture, int maxSizeKB) {
    long maxSizeByte = ((long) maxSizeKB) * 1_000;

    if (capture.length <= maxSizeByte) return capture;

    byte[] compressed = capture;

    // Chosen arbitrarily so that we can compress multiple times to reach the expected size.
    int compression = COMPRESSION_PERCENTAGE_START;

    Bitmap bitmap = BitmapFactory.decodeByteArray(capture, 0, capture.length);
    ByteArrayOutputStream outputStream;
    int iterations = 0;
    while (compressed.length > maxSizeByte) {
        // Just a counter
        iterations++;

        compression -= IMAGE_COMPRESSION_STEP_PERCENT;

        outputStream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, compression, outputStream);
        compressed = outputStream.toByteArray();
    }

    if (iterations > IMAGE_COMPRESSION_EXPECTED_MAX_ITERATIONS) {
        Timber.w("Compression process has iterated more than expected, with " + iterations + " iterations.");
    }

    return compressed;
}

以下是原始大小 1_871_058 字节的输出大小

  • 迭代次数#1(压缩等于80): 528_593 bytes
  • 迭代次数#2(压缩等于75): 456_591 字节

它为我完成了这项工作,但如果您使用它请小心,它当然需要一些微调,我只是在给定的智能手机型号上测试它。

答案 1 :(得分:1)

您遇到的问题是压缩取决于图像的性质。我的猜测是,有学术研究关注图像并选择参数以达到给定的大小。

在JPEG格式中,您有几种缩小尺寸的设置:

  1. 包含或排除COM和APPn标记。
  2. 是使用优化的还是现成的霍夫曼表。
  3. 选择量化表。
  4. Cb和Cr组分的二次取样。
  5. 渐进式或顺序式编码(如果是前者,您可以使用大量的扫描设置)。
  6. 尽管如此,这主要是反复试验。

答案 2 :(得分:0)

很难压缩到给定的大小,我建议使用ImageMagick并调整选项,直到达到你觉得使用的大小。

我总是使用ImageMagick。考虑这个命令

convert -strip -interlace Plane -gaussian-blur 0.05 -quality 85% source.jpg result.jpg

该命令使用以下选项:

  
      
  • 85的质量
  •   
  • 渐进式(压缩压缩)
  •   
  • 非常小的高斯模糊以优化尺寸(半径0.05或0.5)取决于图片的质量和尺寸,这显着   优化jpeg的大小。
  •   
  • 删除任何评论或exif标记
  •   

Source

尝试调整质量参数。如果减少它,它会增加压缩并减小文件大小。

答案 3 :(得分:0)

您可以使用Magick.NET(https://github.com/dlemstra/Magick.NET):

using (var image = new MagickImage(capture))
{
    return image.ToByteArray(new JpegWriteDefines()
    {
        // Will make the maximum size 500KB (it might be smaller)
        Extent = 500
    });
}    

答案 4 :(得分:0)

为了获得更好的图像质量,请将文件保存到存储然后压缩。它可以帮助用户获得最佳质量的图像供自己使用。 在我的一些项目中,我正在使用这个库 https://android-arsenal.com/details/1/3758 这是一个非常好的库,它压缩得很好。 如果您需要文件,请使用此

compressedImage = new Compressor(this)
        .setMaxWidth(640)
        .setMaxHeight(480)
        .setQuality(75)
        .setCompressFormat(Bitmap.CompressFormat.WEBP)
        .setDestinationDirectoryPath(Environment.getExternalStoragePublicDirectory(
          Environment.DIRECTORY_PICTURES).getAbsolutePath())
        .compressToFile(actualImage);

或者如果你需要比这个位图

compressedImageBitmap = new Compressor(this).compressToBitmap(actualImageFile);

快乐编码:)

答案 5 :(得分:0)

没有注意到@dlemstra的回复

Imagemagick有一个文件大小选项,但它从内存较慢,它正在为输出大小尝试不同的选项,并不准确:

convert input -define jpeg:extent=500kb output.jpg

当然,您的手机可能不支持定义。