自定义camera遇到的相关问题总结

来源:互联网 发布:linux打开终端快捷键 编辑:程序博客网 时间:2024/05/20 14:44

前段时间做了一个扫描身份证,获取身份证号相关的功能,涉及到camera,这期间遇见了不少问题,现总结如下:

1.打开camera预览界面时总是黑屏,不能进行预览,经过查看文档发现原来必须为camera设置 setPreviewDisplay(SurfaceHolder),官方文档的描述为

Important: Pass a fully initialized SurfaceHolder to setPreviewDisplay(SurfaceHolder). Without a surface, the camera will be unable to start the preview.


2.设置自动聚焦:camera的自动聚焦,可以自定义一个类实现Camera.AutoFocusCallback ,重写onAutoFocus()方法,在该方法里面可以实现自定义逻辑,比如循环聚焦,在这里我就是实现了一个利用handler发送延迟消息从而进行循环聚焦,以下为我的onAutoFocus()方法实现,这个可以参考二维码的扫描的循环聚焦,比如这篇文章的源码可以参考http://blog.csdn.net/xiaanming/article/details/1016320

public void onAutoFocus(boolean success, Camera camera) {    if (autoFocusHandler != null) {      Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success);      autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS);      autoFocusHandler = null;    } else {      Log.d("dd", "Got auto-focus callback, but no handler for it");    }  }

3.camera预览界面不清晰,这个很大可能性就是 setPictureSize和setPreviewSize有关系,这个问题也是我在实现扫描身份证时遇到的一个很大的难题,因为能力有限,以前也没了解过camera相关,在设置这两个函数的参数时,总是不能做到自适应,利用网上的查到的一些方法,获取这两个函数的合适参数,比如大神yanzi1225627的一篇博客

http://blog.csdn.net/yanzi1225627/article/details/38098729  

这里面就有一个获取这两个方法的参数的函数,但是我利用大神的这个函数获取picturesize以及previewsize后,发现在进行身份证扫描时,效率总是不是很好,因为我用的是ocr实现,所以对图片的清晰度要求比较高,就是比如在华为的荣耀6上,camera预览很清晰,扫描效果也很好,但是在三星的note3上就差强人意了,最后在查看官方文档时发现利用这个parameters的getSupportedPictureSizes和getSupportedPreviewSizes可以获取camera支持的picturesize和previewsize,我在进行循环遍历比较后,将最大的size取出,然后设置,算是勉强解决了这个问题。

Camera.Parameters parameters = camera.getParameters();Size maxPictureSize = parameters.getSupportedPictureSizes().get(0);Size maxPreviewSize = parameters.getSupportedPreviewSizes().get(0);for (int i = 0; i < parameters.getSupportedPictureSizes().size(); i++) {Size s = parameters.getSupportedPictureSizes().get(i);if (s.width > maxPictureSize.width) {maxPictureSize = s;}if(s.width==maxPictureSize.width&&s.height>maxPictureSize.height){maxPictureSize = s;}}for (int i = 0; i < parameters.getSupportedPreviewSizes().size(); i++) {Size s = parameters.getSupportedPreviewSizes().get(i);if (s.width > maxPreviewSize.width) {maxPreviewSize = s;}if(s.width==maxPreviewSize.width&&s.height>maxPreviewSize.height){maxPreviewSize = s;}}parameters.setPictureSize(maxPictureSize.width, maxPictureSize.height);parameters.setPreviewSize(maxPreviewSize.width, maxPreviewSize.height);



4.点击按钮拍摄照片,camera有一个takepicture()方法takePicture (Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg)三个参数分别为拍照声音设置方法,raw个人理解为拍摄有声照片时的回掉方法,jpeg参数即为获取图像的回掉,这里shutter以及raw都可为null,看功能需求,以下为我定义的jpeg参数,

private PictureCallback jpegCallback = new PictureCallback() {public void onPictureTaken(byte[] data, Camera _camera) {byte[] compressData = null;Bitmap b = null;if (null != data) {b = BitmapFactory.decodeByteArray(data, 0, data.length);// data是字节数据,将其解析成位图camera.stopPreview();previewing = false;}// 保存图片到sdcardif (null != b) {// 设置FOCUS_MODE_CONTINUOUS_VIDEO)之后,myParam.set("rotation",// 90)失效。// 图片竟然不能旋转了,故这里要旋转下Bitmap rotaBitmap = ImageUtil.getRotateBitmap(b, 90.0f);getRes(rotaBitmap);if (b.isRecycled()) {b.recycle();b = null;}if (rotaBitmap.isRecycled()) {rotaBitmap.recycle();rotaBitmap = null;}}// 再次进入预览camera.startPreview();previewing = true;}};


5.关于获取camera的实时预览帧,并进行处理的问题,比如获取视频的实时帧进行人脸识别,以及图像识别等,这个也是我在实现我功能时遇到的一个大问题,本来是想像支付宝或微信里的扫描银行卡那样,实现边框识别,从而在检测到边框时调用takepicture方法,采取图像然后识别,但是后来发现这个也需要对实时帧进行处理,后来查看文档发现Camera.PreviewCallback这个类,在这个类的onPreviewFrame里面可以获取实时帧数据,并保存为数据,那么获取到数据又怎么处理呢,因为处理肯定是一个耗时过程,那么这就需要用到AsyncTask了。下面介绍一下整个处理流程,首先在camera的预览过程中需要调用或者是触发onPreviewFrame这个方法,那么第一步是可以自定义一个类previewcallback实现Camera.PreviewCallback接口,然后在预览过程中调用camera.setPreviewCallback(previewcallback)或camera.setOneShotPreviewCallback(previewcallback)或camera.setPreviewCallbackWithBuffer(previewcallback)方法,本人用的是setOneShotPreviewCallback方法,这三个方法可参考

http://blog.csdn.net/ocean20/article/details/8772196

· setPreviewCallback(Camera.PreviewCallback):使用此方法注册一个Camera. PreviewCallback,这将确保在屏幕上显示一个新的预览帧时调用onPreviewFrame方法。传递到onPreviewFrame方法中的数据字节数组最有可能采用YUV格式。但是,Android 2.2是第一个包含了YUV格式解码器(YuvImage)的版本;在以前的版本中,必须手动完成解码。

· setOneShotPreviewCallback(Camera.PreviewCallback):利用Camera对象上的这个方法注册Camera.PreviewCallback,从而当下一幅预览图像可用时调用一次onPreviewFrame。同样,传递到onPreviewFrame方法的预览图像数据最有可能采用YUV格式。可以通过使用ImageFormat中的常量检查Camera. getParameters(). getPreviewFormat()返回的结果来确定这一点。

· setPreviewCallbackWithBuffer(Camera.PreviewCallback):在Android 2.2中引入了该方法,其与setPreviewCallback的工作方式相同,但要求指定一个字节数组作为缓冲区,用于预览图像数据。这是为了能够更好地管理处理预览图像时使用的内存。

在触发onPreviewFrame方法后可以在该方法里实现你对帧数据的处理了,我的处理为

final class PreviewCallback implements Camera.PreviewCallback { private IDScanTask myIDScanTask ; private CameraManager cameraManager; public PreviewCallback(CameraManager cameraManager){ this.cameraManager = cameraManager; } public void onPreviewFrame(final byte[] data, Camera camera) {      myIDScanTask = cameraManager.new IDScanTask(data);  myIDScanTask.execute((Void)null);    }}

IDScanTask 类为

public class IDScanTask extends AsyncTask<Void, Void, Void> {private byte[] mData;private Bitmap rectBitmap;// 构造函数IDScanTask(byte[] data) {this.mData = data;}@Overrideprotected Void doInBackground(Void... params) {if (camera != null) {// TODO Auto-generated method stubSize size = camera.getParameters().getPreviewSize(); // 获取预览大小final int w = size.width; // 宽度final int h = size.height;final YuvImage image = new YuvImage(mData, ImageFormat.NV21, w,h, null);ByteArrayOutputStream os = new ByteArrayOutputStream(mData.length);if (!image.compressToJpeg(new Rect(0, 0, w, h), 100, os)) {return null;}byte[] tmp = os.toByteArray();Bitmap rotaBitmap = BitmapFactory.decodeByteArray(tmp, 0,tmp.length);Bitmap bmp = ImageUtil.getRotateBitmap(rotaBitmap, 90.0f);getRes(bmp);//这是自定义的处理实时预览帧的方法}return null;}}

接下来就是什么时候触发onPreviewFrame()这个函数,可以是按一个按键触发一次,就在按键的监听里写上       myCamera.setOneShotPreviewCallback(RectPhoto.this);便会自动触发一次。有人说想先聚焦,然后再分析预览帧。就在onAutofocus里的回调写。当然也可以自定义一个线程,然后每隔一定时间实现循环触发。

这个也可以参考博客http://blog.csdn.net/yanzi1225627/article/details/8605061


6.最后一个问题也是previewCallback相关,在释放 camera的时候,最好执行camera.setOneShotPreviewCallback(null); 或者camera.setPreviewCallback(null);中止这种回调,然后再释放camera更安全。否则可能会报错。这个我刚开始没有设置,后来在note3上的确有出现问题。

  欢迎android爱好者加群362498984,备注:Android

   原创,转载请注明作者:yizhong123

0 0
原创粉丝点击