Normales de surface

L'estimation des normales de surface prédit la direction vers laquelle fait face chaque surface visible. LibreYOLO l'expose comme la tâche normal, qui renvoie un champ dense de vecteurs unitaires sur le canevas de l'image d'origine.

Définition

La tâche normal prédit depuis une image RVB un vecteur unitaire à trois composantes par pixel, qui indique la direction vers laquelle fait face la surface de ce pixel. Contrairement à la profondeur, la sortie n'a pas d'échelle libre. Deux prédictions sont donc directement comparables sans alignement.

Une prédiction remplit result.normal_map, une charge utile NormalMap qui contient un tableau float32 (H, W, 3) sur le canevas de l'image d'origine, également accessible sous result.normals. Les vecteurs emploient le repère caméra OpenCV de LibreYOLO, avec +x vers la droite, +y vers le bas et +z dans la scène. Ils font face à la caméra. Une surface fronto-parallèle vaut donc (0, 0, -1). .assert_normalized() vérifie que chaque pixel est fini et de longueur unitaire dans une certaine tolérance. result.boxes reste vide. Les arguments conf, iou et max_det n'ont donc aucun effet, et Results.plot() couvre cette tâche.

Modèles

Deux familles couvrent la tâche normal.

MoGe-2 est la famille dédiée. Ce modèle de géométrie monoculaire à propagation unique existe en trois tailles d'encodeur. LibreYOLO ne copie pas ces checkpoints dans sa propre organisation. Le chargement télécharge la taille correspondante depuis les dépôts officiels à une révision fixée, puis la vérifie avec une empreinte SHA-256 enregistrée.

LibreMODUS produit des normales comme l'une des cibles d'un modèle universel et peut recevoir une carte de profondeur plutôt qu'une image RVB en entrée. Il nécessite l'extra modus et votre propre compte Hugging Face authentifié. Il ne propose ni val() ni export(), et ne participe donc pas aux sections de validation et d'exportation ci-dessous.

Prédire

Les poids MoGe-2 sont téléchargés à la première utilisation et mis en cache localement.

Prédire un champ de normales
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreMoGe2s-normal.pt")result = model(SAMPLE_IMAGE, save=True) normals = result.normal_mapprint(normals.data.shape)      # vecteurs unitaires float32 (H, W, 3)normals.assert_normalized()    # déclenche une erreur si un pixel n'est pas unitaire
Lire un pixel
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreMoGe2s-normal.pt")result = model(SAMPLE_IMAGE) # Repère caméra OpenCV : +x à droite, +y vers le bas, +z dans la scène.# Une surface face à la caméra est proche de (0, 0, -1).field = result.normals.datah, w = field.shape[:2]print(field[h // 2, w // 2])
Enregistrer la visualisation
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreMoGe2s-normal.pt")result = model(SAMPLE_IMAGE) # plot() rend le champ ; cette méthode est définie pour les normales et les contours.result.plot().save("normals.png")

imgsz doit être divisible par la taille de patch de l'encodeur ViT, ce que LibreYOLO vérifie avant le début de l'exécution. La prédiction sur une liste d'images effectue une propagation par image. Cette tâche ne possède aucun parcours rapide par lots empilés. Consultez la page prédiction pour les sources, le streaming et la gestion des résultats.

Format du dataset

Pour la validation des normales, chaque image est associée à un PNG 16 bits à trois canaux, de même nom de base et de même résolution, ainsi qu'à un masque de validité facultatif.

dataset/
  data.yaml
  images/
    val/room.jpg
  normals/
    val/room.png
  masks/
    val/room.png
yaml
path: dataset
train: images/train
val: images/val
normals_dir: normals
masks_dir: masks
nc: 1
names: {0: normal}

Le PNG cible doit comporter exactement trois canaux uint16 enregistrés en RVB. Le décodage suit n = png / 65535 * 2 - 1, puis renormalise chaque vecteur. Les vecteurs décodés emploient le même repère caméra OpenCV que les prédictions. Un pixel de masque est valide s'il n'est pas nul. Sans fichier de masque, tout vecteur décodé fini et non nul est valide. Les pixels cibles non valides et ceux ajoutés pour le remplissage sont conservés en interne sous la forme (0, 0, 0) et ne contribuent jamais aux métriques. Consultez les formats de datasets pour le contrat complet.

Entraîner

Aucune des deux familles de normales ne possède d'implémentation d'entraînement. train() déclenche une NotImplementedError dans les deux cas. La page de MoGe-2 renvoie vers ses checkpoints officiels fixés pour la prédiction, la validation et l'exportation.

Valider

val() mesure l'angle entre chaque vecteur prédit et son vecteur de vérité terrain, sur les pixels que le dataset marque comme valides.

Valider et lire les clés des métriques
from libreyolo import LibreYOLO model = LibreYOLO("LibreMoGe2s-normal.pt")metrics = model.val(data="my-dataset.yaml", imgsz=518) print(metrics["metrics/mean_angular_error"])     # degrésprint(metrics["metrics/median_angular_error"])   # degrésprint(metrics["metrics/within_11_25"])           # pourcentage de pixelsprint(metrics["metrics/within_22_5"], metrics["metrics/within_30"])

metrics/mean_angular_error et metrics/median_angular_error représentent cet angle en degrés. Les valeurs faibles sont préférables. metrics/within_11_25, metrics/within_22_5 et metrics/within_30 sont les pourcentages de pixels valides dont l'erreur angulaire se situe respectivement dans les limites de 11,25, 22,5 et 30 degrés. Les valeurs élevées sont donc préférables. Notez l'unité : ces trois valeurs sont des pourcentages et non des fractions. fitness correspond à metrics/within_11_25 divisé par 100, ce qui place la sélection du meilleur checkpoint sur la même échelle [0, 1] que toutes les autres tâches.

Exporter

Un modèle de normales exporté se recharge par LibreYOLO() grâce au suffixe de son fichier. Un fichier .onnx se comporte donc comme un checkpoint et renvoie le même Results.

Exporter
from libreyolo import LibreYOLO model = LibreYOLO("LibreMoGe2s-normal.pt")model.export(format="onnx", imgsz=518)
Exécuter le fichier exporté
from libreyolo import LibreYOLO, SAMPLE_IMAGE # La fabrique s'oriente grâce au suffixe du fichier. Un artefact exporté# se charge donc comme tout checkpoint et renvoie le même objet Results.model = LibreYOLO("LibreMoGe2s-normal.onnx")result = model(SAMPLE_IMAGE) print(result.normal_map.data.shape)

L'exportation des normales emploie un contrat d'exécution à résolution fixe et à lot de taille 1. dynamic et toute valeur de batch différente de 1 sont refusés, et imgsz doit être divisible par la taille de patch de l'encodeur. La couverture de chaque format figure sur la page de MoGe-2 et dans la matrice d'exportation complète. La page Exporter présente les arguments acceptés par chaque format.

Vérifié avec LibreYOLO v1.5.0.