Skip to content

Latest commit

 

History

History

example

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

opencv-js-qrcode

纯JS识别照片或图片中的二维码

场景: 最近在做一个功能,通过识别发票照片获取发票数据用以匹配开票数据存档。 在搜索了各种库踩了一堆坑后,发现微信开源过他们的二维码扫码引擎c++版本,一阵折腾后编译出js版本,整理一下分享出来。本文介绍的方法主要的特点是:

  • 纯前端处理,无须调用后端OCR API等接口;
  • 可处理标准正方形的二维码,最重要的是可以处理照片上的二维码;二维码变形、旋转都可以识别;
  • 基于opencv 编译后的webassembly版本,编译添加了微信开源的wechat_qrcode,有极高的识别率。

在线测试(使用发票照片测试):https://leidenglai.github.io/opencv-js-qrcode/

demo源码: leidenglai/opencv-js-qrcode · GitHub

加载二维码识别引擎

本文中采用的是自定义编译的opencv库,添加了三方组件wechat_qrcode微信二维码引擎,并且去掉不需要的库。 开始找了很多”强大的二维码识别库“,结果发现基本都不识别照片,或者失败率低,只能识别标准的qrcode。后来发现c++的opencv并且可编译为wsam版本。由于没有任何c++基础,在将wechat_qrcode编译进opencv时异常痛苦,好在后来找到一篇blog:编译微信二维码引擎到webAssembly实践 | 虚幻 ,感谢qwertyyb。

在编译好OpenCV后,导出opencv.js,文件比较大进过优化大约有5 M,所以使用时需要异步加载。opencv加载后就能通过window.cv调用到opencv的相关方法;此时并不意味着我们能正常运行使用了,因为我们还需要加载二维码扫码引擎的模型文件:

async function loadModels() {
  const detect_proto = "detect.prototxt";
  const detect_weight = "detect.caffemodel";
  const sr_proto = "sr.prototxt";
  const sr_weight = "sr.caffemodel";

  if (qrcode_detector != undefined) {
    updateStatus("Model Existed");
  } else {
    const dp = await utils.fetchModelsData(detect_proto);
    const dw = await utils.fetchModelsData(detect_weight);
    const sp = await utils.fetchModelsData(sr_proto);
    const sw = await utils.fetchModelsData(sr_weight);

    cv.FS_createDataFile("/", "detect.prototxt", dp, true, false, false);
    cv.FS_createDataFile("/", "detect.caffemodel", dw, true, false, false);
    cv.FS_createDataFile("/", "sr.prototxt", sp, true, false, false);
    cv.FS_createDataFile("/", "sr.caffemodel", sw, true, false, false);

    qrcode_detector = new cv.wechat_qrcode_WeChatQRCode(
      "detect.prototxt",
      "detect.caffemodel",
      "sr.prototxt",
      "sr.caffemodel"
    );
    updateStatus("OpenCV Model Created");
  }
}

这些文件为wechat_qrcode引擎的 CNN models,Detector model 和 Super scale model。将模型文件加载成功后转换为 Uint8Array 加载到引擎中,然后调用new cv.wechat_qrcode_WeChatQRCode( “detect.prototxt”, “detect.caffemodel”, “sr.prototxt”, “sr.caffemodel” )实例化返回引擎实例。 官方model文件 GitHub - WeChatCV/opencv_3rdparty: OpenCV - 3rdparty

作为对比,demo中添加了git上star比较多的jsqr库一同对照测试。

识别二维码

因为demo特定需求,发票二维码基本在左上角,所以在canvas加载图片时只截取左上角图片,提高引擎识别效率。 CleanShot 2022-05-23 at 17 24 28@2x

OpenCV识别结果: CleanShot 2022-05-23 at 17 26 12@2x 将二维码信息和二维码区域都正确的处理,OpenCV耗时: 224.42822265625 ms。

而jsqr是处理不了这样的图片的。

识别旋转的二维码 CleanShot 2022-05-23 at 17 28 48@2x 照片被旋转了90度且不太清晰,同样也被正确的处理,OpenCV耗时: 169.523193359375 ms。

jsqr也不能处理这样的图片。

电子二维码识别: CleanShot 2022-05-23 at 17 32 11@2x 电子版的发票图片都正确的识别了;OpenCV耗时: 165.333984375 ms,QRjs耗时: 44.613037109375 ms。不过QRjs的效率相对会高一些。

浏览器兼容性:

暨WebAssembly兼容性,基本上现代浏览器都是支持的: CleanShot 2022-05-23 at 17 16 35@2x

总结

OpenCV因为需要加入wechat_qrcode组件,所以必须使用自定义编译。需要在本地搭建c++的编译环境,是前端基本不曾涉足的领域,相对较为繁琐,也会有一些困难,但这也让前端具备了更强大的本领,由于WebAssembly的存在,前端也有了更多的可能。

总之,由于前端越强大,我们能做的事也越多,挑战也会越大,大家共勉。

参考

编译微信二维码引擎到webAssembly实践 | 虚幻 OpenCV: https://github.com/opencv/opencv OpenCV’s contrib: https://github.com/opencv/opencv_contrib