id-scanner-lib
Version:
Browser-based ID card, QR code, and face recognition scanner with liveness detection
302 lines (265 loc) • 9.3 kB
text/typescript
/**
* @file OCR处理器
* @description 提供身份证OCR识别功能
* @module modules/id-card/ocr-processor
*/
import { EventEmitter } from '../../core/event-emitter';
import { Logger } from '../../core/logger';
import { IDCardType, IDCardInfo } from './types';
import { IDCardTextParser } from './id-card-text-parser';
import {
createWorker,
Worker as TesseractWorker,
LoggerMessage,
WorkerOptions,
} from "tesseract.js" // 导入 Worker 和 LoggerMessage 类型
import { ImageProcessor } from "../../utils/image-processing"
import { LRUCache, calculateImageFingerprint } from "../../utils/performance"
import {
isWorkerSupported,
createWorker as createCustomWorker,
} from "../../utils/worker"
import { processOCRInWorker, OCRProcessInput } from "./ocr-worker"
import { Disposable } from "../../utils/resource-manager"
// 自定义日志函数类型,兼容字符串和LoggerMessage
type LoggerFunction = ((message: string | LoggerMessage) => void) | undefined;
/**
* OCR处理器选项接口
*/
export interface OCRProcessorOptions {
language?: string
useWorker?: boolean
maxImageDimension?: number
timeout?: number
brightness?: number // 新增亮度参数
contrast?: number // 新增对比度参数
onProgress?: (progress: number) => void
enableCache?: boolean // 添加启用缓存选项
cacheSize?: number // 添加缓存大小选项
logger?: LoggerFunction // 修改为兼容字符串的日志函数类型
}
/**
* OCR处理器类
*
* 使用Tesseract.js实现对身份证图像的OCR文字识别和信息提取功能
*
* @example
* ```typescript
* // 创建OCR处理器
* const ocrProcessor = new OCRProcessor();
*
* // 初始化OCR引擎
* await ocrProcessor.initialize();
*
* // 处理身份证图像
* const idInfo = await ocrProcessor.processIDCard(idCardImageData);
* console.log('识别到的身份证信息:', idInfo);
*
* // 使用结束后释放资源
* await ocrProcessor.terminate();
* ```
*/
export class OCRProcessor implements Disposable {
private worker: TesseractWorker | null = null // 使用导入的 TesseractWorker 类型
private ocrWorker: ReturnType<
typeof createCustomWorker<
OCRProcessInput,
{ idCardInfo: IDCardInfo; processingTime: number }
>
> | null = null
private initialized: boolean = false
private resultCache: LRUCache<string, IDCardInfo>
private options: OCRProcessorOptions
/**
* 创建OCR处理器实例
*
* @param options OCR处理器选项
*/
constructor(options: OCRProcessorOptions = {}) {
this.options = {
useWorker: isWorkerSupported(),
enableCache: true,
cacheSize: 50,
maxImageDimension: 1000,
logger: console.log,
...options,
}
// 初始化缓存
this.resultCache = new LRUCache<string, IDCardInfo>(this.options.cacheSize)
}
/**
* 初始化OCR引擎
*
* 加载Tesseract OCR引擎和中文简体语言包,并设置适合身份证识别的参数
*
* @returns {Promise<void>} 初始化完成的Promise
*/
async initialize(): Promise<void> {
if (this.initialized) return
if (this.options.useWorker) {
// 使用自定义Worker线程处理OCR
this.ocrWorker = createCustomWorker<
OCRProcessInput,
{ idCardInfo: IDCardInfo; processingTime: number }
>(processOCRInWorker as any) // 使用类型断言解决类型不兼容问题
this.initialized = true
this.options.logger?.("OCR Worker 初始化完成")
} else {
// 使用主线程处理OCR
this.worker = createWorker({
logger: this.options.logger,
})
await this.worker.load()
await this.worker.loadLanguage("chi_sim")
await this.worker.initialize("chi_sim")
await this.worker.setParameters({
tessedit_char_whitelist:
"0123456789X年月日壹贰叁肆伍陆柒捌玖拾民族汉满回维吾尔藏苗彝壮朝鲜侗瑶白土家哈尼哈萨克傣黎傈僳佤高山拉祜水东乡纳西景颇柯尔克孜达斡尔仫佬羌布朗撒拉毛南仡佬锡伯阿昌普米塔吉克怒乌孜别克俄罗斯鄂温克德昂保安裕固京塔塔尔独龙鄂伦春赫哲门巴珞巴基诺男女住址出生公民身份号码签发机关有效期省市区县乡镇街道号楼单元室ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", // 优化字符白名单,增加常见地址字符,移除部分不常用汉字
})
// 增加一些针对性的参数,提高识别率
await this.worker.setParameters({
tessedit_pageseg_mode: 7, // PSM_SINGLE_LINE,使用数字而不是字符串
preserve_interword_spaces: "1", // 保留单词间的空格
})
this.initialized = true
this.options.logger?.("OCR引擎初始化完成")
}
}
/**
* 处理身份证图像并提取信息
* @param imageData 要处理的身份证图像数据
* @returns 提取的身份证信息
*/
async processIDCard(imageData: ImageData): Promise<IDCardInfo | null> {
if (!this.initialized) {
await this.initialize()
}
// 计算图像指纹,用于缓存查找
if (this.options.enableCache) {
const fingerprint = calculateImageFingerprint(imageData)
// 检查缓存中是否有结果
const cachedResult = this.resultCache.get(fingerprint)
if (cachedResult) {
this.options.logger?.("使用缓存的OCR结果")
return cachedResult
}
}
// 调整图像大小以提高性能和准确性
const downsampledImage = ImageProcessor.resizeImage(
imageData,
this.options.maxImageDimension || 1000,
this.options.maxImageDimension || 1000,
true // 保持宽高比
)
// 提高图像质量以获得更好的OCR结果
const enhancedImage = ImageProcessor.batchProcess(downsampledImage, {
brightness:
this.options.brightness !== undefined ? this.options.brightness : 10, // 调整默认亮度
contrast:
this.options.contrast !== undefined ? this.options.contrast : 20, // 调整默认对比度
sharpen: true, // 默认启用锐化,通常对OCR有益
})
// 转换为base64供Tesseract处理
// 创建一个canvas元素
const canvas = document.createElement("canvas")
canvas.width = enhancedImage.width
canvas.height = enhancedImage.height
const ctx = canvas.getContext("2d")
if (!ctx) {
throw new Error("无法创建canvas上下文")
}
// 将ImageData绘制到canvas
ctx.putImageData(enhancedImage, 0, 0)
// 转换为Base64
const base64Image = canvas.toDataURL("image/jpeg", 0.7)
// OCR识别
try {
let idCardInfo: IDCardInfo
if (this.options.useWorker && this.ocrWorker) {
// 使用Worker线程处理
const result = await this.ocrWorker.postMessage({
imageBase64: base64Image,
// 不传递函数对象,避免DataCloneError
tessWorkerOptions: {},
})
idCardInfo = result.idCardInfo
this.options.logger?.(
`OCR处理完成,用时: ${result.processingTime.toFixed(2)}ms`
)
} else {
// 使用主线程处理
const startTime = performance.now()
// 转换ImageData为Canvas
const canvas = ImageProcessor.imageDataToCanvas(enhancedImage)
// 确保worker已初始化
if (!this.worker) {
throw new Error("OCR引擎未初始化");
}
const { data } = (await this.worker.recognize(canvas)) as {
data: { text: string }
}
// 解析身份证信息
idCardInfo = IDCardTextParser.parse(data.text)
const processingTime = performance.now() - startTime
this.options.logger?.(
`OCR处理完成,用时: ${processingTime.toFixed(2)}ms`
)
}
// 缓存结果
if (this.options.enableCache) {
const fingerprint = calculateImageFingerprint(imageData)
this.resultCache.set(fingerprint, idCardInfo)
}
return idCardInfo
} catch (error) {
// 改进错误处理
const errorMessage = error instanceof Error
? error.message
: typeof error === 'object'
? JSON.stringify(error)
: String(error);
this.options.logger?.(`OCR识别错误: ${errorMessage}`);
// 返回 null,让调用方知道识别失败
return null;
}
}
/**
* 解析身份证文本信息
*
* 从OCR识别到的文本中提取结构化的身份证信息
*
* @private
* @param {string} text - OCR识别到的文本
* @returns {IDCardInfo} 提取到的身份证信息对象
*/
/**
* 清除结果缓存
*/
clearCache(): void {
this.resultCache.clear()
this.options.logger?.("OCR结果缓存已清除")
}
/**
* 终止OCR引擎并释放资源
*
* @returns {Promise<void>} 终止完成的Promise
*/
async terminate(): Promise<void> {
if (this.worker) {
await this.worker.terminate()
this.worker = null
}
if (this.ocrWorker) {
this.ocrWorker.terminate()
this.ocrWorker = null
}
this.initialized = false
this.options.logger?.("OCR引擎已终止")
}
/**
* 释放资源
*/
dispose(): Promise<void> {
return this.terminate()
}
}