Files
uniface/CONTRIBUTING.md
Yakhyokhuja Valikhujaev 50226041c9 refactor: Standardize naming conventions (#47)
* refactor: Standardize naming conventions

* chore: Update the version and re-run experiments

* chore: Improve code quality tooling and documentation

- Add pre-commit job to CI workflow for automated linting on PRs
- Update uniface/__init__.py with copyright header, module docstring,
  and logically grouped exports
- Revise CONTRIBUTING.md to reflect pre-commit handles all formatting
- Remove redundant ruff check from CI (now handled by pre-commit)
- Update build job Python version to 3.11 (matches requires-python)
2025-12-30 00:20:34 +09:00

5.3 KiB

Contributing to UniFace

Thank you for considering contributing to UniFace! We welcome contributions of all kinds.

How to Contribute

Reporting Issues

  • Use GitHub Issues to report bugs or suggest features
  • Include clear descriptions and reproducible examples
  • Check existing issues before creating new ones

Pull Requests

  1. Fork the repository
  2. Create a new branch for your feature
  3. Write clear, documented code with type hints
  4. Add tests for new functionality
  5. Ensure all tests pass and pre-commit hooks are satisfied
  6. Submit a pull request with a clear description

Development Setup

git clone https://github.com/yakhyo/uniface.git
cd uniface
pip install -e ".[dev]"

Setting Up Pre-commit Hooks

We use pre-commit to ensure code quality and consistency. Install and configure it:

# Install pre-commit
pip install pre-commit

# Install the git hooks
pre-commit install

# (Optional) Run against all files
pre-commit run --all-files

Once installed, pre-commit will automatically run on every commit to check:

  • Code formatting and linting (Ruff)
  • Security issues (Bandit)
  • General file hygiene (trailing whitespace, YAML/TOML validity, etc.)

Note: All PRs are automatically checked by CI. The merge button will only be available after all checks pass.

Code Style

This project uses Ruff for linting and formatting, following modern Python best practices. Pre-commit handles all formatting automatically.

Style Guidelines

General Rules

  • Line length: 120 characters maximum
  • Python version: 3.11+ (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):

# Preferred (modern)
def process(items: list[str], config: dict[str, int] | None = None) -> tuple[int, str]:
    ...

# Avoid (legacy)
from typing import List, Dict, Optional, Tuple
def process(items: List[str], config: Optional[Dict[str, int]] = None) -> Tuple[int, str]:
    ...

Docstrings

Use Google-style docstrings for all public APIs:

def detect_faces(image: np.ndarray, threshold: float = 0.5) -> list[Face]:
    """Detect faces in an image.

    Args:
        image: Input image as a numpy array with shape (H, W, C) in BGR format.
        threshold: Confidence threshold for filtering detections. Defaults to 0.5.

    Returns:
        List of Face objects containing bounding boxes, confidence scores,
        and facial landmarks.

    Raises:
        ValueError: If the input image has invalid dimensions.

    Example:
        >>> from uniface import detect_faces
        >>> faces = detect_faces(image, threshold=0.8)
        >>> print(f"Found {len(faces)} faces")
    """

Import Order

Imports are automatically sorted by Ruff with the following order:

  1. Future imports (from __future__ import annotations)
  2. Standard library (os, sys, typing, etc.)
  3. Third-party (numpy, cv2, onnxruntime, etc.)
  4. First-party (uniface.*)
  5. Local (relative imports like .base, .models)
from __future__ import annotations

import os
from typing import Any

import cv2
import numpy as np

from uniface.constants import RetinaFaceWeights
from uniface.log import Logger

from .base import BaseDetector

Code Comments

  • Add comments for complex logic, magic numbers, and non-obvious behavior
  • Avoid comments that merely restate the code
  • Use # TODO: with issue links for planned improvements
# RetinaFace FPN strides and corresponding anchor sizes per level
steps = [8, 16, 32]
min_sizes = [[16, 32], [64, 128], [256, 512]]

# Add small epsilon to prevent division by zero
similarity = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-5)

Running Tests

# Run all tests
pytest tests/

# Run with verbose output
pytest tests/ -v

# Run specific test file
pytest tests/test_factory.py

# Run with coverage
pytest tests/ --cov=uniface --cov-report=html

Adding New Features

When adding a new model or feature:

  1. Create the model class in the appropriate submodule (e.g., uniface/detection/)
  2. Add weight constants to uniface/constants.py with URLs and SHA256 hashes
  3. Export in __init__.py files at both module and package levels
  4. Write tests in tests/ directory
  5. Add example usage in scripts/ or update existing notebooks
  6. Update documentation if needed

Examples

Example notebooks demonstrating library usage:

Example Notebook
Face Detection 01_face_detection.ipynb
Face Alignment 02_face_alignment.ipynb
Face Verification 03_face_verification.ipynb
Face Search 04_face_search.ipynb
Face Analyzer 05_face_analyzer.ipynb
Face Parsing 06_face_parsing.ipynb
Face Anonymization 07_face_anonymization.ipynb
Gaze Estimation 08_gaze_estimation.ipynb

Questions?

Open an issue or start a discussion on GitHub.