@sssxyd/face-liveness-detector 0.2.22 → 0.2.24

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