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

前段时间做了一个扫描身份证,获取身份证号相关的功能,涉及到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;
			}
			// 保存图片到sdcard
			if (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;
		}

		@Override
		protected Void doInBackground(Void... params) {
			if (camera != null) {
				// TODO Auto-generated method stub
				Size 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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值