《微信二维码引擎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. es6 curry function

    es6 curry function // vuex getters export const getAdsFilterConfig = (state) => (spreader) => ...

  2. 开放式 Web 应用程序安全性项目 OWASP

    开放式 Web 应用程序安全性项目 OWASP Open Web Application Security Project (OWASP) OWASP 基金会是谁? Open Web Applicat ...

  3. React useEffect in depth

    React useEffect in depth useEffect class DogInfo extends React.Component { controller = null state = ...

  4. W3C & 弹幕

    W3C & 弹幕 弹幕用例规范 Draft Community Group Report 21 August 2020 refs https://w3c.github.io/danmaku/u ...

  5. http methods & restful api methods

    http methods & restful api methods 超文本传输​​协议(HTTP)是用于传输超媒体文档(例如HTML)的应用层协议 https://developer.moz ...

  6. html5 & iOS

    html5 & iOS Apple App Store审核指南 https://developer.apple.com/app-store/review/guidelines/ Apple审核 ...

  7. Markdown & Static Site Generator

    Markdown & Static Site Generator https://www.gitbook.com/ https://vuepress.vuejs.org/ https://ww ...

  8. html tag filter in js

    html tag filter in js const html = `可当天预订,必须21时15分之前下单,要求必须<font color=green><b>60</b ...

  9. Flutter FractionallySizedBox 设置维度比例 而不是固定的px

    本周小部件 有时您的设计需要相对的维度. FractionallySizedBox允许您将子项的大小调整为总可用空间的一小部分. Scaffold( body: Center( child: Frac ...

  10. 【SpringMVC】 4.3 拦截器

    SpringMVC学习记录 注意:以下内容是学习 北京动力节点 的SpringMVC视频后所记录的笔记.源码以及个人的理解等,记录下来仅供学习 第4章 SpringMVC 核心技术 4.3 拦截器   ...