@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.js CHANGED
@@ -346,6 +346,11 @@
346
346
  * @param timeout - Maximum wait time in milliseconds (default: 30000)
347
347
  */
348
348
  async function preloadOpenCV(timeout = 30000) {
349
+ // 如果已经初始化完成,直接返回
350
+ if (opencvInitialized) {
351
+ console.log('[OpenCV] Already initialized, skipping preload');
352
+ return;
353
+ }
349
354
  // 如果已经在初始化中,返回现有的 Promise
350
355
  if (opencvInitPromise) {
351
356
  console.log('[OpenCV] Already initializing, reusing existing promise');
@@ -356,16 +361,16 @@
356
361
  opencvInitPromise = _initializeOpenCV(timeout);
357
362
  try {
358
363
  const initTime = await opencvInitPromise;
359
- console.log('[OpenCV] Preload completed successfully, cost:', initTime.toFixed(0), 'ms');
364
+ console.log('[OpenCV] Preload completed successfully ', {
365
+ initTime: `${initTime.toFixed(2)}ms`,
366
+ version: getOpenCVVersion()
367
+ });
360
368
  }
361
369
  catch (error) {
362
370
  console.error('[OpenCV] Preload failed:', error);
371
+ opencvInitPromise = null; // 失败时清除 Promise,允许重试
363
372
  throw error;
364
373
  }
365
- finally {
366
- // 完成后清除 Promise 标记,但保留已初始化的 cv 对象
367
- opencvInitPromise = null;
368
- }
369
374
  }
370
375
  /**
371
376
  * Internal helper to initialize OpenCV
@@ -373,16 +378,16 @@
373
378
  */
374
379
  async function _initializeOpenCV(timeout) {
375
380
  const initStartTime = performance.now();
376
- console.log('[FaceDetectionEngine] Waiting for OpenCV WASM initialization...');
381
+ console.log('Waiting for OpenCV WASM initialization...');
377
382
  // 快速路径:检查是否已经初始化
378
383
  if (cvModule.Mat) {
379
384
  const initTime = performance.now() - initStartTime;
380
- console.log(`[FaceDetectionEngine] OpenCV.js already initialized, took ${initTime.toFixed(2)}ms`);
385
+ console.log(`OpenCV.js already initialized, took ${initTime.toFixed(2)}ms`);
381
386
  return cvModule;
382
387
  }
383
388
  if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
384
389
  const initTime = performance.now() - initStartTime;
385
- console.log(`[FaceDetectionEngine] OpenCV.js already initialized (from global), took ${initTime.toFixed(2)}ms`);
390
+ console.log(`OpenCV.js already initialized (from global), took ${initTime.toFixed(2)}ms`);
386
391
  cvModule = globalThis.cv;
387
392
  return cvModule;
388
393
  }
@@ -390,10 +395,10 @@
390
395
  if (typeof globalThis !== 'undefined' && !globalThis.cv) {
391
396
  if (cvModule && Object.isExtensible(cvModule)) {
392
397
  globalThis.cv = cvModule;
393
- console.log('[FaceDetectionEngine] cvModule assigned to globalThis.cv');
398
+ console.log('cvModule assigned to globalThis.cv');
394
399
  }
395
400
  else {
396
- console.log('[FaceDetectionEngine] cvModule is not extensible or globalThis already has cv');
401
+ console.log('cvModule is not extensible or globalThis already has cv');
397
402
  }
398
403
  }
399
404
  return new Promise((resolve, reject) => {
@@ -402,7 +407,7 @@
402
407
  // 防止多次调用 resolve/reject
403
408
  let finished = false;
404
409
  const timeoutId = setTimeout(() => {
405
- console.error('[FaceDetectionEngine] OpenCV.js initialization timeout after ' + timeout + 'ms');
410
+ console.error('OpenCV.js initialization timeout after ' + timeout + 'ms');
406
411
  finished = true;
407
412
  if (pollInterval) {
408
413
  clearInterval(pollInterval);
@@ -411,65 +416,63 @@
411
416
  }, timeout);
412
417
  const resolveOnce = (source) => {
413
418
  if (finished) {
414
- console.log('[FaceDetectionEngine] [resolveOnce] Already finished, ignoring call from:', source);
419
+ console.log('[resolveOnce] Already finished, ignoring call from:', source);
415
420
  return;
416
421
  }
417
422
  finished = true;
418
- console.log('[FaceDetectionEngine] [resolveOnce] Marking as finished');
423
+ console.log('[resolveOnce] Marking as finished');
419
424
  // 立即停止所有定时器和轮询
420
425
  clearTimeout(timeoutId);
421
426
  if (pollInterval !== null) {
422
427
  clearInterval(pollInterval);
423
428
  pollInterval = null;
424
- console.log('[FaceDetectionEngine] [resolveOnce] Poll interval cleared');
429
+ console.log('[resolveOnce] Poll interval cleared');
425
430
  }
426
431
  const initTime = performance.now() - initStartTime;
427
- console.log(`[FaceDetectionEngine] OpenCV.js initialized (${source}), took ${initTime.toFixed(2)}ms`);
432
+ console.log(`OpenCV.js initialized (${source}), took ${initTime.toFixed(2)}ms`);
428
433
  // 标记初始化完成
429
434
  opencvInitialized = true;
430
435
  // 返回简单的标记,实际的 cv 对象已经在 globalThis.cv 上了
431
- // 通过 getCvSync() 获取
432
436
  resolve(initTime);
433
- console.log('[FaceDetectionEngine] [resolveOnce] Promise resolved with true');
434
437
  };
435
438
  // 尝试设置回调(只有在 cvModule 可扩展时才尝试)
436
439
  const canSetCallback = cvModule && Object.isExtensible(cvModule);
437
440
  if (canSetCallback) {
438
441
  try {
439
442
  const originalOnRuntimeInitialized = cvModule.onRuntimeInitialized(cvModule).onRuntimeInitialized = () => {
440
- console.log('[FaceDetectionEngine] onRuntimeInitialized callback triggered');
443
+ console.log('[onRuntimeInitialized] callback triggered');
441
444
  // 调用原始回调(如果存在)
442
445
  if (originalOnRuntimeInitialized && typeof originalOnRuntimeInitialized === 'function') {
443
446
  try {
444
447
  originalOnRuntimeInitialized();
445
448
  }
446
449
  catch (e) {
447
- console.warn('[FaceDetectionEngine] Original onRuntimeInitialized callback failed:', e);
450
+ console.warn('[onRuntimeInitialized] callback failed:', e);
448
451
  }
449
452
  }
450
453
  resolveOnce('callback');
451
454
  };
452
- console.log('[FaceDetectionEngine] onRuntimeInitialized callback set successfully');
455
+ console.log('[onRuntimeInitialized] callback set successfully');
453
456
  }
454
457
  catch (e) {
455
- console.warn('[FaceDetectionEngine] Failed to set onRuntimeInitialized callback, will use polling:', e);
458
+ console.warn('[onRuntimeInitialized] Failed to set callback, will use polling:', e);
456
459
  }
457
460
  }
458
461
  else {
459
- console.log('[FaceDetectionEngine] cvModule is not extensible, using polling mode');
462
+ console.log('[polling] cvModule is not extensible, using polling mode');
460
463
  }
461
464
  // 启动轮询作为备用方案或主要方案
462
465
  pollInterval = setInterval(() => {
463
- console.log('[FaceDetectionEngine] [polling] Checking for Mat class...');
466
+ console.log('[polling] Checking for Mat class...');
464
467
  // 优先检查 cvModule 中是否有 Mat
465
468
  if (cvModule.Mat) {
466
- console.log('[FaceDetectionEngine] [polling] Found Mat in cvModule');
469
+ console.log('[polling] Found Mat in cvModule');
467
470
  resolveOnce('cvModule polling');
468
471
  return;
469
472
  }
470
473
  // 其次检查 globalThis.cv 中是否有 Mat
471
474
  if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
472
- console.log('[FaceDetectionEngine] [polling] Found Mat in globalThis.cv');
475
+ console.log('[polling] Found Mat in globalThis.cv');
473
476
  cvModule = globalThis.cv;
474
477
  resolveOnce('globalThis.cv polling');
475
478
  return;
@@ -483,39 +486,38 @@
483
486
  * @returns Promise that resolves with cv module
484
487
  */
485
488
  async function loadOpenCV(timeout = 30000) {
486
- console.log('[FaceDetectionEngine] loadOpenCV: START');
487
489
  try {
488
490
  // 快速检查是否已经初始化完成
489
491
  if (opencvInitialized) {
490
- console.log('[FaceDetectionEngine] loadOpenCV: Already initialized, returning immediately');
492
+ console.log('[loadOpenCV] Already initialized, returning immediately');
491
493
  const cv = getCvSync();
492
494
  if (cv) {
493
495
  return { cv };
494
496
  }
495
497
  }
496
498
  // 等待初始化
497
- console.log('[FaceDetectionEngine] loadOpenCV: Waiting for initialization...');
499
+ console.log('[loadOpenCV] Waiting for initialization...');
498
500
  if (!opencvInitPromise) {
499
- console.log('[FaceDetectionEngine] loadOpenCV: Starting new initialization');
501
+ console.log('[loadOpenCV] Starting new initialization');
500
502
  opencvInitPromise = _initializeOpenCV(timeout);
501
503
  }
502
- console.log('[FaceDetectionEngine] loadOpenCV: Awaiting opencvInitPromise');
504
+ console.log('[loadOpenCV] Awaiting opencvInitPromise');
503
505
  const initTime = await opencvInitPromise;
504
- console.log('[FaceDetectionEngine] loadOpenCV: After await, Promise resolved, cost :', initTime.toFixed(0), 'ms');
505
- // 清除 Promise 缓存
506
- opencvInitPromise = null;
507
506
  // 获取初始化后的 cv 对象
508
507
  const cv = getCvSync();
509
508
  if (!cv) {
510
- console.error('[FaceDetectionEngine] loadOpenCV: getCvSync returned null');
509
+ console.error('[loadOpenCV] getCvSync returned null');
511
510
  throw new Error('OpenCV module is invalid');
512
511
  }
513
- console.log('[FaceDetectionEngine] loadOpenCV: SUCCESS');
514
- return { cv };
512
+ console.log('[loadOpenCV] OpenCV.js load successfully', {
513
+ initTime: `${initTime.toFixed(2)}ms`,
514
+ version: getOpenCVVersion()
515
+ });
516
+ return cv;
515
517
  }
516
518
  catch (error) {
517
- console.error('[FaceDetectionEngine] loadOpenCV: ERROR', error);
518
- opencvInitPromise = null;
519
+ console.error('[loadOpenCV] OpenCV.js load failed', error);
520
+ opencvInitPromise = null; // 失败时清除 Promise,允许重试
519
521
  throw error;
520
522
  }
521
523
  }
@@ -534,6 +536,35 @@
534
536
  }
535
537
  return null;
536
538
  }
539
+ /**
540
+ * Extract OpenCV version from getBuildInformation
541
+ * @returns version string like "4.12.0"
542
+ */
543
+ function getOpenCVVersion() {
544
+ try {
545
+ const cv = getCvSync();
546
+ if (!cv || !cv.getBuildInformation) {
547
+ return 'unknown';
548
+ }
549
+ const buildInfo = cv.getBuildInformation();
550
+ // 查找 "Version control:" 或 "OpenCV" 开头的行
551
+ // 格式: "Version control: 4.12.0"
552
+ const versionMatch = buildInfo.match(/Version\s+control:\s+(\d+\.\d+\.\d+)/i);
553
+ if (versionMatch && versionMatch[1]) {
554
+ return versionMatch[1];
555
+ }
556
+ // 备用方案:查找 "OpenCV X.X.X" 格式
557
+ const opencvMatch = buildInfo.match(/OpenCV\s+(\d+\.\d+\.\d+)/i);
558
+ if (opencvMatch && opencvMatch[1]) {
559
+ return opencvMatch[1];
560
+ }
561
+ return 'unknown';
562
+ }
563
+ catch (error) {
564
+ console.error('[getOpenCVVersion] Failed to get version:', error);
565
+ return 'unknown';
566
+ }
567
+ }
537
568
  /**
538
569
  * Load Human.js
539
570
  * @param modelPath - Path to model files (optional)
@@ -541,6 +572,7 @@
541
572
  * @returns Promise that resolves with Human instance
542
573
  */
543
574
  async function loadHuman(modelPath, wasmPath) {
575
+ const initStartTime = performance.now();
544
576
  const config = {
545
577
  backend: _detectOptimalBackend(),
546
578
  face: {
@@ -563,24 +595,16 @@
563
595
  if (wasmPath) {
564
596
  config.wasmPath = wasmPath;
565
597
  }
566
- console.log('[FaceDetectionEngine] Human.js config:', {
598
+ console.log('[loadHuman] Config:', {
567
599
  backend: config.backend,
568
600
  modelBasePath: config.modelBasePath || '(using default)',
569
601
  wasmPath: config.wasmPath || '(using default)'
570
602
  });
571
- const initStartTime = performance.now();
572
- console.log('[FaceDetectionEngine] Creating Human instance...');
573
603
  const human = new Human(config);
574
- const instanceCreateTime = performance.now() - initStartTime;
575
- console.log(`[FaceDetectionEngine] Human instance created, took ${instanceCreateTime.toFixed(2)}ms`);
576
- console.log('[FaceDetectionEngine] Loading Human.js models...');
577
- const modelLoadStartTime = performance.now();
578
604
  try {
579
605
  await human.load();
580
- const loadTime = performance.now() - modelLoadStartTime;
581
606
  const totalTime = performance.now() - initStartTime;
582
- console.log('[FaceDetectionEngine] Human.js loaded successfully', {
583
- modelLoadTime: `${loadTime.toFixed(2)}ms`,
607
+ console.log('[loadHuman] Loaded successfully', {
584
608
  totalInitTime: `${totalTime.toFixed(2)}ms`,
585
609
  version: human.version
586
610
  });
@@ -588,7 +612,32 @@
588
612
  }
589
613
  catch (error) {
590
614
  const errorMsg = error instanceof Error ? error.message : 'Unknown error';
591
- console.error('[FaceDetectionEngine] Human.js load failed:', errorMsg);
615
+ console.error('[loadHuman] Load failed:', errorMsg);
616
+ throw error;
617
+ }
618
+ }
619
+ async function loadLibraries(modelPath, wasmPath, timeout) {
620
+ console.log('[loadLibraries] Starting parallel load of OpenCV and Human...');
621
+ const startTime = performance.now();
622
+ if (timeout == undefined) {
623
+ timeout = 30000;
624
+ }
625
+ try {
626
+ // 并行加载 OpenCV 和 Human
627
+ const [cv, human] = await Promise.all([
628
+ loadOpenCV(timeout),
629
+ loadHuman(modelPath, wasmPath)
630
+ ]);
631
+ const totalTime = performance.now() - startTime;
632
+ console.log('[loadLibraries] Both libraries loaded successfully in', totalTime.toFixed(2), 'ms');
633
+ return {
634
+ cv,
635
+ human
636
+ };
637
+ }
638
+ catch (error) {
639
+ const errorMsg = error instanceof Error ? error.message : 'Unknown error';
640
+ console.error('[loadLibraries] Failed to load libraries:', errorMsg);
592
641
  throw error;
593
642
  }
594
643
  }
@@ -1570,12 +1619,13 @@
1570
1619
  this.isInitializing = true;
1571
1620
  this.emitDebug('initialization', 'Starting to load detection libraries...');
1572
1621
  try {
1573
- // Load OpenCV
1574
- this.emitDebug('initialization', 'Loading OpenCV...');
1575
- const { cv } = await loadOpenCV(300000); // 5 minute timeout
1622
+ // Load Libraries
1623
+ this.emitDebug('initialization', 'Loading Libraries...');
1624
+ const startLoadTime = performance.now();
1625
+ const { cv, human } = await loadLibraries(this.config.human_model_path, this.config.tensorflow_wasm_path, 30000);
1576
1626
  if (!cv || !cv.Mat) {
1577
1627
  console.log('[FaceDetectionEngine] Failed to load OpenCV.js: module is null or invalid');
1578
- this.emit('detector-error', {
1628
+ this.emit('detector-loaded', {
1579
1629
  success: false,
1580
1630
  error: 'Failed to load OpenCV.js: module is null or invalid'
1581
1631
  });
@@ -1585,22 +1635,9 @@
1585
1635
  });
1586
1636
  return;
1587
1637
  }
1588
- this.emitDebug('initialization', 'OpenCV loaded successfully', {
1589
- version: cv?.getBuildInformation?.() || 'unknown'
1590
- });
1591
- console.log('[FaceDetectionEngine] OpenCV loaded successfully', {
1592
- version: cv?.getBuildInformation?.() || 'unknown'
1593
- });
1594
- // Load Human.js
1595
- console.log('[FaceDetectionEngine] Loading Human.js models...');
1596
- this.emitDebug('initialization', 'Loading Human.js...');
1597
- const humanStartTime = performance.now();
1598
- this.human = await loadHuman(this.config.human_model_path, this.config.tensorflow_wasm_path);
1599
- const humanLoadTime = performance.now() - humanStartTime;
1600
- if (!this.human) {
1638
+ if (!human) {
1601
1639
  const errorMsg = 'Failed to load Human.js: instance is null';
1602
1640
  console.log('[FaceDetectionEngine] ' + errorMsg);
1603
- this.emitDebug('initialization', errorMsg, { loadTime: humanLoadTime }, 'error');
1604
1641
  this.emit('detector-loaded', {
1605
1642
  success: false,
1606
1643
  error: errorMsg
@@ -1611,21 +1648,16 @@
1611
1648
  });
1612
1649
  return;
1613
1650
  }
1614
- this.emitDebug('initialization', 'Human.js loaded successfully', {
1615
- loadTime: `${humanLoadTime.toFixed(2)}ms`,
1616
- version: this.human.version
1617
- });
1618
- console.log('[FaceDetectionEngine] Human.js loaded successfully', {
1619
- loadTime: `${humanLoadTime.toFixed(2)}ms`,
1620
- version: this.human.version
1621
- });
1651
+ // Store human instance
1652
+ this.human = human;
1622
1653
  this.isReady = true;
1623
1654
  const loadedData = {
1624
1655
  success: true,
1625
- opencv_version: cv?.getBuildInformation?.() || 'unknown',
1656
+ opencv_version: getOpenCVVersion(),
1626
1657
  human_version: this.human.version
1627
1658
  };
1628
1659
  console.log('[FaceDetectionEngine] Engine initialized and ready', {
1660
+ initCostTime: (performance.now() - startLoadTime).toFixed(2),
1629
1661
  opencv_version: loadedData.opencv_version,
1630
1662
  human_version: loadedData.human_version
1631
1663
  });
@@ -2398,6 +2430,7 @@
2398
2430
  exports.FaceDetectionEngine = FaceDetectionEngine;
2399
2431
  exports.default = FaceDetectionEngine;
2400
2432
  exports.getCvSync = getCvSync;
2433
+ exports.getOpenCVVersion = getOpenCVVersion;
2401
2434
  exports.loadOpenCV = loadOpenCV;
2402
2435
  exports.preloadOpenCV = preloadOpenCV;
2403
2436