@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 CHANGED
@@ -2752,36 +2752,39 @@ function calculateCorrelation(mat1, mat2) {
2752
2752
  * 计算综合屏幕拍摄置信度
2753
2753
  * 基于各项特征指标的加权平均
2754
2754
  *
2755
- * 权重说明:
2756
- * - RGB相关性 (42%):最强屏幕特征,完全独立于分辨率,是核心判断指标
2757
- * - 饱和度 (36%):次强屏幕特征,屏幕图像去饱和特征明显
2758
- * - 像素熵 (22%):辅助特征,屏幕像素分布规则性
2759
- *
2760
- * 删除梯度特征原因:
2761
- * - 梯度特征对分辨率敏感,是主要噪声源
2762
- * - 权重仅 17%,且可靠性最低(⭐⭐⭐)
2763
- * - 删除后准确率反而提升 1-3%(去掉噪声)
2764
- * - 代码简化 40%,性能提升 40%(少 80ms 计算)
2765
- * - RGB相关性 + 饱和度 已足以识别屏幕特性
2755
+ * 针对完整高分辨率图像(1920×1080+)优化:
2756
+ * - 足够的屏幕像素采样,所有特征都清晰
2757
+ * - RGB相关性权重提升(屏幕同质性强)
2758
+ * - 饱和度权重平衡(屏幕特征明显)
2759
+ * - 像素熵权重保持
2766
2760
  */
2767
2761
  function calculateScreenConfidence(saturation, rgbCorrelation, entropy) {
2768
- // 加权平均:各特征的贡献度
2769
- // 权重自动归一化:35/83 : 30/83 : 18/83
2770
2762
  const weights = {
2771
- rgbCorrelation: 0.42, // RGB相关性:42% (from 35/83)
2772
- saturation: 0.36, // 饱和度:36% (from 30/83)
2773
- entropy: 0.22, // 像素熵:22% (from 18/83)
2763
+ rgbCorrelation: 0.42, // RGB相关性:42% (完整图像中最强屏幕特征)
2764
+ saturation: 0.36, // 饱和度:36%
2765
+ entropy: 0.22, // 像素熵:22%
2774
2766
  };
2775
- const confidence = rgbCorrelation.score * weights.rgbCorrelation +
2776
- saturation.score * weights.saturation +
2777
- entropy.score * weights.entropy;
2767
+ // 为每个特征计算连续得分(而不是二元的 0 1)
2768
+ // 这样即使特征未达到硬阈值,仍然可以贡献部分置信度
2769
+ // RGB 相关性:0.75-1.0 范围评分(屏幕通常 > 0.85)
2770
+ const rgbScore = Math.max(0, (rgbCorrelation.value - 0.7) / (1.0 - 0.7));
2771
+ // 饱和度:0-40 范围评分(屏幕通常 < 40)
2772
+ const satScore = Math.max(0, 1 - (saturation.value / 40));
2773
+ // 像素熵:4-8 范围评分(屏幕通常 < 6.5)
2774
+ const entScore = Math.max(0, 1 - (entropy.value / 8));
2775
+ const confidence = rgbScore * weights.rgbCorrelation +
2776
+ satScore * weights.saturation +
2777
+ entScore * weights.entropy;
2778
2778
  return Math.min(1, Math.max(0, confidence));
2779
2779
  }
2780
2780
 
2781
2781
  // ==================== 类型定义 ====================
2782
2782
  // ==================== 默认配置 ====================
2783
2783
  const DEFAULT_CONFIG = {
2784
- moire_threshold: 0.65,
2784
+ // 针对完整高分辨率图像(1920×1080+)优化:
2785
+ // - 完整采样提供充足的屏幕像素网格
2786
+ // - 莫尔纹信号清晰,使用更严格的阈值
2787
+ moire_threshold: 0.55,
2785
2788
  enable_dct: true,
2786
2789
  enable_edge_detection: true,
2787
2790
  };
@@ -2874,6 +2877,16 @@ function detectMoirePattern(cv, gray, config) {
2874
2877
  const finalConfig = { ...DEFAULT_CONFIG, ...config };
2875
2878
  if (!cv)
2876
2879
  throw new Error('OpenCV instance not initialized');
2880
+ // 验证灰度图像是否有效
2881
+ if (!gray || gray.rows === 0 || gray.cols === 0) {
2882
+ console.warn('[MoirePattern] Invalid gray image');
2883
+ return {
2884
+ isScreenCapture: false,
2885
+ confidence: 0.0,
2886
+ moireStrength: 0.0,
2887
+ dominantFrequencies: [],
2888
+ };
2889
+ }
2877
2890
  let moireStrength = 0;
2878
2891
  let dominantFrequencies = [];
2879
2892
  // 使用 DCT 频域分析(优先方案)
@@ -2883,19 +2896,25 @@ function detectMoirePattern(cv, gray, config) {
2883
2896
  dominantFrequencies = dctResult.dominantFrequencies;
2884
2897
  }
2885
2898
  // 辅助:Canny 边缘检测方案用于增强检测
2886
- if (finalConfig.enable_edge_detection) {
2887
- const edges = new cv.Mat();
2888
- cv.Canny(gray, edges, 50, 150);
2889
- const periodicity = detectPeriodicity(cv, edges);
2890
- const directionConsistency = analyzeEdgeDirection(cv, edges);
2891
- edges.delete();
2892
- // 融合两种方法的结果
2893
- moireStrength += (periodicity + directionConsistency) * 0.5 * 0.4;
2899
+ if (finalConfig.enable_edge_detection && moireStrength < 0.5) {
2900
+ try {
2901
+ const edges = new cv.Mat();
2902
+ cv.Canny(gray, edges, 50, 150);
2903
+ const periodicity = detectPeriodicity(cv, edges);
2904
+ const directionConsistency = analyzeEdgeDirection(cv, edges);
2905
+ edges.delete();
2906
+ // 融合两种方法的结果
2907
+ moireStrength += (periodicity + directionConsistency) * 0.5 * 0.4;
2908
+ }
2909
+ catch (edgeError) {
2910
+ console.warn('[MoirePattern] Edge detection failed, skipping:', edgeError);
2911
+ // 继续使用 DCT 结果
2912
+ }
2894
2913
  }
2895
2914
  const isScreenCapture = moireStrength > finalConfig.moire_threshold;
2896
2915
  return {
2897
2916
  isScreenCapture,
2898
- confidence: Math.min(Math.abs(moireStrength - finalConfig.moire_threshold) / 0.35, 1.0),
2917
+ confidence: Math.min(Math.max(0, moireStrength), 1.0),
2899
2918
  moireStrength,
2900
2919
  dominantFrequencies,
2901
2920
  };
@@ -3446,6 +3465,10 @@ function analyzeEdgeDirection(cv, edges) {
3446
3465
 
3447
3466
  /**
3448
3467
  * RGB 发光模式检测的默认配置
3468
+ *
3469
+ * 针对完整高分辨率图像(1920×1080+)优化:
3470
+ * - 足够的频域采样,RGB子像素周期信号清晰
3471
+ * - 使用更严格的置信度阈值
3449
3472
  */
3450
3473
  const DEFAULT_RGB_EMISSION_CONFIG = {
3451
3474
  // 频率带分析参数
@@ -3797,7 +3820,8 @@ class ScreenCaptureDetector {
3797
3820
  });
3798
3821
  }
3799
3822
  // 明确判定为屏幕捕捉时,直接返回
3800
- const rgbCanDetermine = rgbEmissionResult.isScreenCapture && rgbEmissionResult.confidence > 0.75;
3823
+ // 完整高分辨率图像:RGB信号应该很强
3824
+ const rgbCanDetermine = rgbEmissionResult.isScreenCapture && rgbEmissionResult.confidence > 0.65;
3801
3825
  if (rgbCanDetermine) {
3802
3826
  const totalTime = performance.now() - startTime;
3803
3827
  if (debug) {
@@ -3833,7 +3857,8 @@ class ScreenCaptureDetector {
3833
3857
  });
3834
3858
  }
3835
3859
  // 明确判定为屏幕捕捉时,直接返回
3836
- const colorCanDetermine = colorResult.isScreenCapture && colorResult.confidence > 0.75;
3860
+ // 完整高分辨率图像:色彩特征应该清晰
3861
+ const colorCanDetermine = colorResult.isScreenCapture && colorResult.confidence > 0.65;
3837
3862
  if (colorCanDetermine) {
3838
3863
  const totalTime = performance.now() - startTime;
3839
3864
  if (debug) {
@@ -3874,7 +3899,8 @@ class ScreenCaptureDetector {
3874
3899
  });
3875
3900
  }
3876
3901
  // 明确判定为屏幕捕捉时,直接返回
3877
- const moireCanDetermine = moireResult.isScreenCapture && moireResult.confidence > 0.75;
3902
+ // 完整高分辨率图像:莫尔纹应该很清晰
3903
+ const moireCanDetermine = moireResult.isScreenCapture && moireResult.confidence > 0.65;
3878
3904
  if (moireCanDetermine) {
3879
3905
  const totalTime = performance.now() - startTime;
3880
3906
  if (debug) {
@@ -3890,9 +3916,10 @@ class ScreenCaptureDetector {
3890
3916
  }
3891
3917
  }
3892
3918
  // 都不能明确判定,计算加权置信度
3893
- const weightedConfidence = rgbEmissionResult.confidence * 0.5 + // RGB权重 50%
3894
- colorResult.confidence * 0.3 + // Color权重 30%
3895
- moireResult.confidence * 0.2; // Moiré权重 20%
3919
+ // 优化权重比例:针对完整高分辨率图像(1920×1080+)
3920
+ const weightedConfidence = rgbEmissionResult.confidence * 0.50 + // RGB权重 50%
3921
+ colorResult.confidence * 0.25 + // Color权重 25%
3922
+ moireResult.confidence * 0.25; // Moiré权重 25% (提升,完整图像中莫尔纹清晰)
3896
3923
  // 第 3 层:用加权置信度判定最终结果
3897
3924
  const isScreenCapture = weightedConfidence > this.confidenceThreshold;
3898
3925
  // 根据加权置信度判断风险等级
@@ -4626,8 +4653,6 @@ class FaceDetectionEngine extends SimpleEventEmitter {
4626
4653
  // 所有需要删除的 Mat 对象
4627
4654
  let bgrFrame = null;
4628
4655
  let grayFrame = null;
4629
- let bgrFace = null;
4630
- let grayFace = null;
4631
4656
  try {
4632
4657
  // 当前帧图片
4633
4658
  bgrFrame = drawCanvasToMat(this.cv, frameCanvas, false);
@@ -4643,14 +4668,6 @@ class FaceDetectionEngine extends SimpleEventEmitter {
4643
4668
  this.scheduleNextDetection(this.options.detect_error_retry_delay);
4644
4669
  return;
4645
4670
  }
4646
- // 提取人脸区域图片及灰度图片
4647
- bgrFace = bgrFrame.roi(new this.cv.Rect(faceBox[0], faceBox[1], faceBox[2], faceBox[3]));
4648
- grayFace = matToGray(this.cv, bgrFace);
4649
- if (!grayFace) {
4650
- this.emitDebug('detection', 'Failed to convert face Mat to grayscale', {}, 'warn');
4651
- this.scheduleNextDetection(this.options.detect_error_retry_delay);
4652
- return;
4653
- }
4654
4671
  if (!this.detectionState.screenDetector) {
4655
4672
  this.emit('detector-error', {
4656
4673
  code: ErrorCode.INTERNAL_ERROR,
@@ -4668,7 +4685,7 @@ class FaceDetectionEngine extends SimpleEventEmitter {
4668
4685
  return;
4669
4686
  }
4670
4687
  // 屏幕捕获检测, 只关心脸部区域
4671
- const screenResult = this.detectionState.screenDetector.detect(bgrFace, grayFace, this.options.debug_mode);
4688
+ const screenResult = this.detectionState.screenDetector.detect(bgrFrame, grayFrame, this.options.debug_mode);
4672
4689
  // 屏幕捕获检测器已经准备就绪,其验证结果可信
4673
4690
  if (screenResult.isScreenCapture) {
4674
4691
  // 从 executedMethods 提取各检测器的置信度
@@ -4835,10 +4852,6 @@ class FaceDetectionEngine extends SimpleEventEmitter {
4835
4852
  grayFrame.delete();
4836
4853
  if (bgrFrame)
4837
4854
  bgrFrame.delete();
4838
- if (bgrFace)
4839
- bgrFace.delete();
4840
- if (grayFace)
4841
- grayFace.delete();
4842
4855
  }
4843
4856
  }
4844
4857
  /**