Skip to content

Commit

Permalink
Add option for limiting FPS (#4)
Browse files Browse the repository at this point in the history
* reworking camera timing
* add command line argument for limitfps
* adding delay-frames CLI option
* add temporary fix with delay when limiting frames
* updated readme
  • Loading branch information
SamProell authored Sep 30, 2022
1 parent 70d61eb commit a1c27b1
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 15 deletions.
12 changes: 7 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Yet another rPPG
================

*\* This is a work in progress! \**
-----------------------------------
*\* This is a work in progress, do not blindly trust the results \**
--------------------------------------------------------------------


**yarppg** is yet another implementation of remote photoplethysmography in
Expand Down Expand Up @@ -67,11 +67,13 @@ There are a number of options available, when running yarppg:
+-----------------+----------+----------------------------------------------------------------+
| --draw-facemark | False | draw landmarks when using facemesh detector |
+-----------------+----------+----------------------------------------------------------------+
| --blur | -1 | pixelation size of detected ROI |
| --blur | -1 | pixelation size of detected ROI |
+-----------------+----------+----------------------------------------------------------------+
| --video | 0 | video input device number |
| --video | 0 | video input device number or filename |
+-----------------+----------+----------------------------------------------------------------+
| --savepath | '' | store generated signals as data frame to disk |
| --limitfps | None | force a delay when reading frames (specified in milliseconds) |
+-----------------+----------+----------------------------------------------------------------+
| --savepath | '' | store generated signals as data frame to disk |
+-----------------+----------+----------------------------------------------------------------+

Camera setup
Expand Down
9 changes: 5 additions & 4 deletions yarppg/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@


from PyQt5.QtWidgets import QApplication
from yarppg.rppg.camera import Camera
from yarppg.ui import MainWindow
from yarppg.rppg import RPPG
from yarppg.rppg.processors import ColorMeanProcessor, FilteredProcessor
from yarppg.rppg.hr import HRCalculator
from yarppg.rppg.filters import get_butterworth_filter
from yarppg.ui.cli import (get_detector, get_mainparser, get_processor,
parse_frequencies)
parse_frequencies, get_delay)


def main():
Expand All @@ -31,11 +32,11 @@ def main():
digital_bandpass = get_butterworth_filter(30, cutoff, "bandpass")
processor = FilteredProcessor(processor, digital_bandpass)


cam = Camera(video=args.video, limit_fps=get_delay(args))
rppg = RPPG(roi_detector=roi_detector,
video=args.video,
camera=cam,
hr_calculator=hr_calc,
parent=app,
parent=None,
)
rppg.add_processor(processor)
for c in "rgb":
Expand Down
10 changes: 9 additions & 1 deletion yarppg/rppg/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,37 @@ class Camera(QThread):

frame_received = pyqtSignal(np.ndarray)

def __init__(self, video=0, parent=None):
def __init__(self, video=0, parent=None, limit_fps=None):
"""Initialize Camera instance
Args:
video (int or string): ID of camera or video filename
parent (QObject): parent object in Qt context
limit_fps (float): force FPS limit, delay read if necessary.
"""

QThread.__init__(self, parent=parent)
self._cap = cv2.VideoCapture(video)
self._running = False
self._delay = 1 / limit_fps - 0.012 if limit_fps else np.nan
# subtracting a roughly constant delay of 12ms TODO: better way?
# np.nan will always evaluate to False in a comparison

def run(self):
self._running = True
while self._running:
ret, frame = self._cap.read()
last_time = time.perf_counter()

if not ret:
self._running = False
raise RuntimeError("No frame received")
else:
self.frame_received.emit(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))

while (time.perf_counter() - last_time) < self._delay:
time.sleep(0.001)

def stop(self):
self._running = False
time.sleep(0.1)
Expand Down
9 changes: 4 additions & 5 deletions yarppg/rppg/rppg.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from datetime import datetime
import pathlib

import cv2
import numpy as np
import pandas as pd
from PyQt5.QtCore import pyqtSignal, QObject
Expand Down Expand Up @@ -35,14 +34,14 @@ class RPPG(QObject):
rppg_updated = pyqtSignal(RppgResults)
_dummy_signal = pyqtSignal(float)

def __init__(self, roi_detector, parent=None, video=0,
def __init__(self, roi_detector, parent=None, camera=None,
hr_calculator=None):
QObject.__init__(self, parent)
self.roi = None
self._processors = []
self._roi_detector = roi_detector

self._set_camera(video)
self._set_camera(camera)

self._dts = []
self.last_update = datetime.now()
Expand All @@ -57,8 +56,8 @@ def __init__(self, roi_detector, parent=None, video=0,

self.output_filename = None

def _set_camera(self, video):
self._cam = Camera(video=video, parent=self)
def _set_camera(self, camera):
self._cam = camera or Camera(video=0, parent=self)
self._cam.frame_received.connect(self.on_frame_received)

def add_processor(self, processor):
Expand Down
11 changes: 11 additions & 0 deletions yarppg/ui/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,21 @@ def get_mainparser():
parser.add_argument("--video", default=0, help="video input device number")
parser.add_argument("--savepath", default="", type=str,
help="store generated signals as data frame")
parser.add_argument("--limitfps", type=float, default=None,
help="limit FPS to specified maximum")
parser.add_argument("--delay-frames", type=float, default=None,
help=("add a delay of specified number of milliseconds"
" (overrides --limitfps)"))

return parser


def get_delay(args):
if args.delay_frames is not None:
return 1000.0 / args.delay_frames

return args.limitfps

def get_detector(args):
name = args.detector.lower()

Expand Down

0 comments on commit a1c27b1

Please sign in to comment.