测试优化代码
This commit is contained in:
parent
9ec1f7143f
commit
4399ecd601
@ -41,7 +41,7 @@
|
||||
"model_path": "./third_party/rknpu2/examples/rknn_yolov5_demo/model/RK3588/yolov5s-640-640.rknn",
|
||||
"model_version": "v5",
|
||||
"num_classes": 80,
|
||||
"conf": 0.25,
|
||||
"conf": 0.35,
|
||||
"nms": 0.45,
|
||||
"class_filter": []
|
||||
},
|
||||
@ -51,7 +51,7 @@
|
||||
"role": "filter",
|
||||
"enable": true,
|
||||
"model_path": "./models/RetinaFace_mobile320.rknn",
|
||||
"conf": 0.6,
|
||||
"conf": 0.7,
|
||||
"nms": 0.4,
|
||||
"max_faces": 10,
|
||||
"output_landmarks": true,
|
||||
@ -100,14 +100,14 @@
|
||||
"id": "gate_face_cam1",
|
||||
"type": "gate",
|
||||
"role": "filter",
|
||||
"enable": false,
|
||||
"enable": true,
|
||||
"state_key": "cam1_sample_full_pipeline",
|
||||
"class_ids": [0],
|
||||
"require_confirmed": true,
|
||||
"min_count": 1,
|
||||
"max_age_ms": 1000,
|
||||
"min_interval_ms": 200,
|
||||
"min_box_area_ratio": 0.02
|
||||
"min_count": 2,
|
||||
"max_age_ms": 1500,
|
||||
"min_interval_ms": 300,
|
||||
"min_box_area_ratio": 0.03
|
||||
},
|
||||
{
|
||||
"id": "pre_face_cam1",
|
||||
@ -175,8 +175,14 @@
|
||||
"name": "person_in_view",
|
||||
"class_ids": [0],
|
||||
"roi": { "x": 0.0, "y": 0.0, "w": 1.0, "h": 1.0 },
|
||||
"min_duration_ms": 0,
|
||||
"cooldown_ms": 3000
|
||||
"min_score": 0.4,
|
||||
"min_box_area_ratio": 0.02,
|
||||
"require_track_id": true,
|
||||
"min_duration_ms": 1500,
|
||||
"min_hits": 3,
|
||||
"hit_window_ms": 1500,
|
||||
"cooldown_ms": 5000,
|
||||
"per_track_cooldown_ms": 5000
|
||||
}
|
||||
],
|
||||
"face_rules": [],
|
||||
@ -240,8 +246,8 @@
|
||||
"labels": [],
|
||||
"rules": [],
|
||||
"face_rules": [
|
||||
{ "name": "unknown_face", "type": "unknown", "cooldown_ms": 5000, "min_sim": 0.0 },
|
||||
{ "name": "known_person", "type": "person", "cooldown_ms": 5000, "min_sim": 0.5 }
|
||||
{ "name": "unknown_face", "type": "unknown", "cooldown_ms": 7000, "min_sim": 0.35, "min_hits": 2, "hit_window_ms": 1500, "min_face_area_ratio": 0.01, "min_face_aspect": 0.6, "max_face_aspect": 1.6 },
|
||||
{ "name": "known_person", "type": "person", "cooldown_ms": 7000, "min_sim": 0.6, "min_hits": 2, "hit_window_ms": 1500, "min_face_area_ratio": 0.01, "min_face_aspect": 0.6, "max_face_aspect": 1.6 }
|
||||
],
|
||||
"actions": {
|
||||
"log": { "enable": false, "level": "info" },
|
||||
|
||||
@ -85,8 +85,14 @@
|
||||
"name": "person_in_view",
|
||||
"class_ids": [0],
|
||||
"roi": { "x": 0.0, "y": 0.0, "w": 1.0, "h": 1.0 },
|
||||
"min_score": 0.4,
|
||||
"min_box_area_ratio": 0.02,
|
||||
"require_track_id": true,
|
||||
"min_duration_ms": 500,
|
||||
"cooldown_ms": 15000
|
||||
"min_hits": 3,
|
||||
"hit_window_ms": 1500,
|
||||
"cooldown_ms": 15000,
|
||||
"per_track_cooldown_ms": 15000
|
||||
}
|
||||
],
|
||||
"actions": {
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@ -446,6 +447,12 @@ private:
|
||||
Kind kind = Kind::Unknown;
|
||||
std::vector<std::string> persons; // used when kind==Person; empty => any known person
|
||||
int cooldown_ms = 5000;
|
||||
int per_person_cooldown_ms = 0;
|
||||
int min_hits = 1;
|
||||
int hit_window_ms = 0;
|
||||
float min_face_area_ratio = 0.0f;
|
||||
float min_face_aspect = 0.0f;
|
||||
float max_face_aspect = 0.0f;
|
||||
float min_sim = 0.0f;
|
||||
};
|
||||
|
||||
@ -462,6 +469,12 @@ private:
|
||||
for (auto& c : type) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||
r.kind = (type == "person") ? FaceRule::Kind::Person : FaceRule::Kind::Unknown;
|
||||
r.cooldown_ms = std::max(0, item.ValueOr<int>("cooldown_ms", r.cooldown_ms));
|
||||
r.per_person_cooldown_ms = std::max(0, item.ValueOr<int>("per_person_cooldown_ms", r.per_person_cooldown_ms));
|
||||
r.min_hits = std::max(1, item.ValueOr<int>("min_hits", r.min_hits));
|
||||
r.hit_window_ms = std::max(0, item.ValueOr<int>("hit_window_ms", r.hit_window_ms));
|
||||
r.min_face_area_ratio = item.ValueOr<double>("min_face_area_ratio", static_cast<double>(r.min_face_area_ratio));
|
||||
r.min_face_aspect = item.ValueOr<double>("min_face_aspect", static_cast<double>(r.min_face_aspect));
|
||||
r.max_face_aspect = item.ValueOr<double>("max_face_aspect", static_cast<double>(r.max_face_aspect));
|
||||
r.min_sim = item.ValueOr<double>("min_sim", static_cast<double>(r.min_sim));
|
||||
|
||||
if (const SimpleJson* persons = item.Find("persons"); persons && persons->IsArray()) {
|
||||
@ -478,6 +491,8 @@ private:
|
||||
void ParseFaceRules(const SimpleJson& config) {
|
||||
face_rules_ = ParseFaceRulesFromConfig(config);
|
||||
face_last_trigger_.clear();
|
||||
face_person_last_trigger_.clear();
|
||||
face_vote_history_.clear();
|
||||
}
|
||||
|
||||
void EvaluateFaceRulesLocked(const FramePtr& frame) {
|
||||
@ -485,6 +500,9 @@ private:
|
||||
if (!frame || !frame->face_recog || frame->face_recog->items.empty()) return;
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const int img_w = frame->face_recog->img_w > 0 ? frame->face_recog->img_w : frame->width;
|
||||
const int img_h = frame->face_recog->img_h > 0 ? frame->face_recog->img_h : frame->height;
|
||||
const double img_area = (img_w > 0 && img_h > 0) ? static_cast<double>(img_w) * static_cast<double>(img_h) : 0.0;
|
||||
for (const auto& rule : face_rules_) {
|
||||
bool matched = false;
|
||||
std::string matched_name;
|
||||
@ -492,10 +510,21 @@ private:
|
||||
dets.reserve(3);
|
||||
|
||||
for (const auto& it : frame->face_recog->items) {
|
||||
if (rule.min_face_area_ratio > 0.0f && img_area > 0.0) {
|
||||
const double a = static_cast<double>(it.bbox.w) * static_cast<double>(it.bbox.h);
|
||||
const double r = a / img_area;
|
||||
if (r < static_cast<double>(rule.min_face_area_ratio)) continue;
|
||||
}
|
||||
if (rule.min_face_aspect > 0.0f || rule.max_face_aspect > 0.0f) {
|
||||
const double h = std::max(1e-6, static_cast<double>(it.bbox.h));
|
||||
const double aspect = static_cast<double>(it.bbox.w) / h;
|
||||
if (rule.min_face_aspect > 0.0f && aspect < static_cast<double>(rule.min_face_aspect)) continue;
|
||||
if (rule.max_face_aspect > 0.0f && aspect > static_cast<double>(rule.max_face_aspect)) continue;
|
||||
}
|
||||
|
||||
if (rule.kind == FaceRule::Kind::Unknown) {
|
||||
if (!it.unknown) continue;
|
||||
if (it.best_sim < rule.min_sim) continue;
|
||||
matched = true;
|
||||
} else {
|
||||
if (it.unknown) continue;
|
||||
if (it.best_sim < rule.min_sim) continue;
|
||||
@ -509,10 +538,26 @@ private:
|
||||
}
|
||||
if (!ok) continue;
|
||||
}
|
||||
matched = true;
|
||||
matched_name = it.best_name;
|
||||
}
|
||||
|
||||
const std::string key = BuildFaceVoteKey(rule, it);
|
||||
if (rule.per_person_cooldown_ms > 0) {
|
||||
auto it_last = face_person_last_trigger_.find(key);
|
||||
if (it_last != face_person_last_trigger_.end()) {
|
||||
const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - it_last->second).count();
|
||||
if (elapsed < rule.per_person_cooldown_ms) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!CheckFaceVote(rule, key, now)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
matched = true;
|
||||
if (rule.kind == FaceRule::Kind::Person) matched_name = it.best_name;
|
||||
|
||||
Detection d;
|
||||
d.cls_id = -1;
|
||||
d.score = it.best_sim;
|
||||
@ -524,14 +569,21 @@ private:
|
||||
|
||||
if (!matched) continue;
|
||||
|
||||
const auto it_last = face_last_trigger_.find(rule.name);
|
||||
if (it_last != face_last_trigger_.end() && rule.cooldown_ms > 0) {
|
||||
const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - it_last->second).count();
|
||||
if (elapsed < rule.cooldown_ms) {
|
||||
continue;
|
||||
if (rule.per_person_cooldown_ms > 0) {
|
||||
for (const auto& d : dets) {
|
||||
const std::string key = BuildFaceVoteKey(rule, d.track_id, matched_name);
|
||||
face_person_last_trigger_[key] = now;
|
||||
}
|
||||
} else {
|
||||
const auto it_last = face_last_trigger_.find(rule.name);
|
||||
if (it_last != face_last_trigger_.end() && rule.cooldown_ms > 0) {
|
||||
const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - it_last->second).count();
|
||||
if (elapsed < rule.cooldown_ms) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
face_last_trigger_[rule.name] = now;
|
||||
}
|
||||
face_last_trigger_[rule.name] = now;
|
||||
|
||||
RuleMatchResult fake;
|
||||
fake.matched = true;
|
||||
@ -543,6 +595,35 @@ private:
|
||||
TriggerAlarm(fake, frame);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string BuildFaceVoteKey(const FaceRule& rule, const FaceRecogItem& item) {
|
||||
if (rule.kind == FaceRule::Kind::Person) {
|
||||
if (item.best_person_id >= 0) {
|
||||
return rule.name + "#" + std::to_string(item.best_person_id);
|
||||
}
|
||||
return rule.name + "#" + item.best_name;
|
||||
}
|
||||
return rule.name + "#unknown";
|
||||
}
|
||||
|
||||
static std::string BuildFaceVoteKey(const FaceRule& rule, int person_id, const std::string& name) {
|
||||
if (rule.kind == FaceRule::Kind::Person) {
|
||||
if (person_id >= 0) return rule.name + "#" + std::to_string(person_id);
|
||||
return rule.name + "#" + name;
|
||||
}
|
||||
return rule.name + "#unknown";
|
||||
}
|
||||
|
||||
bool CheckFaceVote(const FaceRule& rule, const std::string& key, const std::chrono::steady_clock::time_point& now) {
|
||||
if (rule.min_hits <= 1 || rule.hit_window_ms <= 0) return true;
|
||||
auto& dq = face_vote_history_[key];
|
||||
const auto window = std::chrono::milliseconds(rule.hit_window_ms);
|
||||
while (!dq.empty() && (now - dq.front()) > window) {
|
||||
dq.pop_front();
|
||||
}
|
||||
dq.push_back(now);
|
||||
return static_cast<int>(dq.size()) >= rule.min_hits;
|
||||
}
|
||||
void WorkerLoop() {
|
||||
while (worker_running_.load()) {
|
||||
AlarmJob job;
|
||||
@ -586,6 +667,8 @@ private:
|
||||
RuleEngine rule_engine_;
|
||||
std::vector<FaceRule> face_rules_;
|
||||
std::map<std::string, std::chrono::steady_clock::time_point> face_last_trigger_;
|
||||
std::map<std::string, std::chrono::steady_clock::time_point> face_person_last_trigger_;
|
||||
std::map<std::string, std::deque<std::chrono::steady_clock::time_point>> face_vote_history_;
|
||||
std::shared_ptr<PacketRingBuffer> packet_buffer_;
|
||||
std::vector<std::unique_ptr<IAlarmAction>> actions_;
|
||||
|
||||
|
||||
@ -43,8 +43,14 @@ bool RuleEngine::Init(const SimpleJson& rules_config, const std::vector<std::str
|
||||
for (const auto& rule_json : rules_config.AsArray()) {
|
||||
AlarmRule rule;
|
||||
rule.name = rule_json.ValueOr<std::string>("name", "unnamed_rule");
|
||||
rule.min_score = rule_json.ValueOr<float>("min_score", 0.0f);
|
||||
rule.min_box_area_ratio = rule_json.ValueOr<float>("min_box_area_ratio", 0.0f);
|
||||
rule.require_track_id = rule_json.ValueOr<bool>("require_track_id", false);
|
||||
rule.min_duration_ms = rule_json.ValueOr<int>("min_duration_ms", 0);
|
||||
rule.min_hits = rule_json.ValueOr<int>("min_hits", 1);
|
||||
rule.hit_window_ms = rule_json.ValueOr<int>("hit_window_ms", 0);
|
||||
rule.cooldown_ms = rule_json.ValueOr<int>("cooldown_ms", 5000);
|
||||
rule.per_track_cooldown_ms = rule_json.ValueOr<int>("per_track_cooldown_ms", 0);
|
||||
rule.schedule = rule_json.ValueOr<std::string>("schedule", "");
|
||||
|
||||
// Parse class_ids
|
||||
@ -81,7 +87,12 @@ bool RuleEngine::Init(const SimpleJson& rules_config, const std::vector<std::str
|
||||
oss << "[RuleEngine] loaded rule: " << rule.name
|
||||
<< " class_ids=" << rule.class_ids.size()
|
||||
<< " roi=(" << rule.roi.x << "," << rule.roi.y << "," << rule.roi.w << "," << rule.roi.h << ")"
|
||||
<< " min_score=" << rule.min_score
|
||||
<< " min_box_area_ratio=" << rule.min_box_area_ratio
|
||||
<< " require_track_id=" << (rule.require_track_id ? "true" : "false")
|
||||
<< " min_duration=" << rule.min_duration_ms << "ms"
|
||||
<< " min_hits=" << rule.min_hits
|
||||
<< " hit_window=" << rule.hit_window_ms << "ms"
|
||||
<< " cooldown=" << rule.cooldown_ms << "ms";
|
||||
LogInfo(oss.str());
|
||||
}
|
||||
@ -105,9 +116,11 @@ RuleMatchResult RuleEngine::Evaluate(const std::shared_ptr<Frame>& frame) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check cooldown
|
||||
if (!CheckCooldown(rule.name)) {
|
||||
continue;
|
||||
if (rule.per_track_cooldown_ms <= 0) {
|
||||
// Check rule-level cooldown
|
||||
if (!CheckCooldown(rule.name)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Find matching detections
|
||||
@ -124,17 +137,45 @@ RuleMatchResult RuleEngine::Evaluate(const std::shared_ptr<Frame>& frame) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!PassQuality(rule, det, static_cast<double>(img_w) * static_cast<double>(img_h))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
matched.push_back(det);
|
||||
}
|
||||
|
||||
bool currently_matched = !matched.empty();
|
||||
if (matched.empty()) {
|
||||
CheckDuration(rule.name, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check duration
|
||||
std::vector<Detection> qualified;
|
||||
qualified.reserve(matched.size());
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
for (const auto& det : matched) {
|
||||
if (rule.per_track_cooldown_ms > 0) {
|
||||
if (!CheckPerTrackCooldown(rule.name, det.track_id, rule.per_track_cooldown_ms)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!CheckVote(rule.name, det.track_id, rule.min_hits, rule.hit_window_ms)) {
|
||||
continue;
|
||||
}
|
||||
qualified.push_back(det);
|
||||
}
|
||||
|
||||
const bool currently_matched = !qualified.empty();
|
||||
if (CheckDuration(rule.name, currently_matched)) {
|
||||
result.matched = true;
|
||||
result.rule_name = rule.name;
|
||||
result.matched_detections = matched;
|
||||
TriggerCooldown(rule.name);
|
||||
result.matched_detections = qualified;
|
||||
if (rule.per_track_cooldown_ms > 0) {
|
||||
for (const auto& det : qualified) {
|
||||
if (det.track_id >= 0) TriggerPerTrackCooldown(rule.name, det.track_id);
|
||||
}
|
||||
} else {
|
||||
TriggerCooldown(rule.name);
|
||||
}
|
||||
duration_start_.erase(rule.name);
|
||||
return result;
|
||||
}
|
||||
@ -146,6 +187,8 @@ RuleMatchResult RuleEngine::Evaluate(const std::shared_ptr<Frame>& frame) {
|
||||
void RuleEngine::Reset() {
|
||||
duration_start_.clear();
|
||||
last_trigger_.clear();
|
||||
per_track_last_trigger_.clear();
|
||||
vote_history_.clear();
|
||||
}
|
||||
|
||||
bool RuleEngine::IsInRoi(const Rect& bbox, const RoiRect& roi, int img_w, int img_h) const {
|
||||
@ -231,4 +274,48 @@ void RuleEngine::TriggerCooldown(const std::string& rule_name) {
|
||||
last_trigger_[rule_name] = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
bool RuleEngine::CheckVote(const std::string& rule_name, int track_id, int min_hits, int hit_window_ms) {
|
||||
if (min_hits <= 1 || hit_window_ms <= 0) return true;
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const std::string key = MakeKey(rule_name, track_id);
|
||||
auto& dq = vote_history_[key];
|
||||
const auto window = std::chrono::milliseconds(hit_window_ms);
|
||||
while (!dq.empty() && (now - dq.front()) > window) {
|
||||
dq.pop_front();
|
||||
}
|
||||
dq.push_back(now);
|
||||
return static_cast<int>(dq.size()) >= min_hits;
|
||||
}
|
||||
|
||||
bool RuleEngine::CheckPerTrackCooldown(const std::string& rule_name, int track_id, int per_track_ms) {
|
||||
if (per_track_ms <= 0) return true;
|
||||
if (track_id < 0) return true;
|
||||
const std::string key = MakeKey(rule_name, track_id);
|
||||
auto it = per_track_last_trigger_.find(key);
|
||||
if (it == per_track_last_trigger_.end()) return true;
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - it->second).count();
|
||||
return elapsed >= per_track_ms;
|
||||
}
|
||||
|
||||
void RuleEngine::TriggerPerTrackCooldown(const std::string& rule_name, int track_id) {
|
||||
if (track_id < 0) return;
|
||||
per_track_last_trigger_[MakeKey(rule_name, track_id)] = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
bool RuleEngine::PassQuality(const AlarmRule& rule, const Detection& det, double img_area) const {
|
||||
if (rule.require_track_id && det.track_id < 0) return false;
|
||||
if (rule.min_score > 0.0f && det.score < rule.min_score) return false;
|
||||
if (rule.min_box_area_ratio > 0.0f && img_area > 0.0) {
|
||||
const double a = static_cast<double>(det.bbox.w) * static_cast<double>(det.bbox.h);
|
||||
const double r = a / img_area;
|
||||
if (r < static_cast<double>(rule.min_box_area_ratio)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string RuleEngine::MakeKey(const std::string& rule_name, int track_id) {
|
||||
return rule_name + "#" + std::to_string(track_id);
|
||||
}
|
||||
|
||||
} // namespace rk3588
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
@ -22,8 +23,14 @@ struct AlarmRule {
|
||||
std::string name;
|
||||
std::set<int> class_ids;
|
||||
RoiRect roi;
|
||||
float min_score = 0.0f;
|
||||
float min_box_area_ratio = 0.0f;
|
||||
bool require_track_id = false;
|
||||
int min_duration_ms = 0;
|
||||
int min_hits = 1;
|
||||
int hit_window_ms = 0;
|
||||
int cooldown_ms = 5000;
|
||||
int per_track_cooldown_ms = 0;
|
||||
std::string schedule; // "HH:MM-HH:MM" format, empty = always active
|
||||
};
|
||||
|
||||
@ -45,6 +52,11 @@ private:
|
||||
bool CheckDuration(const std::string& rule_name, bool currently_matched);
|
||||
bool CheckCooldown(const std::string& rule_name);
|
||||
void TriggerCooldown(const std::string& rule_name);
|
||||
bool CheckVote(const std::string& rule_name, int track_id, int min_hits, int hit_window_ms);
|
||||
bool CheckPerTrackCooldown(const std::string& rule_name, int track_id, int per_track_ms);
|
||||
void TriggerPerTrackCooldown(const std::string& rule_name, int track_id);
|
||||
bool PassQuality(const AlarmRule& rule, const Detection& det, double img_area) const;
|
||||
static std::string MakeKey(const std::string& rule_name, int track_id);
|
||||
|
||||
std::vector<AlarmRule> rules_;
|
||||
std::vector<std::string> labels_;
|
||||
@ -54,6 +66,12 @@ private:
|
||||
|
||||
// Cooldown tracking: rule_name -> last trigger time
|
||||
std::map<std::string, std::chrono::steady_clock::time_point> last_trigger_;
|
||||
|
||||
// Per-track cooldown tracking: rule_name#track_id -> last trigger time
|
||||
std::map<std::string, std::chrono::steady_clock::time_point> per_track_last_trigger_;
|
||||
|
||||
// Vote history: rule_name#track_id -> timestamps within window
|
||||
std::map<std::string, std::deque<std::chrono::steady_clock::time_point>> vote_history_;
|
||||
};
|
||||
|
||||
} // namespace rk3588
|
||||
|
||||
Loading…
Reference in New Issue
Block a user