201 lines
8.4 KiB
C++
201 lines
8.4 KiB
C++
#include <algorithm>
|
|
#include <chrono>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
extern "C" {
|
|
#include <lardon3d/app_state.h>
|
|
#include <lardon3d/feature_store.h>
|
|
#include <lardon3d/matcher.h>
|
|
#include <lardon3d/project_db.h>
|
|
}
|
|
|
|
struct Sample {
|
|
uint64_t open_ns;
|
|
uint64_t read_ns;
|
|
uint64_t knn_ns;
|
|
uint64_t filter_ns;
|
|
uint64_t canonicalize_ns;
|
|
uint64_t serialize_ns;
|
|
uint64_t sha_ns;
|
|
uint64_t publish_ns;
|
|
uint64_t database_ns;
|
|
uint64_t total_ns;
|
|
};
|
|
|
|
static uint64_t median(std::vector<uint64_t> values) {
|
|
std::sort(values.begin(), values.end());
|
|
return values[values.size() / 2];
|
|
}
|
|
|
|
static void image_path(const unsigned char hash[32], char path[4096]) {
|
|
static const char digits[] = "0123456789abcdef";
|
|
char hex[65];
|
|
for (size_t i = 0; i < 32; ++i) {
|
|
hex[2 * i] = digits[hash[i] >> 4];
|
|
hex[2 * i + 1] = digits[hash[i] & 15U];
|
|
}
|
|
hex[64] = '\0';
|
|
(void)snprintf(path, 4096, "assets/images/%c%c/%s", hex[0], hex[1], hex);
|
|
}
|
|
|
|
static bool register_image(Lardon3DProjectDb *db, uint64_t scanset_id, unsigned char seed,
|
|
Lardon3DProjectDbImage *image) {
|
|
unsigned char hash[32];
|
|
memset(hash, seed, sizeof(hash));
|
|
char path[4096];
|
|
image_path(hash, path);
|
|
Lardon3DProjectDbImageRegisterStatus status;
|
|
return lardon3d_project_db_register_image(db, scanset_id, hash, path, 1, "fixture.bin",
|
|
"/synthetic/fixture.bin", 0, seed, &status,
|
|
image) == LARDON3D_PROJECT_DB_OK;
|
|
}
|
|
|
|
static void fill_features(Lardon3DExtractedFeatures *features, uint32_t count,
|
|
Lardon3DFeatureDescriptorType type, unsigned char salt) {
|
|
(void)salt;
|
|
memset(features, 0, sizeof(*features));
|
|
features->image_width = 4096;
|
|
features->image_height = 4096;
|
|
features->feature_count = count;
|
|
features->keypoints = static_cast<Lardon3DFeatureKeypoint *>(
|
|
calloc(count == 0 ? 1 : count, sizeof(*features->keypoints)));
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
features->keypoints[i].x = (float)(i % 4096U);
|
|
features->keypoints[i].y = (float)(i % 4096U);
|
|
features->keypoints[i].size = 1.0F;
|
|
}
|
|
size_t scalar = type == LARDON3D_FEATURE_DESCRIPTOR_U8 ? 1 : sizeof(float);
|
|
features->descriptor_bytes = (size_t)count * 128 * scalar;
|
|
features->descriptors = static_cast<unsigned char *>(malloc(features->descriptor_bytes));
|
|
if (type == LARDON3D_FEATURE_DESCRIPTOR_U8) {
|
|
features->descriptor_bytes = (size_t)count * 32;
|
|
features->descriptors = static_cast<unsigned char *>(
|
|
realloc(features->descriptors, features->descriptor_bytes));
|
|
for (uint32_t row = 0; row < count; ++row) {
|
|
uint32_t value = row + 1U;
|
|
for (uint32_t column = 0; column < 32; ++column) {
|
|
value ^= value << 13;
|
|
value ^= value >> 17;
|
|
value ^= value << 5;
|
|
features->descriptors[(size_t)row * 32 + column] = (unsigned char)value;
|
|
}
|
|
}
|
|
} else {
|
|
float *values = reinterpret_cast<float *>(features->descriptors);
|
|
size_t value_count = (size_t)count * 128;
|
|
for (size_t i = 0; i < value_count; ++i)
|
|
values[i] = (float)(((i / 128U) * 37U + (i % 128U) * 11U) % 65521U) / 65521.0F;
|
|
}
|
|
}
|
|
|
|
static bool publish(Lardon3DAppState *state, uint64_t image_id, const char *kind,
|
|
Lardon3DFeatureDescriptorType type, uint32_t count, unsigned char salt,
|
|
Lardon3DProjectDbFeatureSet *set) {
|
|
Lardon3DExtractedFeatures features;
|
|
fill_features(&features, count, type, salt);
|
|
unsigned char fingerprint[32];
|
|
memset(fingerprint, salt, sizeof(fingerprint));
|
|
uint32_t dimension = type == LARDON3D_FEATURE_DESCRIPTOR_U8 ? 32 : 128;
|
|
Lardon3DFeatureStoreResult result = lardon3d_feature_store_publish_v2(
|
|
state, image_id, 0, kind, 1, fingerprint, type, dimension, 0, &features, set);
|
|
free(features.keypoints);
|
|
free(features.descriptors);
|
|
return result == LARDON3D_FEATURE_STORE_OK;
|
|
}
|
|
|
|
static bool benchmark_case(const char *kind, Lardon3DMatcherKind matcher_kind,
|
|
Lardon3DFeatureDescriptorType type, uint32_t count) {
|
|
char root_template[] = "/tmp/lardon3d-matcher-bench-XXXXXX";
|
|
char *root = mkdtemp(root_template);
|
|
if (!root) return false;
|
|
std::string db_path = std::string(root) + "/project.db";
|
|
Lardon3DProjectDb *db = nullptr;
|
|
char error[256];
|
|
if (lardon3d_project_db_open(db_path.c_str(), &db, error) != LARDON3D_PROJECT_DB_OK)
|
|
return false;
|
|
Lardon3DProjectDbScanSet scanset;
|
|
Lardon3DProjectDbImage image_a, image_b;
|
|
Lardon3DAppState state;
|
|
lardon3d_app_state_init(&state);
|
|
state.project_loaded = true;
|
|
state.project_db = db;
|
|
(void)snprintf(state.project_path, sizeof(state.project_path), "%s", root);
|
|
bool ok = lardon3d_project_db_create_scanset(db, "benchmark", &scanset) ==
|
|
LARDON3D_PROJECT_DB_OK &&
|
|
register_image(db, scanset.scanset_id, 1, &image_a) &&
|
|
register_image(db, scanset.scanset_id, 2, &image_b);
|
|
Lardon3DProjectDbFeatureSet set_a, set_b;
|
|
ok = ok && publish(&state, image_a.image_id, kind, type, count, 3, &set_a) &&
|
|
publish(&state, image_b.image_id, kind, type, count, 4, &set_b);
|
|
Lardon3DProjectDbCandidatePair pair;
|
|
ok = ok && lardon3d_project_db_create_candidate_pair(
|
|
db, image_a.image_id, image_b.image_id, 1, &pair) == LARDON3D_PROJECT_DB_OK;
|
|
std::vector<Sample> samples;
|
|
Lardon3DMatcherParams params = {matcher_kind, lardon3d_matcher_default_ratio(matcher_kind)};
|
|
for (int repetition = 0; ok && repetition < 3; ++repetition) {
|
|
Lardon3DMatcherStats stats;
|
|
params.ratio_threshold = lardon3d_matcher_default_ratio(matcher_kind) -
|
|
(float)repetition * 0.01F;
|
|
Lardon3DProjectDbMatchResult result;
|
|
ok = lardon3d_matcher_match_and_publish_profiled(
|
|
root, db, &pair, &set_a, &set_b, ¶ms, &result, &stats) == LARDON3D_MATCHER_OK;
|
|
samples.push_back({stats.feature_open_ns, stats.descriptor_read_ns, stats.knn_ns,
|
|
stats.filter_ns, stats.canonicalize_ns, stats.serialize_ns,
|
|
stats.sha256_ns, stats.publication_ns, stats.database_ns,
|
|
stats.total_ns});
|
|
}
|
|
if (ok) {
|
|
std::vector<uint64_t> values;
|
|
auto field = [&](auto getter) {
|
|
values.clear();
|
|
for (const Sample &sample : samples) values.push_back(getter(sample));
|
|
return (double)median(values) / 1000000.0;
|
|
};
|
|
printf("%s,%u,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f\n", kind, count,
|
|
field([](const Sample &s) { return s.open_ns; }),
|
|
field([](const Sample &s) { return s.read_ns; }),
|
|
field([](const Sample &s) { return s.knn_ns; }),
|
|
field([](const Sample &s) { return s.filter_ns; }),
|
|
field([](const Sample &s) { return s.canonicalize_ns; }),
|
|
field([](const Sample &s) { return s.serialize_ns; }),
|
|
field([](const Sample &s) { return s.sha_ns; }),
|
|
field([](const Sample &s) { return s.publish_ns; }),
|
|
field([](const Sample &s) { return s.database_ns; }),
|
|
field([](const Sample &s) { return s.total_ns; }));
|
|
std::vector<uint64_t> reuse_totals;
|
|
for (int repetition = 0; repetition < 5; ++repetition) {
|
|
Lardon3DMatcherStats reuse_stats;
|
|
Lardon3DProjectDbMatchResult reused;
|
|
ok = ok && lardon3d_matcher_match_and_publish_profiled(
|
|
root, db, &pair, &set_a, &set_b, ¶ms, &reused, &reuse_stats) ==
|
|
LARDON3D_MATCHER_OK;
|
|
reuse_totals.push_back(reuse_stats.total_ns);
|
|
}
|
|
printf("%s-reuse,%u,0,0,0,0,0,0,0,0,0,%.3f\n", kind, count,
|
|
(double)median(reuse_totals) / 1000000.0);
|
|
}
|
|
lardon3d_project_db_close(db);
|
|
std::filesystem::remove_all(root);
|
|
return ok;
|
|
}
|
|
|
|
int main(void) {
|
|
printf("kind,count,open_ms,read_ms,knn_ms,filter_ms,canonicalize_ms,serialize_ms,"
|
|
"sha_ms,publish_ms,db_ms,total_ms\n");
|
|
const uint32_t sizes[] = {64, 256, 1024, 4096, 8192};
|
|
for (uint32_t count : sizes) {
|
|
if (!benchmark_case("orb", LARDON3D_MATCHER_ORB_BF,
|
|
LARDON3D_FEATURE_DESCRIPTOR_U8, count) ||
|
|
!benchmark_case("sift", LARDON3D_MATCHER_SIFT_BF,
|
|
LARDON3D_FEATURE_DESCRIPTOR_F32, count) ||
|
|
!benchmark_case("rootsift", LARDON3D_MATCHER_ROOTSIFT_BF,
|
|
LARDON3D_FEATURE_DESCRIPTOR_F32, count))
|
|
return EXIT_FAILURE;
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|