id-scanner-lib 1.3.0 → 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.
package/src/index.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * @file ID扫描识别库主入口文件
3
3
  * @description 提供身份证识别与二维码、条形码扫描功能的纯前端TypeScript库
4
4
  * @module IDScannerLib
5
- * @version 1.0.0
5
+ * @version 1.3.0
6
6
  * @license MIT
7
7
  */
8
8
 
@@ -18,6 +18,12 @@ import {
18
18
  import type { QRScannerOptions } from "./scanner/qr-scanner"
19
19
  import type { BarcodeScannerOptions } from "./scanner/barcode-scanner"
20
20
 
21
+ // 导入防伪检测器
22
+ import {
23
+ AntiFakeDetector,
24
+ AntiFakeDetectionResult,
25
+ } from "./id-recognition/anti-fake-detector"
26
+
21
27
  /**
22
28
  * IDScanner配置选项接口
23
29
  */
@@ -29,6 +35,7 @@ export interface IDScannerOptions {
29
35
  onBarcodeScanned?: (result: string) => void
30
36
  onIDCardScanned?: (info: IDCardInfo) => void
31
37
  onImageProcessed?: (processedImage: ImageData | File) => void
38
+ onAntiFakeDetected?: (result: AntiFakeDetectionResult) => void
32
39
  onError?: (error: Error) => void
33
40
  }
34
41
 
@@ -42,15 +49,17 @@ export class IDScanner {
42
49
  private camera: Camera
43
50
  private scanMode: "qr" | "barcode" | "idcard" = "qr"
44
51
  private videoElement: HTMLVideoElement | null = null
45
-
46
- // 延迟加载的模块
52
+ private scanning = false
47
53
  private qrModule: any = null
48
54
  private ocrModule: any = null
49
-
50
- // 模块加载状态
55
+ private scanTimer: number | null = null
51
56
  private isQRModuleLoaded: boolean = false
52
57
  private isOCRModuleLoaded: boolean = false
53
58
 
59
+ // 新增防伪检测器
60
+ private antiFakeDetector: AntiFakeDetector | null = null
61
+ private isAntiFakeModuleLoaded: boolean = false
62
+
54
63
  /**
55
64
  * 构造函数
56
65
  * @param options 配置选项
@@ -61,7 +70,7 @@ export class IDScanner {
61
70
 
62
71
  /**
63
72
  * 初始化模块
64
- * 根据需要初始化OCR引擎
73
+ * 根据需要初始化OCR引擎和防伪检测模块
65
74
  */
66
75
  async initialize(): Promise<void> {
67
76
  try {
@@ -80,13 +89,44 @@ export class IDScanner {
80
89
  await this.ocrModule.initialize()
81
90
  }
82
91
 
83
- console.log("IDScanner initialized")
92
+ // 初始化防伪检测模块
93
+ if (!this.isAntiFakeModuleLoaded) {
94
+ this.antiFakeDetector = new AntiFakeDetector()
95
+ this.isAntiFakeModuleLoaded = true
96
+ }
97
+
98
+ console.log("IDScanner初始化完成")
84
99
  } catch (error) {
100
+ console.error("初始化失败:", error)
85
101
  this.handleError(error as Error)
86
102
  throw error
87
103
  }
88
104
  }
89
105
 
106
+ /**
107
+ * 初始化OCR模块
108
+ */
109
+ private async initOCRModule(): Promise<void> {
110
+ if (this.isOCRModuleLoaded) return
111
+
112
+ try {
113
+ // 动态导入OCR模块
114
+ const OCRModule = await import("./ocr-module").then((m) => m.OCRModule)
115
+ this.ocrModule = new OCRModule({
116
+ cameraOptions: this.options.cameraOptions,
117
+ onIDCardScanned: this.options.onIDCardScanned,
118
+ onError: this.options.onError,
119
+ })
120
+ this.isOCRModuleLoaded = true
121
+
122
+ // 初始化OCR模块
123
+ await this.ocrModule.initialize()
124
+ } catch (error) {
125
+ console.error("OCR模块初始化失败:", error)
126
+ throw error
127
+ }
128
+ }
129
+
90
130
  /**
91
131
  * 启动二维码扫描
92
132
  * @param videoElement HTML视频元素
@@ -194,7 +234,7 @@ export class IDScanner {
194
234
  if (this.options.onError) {
195
235
  this.options.onError(error)
196
236
  } else {
197
- console.error("IDScanner error:", error)
237
+ console.error("IDScanner错误:", error)
198
238
  }
199
239
  }
200
240
 
@@ -216,6 +256,13 @@ export class IDScanner {
216
256
  this.qrModule = null
217
257
  this.isQRModuleLoaded = false
218
258
  }
259
+
260
+ // 释放防伪检测资源
261
+ if (this.antiFakeDetector) {
262
+ this.antiFakeDetector.dispose()
263
+ this.antiFakeDetector = null
264
+ this.isAntiFakeModuleLoaded = false
265
+ }
219
266
  }
220
267
 
221
268
  /**
@@ -388,12 +435,11 @@ export class IDScanner {
388
435
  async processIDCardImage(
389
436
  imageSource: HTMLImageElement | HTMLCanvasElement | string | File
390
437
  ): Promise<IDCardInfo> {
391
- try {
392
- // 检查OCR模块是否已加载,若未加载则自动初始化
393
- if (!this.isOCRModuleLoaded) {
394
- await this.initialize()
395
- }
438
+ if (!this.isOCRModuleLoaded) {
439
+ await this.initOCRModule()
440
+ }
396
441
 
442
+ try {
397
443
  // 处理不同类型的图片源
398
444
  let imageElement: HTMLImageElement
399
445
 
@@ -460,7 +506,26 @@ export class IDScanner {
460
506
  })
461
507
 
462
508
  // 使用OCR模块处理图像
463
- return this.ocrModule.processIDCard(enhancedImageData)
509
+ const idInfo = await this.ocrModule.processIDCard(enhancedImageData)
510
+
511
+ // 进行防伪检测并将结果添加到身份证信息中
512
+ if (this.isAntiFakeModuleLoaded && this.antiFakeDetector) {
513
+ try {
514
+ const result = await this.antiFakeDetector.detect(enhancedImageData)
515
+ // 将防伪检测结果添加到身份证信息对象中
516
+ const extendedInfo = idInfo as any
517
+ extendedInfo.antiFakeResult = result
518
+
519
+ // 触发防伪检测回调
520
+ if (this.options.onAntiFakeDetected) {
521
+ this.options.onAntiFakeDetected(result)
522
+ }
523
+ } catch (error) {
524
+ console.warn("身份证防伪检测失败:", error)
525
+ }
526
+ }
527
+
528
+ return idInfo
464
529
  } catch (error) {
465
530
  this.handleError(error as Error)
466
531
  throw error
@@ -596,6 +661,89 @@ export class IDScanner {
596
661
  throw error
597
662
  }
598
663
  }
664
+
665
+ /**
666
+ * 身份证防伪检测
667
+ * @param imageSource 图片源
668
+ * @returns 防伪检测结果
669
+ */
670
+ async detectIDCardAntiFake(
671
+ imageSource: HTMLImageElement | HTMLCanvasElement | string | File
672
+ ): Promise<AntiFakeDetectionResult> {
673
+ if (!this.isAntiFakeModuleLoaded || !this.antiFakeDetector) {
674
+ await this.initialize()
675
+ if (!this.antiFakeDetector) {
676
+ throw new Error("防伪检测模块初始化失败")
677
+ }
678
+ }
679
+
680
+ try {
681
+ // 转换输入为ImageData
682
+ let imageData: ImageData
683
+
684
+ if (typeof imageSource === "string") {
685
+ // 处理URL或Base64
686
+ const img = new Image()
687
+ await new Promise<void>((resolve, reject) => {
688
+ img.onload = () => resolve()
689
+ img.onerror = () => reject(new Error("图像加载失败"))
690
+ img.src = imageSource
691
+ })
692
+
693
+ const canvas = document.createElement("canvas")
694
+ canvas.width = img.width
695
+ canvas.height = img.height
696
+ const ctx = canvas.getContext("2d")
697
+ if (!ctx) {
698
+ throw new Error("无法创建Canvas上下文")
699
+ }
700
+
701
+ ctx.drawImage(img, 0, 0)
702
+ imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
703
+ } else if (imageSource instanceof File) {
704
+ // 处理文件
705
+ imageData = await ImageProcessor.createImageDataFromFile(imageSource)
706
+ } else if (imageSource instanceof HTMLImageElement) {
707
+ // 处理Image元素
708
+ const canvas = document.createElement("canvas")
709
+ canvas.width = imageSource.width
710
+ canvas.height = imageSource.height
711
+ const ctx = canvas.getContext("2d")
712
+ if (!ctx) {
713
+ throw new Error("无法创建Canvas上下文")
714
+ }
715
+
716
+ ctx.drawImage(imageSource, 0, 0)
717
+ imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
718
+ } else {
719
+ // 处理Canvas元素
720
+ const ctx = (imageSource as HTMLCanvasElement).getContext("2d")
721
+ if (!ctx) {
722
+ throw new Error("无法获取Canvas上下文")
723
+ }
724
+
725
+ imageData = ctx.getImageData(
726
+ 0,
727
+ 0,
728
+ imageSource.width,
729
+ imageSource.height
730
+ )
731
+ }
732
+
733
+ // 执行防伪检测
734
+ const result = await this.antiFakeDetector.detect(imageData)
735
+
736
+ // 触发回调
737
+ if (this.options.onAntiFakeDetected) {
738
+ this.options.onAntiFakeDetected(result)
739
+ }
740
+
741
+ return result
742
+ } catch (error) {
743
+ this.handleError(error as Error)
744
+ throw error
745
+ }
746
+ }
599
747
  }
600
748
 
601
749
  // 导出工具类和类型
@@ -607,6 +755,9 @@ export {
607
755
  } from "./utils/image-processing"
608
756
  export { IDCardInfo, DetectionResult } from "./utils/types"
609
757
 
758
+ // 导出防伪检测相关类型和类
759
+ export { AntiFakeDetector, AntiFakeDetectionResult }
760
+
610
761
  // 为了向后兼容,我们创建一个演示类
611
762
  export class IDScannerDemo {
612
763
  private scanner: IDScanner
@@ -759,7 +910,7 @@ export class IDScannerDemo {
759
910
  private handleIDCardResult(info: IDCardInfo): void {
760
911
  // 格式化显示身份证信息
761
912
  const infoHtml = Object.entries(info)
762
- .filter(([key, value]) => value) // 过滤掉空值
913
+ .filter(([key, value]) => value && key !== "antiFakeResult") // 过滤掉空值和防伪结果
763
914
  .map(([key, value]) => {
764
915
  // 转换键名为中文显示
765
916
  const keyMap: { [key: string]: string } = {
@@ -778,9 +929,32 @@ export class IDScannerDemo {
778
929
  })
779
930
  .join("")
780
931
 
932
+ // 检查是否有防伪检测结果
933
+ let antiFakeHtml = ""
934
+ const anyInfo = info as any
935
+ if (anyInfo.antiFakeResult) {
936
+ const antiFakeResult = anyInfo.antiFakeResult
937
+ antiFakeHtml = `
938
+ <h3>防伪检测结果:</h3>
939
+ <div style="color: ${antiFakeResult.isAuthentic ? "green" : "red"}">
940
+ ${
941
+ antiFakeResult.isAuthentic
942
+ ? "✓ 身份证真实"
943
+ : "⚠ 警告:可能为伪造证件"
944
+ }
945
+ </div>
946
+ <div>置信度: ${(antiFakeResult.confidence * 100).toFixed(1)}%</div>
947
+ <div>检测到的特征: ${
948
+ antiFakeResult.detectedFeatures.join(", ") || "无"
949
+ }</div>
950
+ <div>${antiFakeResult.message}</div>
951
+ `
952
+ }
953
+
781
954
  this.updateResultDisplay(`
782
955
  <h3>身份证信息:</h3>
783
956
  ${infoHtml}
957
+ ${antiFakeHtml}
784
958
  `)
785
959
  }
786
960