id-scanner-lib 1.5.0 → 1.6.2

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 (72) hide show
  1. package/README.md +378 -59
  2. package/dist/id-scanner-lib.esm.js +195 -10
  3. package/dist/id-scanner-lib.esm.js.map +1 -1
  4. package/dist/id-scanner-lib.js +4812 -14709
  5. package/dist/id-scanner-lib.js.map +1 -1
  6. package/dist/types/browser-image-compression.d.ts +19 -0
  7. package/dist/types/tesseract.d.ts +280 -0
  8. package/package.json +21 -11
  9. package/src/core/camera-manager.ts +16 -1
  10. package/src/core/config.ts +37 -0
  11. package/src/core/errors.ts +3 -3
  12. package/src/core/event-emitter.test.ts +42 -0
  13. package/src/core/loading-state.test.ts +67 -0
  14. package/src/core/loading-state.ts +156 -0
  15. package/src/core/logger.test.ts +49 -0
  16. package/src/core/module-manager.ts +2 -4
  17. package/src/core/scanner-factory.ts +8 -9
  18. package/src/index.ts +3 -2
  19. package/src/modules/face/face-detector.ts +123 -66
  20. package/src/modules/id-card/anti-fake-detector.ts +2 -2
  21. package/src/modules/id-card/ocr-worker.ts +1 -1
  22. package/src/modules/qrcode/qr-code-scanner.ts +2 -1
  23. package/src/modules/qrcode/types.ts +111 -7
  24. package/src/types/common.test.ts +99 -0
  25. package/src/types/common.ts +166 -0
  26. package/src/utils/camera.test.ts +30 -0
  27. package/src/utils/camera.ts +4 -1
  28. package/src/utils/error-handler.test.ts +137 -0
  29. package/src/utils/error-handler.ts +110 -0
  30. package/src/utils/index.test.ts +186 -0
  31. package/src/utils/index.ts +3 -0
  32. package/src/utils/retry.test.ts +142 -0
  33. package/src/utils/retry.ts +282 -0
  34. package/src/utils/utils.test.ts +171 -0
  35. package/src/version.ts +1 -1
  36. package/dist/types/core/base-module.d.ts +0 -44
  37. package/dist/types/core/camera-manager.d.ts +0 -258
  38. package/dist/types/core/config.d.ts +0 -88
  39. package/dist/types/core/errors.d.ts +0 -111
  40. package/dist/types/core/event-emitter.d.ts +0 -55
  41. package/dist/types/core/logger.d.ts +0 -277
  42. package/dist/types/core/module-manager.d.ts +0 -78
  43. package/dist/types/core/plugin-manager.d.ts +0 -158
  44. package/dist/types/core/resource-manager.d.ts +0 -246
  45. package/dist/types/core/result.d.ts +0 -83
  46. package/dist/types/core/scanner-factory.d.ts +0 -93
  47. package/dist/types/index.bundle.d.ts +0 -1303
  48. package/dist/types/index.d.ts +0 -86
  49. package/dist/types/interfaces/external-types.d.ts +0 -174
  50. package/dist/types/interfaces/face-detection.d.ts +0 -293
  51. package/dist/types/interfaces/scanner-module.d.ts +0 -280
  52. package/dist/types/modules/face/face-detector.d.ts +0 -170
  53. package/dist/types/modules/face/index.d.ts +0 -56
  54. package/dist/types/modules/face/liveness-detector.d.ts +0 -177
  55. package/dist/types/modules/face/types.d.ts +0 -136
  56. package/dist/types/modules/id-card/anti-fake-detector.d.ts +0 -170
  57. package/dist/types/modules/id-card/id-card-detector.d.ts +0 -131
  58. package/dist/types/modules/id-card/index.d.ts +0 -89
  59. package/dist/types/modules/id-card/ocr-processor.d.ts +0 -110
  60. package/dist/types/modules/id-card/ocr-worker.d.ts +0 -31
  61. package/dist/types/modules/id-card/types.d.ts +0 -181
  62. package/dist/types/modules/qrcode/index.d.ts +0 -51
  63. package/dist/types/modules/qrcode/qr-code-scanner.d.ts +0 -64
  64. package/dist/types/modules/qrcode/types.d.ts +0 -67
  65. package/dist/types/utils/camera.d.ts +0 -81
  66. package/dist/types/utils/image-processing.d.ts +0 -176
  67. package/dist/types/utils/index.d.ts +0 -175
  68. package/dist/types/utils/performance.d.ts +0 -81
  69. package/dist/types/utils/resource-manager.d.ts +0 -53
  70. package/dist/types/utils/types.d.ts +0 -166
  71. package/dist/types/utils/worker.d.ts +0 -52
  72. package/dist/types/version.d.ts +0 -7
@@ -0,0 +1,156 @@
1
+ /**
2
+ * @file 加载状态管理
3
+ * @description 提供模块加载状态跟踪功能
4
+ * @module core/loading-state
5
+ */
6
+
7
+ import { EventEmitter } from './event-emitter';
8
+
9
+ /**
10
+ * 加载状态类型
11
+ */
12
+ export enum LoadingState {
13
+ /** 空闲 */
14
+ IDLE = 'idle',
15
+ /** 加载中 */
16
+ LOADING = 'loading',
17
+ /** 就绪 */
18
+ READY = 'ready',
19
+ /** 错误 */
20
+ ERROR = 'error',
21
+ /** 已释放 */
22
+ DISPOSED = 'disposed'
23
+ }
24
+
25
+ /**
26
+ * 加载进度信息
27
+ */
28
+ export interface LoadingProgress {
29
+ /** 当前状态 */
30
+ state: LoadingState;
31
+ /** 已加载的模型 */
32
+ loadedModels: string[];
33
+ /** 正在加载的模型 */
34
+ loadingModel?: string;
35
+ /** 进度百分比 (0-100) */
36
+ progress: number;
37
+ /** 错误信息 */
38
+ error?: string;
39
+ }
40
+
41
+ /**
42
+ * 加载状态管理器
43
+ * 用于跟踪和管理模块的加载状态
44
+ */
45
+ export class LoadingStateManager extends EventEmitter {
46
+ private state: LoadingState = LoadingState.IDLE;
47
+ private loadedModels: Set<string> = new Set();
48
+ private loadingModel?: string;
49
+ private totalModels: number = 0;
50
+ private error?: string;
51
+
52
+ /**
53
+ * 开始加载
54
+ * @param totalModels 总模型数
55
+ */
56
+ startLoading(totalModels: number): void {
57
+ this.totalModels = totalModels;
58
+ this.state = LoadingState.LOADING;
59
+ this.error = undefined;
60
+ this.emit('stateChange', this.getProgress());
61
+ }
62
+
63
+ /**
64
+ * 模型开始加载
65
+ * @param modelName 模型名称
66
+ */
67
+ startModelLoading(modelName: string): void {
68
+ this.loadingModel = modelName;
69
+ this.emit('progress', this.getProgress());
70
+ }
71
+
72
+ /**
73
+ * 模型加载完成
74
+ * @param modelName 模型名称
75
+ */
76
+ completeModelLoading(modelName: string): void {
77
+ this.loadedModels.add(modelName);
78
+ this.loadingModel = undefined;
79
+ this.emit('progress', this.getProgress());
80
+ }
81
+
82
+ /**
83
+ * 加载完成
84
+ */
85
+ complete(): void {
86
+ this.state = LoadingState.READY;
87
+ this.totalModels = 0;
88
+ this.emit('stateChange', this.getProgress());
89
+ }
90
+
91
+ /**
92
+ * 加载失败
93
+ * @param error 错误信息
94
+ */
95
+ fail(error: string): void {
96
+ this.state = LoadingState.ERROR;
97
+ this.error = error;
98
+ this.emit('error', { error, progress: this.getProgress() });
99
+ }
100
+
101
+ /**
102
+ * 释放
103
+ */
104
+ dispose(): void {
105
+ this.state = LoadingState.DISPOSED;
106
+ this.loadedModels.clear();
107
+ this.totalModels = 0;
108
+ this.error = undefined;
109
+ this.emit('stateChange', this.getProgress());
110
+ }
111
+
112
+ /**
113
+ * 获取当前进度
114
+ */
115
+ getProgress(): LoadingProgress {
116
+ const progress = this.totalModels > 0
117
+ ? Math.round((this.loadedModels.size / this.totalModels) * 100)
118
+ : 0;
119
+
120
+ return {
121
+ state: this.state,
122
+ loadedModels: Array.from(this.loadedModels),
123
+ loadingModel: this.loadingModel,
124
+ progress,
125
+ error: this.error
126
+ };
127
+ }
128
+
129
+ /**
130
+ * 获取当前状态
131
+ */
132
+ getState(): LoadingState {
133
+ return this.state;
134
+ }
135
+
136
+ /**
137
+ * 是否已就绪
138
+ */
139
+ isReady(): boolean {
140
+ return this.state === LoadingState.READY;
141
+ }
142
+
143
+ /**
144
+ * 是否有错误
145
+ */
146
+ hasError(): boolean {
147
+ return this.state === LoadingState.ERROR;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * 创建加载状态管理器
153
+ */
154
+ export function createLoadingStateManager(): LoadingStateManager {
155
+ return new LoadingStateManager();
156
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @file Logger 测试
3
+ * @description 测试日志系统
4
+ */
5
+
6
+ import { Logger, LogLevel } from './logger';
7
+
8
+ describe('Logger', () => {
9
+ let logger: Logger;
10
+
11
+ beforeEach(() => {
12
+ logger = Logger.getInstance();
13
+ });
14
+
15
+ it('should get singleton instance', () => {
16
+ const logger2 = Logger.getInstance();
17
+ expect(logger).toBe(logger2);
18
+ });
19
+
20
+ it('should log debug message', () => {
21
+ expect(() => {
22
+ logger.debug('Test', 'Debug message');
23
+ }).not.toThrow();
24
+ });
25
+
26
+ it('should log info message', () => {
27
+ expect(() => {
28
+ logger.info('Test', 'Info message');
29
+ }).not.toThrow();
30
+ });
31
+
32
+ it('should log warn message', () => {
33
+ expect(() => {
34
+ logger.warn('Test', 'Warning message');
35
+ }).not.toThrow();
36
+ });
37
+
38
+ it('should log error message', () => {
39
+ expect(() => {
40
+ logger.error('Test', 'Error message', new Error('test error'));
41
+ }).not.toThrow();
42
+ });
43
+
44
+ it('should set log level', () => {
45
+ expect(() => {
46
+ logger.setLevel(LogLevel.WARN);
47
+ }).not.toThrow();
48
+ });
49
+ });
@@ -112,8 +112,7 @@ export class ModuleManager extends EventEmitter {
112
112
  this.emit('module:initialized', { name });
113
113
  this.logger.debug('ModuleManager', `模块 ${name} 初始化完成`);
114
114
  } catch (error) {
115
- const errorObj = error instanceof Error ? error : new Error(String(error));
116
- this.logger.error('ModuleManager', `模块 ${name} 初始化失败`, errorObj);
115
+ this.logger.error('ModuleManager', `模块 ${name} 初始化失败`, error instanceof Error ? error : undefined);
117
116
  this.emit('module:error', { name, error });
118
117
  throw new Error(`模块 ${name} 初始化失败: ${error instanceof Error ? error.message : String(error)}`);
119
118
  }
@@ -136,8 +135,7 @@ export class ModuleManager extends EventEmitter {
136
135
  await module.dispose();
137
136
  this.emit('module:disposed', { name });
138
137
  } catch (error) {
139
- const errorObj = error instanceof Error ? error : new Error(String(error));
140
- this.logger.error('ModuleManager', `模块 ${name} 资源释放失败`, errorObj);
138
+ this.logger.error('ModuleManager', `模块 ${name} 资源释放失败`, error instanceof Error ? error : undefined);
141
139
  this.emit('module:error', { name, error });
142
140
  }
143
141
  }
@@ -4,19 +4,18 @@
4
4
  * @module core/scanner-factory
5
5
  */
6
6
 
7
- import { ConfigManager } from './config';
7
+ import { ConfigManager, GlobalConfig } from './config';
8
8
  import { Logger } from './logger';
9
9
  import { ResourceManager } from './resource-manager';
10
10
  import { EventEmitter } from './event-emitter';
11
11
  import { InitializationError } from './errors';
12
- import { VERSION } from '../version';
13
12
 
14
13
  /**
15
14
  * 扫描器初始化选项
16
15
  */
17
16
  export interface ScannerFactoryOptions {
18
17
  /** 配置选项 */
19
- config?: Record<string, any>;
18
+ config?: Partial<GlobalConfig>;
20
19
  /** 资源基础路径 */
21
20
  resourceBasePath?: string;
22
21
  /** 调试模式 */
@@ -102,7 +101,7 @@ export class ScannerFactory extends EventEmitter {
102
101
  }
103
102
 
104
103
  // 记录初始化日志
105
- this.logger.info('ScannerFactory', `Initializing ID Scanner Library v${VERSION}`);
104
+ this.logger.info('ScannerFactory', `Initializing ID Scanner Library v1.4.0, debug: ${this.config.get('debug', false)}`);
106
105
 
107
106
  // 如果启用了自动初始化模块,则加载相应模块
108
107
  if (autoInitModules) {
@@ -120,7 +119,7 @@ export class ScannerFactory extends EventEmitter {
120
119
  this.initializing = false;
121
120
 
122
121
  const errorMessage = error instanceof Error ? error.message : String(error);
123
- this.logger.error('ScannerFactory', `Initialization failed: ${errorMessage}`, error instanceof Error ? error : new Error(errorMessage));
122
+ this.logger.error('ScannerFactory', `Initialization failed: ${errorMessage}`, error instanceof Error ? error : undefined);
124
123
 
125
124
  this.emit('initialized', { success: false, error });
126
125
 
@@ -138,19 +137,19 @@ export class ScannerFactory extends EventEmitter {
138
137
  const enabledModules = [];
139
138
 
140
139
  // 检查每个模块的启用状态
141
- if (this.config.get('modules.face.enabled', false)) {
140
+ if (this.config.isModuleEnabled('face')) {
142
141
  enabledModules.push(this.initFaceModule());
143
142
  }
144
143
 
145
- if (this.config.get('modules.qr.enabled', false)) {
144
+ if (this.config.isModuleEnabled('qr')) {
146
145
  enabledModules.push(this.initQRModule());
147
146
  }
148
147
 
149
- if (this.config.get('modules.idcard.enabled', false)) {
148
+ if (this.config.isModuleEnabled('idcard')) {
150
149
  enabledModules.push(this.initIDCardModule());
151
150
  }
152
151
 
153
- if (this.config.get('modules.ocr.enabled', false)) {
152
+ if (this.config.isModuleEnabled('ocr')) {
154
153
  enabledModules.push(this.initOCRModule());
155
154
  }
156
155
 
package/src/index.ts CHANGED
@@ -130,7 +130,7 @@ export class IDScanner {
130
130
  public async dispose(): Promise<void> {
131
131
  if (!this.initialized) {
132
132
  return;
133
- }
133
+ }
134
134
 
135
135
  this.logger.info('IDScanner', '释放IDScanner资源');
136
136
 
@@ -147,6 +147,7 @@ export class IDScanner {
147
147
  }
148
148
 
149
149
  // 导出核心模块
150
+ export * from './core/loading-state';
150
151
  export * from './core/module-manager';
151
152
  export * from './core/logger';
152
153
  export * from './core/errors';
@@ -159,7 +160,7 @@ export { FaceModule } from './modules/face';
159
160
  // 导出类型
160
161
  export * from './utils/types';
161
162
  export { IDCardModuleOptions, IDCardInfo, IDCardType, IDCardVerificationResult } from './modules/id-card/types';
162
- export { QRCodeModuleOptions, QRCodeResult } from './modules/qrcode/types';
163
+ export { QRCodeModuleOptions, QRCodeResult, BarcodeFormat, DEFAULT_FORMATS } from './modules/qrcode/types';
163
164
  export { FaceModuleOptions, FaceDetectionResult, FaceComparisonResult } from './modules/face/types';
164
165
 
165
166
  // 默认导出IDScanner类
@@ -31,6 +31,21 @@ export enum FaceModelType {
31
31
  BLAZE_FACE = 'blazeface'
32
32
  }
33
33
 
34
+ /**
35
+ * 68点人脸关键点索引 (face-api 68-point model)
36
+ * 参考: https://github.com/justadudewhohacks/face-api.js/issues/175
37
+ */
38
+ const FaceLandmarkIndex = {
39
+ LEFT_EYE: 36,
40
+ RIGHT_EYE: 45,
41
+ NOSE: 30,
42
+ MOUTH: 48, // 上唇中心
43
+ LEFT_EYE_CORNER: 36,
44
+ RIGHT_EYE_CORNER: 45,
45
+ NOSE_TIP: 30,
46
+ MOUTH_CENTER: 57,
47
+ } as const;
48
+
34
49
  /**
35
50
  * 人脸检测模块配置
36
51
  */
@@ -92,6 +107,7 @@ export class FaceDetector extends BaseScannerModule {
92
107
 
93
108
  /** 模型加载状态 */
94
109
  private modelsLoaded: boolean = false;
110
+ private loadedModels: Set<string> = new Set();
95
111
 
96
112
  /** 处理计时器ID */
97
113
  private processingTimerId: number | null = null;
@@ -236,59 +252,97 @@ export class FaceDetector extends BaseScannerModule {
236
252
  }
237
253
 
238
254
  /**
239
- * 加载人脸检测模型
255
+ * 懒加载模型 - 仅在需要时加载特定模型
256
+ * @param modelType 模型类型
240
257
  * @param modelPath 模型路径
241
258
  */
242
- private async loadModels(modelPath: string): Promise<void> {
259
+ private async lazyLoadModel(modelType: string, modelPath: string): Promise<void> {
260
+ // 检查模型是否已加载
261
+ const loadedModels = this.loadedModels || new Set();
262
+ if (loadedModels.has(modelType)) {
263
+ return;
264
+ }
265
+
266
+ this.logger.info('FaceDetector', `懒加载模型: ${modelType}`);
267
+
243
268
  try {
244
- // 设置模型路径
245
- faceapi.nets.ssdMobilenetv1.isLoaded && faceapi.nets.ssdMobilenetv1.dispose();
246
-
247
- // 根据配置加载检测模型
248
- switch (this.config.detectionModel) {
249
- case FaceModelType.SSD_MOBILENET:
269
+ switch (modelType) {
270
+ case 'ssdMobilenetv1':
250
271
  await faceapi.nets.ssdMobilenetv1.loadFromUri(modelPath);
251
272
  break;
252
- case FaceModelType.TINY_FACE:
273
+ case 'tinyFaceDetector':
253
274
  await faceapi.nets.tinyFaceDetector.loadFromUri(modelPath);
254
275
  break;
255
- case FaceModelType.MTCNN:
256
- await faceapi.nets.mtcnn.loadFromUri(modelPath);
257
- break;
258
- default:
259
- await faceapi.nets.ssdMobilenetv1.loadFromUri(modelPath);
260
- }
261
-
262
- // 加载关键点检测模型
263
- if (this.config.detectLandmarks) {
264
- if (this.config.landmarksModel === '68_points') {
276
+ case 'faceLandmark68Net':
265
277
  await faceapi.nets.faceLandmark68Net.loadFromUri(modelPath);
266
- } else {
278
+ break;
279
+ case 'faceLandmark68TinyNet':
267
280
  await faceapi.nets.faceLandmark68TinyNet.loadFromUri(modelPath);
268
- }
269
- }
270
-
271
- // 加载表情识别模型
272
- if (this.config.detectExpressions) {
273
- await faceapi.nets.faceExpressionNet.loadFromUri(modelPath);
274
- }
275
-
276
- // 加载年龄性别识别模型
277
- if (this.config.detectAgeGender) {
278
- await faceapi.nets.ageGenderNet.loadFromUri(modelPath);
279
- }
280
-
281
- // 加载人脸识别模型
282
- if (this.config.extractEmbeddings) {
283
- await faceapi.nets.faceRecognitionNet.loadFromUri(modelPath);
281
+ break;
282
+ case 'faceExpressionNet':
283
+ await faceapi.nets.faceExpressionNet.loadFromUri(modelPath);
284
+ break;
285
+ case 'ageGenderNet':
286
+ await faceapi.nets.ageGenderNet.loadFromUri(modelPath);
287
+ break;
288
+ case 'faceRecognitionNet':
289
+ await faceapi.nets.faceRecognitionNet.loadFromUri(modelPath);
290
+ break;
291
+ default:
292
+ this.logger.warn('FaceDetector', `未知模型类型: ${modelType}`);
293
+ return;
284
294
  }
285
-
286
- this.modelsLoaded = true;
287
- this.logger.info('FaceDetector', '所有人脸检测模型加载完成');
295
+
296
+ loadedModels.add(modelType);
297
+ this.loadedModels = loadedModels;
298
+ this.logger.info('FaceDetector', `模型加载完成: ${modelType}`);
288
299
  } catch (error) {
289
- this.logger.error('FaceDetector', `模型加载失败: ${error}`);
290
- throw new ResourceLoadError('face-api-models', `模型加载失败: ${error}`);
300
+ this.logger.error('FaceDetector', `模型加载失败: ${modelType}`, error instanceof Error ? error : undefined);
301
+ throw new ResourceLoadError(modelType, `模型加载失败: ${error}`);
302
+ }
303
+ }
304
+
305
+ /**
306
+ * 根据需求加载模型
307
+ * @param options 检测选项
308
+ * @param modelPath 模型路径
309
+ */
310
+ private async loadModelsOnDemand(options: FaceDetectionOptions, modelPath: string): Promise<void> {
311
+ // 基础检测模型
312
+ await this.lazyLoadModel(this.config.detectionModel, modelPath);
313
+
314
+ // 关键点模型
315
+ if (options.withLandmarks || this.config.detectLandmarks) {
316
+ await this.lazyLoadModel(
317
+ this.config.landmarksModel === '68_points' ? 'faceLandmark68Net' : 'faceLandmark68TinyNet',
318
+ modelPath
319
+ );
320
+ }
321
+
322
+ // 表情模型
323
+ if (options.withExpressions || this.config.detectExpressions) {
324
+ await this.lazyLoadModel('faceExpressionNet', modelPath);
325
+ }
326
+
327
+ // 年龄性别模型
328
+ if (options.withAgeAndGender || this.config.detectAgeGender) {
329
+ await this.lazyLoadModel('ageGenderNet', modelPath);
330
+ }
331
+
332
+ // 人脸识别模型
333
+ if (options.withEmbedding || this.config.extractEmbeddings) {
334
+ await this.lazyLoadModel('faceRecognitionNet', modelPath);
291
335
  }
336
+
337
+ this.modelsLoaded = true;
338
+ }
339
+
340
+ /**
341
+ * 加载人脸检测模型 (旧版 - 保留兼容性)
342
+ * @param modelPath 模型路径
343
+ */
344
+ private async loadModels(modelPath: string): Promise<void> {
345
+ await this.loadModelsOnDemand({}, modelPath);
292
346
  }
293
347
 
294
348
  /**
@@ -308,8 +362,12 @@ export class FaceDetector extends BaseScannerModule {
308
362
 
309
363
  this.setStatus(ModuleStatus.PROCESSING);
310
364
  this.emit(ModuleEvent.PROCESS_START);
311
-
365
+
312
366
  try {
367
+ // 懒加载所需的模型
368
+ const modelPath = this.config.modelPath || '/models';
369
+ await this.loadModelsOnDemand(options, modelPath);
370
+
313
371
  // 合并选项和配置
314
372
  const processOptions: FaceDetectionOptions = {
315
373
  minConfidence: this.config.minConfidence,
@@ -638,15 +696,16 @@ export class FaceDetector extends BaseScannerModule {
638
696
 
639
697
  // 限制检测数量
640
698
  const maxFaces = detectOptions.maxFaces || this.config.maxFaces;
641
- if ((detections as any).length > maxFaces) {
642
- detections = (detections as any).slice(0, maxFaces);
699
+ const detectionsArray: any[] = Array.isArray(detections) ? detections : [detections];
700
+ if (detectionsArray.length > maxFaces) {
701
+ detectionsArray.length = maxFaces;
643
702
  }
644
703
 
645
704
  // 将结果转换为标准格式
646
705
  const results: FaceDetectionResult[] = [];
647
706
  const processingTime = Date.now() - startTime;
648
707
 
649
- for (const detection of detections) {
708
+ for (const detection of detectionsArray) {
650
709
  const boundingBox: Rect = {
651
710
  x: detection.detection?.box.x || 0,
652
711
  y: detection.detection?.box.y || 0,
@@ -667,27 +726,23 @@ export class FaceDetector extends BaseScannerModule {
667
726
  // 添加关键点
668
727
  if (detection.landmarks) {
669
728
  const positions = detection.landmarks.positions;
670
- const leftEyeIdx = 36; // 面部68点模型中左眼的索引
671
- const rightEyeIdx = 45; // 面部68点模型中右眼的索引
672
- const noseIdx = 30; // 鼻尖
673
- const mouthIdx = 57; // 嘴巴中心
674
729
 
675
730
  result.landmarks = {
676
731
  leftEye: {
677
- x: positions[leftEyeIdx].x,
678
- y: positions[leftEyeIdx].y
732
+ x: positions[FaceLandmarkIndex.LEFT_EYE].x,
733
+ y: positions[FaceLandmarkIndex.LEFT_EYE].y
679
734
  },
680
735
  rightEye: {
681
- x: positions[rightEyeIdx].x,
682
- y: positions[rightEyeIdx].y
736
+ x: positions[FaceLandmarkIndex.RIGHT_EYE].x,
737
+ y: positions[FaceLandmarkIndex.RIGHT_EYE].y
683
738
  },
684
739
  nose: {
685
- x: positions[noseIdx].x,
686
- y: positions[noseIdx].y
740
+ x: positions[FaceLandmarkIndex.NOSE].x,
741
+ y: positions[FaceLandmarkIndex.NOSE].y
687
742
  },
688
743
  mouth: {
689
- x: positions[mouthIdx].x,
690
- y: positions[mouthIdx].y
744
+ x: positions[FaceLandmarkIndex.MOUTH_CENTER].x,
745
+ y: positions[FaceLandmarkIndex.MOUTH_CENTER].y
691
746
  },
692
747
  points: positions.map((p: { x: any; y: any; }) => ({ x: p.x, y: p.y }))
693
748
  };
@@ -695,16 +750,18 @@ export class FaceDetector extends BaseScannerModule {
695
750
 
696
751
  // 添加表情属性
697
752
  if (detection.expressions) {
753
+ const emotion = {
754
+ angry: detection.expressions.angry,
755
+ disgust: detection.expressions.disgusted,
756
+ fear: detection.expressions.fearful,
757
+ happy: detection.expressions.happy,
758
+ neutral: detection.expressions.neutral,
759
+ sad: detection.expressions.sad,
760
+ surprise: detection.expressions.surprised
761
+ };
698
762
  result.attributes = {
699
- emotion: {
700
- angry: detection.expressions.angry,
701
- disgust: detection.expressions.disgusted,
702
- fear: detection.expressions.fearful,
703
- happy: detection.expressions.happy,
704
- neutral: detection.expressions.neutral,
705
- sad: detection.expressions.sad,
706
- surprise: detection.expressions.surprised
707
- }
763
+ ...result.attributes,
764
+ emotion
708
765
  };
709
766
  }
710
767
 
@@ -136,7 +136,7 @@ export class AntiFakeDetector implements Disposable {
136
136
  detectedFeatures.length >= 2
137
137
 
138
138
  // 生成结果消息
139
- let message = isAuthentic
139
+ const message = isAuthentic
140
140
  ? `身份证真实,检测到${detectedFeatures.length}个防伪特征`
141
141
  : detectedFeatures.length > 0
142
142
  ? `可疑身份证,仅检测到${detectedFeatures.length}个防伪特征,置信度不足`
@@ -456,7 +456,7 @@ export class AntiFakeDetector implements Disposable {
456
456
  } {
457
457
  const { data, width, height } = edgeData
458
458
  let edgeCount = 0
459
- let totalPixels = width * height
459
+ const totalPixels = width * height
460
460
 
461
461
  // 计算边缘像素的数量
462
462
  for (let i = 0; i < data.length; i += 4) {
@@ -149,7 +149,7 @@ function parseIDCardText(text: string): IDCardInfo {
149
149
  const birthDateRegex2 = /出生[\s\:]*(\d{4})[-\/\.](\d{1,2})[-\/\.](\d{1,2})/;
150
150
  const birthDateRegex3 = /出生日期[\s\:]*(\d{4})[-\/\.\u5e74](\d{1,2})[-\/\.\u6708](\d{1,2})[日号]?/;
151
151
 
152
- let birthDateMatch =
152
+ const birthDateMatch =
153
153
  processedText.match(birthDateRegex1) ||
154
154
  processedText.match(birthDateRegex2) ||
155
155
  processedText.match(birthDateRegex3);
@@ -6,7 +6,7 @@
6
6
 
7
7
  import { EventEmitter } from '../../core/event-emitter';
8
8
  import { Logger } from '../../core/logger';
9
- import { QRCodeResult } from './types';
9
+ import { QRCodeResult, BarcodeFormat } from './types';
10
10
  import jsQR from 'jsqr';
11
11
 
12
12
  /**
@@ -112,6 +112,7 @@ export class QRCodeScanner extends EventEmitter {
112
112
  // 构建结果
113
113
  const result: QRCodeResult = {
114
114
  data: code.data,
115
+ barcodeFormat: BarcodeFormat.QR_CODE,
115
116
  boundingBox: {
116
117
  topLeft: code.location.topLeftCorner,
117
118
  topRight: code.location.topRightCorner,