diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fc1650..5dd4d0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,7 @@ add_library(edge_proxy_lib STATIC src/rknn/rkYolov5s.cc src/rknn/preprocess.cc src/rknn/postprocess.cc - src/videoService/video_service_manager.cc + src/videoServiceManager/video_service_manager.cc src/algorithm/IntrusionModule.cc ) diff --git a/config/video_config.json b/config/video_config.json index 01c614c..924bd13 100644 --- a/config/video_config.json +++ b/config/video_config.json @@ -7,71 +7,41 @@ "enabled": true, "id": "cam_01_intrusion", "input_url": "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1301", - "module_type": "intrusion_detection", - "output_rtsp": "rtsp://127.0.0.1:8554/ch1301", - "module_config": { - "model_path": "/app/edge-proxy/models/RK3588/human.rknn", - "rknn_thread_num": 3, - "pre_processor": { - "type": "letterbox", - "target_width": 640, - "target_height": 640 - }, - "post_processor": { - "type": "yolov5", - "label_path": "/app/edge-proxy/models/human.txt", - "class_num": 3, - "conf_threshold": 0.25, - "nms_threshold": 0.45 - }, - "tracker_config": { - "intrusion_zone": [ - 100, - 100, - 300, - 300 - ], - "time_threshold_sec": 3, - "target_classes": [ - "person" - ] - } - } - }, - { - "enabled": false, - "id": "cam_03_intrusion", - "input_url": "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1501", - "module_type": "intrusion_detection", - "output_rtsp": "rtsp://127.0.0.1:8554/ch1501", "module_config": { + "class_num": 80, + "intrusion_zone": [ + 100, + 100, + 1820, + 1820 + ], + "label_path": "/app/edge-proxy/models/coco_80_labels_list.txt", "model_path": "/app/edge-proxy/models/RK3588/yolov5s-640-640.rknn", "rknn_thread_num": 3, - "pre_processor": { - "type": "letterbox", - "target_width": 640, - "target_height": 640 - }, - "post_processor": { - "type": "yolov5", - "label_path": "/app/edge-proxy/models/coco_80_labels_list.txt", - "class_num": 80, - "conf_threshold": 0.25, - "nms_threshold": 0.45 - }, - "tracker_config": { - "intrusion_zone": [ - 100, - 100, - 300, - 300 - ], - "time_threshold_sec": 3, - "target_classes": [ - "person" - ] - } - } + "time_threshold_sec": 3 + }, + "module_type": "intrusion_detection", + "output_rtsp": "rtsp://127.0.0.1:8554/ch1301" + }, + { + "enabled": true, + "id": "cam_03_intrusion", + "input_url": "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1501", + "module_config": { + "class_num": 80, + "intrusion_zone": [ + 100, + 100, + 300, + 300 + ], + "label_path": "/app/edge-proxy/models/coco_80_labels_list.txt", + "model_path": "/app/edge-proxy/models/RK3588/yolov5s-640-640.rknn", + "rknn_thread_num": 3, + "time_threshold_sec": 3 + }, + "module_type": "intrusion_detection", + "output_rtsp": "rtsp://127.0.0.1:8554/ch1501" } ] } \ No newline at end of file diff --git a/edge-proxy/models/RK3588/human.rknn b/edge-proxy/models/RK3588/human.rknn deleted file mode 100644 index a764cef..0000000 Binary files a/edge-proxy/models/RK3588/human.rknn and /dev/null differ diff --git a/edge-proxy/models/human.txt b/edge-proxy/models/human.txt deleted file mode 100644 index ab2c3a7..0000000 --- a/edge-proxy/models/human.txt +++ /dev/null @@ -1,3 +0,0 @@ -full_body -visible_body -head \ No newline at end of file diff --git a/src/algorithm/intrusion_detection/IntrusionModule.cc b/src/algorithm/IntrusionModule.cc similarity index 69% rename from src/algorithm/intrusion_detection/IntrusionModule.cc rename to src/algorithm/IntrusionModule.cc index 5ead917..3b53295 100644 --- a/src/algorithm/intrusion_detection/IntrusionModule.cc +++ b/src/algorithm/IntrusionModule.cc @@ -4,83 +4,38 @@ #include "spdlog/spdlog.h" #include -/** - * @brief [修改] 默认构造函数实现 - */ -IntrusionModule::IntrusionModule() - : next_track_id_(1), thread_num_(1), intrusion_time_threshold_(3.0) // +IntrusionModule::IntrusionModule(std::string model_path, int thread_num, + cv::Rect intrusion_zone, + double intrusion_time_threshold) + : model_path_(model_path), thread_num_(thread_num), + intrusion_zone_(intrusion_zone), + intrusion_time_threshold_(intrusion_time_threshold), next_track_id_(1) // { - spdlog::info("[IntrusionModule] Instance created. Awaiting initialization " - "from config."); -} - -/** - * @brief [修改] init 函数现在加载所有配置 - */ -bool IntrusionModule::init(const nlohmann::json &module_config) { - - try { - // 1. [新增] 从 JSON 加载所有配置到成员变量 - // 使用 .at() 来确保必需的字段存在 - model_path_ = module_config.at("model_path").get(); - - // 使用 .value() 来为可选字段提供默认值 - thread_num_ = module_config.value("rknn_thread_num", 3); - intrusion_time_threshold_ = module_config.value("time_threshold_sec", 3.0); - std::string label_path = - module_config.value("label_path", "/app/edge-proxy/models/human.txt"); - int class_num = module_config.value("class_num", 3); - - // 2. [新增] 解析入侵区域 (之前在 manager 中) - if (module_config.contains("intrusion_zone") && - module_config["intrusion_zone"].is_array() && - module_config["intrusion_zone"].size() == 4) { - std::vector zone_vec = - module_config["intrusion_zone"].get>(); - intrusion_zone_ = - cv::Rect(zone_vec[0], zone_vec[1], zone_vec[2], zone_vec[3]); - } else { - intrusion_zone_ = cv::Rect(0, 0, 0, 0); // 设置为无效 - } - - // 3. [新增] 验证和日志记录 (从旧构造函数移来) - spdlog::info("[IntrusionModule] Initializing... Model: {}, Threads: {}", - model_path_, thread_num_); - if (intrusion_zone_.width <= 0 || intrusion_zone_.height <= 0) { - spdlog::warn( - "[IntrusionModule] Warning: Intrusion zone is invalid or not " - "provided (0,0,0,0). It will be set at runtime."); - intrusion_zone_ = cv::Rect(0, 0, 0, 0); // 确保是无效的 - } else { - spdlog::info("[IntrusionModule] Loaded intrusion_zone from config: [{}, " - "{}, {}, {}]", - intrusion_zone_.x, intrusion_zone_.y, intrusion_zone_.width, - intrusion_zone_.height); - } - - // 4. [不变] 使用成员变量初始化 rknn_pool_ - rknn_pool_ = - std::make_unique>( - model_path_.c_str(), thread_num_, label_path, class_num); - - if (rknn_pool_->init() != 0) { - spdlog::error("[IntrusionModule] rknnPool init fail!"); - return false; - } - - spdlog::info("[IntrusionModule] rknnPool init success."); - return true; - - } catch (const nlohmann::json::exception &e) { - // 捕获 JSON 解析错误 (例如 "model_path" 缺失) - spdlog::error("[IntrusionModule] Failed to parse module_config: {}. Check " - "your video_config.json.", - e.what()); - return false; + spdlog::info("[IntrusionModule] Created. Model: {}, Threads: {}", model_path_, + thread_num_); + if (intrusion_zone_.width <= 0 || intrusion_zone_.height <= 0) { + spdlog::warn("[IntrusionModule] Warning: Intrusion zone is invalid " + "(0,0,0,0). It will be set at runtime."); } } -// ... (process, update_tracker, draw_results, stop 等函数保持不变) ... +bool IntrusionModule::init(const nlohmann::json &module_config) { + + std::string label_path = module_config.value( + "label_path", "/app/edge-proxy/models/coco_80_labels_list.txt"); + int class_num = module_config.value("class_num", 80); + + rknn_pool_ = + std::make_unique>( + model_path_.c_str(), thread_num_, label_path, class_num); + + if (rknn_pool_->init() != 0) { + spdlog::error("[IntrusionModule] rknnPool init fail!"); + return false; + } + spdlog::info("[IntrusionModule] rknnPool init success."); + return true; +} bool IntrusionModule::process(cv::Mat &frame) { if (frame.empty()) { diff --git a/src/algorithm/intrusion_detection/IntrusionModule.h b/src/algorithm/IntrusionModule.h similarity index 76% rename from src/algorithm/intrusion_detection/IntrusionModule.h rename to src/algorithm/IntrusionModule.h index 2143db0..c4531fb 100644 --- a/src/algorithm/intrusion_detection/IntrusionModule.h +++ b/src/algorithm/IntrusionModule.h @@ -24,9 +24,14 @@ struct TrackedPerson { class IntrusionModule : public IAnalysisModule { public: /** - * @brief [修改] 构造函数修改为默认构造函数 + * @brief 构造入侵检测模块 + * @param model_path rknn模型文件路径 + * @param thread_num rknn线程池数量 + * @param intrusion_zone 报警区域 + * @param intrusion_time_threshold 触发报警的时间阈值(秒) */ - IntrusionModule(); + IntrusionModule(std::string model_path, int thread_num, + cv::Rect intrusion_zone, double intrusion_time_threshold); virtual ~IntrusionModule() = default; diff --git a/src/processors/IPostProcessor.h b/src/processors/IPostProcessor.h deleted file mode 100644 index 66e68b0..0000000 --- a/src/processors/IPostProcessor.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "nlohmann/json.hpp" -#include "processors/impl_yolov5/postprocess_v5.h" // 引用重命名后的文件 -#include "rknn/RawModelOutput.h" - -// 后处理模块的接口 -class IPostProcessor { -public: - virtual ~IPostProcessor() = default; - - // 从 "post_processor" JSON 块中初始化 - virtual bool init(const nlohmann::json &config) = 0; - - /** - * @brief 执行后处理 - * @param model_output [in] rknnPool 输出的原始张量 - * @param pads [in] 预处理模块提供的 letterbox 填充信息 - * @param scale [in] 预处理模块提供的缩放信息 - * @param results [out] 解析后的标准检测结果 - */ - virtual bool process(const RawModelOutput &model_output, const BOX_RECT &pads, - float scale, detect_result_group_t &results) = 0; -}; \ No newline at end of file diff --git a/src/processors/IPreProcessor.h b/src/processors/IPreProcessor.h deleted file mode 100644 index 525fe4b..0000000 --- a/src/processors/IPreProcessor.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "nlohmann/json.hpp" -#include "opencv2/core/core.hpp" -#include "rknn/preprocess_utils.h" // 引用重命名后的文件 - -// 预处理模块的接口 -class IPreProcessor { -public: - virtual ~IPreProcessor() = default; - - // 从 "pre_processor" JSON 块中初始化 - virtual bool init(const nlohmann::json &config) = 0; - - /** - * @brief 执行预处理 - * @param raw_frame [in] 摄像机原始帧 - * @param model_input [out] 处理后用于模型输入的帧 (例如 640x640) - * @param pads [out] 填充(letterbox)信息,用于后处理坐标换算 - * @param scale [out] 缩放比例,用于后处理坐标换算 - */ - virtual bool process(const cv::Mat &raw_frame, cv::Mat &model_input, - BOX_RECT &pads, float &scale) = 0; -}; \ No newline at end of file diff --git a/src/processors/impl_yolov5/Yolov5PostProcessor.cc b/src/processors/impl_yolov5/Yolov5PostProcessor.cc deleted file mode 100644 index e69de29..0000000 diff --git a/src/processors/impl_yolov5/Yolov5PostProcessor.h b/src/processors/impl_yolov5/Yolov5PostProcessor.h deleted file mode 100644 index e69de29..0000000 diff --git a/src/rknn/RawModelOutput.h b/src/rknn/RawModelOutput.h deleted file mode 100644 index 580dfa0..0000000 --- a/src/rknn/RawModelOutput.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include -#include -#include - -// 用于在 rknnPool 线程和主线程间传递“原始”张量数据 -// 它必须深拷贝 rknn_output.buf 的数据 -struct RawModelOutput { - // 每个输出张量的深拷贝数据 - std::vector> output_buffers; - - // 后处理所需的量化参数 - std::vector zps; - std::vector scales; -}; \ No newline at end of file diff --git a/src/rknn/RknnModelRunner.cc b/src/rknn/RknnModelRunner.cc deleted file mode 100644 index e69de29..0000000 diff --git a/src/rknn/RknnModelRunner.h b/src/rknn/RknnModelRunner.h deleted file mode 100644 index e69de29..0000000 diff --git a/src/processors/impl_yolov5/postprocess_v5.cc b/src/rknn/postprocess.cc similarity index 100% rename from src/processors/impl_yolov5/postprocess_v5.cc rename to src/rknn/postprocess.cc diff --git a/src/processors/impl_yolov5/postprocess_v5.h b/src/rknn/postprocess.h similarity index 100% rename from src/processors/impl_yolov5/postprocess_v5.h rename to src/rknn/postprocess.h diff --git a/src/rknn/preprocess_utils.cc b/src/rknn/preprocess.cc similarity index 100% rename from src/rknn/preprocess_utils.cc rename to src/rknn/preprocess.cc diff --git a/src/rknn/preprocess_utils.h b/src/rknn/preprocess.h similarity index 100% rename from src/rknn/preprocess_utils.h rename to src/rknn/preprocess.h diff --git a/src/videoService/video_service.cc b/src/rknn/video_service.cc similarity index 100% rename from src/videoService/video_service.cc rename to src/rknn/video_service.cc diff --git a/src/videoService/video_service.h b/src/rknn/video_service.h similarity index 100% rename from src/videoService/video_service.h rename to src/rknn/video_service.h diff --git a/src/videoService/video_service_manager.cc b/src/videoServiceManager/video_service_manager.cc similarity index 64% rename from src/videoService/video_service_manager.cc rename to src/videoServiceManager/video_service_manager.cc index 16ef6f3..e4729b3 100644 --- a/src/videoService/video_service_manager.cc +++ b/src/videoServiceManager/video_service_manager.cc @@ -1,10 +1,13 @@ -// video_service_manager.cc +// video_service_manager.cc (重构后) #include "video_service_manager.h" #include "spdlog/spdlog.h" -#include +#include // <-- 新增: 用于文件读取 VideoServiceManager::~VideoServiceManager() { stop_all(); } +/** + * @brief [新增] 实现 load_config + */ bool VideoServiceManager::load_config(const std::string &config_path) { std::ifstream ifs(config_path); if (!ifs.is_open()) { @@ -16,14 +19,16 @@ bool VideoServiceManager::load_config(const std::string &config_path) { nlohmann::json video_json; ifs >> video_json; + // 1. 加载 video_service.enabled m_enabled = video_json.value("/video_service/enabled"_json_pointer, false); + // 2. 加载 video_streams 数组 if (video_json.contains("video_streams") && video_json["video_streams"].is_array()) { m_stream_configs_json = video_json["video_streams"]; } else { spdlog::warn("Video config contains no 'video_streams' array."); - m_stream_configs_json = nlohmann::json::array(); + m_stream_configs_json = nlohmann::json::array(); // 确保它是一个空数组 } spdlog::info("Successfully loaded video config. Service enabled: {}. " @@ -38,6 +43,9 @@ bool VideoServiceManager::load_config(const std::string &config_path) { } } +/** + * @brief [修改] 实现 load_and_start + */ void VideoServiceManager::load_and_start() { if (!m_enabled) { spdlog::warn("VideoService is disabled in video configuration. No streams " @@ -48,8 +56,10 @@ void VideoServiceManager::load_and_start() { spdlog::info("Found {} video stream configurations.", m_stream_configs_json.size()); + // 遍历存储的 json 数组 for (const auto &sc_json : m_stream_configs_json) { + // 从 JSON 中提取配置 std::string id = sc_json.value("id", "unknown"); bool enabled = sc_json.value("enabled", false); std::string input_url = sc_json.value("input_url", ""); @@ -68,9 +78,35 @@ void VideoServiceManager::load_and_start() { std::unique_ptr module = nullptr; try { + // 1. 根据配置创建 "AI模块" (策略) if (module_type == "intrusion_detection") { - module = std::make_unique(); + // 从 module_config 中提取用于构造函数的参数 + std::string module_model_path = module_config.value("model_path", ""); + int module_threads = module_config.value("rknn_thread_num", 1); + double threshold = module_config.value("time_threshold_sec", 3.0); + std::vector zone_array; + + if (module_config.contains("intrusion_zone") && + module_config["intrusion_zone"].is_array()) { + zone_array = module_config["intrusion_zone"].get>(); + } + + if (module_threads <= 0) { + spdlog::warn( + "Video stream '{}' has invalid rknn_thread_num. Defaulting to 1.", + id); + module_threads = 1; + } + + cv::Rect zone = (zone_array.size() == 4) + ? cv::Rect(zone_array[0], zone_array[1], + zone_array[2], zone_array[3]) + : cv::Rect(0, 0, 0, 0); + + // 创建模块 (构造函数不变) + module = std::make_unique( + module_model_path, module_threads, zone, threshold); } else if (module_type == "face_recognition") { spdlog::warn("Module type 'face_recognition' for stream '{}' is not " @@ -83,9 +119,17 @@ void VideoServiceManager::load_and_start() { module_type, id); continue; } - auto service = std::make_unique( - std::move(module), input_url, output_rtsp, module_config); + // 2. 创建 "管线" (VideoService) 并注入模块 + // --- 关键修改 --- + // 我们需要将 module_config 传递给 VideoService,以便它在 start() 时 + // 可以调用 module->init(module_config) + auto service = std::make_unique( + std::move(module), input_url, output_rtsp, + module_config // <-- [修改] 传递完整的模块配置 + ); + + // 3. 启动服务 (逻辑不变) if (service->start()) { spdlog::info("Successfully started video service for stream '{}' " "[Module: {}]. Output is [{}].", diff --git a/src/videoService/video_service_manager.h b/src/videoServiceManager/video_service_manager.h similarity index 100% rename from src/videoService/video_service_manager.h rename to src/videoServiceManager/video_service_manager.h