首页 前端知识 vue,js,html 根据 opencv-js-qrcode 识别发票二维码信息

vue,js,html 根据 opencv-js-qrcode 识别发票二维码信息

2024-05-31 19:05:01 前端知识 前端哥 181 914 我要收藏

      由于项目实际应用中,使用的 qrcode-decoder发现识别成功率过低,而且扫描件经常识别不到二维码,或者带有icon的二维码,识别率也惨不忍睹,后面引入了opencv-js-qrcode进行再次优化提升,进一步提升  vue,js,html,识别带二维码的发票前端支持pdf转图片 此文章的二维码识别成功率。

首先想到的是,截取发票左上角二维码位置,在进行放大,提高扫描率成功率,然后我就直接截取二维码区域上传进行扫描,发现成功率还是太低,或者干脆识别不到,于是打算用java后台处理,发现用 hutool包下面的二维码工具 成功率跟前端是一样的,前端识别到的后端也能识别到,识别不到的都识别不到...

但是我用手机是直接能扫出来的,或者微信的扫描能直接扫出来, 发现可能是扫描的方式有问题,在网上冲浪了一番,发现 知乎上JS识别照片或图片中的二维码 这个文章最靠谱:

        

github:https://github.com/leidenglai/opencv-js-qrcode

opencv-js-qrcode demo链接:https://leidenglai.github.io/opencv-js-qrcode/

经测试后发现功能确实强大然后我下载资源封装到了系统中:

opencvUtils.js

/**
 * 扫描对象
 * @constructor
 */
function OpencvUtils() {
	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) {
					resolve();
				} else {
					// WASM
					if (cv instanceof Promise) {
						cv = await cv;
						resolve();
					} else {
						cv['onRuntimeInitialized'] = () => {
							resolve();
						};
					}
				}
			});
			script.addEventListener('error', () => {
				reject();
			});
			script.src = url;
			let node = document.getElementsByTagName('script')[0];
			node.parentNode.insertBefore(script, node);
		});
	};

	/**
	 * 请求二维码训练模型文件
	 * @param modelUrl
	 * @returns {Promise<Uint8Array>}
	 */
	this.fetchModelsData = async function (modelUrl) {
		// 这里是放到public下面的文件
		const response = await fetch(modelUrl, {
			method: 'GET'
		});
		const data = await response.arrayBuffer();
		return new Uint8Array(data);
	};

	/**
	 * 加载图片到canvas
	 * 发票的二维码基本都在左上角
	 * 为提高效率,只截取出图片二维码的左上角区域放入canvas
	 * @param {*} url
	 * @param {*} callBack
	 */
	this.loadImageToCanvas = function (url, callBack) {
		let canvas = document.createElement('canvas');
		canvas.id = 'openCVCanvasInput';
		let ctx = canvas.getContext('2d');
		let img = new Image();
		img.crossOrigin = 'anonymous';
		img.onload = () => {
			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);
			// 加载完回调
			if (callBack) {
				callBack();
			}
		};
		document.body.appendChild(canvas);
		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;
	};
}
// 临时扫描对象
let qrcode_detector = undefined;
// 临时工具类对象
let tempUtils = null;
/**
 * 加载模型进行初始化
 * @returns {Promise<void>}
 */
async function loadModels() {
	if (qrcode_detector !== undefined) {
		console.log('Model Existed');
	} else {
		// 读取public下面的扫描模型
		const dp = await tempUtils.fetchModelsData('/static/models/detect.prototxt');
		const dw = await tempUtils.fetchModelsData('/static/models/detect.caffemodel');
		const sp = await tempUtils.fetchModelsData('/static/models/sr.prototxt');
		const sw = await tempUtils.fetchModelsData('/static/models/sr.caffemodel');
		// 给cv 创建临时文件夹存放模型数据
		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');
		console.log('OpenCV Model Created');
	}
}

// 初始化
function initOpencv() {
	// 创建临时对象
	tempUtils = new OpencvUtils();
	// 加载opencv.js到页面中
	tempUtils
		.loadScript('/static/opencv.js')
		.then(() => {
			// 加载模型
			loadModels();
		})
		.catch(e => {
			console.log('Failed to load ' + 'opencv.js', e);
		});
}
// 初始化
initOpencv();
/**
 * opencv 扫描二维码
 * @param qrcodeUrl
 * @param callBack
 */
export function qrcode_run(qrcodeUrl, callBack) {
	console.time('OpenCV耗时');
	// 创建画布
	tempUtils.loadImageToCanvas(qrcodeUrl, () => {
		let inputImage = cv.imread('openCVCanvasInput', cv.IMREAD_GRAYSCALE);
		let points_vec = new cv.MatVector();
		let res = qrcode_detector.detectAndDecode(inputImage, points_vec);
		if (res.size() > 0) {
			console.log('opencv识别结果:', res.get(0));
			if (callBack) {
				callBack(res?.get(0) || null);
			}
			/* 这一步是把截图出来的二维码放进画布显示,我不需要就没有打开
			// 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);*/
		}
		// 删除画布对象
		document.getElementById('openCVCanvasInput').remove(); // 下载完成移除元素
		console.timeEnd('OpenCV耗时');
	});
}

资源文件放在public目录下,方便请求访问二进制数据:

这个资源需要你去git上自己下载资源:https://github.com/leidenglai/opencv-js-qrcode

使用方法:

import { qrcode_run } from '@/utils/opencv/opencvUtils';

引入后会直接触发初始化方法,因为要加载资源差不多5M大小,所以建议直接引入加载就行了,防止用的时候等待;

// 初始化opencv数据
initOpencv();

生命周期流程:
1.将opencv.js加载到页面
2.加载完成后调用模型数据加载
3.模型文件获取成功后将资源文件转换为 Uint8Array 加载到引擎中
4.初始化完成
5.调用扫描
6.创建画布 加载图片到canvas
7.图片加载完成后进行识别
8.识别后删除画布对象

         先调用qrcode-decoder进行扫描(详情见上篇文章),如果扫描不到数据在调用opencv进行识别:

注意:使用opencv截图的时候还是有部分问题的,可以先去 opencv-js-qrcode的demo 上测试,在考虑要不要在项目中使用。

像是这种,两种方式是都识别不到。

转载请注明出处或者链接地址:https://www.qianduange.cn//article/10139.html
标签
评论
发布的文章

JQuery中的load()、$

2024-05-10 08:05:15

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!