@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.esm.js CHANGED
@@ -289,6 +289,7 @@ if (cvModuleImport && cvModuleImport.default) {
289
289
  }
290
290
  let webglAvailableCache = null;
291
291
  let opencvInitPromise = null;
292
+ let opencvInitialized = false; // 标记是否已初始化完成
292
293
  function _isWebGLAvailable() {
293
294
  if (webglAvailableCache !== null) {
294
295
  return webglAvailableCache;
@@ -332,8 +333,8 @@ async function preloadOpenCV(timeout = 30000) {
332
333
  // 复用 loadOpenCV 的初始化逻辑
333
334
  opencvInitPromise = _initializeOpenCV(timeout);
334
335
  try {
335
- await opencvInitPromise;
336
- console.log('[OpenCV] Preload completed successfully');
336
+ const initTime = await opencvInitPromise;
337
+ console.log('[OpenCV] Preload completed successfully, cost:', initTime.toFixed(0), 'ms');
337
338
  }
338
339
  catch (error) {
339
340
  console.error('[OpenCV] Preload failed:', error);
@@ -350,16 +351,16 @@ async function preloadOpenCV(timeout = 30000) {
350
351
  */
351
352
  async function _initializeOpenCV(timeout) {
352
353
  const initStartTime = performance.now();
353
- console.log('[FaceDetectionEngine] Waiting for OpenCV WASM initialization...');
354
+ console.log('Waiting for OpenCV WASM initialization...');
354
355
  // 快速路径:检查是否已经初始化
355
356
  if (cvModule.Mat) {
356
357
  const initTime = performance.now() - initStartTime;
357
- console.log(`[FaceDetectionEngine] OpenCV.js already initialized, took ${initTime.toFixed(2)}ms`);
358
+ console.log(`OpenCV.js already initialized, took ${initTime.toFixed(2)}ms`);
358
359
  return cvModule;
359
360
  }
360
361
  if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
361
362
  const initTime = performance.now() - initStartTime;
362
- console.log(`[FaceDetectionEngine] OpenCV.js already initialized (from global), took ${initTime.toFixed(2)}ms`);
363
+ console.log(`OpenCV.js already initialized (from global), took ${initTime.toFixed(2)}ms`);
363
364
  cvModule = globalThis.cv;
364
365
  return cvModule;
365
366
  }
@@ -367,10 +368,10 @@ async function _initializeOpenCV(timeout) {
367
368
  if (typeof globalThis !== 'undefined' && !globalThis.cv) {
368
369
  if (cvModule && Object.isExtensible(cvModule)) {
369
370
  globalThis.cv = cvModule;
370
- console.log('[FaceDetectionEngine] cvModule assigned to globalThis.cv');
371
+ console.log('cvModule assigned to globalThis.cv');
371
372
  }
372
373
  else {
373
- console.log('[FaceDetectionEngine] cvModule is not extensible or globalThis already has cv');
374
+ console.log('cvModule is not extensible or globalThis already has cv');
374
375
  }
375
376
  }
376
377
  return new Promise((resolve, reject) => {
@@ -379,7 +380,7 @@ async function _initializeOpenCV(timeout) {
379
380
  // 防止多次调用 resolve/reject
380
381
  let finished = false;
381
382
  const timeoutId = setTimeout(() => {
382
- console.error('[FaceDetectionEngine] OpenCV.js initialization timeout after ' + timeout + 'ms');
383
+ console.error('OpenCV.js initialization timeout after ' + timeout + 'ms');
383
384
  finished = true;
384
385
  if (pollInterval) {
385
386
  clearInterval(pollInterval);
@@ -388,70 +389,65 @@ async function _initializeOpenCV(timeout) {
388
389
  }, timeout);
389
390
  const resolveOnce = (source) => {
390
391
  if (finished) {
391
- console.log('[FaceDetectionEngine] [resolveOnce] Already finished, ignoring call from:', source);
392
+ console.log('[resolveOnce] Already finished, ignoring call from:', source);
392
393
  return;
393
394
  }
394
395
  finished = true;
395
- console.log('[FaceDetectionEngine] [resolveOnce] Marking as finished');
396
+ console.log('[resolveOnce] Marking as finished');
396
397
  // 立即停止所有定时器和轮询
397
398
  clearTimeout(timeoutId);
398
399
  if (pollInterval !== null) {
399
400
  clearInterval(pollInterval);
400
401
  pollInterval = null;
401
- console.log('[FaceDetectionEngine] [resolveOnce] Poll interval cleared:', pollInterval);
402
- }
403
- // 确保返回有效的 cv 对象
404
- let validCv = cvModule;
405
- if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
406
- validCv = globalThis.cv;
402
+ console.log('[resolveOnce] Poll interval cleared');
407
403
  }
408
404
  const initTime = performance.now() - initStartTime;
409
- console.log(`[FaceDetectionEngine] OpenCV.js initialized (${source}), took ${initTime.toFixed(2)}ms`, {
410
- hasMat: !!(validCv && validCv.Mat),
411
- source: source
412
- });
413
- console.log('[FaceDetectionEngine] [resolveOnce] About to call resolve');
414
- resolve(validCv);
415
- console.log('[FaceDetectionEngine] [resolveOnce] resolve called');
405
+ console.log(`OpenCV.js initialized (${source}), took ${initTime.toFixed(2)}ms`);
406
+ // 标记初始化完成
407
+ opencvInitialized = true;
408
+ // 返回简单的标记,实际的 cv 对象已经在 globalThis.cv 上了
409
+ // 通过 getCvSync() 获取
410
+ resolve(initTime);
411
+ console.log('[resolveOnce] Promise resolved');
416
412
  };
417
413
  // 尝试设置回调(只有在 cvModule 可扩展时才尝试)
418
414
  const canSetCallback = cvModule && Object.isExtensible(cvModule);
419
415
  if (canSetCallback) {
420
416
  try {
421
417
  const originalOnRuntimeInitialized = cvModule.onRuntimeInitialized(cvModule).onRuntimeInitialized = () => {
422
- console.log('[FaceDetectionEngine] onRuntimeInitialized callback triggered');
418
+ console.log('[onRuntimeInitialized] callback triggered');
423
419
  // 调用原始回调(如果存在)
424
420
  if (originalOnRuntimeInitialized && typeof originalOnRuntimeInitialized === 'function') {
425
421
  try {
426
422
  originalOnRuntimeInitialized();
427
423
  }
428
424
  catch (e) {
429
- console.warn('[FaceDetectionEngine] Original onRuntimeInitialized callback failed:', e);
425
+ console.warn('[onRuntimeInitialized] callback failed:', e);
430
426
  }
431
427
  }
432
428
  resolveOnce('callback');
433
429
  };
434
- console.log('[FaceDetectionEngine] onRuntimeInitialized callback set successfully');
430
+ console.log('[onRuntimeInitialized] callback set successfully');
435
431
  }
436
432
  catch (e) {
437
- console.warn('[FaceDetectionEngine] Failed to set onRuntimeInitialized callback, will use polling:', e);
433
+ console.warn('[onRuntimeInitialized] Failed to set callback, will use polling:', e);
438
434
  }
439
435
  }
440
436
  else {
441
- console.log('[FaceDetectionEngine] cvModule is not extensible, using polling mode');
437
+ console.log('[polling] cvModule is not extensible, using polling mode');
442
438
  }
443
439
  // 启动轮询作为备用方案或主要方案
444
440
  pollInterval = setInterval(() => {
445
- console.log('[FaceDetectionEngine] [polling] Checking for Mat class...');
441
+ console.log('[polling] Checking for Mat class...');
446
442
  // 优先检查 cvModule 中是否有 Mat
447
443
  if (cvModule.Mat) {
448
- console.log('[FaceDetectionEngine] [polling] Found Mat in cvModule');
444
+ console.log('[polling] Found Mat in cvModule');
449
445
  resolveOnce('cvModule polling');
450
446
  return;
451
447
  }
452
448
  // 其次检查 globalThis.cv 中是否有 Mat
453
449
  if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
454
- console.log('[FaceDetectionEngine] [polling] Found Mat in globalThis.cv');
450
+ console.log('[polling] Found Mat in globalThis.cv');
455
451
  cvModule = globalThis.cv;
456
452
  resolveOnce('globalThis.cv polling');
457
453
  return;
@@ -465,81 +461,40 @@ async function _initializeOpenCV(timeout) {
465
461
  * @returns Promise that resolves with cv module
466
462
  */
467
463
  async function loadOpenCV(timeout = 30000) {
468
- let cv;
469
- console.log('[FaceDetectionEngine] Loading OpenCV.js...');
470
464
  try {
471
- console.log('[FaceDetectionEngine] [loadOpenCV] Starting, checking conditions...');
472
- // 如果已经在初始化中,复用现有的 Promise
473
- if (opencvInitPromise) {
474
- console.log('[FaceDetectionEngine] [loadOpenCV] opencvInitPromise exists, awaiting...');
475
- console.log('[FaceDetectionEngine] [loadOpenCV] Before await, Promise state:', opencvInitPromise.constructor.name);
476
- try {
477
- cv = await opencvInitPromise;
478
- console.log('[FaceDetectionEngine] [loadOpenCV] AFTER AWAIT - OpenCV promise resolved, got cv module:', { hasMat: !!(cv && cv.Mat), type: typeof cv });
479
- }
480
- catch (awaitError) {
481
- console.error('[FaceDetectionEngine] [loadOpenCV] AWAIT ERROR:', awaitError);
482
- throw awaitError;
465
+ // 快速检查是否已经初始化完成
466
+ if (opencvInitialized) {
467
+ console.log('[loadOpenCV] Already initialized, returning immediately');
468
+ const cv = getCvSync();
469
+ if (cv) {
470
+ return { cv };
483
471
  }
484
- // 完成后清除 Promise 标记
485
- console.log('[FaceDetectionEngine] [loadOpenCV] Clearing opencvInitPromise');
486
- opencvInitPromise = null;
487
- console.log('[FaceDetectionEngine] [loadOpenCV] opencvInitPromise cleared');
488
- }
489
- else if (cvModule.Mat) {
490
- // 检查 cvModule 是否已经初始化
491
- console.log('[FaceDetectionEngine] [loadOpenCV] OpenCV.js already initialized in cvModule');
492
- cv = cvModule;
493
- }
494
- else if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
495
- // 检查全局 cv 是否已经初始化
496
- console.log('[FaceDetectionEngine] [loadOpenCV] OpenCV.js already initialized in globalThis.cv');
497
- cvModule = globalThis.cv;
498
- cv = cvModule;
499
472
  }
500
- else {
501
- // 开始新的初始化
502
- console.log('[FaceDetectionEngine] [loadOpenCV] Starting new OpenCV initialization...');
473
+ // 等待初始化
474
+ console.log('[loadOpenCV] Waiting for initialization...');
475
+ if (!opencvInitPromise) {
476
+ console.log('[loadOpenCV] Starting new initialization');
503
477
  opencvInitPromise = _initializeOpenCV(timeout);
504
- try {
505
- console.log('[FaceDetectionEngine] [loadOpenCV] Awaiting _initializeOpenCV...');
506
- cv = await opencvInitPromise;
507
- console.log('[FaceDetectionEngine] [loadOpenCV] OpenCV initialization completed, got cv module:', { hasMat: !!(cv && cv.Mat), type: typeof cv });
508
- // 完成后清除 Promise 标记
509
- opencvInitPromise = null;
510
- }
511
- catch (error) {
512
- // 失败后也要清除 Promise
513
- console.error('[FaceDetectionEngine] [loadOpenCV] _initializeOpenCV threw error:', error);
514
- opencvInitPromise = null;
515
- throw error;
516
- }
517
478
  }
518
- console.log('[FaceDetectionEngine] [loadOpenCV] After await, validating cv module...');
519
- // 最终验证 - 如果 cv 无效,尝试从 globalThis 获取
520
- if (!cv || !cv.Mat) {
521
- console.warn('[FaceDetectionEngine] [loadOpenCV] Returned cv module is invalid, attempting to get from globalThis.cv...');
522
- if (typeof globalThis !== 'undefined' && globalThis.cv && globalThis.cv.Mat) {
523
- console.log('[FaceDetectionEngine] [loadOpenCV] Found valid cv in globalThis.cv, using that');
524
- cv = globalThis.cv;
525
- }
526
- else {
527
- console.error('[FaceDetectionEngine] [loadOpenCV] OpenCV module is invalid:', {
528
- hasMat: cv && cv.Mat,
529
- type: typeof cv,
530
- keys: cv ? Object.keys(cv).slice(0, 10) : 'N/A',
531
- globalCvAvailable: !!(typeof globalThis !== 'undefined' && globalThis.cv)
532
- });
533
- throw new Error('OpenCV.js loaded but module is invalid (no Mat class found)');
534
- }
479
+ console.log('[loadOpenCV] Awaiting opencvInitPromise');
480
+ const initTime = await opencvInitPromise;
481
+ // 清除 Promise 缓存
482
+ opencvInitPromise = null;
483
+ // 获取初始化后的 cv 对象
484
+ const cv = getCvSync();
485
+ if (!cv) {
486
+ console.error('[loadOpenCV] getCvSync returned null');
487
+ throw new Error('OpenCV module is invalid');
535
488
  }
536
- console.log('[FaceDetectionEngine] [loadOpenCV] Validation passed, returning cv module');
537
- console.log('[FaceDetectionEngine] OpenCV.js loaded successfully');
538
- return { cv };
489
+ console.log('[loadOpenCV] OpenCV.js load successfully', {
490
+ initTime: `${initTime.toFixed(2)}ms`,
491
+ version: getOpenCVVersion()
492
+ });
493
+ return cv;
539
494
  }
540
495
  catch (error) {
541
- console.error('[FaceDetectionEngine] [loadOpenCV] Exception caught:', error);
542
- console.error('[FaceDetectionEngine] Failed to load OpenCV.js:', error);
496
+ console.error('[loadOpenCV] OpenCV.js load failed', error);
497
+ opencvInitPromise = null;
543
498
  throw error;
544
499
  }
545
500
  }
@@ -558,6 +513,35 @@ function getCvSync() {
558
513
  }
559
514
  return null;
560
515
  }
516
+ /**
517
+ * Extract OpenCV version from getBuildInformation
518
+ * @returns version string like "4.12.0"
519
+ */
520
+ function getOpenCVVersion() {
521
+ try {
522
+ const cv = getCvSync();
523
+ if (!cv || !cv.getBuildInformation) {
524
+ return 'unknown';
525
+ }
526
+ const buildInfo = cv.getBuildInformation();
527
+ // 查找 "Version control:" 或 "OpenCV" 开头的行
528
+ // 格式: "Version control: 4.12.0"
529
+ const versionMatch = buildInfo.match(/Version\s+control:\s+(\d+\.\d+\.\d+)/i);
530
+ if (versionMatch && versionMatch[1]) {
531
+ return versionMatch[1];
532
+ }
533
+ // 备用方案:查找 "OpenCV X.X.X" 格式
534
+ const opencvMatch = buildInfo.match(/OpenCV\s+(\d+\.\d+\.\d+)/i);
535
+ if (opencvMatch && opencvMatch[1]) {
536
+ return opencvMatch[1];
537
+ }
538
+ return 'unknown';
539
+ }
540
+ catch (error) {
541
+ console.error('[getOpenCVVersion] Failed to get version:', error);
542
+ return 'unknown';
543
+ }
544
+ }
561
545
  /**
562
546
  * Load Human.js
563
547
  * @param modelPath - Path to model files (optional)
@@ -587,24 +571,17 @@ async function loadHuman(modelPath, wasmPath) {
587
571
  if (wasmPath) {
588
572
  config.wasmPath = wasmPath;
589
573
  }
590
- console.log('[FaceDetectionEngine] Human.js config:', {
574
+ console.log('[loadHuman] Config:', {
591
575
  backend: config.backend,
592
576
  modelBasePath: config.modelBasePath || '(using default)',
593
577
  wasmPath: config.wasmPath || '(using default)'
594
578
  });
595
579
  const initStartTime = performance.now();
596
- console.log('[FaceDetectionEngine] Creating Human instance...');
597
580
  const human = new Human(config);
598
- const instanceCreateTime = performance.now() - initStartTime;
599
- console.log(`[FaceDetectionEngine] Human instance created, took ${instanceCreateTime.toFixed(2)}ms`);
600
- console.log('[FaceDetectionEngine] Loading Human.js models...');
601
- const modelLoadStartTime = performance.now();
602
581
  try {
603
582
  await human.load();
604
- const loadTime = performance.now() - modelLoadStartTime;
605
583
  const totalTime = performance.now() - initStartTime;
606
- console.log('[FaceDetectionEngine] Human.js loaded successfully', {
607
- modelLoadTime: `${loadTime.toFixed(2)}ms`,
584
+ console.log('[loadHuman] Loaded successfully', {
608
585
  totalInitTime: `${totalTime.toFixed(2)}ms`,
609
586
  version: human.version
610
587
  });
@@ -612,7 +589,32 @@ async function loadHuman(modelPath, wasmPath) {
612
589
  }
613
590
  catch (error) {
614
591
  const errorMsg = error instanceof Error ? error.message : 'Unknown error';
615
- console.error('[FaceDetectionEngine] Human.js load failed:', errorMsg);
592
+ console.error('[loadHuman] Load failed:', errorMsg);
593
+ throw error;
594
+ }
595
+ }
596
+ async function loadLibraries(modelPath, wasmPath, timeout) {
597
+ console.log('[loadLibraries] Starting parallel load of OpenCV and Human...');
598
+ const startTime = performance.now();
599
+ if (timeout == undefined) {
600
+ timeout = 30000;
601
+ }
602
+ try {
603
+ // 并行加载 OpenCV 和 Human
604
+ const [cv, human] = await Promise.all([
605
+ loadOpenCV(timeout),
606
+ loadHuman(modelPath, wasmPath)
607
+ ]);
608
+ const totalTime = performance.now() - startTime;
609
+ console.log('[loadLibraries] Both libraries loaded successfully in', totalTime.toFixed(2), 'ms');
610
+ return {
611
+ cv,
612
+ human
613
+ };
614
+ }
615
+ catch (error) {
616
+ const errorMsg = error instanceof Error ? error.message : 'Unknown error';
617
+ console.error('[loadLibraries] Failed to load libraries:', errorMsg);
616
618
  throw error;
617
619
  }
618
620
  }
@@ -1594,12 +1596,13 @@ class FaceDetectionEngine extends SimpleEventEmitter {
1594
1596
  this.isInitializing = true;
1595
1597
  this.emitDebug('initialization', 'Starting to load detection libraries...');
1596
1598
  try {
1597
- // Load OpenCV
1598
- this.emitDebug('initialization', 'Loading OpenCV...');
1599
- const { cv } = await loadOpenCV(300000); // 5 minute timeout
1599
+ // Load Libraries
1600
+ this.emitDebug('initialization', 'Loading Libraries...');
1601
+ const startLoadTime = performance.now();
1602
+ const { cv, human } = await loadLibraries(this.config.human_model_path, this.config.tensorflow_wasm_path, 30000);
1600
1603
  if (!cv || !cv.Mat) {
1601
1604
  console.log('[FaceDetectionEngine] Failed to load OpenCV.js: module is null or invalid');
1602
- this.emit('detector-error', {
1605
+ this.emit('detector-loaded', {
1603
1606
  success: false,
1604
1607
  error: 'Failed to load OpenCV.js: module is null or invalid'
1605
1608
  });
@@ -1609,22 +1612,9 @@ class FaceDetectionEngine extends SimpleEventEmitter {
1609
1612
  });
1610
1613
  return;
1611
1614
  }
1612
- this.emitDebug('initialization', 'OpenCV loaded successfully', {
1613
- version: cv?.getBuildInformation?.() || 'unknown'
1614
- });
1615
- console.log('[FaceDetectionEngine] OpenCV loaded successfully', {
1616
- version: cv?.getBuildInformation?.() || 'unknown'
1617
- });
1618
- // Load Human.js
1619
- console.log('[FaceDetectionEngine] Loading Human.js models...');
1620
- this.emitDebug('initialization', 'Loading Human.js...');
1621
- const humanStartTime = performance.now();
1622
- this.human = await loadHuman(this.config.human_model_path, this.config.tensorflow_wasm_path);
1623
- const humanLoadTime = performance.now() - humanStartTime;
1624
1615
  if (!this.human) {
1625
1616
  const errorMsg = 'Failed to load Human.js: instance is null';
1626
1617
  console.log('[FaceDetectionEngine] ' + errorMsg);
1627
- this.emitDebug('initialization', errorMsg, { loadTime: humanLoadTime }, 'error');
1628
1618
  this.emit('detector-loaded', {
1629
1619
  success: false,
1630
1620
  error: errorMsg
@@ -1635,21 +1625,14 @@ class FaceDetectionEngine extends SimpleEventEmitter {
1635
1625
  });
1636
1626
  return;
1637
1627
  }
1638
- this.emitDebug('initialization', 'Human.js loaded successfully', {
1639
- loadTime: `${humanLoadTime.toFixed(2)}ms`,
1640
- version: this.human.version
1641
- });
1642
- console.log('[FaceDetectionEngine] Human.js loaded successfully', {
1643
- loadTime: `${humanLoadTime.toFixed(2)}ms`,
1644
- version: this.human.version
1645
- });
1646
1628
  this.isReady = true;
1647
1629
  const loadedData = {
1648
1630
  success: true,
1649
- opencv_version: cv?.getBuildInformation?.() || 'unknown',
1631
+ opencv_version: getOpenCVVersion(),
1650
1632
  human_version: this.human.version
1651
1633
  };
1652
1634
  console.log('[FaceDetectionEngine] Engine initialized and ready', {
1635
+ initCostTime: (performance.now() - startLoadTime).toFixed(2),
1653
1636
  opencv_version: loadedData.opencv_version,
1654
1637
  human_version: loadedData.human_version
1655
1638
  });
@@ -2419,5 +2402,5 @@ class FaceDetectionEngine extends SimpleEventEmitter {
2419
2402
  }
2420
2403
  }
2421
2404
 
2422
- export { DetectionPeriod, ErrorCode, FaceDetectionEngine, LivenessAction, LivenessActionStatus, PromptCode, FaceDetectionEngine as default, getCvSync, loadOpenCV, preloadOpenCV };
2405
+ export { DetectionPeriod, ErrorCode, FaceDetectionEngine, LivenessAction, LivenessActionStatus, PromptCode, FaceDetectionEngine as default, getCvSync, getOpenCVVersion, loadOpenCV, preloadOpenCV };
2423
2406
  //# sourceMappingURL=index.esm.js.map