id-scanner-lib 1.3.3 → 1.5.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 +55 -460
- package/dist/id-scanner-lib.esm.js +4641 -0
- package/dist/id-scanner-lib.esm.js.map +1 -0
- package/dist/id-scanner-lib.js +14755 -0
- package/dist/id-scanner-lib.js.map +1 -0
- package/dist/types/core/base-module.d.ts +44 -0
- package/dist/types/core/camera-manager.d.ts +258 -0
- package/dist/types/core/config.d.ts +88 -0
- package/dist/types/core/errors.d.ts +111 -0
- package/dist/types/core/event-emitter.d.ts +55 -0
- package/dist/types/core/logger.d.ts +277 -0
- package/dist/types/core/module-manager.d.ts +78 -0
- package/dist/types/core/plugin-manager.d.ts +158 -0
- package/dist/types/core/resource-manager.d.ts +246 -0
- package/dist/types/core/result.d.ts +83 -0
- package/dist/types/core/scanner-factory.d.ts +93 -0
- package/dist/types/index.bundle.d.ts +1303 -0
- package/dist/types/index.d.ts +86 -0
- package/dist/types/interfaces/external-types.d.ts +174 -0
- package/dist/types/interfaces/face-detection.d.ts +293 -0
- package/dist/types/interfaces/scanner-module.d.ts +280 -0
- package/dist/types/modules/face/face-detector.d.ts +170 -0
- package/dist/types/modules/face/index.d.ts +56 -0
- package/dist/types/modules/face/liveness-detector.d.ts +177 -0
- package/dist/types/modules/face/types.d.ts +136 -0
- package/dist/types/modules/id-card/anti-fake-detector.d.ts +170 -0
- package/dist/types/modules/id-card/id-card-detector.d.ts +131 -0
- package/dist/types/modules/id-card/index.d.ts +89 -0
- package/dist/types/modules/id-card/ocr-processor.d.ts +110 -0
- package/dist/types/modules/id-card/ocr-worker.d.ts +31 -0
- package/dist/types/modules/id-card/types.d.ts +181 -0
- package/dist/types/modules/qrcode/index.d.ts +51 -0
- package/dist/types/modules/qrcode/qr-code-scanner.d.ts +64 -0
- package/dist/types/modules/qrcode/types.d.ts +67 -0
- package/dist/types/utils/camera.d.ts +81 -0
- package/dist/types/utils/image-processing.d.ts +176 -0
- package/dist/types/utils/index.d.ts +175 -0
- package/dist/types/utils/performance.d.ts +81 -0
- package/dist/types/utils/resource-manager.d.ts +53 -0
- package/dist/types/utils/types.d.ts +166 -0
- package/dist/types/utils/worker.d.ts +52 -0
- package/dist/types/version.d.ts +7 -0
- package/package.json +76 -75
- package/src/core/base-module.ts +78 -0
- package/src/core/camera-manager.ts +798 -0
- package/src/core/config.ts +268 -0
- package/src/core/errors.ts +174 -0
- package/src/core/event-emitter.ts +110 -0
- package/src/core/logger.ts +549 -0
- package/src/core/module-manager.ts +165 -0
- package/src/core/plugin-manager.ts +429 -0
- package/src/core/resource-manager.ts +762 -0
- package/src/core/result.ts +163 -0
- package/src/core/scanner-factory.ts +237 -0
- package/src/index.ts +113 -936
- package/src/interfaces/external-types.ts +200 -0
- package/src/interfaces/face-detection.ts +309 -0
- package/src/interfaces/scanner-module.ts +384 -0
- package/src/modules/face/face-detector.ts +931 -0
- package/src/modules/face/index.ts +208 -0
- package/src/modules/face/liveness-detector.ts +908 -0
- package/src/modules/face/types.ts +133 -0
- package/src/{id-recognition → modules/id-card}/anti-fake-detector.ts +273 -239
- package/src/modules/id-card/id-card-detector.ts +474 -0
- package/src/modules/id-card/index.ts +425 -0
- package/src/{id-recognition → modules/id-card}/ocr-processor.ts +149 -92
- package/src/modules/id-card/ocr-worker.ts +259 -0
- package/src/modules/id-card/types.ts +178 -0
- package/src/modules/qrcode/index.ts +175 -0
- package/src/modules/qrcode/qr-code-scanner.ts +230 -0
- package/src/modules/qrcode/types.ts +65 -0
- package/src/types/tesseract.d.ts +265 -22
- package/src/utils/image-processing.ts +68 -49
- package/src/utils/index.ts +426 -0
- package/src/utils/performance.ts +168 -131
- package/src/utils/resource-manager.ts +65 -146
- package/src/utils/types.ts +90 -2
- package/src/utils/worker.ts +123 -84
- package/src/version.ts +11 -0
- package/tools/scaffold.js +543 -0
- package/dist/id-scanner-core.esm.js +0 -11349
- package/dist/id-scanner-core.js +0 -11361
- package/dist/id-scanner-core.min.js +0 -1
- package/dist/id-scanner-ocr.esm.js +0 -2319
- package/dist/id-scanner-ocr.js +0 -2328
- package/dist/id-scanner-ocr.min.js +0 -1
- package/dist/id-scanner-qr.esm.js +0 -1296
- package/dist/id-scanner-qr.js +0 -1305
- package/dist/id-scanner-qr.min.js +0 -1
- package/dist/id-scanner.js +0 -4561
- package/dist/id-scanner.min.js +0 -1
- package/src/core.ts +0 -138
- package/src/demo/demo.ts +0 -204
- package/src/id-recognition/data-extractor.ts +0 -262
- package/src/id-recognition/id-detector.ts +0 -510
- package/src/id-recognition/ocr-worker.ts +0 -156
- package/src/index-umd.ts +0 -477
- package/src/ocr-module.ts +0 -187
- package/src/qr-module.ts +0 -179
- package/src/scanner/barcode-scanner.ts +0 -251
- package/src/scanner/qr-scanner.ts +0 -167
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file 身份证模块入口
|
|
3
|
+
* @description 提供身份证识别和验证功能的模块入口
|
|
4
|
+
* @module modules/id-card
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { BaseModule } from '../../core/base-module';
|
|
8
|
+
import { IDCardDetector } from './id-card-detector';
|
|
9
|
+
import { OCRProcessor } from './ocr-processor';
|
|
10
|
+
import { AntiFakeDetector } from './anti-fake-detector';
|
|
11
|
+
import { IDCardInfo, IDCardType, IDCardModuleOptions, IDCardVerificationResult } from './types';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 身份证识别模块
|
|
15
|
+
* 提供身份证检测、OCR识别、防伪检测等功能
|
|
16
|
+
*/
|
|
17
|
+
export class IDCardModule extends BaseModule {
|
|
18
|
+
/** 模块名称 */
|
|
19
|
+
public readonly name: string = 'id-card';
|
|
20
|
+
|
|
21
|
+
/** 模块配置 */
|
|
22
|
+
private options: IDCardModuleOptions;
|
|
23
|
+
|
|
24
|
+
/** 身份证检测器 */
|
|
25
|
+
private detector: IDCardDetector;
|
|
26
|
+
|
|
27
|
+
/** OCR处理器 */
|
|
28
|
+
private ocrProcessor?: OCRProcessor;
|
|
29
|
+
|
|
30
|
+
/** 防伪检测器 */
|
|
31
|
+
private antiFakeDetector?: AntiFakeDetector;
|
|
32
|
+
|
|
33
|
+
/** 最后一次检测结果 */
|
|
34
|
+
private lastDetectionResult?: IDCardInfo;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 构造函数
|
|
38
|
+
* @param options 模块配置选项
|
|
39
|
+
*/
|
|
40
|
+
constructor(options: IDCardModuleOptions = {}) {
|
|
41
|
+
super();
|
|
42
|
+
|
|
43
|
+
this.options = {
|
|
44
|
+
enabled: true,
|
|
45
|
+
detector: {
|
|
46
|
+
minConfidence: 0.7,
|
|
47
|
+
enableOCR: true,
|
|
48
|
+
enableAntiFake: false,
|
|
49
|
+
...options.detector
|
|
50
|
+
},
|
|
51
|
+
ocr: {
|
|
52
|
+
useWorker: true,
|
|
53
|
+
maxImageDimension: 1000,
|
|
54
|
+
brightness: 10,
|
|
55
|
+
contrast: 20,
|
|
56
|
+
...options.ocr
|
|
57
|
+
},
|
|
58
|
+
antiFake: {
|
|
59
|
+
sensitivity: 0.8,
|
|
60
|
+
minConfidence: 0.7,
|
|
61
|
+
...options.antiFake
|
|
62
|
+
},
|
|
63
|
+
...options
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// 创建检测器
|
|
67
|
+
this.detector = new IDCardDetector({
|
|
68
|
+
minConfidence: this.options.detector?.minConfidence,
|
|
69
|
+
enableEdgeDetection: true,
|
|
70
|
+
returnImage: true
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 初始化模块
|
|
76
|
+
*/
|
|
77
|
+
public async initialize(): Promise<void> {
|
|
78
|
+
if (this._isInitialized) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.logger.debug(this.name, '初始化身份证模块');
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
// 初始化检测器
|
|
86
|
+
await this.detector.initialize();
|
|
87
|
+
|
|
88
|
+
// 如果启用OCR,初始化OCR处理器
|
|
89
|
+
if (this.options.detector?.enableOCR) {
|
|
90
|
+
this.ocrProcessor = new OCRProcessor({
|
|
91
|
+
useWorker: this.options.ocr?.useWorker,
|
|
92
|
+
maxImageDimension: this.options.ocr?.maxImageDimension,
|
|
93
|
+
brightness: this.options.ocr?.brightness,
|
|
94
|
+
contrast: this.options.ocr?.contrast
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
await this.ocrProcessor.initialize();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 如果启用防伪检测,初始化防伪检测器
|
|
101
|
+
if (this.options.detector?.enableAntiFake) {
|
|
102
|
+
this.antiFakeDetector = new AntiFakeDetector({
|
|
103
|
+
sensitivity: this.options.antiFake?.sensitivity
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// AntiFakeDetector 不需要初始化
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this._isInitialized = true;
|
|
110
|
+
this.emit('initialized');
|
|
111
|
+
this.logger.debug(this.name, '身份证模块初始化完成');
|
|
112
|
+
} catch (error) {
|
|
113
|
+
this.logger.error(this.name, '身份证模块初始化失败', error instanceof Error ? error : new Error(String(error)));
|
|
114
|
+
throw new Error(`身份证模块初始化失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 识别身份证图像
|
|
120
|
+
* @param image 图像源
|
|
121
|
+
* @returns 识别结果
|
|
122
|
+
*/
|
|
123
|
+
public async recognize(
|
|
124
|
+
image: ImageData | HTMLImageElement | HTMLCanvasElement
|
|
125
|
+
): Promise<IDCardInfo> {
|
|
126
|
+
this.ensureInitialized();
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
// 检测身份证
|
|
130
|
+
const detectionResult = await this.detector.processImage(image);
|
|
131
|
+
|
|
132
|
+
if (!detectionResult.isSuccess() || !detectionResult.data) {
|
|
133
|
+
throw new Error('未检测到身份证');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 创建结果对象
|
|
137
|
+
const idCardInfo: IDCardInfo = {
|
|
138
|
+
type: detectionResult.data.type || IDCardType.FRONT,
|
|
139
|
+
confidence: detectionResult.data.confidence
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// 如果启用OCR且OCR处理器已初始化
|
|
143
|
+
if (this.options.detector?.enableOCR && this.ocrProcessor) {
|
|
144
|
+
// 裁剪并处理图像
|
|
145
|
+
const processedImage = detectionResult.data.image || this.convertToImageData(image);
|
|
146
|
+
|
|
147
|
+
// 识别文本信息
|
|
148
|
+
const ocrResult = await this.ocrProcessor.processIDCard(processedImage);
|
|
149
|
+
|
|
150
|
+
// 合并OCR结果
|
|
151
|
+
Object.assign(idCardInfo, ocrResult);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 如果启用防伪检测且防伪检测器已初始化
|
|
155
|
+
if (this.options.detector?.enableAntiFake && this.antiFakeDetector) {
|
|
156
|
+
const processedImage = this.convertToImageData(image);
|
|
157
|
+
const antiFakeResult = await this.antiFakeDetector.detect(processedImage);
|
|
158
|
+
|
|
159
|
+
// 转换防伪检测结果格式
|
|
160
|
+
idCardInfo.antiFake = {
|
|
161
|
+
passed: antiFakeResult.isAuthentic,
|
|
162
|
+
score: antiFakeResult.confidence,
|
|
163
|
+
features: {
|
|
164
|
+
// 转换检测到的特征
|
|
165
|
+
fluorescent: antiFakeResult.detectedFeatures.includes('荧光油墨特征'),
|
|
166
|
+
microtext: antiFakeResult.detectedFeatures.includes('微缩文字'),
|
|
167
|
+
opticalVariable: antiFakeResult.detectedFeatures.includes('光变图案'),
|
|
168
|
+
texture: antiFakeResult.detectedFeatures.includes('雕刻凹印'),
|
|
169
|
+
watermark: antiFakeResult.detectedFeatures.includes('隐形图案')
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 保存最后一次检测结果
|
|
175
|
+
this.lastDetectionResult = idCardInfo;
|
|
176
|
+
|
|
177
|
+
// 触发事件
|
|
178
|
+
this.emit('recognized', { idCardInfo });
|
|
179
|
+
|
|
180
|
+
return idCardInfo;
|
|
181
|
+
} catch (error) {
|
|
182
|
+
this.logger.error(this.name, '身份证识别失败', error instanceof Error ? error : new Error(String(error)));
|
|
183
|
+
throw new Error(`身份证识别失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 验证身份证信息
|
|
189
|
+
* @param idCardInfo 身份证信息
|
|
190
|
+
* @returns 验证结果
|
|
191
|
+
*/
|
|
192
|
+
public verify(idCardInfo: IDCardInfo): IDCardVerificationResult {
|
|
193
|
+
this.ensureInitialized();
|
|
194
|
+
|
|
195
|
+
const result: IDCardVerificationResult = {
|
|
196
|
+
isValid: true,
|
|
197
|
+
score: 1.0,
|
|
198
|
+
details: {
|
|
199
|
+
idNumberValid: true,
|
|
200
|
+
issueDateValid: true,
|
|
201
|
+
isExpired: false,
|
|
202
|
+
antiFakePassed: true
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// 验证身份证号码
|
|
207
|
+
if (idCardInfo.idNumber) {
|
|
208
|
+
result.details!.idNumberValid = this.validateIDNumber(idCardInfo.idNumber);
|
|
209
|
+
if (!result.details!.idNumberValid) {
|
|
210
|
+
result.isValid = false;
|
|
211
|
+
result.score -= 0.3;
|
|
212
|
+
result.failureReason = '身份证号码无效';
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 验证有效期
|
|
217
|
+
if (idCardInfo.validTo) {
|
|
218
|
+
result.details!.isExpired = this.isIDCardExpired(idCardInfo.validTo);
|
|
219
|
+
if (result.details!.isExpired) {
|
|
220
|
+
result.isValid = false;
|
|
221
|
+
result.score -= 0.2;
|
|
222
|
+
result.failureReason = '身份证已过期';
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// 验证防伪结果
|
|
227
|
+
if (idCardInfo.antiFake) {
|
|
228
|
+
result.details!.antiFakePassed = idCardInfo.antiFake.passed;
|
|
229
|
+
if (!result.details!.antiFakePassed) {
|
|
230
|
+
result.isValid = false;
|
|
231
|
+
result.score -= 0.5;
|
|
232
|
+
result.failureReason = '防伪检测未通过';
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* 获取最后一次识别结果
|
|
241
|
+
*/
|
|
242
|
+
public getLastRecognitionResult(): IDCardInfo | undefined {
|
|
243
|
+
return this.lastDetectionResult;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 释放模块资源
|
|
248
|
+
*/
|
|
249
|
+
public async dispose(): Promise<void> {
|
|
250
|
+
if (!this._isInitialized) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
this.logger.debug(this.name, '释放身份证模块资源');
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
// 释放检测器资源
|
|
258
|
+
await this.detector.dispose();
|
|
259
|
+
|
|
260
|
+
// 释放OCR处理器资源
|
|
261
|
+
if (this.ocrProcessor) {
|
|
262
|
+
await this.ocrProcessor.terminate();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// 释放防伪检测器资源
|
|
266
|
+
if (this.antiFakeDetector) {
|
|
267
|
+
await this.antiFakeDetector.dispose();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// 调用基类的dispose方法
|
|
271
|
+
await super.dispose();
|
|
272
|
+
} catch (error) {
|
|
273
|
+
this.logger.error(this.name, '身份证模块资源释放失败', error instanceof Error ? error : new Error(String(error)));
|
|
274
|
+
throw new Error(`身份证模块资源释放失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* 验证身份证号码是否有效
|
|
280
|
+
* @param idNumber 身份证号码
|
|
281
|
+
* @returns 是否有效
|
|
282
|
+
*/
|
|
283
|
+
private validateIDNumber(idNumber: string): boolean {
|
|
284
|
+
// 基本格式验证
|
|
285
|
+
if (!idNumber || !/^\d{17}[\dX]$/.test(idNumber)) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// 验证出生日期
|
|
290
|
+
const year = parseInt(idNumber.substring(6, 10));
|
|
291
|
+
const month = parseInt(idNumber.substring(10, 12));
|
|
292
|
+
const day = parseInt(idNumber.substring(12, 14));
|
|
293
|
+
|
|
294
|
+
if (year < 1900 || year > new Date().getFullYear() ||
|
|
295
|
+
month < 1 || month > 12 ||
|
|
296
|
+
day < 1 || day > 31) {
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 验证校验位
|
|
301
|
+
const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
|
|
302
|
+
const validationCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
|
|
303
|
+
|
|
304
|
+
let sum = 0;
|
|
305
|
+
for (let i = 0; i < 17; i++) {
|
|
306
|
+
sum += parseInt(idNumber[i]) * weights[i];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const validationCode = validationCodes[sum % 11];
|
|
310
|
+
return validationCode === idNumber[17].toUpperCase();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* 检查身份证是否过期
|
|
315
|
+
* @param validTo 有效期截止日期
|
|
316
|
+
* @returns 是否过期
|
|
317
|
+
*/
|
|
318
|
+
private isIDCardExpired(validTo: string): boolean {
|
|
319
|
+
// 如果是"长期",则视为未过期
|
|
320
|
+
if (validTo === '长期' || validTo === '长期有效') {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
// 解析日期字符串
|
|
326
|
+
const parts = validTo.split('-');
|
|
327
|
+
if (parts.length !== 3) {
|
|
328
|
+
return true; // 格式错误,视为过期
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const year = parseInt(parts[0]);
|
|
332
|
+
const month = parseInt(parts[1]) - 1; // 月份从0开始
|
|
333
|
+
const day = parseInt(parts[2]);
|
|
334
|
+
|
|
335
|
+
const expiryDate = new Date(year, month, day);
|
|
336
|
+
const today = new Date();
|
|
337
|
+
|
|
338
|
+
// 设置时间为当天结束
|
|
339
|
+
today.setHours(0, 0, 0, 0);
|
|
340
|
+
|
|
341
|
+
return expiryDate < today;
|
|
342
|
+
} catch {
|
|
343
|
+
return true; // 解析错误,视为过期
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* 检测身份证
|
|
349
|
+
* @param image 图像源
|
|
350
|
+
* @returns 检测结果
|
|
351
|
+
*/
|
|
352
|
+
public async detect(
|
|
353
|
+
image: ImageData | HTMLImageElement | HTMLCanvasElement
|
|
354
|
+
): Promise<{
|
|
355
|
+
success: boolean;
|
|
356
|
+
type?: IDCardType;
|
|
357
|
+
confidence: number;
|
|
358
|
+
croppedImage?: ImageData;
|
|
359
|
+
}> {
|
|
360
|
+
this.ensureInitialized();
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
// 调用检测器处理图像
|
|
364
|
+
const result = await this.detector.processImage(image);
|
|
365
|
+
|
|
366
|
+
if (!result.isSuccess() || !result.data) {
|
|
367
|
+
return { success: false, confidence: 0 };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
success: true,
|
|
372
|
+
type: result.data.type,
|
|
373
|
+
confidence: result.data.confidence || 0,
|
|
374
|
+
croppedImage: result.data.image
|
|
375
|
+
};
|
|
376
|
+
} catch (error) {
|
|
377
|
+
this.logger.error(this.name, '身份证检测失败', error instanceof Error ? error : new Error(String(error)));
|
|
378
|
+
return { success: false, confidence: 0 };
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* 将图像转换为 ImageData
|
|
384
|
+
* @param image 图像源
|
|
385
|
+
* @returns ImageData 对象
|
|
386
|
+
*/
|
|
387
|
+
private convertToImageData(image: ImageData | HTMLImageElement | HTMLCanvasElement): ImageData {
|
|
388
|
+
// 如果已经是 ImageData,直接返回
|
|
389
|
+
if (image instanceof ImageData) {
|
|
390
|
+
return image;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// 创建 Canvas 用于转换
|
|
394
|
+
const canvas = document.createElement('canvas');
|
|
395
|
+
const width = image instanceof HTMLImageElement ? image.naturalWidth : image.width;
|
|
396
|
+
const height = image instanceof HTMLImageElement ? image.naturalHeight : image.height;
|
|
397
|
+
|
|
398
|
+
canvas.width = width;
|
|
399
|
+
canvas.height = height;
|
|
400
|
+
|
|
401
|
+
const ctx = canvas.getContext('2d');
|
|
402
|
+
if (!ctx) {
|
|
403
|
+
throw new Error('无法创建 Canvas 上下文');
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// 绘制图像到 Canvas
|
|
407
|
+
ctx.drawImage(image, 0, 0);
|
|
408
|
+
|
|
409
|
+
// 返回 ImageData
|
|
410
|
+
return ctx.getImageData(0, 0, width, height);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* 确保模块已初始化
|
|
415
|
+
* @protected
|
|
416
|
+
*/
|
|
417
|
+
protected ensureInitialized(): void {
|
|
418
|
+
if (!this._isInitialized) {
|
|
419
|
+
throw new Error('身份证模块尚未初始化');
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// 导出类型
|
|
425
|
+
export * from './types';
|