8000 GitHub - HaxyMoly/heartlen
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

HaxyMoly/heartlen

Repository files navigation

HeartLen

HeartLen Screenshot

HeartLen is an web-based application for real-time heart rate and heart rate variability (HRV) monitoring using camera-based photoplethysmography (PPG). It analyzes subtle color changes in facial skin that correspond to blood flow, enabling contactless cardiovascular monitoring. Note that this is a student assignment for BIOF3003, do not use this for any medical purpose :)

You can play with the online demo of HeartLen

Table of Contents

User Guide

Getting Started

System Requirements

  • A smart phone/tablet or other devices with built-in camera with a flash light

Accessing HeartLen

  1. Please refer to Development and Deployment Setup if you are willing to self-host HeartLen
  2. Navigate to the HeartLen (e.g, https://heartlen-gamma.vercel.app or https://localhost:3000 if you self-host it locally) in your web browser
  3. When prompted, enter your Subject ID (this is required for data tracking)
  4. When prompted, allow camera access permissions
  5. Position your finger on the camera and flash light

Interface Overview

The HeartLen interface is divided into three main sections:

Login Page:

  • Enter your Subject ID to start a new session or retrieve your historical data

Login screen

Left Panel:

  • Camera Feed displaying real-time video from your camera
  • Current Heart Rate, HRV, and Signal Quality
  • Last access time
  • Con 10000 figuration Options for PPG signal channel combination

Left screen

Right Panel:

  • Real-time PPG Chart to visualize the PPG signal
  • Historical Averages that displays your average heart rate and HRV from previous sessions
  • Historical Trends Chart to demonstrate the heart rate and HRV changed over time

Right screen

Using HeartLen

Recording a Session

  1. Start Recording: Click the "START RECORDING" button in the top-right corner.
  2. Monitor Metrics: Watch as your heart rate, HRV, and signal quality metrics update in real-time.
  3. Adjust Position: If signal quality is poor, adjust your position or lighting conditions.
  4. Save Data: After recording for at least 30 seconds, click "Save Data to MongoDB" to store your session.
  5. Stop Recording: Click "STOP RECORDING" when finished.

Configuring Signal Processing

  1. Click "Show Config" to reveal configuration options.
  2. Select a signal combination method:
    • Default: Balanced approach using a combination of RGB channels (2R-G-B)
    • Red Only: Uses only the red channel (may work better in certain lighting)
    • Green Only: Uses only the green channel (typically most sensitive to blood volume changes)
    • Blue Only: Uses only the blue channel
    • Custom: 3R-G-B channel combination

Understanding Your Metrics

Heart Rate

  • BPM (Beats Per Minute): Normal resting heart rate for adults ranges from 60-100 BPM.
  • Confidence: Indicates the reliability of the current heart rate measurement.
    • High confidence (>70%): Measurement is likely accurate
    • Low confidence (<70%): Measurement may be unreliable

Heart Rate Variability (HRV)

  • HRV Measured in milliseconds, represents the variation in time between heartbeats.
    • Higher values (>50ms): Generally associated with better cardiovascular health and stress resilience
    • Lower values (<30ms): May indicate higher stress levels or potential cardiovascular issues
  • Confidence: Indicates the reliability of the current HRV measurement.

Signal Quality

  • Excellent: Clean signal with minimal noise
  • Acceptable: Mostly clean signal with some minor noise
  • Bad: Bad signal with high noise or artifacts

Troubleshooting

Poor Signal Quality

  • Ensure your finger covers the entire camera and the flash light
  • Sometimes the camera switches randomly, please make sure your fingers are always on the camera being recorded
  • Minimize finger movement during recording
  • Try a different signal combination in the configuration settings

Inaccurate Readings

  • Record for at least 30 seconds to allow the algorithm to stabilize
  • Check signal quality indicator - only trust readings with "Excellent" or "Acceptable" quality
  • Try adjusting the signal combination in the configuration settings

No Camera Feed

  • Check that camera permissions are granted
  • Check if you are aceesing through https in case of self-hosting
  • Try refreshing the page
  • Change another browser

Developer Guide

Development and Deployment Setup

Prerequisites

  • Node.js (v14+)
  • npm or yarn
  • MongoDB (local or Atlas)
  • Git

Setup steps

  1. Clone the repository:

    git clone https://github.com/yourusername/heartlen.git
    cd heartlen
  2. Install dependency:

    npm install
  3. Set up environment variables :Create a .env.local file in the project root with the MONGODB_URI:

    MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/heartlen?retryWrites=true&w=majority
    

    You may create a MongoDB Atlas account for free-tier cluster and get the from Clusters > Connect > MongoDB For VS Code Ensure the database has a collection named records to store PPG data

  4. Start the development server:

     # Must use https to use the camera on mobile devices
     npm run dev --experimental-https

    OR

    Build and start the production server:

    npm run build
    npm run start
  5. Open the application: Open your browser and navigate to

    https://localhost:3000
    

Technology Stack

  • Frontend:

    • Next.js 13+
    • React 18+
    • TypeScript
    • Tailwind CSS
    • Chart.js
  • Backend:

    • Next.js API Routes
    • MongoDB
  • PPG Quality Assessment Model Backend:

    • Tensorflow.js

Project Structure

├── app/
│   ├── components/
│   │   ├── CameraFeed.tsx           # Handles video capture and display
│   │   ├── MetricsCard.tsx          # Displays health metrics with confidence indicators
│   │   ├── SignalCombinationSelector.tsx  # UI for selecting signal processing methods
│   │   ├── ChartComponent.tsx       # Visualizes the PPG signal and detected valleys
│   │   ├── HistoricalChart.tsx      # Displays historical heart rate and HRV data
│   │   └── Layout/
│   │       ├── Header.tsx           # Application header with navigation
│   │       └── Footer.tsx           # Application footer
│   ├── hooks/
│   │   ├── usePPGProcessing.ts      # Handles camera initialization and PPG signal extraction
│   │   └── useSignalQuality.ts      # Evaluates the quality of the PPG signal
│   ├── utils/
│   │   ├── signalProcessing.ts      # Signal processing algorithms
│   │   ├── peakDetection.ts         # Peak/valley detection algorithms
│   │   └── metrics.ts               # Heart rate and HRV calculation functions
│   ├── api/
│   │   └── handle-record/
│   │       └── route.ts             # API endpoint for saving and retrieving PPG records
│   ├── models/
│   │   └── Record.ts                # Mongoose model for PPG records
│   ├── lib/
│   │   └── mongodb.ts               # MongoDB connection utilities
│   ├── layout.tsx                   # Root layout component
│   └── page.tsx                     # Main application page
├── types/
│   └── index.ts                     # TypeScript type definitions
├── styles/
│   └── globals.css                  # Global styles with Tailwind CSS
├── .env.local                       # Environment variables (MongoDB connection string)
├── next.config.js                   # Next.js configuration
├── tailwind.config.js               # Tailwind CSS configuration
├── tsconfig.json                    # TypeScript configuration
├── package.json                     # Project dependencies and scripts
└── README.md                        # Project documentation

Component Architecture

Main Components

  1. CameraFeed (/app/components/CameraFeed.tsx)

    • Purpose: Handles video capture and display
    • Props:
      • videoRef: React ref for the video element
      • canvasRef: React ref for the canvas element used in processing
    • Functionality:
      • Renders video element with appropriate settings
      • Positions canvas for frame processing
      • Handles camera initialization and cleanup
  2. MetricsCard (/app/components/MetricsCard.tsx)

    • Purpose: Displays health metrics with confidence indicators
    • Props:
      • title: Name of the metric (e.g., "HEART RATE")
      • value: Object containing metric values (e.g., {bpm: 75} or {sdnn: 45})
      • confidence: Number between 0-100 indicating measurement reliability
      • className: Optional CSS classes for styling
    • Functionality:
      • Renders metric title and value
      • Displays confidence indicator with appropriate color coding
      • Adapts display based on metric type
  3. SignalCombinationSelector (/app/components/SignalCombinationSelector.tsx)

    • Purpose: Allows users to select different signal processing methods
    • Props:
      • signalCombination: Current selected method
      • setSignalCombination: Function to update the selection
    • Functionality:
      • Renders radio buttons for different signal combinations
      • Provides descriptions of each method
      • Updates parent component when selection changes
  4. ChartComponent (/app/components/ChartComponent.tsx)

    • Purpose: Visualizes the PPG signal and detected valleys
    • Props:
      • ppgData: Array of PPG signal values
      • valleys: Array of indices where valleys are detected
    • Functionality:
      • Renders a line chart of the PPG waveform
      • Highlights detected valleys with markers
      • Updates in real-time as new data arrives
  5. HistoricalChart (/app/components/HistoricalChart.tsx)

    • Purpose: Displays historical heart rate and HRV data
    • Props:
      • historicalData: Array of previously saved records
    • Functionality:
      • Renders a time-series chart of heart rate and HRV
      • Formats timestamps for readability
      • Provides tooltips with detailed information

State Management

The application uses React's built-in state management with useState and useEffect hooks. Here's a detailed breakdown of the state variables in the main application component:

  1. isRecording (Boolean)

    • Purpose: Controls whether the camera is active and processing frames
    • Interactions:
      • Toggled by the START/STOP RECORDING button
      • Triggers camera initialization/shutdown via useEffect
      • Controls the animation frame loop for processing
  2. isSampling (Boolean)

    • Purpose: Enables automatic periodic data sampling
    • Interactions:
      • When true, automatically calls pushDataToMongo at regular intervals
      • Used for continuous monitoring scenarios
  3. isUploading (Boolean)

    • Purpose: Prevents multiple simultaneous API calls
    • Interactions:
      • Set to true before API calls begin
      • Set to false when API calls complete
      • Used to disable the "Save Data" button during uploads
  4. signalCombination (String)

    • Purpose: Determines which color channels to use for PPG extraction
    • Options: "default", "red", "green", "blue", "custom"
    • Interactions:
      • Updated by SignalCombinationSelector component
      • Passed to usePPGProcessing hook to adjust signal processing
  5. showConfig (Boolean)

    • Purpose: Toggles the configuration panel visibility
    • Interactions:
      • Toggled by the "Show/Hide Config" button
      • Controls rendering of SignalCombinationSelector component
  6. lastRecordTime (String | null)

    • Purpose: Stores the timestamp of the last saved record
    • Interactions:
      • Updated by fetchLastRecordTime function
      • Displayed in the UI to show when data was last saved
  7. historicalAvgs (Object)

    • Purpose: Stores average heart rate and HRV from historical data
    • Structure: { heartRate: number, hrv: number }
    • Interactions:
      • Updated by fetchLastRecordTime function
      • Displayed in MetricsCard components
  8. ppgData (Array)

    • Purpose: Stores the processed PPG signal values
    • Interactions:
      • Updated by usePPGProcessing hook
      • Used for real-time chart visualization
      • Sent to backend when saving data
  9. valleys (Array)

    • Purpose: Stores indices where valleys in the PPG signal are detected
    • Interactions:
      • Updated by usePPGProcessing hook
      • Used for heart rate calculation
      • Visualized on the PPG chart
  10. heartRate (Object)

    • Purpose: Stores the calculated heart rate and confidence
    • Structure: { bpm: number, confidence: number }
    • Interactions:
      • Updated by usePPGProcessing hook
      • Displayed in MetricsCard component
      • Sent to backend when saving data
  11. hrv (Object)

    • Purpose: Stores the calculated HRV (SDNN) and confidence
    • Structure: { sdnn: number, confidence: number }
    • Interactions:
      • Updated by usePPGProcessing hook
      • Displayed in MetricsCard component
      • Sent to backend when saving data
  12. signalQuality (String)

    • Purpose: Indicates the quality of the current PPG signal
    • Options: "Excellent", "Acceptable", "Bad"
    • Interactions:
      • Updated by useSignalQuality hook
      • Displayed in MetricsCard component
  13. qualityConfidence (Number)

    • Purpose: Represents confidence in the signal quality assessment
    • Range: 0-100
    • Interactions:
      • Updated by useSignalQuality hook
      • Used for confidence visualization in MetricsCard
  14. historicalData (Array)

    • Purpose: Stores previously saved records for trend visualization
    • Interactions:
      • Updated by fetchLastRecordTime function
      • Used by HistoricalChart component

Custom Hooks

usePPGProcessing

File: /app/hooks/usePPGProcessing.ts

Purpose: Handles camera initialization, frame processing, and PPG signal extraction

Parameters:

  • isRecording: Boolean indicating whether recording is active
  • signalCombination: String indicating which color channels to use
  • videoRef: React ref for the video element
  • canvasRef: React ref for the canvas element

Returns:

  • ppgData: Array of processed PPG signal values
  • valleys: Array of indices where valleys are detected
  • heartRate: Object containing BPM and confidence
  • hrv: Object containing SDNN and confidence
  • processFrame: Function to process a single video frame
  • startCamera: Function to initialize the camera
  • stopCamera: Function to shut down the camera

Please refer to Signal Processing if you are interested in how it works.

useSignalQuality

File: /app/hooks/useSignalQuality.ts

Purpose: Evaluates the quality of the PPG signal by infering a classifier with Tensorflow.js

Parameters:

  • ppgData: Array of PPG signal values

Returns:

  • signalQuality: String indicating quality level
  • qualityConfidence: Number representing confidence in the assessment

Please refer to the the Jupyter notebook ppg-classifier-auto-tuning.ipynb if you are interested in the details of the model.

Data Flow

Frontend to Backend Flow

  1. Data Capture and Processing: Camera → videoRef → Canvas → RGB Extraction → Signal Processing → PPG Data
  • Camera captures frames at ~30fps
  • Frames are rendered to the video element (videoRef)
  • processFrame function extracts RGB values from regions of interest
  • Signal processing algorithms filter and analyze the data
  • Processed data is stored in ppgData state variable
  1. Metric Calculation:
  • Valley detection algorithms identify dips in the PPG signal
  • Inter-beat intervals (IBIs) are calculated from the time between consecutive valleys
  • Heart rate is computed as 60 / (average IBI in seconds)
  • HRV (SDNN) is calculated as the standard deviation of IBIs
  • Confidence metrics are derived from signal stability and consistency
  1. Data Storage: User Action → pushDataToMongo → API Request → MongoDB Storage
  • User clicks "Save Data to MongoDB"
  • pushDataToMongo function prepares data object with metrics and timestamp
  • API request is sent to /api/handle-record endpoint
  • Backend processes and stores data in MongoDB
  • Success/failure response is returned to frontend
  1. Historical Data Retrieval: Page Load → fetchLastRecordTime → API Request → Data Processing → UI Update
  • After entering Subject ID, fetchLastRecordTime function is called
  • GET request is sent to /api/handle-record endpoint
  • Backend retrieves recent records and calculates averages
  • Response data updates lastRecordTime, historicalAvgs, and historicalData states
  • UI components re-render with the updated data

Backend Processing Flow

  1. Record Handling: API Request → Request Validation → Database Operation → Response Formation
  • API route handler receives GET or POST request
  • Request method and body are validated
  • For POST: New record is created and saved to database
  • For GET: Recent 50 records are retrieved and processed
  • Response object is formed with appropriate status and data
  • Response is sent back to client
  1. Data Aggregation: Database Query → Record Filtering → Metric Calculation → Response Preparation
  • Database is queried for 50 most recent records that are not 0
  • Average heart rate and HRV are calculated and display in metric cards
  • Data is structured for frontend visualization

API Endpoints

/api/handle-record

Methods: GET, POST

GET Request:

  • Purpose: Retrieve historical data and statistics for a specific subject
  • Parameters:
    • subjectId: The identifier for the subject whose data is being requested
  • Response:
    {
      "success": true,
      "_id": null,
      "avgHeartRate": 58.6,
      "avgHRV": 292,
      "lastRecordTime": "2025-03-03T17:38:51.113Z",
      "historicalData": [
          {
              "heartRate": {
                  "bpm": 43
              },
              "hrv": {
                  "sdnn": 60
              },
              "_id": "67c5d6e847c20a0430fde691",
              "timestamp": "2025-03-03T16:20:56.950Z"
          },
          {
              "heartRate": {
                  "bpm": 50
              },
              "hrv": {
                  "sdnn": 282
              },
              "_id": "67c5d6dd47c20a0430fde68c",
              "timestamp": "2025-03-03T16:20:45.607Z"
          },
          {
              "heartRate": {
                  "bpm": 70
              },
              "hrv": {
                  "sdnn": 415
              },
              "_id": "67c5d6d847c20a0430fde687",
              "timestamp": "2025-03-03T16:20:40.099Z"
          },
          {...}
      ]
    }

POST Request:

  • Purpose: Save new PPG record to database
  • Body:
    {
      "subjectId": "S001",
      "heartRate": {
        "bpm": 75,
        "confidence": 85
      },
      "hrv": {
        "sdnn": 48,
        "confidence": 80
      },
      "ppgData": [0.5, 0.52, 0.54, ...],
      "timestamp": "2025-03-01T14:30:45.123Z"
    }
  • Response:
    {
        "success": true,
        "data": {
            "subjectId": "S001",
            "heartRate": {
                "bpm": 37,
                "confidence": 0
            },
            "hrv": {
                "sdnn": 0,
                "confidence": 0
            },
            "ppgData": [
                0,
                255,
                255,
                ...
            ],
            "timestamp": "2025-03-03T17:38:51.113Z",
            "_id": "67c5e92bb73962f68c0297e5",
            "__v": 0
        }
    }

Database Schema

Record Model

Collection: ppgrecords

Schema:

{
  subjectId: { type: String, required: true, index: true },
  heartRate: {
    bpm: { type: Number, required: true },
    confidence: { type: Number, default: 0 }
  },
  hrv: {
    sdnn: { type: Number, required: true },
    confidence: { type: Number, default: 0 }
  },
  ppgData: { type: [Number], required: true },
  signalQuality: { type: String, enum: ['Excellent', 'Acceptable', 'Bad'] },
  timestamp: { type: Date, default: Date.now },
  metadata: {
    signalCombination: { type: String, default: 'default' },
    deviceInfo: { type: String },
    sessionDuration: { type: Number }
  }
}

Signal Processing

The application extracts PPG signals from video frames using the following process:

  1. Color Channel Extraction
  • Uses 5 sampling points on the video frame: top-left, top-right, center, bottom-left, bottom-right
  • For each sampling point:
    • Extracts RGB values
    • Accumulates R, G, B values separately (rSum, gSum, bSum)
    • Validates coordinates to ensure they are within canvas bounds
  1. Signal Filtering
  • Maintains a sliding window of 300 most recent PPG signal values
  • Scale all singal values to a 0-1 range
  • Valid sample counting ensures that only frames with sufficient data points are processed
  1. Valley Detection:
  • A window size of 0.5 seconds (based on the current FPS) is used to determine the context for each potential valley point
  • Set a minimum distance of 0.4 seconds between detected valleys to prevent false positives
  • Each detected valley is recorded with its timestamp, signal value, and index in the data array
  • The valley detection algorithm works on normalized signal values to improve consistency across different lighting conditions
  1. Heart Rate Calculation
  • The heart rate calculation requires at least two detected valleys to establish a meaningful interval
  • Time intervals between consecutive valleys are calculated and converted to seconds for processing
  • Filters for valid intervals between 0.4 and 2.0 seconds, corresponding to heart rates between 30 and 150 BPM
  • The median interval is used to calculate the final BPM value
  • A confidence score is calculated based on the Coefficient of Variation, where lower variation results in higher confidence
  1. HRV
  • The Standard Deviation of NN intervals is calculated as the primary HRV metric
  • It requires at least two valleys and filters for valid RR intervals between 250 and 2000 milliseconds
  • Finds the mean of all valid intervals and then computing the standard deviation
  • The confidence score is calculate by adding up both the quantity of valid intervals and their consistency

Machine Learning Model Integration

The application integrates a 1D CNN-based auto tuned machine learning model for signal quality assessment. The model is trained on a dataset of PPG signals and their corresponding quality labels. It's then used to predict the quality of a given PPG signal with a length of 300 data points.

For detailed dataset collection, labeling, and model training, please refer to the the Jupyter notebook ppg-classifier-auto-tuning.ipynb in the root directory, there are detailed instructions on each step.

As long as you are not modifying the format of input data, which is a 300-length array of floating point numbers, and not modifying the output labels, you should be able to train and integrate the model as following:

  1. Run auto-tune to obtain the optimal model:

    # Tuner initialization
     tuner = kt.Hyperband(
         build_tunable_model,
         objective='val_accuracy',
         max_epochs=200,
         factor=3,
         directory='auto_tuning',
         project_name='ppg_cnn'
     )
    
     # Early stop
     early_stop = tf.keras.callbacks.EarlyStopping(
         monitor='val_loss',
         patience=20,
         restore_best_weights=True
     )
    
     # Tuning
     tuner.search(
         X_train, y_train,
         epochs=200,
         validation_split=0.2,
         callbacks=[early_stop],
         batch_size=32,
         verbose=1
     )
    
     best_model = tuner.get_best_models(num_models=1)[0]
    
     best_hp = tuner.get_best_hyperparameters()[0]
     print("Best Hyperparameters:")
     for hp, value in best_hp.values.items():
         print(f"{hp}: {value}")
    
     # Evaluation of the best model
     test_loss, test_acc = best_model.evaluate(X_test, y_test, verbose=0)
     print(f"\nOptimized Test Accuracy: {test_acc:.4f}")
     print(f"Optimized Test Loss: {test_loss:.4f}")
    
     # Save the optimal model
     best_model.save('optimized_cnn_model.h5')
     tfjs.converters.save_keras_model(best_model, 'tfjs_model')

    Then you should able to find a tfjs_model folder in the root directory, which contains the optimized model.

  2. Replace public\tfjs_model with the model folder generated by the auto-tuning process.

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

0