How to reduce image size into 1MB

前端 未结 3 1344
情话喂你
情话喂你 2021-01-13 00:03

I want my application to upload image with no size limit, but in the code, I want to resize the image into 1MB if the image size exceeds. I have tried many ways but I could

相关标签:
3条回答
  • 2021-01-13 00:20

    If you really want the Bitmap that scales down to the Bitmap that is the closest to a given amount of bytes, heres the method I use. (It does not uses a while loop)

    NOTE: This method only works if passed bitmap is in ARGB_8888 configuration. See: Compress bitmap to a specific byte size in Android for the conversion method.

    /**
     * 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;
    }
    

    Where the Sample use would look something like:

    // (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
    
    
    0 讨论(0)
  • 2021-01-13 00:31

    you can use this code to resize a bitmap and for image size < 1MB i recommend use resolution of 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);
        }
    
    0 讨论(0)
  • 2021-01-13 00:37

    I've tried to comment it but the comment become too big. So, I've tested many solutions and it seems like there are only TWO solutions for this problem, which give some results.

    Lets discuss Koen solution first. What it actually does is creates a scaled JPG

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

    Seems like it does not compress it at all but just cuts off the resolution.

    I've tested this code and when I pass MAX_IMAGE_SIZE = 1024000 it gives me 350kb compressed image out of 2.33Mb original image. Bug? Also it lacks quality. I was unable to recognize a text on A4 sheet of paper photo made by Google Pixel.

    There is another solution to this problem, which gives good quality, but lacks in speed. A WHILE LOOP! Basically you just loop through image size, until you get the desired size

    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())
            }
        }
    }
    

    I think that this approach will consume exponential time depending on image resolution. 9mb image takes up to 12 seconds to compress down to 1mb. Quality is good. You can tweak this by reducing the original bitmap resolution(which seems like a constant operation), by doing:

    options.inSampleSize = 2;

    What we need to do is to somehow calculate compressQuality for any image. There should be a math around this, so we can determinate compressQuality from original image size or width + height.

    0 讨论(0)
提交回复
热议问题