Skip to content

Commit

Permalink
Don't detrend for long videos; Add warning for simple rPPG methods
Browse files Browse the repository at this point in the history
  • Loading branch information
prouast committed Jul 20, 2024
1 parent c450c7e commit 781b4a4
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 3 deletions.
8 changes: 7 additions & 1 deletion vitallens/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def __init__(
method: Method = Method.VITALLENS,
api_key: str = None,
detect_faces: bool = True,
fdet_max_faces: int = 2,
fdet_max_faces: int = 1,
fdet_fs: float = 1.0,
fdet_score_threshold: float = 0.9,
fdet_iou_threshold: float = 0.3
Expand All @@ -65,6 +65,7 @@ def __init__(
self.api_key = api_key
# Load the config and model
self.config = load_config(method.name.lower() + ".yaml")
self.method = method
if self.config['model'] == 'g':
self.rppg = GRPPGMethod(self.config)
elif self.config['model'] == 'chrom':
Expand Down Expand Up @@ -151,6 +152,11 @@ def __call__(
"""
# Probe inputs
inputs_shape, fps = probe_video_inputs(video=video, fps=fps)
# TODO: Optimize performance of simple rPPG methods for long videos
# Warning if using long video
target_fps = override_fps_target if override_fps_target is not None else self.rppg.fps_target
if self.method != Method.VITALLENS and inputs_shape[0]/fps*target_fps > 3600:
logging.warn("Inference for long videos has yet to be optimized for POS / G / CHROM. This may run out of memory and crash.")
_, height, width, _ = inputs_shape
if self.detect_faces:
# Detect faces
Expand Down
7 changes: 5 additions & 2 deletions vitallens/methods/vitallens.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def __call__(
(faces[:,3] - faces[:,1]) * 0.5 < np.maximum(0, faces[:,1] - roi[1]) + np.maximum(0, faces[:,3] - roi[3]))):
logging.warn("Large face movement detected")
# Parse the inputs
logging.debug("Preparing video for inference...")
frames_ds, fps, inputs_shape, ds_factor, _ = parse_video_inputs(
video=frames, fps=fps, target_size=self.input_size, roi=roi,
target_fps=override_fps_target if override_fps_target is not None else self.fps_target,
Expand All @@ -95,6 +96,7 @@ def __call__(
split_len = math.ceil((ds_len + (n_splits-1) * API_OVERLAP) / n_splits)
start_idxs = [i for i in range(0, ds_len - n_splits * API_OVERLAP, split_len - API_OVERLAP)]
end_idxs = [min(i + split_len, ds_len) for i in start_idxs]
logging.info("Running inference for {} frames using {} requests...".format(ds_len, n_splits))
# Process the splits in parallel
with concurrent.futures.ThreadPoolExecutor() as executor:
results = list(executor.map(lambda i: self.process_api(frames_ds[start_idxs[i]:end_idxs[i]]), range(n_splits)))
Expand Down Expand Up @@ -211,8 +213,9 @@ def postprocess(self, sig, fps, type='ppg', filter=True):
Lambda = detrend_lambda_for_rr_response(fps)
else:
raise ValueError("Type {} not implemented!".format(type))
# Detrend
sig = detrend(sig, Lambda)
if sig.shape[-1] < 4 * API_MAX_FRAMES:
# Detrend only for shorter videos for performance reasons
sig = detrend(sig, Lambda)
# Moving average
sig = moving_average(sig, size)
# Standardize
Expand Down

0 comments on commit 781b4a4

Please sign in to comment.