parent
e33d0c7854
commit
4e4b893332
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
|
@ -1,3 +0,0 @@
|
|||
full_body
|
||||
visible_body
|
||||
head
|
||||
|
|
@ -4,83 +4,38 @@
|
|||
#include "spdlog/spdlog.h"
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* @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<std::string>();
|
||||
|
||||
// 使用 .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<int> zone_vec =
|
||||
module_config["intrusion_zone"].get<std::vector<int>>();
|
||||
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<rknnPool<rkYolov5s, cv::Mat, detect_result_group_t>>(
|
||||
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<rknnPool<rkYolov5s, cv::Mat, detect_result_group_t>>(
|
||||
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()) {
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
#pragma once
|
||||
#include <rknn_api.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
// 用于在 rknnPool 线程和主线程间传递“原始”张量数据
|
||||
// 它必须深拷贝 rknn_output.buf 的数据
|
||||
struct RawModelOutput {
|
||||
// 每个输出张量的深拷贝数据
|
||||
std::vector<std::vector<int8_t>> output_buffers;
|
||||
|
||||
// 后处理所需的量化参数
|
||||
std::vector<int32_t> zps;
|
||||
std::vector<float> scales;
|
||||
};
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
// video_service_manager.cc
|
||||
// video_service_manager.cc (重构后)
|
||||
#include "video_service_manager.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include <fstream>
|
||||
#include <fstream> // <-- 新增: 用于文件读取
|
||||
|
||||
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<IAnalysisModule> module = nullptr;
|
||||
|
||||
try {
|
||||
// 1. 根据配置创建 "AI模块" (策略)
|
||||
if (module_type == "intrusion_detection") {
|
||||
|
||||
module = std::make_unique<IntrusionModule>();
|
||||
// 从 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<int> zone_array;
|
||||
|
||||
if (module_config.contains("intrusion_zone") &&
|
||||
module_config["intrusion_zone"].is_array()) {
|
||||
zone_array = module_config["intrusion_zone"].get<std::vector<int>>();
|
||||
}
|
||||
|
||||
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<IntrusionModule>(
|
||||
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<VideoService>(
|
||||
std::move(module), input_url, output_rtsp, module_config);
|
||||
|
||||
// 2. 创建 "管线" (VideoService) 并注入模块
|
||||
// --- 关键修改 ---
|
||||
// 我们需要将 module_config 传递给 VideoService,以便它在 start() 时
|
||||
// 可以调用 module->init(module_config)
|
||||
auto service = std::make_unique<VideoService>(
|
||||
std::move(module), input_url, output_rtsp,
|
||||
module_config // <-- [修改] 传递完整的模块配置
|
||||
);
|
||||
|
||||
// 3. 启动服务 (逻辑不变)
|
||||
if (service->start()) {
|
||||
spdlog::info("Successfully started video service for stream '{}' "
|
||||
"[Module: {}]. Output is [{}].",
|
||||
Loading…
Reference in New Issue