id-scanner-lib 1.0.0 → 1.2.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.
- package/README.md +173 -0
- package/dist/core.d.ts +77 -0
- package/dist/id-recognition/data-extractor.d.ts +31 -0
- package/dist/id-recognition/id-detector.d.ts +25 -1
- package/dist/id-scanner-core.esm.js +10870 -0
- package/dist/id-scanner-core.esm.js.map +1 -0
- package/dist/id-scanner-core.js +10882 -0
- package/dist/id-scanner-core.js.map +1 -0
- package/dist/id-scanner-core.min.js +9 -0
- package/dist/id-scanner-core.min.js.map +1 -0
- package/dist/id-scanner-ocr.esm.js +1625 -0
- package/dist/id-scanner-ocr.esm.js.map +1 -0
- package/dist/id-scanner-ocr.js +1634 -0
- package/dist/id-scanner-ocr.js.map +1 -0
- package/dist/id-scanner-ocr.min.js +9 -0
- package/dist/id-scanner-ocr.min.js.map +1 -0
- package/dist/id-scanner-qr.esm.js +773 -0
- package/dist/id-scanner-qr.esm.js.map +1 -0
- package/dist/id-scanner-qr.js +782 -0
- package/dist/id-scanner-qr.js.map +1 -0
- package/dist/id-scanner-qr.min.js +9 -0
- package/dist/id-scanner-qr.min.js.map +1 -0
- package/dist/id-scanner.js +1954 -94656
- package/dist/id-scanner.js.map +1 -1
- package/dist/id-scanner.min.js +7 -7
- package/dist/id-scanner.min.js.map +1 -1
- package/dist/index-umd.d.ts +96 -0
- package/dist/index.d.ts +23 -88
- package/dist/ocr-module.d.ts +67 -0
- package/dist/qr-module.d.ts +68 -0
- package/dist/types/core.d.ts +77 -0
- package/dist/types/demo/demo.d.ts +14 -0
- package/dist/types/id-recognition/data-extractor.d.ts +105 -0
- package/dist/types/id-recognition/id-detector.d.ts +100 -0
- package/dist/types/id-recognition/ocr-processor.d.ts +64 -0
- package/dist/types/index-umd.d.ts +96 -0
- package/dist/types/index.d.ts +78 -0
- package/dist/types/ocr-module.d.ts +67 -0
- package/dist/types/qr-module.d.ts +68 -0
- package/dist/types/scanner/barcode-scanner.d.ts +90 -0
- package/dist/types/scanner/qr-scanner.d.ts +80 -0
- package/dist/types/utils/camera.d.ts +81 -0
- package/dist/types/utils/image-processing.d.ts +75 -0
- package/dist/types/utils/types.d.ts +65 -0
- package/dist/utils/camera.d.ts +18 -13
- package/dist/utils/types.d.ts +6 -6
- package/package.json +25 -4
- package/src/core.ts +138 -0
- package/src/id-recognition/data-extractor.ts +97 -0
- package/src/id-recognition/id-detector.ts +230 -67
- package/src/id-recognition/ocr-processor.ts +145 -27
- package/src/id-recognition/ocr-worker.ts +146 -0
- package/src/index-umd.ts +240 -0
- package/src/index.ts +125 -139
- package/src/ocr-module.ts +139 -0
- package/src/qr-module.ts +129 -0
- package/src/utils/camera.ts +61 -36
- package/src/utils/image-processing.ts +204 -0
- package/src/utils/performance.ts +208 -0
- package/src/utils/resource-manager.ts +198 -0
- package/src/utils/types.ts +23 -6
- package/src/utils/worker.ts +173 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file OCR Worker处理模块
|
|
3
|
+
* @description 用于在Web Worker中执行OCR处理
|
|
4
|
+
* @module OCRWorker
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { IDCardInfo } from '../utils/types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* OCR处理输入接口
|
|
11
|
+
*/
|
|
12
|
+
export interface OCRProcessInput {
|
|
13
|
+
imageBase64: string;
|
|
14
|
+
tessWorkerOptions?: any;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* OCR处理输出接口
|
|
19
|
+
*/
|
|
20
|
+
export interface OCRProcessOutput {
|
|
21
|
+
idCardInfo: IDCardInfo;
|
|
22
|
+
processingTime: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 在Web Worker中执行OCR处理的函数
|
|
27
|
+
*
|
|
28
|
+
* 该函数用于在使用 createWorker 创建的 Worker 中执行
|
|
29
|
+
*
|
|
30
|
+
* @param input OCR处理输入数据
|
|
31
|
+
* @returns OCR处理结果
|
|
32
|
+
*/
|
|
33
|
+
export async function processOCRInWorker(input: OCRProcessInput): Promise<OCRProcessOutput> {
|
|
34
|
+
// 计时开始
|
|
35
|
+
const startTime = performance.now();
|
|
36
|
+
|
|
37
|
+
// 加载Tesseract.js (Worker 环境下动态导入)
|
|
38
|
+
const { createWorker } = await import('tesseract.js');
|
|
39
|
+
|
|
40
|
+
// 创建OCR Worker
|
|
41
|
+
const worker = createWorker(input.tessWorkerOptions || {
|
|
42
|
+
logger: (m: any) => console.log(m)
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
// 初始化OCR引擎
|
|
47
|
+
await worker.load();
|
|
48
|
+
await worker.loadLanguage('chi_sim');
|
|
49
|
+
await worker.initialize('chi_sim');
|
|
50
|
+
await worker.setParameters({
|
|
51
|
+
tessedit_char_whitelist: '0123456789X-年月日一二三四五六七八九十零壹贰叁肆伍陆柒捌玖拾ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz民族汉族满族回族维吾尔族藏族苗族彝族壮族朝鲜族侗族瑶族白族土家族哈尼族哈萨克族傣族黎族傈僳族佤族高山族拉祜族水族东乡族钠西族景颇族柯尔克孜族士族达斡尔族仫佬族羌族布朗族撒拉族毛南族仡佬族锡伯族阿昌族普米族塔吉克族怒族乌孜别克族俄罗斯族鄂温克族德昂族保安族裕固族京族塔塔尔族独龙族鄂伦春族赫哲族门巴族珞巴族基诺族男女性别住址出生公民身份号码签发机关有效期'
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// 识别图像
|
|
55
|
+
const { data } = await worker.recognize(input.imageBase64);
|
|
56
|
+
|
|
57
|
+
// 解析识别结果
|
|
58
|
+
const idCardInfo = parseIDCardText(data.text);
|
|
59
|
+
|
|
60
|
+
// 处理完成后终止worker
|
|
61
|
+
await worker.terminate();
|
|
62
|
+
|
|
63
|
+
// 计算处理时间
|
|
64
|
+
const processingTime = performance.now() - startTime;
|
|
65
|
+
|
|
66
|
+
// 返回处理结果
|
|
67
|
+
return {
|
|
68
|
+
idCardInfo,
|
|
69
|
+
processingTime
|
|
70
|
+
};
|
|
71
|
+
} catch (error) {
|
|
72
|
+
// 确保资源被释放
|
|
73
|
+
await worker.terminate();
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 解析身份证文本信息
|
|
80
|
+
*
|
|
81
|
+
* 从OCR识别到的文本中提取结构化的身份证信息
|
|
82
|
+
*
|
|
83
|
+
* @private
|
|
84
|
+
* @param {string} text - OCR识别到的文本
|
|
85
|
+
* @returns {IDCardInfo} 提取到的身份证信息对象
|
|
86
|
+
*/
|
|
87
|
+
function parseIDCardText(text: string): IDCardInfo {
|
|
88
|
+
const info: IDCardInfo = {};
|
|
89
|
+
|
|
90
|
+
// 拆分为行
|
|
91
|
+
const lines = text.split('\n').filter(line => line.trim());
|
|
92
|
+
|
|
93
|
+
// 解析身份证号码(最容易识别的部分)
|
|
94
|
+
const idNumberRegex = /(\d{17}[\dX])/;
|
|
95
|
+
const idNumberMatch = text.match(idNumberRegex);
|
|
96
|
+
if (idNumberMatch) {
|
|
97
|
+
info.idNumber = idNumberMatch[1];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 解析姓名
|
|
101
|
+
for (const line of lines) {
|
|
102
|
+
if (line.includes('姓名') || line.length < 10 && line.length > 1 && !/\d/.test(line)) {
|
|
103
|
+
info.name = line.replace('姓名', '').trim();
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 解析性别和民族
|
|
109
|
+
const genderNationalityRegex = /(男|女).*(族)/;
|
|
110
|
+
const genderMatch = text.match(genderNationalityRegex);
|
|
111
|
+
if (genderMatch) {
|
|
112
|
+
info.gender = genderMatch[1];
|
|
113
|
+
const nationalityText = genderMatch[0];
|
|
114
|
+
info.nationality = nationalityText.substring(nationalityText.indexOf(genderMatch[1]) + 1).trim();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 解析出生日期
|
|
118
|
+
const birthDateRegex = /(\d{4})年(\d{1,2})月(\d{1,2})日/;
|
|
119
|
+
const birthDateMatch = text.match(birthDateRegex);
|
|
120
|
+
if (birthDateMatch) {
|
|
121
|
+
info.birthDate = `${birthDateMatch[1]}-${birthDateMatch[2]}-${birthDateMatch[3]}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 解析地址
|
|
125
|
+
const addressRegex = /住址([\s\S]*?)公民身份号码/;
|
|
126
|
+
const addressMatch = text.match(addressRegex);
|
|
127
|
+
if (addressMatch) {
|
|
128
|
+
info.address = addressMatch[1].replace(/\n/g, '').trim();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 解析签发机关
|
|
132
|
+
const authorityRegex = /签发机关([\s\S]*?)有效期/;
|
|
133
|
+
const authorityMatch = text.match(authorityRegex);
|
|
134
|
+
if (authorityMatch) {
|
|
135
|
+
info.issuingAuthority = authorityMatch[1].replace(/\n/g, '').trim();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 解析有效期限
|
|
139
|
+
const validPeriodRegex = /有效期限([\s\S]*?)(-|至)/;
|
|
140
|
+
const validPeriodMatch = text.match(validPeriodRegex);
|
|
141
|
+
if (validPeriodMatch) {
|
|
142
|
+
info.validPeriod = validPeriodMatch[0].replace('有效期限', '').trim();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return info;
|
|
146
|
+
}
|
package/src/index-umd.ts
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ID扫描识别库UMD格式入口文件
|
|
3
|
+
* @description 专门为UMD格式构建的入口,使用静态导入而非动态导入
|
|
4
|
+
* @module IDScannerLib
|
|
5
|
+
* @version 1.1.0
|
|
6
|
+
* @license MIT
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Camera, CameraOptions } from './utils/camera';
|
|
10
|
+
import { IDCardInfo, DetectionResult } from './utils/types';
|
|
11
|
+
import type { QRScannerOptions } from './scanner/qr-scanner';
|
|
12
|
+
import type { BarcodeScannerOptions } from './scanner/barcode-scanner';
|
|
13
|
+
|
|
14
|
+
// 静态导入所有依赖
|
|
15
|
+
import { QRScanner } from './scanner/qr-scanner';
|
|
16
|
+
import { BarcodeScanner } from './scanner/barcode-scanner';
|
|
17
|
+
import { IDCardDetector, IDCardDetectorOptions } from './id-recognition/id-detector';
|
|
18
|
+
import { OCRProcessor } from './id-recognition/ocr-processor';
|
|
19
|
+
import { DataExtractor } from './id-recognition/data-extractor';
|
|
20
|
+
import { ImageProcessor } from './utils/image-processing';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* IDScanner配置选项接口
|
|
24
|
+
*/
|
|
25
|
+
export interface IDScannerOptions {
|
|
26
|
+
cameraOptions?: CameraOptions;
|
|
27
|
+
qrScannerOptions?: QRScannerOptions;
|
|
28
|
+
barcodeScannerOptions?: BarcodeScannerOptions;
|
|
29
|
+
onQRCodeScanned?: (result: string) => void;
|
|
30
|
+
onBarcodeScanned?: (result: string) => void;
|
|
31
|
+
onIDCardScanned?: (info: IDCardInfo) => void;
|
|
32
|
+
onError?: (error: Error) => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* IDScanner 主类
|
|
37
|
+
* UMD版本使用静态导入实现
|
|
38
|
+
*/
|
|
39
|
+
export class IDScanner {
|
|
40
|
+
private camera: Camera;
|
|
41
|
+
private qrScanner: QRScanner | null = null;
|
|
42
|
+
private barcodeScanner: BarcodeScanner | null = null;
|
|
43
|
+
private idDetector: IDCardDetector | null = null;
|
|
44
|
+
private ocrProcessor: OCRProcessor | null = null;
|
|
45
|
+
private dataExtractor: DataExtractor | null = null;
|
|
46
|
+
private scanMode: 'qr' | 'barcode' | 'idcard' = 'qr';
|
|
47
|
+
private videoElement: HTMLVideoElement | null = null;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 构造函数
|
|
51
|
+
* @param options 配置选项
|
|
52
|
+
*/
|
|
53
|
+
constructor(private options: IDScannerOptions = {}) {
|
|
54
|
+
this.camera = new Camera(options.cameraOptions);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 初始化模块
|
|
59
|
+
* 根据需要初始化OCR引擎
|
|
60
|
+
*/
|
|
61
|
+
async initialize(): Promise<void> {
|
|
62
|
+
try {
|
|
63
|
+
// 初始化OCR模块
|
|
64
|
+
this.ocrProcessor = new OCRProcessor();
|
|
65
|
+
this.dataExtractor = new DataExtractor();
|
|
66
|
+
await this.ocrProcessor.initialize();
|
|
67
|
+
|
|
68
|
+
console.log('IDScanner initialized');
|
|
69
|
+
} catch (error) {
|
|
70
|
+
this.handleError(error as Error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 启动二维码扫描
|
|
77
|
+
* @param videoElement HTML视频元素
|
|
78
|
+
*/
|
|
79
|
+
async startQRScanner(videoElement: HTMLVideoElement): Promise<void> {
|
|
80
|
+
this.stop();
|
|
81
|
+
this.videoElement = videoElement;
|
|
82
|
+
this.scanMode = 'qr';
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
if (!this.qrScanner) {
|
|
86
|
+
this.qrScanner = new QRScanner({
|
|
87
|
+
...this.options.qrScannerOptions,
|
|
88
|
+
onScan: this.handleQRScan.bind(this)
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
await this.camera.start(videoElement);
|
|
93
|
+
this.qrScanner.start(videoElement);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
this.handleError(error as Error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 启动条形码扫描
|
|
101
|
+
* @param videoElement HTML视频元素
|
|
102
|
+
*/
|
|
103
|
+
async startBarcodeScanner(videoElement: HTMLVideoElement): Promise<void> {
|
|
104
|
+
this.stop();
|
|
105
|
+
this.videoElement = videoElement;
|
|
106
|
+
this.scanMode = 'barcode';
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
if (!this.barcodeScanner) {
|
|
110
|
+
this.barcodeScanner = new BarcodeScanner({
|
|
111
|
+
...this.options.barcodeScannerOptions,
|
|
112
|
+
onScan: this.handleBarcodeScan.bind(this)
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
await this.camera.start(videoElement);
|
|
117
|
+
this.barcodeScanner.start(videoElement);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
this.handleError(error as Error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 启动身份证扫描
|
|
125
|
+
* @param videoElement HTML视频元素
|
|
126
|
+
*/
|
|
127
|
+
async startIDCardScanner(videoElement: HTMLVideoElement): Promise<void> {
|
|
128
|
+
this.stop();
|
|
129
|
+
this.videoElement = videoElement;
|
|
130
|
+
this.scanMode = 'idcard';
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
if (!this.ocrProcessor) {
|
|
134
|
+
await this.initialize();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!this.idDetector) {
|
|
138
|
+
this.idDetector = new IDCardDetector({
|
|
139
|
+
onDetection: this.handleIDDetection.bind(this),
|
|
140
|
+
onError: this.handleError.bind(this)
|
|
141
|
+
} as IDCardDetectorOptions);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
await this.camera.start(videoElement);
|
|
145
|
+
this.idDetector.start(videoElement);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
this.handleError(error as Error);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 停止扫描
|
|
153
|
+
*/
|
|
154
|
+
stop(): void {
|
|
155
|
+
if (this.scanMode === 'qr' && this.qrScanner) {
|
|
156
|
+
this.qrScanner.stop();
|
|
157
|
+
} else if (this.scanMode === 'barcode' && this.barcodeScanner) {
|
|
158
|
+
this.barcodeScanner.stop();
|
|
159
|
+
} else if (this.scanMode === 'idcard' && this.idDetector) {
|
|
160
|
+
this.idDetector.stop();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
this.camera.stop();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 处理二维码扫描结果
|
|
168
|
+
*/
|
|
169
|
+
private handleQRScan(result: string): void {
|
|
170
|
+
if (this.options.onQRCodeScanned) {
|
|
171
|
+
this.options.onQRCodeScanned(result);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 处理条形码扫描结果
|
|
177
|
+
*/
|
|
178
|
+
private handleBarcodeScan(result: string): void {
|
|
179
|
+
if (this.options.onBarcodeScanned) {
|
|
180
|
+
this.options.onBarcodeScanned(result);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 处理身份证检测结果
|
|
186
|
+
*/
|
|
187
|
+
private async handleIDDetection(result: DetectionResult): Promise<void> {
|
|
188
|
+
if (!this.ocrProcessor || !this.dataExtractor) return;
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
// 检查 imageData 是否存在
|
|
192
|
+
if (!result.imageData) {
|
|
193
|
+
this.handleError(new Error('无效的图像数据'));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const idCardInfo = await this.ocrProcessor.processIDCard(result.imageData);
|
|
198
|
+
const extractedInfo = this.dataExtractor.extractAndValidate(idCardInfo);
|
|
199
|
+
|
|
200
|
+
if (this.options.onIDCardScanned) {
|
|
201
|
+
this.options.onIDCardScanned(extractedInfo);
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
this.handleError(error as Error);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 处理错误
|
|
210
|
+
*/
|
|
211
|
+
private handleError(error: Error): void {
|
|
212
|
+
if (this.options.onError) {
|
|
213
|
+
this.options.onError(error);
|
|
214
|
+
} else {
|
|
215
|
+
console.error('IDScanner error:', error);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* 释放资源
|
|
221
|
+
*/
|
|
222
|
+
async terminate(): Promise<void> {
|
|
223
|
+
this.stop();
|
|
224
|
+
|
|
225
|
+
if (this.ocrProcessor) {
|
|
226
|
+
await this.ocrProcessor.terminate();
|
|
227
|
+
this.ocrProcessor = null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
this.qrScanner = null;
|
|
231
|
+
this.barcodeScanner = null;
|
|
232
|
+
this.idDetector = null;
|
|
233
|
+
this.dataExtractor = null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 导出核心类型
|
|
238
|
+
export { IDCardInfo } from './utils/types';
|
|
239
|
+
export { CameraOptions } from './utils/camera';
|
|
240
|
+
export { QRScanner, BarcodeScanner, IDCardDetector, OCRProcessor, DataExtractor, ImageProcessor };
|