id-scanner-lib 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +173 -0
  2. package/dist/core.d.ts +77 -0
  3. package/dist/id-recognition/data-extractor.d.ts +31 -0
  4. package/dist/id-recognition/id-detector.d.ts +25 -1
  5. package/dist/id-scanner-core.esm.js +10870 -0
  6. package/dist/id-scanner-core.esm.js.map +1 -0
  7. package/dist/id-scanner-core.js +10882 -0
  8. package/dist/id-scanner-core.js.map +1 -0
  9. package/dist/id-scanner-core.min.js +9 -0
  10. package/dist/id-scanner-core.min.js.map +1 -0
  11. package/dist/id-scanner-ocr.esm.js +1625 -0
  12. package/dist/id-scanner-ocr.esm.js.map +1 -0
  13. package/dist/id-scanner-ocr.js +1634 -0
  14. package/dist/id-scanner-ocr.js.map +1 -0
  15. package/dist/id-scanner-ocr.min.js +9 -0
  16. package/dist/id-scanner-ocr.min.js.map +1 -0
  17. package/dist/id-scanner-qr.esm.js +773 -0
  18. package/dist/id-scanner-qr.esm.js.map +1 -0
  19. package/dist/id-scanner-qr.js +782 -0
  20. package/dist/id-scanner-qr.js.map +1 -0
  21. package/dist/id-scanner-qr.min.js +9 -0
  22. package/dist/id-scanner-qr.min.js.map +1 -0
  23. package/dist/id-scanner.js +1954 -94656
  24. package/dist/id-scanner.js.map +1 -1
  25. package/dist/id-scanner.min.js +7 -7
  26. package/dist/id-scanner.min.js.map +1 -1
  27. package/dist/index-umd.d.ts +96 -0
  28. package/dist/index.d.ts +23 -88
  29. package/dist/ocr-module.d.ts +67 -0
  30. package/dist/qr-module.d.ts +68 -0
  31. package/dist/types/core.d.ts +77 -0
  32. package/dist/types/demo/demo.d.ts +14 -0
  33. package/dist/types/id-recognition/data-extractor.d.ts +105 -0
  34. package/dist/types/id-recognition/id-detector.d.ts +100 -0
  35. package/dist/types/id-recognition/ocr-processor.d.ts +64 -0
  36. package/dist/types/index-umd.d.ts +96 -0
  37. package/dist/types/index.d.ts +78 -0
  38. package/dist/types/ocr-module.d.ts +67 -0
  39. package/dist/types/qr-module.d.ts +68 -0
  40. package/dist/types/scanner/barcode-scanner.d.ts +90 -0
  41. package/dist/types/scanner/qr-scanner.d.ts +80 -0
  42. package/dist/types/utils/camera.d.ts +81 -0
  43. package/dist/types/utils/image-processing.d.ts +75 -0
  44. package/dist/types/utils/types.d.ts +65 -0
  45. package/dist/utils/camera.d.ts +18 -13
  46. package/dist/utils/types.d.ts +6 -6
  47. package/package.json +25 -4
  48. package/src/core.ts +138 -0
  49. package/src/id-recognition/data-extractor.ts +97 -0
  50. package/src/id-recognition/id-detector.ts +230 -67
  51. package/src/id-recognition/ocr-processor.ts +145 -27
  52. package/src/id-recognition/ocr-worker.ts +146 -0
  53. package/src/index-umd.ts +240 -0
  54. package/src/index.ts +125 -139
  55. package/src/ocr-module.ts +139 -0
  56. package/src/qr-module.ts +129 -0
  57. package/src/utils/camera.ts +61 -36
  58. package/src/utils/image-processing.ts +204 -0
  59. package/src/utils/performance.ts +208 -0
  60. package/src/utils/resource-manager.ts +198 -0
  61. package/src/utils/types.ts +23 -6
  62. package/src/utils/worker.ts +173 -0
@@ -7,6 +7,21 @@
7
7
  import { Camera } from '../utils/camera';
8
8
  import { ImageProcessor } from '../utils/image-processing';
9
9
  import { DetectionResult } from '../utils/types';
10
+ import { throttle, LRUCache, calculateImageFingerprint } from '../utils/performance';
11
+ import { Disposable } from '../utils/resource-manager';
12
+
13
+ /**
14
+ * IDCardDetector配置选项
15
+ */
16
+ export interface IDCardDetectorOptions {
17
+ onDetection?: (result: DetectionResult) => void;
18
+ onError?: (error: Error) => void;
19
+ detectionInterval?: number;
20
+ maxImageDimension?: number;
21
+ enableCache?: boolean;
22
+ cacheSize?: number;
23
+ logger?: (message: any) => void;
24
+ }
10
25
 
11
26
  /**
12
27
  * 身份证检测器类
@@ -33,18 +48,68 @@ import { DetectionResult } from '../utils/types';
33
48
  * detector.stop();
34
49
  * ```
35
50
  */
36
- export class IDCardDetector {
51
+ export class IDCardDetector implements Disposable {
37
52
  private camera: Camera;
38
53
  private detecting = false;
39
54
  private detectTimer: number | null = null;
55
+ private onDetected?: (result: DetectionResult) => void;
56
+ private onError?: (error: Error) => void;
57
+ private detectionInterval: number;
58
+ private maxImageDimension: number;
59
+ private resultCache: LRUCache<string, DetectionResult>;
60
+ private throttledDetect: ReturnType<typeof throttle>;
61
+ private frameCount: number = 0;
62
+ private lastDetectionTime: number = 0;
63
+ private options: IDCardDetectorOptions;
40
64
 
41
65
  /**
42
66
  * 创建身份证检测器实例
43
67
  *
44
- * @param {Function} [onDetected] - 身份证检测成功回调函数,接收检测结果对象
68
+ * @param options 身份证检测器配置选项,或者检测回调函数
45
69
  */
46
- constructor(private onDetected?: (result: DetectionResult) => void) {
70
+ constructor(options?: IDCardDetectorOptions | ((result: DetectionResult) => void)) {
47
71
  this.camera = new Camera();
72
+
73
+ if (typeof options === 'function') {
74
+ // 兼容旧的构造函数方式
75
+ this.onDetected = options;
76
+ this.options = {
77
+ detectionInterval: 200,
78
+ maxImageDimension: 800,
79
+ enableCache: true,
80
+ cacheSize: 20,
81
+ logger: console.log
82
+ };
83
+ } else if (options) {
84
+ // 使用新的选项对象方式
85
+ this.options = {
86
+ detectionInterval: 200,
87
+ maxImageDimension: 800,
88
+ enableCache: true,
89
+ cacheSize: 20,
90
+ logger: console.log,
91
+ ...options
92
+ };
93
+ this.onDetected = options.onDetection;
94
+ this.onError = options.onError;
95
+ } else {
96
+ this.options = {
97
+ detectionInterval: 200,
98
+ maxImageDimension: 800,
99
+ enableCache: true,
100
+ cacheSize: 20,
101
+ logger: console.log
102
+ };
103
+ }
104
+
105
+ this.detectionInterval = this.options.detectionInterval!;
106
+ this.maxImageDimension = this.options.maxImageDimension!;
107
+
108
+ // 初始化结果缓存
109
+ this.resultCache = new LRUCache<string, DetectionResult>(this.options.cacheSize);
110
+
111
+ // 创建节流版本的检测函数
112
+ this.throttledDetect = throttle(this.performDetection.bind(this), this.detectionInterval);
48
113
  }
49
114
 
50
115
  /**
@@ -58,114 +123,212 @@ export class IDCardDetector {
58
123
  async start(videoElement: HTMLVideoElement): Promise<void> {
59
124
  await this.camera.initialize(videoElement);
60
125
  this.detecting = true;
126
+ this.frameCount = 0;
127
+ this.lastDetectionTime = 0;
61
128
  this.detect();
62
129
  }
63
130
 
64
131
  /**
65
- * 执行一次身份证检测
66
- *
67
- * 内部方法,捕获当前视频帧并尝试检测其中的身份证
132
+ * 停止身份证检测
133
+ */
134
+ stop(): void {
135
+ this.detecting = false;
136
+ if (this.detectTimer !== null) {
137
+ cancelAnimationFrame(this.detectTimer);
138
+ this.detectTimer = null;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * 持续检测视频帧
68
144
  *
69
145
  * @private
70
146
  */
71
- private async detect(): Promise<void> {
147
+ private detect(): void {
72
148
  if (!this.detecting) return;
73
149
 
74
- const imageData = this.camera.captureFrame();
75
-
76
- if (imageData) {
150
+ this.detectTimer = requestAnimationFrame(() => {
77
151
  try {
78
- // 简单实现,因为没有完整的OpenCV.js
79
- // 实际项目中应该使用OpenCV.js做更精确的边缘检测
80
- const result = await this.detectIDCard(imageData);
152
+ this.frameCount++;
153
+ const now = performance.now();
81
154
 
82
- if (this.onDetected) {
83
- this.onDetected(result);
155
+ // 帧率控制 - 只有满足时间间隔的帧才进行检测
156
+ // 这样可以显著减少CPU使用率,同时保持良好的用户体验
157
+ if (this.frameCount % 3 === 0 || now - this.lastDetectionTime >= this.detectionInterval) {
158
+ this.throttledDetect();
159
+ this.lastDetectionTime = now;
84
160
  }
161
+
162
+ // 继续下一帧检测
163
+ this.detect();
85
164
  } catch (error) {
86
- console.error('身份证检测错误:', error);
165
+ if (this.onError) {
166
+ this.onError(error as Error);
167
+ } else {
168
+ console.error('身份证检测错误:', error);
169
+ }
170
+
171
+ // 出错后延迟重试
172
+ setTimeout(() => {
173
+ if (this.detecting) {
174
+ this.detect();
175
+ }
176
+ }, 1000);
177
+ }
178
+ });
179
+ }
180
+
181
+ /**
182
+ * 执行单帧检测
183
+ *
184
+ * @private
185
+ */
186
+ private async performDetection(): Promise<void> {
187
+ if (!this.detecting || !this.camera) return;
188
+
189
+ // 获取当前视频帧
190
+ const frame = this.camera.captureFrame();
191
+ if (!frame) return;
192
+
193
+ // 检查缓存
194
+ if (this.options.enableCache) {
195
+ const fingerprint = calculateImageFingerprint(frame, 16); // 使用更大的尺寸提高特征区分度
196
+ const cachedResult = this.resultCache.get(fingerprint);
197
+
198
+ if (cachedResult) {
199
+ this.options.logger?.('使用缓存的检测结果');
200
+
201
+ // 使用缓存结果,但更新图像数据以确保最新
202
+ const updatedResult = {
203
+ ...cachedResult,
204
+ imageData: frame
205
+ };
206
+
207
+ if (this.onDetected) {
208
+ this.onDetected(updatedResult);
209
+ }
210
+ return;
87
211
  }
88
212
  }
89
213
 
90
- this.detectTimer = window.setTimeout(() => this.detect(), 200);
214
+ // 降低分辨率以提高性能
215
+ const downsampledFrame = ImageProcessor.downsampleForProcessing(frame, this.maxImageDimension);
216
+
217
+ try {
218
+ // 检测身份证
219
+ const result = await this.detectIDCard(downsampledFrame);
220
+
221
+ // 如果检测成功,将原始图像添加到结果中
222
+ if (result.success) {
223
+ result.imageData = frame;
224
+
225
+ // 缓存结果
226
+ if (this.options.enableCache) {
227
+ const fingerprint = calculateImageFingerprint(frame, 16);
228
+ this.resultCache.set(fingerprint, result);
229
+ }
230
+ }
231
+
232
+ // 处理检测结果
233
+ if (this.onDetected) {
234
+ this.onDetected(result);
235
+ }
236
+ } catch (error) {
237
+ if (this.onError) {
238
+ this.onError(error as Error);
239
+ } else {
240
+ console.error('身份证检测错误:', error);
241
+ }
242
+ }
91
243
  }
92
244
 
93
245
  /**
94
- * 身份证检测核心算法
95
- *
96
- * 通过图像处理技术检测和提取图像中的身份证区域
246
+ * 检测图像中的身份证
97
247
  *
98
248
  * @private
99
- * @param {ImageData} imageData - 需要检测身份证的图像数据
100
- * @returns {Promise<DetectionResult>} 检测结果,包含成功标志和裁剪后的身份证图像
249
+ * @param {ImageData} imageData - 要分析的图像数据
250
+ * @returns {Promise<DetectionResult>} 检测结果
101
251
  */
102
252
  private async detectIDCard(imageData: ImageData): Promise<DetectionResult> {
103
- // 图像预处理
253
+ // 1. 图像预处理
104
254
  const grayscale = ImageProcessor.toGrayscale(imageData);
105
- const enhanced = ImageProcessor.adjustBrightnessContrast(grayscale, 10, 30);
106
255
 
107
- // 简化的身份证检测算法
108
- // 在真实项目中,这里应该使用OpenCV.js进行轮廓检测和矩形检测
256
+ // 2. 检测矩形和边缘(简化版实现)
257
+ // 注意:实际应用中应使用OpenCV.js或其他计算机视觉库进行更精确的检测
258
+ // 此处仅作为概念性实现,使用基本矩形检测逻辑
109
259
 
110
- // 模拟检测过程
111
- const success = Math.random() > 0.7; // 模拟70%的概率检测成功
260
+ // 模拟检测过程,随机判断是否找到身份证
261
+ // 在实际应用中,此处应当实现实际的计算机视觉算法
262
+ const detectionResult: DetectionResult = {
263
+ success: Math.random() > 0.3, // 70%的概率成功检测到
264
+ message: '身份证检测完成'
265
+ };
112
266
 
113
- if (success) {
114
- // 模拟一个身份证区域,实际项目中应该是根据检测结果
115
- const cardWidth = Math.floor(imageData.width * 0.8);
116
- const cardHeight = Math.floor(cardWidth * 0.63); // 身份证比例大约是8:5
117
- const x = Math.floor((imageData.width - cardWidth) / 2);
118
- const y = Math.floor((imageData.height - cardHeight) / 2);
267
+ if (detectionResult.success) {
268
+ // 模拟一个身份证矩形区域
269
+ const width = imageData.width;
270
+ const height = imageData.height;
271
+
272
+ // 大致的身份证区域(按比例)
273
+ const rectWidth = Math.round(width * 0.7);
274
+ const rectHeight = Math.round(rectWidth * 0.618); // 身份证是黄金比例
275
+ const rectX = Math.round((width - rectWidth) / 2);
276
+ const rectY = Math.round((height - rectHeight) / 2);
119
277
 
120
- // 模拟四个角点
121
- const corners = [
122
- { x, y }, // 左上
123
- { x: x + cardWidth, y }, // 右上
124
- { x: x + cardWidth, y: y + cardHeight }, // 右下
125
- { x, y: y + cardHeight } // 左下
278
+ // 添加四个角点
279
+ detectionResult.corners = [
280
+ { x: rectX, y: rectY },
281
+ { x: rectX + rectWidth, y: rectY },
282
+ { x: rectX + rectWidth, y: rectY + rectHeight },
283
+ { x: rectX, y: rectY + rectHeight }
126
284
  ];
127
285
 
128
- // 模拟裁剪图像(实际项目中应该做透视变换)
286
+ // 添加边界框
287
+ detectionResult.boundingBox = {
288
+ x: rectX,
289
+ y: rectY,
290
+ width: rectWidth,
291
+ height: rectHeight
292
+ };
293
+
294
+ // 裁剪身份证图像
129
295
  const canvas = document.createElement('canvas');
130
- canvas.width = cardWidth;
131
- canvas.height = cardHeight;
296
+ canvas.width = rectWidth;
297
+ canvas.height = rectHeight;
132
298
  const ctx = canvas.getContext('2d');
133
299
 
134
300
  if (ctx) {
135
- // 从原图中裁剪身份证区域
136
- const sourceCanvas = ImageProcessor.imageDataToCanvas(imageData);
301
+ const tempCanvas = ImageProcessor.imageDataToCanvas(imageData);
137
302
  ctx.drawImage(
138
- sourceCanvas,
139
- x, y, cardWidth, cardHeight,
140
- 0, 0, cardWidth, cardHeight
303
+ tempCanvas,
304
+ rectX, rectY, rectWidth, rectHeight,
305
+ 0, 0, rectWidth, rectHeight
141
306
  );
142
307
 
143
- const croppedImage = ctx.getImageData(0, 0, cardWidth, cardHeight);
144
-
145
- return {
146
- success: true,
147
- corners,
148
- croppedImage
149
- };
308
+ detectionResult.croppedImage = ctx.getImageData(0, 0, rectWidth, rectHeight);
150
309
  }
310
+
311
+ // 设置置信度
312
+ detectionResult.confidence = 0.7 + Math.random() * 0.3;
151
313
  }
152
314
 
153
- return { success: false };
315
+ return detectionResult;
154
316
  }
155
317
 
156
318
  /**
157
- * 停止身份证检测
158
- *
159
- * 停止检测循环并释放相机资源
319
+ * 清除检测结果缓存
160
320
  */
161
- stop(): void {
162
- this.detecting = false;
163
-
164
- if (this.detectTimer) {
165
- clearTimeout(this.detectTimer);
166
- this.detectTimer = null;
167
- }
168
-
321
+ clearCache(): void {
322
+ this.resultCache.clear();
323
+ this.options.logger?.('检测结果缓存已清除');
324
+ }
325
+
326
+ /**
327
+ * 释放资源
328
+ */
329
+ dispose(): void {
330
+ this.stop();
169
331
  this.camera.release();
332
+ this.resultCache.clear();
170
333
  }
171
334
  }
@@ -7,6 +7,26 @@
7
7
  import { createWorker } from 'tesseract.js';
8
8
  import { IDCardInfo } from '../utils/types';
9
9
  import { ImageProcessor } from '../utils/image-processing';
10
+ import { LRUCache, calculateImageFingerprint } from '../utils/performance';
11
+ import { isWorkerSupported, createWorker as createCustomWorker } from '../utils/worker';
12
+ import { processOCRInWorker, OCRProcessInput } from './ocr-worker';
13
+ import { Disposable } from '../utils/resource-manager';
14
+
15
+ /**
16
+ * OCR处理器选项接口
17
+ */
18
+ export interface OCRProcessorOptions {
19
+ /** 是否使用Worker线程 */
20
+ useWorker?: boolean;
21
+ /** 是否启用结果缓存 */
22
+ enableCache?: boolean;
23
+ /** 缓存容量 */
24
+ cacheSize?: number;
25
+ /** 图像处理前的最大尺寸 */
26
+ maxImageDimension?: number;
27
+ /** 日志回调函数 */
28
+ logger?: (message: any) => void;
29
+ }
10
30
 
11
31
  /**
12
32
  * OCR处理器类
@@ -29,10 +49,31 @@ import { ImageProcessor } from '../utils/image-processing';
29
49
  * await ocrProcessor.terminate();
30
50
  * ```
31
51
  */
32
- export class OCRProcessor {
52
+ export class OCRProcessor implements Disposable {
33
53
  private worker: any = null;
54
+ private ocrWorker: ReturnType<typeof createCustomWorker<OCRProcessInput, { idCardInfo: IDCardInfo, processingTime: number }>> | null = null;
55
+ private initialized: boolean = false;
56
+ private resultCache: LRUCache<string, IDCardInfo>;
57
+ private options: OCRProcessorOptions;
34
58
 
35
- constructor() {}
59
+ /**
60
+ * 创建OCR处理器实例
61
+ *
62
+ * @param options OCR处理器选项
63
+ */
64
+ constructor(options: OCRProcessorOptions = {}) {
65
+ this.options = {
66
+ useWorker: isWorkerSupported(),
67
+ enableCache: true,
68
+ cacheSize: 50,
69
+ maxImageDimension: 1000,
70
+ logger: console.log,
71
+ ...options
72
+ };
73
+
74
+ // 初始化缓存
75
+ this.resultCache = new LRUCache<string, IDCardInfo>(this.options.cacheSize);
76
+ }
36
77
 
37
78
  /**
38
79
  * 初始化OCR引擎
@@ -42,46 +83,100 @@ export class OCRProcessor {
42
83
  * @returns {Promise<void>} 初始化完成的Promise
43
84
  */
44
85
  async initialize(): Promise<void> {
45
- this.worker = createWorker({
46
- logger: (m: any) => console.log(m)
47
- } as any);
48
-
49
- await this.worker.load();
50
- await this.worker.loadLanguage('chi_sim');
51
- await this.worker.initialize('chi_sim');
52
- await this.worker.setParameters({
53
- tessedit_char_whitelist: '0123456789X-年月日一二三四五六七八九十零壹贰叁肆伍陆柒捌玖拾ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz民族汉族满族回族维吾尔族藏族苗族彝族壮族朝鲜族侗族瑶族白族土家族哈尼族哈萨克族傣族黎族傈僳族佤族高山族拉祜族水族东乡族钠西族景颇族柯尔克孜族士族达斡尔族仫佬族羌族布朗族撒拉族毛南族仡佬族锡伯族阿昌族普米族塔吉克族怒族乌孜别克族俄罗斯族鄂温克族德昂族保安族裕固族京族塔塔尔族独龙族鄂伦春族赫哲族门巴族珞巴族基诺族男女性别住址出生公民身份号码签发机关有效期'
54
- });
86
+ if (this.initialized) return;
87
+
88
+ if (this.options.useWorker) {
89
+ // 使用自定义Worker线程处理OCR
90
+ this.ocrWorker = createCustomWorker<OCRProcessInput, { idCardInfo: IDCardInfo, processingTime: number }>(processOCRInWorker);
91
+ this.initialized = true;
92
+ this.options.logger?.('OCR Worker 初始化完成');
93
+ } else {
94
+ // 使用主线程处理OCR
95
+ this.worker = createWorker({
96
+ logger: this.options.logger
97
+ } as any);
98
+
99
+ await this.worker.load();
100
+ await this.worker.loadLanguage('chi_sim');
101
+ await this.worker.initialize('chi_sim');
102
+ await this.worker.setParameters({
103
+ tessedit_char_whitelist: '0123456789X-年月日一二三四五六七八九十零壹贰叁肆伍陆柒捌玖拾ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz民族汉族满族回族维吾尔族藏族苗族彝族壮族朝鲜族侗族瑶族白族土家族哈尼族哈萨克族傣族黎族傈僳族佤族高山族拉祜族水族东乡族钠西族景颇族柯尔克孜族士族达斡尔族仫佬族羌族布朗族撒拉族毛南族仡佬族锡伯族阿昌族普米族塔吉克族怒族乌孜别克族俄罗斯族鄂温克族德昂族保安族裕固族京族塔塔尔族独龙族鄂伦春族赫哲族门巴族珞巴族基诺族男女性别住址出生公民身份号码签发机关有效期'
104
+ });
105
+
106
+ this.initialized = true;
107
+ this.options.logger?.('OCR引擎初始化完成');
108
+ }
55
109
  }
56
110
 
57
111
  /**
58
112
  * 处理身份证图像并提取信息
59
- *
60
- * 对身份证图像进行OCR识别,并从识别结果中提取结构化信息
61
- *
62
- * @param {ImageData} imageData - 身份证图像数据
63
- * @returns {Promise<IDCardInfo>} 提取到的身份证信息
113
+ * @param imageData 要处理的身份证图像数据
114
+ * @returns 提取的身份证信息
64
115
  */
65
116
  async processIDCard(imageData: ImageData): Promise<IDCardInfo> {
66
- if (!this.worker) {
117
+ if (!this.initialized) {
67
118
  await this.initialize();
68
119
  }
69
120
 
70
- // 图像预处理,提高OCR识别率
71
- const enhancedImage = ImageProcessor.adjustBrightnessContrast(imageData, 15, 25);
121
+ // 计算图像指纹,用于缓存查找
122
+ if (this.options.enableCache) {
123
+ const fingerprint = calculateImageFingerprint(imageData);
124
+
125
+ // 检查缓存中是否有结果
126
+ const cachedResult = this.resultCache.get(fingerprint);
127
+ if (cachedResult) {
128
+ this.options.logger?.('使用缓存的OCR结果');
129
+ return cachedResult;
130
+ }
131
+ }
72
132
 
73
- // 转换ImageData为Canvas
74
- const canvas = ImageProcessor.imageDataToCanvas(enhancedImage);
133
+ // 图像预处理:降低分辨率和增强对比度
134
+ const downsampledImage = ImageProcessor.downsampleForProcessing(imageData, this.options.maxImageDimension);
135
+ const enhancedImage = ImageProcessor.adjustBrightnessContrast(downsampledImage, 15, 25);
75
136
 
76
137
  // OCR识别
77
138
  try {
78
- const { data } = await this.worker.recognize(canvas);
139
+ let idCardInfo: IDCardInfo;
140
+
141
+ if (this.options.useWorker && this.ocrWorker) {
142
+ // 使用Worker线程处理
143
+ const base64Image = ImageProcessor.imageDataToBase64(enhancedImage);
144
+
145
+ const result = await this.ocrWorker.postMessage({
146
+ imageBase64: base64Image,
147
+ tessWorkerOptions: {
148
+ logger: this.options.logger
149
+ }
150
+ });
151
+
152
+ idCardInfo = result.idCardInfo;
153
+ this.options.logger?.(`OCR处理完成,用时: ${result.processingTime.toFixed(2)}ms`);
154
+ } else {
155
+ // 使用主线程处理
156
+ const startTime = performance.now();
157
+
158
+ // 转换ImageData为Canvas
159
+ const canvas = ImageProcessor.imageDataToCanvas(enhancedImage);
160
+
161
+ const { data } = await this.worker.recognize(canvas);
162
+
163
+ // 解析身份证信息
164
+ idCardInfo = this.parseIDCardText(data.text);
165
+
166
+ const processingTime = performance.now() - startTime;
167
+ this.options.logger?.(`OCR处理完成,用时: ${processingTime.toFixed(2)}ms`);
168
+ }
169
+
170
+ // 缓存结果
171
+ if (this.options.enableCache) {
172
+ const fingerprint = calculateImageFingerprint(imageData);
173
+ this.resultCache.set(fingerprint, idCardInfo);
174
+ }
79
175
 
80
- // 解析身份证信息
81
- return this.parseIDCardText(data.text);
176
+ return idCardInfo;
82
177
  } catch (error) {
83
- console.error('OCR识别错误:', error);
84
- return {}; // 返回空对象
178
+ this.options.logger?.(`OCR识别错误: ${error}`);
179
+ return {} as IDCardInfo;
85
180
  }
86
181
  }
87
182
 
@@ -155,6 +250,14 @@ export class OCRProcessor {
155
250
  return info;
156
251
  }
157
252
 
253
+ /**
254
+ * 清除结果缓存
255
+ */
256
+ clearCache(): void {
257
+ this.resultCache.clear();
258
+ this.options.logger?.('OCR结果缓存已清除');
259
+ }
260
+
158
261
  /**
159
262
  * 终止OCR引擎并释放资源
160
263
  *
@@ -165,5 +268,20 @@ export class OCRProcessor {
165
268
  await this.worker.terminate();
166
269
  this.worker = null;
167
270
  }
271
+
272
+ if (this.ocrWorker) {
273
+ this.ocrWorker.terminate();
274
+ this.ocrWorker = null;
275
+ }
276
+
277
+ this.initialized = false;
278
+ this.options.logger?.('OCR引擎已终止');
279
+ }
280
+
281
+ /**
282
+ * 释放资源
283
+ */
284
+ dispose(): Promise<void> {
285
+ return this.terminate();
168
286
  }
169
287
  }