修复了一个关闭程序时因为提前释放了全局变量导致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]
*/
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() {
rknn_pool_ = std::make_unique<rknnPool<rkYolov5s, cv::Mat, detect_result_group_t>>(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.");
}

View File

@ -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<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[])
{
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,

View File

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

View File

@ -7,8 +7,9 @@
#include <mutex>
#include <queue>
#include <memory>
#include <atomic>
#include "spdlog/spdlog.h"
// rknnModel模型类, inputType模型输入类型, outputType模型输出类型
template <typename rknnModel, typename inputType, typename outputType>
class rknnPool
{
@ -22,6 +23,8 @@ private:
std::queue<std::future<outputType>> futs;
std::vector<std::shared_ptr<rknnModel>> models;
std::atomic<bool> 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<rknnModel, inputType, outputType>::rknnPool(const std::string modelPath
this->modelPath = modelPath;
this->threadNum = threadNum;
this->id = 0;
this->is_stopped = false;
}
template <typename rknnModel, typename inputType, typename outputType>
int rknnPool<rknnModel, inputType, outputType>::init()
{
try
{
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>
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);
futs.push(pool->submit(&rknnModel::infer, models[this->getModelId()], inputData));
return 0;
@ -88,22 +100,53 @@ int rknnPool<rknnModel, inputType, outputType>::put(inputType inputData)
template <typename rknnModel, typename inputType, typename outputType>
int rknnPool<rknnModel, inputType, outputType>::get(outputType &outputData)
{
std::lock_guard<std::mutex> lock(queueMtx);
if(futs.empty() == true)
return 1;
outputData = futs.front().get();
futs.pop();
std::future<outputType> future_to_get;
{
std::lock_guard<std::mutex> 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 <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>
rknnPool<rknnModel, inputType, outputType>::~rknnPool()
{
while (!futs.empty())
{
outputType temp = futs.front().get();
futs.pop();
}
stop();
}
#endif

View File

@ -2,33 +2,17 @@
#include "video_service.h"
#include <stdio.h>
#include "opencv2/imgproc/imgproc.hpp"
// #include "rknn/rkYolov5s.hpp" // <-- 移除
// #include "rknn/rknnPool.hpp" // <-- 移除
#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,
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);
}