在opencv 4.5.3 的 contrib中, 腾讯WeChatCV团队贡献了wechat_qrcode模块,3行代码即可在opencv中实现微信的扫码功能。
文档链接为https://docs.opencv.org/4.x/d5/d04/classcv_1_1wechat__qrcode_1_1WeChatQRCode.html。
微信扫码引擎是一款基于开源引擎ZXing,并高度优化和深度改造的高性能轻量二维码识别器。
1、接口、模型说明
Wechat QRCode 使用2个CNN模型,一个用于检测二维码的的包围框bounding box,另一个用于在二维码较小或有变形时的超分辨率重建。
1.1、接口
构造函数简单,传入2个CNN模型的网络结构、模型权重文件的路径。这两个模型都是基于caffe框架的。
// 构造函数
cv::wechat_qrcode::WeChatQRCode::WeChatQRCode(
const std::string & detector_prototxt_path = "", // 检测模型的prototxt
const std::string & detector_caffe_model_path = "", // 检测模型的model
const std::string & super_resolution_prototxt_path = "", // 超分辨率模型的prototxt
const std::string & super_resolution_caffe_model_path = "" // 超分辨率模型的model
)
成员函数只有一个,进行二维码的检测与解码。返回结果为每个二维码的解码的字符串列表,顺序对应可选输出的二维码包围框。
// 检测解码函数
std::vector<std::string> cv::wechat_qrcode::WeChatQRCode::detectAndDecode(
InputArray img, // 灰度图或彩色图
OutputArrayOfArrays points = noArray() // 可选的,保存检测到的所有二维码包围框bounding box
)
1.2、模型
两个模型的下载地址为 https://github.com/WeChatCV/opencv_3rdparty
(1)二维码检测
“一图多码”是扫码支付经常遇到的线下场景。早在2016年,微信扫码引擎在业内率先支持远距离二维码检测、自动调焦定位、多码检测识别。然而,传统方法需要牺牲40%以上的性能来支持多码的检测与识别。伴随着深度学习技术的成熟和移动端计算能力的提升,微信扫码引擎引入基于CNN的二维码检测器解决上述问题。
以SSD框架为基础,构造了短小精干的二维码检测器,采用残差连接(Residual Concat)、深度卷积(Depthwise Convolution)、空洞卷积(DilatedConvolution)、卷积投影(Convolution Projection)等技术进行了针对性的优化。整个模型大小仅943KB,iPhone7(A10)单CPU的推理时间仅需20ms,很好地满足“低延时、小体积、高召回”的业务需求。
(2)基于CNN的二维码增强
“大图小码”是远距扫码和长按识码经常面临的难点,二维码增强技术可以让小码更加清晰。2014年,微信率先在对话中上线“识别图中二维码”能力,离不开增强技术的加持。在长按识别的场景中,二维码图像经过用户的裁剪、压缩、转发,图像质量严重受损,分辨率急剧下降,边缘变得模糊不清,这给扫码引擎的识别带来了极大的挑战。
.
传统图像增强算法很难完美地解决以上问题,因此微信扫码引擎率先在识别流程中引入了基于深度学习的超分辨率技术。在网络结构上,密集连接(Dense Concat)、深度卷积(Depthwise Convolution)、反向卷积(Deconvolution)、残差学习(Residual Learning)等技术改善模型的性能;在目标函数上,针对二维码强边缘和二值化的特点,结合L2/L1损失、边界加权、二值约束设计了针对二维码的目标函数。经过精心的调优,超分辨率模型大小仅23KB,在iPhone7(A10)单CPU的推理时间仅需6ms(100x100超分200x200),完全满足移动端的应用需求。
部分内容摘自 opencv学堂。
2、测试示例
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace std;
using namespace cv;
#include <opencv2/wechat_qrcode.hpp>
int main()
{
const std::string modelDir = R"(D:\opencv\opencv4.5.4\build_contrib\downloads\wechat_qrcode\)";
// 构造(使用异常捕获构造函数是否正常)
wechat_qrcode::WeChatQRCode detector{
modelDir + "detect.prototxt",
modelDir + "detect.caffemodel",
modelDir + "sr.prototxt",
modelDir + "sr.caffemodel"
};
// 临时变量
Mat img;
vector<Mat> points; // qrcode: Retangle, not RotatedBox
auto camIdx = 0;
// auto camIdx = R"(C:\Users\wanggao\Desktop\qrconde_test.jpg)";
VideoCapture cap(camIdx);
while(cap.read(img)){
// 检测
auto res = detector->detectAndDecode(img, points);
// 结果叠加
for(size_t idx = 0; idx < res.size(); idx ++){
Point pt1 = points[idx].at<Point2f>(0);
Point pt2 = points[idx].at<Point2f>(2);
Rect rect{pt1, pt2};
Point center = (pt1 + pt2) / 2;
// 绘制矩形框
rectangle(img, rect, {0,0,255}, 2);
circle(img, center, rect.height / 15, {0,0,255}, -1);
// 解码字符串
putText(img, res[idx], {pt1.x, pt2.y + 16}, 1, 1, {0,0,255});
}
imshow("image", img);
if (waitKey(30) >= 0)
break;
}
return 0;
}
使用360浏览器打开任意网页,查看网站地址的二维码截图进行测试。下面是2个二维码的图片
对上图检测及解码的结果如下图所示: