From 5deadaf428c127dd5895e04e94ac8429a766af96 Mon Sep 17 00:00:00 2001 From: zuyua9 Date: Fri, 8 May 2026 01:35:55 +0800 Subject: [PATCH 1/2] fix(face): reuse pre-detected face list --- modules/face_analyser.py | 13 ++-- tests/test_face_analyser_get_one_face.py | 97 ++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 tests/test_face_analyser_get_one_face.py diff --git a/modules/face_analyser.py b/modules/face_analyser.py index 71e1cc2..355a6af 100644 --- a/modules/face_analyser.py +++ b/modules/face_analyser.py @@ -138,15 +138,16 @@ def _analyse_faces(frame: Frame) -> list: return faces -def get_one_face(frame: Frame) -> Any: - if _is_dml(): - with modules.globals.dml_lock: +def get_one_face(frame: Frame, faces: Any = None) -> Any: + if faces is None: + if _is_dml(): + with modules.globals.dml_lock: + faces = _analyse_faces(frame) + else: faces = _analyse_faces(frame) - else: - faces = _analyse_faces(frame) try: return min(faces, key=lambda x: x.bbox[0]) - except ValueError: + except (TypeError, ValueError): return None diff --git a/tests/test_face_analyser_get_one_face.py b/tests/test_face_analyser_get_one_face.py new file mode 100644 index 0000000..a5146be --- /dev/null +++ b/tests/test_face_analyser_get_one_face.py @@ -0,0 +1,97 @@ +import importlib +import sys +import types +import unittest +from unittest.mock import patch + + +def _install_import_stubs(): + sys.modules.setdefault( + "insightface", + types.SimpleNamespace(app=types.SimpleNamespace(FaceAnalysis=object)), + ) + sys.modules.setdefault( + "cv2", + types.SimpleNamespace( + IMREAD_COLOR=1, + imread=lambda *_args, **_kwargs: None, + imdecode=lambda *_args, **_kwargs: None, + imencode=lambda *_args, **_kwargs: ( + True, + types.SimpleNamespace(tofile=lambda *_a, **_k: None), + ), + ), + ) + sys.modules.setdefault( + "numpy", + types.SimpleNamespace(uint8=object, fromfile=lambda *_args, **_kwargs: b""), + ) + sys.modules.setdefault( + "tqdm", + types.SimpleNamespace(tqdm=lambda iterable, **_kwargs: iterable), + ) + sys.modules["modules.typing"] = types.SimpleNamespace(Frame=object) + sys.modules["modules.cluster_analysis"] = types.SimpleNamespace( + find_cluster_centroids=lambda *args, **kwargs: [], + find_closest_centroid=lambda *args, **kwargs: (0, None), + ) + sys.modules["modules.utilities"] = types.SimpleNamespace( + get_temp_directory_path=lambda path: path, + create_temp=lambda path: None, + extract_frames=lambda path: None, + clean_temp=lambda path: None, + get_temp_frame_paths=lambda path: [], + ) + + +def _load_face_analyser(): + _install_import_stubs() + sys.modules.pop("modules.face_analyser", None) + return importlib.import_module("modules.face_analyser") + + +class Face: + def __init__(self, left): + self.bbox = [left, 0, 10, 10] + + +class GetOneFaceTests(unittest.TestCase): + def test_uses_supplied_detected_faces_without_reanalysing_frame(self): + face_analyser = _load_face_analyser() + right = Face(20) + left = Face(5) + + with patch.object( + face_analyser, + "_analyse_faces", + side_effect=AssertionError("should not analyse"), + ): + self.assertIs(face_analyser.get_one_face("frame", [right, left]), left) + + def test_supplied_empty_detected_faces_returns_none(self): + face_analyser = _load_face_analyser() + + with patch.object( + face_analyser, + "_analyse_faces", + side_effect=AssertionError("should not analyse"), + ): + self.assertIsNone(face_analyser.get_one_face("frame", [])) + + def test_without_supplied_faces_preserves_existing_detection_path(self): + face_analyser = _load_face_analyser() + right = Face(30) + left = Face(3) + + with patch.object(face_analyser, "_is_dml", return_value=False), patch.object( + face_analyser, + "_analyse_faces", + return_value=[right, left], + ) as analyse: + self.assertIs(face_analyser.get_one_face("frame"), left) + + analyse.assert_called_once_with("frame") + + +if __name__ == "__main__": + unittest.main() From d1376b07d187b4bddaf3600cc658f778da4a3295 Mon Sep 17 00:00:00 2001 From: zuyua9 Date: Fri, 8 May 2026 01:50:25 +0800 Subject: [PATCH 2/2] fix(face): avoid hiding invalid face inputs --- modules/face_analyser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/face_analyser.py b/modules/face_analyser.py index 355a6af..754cd9c 100644 --- a/modules/face_analyser.py +++ b/modules/face_analyser.py @@ -147,7 +147,7 @@ def get_one_face(frame: Frame, faces: Any = None) -> Any: faces = _analyse_faces(frame) try: return min(faces, key=lambda x: x.bbox[0]) - except (TypeError, ValueError): + except ValueError: return None