feat(reconstruction): freeze sparse SfM persistence model v1
This commit is contained in:
parent
d91b25fafd
commit
95700de339
18 changed files with 5479 additions and 974 deletions
|
|
@ -119,7 +119,8 @@ l'extraction après import et planification multi-image/DAG.
|
|||
les Feature Sets persistés.
|
||||
|
||||
**IMPLEMENTED** — vérification géométrique et tracks. **Sparse SfM Gate A
|
||||
PASS**, production Sparse SfM remains **NOT_IMPLEMENTED** until its later
|
||||
PASS**, the Sparse SfM v16 persistence model is **FROZEN** after Gate B;
|
||||
the numerical solver and reconstruction pipeline remain **PLANNED** until later
|
||||
implementation gates.
|
||||
|
||||
## Extension v2 multi-descriptor
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
# Base de données projet Lardon3D
|
||||
|
||||
> Version courante : **v15**. La migration transactionnelle v14→v15 ajoute
|
||||
> Version courante : **v16**. La migration transactionnelle v15→v16 ajoute
|
||||
> le modèle persistant Sparse SfM (calibrations, scopes et reconstructions).
|
||||
> La migration transactionnelle v14→v15 ajoute
|
||||
> `track_builder_tasks` pour le payload durable explicite du Task Builder. La
|
||||
> migration transactionnelle v13→v14 ajoute
|
||||
> les tables `track_sets`, `tracks` et `track_observations` pour le Track
|
||||
|
|
@ -20,6 +22,21 @@
|
|||
La base de données projet stocke les métadonnées de reconstruction et les relations entre les
|
||||
entités. Elle est conçue pour être légère, persistante et permettre la reprise après interruption.
|
||||
|
||||
## Sparse SfM Gate B (v16)
|
||||
|
||||
Le modèle Sparse SfM v1 est publié atomiquement dans les tables
|
||||
`sparse_calibrations`, `sparse_calibration_scopes`,
|
||||
`sparse_calibration_scope_images`, `sparse_reconstructions`,
|
||||
`sparse_reconstruction_components`, `sparse_registered_images`,
|
||||
`sparse_landmarks` et `sparse_landmark_observations`. Les résultats sont
|
||||
immuables et les collections volumineuses sont lues par curseurs bornés ; la
|
||||
base ne charge jamais une reconstruction complète par défaut. Les coordonnées
|
||||
restent dans le repère arbitraire de chaque composant et les références
|
||||
d'observation ne dupliquent ni descripteurs ni coordonnées de pixels.
|
||||
Les relations de suppression disposent d'index enfants dédiés, notamment sur
|
||||
`calibration_id` dans les membres de scope et `calibration_scope_id` dans les
|
||||
reconstructions, afin que les vérifications FK restent indexées.
|
||||
|
||||
## Structure conceptuelle
|
||||
|
||||
### Entités principales
|
||||
|
|
@ -758,6 +775,12 @@ sont documentés dans `tracks.md`.
|
|||
**IMPLEMENTED** — Track Builder v1 durable : table `track_builder_tasks`, scope
|
||||
asset atomique, payload v1 validé, migration v14→v15 et rollback/retry testés.
|
||||
|
||||
**IMPLEMENTED** — Sparse SfM v1 persistant : modèle v16 immutable, publication
|
||||
atomique, composants déterministes, lecteurs bornés, corruption/lifecycle
|
||||
validation, migration v15→v16 et comparateur fresh/migrated validés par Gate B.
|
||||
Le modèle de persistance est gelé pour v1 ; le solveur numérique reste hors de
|
||||
Project DB v16.
|
||||
|
||||
**IMPLEMENTED** — API C Track Model v1 : header `project_db.h` et
|
||||
source `project_db.c` exposent `create_track_set`, `load_track_set`,
|
||||
`find_track_set`, `list_track_sets`, `load_track`, `list_tracks`,
|
||||
|
|
|
|||
|
|
@ -124,13 +124,15 @@ USAC/MAGSAC avec configuration, seed et fingerprint déterministes.
|
|||
|
||||
**Statut :** COMPLETED/FROZEN — le Track Builder v1 direct et durable est
|
||||
implémenté dans Project DB v15 (`track_sets`, `tracks`, `track_observations` et
|
||||
le payload de tâche). La triangulation et le Sparse SfM restent PLANNED.
|
||||
le payload de tâche). La triangulation et le solveur Sparse SfM restent PLANNED;
|
||||
le modèle de persistance Sparse SfM v16 est gelé après Gate B.
|
||||
|
||||
**Sparse SfM Gate A : PASS.** Le contrat géométrique, la stratégie
|
||||
incremental, la triangulation candidate, le gauge, les conventions de pose,
|
||||
les limites BA et l'enveloppe matérielle sont documentés dans
|
||||
`architecture/sparse_sfm.md`. La production Sparse SfM reste
|
||||
**NOT_IMPLEMENTED** jusqu'aux Gates B–G.
|
||||
`architecture/sparse_sfm.md`. Le solveur Sparse SfM reste
|
||||
**NOT_IMPLEMENTED** jusqu'aux Gates B3–G; sa
|
||||
persistance v16 et ses lecteurs bornés sont implémentés en B2.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -106,3 +106,8 @@ une distribution de latence estimator-only.
|
|||
|
||||
Le profil maximal explicite et les pools multi-workers restent hors périmètre.
|
||||
SIFT/RootSIFT et Feature Extraction Vulkan restent hors de ce contrat.
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -3,12 +3,17 @@
|
|||
## Status and boundary
|
||||
|
||||
**GATE A — DECISION after contract and probe study.** This document defines the
|
||||
scientific and architectural contract for the future Sparse SfM layer. It does
|
||||
not implement a solver, change Track Model v1, change Track Builder v1, add a
|
||||
Project DB v16, or add a Task Kind. `FACT`, `CANDIDATE`, `DECISION` and `FROZEN`
|
||||
remain explicit: the upstream Track Model and Track Builder are FROZEN; the
|
||||
choices below are Gate A decisions for the next implementation gates, not a
|
||||
claim that Sparse SfM is implemented.
|
||||
scientific and architectural contract for the Sparse SfM layer. B2 implements
|
||||
the immutable v16 persistence model and its bounded readers; it does not
|
||||
implement a numerical solver, change Track Model v1, change Track Builder v1,
|
||||
or add a Task Kind. `FACT`, `CANDIDATE`, `DECISION` and `FROZEN` remain explicit:
|
||||
the upstream Track Model and Track Builder are FROZEN, while numerical Sparse
|
||||
SfM remains deferred to later gates.
|
||||
|
||||
**GATE B — PASS.** The v16 persistence model, migration, corruption/lifecycle
|
||||
proofs, structural comparator, representative resource validation, normal
|
||||
suite and ASan/UBSan closure are complete. Numerical Sparse SfM remains
|
||||
deferred to Gate C and later gates.
|
||||
|
||||
## Scope
|
||||
|
||||
|
|
@ -383,3 +388,140 @@ the synthetic ground-truth and sparse-solver gates.
|
|||
No production Sparse SfM, triangulator, camera solver, BA, Project DB v16,
|
||||
metric alignment, control-point scale, dense/MVS, mesh, texturing, Vulkan SfM,
|
||||
GPU BA, network/distributed scheduling or UI workflow is implemented here.
|
||||
|
||||
## Gate B — model and persistence contract
|
||||
|
||||
This section is the **DECISION** contract for the v16 SQL/API work.
|
||||
It preserves every Gate A decision and supplies only durable vocabulary; no
|
||||
numeric geometry is introduced.
|
||||
|
||||
### Calibration definition and identity
|
||||
|
||||
A calibration is an immutable known pinhole model with `width`, `height`,
|
||||
`fx`, `fy`, `cx`, `cy`, zero skew, `k1`, `k2`, `p1`, and `p2`, all binary64 and
|
||||
finite. `width` and `height` belong to scientific identity. A calibration's
|
||||
provenance is an explicit enum (`USER_EXPLICIT` or `IMPORTED_TRUSTED` in v1)
|
||||
plus a 32-byte provenance fingerprint supplied by the caller. EXIF is never a
|
||||
calibration origin. Two equal numeric models with different provenance
|
||||
fingerprints are distinct scientific calibrations because their trust scope is
|
||||
different; equal content and equal provenance are reused.
|
||||
|
||||
The scientific calibration hash is SHA-256 over explicit little-endian fields:
|
||||
|
||||
```text
|
||||
ASCII "L3D3DCP1" (8 bytes)
|
||||
format_version=1 (uint32)
|
||||
model_kind (uint32), model_version (uint32)
|
||||
width (uint32), height (uint32)
|
||||
fx, fy, cx, cy, k1, k2, p1, p2 (8 canonical binary64 values)
|
||||
provenance_kind (uint32), provenance_fingerprint (32 bytes)
|
||||
```
|
||||
|
||||
NaN and infinities are rejected. Negative zero is canonicalized to positive
|
||||
zero before hashing and storage. No native struct, padding or locale text is
|
||||
serialized. SQLite row IDs remain DB-local references; the hash is the
|
||||
scientific calibration identity.
|
||||
|
||||
### Calibration scope
|
||||
|
||||
A scope is immutable and assigns exactly one calibration to each relevant
|
||||
image. Groups are allowed only when dimensions, crop/orientation coordinate
|
||||
frame, model/version, numeric parameters and provenance identity are equal;
|
||||
device or EXIF model names are insufficient. Scope identity is project-local
|
||||
and content-addressed by SHA-256 over:
|
||||
|
||||
```text
|
||||
ASCII "L3D3DSC1" (8 bytes)
|
||||
format_version=1 (uint32)
|
||||
member_count (uint64)
|
||||
for members sorted by image_id:
|
||||
image_id (uint64), calibration_hash (32 bytes)
|
||||
```
|
||||
|
||||
The member count is consistency metadata and is also encoded in the digest.
|
||||
There is no latest-calibration lookup and no silent K rescaling. Feature File
|
||||
dimensions must match the calibration dimensions exactly.
|
||||
|
||||
### Reconstruction identity and components
|
||||
|
||||
The immutable reconstruction identity is:
|
||||
|
||||
```text
|
||||
(track_set_id,
|
||||
calibration_scope_id,
|
||||
sfm_kind=INCREMENTAL,
|
||||
sfm_version=1,
|
||||
parameter_fingerprint[32])
|
||||
```
|
||||
|
||||
`track_set_id` and `calibration_scope_id` are project-local immutable database
|
||||
references, consistent with the existing Track Model identity convention. The
|
||||
parameter fingerprint is required to be a 32-byte SHA-256 value but its final
|
||||
byte encoding remains a later geometry-gate decision. Runtime IDs, timestamps,
|
||||
worker count, resource state and metrics are excluded.
|
||||
|
||||
Component identity is the minimum registered `image_id` in that component. It
|
||||
is deterministic, project-local, unique because an image belongs to at most one
|
||||
component, independent of DFS/hash order, and compact. A component always has
|
||||
at least one registered image. Its coordinates use an independent unit-baseline
|
||||
gauge and are never comparable to another component without later alignment.
|
||||
|
||||
### Persisted model
|
||||
|
||||
The minimum v16 model is:
|
||||
|
||||
- `sparse_calibrations`: immutable calibration content and hash;
|
||||
- `sparse_calibration_scopes`: immutable scope hash/member count;
|
||||
- `sparse_calibration_scope_images`: one image-to-calibration assignment;
|
||||
- `sparse_reconstructions`: immutable identity and pixel reprojection metrics;
|
||||
- `sparse_reconstruction_components`: component key and counts;
|
||||
- `sparse_registered_images`: one world-to-camera pose per image;
|
||||
- `sparse_landmarks`: one component-local binary64 point per Track;
|
||||
- `sparse_landmark_observations`: minimal references to the upstream Feature Set
|
||||
and feature index, without duplicated descriptors or x/y coordinates.
|
||||
|
||||
Track ID is globally unique in the existing Track Model table, so one landmark
|
||||
per reconstruction is uniquely keyed by `(reconstruction_id, track_id)`;
|
||||
component key remains an attribute/consistency relation rather than redundant
|
||||
landmark identity. Landmark publication validates that the Track belongs to the
|
||||
reconstruction's exact Track Set and that every observation belongs to that
|
||||
Track and its component. Track splitting is impossible in v1.
|
||||
|
||||
Observation references are persisted because future BA needs bounded indexed
|
||||
access from landmark to registered observations without repeatedly reopening
|
||||
Feature Files. Only `feature_set_id`, `feature_index` and canonical track
|
||||
position are stored; image ID and x/y remain derivable from immutable upstream
|
||||
models. This is a deliberate normalization/resource trade-off, not a second
|
||||
copy of Feature data.
|
||||
|
||||
Persisted reprojection metrics use explicit pixel units and names:
|
||||
`reprojection_rmse_px` and `reprojection_median_px`. They are diagnostics, not
|
||||
identity. No metric scale or `_mm` field exists.
|
||||
|
||||
### Constraints and publication
|
||||
|
||||
Publication requires at least two registered images, one component and one
|
||||
landmark. A result with no usable geometry is rejected rather than represented
|
||||
by a meaningless empty scientific row. Two-camera reconstruction is valid.
|
||||
There are no READY/FAILED/PARTIAL scientific states: row existence means a
|
||||
complete immutable publication. A failed transaction leaves no visible row.
|
||||
|
||||
SQL enforces one pose per image, one component per registered image, one
|
||||
landmark per Track, exact reconstruction uniqueness, scope member uniqueness,
|
||||
and child foreign keys. The API additionally validates Track Set ownership,
|
||||
component consistency, calibration coverage, finite values and rotation
|
||||
orthonormality/determinant. Rotation matrices are never repaired.
|
||||
|
||||
The publication transaction inserts the reconstruction and all children using
|
||||
prepared statements in bounded loops. It contains no Feature File I/O, Track
|
||||
paging, solver work or Governor wait. Child pages use cursor order and bounded
|
||||
capacity (64 cameras, 64 landmarks, 64 observations); total scientific counts
|
||||
have no arbitrary cap beyond checked 64-bit/SQLite limits.
|
||||
|
||||
### v16 migration intent
|
||||
|
||||
Project DB v15 remains immutable. Gate B adds one transactional v15→v16
|
||||
migration containing only the eight Sparse SfM model tables and their required
|
||||
indexes. A true historical v15 fixture, injected rollback, retry, fresh-schema
|
||||
equivalence and close/reopen are mandatory. No Task, Governor, triangulation,
|
||||
PnP, BA, GPU or Project DB v17 is introduced.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#include <lardon3d/task.h>
|
||||
|
||||
enum {
|
||||
LARDON3D_PROJECT_DB_SCHEMA_VERSION = 15,
|
||||
LARDON3D_PROJECT_DB_SCHEMA_VERSION = 16,
|
||||
LARDON3D_PROJECT_DB_ID_CAPACITY = 65,
|
||||
LARDON3D_PROJECT_DB_KIND_CAPACITY = 65,
|
||||
LARDON3D_PROJECT_DB_PATH_CAPACITY = 4096,
|
||||
|
|
|
|||
238
include/lardon3d/sparse_sfm_model.h
Normal file
238
include/lardon3d/sparse_sfm_model.h
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
#ifndef LARDON3D_SPARSE_SFM_MODEL_H
|
||||
#define LARDON3D_SPARSE_SFM_MODEL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <lardon3d/project_db.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
LARDON3D_SPARSE_SFM_SHA256_SIZE = 32,
|
||||
LARDON3D_SPARSE_SFM_PAGE_MAX = 64,
|
||||
LARDON3D_SPARSE_SFM_CALIBRATION_KIND_PINHOLE = 1,
|
||||
LARDON3D_SPARSE_SFM_CALIBRATION_VERSION = 1,
|
||||
LARDON3D_SPARSE_SFM_PROVENANCE_USER_EXPLICIT = 1,
|
||||
LARDON3D_SPARSE_SFM_PROVENANCE_IMPORTED_TRUSTED = 2,
|
||||
LARDON3D_SPARSE_SFM_KIND_INCREMENTAL = 1,
|
||||
LARDON3D_SPARSE_SFM_VERSION = 1,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint64_t calibration_id;
|
||||
unsigned char scientific_hash[32];
|
||||
uint32_t model_kind;
|
||||
uint32_t model_version;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
double fx;
|
||||
double fy;
|
||||
double cx;
|
||||
double cy;
|
||||
double k1;
|
||||
double k2;
|
||||
double p1;
|
||||
double p2;
|
||||
uint32_t provenance_kind;
|
||||
unsigned char provenance_fingerprint[32];
|
||||
} Lardon3DSparseCalibration;
|
||||
|
||||
typedef struct {
|
||||
uint64_t image_id;
|
||||
uint64_t calibration_id;
|
||||
unsigned char calibration_hash[32];
|
||||
} Lardon3DSparseCalibrationMember;
|
||||
|
||||
typedef struct {
|
||||
uint64_t scope_id;
|
||||
unsigned char scientific_hash[32];
|
||||
uint64_t member_count;
|
||||
} Lardon3DSparseCalibrationScope;
|
||||
|
||||
typedef struct {
|
||||
uint64_t reconstruction_id;
|
||||
uint64_t track_set_id;
|
||||
uint64_t calibration_scope_id;
|
||||
uint32_t sfm_kind;
|
||||
uint32_t sfm_version;
|
||||
unsigned char parameter_fingerprint[32];
|
||||
uint64_t component_count;
|
||||
uint64_t registered_image_count;
|
||||
uint64_t landmark_count;
|
||||
double reprojection_rmse_px;
|
||||
double reprojection_median_px;
|
||||
} Lardon3DSparseReconstruction;
|
||||
|
||||
typedef struct {
|
||||
uint64_t component_id;
|
||||
uint64_t component_key;
|
||||
uint64_t registered_image_count;
|
||||
uint64_t landmark_count;
|
||||
} Lardon3DSparseComponent;
|
||||
|
||||
typedef struct {
|
||||
uint64_t image_id;
|
||||
uint64_t component_key;
|
||||
double rotation_cw[9];
|
||||
double translation_cw[3];
|
||||
} Lardon3DSparseRegisteredImage;
|
||||
|
||||
typedef struct {
|
||||
uint64_t landmark_id;
|
||||
uint64_t track_id;
|
||||
uint64_t component_key;
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
double reprojection_rmse_px;
|
||||
double reprojection_median_px;
|
||||
uint64_t observation_count;
|
||||
} Lardon3DSparseLandmark;
|
||||
|
||||
typedef struct {
|
||||
uint64_t landmark_id;
|
||||
uint64_t track_id;
|
||||
uint64_t feature_set_id;
|
||||
uint32_t feature_index;
|
||||
uint32_t position_in_track;
|
||||
} Lardon3DSparseLandmarkObservation;
|
||||
|
||||
typedef struct {
|
||||
uint64_t track_set_id;
|
||||
uint64_t calibration_scope_id;
|
||||
uint32_t sfm_kind;
|
||||
uint32_t sfm_version;
|
||||
unsigned char parameter_fingerprint[32];
|
||||
const Lardon3DSparseComponent *components;
|
||||
size_t component_count;
|
||||
const Lardon3DSparseRegisteredImage *registered_images;
|
||||
size_t registered_image_count;
|
||||
const Lardon3DSparseLandmark *landmarks;
|
||||
size_t landmark_count;
|
||||
const Lardon3DSparseLandmarkObservation *observations;
|
||||
size_t observation_count;
|
||||
double reprojection_rmse_px;
|
||||
double reprojection_median_px;
|
||||
int64_t created_at;
|
||||
} Lardon3DSparsePublication;
|
||||
|
||||
typedef struct {
|
||||
uint64_t after_id;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
uint64_t next_after_id;
|
||||
Lardon3DSparseCalibration *items;
|
||||
} Lardon3DSparseCalibrationPage;
|
||||
|
||||
typedef struct {
|
||||
uint64_t after_id;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
uint64_t next_after_id;
|
||||
Lardon3DSparseReconstruction *items;
|
||||
} Lardon3DSparseReconstructionPage;
|
||||
|
||||
typedef struct {
|
||||
uint64_t after_component_key;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
uint64_t next_component_key;
|
||||
Lardon3DSparseComponent *items;
|
||||
} Lardon3DSparseComponentPage;
|
||||
|
||||
typedef struct {
|
||||
uint64_t after_image_id;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
uint64_t next_image_id;
|
||||
Lardon3DSparseRegisteredImage *items;
|
||||
} Lardon3DSparseRegisteredImagePage;
|
||||
|
||||
typedef struct {
|
||||
uint64_t after_track_id;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
uint64_t next_track_id;
|
||||
Lardon3DSparseLandmark *items;
|
||||
} Lardon3DSparseLandmarkPage;
|
||||
|
||||
typedef struct {
|
||||
uint64_t after_landmark_id;
|
||||
uint32_t after_position_in_track;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
uint64_t next_landmark_id;
|
||||
uint32_t next_position_in_track;
|
||||
Lardon3DSparseLandmarkObservation *items;
|
||||
} Lardon3DSparseObservationPage;
|
||||
|
||||
Lardon3DProjectDbResult
|
||||
lardon3d_sparse_calibration_create(Lardon3DProjectDb *database,
|
||||
const Lardon3DSparseCalibration *input,
|
||||
Lardon3DSparseCalibration *output);
|
||||
Lardon3DProjectDbResult
|
||||
lardon3d_sparse_calibration_load(Lardon3DProjectDb *database,
|
||||
uint64_t calibration_id,
|
||||
Lardon3DSparseCalibration *output);
|
||||
Lardon3DProjectDbResult lardon3d_sparse_calibration_find_by_hash(
|
||||
Lardon3DProjectDb *database, const unsigned char scientific_hash[32],
|
||||
Lardon3DSparseCalibration *output);
|
||||
|
||||
Lardon3DProjectDbResult lardon3d_sparse_calibration_scope_create(
|
||||
Lardon3DProjectDb *database, const Lardon3DSparseCalibrationMember *members,
|
||||
size_t member_count, Lardon3DSparseCalibrationScope *output);
|
||||
Lardon3DProjectDbResult
|
||||
lardon3d_sparse_calibration_scope_load(Lardon3DProjectDb *database,
|
||||
uint64_t scope_id,
|
||||
Lardon3DSparseCalibrationScope *output);
|
||||
Lardon3DProjectDbResult lardon3d_sparse_calibration_scope_find_by_hash(
|
||||
Lardon3DProjectDb *database, const unsigned char scientific_hash[32],
|
||||
Lardon3DSparseCalibrationScope *output);
|
||||
Lardon3DProjectDbResult lardon3d_sparse_calibration_scope_list_members(
|
||||
Lardon3DProjectDb *database, uint64_t scope_id, uint64_t after_image_id,
|
||||
Lardon3DSparseCalibrationMember *items, size_t capacity, size_t *count,
|
||||
uint64_t *next_after_image_id);
|
||||
|
||||
Lardon3DProjectDbResult lardon3d_sparse_reconstruction_publish(
|
||||
Lardon3DProjectDb *database, const Lardon3DSparsePublication *publication,
|
||||
Lardon3DSparseReconstruction *output);
|
||||
Lardon3DProjectDbResult lardon3d_sparse_reconstruction_find_exact(
|
||||
Lardon3DProjectDb *database, uint64_t track_set_id,
|
||||
uint64_t calibration_scope_id, uint32_t sfm_kind, uint32_t sfm_version,
|
||||
const unsigned char parameter_fingerprint[32],
|
||||
Lardon3DSparseReconstruction *output);
|
||||
Lardon3DProjectDbResult
|
||||
lardon3d_sparse_reconstruction_load(Lardon3DProjectDb *database,
|
||||
uint64_t reconstruction_id,
|
||||
Lardon3DSparseReconstruction *output);
|
||||
Lardon3DProjectDbResult
|
||||
lardon3d_sparse_reconstruction_list(Lardon3DProjectDb *database,
|
||||
uint64_t after_id, size_t capacity,
|
||||
Lardon3DSparseReconstructionPage *page);
|
||||
Lardon3DProjectDbResult
|
||||
lardon3d_sparse_component_list(Lardon3DProjectDb *database,
|
||||
uint64_t reconstruction_id,
|
||||
uint64_t after_component_key, size_t capacity,
|
||||
Lardon3DSparseComponentPage *page);
|
||||
Lardon3DProjectDbResult
|
||||
lardon3d_sparse_registered_image_list(Lardon3DProjectDb *database,
|
||||
uint64_t reconstruction_id,
|
||||
uint64_t after_image_id, size_t capacity,
|
||||
Lardon3DSparseRegisteredImagePage *page);
|
||||
Lardon3DProjectDbResult lardon3d_sparse_landmark_list(
|
||||
Lardon3DProjectDb *database, uint64_t reconstruction_id,
|
||||
uint64_t after_track_id, size_t capacity, Lardon3DSparseLandmarkPage *page);
|
||||
Lardon3DProjectDbResult lardon3d_sparse_observation_list(
|
||||
Lardon3DProjectDb *database, uint64_t reconstruction_id,
|
||||
uint64_t after_landmark_id, uint32_t after_position_in_track,
|
||||
size_t capacity, Lardon3DSparseObservationPage *page);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
74
meson.build
74
meson.build
|
|
@ -143,7 +143,7 @@ executable(
|
|||
'src/geometric_verifier_task.c',
|
||||
'src/image_view.c',
|
||||
'src/project.c',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/task_checkpoint.c',
|
||||
'src/task_kind_registry.c',
|
||||
|
|
@ -172,7 +172,7 @@ import_test = executable(
|
|||
'src/app_state.c',
|
||||
'src/import.c',
|
||||
'src/image_catalog_persistent.c',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
|
|
@ -191,7 +191,7 @@ import_task_test = executable(
|
|||
'src/import.c',
|
||||
'src/import_task.c',
|
||||
'src/project.c',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/task_checkpoint.c',
|
||||
'src/task_kind_registry.c',
|
||||
|
|
@ -229,7 +229,7 @@ image_catalog_test = executable(
|
|||
'src/app_state.c',
|
||||
'src/image_catalog.c',
|
||||
'src/image_catalog_persistent.c',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
|
|
@ -246,7 +246,7 @@ persistent_image_catalog_test = executable(
|
|||
'tests/test_persistent_image_catalog.c',
|
||||
'src/app_state.c',
|
||||
'src/image_catalog_persistent.c',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
|
|
@ -266,7 +266,7 @@ feature_store_test = executable(
|
|||
'src/image_catalog_persistent.c',
|
||||
'src/feature_extractor_opencv.cpp',
|
||||
'src/feature_store.c',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
|
|
@ -286,7 +286,7 @@ feature_file_robustness_test = executable(
|
|||
'src/image_catalog_persistent.c',
|
||||
'src/feature_extractor_opencv.cpp',
|
||||
'src/feature_store.c',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
|
|
@ -304,7 +304,7 @@ visual_index_test = executable(
|
|||
'tests/test_visual_index.c', 'src/app_state.c',
|
||||
'src/image_catalog_persistent.c', 'src/feature_store.c',
|
||||
'src/feature_extractor_opencv.cpp',
|
||||
'src/visual_index.c', 'src/project_db.c', 'src/task.c',
|
||||
'src/visual_index.c', 'src/project_db.c', 'src/project_db_sparse_sfm.c', 'src/task.c',
|
||||
'src/resource_governor.c', 'src/resource_snapshot.c',
|
||||
],
|
||||
c_args: [
|
||||
|
|
@ -322,7 +322,7 @@ candidate_pair_gen_test = executable(
|
|||
'src/image_catalog_persistent.c', 'src/feature_store.c',
|
||||
'src/feature_extractor_opencv.cpp',
|
||||
'src/visual_index.c', 'src/candidate_pair_gen.c',
|
||||
'src/project_db.c', 'src/task.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c', 'src/task.c',
|
||||
'src/resource_governor.c', 'src/resource_snapshot.c',
|
||||
],
|
||||
include_directories: include_directories('include'),
|
||||
|
|
@ -334,7 +334,7 @@ candidate_pair_task_test = executable(
|
|||
'test-candidate-pair-task',
|
||||
sources: [
|
||||
'tests/test_candidate_pair_task.c', 'src/app_state.c',
|
||||
'src/project.c', 'src/project_db.c', 'src/task.c',
|
||||
'src/project.c', 'src/project_db.c', 'src/project_db_sparse_sfm.c', 'src/task.c',
|
||||
'src/task_checkpoint.c', 'src/task_kind_registry.c',
|
||||
'src/task_kinds.c', 'src/matcher_task.c', 'src/matcher.cpp',
|
||||
'src/geometric_verifier_task.c', 'src/geometric_verifier.cpp',
|
||||
|
|
@ -364,7 +364,7 @@ matcher_task_test = executable(
|
|||
'test-matcher-task',
|
||||
sources: [
|
||||
'tests/test_matcher_task.c', 'src/app_state.c', 'src/project.c',
|
||||
'src/project_db.c', 'src/task.c', 'src/task_checkpoint.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c', 'src/task.c', 'src/task_checkpoint.c',
|
||||
'src/task_kind_registry.c', 'src/task_kinds.c', 'src/task_queue.c',
|
||||
'src/matcher_task.c', 'src/matcher.cpp', 'src/match_file.c',
|
||||
'src/geometric_verifier_task.c', 'src/geometric_verifier.cpp',
|
||||
|
|
@ -390,7 +390,7 @@ feature_task_test = executable(
|
|||
'test-feature-task',
|
||||
sources: [
|
||||
'tests/test_feature_task.c', 'src/app_state.c', 'src/project.c',
|
||||
'src/project_db.c', 'src/task.c', 'src/task_checkpoint.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c', 'src/task.c', 'src/task_checkpoint.c',
|
||||
'src/task_kind_registry.c', 'src/task_kinds.c', 'src/task_queue.c',
|
||||
'src/matcher_task.c', 'src/matcher.cpp', 'src/match_file.c',
|
||||
'src/geometric_verifier_task.c', 'src/geometric_verifier.cpp',
|
||||
|
|
@ -424,7 +424,7 @@ precision_consolidation_test = executable(
|
|||
'test-precision-consolidation',
|
||||
sources: [
|
||||
'tests/test_precision_consolidation.c', 'src/app_state.c',
|
||||
'src/project.c', 'src/project_db.c', 'src/task.c',
|
||||
'src/project.c', 'src/project_db.c', 'src/project_db_sparse_sfm.c', 'src/task.c',
|
||||
'src/task_checkpoint.c', 'src/task_kind_registry.c',
|
||||
'src/task_kinds.c', 'src/matcher_task.c', 'src/matcher.cpp',
|
||||
'src/geometric_verifier_task.c', 'src/geometric_verifier.cpp',
|
||||
|
|
@ -496,7 +496,7 @@ project_db_test = executable(
|
|||
'test-project-db',
|
||||
sources: [
|
||||
'tests/test_project_db.c',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
|
|
@ -508,11 +508,39 @@ project_db_test = executable(
|
|||
|
||||
test('project-db', project_db_test, timeout: 30)
|
||||
|
||||
sparse_sfm_model_test = executable(
|
||||
'test-sparse-sfm-model',
|
||||
sources: [
|
||||
'tests/test_sparse_sfm_model.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
],
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: [threads, sqlite3, openssl],
|
||||
)
|
||||
|
||||
test('sparse-sfm-model', sparse_sfm_model_test, timeout: 30)
|
||||
|
||||
sparse_sfm_resource_test = executable(
|
||||
'test-sparse-sfm-resource',
|
||||
sources: [
|
||||
'tests/test_sparse_sfm_resource.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
],
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: [threads, sqlite3, openssl],
|
||||
)
|
||||
|
||||
match_result_test = executable(
|
||||
'test-match-result',
|
||||
sources: [
|
||||
'tests/test_match_result.c',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
|
|
@ -528,7 +556,7 @@ geometric_verification_test = executable(
|
|||
'test-geometric-verification',
|
||||
sources: [
|
||||
'tests/test_geometric_verification.c',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
|
|
@ -546,7 +574,7 @@ project_test = executable(
|
|||
'tests/test_project.c',
|
||||
'src/app_state.c',
|
||||
'src/project.c',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/task_checkpoint.c',
|
||||
'src/task_kind_registry.c',
|
||||
|
|
@ -679,7 +707,7 @@ matcher_test = executable(
|
|||
'src/matcher.cpp',
|
||||
'src/feature_store.c',
|
||||
'src/feature_extractor_opencv.cpp',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
|
|
@ -749,7 +777,7 @@ matcher_e2e_test = executable(
|
|||
'src/matcher.cpp',
|
||||
'src/feature_store.c',
|
||||
'src/feature_extractor_opencv.cpp',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
|
|
@ -771,7 +799,7 @@ executable(
|
|||
'src/matcher.cpp',
|
||||
'src/feature_store.c',
|
||||
'src/feature_extractor_opencv.cpp',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
|
|
@ -797,7 +825,7 @@ geometric_verifier_test = executable(
|
|||
'src/match_file.c',
|
||||
'src/feature_store.c',
|
||||
'src/feature_extractor_opencv.cpp',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
|
|
@ -815,7 +843,7 @@ geometric_verifier_task_test = executable(
|
|||
'test-geometric-verifier-task',
|
||||
sources: [
|
||||
'tests/test_geometric_verifier_task.c', 'src/app_state.c', 'src/project.c',
|
||||
'src/project_db.c', 'src/task.c', 'src/task_checkpoint.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c', 'src/task.c', 'src/task_checkpoint.c',
|
||||
'src/task_kind_registry.c', 'src/task_kinds.c', 'src/task_queue.c',
|
||||
'src/geometric_verifier_task.c', 'src/geometric_verifier.cpp',
|
||||
'src/match_file.c', 'src/feature_store.c', 'src/resource_governor.c',
|
||||
|
|
@ -851,7 +879,7 @@ track_builder_project_test = executable(
|
|||
'src/track_builder.cpp',
|
||||
'src/track_builder_project.cpp',
|
||||
'src/track_builder_task.cpp',
|
||||
'src/project_db.c',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/app_state.c',
|
||||
'src/task.c',
|
||||
'src/task_kind_registry.c',
|
||||
|
|
|
|||
158
src/project_db.c
158
src/project_db.c
|
|
@ -12,12 +12,7 @@
|
|||
#include <time.h>
|
||||
|
||||
#include <lardon3d/project_db.h>
|
||||
|
||||
struct Lardon3DProjectDb {
|
||||
sqlite3 *connection;
|
||||
pthread_mutex_t mutex;
|
||||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
};
|
||||
#include "project_db_internal.h"
|
||||
|
||||
static const char schema_v5[] =
|
||||
"CREATE TABLE metadata(key TEXT PRIMARY KEY,value INTEGER NOT NULL);"
|
||||
|
|
@ -319,13 +314,122 @@ static const char schema_track_builder_task_v15[] =
|
|||
"scope_sha256 BLOB NOT NULL CHECK(length(scope_sha256)=32),"
|
||||
"scope_format_version INTEGER NOT NULL CHECK(scope_format_version=1));";
|
||||
|
||||
static const char schema_sparse_sfm_v16_a[] =
|
||||
"CREATE TABLE sparse_calibrations("
|
||||
"calibration_id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(calibration_id>0),"
|
||||
"scientific_hash BLOB NOT NULL CHECK(length(scientific_hash)=32) UNIQUE,"
|
||||
"model_kind INTEGER NOT NULL CHECK(model_kind=1),model_version INTEGER NOT "
|
||||
"NULL CHECK(model_version=1),"
|
||||
"width INTEGER NOT NULL CHECK(width>0),height INTEGER NOT NULL "
|
||||
"CHECK(height>0),"
|
||||
"fx REAL NOT NULL,fy REAL NOT NULL,cx REAL NOT NULL,cy REAL NOT NULL,"
|
||||
"k1 REAL NOT NULL,k2 REAL NOT NULL,p1 REAL NOT NULL,p2 REAL NOT NULL,"
|
||||
"provenance_kind INTEGER NOT NULL CHECK(provenance_kind IN(1,2)),"
|
||||
"provenance_fingerprint BLOB NOT NULL "
|
||||
"CHECK(length(provenance_fingerprint)=32));"
|
||||
"CREATE TABLE sparse_calibration_scopes("
|
||||
"scope_id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(scope_id>0),"
|
||||
"scientific_hash BLOB NOT NULL CHECK(length(scientific_hash)=32) UNIQUE,"
|
||||
"member_count INTEGER NOT NULL CHECK(member_count>0));"
|
||||
"CREATE TABLE sparse_calibration_scope_images("
|
||||
"scope_id INTEGER NOT NULL REFERENCES sparse_calibration_scopes(scope_id) "
|
||||
"ON DELETE CASCADE,"
|
||||
"image_id INTEGER NOT NULL REFERENCES images(image_id),"
|
||||
"calibration_id INTEGER NOT NULL REFERENCES "
|
||||
"sparse_calibrations(calibration_id),"
|
||||
"PRIMARY KEY(scope_id,image_id));"
|
||||
"CREATE INDEX sparse_scope_images_calibration_idx ON "
|
||||
"sparse_calibration_scope_images(scope_id,calibration_id);"
|
||||
"CREATE INDEX sparse_scope_images_calibration_lookup_idx ON "
|
||||
"sparse_calibration_scope_images(calibration_id,scope_id);";
|
||||
|
||||
static const char schema_sparse_sfm_v16_b[] =
|
||||
"CREATE TABLE sparse_reconstructions("
|
||||
"reconstruction_id INTEGER PRIMARY KEY AUTOINCREMENT "
|
||||
"CHECK(reconstruction_id>0),"
|
||||
"track_set_id INTEGER NOT NULL REFERENCES track_sets(track_set_id),"
|
||||
"calibration_scope_id INTEGER NOT NULL REFERENCES "
|
||||
"sparse_calibration_scopes(scope_id),"
|
||||
"sfm_kind INTEGER NOT NULL CHECK(sfm_kind=1),sfm_version INTEGER NOT NULL "
|
||||
"CHECK(sfm_version=1),"
|
||||
"parameter_fingerprint BLOB NOT NULL "
|
||||
"CHECK(length(parameter_fingerprint)=32),"
|
||||
"component_count INTEGER NOT NULL CHECK(component_count>0),"
|
||||
"registered_image_count INTEGER NOT NULL CHECK(registered_image_count>=2),"
|
||||
"landmark_count INTEGER NOT NULL CHECK(landmark_count>0),"
|
||||
"reprojection_rmse_px REAL NOT NULL,reprojection_median_px REAL NOT "
|
||||
"NULL,created_at INTEGER NOT NULL,"
|
||||
"UNIQUE(track_set_id,calibration_scope_id,sfm_kind,sfm_version,parameter_"
|
||||
"fingerprint));"
|
||||
"CREATE INDEX sparse_reconstructions_scope_idx ON "
|
||||
"sparse_reconstructions(calibration_scope_id);"
|
||||
"CREATE TABLE sparse_reconstruction_components("
|
||||
"component_id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(component_id>0),"
|
||||
"reconstruction_id INTEGER NOT NULL REFERENCES "
|
||||
"sparse_reconstructions(reconstruction_id) ON DELETE CASCADE,"
|
||||
"component_key INTEGER NOT NULL "
|
||||
"CHECK(component_key>0),registered_image_count INTEGER NOT NULL "
|
||||
"CHECK(registered_image_count>0),"
|
||||
"landmark_count INTEGER NOT NULL CHECK(landmark_count>0),"
|
||||
"UNIQUE(reconstruction_id,component_id),UNIQUE(reconstruction_id,component_"
|
||||
"key));"
|
||||
"CREATE INDEX sparse_components_page_idx ON "
|
||||
"sparse_reconstruction_components(reconstruction_id,component_key);"
|
||||
"CREATE TABLE sparse_registered_images("
|
||||
"reconstruction_id INTEGER NOT NULL REFERENCES "
|
||||
"sparse_reconstructions(reconstruction_id) ON DELETE CASCADE,"
|
||||
"component_id INTEGER NOT NULL, image_id INTEGER NOT NULL REFERENCES "
|
||||
"images(image_id),"
|
||||
"rotation_00 REAL NOT NULL,rotation_01 REAL NOT NULL,rotation_02 REAL NOT "
|
||||
"NULL,"
|
||||
"rotation_10 REAL NOT NULL,rotation_11 REAL NOT NULL,rotation_12 REAL NOT "
|
||||
"NULL,"
|
||||
"rotation_20 REAL NOT NULL,rotation_21 REAL NOT NULL,rotation_22 REAL NOT "
|
||||
"NULL,"
|
||||
"translation_0 REAL NOT NULL,translation_1 REAL NOT NULL,translation_2 "
|
||||
"REAL NOT NULL,"
|
||||
"PRIMARY KEY(reconstruction_id,image_id),"
|
||||
"FOREIGN KEY(reconstruction_id,component_id) REFERENCES "
|
||||
"sparse_reconstruction_components(reconstruction_id,component_id));"
|
||||
"CREATE INDEX sparse_registered_page_idx ON "
|
||||
"sparse_registered_images(reconstruction_id,component_id,image_id);"
|
||||
"CREATE INDEX sparse_registered_image_lookup_idx ON "
|
||||
"sparse_registered_images(reconstruction_id,image_id);"
|
||||
"CREATE TABLE sparse_landmarks("
|
||||
"landmark_id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(landmark_id>0),"
|
||||
"reconstruction_id INTEGER NOT NULL REFERENCES "
|
||||
"sparse_reconstructions(reconstruction_id) ON DELETE CASCADE,"
|
||||
"component_id INTEGER NOT NULL,track_id INTEGER NOT NULL REFERENCES "
|
||||
"tracks(track_id),"
|
||||
"x REAL NOT NULL,y REAL NOT NULL,z REAL NOT NULL,reprojection_rmse_px REAL "
|
||||
"NOT NULL,"
|
||||
"reprojection_median_px REAL NOT NULL,observation_count INTEGER NOT NULL "
|
||||
"CHECK(observation_count>0),"
|
||||
"UNIQUE(reconstruction_id,track_id),FOREIGN "
|
||||
"KEY(reconstruction_id,component_id) "
|
||||
"REFERENCES "
|
||||
"sparse_reconstruction_components(reconstruction_id,component_id));"
|
||||
"CREATE INDEX sparse_landmarks_page_idx ON "
|
||||
"sparse_landmarks(reconstruction_id,component_id,track_id);"
|
||||
"CREATE INDEX sparse_landmark_track_lookup_idx ON "
|
||||
"sparse_landmarks(reconstruction_id,track_id);"
|
||||
"CREATE TABLE sparse_landmark_observations("
|
||||
"landmark_id INTEGER NOT NULL REFERENCES sparse_landmarks(landmark_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(landmark_id,feature_set_id,feature_index));"
|
||||
"CREATE INDEX sparse_landmark_observations_page_idx ON "
|
||||
"sparse_landmark_observations(landmark_id,position_in_track);";
|
||||
|
||||
static void copy_error(char destination[LARDON3D_PROJECT_DB_ERROR_CAPACITY], const char *text) {
|
||||
if (destination) {
|
||||
(void)snprintf(destination, LARDON3D_PROJECT_DB_ERROR_CAPACITY, "%s", text ? text : "");
|
||||
}
|
||||
}
|
||||
|
||||
static Lardon3DProjectDbResult sqlite_result(Lardon3DProjectDb *database, int code,
|
||||
Lardon3DProjectDbResult sqlite_result(Lardon3DProjectDb *database, int code,
|
||||
const char *context) {
|
||||
(void)snprintf(database->error, sizeof(database->error), "%s: %s", context,
|
||||
sqlite3_errmsg(database->connection));
|
||||
|
|
@ -367,7 +471,7 @@ static bool database_time(sqlite3_int64 value, time_t *output) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static Lardon3DProjectDbResult execute(Lardon3DProjectDb *database, const char *sql,
|
||||
Lardon3DProjectDbResult execute(Lardon3DProjectDb *database, const char *sql,
|
||||
const char *context) {
|
||||
char *message = NULL;
|
||||
int code = sqlite3_exec(database->connection, sql, NULL, NULL, &message);
|
||||
|
|
@ -380,7 +484,7 @@ static Lardon3DProjectDbResult execute(Lardon3DProjectDb *database, const char *
|
|||
return sqlite_result(database, code, context);
|
||||
}
|
||||
|
||||
static Lardon3DProjectDbResult prepare(Lardon3DProjectDb *database, const char *sql,
|
||||
Lardon3DProjectDbResult prepare(Lardon3DProjectDb *database, const char *sql,
|
||||
sqlite3_stmt **statement) {
|
||||
int code = sqlite3_prepare_v2(database->connection, sql, -1, statement, NULL);
|
||||
return code == SQLITE_OK ? LARDON3D_PROJECT_DB_OK : sqlite_result(database, code, "prepare");
|
||||
|
|
@ -409,7 +513,8 @@ static Lardon3DProjectDbResult migrate(Lardon3DProjectDb *database, unsigned int
|
|||
if (from_version != 0 && from_version != 1 && from_version != 2 && from_version != 3 &&
|
||||
from_version != 4 && from_version != 5 && from_version != 6 && from_version != 7 &&
|
||||
from_version != 8 && from_version != 9 && from_version != 10 && from_version != 11 &&
|
||||
from_version != 12 && from_version != 13 && from_version != 14) {
|
||||
from_version != 12 && from_version != 13 && from_version != 14 &&
|
||||
from_version != 15) {
|
||||
return LARDON3D_PROJECT_DB_CORRUPT;
|
||||
}
|
||||
Lardon3DProjectDbResult result = execute(database, "BEGIN IMMEDIATE", "begin migration");
|
||||
|
|
@ -713,6 +818,27 @@ static Lardon3DProjectDbResult migrate(Lardon3DProjectDb *database, unsigned int
|
|||
"finish schema v15 migration");
|
||||
}
|
||||
}
|
||||
if (result == LARDON3D_PROJECT_DB_OK && from_version < 16) {
|
||||
result = execute(database, schema_sparse_sfm_v16_a,
|
||||
"migrate schema v15 to v16 (part 1)");
|
||||
#ifdef LARDON3D_PROJECT_DB_TESTING
|
||||
if (result == LARDON3D_PROJECT_DB_OK &&
|
||||
getenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V16")) {
|
||||
result = execute(database, "INSERT INTO missing_v16_test_table VALUES(1)",
|
||||
"forced migration v16 failure");
|
||||
}
|
||||
#endif
|
||||
if (result == LARDON3D_PROJECT_DB_OK) {
|
||||
result = execute(database, schema_sparse_sfm_v16_b,
|
||||
"migrate schema v15 to v16 (part 2)");
|
||||
}
|
||||
if (result == LARDON3D_PROJECT_DB_OK) {
|
||||
result = execute(
|
||||
database,
|
||||
"UPDATE metadata SET value=16 WHERE key='schema_version' AND value=15",
|
||||
"finish schema v16 migration");
|
||||
}
|
||||
}
|
||||
if (result == LARDON3D_PROJECT_DB_OK) {
|
||||
result = execute(database, "COMMIT", "commit migration");
|
||||
}
|
||||
|
|
@ -833,7 +959,15 @@ Lardon3DProjectDbResult lardon3d_project_db_open(const char *path, Lardon3DProje
|
|||
"track_sets",
|
||||
"tracks",
|
||||
"track_observations",
|
||||
"track_builder_tasks"};
|
||||
"track_builder_tasks",
|
||||
"sparse_calibrations",
|
||||
"sparse_calibration_scopes",
|
||||
"sparse_calibration_scope_images",
|
||||
"sparse_reconstructions",
|
||||
"sparse_reconstruction_components",
|
||||
"sparse_registered_images",
|
||||
"sparse_landmarks",
|
||||
"sparse_landmark_observations"};
|
||||
for (size_t index = 0; index < sizeof(required) / sizeof(required[0]) &&
|
||||
result == LARDON3D_PROJECT_DB_OK;
|
||||
++index) {
|
||||
|
|
@ -908,7 +1042,7 @@ Lardon3DProjectDbResult lardon3d_project_db_legacy_catalog_pending(Lardon3DProje
|
|||
return result;
|
||||
}
|
||||
|
||||
static Lardon3DProjectDbResult step_done(Lardon3DProjectDb *database, sqlite3_stmt *statement,
|
||||
Lardon3DProjectDbResult step_done(Lardon3DProjectDb *database, sqlite3_stmt *statement,
|
||||
const char *context) {
|
||||
int code = sqlite3_step(statement);
|
||||
if (code != SQLITE_DONE) {
|
||||
|
|
|
|||
24
src/project_db_internal.h
Normal file
24
src/project_db_internal.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef LARDON3D_PROJECT_DB_INTERNAL_H
|
||||
#define LARDON3D_PROJECT_DB_INTERNAL_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <lardon3d/project_db.h>
|
||||
|
||||
struct Lardon3DProjectDb {
|
||||
sqlite3 *connection;
|
||||
pthread_mutex_t mutex;
|
||||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
};
|
||||
|
||||
Lardon3DProjectDbResult sqlite_result(Lardon3DProjectDb *database, int code,
|
||||
const char *context);
|
||||
Lardon3DProjectDbResult execute(Lardon3DProjectDb *database, const char *sql,
|
||||
const char *context);
|
||||
Lardon3DProjectDbResult prepare(Lardon3DProjectDb *database, const char *sql,
|
||||
sqlite3_stmt **statement);
|
||||
Lardon3DProjectDbResult step_done(Lardon3DProjectDb *database,
|
||||
sqlite3_stmt *statement, const char *context);
|
||||
|
||||
#endif
|
||||
1522
src/project_db_sparse_sfm.c
Normal file
1522
src/project_db_sparse_sfm.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -35,20 +35,24 @@ static bool execute_sql(const char *path, const char *sql) {
|
|||
return sqlite3_close(connection) == SQLITE_OK && ok;
|
||||
}
|
||||
|
||||
static bool query_integer(const char *path, const char *sql, sqlite3_int64 expected) {
|
||||
static bool query_integer(const char *path, const char *sql,
|
||||
sqlite3_int64 expected) {
|
||||
sqlite3 *connection = NULL;
|
||||
sqlite3_stmt *statement = NULL;
|
||||
if (sqlite3_open_v2(path, &connection, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK ||
|
||||
if (sqlite3_open_v2(path, &connection, SQLITE_OPEN_READONLY, NULL) !=
|
||||
SQLITE_OK ||
|
||||
sqlite3_prepare_v2(connection, sql, -1, &statement, NULL) != SQLITE_OK) {
|
||||
if (statement) (void)sqlite3_finalize(statement);
|
||||
if (connection) (void)sqlite3_close(connection);
|
||||
if (statement)
|
||||
(void)sqlite3_finalize(statement);
|
||||
if (connection)
|
||||
(void)sqlite3_close(connection);
|
||||
return false;
|
||||
}
|
||||
bool matches = sqlite3_step(statement) == SQLITE_ROW &&
|
||||
sqlite3_column_int64(statement, 0) == expected &&
|
||||
sqlite3_step(statement) == SQLITE_DONE;
|
||||
return sqlite3_finalize(statement) == SQLITE_OK && sqlite3_close(connection) == SQLITE_OK &&
|
||||
matches;
|
||||
return sqlite3_finalize(statement) == SQLITE_OK &&
|
||||
sqlite3_close(connection) == SQLITE_OK && matches;
|
||||
}
|
||||
|
||||
static void asset_path(const unsigned char hash[32], const char *kind,
|
||||
|
|
@ -60,13 +64,14 @@ static void asset_path(const unsigned char hash[32], const char *kind,
|
|||
hex[index * 2 + 1] = digits[hash[index] & 15U];
|
||||
}
|
||||
hex[64] = '\0';
|
||||
(void)snprintf(path, LARDON3D_PROJECT_DB_PATH_CAPACITY, "assets/%s/%c%c/%s", kind, hex[0],
|
||||
hex[1], hex);
|
||||
(void)snprintf(path, LARDON3D_PROJECT_DB_PATH_CAPACITY, "assets/%s/%c%c/%s",
|
||||
kind, hex[0], hex[1], hex);
|
||||
}
|
||||
|
||||
static bool create_match_result(Lardon3DProjectDb *database, uint64_t pair_id, uint64_t fs_a,
|
||||
uint64_t fs_b, uint32_t version, unsigned char fingerprint_byte,
|
||||
int status, uint32_t match_count, uint64_t *id) {
|
||||
static bool create_match_result(Lardon3DProjectDb *database, uint64_t pair_id,
|
||||
uint64_t fs_a, uint64_t fs_b, uint32_t version,
|
||||
unsigned char fingerprint_byte, int status,
|
||||
uint32_t match_count, uint64_t *id) {
|
||||
unsigned char fingerprint[32] = {0};
|
||||
unsigned char hash[32] = {0};
|
||||
char path[LARDON3D_PROJECT_DB_PATH_CAPACITY];
|
||||
|
|
@ -75,10 +80,12 @@ static bool create_match_result(Lardon3DProjectDb *database, uint64_t pair_id, u
|
|||
asset_path(hash, "matches", path);
|
||||
Lardon3DProjectDbMatchResult result;
|
||||
Lardon3DProjectDbResult db_result = lardon3d_project_db_create_match_result(
|
||||
database, pair_id, fs_a, fs_b, "model-test", version, fingerprint, status, match_count,
|
||||
status == LARDON3D_MATCH_RESULT_STATUS_MATCHED ? hash : NULL,
|
||||
database, pair_id, fs_a, fs_b, "model-test", version, fingerprint, status,
|
||||
match_count, status == LARDON3D_MATCH_RESULT_STATUS_MATCHED ? hash : NULL,
|
||||
status == LARDON3D_MATCH_RESULT_STATUS_MATCHED ? path : NULL,
|
||||
status == LARDON3D_MATCH_RESULT_STATUS_MATCHED ? 32U + (uint64_t)match_count * 12U : 0,
|
||||
status == LARDON3D_MATCH_RESULT_STATUS_MATCHED
|
||||
? 32U + (uint64_t)match_count * 12U
|
||||
: 0,
|
||||
version, &result);
|
||||
if (db_result != LARDON3D_PROJECT_DB_OK) {
|
||||
return false;
|
||||
|
|
@ -89,8 +96,8 @@ static bool create_match_result(Lardon3DProjectDb *database, uint64_t pair_id, u
|
|||
|
||||
static bool create_parents(Lardon3DProjectDb *database, Parents *parents) {
|
||||
Lardon3DProjectDbScanSet scanset;
|
||||
if (lardon3d_project_db_create_scanset(database, "Geometry-model", &scanset) !=
|
||||
LARDON3D_PROJECT_DB_OK) {
|
||||
if (lardon3d_project_db_create_scanset(database, "Geometry-model",
|
||||
&scanset) != LARDON3D_PROJECT_DB_OK) {
|
||||
return false;
|
||||
}
|
||||
unsigned char image_hash_a[32] = {0x11};
|
||||
|
|
@ -102,17 +109,20 @@ static bool create_parents(Lardon3DProjectDb *database, Parents *parents) {
|
|||
Lardon3DProjectDbImageRegisterStatus register_status;
|
||||
Lardon3DProjectDbImage image_a;
|
||||
Lardon3DProjectDbImage image_b;
|
||||
if (lardon3d_project_db_register_image(database, scanset.scanset_id, image_hash_a, image_path_a,
|
||||
10, "a.jpg", "/a.jpg", 0, 1, ®ister_status,
|
||||
if (lardon3d_project_db_register_image(
|
||||
database, scanset.scanset_id, image_hash_a, image_path_a, 10, "a.jpg",
|
||||
"/a.jpg", 0, 1, ®ister_status,
|
||||
&image_a) != LARDON3D_PROJECT_DB_OK ||
|
||||
lardon3d_project_db_register_image(database, scanset.scanset_id, image_hash_b, image_path_b,
|
||||
10, "b.jpg", "/b.jpg", 0, 2, ®ister_status,
|
||||
lardon3d_project_db_register_image(
|
||||
database, scanset.scanset_id, image_hash_b, image_path_b, 10, "b.jpg",
|
||||
"/b.jpg", 0, 2, ®ister_status,
|
||||
&image_b) != LARDON3D_PROJECT_DB_OK) {
|
||||
return false;
|
||||
}
|
||||
Lardon3DProjectDbCandidatePair pair;
|
||||
if (lardon3d_project_db_create_candidate_pair(database, image_a.image_id, image_b.image_id, 3,
|
||||
&pair) != LARDON3D_PROJECT_DB_OK) {
|
||||
if (lardon3d_project_db_create_candidate_pair(database, image_a.image_id,
|
||||
image_b.image_id, 3, &pair) !=
|
||||
LARDON3D_PROJECT_DB_OK) {
|
||||
return false;
|
||||
}
|
||||
unsigned char feature_hash_a[32] = {0x33};
|
||||
|
|
@ -124,39 +134,52 @@ static bool create_parents(Lardon3DProjectDb *database, Parents *parents) {
|
|||
Lardon3DProjectDbFeatureSet feature_a;
|
||||
Lardon3DProjectDbFeatureSet feature_b;
|
||||
if (lardon3d_project_db_register_feature_set(
|
||||
database, image_a.image_id, "orb", 1, feature_hash_a, image_hash_a, 8192, 1, 32,
|
||||
feature_hash_a, feature_path_a, 100, LARDON3D_DB_FEATURE_ASSET_DURABLE, 0, 4,
|
||||
database, image_a.image_id, "orb", 1, feature_hash_a, image_hash_a,
|
||||
8192, 1, 32, feature_hash_a, feature_path_a, 100,
|
||||
LARDON3D_DB_FEATURE_ASSET_DURABLE, 0, 4,
|
||||
&feature_a) != LARDON3D_PROJECT_DB_OK ||
|
||||
lardon3d_project_db_register_feature_set(
|
||||
database, image_b.image_id, "orb", 1, feature_hash_b, image_hash_b, 8192, 1, 32,
|
||||
feature_hash_b, feature_path_b, 100, LARDON3D_DB_FEATURE_ASSET_DURABLE, 0, 5,
|
||||
database, image_b.image_id, "orb", 1, feature_hash_b, image_hash_b,
|
||||
8192, 1, 32, feature_hash_b, feature_path_b, 100,
|
||||
LARDON3D_DB_FEATURE_ASSET_DURABLE, 0, 5,
|
||||
&feature_b) != LARDON3D_PROJECT_DB_OK) {
|
||||
return false;
|
||||
}
|
||||
parents->candidate_pair_id = pair.candidate_pair_id;
|
||||
parents->feature_set_id_a = feature_a.feature_set_id;
|
||||
parents->feature_set_id_b = feature_b.feature_set_id;
|
||||
return create_match_result(database, pair.candidate_pair_id, feature_a.feature_set_id,
|
||||
feature_b.feature_set_id, 1, 0x51,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 9, &parents->matched_id) &&
|
||||
create_match_result(database, pair.candidate_pair_id, feature_a.feature_set_id,
|
||||
feature_b.feature_set_id, 2, 0x52,
|
||||
LARDON3D_MATCH_RESULT_STATUS_NO_MATCH, 0, &parents->no_match_id) &&
|
||||
create_match_result(database, pair.candidate_pair_id, feature_a.feature_set_id,
|
||||
feature_b.feature_set_id, 3, 0x53,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 8192,
|
||||
&parents->second_matched_id);
|
||||
return create_match_result(database, pair.candidate_pair_id,
|
||||
feature_a.feature_set_id, feature_b.feature_set_id,
|
||||
1, 0x51, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 9,
|
||||
&parents->matched_id) &&
|
||||
create_match_result(database, pair.candidate_pair_id,
|
||||
feature_a.feature_set_id, feature_b.feature_set_id,
|
||||
2, 0x52, LARDON3D_MATCH_RESULT_STATUS_NO_MATCH, 0,
|
||||
&parents->no_match_id) &&
|
||||
create_match_result(database, pair.candidate_pair_id,
|
||||
feature_a.feature_set_id, feature_b.feature_set_id,
|
||||
3, 0x53, LARDON3D_MATCH_RESULT_STATUS_MATCHED,
|
||||
8192, &parents->second_matched_id);
|
||||
}
|
||||
|
||||
static bool create_v11_database(const char *path) {
|
||||
Lardon3DProjectDb *database = NULL;
|
||||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
if (lardon3d_project_db_open(path, &database, error) != LARDON3D_PROJECT_DB_OK) {
|
||||
if (lardon3d_project_db_open(path, &database, error) !=
|
||||
LARDON3D_PROJECT_DB_OK) {
|
||||
return false;
|
||||
}
|
||||
lardon3d_project_db_close(database);
|
||||
return execute_sql(path,
|
||||
"PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;"
|
||||
"DROP TABLE IF EXISTS sparse_landmark_observations;"
|
||||
"DROP TABLE IF EXISTS sparse_landmarks;"
|
||||
"DROP TABLE IF EXISTS sparse_registered_images;"
|
||||
"DROP TABLE IF EXISTS sparse_reconstruction_components;"
|
||||
"DROP TABLE IF EXISTS sparse_reconstructions;"
|
||||
"DROP TABLE IF EXISTS sparse_calibration_scope_images;"
|
||||
"DROP TABLE IF EXISTS sparse_calibration_scopes;"
|
||||
"DROP TABLE IF EXISTS sparse_calibrations;"
|
||||
"DROP TABLE geometric_verifier_tasks;"
|
||||
"DROP TABLE geometric_verification_results;"
|
||||
"DROP TABLE track_observations;"
|
||||
|
|
@ -169,8 +192,9 @@ static bool create_v11_database(const char *path) {
|
|||
static bool test_model_api(const char *path) {
|
||||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
Lardon3DProjectDb *database = NULL;
|
||||
CHECK(lardon3d_project_db_open(path, &database, error) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_schema_version(database) == 15);
|
||||
CHECK(lardon3d_project_db_open(path, &database, error) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_schema_version(database) == 16);
|
||||
Parents parents;
|
||||
CHECK(create_parents(database, &parents));
|
||||
|
||||
|
|
@ -182,8 +206,9 @@ static bool test_model_api(const char *path) {
|
|||
double model[9] = {0.0, -1.0, 1e-300, 1e300, 4.0, 5.0, 6.0, 7.0, 8.0};
|
||||
Lardon3DProjectDbGeometricVerificationResult verified;
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 1,
|
||||
fingerprint_a, LARDON3D_GEOMETRIC_VERIFIED, 1, mask_a, sizeof(mask_a), model, 10,
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 1, fingerprint_a,
|
||||
LARDON3D_GEOMETRIC_VERIFIED, 1, mask_a, sizeof(mask_a), model, 10,
|
||||
&verified) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(verified.has_model && verified.inlier_mask_size == 2 &&
|
||||
memcmp(verified.model, model, sizeof(model)) == 0);
|
||||
|
|
@ -191,17 +216,20 @@ static bool test_model_api(const char *path) {
|
|||
unsigned char rejected_mask[2] = {0x03, 0x00};
|
||||
Lardon3DProjectDbGeometricVerificationResult rejected;
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 2,
|
||||
fingerprint_b, LARDON3D_GEOMETRIC_REJECTED, 2, rejected_mask,
|
||||
sizeof(rejected_mask), NULL, 11, &rejected) == LARDON3D_PROJECT_DB_OK);
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 2, fingerprint_b,
|
||||
LARDON3D_GEOMETRIC_REJECTED, 2, rejected_mask,
|
||||
sizeof(rejected_mask), NULL, 11,
|
||||
&rejected) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(!rejected.has_model && rejected.inlier_count == 2);
|
||||
|
||||
unsigned char zero_mask[2] = {0};
|
||||
Lardon3DProjectDbGeometricVerificationResult zero_inliers;
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3,
|
||||
fingerprint_b, LARDON3D_GEOMETRIC_REJECTED, 0, zero_mask, sizeof(zero_mask), NULL, 11,
|
||||
&zero_inliers) == LARDON3D_PROJECT_DB_OK);
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3, fingerprint_b,
|
||||
LARDON3D_GEOMETRIC_REJECTED, 0, zero_mask, sizeof(zero_mask), NULL,
|
||||
11, &zero_inliers) == LARDON3D_PROJECT_DB_OK);
|
||||
|
||||
Lardon3DProjectDbGeometricVerificationResult loaded;
|
||||
CHECK(lardon3d_project_db_load_geometric_verification_result(
|
||||
|
|
@ -211,17 +239,22 @@ static bool test_model_api(const char *path) {
|
|||
memcmp(loaded.model, model, sizeof(model)) == 0 &&
|
||||
memcmp(loaded.inlier_mask, mask_a, sizeof(mask_a)) == 0);
|
||||
CHECK(lardon3d_project_db_find_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 1,
|
||||
fingerprint_a, &loaded) == LARDON3D_PROJECT_DB_OK &&
|
||||
loaded.geometric_verification_result_id == verified.geometric_verification_result_id);
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 1, fingerprint_a,
|
||||
&loaded) == LARDON3D_PROJECT_DB_OK &&
|
||||
loaded.geometric_verification_result_id ==
|
||||
verified.geometric_verification_result_id);
|
||||
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 1,
|
||||
fingerprint_a, LARDON3D_GEOMETRIC_REJECTED, 2, rejected_mask,
|
||||
sizeof(rejected_mask), NULL, 12, &loaded) == LARDON3D_PROJECT_DB_CONSTRAINT);
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 1, fingerprint_a,
|
||||
LARDON3D_GEOMETRIC_REJECTED, 2, rejected_mask,
|
||||
sizeof(rejected_mask), NULL, 12,
|
||||
&loaded) == LARDON3D_PROJECT_DB_CONSTRAINT);
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.no_match_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 1,
|
||||
fingerprint_a, LARDON3D_GEOMETRIC_REJECTED, 0, mask_a, 1, NULL, 12,
|
||||
database, parents.no_match_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 1, fingerprint_a,
|
||||
LARDON3D_GEOMETRIC_REJECTED, 0, mask_a, 1, NULL, 12,
|
||||
&loaded) == LARDON3D_PROJECT_DB_CONSTRAINT);
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, UINT64_C(9223372036854775807),
|
||||
|
|
@ -231,57 +264,68 @@ static bool test_model_api(const char *path) {
|
|||
|
||||
unsigned char bad_padding[2] = {0x01, 0x80};
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3,
|
||||
fingerprint_a, LARDON3D_GEOMETRIC_REJECTED, 1, bad_padding, sizeof(bad_padding),
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3, fingerprint_a,
|
||||
LARDON3D_GEOMETRIC_REJECTED, 1, bad_padding, sizeof(bad_padding),
|
||||
NULL, 12, &loaded) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3,
|
||||
fingerprint_a, LARDON3D_GEOMETRIC_REJECTED, 2, mask_a, sizeof(mask_a), NULL, 12,
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3, fingerprint_a,
|
||||
LARDON3D_GEOMETRIC_REJECTED, 2, mask_a, sizeof(mask_a), NULL, 12,
|
||||
&loaded) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
unsigned char all_inliers[2] = {0xff, 0x01};
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 4,
|
||||
fingerprint_a, LARDON3D_GEOMETRIC_REJECTED, 9, all_inliers, sizeof(all_inliers), NULL,
|
||||
12, &loaded) == LARDON3D_PROJECT_DB_OK);
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 4, fingerprint_a,
|
||||
LARDON3D_GEOMETRIC_REJECTED, 9, all_inliers, sizeof(all_inliers),
|
||||
NULL, 12, &loaded) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 5,
|
||||
fingerprint_a, LARDON3D_GEOMETRIC_REJECTED, 10, all_inliers, sizeof(all_inliers), NULL,
|
||||
12, &loaded) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 5, fingerprint_a,
|
||||
LARDON3D_GEOMETRIC_REJECTED, 10, all_inliers, sizeof(all_inliers),
|
||||
NULL, 12, &loaded) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3,
|
||||
fingerprint_a, LARDON3D_GEOMETRIC_VERIFIED, 1, mask_a, sizeof(mask_a), NULL, 12,
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3, fingerprint_a,
|
||||
LARDON3D_GEOMETRIC_VERIFIED, 1, mask_a, sizeof(mask_a), NULL, 12,
|
||||
&loaded) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3,
|
||||
fingerprint_a, LARDON3D_GEOMETRIC_REJECTED, 1, mask_a, sizeof(mask_a), model, 12,
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3, fingerprint_a,
|
||||
LARDON3D_GEOMETRIC_REJECTED, 1, mask_a, sizeof(mask_a), model, 12,
|
||||
&loaded) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
double bad_model[9] = {0};
|
||||
bad_model[4] = NAN;
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3,
|
||||
fingerprint_a, LARDON3D_GEOMETRIC_VERIFIED, 1, mask_a, sizeof(mask_a), bad_model, 12,
|
||||
&loaded) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3, fingerprint_a,
|
||||
LARDON3D_GEOMETRIC_VERIFIED, 1, mask_a, sizeof(mask_a), bad_model,
|
||||
12, &loaded) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
bad_model[4] = INFINITY;
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3,
|
||||
fingerprint_a, LARDON3D_GEOMETRIC_VERIFIED, 1, mask_a, sizeof(mask_a), bad_model, 12,
|
||||
&loaded) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 3, fingerprint_a,
|
||||
LARDON3D_GEOMETRIC_VERIFIED, 1, mask_a, sizeof(mask_a), bad_model,
|
||||
12, &loaded) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
unsigned char max_mask[LARDON3D_PROJECT_DB_INLIER_MASK_MAX] = {0};
|
||||
max_mask[0] = 0x01;
|
||||
max_mask[1023] = 0x80;
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.second_matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 1,
|
||||
fingerprint_a, LARDON3D_GEOMETRIC_REJECTED, 2, max_mask, sizeof(max_mask), NULL, 13,
|
||||
&loaded) == LARDON3D_PROJECT_DB_OK);
|
||||
database, parents.second_matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 1, fingerprint_a,
|
||||
LARDON3D_GEOMETRIC_REJECTED, 2, max_mask, sizeof(max_mask), NULL,
|
||||
13, &loaded) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(loaded.inlier_mask_size == 1024 && loaded.inlier_count == 2);
|
||||
|
||||
static const uint32_t boundary_counts[] = {1, 2, 7, 8, 9, 63, 64, 65, 8191};
|
||||
for (size_t boundary_index = 0;
|
||||
boundary_index < sizeof(boundary_counts) / sizeof(boundary_counts[0]); ++boundary_index) {
|
||||
boundary_index < sizeof(boundary_counts) / sizeof(boundary_counts[0]);
|
||||
++boundary_index) {
|
||||
uint32_t match_count = boundary_counts[boundary_index];
|
||||
uint64_t parent_id = 0;
|
||||
CHECK(create_match_result(database, parents.candidate_pair_id, parents.feature_set_id_a,
|
||||
CHECK(create_match_result(
|
||||
database, parents.candidate_pair_id, parents.feature_set_id_a,
|
||||
parents.feature_set_id_b, (uint32_t)(10U + boundary_index),
|
||||
(unsigned char)(0x60U + boundary_index),
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, match_count, &parent_id));
|
||||
|
|
@ -294,33 +338,38 @@ static bool test_model_api(const char *path) {
|
|||
sizeof(boundary_fingerprint));
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parent_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 1,
|
||||
boundary_fingerprint, LARDON3D_GEOMETRIC_REJECTED, 1, boundary_mask,
|
||||
boundary_size, NULL, 20 + (int64_t)boundary_index, &loaded) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
boundary_fingerprint, LARDON3D_GEOMETRIC_REJECTED, 1,
|
||||
boundary_mask, boundary_size, NULL, 20 + (int64_t)boundary_index,
|
||||
&loaded) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(loaded.inlier_mask_size == boundary_size &&
|
||||
loaded.inlier_mask[last_index / 8U] == (unsigned char)(1U << (last_index % 8U)));
|
||||
loaded.inlier_mask[last_index / 8U] ==
|
||||
(unsigned char)(1U << (last_index % 8U)));
|
||||
}
|
||||
|
||||
Lardon3DProjectDbGeometricVerificationResult page[2];
|
||||
size_t count = 0;
|
||||
CHECK(lardon3d_project_db_list_geometric_verification_results(
|
||||
database, parents.matched_id, 0, page, 1, &count) == LARDON3D_PROJECT_DB_OK &&
|
||||
database, parents.matched_id, 0, page, 1, &count) ==
|
||||
LARDON3D_PROJECT_DB_OK &&
|
||||
count == 1);
|
||||
uint64_t cursor = page[0].geometric_verification_result_id;
|
||||
CHECK(lardon3d_project_db_list_geometric_verification_results(
|
||||
database, parents.matched_id, cursor, page, 2, &count) == LARDON3D_PROJECT_DB_OK &&
|
||||
database, parents.matched_id, cursor, page, 2, &count) ==
|
||||
LARDON3D_PROJECT_DB_OK &&
|
||||
count == 2 && page[0].geometric_verification_result_id > cursor);
|
||||
CHECK(lardon3d_project_db_list_geometric_verification_results(
|
||||
database, parents.matched_id, 0, page,
|
||||
LARDON3D_PROJECT_DB_GEOMETRIC_RESULT_PAGE_MAX + 1U, &count) ==
|
||||
LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
LARDON3D_PROJECT_DB_GEOMETRIC_RESULT_PAGE_MAX + 1U,
|
||||
&count) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
uint64_t verified_id = verified.geometric_verification_result_id;
|
||||
lardon3d_project_db_close(database);
|
||||
database = NULL;
|
||||
CHECK(lardon3d_project_db_open(path, &database, error) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_load_geometric_verification_result(database, verified_id, &loaded) ==
|
||||
LARDON3D_PROJECT_DB_OK && memcmp(loaded.model, model, sizeof(model)) == 0);
|
||||
CHECK(lardon3d_project_db_open(path, &database, error) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_load_geometric_verification_result(
|
||||
database, verified_id, &loaded) == LARDON3D_PROJECT_DB_OK &&
|
||||
memcmp(loaded.model, model, sizeof(model)) == 0);
|
||||
lardon3d_project_db_close(database);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -329,12 +378,13 @@ static bool loader_rejects_after_sql(const char *path, const char *sql) {
|
|||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
Lardon3DProjectDb *database = NULL;
|
||||
if (!execute_sql(path, sql) ||
|
||||
lardon3d_project_db_open(path, &database, error) != LARDON3D_PROJECT_DB_OK) {
|
||||
lardon3d_project_db_open(path, &database, error) !=
|
||||
LARDON3D_PROJECT_DB_OK) {
|
||||
return false;
|
||||
}
|
||||
Lardon3DProjectDbGeometricVerificationResult result;
|
||||
bool rejected = lardon3d_project_db_load_geometric_verification_result(database, 1, &result) ==
|
||||
LARDON3D_PROJECT_DB_CORRUPT;
|
||||
bool rejected = lardon3d_project_db_load_geometric_verification_result(
|
||||
database, 1, &result) == LARDON3D_PROJECT_DB_CORRUPT;
|
||||
lardon3d_project_db_close(database);
|
||||
return rejected;
|
||||
}
|
||||
|
|
@ -342,37 +392,55 @@ static bool loader_rejects_after_sql(const char *path, const char *sql) {
|
|||
static bool test_corruption(const char *path) {
|
||||
static const char reset[] =
|
||||
"PRAGMA ignore_check_constraints=ON;"
|
||||
"UPDATE geometric_verification_results SET verifier_kind=1,verifier_version=1,"
|
||||
"parameter_fingerprint=zeroblob(32),status=2,inlier_count=1,inlier_mask=x'0100',"
|
||||
"UPDATE geometric_verification_results SET "
|
||||
"verifier_kind=1,verifier_version=1,"
|
||||
"parameter_fingerprint=zeroblob(32),status=2,inlier_count=1,inlier_mask="
|
||||
"x'0100',"
|
||||
"model_m00=0,model_m01=0,model_m02=0,model_m10=0,model_m11=0,model_m12=0,"
|
||||
"model_m20=0,model_m21=0,model_m22=0 WHERE geometric_verification_result_id=1;";
|
||||
"model_m20=0,model_m21=0,model_m22=0 WHERE "
|
||||
"geometric_verification_result_id=1;";
|
||||
CHECK(execute_sql(path, reset));
|
||||
|
||||
static const char *corruptions[] = {
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE geometric_verification_results "
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE "
|
||||
"geometric_verification_results "
|
||||
"SET verifier_kind=9 WHERE geometric_verification_result_id=1;",
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE geometric_verification_results "
|
||||
"SET verifier_kind=1,verifier_version=0 WHERE geometric_verification_result_id=1;",
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE geometric_verification_results "
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE "
|
||||
"geometric_verification_results "
|
||||
"SET verifier_kind=1,verifier_version=0 WHERE "
|
||||
"geometric_verification_result_id=1;",
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE "
|
||||
"geometric_verification_results "
|
||||
"SET verifier_version=1,parameter_fingerprint=x'01' "
|
||||
"WHERE geometric_verification_result_id=1;",
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE geometric_verification_results "
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE "
|
||||
"geometric_verification_results "
|
||||
"SET parameter_fingerprint=zeroblob(32),status=9 "
|
||||
"WHERE geometric_verification_result_id=1;",
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE geometric_verification_results "
|
||||
"SET status=2,inlier_count=9000 WHERE geometric_verification_result_id=1;",
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE geometric_verification_results "
|
||||
"SET inlier_count=1,inlier_mask=x'03' WHERE geometric_verification_result_id=1;",
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE geometric_verification_results "
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE "
|
||||
"geometric_verification_results "
|
||||
"SET status=2,inlier_count=9000 WHERE "
|
||||
"geometric_verification_result_id=1;",
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE "
|
||||
"geometric_verification_results "
|
||||
"SET inlier_count=1,inlier_mask=x'03' WHERE "
|
||||
"geometric_verification_result_id=1;",
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE "
|
||||
"geometric_verification_results "
|
||||
"SET inlier_mask=x'0180' WHERE geometric_verification_result_id=1;",
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE geometric_verification_results "
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE "
|
||||
"geometric_verification_results "
|
||||
"SET inlier_mask=x'0300' WHERE geometric_verification_result_id=1;",
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE geometric_verification_results "
|
||||
"SET inlier_mask=x'0100',model_m00=NULL WHERE geometric_verification_result_id=1;",
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE geometric_verification_results "
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE "
|
||||
"geometric_verification_results "
|
||||
"SET inlier_mask=x'0100',model_m00=NULL WHERE "
|
||||
"geometric_verification_result_id=1;",
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE "
|
||||
"geometric_verification_results "
|
||||
"SET model_m00=1e999 WHERE geometric_verification_result_id=1;",
|
||||
};
|
||||
for (size_t index = 0; index < sizeof(corruptions) / sizeof(corruptions[0]); ++index) {
|
||||
for (size_t index = 0; index < sizeof(corruptions) / sizeof(corruptions[0]);
|
||||
++index) {
|
||||
CHECK(execute_sql(path, reset));
|
||||
CHECK(loader_rejects_after_sql(path, corruptions[index]));
|
||||
}
|
||||
|
|
@ -380,15 +448,17 @@ static bool test_corruption(const char *path) {
|
|||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
Lardon3DProjectDb *database = NULL;
|
||||
CHECK(execute_sql(path, reset));
|
||||
CHECK(lardon3d_project_db_open(path, &database, error) == LARDON3D_PROJECT_DB_OK);
|
||||
Lardon3DProjectDbGeometricVerificationResult result;
|
||||
CHECK(lardon3d_project_db_load_geometric_verification_result(database, 1, &result) ==
|
||||
CHECK(lardon3d_project_db_open(path, &database, error) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
Lardon3DProjectDbGeometricVerificationResult result;
|
||||
CHECK(lardon3d_project_db_load_geometric_verification_result(
|
||||
database, 1, &result) == LARDON3D_PROJECT_DB_OK);
|
||||
uint64_t parent_id = result.match_result_id;
|
||||
lardon3d_project_db_close(database);
|
||||
char parent_corruption[512];
|
||||
CHECK(snprintf(parent_corruption, sizeof(parent_corruption),
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE match_results SET result_status=0 "
|
||||
"PRAGMA ignore_check_constraints=ON;UPDATE match_results SET "
|
||||
"result_status=0 "
|
||||
"WHERE match_result_id=%llu;",
|
||||
(unsigned long long)parent_id) > 0);
|
||||
CHECK(loader_rejects_after_sql(path, parent_corruption));
|
||||
|
|
@ -399,46 +469,60 @@ static bool test_migration(const char *v11_path, const char *failed_path) {
|
|||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
Lardon3DProjectDb *database = NULL;
|
||||
CHECK(create_v11_database(v11_path));
|
||||
CHECK(query_integer(v11_path, "SELECT value FROM metadata WHERE key='schema_version'", 11));
|
||||
CHECK(query_integer(v11_path,
|
||||
CHECK(query_integer(
|
||||
v11_path, "SELECT value FROM metadata WHERE key='schema_version'", 11));
|
||||
CHECK(
|
||||
query_integer(v11_path,
|
||||
"SELECT count(*) FROM sqlite_master WHERE type='table' AND "
|
||||
"name='geometric_verification_results'",
|
||||
0));
|
||||
CHECK(lardon3d_project_db_open(v11_path, &database, error) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_schema_version(database) == 15);
|
||||
CHECK(lardon3d_project_db_open(v11_path, &database, error) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_schema_version(database) == 16);
|
||||
Parents parents;
|
||||
CHECK(create_parents(database, &parents));
|
||||
unsigned char fingerprint[32] = {0x91};
|
||||
unsigned char mask[2] = {0x01, 0x00};
|
||||
Lardon3DProjectDbGeometricVerificationResult migrated_result;
|
||||
CHECK(lardon3d_project_db_create_geometric_verification_result(
|
||||
database, parents.matched_id, LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 1,
|
||||
fingerprint, LARDON3D_GEOMETRIC_REJECTED, 1, mask, sizeof(mask), NULL, 30,
|
||||
database, parents.matched_id,
|
||||
LARDON3D_GEOMETRIC_VERIFIER_FUNDAMENTAL, 1, fingerprint,
|
||||
LARDON3D_GEOMETRIC_REJECTED, 1, mask, sizeof(mask), NULL, 30,
|
||||
&migrated_result) == LARDON3D_PROJECT_DB_OK);
|
||||
uint64_t migrated_result_id = migrated_result.geometric_verification_result_id;
|
||||
uint64_t migrated_result_id =
|
||||
migrated_result.geometric_verification_result_id;
|
||||
lardon3d_project_db_close(database);
|
||||
CHECK(query_integer(v11_path, "SELECT value FROM metadata WHERE key='schema_version'", 15));
|
||||
CHECK(query_integer(v11_path,
|
||||
CHECK(query_integer(
|
||||
v11_path, "SELECT value FROM metadata WHERE key='schema_version'", 16));
|
||||
CHECK(
|
||||
query_integer(v11_path,
|
||||
"SELECT count(*) FROM sqlite_master WHERE type='index' AND "
|
||||
"name='geometric_verification_results_parent_idx'",
|
||||
1));
|
||||
CHECK(lardon3d_project_db_open(v11_path, &database, error) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_open(v11_path, &database, error) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_load_geometric_verification_result(
|
||||
database, migrated_result_id, &migrated_result) == LARDON3D_PROJECT_DB_OK &&
|
||||
database, migrated_result_id, &migrated_result) ==
|
||||
LARDON3D_PROJECT_DB_OK &&
|
||||
migrated_result.match_result_id == parents.matched_id);
|
||||
lardon3d_project_db_close(database);
|
||||
|
||||
CHECK(create_v11_database(failed_path));
|
||||
CHECK(setenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V12", "1", 1) == 0);
|
||||
CHECK(lardon3d_project_db_open(failed_path, &database, error) == LARDON3D_PROJECT_DB_IO_ERROR);
|
||||
CHECK(lardon3d_project_db_open(failed_path, &database, error) ==
|
||||
LARDON3D_PROJECT_DB_IO_ERROR);
|
||||
CHECK(unsetenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V12") == 0);
|
||||
CHECK(query_integer(failed_path, "SELECT value FROM metadata WHERE key='schema_version'", 11));
|
||||
CHECK(query_integer(failed_path,
|
||||
"SELECT value FROM metadata WHERE key='schema_version'",
|
||||
11));
|
||||
CHECK(
|
||||
query_integer(failed_path,
|
||||
"SELECT count(*) FROM sqlite_master WHERE type='table' AND "
|
||||
"name='geometric_verification_results'",
|
||||
0));
|
||||
CHECK(lardon3d_project_db_open(failed_path, &database, error) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_schema_version(database) == 15);
|
||||
CHECK(lardon3d_project_db_open(failed_path, &database, error) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_schema_version(database) == 16);
|
||||
lardon3d_project_db_close(database);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -449,9 +533,11 @@ static bool run_test(void) {
|
|||
char database_path[512];
|
||||
char v11_path[512];
|
||||
char failed_path[512];
|
||||
CHECK(snprintf(database_path, sizeof(database_path), "%s/project.db", directory) > 0);
|
||||
CHECK(snprintf(database_path, sizeof(database_path), "%s/project.db",
|
||||
directory) > 0);
|
||||
CHECK(snprintf(v11_path, sizeof(v11_path), "%s/v11.db", directory) > 0);
|
||||
CHECK(snprintf(failed_path, sizeof(failed_path), "%s/failed-v12.db", directory) > 0);
|
||||
CHECK(snprintf(failed_path, sizeof(failed_path), "%s/failed-v12.db",
|
||||
directory) > 0);
|
||||
CHECK(test_model_api(database_path));
|
||||
CHECK(test_corruption(database_path));
|
||||
CHECK(test_migration(v11_path, failed_path));
|
||||
|
|
@ -462,6 +548,4 @@ static bool run_test(void) {
|
|||
return true;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
return run_test() ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
int main(void) { return run_test() ? EXIT_SUCCESS : EXIT_FAILURE; }
|
||||
|
|
|
|||
|
|
@ -332,6 +332,14 @@ static bool run_task_test(void) {
|
|||
CHECK(exec_sql(
|
||||
database_path,
|
||||
"PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;"
|
||||
"DROP TABLE IF EXISTS sparse_landmark_observations;"
|
||||
"DROP TABLE IF EXISTS sparse_landmarks;"
|
||||
"DROP TABLE IF EXISTS sparse_registered_images;"
|
||||
"DROP TABLE IF EXISTS sparse_reconstruction_components;"
|
||||
"DROP TABLE IF EXISTS sparse_reconstructions;"
|
||||
"DROP TABLE IF EXISTS sparse_calibration_scope_images;"
|
||||
"DROP TABLE IF EXISTS sparse_calibration_scopes;"
|
||||
"DROP TABLE IF EXISTS sparse_calibrations;"
|
||||
"DROP TABLE geometric_verifier_tasks;"
|
||||
"DROP TABLE track_observations;"
|
||||
"DROP TABLE tracks;"
|
||||
|
|
@ -353,7 +361,7 @@ static bool run_task_test(void) {
|
|||
0));
|
||||
CHECK(lardon3d_project_db_open(database_path, &database, error) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_schema_version(database) == 15);
|
||||
CHECK(lardon3d_project_db_schema_version(database) == 16);
|
||||
lardon3d_project_db_close(database);
|
||||
CHECK(remove_tree(root));
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -19,10 +19,12 @@
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
static bool query_integer(const char *path, const char *sql, sqlite3_int64 expected) {
|
||||
static bool query_integer(const char *path, const char *sql,
|
||||
sqlite3_int64 expected) {
|
||||
sqlite3 *connection = NULL;
|
||||
sqlite3_stmt *statement = NULL;
|
||||
if (sqlite3_open_v2(path, &connection, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK ||
|
||||
if (sqlite3_open_v2(path, &connection, SQLITE_OPEN_READONLY, NULL) !=
|
||||
SQLITE_OK ||
|
||||
sqlite3_prepare_v2(connection, sql, -1, &statement, NULL) != SQLITE_OK) {
|
||||
if (statement) {
|
||||
(void)sqlite3_finalize(statement);
|
||||
|
|
@ -35,19 +37,30 @@ static bool query_integer(const char *path, const char *sql, sqlite3_int64 expec
|
|||
bool matches = sqlite3_step(statement) == SQLITE_ROW &&
|
||||
sqlite3_column_int64(statement, 0) == expected &&
|
||||
sqlite3_step(statement) == SQLITE_DONE;
|
||||
return sqlite3_finalize(statement) == SQLITE_OK && sqlite3_close(connection) == SQLITE_OK &&
|
||||
matches;
|
||||
return sqlite3_finalize(statement) == SQLITE_OK &&
|
||||
sqlite3_close(connection) == SQLITE_OK && matches;
|
||||
}
|
||||
|
||||
static bool create_v9_database(const char *path) {
|
||||
Lardon3DProjectDb *database = NULL;
|
||||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
if (lardon3d_project_db_open(path, &database, error) != LARDON3D_PROJECT_DB_OK) return false;
|
||||
if (lardon3d_project_db_open(path, &database, error) !=
|
||||
LARDON3D_PROJECT_DB_OK)
|
||||
return false;
|
||||
lardon3d_project_db_close(database);
|
||||
sqlite3 *connection = NULL;
|
||||
if (sqlite3_open(path, &connection) != SQLITE_OK) return false;
|
||||
if (sqlite3_open(path, &connection) != SQLITE_OK)
|
||||
return false;
|
||||
static const char sql[] =
|
||||
"PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;"
|
||||
"DROP TABLE IF EXISTS sparse_landmark_observations;"
|
||||
"DROP TABLE IF EXISTS sparse_landmarks;"
|
||||
"DROP TABLE IF EXISTS sparse_registered_images;"
|
||||
"DROP TABLE IF EXISTS sparse_reconstruction_components;"
|
||||
"DROP TABLE IF EXISTS sparse_reconstructions;"
|
||||
"DROP TABLE IF EXISTS sparse_calibration_scope_images;"
|
||||
"DROP TABLE IF EXISTS sparse_calibration_scopes;"
|
||||
"DROP TABLE IF EXISTS sparse_calibrations;"
|
||||
"DROP TABLE geometric_verifier_tasks;"
|
||||
"DROP TABLE geometric_verification_results;"
|
||||
"DROP TABLE matcher_tasks;"
|
||||
|
|
@ -55,12 +68,14 @@ static bool create_v9_database(const char *path) {
|
|||
"DROP TABLE track_observations;"
|
||||
"DROP TABLE tracks;"
|
||||
"DROP TABLE track_sets;DROP TABLE track_builder_tasks;"
|
||||
"UPDATE metadata SET value=9 WHERE key='schema_version';COMMIT;PRAGMA foreign_keys=ON;";
|
||||
"UPDATE metadata SET value=9 WHERE key='schema_version';COMMIT;PRAGMA "
|
||||
"foreign_keys=ON;";
|
||||
bool ok = sqlite3_exec(connection, sql, NULL, NULL, NULL) == SQLITE_OK;
|
||||
return sqlite3_close(connection) == SQLITE_OK && ok;
|
||||
}
|
||||
|
||||
static void asset_path_for_hash(const unsigned char hash[LARDON3D_PROJECT_DB_SHA256_SIZE],
|
||||
static void
|
||||
asset_path_for_hash(const unsigned char hash[LARDON3D_PROJECT_DB_SHA256_SIZE],
|
||||
char path[LARDON3D_PROJECT_DB_PATH_CAPACITY]) {
|
||||
static const char digits[] = "0123456789abcdef";
|
||||
char hex[65];
|
||||
|
|
@ -69,8 +84,8 @@ static void asset_path_for_hash(const unsigned char hash[LARDON3D_PROJECT_DB_SHA
|
|||
hex[i * 2 + 1] = digits[hash[i] & 15U];
|
||||
}
|
||||
hex[64] = '\0';
|
||||
(void)snprintf(path, LARDON3D_PROJECT_DB_PATH_CAPACITY, "assets/images/%c%c/%s", hex[0], hex[1],
|
||||
hex);
|
||||
(void)snprintf(path, LARDON3D_PROJECT_DB_PATH_CAPACITY,
|
||||
"assets/images/%c%c/%s", hex[0], hex[1], hex);
|
||||
}
|
||||
|
||||
static void feature_asset_path_for_hash(
|
||||
|
|
@ -83,22 +98,25 @@ static void feature_asset_path_for_hash(
|
|||
hex[i * 2 + 1] = digits[hash[i] & 15U];
|
||||
}
|
||||
hex[64] = '\0';
|
||||
(void)snprintf(path, LARDON3D_PROJECT_DB_PATH_CAPACITY, "assets/features/%c%c/%s", hex[0], hex[1],
|
||||
hex);
|
||||
(void)snprintf(path, LARDON3D_PROJECT_DB_PATH_CAPACITY,
|
||||
"assets/features/%c%c/%s", hex[0], hex[1], hex);
|
||||
}
|
||||
|
||||
static bool run_test(void) {
|
||||
char directory[] = "/tmp/lardon3d-match-result-XXXXXX";
|
||||
CHECK(mkdtemp(directory));
|
||||
char database_path[512], v9_path[512], failed_v10_path[512];
|
||||
CHECK(snprintf(database_path, sizeof(database_path), "%s/project.db", directory) > 0);
|
||||
CHECK(snprintf(database_path, sizeof(database_path), "%s/project.db",
|
||||
directory) > 0);
|
||||
CHECK(snprintf(v9_path, sizeof(v9_path), "%s/v9.db", directory) > 0);
|
||||
CHECK(snprintf(failed_v10_path, sizeof(failed_v10_path), "%s/failed-v10.db", directory) > 0);
|
||||
CHECK(snprintf(failed_v10_path, sizeof(failed_v10_path), "%s/failed-v10.db",
|
||||
directory) > 0);
|
||||
|
||||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
Lardon3DProjectDb *database = NULL;
|
||||
CHECK(lardon3d_project_db_open(database_path, &database, error) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(database && lardon3d_project_db_schema_version(database) == 15);
|
||||
CHECK(lardon3d_project_db_open(database_path, &database, error) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(database && lardon3d_project_db_schema_version(database) == 16);
|
||||
|
||||
Lardon3DProjectDbScanSet scanset;
|
||||
CHECK(lardon3d_project_db_create_scanset(database, "Match-test", &scanset) ==
|
||||
|
|
@ -109,21 +127,24 @@ static bool run_test(void) {
|
|||
asset_path_for_hash(hash_a, path_a);
|
||||
Lardon3DProjectDbImageRegisterStatus identity_status;
|
||||
Lardon3DProjectDbImage image_a;
|
||||
CHECK(lardon3d_project_db_register_image(database, scanset.scanset_id, hash_a, path_a, 1,
|
||||
"a.jpg", "/src/a.jpg", 0, 1, &identity_status,
|
||||
CHECK(lardon3d_project_db_register_image(database, scanset.scanset_id, hash_a,
|
||||
path_a, 1, "a.jpg", "/src/a.jpg", 0,
|
||||
1, &identity_status,
|
||||
&image_a) == LARDON3D_PROJECT_DB_OK);
|
||||
|
||||
unsigned char hash_b[LARDON3D_PROJECT_DB_SHA256_SIZE] = {0xBB};
|
||||
char path_b[LARDON3D_PROJECT_DB_PATH_CAPACITY];
|
||||
asset_path_for_hash(hash_b, path_b);
|
||||
Lardon3DProjectDbImage image_b;
|
||||
CHECK(lardon3d_project_db_register_image(database, scanset.scanset_id, hash_b, path_b, 1,
|
||||
"b.jpg", "/src/b.jpg", 0, 2, &identity_status,
|
||||
CHECK(lardon3d_project_db_register_image(database, scanset.scanset_id, hash_b,
|
||||
path_b, 1, "b.jpg", "/src/b.jpg", 0,
|
||||
2, &identity_status,
|
||||
&image_b) == LARDON3D_PROJECT_DB_OK);
|
||||
|
||||
Lardon3DProjectDbCandidatePair pair;
|
||||
CHECK(lardon3d_project_db_create_candidate_pair(database, image_a.image_id, image_b.image_id, 10,
|
||||
&pair) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_create_candidate_pair(
|
||||
database, image_a.image_id, image_b.image_id, 10, &pair) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(pair.candidate_pair_id > 0);
|
||||
|
||||
/* Create feature sets for foreign key references */
|
||||
|
|
@ -132,8 +153,9 @@ static bool run_test(void) {
|
|||
feature_asset_path_for_hash(feature_hash_a, feature_path_a);
|
||||
Lardon3DProjectDbFeatureSet fs_a;
|
||||
CHECK(lardon3d_project_db_register_feature_set(
|
||||
database, image_a.image_id, "orb", 1, feature_hash_a, hash_a, 100, 1, 32,
|
||||
feature_hash_a, feature_path_a, 128, LARDON3D_DB_FEATURE_ASSET_DURABLE, 0, 10,
|
||||
database, image_a.image_id, "orb", 1, feature_hash_a, hash_a, 100,
|
||||
1, 32, feature_hash_a, feature_path_a, 128,
|
||||
LARDON3D_DB_FEATURE_ASSET_DURABLE, 0, 10,
|
||||
&fs_a) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(fs_a.feature_set_id > 0 && fs_a.image_id == image_a.image_id);
|
||||
|
||||
|
|
@ -142,10 +164,10 @@ static bool run_test(void) {
|
|||
feature_asset_path_for_hash(feature_hash_b, feature_path_b);
|
||||
Lardon3DProjectDbFeatureSet fs_b;
|
||||
CHECK(lardon3d_project_db_register_feature_set(
|
||||
database, image_b.image_id, "sift", 1, feature_hash_b, hash_b, 100, 2, 128,
|
||||
feature_hash_b, feature_path_b, 256,
|
||||
LARDON3D_DB_FEATURE_ASSET_PUBLISHED_NOT_DURABLE, 0, 20, &fs_b) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
database, image_b.image_id, "sift", 1, feature_hash_b, hash_b, 100,
|
||||
2, 128, feature_hash_b, feature_path_b, 256,
|
||||
LARDON3D_DB_FEATURE_ASSET_PUBLISHED_NOT_DURABLE, 0, 20,
|
||||
&fs_b) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(fs_b.feature_set_id > 0 && fs_b.image_id == image_b.image_id);
|
||||
|
||||
/* Create MATCHED result with inliers */
|
||||
|
|
@ -156,14 +178,16 @@ static bool run_test(void) {
|
|||
memset(match_hash, 0x5A, sizeof(match_hash));
|
||||
const char *match_path = "assets/matches/5a/asset";
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 1, fp1, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30,
|
||||
match_hash, match_path, 392, 100,
|
||||
&result) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(result.match_result_id > 0 && result.candidate_pair_id == pair.candidate_pair_id &&
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 1, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30, match_hash, match_path,
|
||||
392, 100, &result) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(result.match_result_id > 0 &&
|
||||
result.candidate_pair_id == pair.candidate_pair_id &&
|
||||
result.feature_set_id_a == fs_a.feature_set_id &&
|
||||
result.feature_set_id_b == fs_b.feature_set_id &&
|
||||
strcmp(result.matcher_kind, "orb_bf") == 0 && result.matcher_version == 1 &&
|
||||
strcmp(result.matcher_kind, "orb_bf") == 0 &&
|
||||
result.matcher_version == 1 &&
|
||||
memcmp(result.parameter_fingerprint, fp1, 32) == 0 &&
|
||||
result.result_status == LARDON3D_MATCH_RESULT_STATUS_MATCHED &&
|
||||
result.match_count == 30 && result.created_at == 100);
|
||||
|
|
@ -173,9 +197,9 @@ static bool run_test(void) {
|
|||
unsigned char fp2[32];
|
||||
memset(fp2, 0x02, 32);
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 2, fp2, LARDON3D_MATCH_RESULT_STATUS_NO_MATCH, 0,
|
||||
NULL, NULL, 0, 200,
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 2, fp2,
|
||||
LARDON3D_MATCH_RESULT_STATUS_NO_MATCH, 0, NULL, NULL, 0, 200,
|
||||
&result) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(result.result_status == LARDON3D_MATCH_RESULT_STATUS_NO_MATCH &&
|
||||
result.match_count == 0 && !result.has_match_asset);
|
||||
|
|
@ -183,10 +207,10 @@ static bool run_test(void) {
|
|||
|
||||
/* Duplicate: same CP + same matcher + same params + same FS → CONSTRAINT */
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 1, fp1, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 10,
|
||||
match_hash, match_path, 152, 500,
|
||||
&result) == LARDON3D_PROJECT_DB_CONSTRAINT);
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 1, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 10, match_hash, match_path,
|
||||
152, 500, &result) == LARDON3D_PROJECT_DB_CONSTRAINT);
|
||||
|
||||
/* Create additional Feature Sets for distinct-FS and inverted-ID tests */
|
||||
unsigned char feature_hash_a2[LARDON3D_PROJECT_DB_SHA256_SIZE] = {0x33};
|
||||
|
|
@ -197,7 +221,8 @@ static bool run_test(void) {
|
|||
memset(fs_a2_fp, 0xAA, 32);
|
||||
CHECK(lardon3d_project_db_register_feature_set(
|
||||
database, image_a.image_id, "orb", 2, fs_a2_fp, hash_a, 200, 1, 32,
|
||||
feature_hash_a2, feature_path_a2, 256, LARDON3D_DB_FEATURE_ASSET_DURABLE, 0, 30,
|
||||
feature_hash_a2, feature_path_a2, 256,
|
||||
LARDON3D_DB_FEATURE_ASSET_DURABLE, 0, 30,
|
||||
&fs_a2) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(fs_a2.feature_set_id > 0 && fs_a2.image_id == image_a.image_id);
|
||||
|
||||
|
|
@ -208,10 +233,10 @@ static bool run_test(void) {
|
|||
unsigned char fs_b2_fp[32];
|
||||
memset(fs_b2_fp, 0xBB, 32);
|
||||
CHECK(lardon3d_project_db_register_feature_set(
|
||||
database, image_b.image_id, "sift", 2, fs_b2_fp, hash_b, 200, 2, 128,
|
||||
feature_hash_b2, feature_path_b2, 512,
|
||||
LARDON3D_DB_FEATURE_ASSET_PUBLISHED_NOT_DURABLE, 0, 40, &fs_b2) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
database, image_b.image_id, "sift", 2, fs_b2_fp, hash_b, 200, 2,
|
||||
128, feature_hash_b2, feature_path_b2, 512,
|
||||
LARDON3D_DB_FEATURE_ASSET_PUBLISHED_NOT_DURABLE, 0, 40,
|
||||
&fs_b2) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(fs_b2.feature_set_id > 0 && fs_b2.image_id == image_b.image_id);
|
||||
|
||||
/* Same CP + same matcher + same params + different FS → distinct */
|
||||
|
|
@ -219,41 +244,46 @@ static bool run_test(void) {
|
|||
memset(fp5, 0x05, 32);
|
||||
Lardon3DProjectDbMatchResult distinct_fs_result;
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a2.feature_set_id, fs_b2.feature_set_id,
|
||||
"orb_bf", 1, fp5, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 20,
|
||||
match_hash, match_path, 272, 400,
|
||||
&distinct_fs_result) == LARDON3D_PROJECT_DB_OK);
|
||||
database, pair.candidate_pair_id, fs_a2.feature_set_id,
|
||||
fs_b2.feature_set_id, "orb_bf", 1, fp5,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 20, match_hash, match_path,
|
||||
272, 400, &distinct_fs_result) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(distinct_fs_result.feature_set_id_a == fs_a2.feature_set_id &&
|
||||
distinct_fs_result.feature_set_id_b == fs_b2.feature_set_id);
|
||||
uint64_t distinct_id = distinct_fs_result.match_result_id;
|
||||
CHECK(distinct_id != first_id);
|
||||
|
||||
/* feature_set_id_a > feature_set_id_b but correct ownership → valid */
|
||||
/* Register image_c, image_d; create FS for d first, then c → fs_c.id > fs_d.id */
|
||||
/* Register image_c, image_d; create FS for d first, then c → fs_c.id >
|
||||
* fs_d.id */
|
||||
unsigned char hash_c[LARDON3D_PROJECT_DB_SHA256_SIZE] = {0xCC};
|
||||
char path_c[LARDON3D_PROJECT_DB_PATH_CAPACITY];
|
||||
asset_path_for_hash(hash_c, path_c);
|
||||
Lardon3DProjectDbImage image_c;
|
||||
CHECK(lardon3d_project_db_register_image(database, scanset.scanset_id, hash_c, path_c, 1,
|
||||
"c.jpg", "/src/c.jpg", 0, 3, &identity_status,
|
||||
CHECK(lardon3d_project_db_register_image(database, scanset.scanset_id, hash_c,
|
||||
path_c, 1, "c.jpg", "/src/c.jpg", 0,
|
||||
3, &identity_status,
|
||||
&image_c) == LARDON3D_PROJECT_DB_OK);
|
||||
|
||||
unsigned char hash_d[LARDON3D_PROJECT_DB_SHA256_SIZE] = {0xDD};
|
||||
char path_d[LARDON3D_PROJECT_DB_PATH_CAPACITY];
|
||||
asset_path_for_hash(hash_d, path_d);
|
||||
Lardon3DProjectDbImage image_d;
|
||||
CHECK(lardon3d_project_db_register_image(database, scanset.scanset_id, hash_d, path_d, 1,
|
||||
"d.jpg", "/src/d.jpg", 0, 4, &identity_status,
|
||||
CHECK(lardon3d_project_db_register_image(database, scanset.scanset_id, hash_d,
|
||||
path_d, 1, "d.jpg", "/src/d.jpg", 0,
|
||||
4, &identity_status,
|
||||
&image_d) == LARDON3D_PROJECT_DB_OK);
|
||||
|
||||
/* Create feature set for image_d first (lower ID), then image_c (higher ID) */
|
||||
/* Create feature set for image_d first (lower ID), then image_c (higher ID)
|
||||
*/
|
||||
unsigned char feature_hash_d[LARDON3D_PROJECT_DB_SHA256_SIZE] = {0x55};
|
||||
char feature_path_d[LARDON3D_PROJECT_DB_PATH_CAPACITY];
|
||||
feature_asset_path_for_hash(feature_hash_d, feature_path_d);
|
||||
Lardon3DProjectDbFeatureSet fs_d;
|
||||
CHECK(lardon3d_project_db_register_feature_set(
|
||||
database, image_d.image_id, "orb", 1, feature_hash_d, hash_d, 100, 1, 32,
|
||||
feature_hash_d, feature_path_d, 128, LARDON3D_DB_FEATURE_ASSET_DURABLE, 0, 50,
|
||||
database, image_d.image_id, "orb", 1, feature_hash_d, hash_d, 100,
|
||||
1, 32, feature_hash_d, feature_path_d, 128,
|
||||
LARDON3D_DB_FEATURE_ASSET_DURABLE, 0, 50,
|
||||
&fs_d) == LARDON3D_PROJECT_DB_OK);
|
||||
|
||||
unsigned char feature_hash_c[LARDON3D_PROJECT_DB_SHA256_SIZE] = {0x66};
|
||||
|
|
@ -261,26 +291,27 @@ static bool run_test(void) {
|
|||
feature_asset_path_for_hash(feature_hash_c, feature_path_c);
|
||||
Lardon3DProjectDbFeatureSet fs_c;
|
||||
CHECK(lardon3d_project_db_register_feature_set(
|
||||
database, image_c.image_id, "sift", 1, feature_hash_c, hash_c, 100, 2, 128,
|
||||
feature_hash_c, feature_path_c, 256,
|
||||
LARDON3D_DB_FEATURE_ASSET_PUBLISHED_NOT_DURABLE, 0, 60, &fs_c) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
database, image_c.image_id, "sift", 1, feature_hash_c, hash_c, 100,
|
||||
2, 128, feature_hash_c, feature_path_c, 256,
|
||||
LARDON3D_DB_FEATURE_ASSET_PUBLISHED_NOT_DURABLE, 0, 60,
|
||||
&fs_c) == LARDON3D_PROJECT_DB_OK);
|
||||
|
||||
/* fs_c was created after fs_d → fs_c.feature_set_id > fs_d.feature_set_id */
|
||||
CHECK(fs_c.feature_set_id > fs_d.feature_set_id);
|
||||
|
||||
/* CP (image_c, image_d): image_c should come first since registered first */
|
||||
Lardon3DProjectDbCandidatePair pair_cd;
|
||||
CHECK(lardon3d_project_db_create_candidate_pair(database, image_c.image_id, image_d.image_id, 20,
|
||||
&pair_cd) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_create_candidate_pair(
|
||||
database, image_c.image_id, image_d.image_id, 20, &pair_cd) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
unsigned char fp6[32];
|
||||
memset(fp6, 0x06, 32);
|
||||
Lardon3DProjectDbMatchResult inverted_fs_result;
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair_cd.candidate_pair_id, fs_c.feature_set_id, fs_d.feature_set_id,
|
||||
"orb_bf", 1, fp6, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 15,
|
||||
match_hash, match_path, 212, 500,
|
||||
&inverted_fs_result) == LARDON3D_PROJECT_DB_OK);
|
||||
database, pair_cd.candidate_pair_id, fs_c.feature_set_id,
|
||||
fs_d.feature_set_id, "orb_bf", 1, fp6,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 15, match_hash, match_path,
|
||||
212, 500, &inverted_fs_result) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(inverted_fs_result.feature_set_id_a == fs_c.feature_set_id &&
|
||||
inverted_fs_result.feature_set_id_b == fs_d.feature_set_id);
|
||||
CHECK(fs_c.feature_set_id > fs_d.feature_set_id);
|
||||
|
|
@ -289,26 +320,27 @@ static bool run_test(void) {
|
|||
unsigned char fp7[32];
|
||||
memset(fp7, 0x07, 32);
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_b.feature_set_id, fs_a.feature_set_id,
|
||||
"orb_bf", 1, fp7, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 5,
|
||||
match_hash, match_path, 92, 600,
|
||||
&result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, pair.candidate_pair_id, fs_b.feature_set_id,
|
||||
fs_a.feature_set_id, "orb_bf", 1, fp7,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 5, match_hash, match_path, 92,
|
||||
600, &result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
/* FeatureSet B belonging to image A (not image B) → rejected */
|
||||
unsigned char fp8[32];
|
||||
memset(fp8, 0x08, 32);
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_a2.feature_set_id,
|
||||
"orb_bf", 1, fp8, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 5,
|
||||
match_hash, match_path, 92, 700,
|
||||
&result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_a2.feature_set_id, "orb_bf", 1, fp8,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 5, match_hash, match_path, 92,
|
||||
700, &result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
/* Load by ID */
|
||||
Lardon3DProjectDbMatchResult loaded;
|
||||
CHECK(lardon3d_project_db_load_match_result(database, first_id, &loaded) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(loaded.match_result_id == first_id && loaded.result_status ==
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED && loaded.match_count == 30);
|
||||
CHECK(loaded.match_result_id == first_id &&
|
||||
loaded.result_status == LARDON3D_MATCH_RESULT_STATUS_MATCHED &&
|
||||
loaded.match_count == 30);
|
||||
|
||||
/* Load not found */
|
||||
CHECK(lardon3d_project_db_load_match_result(database, 999999, &loaded) ==
|
||||
|
|
@ -316,50 +348,52 @@ static bool run_test(void) {
|
|||
|
||||
/* Find by full 6-part identity */
|
||||
Lardon3DProjectDbMatchResult found;
|
||||
CHECK(lardon3d_project_db_find_match_result(database, pair.candidate_pair_id,
|
||||
fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 1, fp1,
|
||||
CHECK(lardon3d_project_db_find_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 1, fp1,
|
||||
&found) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(found.match_result_id == first_id && found.match_count == 30);
|
||||
|
||||
/* Find with wrong feature_set_id → NOT_FOUND */
|
||||
CHECK(lardon3d_project_db_find_match_result(database, pair.candidate_pair_id,
|
||||
fs_a2.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 1, fp1,
|
||||
CHECK(lardon3d_project_db_find_match_result(
|
||||
database, pair.candidate_pair_id, fs_a2.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 1, fp1,
|
||||
&found) == LARDON3D_PROJECT_DB_NOT_FOUND);
|
||||
|
||||
/* Find not found */
|
||||
unsigned char unknown_fp[32];
|
||||
memset(unknown_fp, 0xFF, 32);
|
||||
CHECK(lardon3d_project_db_find_match_result(database, pair.candidate_pair_id,
|
||||
fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 1, unknown_fp,
|
||||
CHECK(lardon3d_project_db_find_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 1, unknown_fp,
|
||||
&found) == LARDON3D_PROJECT_DB_NOT_FOUND);
|
||||
|
||||
/* Find with wrong candidate_pair */
|
||||
CHECK(lardon3d_project_db_find_match_result(database, 999999,
|
||||
fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 1, fp1,
|
||||
&found) == LARDON3D_PROJECT_DB_NOT_FOUND);
|
||||
CHECK(lardon3d_project_db_find_match_result(
|
||||
database, 999999, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 1, fp1, &found) == LARDON3D_PROJECT_DB_NOT_FOUND);
|
||||
|
||||
/* Pagination */
|
||||
Lardon3DProjectDbMatchResult page[LARDON3D_PROJECT_DB_MATCH_RESULT_PAGE_MAX];
|
||||
size_t page_count = 0;
|
||||
CHECK(lardon3d_project_db_list_match_results(database, 0, page, 4, &page_count) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_list_match_results(
|
||||
database, 0, page, 4, &page_count) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(page_count == 4 && page[0].match_result_id == first_id &&
|
||||
page[1].match_result_id == second_id && page[2].match_result_id == distinct_id);
|
||||
page[1].match_result_id == second_id &&
|
||||
page[2].match_result_id == distinct_id);
|
||||
|
||||
/* Cursor pagination */
|
||||
page_count = 0;
|
||||
CHECK(lardon3d_project_db_list_match_results(database, first_id, page, 2, &page_count) ==
|
||||
CHECK(lardon3d_project_db_list_match_results(database, first_id, page, 2,
|
||||
&page_count) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(page_count == 2 && page[0].match_result_id == second_id &&
|
||||
page[1].match_result_id == distinct_id);
|
||||
|
||||
/* End of list */
|
||||
page_count = 0;
|
||||
CHECK(lardon3d_project_db_list_match_results(database, distinct_id, page, 1, &page_count) ==
|
||||
CHECK(lardon3d_project_db_list_match_results(database, distinct_id, page, 1,
|
||||
&page_count) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(page_count == 1 && page[0].match_result_id > distinct_id);
|
||||
|
||||
|
|
@ -369,114 +403,116 @@ static bool run_test(void) {
|
|||
&page_count) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
/* Invalid arguments: NULL database */
|
||||
CHECK(lardon3d_project_db_create_match_result(NULL, pair.candidate_pair_id,
|
||||
fs_a.feature_set_id, fs_b.feature_set_id, "orb_bf", 1, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30,
|
||||
match_hash, match_path, 392, 100,
|
||||
&result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
NULL, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 1, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30, match_hash, match_path,
|
||||
392, 100, &result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
/* Invalid arguments: 0 ids */
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, 0, fs_a.feature_set_id, fs_b.feature_set_id, "orb_bf", 1, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30,
|
||||
match_hash, match_path, 392, 100, &result) ==
|
||||
LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, 0, fs_a.feature_set_id, fs_b.feature_set_id, "orb_bf", 1,
|
||||
fp1, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30, match_hash,
|
||||
match_path, 392, 100,
|
||||
&result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, 0, fs_b.feature_set_id, "orb_bf", 1, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30,
|
||||
match_hash, match_path, 392, 100, &result) ==
|
||||
LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, pair.candidate_pair_id, 0, fs_b.feature_set_id, "orb_bf",
|
||||
1, fp1, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30, match_hash,
|
||||
match_path, 392, 100,
|
||||
&result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
/* Invalid arguments: NULL output */
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 1, fp1, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30,
|
||||
match_hash, match_path, 392, 100, NULL) ==
|
||||
LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 1, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30, match_hash, match_path,
|
||||
392, 100, NULL) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
/* Invalid arguments: empty matcher_kind */
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"", 1, fp1, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30,
|
||||
match_hash, match_path, 392, 100, &result) ==
|
||||
LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "", 1, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30, match_hash, match_path,
|
||||
392, 100, &result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
/* Invalid arguments: 0 matcher_version */
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 0, fp1, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30,
|
||||
match_hash, match_path, 392, 100, &result) ==
|
||||
LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 0, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30, match_hash, match_path,
|
||||
392, 100, &result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
/* Invalid arguments: NULL fingerprint */
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 1, NULL, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30,
|
||||
match_hash, match_path, 392, 100, &result) ==
|
||||
LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 1, NULL,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30, match_hash, match_path,
|
||||
392, 100, &result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
/* Invalid arguments: bad status */
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 1, fp1, 2, 30,
|
||||
match_hash, match_path, 392, 100, &result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 1, fp1, 2, 30, match_hash,
|
||||
match_path, 392, 100,
|
||||
&result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
/* Invalid result/asset combinations */
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 1, fp1, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 10,
|
||||
NULL, NULL, 0, 100, &result) ==
|
||||
LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 1, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 10, NULL, NULL, 0, 100,
|
||||
&result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"bad-no-match", 1, fp1, LARDON3D_MATCH_RESULT_STATUS_NO_MATCH, 0,
|
||||
match_hash, match_path, 32, 100, &result) ==
|
||||
LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "bad-no-match", 1, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_NO_MATCH, 0, match_hash, match_path,
|
||||
32, 100, &result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"too-many", 1, fp1, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 8193,
|
||||
match_hash, match_path, 98348, 100, &result) ==
|
||||
LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "too-many", 1, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 8193, match_hash, match_path,
|
||||
98348, 100, &result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
/* Invalid arguments: negative created_at */
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 1, fp1, LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30,
|
||||
match_hash, match_path, 392, -1, &result) ==
|
||||
LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 1, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED, 30, match_hash, match_path,
|
||||
392, -1, &result) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
||||
/* Status boundary values */
|
||||
CHECK(lardon3d_project_db_create_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"boundary", 1, fp1, LARDON3D_MATCH_RESULT_STATUS_NO_MATCH, 0,
|
||||
NULL, NULL, 0, 500, &result) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "boundary", 1, fp1,
|
||||
LARDON3D_MATCH_RESULT_STATUS_NO_MATCH, 0, NULL, NULL, 0, 500,
|
||||
&result) == LARDON3D_PROJECT_DB_OK);
|
||||
|
||||
/* Close/reopen persistence */
|
||||
lardon3d_project_db_close(database);
|
||||
database = NULL;
|
||||
|
||||
CHECK(lardon3d_project_db_open(database_path, &database, error) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_schema_version(database) == 15);
|
||||
CHECK(lardon3d_project_db_open(database_path, &database, error) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_schema_version(database) == 16);
|
||||
|
||||
/* Verify persistence: load previously created results */
|
||||
CHECK(lardon3d_project_db_load_match_result(database, first_id, &loaded) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(loaded.match_result_id == first_id && loaded.result_status ==
|
||||
LARDON3D_MATCH_RESULT_STATUS_MATCHED &&
|
||||
CHECK(loaded.match_result_id == first_id &&
|
||||
loaded.result_status == LARDON3D_MATCH_RESULT_STATUS_MATCHED &&
|
||||
strcmp(loaded.matcher_kind, "orb_bf") == 0 && loaded.match_count == 30);
|
||||
|
||||
/* Verify persistence: find by key */
|
||||
CHECK(lardon3d_project_db_find_match_result(database, pair.candidate_pair_id,
|
||||
fs_a.feature_set_id, fs_b.feature_set_id,
|
||||
"orb_bf", 1, fp1,
|
||||
CHECK(lardon3d_project_db_find_match_result(
|
||||
database, pair.candidate_pair_id, fs_a.feature_set_id,
|
||||
fs_b.feature_set_id, "orb_bf", 1, fp1,
|
||||
&found) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(found.match_result_id == first_id);
|
||||
|
||||
/* Verify persistence of distinct result */
|
||||
Lardon3DProjectDbMatchResult distinct_loaded;
|
||||
CHECK(lardon3d_project_db_load_match_result(database, distinct_id, &distinct_loaded) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_load_match_result(
|
||||
database, distinct_id, &distinct_loaded) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(distinct_loaded.match_result_id == distinct_id &&
|
||||
distinct_loaded.feature_set_id_a == fs_a2.feature_set_id &&
|
||||
distinct_loaded.feature_set_id_b == fs_b2.feature_set_id);
|
||||
|
|
@ -484,8 +520,9 @@ static bool run_test(void) {
|
|||
|
||||
/* Verify inverted result persisted */
|
||||
Lardon3DProjectDbMatchResult inverted_loaded;
|
||||
CHECK(lardon3d_project_db_load_match_result(database, inverted_fs_result.match_result_id,
|
||||
&inverted_loaded) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_load_match_result(
|
||||
database, inverted_fs_result.match_result_id, &inverted_loaded) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(inverted_loaded.feature_set_id_a == fs_c.feature_set_id &&
|
||||
inverted_loaded.feature_set_id_b == fs_d.feature_set_id);
|
||||
CHECK(inverted_loaded.feature_set_id_a > inverted_loaded.feature_set_id_b);
|
||||
|
|
@ -495,15 +532,20 @@ static bool run_test(void) {
|
|||
|
||||
/* Migration from v9 */
|
||||
CHECK(create_v9_database(v9_path));
|
||||
CHECK(query_integer(v9_path, "SELECT value FROM metadata WHERE key='schema_version'", 9));
|
||||
CHECK(lardon3d_project_db_open(v9_path, &database, error) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_schema_version(database) == 15);
|
||||
CHECK(query_integer(
|
||||
v9_path, "SELECT value FROM metadata WHERE key='schema_version'", 9));
|
||||
CHECK(lardon3d_project_db_open(v9_path, &database, error) ==
|
||||
LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_schema_version(database) == 16);
|
||||
lardon3d_project_db_close(database);
|
||||
database = NULL;
|
||||
CHECK(query_integer(v9_path, "SELECT value FROM metadata WHERE key='schema_version'", 15));
|
||||
CHECK(query_integer(v9_path,
|
||||
CHECK(query_integer(
|
||||
v9_path, "SELECT value FROM metadata WHERE key='schema_version'", 16));
|
||||
CHECK(
|
||||
query_integer(v9_path,
|
||||
"SELECT count(*) FROM sqlite_master WHERE type='table' AND "
|
||||
"name='match_results'", 1));
|
||||
"name='match_results'",
|
||||
1));
|
||||
|
||||
/* Forced migration failure v10 */
|
||||
CHECK(create_v9_database(failed_v10_path));
|
||||
|
|
@ -511,15 +553,22 @@ static bool run_test(void) {
|
|||
CHECK(lardon3d_project_db_open(failed_v10_path, &database, error) ==
|
||||
LARDON3D_PROJECT_DB_IO_ERROR);
|
||||
CHECK(unsetenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V10") == 0);
|
||||
CHECK(query_integer(failed_v10_path, "SELECT value FROM metadata WHERE key='schema_version'", 9));
|
||||
CHECK(query_integer(failed_v10_path,
|
||||
"SELECT value FROM metadata WHERE key='schema_version'",
|
||||
9));
|
||||
CHECK(
|
||||
query_integer(failed_v10_path,
|
||||
"SELECT count(*) FROM sqlite_master WHERE type='table' AND "
|
||||
"name='match_results'", 0));
|
||||
CHECK(query_integer(failed_v10_path,
|
||||
"name='match_results'",
|
||||
0));
|
||||
CHECK(
|
||||
query_integer(failed_v10_path,
|
||||
"SELECT count(*) FROM sqlite_master WHERE type='table' AND "
|
||||
"name='matcher_tasks'", 0));
|
||||
CHECK(lardon3d_project_db_open(failed_v10_path, &database, error) == LARDON3D_PROJECT_DB_OK &&
|
||||
lardon3d_project_db_schema_version(database) == 15);
|
||||
"name='matcher_tasks'",
|
||||
0));
|
||||
CHECK(lardon3d_project_db_open(failed_v10_path, &database, error) ==
|
||||
LARDON3D_PROJECT_DB_OK &&
|
||||
lardon3d_project_db_schema_version(database) == 16);
|
||||
lardon3d_project_db_close(database);
|
||||
database = NULL;
|
||||
|
||||
|
|
@ -530,6 +579,4 @@ static bool run_test(void) {
|
|||
return true;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
return run_test() ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
int main(void) { return run_test() ? EXIT_SUCCESS : EXIT_FAILURE; }
|
||||
|
|
|
|||
|
|
@ -63,10 +63,10 @@ static bool query_integer(const char *path, const char *sql,
|
|||
sqlite3_int64 expected) {
|
||||
sqlite3 *connection = NULL;
|
||||
sqlite3_stmt *statement = NULL;
|
||||
bool success = sqlite3_open_v2(path, &connection, SQLITE_OPEN_READONLY,
|
||||
NULL) == SQLITE_OK &&
|
||||
sqlite3_prepare_v2(connection, sql, -1, &statement, NULL) ==
|
||||
bool success =
|
||||
sqlite3_open_v2(path, &connection, SQLITE_OPEN_READONLY, NULL) ==
|
||||
SQLITE_OK &&
|
||||
sqlite3_prepare_v2(connection, sql, -1, &statement, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW &&
|
||||
sqlite3_column_int64(statement, 0) == expected;
|
||||
if (statement) {
|
||||
|
|
@ -89,6 +89,14 @@ static bool downgrade_project_to_historical_v10(const char *database_path) {
|
|||
}
|
||||
static const char sql[] =
|
||||
"PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;"
|
||||
"DROP TABLE IF EXISTS sparse_landmark_observations;"
|
||||
"DROP TABLE IF EXISTS sparse_landmarks;"
|
||||
"DROP TABLE IF EXISTS sparse_registered_images;"
|
||||
"DROP TABLE IF EXISTS sparse_reconstruction_components;"
|
||||
"DROP TABLE IF EXISTS sparse_reconstructions;"
|
||||
"DROP TABLE IF EXISTS sparse_calibration_scope_images;"
|
||||
"DROP TABLE IF EXISTS sparse_calibration_scopes;"
|
||||
"DROP TABLE IF EXISTS sparse_calibrations;"
|
||||
"DROP TABLE geometric_verifier_tasks;"
|
||||
"DROP TABLE geometric_verification_results;"
|
||||
"DROP TABLE matcher_tasks;"
|
||||
|
|
@ -346,17 +354,20 @@ static bool run_test(void) {
|
|||
CHECK(query_integer(database_path,
|
||||
"SELECT value FROM metadata WHERE key='schema_version'",
|
||||
10));
|
||||
CHECK(query_integer(database_path,
|
||||
CHECK(
|
||||
query_integer(database_path,
|
||||
"SELECT count(*) FROM sqlite_master WHERE type='table' AND "
|
||||
"name='match_results'",
|
||||
1));
|
||||
CHECK(query_integer(database_path,
|
||||
CHECK(
|
||||
query_integer(database_path,
|
||||
"SELECT count(*) FROM sqlite_master WHERE type='table' AND "
|
||||
"name='matcher_tasks'",
|
||||
0));
|
||||
CHECK(reopen_runtime(&fixture));
|
||||
CHECK(lardon3d_project_db_schema_version(fixture.state.project_db) == 15);
|
||||
CHECK(query_integer(database_path,
|
||||
CHECK(lardon3d_project_db_schema_version(fixture.state.project_db) == 16);
|
||||
CHECK(
|
||||
query_integer(database_path,
|
||||
"SELECT count(*) FROM sqlite_master WHERE type='table' AND "
|
||||
"name='matcher_tasks'",
|
||||
1));
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
1075
tests/test_sparse_sfm_model.c
Normal file
1075
tests/test_sparse_sfm_model.c
Normal file
File diff suppressed because it is too large
Load diff
313
tests/test_sparse_sfm_resource.c
Normal file
313
tests/test_sparse_sfm_resource.c
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
#include <sqlite3.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <lardon3d/project_db.h>
|
||||
#include <lardon3d/sparse_sfm_model.h>
|
||||
|
||||
static bool exec_sql(const char *path, const char *sql) {
|
||||
sqlite3 *connection = NULL;
|
||||
bool ok = sqlite3_open(path, &connection) == SQLITE_OK;
|
||||
if (ok)
|
||||
ok = sqlite3_exec(connection, "PRAGMA foreign_keys=ON", NULL, NULL, NULL) ==
|
||||
SQLITE_OK;
|
||||
char *message = NULL;
|
||||
if (ok)
|
||||
ok = sqlite3_exec(connection, sql, NULL, NULL, &message) == SQLITE_OK;
|
||||
if (!ok && connection)
|
||||
fprintf(stderr, "resource SQL failed: %s\n",
|
||||
message ? message : sqlite3_errmsg(connection));
|
||||
sqlite3_free(message);
|
||||
if (connection)
|
||||
ok = sqlite3_close(connection) == SQLITE_OK && ok;
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool seed_upstream(const char *path, uint64_t landmark_count) {
|
||||
char *sql = NULL;
|
||||
int required = snprintf(
|
||||
NULL, 0,
|
||||
"PRAGMA foreign_keys=ON;"
|
||||
"INSERT INTO scansets(scanset_id,name,created_at,updated_at) "
|
||||
"VALUES(1,'resource',1,1);"
|
||||
"WITH RECURSIVE n(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM n WHERE x<500) "
|
||||
"INSERT INTO image_assets(asset_id,sha256,path,size_bytes,state,created_at) "
|
||||
"SELECT x,randomblob(32),printf('resource/image-%%d',x),1,1,1 FROM n;"
|
||||
"WITH RECURSIVE n(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM n WHERE x<500) "
|
||||
"INSERT INTO images(image_id,scanset_id,asset_id,original_name,source_path,"
|
||||
"producer_task_id,imported_at) SELECT x,1,x,printf('image-%%d',x),"
|
||||
"printf('resource/image-%%d',x),NULL,1 FROM n;"
|
||||
"WITH RECURSIVE n(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM n WHERE x<500) "
|
||||
"INSERT INTO feature_assets(feature_asset_id,sha256,path,size_bytes,durability,created_at) "
|
||||
"SELECT x,randomblob(32),printf('resource/feature-%%d',x),1,0,1 FROM n;"
|
||||
"WITH RECURSIVE n(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM n WHERE x<500) "
|
||||
"INSERT INTO feature_sets(feature_set_id,image_id,feature_asset_id,extractor_kind,"
|
||||
"extractor_version,parameter_fingerprint,source_image_sha256,feature_count,"
|
||||
"descriptor_type,descriptor_dimension,producer_task_id,created_at) SELECT x,x,x,"
|
||||
"'resource',1,randomblob(32),randomblob(32),5,1,32,NULL,1 FROM n;"
|
||||
"INSERT INTO track_sets(track_set_id,builder_kind,builder_version,parameter_fingerprint,"
|
||||
"verifier_kind,verifier_version,verifier_fingerprint,input_scope_hash,gvr_count,"
|
||||
"track_count,created_at) VALUES(1,'resource',1,zeroblob(32),1,1,zeroblob(32),"
|
||||
"zeroblob(32),1,%llu,1);"
|
||||
"WITH RECURSIVE n(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM n WHERE x<%llu) "
|
||||
"INSERT INTO tracks(track_id,track_set_id,observation_count) SELECT x,1,5 FROM n;"
|
||||
"WITH RECURSIVE t(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM t WHERE x<%llu), "
|
||||
"p(x) AS (SELECT 0 UNION ALL SELECT x+1 FROM p WHERE x<4) "
|
||||
"INSERT INTO track_observations(track_set_id,track_id,feature_set_id,feature_index,"
|
||||
"position_in_track) SELECT 1,t.x,CASE WHEN t.x<=%llu THEN 1+((((t.x-1)%%50)*5+p.x)%%250) "
|
||||
"ELSE 251+((((t.x-1-%llu)%%50)*5+p.x)%%250) END,CASE WHEN t.x<=%llu "
|
||||
"THEN ((t.x-1)/50) ELSE ((t.x-1-%llu)/50) END,p.x "
|
||||
"FROM t CROSS JOIN p;",
|
||||
(unsigned long long)landmark_count,
|
||||
(unsigned long long)landmark_count,
|
||||
(unsigned long long)landmark_count,
|
||||
(unsigned long long)(landmark_count / 2),
|
||||
(unsigned long long)(landmark_count / 2),
|
||||
(unsigned long long)(landmark_count / 2),
|
||||
(unsigned long long)(landmark_count / 2));
|
||||
if (required < 0)
|
||||
return false;
|
||||
sql = malloc((size_t)required + 1);
|
||||
if (!sql)
|
||||
return false;
|
||||
(void)snprintf(
|
||||
sql, (size_t)required + 1,
|
||||
"PRAGMA foreign_keys=ON;"
|
||||
"INSERT INTO scansets(scanset_id,name,created_at,updated_at) VALUES(1,'resource',1,1);"
|
||||
"WITH RECURSIVE n(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM n WHERE x<500) "
|
||||
"INSERT INTO image_assets(asset_id,sha256,path,size_bytes,state,created_at) "
|
||||
"SELECT x,randomblob(32),printf('resource/image-%%d',x),1,1,1 FROM n;"
|
||||
"WITH RECURSIVE n(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM n WHERE x<500) "
|
||||
"INSERT INTO images(image_id,scanset_id,asset_id,original_name,source_path,producer_task_id,imported_at) "
|
||||
"SELECT x,1,x,printf('image-%%d',x),printf('resource/image-%%d',x),NULL,1 FROM n;"
|
||||
"WITH RECURSIVE n(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM n WHERE x<500) "
|
||||
"INSERT INTO feature_assets(feature_asset_id,sha256,path,size_bytes,durability,created_at) "
|
||||
"SELECT x,randomblob(32),printf('resource/feature-%%d',x),1,0,1 FROM n;"
|
||||
"WITH RECURSIVE n(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM n WHERE x<500) "
|
||||
"INSERT INTO feature_sets(feature_set_id,image_id,feature_asset_id,"
|
||||
"extractor_kind,extractor_version,parameter_fingerprint,source_image_sha256,"
|
||||
"feature_count,descriptor_type,descriptor_dimension,producer_task_id,created_at) "
|
||||
"SELECT x,x,x,'resource',1,randomblob(32),randomblob(32),5,1,32,NULL,1 FROM n;"
|
||||
"INSERT INTO track_sets(track_set_id,builder_kind,builder_version,"
|
||||
"parameter_fingerprint,verifier_kind,verifier_version,"
|
||||
"verifier_fingerprint,input_scope_hash,gvr_count,track_count,created_at) "
|
||||
"VALUES(1,'resource',1,zeroblob(32),1,1,zeroblob(32),zeroblob(32),1,%llu,1);"
|
||||
"WITH RECURSIVE n(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM n WHERE x<%llu) "
|
||||
"INSERT INTO tracks(track_id,track_set_id,observation_count) SELECT x,1,5 FROM n;"
|
||||
"WITH RECURSIVE t(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM t WHERE x<%llu),"
|
||||
"p(x) AS (SELECT 0 UNION ALL SELECT x+1 FROM p WHERE x<4) "
|
||||
"INSERT INTO track_observations(track_set_id,track_id,feature_set_id,"
|
||||
"feature_index,position_in_track) SELECT 1,t.x,CASE WHEN t.x<=%llu "
|
||||
"THEN 1+((((t.x-1)%%50)*5+p.x)%%250) ELSE "
|
||||
"251+((((t.x-1-%llu)%%50)*5+p.x)%%250) END,CASE WHEN t.x<=%llu "
|
||||
"THEN ((t.x-1)/50) ELSE ((t.x-1-%llu)/50) END,p.x FROM t CROSS JOIN p;",
|
||||
(unsigned long long)landmark_count,
|
||||
(unsigned long long)landmark_count,
|
||||
(unsigned long long)landmark_count,
|
||||
(unsigned long long)(landmark_count / 2),
|
||||
(unsigned long long)(landmark_count / 2),
|
||||
(unsigned long long)(landmark_count / 2),
|
||||
(unsigned long long)(landmark_count / 2));
|
||||
bool ok = exec_sql(path, sql);
|
||||
free(sql);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool publish_fixture(const char *path, uint64_t landmark_count) {
|
||||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
Lardon3DProjectDb *db = NULL;
|
||||
if (lardon3d_project_db_open(path, &db, error) != LARDON3D_PROJECT_DB_OK)
|
||||
return false;
|
||||
Lardon3DSparseCalibration calibration = {
|
||||
.model_kind = 1, .model_version = 1, .width = 4000, .height = 3000,
|
||||
.fx = 2000, .fy = 2000, .cx = 2000, .cy = 1500, .provenance_kind = 1};
|
||||
Lardon3DSparseCalibration stored;
|
||||
if (lardon3d_sparse_calibration_create(db, &calibration, &stored) !=
|
||||
LARDON3D_PROJECT_DB_OK)
|
||||
return false;
|
||||
Lardon3DSparseCalibrationMember *members = calloc(500, sizeof(*members));
|
||||
for (size_t index = 0; index < 500; ++index) {
|
||||
members[index].image_id = index + 1;
|
||||
members[index].calibration_id = stored.calibration_id;
|
||||
memcpy(members[index].calibration_hash, stored.scientific_hash, 32);
|
||||
}
|
||||
Lardon3DSparseCalibrationScope scope;
|
||||
bool ok = lardon3d_sparse_calibration_scope_create(db, members, 500, &scope) ==
|
||||
LARDON3D_PROJECT_DB_OK;
|
||||
free(members);
|
||||
if (!ok)
|
||||
fprintf(stderr, "scope or allocation failed\n");
|
||||
if (!ok)
|
||||
return false;
|
||||
Lardon3DSparseComponent components[2] = {
|
||||
{.component_key = 1, .registered_image_count = 250,
|
||||
.landmark_count = landmark_count / 2},
|
||||
{.component_key = 251, .registered_image_count = 250,
|
||||
.landmark_count = landmark_count / 2}};
|
||||
Lardon3DSparseRegisteredImage *poses = calloc(500, sizeof(*poses));
|
||||
Lardon3DSparseLandmark *landmarks = calloc(landmark_count, sizeof(*landmarks));
|
||||
Lardon3DSparseLandmarkObservation *observations =
|
||||
calloc(landmark_count * 5, sizeof(*observations));
|
||||
if (!poses || !landmarks || !observations)
|
||||
return false;
|
||||
for (size_t index = 0; index < 500; ++index)
|
||||
poses[index] = (Lardon3DSparseRegisteredImage){
|
||||
.image_id = index + 1,
|
||||
.component_key = index < 250 ? 1 : 251,
|
||||
.rotation_cw = {1, 0, 0, 0, 1, 0, 0, 0, 1},
|
||||
.translation_cw = {(double)index, 0, 0}};
|
||||
for (uint64_t track = 0; track < landmark_count; ++track) {
|
||||
size_t component_base = track < landmark_count / 2 ? 0 : 250;
|
||||
uint64_t local_track = track < landmark_count / 2
|
||||
? track
|
||||
: track - landmark_count / 2;
|
||||
uint64_t component_key = component_base ? 251 : 1;
|
||||
landmarks[track] = (Lardon3DSparseLandmark){
|
||||
.track_id = track + 1, .component_key = component_key, .x = 0,
|
||||
.y = 0, .z = 1, .reprojection_rmse_px = 0.1,
|
||||
.reprojection_median_px = 0.1, .observation_count = 5};
|
||||
for (uint32_t position = 0; position < 5; ++position) {
|
||||
size_t offset = (size_t)track * 5 + position;
|
||||
uint64_t feature_set = (uint64_t)component_base +
|
||||
((local_track % 50) * 5 + position) % 250 + 1;
|
||||
observations[offset] = (Lardon3DSparseLandmarkObservation){
|
||||
.track_id = track + 1,
|
||||
.feature_set_id = feature_set,
|
||||
.feature_index = (uint32_t)(local_track / 50),
|
||||
.position_in_track = position};
|
||||
}
|
||||
}
|
||||
Lardon3DSparsePublication publication = {
|
||||
.track_set_id = 1,
|
||||
.calibration_scope_id = scope.scope_id,
|
||||
.sfm_kind = 1,
|
||||
.sfm_version = 1,
|
||||
.components = components,
|
||||
.component_count = 2,
|
||||
.registered_images = poses,
|
||||
.registered_image_count = 500,
|
||||
.landmarks = landmarks,
|
||||
.landmark_count = landmark_count,
|
||||
.observations = observations,
|
||||
.observation_count = landmark_count * 5,
|
||||
.reprojection_rmse_px = 0.1,
|
||||
.reprojection_median_px = 0.1,
|
||||
.created_at = 1};
|
||||
publication.parameter_fingerprint[0] = 91;
|
||||
Lardon3DSparseReconstruction reconstruction;
|
||||
Lardon3DProjectDbResult publication_result =
|
||||
lardon3d_sparse_reconstruction_publish(db, &publication, &reconstruction);
|
||||
ok = publication_result == LARDON3D_PROJECT_DB_OK;
|
||||
if (!ok) {
|
||||
char detail[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
(void)lardon3d_project_db_last_error(db, detail);
|
||||
fprintf(stderr, "publish result %d: %s\n", publication_result, detail);
|
||||
}
|
||||
if (ok)
|
||||
printf("reconstruction_id=%llu landmarks=%llu observations=%llu\n",
|
||||
(unsigned long long)reconstruction.reconstruction_id,
|
||||
(unsigned long long)landmark_count,
|
||||
(unsigned long long)(landmark_count * 5));
|
||||
free(poses);
|
||||
free(landmarks);
|
||||
free(observations);
|
||||
lardon3d_project_db_close(db);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool page_fixture(const char *path, uint64_t reconstruction_id,
|
||||
unsigned passes) {
|
||||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
Lardon3DProjectDb *db = NULL;
|
||||
if (lardon3d_project_db_open(path, &db, error) != LARDON3D_PROJECT_DB_OK)
|
||||
return false;
|
||||
uint64_t totals[4] = {0};
|
||||
for (unsigned pass = 0; pass < passes; ++pass) {
|
||||
Lardon3DSparseComponent components_items[64];
|
||||
Lardon3DSparseComponentPage components = {.items = components_items};
|
||||
uint64_t cursor = 0;
|
||||
while (lardon3d_sparse_component_list(db, reconstruction_id, cursor, 64,
|
||||
&components) == LARDON3D_PROJECT_DB_OK &&
|
||||
components.count) {
|
||||
totals[0] += components.count;
|
||||
cursor = components.next_component_key;
|
||||
}
|
||||
Lardon3DSparseRegisteredImage cameras[64];
|
||||
Lardon3DSparseRegisteredImagePage camera_page = {.items = cameras};
|
||||
cursor = 0;
|
||||
while (lardon3d_sparse_registered_image_list(
|
||||
db, reconstruction_id, cursor, 64, &camera_page) ==
|
||||
LARDON3D_PROJECT_DB_OK &&
|
||||
camera_page.count) {
|
||||
totals[1] += camera_page.count;
|
||||
cursor = camera_page.next_image_id;
|
||||
}
|
||||
Lardon3DSparseLandmark landmarks[64];
|
||||
Lardon3DSparseLandmarkPage landmark_page = {.items = landmarks};
|
||||
cursor = 0;
|
||||
while (lardon3d_sparse_landmark_list(db, reconstruction_id, cursor, 64,
|
||||
&landmark_page) ==
|
||||
LARDON3D_PROJECT_DB_OK &&
|
||||
landmark_page.count) {
|
||||
totals[2] += landmark_page.count;
|
||||
cursor = landmark_page.next_track_id;
|
||||
}
|
||||
Lardon3DSparseLandmarkObservation observations[64];
|
||||
Lardon3DSparseObservationPage observation_page = {.items = observations};
|
||||
uint64_t landmark_cursor = 0;
|
||||
uint32_t position_cursor = 0;
|
||||
while (lardon3d_sparse_observation_list(
|
||||
db, reconstruction_id, landmark_cursor, position_cursor, 64,
|
||||
&observation_page) == LARDON3D_PROJECT_DB_OK &&
|
||||
observation_page.count) {
|
||||
totals[3] += observation_page.count;
|
||||
landmark_cursor = observation_page.next_landmark_id;
|
||||
position_cursor = observation_page.next_position_in_track;
|
||||
}
|
||||
}
|
||||
printf("passes=%u components=%llu cameras=%llu landmarks=%llu observations=%llu\n",
|
||||
passes, (unsigned long long)totals[0], (unsigned long long)totals[1],
|
||||
(unsigned long long)totals[2], (unsigned long long)totals[3]);
|
||||
lardon3d_project_db_close(db);
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 3)
|
||||
return 2;
|
||||
const char *mode = argv[1];
|
||||
const char *path = argv[2];
|
||||
if (strcmp(mode, "publish") == 0) {
|
||||
uint64_t landmarks = argc > 3 ? strtoull(argv[3], NULL, 10) : 100000;
|
||||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
Lardon3DProjectDb *database = NULL;
|
||||
if (lardon3d_project_db_open(path, &database, error) !=
|
||||
LARDON3D_PROJECT_DB_OK ||
|
||||
!database) {
|
||||
fprintf(stderr, "open resource database failed: %s\n", error);
|
||||
return 1;
|
||||
}
|
||||
lardon3d_project_db_close(database);
|
||||
if (!seed_upstream(path, landmarks)) {
|
||||
fprintf(stderr, "seed upstream failed\n");
|
||||
return 1;
|
||||
}
|
||||
if (!publish_fixture(path, landmarks)) {
|
||||
fprintf(stderr, "publish fixture failed\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(mode, "page") == 0) {
|
||||
unsigned passes = argc > 4 ? (unsigned)strtoul(argv[4], NULL, 10) : 5;
|
||||
return page_fixture(path, argc > 3 ? strtoull(argv[3], NULL, 10) : 1,
|
||||
passes)
|
||||
? 0
|
||||
: 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
Loading…
Reference in a new issue