修复了一个关闭程序时因为提前释放了全局变量导致SF的问题

This commit is contained in:
GuanYuankai 2025-10-29 07:21:32 +00:00
parent b6315d1790
commit 0de78cd02e
7 changed files with 88 additions and 79 deletions

View File

@ -24,4 +24,6 @@ public:
* @param frame [in/out] * @param frame [in/out]
*/ */
virtual bool process(cv::Mat& frame) = 0; virtual bool process(cv::Mat& frame) = 0;
virtual void stop() = 0;
}; };

View File

@ -20,9 +20,6 @@ IntrusionModule::IntrusionModule(std::string model_path,
} }
} }
//
// init() 函数: 从 video_service.cc 的 start() 移动而来
//
bool IntrusionModule::init() { bool IntrusionModule::init() {
rknn_pool_ = std::make_unique<rknnPool<rkYolov5s, cv::Mat, detect_result_group_t>>(model_path_.c_str(), thread_num_); rknn_pool_ = std::make_unique<rknnPool<rkYolov5s, cv::Mat, detect_result_group_t>>(model_path_.c_str(), thread_num_);
if (rknn_pool_->init() != 0) { if (rknn_pool_->init() != 0) {
@ -33,22 +30,17 @@ bool IntrusionModule::init() {
return true; return true;
} }
//
// process() 函数: 从 video_service.cc 的 processing_loop() 移动而来
//
bool IntrusionModule::process(cv::Mat& frame) { bool IntrusionModule::process(cv::Mat& frame) {
if (frame.empty()) { if (frame.empty()) {
return false; return false;
} }
// 1. 图像预处理 (来自)
cv::Mat model_input_image; cv::Mat model_input_image;
cv::resize(frame, model_input_image, cv::Size(640, 640)); cv::resize(frame, model_input_image, cv::Size(640, 640));
if (!model_input_image.isContinuous()) { if (!model_input_image.isContinuous()) {
model_input_image = model_input_image.clone(); model_input_image = model_input_image.clone();
} }
// 2. RKNN 推理 (来自)
if (rknn_pool_->put(model_input_image) != 0) { if (rknn_pool_->put(model_input_image) != 0) {
spdlog::error("[IntrusionModule] Failed to put frame into rknnPool."); spdlog::error("[IntrusionModule] Failed to put frame into rknnPool.");
return false; return false;
@ -60,19 +52,14 @@ bool IntrusionModule::process(cv::Mat& frame) {
return false; return false;
} }
// 3. 跟踪与报警 (来自)
this->update_tracker(detection_results, frame.size()); this->update_tracker(detection_results, frame.size());
// 4. 绘制结果 (来自)
this->draw_results(frame); // 直接在传入的 frame 上绘制 this->draw_results(frame); // 直接在传入的 frame 上绘制
return true; return true;
} }
//
// 以下所有函数均从 video_service.cc 完整剪切而来
//
void IntrusionModule::trigger_alarm(int person_id, const cv::Rect& box) { 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", 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) 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) { 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); 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_width = 640.0f;
const float model_input_height = 640.0f; const float model_input_height = 640.0f;
float scale_x = (float)frame_size.width / model_input_width; 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) 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_) { 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); 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::putText(frame, label, cv::Point(person.box.x, person.box.y - 10),
cv::FONT_HERSHEY_SIMPLEX, 0.5, box_color, 2); 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.");
} }

View File

@ -40,18 +40,17 @@ public:
virtual ~IntrusionModule() = default; virtual ~IntrusionModule() = default;
// --- 实现 IAnalysisModule 接口 ---
virtual bool init() override; virtual bool init() override;
virtual bool process(cv::Mat& frame) override; virtual bool process(cv::Mat& frame) override;
virtual void stop() override;
private: private:
// --- 以下函数从 video_service.cc 移动到这里 ---
void update_tracker(detect_result_group_t &detect_result_group, const cv::Size& frame_size); void update_tracker(detect_result_group_t &detect_result_group, const cv::Size& frame_size);
void draw_results(cv::Mat& frame); void draw_results(cv::Mat& frame);
void trigger_alarm(int person_id, const cv::Rect& box); void trigger_alarm(int person_id, const cv::Rect& box);
double get_current_time_seconds(); double get_current_time_seconds();
// --- 以下成员变量从 video_service.h 移动到这里 ---
std::string model_path_; std::string model_path_;
int thread_num_; int thread_num_;
std::unique_ptr<rknnPool<rkYolov5s, cv::Mat, detect_result_group_t>> rknn_pool_; std::unique_ptr<rknnPool<rkYolov5s, cv::Mat, detect_result_group_t>> rknn_pool_;

View File

@ -98,8 +98,18 @@ int readLines(const char *fileName, char *lines[], int max_line)
int loadLabelName(const char *locationFilename, char *label[]) int loadLabelName(const char *locationFilename, char *label[])
{ {
printf("loadLabelName %s\n", locationFilename); printf("loadLabelName %s\n", locationFilename);
readLines(locationFilename, label, OBJ_CLASS_NUM); int ret = readLines(locationFilename, label, OBJ_CLASS_NUM);
return 0; 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, static float CalculateOverlap(float xmin0, float ymin0, float xmax0, float ymax0, float xmin1, float ymin1, float xmax1,

View File

@ -281,7 +281,7 @@ detect_result_group_t rkYolov5s::infer(const cv::Mat &orig_img)
rkYolov5s::~rkYolov5s() rkYolov5s::~rkYolov5s()
{ {
deinitPostProcess(); // deinitPostProcess();
ret = rknn_destroy(ctx); ret = rknn_destroy(ctx);

View File

@ -7,8 +7,9 @@
#include <mutex> #include <mutex>
#include <queue> #include <queue>
#include <memory> #include <memory>
#include <atomic>
#include "spdlog/spdlog.h"
// rknnModel模型类, inputType模型输入类型, outputType模型输出类型
template <typename rknnModel, typename inputType, typename outputType> template <typename rknnModel, typename inputType, typename outputType>
class rknnPool class rknnPool
{ {
@ -22,6 +23,8 @@ private:
std::queue<std::future<outputType>> futs; std::queue<std::future<outputType>> futs;
std::vector<std::shared_ptr<rknnModel>> models; std::vector<std::shared_ptr<rknnModel>> models;
std::atomic<bool> is_stopped{false};
protected: protected:
int getModelId(); int getModelId();
@ -32,6 +35,9 @@ public:
int put(inputType inputData); int put(inputType inputData);
// 获取推理结果/Get the results of your inference // 获取推理结果/Get the results of your inference
int get(outputType &outputData); int get(outputType &outputData);
void stop();
~rknnPool(); ~rknnPool();
}; };
@ -41,11 +47,13 @@ rknnPool<rknnModel, inputType, outputType>::rknnPool(const std::string modelPath
this->modelPath = modelPath; this->modelPath = modelPath;
this->threadNum = threadNum; this->threadNum = threadNum;
this->id = 0; this->id = 0;
this->is_stopped = false;
} }
template <typename rknnModel, typename inputType, typename outputType> template <typename rknnModel, typename inputType, typename outputType>
int rknnPool<rknnModel, inputType, outputType>::init() int rknnPool<rknnModel, inputType, outputType>::init()
{ {
try try
{ {
this->pool = std::make_unique<dpool::ThreadPool>(this->threadNum); this->pool = std::make_unique<dpool::ThreadPool>(this->threadNum);
@ -80,6 +88,10 @@ int rknnPool<rknnModel, inputType, outputType>::getModelId()
template <typename rknnModel, typename inputType, typename outputType> template <typename rknnModel, typename inputType, typename outputType>
int rknnPool<rknnModel, inputType, outputType>::put(inputType inputData) int rknnPool<rknnModel, inputType, outputType>::put(inputType inputData)
{ {
if (is_stopped) {
return -1; // Or handle as an error
}
std::lock_guard<std::mutex> lock(queueMtx); std::lock_guard<std::mutex> lock(queueMtx);
futs.push(pool->submit(&rknnModel::infer, models[this->getModelId()], inputData)); futs.push(pool->submit(&rknnModel::infer, models[this->getModelId()], inputData));
return 0; return 0;
@ -88,22 +100,53 @@ int rknnPool<rknnModel, inputType, outputType>::put(inputType inputData)
template <typename rknnModel, typename inputType, typename outputType> template <typename rknnModel, typename inputType, typename outputType>
int rknnPool<rknnModel, inputType, outputType>::get(outputType &outputData) int rknnPool<rknnModel, inputType, outputType>::get(outputType &outputData)
{ {
std::lock_guard<std::mutex> lock(queueMtx); std::future<outputType> future_to_get;
if(futs.empty() == true)
return 1; {
outputData = futs.front().get(); std::lock_guard<std::mutex> lock(queueMtx);
futs.pop(); if (futs.empty() == true)
return 1;
future_to_get = std::move(futs.front());
futs.pop();
}
outputData = future_to_get.get();
return 0; return 0;
} }
template <typename rknnModel, typename inputType, typename outputType>
void rknnPool<rknnModel, inputType, outputType>::stop()
{
spdlog::critical("!!! [RKNN_POOL_STOP_CALLED] Shutdown sequence initiated. !!!");
if (is_stopped.exchange(true)) {
return;
}
while (true)
{
std::future<outputType> future_to_get;
{
std::lock_guard<std::mutex> lock(queueMtx);
if (futs.empty()) {
break;
}
future_to_get = std::move(futs.front());
futs.pop();
}
future_to_get.get();
}
pool.reset();
}
template <typename rknnModel, typename inputType, typename outputType> template <typename rknnModel, typename inputType, typename outputType>
rknnPool<rknnModel, inputType, outputType>::~rknnPool() rknnPool<rknnModel, inputType, outputType>::~rknnPool()
{ {
while (!futs.empty()) stop();
{
outputType temp = futs.front().get();
futs.pop();
}
} }
#endif #endif

View File

@ -2,33 +2,17 @@
#include "video_service.h" #include "video_service.h"
#include <stdio.h> #include <stdio.h>
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/imgproc/imgproc.hpp"
// #include "rknn/rkYolov5s.hpp" // <-- 移除
// #include "rknn/rknnPool.hpp" // <-- 移除
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
// #include <chrono> // <-- 移除 (已移至 IntrusionModule)
// #include <algorithm> // <-- 移除 (已移至 IntrusionModule)
//
// !!! 关键: trigger_alarm, get_current_time_seconds, update_tracker, draw_results
// !!! 所有这些函数 都已被剪切并移动到 IntrusionModule.cc
//
// 构造函数:修改为接收 module
VideoService::VideoService(std::unique_ptr<IAnalysisModule> module, VideoService::VideoService(std::unique_ptr<IAnalysisModule> module,
std::string input_url, std::string input_url,
std::string output_rtsp_url) std::string output_rtsp_url)
: module_(std::move(module)), // <-- 关键:接收模块所有权 : module_(std::move(module)),
input_url_(input_url), input_url_(input_url),
output_rtsp_url_(output_rtsp_url), output_rtsp_url_(output_rtsp_url),
running_(false) running_(false)
{ {
log_prefix_ = "[VideoService: " + input_url + "]"; 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()); 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() { bool VideoService::start() {
// 1. (修改) 初始化AI模块
if (!module_ || !module_->init()) { if (!module_ || !module_->init()) {
spdlog::error("{} Failed to initialize analysis module!", log_prefix_); spdlog::error("{} Failed to initialize analysis module!", log_prefix_);
return false; return false;
} }
spdlog::info("{} Analysis module initialized successfully.", log_prefix_); spdlog::info("{} Analysis module initialized successfully.", log_prefix_);
// 2. (移除) RKNN Pool 初始化
// rknn_pool_ = std::make_unique<...>();
// ...
// 3. (不变) GStreamer 输入管线初始化
std::string gst_input_pipeline = std::string gst_input_pipeline =
"rtspsrc location=" + input_url_ + " latency=0 protocols=tcp ! " "rtspsrc location=" + input_url_ + " latency=0 protocols=tcp ! "
"rtph265depay ! " "rtph265depay ! "
"h265parse ! " "h265parse ! "
"mppvideodec format=16 ! " "mppvideodec format=16 ! "
"videoconvert ! "
"video/x-raw,format=BGR ! " "video/x-raw,format=BGR ! "
"appsink"; "appsink";
@ -98,18 +77,17 @@ bool VideoService::start() {
} else { } else {
spdlog::error("{} Failed to read first frame to determine size. Aborting.", log_prefix_); spdlog::error("{} Failed to read first frame to determine size. Aborting.", log_prefix_);
capture_.release(); capture_.release();
return false; // 提前中止 return false;
} }
} }
printf("RTSP stream opened successfully! (%dx%d @ %.2f FPS)\n", frame_width_, frame_height_, frame_fps_); printf("RTSP stream opened successfully! (%dx%d @ %.2f FPS)\n", frame_width_, frame_height_, frame_fps_);
// 4. (不变) GStreamer 输出管线初始化
std::string gst_pipeline = std::string gst_pipeline =
"appsrc ! " "appsrc ! "
"queue max-size-buffers=2 leaky=downstream ! " "queue max-size-buffers=2 leaky=downstream ! "
"video/x-raw,format=BGR ! " "video/x-raw,format=BGR ! "
"videoconvert ! " "videoconvert ! "
"video/x-raw,format=NV12 ! " "video/x-raw,format=NV12 ! "
"mpph265enc gop=25 rc-mode=fixqp qp-init=26 ! " "mpph265enc gop=25 rc-mode=fixqp qp-init=26 ! "
"h265parse ! " "h265parse ! "
@ -130,7 +108,6 @@ bool VideoService::start() {
} }
printf("VideoWriter opened successfully.\n"); printf("VideoWriter opened successfully.\n");
// 5. (不变) 启动线程
running_ = true; running_ = true;
reading_thread_ = std::thread(&VideoService::reading_loop, this); reading_thread_ = std::thread(&VideoService::reading_loop, this);
processing_thread_ = std::thread(&VideoService::processing_loop, this); processing_thread_ = std::thread(&VideoService::processing_loop, this);
@ -144,7 +121,6 @@ void VideoService::stop() {
printf("Stopping VideoService...\n"); printf("Stopping VideoService...\n");
running_ = false; running_ = false;
// 唤醒可能在 frame_cv_.wait() 处等待的线程
frame_cv_.notify_all(); frame_cv_.notify_all();
if (reading_thread_.joinable()) { if (reading_thread_.joinable()) {
@ -162,17 +138,12 @@ void VideoService::stop() {
if (writer_.isOpened()) { if (writer_.isOpened()) {
writer_.release(); writer_.release();
} }
module_->stop();
// (可选) 确保模块资源被释放 (虽然unique_ptr析构时会自动处理) module_.reset();
module_.reset();
printf("VideoService stopped.\n"); printf("VideoService stopped.\n");
} }
//
// reading_loop() 函数: 完全不变
//
void VideoService::reading_loop() { void VideoService::reading_loop() {
cv::Mat frame; cv::Mat frame;
spdlog::info("Reading thread started."); spdlog::info("Reading thread started.");
@ -202,12 +173,8 @@ void VideoService::reading_loop() {
} }
//
// processing_loop() 函数: 极大简化 (关键修改)
//
void VideoService::processing_loop() { void VideoService::processing_loop() {
cv::Mat frame; cv::Mat frame;
// detect_result_group_t detection_results; // <-- 移除
while (running_) { while (running_) {
{ {
@ -229,16 +196,10 @@ void VideoService::processing_loop() {
if (frame.empty()) { if (frame.empty()) {
continue; continue;
} }
// 2. (关键修改) 调用AI模块处理
// --- 移除所有 resize, put, get, update_tracker, draw_results ---
if (!module_->process(frame)) { if (!module_->process(frame)) {
// 模块报告处理失败 // 模块报告处理失败
spdlog::warn("{} Module failed to process frame. Skipping.", log_prefix_); spdlog::warn("{} Module failed to process frame. Skipping.", log_prefix_);
} }
// 此时 'frame' 已经被 module_->process() 修改(例如,绘制了框)
// 3. (不变) 写入输出流
if (writer_.isOpened()) { if (writer_.isOpened()) {
writer_.write(frame); writer_.write(frame);
} }