lardon3d/docs/architecture/feature_store.md

161 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Feature Store v1/v2
## Rôle et modèle
**IMPLEMENTED.** Le Feature Store est la mémoire visuelle locale persistante.
Un `FeatureSet` logique appartient à une `image_id` et identifie exactement
`orb` version 1 plus une configuration canonique. Il référence un
`FeatureAsset` physique immutable. Deux images logiques portant le même contenu
et la même configuration gardent deux `feature_set_id`, mais partagent le même
asset lorsque leurs octets sont identiques. Les futurs matches référenceront
`feature_set_id + feature_index`; ces deux valeurs sont immuables après
publication.
`feature_set_id` et `feature_asset_id` sont des identités SQLite
`AUTOINCREMENT`. Une identité validée n'est jamais réattribuée. SQLite ne
contient que les relations, empreintes, tailles et paramètres ; les tableaux de
points et descripteurs restent hors DB.
## Extracteur et configuration
L'extracteur production est ORB d'OpenCV 5, derrière une façade C. Aucune
exception C++ ni `cv::Mat` ne traverse cette frontière. La registry statique
expose `features.extract`, version 1. Une tâche traite exactement une image.
La configuration v1 contient trois `uint32` : `max_features` (1..8192),
`pyramid_levels` (1..16) et `fast_threshold` (1..255). Son fingerprint SHA-256
porte sur les 24 octets canoniques `L3DORBP1`, version et trois entiers
little-endian. Il ne dépend ni du padding, ni de la locale. Le contrat ORB v1
produit 32 octets binaires par point.
Au démarrage, avant la création des workers, Lardon3D configure la limite de
threads interne process-wide d'OpenCV à
`min(12, logical_cpu_count - system_cpu_reserve)`. La borne douze est
opérationnelle et correspond au plafond audité. `features.extract` demande
jusqu'à douze threads au Resource Governor ; celui-ci réduit l'admission au
budget hôte, donc à la limite OpenCV configurée. La Queue conserve un callback actif ; aucun pool Lardon3D
supplémentaire n'est créé pour ORB.
Cette limite est une configuration opérationnelle, jamais un paramètre
scientifique : elle n'entre ni dans le fingerprint ORB v1, ni dans l'identité
FeatureSet, ni dans le Feature File. L'audit contrôlé OpenCV 5.0.0 aux limites
1, 2, 4, 8 et 12 exige le même count, le même ordre et les mêmes valeurs
binary32 des six champs keypoint persistés, les mêmes lignes/octets descriptor
et le même SHA-256 du Feature File. `class_id` est une donnée interne
`cv::KeyPoint` non publiée ; elle ne fait pas partie du record Feature File v1/v2
gelé et ne peut donc définir une identité scientifique.
La limite de 100 000 000 pixels est vérifiée après `cv::imread` : l'API utilisée
ne fournit pas de sonde de dimensions multi-format fiable sans décodage. Le pic
mémoire du décodage peut donc précéder le rejet. Lardon3D ne revendique pas de
bornage pré-décodage et n'embarque pas un parseur JPEG/PNG parallèle.
## Feature File v1
Le fichier est little-endian et exige IEEE-754 binary32. Sa taille maximale est
16 Mio et son nombre maximal de points 8192.
| Offset | Taille | Champ |
|---:|---:|---|
| 0 | 8 | magic `L3DFEAT\0` |
| 8 | 4 | format version = 1 |
| 12 | 4 | header size = 160 |
| 16 | 4 | feature count |
| 20 | 4 | descriptor dimension = 32 |
| 24 | 4 | descriptor type = U8 |
| 28 | 4 | keypoint record size = 24 |
| 32,36 | 4+4 | largeur, hauteur décodées |
| 40,48,56 | 8+8+8 | offsets keypoints/descriptors, taille totale |
| 64 | 32 | SHA-256 de l'asset image source |
| 96 | 32 | fingerprint paramètres |
| 128 | 16 | `orb\0` puis zéros réservés |
| 144 | 4 | extractor version = 1 |
| 148 | 12 | réservés, zéro obligatoire |
Chaque keypoint contient six mots de 32 bits : `x`, `y`, `size`, orientation,
response en binary32, puis octave signé. `x/y` sont en pixels de l'image telle
que décodée par OpenCV, origine en haut à gauche. `size` est le diamètre du
voisinage en pixels. L'orientation est en degrés dans `[0,360)`. L'entrée `i`
du bloc keypoints correspond exactement aux 32 octets du descriptor `i`.
Le validateur contrôle magic, version, réservés, type, dimension, bornes,
offsets, taille exacte, multiplications, SHA-256 externe et cohérence DB. Une
version future est distinguée d'une corruption lorsque le fichier et son record
DB sont cohérents. Le reader utilise `pread`, accepte au plus 256 éléments par
appel et ne charge jamais le fichier entier.
Le chemin DB est validé sous sa forme canonique exacte dérivée du SHA-256 avant
toute ouverture. Une absence retourne `NOT_FOUND`; troncature, hash divergent,
header ou métadonnées DB divergents retournent `CORRUPT`. Une version future
retourne distinctement `UNSUPPORTED_VERSION`.
## Publication et persistance
Layout : `assets/features/<2 hex>/<sha256 complet lowercase>`. Le SHA-256 porte
sur le Feature File complet. Le protocole est : temporaire local, écriture,
`fsync`, hash, `link` atomique sans écrasement, validation complète lors d'une
adoption concurrente, `fsync` du répertoire, puis transaction SQLite. Un échec
du dernier `fsync` est enregistré `PUBLISHED_NOT_DURABLE`. Un échec SQLite après
publication laisse un fichier orphelin et aucune ligne logique partielle.
Un nouvel essai qui revalide l'asset et réussit le `fsync` promeut explicitement
sa durabilité vers `DURABLE` dans la transaction DB.
Le sous-schéma introduit en v5 sépare `feature_assets`, `feature_sets` et
`feature_extract_tasks`. L'unicité logique porte sur image, kind, version et
fingerprint. Une tâche est persistée avant enqueue, passe par la queue et le
Governor (CPU, IO, 576 Mio conservateurs, lot 1), puis checkpointée initialement
et terminalement. La reprise recommence l'image entière : il n'existe pas de
fausse reprise intra-ORB. La publication est idempotente.
Pause et annulation sont coopératives avant/après l'appel ORB ; cet appel n'est
pas interruptible. L'image gérée est rehashée avant extraction. Une image
uniforme produit légitimement un Feature Set READY vide.
Lardon3D ne garantit pas des octets ORB identiques entre versions d'OpenCV,
plateformes ou backends. L'idempotence porte sur l'environnement courant et le
contrat `extractor_version`; une évolution qui change durablement la sémantique
ou les octets exige d'auditer et, si nécessaire, d'incrémenter cette version.
## Statut
**IMPLEMENTED** — ORB réel, formats v1/v2, assets content-addressed, DB v7,
publication atomique, reader borné, task kind production et reprise automatique.
**NOT_YET_WIRED** — commande de réconciliation/scrub des orphelins, contrôle fin
du backend parallèle OpenCV, orientation EXIF, lancement automatique de
l'extraction après import et planification multi-image/DAG.
**IMPLEMENTED** — Visual Index v1 consomme ce reader sans changer le format.
**IMPLEMENTED** — paires candidates, Matcher v1 et Match Store v1 consomment
les Feature Sets persistés.
**IMPLEMENTED** — vérification géométrique et tracks. **Sparse SfM Gate A
PASS**, the Sparse SfM v16 persistence model is **FROZEN** after Gate B;
Gate C geometry and the synchronous in-memory Gate D incremental core are
**IMPLEMENTED / PASS**. Final per-component Gate E BA is **PASS / FROZEN**;
Gate F project/task orchestration is **PASS / FROZEN**. Gate G is
**PASS / FROZEN**.
## Extension v2 multi-descriptor
**IMPLEMENTED v1A.** ORB continue d'écrire exactement le v1 U8×32 historique.
SIFT et RootSIFT écrivent le v2 F32×128. Le header v2 de 176 octets contient,
aux offsets 0..64, magic, version, taille header, count, dimension, type, taille
scalaire, taille record, dimensions image, capabilities, offsets des blocs et
taille totale ; puis SHA source à 72, fingerprint à 104, kind sur 16 octets à
136, version extracteur à 152 et vingt octets réservés nuls.
Les valeurs durables sont `U8=1` et `F32=2`, de tailles 1 et 4. Chaque binary32
est encodé little-endian sans dump de struct. Writer et reader rejettent NaN et
Inf. Les lectures `descriptors_u8` et `descriptors_f32` refusent un type
incompatible, utilisent `pread` et restent limitées à 256 features. La limite
v1 reste 16 Mio ; la limite v2 est 64 Mio.
La façade d'extraction transporte aussi `descriptor_bytes`. La publication
exige exactement `count × dimension × scalar_size`; elle refuse donc un buffer
tronqué ou surdimensionné avant toute lecture ou création de fichier.
ProjectDb v7 ajoute les métriques légères de couverture. Le détail multipasse,
la grille et RootSIFT sont canoniques dans
`precision_feature_pipeline.md`.