@sssxyd/face-liveness-detector 0.2.31 → 0.2.33
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/README.md +0 -47
- package/dist/index.esm.js +219 -93
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +219 -93
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/library-loader.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,53 +31,6 @@ pnpm add @sssxyd/face-liveness-detector @vladmandic/human @techstark/opencv-js
|
|
|
31
31
|
|
|
32
32
|
> **Note**: `@vladmandic/human` and `@techstark/opencv-js` are peer dependencies and must be installed separately to avoid bundling large libraries. This keeps your final bundle size smaller if you're already using these libraries elsewhere in your project.
|
|
33
33
|
|
|
34
|
-
## Quick Start - Using Local Model Files (Recommended)
|
|
35
|
-
|
|
36
|
-
To improve performance and reduce external dependencies, you can download and use local copies of model files:
|
|
37
|
-
|
|
38
|
-
### Step 1: Download Model Files
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
# Copy Human.js models locally
|
|
42
|
-
node copy-human-models.js
|
|
43
|
-
|
|
44
|
-
# Download TensorFlow.js WASM files
|
|
45
|
-
node download-tensorflow-wasm.js
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
This will create:
|
|
49
|
-
- `public/models/` - Human.js face detection models
|
|
50
|
-
- `public/wasm/` - TensorFlow.js WASM backend files
|
|
51
|
-
|
|
52
|
-
### Step 2: Initialize Engine with Local Files
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
import FaceDetectionEngine from '@sssxyd/face-liveness-detector'
|
|
56
|
-
|
|
57
|
-
// Configure to use local model files
|
|
58
|
-
const engine = new FaceDetectionEngine({
|
|
59
|
-
human_model_path: '/models', // Path to downloaded models
|
|
60
|
-
tensorflow_wasm_path: '/wasm', // Path to WASM files
|
|
61
|
-
min_face_ratio: 0.5,
|
|
62
|
-
max_face_ratio: 0.9,
|
|
63
|
-
liveness_action_count: 1,
|
|
64
|
-
liveness_action_list: ['blink']
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
// Initialize and start detection
|
|
68
|
-
await engine.initialize()
|
|
69
|
-
const videoElement = document.getElementById('video') as HTMLVideoElement
|
|
70
|
-
await engine.startDetection(videoElement)
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Step 3: Serve Static Files
|
|
74
|
-
|
|
75
|
-
Make sure your web server serves the `public/` directory:
|
|
76
|
-
|
|
77
|
-
```typescript
|
|
78
|
-
// Express.js example
|
|
79
|
-
app.use(express.static('public'))
|
|
80
|
-
```
|
|
81
34
|
|
|
82
35
|
## Quick Start - Using Default CDN Files
|
|
83
36
|
|
package/dist/index.esm.js
CHANGED
|
@@ -310,21 +310,26 @@ function _isWebGLAvailable() {
|
|
|
310
310
|
}
|
|
311
311
|
function _detectBrowserEngine(userAgent) {
|
|
312
312
|
const ua = userAgent.toLowerCase();
|
|
313
|
-
// 检测 Gecko (Firefox)
|
|
313
|
+
// 1. 检测 Gecko (Firefox)
|
|
314
314
|
if (/firefox/i.test(ua) && !/seamonkey/i.test(ua)) {
|
|
315
315
|
return 'gecko';
|
|
316
316
|
}
|
|
317
|
-
// 检测 WebKit
|
|
318
|
-
// Safari 的特征:有 Safari 但没有 Chrome
|
|
319
|
-
if (/safari/i.test(ua) && !/chrome|chromium|crios|edge|edgios|edg|brave|opera|vivaldi|whale|arc|yabrowser|samsung|kiwi|ghostery/i.test(ua)) {
|
|
320
|
-
return 'webkit';
|
|
321
|
-
}
|
|
322
|
-
// 检测 Chromium/Blink
|
|
317
|
+
// 2. 检测 Chromium/Blink(必须在 WebKit 之前,因为 Chrome 的 user-agent 也包含 WebKit)
|
|
323
318
|
// Chrome-based browsers: Chrome, Chromium, Edge, Brave, Opera, Vivaldi, Whale, Arc, etc.
|
|
324
319
|
if (/chrome|chromium|crios|edge|edgios|edg|brave|opera|vivaldi|whale|arc|yabrowser|samsung|kiwi|ghostery/i.test(ua)) {
|
|
325
320
|
return 'chromium';
|
|
326
321
|
}
|
|
327
|
-
//
|
|
322
|
+
// 3. 检测 WebKit(真正的 Safari 和 iOS 浏览器)
|
|
323
|
+
// 注意:真正的 WebKit 浏览器(Safari)user-agent 不包含 Chrome 标识
|
|
324
|
+
// 包括:Safari、iOS 浏览器、以及那些虽然包含 Chrome 标识但实际是 WebKit 的浏览器(Quark、支付宝、微信等)
|
|
325
|
+
if (/webkit/i.test(ua)) {
|
|
326
|
+
// WebKit 特征明显,包括以下几种情况:
|
|
327
|
+
// - 真正的 Safari(有 Safari 标识)
|
|
328
|
+
// - iOS 浏览器(有 Mobile Safari 标识)
|
|
329
|
+
// - Quark、支付宝、微信等虽然包含 Chrome 标识但是基于 WebKit 的浏览器
|
|
330
|
+
return 'webkit';
|
|
331
|
+
}
|
|
332
|
+
// 4. 其他浏览器 - 保守方案,使用 WASM
|
|
328
333
|
return 'other';
|
|
329
334
|
}
|
|
330
335
|
function _getOptimalBackendForEngine(engine) {
|
|
@@ -563,15 +568,11 @@ function getCvSync() {
|
|
|
563
568
|
return null;
|
|
564
569
|
}
|
|
565
570
|
/**
|
|
566
|
-
*
|
|
567
|
-
* @param modelPath - Path to model files (optional)
|
|
568
|
-
* @param wasmPath - Path to WASM files (optional)
|
|
569
|
-
* @param preferredBackend - Preferred TensorFlow backend: 'auto' | 'webgl' | 'wasm' (default: 'auto')
|
|
570
|
-
* @returns Promise that resolves with Human instance
|
|
571
|
+
* Create Human.js configuration object
|
|
571
572
|
*/
|
|
572
|
-
|
|
573
|
+
function _createHumanConfig(backend, modelPath, wasmPath) {
|
|
573
574
|
const config = {
|
|
574
|
-
backend
|
|
575
|
+
backend,
|
|
575
576
|
face: {
|
|
576
577
|
enabled: true,
|
|
577
578
|
detector: { rotation: false, return: true },
|
|
@@ -585,102 +586,196 @@ async function loadHuman(modelPath, wasmPath, preferredBackend) {
|
|
|
585
586
|
object: { enabled: false },
|
|
586
587
|
gesture: { enabled: true }
|
|
587
588
|
};
|
|
588
|
-
// 只在提供了路径时才设置,否则让 Human.js 使用默认加载策略
|
|
589
589
|
if (modelPath) {
|
|
590
590
|
config.modelBasePath = modelPath;
|
|
591
591
|
}
|
|
592
592
|
if (wasmPath) {
|
|
593
593
|
config.wasmPath = wasmPath;
|
|
594
594
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
595
|
+
return config;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Load and verify Human.js models
|
|
599
|
+
*/
|
|
600
|
+
async function _loadAndVerifyHuman(human) {
|
|
601
|
+
const modelLoadStartTime = performance.now();
|
|
602
|
+
try {
|
|
603
|
+
await human.load();
|
|
604
|
+
}
|
|
605
|
+
catch (error) {
|
|
606
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
607
|
+
const errorStack = error instanceof Error ? error.stack : 'N/A';
|
|
608
|
+
console.error('[FaceDetectionEngine] Error during human.load():', {
|
|
609
|
+
errorMsg,
|
|
610
|
+
stack: errorStack,
|
|
611
|
+
backend: human.config?.backend,
|
|
612
|
+
hasModels: !!human.models,
|
|
613
|
+
modelsKeys: human.models ? Object.keys(human.models).length : 0
|
|
614
|
+
});
|
|
615
|
+
throw new Error(`Model loading error: ${errorMsg}`);
|
|
616
|
+
}
|
|
617
|
+
const loadTime = performance.now() - modelLoadStartTime;
|
|
618
|
+
console.log('[FaceDetectionEngine] Human.js loaded successfully', {
|
|
619
|
+
modelLoadTime: `${loadTime.toFixed(2)}ms`,
|
|
620
|
+
version: human.version,
|
|
621
|
+
config: human.config
|
|
601
622
|
});
|
|
623
|
+
// 验证加载后的 Human 实例有必要的方法和属性
|
|
624
|
+
if (typeof human.detect !== 'function') {
|
|
625
|
+
throw new Error('Human.detect method not available after loading');
|
|
626
|
+
}
|
|
627
|
+
if (!human.version) {
|
|
628
|
+
console.warn('[FaceDetectionEngine] Human.js loaded but version is missing');
|
|
629
|
+
}
|
|
630
|
+
// 关键验证:检查模型是否真的加载了
|
|
631
|
+
if (!human.models || Object.keys(human.models).length === 0) {
|
|
632
|
+
console.error('[FaceDetectionEngine] CRITICAL: human.models is empty after loading!');
|
|
633
|
+
throw new Error('No models were loaded - human.models is empty');
|
|
634
|
+
}
|
|
635
|
+
// 详细检查每个关键模型及其结构
|
|
636
|
+
const criticalModels = ['face', 'antispoof', 'liveness'];
|
|
637
|
+
const missingModels = [];
|
|
638
|
+
for (const modelName of criticalModels) {
|
|
639
|
+
const model = human.models[modelName];
|
|
640
|
+
if (!model) {
|
|
641
|
+
missingModels.push(modelName);
|
|
642
|
+
console.error(`[FaceDetectionEngine] CRITICAL: Model '${modelName}' is missing!`);
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
const isLoaded = model.loaded || model.state === 'loaded' || !!model.model;
|
|
646
|
+
// 检查模型是否有必要的内部结构(防止 "Cannot read properties of undefined (reading 'inputs')" 错误)
|
|
647
|
+
const hasExecutor = !!model['executor'];
|
|
648
|
+
const hasInputs = !!model.inputs && Array.isArray(model.inputs) && model.inputs.length > 0;
|
|
649
|
+
const hasModelUrl = !!model['modelUrl'];
|
|
650
|
+
console.log(`[FaceDetectionEngine] Model '${modelName}':`, {
|
|
651
|
+
loaded: isLoaded,
|
|
652
|
+
state: model.state,
|
|
653
|
+
hasModel: !!model.model,
|
|
654
|
+
hasExecutor,
|
|
655
|
+
hasInputs,
|
|
656
|
+
hasModelUrl,
|
|
657
|
+
inputsType: typeof model.inputs,
|
|
658
|
+
inputsLength: Array.isArray(model.inputs) ? model.inputs.length : 'N/A'
|
|
659
|
+
});
|
|
660
|
+
// 严格检查:模型必须有以下结构才能正常工作
|
|
661
|
+
if (!isLoaded || !hasExecutor || !hasModelUrl) {
|
|
662
|
+
missingModels.push(`${modelName} (incomplete)`);
|
|
663
|
+
console.error(`[FaceDetectionEngine] WARNING: Model '${modelName}' may not be fully loaded - missing structure`);
|
|
664
|
+
}
|
|
665
|
+
// 如果 inputs 未定义会导致 "Cannot read properties of undefined (reading 'inputs')" 错误
|
|
666
|
+
if (!hasInputs && modelName !== 'antispoof') {
|
|
667
|
+
console.warn(`[FaceDetectionEngine] WARNING: Model '${modelName}' has no inputs - may cause errors during detection`);
|
|
668
|
+
missingModels.push(`${modelName} (no inputs)`);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (missingModels.length > 0) {
|
|
673
|
+
console.error('[FaceDetectionEngine] Some critical models failed to load:', missingModels);
|
|
674
|
+
throw new Error(`Critical models not loaded: ${missingModels.join(', ')}`);
|
|
675
|
+
}
|
|
676
|
+
// 打印加载的模型信息
|
|
677
|
+
if (human.models) {
|
|
678
|
+
const loadedModels = Object.entries(human.models).map(([name, model]) => ({
|
|
679
|
+
name,
|
|
680
|
+
loaded: model?.loaded || model?.state === 'loaded',
|
|
681
|
+
type: typeof model,
|
|
682
|
+
hasModel: !!model?.model
|
|
683
|
+
}));
|
|
684
|
+
console.log('[FaceDetectionEngine] All loaded models:', {
|
|
685
|
+
backend: human.config?.backend,
|
|
686
|
+
modelBasePath: human.config?.modelBasePath,
|
|
687
|
+
wasmPath: human.config?.wasmPath,
|
|
688
|
+
totalModels: Object.keys(human.models).length,
|
|
689
|
+
models: loadedModels,
|
|
690
|
+
allModelNames: Object.keys(human.models)
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Try to load Human with a specific backend
|
|
696
|
+
* @param config The configuration object
|
|
697
|
+
* @param backend The backend to try
|
|
698
|
+
* @returns Human instance or null if fails
|
|
699
|
+
*/
|
|
700
|
+
async function _tryLoadHumanWithBackend(backend, modelPath, wasmPath) {
|
|
701
|
+
const config = _createHumanConfig(backend, modelPath, wasmPath);
|
|
602
702
|
const initStartTime = performance.now();
|
|
603
|
-
console.log('[FaceDetectionEngine] Creating Human instance...');
|
|
604
703
|
let human;
|
|
605
704
|
try {
|
|
606
705
|
human = new Human(config);
|
|
607
706
|
}
|
|
608
707
|
catch (error) {
|
|
609
708
|
const errorMsg = error instanceof Error ? error.message : 'Unknown error during Human instantiation';
|
|
610
|
-
|
|
611
|
-
|
|
709
|
+
const stack = error instanceof Error ? error.stack : 'N/A';
|
|
710
|
+
console.error(`[FaceDetectionEngine] Failed to create Human instance (${backend}):`, {
|
|
711
|
+
errorMsg,
|
|
712
|
+
stack,
|
|
713
|
+
backend: config.backend,
|
|
714
|
+
userAgent: navigator.userAgent
|
|
715
|
+
});
|
|
716
|
+
return null;
|
|
612
717
|
}
|
|
613
|
-
const instanceCreateTime = performance.now() - initStartTime;
|
|
614
|
-
console.log(`[FaceDetectionEngine] Human instance created, took ${instanceCreateTime.toFixed(2)}ms`);
|
|
615
718
|
// 验证 Human 实例
|
|
616
719
|
if (!human) {
|
|
617
|
-
|
|
720
|
+
console.error(`[FaceDetectionEngine] Human instance is null (${backend})`);
|
|
721
|
+
return null;
|
|
722
|
+
}
|
|
723
|
+
// 验证 Human 实例结构(早期检测 WASM 问题)
|
|
724
|
+
if (!human.config) {
|
|
725
|
+
console.warn('[FaceDetectionEngine] Warning: human.config is missing');
|
|
618
726
|
}
|
|
619
|
-
console.log('[FaceDetectionEngine] Loading Human.js models...');
|
|
620
|
-
const modelLoadStartTime = performance.now();
|
|
621
727
|
try {
|
|
622
|
-
|
|
623
|
-
const loadTimeout = new Promise((_, reject) => {
|
|
624
|
-
const timeoutId = setTimeout(() => {
|
|
625
|
-
reject(new Error('Human.js load() timeout after 60 seconds - possible issue with model loading on mobile'));
|
|
626
|
-
}, 60000);
|
|
627
|
-
});
|
|
628
|
-
// 竞速:哪个先完成就用哪个
|
|
629
|
-
await Promise.race([
|
|
630
|
-
human.load(),
|
|
631
|
-
loadTimeout
|
|
632
|
-
]);
|
|
633
|
-
const loadTime = performance.now() - modelLoadStartTime;
|
|
728
|
+
await _loadAndVerifyHuman(human);
|
|
634
729
|
const totalTime = performance.now() - initStartTime;
|
|
635
|
-
console.log(
|
|
636
|
-
modelLoadTime: `${loadTime.toFixed(2)}ms`,
|
|
637
|
-
totalInitTime: `${totalTime.toFixed(2)}ms`,
|
|
638
|
-
version: human.version,
|
|
639
|
-
config: human.config
|
|
640
|
-
});
|
|
641
|
-
// 验证加载后的 Human 实例有必要的方法和属性
|
|
642
|
-
if (typeof human.detect !== 'function') {
|
|
643
|
-
throw new Error('Human.detect method not available after loading');
|
|
644
|
-
}
|
|
645
|
-
if (!human.version) {
|
|
646
|
-
console.warn('[FaceDetectionEngine] Human.js loaded but version is missing');
|
|
647
|
-
}
|
|
648
|
-
// 打印加载的模型信息
|
|
649
|
-
if (human.models) {
|
|
650
|
-
const loadedModels = Object.entries(human.models).map(([name, model]) => ({
|
|
651
|
-
name,
|
|
652
|
-
loaded: model?.loaded || model?.state === 'loaded',
|
|
653
|
-
type: typeof model
|
|
654
|
-
}));
|
|
655
|
-
console.log('[FaceDetectionEngine] Loaded models:', {
|
|
656
|
-
totalModels: Object.keys(human.models).length,
|
|
657
|
-
models: loadedModels,
|
|
658
|
-
allModels: Object.keys(human.models)
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
else {
|
|
662
|
-
console.warn('[FaceDetectionEngine] human.models is not available');
|
|
663
|
-
}
|
|
664
|
-
// 额外验证:检查模型是否加载成功
|
|
665
|
-
if (!human.models || Object.keys(human.models).length === 0) {
|
|
666
|
-
console.warn('[FaceDetectionEngine] Warning: human.models appears to be empty after loading');
|
|
667
|
-
}
|
|
730
|
+
console.log(`[FaceDetectionEngine] Successfully loaded Human.js with ${backend} backend in ${totalTime.toFixed(2)}ms`);
|
|
668
731
|
return human;
|
|
669
732
|
}
|
|
670
733
|
catch (error) {
|
|
671
734
|
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
errorMsg,
|
|
675
|
-
stack,
|
|
676
|
-
userAgent: navigator.userAgent,
|
|
677
|
-
platform: navigator.platform,
|
|
678
|
-
humanVersion: human?.version,
|
|
679
|
-
humanConfig: human?.config
|
|
680
|
-
});
|
|
681
|
-
throw new Error(`Human.js loading failed: ${errorMsg}`);
|
|
735
|
+
console.error(`[FaceDetectionEngine] Failed to load models with ${backend} backend:`, errorMsg);
|
|
736
|
+
return null;
|
|
682
737
|
}
|
|
683
738
|
}
|
|
739
|
+
/**
|
|
740
|
+
* Load Human.js
|
|
741
|
+
* @param modelPath - Path to model files (optional)
|
|
742
|
+
* @param wasmPath - Path to WASM files (optional)
|
|
743
|
+
* @param preferredBackend - Preferred TensorFlow backend: 'auto' | 'webgl' | 'wasm' (default: 'auto')
|
|
744
|
+
* @returns Promise that resolves with Human instance
|
|
745
|
+
*/
|
|
746
|
+
async function loadHuman(modelPath, wasmPath, preferredBackend) {
|
|
747
|
+
const selectedBackend = _detectOptimalBackend(preferredBackend);
|
|
748
|
+
console.log('[FaceDetectionEngine] Starting Human.js initialization:', {
|
|
749
|
+
selectedBackend,
|
|
750
|
+
modelBasePath: modelPath || '(using default)',
|
|
751
|
+
wasmPath: wasmPath || '(using default)',
|
|
752
|
+
userAgent: navigator.userAgent,
|
|
753
|
+
platform: navigator.platform
|
|
754
|
+
});
|
|
755
|
+
// 尝试用主后端加载
|
|
756
|
+
const human = await _tryLoadHumanWithBackend(selectedBackend, modelPath, wasmPath);
|
|
757
|
+
if (human) {
|
|
758
|
+
return human;
|
|
759
|
+
}
|
|
760
|
+
console.log(`[FaceDetectionEngine] Human.js loading failed with ${selectedBackend} backend.`);
|
|
761
|
+
// 尝试用备选后端加载(最多一次降级)
|
|
762
|
+
let fallbackBackend;
|
|
763
|
+
if (selectedBackend === 'wasm' && _isWebGLAvailable()) {
|
|
764
|
+
fallbackBackend = 'webgl';
|
|
765
|
+
}
|
|
766
|
+
else if (selectedBackend === 'webgl') {
|
|
767
|
+
fallbackBackend = 'wasm';
|
|
768
|
+
}
|
|
769
|
+
if (fallbackBackend) {
|
|
770
|
+
console.warn(`[FaceDetectionEngine] Primary backend (${selectedBackend}) failed, attempting fallback to ${fallbackBackend}...`);
|
|
771
|
+
const humanFallback = await _tryLoadHumanWithBackend(fallbackBackend, modelPath, wasmPath);
|
|
772
|
+
if (humanFallback) {
|
|
773
|
+
return humanFallback;
|
|
774
|
+
}
|
|
775
|
+
throw new Error(`Human.js loading failed: both ${selectedBackend} and ${fallbackBackend} backends failed`);
|
|
776
|
+
}
|
|
777
|
+
throw new Error(`Human.js loading failed: ${selectedBackend} backend failed (no fallback available)`);
|
|
778
|
+
}
|
|
684
779
|
/**
|
|
685
780
|
* Extract OpenCV version from getBuildInformation
|
|
686
781
|
* @returns version string like "4.12.0"
|
|
@@ -1719,28 +1814,59 @@ class FaceDetectionEngine extends SimpleEventEmitter {
|
|
|
1719
1814
|
}
|
|
1720
1815
|
catch (humanError) {
|
|
1721
1816
|
const errorMsg = humanError instanceof Error ? humanError.message : 'Unknown error';
|
|
1722
|
-
|
|
1723
|
-
|
|
1817
|
+
const stack = humanError instanceof Error ? humanError.stack : 'N/A';
|
|
1818
|
+
// 分析错误类型,提供针对性的建议
|
|
1819
|
+
let errorContext = {
|
|
1724
1820
|
error: errorMsg,
|
|
1725
|
-
stack
|
|
1821
|
+
stack,
|
|
1726
1822
|
userAgent: navigator.userAgent,
|
|
1727
1823
|
platform: navigator.platform,
|
|
1728
|
-
browser: this.detectBrowserInfo()
|
|
1729
|
-
|
|
1824
|
+
browser: this.detectBrowserInfo(),
|
|
1825
|
+
backend: this.config.tensorflow_backend,
|
|
1826
|
+
source: 'human.js'
|
|
1827
|
+
};
|
|
1828
|
+
// 特定错误类型的诊断
|
|
1829
|
+
if (errorMsg.includes('inputs')) {
|
|
1830
|
+
errorContext.diagnosis = 'Human.js internal error: Model structure incomplete';
|
|
1831
|
+
errorContext.rootCause = 'Human.js library issue - models not fully loaded or WASM backend initialization incomplete';
|
|
1832
|
+
errorContext.suggestion = 'This is a Human.js library issue. Models may not have proper executor or inputs structure. Check WASM initialization and model integrity.';
|
|
1833
|
+
}
|
|
1834
|
+
else if (errorMsg.includes('timeout')) {
|
|
1835
|
+
errorContext.diagnosis = 'Model loading timeout';
|
|
1836
|
+
errorContext.suggestion = 'Network issue or model file too large - check network conditions';
|
|
1837
|
+
}
|
|
1838
|
+
else if (errorMsg.includes('Critical models not loaded')) {
|
|
1839
|
+
errorContext.diagnosis = 'Human.js failed to load required models';
|
|
1840
|
+
errorContext.rootCause = 'Models (face, antispoof, liveness) are missing or incomplete';
|
|
1841
|
+
errorContext.suggestion = 'Check model files and ensure WASM backend is properly initialized';
|
|
1842
|
+
}
|
|
1843
|
+
else if (errorMsg.includes('empty')) {
|
|
1844
|
+
errorContext.diagnosis = 'Models object is empty after loading';
|
|
1845
|
+
errorContext.suggestion = 'Model path may be incorrect or HTTP response failed';
|
|
1846
|
+
}
|
|
1847
|
+
else if (errorMsg.includes('incomplete')) {
|
|
1848
|
+
errorContext.diagnosis = 'Models loaded but structure is incomplete';
|
|
1849
|
+
errorContext.rootCause = 'Human.js internal issue - missing executor, inputs, or modelUrl';
|
|
1850
|
+
errorContext.suggestion = 'Ensure all model resources are fully loaded and accessible';
|
|
1851
|
+
}
|
|
1852
|
+
console.error('[FaceDetectionEngine] Human.js loading failed with detailed error:', errorContext);
|
|
1853
|
+
this.emitDebug('initialization', 'Human.js loading failed with exception', errorContext, 'error');
|
|
1730
1854
|
this.emit('detector-loaded', {
|
|
1731
1855
|
success: false,
|
|
1732
|
-
error: `Failed to load Human.js: ${errorMsg}
|
|
1856
|
+
error: `Failed to load Human.js: ${errorMsg}`,
|
|
1857
|
+
details: errorContext
|
|
1733
1858
|
});
|
|
1734
1859
|
this.emit('detector-error', {
|
|
1735
1860
|
code: ErrorCode.DETECTOR_NOT_INITIALIZED,
|
|
1736
|
-
message: `Human.js loading error: ${errorMsg}
|
|
1861
|
+
message: `Human.js loading error: ${errorMsg}`,
|
|
1862
|
+
details: errorContext
|
|
1737
1863
|
});
|
|
1738
1864
|
return;
|
|
1739
1865
|
}
|
|
1740
1866
|
const humanLoadTime = performance.now() - humanStartTime;
|
|
1741
1867
|
if (!this.human) {
|
|
1742
1868
|
const errorMsg = 'Failed to load Human.js: instance is null';
|
|
1743
|
-
console.
|
|
1869
|
+
console.error('[FaceDetectionEngine] ' + errorMsg);
|
|
1744
1870
|
this.emitDebug('initialization', errorMsg, { loadTime: humanLoadTime }, 'error');
|
|
1745
1871
|
this.emit('detector-loaded', {
|
|
1746
1872
|
success: false,
|