如何在旋转图像时避免使用OutOfMemory?

时间:2013-07-14 16:11:19

标签: android image memory memory-management out-of-memory

public static boolean rotateBitmapByExifAndSave(File targetFile){

  if (targetFile==null || !targetFile.exists() || !targetFile.canRead() || !targetFile.canWrite())
      return false;

    boolean isSucceed = false;
    // detect if photo is need to be rotated
    try {
        final Matrix matrix = new Matrix();

        ExifInterface exifReader = new ExifInterface(targetFile.getAbsolutePath());

        int orientation = exifReader.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
        boolean isRotationNeeded = true;

        switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
            matrix.postRotate(90);
            break;

        case ExifInterface.ORIENTATION_ROTATE_180:
            matrix.postRotate(180);
            break;

        case ExifInterface.ORIENTATION_ROTATE_270:
            matrix.postRotate(270);
            break;

        default: // ExifInterface.ORIENTATION_NORMAL
            // Do nothing. The original image is fine.
            isRotationNeeded = false;
            isSucceed = true;
            break;
        }

        if (isRotationNeeded){
            BitmapFactory.Options bmfOtions = new BitmapFactory.Options();
            Bitmap bitmap = null;
            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(targetFile);
                bitmap = BitmapFactory.decodeStream(fileInputStream,null,bmfOtions);
            } catch (FileNotFoundException e){
                isSucceed = false;
            }
            finally {
                if (fileInputStream != null)
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {}
            }
            if (bitmap!=null){
                bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
                isSucceed = ImageUtils.saveBitmapToFile(bitmap, targetFile, 100);
                bitmap.recycle();
            }
        }

    } catch (IOException e) {
        Log.e("ImageUtils", e);
    } catch (Exception e) {
        // like there is no EXIF support?
        Log.e("ImageUtils", e);
    } catch (Throwable e) {
        // stupid Out of VM's memory
        Log.e("ImageUtils", e.toString());
    }

    return isSucceed; 
}

我使用此方法旋转设备相机拍摄的原始照片。如今相机可能大于8MPix(三星Galaxy S4拥有13百万像素摄像头)。即使使用较少的MPix相机(我的 5 MP ,2592 x 1944像素,结合ARGB_888根据官方文档需要 19Mb 的RAM)我已经获得OutOfMemory。所以问题是如何在不损失初始分辨率和质量的情况下旋转照片?

2 个答案:

答案 0 :(得分:2)

由于没有答案,我认为没有答案,或者我只是错误地提出了问题。看起来这里唯一的选择是to increase the app's heap size
更新:
还有另一种选择 - 通过NDK / JNI(如here)使用位图或使用Android Image-Magic lib。 Image Magic lib非常酷,旋转图像只需:

ImageInfo imageInfo = new ImageInfo(imageFile.getAbsolutePath());
MagickImage magickImage = new MagickImage(imageInfo);
magickImage.setCompression(100); // to minimize loss
magickImage.rotateImage(90.0f).writeImage(imageInfo);

MagickImage还有许多其他图像处理选项。模糊,哑光,鳞片,木炭等等。但是它的库大小是显而易见的。作者做得很好,他们涵盖了所有可能的平台:arm64-v8a,armeabi,armeabi-v7a,mips,mips64,x86,x86_64以及所有这些库的最终大小超过36Mb。因此,在将所有库添加到一个apk之前,您应该考虑一下,使用清单按芯片组/平台过滤包装6个不同版本是正确的方法。
更新
另一个选择是convert Immutable Bitmap into Mutable (wrap bitmaps into MappedByteBuffer)

答案 1 :(得分:-1)

制作方法名称解码文件:

public static Bitmap decodeFile(File f,int WIDTH,int HIGHT){
         try {
             //Decode image size
             BitmapFactory.Options o = new BitmapFactory.Options();
             o.inJustDecodeBounds = true;
             BitmapFactory.decodeStream(new FileInputStream(f),null,o);

             //The new size we want to scale to
             final int REQUIRED_WIDTH=WIDTH;
             final int REQUIRED_HIGHT=HIGHT;
             //Find the correct scale value. It should be the power of 2.
             int scale=1;
             while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT)
                 scale*=2;

             //Decode with inSampleSize
             BitmapFactory.Options o2 = new BitmapFactory.Options();
             o2.inSampleSize=scale;
             return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
         } catch (FileNotFoundException e) {}
         return null;
     }

然后像这样调用此方法(您可以在按钮单击侦听器中调用此方法)

Bitmap bi = decodeFile(new File(path),1280,800);

路径是保存图像的图像路径。 就我而言,它是

String path = Environment.getExternalStorageDirectory().toString() + "/nature.jpg";

如果有任何问题 - 请问:)希望这有帮助。