id-scanner-lib 1.2.2 → 1.3.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 (67) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +375 -363
  3. package/dist/id-scanner-core.esm.js +427 -221
  4. package/dist/id-scanner-core.esm.js.map +1 -1
  5. package/dist/id-scanner-core.js +427 -221
  6. package/dist/id-scanner-core.js.map +1 -1
  7. package/dist/id-scanner-core.min.js +1 -9
  8. package/dist/id-scanner-core.min.js.map +1 -1
  9. package/dist/id-scanner-ocr.esm.js +451 -276
  10. package/dist/id-scanner-ocr.esm.js.map +1 -1
  11. package/dist/id-scanner-ocr.js +451 -276
  12. package/dist/id-scanner-ocr.js.map +1 -1
  13. package/dist/id-scanner-ocr.min.js +1 -9
  14. package/dist/id-scanner-ocr.min.js.map +1 -1
  15. package/dist/id-scanner-qr.esm.js +483 -233
  16. package/dist/id-scanner-qr.esm.js.map +1 -1
  17. package/dist/id-scanner-qr.js +482 -232
  18. package/dist/id-scanner-qr.js.map +1 -1
  19. package/dist/id-scanner-qr.min.js +1 -9
  20. package/dist/id-scanner-qr.min.js.map +1 -1
  21. package/dist/id-scanner.js +2138 -358
  22. package/dist/id-scanner.js.map +1 -1
  23. package/dist/id-scanner.min.js +1 -9
  24. package/dist/id-scanner.min.js.map +1 -1
  25. package/package.json +27 -7
  26. package/src/demo/demo.ts +178 -62
  27. package/src/id-recognition/anti-fake-detector.ts +317 -0
  28. package/src/id-recognition/id-detector.ts +184 -155
  29. package/src/id-recognition/ocr-processor.ts +193 -146
  30. package/src/id-recognition/ocr-worker.ts +82 -72
  31. package/src/index-umd.ts +347 -110
  32. package/src/index.ts +866 -91
  33. package/src/ocr-module.ts +108 -60
  34. package/src/qr-module.ts +104 -54
  35. package/src/scanner/barcode-scanner.ts +145 -58
  36. package/src/scanner/qr-scanner.ts +86 -47
  37. package/src/utils/image-processing.ts +479 -294
  38. package/dist/core.d.ts +0 -77
  39. package/dist/demo/demo.d.ts +0 -14
  40. package/dist/id-recognition/data-extractor.d.ts +0 -105
  41. package/dist/id-recognition/id-detector.d.ts +0 -100
  42. package/dist/id-recognition/ocr-processor.d.ts +0 -64
  43. package/dist/id-scanner.esm.js +0 -94656
  44. package/dist/id-scanner.esm.js.map +0 -1
  45. package/dist/index-umd.d.ts +0 -96
  46. package/dist/index.d.ts +0 -78
  47. package/dist/ocr-module.d.ts +0 -67
  48. package/dist/qr-module.d.ts +0 -68
  49. package/dist/scanner/barcode-scanner.d.ts +0 -90
  50. package/dist/scanner/qr-scanner.d.ts +0 -80
  51. package/dist/types/core.d.ts +0 -77
  52. package/dist/types/demo/demo.d.ts +0 -14
  53. package/dist/types/id-recognition/data-extractor.d.ts +0 -105
  54. package/dist/types/id-recognition/id-detector.d.ts +0 -100
  55. package/dist/types/id-recognition/ocr-processor.d.ts +0 -64
  56. package/dist/types/index-umd.d.ts +0 -96
  57. package/dist/types/index.d.ts +0 -78
  58. package/dist/types/ocr-module.d.ts +0 -67
  59. package/dist/types/qr-module.d.ts +0 -68
  60. package/dist/types/scanner/barcode-scanner.d.ts +0 -90
  61. package/dist/types/scanner/qr-scanner.d.ts +0 -80
  62. package/dist/types/utils/camera.d.ts +0 -81
  63. package/dist/types/utils/image-processing.d.ts +0 -75
  64. package/dist/types/utils/types.d.ts +0 -65
  65. package/dist/utils/camera.d.ts +0 -81
  66. package/dist/utils/image-processing.d.ts +0 -75
  67. package/dist/utils/types.d.ts +0 -65
@@ -4,31 +4,35 @@
4
4
  * @module IDCardDetector
5
5
  */
6
6
 
7
- import { Camera } from '../utils/camera';
8
- import { ImageProcessor } from '../utils/image-processing';
9
- import { DetectionResult } from '../utils/types';
10
- import { throttle, LRUCache, calculateImageFingerprint } from '../utils/performance';
11
- import { Disposable } from '../utils/resource-manager';
7
+ import { Camera } from "../utils/camera"
8
+ import { ImageProcessor } from "../utils/image-processing"
9
+ import { DetectionResult } from "../utils/types"
10
+ import {
11
+ throttle,
12
+ LRUCache,
13
+ calculateImageFingerprint,
14
+ } from "../utils/performance"
15
+ import { Disposable } from "../utils/resource-manager"
12
16
 
13
17
  /**
14
18
  * IDCardDetector配置选项
15
19
  */
16
20
  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;
21
+ onDetection?: (result: DetectionResult) => void
22
+ onError?: (error: Error) => void
23
+ detectionInterval?: number
24
+ maxImageDimension?: number
25
+ enableCache?: boolean
26
+ cacheSize?: number
27
+ logger?: (message: any) => void
24
28
  }
25
29
 
26
30
  /**
27
31
  * 身份证检测器类
28
- *
32
+ *
29
33
  * 通过图像处理和计算机视觉技术,实时检测视频流中的身份证,并提取身份证区域
30
34
  * 注意:当前实现是简化版,实际项目中建议使用OpenCV.js进行更精确的检测
31
- *
35
+ *
32
36
  * @example
33
37
  * ```typescript
34
38
  * // 创建身份证检测器
@@ -39,47 +43,49 @@ export interface IDCardDetectorOptions {
39
43
  * processIDCardImage(result.croppedImage);
40
44
  * }
41
45
  * });
42
- *
46
+ *
43
47
  * // 启动检测
44
48
  * const videoElement = document.getElementById('video') as HTMLVideoElement;
45
49
  * await detector.start(videoElement);
46
- *
50
+ *
47
51
  * // 停止检测
48
52
  * detector.stop();
49
53
  * ```
50
54
  */
51
55
  export class IDCardDetector implements Disposable {
52
- private camera: Camera;
53
- private detecting = false;
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;
64
-
56
+ private camera: Camera
57
+ private detecting = false
58
+ private detectTimer: number | null = null
59
+ private onDetected?: (result: DetectionResult) => void
60
+ private onError?: (error: Error) => void
61
+ private detectionInterval: number
62
+ private maxImageDimension: number
63
+ private resultCache: LRUCache<string, DetectionResult>
64
+ private throttledDetect: ReturnType<typeof throttle>
65
+ private frameCount: number = 0
66
+ private lastDetectionTime: number = 0
67
+ private options: IDCardDetectorOptions
68
+
65
69
  /**
66
70
  * 创建身份证检测器实例
67
- *
71
+ *
68
72
  * @param options 身份证检测器配置选项,或者检测回调函数
69
73
  */
70
- constructor(options?: IDCardDetectorOptions | ((result: DetectionResult) => void)) {
71
- this.camera = new Camera();
72
-
73
- if (typeof options === 'function') {
74
+ constructor(
75
+ options?: IDCardDetectorOptions | ((result: DetectionResult) => void)
76
+ ) {
77
+ this.camera = new Camera()
78
+
79
+ if (typeof options === "function") {
74
80
  // 兼容旧的构造函数方式
75
- this.onDetected = options;
81
+ this.onDetected = options
76
82
  this.options = {
77
83
  detectionInterval: 200,
78
84
  maxImageDimension: 800,
79
85
  enableCache: true,
80
86
  cacheSize: 20,
81
- logger: console.log
82
- };
87
+ logger: console.log,
88
+ }
83
89
  } else if (options) {
84
90
  // 使用新的选项对象方式
85
91
  this.options = {
@@ -88,247 +94,270 @@ export class IDCardDetector implements Disposable {
88
94
  enableCache: true,
89
95
  cacheSize: 20,
90
96
  logger: console.log,
91
- ...options
92
- };
93
- this.onDetected = options.onDetection;
94
- this.onError = options.onError;
97
+ ...options,
98
+ }
99
+ this.onDetected = options.onDetection
100
+ this.onError = options.onError
95
101
  } else {
96
102
  this.options = {
97
103
  detectionInterval: 200,
98
104
  maxImageDimension: 800,
99
105
  enableCache: true,
100
106
  cacheSize: 20,
101
- logger: console.log
102
- };
107
+ logger: console.log,
108
+ }
103
109
  }
104
-
105
- this.detectionInterval = this.options.detectionInterval!;
106
- this.maxImageDimension = this.options.maxImageDimension!;
107
-
110
+
111
+ this.detectionInterval = this.options.detectionInterval!
112
+ this.maxImageDimension = this.options.maxImageDimension!
113
+
108
114
  // 初始化结果缓存
109
- this.resultCache = new LRUCache<string, DetectionResult>(this.options.cacheSize);
110
-
115
+ this.resultCache = new LRUCache<string, DetectionResult>(
116
+ this.options.cacheSize
117
+ )
118
+
111
119
  // 创建节流版本的检测函数
112
- this.throttledDetect = throttle(this.performDetection.bind(this), this.detectionInterval);
120
+ this.throttledDetect = throttle(
121
+ this.performDetection.bind(this),
122
+ this.detectionInterval
123
+ )
113
124
  }
114
-
125
+
115
126
  /**
116
127
  * 启动身份证检测
117
- *
128
+ *
118
129
  * 初始化相机并开始连续检测视频帧中的身份证
119
- *
130
+ *
120
131
  * @param {HTMLVideoElement} videoElement - 用于显示相机画面的video元素
121
132
  * @returns {Promise<void>} 启动完成的Promise
122
133
  */
123
134
  async start(videoElement: HTMLVideoElement): Promise<void> {
124
- await this.camera.initialize(videoElement);
125
- this.detecting = true;
126
- this.frameCount = 0;
127
- this.lastDetectionTime = 0;
128
- this.detect();
135
+ await this.camera.initialize(videoElement)
136
+ this.detecting = true
137
+ this.frameCount = 0
138
+ this.lastDetectionTime = 0
139
+ this.detect()
129
140
  }
130
-
141
+
131
142
  /**
132
143
  * 停止身份证检测
133
144
  */
134
145
  stop(): void {
135
- this.detecting = false;
146
+ this.detecting = false
136
147
  if (this.detectTimer !== null) {
137
- cancelAnimationFrame(this.detectTimer);
138
- this.detectTimer = null;
148
+ cancelAnimationFrame(this.detectTimer)
149
+ this.detectTimer = null
139
150
  }
140
151
  }
141
-
152
+
142
153
  /**
143
154
  * 持续检测视频帧
144
- *
155
+ *
145
156
  * @private
146
157
  */
147
158
  private detect(): void {
148
- if (!this.detecting) return;
149
-
159
+ if (!this.detecting) return
160
+
150
161
  this.detectTimer = requestAnimationFrame(() => {
151
162
  try {
152
- this.frameCount++;
153
- const now = performance.now();
154
-
163
+ this.frameCount++
164
+ const now = performance.now()
165
+
155
166
  // 帧率控制 - 只有满足时间间隔的帧才进行检测
156
167
  // 这样可以显著减少CPU使用率,同时保持良好的用户体验
157
- if (this.frameCount % 3 === 0 || now - this.lastDetectionTime >= this.detectionInterval) {
158
- this.throttledDetect();
159
- this.lastDetectionTime = now;
168
+ if (
169
+ this.frameCount % 3 === 0 ||
170
+ now - this.lastDetectionTime >= this.detectionInterval
171
+ ) {
172
+ this.throttledDetect()
173
+ this.lastDetectionTime = now
160
174
  }
161
-
175
+
162
176
  // 继续下一帧检测
163
- this.detect();
177
+ this.detect()
164
178
  } catch (error) {
165
179
  if (this.onError) {
166
- this.onError(error as Error);
180
+ this.onError(error as Error)
167
181
  } else {
168
- console.error('身份证检测错误:', error);
182
+ console.error("身份证检测错误:", error)
169
183
  }
170
-
184
+
171
185
  // 出错后延迟重试
172
186
  setTimeout(() => {
173
187
  if (this.detecting) {
174
- this.detect();
188
+ this.detect()
175
189
  }
176
- }, 1000);
190
+ }, 1000)
177
191
  }
178
- });
192
+ })
179
193
  }
180
-
194
+
181
195
  /**
182
196
  * 执行单帧检测
183
- *
197
+ *
184
198
  * @private
185
199
  */
186
200
  private async performDetection(): Promise<void> {
187
- if (!this.detecting || !this.camera) return;
188
-
201
+ if (!this.detecting || !this.camera) return
202
+
189
203
  // 获取当前视频帧
190
- const frame = this.camera.captureFrame();
191
- if (!frame) return;
192
-
204
+ const frame = this.camera.captureFrame()
205
+ if (!frame) return
206
+
193
207
  // 检查缓存
194
208
  if (this.options.enableCache) {
195
- const fingerprint = calculateImageFingerprint(frame, 16); // 使用更大的尺寸提高特征区分度
196
- const cachedResult = this.resultCache.get(fingerprint);
197
-
209
+ const fingerprint = calculateImageFingerprint(frame, 16) // 使用更大的尺寸提高特征区分度
210
+ const cachedResult = this.resultCache.get(fingerprint)
211
+
198
212
  if (cachedResult) {
199
- this.options.logger?.('使用缓存的检测结果');
200
-
213
+ this.options.logger?.("使用缓存的检测结果")
214
+
201
215
  // 使用缓存结果,但更新图像数据以确保最新
202
216
  const updatedResult = {
203
217
  ...cachedResult,
204
- imageData: frame
205
- };
206
-
218
+ imageData: frame,
219
+ }
220
+
207
221
  if (this.onDetected) {
208
- this.onDetected(updatedResult);
222
+ this.onDetected(updatedResult)
209
223
  }
210
- return;
224
+ return
211
225
  }
212
226
  }
213
-
227
+
214
228
  // 降低分辨率以提高性能
215
- const downsampledFrame = ImageProcessor.downsampleForProcessing(frame, this.maxImageDimension);
216
-
229
+ const downsampledFrame = ImageProcessor.resizeImage(
230
+ frame,
231
+ this.maxImageDimension,
232
+ this.maxImageDimension
233
+ )
234
+
217
235
  try {
218
236
  // 检测身份证
219
- const result = await this.detectIDCard(downsampledFrame);
220
-
237
+ const result = await this.detectIDCard(downsampledFrame)
238
+
221
239
  // 如果检测成功,将原始图像添加到结果中
222
240
  if (result.success) {
223
- result.imageData = frame;
224
-
241
+ result.imageData = frame
242
+
225
243
  // 缓存结果
226
244
  if (this.options.enableCache) {
227
- const fingerprint = calculateImageFingerprint(frame, 16);
228
- this.resultCache.set(fingerprint, result);
245
+ const fingerprint = calculateImageFingerprint(frame, 16)
246
+ this.resultCache.set(fingerprint, result)
229
247
  }
230
248
  }
231
-
249
+
232
250
  // 处理检测结果
233
251
  if (this.onDetected) {
234
- this.onDetected(result);
252
+ this.onDetected(result)
235
253
  }
236
254
  } catch (error) {
237
255
  if (this.onError) {
238
- this.onError(error as Error);
256
+ this.onError(error as Error)
239
257
  } else {
240
- console.error('身份证检测错误:', error);
258
+ console.error("身份证检测错误:", error)
241
259
  }
242
260
  }
243
261
  }
244
-
262
+
245
263
  /**
246
264
  * 检测图像中的身份证
247
- *
265
+ *
248
266
  * @private
249
267
  * @param {ImageData} imageData - 要分析的图像数据
250
268
  * @returns {Promise<DetectionResult>} 检测结果
251
269
  */
252
270
  private async detectIDCard(imageData: ImageData): Promise<DetectionResult> {
253
271
  // 1. 图像预处理
254
- const grayscale = ImageProcessor.toGrayscale(imageData);
255
-
272
+ const grayscale = ImageProcessor.toGrayscale(imageData)
273
+
256
274
  // 2. 检测矩形和边缘(简化版实现)
257
275
  // 注意:实际应用中应使用OpenCV.js或其他计算机视觉库进行更精确的检测
258
276
  // 此处仅作为概念性实现,使用基本矩形检测逻辑
259
-
277
+
260
278
  // 模拟检测过程,随机判断是否找到身份证
261
279
  // 在实际应用中,此处应当实现实际的计算机视觉算法
262
280
  const detectionResult: DetectionResult = {
263
281
  success: Math.random() > 0.3, // 70%的概率成功检测到
264
- message: '身份证检测完成'
265
- };
266
-
282
+ message: "身份证检测完成",
283
+ }
284
+
267
285
  if (detectionResult.success) {
268
286
  // 模拟一个身份证矩形区域
269
- const width = imageData.width;
270
- const height = imageData.height;
271
-
287
+ const width = imageData.width
288
+ const height = imageData.height
289
+
272
290
  // 大致的身份证区域(按比例)
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);
277
-
291
+ const rectWidth = Math.round(width * 0.7)
292
+ const rectHeight = Math.round(rectWidth * 0.618) // 身份证是黄金比例
293
+ const rectX = Math.round((width - rectWidth) / 2)
294
+ const rectY = Math.round((height - rectHeight) / 2)
295
+
278
296
  // 添加四个角点
279
297
  detectionResult.corners = [
280
298
  { x: rectX, y: rectY },
281
299
  { x: rectX + rectWidth, y: rectY },
282
300
  { x: rectX + rectWidth, y: rectY + rectHeight },
283
- { x: rectX, y: rectY + rectHeight }
284
- ];
285
-
301
+ { x: rectX, y: rectY + rectHeight },
302
+ ]
303
+
286
304
  // 添加边界框
287
305
  detectionResult.boundingBox = {
288
306
  x: rectX,
289
307
  y: rectY,
290
308
  width: rectWidth,
291
- height: rectHeight
292
- };
293
-
309
+ height: rectHeight,
310
+ }
311
+
294
312
  // 裁剪身份证图像
295
- const canvas = document.createElement('canvas');
296
- canvas.width = rectWidth;
297
- canvas.height = rectHeight;
298
- const ctx = canvas.getContext('2d');
299
-
313
+ const canvas = document.createElement("canvas")
314
+ canvas.width = rectWidth
315
+ canvas.height = rectHeight
316
+ const ctx = canvas.getContext("2d")
317
+
300
318
  if (ctx) {
301
- const tempCanvas = ImageProcessor.imageDataToCanvas(imageData);
319
+ const tempCanvas = ImageProcessor.imageDataToCanvas(imageData)
302
320
  ctx.drawImage(
303
321
  tempCanvas,
304
- rectX, rectY, rectWidth, rectHeight,
305
- 0, 0, rectWidth, rectHeight
306
- );
307
-
308
- detectionResult.croppedImage = ctx.getImageData(0, 0, rectWidth, rectHeight);
322
+ rectX,
323
+ rectY,
324
+ rectWidth,
325
+ rectHeight,
326
+ 0,
327
+ 0,
328
+ rectWidth,
329
+ rectHeight
330
+ )
331
+
332
+ detectionResult.croppedImage = ctx.getImageData(
333
+ 0,
334
+ 0,
335
+ rectWidth,
336
+ rectHeight
337
+ )
309
338
  }
310
-
339
+
311
340
  // 设置置信度
312
- detectionResult.confidence = 0.7 + Math.random() * 0.3;
341
+ detectionResult.confidence = 0.7 + Math.random() * 0.3
313
342
  }
314
-
315
- return detectionResult;
343
+
344
+ return detectionResult
316
345
  }
317
-
346
+
318
347
  /**
319
348
  * 清除检测结果缓存
320
349
  */
321
350
  clearCache(): void {
322
- this.resultCache.clear();
323
- this.options.logger?.('检测结果缓存已清除');
351
+ this.resultCache.clear()
352
+ this.options.logger?.("检测结果缓存已清除")
324
353
  }
325
-
354
+
326
355
  /**
327
356
  * 释放资源
328
357
  */
329
358
  dispose(): void {
330
- this.stop();
331
- this.camera.release();
332
- this.resultCache.clear();
359
+ this.stop()
360
+ this.camera.release()
361
+ this.resultCache.clear()
333
362
  }
334
- }
363
+ }