You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Log in or Sign Up to review the conditions and access this model content.

MOVA Soccer Field Keypoints — HRNet-W18 v1

57 keypoints de la cancha de fútbol broadcast para calibración homográfica y mapeo pixel ↔ coordenadas reales del campo.

Entrenado por Orbital Lab como parte del stack MOVA (análisis táctico de fútbol con CV).


TL;DR

from huggingface_hub import snapshot_download
import sys, cv2

repo = snapshot_download("OrbitalLab/mova-hrnet-keypoints-v1")
sys.path.insert(0, repo)

from keypoints import load_model, preprocess, heatmaps_to_keypoints

model = load_model(f"{repo}/hrnet_w18_keypoints_v1.pth", f"{repo}/config.yaml")

frame = cv2.imread("partido.jpg")
tensor, orig_size = preprocess(frame)
heatmaps = model(tensor.to(next(model.parameters()).device))[0]
kps = heatmaps_to_keypoints(heatmaps, orig_size, conf_threshold=0.1)

for name, kp in kps.items():
    print(f"{name}: ({kp['x']:.0f}, {kp['y']:.0f}) conf={kp['confidence']:.3f}")

→ Devuelve un dict {keypoint_name: {x, y, confidence}} con los keypoints detectados sobre las coordenadas del frame original.


Modelo

Campo Valor
Arquitectura HRNet-W18 (heatmap regression head)
Backbone params ~9.6 M (W18 lightweight)
Output channels 58 (57 keypoints + 1 background)
Input resolution 960 × 540 px (BGR → RGB normalizado ImageNet)
Output heatmap stride 2 (upscale interno = 2)
Head Conv → BN → ReLU → Conv(58 ch) → LogSoftmax
Tamaño checkpoint 113 MB (.pth)
Framework PyTorch ≥ 2.0

Métricas (validación)

Validación interna sobre split propio de Liga Betplay + SoccerNet broadcast.

Métrica Valor
val_loss (KL divergence) 8.21 × 10⁻⁵
val_kp_l2_dist (px en input 960×540) 30.4
PCK @ 5 px (conf ≥ 0.1) 54.6 %
PCK @ 10 px (conf ≥ 0.1) 62.1 %
Epochs entrenados 15

PCK = porcentaje de keypoints predichos que caen dentro del umbral en pixels respecto al ground truth. Calculado solo sobre keypoints visibles + con confianza ≥ 0.1.

Lectura honesta: este es un baseline funcional. Es suficiente para calibración inicial + RANSAC, pero un detector más profundo (HRNet-W32/W48 + más data) cerraría el gap del 38 % de keypoints fuera de tolerancia a 10 px. Pull requests bienvenidos.


Mapa de keypoints (57)

Numerados 0-56 siguiendo el estándar SoccerNet / BOG. El canal 57 del modelo es background (no es un keypoint).

Arco izquierdo (0-7)

ID Nombre Descripción
0 L_GOAL_TL_POST Esquina superior izquierda del poste izquierdo del arco izquierdo
1 L_GOAL_TR_POST Esquina superior derecha del poste izquierdo
2 L_GOAL_BL_POST Esquina inferior izquierda
3 L_GOAL_BR_POST Esquina inferior derecha
4-7 L_GOAL_AREA_*_CORNER 4 esquinas del área chica izquierda

Área grande izquierda + esquinas de cancha (8-15)

ID Nombre
8-11 L_PENALTY_AREA_*_CORNER
12 BL_PITCH_CORNER (esquina inferior izquierda de la cancha)
13 TL_PITCH_CORNER
14 B_TOUCH_AND_HALFWAY_LINES_INTERSECTION
15 T_TOUCH_AND_HALFWAY_LINES_INTERSECTION

Lado derecho (16-29)

Espejo del izquierdo: área grande, área chica, postes, esquinas de cancha.

Círculo central (30-42)

ID Nombre
30-33 Tangentes del círculo central (TR, TL, BR, BL)
34-37 Cuartos del círculo central
38-39 Tangentes laterales (R, L)
40 Intersección línea de medio campo (Top) con círculo central
41 Intersección línea de medio campo (Bottom) con círculo central
42 CENTER_MARK (punto central)

Arcos de penal (43-56)

ID Nombre
43, 50 Centros de arco de penal izquierdo / derecho
44-47 Intersecciones arco de penal izquierdo con línea de 16m + tangentes
48-49 Marca de penal izquierda + punto medio del área izquierda
51-54 Intersecciones arco de penal derecho + tangentes
55-56 Marca de penal derecha + punto medio del área derecha

Para el mapa completo: ver keypoints.py (constante KEYPOINT_INDEX_TO_NAME).


Uso

Opción A — vía snapshot_download (plug-and-play)

Funciona desde cualquier proyecto sin clonar nada manualmente:

from huggingface_hub import snapshot_download
import sys, cv2

repo = snapshot_download("OrbitalLab/mova-hrnet-keypoints-v1")
sys.path.insert(0, repo)

from keypoints import load_model, preprocess, heatmaps_to_keypoints

model = load_model(f"{repo}/hrnet_w18_keypoints_v1.pth", f"{repo}/config.yaml")

frame = cv2.imread("partido.jpg")
tensor, orig_size = preprocess(frame)
with __import__("torch").no_grad():
    heatmaps = model(tensor.to(next(model.parameters()).device))[0]

kps = heatmaps_to_keypoints(heatmaps, orig_size, conf_threshold=0.1)
print(f"Detectados {len(kps)} keypoints")

Opción B — clonando el repo

git clone https://huggingface.co/OrbitalLab/mova-hrnet-keypoints-v1
cd mova-hrnet-keypoints-v1
pip install torch numpy opencv-python PyYAML huggingface_hub

python -c "
from keypoints import load_model, preprocess, heatmaps_to_keypoints
import cv2, torch
m = load_model('hrnet_w18_keypoints_v1.pth', 'config.yaml')
frame = cv2.imread('YOUR_IMAGE.jpg')
t, s = preprocess(frame)
with torch.no_grad():
    hm = m(t.to(next(m.parameters()).device))[0]
print(heatmaps_to_keypoints(hm, s))
"

Opción C — dentro del stack MOVA

Si trabajas en OrbitalLabBOG/mova-futbol, el adapter ya está integrado en mova.adapters.hrnet_adapter.HRNetAdapter. Solo apuntá weights_path al checkpoint descargado de este repo.


Homografía (uso típico downstream)

Los 57 keypoints corresponden a puntos conocidos en el sistema de coordenadas de una cancha estándar FIFA (105 × 68 m). Para obtener la matriz de homografía cámara → cancha:

import cv2
import numpy as np

# Coordenadas reales (metros) de cada keypoint en cancha FIFA estándar
# Tu propio diccionario {keypoint_name: (x_meters, y_meters)}
PITCH_TEMPLATE = {...}  # ver SoccerNet-Calibration o sn-gamestate

# Filtrar keypoints detectados que tengan correspondencia conocida
img_pts = np.array([(kps[n]["x"], kps[n]["y"]) for n in kps if n in PITCH_TEMPLATE])
world_pts = np.array([PITCH_TEMPLATE[n] for n in kps if n in PITCH_TEMPLATE])

# Mínimo 4 puntos. Recomendado: 8+ con RANSAC para robustez
H, mask = cv2.findHomography(img_pts, world_pts, cv2.RANSAC, ransacReprojThreshold=5.0)

Para los PITCH_TEMPLATE (coordenadas en metros), referir a SoccerNet-Calibration o al módulo mova.core.homography del repo MOVA.


Dependencias mínimas

torch>=2.0
numpy>=1.24
opencv-python>=4.8
PyYAML>=6.0
huggingface_hub>=0.24

CUDA opcional pero recomendada (CPU ≈ 1-2 s/frame; RTX 3060 ≈ 30 ms/frame).


Limitaciones conocidas

  • Cobertura desigual: los keypoints del círculo central y tangentes tienen mayor error que los de las áreas (más texturadas en broadcast)
  • Cámaras tácticas / vista cenital: el modelo fue entrenado con broadcast TV (cámara lateral elevada). En vistas cenitales o cámaras tácticas el rendimiento cae fuerte
  • Cuasi-confluencia visual: en planos cerrados donde solo se ve una porción de la cancha, muchos keypoints quedan fuera de frame y la confianza es baja — usar RANSAC con ransacReprojThreshold generoso
  • Iluminación pobre / partidos nocturnos sin TV broadcast: menor precisión

Citación

Si usas este modelo en un trabajo académico o derivado, por favor cita:

@misc{orbitallab-mova-hrnet-keypoints-2026,
  author       = {Orbital Lab},
  title        = {MOVA Soccer Field Keypoints — HRNet-W18 v1},
  year         = {2026},
  publisher    = {HuggingFace},
  howpublished = {\url{https://huggingface.co/OrbitalLab/mova-hrnet-keypoints-v1}}
}

Licencia

Apache 2.0 — uso libre comercial y académico. La arquitectura HRNet original es propiedad de Microsoft Research Asia (MIT). Esta implementación es una reescritura propia.


Repos relacionados


Maintainer: Orbital Lab · orbitallabai@gmail.com · orbitallab.ai Última actualización: 2026-05-14

Downloads last month
4
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support