비접촉 방식 심박수 및 호흡수 실시간 측정 시스템
웹캠만으로 사용자의 심박수(Heart Rate)와 호흡수(Respiratory Rate)를 실시간으로 측정하는 컴퓨터 비전 기반 헬스케어 애플리케이션입니다.
본 프로젝트는 **Remote Photoplethysmography (rPPG)**와 Remote Respiratory Signal Processing (rRSP) 기술을 활용하여:
- 👤 얼굴 피부색 변화를 통한 심박수 측정
- 🫁 상체 미세 움직임을 통한 호흡수 측정
을 구현한 비침습적 생체신호 모니터링 시스템입니다.
- ✅ 비접촉 측정: 센서 없이 웹캠만으로 측정 가능
- ✅ 실시간 처리: 30fps로 즉각적인 피드백 제공
- ✅ 정확한 신호처리: FFT, Bandpass Filter 등 고급 DSP 기법 적용
- ✅ 딥러닝 통합: ONNX 모델 활용한 호흡 패턴 분석
| 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 | 행렬 연산 및 데이터 처리 |
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
- 영상 입력: 웹캠에서 실시간 프레임 수집 (30fps)
- 랜드마크 감지: MediaPipe로 33개 신체 키포인트 추출
- ROI 추출:
- rPPG: 이마 중심 얼굴 영역
- rRSP: 어깨~골반 상체 영역
- 신호 처리:
- rPPG: YCrCb 색공간 변환 → 평균값 계산 → 대역통과 필터링
- rRSP: ONNX 모델로 프레임 간 움직임 예측 → 누적 신호 생성
- BPM 계산: FFT로 주파수 분석 → 피크 주파수를 BPM으로 변환
- 시각화: 신호 파형 + BPM 수치 실시간 표시
- Python 3.8 이상
- 웹캠 (또는 비디오 입력 장치)
- Windows/Linux/macOS
git clone https://github.com/YOUR_USERNAME/Remote_PPG.git
cd Remote_PPG# Windows
python -m venv venv
venv\Scripts\activate
# Linux/macOS
python3 -m venv venv
source venv/bin/activatepip install -r requirements.txt
⚠️ 중요: TensorFlow가 설치되어 있으면 MediaPipe와 protobuf 버전 충돌이 발생합니다.# TensorFlow 제거 (이 프로젝트에서 사용하지 않음) pip uninstall tensorflow tensorflow-intel -y
GPU 가속 사용 시 (NVIDIA CUDA 지원):
pip uninstall onnxruntime pip install onnxruntime-gpu
python main.pypython main.py| Key | Action |
|---|---|
1 |
rPPG + rRSP 모두 활성화 (기본값) |
2 |
rPPG만 활성화 (심박수만 측정) |
3 |
rRSP만 활성화 (호흡수만 측정) |
Space |
얼굴/신체 감지 On/Off |
ESC |
프로그램 종료 |
🎬 실시간 측정 화면
| 구분 | 설명 |
|---|---|
| 🟨 노란색 박스 | 얼굴 ROI - 심박수(rPPG) 측정 영역 |
| 🟥 빨간색 박스 | 상체 ROI - 호흡수(rRSP) 측정 영역 |
| 📊 상단 그래프 | rPPG 신호 파형 → 072 BPM |
| 📈 하단 그래프 | rRSP 신호 파형 → 014 BPM |
| ⚡ 좌측 상단 | 실시간 FPS 표시 (20 fps) |
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 측정 및 타이머 관리
- OpenCV로 웹캠 영상 캡처
- Detector, rPPG, rRSP 모듈 초기화 및 제어
- 실시간 시각화 및 사용자 입력 처리
- MediaPipe Pose를 활용한 신체 랜드마크 감지
get_face_rect(): 얼굴 영역 좌표 반환get_torso_rect(): 상체 영역 좌표 반환
- 알고리즘: YCrCb 색공간에서 피부 영역의 Cr, Cb 채널 평균값 추출
- 신호처리: Detrend → Bandpass Filter (42-180 BPM)
- BPM 계산: FFT로 주파수 스펙트럼 분석
- 알고리즘: ONNX 모델로 프레임 간 움직임 맵 예측
- 신호처리: 누적 신호 생성 → Detrend → Lowpass Filter
- BPM 계산: FFT로 10-50 BPM 범위에서 피크 검출
draw_signal(): 신호 파형을 OpenCV 이미지로 렌더링
Timer.get_fps(): 실시간 FPS 계산Timer.check_sec_ppg/rsp(): 1초 간격 체크 (BPM 업데이트 주기 제어)
심장 박동에 따라 혈액량이 변화하면서 피부색이 미세하게 변합니다. 이를 웹캠으로 캡처하여 분석합니다.
# 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 | 최대 프레임레이트 |
호흡에 따른 흉부의 미세한 움직임을 딥러닝 모델로 감지하여 신호로 변환합니다.
# 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 모델 입력 크기 |
원인: MediaPipe가 랜드마크를 감지하지 못함
해결:
- 조명이 충분한지 확인
- 카메라와 1-2m 거리 유지
- 상체 전체가 프레임에 들어오도록 조정
landmark.py의min_detection_confidence를 낮춤 (예: 0.3)
원인: 신호 버퍼가 채워지지 않았거나 잘못된 범위
해결:
- rPPG: 최소 5초 대기 (버퍼 충전 시간)
- rRSP: 최소 10초 대기
- 피부색이 잘 보이는 조명 확인
- 움직임을 최소화하고 정지 상태 유지
원인: CPU 성능 부족 또는 ONNX 모델 추론 느림
해결:
# GPU 가속 버전 설치 (NVIDIA GPU 필요)
pip uninstall onnxruntime
pip install onnxruntime-gpu또는 rRSP 비활성화 후 rPPG만 사용 (키보드 2 입력)
해결:
pip install -r requirements.txt원인: TensorFlow와 MediaPipe의 protobuf 버전 충돌
해결:
# TensorFlow 제거 (이 프로젝트에서 사용하지 않음)
pip uninstall tensorflow tensorflow-intel -y
# MediaPipe 재설치
pip install mediapipe==0.10.14- CPU: Intel i5-9400F
- RAM: 16GB
- 웹캠: Logitech C920 (1080p@30fps)
⚠️ 주의: 본 시스템은 연구/교육용으로, 의료 진단 목적으로 사용할 수 없습니다.
이동평균을 제거하여 저주파 노이즈(조명 변화, 움직임)를 제거합니다.
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)심박수 유효 범위(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)주파수 해상도를 높이기 위해 신호를 제로패딩합니다.
windowed = signal * np.hanning(len(signal))
padded = np.pad(windowed, (0, len(windowed) * 9))
fft = np.fft.rfft(padded)프로젝트 개선을 위한 기여를 환영합니다!
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
이 프로젝트는 MIT 라이선스 하에 배포됩니다. 자세한 내용은 LICENSE 파일을 참조하세요.
- MediaPipe: Google의 실시간 ML 솔루션
- ONNX Runtime: 크로스 플랫폼 모델 추론 엔진
- SciPy: 과학 컴퓨팅 라이브러리
프로젝트 관련 문의: [이메일 주소 또는 GitHub Issues]
Made with ❤️ for Non-contact Health Monitoring
