Skip to content

Commit 61fab52

Browse files
committed
feat: 创建可直接使用的库
1 parent bce4fb5 commit 61fab52

28 files changed

+11496
-10538
lines changed

.eslintrc.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module.exports = {
2+
env: {
3+
browser: true,
4+
es2021: true,
5+
node: true,
6+
},
7+
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
8+
parser: "@typescript-eslint/parser",
9+
parserOptions: {
10+
ecmaVersion: "latest",
11+
sourceType: "module",
12+
},
13+
plugins: ["@typescript-eslint"],
14+
rules: {
15+
"@typescript-eslint/no-var-requires": 0,
16+
"@typescript-eslint/no-non-null-assertion": 0,
17+
},
18+
};

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.DS_Store
2+
3+
# Logs
4+
logs
5+
*.log
6+
7+
# Dependency directory
8+
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
9+
node_modules
10+
11+
# Don't commit package-lock.json - We don't have any direct dependencies, and the dev dependencies for now at least
12+
# dont't need to be locked beyond the package.json
13+
package-lock.json

.npmignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*
2+
3+
!dist/**
4+
!package.json
5+
!LICENSE
6+
!README.md

README.md

Lines changed: 53 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,81 @@
1-
# opencv-js-qrcode
2-
# 纯JS识别照片或图片中的二维码
3-
**场景**: 最近在做一个功能,通过识别发票照片获取发票数据用以匹配开票数据存档。
4-
在搜索了各种库踩了一堆坑后,发现微信开源过他们的二维码扫码引擎c++版本,一阵折腾后编译出js版本,整理一下分享出来。本文介绍的方法主要的特点是:
1+
# OpencvQr
2+
## 纯JS识别照片或图片中的二维码
3+
本库的主要特点是:
54
* 纯前端处理,无须调用后端OCR API等接口;
65
* 可处理标准正方形的二维码,最重要的是可以处理照片上的二维码;二维码变形、旋转都可以识别;
76
* 基于opencv 编译后的webassembly版本,编译添加了微信开源的wechat_qrcode,有极高的识别率。
87

98

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

1211
**demo源码: [leidenglai/opencv-js-qrcode · GitHub](https://github.com/leidenglai/opencv-js-qrcode)**
1312

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

18-
在编译好OpenCV后,导出opencv.js,文件比较大进过优化大约有5
19-
M,所以使用时需要异步加载。opencv加载后就能通过window.cv调用到opencv的相关方法;此时并不意味着我们能正常运行使用了,因为我们还需要加载二维码扫码引擎的模型文件:
20-
``` javascript
21-
async function loadModels() {
22-
const detect_proto = "detect.prototxt";
23-
const detect_weight = "detect.caffemodel";
24-
const sr_proto = "sr.prototxt";
25-
const sr_weight = "sr.caffemodel";
26-
27-
if (qrcode_detector != undefined) {
28-
updateStatus("Model Existed");
29-
} else {
30-
const dp = await utils.fetchModelsData(detect_proto);
31-
const dw = await utils.fetchModelsData(detect_weight);
32-
const sp = await utils.fetchModelsData(sr_proto);
33-
const sw = await utils.fetchModelsData(sr_weight);
34-
35-
cv.FS_createDataFile("/", "detect.prototxt", dp, true, false, false);
36-
cv.FS_createDataFile("/", "detect.caffemodel", dw, true, false, false);
37-
cv.FS_createDataFile("/", "sr.prototxt", sp, true, false, false);
38-
cv.FS_createDataFile("/", "sr.caffemodel", sw, true, false, false);
39-
40-
qrcode_detector = new cv.wechat_qrcode_WeChatQRCode(
41-
"detect.prototxt",
42-
"detect.caffemodel",
43-
"sr.prototxt",
44-
"sr.caffemodel"
45-
);
46-
updateStatus("OpenCV Model Created");
47-
}
48-
}
16+
### NPM
17+
18+
``` sh
19+
npm install opencv-qr --save
4920
```
5021

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

54-
作为对比,demo中添加了git上star比较多的jsqr库一同对照测试。
23+
``` javascript
24+
// ES6 import
25+
import OpencvQr from "opencv-qr";
5526

56-
## 识别二维码
57-
因为demo特定需求,发票二维码基本在左上角,所以在canvas加载图片时只截取左上角图片,提高引擎识别效率。
58-
<img width="562" alt="CleanShot 2022-05-23 at 17 24 28@2x" src="https://user-images.githubusercontent.com/11383747/169796294-993433c3-3e97-46a9-8651-8fd76152ca27.png">
27+
// CommonJS require
28+
const OpencvQr = require("opencv-qr");
5929

60-
OpenCV识别结果:
61-
![CleanShot 2022-05-23 at 17 26 12@2x](https://user-images.githubusercontent.com/11383747/169796337-7ba2a129-b357-41b8-a8a9-20484a9b30c8.png)
62-
将二维码信息和二维码区域都正确的处理,OpenCV耗时: 224.42822265625 ms。
30+
// 加载模型文件,模型文件请自行保存在静态目录
31+
const cvQr = new OpencvQr({
32+
dw: "http://xxx.com/models/detect.caffemodel",
33+
sw: "http://xxx.com/models/sr.caffemodel",
34+
});
6335

64-
而jsqr是处理不了这样的图片的。
36+
```
6537

38+
### Browser
6639

67-
**识别旋转的二维码**
68-
![CleanShot 2022-05-23 at 17 28 48@2x](https://user-images.githubusercontent.com/11383747/169796380-f85584a5-b8a9-45ca-94ff-80dabc786225.png)
69-
照片被旋转了90度且不太清晰,同样也被正确的处理,OpenCV耗时: 169.523193359375 ms。
40+
也可直接在浏览器中使用
7041

71-
jsqr也不能处理这样的图片。
42+
``` html
43+
<script src="dist/OpencvQr.js"></script>
44+
<script>
45+
const cvQr = new OpencvQr({
46+
dw: "http://xxx.com/models/detect.caffemodel",
47+
sw: "http://xxx.com/models/sr.caffemodel",
48+
});
49+
</script>
7250

51+
```
7352

7453

75-
**电子二维码识别:**
76-
![CleanShot 2022-05-23 at 17 32 11@2x](https://user-images.githubusercontent.com/11383747/169796414-ae279015-dc5e-42f1-af7b-6b085a2b22f8.png)
77-
电子版的发票图片都正确的识别了;OpenCV耗时: 165.333984375 ms,QRjs耗时: 44.613037109375 ms。不过QRjs的效率相对会高一些。
54+
### 使用
7855

56+
OpencvQr暴露一个加载方法和三个使用方法, 支持typescript类型 OpencvQr.d.ts
7957

80-
## 浏览器兼容性:
81-
暨WebAssembly兼容性,基本上现代浏览器都是支持的:
82-
<img width="789" alt="CleanShot 2022-05-23 at 17 16 35@2x" src="https://user-images.githubusercontent.com/11383747/169796444-07af908a-48a6-448f-b458-56175ad2576d.png">
83-
84-
## 总结
85-
OpenCV因为需要加入wechat_qrcode组件,所以必须使用自定义编译。需要在本地搭建c++的编译环境,是前端基本不曾涉足的领域,相对较为繁琐,也会有一些困难,但这也让前端具备了更强大的本领,由于WebAssembly的存在,前端也有了更多的可能。
86-
87-
总之,由于前端越强大,我们能做的事也越多,挑战也会越大,大家共勉。
58+
``` javascript
59+
// 初始化时需自行加载模型文件,模型文件请自行保存,需静态加载,不可编译转换
60+
const cvQr = new OpencvQr({
61+
dw: "http://xxx.com/models/detect.caffemodel",
62+
sw: "http://xxx.com/models/sr.caffemodel",
63+
});
64+
65+
// 加载canvas中的图像
66+
const result = cvQr.load("canvasInput");
67+
68+
// 返回解析结果字符
69+
const infos = result?.getInfos();
70+
// 返回解析的二维码截取图像 ImageData
71+
const images = result?.getImages();
72+
// 返回已识别的二维码图像相对于原图的位置信息 坐标和宽高
73+
const sizes = result?.getSizes();
8874

89-
## 参考
90-
[编译微信二维码引擎到webAssembly实践 | 虚幻](https://qwertyyb.github.io/2021/06/19/%E7%BC%96%E8%AF%91%E5%BE%AE%E4%BF%A1%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%BC%95%E6%93%8E%E5%88%B0webAssembly%E5%AE%9E%E8%B7%B5/)
91-
OpenCV: https://github.com/opencv/opencv
92-
OpenCV’s contrib: https://github.com/opencv/opencv_contrib
75+
```
9376
9477
78+
## 浏览器兼容性:
79+
暨WebAssembly兼容性,基本上现代浏览器都是支持的:
80+
<img width="789" alt="CleanShot 2022-05-23 at 17 16 35@2x" src="https://user-images.githubusercontent.com/11383747/169796444-07af908a-48a6-448f-b458-56175ad2576d.png">
9581

config/webpack.common.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// webpack.common.ts
2+
const path = require("path");
3+
4+
const configCommon = {
5+
module: {
6+
rules: [
7+
{
8+
test: /\.js$/,
9+
use: ["babel-loader"],
10+
exclude: /(node_modules|libs)/,
11+
},
12+
{
13+
test: /\.ts(x?)$/,
14+
use: [
15+
{
16+
loader: "babel-loader",
17+
},
18+
"ts-loader",
19+
],
20+
exclude: /node_modules/,
21+
},
22+
{
23+
test: /\.prototxt$/,
24+
type: "asset/source",
25+
},
26+
],
27+
},
28+
resolve: {
29+
alias: {},
30+
extensions: [".ts", ".js"],
31+
fallback: {
32+
fs: false,
33+
tls: false,
34+
net: false,
35+
path: false,
36+
zlib: false,
37+
http: false,
38+
https: false,
39+
stream: false,
40+
crypto: false,
41+
},
42+
},
43+
devServer: {
44+
static: {
45+
directory: path.join(__dirname, "../example"), // 修改默认静态服务访问public目录
46+
},
47+
},
48+
plugins: [],
49+
};
50+
module.exports = configCommon;

config/webpack.dev.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const path = require("path");
2+
const { merge } = require("webpack-merge");
3+
const HtmlWebpackPlguin = require("html-webpack-plugin");
4+
const webpackCommon = require("./webpack.common");
5+
6+
const devConfig = merge(webpackCommon, {
7+
entry: {
8+
app: path.join(__dirname, "../example/index.ts"),
9+
},
10+
devtool: "inline-source-map",
11+
devServer: {
12+
hot: true,
13+
},
14+
plugins: [
15+
new HtmlWebpackPlguin({
16+
inject: true,
17+
filename: "index.html",
18+
template: path.resolve(__dirname, "../example/index.html"),
19+
title: "example",
20+
}),
21+
],
22+
});
23+
module.exports = devConfig;

config/webpack.prod.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// webpack.prod.ts
2+
const path = require("path");
3+
const { merge } = require("webpack-merge");
4+
const commonConfig = require("./webpack.common");
5+
6+
const prodConfig = merge(commonConfig, {
7+
entry: {
8+
app: path.join(__dirname, "../src/OpencvQr.ts"),
9+
},
10+
output: {
11+
library: {
12+
name: "OpencvQr",
13+
type: "umd",
14+
export: "default",
15+
},
16+
filename: 'OpencvQr.js',
17+
globalObject: "this",
18+
},
19+
mode: "production",
20+
});
21+
module.exports = prodConfig;

dist/OpencvQr.d.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/** Canvas元素Id */
2+
type CanvasElementId = string;
3+
declare class OpencvQr {
4+
/** 图像解析 */
5+
private qrImage;
6+
private qrVec;
7+
/** 识别结果 */
8+
private qrRes;
9+
/** 识别结果数量 */
10+
qrSize: number;
11+
private qrcode_detector?;
12+
cv?: any;
13+
ready: Promise<void>;
14+
constructor(models: {
15+
/** detect.caffemodel模型文件地址 */
16+
dw: string;
17+
/** sr.caffemodel模型文件地址 */
18+
sw: string;
19+
});
20+
init(cv: any, models: any): Promise<void>;
21+
private string2ArrayBuffer;
22+
private res2ArrayBuffer;
23+
private getImageData;
24+
private checkInit;
25+
/**
26+
* 加载图片
27+
* @param imageData canvasDom Id或者Canvas元素或者Image元素或者ImageData
28+
* @returns
29+
*/
30+
load(imageData: CanvasElementId | HTMLCanvasElement | HTMLImageElement | ImageData): this | undefined;
31+
/**
32+
* 返回已识别的二维码信息
33+
*/
34+
getInfos(): string[];
35+
/**
36+
* 返回已识别的二维码图像信息
37+
*/
38+
getImages(): ImageData[];
39+
/**
40+
* 返回已识别的二维码图像相对于原图的位置信息 坐标和宽高
41+
*/
42+
getSizes(): {
43+
x: number;
44+
y: number;
45+
w: number;
46+
h: number;
47+
}[];
48+
}
49+
export default OpencvQr;

dist/OpencvQr.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)