id-scanner-lib 1.6.5 → 1.6.7
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/dist/id-scanner-lib.esm.js +107 -67
- package/dist/id-scanner-lib.esm.js.map +1 -1
- package/dist/id-scanner-lib.js +107 -66
- package/dist/id-scanner-lib.js.map +1 -1
- package/package.json +1 -1
- package/src/core/event-emitter.ts +9 -0
- package/src/core/logger.ts +52 -34
- package/src/core/resource-manager.ts +23 -9
- package/src/core/result.ts +3 -3
- package/src/index.ts +5 -5
- package/src/modules/face/face-detector.ts +8 -6
- package/src/modules/face/liveness-detector.ts +3 -2
- package/src/modules/id-card/index.ts +10 -8
- package/src/utils/image-processing.ts +22 -31
- package/src/core/plugin-manager.ts +0 -429
- package/src/core/scanner-factory.ts +0 -236
|
@@ -259,13 +259,17 @@ class ConfigManager {
|
|
|
259
259
|
/**
|
|
260
260
|
* 日志级别枚举
|
|
261
261
|
*/
|
|
262
|
-
var
|
|
263
|
-
(function (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
})(
|
|
262
|
+
var LoggerLevel;
|
|
263
|
+
(function (LoggerLevel) {
|
|
264
|
+
LoggerLevel["DEBUG"] = "debug";
|
|
265
|
+
LoggerLevel["INFO"] = "info";
|
|
266
|
+
LoggerLevel["WARN"] = "warn";
|
|
267
|
+
LoggerLevel["ERROR"] = "error";
|
|
268
|
+
})(LoggerLevel || (LoggerLevel = {}));
|
|
269
|
+
/**
|
|
270
|
+
* @deprecated 使用 LoggerLevel 代替
|
|
271
|
+
*/
|
|
272
|
+
const LogLevel = LoggerLevel;
|
|
269
273
|
/**
|
|
270
274
|
* 控制台日志处理器
|
|
271
275
|
* 将日志输出到浏览器控制台
|
|
@@ -279,16 +283,16 @@ class ConsoleLogHandler {
|
|
|
279
283
|
const timestamp = new Date(entry.timestamp).toISOString();
|
|
280
284
|
const prefix = `[${timestamp}] [${entry.level.toUpperCase()}] [${entry.tag}]`;
|
|
281
285
|
switch (entry.level) {
|
|
282
|
-
case
|
|
286
|
+
case LoggerLevel.DEBUG:
|
|
283
287
|
console.debug(prefix, entry.message, entry.error || '');
|
|
284
288
|
break;
|
|
285
|
-
case
|
|
289
|
+
case LoggerLevel.INFO:
|
|
286
290
|
console.info(prefix, entry.message, entry.error || '');
|
|
287
291
|
break;
|
|
288
|
-
case
|
|
292
|
+
case LoggerLevel.WARN:
|
|
289
293
|
console.warn(prefix, entry.message, entry.error || '');
|
|
290
294
|
break;
|
|
291
|
-
case
|
|
295
|
+
case LoggerLevel.ERROR:
|
|
292
296
|
console.error(prefix, entry.message, entry.error || '');
|
|
293
297
|
break;
|
|
294
298
|
// 输出什么也不做
|
|
@@ -366,12 +370,15 @@ class RemoteLogHandler {
|
|
|
366
370
|
this.endpoint = endpoint;
|
|
367
371
|
this.maxQueueSize = maxQueueSize;
|
|
368
372
|
this.flushInterval = flushInterval;
|
|
373
|
+
this.isBrowser = typeof window !== 'undefined' && typeof window.addEventListener === 'function';
|
|
369
374
|
// 设置定时发送
|
|
370
375
|
this.startTimer();
|
|
371
376
|
// 页面卸载前尝试发送剩余日志
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
377
|
+
if (this.isBrowser) {
|
|
378
|
+
window.addEventListener('beforeunload', () => {
|
|
379
|
+
this.flush();
|
|
380
|
+
});
|
|
381
|
+
}
|
|
375
382
|
}
|
|
376
383
|
/**
|
|
377
384
|
* 处理日志条目
|
|
@@ -379,7 +386,7 @@ class RemoteLogHandler {
|
|
|
379
386
|
*/
|
|
380
387
|
handle(entry) {
|
|
381
388
|
// 只处理INFO以上级别的日志
|
|
382
|
-
if (entry.level >=
|
|
389
|
+
if (entry.level >= LoggerLevel.INFO) {
|
|
383
390
|
this.queue.push(entry);
|
|
384
391
|
// 如果队列满了,立即发送
|
|
385
392
|
if (this.queue.length >= this.maxQueueSize) {
|
|
@@ -439,6 +446,8 @@ class RemoteLogHandler {
|
|
|
439
446
|
* 开始定时发送
|
|
440
447
|
*/
|
|
441
448
|
startTimer() {
|
|
449
|
+
if (!this.isBrowser)
|
|
450
|
+
return;
|
|
442
451
|
if (this.timerId !== null)
|
|
443
452
|
return;
|
|
444
453
|
this.timerId = window.setInterval(() => {
|
|
@@ -469,7 +478,7 @@ class Logger {
|
|
|
469
478
|
/** 默认标签 */
|
|
470
479
|
this.defaultTag = 'IDScanner';
|
|
471
480
|
/** 日志级别 */
|
|
472
|
-
this.logLevel =
|
|
481
|
+
this.logLevel = LoggerLevel.INFO;
|
|
473
482
|
this.config = ConfigManager.getInstance();
|
|
474
483
|
// 默认添加控制台处理器
|
|
475
484
|
this.addHandler(new ConsoleLogHandler());
|
|
@@ -487,6 +496,12 @@ class Logger {
|
|
|
487
496
|
}
|
|
488
497
|
return Logger.instance;
|
|
489
498
|
}
|
|
499
|
+
/**
|
|
500
|
+
* 重置单例实例(主要用于测试)
|
|
501
|
+
*/
|
|
502
|
+
static resetInstance() {
|
|
503
|
+
Logger.instance = undefined;
|
|
504
|
+
}
|
|
490
505
|
/**
|
|
491
506
|
* 添加日志处理器
|
|
492
507
|
* @param handler 日志处理器
|
|
@@ -524,7 +539,7 @@ class Logger {
|
|
|
524
539
|
* @param error 错误
|
|
525
540
|
*/
|
|
526
541
|
debug(tag, message, error) {
|
|
527
|
-
this.log(
|
|
542
|
+
this.log(LoggerLevel.DEBUG, tag, message, error);
|
|
528
543
|
}
|
|
529
544
|
/**
|
|
530
545
|
* 记录信息级别日志
|
|
@@ -533,7 +548,7 @@ class Logger {
|
|
|
533
548
|
* @param error 错误
|
|
534
549
|
*/
|
|
535
550
|
info(tag, message, error) {
|
|
536
|
-
this.log(
|
|
551
|
+
this.log(LoggerLevel.INFO, tag, message, error);
|
|
537
552
|
}
|
|
538
553
|
/**
|
|
539
554
|
* 记录警告级别日志
|
|
@@ -542,7 +557,7 @@ class Logger {
|
|
|
542
557
|
* @param error 错误
|
|
543
558
|
*/
|
|
544
559
|
warn(tag, message, error) {
|
|
545
|
-
this.log(
|
|
560
|
+
this.log(LoggerLevel.WARN, tag, message, error);
|
|
546
561
|
}
|
|
547
562
|
/**
|
|
548
563
|
* 记录错误级别日志
|
|
@@ -551,7 +566,7 @@ class Logger {
|
|
|
551
566
|
* @param error 错误
|
|
552
567
|
*/
|
|
553
568
|
error(tag, message, error) {
|
|
554
|
-
this.log(
|
|
569
|
+
this.log(LoggerLevel.ERROR, tag, message, error);
|
|
555
570
|
}
|
|
556
571
|
/**
|
|
557
572
|
* 创建标记了特定标签的日志记录器
|
|
@@ -604,16 +619,16 @@ class Logger {
|
|
|
604
619
|
const timestamp = new Date(entry.timestamp).toISOString();
|
|
605
620
|
const prefix = `[${timestamp}] [${entry.level.toUpperCase()}] [${entry.tag}]`;
|
|
606
621
|
switch (entry.level) {
|
|
607
|
-
case
|
|
622
|
+
case LoggerLevel.DEBUG:
|
|
608
623
|
console.debug(`${prefix} ${entry.message}`, entry.error || '');
|
|
609
624
|
break;
|
|
610
|
-
case
|
|
625
|
+
case LoggerLevel.INFO:
|
|
611
626
|
console.info(`${prefix} ${entry.message}`, entry.error || '');
|
|
612
627
|
break;
|
|
613
|
-
case
|
|
628
|
+
case LoggerLevel.WARN:
|
|
614
629
|
console.warn(`${prefix} ${entry.message}`, entry.error || '');
|
|
615
630
|
break;
|
|
616
|
-
case
|
|
631
|
+
case LoggerLevel.ERROR:
|
|
617
632
|
console.error(`${prefix} ${entry.message}`, entry.error || '');
|
|
618
633
|
break;
|
|
619
634
|
}
|
|
@@ -624,13 +639,13 @@ class Logger {
|
|
|
624
639
|
*/
|
|
625
640
|
getLevelValue(level) {
|
|
626
641
|
switch (level) {
|
|
627
|
-
case
|
|
642
|
+
case LoggerLevel.DEBUG:
|
|
628
643
|
return 0;
|
|
629
|
-
case
|
|
644
|
+
case LoggerLevel.INFO:
|
|
630
645
|
return 1;
|
|
631
|
-
case
|
|
646
|
+
case LoggerLevel.WARN:
|
|
632
647
|
return 2;
|
|
633
|
-
case
|
|
648
|
+
case LoggerLevel.ERROR:
|
|
634
649
|
return 3;
|
|
635
650
|
default:
|
|
636
651
|
return 1; // 默认INFO级别
|
|
@@ -644,19 +659,19 @@ class Logger {
|
|
|
644
659
|
if (typeof level === 'string') {
|
|
645
660
|
switch (level) {
|
|
646
661
|
case 'debug':
|
|
647
|
-
this.logLevel =
|
|
662
|
+
this.logLevel = LoggerLevel.DEBUG;
|
|
648
663
|
break;
|
|
649
664
|
case 'info':
|
|
650
|
-
this.logLevel =
|
|
665
|
+
this.logLevel = LoggerLevel.INFO;
|
|
651
666
|
break;
|
|
652
667
|
case 'warn':
|
|
653
|
-
this.logLevel =
|
|
668
|
+
this.logLevel = LoggerLevel.WARN;
|
|
654
669
|
break;
|
|
655
670
|
case 'error':
|
|
656
|
-
this.logLevel =
|
|
671
|
+
this.logLevel = LoggerLevel.ERROR;
|
|
657
672
|
break;
|
|
658
673
|
default:
|
|
659
|
-
this.logLevel =
|
|
674
|
+
this.logLevel = LoggerLevel.INFO;
|
|
660
675
|
}
|
|
661
676
|
}
|
|
662
677
|
else {
|
|
@@ -769,6 +784,14 @@ class EventEmitter {
|
|
|
769
784
|
this.eventHandlers.delete(eventName);
|
|
770
785
|
}
|
|
771
786
|
}
|
|
787
|
+
/**
|
|
788
|
+
* 取消订阅事件 (off的别名)
|
|
789
|
+
* @param eventName 事件名称
|
|
790
|
+
* @param handler 事件处理器
|
|
791
|
+
*/
|
|
792
|
+
removeListener(eventName, handler) {
|
|
793
|
+
this.off(eventName, handler);
|
|
794
|
+
}
|
|
772
795
|
/**
|
|
773
796
|
* 订阅事件,但只触发一次
|
|
774
797
|
* @param eventName 事件名称
|
|
@@ -1056,19 +1079,19 @@ class Result {
|
|
|
1056
1079
|
/**
|
|
1057
1080
|
* 获取结果数据
|
|
1058
1081
|
*/
|
|
1059
|
-
|
|
1082
|
+
getData() {
|
|
1060
1083
|
return this._data;
|
|
1061
1084
|
}
|
|
1062
1085
|
/**
|
|
1063
1086
|
* 获取错误对象
|
|
1064
1087
|
*/
|
|
1065
|
-
|
|
1088
|
+
getError() {
|
|
1066
1089
|
return this._error;
|
|
1067
1090
|
}
|
|
1068
1091
|
/**
|
|
1069
1092
|
* 获取元数据
|
|
1070
1093
|
*/
|
|
1071
|
-
|
|
1094
|
+
getMeta() {
|
|
1072
1095
|
return this._meta;
|
|
1073
1096
|
}
|
|
1074
1097
|
/**
|
|
@@ -1699,17 +1722,32 @@ class ImageProcessor {
|
|
|
1699
1722
|
outputData[pos + 3] = data[pos + 3]; // 保持透明度不变
|
|
1700
1723
|
}
|
|
1701
1724
|
}
|
|
1702
|
-
//
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1725
|
+
// 处理边缘像素(仅遍历四条边,而非全图 O(width×height) → O(width+height))
|
|
1726
|
+
// 上边 + 下边
|
|
1727
|
+
for (let x = 0; x < width; x++) {
|
|
1728
|
+
const topPos = x * 4;
|
|
1729
|
+
const bottomPos = ((height - 1) * width + x) * 4;
|
|
1730
|
+
outputData[topPos] = data[topPos];
|
|
1731
|
+
outputData[topPos + 1] = data[topPos + 1];
|
|
1732
|
+
outputData[topPos + 2] = data[topPos + 2];
|
|
1733
|
+
outputData[topPos + 3] = data[topPos + 3];
|
|
1734
|
+
outputData[bottomPos] = data[bottomPos];
|
|
1735
|
+
outputData[bottomPos + 1] = data[bottomPos + 1];
|
|
1736
|
+
outputData[bottomPos + 2] = data[bottomPos + 2];
|
|
1737
|
+
outputData[bottomPos + 3] = data[bottomPos + 3];
|
|
1738
|
+
}
|
|
1739
|
+
// 左边 + 右边(排除四角,它们已在上下一行处理)
|
|
1740
|
+
for (let y = 1; y < height - 1; y++) {
|
|
1741
|
+
const leftPos = y * width * 4;
|
|
1742
|
+
const rightPos = (y * width + width - 1) * 4;
|
|
1743
|
+
outputData[leftPos] = data[leftPos];
|
|
1744
|
+
outputData[leftPos + 1] = data[leftPos + 1];
|
|
1745
|
+
outputData[leftPos + 2] = data[leftPos + 2];
|
|
1746
|
+
outputData[leftPos + 3] = data[leftPos + 3];
|
|
1747
|
+
outputData[rightPos] = data[rightPos];
|
|
1748
|
+
outputData[rightPos + 1] = data[rightPos + 1];
|
|
1749
|
+
outputData[rightPos + 2] = data[rightPos + 2];
|
|
1750
|
+
outputData[rightPos + 3] = data[rightPos + 3];
|
|
1713
1751
|
}
|
|
1714
1752
|
// 创建新的ImageData对象
|
|
1715
1753
|
return new ImageData(outputData, width, height);
|
|
@@ -1722,11 +1760,10 @@ class ImageProcessor {
|
|
|
1722
1760
|
* @returns 处理后的图像数据
|
|
1723
1761
|
*/
|
|
1724
1762
|
static threshold(imageData, threshold = 128) {
|
|
1725
|
-
//
|
|
1726
|
-
const grayscaleImage = this.toGrayscale(
|
|
1763
|
+
// 先转换为灰度图(toGrayscale 内部已创建新 ImageData,无需外部拷贝)
|
|
1764
|
+
const grayscaleImage = this.toGrayscale(imageData);
|
|
1727
1765
|
const data = grayscaleImage.data;
|
|
1728
|
-
|
|
1729
|
-
for (let i = 0; i < length; i += 4) {
|
|
1766
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
1730
1767
|
// 二值化处理
|
|
1731
1768
|
const value = data[i] < threshold ? 0 : 255;
|
|
1732
1769
|
data[i] = data[i + 1] = data[i + 2] = value;
|
|
@@ -1740,8 +1777,8 @@ class ImageProcessor {
|
|
|
1740
1777
|
* @returns 二值化后的图像数据
|
|
1741
1778
|
*/
|
|
1742
1779
|
static toBinaryImage(imageData) {
|
|
1743
|
-
//
|
|
1744
|
-
const grayscaleImage = this.toGrayscale(
|
|
1780
|
+
// 先转换为灰度图(toGrayscale 内部已创建新 ImageData,无需外部拷贝)
|
|
1781
|
+
const grayscaleImage = this.toGrayscale(imageData);
|
|
1745
1782
|
// 使用OTSU算法自动确定阈值
|
|
1746
1783
|
const threshold = this.getOtsuThreshold(grayscaleImage);
|
|
1747
1784
|
return this.threshold(grayscaleImage, threshold);
|
|
@@ -1754,8 +1791,9 @@ class ImageProcessor {
|
|
|
1754
1791
|
*/
|
|
1755
1792
|
static getOtsuThreshold(imageData) {
|
|
1756
1793
|
const data = imageData.data;
|
|
1757
|
-
|
|
1758
|
-
|
|
1794
|
+
// 使用 Uint8Array 替代 Array<number>,避免 boxing 开销,提升直方图统计性能
|
|
1795
|
+
const histogram = new Uint32Array(256);
|
|
1796
|
+
// 统计灰度直方图(每4字节取R通道,即灰度值)
|
|
1759
1797
|
for (let i = 0; i < data.length; i += 4) {
|
|
1760
1798
|
histogram[data[i]]++;
|
|
1761
1799
|
}
|
|
@@ -3774,18 +3812,19 @@ class IDCardModule extends BaseModule {
|
|
|
3774
3812
|
try {
|
|
3775
3813
|
// 检测身份证
|
|
3776
3814
|
const detectionResult = await this.detector.processImage(image);
|
|
3777
|
-
|
|
3815
|
+
const detectionData = detectionResult.getData();
|
|
3816
|
+
if (!detectionResult.isSuccess() || !detectionData) {
|
|
3778
3817
|
throw new Error('未检测到身份证');
|
|
3779
3818
|
}
|
|
3780
3819
|
// 创建结果对象
|
|
3781
3820
|
const idCardInfo = {
|
|
3782
|
-
type:
|
|
3783
|
-
confidence:
|
|
3821
|
+
type: detectionData.type || IDCardType.FRONT,
|
|
3822
|
+
confidence: detectionData.confidence
|
|
3784
3823
|
};
|
|
3785
3824
|
// 如果启用OCR且OCR处理器已初始化
|
|
3786
3825
|
if (this.options.detector?.enableOCR && this.ocrProcessor) {
|
|
3787
3826
|
// 裁剪并处理图像
|
|
3788
|
-
const processedImage =
|
|
3827
|
+
const processedImage = detectionData.image || this.convertToImageData(image);
|
|
3789
3828
|
// 识别文本信息
|
|
3790
3829
|
const ocrResult = await this.ocrProcessor.processIDCard(processedImage);
|
|
3791
3830
|
// 合并OCR结果
|
|
@@ -3967,14 +4006,15 @@ class IDCardModule extends BaseModule {
|
|
|
3967
4006
|
try {
|
|
3968
4007
|
// 调用检测器处理图像
|
|
3969
4008
|
const result = await this.detector.processImage(image);
|
|
3970
|
-
if (!result.isSuccess() || !result.
|
|
4009
|
+
if (!result.isSuccess() || !result.getData()) {
|
|
3971
4010
|
return { success: false, confidence: 0 };
|
|
3972
4011
|
}
|
|
4012
|
+
const data = result.getData();
|
|
3973
4013
|
return {
|
|
3974
4014
|
success: true,
|
|
3975
|
-
type:
|
|
3976
|
-
confidence:
|
|
3977
|
-
croppedImage:
|
|
4015
|
+
type: data.type,
|
|
4016
|
+
confidence: data.confidence || 0,
|
|
4017
|
+
croppedImage: data.image
|
|
3978
4018
|
};
|
|
3979
4019
|
}
|
|
3980
4020
|
catch (error) {
|
|
@@ -4849,13 +4889,13 @@ class IDScanner {
|
|
|
4849
4889
|
}
|
|
4850
4890
|
this.moduleManager = ModuleManager.getInstance();
|
|
4851
4891
|
// 注册模块
|
|
4852
|
-
if (options.enableIDCard
|
|
4892
|
+
if (options.enableIDCard === true) {
|
|
4853
4893
|
this.moduleManager.register(new IDCardModule(options.idCard));
|
|
4854
4894
|
}
|
|
4855
|
-
if (options.enableQRCode
|
|
4895
|
+
if (options.enableQRCode === true) {
|
|
4856
4896
|
this.moduleManager.register(new QRCodeModule(options.qrCode));
|
|
4857
4897
|
}
|
|
4858
|
-
if (options.enableFace
|
|
4898
|
+
if (options.enableFace === true) {
|
|
4859
4899
|
this.moduleManager.register(new FaceModule(options.face));
|
|
4860
4900
|
}
|
|
4861
4901
|
}
|
|
@@ -4923,5 +4963,5 @@ IDScanner.VERSION = VERSION;
|
|
|
4923
4963
|
/** 构建日期 */
|
|
4924
4964
|
IDScanner.BUILD_DATE = BUILD_DATE;
|
|
4925
4965
|
|
|
4926
|
-
export { BarcodeFormat, CameraAccessError, ConsoleLogHandler, DEFAULT_FORMATS, DeviceError, FaceComparisonError, FaceDetectionError, FaceModule, IDCardDetectionError, IDCardModule, IDCardType, IDScanner, IDScannerError, InitializationError, InvalidArgumentError, LivenessDetectionError, LoadingState, LoadingStateManager, LogLevel, Logger, MemoryLogHandler, ModuleManager, NotSupportedError, OCRProcessingError, QRCodeModule, QRScanError, RemoteLogHandler, ResourceLoadError, TaggedLogger, createLoadingStateManager, IDScanner as default };
|
|
4966
|
+
export { BarcodeFormat, CameraAccessError, ConsoleLogHandler, DEFAULT_FORMATS, DeviceError, FaceComparisonError, FaceDetectionError, FaceModule, IDCardDetectionError, IDCardModule, IDCardType, IDScanner, IDScannerError, InitializationError, InvalidArgumentError, LivenessDetectionError, LoadingState, LoadingStateManager, LogLevel, Logger, LoggerLevel, MemoryLogHandler, ModuleManager, NotSupportedError, OCRProcessingError, QRCodeModule, QRScanError, RemoteLogHandler, ResourceLoadError, TaggedLogger, createLoadingStateManager, IDScanner as default };
|
|
4927
4967
|
//# sourceMappingURL=id-scanner-lib.esm.js.map
|