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
@@ -0,0 +1,425 @@
1
+ /**
2
+ * @file 身份证模块入口
3
+ * @description 提供身份证识别和验证功能的模块入口
4
+ * @module modules/id-card
5
+ */
6
+
7
+ import { BaseModule } from '../../core/base-module';
8
+ import { IDCardDetector } from './id-card-detector';
9
+ import { OCRProcessor } from './ocr-processor';
10
+ import { AntiFakeDetector } from './anti-fake-detector';
11
+ import { IDCardInfo, IDCardType, IDCardModuleOptions, IDCardVerificationResult } from './types';
12
+
13
+ /**
14
+ * 身份证识别模块
15
+ * 提供身份证检测、OCR识别、防伪检测等功能
16
+ */
17
+ export class IDCardModule extends BaseModule {
18
+ /** 模块名称 */
19
+ public readonly name: string = 'id-card';
20
+
21
+ /** 模块配置 */
22
+ private options: IDCardModuleOptions;
23
+
24
+ /** 身份证检测器 */
25
+ private detector: IDCardDetector;
26
+
27
+ /** OCR处理器 */
28
+ private ocrProcessor?: OCRProcessor;
29
+
30
+ /** 防伪检测器 */
31
+ private antiFakeDetector?: AntiFakeDetector;
32
+
33
+ /** 最后一次检测结果 */
34
+ private lastDetectionResult?: IDCardInfo;
35
+
36
+ /**
37
+ * 构造函数
38
+ * @param options 模块配置选项
39
+ */
40
+ constructor(options: IDCardModuleOptions = {}) {
41
+ super();
42
+
43
+ this.options = {
44
+ enabled: true,
45
+ detector: {
46
+ minConfidence: 0.7,
47
+ enableOCR: true,
48
+ enableAntiFake: false,
49
+ ...options.detector
50
+ },
51
+ ocr: {
52
+ useWorker: true,
53
+ maxImageDimension: 1000,
54
+ brightness: 10,
55
+ contrast: 20,
56
+ ...options.ocr
57
+ },
58
+ antiFake: {
59
+ sensitivity: 0.8,
60
+ minConfidence: 0.7,
61
+ ...options.antiFake
62
+ },
63
+ ...options
64
+ };
65
+
66
+ // 创建检测器
67
+ this.detector = new IDCardDetector({
68
+ minConfidence: this.options.detector?.minConfidence,
69
+ enableEdgeDetection: true,
70
+ returnImage: true
71
+ });
72
+ }
73
+
74
+ /**
75
+ * 初始化模块
76
+ */
77
+ public async initialize(): Promise<void> {
78
+ if (this._isInitialized) {
79
+ return;
80
+ }
81
+
82
+ this.logger.debug(this.name, '初始化身份证模块');
83
+
84
+ try {
85
+ // 初始化检测器
86
+ await this.detector.initialize();
87
+
88
+ // 如果启用OCR,初始化OCR处理器
89
+ if (this.options.detector?.enableOCR) {
90
+ this.ocrProcessor = new OCRProcessor({
91
+ useWorker: this.options.ocr?.useWorker,
92
+ maxImageDimension: this.options.ocr?.maxImageDimension,
93
+ brightness: this.options.ocr?.brightness,
94
+ contrast: this.options.ocr?.contrast
95
+ });
96
+
97
+ await this.ocrProcessor.initialize();
98
+ }
99
+
100
+ // 如果启用防伪检测,初始化防伪检测器
101
+ if (this.options.detector?.enableAntiFake) {
102
+ this.antiFakeDetector = new AntiFakeDetector({
103
+ sensitivity: this.options.antiFake?.sensitivity
104
+ });
105
+
106
+ // AntiFakeDetector 不需要初始化
107
+ }
108
+
109
+ this._isInitialized = true;
110
+ this.emit('initialized');
111
+ this.logger.debug(this.name, '身份证模块初始化完成');
112
+ } catch (error) {
113
+ this.logger.error(this.name, '身份证模块初始化失败', error instanceof Error ? error : new Error(String(error)));
114
+ throw new Error(`身份证模块初始化失败: ${error instanceof Error ? error.message : String(error)}`);
115
+ }
116
+ }
117
+
118
+ /**
119
+ * 识别身份证图像
120
+ * @param image 图像源
121
+ * @returns 识别结果
122
+ */
123
+ public async recognize(
124
+ image: ImageData | HTMLImageElement | HTMLCanvasElement
125
+ ): Promise<IDCardInfo> {
126
+ this.ensureInitialized();
127
+
128
+ try {
129
+ // 检测身份证
130
+ const detectionResult = await this.detector.processImage(image);
131
+
132
+ if (!detectionResult.isSuccess() || !detectionResult.data) {
133
+ throw new Error('未检测到身份证');
134
+ }
135
+
136
+ // 创建结果对象
137
+ const idCardInfo: IDCardInfo = {
138
+ type: detectionResult.data.type || IDCardType.FRONT,
139
+ confidence: detectionResult.data.confidence
140
+ };
141
+
142
+ // 如果启用OCR且OCR处理器已初始化
143
+ if (this.options.detector?.enableOCR && this.ocrProcessor) {
144
+ // 裁剪并处理图像
145
+ const processedImage = detectionResult.data.image || this.convertToImageData(image);
146
+
147
+ // 识别文本信息
148
+ const ocrResult = await this.ocrProcessor.processIDCard(processedImage);
149
+
150
+ // 合并OCR结果
151
+ Object.assign(idCardInfo, ocrResult);
152
+ }
153
+
154
+ // 如果启用防伪检测且防伪检测器已初始化
155
+ if (this.options.detector?.enableAntiFake && this.antiFakeDetector) {
156
+ const processedImage = this.convertToImageData(image);
157
+ const antiFakeResult = await this.antiFakeDetector.detect(processedImage);
158
+
159
+ // 转换防伪检测结果格式
160
+ idCardInfo.antiFake = {
161
+ passed: antiFakeResult.isAuthentic,
162
+ score: antiFakeResult.confidence,
163
+ features: {
164
+ // 转换检测到的特征
165
+ fluorescent: antiFakeResult.detectedFeatures.includes('荧光油墨特征'),
166
+ microtext: antiFakeResult.detectedFeatures.includes('微缩文字'),
167
+ opticalVariable: antiFakeResult.detectedFeatures.includes('光变图案'),
168
+ texture: antiFakeResult.detectedFeatures.includes('雕刻凹印'),
169
+ watermark: antiFakeResult.detectedFeatures.includes('隐形图案')
170
+ }
171
+ };
172
+ }
173
+
174
+ // 保存最后一次检测结果
175
+ this.lastDetectionResult = idCardInfo;
176
+
177
+ // 触发事件
178
+ this.emit('recognized', { idCardInfo });
179
+
180
+ return idCardInfo;
181
+ } catch (error) {
182
+ this.logger.error(this.name, '身份证识别失败', error instanceof Error ? error : new Error(String(error)));
183
+ throw new Error(`身份证识别失败: ${error instanceof Error ? error.message : String(error)}`);
184
+ }
185
+ }
186
+
187
+ /**
188
+ * 验证身份证信息
189
+ * @param idCardInfo 身份证信息
190
+ * @returns 验证结果
191
+ */
192
+ public verify(idCardInfo: IDCardInfo): IDCardVerificationResult {
193
+ this.ensureInitialized();
194
+
195
+ const result: IDCardVerificationResult = {
196
+ isValid: true,
197
+ score: 1.0,
198
+ details: {
199
+ idNumberValid: true,
200
+ issueDateValid: true,
201
+ isExpired: false,
202
+ antiFakePassed: true
203
+ }
204
+ };
205
+
206
+ // 验证身份证号码
207
+ if (idCardInfo.idNumber) {
208
+ result.details!.idNumberValid = this.validateIDNumber(idCardInfo.idNumber);
209
+ if (!result.details!.idNumberValid) {
210
+ result.isValid = false;
211
+ result.score -= 0.3;
212
+ result.failureReason = '身份证号码无效';
213
+ }
214
+ }
215
+
216
+ // 验证有效期
217
+ if (idCardInfo.validTo) {
218
+ result.details!.isExpired = this.isIDCardExpired(idCardInfo.validTo);
219
+ if (result.details!.isExpired) {
220
+ result.isValid = false;
221
+ result.score -= 0.2;
222
+ result.failureReason = '身份证已过期';
223
+ }
224
+ }
225
+
226
+ // 验证防伪结果
227
+ if (idCardInfo.antiFake) {
228
+ result.details!.antiFakePassed = idCardInfo.antiFake.passed;
229
+ if (!result.details!.antiFakePassed) {
230
+ result.isValid = false;
231
+ result.score -= 0.5;
232
+ result.failureReason = '防伪检测未通过';
233
+ }
234
+ }
235
+
236
+ return result;
237
+ }
238
+
239
+ /**
240
+ * 获取最后一次识别结果
241
+ */
242
+ public getLastRecognitionResult(): IDCardInfo | undefined {
243
+ return this.lastDetectionResult;
244
+ }
245
+
246
+ /**
247
+ * 释放模块资源
248
+ */
249
+ public async dispose(): Promise<void> {
250
+ if (!this._isInitialized) {
251
+ return;
252
+ }
253
+
254
+ this.logger.debug(this.name, '释放身份证模块资源');
255
+
256
+ try {
257
+ // 释放检测器资源
258
+ await this.detector.dispose();
259
+
260
+ // 释放OCR处理器资源
261
+ if (this.ocrProcessor) {
262
+ await this.ocrProcessor.terminate();
263
+ }
264
+
265
+ // 释放防伪检测器资源
266
+ if (this.antiFakeDetector) {
267
+ await this.antiFakeDetector.dispose();
268
+ }
269
+
270
+ // 调用基类的dispose方法
271
+ await super.dispose();
272
+ } catch (error) {
273
+ this.logger.error(this.name, '身份证模块资源释放失败', error instanceof Error ? error : new Error(String(error)));
274
+ throw new Error(`身份证模块资源释放失败: ${error instanceof Error ? error.message : String(error)}`);
275
+ }
276
+ }
277
+
278
+ /**
279
+ * 验证身份证号码是否有效
280
+ * @param idNumber 身份证号码
281
+ * @returns 是否有效
282
+ */
283
+ private validateIDNumber(idNumber: string): boolean {
284
+ // 基本格式验证
285
+ if (!idNumber || !/^\d{17}[\dX]$/.test(idNumber)) {
286
+ return false;
287
+ }
288
+
289
+ // 验证出生日期
290
+ const year = parseInt(idNumber.substring(6, 10));
291
+ const month = parseInt(idNumber.substring(10, 12));
292
+ const day = parseInt(idNumber.substring(12, 14));
293
+
294
+ if (year < 1900 || year > new Date().getFullYear() ||
295
+ month < 1 || month > 12 ||
296
+ day < 1 || day > 31) {
297
+ return false;
298
+ }
299
+
300
+ // 验证校验位
301
+ const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
302
+ const validationCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
303
+
304
+ let sum = 0;
305
+ for (let i = 0; i < 17; i++) {
306
+ sum += parseInt(idNumber[i]) * weights[i];
307
+ }
308
+
309
+ const validationCode = validationCodes[sum % 11];
310
+ return validationCode === idNumber[17].toUpperCase();
311
+ }
312
+
313
+ /**
314
+ * 检查身份证是否过期
315
+ * @param validTo 有效期截止日期
316
+ * @returns 是否过期
317
+ */
318
+ private isIDCardExpired(validTo: string): boolean {
319
+ // 如果是"长期",则视为未过期
320
+ if (validTo === '长期' || validTo === '长期有效') {
321
+ return false;
322
+ }
323
+
324
+ try {
325
+ // 解析日期字符串
326
+ const parts = validTo.split('-');
327
+ if (parts.length !== 3) {
328
+ return true; // 格式错误,视为过期
329
+ }
330
+
331
+ const year = parseInt(parts[0]);
332
+ const month = parseInt(parts[1]) - 1; // 月份从0开始
333
+ const day = parseInt(parts[2]);
334
+
335
+ const expiryDate = new Date(year, month, day);
336
+ const today = new Date();
337
+
338
+ // 设置时间为当天结束
339
+ today.setHours(0, 0, 0, 0);
340
+
341
+ return expiryDate < today;
342
+ } catch {
343
+ return true; // 解析错误,视为过期
344
+ }
345
+ }
346
+
347
+ /**
348
+ * 检测身份证
349
+ * @param image 图像源
350
+ * @returns 检测结果
351
+ */
352
+ public async detect(
353
+ image: ImageData | HTMLImageElement | HTMLCanvasElement
354
+ ): Promise<{
355
+ success: boolean;
356
+ type?: IDCardType;
357
+ confidence: number;
358
+ croppedImage?: ImageData;
359
+ }> {
360
+ this.ensureInitialized();
361
+
362
+ try {
363
+ // 调用检测器处理图像
364
+ const result = await this.detector.processImage(image);
365
+
366
+ if (!result.isSuccess() || !result.data) {
367
+ return { success: false, confidence: 0 };
368
+ }
369
+
370
+ return {
371
+ success: true,
372
+ type: result.data.type,
373
+ confidence: result.data.confidence || 0,
374
+ croppedImage: result.data.image
375
+ };
376
+ } catch (error) {
377
+ this.logger.error(this.name, '身份证检测失败', error instanceof Error ? error : new Error(String(error)));
378
+ return { success: false, confidence: 0 };
379
+ }
380
+ }
381
+
382
+ /**
383
+ * 将图像转换为 ImageData
384
+ * @param image 图像源
385
+ * @returns ImageData 对象
386
+ */
387
+ private convertToImageData(image: ImageData | HTMLImageElement | HTMLCanvasElement): ImageData {
388
+ // 如果已经是 ImageData,直接返回
389
+ if (image instanceof ImageData) {
390
+ return image;
391
+ }
392
+
393
+ // 创建 Canvas 用于转换
394
+ const canvas = document.createElement('canvas');
395
+ const width = image instanceof HTMLImageElement ? image.naturalWidth : image.width;
396
+ const height = image instanceof HTMLImageElement ? image.naturalHeight : image.height;
397
+
398
+ canvas.width = width;
399
+ canvas.height = height;
400
+
401
+ const ctx = canvas.getContext('2d');
402
+ if (!ctx) {
403
+ throw new Error('无法创建 Canvas 上下文');
404
+ }
405
+
406
+ // 绘制图像到 Canvas
407
+ ctx.drawImage(image, 0, 0);
408
+
409
+ // 返回 ImageData
410
+ return ctx.getImageData(0, 0, width, height);
411
+ }
412
+
413
+ /**
414
+ * 确保模块已初始化
415
+ * @protected
416
+ */
417
+ protected ensureInitialized(): void {
418
+ if (!this._isInitialized) {
419
+ throw new Error('身份证模块尚未初始化');
420
+ }
421
+ }
422
+ }
423
+
424
+ // 导出类型
425
+ export * from './types';