first commit
This commit is contained in:
commit
1ef46ecdbe
BIN
example/img/1.JPG
Normal file
BIN
example/img/1.JPG
Normal file
Binary file not shown.
After Width: | Height: | Size: 296 KiB |
BIN
example/img/2.png
Normal file
BIN
example/img/2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 147 KiB |
BIN
example/img/5.jpeg
Normal file
BIN
example/img/5.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 162 KiB |
160
example/index.html
Normal file
160
example/index.html
Normal file
@ -0,0 +1,160 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
.canvasInput-wrap {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
#canvasInput {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#qrcodeCanvasOutput {
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
object-fit: contain;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<button id="qrcodeTryIt" disabled="true" onclick="qrcode_run()">Try it OpenCV</button><br>
|
||||
<button id="qrcodeTryItJS" disabled="true" onclick="qrcode_run_qrjs()">Try it QRjs</button><br>
|
||||
<div id="status"></div>
|
||||
<hr>
|
||||
<input type="file" id="fileInput" name="file" />
|
||||
<hr>
|
||||
<div id="qrcodeResult"><label for="">OpenCV QRCode:</label><span></span></div>
|
||||
<div id="qrcodeResultQRjs"><label for="">QRjs QRCode:</label><span></span></div>
|
||||
<div id="qrcodeShowcase">
|
||||
<div>
|
||||
<div class="canvasInput-wrap">
|
||||
<canvas id="canvasInput"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div>
|
||||
<label for="">OpenCV QRCode image:</label>
|
||||
<br />
|
||||
<canvas id="qrcodeCanvasOutput"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="./jsqr.js"></script>
|
||||
<script src="./utils.js"></script>
|
||||
|
||||
<script>
|
||||
let qrcode_detector = undefined;
|
||||
const OPENCV_URL = "./opencv.js";
|
||||
const utils = new Utils();
|
||||
|
||||
utils.loadScript(OPENCV_URL).then(
|
||||
() =>
|
||||
{
|
||||
loadModels();
|
||||
},
|
||||
() =>
|
||||
{
|
||||
utils.printError("Failed to load " + OPENCV_URL);
|
||||
}
|
||||
);
|
||||
|
||||
function updateStatus(text)
|
||||
{
|
||||
document.getElementById("status").innerHTML = text;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
function qrcode_run()
|
||||
{
|
||||
console.time("OpenCV耗时");
|
||||
let inputImage = cv.imread("canvasInput", cv.IMREAD_GRAYSCALE);
|
||||
let points_vec = new cv.MatVector();
|
||||
let res = qrcode_detector.detectAndDecode(inputImage, points_vec);
|
||||
|
||||
console.log('opencv识别结果:', res.get(0));
|
||||
if (res.size() !== 0) {
|
||||
document.querySelector("#qrcodeResult span").innerHTML = res.get(0);
|
||||
|
||||
let points = points_vec.get(0);
|
||||
let x = points.floatAt(0);
|
||||
let y = points.floatAt(1);
|
||||
let width = points.floatAt(4) - points.floatAt(0);
|
||||
let height = points.floatAt(5) - points.floatAt(1);
|
||||
let rect = new cv.Rect(x, y, width, height);
|
||||
dst = inputImage.roi(rect);
|
||||
cv.imshow("qrcodeCanvasOutput", dst);
|
||||
}
|
||||
console.timeEnd("OpenCV耗时");
|
||||
}
|
||||
|
||||
function qrcode_run_qrjs()
|
||||
{
|
||||
console.time("QRjs耗时");
|
||||
const canvas = document.getElementById("canvasInput");
|
||||
const ctx = canvas.getContext("2d");
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const qrInfo = jsQR(imageData.data, imageData.width, imageData.height);
|
||||
|
||||
console.log('qrjs识别结果:', qrInfo);
|
||||
if (qrInfo) {
|
||||
document.querySelector("#qrcodeResultQRjs span").innerHTML = qrInfo.data;
|
||||
}
|
||||
console.timeEnd("QRjs耗时");
|
||||
}
|
||||
|
||||
utils.loadImageToCanvas("./img/1.JPG", "canvasInput");
|
||||
|
||||
let fileInputElement = document.getElementById("fileInput");
|
||||
fileInputElement.addEventListener("change", qrcodeHandleFiles, false);
|
||||
function qrcodeHandleFiles(e)
|
||||
{
|
||||
const file = e.target.files[0];
|
||||
const qrcodeUrl = URL.createObjectURL(file);
|
||||
utils.loadImageToCanvas(qrcodeUrl, "canvasInput");
|
||||
}
|
||||
|
||||
function onReady()
|
||||
{
|
||||
document.getElementById("qrcodeTryIt").disabled = false;
|
||||
document.getElementById("qrcodeTryItJS").disabled = false;
|
||||
}
|
||||
if (typeof cv !== "undefined") {
|
||||
onReady();
|
||||
} else {
|
||||
document.getElementById("opencvjs").onload = onReady;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
10100
example/jsqr.js
Normal file
10100
example/jsqr.js
Normal file
File diff suppressed because it is too large
Load Diff
BIN
example/models/detect.caffemodel
Normal file
BIN
example/models/detect.caffemodel
Normal file
Binary file not shown.
2716
example/models/detect.prototxt
Normal file
2716
example/models/detect.prototxt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
example/models/sr.caffemodel
Normal file
BIN
example/models/sr.caffemodel
Normal file
Binary file not shown.
403
example/models/sr.prototxt
Normal file
403
example/models/sr.prototxt
Normal file
@ -0,0 +1,403 @@
|
||||
layer {
|
||||
name: "data"
|
||||
type: "Input"
|
||||
top: "data"
|
||||
input_param {
|
||||
shape {
|
||||
dim: 1
|
||||
dim: 1
|
||||
dim: 224
|
||||
dim: 224
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "conv0"
|
||||
type: "Convolution"
|
||||
bottom: "data"
|
||||
top: "conv0"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 32
|
||||
bias_term: true
|
||||
pad: 1
|
||||
kernel_size: 3
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "conv0/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "conv0"
|
||||
top: "conv0"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/reduce"
|
||||
type: "Convolution"
|
||||
bottom: "conv0"
|
||||
top: "db1/reduce"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 8
|
||||
bias_term: true
|
||||
pad: 0
|
||||
kernel_size: 1
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/reduce/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "db1/reduce"
|
||||
top: "db1/reduce"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/3x3"
|
||||
type: "Convolution"
|
||||
bottom: "db1/reduce"
|
||||
top: "db1/3x3"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 8
|
||||
bias_term: true
|
||||
pad: 1
|
||||
kernel_size: 3
|
||||
group: 8
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/3x3/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "db1/3x3"
|
||||
top: "db1/3x3"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/1x1"
|
||||
type: "Convolution"
|
||||
bottom: "db1/3x3"
|
||||
top: "db1/1x1"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 32
|
||||
bias_term: true
|
||||
pad: 0
|
||||
kernel_size: 1
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/1x1/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "db1/1x1"
|
||||
top: "db1/1x1"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db1/concat"
|
||||
type: "Concat"
|
||||
bottom: "conv0"
|
||||
bottom: "db1/1x1"
|
||||
top: "db1/concat"
|
||||
concat_param {
|
||||
axis: 1
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/reduce"
|
||||
type: "Convolution"
|
||||
bottom: "db1/concat"
|
||||
top: "db2/reduce"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 8
|
||||
bias_term: true
|
||||
pad: 0
|
||||
kernel_size: 1
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/reduce/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "db2/reduce"
|
||||
top: "db2/reduce"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/3x3"
|
||||
type: "Convolution"
|
||||
bottom: "db2/reduce"
|
||||
top: "db2/3x3"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 8
|
||||
bias_term: true
|
||||
pad: 1
|
||||
kernel_size: 3
|
||||
group: 8
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/3x3/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "db2/3x3"
|
||||
top: "db2/3x3"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/1x1"
|
||||
type: "Convolution"
|
||||
bottom: "db2/3x3"
|
||||
top: "db2/1x1"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 32
|
||||
bias_term: true
|
||||
pad: 0
|
||||
kernel_size: 1
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/1x1/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "db2/1x1"
|
||||
top: "db2/1x1"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "db2/concat"
|
||||
type: "Concat"
|
||||
bottom: "db1/concat"
|
||||
bottom: "db2/1x1"
|
||||
top: "db2/concat"
|
||||
concat_param {
|
||||
axis: 1
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "upsample/reduce"
|
||||
type: "Convolution"
|
||||
bottom: "db2/concat"
|
||||
top: "upsample/reduce"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 32
|
||||
bias_term: true
|
||||
pad: 0
|
||||
kernel_size: 1
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "upsample/reduce/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "upsample/reduce"
|
||||
top: "upsample/reduce"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "upsample/deconv"
|
||||
type: "Deconvolution"
|
||||
bottom: "upsample/reduce"
|
||||
top: "upsample/deconv"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 32
|
||||
bias_term: true
|
||||
pad: 1
|
||||
kernel_size: 3
|
||||
group: 32
|
||||
stride: 2
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "upsample/lrelu"
|
||||
type: "ReLU"
|
||||
bottom: "upsample/deconv"
|
||||
top: "upsample/deconv"
|
||||
relu_param {
|
||||
negative_slope: 0.05000000074505806
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "upsample/rec"
|
||||
type: "Convolution"
|
||||
bottom: "upsample/deconv"
|
||||
top: "upsample/rec"
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 1.0
|
||||
}
|
||||
param {
|
||||
lr_mult: 1.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 1
|
||||
bias_term: true
|
||||
pad: 0
|
||||
kernel_size: 1
|
||||
group: 1
|
||||
stride: 1
|
||||
weight_filler {
|
||||
type: "msra"
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "nearest"
|
||||
type: "Deconvolution"
|
||||
bottom: "data"
|
||||
top: "nearest"
|
||||
param {
|
||||
lr_mult: 0.0
|
||||
decay_mult: 0.0
|
||||
}
|
||||
convolution_param {
|
||||
num_output: 1
|
||||
bias_term: false
|
||||
pad: 0
|
||||
kernel_size: 2
|
||||
group: 1
|
||||
stride: 2
|
||||
weight_filler {
|
||||
type: "constant"
|
||||
value: 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
layer {
|
||||
name: "Crop1"
|
||||
type: "Crop"
|
||||
bottom: "nearest"
|
||||
bottom: "upsample/rec"
|
||||
top: "Crop1"
|
||||
}
|
||||
layer {
|
||||
name: "fc"
|
||||
type: "Eltwise"
|
||||
bottom: "Crop1"
|
||||
bottom: "upsample/rec"
|
||||
top: "fc"
|
||||
eltwise_param {
|
||||
operation: SUM
|
||||
}
|
||||
}
|
49
example/opencv.js
Normal file
49
example/opencv.js
Normal file
File diff suppressed because one or more lines are too long
49
example/opencv_old.js
Normal file
49
example/opencv_old.js
Normal file
File diff suppressed because one or more lines are too long
120
example/utils.js
Normal file
120
example/utils.js
Normal file
@ -0,0 +1,120 @@
|
||||
function Utils(errorOutputId) {
|
||||
this.errorOutput = document.getElementById(errorOutputId);
|
||||
|
||||
this.loadScript = function (url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let script = document.createElement("script");
|
||||
script.setAttribute("async", "");
|
||||
script.setAttribute("type", "text/javascript");
|
||||
script.setAttribute("id", "opencvjs");
|
||||
script.addEventListener("load", async () => {
|
||||
if (cv.getBuildInformation) {
|
||||
console.log(cv.getBuildInformation());
|
||||
resolve();
|
||||
} else {
|
||||
// WASM
|
||||
if (cv instanceof Promise) {
|
||||
cv = await cv;
|
||||
console.log(cv.getBuildInformation());
|
||||
resolve();
|
||||
} else {
|
||||
cv["onRuntimeInitialized"] = () => {
|
||||
console.log(cv.getBuildInformation());
|
||||
resolve();
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
script.addEventListener("error", () => {
|
||||
reject();
|
||||
});
|
||||
script.src = url;
|
||||
let node = document.getElementsByTagName("script")[0];
|
||||
node.parentNode.insertBefore(script, node);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 请求二维码训练模型文件
|
||||
*/
|
||||
this.fetchModelsData = async function (name) {
|
||||
// const response = await fetch(`https://static.xxxx.com/common/opencv/models/${name}`, {
|
||||
const response = await fetch(`./models/${name}`, {
|
||||
method: "GET",
|
||||
});
|
||||
const data = await response.arrayBuffer();
|
||||
|
||||
return new Uint8Array(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* 加载图片到canvas
|
||||
* 发票的二维码基本都在左上角
|
||||
* 为提高效率,只截取出图片二维码的左上角区域放入canvas
|
||||
* @param {*} url
|
||||
* @param {*} cavansId
|
||||
*/
|
||||
this.loadImageToCanvas = function (url, cavansId) {
|
||||
let canvas = document.getElementById(cavansId);
|
||||
let ctx = canvas.getContext("2d");
|
||||
let img = new Image();
|
||||
img.crossOrigin = "anonymous";
|
||||
img.onload = function () {
|
||||
const { width, height } = img;
|
||||
const isVertical = width < height;
|
||||
const crossNum = isVertical ? 3 : 4;
|
||||
const verticalNum = isVertical ? 4 : 3;
|
||||
|
||||
canvas.width = width / crossNum;
|
||||
canvas.height = height / verticalNum;
|
||||
ctx.drawImage(img, isVertical ? width * (2 / 3) : 0, 0, width, height, 0, 0, width, height);
|
||||
};
|
||||
img.src = url;
|
||||
};
|
||||
|
||||
/**
|
||||
* canvas转图片
|
||||
*/
|
||||
this.imagedataToImage = function (imagedata) {
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
canvas.width = imagedata.width;
|
||||
canvas.height = imagedata.height;
|
||||
ctx.putImageData(imagedata, 0, 0);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
img.src = canvas.toDataURL();
|
||||
img.onload = () => {
|
||||
resolve(img);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 拆分图片坐标
|
||||
* @param {*} width 图片宽
|
||||
* @param {*} height 图片高
|
||||
*
|
||||
* @returns 坐标数组 [x,y,width,height][]
|
||||
*/
|
||||
this.segmentationImageCoordinates = function (width, height) {
|
||||
const isVertical = width < height;
|
||||
const crossNum = isVertical ? 3 : 5;
|
||||
const verticalNum = isVertical ? 5 : 3;
|
||||
const blockWidth = width / crossNum;
|
||||
const blockHeight = height / verticalNum;
|
||||
const coordinates = [];
|
||||
|
||||
for (let y = 0; y < verticalNum; y++) {
|
||||
for (let x = 0; x < crossNum; x++) {
|
||||
const cx = x * blockWidth;
|
||||
const cy = y * blockHeight;
|
||||
|
||||
coordinates.push([cx, cy, blockWidth, blockHeight]);
|
||||
}
|
||||
}
|
||||
|
||||
return coordinates;
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user