From 0de78cd02e34d95fe5c810166eb5fc4d2ea37170 Mon Sep 17 00:00:00 2001 From: GuanYuankai Date: Wed, 29 Oct 2025 07:21:32 +0000 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E7=A8=8B=E5=BA=8F=E6=97=B6=E5=9B=A0=E4=B8=BA?= =?UTF-8?q?=E6=8F=90=E5=89=8D=E9=87=8A=E6=94=BE=E4=BA=86=E5=85=A8=E5=B1=80?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E5=AF=BC=E8=87=B4SF=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/algorithm/IAnalysisModule.h | 2 + src/algorithm/IntrusionModule.cc | 28 ++++++-------- src/algorithm/IntrusionModule.h | 5 +-- src/rknn/postprocess.cc | 14 ++++++- src/rknn/rkYolov5s.cc | 2 +- src/rknn/rknnPool.hpp | 65 ++++++++++++++++++++++++++------ src/rknn/video_service.cc | 51 +++---------------------- 7 files changed, 88 insertions(+), 79 deletions(-) diff --git a/src/algorithm/IAnalysisModule.h b/src/algorithm/IAnalysisModule.h index 767aeb8..6c4f8dd 100644 --- a/src/algorithm/IAnalysisModule.h +++ b/src/algorithm/IAnalysisModule.h @@ -24,4 +24,6 @@ public: * @param frame [in/out] 传入原始帧,模块应在此帧上直接绘制结果。 */ virtual bool process(cv::Mat& frame) = 0; + + virtual void stop() = 0; }; \ No newline at end of file diff --git a/src/algorithm/IntrusionModule.cc b/src/algorithm/IntrusionModule.cc index 04f7bb1..9a93393 100644 --- a/src/algorithm/IntrusionModule.cc +++ b/src/algorithm/IntrusionModule.cc @@ -20,9 +20,6 @@ IntrusionModule::IntrusionModule(std::string model_path, } } -// -// init() 函数: 从 video_service.cc 的 start() 移动而来 -// bool IntrusionModule::init() { rknn_pool_ = std::make_unique>(model_path_.c_str(), thread_num_); if (rknn_pool_->init() != 0) { @@ -33,22 +30,17 @@ bool IntrusionModule::init() { return true; } -// -// process() 函数: 从 video_service.cc 的 processing_loop() 移动而来 -// bool IntrusionModule::process(cv::Mat& frame) { if (frame.empty()) { return false; } - - // 1. 图像预处理 (来自) + cv::Mat model_input_image; cv::resize(frame, model_input_image, cv::Size(640, 640)); if (!model_input_image.isContinuous()) { model_input_image = model_input_image.clone(); } - // 2. RKNN 推理 (来自) if (rknn_pool_->put(model_input_image) != 0) { spdlog::error("[IntrusionModule] Failed to put frame into rknnPool."); return false; @@ -60,19 +52,14 @@ bool IntrusionModule::process(cv::Mat& frame) { return false; } - // 3. 跟踪与报警 (来自) this->update_tracker(detection_results, frame.size()); - // 4. 绘制结果 (来自) this->draw_results(frame); // 直接在传入的 frame 上绘制 return true; } -// -// 以下所有函数均从 video_service.cc 完整剪切而来 -// void IntrusionModule::trigger_alarm(int person_id, const cv::Rect& box) { printf("[ALARM] Intrusion detected! Person ID: %d at location (%d, %d, %d, %d)\n", @@ -88,12 +75,10 @@ double IntrusionModule::get_current_time_seconds() { void IntrusionModule::update_tracker(detect_result_group_t &detect_result_group, const cv::Size& frame_size) { - // 如果入侵区域无效,则设置为帧中心的 1/4 区域 (基于原始帧大小) if (intrusion_zone_.width <= 0 || intrusion_zone_.height <= 0) { intrusion_zone_ = cv::Rect(frame_size.width / 4, frame_size.height / 4, frame_size.width / 2, frame_size.height / 2); } - // --- 缩放比例计算 --- const float model_input_width = 640.0f; const float model_input_height = 640.0f; float scale_x = (float)frame_size.width / model_input_width; @@ -205,7 +190,7 @@ void IntrusionModule::update_tracker(detect_result_group_t &detect_result_group, void IntrusionModule::draw_results(cv::Mat& frame) { - cv::rectangle(frame, this->intrusion_zone_, cv::Scalar(255, 255, 0), 2); // 黄色 + cv::rectangle(frame, this->intrusion_zone_, cv::Scalar(255, 255, 0), 2); for (auto const& [id, person] : this->tracked_persons_) { cv::Scalar box_color = person.alarm_triggered ? cv::Scalar(0, 0, 255) : cv::Scalar(0, 255, 0); @@ -219,4 +204,13 @@ void IntrusionModule::draw_results(cv::Mat& frame) cv::putText(frame, label, cv::Point(person.box.x, person.box.y - 10), cv::FONT_HERSHEY_SIMPLEX, 0.5, box_color, 2); } +} +void IntrusionModule::stop() { + spdlog::info("Stopping IntrusionModule internal threads..."); + + if (rknn_pool_) { + rknn_pool_->stop(); // <-- 这会阻塞并等待 rknnPool 的所有线程退出 + } + + spdlog::info("IntrusionModule stopped."); } \ No newline at end of file diff --git a/src/algorithm/IntrusionModule.h b/src/algorithm/IntrusionModule.h index 67e5a1c..6ad1e76 100644 --- a/src/algorithm/IntrusionModule.h +++ b/src/algorithm/IntrusionModule.h @@ -40,18 +40,17 @@ public: virtual ~IntrusionModule() = default; - // --- 实现 IAnalysisModule 接口 --- virtual bool init() override; virtual bool process(cv::Mat& frame) override; + virtual void stop() override; private: - // --- 以下函数从 video_service.cc 移动到这里 --- 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(); - // --- 以下成员变量从 video_service.h 移动到这里 --- + std::string model_path_; int thread_num_; std::unique_ptr> rknn_pool_; diff --git a/src/rknn/postprocess.cc b/src/rknn/postprocess.cc index eea9454..89e1c85 100644 --- a/src/rknn/postprocess.cc +++ b/src/rknn/postprocess.cc @@ -98,8 +98,18 @@ int readLines(const char *fileName, char *lines[], int max_line) int loadLabelName(const char *locationFilename, char *label[]) { printf("loadLabelName %s\n", locationFilename); - readLines(locationFilename, label, OBJ_CLASS_NUM); - return 0; + int ret = readLines(locationFilename, label, OBJ_CLASS_NUM); + if (ret < 0) + { + printf("readLines failed!\n"); + return -1; // <--- 返回错误 + } + if (ret == 0) + { + printf("No labels read from %s. File is empty or invalid.\n", locationFilename); + return -1; // <--- 返回错误 + } + return 0; // 只有成功才返回 0 } static float CalculateOverlap(float xmin0, float ymin0, float xmax0, float ymax0, float xmin1, float ymin1, float xmax1, diff --git a/src/rknn/rkYolov5s.cc b/src/rknn/rkYolov5s.cc index f06cfac..bc38d68 100644 --- a/src/rknn/rkYolov5s.cc +++ b/src/rknn/rkYolov5s.cc @@ -281,7 +281,7 @@ detect_result_group_t rkYolov5s::infer(const cv::Mat &orig_img) rkYolov5s::~rkYolov5s() { - deinitPostProcess(); + // deinitPostProcess(); ret = rknn_destroy(ctx); diff --git a/src/rknn/rknnPool.hpp b/src/rknn/rknnPool.hpp index e9fa1ad..370804e 100644 --- a/src/rknn/rknnPool.hpp +++ b/src/rknn/rknnPool.hpp @@ -7,8 +7,9 @@ #include #include #include +#include +#include "spdlog/spdlog.h" -// rknnModel模型类, inputType模型输入类型, outputType模型输出类型 template class rknnPool { @@ -22,6 +23,8 @@ private: std::queue> futs; std::vector> models; + std::atomic is_stopped{false}; + protected: int getModelId(); @@ -32,6 +35,9 @@ public: int put(inputType inputData); // 获取推理结果/Get the results of your inference int get(outputType &outputData); + + void stop(); + ~rknnPool(); }; @@ -41,11 +47,13 @@ rknnPool::rknnPool(const std::string modelPath this->modelPath = modelPath; this->threadNum = threadNum; this->id = 0; + this->is_stopped = false; } template int rknnPool::init() { + try { this->pool = std::make_unique(this->threadNum); @@ -80,6 +88,10 @@ int rknnPool::getModelId() template int rknnPool::put(inputType inputData) { + if (is_stopped) { + return -1; // Or handle as an error + } + std::lock_guard lock(queueMtx); futs.push(pool->submit(&rknnModel::infer, models[this->getModelId()], inputData)); return 0; @@ -88,22 +100,53 @@ int rknnPool::put(inputType inputData) template int rknnPool::get(outputType &outputData) { - std::lock_guard lock(queueMtx); - if(futs.empty() == true) - return 1; - outputData = futs.front().get(); - futs.pop(); + std::future future_to_get; + + { + std::lock_guard lock(queueMtx); + if (futs.empty() == true) + return 1; + + future_to_get = std::move(futs.front()); + futs.pop(); + } + + outputData = future_to_get.get(); return 0; } +template +void rknnPool::stop() +{ + + spdlog::critical("!!! [RKNN_POOL_STOP_CALLED] Shutdown sequence initiated. !!!"); + if (is_stopped.exchange(true)) { + return; + } + + while (true) + { + std::future future_to_get; + { + std::lock_guard lock(queueMtx); + if (futs.empty()) { + break; + } + future_to_get = std::move(futs.front()); + futs.pop(); + } + + future_to_get.get(); + } + + pool.reset(); + +} + template rknnPool::~rknnPool() { - while (!futs.empty()) - { - outputType temp = futs.front().get(); - futs.pop(); - } + stop(); } #endif diff --git a/src/rknn/video_service.cc b/src/rknn/video_service.cc index fd202f8..c711f6c 100644 --- a/src/rknn/video_service.cc +++ b/src/rknn/video_service.cc @@ -2,33 +2,17 @@ #include "video_service.h" #include #include "opencv2/imgproc/imgproc.hpp" -// #include "rknn/rkYolov5s.hpp" // <-- 移除 -// #include "rknn/rknnPool.hpp" // <-- 移除 #include "spdlog/spdlog.h" -// #include // <-- 移除 (已移至 IntrusionModule) -// #include // <-- 移除 (已移至 IntrusionModule) -// -// !!! 关键: trigger_alarm, get_current_time_seconds, update_tracker, draw_results -// !!! 所有这些函数 都已被剪切并移动到 IntrusionModule.cc -// - -// 构造函数:修改为接收 module VideoService::VideoService(std::unique_ptr module, std::string input_url, std::string output_rtsp_url) - : module_(std::move(module)), // <-- 关键:接收模块所有权 + : module_(std::move(module)), input_url_(input_url), output_rtsp_url_(output_rtsp_url), running_(false) { log_prefix_ = "[VideoService: " + input_url + "]"; - - // !!! 移除所有AI相关的初始化 !!! - // next_track_id_ = 1; - // intrusion_time_threshold_ = 3.0; - // intrusion_zone_ = cv::Rect(0, 0, 0, 0); - spdlog::info("{} Created. Input: {}, Output: {}", log_prefix_, input_url_.c_str(), output_rtsp_url_.c_str()); } @@ -40,23 +24,18 @@ VideoService::~VideoService() { bool VideoService::start() { - // 1. (修改) 初始化AI模块 if (!module_ || !module_->init()) { spdlog::error("{} Failed to initialize analysis module!", log_prefix_); return false; } spdlog::info("{} Analysis module initialized successfully.", log_prefix_); - // 2. (移除) RKNN Pool 初始化 - // rknn_pool_ = std::make_unique<...>(); - // ... - - // 3. (不变) GStreamer 输入管线初始化 std::string gst_input_pipeline = "rtspsrc location=" + input_url_ + " latency=0 protocols=tcp ! " "rtph265depay ! " "h265parse ! " "mppvideodec format=16 ! " + "videoconvert ! " "video/x-raw,format=BGR ! " "appsink"; @@ -98,18 +77,17 @@ bool VideoService::start() { } else { spdlog::error("{} Failed to read first frame to determine size. Aborting.", log_prefix_); capture_.release(); - return false; // 提前中止 + return false; } } printf("RTSP stream opened successfully! (%dx%d @ %.2f FPS)\n", frame_width_, frame_height_, frame_fps_); - // 4. (不变) GStreamer 输出管线初始化 std::string gst_pipeline = "appsrc ! " "queue max-size-buffers=2 leaky=downstream ! " "video/x-raw,format=BGR ! " - "videoconvert ! " + "videoconvert ! " "video/x-raw,format=NV12 ! " "mpph265enc gop=25 rc-mode=fixqp qp-init=26 ! " "h265parse ! " @@ -130,7 +108,6 @@ bool VideoService::start() { } printf("VideoWriter opened successfully.\n"); - // 5. (不变) 启动线程 running_ = true; reading_thread_ = std::thread(&VideoService::reading_loop, this); processing_thread_ = std::thread(&VideoService::processing_loop, this); @@ -144,7 +121,6 @@ void VideoService::stop() { printf("Stopping VideoService...\n"); running_ = false; - // 唤醒可能在 frame_cv_.wait() 处等待的线程 frame_cv_.notify_all(); if (reading_thread_.joinable()) { @@ -162,17 +138,12 @@ void VideoService::stop() { if (writer_.isOpened()) { writer_.release(); } - - // (可选) 确保模块资源被释放 (虽然unique_ptr析构时会自动处理) - module_.reset(); + module_->stop(); + module_.reset(); printf("VideoService stopped.\n"); } - -// -// reading_loop() 函数: 完全不变 -// void VideoService::reading_loop() { cv::Mat frame; spdlog::info("Reading thread started."); @@ -202,12 +173,8 @@ void VideoService::reading_loop() { } -// -// processing_loop() 函数: 极大简化 (关键修改) -// void VideoService::processing_loop() { cv::Mat frame; - // detect_result_group_t detection_results; // <-- 移除 while (running_) { { @@ -229,16 +196,10 @@ void VideoService::processing_loop() { if (frame.empty()) { continue; } - - // 2. (关键修改) 调用AI模块处理 - // --- 移除所有 resize, put, get, update_tracker, draw_results --- if (!module_->process(frame)) { // 模块报告处理失败 spdlog::warn("{} Module failed to process frame. Skipping.", log_prefix_); } - // 此时 'frame' 已经被 module_->process() 修改(例如,绘制了框) - - // 3. (不变) 写入输出流 if (writer_.isOpened()) { writer_.write(frame); }