feat: Add yolov5n, update docs and ruff code format

This commit is contained in:
yakhyo
2025-12-11 01:02:18 +09:00
parent 3982d677a9
commit da8a5cf35b
13 changed files with 49 additions and 18 deletions

View File

@@ -57,3 +57,4 @@ Example notebooks demonstrating library usage:
Open an issue or start a discussion on GitHub. Open an issue or start a discussion on GitHub.

View File

@@ -80,10 +80,11 @@ detector = SCRFD(
YOLOv5-Face models provide excellent detection accuracy with 5-point facial landmarks, optimized for real-time applications. YOLOv5-Face models provide excellent detection accuracy with 5-point facial landmarks, optimized for real-time applications.
| Model Name | Params | Size | Easy | Medium | Hard | FLOPs (G) | Use Case | | Model Name | Size | Easy | Medium | Hard | Use Case |
| -------------- | ------ | ---- | ------ | ------ | ------ | --------- | ------------------------------ | | -------------- | ---- | ------ | ------ | ------ | ------------------------------ |
| `YOLOV5S` ⭐ | 7.1M | 28MB | 94.33% | 92.61% | 83.15% | 5.751 | **Real-time + accuracy** | | `YOLOV5N` | 11MB | 93.61% | 91.52% | 80.53% | Lightweight/Mobile |
| `YOLOV5M` | 21.1M | 84MB | 95.30% | 93.76% | 85.28% | 18.146 | High accuracy | | `YOLOV5S` | 28MB | 94.33% | 92.61% | 83.15% | **Real-time + accuracy** |
| `YOLOV5M` | 82MB | 95.30% | 93.76% | 85.28% | High accuracy |
**Accuracy**: WIDER FACE validation set - from [YOLOv5-Face paper](https://arxiv.org/abs/2105.12931) **Accuracy**: WIDER FACE validation set - from [YOLOv5-Face paper](https://arxiv.org/abs/2105.12931)
**Speed**: Benchmark on your own hardware using `scripts/run_detection.py --iterations 100` **Speed**: Benchmark on your own hardware using `scripts/run_detection.py --iterations 100`
@@ -95,6 +96,13 @@ YOLOv5-Face models provide excellent detection accuracy with 5-point facial land
from uniface import YOLOv5Face from uniface import YOLOv5Face
from uniface.constants import YOLOv5FaceWeights from uniface.constants import YOLOv5FaceWeights
# Lightweight/Mobile
detector = YOLOv5Face(
model_name=YOLOv5FaceWeights.YOLOV5N,
conf_thresh=0.6,
nms_thresh=0.5
)
# Real-time detection (recommended) # Real-time detection (recommended)
detector = YOLOv5Face( detector = YOLOv5Face(
model_name=YOLOv5FaceWeights.YOLOV5S, model_name=YOLOv5FaceWeights.YOLOV5S,

View File

@@ -234,7 +234,7 @@ faces = detect_faces(image, method='retinaface', conf_thresh=0.8) # methods: re
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------- | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------- |
| `RetinaFace` | `model_name=RetinaFaceWeights.MNET_V2`, `conf_thresh=0.5`, `nms_thresh=0.4`, `input_size=(640, 640)`, `dynamic_size=False` | Supports 5-point landmarks | | `RetinaFace` | `model_name=RetinaFaceWeights.MNET_V2`, `conf_thresh=0.5`, `nms_thresh=0.4`, `input_size=(640, 640)`, `dynamic_size=False` | Supports 5-point landmarks |
| `SCRFD` | `model_name=SCRFDWeights.SCRFD_10G_KPS`, `conf_thresh=0.5`, `nms_thresh=0.4`, `input_size=(640, 640)` | Supports 5-point landmarks | | `SCRFD` | `model_name=SCRFDWeights.SCRFD_10G_KPS`, `conf_thresh=0.5`, `nms_thresh=0.4`, `input_size=(640, 640)` | Supports 5-point landmarks |
| `YOLOv5Face` | `model_name=YOLOv5FaceWeights.YOLOV5S`, `conf_thresh=0.6`, `nms_thresh=0.5`, `input_size=640` (fixed) | Landmarks supported;`input_size` must be 640 | | `YOLOv5Face` | `model_name=YOLOv5FaceWeights.YOLOV5S`, `conf_thresh=0.6`, `nms_thresh=0.5`, `input_size=640` (fixed) | Supports 5-point landmarks; models: YOLOV5N/S/M; `input_size` must be 640 |
**Recognition** **Recognition**
@@ -265,6 +265,7 @@ faces = detect_faces(image, method='retinaface', conf_thresh=0.8) # methods: re
| retinaface_r34 | 94.16% | 93.12% | 88.90% | High accuracy | | retinaface_r34 | 94.16% | 93.12% | 88.90% | High accuracy |
| scrfd_500m | 90.57% | 88.12% | 68.51% | Real-time applications | | scrfd_500m | 90.57% | 88.12% | 68.51% | Real-time applications |
| scrfd_10g | 95.16% | 93.87% | 83.05% | Best accuracy/speed | | scrfd_10g | 95.16% | 93.87% | 83.05% | Best accuracy/speed |
| yolov5n_face | 93.61% | 91.52% | 80.53% | Lightweight/Mobile |
| yolov5s_face | 94.33% | 92.61% | 83.15% | Real-time + accuracy | | yolov5s_face | 94.33% | 92.61% | 83.15% | Real-time + accuracy |
| yolov5m_face | 95.30% | 93.76% | 85.28% | High accuracy | | yolov5m_face | 95.30% | 93.76% | 85.28% | High accuracy |

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "uniface" name = "uniface"
version = "1.3.1" version = "1.3.2"
description = "UniFace: A Comprehensive Library for Face Detection, Recognition, Landmark Analysis, Age, and Gender Detection" description = "UniFace: A Comprehensive Library for Face Detection, Recognition, Landmark Analysis, Age, and Gender Detection"
readme = "README.md" readme = "README.md"
license = { text = "MIT" } license = { text = "MIT" }

View File

@@ -31,7 +31,9 @@ def process_image(detector, image_path: Path, output_path: Path, threshold: floa
bboxes = [f['bbox'] for f in faces] bboxes = [f['bbox'] for f in faces]
scores = [f['confidence'] for f in faces] scores = [f['confidence'] for f in faces]
landmarks = [f['landmarks'] for f in faces] landmarks = [f['landmarks'] for f in faces]
draw_detections(image=image, bboxes=bboxes, scores=scores, landmarks=landmarks, vis_threshold=threshold, fancy_bbox=True) draw_detections(
image=image, bboxes=bboxes, scores=scores, landmarks=landmarks, vis_threshold=threshold, fancy_bbox=True
)
cv2.putText( cv2.putText(
image, image,

View File

@@ -43,7 +43,9 @@ def process_image(
bboxes = [f['bbox'] for f in faces] bboxes = [f['bbox'] for f in faces]
scores = [f['confidence'] for f in faces] scores = [f['confidence'] for f in faces]
landmarks = [f['landmarks'] for f in faces] landmarks = [f['landmarks'] for f in faces]
draw_detections(image=image, bboxes=bboxes, scores=scores, landmarks=landmarks, vis_threshold=threshold, fancy_bbox=True) draw_detections(
image=image, bboxes=bboxes, scores=scores, landmarks=landmarks, vis_threshold=threshold, fancy_bbox=True
)
for i, face in enumerate(faces): for i, face in enumerate(faces):
gender_id, age = age_gender.predict(image, face['bbox']) gender_id, age = age_gender.predict(image, face['bbox'])

View File

@@ -51,7 +51,15 @@ def run_webcam(detector, threshold: float = 0.6):
bboxes = [f['bbox'] for f in faces] bboxes = [f['bbox'] for f in faces]
scores = [f['confidence'] for f in faces] scores = [f['confidence'] for f in faces]
landmarks = [f['landmarks'] for f in faces] landmarks = [f['landmarks'] for f in faces]
draw_detections(image=frame, bboxes=bboxes, scores=scores, landmarks=landmarks, vis_threshold=threshold, draw_score=True, fancy_bbox=True) draw_detections(
image=frame,
bboxes=bboxes,
scores=scores,
landmarks=landmarks,
vis_threshold=threshold,
draw_score=True,
fancy_bbox=True,
)
cv2.putText( cv2.putText(
frame, frame,
@@ -90,7 +98,7 @@ def main():
else: else:
from uniface.constants import YOLOv5FaceWeights from uniface.constants import YOLOv5FaceWeights
detector = YOLOv5Face(model_name=YOLOv5FaceWeights.YOLOV5M) detector = YOLOv5Face(model_name=YOLOv5FaceWeights.YOLOV5N)
if args.webcam: if args.webcam:
run_webcam(detector, args.threshold) run_webcam(detector, args.threshold)

View File

@@ -42,7 +42,9 @@ def process_image(
bboxes = [f['bbox'] for f in faces] bboxes = [f['bbox'] for f in faces]
scores = [f['confidence'] for f in faces] scores = [f['confidence'] for f in faces]
landmarks = [f['landmarks'] for f in faces] landmarks = [f['landmarks'] for f in faces]
draw_detections(image=image, bboxes=bboxes, scores=scores, landmarks=landmarks, vis_threshold=threshold, fancy_bbox=True) draw_detections(
image=image, bboxes=bboxes, scores=scores, landmarks=landmarks, vis_threshold=threshold, fancy_bbox=True
)
for i, face in enumerate(faces): for i, face in enumerate(faces):
emotion, confidence = emotion_predictor.predict(image, face['landmarks']) emotion, confidence = emotion_predictor.predict(image, face['landmarks'])

View File

@@ -82,7 +82,7 @@ def process_image(analyzer, image_path: str, save_dir: str = 'outputs', show_sim
bboxes = [f.bbox for f in faces] bboxes = [f.bbox for f in faces]
scores = [f.confidence for f in faces] scores = [f.confidence for f in faces]
landmarks = [f.landmarks for f in faces] landmarks = [f.landmarks for f in faces]
draw_detections(image=image, bboxes=bboxes, scores=scores, landmarks=landmarks,fancy_bbox=True) draw_detections(image=image, bboxes=bboxes, scores=scores, landmarks=landmarks, fancy_bbox=True)
for i, face in enumerate(faces, 1): for i, face in enumerate(faces, 1):
draw_face_info(image, face, i) draw_face_info(image, face, i)

View File

@@ -55,7 +55,9 @@ def process_video(
bboxes = [f['bbox'] for f in faces] bboxes = [f['bbox'] for f in faces]
scores = [f['confidence'] for f in faces] scores = [f['confidence'] for f in faces]
landmarks = [f['landmarks'] for f in faces] landmarks = [f['landmarks'] for f in faces]
draw_detections(image=frame, bboxes=bboxes, scores=scores, landmarks=landmarks, vis_threshold=threshold, fancy_bbox=True) draw_detections(
image=frame, bboxes=bboxes, scores=scores, landmarks=landmarks, vis_threshold=threshold, fancy_bbox=True
)
cv2.putText( cv2.putText(
frame, frame,

View File

@@ -13,7 +13,7 @@
__license__ = 'MIT' __license__ = 'MIT'
__author__ = 'Yakhyokhuja Valikhujaev' __author__ = 'Yakhyokhuja Valikhujaev'
__version__ = '1.3.1' __version__ = '1.3.2'
from uniface.face_utils import compute_similarity, face_alignment from uniface.face_utils import compute_similarity, face_alignment

View File

@@ -62,11 +62,13 @@ class YOLOv5FaceWeights(str, Enum):
Exported to ONNX from: https://github.com/yakhyo/yolov5-face-onnx-inference Exported to ONNX from: https://github.com/yakhyo/yolov5-face-onnx-inference
Model Performance (WIDER FACE): Model Performance (WIDER FACE):
- YOLOV5S: 7.1M params, 28MB, 94.33% Easy / 92.61% Medium / 83.15% Hard - YOLOV5N: 11MB, 93.61% Easy / 91.52% Medium / 80.53% Hard
- YOLOV5M: 21.1M params, 84MB, 95.30% Easy / 93.76% Medium / 85.28% Hard - YOLOV5S: 28MB, 94.33% Easy / 92.61% Medium / 83.15% Hard
- YOLOV5M: 82MB, 95.30% Easy / 93.76% Medium / 85.28% Hard
""" """
YOLOV5S = "yolov5s_face" YOLOV5N = "yolov5n"
YOLOV5M = "yolov5m_face" YOLOV5S = "yolov5s"
YOLOV5M = "yolov5m"
class DDAMFNWeights(str, Enum): class DDAMFNWeights(str, Enum):
@@ -117,6 +119,7 @@ MODEL_URLS: Dict[Enum, str] = {
SCRFDWeights.SCRFD_10G_KPS: 'https://github.com/yakhyo/uniface/releases/download/weights/scrfd_10g_kps.onnx', SCRFDWeights.SCRFD_10G_KPS: 'https://github.com/yakhyo/uniface/releases/download/weights/scrfd_10g_kps.onnx',
SCRFDWeights.SCRFD_500M_KPS: 'https://github.com/yakhyo/uniface/releases/download/weights/scrfd_500m_kps.onnx', SCRFDWeights.SCRFD_500M_KPS: 'https://github.com/yakhyo/uniface/releases/download/weights/scrfd_500m_kps.onnx',
# YOLOv5-Face # YOLOv5-Face
YOLOv5FaceWeights.YOLOV5N: 'https://github.com/yakhyo/yolov5-face-onnx-inference/releases/download/weights/yolov5n_face.onnx',
YOLOv5FaceWeights.YOLOV5S: 'https://github.com/yakhyo/yolov5-face-onnx-inference/releases/download/weights/yolov5s_face.onnx', YOLOv5FaceWeights.YOLOV5S: 'https://github.com/yakhyo/yolov5-face-onnx-inference/releases/download/weights/yolov5s_face.onnx',
YOLOv5FaceWeights.YOLOV5M: 'https://github.com/yakhyo/yolov5-face-onnx-inference/releases/download/weights/yolov5m_face.onnx', YOLOv5FaceWeights.YOLOV5M: 'https://github.com/yakhyo/yolov5-face-onnx-inference/releases/download/weights/yolov5m_face.onnx',
# DDAFM # DDAFM
@@ -151,6 +154,7 @@ MODEL_SHA256: Dict[Enum, str] = {
SCRFDWeights.SCRFD_10G_KPS: '5838f7fe053675b1c7a08b633df49e7af5495cee0493c7dcf6697200b85b5b91', SCRFDWeights.SCRFD_10G_KPS: '5838f7fe053675b1c7a08b633df49e7af5495cee0493c7dcf6697200b85b5b91',
SCRFDWeights.SCRFD_500M_KPS: '5e4447f50245bbd7966bd6c0fa52938c61474a04ec7def48753668a9d8b4ea3a', SCRFDWeights.SCRFD_500M_KPS: '5e4447f50245bbd7966bd6c0fa52938c61474a04ec7def48753668a9d8b4ea3a',
# YOLOv5-Face # YOLOv5-Face
YOLOv5FaceWeights.YOLOV5N: 'eb244a06e36999db732b317c2b30fa113cd6cfc1a397eaf738f2d6f33c01f640',
YOLOv5FaceWeights.YOLOV5S: 'fc682801cd5880e1e296184a14aea0035486b5146ec1a1389d2e7149cb134bb2', YOLOv5FaceWeights.YOLOV5S: 'fc682801cd5880e1e296184a14aea0035486b5146ec1a1389d2e7149cb134bb2',
YOLOv5FaceWeights.YOLOV5M: '04302ce27a15bde3e20945691b688e2dd018a10e92dd8932146bede6a49207b2', YOLOv5FaceWeights.YOLOV5M: '04302ce27a15bde3e20945691b688e2dd018a10e92dd8932146bede6a49207b2',
# DDAFM # DDAFM

View File

@@ -17,6 +17,7 @@ class Face:
""" """
Detected face with analysis results. Detected face with analysis results.
""" """
# Required attributes # Required attributes
bbox: np.ndarray bbox: np.ndarray
confidence: float confidence: float