fast-face-python 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. fast_face_python-0.1.1/LICENSE +21 -0
  2. fast_face_python-0.1.1/MANIFEST.in +6 -0
  3. fast_face_python-0.1.1/PKG-INFO +237 -0
  4. fast_face_python-0.1.1/README.md +207 -0
  5. fast_face_python-0.1.1/pyproject.toml +49 -0
  6. fast_face_python-0.1.1/setup.cfg +4 -0
  7. fast_face_python-0.1.1/src/fast_face/__init__.py +6 -0
  8. fast_face_python-0.1.1/src/fast_face/models/__init__.py +11 -0
  9. fast_face_python-0.1.1/src/fast_face/models/adaface.py +91 -0
  10. fast_face_python-0.1.1/src/fast_face/models/base.py +108 -0
  11. fast_face_python-0.1.1/src/fast_face/models/base_recognition.py +46 -0
  12. fast_face_python-0.1.1/src/fast_face/models/downloader.py +46 -0
  13. fast_face_python-0.1.1/src/fast_face/models/factory.py +69 -0
  14. fast_face_python-0.1.1/src/fast_face/models/retinaface.py +109 -0
  15. fast_face_python-0.1.1/src/fast_face/models/session.py +29 -0
  16. fast_face_python-0.1.1/src/fast_face/models/yunet.py +202 -0
  17. fast_face_python-0.1.1/src/fast_face/schema.py +13 -0
  18. fast_face_python-0.1.1/src/fast_face/tools.py +501 -0
  19. fast_face_python-0.1.1/src/fast_face_python.egg-info/PKG-INFO +237 -0
  20. fast_face_python-0.1.1/src/fast_face_python.egg-info/SOURCES.txt +25 -0
  21. fast_face_python-0.1.1/src/fast_face_python.egg-info/dependency_links.txt +1 -0
  22. fast_face_python-0.1.1/src/fast_face_python.egg-info/requires.txt +25 -0
  23. fast_face_python-0.1.1/src/fast_face_python.egg-info/top_level.txt +1 -0
  24. fast_face_python-0.1.1/tests/test_adaface.py +126 -0
  25. fast_face_python-0.1.1/tests/test_import.py +9 -0
  26. fast_face_python-0.1.1/tests/test_retinaface.py +39 -0
  27. fast_face_python-0.1.1/tests/test_yunet.py +21 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 xMaulana
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ global-exclude *.onnx
2
+
3
+ prune .uv_cache
4
+ prune .ruff_cache
5
+ prune .pytest_cache
6
+ prune .venv
@@ -0,0 +1,237 @@
1
+ Metadata-Version: 2.4
2
+ Name: fast-face-python
3
+ Version: 0.1.1
4
+ Summary: Fast and easy face detection using ONNX runtime.
5
+ Author: xMaulana
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: numpy
10
+ Requires-Dist: Pillow
11
+ Requires-Dist: opencv-python
12
+ Requires-Dist: pydantic-settings>=2.15.0
13
+ Provides-Extra: cpu
14
+ Requires-Dist: onnxruntime>=1.24.3; extra == "cpu"
15
+ Provides-Extra: gpu
16
+ Requires-Dist: onnxruntime-gpu<=1.26.1,>=1.24.3; extra == "gpu"
17
+ Requires-Dist: nvidia-cudnn-cu12; extra == "gpu"
18
+ Requires-Dist: nvidia-cublas-cu12; extra == "gpu"
19
+ Requires-Dist: nvidia-cuda-runtime-cu12; extra == "gpu"
20
+ Provides-Extra: openvino
21
+ Requires-Dist: onnxruntime-openvino>=1.24.1; extra == "openvino"
22
+ Provides-Extra: rocm
23
+ Requires-Dist: onnxruntime-rocm>=1.22.1; extra == "rocm"
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest; extra == "dev"
26
+ Requires-Dist: ruff; extra == "dev"
27
+ Requires-Dist: build; extra == "dev"
28
+ Requires-Dist: twine; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ # fast-face-python
32
+
33
+ Fast and easy face detection using ONNX runtime.
34
+
35
+ ## Overview
36
+
37
+ fast-face-python provides a unified interface for running face detection and recognition models using ONNX Runtime. It handles model downloading, preprocessing, inference, and post-processing, including non-maximum suppression for detection and alignment for recognition.
38
+
39
+ The library supports batched inference and configurable execution providers such as CPU, CUDA, ROCm (AMD GPU), and OpenVINO.
40
+
41
+ ## Features
42
+
43
+ * Face Detection: YuNet, RetinaFace (MobileNet, ResNet50)
44
+ * Face Recognition: AdaFace (IR18, IR50, IR101)
45
+ * Hardware Acceleration: CPU, CUDA, ROCm (AMD GPU), and OpenVINO support via ONNX Runtime
46
+ * Batched Inference: Process multiple images simultaneously
47
+ * Automatic Model Management: Downloads required ONNX model weights on first use
48
+
49
+ ## Requirements
50
+
51
+ * Python >= 3.11
52
+ * OpenCV
53
+ * NumPy
54
+ * Pillow
55
+ * ONNX Runtime
56
+
57
+ ## Installation
58
+
59
+ Install the package using pip:
60
+
61
+ ```bash
62
+ pip install fast-face-python
63
+ ```
64
+
65
+ For NVIDIA GPU support via CUDA 12:
66
+
67
+ ```bash
68
+ pip install "fast-face-python[gpu]"
69
+ ```
70
+
71
+ For AMD GPU support via ROCm:
72
+
73
+ ```bash
74
+ pip install "fast-face-python[rocm]"
75
+ ```
76
+
77
+ For OpenVINO support:
78
+
79
+ ```bash
80
+ pip install "fast-face-python[openvino]"
81
+ ```
82
+
83
+ ## Usage
84
+
85
+ ### Face Detection
86
+
87
+ Instantiate a detection model using `FaceModelFactory` and call the `detect` method. The method accepts an image path, a NumPy array in RGB format, or a list of either.
88
+
89
+ ```python
90
+ from fast_face import FaceModelFactory
91
+
92
+ # Initialize the model (weights are downloaded automatically if missing)
93
+ model = FaceModelFactory.get_model("YUNET", top_k=500, conf_threshold=0.6)
94
+
95
+ # Run detection on a local image file
96
+ results = model.detect("image.jpg", return_dict=True)
97
+
98
+ # Process the detections for the first image
99
+ for det in results[0]:
100
+ bbox = det["bbox"]
101
+ confidence = det["score"]
102
+ landmarks = det["landmarks"]
103
+ print(f"Face detected at {bbox} with confidence {confidence}")
104
+ ```
105
+
106
+ To run inference on an existing OpenCV image, convert it to RGB first:
107
+
108
+ ```python
109
+ import cv2
110
+
111
+ img = cv2.imread("image.jpg")
112
+ img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
113
+
114
+ results = model.detect(img_rgb, return_dict=True)
115
+ ```
116
+
117
+ ### Face Recognition
118
+
119
+ Recognition models extract L2-normalized embeddings from aligned face images.
120
+
121
+ ```python
122
+ import numpy as np
123
+ from fast_face import FaceModelFactory
124
+
125
+ model = FaceModelFactory.get_model("ADAFACE_IR50")
126
+
127
+ # Provide pre-aligned 112x112 RGB face crops
128
+ face_crop1 = np.random.randint(0, 255, (112, 112, 3), dtype=np.uint8)
129
+ face_crop2 = np.random.randint(0, 255, (112, 112, 3), dtype=np.uint8)
130
+
131
+ # Extract embeddings for the batch
132
+ embeddings = model.extract([face_crop1, face_crop2])
133
+
134
+ print(f"Extracted {embeddings.shape[0]} embeddings of dimension {embeddings.shape[1]}")
135
+ ```
136
+
137
+ If you have unaligned images and face landmarks from a detection model, you can pass the landmarks to automatically align the faces before extraction:
138
+
139
+ ```python
140
+ landmarks = np.array([
141
+ [200.0, 200.0], [280.0, 200.0], [240.0, 250.0],
142
+ [210.0, 300.0], [270.0, 300.0]
143
+ ], dtype=np.float32)
144
+
145
+ embeddings = model.extract([img_rgb], landmarks=[landmarks])
146
+ ```
147
+
148
+ ### Execution Providers (Hardware Acceleration)
149
+
150
+ By default, all models run using CPU (`["CPUExecutionProvider"]`). You can enable hardware acceleration (CUDA, ROCm, OpenVINO, CoreML, etc.) by passing the `providers` argument to `FaceModelFactory.get_model()`:
151
+
152
+ #### NVIDIA GPU (CUDA)
153
+
154
+ ```python
155
+ model = FaceModelFactory.get_model(
156
+ "RETINAFACE_RESNET50",
157
+ providers=["CUDAExecutionProvider", "CPUExecutionProvider"],
158
+ )
159
+ ```
160
+
161
+ #### AMD GPU (ROCm)
162
+
163
+ ```python
164
+ model = FaceModelFactory.get_model(
165
+ "ADAFACE_IR101",
166
+ providers=["ROCMExecutionProvider", "CPUExecutionProvider"],
167
+ )
168
+ ```
169
+
170
+ #### Intel OpenVINO
171
+
172
+ ```python
173
+ model = FaceModelFactory.get_model(
174
+ "YUNET",
175
+ providers=["OpenVINOExecutionProvider", "CPUExecutionProvider"],
176
+ )
177
+ ```
178
+
179
+ #### Custom Provider Options
180
+
181
+ You can also pass tuple configurations with custom options (such as device ID or memory limits):
182
+
183
+ ```python
184
+ cuda_provider = (
185
+ "CUDAExecutionProvider",
186
+ {
187
+ "device_id": 0,
188
+ "arena_extend_strategy": "kNextPowerOfTwo",
189
+ "gpu_mem_limit": 2 * 1024 * 1024 * 1024, # 2 GB
190
+ },
191
+ )
192
+
193
+ model = FaceModelFactory.get_model(
194
+ "RETINAFACE_RESNET50",
195
+ providers=[cuda_provider, "CPUExecutionProvider"],
196
+ )
197
+ ```
198
+
199
+ ### Supported Models
200
+
201
+ The following model identifiers are supported by `FaceModelFactory`:
202
+
203
+ * `YUNET`
204
+ * `RETINAFACE_MOBILENET`
205
+ * `RETINAFACE_RESNET50`
206
+ * `ADAFACE_IR18`
207
+ * `ADAFACE_IR50`
208
+ * `ADAFACE_IR101`
209
+
210
+ ## Development
211
+
212
+ To set up the repository for development, install the `dev` dependencies:
213
+
214
+ ```bash
215
+ pip install -e ".[dev,cpu]"
216
+ ```
217
+
218
+ ### Testing
219
+
220
+ The project uses `pytest` for testing. Run the test suite:
221
+
222
+ ```bash
223
+ pytest tests/
224
+ ```
225
+
226
+ ### Formatting and Linting
227
+
228
+ The project uses `ruff` for code formatting and linting:
229
+
230
+ ```bash
231
+ ruff check .
232
+ ruff format .
233
+ ```
234
+
235
+ ## License
236
+
237
+ This project is licensed under the terms found in the `LICENSE` file.
@@ -0,0 +1,207 @@
1
+ # fast-face-python
2
+
3
+ Fast and easy face detection using ONNX runtime.
4
+
5
+ ## Overview
6
+
7
+ fast-face-python provides a unified interface for running face detection and recognition models using ONNX Runtime. It handles model downloading, preprocessing, inference, and post-processing, including non-maximum suppression for detection and alignment for recognition.
8
+
9
+ The library supports batched inference and configurable execution providers such as CPU, CUDA, ROCm (AMD GPU), and OpenVINO.
10
+
11
+ ## Features
12
+
13
+ * Face Detection: YuNet, RetinaFace (MobileNet, ResNet50)
14
+ * Face Recognition: AdaFace (IR18, IR50, IR101)
15
+ * Hardware Acceleration: CPU, CUDA, ROCm (AMD GPU), and OpenVINO support via ONNX Runtime
16
+ * Batched Inference: Process multiple images simultaneously
17
+ * Automatic Model Management: Downloads required ONNX model weights on first use
18
+
19
+ ## Requirements
20
+
21
+ * Python >= 3.11
22
+ * OpenCV
23
+ * NumPy
24
+ * Pillow
25
+ * ONNX Runtime
26
+
27
+ ## Installation
28
+
29
+ Install the package using pip:
30
+
31
+ ```bash
32
+ pip install fast-face-python
33
+ ```
34
+
35
+ For NVIDIA GPU support via CUDA 12:
36
+
37
+ ```bash
38
+ pip install "fast-face-python[gpu]"
39
+ ```
40
+
41
+ For AMD GPU support via ROCm:
42
+
43
+ ```bash
44
+ pip install "fast-face-python[rocm]"
45
+ ```
46
+
47
+ For OpenVINO support:
48
+
49
+ ```bash
50
+ pip install "fast-face-python[openvino]"
51
+ ```
52
+
53
+ ## Usage
54
+
55
+ ### Face Detection
56
+
57
+ Instantiate a detection model using `FaceModelFactory` and call the `detect` method. The method accepts an image path, a NumPy array in RGB format, or a list of either.
58
+
59
+ ```python
60
+ from fast_face import FaceModelFactory
61
+
62
+ # Initialize the model (weights are downloaded automatically if missing)
63
+ model = FaceModelFactory.get_model("YUNET", top_k=500, conf_threshold=0.6)
64
+
65
+ # Run detection on a local image file
66
+ results = model.detect("image.jpg", return_dict=True)
67
+
68
+ # Process the detections for the first image
69
+ for det in results[0]:
70
+ bbox = det["bbox"]
71
+ confidence = det["score"]
72
+ landmarks = det["landmarks"]
73
+ print(f"Face detected at {bbox} with confidence {confidence}")
74
+ ```
75
+
76
+ To run inference on an existing OpenCV image, convert it to RGB first:
77
+
78
+ ```python
79
+ import cv2
80
+
81
+ img = cv2.imread("image.jpg")
82
+ img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
83
+
84
+ results = model.detect(img_rgb, return_dict=True)
85
+ ```
86
+
87
+ ### Face Recognition
88
+
89
+ Recognition models extract L2-normalized embeddings from aligned face images.
90
+
91
+ ```python
92
+ import numpy as np
93
+ from fast_face import FaceModelFactory
94
+
95
+ model = FaceModelFactory.get_model("ADAFACE_IR50")
96
+
97
+ # Provide pre-aligned 112x112 RGB face crops
98
+ face_crop1 = np.random.randint(0, 255, (112, 112, 3), dtype=np.uint8)
99
+ face_crop2 = np.random.randint(0, 255, (112, 112, 3), dtype=np.uint8)
100
+
101
+ # Extract embeddings for the batch
102
+ embeddings = model.extract([face_crop1, face_crop2])
103
+
104
+ print(f"Extracted {embeddings.shape[0]} embeddings of dimension {embeddings.shape[1]}")
105
+ ```
106
+
107
+ If you have unaligned images and face landmarks from a detection model, you can pass the landmarks to automatically align the faces before extraction:
108
+
109
+ ```python
110
+ landmarks = np.array([
111
+ [200.0, 200.0], [280.0, 200.0], [240.0, 250.0],
112
+ [210.0, 300.0], [270.0, 300.0]
113
+ ], dtype=np.float32)
114
+
115
+ embeddings = model.extract([img_rgb], landmarks=[landmarks])
116
+ ```
117
+
118
+ ### Execution Providers (Hardware Acceleration)
119
+
120
+ By default, all models run using CPU (`["CPUExecutionProvider"]`). You can enable hardware acceleration (CUDA, ROCm, OpenVINO, CoreML, etc.) by passing the `providers` argument to `FaceModelFactory.get_model()`:
121
+
122
+ #### NVIDIA GPU (CUDA)
123
+
124
+ ```python
125
+ model = FaceModelFactory.get_model(
126
+ "RETINAFACE_RESNET50",
127
+ providers=["CUDAExecutionProvider", "CPUExecutionProvider"],
128
+ )
129
+ ```
130
+
131
+ #### AMD GPU (ROCm)
132
+
133
+ ```python
134
+ model = FaceModelFactory.get_model(
135
+ "ADAFACE_IR101",
136
+ providers=["ROCMExecutionProvider", "CPUExecutionProvider"],
137
+ )
138
+ ```
139
+
140
+ #### Intel OpenVINO
141
+
142
+ ```python
143
+ model = FaceModelFactory.get_model(
144
+ "YUNET",
145
+ providers=["OpenVINOExecutionProvider", "CPUExecutionProvider"],
146
+ )
147
+ ```
148
+
149
+ #### Custom Provider Options
150
+
151
+ You can also pass tuple configurations with custom options (such as device ID or memory limits):
152
+
153
+ ```python
154
+ cuda_provider = (
155
+ "CUDAExecutionProvider",
156
+ {
157
+ "device_id": 0,
158
+ "arena_extend_strategy": "kNextPowerOfTwo",
159
+ "gpu_mem_limit": 2 * 1024 * 1024 * 1024, # 2 GB
160
+ },
161
+ )
162
+
163
+ model = FaceModelFactory.get_model(
164
+ "RETINAFACE_RESNET50",
165
+ providers=[cuda_provider, "CPUExecutionProvider"],
166
+ )
167
+ ```
168
+
169
+ ### Supported Models
170
+
171
+ The following model identifiers are supported by `FaceModelFactory`:
172
+
173
+ * `YUNET`
174
+ * `RETINAFACE_MOBILENET`
175
+ * `RETINAFACE_RESNET50`
176
+ * `ADAFACE_IR18`
177
+ * `ADAFACE_IR50`
178
+ * `ADAFACE_IR101`
179
+
180
+ ## Development
181
+
182
+ To set up the repository for development, install the `dev` dependencies:
183
+
184
+ ```bash
185
+ pip install -e ".[dev,cpu]"
186
+ ```
187
+
188
+ ### Testing
189
+
190
+ The project uses `pytest` for testing. Run the test suite:
191
+
192
+ ```bash
193
+ pytest tests/
194
+ ```
195
+
196
+ ### Formatting and Linting
197
+
198
+ The project uses `ruff` for code formatting and linting:
199
+
200
+ ```bash
201
+ ruff check .
202
+ ruff format .
203
+ ```
204
+
205
+ ## License
206
+
207
+ This project is licensed under the terms found in the `LICENSE` file.
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "fast-face-python"
7
+ version = "0.1.1"
8
+ description = "Fast and easy face detection using ONNX runtime."
9
+ authors = [
10
+ { name="xMaulana" },
11
+ ]
12
+ readme = "README.md"
13
+ requires-python = ">=3.11"
14
+ dependencies = [
15
+ "numpy",
16
+ "Pillow",
17
+ "opencv-python",
18
+ "pydantic-settings>=2.15.0",
19
+ ]
20
+
21
+ [project.optional-dependencies]
22
+ cpu = [
23
+ "onnxruntime>=1.24.3",
24
+ ]
25
+ gpu = [
26
+ "onnxruntime-gpu>=1.24.3, <=1.26.1",
27
+ "nvidia-cudnn-cu12",
28
+ "nvidia-cublas-cu12",
29
+ "nvidia-cuda-runtime-cu12"
30
+ ]
31
+ openvino = [
32
+ "onnxruntime-openvino>=1.24.1"
33
+ ]
34
+ rocm = [
35
+ "onnxruntime-rocm>=1.22.1"
36
+ ]
37
+ dev = [
38
+ "pytest",
39
+ "ruff",
40
+ "build",
41
+ "twine"
42
+ ]
43
+
44
+ [tool.setuptools]
45
+ package-dir = {"" = "src"}
46
+ packages = ["fast_face", "fast_face.models"]
47
+
48
+ [tool.uv]
49
+ cache-dir = ".uv_cache"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ from .models.factory import FaceModelFactory
2
+ from .models.yunet import YuNet
3
+ from .models.retinaface import RetinaFace
4
+ from .models.adaface import AdaFace
5
+
6
+ __all__ = ["FaceModelFactory", "YuNet", "RetinaFace", "AdaFace"]
@@ -0,0 +1,11 @@
1
+ from .base import BaseFaceModel
2
+ from .factory import FaceModelFactory
3
+ from .session import ONNXSession
4
+ from .yunet import YuNet
5
+
6
+ __all__ = [
7
+ "BaseFaceModel",
8
+ "FaceModelFactory",
9
+ "ONNXSession",
10
+ "YuNet",
11
+ ]
@@ -0,0 +1,91 @@
1
+ from typing import Any
2
+
3
+ import cv2
4
+ import numpy as np
5
+
6
+ from ..schema import ProviderType
7
+ from ..tools import align_face
8
+ from .base_recognition import BaseRecognitionModel
9
+
10
+
11
+ class AdaFace(BaseRecognitionModel):
12
+ """AdaFace face recognition model for embedding extraction.
13
+
14
+ Supports IR-18, IR-50, and IR-101 backbones.
15
+ Expects RGB input images; converts to BGR internally as required by AdaFace.
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ model_path: str,
21
+ align_size: tuple[int, int] = (112, 112),
22
+ providers: list[ProviderType] | None = None,
23
+ **kwargs,
24
+ ):
25
+ if providers is None:
26
+ providers = ["CPUExecutionProvider"]
27
+ super().__init__(
28
+ model_path=model_path,
29
+ providers=providers,
30
+ sess_options=kwargs.get("sess_options"),
31
+ )
32
+ self.align_size = align_size
33
+
34
+ def preprocess(self, imgs: list[np.ndarray]) -> np.ndarray:
35
+ """Preprocess aligned face images: RGB->BGR, normalize to [-1, 1], transpose to NCHW."""
36
+ processed = []
37
+ for img in imgs:
38
+ bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
39
+
40
+ h, w = bgr.shape[:2]
41
+ if (w, h) != self.align_size:
42
+ bgr = cv2.resize(bgr, self.align_size)
43
+
44
+ processed.append(bgr)
45
+
46
+ batch = np.stack(processed, axis=0).astype(np.float32)
47
+
48
+ batch = (batch / 255.0 - 0.5) / 0.5
49
+
50
+ batch = np.transpose(batch, (0, 3, 1, 2))
51
+ return batch
52
+
53
+ def extract(
54
+ self,
55
+ imgs: list[np.ndarray] | np.ndarray,
56
+ landmarks: list[np.ndarray | dict[str, Any]] | None = None,
57
+ ) -> np.ndarray:
58
+ """Extract L2-normalized face embeddings from images.
59
+
60
+ Args:
61
+ imgs: List of face images in RGB, or a single image array.
62
+ If landmarks are provided, images should be the full/cropped
63
+ frames from which faces will be aligned.
64
+ landmarks: Optional list of landmarks for face alignment.
65
+ Each element can be an np.ndarray of shape (5, 2) or (10,),
66
+ or a dictionary with landmark keys.
67
+
68
+ Returns:
69
+ np.ndarray: L2-normalized embeddings of shape (N, embedding_dim).
70
+ """
71
+ if isinstance(imgs, np.ndarray) and imgs.ndim == 3:
72
+ imgs = [imgs]
73
+
74
+ if landmarks is not None:
75
+ aligned_imgs = []
76
+ for img, lmk in zip(imgs, landmarks):
77
+ aligned = align_face(img, lmk, align_size=self.align_size)
78
+ aligned_imgs.append(aligned)
79
+ else:
80
+ aligned_imgs = imgs
81
+
82
+ batch = self.preprocess(aligned_imgs)
83
+ outputs = self.session(batch)
84
+
85
+ embeddings = outputs[0]
86
+
87
+ norms = np.linalg.norm(embeddings, axis=1, keepdims=True)
88
+ norms = np.maximum(norms, 1e-10)
89
+ embeddings = embeddings / norms
90
+
91
+ return embeddings