@sssxyd/face-liveness-detector 0.4.0-alpha.4 → 0.4.0-alpha.5

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
@@ -137,16 +137,16 @@
137
137
  motion_liveness_motion_consistency_threshold: 0.3,
138
138
  motion_liveness_strict_photo_detection: false,
139
139
  // Screen Capture Detection Settings
140
- screen_capture_confidence_threshold: 0.6,
140
+ screen_capture_confidence_threshold: 0.7,
141
141
  screen_capture_detection_strategy: 'adaptive',
142
142
  screen_moire_pattern_threshold: 0.65,
143
143
  screen_moire_pattern_enable_dct: true,
144
144
  screen_moire_pattern_enable_edge_detection: true,
145
145
  screen_color_saturation_threshold: 40,
146
- screen_color_rgb_correlation_threshold: 0.85,
146
+ screen_color_rgb_correlation_threshold: 0.75,
147
147
  screen_color_pixel_entropy_threshold: 6.5,
148
148
  screen_color_gradient_smoothness_threshold: 0.7,
149
- screen_color_confidence_threshold: 0.6,
149
+ screen_color_confidence_threshold: 0.65,
150
150
  screen_rgb_low_freq_start_percent: 0.15,
151
151
  screen_rgb_low_freq_end_percent: 0.35,
152
152
  screen_rgb_energy_ratio_normalization_factor: 10,
@@ -154,7 +154,7 @@
154
154
  screen_rgb_energy_score_weight: 0.40,
155
155
  screen_rgb_asymmetry_score_weight: 0.40,
156
156
  screen_rgb_difference_factor_weight: 0.20,
157
- screen_rgb_confidence_threshold: 0.60,
157
+ screen_rgb_confidence_threshold: 0.65,
158
158
  };
159
159
  /**
160
160
  * Merge user configuration with defaults
@@ -4538,9 +4538,16 @@
4538
4538
  };
4539
4539
  if (this.videoElement) {
4540
4540
  this.videoElement.addEventListener('canplay', onCanPlay, { once: true });
4541
- this.videoElement.play().catch(err => {
4541
+ this.videoElement.play().catch((err) => {
4542
4542
  clearTimeout(timeout);
4543
4543
  cleanup();
4544
+ const errorInfo = this.extractErrorInfo(err);
4545
+ this.emitDebug('video-setup', 'Failed to play video', {
4546
+ error: errorInfo.message,
4547
+ stack: errorInfo.stack,
4548
+ name: errorInfo.name,
4549
+ cause: errorInfo.cause
4550
+ }, 'error');
4544
4551
  reject(err);
4545
4552
  });
4546
4553
  }
@@ -4724,34 +4731,34 @@
4724
4731
  this.scheduleNextDetection(this.options.detect_error_retry_delay);
4725
4732
  return;
4726
4733
  }
4727
- // 当前帧图片
4728
- const bgrFrame = drawCanvasToMat(this.cv, frameCanvas, false);
4729
- if (!bgrFrame) {
4730
- this.emitDebug('detection', 'Failed to convert canvas to OpenCV Mat', {}, 'warn');
4731
- this.scheduleNextDetection(this.options.detect_error_retry_delay);
4732
- return;
4733
- }
4734
- // 当前帧灰度图片
4735
- const grayFrame = matToGray(this.cv, bgrFrame);
4736
- if (!grayFrame) {
4737
- bgrFrame.delete();
4738
- this.emitDebug('detection', 'Failed to convert frame Mat to grayscale', {}, 'warn');
4739
- this.scheduleNextDetection(this.options.detect_error_retry_delay);
4740
- return;
4741
- }
4742
- // 提取人脸区域图片及灰度图片
4743
- const bgrFace = bgrFrame.roi(new this.cv.Rect(faceBox[0], faceBox[1], faceBox[2], faceBox[3]));
4744
- const grayFace = matToGray(this.cv, bgrFace);
4745
- if (!grayFace) {
4746
- bgrFrame.delete();
4747
- bgrFace.delete();
4748
- this.emitDebug('detection', 'Failed to convert face Mat to grayscale', {}, 'warn');
4749
- this.scheduleNextDetection(this.options.detect_error_retry_delay);
4750
- return;
4751
- }
4752
- // 释放不再需要的Mat
4753
- bgrFrame.delete();
4734
+ // 所有需要删除的 Mat 对象
4735
+ let bgrFrame = null;
4736
+ let grayFrame = null;
4737
+ let bgrFace = null;
4738
+ let grayFace = null;
4754
4739
  try {
4740
+ // 当前帧图片
4741
+ bgrFrame = drawCanvasToMat(this.cv, frameCanvas, false);
4742
+ if (!bgrFrame) {
4743
+ this.emitDebug('detection', 'Failed to convert canvas to OpenCV Mat', {}, 'warn');
4744
+ this.scheduleNextDetection(this.options.detect_error_retry_delay);
4745
+ return;
4746
+ }
4747
+ // 当前帧灰度图片
4748
+ grayFrame = matToGray(this.cv, bgrFrame);
4749
+ if (!grayFrame) {
4750
+ this.emitDebug('detection', 'Failed to convert frame Mat to grayscale', {}, 'warn');
4751
+ this.scheduleNextDetection(this.options.detect_error_retry_delay);
4752
+ return;
4753
+ }
4754
+ // 提取人脸区域图片及灰度图片
4755
+ bgrFace = bgrFrame.roi(new this.cv.Rect(faceBox[0], faceBox[1], faceBox[2], faceBox[3]));
4756
+ grayFace = matToGray(this.cv, bgrFace);
4757
+ if (!grayFace) {
4758
+ this.emitDebug('detection', 'Failed to convert face Mat to grayscale', {}, 'warn');
4759
+ this.scheduleNextDetection(this.options.detect_error_retry_delay);
4760
+ return;
4761
+ }
4755
4762
  if (!this.detectionState.screenDetector) {
4756
4763
  this.emit('detector-error', {
4757
4764
  code: exports.ErrorCode.INTERNAL_ERROR,
@@ -4770,11 +4777,8 @@
4770
4777
  }
4771
4778
  // 屏幕捕获检测, 只关心脸部区域
4772
4779
  const screenResult = this.detectionState.screenDetector.detectAuto(bgrFace, grayFace);
4773
- bgrFace.delete();
4774
- grayFace.delete();
4775
4780
  // 屏幕捕获检测器已经准备就绪,其验证结果可信
4776
4781
  if (screenResult.isScreenCapture) {
4777
- grayFrame.delete();
4778
4782
  this.emitDetectorInfo({ code: exports.DetectionCode.FACE_NOT_REAL, message: screenResult.getMessage(), screenConfidence: screenResult.confidenceScore });
4779
4783
  this.emitDebug('screen-capture-detection', 'Screen capture detected - possible video replay attack', {
4780
4784
  confidence: screenResult.confidenceScore,
@@ -4789,13 +4793,18 @@
4789
4793
  if (this.detectionState.motionDetector.isReady()) {
4790
4794
  // 运动检测器已经准备就绪,其验证结果可信
4791
4795
  if (!motionResult.isLively) {
4792
- grayFrame.delete();
4793
4796
  this.emitDebug('motion-detection', 'Motion liveness check failed - possible photo attack', {
4794
4797
  motionScore: motionResult.motionScore,
4795
4798
  keypointVariance: motionResult.keypointVariance,
4799
+ opticalFlowMagnitude: motionResult.opticalFlowMagnitude,
4800
+ eyeMotionScore: motionResult.eyeMotionScore,
4801
+ mouthMotionScore: motionResult.mouthMotionScore,
4796
4802
  motionType: motionResult.motionType,
4797
4803
  minMotionScore: this.options.motion_liveness_min_motion_score,
4798
- minKeypointVariance: this.options.motion_liveness_min_keypoint_variance
4804
+ minKeypointVariance: this.options.motion_liveness_min_keypoint_variance,
4805
+ minOpticalFlowThreshold: this.options.motion_liveness_min_optical_flow_threshold,
4806
+ minMotionConsistencyThreshold: this.options.motion_liveness_motion_consistency_threshold,
4807
+ details: motionResult.details
4799
4808
  }, 'warn');
4800
4809
  this.emitDetectorInfo({
4801
4810
  code: exports.DetectionCode.FACE_NOT_LIVE,
@@ -4844,7 +4853,6 @@
4844
4853
  this.scheduleNextDetection(this.options.detect_error_retry_delay);
4845
4854
  return;
4846
4855
  }
4847
- grayFrame.delete();
4848
4856
  // 当前帧通过常规检查
4849
4857
  this.emitDetectorInfo({ passed: true, code: exports.DetectionCode.FACE_CHECK_PASS, faceRatio: faceRatio, faceFrontal: frontal, imageQuality: qualityResult.score });
4850
4858
  // 处理不同检测阶段的逻辑
@@ -4890,18 +4898,15 @@
4890
4898
  this.scheduleNextDetection(this.options.detect_error_retry_delay);
4891
4899
  }
4892
4900
  finally {
4893
- if (grayFrame) {
4901
+ // 统一在 finally 块中删除所有 Mat 对象
4902
+ if (grayFrame)
4894
4903
  grayFrame.delete();
4895
- }
4896
- if (bgrFrame) {
4904
+ if (bgrFrame)
4897
4905
  bgrFrame.delete();
4898
- }
4899
- if (bgrFace) {
4906
+ if (bgrFace)
4900
4907
  bgrFace.delete();
4901
- }
4902
- if (grayFace) {
4908
+ if (grayFace)
4903
4909
  grayFace.delete();
4904
- }
4905
4910
  }
4906
4911
  }
4907
4912
  /**
@@ -5161,7 +5166,7 @@
5161
5166
  return null;
5162
5167
  }
5163
5168
  this.frameCanvasContext.drawImage(this.videoElement, 0, 0, videoWidth_actual, videoHeight_actual);
5164
- this.emitDebug('capture', 'Frame drawn to canvas');
5169
+ this.emitDebug('capture', 'Frame drawn to canvas as ' + videoHeight_actual + 'x' + videoWidth_actual);
5165
5170
  return this.frameCanvasElement;
5166
5171
  }
5167
5172
  catch (e) {