@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.js CHANGED
@@ -2774,36 +2774,39 @@
2774
2774
  * 计算综合屏幕拍摄置信度
2775
2775
  * 基于各项特征指标的加权平均
2776
2776
  *
2777
- * 权重说明:
2778
- * - RGB相关性 (42%):最强屏幕特征,完全独立于分辨率,是核心判断指标
2779
- * - 饱和度 (36%):次强屏幕特征,屏幕图像去饱和特征明显
2780
- * - 像素熵 (22%):辅助特征,屏幕像素分布规则性
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% (from 35/83)
2794
- saturation: 0.36, // 饱和度:36% (from 30/83)
2795
- entropy: 0.22, // 像素熵:22% (from 18/83)
2785
+ rgbCorrelation: 0.42, // RGB相关性:42% (完整图像中最强屏幕特征)
2786
+ saturation: 0.36, // 饱和度:36%
2787
+ entropy: 0.22, // 像素熵:22%
2796
2788
  };
2797
- const confidence = rgbCorrelation.score * weights.rgbCorrelation +
2798
- saturation.score * weights.saturation +
2799
- entropy.score * weights.entropy;
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
- moire_threshold: 0.65,
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
- const edges = new cv.Mat();
2910
- cv.Canny(gray, edges, 50, 150);
2911
- const periodicity = detectPeriodicity(cv, edges);
2912
- const directionConsistency = analyzeEdgeDirection(cv, edges);
2913
- edges.delete();
2914
- // 融合两种方法的结果
2915
- moireStrength += (periodicity + directionConsistency) * 0.5 * 0.4;
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.abs(moireStrength - finalConfig.moire_threshold) / 0.35, 1.0),
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
- const rgbCanDetermine = rgbEmissionResult.isScreenCapture && rgbEmissionResult.confidence > 0.75;
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
- const colorCanDetermine = colorResult.isScreenCapture && colorResult.confidence > 0.75;
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
- const moireCanDetermine = moireResult.isScreenCapture && moireResult.confidence > 0.75;
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
- const weightedConfidence = rgbEmissionResult.confidence * 0.5 + // RGB权重 50%
3916
- colorResult.confidence * 0.3 + // Color权重 30%
3917
- moireResult.confidence * 0.2; // Moiré权重 20%
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(bgrFace, grayFace, this.options.debug_mode);
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
  /**