修复了一个关闭程序时因为提前释放了全局变量导致SF的问题
This commit is contained in:
parent
b6315d1790
commit
0de78cd02e
|
|
@ -24,4 +24,6 @@ public:
|
|||
* @param frame [in/out] 传入原始帧,模块应在此帧上直接绘制结果。
|
||||
*/
|
||||
virtual bool process(cv::Mat& frame) = 0;
|
||||
|
||||
virtual void stop() = 0;
|
||||
};
|
||||
|
|
@ -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.");
|
||||
}
|
||||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ detect_result_group_t rkYolov5s::infer(const cv::Mat &orig_img)
|
|||
|
||||
rkYolov5s::~rkYolov5s()
|
||||
{
|
||||
deinitPostProcess();
|
||||
// deinitPostProcess();
|
||||
|
||||
ret = rknn_destroy(ctx);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue