处理大型位图onPictureTaken android

时间:2014-01-01 15:21:45

标签: android bitmap out-of-memory

我的应用是基于Tesseract的OCR应用。它将从相机图片执行OCR任务。用户可以拍摄许多照片并将其放入OCR队列。为了获得更高的准确度,我希望保持高质量的图像(我选择最小尺寸为1024 x 768(将来可能更大),JPEG,100%质量)。当用户拍摄许多照片时,有三件事要做:

  1. 将图像数据byte[]保存到文件并更正EXIF。
  2. 根据设备的方向校正图像方向。我知道有一些答案说来自相机的图像不是自动定向的,必须从文件中纠正它,如herehere。我不确定,我可以正确设置相机预览方向,但图像结果不正确。
  3. 从拍摄的照片中加载位图,将其转换为灰度并保存到另一个文件以进行OCR任务。
  4. 这是我的尝试:

    public static boolean saveBitmap(byte[] bitmapData, int orientation, String imagePath, String grayScalePath) throws Exception {
        Boolean rotationSuccess = false;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        Bitmap originalBm = null;
        Bitmap bitmapRotate = null;
        Bitmap grayScale = null;
        FileOutputStream outStream = null;
        try {
              // save directly from byte[] to file
            saveBitmap(bitmapData, imagePath);
    
              // down sample
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(imagePath, options);
            int sampleSize = calculateInSampleSize(options, Config.CONFIG_IMAGE_WIDTH, Config.CONFIG_IMAGE_HEIGHT);
            options.inJustDecodeBounds = false;
            options.inSampleSize = sampleSize;
    
            originalBm = BitmapFactory.decodeFile(imagePath, options);
            Matrix mat = new Matrix();
            mat.postRotate(orientation);
            bitmapRotate = Bitmap.createBitmap(originalBm, 0, 0, originalBm.getWidth(), originalBm.getHeight(), mat, true);
            originalBm.recycle();
            originalBm = null;
    
            outStream = new FileOutputStream(new File(imagePath));
            bitmapRotate.compress(CompressFormat.JPEG, 100, outStream);
    
            // convert to gray scale
             grayScale = UIUtil.convertToGrayscale(bitmapRotate);
            saveBitmap(grayScale, grayScalePath);
            grayScale.recycle();
            grayScale = null;
    
            bitmapRotate.recycle();
            bitmapRotate = null;
            rotationSuccess = true;
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
            System.gc();
        } finally {
            if (originalBm != null) {
                originalBm.recycle();
                originalBm = null;
            }
    
            if (bitmapRotate != null) {
                bitmapRotate.recycle();
                bitmapRotate = null;
            }
    
            if (grayScale != null) {
                grayScale.recycle();
                grayScale = null;
            }
    
            if (outStream != null) {
                try {
                    outStream.close();
                } catch (IOException e) {
                }
                outStream = null;
            }
        }
    
                Log.d(TAG,"save completed");
        return rotationSuccess;
    }
    

    直接从byte []

    保存到文件
       public static void saveBitmap(byte[] bitmapData, String fileName) throws Exception {
        File file = new File(fileName);
        FileOutputStream fos;
        BufferedOutputStream bos = null;
        try {
            final int bufferSize = 1024 * 4;
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos, bufferSize);
            bos.write(bitmapData);
            bos.flush();
        } catch (Exception ex) {
            throw ex;
        } finally {
            if (bos != null) {
                bos.close();
            }
        }
    }
    

    计算比例尺寸

        public 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) {
    
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
    
            // Calculate the largest inSampleSize value that is a power of 2 and
            // keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
    
        return inSampleSize;
    }
    

    保存完成后,UIL将此图像加载到缩略图图像视图中。问题是保存任务非常慢(在保存完成并加载到视图之前等待一段时间),有时我得到OutOfMemory异常。
    是否有任何减少保存任务和避免OutOfMemory异常的想法?
    任何帮助,将不胜感激!

    P / S:我第一次尝试将byte []转换为位图而不是保存到文件,然后旋转并转换为灰度,但我仍然遇到上述问题。
    更新:这是灰度位图进程:

       public static Bitmap convertToGrayscale(Bitmap bmpOriginal) {
        int width, height;
        height = bmpOriginal.getHeight();
        width = bmpOriginal.getWidth();    
    
        Bitmap 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(bmpOriginal, 0, 0, paint);
        return bmpGrayscale;
    }
    

    OutOfMemory exception很少发生(只有几次),我现在无法重现。

1 个答案:

答案 0 :(得分:0)

<强>更新: 既然你仍然说这个方法花了太长时间我会定义一个回调接口

interface BitmapCallback {
    onBitmapSaveComplete(Bitmap bitmap, int orientation);

    onBitmapRotateAndBWComlete(Bitmap bitmap);
}

让您的活动实现上述界面,并在byte[]方法的顶部将saveBitmap转换为位图,然后在第一次保存调用之前触发回调。根据方向参数旋转imageView并在imageView上设置黑/白过滤器傻瓜用户认为位图为黑白(请执行此操作)你的活动)。看到调用是在主线程上完成的(对imageView的调用)。 保留旧方法。 (无论如何都需要完成所有步骤)类似于:

public static boolean saveBitmap(byte[] bitmapData, int orientation, String imagePath, String grayScalePath, BitmapCallback callback) throws Exception {
Boolean rotationSuccess = false;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap originalBm = null;
Bitmap bitmapRotate = null;
Bitmap grayScale = null;
FileOutputStream outStream = null;
try {
    // TODO: convert byte to Bitmap, see to that the image is not larger than your wanted size (1024z768)
    callback.onBitmapSaveComplete(bitmap, orientation);

    // save directly from byte[] to file
    saveBitmap(bitmapData, imagePath);
    .
    .
    // same as old 
    .
    .
    saveBitmap(grayScale, grayScalePath);
    // conversion done callback with the real fixed bitmap
    callback.onBitmapRotateAndBWComlete(grayScale);

    grayScale.recycle();
    grayScale = null;

    bitmapRotate.recycle();
    bitmapRotate = null;
    rotationSuccess = true;
  1. 如何设置相机?在第一个saveBitmap调用中可能导致执行时间过长的原因可能是您使用默认的摄像头图片大小设置而不是读取支持的摄像头图片大小并选择最适合您的1024x768图像需求。您可能正在拍摄大像素图像并保存这些图像,但最终需要您需要&lt; 1 mpixles(1024x768)。在代码中有这样的东西:

    Camera camera = Camera.open();
    Parameters params = camera.getParameters();
    List sizes = params.getSupportedPictureSizes();
    // Loop camera sizes and find best match, larger than 1024x768

    如果您还没有这样做,这可能是您节省大部分时间的地方。在初始化阶段只做一次。

  2. 将缓冲区增加到8k中的saveBitmap,将1024*4更改为1024*8,这样可以至少提高性能,也许不会节省任何重要时间

  3. 要保存/重复使用位图内存,请考虑使用inBitmap字段 {strong>如果您有蜂窝后版本BitmapFactory.Options并将该字段设置为指向{ {1}}位图并将选项发送到bitmapRotate方法,不需要在该方法中分配另一个位图。在此处阅读convertToGrayscaleinBitmap