处理和处理时的内存问题处理多个大位图

时间:2014-04-03 07:16:45

标签: android image bitmap out-of-memory heap

我必须让用户逐一从设备摄像头捕获10张图像,然后用灰度缩放处理每张图像,最后将它们上传到服务器上。

现在在RAM低于1 GB的低端Android设备中我遇到一个问题,由于内存/堆问题,页面在从相机应用程序导航回应用程序时再次重新加载,所有edittext数据仍保持设置但设置图像视图中的缩略图消失。

步骤:

  1. 捕捉多达10张图片
  2. 每个GrayScale
  3. 检查方向并以正确的方向保存
  4. 显示灰度图像的小部分
  5. 第二步,第三步和第四步在AsyncTask中完成
  6. 开提交将所有10张图片(SD卡中保存的大位图)与其他数据一起上传到服务器
  7. 我在android开发人员上提到了以下教程:

    用于图像压缩和缩放:

    http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

    用于图像灰度:

    bmpGrayscale = Bitmap.createBitmap(width, height,
                        Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(bmpGrayscale);
    Paint paint = new Paint();
    ColorMatrix cm = new ColorMatrix();
    cm.setSaturation(0);
    ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
    paint.setColorFilter(f);
    c.drawBitmap(bitmap, 0, 0, paint);
    

    用于方向处理:

    Uri uri = Uri.parse(picPath);
        ExifInterface exif = null;
        try {
            exif = new ExifInterface(uri.getPath());
        } catch (IOException e) {
            e.printStackTrace();
        }
    
        int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                            ExifInterface.ORIENTATION_NORMAL);
    
        rotationInDegrees = exifToDegrees(rotation);
    
        private static int exifToDegrees(int exifOrientation) {
            if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
                return 90;
            } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
                return 180;
            } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
                return 270;
            }
            return 0;
        }
    
        public static Bitmap rotate(Bitmap b, int degrees) {
            if (degrees != 0 && b != null) {
                Matrix m = new Matrix();
    
                m.setRotate(degrees, (float) b.getWidth() / 2,
                        (float) b.getHeight() / 2);
                // m.postRotate(degrees);
                try {
                    System.gc();
                    Bitmap b2 = Bitmap.createBitmap(b, 0, 0, b.getWidth(),
                            b.getHeight(), m, true);
                    if (b != b2) {
                        b.recycle();
                        b = b2;
                    }
                } catch (OutOfMemoryError ex) {
                    throw ex;
                }
            }
            return b;
        }
    

    任何人都可以指导我如何压缩或处理内存,以便我执行上述所有步骤并且不会发生所述内存/堆问题。

    可以缓存图像帮助,我试过但它没有帮助我实现它的方式。

    提前致谢。

2 个答案:

答案 0 :(得分:0)

我看到你在做bimap.recycle - 很好。我发现我必须将每个摄像头图像保存到磁盘,然后按服务或同步任务顺序处理每个摄像头。位图是非常占用大量内存的,并且取决于手机上的内存量一次只能处理几个。保存到磁盘时,请调整jpg压缩,以便在消耗较少的内存时回读。

答案 1 :(得分:0)

这是我的代码,不确定它是如何工作的,但是一旦从相机或图库中取出它,它确实可以保存位图上的内存。我可以用这一个很多照片。在那里创建一个类,然后在你的活动中调用它

import java.io.File;

import java.io.FileInputStream;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

public class DecodeImage
{

//decodes image and scales it to reduce memory consumption
public static Bitmap decodeFile(File bitmapFile, int requiredWidth, int requiredHeight, boolean quickAndDirty)
{
    try
    {
        //Decode image size
        BitmapFactory.Options bitmapSizeOptions = new BitmapFactory.Options();
        bitmapSizeOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapSizeOptions);

        // load image using inSampleSize adapted to required image size
        BitmapFactory.Options bitmapDecodeOptions = new BitmapFactory.Options();
        bitmapDecodeOptions.inTempStorage = new byte[16 * 1024];
        bitmapDecodeOptions.inSampleSize = computeInSampleSize(bitmapSizeOptions, requiredWidth, requiredHeight, false);
        bitmapDecodeOptions.inPurgeable = true;
        bitmapDecodeOptions.inDither = !quickAndDirty;
        bitmapDecodeOptions.inPreferredConfig = quickAndDirty ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888;

        Bitmap decodedBitmap = BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapDecodeOptions);

        // scale bitmap to mathc required size (and keep aspect ratio)

        float srcWidth = (float) bitmapDecodeOptions.outWidth;
        float srcHeight = (float) bitmapDecodeOptions.outHeight;

        float dstWidth = (float) requiredWidth;
        float dstHeight = (float) requiredHeight;

        float srcAspectRatio = srcWidth / srcHeight;
        float dstAspectRatio = dstWidth / dstHeight;

        // recycleDecodedBitmap is used to know if we must recycle intermediary 'decodedBitmap'
        // (DO NOT recycle it right away: wait for end of bitmap manipulation process to avoid
        // java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@416ee7d8
        // I do not excatly understand why, but this way it's OK

        boolean recycleDecodedBitmap = false;

        Bitmap scaledBitmap = decodedBitmap;
        if (srcAspectRatio < dstAspectRatio)
        {
            scaledBitmap = getScaledBitmap(decodedBitmap, (int) dstWidth, (int) (srcHeight * (dstWidth / srcWidth)));
            // will recycle recycleDecodedBitmap
            recycleDecodedBitmap = true;
        }
        else if (srcAspectRatio > dstAspectRatio)
        {
            scaledBitmap = getScaledBitmap(decodedBitmap, (int) (srcWidth * (dstHeight / srcHeight)), (int) dstHeight);
            recycleDecodedBitmap = true;
        }

        // crop image to match required image size

        int scaledBitmapWidth = scaledBitmap.getWidth();
        int scaledBitmapHeight = scaledBitmap.getHeight();

        Bitmap croppedBitmap = scaledBitmap;

      /**      if (scaledBitmapWidth > requiredWidth)
        {
            int xOffset = (scaledBitmapWidth - requiredWidth) / 2;
            croppedBitmap = Bitmap.createBitmap(scaledBitmap, xOffset, 0, requiredWidth, requiredHeight);
            scaledBitmap.recycle();
        }
        else if (scaledBitmapHeight > requiredHeight)
        {
            int yOffset = (scaledBitmapHeight - requiredHeight) / 2;
            croppedBitmap = Bitmap.createBitmap(scaledBitmap, 0, yOffset, requiredWidth, requiredHeight);
            scaledBitmap.recycle();
        }
**/
            if (recycleDecodedBitmap)
        {
            decodedBitmap.recycle();
        }
        decodedBitmap = null;

        scaledBitmap = null;
        return croppedBitmap;
    }
    catch (Exception ex)
    {
        ex.printStackTrace();
    }
    return null;
}

/**
 * compute powerOf2 or exact scale to be used as {@link BitmapFactory.Options#inSampleSize} value (for subSampling)
 * 
 * @param requiredWidth
 * @param requiredHeight
 * @param powerOf2
 *            weither we want a power of 2 sclae or not
 * @return
 */
public static int computeInSampleSize(BitmapFactory.Options options, int dstWidth, int dstHeight, boolean powerOf2)
{
    int inSampleSize = 1;

    // Raw height and width of image
    final int srcHeight = options.outHeight;
    final int srcWidth = options.outWidth;

    if (powerOf2)
    {
        //Find the correct scale value. It should be the power of 2.

        int tmpWidth = srcWidth, tmpHeight = srcHeight;
        while (true)
        {
            if (tmpWidth / 2 < dstWidth || tmpHeight / 2 < dstHeight)
                break;
            tmpWidth /= 2;
            tmpHeight /= 2;
            inSampleSize *= 2;
        }
    }
    else
    {
        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) srcHeight / (float) dstHeight);
        final int widthRatio = Math.round((float) srcWidth / (float) dstWidth);

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

public static Bitmap drawableToBitmap(Drawable drawable)
{
    if (drawable instanceof BitmapDrawable)
    {
        return ((BitmapDrawable) drawable).getBitmap();
    }

    Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);

    return bitmap;
}

public static Bitmap getScaledBitmap(Bitmap bitmap, int newWidth, int newHeight)
{
    int width = bitmap.getWidth();
    int height = bitmap.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
    Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
    return resizedBitmap;
}

}

要调用它

//imgpath is the path to the image which you can save in shared preferences
   Bitmap bmp=DecodeImage.decodeFile(imgpath, 800, 1000, true);

希望这有帮助