313 lines
14 KiB
C
313 lines
14 KiB
C
#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;
|
|
}
|