update: add yoloV8 pre/postprocess and human detection method. #4

Merged
guanyuankai merged 1 commits from video into main 2025-11-19 11:13:16 +08:00
14 changed files with 2972 additions and 70 deletions

View File

@ -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"

View File

@ -64,11 +64,13 @@ add_library(edge_proxy_lib STATIC
src/alarm/alarm_service.cc src/alarm/alarm_service.cc
# #
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

View File

@ -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,

View File

@ -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"
} }
] ]
} }

2242
debug_input.rgb Normal file

File diff suppressed because one or more lines are too long

3
models/human.txt Normal file
View File

@ -0,0 +1,3 @@
full_body
visible_body
head

BIN
models/human_detection.rknn Normal file

Binary file not shown.

View File

@ -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.");
}

View File

@ -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_;
};

View File

@ -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);

309
src/rknn/rkYolov8.cc Normal file
View File

@ -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;
}

52
src/rknn/rkYolov8.hpp Normal file
View File

@ -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

View File

@ -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 [{}].",

View File

@ -1,9 +1,9 @@
// video_service_manager.h (重构后)
#pragma once #pragma once
#include "algorithm/IAnalysisModule.h" #include "algorithm/IAnalysisModule.h"
#include "algorithm/IntrusionModule.h" #include "algorithm/IntrusionModule.h"
#include "nlohmann/json.hpp" #include "nlohmann/json.hpp"
#include "rknn/video_service.h" #include "rknn/video_service.h"
#include <memory> #include <memory>
#include <string> #include <string>
@ -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;
}; };