利用ZXing实现远距离扫描二维码逐级放大效果

思路:

  1. 当要扫的二维码处于扫描框中时,获取该二维码在扫描框中的宽度,与扫描框的宽度进行对比,小于扫描框宽度的1/4,则认为二维码在扫描框中较小(镜头较远),则需要放大摄像头焦距,而不需要移动手机来调整。
  2. 放大焦距。

解码流程

  1. 将图像进行二值化处理,1、0代表黑、白。
  2. 寻找定位符、校正符,然后将原图像中符号码部分取出。(detector代码实现的功能)。
  3. 对符号码矩阵按照编码规范进行解码,得到实际信息(decoder代码实现的功能)。

首先

扫描框的尺寸,在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]分别对应的是下图中的左下角–左上角的点,有这两个点就足够了,然后通过两点间的距离公式来获得大致尺寸。
在这里插入图片描述

下面是我提取出来相关的主要文件,一步一步涉及到的文件过程如下

DecodeHandler–>
–>MultiFormatReader multiFormatReader = new MultiFormatReader(); MultiFormatReader–>
setHints(){
//二维码
if (formats.contains(BarcodeFormat.QR_CODE)) {
readers.add(new QRCodeReader());
}
} QRCodeReader–>Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints){

}

我们先添加一个QRCodeReader的构造方法,因为摄像头的相关配置以及扫描框相关信息在CaptureActivity中,所以把CaptureActivity放到QRCodeReader的构造方法中去。

private CaptureActivity activity;

  public QRCodeReader(CaptureActivity activity) {
    this.activity = activity;
  }

接着是MultiFormatReader

private CaptureActivity activity;
public void setActivity(CaptureActivity activity) {
    this.activity = activity;
  }
  //这个方法中有关QRCodeReader的调用改成调用有参的构造方法
 setHints(Map<DecodeHintType,?> hints){
     if (formats.contains(BarcodeFormat.QR_CODE)) {
            readers.add(new QRCodeReader(activity));
     }
     //..... 接着是
     if (readers.isEmpty()){
         readers.add(new QRCodeReader(activity));
     }
}

然后在DecodeHandler的构造方法中对MultiFormatReader的调用setHints之前先调用setActivity()保证CaptureActivity不为空

DecodeHandler(CaptureActivity activity, Map<DecodeHintType, Object> hints) {
        multiFormatReader = new MultiFormatReader();
        multiFormatReader.setActivity(activity);
        multiFormatReader.setHints(hints);
        this.activity = activity;
    }

接下来看QRCodeReader中的这个方法,我又加了三个关键注释

@Override
  public final Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
      throws NotFoundException, ChecksumException, FormatException {
    DecoderResult decoderResult;
    ResultPoint[] points;
    if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {
      BitMatrix bits = extractPureBits(image.getBlackMatrix());
      decoderResult = decoder.decode(bits, hints);
      points = NO_POINTS;
    } else {
    //1、将图像进行二值化处理,1、0代表黑、白。( 二维码的使用getBlackMatrix方法 )
      //2、寻找定位符、校正符,然后将原图像中符号码部分取出。(detector代码实现的功能)
      DetectorResult detectorResult = new Detector(image.getBlackMatrix()).detect(hints);
      //3、对符号码矩阵按照编码规范进行解码,得到实际信息(decoder代码实现的功能)
      decoderResult = decoder.decode(detectorResult.getBits(), hints);
      points = detectorResult.getPoints();
    }

    // If the code was mirrored: swap the bottom-left and the top-right points.
    if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {
      ((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points);
    }

    Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.QR_CODE);
    List<byte[]> byteSegments = decoderResult.getByteSegments();
    if (byteSegments != null) {
      result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
    }
    String ecLevel = decoderResult.getECLevel();
    if (ecLevel != null) {
      result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
    }
    if (decoderResult.hasStructuredAppend()) {
      result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE,
                         decoderResult.getStructuredAppendSequenceNumber());
      result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY,
                         decoderResult.getStructuredAppendParity());
    }
    return result;
  }

通过DetectorResult ,我们可以获取二维码的定位符,所以在执行解码前利用定位符来获取在扫描框中的二维码大小,和扫描框进行大小对比来判断是否需要获取放大摄像头。处理结果如下:

@Override
  public final Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
      throws NotFoundException, ChecksumException, FormatException {
    DecoderResult decoderResult;
    ResultPoint[] points;
    if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {
      BitMatrix bits = extractPureBits(image.getBlackMatrix());
      decoderResult = decoder.decode(bits, hints);
      points = NO_POINTS;
    } else {
      //1、将图像进行二值化处理,1、0代表黑、白。( 二维码的使用getBlackMatrix方法 )
      //2、寻找定位符、校正符,然后将原图像中符号码部分取出。(detector代码实现的功能)
      DetectorResult detectorResult = new Detector(image.getBlackMatrix()).detect(hints);
      if(activity!=null){
        CameraManager cameraManager = activity.cameraManager;
        ResultPoint[] p = detectorResult.getPoints();
        //计算扫描框中的二维码的宽度,两点间距离公式
        float point1X = p[0].getX();
        float point1Y = p[0].getY();
        float point2X = p[1].getX();
        float point2Y = p[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 = cameraManager.getFramingRect();
        if(frameRect!=null){
          int frameWidth = frameRect.right-frameRect.left;
          Camera camera = 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 if (zoom <= maxZoom - 10) {
                zoom = zoom + 10;
              } else {
                zoom = maxZoom;
              }
              parameters.setZoom(zoom);
              camera.setParameters(parameters);
              return null;
            }
          }
        }
      }
      //3、对符号码矩阵按照编码规范进行解码,得到实际信息(decoder代码实现的功能)
      decoderResult = decoder.decode(detectorResult.getBits(), hints);
      points = detectorResult.getPoints();
    }

    // If the code was mirrored: swap the bottom-left and the top-right points.
    if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {
      ((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points);
    }

    Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.QR_CODE);
    List<byte[]> byteSegments = decoderResult.getByteSegments();
    if (byteSegments != null) {
      result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
    }
    String ecLevel = decoderResult.getECLevel();
    if (ecLevel != null) {
      result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
    }
    if (decoderResult.hasStructuredAppend()) {
      result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE,
                         decoderResult.getStructuredAppendSequenceNumber());
      result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY,
                         decoderResult.getStructuredAppendParity());
    }
    return result;
  }

放大摄像头,调整焦距

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
不需要的可以不用加。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Android应用中使用ZXing库来实现扫描二维码功能,您可以按照以下步骤操作: 1. 在您的项目中添加ZXing库的依赖。您可以在build.gradle文件中添加以下依赖: ```gradle implementation 'com.google.zxing:core:3.4.0' implementation 'com.journeyapps:zxing-android-embedded:3.6.0' ``` 2. 在您的布局文件中添加一个用于显示摄像头预览的SurfaceView: ```xml <SurfaceView android:id="@+id/surfaceView" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 3. 在您的代码中创建一个ZXing库的核心对象和一个用于处理扫描结果的回调函数: ```java private CaptureManager capture; private ZXingScannerView scannerView; private void initScanner() { scannerView = new ZXingScannerView(this); setContentView(scannerView); capture = new CaptureManager(this, scannerView); capture.initializeFromIntent(getIntent(), null); capture.decode(); scannerView.setResultHandler(new ResultHandler()); } private class ResultHandler implements ZXingScannerView.ResultHandler { @Override public void handleResult(Result rawResult) { // 处理扫描结果 String result = rawResult.getText(); // 在此处添加您的处理逻辑 // ... // 重新开始扫描 scannerView.resumeCameraPreview(this); } } ``` 4. 在您的Activity的onCreate方法中调用initScanner()方法来初始化扫描器: ```java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initScanner(); } ``` 5. 在您的Activity的onResume方法中调用capture.onResume()和scannerView.onResume()方法来恢复扫描器: ```java @Override protected void onResume() { super.onResume(); capture.onResume(); scannerView.onResume(); } ``` 6. 在您的Activity的onPause方法中调用capture.onPause()和scannerView.onPause()方法来暂停扫描器: ```java @Override protected void onPause() { super.onPause(); capture.onPause(); scannerView.onPause(); } ``` 7. (可选)您可以在您的Activity的onDestroy方法中调用capture.onDestroy()方法来释放扫描器: ```java @Override protected void onDestroy() { super.onDestroy(); capture.onDestroy(); } ``` 现在您的应用已经可以使用ZXing库来扫描二维码了。在您的应用中启动这个Activity,您将看到一个用于扫描二维码的摄像头预览界面。当您扫描到一个二维码时,它的内容将会传递给您在第3步中创建的ResultHandler对象的handleResult方法中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值