@sssxyd/face-liveness-detector 0.2.27 → 0.2.29

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