@sssxyd/face-liveness-detector 0.2.22 → 0.2.24
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 +109 -77
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +109 -76
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/library-loader.d.ts +10 -3
- package/dist/types/library-loader.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -324,6 +324,11 @@ function _detectOptimalBackend() {
|
|
|
324
324
|
* @param timeout - Maximum wait time in milliseconds (default: 30000)
|
|
325
325
|
*/
|
|
326
326
|
async function preloadOpenCV(timeout = 30000) {
|
|
327
|
+
// 如果已经初始化完成,直接返回
|
|
328
|
+
if (opencvInitialized) {
|
|
329
|
+
console.log('[OpenCV] Already initialized, skipping preload');
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
327
332
|
// 如果已经在初始化中,返回现有的 Promise
|
|
328
333
|
if (opencvInitPromise) {
|
|
329
334
|
console.log('[OpenCV] Already initializing, reusing existing promise');
|
|
@@ -334,16 +339,16 @@ async function preloadOpenCV(timeout = 30000) {
|
|
|
334
339
|
opencvInitPromise = _initializeOpenCV(timeout);
|
|
335
340
|
try {
|
|
336
341
|
const initTime = await opencvInitPromise;
|
|
337
|
-
console.log('[OpenCV] Preload completed successfully
|
|
342
|
+
console.log('[OpenCV] Preload completed successfully ', {
|
|
343
|
+
initTime: `${initTime.toFixed(2)}ms`,
|
|
344
|
+
version: getOpenCVVersion()
|
|
345
|
+
});
|
|
338
346
|
}
|
|
339
347
|
catch (error) {
|
|
340
348
|
console.error('[OpenCV] Preload failed:', error);
|
|
349
|
+
opencvInitPromise = null; // 失败时清除 Promise,允许重试
|
|
341
350
|
throw error;
|
|
342
351
|
}
|
|
343
|
-
finally {
|
|
344
|
-
// 完成后清除 Promise 标记,但保留已初始化的 cv 对象
|
|
345
|
-
opencvInitPromise = null;
|
|
346
|
-
}
|
|
347
352
|
}
|
|
348
353
|
/**
|
|
349
354
|
* Internal helper to initialize OpenCV
|
|
@@ -351,16 +356,16 @@ async function preloadOpenCV(timeout = 30000) {
|
|
|
351
356
|
*/
|
|
352
357
|
async function _initializeOpenCV(timeout) {
|
|
353
358
|
const initStartTime = performance.now();
|
|
354
|
-
console.log('
|
|
359
|
+
console.log('Waiting for OpenCV WASM initialization...');
|
|
355
360
|
// 快速路径:检查是否已经初始化
|
|
356
361
|
if (cvModule.Mat) {
|
|
357
362
|
const initTime = performance.now() - initStartTime;
|
|
358
|
-
console.log(`
|
|
363
|
+
console.log(`OpenCV.js already initialized, took ${initTime.toFixed(2)}ms`);
|
|
359
364
|
return cvModule;
|
|
360
365
|
}
|
|
361
366
|
if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
|
|
362
367
|
const initTime = performance.now() - initStartTime;
|
|
363
|
-
console.log(`
|
|
368
|
+
console.log(`OpenCV.js already initialized (from global), took ${initTime.toFixed(2)}ms`);
|
|
364
369
|
cvModule = globalThis.cv;
|
|
365
370
|
return cvModule;
|
|
366
371
|
}
|
|
@@ -368,10 +373,10 @@ async function _initializeOpenCV(timeout) {
|
|
|
368
373
|
if (typeof globalThis !== 'undefined' && !globalThis.cv) {
|
|
369
374
|
if (cvModule && Object.isExtensible(cvModule)) {
|
|
370
375
|
globalThis.cv = cvModule;
|
|
371
|
-
console.log('
|
|
376
|
+
console.log('cvModule assigned to globalThis.cv');
|
|
372
377
|
}
|
|
373
378
|
else {
|
|
374
|
-
console.log('
|
|
379
|
+
console.log('cvModule is not extensible or globalThis already has cv');
|
|
375
380
|
}
|
|
376
381
|
}
|
|
377
382
|
return new Promise((resolve, reject) => {
|
|
@@ -380,7 +385,7 @@ async function _initializeOpenCV(timeout) {
|
|
|
380
385
|
// 防止多次调用 resolve/reject
|
|
381
386
|
let finished = false;
|
|
382
387
|
const timeoutId = setTimeout(() => {
|
|
383
|
-
console.error('
|
|
388
|
+
console.error('OpenCV.js initialization timeout after ' + timeout + 'ms');
|
|
384
389
|
finished = true;
|
|
385
390
|
if (pollInterval) {
|
|
386
391
|
clearInterval(pollInterval);
|
|
@@ -389,65 +394,63 @@ async function _initializeOpenCV(timeout) {
|
|
|
389
394
|
}, timeout);
|
|
390
395
|
const resolveOnce = (source) => {
|
|
391
396
|
if (finished) {
|
|
392
|
-
console.log('[
|
|
397
|
+
console.log('[resolveOnce] Already finished, ignoring call from:', source);
|
|
393
398
|
return;
|
|
394
399
|
}
|
|
395
400
|
finished = true;
|
|
396
|
-
console.log('[
|
|
401
|
+
console.log('[resolveOnce] Marking as finished');
|
|
397
402
|
// 立即停止所有定时器和轮询
|
|
398
403
|
clearTimeout(timeoutId);
|
|
399
404
|
if (pollInterval !== null) {
|
|
400
405
|
clearInterval(pollInterval);
|
|
401
406
|
pollInterval = null;
|
|
402
|
-
console.log('[
|
|
407
|
+
console.log('[resolveOnce] Poll interval cleared');
|
|
403
408
|
}
|
|
404
409
|
const initTime = performance.now() - initStartTime;
|
|
405
|
-
console.log(`
|
|
410
|
+
console.log(`OpenCV.js initialized (${source}), took ${initTime.toFixed(2)}ms`);
|
|
406
411
|
// 标记初始化完成
|
|
407
412
|
opencvInitialized = true;
|
|
408
413
|
// 返回简单的标记,实际的 cv 对象已经在 globalThis.cv 上了
|
|
409
|
-
// 通过 getCvSync() 获取
|
|
410
414
|
resolve(initTime);
|
|
411
|
-
console.log('[FaceDetectionEngine] [resolveOnce] Promise resolved with true');
|
|
412
415
|
};
|
|
413
416
|
// 尝试设置回调(只有在 cvModule 可扩展时才尝试)
|
|
414
417
|
const canSetCallback = cvModule && Object.isExtensible(cvModule);
|
|
415
418
|
if (canSetCallback) {
|
|
416
419
|
try {
|
|
417
420
|
const originalOnRuntimeInitialized = cvModule.onRuntimeInitialized(cvModule).onRuntimeInitialized = () => {
|
|
418
|
-
console.log('[
|
|
421
|
+
console.log('[onRuntimeInitialized] callback triggered');
|
|
419
422
|
// 调用原始回调(如果存在)
|
|
420
423
|
if (originalOnRuntimeInitialized && typeof originalOnRuntimeInitialized === 'function') {
|
|
421
424
|
try {
|
|
422
425
|
originalOnRuntimeInitialized();
|
|
423
426
|
}
|
|
424
427
|
catch (e) {
|
|
425
|
-
console.warn('[
|
|
428
|
+
console.warn('[onRuntimeInitialized] callback failed:', e);
|
|
426
429
|
}
|
|
427
430
|
}
|
|
428
431
|
resolveOnce('callback');
|
|
429
432
|
};
|
|
430
|
-
console.log('[
|
|
433
|
+
console.log('[onRuntimeInitialized] callback set successfully');
|
|
431
434
|
}
|
|
432
435
|
catch (e) {
|
|
433
|
-
console.warn('[
|
|
436
|
+
console.warn('[onRuntimeInitialized] Failed to set callback, will use polling:', e);
|
|
434
437
|
}
|
|
435
438
|
}
|
|
436
439
|
else {
|
|
437
|
-
console.log('[
|
|
440
|
+
console.log('[polling] cvModule is not extensible, using polling mode');
|
|
438
441
|
}
|
|
439
442
|
// 启动轮询作为备用方案或主要方案
|
|
440
443
|
pollInterval = setInterval(() => {
|
|
441
|
-
console.log('[
|
|
444
|
+
console.log('[polling] Checking for Mat class...');
|
|
442
445
|
// 优先检查 cvModule 中是否有 Mat
|
|
443
446
|
if (cvModule.Mat) {
|
|
444
|
-
console.log('[
|
|
447
|
+
console.log('[polling] Found Mat in cvModule');
|
|
445
448
|
resolveOnce('cvModule polling');
|
|
446
449
|
return;
|
|
447
450
|
}
|
|
448
451
|
// 其次检查 globalThis.cv 中是否有 Mat
|
|
449
452
|
if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
|
|
450
|
-
console.log('[
|
|
453
|
+
console.log('[polling] Found Mat in globalThis.cv');
|
|
451
454
|
cvModule = globalThis.cv;
|
|
452
455
|
resolveOnce('globalThis.cv polling');
|
|
453
456
|
return;
|
|
@@ -461,39 +464,38 @@ async function _initializeOpenCV(timeout) {
|
|
|
461
464
|
* @returns Promise that resolves with cv module
|
|
462
465
|
*/
|
|
463
466
|
async function loadOpenCV(timeout = 30000) {
|
|
464
|
-
console.log('[FaceDetectionEngine] loadOpenCV: START');
|
|
465
467
|
try {
|
|
466
468
|
// 快速检查是否已经初始化完成
|
|
467
469
|
if (opencvInitialized) {
|
|
468
|
-
console.log('[
|
|
470
|
+
console.log('[loadOpenCV] Already initialized, returning immediately');
|
|
469
471
|
const cv = getCvSync();
|
|
470
472
|
if (cv) {
|
|
471
473
|
return { cv };
|
|
472
474
|
}
|
|
473
475
|
}
|
|
474
476
|
// 等待初始化
|
|
475
|
-
console.log('[
|
|
477
|
+
console.log('[loadOpenCV] Waiting for initialization...');
|
|
476
478
|
if (!opencvInitPromise) {
|
|
477
|
-
console.log('[
|
|
479
|
+
console.log('[loadOpenCV] Starting new initialization');
|
|
478
480
|
opencvInitPromise = _initializeOpenCV(timeout);
|
|
479
481
|
}
|
|
480
|
-
console.log('[
|
|
482
|
+
console.log('[loadOpenCV] Awaiting opencvInitPromise');
|
|
481
483
|
const initTime = await opencvInitPromise;
|
|
482
|
-
console.log('[FaceDetectionEngine] loadOpenCV: After await, Promise resolved, cost :', initTime.toFixed(0), 'ms');
|
|
483
|
-
// 清除 Promise 缓存
|
|
484
|
-
opencvInitPromise = null;
|
|
485
484
|
// 获取初始化后的 cv 对象
|
|
486
485
|
const cv = getCvSync();
|
|
487
486
|
if (!cv) {
|
|
488
|
-
console.error('[
|
|
487
|
+
console.error('[loadOpenCV] getCvSync returned null');
|
|
489
488
|
throw new Error('OpenCV module is invalid');
|
|
490
489
|
}
|
|
491
|
-
console.log('[
|
|
492
|
-
|
|
490
|
+
console.log('[loadOpenCV] OpenCV.js load successfully', {
|
|
491
|
+
initTime: `${initTime.toFixed(2)}ms`,
|
|
492
|
+
version: getOpenCVVersion()
|
|
493
|
+
});
|
|
494
|
+
return cv;
|
|
493
495
|
}
|
|
494
496
|
catch (error) {
|
|
495
|
-
console.error('[
|
|
496
|
-
opencvInitPromise = null;
|
|
497
|
+
console.error('[loadOpenCV] OpenCV.js load failed', error);
|
|
498
|
+
opencvInitPromise = null; // 失败时清除 Promise,允许重试
|
|
497
499
|
throw error;
|
|
498
500
|
}
|
|
499
501
|
}
|
|
@@ -512,6 +514,35 @@ function getCvSync() {
|
|
|
512
514
|
}
|
|
513
515
|
return null;
|
|
514
516
|
}
|
|
517
|
+
/**
|
|
518
|
+
* Extract OpenCV version from getBuildInformation
|
|
519
|
+
* @returns version string like "4.12.0"
|
|
520
|
+
*/
|
|
521
|
+
function getOpenCVVersion() {
|
|
522
|
+
try {
|
|
523
|
+
const cv = getCvSync();
|
|
524
|
+
if (!cv || !cv.getBuildInformation) {
|
|
525
|
+
return 'unknown';
|
|
526
|
+
}
|
|
527
|
+
const buildInfo = cv.getBuildInformation();
|
|
528
|
+
// 查找 "Version control:" 或 "OpenCV" 开头的行
|
|
529
|
+
// 格式: "Version control: 4.12.0"
|
|
530
|
+
const versionMatch = buildInfo.match(/Version\s+control:\s+(\d+\.\d+\.\d+)/i);
|
|
531
|
+
if (versionMatch && versionMatch[1]) {
|
|
532
|
+
return versionMatch[1];
|
|
533
|
+
}
|
|
534
|
+
// 备用方案:查找 "OpenCV X.X.X" 格式
|
|
535
|
+
const opencvMatch = buildInfo.match(/OpenCV\s+(\d+\.\d+\.\d+)/i);
|
|
536
|
+
if (opencvMatch && opencvMatch[1]) {
|
|
537
|
+
return opencvMatch[1];
|
|
538
|
+
}
|
|
539
|
+
return 'unknown';
|
|
540
|
+
}
|
|
541
|
+
catch (error) {
|
|
542
|
+
console.error('[getOpenCVVersion] Failed to get version:', error);
|
|
543
|
+
return 'unknown';
|
|
544
|
+
}
|
|
545
|
+
}
|
|
515
546
|
/**
|
|
516
547
|
* Load Human.js
|
|
517
548
|
* @param modelPath - Path to model files (optional)
|
|
@@ -519,6 +550,7 @@ function getCvSync() {
|
|
|
519
550
|
* @returns Promise that resolves with Human instance
|
|
520
551
|
*/
|
|
521
552
|
async function loadHuman(modelPath, wasmPath) {
|
|
553
|
+
const initStartTime = performance.now();
|
|
522
554
|
const config = {
|
|
523
555
|
backend: _detectOptimalBackend(),
|
|
524
556
|
face: {
|
|
@@ -541,24 +573,16 @@ async function loadHuman(modelPath, wasmPath) {
|
|
|
541
573
|
if (wasmPath) {
|
|
542
574
|
config.wasmPath = wasmPath;
|
|
543
575
|
}
|
|
544
|
-
console.log('[
|
|
576
|
+
console.log('[loadHuman] Config:', {
|
|
545
577
|
backend: config.backend,
|
|
546
578
|
modelBasePath: config.modelBasePath || '(using default)',
|
|
547
579
|
wasmPath: config.wasmPath || '(using default)'
|
|
548
580
|
});
|
|
549
|
-
const initStartTime = performance.now();
|
|
550
|
-
console.log('[FaceDetectionEngine] Creating Human instance...');
|
|
551
581
|
const human = new Human(config);
|
|
552
|
-
const instanceCreateTime = performance.now() - initStartTime;
|
|
553
|
-
console.log(`[FaceDetectionEngine] Human instance created, took ${instanceCreateTime.toFixed(2)}ms`);
|
|
554
|
-
console.log('[FaceDetectionEngine] Loading Human.js models...');
|
|
555
|
-
const modelLoadStartTime = performance.now();
|
|
556
582
|
try {
|
|
557
583
|
await human.load();
|
|
558
|
-
const loadTime = performance.now() - modelLoadStartTime;
|
|
559
584
|
const totalTime = performance.now() - initStartTime;
|
|
560
|
-
console.log('[
|
|
561
|
-
modelLoadTime: `${loadTime.toFixed(2)}ms`,
|
|
585
|
+
console.log('[loadHuman] Loaded successfully', {
|
|
562
586
|
totalInitTime: `${totalTime.toFixed(2)}ms`,
|
|
563
587
|
version: human.version
|
|
564
588
|
});
|
|
@@ -566,7 +590,32 @@ async function loadHuman(modelPath, wasmPath) {
|
|
|
566
590
|
}
|
|
567
591
|
catch (error) {
|
|
568
592
|
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
569
|
-
console.error('[
|
|
593
|
+
console.error('[loadHuman] Load failed:', errorMsg);
|
|
594
|
+
throw error;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
async function loadLibraries(modelPath, wasmPath, timeout) {
|
|
598
|
+
console.log('[loadLibraries] Starting parallel load of OpenCV and Human...');
|
|
599
|
+
const startTime = performance.now();
|
|
600
|
+
if (timeout == undefined) {
|
|
601
|
+
timeout = 30000;
|
|
602
|
+
}
|
|
603
|
+
try {
|
|
604
|
+
// 并行加载 OpenCV 和 Human
|
|
605
|
+
const [cv, human] = await Promise.all([
|
|
606
|
+
loadOpenCV(timeout),
|
|
607
|
+
loadHuman(modelPath, wasmPath)
|
|
608
|
+
]);
|
|
609
|
+
const totalTime = performance.now() - startTime;
|
|
610
|
+
console.log('[loadLibraries] Both libraries loaded successfully in', totalTime.toFixed(2), 'ms');
|
|
611
|
+
return {
|
|
612
|
+
cv,
|
|
613
|
+
human
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
catch (error) {
|
|
617
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
618
|
+
console.error('[loadLibraries] Failed to load libraries:', errorMsg);
|
|
570
619
|
throw error;
|
|
571
620
|
}
|
|
572
621
|
}
|
|
@@ -1548,12 +1597,13 @@ class FaceDetectionEngine extends SimpleEventEmitter {
|
|
|
1548
1597
|
this.isInitializing = true;
|
|
1549
1598
|
this.emitDebug('initialization', 'Starting to load detection libraries...');
|
|
1550
1599
|
try {
|
|
1551
|
-
// Load
|
|
1552
|
-
this.emitDebug('initialization', 'Loading
|
|
1553
|
-
const
|
|
1600
|
+
// Load Libraries
|
|
1601
|
+
this.emitDebug('initialization', 'Loading Libraries...');
|
|
1602
|
+
const startLoadTime = performance.now();
|
|
1603
|
+
const { cv, human } = await loadLibraries(this.config.human_model_path, this.config.tensorflow_wasm_path, 30000);
|
|
1554
1604
|
if (!cv || !cv.Mat) {
|
|
1555
1605
|
console.log('[FaceDetectionEngine] Failed to load OpenCV.js: module is null or invalid');
|
|
1556
|
-
this.emit('detector-
|
|
1606
|
+
this.emit('detector-loaded', {
|
|
1557
1607
|
success: false,
|
|
1558
1608
|
error: 'Failed to load OpenCV.js: module is null or invalid'
|
|
1559
1609
|
});
|
|
@@ -1563,22 +1613,9 @@ class FaceDetectionEngine extends SimpleEventEmitter {
|
|
|
1563
1613
|
});
|
|
1564
1614
|
return;
|
|
1565
1615
|
}
|
|
1566
|
-
|
|
1567
|
-
version: cv?.getBuildInformation?.() || 'unknown'
|
|
1568
|
-
});
|
|
1569
|
-
console.log('[FaceDetectionEngine] OpenCV loaded successfully', {
|
|
1570
|
-
version: cv?.getBuildInformation?.() || 'unknown'
|
|
1571
|
-
});
|
|
1572
|
-
// Load Human.js
|
|
1573
|
-
console.log('[FaceDetectionEngine] Loading Human.js models...');
|
|
1574
|
-
this.emitDebug('initialization', 'Loading Human.js...');
|
|
1575
|
-
const humanStartTime = performance.now();
|
|
1576
|
-
this.human = await loadHuman(this.config.human_model_path, this.config.tensorflow_wasm_path);
|
|
1577
|
-
const humanLoadTime = performance.now() - humanStartTime;
|
|
1578
|
-
if (!this.human) {
|
|
1616
|
+
if (!human) {
|
|
1579
1617
|
const errorMsg = 'Failed to load Human.js: instance is null';
|
|
1580
1618
|
console.log('[FaceDetectionEngine] ' + errorMsg);
|
|
1581
|
-
this.emitDebug('initialization', errorMsg, { loadTime: humanLoadTime }, 'error');
|
|
1582
1619
|
this.emit('detector-loaded', {
|
|
1583
1620
|
success: false,
|
|
1584
1621
|
error: errorMsg
|
|
@@ -1589,21 +1626,16 @@ class FaceDetectionEngine extends SimpleEventEmitter {
|
|
|
1589
1626
|
});
|
|
1590
1627
|
return;
|
|
1591
1628
|
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
version: this.human.version
|
|
1595
|
-
});
|
|
1596
|
-
console.log('[FaceDetectionEngine] Human.js loaded successfully', {
|
|
1597
|
-
loadTime: `${humanLoadTime.toFixed(2)}ms`,
|
|
1598
|
-
version: this.human.version
|
|
1599
|
-
});
|
|
1629
|
+
// Store human instance
|
|
1630
|
+
this.human = human;
|
|
1600
1631
|
this.isReady = true;
|
|
1601
1632
|
const loadedData = {
|
|
1602
1633
|
success: true,
|
|
1603
|
-
opencv_version:
|
|
1634
|
+
opencv_version: getOpenCVVersion(),
|
|
1604
1635
|
human_version: this.human.version
|
|
1605
1636
|
};
|
|
1606
1637
|
console.log('[FaceDetectionEngine] Engine initialized and ready', {
|
|
1638
|
+
initCostTime: (performance.now() - startLoadTime).toFixed(2),
|
|
1607
1639
|
opencv_version: loadedData.opencv_version,
|
|
1608
1640
|
human_version: loadedData.human_version
|
|
1609
1641
|
});
|
|
@@ -2373,5 +2405,5 @@ class FaceDetectionEngine extends SimpleEventEmitter {
|
|
|
2373
2405
|
}
|
|
2374
2406
|
}
|
|
2375
2407
|
|
|
2376
|
-
export { DetectionPeriod, ErrorCode, FaceDetectionEngine, LivenessAction, LivenessActionStatus, PromptCode, FaceDetectionEngine as default, getCvSync, loadOpenCV, preloadOpenCV };
|
|
2408
|
+
export { DetectionPeriod, ErrorCode, FaceDetectionEngine, LivenessAction, LivenessActionStatus, PromptCode, FaceDetectionEngine as default, getCvSync, getOpenCVVersion, loadOpenCV, preloadOpenCV };
|
|
2377
2409
|
//# sourceMappingURL=index.esm.js.map
|