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