feat(engine): HDF5-native galleries with embedded calibration; TensorRT backends; scene detection
Gallery format switches from JSON to HDF5 exclusively (JSON read-only kept for back-compat): save_gallery always writes HDF5, and the fitted Platt-sigmoid calibration (a, b, valid, hash) is now embedded directly in the gallery file instead of a sidecar .calib_cache.json — identity_matcher reads it from the loaded gallery and writes back only when the embeddings actually changed (hash mismatch), skipping the O(n^2) refit otherwise. Also includes: TensorRT inference backend support (ort_backend.cpp, trt_backend.cpp), gemm_backend improvements, TransNetV2-based scene-boundary detection wired through frame_source/face_tracker/main, and CMake build target updates for the new sources. Bumps the KPN submodule to feature/persistent-pipeline-reuse (push_blocking backpressure, node_ptr/node_stats introspection, ObjectVariantNodeWrapper for stateful functors) — needed by the optimizer's sae_kpn Python bindings.
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
// ── Gallery GEMM backend ──────────────────────────────────────────────────────
|
||||
// GPU similarity engine for the identity matcher. Uploads the reference gallery
|
||||
// once and computes the per-frame similarity matrix with a single SGEMM. The GPU
|
||||
// math library is selected at compile time by CMake (SAE_GEMM_BACKEND):
|
||||
// cuBLAS/CUDA or rocBLAS/HIP. This is the ONLY translation unit that includes
|
||||
// cublas/cuda or rocblas/hip headers.
|
||||
// Similarity engine for the identity matcher. Uploads the reference gallery
|
||||
// once and computes the per-frame similarity matrix with a single SGEMM. The
|
||||
// math backend is selected at compile time by CMake (SAE_GEMM_BACKEND):
|
||||
// cuBLAS/CUDA, rocBLAS/HIP, or a portable CPU reference (SAE_GEMM_CPU). The two
|
||||
// GPU paths are the ONLY translation units that include cublas/cuda or
|
||||
// rocblas/hip headers; the CPU path pulls in no GPU headers at all and exists so
|
||||
// the pipeline can build and be tested on a machine without a GPU (CI).
|
||||
|
||||
#include "inference/similarity.hpp"
|
||||
|
||||
@@ -13,8 +15,10 @@
|
||||
#elif defined(SAE_GEMM_ROCM)
|
||||
#include <hip/hip_runtime.h>
|
||||
#include <rocblas/rocblas.h>
|
||||
#elif defined(SAE_GEMM_CPU)
|
||||
// no external headers — portable reference implementation below
|
||||
#else
|
||||
#error "gemm_backend.cpp requires SAE_GEMM_CUDA or SAE_GEMM_ROCM to be defined"
|
||||
#error "gemm_backend.cpp requires SAE_GEMM_CUDA, SAE_GEMM_ROCM or SAE_GEMM_CPU to be defined"
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
@@ -26,6 +30,64 @@
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kDim = 512;
|
||||
|
||||
#if defined(SAE_GEMM_CPU)
|
||||
|
||||
// ── CPU reference engine ──────────────────────────────────────────────────────
|
||||
// Portable, dependency-free path used for CI and as the correctness oracle for
|
||||
// the GPU backends. The gallery is L2-normalised (as are the queries), so each
|
||||
// similarity is a plain dot product. S is stored column-major to match the GPU
|
||||
// backends: the gallery similarities for face fi start at result + fi*n_gallery.
|
||||
class SimilarityEngine final : public ISimilarityEngine {
|
||||
public:
|
||||
SimilarityEngine(const float* gallery_row_major, int n_gallery, int max_faces)
|
||||
: n_gallery_(n_gallery), max_faces_(max_faces),
|
||||
gallery_(gallery_row_major,
|
||||
gallery_row_major + static_cast<size_t>(n_gallery) * kDim)
|
||||
{
|
||||
host_sims_.resize(static_cast<size_t>(max_faces_) * n_gallery_);
|
||||
std::cerr << "[similarity] CPU reference engine: gallery resident in host RAM ("
|
||||
<< (gallery_.size() * sizeof(float)) / (1024 * 1024) << " MiB)\n";
|
||||
}
|
||||
|
||||
int max_faces() const override { return max_faces_; }
|
||||
|
||||
const float* compute(const float* query_row_major, int n_faces) override {
|
||||
if (n_faces <= 0) return host_sims_.data();
|
||||
if (n_faces > max_faces_)
|
||||
throw std::runtime_error("SimilarityEngine: n_faces exceeds max_faces");
|
||||
|
||||
// S(g, f) col-major = dot(gallery[g], query[f]).
|
||||
for (int f = 0; f < n_faces; ++f) {
|
||||
const float* q = query_row_major + static_cast<size_t>(f) * kDim;
|
||||
float* out = host_sims_.data() + static_cast<size_t>(f) * n_gallery_;
|
||||
for (int g = 0; g < n_gallery_; ++g) {
|
||||
const float* row = gallery_.data() + static_cast<size_t>(g) * kDim;
|
||||
float acc = 0.f;
|
||||
for (int d = 0; d < kDim; ++d) acc += row[d] * q[d];
|
||||
out[g] = acc;
|
||||
}
|
||||
}
|
||||
return host_sims_.data();
|
||||
}
|
||||
|
||||
private:
|
||||
int n_gallery_{0};
|
||||
int max_faces_{0};
|
||||
std::vector<float> gallery_; // n_gallery × 512, row-major
|
||||
std::vector<float> host_sims_; // max_faces × n_gallery, column-major
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<ISimilarityEngine> make_similarity_engine(
|
||||
const float* gallery_row_major, int n_gallery, int max_faces) {
|
||||
return std::make_unique<SimilarityEngine>(gallery_row_major, n_gallery, max_faces);
|
||||
}
|
||||
|
||||
#else // GPU backends (CUDA / ROCM)
|
||||
|
||||
struct GpuError : std::runtime_error {
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
@@ -102,8 +164,6 @@ inline const char* backend_name() { return "rocBLAS/HIP"; }
|
||||
|
||||
#endif
|
||||
|
||||
constexpr int kDim = 512;
|
||||
|
||||
class SimilarityEngine final : public ISimilarityEngine {
|
||||
public:
|
||||
SimilarityEngine(const float* gallery_row_major, int n_gallery, int max_faces)
|
||||
@@ -175,3 +235,5 @@ std::unique_ptr<ISimilarityEngine> make_similarity_engine(
|
||||
const float* gallery_row_major, int n_gallery, int max_faces) {
|
||||
return std::make_unique<SimilarityEngine>(gallery_row_major, n_gallery, max_faces);
|
||||
}
|
||||
|
||||
#endif // SAE_GEMM_CPU / GPU backends
|
||||
|
||||
Reference in New Issue
Block a user