diff --git a/.gitignore b/.gitignore index 4d536d2..6740603 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ mosquitto/data/*.db mosquitto/log/*.log + +build/ +build/edge_proxy_lib.dir \ No newline at end of file diff --git a/build/CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o b/build/CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o index 9a6bf39..5f71b13 100644 Binary files a/build/CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o and b/build/CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o differ diff --git a/build/CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o.d b/build/CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o.d index 7301c00..2da865c 100644 --- a/build/CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o.d +++ b/build/CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o.d @@ -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 \ diff --git a/build/CMakeFiles/edge_proxy_lib.dir/src/videoServiceManager/video_service_manager.cc.o b/build/CMakeFiles/edge_proxy_lib.dir/src/videoServiceManager/video_service_manager.cc.o index 87a20eb..4e2491d 100644 Binary files a/build/CMakeFiles/edge_proxy_lib.dir/src/videoServiceManager/video_service_manager.cc.o and b/build/CMakeFiles/edge_proxy_lib.dir/src/videoServiceManager/video_service_manager.cc.o differ diff --git a/build/CMakeFiles/edge_proxy_lib.dir/src/web/web_server.cc.o b/build/CMakeFiles/edge_proxy_lib.dir/src/web/web_server.cc.o index 63a0d5d..6d1ccb9 100644 Binary files a/build/CMakeFiles/edge_proxy_lib.dir/src/web/web_server.cc.o and b/build/CMakeFiles/edge_proxy_lib.dir/src/web/web_server.cc.o differ diff --git a/build/edge_proxy b/build/edge_proxy index 7519843..41ba1e2 100755 Binary files a/build/edge_proxy and b/build/edge_proxy differ diff --git a/build/libedge_proxy_lib.a b/build/libedge_proxy_lib.a index 91e3ba5..025383b 100644 Binary files a/build/libedge_proxy_lib.a and b/build/libedge_proxy_lib.a differ diff --git a/src/algorithm/HumanDetectionModule.cc b/src/algorithm/HumanDetectionModule.cc index 7a3c81e..d77ae46 100644 --- a/src/algorithm/HumanDetectionModule.cc +++ b/src/algorithm/HumanDetectionModule.cc @@ -1,18 +1,18 @@ -// src/algorithm/HumanDetectionModule.cc #include "algorithm/HumanDetectionModule.h" #include -#include #include +#include #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 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(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>(); - } - // 兼容旧配置 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()); } @@ -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(); - 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 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 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 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."); } \ No newline at end of file diff --git a/src/algorithm/HumanDetectionModule.h b/src/algorithm/HumanDetectionModule.h index 5b1a523..ed7188e 100644 --- a/src/algorithm/HumanDetectionModule.h +++ b/src/algorithm/HumanDetectionModule.h @@ -1,11 +1,14 @@ #pragma once #include +#include #include #include #include #include #include +#include // 必须包含 +#include #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 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 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> rknn_pool_; - std::vector intrusion_zone_; - // cv::Rect intrusion_zone_; + std::map tracked_persons_; int next_track_id_; double intrusion_time_threshold_; double confidence_threshold_; + + std::unique_ptr> rknn_pool_; std::unique_ptr byte_tracker_; - // [新增] 灯光控制相关成员 - LightController* light_controller_ = nullptr; // 灯控对象指针 - // std::string light_device_id_; // 当前流关联的灯ID - std::vector light_device_ids_; - // 状态记录 - double last_person_active_time_ = 0.0; // 最后一次有人活跃的时间戳 - bool is_light_on_ = false; // 记录当前灯是否开着(防止重复发请求) + std::atomic current_mode_{DetectionMode::NORMAL_ALARM}; - // 辅助函数 - void update_smart_light_logic(bool any_person_in_zone, double current_time); + // 灯光控制 (改为列表) + LightController* light_controller_ = nullptr; + std::vector light_device_ids_; // 存储多个灯ID + double last_person_active_time_ = 0.0; + bool is_light_on_ = false; }; \ No newline at end of file diff --git a/src/web/web_server.cc b/src/web/web_server.cc index a5fe26f..bcd7903 100644 --- a/src/web/web_server.cc +++ b/src/web/web_server.cc @@ -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 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 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 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 cam_lights; + if (mc.contains("light_device_ids") && + mc["light_device_ids"].is_array()) { + cam_lights = mc["light_device_ids"].get>(); + } else if (mc.contains("light_device_id") && + mc["light_device_id"].is_string()) { + cam_lights.push_back(mc["light_device_id"].get()); + } + + 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 门禁控制接口