UNPKG

id-scanner-lib

Version:

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

478 lines (428 loc) 14.2 kB
/** * @file ID扫描识别库UMD格式入口文件 * @description 专门为UMD格式构建的入口,使用静态导入而非动态导入 * @module IDScannerLib * @version 1.1.0 * @license MIT */ import { Camera, CameraOptions } from "./utils/camera" import { IDCardInfo, DetectionResult } from "./utils/types" import type { QRScannerOptions } from "./scanner/qr-scanner" import type { BarcodeScannerOptions } from "./scanner/barcode-scanner" // 静态导入所有依赖 import { QRScanner } from "./scanner/qr-scanner" import { BarcodeScanner } from "./scanner/barcode-scanner" import { IDCardDetector, IDCardDetectorOptions, } from "./id-recognition/id-detector" import { OCRProcessor } from "./id-recognition/ocr-processor" import { DataExtractor } from "./id-recognition/data-extractor" import { ImageProcessor } from "./utils/image-processing" // 导入IDScannerDemo import { IDScannerDemo } from "./demo/demo" /** * IDScanner配置选项接口 */ export interface IDScannerOptions { cameraOptions?: CameraOptions qrScannerOptions?: QRScannerOptions barcodeScannerOptions?: BarcodeScannerOptions onQRCodeScanned?: (result: string) => void onBarcodeScanned?: (result: string) => void onIDCardScanned?: (info: IDCardInfo) => void onError?: (error: Error) => void } /** * IDScanner 主类 * UMD版本使用静态导入实现 */ export class IDScanner { private camera: Camera private qrScanner: QRScanner | null = null private barcodeScanner: BarcodeScanner | null = null private idDetector: IDCardDetector | null = null private ocrProcessor: OCRProcessor | null = null private dataExtractor: DataExtractor | null = null private scanMode: "qr" | "barcode" | "idcard" = "qr" private videoElement: HTMLVideoElement | null = null // 添加静态属性IDScannerDemo,使其能被通过IDScanner.IDScannerDemo访问 static IDScannerDemo = IDScannerDemo /** * 构造函数 * @param options 配置选项 */ constructor(private options: IDScannerOptions = {}) { this.camera = new Camera(options.cameraOptions) } /** * 初始化模块 * 根据需要初始化OCR引擎 */ async initialize(): Promise<void> { try { // 初始化OCR模块 this.ocrProcessor = new OCRProcessor() this.dataExtractor = new DataExtractor() await this.ocrProcessor.initialize() console.log("IDScanner initialized") } catch (error) { this.handleError(error as Error) throw error } } /** * 启动二维码扫描 * @param videoElement HTML视频元素 */ async startQRScanner(videoElement: HTMLVideoElement): Promise<void> { this.stop() this.videoElement = videoElement this.scanMode = "qr" try { if (!this.qrScanner) { this.qrScanner = new QRScanner({ ...this.options.qrScannerOptions, onScan: this.handleQRScan.bind(this), }) } await this.camera.start(videoElement) this.qrScanner.start(videoElement) } catch (error) { this.handleError(error as Error) } } /** * 启动条形码扫描 * @param videoElement HTML视频元素 */ async startBarcodeScanner(videoElement: HTMLVideoElement): Promise<void> { this.stop() this.videoElement = videoElement this.scanMode = "barcode" try { if (!this.barcodeScanner) { this.barcodeScanner = new BarcodeScanner({ ...this.options.barcodeScannerOptions, onScan: this.handleBarcodeScan.bind(this), }) } await this.camera.start(videoElement) this.barcodeScanner.start(videoElement) } catch (error) { this.handleError(error as Error) } } /** * 启动身份证扫描 * @param videoElement HTML视频元素 */ async startIDCardScanner(videoElement: HTMLVideoElement): Promise<void> { this.stop() this.videoElement = videoElement this.scanMode = "idcard" try { if (!this.ocrProcessor) { await this.initialize() } if (!this.idDetector) { this.idDetector = new IDCardDetector({ onDetection: this.handleIDDetection.bind(this), onError: this.handleError.bind(this), } as IDCardDetectorOptions) } await this.camera.start(videoElement) this.idDetector.start(videoElement) } catch (error) { this.handleError(error as Error) } } /** * 停止扫描 */ stop(): void { if (this.scanMode === "qr" && this.qrScanner) { this.qrScanner.stop() } else if (this.scanMode === "barcode" && this.barcodeScanner) { this.barcodeScanner.stop() } else if (this.scanMode === "idcard" && this.idDetector) { this.idDetector.stop() } this.camera.stop() } /** * 处理二维码扫描结果 */ private handleQRScan(result: string): void { if (this.options.onQRCodeScanned) { this.options.onQRCodeScanned(result) } } /** * 处理条形码扫描结果 */ private handleBarcodeScan(result: string): void { if (this.options.onBarcodeScanned) { this.options.onBarcodeScanned(result) } } /** * 处理身份证检测结果 */ private async handleIDDetection(result: DetectionResult): Promise<void> { if (!this.ocrProcessor || !this.dataExtractor) return try { // 检查 imageData 是否存在 if (!result.imageData) { this.handleError(new Error("无效的图像数据")) return } const idCardInfo = await this.ocrProcessor.processIDCard(result.imageData) const extractedInfo = this.dataExtractor.extractAndValidate(idCardInfo) if (this.options.onIDCardScanned) { this.options.onIDCardScanned(extractedInfo) } } catch (error) { this.handleError(error as Error) } } /** * 处理错误 */ private handleError(error: Error): void { if (this.options.onError) { this.options.onError(error) } else { console.error("IDScanner error:", error) } } /** * 释放资源 */ async terminate(): Promise<void> { this.stop() if (this.ocrProcessor) { await this.ocrProcessor.terminate() this.ocrProcessor = null } this.qrScanner = null this.barcodeScanner = null this.idDetector = null this.dataExtractor = null } /** * 处理图片中的二维码 * @param imageSource 图片源,可以是Image元素、Canvas元素或URL字符串 * @returns 返回Promise,解析为扫描结果 */ async processQRCodeImage( imageSource: HTMLImageElement | HTMLCanvasElement | string ): Promise<string> { try { if (!this.qrScanner) { this.qrScanner = new QRScanner({ ...this.options.qrScannerOptions, onScan: this.handleQRScan.bind(this), }) } // 处理不同类型的图片源 let imageElement: HTMLImageElement if (typeof imageSource === "string") { // 如果是URL字符串,创建新的Image元素并加载图片 imageElement = new Image() imageElement.crossOrigin = "anonymous" // 处理跨域图片 await new Promise((resolve, reject) => { imageElement.onload = resolve imageElement.onerror = reject imageElement.src = imageSource }) } else if (imageSource instanceof HTMLImageElement) { // 如果已经是Image元素,直接使用 imageElement = imageSource } else if (imageSource instanceof HTMLCanvasElement) { // 如果是Canvas元素,创建新的Image元素并从Canvas获取数据 const dataURL = imageSource.toDataURL() imageElement = new Image() await new Promise((resolve, reject) => { imageElement.onload = resolve imageElement.onerror = reject imageElement.src = dataURL }) } else { throw new Error("不支持的图片源类型") } // 创建Canvas处理图片 const canvas = document.createElement("canvas") const ctx = canvas.getContext("2d") if (!ctx) { throw new Error("无法创建Canvas上下文") } // 设置Canvas尺寸与图片相同 canvas.width = imageElement.naturalWidth canvas.height = imageElement.naturalHeight ctx.drawImage(imageElement, 0, 0) // 获取图像数据并处理 const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height) return new Promise((resolve, reject) => { try { const result = this.qrScanner?.processImageData(imageData) if (result) { resolve(result) } else { reject(new Error("未检测到二维码")) } } catch (error) { reject(error) } }) } catch (error) { this.handleError(error as Error) throw error } } /** * 处理图片中的条形码 * @param imageSource 图片源,可以是Image元素、Canvas元素或URL字符串 * @returns 返回Promise,解析为扫描结果 */ async processBarcodeImage( imageSource: HTMLImageElement | HTMLCanvasElement | string ): Promise<string> { try { if (!this.barcodeScanner) { this.barcodeScanner = new BarcodeScanner({ ...this.options.barcodeScannerOptions, onScan: this.handleBarcodeScan.bind(this), }) } // 处理不同类型的图片源 let imageElement: HTMLImageElement if (typeof imageSource === "string") { // 如果是URL字符串,创建新的Image元素并加载图片 imageElement = new Image() imageElement.crossOrigin = "anonymous" // 处理跨域图片 await new Promise((resolve, reject) => { imageElement.onload = resolve imageElement.onerror = reject imageElement.src = imageSource }) } else if (imageSource instanceof HTMLImageElement) { // 如果已经是Image元素,直接使用 imageElement = imageSource } else if (imageSource instanceof HTMLCanvasElement) { // 如果是Canvas元素,创建新的Image元素并从Canvas获取数据 const dataURL = imageSource.toDataURL() imageElement = new Image() await new Promise((resolve, reject) => { imageElement.onload = resolve imageElement.onerror = reject imageElement.src = dataURL }) } else { throw new Error("不支持的图片源类型") } // 创建Canvas处理图片 const canvas = document.createElement("canvas") const ctx = canvas.getContext("2d") if (!ctx) { throw new Error("无法创建Canvas上下文") } // 设置Canvas尺寸与图片相同 canvas.width = imageElement.naturalWidth canvas.height = imageElement.naturalHeight ctx.drawImage(imageElement, 0, 0) // 获取图像数据并处理 const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height) return new Promise((resolve, reject) => { try { const result = this.barcodeScanner?.processImageData(imageData) if (result) { resolve(result) } else { reject(new Error("未检测到条形码")) } } catch (error) { reject(error) } }) } catch (error) { this.handleError(error as Error) throw error } } /** * 处理图片中的身份证 * @param imageSource 图片源,可以是Image元素、Canvas元素或URL字符串 * @returns 返回Promise,解析为身份证信息 */ async processIDCardImage( imageSource: HTMLImageElement | HTMLCanvasElement | string ): Promise<IDCardInfo> { try { if (!this.ocrProcessor || !this.dataExtractor) { await this.initialize() } // 处理不同类型的图片源 let imageElement: HTMLImageElement if (typeof imageSource === "string") { // 如果是URL字符串,创建新的Image元素并加载图片 imageElement = new Image() imageElement.crossOrigin = "anonymous" // 处理跨域图片 await new Promise((resolve, reject) => { imageElement.onload = resolve imageElement.onerror = reject imageElement.src = imageSource }) } else if (imageSource instanceof HTMLImageElement) { // 如果已经是Image元素,直接使用 imageElement = imageSource } else if (imageSource instanceof HTMLCanvasElement) { // 如果是Canvas元素,创建新的Image元素并从Canvas获取数据 const dataURL = imageSource.toDataURL() imageElement = new Image() await new Promise((resolve, reject) => { imageElement.onload = resolve imageElement.onerror = reject imageElement.src = dataURL }) } else { throw new Error("不支持的图片源类型") } // 创建Canvas处理图片 const canvas = document.createElement("canvas") const ctx = canvas.getContext("2d") if (!ctx) { throw new Error("无法创建Canvas上下文") } // 设置Canvas尺寸与图片相同 canvas.width = imageElement.naturalWidth canvas.height = imageElement.naturalHeight ctx.drawImage(imageElement, 0, 0) // 获取图像数据并处理 const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height) // 调用OCR处理器处理身份证图像 const ocrResult = await this.ocrProcessor!.processIDCard(imageData) const extractedInfo = this.dataExtractor!.extractAndValidate(ocrResult) if (this.options.onIDCardScanned) { this.options.onIDCardScanned(extractedInfo) } return extractedInfo } catch (error) { this.handleError(error as Error) throw error } } } // 导出核心类型 export { IDCardInfo } from "./utils/types" export { CameraOptions } from "./utils/camera" export { QRScanner, BarcodeScanner, IDCardDetector, OCRProcessor, DataExtractor, ImageProcessor, } // 导出IDScannerDemo类 export { IDScannerDemo }