Resize a large bitmap file to scaled output file on Android

前端 未结 21 923
执念已碎
执念已碎 2020-11-22 05:51

I have a large bitmap (say 3888x2592) in a file. Now, I want to resize that bitmap to 800x533 and save it to another file. I normally would scale the bitmap by calling

相关标签:
21条回答
  • 2020-11-22 06:18

    Acknowledging the other excellent answer so far, the best code I've seen yet for this is in the documentation for the photo taking tool.

    See the section entitled "Decode a Scaled Image".

    http://developer.android.com/training/camera/photobasics.html

    The solution it proposes is a resize then scale solution like the others here, but it's quite neat.

    I've copied the code below as a ready-to-go function for convenience.

    private void setPic(String imagePath, ImageView destination) {
        int targetW = destination.getWidth();
        int targetH = destination.getHeight();
        // Get the dimensions of the bitmap
        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
        bmOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(imagePath, bmOptions);
        int photoW = bmOptions.outWidth;
        int photoH = bmOptions.outHeight;
    
        // Determine how much to scale down the image
        int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
    
        // Decode the image file into a Bitmap sized to fill the View
        bmOptions.inJustDecodeBounds = false;
        bmOptions.inSampleSize = scaleFactor;
        bmOptions.inPurgeable = true;
    
        Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
        destination.setImageBitmap(bitmap);
    }
    
    0 讨论(0)
  • 2020-11-22 06:19

    This is 'Mojo Risin's and 'Ofir's solutions "combined". This will give you a proportionally resized image with the boundaries of max width and max height.

    1. It only reads meta data to get the original size (options.inJustDecodeBounds)
    2. It uses a rought resize to save memory (itmap.createScaledBitmap)
    3. It uses a precisely resized image based on the rough Bitamp created earlier.

    For me it has been performing fine on 5 MegaPixel images an below.

    try
    {
        int inWidth = 0;
        int inHeight = 0;
    
        InputStream in = new FileInputStream(pathOfInputImage);
    
        // decode image size (decode metadata only, not the whole image)
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(in, null, options);
        in.close();
        in = null;
    
        // save width and height
        inWidth = options.outWidth;
        inHeight = options.outHeight;
    
        // decode full image pre-resized
        in = new FileInputStream(pathOfInputImage);
        options = new BitmapFactory.Options();
        // calc rought re-size (this is no exact resize)
        options.inSampleSize = Math.max(inWidth/dstWidth, inHeight/dstHeight);
        // decode full image
        Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options);
    
        // calc exact destination size
        Matrix m = new Matrix();
        RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
        RectF outRect = new RectF(0, 0, dstWidth, dstHeight);
        m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
        float[] values = new float[9];
        m.getValues(values);
    
        // resize bitmap
        Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);
    
        // save image
        try
        {
            FileOutputStream out = new FileOutputStream(pathOfOutputImage);
            resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
        }
        catch (Exception e)
        {
            Log.e("Image", e.getMessage(), e);
        }
    }
    catch (IOException e)
    {
        Log.e("Image", e.getMessage(), e);
    }
    
    0 讨论(0)
  • 2020-11-22 06:23

    No. I'd love for someone to correct me, but I accepted the load/resize approach you tried as a compromise.

    Here are the steps for anyone browsing:

    1. Calculate the maximum possible inSampleSize that still yields an image larger than your target.
    2. Load the image using BitmapFactory.decodeFile(file, options), passing inSampleSize as an option.
    3. Resize to the desired dimensions using Bitmap.createScaledBitmap().
    0 讨论(0)
  • 2020-11-22 06:23

    If you absolutely want to do one step resize you could probably load entire bitmap if android:largeHeap = true but as you can see this is not really advisable.

    From docs: android:largeHeap Whether your application's processes should be created with a large Dalvik heap. This applies to all processes created for the application. It only applies to the first application loaded into a process; if you're using a shared user ID to allow multiple applications to use a process, they all must use this option consistently or they will have unpredictable results. Most apps should not need this and should instead focus on reducing their overall memory usage for improved performance. Enabling this also does not guarantee a fixed increase in available memory, because some devices are constrained by their total available memory.

    0 讨论(0)
  • 2020-11-22 06:25

    Resize the bitmap using the following code

        public static Bitmap decodeFile(File file, int reqWidth, int reqHeight){
    
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;        
        BitmapFactory.decodeFile(file.getPath(), options);
    
        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    
        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(file.getPath(), options);
       }
    
        private static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
    
        if (height > reqHeight || width > reqWidth) {
    
            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
    
            // Choose the smallest ratio as inSampleSize value, this will guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
         }
    
         return inSampleSize;
       }    
    

    The same is also explained in the following tip/trick

    http://www.codeproject.com/Tips/625810/Android-Image-Operations-Using-BitmapFactory

    0 讨论(0)
  • 2020-11-22 06:28

    This may be useful for someone else looking at this question. I rewrote Justin's code to allow the method to receive the target size object required as well. This works very well when using Canvas. All credit should go to JUSTIN for his great initial code.

        private Bitmap getBitmap(int path, Canvas canvas) {
    
            Resources resource = null;
            try {
                final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
                resource = getResources();
    
                // Decode image size
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeResource(resource, path, options);
    
                int scale = 1;
                while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > 
                      IMAGE_MAX_SIZE) {
                   scale++;
                }
                Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight);
    
                Bitmap pic = null;
                if (scale > 1) {
                    scale--;
                    // scale to max possible inSampleSize that still yields an image
                    // larger than target
                    options = new BitmapFactory.Options();
                    options.inSampleSize = scale;
                    pic = BitmapFactory.decodeResource(resource, path, options);
    
                    // resize to desired dimensions
                    int height = canvas.getHeight();
                    int width = canvas.getWidth();
                    Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height);
    
                    double y = Math.sqrt(IMAGE_MAX_SIZE
                            / (((double) width) / height));
                    double x = (y / height) * width;
    
                    Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true);
                    pic.recycle();
                    pic = scaledBitmap;
    
                    System.gc();
                } else {
                    pic = BitmapFactory.decodeResource(resource, path);
                }
    
                Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
                return pic;
            } catch (Exception e) {
                Log.e("TAG", e.getMessage(),e);
                return null;
            }
        }
    

    Justin's code is VERY effective at reducing the overhead of working with large Bitmaps.

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