7.6 KiB
Matcher v1 et Match Store v1
Contrat
Le pipeline v1 est ORB → BFMatcher Hamming ou SIFT/RootSIFT → BFMatcher L2,
puis KNN k=2, Lowe ratio, tri canonique, Match File content-addressed et Match
Result. Il s'arrête avant toute vérification géométrique.
Pour chaque descripteur A, OpenCV fournit zéro, un ou deux voisins. Zéro ne
produit rien. Un seul voisin valide est accepté. Avec deux voisins, le premier
est accepté exactement si d1 < threshold * d2; une seconde distance nulle,
une égalité, NaN, Inf ou une distance négative est rejetée. -0.0 est accepté
comme zéro conformément à IEEE-754. Indices invalides et
distances du premier voisin non finies ou négatives sont rejetés.
Un feature_index_a produit donc au maximum une correspondance acceptée.
Sans cross-check, plusieurs indices A peuvent viser le même index B. L'ordre
final est (feature_index_a, distance, feature_index_b) croissant. BFMatcher
fournit une liste KNN par query et le filtre ne conserve que son premier voisin :
la déduplication est donc mathématiquement inutile. Le Matcher rejette toute
sortie anormale contenant deux fois le même index A.
Match File v1
Header fixe de 32 octets, tous les entiers en little-endian :
0..3 octets ASCII exacts "L3DM"
4 format_version = 1
5 descriptor_type (1=U8, 2=F32)
6..7 reserved = 0
8..11 match_count uint32
12..15 descriptor_dimension uint32 (32 ou 128)
16..23 feature_set_id_a uint64
24..31 feature_set_id_b uint64
Chaque entrée fait 12 octets : index A uint32, index B uint32, distance float32.
Le format accepte zéro entrée pour tester reader/writer, mais le Matcher ne
publie pas d'asset vide. La borne est 8192 entrées et la taille maximale exacte
est 32 + 8192 * 12 = 98336 octets, environ 96 Kio.
Le reader valide avant toute allocation : magic physique, version, reserved, type/dimension, borne du compte, calcul de taille, taille physique exacte, Feature Set IDs A/B sans permutation, indices contre les comptes attendus et distance finie non négative. Fichiers tronqués, trailing bytes et versions futures sont rejetés.
Publication et reuse
Pour NO_MATCH, le fichier temporaire est supprimé et seule une ligne sans
asset est créée. Pour MATCHED : temp dans assets/matches, écriture complète,
fsync du fichier, SHA-256, chemin assets/matches/<préfixe>/<sha256>, puis
publication atomique par link. Une race de même contenu est validée; un
contenu différent déjà présent n'est jamais écrasé lors d'une publication
fraîche. Lorsqu'une ligne existante prouve que son asset content-addressed est
corrompu, le Matcher recalcule puis remplace atomiquement cet asset par rename
et répare les métadonnées de la même ligne sous transaction. Le temp est nettoyé
et le répertoire final synchronisé.
Le reuse MATCHED exige métadonnées complètes, fichier régulier non symbolique,
taille, SHA-256, header, type/dimension, IDs A/B, compte et entrées valides. Il
n'existe aucun fallback faisant confiance au chemin ou à l'existence seuls.
La validation de reuse lit le fichier borné une seule fois et calcule le SHA-256
sur ce même buffer.
Bornes mémoire et performance
Les deux buffers de descripteurs contigus occupent au maximum 512 Kio pour ORB
ou 8 Mio pour SIFT/RootSIFT. Les résultats KNN ne contiennent que deux DMatch
par query, les matches filtrés et le Match File sont chacun bornés à environ
96 Kio. Le working set contrôlé du Matcher est donc inférieur à environ 10 Mio,
hors scratch interne borné par OpenCV; aucune structure A × B n'est
matérialisée.
Le Match File complet est sérialisé dans un buffer heap borné à 98336 octets et
écrit par un unique write_exact, puis synchronisé une fois. Les mesures locales
restent dans le rapport de session, pas dans ce contrat canonique.
À 8192 features, le coût CPU dominant reste l'évaluation exacte des distances
dans cv::BFMatcher::knnMatch. ORB peut remplacer ce seul hot path par Vulkan.
Une feasibility réelle a rejeté Vulkan pour SIFT et RootSIFT : leur accumulation
flottante ne garantit pas le top-2 OpenCV sur les égalités adversariales et le
gain n'est présent que sur les grandes paires carrées. Ils restent intégralement
sur BFMatcher CPU.
IMPLEMENTATION IN PROGRESS — parallélisme interne P4. matcher.run v1
orchestre le Matcher sans connaître son backend interne. La
tâche persiste uniquement la configuration, l'identité des Feature Sets à
sélectionner et un curseur Candidate Pair. Une paire est atomique et publiée
immédiatement. Les lots 1/2/4/8 sont séparés par checkpoint et
task_sequence_break(). La tâche utilise l'unique Resource Governor avec une
estimation couvrant ce working set ; aucune seconde logique de budget n'est
introduite. Dans un lot, des fenêtres bornées calculent au plus deux paires par
thread CPU effectivement admis, avec un plafond de huit. Le callback Queue
compte comme un participant, joint au plus cpu_threads - 1 enfants, puis
publie seul les stages en ordre candidate_pair_id. OpenCV reste à un thread
interne pendant cette séquence, ce qui interdit un fan-out BFMatcher imbriqué.
Le curseur ne suit que le préfixe contigu durable ; le premier échec scelle tout
le suffixe calculé. Sa ligne durable matcher_tasks appartient au schéma
Project DB v11 ; le Match Result reste le contrat publié en v10.
Project DB v12 ajoute un enfant Geometric Verification Result référencé par
match_result_id. Le Matcher ne calcule, ne stocke et ne valide aucun inlier
géométrique ; seul son Match File canonique définit l'ordre indexé par le masque.
Backend Vulkan ORB
La frontière évaluée remplace uniquement KNN Hamming par un compute top-2 : un
thread GPU par feature A parcourt B, conserve deux indices/distances et applique
le tie-break du plus petit index. Elle ne matérialise jamais A×B. Lowe,
canonicalisation et persistance restent communs. Sur Radeon 780M, la parité
top-2 avec OpenCV est exacte et le gain warm est supérieur à 90 % à 4096/8192.
Le backend de production conserve exactement cette frontière. Sa parité entière
permet au CPU et à Vulkan de partager l'identité persistante. La sélection est
une politique runtime explicite ; le CPU reste le fallback portable. Les APIs
historiques de création/soumission choisissent toujours le mode CPU parallèle.
Les variantes additives *_with_mode permettent de demander ORB Vulkan avant
l'admission ; elles refusent SIFT/RootSIFT, un profil sans GPU sélectionné ou
un backend absent. Ce mode immutable demande exactement CPU 1, GPU 1 et
640 Kio GPU/UMA. Un échec Vulkan pendant une exécution déjà admise reprend le
top-2 CPU exact sans changer fingerprint ni résultat. Le contrat détaillé est
décrit dans vulkan_matcher.md.
La reprise reconnaît uniquement quatre estimations complètes : CPU8/GPU0 et CPU1/GPU1/640 Kio courantes, plus CPU12/GPU0 et CPU12/GPU1/640 Kio historiques. Les deux formes CPU12 sont normalisées éphémèrement vers leur forme courante avant admission, sans checkpoint d'estimation seule. Une forme voisine est rejetée ; aucun champ isolé ne sert à deviner le backend.
Déterminisme et fingerprint
Le fingerprint couvre kind, version, k=2, threshold float32 et absence de
cross-check. À Feature Files, configuration, implémentation/version OpenCV et
environnement numérique compatibles identiques, SIFT/RootSIFT garantissent les
mêmes paires attendues, l'ordre et la sérialisation stables. Aucune promesse
bit-à-bit cross-platform n'est faite pour les distances L2 flottantes.
ORB/Hamming bénéficie d'une garantie plus forte grâce à sa distance entière.
NEXT: GEOMETRIC VERIFIER v3