@sssxyd/face-liveness-detector 0.2.25 → 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,38 +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
- const cv = getCvSync();
494
- if (cv) {
495
- return { cv };
496
- }
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;
497
478
  }
498
- // 等待初始化
499
- console.log('[loadOpenCV] Waiting for initialization...');
500
- if (!opencvInitPromise) {
501
- console.log('[loadOpenCV] Starting new initialization');
479
+ else {
480
+ // 开始新的初始化
481
+ console.log('[FaceDetectionEngine] Starting OpenCV initialization...');
502
482
  opencvInitPromise = _initializeOpenCV(timeout);
483
+ try {
484
+ cv = await opencvInitPromise;
485
+ }
486
+ catch (error) {
487
+ // 失败后清除 Promise,允许重试
488
+ opencvInitPromise = null;
489
+ throw error;
490
+ }
503
491
  }
504
- console.log('[loadOpenCV] Awaiting opencvInitPromise');
505
- const initTime = await opencvInitPromise;
506
- // 获取初始化后的 cv 对象
507
- const cv = getCvSync();
508
- if (!cv) {
509
- console.error('[loadOpenCV] getCvSync returned null');
510
- 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)');
511
500
  }
512
- console.log('[loadOpenCV] OpenCV.js load successfully', {
513
- initTime: `${initTime.toFixed(2)}ms`,
514
- version: getOpenCVVersion()
515
- });
516
- return cv;
501
+ console.log('[FaceDetectionEngine] OpenCV.js loaded successfully');
502
+ return { cv };
517
503
  }
518
504
  catch (error) {
519
- console.error('[loadOpenCV] OpenCV.js load failed', error);
520
- opencvInitPromise = null; // 失败时清除 Promise,允许重试
505
+ console.error('[FaceDetectionEngine] Failed to load OpenCV.js:', error);
521
506
  throw error;
522
507
  }
523
508
  }
@@ -536,35 +521,6 @@
536
521
  }
537
522
  return null;
538
523
  }
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
- }
568
524
  /**
569
525
  * Load Human.js
570
526
  * @param modelPath - Path to model files (optional)
@@ -572,7 +528,6 @@
572
528
  * @returns Promise that resolves with Human instance
573
529
  */
574
530
  async function loadHuman(modelPath, wasmPath) {
575
- const initStartTime = performance.now();
576
531
  const config = {
577
532
  backend: _detectOptimalBackend(),
578
533
  face: {
@@ -595,16 +550,24 @@
595
550
  if (wasmPath) {
596
551
  config.wasmPath = wasmPath;
597
552
  }
598
- console.log('[loadHuman] Config:', {
553
+ console.log('[FaceDetectionEngine] Human.js config:', {
599
554
  backend: config.backend,
600
555
  modelBasePath: config.modelBasePath || '(using default)',
601
556
  wasmPath: config.wasmPath || '(using default)'
602
557
  });
558
+ const initStartTime = performance.now();
559
+ console.log('[FaceDetectionEngine] Creating Human instance...');
603
560
  const human = new Human(config);
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
566
  await human.load();
567
+ const loadTime = performance.now() - modelLoadStartTime;
606
568
  const totalTime = performance.now() - initStartTime;
607
- console.log('[loadHuman] Loaded successfully', {
569
+ console.log('[FaceDetectionEngine] Human.js loaded successfully', {
570
+ modelLoadTime: `${loadTime.toFixed(2)}ms`,
608
571
  totalInitTime: `${totalTime.toFixed(2)}ms`,
609
572
  version: human.version
610
573
  });
@@ -612,40 +575,37 @@
612
575
  }
613
576
  catch (error) {
614
577
  const errorMsg = error instanceof Error ? error.message : 'Unknown error';
615
- console.error('[loadHuman] Load failed:', errorMsg);
578
+ console.error('[FaceDetectionEngine] Human.js load failed:', errorMsg);
616
579
  throw error;
617
580
  }
618
581
  }
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
- }
582
+ /**
583
+ * Extract OpenCV version from getBuildInformation
584
+ * @returns version string like "4.12.0"
585
+ */
586
+ function getOpenCVVersion() {
625
587
  try {
626
- const cv = await loadOpenCV(timeout);
627
- if (!cv) {
628
- throw new Error('Failed to load OpenCV');
588
+ const cv = getCvSync();
589
+ if (!cv || !cv.getBuildInformation) {
590
+ return 'unknown';
629
591
  }
630
- const human = await loadHuman(modelPath, wasmPath);
631
- if (!human) {
632
- throw new Error('Failed to load Human');
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];
633
598
  }
634
- const totalTime = performance.now() - startTime;
635
- console.log('[loadLibraries] libraries loaded successfully', {
636
- totalInitTime: `${totalTime.toFixed(2)}ms`,
637
- opencvVersion: getOpenCVVersion(),
638
- humanVersion: human.version
639
- });
640
- return {
641
- cv,
642
- human
643
- };
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';
644
605
  }
645
606
  catch (error) {
646
- const errorMsg = error instanceof Error ? error.message : 'Unknown error';
647
- console.error('[loadLibraries] Failed to load libraries:', errorMsg);
648
- throw error;
607
+ console.error('[getOpenCVVersion] Failed to get version:', error);
608
+ return 'unknown';
649
609
  }
650
610
  }
651
611
 
@@ -1626,17 +1586,66 @@
1626
1586
  this.isInitializing = true;
1627
1587
  this.emitDebug('initialization', 'Starting to load detection libraries...');
1628
1588
  try {
1629
- // Load Libraries
1630
- this.emitDebug('initialization', 'Loading Libraries...');
1631
- const { human } = await loadLibraries(this.config.human_model_path, this.config.tensorflow_wasm_path, 30000);
1632
- // Store human instance
1633
- this.human = human;
1589
+ // Load OpenCV
1590
+ this.emitDebug('initialization', 'Loading OpenCV...');
1591
+ const { cv } = await loadOpenCV(60000); // 1 minute timeout
1592
+ if (!cv || !cv.Mat) {
1593
+ console.log('[FaceDetectionEngine] Failed to load OpenCV.js: module is null or invalid');
1594
+ this.emit('detector-error', {
1595
+ success: false,
1596
+ error: 'Failed to load OpenCV.js: module is null or invalid'
1597
+ });
1598
+ this.emit('detector-error', {
1599
+ code: exports.ErrorCode.DETECTOR_NOT_INITIALIZED,
1600
+ message: 'Failed to load OpenCV.js: module is null or invalid'
1601
+ });
1602
+ return;
1603
+ }
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) {
1618
+ const errorMsg = 'Failed to load Human.js: instance is null';
1619
+ console.log('[FaceDetectionEngine] ' + errorMsg);
1620
+ this.emitDebug('initialization', errorMsg, { loadTime: humanLoadTime }, 'error');
1621
+ this.emit('detector-loaded', {
1622
+ success: false,
1623
+ error: errorMsg
1624
+ });
1625
+ this.emit('detector-error', {
1626
+ code: exports.ErrorCode.DETECTOR_NOT_INITIALIZED,
1627
+ message: errorMsg
1628
+ });
1629
+ return;
1630
+ }
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
+ });
1634
1639
  this.isReady = true;
1635
1640
  const loadedData = {
1636
1641
  success: true,
1637
- opencv_version: getOpenCVVersion(),
1642
+ opencv_version: cv_version,
1638
1643
  human_version: this.human.version
1639
1644
  };
1645
+ console.log('[FaceDetectionEngine] Engine initialized and ready', {
1646
+ opencv_version: loadedData.opencv_version,
1647
+ human_version: loadedData.human_version
1648
+ });
1640
1649
  this.emit('detector-loaded', loadedData);
1641
1650
  this.emitDebug('initialization', 'Engine initialized and ready', loadedData);
1642
1651
  }