3 Commits

Author SHA1 Message Date
Yakhyokhuja Valikhujaev
f897482d26 release: Release UniFace v2.2.1 (#69) 2026-01-18 22:38:15 +09:00
Yakhyokhuja Valikhujaev
f3d81eb201 feat: Add providers for chosing inference backend (#68)
* feat: Add providers for chosing inference backend

* docs: Update Python version
2026-01-18 22:29:15 +09:00
Yakhyokhuja Valikhujaev
ea0b56f7e0 fix: Add cache dir check (#67) 2026-01-15 18:07:45 +09:00
23 changed files with 148 additions and 39 deletions

View File

@@ -59,12 +59,12 @@ This project uses [Ruff](https://docs.astral.sh/ruff/) for linting and formattin
#### General Rules
- **Line length:** 120 characters maximum
- **Python version:** 3.11+ (use modern syntax)
- **Python version:** 3.10+ (use modern syntax)
- **Quote style:** Single quotes for strings, double quotes for docstrings
#### Type Hints
Use modern Python 3.11+ type hints (PEP 585 and PEP 604):
Use modern Python 3.10+ type hints (PEP 585 and PEP 604):
```python
# Preferred (modern)

View File

@@ -105,15 +105,18 @@ for face in faces:
## References
- [yakhyo/retinaface-pytorch](https://github.com/yakhyo/retinaface-pytorch) — RetinaFace training
- [yakhyo/yolov5-face-onnx-inference](https://github.com/yakhyo/yolov5-face-onnx-inference) — YOLOv5-Face ONNX
- [yakhyo/yolov8-face-onnx-inference](https://github.com/yakhyo/yolov8-face-onnx-inference) — YOLOv8-Face ONNX
- [yakhyo/face-recognition](https://github.com/yakhyo/face-recognition) — ArcFace, MobileFace, SphereFace
- [yakhyo/face-parsing](https://github.com/yakhyo/face-parsing) — BiSeNet face parsing
- [yakhyo/gaze-estimation](https://github.com/yakhyo/gaze-estimation) — MobileGaze training
- [yakhyo/face-anti-spoofing](https://github.com/yakhyo/face-anti-spoofing) — MiniFASNet inference
- [yakhyo/fairface-onnx](https://github.com/yakhyo/fairface-onnx) — FairFace attributes
- [deepinsight/insightface](https://github.com/deepinsight/insightface) — Model architectures
| Feature | Repository | Training | Description |
|---------|------------|:--------:|-------------|
| Detection | [retinaface-pytorch](https://github.com/yakhyo/retinaface-pytorch) | [x] | RetinaFace PyTorch Training & Export |
| Detection | [yolov5-face-onnx-inference](https://github.com/yakhyo/yolov5-face-onnx-inference) | - | YOLOv5-Face ONNX Inference |
| Detection | [yolov8-face-onnx-inference](https://github.com/yakhyo/yolov8-face-onnx-inference) | - | YOLOv8-Face ONNX Inference |
| Recognition | [face-recognition](https://github.com/yakhyo/face-recognition) | [x] | MobileFace, SphereFace Training |
| Parsing | [face-parsing](https://github.com/yakhyo/face-parsing) | [x] | BiSeNet Face Parsing |
| Gaze | [gaze-estimation](https://github.com/yakhyo/gaze-estimation) | [x] | MobileGaze Training |
| Anti-Spoofing | [face-anti-spoofing](https://github.com/yakhyo/face-anti-spoofing) | - | MiniFASNet Inference |
| Attributes | [fairface-onnx](https://github.com/yakhyo/fairface-onnx) | - | FairFace ONNX Inference |
*SCRFD and ArcFace models are from [InsightFace](https://github.com/deepinsight/insightface).
---

View File

@@ -23,6 +23,33 @@ detector = RetinaFace()
---
## Explicit Provider Selection
You can specify which execution provider to use by passing the `providers` parameter:
```python
from uniface import RetinaFace, ArcFace
# Force CPU execution (even if GPU is available)
detector = RetinaFace(providers=['CPUExecutionProvider'])
recognizer = ArcFace(providers=['CPUExecutionProvider'])
# Use CUDA with CPU fallback
detector = RetinaFace(providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
```
All model classes accept the `providers` parameter:
- Detection: `RetinaFace`, `SCRFD`, `YOLOv5Face`, `YOLOv8Face`
- Recognition: `ArcFace`, `AdaFace`, `MobileFace`, `SphereFace`
- Landmarks: `Landmark106`
- Gaze: `MobileGaze`
- Parsing: `BiSeNet`
- Attributes: `AgeGender`, `FairFace`
- Anti-Spoofing: `MiniFASNet`
---
## Check Available Providers
```python

View File

@@ -32,7 +32,7 @@ ruff check . --fix
**Guidelines:**
- Line length: 120
- Python 3.11+ type hints
- Python 3.10+ type hints
- Google-style docstrings
---

View File

@@ -6,7 +6,7 @@ This guide covers all installation options for UniFace.
## Requirements
- **Python**: 3.11 or higher
- **Python**: 3.10 or higher
- **Operating Systems**: macOS, Linux, Windows
---
@@ -137,11 +137,11 @@ print("Installation successful!")
### Import Errors
If you encounter import errors, ensure you're using Python 3.11+:
If you encounter import errors, ensure you're using Python 3.10+:
```bash
python --version
# Should show: Python 3.11.x or higher
# Should show: Python 3.10.x or higher
```
### Model Download Issues

View File

@@ -68,7 +68,8 @@ detector = RetinaFace(
confidence_threshold=0.5, # Min confidence
nms_threshold=0.4, # NMS IoU threshold
input_size=(640, 640), # Input resolution
dynamic_size=False # Enable dynamic input size
dynamic_size=False, # Enable dynamic input size
providers=None, # Auto-detect, or ['CPUExecutionProvider']
)
```
@@ -112,7 +113,8 @@ detector = SCRFD(
model_name=SCRFDWeights.SCRFD_10G_KPS,
confidence_threshold=0.5,
nms_threshold=0.4,
input_size=(640, 640)
input_size=(640, 640),
providers=None, # Auto-detect, or ['CPUExecutionProvider']
)
```
@@ -163,7 +165,8 @@ detector = YOLOv5Face(
model_name=YOLOv5FaceWeights.YOLOV5S,
confidence_threshold=0.6,
nms_threshold=0.5,
nms_mode='numpy' # or 'torchvision' for faster NMS
nms_mode='numpy', # or 'torchvision' for faster NMS
providers=None, # Auto-detect, or ['CPUExecutionProvider']
)
```
@@ -210,7 +213,8 @@ detector = YOLOv8Face(
model_name=YOLOv8FaceWeights.YOLOV8N,
confidence_threshold=0.5,
nms_threshold=0.45,
nms_mode='numpy' # or 'torchvision' for faster NMS
nms_mode='numpy', # or 'torchvision' for faster NMS
providers=None, # Auto-detect, or ['CPUExecutionProvider']
)
```

View File

@@ -47,6 +47,9 @@ recognizer = AdaFace(model_name=AdaFaceWeights.IR_18)
# High accuracy
recognizer = AdaFace(model_name=AdaFaceWeights.IR_101)
# Force CPU execution
recognizer = AdaFace(providers=['CPUExecutionProvider'])
```
| Variant | Dataset | Size | IJB-B | IJB-C |
@@ -91,6 +94,9 @@ recognizer = ArcFace(model_name=ArcFaceWeights.MNET)
# High accuracy
recognizer = ArcFace(model_name=ArcFaceWeights.RESNET)
# Force CPU execution
recognizer = ArcFace(providers=['CPUExecutionProvider'])
```
| Variant | Backbone | Size | LFW | CFP-FP | AgeDB-30 | IJB-C |

View File

@@ -1,6 +1,6 @@
[project]
name = "uniface"
version = "2.2.0"
version = "2.2.1"
description = "UniFace: A Comprehensive Library for Face Detection, Recognition, Landmark Analysis, Face Parsing, Gaze Estimation, Age, and Gender Detection"
readme = "README.md"
license = "MIT"

View File

@@ -28,7 +28,7 @@ from __future__ import annotations
__license__ = 'MIT'
__author__ = 'Yakhyokhuja Valikhujaev'
__version__ = '2.2.0'
__version__ = '2.2.1'
from uniface.face_utils import compute_similarity, face_alignment
from uniface.log import Logger, enable_logging

View File

@@ -30,12 +30,15 @@ class AgeGender(Attribute):
Defaults to `AgeGenderWeights.DEFAULT`.
input_size (Optional[Tuple[int, int]]): Input size (height, width).
If None, automatically detected from model metadata. Defaults to None.
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
"""
def __init__(
self,
model_name: AgeGenderWeights = AgeGenderWeights.DEFAULT,
input_size: tuple[int, int] | None = None,
providers: list[str] | None = None,
) -> None:
"""
Initializes the AgeGender prediction model.
@@ -44,10 +47,13 @@ class AgeGender(Attribute):
model_name (AgeGenderWeights): The enum specifying the model weights to load.
input_size (Optional[Tuple[int, int]]): Input size (height, width).
If None, automatically detected from model metadata. Defaults to None.
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
"""
Logger.info(f'Initializing AgeGender with model={model_name.name}')
self.model_path = verify_model_weights(model_name)
self._user_input_size = input_size # Store user preference
self.providers = providers
self._initialize_model()
def _initialize_model(self) -> None:
@@ -55,7 +61,7 @@ class AgeGender(Attribute):
Initializes the ONNX model and creates an inference session.
"""
try:
self.session = create_onnx_session(self.model_path)
self.session = create_onnx_session(self.model_path, providers=self.providers)
# Get model input details from the loaded model
input_meta = self.session.get_inputs()[0]
self.input_name = input_meta.name

View File

@@ -44,12 +44,15 @@ class FairFace(Attribute):
Defaults to `FairFaceWeights.DEFAULT`.
input_size (Optional[Tuple[int, int]]): Input size (height, width).
If None, defaults to (224, 224). Defaults to None.
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
"""
def __init__(
self,
model_name: FairFaceWeights = FairFaceWeights.DEFAULT,
input_size: tuple[int, int] | None = None,
providers: list[str] | None = None,
) -> None:
"""
Initializes the FairFace prediction model.
@@ -58,10 +61,13 @@ class FairFace(Attribute):
model_name (FairFaceWeights): The enum specifying the model weights to load.
input_size (Optional[Tuple[int, int]]): Input size (height, width).
If None, defaults to (224, 224).
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
"""
Logger.info(f'Initializing FairFace with model={model_name.name}')
self.model_path = verify_model_weights(model_name)
self.input_size = input_size if input_size is not None else (224, 224)
self.providers = providers
self._initialize_model()
def _initialize_model(self) -> None:
@@ -69,7 +75,7 @@ class FairFace(Attribute):
Initializes the ONNX model and creates an inference session.
"""
try:
self.session = create_onnx_session(self.model_path)
self.session = create_onnx_session(self.model_path, providers=self.providers)
# Get model input details from the loaded model
input_meta = self.session.get_inputs()[0]
self.input_name = input_meta.name

View File

@@ -39,6 +39,8 @@ class RetinaFace(BaseDetector):
input_size (Tuple[int, int]): Fixed input size (width, height) if `dynamic_size=False`.
Defaults to (640, 640).
Note: Non-default sizes may cause slower inference and CoreML compatibility issues.
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
**kwargs: Advanced options:
pre_nms_topk (int): Number of top-scoring boxes considered before NMS. Defaults to 5000.
post_nms_topk (int): Max number of detections kept after NMS. Defaults to 750.
@@ -68,6 +70,7 @@ class RetinaFace(BaseDetector):
confidence_threshold: float = 0.5,
nms_threshold: float = 0.4,
input_size: tuple[int, int] = (640, 640),
providers: list[str] | None = None,
**kwargs: Any,
) -> None:
super().__init__(
@@ -75,6 +78,7 @@ class RetinaFace(BaseDetector):
confidence_threshold=confidence_threshold,
nms_threshold=nms_threshold,
input_size=input_size,
providers=providers,
**kwargs,
)
self._supports_landmarks = True # RetinaFace supports landmarks
@@ -83,6 +87,7 @@ class RetinaFace(BaseDetector):
self.confidence_threshold = confidence_threshold
self.nms_threshold = nms_threshold
self.input_size = input_size
self.providers = providers
# Advanced options from kwargs
self.pre_nms_topk = kwargs.get('pre_nms_topk', 5000)
@@ -116,7 +121,7 @@ class RetinaFace(BaseDetector):
RuntimeError: If the model fails to load.
"""
try:
self.session = create_onnx_session(model_path)
self.session = create_onnx_session(model_path, providers=self.providers)
self.input_names = self.session.get_inputs()[0].name
self.output_names = [x.name for x in self.session.get_outputs()]
Logger.info(f'Successfully initialized the model from {model_path}')

View File

@@ -36,6 +36,8 @@ class SCRFD(BaseDetector):
input_size (Tuple[int, int]): Input image size (width, height).
Defaults to (640, 640).
Note: Non-default sizes may cause slower inference and CoreML compatibility issues.
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
**kwargs: Reserved for future advanced options.
Attributes:
@@ -61,6 +63,7 @@ class SCRFD(BaseDetector):
confidence_threshold: float = 0.5,
nms_threshold: float = 0.4,
input_size: tuple[int, int] = (640, 640),
providers: list[str] | None = None,
**kwargs: Any,
) -> None:
super().__init__(
@@ -68,6 +71,7 @@ class SCRFD(BaseDetector):
confidence_threshold=confidence_threshold,
nms_threshold=nms_threshold,
input_size=input_size,
providers=providers,
**kwargs,
)
self._supports_landmarks = True # SCRFD supports landmarks
@@ -76,6 +80,7 @@ class SCRFD(BaseDetector):
self.confidence_threshold = confidence_threshold
self.nms_threshold = nms_threshold
self.input_size = input_size
self.providers = providers
# ------- SCRFD model params ------
self._num_feature_maps = 3
@@ -106,7 +111,7 @@ class SCRFD(BaseDetector):
RuntimeError: If the model fails to load.
"""
try:
self.session = create_onnx_session(model_path)
self.session = create_onnx_session(model_path, providers=self.providers)
self.input_names = self.session.get_inputs()[0].name
self.output_names = [x.name for x in self.session.get_outputs()]
Logger.info(f'Successfully initialized the model from {model_path}')

View File

@@ -44,7 +44,9 @@ class YOLOv5Face(BaseDetector):
input_size (int): Input image size. Defaults to 640.
Note: ONNX model is fixed at 640. Changing this will cause inference errors.
nms_mode (str): NMS calculation method. Options: 'torchvision' (faster, requires torch)
or 'numpy' (no dependencies). Defaults to 'torchvision' if available.
or 'numpy' (no dependencies). Defaults to 'numpy'.
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
**kwargs: Advanced options:
max_det (int): Maximum number of detections to return. Defaults to 750.
@@ -70,6 +72,7 @@ class YOLOv5Face(BaseDetector):
nms_threshold: float = 0.5,
input_size: int = 640,
nms_mode: Literal['torchvision', 'numpy'] = 'numpy',
providers: list[str] | None = None,
**kwargs: Any,
) -> None:
super().__init__(
@@ -78,6 +81,7 @@ class YOLOv5Face(BaseDetector):
nms_threshold=nms_threshold,
input_size=input_size,
nms_mode=nms_mode,
providers=providers,
**kwargs,
)
self._supports_landmarks = True # YOLOv5-Face supports landmarks
@@ -92,6 +96,7 @@ class YOLOv5Face(BaseDetector):
self.confidence_threshold = confidence_threshold
self.nms_threshold = nms_threshold
self.input_size = input_size
self.providers = providers
# Set NMS mode with automatic fallback
if nms_mode == 'torchvision' and not TORCHVISION_AVAILABLE:
@@ -126,7 +131,7 @@ class YOLOv5Face(BaseDetector):
RuntimeError: If the model fails to load, logs an error and raises an exception.
"""
try:
self.session = create_onnx_session(model_path)
self.session = create_onnx_session(model_path, providers=self.providers)
self.input_names = self.session.get_inputs()[0].name
self.output_names = [x.name for x in self.session.get_outputs()]
Logger.info(f'Successfully initialized the model from {model_path}')

View File

@@ -52,7 +52,9 @@ class YOLOv8Face(BaseDetector):
input_size (int): Input image size. Defaults to 640.
Note: ONNX model is fixed at 640. Changing this will cause inference errors.
nms_mode (str): NMS calculation method. Options: 'torchvision' (faster, requires torch)
or 'numpy' (no dependencies). Defaults to 'torchvision' if available.
or 'numpy' (no dependencies). Defaults to 'numpy'.
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
**kwargs: Advanced options:
max_det (int): Maximum number of detections to return. Defaults to 750.
@@ -78,6 +80,7 @@ class YOLOv8Face(BaseDetector):
nms_threshold: float = 0.45,
input_size: int = 640,
nms_mode: Literal['torchvision', 'numpy'] = 'numpy',
providers: list[str] | None = None,
**kwargs: Any,
) -> None:
super().__init__(
@@ -86,6 +89,7 @@ class YOLOv8Face(BaseDetector):
nms_threshold=nms_threshold,
input_size=input_size,
nms_mode=nms_mode,
providers=providers,
**kwargs,
)
self._supports_landmarks = True # YOLOv8-Face supports landmarks
@@ -100,6 +104,7 @@ class YOLOv8Face(BaseDetector):
self.confidence_threshold = confidence_threshold
self.nms_threshold = nms_threshold
self.input_size = input_size
self.providers = providers
# Set NMS mode with automatic fallback
if nms_mode == 'torchvision' and not TORCHVISION_AVAILABLE:
@@ -137,7 +142,7 @@ class YOLOv8Face(BaseDetector):
RuntimeError: If the model fails to load, logs an error and raises an exception.
"""
try:
self.session = create_onnx_session(model_path)
self.session = create_onnx_session(model_path, providers=self.providers)
self.input_names = self.session.get_inputs()[0].name
self.output_names = [x.name for x in self.session.get_outputs()]
Logger.info(f'Successfully initialized the model from {model_path}')

View File

@@ -38,6 +38,8 @@ class MobileGaze(BaseGazeEstimator):
Defaults to `GazeWeights.RESNET18`.
input_size (Tuple[int, int]): The resolution (width, height) for the model's
input. Defaults to (448, 448).
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
Attributes:
input_size (Tuple[int, int]): Model input dimensions.
@@ -65,12 +67,14 @@ class MobileGaze(BaseGazeEstimator):
self,
model_name: GazeWeights = GazeWeights.RESNET34,
input_size: tuple[int, int] = (448, 448),
providers: list[str] | None = None,
) -> None:
Logger.info(f'Initializing MobileGaze with model={model_name}, input_size={input_size}')
self.input_size = input_size
self.input_mean = [0.485, 0.456, 0.406]
self.input_std = [0.229, 0.224, 0.225]
self.providers = providers
# Model specific parameters for bin-based classification (Gaze360 config)
self._bins = 90
@@ -89,7 +93,7 @@ class MobileGaze(BaseGazeEstimator):
RuntimeError: If the model fails to load or initialize.
"""
try:
self.session = create_onnx_session(self.model_path)
self.session = create_onnx_session(self.model_path, providers=self.providers)
# Get input configuration
input_cfg = self.session.get_inputs()[0]

View File

@@ -31,6 +31,8 @@ class Landmark106(BaseLandmarker):
Defaults to `LandmarkWeights.DEFAULT`.
input_size (Tuple[int, int]): The resolution (width, height) for the model's
input. Defaults to (192, 192).
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
Example:
>>> # Assume 'image' is a loaded image and 'bbox' is a face bounding box
@@ -46,11 +48,13 @@ class Landmark106(BaseLandmarker):
self,
model_name: LandmarkWeights = LandmarkWeights.DEFAULT,
input_size: tuple[int, int] = (192, 192),
providers: list[str] | None = None,
) -> None:
Logger.info(f'Initializing Facial Landmark with model={model_name}, input_size={input_size}')
self.input_size = input_size
self.input_std = 1.0
self.input_mean = 0.0
self.providers = providers
self.model_path = verify_model_weights(model_name)
self._initialize_model()
@@ -62,7 +66,7 @@ class Landmark106(BaseLandmarker):
RuntimeError: If the model fails to load or initialize.
"""
try:
self.session = create_onnx_session(self.model_path)
self.session = create_onnx_session(self.model_path, providers=self.providers)
# Get input configuration
input_metadata = self.session.get_inputs()[0]

View File

@@ -51,6 +51,7 @@ def verify_model_weights(model_name: Enum, root: str = '~/.uniface/models') -> s
'/home/user/.uniface/models/retinaface_mnet_v2.onnx'
"""
root = os.getenv('UNIFACE_CACHE_DIR', root)
root = os.path.expanduser(root)
os.makedirs(root, exist_ok=True)

View File

@@ -37,6 +37,8 @@ class BiSeNet(BaseFaceParser):
Defaults to `ParsingWeights.RESNET18`.
input_size (Tuple[int, int]): The resolution (width, height) for the model's
input. Defaults to (512, 512).
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
Attributes:
input_size (Tuple[int, int]): Model input dimensions.
@@ -64,12 +66,14 @@ class BiSeNet(BaseFaceParser):
self,
model_name: ParsingWeights = ParsingWeights.RESNET18,
input_size: tuple[int, int] = (512, 512),
providers: list[str] | None = None,
) -> None:
Logger.info(f'Initializing BiSeNet with model={model_name}, input_size={input_size}')
self.input_size = input_size
self.input_mean = np.array([0.485, 0.456, 0.406], dtype=np.float32)
self.input_std = np.array([0.229, 0.224, 0.225], dtype=np.float32)
self.providers = providers
self.model_path = verify_model_weights(model_name)
self._initialize_model()
@@ -82,7 +86,7 @@ class BiSeNet(BaseFaceParser):
RuntimeError: If the model fails to load or initialize.
"""
try:
self.session = create_onnx_session(self.model_path)
self.session = create_onnx_session(self.model_path, providers=self.providers)
# Get input configuration
input_cfg = self.session.get_inputs()[0]

View File

@@ -31,6 +31,8 @@ class AdaFace(BaseRecognizer):
Defaults to `AdaFaceWeights.IR_18`.
preprocessing (Optional[PreprocessConfig]): An optional custom preprocessing
configuration. If None, a default config for AdaFace is used.
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
Example:
>>> from uniface.recognition import AdaFace
@@ -46,11 +48,12 @@ class AdaFace(BaseRecognizer):
self,
model_name: AdaFaceWeights = AdaFaceWeights.IR_18,
preprocessing: PreprocessConfig | None = None,
providers: list[str] | None = None,
) -> None:
if preprocessing is None:
preprocessing = PreprocessConfig(input_mean=127.5, input_std=127.5, input_size=(112, 112))
model_path = verify_model_weights(model_name)
super().__init__(model_path=model_path, preprocessing=preprocessing)
super().__init__(model_path=model_path, preprocessing=preprocessing, providers=providers)
def preprocess(self, face_img: np.ndarray) -> np.ndarray:
"""Preprocess the image: resize, normalize, and convert to blob.

View File

@@ -39,19 +39,27 @@ class BaseRecognizer(ABC):
"""
@abstractmethod
def __init__(self, model_path: str, preprocessing: PreprocessConfig) -> None:
def __init__(
self,
model_path: str,
preprocessing: PreprocessConfig,
providers: list[str] | None = None,
) -> None:
"""
Initializes the model. Subclasses must call this.
Args:
model_path (str): The direct path to the verified ONNX model.
preprocessing (PreprocessConfig): The configuration for preprocessing.
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
"""
self.input_mean = preprocessing.input_mean
self.input_std = preprocessing.input_std
self.input_size = preprocessing.input_size
self.model_path = model_path
self.providers = providers
self._initialize_model()
def _initialize_model(self) -> None:
@@ -63,7 +71,7 @@ class BaseRecognizer(ABC):
"""
try:
# Initialize model session with available providers
self.session = create_onnx_session(self.model_path)
self.session = create_onnx_session(self.model_path, providers=self.providers)
# Extract input configuration
input_cfg = self.session.get_inputs()[0]

View File

@@ -24,6 +24,8 @@ class ArcFace(BaseRecognizer):
Defaults to `ArcFaceWeights.MNET`.
preprocessing (Optional[PreprocessConfig]): An optional custom preprocessing
configuration. If None, a default config for ArcFace is used.
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
Example:
>>> from uniface.recognition import ArcFace
@@ -35,11 +37,12 @@ class ArcFace(BaseRecognizer):
self,
model_name: ArcFaceWeights = ArcFaceWeights.MNET,
preprocessing: PreprocessConfig | None = None,
providers: list[str] | None = None,
) -> None:
if preprocessing is None:
preprocessing = PreprocessConfig(input_mean=127.5, input_std=127.5, input_size=(112, 112))
model_path = verify_model_weights(model_name)
super().__init__(model_path=model_path, preprocessing=preprocessing)
super().__init__(model_path=model_path, preprocessing=preprocessing, providers=providers)
class MobileFace(BaseRecognizer):
@@ -54,6 +57,8 @@ class MobileFace(BaseRecognizer):
Defaults to `MobileFaceWeights.MNET_V2`.
preprocessing (Optional[PreprocessConfig]): An optional custom preprocessing
configuration. If None, a default config for MobileFaceNet is used.
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
Example:
>>> from uniface.recognition import MobileFace
@@ -65,11 +70,12 @@ class MobileFace(BaseRecognizer):
self,
model_name: MobileFaceWeights = MobileFaceWeights.MNET_V2,
preprocessing: PreprocessConfig | None = None,
providers: list[str] | None = None,
) -> None:
if preprocessing is None:
preprocessing = PreprocessConfig(input_mean=127.5, input_std=127.5, input_size=(112, 112))
model_path = verify_model_weights(model_name)
super().__init__(model_path=model_path, preprocessing=preprocessing)
super().__init__(model_path=model_path, preprocessing=preprocessing, providers=providers)
class SphereFace(BaseRecognizer):
@@ -84,6 +90,8 @@ class SphereFace(BaseRecognizer):
Defaults to `SphereFaceWeights.SPHERE20`.
preprocessing (Optional[PreprocessConfig]): An optional custom preprocessing
configuration. If None, a default config for SphereFace is used.
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
Example:
>>> from uniface.recognition import SphereFace
@@ -95,9 +103,10 @@ class SphereFace(BaseRecognizer):
self,
model_name: SphereFaceWeights = SphereFaceWeights.SPHERE20,
preprocessing: PreprocessConfig | None = None,
providers: list[str] | None = None,
) -> None:
if preprocessing is None:
preprocessing = PreprocessConfig(input_mean=127.5, input_std=127.5, input_size=(112, 112))
model_path = verify_model_weights(model_name)
super().__init__(model_path=model_path, preprocessing=preprocessing)
super().__init__(model_path=model_path, preprocessing=preprocessing, providers=providers)

View File

@@ -44,6 +44,8 @@ class MiniFASNet(BaseSpoofer):
scale (Optional[float]): Custom crop scale factor for face region.
If None, uses the default scale for the selected model variant.
V1SE uses 4.0, V2 uses 2.7.
providers (list[str] | None): ONNX Runtime execution providers. If None, auto-detects
the best available provider. Example: ['CPUExecutionProvider'] to force CPU.
Attributes:
scale (float): Crop scale factor for face region extraction.
@@ -68,11 +70,13 @@ class MiniFASNet(BaseSpoofer):
self,
model_name: MiniFASNetWeights = MiniFASNetWeights.V2,
scale: float | None = None,
providers: list[str] | None = None,
) -> None:
Logger.info(f'Initializing MiniFASNet with model={model_name.name}')
# Use default scale for the model variant if not specified
self.scale = scale if scale is not None else DEFAULT_SCALES.get(model_name, 2.7)
self.providers = providers
self.model_path = verify_model_weights(model_name)
self._initialize_model()
@@ -85,7 +89,7 @@ class MiniFASNet(BaseSpoofer):
RuntimeError: If the model fails to load or initialize.
"""
try:
self.session = create_onnx_session(self.model_path)
self.session = create_onnx_session(self.model_path, providers=self.providers)
# Get input configuration
input_cfg = self.session.get_inputs()[0]