美文网首页安卓集中营实用控件Android开发之路
QrCodeScanner扫码工具(融合Zxing和Zbar库,

QrCodeScanner扫码工具(融合Zxing和Zbar库,

作者: heiBIN | 来源:发表于2016-12-20 14:51 被阅读8284次

    融合Zxing和Zbar库,对条形码二维码高兼容,扫码效率奇高!

    开发背景

    前段采用Zxing和Zbar扫码库扫码时,发现针对二维码和条形码,两个库有不同的效果,Zxing能高效精准识别二维码,但在条形码上却不尽人意,在某些手机死活识别不出来;而后采用Zbar库,在条形码上效果杠杠的,而二维码相对Zxing就逊色很多了。根据这些情况,我就觉得有必要搞个'万金油',把两者优势融合起来,于是结合Zxing二维码算法和Zbar条形码算法的QrCodeScanner扫码工具就凭空而出。

    开发工作

    比对了Zxing和Zbar两个扫码Demo,Zxing的相机扫描部分算是做得比较精良,Zbar就不想说了,最后决定采用Zxing扫描部分的代码,但是也有很多地方与需求不符,所以结合网上资料和自己的分析,修改了其中的代码。然后整个识别算法代码是放在C层的。下面就将几个核心部分列举出来。

    1.调整扫描采样区域,优化取图速度

    在CameraManager类中不改变扫码框大小,但是增加采样区域大小,我这里是根据屏幕宽度作为边长,截取中间正方形为取图区域。

    public Rect getRealFramingRect() {
        if (realFramingRect == null) {
            //获取屏幕大小,然后根据屏幕宽度由中间截取于宽度等长的正方形
            Point screenResolution = configManager.getScreenResolution();
            int leftOffset = 0;
            int topOffset = (screenResolution.y - screenResolution.x) / 2;
            Rect rect = new Rect(leftOffset, topOffset, screenResolution.x,
                    screenResolution.x+topOffset);
            
            //根据图片分辨率和屏幕分辨率截取实际大小的图片区域
            Point cameraResolution = configManager.getCameraResolution();
            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;
            realFramingRect = rect;
        }
        return realFramingRect;
    }
    
    2.获取适配的摄像头预览图片,防止图片拉伸

    在CameraConfigurationManager类中获取摄像头所有预览尺寸,根据屏幕分辨率选取最适合的预览尺寸。

    private static Point findBestPreviewSizeValue(
        CharSequence previewSizeValueString, Point screenResolution) {
        int bestX = 0;
        int bestY = 0;
        int diff = Integer.MAX_VALUE;
        //previewSizeValueString为包含所有预览尺寸的字符串
        for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {
            previewSize = previewSize.trim();
            int dimPosition = previewSize.indexOf('x');
            if (dimPosition < 0) continue;
            try {
                int newX = Integer.parseInt(previewSize.substring(0, dimPosition));
                int newY = Integer.parseInt(previewSize.substring(dimPosition + 1));
                int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y);
                if (newDiff == 0) {
                    bestX = newX;bestY = newY;
                    break;
                } else if (newDiff < diff) {
                    bestX = newX;bestY = newY;diff = newDiff;
                }
            } catch (NumberFormatException nfe) {
                continue;
            }
        }
        if (bestX > 0 && bestY > 0) {
            return new Point(bestX, bestY);
        }
        return null;
    }
    
    3.旋正摄像头获取的YUV图片数据,供识别库识别

    摄像头获取的图片都是横屏的,所以对图片识别前,我们必须把图片旋正过来,否则就无法正确识别。这个的图片是YUV格式数据,其中"Y"表示明亮度(Lumina nce或Luma),也就是灰阶值;而"U"和"V"表示的则是色度(Chrominance或Chroma),而在识别时,只取Y部分的数据就可以了,相当于把图片灰度化。而为了提高计算效率,我把这部分代码放在C层中实现了。

    char* buffer = (char*) env->GetByteArrayElements(data, JNI_FALSE);
    char*rotateData = new char[dataWidth * dataHeight];
    for(int y = 0; y < dataHeight; y++) {
        for (int x = 0; x < dataWidth; x++) {
            rotateData[x * dataHeight + dataHeight - y - 1] = buffer[x + y * dataWidth];
        }
    }
    int tmp = dataWidth;
    dataWidth = dataHeight;
    dataHeight = tmp;
    
    4.融合ZBar和Zxing库,整个算法部分用NDK实现。

    同样是为了提高运算效率,我把两部分的算法代码都放在C层实现了,Zbar采用官方的C库,Zxing采用C++库,去掉两个库多余的代码,精简代码结构,封装公共部分代码,从而实现识别运算上的优化。

    关于整个项目的优化点还有好多,这里就不一一列举了,这里已经将全部代码放上去了,可以直接通过代码来查看。

    使用说明

    1.使用Android Studio开发,配置Cmake环境

    这里通过Cmake来编译Native部分代码,所以编译时需要配置Cmake工具。如果不修改jni接口(DecodeEntry.cpp),也可以直接使用本项目提供的.so库,把extraLib文件夹中的.so文件复制到项目lib目录下即可。

    2.修改识别模式

    目前识别模式有三种,只识别二维码,只识别条形码,识别二维码加条形码(默认)。可以根据自己需求选择不同模式,有利于提高识别效率。通过BarcodeFormat类进行修改。

        barcodeFormat = new BarcodeFormat();
        barcodeFormat.add(BarcodeFormat.BARCODE);
        barcodeFormat.add(BarcodeFormat.QRCODE);
    

    然后作为参数传进CaptureActivityHandler对象中。

    handler = new CaptureActivityHandler(this, barcodeFormat);
    

    源码地址:

    https://github.com/heiBin/QrCodeScanner

    Demo地址:

    https://github.com/heiBin/QrCodeScanner/raw/master/QrCodeScanner.apk

    (操作补充说明):

    直接使用编译好的.so文件:
    ZlVbuXS2ViNEwKwkJzzeZw==.png

    如上图所示,在app-main下加入jniLibs目录,把extraLib下编译好的.so文件全部复制过来;然后如上图把build.gradle下的cmake相关代码注释掉。

    相关文章

      网友评论

      • liu58999:大神,你好,我用ionic3开发,能否直接用你的代码?
      • e837c157b24a:java.lang.UnsatisfiedLinkError: No implementation found for java.lang.String com.scanner.qrdecode.DecodeEntry.decodeFromJNI(int, byte[], int, int, int, int, int, int) (tried Java_com_framework_scanner_qrdecode_DecodeEntry_decodeFromJNI and Java_com_scanner_qrdecode_DecodeEntry_decodeFromJNI__I_3BIIIIII)
        我把包导进去用就报这个错,咋办呀
        Muse_Ykh:是因为jni的方法里的包名与你实际Java类DecodeEntry的包名不一致,导致无法调用到对应的native方法,所以需要你自己修改demo里DecodeEntry.cpp中的包名,然后自己编译生成so文件再使用
      • 5a37c2f93776:大神你好,我cMake完成后,与直接导入so后,这两种感觉代码不一样啊,直接导入so的识别很精准,cMake的并不准确,而且这两种方式,在从相册选图识别时,很容易卡死,请问有什么方式解决吗
      • 任半生嚣狂:zbar 扫ISBN 时有问题
      • 叨码:用过公司硬件设备商自带的扫码demo,一般都是二维码一放入镜头区域 (并不是完全在预览框内)即可识别。感觉速率很高,怎么优化 却无从下手
      • 叨码:体验了一把 效率奇高 。有点夸大,识别速率还是不很理想。有几款扫码效率当得起奇高的软件,不知道是具体怎么优化的。。
      • Robo_G:为什么我导出的so文件有9M多,而楼主的却很小,是我哪里的姿势不对吗?:sweat:
      • 哈哈zx:华为手机有时打开扫描没有任何反应
      • 风影_638f:确实快了点。
      • AiFocus:公司产品属于金属二维码,有点反光,扫描效果不理想,有思路解决吗
      • 7a56255a97aa:您好,在三星上没有开始扫描 使用的jniLibs so库文件 系统版本 4.3 请问您有碰到类似问题吗
      • null_null_:你的这个项目我打不开
        null_null_:@heiBIN 我看了以下,工程中是NDK路径你都配好了啊,然后我这儿跑不起来,就吧所有的库文件都引用到你的module下,还是跑不起来。
        heiBIN:@HELLO丶GUY 项目大部分是基于ndk开发的,ide使用AS,需要配置NDK和Cmake编译环境。如果你嫌ndk配置麻烦,也可以直接使用我编译好的so库,把ndk部分代码去掉,把so库引用到你项目的libs目录下。
        heiBIN:@HELLO丶GUY 打不开是指编译不成功吗?

      本文标题:QrCodeScanner扫码工具(融合Zxing和Zbar库,

      本文链接:https://www.haomeiwen.com/subject/pjzxvttx.html