结合ZXing实现类似微信扫二维码放大摄像头

来源:互联网 发布:统计学数据分析案例题 编辑:程序博客网 时间:2024/06/07 14:24

目前android中实现扫二维码大多数使用的是zxing这个开源框架,要使用android的核心源码,因为我们需要在源码中做修改,将框架添加到项目中,这里就不多说了,网上都有,这里只说一下放大摄像头部分。涉及到的文件主要有DecodeHandler,CameraManager,Result。

实际应用中,我们都知道镜头离二维码太远或者太近都影响识别,二维码恰好处于扫描框中最好。
思路:
1、当要扫的二维码处于扫描框中时,获取该二维码在扫描框中的宽度,与扫描框的宽度进行对比,小于扫描框宽度的1/4,则认为二维码在扫描框中较小(镜头较远),则需要放大摄像头焦距,而不需要移动手机来调整
2、摄像头焦距的放大

1、获取二维码在扫描框中的宽度
CamerManager中getFramingRect()方法是获取扫描框的矩形尺寸,frameRect.right-frameRect.left来获取扫描框宽度。

Rect frameRect = activity.cameraManager.getFramingRect();if(frameRect!=null){  int frameWidth = frameRect.right-frameRect.left;}

DecodeHandler文件中,decode(byte[] data, int width, int height),是对扫描结果的解析处理,我们在这个方法中进行所有的逻辑工作。
先看一下源码:

/** * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency, * reuse the same reader objects from one decode to the next. * * @param data   The YUV preview frame. * @param width  The width of the preview frame. * @param height The height of the preview frame. */private void decode(byte[] data, int width, int height) {  long start = System.currentTimeMillis();  Result rawResult = null;  PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);  if (source != null) {    BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));    try {      rawResult = multiFormatReader.decodeWithState(bitmap);    } catch (ReaderException re) {      // continue    } finally {      multiFormatReader.reset();    }  }  Handler handler = activity.getHandler();  if (rawResult != null) {    // Don't log the barcode contents for security.    long end = System.currentTimeMillis();    Log.d(TAG, "Found barcode in " + (end - start) + " ms");    if (handler != null) {      Message message = Message.obtain(handler, Ids.decode_succeeded, rawResult);              Bundle bundle = new Bundle();              bundleThumbnail(source, bundle);              message.setData(bundle);              message.sendToTarget();    }  } else {    if (handler != null) {      Message message = Message.obtain(handler, Ids.decode_failed);      message.sendToTarget();    }  }}

Result 就是结果,我们需要从中获取一些信息。要获取二维码的尺寸,只需要获取两个定位的点就行了,这个通过Result的getResultPoints()来获取,得到的是一个ResultPoint数组,通过调试,结合安卓中的坐标系得知的结果是resultPoints[0],resultPoints[1]分别对应的是下图中的左下角–左上角的点,有这两个点就足够了,然后通过两点间的距离公式来获得大致尺寸。
这里写图片描述

//计算扫描框中的二维码的宽度,两点间距离公式float point1X = rawResult.getResultPoints()[0].getX();float point1Y = rawResult.getResultPoints()[0].getY();float point2X = rawResult.getResultPoints()[1].getX();float point2Y = rawResult.getResultPoints()[1].getY();int len =(int) Math.sqrt(Math.abs(point1X-point2X)*Math.abs(point1X-point2X)+Math.abs(point1Y-point2Y)*Math.abs(point1Y-point2Y));

有了扫描框的宽度和二维码的尺寸,就可以判断二维码在扫描框中是不是太小。
小于扫描框宽度的1/4,就放大镜头,通过handler告知此次扫描失败重新扫描,否则不用

二、放大摄像头,调整焦距
Camera的当前设置信息在Parameters可以获取,通过getParameters()获取Parameters。
放大摄像头有个前提条件就是你的手机要支持摄像头焦距的放大和缩小,不过目前大多数手机都支持了,我们还是判断一下为好。parameters.isZoomSupported(),判断是否支持焦距缩放。支持,然后设置需要放大多少,通过parameters.setZoom(int value)来设置,这个值有个限制, The valid range is 0 to {@link #getMaxZoom}.
也就是最大能设置到maxzoom,这个最大值通过getMaxZoom()来获取。设置完成后,然后再调用camera.setParameters(parameters);让设置生效。另外,我加了一个判断,当扫描的是二维码才进行焦距的缩放rawResult.getBarcodeFormat() == BarcodeFormat.QR_CODE
不需要的可以不用加。综上所述,修改这个decode方法如下,代码中的activity是扫描界面。

/** * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency, * reuse the same reader objects from one decode to the next. * * @param data   The YUV preview frame. * @param width  The width of the preview frame. * @param height The height of the preview frame. */private void decode(byte[] data, int width, int height) {  long start = System.currentTimeMillis();  Result rawResult = null;  PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);  if (source != null) {    BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));    try {      rawResult = multiFormatReader.decodeWithState(bitmap);    } catch (ReaderException re) {      // continue    } finally {      multiFormatReader.reset();    }  }  Handler handler = activity.getHandler();  if (rawResult != null) {    // Don't log the barcode contents for security.    long end = System.currentTimeMillis();    Log.d(TAG, "Found barcode in " + (end - start) + " ms");    if (handler != null) {      if (rawResult.getBarcodeFormat() == BarcodeFormat.QR_CODE) {        Log.e("ssssss", "是二维码");        //计算扫描框中的二维码的宽度,两点间距离公式        float point1X = rawResult.getResultPoints()[0].getX();        float point1Y = rawResult.getResultPoints()[0].getY();        float point2X = rawResult.getResultPoints()[1].getX();        float point2Y = rawResult.getResultPoints()[1].getY();        int len =(int) Math.sqrt(Math.abs(point1X-point2X)*Math.abs(point1X-point2X)+Math.abs(point1Y-point2Y)*Math.abs(point1Y-point2Y));        Rect frameRect = activity.cameraManager.getFramingRect();        if(frameRect!=null){          int frameWidth = frameRect.right-frameRect.left;          Camera camera = activity.cameraManager.getOpenCamera().getCamera();          Camera.Parameters parameters = camera.getParameters();          int maxZoom = parameters.getMaxZoom();          int zoom = parameters.getZoom();          if(parameters.isZoomSupported()){//支持放大镜头            if(len <= frameWidth/4){//二维码在扫描框中的宽度小于扫描框的1/4,放大镜头              if(zoom==0){                zoom = maxZoom/2;              }else{                zoom = zoom+10;              }              if(zoom>maxZoom) {                zoom=maxZoom;              }                           parameters.setZoom(zoom);              camera.setParameters(parameters);              //重新扫描              Message message = Message.obtain(handler, Ids.decode_failed);              message.sendToTarget();            }else{              Message message = Message.obtain(handler, Ids.decode_succeeded, rawResult);              Bundle bundle = new Bundle();              bundleThumbnail(source, bundle);              message.setData(bundle);              message.sendToTarget();            }          }else{            Message message = Message.obtain(handler, Ids.decode_succeeded, rawResult);            Bundle bundle = new Bundle();            bundleThumbnail(source, bundle);            message.setData(bundle);            message.sendToTarget();          }        }else{          Message message = Message.obtain(handler, Ids.decode_succeeded, rawResult);          Bundle bundle = new Bundle();          bundleThumbnail(source, bundle);          message.setData(bundle);          message.sendToTarget();        }      }else{        Message message = Message.obtain(handler, Ids.decode_succeeded, rawResult);        Bundle bundle = new Bundle();        bundleThumbnail(source, bundle);        message.setData(bundle);        message.sendToTarget();      }    }  } else {    if (handler != null) {      Message message = Message.obtain(handler, Ids.decode_failed);      message.sendToTarget();    }  }}

运行后就可以实现类似微信的那种效果了。

昨天收到提的问题,这个确实是有问题了,现在是已经识别到然后放大并不直接展示结果而是再去放大镜头再来一次,太多此一举了,后面研究一下优化,谢谢这位朋友提的问题。