Compare commits
No commits in common. "main" and "global-maintenance-2026-09-01" have entirely different histories.
main
...
global-mai
160 changed files with 8386 additions and 30634 deletions
429
AGENTS.md
429
AGENTS.md
|
|
@ -4,15 +4,15 @@
|
|||
|
||||
- The root `README.md` is the index of project documentation.
|
||||
- Canonical documents under `docs/**` define scientific contracts, architecture,
|
||||
FROZEN invariants, persistence semantics, lifecycle state, resource policy,
|
||||
and roadmap ordering.
|
||||
FROZEN invariants, persistence semantics, lifecycle state, and roadmap
|
||||
ordering.
|
||||
- Before changing an architectural, scientific, persistence, runtime, or
|
||||
resource-sensitive area, identify and read the relevant canonical document
|
||||
and its applicable invariants.
|
||||
- FROZEN documentation and contracts must never be changed silently to fit an
|
||||
implementation.
|
||||
- A lower-level implementation convenience never overrides a higher-level
|
||||
canonical contract or an explicit human decision.
|
||||
canonical contract.
|
||||
- Lifecycle markers such as `PLANNED`, `IMPLEMENTED`,
|
||||
`VALIDATION PENDING`, and `PASS/FROZEN` describe repository state and must
|
||||
match actual implementation and validation evidence.
|
||||
|
|
@ -23,36 +23,6 @@
|
|||
Resolve locally determinable engineering problems from the existing code and
|
||||
canonical documentation.
|
||||
|
||||
## Repository language policy
|
||||
|
||||
The canonical language of the Lardon3D repository is English.
|
||||
|
||||
This applies to:
|
||||
|
||||
- README.md;
|
||||
- AGENTS.md;
|
||||
- all current and future files under docs/**;
|
||||
- prompt.md and all files under prompt/**;
|
||||
- source comments under include/lardon3d/** and src/**;
|
||||
- developer-facing technical documentation, diagnostics and engineering contracts where applicable.
|
||||
|
||||
The repository must not intentionally mix French and English technical prose.
|
||||
Existing French documentation and source comments are to be converted to English during the planned documentation and source-comment remediation passes, without changing scientific meaning, FROZEN facts, historical evidence, numeric values, identities, lifecycle truth or contract authority.
|
||||
|
||||
Historical documents may be translated, but translation must not modernize or reinterpret the historical state they record.
|
||||
|
||||
The canonical user-interface language is also English. Existing non-English TUI labels must converge on English during an explicitly scoped UI-language remediation pass; documentation-only work must not silently modify executable UI behavior.
|
||||
|
||||
Canonical markers:
|
||||
|
||||
DOCUMENTATION_LANGUAGE=ENGLISH
|
||||
|
||||
SOURCE_COMMENT_LANGUAGE=ENGLISH
|
||||
|
||||
AGENT_CONTRACT_LANGUAGE=ENGLISH
|
||||
|
||||
USER_INTERFACE_LANGUAGE=ENGLISH
|
||||
|
||||
## 2. FROZEN integrity
|
||||
|
||||
The following current project foundation is protected and may change only
|
||||
|
|
@ -60,12 +30,11 @@ through an explicitly authorized, explicitly scoped human ticket:
|
|||
|
||||
- Gates A–G — PASS/FROZEN
|
||||
- Track Model — PASS/FROZEN
|
||||
- Track Builder scientific contract — PASS/FROZEN
|
||||
- Track Builder — PASS/FROZEN
|
||||
- F0 — PASS/FROZEN
|
||||
- Phase H v1 — PASS/FROZEN
|
||||
- MVS-M1 — PASS/FROZEN
|
||||
- Project DB v22 scientific/persistence foundation — PASS/FROZEN
|
||||
- Project DB v23 generic optical-context overlay — PASS/FROZEN
|
||||
- Calibration Bootstrap v1 — PASS/FROZEN
|
||||
- Selected Scientific Execution — PASS/FROZEN
|
||||
- Photo Quality Triage / Acquisition Selection — PASS/FROZEN
|
||||
|
|
@ -74,48 +43,25 @@ through an explicitly authorized, explicitly scoped human ticket:
|
|||
- S3 Capture / Acquisition Ingestion — PASS/FROZEN
|
||||
- Durable Acquisition-Campaign Execution — PASS/FROZEN
|
||||
- Global Maintenance Audit — PASS/FROZEN
|
||||
- Real S21 Tracks scientific result — PASS/FROZEN
|
||||
- Real A6000 pre-SfM scientific result — PASS/FROZEN
|
||||
|
||||
Detailed subcontracts remain defined by their canonical documents. This file
|
||||
must not duplicate every S3 substage, scientific threshold, migration detail,
|
||||
does not duplicate every S3 substage, scientific threshold, migration detail,
|
||||
or persistence format.
|
||||
|
||||
Project DB v25 is an additive operational schema in the retained lineage. It preserves v24 RAW batch, the v23
|
||||
optical overlay and the v22 scientific/persistence foundation. Its additive purpose is typed durable
|
||||
persistence for `features.extract.batch/1` through `feature_extract_batch_tasks`; it adds no
|
||||
scientific identity and must not reinterpret historical rows. The v25 Feature-batch path has completed
|
||||
its retained real A6000 proof. Historical references to older Project DB versions remain valid where
|
||||
they describe the actual historical contract or migration path.
|
||||
|
||||
Project DB v27 is the current additive operational schema. It preserves the v22 scientific/
|
||||
persistence foundation, v23 optical overlay, v24 RAW batch, v25 Feature batch and v26 exact
|
||||
Capture geometric-state/applicability foundation. Its v27 focus-domain rows are generic bounded
|
||||
exact-token machinery with no historical backfill and no device-specific physical applicability
|
||||
claim. Future schema changes beyond v27 require explicit human authorization.
|
||||
Project DB v23 is the current additive optical-context overlay. It preserves
|
||||
the v22 scientific/persistence foundation and must not infer or backfill optical
|
||||
identity from historical data. Historical references to older Project DB
|
||||
versions remain valid when they describe the actual historical contract or
|
||||
migration path. Do not rewrite legitimate v16–v22 history merely because v23
|
||||
is current.
|
||||
|
||||
The global maintenance implementation, fresh portable/Vulkan/sanitizer/
|
||||
concurrency validation and independent final review are acquired. Its lifecycle
|
||||
is `GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN`. The review independently passed the
|
||||
portable build, complete suites, focused matrices, strict public-header probes,
|
||||
ABI and production-seam checks, retained-manifest verification and diff
|
||||
validation with zero blocking findings. Do not reopen this boundary or infer a
|
||||
new scientific policy from the freeze.
|
||||
|
||||
The canonical review checkpoint is tag `global-maintenance-2026-09-01` at
|
||||
commit `b84f860d868c66d9ee84b85ceb1bc6480b95aca5`; its detailed evidence is
|
||||
[`docs/architecture/global_maintenance_audit.md`](docs/architecture/global_maintenance_audit.md).
|
||||
Future reviews are delta-based from this checkpoint: begin with
|
||||
`git diff global-maintenance-2026-09-01...HEAD` and examine changed files,
|
||||
their directly affected contracts, tests, documentation, and crossed dependency
|
||||
boundaries. Unchanged PASS/FROZEN systems inherit this evidence and are reopened
|
||||
only by concrete evidence; do not repeat a global A-to-Z audit.
|
||||
|
||||
The later real-data checkpoint is tag `real-a6000-pre-sfm-2026-09-02`. It establishes
|
||||
`REAL_A6000_PRE_SFM=PASS/FROZEN` through Geometric Verification and Tracks while explicitly
|
||||
leaving Sparse SfM and Dense/MVS unexecuted. This checkpoint adds later operational and real-data
|
||||
evidence; it does not erase or replace the `global-maintenance-2026-09-01` review authority for
|
||||
unchanged FROZEN systems.
|
||||
portable build, 64/64 complete suite, 15/15 focused matrix, 76/76 strict header
|
||||
probes across 19 modified/new public headers, ABI and production-seam checks,
|
||||
retained-manifest verification and diff validation, with zero blocking findings.
|
||||
Do not reopen this boundary or infer a new scientific policy from the freeze.
|
||||
|
||||
When a ticket declares `NO_NEW_SUBSYSTEM`, do not introduce an unrelated:
|
||||
|
||||
|
|
@ -133,13 +79,11 @@ When a ticket declares `NO_NEW_SUBSYSTEM`, do not introduce an unrelated:
|
|||
- daemon.
|
||||
|
||||
This restriction is ticket-scoped. It does not mean that a subsystem already
|
||||
present in the canonical architecture can never be used or extended by later
|
||||
authorized work.
|
||||
present in the canonical architecture can never be used by later authorized
|
||||
work.
|
||||
|
||||
Never reopen a FROZEN scientific decision merely to simplify an
|
||||
implementation. Operational/resource contracts may be reopened only by explicit
|
||||
human authority and must preserve the scientific result exactly unless the
|
||||
human ticket explicitly says otherwise.
|
||||
Never reopen a FROZEN scientific or architectural decision merely to simplify
|
||||
an implementation.
|
||||
|
||||
## 3. Scope and Git discipline
|
||||
|
||||
|
|
@ -204,8 +148,8 @@ require a clean worktree unless the ticket explicitly requires one.
|
|||
allocation, solver execution or output mutation; do not alter the FROZEN
|
||||
defaults, encodings or fingerprints to accommodate an unsafe cast.
|
||||
|
||||
Compiler success alone is not proof of API, ABI, persistence, resource, or
|
||||
scientific contract correctness.
|
||||
Compiler success alone is not proof of API, ABI, persistence, or scientific
|
||||
contract correctness.
|
||||
|
||||
## 5. Identity discipline
|
||||
|
||||
|
|
@ -251,7 +195,7 @@ explicitly defines such an identity.
|
|||
progress distinct from generic runtime percentage, and keep unknown
|
||||
provenance visibly UNKNOWN. Full layout starts at 100x30, compact is
|
||||
supported through 60x15 (72x20 is the reference compact boundary), and only
|
||||
the bounded terminal-too-small fallback is allowed below that minimum.
|
||||
the bounded "Terminal trop petit" fallback is allowed below that minimum.
|
||||
- Preserve the contextual key contract. `F10 SSD` remains literally visible at
|
||||
60 columns in idle, text-input and import-running modes; text input owns only
|
||||
Enter/Escape/F10 and a running import owns only cancel (`X`) and F10 while
|
||||
|
|
@ -264,132 +208,13 @@ explicitly defines such an identity.
|
|||
- Extend validated abstractions rather than rewriting validated modules.
|
||||
- Reuse the existing Task / Queue / Scheduler / Resource Governor ownership
|
||||
model rather than creating parallel runtime infrastructure.
|
||||
|
||||
### Canonical utilization objective — MAXIMUM SAFE USEFUL THROUGHPUT
|
||||
|
||||
Lardon3D is a throughput-oriented workstation application. The Resource
|
||||
Governor MUST maximize safe and useful utilization for every production Task
|
||||
after preserving only the interactive host reserve needed for normal use of:
|
||||
|
||||
- Arch Linux / Sway and normal desktop services;
|
||||
- Firefox;
|
||||
- normal audio/music playback;
|
||||
- lightweight interactive use of the workstation.
|
||||
|
||||
All CPU, RAM, I/O capacity and validated accelerator capacity beyond that
|
||||
interactive reserve belongs to the active Lardon3D workload when useful work
|
||||
exists.
|
||||
|
||||
On the current reference host, the normal observed policy outcome is
|
||||
approximately:
|
||||
|
||||
```text
|
||||
16 logical CPUs total
|
||||
4 logical CPUs reserved for the interactive host
|
||||
12 logical CPUs available to the compute pool
|
||||
~3 GiB MemAvailable preserved as the hard RAM reserve
|
||||
Radeon 780M UMA available to validated/useful GPU backends
|
||||
```
|
||||
|
||||
These values are reference-host outcomes, NOT portable constants. Topology,
|
||||
process affinity, smaller hosts, memory pressure, I/O pressure and future
|
||||
hardware must be handled dynamically. A future 32-thread host must not inherit a
|
||||
12-thread product ceiling merely because the reference host exposed 12 compute
|
||||
threads.
|
||||
|
||||
The optimization target is:
|
||||
|
||||
```text
|
||||
MAXIMUM SAFE USEFUL THROUGHPUT
|
||||
```
|
||||
|
||||
It is NOT:
|
||||
|
||||
```text
|
||||
minimum resource usage
|
||||
fixed CPU counts
|
||||
CPU/GPU utilization percentage for its own sake
|
||||
preserving historical CPU1/batch1 descriptors without evidence
|
||||
```
|
||||
|
||||
The Governor remains the sole production resource authority. Normal users do
|
||||
not choose CPU count, worker count, batch, inflight depth, GPU backend, scratch
|
||||
mode, or RAM budget for authoritative execution.
|
||||
|
||||
A production Task may deliberately use less than the currently available
|
||||
compute pool only when concrete evidence establishes at least one relevant
|
||||
constraint, such as:
|
||||
|
||||
- true scientific or dependency serialism;
|
||||
- measured useful-scaling knee;
|
||||
- memory bound;
|
||||
- I/O saturation;
|
||||
- validated GPU execution making additional CPU work useless;
|
||||
- deterministic publication constraint that cannot be separated safely from
|
||||
preparation;
|
||||
- another explicit and measured resource limitation.
|
||||
|
||||
The reason must be represented by the Task/Governor contract or canonical
|
||||
resource documentation where non-obvious. A historical descriptor by itself is
|
||||
not evidence.
|
||||
|
||||
### SERIALISM_REQUIRES_PROOF
|
||||
|
||||
`SERIALISM_REQUIRES_PROOF` is canonical operational policy.
|
||||
|
||||
Per-item atomicity does NOT imply cross-item serialization. Owner-only or
|
||||
ordered durable publication does NOT imply serial preparation. When multiple
|
||||
independent work units exist and exact science/persistence semantics are
|
||||
preserved, the Task must expose bounded concurrency to the Governor.
|
||||
|
||||
A long-running `CPU1` or `batch1` path with independent executable work and
|
||||
available safe resources is an operational defect until one of the documented
|
||||
measured limitations above proves otherwise.
|
||||
|
||||
Do not solve this by creating another scheduler, uncontrolled worker pool,
|
||||
unbounded `std::async`, detached threads, or resources outside Governor
|
||||
accounting. Prefer the established bounded pattern where appropriate:
|
||||
|
||||
```text
|
||||
one admitted owner Task
|
||||
-> bounded independent participants/preparation
|
||||
-> join all participants
|
||||
-> deterministic owner-only publication
|
||||
```
|
||||
|
||||
Every participant must be accounted, bounded, cancellable and joined. Shared
|
||||
SQLite access must preserve the existing ownership/serialization contracts.
|
||||
|
||||
### CPU, GPU, RAM and I/O policy
|
||||
|
||||
- CPU: expose truthful minimum/useful/safe concurrency and allow Governor to use
|
||||
the largest currently safe/useful value. Do not hardcode the reference-host
|
||||
12-thread result.
|
||||
- GPU: a validated AND useful production GPU backend MUST be preferred when it
|
||||
is available and Governor-safe. Do not invent or promote an unvalidated GPU
|
||||
path merely to make the GPU busy.
|
||||
- RAM: use available memory aggressively for useful work while preserving the
|
||||
hard interactive reserve, approximately 3 GiB `MemAvailable` on the reference
|
||||
host. The 3–4 GiB band is a pressure/conservatism zone, not a permanent extra
|
||||
1 GiB subtraction from every Task.
|
||||
- UMA: iGPU allocations count exactly once against host RAM.
|
||||
- swap/zram: pressure and safety mechanisms, never admitted working RAM.
|
||||
- scratch SSD: optional external storage, never RAM. A Task may consume it only
|
||||
through an explicitly validated Governor-owned scratch contract.
|
||||
- I/O: if additional concurrency no longer improves useful throughput because
|
||||
storage or another I/O boundary is saturated, the measured knee is the right
|
||||
useful limit. Do not force CPU saturation for appearance.
|
||||
- Pressure: CPU/memory/I/O PSI and active swap-in/out deltas may reduce
|
||||
admission. When pressure clears, useful resources must be re-admitted rather
|
||||
than remaining permanently throttled.
|
||||
|
||||
System responsiveness is therefore protected by the explicit host reserve and
|
||||
pressure feedback; it is not a justification for leaving additional safe/useful
|
||||
compute idle.
|
||||
|
||||
- System stability and responsiveness take priority over throughput.
|
||||
- Bound memory, buffers, files, descriptors, processes, threads, captured
|
||||
output, parser work, temporary storage, and staging honestly for the
|
||||
output, parser work, temporary storage, and staging reasonably for the
|
||||
operation.
|
||||
- Memory shared with an iGPU counts against host RAM.
|
||||
- zram and swap are pressure/safety mechanisms, not normal working-memory
|
||||
budgets.
|
||||
- An operational hardware/resource bound must not accidentally become a
|
||||
scientific dataset-size limit.
|
||||
- Do not invent a new global resource subsystem inside a ticket that defers it.
|
||||
|
|
@ -403,16 +228,12 @@ Resource-sensitive work must identify, where relevant:
|
|||
|
||||
- resource owner;
|
||||
- admission point;
|
||||
- minimum/useful/safe CPU and batch capability;
|
||||
- fixed/per-participant/transient memory budget;
|
||||
- bound or budget;
|
||||
- reservation lifetime;
|
||||
- release point;
|
||||
- failure cleanup;
|
||||
- cancellation cleanup;
|
||||
- I/O/scratch ownership;
|
||||
- validated GPU capability;
|
||||
- whether a bound is operational or scientific;
|
||||
- measured reason for intentional under-utilization.
|
||||
- whether the bound is operational or scientific.
|
||||
|
||||
The reviewed external USB SSD controller is the authorized physical-lifecycle
|
||||
boundary for the exact UDisks Drive/label/UUID contract. Its validated snapshot
|
||||
|
|
@ -421,10 +242,11 @@ production orchestrator for scratch-lease acquire/release. The controller does
|
|||
not replace the Governor or invent Task scratch eligibility, and swap/scratch
|
||||
never become RAM. The application lifetime order is strict: destroy/join the
|
||||
Queue so every Task lease is released, checked-join/unregister the SSD binding,
|
||||
destroy the controller, then destroy the Governor. SSD availability is a
|
||||
capability, not fabricated Task usage. Do not add ad-hoc discovery, mounting,
|
||||
formatting, `swapon`, cleanup, force-drain, shell commands, or a second
|
||||
resource/scheduling subsystem outside the reviewed controller/Governor APIs.
|
||||
destroy the controller, then destroy the Governor. The current fourteen Task
|
||||
kinds have no scratch consumer, so availability is capability, not fabricated
|
||||
usage. Do not add ad-hoc discovery, mounting, formatting, `swapon`, cleanup,
|
||||
force-drain, shell commands, or a second resource/scheduling subsystem outside
|
||||
the reviewed controller/Governor APIs.
|
||||
|
||||
Snapshot conversion is fail-closed by physical state. Any pairing or authority
|
||||
requires current detection of the Drive and both UUID-bearing partitions,
|
||||
|
|
@ -595,8 +417,7 @@ comment.
|
|||
Where relevant, comments must preserve the established distinction between:
|
||||
|
||||
- immutable SOURCE RAW;
|
||||
- camera JPEG SOURCE;
|
||||
- deterministic DERIVED RAW representation;
|
||||
- deterministic DERIVED representation;
|
||||
- RAW Policy v1;
|
||||
- L3DRAWD1 identity;
|
||||
- metadata-only acquisition processing;
|
||||
|
|
@ -608,12 +429,6 @@ Where relevant, comments must preserve the established distinction between:
|
|||
- bounded secondary JPEG validation;
|
||||
- zero-only permitted MPF gaps/trailer.
|
||||
|
||||
For the current A6000 selected scientific execution, paired camera JPEGs are
|
||||
valid Capture source assets and preferred fast proxies for Photo Quality, but
|
||||
the FROZEN geometry representation remains deterministic RAW-derived PNG. Do
|
||||
not substitute camera-JPEG geometry without a separately authorized and proven
|
||||
scientific/calibration equivalence tranche.
|
||||
|
||||
Do not imply that metadata validation performs pixel decoding when it does not.
|
||||
|
||||
### Task / Queue / Scheduler / Governor
|
||||
|
|
@ -630,8 +445,6 @@ In particular:
|
|||
- Resource Reservation belongs to the currently admitted bounded execution.
|
||||
- `sequence_break` is an execution boundary that releases/re-establishes
|
||||
admission according to runtime semantics.
|
||||
- Per-item atomicity does not imply cross-item serialization.
|
||||
- Serial publication may coexist with Governor-admitted parallel preparation.
|
||||
|
||||
Do not imply that a long campaign reserves resources for its entire lifetime
|
||||
when execution is group-bounded.
|
||||
|
|
@ -644,18 +457,16 @@ resource ownership understandable from source.
|
|||
Where applicable document:
|
||||
|
||||
- RAM ownership/bound;
|
||||
- CPU minimum/useful/safe admission;
|
||||
- CPU admission;
|
||||
- GPU admission;
|
||||
- I/O ownership;
|
||||
- process/thread count;
|
||||
- per-participant memory;
|
||||
- file-descriptor lifetime;
|
||||
- temporary/scratch storage ownership;
|
||||
- reservation lifetime;
|
||||
- release point;
|
||||
- failure cleanup;
|
||||
- cancellation cleanup;
|
||||
- measured scaling knee or reason for serial execution.
|
||||
- cancellation cleanup.
|
||||
|
||||
An operational hardware/resource bound MUST NOT accidentally become a
|
||||
scientific dataset-size limit.
|
||||
|
|
@ -682,12 +493,6 @@ The residual pre-return S3-E crash window remains intentional and documented:
|
|||
if S3-E creates a Capture internally and the process dies before S3-E returns
|
||||
the `capture_id`, campaign execution must not guess that Capture identity.
|
||||
|
||||
For `raw.develop.batch/1`, typed `task_id -> selected_execution_id` persistence
|
||||
must remain distinct from generic Task runtime state. Independent RAW
|
||||
participants may prepare concurrently, but owner publication and selected-item
|
||||
cursor advancement remain deterministic and ordered by the selected execution
|
||||
contract.
|
||||
|
||||
### C / C++ ABI boundaries
|
||||
|
||||
Where C++ implements a public C interface or C Task callback, comments should
|
||||
|
|
@ -710,8 +515,7 @@ Comments should state WHAT CONTRACT the fixture proves, for example:
|
|||
- ambiguity;
|
||||
- scientific identity conflict;
|
||||
- cross-ScanSet rejection;
|
||||
- resource-pressure/admission condition;
|
||||
- deliberate CPU1 baseline used only for a scaling comparison.
|
||||
- resource-pressure/admission condition.
|
||||
|
||||
Do not narrate ordinary test mechanics.
|
||||
|
||||
|
|
@ -739,9 +543,9 @@ For every future implementation tranche:
|
|||
4. update canonical documentation when behavior/contracts changed;
|
||||
5. validate every modified public C17 header;
|
||||
6. validate relevant C++ syntax/build integration;
|
||||
7. validate resource ownership, accounting, useful scaling and bounds;
|
||||
8. run applicable build/tests/sanitizers efficiently;
|
||||
9. perform the required bounded review;
|
||||
7. validate resource ownership and bounds;
|
||||
8. run applicable build/tests/sanitizers;
|
||||
9. perform the required review;
|
||||
10. only then claim completion.
|
||||
|
||||
The normalized future implementation standard is:
|
||||
|
|
@ -753,14 +557,12 @@ CODE
|
|||
+ CANONICAL DOCUMENTATION
|
||||
+ C17/C++ SYNTAX
|
||||
+ RESOURCE OWNERSHIP
|
||||
+ MAXIMUM SAFE USEFUL THROUGHPUT
|
||||
+ VALIDATION
|
||||
+ REVIEW
|
||||
```
|
||||
|
||||
A tranche is NOT complete merely because code compiles and tests pass when
|
||||
required contract/invariant comments, canonical documentation, or required
|
||||
resource behavior are missing.
|
||||
required contract/invariant comments or canonical documentation are missing.
|
||||
|
||||
There should be no future project-wide comment-cleanup pass for newly written
|
||||
code: comment debt must be handled when the code is introduced.
|
||||
|
|
@ -802,14 +604,9 @@ Persistence changes require explicit attention to:
|
|||
|
||||
For Project DB:
|
||||
|
||||
- preserve the FROZEN v22 scientific/persistence semantics and v23 optical
|
||||
overlay;
|
||||
- v24 is the explicitly authorized additive operational RAW-batch migration and
|
||||
must not reinterpret scientific history;
|
||||
- v25 is the additive Feature-batch migration for `features.extract.batch/1` and
|
||||
must preserve v22/v23/v24 identities, rows and semantics;
|
||||
- future schema-version changes beyond the current v27 head require
|
||||
explicit human authorization;
|
||||
- preserve the FROZEN v22 semantics and the current additive v23 optical
|
||||
overlay unless a ticket explicitly authorizes a later schema change;
|
||||
- schema-version changes require explicit human authorization;
|
||||
- migrations must be additive unless a different migration is explicitly
|
||||
authorized;
|
||||
- existing projects must remain recoverable;
|
||||
|
|
@ -829,86 +626,41 @@ make retries convenient.
|
|||
- For changes to API, architecture, ownership, concurrency, persistence,
|
||||
pipeline, resources, limits, or scientific semantics, check whether
|
||||
canonical documentation must be updated.
|
||||
- `MAXIMUM SAFE USEFUL THROUGHPUT` and `SERIALISM_REQUIRES_PROOF` are canonical
|
||||
human resource-policy decisions. Any resource document that contradicts them
|
||||
is stale unless it describes an explicitly historical measurement.
|
||||
- Documentation must describe proven implementation, not desired future
|
||||
behavior. Human policy may be marked as policy while implementation work is
|
||||
still `VALIDATION PENDING`.
|
||||
behavior.
|
||||
- Roadmap documents may describe future behavior, but future capabilities must
|
||||
be clearly marked as planned/later/exploratory.
|
||||
- Never describe future viewer, Capture Guidance, video/keyframe, Task scratch
|
||||
consumption, or camera-control capabilities as implemented before they are
|
||||
actually validated.
|
||||
actually validated. The current TUI/F10 and controller-to-Governor registry
|
||||
are validated operationally, but no current Task kind consumes scratch and
|
||||
those interfaces do not make dense/scratch-consuming workflows complete.
|
||||
- Statuses such as `PLANNED`, `IMPLEMENTED`, `VALIDATION PENDING`, and
|
||||
`PASS/FROZEN` are authoritative lifecycle statements.
|
||||
- Update lifecycle state only when implementation, validation, and review
|
||||
evidence support the transition.
|
||||
- Never mark work `PASS/FROZEN` without the required validation and review.
|
||||
- Preserve legitimate historical DB/version/resource references where they
|
||||
describe frozen history; label superseded operational envelopes as historical
|
||||
rather than silently rewriting the evidence.
|
||||
- Preserve legitimate historical DB/version references where they describe
|
||||
frozen history.
|
||||
|
||||
## 12. Required validation and test resource policy
|
||||
## 12. Required validation
|
||||
|
||||
For ordinary implementation tickets, before claiming completion, actually run
|
||||
the applicable commands. Builds, tests, benchmarks and real-proof workloads
|
||||
MUST follow the same resource philosophy as production: preserve the
|
||||
interactive host reserve, then use the maximum safe/useful remaining resources.
|
||||
Artificially serial engineering work wastes both elapsed time and agent budget.
|
||||
the applicable commands.
|
||||
|
||||
### Normal build
|
||||
|
||||
Do not use a fixed historical `-j8` as a product rule. Derive a safe parallel
|
||||
job count from the current host/affinity. On the current 16-logical-CPU
|
||||
reference host, where four logical CPUs are reserved for interactive use, the
|
||||
normal build target is approximately:
|
||||
Normal build:
|
||||
|
||||
```sh
|
||||
meson setup --reconfigure build
|
||||
meson compile -C build -j12
|
||||
meson compile -C build -j8
|
||||
```
|
||||
|
||||
On another host, choose the analogous available-compute value rather than
|
||||
hardcoding 12.
|
||||
|
||||
### Normal tests
|
||||
|
||||
Independent tests should run concurrently within the same host reserve and
|
||||
memory constraints. On the current reference host, a typical command is:
|
||||
Normal tests:
|
||||
|
||||
```sh
|
||||
meson test -C build --num-processes 12 --print-errorlogs
|
||||
meson test -C build --num-processes 1 --print-errorlogs
|
||||
```
|
||||
|
||||
This is not permission to race tests that share mutable fixtures, real projects,
|
||||
fixed ports, exclusive GPU state, or other process-global resources. Such tests
|
||||
must be grouped or serialized for the concrete dependency, and that reason must
|
||||
be understood rather than inherited from an old `--num-processes 1` default.
|
||||
|
||||
A CPU1 run is valid when CPU1 is the actual test case or baseline measurement;
|
||||
it is not the authoritative production configuration after higher safe/useful
|
||||
concurrency has been proven.
|
||||
|
||||
### Validation efficiency
|
||||
|
||||
- Do not rebuild unchanged targets between proof iterations without a concrete
|
||||
need.
|
||||
- Do not rerun already-acquired expensive validation when the relevant code and
|
||||
dependency boundary did not change.
|
||||
- Run focused tests first; widen only when the changed dependency boundary
|
||||
requires it.
|
||||
- Expensive/stress validation must be relevant to the current delta.
|
||||
- Heavy validation is parallel by default when independent and resource-safe;
|
||||
serialize only when scientific determinism, mutable shared fixtures, memory,
|
||||
I/O, GPU exclusivity, sanitizer behavior or measured host pressure requires
|
||||
it.
|
||||
- A test timeout or sanitizer failure must be investigated; do not repeatedly
|
||||
rerun until green.
|
||||
- Real-data proofs must use normal Governor admission. Do not manually force
|
||||
CPU1 merely for reproducibility unless CPU1 itself is the intended comparison
|
||||
cohort.
|
||||
|
||||
Diff validation:
|
||||
|
||||
```sh
|
||||
|
|
@ -931,48 +683,24 @@ For relevant concurrency changes, run TSan when supported and meaningful.
|
|||
If a sanitizer is unavailable or invalid because of the environment/toolchain,
|
||||
report that explicitly rather than claiming PASS.
|
||||
|
||||
Run expensive/stress validation only when relevant.
|
||||
|
||||
Run heavy validation serially when required to preserve machine stability.
|
||||
|
||||
Investigate a timeout or sanitizer failure. Do not repeatedly rerun a failing
|
||||
test until it happens to pass.
|
||||
|
||||
Distinguish third-party sanitizer/environment noise from repository defects
|
||||
using concrete stack/failure evidence.
|
||||
|
||||
Never claim a command, test, sanitizer, review, benchmark, or real-data
|
||||
validation that was not actually performed.
|
||||
Never claim a command, test, sanitizer, review, or real-data validation that
|
||||
was not actually performed.
|
||||
|
||||
For documentation/comment-only changes, do not invent unnecessary sanitizer
|
||||
work, but still run enough build/syntax/diff validation to prove that the
|
||||
non-functional boundary was preserved.
|
||||
|
||||
## 13. Engineering execution and credit economy
|
||||
|
||||
Engineering time and agent budget are finite project resources and must not be
|
||||
wasted.
|
||||
|
||||
- Default to the configured economical parent/orchestrator and existing agent
|
||||
roles. Do not invent a new agent hierarchy.
|
||||
- Use expensive/high-reasoning scientific or final-review roles only when the
|
||||
current delta genuinely requires scientific equivalence, difficult
|
||||
concurrency/persistence reasoning, or final sensitive review.
|
||||
- Do not spawn multiple agents to restate the same architecture or repeat the
|
||||
same review.
|
||||
- Do not delegate ordinary implementation defects upward when a worker,
|
||||
mechanic or resolver can fix them directly within authority.
|
||||
- Prefer one focused independent review after the implementation is stable to
|
||||
repeated speculative reviews during ordinary coding.
|
||||
- Reuse acquired maintenance, sanitizer, benchmark and real-data evidence when
|
||||
the affected boundary is unchanged.
|
||||
- Do not wait on an expensive agent when the next authorized executable action
|
||||
can proceed independently.
|
||||
- Keep tool/log output bounded; inspect targeted portions instead of repeatedly
|
||||
dumping whole logs.
|
||||
- Do not rerun a global A-to-Z audit after the maintenance checkpoint.
|
||||
- `WORK FIRST. RETURN LAST.` Ordinary in-scope findings are repaired and
|
||||
validated before returning to the human.
|
||||
|
||||
Low remaining agent/credit budget is a reason to eliminate redundant work, not
|
||||
a reason to weaken required correctness evidence. If a genuinely required
|
||||
validation cannot be afforded/executed, report it honestly rather than claiming
|
||||
PASS.
|
||||
|
||||
## 14. Review discipline
|
||||
## 13. Review discipline
|
||||
|
||||
A normal review should verify the bounded current delta and direct regressions.
|
||||
|
||||
|
|
@ -990,7 +718,7 @@ A reviewer request does not automatically define new policy.
|
|||
Compare findings against:
|
||||
|
||||
1. canonical FROZEN documentation;
|
||||
2. explicit human decisions, including the resource-utilization policy;
|
||||
2. explicit human decisions;
|
||||
3. established tranche semantics;
|
||||
4. documented future-scope boundaries.
|
||||
|
||||
|
|
@ -998,13 +726,9 @@ Do not invoke an expensive implementation agent merely to satisfy speculative
|
|||
hardening that is outside the current contract.
|
||||
|
||||
Comments and documentation are reviewable implementation artifacts. A
|
||||
misleading contract or resource-policy comment is a defect even if compiled
|
||||
behavior is unchanged.
|
||||
misleading contract comment is a defect even if compiled behavior is unchanged.
|
||||
|
||||
Resource review must specifically flag accidental serialism when independent
|
||||
work exists and safe/useful resources are idle without measured justification.
|
||||
|
||||
## 15. Delivery report / STOP conditions
|
||||
## 14. Delivery report / STOP conditions
|
||||
|
||||
Every completed ticket report must include:
|
||||
|
||||
|
|
@ -1017,17 +741,15 @@ Every completed ticket report must include:
|
|||
- known blockers;
|
||||
- non-blocking findings;
|
||||
- deliberately deferred/future-scope items;
|
||||
- resource impact where relevant, including admitted/useful concurrency for
|
||||
modified resource-sensitive Tasks;
|
||||
- resource impact where relevant;
|
||||
- confirmation that unrelated and FROZEN areas were preserved;
|
||||
- Git state;
|
||||
- confirmation that `scan3d/` remained untouched when protected.
|
||||
|
||||
STOP and request a human decision only when resolution requires:
|
||||
|
||||
- changing a FROZEN scientific contract;
|
||||
- changing Project DB schema/version beyond the current v27 head without
|
||||
prior authorization;
|
||||
- changing a FROZEN contract;
|
||||
- changing Project DB schema/version without prior authorization;
|
||||
- introducing a genuinely new subsystem outside authorized scope;
|
||||
- files outside the authorized scope;
|
||||
- destructive Git action;
|
||||
|
|
@ -1044,12 +766,9 @@ Do NOT stop merely because:
|
|||
- a normal test fails;
|
||||
- an ordinary implementation bug exists;
|
||||
- a reviewer identifies a bounded repairable defect;
|
||||
- a resource descriptor is historically conservative and explicit human
|
||||
authority already allows operational correction while preserving science;
|
||||
- a dependency needs factual investigation;
|
||||
- documentation status is stale;
|
||||
- an implementation detail can be resolved safely from existing code and
|
||||
contracts.
|
||||
|
||||
Advance the project while preserving scientific contracts and maximizing safe,
|
||||
useful throughput.
|
||||
Advance the project while preserving the contracts.
|
||||
|
|
|
|||
21
LICENSE
21
LICENSE
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 LabfyTools
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
596
README.md
596
README.md
|
|
@ -1,334 +1,198 @@
|
|||
# Lardon3D
|
||||
|
||||
Lardon3D is a generic, persistent, incremental, resource-aware photogrammetry engine for Linux,
|
||||
controlled through an ncursesw TUI.
|
||||
Moteur de photogrammétrie générique, persistant, incrémental et sensible aux
|
||||
ressources, piloté par une TUI ncursesw.
|
||||
|
||||
## Vision
|
||||
|
||||
Lardon3D is designed around the following principles:
|
||||
Lardon3D est un moteur de photogrammétrie Linux qui privilégie :
|
||||
|
||||
- **Scientific traceability**: results, identities, parameters and provenance are explicit.
|
||||
- **Determinism**: equivalent inputs and contracts produce reproducible, auditable outputs.
|
||||
- **Persistent progress**: long-running work is checkpointed and restartable.
|
||||
- **Bounded execution**: memory, CPU, GPU, I/O and temporary-storage use are explicitly bounded.
|
||||
- **Maximum safe useful throughput**: after preserving the interactive host reserve, available
|
||||
resources should be used whenever they provide useful throughput.
|
||||
- **Incremental reconstruction**: new observations can extend previous results without silently
|
||||
rewriting validated history.
|
||||
- **Atomic publication**: partially produced scientific outputs never masquerade as complete ones.
|
||||
- **Stabilité** : aucune saturation du système hôte
|
||||
- **Déterminisme** : résultats reproductibles et traçables
|
||||
- **Faible consommation mémoire** : traitement par lots adaptatifs
|
||||
- **Reprise après interruption** : résultats atomiques et persistants
|
||||
- **Protection de la machine** : budgets bornés et respectueux
|
||||
- **Traçabilité** : historique des opérations et métriques
|
||||
- **Enrichissement progressif** : reconstruction incrémentale
|
||||
|
||||
Lardon3D is not simply a "folder of photos -> 3D object" tool. Its target model is:
|
||||
Lardon3D ne vise pas simplement "dossier de photos → objet 3D", mais un ensemble
|
||||
progressif d'observations et de contraintes donnant une reconstruction géométrique
|
||||
persistante, enrichissable et versionnable.
|
||||
|
||||
```text
|
||||
progressive observations and constraints
|
||||
-> persistent geometric reconstruction
|
||||
-> validated incremental enrichment
|
||||
-> dense geometry / mesh / texture / export
|
||||
```
|
||||
## État actuel
|
||||
|
||||
## Current repository state
|
||||
### Briques validées
|
||||
|
||||
### Current Project Database
|
||||
- **Project** : cycle de vie persistant, identité stable et Project Database ouverte
|
||||
- **Import** : premier task kind de production, exécuté par la file générique en lots bornés et reprenables
|
||||
- **ScanSet / Image Catalog v1** : acquisitions, images logiques, provenance et assets SHA-256 persistants et paginés
|
||||
- **Capture / Asset Provenance v1** : Captures par ScanSet, associations source/dérivé
|
||||
et sélection explicite d'une image logique — PASS / FROZEN
|
||||
- **Découverte et planification de campagne bornées** : racines explicites,
|
||||
plan metadata-only et exécution par groupes via S3-E — PASS / FROZEN ; campagne
|
||||
A6000 réelle validée sur 953 ARW + 953 JPEG MPF
|
||||
- **Feature Store v1/v2** : ORB U8×32, SIFT/RootSIFT F32×128 et lecture typée bornée
|
||||
- **Image View** : vues triées et filtrées pour la TUI
|
||||
- **Task** : moteur de tâches avec pause/reprise, annulation et séquences
|
||||
- **Task Checkpoint v1** : snapshot durable, protocole `.chk.next` → SQLite →
|
||||
`.chk` sous verrou par tâche, et reprise sûre
|
||||
- **Project Database v23** : overlay optique additif au-dessus de la fondation
|
||||
v22 PASS / FROZEN ; profils de boîtier et d'objectif, configurations
|
||||
body+lens+focale, affectations campagne/Capture et calibrations exactement
|
||||
compatibles, sans inférence ni backfill — IMPLEMENTED / VALIDATED / REVIEWED
|
||||
- **[Photo Quality Triage](docs/architecture/photo_quality_triage.md)** : métriques JPEG
|
||||
- **[Calibration Bootstrap v1](docs/architecture/calibration_bootstrap.md)** :
|
||||
import borné d'une calibration optimisée et traçable avant le Sparse SfM à
|
||||
calibration connue — PASS / FROZEN ; ni
|
||||
auto-calibration interne ni EXIF comme source de calibration scientifique
|
||||
- **Exécution durable de campagne d'acquisition** : tâche générique à requête
|
||||
typée immuable, confirmations `CALLER_EXPLICIT`, curseur et correspondance
|
||||
tâche/groupe→Capture persistants ; un groupe S3-E par séquence, reprise par
|
||||
la registry, la Queue et le Resource Governor existants
|
||||
- **Sparse SfM Gates C/D/E** : géométrie calibrée, noyau incrémental et Bundle
|
||||
Adjustment final par composante, tous PASS / FROZEN
|
||||
- **Sparse SfM Gate F** : orchestration durable, runtime gouverné et publication
|
||||
atomique, PASS / FROZEN
|
||||
- **MVS-M1** : frontière externe OpenMVS v2.4.0
|
||||
`InterfaceCOLMAP`/`DensifyPointCloud`, export COLMAP déterministe (OpenCV
|
||||
undistortion, observations transformées et tracks réels), texte exporté en
|
||||
flux et tracks indexés sans rescanner quadratiquement les observations ; espace
|
||||
de travail privé neuf par invocation sous le staging appelant, sans réemploi ;
|
||||
identité dense liée à la reconstruction de base, au jeu source, au
|
||||
`calibration_scope_identity` historique, au binding numérique MVS, au backend
|
||||
et aux paramètres ; `L3DMDID2` v2 (220 octets) et binding `L3DMCAL1` v1 ; PLY
|
||||
OpenMVS binaire little-endian validé (en-tête <= 1 MiB en octets bruts,
|
||||
LF/CRLF acceptés, CR seul malformé rejeté, ligne <= 64 KiB), fusionné en
|
||||
mode 0 — PASS / FROZEN
|
||||
- **Geometric Verification Model** : identité, masque d'inliers et modèle 3×3
|
||||
persistants pour les policies Verifier v1/v2 historiques et v3 courante
|
||||
- **Geometric Verifier v3** : Fundamental USAC/MAGSAC, reprise et lots resource-aware
|
||||
- **Internal Parallelism + Compute Resources v1** : parallélisme interne borné,
|
||||
sorties canoniques et admission Governor — PASS / FROZEN
|
||||
- **Task Kind Registry** : identité métier durable et reconstruction runtime explicite
|
||||
- **Recovery projet** : reprise automatique sélective et bornée des imports récupérables
|
||||
- **Task Queue** : file FIFO avec sélection adaptative et backpressure
|
||||
- **Hardware Profile** : détection des capacités matérielles
|
||||
- **Resource Snapshot** : capture instantanée des ressources
|
||||
- **Resource Governor** : arbitrage centralisé des budgets et réservations
|
||||
- **TUI observatoire / centre de contrôle** : modèle de vue pur et borné,
|
||||
observation coalescée, progression durable/ETA honnête, écrans Tasks,
|
||||
Resources, Optique et SSD, avec ncurses exclusivement sur le thread principal
|
||||
— CURRENT / VALIDATED OPERATIONAL
|
||||
- **Contrôleur SSD externe optionnel** : frontière physique UDisks2/GDBus,
|
||||
identité Drive+labels+UUIDs, drain sûr et capacités de contrôle exactes ; son
|
||||
état physique est enregistré auprès du Governor, seul orchestrateur des
|
||||
leases scratch de production — CURRENT / VALIDATED OPERATIONAL
|
||||
|
||||
The current Project DB schema is **v27**.
|
||||
### Intégration réelle validée
|
||||
|
||||
The current head is additive:
|
||||
Sony A6000 et Samsung S21 FE sont des preuves de validation de la chaîne
|
||||
générique. Ils ne définissent ni l'identité produit, ni un profil caméra
|
||||
hardcodé, ni une limite de CPU ou de dataset.
|
||||
|
||||
```text
|
||||
v22 Selected scientific execution foundation
|
||||
v23 Generic optical-context overlay
|
||||
v24 raw.develop.batch/1 persistence
|
||||
v25 features.extract.batch/1 persistence
|
||||
v26 Capture geometric state and exact calibration applicability
|
||||
v27 Generic bounded exact-token focus domains
|
||||
```
|
||||
- **Intégration multi-campagne A6000 + S21 FE Engine Bay** : PASS — les plans
|
||||
réels A6000 (953 paires confirmées `CALLER_EXPLICIT`) et Samsung SM-G990B
|
||||
(3544 JPEG singleton) ont été validés dans deux ScanSets d'un même projet
|
||||
temporaire, avec exécution durable, Governor/Queue et reprise sans Capture
|
||||
dupliqué. Les campagnes réelles actuellement évaluées sont
|
||||
`CALIBRATION_UNAVAILABLE`, donc le Sparse SfM réel est
|
||||
`BLOCKED_BY_KNOWN_CALIBRATION_DATA` : ce n'est ni un échec logiciel, ni un
|
||||
rejet de qualité, ni une autorisation d'importer une pseudo-calibration. La
|
||||
suite reste le pipeline scientifique aval, selon la
|
||||
[roadmap canonique](docs/roadmap/roadmap.md).
|
||||
|
||||
Earlier schema versions remain valid historical contracts where their own documentation says so.
|
||||
No migration silently reinterprets historical scientific identities.
|
||||
### Plus tard / différé
|
||||
|
||||
### Current production task inventory
|
||||
|
||||
The production registry currently contains **16 Task kinds**.
|
||||
|
||||
All production Tasks pass through the existing Task -> Queue -> Resource Governor execution model.
|
||||
The Queue has one active callback at a time; Tasks may use bounded internal participants when their
|
||||
contract and measured scaling justify it.
|
||||
|
||||
### Resource policy
|
||||
|
||||
The canonical operational objective is:
|
||||
|
||||
```text
|
||||
MAXIMUM SAFE USEFUL THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF
|
||||
```
|
||||
|
||||
Lardon3D first preserves the interactive host reserve required for the desktop, Firefox, audio and
|
||||
light interactive use. Safe and useful resources beyond that reserve belong to the active workload.
|
||||
|
||||
On the current validation host, the normal observed outcome is approximately:
|
||||
|
||||
```text
|
||||
16 logical CPUs total
|
||||
4 logical CPUs reserved for interactive host use
|
||||
12 logical CPUs available to the compute pool
|
||||
~3 GiB MemAvailable preserved as the hard RAM reserve
|
||||
Radeon 780M UMA available to validated and useful GPU backends
|
||||
```
|
||||
|
||||
These are **reference-host observations, not portable product constants**. The Resource Governor
|
||||
derives usable capacity from the current host, affinity, topology, memory and pressure state.
|
||||
|
||||
A long-running CPU1 or batch1 path is acceptable only when serialism, a measured scaling knee,
|
||||
memory, I/O, GPU execution or another concrete constraint justifies it. Per-item atomicity does not
|
||||
imply cross-item serialization.
|
||||
|
||||
## Validated foundations
|
||||
|
||||
The following major foundations are implemented and validated at their documented boundaries:
|
||||
|
||||
- **Project / persistent lifecycle**
|
||||
- **Import and ScanSet / Image Catalog**
|
||||
- **Capture / Asset Provenance v1 — PASS / FROZEN**
|
||||
- **Bounded acquisition discovery and campaign execution — PASS / FROZEN**
|
||||
- **Photo Quality Triage / Acquisition Selection — PASS / FROZEN**
|
||||
- **Selected Scientific Execution — PASS / FROZEN**
|
||||
- **Feature Store v1/v2**
|
||||
- ORB U8x32
|
||||
- SIFT / RootSIFT F32x128
|
||||
- bounded typed readers
|
||||
- **Visual Index v1**
|
||||
- **Candidate Pair generation**
|
||||
- **Matcher v1**
|
||||
- ORB CPU / validated Vulkan hot path
|
||||
- SIFT / RootSIFT CPU
|
||||
- **Geometric Verification Model**
|
||||
- **Geometric Verifier v3**
|
||||
- **Track Model / Track Builder v1 — PASS / FROZEN**
|
||||
- **Sparse SfM Gates C/D/E/F/G — PASS / FROZEN**
|
||||
- **Phase H v1 incremental reconstruction — PASS / FROZEN**
|
||||
- **MVS-M1 external OpenMVS boundary — PASS / FROZEN**
|
||||
- **Task Runtime / checkpoints / recovery**
|
||||
- **Task Queue**
|
||||
- **Task Kind Registry**
|
||||
- **Resource Governor / Compute Governor v2**
|
||||
- **Bounded internal parallelism — PASS / FROZEN**
|
||||
- **ORB Vulkan asynchronous execution — PASS / FROZEN**
|
||||
- **TUI runtime observatory / control center — CURRENT / VALIDATED OPERATIONAL**
|
||||
- **Optional external SSD controller — CURRENT / VALIDATED OPERATIONAL**
|
||||
- **Calibration Bootstrap v1 — PASS / FROZEN**
|
||||
- **Calibration Science v1 — PASS / FROZEN**
|
||||
- **Calibration Tooling v1 — PASS / FROZEN**
|
||||
- **Calibration Solver Preflight v1 — PASS**
|
||||
- **Calibration Evidence Solver v1 — IMPLEMENTED / VALIDATED**
|
||||
- **Calibration Tooling planarity alignment — PASS / FROZEN**
|
||||
- **Project DB v24/v25/v26/v27 operational overlays — IMPLEMENTED / VALIDATED**
|
||||
- raw.develop.batch/1 durable selected-execution path
|
||||
- features.extract.batch/1 durable selected-execution path
|
||||
|
||||
## Real-data validation
|
||||
|
||||
Sony A6000 and Samsung S21 FE campaigns are validation evidence for the generic pipeline. They are
|
||||
not product identities, hardcoded camera profiles, CPU limits or dataset-size limits.
|
||||
|
||||
### Real S21 Tracks
|
||||
|
||||
```text
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
```
|
||||
|
||||
The retained real S21 proof validated the complete pre-SfM chain through Track Builder with the
|
||||
compact Track memory model and deterministic restart semantics.
|
||||
|
||||
### Real A6000 pre-SfM
|
||||
|
||||
```text
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
The retained real A6000 proof uses the selected RAW-derived deterministic representation and
|
||||
completed the pipeline through Geometric Verification and Tracks without replaying already acquired
|
||||
upstream work.
|
||||
|
||||
Final retained counts:
|
||||
|
||||
```text
|
||||
Selected images 689
|
||||
Feature Sets 689
|
||||
Candidate Pairs 38,420
|
||||
Match Results 38,420
|
||||
Applicable GVRs 37,805
|
||||
Verified GVRs 10,952
|
||||
Rejected GVRs 26,853
|
||||
Track Sets 1
|
||||
Tracks 130,714
|
||||
Track observations 318,944
|
||||
```
|
||||
|
||||
The restart proof reused the existing Track Set without duplicating GVR mappings, Track observations
|
||||
or Tracks.
|
||||
|
||||
The real A6000 proof intentionally stopped before:
|
||||
|
||||
```text
|
||||
Sparse SfM
|
||||
Sparse reconstruction
|
||||
Dense / MVS
|
||||
multi-campaign fusion
|
||||
```
|
||||
|
||||
### Calibration status of the historical real campaigns
|
||||
|
||||
The historical S21 and A6000 Engine Bay campaigns currently remain `CALIBRATION_UNAVAILABLE` for
|
||||
the known-calibration Sparse SfM contract.
|
||||
|
||||
This is not a source failure, a quality rejection or permission to infer calibration from metadata.
|
||||
No pseudo-calibration, silent interpolation or inferred calibration identity is allowed.
|
||||
|
||||
Therefore real Sparse SfM for those historical campaigns remains:
|
||||
|
||||
```text
|
||||
BLOCKED_BY_KNOWN_CALIBRATION_DATA
|
||||
```
|
||||
|
||||
Calibration Science v1 defines the protocol for future physically controlled calibration
|
||||
acquisitions. The external Calibration Evidence Solver v1 implements the qualified OpenCV 5.x
|
||||
evidence path. Calibration Tooling v1 validates bounded Science v1 evidence and produces the
|
||||
L3DCALB1 artifact; Calibration Bootstrap v1 imports that artifact. None of these stages turns EXIF
|
||||
into scientific calibration.
|
||||
|
||||
The current missing product boundary is the workflow coordinator that binds the immutable physical
|
||||
session and solver bundle to one exact selected execution and drives Tooling/Bootstrap to `READY`.
|
||||
- publication durable dense/mesh et consommation Task explicite du scratch
|
||||
SSD optionnel ;
|
||||
- vidéo/keyframes et **Capture Guidance / Live Coverage** : analyse et viewer de
|
||||
couverture, suggestions de prises de vue puis assistance live, après
|
||||
reconstruction mature ;
|
||||
- exports et publication live ;
|
||||
- DAG général, pools multiples et parallélisme inter-tâches restent différés.
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
TUI / Project
|
||||
|
|
||||
v
|
||||
bounded Task Queue
|
||||
(one active callback)
|
||||
|
|
||||
v
|
||||
Resource Governor
|
||||
(admission and reservation)
|
||||
|
|
||||
v
|
||||
admitted Task callback
|
||||
(bounded internal participants when justified)
|
||||
|
|
||||
v
|
||||
atomic / persistent scientific publication
|
||||
|
|
||||
v
|
||||
passive snapshot consumers
|
||||
(viewer remains future work)
|
||||
TUI / Projet
|
||||
↓
|
||||
Task Queue bornée (un worker, ordre/backpressure)
|
||||
↓
|
||||
Resource Governor (admission et réservation)
|
||||
↓
|
||||
Task callback admis (parallélisme interne borné si prouvé)
|
||||
↓
|
||||
Résultats atomiques / persistants
|
||||
↓
|
||||
Viewer (consommation passive de snapshots)
|
||||
```
|
||||
|
||||
Core invariants:
|
||||
### Invariants fondamentaux
|
||||
|
||||
- no Task callback starts without a valid active reservation;
|
||||
- the Queue does not own resource policy;
|
||||
- the Resource Governor is the sole production resource authority;
|
||||
- ncurses remains owned by the main thread;
|
||||
- Task estimates and installed sequence contracts remain immutable for their defined lifetime;
|
||||
- buffers, queues, files, threads, participants and temporary work remain bounded;
|
||||
- owner-only durable publication does not imply serial preparation;
|
||||
- swap, zram and external scratch never become admitted RAM;
|
||||
- UMA GPU memory is charged exactly once against host memory.
|
||||
- Aucun callback de tâche sans réservation active validée
|
||||
- La Queue/runtime ne décide jamais des ressources
|
||||
- Le Resource Governor est l'unique propriétaire des budgets
|
||||
- ncurses appartient exclusivement au thread principal
|
||||
- Les estimations de ressources sont immuables
|
||||
- Les buffers et files sont strictement bornés
|
||||
|
||||
## Current TUI
|
||||
### TUI opérationnelle
|
||||
|
||||
The TUI is a validated operational observatory and control center.
|
||||
La TUI sépare le modèle de vue pur du rendu ncurses. Son observateur copie au
|
||||
plus 129 entrées Queue (64 pending, une active, 64 historiques) et coalesce les
|
||||
captures hôte autour d'une seconde ; aucun scan DB ou `/proc` volumineux n'a
|
||||
lieu par frame. La progression scientifique exacte provient seulement des
|
||||
compteurs durables typés. Le taux EWMA et l'ETA restent « calcul » jusqu'à deux
|
||||
intervalles positifs, excluent le préfixe repris et deviennent explicitement
|
||||
indéterminés, stalled ou throttled lorsque l'évidence l'exige.
|
||||
|
||||
It provides bounded observation of:
|
||||
Les tailles supportées sont 100×30 et plus en vue complète, 72×20 en compacte
|
||||
de référence, et jusqu'au minimum 60×15 ; en dessous, seul « Terminal trop
|
||||
petit » est affiché. Les couleurs ont toujours un équivalent textuel/bold/dim.
|
||||
`F1` à `F7` ouvrent aide, projets, import, viewer futur, tâches, ressources et
|
||||
optique. Le segment littéral `F10 SSD` reste visible à 60 colonnes et déclenche
|
||||
uniquement l'action autorisée par le contrôleur. Pendant une saisie, seules
|
||||
Enter, Échap et F10 sont actives ; pendant un import, seules `X` et F10 le sont,
|
||||
et quitter/retour accueil sont explicitement désactivés.
|
||||
|
||||
- Project state;
|
||||
- Tasks and durable progress;
|
||||
- Resource Governor state;
|
||||
- CPU / RAM / swap / GPU information;
|
||||
- optical profiles and explicit calibration selection;
|
||||
- optional SSD state and safe control actions.
|
||||
Ouvrir, fermer ou changer de projet détruit et joint d'abord l'unique Queue,
|
||||
callbacks terminaux inclus, puis ferme Project DB et recrée une Queue vide. Le
|
||||
workflow optique utilise les alias metadata exacts, accepte normalement les
|
||||
objectifs manuels sans EXIF, crée des profils/configurations immuables et exige
|
||||
une affectation/sélection de calibration explicite et exactement compatible.
|
||||
|
||||
The ncurses renderer and input handling remain on the main thread. Runtime observation is bounded
|
||||
and coalesced; the renderer does not scan Project DB or `/proc` extensively per frame.
|
||||
|
||||
The validated layout supports:
|
||||
## Pipeline cible
|
||||
|
||||
```text
|
||||
full layout >= 100x30
|
||||
reference compact 72x20
|
||||
minimum supported 60x15
|
||||
Acquisitions
|
||||
→ catalogue
|
||||
→ features
|
||||
→ index visuel
|
||||
→ paires candidates
|
||||
→ matching
|
||||
→ vérification géométrique
|
||||
→ tracks / SfM
|
||||
→ dense
|
||||
→ mesh
|
||||
→ consolidation
|
||||
→ export
|
||||
```
|
||||
|
||||
Below the minimum, only the bounded terminal-too-small fallback is rendered.
|
||||
|
||||
The optical workflow supports electronic metadata aliases and manual lenses without EXIF. Missing,
|
||||
ambiguous or incompatible calibration remains visible and is never silently guessed.
|
||||
|
||||
## Target pipeline
|
||||
|
||||
```text
|
||||
acquisition
|
||||
-> catalog / Capture / provenance
|
||||
-> quality selection
|
||||
-> selected scientific representation
|
||||
-> features
|
||||
-> visual index
|
||||
-> candidate pairs
|
||||
-> matching
|
||||
-> geometric verification
|
||||
-> tracks
|
||||
-> Sparse SfM
|
||||
-> incremental / multi-campaign reconstruction
|
||||
-> dense / MVS
|
||||
-> mesh
|
||||
-> refinement
|
||||
-> texturing
|
||||
-> consolidation
|
||||
-> export
|
||||
```
|
||||
|
||||
## Planned product areas
|
||||
|
||||
The following areas remain future work and must not be confused with current implementation:
|
||||
|
||||
- durable dense / mesh publication;
|
||||
- full Dense/MVS orchestration;
|
||||
- mesh refinement and texturing;
|
||||
- final export workflow;
|
||||
- viewer;
|
||||
- offline coverage analysis;
|
||||
- suggested supplementary viewpoints;
|
||||
- live camera localization;
|
||||
- live coverage overlay;
|
||||
- A6000 live acquisition integration;
|
||||
- S21 live acquisition integration;
|
||||
- capture guidance;
|
||||
- video ingestion and deterministic keyframe extraction;
|
||||
- explicit Task-owned scratch consumers;
|
||||
- general DAG / dependency scheduling.
|
||||
|
||||
The final product contracts for these areas are being defined separately before implementation.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Documentation index](docs/README.md)
|
||||
|
||||
### Architecture
|
||||
|
||||
- [Architecture overview](docs/architecture/overview.md)
|
||||
- [Vue d'ensemble](docs/architecture/overview.md)
|
||||
- [Runtime](docs/architecture/runtime.md)
|
||||
- [Task system](docs/architecture/task_system.md)
|
||||
- [Task Kind Registry](docs/architecture/task_kind_registry.md)
|
||||
- [Task Queue](docs/architecture/task_queue.md)
|
||||
- [Système de tâches](docs/architecture/task_system.md)
|
||||
- [Registry des types de tâches](docs/architecture/task_kind_registry.md)
|
||||
- [File de tâches](docs/architecture/task_queue.md)
|
||||
- [Resource Governor](docs/architecture/resource_governor.md)
|
||||
- [Bounded internal parallelism](docs/architecture/internal_parallelism.md)
|
||||
- [Resource-aware pipeline](docs/architecture/resource_aware_pipeline.md)
|
||||
- [Queue / runtime / Governor integration](docs/architecture/scheduler_resource_integration.md)
|
||||
- [Reconstruction pipeline](docs/architecture/reconstruction_pipeline.md)
|
||||
- [Persistence](docs/architecture/persistence.md)
|
||||
- [Project Database](docs/architecture/project_database.md)
|
||||
- [Parallélisme interne borné](docs/architecture/internal_parallelism.md)
|
||||
- [Pipeline sensible aux ressources](docs/architecture/resource_aware_pipeline.md)
|
||||
- [Intégration Queue/runtime ↔ Governor](docs/architecture/scheduler_resource_integration.md)
|
||||
- [Pipeline de reconstruction](docs/architecture/reconstruction_pipeline.md)
|
||||
- [Persistance](docs/architecture/persistence.md)
|
||||
- [Base de données projet](docs/architecture/project_database.md)
|
||||
- [Feature Store](docs/architecture/feature_store.md)
|
||||
- [Precision Feature Pipeline v1A](docs/architecture/precision_feature_pipeline.md)
|
||||
- [Visual Index](docs/architecture/visual_index.md)
|
||||
|
|
@ -338,60 +202,36 @@ The final product contracts for these areas are being defined separately before
|
|||
- [Geometric Verification](docs/architecture/geometric_verification.md)
|
||||
- [Geometric Verifier](docs/architecture/geometric_verifier.md)
|
||||
- [Track Model](docs/architecture/tracks.md)
|
||||
- [Track Builder](docs/architecture/track_builder.md)
|
||||
- [Sparse SfM](docs/architecture/sparse_sfm.md)
|
||||
- [Calibration Bootstrap v1](docs/architecture/calibration_bootstrap.md)
|
||||
- [Calibration Publication v2](docs/architecture/calibration_publication_v2.md)
|
||||
- [Calibration Science v1](docs/architecture/calibration_science_v1.md)
|
||||
- [Calibration Solver Preflight v1](docs/architecture/calibration_solver_preflight_v1.md)
|
||||
- [Photo Quality Triage](docs/architecture/photo_quality_triage.md)
|
||||
- [Vulkan ORB Matcher](docs/architecture/vulkan_matcher.md)
|
||||
- [Sparse SfM / Triangulation — Gate A](docs/architecture/sparse_sfm.md)
|
||||
- [Backend Vulkan ORB](docs/architecture/vulkan_matcher.md)
|
||||
- [Viewer](docs/architecture/viewer.md)
|
||||
- [Resource Boundary](docs/architecture/resource_boundary.md)
|
||||
|
||||
### Historical audit records
|
||||
|
||||
- [Global Maintenance Audit](docs/architecture/global_maintenance_audit.md)
|
||||
- [Foundation Review](docs/architecture/foundation_review.md)
|
||||
|
||||
Historical audit records preserve the state and evidence of their checkpoint. Older schema versions,
|
||||
Task counts or resource measurements inside them must not be mechanically modernized.
|
||||
- [Resource Boundary — No New Resource Subsystem](docs/architecture/resource_boundary.md)
|
||||
- [Audit global de maintenance — état consolidé](docs/architecture/global_maintenance_audit.md)
|
||||
- [Revue historique des fondations](docs/architecture/foundation_review.md)
|
||||
|
||||
### Concepts
|
||||
|
||||
- [Scan Sets](docs/concepts/scan_sets.md)
|
||||
- [Visual Index](docs/concepts/visual_index.md)
|
||||
- [Matching and Tracks](docs/concepts/matching_and_tracks.md)
|
||||
- [Reconstruction Layers](docs/concepts/reconstruction_layers.md)
|
||||
- [Geometric Constraints](docs/concepts/geometric_constraints.md)
|
||||
|
||||
Some concept documents are explicitly historical or superseded. Their status header determines
|
||||
whether they are current authority.
|
||||
|
||||
### Development
|
||||
- [Index visuel](docs/concepts/visual_index.md)
|
||||
- [Matching et tracks](docs/concepts/matching_and_tracks.md)
|
||||
- [Couches de reconstruction](docs/concepts/reconstruction_layers.md)
|
||||
- [Contraintes géométriques](docs/concepts/geometric_constraints.md)
|
||||
|
||||
### Développement
|
||||
- [Build](docs/development/build.md)
|
||||
- [Testing](docs/development/testing.md)
|
||||
- [Concurrency](docs/development/concurrency.md)
|
||||
- [Validation-host performance profile](docs/performance/target_hardware.md)
|
||||
|
||||
### Roadmap and audits
|
||||
- [Tests](docs/development/testing.md)
|
||||
- [Concurrence](docs/development/concurrency.md)
|
||||
- [Profil de performance de la machine cible](docs/performance/target_hardware.md)
|
||||
|
||||
### Roadmap
|
||||
- [Roadmap](docs/roadmap/roadmap.md)
|
||||
- [Documentation Inventory Audit](docs/audits/documentation_inventory.md)
|
||||
|
||||
## Build
|
||||
|
||||
Meson and Ninja are the canonical build path.
|
||||
## Build rapide
|
||||
|
||||
```sh
|
||||
CC=clang meson setup build
|
||||
meson compile -C build
|
||||
CC=clang meson setup build --wipe
|
||||
meson compile -C build -j8
|
||||
```
|
||||
|
||||
Build parallelism should use safe host capacity. Do not treat a historical `-j8` or the current
|
||||
reference-host result of approximately `-j12` as a portable constant.
|
||||
|
||||
## Tests
|
||||
|
||||
```sh
|
||||
|
|
@ -399,41 +239,57 @@ meson test -C build --print-errorlogs
|
|||
git diff --check
|
||||
```
|
||||
|
||||
For memory-, lifetime- or concurrency-sensitive changes, use the applicable ASan/UBSan and TSan
|
||||
validation described in [Testing](docs/development/testing.md) and preserve documented third-party
|
||||
sanitizer qualifications.
|
||||
Pour les changements sensibles à la mémoire ou à la concurrence, ajouter ASan/UBSan et TSan.
|
||||
|
||||
## Repository language
|
||||
## Statut
|
||||
|
||||
The canonical language for repository documentation, agent contracts and production source comments
|
||||
is English.
|
||||
Lardon3D est en développement actif. La persistance des tâches, le catalogue,
|
||||
le Feature Store multipasse, le Visual Index ORB, Candidate Pair Generator,
|
||||
Matcher v1, Geometric Verification Model et Geometric Verifier Fundamental v3
|
||||
sont implémentés. Le runtime Feature + Matcher + Verifier emploie des tâches durables,
|
||||
de petits lots, le Resource Governor interactif et un hot path Vulkan ORB exact avec
|
||||
fallback CPU. La feasibility Vulkan SIFT/RootSIFT a été rejetée ; ces deux matchers
|
||||
restent sur OpenCV L2. Track Model/Builder, les primitives géométriques Gate C,
|
||||
le noyau Sparse SfM incrémental Gate D et le Bundle Adjustment final Gate E sont
|
||||
implémentés et validés. L'orchestration Sparse SfM Gate F est PASS / FROZEN ;
|
||||
l'intégration Governor Gate G est **PASS / FROZEN**. MVS-M1 est **PASS / FROZEN** :
|
||||
une frontière OpenMVS v2.4.0 externe et bornée, sans publication dense durable
|
||||
ni MVS complet. Les sources sont liées par SHA-256
|
||||
complet, borné à 1 GiB par fichier régulier (sans budget agrégé de dataset) ; les
|
||||
octets source restent un binding distinct de l'identité dense. Celle-ci lie la
|
||||
reconstruction de base, le jeu d'images source, le `calibration_scope_identity`
|
||||
historique, le binding numérique de calibration MVS `L3DMCAL1` v1, le backend et
|
||||
les paramètres dans `L3DMDID2` v2 (220 octets). Chaque appel utilise un espace de
|
||||
travail privé neuf sous le staging appelant, sans réemploi d'une scène,
|
||||
profondeur, cache ou sortie antérieure. Le DAG, le viewer et les autres étapes
|
||||
denses restent des tickets séparés planifiés.
|
||||
La fondation Project DB v22, `raw.develop` et Calibration Bootstrap v1 reste
|
||||
**PASS / FROZEN**. La tête courante v23 ajoute seulement le contexte optique
|
||||
générique : neuf tables, migration transactionnelle sans backfill, objectifs
|
||||
manuels sans EXIF, configurations multiples par campagne et sélection de
|
||||
calibration exactement compatible. Les migrations de copies réelles S21/A6000
|
||||
ont conservé leurs lignes scientifiques et laissé l'overlay vide. Les campagnes
|
||||
réelles S21 et A6000 Engine Bay sont
|
||||
`CALIBRATION_UNAVAILABLE` par non-identifiabilité scientifique des données de
|
||||
calibration connues ; le Sparse SfM réel reste
|
||||
`BLOCKED_BY_KNOWN_CALIBRATION_DATA`, sans pseudo-calibration ni import inféré.
|
||||
Le Resource Governor ne constitue pas un Resource System générique : il reste
|
||||
l'unique propriétaire des budgets et le seul orchestrateur des leases scratch
|
||||
de production. Le contrôleur SSD UDisks2 est une frontière physique séparée,
|
||||
jamais un second scheduler ou Governor. Les quatorze Task kinds actuels ne
|
||||
consomment encore aucun scratch : l'espace disponible est une capacité
|
||||
observable, pas un usage fabriqué, et scratch/swap ne deviennent jamais de la
|
||||
RAM. La TUI/F10 et cette intégration sont validées opérationnellement. L'audit
|
||||
global est désormais `GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN` : après les builds
|
||||
Clang frais portable/Vulkan, les suites normales 64/64 et 65/65, les sanitizers
|
||||
applicables, TSan et les contrôles ABI, l'unique revue finale indépendante
|
||||
GPT-5.6 SOL/ULTRA a conclu PASS sans finding bloquant. Elle a indépendamment
|
||||
rejoué le build portable, la suite 64/64, une matrice focalisée 15/15 et les
|
||||
76/76 probes strictes C17/C++17 couvrant 19 headers publics modifiés/nouveaux,
|
||||
ainsi que l'ABI, les négatifs de seams production, le SHA du manifest GV et le
|
||||
diff-check. Ce gel clôt la gate de maintenance ; il n'exécute pas à lui seul la
|
||||
tranche scientifique suivante.
|
||||
|
||||
User-interface language is a separate product/localization concern.
|
||||
## Licence
|
||||
|
||||
## Status
|
||||
|
||||
Lardon3D is under active development.
|
||||
|
||||
The persistent pre-SfM pipeline is implemented through Tracks, the Sparse SfM C-G capability is
|
||||
implemented and frozen at its documented boundaries, Phase H v1 is frozen, and MVS-M1 provides the
|
||||
validated external OpenMVS boundary. Full real known-calibration Sparse SfM, dense publication,
|
||||
mesh, texturing, viewer and live capture guidance remain future work.
|
||||
|
||||
The global maintenance checkpoint remains:
|
||||
|
||||
```text
|
||||
global-maintenance-2026-09-01
|
||||
```
|
||||
|
||||
The later real A6000 pre-SfM checkpoint is:
|
||||
|
||||
```text
|
||||
real-a6000-pre-sfm-2026-09-02
|
||||
```
|
||||
|
||||
Future reviews should preserve historical checkpoint meaning and review only the relevant delta
|
||||
unless concrete evidence requires reopening an unchanged FROZEN boundary.
|
||||
|
||||
## License
|
||||
|
||||
Lardon3D is licensed under the MIT License.
|
||||
Projet privé - Tous droits réservés.
|
||||
|
|
|
|||
258
docs/README.md
258
docs/README.md
|
|
@ -1,258 +0,0 @@
|
|||
# Lardon3D Documentation Index
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
DOCUMENTATION_INDEX=CURRENT
|
||||
CURRENT_PROJECT_DB_SCHEMA=v27
|
||||
CURRENT_PRODUCTION_TASK_KINDS=16
|
||||
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
PRODUCT_DEFINITION_V1=PASS/FROZEN
|
||||
PROMPT_TREE=CURRENT
|
||||
CURRENT_NEXT=PHYSICAL_AUTOFOCUS_OPTICAL_APPLICABILITY_VALIDATION
|
||||
```
|
||||
|
||||
This index separates current authority, historical evidence and future product-definition work.
|
||||
|
||||
Repository documentation is read in context: an older schema number, Task count or resource measurement
|
||||
inside a historical checkpoint is not stale merely because the current repository has moved forward.
|
||||
|
||||
## Authority order
|
||||
|
||||
When current-state prose disagrees, use this order:
|
||||
|
||||
1. `AGENTS.md` for repository-wide engineering/agent obligations and protected boundaries.
|
||||
2. Specialized architecture documents for the subsystem contract they own.
|
||||
3. `docs/roadmap/roadmap.md` for current lifecycle / next work.
|
||||
4. `README.md` and architecture overview for concise summaries.
|
||||
5. Historical audits only for the checkpoint they explicitly record.
|
||||
|
||||
Executable source and schema implementation remain authoritative when a documentation excerpt claims to
|
||||
quote an exact API, DDL or constant.
|
||||
|
||||
## Current repository state
|
||||
|
||||
```text
|
||||
Project DB head v27
|
||||
Production Task kinds 16
|
||||
|
||||
v22 selected scientific execution foundation
|
||||
v23 generic optical-context overlay
|
||||
v24 raw.develop.batch/1 persistence
|
||||
v25 features.extract.batch/1 persistence
|
||||
v26 Capture geometric state/applicability
|
||||
v27 generic bounded exact-token focus domains
|
||||
```
|
||||
|
||||
Canonical resource objective:
|
||||
|
||||
```text
|
||||
MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF
|
||||
```
|
||||
|
||||
Reference-host values are measurements, not portable constants.
|
||||
|
||||
## Current real-data checkpoints
|
||||
|
||||
### S21
|
||||
|
||||
```text
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
```
|
||||
|
||||
The retained S21 proof reaches Tracks.
|
||||
|
||||
### A6000
|
||||
|
||||
```text
|
||||
real-a6000-pre-sfm-2026-09-02
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
Retained A6000 counts:
|
||||
|
||||
```text
|
||||
Feature Sets 689
|
||||
Candidate Pairs 38,420
|
||||
Match Results 38,420
|
||||
Applicable GVRs 37,805
|
||||
Verified GVRs 10,952
|
||||
Rejected GVRs 26,853
|
||||
Track Sets 1
|
||||
Tracks 130,714
|
||||
Track observations 318,944
|
||||
```
|
||||
|
||||
The A6000 checkpoint stops before real Sparse SfM and Dense/MVS.
|
||||
|
||||
Sparse SfM capability exists through frozen Gates C-G. Real Sparse SfM on the historical S21/A6000
|
||||
campaigns remains blocked by known-calibration data.
|
||||
|
||||
## Current architecture
|
||||
|
||||
Start here:
|
||||
|
||||
- [Architecture overview](architecture/overview.md)
|
||||
- [Reconstruction pipeline](architecture/reconstruction_pipeline.md)
|
||||
- [Runtime](architecture/runtime.md)
|
||||
- [Persistence](architecture/persistence.md)
|
||||
- [Project Database](architecture/project_database.md)
|
||||
|
||||
### Task / resource execution
|
||||
|
||||
- [Task System](architecture/task_system.md)
|
||||
- [Task Queue](architecture/task_queue.md)
|
||||
- [Task Kind Registry](architecture/task_kind_registry.md)
|
||||
- [Resource Governor](architecture/resource_governor.md)
|
||||
- [Resource Boundary](architecture/resource_boundary.md)
|
||||
- [Resource-aware Pipeline](architecture/resource_aware_pipeline.md)
|
||||
- [Internal Parallelism](architecture/internal_parallelism.md)
|
||||
- [Scheduler / Resource Integration](architecture/scheduler_resource_integration.md)
|
||||
|
||||
Resource authority split:
|
||||
|
||||
```text
|
||||
resource_governor.md runtime policy
|
||||
AGENTS.md engineering / agent obligations
|
||||
target_hardware.md reference-host evidence
|
||||
other documents scoped summaries and links
|
||||
```
|
||||
|
||||
## Scientific pipeline
|
||||
|
||||
- [Photo Quality Triage](architecture/photo_quality_triage.md)
|
||||
- [Feature Store](architecture/feature_store.md)
|
||||
- [Precision Feature Pipeline](architecture/precision_feature_pipeline.md)
|
||||
- [Visual Index](architecture/visual_index.md)
|
||||
- [Candidate Pair](architecture/candidate_pair.md)
|
||||
- [Match Result](architecture/match_result.md)
|
||||
- [Matcher](architecture/matcher.md)
|
||||
- [Geometric Verification Model](architecture/geometric_verification.md)
|
||||
- [Geometric Verifier](architecture/geometric_verifier.md)
|
||||
- [Track Model](architecture/tracks.md)
|
||||
- [Track Builder](architecture/track_builder.md)
|
||||
- [Sparse SfM](architecture/sparse_sfm.md)
|
||||
- [Vulkan ORB Matcher](architecture/vulkan_matcher.md)
|
||||
|
||||
Current production Geometric Verifier lineage:
|
||||
|
||||
```text
|
||||
Fundamental v1 historical valid identity
|
||||
Fundamental v2 historical valid identity
|
||||
Fundamental v3 current production identity
|
||||
```
|
||||
|
||||
Historical verifier results are never relabelled.
|
||||
|
||||
## Calibration
|
||||
|
||||
- [Calibration Science v1](architecture/calibration_science_v1.md)
|
||||
- [Calibration Tooling v1](architecture/calibration_tooling.md)
|
||||
- [Calibration Bootstrap v1](architecture/calibration_bootstrap.md)
|
||||
- [Calibration Solver Preflight v1](architecture/calibration_solver_preflight_v1.md)
|
||||
|
||||
Historical S21/A6000 geometry evidence must not be retroactively given invented calibration.
|
||||
|
||||
## Development
|
||||
|
||||
- [Build](development/build.md)
|
||||
- [Testing](development/testing.md)
|
||||
- [Concurrency](development/concurrency.md)
|
||||
- [Target Hardware](performance/target_hardware.md)
|
||||
|
||||
Build/test parallelism is host-aware. Fixed historical `-j8`, `-j12` or serialized-suite evidence does
|
||||
not become a portable default.
|
||||
|
||||
TSan/OpenCV/TBB qualifications and Vulkan validation remain separate evidence boundaries.
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [Roadmap](roadmap/roadmap.md)
|
||||
|
||||
The roadmap owns the current lifecycle / next-work cursor.
|
||||
|
||||
Historical progress cursors inside other records do not override it.
|
||||
|
||||
## Audits
|
||||
|
||||
- [Documentation Inventory Audit](audits/documentation_inventory.md)
|
||||
- [Global Maintenance Audit](architecture/global_maintenance_audit.md)
|
||||
- [Foundation Review](architecture/foundation_review.md)
|
||||
|
||||
### Historical audit rule
|
||||
|
||||
`global_maintenance_audit.md` and `foundation_review.md` are historical evidence.
|
||||
|
||||
Do not mechanically modernize their old:
|
||||
|
||||
- schema version;
|
||||
- Task count;
|
||||
- compiler/build count;
|
||||
- resource measurement;
|
||||
- checkpoint status.
|
||||
|
||||
The later A6000 checkpoint adds new operational evidence; it does not erase the maintenance checkpoint.
|
||||
|
||||
## Concepts
|
||||
|
||||
- [Scan Sets](concepts/scan_sets.md)
|
||||
- [Visual Index concept](concepts/visual_index.md)
|
||||
- [Matching and Tracks](concepts/matching_and_tracks.md)
|
||||
- [Reconstruction Layers](concepts/reconstruction_layers.md)
|
||||
- [Geometric Constraints](concepts/geometric_constraints.md)
|
||||
|
||||
Concept documents may be historical or explanatory. They do not outrank the current specialized
|
||||
architecture contract.
|
||||
|
||||
## Product definition
|
||||
|
||||
Canonical target-product authority:
|
||||
|
||||
- [Product Definition v1](product/product_definition.md)
|
||||
|
||||
```text
|
||||
PRODUCT_DEFINITION_V1=PASS/FROZEN
|
||||
IMPLEMENTATION_AUTHORIZATION=NO
|
||||
PROMPT_TREE=CURRENT
|
||||
```
|
||||
|
||||
The product definition freezes desired end-product behavior without reopening existing FROZEN
|
||||
scientific contracts. Future implementation is authorized only by the subsequent execution-contract
|
||||
phase under explicit human authority.
|
||||
|
||||
## Repository language
|
||||
|
||||
Canonical target:
|
||||
|
||||
```text
|
||||
DOCUMENTATION_LANGUAGE=ENGLISH
|
||||
SOURCE_COMMENT_LANGUAGE=ENGLISH
|
||||
AGENT_CONTRACT_LANGUAGE=ENGLISH
|
||||
USER_INTERFACE_LANGUAGE=ENGLISH
|
||||
```
|
||||
|
||||
The current-state documentation findings have been remediated in English.
|
||||
|
||||
Some untouched historical/current documents may still require a mechanical language-only normalization
|
||||
pass. Such translation must preserve historical facts and must not silently modernize scientific or
|
||||
lifecycle state.
|
||||
|
||||
Executable UI strings are changed only in an explicitly scoped UI-language implementation pass.
|
||||
|
||||
## Navigation rule for future agents
|
||||
|
||||
Before implementation:
|
||||
|
||||
```text
|
||||
read AGENTS.md
|
||||
-> read README.md
|
||||
-> read this index
|
||||
-> read the specialized architecture contract
|
||||
-> read the current roadmap
|
||||
-> inspect historical audits only when their evidence is relevant
|
||||
```
|
||||
|
||||
Never use an older historical checkpoint as a substitute for current authority.
|
||||
|
|
@ -1,225 +0,0 @@
|
|||
# Calibration AF Study Evidence v1
|
||||
|
||||
**Status: PASS / FROZEN.**
|
||||
|
||||
This boundary is an offline scientific-evidence helper for Calibration Science
|
||||
v2. It does not perform camera calibration, Project DB mutation, autofocus
|
||||
control, calibration selection, or physical-validity decisions.
|
||||
|
||||
## Purpose
|
||||
|
||||
Project DB v27 can persist a physically validated exact-token focus domain, but
|
||||
it deliberately does not decide whether a set of focus observations is
|
||||
physically compatible.
|
||||
|
||||
`Calibration AF Study Evidence v1` supplies the missing measurement artifact:
|
||||
|
||||
```text
|
||||
independent calibration results at observed focus states
|
||||
-> deterministic pairwise image-space projection deltas
|
||||
-> L3DAFST1 artifact
|
||||
-> SHA-256 retained evidence
|
||||
-> later human/scientific applicability decision
|
||||
-> v27 focus-domain creation only after that decision
|
||||
```
|
||||
|
||||
The real A6000 + E PZ 16-50 autofocus applicability remains:
|
||||
|
||||
```text
|
||||
A6000_E_PZ_16_50_AF_APPLICABILITY=BLOCKED_BY_PHYSICAL_VALIDATION
|
||||
```
|
||||
|
||||
This tool cannot change that state by itself.
|
||||
|
||||
## Input contract
|
||||
|
||||
The caller supplies one exact study context:
|
||||
|
||||
- nonzero SHA-256 of the retained body/lens/focal/non-focus geometric context;
|
||||
- decoded/oriented width and height;
|
||||
- 2..64 already acquired calibration samples.
|
||||
|
||||
Each sample contains:
|
||||
|
||||
- `FIT` or `HOLDOUT` role;
|
||||
- one bounded, nonempty opaque exact focus token;
|
||||
- nonzero SHA-256 of that calibration's retained evidence;
|
||||
- exact binary64 `fx, fy, cx, cy, k1, k2, p1, p2`.
|
||||
|
||||
Repeated samples at one focus token are allowed only when they identify distinct
|
||||
calibration evidence. Repeating the exact same `(focus token, calibration
|
||||
evidence SHA-256)` is rejected and cannot masquerade as repeatability evidence.
|
||||
|
||||
The API performs no metadata interpretation. A Sony MakerNote value, for
|
||||
example, must first be converted by the future acquisition/evidence layer into
|
||||
the exact retained token policy selected for that study.
|
||||
|
||||
## Probe model
|
||||
|
||||
Version 1 measures the same frozen pinhole + `k1/k2/p1/p2` forward projection
|
||||
model used by calibration/Sparse SfM.
|
||||
|
||||
Nine normalized ideal rays are evaluated:
|
||||
|
||||
```text
|
||||
centre
|
||||
(0, 0)
|
||||
|
||||
edge probes
|
||||
(-0.7, 0) (+0.7, 0) (0, -0.7) (0, +0.7)
|
||||
|
||||
corner probes
|
||||
(-0.7, -0.7) (+0.7, -0.7)
|
||||
(-0.7, +0.7) (+0.7, +0.7)
|
||||
```
|
||||
|
||||
For every pair of calibration samples, the artifact stores:
|
||||
|
||||
- centre delta in pixels;
|
||||
- maximum cardinal edge-probe delta;
|
||||
- maximum corner-probe delta;
|
||||
- maximum over all nine probes;
|
||||
- whether both samples use the same focus token;
|
||||
- whether the pair crosses FIT/HOLDOUT roles.
|
||||
|
||||
These are measurements, not acceptance thresholds.
|
||||
|
||||
## L3DAFST1
|
||||
|
||||
The binary artifact is little-endian and bounded to 128 KiB.
|
||||
|
||||
It contains:
|
||||
|
||||
```text
|
||||
magic = L3DAFST1
|
||||
artifact version
|
||||
probe-model version
|
||||
study-context SHA-256
|
||||
width / height
|
||||
sample / pair / role counts
|
||||
canonical sample records
|
||||
all canonical pair records and projection metrics
|
||||
```
|
||||
|
||||
Sample order is canonicalized by:
|
||||
|
||||
```text
|
||||
focus-token bytes
|
||||
calibration-evidence SHA-256
|
||||
sample role
|
||||
```
|
||||
|
||||
Therefore caller input order does not change artifact bytes or SHA-256.
|
||||
|
||||
Every floating value is finite binary64. Negative zero is normalized to positive
|
||||
zero before serialization.
|
||||
|
||||
The artifact SHA-256 is suitable as retained evidence for a later v27
|
||||
`lardon3d_optical_focus_domain_v2_create(...)` call only after the physical
|
||||
study has been reviewed and its scientific applicability decision has been made.
|
||||
|
||||
## Summary output
|
||||
|
||||
The API also returns bounded aggregate measurements:
|
||||
|
||||
- FIT/HOLDOUT/sample/pair counts;
|
||||
- same-focus / cross-focus / FIT-HOLDOUT pair counts;
|
||||
- maximum centre, edge-probe, corner-probe and global pairwise delta;
|
||||
- maximum same-focus global delta;
|
||||
- maximum cross-focus global delta;
|
||||
- maximum FIT/HOLDOUT global delta.
|
||||
|
||||
The summary is for inspection and orchestration. It does not encode PASS/FAIL.
|
||||
|
||||
## Non-goals
|
||||
|
||||
This v1 boundary does not:
|
||||
|
||||
- solve ChArUco calibration;
|
||||
- parse EXIF/MakerNotes;
|
||||
- invent physical focus distances;
|
||||
- derive autofocus envelopes;
|
||||
- define an acceptance pixel threshold;
|
||||
- interpolate or extrapolate focus;
|
||||
- open Project DB;
|
||||
- create a v27 focus domain;
|
||||
- make an execution READY.
|
||||
|
||||
The future physical study supplies the evidence needed to decide whether the
|
||||
A6000 + E PZ 16-50 supports one domain, discrete domains, or exact focus only.
|
||||
|
||||
## Materialized Workflow bridge v1
|
||||
|
||||
**Status: PASS / FROZEN.**
|
||||
|
||||
`CALIBRATION_AF_STUDY_WORKFLOW_BRIDGE_V1` is the additive conversion boundary
|
||||
between the already-FROZEN Calibration Workflow materializer and AF-study
|
||||
samples.
|
||||
|
||||
It accepts a `Lardon3DCalibrationWorkflowExternalEvidence` that has already
|
||||
passed the existing immutable-file, solver-bundle and provenance checks. It
|
||||
does not parse `solve.json` or any other solver file again.
|
||||
|
||||
The bridge:
|
||||
|
||||
```text
|
||||
materialized Calibration Workflow evidence
|
||||
+ caller-supplied exact focus token
|
||||
+ caller-supplied FIT/HOLDOUT role
|
||||
-> Lardon3DCalibrationAfStudySample
|
||||
```
|
||||
|
||||
The sample publishes `repeated_parameters[0]` only after checking that all three
|
||||
retained full solves remain exactly equal.
|
||||
|
||||
Its `calibration_evidence_sha256` is independent of the focus token and
|
||||
FIT/HOLDOUT role and binds:
|
||||
|
||||
- target identity;
|
||||
- optical-state identity;
|
||||
- solver executable/configuration identity;
|
||||
- initialization evidence;
|
||||
- validation evidence;
|
||||
- exact oriented dimensions;
|
||||
- exact published binary64 intrinsics.
|
||||
|
||||
Therefore relabelling one calibration result cannot manufacture independent
|
||||
calibration evidence.
|
||||
|
||||
The bridge performs no DB access, metadata interpretation, physical AF decision,
|
||||
thresholding, interpolation or extrapolation.
|
||||
|
||||
## Materialized study assembly v1
|
||||
|
||||
**Status: PASS / FROZEN.**
|
||||
|
||||
`CALIBRATION_AF_STUDY_ASSEMBLY_V1` composes the frozen Workflow bridge and
|
||||
`L3DAFST1` producer so a physical AF study does not need caller-written sample
|
||||
arrays.
|
||||
|
||||
The caller supplies:
|
||||
|
||||
```text
|
||||
study_context_sha256
|
||||
2..64 entries {
|
||||
materialized Calibration Workflow evidence
|
||||
exact opaque focus token
|
||||
FIT or HOLDOUT role
|
||||
}
|
||||
```
|
||||
|
||||
The assembly:
|
||||
|
||||
1. converts every entry through the frozen Workflow bridge;
|
||||
2. requires one exact oriented width/height for the whole study;
|
||||
3. rejects a repeated `calibration_evidence_sha256` even when the caller changes
|
||||
focus token or FIT/HOLDOUT role;
|
||||
4. passes the resulting bounded samples to the frozen AF-study producer;
|
||||
5. returns deterministic `L3DAFST1`, artifact SHA-256 and summary.
|
||||
|
||||
`study_context_sha256` remains caller-retained and explicit. This layer does not
|
||||
derive body/lens/focal/non-focus state from solver metadata and does not decide
|
||||
whether different focus observations belong to one physically valid domain.
|
||||
|
||||
No Project DB access, solver execution, metadata inference, thresholding,
|
||||
interpolation or extrapolation is introduced.
|
||||
|
|
@ -145,32 +145,4 @@ advances the selected-execution cursor itself, and retains its published `image_
|
|||
generic Task progress can become durable. Its reservation owns one CPU thread, one I/O slot and a
|
||||
conservative 2 GiB working allowance for the bounded 40 MP decoder and publication buffers only for
|
||||
the callback lifetime; this operational admission bound is not a scientific Capture-count limit.
|
||||
|
||||
## Calibration Tooling v1
|
||||
|
||||
**PASS / FROZEN.** `calibration_tooling.h` is the bounded production bridge
|
||||
between a completed external `CALIBRATION_SCIENCE_V1` evidence bundle and this
|
||||
importer. It does not solve calibration, parse an unbounded user document, own
|
||||
an acquisition session, or add a persistent schema. The caller supplies a
|
||||
borrowed manifest bounded to 4,096 views and 4,096 selected image bindings,
|
||||
with hashes for the target, optical state and four required evidence artifacts.
|
||||
It declares and checks the frozen ChArUco family, `9 x 7` geometry,
|
||||
`DICT_5X5_100`, 30.000 mm squares, 21.000 mm markers, 270.000 x 210.000 mm
|
||||
active area and at least a 30.000 mm white border; the target hash binds the
|
||||
corresponding immutable physical-evidence record.
|
||||
Each view records an accepted/rejected decision and rejected views retain a
|
||||
nonzero rejection reason; accepted statistics alone are evaluated. Each entry
|
||||
carries the same optical-state manifest hash and remains in the exact selected
|
||||
item order required by the importer (it is not reordered by image id).
|
||||
The validator applies every hard Science v1 acceptance rule before allocating
|
||||
or calling Project DB. The producer writes the exact existing `L3DCALB1` v1
|
||||
little-endian record into caller-owned storage (at most 600,000 bytes), hashes
|
||||
it deterministically, and invokes only
|
||||
`lardon3d_calibration_bootstrap_import(...)`.
|
||||
|
||||
Invalid evidence therefore creates no calibration row and never reaches
|
||||
`READY`. A valid exact retry reuses the frozen importer’s immutable
|
||||
calibrations, scope and selected-execution attachment. Tooling stops at
|
||||
`READY`; it never creates a Sparse SfM Task, and it cannot retro-calibrate the
|
||||
historical S21 campaign.
|
||||
A higher-level selected-execution coordinator remains separate scope.
|
||||
|
|
|
|||
|
|
@ -1,117 +0,0 @@
|
|||
# Calibration Publication v2
|
||||
|
||||
**PASS / FROZEN — additive heterogeneous calibration publication boundary.**
|
||||
|
||||
## Authority and scope
|
||||
|
||||
Calibration Science v2 is the frozen scientific authority. This document
|
||||
defines only the additive heterogeneous publication bridge:
|
||||
|
||||
```text
|
||||
caller-owned validated group evidence
|
||||
-> Calibration Tooling v2
|
||||
-> L3DCALB2
|
||||
-> Calibration Bootstrap v2
|
||||
-> existing SparseCalibrationScope(image_id -> calibration_id)
|
||||
```
|
||||
|
||||
It does not change `L3DCALB1`, Calibration Tooling v1, or Calibration Bootstrap
|
||||
v1. It does not run a solver, define optical applicability thresholds, resolve
|
||||
READY outside publication, or add persistence schema.
|
||||
|
||||
## L3DCALB2 format
|
||||
|
||||
The artifact is deterministic, fixed-width, and little-endian. Integers use
|
||||
the stated unsigned width. Binary64 fields contain IEEE-754 bits; producers
|
||||
canonicalize negative zero and importers reject non-finite values. Native
|
||||
structures, padding, locale text, and trailing bytes are forbidden. The whole
|
||||
artifact is at most 600,000 bytes, with 1..4,096 groups and 1..4,096 entries;
|
||||
the byte-size bound may impose a lower combined maximum. SHA-256 fields must be
|
||||
nonzero.
|
||||
|
||||
```text
|
||||
magic 8 bytes = ASCII "L3DCALB2"
|
||||
format_version u32 = 2
|
||||
model_kind u32 = PINHOLE(1)
|
||||
model_version u32 = 1
|
||||
group_count u32, 1..4096
|
||||
entry_count u32, 1..4096
|
||||
|
||||
for each group in strictly increasing
|
||||
(group_identity_sha256, group_version) order:
|
||||
group_identity_sha256 32 bytes
|
||||
group_version u32, nonzero
|
||||
optical_state_sha256 32 bytes
|
||||
target_sha256 32 bytes
|
||||
solver_executable_sha256 32 bytes
|
||||
solver_configuration_sha256 32 bytes
|
||||
initialization_evidence_sha256 32 bytes
|
||||
validation_evidence_sha256 32 bytes
|
||||
member_count u32, nonzero
|
||||
|
||||
for each member in strictly increasing selected_item_index order:
|
||||
selected_item_index u32
|
||||
image_id u64, nonzero
|
||||
representation_sha256 32 bytes
|
||||
width, height u32, u32, nonzero
|
||||
fx, fy, cx, cy f64, f64, f64, f64
|
||||
k1, k2, p1, p2 f64, f64, f64, f64
|
||||
support_images u32, nonzero
|
||||
support_observations u32, nonzero
|
||||
reprojection_rmse_px finite nonnegative f64
|
||||
maximum_parameter_delta finite nonnegative f64
|
||||
validation_flags u32 = 0x0f
|
||||
```
|
||||
|
||||
The pinhole parameters and four validation bits have exactly the v1 meaning.
|
||||
V2 introduces no new solver threshold. Positive focal lengths and principal
|
||||
points inside the declared dimensions are required. Every selected item index
|
||||
from zero through `entry_count - 1` occurs exactly once across the artifact,
|
||||
and every `image_id` occurs exactly once. Group order and member order are
|
||||
validated, not silently normalized.
|
||||
|
||||
## Provenance and publication
|
||||
|
||||
Each complete serialized group record, including its exact membership and all
|
||||
group-local evidence, is independently SHA-256 hashed. That group digest is the
|
||||
`IMPORTED_TRUSTED` provenance fingerprint for each calibration in that group.
|
||||
The independently supplied whole-artifact SHA-256 protects transport and binds
|
||||
the combined publication without replacing group-local provenance.
|
||||
|
||||
Bootstrap validates the whole artifact and then verifies every entry against
|
||||
the immutable selected execution's exact item index, `image_id`, and current
|
||||
representation asset SHA-256 before its first write. It creates or reuses the
|
||||
existing content-addressed Sparse calibrations per entry, creates or reuses one
|
||||
scope over every selected image across all groups. The original Bootstrap v2
|
||||
import attaches that complete scope and retains its existing public behavior.
|
||||
The separately named `publish_unattached` primitive returns the exact
|
||||
selected-item/image/calibration mapping without attaching the scope; it exists
|
||||
only so Workflow v2 can establish v26 compatibility before READY. A
|
||||
pre-publication error attaches no scope. A later failure may retain immutable
|
||||
calibration evidence but cannot make the execution READY. Exact retries reuse
|
||||
calibrations and the scope and converge on the same attachment.
|
||||
|
||||
## Heterogeneous Workflow v2 composition
|
||||
|
||||
Workflow v2 consumes the L3DCALB2 artifact and a complete caller-owned binding
|
||||
for every selected item. Each binding repeats the selected execution's durable
|
||||
`item_index -> capture_id` relation. Capture identity is never recovered from
|
||||
an image ID, path, SHA-256, filename, artifact group identity, or numeric group
|
||||
ID.
|
||||
|
||||
Before unattached publication, Workflow v2 requires complete observed v26
|
||||
geometric state for every selected Capture. Automatic resolution reports zero
|
||||
exact candidates as `CALIBRATION_REQUIRED` and multiple exact candidates as
|
||||
`SELECTION_REQUIRED`. Explicit existing applicability must be exactly
|
||||
compatible. Explicit publication creates/reuses deterministic profile metadata
|
||||
from the immutable artifact calibration and its group-local provenance, then
|
||||
creates/reuses applicability from the declared exact-state exemplar. It does
|
||||
not create scientific Capture identity or authorize interpolation.
|
||||
|
||||
For every selected item, the final resolved v26 sparse `calibration_id` must
|
||||
equal that image's calibration member in the complete unattached scope. Only
|
||||
after all items pass that equality does Workflow v2 attach the scope and return
|
||||
`READY`. Invalid evidence or a wrong assignment is a distinct non-ready error.
|
||||
Any earlier failure leaves the selected execution unattached; exact retries are
|
||||
deterministic and converge through the immutable Bootstrap/profile/
|
||||
applicability/selection APIs. No Project DB schema change is required.
|
||||
|
|
@ -1,539 +0,0 @@
|
|||
# Calibration Science v1
|
||||
|
||||
**PASS / FROZEN — protocole scientifique pour les futures acquisitions.** Ce
|
||||
document définit le contrat complet qui produit l'évidence d'une calibration
|
||||
connue pour Sparse SfM v1. Il ne définit ni un solveur, ni une interface, ni
|
||||
une tâche, ni un import automatique. Il n'autorise pas à rétro-calibrer une
|
||||
acquisition historique dont l'état optique n'est pas prouvé.
|
||||
|
||||
En particulier, `S21_CALIBRATION_PREFLIGHT=BLOCKED` reste définitif pour la
|
||||
campagne S21 Engine Bay historique : une session physique prise ultérieurement
|
||||
ne peut pas attester l'état de prise de vue historique. Ce document s'applique
|
||||
seulement à une campagne future qui respecte ce protocole depuis le début.
|
||||
|
||||
`HISTORICAL_S21_RETRO_CALIBRATION=FORBIDDEN`.
|
||||
|
||||
## 1. Objet, frontières et modèle gelé
|
||||
|
||||
Une calibration Science v1 produit un modèle intrinsèque pour une *géométrie de
|
||||
représentation* donnée. Elle ne produit ni pose externe, ni point 3D, ni Track,
|
||||
ni Match, ni résultat de reconstruction. Sparse SfM v1 conserve exactement le
|
||||
modèle pinhole connu suivant, en `binary64` :
|
||||
|
||||
```text
|
||||
K = [ fx 0 cx ]
|
||||
[ 0 fy cy ]
|
||||
[ 0 0 1 ]
|
||||
|
||||
r2 = x*x + y*y
|
||||
x_d = x * (1 + k1*r2 + k2*r2*r2) + 2*p1*x*y + p2*(r2 + 2*x*x)
|
||||
y_d = y * (1 + k1*r2 + k2*r2*r2) + p1*(r2 + 2*y*y) + 2*p2*x*y
|
||||
u = fx*x_d + cx
|
||||
v = fy*y_d + cy
|
||||
```
|
||||
|
||||
Les huit paramètres persistés sont `fx`, `fy`, `cx`, `cy`, `k1`, `k2`, `p1`
|
||||
et `p2`. Skew est toujours nul. Les modèles à coefficients supplémentaires,
|
||||
rationnels ou thin-prism sont incompatibles : ils sont rejetés, jamais
|
||||
tronqués. EXIF (notamment `FocalLength`) n'est qu'un indice de regroupement ou
|
||||
d'initialisation du solveur ; il ne peut jamais devenir un paramètre publié.
|
||||
|
||||
La calibration s'applique aux coordonnées pixels après la normalisation
|
||||
d'orientation et avant l'extraction de features, exactement dans le repère
|
||||
consommé par le Feature Store : origine au coin supérieur gauche, `x` vers la
|
||||
droite, `y` vers le bas, coordonnées continues en pixels. Le centre d'un pixel
|
||||
est donc au demi-entier usuel ; aucun décalage caché de demi-pixel n'est permis.
|
||||
`width,height` sont les dimensions décodées/orientées de cette géométrie, pas
|
||||
seulement celles du flux JPEG encodé.
|
||||
|
||||
Ce contrat n'autorise aucun changement de Sparse SfM v1, des observations, du
|
||||
Feature Store ni du format `L3DCALB1` v1.
|
||||
|
||||
## 2. Cible physique canonique : ChArUco planaire V1
|
||||
|
||||
La cible canonique est une planche **ChArUco rectangulaire 9 x 7 cases**,
|
||||
dictionnaire `DICT_5X5_100`, avec cases de **30.000 mm** et marqueurs de
|
||||
**21.000 mm**. La géométrie active fait donc 270.000 mm x 210.000 mm ; elle
|
||||
fournit 48 coins de damier interpolés, plus les identifiants non ambigus des
|
||||
marqueurs. Le rectangle non carré évite les ambiguïtés de rotation des damiers
|
||||
carrés ; les identifiants rendent les vues partielles identifiables.
|
||||
|
||||
Ce choix est scientifique, non une commodité d'implémentation : les coins
|
||||
ChArUco combinent l'identification des marqueurs et la localisation subpixel
|
||||
des coins de damier ; la documentation OpenCV les recommande explicitement aux
|
||||
marqueurs ArUco seuls lorsque la précision de calibration compte. Voir
|
||||
[Detection of ChArUco Boards](https://docs.opencv.org/5.0/tutorials/objdetect/charuco_detection/charuco_detection.html).
|
||||
La documentation de motifs OpenCV fournit également un exemple ChArUco
|
||||
physique à 30 mm et rappelle que plus de features et des éléments plus grands
|
||||
réduisent l'incertitude de détection ; les dimensions ci-dessus fixent ces
|
||||
choix de façon reproductible. Voir
|
||||
[Create Calibration Pattern](https://docs.opencv.org/5.0/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.html).
|
||||
|
||||
La cible doit respecter toutes les conditions suivantes :
|
||||
|
||||
- le fichier générateur de la planche, le dictionnaire, le nombre de cases et
|
||||
les longueurs nominales sont archivés et hachés ;
|
||||
- les longueurs réelles de dix cases réparties (cinq horizontales, cinq
|
||||
verticales) sont mesurées avec une résolution de 0.1 mm ou meilleure ; leur
|
||||
écart maximal à 30.000 mm ne dépasse pas 0.30 mm et leur étendue ne dépasse
|
||||
pas 0.20 mm ;
|
||||
- les mesures et l'identifiant de l'instrument sont archivés ; les seuils de
|
||||
1 % et 0.67 % bornent respectivement l'erreur d'échelle et une déformation
|
||||
différentielle qui rendrait le plan objet déclaré faux ;
|
||||
- l'impression est noire/blanche, mate, à contraste uniforme, sans mise à
|
||||
l'échelle par l'imprimeur ; elle est collée ou imprimée sur un support rigide
|
||||
plan ;
|
||||
- une bordure blanche libre d'au moins une largeur de case (30 mm) entoure la
|
||||
géométrie active ; elle protège les marqueurs du recadrage et des ombres ;
|
||||
- toute planche gondolée, brillante, rayée, tachée, dont un marqueur est
|
||||
illisible ou dont les mesures échouent est invalide.
|
||||
|
||||
Les tolérances de fabrication ne sont pas des tolérances de caméra : elles
|
||||
empêchent que la géométrie objet fournie au solveur contredise la cible
|
||||
physique. Aucune correction logicielle de planche n'est autorisée.
|
||||
|
||||
## 3. État optique et clé de regroupement
|
||||
|
||||
Une calibration n'est réutilisable que pour des images qui ont la même clé
|
||||
scientifique suivante. Une valeur inconnue est une incompatibilité, pas une
|
||||
valeur par défaut.
|
||||
|
||||
| Facteur | Règle Science v1 |
|
||||
|---|---|
|
||||
| Corps/module physique | **MUST match** ; un autre module, appareil ou capteur impose un groupe distinct. |
|
||||
| Objectif | **MUST match** ; changement d'objectif ou adaptateur impose un groupe distinct. |
|
||||
| Focale/zoom optique | **MUST match** à l'état explicitement enregistré ; un zoom est calibré séparément à chaque cran verrouillé. |
|
||||
| Zoom numérique / crop capteur | **Unsupported** sauf profil explicite de crop, dimensions et preuve de coordonnées ; sinon groupe impossible. |
|
||||
| Résolution et aspect ratio décodés | **MUST match**. |
|
||||
| Pipeline de correction optique interne | **MUST match** ; changement de mode ou état inconnu impose groupe distinct. |
|
||||
| Stabilisation | **MUST match** ; stabilisation électronique, recadrage stabilisé, ou état inconnu rend le groupe non calibrable. OIS purement optique peut varier seulement si une étude de répétabilité conforme à §7 le démontre. |
|
||||
| Focus | **MUST match**. Le focus est verrouillé à une distance déclarée ; autofocus est non pris en charge par Science v1, sauf si chaque état discret est verrouillé et validé comme groupe indépendant. |
|
||||
| Ouverture | **MAY vary with proof** pour un objectif à diaphragme, seulement si les essais de répétabilité et les seuils de §7 passent à chaque ouverture déclarée ; sinon une ouverture impose un groupe. Pour un module téléphone à ouverture fixe, elle est enregistrée mais ne crée pas seule un groupe. |
|
||||
| Orientation EXIF | N'identifie pas le matériel, mais **MUST be transformed explicitly** vers le repère Feature Store ; les représentations orientées distinctes nécessitent des paramètres publiés compatibles avec leurs coordonnées. |
|
||||
| Resize/crop après capture | **Unsupported** sauf transformation déterministe, hachée, exécutée avant calibration et features, avec preuve de §8. |
|
||||
| Couleur, balance des blancs, compression JPEG | **Irrelevant** pour le modèle géométrique après preuve que les coordonnées de l'image décodée sont inchangées. |
|
||||
|
||||
La clé de groupe conservée dans l'évidence est donc :
|
||||
|
||||
```text
|
||||
body_or_module_identity
|
||||
objective_identity
|
||||
focal_or_locked_zoom_state
|
||||
focus_mode_and_locked_distance
|
||||
stabilization_and_computational_mode
|
||||
encoded_format_and_decode_pipeline_identity
|
||||
oriented_width_height
|
||||
crop_and_resize_transform_identity
|
||||
in_camera_correction_pipeline_identity
|
||||
```
|
||||
|
||||
Une calibration identique peut être liée à plusieurs `image_id` seulement si
|
||||
la clé complète, les huit paramètres publiés, `provenance_kind` et le même
|
||||
`provenance_fingerprint` (pour un import : le SHA-256 du même artifact
|
||||
`L3DCALB1`) sont identiques. Cette règle exige donc l'égalité de l'identité
|
||||
scientifique complète de calibration, pas seulement de ses intrinsics. Le scope
|
||||
reste une relation par image : il doit couvrir toutes les images sélectionnées,
|
||||
même lorsqu'elles réutilisent une même calibration content-addressed.
|
||||
|
||||
## 4. Protocole physique d'acquisition
|
||||
|
||||
Le protocole vaut pour **un seul groupe optique**. Une variation de la clé de
|
||||
§3 impose une session distincte.
|
||||
|
||||
1. Avant la session, verrouiller et noter le module/objectif, focale ou cran de
|
||||
zoom, mise au point, stabilisation, mode HDR/computationnel, résolution,
|
||||
ratio, format et orientation de sortie. Les modes portrait, night, HDR
|
||||
multi-image, super-résolution, correction de perspective, beauty filter,
|
||||
bokeh, panorama et tout mode dont le pipeline géométrique n'est pas déclaré
|
||||
sont interdits.
|
||||
2. Désactiver l'EIS et tout recadrage stabilisé. Désactiver l'autofocus après
|
||||
verrouillage à la distance de travail déclarée. Si l'appareil ne permet pas
|
||||
ce contrôle ou cette observation, ce groupe ne satisfait pas Science v1.
|
||||
3. Capturer **au moins 40 vues acceptées**, viser **60 vues acceptées**, sans
|
||||
réutiliser de rafale quasi identique. Le minimum est quatre fois les dix
|
||||
bonnes vues pratiques indiquées par la documentation de calibration OpenCV :
|
||||
ce surplus n'est pas une précision statistique prétendue, il réserve des
|
||||
vues pour les quatre zones de champ, les quatre inclinaisons et les
|
||||
validations hold-out ci-dessous.
|
||||
4. Dans chaque région externe (haut-gauche, haut-droit, bas-gauche,
|
||||
bas-droit) définie en §4.1, accepter au minimum six vues dont le centre de
|
||||
la cible appartient à cette région ; au minimum huit vues supplémentaires
|
||||
ont le centre dans la région centrale de §4.1. Une vue compte pour une et
|
||||
une seule région. Cette règle empêche une calibration centralisée de
|
||||
prétendre mesurer la distorsion de bord.
|
||||
5. La cible doit occuper entre **20 % et 80 %** de la plus petite dimension de
|
||||
l'image. Sous 20 %, les 30 mm et les coins subpixel deviennent trop petits
|
||||
face à la quantification ; au-dessus de 80 %, la cible ne contraint plus
|
||||
utilement l'ensemble du champ. Cette bande force à la fois précision locale
|
||||
et couverture globale.
|
||||
6. Au moins 24 vues présentent une inclinaison de normale de plan de **20° à
|
||||
60°**, avec au moins six vues dans chacune des classes 20–35°, 35–50° et
|
||||
50–60°. Les vues frontales peuvent compléter la couverture mais ne comptent
|
||||
pas pour cette diversité. Les angles évitent le cas plan quasi frontal,
|
||||
insuffisant pour séparer focale, point principal et distorsion.
|
||||
7. Utiliser au moins trois bandes de distance, dont la distance médiane est
|
||||
proche de la distance de travail de la future campagne ; chaque bande doit
|
||||
fournir huit vues acceptées. Les bandes sont documentées en mètres mesurés,
|
||||
avec un rapport entre distance maximale et minimale d'au moins 1.5. La
|
||||
variation borne la corrélation entre position et paramètres intrinsèques.
|
||||
8. Chaque vue acceptée contient au moins **16 coins ChArUco interpolés**,
|
||||
répartis dans au moins trois quadrants de la boîte englobante de la cible.
|
||||
Une occlusion est admissible seulement si elle ne masque ni ne déforme les
|
||||
coins retenus ; la cible est sinon rejetée pour cette vue. Les identifiants
|
||||
ChArUco permettent cette sélection sans correspondance ambiguë.
|
||||
9. Les images doivent être nettes au niveau des coins ; une vue est rejetée si
|
||||
le solveur/validateur ne peut pas localiser les coins à une incertitude
|
||||
subpixel finie ou si son erreur RMS de coins avant solve est supérieure à
|
||||
**0.25 px**. Ce seuil est un garde-fou de mesure, inférieur au budget global
|
||||
de 0.50 px de §6 : une vue dont la détection est déjà instable ne doit pas
|
||||
être sauvée par l'ajustement global.
|
||||
10. Éclairage diffus, sans reflets saturés, ombres dures ni scintillement ;
|
||||
exposition qui ne clippe aucun carré ou marqueur utilisé. Une vue est
|
||||
rejetée si plus de **1 %** de ses pixels dans la boîte de la cible sont à
|
||||
0 ou 255 après décodage. Cette limite vise le signal de localisation, pas
|
||||
une métrique esthétique générale.
|
||||
11. Le fichier source est conservé sans rotation, resize, crop, recompression,
|
||||
métadonnée réécrite ou conversion après capture. RAW est accepté seulement
|
||||
si le développement vers la représentation géométrique est déterministe,
|
||||
versionné et identique aux futures images ; JPEG source est accepté
|
||||
lorsqu'il est cette représentation. Toute transformation doit être la même
|
||||
avant calibration et features, et doit posséder le manifeste de §8.
|
||||
|
||||
### 4.1 Régions de cadre : définition normative
|
||||
|
||||
La région est calculée après le décodage et la normalisation d'orientation
|
||||
gelés, dans l'image géométrique de largeur orientée W et hauteur orientée H.
|
||||
W et H sont des entiers strictement positifs. Pour une vue acceptée, prendre
|
||||
tous ses coins ChArUco interpolés retenus, trier leurs paires
|
||||
(corner_id, x, y) par corner_id croissant, et rejeter la vue si un ID est
|
||||
dupliqué, si un x/y n'est pas fini, ou si la liste est vide. Son centre est le
|
||||
centroïde arithmétique calculé en IEEE-754 binary64 :
|
||||
|
||||
x_c = (Σ x_i) / n
|
||||
y_c = (Σ y_i) / n
|
||||
u = x_c / W
|
||||
v = y_c / H
|
||||
|
||||
Les x_i,y_i sont les coordonnées continues après orientation, sans
|
||||
compensation de demi-pixel. Elles suivent donc l'origine haut-gauche, x vers
|
||||
la droite, y vers le bas et les centres de pixels demi-entiers déjà gelés. Le
|
||||
dénominateur est exactement la dimension orientée W ou H, jamais W-1, H-1 ni
|
||||
une dimension JPEG non orientée. Le centre est valide seulement si
|
||||
0 <= u < 1 et 0 <= v < 1 ; autrement, la vue est rejetée avant la
|
||||
classification.
|
||||
|
||||
La région centrale est le rectangle normalisé fermé :
|
||||
|
||||
1/3 <= u <= 2/3 et 1/3 <= v <= 2/3
|
||||
|
||||
Les fractions 1/3 et 2/3 sont normatives : une implémentation doit les évaluer
|
||||
ou les comparer avec la même sémantique IEEE-754 binary64, et ne peut les
|
||||
remplacer par une constante décimale arrondie non spécifiée. Si le centre
|
||||
appartient à ce rectangle, sa classe est la région centrale (4).
|
||||
|
||||
Sinon, la classe externe est déterminée par les comparaisons suivantes :
|
||||
|
||||
u < 1/2, v < 1/2 -> haut-gauche (0)
|
||||
u >= 1/2, v < 1/2 -> haut-droit (1)
|
||||
u < 1/2, v >= 1/2 -> bas-gauche (2)
|
||||
u >= 1/2, v >= 1/2 -> bas-droit (3)
|
||||
|
||||
La priorité est donc centrale, puis externe. Les quatre bords et les quatre
|
||||
coins du rectangle central appartiennent à la classe 4 ; ils priment sur les
|
||||
axes u=1/2 et v=1/2. Hors région centrale, un centre exactement sur u=1/2 est
|
||||
à droite et un centre exactement sur v=1/2 est en bas. Cette partition est
|
||||
mutuellement exclusive et exhaustive pour tout centre valide.
|
||||
|
||||
Le rectangle central, symétrique et d'aire 1/9 de l'image, fournit un
|
||||
échantillon autour de l'axe optique sans absorber la majorité du champ. Les
|
||||
quatre régions externes conservent chacune une obligation indépendante de
|
||||
mesure des bords et des coins. Le choix répond donc à l'observabilité de la
|
||||
distorsion et du point principal, non à une recherche de répartition
|
||||
statistiquement uniforme.
|
||||
|
||||
La classe dépend seulement de ce centre : une cible peut traverser plusieurs
|
||||
régions et ne compte néanmoins que dans une classe. Elle ne remplace ni
|
||||
l'occupation de cible 20 %..80 %, ni les coins minimum, ni la couverture des
|
||||
coins de cible, ni les diversités angulaire et de distance.
|
||||
|
||||
Dans la clé de stratification gelée
|
||||
(quadrant, distance_band, angle_class, source_sha256), le terme historique
|
||||
quadrant désigne cette classe de région de cadre à cinq valeurs 0..4, centrale
|
||||
incluse. L'algorithme « chaque cinquième vue de chaque strate » et les minimums
|
||||
fit/hold-out ne changent pas.
|
||||
|
||||
Les seuils de diversité sont des **HARD REJECTS** : ils sont des contraintes
|
||||
d'observabilité, non des recommandations de confort. La recommandation de base
|
||||
de multiples vues à différentes positions est établie dans
|
||||
[Camera calibration With OpenCV](https://docs.opencv.org/5.0/tutorials/calib3d/camera_calibration/camera_calibration.html).
|
||||
|
||||
## 5. Contrat de solveur
|
||||
|
||||
Science v1 choisit **B : contrat d'évidence indépendant du solveur**. Un seul
|
||||
produit ou une seule version de bibliothèque ne doit pas devenir une identité
|
||||
scientifique implicite. Un solveur est conforme seulement s'il publie son
|
||||
exécutable, sa version, son SHA-256, sa configuration complète et les sorties
|
||||
décrites en §9. Un futur outil peut employer une implémentation conforme ; cela
|
||||
n'ajoute pas OpenCV comme dépendance Lardon3D.
|
||||
|
||||
Le solveur doit :
|
||||
|
||||
- prendre les coordonnées physiques mesurées de la planche et les coordonnées
|
||||
image subpixel des coins identifiés ;
|
||||
- estimer exactement les huit paramètres autorisés et une pose par vue ;
|
||||
- conserver `binary64` pour la géométrie de calcul, l'optimisation, les
|
||||
résidus, les projections indépendantes et les rapports faisant autorité,
|
||||
sous la seule exception de transport d'observations définie ci-dessous ;
|
||||
- ne pas fixer `fx=fy`, le point principal, ni les coefficients de distorsion ;
|
||||
- ne pas optimiser d'intrinsics par image dans un même groupe ;
|
||||
- détecter les systèmes de rang insuffisant, valeurs non finies, paramètres
|
||||
hors domaine, échec numérique et absence de convergence ;
|
||||
- rejeter, documenter et exclure explicitement une vue aberrante plutôt que
|
||||
l'effacer silencieusement ;
|
||||
- reporter les résidus 2D par coin et les métriques par vue ;
|
||||
- produire les paramètres sous forme `binary64` directement mappables aux
|
||||
huit champs `L3DCALB1` ;
|
||||
- exécuter avec graine déterministe ou sans aléa et produire les mêmes octets
|
||||
de rapport lors de répétitions identiques.
|
||||
|
||||
### Exception contrôlée : transport d'observations `binary32`
|
||||
|
||||
Les observations 2D image et 3D objet **passées à une API de solveur externe
|
||||
qualifiée** peuvent être en IEEE-754 `binary32` seulement lorsque toutes les
|
||||
conditions suivantes sont satisfaites :
|
||||
|
||||
1. l'API qualifiée exige cette représentation ;
|
||||
2. la mesure ou représentation source n'est elle-même nativement pas plus
|
||||
précise que `binary32` ;
|
||||
3. la conversion est faite exactement une fois, de façon déterministe ;
|
||||
4. aucune quantification supplémentaire de l'observation ne survient après
|
||||
cette conversion ;
|
||||
5. le bundle de provenance enregistre le chemin de conversion ;
|
||||
6. le bundle mesure et rapporte l'erreur maximale de conversion par
|
||||
coordonnée, dans l'unité de la coordonnée concernée ;
|
||||
7. pour chaque coordonnée image, cette borne mesurée est strictement
|
||||
inférieure au seuil gelé d'équivalence de coordonnées, soit `0.01 px` ;
|
||||
pour chaque coordonnée objet, le bundle rapporte la borne en unité physique
|
||||
de la cible et la représentation source qui justifie l'exception.
|
||||
|
||||
Cette exception est une frontière de transport, non une baisse de précision
|
||||
scientifique. Elle ne s'applique jamais aux paramètres publiés
|
||||
`fx, fy, cx, cy, k1, k2, p1, p2`, à la sortie faisant autorité
|
||||
`cameraMatrix/distCoeffs`, aux poses archivées, aux projections et vecteurs
|
||||
de résidu indépendants, aux RMSE par vue et global, au résidu maximal, à la
|
||||
fraction de résidus élevés, aux métriques fit/hold-out, à
|
||||
`maximum_parameter_delta`, à la validation d'équivalence de coordonnées,
|
||||
aux décisions finales de validation ni aux champs numériques `L3DCALB1`.
|
||||
Toutes ces quantités restent en IEEE-754 `binary64`.
|
||||
|
||||
La justification qualifiée pour l'échelle d'image prise en charge est la
|
||||
suivante : à `8192 px`, l'arrondi `binary32` est borné à environ
|
||||
`0.0005 px` par composante, très en dessous de `0.01 px`. La détection
|
||||
ChArUco qualifiée produit nativement des `Point2f` ; promouvoir ces mesures
|
||||
en `binary64` ne restaure donc aucune précision de mesure. Chaque session
|
||||
doit néanmoins rapporter sa borne réellement mesurée : les seuils de §4, §6,
|
||||
§7 et §8, ainsi que tous leurs calculs de validation en `binary64`, restent
|
||||
inchangés.
|
||||
|
||||
La convergence est valide seulement si la terminaison déclarée par le solveur
|
||||
est une convergence réussie, tous les paramètres/résidus sont finis, et tous
|
||||
les contrôles de §6–§8 passent. Une baisse de coût sans convergence ne suffit
|
||||
pas.
|
||||
|
||||
Les covariance/incertitudes ne sont pas un champ persistant Sparse v1 et ne
|
||||
sont donc pas une condition de publication. Le solveur peut les archiver comme
|
||||
diagnostic ; il ne peut pas les substituer aux seuils objectifs du contrat.
|
||||
|
||||
## 6. Acceptation quantitative
|
||||
|
||||
Les métriques utilisent les résidus euclidiens en pixels dans le repère
|
||||
Feature Store. Les seuils ci-dessous sont normatifs. Ils sont volontairement
|
||||
plus stricts que le seuil Sparse générique ne pourrait l'être : une calibration
|
||||
est une mesure d'entrée commune à tout le pipeline.
|
||||
|
||||
| Mesure | Seuil | Décision | Justification / sens de l'échec |
|
||||
|---|---:|---|---|
|
||||
| Vues acceptées | `>= 40` | HARD REJECT | Quatre fois le minimum pratique de dix vues indiqué par OpenCV ; permet diversité et hold-out. Moins de vues rend les tests de couverture non satisfaisables. |
|
||||
| Coins acceptés | `>= 1600` | HARD REJECT | 40 vues × 40 coins moyens : la valeur exige une information répartie, au-delà du minimum local de 16. |
|
||||
| Occupation de cible | `20 %..80 %` | HARD REJECT par vue | Protège localisation subpixel et couverture de champ. |
|
||||
| Couverture des quatre quarts | `>= 6` vues/quart | HARD REJECT | Empêche l'extrapolation de bord depuis le centre. |
|
||||
| Diversité angulaire | §4.6 | HARD REJECT | Rejette la géométrie planaire quasi frontale dégénérée. |
|
||||
| Diversité de distance | §4.7 | HARD REJECT | Réduit les corrélations intrinsèques/pose. |
|
||||
| RMSE globale | `<= 0.50 px` | HARD REJECT | Budget de précision inférieur au pixel ; au-delà, le modèle v1 ou l'état optique ne représente pas la mesure. |
|
||||
| RMSE par vue | `<= 0.75 px` | HARD REJECT | Une bonne moyenne ne doit pas masquer un sous-ensemble mal modélisé. |
|
||||
| Résidu individuel maximal | `<= 1.50 px` | HARD REJECT | Trois fois le budget global : au-delà, correspondance/corner ou modèle incompatible. |
|
||||
| Fraction de résidus `> 1.0 px` | `<= 1 %` | HARD REJECT | Empêche qu'une longue queue soit absorbée par une moyenne basse. |
|
||||
| Détection de coin pré-solve | `<= 0.25 px RMS` | HARD REJECT par vue | Garantit que le bruit de mesure ne consomme pas à lui seul le budget de modèle. |
|
||||
| Répétition même jeu | §7, maximum de projection `<= 0.10 px` sur les cinq rayons canoniques | HARD REJECT | Le résultat doit être stable à une fraction du budget global. |
|
||||
| Hold-out | RMSE `<= 0.75 px`, max `<= 1.50 px` | HARD REJECT | Détecte une calibration qui ne généralise qu'aux vues ajustées. |
|
||||
|
||||
Le seuil 0.50 px n'est pas présenté comme une loi universelle de la vision :
|
||||
c'est une politique v1 fondée sur la localisation ChArUco subpixel et une
|
||||
exigence stricte de cohérence pixel à pixel. Le seuil par vue de 0.75 px et le
|
||||
maximum de 1.50 px sont respectivement 1.5× et 3× ce budget ; leurs rôles sont
|
||||
de détecter l'hétérogénéité et les résidus grossiers. Tout échec signifie que
|
||||
le groupe est incompatible avec le modèle v1, le protocole ou les données ; il
|
||||
ne se corrige jamais par assouplissement des seuils.
|
||||
|
||||
Les avertissements, sans publication bloquée, sont seulement : RMSE globale
|
||||
dans `(0.40, 0.50] px`, ou nombre de vues dans `[40, 59]`. Ils exigent une note
|
||||
de revue dans l'évidence ; ils ne modifient pas les règles HARD REJECT.
|
||||
|
||||
## 7. Répétabilité et sens des quatre flags
|
||||
|
||||
L'évidence de validation doit rendre `validation_flags=0x0f` objectif :
|
||||
|
||||
1. **Convergence (`0x01`)** : le solveur a une terminaison réussie, tous les
|
||||
paramètres et résidus sont finis, et §6 est satisfait.
|
||||
2. **Support non dégénéré (`0x02`)** : le jeu satisfait exactement les règles
|
||||
de nombre de vues, coins, quarts, occupation, angles et distances de §4 et
|
||||
§6. Un solveur qui annonce simplement un rang non nul ne peut pas remplacer
|
||||
ces vérifications de protocole.
|
||||
3. **Stabilité déterministe (`0x04`)** : exécuter trois fois le solveur sur les
|
||||
mêmes octets, même configuration et même ordre donne les mêmes huit valeurs
|
||||
`binary64` et les mêmes listes de vues acceptées/rejetées. Le jeu complet
|
||||
satisfait §4 et §6. Pour le contrôle de généralisation, trier les vues par
|
||||
`(quart_de_centre, bande_distance, classe_angle, sha256_source)` et affecter
|
||||
chaque cinquième vue de chaque strate au hold-out ; les autres forment le
|
||||
fit. Le fit a au moins 32 vues et au moins quatre vues dans chacun des quatre
|
||||
quarts ; le hold-out a au moins huit vues. Ces sous-jeux n'ont pas à répéter
|
||||
le minimum global de 40 vues, qui reste une règle du jeu complet. Ajuster le
|
||||
fit, évaluer le hold-out avec les seuils hold-out de §6, puis comparer le
|
||||
fit et le jeu complet par la métrique ci-dessous.
|
||||
|
||||
La métrique unique de stabilité est le `maximum_parameter_delta` gelé dans
|
||||
chaque entrée `L3DCALB1`, exprimé en pixels : pour chacune des cinq directions
|
||||
normalisées `[(0,0),(-0.7,-0.7),(0.7,-0.7),(-0.7,0.7),(0.7,0.7)]`, appliquer
|
||||
le modèle direct de §1 dans chaque calibration, calculer la norme L2 des deux
|
||||
pixels projetés, puis prendre le maximum des cinq normes. Ce maximum doit
|
||||
être `<= 0.10 px`. Ainsi le champ ne compare pas naïvement des coefficients
|
||||
de distorsion et son unité reste compatible avec le champ artifact existant.
|
||||
4. **Équivalence de coordonnées (`0x08`)** : le manifeste et le test de §8
|
||||
passent pour chaque représentation orientée du groupe.
|
||||
|
||||
Les répétitions utilisent les mêmes images sans modification ; une session
|
||||
physique distincte est une validation de surveillance recommandée avant chaque
|
||||
campagne majeure, mais n'est pas requise pour publier un groupe. Si elle est
|
||||
réalisée, elle doit passer les mêmes seuils et la comparaison de 0.10 px.
|
||||
|
||||
## 8. Preuve obligatoire d'équivalence de coordonnées
|
||||
|
||||
Avant l'import, le producteur d'artifact doit créer un manifeste par
|
||||
représentation qui établit la transformation exacte :
|
||||
|
||||
```text
|
||||
octets source hachés
|
||||
→ décodeur identifié/versionné
|
||||
→ application EXIF documentée
|
||||
→ orientation normalisée
|
||||
→ crop/resize déterministe, s'il existe
|
||||
→ image géométrique de calibration
|
||||
→ convention Feature Store `image_width,image_height` et repère de keypoints
|
||||
```
|
||||
|
||||
Le test reproductible est le suivant :
|
||||
|
||||
1. prendre au moins 20 coins ChArUco répartis sur centre, quatre bords et
|
||||
quatre coins de chaque **vue de calibration** de validation ;
|
||||
2. pour chaque coin, encoder son pixel solver et son pixel après la même chaîne
|
||||
de décodage/orientation que celle qui définit la géométrie Feature Store ;
|
||||
3. vérifier `abs(dx) <= 0.01 px` et `abs(dy) <= 0.01 px` pour chaque point ;
|
||||
4. vérifier l'égalité exacte de `width,height` entre manifeste et l'image
|
||||
géométrique décodée/orientée ;
|
||||
5. vérifier SHA-256 de la représentation source. Après publication des
|
||||
Features de campagne, vérifier séparément que chaque Feature File porte ces
|
||||
mêmes `image_width,image_height` et le SHA-256 de l'image source ; aucun hash
|
||||
de Feature File ne fait partie de `L3DCALB1` v1 ;
|
||||
6. répéter pour les transformations EXIF présentes : identité, 90°, 180° et
|
||||
270°. Toute orientation non testée ne peut pas appartenir au scope.
|
||||
|
||||
Les rotations sont transformées dans le plan image avant calibration : le
|
||||
solveur ne calibre jamais des coordonnées JPEG encodées non orientées alors que
|
||||
les features sont orientés. Row stride/padding sont hors sujet après décodage ;
|
||||
la conversion de couleur est également hors sujet si, et seulement si, elle ne
|
||||
change ni dimensions ni coordonnées. Crop, resize et réencodage ne sont admis
|
||||
que lorsque leur transformée affine/discrète exacte fait partie du manifeste,
|
||||
est appliquée aux images de calibration et de campagne, et réussit ce test.
|
||||
|
||||
Cette procédure est la seule base autorisée pour `0x08`; une simple inspection
|
||||
visuelle, un EXIF Orientation, ou une égalité de ratio d'image ne suffisent pas.
|
||||
|
||||
## 9. Bundle de provenance et L3DCALB1
|
||||
|
||||
Le producteur conserve un bundle immuable, puis place ses quatre SHA-256 dans
|
||||
`L3DCALB1` v1. Les contenus minimaux sont :
|
||||
|
||||
| Digest L3DCALB1 | Contenu canonique haché |
|
||||
|---|---|
|
||||
| `solver_executable_sha256` | binaire exact du solveur, version, plateforme et SHA-256 du binaire. |
|
||||
| `solver_configuration_sha256` | modèle huit paramètres, paramètres numériques, règles d'outliers, ordre des entrées, graines et version de dictionnaire ChArUco. |
|
||||
| `initialization_evidence_sha256` | manifeste de cible, mesures physiques, identifiant de planche, état optique de groupe, liste/sha des vues et initialisation effectivement utilisée. |
|
||||
| `validation_evidence_sha256` | résidus par coin et vue, vues rejetées/motifs, contrôles §4–§8, répétitions, partitions, projections de comparaison, chemin/bornes de conversion d'observations si §5 s'applique, et résultat flag par flag. |
|
||||
|
||||
Le bundle doit en outre archiver : fichier générateur de cible et son SHA,
|
||||
photographies originales de calibration et SHA, snapshots EXIF complets,
|
||||
manifeste de l'état optique, dimensions décodées/orientées, manifeste des
|
||||
transformations de coordonnées, stdout/stderr du solveur, rapport final des
|
||||
huit paramètres et liste explicite de tout fichier refusé. Les diagnostics de
|
||||
poses peuvent être archivés, mais ne franchissent jamais la frontière
|
||||
Lardon3D scientifique.
|
||||
|
||||
Chaque entrée `L3DCALB1` reste une entrée par `image_id` sélectionné :
|
||||
`image_id`, SHA de représentation, dimensions orientées, huit paramètres,
|
||||
supports, RMSE, delta maximal et flags. L'importeur existant vérifie les SHA,
|
||||
le format, les limites et l'appartenance exacte au snapshot, puis crée/réutilise
|
||||
les calibrations, le scope et l'attachement idempotent. Ce contrat ne le
|
||||
modifie pas.
|
||||
|
||||
## 10. Workflow de campagne et invalidation
|
||||
|
||||
Le protocole physique de calibration doit précéder l'acquisition de campagne,
|
||||
mais l'artifact lié à `image_id` ne peut être produit qu'après que les images
|
||||
futures ont une identité et une représentation durable. Le workflow canonique
|
||||
est donc :
|
||||
|
||||
```text
|
||||
qualifier et verrouiller l'état optique
|
||||
→ mesurer et archiver la planche ChArUco
|
||||
→ capturer la session physique de calibration
|
||||
→ acquérir les images de campagne avec le même état documenté
|
||||
→ créer les identités/représentations image durables
|
||||
→ Features (puis contrôle dimensions/SHA des Feature Files)
|
||||
→ solves + répétitions + preuve de coordonnées sur cette géométrie
|
||||
→ bundle immuable + L3DCALB1 lié aux image_id sélectionnés
|
||||
→ import de calibration et scope immutable
|
||||
→ Matching → GV → Tracks → Sparse SfM
|
||||
```
|
||||
|
||||
Une seconde session juste après est recommandée lorsque l'équipement le permet ;
|
||||
elle est requise après toute mise à jour d'OS/appareil photo, changement de
|
||||
module/objectif, chute/réparation, changement de focale/zoom/focus, changement
|
||||
de résolution, ratio, HDR/traitement, correction optique, stabilisation,
|
||||
crop/resize ou décodage. L'image de campagne dont l'état ne peut pas être lié à
|
||||
une clé §3 reste `CALIBRATION_UNAVAILABLE`.
|
||||
|
||||
Les appareils à objectifs interchangeables, fixes, téléphones et sorties HDMI
|
||||
peuvent tous utiliser Science v1 sans changer Sparse SfM v1. La différence est
|
||||
leur clé : objectif/focale/ouverture pour un appareil interchangeable ; module,
|
||||
focus, EIS et pipeline computationnel pour téléphone ; capteur, objectif,
|
||||
mode de capture et transformation HDMI pour une chaîne HDMI. Une chaîne HDMI
|
||||
doit calibrer le flux effectivement décodé, pas le capteur théorique.
|
||||
|
||||
## 11. Conditions d'invalidation normative
|
||||
|
||||
Un groupe est inutilisable pour Known Calibration Sparse SfM si l'une de ces
|
||||
conditions est vraie : état optique inconnu, zoom/crop inconnu, autofocus non
|
||||
verrouillé, EIS ou mode computationnel non maîtrisé, format/résolution/ratio
|
||||
différent, module/objectif changé, correction interne différente, target non
|
||||
mesurable ou non plane, vue insuffisante/floue/écrêtée, échec d'un seuil §6,
|
||||
échec de stabilité §7, échec d'équivalence §8, provenance/hash incomplet, ou
|
||||
absence de binding exact image→calibration dans le scope.
|
||||
|
||||
Il n'existe aucune exception « même appareil » ou « focale EXIF identique ».
|
||||
|
||||
## 12. Statut et suite
|
||||
|
||||
`CALIBRATION_SCIENCE_V1=PASS/FROZEN`. La prochaine tranche autorisée est un
|
||||
producteur borné d'évidence/`L3DCALB1` qui applique exactement ce contrat ; il
|
||||
ne doit ni modifier l'importeur ni lancer Sparse SfM. Une campagne historique
|
||||
ne peut pas devenir éligible rétrospectivement.
|
||||
|
|
@ -1,543 +0,0 @@
|
|||
# Calibration Science v2 — heterogeneous optics and adaptive acquisition
|
||||
|
||||
**Status: PASS/FROZEN — generic additive scientific contract.**
|
||||
|
||||
Calibration Science v1 remains `PASS/FROZEN`. Science v2 does not weaken,
|
||||
reinterpret or retroactively replace v1 evidence. It defines the next
|
||||
scientific generation required by the product requirement that a Lardon3D
|
||||
project may contain photographs from heterogeneous cameras, lenses, focal
|
||||
configurations and normal autofocus operation.
|
||||
|
||||
No device-specific autofocus envelope is validated by this document. Numeric
|
||||
focus-domain limits require dedicated physical evidence.
|
||||
|
||||
```text
|
||||
CALIBRATION_SCIENCE_V2=PASS/FROZEN
|
||||
```
|
||||
|
||||
This freezes the generic heterogeneous-optics, adaptive-exposure and
|
||||
applicability contract only. It does not validate a device-specific optical
|
||||
state, focus envelope or calibration. In particular,
|
||||
`A6000_E_PZ_16_50_AF_APPLICABILITY=BLOCKED_BY_PHYSICAL_VALIDATION` remains
|
||||
unchanged until retained physical evidence satisfies the applicable rules.
|
||||
|
||||
## 1. Product/science problem
|
||||
|
||||
A photogrammetry product must not require the operator to remember a rigid
|
||||
exposure recipe or split one physical subject into artificial projects merely
|
||||
because acquisition state changed.
|
||||
|
||||
The required user experience is:
|
||||
|
||||
```text
|
||||
user frames and captures
|
||||
-> Lardon3D records/observes acquisition state
|
||||
-> Lardon3D measures actual image quality
|
||||
-> Lardon3D resolves exact calibration applicability
|
||||
-> Capture remains usable when quality is good
|
||||
-> reconstruction becomes READY only when calibration coverage is complete
|
||||
```
|
||||
|
||||
A normal supported camera body or rectilinear lens is equipment data. A new
|
||||
brand/model name alone must not require source-code modification.
|
||||
|
||||
Source changes are justified only by a genuinely unsupported transport,
|
||||
encoded representation, geometric camera model or required device-state
|
||||
mechanism.
|
||||
|
||||
## 2. Compatibility with Science v1
|
||||
|
||||
The following remain frozen compatibility contracts:
|
||||
|
||||
```text
|
||||
CALIBRATION_SCIENCE_V1=PASS/FROZEN
|
||||
CALIBRATION_TOOLING_V1=PASS/FROZEN
|
||||
CALIBRATION_BOOTSTRAP_V1=PASS/FROZEN
|
||||
CALIBRATION_WORKFLOW_V1=PASS/FROZEN
|
||||
```
|
||||
|
||||
Historical S21 and A6000 Engine Bay campaigns remain:
|
||||
|
||||
```text
|
||||
CALIBRATION_UNAVAILABLE
|
||||
BLOCKED_BY_KNOWN_CALIBRATION_DATA
|
||||
```
|
||||
|
||||
Science v2 does not authorize retro-calibration by inference.
|
||||
|
||||
## 3. Fundamental separation of states
|
||||
|
||||
Science v2 separates three concepts that must never be conflated.
|
||||
|
||||
### 3.1 Capture existence
|
||||
|
||||
A Capture is durable acquisition evidence. Unsupported or unresolved optics do
|
||||
not erase it.
|
||||
|
||||
### 3.2 Photo quality
|
||||
|
||||
Quality describes whether the decoded representation is useful image evidence.
|
||||
It is evaluated from the image itself and may include:
|
||||
|
||||
- sharpness;
|
||||
- defocus;
|
||||
- motion blur;
|
||||
- black/white clipping;
|
||||
- usable shadow/highlight information;
|
||||
- contrast;
|
||||
- texture;
|
||||
- noise or local signal-to-noise evidence when implemented;
|
||||
- downstream feature usability.
|
||||
|
||||
Photo Quality may recommend `GOOD`, `SUSPECT` or `REJECT` without assigning any
|
||||
calibration identity.
|
||||
|
||||
### 3.3 Calibration readiness
|
||||
|
||||
Calibration readiness answers whether a selected scientific representation has
|
||||
exact compatible geometric calibration evidence.
|
||||
|
||||
Therefore this is valid:
|
||||
|
||||
```text
|
||||
QUALITY=GOOD
|
||||
CALIBRATION=CALIBRATION_REQUIRED
|
||||
```
|
||||
|
||||
The image remains in the project.
|
||||
|
||||
## 4. Heterogeneous selected executions
|
||||
|
||||
A single project and one selected scientific execution may contain images from:
|
||||
|
||||
- different camera bodies;
|
||||
- different lenses;
|
||||
- different focal configurations of one zoom;
|
||||
- different validated focus states or focus applicability domains;
|
||||
- different validated aperture applicability states;
|
||||
- different acquisition sessions.
|
||||
|
||||
The scientific requirement is per selected image:
|
||||
|
||||
```text
|
||||
selected image
|
||||
-> exactly one compatible calibration
|
||||
```
|
||||
|
||||
It is not:
|
||||
|
||||
```text
|
||||
selected execution
|
||||
-> one global calibration
|
||||
```
|
||||
|
||||
A truthful READY state requires complete calibration coverage of the exact
|
||||
selected-image set.
|
||||
|
||||
The current Sparse calibration scope already represents members as
|
||||
`image_id -> calibration_id`; v2 should reuse that per-image model unless
|
||||
repository inspection proves a real missing invariant.
|
||||
|
||||
## 5. No silent substitution
|
||||
|
||||
Science v2 preserves:
|
||||
|
||||
```text
|
||||
NO_SILENT_SCIENTIFIC_SUBSTITUTION
|
||||
```
|
||||
|
||||
The following are forbidden unless a later explicit validated v2 transfer model
|
||||
authorizes them:
|
||||
|
||||
- another camera body's calibration;
|
||||
- another lens's calibration;
|
||||
- a nearby focal calibration;
|
||||
- a guessed focus calibration;
|
||||
- a guessed aperture compatibility;
|
||||
- a guessed stabilization/crop/pipeline equivalence;
|
||||
- fuzzy metadata matching that silently changes scientific identity;
|
||||
- interpolation or extrapolation outside a validated model/domain.
|
||||
|
||||
If exact compatibility cannot be proven:
|
||||
|
||||
```text
|
||||
CALIBRATION_REQUIRED
|
||||
```
|
||||
|
||||
If more than one valid choice remains unresolved:
|
||||
|
||||
```text
|
||||
SELECTION_REQUIRED
|
||||
```
|
||||
|
||||
## 6. Exposure and photometric controls
|
||||
|
||||
Purely photometric controls are adaptive acquisition variables.
|
||||
|
||||
Science v2 does not reject an image merely because of the numeric value of:
|
||||
|
||||
- shutter speed;
|
||||
- ISO;
|
||||
- white balance;
|
||||
- exposure compensation;
|
||||
- other controls proven not to change image geometry.
|
||||
|
||||
Examples such as `1/125`, `ISO 800` or `f/8` from historical campaigns are
|
||||
observations, not universal validity thresholds.
|
||||
|
||||
For shutter/ISO/white balance, actual decoded-image evidence decides whether
|
||||
the result is usable. A faster shutter with higher ISO may be preferable to a
|
||||
blurred low-ISO image; a slower shutter may be valid when the camera is stable.
|
||||
|
||||
Missing photometric metadata must not cause fabricated values.
|
||||
|
||||
## 7. Aperture
|
||||
|
||||
Aperture is not automatically classified as purely photometric because some
|
||||
lenses can exhibit focus shift or geometric changes with aperture.
|
||||
|
||||
The product goal remains that the operator does not manually manage scientific
|
||||
grouping.
|
||||
|
||||
Science v2 must allow either:
|
||||
|
||||
1. one calibration applicability proven valid across an aperture domain; or
|
||||
2. distinct automatically classified aperture applicability states.
|
||||
|
||||
Unknown compatibility produces `CALIBRATION_REQUIRED`, not Capture rejection.
|
||||
|
||||
No aperture-domain threshold is frozen without physical validation.
|
||||
|
||||
## 8. Focal length and zoom
|
||||
|
||||
Multiple focal configurations may coexist in one project and one selected
|
||||
execution.
|
||||
|
||||
Exact calibrated focal states are the baseline v2 mechanism.
|
||||
|
||||
Electronic focal metadata may select an exact compatible state only when its
|
||||
identity and trust rules are satisfied.
|
||||
|
||||
Manual lenses or missing metadata remain normal product paths through explicit
|
||||
bounded state assignment.
|
||||
|
||||
Calibration at one focal state is never silently borrowed for another focal
|
||||
state.
|
||||
|
||||
A future continuous focal transfer/interpolation model is allowed only if it is
|
||||
a separately versioned Science v2 model with fit-domain evidence, hold-out
|
||||
validation, deterministic evaluation and hard rejection outside its validated
|
||||
domain.
|
||||
|
||||
## 9. Autofocus and focus applicability
|
||||
|
||||
Autofocus is a normal acquisition mode in Science v2.
|
||||
|
||||
Normal product use must not require the user to lock manual focus for every
|
||||
project merely to satisfy calibration.
|
||||
|
||||
Science v2 supports the following scientific concepts:
|
||||
|
||||
### 9.1 Exact focus state
|
||||
|
||||
A calibration may apply to one exact observed/declared focus state.
|
||||
|
||||
This remains useful for manual lenses, fixed-focus systems and strict
|
||||
measurements.
|
||||
|
||||
### 9.2 Validated focus applicability domain
|
||||
|
||||
A calibration may apply across a bounded autofocus/focus domain only when
|
||||
physical evidence proves that applicability.
|
||||
|
||||
Required evidence includes:
|
||||
|
||||
- exact camera body and objective identities;
|
||||
- exact focal/geometric pipeline state;
|
||||
- explicit focus-state observation representation and provenance;
|
||||
- repeated physical calibration samples across the intended focus domain;
|
||||
- independent hold-out focus states;
|
||||
- comparison in image-space geometric error, not only parameter percentage;
|
||||
- bounded deterministic applicability;
|
||||
- explicit rejection outside the validated domain.
|
||||
|
||||
Historical EXIF/MakerNote values such as Sony `FocusPosition2` or derived
|
||||
`FocusDistance2` may inform experiment design. They are not by themselves
|
||||
physical calibration evidence.
|
||||
|
||||
The implemented generic Stage E foundation represents a validated discrete
|
||||
domain as:
|
||||
|
||||
```text
|
||||
existing exact calibration applicability
|
||||
+ positive domain format/version
|
||||
+ nonzero retained-evidence/provenance SHA-256
|
||||
+ exact complete exemplar state for every non-focus geometry field
|
||||
+ 1..64 explicit opaque observed focus tokens
|
||||
```
|
||||
|
||||
The 64-token limit is an operational persistence/API bound, not a scientific
|
||||
focus range. Domain membership is exact token equality. Tokens have no numeric
|
||||
distance, order, magnitude, adjacency or EXIF interpretation, and the generic
|
||||
machinery performs no interpolation or extrapolation. The target configuration
|
||||
and every non-focus geometric-state and provenance field must exactly match the
|
||||
exemplar. Unknown/incomplete focus remains `CALIBRATION_REQUIRED`.
|
||||
|
||||
An exact applicability and its attached domain retain one applicability and
|
||||
calibration identity. Candidate enumeration deduplicates that identity if both
|
||||
the exact exemplar state and a domain member match. Distinct overlapping
|
||||
applicabilities remain ambiguous and produce `SELECTION_REQUIRED`.
|
||||
|
||||
This generic representation does not itself validate a real device domain.
|
||||
In particular:
|
||||
|
||||
```text
|
||||
A6000_E_PZ_16_50_AF_APPLICABILITY=BLOCKED_BY_PHYSICAL_VALIDATION
|
||||
```
|
||||
|
||||
### 9.3 Discrete focus bands
|
||||
|
||||
If one calibration is not valid across the complete autofocus range, v2 may
|
||||
use multiple validated discrete focus bands or exact focus states.
|
||||
|
||||
Automatic classification is required where trusted observed state permits it.
|
||||
|
||||
Ambiguous overlap must not silently choose a calibration.
|
||||
|
||||
### 9.4 Continuous focus transfer models
|
||||
|
||||
Continuous interpolation across focus is not assumed.
|
||||
|
||||
If future physical evidence justifies it, such a model must be:
|
||||
|
||||
- explicitly versioned;
|
||||
- deterministic;
|
||||
- bounded to a validated domain;
|
||||
- fitted from retained physical evidence;
|
||||
- tested on excluded hold-out focus states;
|
||||
- rejected outside the domain;
|
||||
- incorporated into scientific identity.
|
||||
|
||||
No extrapolation is silent.
|
||||
|
||||
## 10. Stabilization, crop, orientation and processing pipeline
|
||||
|
||||
Only state capable of changing geometry belongs to calibration applicability.
|
||||
|
||||
Examples requiring explicit treatment unless equivalence is proven:
|
||||
|
||||
- electronic stabilization;
|
||||
- geometric crop;
|
||||
- perspective correction;
|
||||
- resize;
|
||||
- computational geometry correction;
|
||||
- in-camera lens correction that changes pixel coordinates;
|
||||
- orientation normalization.
|
||||
|
||||
Purely cosmetic color processing does not create a new geometric state after
|
||||
coordinate equivalence has been proven.
|
||||
|
||||
Unknown geometric state is incompatible until resolved.
|
||||
|
||||
## 11. Observed optical state and provenance
|
||||
|
||||
Science v2 requires a bounded, versioned observed state sufficient to decide
|
||||
calibration applicability.
|
||||
|
||||
Conceptually it may contain:
|
||||
|
||||
```text
|
||||
camera_body_identity
|
||||
lens_identity
|
||||
focal_state
|
||||
focus_state_or_domain_observation
|
||||
aperture_state_when_geometrically_relevant
|
||||
stabilization_and_geometric_pipeline_state
|
||||
decoded_oriented_width_height
|
||||
crop_resize_transform_identity
|
||||
representation/decode_pipeline_identity
|
||||
observation_provenance
|
||||
```
|
||||
|
||||
The exact public ABI and persistence layout are implementation-contract work.
|
||||
|
||||
Important rules:
|
||||
|
||||
- unknown is explicit;
|
||||
- unknown is never replaced with a guessed default;
|
||||
- electronic metadata is evidence with defined trust, not automatic truth for
|
||||
every scientific field;
|
||||
- manual assignment is explicit and durable where metadata cannot identify a
|
||||
state;
|
||||
- exact retry is idempotent.
|
||||
|
||||
## 12. Automatic calibration resolution
|
||||
|
||||
For each selected image:
|
||||
|
||||
```text
|
||||
load exact Capture and representation
|
||||
-> load/derive trusted observed optical state
|
||||
-> enumerate validated compatible calibration applicability
|
||||
-> exactly one: calibration resolved
|
||||
-> none: CALIBRATION_REQUIRED
|
||||
-> multiple unresolved: SELECTION_REQUIRED
|
||||
```
|
||||
|
||||
A selection must remain reviewable and durable.
|
||||
|
||||
Calibration readiness does not rewrite Photo Quality evidence.
|
||||
|
||||
## 13. Artifact / Tooling / Bootstrap evolution
|
||||
|
||||
Science v2 does not require a new artifact merely because a project is
|
||||
heterogeneous.
|
||||
|
||||
Implementation must first inspect and reuse existing primitives.
|
||||
|
||||
Current architecture already has:
|
||||
|
||||
```text
|
||||
SparseCalibrationScope member = image_id -> calibration_id
|
||||
L3DCALB1 entry = per-image intrinsics
|
||||
```
|
||||
|
||||
However Tooling/Workflow v1 binds one global optical-state/provenance bundle.
|
||||
If heterogeneous independent calibration groups cannot be represented without
|
||||
losing provenance, an additive `L3DCALB2` / Tooling v2 / Bootstrap v2 may be
|
||||
defined.
|
||||
|
||||
Any v2 artifact must preserve per-group:
|
||||
|
||||
- optical/applicability identity;
|
||||
- target identity;
|
||||
- solver executable/configuration identity;
|
||||
- initialization evidence;
|
||||
- validation evidence;
|
||||
- model/dimensions;
|
||||
- exact published intrinsics;
|
||||
- exact selected-image membership.
|
||||
|
||||
V1 artifact semantics remain unchanged.
|
||||
|
||||
## 14. READY invariant
|
||||
|
||||
A selected execution is READY only if:
|
||||
|
||||
- every selected image is covered exactly once;
|
||||
- every calibration is scientifically compatible with that image's observed
|
||||
geometric state;
|
||||
- the final calibration scope covers the exact selected image set;
|
||||
- no unresolved or ambiguous selected image remains;
|
||||
- publication/attachment follows the existing atomicity/idempotence contract.
|
||||
|
||||
A project may retain any number of other non-selected or
|
||||
`CALIBRATION_REQUIRED` Captures.
|
||||
|
||||
## 15. Device independence
|
||||
|
||||
Normal equipment onboarding is data-driven.
|
||||
|
||||
Required product properties:
|
||||
|
||||
```text
|
||||
NEW_CAMERA_REQUIRES_CODE_CHANGE=NO
|
||||
NEW_LENS_REQUIRES_CODE_CHANGE=NO
|
||||
MULTIPLE_LENSES_PER_CAMERA=SUPPORTED
|
||||
ZOOM_MULTIPLE_FOCALS=SUPPORTED
|
||||
MULTIPLE_OPTICAL_CONFIGURATIONS_PER_PROJECT=SUPPORTED
|
||||
```
|
||||
|
||||
A genuinely different geometric model, unsupported file format or unsupported
|
||||
transport may require new code. A different manufacturer/model string does not.
|
||||
|
||||
## 16. Acquisition control philosophy
|
||||
|
||||
Science v2 defines what must be observed and proven; it does not require one
|
||||
specific control transport.
|
||||
|
||||
Future adapters should follow:
|
||||
|
||||
```text
|
||||
if hardware permits control:
|
||||
Lardon3D may choose/control settings automatically
|
||||
else if state can be observed:
|
||||
Lardon3D verifies/classifies automatically
|
||||
else:
|
||||
request only the minimum explicit user action required
|
||||
```
|
||||
|
||||
HDMI, USB remote control and device-specific adapters remain separate future
|
||||
implementation layers.
|
||||
|
||||
## 17. A6000 + E PZ 16-50 current evidence
|
||||
|
||||
Historical A6000 engine-bay images provide operational design evidence only.
|
||||
|
||||
Observed facts include:
|
||||
|
||||
- E PZ 16-50mm F3.5-5.6 OSS;
|
||||
- heavy real use of 16-20 mm focal lengths;
|
||||
- AF-S across the historical JPEG set;
|
||||
- many distinct focus positions even within the 16 mm / f/8 subset;
|
||||
- exposure values varied with scene conditions.
|
||||
|
||||
These observations demonstrate that a locked-focus single-state workflow is
|
||||
not an acceptable general product UX for this use case.
|
||||
|
||||
They do not validate any reusable calibration or autofocus envelope.
|
||||
|
||||
Current device-specific state:
|
||||
|
||||
```text
|
||||
A6000_E_PZ_16_50_AF_APPLICABILITY=BLOCKED_BY_PHYSICAL_VALIDATION
|
||||
```
|
||||
|
||||
## 18. Required future physical study
|
||||
|
||||
Before claiming a real validated autofocus domain, capture dedicated ChArUco
|
||||
evidence across representative focus states and repeats while preserving the
|
||||
other relevant geometric state.
|
||||
|
||||
The study must determine whether the lens supports:
|
||||
|
||||
- one validated focus envelope;
|
||||
- multiple discrete focus bands;
|
||||
- exact focus-only calibration; or
|
||||
- a future explicitly validated continuous transfer model.
|
||||
|
||||
Numeric domains and thresholds must come from the Science v2 validation design
|
||||
and retained physical evidence. They are not inferred from historical scene
|
||||
photos.
|
||||
|
||||
## 19. Implementation dependency
|
||||
|
||||
Current order:
|
||||
|
||||
```text
|
||||
Calibration Science v2 design
|
||||
-> heterogeneous optical-state persistence/applicability foundation
|
||||
-> v2 Tooling/Bootstrap/artifact only if required
|
||||
-> heterogeneous Workflow v2 READY proof
|
||||
-> physical AF/optical applicability study
|
||||
-> dedicated calibrated real campaign
|
||||
-> real Sparse SfM
|
||||
```
|
||||
|
||||
Do not start Sparse SfM or Dense/MVS as part of this design tranche.
|
||||
|
||||
## 20. STOP conditions
|
||||
|
||||
Stop rather than guess if:
|
||||
|
||||
- a required geometric state cannot be observed or explicitly assigned;
|
||||
- a proposed compatibility relation lacks physical validation;
|
||||
- an AF/focal/aperture transfer model needs an invented threshold;
|
||||
- a new schema would force inference into historical rows;
|
||||
- heterogeneous calibration would require silently changing a FROZEN v1
|
||||
scientific meaning;
|
||||
- a new camera requires a genuinely different geometric model not covered by
|
||||
the current pinhole/distortion science.
|
||||
|
||||
Uncertainty remains visible as `CALIBRATION_REQUIRED` or
|
||||
`SELECTION_REQUIRED`.
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
# Calibration Solver Preflight v1
|
||||
|
||||
**PASS — sélection d'un futur générateur externe d'évidence ; aucune
|
||||
implémentation n'est introduite ici.** Ce document n'autorise aucune
|
||||
modification de `CALIBRATION_SCIENCE_V1`, de `CALIBRATION_TOOLING_V1`, de
|
||||
`L3DCALB1`, du Project DB ou de Sparse SfM. S21 historique reste exclu.
|
||||
|
||||
## Décision
|
||||
|
||||
La plus petite voie conforme est un exécutable C++ externe, versionné et
|
||||
haché, construit contre **OpenCV 5.0.x** avec `core`, `imgproc`, `imgcodecs`,
|
||||
`objdetect` et `calib`. Il n'est pas lié par Lardon3D, n'écrit pas le Project
|
||||
DB et produit seulement un bundle d'évidence borné. Un adaptateur ultérieur
|
||||
traduit ce bundle vers `calibration_tooling.h`; Lardon3D reste seul producteur
|
||||
`L3DCALB1` et seul appelant de l'importeur.
|
||||
|
||||
Les alternatives évaluées sont : OpenCV ChArUco externe, retenu ; outils tiers
|
||||
généralistes, non retenus faute de contrat public stable pour résidus point par
|
||||
point et flags exacts ; solveur custom, hors scope car OpenCV fournit déjà la
|
||||
détection, la calibration pinhole et les résidus nécessaires.
|
||||
|
||||
## API OpenCV et modèle
|
||||
|
||||
La cible est créée par `cv::aruco::CharucoBoard(cv::Size(9, 7), 0.030f,
|
||||
0.021f, cv::aruco::getPredefinedDictionary(cv::aruco::DICT_5X5_100))`, puis
|
||||
rendue par `generateImage`. La version OpenCV et `legacyPattern=false` sont
|
||||
archivés. La détection emploie `cv::aruco::CharucoDetector::detectBoard` sans
|
||||
matrice caméra au premier passage et archive coins, IDs, marqueurs et paramètres
|
||||
de détection. `CharucoBoard::matchImagePoints` associe ID, pixel et point objet
|
||||
en mètres. Une vue est explicitement rejetée, avec motif, si elle ne satisfait
|
||||
pas Science v1.
|
||||
|
||||
Dans le chemin OpenCV 5.0.x qualifié, la détection ChArUco fournit les
|
||||
observations image en `Point2f` et la planche fournit les observations objet
|
||||
en `Point3f`. `calibrateCamera` reçoit donc ces observations
|
||||
`binary32` : c'est l'exception explicite de transport de
|
||||
`CALIBRATION_SCIENCE_V1`, et non une troncature silencieuse. Le chemin de
|
||||
conversion, l'absence de quantification ultérieure et les bornes maximales
|
||||
mesurées sont archivés pour chaque session : chaque coordonnée image est
|
||||
strictement inférieure à `0.01 px`; chaque coordonnée objet est rapportée
|
||||
dans l'unité physique de la cible, avec la représentation source `Point3f`
|
||||
de la planche qui justifie cette exception.
|
||||
|
||||
La voie qualifiée convertit les données de calibration dans son calcul interne
|
||||
`CV_64F` là où OpenCV 5.0.x l'établit. La `cameraMatrix` et les
|
||||
`distCoeffs` faisant autorité sont `CV_64F`; les huit paramètres publiés,
|
||||
les poses archivées, les projections/résidus indépendants et toutes les
|
||||
métriques de validation restent `binary64`.
|
||||
|
||||
Le solveur appelle la surcharge étendue `cv::calibrateCamera` :
|
||||
|
||||
```text
|
||||
flags = cv::CALIB_FIX_K3
|
||||
criteria = COUNT | EPS, 500, DBL_EPSILON
|
||||
cameraMatrix = Matx33d::eye()
|
||||
distCoeffs = Mat::zeros(5, 1, CV_64F)
|
||||
```
|
||||
|
||||
L'ordre OpenCV est `[k1,k2,p1,p2,k3]`; `CALIB_FIX_K3` fixe le dernier à zéro,
|
||||
donc les quatre autres se mappent directement à Sparse SfM v1. `fx`, `fy`,
|
||||
`cx`, `cy`, `k1`, `k2`, `p1` et `p2` restent libres. Sont interdits :
|
||||
`CALIB_FIX_ASPECT_RATIO`, `CALIB_FIX_PRINCIPAL_POINT`,
|
||||
`CALIB_FIX_FOCAL_LENGTH`, `CALIB_FIX_K1`, `CALIB_FIX_K2`,
|
||||
`CALIB_ZERO_TANGENT_DIST`, `CALIB_FIX_TANGENT_DIST`,
|
||||
`CALIB_RATIONAL_MODEL`, `CALIB_THIN_PRISM_MODEL`, `CALIB_TILTED_MODEL`, les
|
||||
flags de fixage associés, fisheye, QR/LU, EXIF comme intrinsics et calibration
|
||||
par image.
|
||||
|
||||
La surcharge étendue retourne RMS global et par vue. L'outil doit recalculer
|
||||
avec `cv::projectPoints` chaque résidu par coin, maximum, fraction `>1 px` et
|
||||
RMS avec les mêmes poses ; le succès du solveur seul n'est jamais suffisant.
|
||||
Ces API emploient `fx,fy,cx,cy` en pixels et la même distorsion directe que le
|
||||
modèle gelé : aucune conversion de modèle n'est permise.
|
||||
|
||||
## Entrée, sortie et déterminisme
|
||||
|
||||
L'entrée externe est un répertoire immuable d'originaux hachés, manifeste
|
||||
d'état optique, cible/mesures et chaîne de décodage-orientation. Les vues sont
|
||||
triées par SHA-256 source avant détection. Détection et solve sont deux étapes
|
||||
matérialisées (`detection.json` et `solve.json`) d'un seul exécutable : IDs,
|
||||
pixels subpixel, décisions et rejets sont ainsi vérifiables avant les trois
|
||||
solves. Les fichiers ont ordre canonique, nombres `binary64` hexadécimaux
|
||||
pour les quantités faisant autorité, représentation et chemin de conversion
|
||||
`binary32` explicitement archivés pour les observations d'API, tableaux
|
||||
ordonnés et limites déclarées.
|
||||
|
||||
La sortie archive nom/version/SHA de l'exécutable, OS/architecture, build
|
||||
OpenCV et bibliothèques, configuration hachée, identité de session, vues et
|
||||
motifs, points objet/image, résidus, poses diagnostic, huit paramètres,
|
||||
supports, hold-out, trois répétitions et calculs des flags `0x01..0x08`. Les
|
||||
images de campagne, postérieures à la calibration, sont ajoutées seulement par
|
||||
l'adaptateur comme entrées `image_id`/SHA/dimensions dans l'ordre
|
||||
`selected_execution` : elles ne sont jamais des entrées de solveur.
|
||||
|
||||
La preuve initiale est CPU1 : `cv::setNumThreads(1)`, `cv::setRNGSeed` fixé,
|
||||
aucun travail parallèle, `OMP_NUM_THREADS=1`, `OPENBLAS_NUM_THREADS=1` et
|
||||
`MKL_NUM_THREADS=1` lorsqu'applicables. `cv::getBuildInformation`, backend de
|
||||
threads et environnement effectif sont archivés. Les trois exécutions sur même
|
||||
hôte/architecture/octets doivent donner les mêmes huit `binary64`, décisions
|
||||
et rapports canoniques : tout écart bloque Science v1.
|
||||
|
||||
## Équivalence de coordonnées et versionnement
|
||||
|
||||
La géométrie est : origine haut-gauche, `x` droite, `y` bas, pixels continus à
|
||||
centres demi-entiers. Chaque image archive SHA, décodeur/version, EXIF,
|
||||
dimensions avant/après et les équations `0°:(x,y)`, `90°:(H-y,x)`,
|
||||
`180°:(W-x,H-y)`, `270°:(y,W-x)`. Vingt coins par vue repassent dans la
|
||||
chaîne Feature Store ; chaque erreur est `<=0.01 px` et les dimensions orientées
|
||||
doivent être identiques.
|
||||
|
||||
L'hôte de préflight fournit `opencv 5.0.0-9`. La production future épingle un
|
||||
exécutable contre OpenCV 5.0.x avec SHA obligatoire ; le package Arch est bon
|
||||
pour développement, pas l'unique identité de production. Sont archivés en
|
||||
plus : compilateur, flags, build info, bibliothèques/SHA, OS, architecture et
|
||||
politique CPU. Les quatre SHA `L3DCALB1` restent autoritaires.
|
||||
|
||||
## Prochaine tranche et première preuve physique
|
||||
|
||||
La prochaine tranche implémente uniquement l'exécutable externe et ses tests :
|
||||
caméra pinhole/distorsion connues, projections ChArUco, bruit/outliers,
|
||||
récupération, résidus, répétitions, hold-out, entrées invalides et bundle
|
||||
byte-identique. Puis seulement : fabriquer/mesurer la cible, figer l'état
|
||||
optique, acquérir au moins 40 vues, hacher, résoudre trois fois, vérifier
|
||||
Science v1, passer le bundle à Calibration Tooling dans un projet dédié et
|
||||
vérifier `CALIBRATION → READY`. La preuve s'arrête avant Sparse SfM ; S21 ne
|
||||
participe jamais.
|
||||
|
||||
## Current lifecycle update
|
||||
|
||||
The preflight decision above has now been implemented.
|
||||
|
||||
```text
|
||||
CALIBRATION_EVIDENCE_SOLVER_V1=IMPLEMENTED/VALIDATED
|
||||
```
|
||||
|
||||
`tools/calibration_evidence_solver/` contains the external OpenCV 5.x solver selected by this
|
||||
preflight. Its deterministic synthetic CPU1 self-test passes. It remains external to Lardon3D
|
||||
runtime linkage and Project DB mutation.
|
||||
|
||||
The current missing implementation boundary is no longer the solver. It is the higher-level
|
||||
calibration workflow coordinator that binds an immutable physical session plus the complete solver
|
||||
bundle to the exact selected execution, constructs `Lardon3DCalibrationToolingEvidence`, invokes the
|
||||
frozen Tooling/Bootstrap path and reaches `READY` without manufacturing evidence.
|
||||
|
||||
The original preflight text is retained as decision history; this section owns its later lifecycle
|
||||
status.
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
# Calibration Tooling v1
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
CALIBRATION_TOOLING_V1=PASS/FROZEN
|
||||
CALIBRATION_TOOLING_PLANARITY_ALIGNMENT=PASS/FROZEN
|
||||
L3DCALB1_VERSION=1
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
Calibration Science v1 is the scientific authority. This document specializes
|
||||
the bounded operational bridge implemented by
|
||||
`include/lardon3d/calibration_tooling.h` and `src/calibration_tooling.c`.
|
||||
|
||||
The bridge connects:
|
||||
|
||||
```text
|
||||
Calibration Science v1 evidence
|
||||
-> Calibration Tooling v1
|
||||
-> L3DCALB1 v1
|
||||
-> Calibration Bootstrap v1
|
||||
```
|
||||
|
||||
It does not acquire images, run a solver, infer optical state, create
|
||||
reconstruction results, add a Project DB schema version, add a Task kind, or
|
||||
start Sparse SfM.
|
||||
|
||||
## Planarity alignment
|
||||
|
||||
Calibration Science v1 requires a rigid planar physical target and rejects a
|
||||
warped board. It defines no numeric target-flatness tolerance.
|
||||
|
||||
The canonical external session records categorical physical evidence:
|
||||
|
||||
```text
|
||||
planarity PASS <planarity-evidence-sha256>
|
||||
```
|
||||
|
||||
The Science v1 value `0.20 mm` applies to the allowed range of the ten measured
|
||||
30.000 mm squares. It is not a target-flatness threshold.
|
||||
|
||||
`Lardon3DCalibrationToolingEvidence.target_flatness_mm` is retained only to
|
||||
preserve the existing public structure layout. In v1 it MUST be IEEE-754 NaN.
|
||||
Any finite value is rejected so a caller cannot invent a physical measurement
|
||||
or silently create a new scientific threshold.
|
||||
|
||||
The immutable session containing the categorical planarity attestation is bound
|
||||
by the higher-level workflow through `initialization_evidence_sha256`.
|
||||
|
||||
This corrective alignment changes neither Calibration Science v1 nor
|
||||
`L3DCALB1` v1 nor Calibration Bootstrap v1.
|
||||
|
||||
## Bounded evidence
|
||||
|
||||
Tooling continues to validate the frozen Science v1 requirements, including:
|
||||
|
||||
- exact ChArUco 9 x 7 / DICT_5X5_100 target identity;
|
||||
- 30.000 mm squares and 21.000 mm markers;
|
||||
- ten physical square measurements and instrument resolution;
|
||||
- measured free white border of at least 30 mm, supplied explicitly by the session;
|
||||
- immutable target, optical-state, solver and evidence digests;
|
||||
- accepted and rejected view evidence;
|
||||
- field-region, distance and angle diversity;
|
||||
- corner quality, clipping, residual and hold-out evidence;
|
||||
- deterministic repeated parameters;
|
||||
- representative coordinate equivalence;
|
||||
- exact selected-image order and representation SHA-256;
|
||||
- the exact eight-parameter pinhole model;
|
||||
- zero active extra distortion coefficients.
|
||||
|
||||
Successful solver exit alone is never sufficient acceptance.
|
||||
|
||||
## Artifact and Bootstrap
|
||||
|
||||
For valid evidence Tooling produces exactly the existing fixed-width,
|
||||
little-endian `L3DCALB1` v1 artifact and may invoke only the frozen Calibration
|
||||
Bootstrap importer.
|
||||
|
||||
A failed validation reaches neither artifact publication nor Project DB
|
||||
mutation. Exact successful retries converge through the existing immutable
|
||||
Bootstrap contract.
|
||||
|
||||
## Current next boundary
|
||||
|
||||
The external Calibration Evidence Solver v1 is implemented and validated.
|
||||
|
||||
The missing product boundary is the higher-level calibration workflow
|
||||
coordinator. It must consume:
|
||||
|
||||
```text
|
||||
session.l3dcal
|
||||
session.l3dcal.bundle/detection.json
|
||||
session.l3dcal.bundle/solve.json
|
||||
session.l3dcal.bundle/evidence.json
|
||||
session.l3dcal.bundle/producer.json
|
||||
```
|
||||
|
||||
together with the exact selected execution and optical state, then construct
|
||||
the bounded `Lardon3DCalibrationToolingEvidence`.
|
||||
|
||||
`producer.json` binds the exact solver executable SHA-256, canonical solver-configuration SHA-256, exact session SHA-256, OpenCV build identity, CPU1 policy and optical-state SHA-256.
|
||||
|
||||
`detection.json` also retains the exact per-view Science v1 decisions used by the solver: frame region, distance band, hold-out membership, target occupancy, normal angle, measured distance, target-quadrant mask, corner/residual counts, high-residual count, reprojection RMSE and maximum residual. The coordinator consumes these published values and does not reconstruct them from a later solve.
|
||||
|
||||
The coordinator must verify regular bounded files, immutable digests,
|
||||
session/bundle identity, categorical planarity PASS, selected-execution image
|
||||
binding and optical-state equality. It must never manufacture missing evidence.
|
||||
|
|
@ -1,239 +0,0 @@
|
|||
# Calibration Workflow
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
CALIBRATION_WORKFLOW=PASS/FROZEN
|
||||
CALIBRATION_WORKFLOW_INPUT_BOUNDARY_V1=PASS/FROZEN
|
||||
CALIBRATION_WORKFLOW_EVIDENCE_MATERIALIZATION_V1=PASS/FROZEN
|
||||
CALIBRATION_WORKFLOW_SELECTED_EXECUTION_BINDING_V1=PASS/FROZEN
|
||||
CALIBRATION_WORKFLOW_TOOLING_BOOTSTRAP_READY_V1=PASS/FROZEN
|
||||
CALIBRATION_WORKFLOW_V2=PASS/FROZEN
|
||||
CURRENT_WORKFLOW_NEXT=ADAPTIVE_CAPTURE_SETTINGS_AND_AUTOFOCUS_V2_FOUNDATION
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
Calibration Science v1, Calibration Tooling v1 and Calibration Bootstrap v1
|
||||
remain FROZEN scientific and import authorities.
|
||||
|
||||
This workflow is bounded orchestration only. It does not introduce a solver,
|
||||
Project DB schema version, Task kind, Sparse SfM execution or reconstructed
|
||||
scientific evidence.
|
||||
|
||||
## Frozen flow
|
||||
|
||||
```text
|
||||
physical calibration acquisition
|
||||
-> session.l3dcal
|
||||
-> external Calibration Evidence Solver v1
|
||||
-> immutable solver bundle
|
||||
-> campaign-state evidence
|
||||
-> Calibration Workflow
|
||||
-> Calibration Tooling v1
|
||||
-> L3DCALB1 v1
|
||||
-> Calibration Bootstrap v1
|
||||
-> selected execution READY
|
||||
```
|
||||
|
||||
## Input Boundary v1
|
||||
|
||||
`CALIBRATION_WORKFLOW_INPUT_BOUNDARY_V1=PASS/FROZEN`.
|
||||
|
||||
The implementation is exposed through:
|
||||
|
||||
```text
|
||||
include/lardon3d/calibration_workflow.h
|
||||
src/calibration_workflow.cpp
|
||||
```
|
||||
|
||||
Input Boundary v1 performs no Project DB mutation.
|
||||
|
||||
It accepts only bounded regular files and rejects special files and symlinks
|
||||
before potentially blocking reads. File access follows the nonblocking,
|
||||
close-on-exec regular-file discipline.
|
||||
|
||||
The bounded input set is:
|
||||
|
||||
```text
|
||||
session.l3dcal
|
||||
session.l3dcal.bundle/detection.json
|
||||
session.l3dcal.bundle/solve.json
|
||||
session.l3dcal.bundle/evidence.json
|
||||
session.l3dcal.bundle/producer.json
|
||||
L3DCAL_CAMPAIGN_STATE_V1
|
||||
```
|
||||
|
||||
It verifies:
|
||||
|
||||
- regular bounded files;
|
||||
- SHA-256 identities;
|
||||
- strict session syntax;
|
||||
- structurally valid canonical JSON bundle members;
|
||||
- exact session SHA binding through `producer.json`;
|
||||
- decoder/version consistency;
|
||||
- exact optical-state SHA equality;
|
||||
- exact optical-state token equality;
|
||||
- campaign-state identity consistency.
|
||||
|
||||
Malformed JSON, oversize files, symlinks, FIFOs, session digest mismatch and
|
||||
optical-state mismatch are rejected.
|
||||
|
||||
Input Boundary v1 does not:
|
||||
|
||||
- open or mutate Project DB;
|
||||
- construct `Lardon3DCalibrationToolingEvidence`;
|
||||
- call Calibration Tooling;
|
||||
- produce `L3DCALB1`;
|
||||
- invoke Calibration Bootstrap;
|
||||
- change selected-execution state.
|
||||
|
||||
## Evidence Materialization v1
|
||||
|
||||
`CALIBRATION_WORKFLOW_EVIDENCE_MATERIALIZATION_V1=PASS/FROZEN`.
|
||||
|
||||
The implementation is additive:
|
||||
|
||||
```text
|
||||
src/calibration_workflow_materialize.cpp
|
||||
tests/test_calibration_workflow_materialize.cpp
|
||||
```
|
||||
|
||||
It consumes only inputs that first pass Input Boundary v1 and performs no
|
||||
Project DB access or mutation.
|
||||
|
||||
The caller owns bounded arrays for materialized views and coordinate checks.
|
||||
On success the output borrows those arrays and retains:
|
||||
|
||||
- exact target generator SHA-256 and physical target measurements;
|
||||
- measured white border and categorical planarity evidence;
|
||||
- exact optical-state SHA-256;
|
||||
- exact solver executable and configuration SHA-256;
|
||||
- exact accepted/rejected per-view classifications and rejection reasons;
|
||||
- hold-out assignment, frame region, distance band and target coverage;
|
||||
- retained per-view residual counts and metrics;
|
||||
- coordinate-equivalence checks derived from the retained session points;
|
||||
- all three exact full-solve parameter vectors;
|
||||
- exact fit parameter vector;
|
||||
- support image/observation counts;
|
||||
- global RMSE, maximum residual and high-residual fraction;
|
||||
- hold-out RMSE and maximum residual;
|
||||
- maximum parameter delta and `validation_flags=0x0f`.
|
||||
|
||||
The stage consumes published solver evidence; it does not reclassify views,
|
||||
rerun calibration, average repeated solves or manufacture missing values.
|
||||
|
||||
`initialization_evidence_sha256` is the exact `session.l3dcal` SHA-256.
|
||||
|
||||
`validation_evidence_sha256` is deterministic and domain separated:
|
||||
|
||||
```text
|
||||
SHA256(
|
||||
ASCII("L3DCAL_WORKFLOW_VALIDATION_V1\n")
|
||||
|| detection_sha256_raw32
|
||||
|| solve_sha256_raw32
|
||||
|| evidence_sha256_raw32
|
||||
|| producer_sha256_raw32
|
||||
)
|
||||
```
|
||||
|
||||
This boundary deliberately does not construct per-campaign
|
||||
`Lardon3DCalibrationToolingEntry` rows. Those rows require Project DB proof of
|
||||
the selected image identities, representation bytes/dimensions and explicit
|
||||
Capture optical assignments, which belongs to Selected Execution Binding v1.
|
||||
|
||||
## Campaign optical-state evidence
|
||||
|
||||
Project DB v23 retains exact explicit optical configuration identity, including
|
||||
body, objective and focal state, but Calibration Science v1 requires a broader
|
||||
scientific key including focus, stabilization and processing/decode state.
|
||||
|
||||
No equality may be inferred between those domains.
|
||||
|
||||
`L3DCAL_CAMPAIGN_STATE_V1` therefore provides immutable external evidence for
|
||||
the complete Science v1 optical state. A later coordinator stage must verify
|
||||
this evidence against both the calibration session and each selected Capture's
|
||||
explicit Project DB optical configuration.
|
||||
|
||||
Absence or disagreement remains `CALIBRATION_UNAVAILABLE`.
|
||||
|
||||
## Selected Execution Binding v1
|
||||
|
||||
```text
|
||||
CALIBRATION_WORKFLOW_SELECTED_EXECUTION_BINDING_V1=PASS/FROZEN
|
||||
```
|
||||
|
||||
The implementation is additive:
|
||||
|
||||
```text
|
||||
src/calibration_workflow_bind.cpp
|
||||
tests/test_calibration_workflow_bind.cpp
|
||||
```
|
||||
|
||||
This boundary is read-only. It may read Project DB and managed representation
|
||||
bytes, but does not attach a calibration scope, invoke Calibration Tooling or
|
||||
Bootstrap, or transition the selected execution to `READY`. It proves:
|
||||
|
||||
- exact selected-execution stage, completion, item order and Capture mapping;
|
||||
- exact explicit v23 optical configuration, including campaign-origin facts
|
||||
where present;
|
||||
- exact selected-image/Capture relation and READY image asset identity;
|
||||
- managed representation size and SHA-256 through project-relative `openat`
|
||||
descent that rejects absolute paths, dot components, symlinks, non-directory
|
||||
components and non-regular final files;
|
||||
- grayscale OpenCV decoded width/height equal to the accepted materialized
|
||||
calibration geometry; and
|
||||
- deterministic selected-item-order `Lardon3DCalibrationToolingEntry`
|
||||
construction from the published solve values, without averaging or solver
|
||||
recomputation.
|
||||
|
||||
Entries are staged internally and published to caller storage only after every
|
||||
selected item passes. Exact retries are read-only and deterministic.
|
||||
|
||||
## Tooling / Bootstrap READY v1
|
||||
|
||||
The final public composition validates and materializes input, performs the
|
||||
read-only selected-execution binding, then invokes only FROZEN Calibration
|
||||
Tooling. Tooling produces L3DCALB1 v1 and invokes FROZEN Bootstrap. Success
|
||||
requires the returned scope to be the exact scope attached to READY; exact
|
||||
retries converge through immutable importer semantics.
|
||||
|
||||
The software workflow is PASS/FROZEN. It does not establish physical evidence:
|
||||
historical S21/A6000 campaigns remain CALIBRATION_UNAVAILABLE and
|
||||
BLOCKED_BY_KNOWN_CALIBRATION_DATA.
|
||||
|
||||
## Additive heterogeneous Workflow v2
|
||||
|
||||
`CALIBRATION_WORKFLOW_V2=PASS/FROZEN`. This additive composition leaves every
|
||||
v1 API, artifact and workflow meaning unchanged. It composes v26 Capture
|
||||
geometric state/applicability with the L3DCALB2 publication path for one
|
||||
heterogeneous selected execution.
|
||||
|
||||
The workflow first verifies the exact durable selected-item-to-Capture mapping
|
||||
for every caller binding. It never recovers Capture identity from an image ID,
|
||||
path, SHA-256, filename or operational group ID. Every Capture must have a
|
||||
complete observed geometric state. Missing or incomplete state reports
|
||||
`CALIBRATION_REQUIRED`; multiple exact compatible applicability candidates
|
||||
report `SELECTION_REQUIRED`.
|
||||
|
||||
L3DCALB2 is first published through its additive unattached primitive. This
|
||||
fully validates artifact bytes and selected image/representation bindings and
|
||||
may create reusable immutable calibrations and a complete scope, but cannot
|
||||
transition the execution to READY. Workflow v2 then binds each returned
|
||||
per-image calibration to an exact v26 applicability/selection and verifies the
|
||||
resolved `sparse_calibration_id` equals that scope member. Only after every
|
||||
selected item passes may the existing scope-attachment transaction set READY.
|
||||
|
||||
Invalid artifact evidence and wrong optical assignments are distinct non-ready
|
||||
errors. No pre-final failure attaches a scope. Exact retries reuse immutable
|
||||
calibrations and the same complete scope deterministically.
|
||||
|
||||
## Current next boundary
|
||||
|
||||
```text
|
||||
validated input -> materialized evidence -> selected-execution binding
|
||||
-> FROZEN Calibration Tooling -> FROZEN Calibration Bootstrap -> READY
|
||||
```
|
||||
|
||||
It must preserve the binding's exact provenance and use only the FROZEN
|
||||
Tooling/Bootstrap import path. No failure before import may mutate Project DB.
|
||||
|
|
@ -1,71 +1,48 @@
|
|||
# Candidate Pair subsystem
|
||||
# Sous-système Candidate Pair
|
||||
|
||||
## Status
|
||||
## Vision
|
||||
|
||||
Le sous-système Candidate Pair répond uniquement à la question :
|
||||
|
||||
> « Quelles paires d'images valent la peine d'être présentées au Matcher ? »
|
||||
|
||||
Il ne répond **PAS** à :
|
||||
|
||||
> « Ces images ont-elle réellement des correspondances ? »
|
||||
|
||||
et ne contient **aucune** validation géométrique.
|
||||
|
||||
## Frontières
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
CANDIDATE_PAIR_MODEL=v1
|
||||
CANDIDATE_PAIR_MODEL_STATUS=IMPLEMENTED
|
||||
CANDIDATE_PAIR_TASK=candidate_pair.generate/1
|
||||
CANDIDATE_PAIR_TASK_STATUS=IMPLEMENTED
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
The Candidate Pair scientific model remains the Project DB v8 model. The durable Candidate Pair Task
|
||||
was added in Project DB v9. Later schema versions through v25 are additive and do not redefine
|
||||
Candidate Pair identity.
|
||||
|
||||
This document owns the Candidate Pair subsystem contract. Resource policy is governed by the Resource
|
||||
Governor and the canonical resource documents; Candidate Pair declares bounded demand and preserves
|
||||
deterministic scientific output.
|
||||
|
||||
## Purpose
|
||||
|
||||
The Candidate Pair subsystem answers one question:
|
||||
|
||||
> Which image pairs are worth presenting to the Matcher?
|
||||
|
||||
It does not answer whether two images actually have descriptor correspondences and it performs no
|
||||
geometric verification.
|
||||
|
||||
The downstream Matcher is implemented and consumes persisted Candidate Pairs, but Matcher science and
|
||||
persistence are outside this subsystem.
|
||||
|
||||
```text
|
||||
Visual Index
|
||||
|
|
||||
v
|
||||
↓
|
||||
Candidate Pair Generator
|
||||
|
|
||||
v
|
||||
↓
|
||||
Candidate Pair persistence
|
||||
|
|
||||
v
|
||||
Matcher
|
||||
↓
|
||||
Matcher — HORS SCOPE de ce ticket
|
||||
```
|
||||
|
||||
## Core invariants
|
||||
Le Matcher est un consommateur des paires persistées. Il n'est pas
|
||||
implémenté dans ce sous-système.
|
||||
|
||||
| Invariant | Contract |
|
||||
| --- | --- |
|
||||
| Symmetry | `(A,B)` and `(B,A)` are the same scientific pair |
|
||||
| Canonical order | Persist with `image_id_a < image_id_b` |
|
||||
| No self-pairs | `image_id_a != image_id_b`, implied by the SQL ordering check |
|
||||
| Persistent uniqueness | `UNIQUE(image_id_a, image_id_b)` |
|
||||
| Bounded query result | `top_k <= LARDON3D_VISUAL_INDEX_TOP_K_MAX = 256` |
|
||||
| Determinism | Same inputs and options produce the same pair decisions in the same canonical order |
|
||||
| Idempotence | Repeating generation does not duplicate persisted pairs |
|
||||
| Persistent model | Candidate Pair rows were introduced by Project DB v8 |
|
||||
| Durable execution | `candidate_pair.generate/1` was introduced by Project DB v9 |
|
||||
## Invariants fondamentaux
|
||||
|
||||
Candidate Pair identity is the canonical unordered image pair. Retrieval score, Visual Index provenance,
|
||||
Task ID, timestamps and operational resource choices do not enter that identity.
|
||||
| Invariant | Description |
|
||||
|-----------|-------------|
|
||||
| **Symétrie** | A,B == B,A |
|
||||
| **Ordre canonique** | Stockage avec `image_id_a < image_id_b` |
|
||||
| **Self-pairs interdits** | `image_id_a != image_id_b` (implicite via CHECK SQL) |
|
||||
| **Unicité persistante** | `UNIQUE(image_id_a, image_id_b)` |
|
||||
| **Résultat borné** | `top_k <= LARDON3D_VISUAL_INDEX_TOP_K_MAX = 256` par requête |
|
||||
| **Déterminisme** | Mêmes entrées/configuration → mêmes paires dans le même ordre |
|
||||
| **Idempotence** | Répétition sans duplication |
|
||||
| **Persistance durable** | Paires persistées dans Project DB v8 |
|
||||
|
||||
## Persistent model
|
||||
## Modèle persistant
|
||||
|
||||
### `candidate_pairs` — Project DB v8
|
||||
### Table `candidate_pairs` (Project DB v8)
|
||||
|
||||
```sql
|
||||
CREATE TABLE candidate_pairs(
|
||||
|
|
@ -80,24 +57,17 @@ CREATE INDEX candidate_pairs_image_a_idx ON candidate_pairs(image_id_a);
|
|||
CREATE INDEX candidate_pairs_image_b_idx ON candidate_pairs(image_id_b);
|
||||
```
|
||||
|
||||
The executable schema in `src/project_db.c` remains authoritative if prose and SQL excerpts ever
|
||||
diverge.
|
||||
|
||||
### Public Project DB API
|
||||
|
||||
The Candidate Pair persistence surface includes:
|
||||
|
||||
- `lardon3d_project_db_create_candidate_pair()`;
|
||||
- `lardon3d_project_db_load_candidate_pair()`;
|
||||
- `lardon3d_project_db_find_candidate_pair()`;
|
||||
- `lardon3d_project_db_list_candidate_pairs()`.
|
||||
|
||||
Creation canonicalizes the image order and persistence enforces uniqueness.
|
||||
|
||||
## Single-source generation
|
||||
|
||||
### API
|
||||
|
||||
- `lardon3d_project_db_create_candidate_pair()` — INSERT avec canonicalisation
|
||||
- `lardon3d_project_db_load_candidate_pair()` — SELECT par ID
|
||||
- `lardon3d_project_db_find_candidate_pair()` — SELECT par (image_a, image_b)
|
||||
- `lardon3d_project_db_list_candidate_pairs()` — SELECT paginé ORDER BY id
|
||||
|
||||
## Génération single-source
|
||||
|
||||
### Prototype
|
||||
|
||||
```c
|
||||
Lardon3DVisualIndexResult lardon3d_candidate_pair_generate(
|
||||
const char *project_path, Lardon3DProjectDb *database,
|
||||
|
|
@ -106,117 +76,99 @@ Lardon3DVisualIndexResult lardon3d_candidate_pair_generate(
|
|||
Lardon3DCandidatePairGenStats *stats);
|
||||
```
|
||||
|
||||
### Algorithm
|
||||
### Algorithme
|
||||
|
||||
For one source Feature Set:
|
||||
1. Charger le FeatureSet source
|
||||
2. Obtenir `source_image_id`
|
||||
3. Interroger le Visual Index avec `query_options`
|
||||
4. Pour chaque candidat retourné :
|
||||
- Exclure les self-pairs
|
||||
- Canonicaliser l'ordre (image_a < image_b)
|
||||
- Chercher si la paire existe déjà (`find_candidate_pair`)
|
||||
- Créer si absente (`create_candidate_pair`)
|
||||
5. Retourner les statistiques `{generated, skipped, queried}`
|
||||
|
||||
1. load the source Feature Set;
|
||||
2. obtain its `source_image_id`;
|
||||
3. query the Visual Index with the supplied options;
|
||||
4. for every returned candidate:
|
||||
- reject self-pairs;
|
||||
- canonicalize the image order;
|
||||
- find an existing Candidate Pair;
|
||||
- create the pair only when absent;
|
||||
5. return bounded generation statistics.
|
||||
|
||||
The generator does not perform descriptor matching and does not perform geometric verification.
|
||||
|
||||
### Statistics
|
||||
### Statistiques
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint32_t generated_count;
|
||||
uint32_t skipped_count;
|
||||
uint32_t queried_count;
|
||||
uint32_t generated_count; // paires nouvellement créées
|
||||
uint32_t skipped_count; // paires déjà existantes (idempotence)
|
||||
uint32_t queried_count; // candidats retournés par le Visual Index
|
||||
} Lardon3DCandidatePairGenStats;
|
||||
```
|
||||
|
||||
`generated_count` counts newly persisted pairs. `skipped_count` counts pairs already present.
|
||||
`queried_count` counts candidates returned by the Visual Index query.
|
||||
## Score et provenance
|
||||
|
||||
## Retrieval score and provenance
|
||||
Le score de retrieval et la provenance Visual Index ne sont **PAS**
|
||||
persistés dans la table `candidate_pairs` pour les raisons suivantes :
|
||||
|
||||
Retrieval score and Visual Index provenance are intentionally not stored in `candidate_pairs`.
|
||||
- L'identité Candidate Pair est纯粹 géométrique : (image_a, image_b)
|
||||
- Le score dépend de la configuration du Visual Index et peut changer
|
||||
- Le Matcher calculera ses propres scores de matching
|
||||
- La séparation des responsabilités est plus nette
|
||||
|
||||
Reasons:
|
||||
Le score reste accessible via le Visual Index si nécessaire.
|
||||
|
||||
- Candidate Pair identity is only the canonical image pair;
|
||||
- retrieval score depends on Visual Index configuration;
|
||||
- a later Visual Index execution may score the same pair differently;
|
||||
- Matcher owns descriptor-level matching evidence;
|
||||
- keeping retrieval evidence out of Candidate Pair identity preserves subsystem separation.
|
||||
## Déterminisme
|
||||
|
||||
A generation fingerprint describes the generation request. It does not change the identity of an
|
||||
already persisted Candidate Pair row.
|
||||
### Déterministe
|
||||
|
||||
## Determinism
|
||||
- Mêmes entrées → mêmes paires
|
||||
- Même ordre de sélection top-K
|
||||
- Mêmes décisions de déduplication
|
||||
|
||||
### Deterministic inputs and decisions
|
||||
### Non déterministe
|
||||
|
||||
For identical immutable inputs and options, the subsystem preserves:
|
||||
- `created_at` (timestamp Unix, informatif uniquement)
|
||||
- `candidate_pair_id` (AUTOINCREMENT, identifiant technique)
|
||||
|
||||
- the same Visual Index query contract;
|
||||
- the same top-K selection semantics;
|
||||
- the same canonical image ordering;
|
||||
- the same self-pair rejection;
|
||||
- the same deduplication decisions;
|
||||
- the same publication order for owner-published results.
|
||||
### Tie-breaks
|
||||
|
||||
### Non-scientific values
|
||||
En cas d'égalité de score dans le Visual Index, l'ordre est déterministe
|
||||
selon l'implémentation LSH (ordre des Feature Sets).
|
||||
|
||||
These values are not Candidate Pair scientific identity:
|
||||
## Invalidation
|
||||
|
||||
- `created_at`;
|
||||
- `candidate_pair_id`;
|
||||
- Task ID;
|
||||
- resource reservation ID;
|
||||
- admitted CPU count;
|
||||
- admitted batch size.
|
||||
### Événements et impact
|
||||
|
||||
`candidate_pair_id` is a durable technical identity allocated by SQLite. It is not a scientific
|
||||
fingerprint.
|
||||
| Événement | Ce qui devient invalide | Ce qui reste réutilisable | Ce qui doit être recalculé |
|
||||
|-----------|------------------------|--------------------------|---------------------------|
|
||||
| Nouvelle image | Rien (incrémental) | Paires existantes | Nouvelles requêtes Visual Index |
|
||||
| Nouveau FeatureSet | Rien | Paires existantes | Requête depuis ce FeatureSet |
|
||||
| FeatureSet remplacé | Paires basées sur ce FeatureSet | Autres paires | Nouvelle requête depuis ce FeatureSet |
|
||||
| Visual Index reconstruit | Toutes les paires (nouvelle config) | Rien | Tout recalcul |
|
||||
| Configuration top-K modifiée | Rien (borné par requête) | Paires existantes | Nouvelles requêtes avec nouveau top_k |
|
||||
| Filtre modifié | Rien | Paires existantes | Nouvelles requêtes avec nouveau filtre |
|
||||
| Relance après interruption | Rien | Paires déjà persistées | Suite du traitement |
|
||||
|
||||
## Generation fingerprint
|
||||
### Politique
|
||||
|
||||
### API
|
||||
L'invalidation est aussi locale que possible. On ne supprime jamais
|
||||
toutes les paires du projet suite à une modification locale.
|
||||
|
||||
### Vérification par fingerprint
|
||||
|
||||
Le fingerprint permet de vérifier si une génération doit être recalculée :
|
||||
|
||||
```c
|
||||
void lardon3d_candidate_pair_generation_fingerprint(
|
||||
uint64_t visual_index_id, uint64_t source_feature_set_id,
|
||||
const Lardon3DVisualIndexQueryOptions *query_options,
|
||||
unsigned char fingerprint[32]);
|
||||
unsigned char fp_courant[32], fp_enregistre[32];
|
||||
lardon3d_candidate_pair_generation_fingerprint(..., fp_courant);
|
||||
// Si fp_courant != fp_enregistre → recalcul nécessaire
|
||||
```
|
||||
|
||||
### Included fields
|
||||
### Réutilisation
|
||||
|
||||
The generation fingerprint includes:
|
||||
- Même fingerprint → résultat réutilisable
|
||||
- Différent fingerprint → recalcul nécessaire
|
||||
- Les paires existantes sont conservées même si le fingerprint change
|
||||
|
||||
- `visual_index_id`;
|
||||
- `source_feature_set_id`;
|
||||
- `query_options->top_k`;
|
||||
- `query_options->minimum_evidence_count`;
|
||||
- `query_options->scanset_filter`;
|
||||
- `query_options->exclude_same_asset`.
|
||||
## Batch projet
|
||||
|
||||
### Excluded fields
|
||||
### Granularité
|
||||
|
||||
It excludes:
|
||||
|
||||
- `created_at`;
|
||||
- `candidate_pair_id`;
|
||||
- operational CPU/batch admission;
|
||||
- processing order of unrelated source Feature Sets.
|
||||
|
||||
### Reuse meaning
|
||||
|
||||
The same fingerprint means the same generation request may be reused.
|
||||
|
||||
A different fingerprint means the generation request must be evaluated again. Existing canonical
|
||||
Candidate Pair rows are not silently deleted merely because a different generation request is run;
|
||||
idempotent persistence may reuse rows that remain selected.
|
||||
|
||||
## Project batch generation
|
||||
La génération batch traite un ensemble de FeatureSets de manière
|
||||
bornée et déterministe.
|
||||
|
||||
### API
|
||||
|
||||
|
|
@ -229,352 +181,207 @@ Lardon3DVisualIndexResult lardon3d_candidate_pair_generate_batch(
|
|||
uint64_t *last_feature_set_id);
|
||||
```
|
||||
|
||||
### Ordering and bounds
|
||||
### Algorithme
|
||||
|
||||
The project batch path:
|
||||
1. Lister les FeatureSets par pages de 64
|
||||
2. Pour chaque FeatureSet :
|
||||
- Appeler `lardon3d_candidate_pair_generate()`
|
||||
- Accumuler les statistiques
|
||||
- Mettre à jour le curseur
|
||||
3. Retourner les totaux et le dernier FeatureSet traité
|
||||
|
||||
- pages Feature Sets in bounded pages;
|
||||
- processes source Feature Sets in increasing `feature_set_id`;
|
||||
- never assumes IDs are contiguous;
|
||||
- keeps top-K bounded by the Visual Index contract;
|
||||
- keeps query/result memory bounded;
|
||||
- returns the last processed Feature Set for restart.
|
||||
### Ordre de traitement
|
||||
|
||||
A pair selected from multiple sources is persisted once because canonical pair identity is unique.
|
||||
Feature Sets traités en ordre croissant de `feature_set_id`.
|
||||
|
||||
The historical single-source and project-batch APIs remain valid. They are not, by themselves, the
|
||||
complete current resource-execution description of the durable Task.
|
||||
### Déduplication
|
||||
|
||||
## Durable Task
|
||||
Une paire produite depuis plusieurs sources n'existe qu'une fois.
|
||||
La deduplication est assurée par `find avant create`.
|
||||
|
||||
### Bornes
|
||||
|
||||
- Un seul FeatureSet traité à la fois
|
||||
- Top-K borné par requête
|
||||
- Mémoire bornée : allocation `top_k * sizeof(candidate)` par requête
|
||||
- Pagination bornée (64 FeatureSets par page)
|
||||
|
||||
### Reprise
|
||||
|
||||
Le curseur `after_feature_set_id` permet la reprise après interruption.
|
||||
La fonction retourne le dernier FeatureSet traité.
|
||||
|
||||
## Tâche durable
|
||||
|
||||
### Task Kind
|
||||
|
||||
```text
|
||||
candidate_pair.generate/1
|
||||
`candidate_pair.generate` v1 — **IMPLEMENTED**.
|
||||
|
||||
### Unité de travail
|
||||
|
||||
Un membership FeatureSet source du Visual Index et sa requête associée. Chaque
|
||||
séquence traite un lot borné de memberships (1 à 64 selon le contrat Governor),
|
||||
en ordre croissant d'ID sans supposer des IDs contigus.
|
||||
|
||||
### Checkpoint
|
||||
|
||||
Curseur `after_feature_set_id` persisté dans `candidate_pair_generate_tasks`.
|
||||
Checkpoint sauvé après chaque lot via
|
||||
`lardon3d_project_checkpoint_candidate_pair_generate_task()`.
|
||||
|
||||
### Reprise
|
||||
|
||||
Reprise idempotente : le champ `after_feature_set_id` est rechargé depuis la
|
||||
DB, et les paires déjà persistées sont ignorées par `find avant create`.
|
||||
À l'ouverture du projet, la tâche est automatiquement restaurée via la
|
||||
registry production et resoumise à la queue.
|
||||
|
||||
Les anciens snapshots v1 produits avec l'estimation opérationnelle exacte
|
||||
128 Kio fixes, 64 Kio par item, lot 1–64, CPU 1, IO 1 et GPU 0 sont normalisés
|
||||
éphémèrement par la registry à la forme courante CPU64 avant admission. La
|
||||
forme historique immédiatement précédente CPU12/256 Kio fixes/64 Kio par item
|
||||
est elle aussi reconnue exactement. Le
|
||||
snapshot durable original reste la source du reconstructeur ; aucun checkpoint
|
||||
d'estimation seule n'est stagé, promu ou publié sous le même résumé. Une panne
|
||||
pré-terminale répète donc cette normalisation exacte. Aucun autre snapshot,
|
||||
curseur ou paramètre scientifique n'est réinterprété.
|
||||
|
||||
### Intégration Task/Queue/Governor
|
||||
|
||||
La tâche utilise le runtime générique via le pattern standard :
|
||||
- Estimation opérationnelle (256 Kio fixes, 8 Mio par item, lot 1–64).
|
||||
La Queue conserve un callback ; jusqu'à soixante-quatre participants CPU
|
||||
admis peuvent calculer une fenêtre interne bornée sans modifier l'identité
|
||||
scientifique.
|
||||
- Réservation CPU + IO avant exécution
|
||||
- `lardon3d_task_sequence_break()` entre chaque lot pour réadmission Governor
|
||||
- Callback terminal checkpoint après `COMPLETED`/`FAILED`/`CANCELLED`
|
||||
- Reconstruction depuis `Lardon3DProjectDbCandidatePairGenerateTask`
|
||||
|
||||
### API
|
||||
|
||||
```c
|
||||
Lardon3DTask *lardon3d_project_create_candidate_pair_generate_task(
|
||||
Lardon3DAppState *state, uint64_t visual_index_id,
|
||||
const Lardon3DVisualIndexQueryOptions *query_options, uint64_t *task_id);
|
||||
bool lardon3d_project_enqueue_candidate_pair_generate(
|
||||
Lardon3DAppState *state, uint64_t visual_index_id,
|
||||
const Lardon3DVisualIndexQueryOptions *query_options, uint64_t *task_id);
|
||||
bool lardon3d_candidate_pair_generate_reconstruct(
|
||||
const Lardon3DTaskDurableSnapshot *snapshot, void *context,
|
||||
Lardon3DTaskKindBinding *binding);
|
||||
```
|
||||
|
||||
Status: **IMPLEMENTED**.
|
||||
## Concurrence
|
||||
|
||||
### Durable unit
|
||||
### Garantie actuelle
|
||||
|
||||
The durable cursor is `after_feature_set_id` in `candidate_pair_generate_tasks`.
|
||||
La Queue conserve un callback actif. À l'intérieur de la Task, jusqu'à douze
|
||||
threads CPU admis calculent en parallèle une fenêtre d'au plus deux sources par
|
||||
thread. Chaque participant possède un handle DB de lecture privé. Le thread
|
||||
propriétaire publie ensuite seul et dans l'ordre canonique des sources. Voir le
|
||||
[contrat de parallélisme interne](internal_parallelism.md).
|
||||
|
||||
The Task consumes a bounded ordered set of Visual Index source memberships. A sequence handles an
|
||||
admitted bounded batch, publishes the canonical pair decisions, persists the cursor, checkpoints and
|
||||
returns through `lardon3d_task_sequence_break()` before the next Governor admission.
|
||||
### Atomicité
|
||||
|
||||
### Checkpoint and restart
|
||||
Les workers internes ne créent aucune paire. Après leur jointure, le
|
||||
propriétaire applique seul `find avant create`. La contrainte UNIQUE reste une
|
||||
protection persistante, pas un mécanisme d'ordonnancement parallèle.
|
||||
|
||||
The Task checkpoints through:
|
||||
### Limites
|
||||
|
||||
```text
|
||||
lardon3d_project_checkpoint_candidate_pair_generate_task()
|
||||
Le pattern `find + create` n'est pas atomique entre les deux appels. Le chemin
|
||||
de Task n'introduit aucun writer concurrent ; la sémantique existante reste
|
||||
inchangée pour les autres appelants éventuels.
|
||||
|
||||
## Bornes et ressources
|
||||
|
||||
### Top-K
|
||||
|
||||
`top_k <= 256` (LARDON3D_VISUAL_INDEX_TOP_K_MAX)
|
||||
|
||||
### Mémoire
|
||||
|
||||
Allocation de requête bornée par le top-K, plus un résultat de propositions
|
||||
borné par le même maximum. La Task limite sa fenêtre à 24 sources et annonce
|
||||
256 Kio fixes plus 64 Kio par item au Governor.
|
||||
|
||||
### Complexité
|
||||
|
||||
- O(top_k) par requête (lectures Visual Index)
|
||||
- O(1) par paire (DB write)
|
||||
- Pas de structure O(N²)
|
||||
|
||||
## Fingerprint de génération
|
||||
|
||||
### Composants
|
||||
|
||||
Le fingerprint identifie une génération Candidate Pair unique :
|
||||
|
||||
```c
|
||||
void lardon3d_candidate_pair_generation_fingerprint(
|
||||
uint64_t visual_index_id, uint64_t source_feature_set_id,
|
||||
const Lardon3DVisualIndexQueryOptions *query_options,
|
||||
unsigned char fingerprint[32]);
|
||||
```
|
||||
|
||||
Restart:
|
||||
### Éléments inclus
|
||||
|
||||
1. restores the generic Task snapshot;
|
||||
2. loads the typed Candidate Pair Task payload;
|
||||
3. restores `after_feature_set_id`;
|
||||
4. reconstructs the production binding through the Task Kind registry;
|
||||
5. resubmits through the normal Queue/Governor path;
|
||||
6. reuses already persisted Candidate Pairs idempotently.
|
||||
- `visual_index_id` : Visual Index utilisé
|
||||
- `source_feature_set_id` : Feature Set source
|
||||
- `query_options->top_k` : nombre de candidats par requête
|
||||
- `query_options->minimum_evidence_count` : filtre minimum
|
||||
- `query_options->scanset_filter` : filtre ScanSet
|
||||
- `query_options->exclude_same_asset` : exclusion même asset
|
||||
|
||||
A crash may therefore repeat work after the last durable cursor, but it must not invent a second
|
||||
scientific pair identity.
|
||||
### Éléments exclus (volontairement)
|
||||
|
||||
### Historical resource descriptors
|
||||
- `created_at` : informatif, pas fonctionnel
|
||||
- `candidate_pair_id` : identifiant technique
|
||||
- Ordre des Feature Sets traités en batch
|
||||
|
||||
Older durable snapshots are accepted only through exact compatibility shapes already recognized by the
|
||||
registry.
|
||||
### Stabilité
|
||||
|
||||
Historical forms include the exact earlier descriptors documented by the implementation, including:
|
||||
Le fingerprint est stable pour mêmes entrées et configuration.
|
||||
Un changement de configuration produit un fingerprint différent.
|
||||
|
||||
### Relation avec l'invalidation
|
||||
|
||||
Un fingerprint différent signifie que le travail doit être recalculé.
|
||||
Le même fingerprint signifie que le résultat peut être réutilisé.
|
||||
|
||||
### Relation avec la reprise
|
||||
|
||||
Le fingerprint permet de vérifier qu'une reprise utilise la même
|
||||
configuration que l'originale.
|
||||
|
||||
## Limites connues
|
||||
|
||||
1. Score non persistant (par design)
|
||||
2. La compaction de segments Visual Index n'est pas implémentée
|
||||
3. Aucun Matcher consommateur des paires
|
||||
|
||||
## Statut
|
||||
|
||||
**IMPLEMENTED** — génération single-source, persistance,
|
||||
canonicalisation, idempotence, batch projet, fingerprint et
|
||||
réutilisation/invalidation.
|
||||
|
||||
**IMPLEMENTED** — tâche durable `candidate_pair.generate` v1
|
||||
via le runtime/Queue générique, avec estimation immuable,
|
||||
checkpoint par curseur, reprise idempotente et intégration
|
||||
dans la registry production.
|
||||
|
||||
## Relation avec le pipeline
|
||||
|
||||
Le Candidate Pair Generator est l'étape E du pipeline de reconstruction :
|
||||
|
||||
```text
|
||||
128 KiB fixed
|
||||
64 KiB per item
|
||||
batch 1..64
|
||||
CPU1
|
||||
IO1
|
||||
GPU0
|
||||
```
|
||||
|
||||
and the later exact historical CPU12 / 256 KiB fixed / 64 KiB-per-item form.
|
||||
|
||||
Those shapes are restart compatibility evidence. They are not the current resource model and must not
|
||||
be copied into new Task creation.
|
||||
|
||||
The original durable snapshot remains the source supplied to reconstruction. Compatibility
|
||||
normalization is ephemeral and does not rewrite the persisted checkpoint or Candidate scientific
|
||||
identity.
|
||||
|
||||
## Current resource contract
|
||||
|
||||
### Current Task estimate
|
||||
|
||||
The current validated Candidate Pair Task declares approximately:
|
||||
|
||||
```text
|
||||
fixed RAM 256 KiB
|
||||
per-item RAM 8 MiB
|
||||
batch range 1..64
|
||||
GPU demand 0
|
||||
IO demand bounded by the existing Task estimate
|
||||
CPU demand reducible and bounded by the host compute pool
|
||||
```
|
||||
|
||||
The exact implementation constants remain authoritative in source. This documentation records the
|
||||
current validated capability and intentionally does not retain the obsolete 24-source / 64-KiB-per-item
|
||||
description as current policy.
|
||||
|
||||
### Coupled CPU and batch admission
|
||||
|
||||
Candidate Pair generation has independent source work, but additional CPU cannot exercise additional
|
||||
participants if the admitted source batch remains one.
|
||||
|
||||
For this Task, CPU and batch scaling are therefore coupled during adaptation. Conceptually:
|
||||
|
||||
```text
|
||||
CPU1 / batch1
|
||||
CPU2 / batch2
|
||||
then larger safe coupled rungs
|
||||
```
|
||||
|
||||
subject to:
|
||||
|
||||
- the Task's declared maximums;
|
||||
- the host compute pool;
|
||||
- current Governor pressure;
|
||||
- measured usefulness;
|
||||
- current admission policy.
|
||||
|
||||
This coupling fixes an operational scaling defect. It does not modify Candidate Pair scientific
|
||||
identity, query options, top-K behavior, publication order or persistence.
|
||||
|
||||
### Canonical resource principles
|
||||
|
||||
```text
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
```
|
||||
|
||||
The interactive host reserve is preserved first. After that reserve and all safety constraints are
|
||||
satisfied, safe and useful compute capacity should not be left idle merely to preserve an old
|
||||
single-thread measurement.
|
||||
|
||||
Reference-host values are observations, not portable constants.
|
||||
|
||||
### Atomicity is not serialism
|
||||
|
||||
One source query and one Candidate Pair publication decision remain bounded scientific/transactional
|
||||
units.
|
||||
|
||||
That does not imply that independent source preparation must run serially.
|
||||
|
||||
```text
|
||||
PER_ITEM_ATOMICITY_REQUIRES_CROSS_ITEM_SERIALISM=NO
|
||||
OWNER_ONLY_PUBLICATION_REQUIRES_SERIAL_PREPARATION=NO
|
||||
```
|
||||
|
||||
## Internal concurrency
|
||||
|
||||
The Task Queue still owns one active callback. Candidate Pair uses bounded parallelism inside that
|
||||
callback.
|
||||
|
||||
The validated shape is:
|
||||
|
||||
```text
|
||||
one admitted Candidate owner Task
|
||||
-> bounded source window
|
||||
-> bounded CPU participants
|
||||
-> private read-side preparation
|
||||
-> join
|
||||
-> owner publishes in canonical source order
|
||||
```
|
||||
|
||||
Participants do not create Candidate Pair rows.
|
||||
|
||||
Each participant uses its allowed private read-side state. After participants join, the owner alone
|
||||
performs the canonical `find` / create publication sequence.
|
||||
|
||||
The SQL `UNIQUE(image_id_a, image_id_b)` constraint remains a persistent integrity guard, not a
|
||||
parallel scheduling primitive.
|
||||
|
||||
No second global scheduler, global worker pool or parallel SQLite writer subsystem is introduced.
|
||||
|
||||
## Persistence race boundary
|
||||
|
||||
The public pattern:
|
||||
|
||||
```text
|
||||
find candidate pair
|
||||
-> create candidate pair when absent
|
||||
```
|
||||
|
||||
is not a general atomic compare-and-insert primitive across arbitrary concurrent writers.
|
||||
|
||||
The production durable Candidate Task avoids introducing competing pair writers: participant work is
|
||||
read/preparation only and owner publication is serialized.
|
||||
|
||||
Other callers must not infer a stronger concurrency guarantee from the Task's owner-only publication
|
||||
model.
|
||||
|
||||
## Resource complexity
|
||||
|
||||
### Query bound
|
||||
|
||||
```text
|
||||
top_k <= LARDON3D_VISUAL_INDEX_TOP_K_MAX = 256
|
||||
```
|
||||
|
||||
### Memory
|
||||
|
||||
Current Task admission uses the current estimate:
|
||||
|
||||
```text
|
||||
256 KiB fixed + 8 MiB per admitted item
|
||||
```
|
||||
|
||||
with batch bounded to `1..64`.
|
||||
|
||||
Actual admission can be reduced by the Governor and host compute pool. Swap, zram and external scratch
|
||||
never enlarge admitted RAM.
|
||||
|
||||
Candidate Pair currently has no authoritative scratch consumer.
|
||||
|
||||
### Algorithmic shape
|
||||
|
||||
The subsystem does not allocate an O(N^2) project pair matrix.
|
||||
|
||||
Per source, bounded work is dominated by:
|
||||
|
||||
- bounded Visual Index query;
|
||||
- bounded top-K filtering;
|
||||
- canonical pair lookup/publication.
|
||||
|
||||
Project traversal is paged and restartable.
|
||||
|
||||
## GPU policy
|
||||
|
||||
Candidate Pair currently remains CPU.
|
||||
|
||||
The validated GPU audit classified it as:
|
||||
|
||||
```text
|
||||
CANDIDATE_GPU=REJECTED_WITH_MEASURED_REASON
|
||||
```
|
||||
|
||||
The workload is dominated by Visual Index access, filtering, branching and deterministic ordered SQLite
|
||||
publication, and no validated GPU primitive currently preserves the complete Candidate contract with a
|
||||
useful measured advantage.
|
||||
|
||||
This rejection does not authorize CPU serialism. Safe useful CPU parallelism remains required by the
|
||||
canonical resource policy.
|
||||
|
||||
## Real A6000 evidence
|
||||
|
||||
The retained real A6000 pre-SfM execution exercised the current Candidate path before Matcher, GV and
|
||||
Tracks.
|
||||
|
||||
It produced:
|
||||
|
||||
```text
|
||||
Candidate Pairs = 38,420
|
||||
Match Results = 38,420
|
||||
```
|
||||
|
||||
The later checkpoint is:
|
||||
|
||||
```text
|
||||
real-a6000-pre-sfm-2026-09-02
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
Candidate replay at the retained checkpoint created no new Candidate work. The proof continued through
|
||||
Matcher, Geometric Verifier v3 and Tracks, then stopped before real Sparse SfM.
|
||||
|
||||
This evidence validates the operational Candidate path on that project. It does not change Candidate
|
||||
Pair v1 identity or the Project DB v8/v9 historical contracts.
|
||||
|
||||
## Relationship to current Project DB
|
||||
|
||||
The Candidate Pair model was introduced in Project DB v8 and its durable Task payload in v9.
|
||||
|
||||
The current schema head is v25:
|
||||
|
||||
```text
|
||||
v22 selected scientific execution foundation
|
||||
v23 generic optical-context overlay
|
||||
v24 raw.develop.batch/1 persistence
|
||||
v25 features.extract.batch/1 persistence
|
||||
```
|
||||
|
||||
Those later additive migrations do not reinterpret Candidate Pair rows.
|
||||
|
||||
## Known limits
|
||||
|
||||
Current limits and non-goals include:
|
||||
|
||||
- retrieval score is not persisted in `candidate_pairs` by design;
|
||||
- Visual Index segment compaction remains separate work;
|
||||
- no generic DAG dependency scheduler is introduced by Candidate Pair;
|
||||
- no Candidate GPU backend is currently validated;
|
||||
- no scratch/spill path is authoritative for Candidate Pair;
|
||||
- arbitrary concurrent pair writers are not provided by the Task owner-publication model.
|
||||
|
||||
The downstream Matcher is implemented; it is not a missing Candidate Pair feature.
|
||||
|
||||
## Pipeline relationship
|
||||
|
||||
```text
|
||||
Feature Store
|
||||
|
|
||||
v
|
||||
Visual Index
|
||||
|
|
||||
v
|
||||
Candidate Pair Generator <- this document
|
||||
|
|
||||
v
|
||||
Matcher
|
||||
|
|
||||
v
|
||||
Geometric Verification
|
||||
|
|
||||
v
|
||||
Tracks
|
||||
```
|
||||
|
||||
Candidate Pair selects plausible image pairs. Matcher computes descriptor-level correspondence evidence.
|
||||
Geometric Verification validates geometry. Track Builder creates multi-view observation tracks. These
|
||||
scientific responsibilities remain separate.
|
||||
|
||||
## Summary
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
|
||||
CANDIDATE_PAIR_MODEL=v1
|
||||
CANDIDATE_PAIR_MODEL_STATUS=IMPLEMENTED
|
||||
|
||||
CANDIDATE_PAIR_TASK=candidate_pair.generate/1
|
||||
CANDIDATE_PAIR_TASK_STATUS=IMPLEMENTED
|
||||
|
||||
CANDIDATE_PAIR_PERSISTENCE_VERSION=v8
|
||||
CANDIDATE_PAIR_TASK_PERSISTENCE_VERSION=v9
|
||||
|
||||
CANDIDATE_CURRENT_FIXED_RAM=256_KiB
|
||||
CANDIDATE_CURRENT_PER_ITEM_RAM=8_MiB
|
||||
CANDIDATE_CURRENT_BATCH_RANGE=1..64
|
||||
CANDIDATE_CPU_BATCH_ADAPTATION=COUPLED
|
||||
CANDIDATE_GPU=REJECTED_WITH_MEASURED_REASON
|
||||
CANDIDATE_SCRATCH_CONSUMER=NO
|
||||
|
||||
PER_ITEM_ATOMICITY_REQUIRES_CROSS_ITEM_SERIALISM=NO
|
||||
OWNER_ONLY_PUBLICATION_REQUIRES_SERIAL_PREPARATION=NO
|
||||
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
Feature Store (C)
|
||||
↓
|
||||
Visual Index (D)
|
||||
↓
|
||||
Candidate Pair Generator (E) ← CE DOCUMENT
|
||||
↓
|
||||
Matching (F) — HORS SCOPE
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,169 +1,60 @@
|
|||
# Feature Store v1/v2
|
||||
|
||||
## Status
|
||||
## Rôle et modèle
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
**IMPLEMENTED.** Le Feature Store est la mémoire visuelle locale persistante.
|
||||
Un `FeatureSet` logique appartient à une `image_id` et identifie exactement
|
||||
`orb` version 1 plus une configuration canonique. Il référence un
|
||||
`FeatureAsset` physique immutable. Deux images logiques portant le même contenu
|
||||
et la même configuration gardent deux `feature_set_id`, mais partagent le même
|
||||
asset lorsque leurs octets sont identiques. Les futurs matches référenceront
|
||||
`feature_set_id + feature_index`; ces deux valeurs sont immuables après
|
||||
publication.
|
||||
|
||||
FEATURE_STORE_STATUS=IMPLEMENTED
|
||||
FEATURE_FILE_V1=FROZEN
|
||||
FEATURE_FILE_V2=IMPLEMENTED
|
||||
`feature_set_id` et `feature_asset_id` sont des identités SQLite
|
||||
`AUTOINCREMENT`. Une identité validée n'est jamais réattribuée. SQLite ne
|
||||
contient que les relations, empreintes, tailles et paramètres ; les tableaux de
|
||||
points et descripteurs restent hors DB.
|
||||
|
||||
HISTORICAL_FEATURE_TASK=features.extract/1
|
||||
CURRENT_FEATURE_BATCH_TASK=features.extract.batch/1
|
||||
PROJECT_DB_V25_FEATURE_BATCH=IMPLEMENTED/VALIDATED
|
||||
## Extracteur et configuration
|
||||
|
||||
PER_IMAGE_FEATURE_RESULT=ATOMIC
|
||||
PER_IMAGE_ATOMICITY_REQUIRES_CROSS_IMAGE_SERIALISM=NO
|
||||
OWNER_ONLY_PUBLICATION_REQUIRES_SERIAL_PREPARATION=NO
|
||||
L'extracteur production est ORB d'OpenCV 5, derrière une façade C. Aucune
|
||||
exception C++ ni `cv::Mat` ne traverse cette frontière. La registry statique
|
||||
expose `features.extract`, version 1. Une tâche traite exactement une image.
|
||||
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
La configuration v1 contient trois `uint32` : `max_features` (1..8192),
|
||||
`pyramid_levels` (1..16) et `fast_threshold` (1..255). Son fingerprint SHA-256
|
||||
porte sur les 24 octets canoniques `L3DORBP1`, version et trois entiers
|
||||
little-endian. Il ne dépend ni du padding, ni de la locale. Le contrat ORB v1
|
||||
produit 32 octets binaires par point.
|
||||
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
Au démarrage, avant la création du worker Queue, Lardon3D configure la baseline
|
||||
OpenCV process-wide depuis le compute-pool réel. `features.extract` publie la
|
||||
borne positive `int` acceptée par OpenCV ; le Resource Governor réduit toujours
|
||||
l'admission au budget hôte. Douze threads demeure une cohorte de validation,
|
||||
pas un plafond portable. La Queue conserve un callback actif ; aucun pool
|
||||
Lardon3D supplémentaire n'est créé pour ORB.
|
||||
|
||||
The Feature Store is the persistent local visual-memory layer for Lardon3D.
|
||||
Cette limite est une configuration opérationnelle, jamais un paramètre
|
||||
scientifique : elle n'entre ni dans le fingerprint ORB v1, ni dans l'identité
|
||||
FeatureSet, ni dans le Feature File. L'audit contrôlé OpenCV 5.0.0 aux limites
|
||||
1, 2, 4, 8 et 12 exige le même count, le même ordre et les mêmes valeurs
|
||||
binary32 des six champs keypoint persistés, les mêmes lignes/octets descriptor
|
||||
et le même SHA-256 du Feature File. `class_id` est une donnée interne
|
||||
`cv::KeyPoint` non publiée ; elle ne fait pas partie du record Feature File v1/v2
|
||||
gelé et ne peut donc définir une identité scientifique.
|
||||
|
||||
The scientific identity of a Feature Set remains per image and immutable after publication. Project DB
|
||||
v25 adds an operational selected-execution batch Task, but does not change Feature Set identity,
|
||||
Feature File formats, ORB descriptor semantics or the historical single-image Task contract.
|
||||
|
||||
## Role and identity model
|
||||
|
||||
A logical `FeatureSet` belongs to one `image_id` and identifies:
|
||||
|
||||
- one extractor kind;
|
||||
- one extractor version;
|
||||
- one canonical parameter fingerprint;
|
||||
- one exact source image content identity;
|
||||
- one immutable published Feature Asset.
|
||||
|
||||
Two logical images with identical source content and identical extractor configuration keep distinct
|
||||
`feature_set_id` values. They may share the same immutable physical Feature Asset when the resulting
|
||||
Feature File bytes are identical.
|
||||
|
||||
Downstream correspondence identity references:
|
||||
|
||||
```text
|
||||
feature_set_id + feature_index
|
||||
```
|
||||
|
||||
Both components are immutable after Feature Set publication.
|
||||
|
||||
`feature_set_id` and `feature_asset_id` are SQLite `AUTOINCREMENT` identities. A committed published
|
||||
identity is never reassigned to another object. SQLite stores logical relations, hashes, sizes,
|
||||
extractor metadata and light metrics; keypoint arrays and descriptor arrays remain outside SQLite.
|
||||
|
||||
## Extractor registry and scientific configuration
|
||||
|
||||
The historical production ORB extractor uses OpenCV 5 behind a C boundary. No C++ exception or
|
||||
`cv::Mat` crosses the public C API.
|
||||
|
||||
The historical registered Task Kind is:
|
||||
|
||||
```text
|
||||
features.extract/1
|
||||
```
|
||||
|
||||
That Task processes one image as one scientific unit.
|
||||
|
||||
The current selected-execution operational Task Kind is:
|
||||
|
||||
```text
|
||||
features.extract.batch/1
|
||||
```
|
||||
|
||||
It processes multiple independently selected images under one durable owner while preserving one
|
||||
immutable Feature result per image.
|
||||
|
||||
### ORB v1 configuration
|
||||
|
||||
The ORB v1 configuration contains three `uint32_t` fields:
|
||||
|
||||
- `max_features`, range `1..8192`;
|
||||
- `pyramid_levels`, range `1..16`;
|
||||
- `fast_threshold`, range `1..255`.
|
||||
|
||||
Its SHA-256 parameter fingerprint is computed from the canonical 24-byte domain containing:
|
||||
|
||||
```text
|
||||
L3DORBP1
|
||||
version
|
||||
max_features
|
||||
pyramid_levels
|
||||
fast_threshold
|
||||
```
|
||||
|
||||
with fixed little-endian integer encoding.
|
||||
|
||||
The fingerprint does not depend on C struct padding, locale, Task ID, resource reservation,
|
||||
admitted CPU count or admitted batch size.
|
||||
|
||||
ORB v1 produces 32-byte binary descriptors.
|
||||
|
||||
## OpenCV execution control
|
||||
|
||||
OpenCV thread count is process-wide operational state, not scientific identity.
|
||||
|
||||
Before Queue execution, Lardon3D establishes the validated OpenCV baseline from the actual host
|
||||
compute pool. The active heavy callback applies the count required by its admitted contract and
|
||||
restores the baseline on every exit path.
|
||||
|
||||
The Queue retains one active callback. This prevents unrelated heavy Tasks from racing the same
|
||||
process-wide OpenCV configuration.
|
||||
|
||||
Historical `features.extract/1` may use the admitted OpenCV CPU count inside one image extraction.
|
||||
|
||||
For `features.extract.batch/1`, cross-image participants are the primary concurrency mechanism.
|
||||
Independent participants prepare different images; Lardon3D does not blindly multiply a full OpenCV
|
||||
thread team inside every participant.
|
||||
|
||||
The admitted CPU count is resource policy and never enters:
|
||||
|
||||
- ORB v1 fingerprint;
|
||||
- Feature Set identity;
|
||||
- Feature File bytes;
|
||||
- source-image identity.
|
||||
|
||||
The controlled OpenCV 5.0.0 validation at 1, 2, 4, 8 and 12 threads required identical:
|
||||
|
||||
- feature count;
|
||||
- keypoint order;
|
||||
- binary32 values of all six persisted keypoint fields;
|
||||
- descriptor row order and bytes;
|
||||
- complete Feature File SHA-256.
|
||||
|
||||
Twelve threads are reference-host validation evidence, not a portable product ceiling.
|
||||
|
||||
`cv::KeyPoint::class_id` is not persisted by the frozen Feature File record and therefore cannot
|
||||
define scientific identity.
|
||||
|
||||
## Decode bound
|
||||
|
||||
The 100,000,000-pixel limit is checked after `cv::imread`.
|
||||
|
||||
The API used by this implementation does not provide a reliable multi-format dimension probe without
|
||||
decoding. Therefore decode peak memory can precede rejection.
|
||||
|
||||
Lardon3D does not claim pre-decode memory bounding and does not maintain a parallel JPEG/PNG dimension
|
||||
parser for this purpose.
|
||||
La limite de 100 000 000 pixels est vérifiée après `cv::imread` : l'API utilisée
|
||||
ne fournit pas de sonde de dimensions multi-format fiable sans décodage. Le pic
|
||||
mémoire du décodage peut donc précéder le rejet. Lardon3D ne revendique pas de
|
||||
bornage pré-décodage et n'embarque pas un parseur JPEG/PNG parallèle.
|
||||
|
||||
## Feature File v1
|
||||
|
||||
Feature File v1 is little-endian and requires IEEE-754 binary32.
|
||||
Le fichier est little-endian et exige IEEE-754 binary32. Sa taille maximale est
|
||||
16 Mio et son nombre maximal de points 8192.
|
||||
|
||||
Limits:
|
||||
|
||||
```text
|
||||
maximum file size 16 MiB
|
||||
maximum feature count 8192
|
||||
ORB descriptor U8 x 32
|
||||
```
|
||||
|
||||
### Header
|
||||
|
||||
| Offset | Size | Field |
|
||||
| Offset | Taille | Champ |
|
||||
|---:|---:|---|
|
||||
| 0 | 8 | magic `L3DFEAT\0` |
|
||||
| 8 | 4 | format version = 1 |
|
||||
|
|
@ -172,456 +63,98 @@ ORB descriptor U8 x 32
|
|||
| 20 | 4 | descriptor dimension = 32 |
|
||||
| 24 | 4 | descriptor type = U8 |
|
||||
| 28 | 4 | keypoint record size = 24 |
|
||||
| 32, 36 | 4 + 4 | decoded image width and height |
|
||||
| 40, 48, 56 | 8 + 8 + 8 | keypoint offset, descriptor offset, total size |
|
||||
| 64 | 32 | source image asset SHA-256 |
|
||||
| 96 | 32 | parameter fingerprint |
|
||||
| 128 | 16 | `orb\0` followed by reserved zero bytes |
|
||||
| 32,36 | 4+4 | largeur, hauteur décodées |
|
||||
| 40,48,56 | 8+8+8 | offsets keypoints/descriptors, taille totale |
|
||||
| 64 | 32 | SHA-256 de l'asset image source |
|
||||
| 96 | 32 | fingerprint paramètres |
|
||||
| 128 | 16 | `orb\0` puis zéros réservés |
|
||||
| 144 | 4 | extractor version = 1 |
|
||||
| 148 | 12 | reserved, must be zero |
|
||||
|
||||
Each keypoint contains six 32-bit words:
|
||||
|
||||
1. `x` binary32;
|
||||
2. `y` binary32;
|
||||
3. `size` binary32;
|
||||
4. orientation binary32;
|
||||
5. response binary32;
|
||||
6. signed octave.
|
||||
|
||||
`x` and `y` are image pixels in the OpenCV-decoded image with top-left origin.
|
||||
|
||||
`size` is the neighborhood diameter in pixels.
|
||||
|
||||
Orientation is in degrees in `[0,360)`.
|
||||
|
||||
Keypoint record `i` corresponds exactly to descriptor row `i`.
|
||||
|
||||
## Feature File v1 validation
|
||||
|
||||
The reader validates:
|
||||
|
||||
- magic;
|
||||
- format version;
|
||||
- reserved-zero bytes;
|
||||
- descriptor type;
|
||||
- descriptor dimension;
|
||||
- count bounds;
|
||||
- offset bounds;
|
||||
- exact total size;
|
||||
- checked multiplication;
|
||||
- external SHA-256;
|
||||
- Project DB metadata consistency.
|
||||
|
||||
A future format version is distinguished from corruption when both file and DB record are otherwise
|
||||
coherent.
|
||||
|
||||
The reader uses `pread`, reads at most 256 features per call and does not require loading the entire
|
||||
Feature File.
|
||||
|
||||
The Project DB path must equal the canonical path derived from the stored SHA-256 before the file is
|
||||
opened.
|
||||
|
||||
Outcomes include:
|
||||
|
||||
- missing file -> `NOT_FOUND`;
|
||||
- truncation -> `CORRUPT`;
|
||||
- hash mismatch -> `CORRUPT`;
|
||||
- header/DB disagreement -> `CORRUPT`;
|
||||
- coherent unsupported future version -> `UNSUPPORTED_VERSION`.
|
||||
|
||||
## Publication and persistence
|
||||
|
||||
Physical layout:
|
||||
|
||||
```text
|
||||
assets/features/<first-2-hex>/<full-lowercase-sha256>
|
||||
```
|
||||
|
||||
The SHA-256 covers the complete Feature File.
|
||||
|
||||
The publication protocol is:
|
||||
|
||||
```text
|
||||
local temporary file
|
||||
-> write
|
||||
-> fsync file
|
||||
-> hash complete Feature File
|
||||
-> atomic no-overwrite link/publication
|
||||
-> fully validate any concurrently existing identical asset
|
||||
-> fsync parent directory
|
||||
-> SQLite transaction
|
||||
-> logical READY state
|
||||
```
|
||||
|
||||
If the final parent-directory `fsync` fails after publication, durability is recorded as
|
||||
`PUBLISHED_NOT_DURABLE`.
|
||||
|
||||
If SQLite fails after the physical file is published, the valid file remains an orphan and no partial
|
||||
logical Feature Set row is invented.
|
||||
|
||||
A later exact retry may revalidate the physical asset, complete a successful directory `fsync` and
|
||||
promote durability to `DURABLE` in the Project DB transaction.
|
||||
|
||||
## Historical Project DB Feature foundation
|
||||
|
||||
The Feature Store persistence foundation separates:
|
||||
|
||||
- `feature_assets`;
|
||||
- `feature_sets`;
|
||||
- `feature_extract_tasks`.
|
||||
|
||||
Logical uniqueness is based on image, extractor kind, extractor version and parameter fingerprint.
|
||||
|
||||
The historical `features.extract/1` Task is persisted before enqueue, admitted by Queue/Governor,
|
||||
executes one complete image and checkpoints at its durable boundaries.
|
||||
|
||||
Its historical conservative execution shape includes:
|
||||
|
||||
```text
|
||||
one image
|
||||
one atomic ORB extraction/publication
|
||||
no intra-image restart
|
||||
```
|
||||
|
||||
The existing conservative single-image resource descriptor remains valid for those Tasks and their
|
||||
restart compatibility. It must not be generalized into a rule requiring all future independent images
|
||||
to execute serially.
|
||||
|
||||
Pause and cancellation are cooperative before and after the OpenCV call. The OpenCV extraction itself
|
||||
is not interruptible.
|
||||
|
||||
The managed source image is rehashed before extraction.
|
||||
|
||||
A uniform image may legitimately publish a READY Feature Set with zero features.
|
||||
|
||||
## Selected-execution Feature batch — Project DB v25
|
||||
|
||||
Project DB v25 adds only:
|
||||
|
||||
```text
|
||||
feature_extract_batch_tasks
|
||||
```
|
||||
|
||||
and the durable Task Kind:
|
||||
|
||||
```text
|
||||
features.extract.batch/1
|
||||
```
|
||||
|
||||
The v24 -> v25 migration is transactional, additive and DDL-only.
|
||||
|
||||
It does not:
|
||||
|
||||
- create Feature Sets;
|
||||
- convert historical `features.extract/1` Tasks;
|
||||
- infer an `image_id`;
|
||||
- infer a Feature Set;
|
||||
- change ORB v1 parameters;
|
||||
- change Feature File v1/v2;
|
||||
- change Feature Set identity.
|
||||
|
||||
### Durable owner identity
|
||||
|
||||
One `features.extract.batch/1` owner is bound to:
|
||||
|
||||
- one immutable selected execution;
|
||||
- one monotonic `next_item_index`;
|
||||
- the exact ORB extractor kind;
|
||||
- the exact ORB extractor version;
|
||||
- the exact ORB parameters;
|
||||
- the exact ORB parameter fingerprint.
|
||||
|
||||
The selected execution order is authoritative.
|
||||
|
||||
### Execution shape
|
||||
|
||||
The current operational path is:
|
||||
|
||||
```text
|
||||
one admitted owner Task
|
||||
-> bounded selected-item window
|
||||
-> bounded independent image participants
|
||||
-> each participant prepares one per-image Feature result
|
||||
-> participants perform no SQLite publication
|
||||
-> join
|
||||
-> owner validates or reuses the exact READY Feature Set
|
||||
-> owner publishes in selected-item order
|
||||
-> owner advances the durable Feature cursor
|
||||
-> generic Task progress/checkpoint follows
|
||||
```
|
||||
|
||||
Each participant therefore computes an ordinary per-image scientific Feature result.
|
||||
|
||||
The owner-only publication stage preserves deterministic selected-item order without requiring
|
||||
independent image preparation to run serially.
|
||||
|
||||
### Atomicity and concurrency
|
||||
|
||||
The scientific atomic result remains one Feature Set for one image.
|
||||
|
||||
```text
|
||||
PER_IMAGE_FEATURE_RESULT=ATOMIC
|
||||
PER_IMAGE_ATOMICITY_REQUIRES_CROSS_IMAGE_SERIALISM=NO
|
||||
OWNER_ONLY_PUBLICATION_REQUIRES_SERIAL_PREPARATION=NO
|
||||
```
|
||||
|
||||
Cross-image preparation may be concurrent when the Governor admits a useful bounded window.
|
||||
|
||||
This is not a generic DAG scheduler and does not add a second global worker pool. The Task Queue still
|
||||
owns one active callback; the batch callback contains the bounded participant work.
|
||||
|
||||
### Crash and restart
|
||||
|
||||
An immutable Feature Set can become durable before the Feature-batch cursor or generic Task checkpoint
|
||||
advances.
|
||||
|
||||
Therefore, after a crash:
|
||||
|
||||
```text
|
||||
durable Feature Set may be ahead of Task checkpoint
|
||||
```
|
||||
|
||||
but:
|
||||
|
||||
```text
|
||||
Task checkpoint must never invent a Feature Set that is not durable
|
||||
```
|
||||
|
||||
Restart revalidates and reuses the exact READY Feature Set and then converges the cursor. It does not
|
||||
derive identity from path, processing order or Task-local position.
|
||||
|
||||
### Resource adaptation
|
||||
|
||||
For the selected-execution Feature batch, increasing admitted CPU while the item window remains one
|
||||
cannot exercise additional independent images.
|
||||
|
||||
The current validated resource model may therefore couple CPU and batch growth where required by the
|
||||
Feature-batch rung contract.
|
||||
|
||||
That behavior is Task-specific operational policy, not a universal Governor rule.
|
||||
|
||||
The canonical repository policy remains:
|
||||
|
||||
```text
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
```
|
||||
|
||||
Preserve the interactive host reserve and all RAM/CPU/IO/GPU safety constraints first. Within that
|
||||
safe envelope, use the maximum useful validated execution width.
|
||||
|
||||
Per-item atomicity is not evidence that unrelated items must be serialized.
|
||||
|
||||
## GPU boundary
|
||||
|
||||
Feature Extraction currently remains CPU.
|
||||
|
||||
The current production GPU audit classifies Feature Extraction as:
|
||||
|
||||
```text
|
||||
FEATURE_GPU=REJECTED_WITH_MEASURED_OR_IMPLEMENTATION_EVIDENCE
|
||||
```
|
||||
|
||||
On the validated OpenCV 5.0.0 host:
|
||||
|
||||
- no usable ORB/SIFT Vulkan/OpenCL extraction seam is validated;
|
||||
- CUDA is unavailable in the installed OpenCV build;
|
||||
- no GPU path has proven byte-identical Feature Files for this boundary.
|
||||
|
||||
Therefore Feature Extraction receives no production GPU path merely because the host has a GPU.
|
||||
|
||||
This does not authorize avoidable CPU serialism. Cross-image CPU parallelism remains valid where its
|
||||
Task contract has been proven.
|
||||
|
||||
## Environment and reproducibility
|
||||
|
||||
Lardon3D does not claim ORB Feature File bytes are identical across arbitrary:
|
||||
|
||||
- OpenCV versions;
|
||||
- platforms;
|
||||
- extractor backends.
|
||||
|
||||
Idempotence is defined inside the supported environment and extractor-version contract.
|
||||
|
||||
Any change that modifies durable Feature semantics or bytes requires explicit review and, when
|
||||
necessary, an extractor-version change. It must not silently reuse the old scientific identity.
|
||||
|
||||
## Feature File v2 and multi-descriptor support
|
||||
|
||||
**IMPLEMENTED v1A.**
|
||||
|
||||
ORB continues to write the historical Feature File v1 U8x32 representation.
|
||||
|
||||
SIFT and RootSIFT write Feature File v2 F32x128.
|
||||
|
||||
### v2 header
|
||||
|
||||
Feature File v2 has a 176-byte header.
|
||||
|
||||
Its fields include:
|
||||
|
||||
- magic;
|
||||
- explicit version;
|
||||
- header size;
|
||||
- feature count;
|
||||
- descriptor dimension;
|
||||
- descriptor type;
|
||||
- scalar size;
|
||||
- keypoint-record size;
|
||||
- decoded image dimensions;
|
||||
- capabilities;
|
||||
- keypoint/descriptor block offsets;
|
||||
- total size;
|
||||
- source SHA-256 at offset 72;
|
||||
- parameter fingerprint at offset 104;
|
||||
- 16-byte extractor kind at offset 136;
|
||||
- extractor version at offset 152;
|
||||
- twenty reserved zero bytes.
|
||||
|
||||
Stable descriptor type values are:
|
||||
|
||||
```text
|
||||
U8 = 1
|
||||
F32 = 2
|
||||
```
|
||||
|
||||
with scalar sizes 1 and 4.
|
||||
|
||||
Every binary32 value is explicitly encoded little-endian; persistent output is not a C/C++ struct
|
||||
dump.
|
||||
|
||||
Writer and reader reject NaN and Inf.
|
||||
|
||||
The typed descriptor readers:
|
||||
|
||||
- use `pread`;
|
||||
- refuse incompatible descriptor type;
|
||||
- return at most 256 features per call.
|
||||
|
||||
File-size limits remain:
|
||||
|
||||
```text
|
||||
Feature File v1 16 MiB
|
||||
Feature File v2 64 MiB
|
||||
```
|
||||
|
||||
The extraction facade also carries `descriptor_bytes`.
|
||||
|
||||
Publication requires exactly:
|
||||
|
||||
```text
|
||||
feature_count * descriptor_dimension * scalar_size
|
||||
```
|
||||
|
||||
and rejects both truncated and oversized descriptor buffers before file creation/publication.
|
||||
|
||||
The detailed multipass, grid and RootSIFT scientific contracts remain in
|
||||
[`precision_feature_pipeline.md`](precision_feature_pipeline.md).
|
||||
|
||||
## Downstream consumers
|
||||
|
||||
The Feature Store currently feeds implemented downstream stages:
|
||||
|
||||
```text
|
||||
Feature Store
|
||||
|
|
||||
v
|
||||
Visual Index
|
||||
|
|
||||
v
|
||||
Candidate Pair
|
||||
|
|
||||
v
|
||||
Matcher
|
||||
|
|
||||
v
|
||||
Geometric Verification
|
||||
|
|
||||
v
|
||||
Tracks
|
||||
```
|
||||
|
||||
Visual Index consumes the bounded Feature reader without changing Feature File format.
|
||||
|
||||
Candidate Pair consumes Feature Set identities indirectly through Visual Index membership.
|
||||
|
||||
Matcher consumes persisted Feature Sets and their descriptors.
|
||||
|
||||
Geometric Verification and Tracks consume later correspondence products; they do not rewrite Feature
|
||||
Store identity.
|
||||
|
||||
Sparse SfM Gates C through G are implemented and PASS/FROZEN, but real known-calibration Sparse SfM
|
||||
was not executed in the retained historical S21/A6000 campaigns.
|
||||
|
||||
## Real A6000 evidence
|
||||
|
||||
The retained real A6000 selected execution contains:
|
||||
|
||||
```text
|
||||
Feature Sets 689
|
||||
Candidate Pairs 38,420
|
||||
Match Results 38,420
|
||||
```
|
||||
|
||||
The v25 selected Feature-batch path completed all selected images.
|
||||
|
||||
The retained checkpoint is:
|
||||
|
||||
```text
|
||||
real-a6000-pre-sfm-2026-09-02
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
The later GV/Tracks continuation required:
|
||||
|
||||
```text
|
||||
Feature replay 0
|
||||
Candidate replay 0
|
||||
Matcher replay 0
|
||||
```
|
||||
|
||||
and continued from retained immutable upstream products.
|
||||
|
||||
This proves the current Feature-batch operational path and restart/reuse boundary on that campaign.
|
||||
It does not create calibration for that campaign and does not constitute real Sparse SfM execution.
|
||||
|
||||
## Current unfinished work
|
||||
|
||||
The following remain separate from the Feature Store scientific contract:
|
||||
|
||||
- global orphan-file reconciliation/scrub;
|
||||
- EXIF orientation handling where still required;
|
||||
- generic dependency/DAG scheduling;
|
||||
- future extractor/backend work only after explicit equivalence/version review.
|
||||
|
||||
Multi-image Feature execution itself is no longer an unfinished item:
|
||||
`features.extract.batch/1` is implemented and validated.
|
||||
|
||||
## Summary
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
|
||||
FEATURE_STORE_STATUS=IMPLEMENTED
|
||||
|
||||
FEATURE_FILE_V1=FROZEN
|
||||
FEATURE_FILE_V2=IMPLEMENTED
|
||||
|
||||
HISTORICAL_FEATURE_TASK=features.extract/1
|
||||
CURRENT_FEATURE_BATCH_TASK=features.extract.batch/1
|
||||
PROJECT_DB_V25_FEATURE_BATCH=IMPLEMENTED/VALIDATED
|
||||
|
||||
FEATURE_RESULT_IDENTITY=PER_IMAGE
|
||||
PER_IMAGE_FEATURE_RESULT=ATOMIC
|
||||
PER_IMAGE_ATOMICITY_REQUIRES_CROSS_IMAGE_SERIALISM=NO
|
||||
OWNER_ONLY_PUBLICATION_REQUIRES_SERIAL_PREPARATION=NO
|
||||
|
||||
FEATURE_GPU=REJECTED_WITH_MEASURED_OR_IMPLEMENTATION_EVIDENCE
|
||||
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
|
||||
REAL_A6000_FEATURE_SETS=689
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
| 148 | 12 | réservés, zéro obligatoire |
|
||||
|
||||
Chaque keypoint contient six mots de 32 bits : `x`, `y`, `size`, orientation,
|
||||
response en binary32, puis octave signé. `x/y` sont en pixels de l'image telle
|
||||
que décodée par OpenCV, origine en haut à gauche. `size` est le diamètre du
|
||||
voisinage en pixels. L'orientation est en degrés dans `[0,360)`. L'entrée `i`
|
||||
du bloc keypoints correspond exactement aux 32 octets du descriptor `i`.
|
||||
|
||||
Le validateur contrôle magic, version, réservés, type, dimension, bornes,
|
||||
offsets, taille exacte, multiplications, SHA-256 externe et cohérence DB. Une
|
||||
version future est distinguée d'une corruption lorsque le fichier et son record
|
||||
DB sont cohérents. Le reader utilise `pread`, accepte au plus 256 éléments par
|
||||
appel et ne charge jamais le fichier entier.
|
||||
|
||||
Le chemin DB est validé sous sa forme canonique exacte dérivée du SHA-256 avant
|
||||
toute ouverture. Une absence retourne `NOT_FOUND`; troncature, hash divergent,
|
||||
header ou métadonnées DB divergents retournent `CORRUPT`. Une version future
|
||||
retourne distinctement `UNSUPPORTED_VERSION`.
|
||||
|
||||
## Publication et persistance
|
||||
|
||||
Layout : `assets/features/<2 hex>/<sha256 complet lowercase>`. Le SHA-256 porte
|
||||
sur le Feature File complet. Le protocole est : temporaire local, écriture,
|
||||
`fsync`, hash, `link` atomique sans écrasement, validation complète lors d'une
|
||||
adoption concurrente, `fsync` du répertoire, puis transaction SQLite. Un échec
|
||||
du dernier `fsync` est enregistré `PUBLISHED_NOT_DURABLE`. Un échec SQLite après
|
||||
publication laisse un fichier orphelin et aucune ligne logique partielle.
|
||||
Un nouvel essai qui revalide l'asset et réussit le `fsync` promeut explicitement
|
||||
sa durabilité vers `DURABLE` dans la transaction DB.
|
||||
|
||||
Le sous-schéma introduit en v5 sépare `feature_assets`, `feature_sets` et
|
||||
`feature_extract_tasks`. L'unicité logique porte sur image, kind, version et
|
||||
fingerprint. Une tâche est persistée avant enqueue, passe par la queue et le
|
||||
Governor (CPU, IO, 576 Mio conservateurs, lot 1), puis checkpointée initialement
|
||||
et terminalement. La reprise recommence l'image entière : il n'existe pas de
|
||||
fausse reprise intra-ORB. La publication est idempotente.
|
||||
|
||||
Pause et annulation sont coopératives avant/après l'appel ORB ; cet appel n'est
|
||||
pas interruptible. L'image gérée est rehashée avant extraction. Une image
|
||||
uniforme produit légitimement un Feature Set READY vide.
|
||||
|
||||
Lardon3D ne garantit pas des octets ORB identiques entre versions d'OpenCV,
|
||||
plateformes ou backends. L'idempotence porte sur l'environnement courant et le
|
||||
contrat `extractor_version`; une évolution qui change durablement la sémantique
|
||||
ou les octets exige d'auditer et, si nécessaire, d'incrémenter cette version.
|
||||
|
||||
## Statut
|
||||
|
||||
**IMPLEMENTED** — ORB réel, formats v1/v2, assets content-addressed, DB v7,
|
||||
publication atomique, reader borné, task kind production et reprise automatique.
|
||||
|
||||
**NOT_YET_WIRED** — commande de réconciliation/scrub des orphelins, contrôle fin
|
||||
du backend parallèle OpenCV, orientation EXIF, lancement automatique de
|
||||
l'extraction après import et planification multi-image/DAG.
|
||||
|
||||
**IMPLEMENTED** — Visual Index v1 consomme ce reader sans changer le format.
|
||||
|
||||
**IMPLEMENTED** — paires candidates, Matcher v1 et Match Store v1 consomment
|
||||
les Feature Sets persistés.
|
||||
|
||||
**IMPLEMENTED** — vérification géométrique et tracks. **Sparse SfM Gate A
|
||||
PASS**, the Sparse SfM v16 persistence model is **FROZEN** after Gate B;
|
||||
Gate C geometry and the synchronous in-memory Gate D incremental core are
|
||||
**IMPLEMENTED / PASS**. Final per-component Gate E BA is **PASS / FROZEN**;
|
||||
Gate F project/task orchestration is **PASS / FROZEN**. Gate G is
|
||||
**PASS / FROZEN**.
|
||||
|
||||
## Extension v2 multi-descriptor
|
||||
|
||||
**IMPLEMENTED v1A.** ORB continue d'écrire exactement le v1 U8×32 historique.
|
||||
SIFT et RootSIFT écrivent le v2 F32×128. Le header v2 de 176 octets contient,
|
||||
aux offsets 0..64, magic, version, taille header, count, dimension, type, taille
|
||||
scalaire, taille record, dimensions image, capabilities, offsets des blocs et
|
||||
taille totale ; puis SHA source à 72, fingerprint à 104, kind sur 16 octets à
|
||||
136, version extracteur à 152 et vingt octets réservés nuls.
|
||||
|
||||
Les valeurs durables sont `U8=1` et `F32=2`, de tailles 1 et 4. Chaque binary32
|
||||
est encodé little-endian sans dump de struct. Writer et reader rejettent NaN et
|
||||
Inf. Les lectures `descriptors_u8` et `descriptors_f32` refusent un type
|
||||
incompatible, utilisent `pread` et restent limitées à 256 features. La limite
|
||||
v1 reste 16 Mio ; la limite v2 est 64 Mio.
|
||||
|
||||
La façade d'extraction transporte aussi `descriptor_bytes`. La publication
|
||||
exige exactement `count × dimension × scalar_size`; elle refuse donc un buffer
|
||||
tronqué ou surdimensionné avant toute lecture ou création de fichier.
|
||||
|
||||
ProjectDb v7 ajoute les métriques légères de couverture. Le détail multipasse,
|
||||
la grille et RootSIFT sont canoniques dans
|
||||
`precision_feature_pipeline.md`.
|
||||
|
|
|
|||
|
|
@ -1,408 +1,180 @@
|
|||
# Geometric Verification Model
|
||||
# Geometric Verification
|
||||
|
||||
## Status
|
||||
## 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 :
|
||||
|
||||
```text
|
||||
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
|
||||
Feature Set → Candidate Pair → Match Result → Geometric Verification Result
|
||||
```
|
||||
|
||||
This document owns the **persistent Geometric Verification Result model**.
|
||||
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.
|
||||
|
||||
It does not own the numerical estimator implementation. The current executable scientific verifier is
|
||||
documented in `geometric_verifier.md`.
|
||||
## Scientific ownership
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## Pipeline position
|
||||
## Parent Match Result
|
||||
|
||||
```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.
|
||||
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.
|
||||
|
||||
## Persistent identity
|
||||
|
||||
Exact identity:
|
||||
L'identité demandée et unique est :
|
||||
|
||||
```text
|
||||
(
|
||||
match_result_id,
|
||||
verifier_kind,
|
||||
verifier_version,
|
||||
parameter_fingerprint
|
||||
)
|
||||
(match_result_id, verifier_kind, verifier_version, parameter_fingerprint)
|
||||
```
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## Verifier kind
|
||||
|
||||
The persistent supported model kind is:
|
||||
Le modèle supporte uniquement `FUNDAMENTAL`, valeur persistante stable 1. Aucun comportement fictif
|
||||
`ESSENTIAL` ou `HOMOGRAPHY` n'est réservé dans l'API publique.
|
||||
|
||||
```text
|
||||
FUNDAMENTAL = 1
|
||||
```
|
||||
## Persistent states
|
||||
|
||||
Do not reserve fictitious `ESSENTIAL` or `HOMOGRAPHY` values in prose without an explicit versioned
|
||||
implementation decision.
|
||||
- `GEOMETRIC_REJECTED=1` : calcul scientifique terminé, critère non satisfait ;
|
||||
- `GEOMETRIC_VERIFIED=2` : calcul scientifique terminé, critère satisfait.
|
||||
|
||||
## Scientific states
|
||||
`FAILED`, `RUNNING`, `PAUSED` et `CANCELLED` appartiennent au Task Runtime. REJECTED peut conserver
|
||||
un nombre d'inliers non nul.
|
||||
|
||||
Completed scientific states are:
|
||||
## Model representation
|
||||
|
||||
```text
|
||||
GEOMETRIC_REJECTED = 1
|
||||
GEOMETRIC_VERIFIED = 2
|
||||
```
|
||||
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.
|
||||
|
||||
Runtime states such as RUNNING, FAILED, PAUSED or CANCELLED belong to Task Runtime, not this model.
|
||||
## Inlier representation
|
||||
|
||||
A rejected result may still contain non-zero inlier support.
|
||||
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`.
|
||||
|
||||
## Fundamental matrix representation
|
||||
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.
|
||||
|
||||
A verified Fundamental result contains nine SQLite `REAL` values:
|
||||
## Invariants
|
||||
|
||||
```text
|
||||
m00 ... m22
|
||||
```
|
||||
- `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.
|
||||
|
||||
in row-major order.
|
||||
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.
|
||||
|
||||
The persistent representation is binary64 through SQLite numeric semantics, not a C ABI struct dump.
|
||||
## Persistence semantics
|
||||
|
||||
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.
|
||||
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éé.
|
||||
|
||||
## Reuse
|
||||
|
||||
Exact reuse uses only the full persistent identity.
|
||||
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.
|
||||
|
||||
Existing exact result:
|
||||
## Invalidations
|
||||
|
||||
```text
|
||||
find
|
||||
-> validate
|
||||
-> reuse
|
||||
```
|
||||
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.
|
||||
|
||||
Never:
|
||||
## Project DB schema
|
||||
|
||||
```text
|
||||
INSERT OR REPLACE
|
||||
latest result
|
||||
closest fingerprint
|
||||
same parent with different version
|
||||
```
|
||||
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.
|
||||
|
||||
A new scientific verifier version creates another result identity.
|
||||
## API
|
||||
|
||||
## Parent deletion
|
||||
L'API publique implémente :
|
||||
|
||||
The parent FK uses delete-cascade semantics.
|
||||
- `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()`.
|
||||
|
||||
Explicit deletion of a Match Result deletes its dependent geometric results.
|
||||
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.
|
||||
|
||||
No parallel invalidation engine is required.
|
||||
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.
|
||||
|
||||
## Schema
|
||||
## Resource bounds
|
||||
|
||||
Project DB v12 introduced `geometric_verification_results`.
|
||||
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é.
|
||||
|
||||
The current schema head is v25.
|
||||
## Error ownership
|
||||
|
||||
Later schema additions do not redefine the v12 row format or identity.
|
||||
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.
|
||||
|
||||
## Public API
|
||||
## Recovery semantics
|
||||
|
||||
The model provides bounded create/load/find/list APIs for Geometric Verification Results.
|
||||
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 list API is paged and ordered by increasing ID.
|
||||
## Verifier execution contract
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Exact function declarations in the public headers remain authoritative.
|
||||
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.
|
||||
|
||||
## Error semantics
|
||||
## Explicitly out of scope
|
||||
|
||||
Creation distinguishes invalid local arguments from parent/identity constraints.
|
||||
GPU, Vulkan, OpenCL, shader et nouvelle orchestration restent hors périmètre de ce contrat de
|
||||
persistance.
|
||||
|
||||
Loaders return corruption rather than a partially interpreted result if:
|
||||
## Versioning
|
||||
|
||||
- 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
|
||||
```
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,486 +1,468 @@
|
|||
# Geometric Verifier v1 / v2 / v3
|
||||
|
||||
## Status
|
||||
## Scope
|
||||
|
||||
```text
|
||||
HISTORICAL_GEOMETRIC_VERIFIER_V1=FROZEN
|
||||
HISTORICAL_GEOMETRIC_VERIFIER_V2=FROZEN
|
||||
CURRENT_GEOMETRIC_VERIFIER_V3=PASS/FROZEN
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## Inputs
|
||||
|
||||
The exact Match Result supplies:
|
||||
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.
|
||||
|
||||
- Candidate Pair relation;
|
||||
- Feature Set A/B identities;
|
||||
- Match File path/size/SHA;
|
||||
- `match_count`.
|
||||
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.
|
||||
|
||||
Feature readers provide keypoints for the two immutable Feature Sets.
|
||||
## Fundamental matrix contract
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## Input ordering
|
||||
|
||||
Estimator row `i` corresponds exactly to Match File entry `i`.
|
||||
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.
|
||||
|
||||
The verifier does not reorder or deduplicate Match File rows.
|
||||
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 published inlier bit `i` always maps back to Match File entry `i`.
|
||||
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.
|
||||
|
||||
Out-of-range Feature indices or corrupt assets are runtime/input failure, not scientific rejection.
|
||||
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.
|
||||
|
||||
## Canonical observation identity
|
||||
## Coordinate representation
|
||||
|
||||
For preflight counting:
|
||||
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.
|
||||
|
||||
```text
|
||||
A = (feature_set_id_a, feature_index_a)
|
||||
B = (feature_set_id_b, feature_index_b)
|
||||
```
|
||||
## Algorithm candidates
|
||||
|
||||
V2/v3 require at least seven distinct A observations and seven distinct B observations.
|
||||
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é.
|
||||
|
||||
Failure publishes a zero-inlier `GEOMETRIC_REJECTED` result with a correctly sized all-zero mask and
|
||||
no Fundamental model.
|
||||
## Benchmark methodology
|
||||
|
||||
This preflight does not:
|
||||
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.
|
||||
|
||||
- rewrite Matcher evidence;
|
||||
- enforce one-to-one matching;
|
||||
- deduplicate coordinates;
|
||||
- perform a rank test;
|
||||
- perform collinearity analysis;
|
||||
- run Homography competition.
|
||||
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.
|
||||
|
||||
Distinct Feature IDs with identical coordinates remain distinct observations.
|
||||
| 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 |
|
||||
|
||||
## v3 acceptance-feasibility preflight
|
||||
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 additionally checks the durable configured `min_inlier_count`.
|
||||
## Determinism
|
||||
|
||||
When:
|
||||
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.
|
||||
|
||||
```text
|
||||
match_count < min_inlier_count
|
||||
```
|
||||
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.
|
||||
|
||||
the maximum possible support cannot satisfy acceptance, so V3 publishes the canonical zero rejection
|
||||
without calling USAC.
|
||||
## Random seed policy
|
||||
|
||||
The threshold comes from configuration, never a hidden constant.
|
||||
|
||||
## Scientific model
|
||||
|
||||
The only production model is a 3x3 Fundamental matrix.
|
||||
|
||||
Accepted output must be finite, non-zero and canonical.
|
||||
|
||||
The current verifier does not add Essential/Homography model competition.
|
||||
|
||||
## Production algorithm
|
||||
|
||||
The production estimator uses explicit OpenCV USAC/MAGSAC parameters rather than a hidden preset.
|
||||
|
||||
Scientific choices include:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
The internal scientific estimator remains serial:
|
||||
|
||||
```text
|
||||
UsacParams::isParallel=false
|
||||
```
|
||||
|
||||
Outer Task-level concurrency is separate.
|
||||
|
||||
## Random seed
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## Parameter fingerprint
|
||||
|
||||
The verifier fingerprint is SHA-256 over the frozen canonical 84-byte encoding.
|
||||
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.
|
||||
|
||||
Domain:
|
||||
| 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 |
|
||||
|
||||
```text
|
||||
L3DGVFP1
|
||||
```
|
||||
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.
|
||||
|
||||
It explicitly encodes:
|
||||
## Acceptance policy
|
||||
|
||||
- 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.
|
||||
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.
|
||||
|
||||
Integers are little-endian.
|
||||
## Fundamental matrix canonicalization
|
||||
|
||||
Binary64 values use explicit IEEE-754 bits encoded little-endian.
|
||||
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.
|
||||
|
||||
No C struct dump participates.
|
||||
## Inlier mask generation
|
||||
|
||||
Current v3 production fingerprint:
|
||||
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.
|
||||
|
||||
```text
|
||||
6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c
|
||||
```
|
||||
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é.
|
||||
|
||||
Historical v1/v2 fingerprints remain distinct and valid.
|
||||
## Scientific rejection
|
||||
|
||||
## Acceptance
|
||||
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.
|
||||
|
||||
A candidate is verified only when the frozen support policy is satisfied:
|
||||
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.
|
||||
|
||||
```text
|
||||
inlier_count >= 16
|
||||
inlier_count / match_count >= 0.20
|
||||
```
|
||||
## Execution failure
|
||||
|
||||
Otherwise the result is scientific `GEOMETRIC_REJECTED`.
|
||||
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.
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
One scientific atomic item is one Match Result with at most 8192 correspondences.
|
||||
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é.
|
||||
|
||||
Current outer-parallel Task reservation uses approximately:
|
||||
À 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.
|
||||
|
||||
```text
|
||||
8 MiB per admitted parent
|
||||
batch/window <= 16
|
||||
useful CPU participants <= 8
|
||||
GPU 0
|
||||
```
|
||||
## CPU policy
|
||||
|
||||
The window is safe to 16; CPU8 is the retained useful maximum from measurement.
|
||||
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.
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
Current classification:
|
||||
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.
|
||||
|
||||
```text
|
||||
GEOMETRIC_VERIFIER_GPU=NOT_JUSTIFIED
|
||||
```
|
||||
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é.
|
||||
|
||||
Measured CPU units remained small enough that no production GPU backend was justified.
|
||||
## Task Runtime
|
||||
|
||||
No Vulkan/OpenCL/CUDA verifier backend is currently part of the production identity.
|
||||
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.
|
||||
|
||||
CPU outer parallelism remains valid and should not be disabled merely because GPU is rejected.
|
||||
`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.
|
||||
|
||||
## Durable Task
|
||||
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.
|
||||
|
||||
Task Kind:
|
||||
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.
|
||||
|
||||
```text
|
||||
geometric_verifier.run/1
|
||||
```
|
||||
WHY GENERIC TASK PERSISTENCE IS INSUFFICIENT: aucun champ de payload métier dans le snapshot v1.
|
||||
|
||||
Project DB v13 adds the typed task payload required because generic checkpoint v1 does not contain the
|
||||
scientific verifier parameters/cursor.
|
||||
REQUIRED DURABLE FIELDS: configuration scientifique v1, fingerprint et dernier Match Result
|
||||
publié puis checkpointé.
|
||||
|
||||
Durable payload includes the immutable scientific configuration and:
|
||||
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.
|
||||
|
||||
```text
|
||||
after_match_result_id
|
||||
```
|
||||
## Checkpoint/recovery
|
||||
|
||||
The fingerprint is revalidated on reconstruction.
|
||||
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.
|
||||
|
||||
The durable payload does not contain:
|
||||
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.
|
||||
|
||||
- `cv::Mat`;
|
||||
- RNG engine state;
|
||||
- Governor feedback;
|
||||
- hardware identity;
|
||||
- CPU mask;
|
||||
- transient buffers.
|
||||
## Cancellation
|
||||
|
||||
## Restart
|
||||
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.
|
||||
|
||||
Pagination follows increasing `match_result_id` and does not assume contiguous IDs.
|
||||
## Backend policy
|
||||
|
||||
A GVR is published before the durable cursor advances.
|
||||
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.
|
||||
|
||||
Crash after publication but before checkpoint may replay that parent.
|
||||
## Core publication and reuse
|
||||
|
||||
Restart finds/revalidates the exact GVR identity and reuses it.
|
||||
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.
|
||||
|
||||
No overwrite is performed.
|
||||
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é.
|
||||
|
||||
## Historical resource normalization
|
||||
## Production algorithm
|
||||
|
||||
The exact historical serial resource shape remains accepted only for restart compatibility.
|
||||
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é.
|
||||
|
||||
It may be normalized ephemerally to the current outer-parallel capability.
|
||||
## Production parameters
|
||||
|
||||
The original checkpoint is not rewritten.
|
||||
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.
|
||||
|
||||
Neighboring resource shapes are rejected rather than guessed.
|
||||
## Validation
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Real S21 GV v3
|
||||
|
||||
Retained S21 proof:
|
||||
`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.
|
||||
|
||||
```text
|
||||
REAL_S21_GV_V3=PASS/FROZEN
|
||||
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.
|
||||
|
||||
Match Results 172,741
|
||||
Applicable MATCHED 172,275
|
||||
Verified 24,065
|
||||
Rejected 148,210
|
||||
non-applicable 466
|
||||
duplicate mapping 0
|
||||
```
|
||||
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.
|
||||
|
||||
The v3 fingerprint is the current production fingerprint.
|
||||
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.
|
||||
|
||||
A complete replay produced zero new GVRs.
|
||||
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 SIGKILL/restart proof resumed the same Task and converged to the same complete GVR set.
|
||||
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.
|
||||
|
||||
The run stopped before Track Builder at the original GV-only checkpoint.
|
||||
## Maintenance outer-parallel — preuve représentative réelle
|
||||
|
||||
Later S21 Track evidence exists separately.
|
||||
**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.
|
||||
|
||||
## Real A6000 v3
|
||||
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.
|
||||
|
||||
The retained A6000 pre-SfM continuation contains:
|
||||
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é.
|
||||
|
||||
```text
|
||||
Match Results 38,420
|
||||
Applicable GVRs 37,805
|
||||
Verified GVRs 10,952
|
||||
Rejected GVRs 26,853
|
||||
duplicate mappings 0
|
||||
```
|
||||
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.
|
||||
|
||||
Current fingerprint:
|
||||
## Out of scope
|
||||
|
||||
```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
|
||||
```
|
||||
Tracks, model competition, classification planaire ou faible parallaxe, Essential, calibration,
|
||||
pose, triangulation, bundle adjustment, SfM et Vulkan RANSAC.
|
||||
|
|
|
|||
|
|
@ -198,50 +198,6 @@ au plus `cpu_threads - 1` threads enfants. Tous les enfants sont joints avant
|
|||
la fin de la séquence, la libération de réservation ou
|
||||
`lardon3d_task_sequence_break()`.
|
||||
|
||||
## Développement RAW d'une exécution sélectionnée
|
||||
|
||||
`raw.develop.batch/1` conserve la politique RAW v1, les octets PNG,
|
||||
L3DRAWD1, le fingerprint, le content-addressing et toutes les identités du
|
||||
développeur par Capture. Une séquence prend au plus huit items contigus depuis
|
||||
le curseur durable de l'exécution sélectionnée et utilise au plus le nombre de
|
||||
CPUs admis. Chaque participant possède son LibRaw et ses buffers privés ; les
|
||||
896 Mio chargés par item admis couvrent le workspace LibRaw borné à 40 MP, les
|
||||
copies RGB/BGR, les buffers PNG/validation, la pile enfant bornée de 1 Mio et la
|
||||
marge allocateur/codec. Le petit contexte propriétaire est inclus dans cette
|
||||
charge : un budget hôte post-réserve de 7 Gio admet donc exactement les huit
|
||||
participants sûrs, sans débit fixe redondant. Cette estimation est une borne
|
||||
opérationnelle de mémoire par participant, pas une limite scientifique de
|
||||
dataset.
|
||||
OpenCV est fixé à un thread interne afin de ne pas multiplier le fan-out.
|
||||
|
||||
Les enfants ne modifient ni curseur sélectionné ni progression Task. Après leur
|
||||
jointure complète, le callback Queue propriétaire publie seul les mappings
|
||||
`item_index -> image_id` dans l'ordre croissant. Chaque transaction de mapping
|
||||
avance le curseur contigu, puis seulement le checkpoint Task peut avancer. Une
|
||||
erreur, une annulation ou un échec de création d'enfant joint tout le travail
|
||||
lancé ; des assets immuables content-addressés peuvent déjà exister, mais le
|
||||
curseur ne les devine pas et la reprise converge par le développeur existant.
|
||||
Une `sequence_break` sépare toutes les fenêtres, donc la sélection entière ne
|
||||
retient jamais une réservation longue.
|
||||
|
||||
## Extraction Feature d'une exécution sélectionnée
|
||||
|
||||
`features.extract.batch/1` conserve exactement ORB v1, ses paramètres, son
|
||||
fingerprint, ses keypoints/descripteurs U8×32 et le Feature File existant. Une
|
||||
fenêtre contient au plus douze images indépendantes de l'ordre sélectionné. Le
|
||||
propriétaire configure OpenCV à un thread pour éviter tout fan-out imbriqué ;
|
||||
chaque participant possède son décodage et sa sortie bornés, sans accès SQLite,
|
||||
puis tous sont joints.
|
||||
|
||||
Le propriétaire seul publie dans l'ordre et avance le curseur v25 après preuve
|
||||
du Feature Set READY exact. CPU et lot partent du minimum. Comme un CPU
|
||||
supplémentaire ne peut être exercé que par une image indépendante de la même
|
||||
fenêtre, le Governor essaie et accepte ces deux limites ensemble (`1/1`, `2/2`,
|
||||
`4/4`, puis le maximum sûr admis). Les observations de fenêtres durables
|
||||
permettent de retenir le dernier palier apportant au moins 5 %. Le plafond douze
|
||||
est une capacité opérationnelle de benchmark,
|
||||
jamais une limite scientifique, et aucune voie GPU n'est introduite.
|
||||
|
||||
## Calcul et publication Visual Index
|
||||
|
||||
Une séquence sélectionne le même préfixe durable d'au plus seize Feature Sets
|
||||
|
|
|
|||
|
|
@ -1,459 +1,223 @@
|
|||
# Lardon3D Architecture Overview
|
||||
# Vue d'ensemble de l'architecture Lardon3D
|
||||
|
||||
## Purpose
|
||||
## Finalité et flux global
|
||||
|
||||
Lardon3D is a persistent, incremental, resource-aware photogrammetry engine for Linux.
|
||||
|
||||
The TUI is the operational control center for projects, acquisition, Tasks, durable progress,
|
||||
resource state, optical configuration and optional external-storage control. Rich visualization and
|
||||
live acquisition remain separate product areas and must consume validated snapshots rather than
|
||||
mutable worker buffers.
|
||||
|
||||
## Current authority
|
||||
Lardon3D est un moteur de reconstruction géométrique persistante et incrémentale,
|
||||
piloté par une TUI ncursesw. Le terminal reste le centre de contrôle : il gère
|
||||
les projets, lance les opérations, présente leur progression et permet leur
|
||||
annulation. Le viewer sera un composant graphique séparé mais intégré à
|
||||
l'interface pour un usage confortable sur un seul écran.
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
CURRENT_PRODUCTION_TASK_KINDS=16
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
The current Project DB head is additive:
|
||||
|
||||
```text
|
||||
v22 Selected scientific execution foundation
|
||||
v23 Generic optical-context overlay
|
||||
v24 raw.develop.batch/1 persistence
|
||||
v25 features.extract.batch/1 persistence
|
||||
```
|
||||
|
||||
Earlier schema versions remain valid historical contracts where their documentation says so. Current
|
||||
documentation must not present an older version as the active head merely because that version owns a
|
||||
specific historical scientific or persistence boundary.
|
||||
|
||||
## Global execution model
|
||||
|
||||
```text
|
||||
TUI / Project
|
||||
|
|
||||
v
|
||||
TUI / Projet
|
||||
↓
|
||||
Task
|
||||
|
|
||||
v
|
||||
immutable resource estimate / capability
|
||||
|
|
||||
v
|
||||
bounded Task Queue
|
||||
(one active callback)
|
||||
|
|
||||
v
|
||||
Resource Governor
|
||||
(admission + reservation)
|
||||
|
|
||||
v
|
||||
admitted owner callback
|
||||
|
|
||||
+--> bounded internal participants when justified
|
||||
|
|
||||
v
|
||||
deterministic owner publication
|
||||
|
|
||||
v
|
||||
durable checkpoint / restart boundary
|
||||
|
|
||||
v
|
||||
validated snapshot consumers
|
||||
↓
|
||||
Estimate
|
||||
↓
|
||||
Task Queue
|
||||
↓
|
||||
Resource Governor / Reservation
|
||||
↓
|
||||
callback worker admis
|
||||
↓
|
||||
Résultat atomique
|
||||
↓
|
||||
Viewer live
|
||||
```
|
||||
|
||||
The Queue owns bounded dispatch and backpressure. The Governor owns production resource admission.
|
||||
The active Task owns its execution state and any bounded internal participants. Scientific stores own
|
||||
their own identities and artifact validation.
|
||||
|
||||
No layer may silently absorb another layer's authority.
|
||||
|
||||
## Canonical resource objective
|
||||
|
||||
Lardon3D first preserves the interactive host reserve required for normal workstation use. Safe and
|
||||
useful capacity beyond that reserve belongs to the active workload.
|
||||
|
||||
The objective is:
|
||||
|
||||
```text
|
||||
MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
```
|
||||
|
||||
This does not mean maximizing utilization percentages for appearance. It means using the largest
|
||||
currently safe and useful CPU, RAM, I/O and validated accelerator capability while preserving
|
||||
scientific identity, deterministic publication, host responsiveness and pressure limits.
|
||||
|
||||
The companion rule is:
|
||||
|
||||
```text
|
||||
SERIALISM_REQUIRES_PROOF
|
||||
```
|
||||
|
||||
Per-item atomicity does not imply cross-item serialization. Ordered or owner-only durable publication
|
||||
does not imply serial preparation. Independent work should expose bounded concurrency when exact
|
||||
science and persistence semantics can be preserved.
|
||||
|
||||
A CPU1 or batch1 path may remain when serialism, a measured scaling knee, memory, I/O, GPU execution,
|
||||
exclusive state or another concrete constraint justifies it. Historical conservative descriptors are
|
||||
not portable performance ceilings.
|
||||
|
||||
On the current validation host, the normal observed outcome is approximately:
|
||||
|
||||
```text
|
||||
16 logical CPUs total
|
||||
4 logical CPUs reserved for interactive host use
|
||||
12 logical CPUs available to the compute pool
|
||||
~3 GiB MemAvailable hard reserve
|
||||
Radeon 780M UMA available to validated and useful GPU backends
|
||||
```
|
||||
|
||||
These values are reference-host observations, not product constants.
|
||||
|
||||
## Current components
|
||||
## Composants actuels
|
||||
|
||||
### Project
|
||||
Gestion persistante des projets : création, ouverture, fermeture, structure
|
||||
de répertoires. Chaque projet regroupe configuration, images originales,
|
||||
manifeste, résultats, exports et journaux.
|
||||
|
||||
Persistent project lifecycle, stable identity, directory layout and Project DB ownership.
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
**Status:** IMPLEMENTED
|
||||
### Import
|
||||
Import asynchrone et annulable d'images dans un projet. Copie individuelle
|
||||
des fichiers admissibles et maintenance d'un manifeste cohérent.
|
||||
|
||||
### Project Database
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
SQLite owns durable logical identities, relations, typed Task payloads, scientific metadata and
|
||||
references to external immutable artifacts.
|
||||
### Import Task
|
||||
Premier type métier persistant. Il s'exécute par lots bornés dans le runtime et
|
||||
la Queue génériques, cible explicitement un ScanSet et peut être reconstruit
|
||||
puis repris.
|
||||
|
||||
The current schema head is v25. The scientific meaning of historical rows remains owned by the
|
||||
versioned contracts that created them.
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
**Status:** CURRENT / v25
|
||||
### ScanSet et Image Catalog
|
||||
Catalogue SQLite persistant séparant acquisition, image logique et asset
|
||||
physique SHA-256. Les parcours persistants sont paginés ; l'ancien catalogue
|
||||
mémoire depuis `manifest.tsv` reste une façade legacy pour la TUI.
|
||||
|
||||
### Import and ScanSets
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
Import materializes managed immutable assets and logical images under explicit ScanSets. Provenance
|
||||
from external source paths remains distinct from managed asset identity.
|
||||
### Profils optiques et calibrations
|
||||
|
||||
**Status:** IMPLEMENTED
|
||||
Project DB v23 ajoute un overlay générique distinct pour profils de boîtiers,
|
||||
objectifs électroniques ou manuels, configurations optiques, affectations aux
|
||||
campagnes/Captures et calibrations explicitement compatibles. La migration
|
||||
n'infère ni ne backfill aucune donnée S21/A6000 ; ces appareils restent des
|
||||
preuves de validation, pas des identités produit.
|
||||
|
||||
### Capture / Asset Provenance
|
||||
**Statut :** IMPLEMENTED — OVERLAY ADDITIF v23
|
||||
|
||||
Capture identity remains distinct from file, Asset, `image_id`, SHA-256, path, Task ID and campaign
|
||||
group ID. RAW and JPEG siblings may belong to one physical Capture without becoming one file or one
|
||||
scientific image identity.
|
||||
### Image View
|
||||
Vues triées et filtrées du catalogue pour la TUI. Ne modifie pas le
|
||||
catalogue, le manifeste ou les images.
|
||||
|
||||
**Status:** PASS / FROZEN
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
### Bounded acquisition discovery and campaign execution
|
||||
### TUI observatoire / centre de contrôle
|
||||
|
||||
Discovery and planning are bounded and deterministic. Automatic grouping requires the documented
|
||||
strong evidence; otherwise explicit caller confirmation remains `CALLER_EXPLICIT`.
|
||||
Le thread principal possède ncurses, l'entrée et le rendu. Un modèle pur reçoit
|
||||
des copies bornées de l'unique Queue et du Governor, coalescées autour d'une
|
||||
seconde, et présente progression durable, ETA honnête, pipeline, ressources,
|
||||
profils optiques et SSD. Les dimensions validées sont full ≥100×30, compact de
|
||||
référence 72×20, minimum 60×15, puis le fallback `Terminal trop petit`. Les
|
||||
couleurs s'accompagnent toujours de libellés textuels et `F10 SSD` reste visible
|
||||
au minimum supporté.
|
||||
|
||||
Durable campaign execution uses the existing Task, Queue, Governor and Project DB recovery model.
|
||||
Ouvrir, fermer ou changer de projet détruit/joint l'unique Queue avant de
|
||||
fermer Project DB, puis recrée une Queue vide et rebranche l'observation. Les
|
||||
ABI historiques Task/Resource/AppState/layout restent inchangées ; les vues
|
||||
riches utilisent des structures et fonctions additives décrites dans
|
||||
[Runtime](runtime.md).
|
||||
|
||||
**Status:** PASS / FROZEN
|
||||
|
||||
### Photo Quality Triage
|
||||
|
||||
Quality analysis is an operational selection layer, not a scientific identity. It produces explicit
|
||||
GOOD / SUSPECT / REJECT recommendations and keeps human overrides distinct.
|
||||
|
||||
**Status:** PASS / FROZEN
|
||||
|
||||
### Selected scientific execution
|
||||
|
||||
The selected-execution snapshot binds the retained acquisition groups to explicit scientific
|
||||
representations without inferring identity from paths, filenames or metadata.
|
||||
|
||||
**Status:** PASS / FROZEN
|
||||
|
||||
### Optical profiles and calibration selection
|
||||
|
||||
Project DB v23 provides generic camera-body profiles, lens profiles, optical configurations,
|
||||
campaign/Capture assignments, calibration profiles and explicit compatible calibration selection.
|
||||
|
||||
Electronic metadata aliases are exact. Manual lenses without EXIF are normal. Missing or ambiguous
|
||||
identity remains unresolved rather than guessed.
|
||||
|
||||
**Status:** IMPLEMENTED / VALIDATED
|
||||
|
||||
### RAW development
|
||||
|
||||
The historical `raw.develop/1` path remains valid. Project DB v24 adds
|
||||
`raw.develop.batch/1`, allowing bounded independent RAW preparation with deterministic owner-only
|
||||
publication in selected-item order.
|
||||
|
||||
Per-Capture publication remains atomic while cross-Capture preparation may be concurrent.
|
||||
|
||||
**Status:** IMPLEMENTED / VALIDATED
|
||||
**Statut :** CURRENT / VALIDATED OPERATIONAL
|
||||
|
||||
### Feature Store
|
||||
Extraction ORB réelle par tâche persistante, Feature Sets logiques et assets
|
||||
binaires content-addressed lisibles par plages bornées.
|
||||
|
||||
Feature Store publishes immutable content-addressed Feature Files and bounded typed readers for ORB,
|
||||
SIFT and RootSIFT data.
|
||||
|
||||
The historical `features.extract/1` task remains valid. Project DB v25 adds
|
||||
`features.extract.batch/1`, which prepares independent selected images with bounded participants and
|
||||
publishes Feature Sets deterministically through the owner callback.
|
||||
|
||||
**Status:** IMPLEMENTED / VALIDATED
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
### Visual Index
|
||||
Index de retrieval ORB LSH persistant en segments immuables. Il indexe les
|
||||
Feature Sets par lots bornés et retourne des candidats inter-ScanSets sans
|
||||
matching géométrique.
|
||||
|
||||
The ORB LSH Visual Index is persistent and segmented. It indexes homogeneous Feature Sets in bounded
|
||||
updates and retrieves candidate relationships without performing geometric validation.
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
**Status:** IMPLEMENTED
|
||||
### Task
|
||||
Moteur de tâches avec états, progression, pause/reprise coopérative,
|
||||
annulation, checkpoints et estimations de ressources.
|
||||
|
||||
### Candidate Pair
|
||||
|
||||
Candidate Pair generation answers which image pairs are worth exploring. Scientific pair identity and
|
||||
canonical ordering remain independent from the amount of execution parallelism used to prepare them.
|
||||
|
||||
Candidate currently uses a bounded coupled CPU/batch ladder because additional CPU cannot exercise
|
||||
additional independent pair work while the admitted item window remains one.
|
||||
|
||||
**Status:** IMPLEMENTED
|
||||
|
||||
### Matcher
|
||||
|
||||
Matcher produces deterministic Match Results from Feature Sets.
|
||||
|
||||
ORB supports the validated Vulkan hot path with exact CPU fallback. SIFT and RootSIFT remain CPU
|
||||
OpenCV L2 paths. GPU use is selected only when a backend is both validated and useful.
|
||||
|
||||
**Status:** IMPLEMENTED
|
||||
|
||||
### Geometric Verification
|
||||
|
||||
Geometric Verifier v3 performs Fundamental USAC/MAGSAC verification with bounded restartable Task
|
||||
execution. Scientific solver parallelism remains controlled by its contract while cross-parent
|
||||
preparation can use bounded Governor-admitted participants.
|
||||
|
||||
**Status:** IMPLEMENTED / VALIDATED
|
||||
|
||||
### Track Model and Track Builder
|
||||
|
||||
Track Model v1 and Track Builder publish immutable Track Sets with deterministic identity and bounded
|
||||
recovery semantics.
|
||||
|
||||
Retained real-data evidence includes:
|
||||
|
||||
```text
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
**Status:** PASS / FROZEN
|
||||
|
||||
### Sparse SfM
|
||||
|
||||
Calibrated Sparse SfM capability is implemented through Gates C-G:
|
||||
|
||||
- Gate C: calibrated geometric primitives;
|
||||
- Gate D: incremental reconstruction core;
|
||||
- Gate E: final per-component Bundle Adjustment;
|
||||
- Gate F: durable Task orchestration and atomic Project DB publication;
|
||||
- Gate G: Governor admission and resource integration.
|
||||
|
||||
Phase H v1 adds incremental enrichment from immutable predecessor snapshots without redefining Gate F
|
||||
scientific identity.
|
||||
|
||||
**Status:** C-G PASS / FROZEN; PHASE H V1 PASS / FROZEN
|
||||
|
||||
Implemented capability must remain distinct from execution on a particular real campaign. The
|
||||
historical S21 and A6000 Engine Bay campaigns remain `CALIBRATION_UNAVAILABLE` for known-calibration
|
||||
Sparse SfM. The retained A6000 proof intentionally stopped before Sparse SfM and Dense/MVS.
|
||||
|
||||
### MVS boundary
|
||||
|
||||
MVS-M1 provides the validated bounded external OpenMVS boundary and deterministic COLMAP/PLY exchange
|
||||
contracts.
|
||||
|
||||
Durable dense publication, full Dense/MVS orchestration, mesh refinement, texturing and export remain
|
||||
future work.
|
||||
|
||||
**Status:** PASS / FROZEN boundary
|
||||
|
||||
### Task Runtime
|
||||
|
||||
Tasks own lifecycle state, progress, cooperative pause/cancel behavior, sequence boundaries,
|
||||
checkpoints and typed durable reconstruction.
|
||||
|
||||
**Status:** IMPLEMENTED
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
### Task Queue
|
||||
File FIFO avec worker unique, sélection de la première tâche admissible,
|
||||
backpressure et bornage du nombre de tâches en attente.
|
||||
|
||||
The Queue provides bounded FIFO dispatch with one active callback, stable scanning and resource-WAIT
|
||||
bypass behavior. It does not own resource policy.
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
**Status:** IMPLEMENTED
|
||||
### Hardware Profile
|
||||
Détection des capacités matérielles statiques : cœurs CPU, RAM, GPU/VRAM.
|
||||
|
||||
### Task Kind Registry
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
The production registry currently contains 16 Task kinds. Historical documents may legitimately
|
||||
record smaller inventories at their checkpoint.
|
||||
### Resource Snapshot
|
||||
Capture instantanée des ressources disponibles : RAM libre, charge CPU,
|
||||
VRAM disponible.
|
||||
|
||||
**Status:** IMPLEMENTED
|
||||
|
||||
### Hardware Profile and Resource Snapshot
|
||||
|
||||
Hardware Profile detects static host capability. Resource Snapshot captures current observable
|
||||
capacity and pressure signals required for admission.
|
||||
|
||||
Unavailable optional telemetry remains unknown; it must not be fabricated.
|
||||
|
||||
**Status:** IMPLEMENTED
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
### Resource Governor
|
||||
Arbitrage centralisé des budgets (RAM, GPU, CPU, IO), calcul de lots
|
||||
adaptatifs, réservations opaques et historique borné de métriques.
|
||||
|
||||
The Governor is the sole production authority for CPU, RAM, GPU, I/O admission and process-local
|
||||
scratch leases where explicitly supported.
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
Pressure may throttle future admission. When pressure clears, useful resources must be eligible to
|
||||
ramp back up; throttling is not a permanent lower ceiling.
|
||||
### Contrôleur SSD externe optionnel
|
||||
|
||||
UMA GPU allocations are charged exactly once against host RAM. Swap, zram and external scratch never
|
||||
become admitted RAM.
|
||||
Frontière physique UDisks2 pour la paire de labels `LARDON_SWAP` et
|
||||
`LARDON_SCRATCH`, avec identité Drive+UUID stable, état borné, leases scratch
|
||||
et drain sûr. Il ne formate, ne répare ni ne force jamais l'hôte et ne remplace
|
||||
pas le Resource Governor. La TUI exécute ses actions dans un seul thread
|
||||
joinable et le Governor enregistre l'état physique puis orchestre seul les
|
||||
leases scratch de production. Les quatorze Task kinds courants n'en consomment
|
||||
aucun ; capacité visible ne signifie donc pas usage.
|
||||
|
||||
**Status:** IMPLEMENTED / VALIDATED
|
||||
**Statut :** CURRENT / VALIDATED OPERATIONAL
|
||||
|
||||
### Optional external SSD controller
|
||||
### Candidate Pair
|
||||
Sous-système de génération et persistance de paires d'images candidates pour
|
||||
le matching géométrique. Répond uniquement à « quelles paires valent la peine
|
||||
d'être explorées ? » sans validation géométrique.
|
||||
|
||||
The UDisks2/GDBus controller owns the reviewed physical lifecycle for the exact
|
||||
`LARDON_SWAP` / `LARDON_SCRATCH` device contract. It is not a second scheduler or Governor.
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
The Governor remains the sole production entry for scratch leases. The current 16 production Task
|
||||
kinds have zero authoritative scratch consumption; visible storage capacity is not fabricated usage.
|
||||
### Match Result
|
||||
Persistance d'un calcul descriptor-level réussi entre deux Feature Sets
|
||||
liés à une Candidate Pair. Identité déterministe par
|
||||
`(candidate_pair_id, feature_set_id_a, feature_set_id_b, matcher_kind, matcher_version, parameter_fingerprint)`.
|
||||
Validation d'appartenance Feature Set → image. Les correspondances vivent dans
|
||||
le Match Store v1 content-addressed; les échecs restent dans le Task Runtime.
|
||||
|
||||
**Status:** CURRENT / VALIDATED OPERATIONAL
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
### TUI observatory and control center
|
||||
### Matcher
|
||||
Matching de descripteurs entre deux Feature Sets via BFMatcher OpenCV
|
||||
(ORB Hamming, SIFT/RootSIFT L2) avec Lowe ratio test. Produit un
|
||||
Match File content-addressed et un Match Result dans Project DB.
|
||||
Déterministe, idempotent, borné.
|
||||
|
||||
ncurses input and rendering remain on the main thread. Runtime observation is bounded and coalesced.
|
||||
The TUI exposes project state, Tasks, durable progress, resource state, optical configuration and SSD
|
||||
control.
|
||||
**Statut :** IMPLEMENTED
|
||||
|
||||
Validated layout boundaries are:
|
||||
### Sparse SfM
|
||||
Les primitives géométriques calibrées Gate C, le noyau incrémental synchrone
|
||||
Gate D et le Bundle Adjustment final par composante Gate E sont implémentés.
|
||||
Gate E traite en mémoire une copie du résultat Gate D immutable, sur CPU avec
|
||||
un thread Ceres, et publie atomiquement chaque candidate acceptée. Il n'intègre
|
||||
ni persistance, ni Task Runtime, ni Resource Governor.
|
||||
|
||||
```text
|
||||
full layout >= 100x30
|
||||
reference compact 72x20
|
||||
minimum supported 60x15
|
||||
```
|
||||
**Statut :** GATES C/D/E — PASS / FROZEN
|
||||
|
||||
Below the minimum, only the bounded terminal-too-small fallback is rendered.
|
||||
Gate F relie ces noyaux au Task Runtime durable, à la Queue/Governor/Reservation
|
||||
et à la publication Project DB v17 atomique et idempotente.
|
||||
|
||||
Opening, closing or switching a project is a Queue/DB lifetime boundary: destroy and join the sole
|
||||
Queue first, including finished callbacks, then close Project DB, recreate one empty Queue and rebind
|
||||
observers.
|
||||
**Statut :** GATE F — PASS / FROZEN. Gate G est **PASS / FROZEN**.
|
||||
|
||||
**Status:** CURRENT / VALIDATED OPERATIONAL
|
||||
La phase de pipeline H `incremental_reconstruction.run` v1 enrichit un snapshot
|
||||
publié et produit un nouveau snapshot complet avec provenance prédécesseur dans
|
||||
Project DB v18. Elle reste scientifiquement distincte de Gate F et ne modifie
|
||||
ni F0 ni les contrats Gate D/E.
|
||||
|
||||
## Publication and restart model
|
||||
**Statut :** PHASE H V1 — PASS / FROZEN.
|
||||
|
||||
Long-running processing follows bounded sequence boundaries:
|
||||
## Résultats et publication live
|
||||
|
||||
```text
|
||||
admit bounded work
|
||||
-> prepare bounded items
|
||||
-> join participants
|
||||
-> publish deterministic atomic results
|
||||
-> persist typed cursor/state
|
||||
-> checkpoint generic Task progress
|
||||
-> release transient buffers
|
||||
-> sequence break / re-admission
|
||||
```
|
||||
Les traitements fonctionnent par séquences adaptatives : lire un lot borné,
|
||||
calculer, écrire un résultat atomique, libérer la mémoire, puis traiter le
|
||||
lot suivant. La stabilité du système hôte et la réactivité de la TUI ont
|
||||
priorité sur le débit maximal.
|
||||
|
||||
The exact order varies by Task contract, but durable progress must never claim work that has not
|
||||
reached its authoritative publication boundary.
|
||||
Le viewer consomme des snapshots de résultats validés et publiés
|
||||
atomiquement. Il ne lit jamais un fichier intermédiaire et ne partage pas
|
||||
directement les buffers de travail d'un worker. Une interruption doit laisser
|
||||
le dernier snapshot validé exploitable et permettre la reprise à une
|
||||
frontière de séquence connue.
|
||||
|
||||
A crash may leave generic Task progress behind an already durable immutable result where the
|
||||
documented retry contract permits exact reuse. Recovery must converge from durable identity; it must
|
||||
not guess identity from paths, timestamps, basenames or similar metadata.
|
||||
## Invariants fondamentaux
|
||||
|
||||
## Viewer and live boundaries
|
||||
- Aucun callback de tâche n'est lancé sans réservation active validée.
|
||||
- La Queue ne décide jamais des ressources.
|
||||
- Le Resource Governor est l'unique propriétaire des budgets.
|
||||
- Les réservations sont libérées exactement une fois.
|
||||
- ncurses appartient exclusivement au thread principal.
|
||||
- Les estimations de ressources sont immuables.
|
||||
- Les buffers et files sont strictement bornés.
|
||||
|
||||
Viewer, coverage analysis, live localization and capture guidance remain future product areas.
|
||||
## Limites actuelles
|
||||
|
||||
The architecture requirement is already clear: visual consumers use validated immutable snapshots.
|
||||
They do not access mutable worker buffers, own scientific identity or become a second execution
|
||||
runtime.
|
||||
|
||||
Device-specific acquisition transports such as A6000 HDMI/USB or S21/mobile integration belong at the
|
||||
acquisition adapter boundary. Heavy reconstruction remains PC-side.
|
||||
|
||||
## Core invariants
|
||||
|
||||
- No Task callback executes without a valid active reservation.
|
||||
- Queue/runtime do not own resource policy.
|
||||
- The Resource Governor is the sole production resource authority.
|
||||
- ncurses remains owned by the main thread.
|
||||
- Scientific and operational identities remain distinct.
|
||||
- Task estimates and installed sequence contracts remain immutable for their defined lifetime.
|
||||
- Memory, buffers, queues, files, descriptors, processes, threads and internal participants are bounded.
|
||||
- Per-item atomicity does not imply cross-item serialization.
|
||||
- Owner-only publication does not imply serial preparation.
|
||||
- Validated and useful GPU backends are preferred when eligible and Governor-safe.
|
||||
- UMA GPU memory is charged exactly once against host RAM.
|
||||
- Swap, zram and scratch do not enlarge RAM admission.
|
||||
- Failure and cancellation clean operation-owned transient resources.
|
||||
- Atomic scientific publication never exposes partial results as complete.
|
||||
- FROZEN scientific decisions are not reopened by resource-policy evolution.
|
||||
|
||||
## Current limitations and future scope
|
||||
|
||||
The following remain outside the current implemented execution model:
|
||||
|
||||
- general dependency DAG scheduling;
|
||||
- multiple active Task callbacks / inter-Task worker pools;
|
||||
- general CPU/GPU/I/O worker-pool architecture;
|
||||
- multi-GPU scheduling;
|
||||
- global orphan-artifact reconciliation;
|
||||
- Visual Index compaction / larger-index evolution;
|
||||
- authoritative Task scratch consumers;
|
||||
- durable dense / mesh publication;
|
||||
- full Dense/MVS orchestration;
|
||||
- mesh refinement, texturing and final export workflow;
|
||||
- viewer;
|
||||
- offline coverage analysis;
|
||||
- suggested supplementary viewpoints;
|
||||
- live camera localization;
|
||||
- live coverage overlay;
|
||||
- A6000/S21 live acquisition integration;
|
||||
- capture guidance;
|
||||
- video ingestion and deterministic keyframe extraction.
|
||||
|
||||
Deferring inter-Task parallelism does not authorize accidental serialization inside the one active
|
||||
Task. Bounded internal concurrency remains the current mechanism for independent work under
|
||||
`SERIALISM_REQUIRES_PROOF`.
|
||||
|
||||
## Current summary
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
CURRENT_PRODUCTION_TASK_KINDS=16
|
||||
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
|
||||
RAW_BATCH_PATH=raw.develop.batch/1
|
||||
FEATURE_BATCH_PATH=features.extract.batch/1
|
||||
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
|
||||
SPARSE_SFM_CAPABILITY=C_G_PASS_FROZEN
|
||||
REAL_A6000_SPARSE_SFM_EXECUTED=NO
|
||||
REAL_A6000_DENSE_MVS_EXECUTED=NO
|
||||
|
||||
TUI=CURRENT_VALIDATED_OPERATIONAL
|
||||
VIEWER=FUTURE
|
||||
LIVE_CAPTURE_GUIDANCE=FUTURE
|
||||
```
|
||||
|
||||
Detailed contracts remain owned by the specialized architecture documents. This overview summarizes
|
||||
current repository state and must not replace those normative documents.
|
||||
- File à worker unique avec FIFO stable et bypass des seuls `WAIT` ressources.
|
||||
- Absence de DAG de dépendances.
|
||||
- Absence de priorités.
|
||||
- Absence de pools de workers multiples (CPU/GPU/IO).
|
||||
- La TUI legacy ne sélectionne pas encore explicitement ses ScanSets.
|
||||
- La réconciliation globale des assets/checkpoints orphelins n'est pas implémentée.
|
||||
- La compaction des segments Visual Index n'est pas implémentée.
|
||||
- Viewer et publication live non implémentés.
|
||||
|
|
|
|||
|
|
@ -1,86 +1,80 @@
|
|||
# Lardon3D Persistence and Project Database
|
||||
# Persistance et base de données Lardon3D
|
||||
|
||||
## Current authority
|
||||
## Vision
|
||||
|
||||
The current Project DB schema is **v25**.
|
||||
Lardon3D stocke les métadonnées de reconstruction dans Project DB SQLite,
|
||||
tandis que les données numériques massives restent dans des fichiers/binaires
|
||||
adaptés. Le schéma courant est v23 ; les sections v7 ci-dessous documentent la
|
||||
fondation historique sans prétendre être la tête de migration.
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
## Principes fondamentaux
|
||||
|
||||
v22 selected scientific execution foundation PASS/FROZEN
|
||||
v23 generic optical-context overlay IMPLEMENTED/VALIDATED/REVIEWED
|
||||
v24 raw.develop.batch/1 persistence IMPLEMENTED/VALIDATED
|
||||
v25 features.extract.batch/1 persistence IMPLEMENTED/VALIDATED
|
||||
### Séparation logique/binaire
|
||||
- État logique, relations, index → base persistante légère
|
||||
- Données numériques massives → fichiers/artefacts binaires adaptés
|
||||
|
||||
### Cycle de publication
|
||||
```
|
||||
lot calculé
|
||||
→ artefact temporaire
|
||||
→ validation
|
||||
→ publication atomique
|
||||
→ transaction de métadonnées
|
||||
→ état READY
|
||||
```
|
||||
|
||||
Project DB evolves additively. Older schema versions remain valid historical contracts when a
|
||||
section explicitly documents the state published by that version. They must not be rewritten as if
|
||||
they had always contained later overlays.
|
||||
### Règle de reprise
|
||||
Une reprise ne considère jamais un artefact partiellement publié comme valide.
|
||||
|
||||
The detailed schema, migration ledger and identity contracts are owned by
|
||||
[Project Database](project_database.md). This document owns the persistence model and publication,
|
||||
checkpoint, recovery and artifact-boundary rules.
|
||||
## Concepts de domaine
|
||||
|
||||
## Persistence model
|
||||
Les éléments suivants sont des concepts de domaine, PAS des tables SQL imposées :
|
||||
|
||||
Lardon3D keeps queryable logical state in SQLite Project DB while large numerical payloads remain in
|
||||
bounded external files or artifacts designed for their format.
|
||||
- project
|
||||
- scan_set
|
||||
- image
|
||||
- feature_set
|
||||
- visual_signature
|
||||
- candidate_pair
|
||||
- verified_pair
|
||||
- track
|
||||
- observation
|
||||
- camera
|
||||
- camera_body_profile
|
||||
- lens_profile
|
||||
- optical_configuration
|
||||
- optical_calibration_profile
|
||||
- pose
|
||||
- point3d
|
||||
- reconstruction_layer
|
||||
- measurement
|
||||
- document_source
|
||||
- geometric_constraint
|
||||
- artifact
|
||||
- checkpoint
|
||||
|
||||
The split is intentional:
|
||||
## Invariants
|
||||
|
||||
- identity, lifecycle state, relations, durable Task payloads and publication metadata -> SQLite;
|
||||
- large descriptors, match payloads, checkpoints and other numerical artifacts -> bounded files;
|
||||
- a file path is a storage locator after identity resolution, never scientific identity by itself.
|
||||
- Chaque publication est atomique
|
||||
- Les artefacts partiels ne sont jamais considérés comme valides
|
||||
- La reprise commence à la dernière frontière connue
|
||||
|
||||
Project DB does not serialize runtime-only objects such as mutexes, condition variables, callbacks,
|
||||
userdata pointers, worker threads, Governor reservations or live execution contracts.
|
||||
## Checkpoint durable de tâche v1
|
||||
|
||||
## Core invariants
|
||||
### État durable
|
||||
|
||||
- durable publication is explicit and ordered;
|
||||
- partial artifacts are never accepted as complete scientific outputs;
|
||||
- recovery begins from the last durable boundary the owning contract can prove;
|
||||
- SQLite state and external files are not falsely described as one distributed transaction;
|
||||
- immutable scientific identity is not inferred from path, basename, timestamp or operational IDs;
|
||||
- large collections are read and written through bounded interfaces;
|
||||
- retry behavior must converge only from identities already established by the owning contract;
|
||||
- Project DB schema migration never silently creates new scientific meaning for historical rows.
|
||||
Le modèle durable versionné contient uniquement l'identifiant stable, le nom,
|
||||
l'estimation immuable, l'état observé, l'état de reprise, la progression, le
|
||||
message, les horodatages et le compteur de séquences. Il ne contient aucun gros
|
||||
artefact numérique. Une future version pourra référencer des identifiants
|
||||
d'artefacts publiés et validés sans incorporer leur contenu.
|
||||
|
||||
## Logical and binary publication
|
||||
Les mutex, conditions, callbacks, userdata, workers, gouverneur, réservations et
|
||||
contrats d'exécution sont transitoires et ne sont jamais sérialisés.
|
||||
|
||||
The generic publication shape is:
|
||||
### Normalisation après arrêt de processus
|
||||
|
||||
```text
|
||||
bounded computation
|
||||
-> operation-owned temporary output
|
||||
-> format/content validation
|
||||
-> atomic file publication where required
|
||||
-> Project DB transaction
|
||||
-> READY / published durable state
|
||||
```
|
||||
|
||||
The exact ordering is owned by the subsystem. Some contracts publish an immutable file before the
|
||||
SQLite transaction and therefore explicitly admit an orphan-file window. Others publish metadata and
|
||||
files through a more specialized ordered protocol. No document may strengthen a subsystem guarantee
|
||||
beyond the actual implementation.
|
||||
|
||||
Failure cleanup may remove only resources owned by the current operation. Shared immutable assets are
|
||||
not deleted merely because a later metadata transaction fails.
|
||||
|
||||
## Durable Task checkpoint v1
|
||||
|
||||
### Durable state
|
||||
|
||||
The versioned Task checkpoint contains bounded logical Task state only. It includes the stable Task
|
||||
identity, name, immutable estimate, observed state, recovery state, progress, message, timestamps and
|
||||
sequence count. It does not embed large scientific artifacts.
|
||||
|
||||
Task kind and typed business payload are separate concerns. The generic checkpoint format v1 does not
|
||||
become a miniature Project DB and does not replace typed Project DB persistence.
|
||||
|
||||
### Process-stop normalization
|
||||
|
||||
| Observed state | Restored state |
|
||||
| État observé | État restauré |
|
||||
|---|---|
|
||||
| `TASK_PENDING` | `TASK_PENDING` |
|
||||
| `TASK_RUNNING` | `TASK_PENDING` |
|
||||
|
|
@ -89,355 +83,162 @@ become a miniature Project DB and does not replace typed Project DB persistence.
|
|||
| `TASK_FAILED` | `TASK_FAILED` |
|
||||
| `TASK_CANCELLED` | `TASK_CANCELLED` |
|
||||
|
||||
A sequence break is not a persistent Task state. If execution stopped while a sequence was active,
|
||||
recovery returns the Task to `TASK_PENDING`; its durable sequence count remains retained and a new
|
||||
Governor admission is required before execution resumes.
|
||||
|
||||
### Standalone checkpoint file
|
||||
|
||||
The v1 codec is bounded and field-encoded with explicit magic/version/size/checksum semantics. It does
|
||||
not serialize native structs or native padding.
|
||||
|
||||
Standalone publication uses a unique temporary file in the same directory, synchronizes the file,
|
||||
renames atomically and then synchronizes the parent directory.
|
||||
|
||||
The result distinguishes three important boundaries:
|
||||
|
||||
- before successful rename: failure leaves the previously published checkpoint unchanged;
|
||||
- after successful rename: the new visible checkpoint is published and is not presented as rolled back;
|
||||
- parent-directory sync failure after rename: result is `PUBLISHED_NOT_DURABLE`; the visible file is
|
||||
valid, but name persistence across crash/power loss is not guaranteed by Lardon3D.
|
||||
|
||||
`OK` means both content and directory-entry synchronization completed, subject to the guarantees of the
|
||||
filesystem and storage stack.
|
||||
|
||||
Persistent sizes are rejected before conversion if they exceed the local representable domain. The v1
|
||||
timestamp representation likewise requires values representable by the local `time_t` before runtime
|
||||
conversion.
|
||||
|
||||
## Project-owned checkpoint protocol
|
||||
|
||||
The project checkpoint protocol coordinates a Task file and SQLite metadata without claiming a true
|
||||
filesystem+SQLite transaction.
|
||||
|
||||
The current project-level ordering is:
|
||||
|
||||
```text
|
||||
capture bounded Task snapshot
|
||||
-> publish .chk.next
|
||||
-> record Task/checkpoint summary in SQLite
|
||||
-> promote .chk.next to canonical .chk under .chk.lock
|
||||
```
|
||||
|
||||
The canonical location is:
|
||||
|
||||
```text
|
||||
.lardon3d/checkpoints/<task_id>.chk
|
||||
```
|
||||
|
||||
The advisory `.chk.lock` exists only as process synchronization; it is not recovery data.
|
||||
|
||||
Recovery obtains the lock, reloads the relevant Project DB record because it may have changed while
|
||||
waiting, and then selects a codec/version-valid checkpoint whose stored summary matches the Project DB
|
||||
summary exactly for the fields Project DB owns.
|
||||
|
||||
A valid canonical `.chk` has priority. A valid matching `.chk.next` may be promoted when the canonical
|
||||
file does not match. Stale or corrupt `.next` files never override a valid canonical checkpoint.
|
||||
|
||||
A missing, corrupt, future-version or summary-mismatched checkpoint makes that Task non-recoverable; it
|
||||
does not make unrelated project state invalid.
|
||||
|
||||
## Project DB foundation
|
||||
|
||||
Project DB is SQLite with explicit schema versioning and sequential transactional migrations.
|
||||
|
||||
The retained configuration uses:
|
||||
|
||||
```text
|
||||
foreign_keys=ON
|
||||
journal_mode=DELETE
|
||||
synchronous=FULL
|
||||
busy_timeout=5000
|
||||
```
|
||||
|
||||
`DELETE` journal mode matches the current single-owner Project DB model and avoids persistent WAL/SHM
|
||||
files. The timeout bounds waiting on an external lock.
|
||||
|
||||
The project identity is duplicated intentionally across `project.ini` and the `project` table and must
|
||||
match. A divergence is an error, not an opportunity to invent a new identity.
|
||||
|
||||
Published catalog and scientific IDs use SQLite integer identities according to their owning schema.
|
||||
Where `AUTOINCREMENT` is part of the contract, a committed published identity is not later reused for a
|
||||
different object. An ID allocated only inside a rolled-back transaction is not a published identity.
|
||||
|
||||
## Historical Project DB v7 foundation
|
||||
|
||||
Project DB v7 is the historical persistent project/runtime foundation. It covers the durable project
|
||||
identity, Tasks, checkpoint references, generic artifacts, ScanSets, logical images, image assets,
|
||||
Feature Store metadata and the segmented Visual Index foundation.
|
||||
|
||||
Important v7-era persistence rules remain current unless a later contract explicitly supersedes them:
|
||||
|
||||
- Task summary plus checkpoint reference is a single SQLite transaction;
|
||||
- large checkpoint and scientific files remain external;
|
||||
- an artifact file is published and validated before Project DB marks it `READY`;
|
||||
- Project DB stores bounded metadata and references, not large descriptor/posting payloads;
|
||||
- `AUTOINCREMENT` is used where published catalog/scientific IDs must not be recycled after committed
|
||||
deletion;
|
||||
- the persistent import source path records durable execution intent, while imported scientific data
|
||||
ultimately depends on the managed immutable asset rather than the original external source path.
|
||||
|
||||
### Generic artifact orphan window
|
||||
|
||||
For generic file-first publication, a successful file publication followed by SQLite `BUSY` or another
|
||||
SQLite failure leaves a valid orphan file on disk while Project DB retains its previous truth.
|
||||
|
||||
That file is not silently deleted. Global orphan reconciliation is a separate maintenance capability.
|
||||
The absence of such reconciliation does not justify pretending the file and SQLite update were atomic.
|
||||
|
||||
## Import and managed source assets
|
||||
|
||||
`import.images` persists its source path and ScanSet identity and checkpoints after validated bounded
|
||||
work. Managed image assets are content-addressed by SHA-256 after a complete bounded copy/hash pass.
|
||||
|
||||
A concurrently existing asset is adopted only after the implementation verifies the expected content
|
||||
and size according to the owning contract. SQLite publication follows file publication.
|
||||
|
||||
`manifest.tsv` remains a historical/legacy projection. SQLite is the canonical logical commit for the
|
||||
persistent catalog. The `legacy_image_catalog_pending` marker means legacy data may remain outside the
|
||||
current catalog model; it does not claim that historical manifest rows were silently converted into
|
||||
fully proven catalog identities.
|
||||
|
||||
## Selective Task recovery
|
||||
|
||||
Project open performs bounded selective recovery using Project DB pages and the Task Kind Registry.
|
||||
|
||||
A recoverable Task must have:
|
||||
|
||||
- a valid Project DB Task record;
|
||||
- a supported Task kind and version;
|
||||
- a valid coherent checkpoint boundary required by that kind;
|
||||
- reconstructable typed business persistence where the kind requires it.
|
||||
|
||||
The recovery scan copies records outside the SQLite mutex before business reconstruction and enqueue.
|
||||
A full Queue window stops the scan without mutating the unvisited records.
|
||||
|
||||
Project-level schema, migration and project-identity errors are fatal to opening the project. Per-Task
|
||||
legacy, unknown-kind, unsupported-version, missing-checkpoint, invalid-checkpoint, unavailable-source or
|
||||
reconstruction errors are isolated to the affected Task where the owning contract permits.
|
||||
|
||||
## External scientific artifacts
|
||||
|
||||
### Feature Store
|
||||
|
||||
Feature files are immutable external artifacts with versioned bounded readers. Project DB stores their
|
||||
identity and publication metadata only after the external file is valid according to the Feature Store
|
||||
contract.
|
||||
|
||||
Feature descriptors are not duplicated into SQLite.
|
||||
|
||||
### Visual Index
|
||||
|
||||
Visual Index postings remain in bounded immutable external segments. Project DB stores index identity,
|
||||
segment metadata and memberships. Durable update Tasks retain the bounded cursor required to resume
|
||||
publication without rebuilding already accepted segments.
|
||||
|
||||
### Match and later scientific payloads
|
||||
|
||||
Match payloads and other large scientific representations follow the same architectural principle:
|
||||
SQLite owns durable identity, relations and bounded metadata; specialized external formats own large
|
||||
numerical payloads where the subsystem contract requires them.
|
||||
|
||||
## Project DB v16-v22 retained scientific foundation
|
||||
|
||||
The scientific and persistence contracts introduced through v16-v22 remain historical PASS/FROZEN
|
||||
foundations. Later schema versions are additive overlays and do not reinterpret those rows.
|
||||
|
||||
In particular:
|
||||
|
||||
- v16 publishes the immutable Sparse SfM persistence model;
|
||||
- v17 adds the durable typed Sparse SfM Task payload for Gate F;
|
||||
- v18 adds Phase H v1 incremental-reconstruction identity/persistence;
|
||||
- v19 adds Capture / Asset Provenance v1;
|
||||
- v20 adds durable acquisition-campaign Task persistence;
|
||||
- v21 adds Photo Quality Triage persistence;
|
||||
- v22 adds selected scientific execution, explicit Capture SOURCE-asset mapping and durable selected
|
||||
RAW development persistence.
|
||||
|
||||
Detailed tables, identities and migration invariants are defined in
|
||||
[Project Database](project_database.md).
|
||||
|
||||
## Project DB v23 - generic optical-context overlay
|
||||
|
||||
Project DB v23 is an additive optical-context overlay above the v22 scientific foundation.
|
||||
|
||||
It separates four identities:
|
||||
|
||||
- camera body profile;
|
||||
- lens profile;
|
||||
- optical configuration;
|
||||
- optical calibration profile.
|
||||
|
||||
The migration creates the optical relations empty. It does not inspect EXIF, path, basename, SHA-256,
|
||||
dimensions, device name or historical calibration to backfill identity.
|
||||
|
||||
Electronic metadata aliases, when present, use exact stored identity semantics. A manual lens without
|
||||
EXIF is a normal explicit profile and does not require a fabricated metadata alias.
|
||||
|
||||
Calibration selection is explicit and requires exact compatible optical configuration. No silent
|
||||
interpolation, substitution or inferred calibration identity is introduced by persistence.
|
||||
|
||||
Historical S21/A6000 copies migrated through the optical overlay without changing their existing
|
||||
scientific rows; empty optical tables remain an honest state until explicit data is supplied.
|
||||
|
||||
## Project DB v24 - RAW batch persistence
|
||||
|
||||
Project DB v24 adds only the typed durable relation required by `raw.develop.batch/1`:
|
||||
|
||||
```text
|
||||
raw_development_batch_tasks(task_id, selected_execution_id)
|
||||
```
|
||||
|
||||
The migration is additive and DDL-only. It does not create Capture, Asset, Image or selected-execution
|
||||
identity and does not rewrite historical `raw.develop/1` Tasks.
|
||||
|
||||
The selected execution remains the durable scientific ordering authority. The batch Task may prepare
|
||||
independent RAW items concurrently under Governor admission, but after all participants join, the owner
|
||||
publishes selected representations in deterministic selected-item order.
|
||||
|
||||
The durable ordering remains:
|
||||
|
||||
```text
|
||||
prepare bounded independent RAW items
|
||||
-> join participants
|
||||
-> owner publishes exact selected item
|
||||
-> selected item/cursor commit
|
||||
-> generic Task progress/checkpoint
|
||||
```
|
||||
|
||||
A crash may therefore leave generic Task progress behind already durable selected scientific state; it
|
||||
must never move generic progress ahead of unpublished selected state. Recovery resumes from durable
|
||||
selected-execution identity/cursor and exact already-published representations rather than guessing
|
||||
from paths or files.
|
||||
|
||||
The retained real A6000 run completed this RAW-batch path for all 689 selected RAW representations.
|
||||
|
||||
## Project DB v25 - Feature batch persistence
|
||||
|
||||
Project DB v25 is the current schema head. It adds only the typed durable relation required by
|
||||
`features.extract.batch/1` through `feature_extract_batch_tasks`.
|
||||
|
||||
The v25 row binds one durable Feature-batch Task to:
|
||||
|
||||
- one immutable selected execution;
|
||||
- the monotone selected-item prefix/cursor;
|
||||
- the exact ORB extractor kind/version/parameters/fingerprint domain required by the Task contract.
|
||||
|
||||
The migration is additive and DDL-only. It creates no Feature Set, converts no historical
|
||||
`features.extract/1` Task and infers no Image or Feature Set identity.
|
||||
|
||||
Selected images may be prepared concurrently without SQLite access. After participants join, the owner
|
||||
publishes Feature Sets in selected order and advances the durable Feature cursor only when the exact
|
||||
READY Feature Set is already durable.
|
||||
|
||||
A crash may leave generic Task checkpoint/progress or the Feature cursor behind an immutable Feature
|
||||
Set that was already published. Recovery must revalidate and reuse that exact result; it must not infer
|
||||
a replacement identity.
|
||||
|
||||
The retained real A6000 proof completed the v25 path with 689 Feature Sets and then continued through
|
||||
Visual Index, 38,420 Candidate Pairs, 38,420 Match Results, Geometric Verification and Tracks without
|
||||
replaying acquisition, RAW or Feature work.
|
||||
|
||||
This persistence result does **not** imply that real Sparse SfM or Dense/MVS was executed. Those counts
|
||||
remain zero in `REAL_A6000_PRE_SFM=PASS/FROZEN`.
|
||||
|
||||
## Capture / Asset / Image identity boundary
|
||||
|
||||
Persistence must preserve these distinctions:
|
||||
|
||||
```text
|
||||
Capture != file
|
||||
Capture != Asset
|
||||
Capture != image_id
|
||||
Capture != SHA-256
|
||||
Capture != path
|
||||
Capture != basename
|
||||
Capture != Task ID
|
||||
Capture != campaign group ID
|
||||
```
|
||||
|
||||
`asset_id` identifies a managed immutable asset record. SHA-256 identifies immutable bytes according to
|
||||
the asset contract. `image_id` identifies a scientific image representation. `capture_id` identifies a
|
||||
physical acquisition representation in Project DB. Task and campaign-group IDs remain operational
|
||||
identities.
|
||||
|
||||
Retry and recovery may use an explicit persisted mapping between these domains only where a canonical
|
||||
contract defines it. They may not reconstruct missing identity from coincidental equality or metadata.
|
||||
|
||||
## Acquisition-campaign crash/restart ordering
|
||||
|
||||
For durable campaign execution, the important persistence boundary is conceptually:
|
||||
|
||||
```text
|
||||
S3-E returns capture_id
|
||||
-> persist group_id -> capture_id mapping and campaign cursor
|
||||
-> advance generic Task progress/checkpoint
|
||||
-> next group may execute
|
||||
```
|
||||
|
||||
The retained pre-return S3-E crash window remains intentional. If a Capture is created internally but
|
||||
the process dies before the caller receives and durably retains its `capture_id`, campaign persistence
|
||||
does not guess that identity from path, digest, basename, timestamp, metadata or `image_id`.
|
||||
|
||||
## Concurrency and ownership
|
||||
|
||||
A Project DB connection is serialized by its internal mutex. Each public compound operation owns its
|
||||
entire transaction; a public transaction is not left open across calls.
|
||||
|
||||
External artifact I/O is not performed while holding the Project DB mutex where the owning contract
|
||||
separates those operations. SQLite-owned strings and records are copied into caller-owned bounded
|
||||
storage before returning.
|
||||
|
||||
Closing Project DB concurrently with an active Project DB call is forbidden by owner lifetime rules.
|
||||
Application project/session teardown must first destroy and join the Queue so Task callbacks and leases
|
||||
are finished, then close Project DB.
|
||||
|
||||
## Schema migration discipline
|
||||
|
||||
Project DB migrations are sequential, transactional and additive unless an explicit future human ticket
|
||||
authorizes a different migration.
|
||||
|
||||
Current known sequence:
|
||||
|
||||
```text
|
||||
v1 -> ... -> v22
|
||||
v22 -> v23 generic optical-context overlay
|
||||
v23 -> v24 RAW batch Task persistence
|
||||
v24 -> v25 Feature batch Task persistence
|
||||
```
|
||||
|
||||
A migration failure rolls back both its new schema objects and its schema-version publication marker.
|
||||
A retry therefore starts from the previous complete known schema.
|
||||
|
||||
Future schema versions beyond v25 are rejected by the current implementation and require explicit human
|
||||
authorization before code or documentation may treat them as current.
|
||||
|
||||
## Current status
|
||||
|
||||
```text
|
||||
PERSISTENCE_DOCUMENT=CURRENT
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
|
||||
TASK_CHECKPOINT_V1 IMPLEMENTED
|
||||
PROJECT_CHECKPOINT_PROTOCOL IMPLEMENTED/VALIDATED
|
||||
PROJECT_DB_V7_FOUNDATION IMPLEMENTED
|
||||
PROJECT_DB_V16_TO_V22_FOUNDATION PASS/FROZEN
|
||||
PROJECT_DB_V23_OPTICAL_OVERLAY IMPLEMENTED/VALIDATED/REVIEWED
|
||||
PROJECT_DB_V24_RAW_BATCH IMPLEMENTED/VALIDATED
|
||||
PROJECT_DB_V25_FEATURE_BATCH IMPLEMENTED/VALIDATED
|
||||
REAL_A6000_PRE_SFM PASS/FROZEN
|
||||
```
|
||||
|
||||
Current intentionally unfinished persistence-adjacent work includes global orphan-file reconciliation,
|
||||
asset scrub/reconciliation and a general dependency/DAG recovery model. Those are separate future
|
||||
capabilities; they do not change the current v25 schema authority.
|
||||
Une rupture de séquence n'est pas un état : elle est observée comme
|
||||
`TASK_RUNNING`. Son `sequence_count` est durable, mais la reprise revient à
|
||||
`TASK_PENDING` et exige une nouvelle admission.
|
||||
|
||||
### Stockage minimal
|
||||
|
||||
Le codec v1 est indépendant de la future Project Database. Le fichier est de
|
||||
taille fixe et bornée, encodé champ par champ, avec magie, version, taille et
|
||||
checksum de payload.
|
||||
La publication écrit un fichier temporaire unique dans le même répertoire,
|
||||
effectue `fsync`, renomme atomiquement puis synchronise le répertoire parent.
|
||||
La lecture distingue absence, corruption, version inconnue et erreur d'I/O.
|
||||
|
||||
La sauvegarde distingue trois frontières :
|
||||
|
||||
- avant `rename`, toute erreur retourne `IO_ERROR`, supprime le temporaire et
|
||||
laisse l'ancien checkpoint publié inchangé ;
|
||||
- après un `rename` réussi, le nouveau checkpoint est publié et visible et
|
||||
n'est jamais présenté comme rollbackable ;
|
||||
- si le `fsync` du répertoire échoue après ce `rename`, le résultat est
|
||||
`PUBLISHED_NOT_DURABLE` : le fichier visible est valide, mais sa présence sous
|
||||
ce nom après un crash ou une coupure n'est pas garantie. `OK` garantit que le
|
||||
contenu et l'entrée de répertoire ont tous deux été synchronisés avec succès,
|
||||
sous réserve des garanties fournies par le système de fichiers et le stockage.
|
||||
|
||||
Les tailles persistantes sont refusées avant conversion lorsqu'elles dépassent
|
||||
`SIZE_MAX`. Les secondes sont des entiers non signés v1 : les timestamps
|
||||
négatifs ne sont pas sérialisables et une valeur lue doit être représentable
|
||||
par le `time_t` local avant conversion. Le format reste donc lisible entre
|
||||
plateformes uniquement pour les valeurs communes à leurs domaines `size_t` et
|
||||
`time_t`.
|
||||
|
||||
## Project Database v7 — fondation historique
|
||||
|
||||
SQLite contient l'état logique interrogable et les références aux fichiers ;
|
||||
les checkpoints et artefacts volumineux restent externes. L'enregistrement du
|
||||
résumé de tâche et de sa référence checkpoint est une transaction unique. Un
|
||||
artefact est d'abord publié et vérifié comme fichier régulier, puis seulement
|
||||
marqué `READY` en DB. Le chemin inverse est interdit.
|
||||
|
||||
### Protocole checkpoint projet
|
||||
|
||||
Le protocole réel n'est pas une transaction distribuée :
|
||||
|
||||
1. capture locale du snapshot sous le mutex de tâche puis déverrouillage ;
|
||||
2. publication atomique du fichier sous
|
||||
`.lardon3d/checkpoints/<task_id>.chk` ;
|
||||
3. transaction SQLite sur `tasks` et `checkpoints` avec chemin relatif.
|
||||
|
||||
Une erreur avant publication ne modifie pas la DB. `PUBLISHED_NOT_DURABLE` est
|
||||
conservé comme tel en DB. Si la publication réussit puis que SQLite retourne
|
||||
`BUSY` ou une erreur, le fichier valide reste sur disque, la DB conserve son
|
||||
ancienne vérité et le nouveau fichier est un orphelin à réconcilier plus tard.
|
||||
Il n'est pas supprimé et aucune atomicité FS+SQLite n'est revendiquée.
|
||||
|
||||
L'inventaire distingue checkpoint récupérable durable, récupérable mais publié
|
||||
non durable, absent, invalide, version inconnue et erreur d'I/O. Aucune réparation
|
||||
ou suppression silencieuse n'est effectuée.
|
||||
|
||||
Le format checkpoint reste en version 1 et ne contient pas de `task_kind`. Le
|
||||
schéma SQLite v4 conserve `task_kind` et `task_kind_version` dans le résumé
|
||||
logique interrogable. La migration v1→v2 laisse ces deux colonnes à `NULL` : une
|
||||
tâche legacy reste inspectable mais ne peut pas être reconstruite ou resoumise.
|
||||
Un kind inconnu ou une version non supportée est diagnostiqué sans exécuter de
|
||||
code.
|
||||
|
||||
## Statut
|
||||
|
||||
**IMPLEMENTED** — modèle durable, codec v1, lecture validée, publication
|
||||
atomique et restauration sûre d'une tâche isolée.
|
||||
|
||||
**IMPLEMENTED** — Project Database v7 pour identité, tâches typées, ScanSets,
|
||||
images logiques, Feature Sets/assets SHA-256, Visual Index segmenté,
|
||||
checkpoints et artefacts génériques.
|
||||
|
||||
**IMPLEMENTED** — registry statique bornée et reconstruction explicite avec
|
||||
ownership du userdata.
|
||||
|
||||
**IMPLEMENTED** — API projet de sauvegarde fichier+DB et inventaire validé au
|
||||
redémarrage.
|
||||
|
||||
**IMPLEMENTED** — `import.images` persiste son chemin source absolu et son
|
||||
`scanset_id`, puis publie un checkpoint après chaque lot validé. Le catalogue
|
||||
SQLite rend le rejeu idempotent à la granularité du contenu dans un ScanSet.
|
||||
|
||||
Le chemin source absolu est l'intention durable v1 : il doit rester accessible
|
||||
après redémarrage et un projet déplacé ne rend pas une source externe portable.
|
||||
Une source absente ou devenue non-répertoire fait échouer proprement la
|
||||
reconstruction. Après import terminé, l'image dépend de l'asset géré, plus de la
|
||||
source. Le SHA-256 est calculé pendant la copie avec un tampon fixe de 64 Kio.
|
||||
L'asset est publié sans écrasement sous
|
||||
`assets/images/<prefix>/<sha256>`, puis seulement enregistré `READY` dans une
|
||||
transaction SQLite. Un asset concurrent déjà présent n'est adopté qu'après
|
||||
rehash complet et vérification de taille. Si SQLite échoue après publication,
|
||||
le fichier reste orphelin pour une future réconciliation ; aucune transaction
|
||||
FS+SQLite n'est revendiquée.
|
||||
|
||||
Les identités publiées `scanset_id`, `image_id` et `asset_id` utilisent les
|
||||
séquences SQLite `AUTOINCREMENT` : une valeur issue d'une transaction validée
|
||||
n'est jamais réattribuée à un autre objet, même après suppression de la ligne.
|
||||
Une valeur réservée par une transaction annulée n'est pas une identité publiée.
|
||||
|
||||
`manifest.tsv` reste supporté par l'ancien chemin d'import/catalogue. Le chemin
|
||||
persistant entretient une projection best-effort par hardlinks pour la TUI,
|
||||
mais SQLite est le commit logique canonique : la correction de la reprise ne
|
||||
dépend plus de l'ordre de `readdir()` ni du nom de fichier.
|
||||
|
||||
La migration v3 vers v4 ne transforme pas les lignes du manifeste historique
|
||||
en images cataloguées : elles ne contiennent pas toutes les preuves exigées par
|
||||
le modèle v4. Le marqueur durable `legacy_image_catalog_pending` rend cette
|
||||
situation visible. Une tâche récupérable peut repeupler le catalogue par rejeu
|
||||
si sa source existe encore ; sinon les fichiers et le manifeste restent une
|
||||
projection legacy, explicitement non cataloguée. Une tâche v3 déjà terminée
|
||||
n'est pas rejouée automatiquement.
|
||||
|
||||
**IMPLEMENTED** — reprise automatique sélective à l'ouverture : pagination
|
||||
bornée, validation checkpoint/kind, reconstruction production et enqueue sans
|
||||
claim persistant supplémentaire.
|
||||
|
||||
Les records sont parcourus par task ID croissant. Un checkpoint
|
||||
`PUBLISHED_NOT_DURABLE` présent, valide et cohérent peut être repris ; le résumé
|
||||
conserve cet avertissement jusqu'au prochain checkpoint durable. Une tâche
|
||||
terminale n'appartient pas à la requête de reprise.
|
||||
|
||||
**NOT_YET_WIRED** — réconciliation des fichiers orphelins et retry piloté par
|
||||
l'utilisateur pour les sources indisponibles. Les checkpoints existants sont
|
||||
kind-owned aux frontières métier ; aucun timer générique ne peut les remplacer.
|
||||
|
||||
**NOT_YET_WIRED** — migration de la vue TUI en mémoire vers la pagination
|
||||
SQLite, scrub des assets et réconciliation globale des orphelins.
|
||||
|
||||
**IMPLEMENTED** — Feature Store externe versionné, immutable, borné et relié
|
||||
transactionnellement à ses métadonnées SQLite après publication.
|
||||
|
||||
**IMPLEMENTED** — Visual Index externe segmenté, memberships transactionnels
|
||||
et tâche `visual_index.update` récupérable.
|
||||
|
||||
**IMPLEMENTED** — migrations additives et séquentielles jusqu'à Project DB
|
||||
v23. Les versions v16 à v22 restent l'histoire scientifique et de persistance
|
||||
gelée ; v23 ajoute uniquement l'overlay optique générique.
|
||||
|
||||
Les neuf relations v23 séparent profils de boîtier et alias, profils d'objectif
|
||||
et alias, configurations optiques, affectations de configuration aux groupes
|
||||
de campagne et aux Captures, profils de calibration et sélection explicite par
|
||||
Capture. Une configuration référence exactement un boîtier et un objectif ; un
|
||||
objectif manuel sans EXIF est normal. La compatibilité d'une
|
||||
calibration est exacte sur la configuration optique et ses dimensions/champs
|
||||
scientifiques. Aucun profil S21, A6000 ou Meike n'est inséré ou déduit par la
|
||||
migration : les tables nouvelles restent vides tant qu'un caller ne fournit
|
||||
pas explicitement les données.
|
||||
|
||||
La migration v22→v23 est une transaction additive. Elle ne réinterprète ni les
|
||||
Captures, ni les Images, ni les résultats scientifiques historiques. Une copie
|
||||
S21 et une copie A6000 ont atteint v23 avec intégrité et clés étrangères
|
||||
valides, comptes scientifiques inchangés et tables optiques vides. Les détails
|
||||
normatifs sont dans [Project Database](project_database.md).
|
||||
|
||||
**NOT_YET_WIRED** — reprise ordonnée par dépendances/DAG et réconciliation
|
||||
globale des artefacts orphelins.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,498 +1,158 @@
|
|||
# Lardon3D Resource-Aware Pipeline
|
||||
# Pipeline Feature + Matcher sensible aux ressources
|
||||
|
||||
## Status
|
||||
## Contrat portable
|
||||
|
||||
This document describes the current production resource model for the selected pre-SfM pipeline and
|
||||
its resource-sensitive execution paths.
|
||||
Une unité lourde ne démarre qu'avec une réservation active. Elle termine son
|
||||
petit travail courant sans être tuée sur une mesure instantanée, publie le
|
||||
résultat atomiquement, checkpoint, libère ses buffers, puis repasse par le
|
||||
Governor avant la séquence suivante. Les files restent bornées et le swap n'est
|
||||
jamais ajouté au budget de travail.
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
CURRENT_PRODUCTION_TASK_KINDS=16
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
Le mode normal est interactif : il réserve de la RAM et des threads logiques au
|
||||
desktop. Les signaux d'admission combinent `MemAvailable`, charge CPU, PSI CPU,
|
||||
PSI mémoire, PSI I/O et deltas `pswpin`/`pswpout`. Un seuil dépassé empêche une
|
||||
nouvelle admission ; il ne rompt pas une réservation saine déjà active.
|
||||
|
||||
The scientific contracts of Feature Store, Candidate Pair, Matcher, Geometric Verification and Track
|
||||
Builder remain owned by their specialized documents. This document owns the operational view:
|
||||
admission, bounded work, internal concurrency, publication boundaries, pressure response and
|
||||
reference-host measurements.
|
||||
Le Governor maintient trois zones. GREEN emploie le lot adapté normal. La zone
|
||||
de prudence RAM entre 3 et 4 Gio, un PSI au seuil ou un premier intervalle avec
|
||||
swap actif produit YELLOW et interdit toute croissance. Deux observations de
|
||||
pression consécutives, ou `MemAvailable` sous la réserve dure de 3 Gio,
|
||||
produisent RED et suspendent toute admission. Le premier snapshot swap établit
|
||||
seulement la baseline.
|
||||
|
||||
## Canonical execution rule
|
||||
La récupération possède deux phases distinctes : trois observations saines
|
||||
font `RED → YELLOW`, puis trois nouvelles observations saines font
|
||||
`YELLOW → GREEN`. Après RED, le plafond de lot reste 1. En GREEN, trois
|
||||
observations saines sont nécessaires à chaque palier `1 → 2 → 4 → 8`. Une
|
||||
nouvelle pression réinitialise cette progression. Cette mémoire est
|
||||
process-local, bornée et protégée par le mutex du Governor.
|
||||
|
||||
A heavy production unit starts only while an active Resource Governor reservation authorizes it.
|
||||
Gate G gèle le rafraîchissement initial : lorsqu'il existe du travail PENDING
|
||||
en `WAIT` de ressources, le worker unique de la Task Queue dort au plus 500 ms
|
||||
avant de rescanner la file et de recapturer les ressources. Un signal explicite
|
||||
le réveille plus tôt. Cette cadence ne remplace pas les 50 ms existantes d'une
|
||||
tâche déjà active qui attend sa réadmission à une frontière de séquence.
|
||||
|
||||
The normal sequence is:
|
||||
**Gate G — PASS / FROZEN.** Cette réévaluation bornée, la
|
||||
fraîcheur des snapshots et l'identité GPU sélectionnée sont raccordées aux
|
||||
chemins de production existants et leur validation finale est terminée.
|
||||
|
||||
```text
|
||||
immutable Task payload
|
||||
-> Governor admission
|
||||
-> bounded preparation / computation
|
||||
-> deterministic owner publication
|
||||
-> durable cursor / checkpoint
|
||||
-> release sequence-local buffers and reservation
|
||||
-> next admission
|
||||
```
|
||||
|
||||
The Queue keeps one active Task callback. That does not require a Task callback itself to perform all
|
||||
independent work serially. Where a Task owns multiple independent units, bounded internal participants
|
||||
may prepare those units concurrently when the scientific and persistence contracts permit it.
|
||||
|
||||
Canonical policy:
|
||||
|
||||
```text
|
||||
MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF
|
||||
```
|
||||
|
||||
Per-item atomicity does not imply cross-item serialization. Owner-only or ordered durable publication
|
||||
does not imply serial preparation.
|
||||
|
||||
A long-running CPU1 or batch1 path is acceptable only when a concrete dependency, measured scaling
|
||||
knee, RAM limit, I/O limit, validated GPU path or another documented operational constraint proves
|
||||
that more concurrency would not be safely useful.
|
||||
|
||||
## Host reserve and pressure model
|
||||
|
||||
Lardon3D preserves the interactive host reserve first, then gives the active workload the safe and
|
||||
useful remaining capacity.
|
||||
|
||||
On the current validation host, the normal observed outcome is approximately:
|
||||
|
||||
```text
|
||||
16 logical CPUs total
|
||||
4 logical CPUs reserved for interactive host use
|
||||
12 logical CPUs available to the compute pool
|
||||
~3 GiB MemAvailable preserved as the hard RAM reserve
|
||||
Radeon 780M UMA available to validated and useful GPU backends
|
||||
```
|
||||
|
||||
These values are evidence for the current host, not portable product constants.
|
||||
|
||||
The Governor may use `MemAvailable`, CPU pressure, memory PSI, I/O PSI, active `pswpin` / `pswpout`
|
||||
deltas, selected-GPU state, and Task-declared fixed, per-participant and transient costs.
|
||||
|
||||
Swap, zram and external scratch never enlarge admitted RAM. UMA GPU allocations are charged exactly
|
||||
once against host RAM.
|
||||
|
||||
Pressure may reduce admission. When pressure clears, safe and useful capacity must be re-admitted
|
||||
rather than leaving the process permanently throttled.
|
||||
|
||||
## Queue and sequence boundaries
|
||||
|
||||
The Task Queue remains bounded and has one active callback.
|
||||
|
||||
When pending work exists but all candidates receive `WAIT`, the Queue worker performs the validated
|
||||
bounded wait and retries normal admission with a fresh snapshot. A running sequential Task may also
|
||||
cross `sequence_break`, which releases the current reservation and requires fresh admission for the
|
||||
next sequence.
|
||||
|
||||
The current architecture separates:
|
||||
|
||||
```text
|
||||
cross-Task dispatch -> one active Queue callback
|
||||
inside-Task independent work -> bounded participants when justified
|
||||
durable publication -> owner-only where required
|
||||
```
|
||||
|
||||
No second scheduler, uncontrolled worker pool, detached-thread system or parallel persistence
|
||||
subsystem is introduced.
|
||||
|
||||
## Current Task inventory
|
||||
|
||||
The production registry contains 16 Task kinds.
|
||||
|
||||
The two additive kinds beyond the historical fourteen-kind maintenance inventory are:
|
||||
|
||||
```text
|
||||
raw.develop.batch/1
|
||||
features.extract.batch/1
|
||||
```
|
||||
|
||||
Historical documents that correctly recorded fourteen kinds at their checkpoint remain historical
|
||||
evidence. Fourteen is not the current production count.
|
||||
Les snapshots emploient `CLOCK_MONOTONIC` et leur âge maximal est 1000 ms. Une
|
||||
capture complète impossible est une erreur opérationnelle, tandis qu'une PSI
|
||||
ou télémétrie swap optionnelle absente reste inconnue. Compute Governor v2
|
||||
observe maintenant le RSS/HWM courant dans un buffer borné, uniquement comme
|
||||
diagnostic du processus : il ne le confond ni avec la réservation Task ni avec
|
||||
un coût attribuable. Le modèle cible un hôte Linux natif non contraint ; cgroups,
|
||||
limites systemd/RLIMIT, multi-GPU, historique/monitoring RSS long terme,
|
||||
redimensionnement d'admission depuis le RSS et consommation Task du scratch
|
||||
restent différés. Le contrôleur SSD optionnel gère le cycle de vie physique ;
|
||||
son état est enregistré auprès du Governor, qui est l'unique orchestrateur des
|
||||
leases scratch de production. Aucun des quatorze kinds actuels ne les consomme,
|
||||
et ni le registre, ni les leases, ni le swap ne créent un budget RAM.
|
||||
|
||||
## Feature Extraction
|
||||
|
||||
### Scientific atomicity
|
||||
ORB est déjà une tâche durable par image : source validée, extraction,
|
||||
publication Feature Store, métadonnées DB, checkpoint terminal et libération du
|
||||
buffer. Le batch vaut donc une image et la granularité de reprise est une image.
|
||||
Le worker unique et la file bornée fournissent la backpressure actuelle.
|
||||
|
||||
A Feature Set remains an immutable per-image scientific result.
|
||||
|
||||
The historical production path remains valid:
|
||||
|
||||
```text
|
||||
features.extract/1
|
||||
one image
|
||||
-> feature preparation
|
||||
-> Feature File publication
|
||||
-> Project DB metadata
|
||||
-> terminal Task checkpoint
|
||||
```
|
||||
|
||||
That path remains important for legacy Tasks and exact restart compatibility.
|
||||
|
||||
### Current selected-execution batch path
|
||||
|
||||
Project DB v25 adds the current operational path:
|
||||
|
||||
```text
|
||||
features.extract.batch/1
|
||||
```
|
||||
|
||||
The durable owner is bound to one immutable selected execution, the exact ORB domain and a monotone
|
||||
`next_item_index`. The selected item order remains authoritative.
|
||||
|
||||
The batch path is:
|
||||
|
||||
```text
|
||||
one admitted owner Task
|
||||
-> bounded independent selected-image participants
|
||||
-> each participant prepares one per-image Feature result without SQLite access
|
||||
-> join all admitted participants
|
||||
-> owner validates / reuses the exact READY Feature Set
|
||||
-> owner publishes in selected-item order
|
||||
-> owner advances the durable Feature cursor
|
||||
-> generic Task checkpoint follows
|
||||
```
|
||||
|
||||
A crash may leave the generic checkpoint or Feature-batch cursor behind an already immutable Feature
|
||||
Set. Restart revalidates and reuses that exact result; it does not infer a new identity.
|
||||
|
||||
Per-image Feature atomicity is therefore preserved while cross-image preparation may be concurrent.
|
||||
|
||||
### Feature CPU control
|
||||
|
||||
OpenCV thread count is process-wide state, so it must remain controlled.
|
||||
|
||||
The runtime establishes the validated baseline from the available compute pool before Queue
|
||||
execution. The active heavy callback temporarily applies the admitted count required by its current
|
||||
contract and restores the baseline on every exit path.
|
||||
|
||||
The Queue still has one active callback, so unrelated Tasks do not race process-wide OpenCV thread
|
||||
configuration.
|
||||
|
||||
For `features.extract.batch/1`, cross-image participants are the concurrency mechanism. Additional
|
||||
internal OpenCV threads inside every participant are not multiplied blindly.
|
||||
|
||||
The current coupled Feature-batch admission exists because increasing CPU while the admitted item
|
||||
window remains one cannot exercise additional independent images. CPU and batch may therefore move
|
||||
together for this Task where the validated rung contract requires it.
|
||||
|
||||
This is an explicit operational exception, not a universal Governor rule.
|
||||
|
||||
## RAW selected-execution batch
|
||||
|
||||
Project DB v24 provides `raw.develop.batch/1`.
|
||||
|
||||
Its pattern is analogous at the execution boundary:
|
||||
|
||||
```text
|
||||
one admitted owner Task
|
||||
-> bounded independent RAW participants
|
||||
-> join
|
||||
-> deterministic owner-only publication by selected_item_index
|
||||
-> durable selected-execution cursor advancement
|
||||
```
|
||||
|
||||
Per-Capture RAW publication remains atomic. Cross-Capture preparation need not be serial.
|
||||
|
||||
The retained real A6000 proof completed all 689 selected RAW representations through this path.
|
||||
|
||||
## Visual Index
|
||||
|
||||
Visual Index remains a bounded CPU path.
|
||||
|
||||
Its scientific identity and segment publication are unchanged. Internal parallelism may be used only
|
||||
within its validated contract; no GPU path is promoted merely to make the accelerator busy.
|
||||
|
||||
The current GPU audit rejected Visual Index as a useful production GPU candidate. That rejection is a
|
||||
measured backend decision, not permission to leave useful CPU capacity idle.
|
||||
|
||||
## Candidate Pair generation
|
||||
|
||||
Candidate Pair generation processes bounded Visual Index input and publishes deterministic canonical
|
||||
pairs.
|
||||
|
||||
Current resource behavior distinguishes per-pair scientific identity from cross-item execution.
|
||||
|
||||
The validated Candidate path may use coupled CPU/batch rungs because additional CPU cannot exercise
|
||||
additional independent source work while the admitted item window remains one.
|
||||
|
||||
The current operational model therefore permits:
|
||||
|
||||
```text
|
||||
bounded source/item window
|
||||
+ bounded CPU participants
|
||||
-> deterministic Candidate preparation
|
||||
-> owner publication
|
||||
```
|
||||
|
||||
The exact current memory model and batch limits are owned by [`candidate_pair.md`](candidate_pair.md).
|
||||
A historical CPU1/batch1 descriptor or older window estimate is not a permanent product ceiling.
|
||||
Le démarrage configure une baseline OpenCV issue du compute-pool réellement
|
||||
disponible avant la création de Queue. L'unique callback lourd applique ensuite
|
||||
temporairement le compte CPU immuable admis pour sa séquence, dans
|
||||
`1..compute-pool`, et restaure la baseline sur toute
|
||||
sortie, y compris après une mutation suivie d'un échec de vérification. La tâche
|
||||
réserve donc le nombre réellement appliqué au lieu d'annoncer artificiellement
|
||||
un thread pendant qu'une primitive interne en utilise davantage. Une mutation
|
||||
process-wide concurrente par plusieurs workers n'est pas supportée ; Queue
|
||||
conserve un seul callback actif.
|
||||
|
||||
## Matcher
|
||||
|
||||
`matcher.run` v1 is durable. Its scientific atomic unit is one Candidate Pair.
|
||||
`matcher.run` v1 est une tâche durable. Son unité atomique est une Candidate
|
||||
Pair et son lot vaut 1, 2, 4 ou 8 paires. La tâche page la DB par
|
||||
`candidate_pair_id`, sans supposer des IDs continus, et ne conserve jamais la
|
||||
liste entière. Chaque paire publie immédiatement son Match Result avant que le
|
||||
curseur ne soit avancé en mémoire.
|
||||
|
||||
The normal Task pages Project DB by `candidate_pair_id`; it does not assume contiguous IDs and does
|
||||
not retain the whole Candidate Pair set in memory.
|
||||
Project DB v10 porte le Match Result publié. La migration transactionnelle
|
||||
v10→v11 ajoute uniquement `matcher_tasks`, qui porte la configuration et ce
|
||||
curseur durable.
|
||||
|
||||
The durable Matcher sequence is:
|
||||
|
||||
```text
|
||||
bounded Candidate Pair page
|
||||
-> match one or more admitted pairs
|
||||
-> publish each exact Match Result
|
||||
-> advance durable cursor
|
||||
-> checkpoint
|
||||
-> sequence_break
|
||||
```
|
||||
|
||||
The current adaptive batch rungs are:
|
||||
|
||||
```text
|
||||
1 -> 2 -> 4 -> 8
|
||||
```
|
||||
|
||||
A published Match Result may precede its generic checkpoint after a crash. Restart finds and reuses
|
||||
the exact durable Match Result rather than recomputing or inventing identity.
|
||||
|
||||
### ORB Vulkan
|
||||
|
||||
The validated ORB Matcher Vulkan backend is GPU-first when eligible.
|
||||
|
||||
The Radeon 780M is UMA, so backend memory is host RAM and is charged once.
|
||||
|
||||
The current production Vulkan contract retains:
|
||||
|
||||
```text
|
||||
normal useful inflight depth = 1
|
||||
validated private safety capacity = 2
|
||||
helpers = 0
|
||||
```
|
||||
|
||||
Depth 2 was measured and rejected as the normal useful setting because its improvement remained below
|
||||
the accepted deadband. It remains a validated private capacity, not a production default.
|
||||
|
||||
The backend preserves canonical Match Result identity and exact CPU fallback. An ineligible pair or
|
||||
backend failure is recomputed completely on CPU; no partial GPU result is published.
|
||||
|
||||
SIFT and RootSIFT remain CPU OpenCV L2 paths. Their Vulkan feasibility work did not reach production
|
||||
eligibility and therefore receives no authoritative production GPU reservation.
|
||||
Après chaque lot, la tâche persiste le curseur, checkpoint, puis appelle
|
||||
`lardon3d_task_sequence_break()`. Pause et annulation sont vérifiées avant
|
||||
chaque paire et entre les lots. Un crash après publication mais avant le
|
||||
checkpoint revoit la paire : le Matcher réutilise alors le Match Result et ne
|
||||
recalcule pas les descripteurs.
|
||||
|
||||
## Geometric Verification
|
||||
|
||||
The current production verifier lineage is Geometric Verifier v3.
|
||||
|
||||
One Match Result remains the scientific atomic input.
|
||||
|
||||
The bounded execution shape is:
|
||||
|
||||
```text
|
||||
one admitted owner Task
|
||||
-> bounded independent GVR preparation
|
||||
-> join
|
||||
-> owner publishes the canonical prefix in order
|
||||
-> durable cursor / checkpoint
|
||||
```
|
||||
|
||||
The validated path may use up to eight useful participants and sixteen safe participants, with
|
||||
admitted windows up to sixteen Match Results.
|
||||
|
||||
The internal USAC/MAGSAC scientific solver keeps its validated `isParallel=false` behavior. Cross-item
|
||||
parallelism belongs outside that per-item solver and does not modify scientific thresholds or identity.
|
||||
|
||||
The retained real A6000 continuation completed:
|
||||
|
||||
```text
|
||||
Match Results 38,420
|
||||
Applicable GVRs 37,805
|
||||
Verified GVRs 10,952
|
||||
Rejected GVRs 26,853
|
||||
```
|
||||
|
||||
with deterministic restart and no duplicate mappings.
|
||||
|
||||
## Track Builder
|
||||
|
||||
Track Builder consumes an immutable GVR scope.
|
||||
|
||||
Its compact current memory model supersedes the historical rejected S21 envelope that attempted to
|
||||
reserve approximately 18.204 GiB.
|
||||
|
||||
The retained real S21 proof completed:
|
||||
|
||||
```text
|
||||
Tracks 912,447
|
||||
Track observations 2,495,768
|
||||
```
|
||||
|
||||
The retained real A6000 proof completed:
|
||||
|
||||
```text
|
||||
Tracks 130,714
|
||||
Track observations 318,944
|
||||
```
|
||||
|
||||
Both proofs preserve deterministic restart semantics and no authoritative scratch consumption.
|
||||
|
||||
Track Builder resource details remain owned by [`track_builder.md`](track_builder.md).
|
||||
|
||||
## GPU policy
|
||||
|
||||
GPU use is capability- and evidence-driven.
|
||||
|
||||
Canonical rule:
|
||||
|
||||
```text
|
||||
validated AND useful backend -> preferred when eligible and Governor-safe
|
||||
unvalidated backend -> never promoted for utilization appearance
|
||||
measured non-useful backend -> may remain CPU
|
||||
```
|
||||
|
||||
Current examples:
|
||||
|
||||
```text
|
||||
ORB Matcher Vulkan validated and preferred
|
||||
SIFT / RootSIFT Matcher CPU
|
||||
Candidate CPU
|
||||
Feature CPU
|
||||
Visual Index CPU
|
||||
Geometric Verification CPU
|
||||
```
|
||||
|
||||
This inventory may evolve only through measured, validated backend work.
|
||||
|
||||
## External SSD and scratch
|
||||
|
||||
The optional external SSD controller is a physical-lifecycle boundary using the reviewed
|
||||
UDisks2/GDBus contract.
|
||||
|
||||
Its state is registered with the Resource Governor. The Governor is the sole production orchestrator
|
||||
for scratch leases.
|
||||
|
||||
At the current checkpoint:
|
||||
|
||||
```text
|
||||
16 production Task kinds
|
||||
0 authoritative scratch-consuming Task kinds
|
||||
```
|
||||
|
||||
Scratch availability is therefore a capability, not fabricated usage.
|
||||
|
||||
A future Task may use scratch only after an explicit Task-specific contract defines eligibility,
|
||||
lease lifetime, path ownership, cleanup, cancellation, failure handling, capacity accounting and
|
||||
restart behavior.
|
||||
|
||||
Scratch and swap never become RAM.
|
||||
|
||||
## Reference-host measurements
|
||||
|
||||
The following measurements are evidence, not portable constants.
|
||||
|
||||
### CPU reserve
|
||||
|
||||
On the Ryzen 7 8845HS reference host:
|
||||
|
||||
```text
|
||||
16 logical CPUs total
|
||||
~4 logical CPUs reserved for interactive use
|
||||
~12 logical CPUs available to Lardon3D compute
|
||||
```
|
||||
|
||||
Historical isolated Matcher measurements observed only a small additional gain from 12 to 16 logical
|
||||
threads. That observation supports the reference-host reserve; it does not create a global 12-thread
|
||||
product ceiling.
|
||||
|
||||
### Geometric Verifier historical resource-aware run
|
||||
|
||||
A retained resource-aware GV run traversed approximately 2001 reused parents in about 5.870 seconds
|
||||
through Task, Project DB, checkpoints and Governor.
|
||||
|
||||
The test process peaked around 25,964 KiB RSS, `MemAvailable` remained above approximately 10.69 GiB
|
||||
and swap deltas remained zero.
|
||||
|
||||
This measurement validates the bounded path and restart behavior. It is not a universal latency
|
||||
model.
|
||||
|
||||
### A6000 current proof
|
||||
|
||||
The later A6000 proof recorded 2,714 Governor admissions while continuing Match Result -> GV -> Tracks.
|
||||
|
||||
Its final GV admitted window was 16, with the reference-host compute pool at 12 logical CPUs and four
|
||||
reserved for host use. Swap-in/out deltas remained zero.
|
||||
|
||||
The checkpoint is:
|
||||
|
||||
```text
|
||||
real-a6000-pre-sfm-2026-09-02
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
Sparse SfM and Dense/MVS were not executed by this proof.
|
||||
|
||||
## Recovery and deterministic publication
|
||||
|
||||
Parallel preparation never weakens publication ordering.
|
||||
|
||||
For every path where durable order matters:
|
||||
|
||||
```text
|
||||
prepare independent work
|
||||
-> join
|
||||
-> validate exact immutable result
|
||||
-> owner-only deterministic publication
|
||||
-> durable typed cursor
|
||||
-> generic Task checkpoint
|
||||
```
|
||||
|
||||
A checkpoint may lag an already published immutable scientific result. Restart must reuse or validate
|
||||
that result through its exact scientific identity; it must not fabricate a replacement identity.
|
||||
|
||||
Cancellation is cooperative at the Task's documented boundaries. All admitted participants are
|
||||
bounded and joined before sequence completion or owner cleanup.
|
||||
|
||||
## Limits and deferred work
|
||||
|
||||
The following remain outside this document's current production contract:
|
||||
|
||||
- cross-Task worker pools;
|
||||
- multiple simultaneous active Queue callbacks;
|
||||
- general DAG scheduling;
|
||||
- multi-GPU scheduling;
|
||||
- cgroup/systemd constrained-runtime capacity accounting;
|
||||
- generic resource residency/cache management;
|
||||
- authoritative Task scratch consumers;
|
||||
- unvalidated GPU ports;
|
||||
- dense/MVS resource orchestration.
|
||||
|
||||
Deferring cross-Task parallelism is not permission to serialize independent units inside one active
|
||||
Task.
|
||||
|
||||
The Queue/Governor plus bounded internal participants are the current production architecture.
|
||||
|
||||
## Current summary
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
CURRENT_PRODUCTION_TASK_KINDS=16
|
||||
|
||||
raw.develop.batch/1 CURRENT
|
||||
features.extract.batch/1 CURRENT
|
||||
|
||||
PER_ITEM_ATOMICITY_REQUIRES_CROSS_ITEM_SERIALISM=NO
|
||||
OWNER_ONLY_PUBLICATION_REQUIRES_SERIAL_PREPARATION=NO
|
||||
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
|
||||
VALIDATED_GPU_BACKENDS_PREFERRED_WHEN_USEFUL=YES
|
||||
UMA_ACCOUNTED_ONCE_AGAINST_HOST_RAM=YES
|
||||
SWAP_ZRAM_SCRATCH_AS_RAM=FORBIDDEN
|
||||
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
SPARSE_SFM_EXECUTED_IN_A6000_PROOF=NO
|
||||
DENSE_MVS_EXECUTED_IN_A6000_PROOF=NO
|
||||
```
|
||||
Project DB v12 stocke un résultat borné à 1024 octets de masque et neuf
|
||||
binary64. `geometric_verifier.run` v1 traite chaque Match Result comme unité
|
||||
scientifique atomique, publie par une transaction courte puis checkpoint son
|
||||
curseur par lots 1..16 avant
|
||||
`task_sequence_break()`. Sa ligne durable appartient à Project DB v13. Le job
|
||||
peut employer jusqu'à huit participants utiles et seize participants sûrs, par
|
||||
lots au plus seize, avec 8 Mio par parent et sans slot GPU. Le propriétaire
|
||||
publie le préfixe canonique dans l'ordre. Admission, pression, lots et
|
||||
slow-start restent exclusivement décidés par Runtime et Governor ; l'USAC
|
||||
scientifique interne conserve `isParallel=false`.
|
||||
|
||||
## GPU et files
|
||||
|
||||
La Radeon 780M est UMA : toute mémoire GPU compte aussi comme pression RAM.
|
||||
Vulkan 1.4.354 énumère la 780M RADV et une file compute dédiée. Le backend ORB
|
||||
top-2 de production possède un contexte lazy réutilisable et jusqu'à deux jobs
|
||||
privés en vol sur cette file, sans helper hôte. Chaque slot mappé vaut 640 Kio.
|
||||
Le backend part de zéro et retient exactement un slot après une initialisation
|
||||
ou séquence AUTO normale depth 1. Il n'alloue le second que sous un contrat
|
||||
privé de sûreté/benchmark depth 2 déjà admis, puis le libère avant de franchir
|
||||
la prochaine admission depth 1. Les
|
||||
publications restent strictement ordonnées et le fallback CPU reste exact. Le
|
||||
CPU reste le fallback portable si Vulkan est absent, incompatible ou désactivé
|
||||
pour la session.
|
||||
|
||||
La feasibility SIFT/RootSIFT a borné son prototype Vulkan à 8,125 Mio de
|
||||
payload lazy, mais n'a pas franchi la Gate de production. Le Governor ne réserve
|
||||
donc aucun slot ni budget GPU pour SIFT/RootSIFT ; leur estimation CPU publiée
|
||||
reste inchangée.
|
||||
|
||||
## Profil interactif 8845HS mesuré
|
||||
|
||||
- budget CPU Lardon3D observé : 12 threads logiques, 4 réservés au desktop ;
|
||||
- réserve dure `MemAvailable` : 3 Gio ;
|
||||
- zone de prudence `MemAvailable` : de 3 à 4 Gio ;
|
||||
- Feature workers : 1 ; batch : 1 image ;
|
||||
- Matcher workers : 1 ; lots adaptatifs 1, 2, 4 ou 8 Candidate Pairs ;
|
||||
- Geometric Verifier : un callback propriétaire, jusqu'à 8 participants utiles,
|
||||
lots adaptatifs jusqu'à 16 Match Results ;
|
||||
- profondeur de la Task Queue : 64 tâches légères, un seul callback actif ;
|
||||
- PSI CPU avg10 : nouvelle admission suspendue à 20 % ;
|
||||
- PSI mémoire avg10 : nouvelle admission suspendue à 1 % ;
|
||||
- PSI I/O avg10 : seuil existant 80 %.
|
||||
|
||||
Le benchmark Matcher 8192 mesure environ 70 ms ORB et 135 ms SIFT à 12 threads,
|
||||
contre 68 ms et 127 ms à 16 threads : le profil interactif abandonne environ
|
||||
3–7 % de latence isolée pour réserver quatre threads logiques au desktop.
|
||||
|
||||
Le run soutenu Geometric Verifier traverse environ 2001 parents réutilisés en
|
||||
5,870 s via Task, DB, checkpoints et Governor. Le processus de test culmine à
|
||||
25 964 Kio RSS ; `MemAvailable` reste au-dessus de 10,69 Gio et les compteurs
|
||||
swap restent nuls. PSI avg10 final vaut 0,34 % CPU et 0 % mémoire/I/O. Cette
|
||||
mesure valide le chemin resource-aware et la reprise ; elle ne prétend pas être
|
||||
une distribution de latence estimator-only.
|
||||
|
||||
## Limites
|
||||
|
||||
Les pools multi-workers restent hors périmètre. La capacité CPU portable est
|
||||
désormais bornée par le compute-pool de l'hôte et la limite intrinsèque du kind,
|
||||
jamais par un plafond global 12.
|
||||
SIFT/RootSIFT et Feature Extraction Vulkan restent hors de ce contrat.
|
||||
Swap, zram et disque externe ne sont jamais ajoutés au budget RAM. Aucun chemin
|
||||
scratch/spill Task n'appartient à Gate G core. La commande SSD optionnelle est
|
||||
une intégration opérationnelle additive, pas une admission scientifique.
|
||||
|
||||
La validation B3 du modèle Sparse SfM v16 a utilisé des processus frais, un
|
||||
fixture synthétique de 100 000 landmarks et 500 000 observations, cinq passes
|
||||
de paging sur 50 000 landmarks, et n'a observé ni OOM, ni swap storm, ni dérive
|
||||
RSS applicative. Cette preuve concerne la persistance bornée, pas le solveur.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -268,7 +268,7 @@ et Visual Index restent CPU; SIFT/RootSIFT Matcher restent BFMatcher L2 CPU. Il
|
|||
n'est introduit ni second scheduler, ni Queue, ni daemon, ni sous-système de
|
||||
ressources.
|
||||
|
||||
### Audit des 16 kinds de production
|
||||
### Audit des 14 kinds de production
|
||||
|
||||
Tous les kinds passent par l'unique Queue et l'unique Governor, y compris ceux
|
||||
dont toutes les dimensions sont fixes. Dans le tableau, `CPU 1..N` décrit la
|
||||
|
|
@ -283,16 +283,14 @@ allocations driver opaques ne reçoivent pas un coût inventé.
|
|||
| Kind v1 | Estimation courante | Dimension consommée et raison |
|
||||
| --- | --- | --- |
|
||||
| `raw.develop` | MIXED; CPU 1; lot 1; hôte `2 Gio + contexte`, 0/item; I/O 1; GPU 0 | Un Capture atomique. Le garde applique/restaure CPU1 au pool OpenCV global. Les 2 Gio sont une allowance de travail opérationnelle, pas une limite de dataset. |
|
||||
| `raw.develop.batch` | MIXED; CPU 1..8; lot 1..8; hôte 0 fixe + 896 Mio/item (contexte propriétaire, workspace LibRaw 40 MP, copies RGB/BGR, PNG/validation, pile enfant 1 Mio et marge allocateur/codec); I/O 1; GPU 0 | Une fenêtre de Captures indépendants. `896 Mio × 8 = 7 Gio`, donc le budget post-réserve de l'hôte de référence peut admettre la fenêtre sûre complète. OpenCV reste à 1 thread interne, tous les enfants sont joints, puis le propriétaire publie le préfixe ordonné avant `sequence_break`. Le plafond est opérationnel, jamais une limite scientifique de sélection. |
|
||||
| `photo_quality.triage` | IMPORT; CPU 1; lot 1; hôte `contexte retenu + 20 Mio`, 0/item; I/O 1; GPU 0 | Un groupe par séquence ; garde OpenCV CPU1. Aucun scaling utile déterministe n'est acquis. |
|
||||
| `acquisition_campaign.run` | JPEG: IMPORT ; RAW: MIXED. CPU 1; lot 1; hôte `contexte retenu + requête transitoire exacte + 256 Kio` + 64 Kio/item ; DEVELOP_RAW ajoute 2 Gio; I/O 1; GPU 0 | Un groupe S3-E par séquence, sans Task imbriqué. La création et la reprise dérivent la même estimation ; la forme historique exacte est normalisée seulement en mémoire. |
|
||||
| `import.images` | IMPORT; CPU 1; lot 1..32; hôte 128 Kio + `NAME_MAX+64`/item; I/O 1; GPU 0 | Copie/hash I/O-bound. Le callback consomme le lot admis et réadmet entre lots. |
|
||||
| `features.extract` | CPU; CPU 1..compute-pool; lot 1; hôte 64 Mio + 512 Mio/item; I/O 1; GPU 0 | La demande durable emploie le maximum `int` positif de l'API OpenCV ; le Governor la borne à l'hôte. Le garde applique/restaure exactement le CPU admis. |
|
||||
| `features.extract.batch` | CPU; CPU 1..12; lot 1..12; hôte 64 Mio + 512 Mio/item; I/O 1; GPU 0 | Images sélectionnées indépendantes, OpenCV CPU1 par participant, enfants joints et publication owner-only ordonnée. Le plafond 12 est une capacité opérationnelle de mesure ; le feedback ≥5 % établit le palier utile sans devenir une limite scientifique. |
|
||||
| `features.extract.sift` | CPU; CPU 1..compute-pool; lot 1; hôte 64 Mio + 1 Gio/item; I/O 1; GPU 0 | Même contrat OpenCV. Les formes CPU12 et CPU1 historiques complètes sont acceptées et normalisées en mémoire. |
|
||||
| `features.extract.rootsift` | CPU; CPU 1..compute-pool; lot 1; hôte 64 Mio + 1 Gio/item; I/O 1; GPU 0 | Même contrat que SIFT ; aucune couture GPU scientifiquement compatible n'est validée. |
|
||||
| `visual_index.update` | CPU; CPU 1..16; lot 1..16; hôte 8 Mio + 2 Mio/item; I/O 1; GPU 0 | Le segment contient au plus 16 Feature Sets indépendants. Au plus `cpu_threads-1` enfants sont joints avant publication owner-only. |
|
||||
| `candidate_pair.generate` | CPU; CPU 1..64; lot 1..64; hôte 256 Kio + 8 Mio/item; I/O 1; GPU 0 | Le batch de 64 est la borne algorithmique et de ressources. CPU/lot sont couplés pour que chaque palier de feedback exerce ses participants; fenêtre `min(2*CPU, 64, reste_du_lot)`, un handle DB privé par participant, piles enfants de 4 Mio facturées. |
|
||||
| `candidate_pair.generate` | CPU; CPU 1..64; lot 1..64; hôte 256 Kio + 8 Mio/item; I/O 1; GPU 0 | Le batch de 64 est la borne algorithmique et de ressources. Fenêtre `min(2*CPU, 64, reste_du_lot)`, un handle DB privé par participant, piles enfants de 4 Mio facturées. |
|
||||
| `matcher.run` | CPU: CPU 1..12, lot sûr 1..12, hôte 0 + 10 Mio/item, I/O 1, GPU 0. ORB Vulkan AUTO: CPU 1, lot utile 1..8, même hôte/item, GPU 1 + 640 Kio, inflight 1. | Le batch/participant 12 est une borne intrinsèque mesurée du Matcher. Batch 12 et depth 2 (1,25 Mio) restent sûrs pour preuves privées, mais insuffisamment utiles en AUTO. CPU complet est le fallback. |
|
||||
| `geometric_verifier.run` | CPU; CPU utile 1..8, fenêtre sûre 16; lot 1..16; hôte 0 + 8 Mio/item; I/O 1; GPU 0 | Des parents indépendants sont préparés en parallèle, puis publiés/checkpointés en ordre par le propriétaire. L'USAC scientifique conserve `isParallel=false`. |
|
||||
| `track_builder.run` | CPU; CPU 1; lot 1; fixe `(4 Mio + arêtes * (48 + 2*160)) * facteur`, facteur 2 jusqu'à 400k arêtes puis 8; 0/item; I/O 1; GPU 0 | Rebuild DSU atomique et publication owner-only ; aucune partition scientifiquement validée n'est acquise. |
|
||||
|
|
@ -424,7 +422,7 @@ retirer l'autorité mais ne peut jamais recopier un ancien compte physique.
|
|||
Le teardown production suit strictement : Queue détruite/jointe et chaque
|
||||
lease Task rendu, worker SSD joint puis unregister vérifié, contrôleur détruit,
|
||||
Governor détruit. Un unregister est refusé tant qu'une opération wrapper ou un
|
||||
lease exact subsiste. Les seize Task kinds courants n'ont aucun consommateur
|
||||
lease exact subsiste. Les quatorze Task kinds courants n'ont aucun consommateur
|
||||
scratch : le compte normal est donc réellement zéro et la capacité disponible
|
||||
n'autorise aucun usage implicite. Une future Task consommatrice devra définir
|
||||
son propre contrat d'éligibilité et son ownership sans transformer scratch ou
|
||||
|
|
|
|||
|
|
@ -1,505 +1,314 @@
|
|||
# Lardon3D Runtime
|
||||
# Exécution et runtime Lardon3D
|
||||
|
||||
## Status
|
||||
## 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
|
||||
|
||||
```text
|
||||
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
|
||||
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
|
||||
```
|
||||
|
||||
The runtime coordinates the main thread, Task Queue, Resource Governor, Project DB lifecycle, bounded
|
||||
Task-internal participants, runtime observation and optional external-SSD controller.
|
||||
|
||||
It does not introduce a second scheduler.
|
||||
|
||||
## Thread model
|
||||
|
||||
### Main thread
|
||||
|
||||
Owns:
|
||||
|
||||
- input;
|
||||
- ncurses;
|
||||
- TUI model binding;
|
||||
- project open/close orchestration;
|
||||
- bounded polling of runtime/SSD state.
|
||||
|
||||
ncurses remains main-thread-only.
|
||||
|
||||
### Task Queue worker
|
||||
|
||||
The single Queue worker owns one active heavy Task callback at a time.
|
||||
|
||||
A Task callback may create bounded internal participants only when its admitted Task contract permits
|
||||
them.
|
||||
|
||||
Those participants join before owner publication.
|
||||
|
||||
### SSD operation thread
|
||||
|
||||
The SSD controller may own at most one bounded joinable operation thread while executing a synchronous
|
||||
UDisks operation.
|
||||
|
||||
It does not become:
|
||||
|
||||
- a Task worker;
|
||||
- a Queue;
|
||||
- a scheduler;
|
||||
- an ncurses owner.
|
||||
|
||||
## Task lifecycle
|
||||
|
||||
Conceptual lifecycle:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Pause/cancel are cooperative.
|
||||
|
||||
A sequence break is not a Task state.
|
||||
|
||||
## Durable restart
|
||||
|
||||
A generic snapshot stores logical Task state, not live execution machinery.
|
||||
|
||||
On restoration:
|
||||
|
||||
```text
|
||||
RUNNING -> PENDING
|
||||
PAUSED -> PENDING
|
||||
```
|
||||
|
||||
Terminal states remain terminal.
|
||||
|
||||
Restart never restores:
|
||||
|
||||
- worker thread;
|
||||
- callback pointer;
|
||||
- userdata pointer;
|
||||
- CPU affinity;
|
||||
- live reservation;
|
||||
- GPU handle;
|
||||
- scratch lease;
|
||||
- adaptive feedback history.
|
||||
|
||||
Task Kind Registry reconstructs fresh runtime binding from exact durable kind/version and typed payload.
|
||||
|
||||
Every resumed Task is re-admitted by the Resource Governor.
|
||||
|
||||
## Project-open recovery
|
||||
|
||||
`project_open()` discovers durable Tasks in bounded pages, validates their checkpoint/typed identity,
|
||||
reconstructs eligible bindings and submits them to the existing Queue.
|
||||
|
||||
It returns after enqueue; it does not wait for those Tasks to finish.
|
||||
|
||||
Unknown kinds, unsupported versions, legacy-untyped Tasks, invalid checkpoints or Task-specific
|
||||
non-reconstructible input remain inspectable and do not cause guessed execution.
|
||||
|
||||
Generic dependency/DAG recovery remains unimplemented.
|
||||
|
||||
## Initialization order
|
||||
|
||||
Production startup establishes the safe driver/runtime policy before heavy worker/backend activity.
|
||||
|
||||
Conceptually:
|
||||
|
||||
```text
|
||||
driver policy
|
||||
-> hardware profile
|
||||
-> Resource Governor
|
||||
-> optional backend metadata
|
||||
-> Task Queue / worker
|
||||
-> optional SSD controller + Governor binding
|
||||
-> TUI
|
||||
```
|
||||
|
||||
Project open/recovery is then driven from the main thread.
|
||||
|
||||
The exact source initialization sequence remains authoritative.
|
||||
|
||||
## Project lifetime boundary
|
||||
|
||||
Changing/closing project is an exact ownership boundary.
|
||||
|
||||
Before Project DB close:
|
||||
|
||||
```text
|
||||
views release DB borrows
|
||||
-> Queue ingress closes
|
||||
-> Queue cancels/joins/destroys
|
||||
-> terminal callbacks finish
|
||||
-> Project DB closes
|
||||
```
|
||||
|
||||
A fresh empty Queue can then be created for the next project.
|
||||
|
||||
No terminal callback may dereference a closed Project DB.
|
||||
|
||||
Queue terminal history belongs to the current runtime session and does not leak between projects.
|
||||
|
||||
## Project Database
|
||||
|
||||
Project DB uses an opaque serialized SQLite connection with bounded transactional operations.
|
||||
|
||||
Current schema head:
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
```
|
||||
|
||||
Current additive selected-execution overlays include:
|
||||
|
||||
```text
|
||||
v24 raw.develop.batch/1
|
||||
v25 features.extract.batch/1
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
## Synchronisation
|
||||
|
||||
### Mutex
|
||||
- Protection des données partagées
|
||||
- Accès exclusif aux ressources critiques
|
||||
|
||||
### Variables de condition
|
||||
- Coordination entre threads
|
||||
- Notification de changement d'état
|
||||
- Attente passive ; timeout borné seulement pour réévaluer un `WAIT` ressources
|
||||
|
||||
### Atomicité
|
||||
- Opérations indivisibles
|
||||
- État cohérent garanti
|
||||
|
||||
## Gestion des erreurs
|
||||
|
||||
### 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
|
||||
|
||||
### Récupération
|
||||
- Reprise à la dernière frontière connue
|
||||
- Ignorance des artefacts partiels
|
||||
- Validation avant publication
|
||||
|
||||
## Limites actuelles
|
||||
|
||||
- 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
|
||||
|
||||
## Reprise durable
|
||||
|
||||
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.
|
||||
|
||||
`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.
|
||||
|
||||
**IMPLEMENTED** — snapshot, codec v1 et restauration isolée.
|
||||
|
||||
**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.
|
||||
|
||||
**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.
|
||||
|
||||
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.
|
||||
|
||||
**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.
|
||||
|
||||
**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.
|
||||
|
||||
## Accès Project Database
|
||||
|
||||
**IMPLEMENTED** — une connexion SQLite opaque sérialisée par mutex interne ;
|
||||
les opérations multi-tables sont transactionnelles et bornées.
|
||||
|
||||
**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é.
|
||||
|
||||
**IMPLEMENTED** — la registry reconstruit explicitement callback/userdata hors
|
||||
mutex DB pour un kind connu ; elle ne soumet aucune tâche.
|
||||
|
||||
**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.
|
||||
|
||||
**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.
|
||||
|
||||
**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.
|
||||
|
||||
## Durée de vie terminale et fermeture Queue
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Observatoire TUI actuel
|
||||
|
||||
**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.
|
||||
|
||||
### Séparation modèle, observation et rendu
|
||||
|
||||
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.
|
||||
|
||||
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é.
|
||||
|
||||
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`.
|
||||
|
||||
### Progression et ETA
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
### Pipeline et ressources
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
### Dimensions, couleurs et clavier
|
||||
|
||||
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.
|
||||
|
||||
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 :
|
||||
|
||||
- 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.
|
||||
|
||||
### Workflow optique
|
||||
|
||||
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.
|
||||
|
||||
### SSD F10 et lifetime application
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Invariants
|
||||
|
||||
- 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
|
||||
|
||||
## Statut : CURRENT / VALIDATED OPERATIONAL
|
||||
|
||||
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.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,628 +1,266 @@
|
|||
# Task Kind Registry
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
CURRENT_PRODUCTION_TASK_KINDS=16
|
||||
|
||||
TASK_KIND_REGISTRY_STATUS=IMPLEMENTED
|
||||
COMPUTE_GOVERNOR_V2=PASS/FROZEN
|
||||
ORB_VULKAN_ASYNC_EXECUTION=PASS/FROZEN
|
||||
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
```
|
||||
|
||||
The production registry contains exactly sixteen version-1 Task Kinds.
|
||||
|
||||
The registry is a bounded static dispatch table. It maps durable Task Kind identity to a reconstruction
|
||||
function. It is not a scheduler, dependency graph, plugin loader, resource-policy database or
|
||||
scientific identity resolver.
|
||||
|
||||
Project DB v25 is the current schema head. Earlier schema versions remain authoritative for the Task
|
||||
payloads they introduced.
|
||||
|
||||
## Identity contract
|
||||
|
||||
A durable Task has several distinct identities and versions:
|
||||
|
||||
- stable Task ID;
|
||||
- Task Kind string;
|
||||
- Task Kind version;
|
||||
- generic checkpoint codec version;
|
||||
- Task state;
|
||||
- scientific input identity owned by the Task-specific payload.
|
||||
|
||||
These fields are not interchangeable.
|
||||
|
||||
A Task Kind v1 string is ASCII, 1 to 64 characters, with the form:
|
||||
|
||||
```text
|
||||
[a-z0-9][a-z0-9._-]*
|
||||
```
|
||||
|
||||
The Task Kind version is a non-zero integer.
|
||||
|
||||
No Task Kind is inferred from:
|
||||
|
||||
- Task display name;
|
||||
- callback address;
|
||||
- userdata address;
|
||||
- source file name;
|
||||
- persisted function pointer;
|
||||
- Task ID;
|
||||
- scientific fingerprint.
|
||||
|
||||
No normalization is performed on the persisted kind string.
|
||||
|
||||
## Registry ownership
|
||||
|
||||
The registry contains at most 64 descriptors in a static immutable array.
|
||||
|
||||
Lookup is:
|
||||
|
||||
- linear;
|
||||
- deterministic;
|
||||
- allocation-free;
|
||||
- safe for concurrent readers.
|
||||
|
||||
The registry never dynamically loads code.
|
||||
|
||||
A descriptor contains only:
|
||||
|
||||
- Task Kind string;
|
||||
- Task Kind version;
|
||||
- reconstruction function.
|
||||
|
||||
The public descriptor does not contain resource-policy state, CPU topology, GPU identity, scratch
|
||||
state or persisted scheduler configuration.
|
||||
|
||||
## Reconstruction ownership
|
||||
|
||||
A reconstruction function returns:
|
||||
|
||||
- callback;
|
||||
- callback userdata;
|
||||
- optional userdata destructor;
|
||||
- Task-specific private binding state where required.
|
||||
|
||||
Before ownership transfer, the registry destroys any newly allocated userdata on failure.
|
||||
|
||||
After a successful `Lardon3DTask` restoration, the Task owns the userdata and destroys it exactly once
|
||||
after execution lifetime ends.
|
||||
|
||||
A business-specific constructor is never called while the Project DB mutex is held.
|
||||
|
||||
Project-open recovery copies the durable record out of the DB boundary before registry lookup and
|
||||
Task-specific reconstruction.
|
||||
|
||||
## Exact legacy estimate normalization
|
||||
|
||||
The registry may recognize exact historical operational resource descriptors for restart compatibility.
|
||||
|
||||
The reconstruction function always receives the original durable snapshot.
|
||||
|
||||
If an exact historical descriptor is recognized, the registry may replace only the private effective
|
||||
resource estimate passed to the restored runtime Task.
|
||||
|
||||
This normalization is:
|
||||
|
||||
- ephemeral;
|
||||
- deterministic;
|
||||
- exact-shape only;
|
||||
- non-persistent.
|
||||
|
||||
It never rewrites:
|
||||
|
||||
- the generic checkpoint;
|
||||
- Task ID;
|
||||
- Task Kind/version;
|
||||
- Task progress;
|
||||
- Task-specific cursor;
|
||||
- scientific parameters;
|
||||
- scientific fingerprint;
|
||||
- Project DB payload.
|
||||
|
||||
A neighboring or partially matching historical resource descriptor is rejected rather than guessed.
|
||||
|
||||
A crash before a later terminal checkpoint may therefore repeat the same exact normalization on the
|
||||
next restart.
|
||||
|
||||
## Production inventory
|
||||
|
||||
The production registry is created by:
|
||||
|
||||
```text
|
||||
src/task_kinds.c::lardon3d_task_kind_registry_production()
|
||||
```
|
||||
|
||||
It contains exactly these sixteen version-1 kinds:
|
||||
|
||||
| # | Task Kind | Durable payload / introduction | Reconstruction |
|
||||
| ---: | --- | --- | --- |
|
||||
| 1 | `raw.develop` | Project DB v22 `raw_development_tasks` | `lardon3d_raw_development_task_reconstruct` |
|
||||
| 2 | `raw.develop.batch` | Project DB v24 `raw_development_batch_tasks` | `lardon3d_raw_development_batch_task_reconstruct` |
|
||||
| 3 | `photo_quality.triage` | Project DB v21 | `lardon3d_photo_quality_task_reconstruct` |
|
||||
| 4 | `acquisition_campaign.run` | Project DB v20 | `lardon3d_acquisition_campaign_task_reconstruct` |
|
||||
| 5 | `import.images` | historical typed import payload | `lardon3d_image_import_reconstruct` |
|
||||
| 6 | `features.extract` | historical Feature Task payload | `lardon3d_feature_extract_reconstruct` |
|
||||
| 7 | `features.extract.batch` | Project DB v25 `feature_extract_batch_tasks` | `lardon3d_feature_extract_batch_reconstruct` |
|
||||
| 8 | `features.extract.sift` | typed SIFT Feature payload | `lardon3d_sift_extract_reconstruct` |
|
||||
| 9 | `features.extract.rootsift` | typed RootSIFT Feature payload | `lardon3d_sift_extract_reconstruct` |
|
||||
| 10 | `visual_index.update` | Visual Index Task payload | `lardon3d_visual_index_update_reconstruct` |
|
||||
| 11 | `candidate_pair.generate` | Project DB v9 `candidate_pair_generate_tasks` | `lardon3d_candidate_pair_generate_reconstruct` |
|
||||
| 12 | `matcher.run` | Project DB v11 `matcher_tasks` | `lardon3d_matcher_task_reconstruct` |
|
||||
| 13 | `geometric_verifier.run` | Project DB v13 `geometric_verifier_tasks` | `lardon3d_geometric_verifier_task_reconstruct` |
|
||||
| 14 | `track_builder.run` | Project DB v15 `track_builder_tasks` | `lardon3d_track_builder_task_reconstruct` |
|
||||
| 15 | `sparse_sfm.run` | Project DB v17 `sparse_sfm_tasks` | `lardon3d_sparse_sfm_task_reconstruct` |
|
||||
| 16 | `incremental_reconstruction.run` | Project DB v18 `incremental_reconstruction_tasks` | `lardon3d_incremental_reconstruction_task_reconstruct` |
|
||||
|
||||
The current detailed resource-capability inventory is owned by
|
||||
[`resource_governor.md`](resource_governor.md), in its production sixteen-kind audit.
|
||||
|
||||
Historical documents that correctly recorded fewer kinds at their checkpoint remain historical
|
||||
evidence. They must not be rewritten merely to make their old count equal the current count.
|
||||
|
||||
## Current additive Task Kinds
|
||||
|
||||
The two Task Kinds added after the historical fourteen-kind maintenance inventory are:
|
||||
|
||||
```text
|
||||
raw.develop.batch/1
|
||||
features.extract.batch/1
|
||||
```
|
||||
|
||||
### `raw.develop.batch/1`
|
||||
|
||||
Project DB v24 binds one Task to one immutable selected execution.
|
||||
|
||||
The selected-execution cursor remains authoritative. Independent RAW items may be prepared within a
|
||||
bounded admitted window, participants join, and the owner publishes the selected prefix in canonical
|
||||
order.
|
||||
|
||||
The Task-specific restart path reuses exact already published RAW-derived representations and does not
|
||||
infer identity from path, basename or processing position.
|
||||
|
||||
### `features.extract.batch/1`
|
||||
|
||||
Project DB v25 binds one Task to:
|
||||
|
||||
- one immutable selected execution;
|
||||
- one monotonic `next_item_index`;
|
||||
- exact ORB kind/version/parameters/fingerprint.
|
||||
|
||||
Independent selected images may be prepared concurrently without participant SQLite publication.
|
||||
|
||||
After join, the owner validates/reuses the exact READY Feature Set, publishes in selected-item order,
|
||||
advances the typed cursor and then advances generic Task progress/checkpoint.
|
||||
|
||||
The historical `features.extract/1` kind remains valid and reconstructible.
|
||||
|
||||
```text
|
||||
PER_IMAGE_FEATURE_RESULT=ATOMIC
|
||||
PER_IMAGE_ATOMICITY_REQUIRES_CROSS_IMAGE_SERIALISM=NO
|
||||
OWNER_ONLY_PUBLICATION_REQUIRES_SERIAL_PREPARATION=NO
|
||||
```
|
||||
|
||||
## Compute Governor v2 private seam
|
||||
|
||||
```text
|
||||
COMPUTE_GOVERNOR_V2=PASS/FROZEN
|
||||
```
|
||||
|
||||
The public registry descriptor remains limited to kind, version and reconstruct function.
|
||||
|
||||
Resource capability alternatives live in private runtime state, including the opaque `Lardon3DTask`
|
||||
and private Task/Resource Governor seams.
|
||||
|
||||
The key production admission boundaries remain:
|
||||
|
||||
- Queue admissibility selection;
|
||||
- `lardon3d_task_sequence_break()`;
|
||||
- Resource Governor capability selection from one fresh snapshot.
|
||||
|
||||
This private capability envelope is not:
|
||||
|
||||
- scientific identity;
|
||||
- a new Project DB payload;
|
||||
- a Task Kind version;
|
||||
- a scheduler;
|
||||
- a dependency graph;
|
||||
- durable hardware identity.
|
||||
|
||||
The selected resource contract is immutable for one admitted sequence. A later sequence may receive a
|
||||
different admitted contract.
|
||||
|
||||
A Task with no valid alternatives receives the honest fixed capability represented by its contract.
|
||||
|
||||
## Host CPU policy
|
||||
|
||||
The Resource Governor owns host CPU policy.
|
||||
|
||||
It determines:
|
||||
|
||||
- allowed affinity mask;
|
||||
- package/core/SMT topology where available;
|
||||
- interactive reserve;
|
||||
- compute pool;
|
||||
- actual worker affinity;
|
||||
- per-kind admission bounds.
|
||||
|
||||
CPU IDs never enter:
|
||||
|
||||
- Task Kind descriptor;
|
||||
- generic checkpoint;
|
||||
- Project DB Task payload;
|
||||
- scientific fingerprint.
|
||||
|
||||
Reference-host observations such as a 12-logical-CPU compute pool are evidence for that host, not a
|
||||
portable registry ceiling.
|
||||
|
||||
The canonical repository policy is:
|
||||
|
||||
```text
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
```
|
||||
|
||||
After the interactive reserve and all safety constraints are preserved, useful validated compute
|
||||
capacity should not remain idle merely because an older durable resource descriptor was smaller.
|
||||
|
||||
## OpenCV kinds
|
||||
|
||||
The OpenCV-controlled kinds include:
|
||||
|
||||
```text
|
||||
features.extract
|
||||
features.extract.sift
|
||||
features.extract.rootsift
|
||||
```
|
||||
|
||||
Exact historical CPU1/CPU12 descriptors may be recognized for restart compatibility.
|
||||
|
||||
The effective current Task request can use the portable positive OpenCV upper bound, while the Resource
|
||||
Governor limits actual admission to the host compute pool.
|
||||
|
||||
The callback applies the admitted OpenCV thread count and restores the process-wide baseline on every
|
||||
exit path.
|
||||
|
||||
Controlled validation at 1/2/4/8/12 threads preserved the required scientific outputs for the
|
||||
validated ORB/SIFT/RootSIFT contracts.
|
||||
|
||||
Those thread counts are operational evidence and do not change:
|
||||
|
||||
- extractor fingerprint;
|
||||
- Feature Set identity;
|
||||
- Feature File format;
|
||||
- Task Kind version.
|
||||
|
||||
For `features.extract.batch/1`, cross-image participants are the primary concurrency mechanism.
|
||||
OpenCV CPU teams are not multiplied blindly inside every participant.
|
||||
|
||||
## Candidate Pair normalization
|
||||
|
||||
`candidate_pair.generate/1` reconstructs:
|
||||
|
||||
- `visual_index_id`;
|
||||
- `after_feature_set_id`;
|
||||
- `top_k`;
|
||||
- `minimum_evidence_count`;
|
||||
- `scanset_filter`;
|
||||
- `exclude_same_asset`.
|
||||
|
||||
Exact earlier CPU1 and CPU12 resource shapes may be normalized in memory.
|
||||
|
||||
The current validated Candidate capability is represented by the current source/runtime contract,
|
||||
including:
|
||||
|
||||
```text
|
||||
fixed RAM 256 KiB
|
||||
per-item RAM 8 MiB
|
||||
batch range 1..64
|
||||
CPU/batch coupled during useful scaling
|
||||
GPU none
|
||||
```
|
||||
|
||||
The registry does not reinterpret the old durable checkpoint as if it had been created with those
|
||||
current values.
|
||||
|
||||
## Matcher reconstruction
|
||||
|
||||
`matcher.run/1` reconstructs the immutable Matcher configuration and durable
|
||||
`after_candidate_pair_id` cursor.
|
||||
|
||||
Project DB v11 introduced the typed Matcher payload after Match Result persistence in v10.
|
||||
|
||||
### Durable backend-class compatibility
|
||||
|
||||
The reconstruct path accepts only explicitly supported exact historical/current resource signatures.
|
||||
|
||||
These include the validated classes required for:
|
||||
|
||||
- current CPU operation;
|
||||
- current ORB AUTO/MIXED policy;
|
||||
- explicit Vulkan operation;
|
||||
- exact historical CPU/Vulkan restart shapes.
|
||||
|
||||
A neighboring signature fails rather than being treated as a backend hint.
|
||||
|
||||
No hardware identity is persisted in `matcher_tasks`.
|
||||
|
||||
### ORB AUTO
|
||||
|
||||
Only the AUTO/MIXED durable class reconstructs the shared GPU-first policy.
|
||||
|
||||
On a portable build with no usable Vulkan backend, the same durable policy can reconstruct while
|
||||
exposing only the CPU capability.
|
||||
|
||||
Restoring a fixed CPU or fixed Vulkan historical Task does not mutate shared AUTO availability state.
|
||||
|
||||
Backend probing/initialization does not occur on the `project_open()` thread. Actual backend begin
|
||||
belongs to admitted Queue execution.
|
||||
|
||||
### Current useful ORB bounds
|
||||
|
||||
The current production AUTO policy retains:
|
||||
|
||||
```text
|
||||
GPU-first when eligible
|
||||
CPU fallback complete
|
||||
normal Vulkan inflight depth = 1
|
||||
validated private safety depth = 2
|
||||
normal useful Vulkan batch <= 8
|
||||
```
|
||||
|
||||
Depth 2 and Matcher batch 12 remain validated private/safety or benchmark capabilities where
|
||||
applicable, but did not meet the useful-throughput deadband for normal AUTO policy.
|
||||
|
||||
Those performance decisions do not change:
|
||||
|
||||
- `matcher.run/1`;
|
||||
- Matcher fingerprint;
|
||||
- Match Result identity;
|
||||
- Match File bytes;
|
||||
- durable cursor.
|
||||
|
||||
## Geometric Verifier reconstruction
|
||||
|
||||
`geometric_verifier.run/1` reloads:
|
||||
|
||||
- immutable Fundamental verifier configuration;
|
||||
- validated verifier fingerprint;
|
||||
- `after_match_result_id`.
|
||||
|
||||
Project DB v13 adds only its typed Task payload.
|
||||
|
||||
The exact historical serial resource descriptor is recognized for compatibility and may be normalized
|
||||
ephemerally to the current outer-parallel capability.
|
||||
|
||||
The current validated execution shape allows:
|
||||
|
||||
- up to 8 useful participants;
|
||||
- a safe admitted window up to 16 Match Results;
|
||||
- 8 MiB per admitted parent;
|
||||
- owner-only ordered publication.
|
||||
|
||||
The scientific USAC/MAGSAC inner solver remains in its validated serial configuration. Outer
|
||||
cross-item concurrency does not change verifier science.
|
||||
|
||||
## Track Builder reconstruction
|
||||
|
||||
`track_builder.run/1` reconstructs the explicit immutable scope from:
|
||||
|
||||
- Project DB v15 typed Task payload;
|
||||
- validated little-endian scope asset;
|
||||
- exact builder selector/fingerprint;
|
||||
- exact GVR scope identity.
|
||||
|
||||
Corruption, unsupported version, fingerprint mismatch, checksum mismatch, non-canonical ordering,
|
||||
duplicates or invalid scope identity make the Task non-reconstructible.
|
||||
|
||||
Track Builder restart does not invent a scope from current Project DB contents.
|
||||
|
||||
## Sparse SfM reconstruction
|
||||
|
||||
```text
|
||||
sparse_sfm.run/1
|
||||
```
|
||||
|
||||
is **PASS/FROZEN**.
|
||||
|
||||
It reloads the explicit Project DB v17 payload, restores the persisted generic Task estimate and
|
||||
replays the frozen Sparse SfM D/E execution from explicit Track Set and calibration-scope references.
|
||||
|
||||
F0 is recomputed from the scientific payload.
|
||||
|
||||
The generic checkpoint remains version 1.
|
||||
|
||||
The historical fixed CPU1/batch1 Sparse SfM estimate is part of that frozen Task contract. It is not a
|
||||
global argument for serializing unrelated modern Task Kinds.
|
||||
|
||||
## Incremental reconstruction
|
||||
|
||||
```text
|
||||
incremental_reconstruction.run/1
|
||||
```
|
||||
|
||||
is **PASS/FROZEN**.
|
||||
|
||||
It reloads the Project DB v18 payload containing:
|
||||
|
||||
- predecessor reconstruction;
|
||||
- extension Track Set;
|
||||
- calibration scope;
|
||||
- Phase H fingerprint.
|
||||
|
||||
Restart recomputes from those immutable inputs.
|
||||
|
||||
No solver state, generic DAG or hidden dependency edge is persisted.
|
||||
|
||||
## Generic checkpoint and Project DB boundary
|
||||
|
||||
The generic checkpoint codec remains version 1.
|
||||
|
||||
Project DB stores Task Kind and Task Kind version in the generic Task summary.
|
||||
|
||||
Rows migrated from the oldest untyped schema remain:
|
||||
|
||||
```text
|
||||
task_kind = NULL
|
||||
task_kind_version = NULL
|
||||
```
|
||||
|
||||
and are classified as:
|
||||
|
||||
```text
|
||||
LEGACY_UNTYPED
|
||||
```
|
||||
|
||||
They remain inspectable but cannot be reconstructed as a typed production Task.
|
||||
|
||||
Unknown kinds are classified separately from unsupported versions.
|
||||
|
||||
A future unknown Task Kind or unsupported Task Kind version must never trigger guessed code execution.
|
||||
|
||||
No function address is persisted.
|
||||
|
||||
## Project-open recovery
|
||||
|
||||
`project_open()` uses the immutable production registry to restore eligible Tasks.
|
||||
|
||||
The recovery flow is conceptually:
|
||||
|
||||
```text
|
||||
load bounded Task page
|
||||
-> copy durable record outside DB mutex
|
||||
-> validate checkpoint and summary
|
||||
-> classify Task Kind/version
|
||||
-> reconstruct Task-specific binding
|
||||
-> apply exact legacy resource normalization when allowed
|
||||
-> restore Lardon3DTask
|
||||
-> enqueue through normal Queue
|
||||
```
|
||||
|
||||
A full Queue window stops further recovery without mutating unprocessed Tasks.
|
||||
|
||||
Task-local failures remain task-local unless the project schema/identity itself is invalid.
|
||||
|
||||
## Resource feedback is not scientific state
|
||||
|
||||
Private feedback may record bounded throughput observations per Task Kind/backend.
|
||||
|
||||
It may influence a later resource contract only where the capability contract permits adaptation.
|
||||
|
||||
It must not modify:
|
||||
|
||||
- scientific inputs;
|
||||
- Task-specific durable cursor;
|
||||
- Project DB identity;
|
||||
- Task Kind/version;
|
||||
- generic checkpoint codec;
|
||||
- scientific fingerprint;
|
||||
- canonical result ordering.
|
||||
|
||||
Only successfully completed durable work counts as useful work for adaptive feedback where that Task
|
||||
contract requires it.
|
||||
|
||||
Uncertain publication does not become a successful scientific sample.
|
||||
|
||||
## Scratch boundary
|
||||
|
||||
The current production registry contains sixteen Task Kinds, but none has an authoritative
|
||||
scratch-consuming scientific Task contract.
|
||||
|
||||
```text
|
||||
CURRENT_PRODUCTION_TASK_KINDS=16
|
||||
CURRENT_SCRATCH_CONSUMING_TASK_KINDS=0
|
||||
```
|
||||
|
||||
The external SSD controller and Resource Governor scratch wrappers remain operational infrastructure.
|
||||
|
||||
Scratch, swap and zram do not enlarge RAM admission.
|
||||
|
||||
A future scratch consumer requires an explicit Task-specific eligibility, lifetime and cleanup
|
||||
contract. Merely registering the SSD does not change a Task Kind.
|
||||
|
||||
## What the registry does not own
|
||||
|
||||
The registry does not own:
|
||||
|
||||
- Queue scheduling;
|
||||
- resource admission;
|
||||
- CPU topology;
|
||||
- GPU selection;
|
||||
- scratch allocation policy;
|
||||
- Project DB migration;
|
||||
- scientific fingerprint definitions;
|
||||
- artifact path resolution;
|
||||
- dependency/DAG planning;
|
||||
- autosave timers;
|
||||
- orphan reconciliation.
|
||||
|
||||
It only provides typed durable dispatch and reconstruction ownership.
|
||||
|
||||
## Current intentionally unfinished work
|
||||
|
||||
These remain outside the registry contract:
|
||||
|
||||
- global orphan-file reconciliation;
|
||||
- generic dependency/DAG scheduling;
|
||||
- any future new Task Kind not explicitly added to the production array;
|
||||
- future resource-capability changes requiring new proof.
|
||||
|
||||
Existing reconstructible Task Kinds already checkpoint at their Task-specific durable boundaries.
|
||||
The registry must not move those cursors forward with an independent autosave timer.
|
||||
|
||||
## Current checkpoint evidence
|
||||
|
||||
The current registry inventory includes both additive selected-execution kinds:
|
||||
|
||||
```text
|
||||
raw.develop.batch/1
|
||||
features.extract.batch/1
|
||||
```
|
||||
|
||||
The retained A6000 checkpoint is:
|
||||
|
||||
```text
|
||||
real-a6000-pre-sfm-2026-09-02
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
That proof exercised the current upstream/downstream registry through:
|
||||
|
||||
- selected RAW batch;
|
||||
- selected Feature batch;
|
||||
- Visual Index;
|
||||
- Candidate Pair;
|
||||
- Matcher;
|
||||
- Geometric Verifier v3;
|
||||
- Track Builder;
|
||||
|
||||
and stopped before real Sparse SfM.
|
||||
|
||||
The current production inventory remains exactly sixteen Task Kinds.
|
||||
|
||||
## Summary
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
CURRENT_PRODUCTION_TASK_KINDS=16
|
||||
CURRENT_SCRATCH_CONSUMING_TASK_KINDS=0
|
||||
|
||||
TASK_KIND_REGISTRY_STATUS=IMPLEMENTED
|
||||
TASK_KIND_REGISTRY_CAPACITY=64_DESCRIPTORS
|
||||
GENERIC_CHECKPOINT_VERSION=1
|
||||
|
||||
RAW_BATCH_TASK=raw.develop.batch/1
|
||||
FEATURE_BATCH_TASK=features.extract.batch/1
|
||||
|
||||
COMPUTE_GOVERNOR_V2=PASS/FROZEN
|
||||
ORB_VULKAN_ASYNC_EXECUTION=PASS/FROZEN
|
||||
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
# Registry des types métier de tâches
|
||||
|
||||
## Feature kinds v1A
|
||||
|
||||
La registry statique conserve `features.extract` v1 pour ORB et ajoute
|
||||
`features.extract.sift` v1 et `features.extract.rootsift` v1. Les reconstructeurs
|
||||
chargent la table dédiée, revalident le fingerprint et ne capturent aucun
|
||||
`AppState`.
|
||||
|
||||
## Contrat
|
||||
|
||||
Une instance possède un **task ID** stable. Son **task kind** décrit son
|
||||
comportement métier, son **task state** décrit son état d'avancement, la
|
||||
**checkpoint version** décrit le codec générique et la **task kind version**
|
||||
versionne les paramètres nécessaires au reconstructeur. Ces identités ne sont
|
||||
pas interchangeables.
|
||||
|
||||
Le kind v1 est une chaîne ASCII de 1 à 64 caractères au format
|
||||
`[a-z0-9][a-z0-9._-]*`. La version est un entier non nul. Aucun kind n'est
|
||||
déduit d'un nom, callback ou pointeur et aucune normalisation n'est effectuée.
|
||||
|
||||
## Registry et ownership
|
||||
|
||||
La registry est une vue bornée à 64 descriptors sur un tableau statique
|
||||
immutable. Le lookup est linéaire, déterministe, sans allocation et sûr en
|
||||
lecture concurrente. Elle ne charge aucun code dynamiquement.
|
||||
|
||||
Un descriptor contient exactement le kind, sa version et un reconstructeur.
|
||||
Le reconstructeur produit callback, userdata et destructeur optionnel. Avant le
|
||||
transfert, la registry nettoie le userdata sur toute erreur ; après restauration
|
||||
réussie, `Lardon3DTask` en devient propriétaire et le détruit une fois après la
|
||||
fin de l'exécution. Le constructeur métier n'est jamais appelé sous mutex DB.
|
||||
|
||||
La registry peut normaliser une ancienne estimation opérationnelle connue. Le
|
||||
reconstructeur reçoit toujours le snapshot durable original afin de valider le
|
||||
mode exact ; la registry applique ensuite l'estimation effective uniquement à
|
||||
la copie privée transmise à la restauration de `Task`. Cette normalisation est
|
||||
éphémère et déterministe : elle ne stage, ne promeut et ne publie aucun
|
||||
checkpoint contenant seulement une estimation différente sous le même résumé.
|
||||
Une panne pré-terminale peut donc répéter la normalisation exacte. Cette couture
|
||||
ne peut modifier ni identité, paramètres scientifiques, progression ou curseur
|
||||
métier, et toute forme voisine est rejetée.
|
||||
|
||||
## Inventaire production et entrées runtime
|
||||
|
||||
`src/task_kinds.c::lardon3d_task_kind_registry_production()` enregistre les
|
||||
14 kinds v1 du profil de production courant. La colonne « reprise » nomme le
|
||||
reconstructeur du binding ; « callback » nomme l'entrée runtime privée dans le
|
||||
même fichier. Le détail chiffré des capacités est centralisé dans l'[audit des
|
||||
14 kinds](resource_governor.md#audit-des-14-kinds-de-production).
|
||||
|
||||
| Kind v1 | Source, reprise et callback | Réconciliation pré-admission courante |
|
||||
| --- | --- | --- |
|
||||
| `raw.develop` | `raw_development_task.cpp`; `lardon3d_raw_development_task_reconstruct`; `run` | Aucune |
|
||||
| `photo_quality.triage` | `photo_quality_task.cpp`; `lardon3d_photo_quality_task_reconstruct`; `run` | Aucune |
|
||||
| `acquisition_campaign.run` | `acquisition_campaign_task.cpp`; `lardon3d_acquisition_campaign_task_reconstruct`; `run` | Forme courante ou forme v22 exacte vérifiée contre la requête immuable → capacité courante en mémoire |
|
||||
| `import.images` | `import_task.c`; `lardon3d_image_import_reconstruct`; `run_image_import` | Aucune |
|
||||
| `features.extract` | `feature_task.c`; `lardon3d_feature_extract_reconstruct`; `run` | Formes CPU12/CPU1 historiques exactes → demande OpenCV portable ; runtime borné au compute-pool |
|
||||
| `features.extract.sift` | `sift_task.c`; `lardon3d_sift_extract_reconstruct`; `run` | CPU12/CPU1 historiques exacts → demande OpenCV portable ; runtime borné au compute-pool |
|
||||
| `features.extract.rootsift` | `sift_task.c`; `lardon3d_sift_extract_reconstruct`; `run` | Même réconciliation SIFT ; aucune voie GPU validée |
|
||||
| `visual_index.update` | `visual_index_task.c`; `lardon3d_visual_index_update_reconstruct`; `run` | Formes CPU12/CPU1 historiques exactes → CPU/lot 1..16 |
|
||||
| `candidate_pair.generate` | `candidate_pair_task.c`; `lardon3d_candidate_pair_generate_reconstruct`; `run` | Formes CPU12 et CPU1 historiques exactes → CPU/lot 1..64 |
|
||||
| `matcher.run` | `matcher_task.c`; `lardon3d_matcher_task_reconstruct`; `run` | Signatures CPU/Vulkan historiques exactes → formes courantes en mémoire |
|
||||
| `geometric_verifier.run` | `geometric_verifier_task.c`; `lardon3d_geometric_verifier_task_reconstruct`; `run` | Forme sérielle CPU1/batch8 exacte → CPU utile 1..8, lot 1..16 |
|
||||
| `track_builder.run` | `track_builder_task.cpp`; `lardon3d_track_builder_task_reconstruct`; `run` | Aucune |
|
||||
| `sparse_sfm.run` | `sparse_sfm_task.cpp`; `lardon3d_sparse_sfm_task_reconstruct`; `run` | Aucune |
|
||||
| `incremental_reconstruction.run` | `incremental_reconstruction_task.cpp`; `lardon3d_incremental_reconstruction_task_reconstruct`; `run` | Aucune |
|
||||
|
||||
## Couture privée Compute Governor v2
|
||||
|
||||
**COMPUTE_GOVERNOR_V2 — PASS / FROZEN.** Le descriptor C public
|
||||
reste limité à kind, version et reconstructeur. L'enveloppe de capacités est
|
||||
intégrée sans changement d'ABI dans le `struct Lardon3DTask` opaque et les
|
||||
coutures privées `src/task_internal.h` / `src/resource_governor_internal.h`.
|
||||
Les coutures d'admission sont `src/task_queue.c::select_admissible()`,
|
||||
`src/task.c::lardon3d_task_sequence_break()` et, côté Governor,
|
||||
la sélection multi-capacité sur un snapshot unique. La normalisation historique
|
||||
exacte reste dans `src/task_kind_registry.c::normalize_known_legacy_estimate()`.
|
||||
|
||||
Cette enveloppe n'est ni une identité scientifique, ni un nouveau payload
|
||||
Project DB, ni un nouveau scheduler. Le Governor possède l'admission de tous
|
||||
les kinds, y compris les formes entièrement fixes. Le contrat choisi est
|
||||
immutable pendant une séquence et seule la suivante peut être adaptée. Une Task
|
||||
sans alternative reçoit automatiquement une capacité égale à son estimation
|
||||
durable. Le Governor conserve un état borné par kind/backend et un dernier
|
||||
diagnostic ; ni l'enveloppe ni ce choix ne sont persistés.
|
||||
|
||||
La politique CPU hôte reste privée au Governor : masque permis, groupes
|
||||
package/core/SMT, compute-pool et résultat d'application du worker Queue. Le
|
||||
compute-pool borne l'admission de chaque kind. Feature/SIFT/RootSIFT utilisent
|
||||
le maximum `int` positif comme borne de l'API OpenCV, puis consomment le compte
|
||||
immutable réellement admis ; les CPU12 durables ne sont plus que des signatures
|
||||
historiques exactes. Les kinds CPU1 justifiés restent fixes. Aucun ID CPU ou
|
||||
choix d'affinité n'entre dans le descriptor, le checkpoint ou le Project DB.
|
||||
|
||||
Le feedback ne requalifie pas un succès de reprise en travail durable : les
|
||||
kinds Feature/SIFT/RootSIFT comptent un item seulement après extraction et
|
||||
publication durable propre. READY, collision `ALREADY_PRESENT` ou publication
|
||||
incertaine compte zéro ; Visual Index compte pareillement zéro pour un segment
|
||||
`PUBLISHED_NOT_DURABLE`.
|
||||
|
||||
L'état privé par kind/backend coordonne désormais une seule dimension d'essai.
|
||||
Les CPU réductibles slow-startent par doubles successifs depuis 1, puis le
|
||||
maximum exact de leur capacité, toujours bornés par le compute-pool. Après deux
|
||||
observations de baseline, deux observations à au moins +5 % sont nécessaires
|
||||
pour accepter le palier.
|
||||
Une fois CPU stabilisé, seuls les kinds dont le callback consomme réellement
|
||||
son lot peuvent ouvrir un essai de lot. `features.extract`, SIFT et RootSIFT
|
||||
enregistrent une observation atomique réussie partagée entre Tasks ; Visual
|
||||
Index, Candidate et Matcher enregistrent chaque séquence. Les autres formes ou
|
||||
dimensions non adaptables restent égales à leur capacité fixe honnête.
|
||||
|
||||
## Persistance et legacy
|
||||
|
||||
Le checkpoint générique reste en version 1. Project Database v7 conserve le
|
||||
kind/version ; les lignes migrées depuis v1 restent `NULL/NULL` et sont classées
|
||||
`LEGACY_UNTYPED`. Un kind inconnu ou une version non supportée reste inspectable
|
||||
mais inexécutable. Aucun type n'est inventé et aucun code n'est sélectionné par
|
||||
adresse persistée.
|
||||
|
||||
## Statut
|
||||
|
||||
**IMPLEMENTED** — identité typée immutable, registry statique, lookup,
|
||||
migration DB v1→v2, classification recovery et restauration explicite testée.
|
||||
|
||||
**IMPLEMENTED** — le descriptor production `import.images`, version 1, charge
|
||||
le chemin source borné depuis la table dédiée et reconstruit callback et
|
||||
userdata sans `AppState *` ancien.
|
||||
|
||||
**IMPLEMENTED** — `project_open()` utilise la registry production immutable
|
||||
pour restaurer hors mutex DB et transférer chaque tâche acceptée à la queue.
|
||||
|
||||
**NOT_YET_WIRED** — réconciliation orpheline et dépendances/DAG. Les kinds
|
||||
reconstructibles checkpointent déjà à leurs frontières métier ; la Registry ne
|
||||
possède pas un timer autosave et ne doit pas devancer leurs curseurs durables.
|
||||
|
||||
**IMPLEMENTED** — `features.extract` version 1 reconstruit une extraction ORB
|
||||
depuis `image_id` et ses paramètres bornés.
|
||||
|
||||
**IMPLEMENTED** — `visual_index.update`, version 1, recharge
|
||||
`visual_index_id + after_feature_set_id` et reconstruit un contexte neuf.
|
||||
|
||||
**IMPLEMENTED** — `candidate_pair.generate`, version 1, recharge
|
||||
`visual_index_id + after_feature_set_id + top_k + minimum_evidence_count
|
||||
+ scanset_filter + exclude_same_asset` depuis `candidate_pair_generate_tasks`
|
||||
et reconstruit un contexte boundé. La restauration reconnaît le snapshot
|
||||
opérationnel sériel historique v1 exact et la forme CPU12 immédiatement
|
||||
antérieure, puis les remplace éphémèrement par la demande courante CPU64 avec
|
||||
8 Mio par item avant admission. Le checkpoint historique et le curseur typé
|
||||
restent inchangés ; une forme voisine est rejetée.
|
||||
|
||||
**PASS / FROZEN — Compute Governor v2.** `matcher.run`, version 1,
|
||||
recharge la configuration Matcher, l'identité Feature Set et le curseur
|
||||
`after_candidate_pair_id`. Il traite une Candidate Pair atomique à la fois dans
|
||||
des lots bornés à douze, checkpoint le curseur et repasse par le Governor entre
|
||||
les lots. La table durable `matcher_tasks` est introduite par Project DB v11,
|
||||
après le Match Result v10. Son reconstructeur accepte les formes courantes
|
||||
CPU12/GPU0 et CPU1/GPU1/640 Kio à lot `1..12`, les signatures historiques
|
||||
CPU8/GPU0 et Vulkan à lot maximal 8, puis les formes CPU12
|
||||
pré-estimation-par-paire. La normalisation reste en mémoire. Une forme voisine
|
||||
échoue au lieu de servir d'indice de backend ; le payload Project DB ne change
|
||||
pas et ne persiste aucune identité matérielle. Les nouvelles Tasks ORB normales
|
||||
ont une signature de classe `MIXED`, dont les autres champs restent une demande
|
||||
de ressources réelle ; elle seule reconstruit la politique Governor `AUTO`.
|
||||
Toutes les formes ORB de classe `CPU`, anciennes ou courantes, reconstruisent
|
||||
un CPU fixe pour préserver les overrides explicites et une compatibilité sûre ;
|
||||
une forme Vulkan restaurée reste fixe Vulkan. Un build portable reconstruit la
|
||||
même politique `MIXED` mais n'expose que sa capacité CPU. Les snapshots tout à
|
||||
zéro Candidate/SIFT/RootSIFT sont explicitement corrompus ; seules leurs
|
||||
signatures historiques complètes exactes sont acceptées. Seule la forme AUTO
|
||||
restaurée établit la disponibilité Vulkan partagée. Restaurer ensuite CPU,
|
||||
Vulkan ou une signature historique fixe n'écrit rien dans cet état : la
|
||||
co-restauration est indépendante de l'ordre. Aucun nouvel état de
|
||||
backend n'est persisté.
|
||||
|
||||
Pour une Task AUTO, la Registry reconstruit aussi l'enveloppe privée Vulkan
|
||||
CPU1/GPU1, lot opérationnel `1..8`, helpers 0 et inflight 1. La signature
|
||||
durable historique reste à lot `1..12`; la signature 640 Kio
|
||||
reste la forme depth-1 minimale et n'est pas mutée; l'admission normale facture
|
||||
exactement 640 Kio une seule fois sur UMA. Le choix inflight est immutable dans
|
||||
la séquence et ne devient ni payload, ni fingerprint, ni indice de reprise.
|
||||
Vulkan explicite reste depth 1. La capacité privée de sûreté/benchmark peut
|
||||
forcer deux slots et 1,25 Mio sans changer la reconstruction normale.
|
||||
Le backend ne mappe pas le maximum de l'enveloppe à sa création : il retient
|
||||
exactement un slot à depth 1 et deux seulement sous une séquence depth 2 admise,
|
||||
puis libère le second avant l'admission suivante. La signature durable 640 Kio
|
||||
reste donc inchangée sans sous-facturer une allocation depth 2 forcée.
|
||||
|
||||
L'A/B forcé ABBA a mesuré 54,661652238 paires/s à depth 1 et 55,797311953 à
|
||||
depth 2, soit +2,077617 %, sous le deadband 5 %, avec digest identique, quatre
|
||||
séquences de fallback local par exécution et zéro panne/discard. La Registry conserve donc
|
||||
`DEPTH_MAX_VALIDATED_SAFETY=2` pour les seules coutures privées, mais la
|
||||
capacité AUTO normale suit `DEPTH_MAX_USEFUL=1` : depth 2 est
|
||||
**REJECTED_WITH_MEASURED_REASON**, sans nouvelle signature durable.
|
||||
|
||||
La télémétrie privée de `matcher.run` conserve les classes de fallback par
|
||||
séquence et compte aussi les items exacts local-ineligible/backend-failure/other
|
||||
après leur publication durable. Ce détail opérationnel n'ajoute aucun kind,
|
||||
champ durable ou identité et empêche le regroupement batch de devenir un
|
||||
comparateur scientifique. Le commit immédiat par item reste acquis si une
|
||||
paire suivante avorte, tandis que le feedback de séquence n'est pas enregistré;
|
||||
la déduplication actuelle vit seulement avec la Task reconstruite en mémoire.
|
||||
Les logs batch 2/4 antérieurs à ce compteur restent préliminaires et prouvent
|
||||
seulement l'invalidité du comparateur par séquences. Les huit runs item-valides
|
||||
`forced-batch{2,4,8,12}-items{,-b}.stdout.jsonl` conservent chacun 4113 paires,
|
||||
six items locaux, zéro panne/autre et le même digest. Les débits combinés sont
|
||||
54,180767704, 66,094373197, 74,784998723 et 76,755814095 paires/s. Les gains
|
||||
jusqu'à batch 8 dépassent 5 %, celui de 8 à 12 vaut seulement +2,635308425 % :
|
||||
la Registry expose `BATCH_MAX_USEFUL=8` en AUTO normal et réserve batch 12 aux
|
||||
preuves privées (`REJECTED_WITH_MEASURED_REASON`).
|
||||
Le S21 final confirme l'enveloppe Registry en production : `matcher.run` v1
|
||||
reste le même kind durable, AUTO choisit Vulkan pour 21 630 admissions et
|
||||
termine 172 741/172 741 résultats à batch 8/inflight 1/helpers 0. Le passage
|
||||
transitoire 8 → 1 → 2 → 4 → 8 ne modifie ni signature durable, ni
|
||||
fingerprint, ni digest scientifique. Aucun backend ou champ persistant n'est
|
||||
ajouté par cette adaptation.
|
||||
|
||||
La reconstruction AUTO ne sonde ni n'initialise Vulkan sur le thread
|
||||
`project_open()`. Elle expose la capacité depuis les seules métadonnées runtime
|
||||
build/backend/GPU ; le Governor possède le dimensionnement exact et
|
||||
l'admission UMA sur son snapshot. Le premier begin appartient au worker Queue après son
|
||||
affinité. La politique Mesa sûre est déjà établie avant les pthreads et
|
||||
l'initialisation du driver ; aucun sweep/latch auxiliaire n'appartient donc au
|
||||
contexte Task. Une paire localement inéligible n'initialise pas le backend. Une
|
||||
panne réelle produit des paires CPU complètes et rend le backend indisponible
|
||||
aux admissions AUTO suivantes sans réécrire le snapshot durable.
|
||||
|
||||
Le contrôle de benchmark `synchronous` du runner réel n'étend pas le descriptor
|
||||
ni le reconstructeur. Il est compilé hors du binaire production, attaché
|
||||
seulement au contexte éphémère d'une nouvelle Task et refusé par le runner si
|
||||
une Task Matcher doit être reprise. La Registry continue donc à reconstruire
|
||||
uniquement la politique AUTO/fixe déduite de la signature durable, jamais un
|
||||
pipeline de benchmark.
|
||||
|
||||
**IMPLEMENTED** — ORB, SIFT et RootSIFT acceptent leurs formes CPU12/CPU1
|
||||
historiques complètes et les normalisent vers la demande OpenCV portable
|
||||
`INT_MAX`. Le Governor borne l'exécution au compute-pool ; les sorties testées
|
||||
à 1/2/4/8/12 restent égales. Cette compatibilité opérationnelle n'altère ni
|
||||
fingerprint, Feature Set, checkpoint durable, ni politique scientifique.
|
||||
|
||||
**IMPLEMENTED** — `geometric_verifier.run`, version 1, recharge la configuration
|
||||
Fundamental immuable, en revalide le fingerprint et reprend `after_match_result_id`.
|
||||
Project DB v13 ajoute uniquement `geometric_verifier_tasks`, car le checkpoint
|
||||
générique v1 ne possède aucun payload propre au kind.
|
||||
|
||||
La forme historique série exacte (4 Mio fixes, CPU1, batch 1..8) est normalisée
|
||||
en mémoire vers 8 Mio par item, CPU utile 8 et batch maximal 16. Cette évolution
|
||||
ne touche ni fingerprint, GVR, ordre, curseur ni checkpoint historique ; une
|
||||
forme voisine est refusée.
|
||||
|
||||
**IMPLEMENTED** — `track_builder.run`, version 1, reconstruit un scope explicite
|
||||
depuis son payload Project DB v15 et son asset little-endian validé. Le callback
|
||||
réutilise l'orchestration Gate C et le reconstructeur refuse toute corruption,
|
||||
version, fingerprint, checksum, tri, unicité ou L3DTSIS1 incohérents.
|
||||
|
||||
**PASS / FROZEN** — `sparse_sfm.run`, version 1, recharge le
|
||||
payload scientifique explicite Project DB v17, restaure l'estimation générique
|
||||
persistée et rejoue D puis E depuis les références Track Set/calibration. Le
|
||||
fingerprint F0 est recalculé ; le checkpoint générique v1 reste inchangé.
|
||||
|
||||
**PASS / FROZEN** — `incremental_reconstruction.run`,
|
||||
version 1, recharge le payload Project DB v18 composé du prédécesseur, du Track
|
||||
Set d'extension, du scope de calibration et du fingerprint H. La tâche atomique
|
||||
recalcule depuis ces entrées après redémarrage, passe par la Queue et le
|
||||
Governor avec son estimation H immuable, et ne persiste aucun état de solveur.
|
||||
Elle n'ajoute ni DAG ni dépendance implicite.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -295,60 +295,14 @@ a complete Track Set with the exact non-empty scope.
|
|||
|
||||
## Resource model
|
||||
|
||||
The scientific choice remains **FROZEN**: a full sparse graph and deterministic
|
||||
edge sort, never a dense matrix. The operational representation was reopened
|
||||
after the S21 admission failure without changing observation identity, edges,
|
||||
DSU components, conflict rejection, canonicalization, fingerprint or version.
|
||||
Input Match Files are still read one at a time and are never all resident.
|
||||
**DECISION:** use a full sparse in-memory graph with deterministic edge sort,
|
||||
not a dense matrix, SQLite temporary graph or external merge in v1. This is
|
||||
the minimum complexity credible for the 16 GiB target and permits exhaustive
|
||||
canonical finalization. Input Match Files are read one at a time; they are
|
||||
never all resident.
|
||||
|
||||
The Project path now constructs one private compact graph directly. It owns a
|
||||
Feature Set metadata projection, 16-byte nodes, 8-byte indexed raw edges and
|
||||
one 16-byte-slot open-addressed identity index. The public C core remains an
|
||||
adapter; Project no longer creates a `deque<Observation>`, pointer-edge vector,
|
||||
second observation/map/edge transport and then another public-core table. DSU
|
||||
uses a 32-bit parent array and byte ranks. Component grouping and accepted
|
||||
Track ranges are flat arrays, not `vector<vector<...>>` ownership forests.
|
||||
|
||||
The RAM-only path is valid and is the current production path. No external
|
||||
merge or scratch lease is used, no temporary graph state is authoritative or
|
||||
stored in the DB, and no user flag selects a representation. Consequently no
|
||||
new Governor/SSD wrapper is justified by this change.
|
||||
|
||||
The Task admission estimator is checked integer arithmetic over retained
|
||||
capacities. For `N = 2 × E_raw` (the safe endpoint upper bound), `S` the next
|
||||
power of two at least `1.5 × N + 1`, and `F` the number of distinct Feature
|
||||
Sets resolved from the exact GVR scope, and `M_max` the largest parent Match
|
||||
Result `match_count` in that scope, it reserves:
|
||||
|
||||
`4 MiB + 133 × N + 8 × E_raw + 16 × S + 640 × F + 12 × M_max` bytes.
|
||||
|
||||
The `133 × N` term is documented code accounting: retained compact nodes (16),
|
||||
DSU/group/conflict scratch (17), flat canonical output (24), and worst-case
|
||||
simultaneous per-Track publication serialization (44): stored observation
|
||||
capacity (16), vector objects/capacity (up to 12), and publication rows (16),
|
||||
plus two 16-byte transient sorted publication-validation keys. Those keys prove
|
||||
global observation uniqueness and one-image-per-Track independently of the
|
||||
Builder before the atomic database transaction. They are released before
|
||||
`COMMIT` and are not persistent state.
|
||||
The other terms are actual reserved
|
||||
edge capacity, actual power-of-two identity slot capacity, and a conservative
|
||||
Feature Set projection/cache/hash allowance. The final term is the largest
|
||||
live `Lardon3DMatchFileEntry` vector: Match Files are resolved sequentially,
|
||||
so this is a peak rather than a sum, and `match_count` is intentionally not
|
||||
approximated by the potentially much smaller `inlier_count`. Overflow or more than
|
||||
`UINT32_MAX` possible nodes fails closed before Task creation.
|
||||
|
||||
The former Task formula
|
||||
`(4 MiB + E×48 + 2E×160) × (2 or 8)` was a historical envelope whose
|
||||
coefficients and multiplier did not name live allocations. It is not the
|
||||
current estimator and must not be reinterpreted as such. At S21
|
||||
`E_raw = 6,628,174`, it produced `19,546,898,688` bytes (18.204 GiB), exceeding
|
||||
the `12,750,811,136`-byte host capacity after canonical reserve. The compact
|
||||
formula with `F=0, M_max=0` produces `2,357,184,892` bytes; real admission adds
|
||||
exactly `640 × F + 12 × M_max` and therefore remains derived from the exact scope rather than from
|
||||
an invented fixed 10.48 GiB target.
|
||||
|
||||
Historical implementation-neutral guidance, retained only for comparison:
|
||||
Approximate implementation-neutral accounting, including allocator/alignment
|
||||
headroom:
|
||||
|
||||
- observation key: 16–24 bytes;
|
||||
- unique edge: 16–32 bytes for two compact node references and sort metadata;
|
||||
|
|
@ -426,13 +380,6 @@ unit. It replays the explicit scope after recovery and pauses/cancels only at
|
|||
safe boundaries; publication occurs only after complete canonical output and
|
||||
final scope validation. A cancelled or failed run publishes no incomplete set.
|
||||
|
||||
The Task supplies an internal checkpoint callback to the Project adapter after
|
||||
each complete GVR has been read and incorporated. It never preempts Match File
|
||||
reading, DSU/canonicalization, final revalidation or atomic publication. A
|
||||
pause/cancel therefore discards only reconstructible, non-authoritative RAM;
|
||||
restart replays the same explicit durable scope and exact-identity lookup
|
||||
reuses a publication that committed before a crash.
|
||||
|
||||
## Resource Governor integration (Gate D)
|
||||
|
||||
The Task estimates bounded graph memory and CPU, obtains reservations before
|
||||
|
|
@ -564,17 +511,14 @@ internal SHA-256 helper. Its fingerprint vector remains
|
|||
|
||||
The historical isolated Gate B 1M benchmark's `973752 KiB` is a process
|
||||
high-water RSS measurement; it is not the Gate C/D project integration
|
||||
measurement and predates the compact Project ownership change. The
|
||||
measurement. The
|
||||
synthetic caller retains 136,000,136 bytes of observations and 16,000,000
|
||||
bytes of pointer edges while the core retains its 136,000,136-byte canonical
|
||||
metadata table, 16,000,000-byte normalized edges, DSU/component temporaries
|
||||
and compact output. Compact edges contain node indices only; they do not copy
|
||||
136-byte metadata at either endpoint. The remaining high-water gap is
|
||||
primarily allocator retention plus hash-table buckets/nodes and vector
|
||||
capacity. Those figures characterize the historical public-adapter benchmark,
|
||||
not the current direct Project path; the operational reopening removed the
|
||||
identified Project-to-core duplicate transport while preserving that public
|
||||
adapter for ABI compatibility.
|
||||
capacity. No core duplication bug was found or changed in the closure audit.
|
||||
|
||||
The targeted corpus explicitly covers reversed endpoints, 100 identical
|
||||
edges, contradictory metadata, fingerprint/dimension/version conflicts,
|
||||
|
|
@ -598,8 +542,7 @@ Only Feature Set metadata is loaded—descriptor payloads are never read. A
|
|||
small per-build Feature Set cache is permitted, while Match File buffers are
|
||||
released immediately after edge extraction.
|
||||
|
||||
The complete resolved evidence is inserted directly into the shared private
|
||||
compact core representation. Before the
|
||||
The complete resolved graph is passed unchanged to the Gate B core. Before the
|
||||
frozen `lardon3d_project_db_create_track_set()` publication transaction, the
|
||||
same explicit GVR IDs, status, selector, parent chain and scope count/hash are
|
||||
revalidated. Any missing, rejected, mismatched or corrupt selected input aborts
|
||||
|
|
@ -710,57 +653,3 @@ persistent shell that launched direct runs; it was not Track Builder memory.
|
|||
A normal 250k hardware run completed in 0.368 seconds with 33,228 KiB peak RSS,
|
||||
MemAvailable loss of 14,108 KiB, no swap delta and zero PSI averages. Gate D
|
||||
is **PASS**; Gate E final validation and freeze are complete.
|
||||
|
||||
The final real S21 proof validates the compact operational path on the immutable
|
||||
GV v3 source (Project DB SHA-256 unchanged before and after:
|
||||
`56aa5ec37624b322e9f77a90b138cc7390ef817a9cec3bef7e4c87609fd2eeed`). The
|
||||
fresh primary task 2837 completed at 100% and published Track Set 1 with
|
||||
912,447 Tracks and 2,495,768 observations (length min/max/mean
|
||||
2/42/2.7352470883240341), zero duplicates, zero conflicting-image Tracks and
|
||||
the persisted canonical digest
|
||||
`c30eba192627bf73eaf21ff30d81038d8cc6bbf36a69226f88cdc8c37f7d74a1`. Its
|
||||
wall time was 3,316 seconds (1788262322 to 1788265638). Exact resume reused
|
||||
that Track Set unchanged. In a separate reflink, pending Task 2835 was
|
||||
terminated with `SIGTERM` before publication (zero published Track Sets) and
|
||||
then completed at 100% through the corrected runner, producing the identical
|
||||
Track Set/counts/digest. RSS samples remained stable at approximately 845 MiB
|
||||
and 862 MiB, minimum observed MemAvailable was about 8 GiB, no swap delta was
|
||||
observed, and Governor admission passed. No scratch or scratch lease was used.
|
||||
No Feature, Candidate Pair, Matcher or GVR work was replayed or created, and
|
||||
Sparse SfM/Dense remained at zero.
|
||||
|
||||
## Operational performance slice — PASS
|
||||
|
||||
This post-freeze slice changes no Track science, identity, fingerprint,
|
||||
serialization, Task identity, recovery rule or publication boundary. Phase-A
|
||||
profiling on the exact S21 scope measured 6,628,174 raw inlier edges and found
|
||||
the graph work small: resolve/register 6.099 s, DSU/canonicalization 1.477 s,
|
||||
serialization preparation 0.056 s and final snapshot revalidation 1.618 s.
|
||||
The dominant cost was instead the pre-publication defensive validator, which
|
||||
prepared SQLite statements per observation and compared every pair of Tracks.
|
||||
At 912,447 Tracks, that latter all-pairs relation was accidentally
|
||||
superlinear.
|
||||
|
||||
Publication validation now sorts bounded 16-byte observation and image keys,
|
||||
reuses one Feature Set lookup statement per distinct Feature Set, and rejects
|
||||
exactly the prior invalid inputs: invalid feature index, duplicate observation
|
||||
membership or repeated image in one Track. Its complexity is `O(V log V)` for
|
||||
`V` published observations, with an explicit additional 32-byte-per-node peak
|
||||
in the Governor model above. The atomic `BEGIN IMMEDIATE`/`COMMIT` publication
|
||||
and rollback behavior remain unchanged.
|
||||
|
||||
On dedicated reflinks of the immutable S21 GV v3 source, fresh Task 2835
|
||||
published Track Set 1 in 21 s and the interrupted pending Task 2835 recovered
|
||||
to the same result in 24 s, versus the frozen 3,316 s baseline (approximately
|
||||
157.9× and 138.2× respectively). Both retained 912,447 Tracks, 2,495,768
|
||||
observations, min/max/mean length 2/42/2.7352470883240341, zero duplicate
|
||||
memberships and zero duplicate-image components. Their complete canonical row
|
||||
stream is identical to the frozen proof, so the persistent digest remains
|
||||
`c30eba192627bf73eaf21ff30d81038d8cc6bbf36a69226f88cdc8c37f7d74a1`.
|
||||
|
||||
CPU parallelism was deliberately not added: the remaining Track Builder Task
|
||||
is only tens of seconds, while the profile gives no material CPU-bound graph
|
||||
phase whose parallelization would repay the added determinism, ownership and
|
||||
memory-risk surface. Production still uses its one admitted CPU worker; this
|
||||
is a measured operational decision, not an arbitrary CPU1 policy. No GPU,
|
||||
scratch lease, Sparse SfM or downstream work ran, and no swap was observed.
|
||||
|
|
|
|||
|
|
@ -1,450 +1,526 @@
|
|||
# Track Model v1
|
||||
|
||||
## Status
|
||||
## Scope
|
||||
|
||||
```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 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.
|
||||
|
||||
## Track definition
|
||||
|
||||
A Track is a coherent set of 2D observations believed to correspond to the same physical scene point
|
||||
across multiple images.
|
||||
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)`.
|
||||
|
||||
Observation identity is exactly:
|
||||
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 :
|
||||
|
||||
```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` identifies one immutable Feature Set.
|
||||
- `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_index` is the zero-based keypoint ordinal inside that immutable Feature File.
|
||||
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`.
|
||||
|
||||
The Feature Set directly owns `image_id`; image identity is therefore derivable and is not duplicated
|
||||
in Track observation identity.
|
||||
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.
|
||||
|
||||
## Scientific input
|
||||
## Scientific inputs
|
||||
|
||||
Track Builder consumes only completed verified geometric results selected by one exact verifier
|
||||
identity.
|
||||
Les Tracks sont construits exclusivement à partir de :
|
||||
|
||||
Current production lineage:
|
||||
|
||||
```text
|
||||
verifier_kind = FUNDAMENTAL
|
||||
verifier_version = 3
|
||||
verifier_fingerprint =
|
||||
6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c
|
||||
```
|
||||
Geometric Verification Result
|
||||
status == GEOMETRIC_VERIFIED (2)
|
||||
```
|
||||
|
||||
Historical Track Sets created from Fundamental verifier v1 or v2 remain valid historical scientific
|
||||
objects.
|
||||
correspondant exactement au VERIFICATION_SELECTOR du Track Set.
|
||||
|
||||
They must not be relabelled as v3.
|
||||
|
||||
For each selected `GEOMETRIC_VERIFIED` result, only Match File entries whose corresponding inlier-mask
|
||||
bit is one contribute observation edges.
|
||||
|
||||
A rejected GVR contributes no Track edge.
|
||||
|
||||
## Verification selector
|
||||
|
||||
A Track Set stores the exact verifier selector:
|
||||
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 :
|
||||
|
||||
```text
|
||||
verifier_kind
|
||||
verifier_version
|
||||
verifier_fingerprint
|
||||
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)
|
||||
```
|
||||
|
||||
The builder never selects verification evidence using:
|
||||
Un `GEOMETRIC_REJECTED` ne produit aucun track. Un Match Result non vérifié
|
||||
géométriquement ne suffit pas.
|
||||
|
||||
- timestamp;
|
||||
- "latest";
|
||||
- insertion order;
|
||||
- approximate fingerprint match.
|
||||
## VERIFICATION_SELECTOR
|
||||
|
||||
The current default producer is v3, but the Track Model remains version-independent and can store valid
|
||||
sets from explicitly supported historical selectors.
|
||||
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.
|
||||
|
||||
## Input scope
|
||||
|
||||
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
|
||||
```
|
||||
(
|
||||
verifier_kind INTEGER, -- ex: 1 = FUNDAMENTAL
|
||||
verifier_version INTEGER,
|
||||
parameter_fingerprint BLOB(32)
|
||||
)
|
||||
```
|
||||
|
||||
Conceptually:
|
||||
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.
|
||||
|
||||
```text
|
||||
SHA-256(L3DTSIS1 || id_0 || id_1 || ... || id_N)
|
||||
Valeur production : `(1, 1, SHA-256 de l'encodage canonique 84 octets)`.
|
||||
|
||||
## INPUT_SCOPE
|
||||
|
||||
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_hash BLOB(32) -- SHA-256 canonique
|
||||
gvr_count INTEGER -- nombre de GVR consommés
|
||||
```
|
||||
|
||||
The scope is Project-DB-local because SQLite GVR IDs participate directly.
|
||||
### INPUT_SCOPE_HASH
|
||||
|
||||
`gvr_count` is retained as validation metadata.
|
||||
| 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 |
|
||||
|
||||
An empty scope is invalid.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Track membership invariants
|
||||
|
||||
### Minimum size
|
||||
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.
|
||||
|
||||
A Track has at least two observations.
|
||||
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`.
|
||||
|
||||
### At most one observation per image
|
||||
**SQL** : non protégé (pas de colonne `image_id` dans `track_observations`).
|
||||
**API** : validation par jointure `feature_sets.image_id` avant INSERT.
|
||||
|
||||
One Track cannot contain two observations derived from the same 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.
|
||||
|
||||
This is validated through `feature_sets.image_id`.
|
||||
**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.
|
||||
|
||||
### Observation uniqueness inside one Track Set
|
||||
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.
|
||||
|
||||
Within one Track Set:
|
||||
**SQL** : `REFERENCES feature_sets(feature_set_id)`.
|
||||
|
||||
```text
|
||||
(feature_set_id, feature_index)
|
||||
```
|
||||
5. **Feature index bounds** : `feature_index < feature_sets.feature_count`
|
||||
pour l'observation correspondante.
|
||||
|
||||
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.
|
||||
**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).
|
||||
|
||||
## Track identity
|
||||
|
||||
Persistent Track identity is the opaque SQLite:
|
||||
Un Track persistant possède un identifiant opaque :
|
||||
|
||||
```text
|
||||
track_id
|
||||
```
|
||||
track_id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(track_id > 0)
|
||||
```
|
||||
|
||||
Track Model v1 does not define a content-derived Track hash.
|
||||
Il n'a pas d'identité scientifique dérivée de son contenu en v1. Les raisons :
|
||||
|
||||
Reproducibility and reuse are owned by the Track Set identity, builder configuration and exact input
|
||||
scope.
|
||||
- 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.
|
||||
|
||||
## Track Set identity
|
||||
La reproductibilité est assurée au niveau du Track Set (parent), pas du Track
|
||||
individuel.
|
||||
|
||||
A Track Set is one complete immutable generation.
|
||||
## Track Set / Generation
|
||||
|
||||
Its reuse identity contains:
|
||||
Un **Track Set** est le parent obligatoire de tout Track persistant. Il
|
||||
représente une génération complète de Track Building.
|
||||
|
||||
```text
|
||||
builder_kind
|
||||
builder_version
|
||||
builder_parameter_fingerprint
|
||||
verifier_kind
|
||||
verifier_version
|
||||
verifier_fingerprint
|
||||
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
|
||||
```
|
||||
|
||||
### Identité de reuse
|
||||
|
||||
```
|
||||
(
|
||||
builder_kind,
|
||||
builder_version,
|
||||
parameter_fingerprint,
|
||||
verifier_kind,
|
||||
verifier_version,
|
||||
verifier_fingerprint,
|
||||
input_scope_hash
|
||||
)
|
||||
```
|
||||
|
||||
`gvr_count` validates the scope metadata but is not an independent reuse discriminator.
|
||||
`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).
|
||||
|
||||
`INSERT OR REPLACE` is forbidden.
|
||||
Un set existant avec cette identité exacte est réutilisé. `INSERT OR REPLACE`
|
||||
est interdit.
|
||||
|
||||
An exact existing immutable set is reused.
|
||||
### Immutabilité
|
||||
|
||||
A scientifically different scope/configuration creates a new Track Set.
|
||||
Un Track Set publié est **immutable**. Aucune opération d'append, remove ou
|
||||
merge n'est permise sur un track ou un set existant.
|
||||
|
||||
## Immutability
|
||||
L'invalidation scientifique (nouvelle entrée, nouveau scope, nouvelle
|
||||
configuration) produit un nouveau Track Set. Le set précédent reste intact.
|
||||
|
||||
A published Track Set is immutable.
|
||||
La suppression référentielle utilise `ON DELETE CASCADE` : supprimer un
|
||||
Track Set supprime ses tracks et observations.
|
||||
|
||||
No production operation:
|
||||
### Justification
|
||||
|
||||
- appends to it;
|
||||
- removes observations;
|
||||
- merges existing Tracks;
|
||||
- rewrites memberships;
|
||||
- updates it to a newer verifier version.
|
||||
- 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.
|
||||
|
||||
New evidence creates a new generation.
|
||||
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é.
|
||||
|
||||
Historical generations remain queryable until explicitly deleted.
|
||||
## 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.
|
||||
|
||||
## Persistence
|
||||
|
||||
Track storage was introduced by Project DB v14.
|
||||
### Conceptual schema
|
||||
|
||||
Durable Track Builder Task payload persistence was added in Project DB v15.
|
||||
```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)
|
||||
);
|
||||
|
||||
Later schema migrations through v25 do not reinterpret Track Model v1.
|
||||
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)
|
||||
);
|
||||
|
||||
Conceptual tables:
|
||||
CREATE INDEX tracks_set_idx
|
||||
ON tracks(track_set_id, track_id);
|
||||
|
||||
```text
|
||||
track_sets
|
||||
tracks
|
||||
track_observations
|
||||
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);
|
||||
```
|
||||
|
||||
Publication is atomic for the complete Track Set under one transaction.
|
||||
### Schema invariants
|
||||
|
||||
No Track from that set becomes visible before the complete generation validates and commits.
|
||||
**SQL-enforced :**
|
||||
|
||||
Rollback leaves no partial Track Set.
|
||||
- `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.
|
||||
|
||||
## Ordering
|
||||
**API-enforced :**
|
||||
|
||||
Track Builder publishes deterministic canonical order.
|
||||
- `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.
|
||||
|
||||
`position_in_track` is contiguous from zero.
|
||||
### Note sur la dénormalisation
|
||||
|
||||
The exact builder contract owns edge ordering and conflict resolution; Track Model only persists the
|
||||
validated result.
|
||||
`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.
|
||||
|
||||
No hash-table iteration order may define persistent scientific ordering.
|
||||
|
||||
## Deletion
|
||||
|
||||
Deleting a Track Set cascades to its Tracks and observations.
|
||||
|
||||
A Feature Set referenced by a Track observation cannot be silently removed while the reference remains
|
||||
valid.
|
||||
|
||||
Deletion semantics do not mutate other immutable Track Sets.
|
||||
|
||||
## Pagination and resource bounds
|
||||
|
||||
Track Model storage APIs are paged.
|
||||
|
||||
The model does not impose an arbitrary scientific maximum Track length below the number of images that
|
||||
could legitimately observe the same point.
|
||||
|
||||
It does not materialize a dense image-by-image covisibility matrix.
|
||||
|
||||
Loading one Track loads that Track's observations; project-wide traversal remains paged.
|
||||
|
||||
Execution-memory strategy belongs to Track Builder, not Track Model.
|
||||
|
||||
## Corruption handling
|
||||
|
||||
A loader returns corruption rather than partial best-effort data when it detects conditions such as:
|
||||
|
||||
- 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.
|
||||
|
||||
No loader repairs scientific identity in place.
|
||||
`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`.
|
||||
|
||||
## Provenance
|
||||
|
||||
Track Set provenance includes:
|
||||
### Track Set provenance
|
||||
|
||||
```text
|
||||
builder identity
|
||||
verifier selector
|
||||
input scope hash
|
||||
gvr count
|
||||
```
|
||||
Chaque Track Set conserve :
|
||||
|
||||
Detailed per-edge provenance is not persisted by Track Model v1.
|
||||
- `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.
|
||||
|
||||
Adding such provenance later requires an explicit version/schema decision if persistent representation
|
||||
changes.
|
||||
Ces champs suffisent pour identifier la configuration scientifique complète
|
||||
ayant produit le set.
|
||||
|
||||
## Current production verifier lineage
|
||||
### Edge provenance (deferred)
|
||||
|
||||
Fundamental verifier v1 and v2 are historical scientific identities.
|
||||
En v1, la provenance détaillée (quels GVR spécifiques ont contribué à quel
|
||||
track individuel) n'est pas persistée. Les raisons :
|
||||
|
||||
Current new production verification uses Fundamental v3.
|
||||
- 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.
|
||||
|
||||
V3 adds bounded preflight rejection before the unchanged scientific estimator path and has its own
|
||||
fingerprint.
|
||||
## Invalidation
|
||||
|
||||
Track Builder consumes only exact matching GVR identities.
|
||||
### Invalidation scientifique
|
||||
|
||||
Therefore:
|
||||
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.
|
||||
|
||||
```text
|
||||
HISTORICAL_TRACK_SET_VERIFIER_V1=VALID
|
||||
HISTORICAL_TRACK_SET_VERIFIER_V2=VALID
|
||||
CURRENT_TRACK_SET_VERIFIER_V3=PRODUCTION
|
||||
```
|
||||
### Suppression référentielle
|
||||
|
||||
No historical Track Set is upgraded in place.
|
||||
`ON DELETE CASCADE` s'applique :
|
||||
|
||||
## Real S21 evidence
|
||||
- `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.
|
||||
|
||||
The retained S21 Track proof is:
|
||||
## Atomic publication
|
||||
|
||||
```text
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
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.
|
||||
|
||||
Track Set observations = 2,495,768
|
||||
Tracks = 912,447
|
||||
minimum Track length = 2
|
||||
maximum Track length = 42
|
||||
mean Track length = 2.7352470883240341
|
||||
```
|
||||
- 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.
|
||||
|
||||
Retained digest:
|
||||
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.
|
||||
|
||||
```text
|
||||
c30eba192627bf73eaf21ff30d81038d8cc6bbf36a69226f88cdc8c37f7d74a1
|
||||
```
|
||||
## Resource bounds
|
||||
|
||||
The compact memory model supersedes the older historical 18.204 GiB envelope.
|
||||
- **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.
|
||||
|
||||
That checkpoint did not execute real Sparse SfM.
|
||||
## Corruption handling
|
||||
|
||||
## Real A6000 evidence
|
||||
Le loader doit détecter :
|
||||
|
||||
The current retained A6000 checkpoint is:
|
||||
- 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.
|
||||
|
||||
```text
|
||||
real-a6000-pre-sfm-2026-09-02
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
Toute corruption retourne `CORRUPT` sans résultat partiel.
|
||||
|
||||
Track output:
|
||||
## API
|
||||
|
||||
```text
|
||||
Track Set 1
|
||||
Tracks 130,714
|
||||
Track observations 318,944
|
||||
duplicate obs 0
|
||||
repeated-image 0
|
||||
orphan obs 0
|
||||
```
|
||||
L'API publique implémente :
|
||||
|
||||
The upstream v3 GV scope contained:
|
||||
- `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
|
||||
Applicable GVRs 37,805
|
||||
Verified GVRs 10,952
|
||||
Rejected GVRs 26,853
|
||||
```
|
||||
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.
|
||||
|
||||
Restart traversed the retained scope and reused the same Track Set without creating a duplicate
|
||||
scientific generation.
|
||||
## Explicitly out of scope
|
||||
|
||||
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;
|
||||
- Track Builder algorithmique (union-find, connected components) ;
|
||||
- triangulation ;
|
||||
- 3D coordinates;
|
||||
- coordonnées 3D ;
|
||||
- Essential matrix ;
|
||||
- camera pose ;
|
||||
- bundle adjustment ;
|
||||
- sparse reconstruction / Sparse SfM ;
|
||||
- reprojection error ;
|
||||
- Bundle Adjustment;
|
||||
- dense reconstruction ;
|
||||
- metric scale;
|
||||
- Track mutation/merge;
|
||||
- selection by "latest".
|
||||
- Track optimization ou merge ;
|
||||
- mutation de tracks existants ;
|
||||
- co-visibilité (matrice ou calcul) ;
|
||||
- sélection par timestamp ou "latest".
|
||||
|
||||
## Summary
|
||||
## Track rejected state
|
||||
|
||||
```text
|
||||
TRACK_MODEL_V1=FROZEN
|
||||
TRACK_BUILDER_V1=PASS/FROZEN
|
||||
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_PRODUCTION_VERIFIER=FUNDAMENTAL_V3
|
||||
CURRENT_VERIFIER_FINGERPRINT=6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c
|
||||
## Versioning
|
||||
|
||||
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
|
||||
```
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,430 +1,207 @@
|
|||
# Visual Index v1
|
||||
|
||||
## Status
|
||||
## Problème et frontière
|
||||
|
||||
```text
|
||||
VISUAL_INDEX_V1=IMPLEMENTED
|
||||
VISUAL_INDEX_KIND=orb-lsh
|
||||
VISUAL_INDEX_VERSION=1
|
||||
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.
|
||||
|
||||
CANDIDATE_PAIR=IMPLEMENTED
|
||||
MATCHER=IMPLEMENTED
|
||||
## Choix algorithmique
|
||||
|
||||
VISUAL_INDEX_GPU=REJECTED_WITH_MEASURED_REASON
|
||||
CURRENT_PROJECT_DB_SCHEMA=v25
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
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 turns a homogeneous collection of immutable Feature Sets into bounded image-retrieval
|
||||
candidates.
|
||||
Alternatives évaluées :
|
||||
|
||||
It is a retrieval stage, not a Matcher and not a geometric verifier.
|
||||
- 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.
|
||||
|
||||
Current downstream consumers are implemented:
|
||||
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`.
|
||||
|
||||
```text
|
||||
Feature Store
|
||||
-> Visual Index
|
||||
-> Candidate Pair Generator
|
||||
-> Matcher
|
||||
-> Geometric Verification
|
||||
-> Tracks
|
||||
```
|
||||
## Identité et configuration
|
||||
|
||||
Older text describing Candidate Pair or Matcher as future consumers is historical design context and is
|
||||
not current status.
|
||||
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 :
|
||||
|
||||
## Algorithm
|
||||
- `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`.
|
||||
|
||||
Visual Index v1 uses deterministic binary LSH over ORB descriptors.
|
||||
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.
|
||||
|
||||
It uses six tables.
|
||||
## Échantillonnage
|
||||
|
||||
Each table selects 24 distinct positions from the 256 ORB bits.
|
||||
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.
|
||||
|
||||
The frozen v1 position rule is:
|
||||
## Segments introduits en Project Database v6, conservés en v7
|
||||
|
||||
```text
|
||||
position = (41 * table + 11 * bit) mod 256
|
||||
```
|
||||
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.
|
||||
|
||||
A posting key is:
|
||||
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.
|
||||
|
||||
```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.
|
||||
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.
|
||||
|
||||
## Segment File v1
|
||||
|
||||
Segment File v1 is explicitly little-endian and does not serialize C structs.
|
||||
|
||||
Magic:
|
||||
|
||||
```text
|
||||
L3DVIDX\0
|
||||
```
|
||||
|
||||
A posting contains:
|
||||
|
||||
```text
|
||||
table_id:u32
|
||||
key24:u32
|
||||
feature_set_id:u64
|
||||
feature_index:u32
|
||||
reserved_zero:u32
|
||||
```
|
||||
|
||||
Canonical persistent ordering is:
|
||||
|
||||
```text
|
||||
table_id
|
||||
key24
|
||||
feature_set_id
|
||||
feature_index
|
||||
```
|
||||
|
||||
The complete Segment File is content-addressed by SHA-256 under the Visual Index asset tree.
|
||||
|
||||
## Publication
|
||||
|
||||
Publication follows the normal immutable-asset pattern:
|
||||
|
||||
```text
|
||||
local temp
|
||||
-> write
|
||||
-> fsync
|
||||
-> hash
|
||||
-> no-overwrite publication/adoption validation
|
||||
-> fsync directory
|
||||
-> short Project DB transaction
|
||||
```
|
||||
|
||||
A physical file may remain orphaned if DB publication fails after the file is published.
|
||||
|
||||
No partially committed READY segment is invented.
|
||||
|
||||
Durability distinguishes:
|
||||
|
||||
```text
|
||||
DURABLE
|
||||
PUBLISHED_NOT_DURABLE
|
||||
```
|
||||
|
||||
## Reader validation
|
||||
|
||||
The reader validates the asset SHA before trusting the format.
|
||||
|
||||
It rejects:
|
||||
|
||||
- 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
|
||||
```
|
||||
Le fichier est little-endian et ne sérialise aucune structure C. Layout :
|
||||
|
||||
| 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 |
|
||||
|
||||
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>`.
|
||||
|
||||
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`.
|
||||
|
||||
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.
|
||||
|
||||
## Recherche, score et bornes
|
||||
|
||||
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.
|
||||
|
||||
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`.
|
||||
|
||||
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.
|
||||
|
||||
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é.
|
||||
|
||||
## Tâche, reprise et ressources
|
||||
|
||||
`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.
|
||||
|
||||
**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.
|
||||
|
||||
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).
|
||||
|
||||
## Complexité et limites
|
||||
|
||||
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`.
|
||||
|
||||
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.
|
||||
|
||||
## Frontière future
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,625 +0,0 @@
|
|||
# Lardon3D — Documentation Inventory Audit
|
||||
|
||||
## Status
|
||||
|
||||
DOCUMENTATION_INVENTORY_AUDIT=PASS_WITH_FINDINGS
|
||||
|
||||
Audit branch: docs-audit
|
||||
|
||||
Reference checkpoints:
|
||||
|
||||
- global-maintenance-2026-09-01
|
||||
- real-a6000-pre-sfm-2026-09-02
|
||||
|
||||
This audit concerns documentation only.
|
||||
|
||||
It does not reopen scientific contracts, alter executable behavior, redefine FROZEN evidence, or authorize implementation work.
|
||||
|
||||
## Classification vocabulary
|
||||
|
||||
Documents/findings use: CURRENT_AND_COMPLETE, CURRENT_BUT_INCOMPLETE, PARTIALLY_STALE, STALE, HISTORICAL_AND_VALID, DUPLICATED_AUTHORITY, AMBIGUOUS_AUTHORITY, NEEDS_REORGANIZATION.
|
||||
|
||||
Finding severity: BLOCKING_DOC, IMPORTANT, IMPROVEMENT.
|
||||
|
||||
A BLOCKING_DOC finding means documentation could make a future agent work from the wrong project state. It does not mean executable code is known to be incorrect.
|
||||
|
||||
## Current facts
|
||||
|
||||
Current Project DB head: v25.
|
||||
|
||||
Valid historical/additive foundations remain:
|
||||
|
||||
- v22 = selected scientific execution foundation
|
||||
- v23 = generic optical-context overlay
|
||||
- v24 = raw.develop.batch persistence
|
||||
- v25 = features.extract.batch persistence
|
||||
|
||||
Current production inventory: 16 Task kinds.
|
||||
|
||||
Canonical resource policy:
|
||||
|
||||
- MAXIMUM SAFE USEFUL THROUGHPUT
|
||||
- SERIALISM_REQUIRES_PROOF
|
||||
|
||||
Reference-host evidence:
|
||||
|
||||
- 16 logical CPUs total
|
||||
- approximately 4 logical CPUs reserved for interactive host use
|
||||
- approximately 12 logical CPUs available to compute
|
||||
- approximately 3 GiB MemAvailable hard reserve
|
||||
- Radeon 780M UMA
|
||||
|
||||
These are observations of the reference host, not portable product constants.
|
||||
|
||||
Real-data checkpoints:
|
||||
|
||||
- REAL_S21_TRACKS=PASS/FROZEN
|
||||
- REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
|
||||
A6000 final pre-SfM evidence:
|
||||
|
||||
- Feature Sets: 689
|
||||
- Candidate Pairs: 38,420
|
||||
- Match Results: 38,420
|
||||
- Applicable GVR: 37,805
|
||||
- Verified GVR: 10,952
|
||||
- Rejected GVR: 26,853
|
||||
- Track Sets: 1
|
||||
- Tracks: 130,714
|
||||
- Track observations: 318,944
|
||||
- Sparse SfM: NOT EXECUTED
|
||||
- Dense/MVS: NOT EXECUTED
|
||||
|
||||
# BLOCKING_DOC findings
|
||||
|
||||
## DOC-B01 — Roadmap contains mutually exclusive current states
|
||||
|
||||
File: docs/roadmap/roadmap.md
|
||||
|
||||
The roadmap contains the acquired REAL_A6000_PRE_SFM=PASS/FROZEN result but also retains obsolete current-next material describing Project DB v24/v25 as validation in progress and instructing a resume from A6000 cursor 259 with 430 RAW representations remaining.
|
||||
|
||||
Those operations have already completed.
|
||||
|
||||
Required correction:
|
||||
|
||||
- preserve useful historical evidence;
|
||||
- mark the cursor-259 and validation-in-progress material as historical;
|
||||
- make the final A6000 pre-SfM proof authoritative;
|
||||
- define one unambiguous CURRENT NEXT state.
|
||||
|
||||
## DOC-B02 — Project Database has competing current heads
|
||||
|
||||
File: docs/architecture/project_database.md
|
||||
|
||||
Different sections present v23, v24 and v25 as current state.
|
||||
|
||||
The actual current schema head is v25.
|
||||
|
||||
v22, v23 and v24 remain valid historical and additive contracts. They must not be erased or rewritten as though they never existed.
|
||||
|
||||
Required correction:
|
||||
|
||||
- state once near the top that current Project DB head is v25;
|
||||
- preserve v22 selected execution, v23 optics and v24 RAW batch as historical additive foundations;
|
||||
- update v25 Feature batch lifecycle to match acquired real evidence.
|
||||
|
||||
## DOC-B03 — Persistence advertises v23 as current
|
||||
|
||||
File: docs/architecture/persistence.md
|
||||
|
||||
The document currently states that the current schema is v23.
|
||||
|
||||
Required correction:
|
||||
|
||||
- current schema = v25;
|
||||
- retain historical v7, v16, v22 and v23 contracts;
|
||||
- add a concise v24 RAW batch and v25 Feature batch persistence summary.
|
||||
|
||||
## DOC-B04 — Resource-aware pipeline describes superseded serial Features
|
||||
|
||||
File: docs/architecture/resource_aware_pipeline.md
|
||||
|
||||
Current prose still describes Feature Extraction as one durable task per image with batch one and one worker as the complete operational model.
|
||||
|
||||
That description is incomplete after features.extract.batch/1, Project DB v25, bounded cross-image participants, coupled CPU/batch scaling and owner-only ordered publication.
|
||||
|
||||
The document also retains a fourteen-Task current inventory even though production now contains sixteen Task kinds.
|
||||
|
||||
Required correction:
|
||||
|
||||
- preserve per-image Feature scientific atomicity;
|
||||
- document the v25 cross-image batch path;
|
||||
- distinguish per-item atomicity from cross-item serialism;
|
||||
- update current Task count to sixteen.
|
||||
|
||||
## DOC-B05 — Development testing guide uses obsolete API examples
|
||||
|
||||
File: docs/development/testing.md
|
||||
|
||||
The guide contains examples based on old-style names including task_estimate_t, task_t, task_create, TASK_STATE_IDLE and g_test_*.
|
||||
|
||||
These examples do not represent the current repository API and test style.
|
||||
|
||||
Because this is a developer instruction document rather than historical evidence, stale examples are dangerous.
|
||||
|
||||
It also retains repeated wipe-build and fixed -j8 assumptions that conflict with current delta-validation and host-aware parallelism policy.
|
||||
|
||||
Required correction:
|
||||
|
||||
- rewrite examples against actual current repository tests and public APIs;
|
||||
- use current Meson targets and validation discipline;
|
||||
- preserve exact sanitizer qualifications;
|
||||
- avoid mandatory repeated full rebuilds when the delta does not justify them.
|
||||
|
||||
## DOC-B06 — Sparse SfM opening lifecycle is historical but presented as current
|
||||
|
||||
File: docs/architecture/sparse_sfm.md
|
||||
|
||||
The opening still describes numerical Sparse SfM as deferred to Gate C and later gates.
|
||||
|
||||
Later sections and other canonical documents establish Gates C, D, E, F and G as acquired.
|
||||
|
||||
Required correction:
|
||||
|
||||
- retain Gate A and Gate B history;
|
||||
- label early lifecycle wording as historical;
|
||||
- add a concise current lifecycle summary;
|
||||
- distinguish implemented Sparse SfM capability from real Sparse SfM execution on current historical campaigns, which remains unexecuted because known calibration data is unavailable.
|
||||
|
||||
## DOC-B07 — README presents obsolete current DB state
|
||||
|
||||
File: README.md
|
||||
|
||||
README still presents Project DB v23 as the effective current head.
|
||||
|
||||
Required correction:
|
||||
|
||||
- current Project DB = v25;
|
||||
- mention RAW batch v24 and Feature batch v25;
|
||||
- register REAL_S21_TRACKS=PASS/FROZEN;
|
||||
- register REAL_A6000_PRE_SFM=PASS/FROZEN;
|
||||
- state clearly that Sparse SfM and Dense/MVS were not executed in the A6000 real pre-SfM proof.
|
||||
|
||||
# IMPORTANT findings
|
||||
|
||||
## DOC-I01 — AGENTS.md still describes v25 proof as unfinished
|
||||
|
||||
File: AGENTS.md
|
||||
|
||||
The resource policy and Source Comment Contract are strong and current.
|
||||
|
||||
However, lifecycle prose still describes the v25 Feature-batch tranche as not yet eligible for final closure.
|
||||
|
||||
Required correction:
|
||||
|
||||
- update the v25 lifecycle wording to match acquired evidence;
|
||||
- register REAL_A6000_PRE_SFM=PASS/FROZEN;
|
||||
- retain global-maintenance-2026-09-01 as valid historical review evidence;
|
||||
- document the later A6000 checkpoint without erasing the maintenance checkpoint.
|
||||
|
||||
## DOC-I02 — Resource Boundary contains stale current schema and Task count
|
||||
|
||||
File: docs/architecture/resource_boundary.md
|
||||
|
||||
Present-tense material still contains combinations of current schema v24, Project Database v23 as current identity owner, fourteen current Task kinds and migrations only through v23.
|
||||
|
||||
The no-generic-Resource-System decision remains valid and must remain historical authority.
|
||||
|
||||
Required correction:
|
||||
|
||||
- update only stale present-tense current-state claims;
|
||||
- current schema = v25;
|
||||
- current production inventory = 16 Task kinds;
|
||||
- preserve the original Gate G and no-new-subsystem decisions.
|
||||
|
||||
## DOC-I03 — Architecture overview weakens the canonical resource policy
|
||||
|
||||
File: docs/architecture/overview.md
|
||||
|
||||
The overview states that host stability and TUI responsiveness have priority over maximum throughput without the qualification introduced by the current canonical policy.
|
||||
|
||||
The current rule is to preserve the defined interactive host reserve and then maximize safe useful throughput.
|
||||
|
||||
Leaving additional safe and useful resources idle is not a stability strategy.
|
||||
|
||||
Required correction:
|
||||
|
||||
- align the overview with MAXIMUM SAFE USEFUL THROUGHPUT;
|
||||
- preserve the interactive host reserve as the safety boundary;
|
||||
- summarize v24 and v25 operational overlays without making reference-host values portable constants.
|
||||
|
||||
## DOC-I04 — Candidate resource sections contradict each other
|
||||
|
||||
File: docs/architecture/candidate_pair.md
|
||||
|
||||
The current Task section describes approximately 256 KiB fixed memory, 8 MiB per admitted item, batch 1..64 and coupled CPU/batch scaling.
|
||||
|
||||
A later resource section still describes historical values around a 24-source window and 64 KiB per item.
|
||||
|
||||
Required correction:
|
||||
|
||||
- reconcile the later resource section with the current validated Task capability;
|
||||
- preserve Candidate scientific identity, scoring, ordering and persistence;
|
||||
- keep historical estimates only when explicitly labelled historical.
|
||||
|
||||
## DOC-I05 — Feature Store omits the v25 selected-execution batch path
|
||||
|
||||
File: docs/architecture/feature_store.md
|
||||
|
||||
The scientific Feature File contract is strong, but operational prose still describes only the historical single-image features.extract/1 path as the production model.
|
||||
|
||||
Required correction:
|
||||
|
||||
- preserve features.extract/1;
|
||||
- document features.extract.batch/1 as an additional current operational path;
|
||||
- distinguish per-image scientific atomicity from cross-image execution concurrency;
|
||||
- keep general DAG planning separately deferred.
|
||||
|
||||
## DOC-I06 — Task Kind Registry uses an obsolete 15-kind anchor
|
||||
|
||||
File: docs/architecture/task_kind_registry.md
|
||||
|
||||
The document correctly states that production contains 16 Task kinds but links to resource_governor.md#audit-des-15-kinds-de-production.
|
||||
|
||||
Required correction:
|
||||
|
||||
- synchronize the Resource Governor heading and Registry link around the current 16-kind inventory;
|
||||
- do not alter historical audit counts that were correct at their checkpoint.
|
||||
|
||||
## DOC-I07 — Generic Task adaptation prose conflicts with coupled CPU/batch kinds
|
||||
|
||||
File: docs/architecture/task_system.md
|
||||
|
||||
Generic Compute Governor prose says CPU and batch are never tried together.
|
||||
|
||||
Feature Batch and Candidate now have legitimate coupled CPU/batch rungs because additional CPU cannot exercise additional independent work while the admitted item window remains one.
|
||||
|
||||
Required correction:
|
||||
|
||||
- retain independent-dimension adaptation as the generic rule;
|
||||
- document explicit coupled cross-item exceptions where the dimensions are operationally inseparable for measurement;
|
||||
- do not make coupled scaling universal.
|
||||
|
||||
## DOC-I08 — Build guide retains historical fixed -j8 examples
|
||||
|
||||
File: docs/development/build.md
|
||||
|
||||
Build examples repeatedly use -j8 while current engineering policy requires safe host-aware parallelism.
|
||||
|
||||
Required correction:
|
||||
|
||||
- describe build parallelism as host-derived;
|
||||
- a reference-host example may use approximately -j12 when clearly labelled as reference-host evidence;
|
||||
- no fixed job count becomes a portable product constant.
|
||||
|
||||
## DOC-I09 — Concurrency guide retains historical build-policy examples
|
||||
|
||||
File: docs/development/concurrency.md
|
||||
|
||||
The concurrency, lifetime and TSan qualification rules are valuable and mostly current, but the build example still uses fixed -j8 and some illustrative snippets do not represent exact current public API names.
|
||||
|
||||
Required correction:
|
||||
|
||||
- align build parallelism with host-aware policy;
|
||||
- identify illustrative pseudo-code as illustrative when it is not exact repository API;
|
||||
- preserve the external OpenCV and TBB TSan qualification;
|
||||
- preserve the separate Vulkan validation boundary.
|
||||
|
||||
## DOC-I10 — Visual Index describes implemented downstream work as future
|
||||
|
||||
File: docs/architecture/visual_index.md
|
||||
|
||||
The final future section still describes Candidate Pair Generator and Matcher as future consumers.
|
||||
|
||||
Both are implemented.
|
||||
|
||||
Required correction:
|
||||
|
||||
- replace the stale future wording with the actual current downstream relationship or explicitly mark it as historical design context;
|
||||
- do not change Visual Index scientific identity or capacity contracts.
|
||||
|
||||
## DOC-I11 — Track Model contains stale production selector and pipeline wording
|
||||
|
||||
File: docs/architecture/tracks.md
|
||||
|
||||
The Track Model scientific contract remains valid, but some prose still presents Geometric Verifier v1 as the production selector and Sparse SfM as future.
|
||||
|
||||
Current real Track evidence uses Geometric Verifier v3. Sparse SfM Gates C through G are implemented, while real known-calibration Sparse SfM on the historical real campaigns remains unexecuted.
|
||||
|
||||
Required correction:
|
||||
|
||||
- preserve Track Model v1 identity and persistence semantics;
|
||||
- distinguish historical verifier-v1 examples from the current v3 production lineage;
|
||||
- distinguish implemented Sparse SfM capability from real campaign execution.
|
||||
|
||||
# Document classification
|
||||
|
||||
## CURRENT_AND_COMPLETE
|
||||
|
||||
The following documents are currently strong enough that no major contract rewrite is justified by this audit:
|
||||
|
||||
- docs/architecture/calibration_science_v1.md
|
||||
- docs/architecture/calibration_bootstrap.md
|
||||
- docs/architecture/calibration_solver_preflight_v1.md
|
||||
- docs/architecture/photo_quality_triage.md
|
||||
- docs/architecture/task_queue.md
|
||||
- docs/architecture/matcher.md
|
||||
- docs/architecture/track_builder.md
|
||||
- docs/performance/target_hardware.md
|
||||
|
||||
Small consistency edits may still be appropriate later.
|
||||
|
||||
## CURRENT_BUT_INCOMPLETE
|
||||
|
||||
The following documents have strong core contracts but need limited current-state reconciliation:
|
||||
|
||||
- docs/architecture/internal_parallelism.md
|
||||
- docs/architecture/runtime.md
|
||||
- docs/architecture/geometric_verification.md
|
||||
- docs/architecture/geometric_verifier.md
|
||||
|
||||
## HISTORICAL_AND_VALID
|
||||
|
||||
The following documents must remain historical evidence and must not be mass-modernized:
|
||||
|
||||
- docs/architecture/global_maintenance_audit.md
|
||||
- docs/architecture/foundation_review.md
|
||||
- docs/concepts/matching_and_tracks.md
|
||||
- docs/concepts/reconstruction_layers.md
|
||||
|
||||
Older Task counts, schema versions, measurements and decisions may be exactly correct for the checkpoint described by those documents.
|
||||
|
||||
An old version number is stale only when the prose claims that it is the current state.
|
||||
|
||||
# Documentation authority findings
|
||||
|
||||
## AUTH-01 — Current Project DB authority is ambiguous
|
||||
|
||||
Current-version claims are distributed across:
|
||||
|
||||
- README.md
|
||||
- docs/architecture/project_database.md
|
||||
- docs/architecture/persistence.md
|
||||
- docs/architecture/resource_boundary.md
|
||||
- docs/roadmap/roadmap.md
|
||||
|
||||
They must converge on one current-state fact:
|
||||
|
||||
Project DB current schema = v25
|
||||
|
||||
Detailed v22, v23, v24 and v25 contracts remain owned by the specialized architecture documents.
|
||||
|
||||
## AUTH-02 — Resource policy is duplicated across too many authorities
|
||||
|
||||
Resource policy currently appears in AGENTS, Governor, Resource Boundary, Resource Aware Pipeline, Internal Parallelism, Overview, Target Hardware, Build and Concurrency documentation.
|
||||
|
||||
Desired authority split:
|
||||
|
||||
- docs/architecture/resource_governor.md owns runtime resource policy;
|
||||
- AGENTS.md owns engineering and agent obligations;
|
||||
- docs/performance/target_hardware.md owns reference-host measurements;
|
||||
- other documents summarize and link instead of redefining policy.
|
||||
|
||||
## AUTH-03 — Latest real checkpoint needs first-class documentation
|
||||
|
||||
The repository now contains the real checkpoint real-a6000-pre-sfm-2026-09-02.
|
||||
|
||||
Documentation must explain its relationship to global-maintenance-2026-09-01.
|
||||
|
||||
The newer checkpoint does not erase the maintenance checkpoint. The maintenance checkpoint remains historical review authority for unchanged frozen systems; the A6000 checkpoint adds later operational and real-data evidence.
|
||||
|
||||
## LINK-01 — Task Kind Registry anchor is stale
|
||||
|
||||
docs/architecture/task_kind_registry.md states sixteen kinds but points to a Resource Governor anchor named for fifteen kinds.
|
||||
|
||||
The heading and cross-link must be synchronized without altering historical fourteen-kind audit evidence.
|
||||
|
||||
# Repository language decision
|
||||
|
||||
Human authority has selected English as the canonical repository language.
|
||||
|
||||
The final repository target is:
|
||||
|
||||
- DOCUMENTATION_LANGUAGE=ENGLISH
|
||||
- SOURCE_COMMENT_LANGUAGE=ENGLISH
|
||||
- AGENT_CONTRACT_LANGUAGE=ENGLISH
|
||||
- USER_INTERFACE_LANGUAGE=ENGLISH
|
||||
|
||||
All technical documentation, agent contracts, prompt files and production source comments are to converge on English during the remediation passes.
|
||||
|
||||
Translation must preserve scientific meaning, historical truth, FROZEN contracts, identities, numeric values, lifecycle state and evidence. A historical document may be translated but must not be silently modernized.
|
||||
|
||||
The canonical TUI language is English. Existing non-English UI strings are to be remediated in an explicitly scoped UI-language pass; this documentation audit does not itself authorize executable UI changes.
|
||||
|
||||
# Product documentation intentionally deferred
|
||||
|
||||
This documentation cleanup must not invent final contracts for:
|
||||
|
||||
- Viewer behavior;
|
||||
- Sony A6000 live acquisition;
|
||||
- Samsung S21 live acquisition;
|
||||
- Coverage Analysis;
|
||||
- Capture Guidance;
|
||||
- suggested viewpoints;
|
||||
- video and keyframe ingestion;
|
||||
- final optics onboarding UX;
|
||||
- optics profile import and export;
|
||||
- final mesh, texture and export UX.
|
||||
|
||||
Those areas will be defined explicitly by the human during the PRODUCT_DEFINITION phase before the final prompt/ execution contract is frozen.
|
||||
|
||||
Already established human product intent includes:
|
||||
|
||||
- NEW_CAMERA_REQUIRES_CODE_CHANGE=NO
|
||||
- NEW_LENS_REQUIRES_CODE_CHANGE=NO
|
||||
- ELECTRONIC_LENS_WITH_METADATA=SUPPORTED
|
||||
- MANUAL_LENS_WITHOUT_EXIF=SUPPORTED
|
||||
- MULTIPLE_LENSES_PER_CAMERA=SUPPORTED
|
||||
- ZOOM_MULTIPLE_FOCALS=SUPPORTED
|
||||
- MULTIPLE_OPTICAL_CONFIGURATIONS_PER_PROJECT=SUPPORTED
|
||||
- SILENT_CALIBRATION_SUBSTITUTION=FORBIDDEN
|
||||
- SILENT_LENS_IDENTITY_INFERENCE=FORBIDDEN
|
||||
- OPTICS_TUI_WORKFLOW=REQUIRED
|
||||
- PROFILE_IMPORT_EXPORT=REQUIRED
|
||||
|
||||
These statements describe product intent only. They do not authorize implementation during this documentation audit.
|
||||
|
||||
# Documentation remediation order
|
||||
|
||||
## D1 — Current-state authority
|
||||
|
||||
Correct first:
|
||||
|
||||
- README.md
|
||||
- AGENTS.md
|
||||
- docs/roadmap/roadmap.md
|
||||
- docs/architecture/project_database.md
|
||||
- docs/architecture/persistence.md
|
||||
|
||||
Goal: one coherent current lifecycle and one explicit Project DB v25 head.
|
||||
|
||||
## D2 — Resource and runtime consistency
|
||||
|
||||
Then correct:
|
||||
|
||||
- docs/architecture/resource_boundary.md
|
||||
- docs/architecture/resource_aware_pipeline.md
|
||||
- docs/architecture/overview.md
|
||||
- docs/architecture/candidate_pair.md
|
||||
- docs/architecture/feature_store.md
|
||||
- docs/architecture/task_kind_registry.md
|
||||
- docs/architecture/task_system.md
|
||||
|
||||
Goal: current operational descriptions obey MAXIMUM SAFE USEFUL THROUGHPUT and SERIALISM_REQUIRES_PROOF without rewriting frozen science.
|
||||
|
||||
## D3 — Developer instructions
|
||||
|
||||
Then correct:
|
||||
|
||||
- docs/development/testing.md
|
||||
- docs/development/build.md
|
||||
- docs/development/concurrency.md
|
||||
|
||||
Goal: developer instructions match current APIs, build policy and validation discipline.
|
||||
|
||||
## D4 — Targeted scientific and current-state wording cleanup
|
||||
|
||||
Then inspect and correct only stale lifecycle wording in:
|
||||
|
||||
- docs/architecture/sparse_sfm.md
|
||||
- docs/architecture/tracks.md
|
||||
- docs/architecture/visual_index.md
|
||||
- docs/architecture/geometric_verification.md
|
||||
- docs/architecture/geometric_verifier.md
|
||||
- docs/architecture/runtime.md
|
||||
|
||||
This phase must not reopen FROZEN science.
|
||||
|
||||
## D5 — Documentation index and links
|
||||
|
||||
Finally reconcile README navigation, historical/current labels and cross-document anchors.
|
||||
|
||||
# Finding summary
|
||||
|
||||
BLOCKING_DOC = 7
|
||||
|
||||
IMPORTANT = 11
|
||||
|
||||
Highest-priority files:
|
||||
|
||||
1. docs/roadmap/roadmap.md
|
||||
2. docs/architecture/project_database.md
|
||||
3. docs/architecture/persistence.md
|
||||
4. docs/architecture/resource_aware_pipeline.md
|
||||
5. docs/development/testing.md
|
||||
6. docs/architecture/sparse_sfm.md
|
||||
7. README.md
|
||||
|
||||
# Audit boundary
|
||||
|
||||
This report does not authorize:
|
||||
|
||||
- scientific threshold changes;
|
||||
- new schema versions;
|
||||
- new Task kinds;
|
||||
- resource-policy redesign;
|
||||
- Viewer implementation;
|
||||
- live-capture implementation;
|
||||
- Sparse SfM execution;
|
||||
- Dense/MVS execution.
|
||||
|
||||
The next separate audit is SOURCE_COMMENT_AUDIT over include/lardon3d/** and src/**.
|
||||
|
||||
Source comments will later be classified as:
|
||||
|
||||
- EXCELLENT
|
||||
- GOOD
|
||||
- ACCEPTABLE
|
||||
- UNDER_COMMENTED
|
||||
- SEVERELY_UNDER_COMMENTED
|
||||
- STALE_COMMENT
|
||||
- MISLEADING_CONTRACT_COMMENT
|
||||
|
||||
The comment audit will focus on WHY, CONTRACT, INVARIANT, OWNERSHIP, IDENTITY, persistence ordering, restart, cancellation, concurrency, resource accounting and FROZEN boundaries.
|
||||
|
||||
No mass source-comment editing is authorized by this inventory.
|
||||
|
||||
# Remediation closure — 2026-09-02
|
||||
|
||||
The documentation finding-remediation pass is complete.
|
||||
|
||||
```text
|
||||
D1=PASS
|
||||
D2=PASS
|
||||
D3=PASS
|
||||
D4=PASS
|
||||
D5=PASS
|
||||
|
||||
BLOCKING_DOC_RESOLVED=7/7
|
||||
IMPORTANT_RESOLVED=11/11
|
||||
|
||||
AUTHORITY_FINDINGS_RESOLVED=3/3
|
||||
LINK_FINDINGS_RESOLVED=1/1
|
||||
```
|
||||
|
||||
D1 converged README, AGENTS, roadmap, Project DB and persistence on the current v25 lifecycle.
|
||||
|
||||
D2 reconciled resource/runtime documentation with sixteen production Task kinds,
|
||||
`MAXIMUM_SAFE_USEFUL_THROUGHPUT`, `SERIALISM_REQUIRES_PROOF`, the v24 RAW batch path and the v25
|
||||
Feature batch path.
|
||||
|
||||
D3 replaced stale developer API/build examples, removed fixed build-width policy and preserved the
|
||||
qualified sanitizer/TSan/Vulkan evidence boundaries.
|
||||
|
||||
D4 reconciled Sparse SfM lifecycle, Track verifier lineage, Visual Index downstream status, current
|
||||
Geometric Verifier v3 and runtime current-state wording without reopening frozen scientific contracts.
|
||||
|
||||
D5 adds `docs/README.md` as the navigation/authority map and removes the stale fifteen-kind
|
||||
cross-document anchor dependency.
|
||||
|
||||
The historical `global-maintenance-2026-09-01` checkpoint remains authoritative evidence for the
|
||||
unchanged boundaries it reviewed. The later `real-a6000-pre-sfm-2026-09-02` checkpoint adds real-data
|
||||
operational evidence and does not erase the maintenance checkpoint.
|
||||
|
||||
No source code, schema, Task Kind, scientific threshold, Sparse SfM execution, Dense/MVS execution,
|
||||
viewer or live-capture implementation is authorized by this closure.
|
||||
|
||||
## Language normalization boundary
|
||||
|
||||
Human authority selected English as the repository language.
|
||||
|
||||
The documents modified by the finding-remediation pass are English. Other untouched documentation may
|
||||
still contain historical or current non-English prose. That remaining language-only normalization is
|
||||
tracked separately and must not be mistaken for an unresolved current-state authority finding.
|
||||
|
||||
Translation of historical evidence must preserve the historical checkpoint exactly.
|
||||
|
||||
# Phase state
|
||||
|
||||
- DOCUMENTATION_INVENTORY_AUDIT=PASS_WITH_FINDINGS
|
||||
- DOCUMENTATION_FINDING_REMEDIATION=PASS
|
||||
- DOCUMENTATION_LANGUAGE_NORMALIZATION=IN_PROGRESS
|
||||
- SOURCE_COMMENT_AUDIT=PASS
|
||||
- SOURCE_COMMENT_REMEDIATION=PASS
|
||||
- PRODUCT_DEFINITION=PASS/FROZEN
|
||||
- PROMPT_TREE=NEXT
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
# Lardon3D — Source Comment Audit
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
SOURCE_COMMENT_AUDIT=PASS
|
||||
SOURCE_COMMENT_REMEDIATION=PASS
|
||||
SOURCE_COMMENT_SCOPE=include/lardon3d/** + src/**
|
||||
SOURCE_COMMENT_LANGUAGE=ENGLISH
|
||||
SOURCE_CODE_BEHAVIOR_CHANGED=NO
|
||||
```
|
||||
|
||||
The audit covered 148 C/C++ source/header files under `include/lardon3d/**` and `src/**`.
|
||||
|
||||
The initial static inventory reported:
|
||||
|
||||
```text
|
||||
comment blocks 807
|
||||
raw non-English lexical candidates 33
|
||||
actual French comment blocks 29
|
||||
English false positives 4
|
||||
stale-contract candidates 0
|
||||
raw unannotated-public declarations 324
|
||||
```
|
||||
|
||||
## Remediation
|
||||
|
||||
All 29 actual French source comments were translated to English.
|
||||
|
||||
The four raw language-detector false positives were already English and were left unchanged.
|
||||
|
||||
The remediation touched comments only. A file-level lexical comparison removed comments entirely and verified that every non-comment source
|
||||
byte remained identical before and after the declared replacements.
|
||||
|
||||
No executable UI/error string was changed in this pass. UI language is a separate explicitly scoped
|
||||
implementation pass.
|
||||
|
||||
## Stale and misleading comments
|
||||
|
||||
The lexical stale-contract pass found zero candidate comments.
|
||||
|
||||
No scientific threshold, schema version, Task Kind, resource contract, persistence identity or FROZEN
|
||||
boundary was changed by this audit.
|
||||
|
||||
## Public API adjacency heuristic
|
||||
|
||||
The first inventory reported 324 public declarations without an immediately adjacent comment.
|
||||
|
||||
That number is **informational only**, not 324 defects.
|
||||
|
||||
The repository Source Comment Contract requires comments where WHY, CONTRACT, INVARIANT, OWNERSHIP,
|
||||
IDENTITY, persistence ordering, restart, cancellation, concurrency, resource accounting or a FROZEN
|
||||
boundary is non-obvious. It does not require one redundant comment for every getter, thin wrapper,
|
||||
paired create/load/list function or declaration already covered by a surrounding contract block.
|
||||
|
||||
Therefore absence of an adjacent comment alone is not a valid
|
||||
`UNDER_COMMENTED`/`SEVERELY_UNDER_COMMENTED` finding.
|
||||
|
||||
Future audits must review semantic contract coverage rather than using declaration adjacency as a
|
||||
blanket failure criterion.
|
||||
|
||||
## Source Comment Contract
|
||||
|
||||
Production source comments:
|
||||
|
||||
- are English;
|
||||
- explain non-obvious WHY/CONTRACT/INVARIANT facts;
|
||||
- preserve ownership and lifetime boundaries;
|
||||
- preserve persistence/restart ordering;
|
||||
- preserve concurrency/resource-accounting boundaries;
|
||||
- preserve scientific identity and FROZEN constraints;
|
||||
- do not paraphrase obvious code line-by-line;
|
||||
- are updated when behavior changes.
|
||||
|
||||
## Validation
|
||||
|
||||
The remediation runner requires:
|
||||
|
||||
```text
|
||||
branch = docs-audit
|
||||
no unrelated worktree changes
|
||||
all expected old comments match exactly once
|
||||
file-level source with all comments removed remains byte-for-byte identical
|
||||
no strong French-comment candidate remains
|
||||
git diff --check passes
|
||||
```
|
||||
|
||||
## Phase state
|
||||
|
||||
```text
|
||||
DOCUMENTATION_FINDING_REMEDIATION=PASS
|
||||
DOCUMENTATION_LANGUAGE_NORMALIZATION=IN_PROGRESS
|
||||
|
||||
SOURCE_COMMENT_AUDIT=PASS
|
||||
SOURCE_COMMENT_REMEDIATION=PASS
|
||||
|
||||
PRODUCT_DEFINITION=PASS/FROZEN
|
||||
PROMPT_TREE=NEXT
|
||||
```
|
||||
|
|
@ -1,293 +1,193 @@
|
|||
# Build
|
||||
# Instructions de build
|
||||
|
||||
## Status
|
||||
## Prérequis
|
||||
|
||||
```text
|
||||
BUILD_SYSTEM=MESON_NINJA
|
||||
PUBLIC_API_LANGUAGE=C17
|
||||
IMPLEMENTATION_LANGUAGES=C17_CXX17
|
||||
BUILD_PARALLELISM=HOST_AWARE
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
```
|
||||
- **OS** : Linux (testé sur distributions récentes)
|
||||
- **Compilateur** : Clang (recommandé) ou GCC
|
||||
- **Système de build** : Meson + Ninja
|
||||
- **Dépendances principales** : ncursesw, SQLite, OpenSSL, GIO/GLib, OpenCV,
|
||||
LibRaw, libexif, libpng, libdeflate, Ceres ; Vulkan reste optionnel
|
||||
- **Langages** : API publiques C17 et implémentation mixte C17/C++17
|
||||
|
||||
Meson is the build-system authority. Do not duplicate dependency-version truth
|
||||
in this document when `meson.build` already enforces it.
|
||||
## Bootstrap des outils
|
||||
|
||||
## Requirements
|
||||
|
||||
Lardon3D targets Linux.
|
||||
|
||||
Primary toolchain:
|
||||
|
||||
```text
|
||||
Clang or GCC
|
||||
Meson
|
||||
Ninja
|
||||
pkg-config
|
||||
```
|
||||
|
||||
Major dependencies currently include ncursesw, SQLite, OpenSSL, GIO/GLib,
|
||||
OpenCV, LibRaw, libexif, libpng, libdeflate and Ceres. Vulkan remains optional
|
||||
at configuration level.
|
||||
|
||||
Public APIs are C17. Implementation is mixed C17/C++17.
|
||||
|
||||
## Bootstrap examples
|
||||
|
||||
These commands install only the basic compiler/build front end; Meson remains
|
||||
authoritative for the complete dependency set.
|
||||
|
||||
Debian/Ubuntu:
|
||||
Les commandes ci-dessous installent seulement le compilateur, Meson/Ninja,
|
||||
`pkg-config` et ncurses. Les bibliothèques listées plus haut doivent aussi être
|
||||
disponibles dans les versions acceptées par `meson.build`; Meson reste la source
|
||||
de vérité et refuse explicitement une dépendance absente ou incompatible.
|
||||
|
||||
```sh
|
||||
# Debian / Ubuntu
|
||||
sudo apt install clang meson ninja-build libncursesw5-dev pkg-config
|
||||
```
|
||||
|
||||
Fedora:
|
||||
|
||||
```sh
|
||||
# Fedora
|
||||
sudo dnf install clang meson ninja-build ncurses-devel pkg-config
|
||||
```
|
||||
|
||||
Arch Linux:
|
||||
|
||||
```sh
|
||||
# Arch
|
||||
sudo pacman -S clang meson ninja ncurses pkgconf
|
||||
```
|
||||
|
||||
## Standard build
|
||||
|
||||
First configuration:
|
||||
## Build standard
|
||||
|
||||
```sh
|
||||
CC=clang CXX=clang++ meson setup build
|
||||
```
|
||||
# Première configuration
|
||||
CC=clang meson setup build
|
||||
|
||||
Existing tree:
|
||||
|
||||
```sh
|
||||
# Arbre existant
|
||||
meson setup --reconfigure build
|
||||
meson compile -C build
|
||||
meson compile -C build -j8
|
||||
```
|
||||
|
||||
Do not hard-code `-j8` as project policy.
|
||||
|
||||
Meson/Ninja should use host-appropriate parallelism unless a specific
|
||||
validation has a reason to constrain it.
|
||||
|
||||
## Build parallelism policy
|
||||
|
||||
The build is not governed by a portable fixed job count.
|
||||
|
||||
Canonical policy:
|
||||
|
||||
```text
|
||||
preserve the interactive host reserve
|
||||
then use maximum safe useful throughput
|
||||
```
|
||||
|
||||
A reference host measurement such as 8 or 12 useful jobs is evidence for that
|
||||
host at that time, not a repository constant.
|
||||
|
||||
If memory-heavy compilation or another active workload creates pressure,
|
||||
reduce build width for that run. Do not convert the temporary reduction into a
|
||||
global documentation rule.
|
||||
|
||||
## Reconfigure versus wipe
|
||||
|
||||
Prefer incremental reuse:
|
||||
### Options utiles
|
||||
|
||||
```sh
|
||||
meson setup --reconfigure build
|
||||
meson compile -C build
|
||||
# Build de debug (défaut)
|
||||
meson setup build --wipe
|
||||
|
||||
# Build de release
|
||||
meson setup build --wipe --buildtype=release
|
||||
|
||||
# Build avec optimisations aggressive
|
||||
meson setup build --wipe --buildtype=release -Db_lto=true
|
||||
```
|
||||
|
||||
Use `--wipe` only when a fresh configuration is actually required, such as:
|
||||
|
||||
- switching sanitizer configuration in the same directory;
|
||||
- changing compiler family;
|
||||
- changing a configuration whose cached state cannot be reused safely;
|
||||
- reproducing a clean release/global-maintenance proof;
|
||||
- recovering from a stale or corrupt build directory.
|
||||
|
||||
A normal edit/test loop should not wipe the build tree repeatedly.
|
||||
|
||||
## Release build
|
||||
|
||||
Use an explicit release directory or deliberate reconfiguration.
|
||||
|
||||
Example:
|
||||
|
||||
```sh
|
||||
CC=clang CXX=clang++ meson setup build-release --buildtype=release
|
||||
meson compile -C build-release
|
||||
```
|
||||
|
||||
For LTO:
|
||||
|
||||
```sh
|
||||
CC=clang CXX=clang++ meson setup build-release-lto --buildtype=release -Db_lto=true
|
||||
meson compile -C build-release-lto
|
||||
```
|
||||
|
||||
Separate directories avoid destroying a useful incremental debug tree.
|
||||
|
||||
## Vulkan configuration
|
||||
|
||||
Portable CPU-only proof:
|
||||
|
||||
```sh
|
||||
CC=clang CXX=clang++ meson setup build-portable -Dvulkan_orb=disabled
|
||||
meson compile -C build-portable
|
||||
```
|
||||
|
||||
Vulkan-enabled proof:
|
||||
|
||||
```sh
|
||||
CC=clang CXX=clang++ meson setup build-vulkan -Dvulkan_orb=enabled
|
||||
meson compile -C build-vulkan
|
||||
```
|
||||
|
||||
A Vulkan-on build is not automatically a proof that every scientific path uses
|
||||
or should use the GPU.
|
||||
|
||||
Current production GPU promotion remains limited by each subsystem's validated
|
||||
backend contract.
|
||||
|
||||
## Validation
|
||||
|
||||
Normal configured tests:
|
||||
|
||||
```sh
|
||||
# Tests unitaires
|
||||
meson test -C build --print-errorlogs
|
||||
```
|
||||
|
||||
Whitespace/style boundary:
|
||||
|
||||
```sh
|
||||
# Vérification du style (whitespace)
|
||||
git diff --check
|
||||
|
||||
# Vérification autonome d'un header C public modifié
|
||||
cc -x c -std=c17 -fsyntax-only -Iinclude \
|
||||
-include lardon3d/<header>.h /dev/null
|
||||
```
|
||||
|
||||
Public C header probe:
|
||||
### Preuve fraîche de maintenance globale — 1er septembre 2026
|
||||
|
||||
Le [registre canonique](../architecture/global_maintenance_audit.md) conserve
|
||||
le détail et les qualifications. Les résultats reproductibles acquis sont :
|
||||
|
||||
| Configuration fraîche | Compilateurs/options | Build | Suite |
|
||||
| --- | --- | ---: | ---: |
|
||||
| portable | Clang/Clang++ 22.1.8, C17/C++17, `-Dvulkan_orb=disabled` | 931/931 | 64/64 sériel |
|
||||
| Vulkan | Clang/Clang++ 22.1.8, C17/C++17, `-Dvulkan_orb=enabled` | 939/939 | 65/65 sériel |
|
||||
| ASan/UBSan portable | Clang/Clang++ 22.1.8, `address,undefined` | graphe complet | 64/64 avec LSan désactivé après attribution externe |
|
||||
| TSan portable | GCC/G++ 16.2.1, Vulkan désactivé | cibles concurrentes | 14/14 + 220 répétitions |
|
||||
|
||||
La suite Vulkan comprend `orb-vulkan-backend` sur la Radeon 780M RADV PHOENIX
|
||||
réelle. La cible de feasibility SIFT/RootSIFT, non enregistrée dans la suite,
|
||||
a été compilée/exécutée séparément : zéro divergence de décision Lowe mais des
|
||||
divergences d'index et de bits de distance, donc aucune promotion en backend
|
||||
production. Les probes stricts GCC/Clang C17+C++17 passent 76/76 sur les
|
||||
19 headers publics modifiés ou nouveaux, ainsi que le fixture ABI, le lien
|
||||
application et `git diff --check`; `scan3d/` reste intact.
|
||||
|
||||
L'unique revue finale indépendante GPT-5.6 SOL/ULTRA a conclu PASS sans finding
|
||||
bloquant. Elle a indépendamment rejoué le build portable, la suite complète
|
||||
64/64, une matrice focalisée 15/15, les 76/76 probes de headers, l'ABI, les
|
||||
négatifs de seams production, le SHA-256 du manifest GV retenu et le diff-check.
|
||||
Le statut canonique est donc `GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN` ; les
|
||||
qualifications sanitizer ci-dessous restent néanmoins partie de la preuve.
|
||||
|
||||
Une validation post-freeze a ensuite attribué le délai intermittent de
|
||||
`test-feature-task` à la capture de la télémétrie hôte réelle par ses Governors
|
||||
synthétiques. Le fixture utilise maintenant un `ResourceSnapshot` complet,
|
||||
privé, par Governor et compilé pour cette seule cible ; production continue de
|
||||
lire la télémétrie réelle et n'exporte aucun seam. Après correction du second
|
||||
Governor relevé en revue, Feature passe 100/100, la matrice ordonnée 4/4 et les
|
||||
suites finales portable/Vulkan 64/64 et 65/65 ; ASan/UBSan ciblé avec
|
||||
`detect_leaks=0` et TSan passent. Le registre canonique conserve la régression
|
||||
charge 5 `WAIT`/charge 0 `START` et la qualification exacte. Un timeout `task`
|
||||
isolé dans une suite normale mixte après reconstruction large reste
|
||||
non reproductible : le ciblé immédiat et sa matrice de revue 100/100 passent,
|
||||
sans modification de Task ni de son timeout.
|
||||
|
||||
La première suite LSan complète est volontairement conservée comme non-PASS :
|
||||
57 OK, 6 FAIL et 1 timeout. Cinq échecs partagent exactement la fuite externe
|
||||
OpenCL de 3 808 octets/68 allocations ; les deux anomalies de 30 s n'ont aucun
|
||||
diagnostic sanitizer. Le délai Feature, absent du suivi initial, a ensuite été
|
||||
reproduit et corrigé comme décrit ci-dessus ; le délai Task reste non
|
||||
reproductible. La suite entière passe 64/64 avec ASan/UBSan actifs et
|
||||
`detect_leaks=0`, tandis qu'un sous-ensemble prouvé sans loader OpenCV/OpenCL
|
||||
passe 20/20 avec LSan actif. Il est donc incorrect de résumer cette preuve par
|
||||
« LSan 64/64 ».
|
||||
|
||||
Le log Clang complet a aussi été audité. La conversion publique Sparse SfM
|
||||
`uint32_t → int` était matérielle et a été corrigée avec validation ciblée ; les
|
||||
autres émissions sont soit des conversions baseline déjà bornées, soit des
|
||||
tests/benchmarks, soit des headers OpenCV/Ceres externes. Les emplacements et
|
||||
justifications exacts restent centralisés dans le registre afin de ne pas
|
||||
dupliquer une seconde liste normative ici.
|
||||
|
||||
## Build ASan/UBSan (debug mémoire)
|
||||
|
||||
À exécuter pour tout ticket touchant la mémoire, les durées de vie ou les
|
||||
allocations :
|
||||
|
||||
```sh
|
||||
cc -x c -std=c17 -fsyntax-only -Iinclude -include lardon3d/<header>.h /dev/null
|
||||
```
|
||||
|
||||
Use `docs/development/testing.md` for sanitizer and validation policy.
|
||||
|
||||
## ASan / UBSan build
|
||||
|
||||
Example dedicated directory:
|
||||
|
||||
```sh
|
||||
CC=clang CXX=clang++ meson setup build-asan -Db_sanitize=address,undefined
|
||||
meson compile -C build-asan
|
||||
CC=clang meson setup build-asan --wipe \
|
||||
-Db_sanitize=address,undefined
|
||||
meson compile -C build-asan -j8
|
||||
meson test -C build-asan --print-errorlogs
|
||||
```
|
||||
|
||||
Do not claim an unqualified full LeakSanitizer pass from the retained global
|
||||
maintenance checkpoint. The external OpenCL loader qualification documented in
|
||||
the canonical audit remains part of that evidence.
|
||||
## Build TSan (concurrence)
|
||||
|
||||
## TSan build
|
||||
|
||||
Use TSan only with the configuration that matches the intended proof.
|
||||
|
||||
The retained global maintenance concurrency proof used GCC/G++ with Vulkan
|
||||
disabled, because the project TSan matrix and the Vulkan runtime validation are
|
||||
separate evidence boundaries.
|
||||
|
||||
Example:
|
||||
À exécuter pour tout ticket touchant la concurrence (pthread, mutex,
|
||||
variables de condition, états partagés) :
|
||||
|
||||
```sh
|
||||
CC=gcc CXX=g++ meson setup build-tsan -Db_sanitize=thread -Db_lundef=false -Dvulkan_orb=disabled
|
||||
meson compile -C build-tsan
|
||||
CC=clang meson setup build-tsan --wipe \
|
||||
-Db_sanitize=thread \
|
||||
-Db_lundef=false
|
||||
meson compile -C build-tsan -j8
|
||||
meson test -C build-tsan --print-errorlogs
|
||||
```
|
||||
|
||||
The exact target subset, suppression qualification and repetition evidence are
|
||||
documented in `docs/development/concurrency.md` and the global maintenance
|
||||
audit.
|
||||
## Variables d'environnement
|
||||
|
||||
## Current retained maintenance checkpoint
|
||||
| Variable | Description |
|
||||
|---|---|
|
||||
| `CC` | Compilateur C (défaut : gcc) |
|
||||
| `CFLAGS` | Drapeaux de compilation supplémentaires |
|
||||
| `LDFLAGS` | Drapeaux de liaison supplémentaires |
|
||||
|
||||
The canonical detailed evidence is:
|
||||
|
||||
```text
|
||||
docs/architecture/global_maintenance_audit.md
|
||||
GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN
|
||||
```
|
||||
|
||||
The 2026-09-01 checkpoint retained:
|
||||
|
||||
```text
|
||||
portable Clang/Clang++ build and suite
|
||||
Vulkan-on Clang/Clang++ build and suite
|
||||
ASan/UBSan qualified run
|
||||
portable GCC/G++ TSan matrix
|
||||
public-header C17/C++17 probes
|
||||
ABI and application-link checks
|
||||
independent review
|
||||
```
|
||||
|
||||
Those exact historical counts belong to the audit and should not be duplicated
|
||||
as a new current build contract.
|
||||
|
||||
## Build directory layout
|
||||
|
||||
A configured Meson tree typically contains:
|
||||
## Structure du build
|
||||
|
||||
```text
|
||||
build/
|
||||
src/
|
||||
tests/
|
||||
compile_commands.json
|
||||
├── src/ # objets et binaires
|
||||
├── tests/ # binaires de tests
|
||||
└── compile_commands.json # pour LSP / clangd
|
||||
```
|
||||
|
||||
Exact generated layout is Meson/Ninja output and may evolve.
|
||||
## Dépannage
|
||||
|
||||
## Environment
|
||||
|
||||
Common variables include:
|
||||
|
||||
| Variable | Purpose |
|
||||
| --- | --- |
|
||||
| `CC` | C compiler |
|
||||
| `CXX` | C++ compiler |
|
||||
| `CFLAGS` | additional C flags |
|
||||
| `CXXFLAGS` | additional C++ flags |
|
||||
| `LDFLAGS` | additional linker flags |
|
||||
|
||||
Prefer Meson options for project features rather than ad-hoc environment flags
|
||||
that make builds difficult to reproduce.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Check ncursesw discovery:
|
||||
### Erreur : ncursesw introuvable
|
||||
|
||||
```sh
|
||||
# Vérifier l'installation
|
||||
pkg-config --libs ncursesw
|
||||
# Si absent, installer le paquet de développement ncursesw
|
||||
```
|
||||
|
||||
If Clang is unavailable, GCC is supported where the current Meson checks allow
|
||||
it.
|
||||
### Erreur : clang introuvable
|
||||
|
||||
For a slow build, first preserve the existing build tree and let Ninja use
|
||||
normal host-aware scheduling. Reduce concurrency only when actual host pressure
|
||||
or another active workload justifies it.
|
||||
|
||||
`ccache` may be used when available, but it is optional operational tooling and
|
||||
not part of scientific identity.
|
||||
|
||||
## Rules
|
||||
|
||||
```text
|
||||
NO_FIXED_GLOBAL_J8=YES
|
||||
NO_REPEATED_UNCHANGED_WIPE=YES
|
||||
HOST_AWARE_BUILD_PARALLELISM=YES
|
||||
```sh
|
||||
# Utiliser gcc en alternative
|
||||
meson setup build --wipe
|
||||
# ou installer clang
|
||||
sudo apt install clang
|
||||
```
|
||||
|
||||
Build configuration is operational state. It must not silently redefine
|
||||
scientific formats, fingerprints or persistence contracts.
|
||||
### Build lent
|
||||
|
||||
```sh
|
||||
# Réduire la parallélisation
|
||||
meson compile -C build -j4
|
||||
# ou utiliser ccache
|
||||
CC="ccache clang" meson setup build --wipe
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,412 +1,255 @@
|
|||
# Concurrency
|
||||
# Règles de concurrence
|
||||
|
||||
## Status
|
||||
## Vue d'ensemble
|
||||
|
||||
Lardon3D utilise un modèle de concurrence à thread unique pour ncurses
|
||||
et un modèle multi-thread pour le traitement. La séparation est stricte :
|
||||
le thread ncurses ne fait jamais de travail métier, et les workers ne
|
||||
touchent jamais ncurses.
|
||||
|
||||
## Modèle de concurrence
|
||||
|
||||
```text
|
||||
NCURSES_OWNER=MAIN_THREAD_ONLY
|
||||
ACTIVE_HEAVY_QUEUE_CALLBACKS=1
|
||||
TASK_CANCELLATION=COOPERATIVE
|
||||
INTERNAL_PARALLELISM=BOUNDED
|
||||
OWNER_ONLY_PUBLICATION=CANONICAL_WHERE_REQUIRED
|
||||
Thread principal (ncurses)
|
||||
├── Gestion des entrées
|
||||
├── Affichage TUI
|
||||
└── Orchestration
|
||||
|
||||
TSAN_PORTABLE_PROJECT_MATRIX=QUALIFIED_PASS
|
||||
TSAN_EXTERNAL_OPENCV_TBB=QUALIFIED
|
||||
VULKAN_CONCURRENCY_VALIDATION=SEPARATE
|
||||
Worker thread
|
||||
├── Exécution des tâches
|
||||
├── Calculs métier
|
||||
└── Écritures de résultats
|
||||
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
SSD operation thread (0 ou 1, joinable)
|
||||
└── Un poll ou contrôle UDisks synchrone borné, sans ncurses ni Task
|
||||
```
|
||||
|
||||
Lardon3D separates UI ownership from heavy processing.
|
||||
## Règles fondamentales
|
||||
|
||||
The main thread owns ncurses. The Task Queue owns one active heavy callback.
|
||||
Individual validated Task Kinds may create bounded internal participants inside
|
||||
that callback.
|
||||
### 1. ncurses appartient au thread principal
|
||||
|
||||
An SSD controller operation may also use at most one bounded joinable operation
|
||||
thread under its own ownership contract.
|
||||
```c
|
||||
// ✅ Correct : appel depuis le thread principal
|
||||
mvprintw(0, 0, "Progression: %d%%", progress);
|
||||
|
||||
## Execution model
|
||||
|
||||
```text
|
||||
main thread
|
||||
input
|
||||
ncurses
|
||||
TUI orchestration
|
||||
|
||||
Task Queue worker
|
||||
one active heavy callback
|
||||
admitted Task sequence
|
||||
optional bounded internal participants
|
||||
deterministic owner publication
|
||||
|
||||
SSD operation thread
|
||||
zero or one bounded joinable controller operation
|
||||
no ncurses
|
||||
no Task callback
|
||||
// ❌ Interdit : appel depuis un worker
|
||||
// mvprintw() dans un thread secondaire
|
||||
```
|
||||
|
||||
Internal participants are not a second global scheduler or Queue.
|
||||
### 2. Variables partagées protégées par mutex
|
||||
|
||||
## Fundamental rules
|
||||
```c
|
||||
// ✅ Correct
|
||||
pthread_mutex_lock(&queue->mutex);
|
||||
queue->count++;
|
||||
pthread_mutex_unlock(&queue->mutex);
|
||||
|
||||
### ncurses ownership
|
||||
|
||||
Only the main thread calls ncurses.
|
||||
|
||||
Workers publish observable state through protected data. They never call
|
||||
`mvprintw`, `wrefresh`, or other ncurses APIs.
|
||||
|
||||
### Shared mutable state
|
||||
|
||||
Shared mutable state must have an explicit synchronization owner:
|
||||
|
||||
```text
|
||||
mutex
|
||||
condition variable
|
||||
atomic primitive where the contract explicitly permits it
|
||||
single-thread ownership
|
||||
immutable-after-publication
|
||||
// ❌ Interdit
|
||||
// queue->count++; sans protection
|
||||
```
|
||||
|
||||
Do not rely on timing or "normally only one caller".
|
||||
### 3. Variables de condition pour la synchronisation
|
||||
|
||||
### Condition variables
|
||||
```c
|
||||
// Producteur (caller de la Task Queue)
|
||||
pthread_mutex_lock(&queue->mutex);
|
||||
queue->ready = true;
|
||||
pthread_cond_signal(&queue->cond);
|
||||
pthread_mutex_unlock(&queue->mutex);
|
||||
|
||||
Always test the predicate in a loop around `pthread_cond_wait()`.
|
||||
|
||||
A signal is not durable state; the protected predicate is.
|
||||
|
||||
### Cooperative cancellation
|
||||
|
||||
Production Task cancellation is cooperative.
|
||||
|
||||
Do not use `pthread_cancel()` to stop a Task.
|
||||
|
||||
Task-specific non-preemptible operations finish their current atomic boundary
|
||||
before pause/cancel is observed.
|
||||
|
||||
### Reservation before callback
|
||||
|
||||
No Task callback runs without the Resource Governor admission/reservation
|
||||
required by its installed sequence contract.
|
||||
|
||||
Fixed-resource Tasks do not bypass the Governor.
|
||||
|
||||
### Terminal lifetime
|
||||
|
||||
Terminal callback completion precedes destruction of Task userdata.
|
||||
|
||||
Queue/Task ownership must ensure no observer dereferences freed userdata.
|
||||
|
||||
## Lock ordering
|
||||
|
||||
When multiple locks are required, the owning subsystem must define and preserve
|
||||
one order.
|
||||
|
||||
Never add a reverse-order path to solve a local problem.
|
||||
|
||||
Avoid holding one subsystem mutex while calling into another subsystem that may
|
||||
call back.
|
||||
|
||||
Where practical:
|
||||
|
||||
```text
|
||||
copy bounded state under lock
|
||||
release lock
|
||||
perform I/O / expensive work
|
||||
reacquire only for publication
|
||||
// Consommateur (worker)
|
||||
pthread_mutex_lock(&queue->mutex);
|
||||
while (!queue->ready) {
|
||||
pthread_cond_wait(&queue->cond, &queue->mutex);
|
||||
}
|
||||
// traitement
|
||||
pthread_mutex_unlock(&queue->mutex);
|
||||
```
|
||||
|
||||
## Queue ingress lifetime
|
||||
### 4. Pas de callback ncurses depuis un worker
|
||||
|
||||
The Queue owner closes ingress before destruction.
|
||||
```c
|
||||
// ✅ Correct : le worker signale au thread principal
|
||||
void worker_callback(task_t *task, void *userdata) {
|
||||
shared_state_t *state = userdata;
|
||||
pthread_mutex_lock(&state->mutex);
|
||||
state->result_ready = true;
|
||||
pthread_cond_signal(&state->cond);
|
||||
pthread_mutex_unlock(&state->mutex);
|
||||
}
|
||||
|
||||
Shutdown waits for:
|
||||
|
||||
- active worker completion;
|
||||
- registered in-flight API calls covered by the ownership contract;
|
||||
- terminal callbacks.
|
||||
|
||||
This cannot make a raw C pointer safe if a caller begins a new call after the
|
||||
object has already been freed. Callers must obey lifetime ownership.
|
||||
|
||||
## Bounded internal parallelism
|
||||
|
||||
A validated Task Kind may use internal participants while the Queue callback
|
||||
remains the sole Task owner.
|
||||
|
||||
Required shape:
|
||||
|
||||
```text
|
||||
one admitted Task owner
|
||||
-> bounded participant count
|
||||
-> bounded private work
|
||||
-> join all participants
|
||||
-> owner-only deterministic publication when required
|
||||
-> Task-specific durable cursor
|
||||
-> generic checkpoint
|
||||
-> sequence_break
|
||||
// ❌ Interdit : appel ncurses depuis le worker
|
||||
// void worker_callback(...) {
|
||||
// mvprintw(...);
|
||||
// }
|
||||
```
|
||||
|
||||
Participant count and memory must fit the admitted Resource Governor contract.
|
||||
## Primitives utilisées
|
||||
|
||||
No participant may silently exceed the installed sequence contract.
|
||||
| Primitive | Usage |
|
||||
|---|---|
|
||||
| `pthread_mutex_t` | Protection des données partagées |
|
||||
| `pthread_cond_t` | Synchronisation producteur/consommateur |
|
||||
| `pthread_create()` | Création des workers |
|
||||
| `pthread_join()` | Attente de fin des workers |
|
||||
| `pthread_cancel()` | Non utilisé pour interrompre une Task ; annulation coopérative |
|
||||
|
||||
## Atomicity does not imply serialism
|
||||
## Invariants de concurrence
|
||||
|
||||
Per-item scientific atomicity and cross-item execution width are separate.
|
||||
1. **Un seul thread ncurses** : ncurses n'est jamais appelé depuis un
|
||||
worker. Toute mise à jour de l'UI passe par des variables partagées
|
||||
protégées.
|
||||
|
||||
```text
|
||||
PER_ITEM_ATOMICITY_REQUIRES_CROSS_ITEM_SERIALISM=NO
|
||||
OWNER_ONLY_PUBLICATION_REQUIRES_SERIAL_PREPARATION=NO
|
||||
2. **Mutex hiérarchique** : si plusieurs mutex sont acquis, toujours dans
|
||||
le même ordre pour éviter les deadlocks.
|
||||
|
||||
3. **Annulation coopérative** : les workers vérifient périodiquement un
|
||||
drapeau d'annulation. Une Task n'est pas interrompue brutalement.
|
||||
|
||||
4. **Réservation atomique** : la réservation du gouverneur est atomique.
|
||||
Deux threads ne peuvent pas obtenir la même réservation.
|
||||
|
||||
5. **Pas de callback sans réservation** : aucun callback de tâche n'est
|
||||
invoqué sans réservation active. Cet invariant est maintenu même en
|
||||
présence d'erreurs.
|
||||
|
||||
6. **Retraite après callback** : la notification terminale finit avant la
|
||||
destruction du userdata. Queue détruit la Task hors de son mutex et ne
|
||||
conserve ensuite qu'un snapshot borné.
|
||||
|
||||
7. **Fermeture d'ingress** : le propriétaire empêche les nouveaux appels Queue
|
||||
avant `destroy()`. La fermeture interne attend le worker et chaque appel
|
||||
enregistré avant le close ; elle ne peut rendre sûr un appel démarré après
|
||||
la libération d'un pointeur C brut.
|
||||
|
||||
8. **Parallélisme scientifique propriétaire** : lorsqu'un kind emploie des
|
||||
participants internes, le callback Queue demeure l'unique propriétaire. Le
|
||||
nombre de participants et leur mémoire sont admis par le Governor ; seul le
|
||||
propriétaire publie le préfixe durable ordonné et joint tous les enfants.
|
||||
|
||||
9. **Lease SSD par objet** : un lease scratch appartient à l'adresse exacte de
|
||||
l'objet fourni par le caller. Tous ses champs sont lus/écrits sous le mutex
|
||||
du contrôleur. Le caller lui garantit un accès exclusif et ne le copie, ne le
|
||||
déplace ni ne le présente simultanément à deux contrôleurs. En production,
|
||||
acquire/release passent par les wrappers Governor ; le Governor relâche son
|
||||
mutex avant l'appel contrôleur, et le contrôleur ne rappelle jamais le
|
||||
Governor. À la saturation légale `generation == UINT64_MAX`, seule la fin
|
||||
du wrapper exact déjà sérialisé peut réconcilier sa propre opération et le
|
||||
compte fondé sur les adresses ; une update publique au même watermark ne
|
||||
peut pas rendre une autorité stale.
|
||||
|
||||
10. **Owner SSD unique** : la TUI/main demande et poll l'opération ; au plus un
|
||||
thread joinable exécute une opération bornée et ne touche jamais ncurses.
|
||||
Le destroy le joint avant unregister. Une observation malformée enregistre
|
||||
`ERROR` et ne confère aucune autorité de contrôle ou de lease.
|
||||
|
||||
11. **Frontière projet** : les vues libèrent leurs borrows, puis la Queue est
|
||||
annulée/jointe/détruite avant Project DB. Une Queue vide est créée ensuite.
|
||||
Aucun callback terminal ne peut donc déréférencer une DB déjà fermée et
|
||||
l'histoire d'un projet ne fuit pas dans le suivant.
|
||||
|
||||
12. **Ordre d'arrêt global** : Queue et leases Task, puis fermeture projet,
|
||||
join/unregister du binding SSD, contrôleur SSD, et enfin Governor. Un
|
||||
unregister encore bloqué par un lease est un échec observable, jamais un
|
||||
pointeur abandonné.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
### Deadlock
|
||||
|
||||
```c
|
||||
// ❌ Risque de deadlock
|
||||
pthread_mutex_lock(&mutex_a);
|
||||
pthread_mutex_lock(&mutex_b); // attend mutex_b
|
||||
|
||||
// Dans un autre thread :
|
||||
pthread_mutex_lock(&mutex_b);
|
||||
pthread_mutex_lock(&mutex_a); // attend mutex_a → DEADLOCK
|
||||
```
|
||||
|
||||
Current examples include selected RAW, selected Feature extraction, Candidate
|
||||
Pair source work and outer Geometric Verification preparation.
|
||||
**Solution** : toujours acquérir les mutex dans le même ordre.
|
||||
|
||||
Serialization is valid only where the subsystem's scientific, persistence,
|
||||
library or measured-throughput contract proves it necessary.
|
||||
### Race condition
|
||||
|
||||
## CPU/batch coupling
|
||||
```c
|
||||
// ❌ Race condition
|
||||
if (task->state == TASK_STATE_QUEUED) {
|
||||
task->state = TASK_STATE_RUNNING;
|
||||
}
|
||||
|
||||
CPU and batch/window are not globally independent dimensions.
|
||||
|
||||
For a Task whose additional participants cannot do useful work while the
|
||||
admitted item window remains one, a Task-specific capability may couple those
|
||||
dimensions.
|
||||
|
||||
Current validated examples include:
|
||||
|
||||
```text
|
||||
candidate_pair.generate/1
|
||||
features.extract.batch/1
|
||||
// ✅ Correct
|
||||
pthread_mutex_lock(&task->mutex);
|
||||
if (task->state == TASK_STATE_QUEUED) {
|
||||
task->state = TASK_STATE_RUNNING;
|
||||
}
|
||||
pthread_mutex_unlock(&task->mutex);
|
||||
```
|
||||
|
||||
This is not a universal rule for all Task Kinds.
|
||||
### Use-after-free
|
||||
|
||||
## Project lifetime boundary
|
||||
```c
|
||||
// ❌ Use-after-free
|
||||
task_destroy(task);
|
||||
task_callback(task); // task est libéré
|
||||
|
||||
Before closing a project:
|
||||
|
||||
```text
|
||||
views release Project DB borrows
|
||||
-> Queue is cancelled/joined/destroyed
|
||||
-> Project DB closes
|
||||
-> fresh empty Queue may be created for the next project
|
||||
// ✅ Correct : le callback est entièrement revenu avant la destruction
|
||||
task_callback(task);
|
||||
task_destroy(task);
|
||||
```
|
||||
|
||||
A terminal callback must never observe a Project DB already destroyed.
|
||||
## Validation
|
||||
|
||||
Project-specific runtime history must not leak into the next project.
|
||||
Les readers Visual Index sont sans état partagé mutable. Une query copie la
|
||||
liste bornée des segments sous le mutex DB, puis effectue hash, lectures et
|
||||
accumulation après déverrouillage. Un update ne rend le nouveau segment visible
|
||||
qu'au commit memberships+segment ; une query en cours garde son snapshot.
|
||||
|
||||
## Global shutdown boundary
|
||||
|
||||
The current shutdown order preserves ownership across:
|
||||
|
||||
```text
|
||||
Task Queue and Task leases
|
||||
-> project close
|
||||
-> join/unregister SSD binding
|
||||
-> SSD controller
|
||||
-> Resource Governor
|
||||
```
|
||||
|
||||
A scratch unregister blocked by a real outstanding lease is an observable
|
||||
failure, not permission to abandon a live pointer.
|
||||
|
||||
## SSD lease ownership
|
||||
|
||||
A scratch lease belongs to the exact caller-owned lease object used for the
|
||||
operation.
|
||||
|
||||
Its mutable fields are controlled under the SSD controller mutex.
|
||||
|
||||
The caller must not:
|
||||
|
||||
- copy a live lease;
|
||||
- move a live lease;
|
||||
- present the same lease object to two controllers;
|
||||
- release through a different ownership path.
|
||||
|
||||
Production acquire/release uses the Resource Governor wrappers.
|
||||
|
||||
The Governor releases its own mutex before entering the controller, and the
|
||||
controller does not callback into the Governor while holding its mutex.
|
||||
|
||||
Scratch remains storage capacity, never RAM admission.
|
||||
|
||||
## Visual Index readers
|
||||
|
||||
Visual Index query readers do not share mutable query state.
|
||||
|
||||
A query obtains a bounded segment snapshot under the Project DB boundary, then
|
||||
performs hash/read/accumulation after release.
|
||||
|
||||
A concurrent update makes a new segment visible only at its canonical commit
|
||||
boundary. An already running query continues with its retained snapshot.
|
||||
|
||||
## OpenCV process-wide state
|
||||
|
||||
OpenCV thread configuration is process-wide.
|
||||
|
||||
The active heavy Queue callback owns temporary mutation of that setting where a
|
||||
Task contract requires it and restores the previous/baseline value on all exit
|
||||
paths.
|
||||
|
||||
Internal participants must not independently race `cv::setNumThreads()`.
|
||||
|
||||
For Feature batch, cross-image participants are used while internal OpenCV
|
||||
threading is controlled explicitly.
|
||||
|
||||
## Vulkan boundary
|
||||
|
||||
ORB Vulkan concurrency is validated separately from portable TSan.
|
||||
|
||||
The production AUTO contract currently uses:
|
||||
|
||||
```text
|
||||
normal inflight depth = 1
|
||||
private validated safety depth = 2
|
||||
helpers = 0
|
||||
```
|
||||
|
||||
Depth 2 is a private safety/benchmark capability and was rejected as the normal
|
||||
useful setting by measured throughput.
|
||||
|
||||
A Vulkan backend failure produces complete CPU fallback before publication.
|
||||
Partial GPU scientific output is never published.
|
||||
|
||||
## TSan policy
|
||||
|
||||
For project concurrency changes, use a dedicated TSan build that matches the
|
||||
supported proof boundary.
|
||||
|
||||
The retained global maintenance matrix used:
|
||||
|
||||
```text
|
||||
GCC/G++
|
||||
Vulkan disabled
|
||||
selected concurrent targets
|
||||
deterministic repetitions
|
||||
```
|
||||
|
||||
and completed the retained 14/14 target matrix plus 220 repetitions.
|
||||
|
||||
The narrow suppression file is:
|
||||
|
||||
```text
|
||||
tests/tsan-opencv.supp
|
||||
```
|
||||
|
||||
It covers external non-instrumented OpenCV/TBB objects only.
|
||||
|
||||
It must not suppress Lardon3D frames.
|
||||
|
||||
Therefore the correct retained claim is not "TSan proves all concurrency".
|
||||
It is:
|
||||
|
||||
```text
|
||||
portable project concurrency matrix passed under the documented qualification
|
||||
external OpenCV/TBB reports are narrowly qualified
|
||||
Vulkan concurrency has a separate validation boundary
|
||||
```
|
||||
|
||||
## What TSan does not prove
|
||||
|
||||
TSan is useful for instrumented conflicting memory access and some
|
||||
synchronization misuse.
|
||||
|
||||
It does not prove absence of:
|
||||
|
||||
- deadlock;
|
||||
- lost wakeup caused by incorrect predicate design;
|
||||
- lifetime bugs outside the exercised paths;
|
||||
- races hidden inside non-instrumented external libraries;
|
||||
- Vulkan driver/runtime correctness;
|
||||
- scientific determinism.
|
||||
|
||||
Lock-order review, ownership reasoning and deterministic tests remain required.
|
||||
|
||||
## Sanitizer command policy
|
||||
|
||||
Do not encode fixed `-j8` as canonical validation.
|
||||
|
||||
Example configuration:
|
||||
Pour tout ticket touchant la concurrence, exécuter :
|
||||
|
||||
```sh
|
||||
CC=gcc CXX=g++ meson setup build-tsan -Db_sanitize=thread -Db_lundef=false -Dvulkan_orb=disabled
|
||||
meson compile -C build-tsan
|
||||
# Build TSan
|
||||
CC=clang meson setup build-tsan --wipe -Db_sanitize=thread -Db_lundef=false
|
||||
meson compile -C build-tsan -j8
|
||||
meson test -C build-tsan --print-errorlogs
|
||||
```
|
||||
|
||||
Use host-aware compile/test parallelism unless the proof itself requires
|
||||
serialization.
|
||||
TSan détecte automatiquement :
|
||||
|
||||
Do not repeatedly wipe an unchanged TSan tree.
|
||||
- les accès concurrents conflictuels instrumentés ;
|
||||
- certaines utilisations incohérentes des primitives de synchronisation.
|
||||
|
||||
## Concurrency review checklist
|
||||
Il ne prouve pas l'absence de deadlock, de signal perdu ou de bug dans une
|
||||
bibliothèque non instrumentée. Les invariants de lifetime et d'ordre de locks
|
||||
restent donc soumis aux tests déterministes et à la revue.
|
||||
|
||||
Before closing a concurrency-sensitive change, verify:
|
||||
### Preuve TSan globale courante
|
||||
|
||||
- ncurses remains main-thread-only;
|
||||
- every shared mutable field has an explicit synchronization owner;
|
||||
- condition predicates are checked in loops;
|
||||
- lock order remains consistent;
|
||||
- no Task uses forced asynchronous cancellation;
|
||||
- Queue callbacks have an active reservation;
|
||||
- internal participants stay within the admitted contract;
|
||||
- all children join on every exit path;
|
||||
- owner-only publication remains ordered where required;
|
||||
- project-close ordering prevents DB use-after-close;
|
||||
- SSD lease ownership remains exact;
|
||||
- Task userdata outlives terminal notification;
|
||||
- appropriate deterministic concurrency tests pass;
|
||||
- portable TSan qualification is preserved;
|
||||
- Vulkan validation is reported separately;
|
||||
- ASan/UBSan is run when the change also affects lifetime/memory.
|
||||
La matrice fraîche emploie GCC/G++ 16.2.1 et désactive explicitement Vulkan.
|
||||
Elle passe 14/14 cibles couvrant Task, Project, Queue, Governor, registre/leases
|
||||
SSD, contrôleur SSD, observateur/TUI async, Candidate, Visual Index, Feature,
|
||||
Matcher et GV, puis 220/220 répétitions déterministes : **234/234** au total.
|
||||
|
||||
## Current retained evidence
|
||||
La seule liste de suppressions est `tests/tsan-opencv.supp`, limitée aux objets
|
||||
partagés externes non instrumentés `libopencv_features.so`,
|
||||
`libopencv_core.so` et `libtbb.so`. Elle ne masque aucune frame Lardon3D. Les
|
||||
warnings GCC `-Wmaybe-uninitialized` des contrôles OpenCV Feature/SIFT sont
|
||||
classés non matériels : le callback fournit une Task non nulle et le helper
|
||||
initialise la structure avant toute autre sortie d'échec. Les warnings OpenCV
|
||||
du build GV appartiennent aux headers externes.
|
||||
|
||||
The canonical global-maintenance record is:
|
||||
Cette preuve TSan ne vaut pas validation de concurrence Vulkan. Le backend
|
||||
ORB Vulkan réel est couvert séparément par le build Clang Vulkan-on 939/939,
|
||||
la suite 65/65 et ses tests de backend/handle/publication ; cette séparation
|
||||
doit rester explicite dans tout rapport.
|
||||
|
||||
```text
|
||||
docs/architecture/global_maintenance_audit.md
|
||||
GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN
|
||||
```
|
||||
## Checklist de concurrence
|
||||
|
||||
The current A6000 checkpoint is later:
|
||||
Avant de livrer un ticket touchant la concurrence :
|
||||
|
||||
```text
|
||||
real-a6000-pre-sfm-2026-09-02
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
The later A6000 proof exercised current bounded parallel paths through selected
|
||||
Feature batch, Candidate, Matcher, Geometric Verifier v3 and Tracks without
|
||||
changing the historical TSan qualification.
|
||||
|
||||
Historical evidence remains historical; new changes require validation scoped
|
||||
to their actual concurrency surface.
|
||||
|
||||
## Summary
|
||||
|
||||
```text
|
||||
NCURSES_OWNER=MAIN_THREAD_ONLY
|
||||
ACTIVE_HEAVY_QUEUE_CALLBACKS=1
|
||||
TASK_CANCELLATION=COOPERATIVE
|
||||
INTERNAL_PARALLELISM=BOUNDED
|
||||
|
||||
PER_ITEM_ATOMICITY_REQUIRES_CROSS_ITEM_SERIALISM=NO
|
||||
OWNER_ONLY_PUBLICATION_REQUIRES_SERIAL_PREPARATION=NO
|
||||
|
||||
TSAN_PORTABLE_PROJECT_MATRIX=QUALIFIED_PASS
|
||||
TSAN_EXTERNAL_OPENCV_TBB=QUALIFIED
|
||||
VULKAN_CONCURRENCY_VALIDATION=SEPARATE
|
||||
|
||||
NO_FIXED_GLOBAL_J8=YES
|
||||
NO_REPEATED_UNCHANGED_WIPE=YES
|
||||
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
```
|
||||
- [ ] Toutes les variables partagées sont protégées par un mutex
|
||||
- [ ] Les mutex sont toujours libérés (même en cas d'erreur)
|
||||
- [ ] Les variables de condition sont vérifiées dans une boucle `while`
|
||||
- [ ] Aucun appel ncurses depuis un worker
|
||||
- [ ] L'annulation des Tasks est coopérative (pas de `pthread_cancel`)
|
||||
- [ ] TSan ne signale aucune erreur
|
||||
- [ ] Le build ASan ne signale aucune fuite mémoire liée aux threads
|
||||
|
|
|
|||
|
|
@ -1,298 +1,136 @@
|
|||
# Testing
|
||||
# Procédures de test
|
||||
|
||||
## Status
|
||||
## Vue d'ensemble
|
||||
|
||||
```text
|
||||
DOCUMENTATION_LANGUAGE=ENGLISH
|
||||
TEST_POLICY=HOST_AWARE
|
||||
REPEATED_UNCHANGED_EXPENSIVE_VALIDATION=AVOID
|
||||
TSAN_OPEN_CV_TBB_QUALIFICATION=REQUIRED
|
||||
VULKAN_CONCURRENCY_VALIDATION=SEPARATE
|
||||
```
|
||||
Lardon3D utilise le framework de test intégré à Meson. Chaque module possède
|
||||
un fichier de test dans `tests/` correspondant au module testé.
|
||||
|
||||
Lardon3D uses Meson's test runner. Tests live under `tests/` and combine unit,
|
||||
integration, persistence, restart, resource and real-path validation.
|
||||
|
||||
## Normal commands
|
||||
|
||||
Run the configured suite:
|
||||
## Lancer les tests
|
||||
|
||||
```sh
|
||||
# Tous les tests
|
||||
meson test -C build --print-errorlogs
|
||||
```
|
||||
|
||||
Run one named test:
|
||||
# Un test spécifique
|
||||
meson test -C build test_task_queue --print-errorlogs
|
||||
|
||||
```sh
|
||||
meson test -C build <test-name> --print-errorlogs
|
||||
```
|
||||
|
||||
Verbose execution:
|
||||
|
||||
```sh
|
||||
# Tests avec verbose
|
||||
meson test -C build -v --print-errorlogs
|
||||
```
|
||||
|
||||
Re-run failures only:
|
||||
|
||||
```sh
|
||||
# Réexécuter uniquement les tests échoués
|
||||
meson test -C build --reprint=failed
|
||||
```
|
||||
|
||||
Use the names registered by the current `meson.build`; this document does not
|
||||
maintain a second authoritative list of every test target.
|
||||
|
||||
## Validation policy
|
||||
|
||||
Validation must match the change.
|
||||
|
||||
A documentation-only change normally requires:
|
||||
## Structure des tests
|
||||
|
||||
```text
|
||||
tests/
|
||||
├── test_task_queue.c # tests de la file de tâches
|
||||
├── test_task.c # tests du module task
|
||||
├── test_resource_governor.c # tests du gouverneur
|
||||
├── test_hardware_profile.c # tests du profil matériel
|
||||
├── test_import.c # tests de l'import
|
||||
├── test_project.c # tests des projets
|
||||
└── test_*.c # autres modules
|
||||
```
|
||||
|
||||
## Écrire un test
|
||||
|
||||
```c
|
||||
#include <glib.h>
|
||||
#include "lardon3d/task.h"
|
||||
|
||||
void test_task_create(void) {
|
||||
task_estimate_t est = {
|
||||
.ram_bytes = 1024 * 1024,
|
||||
.gpu_bytes = 0,
|
||||
.cpu_weight = 1,
|
||||
.io_weight = 0,
|
||||
.batch_size = 10,
|
||||
.batch_max = 100
|
||||
};
|
||||
task_t *t = task_create("test", &est, NULL, NULL);
|
||||
g_assert_nonnull(t);
|
||||
g_assert_cmpint(task_get_state(t), ==, TASK_STATE_IDLE);
|
||||
task_destroy(t);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/task/create", test_task_create);
|
||||
return g_test_run();
|
||||
}
|
||||
```
|
||||
|
||||
## Conventions
|
||||
|
||||
1. **Préfixe `test_`** : chaque fonction de test porte le préfixe `test_`.
|
||||
2. **Chemin hiérarchique** : le nom du test suit le pattern `/module/action`.
|
||||
3. **Asserts GLib** : utiliser `g_assert_*` pour les vérifications.
|
||||
4. **Nettoyage** : chaque test libère toutes ses ressources.
|
||||
5. **Isolation** : un test ne dépend pas de l'état d'un autre test.
|
||||
6. **Déterminisme** : les tests ne dépendent pas de l'heure, du filesystem
|
||||
ou de l'état réseau (sauf test d'import).
|
||||
|
||||
## Commentaires source
|
||||
|
||||
Les commentaires documentent le pourquoi et les contrats non évidents :
|
||||
invariants, propriété et durée de vie, persistance, ainsi que limites et
|
||||
frontières de ressources. Les API publiques documentent leurs contrats non
|
||||
évidents. Ils ne paraphrasent pas le code ligne par ligne et sont mis à jour
|
||||
avec tout changement de comportement.
|
||||
|
||||
## Tests unitaires vs tests d'intégration
|
||||
|
||||
`test-visual-index` couvre les descriptors synthétiques, le retrieval ORB réel,
|
||||
les filtres inter-ScanSets, quatre queries concurrentes, la corruption/absence/
|
||||
troncature d'un segment et 4 000 Feature Sets synthétiques. Le scénario de
|
||||
reprise `visual_index.update` est exercé dans `test-feature-task`.
|
||||
|
||||
| Type | Portée | Fichier |
|
||||
|---|---|---|
|
||||
| Unitaire | Un module isolé | `tests/test_<module>.c` |
|
||||
| Intégration | Interaction entre modules | `tests/test_<module>.c` avec dépendances réelles |
|
||||
|
||||
## Validation par ticket
|
||||
|
||||
Avant de livrer un ticket, exécuter la séquence complète :
|
||||
|
||||
```sh
|
||||
# 1. Build clean
|
||||
CC=clang meson setup build --wipe
|
||||
meson compile -C build -j8
|
||||
|
||||
# 2. Tests
|
||||
meson test -C build --print-errorlogs
|
||||
|
||||
# 3. Style
|
||||
git diff --check
|
||||
targeted content checks
|
||||
targeted link/authority review
|
||||
```
|
||||
|
||||
It does not justify wiping and rebuilding unchanged code.
|
||||
|
||||
A code change normally requires, in increasing scope:
|
||||
|
||||
```text
|
||||
targeted build
|
||||
targeted tests
|
||||
broader affected suite
|
||||
sanitizer or concurrency validation when relevant
|
||||
full suite when the change or release boundary justifies it
|
||||
```
|
||||
|
||||
Do not repeatedly rerun an unchanged expensive suite between documentation
|
||||
edits merely to create activity.
|
||||
|
||||
## Host-aware parallelism
|
||||
|
||||
Build and test parallelism are host-aware.
|
||||
|
||||
Do not encode a project-wide fixed `-j8`, `--num-processes 1`, or equivalent
|
||||
constant as canonical policy.
|
||||
|
||||
The correct width depends on the current machine, interactive reserve, memory,
|
||||
toolchain and workload. Use all safe useful host capacity while preserving the
|
||||
defined interactive reserve.
|
||||
|
||||
```text
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
```
|
||||
|
||||
If a temporary validation must be serialized for determinism, diagnosis or a
|
||||
known tool limitation, label that serialization as test-specific evidence
|
||||
rather than a global default.
|
||||
|
||||
## Fresh build policy
|
||||
|
||||
Do not use `meson setup --wipe` by default.
|
||||
|
||||
Prefer:
|
||||
|
||||
```sh
|
||||
meson setup --reconfigure build
|
||||
meson compile -C build
|
||||
```
|
||||
|
||||
Create or wipe a build directory when the configuration genuinely needs a
|
||||
fresh environment, for example:
|
||||
|
||||
```text
|
||||
different sanitizer set
|
||||
portable Vulkan-off proof
|
||||
Vulkan-on proof
|
||||
compiler-family change
|
||||
known stale/corrupt build directory
|
||||
release-grade clean proof
|
||||
```
|
||||
|
||||
Repeated wipes of the same unchanged configuration waste time and invalidate
|
||||
incremental-build advantages.
|
||||
|
||||
## Sanitizers
|
||||
|
||||
### ASan / UBSan
|
||||
|
||||
For memory, lifetime, ownership or undefined-behavior changes, use a dedicated
|
||||
sanitizer build.
|
||||
|
||||
Example configuration:
|
||||
|
||||
```sh
|
||||
CC=clang CXX=clang++ meson setup build-asan -Db_sanitize=address,undefined
|
||||
meson compile -C build-asan
|
||||
# 4. Si mémoire/concurrence touchés
|
||||
CC=clang meson setup build-asan --wipe -Db_sanitize=address,undefined
|
||||
meson compile -C build-asan -j8
|
||||
meson test -C build-asan --print-errorlogs
|
||||
|
||||
# 5. Si concurrence touchée
|
||||
CC=clang meson setup build-tsan --wipe -Db_sanitize=thread -Db_lundef=false
|
||||
meson compile -C build-tsan -j8
|
||||
meson test -C build-tsan --print-errorlogs
|
||||
```
|
||||
|
||||
Reconfigure or wipe only when the existing sanitizer directory does not match
|
||||
the requested configuration.
|
||||
## Dépannage
|
||||
|
||||
### LeakSanitizer qualification
|
||||
### Test qui échoue en ASan
|
||||
|
||||
The retained global maintenance evidence must not be summarized as
|
||||
`LSan 64/64`.
|
||||
Vérifier les durées de vie des allocations. Ne jamais libérer un objet puis
|
||||
y accéder. Vérifier que chaque `task_destroy()` est appelée.
|
||||
|
||||
The full first leak-enabled run exposed an externally attributed OpenCL loader
|
||||
leak and two timeout anomalies. The retained qualified result is:
|
||||
### Test qui échoue en TSan
|
||||
|
||||
```text
|
||||
ASan/UBSan full suite: PASS with detect_leaks=0
|
||||
proved subset without the external loader: LSan PASS
|
||||
full leak-enabled suite: not an unqualified PASS
|
||||
```
|
||||
Vérifier que toutes les variables partagées sont protégées par un mutex.
|
||||
Vérifier que ncurses est utilisé uniquement depuis le thread principal.
|
||||
|
||||
Preserve that distinction in future reports unless new evidence supersedes it.
|
||||
### Test qui échoue uniquement en release
|
||||
|
||||
## ThreadSanitizer
|
||||
|
||||
Concurrency changes require TSan where the instrumented boundary is meaningful.
|
||||
|
||||
The retained global maintenance TSan proof used GCC/G++ with Vulkan disabled
|
||||
and covered the selected concurrent targets plus deterministic repetitions.
|
||||
|
||||
The only retained suppression file is:
|
||||
|
||||
```text
|
||||
tests/tsan-opencv.supp
|
||||
```
|
||||
|
||||
Its purpose is limited to external OpenCV/TBB objects. It must not suppress
|
||||
Lardon3D frames.
|
||||
|
||||
Therefore never report a blanket statement such as:
|
||||
|
||||
```text
|
||||
TSan proves the entire Vulkan build race-free
|
||||
```
|
||||
|
||||
The valid qualification is:
|
||||
|
||||
```text
|
||||
project concurrent paths covered by the retained portable TSan matrix
|
||||
external OpenCV/TBB reports qualified by the narrow suppression boundary
|
||||
Vulkan concurrency validated separately
|
||||
```
|
||||
|
||||
## Vulkan validation
|
||||
|
||||
ORB Vulkan uses a separate validation boundary.
|
||||
|
||||
The retained global maintenance evidence includes a Vulkan-on build and suite
|
||||
on the real Radeon 780M/RADV host, plus dedicated backend/handle/publication
|
||||
tests.
|
||||
|
||||
That evidence is not interchangeable with TSan.
|
||||
|
||||
SIFT/RootSIFT feasibility results did not establish a production GPU backend;
|
||||
do not turn feasibility checks into production validation claims.
|
||||
|
||||
## Determinism and repetition
|
||||
|
||||
Repeat tests when repetition proves something specific:
|
||||
|
||||
```text
|
||||
deterministic restart
|
||||
race sensitivity
|
||||
resource adaptation
|
||||
ordering stability
|
||||
flaky regression reproduction
|
||||
```
|
||||
|
||||
Do not repeat unchanged tests without a stated purpose.
|
||||
|
||||
When repetition is the evidence, record:
|
||||
|
||||
```text
|
||||
exact test/corpus
|
||||
run count
|
||||
relevant configuration
|
||||
success/failure count
|
||||
digest or invariant when applicable
|
||||
```
|
||||
|
||||
## Test isolation
|
||||
|
||||
Tests should:
|
||||
|
||||
- own and clean up their temporary resources;
|
||||
- avoid depending on another test's execution order;
|
||||
- avoid network state unless the test explicitly owns that dependency;
|
||||
- use synthetic/private Resource snapshots where the test is about deterministic
|
||||
policy rather than live host telemetry;
|
||||
- avoid changing global process state without restoring it.
|
||||
|
||||
OpenCV thread configuration is process-wide and must be restored on every exit
|
||||
path in tests that change it.
|
||||
|
||||
## Public-header validation
|
||||
|
||||
When a public C header changes, run a standalone C17 syntax probe in addition to
|
||||
normal build coverage.
|
||||
|
||||
Conceptually:
|
||||
|
||||
```sh
|
||||
cc -x c -std=c17 -fsyntax-only -Iinclude -include lardon3d/<header>.h /dev/null
|
||||
```
|
||||
|
||||
Use the current supported compiler matrix when the change affects ABI or
|
||||
C/C++ interoperability.
|
||||
|
||||
## Source comments
|
||||
|
||||
Source comments explain non-obvious contracts:
|
||||
|
||||
```text
|
||||
invariants
|
||||
ownership and lifetime
|
||||
persistence ordering
|
||||
resource boundaries
|
||||
recovery behavior
|
||||
scientific constraints
|
||||
```
|
||||
|
||||
They should not paraphrase obvious code line by line.
|
||||
|
||||
Repository source comments are English.
|
||||
|
||||
## Current retained global maintenance evidence
|
||||
|
||||
The canonical detailed record is:
|
||||
|
||||
```text
|
||||
docs/architecture/global_maintenance_audit.md
|
||||
GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN
|
||||
```
|
||||
|
||||
That historical checkpoint includes fresh portable/Vulkan builds, full suites,
|
||||
sanitizer work, TSan work, public-header probes, ABI/application-link checks and
|
||||
independent review.
|
||||
|
||||
It remains historical evidence. New changes require only the validation
|
||||
appropriate to the changed surface unless a new global checkpoint is being
|
||||
created.
|
||||
|
||||
## Ticket closure checklist
|
||||
|
||||
Before closing a code ticket:
|
||||
|
||||
- confirm the requested scope only was changed;
|
||||
- run `git diff --check`;
|
||||
- run targeted tests for changed behavior;
|
||||
- run the affected broader suite when justified;
|
||||
- run ASan/UBSan for memory/lifetime-sensitive changes;
|
||||
- run TSan for concurrency-sensitive project code when applicable;
|
||||
- keep Vulkan validation separate from portable TSan claims;
|
||||
- preserve exact external-library qualifications;
|
||||
- avoid fixed host-parallelism constants;
|
||||
- avoid repeated unchanged clean builds or suites;
|
||||
- report what actually ran, not a stronger claim.
|
||||
|
||||
For documentation-only remediation, use documentation checks rather than
|
||||
rebuilding unchanged production code.
|
||||
Vérifier les assertions et les overflow arithmétiques. Compiler avec
|
||||
`-fsanitize=undefined` pour détecter les comportements indéfinis.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,97 +0,0 @@
|
|||
#ifndef LARDON3D_CALIBRATION_AF_STUDY_H
|
||||
#define LARDON3D_CALIBRATION_AF_STUDY_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
LARDON3D_CALIBRATION_AF_STUDY_VERSION = 1,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_PROBE_MODEL_VERSION = 1,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_SHA256_SIZE = 32,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_FOCUS_TOKEN_CAPACITY = 128,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_MAX_SAMPLES = 64,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_MAX_PAIRS = 2016,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_MAX_ARTIFACT_BYTES = 131072,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_CALIBRATION_AF_STUDY_OK = 0,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_INVALID_ARGUMENT,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_CAPACITY,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_INVALID_EVIDENCE,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_ENCODING_ERROR,
|
||||
} Lardon3DCalibrationAfStudyResult;
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT = 1,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_HOLDOUT = 2,
|
||||
} Lardon3DCalibrationAfStudySampleRole;
|
||||
|
||||
typedef struct {
|
||||
Lardon3DCalibrationAfStudySampleRole role;
|
||||
char focus_token[LARDON3D_CALIBRATION_AF_STUDY_FOCUS_TOKEN_CAPACITY];
|
||||
unsigned char calibration_evidence_sha256[LARDON3D_CALIBRATION_AF_STUDY_SHA256_SIZE];
|
||||
double fx;
|
||||
double fy;
|
||||
double cx;
|
||||
double cy;
|
||||
double k1;
|
||||
double k2;
|
||||
double p1;
|
||||
double p2;
|
||||
} Lardon3DCalibrationAfStudySample;
|
||||
|
||||
typedef struct {
|
||||
unsigned char study_context_sha256[LARDON3D_CALIBRATION_AF_STUDY_SHA256_SIZE];
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
const Lardon3DCalibrationAfStudySample *samples;
|
||||
size_t sample_count;
|
||||
} Lardon3DCalibrationAfStudyInput;
|
||||
|
||||
typedef struct {
|
||||
uint32_t sample_count;
|
||||
uint32_t fit_count;
|
||||
uint32_t holdout_count;
|
||||
uint32_t pair_count;
|
||||
uint32_t same_focus_pair_count;
|
||||
uint32_t cross_focus_pair_count;
|
||||
uint32_t fit_holdout_pair_count;
|
||||
double all_center_max_px;
|
||||
double all_edge_probe_max_px;
|
||||
double all_corner_probe_max_px;
|
||||
double all_global_probe_max_px;
|
||||
double same_focus_global_probe_max_px;
|
||||
double cross_focus_global_probe_max_px;
|
||||
double fit_holdout_global_probe_max_px;
|
||||
} Lardon3DCalibrationAfStudySummary;
|
||||
|
||||
/* Produce deterministic AF-study evidence from already acquired calibration
|
||||
* results. This API performs no calibration solve, no Project DB access and no
|
||||
* scientific PASS/FAIL decision. `study_context_sha256` is the caller-retained
|
||||
* identity of the exact body/lens/focal/non-focus geometric study context.
|
||||
* Focus tokens are opaque exact observations and may repeat across independent
|
||||
* calibration samples. A repeated exact (focus token, calibration evidence
|
||||
* SHA-256) pair is rejected because it is not independent evidence.
|
||||
*
|
||||
* Projection deltas use the frozen pinhole + k1/k2/p1/p2 forward model on nine
|
||||
* canonical normalized probes: centre, four edge probes and four corner probes
|
||||
* at +/-0.7. Metrics are measurements only; this v1 API freezes no acceptance
|
||||
* threshold. The binary L3DAFST1 artifact canonicalizes samples independent of
|
||||
* caller order, includes every pairwise metric, and is suitable for hashing as
|
||||
* retained evidence. */
|
||||
Lardon3DCalibrationAfStudyResult lardon3d_calibration_af_study_produce(
|
||||
const Lardon3DCalibrationAfStudyInput *input,
|
||||
unsigned char *artifact, size_t artifact_capacity, size_t *written,
|
||||
unsigned char artifact_sha256[LARDON3D_CALIBRATION_AF_STUDY_SHA256_SIZE],
|
||||
Lardon3DCalibrationAfStudySummary *summary);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
#ifndef LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_H
|
||||
#define LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_H
|
||||
|
||||
#include <lardon3d/calibration_af_study.h>
|
||||
#include <lardon3d/calibration_af_study_workflow.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_OK = 0,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_ARGUMENT,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_CAPACITY,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_CRYPTO_ERROR,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_ENCODING_ERROR,
|
||||
} Lardon3DCalibrationAfStudyAssemblyResult;
|
||||
|
||||
typedef struct {
|
||||
const Lardon3DCalibrationWorkflowExternalEvidence *external;
|
||||
Lardon3DCalibrationAfStudySampleRole role;
|
||||
const char *focus_token;
|
||||
} Lardon3DCalibrationAfStudyAssemblyEntry;
|
||||
|
||||
typedef struct {
|
||||
/* Exact caller-retained identity of the common body/lens/focal/non-focus
|
||||
* geometric study context. The assembly does not derive or reinterpret it. */
|
||||
unsigned char study_context_sha256[
|
||||
LARDON3D_CALIBRATION_AF_STUDY_SHA256_SIZE];
|
||||
const Lardon3DCalibrationAfStudyAssemblyEntry *entries;
|
||||
size_t entry_count;
|
||||
} Lardon3DCalibrationAfStudyAssemblyInput;
|
||||
|
||||
/* Assemble 2..MAX_SAMPLES already-materialized Calibration Workflow results
|
||||
* into one deterministic L3DAFST1 artifact.
|
||||
*
|
||||
* Each entry is converted through the frozen Workflow bridge; no solver file
|
||||
* is parsed here. All samples must share exact oriented dimensions because one
|
||||
* L3DAFST1 study has one image geometry. The common optical/non-focus identity
|
||||
* remains the explicit study_context_sha256 supplied by the caller.
|
||||
*
|
||||
* Duplicate calibration_evidence_sha256 values are rejected regardless of
|
||||
* focus token or FIT/HOLDOUT role. A single physical calibration result cannot
|
||||
* therefore be relabelled to masquerade as independent AF evidence.
|
||||
*
|
||||
* This boundary performs no Project DB access, no metadata interpretation, no
|
||||
* physical applicability decision and no acceptance thresholding. */
|
||||
Lardon3DCalibrationAfStudyAssemblyResult
|
||||
lardon3d_calibration_af_study_assemble_materialized(
|
||||
const Lardon3DCalibrationAfStudyAssemblyInput *input,
|
||||
unsigned char *artifact, size_t artifact_capacity, size_t *written,
|
||||
unsigned char artifact_sha256[
|
||||
LARDON3D_CALIBRATION_AF_STUDY_SHA256_SIZE],
|
||||
Lardon3DCalibrationAfStudySummary *summary);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
#ifndef LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_H
|
||||
#define LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_H
|
||||
|
||||
#include <lardon3d/calibration_af_study.h>
|
||||
#include <lardon3d/calibration_workflow.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_OK = 0,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_ARGUMENT,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_EVIDENCE,
|
||||
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_CRYPTO_ERROR,
|
||||
} Lardon3DCalibrationAfStudyWorkflowResult;
|
||||
|
||||
/* Convert one already validated/materialized Calibration Workflow result into
|
||||
* one AF-study sample without re-parsing any solver file.
|
||||
*
|
||||
* `focus_token` and FIT/HOLDOUT role are study annotations. They are
|
||||
* deliberately excluded from calibration_evidence_sha256, so relabelling the
|
||||
* same calibration result cannot manufacture a new calibration-evidence
|
||||
* identity.
|
||||
*
|
||||
* calibration_evidence_sha256 is domain-separated and binds:
|
||||
* - target identity;
|
||||
* - optical-state identity;
|
||||
* - solver executable/configuration identity;
|
||||
* - initialization and validation evidence;
|
||||
* - exact oriented dimensions;
|
||||
* - exact published binary64 fx,fy,cx,cy,k1,k2,p1,p2.
|
||||
*
|
||||
* The bridge checks the materialization invariants it consumes, including
|
||||
* deterministic validation-evidence binding and equality of all three repeated
|
||||
* full solves. It publishes repeated_parameters[0].
|
||||
*
|
||||
* No Project DB access, metadata interpretation, physical AF applicability
|
||||
* decision, interpolation, extrapolation, solver execution or thresholding
|
||||
* occurs here. */
|
||||
Lardon3DCalibrationAfStudyWorkflowResult
|
||||
lardon3d_calibration_af_study_sample_from_materialized_evidence(
|
||||
const Lardon3DCalibrationWorkflowExternalEvidence *external,
|
||||
Lardon3DCalibrationAfStudySampleRole role, const char *focus_token,
|
||||
Lardon3DCalibrationAfStudySample *output);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
#ifndef LARDON3D_CALIBRATION_BOOTSTRAP_V2_H
|
||||
#define LARDON3D_CALIBRATION_BOOTSTRAP_V2_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <lardon3d/project_db.h>
|
||||
#include <lardon3d/sparse_sfm_model.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
LARDON3D_CALIBRATION_BOOTSTRAP_V2_ARTIFACT_VERSION = 2,
|
||||
LARDON3D_CALIBRATION_BOOTSTRAP_V2_MAX_BYTES = 600000,
|
||||
LARDON3D_CALIBRATION_BOOTSTRAP_V2_MAX_GROUPS = 4096,
|
||||
LARDON3D_CALIBRATION_BOOTSTRAP_V2_MAX_ENTRIES = 4096,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK = 0,
|
||||
LARDON3D_CALIBRATION_BOOTSTRAP_V2_INVALID_ARGUMENT,
|
||||
LARDON3D_CALIBRATION_BOOTSTRAP_V2_MALFORMED_ARTIFACT,
|
||||
LARDON3D_CALIBRATION_BOOTSTRAP_V2_PROVENANCE_MISMATCH,
|
||||
LARDON3D_CALIBRATION_BOOTSTRAP_V2_SELECTION_CONFLICT,
|
||||
LARDON3D_CALIBRATION_BOOTSTRAP_V2_DB_ERROR,
|
||||
LARDON3D_CALIBRATION_BOOTSTRAP_V2_OUT_OF_MEMORY,
|
||||
} Lardon3DCalibrationBootstrapV2Result;
|
||||
|
||||
typedef struct {
|
||||
unsigned char artifact_sha256[LARDON3D_PROJECT_DB_SHA256_SIZE];
|
||||
Lardon3DSparseCalibrationScope scope;
|
||||
uint32_t calibration_count;
|
||||
uint32_t group_count;
|
||||
} Lardon3DCalibrationBootstrapV2Output;
|
||||
|
||||
typedef struct {
|
||||
uint32_t selected_item_index;
|
||||
uint64_t image_id;
|
||||
uint64_t calibration_id;
|
||||
} Lardon3DCalibrationBootstrapV2Member;
|
||||
|
||||
/* Validate and publish one complete artifact without attaching its scope.
|
||||
* `members` is caller-owned storage of at least V2_MAX_ENTRIES only when that
|
||||
* many entries are accepted; `member_capacity` may be smaller and yields
|
||||
* INVALID_ARGUMENT before database mutation when the artifact will not fit.
|
||||
* Successful members are ordered by selected_item_index and expose the exact
|
||||
* immutable calibration created/reused for each selected image.
|
||||
*
|
||||
* This primitive exists so Workflow v2 can establish exact Capture-owned v26
|
||||
* applicability and selection before the single READY attachment. It never
|
||||
* infers Capture identity from image, path, digest, or group identity, and it
|
||||
* never attaches the returned complete scope. Exact retry is deterministic;
|
||||
* immutable calibrations and the unattached scope may survive later workflow
|
||||
* failure under the existing publication contract. */
|
||||
Lardon3DCalibrationBootstrapV2Result
|
||||
lardon3d_calibration_bootstrap_v2_publish_unattached(
|
||||
Lardon3DProjectDb *database, uint64_t execution_id,
|
||||
const unsigned char *artifact, size_t artifact_size,
|
||||
const unsigned char expected_artifact_sha256[LARDON3D_PROJECT_DB_SHA256_SIZE],
|
||||
Lardon3DCalibrationBootstrapV2Member *members, size_t member_capacity,
|
||||
Lardon3DCalibrationBootstrapV2Output *output);
|
||||
|
||||
/* Import one complete L3DCALB2 artifact for an immutable selected execution.
|
||||
* All pointers are required and borrowed only for the call; artifact_size is
|
||||
* bounded by V2_MAX_BYTES and must match expected_artifact_sha256 before any
|
||||
* parsing or database access. The fixed-width little-endian format accepts
|
||||
* only the existing eight-parameter PINHOLE model and complete, nonoverlapping
|
||||
* coverage of selected item indexes, image IDs, and representation SHA-256s.
|
||||
*
|
||||
* Each serialized group is hashed independently and that digest becomes every
|
||||
* member calibration's IMPORTED_TRUSTED provenance fingerprint. Thus target,
|
||||
* optical-state, solver, initialization, validation, and exact membership
|
||||
* evidence remain group-local even when one scope contains heterogeneous
|
||||
* groups. Validation completes before publication. Existing immutable
|
||||
* calibrations and one exact all-image scope are reused on retry; the scope is
|
||||
* attached only after complete publication, so a failure cannot make the
|
||||
* execution READY. No Capture identity, solver result beyond intrinsics, or
|
||||
* compatibility relation is inferred by this C ABI. */
|
||||
Lardon3DCalibrationBootstrapV2Result lardon3d_calibration_bootstrap_v2_import(
|
||||
Lardon3DProjectDb *database, uint64_t execution_id,
|
||||
const unsigned char *artifact, size_t artifact_size,
|
||||
const unsigned char expected_artifact_sha256[LARDON3D_PROJECT_DB_SHA256_SIZE],
|
||||
Lardon3DCalibrationBootstrapV2Output *output);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
#ifndef LARDON3D_CALIBRATION_TOOLING_H
|
||||
#define LARDON3D_CALIBRATION_TOOLING_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <lardon3d/calibration_bootstrap.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
LARDON3D_CALIBRATION_TOOLING_VERSION = 1,
|
||||
LARDON3D_CALIBRATION_TOOLING_MAX_VIEWS = 4096,
|
||||
LARDON3D_CALIBRATION_TOOLING_MAX_COORDINATE_CHECKS = 81920,
|
||||
LARDON3D_CALIBRATION_TOOLING_TARGET_MEASUREMENTS = 10,
|
||||
LARDON3D_CALIBRATION_TOOLING_VALIDATION_FLAGS = 15,
|
||||
LARDON3D_CALIBRATION_TOOLING_TARGET_CHARUCO_9X7_DICT_5X5_100 = 1,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_CALIBRATION_TOOLING_OK = 0,
|
||||
LARDON3D_CALIBRATION_TOOLING_INVALID_ARGUMENT,
|
||||
LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED,
|
||||
LARDON3D_CALIBRATION_TOOLING_CAPACITY,
|
||||
LARDON3D_CALIBRATION_TOOLING_ENCODING_ERROR,
|
||||
LARDON3D_CALIBRATION_TOOLING_IMPORT_ERROR,
|
||||
} Lardon3DCalibrationToolingResult;
|
||||
|
||||
/* One solver-reported view. `quadrant` is 0..3 for frame quadrants and 4 for
|
||||
* the centre. `distance_band` is the declared near/mid/far band 0..2.
|
||||
* Rejected views retain their source identity and a nonzero rejection reason;
|
||||
* only accepted views contribute to the frozen acceptance statistics. The
|
||||
* manifest is caller-owned and borrowed only during validation/production. */
|
||||
typedef struct {
|
||||
unsigned char source_sha256[32];
|
||||
uint32_t accepted;
|
||||
uint32_t rejection_reason;
|
||||
uint32_t holdout;
|
||||
uint32_t quadrant;
|
||||
uint32_t distance_band;
|
||||
uint32_t orientation_degrees;
|
||||
uint32_t target_corner_quadrant_mask;
|
||||
uint32_t corner_count;
|
||||
uint32_t residual_count;
|
||||
uint32_t high_residual_count;
|
||||
double target_occupancy;
|
||||
double normal_angle_degrees;
|
||||
double distance_metres;
|
||||
double corner_rms_px;
|
||||
double clipped_fraction;
|
||||
double reprojection_rmse_px;
|
||||
double maximum_residual_px;
|
||||
} Lardon3DCalibrationToolingView;
|
||||
|
||||
/* Every selected image has exactly one artifact entry in selected-item order.
|
||||
* `fit_parameters` are the deterministic 80% fit result used to verify `maximum_parameter_delta`; the
|
||||
* published parameters are the complete-set result. */
|
||||
typedef struct {
|
||||
uint64_t image_id;
|
||||
unsigned char representation_sha256[32];
|
||||
unsigned char optical_state_sha256[32];
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
double fx, fy, cx, cy, k1, k2, p1, p2;
|
||||
double fit_fx, fit_fy, fit_cx, fit_cy, fit_k1, fit_k2, fit_p1, fit_p2;
|
||||
double repeated_parameters[3][8];
|
||||
uint32_t support_images;
|
||||
uint32_t support_observations;
|
||||
double reprojection_rmse_px;
|
||||
double maximum_parameter_delta;
|
||||
uint32_t validation_flags;
|
||||
} Lardon3DCalibrationToolingEntry;
|
||||
|
||||
typedef struct {
|
||||
unsigned char source_sha256[32];
|
||||
uint32_t orientation_degrees;
|
||||
double dx_px;
|
||||
double dy_px;
|
||||
} Lardon3DCalibrationToolingCoordinateCheck;
|
||||
|
||||
/* This is an operational, bounded view of a completed Science v1 bundle. WHY:
|
||||
* the solver remains external, so this API validates its immutable evidence
|
||||
* rather than importing a solver-private format. CONTRACT: all pointers are
|
||||
* borrowed; each digest is SHA-256; no field is persisted by this API except
|
||||
* through the frozen bootstrap importer. INVARIANT: entries are in selected
|
||||
* item order and never include Tracks, poses, or reconstruction results. */
|
||||
typedef struct {
|
||||
unsigned char target_sha256[32];
|
||||
unsigned char optical_state_sha256[32];
|
||||
unsigned char solver_executable_sha256[32];
|
||||
unsigned char solver_configuration_sha256[32];
|
||||
unsigned char initialization_evidence_sha256[32];
|
||||
unsigned char validation_evidence_sha256[32];
|
||||
uint32_t target_family;
|
||||
uint32_t target_squares_x;
|
||||
uint32_t target_squares_y;
|
||||
double target_square_length_mm;
|
||||
double target_marker_length_mm;
|
||||
double target_active_width_mm;
|
||||
double target_active_height_mm;
|
||||
double target_white_border_mm;
|
||||
double target_measurements_mm[LARDON3D_CALIBRATION_TOOLING_TARGET_MEASUREMENTS];
|
||||
double measurement_resolution_mm;
|
||||
/* Calibration Science v1 defines planarity as a categorical physical
|
||||
* attestation, not a numeric flatness tolerance. This legacy ABI field is
|
||||
* therefore a required NAN sentinel and MUST NOT carry an invented physical
|
||||
* measurement. The future workflow coordinator binds the canonical session
|
||||
* manifest containing `planarity PASS <sha256>` through
|
||||
* initialization_evidence_sha256. */
|
||||
double target_flatness_mm;
|
||||
double holdout_rmse_px;
|
||||
double holdout_maximum_residual_px;
|
||||
uint32_t extra_distortion_coefficient_count;
|
||||
const Lardon3DCalibrationToolingView *views;
|
||||
size_t view_count;
|
||||
const Lardon3DCalibrationToolingEntry *entries;
|
||||
size_t entry_count;
|
||||
const Lardon3DCalibrationToolingCoordinateCheck *coordinate_checks;
|
||||
size_t coordinate_check_count;
|
||||
} Lardon3DCalibrationToolingEvidence;
|
||||
|
||||
/* Validate all CALIBRATION_SCIENCE_V1 hard rejects. No allocations, DB access,
|
||||
* solver execution, or persistent writes occur. */
|
||||
Lardon3DCalibrationToolingResult lardon3d_calibration_tooling_validate(
|
||||
const Lardon3DCalibrationToolingEvidence *evidence);
|
||||
|
||||
/* Encode exactly L3DCALB1 v1 into caller storage and return its SHA-256. The
|
||||
* output is deterministic for identical evidence. `written` may be NULL. */
|
||||
Lardon3DCalibrationToolingResult lardon3d_calibration_tooling_produce(
|
||||
const Lardon3DCalibrationToolingEvidence *evidence, unsigned char *artifact,
|
||||
size_t artifact_capacity, size_t *written, unsigned char artifact_sha256[32]);
|
||||
|
||||
/* Validate, produce, then invoke only the frozen production importer. A failed
|
||||
* validation/encoding never reaches the DB. Import semantics, including its
|
||||
* permitted immutable-row recovery behavior, remain owned by that importer. */
|
||||
Lardon3DCalibrationToolingResult lardon3d_calibration_tooling_import(
|
||||
Lardon3DProjectDb *database, uint64_t execution_id,
|
||||
const Lardon3DCalibrationToolingEvidence *evidence, unsigned char *artifact,
|
||||
size_t artifact_capacity, size_t *written, Lardon3DCalibrationBootstrapOutput *output);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
#ifndef LARDON3D_CALIBRATION_TOOLING_V2_H
|
||||
#define LARDON3D_CALIBRATION_TOOLING_V2_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <lardon3d/calibration_bootstrap_v2.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
LARDON3D_CALIBRATION_TOOLING_V2_VERSION = 2,
|
||||
LARDON3D_CALIBRATION_TOOLING_V2_VALIDATION_FLAGS = 15,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_CALIBRATION_TOOLING_V2_OK = 0,
|
||||
LARDON3D_CALIBRATION_TOOLING_V2_INVALID_ARGUMENT,
|
||||
LARDON3D_CALIBRATION_TOOLING_V2_EVIDENCE_REJECTED,
|
||||
LARDON3D_CALIBRATION_TOOLING_V2_CAPACITY,
|
||||
LARDON3D_CALIBRATION_TOOLING_V2_ENCODING_ERROR,
|
||||
LARDON3D_CALIBRATION_TOOLING_V2_IMPORT_ERROR,
|
||||
} Lardon3DCalibrationToolingV2Result;
|
||||
|
||||
/* One selected-image member of a caller-declared scientific group. The item
|
||||
* index is the immutable selected-execution order, not an operational group
|
||||
* index. Parameters use the exact v1/OpenCV pinhole meaning. All storage is
|
||||
* caller-owned and borrowed only while the API call is active. */
|
||||
typedef struct {
|
||||
uint32_t selected_item_index;
|
||||
uint64_t image_id;
|
||||
unsigned char representation_sha256[32];
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
double fx, fy, cx, cy, k1, k2, p1, p2;
|
||||
uint32_t support_images;
|
||||
uint32_t support_observations;
|
||||
double reprojection_rmse_px;
|
||||
double maximum_parameter_delta;
|
||||
uint32_t validation_flags;
|
||||
} Lardon3DCalibrationToolingV2Entry;
|
||||
|
||||
/* Group identity is the exact bounded state identifier plus its explicit
|
||||
* version. The six nonzero digests independently bind optical applicability,
|
||||
* physical target, solver executable/configuration, initialization, and
|
||||
* validation evidence. Entries must be strictly increasing by selected item
|
||||
* index; groups must be strictly increasing by (group_identity_sha256,
|
||||
* group_version). These canonical orders make identical evidence byte-stable
|
||||
* without hiding caller mistakes by silently reordering it. */
|
||||
typedef struct {
|
||||
unsigned char group_identity_sha256[32];
|
||||
uint32_t group_version;
|
||||
unsigned char optical_state_sha256[32];
|
||||
unsigned char target_sha256[32];
|
||||
unsigned char solver_executable_sha256[32];
|
||||
unsigned char solver_configuration_sha256[32];
|
||||
unsigned char initialization_evidence_sha256[32];
|
||||
unsigned char validation_evidence_sha256[32];
|
||||
const Lardon3DCalibrationToolingV2Entry *entries;
|
||||
size_t entry_count;
|
||||
} Lardon3DCalibrationToolingV2Group;
|
||||
|
||||
/* A bounded heterogeneous publication manifest. Every item index in
|
||||
* [0, entry_count) must occur exactly once across all groups, and every image
|
||||
* ID must be globally unique. Tooling validates only explicit evidence and
|
||||
* model-domain safety; it does not run a solver, invent acceptance thresholds,
|
||||
* or decide optical compatibility. */
|
||||
typedef struct {
|
||||
const Lardon3DCalibrationToolingV2Group *groups;
|
||||
size_t group_count;
|
||||
size_t entry_count;
|
||||
} Lardon3DCalibrationToolingV2Evidence;
|
||||
|
||||
/* Validate borrowed evidence and its borrowed nested group/entry arrays for
|
||||
* bounded counts, canonical ordering, and exact selected-index/image coverage.
|
||||
* Validation does not mutate evidence, artifact bytes, or Project DB state.
|
||||
* INVALID_ARGUMENT reports a missing top-level pointer/groups array or
|
||||
* out-of-range top-level counts; EVIDENCE_REJECTED reports invalid nested or
|
||||
* scientific evidence within an otherwise bounded top-level manifest. */
|
||||
Lardon3DCalibrationToolingV2Result lardon3d_calibration_tooling_v2_validate(
|
||||
const Lardon3DCalibrationToolingV2Evidence *evidence);
|
||||
|
||||
/* Encode deterministic L3DCALB2 into caller storage and return its SHA-256.
|
||||
* `written` may be NULL; no partial artifact is reported as written. */
|
||||
Lardon3DCalibrationToolingV2Result lardon3d_calibration_tooling_v2_produce(
|
||||
const Lardon3DCalibrationToolingV2Evidence *evidence, unsigned char *artifact,
|
||||
size_t artifact_capacity, size_t *written, unsigned char artifact_sha256[32]);
|
||||
|
||||
/* Validate, encode, then call the production v2 bootstrap importer. The DB is
|
||||
* untouched when validation or encoding fails. */
|
||||
Lardon3DCalibrationToolingV2Result lardon3d_calibration_tooling_v2_import(
|
||||
Lardon3DProjectDb *database, uint64_t execution_id,
|
||||
const Lardon3DCalibrationToolingV2Evidence *evidence, unsigned char *artifact,
|
||||
size_t artifact_capacity, size_t *written,
|
||||
Lardon3DCalibrationBootstrapV2Output *output);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
#ifndef LARDON3D_CALIBRATION_WORKFLOW_H
|
||||
#define LARDON3D_CALIBRATION_WORKFLOW_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <lardon3d/calibration_tooling.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
LARDON3D_CALIBRATION_WORKFLOW_VERSION = 1,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_MAX_FILE_BYTES = 128 * 1024 * 1024,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_MAX_SELECTED_ITEMS = 4096,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_OPTICAL_STATE_TOKEN_CAPACITY = 1025,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_CALIBRATION_WORKFLOW_OK = 0,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_INVALID_ARGUMENT,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_IO_ERROR,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_NON_REGULAR_FILE,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_CAPACITY,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_MALFORMED_EVIDENCE,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_PROVENANCE_MISMATCH,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_PROJECT_DB_ERROR,
|
||||
} Lardon3DCalibrationWorkflowResult;
|
||||
|
||||
/* `campaign_state_path` is a canonical external acquisition manifest:
|
||||
*
|
||||
* L3DCAL_CAMPAIGN_STATE_V1
|
||||
* execution <selected-execution-id>
|
||||
* optical_configuration <explicit-v23-configuration-id>
|
||||
* optical_state <sha256> <complete-Science-v1-state-token>
|
||||
* capture <zero-based-selected-item-index> <capture-id>
|
||||
*
|
||||
* Capture rows are contiguous and ordered. The later DB-binding stage proves
|
||||
* that these IDs are exactly the selected execution and that each Capture owns
|
||||
* the declared explicit optical configuration; this input boundary performs no
|
||||
* DB access and therefore never treats the manifest alone as that proof. */
|
||||
typedef struct {
|
||||
const char *session_path;
|
||||
const char *detection_path;
|
||||
const char *solve_path;
|
||||
const char *evidence_path;
|
||||
const char *producer_path;
|
||||
const char *campaign_state_path;
|
||||
} Lardon3DCalibrationWorkflowInputFiles;
|
||||
|
||||
typedef struct {
|
||||
unsigned char session_sha256[32];
|
||||
unsigned char detection_sha256[32];
|
||||
unsigned char solve_sha256[32];
|
||||
unsigned char evidence_sha256[32];
|
||||
unsigned char producer_sha256[32];
|
||||
unsigned char campaign_state_sha256[32];
|
||||
unsigned char optical_state_sha256[32];
|
||||
unsigned char solver_executable_sha256[32];
|
||||
unsigned char solver_configuration_sha256[32];
|
||||
uint64_t selected_execution_id;
|
||||
uint64_t optical_configuration_id;
|
||||
uint32_t capture_count;
|
||||
uint64_t capture_ids[LARDON3D_CALIBRATION_WORKFLOW_MAX_SELECTED_ITEMS];
|
||||
char optical_state_token[LARDON3D_CALIBRATION_WORKFLOW_OPTICAL_STATE_TOKEN_CAPACITY];
|
||||
} Lardon3DCalibrationWorkflowInputBoundary;
|
||||
|
||||
/* Validate only the immutable external input boundary. This function performs
|
||||
* no Project DB access, no calibration solve, no Tooling import and no
|
||||
* selected-execution mutation. Every path is opened O_NONBLOCK/O_NOFOLLOW,
|
||||
* must resolve to a bounded regular file, and is SHA-256 checked before its
|
||||
* syntax/provenance is consumed. */
|
||||
Lardon3DCalibrationWorkflowResult lardon3d_calibration_workflow_validate_input_boundary(
|
||||
const Lardon3DCalibrationWorkflowInputFiles *files,
|
||||
Lardon3DCalibrationWorkflowInputBoundary *boundary);
|
||||
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_CALIBRATION_WORKFLOW_REJECTION_SOURCE_SIZE = 1,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_REJECTION_DECODE = 2,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_REJECTION_DECODED_DIMENSIONS = 3,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_REJECTION_INSUFFICIENT_CHARUCO = 4,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_REJECTION_INVALID_CHARUCO_ID = 5,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_REJECTION_OCCUPANCY = 6,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_REJECTION_TARGET_PHYSICAL_QUADRANTS = 7,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_REJECTION_CLIPPING = 8,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_REJECTION_PRE_SOLVE_CORNER_RMS = 9,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_REJECTION_COORDINATE_EQUIVALENCE = 10,
|
||||
} Lardon3DCalibrationWorkflowRejectionReason;
|
||||
|
||||
typedef struct {
|
||||
Lardon3DCalibrationWorkflowInputBoundary boundary;
|
||||
unsigned char target_sha256[32];
|
||||
unsigned char optical_state_sha256[32];
|
||||
unsigned char solver_executable_sha256[32];
|
||||
unsigned char solver_configuration_sha256[32];
|
||||
unsigned char initialization_evidence_sha256[32];
|
||||
unsigned char validation_evidence_sha256[32];
|
||||
uint32_t target_family;
|
||||
uint32_t target_squares_x;
|
||||
uint32_t target_squares_y;
|
||||
double target_square_length_mm;
|
||||
double target_marker_length_mm;
|
||||
double target_active_width_mm;
|
||||
double target_active_height_mm;
|
||||
double target_white_border_mm;
|
||||
double target_measurements_mm[LARDON3D_CALIBRATION_TOOLING_TARGET_MEASUREMENTS];
|
||||
double measurement_resolution_mm;
|
||||
double target_flatness_mm;
|
||||
double holdout_rmse_px;
|
||||
double holdout_maximum_residual_px;
|
||||
uint32_t extra_distortion_coefficient_count;
|
||||
/* Exact decoded/oriented geometry shared by every accepted calibration view.
|
||||
* It is retained from detection.json + coordinate evidence and is never
|
||||
* inferred from camera parameters or campaign images. */
|
||||
uint32_t oriented_width;
|
||||
uint32_t oriented_height;
|
||||
const Lardon3DCalibrationToolingView *views;
|
||||
size_t view_count;
|
||||
const Lardon3DCalibrationToolingCoordinateCheck *coordinate_checks;
|
||||
size_t coordinate_check_count;
|
||||
double repeated_parameters[3][8];
|
||||
double fit_parameters[8];
|
||||
uint32_t support_images;
|
||||
uint32_t support_observations;
|
||||
double reprojection_rmse_px;
|
||||
double maximum_residual_px;
|
||||
double high_residual_fraction;
|
||||
double maximum_parameter_delta;
|
||||
uint32_t validation_flags;
|
||||
} Lardon3DCalibrationWorkflowExternalEvidence;
|
||||
|
||||
/* Materialize the already validated external session and solver bundle into
|
||||
* bounded Science-v1 evidence. Caller owns `views` and `coordinate_checks`;
|
||||
* output borrows those arrays on success. This stage performs no Project DB
|
||||
* access, no Tooling import and no selected-execution mutation. */
|
||||
Lardon3DCalibrationWorkflowResult
|
||||
lardon3d_calibration_workflow_materialize_external_evidence(
|
||||
const Lardon3DCalibrationWorkflowInputFiles *files,
|
||||
Lardon3DCalibrationToolingView *views, size_t view_capacity,
|
||||
Lardon3DCalibrationToolingCoordinateCheck *coordinate_checks,
|
||||
size_t coordinate_check_capacity,
|
||||
Lardon3DCalibrationWorkflowExternalEvidence *output);
|
||||
|
||||
/* Bind already materialized external evidence to the exact durable selected
|
||||
* execution. This stage is read-only: it verifies selected item order/Capture
|
||||
* identity, explicit v23 optical assignment, managed representation
|
||||
* size/SHA-256, safe project-relative regular-file access and the exact
|
||||
* OpenCV-decoded oriented dimensions. Caller owns `entries`; `output` borrows
|
||||
* them on success. No Tooling import or selected-execution mutation occurs. */
|
||||
Lardon3DCalibrationWorkflowResult
|
||||
lardon3d_calibration_workflow_bind_selected_execution(
|
||||
Lardon3DProjectDb *database, const char *project_path,
|
||||
const Lardon3DCalibrationWorkflowExternalEvidence *external,
|
||||
Lardon3DCalibrationToolingEntry *entries, size_t entry_capacity,
|
||||
Lardon3DCalibrationToolingEvidence *output);
|
||||
|
||||
/* Complete the bounded calibration workflow through the frozen Tooling and
|
||||
* Bootstrap importer. All supplied arrays and artifact storage remain caller
|
||||
* owned; `output` is cleared unless import reaches the exact READY transition.
|
||||
* Input validation, materialization and selected-execution binding finish
|
||||
* before this function invokes the only mutating operation. Exact retries use
|
||||
* Bootstrap's immutable import semantics. */
|
||||
Lardon3DCalibrationWorkflowResult
|
||||
lardon3d_calibration_workflow_complete(
|
||||
Lardon3DProjectDb *database, const char *project_path,
|
||||
const Lardon3DCalibrationWorkflowInputFiles *files,
|
||||
Lardon3DCalibrationToolingView *views, size_t view_capacity,
|
||||
Lardon3DCalibrationToolingCoordinateCheck *coordinate_checks,
|
||||
size_t coordinate_check_capacity, Lardon3DCalibrationToolingEntry *entries,
|
||||
size_t entry_capacity, unsigned char *artifact, size_t artifact_capacity,
|
||||
size_t *artifact_size, Lardon3DCalibrationBootstrapOutput *output);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
#ifndef LARDON3D_CALIBRATION_WORKFLOW_V2_H
|
||||
#define LARDON3D_CALIBRATION_WORKFLOW_V2_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <lardon3d/calibration_bootstrap_v2.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_CALIBRATION_WORKFLOW_V2_AUTOMATIC = 1,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_V2_EXISTING_EXPLICIT = 2,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_V2_PUBLISH_EXPLICIT = 3,
|
||||
} Lardon3DCalibrationWorkflowV2BindingKind;
|
||||
|
||||
typedef struct {
|
||||
uint32_t selected_item_index;
|
||||
/* Capture identity must be copied from the selected execution's durable item
|
||||
* mapping. Workflow rejects any mismatch and never derives it from image_id,
|
||||
* artifact group number, path, filename, or SHA-256. */
|
||||
uint64_t capture_id;
|
||||
Lardon3DCalibrationWorkflowV2BindingKind kind;
|
||||
/* EXISTING_EXPLICIT requires an existing exact v26 applicability. */
|
||||
uint64_t applicability_id;
|
||||
/* PUBLISH_EXPLICIT uses this Capture's complete observed tuple as the exact
|
||||
* applicability exemplar. It may equal capture_id. Other modes require 0. */
|
||||
uint64_t exemplar_capture_id;
|
||||
} Lardon3DCalibrationWorkflowV2Binding;
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_CALIBRATION_WORKFLOW_V2_READY = 0,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_V2_CALIBRATION_REQUIRED,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_V2_SELECTION_REQUIRED,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_V2_INVALID_ARGUMENT,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_V2_INVALID_EVIDENCE,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_V2_ASSIGNMENT_CONFLICT,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_V2_DB_ERROR,
|
||||
LARDON3D_CALIBRATION_WORKFLOW_V2_OUT_OF_MEMORY,
|
||||
} Lardon3DCalibrationWorkflowV2Result;
|
||||
|
||||
typedef struct {
|
||||
Lardon3DCalibrationBootstrapV2Output publication;
|
||||
uint32_t selected_item_count;
|
||||
} Lardon3DCalibrationWorkflowV2Output;
|
||||
|
||||
/* Compose heterogeneous L3DCALB2 publication with exact Project DB v26
|
||||
* applicability and the final selected-execution READY transition.
|
||||
*
|
||||
* All pointers are borrowed for the call. `bindings` must cover every selected
|
||||
* item exactly once and must repeat the durable selected item -> Capture
|
||||
* mapping. AUTOMATIC accepts exactly one compatible applicability and reports
|
||||
* CALIBRATION_REQUIRED/SELECTION_REQUIRED truthfully for zero/multiple choices.
|
||||
* EXISTING_EXPLICIT verifies and durably selects the requested exact
|
||||
* applicability. PUBLISH_EXPLICIT creates/reuses deterministic optical profile
|
||||
* metadata for the artifact calibration, creates/reuses exact exemplar
|
||||
* applicability, and selects it explicitly.
|
||||
*
|
||||
* READY is returned only when every durable Capture resolves to the same
|
||||
* calibration_id as its complete scope member and that exact scope is attached.
|
||||
* Invalid evidence or an assignment mismatch is a distinct non-ready error.
|
||||
* Pre-attachment failure never attaches a scope; immutable publication and
|
||||
* optical evidence created by an earlier phase may remain and exact retry
|
||||
* converges. No schema change, solver execution, or Capture inference occurs.
|
||||
*/
|
||||
Lardon3DCalibrationWorkflowV2Result lardon3d_calibration_workflow_v2_complete(
|
||||
Lardon3DProjectDb *database, uint64_t execution_id,
|
||||
const unsigned char *artifact, size_t artifact_size,
|
||||
const unsigned char
|
||||
expected_artifact_sha256[LARDON3D_PROJECT_DB_SHA256_SIZE],
|
||||
const Lardon3DCalibrationWorkflowV2Binding *bindings, size_t binding_count,
|
||||
Lardon3DCalibrationWorkflowV2Output *output);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -9,9 +9,9 @@
|
|||
#include <lardon3d/visual_index.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t generated_count; // newly created pairs
|
||||
uint32_t skipped_count; // already existing pairs (idempotent reuse)
|
||||
uint32_t queried_count; // candidates returned by the Visual Index
|
||||
uint32_t generated_count; // paires nouvellement créées
|
||||
uint32_t skipped_count; // paires déjà existantes (idempotence)
|
||||
uint32_t queried_count; // candidats retournés par le Visual Index
|
||||
} Lardon3DCandidatePairGenStats;
|
||||
|
||||
Lardon3DVisualIndexResult lardon3d_candidate_pair_generate(
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@
|
|||
|
||||
#define LARDON3D_FEATURE_EXTRACT_TASK_KIND "features.extract"
|
||||
enum { LARDON3D_FEATURE_EXTRACT_TASK_KIND_VERSION = 1 };
|
||||
#define LARDON3D_FEATURE_EXTRACT_BATCH_TASK_KIND "features.extract.batch"
|
||||
enum { LARDON3D_FEATURE_EXTRACT_BATCH_TASK_KIND_VERSION = 1 };
|
||||
|
||||
Lardon3DTask *
|
||||
lardon3d_project_create_feature_extract_task(Lardon3DAppState *state, uint64_t image_id,
|
||||
|
|
@ -22,18 +20,5 @@ bool lardon3d_project_enqueue_feature_extract(Lardon3DAppState *state, uint64_t
|
|||
uint64_t *task_id);
|
||||
bool lardon3d_feature_extract_reconstruct(const Lardon3DTaskDurableSnapshot *snapshot,
|
||||
void *context, Lardon3DTaskKindBinding *binding);
|
||||
/* Create/enqueue one durable owner Task over the immutable selected-execution
|
||||
* image order. ORB parameters are copied into typed v25 state; participants are
|
||||
* Governor-bounded, cancellable at window boundaries, and joined before the
|
||||
* owner publishes or advances its cursor. */
|
||||
Lardon3DTask *lardon3d_project_create_feature_extract_batch_task(
|
||||
Lardon3DAppState *state, uint64_t selected_execution_id,
|
||||
const Lardon3DFeatureExtractorParameters *parameters, uint64_t *task_id);
|
||||
bool lardon3d_project_enqueue_feature_extract_batch(
|
||||
Lardon3DAppState *state, uint64_t selected_execution_id,
|
||||
const Lardon3DFeatureExtractorParameters *parameters, uint64_t *task_id);
|
||||
bool lardon3d_feature_extract_batch_reconstruct(
|
||||
const Lardon3DTaskDurableSnapshot *snapshot, void *context,
|
||||
Lardon3DTaskKindBinding *binding);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ bool lardon3d_project_enqueue_image_import(Lardon3DAppState *state, uint64_t sca
|
|||
bool lardon3d_image_import_reconstruct(const Lardon3DTaskDurableSnapshot *snapshot, void *context,
|
||||
Lardon3DTaskKindBinding *binding);
|
||||
|
||||
/* TUI compatibility: lightweight handle over a Queue Task, with no private thread. */
|
||||
/* Compatibilité TUI : handle léger sur une tâche de la queue, sans thread. */
|
||||
Lardon3DImportTask *lardon3d_import_task_create(void);
|
||||
bool lardon3d_import_task_start(Lardon3DImportTask *task, Lardon3DAppState *state,
|
||||
const char *source_directory);
|
||||
|
|
|
|||
|
|
@ -15,10 +15,6 @@ enum {
|
|||
LARDON3D_OPTICAL_TEXT_CAPACITY = 128,
|
||||
LARDON3D_OPTICAL_PROVENANCE_CAPACITY = 256,
|
||||
LARDON3D_OPTICAL_PAGE_MAX = 128,
|
||||
LARDON3D_OPTICAL_FOCUS_DOMAIN_DIGEST_SIZE = 32,
|
||||
/* This is an operational persistence/API bound, not a scientific focus
|
||||
* range. Every member remains one opaque exact observation token. */
|
||||
LARDON3D_OPTICAL_FOCUS_DOMAIN_TOKEN_MAX = 64,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
|
|
@ -123,86 +119,6 @@ typedef struct {
|
|||
uint64_t sparse_calibration_id;
|
||||
} Lardon3DOpticalCaptureCalibrationSelection;
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_OPTICAL_OBSERVATION_UNKNOWN = 1,
|
||||
LARDON3D_OPTICAL_OBSERVATION_OBSERVED = 2,
|
||||
} Lardon3DOpticalObservationState;
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_OPTICAL_STABILIZATION_UNKNOWN = 1,
|
||||
LARDON3D_OPTICAL_STABILIZATION_OFF = 2,
|
||||
LARDON3D_OPTICAL_STABILIZATION_ON = 3,
|
||||
} Lardon3DOpticalStabilizationState;
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_OPTICAL_GEOMETRIC_STATE_METADATA = 1,
|
||||
LARDON3D_OPTICAL_GEOMETRIC_STATE_CALLER_EXPLICIT = 2,
|
||||
} Lardon3DOpticalGeometricStateProvenance;
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_OPTICAL_CALIBRATION_REQUIRED = 1,
|
||||
LARDON3D_OPTICAL_CALIBRATION_RESOLVED = 2,
|
||||
LARDON3D_OPTICAL_CALIBRATION_SELECTION_REQUIRED = 3,
|
||||
} Lardon3DOpticalCalibrationResolutionKind;
|
||||
|
||||
typedef struct {
|
||||
uint64_t capture_id;
|
||||
uint64_t optical_configuration_id;
|
||||
uint32_t state_version;
|
||||
Lardon3DOpticalGeometricStateProvenance provenance;
|
||||
Lardon3DOpticalObservationState focus_state;
|
||||
/* Focus is an exact, bounded observation token, not a physical-distance or
|
||||
* autofocus domain. Empty is required when focus is explicitly unknown. */
|
||||
char focus_observation[LARDON3D_OPTICAL_TEXT_CAPACITY];
|
||||
Lardon3DOpticalObservationState aperture_state;
|
||||
uint32_t aperture_x1000;
|
||||
Lardon3DOpticalStabilizationState stabilization;
|
||||
Lardon3DOpticalObservationState crop_state;
|
||||
char crop_observation[LARDON3D_OPTICAL_TEXT_CAPACITY];
|
||||
Lardon3DOpticalObservationState pipeline_state;
|
||||
char pipeline_observation[LARDON3D_OPTICAL_TEXT_CAPACITY];
|
||||
Lardon3DOpticalObservationState representation_state;
|
||||
char representation_observation[LARDON3D_OPTICAL_TEXT_CAPACITY];
|
||||
Lardon3DOpticalObservationState decoded_geometry_state;
|
||||
uint32_t decoded_width;
|
||||
uint32_t decoded_height;
|
||||
} Lardon3DOpticalCaptureGeometricState;
|
||||
|
||||
typedef struct {
|
||||
uint64_t applicability_id;
|
||||
uint64_t calibration_profile_id;
|
||||
uint64_t optical_configuration_id;
|
||||
uint64_t exemplar_capture_id;
|
||||
} Lardon3DOpticalCalibrationApplicabilityV2;
|
||||
|
||||
typedef struct {
|
||||
uint64_t focus_domain_id;
|
||||
/* The v26 applicability retains calibration/configuration identity and its
|
||||
* exemplar retains the exact complete non-focus geometric tuple. */
|
||||
uint64_t applicability_id;
|
||||
uint64_t calibration_profile_id;
|
||||
uint64_t optical_configuration_id;
|
||||
uint64_t exemplar_capture_id;
|
||||
uint32_t domain_version;
|
||||
unsigned char evidence_sha256[LARDON3D_OPTICAL_FOCUS_DOMAIN_DIGEST_SIZE];
|
||||
uint32_t token_count;
|
||||
} Lardon3DOpticalFocusDomainV2;
|
||||
|
||||
typedef struct {
|
||||
Lardon3DOpticalCalibrationResolutionKind kind;
|
||||
uint64_t applicability_id;
|
||||
uint64_t calibration_profile_id;
|
||||
uint64_t sparse_calibration_id;
|
||||
} Lardon3DOpticalCalibrationResolutionV2;
|
||||
|
||||
typedef struct {
|
||||
uint64_t capture_id;
|
||||
uint64_t applicability_id;
|
||||
uint64_t calibration_profile_id;
|
||||
uint64_t optical_configuration_id;
|
||||
uint64_t sparse_calibration_id;
|
||||
} Lardon3DOpticalCaptureCalibrationSelectionV2;
|
||||
|
||||
/* All profile/config creation calls borrow input only for the call and return a
|
||||
* caller-owned, NUL-terminated copy; input and output storage must not overlap.
|
||||
* The generated row-ID field in a create input must be zero; referenced IDs
|
||||
|
|
@ -331,64 +247,6 @@ Lardon3DProjectDbResult lardon3d_optical_capture_calibration_selection_load(
|
|||
Lardon3DProjectDb *database, uint64_t capture_id,
|
||||
Lardon3DOpticalCaptureCalibrationSelection *output);
|
||||
|
||||
/* v26 state is Capture-owned and separate from both Capture identity and the
|
||||
* v23 body/lens/focal configuration. Unknown values remain explicit. Create is
|
||||
* immutable: an exact retry is idempotent and any differing tuple conflicts. */
|
||||
Lardon3DProjectDbResult lardon3d_optical_capture_geometric_state_create(
|
||||
Lardon3DProjectDb *database,
|
||||
const Lardon3DOpticalCaptureGeometricState *input,
|
||||
Lardon3DOpticalCaptureGeometricState *output);
|
||||
Lardon3DProjectDbResult lardon3d_optical_capture_geometric_state_load(
|
||||
Lardon3DProjectDb *database, uint64_t capture_id,
|
||||
Lardon3DOpticalCaptureGeometricState *output);
|
||||
|
||||
/* Applicability binds an existing v1 calibration profile to the exemplar's
|
||||
* exact configuration and complete observed-state tuple. It authorizes no
|
||||
* body/lens/focal substitution, unknown default, interpolation or extrapolation. */
|
||||
Lardon3DProjectDbResult lardon3d_optical_calibration_applicability_v2_create(
|
||||
Lardon3DProjectDb *database, uint64_t calibration_profile_id,
|
||||
uint64_t exemplar_capture_id,
|
||||
Lardon3DOpticalCalibrationApplicabilityV2 *output);
|
||||
|
||||
/* Attach one physically validated discrete focus domain to an existing exact
|
||||
* v26 applicability. evidence_sha256 must be a nonzero retained-evidence or
|
||||
* provenance digest. focus_tokens contains 1..FOCUS_DOMAIN_TOKEN_MAX distinct,
|
||||
* nonempty, NUL-terminated opaque tokens; the call borrows the array and
|
||||
* strings only for its duration. Members are stored as a deterministic exact
|
||||
* set: order has no meaning, exact retry is idempotent, and conflicting reuse
|
||||
* of the applicability is CONSTRAINT. The API performs no numeric conversion,
|
||||
* EXIF interpretation, interpolation, extrapolation, or physical validation.
|
||||
* output is mandatory. On OK, it receives the durable focus domain and its
|
||||
* attached applicability identity; for valid calls it is zeroed on non-OK. */
|
||||
Lardon3DProjectDbResult lardon3d_optical_focus_domain_v2_create(
|
||||
Lardon3DProjectDb *database, uint64_t applicability_id,
|
||||
uint32_t domain_version,
|
||||
const unsigned char evidence_sha256[LARDON3D_OPTICAL_FOCUS_DOMAIN_DIGEST_SIZE],
|
||||
const char *const *focus_tokens, size_t token_count,
|
||||
Lardon3DOpticalFocusDomainV2 *output);
|
||||
|
||||
/* Resolution counts distinct eligible applicability rows: exact v26 state
|
||||
* matches plus v27 domains whose non-focus geometry is exact and whose focus
|
||||
* token is an exact member. NONE is CALIBRATION_REQUIRED, ONE is RESOLVED, and
|
||||
* MANY is SELECTION_REQUIRED. All are successful outcomes. */
|
||||
Lardon3DProjectDbResult lardon3d_optical_capture_calibration_resolve_v2(
|
||||
Lardon3DProjectDb *database, uint64_t capture_id,
|
||||
Lardon3DOpticalCalibrationResolutionV2 *output);
|
||||
/* Selection requires a complete observed geometric-state tuple and an
|
||||
* applicability currently eligible by exact v26 match or exact v27 domain
|
||||
* membership; UNKNOWN in any geometry-relevant field is a CONSTRAINT. The
|
||||
* first selection is immutable, exact retry is idempotent, and a conflicting
|
||||
* applicability is rejected without changing the selection. */
|
||||
Lardon3DProjectDbResult lardon3d_optical_capture_calibration_select_v2(
|
||||
Lardon3DProjectDb *database, uint64_t capture_id, uint64_t applicability_id);
|
||||
/* Loads the Capture's durable v2 selection without creating or changing it.
|
||||
* NOT_FOUND means no selection exists. OK returns the complete stored tuple
|
||||
* only when its applicability remains exactly compatible with the Capture's
|
||||
* complete observed geometric state; broken dependencies are CORRUPT. */
|
||||
Lardon3DProjectDbResult lardon3d_optical_capture_calibration_selection_load_v2(
|
||||
Lardon3DProjectDb *database, uint64_t capture_id,
|
||||
Lardon3DOpticalCaptureCalibrationSelectionV2 *output);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -73,21 +73,9 @@ Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_photo_quality_ta
|
|||
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_raw_development_task(
|
||||
Lardon3DAppState *state, const Lardon3DTask *task,
|
||||
const Lardon3DProjectDbRawDevelopmentTask *parameters);
|
||||
/* Persist generic parent Task state atomically with its immutable selected
|
||||
* execution association. Call only after the selected execution cursor commit
|
||||
* so generic progress can lag but never lead scientific publication. */
|
||||
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_raw_development_batch_task(
|
||||
Lardon3DAppState *state, const Lardon3DTask *task,
|
||||
const Lardon3DProjectDbRawDevelopmentBatchTask *parameters);
|
||||
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_feature_extract_task(
|
||||
Lardon3DAppState *state, const Lardon3DTask *task,
|
||||
const Lardon3DProjectDbFeatureExtractTask *parameters);
|
||||
/* Persist the generic checkpoint and typed batch domain/cursor in one SQLite
|
||||
* transaction. Call only after owner-only Feature publication and cursor
|
||||
* advancement, so generic progress may lag but never lead durable work. */
|
||||
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_feature_extract_batch_task(
|
||||
Lardon3DAppState *state, const Lardon3DTask *task,
|
||||
const Lardon3DProjectDbFeatureExtractBatchTask *parameters);
|
||||
Lardon3DProjectTaskCheckpointResult lardon3d_project_checkpoint_sift_extract_task(
|
||||
Lardon3DAppState *state, const Lardon3DTask *task,
|
||||
const Lardon3DProjectDbSiftExtractTask *parameters);
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@
|
|||
#include <lardon3d/sparse_sfm_incremental.h>
|
||||
|
||||
enum {
|
||||
/* v27 additively stores validated focus domains as bounded sets of exact
|
||||
* observed tokens attached to v26 applicability. Migration creates no
|
||||
* domain or token from historical geometric state. */
|
||||
LARDON3D_PROJECT_DB_SCHEMA_VERSION = 27,
|
||||
/* v23 is an additive optical-context layer. Historical v22 rows and sparse
|
||||
* calibration identities remain untouched; migration never guesses profiles
|
||||
* or assignments for existing Captures. */
|
||||
LARDON3D_PROJECT_DB_SCHEMA_VERSION = 23,
|
||||
LARDON3D_PROJECT_DB_ID_CAPACITY = 65,
|
||||
LARDON3D_PROJECT_DB_KIND_CAPACITY = 65,
|
||||
LARDON3D_PROJECT_DB_PATH_CAPACITY = 4096,
|
||||
|
|
@ -145,13 +145,6 @@ typedef struct {
|
|||
uint64_t image_id;
|
||||
} Lardon3DProjectDbRawDevelopmentTask;
|
||||
|
||||
typedef struct {
|
||||
uint64_t task_id;
|
||||
/* Selected execution owns the ordered scientific inputs and durable cursor.
|
||||
* This typed Task row deliberately copies none of those identities. */
|
||||
uint64_t selected_execution_id;
|
||||
} Lardon3DProjectDbRawDevelopmentBatchTask;
|
||||
|
||||
typedef struct {
|
||||
uint64_t task_id;
|
||||
uint32_t group_id;
|
||||
|
|
@ -413,19 +406,6 @@ typedef struct {
|
|||
unsigned char parameter_fingerprint[32];
|
||||
} Lardon3DProjectDbFeatureExtractTask;
|
||||
|
||||
typedef struct {
|
||||
uint64_t task_id;
|
||||
uint64_t selected_execution_id;
|
||||
/* Zero-based durable prefix in the immutable selected-execution order. */
|
||||
uint32_t next_item_index;
|
||||
char extractor_kind[LARDON3D_PROJECT_DB_KIND_CAPACITY];
|
||||
uint32_t extractor_version;
|
||||
uint32_t max_features;
|
||||
uint32_t pyramid_levels;
|
||||
uint32_t fast_threshold;
|
||||
unsigned char parameter_fingerprint[LARDON3D_PROJECT_DB_SHA256_SIZE];
|
||||
} Lardon3DProjectDbFeatureExtractBatchTask;
|
||||
|
||||
typedef struct {
|
||||
uint64_t task_id;
|
||||
uint64_t image_id;
|
||||
|
|
@ -805,21 +785,6 @@ Lardon3DProjectDbResult lardon3d_project_db_record_raw_development_task(
|
|||
Lardon3DProjectDbResult lardon3d_project_db_load_raw_development_task(
|
||||
Lardon3DProjectDb *database, uint64_t task_id,
|
||||
Lardon3DProjectDbRawDevelopmentTask *parameters);
|
||||
/* Atomically record generic raw.develop.batch/1 state and its immutable link
|
||||
* to one selected execution. Exact retry is idempotent; rebinding either a
|
||||
* Task or selected execution conflicts. The selected execution cursor remains
|
||||
* the sole durable item-progress authority. */
|
||||
Lardon3DProjectDbResult lardon3d_project_db_record_raw_development_batch_task(
|
||||
Lardon3DProjectDb *database, const Lardon3DTaskDurableSnapshot *snapshot,
|
||||
const char *task_kind, uint32_t task_kind_version,
|
||||
const Lardon3DProjectDbCheckpoint *checkpoint,
|
||||
const Lardon3DProjectDbRawDevelopmentBatchTask *parameters, int64_t updated_at);
|
||||
/* Load the caller-owned immutable Task -> selected-execution association.
|
||||
* Malformed integer storage or a generic kind/version mismatch is corruption;
|
||||
* no item identity or cursor is inferred from other durable state. */
|
||||
Lardon3DProjectDbResult lardon3d_project_db_load_raw_development_batch_task(
|
||||
Lardon3DProjectDb *database, uint64_t task_id,
|
||||
Lardon3DProjectDbRawDevelopmentBatchTask *parameters);
|
||||
/* Result publication and next_group_id advance are atomic. result->group_id is
|
||||
* the canonical plan ID in 1..N and must equal the task's current one-based
|
||||
* cursor; next_group_id must equal result->group_id+1 (N+1 after the last
|
||||
|
|
@ -904,26 +869,6 @@ Lardon3DProjectDbResult lardon3d_project_db_record_feature_extract_task(
|
|||
Lardon3DProjectDbResult
|
||||
lardon3d_project_db_load_feature_extract_task(Lardon3DProjectDb *database, uint64_t task_id,
|
||||
Lardon3DProjectDbFeatureExtractTask *parameters);
|
||||
/* Atomically persist generic features.extract.batch/1 state and its typed
|
||||
* immutable selected-execution/ORB domain plus monotone cursor. A stale
|
||||
* generic checkpoint may repeat an older cursor but can never move it back. */
|
||||
Lardon3DProjectDbResult lardon3d_project_db_record_feature_extract_batch_task(
|
||||
Lardon3DProjectDb *database, const Lardon3DTaskDurableSnapshot *snapshot,
|
||||
const char *task_kind, uint32_t task_kind_version,
|
||||
const Lardon3DProjectDbCheckpoint *checkpoint,
|
||||
const Lardon3DProjectDbFeatureExtractBatchTask *parameters, int64_t updated_at);
|
||||
/* Load and validate the exact typed batch domain and generic dispatch. The
|
||||
* selected execution must be representation-complete and the cursor bounded by
|
||||
* its immutable item count; malformed durable state is CORRUPT. */
|
||||
Lardon3DProjectDbResult lardon3d_project_db_load_feature_extract_batch_task(
|
||||
Lardon3DProjectDb *database, uint64_t task_id,
|
||||
Lardon3DProjectDbFeatureExtractBatchTask *parameters);
|
||||
/* Advance exactly one ordered item only after its selected image has a READY
|
||||
* Feature Set for the task's immutable ORB fingerprint. Exact retry after an
|
||||
* already committed advance converges; no image identity is inferred. */
|
||||
Lardon3DProjectDbResult lardon3d_project_db_advance_feature_extract_batch_task(
|
||||
Lardon3DProjectDb *database, uint64_t task_id, uint32_t item_index,
|
||||
uint32_t next_item_index);
|
||||
Lardon3DProjectDbResult lardon3d_project_db_record_sift_extract_task(
|
||||
Lardon3DProjectDb *database, const Lardon3DTaskDurableSnapshot *snapshot,
|
||||
const char *task_kind, uint32_t task_kind_version,
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ extern "C" {
|
|||
|
||||
#define LARDON3D_RAW_DEVELOPMENT_TASK_KIND "raw.develop"
|
||||
enum { LARDON3D_RAW_DEVELOPMENT_TASK_KIND_VERSION = 1 };
|
||||
#define LARDON3D_RAW_DEVELOPMENT_BATCH_TASK_KIND "raw.develop.batch"
|
||||
enum { LARDON3D_RAW_DEVELOPMENT_BATCH_TASK_KIND_VERSION = 1 };
|
||||
|
||||
/* Create one durable, initially unqueued S3-B1 Task for the exact existing
|
||||
* Capture-owned SOURCE RAW asset. The two IDs are immutable and are never
|
||||
|
|
@ -39,25 +37,6 @@ bool lardon3d_raw_development_task_reconstruct(
|
|||
const Lardon3DTaskDurableSnapshot *snapshot, void *context,
|
||||
Lardon3DTaskKindBinding *binding);
|
||||
|
||||
/* Create one durable, initially unqueued parent Task for the exact immutable
|
||||
* selected execution. The selected execution, not Task identity, owns item
|
||||
* order, explicit Capture/SOURCE RAW identity, and the durable cursor. Queue
|
||||
* admission bounds each parallel window; the caller owns the returned Task. */
|
||||
Lardon3DTask *lardon3d_project_create_raw_development_batch_task(
|
||||
Lardon3DAppState *state, uint64_t selected_execution_id, uint64_t *task_id);
|
||||
|
||||
/* Create and transfer the parent Task to state's existing sole Queue. The
|
||||
* Queue/Governor reservation covers only one bounded window at a time and is
|
||||
* released/re-established at each sequence boundary. */
|
||||
bool lardon3d_project_enqueue_raw_development_batch(
|
||||
Lardon3DAppState *state, uint64_t selected_execution_id, uint64_t *task_id);
|
||||
|
||||
/* Reconstruct only from the validated v24 Task -> selected execution row.
|
||||
* binding owns its returned context until the runtime invokes its destroyer. */
|
||||
bool lardon3d_raw_development_batch_task_reconstruct(
|
||||
const Lardon3DTaskDurableSnapshot *snapshot, void *context,
|
||||
Lardon3DTaskKindBinding *binding);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -325,11 +325,11 @@ const char *lardon3d_resource_decision_name(
|
|||
Lardon3DResourcePressure lardon3d_resource_governor_pressure(
|
||||
Lardon3DResourceGovernor *governor
|
||||
);
|
||||
/* Records metrics from one completed batch for dynamic adaptation of future
|
||||
* batch sizes. batch_size is the number of items whose processing was validated
|
||||
* in this batch. peak_memory_bytes == 0 means the measurement is unknown and
|
||||
* never feeds memory adaptation. The buffer is bounded to 8 entries per Task
|
||||
* class. Thread-safe. */
|
||||
/* Enregistre les métriques d'un lot terminé pour l'adaptation dynamique
|
||||
* de la taille des lots futurs. batch_size est le nombre d'éléments dont le
|
||||
* traitement a été validé dans ce lot. peak_memory_bytes == 0 signifie que
|
||||
* la mesure est inconnue et n'alimente jamais l'adaptation mémoire. Le buffer
|
||||
* est borné (8 entrées par classe de tâche). Thread-safe. */
|
||||
bool lardon3d_resource_governor_record_batch(
|
||||
Lardon3DResourceGovernor *governor,
|
||||
Lardon3DResourceTaskClass task_class,
|
||||
|
|
|
|||
|
|
@ -108,8 +108,8 @@ Lardon3DTask *lardon3d_task_create_typed(
|
|||
Lardon3DTaskUserdataDestroy userdata_destroy
|
||||
);
|
||||
void lardon3d_task_destroy(Lardon3DTask *task);
|
||||
/* Executes the callback on the calling thread. The callback runs outside
|
||||
* the Task mutex; the execution contract and state remain owned by the Task.
|
||||
/* Exécute le callback dans le thread appelant. Le callback est invoqué hors
|
||||
* mutex de tâche; le contract d'exécution et l'état appartiennent à la tâche.
|
||||
*/
|
||||
bool lardon3d_task_start(
|
||||
Lardon3DTask *task,
|
||||
|
|
@ -169,16 +169,16 @@ Lardon3DTask *lardon3d_task_restore_typed(
|
|||
void *userdata,
|
||||
Lardon3DTaskUserdataDestroy userdata_destroy
|
||||
);
|
||||
/* A successful typed restore transfers userdata/userdata_destroy ownership
|
||||
* to the Task. On failure, the caller retains ownership. */
|
||||
/* Une restauration typée réussie transfère userdata/userdata_destroy à la
|
||||
* tâche. En cas d'échec, l'appelant en reste propriétaire. */
|
||||
bool lardon3d_task_kind_is_valid(const char *task_kind);
|
||||
bool lardon3d_task_kind(
|
||||
const Lardon3DTask *task,
|
||||
char task_kind[LARDON3D_TASK_KIND_CAPACITY],
|
||||
uint32_t *task_kind_version
|
||||
);
|
||||
/* Invoked at most once, outside the Task mutex and after the terminal
|
||||
* reservation is released. Task userdata remains alive until the callback returns. */
|
||||
/* Appelé au plus une fois, hors mutex de tâche et après libération de la
|
||||
* réservation terminale. Le userdata de tâche reste vivant jusqu'au retour. */
|
||||
bool lardon3d_task_set_finished_callback(
|
||||
Lardon3DTask *task,
|
||||
Lardon3DTaskFinishedCallback callback,
|
||||
|
|
@ -190,20 +190,21 @@ bool lardon3d_task_resource_estimate(
|
|||
const Lardon3DTask *task,
|
||||
Lardon3DResourceEstimate *estimate
|
||||
);
|
||||
/* Execution does not receive admission policy: the Governor must confirm
|
||||
* the reservation before execution.
|
||||
/* L'exécution ne reçoit pas de politique d'admission : c'est au gouverneur de
|
||||
* confirmer la réservation avant l'exécution.
|
||||
*/
|
||||
bool lardon3d_task_execution_contract(
|
||||
const Lardon3DTask *task,
|
||||
Lardon3DTaskExecutionContract *contract
|
||||
);
|
||||
/* Releases the current reservation, obtains a new one from the Governor,
|
||||
* and updates the execution contract. Call only from the currently executing
|
||||
* callback. A Governor WAIT is temporary unavailability: this function waits
|
||||
* for a resource change and retries admission without failing the Task. Batch
|
||||
* bounds continue under the new admission. Returns false if the Task is
|
||||
* cancelled (TASK_CANCELLED), the Governor returns REJECT, or an internal
|
||||
* error fails the Task (TASK_FAILED). */
|
||||
/* Libère la réservation courante, en obtient une nouvelle auprès du gouverneur
|
||||
* et met à jour le contrat. À appeler uniquement depuis le callback en cours
|
||||
* d'exécution. Une réponse WAIT du gouverneur est une indisponibilité
|
||||
* temporaire : la fonction attend un changement de ressources puis retente
|
||||
* l'admission sans échouer la tâche. Les bornes de lot se poursuivent après
|
||||
* cette nouvelle admission. Retourne false si la tâche est annulée
|
||||
* (TASK_CANCELLED), si le gouverneur répond REJECT ou en cas d'erreur interne
|
||||
* (TASK_FAILED). */
|
||||
bool lardon3d_task_sequence_break(
|
||||
Lardon3DTask *task,
|
||||
Lardon3DResourceGovernor *governor,
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ typedef struct {
|
|||
* serialized worker. capacity bounds pending Tasks, not terminal history; the
|
||||
* Governor retains resource-admission ownership and must outlive the Queue.
|
||||
*
|
||||
* File d'exécution bornée, à ordre d'attente FIFO avec sélection adaptative du
|
||||
* premier travail admissible, et un seul worker: ownership d'ordonnancement et
|
||||
* de backpressure seulement. L'admission des demandes reste au Governneur.
|
||||
*
|
||||
* CONTRACT: after a successful add, the Queue owns Task and its userdata. Once
|
||||
* the Task is terminal and its finished callback has returned, the Queue keeps
|
||||
* only a snapshot and promptly destroys the real Task outside the Queue lock.
|
||||
|
|
@ -69,8 +73,8 @@ Lardon3DTaskQueue *lardon3d_task_queue_create(
|
|||
* starts after destruction safe. NULL is accepted. Never call destroy
|
||||
* synchronously from a Task finished callback running on this Queue. */
|
||||
void lardon3d_task_queue_destroy(Lardon3DTaskQueue *queue);
|
||||
/* The Queue takes ownership of task only on success.
|
||||
* Blocking: waits for a free slot when the Queue is full. A zero Task ID is
|
||||
/* La file devient propriétaire de task uniquement en cas de succès.
|
||||
* Bloquante : attend une place libre si la file est pleine. A zero Task ID is
|
||||
* assigned from a nonzero monotonic sequence and is never generated twice
|
||||
* during this Queue lifetime, including after terminal-history eviction or
|
||||
* removal. Once UINT64_MAX has been generated (or consumed by a restored
|
||||
|
|
@ -81,8 +85,8 @@ bool lardon3d_task_queue_add(
|
|||
Lardon3DTask *task,
|
||||
uint64_t *task_id
|
||||
);
|
||||
/* Non-blocking: returns false if the Queue is full or stopping.
|
||||
The Queue takes ownership of task only on success. */
|
||||
/* Non-bloquante : retourne false si la file est pleine ou en arrêt.
|
||||
La file devient propriétaire de task uniquement en cas de succès. */
|
||||
bool lardon3d_task_queue_try_add(
|
||||
Lardon3DTaskQueue *queue,
|
||||
Lardon3DTask *task,
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ typedef enum {
|
|||
LARDON3D_TRACK_BUILDER_PROJECT_DATABASE_ERROR,
|
||||
LARDON3D_TRACK_BUILDER_PROJECT_OUT_OF_MEMORY,
|
||||
LARDON3D_TRACK_BUILDER_PROJECT_CORE_ERROR,
|
||||
LARDON3D_TRACK_BUILDER_PROJECT_INTERRUPTED,
|
||||
} Lardon3DTrackBuilderProjectStatus;
|
||||
|
||||
Lardon3DTrackBuilderProjectStatus lardon3d_track_builder_build_project(
|
||||
|
|
|
|||
|
|
@ -44,18 +44,6 @@ bool lardon3d_track_builder_task_reconstruct(
|
|||
const Lardon3DTaskDurableSnapshot *snapshot, void *context,
|
||||
Lardon3DTaskKindBinding *binding);
|
||||
|
||||
/* Checked operational envelope for the compact RAM-only implementation.
|
||||
* This is admission accounting, not Builder identity or scientific policy. */
|
||||
bool lardon3d_track_builder_task_memory_estimate(
|
||||
uint64_t raw_edge_count, uint64_t feature_set_count,
|
||||
uint64_t *memory_bytes);
|
||||
|
||||
/* Additive operational estimator used when the exact scope scan has resolved
|
||||
* the largest parent Match Result. The legacy E/F estimator remains ABI-stable. */
|
||||
bool lardon3d_track_builder_task_memory_estimate_with_match_peak(
|
||||
uint64_t raw_edge_count, uint64_t feature_set_count,
|
||||
uint64_t max_match_count, uint64_t *memory_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
181
meson.build
181
meson.build
|
|
@ -11,8 +11,6 @@ project(
|
|||
],
|
||||
)
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
add_project_arguments(
|
||||
'-D_POSIX_C_SOURCE=200809L',
|
||||
'-Wpedantic',
|
||||
|
|
@ -179,17 +177,6 @@ lardon3d_app = executable(
|
|||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/optical_profiles.c',
|
||||
'src/calibration_bootstrap.c',
|
||||
'src/calibration_bootstrap_v2.c',
|
||||
'src/calibration_workflow_v2.c',
|
||||
'src/calibration_tooling.c',
|
||||
'src/calibration_tooling_v2.c',
|
||||
'src/calibration_af_study.c',
|
||||
'src/calibration_af_study_workflow.c',
|
||||
'src/calibration_af_study_assembly.c',
|
||||
'src/calibration_workflow.cpp',
|
||||
'src/calibration_workflow_materialize.cpp',
|
||||
'src/calibration_workflow_bind.cpp',
|
||||
'src/calibration_workflow_complete.cpp',
|
||||
'src/sparse_sfm_geometry.cpp',
|
||||
'src/sparse_sfm_incremental.cpp',
|
||||
'src/sparse_sfm_bundle_adjustment.cpp',
|
||||
|
|
@ -890,174 +877,6 @@ calibration_bootstrap_test = executable(
|
|||
|
||||
test('calibration-bootstrap', calibration_bootstrap_test, timeout: 30)
|
||||
|
||||
calibration_tooling_test = executable(
|
||||
'test-calibration-tooling',
|
||||
sources: [
|
||||
'tests/test_calibration_tooling.c',
|
||||
'src/calibration_tooling.c', 'src/calibration_bootstrap.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c', 'src/resource_governor.c', 'src/resource_snapshot.c',
|
||||
],
|
||||
c_args: ['-DLARDON3D_PROJECT_DB_TESTING'],
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: [threads, sqlite3, openssl, cc.find_library('m')],
|
||||
)
|
||||
|
||||
test('calibration-tooling', calibration_tooling_test, timeout: 30)
|
||||
|
||||
calibration_publication_v2_test = executable(
|
||||
'test-calibration-publication-v2',
|
||||
sources: [
|
||||
'tests/test_calibration_publication_v2.c',
|
||||
'src/calibration_tooling_v2.c', 'src/calibration_bootstrap_v2.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c', 'src/resource_governor.c', 'src/resource_snapshot.c',
|
||||
],
|
||||
c_args: ['-DLARDON3D_PROJECT_DB_TESTING'],
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: [threads, sqlite3, openssl, cc.find_library('m')],
|
||||
)
|
||||
|
||||
test('calibration-publication-v2', calibration_publication_v2_test, timeout: 30)
|
||||
|
||||
calibration_af_study_test = executable(
|
||||
'test-calibration-af-study',
|
||||
sources: [
|
||||
'tests/test_calibration_af_study.c',
|
||||
'src/calibration_af_study.c',
|
||||
],
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: [openssl, cc.find_library('m')],
|
||||
)
|
||||
|
||||
test('calibration-af-study', calibration_af_study_test, timeout: 30)
|
||||
|
||||
calibration_af_study_workflow_test = executable(
|
||||
'test-calibration-af-study-workflow',
|
||||
sources: [
|
||||
'tests/test_calibration_af_study_workflow.c',
|
||||
'src/calibration_af_study_workflow.c',
|
||||
],
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: [openssl, cc.find_library('m')],
|
||||
)
|
||||
|
||||
test(
|
||||
'calibration-af-study-workflow',
|
||||
calibration_af_study_workflow_test,
|
||||
timeout: 30,
|
||||
)
|
||||
|
||||
calibration_af_study_assembly_test = executable(
|
||||
'test-calibration-af-study-assembly',
|
||||
sources: [
|
||||
'tests/test_calibration_af_study_assembly.c',
|
||||
'src/calibration_af_study_assembly.c',
|
||||
'src/calibration_af_study_workflow.c',
|
||||
'src/calibration_af_study.c',
|
||||
],
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: [openssl, cc.find_library('m')],
|
||||
)
|
||||
|
||||
test(
|
||||
'calibration-af-study-assembly',
|
||||
calibration_af_study_assembly_test,
|
||||
timeout: 30,
|
||||
)
|
||||
|
||||
calibration_workflow_v2_test = executable(
|
||||
'test-calibration-workflow-v2',
|
||||
sources: [
|
||||
'tests/test_calibration_workflow_v2.c',
|
||||
'src/calibration_workflow_v2.c',
|
||||
'src/calibration_tooling_v2.c', 'src/calibration_bootstrap_v2.c',
|
||||
'src/optical_profiles.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c', 'src/resource_governor.c', 'src/resource_snapshot.c',
|
||||
],
|
||||
c_args: ['-DLARDON3D_PROJECT_DB_TESTING'],
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: [threads, sqlite3, openssl, cc.find_library('m')],
|
||||
)
|
||||
|
||||
test('calibration-workflow-v2', calibration_workflow_v2_test, timeout: 30)
|
||||
|
||||
calibration_workflow_test = executable(
|
||||
'test-calibration-workflow',
|
||||
sources: [
|
||||
'tests/test_calibration_workflow.cpp',
|
||||
'src/calibration_workflow.cpp',
|
||||
],
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: [openssl],
|
||||
)
|
||||
|
||||
test('calibration-workflow', calibration_workflow_test, timeout: 30)
|
||||
|
||||
|
||||
calibration_workflow_materialization_test = executable(
|
||||
'test-calibration-workflow-materialization',
|
||||
sources: [
|
||||
'tests/test_calibration_workflow_materialize.cpp',
|
||||
'src/calibration_workflow.cpp',
|
||||
'src/calibration_workflow_materialize.cpp',
|
||||
],
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: [openssl],
|
||||
)
|
||||
|
||||
test(
|
||||
'calibration-workflow-materialization',
|
||||
calibration_workflow_materialization_test,
|
||||
timeout: 30,
|
||||
)
|
||||
|
||||
|
||||
calibration_workflow_binding_test = executable(
|
||||
'test-calibration-workflow-binding',
|
||||
sources: [
|
||||
'tests/test_calibration_workflow_bind.cpp',
|
||||
'src/calibration_workflow_bind.cpp',
|
||||
'src/optical_profiles.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c', 'src/resource_governor.c', 'src/resource_snapshot.c',
|
||||
],
|
||||
c_args: ['-DLARDON3D_PROJECT_DB_TESTING'],
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: [threads, sqlite3, openssl, opencv],
|
||||
)
|
||||
|
||||
test(
|
||||
'calibration-workflow-binding',
|
||||
calibration_workflow_binding_test,
|
||||
timeout: 30,
|
||||
)
|
||||
|
||||
calibration_workflow_ready_test = executable(
|
||||
'test-calibration-workflow-ready',
|
||||
sources: [
|
||||
'tests/test_calibration_workflow_ready.cpp',
|
||||
'src/calibration_workflow.cpp',
|
||||
'src/calibration_workflow_materialize.cpp',
|
||||
'src/calibration_workflow_bind.cpp',
|
||||
'src/calibration_workflow_complete.cpp',
|
||||
'src/calibration_tooling.c', 'src/calibration_bootstrap.c',
|
||||
'src/optical_profiles.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c', 'src/resource_governor.c', 'src/resource_snapshot.c',
|
||||
],
|
||||
c_args: ['-DLARDON3D_PROJECT_DB_TESTING'],
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: [threads, sqlite3, openssl, opencv, cc.find_library('m')],
|
||||
)
|
||||
|
||||
test(
|
||||
'calibration-workflow-ready',
|
||||
calibration_workflow_ready_test,
|
||||
timeout: 30,
|
||||
)
|
||||
|
||||
optical_profiles_test = executable(
|
||||
'test-optical-profiles',
|
||||
sources: [
|
||||
|
|
|
|||
90
prompt.md
90
prompt.md
|
|
@ -1,90 +0,0 @@
|
|||
# Lardon3D — Final Execution Contract
|
||||
|
||||
This file is the entry point for the collective Lardon3D execution contract.
|
||||
|
||||
```text
|
||||
CONTRACT_SCOPE=ENTIRE_PROMPT_TREE
|
||||
CONTRACT_LANGUAGE=ENGLISH
|
||||
WORK_FIRST_RETURN_LAST=REQUIRED
|
||||
CANONICAL_WORKING_BRANCH=main
|
||||
CURRENT_STATE_AUTHORITY=main
|
||||
GIT_CLOSURE_OWNER=HUMAN
|
||||
IMPLEMENTATION_AUTHORIZATION=NO
|
||||
```
|
||||
|
||||
## Mandatory reading order
|
||||
|
||||
Before any major implementation tranche, read:
|
||||
|
||||
1. `AGENTS.md`
|
||||
2. `README.md`
|
||||
3. `docs/README.md`
|
||||
4. `docs/product/product_definition.md`
|
||||
5. `docs/roadmap/roadmap.md`
|
||||
6. every file under `prompt/` in numeric order from `00_AUTHORITY.md` through `33_DEFINITION_OF_DONE.md`
|
||||
7. the specialized canonical `docs/architecture/**` documents cited by the tranche
|
||||
|
||||
The files under `prompt/` form one collective contract. No child file may be interpreted in isolation when another child file defines a complementary constraint.
|
||||
|
||||
## Absolute rules
|
||||
|
||||
- Preserve all applicable PASS/FROZEN scientific and architectural boundaries.
|
||||
- Never invent calibration, lens identity, metric scale, campaign equivalence, provenance, or scientific identity.
|
||||
- Never create a Project DB version or production Task Kind without explicit human authorization.
|
||||
- Never create a second Task Runtime, Queue, scheduler, Resource Governor, generic executor, or backend framework merely for convenience.
|
||||
- Never modify `scan3d/` without explicit human authorization.
|
||||
- Never use retained real scientific projects as destructive scratch/test workspaces.
|
||||
- Never reinterpret historical checkpoints as current state.
|
||||
- Never treat GitHub and Forgejo as different project authorities; they are mirrors of the same canonical `main`.
|
||||
- Never stage with `git add -A`.
|
||||
- Never commit or push unless the human explicitly authorizes Git closure.
|
||||
- Never force-push unless the human explicitly authorizes that exact operation.
|
||||
|
||||
## STOP
|
||||
|
||||
STOP before any change that would:
|
||||
|
||||
- reopen a PASS/FROZEN boundary;
|
||||
- change a FROZEN scientific threshold or identity;
|
||||
- introduce pseudo-calibration or silent calibration substitution;
|
||||
- introduce an unauthorized Project DB schema version;
|
||||
- introduce an unauthorized production Task Kind;
|
||||
- create an unnecessary parallel runtime/scheduler/backend framework;
|
||||
- modify `scan3d/`;
|
||||
- perform a destructive operation;
|
||||
- create/switch/merge/rebase branches without authorization;
|
||||
- resolve an unexplained mirror divergence by overwriting one remote;
|
||||
- contradict executable code/schema and a canonical contract;
|
||||
- resolve a material product ambiguity by assumption.
|
||||
|
||||
A STOP report contains only the relevant authority, the exact contradiction, and the minimum human decision required.
|
||||
|
||||
## Execution discipline
|
||||
|
||||
For an authorized tranche:
|
||||
|
||||
1. identify the governing authority;
|
||||
2. inspect only the context needed to execute correctly;
|
||||
3. define the exact file and subsystem scope;
|
||||
4. implement the complete tranche;
|
||||
5. run proportional delta-based validation;
|
||||
6. fix defects within scope;
|
||||
7. run `git diff --check`;
|
||||
8. report only after the tranche is complete or a real STOP condition is reached.
|
||||
|
||||
Do not return after each file. Do not repeat global A-to-Z audits when unchanged FROZEN systems already have retained evidence.
|
||||
|
||||
## Current head assumptions
|
||||
|
||||
This contract was prepared against the repository state that declares:
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v27
|
||||
PRODUCTION_TASK_KINDS=16
|
||||
PRODUCT_DEFINITION_V1=PASS/FROZEN
|
||||
PROMPT_TREE=NEXT
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
```
|
||||
|
||||
Before implementation, verify these assumptions against current `main`. If current `main` has legitimately advanced, update only the CURRENT statements that are stale; never rewrite historical or FROZEN meaning silently.
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
# 00 — Authority
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
AUTHORITY_CONTRACT=REQUIRED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
Primary authority is the repository on canonical branch `main`.
|
||||
|
||||
Read and apply, in order of role rather than by blindly overriding specialized contracts:
|
||||
|
||||
- `AGENTS.md`: global agent obligations and repository limits.
|
||||
- `docs/product/product_definition.md`: final product destination.
|
||||
- specialized `docs/architecture/**`: acquired scientific, persistence, runtime, identity and resource contracts.
|
||||
- `docs/roadmap/roadmap.md`: lifecycle and current sequencing.
|
||||
- `README.md` and `docs/README.md`: current navigation and summary.
|
||||
- historical audits/tags/checkpoints: evidence for their own checkpoint only.
|
||||
- executable code/schema: authority when exact APIs, constants, DDL or runtime behavior are being quoted.
|
||||
|
||||
|
||||
## CURRENT
|
||||
|
||||
The canonical working branch is `main`.
|
||||
|
||||
```text
|
||||
CANONICAL_WORKING_BRANCH=main
|
||||
CURRENT_STATE_AUTHORITY=main
|
||||
OTHER_BRANCHES_ARE_NOT_AUTHORITY
|
||||
```
|
||||
|
||||
## FROZEN
|
||||
|
||||
A specialized PASS/FROZEN contract is not superseded by this prompt tree. This tree references and constrains execution around those contracts; it does not duplicate them.
|
||||
|
||||
## Git authority
|
||||
|
||||
The local working tree is the current working authority. GitHub (`github`) and Forgejo (`origin`) are mirrors of the same project, not independent variants.
|
||||
|
||||
After an explicitly authorized Git closure:
|
||||
|
||||
```text
|
||||
local main
|
||||
-> github/main
|
||||
-> origin/main
|
||||
```
|
||||
|
||||
should normally identify the same commit.
|
||||
|
||||
## Required Git inspection before Git-sensitive work
|
||||
|
||||
```sh
|
||||
git branch --show-current
|
||||
git status --porcelain
|
||||
git remote -v
|
||||
git rev-parse HEAD
|
||||
git rev-parse github/main
|
||||
git rev-parse origin/main
|
||||
```
|
||||
|
||||
If a remote is missing, inaccessible, or unexpectedly divergent: STOP. Do not choose one mirror as the winner by assumption.
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
# 01 — Product Vision
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
PRODUCT_VISION=FROZEN_TARGET
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md` is the product authority.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
Lardon3D is a persistent, incremental, resource-aware Linux photogrammetry system controlled primarily by an ncurses TUI, with a required companion graphical viewer.
|
||||
|
||||
The product must let a user:
|
||||
|
||||
- create and reopen durable projects;
|
||||
- ingest traceable still, future live-device, and video-derived captures;
|
||||
- manage camera/lens/optical configurations without code changes for ordinary equipment;
|
||||
- reach explicit calibration readiness;
|
||||
- execute the existing scientific pre-SfM and Sparse SfM pipeline without invented science;
|
||||
- produce durable Dense/MVS, mesh, refinement, texturing and exports;
|
||||
- inspect sparse/dense/mesh/texture and camera evidence;
|
||||
- analyze coverage;
|
||||
- receive actionable supplementary-capture guidance;
|
||||
- use live A6000 HDMI and S21 acquisition adapters without forking the scientific pipeline;
|
||||
- restart after crashes/reboots without hidden transient knowledge.
|
||||
|
||||
## Principles
|
||||
|
||||
```text
|
||||
SCIENTIFIC_TRACEABILITY=REQUIRED
|
||||
DETERMINISM=REQUIRED
|
||||
PERSISTENT_PROGRESS=REQUIRED
|
||||
ATOMIC_PUBLICATION=REQUIRED
|
||||
BOUNDED_EXECUTION=REQUIRED
|
||||
MAXIMUM_SAFE_USEFUL_THROUGHPUT=REQUIRED
|
||||
SERIALISM_REQUIRES_PROOF=REQUIRED
|
||||
NO_SILENT_SCIENTIFIC_SUBSTITUTION=REQUIRED
|
||||
NO_DESTRUCTIVE_AUTOMATION=REQUIRED
|
||||
```
|
||||
|
||||
The system must expose uncertainty rather than replace it with guesses.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
# 02 — Current and Frozen State
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
CURRENT_PROJECT_DB_SCHEMA=v27
|
||||
PRODUCTION_TASK_KINDS=16
|
||||
USER_FACING_UI_LANGUAGE_NORMALIZATION=PASS
|
||||
CURRENT_IMPLEMENTATION_CURSOR=PHYSICAL_AUTOFOCUS_OPTICAL_APPLICABILITY_VALIDATION
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`README.md`, `AGENTS.md`, `docs/roadmap/roadmap.md`, specialized architecture documents, and retained checkpoint evidence.
|
||||
|
||||
## CURRENT
|
||||
|
||||
```text
|
||||
Project DB head v27
|
||||
Production Task kinds 16
|
||||
Feature Store IMPLEMENTED
|
||||
Visual Index IMPLEMENTED
|
||||
Candidate Pair IMPLEMENTED
|
||||
Matcher IMPLEMENTED
|
||||
TUI operational observatory VALIDATED
|
||||
User-facing TUI/control language PASS
|
||||
External SSD controller VALIDATED
|
||||
Calibration Solver Preflight v1 PASS
|
||||
Calibration Evidence Solver v1 IMPLEMENTED/VALIDATED
|
||||
Calibration solver white-border evidence PASS/FROZEN
|
||||
Calibration solver producer identity PASS/FROZEN
|
||||
Calibration solver per-view evidence PASS/FROZEN
|
||||
Calibration solver bundle repair PASS/FROZEN
|
||||
Calibration workflow input boundary PASS/FROZEN
|
||||
Calibration workflow evidence materialization PASS/FROZEN
|
||||
Calibration workflow selected-execution binding PASS/FROZEN
|
||||
Calibration workflow Tooling/Bootstrap READY PASS/FROZEN
|
||||
Calibration Science v2 heterogeneous optics PASS/FROZEN
|
||||
Calibration v2 optical-state foundation PASS/FROZEN
|
||||
Calibration v2 heterogeneous publication PASS/FROZEN
|
||||
Calibration Workflow v2 PASS/FROZEN
|
||||
Adaptive capture settings semantics PASS/FROZEN
|
||||
Autofocus v2 foundation PASS/FROZEN
|
||||
Calibration AF study evidence v1 PASS/FROZEN
|
||||
Calibration AF study Workflow bridge v1 PASS/FROZEN
|
||||
Calibration AF study assembly v1 PASS/FROZEN
|
||||
Calibration Tooling planarity alignment PASS/FROZEN
|
||||
```
|
||||
|
||||
The additive schema lineage is:
|
||||
|
||||
```text
|
||||
v22 selected scientific execution foundation
|
||||
v23 generic optical-context overlay
|
||||
v24 raw.develop.batch/1
|
||||
v25 features.extract.batch/1
|
||||
v26 Capture geometric state/applicability
|
||||
```
|
||||
|
||||
## FROZEN
|
||||
|
||||
Preserve at their documented boundaries:
|
||||
|
||||
- Capture / Asset Provenance;
|
||||
- acquisition ingestion and durable campaign execution;
|
||||
- Photo Quality Triage;
|
||||
- Selected Scientific Execution;
|
||||
- Geometric Verifier v3;
|
||||
- Track Model / Track Builder;
|
||||
- Sparse SfM capability through Gate G (detailed lifecycle: Gate A decision/historical; Gates B-G PASS/FROZEN);
|
||||
- Phase H v1;
|
||||
- MVS-M1 external OpenMVS 2.4.0 boundary;
|
||||
- Calibration Science v1;
|
||||
- Calibration Tooling v1;
|
||||
- Calibration Bootstrap v1;
|
||||
- Internal Parallelism / Compute Resources;
|
||||
- Resource / Compute Governor;
|
||||
- ORB Vulkan backend;
|
||||
- Global Maintenance Audit;
|
||||
- Real S21 Tracks;
|
||||
- Real A6000 pre-SfM.
|
||||
|
||||
`FROZEN` protects meaning and boundary, not necessarily every file byte.
|
||||
|
||||
## Current calibration-v2 design intent
|
||||
|
||||
Calibration v1 remains a valid frozen compatibility path. It is not weakened or
|
||||
retroactively reinterpreted.
|
||||
|
||||
The current product dependency has changed before the physical campaign because
|
||||
real A6000 engine-bay acquisition evidence showed that normal operation requires
|
||||
heterogeneous optics and autofocus. Calibration Science v2 must therefore define
|
||||
an additive path in which:
|
||||
|
||||
```text
|
||||
one project may mix camera bodies
|
||||
one project may mix lenses
|
||||
one project may mix focal configurations
|
||||
one selected execution may use multiple calibrations
|
||||
each selected image must resolve to exactly one compatible calibration
|
||||
autofocus is a normal supported acquisition mode
|
||||
exposure settings are adaptive rather than fixed scientific constants
|
||||
```
|
||||
|
||||
Capture/quality acceptance and calibration readiness are distinct. A sharp,
|
||||
usable image with unresolved optics is retained and reported as
|
||||
`CALIBRATION_REQUIRED`; it is not discarded merely because calibration evidence
|
||||
is not yet available.
|
||||
|
||||
No v2 rule may silently substitute another body, lens, focal state, focus state
|
||||
or calibration.
|
||||
|
||||
|
||||
## Retained real S21 checkpoint
|
||||
|
||||
```text
|
||||
REAL_S21_TRACKS=PASS/FROZEN
|
||||
project=/home/fy59/Documents/Lardon/.real-pre-sfm-2026-08-31/s21-gv-v3
|
||||
Tracks=912447
|
||||
Track observations=2495768
|
||||
Track length min/max=2/42
|
||||
digest=c30eba192627bf73eaf21ff30d81038d8cc6bbf36a69226f88cdc8c37f7d74a1
|
||||
Sparse SfM=NOT EXECUTED
|
||||
Dense/MVS=NOT EXECUTED
|
||||
```
|
||||
|
||||
## Retained real A6000 checkpoint
|
||||
|
||||
```text
|
||||
REAL_A6000_PRE_SFM=PASS/FROZEN
|
||||
tag=real-a6000-pre-sfm-2026-09-02
|
||||
project=/home/fy59/Documents/Lardon/.real-pre-sfm-2026-09-01/a6000-pre-sfm-v23-final
|
||||
Project DB=v25
|
||||
Selected images=689
|
||||
Feature Sets=689
|
||||
Candidate Pairs=38420
|
||||
Match Results=38420
|
||||
Applicable GVRs=37805
|
||||
Verified GVRs=10952
|
||||
Rejected GVRs=26853
|
||||
Track Sets=1
|
||||
Tracks=130714
|
||||
Track observations=318944
|
||||
Sparse SfM Tasks=0
|
||||
Sparse Reconstructions=0
|
||||
Dense/MVS=0
|
||||
GV v3 fingerprint=6944a471d611d8ffc59dac7cf15a5b79b97e2371d4c51785c477d68c1577f74c
|
||||
```
|
||||
|
||||
The project-directory name is historical and must not be mistaken for schema head.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
# 03 — End Product
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
END_PRODUCT=REQUIRED_PRODUCT_TARGET
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md`.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
The finished product is not complete at Sparse SfM. It is complete only when the durable workflow reaches usable reconstruction, inspection, improvement and export.
|
||||
|
||||
Required top-level capabilities:
|
||||
|
||||
- data-driven optics onboarding;
|
||||
- calibration assistant and accepted calibration import;
|
||||
- real known-calibration Sparse SfM;
|
||||
- durable restartable Dense/OpenMVS execution;
|
||||
- mesh, refinement, texturing and consolidation;
|
||||
- interoperable traceable export;
|
||||
- passive graphical viewer;
|
||||
- offline coverage analysis;
|
||||
- actionable capture guidance;
|
||||
- live localization and truthful loss/confidence states;
|
||||
- stock A6000 HDMI integration;
|
||||
- S21 integration without root;
|
||||
- deterministic video keyframes;
|
||||
- safe optional SSD scratch;
|
||||
- recovery, diagnostics and resource-aware performance;
|
||||
- local-first operation with no cloud dependency for core workflows;
|
||||
- explicit preview-before-delete cleanup/storage management.
|
||||
|
||||
## Local-first and cleanup requirements
|
||||
|
||||
Core project operation, reconstruction, calibration processing, viewer, coverage analysis, capture guidance and export must not require a cloud service. Future optional network/device adapters must make network use explicit; user project imagery is not uploaded merely to operate the product.
|
||||
|
||||
Cleanup is explicit and reviewable. The product may identify orphan temporary assets, superseded immutable generations, export caches and stale scratch, but it must show what will be removed before destructive cleanup. FROZEN/historical evidence is never deleted automatically because a newer generation exists.
|
||||
|
||||
## REJECTED
|
||||
|
||||
|
||||
The final product does not require:
|
||||
|
||||
- a GUI replacement for the TUI;
|
||||
- camera firmware or hardware modification;
|
||||
- cloud reconstruction;
|
||||
- generic backend frameworks without demonstrated need;
|
||||
- a second scheduler/runtime;
|
||||
- invented metric scale.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
# 04 — End-to-End Pipeline
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
PIPELINE_TARGET=REQUIRED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md` plus specialized architecture contracts for each existing stage.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
```text
|
||||
project
|
||||
-> acquisition / Capture / provenance
|
||||
-> optics assignment
|
||||
-> calibration readiness
|
||||
-> quality selection
|
||||
-> deterministic selected scientific representation
|
||||
-> Features
|
||||
-> Visual Index
|
||||
-> Candidate Pairs
|
||||
-> Matcher
|
||||
-> Geometric Verification
|
||||
-> Tracks
|
||||
-> Sparse SfM
|
||||
-> incremental enrichment when lineage permits
|
||||
-> explicit multi-campaign registration when required
|
||||
-> Dense / MVS
|
||||
-> mesh
|
||||
-> refinement
|
||||
-> texturing
|
||||
-> consolidation
|
||||
-> coverage analysis
|
||||
-> viewer / capture guidance
|
||||
-> export
|
||||
```
|
||||
|
||||
Video and live sources enter through acquisition boundaries and converge into the same normal scientific pipeline after durable Capture promotion.
|
||||
|
||||
## Invariants
|
||||
|
||||
Every scientific stage must consume explicit durable identities and publish atomically. A downstream failure must not invalidate a valid upstream immutable generation.
|
||||
|
||||
## Quality boundary
|
||||
|
||||
Existing quality recommendations remain explainable and non-destructive:
|
||||
|
||||
```text
|
||||
GOOD
|
||||
SUSPECT
|
||||
REJECT
|
||||
```
|
||||
|
||||
Where the FROZEN selection contract permits a human override, that override is explicit, durable and visible and does not rewrite measured quality evidence. Live guidance should reuse the same quality concepts where practical without turning preview evidence into a scientific Capture.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# 05 — Scientific Invariants
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
SCIENTIFIC_INVARIANTS=MANDATORY
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
Specialized PASS/FROZEN architecture documents remain authoritative.
|
||||
|
||||
## FROZEN
|
||||
|
||||
- No pseudo-calibration.
|
||||
- No EXIF-as-calibration fallback.
|
||||
- No silent lens identity inference.
|
||||
- No silent calibration substitution or interpolation.
|
||||
- No metric-scale claim for arbitrary monocular gauge.
|
||||
- No silent merging of independent campaigns.
|
||||
- No mutation of immutable upstream scientific generations.
|
||||
- No treating an external process success code as scientific acceptance.
|
||||
- No anonymous temporary representation may become a durable scientific result without provenance.
|
||||
- Deterministic scientific identities and parameter fingerprints remain explicit.
|
||||
- Scientific identity must not depend on CPU thread count, worker scheduling, host model, temporary path or wall-clock duration.
|
||||
- GPU/CPU execution choice is operational unless a scientific contract explicitly makes a backend scientifically non-transparent.
|
||||
- Where exact bit identity cannot honestly be guaranteed across dependency/hardware versions, the owning scientific contract must define the reproducibility boundary explicitly.
|
||||
|
||||
## STOP
|
||||
|
||||
If a planned feature requires a new threshold, weighting function, estimator, classification policy, frame-selection science, coverage score or other scientific rule that is not already FROZEN, first define and validate a versioned scientific contract. Do not hide new science inside implementation code.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
# 06 — Identity Model
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
IDENTITY_DISCIPLINE=REQUIRED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`AGENTS.md`, persistence/project database documents, Capture/provenance and scientific model documents.
|
||||
|
||||
## FROZEN
|
||||
|
||||
Never silently equate:
|
||||
|
||||
```text
|
||||
Capture != file
|
||||
Capture != asset
|
||||
Capture != image_id
|
||||
Capture != SHA-256
|
||||
Capture != path
|
||||
Capture != filename/basename
|
||||
Capture != Task ID
|
||||
Capture != campaign group ID
|
||||
Task ID != scientific acquisition identity
|
||||
campaign group ID != Capture identity
|
||||
```
|
||||
|
||||
Current meanings:
|
||||
|
||||
- SHA-256: immutable asset bytes.
|
||||
- `capture_id`: physical acquisition representation in Project DB.
|
||||
- `image_id`: scientific image representation.
|
||||
- Task ID: durable operational work.
|
||||
- campaign group ID: stable operational grouping within an acquisition request.
|
||||
|
||||
Future Dense, mesh, texture, coverage, registration, live-promotion and video-keyframe identities must be explicit, versioned, durable and provenance-linked. They must not be inferred from paths or timestamps.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
# 07 — Persistence and Recovery
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
PERSISTENCE_RECOVERY=REQUIRED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/architecture/persistence.md`, `docs/architecture/project_database.md`, `docs/architecture/runtime.md`, `docs/architecture/task_system.md`.
|
||||
|
||||
## FROZEN
|
||||
|
||||
Existing persistence semantics, task recovery and schema lineage are consumed as-is.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
New long-running product stages must be:
|
||||
|
||||
- durable;
|
||||
- restartable;
|
||||
- failure-atomic;
|
||||
- bounded;
|
||||
- explicit about immutable inputs and published generations;
|
||||
- recoverable without private solver/process state.
|
||||
|
||||
Do not persist transient library internals merely to resume work.
|
||||
|
||||
External-process orchestration must durably preserve enough exact input/output identity and checkpoint state to restart safely.
|
||||
|
||||
### Project portability
|
||||
|
||||
The durable project must not depend on:
|
||||
|
||||
- absolute temporary paths;
|
||||
- current CPU topology;
|
||||
- current GPU device;
|
||||
- current external-scratch mount path;
|
||||
- a live camera connection;
|
||||
- a previous process ID;
|
||||
- a specific Task worker instance.
|
||||
|
||||
Hardware-specific operational state is rediscovered; scientific identities remain stable.
|
||||
|
||||
### Recovery cases
|
||||
|
||||
Final-product long-running stages must define recovery behavior for application close, system reboot, Task cancellation, process crash, external-process failure, safe-drained scratch disconnect and partial physical-asset publication where existing orphan semantics permit it.
|
||||
|
||||
Restart must never infer the scientific input from filename, timestamp or another heuristic.
|
||||
|
||||
## Schema rule
|
||||
|
||||
```text
|
||||
NEW_PROJECT_DB_VERSION_REQUIRES_HUMAN_AUTHORIZATION
|
||||
NEW_PRODUCTION_TASK_KIND_REQUIRES_HUMAN_AUTHORIZATION
|
||||
```
|
||||
|
||||
A planned capability does not itself grant that authorization.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
# 08 — Resource Policy
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
RESOURCE_UTILIZATION_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
SERIALISM_REQUIRES_PROOF=CANONICAL
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`AGENTS.md`, `docs/architecture/resource_governor.md`, `docs/architecture/internal_parallelism.md`, `docs/architecture/resource_aware_pipeline.md`.
|
||||
|
||||
## FROZEN
|
||||
|
||||
First preserve normal interactive workstation use. Then use all remaining resources that are both safe and useful.
|
||||
|
||||
Reference-host evidence only:
|
||||
|
||||
```text
|
||||
16 logical CPUs
|
||||
~4 logical CPUs interactive reserve
|
||||
~12 logical CPUs compute
|
||||
~3 GiB MemAvailable hard reserve
|
||||
Radeon 780M UMA
|
||||
```
|
||||
|
||||
These are not product constants.
|
||||
|
||||
## Rules
|
||||
|
||||
- per-item atomicity does not imply cross-item serialization;
|
||||
- owner-only publication does not imply serial preparation;
|
||||
- bounded internal fan-out is expected when independent useful work exists;
|
||||
- all participants are bounded, accounted, cancellable and joined;
|
||||
- UMA is charged once against host RAM;
|
||||
- swap and zram are pressure mechanisms, not admitted RAM;
|
||||
- scratch is storage, not RAM;
|
||||
- pressure may reduce admission;
|
||||
- safe useful capacity must be readmitted when pressure clears;
|
||||
- I/O or measured scaling knees may justify under-utilization;
|
||||
- fixed historical `CPU1`, `-j8`, batch sizes or worker counts never become universal policy;
|
||||
- normal users observe but do not need to select authoritative CPU width, batch/window, inflight depth, participant count, GPU backend, scratch mode or RAM budget;
|
||||
- no stage may introduce an unbounded whole-project load merely for implementation convenience.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
# 09 — Task, Queue and Governor
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
TASK_RUNTIME_AUTHORITY=SINGLE
|
||||
QUEUE_AUTHORITY=SINGLE
|
||||
RESOURCE_GOVERNOR_AUTHORITY=SINGLE
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/architecture/task_system.md`, `task_queue.md`, `task_kind_registry.md`, `resource_governor.md`, `scheduler_resource_integration.md`.
|
||||
|
||||
## CURRENT/FROZEN
|
||||
|
||||
The current production Queue executes one active callback at a time. Reuse the existing Task -> Queue -> Resource Governor ownership model.
|
||||
|
||||
Do not introduce:
|
||||
|
||||
- a second scheduler;
|
||||
- a second production queue;
|
||||
- a generic executor;
|
||||
- an uncontrolled worker pool;
|
||||
- a parallel resource authority.
|
||||
|
||||
General inter-Task parallelism remains deferred unless a future dependency and correctness proof requires it.
|
||||
|
||||
Bounded internal parallelism within one admitted Task is allowed and expected when scientifically independent work exists and publication semantics remain deterministic.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
# 10 — Camera Model
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
CAMERA_MODEL_V1=FROZEN
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/architecture/sparse_sfm.md` and calibration contracts.
|
||||
|
||||
## FROZEN
|
||||
|
||||
Sparse SfM v1 is known-calibration only.
|
||||
|
||||
The v1 model is binary64 pinhole with zero skew and OpenCV-compatible distortion:
|
||||
|
||||
```text
|
||||
fx, fy, cx, cy
|
||||
k1, k2, p1, p2
|
||||
```
|
||||
|
||||
or explicit zero distortion.
|
||||
|
||||
Image coordinates: top-left origin, +x right, +y down.
|
||||
|
||||
Camera frame: x right, y down, z forward.
|
||||
|
||||
Pose is world-to-camera:
|
||||
|
||||
```text
|
||||
Xc = R_cw * Xw + t_cw
|
||||
Cw = -transpose(R_cw) * t_cw
|
||||
```
|
||||
|
||||
Higher-order lens models are outside this FROZEN v1 boundary unless separately authorized and scientifically contracted.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
# 11 — Optics Onboarding
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
OPTICS_ONBOARDING=PLANNED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md`; current v23 optical-context architecture and persistence remain authoritative.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
```text
|
||||
NEW_CAMERA_REQUIRES_CODE_CHANGE=NO
|
||||
NEW_LENS_REQUIRES_CODE_CHANGE=NO
|
||||
ELECTRONIC_LENS_WITH_METADATA=SUPPORTED
|
||||
MANUAL_LENS_WITHOUT_EXIF=SUPPORTED
|
||||
MULTIPLE_LENSES_PER_CAMERA=SUPPORTED
|
||||
ZOOM_MULTIPLE_FOCALS=SUPPORTED
|
||||
MULTIPLE_OPTICAL_CONFIGURATIONS_PER_PROJECT=SUPPORTED
|
||||
SILENT_CALIBRATION_SUBSTITUTION=FORBIDDEN
|
||||
SILENT_LENS_IDENTITY_INFERENCE=FORBIDDEN
|
||||
OPTICS_TUI_WORKFLOW=REQUIRED
|
||||
PROFILE_IMPORT_EXPORT=REQUIRED
|
||||
OPTICS_PROFILE_ONBOARDING_TARGET<=5_MINUTES
|
||||
```
|
||||
|
||||
The time target excludes physical calibration acquisition.
|
||||
|
||||
Aliases must be exact and reviewable. Fuzzy metadata matching may assist discovery but may never silently create scientific identity.
|
||||
|
||||
Profile import/export must be bounded and versioned, support preview/dry-run, reject incompatible required semantics, preserve identities, and never be implemented as a raw SQLite dump.
|
||||
|
||||
|
||||
## Portable profile encoding decision
|
||||
|
||||
The v1 portable equipment-profile encoding is a single deterministic bounded binary container:
|
||||
|
||||
```text
|
||||
OPTICS_PROFILE_PORTABLE_FORMAT=L3DOPRF1
|
||||
OPTICS_PROFILE_PORTABLE_VERSION=1
|
||||
BYTE_ORDER=LITTLE_ENDIAN
|
||||
NATIVE_STRUCT_SERIALIZATION=FORBIDDEN
|
||||
RAW_SQLITE_EXPORT=FORBIDDEN
|
||||
UNBOUNDED_JSON=FORBIDDEN
|
||||
```
|
||||
|
||||
`L3DOPRF1` carries camera-body profiles, lens profiles, explicit aliases and optical configurations. All integers/floating-point fields use explicit fixed widths; variable UTF-8 strings and record arrays are length/count-prefixed and must have explicit hard maxima in the public format contract before parser implementation. Unknown required semantics or unsupported versions are rejected before database mutation.
|
||||
|
||||
Calibration scientific payloads are not re-encoded as profile data. Portable calibrations remain exact `L3DCALB1` artifacts and continue through Calibration Bootstrap v1. The optics-profile import preview may report compatible/missing calibration requirements but may not synthesize or merge calibration.
|
||||
|
||||
Import is two-phase:
|
||||
|
||||
```text
|
||||
bounded parse + validation + conflict preview
|
||||
-> explicit human acceptance
|
||||
-> one failure-atomic database mutation
|
||||
```
|
||||
|
||||
Conflicting profile identities are never silently merged.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
# 12 — Calibration
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
CALIBRATION_SCIENCE_V1=PASS/FROZEN
|
||||
CALIBRATION_TOOLING_V1=PASS/FROZEN
|
||||
CALIBRATION_TOOLING_PLANARITY_ALIGNMENT=PASS/FROZEN
|
||||
CALIBRATION_BOOTSTRAP_V1=PASS/FROZEN
|
||||
CALIBRATION_EVIDENCE_SOLVER_V1=IMPLEMENTED/VALIDATED
|
||||
CALIBRATION_SOLVER_WHITE_BORDER_V1=PASS/FROZEN
|
||||
CALIBRATION_SOLVER_PRODUCER_IDENTITY_V1=PASS/FROZEN
|
||||
CALIBRATION_SOLVER_PER_VIEW_EVIDENCE_V1=PASS/FROZEN
|
||||
CALIBRATION_SOLVER_BUNDLE_REPAIR_V1=PASS/FROZEN
|
||||
CALIBRATION_WORKFLOW=PASS/FROZEN
|
||||
CALIBRATION_WORKFLOW_INPUT_BOUNDARY_V1=PASS/FROZEN
|
||||
CALIBRATION_WORKFLOW_EVIDENCE_MATERIALIZATION_V1=PASS/FROZEN
|
||||
CALIBRATION_WORKFLOW_SELECTED_EXECUTION_BINDING_V1=PASS/FROZEN
|
||||
CALIBRATION_WORKFLOW_TOOLING_BOOTSTRAP_READY_V1=PASS/FROZEN
|
||||
CALIBRATION_SCIENCE_V2=PASS/FROZEN
|
||||
CALIBRATION_V2_HETEROGENEOUS_OPTICS=PASS/FROZEN
|
||||
CALIBRATION_V2_HETEROGENEOUS_CALIBRATION_PUBLICATION=PASS/FROZEN
|
||||
CALIBRATION_V2_WORKFLOW_READY=PASS/FROZEN
|
||||
ADAPTIVE_CAPTURE_SETTINGS_CONTRACT=PASS/FROZEN
|
||||
AUTOFOCUS_V2_FOUNDATION=PASS/FROZEN
|
||||
CALIBRATION_AF_STUDY_EVIDENCE_V1=PASS/FROZEN
|
||||
CALIBRATION_AF_STUDY_WORKFLOW_BRIDGE_V1=PASS/FROZEN
|
||||
CALIBRATION_AF_STUDY_ASSEMBLY_V1=PASS/FROZEN
|
||||
CURRENT_CALIBRATION_NEXT=PHYSICAL_AUTOFOCUS_OPTICAL_APPLICABILITY_VALIDATION
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/architecture/calibration_science_v1.md`, `docs/architecture/calibration_science_v2.md`, `docs/architecture/calibration_bootstrap.md` and `docs/architecture/calibration_solver_preflight_v1.md` are the current specialized calibration documents. Science v1 remains FROZEN; the v2 document is the current additive design authority until its contract is validated and frozen.
|
||||
|
||||
`docs/architecture/calibration_tooling.md` is the specialized Tooling authority.
|
||||
|
||||
`docs/architecture/calibration_workflow.md` is the specialized workflow authority. Its public API is `include/lardon3d/calibration_workflow.h`; the FROZEN Tooling API remains `include/lardon3d/calibration_tooling.h`.
|
||||
|
||||
A bounded corrective review established that Calibration Science v1 defines target planarity as a categorical physical attestation, not a numeric flatness tolerance. Tooling preserves its public structure layout while requiring `target_flatness_mm` to be NaN, so callers cannot invent a millimetre measurement. The canonical session's `planarity PASS <sha256>` evidence is bound through immutable initialization evidence.
|
||||
|
||||
The external `tools/calibration_evidence_solver/` implementation is present and validated by its deterministic synthetic CPU1 self-test. It remains external to the Lardon3D runtime and Project DB.
|
||||
|
||||
Its session v1 now requires an explicit measured `white_border` of at least 30 mm. No default border width may be invented by the solver or the future coordinator.
|
||||
|
||||
## CURRENT
|
||||
|
||||
Historical S21 and A6000 Engine Bay campaigns:
|
||||
|
||||
```text
|
||||
CALIBRATION_UNAVAILABLE
|
||||
BLOCKED_BY_KNOWN_CALIBRATION_DATA
|
||||
```
|
||||
|
||||
Do not retro-calibrate them by invention.
|
||||
|
||||
## Calibration Science v2 product requirements
|
||||
|
||||
Science v2 is additive. It does not weaken or rewrite Science v1.
|
||||
|
||||
The product requirement is:
|
||||
|
||||
```text
|
||||
ONE_PROJECT_MAY_MIX_CAMERAS=YES
|
||||
ONE_PROJECT_MAY_MIX_LENSES=YES
|
||||
ONE_PROJECT_MAY_MIX_FOCAL_CONFIGURATIONS=YES
|
||||
PER_IMAGE_COMPATIBLE_CALIBRATION=REQUIRED
|
||||
|
||||
AUTOFOCUS_NORMAL_OPERATION=SUPPORTED_TARGET
|
||||
MANUAL_FOCUS_LOCK_REQUIRED_FOR_NORMAL_CAPTURE=NO
|
||||
|
||||
SHUTTER_SPEED_FIXED=NO
|
||||
ISO_FIXED=NO
|
||||
WHITE_BALANCE_FIXED=NO
|
||||
EXPOSURE_SETTINGS_ADAPTIVE=YES
|
||||
|
||||
PHOTO_REJECTION_BASED_SOLELY_ON_EXPOSURE_METADATA=FORBIDDEN
|
||||
UNKNOWN_OPTICS_REJECT_CAPTURE=NO
|
||||
UNKNOWN_OPTICS_STATE=CALIBRATION_REQUIRED
|
||||
|
||||
SILENT_CALIBRATION_SUBSTITUTION=FORBIDDEN
|
||||
```
|
||||
|
||||
The core separation is:
|
||||
|
||||
```text
|
||||
CAPTURE / PHOTO QUALITY
|
||||
!=
|
||||
CALIBRATION READINESS
|
||||
```
|
||||
|
||||
A Capture may be sharp, non-clipped and scientifically useful as image data
|
||||
while still being `CALIBRATION_REQUIRED`. It remains durable in the project.
|
||||
|
||||
Purely photometric settings such as shutter speed, ISO, white balance and
|
||||
exposure compensation may vary according to conditions. Their numeric values
|
||||
alone are not scientific rejection criteria. Actual decoded-image evidence
|
||||
remains authoritative for quality: sharpness/defocus/motion blur, clipping,
|
||||
usable tonal information, contrast/texture, noise or local SNR where available,
|
||||
and downstream feature usability.
|
||||
|
||||
Camera/lens/focal/focus/aperture/stabilization/crop/pipeline state is treated
|
||||
according to whether it can alter geometry. Lardon3D must observe and classify
|
||||
that state, automatically select an exactly compatible validated calibration
|
||||
when one exists, expose `CALIBRATION_REQUIRED` when none exists, and expose
|
||||
`SELECTION_REQUIRED` when compatibility remains ambiguous.
|
||||
|
||||
Autofocus is a normal v2 acquisition mode. A real autofocus applicability
|
||||
envelope must be supported only by physical calibration/hold-out evidence.
|
||||
Historical EXIF or MakerNote focus observations may guide experiment design but
|
||||
must never fabricate such evidence.
|
||||
|
||||
See `docs/architecture/calibration_science_v2.md`.
|
||||
|
||||
## FROZEN v1 flow
|
||||
|
||||
```text
|
||||
dedicated physical calibration acquisition
|
||||
-> external OpenCV 5.x Calibration Evidence Solver v1
|
||||
-> immutable session manifest + complete solver bundle
|
||||
-> workflow coordinator
|
||||
-> Calibration Tooling v1
|
||||
-> deterministic L3DCALB1 v1
|
||||
-> Calibration Bootstrap v1
|
||||
-> exact selected-execution calibration scope attachment
|
||||
-> READY
|
||||
-> real Sparse SfM
|
||||
```
|
||||
|
||||
The workflow coordinator now has three PASS/FROZEN non-mutating checkpoints. Input Boundary v1 validates immutable files, hashes, formats and complete optical-state equality. Evidence Materialization v1 parses the retained session and solver bundle into bounded Science v1 target, per-view, coordinate, repeated-solve, fit, residual, hold-out and provenance evidence without opening Project DB. Selected Execution Binding v1 is read-only: it proves the exact selected execution and Capture mapping, explicit optical configuration, managed representation size/SHA-256, safe project-relative file containment, decoded geometry dimensions, and deterministic `Lardon3DCalibrationToolingEntry` construction. Missing or mismatched evidence is rejected; nothing is inferred. The final remaining boundary composes the validated binding with the FROZEN Tooling and Bootstrap import path to reach truthful `READY`.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
The TUI calibration assistant must expose:
|
||||
|
||||
```text
|
||||
READY
|
||||
CALIBRATION_REQUIRED
|
||||
SELECTION_REQUIRED
|
||||
```
|
||||
|
||||
It must guide physical evidence acquisition, exact optical configuration, accepted/rejected evidence and remaining evidence requirements.
|
||||
|
||||
Solver exit success is never sufficient acceptance.
|
||||
|
||||
## REJECTED
|
||||
|
||||
Pseudo-calibration, EXIF calibration, nearby focal substitution, lens substitution, silent interpolation, silent backfill, and silent identity inference.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
# 13 — Sparse SfM
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
SPARSE_SFM_V1=IMPLEMENTED
|
||||
GATE_A=DECISION/HISTORICAL
|
||||
GATE_B=PASS/FROZEN
|
||||
GATE_C=PASS/FROZEN
|
||||
GATE_D=PASS/FROZEN
|
||||
GATE_E=PASS/FROZEN
|
||||
GATE_F=PASS/FROZEN
|
||||
GATE_G=PASS/FROZEN
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/architecture/sparse_sfm.md` is the detailed authority.
|
||||
|
||||
## FROZEN
|
||||
|
||||
Sparse SfM consumes one exact immutable Track Set and one exact compatible known-calibration scope.
|
||||
|
||||
It does not recover unknown intrinsics, infer metric scale, mutate Tracks, or merge unrelated campaigns.
|
||||
|
||||
The production Task remains:
|
||||
|
||||
```text
|
||||
sparse_sfm.run/1
|
||||
```
|
||||
|
||||
Its FROZEN CPU1/BATCH1 execution is a specific validated Sparse v1 choice and must not be generalized to unrelated stages.
|
||||
|
||||
## CURRENT
|
||||
|
||||
```text
|
||||
REAL_S21_SPARSE_SFM=NOT_EXECUTED
|
||||
REAL_A6000_SPARSE_SFM=NOT_EXECUTED
|
||||
REAL_SPARSE_SFM_BLOCKER=KNOWN_CALIBRATION_DATA
|
||||
```
|
||||
|
||||
## PLANNED
|
||||
|
||||
Run a dedicated physically calibrated real campaign, reach `READY`, then execute the existing Sparse SfM path as a retained real proof. Do not use historical uncalibrated campaigns to bypass the calibration contract.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
# 14 — Multi-Campaign Reconstruction
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
MULTI_CAMPAIGN_REGISTRATION=PLANNED
|
||||
MULTI_CAMPAIGN_FUSION=PLANNED
|
||||
RAW_PROJECT_MERGE_WITHOUT_REGISTRATION=REJECTED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md`, Phase H documentation and reconstruction architecture.
|
||||
|
||||
## FROZEN
|
||||
|
||||
Phase H v1 is used only when its FROZEN lineage prerequisites are actually satisfied. It is not a generic merger of independent campaigns.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
Independent campaigns such as separate S21 and A6000 reconstructions require:
|
||||
|
||||
```text
|
||||
independent reconstruction A
|
||||
+
|
||||
independent reconstruction B
|
||||
-> explicit registration
|
||||
-> durable transform
|
||||
-> registration quality/provenance
|
||||
-> explicit accepted alignment
|
||||
-> fusion/consolidation
|
||||
```
|
||||
|
||||
Use rigid or similarity transform according to explicit scale knowledge.
|
||||
|
||||
Campaign contribution and provenance must remain inspectable after alignment and fusion.
|
||||
|
||||
Registration may use automatic overlap evidence and may offer explicit manual control-point assistance as a fallback. The registration algorithm, acceptance thresholds and quality policy are future science and must be versioned/validated before implementation.
|
||||
|
||||
## REJECTED
|
||||
|
||||
Naively combining independent Feature Sets, Tracks or reconstructions merely because they depict the same object.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
# 15 — Dense / MVS
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
MVS_M1=PASS/FROZEN
|
||||
DURABLE_DENSE_EXECUTION=PLANNED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/architecture/reconstruction_pipeline.md` and the canonical MVS-M1 boundary documented there.
|
||||
|
||||
## FROZEN
|
||||
|
||||
OpenMVS is the first dense backend target. MVS-M1 defines the existing external OpenMVS v2.4.0 boundary.
|
||||
|
||||
Do not begin by inventing a generic backend framework.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
Dense execution must become:
|
||||
|
||||
- durable;
|
||||
- restartable;
|
||||
- Resource-Governor controlled;
|
||||
- scratch-aware;
|
||||
- bounded;
|
||||
- cancellable;
|
||||
- failure-atomic;
|
||||
- inspectable in the TUI.
|
||||
|
||||
Validate produced assets before publication. A zero external-process exit code is not sufficient proof.
|
||||
|
||||
Dense failure must never invalidate the source Sparse Reconstruction.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
# 16 — Mesh, Refinement, Texture and Export
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
MESH_TEXTURE_EXPORT=PLANNED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md` and future scoped architecture contracts created before implementation.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
```text
|
||||
Dense
|
||||
-> mesh
|
||||
-> refinement
|
||||
-> texturing
|
||||
-> consolidation
|
||||
-> export
|
||||
```
|
||||
|
||||
Every stage publishes an explicit immutable generation with provenance.
|
||||
|
||||
Minimum target formats:
|
||||
|
||||
```text
|
||||
point cloud: PLY
|
||||
mesh: PLY, OBJ
|
||||
textured mesh: OBJ + MTL + texture assets
|
||||
portable viewer/export: GLB/GLTF
|
||||
STL: optional
|
||||
```
|
||||
|
||||
Export must preserve scale truth. Never label arbitrary monocular gauge units as millimetres or metres.
|
||||
|
||||
Exports must carry a manifest or sidecar sufficient to identify the source reconstruction, mesh/texture generation and scale status.
|
||||
|
||||
Consolidation selects an explicit user-facing result from immutable upstream generations. It does not delete those generations; selection remains explicit and reversible until deliberate cleanup.
|
||||
|
||||
Intermediate external-tool artifacts must not become authoritative until validated and atomically published.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
# 17 — TUI
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
TUI=PRIMARY_CONTROL_SURFACE
|
||||
USER_INTERFACE_LANGUAGE=ENGLISH
|
||||
USER_FACING_TUI_LANGUAGE_NORMALIZATION=PASS
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`AGENTS.md`, `README.md`, current TUI architecture and `docs/product/product_definition.md`.
|
||||
|
||||
## CURRENT/FROZEN
|
||||
|
||||
ncurses remains on its designated/main thread. Reuse the current business-logic / TUI / layout separation and current Queue/Project lifetime boundary.
|
||||
|
||||
Validated sizing includes:
|
||||
|
||||
```text
|
||||
full >=100x30
|
||||
reference compact 72x20
|
||||
minimum supported 60x15
|
||||
```
|
||||
|
||||
Below minimum, render only the bounded terminal-too-small fallback.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
The TUI progressively controls:
|
||||
|
||||
- project;
|
||||
- acquisition;
|
||||
- optics;
|
||||
- calibration;
|
||||
- quality;
|
||||
- pipeline;
|
||||
- Tasks;
|
||||
- Governor;
|
||||
- SSD/scratch;
|
||||
- reconstructions;
|
||||
- viewer;
|
||||
- coverage;
|
||||
- capture guidance;
|
||||
- export;
|
||||
- diagnostics/help.
|
||||
|
||||
Displayed actions and actual handlers must remain consistent.
|
||||
|
||||
Diagnostics must distinguish at least:
|
||||
|
||||
- scientific rejection;
|
||||
- invalid input;
|
||||
- missing prerequisite;
|
||||
- resource wait/throttle;
|
||||
- runtime failure;
|
||||
- corruption;
|
||||
- unsupported version/backend;
|
||||
- user cancellation.
|
||||
|
||||
A generic unowned `failed` state is insufficient for final-product workflows.
|
||||
|
||||
Direct TUI/control labels and the user-facing project/import/catalog/runtime-session surface have been normalized to English and validated.
|
||||
|
||||
Historical persisted labels, path names and deliberate UTF-8 test fixtures are not renamed merely to satisfy a text scan. Internal subsystem diagnostics are not to be mechanically rewritten across FROZEN or persistence-sensitive boundaries; any diagnostic intentionally exposed to the final user-facing product must converge to English when its owning subsystem is explicitly scoped.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
# 18 — Viewer
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
VIEWER=PLANNED
|
||||
VIEWER_ROLE=PASSIVE_SNAPSHOT_CONSUMER
|
||||
VIEWER_CAN_BE_DISABLED=REQUIRED
|
||||
VIEWER_BLOCKS_ENGINE=NO
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/architecture/viewer.md` plus `docs/product/product_definition.md`.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
The graphical viewer is required but does not replace the TUI.
|
||||
|
||||
It never owns mutable scientific truth, Task scheduling or Resource Governor policy.
|
||||
|
||||
It must be able to slow down, drop frames, close or crash without corrupting or blocking the engine.
|
||||
|
||||
Target visualization includes:
|
||||
|
||||
- sparse landmarks;
|
||||
- dense cloud;
|
||||
- mesh;
|
||||
- textured mesh;
|
||||
- registered cameras/frustums;
|
||||
- reconstruction components;
|
||||
- Track support;
|
||||
- reprojection diagnostics;
|
||||
- campaign/ScanSet contribution;
|
||||
- coverage heatmap;
|
||||
- weak/unseen areas and holes;
|
||||
- live camera pose;
|
||||
- suggested capture targets.
|
||||
|
||||
Required interaction includes orbit/pan/zoom, reset/focus, element/region selection, camera selection, visibility toggles, diagnostic inspection, valid-scale measurement and region-of-interest selection for coverage/guidance.
|
||||
|
||||
Visual snapshot buffering must remain bounded. When the viewer falls behind, obsolete snapshots are dropped rather than accumulated without bound.
|
||||
|
||||
Viewer annotations do not silently become scientific input. They may enter science only through an explicit supported operation such as a future control-point registration workflow.
|
||||
|
||||
Functional isolation is mandatory. A separate Unix process is allowed if it improves isolation without duplicating authority.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# 19 — Live Capture Foundation
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
LIVE_CAMERA_SOURCE=PLANNED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md`; Capture/provenance contracts remain authoritative downstream.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
Create one generic live-acquisition adapter boundary before device-specific integrations.
|
||||
|
||||
An adapter may own:
|
||||
|
||||
- discovery;
|
||||
- connection;
|
||||
- preview transport;
|
||||
- optional shutter/control;
|
||||
- metadata retrieval;
|
||||
- full-resolution file transfer;
|
||||
- disconnect/reconnect.
|
||||
|
||||
It may not redefine scientific identities, calibration, Features, Tracks, reconstruction or resource accounting.
|
||||
|
||||
Preview frames are ephemeral observations until explicitly promoted through the supported Capture/provenance path.
|
||||
|
||||
Heavy reconstruction compute remains PC-side.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
# 20 — Sony A6000
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
A6000_LIVE_INTEGRATION=PLANNED
|
||||
A6000_FIRMWARE_MODIFICATION=REJECTED
|
||||
A6000_HARDWARE_MODIFICATION=REJECTED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md`.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
The Sony A6000 remains stock.
|
||||
|
||||
Primary live path:
|
||||
|
||||
```text
|
||||
A6000 native HDMI
|
||||
-> external capture device
|
||||
-> Linux capture device (V4L2/UVC-class where available)
|
||||
-> Lardon3D live adapter
|
||||
-> viewer/localization/guidance
|
||||
```
|
||||
|
||||
Discover actual capture-device capabilities; do not hard-code one USB capture card.
|
||||
|
||||
USB may be used only for capabilities genuinely supported and verified: control, shutter, metadata and file transfer.
|
||||
|
||||
Live preview correctness must not depend on USB control availability.
|
||||
|
||||
If remote shutter is unavailable, on-camera capture followed by normal full-resolution ingestion remains valid.
|
||||
|
||||
```text
|
||||
A6000_HEAVY_COMPUTE=PC_SIDE_ONLY
|
||||
```
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# 21 — Samsung S21
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
S21_LIVE_INTEGRATION=PLANNED
|
||||
S21_ROOT_REQUIRED=NO
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md`.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
The S21 uses a device-specific acquisition adapter only at the device/acquisition boundary.
|
||||
|
||||
Required properties:
|
||||
|
||||
- no root;
|
||||
- no scientific-core fork;
|
||||
- graceful disconnect/reconnect;
|
||||
- explicit full-resolution Capture ingestion;
|
||||
- preview frames remain ephemeral until promoted;
|
||||
- exact optical/calibration assignment for scientific use.
|
||||
|
||||
The exact Android transport is deliberately deferred until implementation research proves the best supported mechanism.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
# 22 — Coverage Analysis
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
COVERAGE_ANALYSIS=PLANNED
|
||||
COVERAGE_SCIENCE=NOT_YET_FROZEN
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md` defines the product need; a separate versioned scientific contract is required before implementation.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
Minimum classification:
|
||||
|
||||
```text
|
||||
UNKNOWN
|
||||
UNSEEN
|
||||
WEAK
|
||||
ADEQUATE
|
||||
```
|
||||
|
||||
Candidate evidence may include observation/view count, distinct camera count, angular diversity, parallax, incidence angle, camera distance, projected resolution, image quality, Feature/Track support, reprojection quality, triangulation quality, visibility/occlusion, dense/mesh evidence, hole/boundary evidence and campaign provenance.
|
||||
|
||||
Coverage may operate on the complete meaningful reconstructed target surface or an explicit user-selected region of interest. A global `complete` claim is invalid when no meaningful target surface/ROI exists.
|
||||
|
||||
Sparse-only coverage analysis is allowed only with an explicit lower-confidence/support boundary. Dense/mesh evidence may strengthen visibility and hole reasoning but must not retroactively falsify sparse uncertainty.
|
||||
|
||||
## STOP
|
||||
|
||||
Thresholds, weights, confidence computation and classification rules are science. Freeze and validate them before implementation.
|
||||
|
||||
Never fabricate a closed surface from sparse points merely to claim that an area is covered.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
# 23 — Capture Guidance
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
CAPTURE_GUIDANCE=PLANNED
|
||||
AUTO_CAPTURE=IDEA
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md` and the future FROZEN coverage/localization contracts.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
Guidance must transform a weakness into an actionable suggestion containing, where available:
|
||||
|
||||
- target region;
|
||||
- suggested direction;
|
||||
- viewpoint/position zone;
|
||||
- angle;
|
||||
- distance;
|
||||
- baseline;
|
||||
- expected evidence improvement;
|
||||
- confidence;
|
||||
- reason.
|
||||
|
||||
Target loop:
|
||||
|
||||
```text
|
||||
existing reconstruction
|
||||
-> coverage analysis
|
||||
-> weak/unseen target
|
||||
-> live camera localization
|
||||
-> project target into live view
|
||||
-> guide operator
|
||||
-> operator acquires full-resolution capture
|
||||
-> normal ingestion
|
||||
-> normal incremental/re-registration scientific update as allowed by existing lineage contracts
|
||||
-> coverage refresh
|
||||
```
|
||||
|
||||
Minimum live localization states:
|
||||
|
||||
```text
|
||||
UNAVAILABLE
|
||||
SEARCHING
|
||||
LOCALIZED
|
||||
LOW_CONFIDENCE
|
||||
LOST
|
||||
```
|
||||
|
||||
Never display a stale pose as current after tracking loss without an explicit stale/lost indication.
|
||||
|
||||
Guidance must be actionable in operator terms where applicable: left/right/up/down, closer/farther, rotate toward/away from target, change incidence angle and change baseline.
|
||||
|
||||
Weak/unseen semantics must not rely on color alone.
|
||||
|
||||
When localization or coverage confidence is insufficient, do not assert a precise overlay or a certain `capture here` instruction. Fall back to a truthful search/diagnostic state.
|
||||
|
||||
For v1, capture remains user-triggered.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
# 24 — Video and Deterministic Keyframes
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
VIDEO_INGESTION=PLANNED
|
||||
KEYFRAME_SCIENCE=NOT_YET_FROZEN
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md`.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
Video is an acquisition source, not a second SfM pipeline.
|
||||
|
||||
```text
|
||||
SOURCE video asset
|
||||
-> deterministic timeline/frame identity
|
||||
-> bounded deterministic keyframe extraction
|
||||
-> quality/blur/redundancy analysis
|
||||
-> explicit selected keyframes
|
||||
-> normal Capture/provenance
|
||||
-> existing scientific pipeline
|
||||
```
|
||||
|
||||
Every retained keyframe must preserve:
|
||||
|
||||
- source video asset identity;
|
||||
- exact frame/timestamp identity;
|
||||
- extraction algorithm/version;
|
||||
- parameter fingerprint.
|
||||
|
||||
## STOP
|
||||
|
||||
Define and validate the versioned keyframe scoring/selection science before implementation. Do not hide heuristic thresholds in unversioned code.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
# 25 — External SSD and Scratch
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
EXTERNAL_SSD_CONTROLLER=VALIDATED
|
||||
TASK_SCRATCH_CONSUMERS=PLANNED
|
||||
PROJECT_SCRATCH_OPT_IN=REQUIRED
|
||||
SWAP_OPT_IN=REQUIRED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
Current SSD controller/resource documents and `docs/product/product_definition.md`.
|
||||
|
||||
## REQUIRED_PRODUCT_TARGET
|
||||
|
||||
Expose:
|
||||
|
||||
- physical device identity;
|
||||
- mount state;
|
||||
- scratch state;
|
||||
- swap state;
|
||||
- capacity/usage;
|
||||
- active leases;
|
||||
- drain state;
|
||||
- safe-to-unplug state.
|
||||
|
||||
Project scratch use is explicit opt-in. Swap enable/disable is also explicit user choice when supported by the validated controller. A newly connected device is never adopted automatically.
|
||||
|
||||
Dense/mesh/refinement/texturing scratch consumers acquire storage only through the existing Governor-owned scratch lease boundary.
|
||||
|
||||
```text
|
||||
SCRATCH!=RAM
|
||||
SWAP!=RAM
|
||||
```
|
||||
|
||||
## REJECTED
|
||||
|
||||
Automatic destructive repartitioning, formatting, destructive fsck, overwriting unknown filesystems, or force-unmounting an active lease.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
# 26 — Performance
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
PERFORMANCE_POLICY=MAXIMUM_SAFE_USEFUL_THROUGHPUT
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`AGENTS.md`, resource-governor and internal-parallelism documents, plus measured tranche-specific evidence.
|
||||
|
||||
## REQUIRED
|
||||
|
||||
Performance work is evidence-driven and must preserve science, identity, publication order and boundedness.
|
||||
|
||||
Use host-aware build/test parallelism and Governor-aware production execution.
|
||||
|
||||
Do not freeze reference-host values such as `12` CPUs, `-j8`, a historic batch size, or `--num-processes 1` as universal policy.
|
||||
|
||||
A long serial path with independent work and safe idle resources is a defect unless there is concrete proof of:
|
||||
|
||||
- true dependency/scientific serialism;
|
||||
- useful-scaling knee;
|
||||
- memory bound/pressure;
|
||||
- I/O saturation;
|
||||
- validated GPU execution;
|
||||
- unavoidable deterministic publication constraint;
|
||||
- another measured bottleneck.
|
||||
|
||||
Validated useful GPU backends are preferred automatically when eligible. Backend failure/fallback behavior must be explicit where the scientific stage supports fallback; do not silently change scientific identity.
|
||||
|
||||
Optimize useful throughput, not utilization graphs.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
# 27 — Tests and Validation
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
VALIDATION_STRATEGY=DELTA_BASED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/development/testing.md`, `AGENTS.md`, global-maintenance evidence and specialized architecture documents.
|
||||
|
||||
## REQUIRED
|
||||
|
||||
Unchanged PASS/FROZEN boundaries inherit retained evidence unless the delta touches their assumptions.
|
||||
|
||||
For unchanged globally reviewed foundations, the retained maintenance baseline is tag `global-maintenance-2026-09-01`; the later `real-a6000-pre-sfm-2026-09-02` checkpoint adds real-data evidence without replacing the maintenance authority. Use the relevant delta rather than replaying a global audit.
|
||||
|
||||
For each tranche:
|
||||
|
||||
1. identify changed contracts;
|
||||
2. build with safe host-aware parallelism;
|
||||
3. run focused unit/integration tests;
|
||||
4. run relevant persistence/restart/resource/concurrency checks;
|
||||
5. use ASan/UBSan and TSan only where applicable and preserve documented third-party qualifications;
|
||||
6. validate real-data only when the tranche explicitly requires it and use non-destructive copies/workspaces;
|
||||
7. run `git diff --check`.
|
||||
|
||||
Do not claim blanket TSan validity for Vulkan unless explicitly proven.
|
||||
|
||||
Historical schema downgrade fixtures must be structurally truthful. A fixture targeting schema version N must remove every schema object introduced after N before rewriting `schema_version`; changing metadata alone is not a valid historical fixture. In particular, v25-aware downgrade fixtures must account for the additive v24 `raw_development_batch_tasks` and v25 `feature_extract_batch_tasks` overlays when targeting earlier versions.
|
||||
|
||||
Do not rerun full historical A-to-Z qualification merely for confidence theater.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
# 28 — Documentation and Comments
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
DOCUMENTATION_LANGUAGE=ENGLISH
|
||||
SOURCE_COMMENT_LANGUAGE=ENGLISH
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`AGENTS.md` repository language policy.
|
||||
|
||||
## REQUIRED
|
||||
|
||||
Current repository technical prose and production source comments converge on English.
|
||||
|
||||
Comments should document mainly:
|
||||
|
||||
- why;
|
||||
- contract;
|
||||
- invariant;
|
||||
- ownership/lifetime;
|
||||
- identity;
|
||||
- persistence ordering;
|
||||
- restart;
|
||||
- cancellation;
|
||||
- concurrency;
|
||||
- resource accounting;
|
||||
- FROZEN boundaries.
|
||||
|
||||
Do not comment obvious statements line by line.
|
||||
|
||||
Historical documents may be translated, but translation must not silently modernize their lifecycle, schema version, identities, numbers, evidence or conclusions.
|
||||
|
||||
This prompt tree must not become a duplicate shadow architecture. Reference canonical documents and restate only execution-critical invariants.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
# 29 — Agent Routing
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
AGENT_ARCHITECTURE=IMPLEMENTATION_NEUTRAL
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`AGENTS.md` and this collective prompt contract.
|
||||
|
||||
## REQUIRED
|
||||
|
||||
This contract must work with:
|
||||
|
||||
- one coding agent;
|
||||
- Codex;
|
||||
- ChatGPT plus a coding agent;
|
||||
- future compatible coding agents.
|
||||
|
||||
The primary executing agent owns tranche coherence.
|
||||
|
||||
Sub-agents are optional and may be used only when they reduce cost/time without losing scope control, authority awareness or validation quality.
|
||||
|
||||
Do not create agent hierarchies for appearance.
|
||||
|
||||
For Codex specifically: read the entire prompt tree before major implementation, use `main`, do not choose historical branches, do not treat the two remotes as separate projects, and return only after complete scoped work or a real STOP.
|
||||
|
||||
## Canonical-reference rule
|
||||
|
||||
When implementation enters this area, read the cited canonical document in full before editing. If this prompt summary and the canonical document appear to conflict, do not silently choose the shorter text: determine whether this file is stale or whether a real contract contradiction exists, then apply the STOP rules where necessary.
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
# 30 — Git, Scope and STOP
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
GIT_CLOSURE_OWNER=HUMAN
|
||||
PROTECTED_SCOPE=scan3d/
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`AGENTS.md` and explicit human ticket scope.
|
||||
|
||||
## Git rules
|
||||
|
||||
Without explicit human authorization, do not:
|
||||
|
||||
- stage;
|
||||
- commit;
|
||||
- push;
|
||||
- create/switch branches or continue development from a detached historical tag/checkpoint;
|
||||
- merge;
|
||||
- rebase;
|
||||
- reset;
|
||||
- restore;
|
||||
- stash;
|
||||
- clean;
|
||||
- amend;
|
||||
- force a Git operation.
|
||||
|
||||
Never use `git add -A`.
|
||||
|
||||
If Git closure is explicitly authorized, stage only exact tranche paths and synchronize both mirrors as a pair unless the human explicitly says otherwise.
|
||||
|
||||
## Protected scope
|
||||
|
||||
- do not modify `scan3d/`;
|
||||
- do not modify other repositories;
|
||||
- do not use retained real scientific projects destructively.
|
||||
|
||||
## STOP conditions
|
||||
|
||||
STOP before:
|
||||
|
||||
- reopening PASS/FROZEN;
|
||||
- changing FROZEN science/identity;
|
||||
- pseudo-calibration;
|
||||
- unauthorized DB schema or Task Kind;
|
||||
- unnecessary generic runtime/backend subsystem;
|
||||
- destructive operations;
|
||||
- protected-scope edits;
|
||||
- unauthorized branch operations;
|
||||
- force push;
|
||||
- unilateral mirror overwrite;
|
||||
- real contradiction between executable code/schema and canonical contract;
|
||||
- material unresolved product ambiguity.
|
||||
|
||||
Difficulty alone is not a STOP condition.
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
# 31 — Implementation Order
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
IMPLEMENTATION_ORDER=DEPENDENCY_DRIVEN
|
||||
IMPLEMENTATION_AUTHORIZATION=NO
|
||||
STEP_0_USER_FACING_LANGUAGE_NORMALIZATION=PASS
|
||||
CURRENT_NEXT=PHYSICAL_AUTOFOCUS_OPTICAL_APPLICABILITY_VALIDATION
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md`, current roadmap and dependencies confirmed on current `main`.
|
||||
|
||||
## PLANNED ORDER
|
||||
|
||||
Implementation remains unauthorized until the human explicitly authorizes a tranche.
|
||||
|
||||
Default dependency order:
|
||||
|
||||
0. user-facing repository/UI language normalization where appropriate — PASS;
|
||||
1. final usable Calibration Science/Tooling/Bootstrap/Workflow v1 compatibility path — PASS/FROZEN;
|
||||
2. Calibration Science v2 design for heterogeneous cameras/lenses/focals, adaptive capture settings and autofocus — PASS/FROZEN;
|
||||
3. Calibration v2 heterogeneous-optics persistence foundation — PASS/FROZEN;
|
||||
4. Heterogeneous calibration publication / Tooling / Bootstrap evolution — PASS/FROZEN;
|
||||
5. Heterogeneous Workflow v2 truthful READY proof — PASS/FROZEN;
|
||||
6. Adaptive capture settings / generic autofocus foundation — PASS/FROZEN;
|
||||
7. physical autofocus/optical applicability validation and dedicated calibrated real campaign — CURRENT; `CALIBRATION_AF_STUDY_EVIDENCE_V1=PASS/FROZEN` supplies deterministic measurement evidence, `CALIBRATION_AF_STUDY_WORKFLOW_BRIDGE_V1=PASS/FROZEN` converts already-materialized Calibration Workflow evidence into AF-study samples without reparsing solver artifacts, and `CALIBRATION_AF_STUDY_ASSEMBLY_V1=PASS/FROZEN` assembles multiple independent materialized samples into one bounded L3DAFST1 artifact; none makes a physical applicability decision;
|
||||
8. real Sparse SfM proof;
|
||||
9. durable Dense/OpenMVS orchestration;
|
||||
10. mesh / refinement / texturing / export;
|
||||
11. viewer foundation;
|
||||
12. offline Coverage Analysis scientific contract and implementation;
|
||||
13. multi-campaign registration / fusion;
|
||||
14. generic live acquisition adapter foundation;
|
||||
15. A6000 HDMI integration;
|
||||
16. S21 integration;
|
||||
17. live camera localization;
|
||||
18. live coverage overlay;
|
||||
19. actionable Capture Guidance;
|
||||
20. video ingestion / deterministic keyframes;
|
||||
21. final integration, UX, restart and performance proof;
|
||||
22. Product Definition v1 Definition-of-Done closure.
|
||||
|
||||
## Adjustment rule
|
||||
|
||||
Change this order only when current repository inspection proves a real dependency difference. Preserve product intent and document the dependency.
|
||||
|
||||
Step 0 closure does not authorize mechanical rewriting of historical persisted labels, path names, deliberate UTF-8 fixtures or persistence-sensitive internal strings. Such text is changed only when its owning scope requires it.
|
||||
|
||||
Do not jump to A6000 live work merely because it is visually interesting. Reconstruction/viewer/coverage foundations required for truthful guidance come first.
|
||||
|
||||
The calibration-v2 insertion before the physical campaign is dependency-driven,
|
||||
not feature creep: real acquisition evidence established that the v1 locked-focus
|
||||
single-optical-state path would make normal engine-bay capture impractical and
|
||||
would contradict the already-FROZEN product requirement for multiple optical
|
||||
configurations per project. Science v2 must be defined before collecting the
|
||||
physical evidence intended to validate it.
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
# 32 — Feature Backlog
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
BACKLOG_CLASSIFICATION=REQUIRED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md` and this execution contract.
|
||||
|
||||
## REQUIRED_V1
|
||||
|
||||
- data-driven camera/lens onboarding;
|
||||
- manual lens support;
|
||||
- zoom/multi-focal configurations;
|
||||
- profile import/export;
|
||||
- usable calibration workflow;
|
||||
- real calibrated Sparse SfM;
|
||||
- durable Dense/OpenMVS;
|
||||
- mesh/refinement/texturing/export;
|
||||
- graphical viewer;
|
||||
- offline coverage;
|
||||
- multi-campaign registration/fusion where required;
|
||||
- generic live adapter;
|
||||
- stock A6000 HDMI integration;
|
||||
- S21 integration without root;
|
||||
- live localization;
|
||||
- live coverage overlay;
|
||||
- actionable capture guidance;
|
||||
- deterministic video keyframes;
|
||||
- restart/recovery;
|
||||
- safe useful resource utilization;
|
||||
- optional SSD scratch lifecycle.
|
||||
|
||||
## OPTIONAL_FUTURE / DEFERRED
|
||||
|
||||
```text
|
||||
AUTO_CAPTURE
|
||||
MULTI_GPU
|
||||
DISTRIBUTED_COMPUTE
|
||||
GENERAL_INTER_TASK_DAG
|
||||
GENERIC_BACKEND_FRAMEWORK
|
||||
CLOUD_RECONSTRUCTION
|
||||
```
|
||||
|
||||
## REJECTED
|
||||
|
||||
```text
|
||||
CAMERA_FIRMWARE_MODIFICATION
|
||||
CAMERA_HARDWARE_MODIFICATION
|
||||
SILENT_CALIBRATION_SUBSTITUTION
|
||||
SILENT_LENS_IDENTITY_INFERENCE
|
||||
```
|
||||
|
||||
An IDEA or OPTIONAL item never becomes v1 scope because an agent prefers it.
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
# 33 — Definition of Done
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
PRODUCT_DONE=OBJECTIVE_EVIDENCE_REQUIRED
|
||||
```
|
||||
|
||||
## Authority
|
||||
|
||||
`docs/product/product_definition.md` and all specialized FROZEN contracts.
|
||||
|
||||
## Final objective evidence
|
||||
|
||||
Lardon3D v1 is complete only when retained evidence demonstrates all of the following:
|
||||
|
||||
- ordinary new camera-body onboarding requires no code change;
|
||||
- ordinary new-lens onboarding requires no code change;
|
||||
- manual lens without EXIF is supported;
|
||||
- zoom/multiple focal optical configurations are supported;
|
||||
- profile import/export is bounded, versioned and conflict-safe;
|
||||
- calibration workflow reaches truthful `READY`;
|
||||
- a dedicated calibrated real campaign completes real Sparse SfM;
|
||||
- Dense/OpenMVS execution is durable, restartable and failure-atomic;
|
||||
- mesh generation works;
|
||||
- refinement works;
|
||||
- texturing works;
|
||||
- interoperable exports are traceable;
|
||||
- arbitrary monocular scale is never mislabeled metric;
|
||||
- viewer displays sparse evidence;
|
||||
- viewer displays dense evidence;
|
||||
- viewer displays mesh;
|
||||
- viewer displays textured mesh;
|
||||
- registered cameras/frustums and components are inspectable;
|
||||
- viewer can be disabled, lag or close/crash without corrupting or blocking the engine;
|
||||
- offline Coverage Analysis classifies `UNKNOWN/UNSEEN/WEAK/ADEQUATE` with versioned validated science and confidence;
|
||||
- the system produces an actionable supplementary viewpoint;
|
||||
- live localization exposes truthful confidence and loss;
|
||||
- stock Sony A6000 works through native HDMI capture without camera modification;
|
||||
- a supplementary full-resolution capture returns through the normal provenance/scientific pipeline;
|
||||
- S21 integration requires no root and does not fork the scientific core;
|
||||
- independent multi-campaign reconstruction can produce an explicit durable registration transform with quality/provenance and accepted fusion without raw Feature/Track merging;
|
||||
- video ingestion produces deterministic bounded keyframes traceable to exact source timeline identity, algorithm/version and parameter fingerprint before normal Capture ingestion;
|
||||
- restart/recovery works across long-running stages;
|
||||
- safe useful CPU/GPU execution is demonstrated on representative hardware;
|
||||
- optional SSD scratch has complete lease/drain/safe-unplug lifecycle when used and swap remains explicit opt-in;
|
||||
- project portability does not depend on temporary paths, current hardware topology/device IDs, scratch mount path, live-device connection, process ID or worker instance;
|
||||
- core workflows remain local-first with no required cloud upload;
|
||||
- cleanup previews destructive removals and never automatically deletes FROZEN/historical evidence;
|
||||
- user-visible diagnostics distinguish scientific rejection, invalid input, missing prerequisite, resource wait/throttle, runtime failure, corruption, unsupported version/backend and cancellation;
|
||||
- no calibration, lens, scale, campaign relationship or provenance is invented;
|
||||
- historical identities and PASS/FROZEN checkpoints remain interpretable;
|
||||
- documentation and TUI behavior match the acquired product state.
|
||||
|
||||
## Not sufficient
|
||||
|
||||
The product is not done merely because it compiles, unit tests pass, Sparse SfM exists, OpenMVS can be launched manually, or a mesh can be produced by hand.
|
||||
|
||||
## Closure
|
||||
|
||||
Final closure requires a delta-based validation report showing how every REQUIRED_V1 item above is satisfied or explicitly identifying any remaining blocker. Only then may `PRODUCT_DEFINITION_V1` product implementation be considered fulfilled.
|
||||
|
|
@ -397,9 +397,9 @@ bool run_impl(Lardon3DTask *t, void *p) {
|
|||
c->db, lardon3d_task_id(t), group_id, materialized_capture_id,
|
||||
group_id) != LARDON3D_PROJECT_DB_OK)
|
||||
return lardon3d_task_fail(t, "Rétention de Capture impossible.");
|
||||
/* Accepted recovery boundary: between S3-E returning and this durable
|
||||
* retention, Capture identity cannot be reconstructed retroactively from
|
||||
* paths, metadata, or image IDs.
|
||||
/* Limite de reprise acceptée: entre le retour de S3-E et cette rétention
|
||||
* durable, une identité de capture ne peut pas être déduite à posteriori
|
||||
* depuis les chemins/métadonnées/ID d'image.
|
||||
*/
|
||||
#ifdef LARDON3D_ACQUISITION_CAMPAIGN_TASK_TESTING
|
||||
const char *after_retention =
|
||||
|
|
|
|||
|
|
@ -1,275 +0,0 @@
|
|||
#include <lardon3d/calibration_af_study.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
enum {
|
||||
kHeaderSize = 72,
|
||||
kSampleSize = 232,
|
||||
kPairSize = 44,
|
||||
kPairFlagSameFocus = 1,
|
||||
kPairFlagFitHoldout = 2,
|
||||
};
|
||||
|
||||
static const unsigned char kMagic[8] = {'L','3','D','A','F','S','T','1'};
|
||||
static const double kProbes[9][2] = {
|
||||
{0.0, 0.0},
|
||||
{-0.7, 0.0}, {0.7, 0.0}, {0.0, -0.7}, {0.0, 0.7},
|
||||
{-0.7, -0.7}, {0.7, -0.7}, {-0.7, 0.7}, {0.7, 0.7},
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
size_t original_index;
|
||||
size_t token_length;
|
||||
} CanonicalSample;
|
||||
|
||||
typedef struct {
|
||||
double center;
|
||||
double edge;
|
||||
double corner;
|
||||
double global;
|
||||
} PairMetric;
|
||||
|
||||
static bool nonzero_digest(const unsigned char value[32]) {
|
||||
unsigned char any = 0;
|
||||
for (size_t i = 0; i < 32; ++i) any |= value[i];
|
||||
return any != 0;
|
||||
}
|
||||
|
||||
static bool bounded_token_length(const char token[128], size_t *length) {
|
||||
if (!token || !length) return false;
|
||||
for (size_t i = 0; i < 128; ++i) {
|
||||
if (token[i] == '\0') {
|
||||
if (i == 0) return false;
|
||||
*length = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool finite_parameters(const Lardon3DCalibrationAfStudySample *s,
|
||||
uint32_t width, uint32_t height) {
|
||||
const double p[8] = {s->fx,s->fy,s->cx,s->cy,s->k1,s->k2,s->p1,s->p2};
|
||||
for (size_t i = 0; i < 8; ++i)
|
||||
if (!isfinite(p[i])) return false;
|
||||
return s->fx > 0.0 && s->fy > 0.0 && s->cx >= 0.0 && s->cy >= 0.0 &&
|
||||
s->cx < (double)width && s->cy < (double)height;
|
||||
}
|
||||
|
||||
static int byte_compare(const unsigned char *a, size_t an,
|
||||
const unsigned char *b, size_t bn) {
|
||||
const size_t n = an < bn ? an : bn;
|
||||
const int cmp = memcmp(a, b, n);
|
||||
if (cmp != 0) return cmp;
|
||||
if (an < bn) return -1;
|
||||
if (an > bn) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int canonical_compare(const Lardon3DCalibrationAfStudyInput *input,
|
||||
const CanonicalSample *a,
|
||||
const CanonicalSample *b) {
|
||||
const Lardon3DCalibrationAfStudySample *sa = &input->samples[a->original_index];
|
||||
const Lardon3DCalibrationAfStudySample *sb = &input->samples[b->original_index];
|
||||
int cmp = byte_compare((const unsigned char *)sa->focus_token, a->token_length,
|
||||
(const unsigned char *)sb->focus_token, b->token_length);
|
||||
if (cmp != 0) return cmp;
|
||||
cmp = memcmp(sa->calibration_evidence_sha256,
|
||||
sb->calibration_evidence_sha256, 32);
|
||||
if (cmp != 0) return cmp;
|
||||
if ((uint32_t)sa->role < (uint32_t)sb->role) return -1;
|
||||
if ((uint32_t)sa->role > (uint32_t)sb->role) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void canonical_sort(const Lardon3DCalibrationAfStudyInput *input,
|
||||
CanonicalSample *values, size_t count) {
|
||||
for (size_t i = 1; i < count; ++i) {
|
||||
CanonicalSample value = values[i];
|
||||
size_t j = i;
|
||||
while (j > 0 && canonical_compare(input, &value, &values[j - 1]) < 0) {
|
||||
values[j] = values[j - 1];
|
||||
--j;
|
||||
}
|
||||
values[j] = value;
|
||||
}
|
||||
}
|
||||
|
||||
static void project(const Lardon3DCalibrationAfStudySample *s,
|
||||
double x, double y, double *u, double *v) {
|
||||
const double r2 = x*x + y*y;
|
||||
const double radial = 1.0 + s->k1*r2 + s->k2*r2*r2;
|
||||
const double xd = x*radial + 2.0*s->p1*x*y + s->p2*(r2 + 2.0*x*x);
|
||||
const double yd = y*radial + s->p1*(r2 + 2.0*y*y) + 2.0*s->p2*x*y;
|
||||
*u = s->fx*xd + s->cx;
|
||||
*v = s->fy*yd + s->cy;
|
||||
}
|
||||
|
||||
static bool pair_metric(const Lardon3DCalibrationAfStudySample *a,
|
||||
const Lardon3DCalibrationAfStudySample *b,
|
||||
PairMetric *out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
for (size_t i = 0; i < 9; ++i) {
|
||||
double au, av, bu, bv;
|
||||
project(a, kProbes[i][0], kProbes[i][1], &au, &av);
|
||||
project(b, kProbes[i][0], kProbes[i][1], &bu, &bv);
|
||||
const double delta = hypot(au - bu, av - bv);
|
||||
if (!isfinite(delta)) return false;
|
||||
if (i == 0) out->center = delta;
|
||||
else if (i <= 4 && delta > out->edge) out->edge = delta;
|
||||
else if (i >= 5 && delta > out->corner) out->corner = delta;
|
||||
if (delta > out->global) out->global = delta;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void put_u32(unsigned char **p, uint32_t value) {
|
||||
for (size_t i = 0; i < 4; ++i) (*p)[i] = (unsigned char)(value >> (8u*i));
|
||||
*p += 4;
|
||||
}
|
||||
|
||||
static void put_u64(unsigned char **p, uint64_t value) {
|
||||
for (size_t i = 0; i < 8; ++i) (*p)[i] = (unsigned char)(value >> (8u*i));
|
||||
*p += 8;
|
||||
}
|
||||
|
||||
static void put_f64(unsigned char **p, double value) {
|
||||
uint64_t bits = 0;
|
||||
if (value == 0.0) value = 0.0;
|
||||
memcpy(&bits, &value, sizeof(bits));
|
||||
put_u64(p, bits);
|
||||
}
|
||||
|
||||
static bool sha256(const unsigned char *bytes, size_t size, unsigned char out[32]) {
|
||||
unsigned int length = 0;
|
||||
return EVP_Digest(bytes, size, out, &length, EVP_sha256(), NULL) == 1 && length == 32;
|
||||
}
|
||||
|
||||
Lardon3DCalibrationAfStudyResult lardon3d_calibration_af_study_produce(
|
||||
const Lardon3DCalibrationAfStudyInput *input,
|
||||
unsigned char *artifact, size_t artifact_capacity, size_t *written,
|
||||
unsigned char artifact_sha256[32],
|
||||
Lardon3DCalibrationAfStudySummary *summary) {
|
||||
if (written) *written = 0;
|
||||
if (artifact_sha256) memset(artifact_sha256, 0, 32);
|
||||
if (summary) memset(summary, 0, sizeof(*summary));
|
||||
if (!input || !artifact || !written || !artifact_sha256 || !summary ||
|
||||
!input->samples || input->sample_count < 2 ||
|
||||
input->sample_count > LARDON3D_CALIBRATION_AF_STUDY_MAX_SAMPLES ||
|
||||
input->width == 0 || input->height == 0 ||
|
||||
!nonzero_digest(input->study_context_sha256))
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_INVALID_ARGUMENT;
|
||||
|
||||
CanonicalSample canonical[LARDON3D_CALIBRATION_AF_STUDY_MAX_SAMPLES];
|
||||
uint32_t fit_count = 0, holdout_count = 0;
|
||||
for (size_t i = 0; i < input->sample_count; ++i) {
|
||||
const Lardon3DCalibrationAfStudySample *s = &input->samples[i];
|
||||
size_t token_length = 0;
|
||||
if ((s->role != LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT &&
|
||||
s->role != LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_HOLDOUT) ||
|
||||
!bounded_token_length(s->focus_token, &token_length) ||
|
||||
!nonzero_digest(s->calibration_evidence_sha256) ||
|
||||
!finite_parameters(s, input->width, input->height))
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_INVALID_EVIDENCE;
|
||||
canonical[i].original_index = i;
|
||||
canonical[i].token_length = token_length;
|
||||
if (s->role == LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT) ++fit_count;
|
||||
else ++holdout_count;
|
||||
}
|
||||
canonical_sort(input, canonical, input->sample_count);
|
||||
for (size_t i = 1; i < input->sample_count; ++i) {
|
||||
const Lardon3DCalibrationAfStudySample *a = &input->samples[canonical[i-1].original_index];
|
||||
const Lardon3DCalibrationAfStudySample *b = &input->samples[canonical[i].original_index];
|
||||
if (canonical[i-1].token_length == canonical[i].token_length &&
|
||||
memcmp(a->focus_token, b->focus_token, canonical[i].token_length) == 0 &&
|
||||
memcmp(a->calibration_evidence_sha256, b->calibration_evidence_sha256, 32) == 0)
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_INVALID_EVIDENCE;
|
||||
}
|
||||
|
||||
const size_t pair_count = input->sample_count * (input->sample_count - 1) / 2;
|
||||
if (pair_count > LARDON3D_CALIBRATION_AF_STUDY_MAX_PAIRS)
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_CAPACITY;
|
||||
const size_t required = (size_t)kHeaderSize + input->sample_count*(size_t)kSampleSize + pair_count*(size_t)kPairSize;
|
||||
if (required > LARDON3D_CALIBRATION_AF_STUDY_MAX_ARTIFACT_BYTES || artifact_capacity < required)
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_CAPACITY;
|
||||
|
||||
unsigned char *p = artifact;
|
||||
memcpy(p, kMagic, 8); p += 8;
|
||||
put_u32(&p, LARDON3D_CALIBRATION_AF_STUDY_VERSION);
|
||||
put_u32(&p, LARDON3D_CALIBRATION_AF_STUDY_PROBE_MODEL_VERSION);
|
||||
memcpy(p, input->study_context_sha256, 32); p += 32;
|
||||
put_u32(&p, input->width);
|
||||
put_u32(&p, input->height);
|
||||
put_u32(&p, (uint32_t)input->sample_count);
|
||||
put_u32(&p, (uint32_t)pair_count);
|
||||
put_u32(&p, fit_count);
|
||||
put_u32(&p, holdout_count);
|
||||
|
||||
for (size_t rank = 0; rank < input->sample_count; ++rank) {
|
||||
const CanonicalSample *c = &canonical[rank];
|
||||
const Lardon3DCalibrationAfStudySample *s = &input->samples[c->original_index];
|
||||
put_u32(&p, (uint32_t)s->role);
|
||||
put_u32(&p, (uint32_t)c->token_length);
|
||||
memset(p, 0, 128);
|
||||
memcpy(p, s->focus_token, c->token_length); p += 128;
|
||||
memcpy(p, s->calibration_evidence_sha256, 32); p += 32;
|
||||
put_f64(&p, s->fx); put_f64(&p, s->fy); put_f64(&p, s->cx); put_f64(&p, s->cy);
|
||||
put_f64(&p, s->k1); put_f64(&p, s->k2); put_f64(&p, s->p1); put_f64(&p, s->p2);
|
||||
}
|
||||
|
||||
Lardon3DCalibrationAfStudySummary local_summary = {0};
|
||||
local_summary.sample_count = (uint32_t)input->sample_count;
|
||||
local_summary.fit_count = fit_count;
|
||||
local_summary.holdout_count = holdout_count;
|
||||
local_summary.pair_count = (uint32_t)pair_count;
|
||||
|
||||
for (size_t ai = 0; ai < input->sample_count; ++ai) {
|
||||
for (size_t bi = ai + 1; bi < input->sample_count; ++bi) {
|
||||
const CanonicalSample *ca = &canonical[ai];
|
||||
const CanonicalSample *cb = &canonical[bi];
|
||||
const Lardon3DCalibrationAfStudySample *a = &input->samples[ca->original_index];
|
||||
const Lardon3DCalibrationAfStudySample *b = &input->samples[cb->original_index];
|
||||
PairMetric metric;
|
||||
if (!pair_metric(a, b, &metric))
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_INVALID_EVIDENCE;
|
||||
const bool same_focus = ca->token_length == cb->token_length &&
|
||||
memcmp(a->focus_token, b->focus_token, ca->token_length) == 0;
|
||||
const bool fit_holdout = a->role != b->role;
|
||||
uint32_t flags = 0;
|
||||
if (same_focus) flags |= kPairFlagSameFocus;
|
||||
if (fit_holdout) flags |= kPairFlagFitHoldout;
|
||||
put_u32(&p, (uint32_t)ai);
|
||||
put_u32(&p, (uint32_t)bi);
|
||||
put_u32(&p, flags);
|
||||
put_f64(&p, metric.center);
|
||||
put_f64(&p, metric.edge);
|
||||
put_f64(&p, metric.corner);
|
||||
put_f64(&p, metric.global);
|
||||
|
||||
if (same_focus) ++local_summary.same_focus_pair_count;
|
||||
else ++local_summary.cross_focus_pair_count;
|
||||
if (fit_holdout) ++local_summary.fit_holdout_pair_count;
|
||||
if (metric.center > local_summary.all_center_max_px) local_summary.all_center_max_px = metric.center;
|
||||
if (metric.edge > local_summary.all_edge_probe_max_px) local_summary.all_edge_probe_max_px = metric.edge;
|
||||
if (metric.corner > local_summary.all_corner_probe_max_px) local_summary.all_corner_probe_max_px = metric.corner;
|
||||
if (metric.global > local_summary.all_global_probe_max_px) local_summary.all_global_probe_max_px = metric.global;
|
||||
if (same_focus && metric.global > local_summary.same_focus_global_probe_max_px)
|
||||
local_summary.same_focus_global_probe_max_px = metric.global;
|
||||
if (!same_focus && metric.global > local_summary.cross_focus_global_probe_max_px)
|
||||
local_summary.cross_focus_global_probe_max_px = metric.global;
|
||||
if (fit_holdout && metric.global > local_summary.fit_holdout_global_probe_max_px)
|
||||
local_summary.fit_holdout_global_probe_max_px = metric.global;
|
||||
}
|
||||
}
|
||||
if ((size_t)(p - artifact) != required)
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ENCODING_ERROR;
|
||||
if (!sha256(artifact, required, artifact_sha256))
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ENCODING_ERROR;
|
||||
*summary = local_summary;
|
||||
*written = required;
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_OK;
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
#include <lardon3d/calibration_af_study_assembly.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool nonzero_digest(const unsigned char value[32]) {
|
||||
unsigned char any = 0;
|
||||
for (size_t index = 0; index < 32; ++index) any |= value[index];
|
||||
return any != 0;
|
||||
}
|
||||
|
||||
static Lardon3DCalibrationAfStudyAssemblyResult map_bridge_result(
|
||||
Lardon3DCalibrationAfStudyWorkflowResult result) {
|
||||
switch (result) {
|
||||
case LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_ARGUMENT:
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_ARGUMENT;
|
||||
case LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_EVIDENCE:
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE;
|
||||
case LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_CRYPTO_ERROR:
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_CRYPTO_ERROR;
|
||||
case LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_OK:
|
||||
break;
|
||||
}
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE;
|
||||
}
|
||||
|
||||
static Lardon3DCalibrationAfStudyAssemblyResult map_study_result(
|
||||
Lardon3DCalibrationAfStudyResult result) {
|
||||
switch (result) {
|
||||
case LARDON3D_CALIBRATION_AF_STUDY_INVALID_ARGUMENT:
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_ARGUMENT;
|
||||
case LARDON3D_CALIBRATION_AF_STUDY_CAPACITY:
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_CAPACITY;
|
||||
case LARDON3D_CALIBRATION_AF_STUDY_INVALID_EVIDENCE:
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE;
|
||||
case LARDON3D_CALIBRATION_AF_STUDY_ENCODING_ERROR:
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_ENCODING_ERROR;
|
||||
case LARDON3D_CALIBRATION_AF_STUDY_OK:
|
||||
break;
|
||||
}
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE;
|
||||
}
|
||||
|
||||
Lardon3DCalibrationAfStudyAssemblyResult
|
||||
lardon3d_calibration_af_study_assemble_materialized(
|
||||
const Lardon3DCalibrationAfStudyAssemblyInput *input,
|
||||
unsigned char *artifact, size_t artifact_capacity, size_t *written,
|
||||
unsigned char artifact_sha256[32],
|
||||
Lardon3DCalibrationAfStudySummary *summary) {
|
||||
if (written) *written = 0;
|
||||
if (artifact_sha256) memset(artifact_sha256, 0, 32);
|
||||
if (summary) memset(summary, 0, sizeof(*summary));
|
||||
|
||||
if (!input || !artifact || !written || !artifact_sha256 || !summary ||
|
||||
!input->entries || input->entry_count < 2 ||
|
||||
input->entry_count > LARDON3D_CALIBRATION_AF_STUDY_MAX_SAMPLES ||
|
||||
!nonzero_digest(input->study_context_sha256))
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_ARGUMENT;
|
||||
|
||||
Lardon3DCalibrationAfStudySample
|
||||
samples[LARDON3D_CALIBRATION_AF_STUDY_MAX_SAMPLES];
|
||||
memset(samples, 0, sizeof(samples));
|
||||
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
|
||||
for (size_t index = 0; index < input->entry_count; ++index) {
|
||||
const Lardon3DCalibrationAfStudyAssemblyEntry *entry =
|
||||
&input->entries[index];
|
||||
if (!entry->external || !entry->focus_token)
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_ARGUMENT;
|
||||
|
||||
Lardon3DCalibrationAfStudyWorkflowResult bridge =
|
||||
lardon3d_calibration_af_study_sample_from_materialized_evidence(
|
||||
entry->external, entry->role, entry->focus_token, &samples[index]);
|
||||
if (bridge != LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_OK)
|
||||
return map_bridge_result(bridge);
|
||||
|
||||
if (index == 0) {
|
||||
width = entry->external->oriented_width;
|
||||
height = entry->external->oriented_height;
|
||||
} else if (entry->external->oriented_width != width ||
|
||||
entry->external->oriented_height != height) {
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE;
|
||||
}
|
||||
|
||||
/* Stronger than the raw L3DAFST1 producer's (token,digest) duplicate rule:
|
||||
* an already-materialized calibration result is independent evidence only
|
||||
* once, regardless of how the caller labels focus or study role. */
|
||||
for (size_t previous = 0; previous < index; ++previous) {
|
||||
if (memcmp(samples[previous].calibration_evidence_sha256,
|
||||
samples[index].calibration_evidence_sha256, 32) == 0)
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_INVALID_EVIDENCE;
|
||||
}
|
||||
}
|
||||
|
||||
Lardon3DCalibrationAfStudyInput study = {0};
|
||||
memcpy(study.study_context_sha256, input->study_context_sha256, 32);
|
||||
study.width = width;
|
||||
study.height = height;
|
||||
study.samples = samples;
|
||||
study.sample_count = input->entry_count;
|
||||
|
||||
Lardon3DCalibrationAfStudyResult result =
|
||||
lardon3d_calibration_af_study_produce(
|
||||
&study, artifact, artifact_capacity, written, artifact_sha256,
|
||||
summary);
|
||||
if (result != LARDON3D_CALIBRATION_AF_STUDY_OK)
|
||||
return map_study_result(result);
|
||||
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_ASSEMBLY_OK;
|
||||
}
|
||||
|
|
@ -1,211 +0,0 @@
|
|||
#include <lardon3d/calibration_af_study_workflow.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool nonzero_digest(const unsigned char value[32]) {
|
||||
unsigned char any = 0;
|
||||
for (size_t index = 0; index < 32; ++index) any |= value[index];
|
||||
return any != 0;
|
||||
}
|
||||
|
||||
static bool token_copy(
|
||||
const char *input,
|
||||
char output[LARDON3D_CALIBRATION_AF_STUDY_FOCUS_TOKEN_CAPACITY]) {
|
||||
if (!input) return false;
|
||||
size_t length = 0;
|
||||
while (length < LARDON3D_CALIBRATION_AF_STUDY_FOCUS_TOKEN_CAPACITY &&
|
||||
input[length] != '\0')
|
||||
++length;
|
||||
if (length == 0 ||
|
||||
length >= LARDON3D_CALIBRATION_AF_STUDY_FOCUS_TOKEN_CAPACITY)
|
||||
return false;
|
||||
memset(output, 0, LARDON3D_CALIBRATION_AF_STUDY_FOCUS_TOKEN_CAPACITY);
|
||||
memcpy(output, input, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool digest_begin(EVP_MD_CTX **ctx, const char *domain) {
|
||||
*ctx = EVP_MD_CTX_new();
|
||||
return *ctx &&
|
||||
EVP_DigestInit_ex(*ctx, EVP_sha256(), NULL) == 1 &&
|
||||
EVP_DigestUpdate(*ctx, domain, strlen(domain)) == 1;
|
||||
}
|
||||
|
||||
static bool digest_add(EVP_MD_CTX *ctx, const void *bytes, size_t size) {
|
||||
return EVP_DigestUpdate(ctx, bytes, size) == 1;
|
||||
}
|
||||
|
||||
static bool digest_finish(EVP_MD_CTX *ctx, unsigned char output[32]) {
|
||||
unsigned int output_size = 0;
|
||||
const bool ok =
|
||||
EVP_DigestFinal_ex(ctx, output, &output_size) == 1 && output_size == 32;
|
||||
EVP_MD_CTX_free(ctx);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void encode_u32_le(uint32_t value, unsigned char output[4]) {
|
||||
for (size_t index = 0; index < 4; ++index)
|
||||
output[index] = (unsigned char)(value >> (8u * index));
|
||||
}
|
||||
|
||||
static void encode_f64_le(double value, unsigned char output[8]) {
|
||||
uint64_t bits = 0;
|
||||
if (value == 0.0) value = 0.0;
|
||||
memcpy(&bits, &value, sizeof(bits));
|
||||
for (size_t index = 0; index < 8; ++index)
|
||||
output[index] = (unsigned char)(bits >> (8u * index));
|
||||
}
|
||||
|
||||
static bool validation_binding(
|
||||
const Lardon3DCalibrationWorkflowInputBoundary *boundary,
|
||||
unsigned char output[32]) {
|
||||
static const char domain[] = "L3DCAL_WORKFLOW_VALIDATION_V1\n";
|
||||
EVP_MD_CTX *ctx = NULL;
|
||||
if (!digest_begin(&ctx, domain)) {
|
||||
if (ctx) EVP_MD_CTX_free(ctx);
|
||||
return false;
|
||||
}
|
||||
const bool ok =
|
||||
digest_add(ctx, boundary->detection_sha256, 32) &&
|
||||
digest_add(ctx, boundary->solve_sha256, 32) &&
|
||||
digest_add(ctx, boundary->evidence_sha256, 32) &&
|
||||
digest_add(ctx, boundary->producer_sha256, 32);
|
||||
if (!ok) {
|
||||
EVP_MD_CTX_free(ctx);
|
||||
return false;
|
||||
}
|
||||
return digest_finish(ctx, output);
|
||||
}
|
||||
|
||||
static bool sample_identity(
|
||||
const Lardon3DCalibrationWorkflowExternalEvidence *external,
|
||||
unsigned char output[32]) {
|
||||
static const char domain[] = "L3DAF_CALIBRATION_SAMPLE_V1\n";
|
||||
EVP_MD_CTX *ctx = NULL;
|
||||
if (!digest_begin(&ctx, domain)) {
|
||||
if (ctx) EVP_MD_CTX_free(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok =
|
||||
digest_add(ctx, external->target_sha256, 32) &&
|
||||
digest_add(ctx, external->optical_state_sha256, 32) &&
|
||||
digest_add(ctx, external->solver_executable_sha256, 32) &&
|
||||
digest_add(ctx, external->solver_configuration_sha256, 32) &&
|
||||
digest_add(ctx, external->initialization_evidence_sha256, 32) &&
|
||||
digest_add(ctx, external->validation_evidence_sha256, 32);
|
||||
|
||||
unsigned char encoded_u32[4];
|
||||
encode_u32_le(external->oriented_width, encoded_u32);
|
||||
ok = ok && digest_add(ctx, encoded_u32, sizeof(encoded_u32));
|
||||
encode_u32_le(external->oriented_height, encoded_u32);
|
||||
ok = ok && digest_add(ctx, encoded_u32, sizeof(encoded_u32));
|
||||
|
||||
for (size_t parameter = 0; parameter < 8 && ok; ++parameter) {
|
||||
unsigned char encoded_f64[8];
|
||||
encode_f64_le(external->repeated_parameters[0][parameter], encoded_f64);
|
||||
ok = digest_add(ctx, encoded_f64, sizeof(encoded_f64));
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
EVP_MD_CTX_free(ctx);
|
||||
return false;
|
||||
}
|
||||
return digest_finish(ctx, output);
|
||||
}
|
||||
|
||||
static bool boundary_provenance_valid(
|
||||
const Lardon3DCalibrationWorkflowExternalEvidence *external) {
|
||||
const Lardon3DCalibrationWorkflowInputBoundary *boundary =
|
||||
&external->boundary;
|
||||
|
||||
if (!nonzero_digest(boundary->session_sha256) ||
|
||||
!nonzero_digest(boundary->detection_sha256) ||
|
||||
!nonzero_digest(boundary->solve_sha256) ||
|
||||
!nonzero_digest(boundary->evidence_sha256) ||
|
||||
!nonzero_digest(boundary->producer_sha256) ||
|
||||
!nonzero_digest(boundary->optical_state_sha256) ||
|
||||
!nonzero_digest(boundary->solver_executable_sha256) ||
|
||||
!nonzero_digest(boundary->solver_configuration_sha256))
|
||||
return false;
|
||||
|
||||
return memcmp(external->optical_state_sha256,
|
||||
boundary->optical_state_sha256, 32) == 0 &&
|
||||
memcmp(external->solver_executable_sha256,
|
||||
boundary->solver_executable_sha256, 32) == 0 &&
|
||||
memcmp(external->solver_configuration_sha256,
|
||||
boundary->solver_configuration_sha256, 32) == 0 &&
|
||||
memcmp(external->initialization_evidence_sha256,
|
||||
boundary->session_sha256, 32) == 0;
|
||||
}
|
||||
|
||||
static bool published_parameters_valid(
|
||||
const Lardon3DCalibrationWorkflowExternalEvidence *external) {
|
||||
const double *published = external->repeated_parameters[0];
|
||||
for (size_t parameter = 0; parameter < 8; ++parameter) {
|
||||
if (!isfinite(published[parameter])) return false;
|
||||
if (external->repeated_parameters[1][parameter] != published[parameter] ||
|
||||
external->repeated_parameters[2][parameter] != published[parameter])
|
||||
return false;
|
||||
}
|
||||
|
||||
return published[0] > 0.0 && published[1] > 0.0 &&
|
||||
published[2] >= 0.0 && published[3] >= 0.0 &&
|
||||
published[2] < (double)external->oriented_width &&
|
||||
published[3] < (double)external->oriented_height;
|
||||
}
|
||||
|
||||
Lardon3DCalibrationAfStudyWorkflowResult
|
||||
lardon3d_calibration_af_study_sample_from_materialized_evidence(
|
||||
const Lardon3DCalibrationWorkflowExternalEvidence *external,
|
||||
Lardon3DCalibrationAfStudySampleRole role, const char *focus_token,
|
||||
Lardon3DCalibrationAfStudySample *output) {
|
||||
if (output) memset(output, 0, sizeof(*output));
|
||||
|
||||
if (!external || !focus_token || !output ||
|
||||
(role != LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT &&
|
||||
role != LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_HOLDOUT))
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_ARGUMENT;
|
||||
|
||||
Lardon3DCalibrationAfStudySample sample = {0};
|
||||
if (!token_copy(focus_token, sample.focus_token))
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_ARGUMENT;
|
||||
|
||||
if (external->oriented_width == 0 || external->oriented_height == 0 ||
|
||||
!nonzero_digest(external->target_sha256) ||
|
||||
!nonzero_digest(external->optical_state_sha256) ||
|
||||
!nonzero_digest(external->solver_executable_sha256) ||
|
||||
!nonzero_digest(external->solver_configuration_sha256) ||
|
||||
!nonzero_digest(external->initialization_evidence_sha256) ||
|
||||
!nonzero_digest(external->validation_evidence_sha256) ||
|
||||
!boundary_provenance_valid(external) ||
|
||||
!published_parameters_valid(external))
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_EVIDENCE;
|
||||
|
||||
unsigned char expected_validation[32] = {0};
|
||||
if (!validation_binding(&external->boundary, expected_validation))
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_CRYPTO_ERROR;
|
||||
if (memcmp(external->validation_evidence_sha256,
|
||||
expected_validation, 32) != 0)
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_EVIDENCE;
|
||||
|
||||
sample.role = role;
|
||||
if (!sample_identity(external, sample.calibration_evidence_sha256))
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_CRYPTO_ERROR;
|
||||
|
||||
sample.fx = external->repeated_parameters[0][0];
|
||||
sample.fy = external->repeated_parameters[0][1];
|
||||
sample.cx = external->repeated_parameters[0][2];
|
||||
sample.cy = external->repeated_parameters[0][3];
|
||||
sample.k1 = external->repeated_parameters[0][4];
|
||||
sample.k2 = external->repeated_parameters[0][5];
|
||||
sample.p1 = external->repeated_parameters[0][6];
|
||||
sample.p2 = external->repeated_parameters[0][7];
|
||||
|
||||
*output = sample;
|
||||
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_OK;
|
||||
}
|
||||
|
|
@ -1,350 +0,0 @@
|
|||
#include <lardon3d/calibration_bootstrap_v2.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
enum {
|
||||
BOOTSTRAP_V2_HEADER_SIZE = 28,
|
||||
BOOTSTRAP_V2_GROUP_SIZE = 232,
|
||||
BOOTSTRAP_V2_ENTRY_SIZE = 144,
|
||||
BOOTSTRAP_V2_VALIDATION_FLAGS = 15,
|
||||
};
|
||||
|
||||
static const unsigned char bootstrap_v2_magic[8] = {'L', '3', 'D', 'C', 'A', 'L', 'B', '2'};
|
||||
|
||||
typedef struct {
|
||||
const unsigned char *bytes;
|
||||
size_t remaining;
|
||||
} BootstrapV2Reader;
|
||||
|
||||
typedef struct {
|
||||
uint32_t selected_item_index;
|
||||
unsigned char representation_sha256[32];
|
||||
Lardon3DSparseCalibration calibration;
|
||||
Lardon3DSparseCalibrationMember member;
|
||||
} BootstrapV2Entry;
|
||||
|
||||
static bool take(BootstrapV2Reader *reader, void *output, size_t count) {
|
||||
if (count > reader->remaining) return false;
|
||||
if (output) memcpy(output, reader->bytes, count);
|
||||
reader->bytes += count;
|
||||
reader->remaining -= count;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_u32(BootstrapV2Reader *reader, uint32_t *output) {
|
||||
unsigned char bytes[4];
|
||||
if (!take(reader, bytes, sizeof(bytes))) return false;
|
||||
*output = (uint32_t)bytes[0] | ((uint32_t)bytes[1] << 8) |
|
||||
((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_u64(BootstrapV2Reader *reader, uint64_t *output) {
|
||||
unsigned char bytes[8];
|
||||
if (!take(reader, bytes, sizeof(bytes))) return false;
|
||||
*output = 0;
|
||||
for (size_t index = 0; index < sizeof(bytes); ++index)
|
||||
*output |= (uint64_t)bytes[index] << (8u * index);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_f64(BootstrapV2Reader *reader, double *output) {
|
||||
uint64_t bits = 0;
|
||||
if (!read_u64(reader, &bits)) return false;
|
||||
memcpy(output, &bits, sizeof(bits));
|
||||
if (!isfinite(*output)) return false;
|
||||
if (*output == 0.0) *output = 0.0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sha256(const unsigned char *bytes, size_t size, unsigned char output[32]) {
|
||||
unsigned int output_size = 0;
|
||||
return EVP_Digest(bytes, size, output, &output_size, EVP_sha256(), NULL) == 1 &&
|
||||
output_size == 32;
|
||||
}
|
||||
|
||||
static bool nonzero_digest(const unsigned char value[32]) {
|
||||
unsigned char any = 0;
|
||||
for (size_t index = 0; index < 32; ++index) any |= value[index];
|
||||
return any != 0;
|
||||
}
|
||||
|
||||
static Lardon3DCalibrationBootstrapV2Result db_result(Lardon3DProjectDbResult result) {
|
||||
if (result == LARDON3D_PROJECT_DB_CONSTRAINT || result == LARDON3D_PROJECT_DB_NOT_FOUND)
|
||||
return LARDON3D_CALIBRATION_BOOTSTRAP_V2_SELECTION_CONFLICT;
|
||||
return LARDON3D_CALIBRATION_BOOTSTRAP_V2_DB_ERROR;
|
||||
}
|
||||
|
||||
static Lardon3DCalibrationBootstrapV2Result bootstrap_v2_publish(
|
||||
Lardon3DProjectDb *database, uint64_t execution_id,
|
||||
const unsigned char *artifact, size_t artifact_size,
|
||||
const unsigned char expected_artifact_sha256[32],
|
||||
Lardon3DCalibrationBootstrapV2Member *published_members,
|
||||
size_t member_capacity, bool attach_scope,
|
||||
Lardon3DCalibrationBootstrapV2Output *output) {
|
||||
if (!database || execution_id == 0 || !artifact || !expected_artifact_sha256 || !output ||
|
||||
artifact_size < BOOTSTRAP_V2_HEADER_SIZE ||
|
||||
artifact_size > LARDON3D_CALIBRATION_BOOTSTRAP_V2_MAX_BYTES)
|
||||
return LARDON3D_CALIBRATION_BOOTSTRAP_V2_INVALID_ARGUMENT;
|
||||
memset(output, 0, sizeof(*output));
|
||||
|
||||
unsigned char artifact_sha256[32];
|
||||
if (!sha256(artifact, artifact_size, artifact_sha256))
|
||||
return LARDON3D_CALIBRATION_BOOTSTRAP_V2_DB_ERROR;
|
||||
if (memcmp(artifact_sha256, expected_artifact_sha256, 32) != 0)
|
||||
return LARDON3D_CALIBRATION_BOOTSTRAP_V2_PROVENANCE_MISMATCH;
|
||||
|
||||
BootstrapV2Reader reader = {artifact, artifact_size};
|
||||
unsigned char magic[8];
|
||||
uint32_t version, model_kind, model_version, group_count, entry_count;
|
||||
if (!take(&reader, magic, sizeof(magic)) || !read_u32(&reader, &version) ||
|
||||
!read_u32(&reader, &model_kind) || !read_u32(&reader, &model_version) ||
|
||||
!read_u32(&reader, &group_count) || !read_u32(&reader, &entry_count) ||
|
||||
memcmp(magic, bootstrap_v2_magic, sizeof(magic)) != 0 ||
|
||||
version != LARDON3D_CALIBRATION_BOOTSTRAP_V2_ARTIFACT_VERSION ||
|
||||
model_kind != LARDON3D_SPARSE_SFM_CALIBRATION_KIND_PINHOLE ||
|
||||
model_version != LARDON3D_SPARSE_SFM_CALIBRATION_VERSION || group_count == 0 ||
|
||||
group_count > LARDON3D_CALIBRATION_BOOTSTRAP_V2_MAX_GROUPS || entry_count == 0 ||
|
||||
entry_count > LARDON3D_CALIBRATION_BOOTSTRAP_V2_MAX_ENTRIES)
|
||||
return LARDON3D_CALIBRATION_BOOTSTRAP_V2_MALFORMED_ARTIFACT;
|
||||
if (!attach_scope && (!published_members || member_capacity < entry_count))
|
||||
return LARDON3D_CALIBRATION_BOOTSTRAP_V2_INVALID_ARGUMENT;
|
||||
size_t expected_size = BOOTSTRAP_V2_HEADER_SIZE + (size_t)group_count * BOOTSTRAP_V2_GROUP_SIZE;
|
||||
if (entry_count > (SIZE_MAX - expected_size) / BOOTSTRAP_V2_ENTRY_SIZE)
|
||||
return LARDON3D_CALIBRATION_BOOTSTRAP_V2_MALFORMED_ARTIFACT;
|
||||
expected_size += (size_t)entry_count * BOOTSTRAP_V2_ENTRY_SIZE;
|
||||
if (artifact_size != expected_size)
|
||||
return LARDON3D_CALIBRATION_BOOTSTRAP_V2_MALFORMED_ARTIFACT;
|
||||
|
||||
BootstrapV2Entry *entries = calloc(entry_count, sizeof(*entries));
|
||||
bool *covered = calloc(entry_count, sizeof(*covered));
|
||||
if (!entries || !covered) {
|
||||
free(entries);
|
||||
free(covered);
|
||||
return LARDON3D_CALIBRATION_BOOTSTRAP_V2_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
Lardon3DCalibrationBootstrapV2Result result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK;
|
||||
size_t parsed_entries = 0;
|
||||
unsigned char previous_group_identity[32] = {0};
|
||||
uint32_t previous_group_version = 0;
|
||||
for (uint32_t group_index = 0;
|
||||
group_index < group_count && result == LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK;
|
||||
++group_index) {
|
||||
const unsigned char *group_start = reader.bytes;
|
||||
unsigned char group_identity[32], evidence[6][32];
|
||||
uint32_t group_version, member_count;
|
||||
if (!take(&reader, group_identity, 32) || !read_u32(&reader, &group_version)) {
|
||||
result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_MALFORMED_ARTIFACT;
|
||||
break;
|
||||
}
|
||||
for (size_t index = 0; index < 6; ++index)
|
||||
if (!take(&reader, evidence[index], 32)) {
|
||||
result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_MALFORMED_ARTIFACT;
|
||||
break;
|
||||
}
|
||||
if (result != LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK ||
|
||||
!read_u32(&reader, &member_count)) {
|
||||
result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_MALFORMED_ARTIFACT;
|
||||
break;
|
||||
}
|
||||
int group_order = group_index == 0 ? -1 :
|
||||
memcmp(previous_group_identity, group_identity, 32);
|
||||
if (!nonzero_digest(group_identity) || group_version == 0 || member_count == 0 ||
|
||||
member_count > entry_count - parsed_entries || group_order > 0 ||
|
||||
(group_order == 0 && previous_group_version >= group_version)) {
|
||||
result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_MALFORMED_ARTIFACT;
|
||||
break;
|
||||
}
|
||||
for (size_t index = 0; index < 6; ++index)
|
||||
if (!nonzero_digest(evidence[index])) {
|
||||
result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_MALFORMED_ARTIFACT;
|
||||
break;
|
||||
}
|
||||
memcpy(previous_group_identity, group_identity, 32);
|
||||
previous_group_version = group_version;
|
||||
size_t group_first_entry = parsed_entries;
|
||||
uint32_t previous_item_index = 0;
|
||||
for (uint32_t member_index = 0;
|
||||
member_index < member_count && result == LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK;
|
||||
++member_index) {
|
||||
BootstrapV2Entry *entry = &entries[parsed_entries];
|
||||
uint32_t width, height, support_images, support_observations, validation_flags;
|
||||
double reprojection_rmse_px, maximum_parameter_delta;
|
||||
if (!read_u32(&reader, &entry->selected_item_index) ||
|
||||
!read_u64(&reader, &entry->member.image_id) ||
|
||||
!take(&reader, entry->representation_sha256, 32) || !read_u32(&reader, &width) ||
|
||||
!read_u32(&reader, &height) || !read_f64(&reader, &entry->calibration.fx) ||
|
||||
!read_f64(&reader, &entry->calibration.fy) ||
|
||||
!read_f64(&reader, &entry->calibration.cx) ||
|
||||
!read_f64(&reader, &entry->calibration.cy) ||
|
||||
!read_f64(&reader, &entry->calibration.k1) ||
|
||||
!read_f64(&reader, &entry->calibration.k2) ||
|
||||
!read_f64(&reader, &entry->calibration.p1) ||
|
||||
!read_f64(&reader, &entry->calibration.p2) ||
|
||||
!read_u32(&reader, &support_images) ||
|
||||
!read_u32(&reader, &support_observations) ||
|
||||
!read_f64(&reader, &reprojection_rmse_px) ||
|
||||
!read_f64(&reader, &maximum_parameter_delta) ||
|
||||
!read_u32(&reader, &validation_flags) ||
|
||||
entry->selected_item_index >= entry_count || covered[entry->selected_item_index] ||
|
||||
(member_index > 0 && previous_item_index >= entry->selected_item_index) ||
|
||||
entry->member.image_id == 0 || !nonzero_digest(entry->representation_sha256) ||
|
||||
width == 0 || height == 0 || entry->calibration.fx <= 0.0 ||
|
||||
entry->calibration.fy <= 0.0 || entry->calibration.cx < 0.0 ||
|
||||
entry->calibration.cy < 0.0 || entry->calibration.cx >= (double)width ||
|
||||
entry->calibration.cy >= (double)height || support_images == 0 ||
|
||||
support_observations == 0 || reprojection_rmse_px < 0.0 ||
|
||||
maximum_parameter_delta < 0.0 || validation_flags != BOOTSTRAP_V2_VALIDATION_FLAGS) {
|
||||
result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_MALFORMED_ARTIFACT;
|
||||
break;
|
||||
}
|
||||
for (size_t prior = 0; prior < parsed_entries; ++prior)
|
||||
if (entries[prior].member.image_id == entry->member.image_id) {
|
||||
result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_MALFORMED_ARTIFACT;
|
||||
break;
|
||||
}
|
||||
covered[entry->selected_item_index] = true;
|
||||
previous_item_index = entry->selected_item_index;
|
||||
entry->calibration.model_kind = model_kind;
|
||||
entry->calibration.model_version = model_version;
|
||||
entry->calibration.width = width;
|
||||
entry->calibration.height = height;
|
||||
entry->calibration.provenance_kind =
|
||||
LARDON3D_SPARSE_SFM_PROVENANCE_IMPORTED_TRUSTED;
|
||||
++parsed_entries;
|
||||
}
|
||||
if (result == LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK) {
|
||||
unsigned char group_fingerprint[32];
|
||||
size_t group_size = (size_t)(reader.bytes - group_start);
|
||||
if (!sha256(group_start, group_size, group_fingerprint)) {
|
||||
result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_DB_ERROR;
|
||||
break;
|
||||
}
|
||||
/* Group-local hashing preserves independently acquired provenance: an
|
||||
* unrelated group's addition cannot redefine these calibrations. */
|
||||
for (size_t index = group_first_entry; index < parsed_entries; ++index)
|
||||
memcpy(entries[index].calibration.provenance_fingerprint, group_fingerprint, 32);
|
||||
}
|
||||
}
|
||||
if (result == LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK &&
|
||||
(parsed_entries != entry_count || reader.remaining != 0))
|
||||
result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_MALFORMED_ARTIFACT;
|
||||
for (uint32_t index = 0;
|
||||
index < entry_count && result == LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK; ++index)
|
||||
if (!covered[index]) result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_MALFORMED_ARTIFACT;
|
||||
|
||||
Lardon3DProjectDbSelectedExecution execution;
|
||||
Lardon3DProjectDbResult db_status = LARDON3D_PROJECT_DB_OK;
|
||||
if (result == LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK) {
|
||||
db_status = lardon3d_project_db_load_selected_execution(database, execution_id, &execution);
|
||||
if (db_status != LARDON3D_PROJECT_DB_OK)
|
||||
result = db_result(db_status);
|
||||
else if (execution.item_count != entry_count ||
|
||||
(execution.stage != LARDON3D_SELECTED_EXECUTION_CALIBRATION &&
|
||||
execution.stage != LARDON3D_SELECTED_EXECUTION_READY))
|
||||
result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_SELECTION_CONFLICT;
|
||||
}
|
||||
for (size_t index = 0;
|
||||
index < parsed_entries && result == LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK; ++index) {
|
||||
BootstrapV2Entry *entry = &entries[index];
|
||||
Lardon3DProjectDbSelectedExecutionItem selected;
|
||||
Lardon3DProjectDbImage image;
|
||||
Lardon3DProjectDbImageAsset asset;
|
||||
db_status = lardon3d_project_db_load_selected_execution_item(
|
||||
database, execution_id, entry->selected_item_index, &selected);
|
||||
if (db_status != LARDON3D_PROJECT_DB_OK || !selected.has_image ||
|
||||
selected.image_id != entry->member.image_id) {
|
||||
result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_SELECTION_CONFLICT;
|
||||
break;
|
||||
}
|
||||
db_status = lardon3d_project_db_load_image(database, entry->member.image_id, &image, &asset);
|
||||
if (db_status != LARDON3D_PROJECT_DB_OK ||
|
||||
memcmp(asset.sha256, entry->representation_sha256, 32) != 0) {
|
||||
result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_SELECTION_CONFLICT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* All bytes and immutable selected bindings are verified before this first
|
||||
* write. Publication uses the existing short, content-addressed DB APIs; a
|
||||
* crash may retain reusable calibrations but READY remains guarded by the
|
||||
* single complete-scope attachment below. */
|
||||
for (size_t index = 0;
|
||||
index < parsed_entries && result == LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK; ++index) {
|
||||
Lardon3DSparseCalibration stored;
|
||||
db_status = lardon3d_sparse_calibration_create(database, &entries[index].calibration,
|
||||
&stored);
|
||||
if (db_status != LARDON3D_PROJECT_DB_OK) {
|
||||
result = db_result(db_status);
|
||||
break;
|
||||
}
|
||||
entries[index].member.calibration_id = stored.calibration_id;
|
||||
memcpy(entries[index].member.calibration_hash, stored.scientific_hash, 32);
|
||||
}
|
||||
|
||||
Lardon3DSparseCalibrationScope scope;
|
||||
if (result == LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK) {
|
||||
Lardon3DSparseCalibrationMember *members = calloc(entry_count, sizeof(*members));
|
||||
if (!members)
|
||||
result = LARDON3D_CALIBRATION_BOOTSTRAP_V2_OUT_OF_MEMORY;
|
||||
else {
|
||||
for (size_t index = 0; index < entry_count; ++index) members[index] = entries[index].member;
|
||||
db_status = lardon3d_sparse_calibration_scope_create(database, members, entry_count, &scope);
|
||||
free(members);
|
||||
if (db_status != LARDON3D_PROJECT_DB_OK) result = db_result(db_status);
|
||||
}
|
||||
}
|
||||
if (result == LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK && attach_scope) {
|
||||
db_status = lardon3d_project_db_assign_selected_calibration_scope(database, execution_id,
|
||||
scope.scope_id);
|
||||
if (db_status != LARDON3D_PROJECT_DB_OK) result = db_result(db_status);
|
||||
}
|
||||
if (result == LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK) {
|
||||
memcpy(output->artifact_sha256, artifact_sha256, 32);
|
||||
output->scope = scope;
|
||||
output->calibration_count = entry_count;
|
||||
output->group_count = group_count;
|
||||
if (!attach_scope) {
|
||||
/* Item order, not artifact group order, is the durable composition key.
|
||||
* Workflow must bind it back to selected_execution_items.capture_id and
|
||||
* must never reverse-map Capture identity from image_id. */
|
||||
for (size_t index = 0; index < entry_count; ++index) {
|
||||
BootstrapV2Entry *entry = &entries[index];
|
||||
Lardon3DCalibrationBootstrapV2Member *member =
|
||||
&published_members[entry->selected_item_index];
|
||||
member->selected_item_index = entry->selected_item_index;
|
||||
member->image_id = entry->member.image_id;
|
||||
member->calibration_id = entry->member.calibration_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(entries);
|
||||
free(covered);
|
||||
return result;
|
||||
}
|
||||
|
||||
Lardon3DCalibrationBootstrapV2Result
|
||||
lardon3d_calibration_bootstrap_v2_publish_unattached(
|
||||
Lardon3DProjectDb *database, uint64_t execution_id,
|
||||
const unsigned char *artifact, size_t artifact_size,
|
||||
const unsigned char expected_artifact_sha256[32],
|
||||
Lardon3DCalibrationBootstrapV2Member *members, size_t member_capacity,
|
||||
Lardon3DCalibrationBootstrapV2Output *output) {
|
||||
return bootstrap_v2_publish(database, execution_id, artifact, artifact_size,
|
||||
expected_artifact_sha256, members,
|
||||
member_capacity, false, output);
|
||||
}
|
||||
|
||||
Lardon3DCalibrationBootstrapV2Result lardon3d_calibration_bootstrap_v2_import(
|
||||
Lardon3DProjectDb *database, uint64_t execution_id,
|
||||
const unsigned char *artifact, size_t artifact_size,
|
||||
const unsigned char expected_artifact_sha256[32],
|
||||
Lardon3DCalibrationBootstrapV2Output *output) {
|
||||
return bootstrap_v2_publish(database, execution_id, artifact, artifact_size,
|
||||
expected_artifact_sha256, NULL, 0, true, output);
|
||||
}
|
||||
|
|
@ -1,234 +0,0 @@
|
|||
#include <lardon3d/calibration_tooling.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
enum { kHeaderSize = 152, kEntrySize = 140, kMinimumViews = 40, kMinimumCorners = 1600 };
|
||||
|
||||
static bool nonzero(const unsigned char value[32]) {
|
||||
unsigned char any = 0;
|
||||
for (size_t i = 0; i < 32; ++i) any |= value[i];
|
||||
return any != 0;
|
||||
}
|
||||
static bool finite_value(double value) { return isfinite(value); }
|
||||
static void put_u32(unsigned char *p, uint32_t value) {
|
||||
for (size_t i = 0; i < 4; ++i) p[i] = (unsigned char)(value >> (8u * i));
|
||||
}
|
||||
static void put_u64(unsigned char *p, uint64_t value) {
|
||||
for (size_t i = 0; i < 8; ++i) p[i] = (unsigned char)(value >> (8u * i));
|
||||
}
|
||||
static void put_f64(unsigned char *p, double value) {
|
||||
uint64_t bits = 0; memcpy(&bits, &value, sizeof(bits)); put_u64(p, bits);
|
||||
}
|
||||
static bool digest(const unsigned char *p, size_t n, unsigned char out[32]) {
|
||||
unsigned int length = 0;
|
||||
return EVP_Digest(p, n, out, &length, EVP_sha256(), NULL) == 1 && length == 32;
|
||||
}
|
||||
static bool parameters_valid(const Lardon3DCalibrationToolingEntry *v, bool fit) {
|
||||
const double *p = fit ? &v->fit_fx : &v->fx;
|
||||
return p[0] > 0.0 && p[1] > 0.0 && p[2] >= 0.0 && p[3] >= 0.0 &&
|
||||
p[2] < (double)v->width && p[3] < (double)v->height &&
|
||||
finite_value(p[0]) && finite_value(p[1]) && finite_value(p[2]) && finite_value(p[3]) &&
|
||||
finite_value(p[4]) && finite_value(p[5]) && finite_value(p[6]) && finite_value(p[7]);
|
||||
}
|
||||
static int angle_class(double degrees) {
|
||||
if (degrees < 20.0 || degrees > 60.0) return -1;
|
||||
return degrees <= 35.0 ? 0 : (degrees <= 50.0 ? 1 : 2);
|
||||
}
|
||||
static unsigned int popcount4(uint32_t value) {
|
||||
unsigned int count = 0;
|
||||
for (unsigned int bit = 0; bit < 4; ++bit) count += (value >> bit) & 1u;
|
||||
return count;
|
||||
}
|
||||
static bool projected_delta_ok(const Lardon3DCalibrationToolingEntry *v) {
|
||||
static const double rays[5][2] = {{0, 0}, {-0.7, -0.7}, {0.7, -0.7},
|
||||
{-0.7, 0.7}, {0.7, 0.7}};
|
||||
const double *a = &v->fx, *b = &v->fit_fx;
|
||||
double maximum = 0.0;
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
double x = rays[i][0], y = rays[i][1], r2 = x*x + y*y;
|
||||
double ax = x*(1+a[4]*r2+a[5]*r2*r2)+2*a[6]*x*y+a[7]*(r2+2*x*x);
|
||||
double ay = y*(1+a[4]*r2+a[5]*r2*r2)+a[6]*(r2+2*y*y)+2*a[7]*x*y;
|
||||
double bx = x*(1+b[4]*r2+b[5]*r2*r2)+2*b[6]*x*y+b[7]*(r2+2*x*x);
|
||||
double by = y*(1+b[4]*r2+b[5]*r2*r2)+b[6]*(r2+2*y*y)+2*b[7]*x*y;
|
||||
double delta = hypot(a[0]*ax+a[2] - (b[0]*bx+b[2]),
|
||||
a[1]*ay+a[3] - (b[1]*by+b[3]));
|
||||
if (!finite_value(delta)) return false;
|
||||
if (delta > maximum) maximum = delta;
|
||||
}
|
||||
return finite_value(v->maximum_parameter_delta) &&
|
||||
fabs(v->maximum_parameter_delta - maximum) <= 1e-12 && maximum <= 0.10;
|
||||
}
|
||||
|
||||
Lardon3DCalibrationToolingResult lardon3d_calibration_tooling_validate(
|
||||
const Lardon3DCalibrationToolingEvidence *e) {
|
||||
if (!e || !e->views || !e->entries || !e->coordinate_checks || e->view_count > LARDON3D_CALIBRATION_TOOLING_MAX_VIEWS ||
|
||||
e->entry_count == 0 || e->entry_count > 4096 || e->coordinate_check_count > LARDON3D_CALIBRATION_TOOLING_MAX_COORDINATE_CHECKS)
|
||||
return LARDON3D_CALIBRATION_TOOLING_INVALID_ARGUMENT;
|
||||
if (!nonzero(e->target_sha256) || !nonzero(e->optical_state_sha256) ||
|
||||
!nonzero(e->solver_executable_sha256) || !nonzero(e->solver_configuration_sha256) ||
|
||||
!nonzero(e->initialization_evidence_sha256) || !nonzero(e->validation_evidence_sha256) ||
|
||||
e->target_family != LARDON3D_CALIBRATION_TOOLING_TARGET_CHARUCO_9X7_DICT_5X5_100 ||
|
||||
e->target_squares_x != 9 || e->target_squares_y != 7 ||
|
||||
!finite_value(e->target_square_length_mm) || e->target_square_length_mm != 30.0 ||
|
||||
!finite_value(e->target_marker_length_mm) || e->target_marker_length_mm != 21.0 ||
|
||||
!finite_value(e->target_active_width_mm) || e->target_active_width_mm != 270.0 ||
|
||||
!finite_value(e->target_active_height_mm) || e->target_active_height_mm != 210.0 ||
|
||||
!finite_value(e->target_white_border_mm) || e->target_white_border_mm < 30.0 ||
|
||||
e->extra_distortion_coefficient_count != 0 || !finite_value(e->measurement_resolution_mm) ||
|
||||
e->measurement_resolution_mm <= 0 || e->measurement_resolution_mm > 0.1 ||
|
||||
!isnan(e->target_flatness_mm))
|
||||
return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
double lo = 30.0, hi = 30.0;
|
||||
for (size_t i = 0; i < LARDON3D_CALIBRATION_TOOLING_TARGET_MEASUREMENTS; ++i) {
|
||||
if (!finite_value(e->target_measurements_mm[i]) || fabs(e->target_measurements_mm[i] - 30.0) > 0.30)
|
||||
return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
if (e->target_measurements_mm[i] < lo) lo = e->target_measurements_mm[i];
|
||||
if (e->target_measurements_mm[i] > hi) hi = e->target_measurements_mm[i];
|
||||
}
|
||||
if (hi - lo > 0.20 || !finite_value(e->holdout_rmse_px) || !finite_value(e->holdout_maximum_residual_px) ||
|
||||
e->holdout_rmse_px < 0 || e->holdout_maximum_residual_px < 0 ||
|
||||
e->holdout_rmse_px > 0.75 || e->holdout_maximum_residual_px > 1.50)
|
||||
return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
uint64_t corners = 0, residuals = 0, high = 0;
|
||||
size_t accepted_views = 0;
|
||||
uint32_t quadrant[5] = {0}, distance[3] = {0}, angles[3] = {0}, inclined = 0;
|
||||
size_t fit_views = 0, holdout_views = 0, fit_quadrant[4] = {0};
|
||||
double min_distance = INFINITY, max_distance = 0.0;
|
||||
for (size_t i = 0; i < e->view_count; ++i) {
|
||||
const Lardon3DCalibrationToolingView *v = &e->views[i];
|
||||
if (!nonzero(v->source_sha256) || v->accepted > 1 || v->holdout > 1 ||
|
||||
(!v->accepted && v->rejection_reason == 0) ||
|
||||
(v->accepted && v->rejection_reason != 0))
|
||||
return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
if (!v->accepted) {
|
||||
if (v->holdout != 0) return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
continue;
|
||||
}
|
||||
if (v->quadrant > 4 || v->distance_band > 2 ||
|
||||
(v->orientation_degrees != 0 && v->orientation_degrees != 90 &&
|
||||
v->orientation_degrees != 180 && v->orientation_degrees != 270) ||
|
||||
popcount4(v->target_corner_quadrant_mask) < 3 ||
|
||||
v->corner_count < 16 || !finite_value(v->target_occupancy) || v->target_occupancy < .20 || v->target_occupancy > .80 ||
|
||||
!finite_value(v->normal_angle_degrees) || !finite_value(v->distance_metres) || v->distance_metres <= 0 ||
|
||||
!finite_value(v->corner_rms_px) || v->corner_rms_px < 0 || v->corner_rms_px > .25 || !finite_value(v->clipped_fraction) || v->clipped_fraction < 0 || v->clipped_fraction > .01 ||
|
||||
!finite_value(v->reprojection_rmse_px) || v->reprojection_rmse_px < 0 || v->reprojection_rmse_px > .75 ||
|
||||
!finite_value(v->maximum_residual_px) || v->maximum_residual_px < 0 || v->maximum_residual_px > 1.50)
|
||||
return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
if (v->high_residual_count > v->residual_count || UINT64_MAX - corners < v->corner_count || UINT64_MAX - residuals < v->residual_count || UINT64_MAX - high < v->high_residual_count)
|
||||
return LARDON3D_CALIBRATION_TOOLING_CAPACITY;
|
||||
++accepted_views;
|
||||
corners += v->corner_count; residuals += v->residual_count; high += v->high_residual_count;
|
||||
++quadrant[v->quadrant]; ++distance[v->distance_band];
|
||||
int class_index = angle_class(v->normal_angle_degrees);
|
||||
if (class_index >= 0) { ++angles[class_index]; ++inclined; }
|
||||
if (v->holdout) ++holdout_views;
|
||||
else { ++fit_views; if (v->quadrant < 4) ++fit_quadrant[v->quadrant]; }
|
||||
if (v->distance_metres < min_distance) min_distance = v->distance_metres;
|
||||
if (v->distance_metres > max_distance) max_distance = v->distance_metres;
|
||||
}
|
||||
if (accepted_views < kMinimumViews || corners < kMinimumCorners || quadrant[0] < 6 || quadrant[1] < 6 || quadrant[2] < 6 || quadrant[3] < 6 || quadrant[4] < 8 ||
|
||||
distance[0] < 8 || distance[1] < 8 || distance[2] < 8 || max_distance / min_distance < 1.5 ||
|
||||
inclined < 24 || angles[0] < 6 || angles[1] < 6 || angles[2] < 6 || fit_views < 32 || holdout_views < 8 ||
|
||||
fit_quadrant[0] < 4 || fit_quadrant[1] < 4 || fit_quadrant[2] < 4 || fit_quadrant[3] < 4 ||
|
||||
residuals == 0 || high * 100 > residuals)
|
||||
return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
for (size_t i = 0; i < e->view_count; ++i) {
|
||||
const Lardon3DCalibrationToolingView *v = &e->views[i];
|
||||
if (!v->accepted) continue;
|
||||
size_t rank = 0;
|
||||
int class_index = angle_class(v->normal_angle_degrees);
|
||||
for (size_t other = 0; other < e->view_count; ++other) {
|
||||
const Lardon3DCalibrationToolingView *candidate = &e->views[other];
|
||||
if (!candidate->accepted || candidate->quadrant != v->quadrant ||
|
||||
candidate->distance_band != v->distance_band ||
|
||||
angle_class(candidate->normal_angle_degrees) != class_index) continue;
|
||||
int compare = memcmp(candidate->source_sha256, v->source_sha256, 32);
|
||||
if (compare == 0 && other != i) return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
if (compare < 0) ++rank;
|
||||
}
|
||||
if (v->holdout != (rank % 5 == 4)) return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
}
|
||||
for (size_t i = 0; i < e->coordinate_check_count; ++i) {
|
||||
const Lardon3DCalibrationToolingCoordinateCheck *c = &e->coordinate_checks[i];
|
||||
if (!nonzero(c->source_sha256) || (c->orientation_degrees != 0 && c->orientation_degrees != 90 &&
|
||||
c->orientation_degrees != 180 && c->orientation_degrees != 270) ||
|
||||
!finite_value(c->dx_px) || !finite_value(c->dy_px) || fabs(c->dx_px) > .01 || fabs(c->dy_px) > .01)
|
||||
return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
}
|
||||
for (size_t view_index = 0; view_index < e->view_count; ++view_index) {
|
||||
const Lardon3DCalibrationToolingView *v = &e->views[view_index];
|
||||
if (!v->accepted) continue;
|
||||
size_t matches = 0;
|
||||
for (size_t check_index = 0; check_index < e->coordinate_check_count; ++check_index) {
|
||||
const Lardon3DCalibrationToolingCoordinateCheck *c = &e->coordinate_checks[check_index];
|
||||
if (memcmp(c->source_sha256, v->source_sha256, 32) == 0) {
|
||||
if (c->orientation_degrees != v->orientation_degrees)
|
||||
return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
++matches;
|
||||
}
|
||||
}
|
||||
if (matches < 20) return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
}
|
||||
for (size_t i = 0; i < e->entry_count; ++i) {
|
||||
const Lardon3DCalibrationToolingEntry *v = &e->entries[i];
|
||||
if (!v->image_id || !nonzero(v->representation_sha256) ||
|
||||
memcmp(v->optical_state_sha256, e->optical_state_sha256, 32) != 0 ||
|
||||
!v->width || !v->height ||
|
||||
!parameters_valid(v, false) || !parameters_valid(v, true) || !v->support_images || !v->support_observations ||
|
||||
!finite_value(v->reprojection_rmse_px) || v->reprojection_rmse_px < 0 || v->reprojection_rmse_px > .50 ||
|
||||
v->validation_flags != LARDON3D_CALIBRATION_TOOLING_VALIDATION_FLAGS || !projected_delta_ok(v))
|
||||
return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
for (size_t previous = 0; previous < i; ++previous)
|
||||
if (v->image_id == e->entries[previous].image_id)
|
||||
return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
const double published[8] = {v->fx,v->fy,v->cx,v->cy,v->k1,v->k2,v->p1,v->p2};
|
||||
for (size_t repeat = 0; repeat < 3; ++repeat)
|
||||
for (size_t parameter = 0; parameter < 8; ++parameter)
|
||||
if (!finite_value(v->repeated_parameters[repeat][parameter]) ||
|
||||
v->repeated_parameters[repeat][parameter] != published[parameter])
|
||||
return LARDON3D_CALIBRATION_TOOLING_SCIENCE_REJECTED;
|
||||
}
|
||||
return LARDON3D_CALIBRATION_TOOLING_OK;
|
||||
}
|
||||
|
||||
Lardon3DCalibrationToolingResult lardon3d_calibration_tooling_produce(
|
||||
const Lardon3DCalibrationToolingEvidence *e, unsigned char *artifact,
|
||||
size_t capacity, size_t *written, unsigned char hash[32]) {
|
||||
if (written) *written = 0;
|
||||
if (!artifact || !hash) return LARDON3D_CALIBRATION_TOOLING_INVALID_ARGUMENT;
|
||||
Lardon3DCalibrationToolingResult valid = lardon3d_calibration_tooling_validate(e);
|
||||
if (valid != LARDON3D_CALIBRATION_TOOLING_OK) return valid;
|
||||
if (e->entry_count > (SIZE_MAX - kHeaderSize) / kEntrySize) return LARDON3D_CALIBRATION_TOOLING_CAPACITY;
|
||||
size_t size = kHeaderSize + e->entry_count * kEntrySize;
|
||||
if (size > LARDON3D_CALIBRATION_BOOTSTRAP_MAX_BYTES || capacity < size) return LARDON3D_CALIBRATION_TOOLING_CAPACITY;
|
||||
size_t at = 0; memcpy(artifact + at, "L3DCALB1", 8); at += 8;
|
||||
put_u32(artifact + at, 1); at += 4; put_u32(artifact + at, 1); at += 4; put_u32(artifact + at, 1); at += 4; put_u32(artifact + at, (uint32_t)e->entry_count); at += 4;
|
||||
memcpy(artifact + at, e->solver_executable_sha256, 32); at += 32; memcpy(artifact + at, e->solver_configuration_sha256, 32); at += 32;
|
||||
memcpy(artifact + at, e->initialization_evidence_sha256, 32); at += 32; memcpy(artifact + at, e->validation_evidence_sha256, 32); at += 32;
|
||||
for (size_t i = 0; i < e->entry_count; ++i) {
|
||||
const Lardon3DCalibrationToolingEntry *v = &e->entries[i];
|
||||
put_u64(artifact + at, v->image_id); at += 8; memcpy(artifact + at, v->representation_sha256, 32); at += 32;
|
||||
put_u32(artifact + at, v->width); at += 4; put_u32(artifact + at, v->height); at += 4;
|
||||
const double values[8] = {v->fx,v->fy,v->cx,v->cy,v->k1,v->k2,v->p1,v->p2};
|
||||
for (size_t j = 0; j < 8; ++j) { put_f64(artifact + at, values[j]); at += 8; }
|
||||
put_u32(artifact + at, v->support_images); at += 4; put_u32(artifact + at, v->support_observations); at += 4;
|
||||
put_f64(artifact + at, v->reprojection_rmse_px); at += 8; put_f64(artifact + at, v->maximum_parameter_delta); at += 8;
|
||||
put_u32(artifact + at, v->validation_flags); at += 4;
|
||||
}
|
||||
if (at != size || !digest(artifact, size, hash)) return LARDON3D_CALIBRATION_TOOLING_ENCODING_ERROR;
|
||||
if (written) *written = size;
|
||||
return LARDON3D_CALIBRATION_TOOLING_OK;
|
||||
}
|
||||
|
||||
Lardon3DCalibrationToolingResult lardon3d_calibration_tooling_import(
|
||||
Lardon3DProjectDb *db, uint64_t execution_id, const Lardon3DCalibrationToolingEvidence *e,
|
||||
unsigned char *artifact, size_t capacity, size_t *written, Lardon3DCalibrationBootstrapOutput *out) {
|
||||
unsigned char hash[32];
|
||||
Lardon3DCalibrationToolingResult result = lardon3d_calibration_tooling_produce(e, artifact, capacity, written, hash);
|
||||
if (result != LARDON3D_CALIBRATION_TOOLING_OK || !db || !execution_id || !out) return result == LARDON3D_CALIBRATION_TOOLING_OK ? LARDON3D_CALIBRATION_TOOLING_INVALID_ARGUMENT : result;
|
||||
return lardon3d_calibration_bootstrap_import(db, execution_id, artifact, *written, hash, out) == LARDON3D_CALIBRATION_BOOTSTRAP_OK ?
|
||||
LARDON3D_CALIBRATION_TOOLING_OK : LARDON3D_CALIBRATION_TOOLING_IMPORT_ERROR;
|
||||
}
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
#include <lardon3d/calibration_tooling_v2.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
enum {
|
||||
TOOLING_V2_HEADER_SIZE = 28,
|
||||
TOOLING_V2_GROUP_SIZE = 232,
|
||||
TOOLING_V2_ENTRY_SIZE = 144,
|
||||
};
|
||||
|
||||
static bool nonzero_digest(const unsigned char value[32]) {
|
||||
unsigned char any = 0;
|
||||
for (size_t index = 0; index < 32; ++index) any |= value[index];
|
||||
return any != 0;
|
||||
}
|
||||
|
||||
static void put_u32(unsigned char *output, uint32_t value) {
|
||||
for (size_t index = 0; index < 4; ++index)
|
||||
output[index] = (unsigned char)(value >> (8u * index));
|
||||
}
|
||||
|
||||
static void put_u64(unsigned char *output, uint64_t value) {
|
||||
for (size_t index = 0; index < 8; ++index)
|
||||
output[index] = (unsigned char)(value >> (8u * index));
|
||||
}
|
||||
|
||||
static void put_f64(unsigned char *output, double value) {
|
||||
uint64_t bits = 0;
|
||||
/* The format has one representation of zero so equal scientific values do
|
||||
* not acquire different artifact identities through a negative sign bit. */
|
||||
if (value == 0.0) value = 0.0;
|
||||
memcpy(&bits, &value, sizeof(bits));
|
||||
put_u64(output, bits);
|
||||
}
|
||||
|
||||
static bool sha256(const unsigned char *bytes, size_t size, unsigned char output[32]) {
|
||||
unsigned int output_size = 0;
|
||||
return EVP_Digest(bytes, size, output, &output_size, EVP_sha256(), NULL) == 1 &&
|
||||
output_size == 32;
|
||||
}
|
||||
|
||||
static bool entry_valid(const Lardon3DCalibrationToolingV2Entry *entry) {
|
||||
const double parameters[] = {entry->fx, entry->fy, entry->cx, entry->cy,
|
||||
entry->k1, entry->k2, entry->p1, entry->p2};
|
||||
if (entry->image_id == 0 || !nonzero_digest(entry->representation_sha256) ||
|
||||
entry->width == 0 || entry->height == 0 || entry->support_images == 0 ||
|
||||
entry->support_observations == 0 ||
|
||||
entry->validation_flags != LARDON3D_CALIBRATION_TOOLING_V2_VALIDATION_FLAGS)
|
||||
return false;
|
||||
for (size_t index = 0; index < sizeof(parameters) / sizeof(parameters[0]); ++index)
|
||||
if (!isfinite(parameters[index])) return false;
|
||||
return entry->fx > 0.0 && entry->fy > 0.0 && entry->cx >= 0.0 && entry->cy >= 0.0 &&
|
||||
entry->cx < (double)entry->width && entry->cy < (double)entry->height &&
|
||||
isfinite(entry->reprojection_rmse_px) && entry->reprojection_rmse_px >= 0.0 &&
|
||||
isfinite(entry->maximum_parameter_delta) && entry->maximum_parameter_delta >= 0.0;
|
||||
}
|
||||
|
||||
Lardon3DCalibrationToolingV2Result lardon3d_calibration_tooling_v2_validate(
|
||||
const Lardon3DCalibrationToolingV2Evidence *evidence) {
|
||||
if (!evidence || !evidence->groups || evidence->group_count == 0 ||
|
||||
evidence->group_count > LARDON3D_CALIBRATION_BOOTSTRAP_V2_MAX_GROUPS ||
|
||||
evidence->entry_count == 0 ||
|
||||
evidence->entry_count > LARDON3D_CALIBRATION_BOOTSTRAP_V2_MAX_ENTRIES)
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_INVALID_ARGUMENT;
|
||||
|
||||
bool covered[LARDON3D_CALIBRATION_BOOTSTRAP_V2_MAX_ENTRIES] = {false};
|
||||
size_t total_entries = 0;
|
||||
for (size_t group_index = 0; group_index < evidence->group_count; ++group_index) {
|
||||
const Lardon3DCalibrationToolingV2Group *group = &evidence->groups[group_index];
|
||||
if (!group->entries || group->entry_count == 0 || group->group_version == 0 ||
|
||||
!nonzero_digest(group->group_identity_sha256) ||
|
||||
!nonzero_digest(group->optical_state_sha256) || !nonzero_digest(group->target_sha256) ||
|
||||
!nonzero_digest(group->solver_executable_sha256) ||
|
||||
!nonzero_digest(group->solver_configuration_sha256) ||
|
||||
!nonzero_digest(group->initialization_evidence_sha256) ||
|
||||
!nonzero_digest(group->validation_evidence_sha256))
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_EVIDENCE_REJECTED;
|
||||
if (group_index > 0) {
|
||||
const Lardon3DCalibrationToolingV2Group *previous = &evidence->groups[group_index - 1];
|
||||
int order = memcmp(previous->group_identity_sha256, group->group_identity_sha256, 32);
|
||||
if (order > 0 || (order == 0 && previous->group_version >= group->group_version))
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_EVIDENCE_REJECTED;
|
||||
}
|
||||
if (group->entry_count > evidence->entry_count - total_entries)
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_EVIDENCE_REJECTED;
|
||||
total_entries += group->entry_count;
|
||||
for (size_t entry_index = 0; entry_index < group->entry_count; ++entry_index) {
|
||||
const Lardon3DCalibrationToolingV2Entry *entry = &group->entries[entry_index];
|
||||
if (!entry_valid(entry) || entry->selected_item_index >= evidence->entry_count ||
|
||||
covered[entry->selected_item_index] ||
|
||||
(entry_index > 0 &&
|
||||
group->entries[entry_index - 1].selected_item_index >= entry->selected_item_index))
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_EVIDENCE_REJECTED;
|
||||
covered[entry->selected_item_index] = true;
|
||||
/* Scientific images cannot belong to two groups under different item
|
||||
* indexes; item coverage alone would not detect that identity conflict. */
|
||||
for (size_t prior_group = 0; prior_group <= group_index; ++prior_group) {
|
||||
const Lardon3DCalibrationToolingV2Group *candidate_group =
|
||||
&evidence->groups[prior_group];
|
||||
size_t limit = prior_group == group_index ? entry_index : candidate_group->entry_count;
|
||||
for (size_t prior_entry = 0; prior_entry < limit; ++prior_entry)
|
||||
if (candidate_group->entries[prior_entry].image_id == entry->image_id)
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_EVIDENCE_REJECTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (total_entries != evidence->entry_count)
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_EVIDENCE_REJECTED;
|
||||
for (size_t index = 0; index < evidence->entry_count; ++index)
|
||||
if (!covered[index]) return LARDON3D_CALIBRATION_TOOLING_V2_EVIDENCE_REJECTED;
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_OK;
|
||||
}
|
||||
|
||||
Lardon3DCalibrationToolingV2Result lardon3d_calibration_tooling_v2_produce(
|
||||
const Lardon3DCalibrationToolingV2Evidence *evidence, unsigned char *artifact,
|
||||
size_t artifact_capacity, size_t *written, unsigned char artifact_sha256[32]) {
|
||||
if (written) *written = 0;
|
||||
if (!artifact || !artifact_sha256) return LARDON3D_CALIBRATION_TOOLING_V2_INVALID_ARGUMENT;
|
||||
Lardon3DCalibrationToolingV2Result result =
|
||||
lardon3d_calibration_tooling_v2_validate(evidence);
|
||||
if (result != LARDON3D_CALIBRATION_TOOLING_V2_OK) return result;
|
||||
if (evidence->group_count > (SIZE_MAX - TOOLING_V2_HEADER_SIZE) / TOOLING_V2_GROUP_SIZE)
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_CAPACITY;
|
||||
size_t size = TOOLING_V2_HEADER_SIZE + evidence->group_count * TOOLING_V2_GROUP_SIZE;
|
||||
if (evidence->entry_count > (SIZE_MAX - size) / TOOLING_V2_ENTRY_SIZE)
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_CAPACITY;
|
||||
size += evidence->entry_count * TOOLING_V2_ENTRY_SIZE;
|
||||
if (size > LARDON3D_CALIBRATION_BOOTSTRAP_V2_MAX_BYTES || artifact_capacity < size)
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_CAPACITY;
|
||||
|
||||
size_t at = 0;
|
||||
memcpy(artifact + at, "L3DCALB2", 8);
|
||||
at += 8;
|
||||
#define PUT32(value) do { put_u32(artifact + at, (uint32_t)(value)); at += 4; } while (0)
|
||||
PUT32(LARDON3D_CALIBRATION_TOOLING_V2_VERSION);
|
||||
PUT32(LARDON3D_SPARSE_SFM_CALIBRATION_KIND_PINHOLE);
|
||||
PUT32(LARDON3D_SPARSE_SFM_CALIBRATION_VERSION);
|
||||
PUT32(evidence->group_count);
|
||||
PUT32(evidence->entry_count);
|
||||
for (size_t group_index = 0; group_index < evidence->group_count; ++group_index) {
|
||||
const Lardon3DCalibrationToolingV2Group *group = &evidence->groups[group_index];
|
||||
const unsigned char *digests[] = {
|
||||
group->group_identity_sha256, group->optical_state_sha256, group->target_sha256,
|
||||
group->solver_executable_sha256, group->solver_configuration_sha256,
|
||||
group->initialization_evidence_sha256, group->validation_evidence_sha256};
|
||||
memcpy(artifact + at, digests[0], 32); at += 32;
|
||||
PUT32(group->group_version);
|
||||
for (size_t digest_index = 1; digest_index < 7; ++digest_index) {
|
||||
memcpy(artifact + at, digests[digest_index], 32);
|
||||
at += 32;
|
||||
}
|
||||
PUT32(group->entry_count);
|
||||
for (size_t entry_index = 0; entry_index < group->entry_count; ++entry_index) {
|
||||
const Lardon3DCalibrationToolingV2Entry *entry = &group->entries[entry_index];
|
||||
PUT32(entry->selected_item_index);
|
||||
put_u64(artifact + at, entry->image_id); at += 8;
|
||||
memcpy(artifact + at, entry->representation_sha256, 32); at += 32;
|
||||
PUT32(entry->width); PUT32(entry->height);
|
||||
const double parameters[] = {entry->fx, entry->fy, entry->cx, entry->cy,
|
||||
entry->k1, entry->k2, entry->p1, entry->p2};
|
||||
for (size_t index = 0; index < 8; ++index) {
|
||||
put_f64(artifact + at, parameters[index]); at += 8;
|
||||
}
|
||||
PUT32(entry->support_images); PUT32(entry->support_observations);
|
||||
put_f64(artifact + at, entry->reprojection_rmse_px); at += 8;
|
||||
put_f64(artifact + at, entry->maximum_parameter_delta); at += 8;
|
||||
PUT32(entry->validation_flags);
|
||||
}
|
||||
}
|
||||
#undef PUT32
|
||||
if (at != size || !sha256(artifact, size, artifact_sha256))
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_ENCODING_ERROR;
|
||||
if (written) *written = size;
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_OK;
|
||||
}
|
||||
|
||||
Lardon3DCalibrationToolingV2Result lardon3d_calibration_tooling_v2_import(
|
||||
Lardon3DProjectDb *database, uint64_t execution_id,
|
||||
const Lardon3DCalibrationToolingV2Evidence *evidence, unsigned char *artifact,
|
||||
size_t artifact_capacity, size_t *written,
|
||||
Lardon3DCalibrationBootstrapV2Output *output) {
|
||||
unsigned char artifact_sha256[32];
|
||||
size_t local_written = 0;
|
||||
size_t *encoded_size = written ? written : &local_written;
|
||||
Lardon3DCalibrationToolingV2Result result = lardon3d_calibration_tooling_v2_produce(
|
||||
evidence, artifact, artifact_capacity, encoded_size, artifact_sha256);
|
||||
if (result != LARDON3D_CALIBRATION_TOOLING_V2_OK) return result;
|
||||
if (!database || execution_id == 0 || !output)
|
||||
return LARDON3D_CALIBRATION_TOOLING_V2_INVALID_ARGUMENT;
|
||||
return lardon3d_calibration_bootstrap_v2_import(
|
||||
database, execution_id, artifact, *encoded_size, artifact_sha256, output) ==
|
||||
LARDON3D_CALIBRATION_BOOTSTRAP_V2_OK
|
||||
? LARDON3D_CALIBRATION_TOOLING_V2_OK
|
||||
: LARDON3D_CALIBRATION_TOOLING_V2_IMPORT_ERROR;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue