# SPDX-FileCopyrightText: 2025 Idiap Research Institute # SPDX-FileContributor: Anjith George # SPDX-License-Identifier: BSD-3-Clause """EdgeFace demo""" from __future__ import annotations from pathlib import Path import cv2 import gradio as gr import numpy as np import torch import torch.nn.functional as F from torchvision import transforms from huggingface_hub import hf_hub_download from utils import align_crop from title import title_css, title_with_logo from timmfrv2 import TimmFRWrapperV2, model_configs # ─────────────────────────────── # Data & models # ─────────────────────────────── DATA_DIR = Path("data") EXTS = (".jpg", ".jpeg", ".png", ".bmp", ".webp") PRELOADED = sorted(p for p in DATA_DIR.iterdir() if p.suffix.lower() in EXTS) EDGE_MODELS = [ "edgeface_base", "edgeface_s_gamma_05", "edgeface_xs_gamma_06", "edgeface_xxs", ] # ─────────────────────────────── # Styling (orange palette) # ─────────────────────────────── PRIMARY = "#F97316" PRIMARY_DARK = "#C2410C" ACCENT_LIGHT = "#FFEAD2" BG_LIGHT = "#FFFBF7" CARD_BG_DARK = "#473f38" BG_DARK = "#332a22" TEXT_DARK = "#0F172A" TEXT_LIGHT = "#f8fafc" CSS = f""" @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap'); /* ─── palette ───────────────────────────────────────────── */ body, .gradio-container {{ font-family: 'Inter', sans-serif; background: {BG_LIGHT}; color: {TEXT_DARK}; }} a {{ color: {PRIMARY}; text-decoration: none; font-weight: 600; }} a:hover {{ color: {PRIMARY_DARK}; }} /* ─── headline ──────────────────────────────────────────── */ #titlebar {{ text-align: center; margin-top: 2.4rem; margin-bottom: .9rem; }} /* ─── card look ─────────────────────────────────────────── */ .gr-block, .gr-box, .gr-row, #cite-wrapper {{ border: 1px solid #F8C89B; border-radius: 10px; background: #fff; box-shadow: 0 3px 6px rgba(0, 0, 0, .05); }} .gr-gallery-item {{ background: #fff; }} /* ─── controls / inputs ─────────────────────────────────── */ .gr-button-primary, #copy-btn {{ background: linear-gradient(90deg, {PRIMARY} 0%, {PRIMARY_DARK} 100%); border: none; color: #fff; border-radius: 6px; font-weight: 600; transition: transform .12s ease, box-shadow .12s ease; }} .gr-button-primary:hover, #copy-btn:hover {{ transform: translateY(-2px); box-shadow: 0 4px 12px rgba(249, 115, 22, .35); }} .gr-dropdown input {{ border: 1px solid {PRIMARY}99; }} .preview img, .preview canvas {{ object-fit: contain !important; }} /* ─── hero section ─────────────────────────────────────── */ #hero-wrapper {{ text-align: center; }} #hero-badge {{ display: inline-block; padding: .85rem 1.2rem; border-radius: 8px; background: {ACCENT_LIGHT}; border: 1px solid {PRIMARY}55; font-size: .95rem; font-weight: 600; margin-bottom: .5rem; }} #hero-links {{ font-size: .95rem; font-weight: 600; margin-bottom: 1.6rem; }} #hero-links img {{ height: 22px; vertical-align: middle; margin-left: .55rem; }} /* ─── score area ───────────────────────────────────────── */ #score-area {{ text-align: center; }} .title-container {{ display: flex; align-items: center; gap: 12px; justify-content: center; margin-bottom: 10px; text-align: center; }} .match-badge {{ display: inline-block; padding: .35rem .9rem; border-radius: 9999px; font-weight: 600; font-size: 1.25rem; }} /* ─── citation card ────────────────────────────────────── */ #cite-wrapper {{ position: relative; padding: .9rem 1rem; margin-top: 2rem; }} #cite-wrapper code {{ font-family: SFMono-Regular, Consolas, monospace; font-size: .84rem; white-space: pre-wrap; color: {TEXT_DARK}; }} #copy-btn {{ position: absolute; top: .55rem; right: .6rem; padding: .18rem .7rem; font-size: .72rem; line-height: 1; }} /* ─── dark mode ────────────────────────────────────── */ .dark body, .dark .gradio-container {{ background-color: {BG_DARK}; color: #e5e7eb; }} .dark .gr-block, .dark .gr-box, .dark .gr-row {{ background-color: {BG_DARK}; border: 1px solid #4b5563; }} .dark .gr-dropdown input {{ background-color: {BG_DARK}; color: #f1f5f9; border: 1px solid {PRIMARY}aa; }} .dark #hero-badge {{ background: #334155; border: 1px solid {PRIMARY}55; color: #fefefe; }} .dark #cite-wrapper {{ background-color: {CARD_BG_DARK}; }} .dark #bibtex {{ color: {TEXT_LIGHT} !important; }} .dark .card {{ background-color: {CARD_BG_DARK}; }} /* ─── switch logo for light/dark theme ─────────────── */ .logo-dark {{ display: none; }} .dark .logo-light {{ display: none; }} .dark .logo-dark {{ display: inline; }} """ FULL_CSS = CSS + title_css(TEXT_DARK, PRIMARY, PRIMARY_DARK, TEXT_LIGHT) # ─────────────────────────────── # Torch / transforms # ─────────────────────────────── _tx = transforms.Compose( [ transforms.ToTensor(), transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]), ] ) def get_edge_model(name: str) -> torch.nn.Module: if name not in get_edge_model.cache: device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model_path = hf_hub_download( repo_id=model_configs[name]["repo"], filename=model_configs[name]["filename"], local_dir="models", ) model = TimmFRWrapperV2(model_configs[name]["timm_model"], batchnorm=False) model = model_configs[name]["post_setup"](model) model.load_state_dict(torch.load(model_path, map_location="cpu")) model = model.eval() model.to(device) get_edge_model.cache[name] = model return get_edge_model.cache[name] get_edge_model.cache = {} # ─────────────────────────────── # Helpers # ─────────────────────────────── def _as_rgb(path: Path) -> np.ndarray: return cv2.cvtColor(cv2.imread(str(path)), cv2.COLOR_BGR2RGB) def badge(text: str, colour: str) -> str: return f'
@article{edgeface,
title = {{EdgeFace: Efficient Face Recognition Model for Edge Devices}},
author = {{George, A. and Ecabert, C. and Otroshi, H. and Kotwal, K. and Marcel, S.}},
journal= {{IEEE Trans. Biometrics, Behavior, & Identity Science}},
year = {{2024}}
}