Epipolar Geometry
La géométrie épipolaire relie deux vues d’une scène en limitant la correspondance de chaque point à une ligne dans l’autre image ; elle est à la base de l’estimation stéréo de profondeur et de la cartographie 3D.
En vision par ordinateur, la géométrie épipolaire décrit la relation géométrique entre deux vues d’une même scène prises par des caméras. Un pixel d’une image correspond à un rayon de points 3D possibles, et ce rayon se projette dans la seconde image sous la forme d’une droite, la droite épipolaire, sur laquelle doit se trouver le pixel correspondant. La contrainte dépend uniquement des paramètres internes des caméras et de leur pose relative, indépendamment de la structure de la scène ; elle transforme ainsi la recherche d’une correspondance, qui portait sur toute l’image, en une recherche le long d’une seule droite. La géométrie épipolaire est donc à la base de la vision stéréoscopique, de la structure à partir du mouvement et du SLAM visuel qui guide les robots et les casques. L’ouvrage de référence est Multiple View Geometry in Computer Vision, de Richard Hartley et Andrew Zisserman.
Épipôles, droites épipolaires et plan épipolaire#
Les deux centres des caméras et tout point de la scène définissent un plan, le plan épipolaire. La droite qui relie les centres des caméras est la ligne de base ; elle perce chaque plan image au niveau de l’épipôle, image du centre de l’autre caméra, qui peut se trouver hors du cadre ou à l’infini. Chaque plan épipolaire coupe chaque image selon une droite épipolaire, également appelée épiligne, de sorte que toutes les droites épipolaires d’une image passent par son épipôle. Lorsque le point de la scène glisse le long du rayon de visée de la première caméra, sa projection dans la seconde image glisse le long de la droite épipolaire.
Matrices fondamentale et essentielle#
La matrice fondamentale F, une matrice 3 × 3 de rang deux, décrit l’ensemble de la relation en coordonnées de pixels. Pour des points correspondants x et x′ écrits sous forme homogène (x, y, 1), la contrainte épipolaire est x′ᵀ F x = 0, et le produit F x donne les coefficients de la droite épipolaire de x dans la seconde image. Lorsque les caméras sont calibrées par étalonnage de caméra, la matrice essentielle E = K′ᵀ F K exprime la même contrainte en coordonnées normalisées et se décompose en rotation relative R et translation t sous la forme E = [t]ₓ R. Christopher Longuet-Higgins a introduit la matrice essentielle et l’algorithme à huit points en 1981 ; Olivier Faugeras, Quang-Tuan Luong et Richard Hartley ont élaboré la matrice fondamentale non calibrée au début des années 1990. Comme E ne fixe que la direction de t, deux vues seules donnent la forme de la scène à une échelle inconnue près, et une ligne de base ou une taille d’objet connue permet de fixer l’échelle.
Les deux matrices sont estimées à partir de points clés appariés, par exemple des caractéristiques SIFT, à l’aide d’un estimateur robuste comme RANSAC, qui ajuste plusieurs fois la matrice à un échantillon minimal de correspondances et conserve le modèle sur lequel s’accorde le plus grand nombre de correspondances. Cet exemple construit deux vues de 100 points, remplace 20 correspondances par de fausses correspondances aléatoires et estime F avec la variante MAGSAC de RANSAC d’OpenCV :
import cv2
import numpy as np
rng = np.random.default_rng(0)
K = np.array([[800.0, 0, 320], [0, 800.0, 240], [0, 0, 1]]) # intrinsics shared by both cameras
# 100 scene points seen by two cameras; the second sits 0.5 m to the right, turned 5 degrees
points = rng.uniform([-2, -1, 4], [2, 1, 8], (100, 3))
rvec = np.array([0.0, np.radians(5), 0.0])
tvec = -cv2.Rodrigues(rvec)[0] @ np.array([0.5, 0.0, 0.0])
x1 = cv2.projectPoints(points, np.zeros(3), np.zeros(3), K, None)[0].reshape(-1, 2) + rng.normal(0, 0.5, (100, 2))
x2 = cv2.projectPoints(points, rvec, tvec, K, None)[0].reshape(-1, 2) + rng.normal(0, 0.5, (100, 2))
x2[:20] = rng.uniform([0, 0], [640, 480], (20, 2)) # replace 20 matches with wrong ones
# Robust estimation keeps the matches that agree with a single fundamental matrix
F, inliers = cv2.findFundamentalMat(x1, x2, cv2.USAC_MAGSAC, 1.0)
kept = inliers.ravel() == 1
print(f"Kept {kept[20:].sum()} of 80 true matches and {kept[:20].sum()} of 20 wrong ones")
# Each point in image 1 defines a line a*x + b*y + c = 0 in image 2 that its match must lie on
lines = cv2.computeCorrespondEpilines(x1, 1, F).reshape(-1, 3)
dist = np.abs((lines[:, :2] * x2).sum(axis=1) + lines[:, 2])
print(f"Median distance to epipolar line: {np.median(dist[20:]):.2f} px (true), {np.median(dist[:20]):.0f} px (wrong)")
print(f"Wrong match that survived: {dist[:20][kept[:20]].round(2)} px from its line")Il conserve 75 des 80 vraies correspondances et rejette 19 des 20 fausses. Les vraies correspondances se trouvent à une distance médiane de 0,43 pixel de leurs droites épipolaires, ce qui s’explique par le bruit d’un demi-pixel ; les fausses, à 144 pixels. La seule fausse correspondance qui subsiste se trouve par hasard à 0,61 pixel de sa droite, car la contrainte limite une correspondance à une droite et seule l’apparence permet de choisir le bon point le long de celle-ci.
Une rotation pure ne comporte pas de ligne de base, et un seul plan est compatible avec de nombreuses matrices fondamentales différentes ; dans ces deux cas, on utilise donc une homographie, la transformation 3 × 3 entre les vues d’un plan. ORB-SLAM initialise sa carte en ajustant en parallèle une homographie et une matrice fondamentale, puis conserve celle qui explique le mieux les correspondances.
Rectification, disparité et profondeur#
La rectification déforme une paire stéréo afin que ses droites épipolaires soient horizontales et alignées, plaçant chaque correspondance sur la même ligne de l’image. Les algorithmes d’appariement stéréo parcourent ensuite chaque ligne ; le décalage entre les colonnes des correspondances, appelé disparité d, donne la profondeur Z = f × B / d, où f est la distance focale et B la ligne de base. C’est ainsi que les systèmes stéréo produisent des cartes d’estimation de profondeur. Le benchmark stéréo KITTI évalue ces algorithmes sur des scènes rectifiées issues de la conduite autonome.
Applications au-delà de la stéréo#
En reconstruction 3D, COLMAP vérifie chaque paire d’images en ajustant des matrices fondamentales, essentielles ou d’homographie, puis écarte les paires et les correspondances qui ne bénéficient pas d’un appui géométrique suffisant. Les pipelines d’appariement d’images utilisent la même vérification pour rejeter les correspondances erronées, et l’enregistrement d’images utilise la méthode par homographie décrite plus haut lorsque la scène est plane ou que la caméra ne fait que tourner, comme dans l’assemblage de panoramas. Entre deux images prises par une caméra en mouvement, la contrainte épipolaire ne s’applique qu’aux surfaces statiques ; les vecteurs de flux optique qui la violent révèlent donc les objets qui se déplacent indépendamment. Lors de la capture multi-caméras de la pose humaine, on associe une même personne entre les vues en mesurant à quel point ses articulations se trouvent près des droites épipolaires correspondantes, avant de trianguler un squelette 3D. Un modèle d’estimation de pose Ultralytics YOLO26 fournit 17 points clés corporels COCO par personne et par vue.










