【转载】Android Camera开发:扫描二维码,周期性循环
背景:我在项目中需要用到百度的身份证识别的 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 模式,而不是连续聚焦,所以可以肯定,在这种模式下,获取数据是从预览帧里面截取的,而不是拍照。因为拍照在普通手机上一定得停预览然后开预览,这个花费时间太大了!如果是为了高更分辨率,只能拍照的话一定要控制好扫描周期。