Android

【转载】Android Camera开发:扫描二维码,周期性循环

2017-09-11  本文已影响36人  ThrowException

背景:我在项目中需要用到百度的身份证识别的 SDK ,同时使用了百度提供的 OCR-UI 模块,在测试阶段出现问题。华为 EVA AL10 机型在打开相机扫描身份证的时候频繁闪退。我这种情况跟原作者文中的情况差不多,都是自动对焦拍照然后进行识别。

原文-yanzi1225627的专栏
原文分割线


问题背景:

要让 Camera 循环聚焦,聚焦完成后进行拍照,在拍照的数据里截取出一定区域的数据。在 initCamera 里面设置聚焦模式:

List<String> allFocus = myParam.getSupportedFocusModes();
for (String ff : allFocus) {
    Log.i(tag, ff + "...FOCUS...");
}
if (allFocus.contains(Camera.Parameters.FLASH_MODE_AUTO)) { 
    myParam.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
    Focus_Mode = 1;
} else if (allFocus.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
    myParam.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);  // FOCUS_MODE_CONTINUOUS_PICTURE FOCUS_MODE_AUTO  
    Focus_Mode = 2;
}

然后有个 GetPictureThread 进行每隔一段时间聚焦并且拍照:

class GetPictureThread implements Runnable{  
    public void run() {  
        // TODO Auto-generated method stub  
        while (!Thread.currentThread().isInterrupted()) {  
            if (myCamera != null && isPreview) {  
                if (Focus_Mode == 1 && (!isFocusing)) {  
                    myCamera.autoFocus(mAutoFocusCallback);  
                } else if (Focus_Mode == 2){  
                    myCamera.takePicture(myShutterCallback, null, myJpegCallback);  
                }  
                try {  
                    Thread.sleep(3000);  
                } catch (InterruptedException e) {  
                    // TODO Auto-generated catch block  
                    e.printStackTrace();  
                    Thread.currentThread().interrupt();  
                }
            }
        }
    }  
}

原来设的 GetPictureThread 是每隔 1200 毫秒触发一次,在中兴的 Geek 手机上一切良好。在华为 G700 上,总是拍一张就挂,挂的 log 如下:

12-07 18:05:33.227: D/dalvikvm(13589): threadid=11: exiting  
12-07 18:05:33.227: W/dalvikvm(13589): threadid=11: thread exiting with uncaught exception (group=0x417669a8)  
12-07 18:05:33.230: E/AndroidRuntime(13589): FATAL EXCEPTION: Thread-1177  
12-07 18:05:33.230: E/AndroidRuntime(13589): java.lang.RuntimeException: autoFocus failed  
12-07 18:05:33.230: E/AndroidRuntime(13589):    at android.hardware.Camera.native_autoFocus(Native Method)  
12-07 18:05:33.230: E/AndroidRuntime(13589):    at android.hardware.Camera.autoFocus(Camera.java:1120)  
12-07 18:05:33.230: E/AndroidRuntime(13589):    at org.yanzi.rectphoto_wuzhou.RectPhoto$GetPictureThread.run(RectPhoto.java:428)  
12-07 18:05:33.230: E/AndroidRuntime(13589):    at java.lang.Thread.run(Thread.java:838)  
12-07 18:05:33.240: I/Camera(13589): handleMessage: 2

反正就是 auto focus 出问题了,单看这里的 log 看不出所以然来。话说 2000 元买的 G700 还是支持自动聚焦的吧。截取所有的 log 又看了次,搜索关键字: autoFocus,得到以下信息:

F:\1.log (12 hits)  
Line 12829: 12-08 10:14:19.477 D/CameraClient(  142): autoFocus (pid 10314)  
Line 12831: 12-08 10:14:19.477 D/MtkCam/CamDevice(  142): (599)(Default:0)[CamDevice::autoFocus] +  
Line 12834: 12-08 10:14:19.477 D/MtkCam/CamAdapter(  142): (599)(Default)[autoFocus] +  
Line 12861: 12-08 10:14:19.477 D/aaa_hal (  142): [autoFocus()]  
Line 12877: 12-08 10:14:19.477 D/MtkCam/CamAdapter(  142): (599)(Default)[autoFocus] -  
Line 20489: 12-08 10:14:20.677 D/CameraClient(  142): autoFocus (pid 10314)  
Line 20491: 12-08 10:14:20.677 D/MtkCam/CamDevice(  142): (142)(Default:0)[CamDevice::autoFocus] +  
Line 20503: 12-08 10:14:20.678 E/MtkCam/CamDevice(  142): (142)(Default:0)[CamDevice::autoFocus] <span style="color:#ff0000;"><strong>preview is not enabled </strong></span>(autoFocus){#552:mediatek/hardware/camera/device/CamDevice/CamDevice.cpp}  
Line 20503: 12-08 10:14:20.678 E/MtkCam/CamDevice(  142): (142)(Default:0)[CamDevice::autoFocus] <strong><span style="color:#ff0000;">preview is not enabled (autoFocus){#552:mediatek/hardware/camera/device/CamDevice/CamDevice.cpp}</span></strong>  
Line 20512: 12-08 10:14:20.679 E/AndroidRuntime(10314): java.lang.RuntimeException: autoFocus failed  
Line 20514: 12-08 10:14:20.679 E/AndroidRuntime(10314):     at android.hardware.Camera.native_autoFocus(Native Method)  
Line 20516: 12-08 10:14:20.679 E/AndroidRuntime(10314):     at android.hardware.Camera.autoFocus(Camera.java:1120)

可以看到,上面提到 preview is not enable,竟然说 preview 没有开启。可我明明 preview 已经开启了,而且我在扫描线程里面设置了判断 if
(myCamera != null && isPreview)。参考国外这位大大的帖子 传送门-地址已经失效 上面提到 auto focus 失败原因是 surfaceholder 还没有被创建,换句话 camera 还没有开启预览就进行自动聚焦了。但其实我的扫描线程启动已经加了延迟,确保 camera 预览已经开启,姑且信了吧。把线程开启的地方加到了 surfacechanged,因为我的 initCamera 是在 surfacechanged里面,initCamera 的最后就是 startPreview,但依然出错。我加了个延迟在 surfacechanged 依旧报错。后来参考又一个人的帖子,在 activity 的 onResume 方法里面进行 mySurfaceHolder.addCallback(this) 确保 surfaceview 已经创建了额再添加回调。这样干确实严谨了一点,但依旧报错。事实上这样做不是必须的,因为我把 GetPictureThread 去掉之后,一切 ok。我加个 button,拍照的时候自动聚焦,如果聚焦成功再触发 takePicture 也是 ok 的。
问题出在哪呢?最后才恍然大悟,问题在拍照的 jpegCallback 上,代码如下:

PictureCallback myJpegCallback = new PictureCallback() {  

    public void onPictureTaken(byte[] data, Camera camera) {  
        // TODO Auto-generated method stub  
        Log.i(tag, "myJpegCallback:onPictureTaken...");  

        long t1 = System.currentTimeMillis();  
        if (null != data) {  
            mBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);            
            myCamera.stopPreview(); 
            isPreview = false;  
        }  
        Matrix matrix = new Matrix();  
        matrix.postRotate((float)90.0);  
        Bitmap rotaBitmap = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth(), mBitmap.getHeight(), matrix, false);  

        //Bitmap sizeBitmap = Bitmap.createScaledBitmap(rotaBitmap, 540, 800, true);  
        Bitmap rectBitmap = Bitmap.createBitmap(rotaBitmap, square.left, square.top, square.width(), square.height());  

        if (null != rectBitmap) {             
            saveThread.setSaveBitmap(rectBitmap);  
        }  
        //ImageUtil.saveJpeg(rotaBitmap);  
        myCamera.startPreview();  
        isPreview = true;  
        long t2 = System.currentTimeMillis();  
        Log.i(tag, "本次保存耗时:" + (t2 - t1) + "毫秒");  
    }  
};

注意在拍照时,camera 首先停止预览,保存完照片后再次开启预览。尽管我加了 isPreview 这个标志,但这个标志位是不起作用的。推测, stopPreview 和 startPreview 的时候,camera 在底层是异步处理的。也就是说程序执行到 startPreview,isPreview 为真了,但这时 camera 还没有完全开启预览,而扫描线程再次出发 auto focus 就会报上面的错误。后来我对这个 myJpedCallback 测了下时间,完全同样的代码,在 geek 手机上是 900 毫秒左右,在 g700 上偶尔也还是会报错,这是因为内存占用太多,手机速度变慢, myJpegCallback 回调的周期超过了 3 秒,重启下手机就好了。在 AutoFocusCallback 里面设置标志 isFocusing 也是必须的。

final AutoFocusCallback mAutoFocusCallback = new AutoFocusCallback() {  

    public void onAutoFocus(boolean success, Camera camera) {  
        // TODO Auto-generated method stub  
        isFocusing = true;  
        if (success) {  
            Log.i(tag, "聚焦成功...");  
            myCamera.takePicture(myShutterCallback, null, myJpegCallback);  
        } else {  
            Log.i(tag, "聚焦失败...");  
        }     
        isFocusing = false;  
    }  
};

看来,要玩 Camera 还是得整个高端点的手机啊!!!

参考 StackOverflow传送门,自从 API 19,就有了连续聚焦。参考 官方对聚焦模式的说明,聚焦最强的是 FOCUS_MODE_CONTINUOUS_PICTURE,它是和 AutoFocus 兼容的,其次是 FOCUS_MODE_CONTINUOUS_VIDEO,聚焦强度弱于 CONTINUOUS_PICTURE。所以如果能满足需求的话,FOCUS_MODE_CONTINUOUS_VIDEO 就 ok 了!有些资料不建议周期性 auto focus,但看今天的扫描二维码貌似都是用的 auto focus 模式,而不是连续聚焦,所以可以肯定,在这种模式下,获取数据是从预览帧里面截取的,而不是拍照。因为拍照在普通手机上一定得停预览然后开预览,这个花费时间太大了!如果是为了高更分辨率,只能拍照的话一定要控制好扫描周期。

上一篇下一篇

猜你喜欢

热点阅读