From 6dbf4e6b8799c1485f72754b20467aa0ca1cdb0a Mon Sep 17 00:00:00 2001 From: yakhyo Date: Wed, 20 Nov 2024 08:43:25 +0000 Subject: [PATCH] Initial commit --- .github/workflows/build.yml | 64 +++++++++ .gitignore | 162 +++++++++++++++++++++++ LICENSE | 21 +++ README.md | 239 +++++++++++++++++++++++++++++++++ assets/test.jpg | Bin 0 -> 110760 bytes requirements.txt | 8 ++ scripts/release.sh | 23 ++++ setup.py | 43 ++++++ test.py | 57 ++++++++ tests/test_retinaface.py | 78 +++++++++++ uniface/__init__.py | 28 ++++ uniface/common.py | 178 +++++++++++++++++++++++++ uniface/constants.py | 26 ++++ uniface/log.py | 7 + uniface/model_store.py | 102 ++++++++++++++ uniface/retinaface.py | 256 ++++++++++++++++++++++++++++++++++++ uniface/version.py | 15 +++ uniface/visualization.py | 38 ++++++ 18 files changed, 1345 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 assets/test.jpg create mode 100644 requirements.txt create mode 100644 scripts/release.sh create mode 100644 setup.py create mode 100644 test.py create mode 100644 tests/test_retinaface.py create mode 100644 uniface/__init__.py create mode 100644 uniface/common.py create mode 100644 uniface/constants.py create mode 100644 uniface/log.py create mode 100644 uniface/model_store.py create mode 100644 uniface/retinaface.py create mode 100644 uniface/version.py create mode 100644 uniface/visualization.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..0f86164 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,64 @@ +name: Build, Test, and Publish + +on: + push: + branches: + - main + tags: + - "v*.*.*" # Trigger publish on version tags + pull_request: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10"] + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install .[dev] || pip install pytest # Use extras_require if available + + - name: Run Tests + run: | + pytest + + publish: + runs-on: ubuntu-latest + needs: build # Publish only if tests pass + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" # Use a single Python version for publishing + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install build twine + + - name: Build Package + run: python -m build + + - name: Publish to PyPI + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + run: twine upload dist/* diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82f9275 --- /dev/null +++ b/.gitignore @@ -0,0 +1,162 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7646fd5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Yakhyokhuja Valikhujaev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c4e406e --- /dev/null +++ b/README.md @@ -0,0 +1,239 @@ +# UniFace: All-in-One Face Analysis Library + +
+ +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) +![Python](https://img.shields.io/badge/Python-3.8%2B-blue) +[![PyPI Version](https://img.shields.io/pypi/v/uniface.svg)](https://pypi.org/project/uniface/) +[![Build Status](https://github.com/yakhyo/uniface/actions/workflows/build.yml/badge.svg)](https://github.com/yakhyo/uniface/actions) +[![Downloads](https://pepy.tech/badge/uniface)](https://pepy.tech/project/uniface) +[![Code Style: PEP8](https://img.shields.io/badge/code%20style-PEP8-green.svg)](https://www.python.org/dev/peps/pep-0008/) +[![GitHub Release Downloads](https://img.shields.io/github/downloads/yakhyo/uniface/total.svg?label=Model%20Downloads)](https://github.com/yakhyo/uniface/releases) + +
+ +**uniface** is a lightweight face detection library designed for high-performance face localization and landmark detection. The library supports ONNX models and provides utilities for bounding box visualization and landmark plotting. To train RetinaFace model, see https://github.com/yakhyo/retinaface-pytorch. + +--- + +## Features +- [ ] Age and gender detection (Planned). +- [ ] Face recognition (Planned). +- [x] High-speed face detection using ONNX models (Added: 2024-11-20). +- [x] Accurate facial landmark localization (e.g., eyes, nose, and mouth) (Added: 2024-11-20). +- [x] Easy-to-use API for inference and visualization (Added: 2024-11-20). + +--- + +## Installation + +### Using pip + +```bash +pip install uniface +``` + +### Local installation using pip + +**Clone the repository** + +```bash +git clone https://github.com/yakhyo/uniface.git +cd uniface +``` + +**Install using pip** + +```bash +pip install . +``` + +--- + +## Quick Start + +### Initialize the Model + +```python +from uniface import RetinaFace + +# Initialize the RetinaFace model +uniface_inference = RetinaFace( + model="retinaface_mnet_v2", # Model name + conf_thresh=0.5, # Confidence threshold + pre_nms_topk=5000, # Pre-NMS Top-K detections + nms_thresh=0.4, # NMS IoU threshold + post_nms_topk=750 # Post-NMS Top-K detections +) +``` + +### Run Inference + +Inference on image: + +```python +import cv2 +from uniface.visualization import draw_detections + +# Load an image +image_path = "assets/test.jpg" +original_image = cv2.imread(image_path) + +# Perform inference +boxes, landmarks = uniface_inference.detect(original_image) + +# Visualize results +draw_detections(original_image, (boxes, landmarks), vis_threshold=0.6) + +# Save the output image +output_path = "output.jpg" +cv2.imwrite(output_path, original_image) +print(f"Saved output image to {output_path}") +``` + +Inference on video: + +```python +import cv2 +from uniface.visualization import draw_detections + +# Initialize the webcam +cap = cv2.VideoCapture(0) + +if not cap.isOpened(): + print("Error: Unable to access the webcam.") + exit() + +while True: + # Capture a frame from the webcam + ret, frame = cap.read() + if not ret: + print("Error: Failed to read frame.") + break + + # Perform inference + boxes, landmarks = uniface_inference.detect(frame) + + # Draw detections on the frame + draw_detections(frame, (boxes, landmarks), vis_threshold=0.6) + + # Display the output + cv2.imshow("Webcam Inference", frame) + + # Exit if 'q' is pressed + if cv2.waitKey(1) & 0xFF == ord('q'): + break + +# Release the webcam and close all OpenCV windows +cap.release() +cv2.destroyAllWindows() +``` + +--- + +### Evaluation results of available models on WiderFace + +| RetinaFace Models | Easy | Medium | Hard | +| ------------------ | ---------- | ---------- | ---------- | +| retinaface_mnet025 | 88.48% | 87.02% | 80.61% | +| retinaface_mnet050 | 89.42% | 87.97% | 82.40% | +| retinaface_mnet_v1 | 90.59% | 89.14% | 84.13% | +| retinaface_mnet_v2 | 91.70% | 91.03% | 86.60% | +| retinaface_r18 | 92.50% | 91.02% | 86.63% | +| retinaface_r34 | **94.16%** | **93.12%** | **88.90%** | + +## API Reference + +### `RetinaFace` Class + +#### Initialization +```python +RetinaFace( + model: str, + conf_thresh: float = 0.5, + pre_nms_topk: int = 5000, + nms_thresh: float = 0.4, + post_nms_topk: int = 750 +) +``` + +**Parameters**: +- `model` *(str)*: Name of the model to use. Supported models: + - `retinaface_mnet025`, `retinaface_mnet050`, `retinaface_mnet_v1`, `retinaface_mnet_v2` + - `retinaface_r18`, `retinaface_r34` +- `conf_thresh` *(float, default=0.5)*: Minimum confidence score for detections. +- `pre_nms_topk` *(int, default=5000)*: Max detections to keep before NMS. +- `nms_thresh` *(float, default=0.4)*: IoU threshold for Non-Maximum Suppression. +- `post_nms_topk` *(int, default=750)*: Max detections to keep after NMS. + +--- + +### `detect` Method +```python +detect( + image: np.ndarray, + max_num: int = 0, + metric: str = "default", + center_weight: float = 2.0 +) -> Tuple[np.ndarray, np.ndarray] +``` + +**Description**: +Detects faces in the given image and returns bounding boxes and landmarks. + +**Parameters**: +- `image` *(np.ndarray)*: Input image in BGR format. +- `max_num` *(int, default=0)*: Maximum number of faces to return. `0` means return all. +- `metric` *(str, default="default")*: Metric for prioritizing detections: + - `"default"`: Prioritize detections closer to the image center. + - `"max"`: Prioritize larger bounding box areas. +- `center_weight` *(float, default=2.0)*: Weight for prioritizing center-aligned faces. + +**Returns**: +- `bounding_boxes` *(np.ndarray)*: Array of detections as `[x_min, y_min, x_max, y_max, confidence]`. +- `landmarks` *(np.ndarray)*: Array of landmarks as `[(x1, y1), ..., (x5, y5)]`. + +--- + +### Visualization Utilities + +#### `draw_detections` +```python +draw_detections( + image: np.ndarray, + detections: Tuple[np.ndarray, np.ndarray], + vis_threshold: float +) -> None +``` + +**Description**: +Draws bounding boxes and landmarks on the given image. + +**Parameters**: +- `image` *(np.ndarray)*: The input image in BGR format. +- `detections` *(Tuple[np.ndarray, np.ndarray])*: A tuple of bounding boxes and landmarks. +- `vis_threshold` *(float)*: Minimum confidence score for visualization. + +--- + +## Contributing + +We welcome contributions to enhance the library! Feel free to: + +- Submit bug reports or feature requests. +- Fork the repository and create a pull request. + +--- + +## License + +This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. + +--- + +## Acknowledgments + +- Based on the RetinaFace model for face detection ([https://github.com/yakhyo/retinaface-pytorch](https://github.com/yakhyo/retinaface-pytorch)). +- Inspired by InsightFace and other face detection projects. + +--- diff --git a/assets/test.jpg b/assets/test.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4559d9f9003865b862a4734534bce2f7c4d2268b GIT binary patch literal 110760 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
LF&1 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%0AcvnPltRzrs#7>mP+cber98mE0dF<>?~wHHnurW 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�VqAhmSG!;_E(IuGYq*BW@Tx|Z=tWRtKN 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.8", +) diff --git a/test.py b/test.py new file mode 100644 index 0000000..c8f241d --- /dev/null +++ b/test.py @@ -0,0 +1,57 @@ +import os +import cv2 +import numpy as np + +from uniface import RetinaFace, draw_detections + + +def run_inference(image_path, save_image=False, vis_threshold=0.6): + """ + Perform inference on an image, draw detections, and optionally save the output image. + + Args: + image_path (str): Path to the input image. + save_image (bool): Whether to save the output image with detections. + vis_threshold (float): Confidence threshold for displaying detections. + """ + # Load the image + original_image = cv2.imread(image_path) + if original_image is None: + print(f"Error: Could not read image from {image_path}") + return + + # Perform face detection + boxes, landmarks = retinaface_inference.detect(original_image) + + # Draw detections on the image + draw_detections(original_image, (boxes, landmarks), vis_threshold) + + # Save the output image if requested + if save_image: + im_name = os.path.splitext(os.path.basename(image_path))[0] + save_name = f"{im_name}_out.jpg" + cv2.imwrite(save_name, original_image) + print(f"Image saved at '{save_name}'") + + +if __name__ == '__main__': + import time + + # Initialize and run the ONNX inference + retinaface_inference = RetinaFace( + model="retinaface_mnet_v2", + conf_thresh=0.5, + pre_nms_topk=5000, + nms_thresh=0.4, + post_nms_topk=750, + ) + + img_path = "assets/test.jpg" + avg = 0 + for _ in range(50): + st = time.time() + run_inference(img_path, save_image=True, vis_threshold=0.6) + d = time.time() - st + print(d) + avg += d + print("avg", avg / 50) diff --git a/tests/test_retinaface.py b/tests/test_retinaface.py new file mode 100644 index 0000000..e49ab17 --- /dev/null +++ b/tests/test_retinaface.py @@ -0,0 +1,78 @@ +import pytest +import numpy as np +from uniface import RetinaFace + + +@pytest.fixture +def retinaface_model(): + """ + Fixture to initialize the RetinaFace model for testing. + """ + return RetinaFace( + model="retinaface_mnet_v2", + conf_thresh=0.5, + pre_nms_topk=5000, + nms_thresh=0.4, + post_nms_topk=750, + ) + + +def test_model_initialization(retinaface_model): + """ + Test that the RetinaFace model initializes correctly. + """ + assert retinaface_model is not None, "Model initialization failed." + + +def test_inference_on_640x640_image(retinaface_model): + """ + Test inference on a 640x640 BGR image. + """ + # Generate a mock 640x640 BGR image + mock_image = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8) + + # Run inference + detections, landmarks = retinaface_model.detect(mock_image) + + # Check output types + assert isinstance(detections, np.ndarray), "Detections should be a numpy array." + assert isinstance(landmarks, np.ndarray), "Landmarks should be a numpy array." + + # Check that detections have the expected shape + if detections.size > 0: # If faces are detected + assert detections.shape[1] == 5, "Each detection should have 5 values (x1, y1, x2, y2, score)." + + # Check landmarks shape + if landmarks.size > 0: + assert landmarks.shape[1:] == (5, 2), "Landmarks should have shape (N, 5, 2)." + + +def test_confidence_threshold(retinaface_model): + """ + Test that detections respect the confidence threshold. + """ + # Generate a mock 640x640 BGR image + mock_image = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8) + + # Run inference + detections, _ = retinaface_model.detect(mock_image) + + # Ensure all detections have confidence scores above the threshold + if detections.size > 0: # If faces are detected + confidence_scores = detections[:, 4] + assert (confidence_scores >= 0.5).all(), "Some detections have confidence below the threshold." + + +def test_no_faces_detected(retinaface_model): + """ + Test inference on an image without detectable faces. + """ + # Generate an empty (black) 640x640 image + empty_image = np.zeros((640, 640, 3), dtype=np.uint8) + + # Run inference + detections, landmarks = retinaface_model.detect(empty_image) + + # Ensure no detections or landmarks are found + assert detections.size == 0, "Detections should be empty for a blank image." + assert landmarks.size == 0, "Landmarks should be empty for a blank image." diff --git a/uniface/__init__.py b/uniface/__init__.py new file mode 100644 index 0000000..006c1c2 --- /dev/null +++ b/uniface/__init__.py @@ -0,0 +1,28 @@ +# Copyright 2024 Yakhyokhuja Valikhujaev +# +# Licensed under the MIT License. +# You may obtain a copy of the License at +# +# https://opensource.org/licenses/MIT +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from uniface.retinaface import RetinaFace +from uniface.log import Logger +from uniface.model_store import verify_model_weights +from uniface.version import __version__, __author__ +from uniface.visualization import draw_detections + +__all__ = [ + "__version__", + "__author__" + "RetinaFace", + "Logger", + "verify_model_weights", + "draw_detections" +] diff --git a/uniface/common.py b/uniface/common.py new file mode 100644 index 0000000..b65a966 --- /dev/null +++ b/uniface/common.py @@ -0,0 +1,178 @@ +# Copyright 2024 Yakhyokhuja Valikhujaev +# Author: Yakhyokhuja Valikhujaev +# GitHub: https://github.com/yakhyo + +import cv2 +import math +import itertools +import numpy as np + +import torch +from typing import Tuple, List + + +def resize_image(frame, target_shape: Tuple[int, int] = (640, 640)) -> Tuple[np.ndarray, float]: + """ + Resize an image to fit within a target shape while keeping its aspect ratio. + + Args: + frame (np.ndarray): Input image. + target_shape (Tuple[int, int]): Target size (width, height). Defaults to (640, 640). + + Returns: + Tuple[np.ndarray, float]: Resized image on a blank canvas and the resize factor. + """ + width, height = target_shape + + # Aspect-ratio preserving resize + im_ratio = float(frame.shape[0]) / frame.shape[1] + model_ratio = height / width + if im_ratio > model_ratio: + new_height = height + new_width = int(new_height / im_ratio) + else: + new_width = width + new_height = int(new_width * im_ratio) + + resize_factor = float(new_height) / frame.shape[0] + resized_frame = cv2.resize(frame, (new_width, new_height)) + + # Create blank image and place resized image on it + image = np.zeros((height, width, 3), dtype=np.uint8) + image[:new_height, :new_width, :] = resized_frame + + return image, resize_factor + + +def generate_anchors(image_size: Tuple[int, int] = (640, 640)) -> torch.Tensor: + """ + Generate anchor boxes for a given image size. + + Args: + image_size (Tuple[int, int]): Input image size (width, height). Defaults to (640, 640). + + Returns: + torch.Tensor: Anchor box coordinates as a tensor. + """ + image_size = image_size + + steps = [8, 16, 32] + min_sizes = [[16, 32], [64, 128], [256, 512]] + + anchors = [] + feature_maps = [ + [ + math.ceil(image_size[0] / step), + math.ceil(image_size[1] / step) + ] for step in steps + ] + + for k, (map_height, map_width) in enumerate(feature_maps): + step = steps[k] + for i, j in itertools.product(range(map_height), range(map_width)): + for min_size in min_sizes[k]: + s_kx = min_size / image_size[1] + s_ky = min_size / image_size[0] + + dense_cx = [x * step / image_size[1] for x in [j + 0.5]] + dense_cy = [y * step / image_size[0] for y in [i + 0.5]] + for cy, cx in itertools.product(dense_cy, dense_cx): + anchors += [cx, cy, s_kx, s_ky] + + output = torch.Tensor(anchors).view(-1, 4) + return output + + +def nms(dets: List[np.ndarray], threshold: float): + """ + Apply Non-Maximum Suppression (NMS) to reduce overlapping bounding boxes based on a threshold. + + Args: + dets (numpy.ndarray): Array of detections with each row as [x1, y1, x2, y2, score]. + threshold (float): IoU threshold for suppression. + + Returns: + list: Indices of bounding boxes retained after suppression. + """ + x1 = dets[:, 0] + y1 = dets[:, 1] + x2 = dets[:, 2] + y2 = dets[:, 3] + scores = dets[:, 4] + + areas = (x2 - x1 + 1) * (y2 - y1 + 1) + order = scores.argsort()[::-1] + + keep = [] + while order.size > 0: + i = order[0] + keep.append(i) + xx1 = np.maximum(x1[i], x1[order[1:]]) + yy1 = np.maximum(y1[i], y1[order[1:]]) + xx2 = np.minimum(x2[i], x2[order[1:]]) + yy2 = np.minimum(y2[i], y2[order[1:]]) + + w = np.maximum(0.0, xx2 - xx1 + 1) + h = np.maximum(0.0, yy2 - yy1 + 1) + inter = w * h + ovr = inter / (areas[i] + areas[order[1:]] - inter) + + inds = np.where(ovr <= threshold)[0] + order = order[inds + 1] + + return keep + + +def decode_boxes(loc, priors, variances=[0.1, 0.2]) -> torch.Tensor: + """ + Decode locations from predictions using priors to undo + the encoding done for offset regression at train time. + + Args: + loc (tensor): Location predictions for loc layers, shape: [num_priors, 4] + priors (tensor): Prior boxes in center-offset form, shape: [num_priors, 4] + variances (list[float]): Variances of prior boxes + + Returns: + tensor: Decoded bounding box predictions + """ + # Compute centers of predicted boxes + cxcy = priors[:, :2] + loc[:, :2] * variances[0] * priors[:, 2:] + + # Compute widths and heights of predicted boxes + wh = priors[:, 2:] * torch.exp(loc[:, 2:] * variances[1]) + + # Convert center, size to corner coordinates + boxes = torch.empty_like(loc) + boxes[:, :2] = cxcy - wh / 2 # xmin, ymin + boxes[:, 2:] = cxcy + wh / 2 # xmax, ymax + + return boxes + + +def decode_landmarks(predictions, priors, variances=[0.1, 0.2]) -> torch.Tensor: + """ + Decode landmarks from predictions using prior boxes to reverse the encoding done during training. + + Args: + predictions (tensor): Landmark predictions for localization layers. + Shape: [num_priors, 10] where each prior contains 5 landmark (x, y) pairs. + priors (tensor): Prior boxes in center-offset form. + Shape: [num_priors, 4], where each prior has (cx, cy, width, height). + variances (list[float]): Variances of the prior boxes to scale the decoded values. + + Returns: + landmarks (tensor): Decoded landmark predictions. + Shape: [num_priors, 10] where each row contains the decoded (x, y) pairs for 5 landmarks. + """ + + # Reshape predictions to [num_priors, 5, 2] to handle each pair (x, y) in a batch + predictions = predictions.view(predictions.size(0), 5, 2) + + # Perform the same operation on all landmark pairs at once + landmarks = priors[:, :2].unsqueeze(1) + predictions * variances[0] * priors[:, 2:].unsqueeze(1) + + # Flatten back to [num_priors, 10] + landmarks = landmarks.view(landmarks.size(0), -1) + + return landmarks diff --git a/uniface/constants.py b/uniface/constants.py new file mode 100644 index 0000000..b4131d5 --- /dev/null +++ b/uniface/constants.py @@ -0,0 +1,26 @@ +# Copyright 2024 Yakhyokhuja Valikhujaev +# Author: Yakhyokhuja Valikhujaev +# GitHub: https://github.com/yakhyo + +from typing import Dict + + +MODEL_URLS: Dict[str, str] = { + 'retinaface_mnet025': 'https://github.com/yakhyo/uniface/releases/download/v0.0.1/retinaface_mv1_0.25.onnx', + 'retinaface_mnet050': 'https://github.com/yakhyo/uniface/releases/download/v0.0.1/retinaface_mv1_0.50.onnx', + 'retinaface_mnet_v1': 'https://github.com/yakhyo/uniface/releases/download/v0.0.1/retinaface_mv1.onnx', + 'retinaface_mnet_v2': 'https://github.com/yakhyo/uniface/releases/download/v0.0.1/retinaface_mv2.onnx', + 'retinaface_r18': 'https://github.com/yakhyo/uniface/releases/download/v0.0.1/retinaface_r18.onnx', + 'retinaface_r34': 'https://github.com/yakhyo/uniface/releases/download/v0.0.1/retinaface_r34.onnx' +} + +MODEL_SHA256: Dict[str, str] = { + 'retinaface_mnet025': 'b7a7acab55e104dce6f32cdfff929bd83946da5cd869b9e2e9bdffafd1b7e4a5', + 'retinaface_mnet050': 'd8977186f6037999af5b4113d42ba77a84a6ab0c996b17c713cc3d53b88bfc37', + 'retinaface_mnet_v1': '75c961aaf0aff03d13c074e9ec656e5510e174454dd4964a161aab4fe5f04153', + 'retinaface_mnet_v2': '3ca44c045651cabeed1193a1fae8946ad1f3a55da8fa74b341feab5a8319f757', + 'retinaface_r18': 'e8b5ddd7d2c3c8f7c942f9f10cec09d8e319f78f09725d3f709631de34fb649d', + 'retinaface_r34': 'bd0263dc2a465d32859555cb1741f2d98991eb0053696e8ee33fec583d30e630' +} + +CHUNK_SIZE = 8192 diff --git a/uniface/log.py b/uniface/log.py new file mode 100644 index 0000000..3ac8c9f --- /dev/null +++ b/uniface/log.py @@ -0,0 +1,7 @@ +import logging + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s" +) +Logger = logging.getLogger("retinaface") diff --git a/uniface/model_store.py b/uniface/model_store.py new file mode 100644 index 0000000..d267dad --- /dev/null +++ b/uniface/model_store.py @@ -0,0 +1,102 @@ +# Copyright 2024 Yakhyokhuja Valikhujaev +# Author: Yakhyokhuja Valikhujaev +# GitHub: https://github.com/yakhyo + +import os +import hashlib +import requests + +from uniface.log import Logger +import uniface.constants as const + + +def verify_model_weights(model_name: str, root: str = '~/.uniface/models') -> str: + """ + Ensures model weights are available by downloading if missing and verifying integrity with a SHA-256 hash. + + Checks if the specified model weights file exists in `root`. If missing, downloads from a predefined URL. + The file is then verified using its SHA-256 hash. If verification fails, the corrupted file is deleted, + and an error is raised. + + Args: + model_name (str): Name of the model weights to verify or download. + root (str, optional): Directory to store the model weights. Defaults to '~/.uniface/models'. + + Returns: + str: Path to the verified model weights file. + + Raises: + ValueError: If the model is not found or if verification fails. + ConnectionError: If downloading the file fails. + + Examples: + >>> # Download and verify 'retinaface_mnet025' weights + >>> verify_model_weights('retinaface_mnet025') + '/home/user/.uniface/models/retinaface_mnet025.onnx' + + >>> # Use a custom directory + >>> verify_model_weights('retinaface_r34', root='/custom/dir') + '/custom/dir/retinaface_r34.onnx' + """ + + root = os.path.expanduser(root) + os.makedirs(root, exist_ok=True) + model_path = os.path.join(root, f'{model_name}.onnx') + + if not os.path.exists(model_path): + url = const.MODEL_URLS.get(model_name) + if not url: + Logger.error(f"No URL found for model '{model_name}'") + raise ValueError(f"No URL found for model '{model_name}'") + + Logger.info(f"Downloading '{model_name}' from {url}") + download_file(url, model_path) + Logger.info(f"Successfully '{model_name}' downloaded to {model_path}") + + expected_hash = const.MODEL_SHA256.get(model_name) + if expected_hash and not verify_file_hash(model_path, expected_hash): + os.remove(model_path) # Remove corrupted file + Logger.warning("Corrupted weight detected. Removing...") + raise ValueError(f"Hash mismatch for '{model_name}'. The file may be corrupted; please try downloading again.") + + return model_path + + +def download_file(url: str, dest_path: str) -> None: + """Download a file from a URL in chunks and save it to the destination path.""" + try: + response = requests.get(url, stream=True) + response.raise_for_status() + with open(dest_path, "wb") as file: + for chunk in response.iter_content(chunk_size=const.CHUNK_SIZE): + if chunk: + file.write(chunk) + except requests.RequestException as e: + raise ConnectionError(f"Failed to download file from {url}. Error: {e}") + + +def verify_file_hash(file_path: str, expected_hash: str) -> bool: + """Compute the SHA-256 hash of the file and compare it with the expected hash.""" + file_hash = hashlib.sha256() + with open(file_path, "rb") as f: + for chunk in iter(lambda: f.read(const.CHUNK_SIZE), b""): + file_hash.update(chunk) + actual_hash = file_hash.hexdigest() + if actual_hash != expected_hash: + Logger.warning(f"Expected hash: {expected_hash}, but got: {actual_hash}") + return actual_hash == expected_hash + + +if __name__ == "__main__": + model_names = [ + 'retinaface_mnet025', + 'retinaface_mnet050', + 'retinaface_mnet_v1', + 'retinaface_mnet_v2', + 'retinaface_r18', + 'retinaface_r34' + ] + + # Download each model in the list + for model_name in model_names: + model_path = verify_model_weights(model_name) diff --git a/uniface/retinaface.py b/uniface/retinaface.py new file mode 100644 index 0000000..570f5c3 --- /dev/null +++ b/uniface/retinaface.py @@ -0,0 +1,256 @@ +# Copyright 2024 Yakhyokhuja Valikhujaev +# Author: Yakhyokhuja Valikhujaev +# GitHub: https://github.com/yakhyo + +import os +import cv2 +import numpy as np +import onnxruntime as ort + +import torch +from typing import Tuple, List, Optional, Literal + +from uniface.log import Logger +from uniface.model_store import verify_model_weights + +from uniface.common import ( + nms, + resize_image, + decode_boxes, + generate_anchors, + decode_landmarks +) + + +class RetinaFace: + """ + A class for face detection using the RetinaFace model. + + Args: + model (str): Path or identifier of the model weights. + conf_thresh (float): Confidence threshold for detections. Defaults to 0.5. + nms_thresh (float): Non-maximum suppression threshold. Defaults to 0.4. + pre_nms_topk (int): Maximum number of detections before NMS. Defaults to 5000. + post_nms_topk (int): Maximum number of detections after NMS. Defaults to 750. + dynamic_size (Optional[bool]): Whether to adjust anchor generation dynamically based on image size. Defaults to False. + input_size (Optional[Tuple[int, int]]): Static input size for the model (width, height). Defaults to (640, 640). + + Attributes: + conf_thresh (float): Confidence threshold for filtering detections. + nms_thresh (float): Threshold for NMS to remove duplicate detections. + pre_nms_topk (int): Maximum detections to consider before applying NMS. + post_nms_topk (int): Maximum detections retained after applying NMS. + dynamic_size (bool): Indicates if input size and anchors are dynamically adjusted. + input_size (Tuple[int, int]): The model's input image size. + _model_path (str): Path to the model weights. + _priors (torch.Tensor): Precomputed anchor boxes for static input size. + """ + + def __init__( + self, + model: str, + conf_thresh: float = 0.5, + nms_thresh: float = 0.4, + pre_nms_topk: int = 5000, + post_nms_topk: int = 750, + dynamic_size: Optional[bool] = False, + input_size: Optional[Tuple[int, int]] = (640, 640), # Default input size if dynamic_size=False + ) -> None: + + self.conf_thresh = conf_thresh + self.nms_thresh = nms_thresh + self.pre_nms_topk = pre_nms_topk + self.post_nms_topk = post_nms_topk + self.dynamic_size = dynamic_size + self.input_size = input_size + + Logger.info( + f"Initializing RetinaFace with model={model}, conf_thresh={conf_thresh}, nms_thresh={nms_thresh}, " + f"pre_nms_topk={pre_nms_topk}, post_nms_topk={post_nms_topk}, dynamic_size={dynamic_size}, " + f"input_size={input_size}" + ) + + # Get path to model weights + self._model_path = verify_model_weights(model) + Logger.info(f"Verified model weights located at: {self._model_path}") + + # Precompute anchors if using static size + if not dynamic_size and input_size is not None: + self._priors = generate_anchors(image_size=input_size) + Logger.debug("Generated anchors for static input size.") + + # Initialize model + self._initialize_model(self._model_path) + + def _initialize_model(self, model_path: str) -> None: + """ + Initializes an ONNX model session from the given path. + + Args: + model_path (str): The file path to the ONNX model. + + Raises: + RuntimeError: If the model fails to load, logs an error and raises an exception. + """ + try: + self.session = ort.InferenceSession(model_path) + self.input_name = self.session.get_inputs()[0].name + Logger.info(f"Successfully initialized the model from {model_path}") + except Exception as e: + Logger.error(f"Failed to load model from '{model_path}': {e}") + raise RuntimeError(f"Failed to initialize model session for '{model_path}'") from e + + def preprocess(self, image: np.ndarray) -> np.ndarray: + """Preprocess input image for model inference. + + Args: + image (np.ndarray): Input image. + + Returns: + np.ndarray: Preprocessed image tensor with shape (1, C, H, W) + """ + image = np.float32(image) - np.array([104, 117, 123], dtype=np.float32) + image = image.transpose(2, 0, 1) # HWC to CHW + image = np.expand_dims(image, axis=0) # Add batch dimension (1, C, H, W) + return image + + def inference(self, input_tensor: np.ndarray) -> List[np.ndarray]: + """Perform model inference on the preprocessed image tensor. + + Args: + input_tensor (np.ndarray): Preprocessed input tensor. + + Returns: + Tuple[np.ndarray, np.ndarray]: Raw model outputs. + """ + return self.session.run(None, {self.input_name: input_tensor}) + + def detect( + self, + image: np.ndarray, + max_num: Optional[int] = 0, + metric: Literal["default", "max"] = "default", + center_weight: Optional[float] = 2.0 + ) -> Tuple[np.ndarray, np.ndarray]: + """ + Perform face detection on an input image and return bounding boxes and landmarks. + + Args: + image (np.ndarray): Input image as a NumPy array of shape (height, width, channels). + max_num (int, optional): Maximum number of detections to return. Defaults to 1. + metric (str, optional): Metric for ranking detections when `max_num` is specified. + Options: + - "default": Prioritize detections closer to the image center. + - "max": Prioritize detections with larger bounding box areas. + center_weight (float, optional): Weight for penalizing detections farther from the image center + when using the "default" metric. Defaults to 2.0. + + Returns: + Tuple[np.ndarray, np.ndarray]: Detection results containing: + - detections (np.ndarray): Array of detected bounding boxes with confidence scores. + Shape: (num_detections, 5), where each row is [x_min, y_min, x_max, y_max, score]. + - landmarks (np.ndarray): Array of detected facial landmarks. + Shape: (num_detections, 5, 2), where each row contains 5 landmark points (x, y). + """ + + if self.dynamic_size: + height, width, _ = image.shape + self._priors = generate_anchors(image_size=(height, width)) # generate anchors for each input image + resize_factor = 1.0 # No resizing + else: + image, resize_factor = resize_image(image, target_shape=self.input_size) + + height, width, _ = image.shape + image_tensor = self.preprocess(image) + + # ONNXRuntime inference + outputs = self.inference(image_tensor) + + # Postprocessing + detections, landmarks = self.postprocess(outputs, resize_factor, shape=(width, height)) + + if max_num > 0 and detections.shape[0] > max_num: + # Calculate area of detections + areas = (detections[:, 2] - detections[:, 0]) * (detections[:, 3] - detections[:, 1]) + + # Calculate offsets from image center + center = (height // 2, width // 2) + offsets = np.vstack([ + (detections[:, 0] + detections[:, 2]) / 2 - center[1], + (detections[:, 1] + detections[:, 3]) / 2 - center[0] + ]) + offset_dist_squared = np.sum(np.power(offsets, 2.0), axis=0) + + # Calculate scores based on the chosen metric + if metric == 'max': + scores = areas + else: + scores = areas - offset_dist_squared * center_weight + + # Sort by scores and select top `max_num` + sorted_indices = np.argsort(scores)[::-1][:max_num] + + detections = detections[sorted_indices] + landmarks = landmarks[sorted_indices] + + return detections, landmarks + + def postprocess(self, outputs: List[np.ndarray], resize_factor: float, shape: Tuple[int, int]) -> Tuple[np.ndarray, np.ndarray]: + """ + Process the model outputs into final detection results. + + Args: + outputs (List[np.ndarray]): Raw outputs from the detection model. + - outputs[0]: Location predictions (bounding box coordinates). + - outputs[1]: Class confidence scores. + - outputs[2]: Landmark predictions. + resize_factor (float): Factor used to resize the input image during preprocessing. + shape (Tuple[int, int]): Original shape of the image as (height, width). + + Returns: + Tuple[np.ndarray, np.ndarray]: Processed results containing: + - detections (np.ndarray): Array of detected bounding boxes with confidence scores. + Shape: (num_detections, 5), where each row is [x_min, y_min, x_max, y_max, score]. + - landmarks (np.ndarray): Array of detected facial landmarks. + Shape: (num_detections, 5, 2), where each row contains 5 landmark points (x, y). + """ + loc, conf, landmarks = outputs[0].squeeze(0), outputs[1].squeeze(0), outputs[2].squeeze(0) + + # Decode boxes and landmarks + boxes = decode_boxes(torch.tensor(loc), self._priors).cpu().numpy() + landmarks = decode_landmarks(torch.tensor(landmarks), self._priors).cpu().numpy() + + boxes, landmarks = self._scale_detections(boxes, landmarks, resize_factor, shape=(shape[0], shape[1])) + + # Extract confidence scores for the face class + scores = conf[:, 1] + mask = scores > self.conf_thresh + + # Filter by confidence threshold + boxes, landmarks, scores = boxes[mask], landmarks[mask], scores[mask] + + # Sort by scores + order = scores.argsort()[::-1][:self.pre_nms_topk] + boxes, landmarks, scores = boxes[order], landmarks[order], scores[order] + + # Apply NMS + detections = np.hstack((boxes, scores[:, np.newaxis])).astype(np.float32, copy=False) + keep = nms(detections, self.nms_thresh) + detections, landmarks = detections[keep], landmarks[keep] + + # Keep top-k detections + detections, landmarks = detections[:self.post_nms_topk], landmarks[:self.post_nms_topk] + + landmarks = landmarks.reshape(-1, 5, 2).astype(np.int32) + + return detections, landmarks + + def _scale_detections(self, boxes: np.ndarray, landmarks: np.ndarray, resize_factor: float, shape: Tuple[int, int]) -> Tuple[np.ndarray, np.ndarray]: + """Scale bounding boxes and landmarks to the original image size.""" + bbox_scale = np.array([shape[0], shape[1]] * 2) + boxes = boxes * bbox_scale / resize_factor + + landmark_scale = np.array([shape[0], shape[1]] * 5) + landmarks = landmarks * landmark_scale / resize_factor + + return boxes, landmarks diff --git a/uniface/version.py b/uniface/version.py new file mode 100644 index 0000000..ef80910 --- /dev/null +++ b/uniface/version.py @@ -0,0 +1,15 @@ +# Copyright 2024 Yakhyokhuja Valikhujaev +# +# Licensed under the MIT License. +# You may obtain a copy of the License at +# +# https://opensource.org/licenses/MIT +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__version__ = "0.1.2" +__author__ = "Yakhyokhuja Valikhujaev" diff --git a/uniface/visualization.py b/uniface/visualization.py new file mode 100644 index 0000000..5d73fda --- /dev/null +++ b/uniface/visualization.py @@ -0,0 +1,38 @@ +# Copyright 2024 Yakhyokhuja Valikhujaev +# Author: Yakhyokhuja Valikhujaev +# GitHub: https://github.com/yakhyo + +import cv2 +import numpy as np + + +def draw_detections(image, detections, vis_threshold=0.6): + """ + Draw bounding boxes and landmarks on the image. + + Args: + image (ndarray): Image to draw detections on. + detections (tuple): (bounding boxes, landmarks) as NumPy arrays. + vis_threshold (float): Confidence threshold for filtering detections. + """ + + _colors = [(0, 0, 255), (0, 255, 255), (255, 0, 255), (0, 255, 0), (255, 0, 0)] + + # Unpack detections + boxes, landmarks = detections + scores = boxes[:, 4] + + # Filter detections by confidence threshold + filtered = scores >= vis_threshold + boxes = boxes[filtered, :4].astype(np.int32) + landmarks = landmarks[filtered] + scores = scores[filtered] + + print(f"#faces: {len(scores)}") + + # Draw bounding boxes, scores, and landmarks + for box, score, landmark in zip(boxes, scores, landmarks): + cv2.rectangle(image, box[:2], box[2:], (0, 0, 255), 2) + cv2.putText(image, f"{score:.2f}", (box[0], box[1] + 12), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) + for point, color in zip(landmark, _colors): + cv2.circle(image, tuple(point), 2, color, -1)