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
package/src/index.ts
CHANGED
|
@@ -6,25 +6,15 @@
|
|
|
6
6
|
* @license MIT
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { QRScanner, QRScannerOptions } from './scanner/qr-scanner';
|
|
10
|
-
import { BarcodeScanner, BarcodeScannerOptions } from './scanner/barcode-scanner';
|
|
11
|
-
import { IDCardDetector } from './id-recognition/id-detector';
|
|
12
|
-
import { OCRProcessor } from './id-recognition/ocr-processor';
|
|
13
|
-
import { DataExtractor } from './id-recognition/data-extractor';
|
|
14
9
|
import { Camera, CameraOptions } from './utils/camera';
|
|
15
|
-
import { ImageProcessor } from './utils/image-processing';
|
|
16
10
|
import { IDCardInfo, DetectionResult } from './utils/types';
|
|
17
11
|
|
|
12
|
+
// 先只导入类型定义,不导入实际实现
|
|
13
|
+
import type { QRScannerOptions } from './scanner/qr-scanner';
|
|
14
|
+
import type { BarcodeScannerOptions } from './scanner/barcode-scanner';
|
|
15
|
+
|
|
18
16
|
/**
|
|
19
17
|
* IDScanner配置选项接口
|
|
20
|
-
* @interface IDScannerOptions
|
|
21
|
-
* @property {CameraOptions} [cameraOptions] - 相机配置选项
|
|
22
|
-
* @property {QRScannerOptions} [qrScannerOptions] - 二维码扫描配置选项
|
|
23
|
-
* @property {BarcodeScannerOptions} [barcodeScannerOptions] - 条形码扫描配置选项
|
|
24
|
-
* @property {Function} [onQRCodeScanned] - 二维码识别成功回调
|
|
25
|
-
* @property {Function} [onBarcodeScanned] - 条形码识别成功回调
|
|
26
|
-
* @property {Function} [onIDCardScanned] - 身份证识别成功回调
|
|
27
|
-
* @property {Function} [onError] - 错误处理回调
|
|
28
18
|
*/
|
|
29
19
|
export interface IDScannerOptions {
|
|
30
20
|
cameraOptions?: CameraOptions;
|
|
@@ -40,189 +30,185 @@ export interface IDScannerOptions {
|
|
|
40
30
|
* IDScanner 主类
|
|
41
31
|
*
|
|
42
32
|
* 整合二维码、条形码扫描和身份证识别功能,提供统一的接口
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* ```typescript
|
|
46
|
-
* // 创建扫描器实例
|
|
47
|
-
* const scanner = new IDScanner({
|
|
48
|
-
* onQRCodeScanned: (result) => {
|
|
49
|
-
* console.log('扫描到二维码:', result);
|
|
50
|
-
* },
|
|
51
|
-
* onIDCardScanned: (info) => {
|
|
52
|
-
* console.log('识别到身份证信息:', info);
|
|
53
|
-
* }
|
|
54
|
-
* });
|
|
55
|
-
*
|
|
56
|
-
* // 初始化OCR引擎和相关资源
|
|
57
|
-
* await scanner.initialize();
|
|
58
|
-
*
|
|
59
|
-
* // 启动二维码扫描
|
|
60
|
-
* const videoElement = document.getElementById('video');
|
|
61
|
-
* await scanner.startQRScanner(videoElement);
|
|
62
|
-
*
|
|
63
|
-
* // 停止扫描
|
|
64
|
-
* scanner.stop();
|
|
65
|
-
*
|
|
66
|
-
* // 使用结束后释放资源
|
|
67
|
-
* scanner.terminate();
|
|
68
|
-
* ```
|
|
33
|
+
* 使用动态导入实现按需加载
|
|
69
34
|
*/
|
|
70
35
|
export class IDScanner {
|
|
71
|
-
private
|
|
72
|
-
private barcodeScanner: BarcodeScanner;
|
|
73
|
-
private idDetector: IDCardDetector;
|
|
74
|
-
private ocrProcessor: OCRProcessor;
|
|
36
|
+
private camera: Camera;
|
|
75
37
|
private scanMode: 'qr' | 'barcode' | 'idcard' = 'qr';
|
|
38
|
+
private videoElement: HTMLVideoElement | null = null;
|
|
39
|
+
|
|
40
|
+
// 延迟加载的模块
|
|
41
|
+
private qrModule: any = null;
|
|
42
|
+
private ocrModule: any = null;
|
|
43
|
+
|
|
44
|
+
// 模块加载状态
|
|
45
|
+
private isQRModuleLoaded: boolean = false;
|
|
46
|
+
private isOCRModuleLoaded: boolean = false;
|
|
76
47
|
|
|
77
48
|
/**
|
|
78
|
-
*
|
|
79
|
-
* @param
|
|
49
|
+
* 构造函数
|
|
50
|
+
* @param options 配置选项
|
|
80
51
|
*/
|
|
81
52
|
constructor(private options: IDScannerOptions = {}) {
|
|
82
|
-
this.
|
|
83
|
-
...options.qrScannerOptions,
|
|
84
|
-
onScan: this.handleQRScan.bind(this),
|
|
85
|
-
onError: this.handleError.bind(this)
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
this.barcodeScanner = new BarcodeScanner({
|
|
89
|
-
...options.barcodeScannerOptions,
|
|
90
|
-
onScan: this.handleBarcodeScan.bind(this),
|
|
91
|
-
onError: this.handleError.bind(this)
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
this.idDetector = new IDCardDetector(this.handleIDDetection.bind(this));
|
|
95
|
-
this.ocrProcessor = new OCRProcessor();
|
|
53
|
+
this.camera = new Camera(options.cameraOptions);
|
|
96
54
|
}
|
|
97
55
|
|
|
98
56
|
/**
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* @returns {Promise<void>} 初始化完成的Promise
|
|
102
|
-
* @throws 如果初始化失败,将抛出错误
|
|
57
|
+
* 初始化模块
|
|
58
|
+
* 根据需要初始化OCR引擎
|
|
103
59
|
*/
|
|
104
60
|
async initialize(): Promise<void> {
|
|
105
61
|
try {
|
|
106
|
-
|
|
62
|
+
// 预加载OCR模块但不初始化
|
|
63
|
+
if (!this.isOCRModuleLoaded) {
|
|
64
|
+
// 动态导入OCR模块
|
|
65
|
+
const OCRModule = await import('./ocr-module').then(m => m.OCRModule);
|
|
66
|
+
this.ocrModule = new OCRModule({
|
|
67
|
+
cameraOptions: this.options.cameraOptions,
|
|
68
|
+
onIDCardScanned: this.options.onIDCardScanned,
|
|
69
|
+
onError: this.options.onError
|
|
70
|
+
});
|
|
71
|
+
this.isOCRModuleLoaded = true;
|
|
72
|
+
|
|
73
|
+
// 初始化OCR模块
|
|
74
|
+
await this.ocrModule.initialize();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log('IDScanner initialized');
|
|
107
78
|
} catch (error) {
|
|
108
|
-
this.handleError(error
|
|
79
|
+
this.handleError(error as Error);
|
|
80
|
+
throw error;
|
|
109
81
|
}
|
|
110
82
|
}
|
|
111
83
|
|
|
112
84
|
/**
|
|
113
85
|
* 启动二维码扫描
|
|
114
|
-
*
|
|
115
|
-
* @param {HTMLVideoElement} videoElement - 用于显示相机画面的video元素
|
|
116
|
-
* @returns {Promise<void>} 启动完成的Promise
|
|
86
|
+
* @param videoElement HTML视频元素
|
|
117
87
|
*/
|
|
118
88
|
async startQRScanner(videoElement: HTMLVideoElement): Promise<void> {
|
|
89
|
+
this.stop();
|
|
90
|
+
this.videoElement = videoElement;
|
|
119
91
|
this.scanMode = 'qr';
|
|
120
|
-
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// 动态加载二维码模块
|
|
95
|
+
if (!this.isQRModuleLoaded) {
|
|
96
|
+
const ScannerModule = await import('./qr-module').then(m => m.ScannerModule);
|
|
97
|
+
this.qrModule = new ScannerModule({
|
|
98
|
+
cameraOptions: this.options.cameraOptions,
|
|
99
|
+
qrScannerOptions: this.options.qrScannerOptions,
|
|
100
|
+
barcodeScannerOptions: this.options.barcodeScannerOptions,
|
|
101
|
+
onQRCodeScanned: this.options.onQRCodeScanned,
|
|
102
|
+
onBarcodeScanned: this.options.onBarcodeScanned,
|
|
103
|
+
onError: this.options.onError
|
|
104
|
+
});
|
|
105
|
+
this.isQRModuleLoaded = true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
await this.qrModule.startQRScanner(videoElement);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
this.handleError(error as Error);
|
|
111
|
+
}
|
|
121
112
|
}
|
|
122
113
|
|
|
123
114
|
/**
|
|
124
115
|
* 启动条形码扫描
|
|
125
|
-
*
|
|
126
|
-
* @param {HTMLVideoElement} videoElement - 用于显示相机画面的video元素
|
|
127
|
-
* @returns {Promise<void>} 启动完成的Promise
|
|
116
|
+
* @param videoElement HTML视频元素
|
|
128
117
|
*/
|
|
129
118
|
async startBarcodeScanner(videoElement: HTMLVideoElement): Promise<void> {
|
|
119
|
+
this.stop();
|
|
120
|
+
this.videoElement = videoElement;
|
|
130
121
|
this.scanMode = 'barcode';
|
|
131
|
-
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
// 动态加载二维码模块
|
|
125
|
+
if (!this.isQRModuleLoaded) {
|
|
126
|
+
const ScannerModule = await import('./qr-module').then(m => m.ScannerModule);
|
|
127
|
+
this.qrModule = new ScannerModule({
|
|
128
|
+
cameraOptions: this.options.cameraOptions,
|
|
129
|
+
qrScannerOptions: this.options.qrScannerOptions,
|
|
130
|
+
barcodeScannerOptions: this.options.barcodeScannerOptions,
|
|
131
|
+
onQRCodeScanned: this.options.onQRCodeScanned,
|
|
132
|
+
onBarcodeScanned: this.options.onBarcodeScanned,
|
|
133
|
+
onError: this.options.onError
|
|
134
|
+
});
|
|
135
|
+
this.isQRModuleLoaded = true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
await this.qrModule.startBarcodeScanner(videoElement);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
this.handleError(error as Error);
|
|
141
|
+
}
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* @param {HTMLVideoElement} videoElement - 用于显示相机画面的video元素
|
|
138
|
-
* @returns {Promise<void>} 启动完成的Promise
|
|
145
|
+
* 启动身份证扫描
|
|
146
|
+
* @param videoElement HTML视频元素
|
|
139
147
|
*/
|
|
140
148
|
async startIDCardScanner(videoElement: HTMLVideoElement): Promise<void> {
|
|
149
|
+
this.stop();
|
|
150
|
+
this.videoElement = videoElement;
|
|
141
151
|
this.scanMode = 'idcard';
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
this.barcodeScanner.stop();
|
|
153
|
-
} else {
|
|
154
|
-
this.idDetector.stop();
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* 处理二维码扫描结果
|
|
160
|
-
* @private
|
|
161
|
-
* @param {string} result - 扫描到的二维码内容
|
|
162
|
-
*/
|
|
163
|
-
private handleQRScan(result: string): void {
|
|
164
|
-
if (this.options.onQRCodeScanned) {
|
|
165
|
-
this.options.onQRCodeScanned(result);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* 处理条形码扫描结果
|
|
171
|
-
* @private
|
|
172
|
-
* @param {string} result - 扫描到的条形码内容
|
|
173
|
-
*/
|
|
174
|
-
private handleBarcodeScan(result: string): void {
|
|
175
|
-
if (this.options.onBarcodeScanned) {
|
|
176
|
-
this.options.onBarcodeScanned(result);
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
// 检查OCR模块是否已加载,若未加载则自动初始化
|
|
155
|
+
if (!this.isOCRModuleLoaded) {
|
|
156
|
+
await this.initialize();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
await this.ocrModule.startIDCardScanner(videoElement);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
this.handleError(error as Error);
|
|
177
162
|
}
|
|
178
163
|
}
|
|
179
164
|
|
|
180
165
|
/**
|
|
181
|
-
*
|
|
182
|
-
* @private
|
|
183
|
-
* @param {DetectionResult} result - 身份证检测结果
|
|
166
|
+
* 停止扫描
|
|
184
167
|
*/
|
|
185
|
-
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (this.options.onIDCardScanned) {
|
|
194
|
-
this.options.onIDCardScanned(enhancedInfo);
|
|
195
|
-
}
|
|
196
|
-
} catch (error) {
|
|
197
|
-
this.handleError(error instanceof Error ? error : new Error(String(error)));
|
|
168
|
+
stop(): void {
|
|
169
|
+
if (this.scanMode === 'qr' || this.scanMode === 'barcode') {
|
|
170
|
+
if (this.qrModule) {
|
|
171
|
+
this.qrModule.stop();
|
|
172
|
+
}
|
|
173
|
+
} else if (this.scanMode === 'idcard') {
|
|
174
|
+
if (this.ocrModule) {
|
|
175
|
+
this.ocrModule.stop();
|
|
198
176
|
}
|
|
199
177
|
}
|
|
200
178
|
}
|
|
201
179
|
|
|
202
180
|
/**
|
|
203
181
|
* 处理错误
|
|
204
|
-
* @private
|
|
205
|
-
* @param {Error} error - 错误对象
|
|
206
182
|
*/
|
|
207
183
|
private handleError(error: Error): void {
|
|
208
184
|
if (this.options.onError) {
|
|
209
185
|
this.options.onError(error);
|
|
210
186
|
} else {
|
|
211
|
-
console.error('
|
|
187
|
+
console.error('IDScanner error:', error);
|
|
212
188
|
}
|
|
213
189
|
}
|
|
214
190
|
|
|
215
191
|
/**
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
* @returns {Promise<void>} 资源释放完成的Promise
|
|
192
|
+
* 释放资源
|
|
219
193
|
*/
|
|
220
194
|
async terminate(): Promise<void> {
|
|
221
195
|
this.stop();
|
|
222
|
-
|
|
196
|
+
|
|
197
|
+
// 释放OCR资源
|
|
198
|
+
if (this.isOCRModuleLoaded && this.ocrModule) {
|
|
199
|
+
await this.ocrModule.terminate();
|
|
200
|
+
this.ocrModule = null;
|
|
201
|
+
this.isOCRModuleLoaded = false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 释放QR扫描资源
|
|
205
|
+
if (this.isQRModuleLoaded && this.qrModule) {
|
|
206
|
+
this.qrModule = null;
|
|
207
|
+
this.isQRModuleLoaded = false;
|
|
208
|
+
}
|
|
223
209
|
}
|
|
224
210
|
}
|
|
225
211
|
|
|
226
|
-
//
|
|
227
|
-
export {
|
|
228
|
-
export
|
|
212
|
+
// 导出核心类型
|
|
213
|
+
export { IDCardInfo } from './utils/types';
|
|
214
|
+
export { CameraOptions } from './utils/camera';
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file OCR模块入口文件
|
|
3
|
+
* @description 包含身份证OCR识别相关功能
|
|
4
|
+
* @module IDScannerOCR
|
|
5
|
+
* @version 1.0.0
|
|
6
|
+
* @license MIT
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Camera, CameraOptions } from './utils/camera';
|
|
10
|
+
import { ImageProcessor } from './utils/image-processing';
|
|
11
|
+
import { IDCardInfo, DetectionResult } from './utils/types';
|
|
12
|
+
import { IDCardDetector, IDCardDetectorOptions } from './id-recognition/id-detector';
|
|
13
|
+
import { OCRProcessor } from './id-recognition/ocr-processor';
|
|
14
|
+
import { DataExtractor } from './id-recognition/data-extractor';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* OCR模块配置选项
|
|
18
|
+
*/
|
|
19
|
+
export interface OCRModuleOptions {
|
|
20
|
+
cameraOptions?: CameraOptions;
|
|
21
|
+
onIDCardScanned?: (info: IDCardInfo) => void;
|
|
22
|
+
onError?: (error: Error) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* OCR模块类
|
|
27
|
+
*
|
|
28
|
+
* 提供身份证检测和OCR文字识别功能
|
|
29
|
+
*/
|
|
30
|
+
export class OCRModule {
|
|
31
|
+
private idDetector: IDCardDetector;
|
|
32
|
+
private ocrProcessor: OCRProcessor;
|
|
33
|
+
private dataExtractor: DataExtractor;
|
|
34
|
+
private camera: Camera;
|
|
35
|
+
private isRunning: boolean = false;
|
|
36
|
+
private videoElement: HTMLVideoElement | null = null;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 构造函数
|
|
40
|
+
* @param options 配置选项
|
|
41
|
+
*/
|
|
42
|
+
constructor(private options: OCRModuleOptions = {}) {
|
|
43
|
+
this.camera = new Camera(options.cameraOptions);
|
|
44
|
+
this.idDetector = new IDCardDetector({
|
|
45
|
+
onDetection: this.handleIDDetection.bind(this),
|
|
46
|
+
onError: this.handleError.bind(this)
|
|
47
|
+
} as IDCardDetectorOptions);
|
|
48
|
+
this.ocrProcessor = new OCRProcessor();
|
|
49
|
+
this.dataExtractor = new DataExtractor();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 初始化OCR引擎
|
|
54
|
+
*
|
|
55
|
+
* @returns Promise<void>
|
|
56
|
+
*/
|
|
57
|
+
async initialize(): Promise<void> {
|
|
58
|
+
try {
|
|
59
|
+
await this.ocrProcessor.initialize();
|
|
60
|
+
console.log('OCR engine initialized');
|
|
61
|
+
} catch (error) {
|
|
62
|
+
this.handleError(error as Error);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 启动身份证扫描
|
|
69
|
+
* @param videoElement HTML视频元素
|
|
70
|
+
*/
|
|
71
|
+
async startIDCardScanner(videoElement: HTMLVideoElement): Promise<void> {
|
|
72
|
+
if (!this.ocrProcessor) {
|
|
73
|
+
throw new Error('OCR engine not initialized. Call initialize() first.');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.videoElement = videoElement;
|
|
77
|
+
this.isRunning = true;
|
|
78
|
+
await this.camera.start(videoElement);
|
|
79
|
+
this.idDetector.start(videoElement);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 停止扫描
|
|
84
|
+
*/
|
|
85
|
+
stop(): void {
|
|
86
|
+
this.isRunning = false;
|
|
87
|
+
this.idDetector.stop();
|
|
88
|
+
this.camera.stop();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 处理身份证检测结果
|
|
93
|
+
*/
|
|
94
|
+
private async handleIDDetection(result: DetectionResult): Promise<void> {
|
|
95
|
+
if (!this.isRunning) return;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
// 检查 imageData 是否存在
|
|
99
|
+
if (!result.imageData) {
|
|
100
|
+
this.handleError(new Error('无效的图像数据'));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const idCardInfo = await this.ocrProcessor.processIDCard(result.imageData);
|
|
105
|
+
const extractedInfo = this.dataExtractor.extractAndValidate(idCardInfo);
|
|
106
|
+
|
|
107
|
+
if (this.options.onIDCardScanned) {
|
|
108
|
+
this.options.onIDCardScanned(extractedInfo);
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
this.handleError(error as Error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 处理错误
|
|
117
|
+
*/
|
|
118
|
+
private handleError(error: Error): void {
|
|
119
|
+
if (this.options.onError) {
|
|
120
|
+
this.options.onError(error);
|
|
121
|
+
} else {
|
|
122
|
+
console.error('OCRModule error:', error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 释放资源
|
|
128
|
+
*/
|
|
129
|
+
async terminate(): Promise<void> {
|
|
130
|
+
this.stop();
|
|
131
|
+
await this.ocrProcessor.terminate();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 导出相关类型和工具
|
|
136
|
+
export { IDCardDetector } from './id-recognition/id-detector';
|
|
137
|
+
export { OCRProcessor } from './id-recognition/ocr-processor';
|
|
138
|
+
export { DataExtractor } from './id-recognition/data-extractor';
|
|
139
|
+
export { IDCardInfo } from './utils/types';
|
package/src/qr-module.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file 二维码和条形码扫描模块
|
|
3
|
+
* @description 包含二维码和条形码扫描功能
|
|
4
|
+
* @module IDScannerQR
|
|
5
|
+
* @version 1.0.0
|
|
6
|
+
* @license MIT
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { QRScanner, QRScannerOptions } from './scanner/qr-scanner';
|
|
10
|
+
import { BarcodeScanner, BarcodeScannerOptions } from './scanner/barcode-scanner';
|
|
11
|
+
import { Camera, CameraOptions } from './utils/camera';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 扫描模块配置选项
|
|
15
|
+
*/
|
|
16
|
+
export interface ScannerModuleOptions {
|
|
17
|
+
cameraOptions?: CameraOptions;
|
|
18
|
+
qrScannerOptions?: QRScannerOptions;
|
|
19
|
+
barcodeScannerOptions?: BarcodeScannerOptions;
|
|
20
|
+
onQRCodeScanned?: (result: string) => void;
|
|
21
|
+
onBarcodeScanned?: (result: string) => void;
|
|
22
|
+
onError?: (error: Error) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 扫描模块类
|
|
27
|
+
*
|
|
28
|
+
* 提供独立的二维码和条形码扫描功能
|
|
29
|
+
*/
|
|
30
|
+
export class ScannerModule {
|
|
31
|
+
private qrScanner: QRScanner;
|
|
32
|
+
private barcodeScanner: BarcodeScanner;
|
|
33
|
+
private camera: Camera;
|
|
34
|
+
private scanMode: 'qr' | 'barcode' | null = null;
|
|
35
|
+
private videoElement: HTMLVideoElement | null = null;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 构造函数
|
|
39
|
+
* @param options 配置选项
|
|
40
|
+
*/
|
|
41
|
+
constructor(private options: ScannerModuleOptions = {}) {
|
|
42
|
+
this.camera = new Camera(options.cameraOptions);
|
|
43
|
+
this.qrScanner = new QRScanner({
|
|
44
|
+
...options.qrScannerOptions,
|
|
45
|
+
onScan: this.handleQRScan.bind(this)
|
|
46
|
+
});
|
|
47
|
+
this.barcodeScanner = new BarcodeScanner({
|
|
48
|
+
...options.barcodeScannerOptions,
|
|
49
|
+
onScan: this.handleBarcodeScan.bind(this)
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 启动二维码扫描
|
|
55
|
+
* @param videoElement HTML视频元素
|
|
56
|
+
*/
|
|
57
|
+
async startQRScanner(videoElement: HTMLVideoElement): Promise<void> {
|
|
58
|
+
this.stop(); // 确保先停止可能正在运行的扫描
|
|
59
|
+
|
|
60
|
+
this.videoElement = videoElement;
|
|
61
|
+
this.scanMode = 'qr';
|
|
62
|
+
await this.camera.start(videoElement);
|
|
63
|
+
this.qrScanner.start(videoElement);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 启动条形码扫描
|
|
68
|
+
* @param videoElement HTML视频元素
|
|
69
|
+
*/
|
|
70
|
+
async startBarcodeScanner(videoElement: HTMLVideoElement): Promise<void> {
|
|
71
|
+
this.stop(); // 确保先停止可能正在运行的扫描
|
|
72
|
+
|
|
73
|
+
this.videoElement = videoElement;
|
|
74
|
+
this.scanMode = 'barcode';
|
|
75
|
+
await this.camera.start(videoElement);
|
|
76
|
+
this.barcodeScanner.start(videoElement);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 停止扫描
|
|
81
|
+
*/
|
|
82
|
+
stop(): void {
|
|
83
|
+
if (this.scanMode === 'qr') {
|
|
84
|
+
this.qrScanner.stop();
|
|
85
|
+
} else if (this.scanMode === 'barcode') {
|
|
86
|
+
this.barcodeScanner.stop();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (this.videoElement) {
|
|
90
|
+
this.camera.stop();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.scanMode = null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 处理二维码扫描结果
|
|
98
|
+
*/
|
|
99
|
+
private handleQRScan(result: string): void {
|
|
100
|
+
if (this.options.onQRCodeScanned) {
|
|
101
|
+
this.options.onQRCodeScanned(result);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 处理条形码扫描结果
|
|
107
|
+
*/
|
|
108
|
+
private handleBarcodeScan(result: string): void {
|
|
109
|
+
if (this.options.onBarcodeScanned) {
|
|
110
|
+
this.options.onBarcodeScanned(result);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 处理错误
|
|
116
|
+
*/
|
|
117
|
+
private handleError(error: Error): void {
|
|
118
|
+
if (this.options.onError) {
|
|
119
|
+
this.options.onError(error);
|
|
120
|
+
} else {
|
|
121
|
+
console.error('ScannerModule error:', error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 导出相关类型和工具
|
|
127
|
+
export { QRScanner, QRScannerOptions } from './scanner/qr-scanner';
|
|
128
|
+
export { BarcodeScanner, BarcodeScannerOptions } from './scanner/barcode-scanner';
|
|
129
|
+
export { Camera, CameraOptions } from './utils/camera';
|