docs: reconcile current reconstruction architecture
This commit is contained in:
parent
7574362ba9
commit
a3a005b38e
6 changed files with 2407 additions and 2723 deletions
|
|
@ -1,180 +1,408 @@
|
|||
# Geometric Verification
|
||||
# Geometric Verification Model
|
||||
|
||||
## Scope
|
||||
|
||||
Geometric Verification Model est le contrat persistant placé après le Matcher.
|
||||
Sa représentation stocke les identités scientifiques Geometric Verifier v1/v2
|
||||
historiques et v3 courantes, sans changement de schéma : `verifier_version` et
|
||||
`parameter_fingerprint` appartiennent déjà à l'identité exacte. Il stocke un
|
||||
résultat terminé, compact et immutable. Il n'est ni un moteur de calcul ni une
|
||||
tâche.
|
||||
Aucun RANSAC, USAC, MAGSAC, calcul d'inliers ou backend géométrique n'appartient à ce ticket.
|
||||
|
||||
## Position in reconstruction pipeline
|
||||
|
||||
La chaîne d'ownership est :
|
||||
## Status
|
||||
|
||||
```text
|
||||
Feature Set → Candidate Pair → Match Result → Geometric Verification Result
|
||||
GEOMETRIC_VERIFICATION_MODEL=IMPLEMENTED
|
||||
PROJECT_DB_GEOMETRIC_VERIFICATION=v12
|
||||
|
||||
HISTORICAL_VERIFIER_V1=VALID
|
||||
HISTORICAL_VERIFIER_V2=VALID
|
||||
CURRENT_PRODUCTION_VERIFIER_V3=FROZEN
|
||||
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
REAL_S21_GV_V3=PASS/FROZEN
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
Le masque indexe exclusivement l'ordre des entrées du Match File canonique du Match Result. Il
|
||||
n'indexe directement ni les features, ni la Candidate Pair, ni un ordre temporaire de backend.
|
||||
This document owns the **persistent Geometric Verification Result model**.
|
||||
|
||||
## Scientific ownership
|
||||
It does not own the numerical estimator implementation. The current executable scientific verifier is
|
||||
documented in `geometric_verifier.md`.
|
||||
|
||||
Le parent scientifique est `match_result_id`. L'API accepte uniquement un Match Result existant,
|
||||
`MATCHED`, avec `match_count` strictement positif. `NO_MATCH` et les erreurs runtime ne peuvent pas
|
||||
produire de résultat géométrique.
|
||||
The persistence model was deliberately version-ready from Project DB v12: `verifier_version` and
|
||||
`parameter_fingerprint` already belong to exact result identity. Therefore historical verifier v1/v2
|
||||
and current v3 results coexist without a schema reinterpretation.
|
||||
|
||||
## Parent Match Result
|
||||
## Pipeline position
|
||||
|
||||
Le Match Store reste propriétaire de la validation du Match File. La création consulte le parent
|
||||
et son `match_count` en DB ; elle ne relit pas l'asset. Un load valide aussi l'existence et l'état du
|
||||
parent afin qu'une ligne corrompue ne soit jamais rendue comme résultat valide.
|
||||
```text
|
||||
Feature Set
|
||||
-> Candidate Pair
|
||||
-> Match Result
|
||||
-> Geometric Verification Result
|
||||
-> Track Builder
|
||||
-> Track Model
|
||||
```
|
||||
|
||||
The inlier mask indexes the canonical Match File entry order.
|
||||
|
||||
It does not directly index:
|
||||
|
||||
- Feature Store physical order;
|
||||
- Candidate Pair order;
|
||||
- temporary backend order.
|
||||
|
||||
## Scientific parent
|
||||
|
||||
The exact parent is:
|
||||
|
||||
```text
|
||||
match_result_id
|
||||
```
|
||||
|
||||
A Geometric Verification Result may be created only for a valid `MATCHED` parent with positive
|
||||
`match_count`.
|
||||
|
||||
`NO_MATCH` and runtime failures do not produce a scientific geometric result.
|
||||
|
||||
## Persistent identity
|
||||
|
||||
L'identité demandée et unique est :
|
||||
Exact identity:
|
||||
|
||||
```text
|
||||
(match_result_id, verifier_kind, verifier_version, parameter_fingerprint)
|
||||
(
|
||||
match_result_id,
|
||||
verifier_kind,
|
||||
verifier_version,
|
||||
parameter_fingerprint
|
||||
)
|
||||
```
|
||||
|
||||
Le fingerprint est le SHA-256 opaque de 32 octets déjà standard dans le projet. Il représentera
|
||||
un encodage de paramètres versionné, stable, à ordre de champs explicite et, pour les nombres
|
||||
binaires, little-endian. Aucun timestamp, résultat, PID, durée ou identifiant matériel n'y entre.
|
||||
No selection by timestamp or "latest" is permitted.
|
||||
|
||||
The fingerprint is an opaque canonical SHA-256 scientific parameter identity.
|
||||
|
||||
It excludes:
|
||||
|
||||
- Task ID;
|
||||
- PID;
|
||||
- elapsed time;
|
||||
- CPU count;
|
||||
- batch size;
|
||||
- GPU identity;
|
||||
- hardware identity.
|
||||
|
||||
## Verifier kind
|
||||
|
||||
Le modèle supporte uniquement `FUNDAMENTAL`, valeur persistante stable 1. Aucun comportement fictif
|
||||
`ESSENTIAL` ou `HOMOGRAPHY` n'est réservé dans l'API publique.
|
||||
The persistent supported model kind is:
|
||||
|
||||
## Persistent states
|
||||
```text
|
||||
FUNDAMENTAL = 1
|
||||
```
|
||||
|
||||
- `GEOMETRIC_REJECTED=1` : calcul scientifique terminé, critère non satisfait ;
|
||||
- `GEOMETRIC_VERIFIED=2` : calcul scientifique terminé, critère satisfait.
|
||||
Do not reserve fictitious `ESSENTIAL` or `HOMOGRAPHY` values in prose without an explicit versioned
|
||||
implementation decision.
|
||||
|
||||
`FAILED`, `RUNNING`, `PAUSED` et `CANCELLED` appartiennent au Task Runtime. REJECTED peut conserver
|
||||
un nombre d'inliers non nul.
|
||||
## Scientific states
|
||||
|
||||
## Model representation
|
||||
Completed scientific states are:
|
||||
|
||||
FUNDAMENTAL utilise neuf colonnes SQLite `REAL`, en ordre ligne-major `m00` à `m22`. SQLite
|
||||
convertit les valeurs numériques en binary64 sans exposer une ABI C. VERIFIED exige les neuf
|
||||
valeurs présentes et finies. REJECTED exige les neuf valeurs NULL. Le modèle n'impose ni rang 2,
|
||||
ni déterminant, ni normalisation ou échelle canonique ; ces règles relèvent du futur verifier.
|
||||
```text
|
||||
GEOMETRIC_REJECTED = 1
|
||||
GEOMETRIC_VERIFIED = 2
|
||||
```
|
||||
|
||||
## Inlier representation
|
||||
Runtime states such as RUNNING, FAILED, PAUSED or CANCELLED belong to Task Runtime, not this model.
|
||||
|
||||
Le masque est un BLOB SQLite obligatoire de taille exacte `ceil(match_count / 8)`. Pour l'entrée
|
||||
`i`, `byte_index=i/8`, `bit_index=i%8` et le masque vaut `1u << bit_index`. Le bit 0 est donc le bit
|
||||
de poids faible de l'octet 0. Cette convention est indépendante de l'endianness CPU et de l'ABI.
|
||||
Les bits de padding du dernier octet valent zéro et le popcount est exactement `inlier_count`.
|
||||
A rejected result may still contain non-zero inlier support.
|
||||
|
||||
Le masque existe pour REJECTED comme pour VERIFIED. Avec 8192 matches, il mesure au maximum
|
||||
1024 octets. Un BLOB SQLite évite les milliers de lignes secondaires et la publication, le hash,
|
||||
le nettoyage et la récupération d'un asset externe d'environ 1 Kio. Une liste `uint32_t` serait
|
||||
jusqu'à 32 fois plus grande au cas dense et aurait un encodage supplémentaire à versionner.
|
||||
## Fundamental matrix representation
|
||||
|
||||
## Invariants
|
||||
A verified Fundamental result contains nine SQLite `REAL` values:
|
||||
|
||||
- `0 <= inlier_count <= parent.match_count <= 8192` ;
|
||||
- longueur, padding et popcount du masque sont canoniques ;
|
||||
- REJECTED possède un masque cohérent et aucun modèle ;
|
||||
- VERIFIED possède un masque cohérent et exactement neuf valeurs finies ;
|
||||
- kind, version et fingerprint ont une sérialisation stable ;
|
||||
- une ligne publiée est complète et immutable.
|
||||
```text
|
||||
m00 ... m22
|
||||
```
|
||||
|
||||
Exemple : pour 100 matches, une identité FUNDAMENTAL v1, v2 ou v3/fingerprint
|
||||
X peut publier REJECTED avec 23 inliers, un masque de 13 octets et aucun modèle.
|
||||
Une autre identité peut publier VERIFIED avec 67 inliers, le même format de
|
||||
masque et une matrice 3×3 finie.
|
||||
in row-major order.
|
||||
|
||||
## Persistence semantics
|
||||
The persistent representation is binary64 through SQLite numeric semantics, not a C ABI struct dump.
|
||||
|
||||
Une création valide puis insère identité, état, masque et modèle dans une transaction courte. Le
|
||||
calcul futur se fera entièrement avant cette transaction. SQLite fournit l'atomicité ; aucun asset
|
||||
ou journal secondaire n'est créé.
|
||||
A verified row requires nine finite values.
|
||||
|
||||
A rejected row contains no model.
|
||||
|
||||
Rank/canonicalization/scientific-estimator rules belong to the versioned verifier contract.
|
||||
|
||||
## Inlier mask
|
||||
|
||||
The mask is a required SQLite BLOB of exact size:
|
||||
|
||||
```text
|
||||
ceil(match_count / 8)
|
||||
```
|
||||
|
||||
Bit convention for Match File entry `i`:
|
||||
|
||||
```text
|
||||
byte = i / 8
|
||||
bit = i % 8
|
||||
mask[byte] & (1u << bit)
|
||||
```
|
||||
|
||||
The mask is LSB-first inside each byte.
|
||||
|
||||
Padding bits in the final byte are zero.
|
||||
|
||||
The mask popcount must equal `inlier_count`.
|
||||
|
||||
The mask exists for both verified and rejected scientific results.
|
||||
|
||||
With the current Match File bound of 8192 matches, the mask is at most 1024 bytes.
|
||||
|
||||
## Persistent invariants
|
||||
|
||||
For every row:
|
||||
|
||||
```text
|
||||
0 <= inlier_count <= parent.match_count <= 8192
|
||||
mask size is canonical
|
||||
padding bits are zero
|
||||
mask popcount == inlier_count
|
||||
```
|
||||
|
||||
Additionally:
|
||||
|
||||
```text
|
||||
REJECTED -> no Fundamental matrix
|
||||
VERIFIED -> exactly nine finite matrix coefficients
|
||||
```
|
||||
|
||||
A published row is immutable.
|
||||
|
||||
## Publication
|
||||
|
||||
Numerical estimation completes before the short Project DB publication transaction.
|
||||
|
||||
Publication inserts:
|
||||
|
||||
- exact parent;
|
||||
- exact verifier identity;
|
||||
- completed state;
|
||||
- canonical mask;
|
||||
- optional verified Fundamental model.
|
||||
|
||||
No external asset is required because the bounded mask/model fit naturally in SQLite.
|
||||
|
||||
Rollback leaves no partial scientific result.
|
||||
|
||||
## Reuse
|
||||
|
||||
Le reuse cherche uniquement l'identité exacte, jamais le résultat le plus récent. Une identité
|
||||
existante retourne une erreur de contrainte à `create`; le runtime fera `find`, validera puis
|
||||
réutilisera. `INSERT OR REPLACE` est interdit, même si le nouveau contenu semble identique.
|
||||
Exact reuse uses only the full persistent identity.
|
||||
|
||||
## Invalidations
|
||||
Existing exact result:
|
||||
|
||||
Un nouveau Match Result possède un nouvel ID et ne réutilise donc aucun ancien résultat
|
||||
géométrique. La FK emploie `ON DELETE CASCADE` : supprimer explicitement le parent supprime ses
|
||||
enfants et ne crée pas d'orphelin. Aucun moteur d'invalidation parallèle n'est nécessaire.
|
||||
```text
|
||||
find
|
||||
-> validate
|
||||
-> reuse
|
||||
```
|
||||
|
||||
## Project DB schema
|
||||
Never:
|
||||
|
||||
Project DB v12 ajoute `geometric_verification_results`, une contrainte UNIQUE sur l'identité et un
|
||||
index de pagination `(match_result_id, geometric_verification_result_id)`. Les CHECK SQL portent
|
||||
les bornes scalaires, tailles locales et nullabilité modèle/état. La cohérence avec le parent, le
|
||||
padding, le popcount et la finitude restent validés en C.
|
||||
```text
|
||||
INSERT OR REPLACE
|
||||
latest result
|
||||
closest fingerprint
|
||||
same parent with different version
|
||||
```
|
||||
|
||||
## API
|
||||
A new scientific verifier version creates another result identity.
|
||||
|
||||
L'API publique implémente :
|
||||
## Parent deletion
|
||||
|
||||
- `lardon3d_project_db_create_geometric_verification_result()` ;
|
||||
- `lardon3d_project_db_load_geometric_verification_result()` ;
|
||||
- `lardon3d_project_db_find_geometric_verification_result()` ;
|
||||
- `lardon3d_project_db_list_geometric_verification_results()`.
|
||||
The parent FK uses delete-cascade semantics.
|
||||
|
||||
La liste est bornée à 256 entrées, filtrée par parent puis ordonnée par ID croissant avec curseur.
|
||||
Le résultat en mémoire contient son `created_at` et son masque dans une capacité fixe de 1024
|
||||
octets : aucun ownership dynamique ni fonction de destruction. Les fonctions copient fingerprint,
|
||||
masque et neuf coefficients ; l'appelant conserve ses entrées.
|
||||
Explicit deletion of a Match Result deletes its dependent geometric results.
|
||||
|
||||
Parent absent retourne `NOT_FOUND`; parent NO_MATCH ou parent incohérent retourne `CONSTRAINT` à
|
||||
la création. Masque, modèle ou arguments locaux invalides retournent `INVALID_ARGUMENT`; duplicate
|
||||
identity retourne `CONSTRAINT`. Un loader qui rencontre une ligne ou un parent incohérent retourne
|
||||
`CORRUPT`, sans résultat partiel.
|
||||
No parallel invalidation engine is required.
|
||||
|
||||
## Resource bounds
|
||||
## Schema
|
||||
|
||||
Un résultat contient au plus 1024 octets de masque et 72 octets de valeurs numériques, plus de
|
||||
petites métadonnées. Une page est bornée. Le loader vérifie les entiers et tailles SQLite avant
|
||||
tout cast ou copie. Il n'existe ni cache global, ni lecture non bornée, ni Content Store associé.
|
||||
Le Match File parent mesure au plus 98 336 octets ; le futur job peut donc rester une petite unité.
|
||||
Project DB v12 introduced `geometric_verification_results`.
|
||||
|
||||
## Error ownership
|
||||
The current schema head is v25.
|
||||
|
||||
Seuls les résultats scientifiques terminés sont persistés. OOM, exception, annulation, timeout,
|
||||
device lost, I/O transitoire ou panne de thread appartiennent à l'exécution de tâche. État du modèle
|
||||
et état d'exécution sont deux contrats distincts.
|
||||
Later schema additions do not redefine the v12 row format or identity.
|
||||
|
||||
## Recovery semantics
|
||||
## Public API
|
||||
|
||||
Après commit, le résultat est complet et réutilisable après réouverture. Avant commit, le rollback
|
||||
ne laisse aucune ligne partielle. Un loader rejette toute ligne incohérente comme corruption au
|
||||
lieu de réparer ou d'interpréter au mieux.
|
||||
The model provides bounded create/load/find/list APIs for Geometric Verification Results.
|
||||
|
||||
## Verifier execution contract
|
||||
The list API is paged and ordered by increasing ID.
|
||||
|
||||
L'exécution prend un Match Result et son Match File borné. L'accès nécessaire existe via
|
||||
`lardon3d_feature_reader_keypoints()`, borné à 256 keypoints par appel ; l'intégration devra relier
|
||||
les deux Feature Sets et les indices du Match File sans modifier le Feature Store. Le verifier
|
||||
estimera hors transaction, dérivera état/masque/modèle, publiera en une courte transaction,
|
||||
checkpoint puis libérera les buffers. Une paire est l'unité atomique. Task Runtime et Resource
|
||||
Governor décideront admission, threads et lots ; zram/swap ne sont jamais un budget.
|
||||
In-memory result storage remains bounded: the inlier mask has fixed maximum capacity and no result-owned
|
||||
heap destructor is required for the core row object.
|
||||
|
||||
Un backend reste hors identité seulement s'il est scientifiquement transparent. Sinon son
|
||||
algorithme ou contrat doit apparaître dans kind/version/fingerprint avant publication. Toute seed
|
||||
influençant le résultat doit avoir une politique déterministe versionnée ou être couverte par le
|
||||
fingerprint. Aucun nombre de threads ou hardware ID n'est un paramètre scientifique par défaut.
|
||||
Exact function declarations in the public headers remain authoritative.
|
||||
|
||||
## Explicitly out of scope
|
||||
## Error semantics
|
||||
|
||||
GPU, Vulkan, OpenCL, shader et nouvelle orchestration restent hors périmètre de ce contrat de
|
||||
persistance.
|
||||
Creation distinguishes invalid local arguments from parent/identity constraints.
|
||||
|
||||
## Versioning
|
||||
Loaders return corruption rather than a partially interpreted result if:
|
||||
|
||||
Project DB schema version 12 décrit le stockage. `verifier_version` décrit indépendamment le
|
||||
contrat scientifique. Changer un algorithme n'impose une migration DB que si la représentation
|
||||
persistante change.
|
||||
- parent is missing or invalid;
|
||||
- stored mask length is wrong;
|
||||
- padding is non-canonical;
|
||||
- popcount disagrees;
|
||||
- model/state nullability is inconsistent;
|
||||
- a verified matrix contains non-finite values.
|
||||
|
||||
Scientific rejection is not a database/runtime failure.
|
||||
|
||||
Runtime OOM, exception, cancellation, estimator failure or device failure are not persisted as
|
||||
`GEOMETRIC_REJECTED`.
|
||||
|
||||
## Current verifier lineage
|
||||
|
||||
The model stores all supported versions through the same identity fields.
|
||||
|
||||
### v1
|
||||
|
||||
Historical Fundamental verifier v1 remains immutable and valid.
|
||||
|
||||
### v2
|
||||
|
||||
Historical Fundamental verifier v2 remains immutable and valid.
|
||||
|
||||
V2 added the distinct-canonical-observation preflight in the scientific execution contract.
|
||||
|
||||
### v3
|
||||
|
||||
Current production verifier is Fundamental v3.
|
||||
|
||||
Production fingerprint:
|
||||
|
||||
```text
|
||||
6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c
|
||||
```
|
||||
|
||||
V3 preserves the persistent model and adds its versioned scientific preflight before the unchanged
|
||||
eligible estimator path.
|
||||
|
||||
No Project DB migration was needed for v3 because v12 already stores verifier version and fingerprint.
|
||||
|
||||
## Task relationship
|
||||
|
||||
The production Task Kind is:
|
||||
|
||||
```text
|
||||
geometric_verifier.run/1
|
||||
```
|
||||
|
||||
Project DB v13 adds only its typed durable Task payload.
|
||||
|
||||
The Task:
|
||||
|
||||
```text
|
||||
pages Match Results
|
||||
-> validates eligibility
|
||||
-> computes or reuses exact GVR identity
|
||||
-> owner publishes in canonical parent order
|
||||
-> advances typed cursor
|
||||
-> checkpoints
|
||||
-> sequence_break
|
||||
```
|
||||
|
||||
Task/runtime state remains separate from GVR scientific state.
|
||||
|
||||
## Current resource boundary
|
||||
|
||||
One Match Result is the scientific atomic item.
|
||||
|
||||
Current validated outer-parallel Task execution may prepare independent parents concurrently.
|
||||
|
||||
The owner publishes the contiguous canonical prefix.
|
||||
|
||||
Current validated bounds include:
|
||||
|
||||
```text
|
||||
useful CPU participants <= 8
|
||||
safe parent/window size <= 16
|
||||
per-item reservation approximately 8 MiB
|
||||
GPU = 0
|
||||
```
|
||||
|
||||
The internal USAC/MAGSAC scientific solver remains `isParallel=false`.
|
||||
|
||||
These operational values do not enter GVR identity.
|
||||
|
||||
## Real S21 v3 evidence
|
||||
|
||||
Retained S21 proof:
|
||||
|
||||
```text
|
||||
REAL_S21_GV_V3=PASS/FROZEN
|
||||
|
||||
Match Results 172,741
|
||||
Applicable MATCHED 172,275
|
||||
Verified GVRs 24,065
|
||||
Rejected GVRs 148,210
|
||||
non-applicable 466
|
||||
duplicate mappings 0
|
||||
```
|
||||
|
||||
The source Matcher project was retained unchanged and GV ran only from the Match Result boundary.
|
||||
|
||||
Restart/idempotence evidence preserved the complete GVR result set.
|
||||
|
||||
No Track/Sparse work belonged to the original GV-only boundary.
|
||||
|
||||
## Real A6000 v3 evidence
|
||||
|
||||
Retained current A6000 pre-SfM continuation:
|
||||
|
||||
```text
|
||||
Match Results 38,420
|
||||
Applicable GVRs 37,805
|
||||
Verified GVRs 10,952
|
||||
Rejected GVRs 26,853
|
||||
duplicate mappings 0
|
||||
```
|
||||
|
||||
Fingerprint:
|
||||
|
||||
```text
|
||||
6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c
|
||||
```
|
||||
|
||||
The continuation then built Tracks and stopped before real Sparse SfM.
|
||||
|
||||
Checkpoint:
|
||||
|
||||
```text
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
## Out of scope
|
||||
|
||||
This persistence model does not define:
|
||||
|
||||
- RANSAC/USAC/MAGSAC implementation;
|
||||
- GPU kernels;
|
||||
- Task scheduling;
|
||||
- Track construction;
|
||||
- Essential pose;
|
||||
- triangulation;
|
||||
- Sparse SfM;
|
||||
- Homography competition.
|
||||
|
||||
Those belong to their versioned scientific/runtime contracts.
|
||||
|
||||
## Summary
|
||||
|
||||
```text
|
||||
GEOMETRIC_VERIFICATION_MODEL=IMPLEMENTED
|
||||
PROJECT_DB_GEOMETRIC_VERIFICATION=v12
|
||||
PROJECT_DB_GEOMETRIC_VERIFIER_TASK=v13
|
||||
|
||||
CURRENT_PRODUCTION_VERIFIER=FUNDAMENTAL_V3
|
||||
CURRENT_VERIFIER_FINGERPRINT=6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c
|
||||
|
||||
REAL_S21_GV_V3=PASS/FROZEN
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,468 +1,486 @@
|
|||
# Geometric Verifier v1 / v2 / v3
|
||||
|
||||
## Scope
|
||||
## Status
|
||||
|
||||
Ce document décrit l'exécution scientifique qui transforme un Match Result `MATCHED` en résultat
|
||||
Fundamental `GEOMETRIC_REJECTED` ou `GEOMETRIC_VERIFIED`. Le contrat persistant reste défini par
|
||||
[`geometric_verification.md`](geometric_verification.md). Tracks, pose, Essential, compétition
|
||||
Homography, triangulation et SfM sont hors périmètre.
|
||||
```text
|
||||
HISTORICAL_GEOMETRIC_VERIFIER_V1=FROZEN
|
||||
HISTORICAL_GEOMETRIC_VERIFIER_V2=FROZEN
|
||||
CURRENT_GEOMETRIC_VERIFIER_V3=PASS/FROZEN
|
||||
|
||||
V1 et v2 restent des identités scientifiques historiques et immutables : leurs versions,
|
||||
fingerprints, lignes et résultats existants ne sont jamais réinterprétés. V2 conserve l'estimator,
|
||||
les paramètres, l'ordre et l'acceptance v1, mais ajoute avant USAC le support minimal par
|
||||
observations canoniques distinctes décrit ci-dessous. V3 est la policy de production courante :
|
||||
après les mêmes validations intégrales, elle compose ce support v2 avec la preuve exacte de
|
||||
faisabilité d'acceptation `match_count >= min_inlier_count`. Chaque policy possède sa version et son
|
||||
fingerprint distincts ; le schéma les stocke déjà depuis v12 et la tête courante
|
||||
Project DB v23 ne nécessite aucune migration GV.
|
||||
CURRENT_VERIFIER_KIND=FUNDAMENTAL
|
||||
CURRENT_VERIFIER_VERSION=3
|
||||
CURRENT_VERIFIER_FINGERPRINT=6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c
|
||||
|
||||
GEOMETRIC_VERIFIER_GPU=NOT_JUSTIFIED
|
||||
REAL_S21_GV_V3=PASS/FROZEN
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
This document owns the scientific execution that turns one valid `MATCHED` Match Result into one
|
||||
completed Fundamental Geometric Verification Result.
|
||||
|
||||
The persistent row model is owned by `geometric_verification.md`.
|
||||
|
||||
Tracks, Essential pose, triangulation and Sparse SfM are downstream.
|
||||
|
||||
## Version lineage
|
||||
|
||||
### v1
|
||||
|
||||
Verifier v1 is a frozen historical scientific identity.
|
||||
|
||||
Its existing fingerprints and GVR rows remain immutable.
|
||||
|
||||
### v2
|
||||
|
||||
Verifier v2 preserves the v1 estimator/acceptance path but adds a bounded preflight requiring enough
|
||||
distinct canonical observations on both sides before estimator execution.
|
||||
|
||||
It has its own version/fingerprint.
|
||||
|
||||
### v3
|
||||
|
||||
Verifier v3 is the current production policy.
|
||||
|
||||
It preserves v2 validation and additionally rejects estimator-ineligible parents when:
|
||||
|
||||
```text
|
||||
match_count < min_inlier_count
|
||||
```
|
||||
|
||||
because acceptance is mathematically impossible in that case.
|
||||
|
||||
At equality, the parent remains estimator-eligible.
|
||||
|
||||
V3 does not relabel or mutate v1/v2 rows.
|
||||
|
||||
Project DB v12 already stores verifier version/fingerprint, so no GVR schema migration is needed.
|
||||
|
||||
Current project schema head is v25.
|
||||
|
||||
## Inputs
|
||||
|
||||
Le parent DB fournit les deux Feature Set IDs, le compte, le chemin, la taille et le SHA-256 du
|
||||
Match File. Le reader Feature Store ouvre séparément chaque Feature Set validé et expose les
|
||||
keypoints par plages d'au plus 256. Le verifier n'a besoin d'aucun descriptor : charger les blocs
|
||||
ORB ou SIFT/RootSIFT serait inutile et est interdit dans le chemin normal.
|
||||
The exact Match Result supplies:
|
||||
|
||||
Les keypoints persistants portent des coordonnées `binary32`. `x/y` sont exprimés en pixels de
|
||||
l'image exactement décodée par OpenCV lors de l'extraction, avec origine en haut à gauche et
|
||||
positions subpixel possibles. Les dimensions décodées sont disponibles dans les métadonnées du
|
||||
Feature File.
|
||||
- Candidate Pair relation;
|
||||
- Feature Set A/B identities;
|
||||
- Match File path/size/SHA;
|
||||
- `match_count`.
|
||||
|
||||
## Fundamental matrix contract
|
||||
Feature readers provide keypoints for the two immutable Feature Sets.
|
||||
|
||||
Le seul modèle v1 est une matrice Fundamental 3×3. Une sortie acceptée doit être unique, finie,
|
||||
de norme non nulle et canonique avant publication. V1 ne projette pas la matrice vers le rang 2.
|
||||
The verifier does not need descriptor blocks in its normal geometry path.
|
||||
|
||||
Feature coordinates are persistent binary32 decoded-image pixels with top-left origin.
|
||||
|
||||
They are converted to binary64 `Point2d` for geometric computation.
|
||||
|
||||
## Input ordering
|
||||
|
||||
L'entrée `i` de l'estimator correspond exactement à l'entrée `i` du Match File :
|
||||
`feature_index_a` sélectionne le Feature Set A et `feature_index_b` le Feature Set B. Le Match
|
||||
File impose déjà des indices A strictement croissants ; le verifier ne trie et ne filtre pas les
|
||||
correspondances. Toute corruption d'index est une erreur d'exécution, jamais un rejet scientifique.
|
||||
Estimator row `i` corresponds exactly to Match File entry `i`.
|
||||
|
||||
V2 distingue le nombre brut de lignes des observations canoniques distinctes. Les identités sont
|
||||
`A=(feature_set_id_a, feature_index_a)` et
|
||||
`B=(feature_set_id_b, feature_index_b)`. Après validation intégrale du parent, du Match asset, des
|
||||
Feature Sets et Feature assets, v2 exige au moins sept A distincts **et** sept B distincts. Sinon il
|
||||
publie `GEOMETRIC_REJECTED`, `inlier_count=0`, masque intégralement nul de longueur exactement
|
||||
`ceil(match_count/8)`, sans modèle et sans appel USAC.
|
||||
The verifier does not reorder or deduplicate Match File rows.
|
||||
|
||||
Ce préflight ne modifie jamais l'évidence Matcher : aucune déduplication, tri, contrainte
|
||||
one-to-one, unicité de coordonnées, limite de multiplicité, analyse de rang/conditionnement/
|
||||
colinéarité ou compétition Homography n'est appliquée. Des IDs distincts ayant les mêmes
|
||||
coordonnées restent des observations distinctes. Le Match File canonique rendant A strictement
|
||||
croissant, une insuffisance A implique en pratique moins de sept lignes valides ; B peut en revanche
|
||||
être insuffisant malgré un grand nombre de lignes brutes.
|
||||
The published inlier bit `i` always maps back to Match File entry `i`.
|
||||
|
||||
V3 exécute ensuite USAC seulement si `match_count >= min_inlier_count`. Lorsque cette inégalité
|
||||
échoue, au plus `match_count` bits du masque pourraient être inliers : l'acceptation est donc
|
||||
mathématiquement impossible. V3 publie alors le même rejet zéro borné sans appel estimator. La
|
||||
borne vient du paramètre durable, jamais d'une constante `16`. À l'égalité, l'entrée reste éligible.
|
||||
Ce contrat n'ajoute aucune règle `N<20`, rang, coordonnées, homographie, déduplication ou retry ;
|
||||
toute entrée qui franchit les deux préflights appelle l'USAC inchangé et toute exception inattendue
|
||||
reste un échec Task sans publication.
|
||||
Out-of-range Feature indices or corrupt assets are runtime/input failure, not scientific rejection.
|
||||
|
||||
## Coordinate representation
|
||||
## Canonical observation identity
|
||||
|
||||
Le stockage source reste `binary32`. Sur 1024 points, bruit 0,75 px et 50 % d'outliers, Point2f et
|
||||
Point2d ont produit le même masque et la même qualité, en 3,58 et 3,55 ms. La production convertit
|
||||
vers Point2d pour rendre le calcul et la sortie binary64 explicites, pour 256 Kio au maximum.
|
||||
Aucune mise à l'échelle par résolution ni conversion de repère n'est appliquée implicitement.
|
||||
For preflight counting:
|
||||
|
||||
## Algorithm candidates
|
||||
```text
|
||||
A = (feature_set_id_a, feature_index_a)
|
||||
B = (feature_set_id_b, feature_index_b)
|
||||
```
|
||||
|
||||
OpenCV 5 installé expose `FM_RANSAC`, `USAC_DEFAULT`, `USAC_ACCURATE`, `USAC_PROSAC` et
|
||||
`USAC_MAGSAC`. La shortlist Gate A est FM_RANSAC comme baseline, puis USAC_DEFAULT,
|
||||
USAC_MAGSAC et USAC_ACCURATE. PROSAC est `NOT_APPLICABLE` en v1 : la distance descriptor est
|
||||
persistée mais l'ordre canonique suit l'index de query, pas un classement de qualité benchmarké.
|
||||
V2/v3 require at least seven distinct A observations and seven distinct B observations.
|
||||
|
||||
## Benchmark methodology
|
||||
Failure publishes a zero-inlier `GEOMETRIC_REJECTED` result with a correctly sized all-zero mask and
|
||||
no Fundamental model.
|
||||
|
||||
Un corpus synthétique déterministe avec Fundamental ground truth couvrira bruit, outliers,
|
||||
résolutions, tailles, géométries saines, faibles et adversariales. Les méthodes seront comparées
|
||||
par précision/recall du masque, erreurs épipolaires, échecs, repeatability, temps et ressources.
|
||||
Le benchmark lourd restera hors build et suite par défaut. Aucune fixture photo réelle ne sera
|
||||
revendiquée sans fixture non sensible présente dans le dépôt.
|
||||
This preflight does not:
|
||||
|
||||
La campagne Gate A du 9 août 2026 utilise OpenCV 5.0.0, Clang 22.1.8, une seed fixe et 32
|
||||
répétitions. Elle couvre 7 à 8192 points, 0 à 100 % d'outliers, bruit 0 à 1,5 px, 1280×720 à
|
||||
4000×3000, baseline faible/large, concentration, quasi-colinéarité, planéité, rotation dominante
|
||||
et duplications. Aucune fixture photo réelle représentative n'existe dans le dépôt.
|
||||
- rewrite Matcher evidence;
|
||||
- enforce one-to-one matching;
|
||||
- deduplicate coordinates;
|
||||
- perform a rank test;
|
||||
- perform collinearity analysis;
|
||||
- run Homography competition.
|
||||
|
||||
| Algorithme | P/R 1024, 30 % | P/R 8192, 70 % | Médiane/p95/pire 8192 | Seed locale | Stable 32× |
|
||||
|---|---:|---:|---:|---|---|
|
||||
| FM_RANSAC | 0,998/0,720 | 0,993/0,413 | 316,4/318,9/319,7 ms | non | oui observé |
|
||||
| USAC_DEFAULT | 0,996/0,960 | 0,997/0,959 | 43,0/43,2/44,8 ms | preset non | oui |
|
||||
| USAC_MAGSAC | 0,993/0,965 | 0,994/0,962 | 11,4/12,0/12,1 ms | preset non | oui |
|
||||
| USAC_ACCURATE | 0,996/0,960 | 0,996/0,961 | 30,5/32,0/32,1 ms | preset non | oui |
|
||||
| MAGSAC params v1 | 0,997/0,957 | 0,996/0,894 | 10,8/11,0/11,4 ms | oui | oui |
|
||||
Distinct Feature IDs with identical coordinates remain distinct observations.
|
||||
|
||||
FM_RANSAC est rejeté pour son recall et son pire temps. DEFAULT et ACCURATE n'améliorent pas assez
|
||||
la qualité pour leur coût. La production emploie des UsacParams explicites : la seed par appel
|
||||
prime sur la variation du cas extrême liée à la seed fixe. À bruit 0,75 px/50 % d'outliers, les
|
||||
seuils 0,5/1,0/1,5/2,0/3,0 donnent des recalls 0,535/0,811/0,961/0,990/1,000 et des precisions
|
||||
0,996/0,988/0,990/0,986/0,985. Le compromis retenu est 1,5 px.
|
||||
## v3 acceptance-feasibility preflight
|
||||
|
||||
## Determinism
|
||||
V3 additionally checks the durable configured `min_inlier_count`.
|
||||
|
||||
USAC expose `cv::UsacParams::randomGeneratorState`, un entier par appel, ainsi que les paramètres
|
||||
de sampling, score, optimisation locale et polishing. Cette API est préférable à une mutation de
|
||||
`cv::theRNG()` process-global. FM_RANSAC restera une baseline scientifique tant que son contrôle
|
||||
RNG et sa repeatability n'ont pas été mesurés.
|
||||
When:
|
||||
|
||||
Les cinq candidats ont donné un hash modèle+masque identique sur 32 appels et dans trois processus
|
||||
distincts. La garantie v1 reste intra-environnement : mêmes octets, ordre, configuration, seed,
|
||||
OpenCV 5.0.0 et architecture. Aucun bit-exact cross-version ou cross-architecture n'est promis.
|
||||
```text
|
||||
match_count < min_inlier_count
|
||||
```
|
||||
|
||||
## Random seed policy
|
||||
the maximum possible support cannot satisfy acceptance, so V3 publishes the canonical zero rejection
|
||||
without calling USAC.
|
||||
|
||||
La policy v1 calcule SHA-256 sur `L3DGVSE1`, le SHA-256 du Match File puis le fingerprint. Les
|
||||
quatre premiers octets sont décodés little-endian et les 31 bits faibles alimentent
|
||||
`randomGeneratorState`. La policy est version 1.
|
||||
The threshold comes from configuration, never a hidden constant.
|
||||
|
||||
## Parameter fingerprint
|
||||
## Scientific model
|
||||
|
||||
Le fingerprint v1/v2/v3 est SHA-256 des 84 octets suivants. Les entiers sont little-endian ; les doubles
|
||||
sont leurs bits IEEE-754 binary64 écrits comme `uint64_t` little-endian. NaN/Inf sont refusés et
|
||||
le seul champ autorisant zéro signé, `min_inlier_ratio`, normalise `-0.0` en `+0.0`. Aucun octet ne
|
||||
provient d'un dump de structure.
|
||||
The only production model is a 3x3 Fundamental matrix.
|
||||
|
||||
| Offset | Taille | Champ |
|
||||
|---:|---:|---|
|
||||
| 0 | 8 | domaine ASCII `L3DGVFP1` |
|
||||
| 8 | 4 | version encodage = 1 |
|
||||
| 12 | 4 | kind FUNDAMENTAL = 1 |
|
||||
| 16 | 4 | verifier version = 1, 2 ou 3 |
|
||||
| 20 | 4 | algorithme USAC_MAGSAC explicite = 1 |
|
||||
| 24 | 8 | threshold binary64 |
|
||||
| 32 | 8 | confidence binary64 |
|
||||
| 40 | 4 | max iterations |
|
||||
| 44 | 4 | minimum inlier count |
|
||||
| 48 | 8 | minimum inlier ratio binary64 |
|
||||
| 56 | 4 | seed policy version |
|
||||
| 60 | 4 | canonicalisation version |
|
||||
| 64 | 1 | représentation Point2d = 2 |
|
||||
| 65 | 1 | sampler uniforme = 0 |
|
||||
| 66 | 1 | score MAGSAC = 2 |
|
||||
| 67 | 1 | isParallel = 0 |
|
||||
| 68 | 1 | LO inner = 1 |
|
||||
| 69 | 4 | LO iterations = 5 |
|
||||
| 73 | 4 | LO sample size = 14 |
|
||||
| 77 | 1 | neighbor grid = 1 |
|
||||
| 78 | 1 | COV polisher = 3 |
|
||||
| 79 | 4 | polisher iterations = 3 |
|
||||
| 83 | 1 | réservé nul |
|
||||
Accepted output must be finite, non-zero and canonical.
|
||||
|
||||
Le vector golden v1 de la configuration historique commence par les 84 octets hexadécimaux
|
||||
`4c33444756465031...0300000000` et donne le SHA-256
|
||||
`ddb44bb070c62be66c405946e89cbb49c084f8f30a21d6f408dc239225b7bbd0`. Pour un Match File SHA
|
||||
composé de 31 octets nuls puis `01`, cette configuration donne la seed décimale `1910542150`.
|
||||
V2 conserve cet encodage et place `2` au champ `verifier version`; son fingerprint diffère donc
|
||||
obligatoirement même lorsque les sept paramètres numériques sont identiques. La seed dérivée suit
|
||||
ce fingerprint v2 et appartient à cette nouvelle identité. Pour la configuration production, le
|
||||
fingerprint v2 est `7868a893437ee611a10008a093286997212fa8bd80b2afd2bb1d11f04f01c5ae` ;
|
||||
le même Match SHA golden donne la seed décimale `1528046088`.
|
||||
V3 conserve encore exactement les 84 octets et place `3` au champ version. Pour la configuration
|
||||
production, son fingerprint est
|
||||
`6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c` ; le même Match SHA golden
|
||||
donne la seed décimale `188721673`.
|
||||
Les politiques de
|
||||
ressources, hardware, PSI, lot, worker et réservation CPU ne sont ni des champs ni des entrées.
|
||||
|
||||
## Acceptance policy
|
||||
|
||||
Un modèle candidat qui échoue à cette policy publie REJECTED avec son masque et son compte
|
||||
d'inliers, sans matrice. La production exige `inlier_count >= 16` et
|
||||
`inlier_count / match_count >= 0,20`. Les cas 100 % faux produisent 10/64, 14/256, 26/1024 et
|
||||
28/4096 inliers, ratio maximal 0,15625. Les scènes saines produisent 45/64, 129/256 et 297/1024 ;
|
||||
la faible baseline produit 126/256.
|
||||
|
||||
## Fundamental matrix canonicalization
|
||||
|
||||
La production adopte cette canonicalisation version 1. Les neuf valeurs doivent être finies. La
|
||||
norme de Frobenius est calculée avec une accumulation `hypot` résistante au débordement ; zéro est
|
||||
refusé. Le premier coefficient de valeur absolue strictement maximale gagne, donc un tie conserve
|
||||
le plus petit index ligne-major. Après division, le signe rend ce pivot positif et les zéros signés
|
||||
sont normalisés à `+0.0`. Sur 8192/70 %, les singular values sont
|
||||
3,392e-2, 1,374e-4 et 5,915e-24. OpenCV fournit déjà rank-2 à précision numérique. V1 ne calcule
|
||||
aucune SVD en production, n'impose aucun seuil de rang et n'effectue aucune post-projection rank-2.
|
||||
Les validations production portent uniquement sur la forme 3×3 unique, la finitude et la norme.
|
||||
|
||||
## Inlier mask generation
|
||||
|
||||
Le masque OpenCV est validé en type, taille et valeurs, puis converti sans réordonnancement vers
|
||||
le bitset LSB-first du modèle. Les frontières 7/8/9, 63/64/65 et 8191/8192 sont testées.
|
||||
|
||||
Le core conserve strictement l'ordre d'entrée du Match File. Les tests utilisent des indices B
|
||||
permutés et des masques non contigus ; le bit `i` publié reste l'élément `i` du fichier, jamais
|
||||
l'index de feature. Les tailles 1, 2, 7, 8, 9, 63, 64, 65, 8191 et 8192, le padding nul et le
|
||||
popcount sont couverts avec le Model v1 inchangé.
|
||||
|
||||
## Scientific rejection
|
||||
|
||||
En v1, un nombre brut de matches inférieur au minimum réel produit le rejet zéro historique. En
|
||||
v2, moins de sept observations canoniques distinctes sur A ou B produit le rejet zéro décrit dans
|
||||
`Input ordering`. V3 conserve ce test et rejette également quand le nombre brut est strictement
|
||||
inférieur au `min_inlier_count` configuré. L'absence de modèle sur entrée valide ou l'échec de
|
||||
l'acceptance policy produit un résultat scientifique REJECTED cohérent.
|
||||
|
||||
Le minimum USAC observé est sept. Sept à quinze observations supportées peuvent produire une
|
||||
hypothèse mais ne franchissent pas nécessairement le support d'acceptation production.
|
||||
|
||||
## Execution failure
|
||||
|
||||
Match/Feature asset absent ou corrompu, index hors bornes, exception OpenCV, OOM, masque malformé,
|
||||
matrice non finie ou invariant interne invalide échoue dans le Task Runtime. Aucun résultat
|
||||
scientifique n'est publié dans ces cas.
|
||||
|
||||
Le core traduit parent absent/NO_MATCH et Feature Set absent en erreur d'exécution `NOT_FOUND` ;
|
||||
asset absent, tronqué, hash divergent, ownership ou index incohérent en `CORRUPT` ; exception ou
|
||||
sortie estimator malformée/non finie en `ESTIMATOR_ERROR` ; `bad_alloc` en `OUT_OF_MEMORY` ; et
|
||||
échec Model en `DATABASE_ERROR`. Les seams test-only couvrent erreur estimator, mask/matrice
|
||||
malformés, NaN, OOM et publication. Aucun de ces chemins ne crée de résultat scientifique.
|
||||
|
||||
## Resource bounds
|
||||
|
||||
Une unité atomique est un Match Result, au maximum 8192 correspondances. Le Match File est borné
|
||||
à 98 336 octets et le bitset à 1024 octets. Aucun cache global ni préchargement de projet complet
|
||||
n'est utilisé.
|
||||
|
||||
À 8192 matches, les allocations directement contrôlées maximales sont 98 304 octets d'entries,
|
||||
393 216 octets de keypoints A/B, 262 144 octets de Point2d A/B, deux cartes v2 de présence de
|
||||
8192 octets, 1024 octets de bitset, environ 8192 octets de mask OpenCV et 72 octets de modèle, soit
|
||||
environ 761 Kio hors petits objets et scratch OpenCV. Les cartes appartiennent à une paire et sont
|
||||
libérées au retour ; leur borne Feature Store est opérationnelle et n'ajoute aucune limite
|
||||
scientifique. Aucun descriptor ni matrice A×B n'est lu. Massif mesure 2,445 Mio de heap au pic
|
||||
du test E2E complet, incluant SQLite, OpenCV, fixtures Feature Store et toutes les séquences de test.
|
||||
La forme sérielle historique réservait 4 Mio fixes. La Task outer-parallel
|
||||
courante réserve 8 Mio par item admis afin de couvrir aussi la pile enfant
|
||||
bornée de 4 Mio, l'objet préparé, les readers et le scratch opaque. Avec un lot
|
||||
maximal 16, cette charge reste bornée à 128 Mio et ne limite pas la cardinalité
|
||||
du dataset.
|
||||
|
||||
## CPU policy
|
||||
|
||||
Le parallélisme scientifique interne OpenCV reste explicitement désactivé :
|
||||
`UsacParams::isParallel=false` appartient au fingerprint FROZEN. Le verifier ne
|
||||
change pas `cv::setNumThreads()` par paire.
|
||||
|
||||
Le parallélisme courant porte uniquement sur des Match Results indépendants.
|
||||
Le Governor peut admettre CPU1..8 pour une fenêtre/lot d'au plus 16 ; le
|
||||
callback Queue compte comme participant, crée au plus `cpu_threads-1` enfants,
|
||||
les joint, puis publie seul dans l'ordre. La fenêtre participant a été validée
|
||||
sûre jusqu'à 16, mais CPU8 est le maximum utile mesuré. Ce choix opérationnel
|
||||
n'entre ni dans le fingerprint, ni dans le GVR.
|
||||
|
||||
## GPU policy
|
||||
|
||||
Aucun backend Vulkan n'est implémenté avant profil du chemin CPU final. La décision attendue est
|
||||
`NOT_JUSTIFIED` si les unités restent sub-millisecondes ou de quelques millisecondes.
|
||||
|
||||
Verdict Gate A : `NOT_JUSTIFIED`. Les cas usuels prennent 0,3 à 5,6 ms et le pire MAGSAC local
|
||||
mesuré reste à 11,4 ms. Aucun backend Vulkan de vérification n'est implémenté.
|
||||
|
||||
## Task Runtime
|
||||
|
||||
L'audit Gate C conclut que le checkpoint générique v1 est insuffisant : il conserve l'état,
|
||||
la progression, le compteur de séquences et les temps, mais aucun payload propre au kind. Le
|
||||
reconstructeur doit retrouver les paramètres scientifiques immuables et le curseur sans les
|
||||
inventer depuis un fingerprint irréversible.
|
||||
|
||||
`geometric_verifier_tasks` est donc la seule raison de Project DB v13. Elle doit conserver
|
||||
`task_id`, `after_match_result_id`, les sept paramètres de configuration v1 et le fingerprint
|
||||
calculé à la création pour validation à la reconstruction. Aucun `cv::Mat`, buffer, état RNG,
|
||||
paramètre Governor ou donnée hardware n'y appartient. La tâche calcule hors transaction et publie
|
||||
chaque résultat par transaction courte avant avancement du curseur.
|
||||
|
||||
Le payload v22 ne duplique pas `verifier_version`. À la reconstruction, le fingerprint exact est
|
||||
comparé aux encodages supportés v1, v2 et v3 des sept paramètres durables : cela restaure sans
|
||||
ambiguïté chaque policy, sans migration ni nouveau système d'identité. Toute nouvelle tâche
|
||||
sélectionne v3 ; le payload et l'ABI publique de configuration restent inchangés. Une tâche v3
|
||||
démarre avec une identité neuve et ne reprend ni ne ré-étiquette une tâche v1/v2.
|
||||
|
||||
Le Task Kind production est `geometric_verifier.run` version 1. Il pagine les Match Results par ID
|
||||
strictement croissant avec une page de `batch + 1`, et traite des lots Governor
|
||||
1..16 avec CPU utile 1..8. Les préparations éligibles peuvent s'exécuter en
|
||||
parallèle, mais la publication, l'avancement du curseur contigu et le checkpoint
|
||||
restent owner-only et ordonnés. Les parents autres que `MATCHED` avec
|
||||
`match_count > 0` sont seulement traversés par le curseur. Une unité éligible
|
||||
appelle le core, qui reuse l'identité exacte avant toute lecture d'asset.
|
||||
|
||||
WHY GENERIC TASK PERSISTENCE IS INSUFFICIENT: aucun champ de payload métier dans le snapshot v1.
|
||||
|
||||
REQUIRED DURABLE FIELDS: configuration scientifique v1, fingerprint et dernier Match Result
|
||||
publié puis checkpointé.
|
||||
|
||||
WHY EXISTING DB CANNOT STORE THEM: `tasks` et `checkpoints` ne portent que le résumé générique ;
|
||||
aucune table v12 ne possède une ligne 1:1 adaptée à ce Task Kind.
|
||||
|
||||
## Checkpoint/recovery
|
||||
|
||||
La pagination suit `match_result_id` croissant sans supposer des IDs contigus. Le résultat est
|
||||
publié avant que `after_match_result_id` avance en mémoire ; le curseur n'est persisté qu'après le
|
||||
lot. Après chaque lot non terminal, `task_sequence_break()` rend la réservation au Governor.
|
||||
|
||||
Le test de crash publie puis interrompt avant checkpoint du curseur, ferme runtime et DB, recharge
|
||||
le checkpoint antérieur et reconstruit le Task Kind. Le parent est revu, son résultat exact est
|
||||
réutilisé, puis le curseur progresse.
|
||||
|
||||
## Cancellation
|
||||
|
||||
Pause et annulation sont coopératives avant chaque parent et entre lots. Une petite estimation
|
||||
OpenCV engagée finit et publie avant l'arrêt ; aucun résultat scientifique CANCELLED n'est créé.
|
||||
Les résultats déjà publiés restent durables.
|
||||
|
||||
## Backend policy
|
||||
|
||||
Un backend n'est transparent pour l'identité que si ses sorties scientifiques sont équivalentes
|
||||
selon le contrat. V1 possède une seule implémentation CPU de production.
|
||||
|
||||
## Core publication and reuse
|
||||
|
||||
Le core charge les métadonnées DB, relâche les mutex internes après chaque API, lit les assets et
|
||||
calcule sans transaction longue, puis appelle une publication Model v1 courte. Une identité exacte
|
||||
VERIFIED ou REJECTED est retournée avant toute lecture Feature/Match et sans appel estimator. Une
|
||||
contrainte concurrente déclenche un unique `find` de l'identité, jamais un overwrite ou une
|
||||
récursion. Changer un paramètre scientifique produit un autre fingerprint et un autre résultat.
|
||||
|
||||
Les tests E2E utilisent le vrai Project DB (migré séquentiellement jusqu'à v15 à
|
||||
l'ouverture), deux Feature Files à 8192 points, des Match Files hashés, le vrai
|
||||
MAGSAC et le Model v1. VERIFIED est rechargé après close/reopen avec modèle et
|
||||
masque bit-identiques ; REJECTED conserve son support et est également réutilisé.
|
||||
The current verifier does not add Essential/Homography model competition.
|
||||
|
||||
## Production algorithm
|
||||
|
||||
UsacParams explicites, sampler uniforme, score MAGSAC, non parallèle et seed locale par appel.
|
||||
Les champs LO et polishing effectifs sont encodés explicitement ; aucun preset enum caché.
|
||||
The production estimator uses explicit OpenCV USAC/MAGSAC parameters rather than a hidden preset.
|
||||
|
||||
## Production parameters
|
||||
Scientific choices include:
|
||||
|
||||
FUNDAMENTAL version 1 ; seuil 1,5 px ; confiance 0,999 ; 5000 itérations ; 16 inliers ; ratio 0,20 ;
|
||||
seed policy 1 ; canonicalisation 1 ; Point2d. Tous les champs scientifiques appartiennent au
|
||||
fingerprint version 1.
|
||||
```text
|
||||
model FUNDAMENTAL
|
||||
algorithm USAC_MAGSAC
|
||||
point representation Point2d
|
||||
threshold 1.5 px
|
||||
confidence 0.999
|
||||
max iterations 5000
|
||||
minimum inlier count 16
|
||||
minimum inlier ratio 0.20
|
||||
sampler uniform
|
||||
score MAGSAC
|
||||
isParallel false
|
||||
LO iterations 5
|
||||
LO sample size 14
|
||||
polisher COV
|
||||
polisher iterations 3
|
||||
```
|
||||
|
||||
## Validation
|
||||
The internal scientific estimator remains serial:
|
||||
|
||||
Gate A couvre corpus, comparaison, seed et repeatability. Gate B couvre fingerprint/seed golden,
|
||||
canonicalisation, mapping bit à bit, frontières d'acceptation, E2E DB, reuse, corruption,
|
||||
publication, 8192 matches et ASan/UBSan. Gate C couvre Task, publication avant curseur et reprise.
|
||||
```text
|
||||
UsacParams::isParallel=false
|
||||
```
|
||||
|
||||
La référence A6000 v3 complète contient 37 805 parents `MATCHED`. Le préflight v3 rejette à zéro
|
||||
9 368 parents avec `N<16`, puis le support distinct A/B rejette 117 parents supplémentaires avec
|
||||
`N>=16`. Les 28 320 autres parents appellent USAC sous leur seed v3 exacte ; ils terminent tous par
|
||||
un modèle ou une absence de modèle, sans exception estimator. Cette exécution démarre une tâche v3
|
||||
neuve et ne reprend ni ne ré-étiquette la tâche v2 historique 1385.
|
||||
Outer Task-level concurrency is separate.
|
||||
|
||||
Gate D a exécuté 1000 parents configurés dans la vraie Task, puis les reprises et variantes de
|
||||
configuration du test : environ 2001 traversées réutilisées en 5,870 s, soit environ 341/s. Ce
|
||||
run valide pagination, checkpoints, Governor et reuse ; il n'est pas une mesure de latence MAGSAC
|
||||
et n'en revendique ni médiane ni p95. Le RSS pic observé est 25 964 Kio pour le processus de test
|
||||
complet. `MemAvailable` passe de 10 702 988 à 10 692 916 Kio ; `pswpin/pswpout` restent 0/0 ; en
|
||||
fin de run, PSI avg10 vaut 0,34 % CPU, 0 % mémoire et 0 % I/O. Le chemin calculé reste couvert par
|
||||
le vrai E2E MAGSAC Gate B et ses bornes, sans campagne scientifique répétée.
|
||||
## Random seed
|
||||
|
||||
TSan couvre core, Task, sequencing et Governor (4/4), avec uniquement la suppression OpenCV
|
||||
existante. Le build CPU-only couvre la suite normale (31/31). La suite normale ne contient ni
|
||||
benchmark lourd ni stress. Le clean build Clang/Clang++ et la campagne normale finale passent
|
||||
32/32 avec ORB Vulkan matériel sur Radeon 780M RADV PHOENIX.
|
||||
The seed is derived locally from immutable scientific input.
|
||||
|
||||
The frozen seed domain is based on:
|
||||
|
||||
```text
|
||||
L3DGVSE1
|
||||
Match File SHA-256
|
||||
verifier parameter fingerprint
|
||||
```
|
||||
|
||||
The resulting seed is supplied to the per-call USAC parameter object.
|
||||
|
||||
Global `cv::theRNG()` mutation is forbidden.
|
||||
|
||||
## Parameter fingerprint
|
||||
|
||||
The verifier fingerprint is SHA-256 over the frozen canonical 84-byte encoding.
|
||||
|
||||
Domain:
|
||||
|
||||
```text
|
||||
L3DGVFP1
|
||||
```
|
||||
|
||||
It explicitly encodes:
|
||||
|
||||
- verifier kind;
|
||||
- verifier version;
|
||||
- algorithm;
|
||||
- threshold;
|
||||
- confidence;
|
||||
- iteration bound;
|
||||
- minimum inlier count;
|
||||
- minimum inlier ratio;
|
||||
- seed-policy version;
|
||||
- canonicalization version;
|
||||
- Point2d selection;
|
||||
- sampler;
|
||||
- score;
|
||||
- `isParallel`;
|
||||
- LO settings;
|
||||
- neighbor mode;
|
||||
- polisher settings;
|
||||
- reserved zero byte.
|
||||
|
||||
Integers are little-endian.
|
||||
|
||||
Binary64 values use explicit IEEE-754 bits encoded little-endian.
|
||||
|
||||
No C struct dump participates.
|
||||
|
||||
Current v3 production fingerprint:
|
||||
|
||||
```text
|
||||
6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c
|
||||
```
|
||||
|
||||
Historical v1/v2 fingerprints remain distinct and valid.
|
||||
|
||||
## Acceptance
|
||||
|
||||
A candidate is verified only when the frozen support policy is satisfied:
|
||||
|
||||
```text
|
||||
inlier_count >= 16
|
||||
inlier_count / match_count >= 0.20
|
||||
```
|
||||
|
||||
Otherwise the result is scientific `GEOMETRIC_REJECTED`.
|
||||
|
||||
A scientific rejection is a completed valid result.
|
||||
|
||||
Runtime failure publishes no scientific GVR.
|
||||
|
||||
## Fundamental canonicalization
|
||||
|
||||
The nine coefficients must be finite.
|
||||
|
||||
Frobenius norm is computed robustly; zero norm is rejected.
|
||||
|
||||
The pivot is the first row-major coefficient having the strictly greatest absolute value.
|
||||
|
||||
The matrix is normalized and signed so that pivot is positive.
|
||||
|
||||
Signed zeros are normalized to `+0.0`.
|
||||
|
||||
No production SVD/rank-2 post-projection is performed by this verifier version.
|
||||
|
||||
## Inlier mask
|
||||
|
||||
OpenCV output mask is validated for type, length and values.
|
||||
|
||||
It is converted without reordering to the persistent LSB-first GVR bitset.
|
||||
|
||||
Padding bits are zero and popcount matches `inlier_count`.
|
||||
|
||||
## Runtime failure versus rejection
|
||||
|
||||
Scientific rejection includes valid cases such as:
|
||||
|
||||
- insufficient canonical support;
|
||||
- v3 impossible acceptance support;
|
||||
- estimator returns no acceptable model;
|
||||
- inlier support below frozen acceptance.
|
||||
|
||||
Runtime/input failure includes:
|
||||
|
||||
- missing/corrupt Match or Feature asset;
|
||||
- out-of-range Feature index;
|
||||
- malformed estimator mask;
|
||||
- non-finite invalid model;
|
||||
- OOM;
|
||||
- unexpected OpenCV exception;
|
||||
- Project DB publication failure.
|
||||
|
||||
Runtime failure publishes no fake rejected result.
|
||||
|
||||
## Resource bounds
|
||||
|
||||
One scientific atomic item is one Match Result with at most 8192 correspondences.
|
||||
|
||||
Current outer-parallel Task reservation uses approximately:
|
||||
|
||||
```text
|
||||
8 MiB per admitted parent
|
||||
batch/window <= 16
|
||||
useful CPU participants <= 8
|
||||
GPU 0
|
||||
```
|
||||
|
||||
The window is safe to 16; CPU8 is the retained useful maximum from measurement.
|
||||
|
||||
These are operational resource values, not fingerprint fields.
|
||||
|
||||
No descriptors or dense A-by-B matrix are loaded by the normal verifier.
|
||||
|
||||
## Outer parallel execution
|
||||
|
||||
Current validated shape:
|
||||
|
||||
```text
|
||||
one admitted owner Task
|
||||
-> bounded independent Match Result preparation
|
||||
-> up to admitted CPU participants
|
||||
-> join
|
||||
-> owner publishes canonical contiguous prefix
|
||||
-> owner advances cursor
|
||||
-> checkpoint
|
||||
-> sequence_break
|
||||
```
|
||||
|
||||
Participant preparation writes no GVR rows.
|
||||
|
||||
Only the owner publishes after join.
|
||||
|
||||
This preserves deterministic parent ordering and restart semantics.
|
||||
|
||||
The inner USAC solver remains scientifically serial.
|
||||
|
||||
## GPU policy
|
||||
|
||||
Current classification:
|
||||
|
||||
```text
|
||||
GEOMETRIC_VERIFIER_GPU=NOT_JUSTIFIED
|
||||
```
|
||||
|
||||
Measured CPU units remained small enough that no production GPU backend was justified.
|
||||
|
||||
No Vulkan/OpenCL/CUDA verifier backend is currently part of the production identity.
|
||||
|
||||
CPU outer parallelism remains valid and should not be disabled merely because GPU is rejected.
|
||||
|
||||
## Durable Task
|
||||
|
||||
Task Kind:
|
||||
|
||||
```text
|
||||
geometric_verifier.run/1
|
||||
```
|
||||
|
||||
Project DB v13 adds the typed task payload required because generic checkpoint v1 does not contain the
|
||||
scientific verifier parameters/cursor.
|
||||
|
||||
Durable payload includes the immutable scientific configuration and:
|
||||
|
||||
```text
|
||||
after_match_result_id
|
||||
```
|
||||
|
||||
The fingerprint is revalidated on reconstruction.
|
||||
|
||||
The durable payload does not contain:
|
||||
|
||||
- `cv::Mat`;
|
||||
- RNG engine state;
|
||||
- Governor feedback;
|
||||
- hardware identity;
|
||||
- CPU mask;
|
||||
- transient buffers.
|
||||
|
||||
## Restart
|
||||
|
||||
Pagination follows increasing `match_result_id` and does not assume contiguous IDs.
|
||||
|
||||
A GVR is published before the durable cursor advances.
|
||||
|
||||
Crash after publication but before checkpoint may replay that parent.
|
||||
|
||||
Restart finds/revalidates the exact GVR identity and reuses it.
|
||||
|
||||
No overwrite is performed.
|
||||
|
||||
## Historical resource normalization
|
||||
|
||||
The exact historical serial resource shape remains accepted only for restart compatibility.
|
||||
|
||||
It may be normalized ephemerally to the current outer-parallel capability.
|
||||
|
||||
The original checkpoint is not rewritten.
|
||||
|
||||
Neighboring resource shapes are rejected rather than guessed.
|
||||
|
||||
## Real S21 GV v3
|
||||
|
||||
`REAL_S21_GV_V3=PASS/FROZEN` au 31 août 2026. La preuve part d'une copie reflink entière du projet
|
||||
Matcher S21 gelé à 2 826 Feature Sets, 172 741 Candidate Pairs et 172 741 Match Results. Le SHA-256
|
||||
DB source vaut avant et après
|
||||
`9f5ee4877bca25db3d4929be06d8e6ff4fa1c29e11249e4125266a833f09f3e0` ; le projet source n'est
|
||||
jamais ouvert en écriture. La copie de travail reprend exclusivement à la frontière Match Result,
|
||||
par la Task, la Queue et le Resource Governor AUTO de production, puis s'arrête avant Track
|
||||
Builder. Avant GV, la Task Matcher 2831 est `COMPLETE`, progression 100,
|
||||
`sequence_count=21629`, curseur 172 741 ; son checkpoint SHA-256 vaut
|
||||
`636f4f4a20f27308d90142c495c9f6ffc04b4c0dfcca0fdc75cfeb5366ab50b1`. Le projet contient alors
|
||||
zéro GVR, Track Set, Track ou Sparse Reconstruction.
|
||||
Retained S21 proof:
|
||||
|
||||
La Task 2832 consomme le curseur complet de 172 741 Match Results. Parmi eux, 172 275 parents
|
||||
`MATCHED` applicables produisent exactement 172 275 identités v3 : 24 065
|
||||
`GEOMETRIC_VERIFIED` et 148 210 `GEOMETRIC_REJECTED`. Les 466 autres Match Results sont traversés
|
||||
sans GVR conformément au contrat. Le fingerprint est
|
||||
`6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c`. La Task termine
|
||||
`COMPLETE`, progression 100, `sequence_count=21592`, curseur 172 741 et zéro mapping dupliqué. Son
|
||||
checkpoint final SHA-256 vaut
|
||||
`3e6bed97cee9c96f229ef19a3c905d19d4693cdbf43b30319edcdeed6c4e378e`. Le wall propre à
|
||||
l'enqueue/attente GV vaut 3 221,757986763 s ; le wall du runner incluant l'audit intégral amont
|
||||
vaut 3 252,89 s.
|
||||
```text
|
||||
REAL_S21_GV_V3=PASS/FROZEN
|
||||
|
||||
L'audit relit les 172 741 mappings Candidate/Match et les 172 275 Match Files : SHA, taille,
|
||||
header, entrées, ordre et curseur Matcher restent valides. Le digest `L3DMRD1` demeure
|
||||
`e5128a2e599ff593c4f79850e067254b1f249d19e8480a44973306b1af250f70`. Feature Sets, Candidate
|
||||
Pairs et Match Results gardent respectivement 2 826, 172 741 et 172 741 lignes ; aucune Task
|
||||
Feature, Candidate ou Matcher n'est rejouée. Track Set, Track, Track Builder Task, Sparse SfM Task
|
||||
et Sparse Reconstruction restent tous à zéro.
|
||||
Match Results 172,741
|
||||
Applicable MATCHED 172,275
|
||||
Verified 24,065
|
||||
Rejected 148,210
|
||||
non-applicable 466
|
||||
duplicate mapping 0
|
||||
```
|
||||
|
||||
Le Governor enregistre 21 593 admissions, exclusivement backend fixe, sans changement de contrat.
|
||||
Le dernier contrat est GREEN, CPU 1, GPU 0, I/O 1, batch 8, hôte 4 Mio et GPU 0. Les masques sont
|
||||
compute `0-5,8-13` et reserve `6,7,14,15`. Sur l'échantillonnage coalescé de 21 590 changements,
|
||||
le minimum `MemAvailable` vaut 8 907 714 560 octets, le RSS/HWM processus maximal 45 690 880
|
||||
octets, le PSI mémoire maximal 0,90 %, le PSI I/O maximal 50,17 % et les deltas swap-in/out sont
|
||||
0/0. Les réserves 3 Gio/2 Gio alors en vigueur pour ce run historique restent
|
||||
respectées ; aucune voie GPU GV n'est créée.
|
||||
The v3 fingerprint is the current production fingerprint.
|
||||
|
||||
La seconde reprise complète crée la Task 2833, traverse le même curseur et crée zéro GVR. Les
|
||||
172 275 lignes avant/après sont égales sur toutes leurs colonnes par `EXCEPT` dans les deux sens,
|
||||
avec zéro différence, les mêmes IDs 1..172275 et les mêmes comptes accepté/rejeté. Sur une copie
|
||||
reflink séparée, SIGKILL interrompt la Task 2834 après un préfixe checkpointé : l'état durable reste
|
||||
`RUNNING/PENDING`, puis la registry de production reprend cette même Task (`inspected=1`,
|
||||
`resumed=1`) jusqu'à `COMPLETE`, progression 100 et curseur 172 741. L'égalité complète des GVR
|
||||
avec le projet terminé reste zéro différence dans les deux sens ; aucun résultat n'est perdu ou
|
||||
dupliqué et aucun travail amont/aval n'est exécuté.
|
||||
A complete replay produced zero new GVRs.
|
||||
|
||||
Les builds normaux Vulkan et portable sans Vulkan passent. Les 14 tests focalisés GV, Task,
|
||||
checkpoint, Project DB/Project, registry, Queue et Governor passent dans chaque configuration ; le
|
||||
test runner ciblé passe aussi sous ASan/UBSan. `REAL_S21_GV_V3`, `RESTART_IDEMPOTENCE`,
|
||||
`DETERMINISM`, `GOVERNOR_ADMISSION` et `DOWNSTREAM_STOP` sont donc `PASS/FROZEN`. Ce gel porte sur
|
||||
la preuve réelle de la policy v3 déjà gelée ; il ne rouvre ni algorithme, seuil, RNG, fingerprint,
|
||||
Project DB v22, Matcher/Governor v2, Track Builder ou Sparse SfM.
|
||||
A SIGKILL/restart proof resumed the same Task and converged to the same complete GVR set.
|
||||
|
||||
## Maintenance outer-parallel — preuve représentative réelle
|
||||
The run stopped before Track Builder at the original GV-only checkpoint.
|
||||
|
||||
**IMPLEMENTED / VALIDATED / REVIEWED.** La maintenance sépare la préparation
|
||||
scientifique de la publication sans modifier la policy v3. Une préparation
|
||||
valide tout l'input immuable et produit un objet opaque borné ; elle n'écrit
|
||||
jamais Project DB. Après jointure, le callback propriétaire publie ces objets
|
||||
en ordre de `match_result_id`, détruit chacun exactement une fois et checkpoint
|
||||
le seul préfixe contigu. Les pannes de création partielle, calcul, publication,
|
||||
annulation et reprise ne peuvent donc ni publier un suffixe devant un trou, ni
|
||||
laisser un enfant/handle vivant à la libération de réservation.
|
||||
Later S21 Track evidence exists separately.
|
||||
|
||||
Le corpus représentatif réel contient 4 113 parents, dont 4 102 applicables,
|
||||
578 `GEOMETRIC_VERIFIED` et 3 524 `GEOMETRIC_REJECTED`. CPU1/2/4/8/12
|
||||
conservent les mêmes IDs 1..4102, toutes les colonnes scientifiques et le digest
|
||||
`9401ef6168804b6f1d51f4cdf64cd6b33cbebd2934e5294c8feacc87f9c8ce86`.
|
||||
Les walls Task complets sont 67,521078032/48,859141912/39,158236068/
|
||||
35,170176868/34,251675780 s, soit 60,7514/83,9556/104,7545/116,6329/
|
||||
119,7606 parents/s. Le gain CPU8→CPU12 vaut seulement 2,68 %, sous le seuil de
|
||||
5 %. La capacité production est donc CPU utile 8, batch 16 et fenêtre sûre 16.
|
||||
## Real A6000 v3
|
||||
|
||||
Les tests focalisés finaux passent 8/8, les répétitions de stress 60/60,
|
||||
ASan/UBSan 3/3 et TSan 3/3, avec contrôles C17 GCC/Clang. La preuve S21
|
||||
historique ci-dessus reste le run complet CPU1/batch8 acquis ; aucun rerun
|
||||
complet de 3 221 s n'est revendiqué pour cette maintenance bornée. Le manifest
|
||||
retenu de cette preuve a le SHA-256
|
||||
`52a4412299c74050a66d5690122a793c9451c79faf47e32b6e65a5958f804856`.
|
||||
Il compare littéralement les 4 102 GVR applicables — ordre/IDs 1..4102,
|
||||
statut, compteur/masque d'inliers, présence et octets binary64 du modèle — et
|
||||
vérifie intégrité DB, clés étrangères, absence de replay amont et absence de
|
||||
travail Tracks/Sparse. Le run S21 complet acquis couvre déjà la policy v3 et
|
||||
sa persistance FROZEN ; la maintenance ne change que la préparation externe et
|
||||
la publication owner-only. Cette combinaison réelle bornée + tests ciblés de
|
||||
panne/checkpoint/reprise discrimine donc le changement sans payer un second run
|
||||
scientifique intégral ni prétendre l'avoir exécuté.
|
||||
The retained A6000 pre-SfM continuation contains:
|
||||
|
||||
La validation globale fraîche qui englobe ce delta passe aussi dans le graphe
|
||||
Clang portable Vulkan-off 931/931 et sa suite 64/64, puis le graphe Vulkan-on
|
||||
939/939 et sa suite 65/65. Le TSan global reste volontairement Vulkan-disabled
|
||||
et couvre les deux cibles GV dans sa matrice 14/14 plus répétitions ; il utilise
|
||||
uniquement les suppressions externes OpenCV/TBB documentées par le projet.
|
||||
```text
|
||||
Match Results 38,420
|
||||
Applicable GVRs 37,805
|
||||
Verified GVRs 10,952
|
||||
Rejected GVRs 26,853
|
||||
duplicate mappings 0
|
||||
```
|
||||
|
||||
## Out of scope
|
||||
Current fingerprint:
|
||||
|
||||
Tracks, model competition, classification planaire ou faible parallaxe, Essential, calibration,
|
||||
pose, triangulation, bundle adjustment, SfM et Vulkan RANSAC.
|
||||
```text
|
||||
6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c
|
||||
```
|
||||
|
||||
The Task completed the full cursor.
|
||||
|
||||
Restart traversed the cursor and created zero new GVRs.
|
||||
|
||||
The continuation then reused/built the frozen Track Set and stopped before real Sparse SfM.
|
||||
|
||||
```text
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
## Validation boundaries
|
||||
|
||||
The scientific verifier validation covers:
|
||||
|
||||
- canonical fingerprint/seed;
|
||||
- bit-exact mask mapping;
|
||||
- acceptance boundaries;
|
||||
- corruption;
|
||||
- publication/reuse;
|
||||
- maximum Match File cardinality;
|
||||
- restart;
|
||||
- deterministic canonicalization;
|
||||
- outer-parallel owner publication.
|
||||
|
||||
TSan qualification must preserve the external OpenCV/TBB boundary described by the concurrency/global
|
||||
maintenance documents.
|
||||
|
||||
Do not claim Vulkan verifier validation: there is no production Vulkan verifier backend.
|
||||
|
||||
## Summary
|
||||
|
||||
```text
|
||||
CURRENT_GEOMETRIC_VERIFIER=FUNDAMENTAL_V3
|
||||
CURRENT_VERIFIER_VERSION=3
|
||||
CURRENT_VERIFIER_FINGERPRINT=6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c
|
||||
|
||||
GEOMETRIC_VERIFIER_TASK=geometric_verifier.run/1
|
||||
PROJECT_DB_GEOMETRIC_VERIFICATION=v12
|
||||
PROJECT_DB_GEOMETRIC_VERIFIER_TASK=v13
|
||||
|
||||
INNER_USAC_PARALLEL=false
|
||||
OUTER_PARALLEL=VALIDATED
|
||||
USEFUL_CPU_MAX=8
|
||||
SAFE_WINDOW_MAX=16
|
||||
PER_ITEM_RAM=8_MiB
|
||||
GPU=NOT_JUSTIFIED
|
||||
|
||||
REAL_S21_GV_V3=PASS/FROZEN
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,314 +1,505 @@
|
|||
# Exécution et runtime Lardon3D
|
||||
# Lardon3D Runtime
|
||||
|
||||
## Modèle d'exécution
|
||||
|
||||
### Threads
|
||||
- Thread principal : entrée, modèle de vue et rendu TUI ncursesw (exclusif)
|
||||
- Thread worker Queue : exécution sérielle des tâches métier
|
||||
- Participants internes : uniquement ceux du contrat Task admis, joints par le
|
||||
callback propriétaire avant publication
|
||||
- Opération SSD : au plus un thread joinable, uniquement pendant une opération
|
||||
UDisks bornée ; il ne rend rien et ne devient ni Queue ni scheduler
|
||||
|
||||
### Synchronisation
|
||||
- Mutex pour les accès partagés
|
||||
- Variables de condition pour la coordination
|
||||
- Atomicité des opérations critiques
|
||||
|
||||
## Cycle de vie d'une tâche
|
||||
## Status
|
||||
|
||||
```text
|
||||
1. Création (PENDING)
|
||||
2. Soumission à la file
|
||||
3. Sélection FIFO/adaptative par la Queue
|
||||
4. Réservation obligatoire
|
||||
5. Exécution (RUNNING)
|
||||
- Pause/reprise coopérative
|
||||
- Annulation coopérative
|
||||
- Séquences adaptatives
|
||||
6. Complétion (COMPLETED) ou Échec (FAILED)
|
||||
7. Nettoyage des ressources
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
CURRENT_PRODUCTION_TASK_KINDS=16
|
||||
|
||||
TASK_QUEUE_WORKERS=1
|
||||
INTERNAL_PARALLELISM=BOUNDED
|
||||
INTER_TASK_PARALLELISM=NOT_IMPLEMENTED
|
||||
|
||||
RUNTIME_OBSERVER=CURRENT/VALIDATED_OPERATIONAL
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
## Synchronisation
|
||||
The runtime coordinates the main thread, Task Queue, Resource Governor, Project DB lifecycle, bounded
|
||||
Task-internal participants, runtime observation and optional external-SSD controller.
|
||||
|
||||
### Mutex
|
||||
- Protection des données partagées
|
||||
- Accès exclusif aux ressources critiques
|
||||
It does not introduce a second scheduler.
|
||||
|
||||
### Variables de condition
|
||||
- Coordination entre threads
|
||||
- Notification de changement d'état
|
||||
- Attente passive ; timeout borné seulement pour réévaluer un `WAIT` ressources
|
||||
## Thread model
|
||||
|
||||
### Atomicité
|
||||
- Opérations indivisibles
|
||||
- État cohérent garanti
|
||||
### Main thread
|
||||
|
||||
## Gestion des erreurs
|
||||
Owns:
|
||||
|
||||
### Rollback
|
||||
- Rollback des transactions locales avant publication
|
||||
- Nettoyage complet des ressources possédées par l'opération
|
||||
- Une publication fichier réussie suivie d'un échec DB peut laisser un orphelin
|
||||
valide ; aucune transaction distribuée fichier+SQLite n'est revendiquée
|
||||
- input;
|
||||
- ncurses;
|
||||
- TUI model binding;
|
||||
- project open/close orchestration;
|
||||
- bounded polling of runtime/SSD state.
|
||||
|
||||
### Récupération
|
||||
- Reprise à la dernière frontière connue
|
||||
- Ignorance des artefacts partiels
|
||||
- Validation avant publication
|
||||
ncurses remains main-thread-only.
|
||||
|
||||
## Limites actuelles
|
||||
### Task Queue worker
|
||||
|
||||
- Worker Queue unique (pas de pools inter-Tasks multiples)
|
||||
- Pas de parallélisme inter-Tasks ; certains kinds possèdent des participants
|
||||
internes bornés, comptés par leur contrat Governor
|
||||
- Reprise automatique limitée aux tâches indépendantes reconstructibles
|
||||
The single Queue worker owns one active heavy Task callback at a time.
|
||||
|
||||
## Reprise durable
|
||||
A Task callback may create bounded internal participants only when its admitted Task contract permits
|
||||
them.
|
||||
|
||||
Un snapshot ne conserve que l'état logique d'une tâche. `RUNNING` et `PAUSED`
|
||||
sont normalisés vers `PENDING`; aucun worker, callback brut, pointeur, contrat
|
||||
ou réservation n'est restauré. Le propriétaire fournit un nouveau callback et
|
||||
resoumet la tâche. Les états terminaux sont conservés.
|
||||
Those participants join before owner publication.
|
||||
|
||||
`started_at` désigne le début de la tentative d'exécution courante, pas le
|
||||
premier démarrage historique. Un checkpoint `RUNNING` restauré en `PENDING`
|
||||
conserve temporairement l'horodatage de la tentative interrompue pour
|
||||
l'observation ; lors de `lardon3d_task_start()`, `started_at` est remplacé par le
|
||||
nouveau démarrage et `finished_at` est remis à zéro. `finished_at` n'est fixé
|
||||
qu'à la terminaison de cette tentative.
|
||||
### SSD operation thread
|
||||
|
||||
**IMPLEMENTED** — snapshot, codec v1 et restauration isolée.
|
||||
The SSD controller may own at most one bounded joinable operation thread while executing a synchronous
|
||||
UDisks operation.
|
||||
|
||||
**IMPLEMENTED** — l'import `import.images` se sauvegarde à chaque fin de lot et
|
||||
se reconstruit explicitement avec un userdata neuf lié au projet rouvert. Son
|
||||
intention durable contient `source_path + scanset_id`; le hash/copie et la
|
||||
transaction catalogue restent hors mutex Task et hors mutex DB pendant l'I/O.
|
||||
It does not become:
|
||||
|
||||
**IMPLEMENTED** — `project_open()` inventorie par pages de 8, restaure puis
|
||||
resoumet automatiquement les tâches production valides. Il retourne après
|
||||
l'enqueue et n'attend jamais leur terminaison.
|
||||
- a Task worker;
|
||||
- a Queue;
|
||||
- a scheduler;
|
||||
- an ncurses owner.
|
||||
|
||||
L'ordre d'initialisation production est : politique driver, profil matériel,
|
||||
Governor, backend, Queue/worker, contrôleur SSD optionnel et binding
|
||||
Governor, puis TUI. L'ouverture DB/projet et la reprise synchrone sont pilotées
|
||||
ensuite depuis le thread principal. Une fermeture ne peut commencer qu'après le
|
||||
retour de `project_open()`. Le worker peut consommer pendant le scan ; chaque
|
||||
tâche exécutée est néanmoins réadmise normalement.
|
||||
## Task lifecycle
|
||||
|
||||
**NOT_YET_WIRED** — reprise ordonnée par dépendances/DAG. Les kinds de
|
||||
production reconstructibles checkpointent déjà à leurs frontières métier ;
|
||||
aucun timer autosave générique ne doit avancer devant leur publication durable.
|
||||
Conceptual lifecycle:
|
||||
|
||||
**IMPLEMENTED** — reprise sélective des kinds reconstructibles via Project DB,
|
||||
Task Kind Registry et Queue. Les dépendances/DAG restent différées ; il
|
||||
n'existe pas de scheduler global distinct à restaurer.
|
||||
```text
|
||||
create PENDING
|
||||
-> persist typed intent where required
|
||||
-> enqueue
|
||||
-> Queue selects
|
||||
-> Resource Governor admits/reserves
|
||||
-> RUNNING
|
||||
-> bounded sequence work
|
||||
-> Task-specific durable publication
|
||||
-> generic checkpoint
|
||||
-> optional sequence_break/re-admission
|
||||
-> COMPLETED | FAILED | CANCELLED
|
||||
-> terminal callback
|
||||
-> destruction
|
||||
```
|
||||
|
||||
## Accès Project Database
|
||||
Pause/cancel are cooperative.
|
||||
|
||||
**IMPLEMENTED** — une connexion SQLite opaque sérialisée par mutex interne ;
|
||||
les opérations multi-tables sont transactionnelles et bornées.
|
||||
A sequence break is not a Task state.
|
||||
|
||||
**IMPLEMENTED** — le cycle de vie projet ouvre/crée `project.db`, vérifie
|
||||
l'identité et ferme la connexion. Ouvrir, fermer ou changer de projet est une
|
||||
frontière exacte : l'observateur et la vue optique libèrent leurs borrows, puis
|
||||
l'unique Queue est annulée, jointe et détruite, callbacks terminaux inclus,
|
||||
avant la fermeture de Project DB. Une seule Queue vide est ensuite recréée et
|
||||
les observateurs sont rebondés. Il n'existe jamais deux schedulers simultanés.
|
||||
L'historique terminal et l'espace d'IDs Queue sont ainsi propres à la session ;
|
||||
les mêmes Task IDs durables de deux projets restent indépendants et aucun
|
||||
historique fourni n'est affiché lorsqu'aucun projet n'est chargé.
|
||||
## Durable restart
|
||||
|
||||
**IMPLEMENTED** — la registry reconstruit explicitement callback/userdata hors
|
||||
mutex DB pour un kind connu ; elle ne soumet aucune tâche.
|
||||
A generic snapshot stores logical Task state, not live execution machinery.
|
||||
|
||||
**IMPLEMENTED** — la queue accepte un identifiant restauré préassigné s'il
|
||||
n'entre en collision avec aucune tâche connue. L'import production peut donc
|
||||
être reconstruit puis soumis explicitement.
|
||||
On restoration:
|
||||
|
||||
**IMPLEMENTED** — la resoumission automatique utilise la registry production,
|
||||
conserve le task ID et laisse le worker obtenir une nouvelle réservation.
|
||||
Kinds inconnus, tâches legacy, checkpoints invalides et sources absentes ne
|
||||
bloquent pas l'ouverture.
|
||||
```text
|
||||
RUNNING -> PENDING
|
||||
PAUSED -> PENDING
|
||||
```
|
||||
|
||||
**IMPLEMENTED** — `visual_index.update` reprend à la dernière membership
|
||||
commitée. Un segment temporaire n'est jamais visible et un rejeu exclut les
|
||||
Feature Sets déjà membres.
|
||||
Terminal states remain terminal.
|
||||
|
||||
## Durée de vie terminale et fermeture Queue
|
||||
Restart never restores:
|
||||
|
||||
Une Task terminale reste vivante jusqu'au retour complet de son callback
|
||||
terminé. Queue la retire alors de la liste active et la détruit hors de son
|
||||
mutex ; seule une histoire de 64 snapshots reste observable. Les appels déjà
|
||||
enregistrés avant `task_queue_destroy()` sont attendus. Le propriétaire doit
|
||||
empêcher tout nouvel appel dès le début de la destruction, règle nécessaire à
|
||||
toute API C adressée par pointeur brut.
|
||||
- worker thread;
|
||||
- callback pointer;
|
||||
- userdata pointer;
|
||||
- CPU affinity;
|
||||
- live reservation;
|
||||
- GPU handle;
|
||||
- scratch lease;
|
||||
- adaptive feedback history.
|
||||
|
||||
Un callback terminé peut consulter les vues Queue tant que le propriétaire la
|
||||
maintient vivante. Il ne peut pas détruire cette Queue, retirer son propre
|
||||
record ni attendre une opération dépendante de son retour.
|
||||
Task Kind Registry reconstructs fresh runtime binding from exact durable kind/version and typed payload.
|
||||
|
||||
## Observatoire TUI actuel
|
||||
Every resumed Task is re-admitted by the Resource Governor.
|
||||
|
||||
**CURRENT / VALIDATED OPERATIONAL.** Ce statut décrit l'implémentation et ses
|
||||
tests courants. L'audit global qui contient cette frontière est désormais
|
||||
`PASS/FROZEN` après revue indépendante ; le statut TUI reste volontairement
|
||||
opérationnel et n'interdit pas ses évolutions futures sous un ticket distinct.
|
||||
## Project-open recovery
|
||||
|
||||
### Séparation modèle, observation et rendu
|
||||
`project_open()` discovers durable Tasks in bounded pages, validates their checkpoint/typed identity,
|
||||
reconstructs eligible bindings and submits them to the existing Queue.
|
||||
|
||||
Le modèle `tui_model` est pur et testable sans terminal. Le renderer reçoit
|
||||
seulement des copies bornées et n'interroge ni Queue, ni Governor, ni Project
|
||||
DB, ni contrôleur SSD. Toutes les fonctions ncurses, l'entrée clavier et le
|
||||
rendu demeurent sur le thread principal.
|
||||
It returns after enqueue; it does not wait for those Tasks to finish.
|
||||
|
||||
L'observateur runtime emprunte Queue et Governor et conserve une seule copie
|
||||
cohérente. Les captures ordinaires sont coalescées pendant au moins une seconde
|
||||
monotone ; un échec conserve la dernière vue bornée en la marquant stale.
|
||||
Il observe au plus 129 Tasks : les 64 pending possibles, l'unique active et les
|
||||
64 snapshots terminaux récents. L'ordre Queue place le travail vivant du plus
|
||||
récent au plus ancien, puis l'histoire par terminaison décroissante ; une Task
|
||||
active ne peut donc pas être masquée par un vieux préfixe historique. Il
|
||||
n'existe ni scan DB par frame, ni lecture `/proc` volumineuse, ni historique
|
||||
non borné.
|
||||
Unknown kinds, unsupported versions, legacy-untyped Tasks, invalid checkpoints or Task-specific
|
||||
non-reconstructible input remain inspectable and do not cause guessed execution.
|
||||
|
||||
Les ABI historiques restent exactes : `Lardon3DTaskSnapshot`,
|
||||
`Lardon3DResourceSnapshot`, `Lardon3DAppState` et
|
||||
`lardon3d_layout_draw()` ne sont pas étendus en place. Les surfaces additives
|
||||
`Lardon3DTaskObservation`, `lardon3d_task_queue_observe()`,
|
||||
`Lardon3DResourceObservation`, `Lardon3DRuntimeSnapshot` et
|
||||
`lardon3d_layout_draw_runtime()` portent les nouveaux champs. De même,
|
||||
`lardon3d_tui_run()` reste le symbole historique ; l'application utilise
|
||||
`lardon3d_tui_run_with_ssd_operation()` avec un owner SSD conservé hors de
|
||||
`Lardon3DAppState`.
|
||||
Generic dependency/DAG recovery remains unimplemented.
|
||||
|
||||
### Progression et ETA
|
||||
## Initialization order
|
||||
|
||||
Une Task typée publie `completed/total` seulement après son propre commit
|
||||
métier durable. Quand ces compteurs sont connus, la TUI les affiche toujours et
|
||||
en dérive le pourcentage sans utiliser le message ou le nom. Une Task marquée
|
||||
`COMPLETED` avec un préfixe durable incomplet est une erreur d'intégrité
|
||||
visible, jamais 100 %. Quand les comptes typés sont inconnus, le lifecycle peut
|
||||
être terminal mais la progression scientifique reste indéterminée. Le
|
||||
pourcentage générique non typé, lorsqu'il est utile, porte explicitement le
|
||||
libellé runtime.
|
||||
Production startup establishes the safe driver/runtime policy before heavy worker/backend activity.
|
||||
|
||||
Le débit est un EWMA borné. La première observation établit seulement le
|
||||
préfixe de reprise et ne contribue pas au taux ; une reprise de RUNNING remet
|
||||
également la fenêtre temporelle à zéro. Deux intervalles strictement positifs
|
||||
sont nécessaires avant un débit et une ETA connus. Une absence de progrès,
|
||||
une pression Governor, une régression ou une preuve insuffisante produit
|
||||
respectivement `STALLED`, `THROTTLED`, reset ou `INDETERMINATE/CALCULATING`.
|
||||
Seule une complétion cohérente vaut exactement 100 % et ETA zéro ; aucune fausse
|
||||
précision n'est affichée.
|
||||
Conceptually:
|
||||
|
||||
### Pipeline et ressources
|
||||
```text
|
||||
driver policy
|
||||
-> hardware profile
|
||||
-> Resource Governor
|
||||
-> optional backend metadata
|
||||
-> Task Queue / worker
|
||||
-> optional SSD controller + Governor binding
|
||||
-> TUI
|
||||
```
|
||||
|
||||
La synthèse utilise les étapes Acquisition, RAW, Quality, Features, Visual
|
||||
Index, Candidate, Matcher, GV, Tracks, Sparse SfM et future Dense. Les états
|
||||
sont `NOT_READY`, `READY`, `QUEUED`, `RUNNING`, `THROTTLED`, `BLOCKED`,
|
||||
`COMPLETE`, `FAILED` et `NOT_APPLICABLE`. Dense reste explicitement
|
||||
`NOT_APPLICABLE` tant qu'aucun Task kind de production n'existe ; une étape
|
||||
future n'est jamais devinée active depuis un nom ou un message.
|
||||
Project open/recovery is then driven from the main thread.
|
||||
|
||||
Le panneau ressources expose CPU actif/admis/disponible et sa raison, GPU
|
||||
présent/mémoire/busy/backend lorsqu'ils sont connus, RAM/MemAvailable/réserve,
|
||||
swap total/utilisé et deltas actifs, lot/inflight/helpers/I/O, scratch et
|
||||
pression Governor GREEN/YELLOW/RED. Le contrat installé de l'exacte Task active
|
||||
est l'autorité pour CPU et lot. Un dernier diagnostic privé seulement indexé
|
||||
par kind peut appartenir à une autre Task ou séquence : backend, inflight,
|
||||
helpers, utilisation ou raison restent donc `UNKNOWN` sans association exacte
|
||||
Task+séquence. La mémoire UMA est comptée une seule fois et ni swap ni scratch
|
||||
ne sont ajoutés à la capacité RAM.
|
||||
The exact source initialization sequence remains authoritative.
|
||||
|
||||
### Dimensions, couleurs et clavier
|
||||
## Project lifetime boundary
|
||||
|
||||
Le layout complet demande au moins 100×30. Le layout compact est validé à la
|
||||
frontière 72×20 et reste supporté jusqu'au minimum 60×15. En dessous, le rendu
|
||||
se réduit au message borné `Terminal trop petit`; un resize recalcule la classe
|
||||
sans faire travailler un worker. Les rôles sémantiques sont healthy vert,
|
||||
warning jaune, error rouge, GPU cyan, CPU bleu, SSD magenta, plus dim/bold.
|
||||
Les libellés textuels demeurent l'autorité lorsqu'il n'y a pas de couleur ou
|
||||
pas assez de paires terminal.
|
||||
Changing/closing project is an exact ownership boundary.
|
||||
|
||||
Les écrans courants sont accueil, projets, import, viewer futur, tâches,
|
||||
ressources, optique, SSD et aide. `F1..F7` naviguent respectivement vers aide,
|
||||
projets, import, viewer, tâches, ressources et optique. Le segment littéral
|
||||
`F10 SSD` est réservé au début du footer et reste visible à 60 colonnes dans
|
||||
tous les modes pertinents. Les footers dérivent du même mode que le handler :
|
||||
Before Project DB close:
|
||||
|
||||
- saisie active : Enter valide, Échap annule, F10 reste disponible ;
|
||||
- import actif : `X` demande l'annulation et F10 reste disponible ; `q` et
|
||||
Échap sont affichés comme désactivés ;
|
||||
- mode idle : `q`, Échap/navigation et les commandes propres à l'écran sont
|
||||
annoncés seulement lorsqu'ils sont réellement traités ;
|
||||
- Tasks : flèches/`j`/`k`, `P` pause, `R` reprise, `C` annulation ;
|
||||
- Optique : Tab change de panneau, flèches/`j`/`k` sélectionnent, `[` revient à
|
||||
la première page et `]` charge la suivante ; `B/L/C/V/A/G/K/E` déclenchent
|
||||
les opérations indiquées et `R` retente explicitement un bind/chargement.
|
||||
```text
|
||||
views release DB borrows
|
||||
-> Queue ingress closes
|
||||
-> Queue cancels/joins/destroys
|
||||
-> terminal callbacks finish
|
||||
-> Project DB closes
|
||||
```
|
||||
|
||||
### Workflow optique
|
||||
A fresh empty Queue can then be created for the next project.
|
||||
|
||||
La TUI consomme les API v23 décrites dans
|
||||
[Project Database](project_database.md), sans SQL direct ni édition d'une ligne
|
||||
immuable. Elle inspecte une affectation Capture, effectue seulement des lookup
|
||||
metadata exacts, liste les profils de boîtier/objectif/configuration et accepte
|
||||
un objectif manuel sans électronique ni alias — le Meike de test est un cas
|
||||
normal, pas une branche produit. « Modifier » signifie créer un nouveau profil
|
||||
ou une nouvelle configuration immuable, puis l'assigner explicitement à un
|
||||
groupe de campagne encore éligible ou à un Capture non affecté. Les
|
||||
calibrations listées doivent être compatibles avec l'exacte configuration et
|
||||
la sélection reste explicite ; absence, ambiguïté, incompatibilité, BUSY, I/O
|
||||
et corruption sont rendues sans profil fabriqué. Les pages ont 16 lignes,
|
||||
rapportent un compte page-local et un indicateur « suite » exact.
|
||||
No terminal callback may dereference a closed Project DB.
|
||||
|
||||
### SSD F10 et lifetime application
|
||||
Queue terminal history belongs to the current runtime session and does not leak between projects.
|
||||
|
||||
La TUI affiche les huit états physiques `ABSENT`, `DETECTED`, `ENABLING`,
|
||||
`ENABLED`, `IN_USE`, `DRAINING`, `SAFE_TO_UNPLUG` et `ERROR`, avec identité
|
||||
stable, modèle/télémétrie lorsqu'ils sont connus, swap, scratch, mount, usage,
|
||||
leases, drain et raison. `UNKNOWN` n'est jamais remplacé par zéro ou par une
|
||||
supposition ; `SAFE_TO_UNPLUG` est mis en évidence comme endpoint sûr. F10
|
||||
choisit exclusivement l'une des capacités exactes
|
||||
`can_enable`, `can_disable` ou `can_cancel_drain` publiée par le contrôleur ; un
|
||||
état incomplet, une paire de remplacement ou un résultat malformé n'accorde
|
||||
aucune autorité. L'opération synchrone UDisks s'exécute dans au plus un thread
|
||||
joinable, tandis que le main continue de rendre et de poller sans blocage.
|
||||
## Project Database
|
||||
|
||||
La validation qui alimente ces capacités est fail-closed par état : toute
|
||||
autorité exige Drive et deux partitions détectés, identités Drive+UUID exactes,
|
||||
extents positifs connus et faits mount/activité/drain cohérents. `ABSENT` ne
|
||||
peut transporter aucun fait actif, `DETECTED` partiel n'a aucune action et un
|
||||
hazard `ERROR` déconnecté ne peut que retenir l'identité originale sans
|
||||
allocation. Seule la reconnexion complète de ce tuple peut autoriser son drain.
|
||||
Project DB uses an opaque serialized SQLite connection with bounded transactional operations.
|
||||
|
||||
Après chaque observation ou résultat validé, l'adaptateur enregistre une copie
|
||||
bornée de l'état physique auprès du Governor. Une copie malformée devient
|
||||
`ERROR` et interdit les nouvelles allocations ; l'observation ressources lit
|
||||
cet état Governor-owned, tandis que les détails/permissions F10 restent dans
|
||||
le snapshot physique. La génération source peut saturer à `UINT64_MAX` : une
|
||||
update publique égale ne réaccorde jamais une autorité stale ; seule la
|
||||
complétion du wrapper exact déjà engagé réconcilie son lease adressé. À l'arrêt,
|
||||
l'ordre est : destruction/join de la Queue et
|
||||
libération de chaque lease Task, fermeture du projet/DB, join puis unregister
|
||||
vérifié de l'adaptateur SSD, destruction du contrôleur, puis destruction du
|
||||
Governor. Les tests utilisent un provider factice et n'exécutent aucune vraie
|
||||
mutation SSD.
|
||||
Current schema head:
|
||||
|
||||
## Invariants
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
```
|
||||
|
||||
- ncurses appartient exclusivement au thread principal
|
||||
- Aucune tâche ne démarre sans réservation active
|
||||
- Les réservations sont libérées exactement une fois
|
||||
- Les buffers sont strictement bornés
|
||||
- Le Resource Governor reste l'unique propriétaire de l'admission ; ni Queue,
|
||||
ni contrôleur SSD ne constituent un second orchestrateur de ressources
|
||||
Current additive selected-execution overlays include:
|
||||
|
||||
## Statut : CURRENT / VALIDATED OPERATIONAL
|
||||
```text
|
||||
v24 raw.develop.batch/1
|
||||
v25 features.extract.batch/1
|
||||
```
|
||||
|
||||
La TUI/runtime et son raccordement SSD sont implémentés, testés et relus dans
|
||||
leur tranche. Le statut global est
|
||||
`GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN`. Les builds portables/Vulkan,
|
||||
sanitizers, contrôles de concurrence et ABI frais sont acquis ; l'unique revue
|
||||
finale indépendante a conclu PASS sans finding bloquant.
|
||||
The v23 optical model remains valid but is no longer the schema head.
|
||||
|
||||
## Current production Task inventory
|
||||
|
||||
Production currently has sixteen Task Kinds.
|
||||
|
||||
Important selected-execution additions:
|
||||
|
||||
```text
|
||||
raw.develop.batch/1
|
||||
features.extract.batch/1
|
||||
```
|
||||
|
||||
The Queue still has one active heavy callback; batch Task Kinds obtain throughput from bounded
|
||||
participants inside that callback.
|
||||
|
||||
Per-item atomicity does not require cross-item serial execution.
|
||||
|
||||
## Resource Governor
|
||||
|
||||
Every production Task goes through the Resource Governor, including fixed-resource Tasks.
|
||||
|
||||
The canonical host policy is:
|
||||
|
||||
```text
|
||||
preserve defined interactive reserve
|
||||
then maximize safe useful throughput
|
||||
```
|
||||
|
||||
```text
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
```
|
||||
|
||||
Reference-host CPU counts are evidence, not portable constants.
|
||||
|
||||
Pressure may reduce a later admission; healthy recovery may ramp useful width again.
|
||||
|
||||
Swap/zram/scratch do not enlarge admitted RAM.
|
||||
|
||||
UMA GPU memory is charged once against host RAM.
|
||||
|
||||
## Internal parallelism
|
||||
|
||||
Validated Task Kinds may execute:
|
||||
|
||||
```text
|
||||
one Queue owner callback
|
||||
-> bounded participants
|
||||
-> join
|
||||
-> deterministic owner publication
|
||||
```
|
||||
|
||||
Current examples include:
|
||||
|
||||
- Feature selected batch;
|
||||
- Visual Index;
|
||||
- Candidate Pair;
|
||||
- Matcher CPU work;
|
||||
- Geometric Verifier outer parallel preparation;
|
||||
- RAW selected batch.
|
||||
|
||||
This is not inter-Task parallelism and does not create another global worker pool.
|
||||
|
||||
## Current Matcher backend policy
|
||||
|
||||
ORB Matcher normal production is Governor-owned AUTO.
|
||||
|
||||
Eligible ORB work prefers the validated Vulkan backend.
|
||||
|
||||
Fallback is complete CPU recomputation.
|
||||
|
||||
Normal Vulkan contract:
|
||||
|
||||
```text
|
||||
inflight = 1
|
||||
helpers = 0
|
||||
useful batch <= 8
|
||||
```
|
||||
|
||||
Depth 2 remains validated private safety/benchmark capacity but was rejected as normal useful policy.
|
||||
|
||||
SIFT/RootSIFT Matcher remains CPU.
|
||||
|
||||
## Runtime observation
|
||||
|
||||
The runtime observer borrows Queue/Governor state and publishes one bounded coherent snapshot for the
|
||||
TUI.
|
||||
|
||||
It does not retain Task userdata.
|
||||
|
||||
Ordinary snapshots are rate-limited/coalesced.
|
||||
|
||||
On observation failure, the previous bounded view may be retained and explicitly marked stale.
|
||||
|
||||
Task observation includes live/pending/recent-terminal entries only within fixed capacity.
|
||||
|
||||
No unbounded Project DB scan is performed per frame.
|
||||
|
||||
## Durable progress and ETA
|
||||
|
||||
Typed Tasks publish exact `completed/total` only after their Task-specific durable prefix is committed.
|
||||
|
||||
The TUI must not infer exact scientific progress from:
|
||||
|
||||
- Task name;
|
||||
- message text;
|
||||
- generic percentage.
|
||||
|
||||
When exact counters exist, they are authoritative.
|
||||
|
||||
Throughput/ETA needs enough positive-time progress observations.
|
||||
|
||||
No-progress/pressure/restart cases become explicit states such as stalled, throttled or indeterminate
|
||||
rather than fabricated precision.
|
||||
|
||||
A terminal Task with inconsistent durable progress is visible as an integrity problem rather than
|
||||
silently forced to 100%.
|
||||
|
||||
## Pipeline observation
|
||||
|
||||
Current observable stages include:
|
||||
|
||||
```text
|
||||
Acquisition
|
||||
RAW
|
||||
Quality
|
||||
Features
|
||||
Visual Index
|
||||
Candidate
|
||||
Matcher
|
||||
GV
|
||||
Tracks
|
||||
Sparse SfM
|
||||
Dense
|
||||
```
|
||||
|
||||
Sparse SfM capability exists.
|
||||
|
||||
Dense has no production Task Kind and remains not applicable/unimplemented at the current checkpoint.
|
||||
|
||||
Historical S21/A6000 real campaigns have not executed Sparse SfM because known calibration is
|
||||
unavailable for those campaigns.
|
||||
|
||||
## TUI resource observation
|
||||
|
||||
Resource UI may display known bounded values for:
|
||||
|
||||
- active/admitted/available CPU;
|
||||
- GPU presence/backend/busy/memory;
|
||||
- RAM and `MemAvailable`;
|
||||
- host reserve;
|
||||
- swap state and active deltas;
|
||||
- batch/inflight/helpers;
|
||||
- IO;
|
||||
- scratch;
|
||||
- Governor pressure.
|
||||
|
||||
The installed contract of the exact active Task is authoritative for that sequence.
|
||||
|
||||
A diagnostic indexed only by Task Kind cannot automatically be attributed to another Task/sequence.
|
||||
|
||||
Unknown values remain unknown.
|
||||
|
||||
## TUI layout
|
||||
|
||||
The validated layout classes remain bounded.
|
||||
|
||||
Current thresholds include:
|
||||
|
||||
```text
|
||||
full layout >= 100x30
|
||||
compact boundary = 72x20
|
||||
supported minimum = 60x15
|
||||
```
|
||||
|
||||
Below the supported minimum, the UI uses its too-small-terminal fallback.
|
||||
|
||||
Repository documentation is English. Any remaining non-English executable UI literal is legacy runtime
|
||||
text and must be changed only in the explicitly scoped UI-language remediation pass; documentation does
|
||||
not redefine executable behavior by pretending that source literal has already changed.
|
||||
|
||||
## Navigation
|
||||
|
||||
The current TUI provides screens for the implemented runtime surfaces, including:
|
||||
|
||||
- home;
|
||||
- projects;
|
||||
- import;
|
||||
- tasks;
|
||||
- resources;
|
||||
- optics;
|
||||
- SSD;
|
||||
- help;
|
||||
- viewer placeholder/future surface.
|
||||
|
||||
Key bindings and exact executable labels remain owned by the TUI source and its tests.
|
||||
|
||||
Documentation should describe behavior rather than preserve stale localized literals as authority.
|
||||
|
||||
## Optical workflow
|
||||
|
||||
The TUI uses the public optical APIs introduced by the v23 overlay.
|
||||
|
||||
It does not write optical SQLite rows directly.
|
||||
|
||||
It supports explicit inspection/selection and immutable profile creation.
|
||||
|
||||
Manual lenses without electronic metadata are valid data.
|
||||
|
||||
No workflow may fabricate:
|
||||
|
||||
- "unknown" lens identity;
|
||||
- calibration compatibility;
|
||||
- metadata match;
|
||||
- focal/lens substitution.
|
||||
|
||||
Calibration selection remains explicit and exact.
|
||||
|
||||
## SSD F10 boundary
|
||||
|
||||
The SSD UI reflects controller capability/state rather than inventing actions.
|
||||
|
||||
The controller owns physical detection, pairing, mount/swap/scratch state and bounded UDisks operations.
|
||||
|
||||
The Resource Governor owns scratch lease admission.
|
||||
|
||||
A state that is incomplete, stale or physically inconsistent grants no control/lease authority.
|
||||
|
||||
Scratch is storage capacity, never RAM.
|
||||
|
||||
## Global shutdown
|
||||
|
||||
Ownership shutdown preserves:
|
||||
|
||||
```text
|
||||
Task Queue / Task leases
|
||||
-> project close
|
||||
-> SSD operation join / Governor unregister
|
||||
-> SSD controller
|
||||
-> Resource Governor
|
||||
```
|
||||
|
||||
A real outstanding scratch lease can block unregister and must remain an observable error.
|
||||
|
||||
Do not abandon a live lease pointer.
|
||||
|
||||
## Error/recovery model
|
||||
|
||||
Runtime operations use local rollback and explicit publication boundaries.
|
||||
|
||||
File asset publication plus SQLite is not treated as one distributed transaction.
|
||||
|
||||
A successfully published physical asset followed by DB failure may leave a valid orphan.
|
||||
|
||||
Recovery validates known durable representations; it does not guess or silently repair scientific
|
||||
identity.
|
||||
|
||||
## Current real checkpoint
|
||||
|
||||
Current retained A6000 checkpoint:
|
||||
|
||||
```text
|
||||
real-a6000-pre-sfm-2026-09-02
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
It exercised current runtime/Queue/Governor behavior through:
|
||||
|
||||
```text
|
||||
selected RAW batch
|
||||
selected Feature batch
|
||||
Visual Index
|
||||
Candidate Pair
|
||||
Matcher
|
||||
Geometric Verifier v3
|
||||
Track Builder
|
||||
```
|
||||
|
||||
The final continuation recorded deterministic restart/reuse and stopped with:
|
||||
|
||||
```text
|
||||
Sparse SfM Tasks 0
|
||||
Sparse Reconstructions 0
|
||||
Dense/MVS 0
|
||||
```
|
||||
|
||||
This is a current real runtime checkpoint, later than the historical global-maintenance checkpoint.
|
||||
|
||||
Both remain valid for the boundaries they prove.
|
||||
|
||||
## Current limits
|
||||
|
||||
Current runtime intentionally does not provide:
|
||||
|
||||
- multiple concurrent heavy Queue callbacks;
|
||||
- generic inter-Task DAG scheduling;
|
||||
- generic Task priorities beyond current Queue policy;
|
||||
- generic autosave ahead of Task-specific durable publication;
|
||||
- Dense/MVS production Task Kind;
|
||||
- live capture/viewer reconstruction loop;
|
||||
- generic scratch-consuming Task Kind.
|
||||
|
||||
Those are future product/implementation decisions, not silently missing state.
|
||||
|
||||
## Summary
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
CURRENT_PRODUCTION_TASK_KINDS=16
|
||||
|
||||
TASK_QUEUE_WORKERS=1
|
||||
ACTIVE_HEAVY_CALLBACKS=1
|
||||
INTER_TASK_PARALLELISM=NOT_IMPLEMENTED
|
||||
INTERNAL_PARALLELISM=BOUNDED
|
||||
|
||||
GENERIC_DAG=NOT_IMPLEMENTED
|
||||
DENSE_TASK_KIND=NOT_IMPLEMENTED
|
||||
CURRENT_SCRATCH_CONSUMING_TASK_KINDS=0
|
||||
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,526 +1,450 @@
|
|||
# Track Model v1
|
||||
|
||||
## Scope
|
||||
## Status
|
||||
|
||||
Track Model v1 est le contrat persistant qui transforme les correspondances
|
||||
géométriquement vérifiées en structures multi-view cohérentes. Il stocke des
|
||||
ensembles d'observations 2D liées à un même point physique supposé. Il ne
|
||||
calcule rien, ne triangule pas, ne contient aucune coordonnée 3D et ne résout
|
||||
aucun conflit. Le Track Builder, la triangulation, le Sparse SfM et le Bundle
|
||||
Adjustment sont des étapes séparées ; Gate E a gelé le Builder v1 sans
|
||||
implémenter ces étapes 3D.
|
||||
```text
|
||||
TRACK_MODEL_V1=FROZEN
|
||||
TRACK_BUILDER_V1=PASS/FROZEN
|
||||
|
||||
CURRENT_PRODUCTION_VERIFIER=FUNDAMENTAL_V3
|
||||
SPARSE_SFM_CAPABILITY=IMPLEMENTED_THROUGH_GATE_G
|
||||
REAL_S21_SPARSE_SFM=NOT_EXECUTED
|
||||
REAL_A6000_SPARSE_SFM=NOT_EXECUTED
|
||||
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
Track Model v1 is the persistent scientific contract for coherent multi-view 2D observation sets.
|
||||
|
||||
A Track is **not** a 3D point.
|
||||
|
||||
It contains no camera pose, triangulated coordinate, reprojection error or Bundle Adjustment state.
|
||||
|
||||
Track Builder v1 constructs Tracks from verified Geometric Verification Results. Sparse SfM consumes an
|
||||
immutable Track Set later.
|
||||
|
||||
## Pipeline position
|
||||
|
||||
Current pipeline:
|
||||
|
||||
```text
|
||||
Feature Set
|
||||
-> Candidate Pair
|
||||
-> Match Result
|
||||
-> Geometric Verifier v3
|
||||
-> Geometric Verification Result
|
||||
-> Track Builder v1
|
||||
-> Track Model v1
|
||||
-> Sparse SfM capability
|
||||
```
|
||||
|
||||
Sparse SfM Gates C through G are implemented and frozen.
|
||||
|
||||
The retained S21 and A6000 historical campaigns stop before real Sparse SfM because known calibration
|
||||
data is unavailable for those campaigns.
|
||||
|
||||
Older Track Model text that called Sparse SfM "future" describes historical lifecycle, not current
|
||||
implementation status.
|
||||
|
||||
## Track definition
|
||||
|
||||
Un **Track** est un ensemble d'observations 2D cohérentes d'un même point
|
||||
physique supposé, observé à travers plusieurs images. Chaque observation est
|
||||
identifiée par `(feature_set_id, feature_index)`.
|
||||
A Track is a coherent set of 2D observations believed to correspond to the same physical scene point
|
||||
across multiple images.
|
||||
|
||||
Un Track n'est **pas** un point 3D. Il ne contient aucune coordonnée 3D,
|
||||
aucune erreur de reprojection, aucun statut de triangulation. La
|
||||
triangulation appartient à une étape ultérieure.
|
||||
|
||||
La chaîne scientifique correcte est :
|
||||
Observation identity is exactly:
|
||||
|
||||
```text
|
||||
Matcher → Match Result → Geometric Verification → Track Builder v1
|
||||
→ Track Model → Sparse SfM (futur)
|
||||
```
|
||||
|
||||
Le Matcher ne produit pas les Tracks. Le Track Builder v1 les assemble à partir
|
||||
des Geometric Verification Results.
|
||||
|
||||
## Observation identity
|
||||
|
||||
Une observation est identifiée par :
|
||||
|
||||
```
|
||||
(feature_set_id, feature_index)
|
||||
```
|
||||
|
||||
- `feature_set_id` : identifiant SQLite AUTOINCREMENT du Feature Set. Le
|
||||
Feature Set porte directement `image_id` comme colonne NOT NULL FK. L'image
|
||||
est dérivable par `SELECT image_id FROM feature_sets WHERE feature_set_id=?`.
|
||||
- `feature_index` : ordinal zero-based dans le tableau de keypoints du Feature
|
||||
File, stable tant que le Feature Set existe. Un Feature Set publié est
|
||||
immutable : aucune API de production ne modifie ses colonnes après INSERT.
|
||||
`feature_set_id` identifies one immutable Feature Set.
|
||||
|
||||
L'identité `(feature_set_id, feature_index)` est suffisante. Il est inutile
|
||||
de porter `image_id` dans la table d'observations car il est dérivable via
|
||||
`feature_sets.image_id`.
|
||||
`feature_index` is the zero-based keypoint ordinal inside that immutable Feature File.
|
||||
|
||||
Note : `feature_sets` ne possède pas de colonne d'état. L'existence d'une
|
||||
ligne publiée dans la table constitue le contrat réel de disponibilité du
|
||||
Feature Set.
|
||||
The Feature Set directly owns `image_id`; image identity is therefore derivable and is not duplicated
|
||||
in Track observation identity.
|
||||
|
||||
## Scientific inputs
|
||||
## Scientific input
|
||||
|
||||
Les Tracks sont construits exclusivement à partir de :
|
||||
Track Builder consumes only completed verified geometric results selected by one exact verifier
|
||||
identity.
|
||||
|
||||
```
|
||||
Geometric Verification Result
|
||||
status == GEOMETRIC_VERIFIED (2)
|
||||
```
|
||||
|
||||
correspondant exactement au VERIFICATION_SELECTOR du Track Set.
|
||||
|
||||
Pour chaque résultat vérifié, les entrées du Match File dont le bit
|
||||
correspondant dans le masque d'inliers vaut 1 fournissent les correspondances
|
||||
valides. La chaîne de dérivation est :
|
||||
Current production lineage:
|
||||
|
||||
```text
|
||||
GVR → match_result_id
|
||||
→ candidate_pair + feature_set_id_a + feature_set_id_b
|
||||
→ Match File entry[i] = (feature_index_a, feature_index_b, distance)
|
||||
→ bit i du masque d'inliers = 1
|
||||
→ observation A: (feature_set_id_a, feature_index_a)
|
||||
→ observation B: (feature_set_id_b, feature_index_b)
|
||||
verifier_kind = FUNDAMENTAL
|
||||
verifier_version = 3
|
||||
verifier_fingerprint =
|
||||
6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c
|
||||
```
|
||||
|
||||
Un `GEOMETRIC_REJECTED` ne produit aucun track. Un Match Result non vérifié
|
||||
géométriquement ne suffit pas.
|
||||
Historical Track Sets created from Fundamental verifier v1 or v2 remain valid historical scientific
|
||||
objects.
|
||||
|
||||
## VERIFICATION_SELECTOR
|
||||
They must not be relabelled as v3.
|
||||
|
||||
Le VERIFICATION_SELECTOR définit la configuration de Geometric Verification
|
||||
éligible pour un Track Set. Il est stocké sur le Track Set et fait partie de
|
||||
son identité de reuse.
|
||||
For each selected `GEOMETRIC_VERIFIED` result, only Match File entries whose corresponding inlier-mask
|
||||
bit is one contribute observation edges.
|
||||
|
||||
```
|
||||
(
|
||||
verifier_kind INTEGER, -- ex: 1 = FUNDAMENTAL
|
||||
verifier_version INTEGER,
|
||||
parameter_fingerprint BLOB(32)
|
||||
)
|
||||
A rejected GVR contributes no Track edge.
|
||||
|
||||
## Verification selector
|
||||
|
||||
A Track Set stores the exact verifier selector:
|
||||
|
||||
```text
|
||||
verifier_kind
|
||||
verifier_version
|
||||
verifier_fingerprint
|
||||
```
|
||||
|
||||
Le Track Builder ne consomme que les GVR avec `status == GEOMETRIC_VERIFIED`
|
||||
correspondant exactement à ce tuple. Aucune sélection par timestamp, "latest"
|
||||
ou ordre d'insertion n'est permise.
|
||||
The builder never selects verification evidence using:
|
||||
|
||||
Valeur production : `(1, 1, SHA-256 de l'encodage canonique 84 octets)`.
|
||||
- timestamp;
|
||||
- "latest";
|
||||
- insertion order;
|
||||
- approximate fingerprint match.
|
||||
|
||||
## INPUT_SCOPE
|
||||
The current default producer is v3, but the Track Model remains version-independent and can store valid
|
||||
sets from explicitly supported historical selectors.
|
||||
|
||||
L'INPUT_SCOPE représente l'ensemble scientifique réel des entrées consommées
|
||||
par une Track Generation donnée. Il est distinct du VERIFICATION_SELECTOR :
|
||||
le selector dit quels GVR sont admissibles, le scope dit quels GVR ont
|
||||
effectivement été consommés.
|
||||
## Input scope
|
||||
|
||||
```
|
||||
input_scope_hash BLOB(32) -- SHA-256 canonique
|
||||
gvr_count INTEGER -- nombre de GVR consommés
|
||||
A Track Set also records the exact consumed GVR scope.
|
||||
|
||||
Canonical scope identity uses:
|
||||
|
||||
```text
|
||||
domain: L3DTSIS1
|
||||
items: geometric_verification_result_id
|
||||
order: strictly increasing
|
||||
encoding: uint64 little-endian
|
||||
digest: SHA-256
|
||||
```
|
||||
|
||||
### INPUT_SCOPE_HASH
|
||||
Conceptually:
|
||||
|
||||
| Propriété | Valeur |
|
||||
|-----------|--------|
|
||||
| Domain/version | `L3DTSIS1` (8 octets ASCII) |
|
||||
| Items | `geometric_verification_result_id` des GVR consommés |
|
||||
| Canonical ordering | IDs triés par ordre croissant |
|
||||
| Serialization | Chaque ID : 8 octets little-endian |
|
||||
| Digest | SHA-256 |
|
||||
| DB-local IDs | OUI — le reuse est scoped à une DB projet |
|
||||
| Duplicate handling | Inutile — les IDs sont uniques par construction |
|
||||
| Empty scope | Interdit — un Track Set sans GVR n'a pas de sens |
|
||||
```text
|
||||
SHA-256(L3DTSIS1 || id_0 || id_1 || ... || id_N)
|
||||
```
|
||||
|
||||
Le digest est calculé sur `L3DTSIS1` (8 octets) suivi des IDs sérialisés :
|
||||
`SHA-256(L3DTSIS1 || id_0 || id_1 || ... || id_N)` où chaque `id_i` est
|
||||
8 octets little-endian et les IDs sont triés par ordre croissant.
|
||||
The scope is Project-DB-local because SQLite GVR IDs participate directly.
|
||||
|
||||
Le `gvr_count` est stocké comme métadonnée de validation. Il permet de
|
||||
détecter un scope incomplet sans re-hasher. Il ne fait pas partie du hash
|
||||
lui-même.
|
||||
`gvr_count` is retained as validation metadata.
|
||||
|
||||
Le scope_hash est DB-local : il utilise les `geometric_verification_result_id`
|
||||
SQLite. Deux DB distinctes avec les mêmes données produiront des IDs
|
||||
différents. Le reuse est donc scoped à une seule DB projet.
|
||||
An empty scope is invalid.
|
||||
|
||||
## Track membership invariants
|
||||
|
||||
1. **Minimum structurel** : un Track contient au moins 2 observations.
|
||||
Une seule observation ne constitue aucune relation multi-view. Le futur
|
||||
Track Builder v1, la triangulation ou le Sparse SfM pourront appliquer des
|
||||
critères plus stricts. Le Model ne fixe pas de plafond de reconstruction.
|
||||
### Minimum size
|
||||
|
||||
2. **One observation per image** : un Track ne contient pas deux observations
|
||||
issues de la même image. Cette contrainte est validée par l'API lors de la
|
||||
création. Le schéma v1 ne dénormalise pas `image_id` dans
|
||||
`track_observations` ; l'API vérifie déterministement la relation via
|
||||
`feature_sets.image_id` avant publication sous `BEGIN IMMEDIATE`.
|
||||
A Track has at least two observations.
|
||||
|
||||
**SQL** : non protégé (pas de colonne `image_id` dans `track_observations`).
|
||||
**API** : validation par jointure `feature_sets.image_id` avant INSERT.
|
||||
### At most one observation per image
|
||||
|
||||
3. **Observation unique across tracks** : dans un même Track Set, une
|
||||
observation `(feature_set_id, feature_index)` n'appartient qu'à un seul
|
||||
Track.
|
||||
One Track cannot contain two observations derived from the same image.
|
||||
|
||||
**SQL** : `PRIMARY KEY(track_set_id, feature_set_id, feature_index)` sur
|
||||
`track_observations`. Le `track_set_id` est dénormalisé depuis `tracks`.
|
||||
**API** : validation que `track_set_id` correspond au `track_set_id` du
|
||||
`track_id` parent.
|
||||
This is validated through `feature_sets.image_id`.
|
||||
|
||||
4. **Feature Set existence** : chaque `feature_set_id` référencé existe dans
|
||||
la table `feature_sets`. La FK SQLite garantit la référence.
|
||||
### Observation uniqueness inside one Track Set
|
||||
|
||||
**SQL** : `REFERENCES feature_sets(feature_set_id)`.
|
||||
Within one Track Set:
|
||||
|
||||
5. **Feature index bounds** : `feature_index < feature_sets.feature_count`
|
||||
pour l'observation correspondante.
|
||||
```text
|
||||
(feature_set_id, feature_index)
|
||||
```
|
||||
|
||||
**SQL** : `CHECK(feature_index >= 0)`.
|
||||
**API** : validation de la borne supérieure via `feature_sets.feature_count`
|
||||
(SQLite CHECK ne peut pas référencer une autre table).
|
||||
belongs to at most one Track.
|
||||
|
||||
The persistence schema enforces this using the Track Set-scoped primary key.
|
||||
|
||||
### Feature Set existence
|
||||
|
||||
Every referenced Feature Set must exist.
|
||||
|
||||
### Feature index bound
|
||||
|
||||
For every observation:
|
||||
|
||||
```text
|
||||
0 <= feature_index < feature_count
|
||||
```
|
||||
|
||||
The upper bound is validated against the referenced Feature Set.
|
||||
|
||||
### Parent consistency
|
||||
|
||||
The denormalized Track Set ID carried by an observation must equal the Track Set of its parent Track.
|
||||
|
||||
## Track identity
|
||||
|
||||
Un Track persistant possède un identifiant opaque :
|
||||
Persistent Track identity is the opaque SQLite:
|
||||
|
||||
```
|
||||
track_id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(track_id > 0)
|
||||
```text
|
||||
track_id
|
||||
```
|
||||
|
||||
Il n'a pas d'identité scientifique dérivée de son contenu en v1. Les raisons :
|
||||
Track Model v1 does not define a content-derived Track hash.
|
||||
|
||||
- un hash de membership rendrait les INSERTs dépendants de l'ordre ;
|
||||
- le contenu d'un track peut être reconstruit depuis les GVR sources ;
|
||||
- un `track_id` opaque suffit pour la persistance, le référencement et la
|
||||
pagination ;
|
||||
- la corruption est détectée par cohérence interne (doublons, images
|
||||
manquantes, index hors bornes) plutôt que par re-hash.
|
||||
Reproducibility and reuse are owned by the Track Set identity, builder configuration and exact input
|
||||
scope.
|
||||
|
||||
La reproductibilité est assurée au niveau du Track Set (parent), pas du Track
|
||||
individuel.
|
||||
## Track Set identity
|
||||
|
||||
## Track Set / Generation
|
||||
A Track Set is one complete immutable generation.
|
||||
|
||||
Un **Track Set** est le parent obligatoire de tout Track persistant. Il
|
||||
représente une génération complète de Track Building.
|
||||
Its reuse identity contains:
|
||||
|
||||
Champs :
|
||||
|
||||
```
|
||||
track_set_id INTEGER PK AUTOINCREMENT
|
||||
builder_kind TEXT(1..64)
|
||||
builder_version INTEGER > 0
|
||||
parameter_fingerprint BLOB(32)
|
||||
verifier_kind INTEGER -- VERIFICATION_SELECTOR
|
||||
verifier_version INTEGER
|
||||
verifier_fingerprint BLOB(32)
|
||||
input_scope_hash BLOB(32)
|
||||
gvr_count INTEGER >= 1
|
||||
track_count INTEGER >= 0
|
||||
created_at INTEGER >= 0
|
||||
```text
|
||||
builder_kind
|
||||
builder_version
|
||||
builder_parameter_fingerprint
|
||||
verifier_kind
|
||||
verifier_version
|
||||
verifier_fingerprint
|
||||
input_scope_hash
|
||||
```
|
||||
|
||||
### Identité de reuse
|
||||
`gvr_count` validates the scope metadata but is not an independent reuse discriminator.
|
||||
|
||||
```
|
||||
(
|
||||
builder_kind,
|
||||
builder_version,
|
||||
parameter_fingerprint,
|
||||
verifier_kind,
|
||||
verifier_version,
|
||||
verifier_fingerprint,
|
||||
input_scope_hash
|
||||
)
|
||||
```
|
||||
`INSERT OR REPLACE` is forbidden.
|
||||
|
||||
`gvr_count` est stocké comme métadonnée de validation mais ne fait pas
|
||||
partie de l'identité de reuse. Le même `input_scope_hash` avec un `gvr_count`
|
||||
différent indiquerait une corruption (hash cohérent mais nombre de sources
|
||||
incohérent).
|
||||
An exact existing immutable set is reused.
|
||||
|
||||
Un set existant avec cette identité exacte est réutilisé. `INSERT OR REPLACE`
|
||||
est interdit.
|
||||
A scientifically different scope/configuration creates a new Track Set.
|
||||
|
||||
### Immutabilité
|
||||
## Immutability
|
||||
|
||||
Un Track Set publié est **immutable**. Aucune opération d'append, remove ou
|
||||
merge n'est permise sur un track ou un set existant.
|
||||
A published Track Set is immutable.
|
||||
|
||||
L'invalidation scientifique (nouvelle entrée, nouveau scope, nouvelle
|
||||
configuration) produit un nouveau Track Set. Le set précédent reste intact.
|
||||
No production operation:
|
||||
|
||||
La suppression référentielle utilise `ON DELETE CASCADE` : supprimer un
|
||||
Track Set supprime ses tracks et observations.
|
||||
- appends to it;
|
||||
- removes observations;
|
||||
- merges existing Tracks;
|
||||
- rewrites memberships;
|
||||
- updates it to a newer verifier version.
|
||||
|
||||
### Justification
|
||||
New evidence creates a new generation.
|
||||
|
||||
- chaque rebuild crée un nouveau set, les anciens restent intacts ;
|
||||
- plusieurs configurations peuvent coexister (expérimentation) ;
|
||||
- l'invalidation est simple : supprimer un set supprime ses tracks via
|
||||
CASCADE ;
|
||||
- la reproductibilité est portée par le fingerprint et le scope_hash ;
|
||||
- pas d'UPDATE/INSERT/MERGE sur des tracks existants ;
|
||||
- cohérent avec tous les résultats publiés existants (Feature Sets, Match
|
||||
Results, GVRs) qui sont immutables après publication.
|
||||
|
||||
Le Track Builder v1 construit en mémoire, puis publie un set complet
|
||||
dans une transaction. Aucun track n'est visible avant que le set entier soit
|
||||
validé.
|
||||
|
||||
## Immutability / incrementality
|
||||
|
||||
Un Track publié dans un set est **immutable**.
|
||||
|
||||
L'incrémentalité est gérée par création de nouveaux sets :
|
||||
|
||||
1. nouvelles images → nouveaux Match Results → nouveaux GVR → nouveau
|
||||
Track Set ;
|
||||
2. le set précédent reste valide et consultable ;
|
||||
3. le futur Sparse SfM choisira quel set consommer.
|
||||
|
||||
Cette approche est cohérente avec la philosophie Lardon3D :
|
||||
|
||||
- résultats atomiques ;
|
||||
- pas de destruction silencieuse ;
|
||||
- reprise à frontière connue ;
|
||||
- conservation de l'historique.
|
||||
Historical generations remain queryable until explicitly deleted.
|
||||
|
||||
## Persistence
|
||||
|
||||
### Conceptual schema
|
||||
Track storage was introduced by Project DB v14.
|
||||
|
||||
```sql
|
||||
CREATE TABLE track_sets(
|
||||
track_set_id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
CHECK(track_set_id > 0),
|
||||
builder_kind TEXT NOT NULL
|
||||
CHECK(length(builder_kind) > 0 AND length(builder_kind) <= 64),
|
||||
builder_version INTEGER NOT NULL CHECK(builder_version > 0),
|
||||
parameter_fingerprint BLOB NOT NULL
|
||||
CHECK(length(parameter_fingerprint) = 32),
|
||||
verifier_kind INTEGER NOT NULL CHECK(verifier_kind > 0),
|
||||
verifier_version INTEGER NOT NULL CHECK(verifier_version > 0),
|
||||
verifier_fingerprint BLOB NOT NULL
|
||||
CHECK(length(verifier_fingerprint) = 32),
|
||||
input_scope_hash BLOB NOT NULL
|
||||
CHECK(length(input_scope_hash) = 32),
|
||||
gvr_count INTEGER NOT NULL CHECK(gvr_count >= 1),
|
||||
track_count INTEGER NOT NULL CHECK(track_count >= 0),
|
||||
created_at INTEGER NOT NULL CHECK(created_at >= 0),
|
||||
UNIQUE(builder_kind, builder_version, parameter_fingerprint,
|
||||
verifier_kind, verifier_version, verifier_fingerprint,
|
||||
input_scope_hash)
|
||||
);
|
||||
Durable Track Builder Task payload persistence was added in Project DB v15.
|
||||
|
||||
CREATE TABLE tracks(
|
||||
track_id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(track_id > 0),
|
||||
track_set_id INTEGER NOT NULL
|
||||
REFERENCES track_sets(track_set_id) ON DELETE CASCADE,
|
||||
observation_count INTEGER NOT NULL CHECK(observation_count >= 2)
|
||||
);
|
||||
Later schema migrations through v25 do not reinterpret Track Model v1.
|
||||
|
||||
CREATE INDEX tracks_set_idx
|
||||
ON tracks(track_set_id, track_id);
|
||||
Conceptual tables:
|
||||
|
||||
CREATE TABLE track_observations(
|
||||
track_set_id INTEGER NOT NULL,
|
||||
track_id INTEGER NOT NULL
|
||||
REFERENCES tracks(track_id) ON DELETE CASCADE,
|
||||
feature_set_id INTEGER NOT NULL
|
||||
REFERENCES feature_sets(feature_set_id),
|
||||
feature_index INTEGER NOT NULL CHECK(feature_index >= 0),
|
||||
position_in_track INTEGER NOT NULL CHECK(position_in_track >= 0),
|
||||
PRIMARY KEY(track_set_id, feature_set_id, feature_index),
|
||||
UNIQUE(track_id, position_in_track)
|
||||
);
|
||||
|
||||
CREATE INDEX track_observations_lookup_idx
|
||||
ON track_observations(feature_set_id, feature_index, track_set_id);
|
||||
```text
|
||||
track_sets
|
||||
tracks
|
||||
track_observations
|
||||
```
|
||||
|
||||
### Schema invariants
|
||||
Publication is atomic for the complete Track Set under one transaction.
|
||||
|
||||
**SQL-enforced :**
|
||||
No Track from that set becomes visible before the complete generation validates and commits.
|
||||
|
||||
- `track_observations.PRIMARY KEY(track_set_id, feature_set_id, feature_index)`
|
||||
: dans un Track Set donné, une observation n'apparaît qu'une fois. Cela
|
||||
garantit qu'une observation scientifique appartient à au plus un Track dans
|
||||
ce set.
|
||||
- `REFERENCES tracks(track_id) ON DELETE CASCADE` : l'observation appartient
|
||||
à un track existant ; supprimer le track supprime l'observation.
|
||||
- `REFERENCES feature_sets(feature_set_id)` : le Feature Set existe.
|
||||
- `CHECK(observation_count >= 2)` : minimum structurel.
|
||||
- `UNIQUE(builder_kind, builder_version, parameter_fingerprint,
|
||||
verifier_kind, verifier_version, verifier_fingerprint,
|
||||
input_scope_hash)` sur `track_sets` : identité de reuse, empêche les
|
||||
doublons de set pour une même configuration et un même scope.
|
||||
- `ON DELETE CASCADE` depuis `track_sets` : supprimer un set supprime tout.
|
||||
- `CHECK(feature_index >= 0)` : borne inférieure de l'index.
|
||||
- `UNIQUE(track_id, position_in_track)` : chaque position dans un track est
|
||||
unique. L'ordre est déterminé par le Track Builder lors de la publication.
|
||||
Rollback leaves no partial Track Set.
|
||||
|
||||
**API-enforced :**
|
||||
## Ordering
|
||||
|
||||
- `track_set_id` dans `track_observations` correspond au `track_set_id` du
|
||||
`track_id` parent. Le schéma ne comporte pas de FK composite (aucun
|
||||
précédent dans le codebase). L'API valide cette cohérence avant INSERT sous
|
||||
`BEGIN IMMEDIATE`.
|
||||
- Une seule observation par image par track. L'API valide via jointure à
|
||||
`feature_sets.image_id`.
|
||||
- `feature_index < feature_sets.feature_count`. L'API valide la borne
|
||||
supérieure.
|
||||
- `observation_count` cohérent avec le nombre réel d'observations insérées.
|
||||
- `track_count` cohérent avec le nombre réel de tracks insérés.
|
||||
- `position_in_track` contigu à partir de 0 pour chaque track.
|
||||
Track Builder publishes deterministic canonical order.
|
||||
|
||||
### Note sur la dénormalisation
|
||||
`position_in_track` is contiguous from zero.
|
||||
|
||||
`track_set_id` dans `track_observations` dénormalise une clé grandparent,
|
||||
après le même pattern utilisé par `visual_index_memberships.visual_index_id`.
|
||||
Le pattern parent-key-in-UNIQUE est déjà répandu dans le codebase. La cohérence
|
||||
repose sur le chemin d'écriture unique du Track Builder et la validation API
|
||||
sous transaction.
|
||||
The exact builder contract owns edge ordering and conflict resolution; Track Model only persists the
|
||||
validated result.
|
||||
|
||||
`track_observations.track_set_id` n'a pas de FK directe vers `track_sets`
|
||||
pour éviter un second chemin CASCADE depuis `track_sets` vers
|
||||
`track_observations` (le premier chemin passe par `tracks`). La cohérence
|
||||
est garantie par l'API sous `BEGIN IMMEDIATE`.
|
||||
No hash-table iteration order may define persistent scientific ordering.
|
||||
|
||||
## Provenance
|
||||
## Deletion
|
||||
|
||||
### Track Set provenance
|
||||
Deleting a Track Set cascades to its Tracks and observations.
|
||||
|
||||
Chaque Track Set conserve :
|
||||
A Feature Set referenced by a Track observation cannot be silently removed while the reference remains
|
||||
valid.
|
||||
|
||||
- `builder_kind`, `builder_version`, `parameter_fingerprint` : configuration
|
||||
du Track Builder ;
|
||||
- `verifier_kind`, `verifier_version`, `verifier_fingerprint` : configuration
|
||||
du Geometric Verifier consommé ;
|
||||
- `input_scope_hash`, `gvr_count` : ensemble réel des GVR consommés.
|
||||
Deletion semantics do not mutate other immutable Track Sets.
|
||||
|
||||
Ces champs suffisent pour identifier la configuration scientifique complète
|
||||
ayant produit le set.
|
||||
## Pagination and resource bounds
|
||||
|
||||
### Edge provenance (deferred)
|
||||
Track Model storage APIs are paged.
|
||||
|
||||
En v1, la provenance détaillée (quels GVR spécifiques ont contribué à quel
|
||||
track individuel) n'est pas persistée. Les raisons :
|
||||
The model does not impose an arbitrary scientific maximum Track length below the number of images that
|
||||
could legitimately observe the same point.
|
||||
|
||||
- elle peut être reconstruite en comparant les memberships du set aux GVR
|
||||
disponibles ;
|
||||
- une table `track_set_sources` volumineuse complexifie la DB sans bénéfice
|
||||
immédiat ;
|
||||
- une future version du Track Builder pourra l'ajouter dans une migration
|
||||
ultérieure.
|
||||
It does not materialize a dense image-by-image covisibility matrix.
|
||||
|
||||
## Invalidation
|
||||
Loading one Track loads that Track's observations; project-wide traversal remains paged.
|
||||
|
||||
### Invalidation scientifique
|
||||
|
||||
Un nouveau scope, une nouvelle configuration de verifier ou un nouveau
|
||||
builder produit un **nouveau** Track Set avec une identité différente. Le set
|
||||
précédent reste intact et consultable. Aucune mutation silencieuse n'est
|
||||
permise.
|
||||
|
||||
### Suppression référentielle
|
||||
|
||||
`ON DELETE CASCADE` s'applique :
|
||||
|
||||
- `track_sets` → `tracks` → `track_observations` : supprimer un set supprime
|
||||
tous ses tracks et observations ;
|
||||
- `feature_sets` → (pas de CASCADE vers `track_observations`) : la FK utilise
|
||||
le comportement par défaut (NO ACTION). Supprimer un Feature Set référencé
|
||||
par une observation est interdit tant que l'observation existe.
|
||||
|
||||
## Atomic publication
|
||||
|
||||
L'unité persistante est le Track Set complet. La publication est une seule
|
||||
transaction `BEGIN IMMEDIATE` contenant l'INSERT du set, de tous ses tracks
|
||||
et de toutes ses observations.
|
||||
|
||||
- aucun track n'est visible avant le COMMIT du set entier ;
|
||||
- un rollback ne laisse aucune ligne partielle ;
|
||||
- le `created_at` du set est le timestamp de la transaction ;
|
||||
- le `track_count` et `gvr_count` sont validés contre les INSERTs réels.
|
||||
|
||||
Le Track Builder v1 utilise le Task Runtime pour le checkpoint/reprise et le
|
||||
Resource Governor pour l'admission. Le Model ne contient aucune
|
||||
logique d'exécution.
|
||||
|
||||
## Resource bounds
|
||||
|
||||
- **Pas de plafond de longueur arbitraire** : le Model ne fixe pas de
|
||||
maximum sur le nombre d'observations par Track. Un projet avec N images
|
||||
peut produire des tracks de longueur jusqu'à N.
|
||||
- **Lecture paginée** : `list_tracks` et `list_track_sets` utilisent une
|
||||
page de 64 entrées avec curseur.
|
||||
- **Chargement borné** : load track by id charge les observations du track ;
|
||||
la taille est bornée naturellement par le nombre d'images dans le scope.
|
||||
- **Pas de chargement complet du graphe** : aucune API ne charge tous les
|
||||
tracks et toutes les observations d'un projet en une seule fois.
|
||||
- **Pas de matrice dense** : aucune matrice de co-visibilité N×N n'est
|
||||
matérialisée par le Model.
|
||||
Execution-memory strategy belongs to Track Builder, not Track Model.
|
||||
|
||||
## Corruption handling
|
||||
|
||||
Le loader doit détecter :
|
||||
A loader returns corruption rather than partial best-effort data when it detects conditions such as:
|
||||
|
||||
- track absent (`track_id` référencé mais inexistant) ;
|
||||
- observation invalide (`feature_set_id` inexistant) ;
|
||||
- duplicate observation dans un même track set ;
|
||||
- deux observations de la même image dans un même track ;
|
||||
- `feature_index` hors bornes du Feature Set ;
|
||||
- `observation_count` incohérent avec le nombre réel d'observations ;
|
||||
- `track_set_id` dans `track_observations` ne correspondant pas au
|
||||
`track_set_id` du `track_id` parent ;
|
||||
- `track_set` parent absent.
|
||||
- missing parent Track or Track Set;
|
||||
- missing Feature Set;
|
||||
- duplicate observation in one Track Set;
|
||||
- repeated image inside one Track;
|
||||
- out-of-range feature index;
|
||||
- inconsistent observation count;
|
||||
- inconsistent Track count;
|
||||
- inconsistent denormalized Track Set ID;
|
||||
- invalid/non-contiguous position ordering.
|
||||
|
||||
Toute corruption retourne `CORRUPT` sans résultat partiel.
|
||||
No loader repairs scientific identity in place.
|
||||
|
||||
## API
|
||||
## Provenance
|
||||
|
||||
L'API publique implémente :
|
||||
Track Set provenance includes:
|
||||
|
||||
- `lardon3d_project_db_create_track_set()` — INSERT set + ses tracks +
|
||||
observations dans une seule transaction `BEGIN IMMEDIATE`.
|
||||
- `lardon3d_project_db_load_track_set()` — SELECT par ID.
|
||||
- `lardon3d_project_db_find_track_set()` — SELECT par identité exacte.
|
||||
- `lardon3d_project_db_list_track_sets()` — SELECT paginé ORDER BY id,
|
||||
page 64.
|
||||
- `lardon3d_project_db_load_track()` — SELECT par ID avec observations.
|
||||
- `lardon3d_project_db_list_tracks()` — SELECT par set, paginé ORDER BY
|
||||
id, page 64.
|
||||
- `lardon3d_project_db_find_track_by_observation()` — recherche par
|
||||
`(feature_set_id, feature_index)` dans un set donné.
|
||||
```text
|
||||
builder identity
|
||||
verifier selector
|
||||
input scope hash
|
||||
gvr count
|
||||
```
|
||||
|
||||
La création valide en C : existence des Feature Sets, bornes des
|
||||
`feature_index`, unicité des observations, unicité image par track,
|
||||
`observation_count` cohérent, `track_set_id` cohérent. L'INSERT est
|
||||
transactionnel.
|
||||
Detailed per-edge provenance is not persisted by Track Model v1.
|
||||
|
||||
## Explicitly out of scope
|
||||
Adding such provenance later requires an explicit version/schema decision if persistent representation
|
||||
changes.
|
||||
|
||||
- Track Builder algorithmique (union-find, connected components) ;
|
||||
- triangulation ;
|
||||
- coordonnées 3D ;
|
||||
- Essential matrix ;
|
||||
- camera pose ;
|
||||
- bundle adjustment ;
|
||||
- sparse reconstruction / Sparse SfM ;
|
||||
- reprojection error ;
|
||||
- dense reconstruction ;
|
||||
- Track optimization ou merge ;
|
||||
- mutation de tracks existants ;
|
||||
- co-visibilité (matrice ou calcul) ;
|
||||
- sélection par timestamp ou "latest".
|
||||
## Current production verifier lineage
|
||||
|
||||
## Track rejected state
|
||||
Fundamental verifier v1 and v2 are historical scientific identities.
|
||||
|
||||
Le Model v1 ne persiste pas d'état Track rejected. Le Model représente des
|
||||
Tracks structurellement valides (≥ 2 observations, cohérents). Le Track Builder
|
||||
v1 décide quels candidats publier. Les candidats non publiés n'existent pas dans
|
||||
le Model ; cette séparation reste la frontière scientifique figée.
|
||||
Current new production verification uses Fundamental v3.
|
||||
|
||||
## Versioning
|
||||
V3 adds bounded preflight rejection before the unchanged scientific estimator path and has its own
|
||||
fingerprint.
|
||||
|
||||
Project DB v14 introduced the Track storage and v15 adds only durable Track
|
||||
Builder task payload persistence. `builder_version` et `verifier_version`
|
||||
décrivent indépendamment les contrats scientifiques.
|
||||
Changer un algorithme n'impose une migration DB que si la représentation
|
||||
persistante change.
|
||||
Track Builder consumes only exact matching GVR identities.
|
||||
|
||||
Therefore:
|
||||
|
||||
```text
|
||||
HISTORICAL_TRACK_SET_VERIFIER_V1=VALID
|
||||
HISTORICAL_TRACK_SET_VERIFIER_V2=VALID
|
||||
CURRENT_TRACK_SET_VERIFIER_V3=PRODUCTION
|
||||
```
|
||||
|
||||
No historical Track Set is upgraded in place.
|
||||
|
||||
## Real S21 evidence
|
||||
|
||||
The retained S21 Track proof is:
|
||||
|
||||
```text
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
|
||||
Track Set observations = 2,495,768
|
||||
Tracks = 912,447
|
||||
minimum Track length = 2
|
||||
maximum Track length = 42
|
||||
mean Track length = 2.7352470883240341
|
||||
```
|
||||
|
||||
Retained digest:
|
||||
|
||||
```text
|
||||
c30eba192627bf73eaf21ff30d81038d8cc6bbf36a69226f88cdc8c37f7d74a1
|
||||
```
|
||||
|
||||
The compact memory model supersedes the older historical 18.204 GiB envelope.
|
||||
|
||||
That checkpoint did not execute real Sparse SfM.
|
||||
|
||||
## Real A6000 evidence
|
||||
|
||||
The current retained A6000 checkpoint is:
|
||||
|
||||
```text
|
||||
real-a6000-pre-sfm-2026-09-02
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
Track output:
|
||||
|
||||
```text
|
||||
Track Set 1
|
||||
Tracks 130,714
|
||||
Track observations 318,944
|
||||
duplicate obs 0
|
||||
repeated-image 0
|
||||
orphan obs 0
|
||||
```
|
||||
|
||||
The upstream v3 GV scope contained:
|
||||
|
||||
```text
|
||||
Applicable GVRs 37,805
|
||||
Verified GVRs 10,952
|
||||
Rejected GVRs 26,853
|
||||
```
|
||||
|
||||
Restart traversed the retained scope and reused the same Track Set without creating a duplicate
|
||||
scientific generation.
|
||||
|
||||
No Sparse SfM Task or Sparse Reconstruction was created.
|
||||
|
||||
## Sparse SfM relationship
|
||||
|
||||
Track Model does not perform Sparse SfM.
|
||||
|
||||
Sparse SfM capability is nevertheless implemented through Gate G.
|
||||
|
||||
Correct current statement:
|
||||
|
||||
```text
|
||||
TRACK_MODEL_OUTPUT=AVAILABLE
|
||||
SPARSE_SFM_IMPLEMENTATION=AVAILABLE
|
||||
REAL_HISTORICAL_CAMPAIGN_SPARSE_SFM=BLOCKED_BY_KNOWN_CALIBRATION_DATA
|
||||
```
|
||||
|
||||
These are separate lifecycle facts.
|
||||
|
||||
## Out of scope
|
||||
|
||||
Track Model v1 does not own:
|
||||
|
||||
- Track Builder union/find algorithm;
|
||||
- Fundamental estimation;
|
||||
- Essential estimation;
|
||||
- camera pose;
|
||||
- triangulation;
|
||||
- 3D coordinates;
|
||||
- reprojection error;
|
||||
- Bundle Adjustment;
|
||||
- dense reconstruction;
|
||||
- metric scale;
|
||||
- Track mutation/merge;
|
||||
- selection by "latest".
|
||||
|
||||
## Summary
|
||||
|
||||
```text
|
||||
TRACK_MODEL_V1=FROZEN
|
||||
TRACK_BUILDER_V1=PASS/FROZEN
|
||||
|
||||
CURRENT_PRODUCTION_VERIFIER=FUNDAMENTAL_V3
|
||||
CURRENT_VERIFIER_FINGERPRINT=6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c
|
||||
|
||||
PROJECT_DB_TRACK_MODEL=v14
|
||||
PROJECT_DB_TRACK_TASK=v15
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
|
||||
SPARSE_SFM_CAPABILITY=IMPLEMENTED_THROUGH_GATE_G
|
||||
REAL_S21_SPARSE_SFM=NOT_EXECUTED
|
||||
REAL_A6000_SPARSE_SFM=NOT_EXECUTED
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,207 +1,430 @@
|
|||
# Visual Index v1
|
||||
|
||||
## Problème et frontière
|
||||
## Status
|
||||
|
||||
Le Visual Index transforme une collection homogène de `FeatureSet` READY en
|
||||
candidats de recherche. Il consomme exclusivement `feature_set_id` et les
|
||||
descripteurs ORB lus par le Feature Reader. Il ne fait ni matching final, ni
|
||||
ratio test, ni vérification géométrique.
|
||||
```text
|
||||
VISUAL_INDEX_V1=IMPLEMENTED
|
||||
VISUAL_INDEX_KIND=orb-lsh
|
||||
VISUAL_INDEX_VERSION=1
|
||||
|
||||
## Choix algorithmique
|
||||
CANDIDATE_PAIR=IMPLEMENTED
|
||||
MATCHER=IMPLEMENTED
|
||||
|
||||
La v1 utilise un LSH binaire déterministe à six tables. Chaque table extrait
|
||||
24 positions distinctes des 256 bits ORB. La position v1 est
|
||||
`(41*table + 11*bit) mod 256`; 11 étant premier avec 256, les 24 positions
|
||||
d'une table sont distinctes. Une clé est `(table_id, key24)`. Des descripteurs proches en
|
||||
Hamming ont une probabilité élevée de collision dans au moins une table, sans
|
||||
conversion flottante.
|
||||
VISUAL_INDEX_GPU=REJECTED_WITH_MEASURED_REASON
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
Alternatives évaluées :
|
||||
Visual Index turns a homogeneous collection of immutable Feature Sets into bounded image-retrieval
|
||||
candidates.
|
||||
|
||||
- le hash exact est très compact et déterministe, mais son rappel s'effondre
|
||||
dès qu'un descriptor varie d'un bit ;
|
||||
- le multi-index hashing avec sous-chaînes et multiprobes offre des garanties
|
||||
Hamming intéressantes, mais le nombre de postings/probes nécessaire au
|
||||
rappel utile d'ORB est trop élevé pour une v1 bornée ;
|
||||
- FLANN-LSH masque son format, ses allocations et sa stabilité de
|
||||
sérialisation, ce qui nuit à la reprise et à l'audit ;
|
||||
- BoW/IVF donne un bon retrieval image, mais impose vocabulaire, entraînement,
|
||||
identité et politique de mise à jour avant de pouvoir être incrémental ;
|
||||
- HNSW et FAISS ajoutent une dépendance et un état mutable complexes sans
|
||||
avantage décisif à quelques milliers d'images.
|
||||
It is a retrieval stage, not a Matcher and not a geometric verifier.
|
||||
|
||||
Ce LSH n'est pas un matcher. Il privilégie une base déterministe, segmentable
|
||||
et contrôlable. Une évolution de la sélection de bits exige une nouvelle
|
||||
`visual_index_version`.
|
||||
Current downstream consumers are implemented:
|
||||
|
||||
## Identité et configuration
|
||||
```text
|
||||
Feature Store
|
||||
-> Visual Index
|
||||
-> Candidate Pair Generator
|
||||
-> Matcher
|
||||
-> Geometric Verification
|
||||
-> Tracks
|
||||
```
|
||||
|
||||
Le kind est `orb-lsh`, version 1. Un index contient exclusivement des Feature
|
||||
Sets de même `descriptor_type`, dimension, `extractor_kind`, version et
|
||||
`parameter_fingerprint`. Sa configuration v1 contient :
|
||||
Older text describing Candidate Pair or Matcher as future consumers is historical design context and is
|
||||
not current status.
|
||||
|
||||
- `table_count=6` ;
|
||||
- `key_bits=24` ;
|
||||
- `max_features_per_set` entre 1 et 1024, défaut 512 ;
|
||||
- `max_bucket_postings` entre 1 et 4096, défaut 256 ;
|
||||
- `max_segments=256` ;
|
||||
- `max_feature_sets_per_segment=16`.
|
||||
## Algorithm
|
||||
|
||||
Le fingerprint de paramètres est SHA-256 des 32 octets canoniques
|
||||
`L3DVICF1`, version et cinq entiers little-endian. Aucun padding, JSON, locale
|
||||
ou endianness hôte n'intervient. `visual_index_id` est une identité SQLite
|
||||
`AUTOINCREMENT`, jamais réutilisée après publication.
|
||||
Visual Index v1 uses deterministic binary LSH over ORB descriptors.
|
||||
|
||||
## Échantillonnage
|
||||
It uses six tables.
|
||||
|
||||
Au plus `max_features_per_set` features sont indexées. La sélection v1 retient
|
||||
le préfixe de `feature_index` croissant. Les postings conservent l'indice
|
||||
original. Un Feature Set vide est membre valide sans posting. Le build Task
|
||||
peut lire en parallèle jusqu'à douze Feature Files, avec un reader et une
|
||||
tranche de 256 descripteurs privés par participant effectivement admis. Chaque
|
||||
Feature Set écrit dans une tranche privée de la capacité de postings déjà
|
||||
réservée pour le segment ; le propriétaire compacte ensuite les tranches dans
|
||||
l'ordre de sélection et applique seul l'ordre total persistant.
|
||||
Each table selects 24 distinct positions from the 256 ORB bits.
|
||||
|
||||
## Segments introduits en Project Database v6, conservés en v7
|
||||
The frozen v1 position rule is:
|
||||
|
||||
Un index logique possède des segments immuables READY. Chaque update publie un
|
||||
segment de un à seize nouveaux Feature Sets, puis ajoute atomiquement segments
|
||||
et memberships. `UNIQUE(visual_index_id,feature_set_id)` assure l'idempotence.
|
||||
Une recherche copie au début la liste bornée des segments READY, relâche le
|
||||
mutex DB, puis lit ce snapshot. Un segment commité au milieu sera visible à la
|
||||
requête suivante.
|
||||
```text
|
||||
position = (41 * table + 11 * bit) mod 256
|
||||
```
|
||||
|
||||
SQLite conserve les tables `visual_indexes`, `visual_index_segments`,
|
||||
`visual_index_memberships` et `visual_index_update_tasks`. Les gros postings
|
||||
restent hors DB. La configuration et chaque membership sont immuables. La
|
||||
compaction est `NOT_YET_WIRED`; au-delà de 256 segments une update est refusée avec
|
||||
`LARDON3D_VISUAL_INDEX_LIMIT`. Avec seize membres par segment, la capacité v1 est donc
|
||||
exactement 4096 Feature Sets par index. Le refus ne publie ni segment ni membership et
|
||||
l'index existant reste requêtable.
|
||||
A posting key is:
|
||||
|
||||
Le DDL v6 exact est `schema_visual_v6` dans `src/project_db.c`. Il impose
|
||||
`AUTOINCREMENT` aux index/segments, les uniques
|
||||
`(visual_index_id,generation)`, `(visual_index_id,sha256)` et
|
||||
`(visual_index_id,feature_set_id)`, ainsi que les FKs vers index, Feature Set,
|
||||
segment et tâche. Les CHECKS bornent tables 1..32, bits 8..32, sampling
|
||||
1..1024, bucket 1..4096, membres segment 1..16 et durabilité 0..1. La migration
|
||||
entière reste sous `BEGIN IMMEDIATE` et possède une injection de rollback v6.
|
||||
```text
|
||||
(table_id, key24)
|
||||
```
|
||||
|
||||
Changing this bit-selection policy requires a new Visual Index scientific version.
|
||||
|
||||
## Identity and configuration
|
||||
|
||||
Current kind/version:
|
||||
|
||||
```text
|
||||
orb-lsh / 1
|
||||
```
|
||||
|
||||
One index contains Feature Sets with homogeneous:
|
||||
|
||||
- descriptor type;
|
||||
- descriptor dimension;
|
||||
- extractor kind;
|
||||
- extractor version;
|
||||
- extractor parameter fingerprint.
|
||||
|
||||
Frozen v1 configuration contains:
|
||||
|
||||
```text
|
||||
table_count = 6
|
||||
key_bits = 24
|
||||
max_features_per_set = 1..1024, default 512
|
||||
max_bucket_postings = 1..4096, default 256
|
||||
max_segments = 256
|
||||
max_feature_sets_per_segment = 16
|
||||
```
|
||||
|
||||
The canonical parameter fingerprint uses domain:
|
||||
|
||||
```text
|
||||
L3DVICF1
|
||||
```
|
||||
|
||||
with explicit little-endian fields.
|
||||
|
||||
No C struct padding, locale or host endianness enters the fingerprint.
|
||||
|
||||
## Sampling
|
||||
|
||||
At most `max_features_per_set` Feature entries are indexed.
|
||||
|
||||
V1 selects the increasing `feature_index` prefix.
|
||||
|
||||
Postings retain the original Feature index.
|
||||
|
||||
An empty Feature Set is a valid member and contributes no posting.
|
||||
|
||||
## Segment persistence
|
||||
|
||||
Project DB v6 introduced Visual Index persistence. v7 retained the model.
|
||||
|
||||
Later schema versions through v25 do not reinterpret Visual Index v1.
|
||||
|
||||
A logical index owns immutable READY segments.
|
||||
|
||||
One update publishes one segment containing between one and sixteen new Feature Sets.
|
||||
|
||||
Membership uniqueness is enforced on:
|
||||
|
||||
```text
|
||||
(visual_index_id, feature_set_id)
|
||||
```
|
||||
|
||||
A query snapshots the bounded READY segment list before asset reads.
|
||||
|
||||
A segment committed after that snapshot is visible to the next query, not retroactively injected into
|
||||
the running query.
|
||||
|
||||
## Capacity
|
||||
|
||||
V1 currently allows:
|
||||
|
||||
```text
|
||||
max_segments = 256
|
||||
max_feature_sets_per_segment = 16
|
||||
```
|
||||
|
||||
Therefore one v1 index can contain exactly up to:
|
||||
|
||||
```text
|
||||
4096 Feature Sets
|
||||
```
|
||||
|
||||
before another update returns the Visual Index limit.
|
||||
|
||||
This is an index-v1 capacity bound, not a project-wide image-count limit.
|
||||
|
||||
Compaction/base-delta redesign remains deferred.
|
||||
|
||||
## Segment File v1
|
||||
|
||||
Le fichier est little-endian et ne sérialise aucune structure C. Layout :
|
||||
Segment File v1 is explicitly little-endian and does not serialize C structs.
|
||||
|
||||
| Offset | Taille | Champ |
|
||||
|---:|---:|---|
|
||||
| 0 | 8 | magic `L3DVIDX\0` |
|
||||
| 8 | 4 | format version 1 |
|
||||
| 12 | 4 | header size 128 |
|
||||
| 16 | 4 | table count |
|
||||
| 20 | 4 | key bits |
|
||||
| 24 | 8 | posting count |
|
||||
| 32 | 8 | member count |
|
||||
| 40 | 8 | postings offset, 128 |
|
||||
| 48 | 8 | total size |
|
||||
| 56 | 32 | index parameter fingerprint |
|
||||
| 88 | 32 | feature parameter fingerprint |
|
||||
| 120 | 8 | réservés, zéro |
|
||||
Magic:
|
||||
|
||||
Chaque posting fait 24 octets : `table_id:u32`, `key24:u32`,
|
||||
`feature_set_id:u64`, `feature_index:u32`, réservé zéro `u32`. L'ordre est
|
||||
`table_id`, clé, Feature Set, feature index. Le fichier exact est SHA-256 et
|
||||
vit sous `assets/visual-index/<2 hex>/<sha256 lowercase>`.
|
||||
```text
|
||||
L3DVIDX\0
|
||||
```
|
||||
|
||||
Publication : temporaire local, écriture, `fsync`, hash, `link` sans
|
||||
écrasement, validation d'une adoption concurrente, `fsync` du répertoire, puis
|
||||
transaction DB. Un échec après publication peut laisser un orphelin mais jamais
|
||||
un segment READY partiel. La durabilité distingue `DURABLE` et
|
||||
`PUBLISHED_NOT_DURABLE`.
|
||||
A posting contains:
|
||||
|
||||
Le reader vérifie le SHA avant le parsing. Un fichier au SHA et aux métadonnées cohérents
|
||||
mais portant une version future produit `UNSUPPORTED_VERSION`; les comptes et produits
|
||||
d'offset invalides produisent `CORRUPT` avant allocation, conversion ou lecture de posting.
|
||||
```text
|
||||
table_id:u32
|
||||
key24:u32
|
||||
feature_set_id:u64
|
||||
feature_index:u32
|
||||
reserved_zero:u32
|
||||
```
|
||||
|
||||
## Recherche, score et bornes
|
||||
Canonical persistent ordering is:
|
||||
|
||||
L'API est centrée sur `(visual_index_id, query_feature_set_id)`. Elle accepte
|
||||
`ANY_SCANSET`, `SAME_SCANSET` ou `OTHER_SCANSETS`, l'exclusion du même asset,
|
||||
un minimum de preuves et `top_k` entre 1 et 256. Elle ne retourne jamais le
|
||||
Feature Set ni l'image de requête.
|
||||
```text
|
||||
table_id
|
||||
key24
|
||||
feature_set_id
|
||||
feature_index
|
||||
```
|
||||
|
||||
Une preuve est un `feature_index` de requête distinct ayant au moins une
|
||||
collision avec le candidat. Plusieurs tables, postings ou descriptors du
|
||||
candidat ne multiplient pas cette preuve. Le score final vaut
|
||||
`evidence_count / sampled_query_feature_count` dans `[0,1]`. Le volume du
|
||||
candidat ne peut donc pas augmenter le score sans preuve distincte. L'ordre est
|
||||
score décroissant, preuves décroissantes, `image_id`, puis `feature_set_id`.
|
||||
The complete Segment File is content-addressed by SHA-256 under the Visual Index asset tree.
|
||||
|
||||
La burstiness est bornée par une contribution maximum par feature de requête
|
||||
et candidat. Une première passe additionne la fréquence d'un bucket sur tous
|
||||
les segments du snapshot. Au-delà de `max_bucket_postings`, il est ignoré : un motif
|
||||
très commun ne peut ni allouer une liste géante ni dominer le score. Le reader
|
||||
lit au plus 256 postings par appel. L'accumulateur contient au plus 4096
|
||||
candidats ; les nouveaux candidats sont ignorés après saturation, de manière
|
||||
déterministe par l'ordre des postings. Aucun cache global n'existe et un seul
|
||||
segment est ouvert à la fois.
|
||||
## Publication
|
||||
|
||||
Deux updates concurrentes peuvent sélectionner le même lot et construire le même asset.
|
||||
La transaction SQLite et les contraintes uniques ne laissent publier qu'un segment et
|
||||
un membership par Feature Set; l'autre update échoue/rejoue en no-op. Une query prend son
|
||||
snapshot de métadonnées avant les lectures et ouvre/ferme un seul segment à la fois, y
|
||||
compris avec 250 à 256 segments : le nombre de descripteurs de fichier reste borné.
|
||||
Publication follows the normal immutable-asset pattern:
|
||||
|
||||
## Tâche, reprise et ressources
|
||||
```text
|
||||
local temp
|
||||
-> write
|
||||
-> fsync
|
||||
-> hash
|
||||
-> no-overwrite publication/adoption validation
|
||||
-> fsync directory
|
||||
-> short Project DB transaction
|
||||
```
|
||||
|
||||
`visual_index.update`, version 1, persiste `visual_index_id` et un curseur
|
||||
`after_feature_set_id`. Une séquence traite au plus seize Feature Sets non
|
||||
indexés, publie et commit un segment, puis checkpoint. Pause et annulation sont
|
||||
coopératives entre lectures et avant publication ; un segment déjà READY reste
|
||||
valide. La reprise recommence au dernier curseur commité et l'unicité des
|
||||
memberships rend le rejeu idempotent.
|
||||
A physical file may remain orphaned if DB publication fails after the file is published.
|
||||
|
||||
**IMPLEMENTED — parallélisme interne borné.** La Queue exécute toujours un seul
|
||||
callback. L'estimation demande jusqu'à seize threads CPU, un slot I/O, GPU zéro,
|
||||
8 Mio fixes et 2 Mio par Feature Set, lot 1..16. Le callback compte comme un
|
||||
participant et crée au plus `cpu_threads - 1` enfants. Chaque enfant lit
|
||||
exclusivement des Feature Files immuables et écrit une tranche privée ; il ne
|
||||
touche ni au handle Project DB partagé, ni au fichier de segment, ni au curseur.
|
||||
Tous les enfants sont joints avant tri, sérialisation, publication asset et
|
||||
transaction SQLite.
|
||||
No partially committed READY segment is invented.
|
||||
|
||||
La réduction emploie l'ordre total v1
|
||||
`table_id,key24,feature_set_id,feature_index`. Le fichier, son SHA-256, le
|
||||
chemin, les memberships, la génération, le fingerprint et les résultats de
|
||||
requête sont donc exactement identiques à un build avec un participant. Une
|
||||
erreur de lecture dans une tranche interdit toute publication ; une création
|
||||
de thread refusée est remplacée par le calcul de cette tranche sur le callback,
|
||||
sans changer la réduction. Le curseur n'avance qu'après la publication
|
||||
transactionnelle du segment, puis le checkpoint existant reste le seul point
|
||||
de reprise Task. `record_batch` reçoit le nombre de Feature Sets réellement
|
||||
commités, la durée réelle et `peak_memory_bytes=0` (inconnue).
|
||||
Durability distinguishes:
|
||||
|
||||
## Complexité et limites
|
||||
```text
|
||||
DURABLE
|
||||
PUBLISHED_NOT_DURABLE
|
||||
```
|
||||
|
||||
Pour `D` descriptors échantillonnés, construction et disque sont `O(6D)`.
|
||||
Une requête effectue `O(6Q log P + H)` par segment (`Q<=1024`, `H` hits bornés),
|
||||
pas `O(images²)`. La mémoire build est bornée par les métadonnées, les tranches
|
||||
de 256 descripteurs privées des participants et les postings d'un segment ; les
|
||||
tranches privées partitionnent le buffer de postings existant et ne le
|
||||
dupliquent pas. Chaque participant garde au plus un reader/FD de Feature File.
|
||||
La mémoire query est bornée par 4096 candidats, 256 postings et 256 résultats.
|
||||
À 3700 images et 512 features, environ 11,4
|
||||
millions de postings sont produits. Un test structurel persiste 50 000 Feature Sets puis
|
||||
confirme la pagination par 16 et le refus propre après 4096 memberships. Un index unique
|
||||
ne couvre donc pas encore 50 000 images : le risque principal est le nombre de segments
|
||||
et les seeks. Une compaction/base+delta ou une évolution v2 sera nécessaire, sans changer
|
||||
les identités durables; elle est `NOT_YET_WIRED`.
|
||||
## Reader validation
|
||||
|
||||
Les fixtures de validation incluent un Feature Set vide, un crop réel, une rotation de
|
||||
8 degrés, deux campagnes, un asset source partagé et une attaque de motif répétitif. Ces
|
||||
tests valident le classement de candidats LSH, jamais une compatibilité géométrique.
|
||||
The reader validates the asset SHA before trusting the format.
|
||||
|
||||
## Frontière future
|
||||
It rejects:
|
||||
|
||||
Le Candidate Pair Generator pourra filtrer sur score et `evidence_count`, puis
|
||||
transmettre `feature_set_id + feature_index` au futur matcher. Le score Visual
|
||||
Index ne constitue jamais une preuve géométrique.
|
||||
- invalid counts;
|
||||
- invalid offsets;
|
||||
- overflow;
|
||||
- malformed reserved fields;
|
||||
- inconsistent DB metadata;
|
||||
- unsupported future version.
|
||||
|
||||
A coherent future format version is `UNSUPPORTED_VERSION`, not generic corruption.
|
||||
|
||||
## Query
|
||||
|
||||
Query identity is centered on:
|
||||
|
||||
```text
|
||||
(visual_index_id, query_feature_set_id)
|
||||
```
|
||||
|
||||
Options include:
|
||||
|
||||
- ScanSet filter;
|
||||
- same/other ScanSet policy;
|
||||
- source-asset exclusion;
|
||||
- minimum evidence count;
|
||||
- `top_k` in `1..256`.
|
||||
|
||||
The source Feature Set/image is never returned as its own candidate.
|
||||
|
||||
## Evidence and score
|
||||
|
||||
One evidence unit is one distinct query `feature_index` that collides with the candidate.
|
||||
|
||||
Multiple tables or candidate postings do not multiply the same query-feature evidence.
|
||||
|
||||
Score:
|
||||
|
||||
```text
|
||||
evidence_count / sampled_query_feature_count
|
||||
```
|
||||
|
||||
Range:
|
||||
|
||||
```text
|
||||
0..1
|
||||
```
|
||||
|
||||
Canonical result order:
|
||||
|
||||
```text
|
||||
score descending
|
||||
evidence_count descending
|
||||
image_id ascending
|
||||
feature_set_id ascending
|
||||
```
|
||||
|
||||
Visual Index score is retrieval evidence only.
|
||||
|
||||
It is not descriptor-match evidence and not geometric evidence.
|
||||
|
||||
## Burstiness bound
|
||||
|
||||
Bucket frequency is bounded across the retained query snapshot.
|
||||
|
||||
A bucket above `max_bucket_postings` is ignored.
|
||||
|
||||
This prevents common patterns from dominating score or creating unbounded posting accumulation.
|
||||
|
||||
The query accumulator is bounded to 4096 candidates and 256 returned results.
|
||||
|
||||
No global query cache is required.
|
||||
|
||||
## Durable Task
|
||||
|
||||
Task Kind:
|
||||
|
||||
```text
|
||||
visual_index.update/1
|
||||
```
|
||||
|
||||
Durable cursor:
|
||||
|
||||
```text
|
||||
after_feature_set_id
|
||||
```
|
||||
|
||||
One sequence handles a bounded admitted set of new Feature Sets, publishes a complete segment, commits
|
||||
memberships, advances the cursor/checkpoint and returns through `sequence_break()` if more work remains.
|
||||
|
||||
Restart resumes from the durable cursor and membership uniqueness makes replay idempotent.
|
||||
|
||||
## Internal parallelism
|
||||
|
||||
The Queue owns one active heavy callback.
|
||||
|
||||
Visual Index may use bounded internal CPU participants inside that callback.
|
||||
|
||||
Current validated shape:
|
||||
|
||||
```text
|
||||
CPU up to 16
|
||||
batch/window 1..16
|
||||
GPU 0
|
||||
fixed RAM approximately 8 MiB
|
||||
per-item RAM approximately 2 MiB
|
||||
```
|
||||
|
||||
Each participant reads immutable Feature data into private work.
|
||||
|
||||
Participants do not publish the segment.
|
||||
|
||||
After join, the owner performs canonical total ordering, serialization, asset publication and Project DB
|
||||
commit.
|
||||
|
||||
Thread-creation failure may fall back to owner computation of that slice without changing output.
|
||||
|
||||
## Determinism
|
||||
|
||||
The following must match the serial scientific result:
|
||||
|
||||
- posting set;
|
||||
- posting order;
|
||||
- Segment File bytes;
|
||||
- SHA-256;
|
||||
- membership set;
|
||||
- generation ordering;
|
||||
- query results.
|
||||
|
||||
Operational CPU width does not enter scientific identity.
|
||||
|
||||
## GPU policy
|
||||
|
||||
Current GPU classification:
|
||||
|
||||
```text
|
||||
VISUAL_INDEX_GPU=REJECTED_WITH_MEASURED_REASON
|
||||
```
|
||||
|
||||
The stage is dominated by posting construction, total ordering, hashing and deterministic publication,
|
||||
and there is no validated production GPU seam that preserves the full contract with useful measured
|
||||
benefit.
|
||||
|
||||
This does not authorize avoidable CPU serialism.
|
||||
|
||||
```text
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
```
|
||||
|
||||
## Current downstream relationship
|
||||
|
||||
Candidate Pair Generator is implemented and consumes Visual Index queries.
|
||||
|
||||
Matcher is implemented and consumes persisted Candidate Pairs.
|
||||
|
||||
Therefore current relationship is:
|
||||
|
||||
```text
|
||||
Visual Index
|
||||
-> Candidate Pair Generator
|
||||
-> Candidate Pair persistence
|
||||
-> Matcher
|
||||
```
|
||||
|
||||
Visual Index does not pass raw `feature_set_id + feature_index` pairs directly into a hypothetical
|
||||
future Matcher.
|
||||
|
||||
The persisted Candidate Pair boundary remains explicit.
|
||||
|
||||
## Real A6000 evidence
|
||||
|
||||
The retained A6000 proof contains:
|
||||
|
||||
```text
|
||||
Feature Sets 689
|
||||
Candidate Pairs 38,420
|
||||
Match Results 38,420
|
||||
```
|
||||
|
||||
Final continuation replayed no new Visual Index work.
|
||||
|
||||
Checkpoint:
|
||||
|
||||
```text
|
||||
real-a6000-pre-sfm-2026-09-02
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
This confirms the current Visual Index path was already durably reusable before downstream GV/Tracks
|
||||
continuation.
|
||||
|
||||
## Limits
|
||||
|
||||
Current v1 limits/non-goals include:
|
||||
|
||||
- no segment compaction;
|
||||
- one index limited to 4096 Feature Sets;
|
||||
- no GPU backend;
|
||||
- no geometric meaning assigned to retrieval score;
|
||||
- no dense project-wide pair matrix.
|
||||
|
||||
A future index version may change capacity or data structure only through an explicit versioned
|
||||
scientific/persistence decision.
|
||||
|
||||
## Summary
|
||||
|
||||
```text
|
||||
VISUAL_INDEX_V1=IMPLEMENTED
|
||||
VISUAL_INDEX_KIND=orb-lsh
|
||||
VISUAL_INDEX_VERSION=1
|
||||
VISUAL_INDEX_CAPACITY=4096_FEATURE_SETS
|
||||
VISUAL_INDEX_SEGMENT_MEMBERS=16
|
||||
VISUAL_INDEX_TOP_K_MAX=256
|
||||
|
||||
VISUAL_INDEX_TASK=visual_index.update/1
|
||||
VISUAL_INDEX_GPU=REJECTED_WITH_MEASURED_REASON
|
||||
|
||||
CANDIDATE_PAIR=IMPLEMENTED
|
||||
MATCHER=IMPLEMENTED
|
||||
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
|
|
|||
Loading…
Reference in a new issue