Add face recognition debug switch

This commit is contained in:
tian 2026-04-15 11:02:18 +08:00
parent 7e8a4c870e
commit ba4bf5339d
5 changed files with 151 additions and 1 deletions

View File

@ -103,6 +103,11 @@
"load_on_start": true,
"expected_dim": 512,
"dtype": "auto"
},
"debug": {
"enabled": false,
"log_matches": false,
"min_log_interval_ms": 1000
}
},
{

View File

@ -0,0 +1,57 @@
#pragma once
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <iomanip>
#include <sstream>
#include <string>
#include "face/face_result.h"
#include "utils/simple_json.h"
namespace rk3588 {
struct FaceRecogDebugConfig {
bool enabled = false;
bool log_matches = false;
int min_log_interval_ms = 2000;
};
inline FaceRecogDebugConfig ParseFaceRecogDebugConfig(
const SimpleJson& cfg, const FaceRecogDebugConfig& base = {}) {
FaceRecogDebugConfig out = base;
const SimpleJson* debug = cfg.Find("debug");
if (!debug || !debug->IsObject()) return out;
out.enabled = debug->ValueOr<bool>("enabled", out.enabled);
out.log_matches = debug->ValueOr<bool>("log_matches", out.log_matches);
out.min_log_interval_ms = std::max(0, debug->ValueOr<int>("min_log_interval_ms", out.min_log_interval_ms));
return out;
}
inline std::string BuildFaceRecogDebugSummaryLine(
const std::string& node_id, uint64_t frame_id, const FaceRecogItem& item) {
const float sim_margin = item.best_sim - item.second_sim;
const std::string candidate = item.candidate_name.empty() ? "n/a" : item.candidate_name;
const std::string status = item.unknown ? "unknown" : "known";
std::ostringstream oss;
oss << "[ai_face_recog] match"
<< " id=" << node_id
<< " frame=" << frame_id
<< " status=" << status
<< " candidate=" << candidate
<< " candidate_id=" << item.candidate_person_id
<< " best_sim=" << std::fixed << std::setprecision(2) << item.best_sim
<< " second_sim=" << std::fixed << std::setprecision(2) << item.second_sim
<< " sim_margin=" << std::fixed << std::setprecision(2) << sim_margin
<< " bbox=(" << static_cast<int>(std::lround(item.bbox.x))
<< "," << static_cast<int>(std::lround(item.bbox.y))
<< "," << static_cast<int>(std::lround(item.bbox.w))
<< "," << static_cast<int>(std::lround(item.bbox.h))
<< ")";
return oss.str();
}
} // namespace rk3588

View File

@ -13,6 +13,7 @@
#include <vector>
#include "hw/i_infer_backend.h"
#include "face/face_recog_debug.h"
#include "face/face_result.h"
#include "node.h"
#include "utils/dma_alloc.h"
@ -570,6 +571,8 @@ struct FaceRecogConfigSnapshot {
int gallery_expected_dim = 512;
std::string gallery_dtype = "auto";
int gallery_reload_seq = 0;
FaceRecogDebugConfig debug;
};
static bool BuildFaceRecogConfigSnapshot(const SimpleJson& config,
@ -627,6 +630,7 @@ static bool BuildFaceRecogConfigSnapshot(const SimpleJson& config,
snap->gallery_dtype = g->ValueOr<std::string>("dtype", snap->gallery_dtype);
snap->gallery_reload_seq = g->ValueOr<int>("reload_seq", snap->gallery_reload_seq);
}
snap->debug = ParseFaceRecogDebugConfig(config, snap->debug);
for (auto& c : snap->gallery_backend) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
for (auto& c : snap->gallery_dtype) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
@ -719,10 +723,16 @@ public:
const bool align = cfg ? cfg->align : false;
const float thr_accept = cfg ? cfg->thr_accept : 0.0f;
const float thr_margin = cfg ? cfg->thr_margin : 0.0f;
const bool debug_enabled = cfg ? cfg->debug.enabled : false;
const bool debug_log_matches = cfg ? cfg->debug.log_matches : false;
const int debug_interval_ms = cfg ? cfg->debug.min_log_interval_ms : 0;
LogInfo("[ai_face_recog] start id=" + id_ + " align=" + std::string(align ? "true" : "false") +
" thr_accept=" + std::to_string(thr_accept) +
" thr_margin=" + std::to_string(thr_margin) +
" infer_interval_ms=" + std::to_string(infer_interval_ms_));
" infer_interval_ms=" + std::to_string(infer_interval_ms_) +
" debug=" + std::string(debug_enabled ? "true" : "false") +
" debug_log_matches=" + std::string(debug_log_matches ? "true" : "false") +
" debug_min_log_interval_ms=" + std::to_string(debug_interval_ms));
return true;
}
@ -960,6 +970,7 @@ private:
item.unknown = !accept;
if (cfg->emit_embedding) item.embedding = emb;
MaybeLogDebugMatch(*cfg, frame->frame_id, frame->pts, item);
rr.items.push_back(std::move(item));
}
@ -967,6 +978,24 @@ private:
frame->face_recog = std::make_shared<FaceRecogResult>(std::move(rr));
}
void MaybeLogDebugMatch(const FaceRecogConfigSnapshot& cfg, uint64_t frame_id, uint64_t pts_us,
const FaceRecogItem& item) {
if (!cfg.debug.enabled || !cfg.debug.log_matches) return;
if (cfg.debug.min_log_interval_ms > 0) {
const int64_t now_key_ms = pts_us > 0
? static_cast<int64_t>(pts_us / 1000ULL)
: static_cast<int64_t>(frame_id);
if (now_key_ms > 0 &&
(now_key_ms - last_debug_log_pts_ms_) < cfg.debug.min_log_interval_ms) {
return;
}
last_debug_log_pts_ms_ = now_key_ms;
}
LogInfo(BuildFaceRecogDebugSummaryLine(id_, frame_id, item));
}
#endif
std::string id_;
@ -989,6 +1018,7 @@ private:
int64_t infer_interval_ms_ = 0;
int64_t infer_phase_ms_ = 0;
int64_t last_infer_pts_ms_ = 0;
int64_t last_debug_log_pts_ms_ = std::numeric_limits<int64_t>::min();
};
REGISTER_NODE(AiFaceRecogNode, "ai_face_recog");

View File

@ -47,6 +47,7 @@ add_executable(rk3588_gtests
test_event_fusion.cpp
test_alarm_behavior_events.cpp
test_log_action.cpp
test_face_recog_debug.cpp
test_infer_backend.cpp
test_image_processor.cpp
test_codec_backend.cpp

View File

@ -0,0 +1,57 @@
#include <gtest/gtest.h>
#include <string>
#include "face/face_recog_debug.h"
#include "utils/simple_json.h"
namespace rk3588 {
namespace {
TEST(FaceRecogDebugTest, ParsesDebugConfigWithDefaultsAndOverrides) {
FaceRecogDebugConfig defaults;
EXPECT_FALSE(defaults.enabled);
EXPECT_FALSE(defaults.log_matches);
EXPECT_EQ(defaults.min_log_interval_ms, 2000);
SimpleJson::Object debug_obj;
debug_obj.emplace("enabled", SimpleJson(true));
debug_obj.emplace("log_matches", SimpleJson(true));
debug_obj.emplace("min_log_interval_ms", SimpleJson(350.0));
SimpleJson::Object root_obj;
root_obj.emplace("debug", SimpleJson(std::move(debug_obj)));
SimpleJson cfg(std::move(root_obj));
FaceRecogDebugConfig parsed = ParseFaceRecogDebugConfig(cfg, defaults);
EXPECT_TRUE(parsed.enabled);
EXPECT_TRUE(parsed.log_matches);
EXPECT_EQ(parsed.min_log_interval_ms, 350);
}
TEST(FaceRecogDebugTest, BuildsSummaryLineForUnknownCandidate) {
FaceRecogItem item;
item.bbox = Rect{10.0f, 20.0f, 30.0f, 40.0f};
item.candidate_person_id = 7;
item.candidate_name = "alice";
item.best_name = "unknown";
item.best_sim = 0.44f;
item.second_sim = 0.41f;
item.unknown = true;
const std::string line = BuildFaceRecogDebugSummaryLine("face_recog", 42, item);
EXPECT_NE(line.find("[ai_face_recog] match"), std::string::npos);
EXPECT_NE(line.find("id=face_recog"), std::string::npos);
EXPECT_NE(line.find("frame=42"), std::string::npos);
EXPECT_NE(line.find("status=unknown"), std::string::npos);
EXPECT_NE(line.find("candidate=alice"), std::string::npos);
EXPECT_NE(line.find("candidate_id=7"), std::string::npos);
EXPECT_NE(line.find("best_sim=0.44"), std::string::npos);
EXPECT_NE(line.find("second_sim=0.41"), std::string::npos);
EXPECT_NE(line.find("sim_margin=0.03"), std::string::npos);
EXPECT_NE(line.find("bbox=(10,20,30,40)"), std::string::npos);
}
} // namespace
} // namespace rk3588