仓库结构: edge/ 设备端(原根目录设备端代码整体移入) control/ 管理端(清理后) docs/ 文档(PRD 移入 design/) README.md 根导航(新增) 清理: - control/.brainstorm 临时草稿删除 - control 根级重复文档(API表/PRD_04)并入 docs/design/ - control/plan.md -> docs/implementation/control-plan.md - control/safesightd-linux-arm64 二进制取消版本控制(.gitignore) - edge/transform 模型转换产物归入 models/,onnx/pt 大源文件取消跟踪(.gitignore) - Readme.md(PRD) -> docs/design/PRD_Product_v1.2.md(避开 README 大小写冲突) 更新: - 根 README.md 导航、docs/README.md 文档索引 - deployment.md/检查表路径加 edge/ 前缀 - .gitignore 重写(edge/control 分区规则)
88 lines
2.8 KiB
C++
88 lines
2.8 KiB
C++
#pragma once
|
|
|
|
#include <limits>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace rk3588 {
|
|
|
|
struct GallerySearchEntry {
|
|
int person_id = -1;
|
|
std::string name;
|
|
std::vector<float> emb;
|
|
};
|
|
|
|
struct GallerySearchResult {
|
|
int best_person_id = -1;
|
|
std::string best_name;
|
|
float best_sim = 0.0f;
|
|
float second_sim = 0.0f;
|
|
};
|
|
|
|
template <typename EntryT, typename PersonIdFn, typename NameFn, typename EmbFn>
|
|
GallerySearchResult SearchTop2ByPersonGeneric(const std::vector<EntryT>& entries,
|
|
const std::vector<float>& emb_normed,
|
|
PersonIdFn person_id_fn,
|
|
NameFn name_fn,
|
|
EmbFn emb_fn) {
|
|
GallerySearchResult r;
|
|
if (entries.empty() || emb_normed.empty()) return r;
|
|
|
|
std::unordered_map<int, std::pair<std::string, float>> per_person_best;
|
|
per_person_best.reserve(entries.size());
|
|
|
|
for (const auto& entry : entries) {
|
|
const auto& entry_emb = emb_fn(entry);
|
|
if (entry_emb.size() != emb_normed.size()) continue;
|
|
|
|
float sim = 0.0f;
|
|
for (size_t i = 0; i < emb_normed.size(); ++i) {
|
|
sim += emb_normed[i] * entry_emb[i];
|
|
}
|
|
|
|
const int person_id = person_id_fn(entry);
|
|
auto it = per_person_best.find(person_id);
|
|
if (it == per_person_best.end() || sim > it->second.second) {
|
|
per_person_best[person_id] = std::make_pair(name_fn(entry), sim);
|
|
}
|
|
}
|
|
|
|
float best = -std::numeric_limits<float>::infinity();
|
|
float second = -std::numeric_limits<float>::infinity();
|
|
int best_person_id = -1;
|
|
std::string best_name;
|
|
|
|
for (const auto& kv : per_person_best) {
|
|
const float sim = kv.second.second;
|
|
if (sim > best) {
|
|
second = best;
|
|
best = sim;
|
|
best_person_id = kv.first;
|
|
best_name = kv.second.first;
|
|
} else if (sim > second) {
|
|
second = sim;
|
|
}
|
|
}
|
|
|
|
if (best_person_id >= 0) {
|
|
r.best_person_id = best_person_id;
|
|
r.best_name = best_name;
|
|
r.best_sim = best;
|
|
r.second_sim = std::isfinite(second) ? second : 0.0f;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
inline GallerySearchResult SearchTop2ByPerson(const std::vector<GallerySearchEntry>& entries,
|
|
const std::vector<float>& emb_normed) {
|
|
return SearchTop2ByPersonGeneric(
|
|
entries, emb_normed,
|
|
[](const GallerySearchEntry& entry) { return entry.person_id; },
|
|
[](const GallerySearchEntry& entry) -> const std::string& { return entry.name; },
|
|
[](const GallerySearchEntry& entry) -> const std::vector<float>& { return entry.emb; });
|
|
}
|
|
|
|
} // namespace rk3588
|