Skip to content

实时心率估算器

NOTE

僅限网页 SDK 实时估算功能目前僅适用於网页版 SDK(JavaScript、React、Vue)。不适用於行動装置 SDK(iOS、Android、Flutter、React Native)。

概述

实时估算器在影片撷取過程中提供实时心率反馈,最快可在 3-5 秒內显示结果,无需等待完整的 30 秒扫描。這創造了更具吸引力的使用者體驗,並幫助使用者调整位置和光线以获得最佳测量结果。

快速开始

typescript
import { createVitalSignCamera, RealtimeEstimatorType } from 'ts-vital-sign-camera';

const camera = createVitalSignCamera({
  realtimeEstimationConfig: {
    estimatorType: RealtimeEstimatorType.MeRppg, // 预设:AI 驱动
    earlyEstimation: true,
    minDuration: 3,
    minConfidence: 0.3
  },
  onVideoFrameProcessed: (event) => {
    // 实时估算可從事件中取得
    if (event.realtimeEstimation) {
      console.log(`心率:${event.realtimeEstimation.heartRate} BPM`);
      console.log(`置信度:${event.realtimeEstimation.confidence}`);
      console.log(`稳定:${event.realtimeEstimation.isStable}`);
    }
  }
});

可用的估算器

ME-rPPG 估算器(预设)🤖

AI 驱动的神經網路方法

  • 最適合:消费者应用程序、变化的环境、现代装置
  • 准确度:最先進(±2-3 BPM)
  • 速度:約 3 秒內首次结果
  • 动作容忍度:优秀
  • 光线容忍度:优秀
  • 授权:开源
  • 需求:約 10 MB 模型文档
typescript
const camera = createVitalSignCamera({
  realtimeEstimationConfig: {
    estimatorType: RealtimeEstimatorType.MeRppg
  }
});

FDA 估算器 📈

信号处理方法

  • 最適合:医疗应用程序、实时初始化、最小套件大小
  • 准确度:医疗级(±2-3 BPM)
  • 速度:約 5 秒內首次结果
  • 动作容忍度:良好
  • 光线容忍度:良好
  • 授权:专有(PanopticAI)
  • 需求:无(实时初始化)
typescript
const camera = createVitalSignCamera({
  realtimeEstimationConfig: {
    estimatorType: RealtimeEstimatorType.Fda
  }
});

配置选项

基本配置

typescript
interface RealtimeEstimationConfig {
  // 要使用的估算器
  estimatorType?: RealtimeEstimatorType;
  
  // 在完整扫描完成前显示结果
  earlyEstimation?: boolean;
  
  // 显示结果前的最小秒數
  minDuration?: number;
  
  // 最小置信度阈值(0-1)
  minConfidence?: number;
  
  // 启用调试记录
  debug?: boolean;
}

ME-rPPG 特定选项

typescript
{
  // 模型文档路径(可選,提供预设值)
  modelPath?: string;
  statePath?: string;
  welchPath?: string;
  hrPath?: string;
  
  // 時間正规化参数
  lambda?: number; // 预设:1.0
}

估算结果

RealtimeEstimation 接口

typescript
interface RealtimeEstimation {
  // 心率(BPM)
  heartRate: number;
  
  // 置信度(0-1)
  confidence: number;
  
  // 结果是否足夠稳定可显示
  isStable: boolean;
  
  // 信噪比(可選)
  snr?: number;
  
  // 信号质量分数 0-100(可選)
  signalQuality?: number;
}

使用估算结果

typescript
camera.onVideoFrameProcessed = (event) => {
  const estimation = event.realtimeEstimation;
  
  if (!estimation) return;
  
  if (estimation.isStable && estimation.confidence > 0.6) {
    // 高置信度 - 显著显示
    displayHeartRate(estimation.heartRate);
  } else if (estimation.confidence > 0.3) {
    // 中等置信度 - 显示指示器
    displayHeartRate(estimation.heartRate, '精炼中...');
  } else {
    // 低置信度 - 引导使用者
    showMessage('调整中... 请保持静止');
  }
};

信号可视化

显示原始 PPG 信号以進行调试或使用者反馈。

基本用法

typescript
import { SignalVisualizer } from 'ts-vital-sign-camera';

// 建立可视化器
const container = document.getElementById('signal-container');
const visualizer = new SignalVisualizer(container, {
  width: 600,
  height: 200,
  visualizationType: 'PPG', // 或 'ECG' 進行模拟
  lineColor: '#00ff00',
  backgroundColor: '#000000'
});

// 使用影片帧事件中的信号数据更新
camera.onVideoFrameProcessed = (event) => {
  if (event.signalData) {
    visualizer.updateSignal(event.signalData, event.realtimeEstimation);
  }
};

// 清理
visualizer.destroy();

配置选项

typescript
interface SignalVisualizerConfig {
  width?: number;              // 画布宽度(预设:600)
  height?: number;             // 画布高度(预设:200)
  maxPoints?: number;          // 最大数据点(预设:300)
  lineColor?: string;          // 信号线颜色
  backgroundColor?: string;    // 画布背景
  gridColor?: string;          // 网格线颜色
  visualizationType?: 'PPG' | 'ECG';  // 信号类型
  debug?: boolean;             // 调试记录
}

可视化类型

PPG 模式(预设):

  • 显示來自相机的实际光体积描记信号
  • 实时滚动波形
  • 适用於调试信号质量

ECG 模式

  • 模拟心電圖动画
  • 测量期間的視覺反馈
  • 符合估算的心率
typescript
// PPG 可视化(实际信号)
const ppgVisualizer = new SignalVisualizer(container, {
  visualizationType: 'PPG'
});

// ECG 模拟(动画)
const ecgVisualizer = new SignalVisualizer(container, {
  visualizationType: 'ECG',
  lineColor: '#ff6b6b'
});

SDK 行為

时间轴

0秒 ────────► 3秒 ────────► 10秒 ────────► 30秒
│            │            │             │
│            │            │             └─ 完整扫描完成
│            │            └─ 达到最佳准确度
│            └─ 首次实时估算(ME-rPPG)
└─ 扫描开始

估算生命周期

  1. 初始化(0-3秒)

    • 相机开始撷取
    • 人脸侦测启动
    • 信号缓冲区填充
  2. 早期估算(3-5秒)

    • 首次结果可用
    • 较低置信度
    • 持续精炼
  3. 稳定估算(10秒以上)

    • 高置信度结果
    • 最佳准确度
    • 準備显示
  4. 扫描完成(30秒)

    • 服务器端验证
    • 最終结果可用
    • 实时估算器可重置

事件流程

typescript
// 扫描生命周期
camera.onVideoFrameProcessed = (event) => {
  // 实时估算在此可用
  if (event.realtimeEstimation) {
    console.log('实时更新:', event.realtimeEstimation);
  }
};

camera.startScanning(); // 开始扫描

// 最終结果來自健康结果
camera.onVideoFrameProcessed = (event) => {
  if (event.healthResult) {
    console.log('最終结果:', event.healthResult);
  }
};

最佳實踐

1. 选择正確的估算器

typescript
// 适用於具有变化环境的消费者应用程序
const config = {
  estimatorType: RealtimeEstimatorType.MeRppg,
  earlyEstimation: true,
  minDuration: 3,
  minConfidence: 0.3
};

// 适用於需要实时初始化的医疗应用程序
const config = {
  estimatorType: RealtimeEstimatorType.Fda,
  earlyEstimation: false,
  minDuration: 10,
  minConfidence: 0.7
};

2. 提供使用者反馈

typescript
camera.onVideoFrameProcessed = (event) => {
  const estimation = event.realtimeEstimation;
  if (!estimation) return;
  
  // 显示置信度指示器
  updateConfidenceBar(estimation.confidence);
  
  // 根据质量引导使用者
  if (estimation.confidence < 0.4) {
    showTip('改善光线或保持静止');
  }
  
  // 稳定時显示
  if (estimation.isStable) {
    displayHeartRate(estimation.heartRate);
  }
};

3. 处理边缘情况

typescript
let noEstimationTimeout;
let hasReceivedEstimation = false;

camera.startScanning();

// 设定无估算的超时
noEstimationTimeout = setTimeout(() => {
  if (!hasReceivedEstimation) {
    showError('无法侦测心率。请调整光线。');
  }
}, 15000); // 15 秒

camera.onVideoFrameProcessed = (event) => {
  const estimation = event.realtimeEstimation;
  
  if (estimation) {
    hasReceivedEstimation = true;
    clearTimeout(noEstimationTimeout);
    
    if (estimation.confidence > 0.5) {
      hideError();
    }
  }
};

4. 最佳化效能

typescript
// 节流 UI 更新
let lastUpdate = 0;
const UPDATE_INTERVAL = 500; // 500毫秒

camera.onVideoFrameProcessed = (event) => {
  const estimation = event.realtimeEstimation;
  if (!estimation) return;
  
  const now = Date.now();
  if (now - lastUpdate > UPDATE_INTERVAL) {
    updateUI(estimation);
    lastUpdate = now;
  }
};

常见使用案例

健身应用程序

typescript
const camera = createVitalSignCamera({
  realtimeEstimationConfig: {
    estimatorType: RealtimeEstimatorType.MeRppg,
    earlyEstimation: true,
    minDuration: 3,
    minConfidence: 0.3
  },
  onVideoFrameProcessed: (event) => {
    if (event.realtimeEstimation) {
      updateWorkoutDisplay(event.realtimeEstimation.heartRate);
      updateHeartRateZone(event.realtimeEstimation.heartRate);
    }
  }
});

医疗应用程序

typescript
const camera = createVitalSignCamera({
  realtimeEstimationConfig: {
    estimatorType: RealtimeEstimatorType.Fda,
    earlyEstimation: false,
    minDuration: 10,
    minConfidence: 0.7
  },
  onVideoFrameProcessed: (event) => {
    const estimation = event.realtimeEstimation;
    if (estimation?.isStable && estimation.confidence > 0.7) {
      recordMeasurement(estimation);
    }
  }
});

使用信号可视化

typescript
const visualizer = new SignalVisualizer(container, {
  visualizationType: 'PPG',
  width: 800,
  height: 300
});

camera.onVideoFrameProcessed = (event) => {
  if (event.signalData) {
    visualizer.updateSignal(event.signalData, event.realtimeEstimation);
  }
};

疑難排解

无实时估算

问题:事件中未收到实时估算

解决方案

  1. 确保设定 earlyEstimation: true
  2. 检查人脸是否正確侦测
  3. 验证光线条件是否充足
  4. 至少等待 minDuration
  5. 检查 event.realtimeEstimation 不为 null

低置信度分数

问题:置信度持续低于阈值

解决方案

  1. 改善光线(自然光最佳)
  2. 确保使用者保持静止
  3. 检查人脸是否居中且稳定
  4. 降低 minConfidence 阈值
  5. 使用 ME-rPPG 以获得更好的动作容忍度

ME-rPPG 模型未加载

问题:模型加载失败

解决方案

  1. 验证模型文档在正確的目录中
  2. 检查 CORS 标头允许模型加载
  3. 确保正確的文档路径(使用绝对路径)
  4. 检查浏览器控制台是否有错误

API 参考

RealtimeEstimator 接口

typescript
interface RealtimeEstimator {
  readonly estimation: RealtimeEstimation | null;
  readonly signalData: SignalData | null;
  
  configure(config?: RealtimeEstimationConfig): void;
  reset(): void;
}

SignalVisualizer 類別

typescript
class SignalVisualizer {
  constructor(container: HTMLElement, config?: SignalVisualizerConfig);
  
  updateSignal(
    signalData: SignalData | null,
    realtimeEstimation?: RealtimeEstimation | null
  ): void;
  
  resetScale(): void;
  destroy(): void;
}

VideoFrameProcessedEvent

typescript
interface VideoFrameProcessedEvent {
  videoFrameInfo: VideoFrameInfo;
  videoFrame: VideoFrame;
  facebox?: NormalizedFacebox;
  healthResult?: ScanResult;
  scanConditions?: ScanConditions;
  landmarks?: NormalizedLandmarkList;
  
  // 实时估算数据
  realtimeEstimation?: RealtimeEstimation | null;
  signalData?: SignalData | null;
}

下一步