正常使用版本
This commit is contained in:
parent
df2e1db689
commit
8950b350ad
|
|
@ -1,2 +1,5 @@
|
|||
mosquitto/data/*.db
|
||||
mosquitto/log/*.log
|
||||
|
||||
build/
|
||||
build/edge_proxy_lib.dir
|
||||
Binary file not shown.
|
|
@ -98,12 +98,15 @@ CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o: \
|
|||
/usr/include/c++/11/bits/stl_vector.h \
|
||||
/usr/include/c++/11/bits/stl_bvector.h \
|
||||
/usr/include/c++/11/bits/vector.tcc \
|
||||
/usr/include/c++/11/pstl/execution_defs.h /usr/include/c++/11/chrono \
|
||||
/usr/include/c++/11/ratio /usr/include/c++/11/cstdint \
|
||||
/usr/include/c++/11/pstl/execution_defs.h /usr/include/c++/11/atomic \
|
||||
/usr/include/c++/11/bits/atomic_base.h \
|
||||
/usr/lib/gcc/aarch64-linux-gnu/11/include/stdint.h /usr/include/stdint.h \
|
||||
/usr/include/aarch64-linux-gnu/bits/wchar.h \
|
||||
/usr/include/aarch64-linux-gnu/bits/stdint-uintn.h \
|
||||
/usr/include/c++/11/limits /usr/include/c++/11/ctime /usr/include/time.h \
|
||||
/usr/include/c++/11/bits/atomic_lockfree_defines.h \
|
||||
/usr/include/c++/11/chrono /usr/include/c++/11/ratio \
|
||||
/usr/include/c++/11/cstdint /usr/include/c++/11/limits \
|
||||
/usr/include/c++/11/ctime /usr/include/time.h \
|
||||
/usr/include/aarch64-linux-gnu/bits/time.h \
|
||||
/usr/include/aarch64-linux-gnu/bits/timex.h \
|
||||
/usr/include/aarch64-linux-gnu/bits/types/struct_tm.h \
|
||||
|
|
@ -142,8 +145,6 @@ CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o: \
|
|||
/usr/include/c++/11/bits/cxxabi_init_exception.h \
|
||||
/usr/include/c++/11/bits/nested_exception.h \
|
||||
/usr/include/c++/11/bits/shared_ptr_atomic.h \
|
||||
/usr/include/c++/11/bits/atomic_base.h \
|
||||
/usr/include/c++/11/bits/atomic_lockfree_defines.h \
|
||||
/usr/include/c++/11/backward/auto_ptr.h \
|
||||
/usr/include/c++/11/pstl/glue_memory_defs.h \
|
||||
/usr/include/opencv4/opencv2/core/core.hpp \
|
||||
|
|
@ -255,7 +256,9 @@ CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o: \
|
|||
/usr/include/c++/11/bits/unique_lock.h \
|
||||
/usr/include/opencv4/opencv2/core/optim.hpp \
|
||||
/usr/include/opencv4/opencv2/core/ovx.hpp \
|
||||
/usr/include/opencv4/opencv2/core/cvdef.h \
|
||||
/usr/include/opencv4/opencv2/core/cvdef.h /usr/include/c++/11/thread \
|
||||
/usr/include/c++/11/bits/std_thread.h \
|
||||
/usr/include/c++/11/bits/this_thread_sleep.h \
|
||||
/app/src/algorithm/IAnalysisModule.h /app/src/vendor/nlohmann/json.hpp \
|
||||
/usr/include/c++/11/iterator /usr/include/c++/11/bits/stream_iterator.h \
|
||||
/usr/include/c++/11/forward_list /usr/include/c++/11/bits/forward_list.h \
|
||||
|
|
@ -387,7 +390,6 @@ CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o: \
|
|||
/usr/include/eigen3/Eigen/src/Core/SelfAdjointView.h \
|
||||
/usr/include/eigen3/Eigen/src/Core/products/GeneralBlockPanelKernel.h \
|
||||
/usr/include/eigen3/Eigen/src/Core/products/Parallelizer.h \
|
||||
/usr/include/c++/11/atomic \
|
||||
/usr/include/eigen3/Eigen/src/Core/ProductEvaluators.h \
|
||||
/usr/include/eigen3/Eigen/src/Core/products/GeneralMatrixVector.h \
|
||||
/usr/include/eigen3/Eigen/src/Core/products/GeneralMatrixMatrix.h \
|
||||
|
|
@ -622,15 +624,12 @@ CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o: \
|
|||
/usr/include/im2d_mpi.h /usr/include/rga.h /app/src/rknn/rknn_api.h \
|
||||
/app/src/rknn/rknnPool.hpp /app/src/rknn/ThreadPool.hpp \
|
||||
/usr/include/c++/11/condition_variable /usr/include/c++/11/future \
|
||||
/usr/include/c++/11/bits/atomic_futex.h \
|
||||
/usr/include/c++/11/bits/std_thread.h /usr/include/c++/11/thread \
|
||||
/usr/include/c++/11/bits/this_thread_sleep.h \
|
||||
/usr/include/spdlog/spdlog.h /usr/include/spdlog/common.h \
|
||||
/usr/include/spdlog/tweakme.h /usr/include/spdlog/details/null_mutex.h \
|
||||
/usr/include/spdlog/fmt/fmt.h /usr/include/fmt/core.h \
|
||||
/usr/include/fmt/format.h /usr/include/fmt/core.h \
|
||||
/usr/include/spdlog/details/registry.h /usr/include/spdlog/logger.h \
|
||||
/usr/include/spdlog/details/log_msg.h \
|
||||
/usr/include/c++/11/bits/atomic_futex.h /usr/include/spdlog/spdlog.h \
|
||||
/usr/include/spdlog/common.h /usr/include/spdlog/tweakme.h \
|
||||
/usr/include/spdlog/details/null_mutex.h /usr/include/spdlog/fmt/fmt.h \
|
||||
/usr/include/fmt/core.h /usr/include/fmt/format.h \
|
||||
/usr/include/fmt/core.h /usr/include/spdlog/details/registry.h \
|
||||
/usr/include/spdlog/logger.h /usr/include/spdlog/details/log_msg.h \
|
||||
/usr/include/spdlog/details/backtracer.h \
|
||||
/usr/include/spdlog/details/log_msg_buffer.h \
|
||||
/usr/include/spdlog/details/circular_q.h /usr/include/spdlog/version.h \
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
BIN
build/edge_proxy
BIN
build/edge_proxy
Binary file not shown.
Binary file not shown.
|
|
@ -1,18 +1,18 @@
|
|||
// src/algorithm/HumanDetectionModule.cc
|
||||
#include "algorithm/HumanDetectionModule.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "opencv2/imgproc/imgproc.hpp"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
#ifndef OBJ_NUMB_MAX_SIZE
|
||||
#define OBJ_NUMB_MAX_SIZE 64
|
||||
#endif
|
||||
|
||||
// [修改] 构造函数:移除了 intrusion_zone 参数和相关检查
|
||||
// 构造函数
|
||||
HumanDetectionModule::HumanDetectionModule(std::string model_path, int thread_num,
|
||||
std::vector<cv::Point> intrusion_zone,
|
||||
double intrusion_time_threshold)
|
||||
|
|
@ -25,14 +25,27 @@ HumanDetectionModule::HumanDetectionModule(std::string model_path, int thread_nu
|
|||
spdlog::info("[HumanDetectionModule] Created. Model: {}, Threads: {}", model_path_,
|
||||
thread_num_);
|
||||
|
||||
// [删除] 旧的 rect 宽高检查,因为现在是多边形,且在 init 中初始化
|
||||
// if (intrusion_zone_.width <= 0 ... ) { ... }
|
||||
|
||||
if (intrusion_zone_.empty()) {
|
||||
spdlog::warn("[HumanDetectionModule] Warning: Intrusion zone is empty.");
|
||||
}
|
||||
byte_tracker_ = std::make_unique<byte_track::BYTETracker>(20, 150);
|
||||
}
|
||||
|
||||
// 注入 LightController
|
||||
void HumanDetectionModule::set_light_controller(LightController* controller) {
|
||||
this->light_controller_ = controller;
|
||||
}
|
||||
|
||||
// 设置模式
|
||||
void HumanDetectionModule::set_mode(DetectionMode mode) {
|
||||
current_mode_ = mode;
|
||||
spdlog::info("[HumanDetectionModule] Switched to Mode: {}", (int)mode);
|
||||
}
|
||||
|
||||
DetectionMode HumanDetectionModule::get_mode() const {
|
||||
return current_mode_.load();
|
||||
}
|
||||
|
||||
bool HumanDetectionModule::init(const nlohmann::json& module_config) {
|
||||
std::string label_path =
|
||||
module_config.value("label_path", "/app/edge-proxy/models/coco_80_labels_list.txt");
|
||||
|
|
@ -50,15 +63,13 @@ bool HumanDetectionModule::init(const nlohmann::json& module_config) {
|
|||
}
|
||||
spdlog::info("[HumanDetectionModule] Initialized with Mode: {}", saved_mode);
|
||||
|
||||
// [正确] 这里已经处理了新旧两种格式,存入 light_device_ids_
|
||||
light_device_ids_.clear();
|
||||
// 优先读取 light_device_ids (数组)
|
||||
if (module_config.contains("light_device_ids") &&
|
||||
module_config["light_device_ids"].is_array()) {
|
||||
light_device_ids_ = module_config["light_device_ids"].get<std::vector<std::string>>();
|
||||
}
|
||||
// 兼容旧配置 light_device_id (字符串)
|
||||
else if (module_config.contains("light_device_id") &&
|
||||
module_config["light_device_id"].is_string()) {
|
||||
} else if (module_config.contains("light_device_id") &&
|
||||
module_config["light_device_id"].is_string()) {
|
||||
light_device_ids_.push_back(module_config["light_device_id"].get<std::string>());
|
||||
}
|
||||
|
||||
|
|
@ -67,13 +78,12 @@ bool HumanDetectionModule::init(const nlohmann::json& module_config) {
|
|||
light_device_ids_.size());
|
||||
}
|
||||
|
||||
// [新增] 解析多边形区域配置
|
||||
// 解析多边形区域配置
|
||||
if (module_config.contains("intrusion_zone")) {
|
||||
auto zone_json = module_config["intrusion_zone"];
|
||||
intrusion_zone_.clear();
|
||||
|
||||
if (zone_json.is_array()) {
|
||||
// 情况1: 新格式 [[x,y], [x,y], ...]
|
||||
if (zone_json.size() > 0 && zone_json[0].is_array()) {
|
||||
for (const auto& point_json : zone_json) {
|
||||
if (point_json.size() >= 2) {
|
||||
|
|
@ -82,9 +92,7 @@ bool HumanDetectionModule::init(const nlohmann::json& module_config) {
|
|||
}
|
||||
spdlog::info("[HumanDetectionModule] Polygon zone initialized with {} points.",
|
||||
intrusion_zone_.size());
|
||||
}
|
||||
// 情况2: 兼容旧格式 [x, y, w, h] -> 转换为矩形多边形
|
||||
else if (zone_json.size() == 4 && zone_json[0].is_number()) {
|
||||
} else if (zone_json.size() == 4 && zone_json[0].is_number()) {
|
||||
int x = zone_json[0];
|
||||
int y = zone_json[1];
|
||||
int w = zone_json[2];
|
||||
|
|
@ -113,16 +121,14 @@ bool HumanDetectionModule::init(const nlohmann::json& module_config) {
|
|||
}
|
||||
spdlog::info("[HumanDetectionModule] rknnPool init success (YOLOv8 + RGA).");
|
||||
|
||||
if (module_config.contains("light_device_id")) {
|
||||
this->light_device_id_ = module_config["light_device_id"].get<std::string>();
|
||||
spdlog::info("[HumanDetectionModule] Linked with Light Device ID: {}", light_device_id_);
|
||||
}
|
||||
// [删除] 这里原来有多余的旧代码,现在删掉后就可以编译通过了
|
||||
// if (module_config.contains("light_device_id")) { ... } <-- 删掉这段
|
||||
|
||||
// 初始化最后活跃时间为当前时间,避免启动瞬间误关灯
|
||||
last_person_active_time_ = get_current_time_seconds();
|
||||
return true;
|
||||
}
|
||||
// [新增] 智能灯光逻辑核心函数
|
||||
|
||||
// [核心修复] 智能灯光逻辑:全并发执行,实现“瞬间全亮”
|
||||
void HumanDetectionModule::update_smart_light_logic(bool any_person_in_zone, double current_time) {
|
||||
// 1. 基础检查
|
||||
if (light_device_ids_.empty() || !light_controller_)
|
||||
|
|
@ -130,8 +136,7 @@ void HumanDetectionModule::update_smart_light_logic(bool any_person_in_zone, dou
|
|||
if (current_mode_.load() != DetectionMode::QUICK_ACTION)
|
||||
return;
|
||||
|
||||
// const double LIGHT_OFF_DELAY = 60.0; // 无人60秒关灯
|
||||
const double LIGHT_OFF_DELAY = 30.0; // 无人60秒关灯
|
||||
const double LIGHT_OFF_DELAY = 60.0; // 无人60秒关灯
|
||||
|
||||
std::string action = ""; // 待执行的动作
|
||||
|
||||
|
|
@ -175,34 +180,23 @@ void HumanDetectionModule::update_smart_light_logic(bool any_person_in_zone, dou
|
|||
}).detach();
|
||||
}
|
||||
}
|
||||
|
||||
bool HumanDetectionModule::process(cv::Mat& frame) {
|
||||
if (frame.empty()) {
|
||||
if (frame.empty())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rknn_pool_->put(frame) != 0) {
|
||||
spdlog::error("[HumanDetectionModule] Failed to put frame into rknnPool.");
|
||||
if (rknn_pool_->put(frame) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
detect_result_group_t detection_results;
|
||||
if (rknn_pool_->get(detection_results) != 0) {
|
||||
spdlog::error("[HumanDetectionModule] Failed to get frame from rknnPool.");
|
||||
if (rknn_pool_->get(detection_results) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// 更新追踪器逻辑
|
||||
this->update_tracker(detection_results, frame.size());
|
||||
|
||||
// 绘制结果
|
||||
this->draw_results(frame);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HumanDetectionModule::trigger_alarm(int person_id, const cv::Rect& box) {
|
||||
spdlog::warn("[ALARM] Human Intrusion! ID: {} at [{}, {}, {} x {}]", person_id, box.x, box.y,
|
||||
box.width, box.height);
|
||||
spdlog::warn("[ALARM] Human Intrusion! ID: {}", person_id);
|
||||
}
|
||||
|
||||
double HumanDetectionModule::get_current_time_seconds() {
|
||||
|
|
@ -211,176 +205,59 @@ double HumanDetectionModule::get_current_time_seconds() {
|
|||
.count();
|
||||
}
|
||||
|
||||
// [辅助函数] 计算两个矩形的中心点距离平方
|
||||
double calculate_distance_sq(const cv::Rect& a, const cv::Rect& b) {
|
||||
cv::Point c1 = (a.tl() + a.br()) / 2;
|
||||
cv::Point c2 = (b.tl() + b.br()) / 2;
|
||||
double dx = c1.x - c2.x;
|
||||
double dy = c1.y - c2.y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
// [辅助函数] 计算两个矩形的 IoU (交并比)
|
||||
double calculate_iou(const cv::Rect& box1, const cv::Rect& box2) {
|
||||
int x1 = std::max(box1.x, box2.x);
|
||||
int y1 = std::max(box1.y, box2.y);
|
||||
int x2 = std::min(box1.x + box1.width, box2.x + box2.width);
|
||||
int y2 = std::min(box1.y + box1.height, box2.y + box2.height);
|
||||
|
||||
if (x1 >= x2 || y1 >= y2)
|
||||
return 0.0;
|
||||
|
||||
double intersection = (x2 - x1) * (y2 - y1);
|
||||
double area1 = box1.width * box1.height;
|
||||
double area2 = box2.width * box2.height;
|
||||
return intersection / (area1 + area2 - intersection);
|
||||
}
|
||||
|
||||
void HumanDetectionModule::set_mode(DetectionMode mode) {
|
||||
current_mode_ = mode;
|
||||
spdlog::info("[HumanDetectionModule] Switched to Mode: {}", (int)mode);
|
||||
}
|
||||
|
||||
DetectionMode HumanDetectionModule::get_mode() const {
|
||||
return current_mode_.load();
|
||||
}
|
||||
|
||||
// [新增] 模式2的触发逻辑(预留)
|
||||
void HumanDetectionModule::trigger_quick_execution(int person_id, const cv::Rect& box) {
|
||||
// 这里的逻辑你之后可以填充,比如控制继电器、发送HTTP请求等
|
||||
spdlog::info("[ACTION] Quick Execution Triggered! ID: {} (Detected > 1s)", person_id);
|
||||
}
|
||||
|
||||
void HumanDetectionModule::update_tracker(detect_result_group_t& detect_result_group,
|
||||
const cv::Size& frame_size) {
|
||||
// 1. 准备数据给 ByteTrack
|
||||
std::vector<byte_track::Object> objects;
|
||||
for (int i = 0; i < detect_result_group.count; i++) {
|
||||
detect_result_t* det = &(detect_result_group.results[i]);
|
||||
if (det->prop < 0.1)
|
||||
continue;
|
||||
|
||||
byte_track::Rect<float> rect((float)det->box.left, (float)det->box.top,
|
||||
(float)(det->box.right - det->box.left),
|
||||
(float)(det->box.bottom - det->box.top));
|
||||
byte_track::Object obj(rect, 0, det->prop);
|
||||
objects.push_back(obj);
|
||||
objects.push_back(byte_track::Object(rect, 0, det->prop));
|
||||
}
|
||||
|
||||
// 2. 运行追踪
|
||||
auto output_stracks = byte_tracker_->update(objects);
|
||||
|
||||
// 3. 将结果映射回业务逻辑
|
||||
double current_time = get_current_time_seconds();
|
||||
|
||||
bool person_qualified_for_light = false;
|
||||
|
||||
// 遍历所有追踪对象
|
||||
for (auto& [id, person] : tracked_persons_) {
|
||||
// 必须是在区域内
|
||||
if (person.is_in_zone) {
|
||||
// 计算停留时间
|
||||
double stay_time = current_time - person.entry_time;
|
||||
|
||||
// 满足开灯条件:在区域内且停留超过1秒
|
||||
if (stay_time >= 0.2) {
|
||||
person_qualified_for_light = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [新增] 执行灯光逻辑
|
||||
this->update_smart_light_logic(person_qualified_for_light, current_time);
|
||||
|
||||
std::vector<int> current_frame_ids;
|
||||
|
||||
const double KEEP_ALIVE_TIME = 3.0;
|
||||
const double RECONNECT_DISTANCE_SQ = 100 * 100;
|
||||
bool person_qualified_for_light = false;
|
||||
|
||||
for (const auto& track_ptr : output_stracks) {
|
||||
int id = track_ptr->getTrackId();
|
||||
float score = track_ptr->getScore();
|
||||
if (score < confidence_threshold_)
|
||||
if (track_ptr->getScore() < confidence_threshold_)
|
||||
continue;
|
||||
|
||||
const auto& rect = track_ptr->getRect();
|
||||
cv::Rect box((int)rect.x(), (int)rect.y(), (int)rect.width(), (int)rect.height());
|
||||
|
||||
// --- ID Switch 处理逻辑 ---
|
||||
bool is_new_id = (tracked_persons_.find(id) == tracked_persons_.end());
|
||||
|
||||
if (is_new_id) {
|
||||
int best_match_old_id = -1;
|
||||
double min_dist_sq = RECONNECT_DISTANCE_SQ;
|
||||
double max_iou = 0.3;
|
||||
|
||||
for (auto& [old_id, person] : tracked_persons_) {
|
||||
if (person.missing_frames > 0) {
|
||||
double dist_sq = calculate_distance_sq(box, person.box);
|
||||
double iou = calculate_iou(box, person.box);
|
||||
|
||||
if (iou > max_iou) {
|
||||
max_iou = iou;
|
||||
best_match_old_id = old_id;
|
||||
} else if (dist_sq < min_dist_sq && max_iou <= 0.3) {
|
||||
min_dist_sq = dist_sq;
|
||||
best_match_old_id = old_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_match_old_id != -1) {
|
||||
// 继承旧 ID
|
||||
spdlog::info("ID Switch Detected! Inheriting info: Old ID {} -> New ID {}",
|
||||
best_match_old_id, id);
|
||||
PersonTrackInfo& old_info = tracked_persons_[best_match_old_id];
|
||||
PersonTrackInfo new_person;
|
||||
new_person.id = id;
|
||||
new_person.entry_time = old_info.entry_time;
|
||||
new_person.is_in_zone = old_info.is_in_zone;
|
||||
new_person.alarm_triggered = old_info.alarm_triggered;
|
||||
new_person.box = box;
|
||||
new_person.confidence = score;
|
||||
new_person.last_seen_time = current_time;
|
||||
new_person.missing_frames = 0;
|
||||
|
||||
tracked_persons_[id] = new_person;
|
||||
tracked_persons_.erase(best_match_old_id);
|
||||
} else {
|
||||
// 新 ID
|
||||
PersonTrackInfo new_person;
|
||||
new_person.id = id;
|
||||
new_person.entry_time = 0;
|
||||
new_person.is_in_zone = false;
|
||||
new_person.alarm_triggered = false;
|
||||
new_person.box = box;
|
||||
new_person.confidence = score;
|
||||
new_person.last_seen_time = current_time;
|
||||
new_person.missing_frames = 0;
|
||||
tracked_persons_[id] = new_person;
|
||||
}
|
||||
// 简化版 ID 插入/更新
|
||||
if (tracked_persons_.find(id) == tracked_persons_.end()) {
|
||||
PersonTrackInfo new_person;
|
||||
new_person.id = id;
|
||||
new_person.entry_time = 0;
|
||||
new_person.is_in_zone = false;
|
||||
new_person.alarm_triggered = false;
|
||||
new_person.box = box;
|
||||
new_person.confidence = track_ptr->getScore();
|
||||
new_person.last_seen_time = current_time;
|
||||
new_person.missing_frames = 0;
|
||||
tracked_persons_[id] = new_person;
|
||||
} else {
|
||||
// 更新现有 ID
|
||||
tracked_persons_[id].box = box;
|
||||
tracked_persons_[id].confidence = score;
|
||||
tracked_persons_[id].confidence = track_ptr->getScore();
|
||||
tracked_persons_[id].last_seen_time = current_time;
|
||||
tracked_persons_[id].missing_frames = 0;
|
||||
}
|
||||
|
||||
current_frame_ids.push_back(id);
|
||||
|
||||
// --- [修复] 入侵判断逻辑 (多边形) ---
|
||||
PersonTrackInfo& person = tracked_persons_[id];
|
||||
cv::Point center_point = (box.tl() + box.br()) / 2;
|
||||
|
||||
// 使用 pointPolygonTest 判断点是否在多边形内
|
||||
// 返回值 >= 0 表示在内部或边缘
|
||||
// double dist = cv::pointPolygonTest(intrusion_zone_, center_point, false);
|
||||
// bool currently_in_zone = (dist >= 0);
|
||||
// 多边形判定
|
||||
bool currently_in_zone = false;
|
||||
if (!intrusion_zone_.empty()) {
|
||||
double result = cv::pointPolygonTest(intrusion_zone_, center_point, false);
|
||||
if (result >= 0) {
|
||||
if (cv::pointPolygonTest(intrusion_zone_, center_point, false) >= 0) {
|
||||
currently_in_zone = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -388,58 +265,43 @@ void HumanDetectionModule::update_tracker(detect_result_group_t& detect_result_g
|
|||
if (currently_in_zone) {
|
||||
if (!person.is_in_zone) {
|
||||
person.is_in_zone = true;
|
||||
if (person.entry_time == 0) {
|
||||
if (person.entry_time == 0)
|
||||
person.entry_time = current_time;
|
||||
}
|
||||
} else {
|
||||
// 已经在区域内,根据当前模式判断
|
||||
double stay_time = current_time - person.entry_time;
|
||||
|
||||
// 获取当前模式快照
|
||||
DetectionMode mode = current_mode_.load();
|
||||
|
||||
if (!person.alarm_triggered) {
|
||||
if (mode == DetectionMode::NORMAL_ALARM) {
|
||||
// 模式1:使用配置文件中的阈值 (intrusion_time_threshold_)
|
||||
if (stay_time >= intrusion_time_threshold_) {
|
||||
person.alarm_triggered = true;
|
||||
trigger_alarm(id, box); // 原来的报警
|
||||
}
|
||||
} else if (mode == DetectionMode::QUICK_ACTION) {
|
||||
// 模式2:固定 1.0 秒阈值
|
||||
if (stay_time >= 1.0) {
|
||||
person.alarm_triggered = true;
|
||||
trigger_quick_execution(id, box); // 新的操作
|
||||
}
|
||||
if (current_mode_.load() == DetectionMode::NORMAL_ALARM) {
|
||||
if (!person.alarm_triggered && stay_time >= intrusion_time_threshold_) {
|
||||
person.alarm_triggered = true;
|
||||
trigger_alarm(id, box);
|
||||
}
|
||||
} else if (current_mode_.load() == DetectionMode::QUICK_ACTION) {
|
||||
// [0.1秒极速响应]
|
||||
if (stay_time >= 0.1) {
|
||||
person_qualified_for_light = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 离开了区域:重置
|
||||
person.is_in_zone = false;
|
||||
person.entry_time = 0;
|
||||
person.alarm_triggered = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 清理旧数据 (Soft Delete)
|
||||
// 执行灯光逻辑
|
||||
this->update_smart_light_logic(person_qualified_for_light, current_time);
|
||||
|
||||
// 清理逻辑
|
||||
for (auto it = tracked_persons_.begin(); it != tracked_persons_.end();) {
|
||||
int id = it->first;
|
||||
bool exists_in_current_frame = false;
|
||||
bool exists = false;
|
||||
for (int eid : current_frame_ids)
|
||||
if (it->first == eid)
|
||||
exists = true;
|
||||
|
||||
for (int exist_id : current_frame_ids) {
|
||||
if (id == exist_id) {
|
||||
exists_in_current_frame = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists_in_current_frame) {
|
||||
if (!exists) {
|
||||
it->second.missing_frames++;
|
||||
double time_since_lost = current_time - it->second.last_seen_time;
|
||||
|
||||
if (time_since_lost > KEEP_ALIVE_TIME) {
|
||||
spdlog::debug("Removing person ID {} (Lost for {:.1f}s)", id, time_since_lost);
|
||||
if (current_time - it->second.last_seen_time > 3.0) {
|
||||
it = tracked_persons_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
|
|
@ -451,60 +313,25 @@ void HumanDetectionModule::update_tracker(detect_result_group_t& detect_result_g
|
|||
}
|
||||
|
||||
void HumanDetectionModule::draw_results(cv::Mat& frame) {
|
||||
// [修复] 绘制多边形区域
|
||||
if (!intrusion_zone_.empty()) {
|
||||
const cv::Point* pts = &intrusion_zone_[0]; // 获取点集数据的指针
|
||||
const cv::Point* pts = &intrusion_zone_[0];
|
||||
int npts = (int)intrusion_zone_.size();
|
||||
// 绘制闭合多边形,颜色青色 (255, 255, 0),线宽 2
|
||||
cv::polylines(frame, &pts, &npts, 1, true, cv::Scalar(255, 255, 0), 2);
|
||||
}
|
||||
|
||||
// 绘制每个追踪的人
|
||||
for (auto const& [id, person] : this->tracked_persons_) {
|
||||
// 如果正在丢失中,暂不绘制
|
||||
if (person.missing_frames > 0) {
|
||||
if (person.missing_frames > 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
cv::Scalar box_color =
|
||||
person.alarm_triggered ? cv::Scalar(0, 0, 255) : cv::Scalar(0, 255, 0);
|
||||
int line_thickness = person.alarm_triggered ? 3 : 2;
|
||||
|
||||
cv::rectangle(frame, person.box, box_color, line_thickness);
|
||||
|
||||
// 绘制中心点
|
||||
cv::Point center_point = (person.box.tl() + person.box.br()) / 2;
|
||||
cv::circle(frame, center_point, 4, cv::Scalar(0, 0, 255), -1);
|
||||
|
||||
// 文字信息
|
||||
std::string label = fmt::format("ID:{} Conf:{:.2f}", id, person.confidence);
|
||||
if (person.is_in_zone) {
|
||||
double stay_time = get_current_time_seconds() - person.entry_time;
|
||||
label += fmt::format(" T:{:.1f}s", stay_time);
|
||||
}
|
||||
|
||||
int base_line;
|
||||
double font_scale = 0.6;
|
||||
int thickness = 2;
|
||||
cv::Size label_size =
|
||||
cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, font_scale, thickness, &base_line);
|
||||
|
||||
int label_y = std::max(0, person.box.y - label_size.height - 10);
|
||||
cv::Rect label_bg(person.box.x, label_y, label_size.width, label_size.height + 10);
|
||||
|
||||
cv::rectangle(frame, label_bg, box_color, cv::FILLED);
|
||||
|
||||
cv::putText(frame, label, cv::Point(person.box.x, label_y + label_size.height + 5),
|
||||
cv::FONT_HERSHEY_SIMPLEX, font_scale, cv::Scalar(255, 255, 255), thickness);
|
||||
cv::rectangle(frame, person.box, box_color, 2);
|
||||
std::string label = "ID:" + std::to_string(id);
|
||||
cv::putText(frame, label, person.box.tl(), cv::FONT_HERSHEY_SIMPLEX, 0.6,
|
||||
cv::Scalar(255, 255, 255), 2);
|
||||
}
|
||||
}
|
||||
|
||||
void HumanDetectionModule::stop() {
|
||||
spdlog::info("Stopping HumanDetectionModule internal threads...");
|
||||
|
||||
if (rknn_pool_) {
|
||||
if (rknn_pool_)
|
||||
rknn_pool_->stop();
|
||||
}
|
||||
|
||||
spdlog::info("HumanDetectionModule stopped.");
|
||||
}
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <string>
|
||||
#include <thread> // 必须包含
|
||||
#include <vector>
|
||||
|
||||
#include "IAnalysisModule.h"
|
||||
#include "algorithm/bytetrack/include/ByteTrack/BYTETracker.h"
|
||||
|
|
@ -13,34 +16,24 @@
|
|||
#include "rknn/postprocess.h"
|
||||
#include "rknn/rkYolov8.hpp"
|
||||
#include "rknn/rknnPool.hpp"
|
||||
enum class DetectionMode {
|
||||
NORMAL_ALARM = 1, // 模式1:原逻辑(读取配置文件的阈值)
|
||||
QUICK_ACTION = 2 // 模式2:1秒触发,执行特定操作
|
||||
};
|
||||
// [修改] 结构体增加了 missing_frames 和 last_seen_time
|
||||
|
||||
enum class DetectionMode { NORMAL_ALARM = 1, QUICK_ACTION = 2 };
|
||||
|
||||
struct PersonTrackInfo {
|
||||
int id;
|
||||
cv::Rect box;
|
||||
double entry_time;
|
||||
bool is_in_zone;
|
||||
bool alarm_triggered;
|
||||
|
||||
// --- 新增字段开始 ---
|
||||
int missing_frames; // 对应 .cc 中的 missing_frames
|
||||
double last_seen_time; // 对应 .cc 中的 last_seen_time
|
||||
// --- 新增字段结束 ---
|
||||
|
||||
double confidence; // 当前目标的置信度
|
||||
int frames_unseen; // (保留旧字段以防万一,如果你的.cc只用了missing_frames,这个其实可以删掉,但保留也不影响)
|
||||
int missing_frames;
|
||||
double last_seen_time;
|
||||
double confidence;
|
||||
int frames_unseen;
|
||||
};
|
||||
|
||||
class HumanDetectionModule : public IAnalysisModule {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
// HumanDetectionModule(std::string model_path, int thread_num, cv::Rect intrusion_zone,
|
||||
// double intrusion_time_threshold);
|
||||
// 构造函数
|
||||
HumanDetectionModule(std::string model_path, int thread_num,
|
||||
std::vector<cv::Point> intrusion_zone, double intrusion_time_threshold);
|
||||
|
||||
|
|
@ -51,42 +44,38 @@ public:
|
|||
virtual void stop() override;
|
||||
|
||||
void set_mode(DetectionMode mode);
|
||||
|
||||
// [新增] 获取当前模式
|
||||
DetectionMode get_mode() const;
|
||||
|
||||
// 注入 LightController
|
||||
void set_light_controller(LightController* controller);
|
||||
|
||||
private:
|
||||
std::atomic<DetectionMode> current_mode_{DetectionMode::NORMAL_ALARM};
|
||||
void trigger_quick_execution(int person_id, const cv::Rect& box);
|
||||
|
||||
void update_tracker(detect_result_group_t& detect_result_group, const cv::Size& frame_size);
|
||||
void draw_results(cv::Mat& frame);
|
||||
void trigger_alarm(int person_id, const cv::Rect& box);
|
||||
double get_current_time_seconds();
|
||||
|
||||
// 智能灯光逻辑
|
||||
void update_smart_light_logic(bool any_person_in_zone, double current_time);
|
||||
|
||||
std::string model_path_;
|
||||
int thread_num_;
|
||||
|
||||
std::unique_ptr<rknnPool<rkYolov8, cv::Mat, detect_result_group_t>> rknn_pool_;
|
||||
|
||||
std::vector<cv::Point> intrusion_zone_;
|
||||
// cv::Rect intrusion_zone_;
|
||||
|
||||
std::map<int, PersonTrackInfo> tracked_persons_;
|
||||
int next_track_id_;
|
||||
double intrusion_time_threshold_;
|
||||
double confidence_threshold_;
|
||||
|
||||
std::unique_ptr<rknnPool<rkYolov8, cv::Mat, detect_result_group_t>> rknn_pool_;
|
||||
std::unique_ptr<byte_track::BYTETracker> byte_tracker_;
|
||||
|
||||
// [新增] 灯光控制相关成员
|
||||
LightController* light_controller_ = nullptr; // 灯控对象指针
|
||||
// std::string light_device_id_; // 当前流关联的灯ID
|
||||
std::vector<std::string> light_device_ids_;
|
||||
// 状态记录
|
||||
double last_person_active_time_ = 0.0; // 最后一次有人活跃的时间戳
|
||||
bool is_light_on_ = false; // 记录当前灯是否开着(防止重复发请求)
|
||||
std::atomic<DetectionMode> current_mode_{DetectionMode::NORMAL_ALARM};
|
||||
|
||||
// 辅助函数
|
||||
void update_smart_light_logic(bool any_person_in_zone, double current_time);
|
||||
// 灯光控制 (改为列表)
|
||||
LightController* light_controller_ = nullptr;
|
||||
std::vector<std::string> light_device_ids_; // 存储多个灯ID
|
||||
double last_person_active_time_ = 0.0;
|
||||
bool is_light_on_ = false;
|
||||
};
|
||||
|
|
@ -680,34 +680,133 @@ void WebServer::setup_routes() {
|
|||
try {
|
||||
j_body = crow::json::load(req.body);
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::warn("Invalid JSON in light control request: {}", e.what());
|
||||
return crow::response(400, "{\"error\":\"Invalid JSON format.\"}");
|
||||
}
|
||||
|
||||
// 参数校验
|
||||
if (!j_body.has("device_id") || !j_body.has("action")) {
|
||||
return crow::response(400, "{\"error\":\"Missing 'device_id' or 'action'.\"}");
|
||||
}
|
||||
|
||||
std::string deviceId = j_body["device_id"].s();
|
||||
std::string action = j_body["action"].s(); // "0" or "1"
|
||||
std::string req_deviceId = j_body["device_id"].s();
|
||||
std::string action = j_body["action"].s();
|
||||
|
||||
spdlog::info("Web API: Requesting light control. Device: {}, Action: {}", deviceId,
|
||||
action);
|
||||
spdlog::info("Web API: Light Control. Device: {}, Action: {}", req_deviceId, action);
|
||||
|
||||
// 调用控制器
|
||||
bool success = m_light_controller.ControlLight(deviceId, action);
|
||||
// 1. 定义要控制的设备列表 (将前端的联动逻辑移到这里)
|
||||
std::vector<std::string> target_devices;
|
||||
|
||||
// 如果是总控开关 351,则添加所有相关设备
|
||||
if (req_deviceId == "351") {
|
||||
// 顺序很重要,建议把不太重要的放后面
|
||||
target_devices = {"353", "350", "351", "352", "354", "349"};
|
||||
} else {
|
||||
// 普通设备,只控制自己
|
||||
target_devices.push_back(req_deviceId);
|
||||
}
|
||||
|
||||
// 2. [关键优化] 启动后台线程执行物理控制 (Fire-and-Forget)
|
||||
// 这样前端会立刻收到 200 OK,切换页面也不会断连
|
||||
// 复制必要的变量到 lambda
|
||||
LightController* ctrl = &m_light_controller;
|
||||
std::string act = action;
|
||||
std::vector<std::string> thread_targets = target_devices; // 拷贝一份
|
||||
|
||||
std::thread([ctrl, thread_targets, act]() {
|
||||
for (const auto& id : thread_targets) {
|
||||
// 依次控制,防止网关拥堵
|
||||
bool ok = ctrl->ControlLight(id, act);
|
||||
if (!ok) {
|
||||
// 失败稍微停顿一下
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
// 成功也稍微停顿一下,让网关喘口气 (例如 20ms)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
spdlog::info("Async Light Control Finished for {} devices.", thread_targets.size());
|
||||
}).detach(); // 分离线程
|
||||
|
||||
// 3. 处理"智能模式"冲突 (保持之前的逻辑,但要针对列表里的所有设备)
|
||||
// 也就是:如果我手动关了灯,那么关联这个灯的摄像头必须退出智能模式
|
||||
try {
|
||||
std::string config_path = ConfigManager::getInstance().getVideoConfigPath();
|
||||
std::ifstream ifs(config_path);
|
||||
if (ifs.is_open()) {
|
||||
nlohmann::json json_config;
|
||||
ifs >> json_config;
|
||||
ifs.close();
|
||||
|
||||
bool config_changed = false;
|
||||
std::vector<std::string> affected_streams;
|
||||
|
||||
if (json_config.contains("video_streams") &&
|
||||
json_config["video_streams"].is_array()) {
|
||||
for (auto& stream : json_config["video_streams"]) {
|
||||
if (!stream.contains("module_config"))
|
||||
continue;
|
||||
auto& mc = stream["module_config"];
|
||||
|
||||
bool is_associated = false;
|
||||
|
||||
// 检查该摄像头的 light_device_ids 是否包含我们这次操作的 *任何一个* 灯
|
||||
// 这样无论是单控还是群控,只要涉及到了,就关闭智能模式
|
||||
std::vector<std::string> cam_lights;
|
||||
if (mc.contains("light_device_ids") &&
|
||||
mc["light_device_ids"].is_array()) {
|
||||
cam_lights = mc["light_device_ids"].get<std::vector<std::string>>();
|
||||
} else if (mc.contains("light_device_id") &&
|
||||
mc["light_device_id"].is_string()) {
|
||||
cam_lights.push_back(mc["light_device_id"].get<std::string>());
|
||||
}
|
||||
|
||||
for (const auto& target_id : target_devices) {
|
||||
for (const auto& cam_light_id : cam_lights) {
|
||||
if (target_id == cam_light_id) {
|
||||
is_associated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_associated)
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果关联且处于智能模式,强制关闭
|
||||
if (is_associated) {
|
||||
int current_mode = mc.value("mode", 1);
|
||||
if (current_mode == 2) {
|
||||
mc["mode"] = 1;
|
||||
config_changed = true;
|
||||
|
||||
std::string stream_id = stream.value("id", "");
|
||||
if (!stream_id.empty()) {
|
||||
auto service = m_video_manager.getService(stream_id);
|
||||
if (service) {
|
||||
service->set_analysis_mode(1);
|
||||
affected_streams.push_back(stream_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config_changed) {
|
||||
std::ofstream ofs(config_path);
|
||||
if (ofs.is_open()) {
|
||||
ofs << json_config.dump(4);
|
||||
ofs.close();
|
||||
spdlog::info("Manual Override: Disabled Smart Mode for: {}",
|
||||
fmt::join(affected_streams, ", "));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
// 忽略非关键错误
|
||||
}
|
||||
|
||||
crow::json::wvalue response_json;
|
||||
if (success) {
|
||||
response_json["status"] = "success";
|
||||
response_json["message"] = "Light control command sent.";
|
||||
return crow::response(200, response_json);
|
||||
} else {
|
||||
response_json["status"] = "error";
|
||||
response_json["message"] = "Failed to send control command (upstream error).";
|
||||
return crow::response(502, response_json);
|
||||
}
|
||||
response_json["status"] = "success";
|
||||
response_json["message"] = "Command received. Executing in background.";
|
||||
return crow::response(200, response_json);
|
||||
});
|
||||
/**
|
||||
* @brief 门禁控制接口
|
||||
|
|
|
|||
Loading…
Reference in New Issue