【问题标题】:How to reduce image size into 1MB如何将图像大小减小到 1MB
【发布时间】:2016-10-18 19:24:06
【问题描述】:

我希望我的应用程序上传没有大小限制的图像,但在代码中,如果图像大小超过,我想将图像大小调整为 1MB。我尝试了很多方法,但找不到任何代码满足我上面提到的要求。

这一次,我试过这个:

public void scaleDown() {
    int width = stdImageBmp.getWidth();
    int height = stdImageBmp.getHeight();
    Matrix matrix = new Matrix();
    float scaleWidth = ((float) MAX_WIDTH) / width;
    float scaleHeight = ((float) MAX_HEIGHT) / height;


    matrix.postScale(scaleWidth, scaleHeight);
    stdImageBmp = Bitmap.createBitmap(stdImageBmp, 0, 0, width, height, matrix, true);

    File Image = new File("path");


    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    //compress bmp
    stdImageBmp.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream);
    byte[] byteArray = byteArrayOutputStream.toByteArray();


    imgViewStd.setImageBitmap(stdImageBmp);
    Log.d("resizedBitmap", stdImageBmp.toString());

    width = stdImageBmp.getWidth();
    height = stdImageBmp.getHeight();
    System.out.println("imgWidth" + width);
    System.out.println("imgHeight" + height);
}

【问题讨论】:

    标签: android image-resizing


    【解决方案1】:

    我试图评论它,但评论变得太大了。 所以,我已经测试了很多解决方案,似乎只有 两种 解决方案可以解决这个问题,并给出了一些结果。

    让我们先讨论 Koen 解决方案。它实际上做的是创建一个缩放的 JPG

    Bitmap.createScaledBitmap(input, newWidthPx, newHeightPx, true)
    

    似乎根本不压缩它,只是切断了分辨率。

    我已经测试了这段代码,当我通过 MAX_IMAGE_SIZE = 1024000 时,它会从 2.33Mb 的原始图像中得到 350kb 的压缩图像。漏洞? 它也缺乏质量。我无法识别 Google Pixel 制作的 A4 纸照片上的文字。

    这个问题还有另一种解决方案,它提供了良好的质量,但缺乏速度。 一个WHILE LOOP! 基本上你只是循环通过图像大小,直到你得到所需的大小

    private fun scaleBitmap() {
        if (originalFile.length() > MAX_IMAGE_SIZE) {
            var streamLength = MAX_IMAGE_SIZE
            var compressQuality = 100
            val bmpStream = ByteArrayOutputStream()
            while (streamLength >= MAX_IMAGE_SIZE) {
                bmpStream.use {
                    it.flush()
                    it.reset()
                }
    
                compressQuality -= 8
                val bitmap = BitmapFactory.decodeFile(originalFile.absolutePath, BitmapFactory.Options())
                bitmap.compress(Bitmap.CompressFormat.JPEG, compressQuality, bmpStream)
                streamLength = bmpStream.toByteArray().size
            }
    
            FileOutputStream(compressedFile).use {
                it.write(bmpStream.toByteArray())
            }
        }
    }
    

    我认为这种方法会消耗指数级的时间,具体取决于图像分辨率。 9mb 图像最多需要 12 秒才能压缩到 1mb。 质量很好。 您可以通过降低原始位图分辨率(这似乎是一个常量操作)来调整它,方法是:

    options.inSampleSize = 2;

    我们需要做的是以某种方式计算任何图像的 compressQuality。 应该有一个数学计算,所以我们可以根据原始图像大小或宽度 + 高度来确定 compressQuality。

    【讨论】:

    • 我删除了问号以减少混乱
    • 关于我的实现更正,它只能缩放,因此名称为“scaleBitmap” 同意质量会因为它的缩小而丢失。如前所述,它仅适用于具有 ARGB_8888 配置的位图。如果它不是具有该配置的位图,它将产生那些错误的结果(350 KB 而不是近 1 MB)
    【解决方案2】:

    如果您真的想要缩小到最接近给定字节数的位图的位图,这是我使用的方法。 (它确实使用了一个while循环)

    注意:此方法仅在传递的位图为 ARGB_8888 配置时有效。 转换方法见:Compress bitmap to a specific byte size in Android

    /**
     * Method to scale the Bitmap to respect the max bytes
     *
     * @param input    the Bitmap to scale if too large
     * @param maxBytes the amount of bytes the Image may be
     * @return The scaled bitmap or the input if already valid
     * @Note: The caller of this function is responsible for recycling once the input is no longer needed
     */
    public static Bitmap scaleBitmap(final Bitmap input, final long maxBytes) {
        final int currentWidth = input.getWidth();
        final int currentHeight = input.getHeight();
        final int currentPixels = currentWidth * currentHeight;
        // Get the amount of max pixels:
        // 1 pixel = 4 bytes (R, G, B, A)
        final long maxPixels = maxBytes / 4; // Floored
        if (currentPixels <= maxPixels) {
            // Already correct size:
            return input;
        }
        // Scaling factor when maintaining aspect ratio is the square root since x and y have a relation:
        final double scaleFactor = Math.sqrt(maxPixels / (double) currentPixels);
        final int newWidthPx = (int) Math.floor(currentWidth * scaleFactor);
        final int newHeightPx = (int) Math.floor(currentHeight * scaleFactor);
        Timber.i("Scaled bitmap sizes are %1$s x %2$s when original sizes are %3$s x %4$s and currentPixels %5$s and maxPixels %6$s and scaled total pixels are: %7$s",
                newWidthPx, newHeightPx, currentWidth, currentHeight, currentPixels, maxPixels, (newWidthPx * newHeightPx));
        final Bitmap output = Bitmap.createScaledBitmap(input, newWidthPx, newHeightPx, true);
        return output;
    }
    

    示例用法如下所示:

    // (1 MB)
    final long maxBytes = 1024 * 1024; 
    // Scale it
    final Bitmap scaledBitmap = BitmapUtils.scaleBitmap(yourBitmap, maxBytes);
    if(scaledBitmap != yourBitmap){
        // Recycle the bitmap since we can use the scaled variant:
        yourBitmap.recycle();
    } 
    // ... do something with the scaled bitmap
    
    

    【讨论】:

    【解决方案3】:

    您可以使用此代码调整位图大小,对于 480x640 的分辨率

    public Bitmap getResizedBitmap(Bitmap bm, int newWidth, int newHeight) {
            int width = bm.getWidth();
            int height = bm.getHeight();
            float scaleWidth = ((float) newWidth) / width;
            float scaleHeight = ((float) newHeight) / height;
            // CREATE A MATRIX FOR THE MANIPULATION
            Matrix matrix = new Matrix();
            // RESIZE THE BIT MAP
            matrix.postScale(scaleWidth, scaleHeight);
    
            // "RECREATE" THE NEW BITMAP
            return Bitmap.createBitmap(
                    bm, 0, 0, width, height, matrix, false);
        }
    

    【讨论】:

    • 生成的图像将略微> 1MB:480 x 640 x 4 = 1228800 字节。
    • 实际上是 480 x 640 x 3 = 921600 字节
    • 不,实际上是 480 x 640 x 4:R、G、B 和 A。这是 4 个字节,而不是 3 个。
    • 能否给点正宗的出处?
    • 谢谢@BobMalooga。学习了新事物。
    猜你喜欢
    • 1970-01-01
    • 2018-04-06
    • 2012-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-06
    • 2019-04-13
    • 1970-01-01
    相关资源
    最近更新 更多