大位图处理onPictureTaken机器人位图、机器人、onPictureTaken

2023-09-06 14:45:46 作者:∞ 记忆飘扬在共度旳天空の∞ 歌声回荡在初遇旳小巷

我的应用程序是在的tesseract 的OCR应用基地。它将从摄像机画面做OCR任务。用户可以利用多张图片,并把它们到OCR队列。为了得到更准确,我想保持高品质的图像(我选择最小尺寸为1024×768(也许在未来大),JPEG,质量100%)。当用户需要很多的图片,有三件事情要做:

保存的图像数据字节[] 到文件,并正确的EXIF。修正对设备的方向图像方向基地。我知道有一些答案提到它散发出来的摄像机的图像不会自动导向,必须从文件中更正它,就像这里和这里。我不知道这件事,我可以正确地设置摄像机preVIEW方向,但在图片搜索结果不正确。从拍摄的照片加载位图,将其转换为灰度图像并保存到另一个文件用于OCR的任务。

这是我的尝试:

 公共静态布尔saveBitmap(字节[]的位图数据,INT定位,字符串的ImagePath,字符串grayScalePath)抛出异常{    布尔rotationSuccess = FALSE;    BitmapFactory.Options选项=新BitmapFactory.Options();    options.in preferredConfig = Bitmap.Config.ARGB_8888;    位图originalBm = NULL;    位图bitmapRotate = NULL;    灰度位图= NULL;    FileOutputStream中outStream = NULL;    尝试{          //直接从字节保存[]到文件        saveBitmap(位图数据,的ImagePath);          //下采样        options.inJustDe codeBounds = TRUE;        BitmapFactory.de codeFILE(的ImagePath,期权);        INT采样大小= calculateInSampleSize(选项,Config.CONFIG_IMAGE_WIDTH,Config.CONFIG_IMAGE_HEIGHT);        options.inJustDe codeBounds = FALSE;        options.inSampleSize =采样大小;        originalBm = BitmapFactory.de codeFILE(的ImagePath,期权);        矩阵mat =新的Matrix();        mat.postRotate(方向);        bitmapRotate = Bitmap.createBitmap(originalBm,0,0,originalBm.getWidth(),originalBm.getHeight(),凉席,真);        originalBm.recycle();        originalBm = NULL;        outStream =新的FileOutputStream(新文件(的ImagePath));        bitmapRotate.com preSS(比较pressFormat.JPEG,100,outStream);        //转换为灰度         灰度= UIUtil.convertToGrayscale(bitmapRotate);        saveBitmap(灰度,grayScalePath);        grayScale.recycle();        灰度= NULL;        bitmapRotate.recycle();        bitmapRotate = NULL;        rotationSuccess =真;    }赶上(OutOfMemoryError异常五){        e.printStackTrace();        System.gc()的;    } {最后        如果(originalBm!= NULL){            originalBm.recycle();            originalBm = NULL;        }        如果(bitmapRotate!= NULL){            bitmapRotate.recycle();            bitmapRotate = NULL;        }        如果(灰度!= NULL){            grayScale.recycle();            灰度= NULL;        }        如果(outStream!= NULL){            尝试{                outStream.close();            }赶上(IOException异常五){            }            outStream = NULL;        }    }            Log.d(TAG,保存完成);    返回rotationSuccess;} 

保存到直接字节的文件[]

 公共静态无效saveBitmap(字节[]的位图数据,字符串文件名)抛出异常{    档案文件=新的文件(文件名);    FileOutputStream中FOS;    BOS的BufferedOutputStream = NULL;    尝试{        最终诠释缓冲区大小= 1024 * 4;        FOS =新的FileOutputStream(文件);        BOS =新的BufferedOutputStream(FOS,缓冲区大小);        bos.write(位图数据);        bos.flush();    }赶上(例外前){        扔恩;    } {最后        如果(BOS!= NULL){            bos.close();        }    }} 

计算规模大小

 公共静态INT calculateInSampleSize(BitmapFactory.Options选项,诠释reqWidth,诠释reqHeight){    //原始高度和图像宽度    最终诠释身高= options.outHeight;    最终诠释宽度= options.outWidth;    INT inSampleSize = 1;    如果(高度> reqHeight ||宽度GT; reqWidth){        最终诠释halfHeight =身高/ 2;        最终诠释半宽度=宽度/ 2;        //计算最大inSampleSize值是2的幂并        //既保持        //高度和宽度大于所请求的高度和宽度。        而((halfHeight / inSampleSize)GT; reqHeight和放大器;及(半角/ inSampleSize)GT; reqWidth){            inSampleSize * = 2;        }    }    返回inSampleSize;} 
四大会计师事务所智能财务机器人大对比

在保存完整,这种图像是由 UIL 加载到缩略图视图。问题是保存的任务是很慢的(之前等待第二部分保存完整,装入视图),有时候我得到了内存不足例外。结果有任何想法,以减少存储任务,并避免内存不足的异常?结果任何帮助将是AP preciated!

P / S:我第一次尝试转换字节[]为位图,而不是保存到文件,然后旋转并转换为灰度,但我上述问题仍然得到了结果。更新:这里是灰度位图处理:

 公共静态位图convertToGrayscale(位图bmpOriginal){    INT宽度,高度;    高度= bmpOriginal.getHeight();    宽度= bmpOriginal.getWidth();    位图bmpGrayscale = Bitmap.createBitmap(宽度,高度,Bitmap.Config.ARGB_8888);    帆布C =新的Canvas(bmpGrayscale);    涂料粉刷=新的油漆();    嘉洛斯厘米=新嘉洛斯();    cm.setSaturation(0);    ColorMatrixColorFilter F =新ColorMatrixColorFilter(厘米);    paint.setColorFilter(F);    c.drawBitmap(bmpOriginal,0,0,油漆);    返回bmpGrayscale;} 

OutOfMemory例外很少发生(只是几次),现在我无法重现它。

解决方案

更新:既然你还在说,该方法需要很长的时间我会定义一个回调接口

 接口BitmapCallback {    onBitmapSaveComplete(位图位图,诠释方向);    onBitmapRotateAndBWComlete(位图位图);} 

让您的活动实现上述接口和转换字节[] saveBitmap 方法的顶部为位图并触发回调,在第一次调用之前保存。旋转的ImageView 基于取向参数,并设置了的ImageView 来的骗到黑/白过滤的用户误以为位图是黑白的(在你的活动做到这一点)。看到该呼叫在主线程完成(来电来的ImageView )。 让您的老方法,你拥有了它。 (所有步骤必须完成反正)是这样的:

 公共静态布尔saveBitmap(字节[]的位图数据,INT定位,字符串的ImagePath,字符串grayScalePath,BitmapCallback回调)抛出异常{布尔rotationSuccess = FALSE;BitmapFactory.Options选项=新BitmapFactory.Options();options.in preferredConfig = Bitmap.Config.ARGB_8888;位图originalBm = NULL;位图bitmapRotate = NULL;灰度位图= NULL;FileOutputStream中outStream = NULL;尝试{    // TODO:字节转换为位图,看到该图像是不是比你想要的大小(1024z768)    callback.onBitmapSaveComplete(位图,方向);    //直接从字节保存[]到文件    saveBitmap(位图数据,的ImagePath);    。    。    //一样古老    。    。    saveBitmap(灰度,grayScalePath);    //与真正的固定位图转换完成回调    callback.onBitmapRotateAndBWComlete(灰度);    grayScale.recycle();    灰度= NULL;    bitmapRotate.recycle();    bitmapRotate = NULL;    rotationSuccess =真; 

你如何设置你的相机?什么可能导致长期执行在第一时间 saveBitmap 电话,可能是因为你使用的是默认摄像机图像尺寸设置,而不是阅读支持的相机图片的大小,选择最佳适合你的1024×768图像的需求。你可能会采取大的百万像素图像和保存等,但最终需要你需要< 1 mpixles(1024×768)。像这样的事情在code:

摄像头摄像头= Camera.open();结果参数PARAMS = camera.getParameters();结果列表大小= params.getSupportedPictureSizes();结果//循环相机尺寸和找到最佳匹配,大于1024×768

这可能是在那里你会节省大部分的时间,如果你不这样做了。而做到这一点只有一次,在一些初始化阶段。​​

增加缓冲 8K saveBitmap ,修改 1024 * 4 1024 * 8 ,这将提高性能至少不保存任何显著的时间也许吧。

要保存/重复使用位图的内存可以考虑使用 inBitmap 字段,如果你有一个职位蜂巢版本的 BitmapFactory.Options ,并设置该字段为指向 bitmapRotate 位图和发送选项到你的 convertToGrayscale 方法无需在这种方法下分配另一个位图。阅读关于 inBitmap 此处的 inBitmap

My app is an OCR app base on Tesseract. It will do OCR task from camera picture. Users can take many pictures and put them into an OCR queue. To get more accuracy, I want to keep high quality image (I choose min size is 1024 x 768 (maybe larger in future), JPEG, 100% quality). When users take many pictures, there are three things to do:

Save the image data byte[] to file and correct EXIF. Correct the image orientation base on device's orientation. I know there are some answers that said the image which comes out of the camera is not oriented automatically, have to correct it from file, like here and here. I'm not sure about it, I can setup the camera preview orientation correctly, but the image results aren't correct. Load bitmap from taken picture, convert it to grayscale and save to another file for OCR task.

And here is my try:

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

Save to file directly from 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();
        }
    }
}

Calculate scale size

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

When save complete, this image is loaded into thumbnail image view by UIL. The problem is the save task is very slow (wait some second before save complete and load into view), and sometime I got OutOfMemory exception. Is there any ideas to reduce the save task and avoid OutOfMemory exception? Any help would be appreciated!

P/S: the first time I try to convert byte[] to bitmap instead of save to file, and then rotate and convert to grayscale, but I still got above issues. Update: here is the grayscale bitmap process:

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

The OutOfMemory exception seldom occurred (just a few times) and I can't reproduce it now.

解决方案

Update: Since you're still saying that the method takes too long time I would define a callback interface

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

    onBitmapRotateAndBWComlete(Bitmap bitmap);
}

Let your activity implement the above interface and convert the byte[] to bitmap in top of your saveBitmap method and fire the callback, before the first call to save. Rotate the imageView based on the orientation parameter and set a black/white filter on the imageView to fool the user into thinking that the bitmap is black and white (do this in your activity). See to that the calls are done on main thread (the calls to imageView). Keep your old method as you have it. (all steps need to be done anyway) Something like:

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;

How do you setup your camera? What might be causing the long execution time in the first saveBitmap call, could be that you are using the default camera picture size settings and not reading the supported camera picture size and choosing best fit for your 1024x768 image needs. You might be taking big mpixel images and saving such, but in the end need you need < 1 mpixles (1024x768). Something like this in code:

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

This is probably where you will save most of the time if you are not doing this already. And do it only once, during some initialization phase.

Increase the buffer to 8k in saveBitmap, change the 1024*4 to 1024*8, this would increase the performance at least, not save any significant time perhaps.

To save/reuse bitmap memory consider using inBitmap field, if you have a post honeycomb version, of BitmapFactory.Options and set that field to point to bitmapRotate bitmap and send options down to your convertToGrayscale method to not need allocating yet another bitmap down in that method. Read about inBitmap here: inBitmap