Complete the A-to-Z Lardon3D maintenance and coherence pass. Generalize host resource policy, remove the global CPU12 ceiling, preserve host CPU/RAM reserves, scale Task capabilities through the Resource Governor, and validate deterministic parallel GV execution. Migrate Project DB to v23 with data-driven camera, lens, optical configuration and calibration profiles, including manual lenses without EXIF. Integrate safe optional LARDON SSD swap/scratch control with Governor and F10 drain/safe-to-unplug semantics. Refactor the ncurses TUI into a runtime observatory with durable progress, elapsed time, smoothed ETA, throughput, resource telemetry, Governor state, optics workflow, colors and compact/no-color fallbacks. Reconcile Queue lifetime, persistence, concurrency, comments, tests, README, AGENTS and canonical documentation. GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN
227 lines
8.9 KiB
C
227 lines
8.9 KiB
C
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include <lardon3d/candidate_pair_gen.h>
|
|
#include <lardon3d/project_db.h>
|
|
#include <lardon3d/visual_index.h>
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
#include "candidate_pair_gen_internal.h"
|
|
|
|
static Lardon3DVisualIndexResult db_result(Lardon3DProjectDbResult result) {
|
|
switch (result) {
|
|
case LARDON3D_PROJECT_DB_OK:
|
|
return LARDON3D_VISUAL_INDEX_OK;
|
|
case LARDON3D_PROJECT_DB_NOT_FOUND:
|
|
return LARDON3D_VISUAL_INDEX_NOT_FOUND;
|
|
case LARDON3D_PROJECT_DB_BUSY:
|
|
return LARDON3D_VISUAL_INDEX_DB_BUSY;
|
|
case LARDON3D_PROJECT_DB_INVALID_ARGUMENT:
|
|
return LARDON3D_VISUAL_INDEX_INVALID_ARGUMENT;
|
|
case LARDON3D_PROJECT_DB_UNSUPPORTED_SCHEMA:
|
|
return LARDON3D_VISUAL_INDEX_UNSUPPORTED_VERSION;
|
|
case LARDON3D_PROJECT_DB_CORRUPT:
|
|
return LARDON3D_VISUAL_INDEX_CORRUPT;
|
|
default:
|
|
return LARDON3D_VISUAL_INDEX_DB_ERROR;
|
|
}
|
|
}
|
|
|
|
Lardon3DVisualIndexResult lardon3d_candidate_pair_compute(
|
|
const char *project_path, Lardon3DProjectDb *database, uint64_t visual_index_id,
|
|
uint64_t source_feature_set_id, const Lardon3DVisualIndexQueryOptions *query_options,
|
|
Lardon3DCandidatePairComputation *computed) {
|
|
if (computed) {
|
|
memset(computed, 0, sizeof(*computed));
|
|
}
|
|
if (!project_path || !database || visual_index_id == 0 || source_feature_set_id == 0 ||
|
|
!query_options || !computed || query_options->top_k == 0 ||
|
|
query_options->top_k > LARDON3D_VISUAL_INDEX_TOP_K_MAX ||
|
|
query_options->minimum_evidence_count > 1024 ||
|
|
query_options->scanset_filter > LARDON3D_VISUAL_INDEX_OTHER_SCANSETS) {
|
|
return LARDON3D_VISUAL_INDEX_INVALID_ARGUMENT;
|
|
}
|
|
Lardon3DProjectDbFeatureSet source_set;
|
|
Lardon3DProjectDbResult loaded =
|
|
lardon3d_project_db_load_feature_set(database, source_feature_set_id, &source_set);
|
|
if (loaded != LARDON3D_PROJECT_DB_OK) {
|
|
return db_result(loaded);
|
|
}
|
|
Lardon3DVisualIndexCandidate *candidates = calloc(query_options->top_k, sizeof(*candidates));
|
|
if (!candidates) {
|
|
return LARDON3D_VISUAL_INDEX_IO_ERROR;
|
|
}
|
|
size_t result_count = 0;
|
|
Lardon3DVisualIndexResult result = lardon3d_visual_index_query(
|
|
project_path, database, visual_index_id, source_feature_set_id, query_options, candidates,
|
|
query_options->top_k, &result_count);
|
|
if (result != LARDON3D_VISUAL_INDEX_OK) {
|
|
free(candidates);
|
|
return result;
|
|
}
|
|
computed->source_feature_set_id = source_feature_set_id;
|
|
computed->queried_count = (uint32_t)result_count;
|
|
for (size_t i = 0; i < result_count; ++i) {
|
|
uint64_t candidate_image_id = candidates[i].image_id;
|
|
if (candidate_image_id == source_set.image_id) {
|
|
continue;
|
|
}
|
|
uint64_t image_a = candidate_image_id < source_set.image_id ? candidate_image_id
|
|
: source_set.image_id;
|
|
uint64_t image_b = candidate_image_id < source_set.image_id ? source_set.image_id
|
|
: candidate_image_id;
|
|
computed->proposals[computed->proposal_count++] =
|
|
(Lardon3DCandidatePairProposal){.image_id_a = image_a, .image_id_b = image_b};
|
|
}
|
|
free(candidates);
|
|
return LARDON3D_VISUAL_INDEX_OK;
|
|
}
|
|
|
|
Lardon3DVisualIndexResult lardon3d_candidate_pair_publish(
|
|
Lardon3DProjectDb *database, const Lardon3DCandidatePairComputation *computed,
|
|
Lardon3DCandidatePairGenStats *stats) {
|
|
if (stats) {
|
|
memset(stats, 0, sizeof(*stats));
|
|
}
|
|
if (!database || !computed || !stats || computed->source_feature_set_id == 0 ||
|
|
computed->proposal_count > LARDON3D_VISUAL_INDEX_TOP_K_MAX) {
|
|
return LARDON3D_VISUAL_INDEX_INVALID_ARGUMENT;
|
|
}
|
|
stats->queried_count = computed->queried_count;
|
|
time_t now = time(NULL);
|
|
if (now < 0) {
|
|
return LARDON3D_VISUAL_INDEX_IO_ERROR;
|
|
}
|
|
/* Publication deliberately preserves proposal order, including duplicates.
|
|
* The established find-before-create behavior therefore retains its exact
|
|
* generated/skipped accounting while the UNIQUE key remains the authority. */
|
|
for (size_t i = 0; i < computed->proposal_count; ++i) {
|
|
Lardon3DProjectDbCandidatePair pair;
|
|
Lardon3DProjectDbResult found = lardon3d_project_db_find_candidate_pair(
|
|
database, computed->proposals[i].image_id_a, computed->proposals[i].image_id_b, &pair);
|
|
if (found == LARDON3D_PROJECT_DB_OK) {
|
|
++stats->skipped_count;
|
|
} else if (found == LARDON3D_PROJECT_DB_NOT_FOUND) {
|
|
Lardon3DProjectDbResult created = lardon3d_project_db_create_candidate_pair(
|
|
database, computed->proposals[i].image_id_a, computed->proposals[i].image_id_b,
|
|
(int64_t)now, &pair);
|
|
if (created != LARDON3D_PROJECT_DB_OK) {
|
|
return db_result(created);
|
|
}
|
|
++stats->generated_count;
|
|
} else {
|
|
return db_result(found);
|
|
}
|
|
}
|
|
return LARDON3D_VISUAL_INDEX_OK;
|
|
}
|
|
|
|
Lardon3DVisualIndexResult lardon3d_candidate_pair_generate(
|
|
const char *project_path, Lardon3DProjectDb *database, uint64_t visual_index_id,
|
|
uint64_t source_feature_set_id, const Lardon3DVisualIndexQueryOptions *query_options,
|
|
Lardon3DCandidatePairGenStats *stats) {
|
|
if (stats) {
|
|
memset(stats, 0, sizeof(*stats));
|
|
}
|
|
if (!stats) {
|
|
return LARDON3D_VISUAL_INDEX_INVALID_ARGUMENT;
|
|
}
|
|
Lardon3DCandidatePairComputation computed;
|
|
Lardon3DVisualIndexResult result = lardon3d_candidate_pair_compute(
|
|
project_path, database, visual_index_id, source_feature_set_id, query_options, &computed);
|
|
return result == LARDON3D_VISUAL_INDEX_OK
|
|
? lardon3d_candidate_pair_publish(database, &computed, stats)
|
|
: result;
|
|
}
|
|
|
|
Lardon3DVisualIndexResult lardon3d_candidate_pair_generate_batch(
|
|
const char *project_path, Lardon3DProjectDb *database,
|
|
uint64_t visual_index_id, uint64_t after_feature_set_id,
|
|
const Lardon3DVisualIndexQueryOptions *query_options,
|
|
Lardon3DCandidatePairGenStats *total_stats,
|
|
uint64_t *last_feature_set_id) {
|
|
if (total_stats) {
|
|
total_stats->generated_count = 0;
|
|
total_stats->skipped_count = 0;
|
|
total_stats->queried_count = 0;
|
|
}
|
|
if (last_feature_set_id) {
|
|
*last_feature_set_id = after_feature_set_id;
|
|
}
|
|
if (!project_path || !database || visual_index_id == 0 || !query_options ||
|
|
!total_stats || !last_feature_set_id) {
|
|
return LARDON3D_VISUAL_INDEX_INVALID_ARGUMENT;
|
|
}
|
|
enum { PAGE_SIZE = 64 };
|
|
Lardon3DProjectDbFeatureSet page[PAGE_SIZE];
|
|
size_t count = 0;
|
|
uint64_t cursor = after_feature_set_id;
|
|
Lardon3DProjectDbResult listed =
|
|
lardon3d_project_db_list_feature_sets(database, cursor, page, PAGE_SIZE, &count);
|
|
if (listed != LARDON3D_PROJECT_DB_OK) {
|
|
return db_result(listed);
|
|
}
|
|
while (count > 0) {
|
|
for (size_t i = 0; i < count; ++i) {
|
|
Lardon3DCandidatePairGenStats stats;
|
|
Lardon3DVisualIndexResult result = lardon3d_candidate_pair_generate(
|
|
project_path, database, visual_index_id, page[i].feature_set_id,
|
|
query_options, &stats);
|
|
if (result != LARDON3D_VISUAL_INDEX_OK) {
|
|
return result;
|
|
}
|
|
total_stats->generated_count += stats.generated_count;
|
|
total_stats->skipped_count += stats.skipped_count;
|
|
total_stats->queried_count += stats.queried_count;
|
|
cursor = page[i].feature_set_id;
|
|
}
|
|
listed = lardon3d_project_db_list_feature_sets(database, cursor, page, PAGE_SIZE, &count);
|
|
if (listed != LARDON3D_PROJECT_DB_OK) {
|
|
return db_result(listed);
|
|
}
|
|
}
|
|
*last_feature_set_id = cursor;
|
|
return LARDON3D_VISUAL_INDEX_OK;
|
|
}
|
|
|
|
void lardon3d_candidate_pair_generation_fingerprint(
|
|
uint64_t visual_index_id, uint64_t source_feature_set_id,
|
|
const Lardon3DVisualIndexQueryOptions *query_options,
|
|
unsigned char fingerprint[32]) {
|
|
if (!fingerprint) return;
|
|
/* FROZEN SCIENTIFIC CONTRACT: v1 hashes the same 29 bytes historically
|
|
* produced on validated little-endian hosts, but spells out widths and byte
|
|
* order so a new architecture cannot silently change Candidate identity.
|
|
* The optional-query form remains the historical 16-byte ID prefix. */
|
|
unsigned char canonical[29];
|
|
for (unsigned int index = 0; index < 8; ++index) {
|
|
canonical[index] = (unsigned char)(visual_index_id >> (8U * index));
|
|
canonical[8 + index] =
|
|
(unsigned char)(source_feature_set_id >> (8U * index));
|
|
}
|
|
size_t size = 16;
|
|
if (query_options) {
|
|
const uint32_t values[] = {
|
|
query_options->top_k,
|
|
query_options->minimum_evidence_count,
|
|
(uint32_t)query_options->scanset_filter,
|
|
};
|
|
for (size_t value = 0; value < 3; ++value) {
|
|
for (unsigned int index = 0; index < 4; ++index) {
|
|
canonical[16 + value * 4 + index] =
|
|
(unsigned char)(values[value] >> (8U * index));
|
|
}
|
|
}
|
|
canonical[28] = query_options->exclude_same_asset ? 1U : 0U;
|
|
size = sizeof(canonical);
|
|
}
|
|
unsigned int digest_size = 0;
|
|
if (EVP_Digest(canonical, size, fingerprint, &digest_size, EVP_sha256(), NULL) != 1 ||
|
|
digest_size != 32) {
|
|
memset(fingerprint, 0, 32);
|
|
}
|
|
}
|