Skip to content

Commit

Permalink
Add new function that checks whether faces are sufficiently in ROI
Browse files Browse the repository at this point in the history
  • Loading branch information
prouast committed Jul 23, 2024
1 parent 2217d45 commit 3733269
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 2 deletions.
21 changes: 19 additions & 2 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

from vitallens.client import Method
from vitallens.utils import load_config, probe_video_inputs, parse_video_inputs
from vitallens.utils import merge_faces, check_faces
from vitallens.utils import merge_faces, check_faces, check_faces_in_roi

@pytest.mark.parametrize("method", [m for m in Method])
def test_load_config(method):
Expand Down Expand Up @@ -152,4 +152,21 @@ def test_check_faces_n_frames_n_dets_not_matching_2():
def test_check_faces_invalid_dets():
with pytest.raises(Exception):
_ = check_faces([1, 3, 2, 2], inputs_shape=(2, 10, 10, 3))


@pytest.mark.parametrize("scenario", [([[0.4, 0.4, 0.6, 0.6], [0.4, 0.4, 0.6, 0.6]], [0.3, 0.3, 0.7, 0.7], (0.5, 0.5), True), # Fully in ROI
([[0.2, 0.4, 0.6, 0.6], [0.4, 0.4, 0.6, 0.6]], [0.3, 0.3, 0.7, 0.7], (0.5, 0.5), True), # Slightly out to left but ok
([[0.0, 0.4, 0.4, 0.6], [0.4, 0.4, 0.6, 0.6]], [0.3, 0.3, 0.7, 0.7], (0.5, 0.5), False), # Too much out to left
([[0.5, 0.4, 0.9, 0.6], [0.4, 0.4, 0.6, 0.6]], [0.3, 0.3, 0.7, 0.7], (0.2, 0.5), True), # Slightly out to right but ok
([[0.5, 0.4, 0.9, 0.6], [0.4, 0.4, 0.6, 0.6]], [0.3, 0.3, 0.7, 0.7], (0.6, 0.5), False), # Too much out to right
([[0.4, 0.2, 0.6, 0.5], [0.4, 0.4, 0.6, 0.6]], [0.3, 0.3, 0.7, 0.7], (0.5, 0.5), True), # Slightly out to top but ok
([[0.4, 0.1, 0.6, 0.5], [0.4, 0.4, 0.6, 0.6]], [0.3, 0.3, 0.7, 0.7], (0.5, 0.6), False), # Too much out to top
([[0.4, 0.5, 0.6, 0.8], [0.4, 0.4, 0.6, 0.6]], [0.3, 0.3, 0.7, 0.7], (0.5, 0.5), True), # Slightly out to bottom but ok
([[0.4, 0.6, 0.6, 0.9], [0.4, 0.4, 0.6, 0.6]], [0.3, 0.3, 0.7, 0.7], (0.5, 0.5), False), # Too much out to bottom
])
def test_check_faces_in_roi(scenario):
faces = np.asarray(scenario[0])
roi = np.asarray(scenario[1])
percentage_required_inside_roi = scenario[2]
expected = scenario[3]
actual = check_faces_in_roi(faces=faces, roi=roi, percentage_required_inside_roi=percentage_required_inside_roi)
assert expected == actual
25 changes: 25 additions & 0 deletions vitallens/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,31 @@ def check_faces(
raise ValueError("Face detections are invalid, should be in form [x0, y0, x1, y1]")
return faces

def check_faces_in_roi(
faces: np.ndarray,
roi: np.ndarray,
percentage_required_inside_roi: tuple = (0.5, 0.5)
) -> bool:
"""Check whether all faces are sufficiently inside the ROI.
Args:
faces: The faces. Shape (n_faces, 4) in form (x0, y0, x1, y1)
roi: The region of interest. Shape (4,) in form (x0, y0, x1, y1)
percentage_required_inside_roi: Tuple (w, h) indicating what percentage
of width/height of face is required to remain inside the ROI.
Returns:
out: True if all faces are sufficiently inside the ROI.
"""
faces_w = faces[:,2] - faces[:,0]
faces_h = faces[:,3] - faces[:,1]
faces_inside_roi = np.logical_and(
np.logical_and(faces[:,2] - roi[0] > percentage_required_inside_roi * faces_w,
roi[2] - faces[:,0] > percentage_required_inside_roi * faces_w),
np.logical_and(faces[:,3] - roi[1] > percentage_required_inside_roi * faces_h,
roi[3] - faces[:,1] > percentage_required_inside_roi * faces_h))
facess_inside_roi = np.all(faces_inside_roi)
return facess_inside_roi

def convert_ndarray_to_list(d: Union[dict, list, np.ndarray]):
"""Recursively convert any np.ndarray to list in nested object.
Expand Down

0 comments on commit 3733269

Please sign in to comment.