flutter_ppg 0.2.0 copy "flutter_ppg: ^0.2.0" to clipboard
flutter_ppg: ^0.2.0 copied to clipboard

A Flutter package for camera-based PPG (Photoplethysmography) signal processing. Extracts RR intervals from camera frames for heart rate and HRV analysis.

Flutter PPG (Photoplethysmography) #

A Flutter package for real-time camera-based PPG (Photoplethysmography) signal processing. This package extracts the raw red channel intensity from camera frames, filters the signal to isolate cardiac activity, and detects heartbeats (RR intervals).

⚠️ Requirements #

  1. Hardware: A device with a Camera and a Flash (Torch).
  2. Platform: iOS or Android (Real device required, Simulator/Emulator usually cannot provide camera/flash access).

🚀 Getting Started #

1. Installation #

Add flutter_ppg to your pubspec.yaml:

dependencies:
  flutter_ppg:
    path: ../ # Or your git/pub path

2. Basic Usage #

Use the FlutterPPGService to process a stream of CameraImages from the camera package.

// 1. Initialize Service
final ppgService = FlutterPPGService();

// 2. Start Camera Image Stream
// (Assuming standard camera controller setup)
controller.startImageStream((CameraImage image) {
  // Bridge to stream if necessary, or just use the service directly if updated
});

// Since the current API expects a Stream<CameraImage>, you can use a StreamController:
final streamController = StreamController<CameraImage>();
controller.startImageStream((image) => streamController.add(image));

// 3. Process Stream
ppgService.processImageStream(streamController.stream).listen((PPGSignal signal) {
  print('Quality: ${signal.quality}');
  print('Filtered Value: ${signal.filteredIntensity}');
  if (signal.rrIntervals.isNotEmpty) {
    print('Last RR: ${signal.rrIntervals.last} ms');
  }
});

// Remember to dispose when done:
// streamController.close();
// ppgService.dispose();

See the example/ directory for a complete working application.

3. Custom Configuration (PPGConfig) #

You can override thresholds and timing using PPGConfig:

final config = PPGConfig(
  samplingRate: 30,
  windowSizeSeconds: 10,
  minRRMs: 300.0,
  maxRRMs: 2000.0,
  maxAdjacentRRChangeRatio: 0.30,
  maxAcceptableSDRRMs: 150.0,
  maxDriftRate: 50.0,
  minGoodSNR: 5.0,
  minFairSNR: 0.0,
  fingerPresenceMin: 30.0,
  fingerPresenceMax: 250.0,
);

final ppgService = FlutterPPGService(config: config);

Override Priority (SSOT rules):

  • If you pass custom instances (qualityAssessor, outlierFilter, rrIntervalAnalyzer, peakDetector), those override PPGConfig values for their respective behavior.
  • If you do not pass a custom instance, FlutterPPGService builds it from PPGConfig.
  • If you pass a custom PeakDetector, adaptive min-distance (FPS-based) is not applied.

The library emits raw metrics every frame. Session control (warm-up, duration, acceptance) should live in your app, not inside the package.

Suggested gating logic:

  • Ignore the first ~10 seconds (warm-up)
  • Require signal.isFPSStable == true
  • Require signal.rejectionRatio <= 0.20
  • Require signal.isSDRRAcceptable == true

Note: when isFPSStable is false, the package skips RR interval generation and emits empty rrIntervals for that frame.

Example (pseudo):

if (signal.isFPSStable &&
    signal.rejectionRatio <= 0.20 &&
    signal.isSDRRAcceptable &&
    signal.quality != SignalQuality.poor) {
  // Accept this signal for BPM/HRV calculation
}

These are practical guidelines for more stable measurements in real apps:

  • Low FPS segments: if signal.frameRate drops significantly (e.g., <20 FPS), discard those samples from BPM/HRV aggregation.
  • isFPSStable behavior: stability requires sustained FPS above a minimum threshold (internally, consecutive low-FPS updates will set isFPSStable=false).
  • Recommended measurement duration: 30–60 seconds yields more stable HRV metrics.
  • UI load matters: heavy animations or frequent setState can reduce FPS and degrade signal quality.
  • Finger placement: cover both lens and flash; minimize motion and pressure changes.
  • FPS stability: frame rate is estimated from a monotonic clock to reduce wall-clock jitter.
  • Logging (optional): for debugging, capture at least:
    • frameRate, isFPSStable, quality, snr
    • sdrr, rejectionRatio, driftRate
    • raw/filtered mean + range over a short window

Adaptive prominence details (current defaults):

  • minProminence = max(0.2, 0.5 * stddev) computed from the last ~60 filtered samples
  • The detector is updated only when the prominence changes by ≥ 0.05
  • If there is not enough data, the base PeakDetector.minProminence is used

👆 How to Measure #

For accurate readings in the example app (or your implementation):

  1. Enable Flash: The back camera flash MUST be on (FlashMode.torch).
  2. Cover Lens & Flash: Place your fingertip gently over both the camera lens and the flash.
    • Do not press too hard (this restricts blood flow).
    • Do not press too ligthly (ambient light leaks in).
  3. Stay Still: Motion introduces significant noise. Keep your finger and the phone steady.

📦 Features #

  • Signal Processing:
    • Red channel extraction (optimized for YUV420/BGRA8888).
    • Simple bandpass approximation (SMA short - SMA long) to isolate pulse band.
    • Detrending helper available (not wired into the current pipeline).
  • Peak Detection:
    • Robust local maxima detection with minimum distance enforcement.
    • Outlier rejection (IQR method) for cleaner RR intervals.
    • Adaptive prominence based on recent filtered signal variability.
  • Quality Assessment:
    • Real-time Signal Quality Index (Good/Fair/Poor).
    • Finger presence detection based on light intensity.
    • SNR-based quality evaluation.

🧩 Architecture #

The package follows clean architecture principles:

  • SignalProcessor: Pure logic for math and filtering (Stateless).
  • PeakDetector: Algorithms for identifying heartbeats.
  • SignalQualityAssessor: Heuristics for signal validity.
  • FlutterPPGService: Orchestrator (Stateful) managing the data flow and buffers.

✅ Current Defaults / Planned Thresholds (Confirm in Refactor) #

Current defaults (v0.1.x):

  • Sampling rate: 30 FPS (PPGConfig.samplingRate)
  • Window size: 10 seconds (PPGConfig.windowSizeSeconds)
  • Peak min distance: derived from PPGConfig.minRRMs + FPS
  • Peak min prominence: 0.5 (PeakDetector.minProminence)
  • RR physiological range: 300–2000ms (PPGConfig.minRRMs / maxRRMs)
  • Finger presence: 30–250 intensity (PPGConfig.fingerPresenceMin / fingerPresenceMax)
  • SNR quality: >5 dB = good, >0 dB = fair (PPGConfig.minGoodSNR / minFairSNR)

Planned thresholds (v0.2.x, needs agreement):

  • Max adjacent RR change: 30% (PPGConfig.maxAdjacentRRChangeRatio)
  • Max acceptable SDRR: 150ms (PPGConfig.maxAcceptableSDRRMs)
  • Max baseline drift: 50 intensity units/sec (PPGConfig.maxDriftRate)
  • Frame-rate auto detection: 24/25/30/60 FPS + adaptive timing windows

Notes:

  • "Intensity" is derived from camera pixel data (0–255-ish scale).
  • Once FPS detection is added, time-based windows/min-distance will be derived from actual FPS.

📝 License #

MIT

1
likes
0
points
266
downloads

Publisher

verified publishershigindo.com

Weekly Downloads

A Flutter package for camera-based PPG (Photoplethysmography) signal processing. Extracts RR intervals from camera frames for heart rate and HRV analysis.

Repository (GitHub)
View/report issues

Topics

#ppg #heart-rate #hrv #camera #health

License

unknown (license)

Dependencies

camera, flutter

More

Packages that depend on flutter_ppg