id-scanner-lib 1.3.3 → 1.5.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 (101) hide show
  1. package/README.md +55 -460
  2. package/dist/id-scanner-lib.esm.js +4641 -0
  3. package/dist/id-scanner-lib.esm.js.map +1 -0
  4. package/dist/id-scanner-lib.js +14755 -0
  5. package/dist/id-scanner-lib.js.map +1 -0
  6. package/dist/types/core/base-module.d.ts +44 -0
  7. package/dist/types/core/camera-manager.d.ts +258 -0
  8. package/dist/types/core/config.d.ts +88 -0
  9. package/dist/types/core/errors.d.ts +111 -0
  10. package/dist/types/core/event-emitter.d.ts +55 -0
  11. package/dist/types/core/logger.d.ts +277 -0
  12. package/dist/types/core/module-manager.d.ts +78 -0
  13. package/dist/types/core/plugin-manager.d.ts +158 -0
  14. package/dist/types/core/resource-manager.d.ts +246 -0
  15. package/dist/types/core/result.d.ts +83 -0
  16. package/dist/types/core/scanner-factory.d.ts +93 -0
  17. package/dist/types/index.bundle.d.ts +1303 -0
  18. package/dist/types/index.d.ts +86 -0
  19. package/dist/types/interfaces/external-types.d.ts +174 -0
  20. package/dist/types/interfaces/face-detection.d.ts +293 -0
  21. package/dist/types/interfaces/scanner-module.d.ts +280 -0
  22. package/dist/types/modules/face/face-detector.d.ts +170 -0
  23. package/dist/types/modules/face/index.d.ts +56 -0
  24. package/dist/types/modules/face/liveness-detector.d.ts +177 -0
  25. package/dist/types/modules/face/types.d.ts +136 -0
  26. package/dist/types/modules/id-card/anti-fake-detector.d.ts +170 -0
  27. package/dist/types/modules/id-card/id-card-detector.d.ts +131 -0
  28. package/dist/types/modules/id-card/index.d.ts +89 -0
  29. package/dist/types/modules/id-card/ocr-processor.d.ts +110 -0
  30. package/dist/types/modules/id-card/ocr-worker.d.ts +31 -0
  31. package/dist/types/modules/id-card/types.d.ts +181 -0
  32. package/dist/types/modules/qrcode/index.d.ts +51 -0
  33. package/dist/types/modules/qrcode/qr-code-scanner.d.ts +64 -0
  34. package/dist/types/modules/qrcode/types.d.ts +67 -0
  35. package/dist/types/utils/camera.d.ts +81 -0
  36. package/dist/types/utils/image-processing.d.ts +176 -0
  37. package/dist/types/utils/index.d.ts +175 -0
  38. package/dist/types/utils/performance.d.ts +81 -0
  39. package/dist/types/utils/resource-manager.d.ts +53 -0
  40. package/dist/types/utils/types.d.ts +166 -0
  41. package/dist/types/utils/worker.d.ts +52 -0
  42. package/dist/types/version.d.ts +7 -0
  43. package/package.json +76 -75
  44. package/src/core/base-module.ts +78 -0
  45. package/src/core/camera-manager.ts +798 -0
  46. package/src/core/config.ts +268 -0
  47. package/src/core/errors.ts +174 -0
  48. package/src/core/event-emitter.ts +110 -0
  49. package/src/core/logger.ts +549 -0
  50. package/src/core/module-manager.ts +165 -0
  51. package/src/core/plugin-manager.ts +429 -0
  52. package/src/core/resource-manager.ts +762 -0
  53. package/src/core/result.ts +163 -0
  54. package/src/core/scanner-factory.ts +237 -0
  55. package/src/index.ts +113 -936
  56. package/src/interfaces/external-types.ts +200 -0
  57. package/src/interfaces/face-detection.ts +309 -0
  58. package/src/interfaces/scanner-module.ts +384 -0
  59. package/src/modules/face/face-detector.ts +931 -0
  60. package/src/modules/face/index.ts +208 -0
  61. package/src/modules/face/liveness-detector.ts +908 -0
  62. package/src/modules/face/types.ts +133 -0
  63. package/src/{id-recognition → modules/id-card}/anti-fake-detector.ts +273 -239
  64. package/src/modules/id-card/id-card-detector.ts +474 -0
  65. package/src/modules/id-card/index.ts +425 -0
  66. package/src/{id-recognition → modules/id-card}/ocr-processor.ts +149 -92
  67. package/src/modules/id-card/ocr-worker.ts +259 -0
  68. package/src/modules/id-card/types.ts +178 -0
  69. package/src/modules/qrcode/index.ts +175 -0
  70. package/src/modules/qrcode/qr-code-scanner.ts +230 -0
  71. package/src/modules/qrcode/types.ts +65 -0
  72. package/src/types/tesseract.d.ts +265 -22
  73. package/src/utils/image-processing.ts +68 -49
  74. package/src/utils/index.ts +426 -0
  75. package/src/utils/performance.ts +168 -131
  76. package/src/utils/resource-manager.ts +65 -146
  77. package/src/utils/types.ts +90 -2
  78. package/src/utils/worker.ts +123 -84
  79. package/src/version.ts +11 -0
  80. package/tools/scaffold.js +543 -0
  81. package/dist/id-scanner-core.esm.js +0 -11349
  82. package/dist/id-scanner-core.js +0 -11361
  83. package/dist/id-scanner-core.min.js +0 -1
  84. package/dist/id-scanner-ocr.esm.js +0 -2319
  85. package/dist/id-scanner-ocr.js +0 -2328
  86. package/dist/id-scanner-ocr.min.js +0 -1
  87. package/dist/id-scanner-qr.esm.js +0 -1296
  88. package/dist/id-scanner-qr.js +0 -1305
  89. package/dist/id-scanner-qr.min.js +0 -1
  90. package/dist/id-scanner.js +0 -4561
  91. package/dist/id-scanner.min.js +0 -1
  92. package/src/core.ts +0 -138
  93. package/src/demo/demo.ts +0 -204
  94. package/src/id-recognition/data-extractor.ts +0 -262
  95. package/src/id-recognition/id-detector.ts +0 -510
  96. package/src/id-recognition/ocr-worker.ts +0 -156
  97. package/src/index-umd.ts +0 -477
  98. package/src/ocr-module.ts +0 -187
  99. package/src/qr-module.ts +0 -179
  100. package/src/scanner/barcode-scanner.ts +0 -251
  101. package/src/scanner/qr-scanner.ts +0 -167
@@ -1,510 +0,0 @@
1
- /**
2
- * @file 身份证检测模块
3
- * @description 提供自动检测和定位图像中的身份证功能
4
- * @module IDCardDetector
5
- * @version 1.3.2
6
- */
7
-
8
- import { Camera } from "../utils/camera"
9
- import { ImageProcessor } from "../utils/image-processing"
10
- import { DetectionResult } from "../utils/types"
11
- import {
12
- throttle,
13
- LRUCache,
14
- calculateImageFingerprint,
15
- } from "../utils/performance"
16
- import { Disposable } from "../utils/resource-manager"
17
-
18
- /**
19
- * IDCardDetector配置选项
20
- */
21
- export interface IDCardDetectorOptions {
22
- onDetection?: (result: DetectionResult) => void
23
- onError?: (error: Error) => void
24
- detectionInterval?: number
25
- maxImageDimension?: number
26
- enableCache?: boolean
27
- cacheSize?: number
28
- logger?: (message: any) => void
29
- }
30
-
31
- /**
32
- * 身份证检测器类
33
- *
34
- * 通过图像处理和计算机视觉技术,实时检测视频流中的身份证,并提取身份证区域
35
- * 注意:当前实现是简化版,实际项目中建议使用OpenCV.js进行更精确的检测
36
- *
37
- * @example
38
- * ```typescript
39
- * // 创建身份证检测器
40
- * const detector = new IDCardDetector((result) => {
41
- * if (result.success && result.croppedImage) {
42
- * console.log('检测到身份证!');
43
- * // 对裁剪出的身份证图像进行处理
44
- * processIDCardImage(result.croppedImage);
45
- * }
46
- * });
47
- *
48
- * // 启动检测
49
- * const videoElement = document.getElementById('video') as HTMLVideoElement;
50
- * await detector.start(videoElement);
51
- *
52
- * // 停止检测
53
- * detector.stop();
54
- * ```
55
- */
56
- export class IDCardDetector implements Disposable {
57
- // 身份证标准宽高比(近似黄金比例)
58
- private static readonly ID_CARD_ASPECT_RATIO = 1.58 // 标准身份证宽高比
59
- private camera: Camera
60
- private detecting = false
61
- private detectTimer: number | null = null
62
- private onDetected?: (result: DetectionResult) => void
63
- private onError?: (error: Error) => void
64
- private detectionInterval: number
65
- private maxImageDimension: number
66
- private resultCache: LRUCache<string, DetectionResult>
67
- private throttledDetect: ReturnType<typeof throttle>
68
- private frameCount: number = 0
69
- private lastDetectionTime: number = 0
70
- private options: IDCardDetectorOptions
71
-
72
- /**
73
- * 创建身份证检测器实例
74
- *
75
- * @param options 身份证检测器配置选项,或者检测回调函数
76
- */
77
- constructor(
78
- options?: IDCardDetectorOptions | ((result: DetectionResult) => void)
79
- ) {
80
- this.camera = new Camera()
81
-
82
- if (typeof options === "function") {
83
- // 兼容旧的构造函数方式
84
- this.onDetected = options
85
- this.options = {
86
- detectionInterval: 200,
87
- maxImageDimension: 800,
88
- enableCache: true,
89
- cacheSize: 20,
90
- logger: console.log,
91
- }
92
- } else if (options) {
93
- // 使用新的选项对象方式
94
- this.options = {
95
- detectionInterval: 200,
96
- maxImageDimension: 800,
97
- enableCache: true,
98
- cacheSize: 20,
99
- logger: console.log,
100
- ...options,
101
- }
102
- this.onDetected = options.onDetection
103
- this.onError = options.onError
104
- } else {
105
- this.options = {
106
- detectionInterval: 200,
107
- maxImageDimension: 800,
108
- enableCache: true,
109
- cacheSize: 20,
110
- logger: console.log,
111
- }
112
- }
113
-
114
- this.detectionInterval = this.options.detectionInterval!
115
- this.maxImageDimension = this.options.maxImageDimension!
116
-
117
- // 初始化结果缓存
118
- this.resultCache = new LRUCache<string, DetectionResult>(
119
- this.options.cacheSize
120
- )
121
-
122
- // 创建节流版本的检测函数
123
- this.throttledDetect = throttle(
124
- this.performDetection.bind(this),
125
- this.detectionInterval
126
- )
127
- }
128
-
129
- /**
130
- * 启动身份证检测
131
- *
132
- * 初始化相机并开始连续检测视频帧中的身份证
133
- *
134
- * @param {HTMLVideoElement} videoElement - 用于显示相机画面的video元素
135
- * @returns {Promise<void>} 启动完成的Promise
136
- */
137
- async start(videoElement: HTMLVideoElement): Promise<void> {
138
- await this.camera.initialize(videoElement)
139
- this.detecting = true
140
- this.frameCount = 0
141
- this.lastDetectionTime = 0
142
- this.detect()
143
- }
144
-
145
- /**
146
- * 停止身份证检测
147
- */
148
- stop(): void {
149
- this.detecting = false
150
- if (this.detectTimer !== null) {
151
- cancelAnimationFrame(this.detectTimer)
152
- this.detectTimer = null
153
- }
154
- }
155
-
156
- /**
157
- * 持续检测视频帧
158
- *
159
- * @private
160
- */
161
- private detect(): void {
162
- if (!this.detecting) return
163
-
164
- this.detectTimer = requestAnimationFrame(() => {
165
- try {
166
- this.frameCount++
167
- const now = performance.now()
168
-
169
- // 帧率控制 - 只有满足时间间隔的帧才进行检测
170
- // 这样可以显著减少CPU使用率,同时保持良好的用户体验
171
- if (
172
- this.frameCount % 3 === 0 ||
173
- now - this.lastDetectionTime >= this.detectionInterval
174
- ) {
175
- this.throttledDetect()
176
- this.lastDetectionTime = now
177
- }
178
-
179
- // 继续下一帧检测
180
- this.detect()
181
- } catch (error) {
182
- if (this.onError) {
183
- this.onError(error as Error)
184
- } else {
185
- console.error("身份证检测错误:", error)
186
- }
187
-
188
- // 出错后延迟重试
189
- setTimeout(() => {
190
- if (this.detecting) {
191
- this.detect()
192
- }
193
- }, 1000)
194
- }
195
- })
196
- }
197
-
198
- /**
199
- * 执行单帧检测
200
- *
201
- * @private
202
- */
203
- private async performDetection(): Promise<void> {
204
- if (!this.detecting || !this.camera) return
205
-
206
- // 获取当前视频帧
207
- const frame = this.camera.captureFrame()
208
- if (!frame) return
209
-
210
- // 检查缓存
211
- if (this.options.enableCache) {
212
- const fingerprint = calculateImageFingerprint(frame, 16) // 使用更大的尺寸提高特征区分度
213
- const cachedResult = this.resultCache.get(fingerprint)
214
-
215
- if (cachedResult) {
216
- this.options.logger?.("使用缓存的检测结果")
217
-
218
- // 使用缓存结果,但更新图像数据以确保最新
219
- const updatedResult = {
220
- ...cachedResult,
221
- imageData: frame,
222
- }
223
-
224
- if (this.onDetected) {
225
- this.onDetected(updatedResult)
226
- }
227
- return
228
- }
229
- }
230
-
231
- // 降低分辨率以提高性能
232
- const downsampledFrame = ImageProcessor.resizeImage(
233
- frame,
234
- this.maxImageDimension,
235
- this.maxImageDimension
236
- )
237
-
238
- try {
239
- // 检测身份证
240
- const result = await this.detectIDCard(downsampledFrame)
241
-
242
- // 如果检测成功,将原始图像添加到结果中
243
- if (result.success) {
244
- result.imageData = frame
245
-
246
- // 缓存结果
247
- if (this.options.enableCache) {
248
- const fingerprint = calculateImageFingerprint(frame, 16)
249
- this.resultCache.set(fingerprint, result)
250
- }
251
- }
252
-
253
- // 处理检测结果
254
- if (this.onDetected) {
255
- this.onDetected(result)
256
- }
257
- } catch (error) {
258
- if (this.onError) {
259
- this.onError(error as Error)
260
- } else {
261
- console.error("身份证检测错误:", error)
262
- }
263
- }
264
- }
265
-
266
- /**
267
- * 检测图像中的身份证
268
- *
269
- * @private
270
- * @param {ImageData} imageData - 要分析的图像数据
271
- * @returns {Promise<DetectionResult>} 检测结果
272
- */
273
- private async detectIDCard(imageData: ImageData): Promise<DetectionResult> {
274
- // 1. 图像预处理
275
- const grayscale = ImageProcessor.toGrayscale(imageData)
276
-
277
- // 2. 使用Sobel边缘检测算法检测边缘
278
- const edgeData = ImageProcessor.detectEdges(grayscale)
279
-
280
- // 3. 检测矩形和边缘
281
- // 使用基于边缘的矩形检测
282
- const rectangles = this.detectRectangles(edgeData)
283
-
284
- // 4. 评估检测结果 - 检查是否找到了合适的矩形
285
- const idCardRect = this.findIdCardRectangle(rectangles, imageData.width, imageData.height)
286
-
287
- const detectionResult: DetectionResult = {
288
- success: idCardRect !== null,
289
- message: idCardRect ? "身份证检测成功" : "未检测到身份证",
290
- }
291
-
292
- if (detectionResult.success && idCardRect) {
293
- // 使用检测到的身份证矩形区域
294
- const width = imageData.width
295
- const height = imageData.height
296
-
297
- // 使用实际检测到的身份证区域
298
- const rectWidth = idCardRect.width
299
- const rectHeight = idCardRect.height
300
- const rectX = idCardRect.x
301
- const rectY = idCardRect.y
302
-
303
- // 添加四个角点
304
- detectionResult.corners = [
305
- { x: rectX, y: rectY },
306
- { x: rectX + rectWidth, y: rectY },
307
- { x: rectX + rectWidth, y: rectY + rectHeight },
308
- { x: rectX, y: rectY + rectHeight },
309
- ]
310
-
311
- // 添加边界框
312
- detectionResult.boundingBox = {
313
- x: rectX,
314
- y: rectY,
315
- width: rectWidth,
316
- height: rectHeight,
317
- }
318
-
319
- // 裁剪身份证图像
320
- const canvas = document.createElement("canvas")
321
- canvas.width = rectWidth
322
- canvas.height = rectHeight
323
- const ctx = canvas.getContext("2d")
324
-
325
- if (ctx) {
326
- const tempCanvas = ImageProcessor.imageDataToCanvas(imageData)
327
- ctx.drawImage(
328
- tempCanvas,
329
- rectX,
330
- rectY,
331
- rectWidth,
332
- rectHeight,
333
- 0,
334
- 0,
335
- rectWidth,
336
- rectHeight
337
- )
338
-
339
- detectionResult.croppedImage = ctx.getImageData(
340
- 0,
341
- 0,
342
- rectWidth,
343
- rectHeight
344
- )
345
- }
346
-
347
- // 设置置信度 - 基于边缘强度和矩形形状评分
348
- detectionResult.confidence = this.calculateConfidence(idCardRect, edgeData)
349
- }
350
-
351
- return detectionResult
352
- }
353
-
354
- /**
355
- * 清除检测结果缓存
356
- */
357
- clearCache(): void {
358
- this.resultCache.clear()
359
- this.options.logger?.("检测结果缓存已清除")
360
- }
361
-
362
- /**
363
- * 释放资源
364
- */
365
- dispose(): void {
366
- this.stop()
367
- this.camera.release()
368
- this.resultCache.clear()
369
- }
370
-
371
- /**
372
- * 从边缘图像中检测矩形
373
- * @param edgeData 边缘检测后的图像数据
374
- * @returns 检测到的矩形数组
375
- */
376
- private detectRectangles(edgeData: ImageData): Array<{
377
- x: number;
378
- y: number;
379
- width: number;
380
- height: number;
381
- confidence: number;
382
- }> {
383
- const width = edgeData.width;
384
- const height = edgeData.height;
385
- const minSize = Math.min(width, height) * 0.2; // 最小矩形尺寸
386
- const rectangles = [];
387
-
388
- // 使用积分图像加速边缘密度计算
389
- const integralImg = new Uint32Array(width * height);
390
-
391
- // 计算积分图像
392
- for (let y = 0; y < height; y++) {
393
- for (let x = 0; x < width; x++) {
394
- const idx = y * width + x;
395
- const pixel = (edgeData.data[idx * 4] > 128) ? 1 : 0; // 边缘为白色
396
-
397
- // 计算积分图
398
- const above = y > 0 ? integralImg[(y - 1) * width + x] : 0;
399
- const left = x > 0 ? integralImg[y * width + (x - 1)] : 0;
400
- const diagonal = (x > 0 && y > 0) ? integralImg[(y - 1) * width + (x - 1)] : 0;
401
-
402
- integralImg[idx] = pixel + above + left - diagonal;
403
- }
404
- }
405
-
406
- // 滑动窗口检测矩形
407
- for (let h = minSize; h < height * 0.9; h += Math.max(2, Math.floor(h * 0.05))) {
408
- // 计算当前高度下,按照标准身份证比例的宽度
409
- const w = Math.round(h * IDCardDetector.ID_CARD_ASPECT_RATIO);
410
- if (w > width * 0.9) continue;
411
-
412
- for (let y = 0; y < height - h; y += Math.max(2, Math.floor(h * 0.1))) {
413
- for (let x = 0; x < width - w; x += Math.max(2, Math.floor(w * 0.1))) {
414
- // 计算矩形区域内的边缘密度
415
- const edgeCount = this.calculateRectSum(integralImg, x, y, w, h, width);
416
- const avgEdgeDensity = edgeCount / (w * h);
417
-
418
- // 计算矩形边界的边缘密度
419
- const perimeterEdgeCount = this.calculateRectPerimeter(integralImg, x, y, w, h, width);
420
- const perimeterLength = 2 * (w + h);
421
- const perimeterDensity = perimeterEdgeCount / perimeterLength;
422
-
423
- // 矩形得分 - 边界边缘密度高且内部适中
424
- const rectScore = perimeterDensity * 0.7 + (0.3 - Math.abs(0.15 - avgEdgeDensity)) * 0.3;
425
-
426
- if (rectScore > 0.4) { // 阈值可根据实际项目调整
427
- rectangles.push({
428
- x,
429
- y,
430
- width: w,
431
- height: h,
432
- confidence: rectScore
433
- });
434
- }
435
- }
436
- }
437
- }
438
-
439
- // 按得分排序
440
- return rectangles.sort((a, b) => b.confidence - a.confidence);
441
- }
442
-
443
- /**
444
- * 使用积分图计算矩形区域内的总和
445
- */
446
- private calculateRectSum(integral: Uint32Array, x: number, y: number, w: number, h: number, stride: number): number {
447
- const x2 = Math.min(x + w - 1, stride - 1);
448
- const y2 = Math.min(y + h - 1, integral.length / stride - 1);
449
-
450
- const topLeft = (x > 0 && y > 0) ? integral[(y - 1) * stride + (x - 1)] : 0;
451
- const topRight = y > 0 ? integral[(y - 1) * stride + x2] : 0;
452
- const bottomLeft = x > 0 ? integral[y2 * stride + (x - 1)] : 0;
453
- const bottomRight = integral[y2 * stride + x2];
454
-
455
- return bottomRight - topRight - bottomLeft + topLeft;
456
- }
457
-
458
- /**
459
- * 计算矩形周长上的边缘点数量
460
- */
461
- private calculateRectPerimeter(integral: Uint32Array, x: number, y: number, w: number, h: number, stride: number): number {
462
- // 上边缘
463
- const topEdgeSum = this.calculateRectSum(integral, x, y, w, 1, stride);
464
- // 下边缘
465
- const bottomEdgeSum = this.calculateRectSum(integral, x, y + h - 1, w, 1, stride);
466
- // 左边缘
467
- const leftEdgeSum = this.calculateRectSum(integral, x, y, 1, h, stride);
468
- // 右边缘
469
- const rightEdgeSum = this.calculateRectSum(integral, x + w - 1, y, 1, h, stride);
470
-
471
- return topEdgeSum + bottomEdgeSum + leftEdgeSum + rightEdgeSum;
472
- }
473
-
474
- /**
475
- * 从检测到的矩形中找出最可能是身份证的矩形
476
- */
477
- private findIdCardRectangle(rectangles: Array<{x: number; y: number; width: number; height: number; confidence: number}>, imageWidth: number, imageHeight: number): {x: number; y: number; width: number; height: number; confidence: number} | null {
478
- if (rectangles.length === 0) return null;
479
-
480
- // 筛选符合身份证宽高比的矩形
481
- const filteredRects = rectangles.filter(rect => {
482
- const aspectRatio = rect.width / rect.height;
483
- return Math.abs(aspectRatio - IDCardDetector.ID_CARD_ASPECT_RATIO) < 0.2; // 允许20%的误差
484
- });
485
-
486
- if (filteredRects.length === 0) return null;
487
-
488
- // 返回得分最高的矩形
489
- return filteredRects[0];
490
- }
491
-
492
- /**
493
- * 计算身份证检测的置信度
494
- */
495
- private calculateConfidence(rect: {x: number; y: number; width: number; height: number; confidence: number} | null, edgeData: ImageData): number {
496
- if (!rect) return 0;
497
-
498
- // 基本得分来自矩形检测
499
- let score = rect.confidence;
500
-
501
- // 额外因素:矩形大小相对于图像
502
- const relativeSize = (rect.width * rect.height) / (edgeData.width * edgeData.height);
503
- if (relativeSize > 0.1 && relativeSize < 0.7) {
504
- score += 0.1; // 身份证通常占据图像的合理比例
505
- }
506
-
507
- // 范围限制在0-1之间
508
- return Math.min(Math.max(score, 0), 1);
509
- }
510
- }
@@ -1,156 +0,0 @@
1
- /**
2
- * @file OCR Worker处理模块
3
- * @description 用于在Web Worker中执行OCR处理
4
- * @module OCRWorker
5
- */
6
-
7
- import type { IDCardInfo } from "../utils/types"
8
-
9
- /**
10
- * OCR处理输入接口
11
- */
12
- export interface OCRProcessInput {
13
- imageBase64: string
14
- tessWorkerOptions?: any
15
- }
16
-
17
- /**
18
- * OCR处理输出接口
19
- */
20
- export interface OCRProcessOutput {
21
- idCardInfo: IDCardInfo
22
- processingTime: number
23
- }
24
-
25
- /**
26
- * 在Web Worker中执行OCR处理的函数
27
- *
28
- * 该函数用于在使用 createWorker 创建的 Worker 中执行
29
- *
30
- * @param input OCR处理输入数据
31
- * @returns OCR处理结果
32
- */
33
- export async function processOCRInWorker(
34
- input: OCRProcessInput
35
- ): Promise<OCRProcessOutput> {
36
- // 计时开始
37
- const startTime = performance.now()
38
-
39
- // 加载Tesseract.js (Worker 环境下动态导入)
40
- const { createWorker } = await import("tesseract.js")
41
-
42
- // 创建OCR Worker
43
- const worker = (await createWorker(
44
- input.tessWorkerOptions || {
45
- logger: (m: any) => console.log(m),
46
- }
47
- )) as any // 添加类型断言,避免TypeScript错误
48
-
49
- try {
50
- // 初始化OCR引擎
51
- await worker.load()
52
- await worker.loadLanguage("chi_sim")
53
- await worker.initialize("chi_sim")
54
- await worker.setParameters({
55
- tessedit_char_whitelist:
56
- "0123456789X-年月日一二三四五六七八九十零壹贰叁肆伍陆柒捌玖拾ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz民族汉族满族回族维吾尔族藏族苗族彝族壮族朝鲜族侗族瑶族白族土家族哈尼族哈萨克族傣族黎族傈僳族佤族高山族拉祜族水族东乡族钠西族景颇族柯尔克孜族士族达斡尔族仫佬族羌族布朗族撒拉族毛南族仡佬族锡伯族阿昌族普米族塔吉克族怒族乌孜别克族俄罗斯族鄂温克族德昂族保安族裕固族京族塔塔尔族独龙族鄂伦春族赫哲族门巴族珞巴族基诺族男女性别住址出生公民身份号码签发机关有效期",
57
- })
58
-
59
- // 识别图像
60
- const { data } = await worker.recognize(input.imageBase64)
61
-
62
- // 解析识别结果
63
- const idCardInfo = parseIDCardText(data.text)
64
-
65
- // 处理完成后终止worker
66
- await worker.terminate()
67
-
68
- // 计算处理时间
69
- const processingTime = performance.now() - startTime
70
-
71
- // 返回处理结果
72
- return {
73
- idCardInfo,
74
- processingTime,
75
- }
76
- } catch (error) {
77
- // 确保资源被释放
78
- await worker.terminate()
79
- throw error
80
- }
81
- }
82
-
83
- /**
84
- * 解析身份证文本信息
85
- *
86
- * 从OCR识别到的文本中提取结构化的身份证信息
87
- *
88
- * @private
89
- * @param {string} text - OCR识别到的文本
90
- * @returns {IDCardInfo} 提取到的身份证信息对象
91
- */
92
- function parseIDCardText(text: string): IDCardInfo {
93
- const info: IDCardInfo = {}
94
-
95
- // 拆分为行
96
- const lines = text.split("\n").filter((line) => line.trim())
97
-
98
- // 解析身份证号码(最容易识别的部分)
99
- const idNumberRegex = /(\d{17}[\dX])/
100
- const idNumberMatch = text.match(idNumberRegex)
101
- if (idNumberMatch) {
102
- info.idNumber = idNumberMatch[1]
103
- }
104
-
105
- // 解析姓名
106
- for (const line of lines) {
107
- if (
108
- line.includes("姓名") ||
109
- (line.length < 10 && line.length > 1 && !/\d/.test(line))
110
- ) {
111
- info.name = line.replace("姓名", "").trim()
112
- break
113
- }
114
- }
115
-
116
- // 解析性别和民族
117
- const genderNationalityRegex = /(男|女).*(族)/
118
- const genderMatch = text.match(genderNationalityRegex)
119
- if (genderMatch) {
120
- info.gender = genderMatch[1]
121
- const nationalityText = genderMatch[0]
122
- info.nationality = nationalityText
123
- .substring(nationalityText.indexOf(genderMatch[1]) + 1)
124
- .trim()
125
- }
126
-
127
- // 解析出生日期
128
- const birthDateRegex = /(\d{4})年(\d{1,2})月(\d{1,2})日/
129
- const birthDateMatch = text.match(birthDateRegex)
130
- if (birthDateMatch) {
131
- info.birthDate = `${birthDateMatch[1]}-${birthDateMatch[2]}-${birthDateMatch[3]}`
132
- }
133
-
134
- // 解析地址
135
- const addressRegex = /住址([\s\S]*?)公民身份号码/
136
- const addressMatch = text.match(addressRegex)
137
- if (addressMatch) {
138
- info.address = addressMatch[1].replace(/\n/g, "").trim()
139
- }
140
-
141
- // 解析签发机关
142
- const authorityRegex = /签发机关([\s\S]*?)有效期/
143
- const authorityMatch = text.match(authorityRegex)
144
- if (authorityMatch) {
145
- info.issuingAuthority = authorityMatch[1].replace(/\n/g, "").trim()
146
- }
147
-
148
- // 解析有效期限
149
- const validPeriodRegex = /有效期限([\s\S]*?)(-|至)/
150
- const validPeriodMatch = text.match(validPeriodRegex)
151
- if (validPeriodMatch) {
152
- info.validPeriod = validPeriodMatch[0].replace("有效期限", "").trim()
153
- }
154
-
155
- return info
156
- }