@sssxyd/face-liveness-detector 0.2.21 → 0.2.23

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
@@ -311,6 +311,7 @@
311
311
  }
312
312
  let webglAvailableCache = null;
313
313
  let opencvInitPromise = null;
314
+ let opencvInitialized = false; // 标记是否已初始化完成
314
315
  function _isWebGLAvailable() {
315
316
  if (webglAvailableCache !== null) {
316
317
  return webglAvailableCache;
@@ -354,8 +355,8 @@
354
355
  // 复用 loadOpenCV 的初始化逻辑
355
356
  opencvInitPromise = _initializeOpenCV(timeout);
356
357
  try {
357
- await opencvInitPromise;
358
- console.log('[OpenCV] Preload completed successfully');
358
+ const initTime = await opencvInitPromise;
359
+ console.log('[OpenCV] Preload completed successfully, cost:', initTime.toFixed(0), 'ms');
359
360
  }
360
361
  catch (error) {
361
362
  console.error('[OpenCV] Preload failed:', error);
@@ -372,16 +373,16 @@
372
373
  */
373
374
  async function _initializeOpenCV(timeout) {
374
375
  const initStartTime = performance.now();
375
- console.log('[FaceDetectionEngine] Waiting for OpenCV WASM initialization...');
376
+ console.log('Waiting for OpenCV WASM initialization...');
376
377
  // 快速路径:检查是否已经初始化
377
378
  if (cvModule.Mat) {
378
379
  const initTime = performance.now() - initStartTime;
379
- console.log(`[FaceDetectionEngine] OpenCV.js already initialized, took ${initTime.toFixed(2)}ms`);
380
+ console.log(`OpenCV.js already initialized, took ${initTime.toFixed(2)}ms`);
380
381
  return cvModule;
381
382
  }
382
383
  if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
383
384
  const initTime = performance.now() - initStartTime;
384
- console.log(`[FaceDetectionEngine] OpenCV.js already initialized (from global), took ${initTime.toFixed(2)}ms`);
385
+ console.log(`OpenCV.js already initialized (from global), took ${initTime.toFixed(2)}ms`);
385
386
  cvModule = globalThis.cv;
386
387
  return cvModule;
387
388
  }
@@ -389,10 +390,10 @@
389
390
  if (typeof globalThis !== 'undefined' && !globalThis.cv) {
390
391
  if (cvModule && Object.isExtensible(cvModule)) {
391
392
  globalThis.cv = cvModule;
392
- console.log('[FaceDetectionEngine] cvModule assigned to globalThis.cv');
393
+ console.log('cvModule assigned to globalThis.cv');
393
394
  }
394
395
  else {
395
- console.log('[FaceDetectionEngine] cvModule is not extensible or globalThis already has cv');
396
+ console.log('cvModule is not extensible or globalThis already has cv');
396
397
  }
397
398
  }
398
399
  return new Promise((resolve, reject) => {
@@ -401,7 +402,7 @@
401
402
  // 防止多次调用 resolve/reject
402
403
  let finished = false;
403
404
  const timeoutId = setTimeout(() => {
404
- console.error('[FaceDetectionEngine] OpenCV.js initialization timeout after ' + timeout + 'ms');
405
+ console.error('OpenCV.js initialization timeout after ' + timeout + 'ms');
405
406
  finished = true;
406
407
  if (pollInterval) {
407
408
  clearInterval(pollInterval);
@@ -410,70 +411,65 @@
410
411
  }, timeout);
411
412
  const resolveOnce = (source) => {
412
413
  if (finished) {
413
- console.log('[FaceDetectionEngine] [resolveOnce] Already finished, ignoring call from:', source);
414
+ console.log('[resolveOnce] Already finished, ignoring call from:', source);
414
415
  return;
415
416
  }
416
417
  finished = true;
417
- console.log('[FaceDetectionEngine] [resolveOnce] Marking as finished');
418
+ console.log('[resolveOnce] Marking as finished');
418
419
  // 立即停止所有定时器和轮询
419
420
  clearTimeout(timeoutId);
420
421
  if (pollInterval !== null) {
421
422
  clearInterval(pollInterval);
422
423
  pollInterval = null;
423
- console.log('[FaceDetectionEngine] [resolveOnce] Poll interval cleared:', pollInterval);
424
- }
425
- // 确保返回有效的 cv 对象
426
- let validCv = cvModule;
427
- if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
428
- validCv = globalThis.cv;
424
+ console.log('[resolveOnce] Poll interval cleared');
429
425
  }
430
426
  const initTime = performance.now() - initStartTime;
431
- console.log(`[FaceDetectionEngine] OpenCV.js initialized (${source}), took ${initTime.toFixed(2)}ms`, {
432
- hasMat: !!(validCv && validCv.Mat),
433
- source: source
434
- });
435
- console.log('[FaceDetectionEngine] [resolveOnce] About to call resolve');
436
- resolve(validCv);
437
- console.log('[FaceDetectionEngine] [resolveOnce] resolve called');
427
+ console.log(`OpenCV.js initialized (${source}), took ${initTime.toFixed(2)}ms`);
428
+ // 标记初始化完成
429
+ opencvInitialized = true;
430
+ // 返回简单的标记,实际的 cv 对象已经在 globalThis.cv 上了
431
+ // 通过 getCvSync() 获取
432
+ resolve(initTime);
433
+ console.log('[resolveOnce] Promise resolved');
438
434
  };
439
435
  // 尝试设置回调(只有在 cvModule 可扩展时才尝试)
440
436
  const canSetCallback = cvModule && Object.isExtensible(cvModule);
441
437
  if (canSetCallback) {
442
438
  try {
443
439
  const originalOnRuntimeInitialized = cvModule.onRuntimeInitialized(cvModule).onRuntimeInitialized = () => {
444
- console.log('[FaceDetectionEngine] onRuntimeInitialized callback triggered');
440
+ console.log('[onRuntimeInitialized] callback triggered');
445
441
  // 调用原始回调(如果存在)
446
442
  if (originalOnRuntimeInitialized && typeof originalOnRuntimeInitialized === 'function') {
447
443
  try {
448
444
  originalOnRuntimeInitialized();
449
445
  }
450
446
  catch (e) {
451
- console.warn('[FaceDetectionEngine] Original onRuntimeInitialized callback failed:', e);
447
+ console.warn('[onRuntimeInitialized] callback failed:', e);
452
448
  }
453
449
  }
454
450
  resolveOnce('callback');
455
451
  };
456
- console.log('[FaceDetectionEngine] onRuntimeInitialized callback set successfully');
452
+ console.log('[onRuntimeInitialized] callback set successfully');
457
453
  }
458
454
  catch (e) {
459
- console.warn('[FaceDetectionEngine] Failed to set onRuntimeInitialized callback, will use polling:', e);
455
+ console.warn('[onRuntimeInitialized] Failed to set callback, will use polling:', e);
460
456
  }
461
457
  }
462
458
  else {
463
- console.log('[FaceDetectionEngine] cvModule is not extensible, using polling mode');
459
+ console.log('[polling] cvModule is not extensible, using polling mode');
464
460
  }
465
461
  // 启动轮询作为备用方案或主要方案
466
462
  pollInterval = setInterval(() => {
467
- console.log('[FaceDetectionEngine] [polling] Checking for Mat class...');
463
+ console.log('[polling] Checking for Mat class...');
468
464
  // 优先检查 cvModule 中是否有 Mat
469
465
  if (cvModule.Mat) {
470
- console.log('[FaceDetectionEngine] [polling] Found Mat in cvModule');
466
+ console.log('[polling] Found Mat in cvModule');
471
467
  resolveOnce('cvModule polling');
472
468
  return;
473
469
  }
474
470
  // 其次检查 globalThis.cv 中是否有 Mat
475
471
  if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
476
- console.log('[FaceDetectionEngine] [polling] Found Mat in globalThis.cv');
472
+ console.log('[polling] Found Mat in globalThis.cv');
477
473
  cvModule = globalThis.cv;
478
474
  resolveOnce('globalThis.cv polling');
479
475
  return;
@@ -487,81 +483,40 @@
487
483
  * @returns Promise that resolves with cv module
488
484
  */
489
485
  async function loadOpenCV(timeout = 30000) {
490
- let cv;
491
- console.log('[FaceDetectionEngine] Loading OpenCV.js...');
492
486
  try {
493
- console.log('[FaceDetectionEngine] [loadOpenCV] Starting, checking conditions...');
494
- // 如果已经在初始化中,复用现有的 Promise
495
- if (opencvInitPromise) {
496
- console.log('[FaceDetectionEngine] [loadOpenCV] opencvInitPromise exists, awaiting...');
497
- console.log('[FaceDetectionEngine] [loadOpenCV] Before await, Promise state:', opencvInitPromise.constructor.name);
498
- try {
499
- cv = await opencvInitPromise;
500
- console.log('[FaceDetectionEngine] [loadOpenCV] AFTER AWAIT - OpenCV promise resolved, got cv module:', { hasMat: !!(cv && cv.Mat), type: typeof cv });
501
- }
502
- catch (awaitError) {
503
- console.error('[FaceDetectionEngine] [loadOpenCV] AWAIT ERROR:', awaitError);
504
- throw awaitError;
487
+ // 快速检查是否已经初始化完成
488
+ if (opencvInitialized) {
489
+ console.log('[loadOpenCV] Already initialized, returning immediately');
490
+ const cv = getCvSync();
491
+ if (cv) {
492
+ return { cv };
505
493
  }
506
- // 完成后清除 Promise 标记
507
- console.log('[FaceDetectionEngine] [loadOpenCV] Clearing opencvInitPromise');
508
- opencvInitPromise = null;
509
- console.log('[FaceDetectionEngine] [loadOpenCV] opencvInitPromise cleared');
510
- }
511
- else if (cvModule.Mat) {
512
- // 检查 cvModule 是否已经初始化
513
- console.log('[FaceDetectionEngine] [loadOpenCV] OpenCV.js already initialized in cvModule');
514
- cv = cvModule;
515
- }
516
- else if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
517
- // 检查全局 cv 是否已经初始化
518
- console.log('[FaceDetectionEngine] [loadOpenCV] OpenCV.js already initialized in globalThis.cv');
519
- cvModule = globalThis.cv;
520
- cv = cvModule;
521
494
  }
522
- else {
523
- // 开始新的初始化
524
- console.log('[FaceDetectionEngine] [loadOpenCV] Starting new OpenCV initialization...');
495
+ // 等待初始化
496
+ console.log('[loadOpenCV] Waiting for initialization...');
497
+ if (!opencvInitPromise) {
498
+ console.log('[loadOpenCV] Starting new initialization');
525
499
  opencvInitPromise = _initializeOpenCV(timeout);
526
- try {
527
- console.log('[FaceDetectionEngine] [loadOpenCV] Awaiting _initializeOpenCV...');
528
- cv = await opencvInitPromise;
529
- console.log('[FaceDetectionEngine] [loadOpenCV] OpenCV initialization completed, got cv module:', { hasMat: !!(cv && cv.Mat), type: typeof cv });
530
- // 完成后清除 Promise 标记
531
- opencvInitPromise = null;
532
- }
533
- catch (error) {
534
- // 失败后也要清除 Promise
535
- console.error('[FaceDetectionEngine] [loadOpenCV] _initializeOpenCV threw error:', error);
536
- opencvInitPromise = null;
537
- throw error;
538
- }
539
500
  }
540
- console.log('[FaceDetectionEngine] [loadOpenCV] After await, validating cv module...');
541
- // 最终验证 - 如果 cv 无效,尝试从 globalThis 获取
542
- if (!cv || !cv.Mat) {
543
- console.warn('[FaceDetectionEngine] [loadOpenCV] Returned cv module is invalid, attempting to get from globalThis.cv...');
544
- if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
545
- console.log('[FaceDetectionEngine] [loadOpenCV] Found valid cv in globalThis.cv, using that');
546
- cv = globalThis.cv;
547
- }
548
- else {
549
- console.error('[FaceDetectionEngine] [loadOpenCV] OpenCV module is invalid:', {
550
- hasMat: cv && cv.Mat,
551
- type: typeof cv,
552
- keys: cv ? Object.keys(cv).slice(0, 10) : 'N/A',
553
- globalCvAvailable: !!(typeof globalThis !== 'undefined' && globalThis.cv)
554
- });
555
- throw new Error('OpenCV.js loaded but module is invalid (no Mat class found)');
556
- }
501
+ console.log('[loadOpenCV] Awaiting opencvInitPromise');
502
+ const initTime = await opencvInitPromise;
503
+ // 清除 Promise 缓存
504
+ opencvInitPromise = null;
505
+ // 获取初始化后的 cv 对象
506
+ const cv = getCvSync();
507
+ if (!cv) {
508
+ console.error('[loadOpenCV] getCvSync returned null');
509
+ throw new Error('OpenCV module is invalid');
557
510
  }
558
- console.log('[FaceDetectionEngine] [loadOpenCV] Validation passed, returning cv module');
559
- console.log('[FaceDetectionEngine] OpenCV.js loaded successfully');
560
- return { cv };
511
+ console.log('[loadOpenCV] OpenCV.js load successfully', {
512
+ initTime: `${initTime.toFixed(2)}ms`,
513
+ version: getOpenCVVersion()
514
+ });
515
+ return cv;
561
516
  }
562
517
  catch (error) {
563
- console.error('[FaceDetectionEngine] [loadOpenCV] Exception caught:', error);
564
- console.error('[FaceDetectionEngine] Failed to load OpenCV.js:', error);
518
+ console.error('[loadOpenCV] OpenCV.js load failed', error);
519
+ opencvInitPromise = null;
565
520
  throw error;
566
521
  }
567
522
  }
@@ -580,6 +535,35 @@
580
535
  }
581
536
  return null;
582
537
  }
538
+ /**
539
+ * Extract OpenCV version from getBuildInformation
540
+ * @returns version string like "4.12.0"
541
+ */
542
+ function getOpenCVVersion() {
543
+ try {
544
+ const cv = getCvSync();
545
+ if (!cv || !cv.getBuildInformation) {
546
+ return 'unknown';
547
+ }
548
+ const buildInfo = cv.getBuildInformation();
549
+ // 查找 "Version control:" 或 "OpenCV" 开头的行
550
+ // 格式: "Version control: 4.12.0"
551
+ const versionMatch = buildInfo.match(/Version\s+control:\s+(\d+\.\d+\.\d+)/i);
552
+ if (versionMatch && versionMatch[1]) {
553
+ return versionMatch[1];
554
+ }
555
+ // 备用方案:查找 "OpenCV X.X.X" 格式
556
+ const opencvMatch = buildInfo.match(/OpenCV\s+(\d+\.\d+\.\d+)/i);
557
+ if (opencvMatch && opencvMatch[1]) {
558
+ return opencvMatch[1];
559
+ }
560
+ return 'unknown';
561
+ }
562
+ catch (error) {
563
+ console.error('[getOpenCVVersion] Failed to get version:', error);
564
+ return 'unknown';
565
+ }
566
+ }
583
567
  /**
584
568
  * Load Human.js
585
569
  * @param modelPath - Path to model files (optional)
@@ -609,24 +593,17 @@
609
593
  if (wasmPath) {
610
594
  config.wasmPath = wasmPath;
611
595
  }
612
- console.log('[FaceDetectionEngine] Human.js config:', {
596
+ console.log('[loadHuman] Config:', {
613
597
  backend: config.backend,
614
598
  modelBasePath: config.modelBasePath || '(using default)',
615
599
  wasmPath: config.wasmPath || '(using default)'
616
600
  });
617
601
  const initStartTime = performance.now();
618
- console.log('[FaceDetectionEngine] Creating Human instance...');
619
602
  const human = new Human(config);
620
- const instanceCreateTime = performance.now() - initStartTime;
621
- console.log(`[FaceDetectionEngine] Human instance created, took ${instanceCreateTime.toFixed(2)}ms`);
622
- console.log('[FaceDetectionEngine] Loading Human.js models...');
623
- const modelLoadStartTime = performance.now();
624
603
  try {
625
604
  await human.load();
626
- const loadTime = performance.now() - modelLoadStartTime;
627
605
  const totalTime = performance.now() - initStartTime;
628
- console.log('[FaceDetectionEngine] Human.js loaded successfully', {
629
- modelLoadTime: `${loadTime.toFixed(2)}ms`,
606
+ console.log('[loadHuman] Loaded successfully', {
630
607
  totalInitTime: `${totalTime.toFixed(2)}ms`,
631
608
  version: human.version
632
609
  });
@@ -634,7 +611,32 @@
634
611
  }
635
612
  catch (error) {
636
613
  const errorMsg = error instanceof Error ? error.message : 'Unknown error';
637
- console.error('[FaceDetectionEngine] Human.js load failed:', errorMsg);
614
+ console.error('[loadHuman] Load failed:', errorMsg);
615
+ throw error;
616
+ }
617
+ }
618
+ async function loadLibraries(modelPath, wasmPath, timeout) {
619
+ console.log('[loadLibraries] Starting parallel load of OpenCV and Human...');
620
+ const startTime = performance.now();
621
+ if (timeout == undefined) {
622
+ timeout = 30000;
623
+ }
624
+ try {
625
+ // 并行加载 OpenCV 和 Human
626
+ const [cv, human] = await Promise.all([
627
+ loadOpenCV(timeout),
628
+ loadHuman(modelPath, wasmPath)
629
+ ]);
630
+ const totalTime = performance.now() - startTime;
631
+ console.log('[loadLibraries] Both libraries loaded successfully in', totalTime.toFixed(2), 'ms');
632
+ return {
633
+ cv,
634
+ human
635
+ };
636
+ }
637
+ catch (error) {
638
+ const errorMsg = error instanceof Error ? error.message : 'Unknown error';
639
+ console.error('[loadLibraries] Failed to load libraries:', errorMsg);
638
640
  throw error;
639
641
  }
640
642
  }
@@ -1616,12 +1618,13 @@
1616
1618
  this.isInitializing = true;
1617
1619
  this.emitDebug('initialization', 'Starting to load detection libraries...');
1618
1620
  try {
1619
- // Load OpenCV
1620
- this.emitDebug('initialization', 'Loading OpenCV...');
1621
- const { cv } = await loadOpenCV(300000); // 5 minute timeout
1621
+ // Load Libraries
1622
+ this.emitDebug('initialization', 'Loading Libraries...');
1623
+ const startLoadTime = performance.now();
1624
+ const { cv, human } = await loadLibraries(this.config.human_model_path, this.config.tensorflow_wasm_path, 30000);
1622
1625
  if (!cv || !cv.Mat) {
1623
1626
  console.log('[FaceDetectionEngine] Failed to load OpenCV.js: module is null or invalid');
1624
- this.emit('detector-error', {
1627
+ this.emit('detector-loaded', {
1625
1628
  success: false,
1626
1629
  error: 'Failed to load OpenCV.js: module is null or invalid'
1627
1630
  });
@@ -1631,22 +1634,9 @@
1631
1634
  });
1632
1635
  return;
1633
1636
  }
1634
- this.emitDebug('initialization', 'OpenCV loaded successfully', {
1635
- version: cv?.getBuildInformation?.() || 'unknown'
1636
- });
1637
- console.log('[FaceDetectionEngine] OpenCV loaded successfully', {
1638
- version: cv?.getBuildInformation?.() || 'unknown'
1639
- });
1640
- // Load Human.js
1641
- console.log('[FaceDetectionEngine] Loading Human.js models...');
1642
- this.emitDebug('initialization', 'Loading Human.js...');
1643
- const humanStartTime = performance.now();
1644
- this.human = await loadHuman(this.config.human_model_path, this.config.tensorflow_wasm_path);
1645
- const humanLoadTime = performance.now() - humanStartTime;
1646
1637
  if (!this.human) {
1647
1638
  const errorMsg = 'Failed to load Human.js: instance is null';
1648
1639
  console.log('[FaceDetectionEngine] ' + errorMsg);
1649
- this.emitDebug('initialization', errorMsg, { loadTime: humanLoadTime }, 'error');
1650
1640
  this.emit('detector-loaded', {
1651
1641
  success: false,
1652
1642
  error: errorMsg
@@ -1657,21 +1647,14 @@
1657
1647
  });
1658
1648
  return;
1659
1649
  }
1660
- this.emitDebug('initialization', 'Human.js loaded successfully', {
1661
- loadTime: `${humanLoadTime.toFixed(2)}ms`,
1662
- version: this.human.version
1663
- });
1664
- console.log('[FaceDetectionEngine] Human.js loaded successfully', {
1665
- loadTime: `${humanLoadTime.toFixed(2)}ms`,
1666
- version: this.human.version
1667
- });
1668
1650
  this.isReady = true;
1669
1651
  const loadedData = {
1670
1652
  success: true,
1671
- opencv_version: cv?.getBuildInformation?.() || 'unknown',
1653
+ opencv_version: getOpenCVVersion(),
1672
1654
  human_version: this.human.version
1673
1655
  };
1674
1656
  console.log('[FaceDetectionEngine] Engine initialized and ready', {
1657
+ initCostTime: (performance.now() - startLoadTime).toFixed(2),
1675
1658
  opencv_version: loadedData.opencv_version,
1676
1659
  human_version: loadedData.human_version
1677
1660
  });
@@ -2444,6 +2427,7 @@
2444
2427
  exports.FaceDetectionEngine = FaceDetectionEngine;
2445
2428
  exports.default = FaceDetectionEngine;
2446
2429
  exports.getCvSync = getCvSync;
2430
+ exports.getOpenCVVersion = getOpenCVVersion;
2447
2431
  exports.loadOpenCV = loadOpenCV;
2448
2432
  exports.preloadOpenCV = preloadOpenCV;
2449
2433