update: add yoloV8 pre/postprocess and human detection method. #4
|
|
@ -8,7 +8,8 @@
|
||||||
"/usr/include/gstreamer-1.0",
|
"/usr/include/gstreamer-1.0",
|
||||||
"/usr/include/glib-2.0",
|
"/usr/include/glib-2.0",
|
||||||
"/usr/lib/aarch64-linux-gnu/glib-2.0/include",
|
"/usr/lib/aarch64-linux-gnu/glib-2.0/include",
|
||||||
"/usr/include/opencv4"
|
"/usr/include/opencv4",
|
||||||
|
"/usr/include/rga"
|
||||||
],
|
],
|
||||||
"defines": [
|
"defines": [
|
||||||
"CROW_USE_BOOST"
|
"CROW_USE_BOOST"
|
||||||
|
|
|
||||||
|
|
@ -65,10 +65,12 @@ add_library(edge_proxy_lib STATIC
|
||||||
# 推理模块
|
# 推理模块
|
||||||
src/rknn/video_service.cc
|
src/rknn/video_service.cc
|
||||||
src/rknn/rkYolov5s.cc
|
src/rknn/rkYolov5s.cc
|
||||||
|
src/rknn/rkYolov8.cc
|
||||||
src/rknn/preprocess.cc
|
src/rknn/preprocess.cc
|
||||||
src/rknn/postprocess.cc
|
src/rknn/postprocess.cc
|
||||||
src/videoServiceManager/video_service_manager.cc
|
src/videoServiceManager/video_service_manager.cc
|
||||||
src/algorithm/IntrusionModule.cc
|
src/algorithm/IntrusionModule.cc
|
||||||
|
src/algorithm/HumanDetectionModule.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(edge_proxy_lib PUBLIC
|
target_include_directories(edge_proxy_lib PUBLIC
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"modbus_rtu_devices": [
|
"modbus_rtu_devices": [
|
||||||
{
|
{
|
||||||
"enabled": true,
|
"enabled": false,
|
||||||
"device_id": "rtu_temp_sensor_lab",
|
"device_id": "rtu_temp_sensor_lab",
|
||||||
"port_path": "/dev/ttyS7",
|
"port_path": "/dev/ttyS7",
|
||||||
"baud_rate": 9600,
|
"baud_rate": 9600,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
},
|
},
|
||||||
"video_streams": [
|
"video_streams": [
|
||||||
{
|
{
|
||||||
"enabled": true,
|
"enabled": false,
|
||||||
"id": "cam_01_intrusion",
|
"id": "cam_01_intrusion",
|
||||||
"input_url": "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1301",
|
"input_url": "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1301",
|
||||||
"module_config": {
|
"module_config": {
|
||||||
|
|
@ -25,23 +25,23 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"id": "cam_03_intrusion",
|
"id": "cam_01_intrusion",
|
||||||
"input_url": "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1501",
|
"input_url": "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1301",
|
||||||
"module_config": {
|
"module_config": {
|
||||||
"class_num": 80,
|
"class_num": 3,
|
||||||
"intrusion_zone": [
|
"intrusion_zone": [
|
||||||
100,
|
100,
|
||||||
100,
|
100,
|
||||||
300,
|
1820,
|
||||||
300
|
1820
|
||||||
],
|
],
|
||||||
"label_path": "/app/edge-proxy/coco_80_labels_list.txt",
|
"label_path": "/app/models/human.txt",
|
||||||
"model_path": "/app/edge-proxy/RK3588/yolov5s-640-640.rknn",
|
"model_path": "/app/models/human_detection.rknn",
|
||||||
"rknn_thread_num": 3,
|
"rknn_thread_num": 3,
|
||||||
"time_threshold_sec": 3
|
"time_threshold_sec": 3
|
||||||
},
|
},
|
||||||
"module_type": "intrusion_detection",
|
"module_type": "human_detection",
|
||||||
"output_rtsp": "rtsp://127.0.0.1:8554/ch1501"
|
"output_rtsp": "rtsp://127.0.0.1:8554/ch1301"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,3 @@
|
||||||
|
full_body
|
||||||
|
visible_body
|
||||||
|
head
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,251 @@
|
||||||
|
// src/algorithm/HumanDetectionModule.cc
|
||||||
|
#include "algorithm/HumanDetectionModule.h"
|
||||||
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifndef OBJ_NUMB_MAX_SIZE
|
||||||
|
#define OBJ_NUMB_MAX_SIZE 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
HumanDetectionModule::HumanDetectionModule(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("[HumanDetectionModule] Created. Model: {}, Threads: {}",
|
||||||
|
model_path_, thread_num_);
|
||||||
|
|
||||||
|
if (intrusion_zone_.width <= 0 || intrusion_zone_.height <= 0) {
|
||||||
|
spdlog::warn("[HumanDetectionModule] Warning: Intrusion zone is invalid "
|
||||||
|
"(0,0,0,0). It will be set to default at runtime.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HumanDetectionModule::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);
|
||||||
|
|
||||||
|
// --- 核心修改:实例化 rkYolov8 类型的线程池 ---
|
||||||
|
rknn_pool_ =
|
||||||
|
std::make_unique<rknnPool<rkYolov8, cv::Mat, detect_result_group_t>>(
|
||||||
|
model_path_.c_str(), thread_num_, label_path, class_num);
|
||||||
|
|
||||||
|
if (rknn_pool_->init() != 0) {
|
||||||
|
spdlog::error("[HumanDetectionModule] rknnPool init fail!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
spdlog::info("[HumanDetectionModule] rknnPool init success (YOLOv8 + RGA).");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HumanDetectionModule::process(cv::Mat &frame) {
|
||||||
|
if (frame.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (rknn_pool_->put(frame) != 0) {
|
||||||
|
spdlog::error("[HumanDetectionModule] Failed to put frame into rknnPool.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_result_group_t detection_results;
|
||||||
|
if (rknn_pool_->get(detection_results) != 0) {
|
||||||
|
spdlog::error("[HumanDetectionModule] Failed to get frame from rknnPool.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新追踪器逻辑
|
||||||
|
this->update_tracker(detection_results, frame.size());
|
||||||
|
|
||||||
|
// 绘制结果 (直接在 frame 上绘制,因为坐标已经对应原图)
|
||||||
|
this->draw_results(frame);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HumanDetectionModule::trigger_alarm(int person_id, const cv::Rect &box) {
|
||||||
|
// 简单的日志报警,实际可对接 MQTT
|
||||||
|
spdlog::warn("[ALARM] Human Intrusion! ID: {} at [{}, {}, {} x {}]",
|
||||||
|
person_id, box.x, box.y, box.width, box.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
double HumanDetectionModule::get_current_time_seconds() {
|
||||||
|
return std::chrono::duration_cast<std::chrono::duration<double>>(
|
||||||
|
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||||
|
.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HumanDetectionModule::update_tracker(
|
||||||
|
detect_result_group_t &detect_result_group, const cv::Size &frame_size) {
|
||||||
|
// 初始化默认区域
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 1. 提取当前帧的检测框 ---
|
||||||
|
std::vector<cv::Rect> current_detections;
|
||||||
|
for (int i = 0; i < detect_result_group.count; i++) {
|
||||||
|
detect_result_t *det_result = &(detect_result_group.results[i]);
|
||||||
|
|
||||||
|
// 兼容性检查:rkYolov8 可能返回 "person" (如果加载了label) 或 "0"
|
||||||
|
// (如果只返回ID)
|
||||||
|
std::string label_name(det_result->name);
|
||||||
|
bool is_person = (label_name == "person" || label_name == "0");
|
||||||
|
|
||||||
|
if (is_person) {
|
||||||
|
// --- 关键差异 ---
|
||||||
|
// rkYolov8 的 post_process 已经将坐标映射回了原图尺寸。
|
||||||
|
// 所以这里不需要像 IntrusionModule 那样乘以 scale_x / scale_y。
|
||||||
|
int left = det_result->box.left;
|
||||||
|
int top = det_result->box.top;
|
||||||
|
int right = det_result->box.right;
|
||||||
|
int bottom = det_result->box.bottom;
|
||||||
|
|
||||||
|
// 边界保护
|
||||||
|
left = std::max(0, std::min(left, frame_size.width - 1));
|
||||||
|
top = std::max(0, std::min(top, frame_size.height - 1));
|
||||||
|
right = std::max(left, std::min(right, frame_size.width));
|
||||||
|
bottom = std::max(top, std::min(bottom, frame_size.height));
|
||||||
|
|
||||||
|
if (right > left && bottom > top) {
|
||||||
|
current_detections.push_back(
|
||||||
|
cv::Rect(left, top, right - left, bottom - top));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 2. 简单的 IOU 追踪逻辑 (保持原有逻辑不变) ---
|
||||||
|
|
||||||
|
// 增加所有现有追踪对象的未见帧数
|
||||||
|
for (auto it = tracked_persons_.begin(); it != tracked_persons_.end(); ++it) {
|
||||||
|
it->second.frames_unseen++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> matched_track_ids;
|
||||||
|
|
||||||
|
for (const auto &det_box : current_detections) {
|
||||||
|
int best_match_id = -1;
|
||||||
|
double max_iou_threshold = 0.3; // IOU 匹配阈值
|
||||||
|
double best_iou = 0.0;
|
||||||
|
|
||||||
|
for (auto const &[id, person] : tracked_persons_) {
|
||||||
|
// 跳过已被当前帧其他检测框匹配过的 ID
|
||||||
|
bool already_matched = false;
|
||||||
|
for (int matched_id : matched_track_ids) {
|
||||||
|
if (id == matched_id) {
|
||||||
|
already_matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (already_matched)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
double iou = (double)(det_box & person.box).area() /
|
||||||
|
(double)(det_box | person.box).area();
|
||||||
|
if (iou > best_iou && iou >= max_iou_threshold) {
|
||||||
|
best_iou = iou;
|
||||||
|
best_match_id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best_match_id != -1) {
|
||||||
|
// 匹配成功:更新位置,重置未见计数
|
||||||
|
tracked_persons_[best_match_id].box = det_box;
|
||||||
|
tracked_persons_[best_match_id].frames_unseen = 0;
|
||||||
|
matched_track_ids.push_back(best_match_id);
|
||||||
|
} else {
|
||||||
|
// 新目标:创建新 ID
|
||||||
|
// 注意:这里假设 PersonTrackInfo 结构体在头文件中定义,且包含下列字段
|
||||||
|
PersonTrackInfo new_person;
|
||||||
|
new_person.id = next_track_id_++;
|
||||||
|
new_person.box = det_box;
|
||||||
|
new_person.entry_time = 0;
|
||||||
|
new_person.is_in_zone = false;
|
||||||
|
new_person.alarm_triggered = false;
|
||||||
|
new_person.frames_unseen = 0;
|
||||||
|
tracked_persons_[new_person.id] = new_person;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 3. 区域入侵判定 ---
|
||||||
|
double current_time = get_current_time_seconds();
|
||||||
|
for (auto it = tracked_persons_.begin(); it != tracked_persons_.end(); ++it) {
|
||||||
|
PersonTrackInfo &person = it->second;
|
||||||
|
|
||||||
|
// 计算人框与区域的交集
|
||||||
|
bool currently_in_zone = (intrusion_zone_ & person.box).area() > 0;
|
||||||
|
|
||||||
|
if (currently_in_zone) {
|
||||||
|
if (!person.is_in_zone) {
|
||||||
|
// 刚进入区域
|
||||||
|
person.is_in_zone = true;
|
||||||
|
person.entry_time = current_time;
|
||||||
|
person.alarm_triggered = false;
|
||||||
|
} else {
|
||||||
|
// 已经在区域内,检查时间阈值
|
||||||
|
if (!person.alarm_triggered &&
|
||||||
|
(current_time - person.entry_time) >= intrusion_time_threshold_) {
|
||||||
|
person.alarm_triggered = true;
|
||||||
|
trigger_alarm(person.id, person.box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 离开区域
|
||||||
|
if (person.is_in_zone) {
|
||||||
|
person.is_in_zone = false;
|
||||||
|
person.entry_time = 0;
|
||||||
|
person.alarm_triggered = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 4. 清理消失的目标 ---
|
||||||
|
for (auto it = tracked_persons_.begin(); it != tracked_persons_.end();
|
||||||
|
/* no inc */) {
|
||||||
|
if (it->second.frames_unseen > 50) { // 超过 50 帧未检测到则删除
|
||||||
|
it = tracked_persons_.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HumanDetectionModule::draw_results(cv::Mat &frame) {
|
||||||
|
// 绘制报警区域
|
||||||
|
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);
|
||||||
|
int line_thickness = person.alarm_triggered ? 3 : 2;
|
||||||
|
|
||||||
|
cv::rectangle(frame, person.box, box_color, line_thickness);
|
||||||
|
|
||||||
|
std::string label = "ID: " + std::to_string(id);
|
||||||
|
if (person.is_in_zone) {
|
||||||
|
label += fmt::format(" ({:.1f}s)",
|
||||||
|
get_current_time_seconds() - person.entry_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::putText(frame, label, cv::Point(person.box.x, person.box.y - 10),
|
||||||
|
cv::FONT_HERSHEY_SIMPLEX, 0.6, box_color, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HumanDetectionModule::stop() {
|
||||||
|
spdlog::info("Stopping HumanDetectionModule internal threads...");
|
||||||
|
|
||||||
|
if (rknn_pool_) {
|
||||||
|
rknn_pool_->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::info("HumanDetectionModule stopped.");
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IAnalysisModule.h"
|
||||||
|
#include "rknn/postprocess.h"
|
||||||
|
#include "rknn/rkYolov8.hpp"
|
||||||
|
#include "rknn/rknnPool.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <opencv2/core/core.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct PersonTrackInfo {
|
||||||
|
int id;
|
||||||
|
cv::Rect box;
|
||||||
|
double entry_time;
|
||||||
|
bool is_in_zone;
|
||||||
|
bool alarm_triggered;
|
||||||
|
int frames_unseen;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HumanDetectionModule : public IAnalysisModule {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 构造函数
|
||||||
|
*/
|
||||||
|
HumanDetectionModule(std::string model_path, int thread_num,
|
||||||
|
cv::Rect intrusion_zone,
|
||||||
|
double intrusion_time_threshold);
|
||||||
|
|
||||||
|
virtual ~HumanDetectionModule() = default;
|
||||||
|
|
||||||
|
virtual bool init(const nlohmann::json &module_config) override;
|
||||||
|
virtual bool process(cv::Mat &frame) override;
|
||||||
|
virtual void stop() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
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();
|
||||||
|
|
||||||
|
std::string model_path_;
|
||||||
|
int thread_num_;
|
||||||
|
|
||||||
|
std::unique_ptr<rknnPool<rkYolov8, cv::Mat, detect_result_group_t>>
|
||||||
|
rknn_pool_;
|
||||||
|
|
||||||
|
cv::Rect intrusion_zone_;
|
||||||
|
std::map<int, PersonTrackInfo> tracked_persons_;
|
||||||
|
int next_track_id_;
|
||||||
|
double intrusion_time_threshold_;
|
||||||
|
};
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
// #include <mutex> // 已移除
|
|
||||||
// #include <chrono> // 已移除
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
// #include <algorithm> // 已移除
|
|
||||||
|
|
||||||
#include "postprocess.h"
|
#include "postprocess.h"
|
||||||
#include "preprocess.h"
|
#include "preprocess.h"
|
||||||
|
|
@ -15,8 +13,6 @@
|
||||||
#include "opencv2/imgproc/imgproc.hpp"
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
#include "rknn/rknn_api.h"
|
#include "rknn/rknn_api.h"
|
||||||
|
|
||||||
// trigger_alarm 和 get_current_time_seconds 已被移至 video_service.cc
|
|
||||||
|
|
||||||
static void dump_tensor_attr(rknn_tensor_attr *attr) {
|
static void dump_tensor_attr(rknn_tensor_attr *attr) {
|
||||||
std::string shape_str = attr->n_dims < 1 ? "" : std::to_string(attr->dims[0]);
|
std::string shape_str = attr->n_dims < 1 ? "" : std::to_string(attr->dims[0]);
|
||||||
for (int i = 1; i < attr->n_dims; ++i) {
|
for (int i = 1; i < attr->n_dims; ++i) {
|
||||||
|
|
@ -83,15 +79,13 @@ static int saveFloat(const char *file_name, float *output, int element_size) {
|
||||||
rkYolov5s::rkYolov5s(const std::string &model_path,
|
rkYolov5s::rkYolov5s(const std::string &model_path,
|
||||||
const std::string &label_path, int class_num) {
|
const std::string &label_path, int class_num) {
|
||||||
this->model_path = model_path;
|
this->model_path = model_path;
|
||||||
this->m_label_path = label_path; // [新增]
|
this->m_label_path = label_path;
|
||||||
this->m_class_num = class_num; // [新增]
|
this->m_class_num = class_num;
|
||||||
|
|
||||||
nms_threshold = NMS_THRESH;
|
nms_threshold = NMS_THRESH;
|
||||||
box_conf_threshold = BOX_THRESH;
|
box_conf_threshold = BOX_THRESH;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set_intrusion_zone 已被移除
|
|
||||||
|
|
||||||
int rkYolov5s::init(rknn_context *ctx_in, bool share_weight) {
|
int rkYolov5s::init(rknn_context *ctx_in, bool share_weight) {
|
||||||
printf("Loading model...\n");
|
printf("Loading model...\n");
|
||||||
int model_data_size = 0;
|
int model_data_size = 0;
|
||||||
|
|
@ -189,24 +183,24 @@ int rkYolov5s::init(rknn_context *ctx_in, bool share_weight) {
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(postprocess_init_mutex);
|
std::lock_guard<std::mutex> lock(postprocess_init_mutex);
|
||||||
if (!postprocess_initialized) {
|
if (!postprocess_initialized) {
|
||||||
// 调用我们新定义的 initPostProcess
|
|
||||||
if (initPostProcess(m_label_path.c_str(), m_class_num) == 0) {
|
if (initPostProcess(m_label_path.c_str(), m_class_num) == 0) {
|
||||||
postprocess_initialized = true;
|
postprocess_initialized = true;
|
||||||
printf("PostProcess initialized successfully with %d classes from %s\n",
|
printf("PostProcess initialized successfully with %d classes from %s\n",
|
||||||
m_class_num, m_label_path.c_str());
|
m_class_num, m_label_path.c_str());
|
||||||
} else {
|
} else {
|
||||||
printf("Failed to initialize PostProcess!\n");
|
printf("Failed to initialize PostProcess!\n");
|
||||||
return -1; // 初始化失败
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0; // rknn 初始化成功
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
rknn_context *rkYolov5s::get_pctx() { return &ctx; }
|
rknn_context *rkYolov5s::get_pctx() { return &ctx; }
|
||||||
|
|
||||||
detect_result_group_t rkYolov5s::infer(const cv::Mat &orig_img) {
|
detect_result_group_t rkYolov5s::infer(const cv::Mat &orig_img) {
|
||||||
// std::lock_guard<std::mutex> lock(mtx); // 已移除
|
|
||||||
cv::Mat img;
|
cv::Mat img;
|
||||||
cv::cvtColor(orig_img, img, cv::COLOR_BGR2RGB);
|
cv::cvtColor(orig_img, img, cv::COLOR_BGR2RGB);
|
||||||
img_width = img.cols;
|
img_width = img.cols;
|
||||||
|
|
@ -258,13 +252,10 @@ detect_result_group_t rkYolov5s::infer(const cv::Mat &orig_img) {
|
||||||
m_class_num, &detect_result_group);
|
m_class_num, &detect_result_group);
|
||||||
|
|
||||||
ret = rknn_outputs_release(ctx, io_num.n_output, outputs);
|
ret = rknn_outputs_release(ctx, io_num.n_output, outputs);
|
||||||
|
|
||||||
// 返回原始检测结果
|
|
||||||
return detect_result_group;
|
return detect_result_group;
|
||||||
}
|
}
|
||||||
|
|
||||||
rkYolov5s::~rkYolov5s() {
|
rkYolov5s::~rkYolov5s() {
|
||||||
// deinitPostProcess();
|
|
||||||
|
|
||||||
ret = rknn_destroy(ctx);
|
ret = rknn_destroy(ctx);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,309 @@
|
||||||
|
#include "rkYolov8.hpp"
|
||||||
|
#include "opencv2/imgproc/imgproc.hpp"
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <opencv2/dnn.hpp>
|
||||||
|
#include <set>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
static inline float sigmoid(float x) { return 1.0f / (1.0f + expf(-x)); }
|
||||||
|
|
||||||
|
static void compute_dfl(float *tensor, int dfl_len, float *box) {
|
||||||
|
for (int b = 0; b < 4; b++) {
|
||||||
|
float exp_t[16];
|
||||||
|
float exp_sum = 0;
|
||||||
|
float acc_sum = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < dfl_len; i++) {
|
||||||
|
exp_t[i] = expf(tensor[i + b * dfl_len]);
|
||||||
|
exp_sum += exp_t[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < dfl_len; i++) {
|
||||||
|
acc_sum += (exp_t[i] / exp_sum) * i;
|
||||||
|
}
|
||||||
|
box[b] = acc_sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *rkYolov8::load_model(const char *filename, int *model_size) {
|
||||||
|
FILE *fp = fopen(filename, "rb");
|
||||||
|
if (fp == nullptr) {
|
||||||
|
printf("Open file %s failed.\n", filename);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
int size = ftell(fp);
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
unsigned char *data = (unsigned char *)malloc(size);
|
||||||
|
fread(data, 1, size, fp);
|
||||||
|
fclose(fp);
|
||||||
|
*model_size = size;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
rkYolov8::rkYolov8(const std::string &model_path, const std::string &label_path,
|
||||||
|
int class_num) {
|
||||||
|
this->model_path = model_path;
|
||||||
|
this->m_label_path = label_path;
|
||||||
|
this->m_class_num = class_num;
|
||||||
|
this->conf_threshold = 0.3f;
|
||||||
|
this->nms_threshold = 0.5f;
|
||||||
|
this->model_data = nullptr;
|
||||||
|
this->input_attrs = nullptr;
|
||||||
|
this->output_attrs = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
rkYolov8::~rkYolov8() {
|
||||||
|
if (input_attrs)
|
||||||
|
free(input_attrs);
|
||||||
|
if (output_attrs)
|
||||||
|
free(output_attrs);
|
||||||
|
if (model_data)
|
||||||
|
free(model_data);
|
||||||
|
if (rga_buffer_ptr)
|
||||||
|
free(rga_buffer_ptr);
|
||||||
|
rknn_destroy(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
rknn_context *rkYolov8::get_pctx() { return &ctx; }
|
||||||
|
|
||||||
|
int rkYolov8::init(rknn_context *ctx_in, bool is_slave) {
|
||||||
|
int model_data_size = 0;
|
||||||
|
model_data = load_model(model_path.c_str(), &model_data_size);
|
||||||
|
if (!model_data)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (is_slave) {
|
||||||
|
ret = rknn_dup_context(ctx_in, &ctx);
|
||||||
|
} else {
|
||||||
|
ret = rknn_init(&ctx, model_data, model_data_size, 0, nullptr);
|
||||||
|
}
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
|
||||||
|
|
||||||
|
input_attrs =
|
||||||
|
(rknn_tensor_attr *)calloc(io_num.n_input, sizeof(rknn_tensor_attr));
|
||||||
|
output_attrs =
|
||||||
|
(rknn_tensor_attr *)calloc(io_num.n_output, sizeof(rknn_tensor_attr));
|
||||||
|
|
||||||
|
for (int i = 0; i < io_num.n_input; i++) {
|
||||||
|
input_attrs[i].index = i;
|
||||||
|
rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]),
|
||||||
|
sizeof(rknn_tensor_attr));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < io_num.n_output; i++) {
|
||||||
|
output_attrs[i].index = i;
|
||||||
|
rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]),
|
||||||
|
sizeof(rknn_tensor_attr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_attrs[0].fmt == RKNN_TENSOR_NCHW) {
|
||||||
|
channel = input_attrs[0].dims[1];
|
||||||
|
height = input_attrs[0].dims[2];
|
||||||
|
width = input_attrs[0].dims[3];
|
||||||
|
} else {
|
||||||
|
height = input_attrs[0].dims[1];
|
||||||
|
width = input_attrs[0].dims[2];
|
||||||
|
channel = input_attrs[0].dims[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[rkYolov8] Init: %dx%d, Output Num: %d\n", width, height,
|
||||||
|
io_num.n_output);
|
||||||
|
|
||||||
|
rga_buffer_size = width * height * channel;
|
||||||
|
rga_buffer_ptr = malloc(rga_buffer_size);
|
||||||
|
memset(rga_buffer_ptr, 114, rga_buffer_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_result_group_t rkYolov8::infer(const cv::Mat &ori_img) {
|
||||||
|
detect_result_group_t detect_result;
|
||||||
|
memset(&detect_result, 0, sizeof(detect_result_group_t));
|
||||||
|
|
||||||
|
if (ori_img.empty())
|
||||||
|
return detect_result;
|
||||||
|
|
||||||
|
int img_w = ori_img.cols;
|
||||||
|
int img_h = ori_img.rows;
|
||||||
|
float scale = std::min((float)width / img_w, (float)height / img_h);
|
||||||
|
int new_w = (int)(img_w * scale);
|
||||||
|
int new_h = (int)(img_h * scale);
|
||||||
|
int pad_w = (width - new_w) / 2;
|
||||||
|
int pad_h = (height - new_h) / 2;
|
||||||
|
|
||||||
|
rga_buffer_t src_img = wrapbuffer_virtualaddr((void *)ori_img.data, img_w,
|
||||||
|
img_h, RK_FORMAT_BGR_888);
|
||||||
|
rga_buffer_t dst_img =
|
||||||
|
wrapbuffer_virtualaddr(rga_buffer_ptr, width, height, RK_FORMAT_RGB_888);
|
||||||
|
|
||||||
|
rga_buffer_t pat;
|
||||||
|
memset(&pat, 0, sizeof(pat));
|
||||||
|
im_rect src_rect = {0, 0, img_w, img_h};
|
||||||
|
im_rect dst_rect = {pad_w, pad_h, new_w, new_h};
|
||||||
|
im_rect pat_rect = {0, 0, 0, 0};
|
||||||
|
|
||||||
|
memset(rga_buffer_ptr, 114, rga_buffer_size);
|
||||||
|
improcess(src_img, dst_img, pat, src_rect, dst_rect, pat_rect, IM_SYNC);
|
||||||
|
|
||||||
|
inputs[0].index = 0;
|
||||||
|
inputs[0].type = RKNN_TENSOR_UINT8;
|
||||||
|
inputs[0].size = rga_buffer_size;
|
||||||
|
inputs[0].fmt = RKNN_TENSOR_NHWC;
|
||||||
|
inputs[0].buf = rga_buffer_ptr;
|
||||||
|
rknn_inputs_set(ctx, io_num.n_input, inputs);
|
||||||
|
|
||||||
|
rknn_output outputs[io_num.n_output];
|
||||||
|
memset(outputs, 0, sizeof(outputs));
|
||||||
|
for (int i = 0; i < io_num.n_output; i++)
|
||||||
|
outputs[i].want_float = 1;
|
||||||
|
|
||||||
|
// FILE *fp = fopen("/app/debug_input.rgb", "wb");
|
||||||
|
// fwrite(rga_buffer_ptr, 1, rga_buffer_size, fp);
|
||||||
|
// fclose(fp);
|
||||||
|
// printf("Saved debug input image.\n");
|
||||||
|
|
||||||
|
rknn_run(ctx, nullptr);
|
||||||
|
rknn_outputs_get(ctx, io_num.n_output, outputs, nullptr);
|
||||||
|
|
||||||
|
post_process_v8_dfl(outputs, scale, pad_w, pad_h, &detect_result);
|
||||||
|
|
||||||
|
rknn_outputs_release(ctx, io_num.n_output, outputs);
|
||||||
|
return detect_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rkYolov8::post_process_v8_dfl(rknn_output *outputs, float scale, int pad_w,
|
||||||
|
int pad_h, detect_result_group_t *group) {
|
||||||
|
std::vector<float> filterBoxes;
|
||||||
|
std::vector<float> objProbs;
|
||||||
|
std::vector<int> classId;
|
||||||
|
|
||||||
|
int output_per_branch = io_num.n_output / 3;
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
|
||||||
|
int box_idx = i * output_per_branch;
|
||||||
|
int cls_idx = i * output_per_branch + 1;
|
||||||
|
|
||||||
|
float *box_tensor = (float *)outputs[box_idx].buf;
|
||||||
|
float *cls_tensor = (float *)outputs[cls_idx].buf;
|
||||||
|
|
||||||
|
int grid_h = output_attrs[box_idx].dims[2];
|
||||||
|
int grid_w = output_attrs[box_idx].dims[3];
|
||||||
|
int stride = height / grid_h;
|
||||||
|
|
||||||
|
int box_channel = output_attrs[box_idx].dims[1];
|
||||||
|
int dfl_len = box_channel / 4;
|
||||||
|
|
||||||
|
int grid_len = grid_h * grid_w;
|
||||||
|
|
||||||
|
for (int h = 0; h < grid_h; h++) {
|
||||||
|
for (int w = 0; w < grid_w; w++) {
|
||||||
|
int offset = h * grid_w + w;
|
||||||
|
|
||||||
|
float max_score = 0.0f;
|
||||||
|
int max_class_id = -1;
|
||||||
|
|
||||||
|
for (int c = 0; c < m_class_num; c++) {
|
||||||
|
|
||||||
|
int idx = c * grid_len + offset;
|
||||||
|
float score = cls_tensor[idx];
|
||||||
|
// printf("Raw: %f, Sigmoid: %f\n", raw_score, sigmoid(raw_score));
|
||||||
|
|
||||||
|
// float score = sigmoid(cls_tensor[idx]);
|
||||||
|
if (score > max_score) {
|
||||||
|
max_score = score;
|
||||||
|
max_class_id = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_score > conf_threshold && max_class_id == 0) {
|
||||||
|
float box_pred[4];
|
||||||
|
float dfl_buffer[64];
|
||||||
|
|
||||||
|
for (int k = 0; k < 4 * dfl_len; k++) {
|
||||||
|
dfl_buffer[k] = box_tensor[k * grid_len + offset];
|
||||||
|
}
|
||||||
|
compute_dfl(dfl_buffer, dfl_len, box_pred);
|
||||||
|
|
||||||
|
float x1 = (-box_pred[0] + w + 0.5f) * stride;
|
||||||
|
float y1 = (-box_pred[1] + h + 0.5f) * stride;
|
||||||
|
float x2 = (box_pred[2] + w + 0.5f) * stride;
|
||||||
|
float y2 = (box_pred[3] + h + 0.5f) * stride;
|
||||||
|
|
||||||
|
filterBoxes.push_back(x1);
|
||||||
|
filterBoxes.push_back(y1);
|
||||||
|
filterBoxes.push_back(x2 - x1);
|
||||||
|
filterBoxes.push_back(y2 - y1);
|
||||||
|
|
||||||
|
objProbs.push_back(max_score);
|
||||||
|
classId.push_back(max_class_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_score > conf_threshold) {
|
||||||
|
|
||||||
|
float box_pred[4];
|
||||||
|
float dfl_buffer[64];
|
||||||
|
|
||||||
|
for (int k = 0; k < 4 * dfl_len; k++) {
|
||||||
|
dfl_buffer[k] = box_tensor[k * grid_len + offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
compute_dfl(dfl_buffer, dfl_len, box_pred);
|
||||||
|
|
||||||
|
float x1 = (-box_pred[0] + w + 0.5f) * stride;
|
||||||
|
float y1 = (-box_pred[1] + h + 0.5f) * stride;
|
||||||
|
float x2 = (box_pred[2] + w + 0.5f) * stride;
|
||||||
|
float y2 = (box_pred[3] + h + 0.5f) * stride;
|
||||||
|
|
||||||
|
filterBoxes.push_back(x1);
|
||||||
|
filterBoxes.push_back(y1);
|
||||||
|
filterBoxes.push_back(x2 - x1);
|
||||||
|
filterBoxes.push_back(y2 - y1);
|
||||||
|
|
||||||
|
objProbs.push_back(max_score);
|
||||||
|
classId.push_back(max_class_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<cv::Rect> cvBoxes;
|
||||||
|
for (size_t i = 0; i < filterBoxes.size(); i += 4) {
|
||||||
|
cvBoxes.push_back(cv::Rect(filterBoxes[i], filterBoxes[i + 1],
|
||||||
|
filterBoxes[i + 2], filterBoxes[i + 3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> indices;
|
||||||
|
cv::dnn::NMSBoxes(cvBoxes, objProbs, conf_threshold, nms_threshold, indices);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (int idx : indices) {
|
||||||
|
if (count >= OBJ_NUMB_MAX_SIZE)
|
||||||
|
break;
|
||||||
|
|
||||||
|
cv::Rect box = cvBoxes[idx];
|
||||||
|
|
||||||
|
int x = (int)((box.x - pad_w) / scale);
|
||||||
|
int y = (int)((box.y - pad_h) / scale);
|
||||||
|
int width = (int)(box.width / scale);
|
||||||
|
int height = (int)(box.height / scale);
|
||||||
|
|
||||||
|
detect_result_t *det = &group->results[count];
|
||||||
|
det->box.left = std::max(0, x);
|
||||||
|
det->box.top = std::max(0, y);
|
||||||
|
det->box.right = x + width;
|
||||||
|
det->box.bottom = y + height;
|
||||||
|
det->prop = objProbs[idx];
|
||||||
|
snprintf(det->name, OBJ_NAME_MAX_SIZE, "%d", classId[idx]);
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
group->count = count;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
#ifndef RKYOLOV8_H
|
||||||
|
#define RKYOLOV8_H
|
||||||
|
|
||||||
|
#include "opencv2/core/core.hpp"
|
||||||
|
#include "postprocess.h"
|
||||||
|
#include "rknn_api.h"
|
||||||
|
|
||||||
|
#include "im2d.h"
|
||||||
|
#include "rga.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class rkYolov8 {
|
||||||
|
private:
|
||||||
|
int ret;
|
||||||
|
std::string model_path;
|
||||||
|
unsigned char *model_data;
|
||||||
|
|
||||||
|
rknn_context ctx;
|
||||||
|
rknn_input_output_num io_num;
|
||||||
|
rknn_tensor_attr *input_attrs;
|
||||||
|
rknn_tensor_attr *output_attrs;
|
||||||
|
rknn_input inputs[1];
|
||||||
|
|
||||||
|
int channel, width, height;
|
||||||
|
|
||||||
|
float nms_threshold;
|
||||||
|
float conf_threshold;
|
||||||
|
|
||||||
|
std::string m_label_path;
|
||||||
|
int m_class_num;
|
||||||
|
|
||||||
|
void *rga_buffer_ptr = nullptr;
|
||||||
|
int rga_buffer_size = 0;
|
||||||
|
|
||||||
|
static unsigned char *load_model(const char *filename, int *model_size);
|
||||||
|
|
||||||
|
void post_process_v8_dfl(rknn_output *outputs, float scale, int pad_w,
|
||||||
|
int pad_h, detect_result_group_t *group);
|
||||||
|
|
||||||
|
public:
|
||||||
|
rkYolov8(const std::string &model_path, const std::string &label_path,
|
||||||
|
int class_num);
|
||||||
|
~rkYolov8();
|
||||||
|
|
||||||
|
int init(rknn_context *ctx_in, bool is_slave);
|
||||||
|
rknn_context *get_pctx();
|
||||||
|
detect_result_group_t infer(const cv::Mat &ori_img);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
// video_service_manager.cc (重构后)
|
|
||||||
#include "video_service_manager.h"
|
#include "video_service_manager.h"
|
||||||
|
#include "algorithm/HumanDetectionModule.h"
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include <fstream> // <-- 新增: 用于文件读取
|
#include <fstream>
|
||||||
|
|
||||||
VideoServiceManager::~VideoServiceManager() { stop_all(); }
|
VideoServiceManager::~VideoServiceManager() { stop_all(); }
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief [新增] 实现 load_config
|
|
||||||
*/
|
|
||||||
bool VideoServiceManager::load_config(const std::string &config_path) {
|
bool VideoServiceManager::load_config(const std::string &config_path) {
|
||||||
std::ifstream ifs(config_path);
|
std::ifstream ifs(config_path);
|
||||||
if (!ifs.is_open()) {
|
if (!ifs.is_open()) {
|
||||||
|
|
@ -19,16 +17,14 @@ bool VideoServiceManager::load_config(const std::string &config_path) {
|
||||||
nlohmann::json video_json;
|
nlohmann::json video_json;
|
||||||
ifs >> video_json;
|
ifs >> video_json;
|
||||||
|
|
||||||
// 1. 加载 video_service.enabled
|
|
||||||
m_enabled = video_json.value("/video_service/enabled"_json_pointer, false);
|
m_enabled = video_json.value("/video_service/enabled"_json_pointer, false);
|
||||||
|
|
||||||
// 2. 加载 video_streams 数组
|
|
||||||
if (video_json.contains("video_streams") &&
|
if (video_json.contains("video_streams") &&
|
||||||
video_json["video_streams"].is_array()) {
|
video_json["video_streams"].is_array()) {
|
||||||
m_stream_configs_json = video_json["video_streams"];
|
m_stream_configs_json = video_json["video_streams"];
|
||||||
} else {
|
} else {
|
||||||
spdlog::warn("Video config contains no 'video_streams' array.");
|
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: {}. "
|
spdlog::info("Successfully loaded video config. Service enabled: {}. "
|
||||||
|
|
@ -43,9 +39,6 @@ bool VideoServiceManager::load_config(const std::string &config_path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief [修改] 实现 load_and_start
|
|
||||||
*/
|
|
||||||
void VideoServiceManager::load_and_start() {
|
void VideoServiceManager::load_and_start() {
|
||||||
if (!m_enabled) {
|
if (!m_enabled) {
|
||||||
spdlog::warn("VideoService is disabled in video configuration. No streams "
|
spdlog::warn("VideoService is disabled in video configuration. No streams "
|
||||||
|
|
@ -56,10 +49,8 @@ void VideoServiceManager::load_and_start() {
|
||||||
spdlog::info("Found {} video stream configurations.",
|
spdlog::info("Found {} video stream configurations.",
|
||||||
m_stream_configs_json.size());
|
m_stream_configs_json.size());
|
||||||
|
|
||||||
// 遍历存储的 json 数组
|
|
||||||
for (const auto &sc_json : m_stream_configs_json) {
|
for (const auto &sc_json : m_stream_configs_json) {
|
||||||
|
|
||||||
// 从 JSON 中提取配置
|
|
||||||
std::string id = sc_json.value("id", "unknown");
|
std::string id = sc_json.value("id", "unknown");
|
||||||
bool enabled = sc_json.value("enabled", false);
|
bool enabled = sc_json.value("enabled", false);
|
||||||
std::string input_url = sc_json.value("input_url", "");
|
std::string input_url = sc_json.value("input_url", "");
|
||||||
|
|
@ -78,10 +69,9 @@ void VideoServiceManager::load_and_start() {
|
||||||
std::unique_ptr<IAnalysisModule> module = nullptr;
|
std::unique_ptr<IAnalysisModule> module = nullptr;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 根据配置创建 "AI模块" (策略)
|
|
||||||
if (module_type == "intrusion_detection") {
|
if (module_type == "intrusion_detection") {
|
||||||
|
|
||||||
// 从 module_config 中提取用于构造函数的参数
|
|
||||||
std::string module_model_path = module_config.value("model_path", "");
|
std::string module_model_path = module_config.value("model_path", "");
|
||||||
int module_threads = module_config.value("rknn_thread_num", 1);
|
int module_threads = module_config.value("rknn_thread_num", 1);
|
||||||
double threshold = module_config.value("time_threshold_sec", 3.0);
|
double threshold = module_config.value("time_threshold_sec", 3.0);
|
||||||
|
|
@ -104,11 +94,38 @@ void VideoServiceManager::load_and_start() {
|
||||||
zone_array[2], zone_array[3])
|
zone_array[2], zone_array[3])
|
||||||
: cv::Rect(0, 0, 0, 0);
|
: cv::Rect(0, 0, 0, 0);
|
||||||
|
|
||||||
// 创建模块 (构造函数不变)
|
|
||||||
module = std::make_unique<IntrusionModule>(
|
module = std::make_unique<IntrusionModule>(
|
||||||
module_model_path, module_threads, zone, threshold);
|
module_model_path, module_threads, zone, threshold);
|
||||||
|
|
||||||
} else if (module_type == "face_recognition") {
|
} else if (module_type == "human_detection") {
|
||||||
|
|
||||||
|
std::string module_model_path = module_config.value("model_path", "");
|
||||||
|
int module_threads = module_config.value("rknn_thread_num", 3);
|
||||||
|
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)
|
||||||
|
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<HumanDetectionModule>(
|
||||||
|
module_model_path, module_threads, zone, threshold);
|
||||||
|
|
||||||
|
spdlog::info("Stream '{}': Initialized HumanDetectionModule (YOLOv8)",
|
||||||
|
id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (module_type == "face_recognition") {
|
||||||
spdlog::warn("Module type 'face_recognition' for stream '{}' is not "
|
spdlog::warn("Module type 'face_recognition' for stream '{}' is not "
|
||||||
"yet implemented.",
|
"yet implemented.",
|
||||||
id);
|
id);
|
||||||
|
|
@ -120,16 +137,9 @@ void VideoServiceManager::load_and_start() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 创建 "管线" (VideoService) 并注入模块
|
|
||||||
// --- 关键修改 ---
|
|
||||||
// 我们需要将 module_config 传递给 VideoService,以便它在 start() 时
|
|
||||||
// 可以调用 module->init(module_config)
|
|
||||||
auto service = std::make_unique<VideoService>(
|
auto service = std::make_unique<VideoService>(
|
||||||
std::move(module), input_url, output_rtsp,
|
std::move(module), input_url, output_rtsp, module_config);
|
||||||
module_config // <-- [修改] 传递完整的模块配置
|
|
||||||
);
|
|
||||||
|
|
||||||
// 3. 启动服务 (逻辑不变)
|
|
||||||
if (service->start()) {
|
if (service->start()) {
|
||||||
spdlog::info("Successfully started video service for stream '{}' "
|
spdlog::info("Successfully started video service for stream '{}' "
|
||||||
"[Module: {}]. Output is [{}].",
|
"[Module: {}]. Output is [{}].",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// video_service_manager.h (重构后)
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "algorithm/IAnalysisModule.h"
|
#include "algorithm/IAnalysisModule.h"
|
||||||
|
|
@ -14,32 +14,18 @@ public:
|
||||||
VideoServiceManager() = default;
|
VideoServiceManager() = default;
|
||||||
~VideoServiceManager();
|
~VideoServiceManager();
|
||||||
|
|
||||||
// 禁止拷贝和赋值
|
|
||||||
VideoServiceManager(const VideoServiceManager &) = delete;
|
VideoServiceManager(const VideoServiceManager &) = delete;
|
||||||
VideoServiceManager &operator=(const VideoServiceManager &) = delete;
|
VideoServiceManager &operator=(const VideoServiceManager &) = delete;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief [新增] 从独立的 video_config.json 加载配置
|
|
||||||
* @param config_path 视频配置文件的路径
|
|
||||||
* @return bool 是否加载成功
|
|
||||||
*/
|
|
||||||
bool load_config(const std::string &config_path);
|
bool load_config(const std::string &config_path);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief [修改] 根据已加载的配置启动所有服务
|
|
||||||
* 不再需要 ConfigManager& config 参数
|
|
||||||
*/
|
|
||||||
void load_and_start();
|
void load_and_start();
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 停止并销毁所有正在运行的视频服务
|
|
||||||
*/
|
|
||||||
void stop_all();
|
void stop_all();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::unique_ptr<VideoService>> services_;
|
std::vector<std::unique_ptr<VideoService>> services_;
|
||||||
|
|
||||||
// [新增] 用于存储从 video_config.json 解析的数据
|
|
||||||
bool m_enabled = false;
|
bool m_enabled = false;
|
||||||
nlohmann::json m_stream_configs_json;
|
nlohmann::json m_stream_configs_json;
|
||||||
};
|
};
|
||||||
Loading…
Reference in New Issue