2021SC@SDUSC Zxing开源代码(四)Camera相关优化策略


前言

在上一篇博客,具体分析了Camera相关配置的一些问题,但是在实际实用真机调试的时候,发现还是有很多与摄像头相关的问题,包括只支持横屏扫描、识别精准度不高、扫描区域小等。因此尝试对相关问题进行改进


一、扫描方向问题

将 Zxing 项目使用真机调试后,所显示的页面如下:
在这里插入图片描述
也就是说,Zxing 项目在默认情况下是横屏扫描的,并且不支持随手机自动旋转。在前面已经学习了 Android 中 Camera 方向的一些知识,这里尝试修改一下源码,以使项目支持自动旋转。

首先修改 AndroidManifest.xml 文件,将下面设置屏幕横向的代码注释

<uses-feature android:name="android.hardware.screen.landscape"/>

MyOrientationDetector

在 Android 中,OrientationEventListener 可以监听屏幕旋转角度,因此为了实现 Zxing 项目的自动旋转,要自定义一个类来继承 OrientationEventListener
这里将其定义在 CaptureActivity 中

  private class MyOrientationDetector extends OrientationEventListener {
    // 继承构造方法
    MyOrientationDetector(Context context) {
      super(context);
    }

    @Override
    public void onOrientationChanged(int orientation) {
      // 如果手机平放,无法检测方向
      if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN)
        return;
      if (orientation > 350 || orientation < 10)
        orientation = 0;
      else if (orientation > 80 && orientation < 100)
        orientation = 90;
      else if (orientation > 170 && orientation < 190)
        orientation = 180;
      else
        return;
    }
  }

在 CaptureActivity 中声明变量

MyOrientationDetector myOrientationDetector;

在 onCreate 中创建 MyOrientationDetector 实例对象

myOrientationDetector = new MyOrientationDetector(this);

在 onResume 中判断是否允许屏幕自动旋转,如果允许则启动监听

if (prefs.getBoolean(PreferencesActivity.KEY_DISABLE_AUTO_ORIENTATION, true)) {
      setRequestedOrientation(getCurrentOrientation());
    } else {
      setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR); // 旋转
      myOrientationDetector.enable(); //启用监听
    }

在 onPause 中关闭屏幕旋转监听

myOrientationDetector.disable();

打开真机调试,此时已经可以竖屏显示页面:
在这里插入图片描述
但是仅仅实现了屏幕旋转监听,此时无法扫描探测到二维码,还需要进行更多的修改
首先是相机预览框的设置,在 CameraManager 的 getFramingRectInPreview() 方法中,需要设置竖屏情况下预览框的绘制

 if(screenResolution.x < screenResolution.y){
        // 竖屏情况
        rect.left = rect.left * cameraResolution.y / screenResolution.x;
        rect.right = rect.right * cameraResolution.y / screenResolution.x;
        rect.top = rect.top * cameraResolution.x / screenResolution.y;
        rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
      } else {
        // 横屏情况
        rect.left = rect.left * cameraResolution.x / screenResolution.x;
        rect.right = rect.right * cameraResolution.x / screenResolution.x;
        rect.top = rect.top * cameraResolution.y / screenResolution.y;
        rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
      }

但是在真机调试中发现,虽然页面显示了取景框,扫描二维码还是没有反应。在查找资料后得知,在 PreviewCallback 的 onPreviewFrame 方法中,获取的是默认情况下预览页面的一帧数据,也就是说虽然屏幕的方向是竖向的,扫描得到的图像仍是横向的。因此需要对获取到的图像数组进行反置,这里在 DecodeHandler 的 decode 方法中处理:

  private void decode(byte[] data, int width, int height) {
    if (width < height) {
      // 竖屏情况,将数组内容反置
      byte[] rotatedData = new byte[data.length];
      for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++)
          rotatedData[y * width + width - x - 1] = data[y + x * height];
      }
      data = rotatedData;
    }
    ......

再进行真机调试,则成功扫码解码!
在这里插入图片描述

二、图形拉伸问题

在上一篇博客中,我们分析到获得相机分配率的函数时,已经了解到了 Zxing 项目获取相机最佳分辨率 findBestPreviewSizeValue 方法中,如果没有找到精确匹配的尺寸,那么就选择最大尺寸。但是带来的问题是最大尺寸宽高比与屏幕宽高比相差可能很大,造成图像拉伸问题。

因此这里考虑修改下寻找尺寸的策略,将算法的核心从最大的尺寸改为比例最接近的尺寸,这样便能够最原始地接近屏幕分辨率的宽高比

在 CameraConfigurationUtils 中定义一个内部类 SizeComparator,用以比较两个尺寸的接近程度

private static class SizeComparator implements Comparator<Camera.Size> {
    private final int width;
    private final int height;
    // 宽高比
    private final float ratio;
	// 构造函数
    SizeComparator(int width, int height) {
        if (width < height) {
            this.width = height;
            this.height = width;
        } else {
            this.width = width;
            this.height = height;
        }
        this.ratio = (float) this.height / this.width;
    }

    @Override
    public int compare(Camera.Size size1, Camera.Size size2) {
        int width1 = size1.width;
        int height1 = size1.height;
        int width2 = size2.width;
        int height2 = size2.height;

        float ratio1 = Math.abs((float) height1 / width1 - ratio);
        float ratio2 = Math.abs((float) height2 / width2 - ratio);
        int result = Float.compare(ratio1, ratio2);
        if (result != 0) {
            return result;
        } else {
            int minGap1 = Math.abs(width - width1) + Math.abs(height - height1);
            int minGap2 = Math.abs(width - width2) + Math.abs(height - height2);
            return minGap1 - minGap2;
        }
    }
}

再定义方法 findCloselySize,在所有相机支持的预览尺寸中进行排序,得到与宽高比最接近的尺寸

/**
 * 通过对比得到与宽高比最接近的尺寸(如果有相同尺寸,优先选择)
 *
 * @param surfaceWidth 需要被进行对比的原宽
 * @param surfaceHeight 需要被进行对比的原高
 * @param preSizeList 需要对比的预览尺寸列表
 * @return 得到与原宽高比例最接近的尺寸
 */
protected Camera.Size findCloselySize(int surfaceWidth, int surfaceHeight, List<Camera.Size> preSizeList) {
    Collections.sort(preSizeList, new SizeComparator(surfaceWidth, surfaceHeight));
    return preSizeList.get(0);
}

三、相机预览倍数设置及聚焦时间调整

如果使用zxing默认的相机配置,会发现需要离二维码很近才能够识别出来,但这样会带来一个问题——聚焦困难。解决办法就是调整相机预览倍数以及减小相机聚焦的时间。

通过测试可以发现,每个手机的最大放大倍数几乎是不一样的,这可能和摄像头的型号有关。如果设置成一个固定的值,那可能会产生在某些手机上过度放大,某些手机上放大的倍数不够。索性相机的参数设定里给我们提供了最大的放大倍数值,通过取放大倍数值的N分之一作为当前的放大倍数,就完美地解决了手机的适配问题。

// 需要判断摄像头是否支持缩放
Parameters parameters = camera.getParameters();
if (parameters.isZoomSupported()) {
	// 设置成最大倍数的1/10,基本符合远近需求
    parameters.setZoom(parameters.getMaxZoom() / 10);
}

zxing默认的相机聚焦时间是2s,可以根据扫描的视觉适当调整。聚焦时间的调整也很简单,在 AutoFocusCallback 这个类里,调整 AUTO_FOCUS_INTERVAL_MS 这个值就可以了


总结

在一开始分析完 Camera 部分代码,并且在真机调试中发现相机的相关问题后,就打算对这些问题进行优化。但是当时对 Zxing 的代码很不熟悉,了解到的只是片面的内容。因此在后面分析完扫码流程部分后,才真正地实现了优化,并且补写了这篇博客。对 Zxing 代码的理解更进一步加深了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值