Improve face alarm log explainability
This commit is contained in:
parent
a458b46f42
commit
ee4edd2d93
@ -37,6 +37,8 @@ struct FaceRecogItem {
|
||||
float best_sim = 0.0f;
|
||||
bool unknown = true;
|
||||
float second_sim = 0.0f;
|
||||
int candidate_person_id = -1;
|
||||
std::string candidate_name;
|
||||
|
||||
bool has_landmarks = false;
|
||||
std::array<Point2f, 5> landmarks{};
|
||||
|
||||
@ -951,6 +951,8 @@ private:
|
||||
item.has_landmarks = face.has_landmarks;
|
||||
item.landmarks = face.landmarks;
|
||||
|
||||
item.candidate_person_id = sr.best_person_id;
|
||||
item.candidate_name = sr.best_name;
|
||||
item.best_person_id = accept ? sr.best_person_id : -1;
|
||||
item.best_name = accept ? sr.best_name : "unknown";
|
||||
item.best_sim = sr.best_sim;
|
||||
|
||||
@ -9,12 +9,21 @@
|
||||
|
||||
namespace rk3588 {
|
||||
|
||||
struct FaceAlarmMatch {
|
||||
bool unknown = true;
|
||||
int candidate_person_id = -1;
|
||||
std::string candidate_name;
|
||||
float best_sim = 0.0f;
|
||||
float second_sim = 0.0f;
|
||||
};
|
||||
|
||||
struct AlarmEvent {
|
||||
std::string rule_name;
|
||||
std::string node_id;
|
||||
uint64_t timestamp_ms;
|
||||
uint64_t frame_id;
|
||||
std::vector<Detection> detections;
|
||||
std::vector<FaceAlarmMatch> face_matches;
|
||||
std::vector<BehaviorEventItem> behavior_events;
|
||||
std::string snapshot_url;
|
||||
std::string clip_url;
|
||||
|
||||
@ -70,6 +70,24 @@ void LogAction::Execute(AlarmEvent& event, std::shared_ptr<Frame> /*frame*/) {
|
||||
oss << "]";
|
||||
}
|
||||
|
||||
if (!event.face_matches.empty()) {
|
||||
oss << " face_matches=[";
|
||||
for (size_t i = 0; i < event.face_matches.size(); ++i) {
|
||||
const auto& match = event.face_matches[i];
|
||||
if (i > 0) oss << ", ";
|
||||
const float sim_margin = match.best_sim - match.second_sim;
|
||||
const std::string candidate = match.candidate_name.empty() ? "n/a" : match.candidate_name;
|
||||
oss << "{status=" << (match.unknown ? "unknown" : "known")
|
||||
<< " candidate=" << candidate
|
||||
<< " candidate_id=" << match.candidate_person_id
|
||||
<< " best_sim=" << std::fixed << std::setprecision(2) << match.best_sim
|
||||
<< " second_sim=" << std::fixed << std::setprecision(2) << match.second_sim
|
||||
<< " sim_margin=" << std::fixed << std::setprecision(2) << sim_margin
|
||||
<< "}";
|
||||
}
|
||||
oss << "]";
|
||||
}
|
||||
|
||||
if (!event.snapshot_url.empty()) {
|
||||
oss << " snapshot=" << event.snapshot_url;
|
||||
}
|
||||
|
||||
@ -519,7 +519,9 @@ private:
|
||||
bool matched = false;
|
||||
std::string matched_name;
|
||||
std::vector<Detection> dets;
|
||||
std::vector<FaceAlarmMatch> face_matches;
|
||||
dets.reserve(3);
|
||||
face_matches.reserve(3);
|
||||
|
||||
for (const auto& it : frame->face_recog->items) {
|
||||
if (rule.min_face_area_ratio > 0.0f && img_area > 0.0) {
|
||||
@ -576,6 +578,13 @@ private:
|
||||
d.bbox = it.bbox;
|
||||
d.track_id = it.best_person_id;
|
||||
dets.push_back(std::move(d));
|
||||
FaceAlarmMatch fm;
|
||||
fm.unknown = it.unknown;
|
||||
fm.candidate_person_id = it.candidate_person_id;
|
||||
fm.candidate_name = it.candidate_name;
|
||||
fm.best_sim = it.best_sim;
|
||||
fm.second_sim = it.second_sim;
|
||||
face_matches.push_back(std::move(fm));
|
||||
if (dets.size() >= 3) break;
|
||||
}
|
||||
|
||||
@ -604,7 +613,7 @@ private:
|
||||
fake.rule_name = rule.name + ":" + matched_name;
|
||||
}
|
||||
fake.matched_detections = std::move(dets);
|
||||
TriggerAlarm(fake, frame);
|
||||
TriggerAlarm(fake, frame, std::move(face_matches));
|
||||
}
|
||||
}
|
||||
|
||||
@ -654,7 +663,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerAlarm(const RuleMatchResult& result, FramePtr frame) {
|
||||
void TriggerAlarm(const RuleMatchResult& result, FramePtr frame, std::vector<FaceAlarmMatch> face_matches = {}) {
|
||||
++alarm_count_;
|
||||
|
||||
AlarmEvent event;
|
||||
@ -664,6 +673,7 @@ private:
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
event.frame_id = frame->frame_id;
|
||||
event.detections = result.matched_detections;
|
||||
event.face_matches = std::move(face_matches);
|
||||
event.behavior_events = result.matched_behavior_events;
|
||||
|
||||
AlarmJob job;
|
||||
|
||||
@ -46,6 +46,7 @@ add_executable(rk3588_gtests
|
||||
test_action_recog.cpp
|
||||
test_event_fusion.cpp
|
||||
test_alarm_behavior_events.cpp
|
||||
test_log_action.cpp
|
||||
test_infer_backend.cpp
|
||||
test_image_processor.cpp
|
||||
test_codec_backend.cpp
|
||||
@ -59,6 +60,7 @@ add_executable(rk3588_gtests
|
||||
${CMAKE_SOURCE_DIR}/plugins/action_recog/action_recog_node.cpp
|
||||
${CMAKE_SOURCE_DIR}/plugins/event_fusion/event_fusion_node.cpp
|
||||
${CMAKE_SOURCE_DIR}/plugins/logic_gate/color_analyzer.cpp
|
||||
${CMAKE_SOURCE_DIR}/plugins/alarm/actions/log_action.cpp
|
||||
${CMAKE_SOURCE_DIR}/plugins/alarm/rule_engine.cpp
|
||||
)
|
||||
|
||||
|
||||
56
tests/test_log_action.cpp
Normal file
56
tests/test_log_action.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "utils/logger.h"
|
||||
#include "../plugins/alarm/actions/log_action.h"
|
||||
|
||||
namespace rk3588 {
|
||||
namespace {
|
||||
|
||||
TEST(LogActionTest, IncludesFaceRecognitionDetailsInAlarmLog) {
|
||||
Logger::Instance().SetLevel(LogLevel::Info);
|
||||
|
||||
LogAction action;
|
||||
SimpleJson cfg(SimpleJson::Object{
|
||||
{"level", SimpleJson(std::string("info"))},
|
||||
{"include_detections", SimpleJson(true)},
|
||||
});
|
||||
ASSERT_TRUE(action.Init(cfg));
|
||||
|
||||
AlarmEvent event;
|
||||
event.node_id = "alarm_face_cam1";
|
||||
event.rule_name = "unknown_face";
|
||||
event.frame_id = 42;
|
||||
Detection det;
|
||||
det.cls_id = -1;
|
||||
det.score = 0.44f;
|
||||
det.bbox = Rect{10.0f, 20.0f, 30.0f, 40.0f};
|
||||
det.track_id = -1;
|
||||
event.detections.push_back(det);
|
||||
|
||||
FaceAlarmMatch match;
|
||||
match.unknown = true;
|
||||
match.candidate_person_id = 7;
|
||||
match.candidate_name = "alice";
|
||||
match.best_sim = 0.44f;
|
||||
match.second_sim = 0.41f;
|
||||
event.face_matches.push_back(match);
|
||||
|
||||
action.Execute(event, std::make_shared<Frame>());
|
||||
|
||||
const auto lines = Logger::Instance().RecentLines(10);
|
||||
ASSERT_FALSE(lines.empty());
|
||||
const std::string& line = lines.back();
|
||||
EXPECT_NE(line.find("rule=unknown_face"), std::string::npos);
|
||||
EXPECT_NE(line.find("candidate=alice"), std::string::npos);
|
||||
EXPECT_NE(line.find("status=unknown"), 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);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace rk3588
|
||||
Loading…
Reference in New Issue
Block a user