在Android中将位图的大小缩小到指定大小

36

我想把一张位图压缩到恰好200kb的大小。我从SD卡中获取一个图像,对其进行压缩并将其另存为一个不同名称和目录的文件。压缩效果良好(3 MB左右的图像可以压缩到约100 KB)。我编写了下面的代码:

String imagefile ="/sdcard/DCIM/100ANDRO/DSC_0530.jpg";
Bitmap bm = ShrinkBitmap(imagefile, 300, 300);

//this method compresses the image and saves into a location in sdcard
    Bitmap ShrinkBitmap(String file, int width, int height){
           
         BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
            bmpFactoryOptions.inJustDecodeBounds = true;
            Bitmap bitmap = BitmapFactory.decodeFile(file, bmpFactoryOptions);
             
            int heightRatio = (int)Math.ceil(bmpFactoryOptions.outHeight/(float)height);
            int widthRatio = (int)Math.ceil(bmpFactoryOptions.outWidth/(float)width);
             
            if (heightRatio > 1 || widthRatio > 1)
            {
             if (heightRatio > widthRatio)
             {
              bmpFactoryOptions.inSampleSize = heightRatio;
             } else {
              bmpFactoryOptions.inSampleSize = widthRatio; 
             }
            }
             
            bmpFactoryOptions.inJustDecodeBounds = false;
            bitmap = BitmapFactory.decodeFile(file, bmpFactoryOptions);
            
            ByteArrayOutputStream stream = new ByteArrayOutputStream();   
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);   
            byte[] imageInByte = stream.toByteArray(); 
            //this gives the size of the compressed image in kb
            long lengthbmp = imageInByte.length / 1024; 
            
            try {
                bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream("/sdcard/mediaAppPhotos/compressed_new.jpg"));
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            
         return bitmap;
        }
    

更改该图像的宽度和高度。 - Sagar Maiyad
是的,根据你的要求200kb...一直尝试直到得到结果。 - Sagar Maiyad
好的,如果我给定一个固定的宽度和高度,那么图片会永远是相同的尺寸吗?我已经完成了这个操作:ShrinkBitmap(imagefile, 300, 300)。 - TharakaNirmana
不,根据您的尺寸调整宽度和高度。如果在300*300上获得了100kb,则为了使大小为200kb,应提高宽度和高度。 - Sagar Maiyad
200 x 200 = 81kb 300 x 300 = 81kb 350 x 350 = 81kb 400 x 300 = 310kb 400 x 400 = 310kb 以上是我得到的数值,似乎没有达到200kb。 - TharakaNirmana
显示剩余3条评论
3个回答

79

我找到了一个对我完美运作的答案:

/**
 * reduces the size of the image
 * @param image
 * @param maxSize
 * @return
 */
public Bitmap getResizedBitmap(Bitmap image, int maxSize) {
    int width = image.getWidth();
    int height = image.getHeight();

    float bitmapRatio = (float)width / (float) height;
    if (bitmapRatio > 1) {
        width = maxSize;
        height = (int) (width / bitmapRatio);
    } else {
        height = maxSize;
        width = (int) (height * bitmapRatio);
    }
    return Bitmap.createScaledBitmap(image, width, height, true);
}

调用这个方法:

Bitmap converetdImage = getResizedBitmap(photo, 500);

其中photo是您的位图


这个函数将会在位图尺寸过大时进行缩小,但是对于较小的位图呢?它们会按照规格进行放大吗? - Narendra Singh
11
@TharakaNirmana,"maxSize"是什么?在你的方法调用中,500是指500千字节还是500兆字节? - TheQ
4
Max指的是宽度和高度,而不是大小。https://developer.android.com/reference/android/graphics/Bitmap.html#createScaledBitmap(android.graphics.Bitmap, int, int, boolean) - PGMacDesign
1
@TharakaNirmana 500MB可能不是这样,但它可能是500KB、500KB、500ko... - letroll
这根本没有任何意义。maxSize是像素大小还是文件大小(以千字节为单位),就像你想要的那样?我也在寻找类似的解决方案,但这是我找到的最接近的,但我似乎无法理解该方法与问题的关系。 - TheRealChx101
显示剩余2条评论

10

这里是一种解决方案,它在不改变 宽度高度 的情况下限制图像质量。该方法的主要思想是在输出大小大于 maxSizeBytesCount 的情况下,在循环内压缩位图。感谢这个问题提供的答案。以下代码块仅展示减少图像质量的逻辑:

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        int currSize;
        int currQuality = 100;

        do {
            bitmap.compress(Bitmap.CompressFormat.JPEG, currQuality, stream);
            currSize = stream.toByteArray().length;
            // limit quality by 5 percent every time
            currQuality -= 5;

        } while (currSize >= maxSizeBytes);

以下是完整的方法:

public class ImageUtils {

    public byte[] compressBitmap(
            String file, 
            int width, 
            int height,
            int maxSizeBytes
    ) {
        BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
        bmpFactoryOptions.inJustDecodeBounds = true;
        Bitmap bitmap;

        int heightRatio = (int)Math.ceil(bmpFactoryOptions.outHeight/(float)height);
        int widthRatio = (int)Math.ceil(bmpFactoryOptions.outWidth/(float)width);

        if (heightRatio > 1 || widthRatio > 1)
        {
            if (heightRatio > widthRatio)
            {
                bmpFactoryOptions.inSampleSize = heightRatio;
            } else {
                bmpFactoryOptions.inSampleSize = widthRatio;
            }
        }

        bmpFactoryOptions.inJustDecodeBounds = false;
        bitmap = BitmapFactory.decodeFile(file, bmpFactoryOptions);

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        int currSize;
        int currQuality = 100;

        do {
            bitmap.compress(Bitmap.CompressFormat.JPEG, currQuality, stream);
            currSize = stream.toByteArray().length;
            // limit quality by 5 percent every time
            currQuality -= 5;

        } while (currSize >= maxSizeBytes);

        return stream.toByteArray();
    }
}

不是很好的方法,压缩是一项要求高的操作。如果我们能够预测质量并仅压缩位图一次,那就太好了。不过,这种创造性的思维还是值得肯定的。 - UrosKekovic
我希望你所描述的解决方案存在,但我认为这在JPEG上是不可能的 :( - Alex Misiulia
尝试在空对象引用上调用虚拟方法'boolean android.graphics.Bitmap.compress(android.graphics.Bitmap$CompressFormat, int, java.io.OutputStream)',位于bitmap.compress(Bitmap.CompressFormat.JPEG, currQuality, stream); - John dahat
1
之前的解决方案进入了无限循环,请在循环内部加上 "ByteArrayOutputStream stream = new ByteArrayOutputStream();"。 - Hamza Awwad

-1

@TharakaNirmana的方法很好用,maxSize以千字节为单位

例子:

fun getResizedBitmap(image: Bitmap, maxSize: Int): Bitmap? {
    var width = image.width
    var height = image.height
    val bitmapRatio = width.toFloat() / height.toFloat()
    if (bitmapRatio > 1) {
        width = maxSize
        height = (width / bitmapRatio).toInt()
    } else {
        height = maxSize
        width = (height * bitmapRatio).toInt()
    }
    return Bitmap.createScaledBitmap(image, width, height, true)
}

使用方法:

bitmap.let {
    val data = it.value

    if (data != null) {

        var image = viewModel.getResizedBitmap(data, 170)
        Log.d ("bitmap size", image!!.byteCount.toString()) ...

答案

2021-12-18 12:38:11.218 25502-25502/com.jdsalasc.sophosSolutions D/bitmap size: 67200 2021-12-18 12:39:11.673 25745-25745/com.jdsalasc.sophosSolutions D/bitmap size: 67200 2021-12-18 12:39:14.511 25745-25745/com.jdsalasc.sophosSolutions D/bitmap size: 90000 2021-12-18 12:39:20.423 25745-25745/com.jdsalasc.sophosSolutions D/bitmap size: 67200 2021-12-18 12:39:40.112 25907-25907/com.jdsalasc.sophosSolutions D/bitmap size: 120000 2021-12-18 12:39:43.322 25907-25907/com.jdsalasc.sophosSolutions D/bitmap size: 120000 2021-12-18 12:39:47.314 25907-25907/com.jdsalasc.sophosSolutions D/bitmap size: 160000 2021-12-18 12:40:17.231 26069-26069/com.jdsalasc.sophosSolutions D/bitmap size: 115600 2021-12-18 12:40:19.583 26069-26069/com.jdsalasc.sophosSolutions D/bitmap size: 86360

你看,字节大小从未超过150 KB

谢谢Zoe和TharakaNirmana c:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接