GitHub - jinseok19/Remote_PPG: 🫀비접촉 방식 심박수 및 호흡수 실시간 측정 시스템(Remote PPG & RSP) 🫀 · GitHub
Skip to content

About

🫀비접촉 방식 심박수 및 호흡수 실시간 측정 시스템(Remote PPG & RSP) 🫀

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

Remote PPG & RSP 🫀

비접촉 방식 심박수 및 호흡수 실시간 측정 시스템

Remote PPG Demo

웹캠만으로 사용자의 심박수(Heart Rate)와 호흡수(Respiratory Rate)를 실시간으로 측정하는 컴퓨터 비전 기반 헬스케어 애플리케이션입니다.


📋 Project Overview

본 프로젝트는 **Remote Photoplethysmography (rPPG)**와 Remote Respiratory Signal Processing (rRSP) 기술을 활용하여:

  • 👤 얼굴 피부색 변화를 통한 심박수 측정
  • 🫁 상체 미세 움직임을 통한 호흡수 측정

을 구현한 비침습적 생체신호 모니터링 시스템입니다.

핵심 가치

  • ✅ 비접촉 측정: 센서 없이 웹캠만으로 측정 가능
  • ✅ 실시간 처리: 30fps로 즉각적인 피드백 제공
  • ✅ 정확한 신호처리: FFT, Bandpass Filter 등 고급 DSP 기법 적용
  • ✅ 딥러닝 통합: ONNX 모델 활용한 호흡 패턴 분석

🛠 Tech Stack

Category Technology Purpose
Language Python 3.8+ 메인 개발 언어
Computer Vision OpenCV 영상 처리 및 시각화
Pose Estimation MediaPipe 얼굴/신체 랜드마크 감지
Signal Processing SciPy FFT, Bandpass Filter 등 신호처리
Deep Learning ONNX Runtime 호흡 신호 예측 모델 추론
Numerical Computing NumPy 행렬 연산 및 데이터 처리

🏗 Architecture

graph TB
    subgraph Input["📹 Input Layer"]
        Camera["Webcam Feed<br/>(30fps)"]
    end
    
    subgraph Detection["🎯 Detection Layer"]
        Mediapipe["MediaPipe Pose<br/>Landmark Detection"]
        Face["Face ROI<br/>(Forehead)"]
        Torso["Torso ROI<br/>(Chest)"]
    end
    
    subgraph Processing["⚙️ Signal Processing Layer"]
        direction TB
        
        subgraph PPG["rPPG Pipeline"]
            PPG1["Skin Mask<br/>(YCrCb)"]
            PPG2["Color Channel<br/>Averaging"]
            PPG3["Detrend +<br/>Bandpass Filter"]
            PPG4["FFT Analysis<br/>(42-180 BPM)"]
        end
        
        subgraph RSP["rRSP Pipeline"]
            RSP1["ONNX Model<br/>Prediction"]
            RSP2["Cumulative Signal<br/>Buffering"]
            RSP3["Detrend +<br/>Lowpass Filter"]
            RSP4["FFT Analysis<br/>(10-50 BPM)"]
        end
    end
    
    subgraph Output["📊 Output Layer"]
        Viz["Real-time<br/>Visualization"]
        BPM["BPM Values<br/>(Heart & Resp)"]
    end
    
    Camera --> Mediapipe
    Mediapipe --> Face
    Mediapipe --> Torso
    
    Face --> PPG1
    PPG1 --> PPG2
    PPG2 --> PPG3
    PPG3 --> PPG4
    
    Torso --> RSP1
    RSP1 --> RSP2
    RSP2 --> RSP3
    RSP3 --> RSP4
    
    PPG4 --> Viz
    RSP4 --> Viz
    PPG4 --> BPM
    RSP4 --> BPM
    
    style Camera fill:#e1f5ff
    style Mediapipe fill:#fff4e1
    style PPG fill:#ffe1f5
    style RSP fill:#e1ffe1
    style Viz fill:#f5e1ff
    style BPM fill:#f5e1ff
Loading

Data Flow

  1. 영상 입력: 웹캠에서 실시간 프레임 수집 (30fps)
  2. 랜드마크 감지: MediaPipe로 33개 신체 키포인트 추출
  3. ROI 추출:
    • rPPG: 이마 중심 얼굴 영역
    • rRSP: 어깨~골반 상체 영역
  4. 신호 처리:
    • rPPG: YCrCb 색공간 변환 → 평균값 계산 → 대역통과 필터링
    • rRSP: ONNX 모델로 프레임 간 움직임 예측 → 누적 신호 생성
  5. BPM 계산: FFT로 주파수 분석 → 피크 주파수를 BPM으로 변환
  6. 시각화: 신호 파형 + BPM 수치 실시간 표시

🚀 Getting Started

Prerequisites

  • Python 3.8 이상
  • 웹캠 (또는 비디오 입력 장치)
  • Windows/Linux/macOS

Installation

1. 저장소 클론

git clone https://github.com/YOUR_USERNAME/Remote_PPG.git
cd Remote_PPG

2. 가상환경 생성 (권장)

# Windows
python -m venv venv
venv\Scripts\activate

# Linux/macOS
python3 -m venv venv
source venv/bin/activate

3. 의존성 설치

pip install -r requirements.txt

⚠️ 중요: TensorFlow가 설치되어 있으면 MediaPipe와 protobuf 버전 충돌이 발생합니다.

# TensorFlow 제거 (이 프로젝트에서 사용하지 않음)
pip uninstall tensorflow tensorflow-intel -y

GPU 가속 사용 시 (NVIDIA CUDA 지원):

pip uninstall onnxruntime
pip install onnxruntime-gpu

4. 실행

python main.py

🎮 Usage

기본 실행

python main.py

키보드 단축키

Key Action
1 rPPG + rRSP 모두 활성화 (기본값)
2 rPPG만 활성화 (심박수만 측정)
3 rRSP만 활성화 (호흡수만 측정)
Space 얼굴/신체 감지 On/Off
ESC 프로그램 종료

실행 화면 예시

Remote PPG & RSP 실행 화면

🎬 실시간 측정 화면

구분 설명
🟨 노란색 박스 얼굴 ROI - 심박수(rPPG) 측정 영역
🟥 빨간색 박스 상체 ROI - 호흡수(rRSP) 측정 영역
📊 상단 그래프 rPPG 신호 파형 → 072 BPM
📈 하단 그래프 rRSP 신호 파형 → 014 BPM
⚡ 좌측 상단 실시간 FPS 표시 (20 fps)

📂 Project Structure

Remote_PPG/
│
├── main.py                          # 메인 실행 파일
├── requirements.txt                 # Python 의존성 패키지 목록
├── README.md                        # 프로젝트 문서
│
├── detector/                        # 신호 감지 및 처리 모듈
│   ├── __init__.py
│   ├── landmark.py                  # MediaPipe 랜드마크 감지
│   ├── rppg.py                      # 심박수 측정 (rPPG)
│   └── rrsp.py                      # 호흡수 측정 (rRSP)
│
├── model/
│   └── onnx_model.onnx              # 호흡 신호 예측 딥러닝 모델
│
├── utils.py                         # 신호 시각화 유틸리티
└── timer.py                         # FPS 측정 및 타이머 관리

주요 파일 설명

main.py

  • OpenCV로 웹캠 영상 캡처
  • Detector, rPPG, rRSP 모듈 초기화 및 제어
  • 실시간 시각화 및 사용자 입력 처리

detector/landmark.py

  • MediaPipe Pose를 활용한 신체 랜드마크 감지
  • get_face_rect(): 얼굴 영역 좌표 반환
  • get_torso_rect(): 상체 영역 좌표 반환

detector/rppg.py

  • 알고리즘: YCrCb 색공간에서 피부 영역의 Cr, Cb 채널 평균값 추출
  • 신호처리: Detrend → Bandpass Filter (42-180 BPM)
  • BPM 계산: FFT로 주파수 스펙트럼 분석

detector/rrsp.py

  • 알고리즘: ONNX 모델로 프레임 간 움직임 맵 예측
  • 신호처리: 누적 신호 생성 → Detrend → Lowpass Filter
  • BPM 계산: FFT로 10-50 BPM 범위에서 피크 검출

utils.py

  • draw_signal(): 신호 파형을 OpenCV 이미지로 렌더링

timer.py

  • Timer.get_fps(): 실시간 FPS 계산
  • Timer.check_sec_ppg/rsp(): 1초 간격 체크 (BPM 업데이트 주기 제어)

🧪 Core Logic & API Reference

1. rPPG (Remote Photoplethysmography)

원리

심장 박동에 따라 혈액량이 변화하면서 피부색이 미세하게 변합니다. 이를 웹캠으로 캡처하여 분석합니다.

처리 파이프라인

# 1. 피부 마스크 생성 (YCrCb 색공간)
mask = cv2.inRange(ycrcb, low=[0,133,77], high=[235,173,127])

# 2. Cr, Cb 채널 평균값 계산
ppg_value = (sum(Cr) + sum(Cb)) / n_pixels

# 3. Detrend (이동평균 제거)
detrended = (signal - moving_avg) / (moving_avg + 1e-15)

# 4. Bandpass Filter (42-180 BPM = 0.7-3 Hz)
filtered = butter_bandpass_filter(detrended, lowcut=0.7, highcut=3, fs=30)

# 5. FFT로 BPM 계산
fft = np.fft.rfft(windowed_signal, n=zero_padded_length)
bpm = freq[argmax(abs(fft))] * 60

주요 파라미터

Parameter Value Description
DURATION_RAW 5초 신호 버퍼 길이
BPM_BAND (42, 180) 유효 BPM 범위
MAX_FPS 30 최대 프레임레이트

2. rRSP (Remote Respiratory Signal Processing)

원리

호흡에 따른 흉부의 미세한 움직임을 딥러닝 모델로 감지하여 신호로 변환합니다.

처리 파이프라인

# 1. ONNX 모델 입력 생성 (연속 프레임 쌍)
curr_frame = resize(frame_t, (200, 200)) / 255.0
prev_frame = resize(frame_t-1, (200, 200)) / 255.0

# 2. 모델 예측 (움직임 맵)
motion_val = onnx_model([prev_frame, curr_frame]).mean()

# 3. 누적 신호 생성
cumulative_signal[t] = cumulative_signal[t-1] + motion_val

# 4. Detrend + Lowpass Filter
detrended = signal - moving_average(signal, kernel=6s)
lowpassed = convolve(detrended, kernel=0.2s)

# 5. FFT로 BPM 계산
fft = np.fft.rfft(zero_padded_signal)
bpm = freq[argmax(abs(fft))] * 60  # 10-50 BPM 범위에서만

주요 파라미터

Parameter Value Description
DURATION_MIN_BUFFER 10초 최소 신호 버퍼 길이
DURATION_CALCULATE_BPM 17초 BPM 계산에 사용되는 신호 길이
BPM_BAND (10, 50) 유효 호흡수 범위
MAX_INPUT_SIZE 200 ONNX 모델 입력 크기

🔧 Troubleshooting

문제: "No face detected" 메시지가 계속 뜹니다

원인: MediaPipe가 랜드마크를 감지하지 못함

해결:

  • 조명이 충분한지 확인
  • 카메라와 1-2m 거리 유지
  • 상체 전체가 프레임에 들어오도록 조정
  • landmark.py의 min_detection_confidence를 낮춤 (예: 0.3)

문제: BPM 값이 0으로 나옵니다

원인: 신호 버퍼가 채워지지 않았거나 잘못된 범위

해결:

  • rPPG: 최소 5초 대기 (버퍼 충전 시간)
  • rRSP: 최소 10초 대기
  • 피부색이 잘 보이는 조명 확인
  • 움직임을 최소화하고 정지 상태 유지

문제: FPS가 너무 낮습니다 (<15fps)

원인: CPU 성능 부족 또는 ONNX 모델 추론 느림

해결:

# GPU 가속 버전 설치 (NVIDIA GPU 필요)
pip uninstall onnxruntime
pip install onnxruntime-gpu

또는 rRSP 비활성화 후 rPPG만 사용 (키보드 2 입력)


문제: ModuleNotFoundError 발생

해결:

pip install -r requirements.txt

문제: ImportError: cannot import name 'runtime_version' from 'google.protobuf'

원인: TensorFlow와 MediaPipe의 protobuf 버전 충돌

해결:

# TensorFlow 제거 (이 프로젝트에서 사용하지 않음)
pip uninstall tensorflow tensorflow-intel -y

# MediaPipe 재설치
pip install mediapipe==0.10.14

📊 Performance & Accuracy

측정 환경

  • CPU: Intel i5-9400F
  • RAM: 16GB
  • 웹캠: Logitech C920 (1080p@30fps)

성능 지표

Metric rPPG rRSP
평균 FPS 28-30 18-22
초기화 시간 5초 10초
BPM 정확도 ±5 BPM ±3 BPM
측정 범위 42-180 BPM 10-50 BPM

⚠️ 주의: 본 시스템은 연구/교육용으로, 의료 진단 목적으로 사용할 수 없습니다.


🔬 Technical Details

신호처리 기법

Detrend

이동평균을 제거하여 저주파 노이즈(조명 변화, 움직임)를 제거합니다.

norm = np.convolve(np.ones(len(signal)), np.ones(window), mode='same')
mean = np.convolve(signal, np.ones(window), mode='same') / norm
detrended = (signal - mean) / (mean + 1e-15)

Bandpass Filter (rPPG)

심박수 유효 범위(42-180 BPM = 0.7-3 Hz)만 통과시킵니다.

nyquist = 60 * fps / 2
sos = signal.butter(5, [42/nyquist, 180/nyquist], 'bandpass')
filtered = signal.filtfilt(sos, signal)

FFT with Zero Padding

주파수 해상도를 높이기 위해 신호를 제로패딩합니다.

windowed = signal * np.hanning(len(signal))
padded = np.pad(windowed, (0, len(windowed) * 9))
fft = np.fft.rfft(padded)

🤝 Contributing

프로젝트 개선을 위한 기여를 환영합니다!

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

📜 License

이 프로젝트는 MIT 라이선스 하에 배포됩니다. 자세한 내용은 LICENSE 파일을 참조하세요.


🙏 Acknowledgments

  • MediaPipe: Google의 실시간 ML 솔루션
  • ONNX Runtime: 크로스 플랫폼 모델 추론 엔진
  • SciPy: 과학 컴퓨팅 라이브러리

📧 Contact

프로젝트 관련 문의: [이메일 주소 또는 GitHub Issues]

Made with ❤️ for Non-contact Health Monitoring

About

🫀비접촉 방식 심박수 및 호흡수 실시간 측정 시스템(Remote PPG & RSP) 🫀

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages