@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.js
CHANGED
|
@@ -354,6 +354,43 @@
|
|
|
354
354
|
// 4. 其他浏览器 - 保守方案,使用 WASM
|
|
355
355
|
return 'other';
|
|
356
356
|
}
|
|
357
|
+
/**
|
|
358
|
+
* 检测环境信息(用于诊断)
|
|
359
|
+
*/
|
|
360
|
+
function _detectEnvironmentInfo() {
|
|
361
|
+
const isMobile = /android|iphone|ipad|ipod|opera mini|iemobile|wpdesktop/i.test(navigator.userAgent.toLowerCase());
|
|
362
|
+
const isAndroid = /android/i.test(navigator.userAgent);
|
|
363
|
+
const isIOS = /iphone|ipad|ipod/i.test(navigator.userAgent);
|
|
364
|
+
// 检测内存
|
|
365
|
+
let memory = { available: 'unknown' };
|
|
366
|
+
if (navigator.deviceMemory) {
|
|
367
|
+
memory = { available: `${navigator.deviceMemory}GB` };
|
|
368
|
+
}
|
|
369
|
+
// 检测连接
|
|
370
|
+
let connection = { type: 'unknown' };
|
|
371
|
+
if (navigator.connection) {
|
|
372
|
+
const conn = navigator.connection;
|
|
373
|
+
connection = {
|
|
374
|
+
type: conn.effectiveType,
|
|
375
|
+
downlink: `${conn.downlink}Mbps`,
|
|
376
|
+
rtt: `${conn.rtt}ms`,
|
|
377
|
+
saveData: conn.saveData
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
isMobile,
|
|
382
|
+
isAndroid,
|
|
383
|
+
isIOS,
|
|
384
|
+
memory,
|
|
385
|
+
connection,
|
|
386
|
+
userAgent: navigator.userAgent,
|
|
387
|
+
platform: navigator.platform,
|
|
388
|
+
language: navigator.language,
|
|
389
|
+
hardwareConcurrency: navigator.hardwareConcurrency,
|
|
390
|
+
maxTouchPoints: navigator.maxTouchPoints,
|
|
391
|
+
vendor: navigator.vendor
|
|
392
|
+
};
|
|
393
|
+
}
|
|
357
394
|
function _getOptimalBackendForEngine(engine) {
|
|
358
395
|
// 针对不同内核的优化策略
|
|
359
396
|
const backendConfig = {
|
|
@@ -627,14 +664,28 @@
|
|
|
627
664
|
catch (error) {
|
|
628
665
|
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
629
666
|
const errorStack = error instanceof Error ? error.stack : 'N/A';
|
|
667
|
+
const isCORSError = errorMsg.toLowerCase().includes('cors') ||
|
|
668
|
+
errorMsg.toLowerCase().includes('cross-origin') ||
|
|
669
|
+
errorMsg.toLowerCase().includes('blocked by cors policy');
|
|
670
|
+
const isNetworkError = errorMsg.toLowerCase().includes('network') ||
|
|
671
|
+
errorMsg.toLowerCase().includes('failed to fetch') ||
|
|
672
|
+
errorMsg.toLowerCase().includes('fetch failed');
|
|
673
|
+
const isWASMError = errorMsg.toLowerCase().includes('wasm') ||
|
|
674
|
+
errorMsg.toLowerCase().includes('webassembly');
|
|
630
675
|
console.error('[FaceDetectionEngine] Error during human.load():', {
|
|
631
676
|
errorMsg,
|
|
632
677
|
stack: errorStack,
|
|
633
678
|
backend: human.config?.backend,
|
|
634
679
|
hasModels: !!human.models,
|
|
635
|
-
modelsKeys: human.models ? Object.keys(human.models).length : 0
|
|
680
|
+
modelsKeys: human.models ? Object.keys(human.models).length : 0,
|
|
681
|
+
isCORSError,
|
|
682
|
+
isNetworkError,
|
|
683
|
+
isWASMError,
|
|
684
|
+
modelBasePath: human.config?.modelBasePath,
|
|
685
|
+
wasmPath: human.config?.wasmPath,
|
|
686
|
+
userAgent: navigator.userAgent
|
|
636
687
|
});
|
|
637
|
-
throw new Error(`Model loading error: ${errorMsg}`);
|
|
688
|
+
throw new Error(`Model loading error (${human.config?.backend} backend): ${errorMsg}`);
|
|
638
689
|
}
|
|
639
690
|
const loadTime = performance.now() - modelLoadStartTime;
|
|
640
691
|
console.log('[FaceDetectionEngine] Human.js loaded successfully', {
|
|
@@ -654,54 +705,14 @@
|
|
|
654
705
|
console.error('[FaceDetectionEngine] CRITICAL: human.models is empty after loading!');
|
|
655
706
|
throw new Error('No models were loaded - human.models is empty');
|
|
656
707
|
}
|
|
657
|
-
// 详细检查每个关键模型及其结构
|
|
658
|
-
const criticalModels = ['face', 'antispoof', 'liveness'];
|
|
659
|
-
const missingModels = [];
|
|
660
|
-
for (const modelName of criticalModels) {
|
|
661
|
-
const model = human.models[modelName];
|
|
662
|
-
if (!model) {
|
|
663
|
-
missingModels.push(modelName);
|
|
664
|
-
console.error(`[FaceDetectionEngine] CRITICAL: Model '${modelName}' is missing!`);
|
|
665
|
-
}
|
|
666
|
-
else {
|
|
667
|
-
const isLoaded = model.loaded || model.state === 'loaded' || !!model.model;
|
|
668
|
-
// 检查模型是否有必要的内部结构(防止 "Cannot read properties of undefined (reading 'inputs')" 错误)
|
|
669
|
-
const hasExecutor = !!model['executor'];
|
|
670
|
-
const hasInputs = !!model.inputs && Array.isArray(model.inputs) && model.inputs.length > 0;
|
|
671
|
-
const hasModelUrl = !!model['modelUrl'];
|
|
672
|
-
console.log(`[FaceDetectionEngine] Model '${modelName}':`, {
|
|
673
|
-
loaded: isLoaded,
|
|
674
|
-
state: model.state,
|
|
675
|
-
hasModel: !!model.model,
|
|
676
|
-
hasExecutor,
|
|
677
|
-
hasInputs,
|
|
678
|
-
hasModelUrl,
|
|
679
|
-
inputsType: typeof model.inputs,
|
|
680
|
-
inputsLength: Array.isArray(model.inputs) ? model.inputs.length : 'N/A'
|
|
681
|
-
});
|
|
682
|
-
// 严格检查:模型必须有以下结构才能正常工作
|
|
683
|
-
if (!isLoaded || !hasExecutor || !hasModelUrl) {
|
|
684
|
-
missingModels.push(`${modelName} (incomplete)`);
|
|
685
|
-
console.error(`[FaceDetectionEngine] WARNING: Model '${modelName}' may not be fully loaded - missing structure`);
|
|
686
|
-
}
|
|
687
|
-
// 如果 inputs 未定义会导致 "Cannot read properties of undefined (reading 'inputs')" 错误
|
|
688
|
-
if (!hasInputs && modelName !== 'antispoof') {
|
|
689
|
-
console.warn(`[FaceDetectionEngine] WARNING: Model '${modelName}' has no inputs - may cause errors during detection`);
|
|
690
|
-
missingModels.push(`${modelName} (no inputs)`);
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
if (missingModels.length > 0) {
|
|
695
|
-
console.error('[FaceDetectionEngine] Some critical models failed to load:', missingModels);
|
|
696
|
-
throw new Error(`Critical models not loaded: ${missingModels.join(', ')}`);
|
|
697
|
-
}
|
|
698
708
|
// 打印加载的模型信息
|
|
699
709
|
if (human.models) {
|
|
700
710
|
const loadedModels = Object.entries(human.models).map(([name, model]) => ({
|
|
701
711
|
name,
|
|
702
712
|
loaded: model?.loaded || model?.state === 'loaded',
|
|
703
713
|
type: typeof model,
|
|
704
|
-
hasModel: !!model?.model
|
|
714
|
+
hasModel: !!model?.model,
|
|
715
|
+
keys: Object.keys(model).length
|
|
705
716
|
}));
|
|
706
717
|
console.log('[FaceDetectionEngine] All loaded models:', {
|
|
707
718
|
backend: human.config?.backend,
|
|
@@ -733,7 +744,11 @@
|
|
|
733
744
|
errorMsg,
|
|
734
745
|
stack,
|
|
735
746
|
backend: config.backend,
|
|
736
|
-
userAgent: navigator.userAgent
|
|
747
|
+
userAgent: navigator.userAgent,
|
|
748
|
+
isCORSError: errorMsg.toLowerCase().includes('cors'),
|
|
749
|
+
isNetworkError: errorMsg.toLowerCase().includes('network'),
|
|
750
|
+
isWASMError: errorMsg.toLowerCase().includes('wasm'),
|
|
751
|
+
environmentInfo: _detectEnvironmentInfo()
|
|
737
752
|
});
|
|
738
753
|
return null;
|
|
739
754
|
}
|
|
@@ -742,11 +757,8 @@
|
|
|
742
757
|
console.error(`[FaceDetectionEngine] Human instance is null (${backend})`);
|
|
743
758
|
return null;
|
|
744
759
|
}
|
|
745
|
-
// 验证 Human 实例结构(早期检测 WASM 问题)
|
|
746
|
-
if (!human.config) {
|
|
747
|
-
console.warn('[FaceDetectionEngine] Warning: human.config is missing');
|
|
748
|
-
}
|
|
749
760
|
try {
|
|
761
|
+
console.log(`[FaceDetectionEngine] Starting model loading for ${backend} backend...`);
|
|
750
762
|
await _loadAndVerifyHuman(human);
|
|
751
763
|
const totalTime = performance.now() - initStartTime;
|
|
752
764
|
console.log(`[FaceDetectionEngine] Successfully loaded Human.js with ${backend} backend in ${totalTime.toFixed(2)}ms`);
|
|
@@ -754,7 +766,19 @@
|
|
|
754
766
|
}
|
|
755
767
|
catch (error) {
|
|
756
768
|
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
757
|
-
|
|
769
|
+
const stack = error instanceof Error ? error.stack : 'N/A';
|
|
770
|
+
console.error(`[FaceDetectionEngine] Failed to load models with ${backend} backend:`, {
|
|
771
|
+
errorMsg,
|
|
772
|
+
stack,
|
|
773
|
+
backend,
|
|
774
|
+
modelPath,
|
|
775
|
+
wasmPath,
|
|
776
|
+
isCORSError: errorMsg.toLowerCase().includes('cors') || errorMsg.toLowerCase().includes('cross-origin'),
|
|
777
|
+
isNetworkError: errorMsg.toLowerCase().includes('network') || errorMsg.toLowerCase().includes('failed to fetch'),
|
|
778
|
+
isWASMError: errorMsg.toLowerCase().includes('wasm'),
|
|
779
|
+
duration: `${(performance.now() - initStartTime).toFixed(2)}ms`,
|
|
780
|
+
environmentInfo: _detectEnvironmentInfo()
|
|
781
|
+
});
|
|
758
782
|
return null;
|
|
759
783
|
}
|
|
760
784
|
}
|
|
@@ -767,14 +791,15 @@
|
|
|
767
791
|
*/
|
|
768
792
|
async function loadHuman(modelPath, wasmPath, preferredBackend) {
|
|
769
793
|
const selectedBackend = _detectOptimalBackend(preferredBackend);
|
|
794
|
+
const environmentInfo = _detectEnvironmentInfo();
|
|
770
795
|
console.log('[FaceDetectionEngine] Starting Human.js initialization:', {
|
|
771
796
|
selectedBackend,
|
|
772
797
|
modelBasePath: modelPath || '(using default)',
|
|
773
798
|
wasmPath: wasmPath || '(using default)',
|
|
774
|
-
|
|
775
|
-
platform: navigator.platform
|
|
799
|
+
...environmentInfo
|
|
776
800
|
});
|
|
777
801
|
// 尝试用主后端加载
|
|
802
|
+
console.log(`[FaceDetectionEngine] Attempting to load Human.js with ${selectedBackend} backend...`);
|
|
778
803
|
const human = await _tryLoadHumanWithBackend(selectedBackend, modelPath, wasmPath);
|
|
779
804
|
if (human) {
|
|
780
805
|
return human;
|
|
@@ -792,11 +817,38 @@
|
|
|
792
817
|
console.warn(`[FaceDetectionEngine] Primary backend (${selectedBackend}) failed, attempting fallback to ${fallbackBackend}...`);
|
|
793
818
|
const humanFallback = await _tryLoadHumanWithBackend(fallbackBackend, modelPath, wasmPath);
|
|
794
819
|
if (humanFallback) {
|
|
820
|
+
console.log(`[FaceDetectionEngine] Successfully loaded with fallback backend: ${fallbackBackend}`);
|
|
795
821
|
return humanFallback;
|
|
796
822
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
823
|
+
const errorDetails = {
|
|
824
|
+
message: `Human.js loading failed: both ${selectedBackend} and ${fallbackBackend} backends failed`,
|
|
825
|
+
backends: {
|
|
826
|
+
primary: selectedBackend,
|
|
827
|
+
fallback: fallbackBackend,
|
|
828
|
+
both_failed: true
|
|
829
|
+
},
|
|
830
|
+
environment: environmentInfo,
|
|
831
|
+
paths: {
|
|
832
|
+
modelBasePath: modelPath || '(using default)',
|
|
833
|
+
wasmPath: wasmPath || '(using default)'
|
|
834
|
+
},
|
|
835
|
+
webglAvailable: _isWebGLAvailable()
|
|
836
|
+
};
|
|
837
|
+
console.error('[FaceDetectionEngine] CRITICAL ERROR:', errorDetails);
|
|
838
|
+
throw new Error(errorDetails.message);
|
|
839
|
+
}
|
|
840
|
+
const errorDetails = {
|
|
841
|
+
message: `Human.js loading failed: ${selectedBackend} backend failed (no fallback available)`,
|
|
842
|
+
backend: selectedBackend,
|
|
843
|
+
environment: environmentInfo,
|
|
844
|
+
paths: {
|
|
845
|
+
modelBasePath: modelPath || '(using default)',
|
|
846
|
+
wasmPath: wasmPath || '(using default)'
|
|
847
|
+
},
|
|
848
|
+
webglAvailable: _isWebGLAvailable()
|
|
849
|
+
};
|
|
850
|
+
console.error('[FaceDetectionEngine] CRITICAL ERROR:', errorDetails);
|
|
851
|
+
throw new Error(errorDetails.message);
|
|
800
852
|
}
|
|
801
853
|
/**
|
|
802
854
|
* Extract OpenCV version from getBuildInformation
|