@sssxyd/face-liveness-detector 0.4.0-alpha.10 → 0.4.0-alpha.11
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/index.esm.js +63 -50
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +63 -50
- package/dist/index.js.map +1 -1
- package/dist/types/face-detection-engine.d.ts.map +1 -1
- package/dist/types/screen-capture-detector.d.ts.map +1 -1
- package/dist/types/screen-moire-pattern-detect.d.ts.map +1 -1
- package/dist/types/screen-rgb-emission-detect.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2774,36 +2774,39 @@
|
|
|
2774
2774
|
* 计算综合屏幕拍摄置信度
|
|
2775
2775
|
* 基于各项特征指标的加权平均
|
|
2776
2776
|
*
|
|
2777
|
-
*
|
|
2778
|
-
* -
|
|
2779
|
-
* -
|
|
2780
|
-
* -
|
|
2781
|
-
*
|
|
2782
|
-
* 删除梯度特征原因:
|
|
2783
|
-
* - 梯度特征对分辨率敏感,是主要噪声源
|
|
2784
|
-
* - 权重仅 17%,且可靠性最低(⭐⭐⭐)
|
|
2785
|
-
* - 删除后准确率反而提升 1-3%(去掉噪声)
|
|
2786
|
-
* - 代码简化 40%,性能提升 40%(少 80ms 计算)
|
|
2787
|
-
* - RGB相关性 + 饱和度 已足以识别屏幕特性
|
|
2777
|
+
* 针对完整高分辨率图像(1920×1080+)优化:
|
|
2778
|
+
* - 足够的屏幕像素采样,所有特征都清晰
|
|
2779
|
+
* - RGB相关性权重提升(屏幕同质性强)
|
|
2780
|
+
* - 饱和度权重平衡(屏幕特征明显)
|
|
2781
|
+
* - 像素熵权重保持
|
|
2788
2782
|
*/
|
|
2789
2783
|
function calculateScreenConfidence(saturation, rgbCorrelation, entropy) {
|
|
2790
|
-
// 加权平均:各特征的贡献度
|
|
2791
|
-
// 权重自动归一化:35/83 : 30/83 : 18/83
|
|
2792
2784
|
const weights = {
|
|
2793
|
-
rgbCorrelation: 0.42, // RGB相关性:42% (
|
|
2794
|
-
saturation: 0.36, // 饱和度:36%
|
|
2795
|
-
entropy: 0.22, // 像素熵:22%
|
|
2785
|
+
rgbCorrelation: 0.42, // RGB相关性:42% (完整图像中最强屏幕特征)
|
|
2786
|
+
saturation: 0.36, // 饱和度:36%
|
|
2787
|
+
entropy: 0.22, // 像素熵:22%
|
|
2796
2788
|
};
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2789
|
+
// 为每个特征计算连续得分(而不是二元的 0 或 1)
|
|
2790
|
+
// 这样即使特征未达到硬阈值,仍然可以贡献部分置信度
|
|
2791
|
+
// RGB 相关性:0.75-1.0 范围评分(屏幕通常 > 0.85)
|
|
2792
|
+
const rgbScore = Math.max(0, (rgbCorrelation.value - 0.7) / (1.0 - 0.7));
|
|
2793
|
+
// 饱和度:0-40 范围评分(屏幕通常 < 40)
|
|
2794
|
+
const satScore = Math.max(0, 1 - (saturation.value / 40));
|
|
2795
|
+
// 像素熵:4-8 范围评分(屏幕通常 < 6.5)
|
|
2796
|
+
const entScore = Math.max(0, 1 - (entropy.value / 8));
|
|
2797
|
+
const confidence = rgbScore * weights.rgbCorrelation +
|
|
2798
|
+
satScore * weights.saturation +
|
|
2799
|
+
entScore * weights.entropy;
|
|
2800
2800
|
return Math.min(1, Math.max(0, confidence));
|
|
2801
2801
|
}
|
|
2802
2802
|
|
|
2803
2803
|
// ==================== 类型定义 ====================
|
|
2804
2804
|
// ==================== 默认配置 ====================
|
|
2805
2805
|
const DEFAULT_CONFIG = {
|
|
2806
|
-
|
|
2806
|
+
// 针对完整高分辨率图像(1920×1080+)优化:
|
|
2807
|
+
// - 完整采样提供充足的屏幕像素网格
|
|
2808
|
+
// - 莫尔纹信号清晰,使用更严格的阈值
|
|
2809
|
+
moire_threshold: 0.55,
|
|
2807
2810
|
enable_dct: true,
|
|
2808
2811
|
enable_edge_detection: true,
|
|
2809
2812
|
};
|
|
@@ -2896,6 +2899,16 @@
|
|
|
2896
2899
|
const finalConfig = { ...DEFAULT_CONFIG, ...config };
|
|
2897
2900
|
if (!cv)
|
|
2898
2901
|
throw new Error('OpenCV instance not initialized');
|
|
2902
|
+
// 验证灰度图像是否有效
|
|
2903
|
+
if (!gray || gray.rows === 0 || gray.cols === 0) {
|
|
2904
|
+
console.warn('[MoirePattern] Invalid gray image');
|
|
2905
|
+
return {
|
|
2906
|
+
isScreenCapture: false,
|
|
2907
|
+
confidence: 0.0,
|
|
2908
|
+
moireStrength: 0.0,
|
|
2909
|
+
dominantFrequencies: [],
|
|
2910
|
+
};
|
|
2911
|
+
}
|
|
2899
2912
|
let moireStrength = 0;
|
|
2900
2913
|
let dominantFrequencies = [];
|
|
2901
2914
|
// 使用 DCT 频域分析(优先方案)
|
|
@@ -2905,19 +2918,25 @@
|
|
|
2905
2918
|
dominantFrequencies = dctResult.dominantFrequencies;
|
|
2906
2919
|
}
|
|
2907
2920
|
// 辅助:Canny 边缘检测方案用于增强检测
|
|
2908
|
-
if (finalConfig.enable_edge_detection) {
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2921
|
+
if (finalConfig.enable_edge_detection && moireStrength < 0.5) {
|
|
2922
|
+
try {
|
|
2923
|
+
const edges = new cv.Mat();
|
|
2924
|
+
cv.Canny(gray, edges, 50, 150);
|
|
2925
|
+
const periodicity = detectPeriodicity(cv, edges);
|
|
2926
|
+
const directionConsistency = analyzeEdgeDirection(cv, edges);
|
|
2927
|
+
edges.delete();
|
|
2928
|
+
// 融合两种方法的结果
|
|
2929
|
+
moireStrength += (periodicity + directionConsistency) * 0.5 * 0.4;
|
|
2930
|
+
}
|
|
2931
|
+
catch (edgeError) {
|
|
2932
|
+
console.warn('[MoirePattern] Edge detection failed, skipping:', edgeError);
|
|
2933
|
+
// 继续使用 DCT 结果
|
|
2934
|
+
}
|
|
2916
2935
|
}
|
|
2917
2936
|
const isScreenCapture = moireStrength > finalConfig.moire_threshold;
|
|
2918
2937
|
return {
|
|
2919
2938
|
isScreenCapture,
|
|
2920
|
-
confidence: Math.min(Math.
|
|
2939
|
+
confidence: Math.min(Math.max(0, moireStrength), 1.0),
|
|
2921
2940
|
moireStrength,
|
|
2922
2941
|
dominantFrequencies,
|
|
2923
2942
|
};
|
|
@@ -3468,6 +3487,10 @@
|
|
|
3468
3487
|
|
|
3469
3488
|
/**
|
|
3470
3489
|
* RGB 发光模式检测的默认配置
|
|
3490
|
+
*
|
|
3491
|
+
* 针对完整高分辨率图像(1920×1080+)优化:
|
|
3492
|
+
* - 足够的频域采样,RGB子像素周期信号清晰
|
|
3493
|
+
* - 使用更严格的置信度阈值
|
|
3471
3494
|
*/
|
|
3472
3495
|
const DEFAULT_RGB_EMISSION_CONFIG = {
|
|
3473
3496
|
// 频率带分析参数
|
|
@@ -3819,7 +3842,8 @@
|
|
|
3819
3842
|
});
|
|
3820
3843
|
}
|
|
3821
3844
|
// 明确判定为屏幕捕捉时,直接返回
|
|
3822
|
-
|
|
3845
|
+
// 完整高分辨率图像:RGB信号应该很强
|
|
3846
|
+
const rgbCanDetermine = rgbEmissionResult.isScreenCapture && rgbEmissionResult.confidence > 0.65;
|
|
3823
3847
|
if (rgbCanDetermine) {
|
|
3824
3848
|
const totalTime = performance.now() - startTime;
|
|
3825
3849
|
if (debug) {
|
|
@@ -3855,7 +3879,8 @@
|
|
|
3855
3879
|
});
|
|
3856
3880
|
}
|
|
3857
3881
|
// 明确判定为屏幕捕捉时,直接返回
|
|
3858
|
-
|
|
3882
|
+
// 完整高分辨率图像:色彩特征应该清晰
|
|
3883
|
+
const colorCanDetermine = colorResult.isScreenCapture && colorResult.confidence > 0.65;
|
|
3859
3884
|
if (colorCanDetermine) {
|
|
3860
3885
|
const totalTime = performance.now() - startTime;
|
|
3861
3886
|
if (debug) {
|
|
@@ -3896,7 +3921,8 @@
|
|
|
3896
3921
|
});
|
|
3897
3922
|
}
|
|
3898
3923
|
// 明确判定为屏幕捕捉时,直接返回
|
|
3899
|
-
|
|
3924
|
+
// 完整高分辨率图像:莫尔纹应该很清晰
|
|
3925
|
+
const moireCanDetermine = moireResult.isScreenCapture && moireResult.confidence > 0.65;
|
|
3900
3926
|
if (moireCanDetermine) {
|
|
3901
3927
|
const totalTime = performance.now() - startTime;
|
|
3902
3928
|
if (debug) {
|
|
@@ -3912,9 +3938,10 @@
|
|
|
3912
3938
|
}
|
|
3913
3939
|
}
|
|
3914
3940
|
// 都不能明确判定,计算加权置信度
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3941
|
+
// 优化权重比例:针对完整高分辨率图像(1920×1080+)
|
|
3942
|
+
const weightedConfidence = rgbEmissionResult.confidence * 0.50 + // RGB权重 50%
|
|
3943
|
+
colorResult.confidence * 0.25 + // Color权重 25%
|
|
3944
|
+
moireResult.confidence * 0.25; // Moiré权重 25% (提升,完整图像中莫尔纹清晰)
|
|
3918
3945
|
// 第 3 层:用加权置信度判定最终结果
|
|
3919
3946
|
const isScreenCapture = weightedConfidence > this.confidenceThreshold;
|
|
3920
3947
|
// 根据加权置信度判断风险等级
|
|
@@ -4648,8 +4675,6 @@
|
|
|
4648
4675
|
// 所有需要删除的 Mat 对象
|
|
4649
4676
|
let bgrFrame = null;
|
|
4650
4677
|
let grayFrame = null;
|
|
4651
|
-
let bgrFace = null;
|
|
4652
|
-
let grayFace = null;
|
|
4653
4678
|
try {
|
|
4654
4679
|
// 当前帧图片
|
|
4655
4680
|
bgrFrame = drawCanvasToMat(this.cv, frameCanvas, false);
|
|
@@ -4665,14 +4690,6 @@
|
|
|
4665
4690
|
this.scheduleNextDetection(this.options.detect_error_retry_delay);
|
|
4666
4691
|
return;
|
|
4667
4692
|
}
|
|
4668
|
-
// 提取人脸区域图片及灰度图片
|
|
4669
|
-
bgrFace = bgrFrame.roi(new this.cv.Rect(faceBox[0], faceBox[1], faceBox[2], faceBox[3]));
|
|
4670
|
-
grayFace = matToGray(this.cv, bgrFace);
|
|
4671
|
-
if (!grayFace) {
|
|
4672
|
-
this.emitDebug('detection', 'Failed to convert face Mat to grayscale', {}, 'warn');
|
|
4673
|
-
this.scheduleNextDetection(this.options.detect_error_retry_delay);
|
|
4674
|
-
return;
|
|
4675
|
-
}
|
|
4676
4693
|
if (!this.detectionState.screenDetector) {
|
|
4677
4694
|
this.emit('detector-error', {
|
|
4678
4695
|
code: exports.ErrorCode.INTERNAL_ERROR,
|
|
@@ -4690,7 +4707,7 @@
|
|
|
4690
4707
|
return;
|
|
4691
4708
|
}
|
|
4692
4709
|
// 屏幕捕获检测, 只关心脸部区域
|
|
4693
|
-
const screenResult = this.detectionState.screenDetector.detect(
|
|
4710
|
+
const screenResult = this.detectionState.screenDetector.detect(bgrFrame, grayFrame, this.options.debug_mode);
|
|
4694
4711
|
// 屏幕捕获检测器已经准备就绪,其验证结果可信
|
|
4695
4712
|
if (screenResult.isScreenCapture) {
|
|
4696
4713
|
// 从 executedMethods 提取各检测器的置信度
|
|
@@ -4857,10 +4874,6 @@
|
|
|
4857
4874
|
grayFrame.delete();
|
|
4858
4875
|
if (bgrFrame)
|
|
4859
4876
|
bgrFrame.delete();
|
|
4860
|
-
if (bgrFace)
|
|
4861
|
-
bgrFace.delete();
|
|
4862
|
-
if (grayFace)
|
|
4863
|
-
grayFace.delete();
|
|
4864
4877
|
}
|
|
4865
4878
|
}
|
|
4866
4879
|
/**
|