《微信二维码引擎OpenCV开源研究》

一、编译和Test测试

       opencv_wechat_qrcode的编译需要同时下载opencv(https://github.com/opencv/opencv)和opencv_contrib(https://github.com/opencv/opencv_contrib),如果需要正常运行Test,还要下载opencv_extra(https://github.com/opencv/opencv_extra)。
       Windows环境下,使用Cmake进行编译,总的来说是“两次Configue一次Generate",这个过程中,由于网络和基础环境原因,可能出现各种问题,需要根据实际情况解决,其中一个必须解决的一个问题是需要自己下载模型文件,改名后拷贝到指定目录下来。
在cmake的过程中,可以关闭不需要生产的模块。
       
打开VisualStudio,选择”批生成Install",确保编译过程中不出现错误。
       如果上面都顺利,那么我们能够在Cmake中“where to build the binaries"目录下得到新建的Install目录。
       进一步,将opencv_extra解压出来的testdata目录防止install下,则可以开启Test测试。
这些图片还是非常有代表性的,具体位置:testdata\cv\qrcode
进入VisualStudio,找到opencv_test_wechat_qrcode,右击设置为启动,如果看到全绿回显,证明前面配置全部正确。
这样,我们就可以在opencv_wechat_qrcode中设置断点,逐句解析其实现。
二、代码理解
在Opencv_wechat_qrcode中,wechat_qrcode.cpp是主要文件,其他的是配合文件。
vector<float> WeChatQRCode::Impl::getScaleList(const int width, const int height) {
    if (width < 320 || height < 320) return {1.0, 2.0, 0.5};
    if (width < 640 && height < 640) return {1.0, 0.5};
    return {0.5, 1.0};
}

根据分辨率获得缩放的可能选项。
Mat SuperScale::processImageScale(const Mat &src, float scale, const bool &use_sr,
                                  int sr_max_size) {
    Mat dst = src;
    if (scale == 1.0) {  // src
        return dst;
    }
    int width = src.cols;
    int height = src.rows;
    if (scale == 2.0) {  // upsample
        int SR_TH = sr_max_size;
        if (use_sr && (int)sqrt(width * height * 1.0) < SR_TH && net_loaded_) {
            int ret = superResoutionScale(src, dst);
            if (ret == 0) return dst;
        }
        { resize(src, dst, Size(), scale, scale, INTER_CUBIC); }
    } else if (scale < 1.0) {  // downsample
        resize(src, dst, Size(), scale, scale, INTER_AREA);
    }
    return dst;
}

具体调用方法是使用dnn的方法
int SuperScale::superResoutionScale(const Mat &src, Mat &dst) {
    Mat blob;
    dnn::blobFromImage(src, blob, 1.0 / 255, Size(src.cols, src.rows), {0.0f}, falsefalse);
    srnet_.setInput(blob);
    auto prob = srnet_.forward();
    dst = Mat(prob.size[2], prob.size[3], CV_8UC1);
    for (int row = 0; row < prob.size[2]; row++) {
        const float *prob_score = prob.ptr<float>(0, 0, row);
        for (int col = 0; col < prob.size[3]; col++) {
            float pixel = prob_score[col] * 255.0;
            dst.at<uint8_t>(row, col) = static_cast<uint8_t>(CLIP(pixel, 0.0f, 255.0f));
        }
    }
    return 0;
}

这里的srnet_就是特定的网络。
int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, string& result) {
    int width = src.cols;
    int height = src.rows;
    if (width <= 20 || height <= 20)
        return -1;  // image data is not enough for providing reliable results
    std::vector<uint8_t> scaled_img_data(src.data, src.data + width * height);
    zxing::ArrayRef<uint8_t> scaled_img_zx =
        zxing::ArrayRef<uint8_t>(new zxing::Array<uint8_t>(scaled_img_data));
    zxing::Ref<zxing::Result> zx_result    ;
    decode_hints_.setUseNNDetector(use_nn_detector);
    Ref<ImgSource> source;
    qbarUicomBlock_ = new UnicomBlock(width, height);
    // Four Binarizers
    int tryBinarizeTime = 4;
    for (int tb = 0; tb < tryBinarizeTime; tb++) {
        if (source == NULL || height * width > source->getMaxSize()) {
            source = ImgSource::create(scaled_img_zx.data(), width, height);
        } else {
            source->reset(scaled_img_zx.data(), width, height);
        }
        int ret = TryDecode(source, zx_result);
        if (!ret) {
            result = zx_result->getText()->getText();
            return ret;
        }
        // try different binarizers
        binarizer_mgr_.SwitchBinarizer();
    }
    return -1;
}


相比较直接使用ZXing来解码,这里做了很多的前置算法操作.目前能够看懂的部分就是tryBinarizeTime=4,这里进行了4次运算。每一次都是trydecode,这种模式是可以借鉴的。
int DecoderMgr::TryDecode(Ref<LuminanceSource> source, Ref<Result>& result) {
    int res = -1;
    string cell_result;
    // get binarizer
    zxing::Ref<zxing::Binarizer> binarizer = binarizer_mgr_.Binarize(source);
    zxing::Ref<zxing::BinaryBitmap> binary_bitmap(new BinaryBitmap(binarizer));
    binary_bitmap->m_poUnicomBlock = qbarUicomBlock_;
    result = Decode(binary_bitmap, decode_hints_);
    res = (result == NULL) ? 1 : 0;
    if (res == 0) {
        result->setBinaryMethod(int(binarizer_mgr_.GetCurBinarizer()));
    }
    return res;
}


TryDecode这个就是具体的解码操作,具体就是调用

 zxing::Ref<zxing::qrcode::QRCodeReader> reader_;

从Test结果来看,如果不适用DNN,是33个通过;如果使用DNN是30个通过。这里的差异的原因是什么?那么使用DNN、训练这些模型的价值体现在哪里?
[  PASSED  ] 30 tests.
[  FAILED  ] 3 tests, listed below:
[  FAILED  ] Objdetect_QRCode.regression/0, where GetParam() = "version_1_down.jpg"
[  FAILED  ] Objdetect_QRCode.regression/10, where GetParam() = "version_4_left.jpg"
[  FAILED  ] Objdetect_QRCode.regression/19, where GetParam() = "link_ocv.jpg"

三、借鉴使用
1、规范的语法和构建
我也在向OpenCV提交代码,我需要从这个例子中学到业务方面的操作。
2、CNN方法和传统方法无缝连接
    p = makePtr<WeChatQRCode::Impl>();
    if (!detector_caffe_model_path.empty() && !detector_prototxt_path.empty()) {
        // initialize detector model (caffe)
        p->use_nn_detector_ = true;
        CV_Assert(utils::fs::exists(detector_prototxt_path));
        CV_Assert(utils::fs::exists(detector_caffe_model_path));
        p->detector_ = make_shared<SSDDetector>();
        auto ret = p->detector_->init(detector_prototxt_path, detector_caffe_model_path);
        CV_Assert(ret == 0);
    } else {
        p->use_nn_detector_ = false;
        p->detector_ = NULL;
    }

当CNN无法正确调用的时候,几个图片都是一致的,采用传统方法进行处理:
3、最后我想说值得学习的还有这个思想
传统已经无法实现很好解决的问题,我们通过训练模型方式来进行解决。OpenCV用于实际的解码,就部署来说是非常方便的。这是一种绝佳的配合。二维码的扫描具有很强的专用性,只有微信、支付宝一类的平台软件才会具备,当然我们自己也可以写来测试一下。因此这里将其开源出来,是非常聪明的选择。


微信二维码引擎OpenCV开源研究的更多相关文章

  1. 微信二维码支付-模式一(PC端,解决中文乱码问题)

    近期公司调完银联,调支付宝,调完支付宝调微信.说实话微信的帮助文档确实是烂,而且有没有技术支持,害的我头发都掉了一桌.不说废话了,看代码. 首先登陆微信的公众平台(微信的服务号不是订阅号),然后选择微 ...

  2. HTML5 微信二维码提示框

    这是一个js的小案例,主要效果是显示一个微信二维码的提示框,非常简单实用. 源码如下: JS部分 <script src="js/jquery-1.8.3.min.js"&g ...

  3. CSS实现鼠标经过网页图标弹出微信二维码

     特点 1.纯CSS实现二维码展示功能,减少加载JS: 2.使用CSS3 transform 属性: ## 第一步 在需要展示二维码的地方添加如下代码,其中<a>标签内容可以根据需要修改成 ...

  4. C#获取微信二维码显示到wpf

    微信的api开放的二维码是一个链接地址,而我们要将这个二维码显示到客户端.方式很多,今天我们讲其中一种. /// <summary> /// 获取图片路径 /// </summary ...

  5. php 人人商城 生成 临时微信二维码,并保存成海报图片 有效期一个月

    public function getPoster(){ global $_W; global $_GPC; $mm = pdo_fetch('select nickname,codetime fro ...

  6. CSDN的个人主页如何添加微信二维码

    -–零-– 对于CSDN,这里是技术的交流的地方,有些大神,隐于此.各有各的技能,各有各的魅力. 在这里,如果有自己的能力,你想推广你个人.我想,你将你的微信二维码或者你的微信公众号的二维码放在这里, ...

  7. react页面内嵌微信二维码 和 自定义样式 以及 微信网页共用unionId问题

    在react页面内嵌“微信二维码”,实现PC端通过微信扫码进行登录.首先去微信开放平台注册一个账号,创建一个网站应用,提交网站备案审核,获取appid和appsecret:其他开发流程根据微信文档来进 ...

  8. 为微信二维码添加gif动态背景

    环境准备 来源: https://github.com/sylnsfar/qrcode/blob/master/README-cn.md#%E5%8A%A8%E6%80%81gif%E4%BA%8C% ...

  9. Android仿微信二维码扫描

    转载:http://blog.csdn.net/xiaanming/article/details/10163203 了解二维码这个东西还是从微信中,当时微信推出二维码扫描功能,自己感觉挺新颖的,从一 ...

随机推荐

  1. Linux cp command All In One

    Linux cp command All In One $ man cp $ cp -h # 强制 $ cp -f # 递归,复制文件夹 $ cp -r demos cp -fr # ./folder ...

  2. CSS margin collapsing All In One

    CSS margin collapsing All In One margin collapsing margin 塌陷 / margin 合并 会出现 margin collapsing 的 3 种 ...

  3. Professional JavaScript for Web Developers 4th Edition

    Professional JavaScript for Web Developers 4th Edition learning notes / 学习笔记 https://github.com/xgqf ...

  4. HTTP/HTTPS Proxy & Charles

    HTTP/HTTPS Proxy & Charles Charles https://www.charlesproxy.com/ https://www.jianshu.com/p/53d2c ...

  5. 两百万SPC空投来袭,带动市场热情!

    NGK投放项目的时间节点总是以牛市为主,像是上一次的BGV项目投放就在2020年末的数字加密货币牛市,其结果想必各位生态建设者们都已经见到了,在登陆交易所首日便高收于近889美金,创下惊人的近一千七百 ...

  6. 【Azure 云服务】如何从Azure Cloud Service中获取项目的部署文件

    问题描述 在历史已经部署的云服务(Azure Cloud Service)中,如何获取到项目在很久以前的部署包文件呢? 解决办法 1)如果部署云服务是通过门户上传部署包到存储账号中,则可以直接从存储账 ...

  7. redis环境配置

    1.解压redis压缩包 tar -zxvf redis-5.0.7 2. 基本环境安装 进入解压后的目录 安装yum(cents需要 其它版本Linux可能不适用yum用其它工具)ubuntu:ap ...

  8. 安装mysql报错

    原文链接:https://blog.csdn.net/bao19901210/article/details/51917641 二进制安装 1.添加mysql组和mysql用户,用于设置mysql安装 ...

  9. web前端学习笔记(python)(一)

    瞎JB搞]感觉自己全栈了,又要把数据库里面的内容,以web形式展示出来,并支持数据操作.占了好多坑.....慢慢填(主要参考廖雪峰的官网,不懂的再百度) 一.web概念 Client/Server模式 ...

  10. 从零开始搞后台管理系统(2)——shin-server

      shin 的读音是[ʃɪn],谐音就是行,寓意可行的后端系统服务,shin-server 的特点是: 站在巨人的肩膀上,依托KOA2.bunyan.Sequelize等优秀的框架和库所搭建的定制化 ...