@sssxyd/face-liveness-detector 0.2.27 → 0.2.28

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`);
377
+ console.log(`[FaceDetectionEngine] OpenCV.js already initialized, took ${initTime.toFixed(2)}ms`);
386
378
  return cvModule;
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,63 @@
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
- let pollInterval = null;
407
- // 防止多次调用 resolve/reject
408
- let finished = false;
409
397
  const timeoutId = setTimeout(() => {
410
- console.error('OpenCV.js initialization timeout after ' + timeout + 'ms');
411
- finished = true;
412
- if (pollInterval) {
413
- clearInterval(pollInterval);
414
- }
398
+ console.error('[FaceDetectionEngine] OpenCV.js initialization timeout after ' + timeout + 'ms');
415
399
  reject(new Error('OpenCV.js initialization timeout'));
416
400
  }, timeout);
401
+ let resolved = false;
417
402
  const resolveOnce = (source) => {
418
- if (finished) {
419
- console.log('[resolveOnce] Already finished, ignoring call from:', source);
403
+ if (resolved)
420
404
  return;
421
- }
422
- finished = true;
423
- console.log('[resolveOnce] Marking as finished');
424
- // 立即停止所有定时器和轮询
405
+ resolved = true;
425
406
  clearTimeout(timeoutId);
426
- if (pollInterval !== null) {
427
- clearInterval(pollInterval);
428
- pollInterval = null;
429
- console.log('[resolveOnce] Poll interval cleared');
430
- }
407
+ clearInterval(pollInterval);
431
408
  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);
409
+ console.log(`[FaceDetectionEngine] OpenCV.js initialized (${source}), took ${initTime.toFixed(2)}ms`);
410
+ resolve(cvModule);
437
411
  };
438
412
  // 尝试设置回调(只有在 cvModule 可扩展时才尝试)
439
413
  const canSetCallback = cvModule && Object.isExtensible(cvModule);
440
414
  if (canSetCallback) {
441
415
  try {
442
416
  const originalOnRuntimeInitialized = cvModule.onRuntimeInitialized(cvModule).onRuntimeInitialized = () => {
443
- console.log('[onRuntimeInitialized] callback triggered');
417
+ console.log('[FaceDetectionEngine] onRuntimeInitialized callback triggered');
444
418
  // 调用原始回调(如果存在)
445
419
  if (originalOnRuntimeInitialized && typeof originalOnRuntimeInitialized === 'function') {
446
420
  try {
447
421
  originalOnRuntimeInitialized();
448
422
  }
449
423
  catch (e) {
450
- console.warn('[onRuntimeInitialized] callback failed:', e);
424
+ console.warn('[FaceDetectionEngine] Original onRuntimeInitialized callback failed:', e);
451
425
  }
452
426
  }
453
427
  resolveOnce('callback');
454
428
  };
455
- console.log('[onRuntimeInitialized] callback set successfully');
429
+ console.log('[FaceDetectionEngine] onRuntimeInitialized callback set successfully');
456
430
  }
457
431
  catch (e) {
458
- console.warn('[onRuntimeInitialized] Failed to set callback, will use polling:', e);
432
+ console.warn('[FaceDetectionEngine] Failed to set onRuntimeInitialized callback, will use polling:', e);
459
433
  }
460
434
  }
461
435
  else {
462
- console.log('[polling] cvModule is not extensible, using polling mode');
436
+ console.log('[FaceDetectionEngine] cvModule is not extensible, using polling mode');
463
437
  }
464
438
  // 启动轮询作为备用方案或主要方案
465
- pollInterval = setInterval(() => {
466
- console.log('[polling] Checking for Mat class...');
439
+ const pollInterval = setInterval(() => {
467
440
  // 优先检查 cvModule 中是否有 Mat
468
441
  if (cvModule.Mat) {
469
- console.log('[polling] Found Mat in cvModule');
470
442
  resolveOnce('cvModule polling');
471
443
  return;
472
444
  }
473
445
  // 其次检查 globalThis.cv 中是否有 Mat
474
446
  if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
475
- console.log('[polling] Found Mat in globalThis.cv');
476
447
  cvModule = globalThis.cv;
477
448
  resolveOnce('globalThis.cv polling');
478
449
  return;
@@ -486,35 +457,52 @@
486
457
  * @returns Promise that resolves with cv module
487
458
  */
488
459
  async function loadOpenCV(timeout = 30000) {
460
+ let cv;
461
+ console.log('[FaceDetectionEngine] Loading OpenCV.js...');
489
462
  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');
463
+ // 如果已经在初始化中,复用现有的 Promise
464
+ if (opencvInitPromise) {
465
+ console.log('[FaceDetectionEngine] OpenCV initialization in progress, waiting...');
466
+ cv = await opencvInitPromise;
467
+ }
468
+ else if (cvModule.Mat) {
469
+ // 检查 cvModule 是否已经初始化
470
+ console.log('[FaceDetectionEngine] OpenCV.js already initialized');
471
+ cv = cvModule;
472
+ }
473
+ else if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
474
+ // 检查全局 cv 是否已经初始化
475
+ console.log('[FaceDetectionEngine] OpenCV.js already initialized (global)');
476
+ cvModule = globalThis.cv;
477
+ cv = cvModule;
478
+ }
479
+ else {
480
+ // 开始新的初始化
481
+ console.log('[FaceDetectionEngine] Starting OpenCV initialization...');
499
482
  opencvInitPromise = _initializeOpenCV(timeout);
483
+ try {
484
+ cv = await opencvInitPromise;
485
+ }
486
+ catch (error) {
487
+ // 失败后清除 Promise,允许重试
488
+ opencvInitPromise = null;
489
+ throw error;
490
+ }
500
491
  }
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');
492
+ // 最终验证
493
+ if (!cv || !cv.Mat) {
494
+ console.error('[FaceDetectionEngine] OpenCV module is invalid:', {
495
+ hasMat: cv && cv.Mat,
496
+ type: typeof cv,
497
+ keys: cv ? Object.keys(cv).slice(0, 10) : 'N/A'
498
+ });
499
+ throw new Error('OpenCV.js loaded but module is invalid (no Mat class found)');
508
500
  }
509
- console.log('[loadOpenCV] OpenCV.js load successfully', {
510
- initTime: `${initTime.toFixed(2)}ms`,
511
- version: getOpenCVVersion()
512
- });
513
- return cv;
501
+ console.log('[FaceDetectionEngine] OpenCV.js loaded successfully');
502
+ return { cv };
514
503
  }
515
504
  catch (error) {
516
- console.error('[loadOpenCV] OpenCV.js load failed', error);
517
- opencvInitPromise = null; // 失败时清除 Promise,允许重试
505
+ console.error('[FaceDetectionEngine] Failed to load OpenCV.js:', error);
518
506
  throw error;
519
507
  }
520
508
  }
@@ -533,35 +521,6 @@
533
521
  }
534
522
  return null;
535
523
  }
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
524
  /**
566
525
  * Load Human.js
567
526
  * @param modelPath - Path to model files (optional)
@@ -569,8 +528,6 @@
569
528
  * @returns Promise that resolves with Human instance
570
529
  */
571
530
  async function loadHuman(modelPath, wasmPath) {
572
- console.log('[loadHuman] START - creating config');
573
- const initStartTime = performance.now();
574
531
  const config = {
575
532
  backend: _detectOptimalBackend(),
576
533
  face: {
@@ -593,20 +550,24 @@
593
550
  if (wasmPath) {
594
551
  config.wasmPath = wasmPath;
595
552
  }
596
- console.log('[loadHuman] Config:', {
553
+ console.log('[FaceDetectionEngine] Human.js config:', {
597
554
  backend: config.backend,
598
555
  modelBasePath: config.modelBasePath || '(using default)',
599
556
  wasmPath: config.wasmPath || '(using default)'
600
557
  });
601
- console.log('[loadHuman] Creating Human instance...');
558
+ const initStartTime = performance.now();
559
+ console.log('[FaceDetectionEngine] Creating Human instance...');
602
560
  const human = new Human(config);
603
- console.log('[loadHuman] Human instance created, starting load...');
561
+ const instanceCreateTime = performance.now() - initStartTime;
562
+ console.log(`[FaceDetectionEngine] Human instance created, took ${instanceCreateTime.toFixed(2)}ms`);
563
+ console.log('[FaceDetectionEngine] Loading Human.js models...');
564
+ const modelLoadStartTime = performance.now();
604
565
  try {
605
- console.log('[loadHuman] Calling human.load()...');
606
566
  await human.load();
607
- console.log('[loadHuman] human.load() completed');
567
+ const loadTime = performance.now() - modelLoadStartTime;
608
568
  const totalTime = performance.now() - initStartTime;
609
- console.log('[loadHuman] Loaded successfully', {
569
+ console.log('[FaceDetectionEngine] Human.js loaded successfully', {
570
+ modelLoadTime: `${loadTime.toFixed(2)}ms`,
610
571
  totalInitTime: `${totalTime.toFixed(2)}ms`,
611
572
  version: human.version
612
573
  });
@@ -614,33 +575,37 @@
614
575
  }
615
576
  catch (error) {
616
577
  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');
578
+ console.error('[FaceDetectionEngine] Human.js load failed:', errorMsg);
619
579
  throw error;
620
580
  }
621
581
  }
622
- async function loadLibraries(modelPath, wasmPath, timeout = 30000) {
623
- console.log('[loadLibraries] Starting parallel load of OpenCV and Human...');
624
- const startTime = performance.now();
582
+ /**
583
+ * Extract OpenCV version from getBuildInformation
584
+ * @returns version string like "4.12.0"
585
+ */
586
+ function getOpenCVVersion() {
625
587
  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 };
588
+ const cv = getCvSync();
589
+ if (!cv || !cv.getBuildInformation) {
590
+ return 'unknown';
591
+ }
592
+ const buildInfo = cv.getBuildInformation();
593
+ // 查找 "Version control:" 或 "OpenCV" 开头的行
594
+ // 格式: "Version control: 4.12.0"
595
+ const versionMatch = buildInfo.match(/Version\s+control:\s+(\d+\.\d+\.\d+)/i);
596
+ if (versionMatch && versionMatch[1]) {
597
+ return versionMatch[1];
598
+ }
599
+ // 备用方案:查找 "OpenCV X.X.X" 格式
600
+ const opencvMatch = buildInfo.match(/OpenCV\s+(\d+\.\d+\.\d+)/i);
601
+ if (opencvMatch && opencvMatch[1]) {
602
+ return opencvMatch[1];
603
+ }
604
+ return 'unknown';
639
605
  }
640
606
  catch (error) {
641
- const errorMsg = error instanceof Error ? error.message : 'Unknown error';
642
- console.error('[loadLibraries] Failed to load libraries:', errorMsg);
643
- throw error;
607
+ console.error('[getOpenCVVersion] Failed to get version:', error);
608
+ return 'unknown';
644
609
  }
645
610
  }
646
611
 
@@ -1621,13 +1586,12 @@
1621
1586
  this.isInitializing = true;
1622
1587
  this.emitDebug('initialization', 'Starting to load detection libraries...');
1623
1588
  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);
1589
+ // Load OpenCV
1590
+ this.emitDebug('initialization', 'Loading OpenCV...');
1591
+ const { cv } = await loadOpenCV(60000); // 1 minute timeout
1628
1592
  if (!cv || !cv.Mat) {
1629
1593
  console.log('[FaceDetectionEngine] Failed to load OpenCV.js: module is null or invalid');
1630
- this.emit('detector-loaded', {
1594
+ this.emit('detector-error', {
1631
1595
  success: false,
1632
1596
  error: 'Failed to load OpenCV.js: module is null or invalid'
1633
1597
  });
@@ -1637,9 +1601,23 @@
1637
1601
  });
1638
1602
  return;
1639
1603
  }
1640
- if (!human) {
1604
+ const cv_version = getOpenCVVersion();
1605
+ this.emitDebug('initialization', 'OpenCV loaded successfully', {
1606
+ version: cv_version
1607
+ });
1608
+ console.log('[FaceDetectionEngine] OpenCV loaded successfully', {
1609
+ version: cv_version
1610
+ });
1611
+ // Load Human.js
1612
+ console.log('[FaceDetectionEngine] Loading Human.js models...');
1613
+ this.emitDebug('initialization', 'Loading Human.js...');
1614
+ const humanStartTime = performance.now();
1615
+ this.human = await loadHuman(this.config.human_model_path, this.config.tensorflow_wasm_path);
1616
+ const humanLoadTime = performance.now() - humanStartTime;
1617
+ if (!this.human) {
1641
1618
  const errorMsg = 'Failed to load Human.js: instance is null';
1642
1619
  console.log('[FaceDetectionEngine] ' + errorMsg);
1620
+ this.emitDebug('initialization', errorMsg, { loadTime: humanLoadTime }, 'error');
1643
1621
  this.emit('detector-loaded', {
1644
1622
  success: false,
1645
1623
  error: errorMsg
@@ -1650,16 +1628,21 @@
1650
1628
  });
1651
1629
  return;
1652
1630
  }
1653
- // Store human instance
1654
- this.human = human;
1631
+ this.emitDebug('initialization', 'Human.js loaded successfully', {
1632
+ loadTime: `${humanLoadTime.toFixed(2)}ms`,
1633
+ version: this.human.version
1634
+ });
1635
+ console.log('[FaceDetectionEngine] Human.js loaded successfully', {
1636
+ loadTime: `${humanLoadTime.toFixed(2)}ms`,
1637
+ version: this.human.version
1638
+ });
1655
1639
  this.isReady = true;
1656
1640
  const loadedData = {
1657
1641
  success: true,
1658
- opencv_version: getOpenCVVersion(),
1642
+ opencv_version: cv_version,
1659
1643
  human_version: this.human.version
1660
1644
  };
1661
1645
  console.log('[FaceDetectionEngine] Engine initialized and ready', {
1662
- initCostTime: (performance.now() - startLoadTime).toFixed(2),
1663
1646
  opencv_version: loadedData.opencv_version,
1664
1647
  human_version: loadedData.human_version
1665
1648
  });