@sssxyd/face-liveness-detector 0.2.31 → 0.2.32

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
@@ -332,21 +332,25 @@
332
332
  }
333
333
  function _detectBrowserEngine(userAgent) {
334
334
  const ua = userAgent.toLowerCase();
335
- // 检测 Gecko (Firefox)
335
+ // 1. 检测 Gecko (Firefox)
336
336
  if (/firefox/i.test(ua) && !/seamonkey/i.test(ua)) {
337
337
  return 'gecko';
338
338
  }
339
- // 检测 WebKit (Safari, iOS browsers)
340
- // Safari 的特征:有 Safari 但没有 Chrome
341
- if (/safari/i.test(ua) && !/chrome|chromium|crios|edge|edgios|edg|brave|opera|vivaldi|whale|arc|yabrowser|samsung|kiwi|ghostery/i.test(ua)) {
339
+ // 2. 检测 WebKit
340
+ // 包括:Safari、iOS 浏览器、以及那些虽然包含 Chrome 标识但实际是 WebKit 的浏览器(Quark、支付宝、微信等)
341
+ if (/webkit/i.test(ua)) {
342
+ // WebKit 特征明显,包括以下几种情况:
343
+ // - 真正的 Safari(有 Safari 标识)
344
+ // - iOS 浏览器(有 Mobile Safari 标识)
345
+ // - Quark、支付宝、微信等虽然包含 Chrome 标识但是基于 WebKit 的浏览器
342
346
  return 'webkit';
343
347
  }
344
- // 检测 Chromium/Blink
348
+ // 3. 检测 Chromium/Blink
345
349
  // Chrome-based browsers: Chrome, Chromium, Edge, Brave, Opera, Vivaldi, Whale, Arc, etc.
346
350
  if (/chrome|chromium|crios|edge|edgios|edg|brave|opera|vivaldi|whale|arc|yabrowser|samsung|kiwi|ghostery/i.test(ua)) {
347
351
  return 'chromium';
348
352
  }
349
- // 默认为 other
353
+ // 4. 其他浏览器 - 保守方案,使用 WASM
350
354
  return 'other';
351
355
  }
352
356
  function _getOptimalBackendForEngine(engine) {
@@ -592,8 +596,9 @@
592
596
  * @returns Promise that resolves with Human instance
593
597
  */
594
598
  async function loadHuman(modelPath, wasmPath, preferredBackend) {
599
+ const selectedBackend = _detectOptimalBackend(preferredBackend);
595
600
  const config = {
596
- backend: _detectOptimalBackend(preferredBackend),
601
+ backend: selectedBackend,
597
602
  face: {
598
603
  enabled: true,
599
604
  detector: { rotation: false, return: true },
@@ -629,7 +634,13 @@
629
634
  }
630
635
  catch (error) {
631
636
  const errorMsg = error instanceof Error ? error.message : 'Unknown error during Human instantiation';
632
- console.error('[FaceDetectionEngine] Failed to create Human instance:', errorMsg);
637
+ const stack = error instanceof Error ? error.stack : 'N/A';
638
+ console.error('[FaceDetectionEngine] Failed to create Human instance:', {
639
+ errorMsg,
640
+ stack,
641
+ backend: config.backend,
642
+ userAgent: navigator.userAgent
643
+ });
633
644
  throw new Error(`Human instantiation failed: ${errorMsg}`);
634
645
  }
635
646
  const instanceCreateTime = performance.now() - initStartTime;
@@ -638,6 +649,10 @@
638
649
  if (!human) {
639
650
  throw new Error('Human instance is null after creation');
640
651
  }
652
+ // 验证 Human 实例结构(早期检测 WASM 问题)
653
+ if (!human.config) {
654
+ console.warn('[FaceDetectionEngine] Warning: human.config is missing');
655
+ }
641
656
  console.log('[FaceDetectionEngine] Loading Human.js models...');
642
657
  const modelLoadStartTime = performance.now();
643
658
  try {
@@ -648,10 +663,29 @@
648
663
  }, 60000);
649
664
  });
650
665
  // 竞速:哪个先完成就用哪个
651
- await Promise.race([
652
- human.load(),
653
- loadTimeout
654
- ]);
666
+ try {
667
+ await Promise.race([
668
+ human.load(),
669
+ loadTimeout
670
+ ]);
671
+ }
672
+ catch (raceError) {
673
+ // 如果是超时错误,直接抛出
674
+ if (raceError instanceof Error && raceError.message.includes('timeout')) {
675
+ throw raceError;
676
+ }
677
+ // 其他错误,提供更多上下文
678
+ const raceErrorMsg = raceError instanceof Error ? raceError.message : 'Unknown error';
679
+ const raceErrorStack = raceError instanceof Error ? raceError.stack : 'N/A';
680
+ console.error('[FaceDetectionEngine] Error during human.load():', {
681
+ errorMsg: raceErrorMsg,
682
+ stack: raceErrorStack,
683
+ backend: config.backend,
684
+ hasModels: !!human.models,
685
+ modelsKeys: human.models ? Object.keys(human.models).length : 0
686
+ });
687
+ throw new Error(`Model loading error: ${raceErrorMsg}`);
688
+ }
655
689
  const loadTime = performance.now() - modelLoadStartTime;
656
690
  const totalTime = performance.now() - initStartTime;
657
691
  console.log('[FaceDetectionEngine] Human.js loaded successfully', {
@@ -667,26 +701,66 @@
667
701
  if (!human.version) {
668
702
  console.warn('[FaceDetectionEngine] Human.js loaded but version is missing');
669
703
  }
704
+ // 关键验证:检查模型是否真的加载了
705
+ if (!human.models || Object.keys(human.models).length === 0) {
706
+ console.error('[FaceDetectionEngine] CRITICAL: human.models is empty after loading!');
707
+ throw new Error('No models were loaded - human.models is empty');
708
+ }
709
+ // 详细检查每个关键模型及其结构
710
+ const criticalModels = ['face', 'antispoof', 'liveness'];
711
+ const missingModels = [];
712
+ for (const modelName of criticalModels) {
713
+ const model = human.models[modelName];
714
+ if (!model) {
715
+ missingModels.push(modelName);
716
+ console.error(`[FaceDetectionEngine] CRITICAL: Model '${modelName}' is missing!`);
717
+ }
718
+ else {
719
+ const isLoaded = model.loaded || model.state === 'loaded' || !!model.model;
720
+ // 检查模型是否有必要的内部结构(防止 "Cannot read properties of undefined (reading 'inputs')" 错误)
721
+ const hasExecutor = !!model['executor'];
722
+ const hasInputs = !!model.inputs && Array.isArray(model.inputs) && model.inputs.length > 0;
723
+ const hasModelUrl = !!model['modelUrl'];
724
+ console.log(`[FaceDetectionEngine] Model '${modelName}':`, {
725
+ loaded: isLoaded,
726
+ state: model.state,
727
+ hasModel: !!model.model,
728
+ hasExecutor,
729
+ hasInputs,
730
+ hasModelUrl,
731
+ inputsType: typeof model.inputs,
732
+ inputsLength: Array.isArray(model.inputs) ? model.inputs.length : 'N/A'
733
+ });
734
+ // 严格检查:模型必须有以下结构才能正常工作
735
+ if (!isLoaded || !hasExecutor || !hasModelUrl) {
736
+ missingModels.push(`${modelName} (incomplete)`);
737
+ console.error(`[FaceDetectionEngine] WARNING: Model '${modelName}' may not be fully loaded - missing structure`);
738
+ }
739
+ // 如果 inputs 未定义会导致 "Cannot read properties of undefined (reading 'inputs')" 错误
740
+ if (!hasInputs && modelName !== 'antispoof') {
741
+ console.warn(`[FaceDetectionEngine] WARNING: Model '${modelName}' has no inputs - may cause errors during detection`);
742
+ missingModels.push(`${modelName} (no inputs)`);
743
+ }
744
+ }
745
+ }
746
+ if (missingModels.length > 0) {
747
+ console.error('[FaceDetectionEngine] Some critical models failed to load:', missingModels);
748
+ throw new Error(`Critical models not loaded: ${missingModels.join(', ')}`);
749
+ }
670
750
  // 打印加载的模型信息
671
751
  if (human.models) {
672
752
  const loadedModels = Object.entries(human.models).map(([name, model]) => ({
673
753
  name,
674
754
  loaded: model?.loaded || model?.state === 'loaded',
675
- type: typeof model
755
+ type: typeof model,
756
+ hasModel: !!model?.model
676
757
  }));
677
- console.log('[FaceDetectionEngine] Loaded models:', {
758
+ console.log('[FaceDetectionEngine] All loaded models:', {
678
759
  totalModels: Object.keys(human.models).length,
679
760
  models: loadedModels,
680
- allModels: Object.keys(human.models)
761
+ allModelNames: Object.keys(human.models)
681
762
  });
682
763
  }
683
- else {
684
- console.warn('[FaceDetectionEngine] human.models is not available');
685
- }
686
- // 额外验证:检查模型是否加载成功
687
- if (!human.models || Object.keys(human.models).length === 0) {
688
- console.warn('[FaceDetectionEngine] Warning: human.models appears to be empty after loading');
689
- }
690
764
  return human;
691
765
  }
692
766
  catch (error) {
@@ -698,8 +772,25 @@
698
772
  userAgent: navigator.userAgent,
699
773
  platform: navigator.platform,
700
774
  humanVersion: human?.version,
701
- humanConfig: human?.config
775
+ humanConfig: human?.config,
776
+ backend: config.backend
702
777
  });
778
+ // 如果是 WASM 后端失败,尝试降级到 WebGL
779
+ if (selectedBackend === 'wasm' && !errorMsg.includes('timeout') && _isWebGLAvailable()) {
780
+ console.warn('[FaceDetectionEngine] WASM backend failed, attempting fallback to WebGL...');
781
+ config.backend = 'webgl';
782
+ try {
783
+ console.log('[FaceDetectionEngine] Retrying with WebGL backend');
784
+ human = new Human(config);
785
+ await human.load();
786
+ console.log('[FaceDetectionEngine] Successfully loaded with WebGL fallback');
787
+ return human;
788
+ }
789
+ catch (fallbackError) {
790
+ const fallbackMsg = fallbackError instanceof Error ? fallbackError.message : 'Unknown error';
791
+ console.error('[FaceDetectionEngine] WebGL fallback also failed:', fallbackMsg);
792
+ }
793
+ }
703
794
  throw new Error(`Human.js loading failed: ${errorMsg}`);
704
795
  }
705
796
  }
@@ -1741,28 +1832,59 @@
1741
1832
  }
1742
1833
  catch (humanError) {
1743
1834
  const errorMsg = humanError instanceof Error ? humanError.message : 'Unknown error';
1744
- console.error('[FaceDetectionEngine] Human.js loading failed with error:', errorMsg);
1745
- this.emitDebug('initialization', 'Human.js loading failed with exception', {
1835
+ const stack = humanError instanceof Error ? humanError.stack : 'N/A';
1836
+ // 分析错误类型,提供针对性的建议
1837
+ let errorContext = {
1746
1838
  error: errorMsg,
1747
- stack: humanError instanceof Error ? humanError.stack : 'N/A',
1839
+ stack,
1748
1840
  userAgent: navigator.userAgent,
1749
1841
  platform: navigator.platform,
1750
- browser: this.detectBrowserInfo()
1751
- }, 'error');
1842
+ browser: this.detectBrowserInfo(),
1843
+ backend: this.config.tensorflow_backend,
1844
+ source: 'human.js'
1845
+ };
1846
+ // 特定错误类型的诊断
1847
+ if (errorMsg.includes('inputs')) {
1848
+ errorContext.diagnosis = 'Human.js internal error: Model structure incomplete';
1849
+ errorContext.rootCause = 'Human.js library issue - models not fully loaded or WASM backend initialization incomplete';
1850
+ errorContext.suggestion = 'This is a Human.js library issue. Models may not have proper executor or inputs structure. Check WASM initialization and model integrity.';
1851
+ }
1852
+ else if (errorMsg.includes('timeout')) {
1853
+ errorContext.diagnosis = 'Model loading timeout';
1854
+ errorContext.suggestion = 'Network issue or model file too large - check network conditions';
1855
+ }
1856
+ else if (errorMsg.includes('Critical models not loaded')) {
1857
+ errorContext.diagnosis = 'Human.js failed to load required models';
1858
+ errorContext.rootCause = 'Models (face, antispoof, liveness) are missing or incomplete';
1859
+ errorContext.suggestion = 'Check model files and ensure WASM backend is properly initialized';
1860
+ }
1861
+ else if (errorMsg.includes('empty')) {
1862
+ errorContext.diagnosis = 'Models object is empty after loading';
1863
+ errorContext.suggestion = 'Model path may be incorrect or HTTP response failed';
1864
+ }
1865
+ else if (errorMsg.includes('incomplete')) {
1866
+ errorContext.diagnosis = 'Models loaded but structure is incomplete';
1867
+ errorContext.rootCause = 'Human.js internal issue - missing executor, inputs, or modelUrl';
1868
+ errorContext.suggestion = 'Ensure all model resources are fully loaded and accessible';
1869
+ }
1870
+ console.error('[FaceDetectionEngine] Human.js loading failed with detailed error:', errorContext);
1871
+ this.emitDebug('initialization', 'Human.js loading failed with exception', errorContext, 'error');
1752
1872
  this.emit('detector-loaded', {
1753
1873
  success: false,
1754
- error: `Failed to load Human.js: ${errorMsg}`
1874
+ error: `Failed to load Human.js: ${errorMsg}`,
1875
+ details: errorContext
1755
1876
  });
1756
1877
  this.emit('detector-error', {
1757
1878
  code: exports.ErrorCode.DETECTOR_NOT_INITIALIZED,
1758
- message: `Human.js loading error: ${errorMsg}`
1879
+ message: `Human.js loading error: ${errorMsg}`,
1880
+ details: errorContext
1759
1881
  });
1760
1882
  return;
1761
1883
  }
1762
1884
  const humanLoadTime = performance.now() - humanStartTime;
1763
1885
  if (!this.human) {
1764
1886
  const errorMsg = 'Failed to load Human.js: instance is null';
1765
- console.log('[FaceDetectionEngine] ' + errorMsg);
1887
+ console.error('[FaceDetectionEngine] ' + errorMsg);
1766
1888
  this.emitDebug('initialization', errorMsg, { loadTime: humanLoadTime }, 'error');
1767
1889
  this.emit('detector-loaded', {
1768
1890
  success: false,