UNPKG

id-scanner-lib

Version:

一款纯前端实现的TypeScript身份证&二维码识别库,无需后端支持,所有处理在浏览器端完成,新增图像批处理与优化

252 lines (221 loc) 6.94 kB
/** * @file 条形码扫描模块 * @description 提供实时条形码扫描和识别功能 * @module BarcodeScanner */ import { Camera } from "../utils/camera" import { ImageProcessor } from "../utils/image-processing" /** * 条形码扫描器配置选项 * * @interface BarcodeScannerOptions * @property {number} [scanInterval] - 扫描间隔时间(毫秒),默认为200ms * @property {Function} [onScan] - 扫描成功回调函数 * @property {Function} [onError] - 错误处理回调函数 */ export interface BarcodeScannerOptions { scanInterval?: number onScan?: (result: string) => void onError?: (error: Error) => void } /** * 条形码扫描器类 * * 提供实时扫描和识别摄像头中的条形码的功能 * 注意:当前实现是简化版,实际项目中建议集成专门的条形码识别库如ZXing或Quagga.js * * @example * ```typescript * // 创建条形码扫描器 * const barcodeScanner = new BarcodeScanner({ * scanInterval: 100, // 每100ms扫描一次 * onScan: (result) => { * console.log('扫描到条形码:', result); * }, * onError: (error) => { * console.error('扫描错误:', error); * } * }); * * // 启动扫描 * const videoElement = document.getElementById('video') as HTMLVideoElement; * await barcodeScanner.start(videoElement); * * // 停止扫描 * barcodeScanner.stop(); * ``` */ export class BarcodeScanner { private camera: Camera private scanning = false private scanTimer: number | null = null /** * 创建条形码扫描器实例 * * @param {BarcodeScannerOptions} [options] - 扫描器配置选项 */ constructor(private options: BarcodeScannerOptions = {}) { this.options = { scanInterval: 200, ...options, } this.camera = new Camera() } /** * 启动条形码扫描 * * 初始化相机并开始连续扫描视频帧中的条形码 * * @param {HTMLVideoElement} videoElement - 用于显示相机画面的video元素 * @returns {Promise<void>} 启动完成的Promise * @throws 如果无法访问相机,将通过onError回调报告错误 */ async start(videoElement: HTMLVideoElement): Promise<void> { try { await this.camera.initialize(videoElement) this.scanning = true this.scan() } catch (error) { if (this.options.onError) { this.options.onError( error instanceof Error ? error : new Error(String(error)) ) } } } /** * 执行一次条形码扫描 * * 内部方法,捕获当前视频帧并尝试识别其中的条形码 * * @private */ private scan(): void { if (!this.scanning) return const imageData = this.camera.captureFrame() if (imageData) { try { // 图像预处理,提高识别率 const enhancedImage = ImageProcessor.adjustBrightnessContrast( ImageProcessor.toGrayscale(imageData), 10, // 亮度 20 // 对比度 ) // 这里实际项目中可以集成第三方条形码扫描库 // 如 ZXing 或 QuaggaJS // 简化实现,这里仅为示例 this.detectBarcode(enhancedImage) } catch (error) { console.error("条形码扫描错误:", error) } } this.scanTimer = window.setTimeout( () => this.scan(), this.options.scanInterval ) } /** * 条形码检测方法 * * 注意:这是一个简化实现,实际需要集成专门的条形码识别库 * * @private * @param {ImageData} imageData - 要检测条形码的图像数据 */ private detectBarcode(imageData: ImageData): void { // 这里应集成条形码识别库 // 如 ZXing 或 QuaggaJS // 简化示例,实际项目中请替换为真实实现 console.log("正在扫描条形码...") // 模拟找到条形码 if (Math.random() > 0.95) { const mockResult = "6901234567890" // 模拟条形码结果 if (this.options.onScan) { this.options.onScan(mockResult) } } } /** * 停止条形码扫描 * * 停止扫描循环并释放相机资源 */ stop(): void { this.scanning = false if (this.scanTimer) { clearTimeout(this.scanTimer) this.scanTimer = null } this.camera.release() } /** * 处理图像数据中的条形码 * * @param {ImageData} imageData - 要处理的图像数据 * @returns {string | null} 识别到的条形码内容,如未识别到则返回null */ processImageData(imageData: ImageData): string | null { try { if ( !imageData || !imageData.data || imageData.width <= 0 || imageData.height <= 0 ) { throw new Error("无效的图像数据") } // 图像预处理,提高识别率 const enhancedImage = ImageProcessor.adjustBrightnessContrast( ImageProcessor.toGrayscale(imageData), 10, // 亮度 20 // 对比度 ) // 注意:这里是简化实现 // 实际项目中,应该集成专门的条形码识别库如ZXing或Quagga.js // 模拟条形码识别 // 在真实项目中,请替换为实际的条形码识别算法 const result = this.simulateBarcodeDetection(enhancedImage) return result } catch (error) { if (this.options.onError) { this.options.onError( error instanceof Error ? error : new Error(String(error)) ) } return null } } /** * 模拟条形码检测 * 仅用于演示,实际使用时应该替换为真实的条形码识别算法 * * @private * @param {ImageData} imageData - 要检测条形码的图像数据 * @returns {string | null} 模拟的条形码识别结果 */ private simulateBarcodeDetection(imageData: ImageData): string | null { // 这里只是模拟,真实环境中应当使用条形码识别库进行识别 // 在中间区域检测到足够多垂直边缘时,认为可能存在条形码 const midX = Math.floor(imageData.width / 2) const midY = Math.floor(imageData.height / 2) const sampleWidth = Math.min(100, Math.floor(imageData.width / 3)) let edgeCount = 0 let lastPixel = 0 // 简单的边缘检测,统计中心水平线上像素变化次数 for (let x = midX - sampleWidth / 2; x < midX + sampleWidth / 2; x++) { const pixelPos = (midY * imageData.width + x) * 4 const pixelValue = imageData.data[pixelPos] if (Math.abs(pixelValue - lastPixel) > 30) { edgeCount++ } lastPixel = pixelValue } // 如果边缘变化次数在合理范围内,认为是条形码 // 实际的条形码具有规律的宽窄条纹 if (edgeCount > 10 && edgeCount < 50) { // 生成一个模拟的条形码结果 return "690" + Math.floor(Math.random() * 10000000000) } return null } }