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,61 +4,69 @@
4
4
  * @module OCRProcessor
5
5
  */
6
6
 
7
- import { createWorker } from 'tesseract.js';
8
- import { IDCardInfo } from '../utils/types';
9
- import { ImageProcessor } from '../utils/image-processing';
10
- import { LRUCache, calculateImageFingerprint } from '../utils/performance';
11
- import { isWorkerSupported, createWorker as createCustomWorker } from '../utils/worker';
12
- import { processOCRInWorker, OCRProcessInput } from './ocr-worker';
13
- import { Disposable } from '../utils/resource-manager';
7
+ import { createWorker } from "tesseract.js"
8
+ import { IDCardInfo } from "../utils/types"
9
+ import { ImageProcessor } from "../utils/image-processing"
10
+ import { LRUCache, calculateImageFingerprint } from "../utils/performance"
11
+ import {
12
+ isWorkerSupported,
13
+ createWorker as createCustomWorker,
14
+ } from "../utils/worker"
15
+ import { processOCRInWorker, OCRProcessInput } from "./ocr-worker"
16
+ import { Disposable } from "../utils/resource-manager"
14
17
 
15
18
  /**
16
19
  * OCR处理器选项接口
17
20
  */
18
21
  export interface OCRProcessorOptions {
19
- /** 是否使用Worker线程 */
20
- useWorker?: boolean;
21
- /** 是否启用结果缓存 */
22
- enableCache?: boolean;
23
- /** 缓存容量 */
24
- cacheSize?: number;
25
- /** 图像处理前的最大尺寸 */
26
- maxImageDimension?: number;
27
- /** 日志回调函数 */
28
- logger?: (message: any) => void;
22
+ language?: string
23
+ useWorker?: boolean
24
+ maxImageDimension?: number
25
+ timeout?: number
26
+ brightness?: number // 新增亮度参数
27
+ contrast?: number // 新增对比度参数
28
+ onProgress?: (progress: number) => void
29
+ enableCache?: boolean // 添加启用缓存选项
30
+ cacheSize?: number // 添加缓存大小选项
31
+ logger?: (message: any) => void // 添加日志记录器选项
29
32
  }
30
33
 
31
34
  /**
32
35
  * OCR处理器类
33
- *
36
+ *
34
37
  * 使用Tesseract.js实现对身份证图像的OCR文字识别和信息提取功能
35
- *
38
+ *
36
39
  * @example
37
40
  * ```typescript
38
41
  * // 创建OCR处理器
39
42
  * const ocrProcessor = new OCRProcessor();
40
- *
43
+ *
41
44
  * // 初始化OCR引擎
42
45
  * await ocrProcessor.initialize();
43
- *
46
+ *
44
47
  * // 处理身份证图像
45
48
  * const idInfo = await ocrProcessor.processIDCard(idCardImageData);
46
49
  * console.log('识别到的身份证信息:', idInfo);
47
- *
50
+ *
48
51
  * // 使用结束后释放资源
49
52
  * await ocrProcessor.terminate();
50
53
  * ```
51
54
  */
52
55
  export class OCRProcessor implements Disposable {
53
- private worker: any = null;
54
- private ocrWorker: ReturnType<typeof createCustomWorker<OCRProcessInput, { idCardInfo: IDCardInfo, processingTime: number }>> | null = null;
55
- private initialized: boolean = false;
56
- private resultCache: LRUCache<string, IDCardInfo>;
57
- private options: OCRProcessorOptions;
58
-
56
+ private worker: any = null
57
+ private ocrWorker: ReturnType<
58
+ typeof createCustomWorker<
59
+ OCRProcessInput,
60
+ { idCardInfo: IDCardInfo; processingTime: number }
61
+ >
62
+ > | null = null
63
+ private initialized: boolean = false
64
+ private resultCache: LRUCache<string, IDCardInfo>
65
+ private options: OCRProcessorOptions
66
+
59
67
  /**
60
68
  * 创建OCR处理器实例
61
- *
69
+ *
62
70
  * @param options OCR处理器选项
63
71
  */
64
72
  constructor(options: OCRProcessorOptions = {}) {
@@ -68,46 +76,50 @@ export class OCRProcessor implements Disposable {
68
76
  cacheSize: 50,
69
77
  maxImageDimension: 1000,
70
78
  logger: console.log,
71
- ...options
72
- };
73
-
79
+ ...options,
80
+ }
81
+
74
82
  // 初始化缓存
75
- this.resultCache = new LRUCache<string, IDCardInfo>(this.options.cacheSize);
83
+ this.resultCache = new LRUCache<string, IDCardInfo>(this.options.cacheSize)
76
84
  }
77
-
85
+
78
86
  /**
79
87
  * 初始化OCR引擎
80
- *
88
+ *
81
89
  * 加载Tesseract OCR引擎和中文简体语言包,并设置适合身份证识别的参数
82
- *
90
+ *
83
91
  * @returns {Promise<void>} 初始化完成的Promise
84
92
  */
85
93
  async initialize(): Promise<void> {
86
- if (this.initialized) return;
87
-
94
+ if (this.initialized) return
95
+
88
96
  if (this.options.useWorker) {
89
97
  // 使用自定义Worker线程处理OCR
90
- this.ocrWorker = createCustomWorker<OCRProcessInput, { idCardInfo: IDCardInfo, processingTime: number }>(processOCRInWorker);
91
- this.initialized = true;
92
- this.options.logger?.('OCR Worker 初始化完成');
98
+ this.ocrWorker = createCustomWorker<
99
+ OCRProcessInput,
100
+ { idCardInfo: IDCardInfo; processingTime: number }
101
+ >(processOCRInWorker)
102
+ this.initialized = true
103
+ this.options.logger?.("OCR Worker 初始化完成")
93
104
  } else {
94
105
  // 使用主线程处理OCR
95
106
  this.worker = createWorker({
96
- logger: this.options.logger
97
- } as any);
98
-
99
- await this.worker.load();
100
- await this.worker.loadLanguage('chi_sim');
101
- await this.worker.initialize('chi_sim');
107
+ logger: this.options.logger,
108
+ } as any)
109
+
110
+ await this.worker.load()
111
+ await this.worker.loadLanguage("chi_sim")
112
+ await this.worker.initialize("chi_sim")
102
113
  await this.worker.setParameters({
103
- tessedit_char_whitelist: '0123456789X-年月日一二三四五六七八九十零壹贰叁肆伍陆柒捌玖拾ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz民族汉族满族回族维吾尔族藏族苗族彝族壮族朝鲜族侗族瑶族白族土家族哈尼族哈萨克族傣族黎族傈僳族佤族高山族拉祜族水族东乡族钠西族景颇族柯尔克孜族士族达斡尔族仫佬族羌族布朗族撒拉族毛南族仡佬族锡伯族阿昌族普米族塔吉克族怒族乌孜别克族俄罗斯族鄂温克族德昂族保安族裕固族京族塔塔尔族独龙族鄂伦春族赫哲族门巴族珞巴族基诺族男女性别住址出生公民身份号码签发机关有效期'
104
- });
105
-
106
- this.initialized = true;
107
- this.options.logger?.('OCR引擎初始化完成');
114
+ tessedit_char_whitelist:
115
+ "0123456789X-年月日一二三四五六七八九十零壹贰叁肆伍陆柒捌玖拾ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz民族汉族满族回族维吾尔族藏族苗族彝族壮族朝鲜族侗族瑶族白族土家族哈尼族哈萨克族傣族黎族傈僳族佤族高山族拉祜族水族东乡族钠西族景颇族柯尔克孜族士族达斡尔族仫佬族羌族布朗族撒拉族毛南族仡佬族锡伯族阿昌族普米族塔吉克族怒族乌孜别克族俄罗斯族鄂温克族德昂族保安族裕固族京族塔塔尔族独龙族鄂伦春族赫哲族门巴族珞巴族基诺族男女性别住址出生公民身份号码签发机关有效期",
116
+ })
117
+
118
+ this.initialized = true
119
+ this.options.logger?.("OCR引擎初始化完成")
108
120
  }
109
121
  }
110
-
122
+
111
123
  /**
112
124
  * 处理身份证图像并提取信息
113
125
  * @param imageData 要处理的身份证图像数据
@@ -115,173 +127,208 @@ export class OCRProcessor implements Disposable {
115
127
  */
116
128
  async processIDCard(imageData: ImageData): Promise<IDCardInfo> {
117
129
  if (!this.initialized) {
118
- await this.initialize();
130
+ await this.initialize()
119
131
  }
120
-
132
+
121
133
  // 计算图像指纹,用于缓存查找
122
134
  if (this.options.enableCache) {
123
- const fingerprint = calculateImageFingerprint(imageData);
124
-
135
+ const fingerprint = calculateImageFingerprint(imageData)
136
+
125
137
  // 检查缓存中是否有结果
126
- const cachedResult = this.resultCache.get(fingerprint);
138
+ const cachedResult = this.resultCache.get(fingerprint)
127
139
  if (cachedResult) {
128
- this.options.logger?.('使用缓存的OCR结果');
129
- return cachedResult;
140
+ this.options.logger?.("使用缓存的OCR结果")
141
+ return cachedResult
130
142
  }
131
143
  }
132
-
133
- // 图像预处理:降低分辨率和增强对比度
134
- const downsampledImage = ImageProcessor.downsampleForProcessing(imageData, this.options.maxImageDimension);
135
- const enhancedImage = ImageProcessor.adjustBrightnessContrast(downsampledImage, 15, 25);
136
-
144
+
145
+ // 调整图像大小以提高性能和准确性
146
+ const downsampledImage = ImageProcessor.resizeImage(
147
+ imageData,
148
+ this.options.maxImageDimension || 1000,
149
+ this.options.maxImageDimension || 1000,
150
+ true // 保持宽高比
151
+ )
152
+
153
+ // 提高图像质量以获得更好的OCR结果
154
+ const enhancedImage = ImageProcessor.batchProcess(downsampledImage, {
155
+ brightness: this.options.brightness || 15,
156
+ contrast: this.options.contrast || 25,
157
+ sharpen: true,
158
+ })
159
+
160
+ // 转换为base64供Tesseract处理
161
+ // 创建一个canvas元素
162
+ const canvas = document.createElement("canvas")
163
+ canvas.width = enhancedImage.width
164
+ canvas.height = enhancedImage.height
165
+ const ctx = canvas.getContext("2d")
166
+
167
+ if (!ctx) {
168
+ throw new Error("无法创建canvas上下文")
169
+ }
170
+
171
+ // 将ImageData绘制到canvas
172
+ ctx.putImageData(enhancedImage, 0, 0)
173
+
174
+ // 转换为Base64
175
+ const base64Image = canvas.toDataURL("image/jpeg", 0.7)
176
+
137
177
  // OCR识别
138
178
  try {
139
- let idCardInfo: IDCardInfo;
140
-
179
+ let idCardInfo: IDCardInfo
180
+
141
181
  if (this.options.useWorker && this.ocrWorker) {
142
182
  // 使用Worker线程处理
143
- const base64Image = ImageProcessor.imageDataToBase64(enhancedImage);
144
-
145
183
  const result = await this.ocrWorker.postMessage({
146
184
  imageBase64: base64Image,
147
185
  tessWorkerOptions: {
148
- logger: this.options.logger
149
- }
150
- });
151
-
152
- idCardInfo = result.idCardInfo;
153
- this.options.logger?.(`OCR处理完成,用时: ${result.processingTime.toFixed(2)}ms`);
186
+ logger: this.options.logger,
187
+ },
188
+ })
189
+
190
+ idCardInfo = result.idCardInfo
191
+ this.options.logger?.(
192
+ `OCR处理完成,用时: ${result.processingTime.toFixed(2)}ms`
193
+ )
154
194
  } else {
155
195
  // 使用主线程处理
156
- const startTime = performance.now();
157
-
196
+ const startTime = performance.now()
197
+
158
198
  // 转换ImageData为Canvas
159
- const canvas = ImageProcessor.imageDataToCanvas(enhancedImage);
160
-
161
- const { data } = await this.worker.recognize(canvas);
162
-
199
+ const canvas = ImageProcessor.imageDataToCanvas(enhancedImage)
200
+
201
+ const { data } = await this.worker.recognize(canvas)
202
+
163
203
  // 解析身份证信息
164
- idCardInfo = this.parseIDCardText(data.text);
165
-
166
- const processingTime = performance.now() - startTime;
167
- this.options.logger?.(`OCR处理完成,用时: ${processingTime.toFixed(2)}ms`);
204
+ idCardInfo = this.parseIDCardText(data.text)
205
+
206
+ const processingTime = performance.now() - startTime
207
+ this.options.logger?.(
208
+ `OCR处理完成,用时: ${processingTime.toFixed(2)}ms`
209
+ )
168
210
  }
169
-
211
+
170
212
  // 缓存结果
171
213
  if (this.options.enableCache) {
172
- const fingerprint = calculateImageFingerprint(imageData);
173
- this.resultCache.set(fingerprint, idCardInfo);
214
+ const fingerprint = calculateImageFingerprint(imageData)
215
+ this.resultCache.set(fingerprint, idCardInfo)
174
216
  }
175
-
176
- return idCardInfo;
217
+
218
+ return idCardInfo
177
219
  } catch (error) {
178
- this.options.logger?.(`OCR识别错误: ${error}`);
179
- return {} as IDCardInfo;
220
+ this.options.logger?.(`OCR识别错误: ${error}`)
221
+ return {} as IDCardInfo
180
222
  }
181
223
  }
182
-
224
+
183
225
  /**
184
226
  * 解析身份证文本信息
185
- *
227
+ *
186
228
  * 从OCR识别到的文本中提取结构化的身份证信息
187
- *
229
+ *
188
230
  * @private
189
231
  * @param {string} text - OCR识别到的文本
190
232
  * @returns {IDCardInfo} 提取到的身份证信息对象
191
233
  */
192
234
  private parseIDCardText(text: string): IDCardInfo {
193
- const info: IDCardInfo = {};
194
-
235
+ const info: IDCardInfo = {}
236
+
195
237
  // 拆分为行
196
- const lines = text.split('\n').filter(line => line.trim());
197
-
238
+ const lines = text.split("\n").filter((line) => line.trim())
239
+
198
240
  // 解析身份证号码(最容易识别的部分)
199
- const idNumberRegex = /(\d{17}[\dX])/;
200
- const idNumberMatch = text.match(idNumberRegex);
241
+ const idNumberRegex = /(\d{17}[\dX])/
242
+ const idNumberMatch = text.match(idNumberRegex)
201
243
  if (idNumberMatch) {
202
- info.idNumber = idNumberMatch[1];
244
+ info.idNumber = idNumberMatch[1]
203
245
  }
204
-
246
+
205
247
  // 解析姓名
206
248
  for (const line of lines) {
207
- if (line.includes('姓名') || line.length < 10 && line.length > 1 && !/\d/.test(line)) {
208
- info.name = line.replace('姓名', '').trim();
209
- break;
249
+ if (
250
+ line.includes("姓名") ||
251
+ (line.length < 10 && line.length > 1 && !/\d/.test(line))
252
+ ) {
253
+ info.name = line.replace("姓名", "").trim()
254
+ break
210
255
  }
211
256
  }
212
-
257
+
213
258
  // 解析性别和民族
214
- const genderNationalityRegex = /(男|女).*(族)/;
215
- const genderMatch = text.match(genderNationalityRegex);
259
+ const genderNationalityRegex = /(男|女).*(族)/
260
+ const genderMatch = text.match(genderNationalityRegex)
216
261
  if (genderMatch) {
217
- info.gender = genderMatch[1];
218
- const nationalityText = genderMatch[0];
219
- info.nationality = nationalityText.substring(nationalityText.indexOf(genderMatch[1]) + 1).trim();
262
+ info.gender = genderMatch[1]
263
+ const nationalityText = genderMatch[0]
264
+ info.nationality = nationalityText
265
+ .substring(nationalityText.indexOf(genderMatch[1]) + 1)
266
+ .trim()
220
267
  }
221
-
268
+
222
269
  // 解析出生日期
223
- const birthDateRegex = /(\d{4})年(\d{1,2})月(\d{1,2})日/;
224
- const birthDateMatch = text.match(birthDateRegex);
270
+ const birthDateRegex = /(\d{4})年(\d{1,2})月(\d{1,2})日/
271
+ const birthDateMatch = text.match(birthDateRegex)
225
272
  if (birthDateMatch) {
226
- info.birthDate = `${birthDateMatch[1]}-${birthDateMatch[2]}-${birthDateMatch[3]}`;
273
+ info.birthDate = `${birthDateMatch[1]}-${birthDateMatch[2]}-${birthDateMatch[3]}`
227
274
  }
228
-
275
+
229
276
  // 解析地址
230
- const addressRegex = /住址([\s\S]*?)公民身份号码/;
231
- const addressMatch = text.match(addressRegex);
277
+ const addressRegex = /住址([\s\S]*?)公民身份号码/
278
+ const addressMatch = text.match(addressRegex)
232
279
  if (addressMatch) {
233
- info.address = addressMatch[1].replace(/\n/g, '').trim();
280
+ info.address = addressMatch[1].replace(/\n/g, "").trim()
234
281
  }
235
-
282
+
236
283
  // 解析签发机关
237
- const authorityRegex = /签发机关([\s\S]*?)有效期/;
238
- const authorityMatch = text.match(authorityRegex);
284
+ const authorityRegex = /签发机关([\s\S]*?)有效期/
285
+ const authorityMatch = text.match(authorityRegex)
239
286
  if (authorityMatch) {
240
- info.issuingAuthority = authorityMatch[1].replace(/\n/g, '').trim();
287
+ info.issuingAuthority = authorityMatch[1].replace(/\n/g, "").trim()
241
288
  }
242
-
289
+
243
290
  // 解析有效期限
244
- const validPeriodRegex = /有效期限([\s\S]*?)(-|至)/;
245
- const validPeriodMatch = text.match(validPeriodRegex);
291
+ const validPeriodRegex = /有效期限([\s\S]*?)(-|至)/
292
+ const validPeriodMatch = text.match(validPeriodRegex)
246
293
  if (validPeriodMatch) {
247
- info.validPeriod = validPeriodMatch[0].replace('有效期限', '').trim();
294
+ info.validPeriod = validPeriodMatch[0].replace("有效期限", "").trim()
248
295
  }
249
-
250
- return info;
296
+
297
+ return info
251
298
  }
252
-
299
+
253
300
  /**
254
301
  * 清除结果缓存
255
302
  */
256
303
  clearCache(): void {
257
- this.resultCache.clear();
258
- this.options.logger?.('OCR结果缓存已清除');
304
+ this.resultCache.clear()
305
+ this.options.logger?.("OCR结果缓存已清除")
259
306
  }
260
-
307
+
261
308
  /**
262
309
  * 终止OCR引擎并释放资源
263
- *
310
+ *
264
311
  * @returns {Promise<void>} 终止完成的Promise
265
312
  */
266
313
  async terminate(): Promise<void> {
267
314
  if (this.worker) {
268
- await this.worker.terminate();
269
- this.worker = null;
315
+ await this.worker.terminate()
316
+ this.worker = null
270
317
  }
271
-
318
+
272
319
  if (this.ocrWorker) {
273
- this.ocrWorker.terminate();
274
- this.ocrWorker = null;
320
+ this.ocrWorker.terminate()
321
+ this.ocrWorker = null
275
322
  }
276
-
277
- this.initialized = false;
278
- this.options.logger?.('OCR引擎已终止');
323
+
324
+ this.initialized = false
325
+ this.options.logger?.("OCR引擎已终止")
279
326
  }
280
-
327
+
281
328
  /**
282
329
  * 释放资源
283
330
  */
284
331
  dispose(): Promise<void> {
285
- return this.terminate();
332
+ return this.terminate()
286
333
  }
287
- }
334
+ }