@sssxyd/face-liveness-detector 0.2.33 → 0.2.35
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 +107 -55
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +107 -55
- package/dist/index.js.map +1 -1
- package/dist/types/library-loader.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -332,6 +332,43 @@ function _detectBrowserEngine(userAgent) {
|
|
|
332
332
|
// 4. 其他浏览器 - 保守方案,使用 WASM
|
|
333
333
|
return 'other';
|
|
334
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* 检测环境信息(用于诊断)
|
|
337
|
+
*/
|
|
338
|
+
function _detectEnvironmentInfo() {
|
|
339
|
+
const isMobile = /android|iphone|ipad|ipod|opera mini|iemobile|wpdesktop/i.test(navigator.userAgent.toLowerCase());
|
|
340
|
+
const isAndroid = /android/i.test(navigator.userAgent);
|
|
341
|
+
const isIOS = /iphone|ipad|ipod/i.test(navigator.userAgent);
|
|
342
|
+
// 检测内存
|
|
343
|
+
let memory = { available: 'unknown' };
|
|
344
|
+
if (navigator.deviceMemory) {
|
|
345
|
+
memory = { available: `${navigator.deviceMemory}GB` };
|
|
346
|
+
}
|
|
347
|
+
// 检测连接
|
|
348
|
+
let connection = { type: 'unknown' };
|
|
349
|
+
if (navigator.connection) {
|
|
350
|
+
const conn = navigator.connection;
|
|
351
|
+
connection = {
|
|
352
|
+
type: conn.effectiveType,
|
|
353
|
+
downlink: `${conn.downlink}Mbps`,
|
|
354
|
+
rtt: `${conn.rtt}ms`,
|
|
355
|
+
saveData: conn.saveData
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
return {
|
|
359
|
+
isMobile,
|
|
360
|
+
isAndroid,
|
|
361
|
+
isIOS,
|
|
362
|
+
memory,
|
|
363
|
+
connection,
|
|
364
|
+
userAgent: navigator.userAgent,
|
|
365
|
+
platform: navigator.platform,
|
|
366
|
+
language: navigator.language,
|
|
367
|
+
hardwareConcurrency: navigator.hardwareConcurrency,
|
|
368
|
+
maxTouchPoints: navigator.maxTouchPoints,
|
|
369
|
+
vendor: navigator.vendor
|
|
370
|
+
};
|
|
371
|
+
}
|
|
335
372
|
function _getOptimalBackendForEngine(engine) {
|
|
336
373
|
// 针对不同内核的优化策略
|
|
337
374
|
const backendConfig = {
|
|
@@ -605,14 +642,28 @@ async function _loadAndVerifyHuman(human) {
|
|
|
605
642
|
catch (error) {
|
|
606
643
|
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
607
644
|
const errorStack = error instanceof Error ? error.stack : 'N/A';
|
|
645
|
+
const isCORSError = errorMsg.toLowerCase().includes('cors') ||
|
|
646
|
+
errorMsg.toLowerCase().includes('cross-origin') ||
|
|
647
|
+
errorMsg.toLowerCase().includes('blocked by cors policy');
|
|
648
|
+
const isNetworkError = errorMsg.toLowerCase().includes('network') ||
|
|
649
|
+
errorMsg.toLowerCase().includes('failed to fetch') ||
|
|
650
|
+
errorMsg.toLowerCase().includes('fetch failed');
|
|
651
|
+
const isWASMError = errorMsg.toLowerCase().includes('wasm') ||
|
|
652
|
+
errorMsg.toLowerCase().includes('webassembly');
|
|
608
653
|
console.error('[FaceDetectionEngine] Error during human.load():', {
|
|
609
654
|
errorMsg,
|
|
610
655
|
stack: errorStack,
|
|
611
656
|
backend: human.config?.backend,
|
|
612
657
|
hasModels: !!human.models,
|
|
613
|
-
modelsKeys: human.models ? Object.keys(human.models).length : 0
|
|
658
|
+
modelsKeys: human.models ? Object.keys(human.models).length : 0,
|
|
659
|
+
isCORSError,
|
|
660
|
+
isNetworkError,
|
|
661
|
+
isWASMError,
|
|
662
|
+
modelBasePath: human.config?.modelBasePath,
|
|
663
|
+
wasmPath: human.config?.wasmPath,
|
|
664
|
+
userAgent: navigator.userAgent
|
|
614
665
|
});
|
|
615
|
-
throw new Error(`Model loading error: ${errorMsg}`);
|
|
666
|
+
throw new Error(`Model loading error (${human.config?.backend} backend): ${errorMsg}`);
|
|
616
667
|
}
|
|
617
668
|
const loadTime = performance.now() - modelLoadStartTime;
|
|
618
669
|
console.log('[FaceDetectionEngine] Human.js loaded successfully', {
|
|
@@ -632,54 +683,14 @@ async function _loadAndVerifyHuman(human) {
|
|
|
632
683
|
console.error('[FaceDetectionEngine] CRITICAL: human.models is empty after loading!');
|
|
633
684
|
throw new Error('No models were loaded - human.models is empty');
|
|
634
685
|
}
|
|
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
686
|
// 打印加载的模型信息
|
|
677
687
|
if (human.models) {
|
|
678
688
|
const loadedModels = Object.entries(human.models).map(([name, model]) => ({
|
|
679
689
|
name,
|
|
680
690
|
loaded: model?.loaded || model?.state === 'loaded',
|
|
681
691
|
type: typeof model,
|
|
682
|
-
hasModel: !!model?.model
|
|
692
|
+
hasModel: !!model?.model,
|
|
693
|
+
keys: Object.keys(model).length
|
|
683
694
|
}));
|
|
684
695
|
console.log('[FaceDetectionEngine] All loaded models:', {
|
|
685
696
|
backend: human.config?.backend,
|
|
@@ -711,7 +722,11 @@ async function _tryLoadHumanWithBackend(backend, modelPath, wasmPath) {
|
|
|
711
722
|
errorMsg,
|
|
712
723
|
stack,
|
|
713
724
|
backend: config.backend,
|
|
714
|
-
userAgent: navigator.userAgent
|
|
725
|
+
userAgent: navigator.userAgent,
|
|
726
|
+
isCORSError: errorMsg.toLowerCase().includes('cors'),
|
|
727
|
+
isNetworkError: errorMsg.toLowerCase().includes('network'),
|
|
728
|
+
isWASMError: errorMsg.toLowerCase().includes('wasm'),
|
|
729
|
+
environmentInfo: _detectEnvironmentInfo()
|
|
715
730
|
});
|
|
716
731
|
return null;
|
|
717
732
|
}
|
|
@@ -720,11 +735,8 @@ async function _tryLoadHumanWithBackend(backend, modelPath, wasmPath) {
|
|
|
720
735
|
console.error(`[FaceDetectionEngine] Human instance is null (${backend})`);
|
|
721
736
|
return null;
|
|
722
737
|
}
|
|
723
|
-
// 验证 Human 实例结构(早期检测 WASM 问题)
|
|
724
|
-
if (!human.config) {
|
|
725
|
-
console.warn('[FaceDetectionEngine] Warning: human.config is missing');
|
|
726
|
-
}
|
|
727
738
|
try {
|
|
739
|
+
console.log(`[FaceDetectionEngine] Starting model loading for ${backend} backend...`);
|
|
728
740
|
await _loadAndVerifyHuman(human);
|
|
729
741
|
const totalTime = performance.now() - initStartTime;
|
|
730
742
|
console.log(`[FaceDetectionEngine] Successfully loaded Human.js with ${backend} backend in ${totalTime.toFixed(2)}ms`);
|
|
@@ -732,7 +744,19 @@ async function _tryLoadHumanWithBackend(backend, modelPath, wasmPath) {
|
|
|
732
744
|
}
|
|
733
745
|
catch (error) {
|
|
734
746
|
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
735
|
-
|
|
747
|
+
const stack = error instanceof Error ? error.stack : 'N/A';
|
|
748
|
+
console.error(`[FaceDetectionEngine] Failed to load models with ${backend} backend:`, {
|
|
749
|
+
errorMsg,
|
|
750
|
+
stack,
|
|
751
|
+
backend,
|
|
752
|
+
modelPath,
|
|
753
|
+
wasmPath,
|
|
754
|
+
isCORSError: errorMsg.toLowerCase().includes('cors') || errorMsg.toLowerCase().includes('cross-origin'),
|
|
755
|
+
isNetworkError: errorMsg.toLowerCase().includes('network') || errorMsg.toLowerCase().includes('failed to fetch'),
|
|
756
|
+
isWASMError: errorMsg.toLowerCase().includes('wasm'),
|
|
757
|
+
duration: `${(performance.now() - initStartTime).toFixed(2)}ms`,
|
|
758
|
+
environmentInfo: _detectEnvironmentInfo()
|
|
759
|
+
});
|
|
736
760
|
return null;
|
|
737
761
|
}
|
|
738
762
|
}
|
|
@@ -745,14 +769,15 @@ async function _tryLoadHumanWithBackend(backend, modelPath, wasmPath) {
|
|
|
745
769
|
*/
|
|
746
770
|
async function loadHuman(modelPath, wasmPath, preferredBackend) {
|
|
747
771
|
const selectedBackend = _detectOptimalBackend(preferredBackend);
|
|
772
|
+
const environmentInfo = _detectEnvironmentInfo();
|
|
748
773
|
console.log('[FaceDetectionEngine] Starting Human.js initialization:', {
|
|
749
774
|
selectedBackend,
|
|
750
775
|
modelBasePath: modelPath || '(using default)',
|
|
751
776
|
wasmPath: wasmPath || '(using default)',
|
|
752
|
-
|
|
753
|
-
platform: navigator.platform
|
|
777
|
+
...environmentInfo
|
|
754
778
|
});
|
|
755
779
|
// 尝试用主后端加载
|
|
780
|
+
console.log(`[FaceDetectionEngine] Attempting to load Human.js with ${selectedBackend} backend...`);
|
|
756
781
|
const human = await _tryLoadHumanWithBackend(selectedBackend, modelPath, wasmPath);
|
|
757
782
|
if (human) {
|
|
758
783
|
return human;
|
|
@@ -770,11 +795,38 @@ async function loadHuman(modelPath, wasmPath, preferredBackend) {
|
|
|
770
795
|
console.warn(`[FaceDetectionEngine] Primary backend (${selectedBackend}) failed, attempting fallback to ${fallbackBackend}...`);
|
|
771
796
|
const humanFallback = await _tryLoadHumanWithBackend(fallbackBackend, modelPath, wasmPath);
|
|
772
797
|
if (humanFallback) {
|
|
798
|
+
console.log(`[FaceDetectionEngine] Successfully loaded with fallback backend: ${fallbackBackend}`);
|
|
773
799
|
return humanFallback;
|
|
774
800
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
801
|
+
const errorDetails = {
|
|
802
|
+
message: `Human.js loading failed: both ${selectedBackend} and ${fallbackBackend} backends failed`,
|
|
803
|
+
backends: {
|
|
804
|
+
primary: selectedBackend,
|
|
805
|
+
fallback: fallbackBackend,
|
|
806
|
+
both_failed: true
|
|
807
|
+
},
|
|
808
|
+
environment: environmentInfo,
|
|
809
|
+
paths: {
|
|
810
|
+
modelBasePath: modelPath || '(using default)',
|
|
811
|
+
wasmPath: wasmPath || '(using default)'
|
|
812
|
+
},
|
|
813
|
+
webglAvailable: _isWebGLAvailable()
|
|
814
|
+
};
|
|
815
|
+
console.error('[FaceDetectionEngine] CRITICAL ERROR:', errorDetails);
|
|
816
|
+
throw new Error(errorDetails.message);
|
|
817
|
+
}
|
|
818
|
+
const errorDetails = {
|
|
819
|
+
message: `Human.js loading failed: ${selectedBackend} backend failed (no fallback available)`,
|
|
820
|
+
backend: selectedBackend,
|
|
821
|
+
environment: environmentInfo,
|
|
822
|
+
paths: {
|
|
823
|
+
modelBasePath: modelPath || '(using default)',
|
|
824
|
+
wasmPath: wasmPath || '(using default)'
|
|
825
|
+
},
|
|
826
|
+
webglAvailable: _isWebGLAvailable()
|
|
827
|
+
};
|
|
828
|
+
console.error('[FaceDetectionEngine] CRITICAL ERROR:', errorDetails);
|
|
829
|
+
throw new Error(errorDetails.message);
|
|
778
830
|
}
|
|
779
831
|
/**
|
|
780
832
|
* Extract OpenCV version from getBuildInformation
|