降低视频采集的CPU占有率

This commit is contained in:
GuanYuankai 2025-10-27 07:23:35 +00:00
parent 1e77b39d69
commit f9a48f07b7
1 changed files with 110 additions and 47 deletions

View File

@ -53,13 +53,24 @@ bool VideoService::start() {
}
printf("rknnPool init success.\n");
setenv("OPENCV_FFMPEG_CAPTURE_OPTIONS", "rtsp_transport;tcp", 1);
printf("Set RTSP transport protocol to TCP\n");
// setenv("OPENCV_FFMPEG_CAPTURE_OPTIONS", "rtsp_transport;tcp", 1);
// printf("Set RTSP transport protocol to TCP\n");
std::string gst_input_pipeline =
"rtspsrc location=" + input_url_ + " latency=0 protocols=tcp ! "
"rtph265depay ! "
"h265parse ! "
"mppvideodec format=16 ! "
"video/x-raw,format=BGR ! " // <-- 关键:直接请求 mppvideodec 输出 BGR 格式
"appsink";
capture_.open(input_url_, cv::CAP_FFMPEG);
spdlog::info("Try to Open RTSP Stream");
capture_.open(gst_input_pipeline, cv::CAP_GSTREAMER);
if (!capture_.isOpened()) {
printf("Error: Could not open RTSP stream: %s\n", input_url_.c_str());
return false;
}else{
spdlog::info("RTSP Stream Opened!");
}
frame_width_ = static_cast<int>(capture_.get(cv::CAP_PROP_FRAME_WIDTH));
@ -70,13 +81,13 @@ bool VideoService::start() {
printf("RTSP stream opened successfully! (%dx%d @ %.2f FPS)\n", frame_width_, frame_height_, frame_fps_);
std::string gst_pipeline =
std::string gst_pipeline =
"appsrc ! "
"queue max-size-buffers=2 leaky=downstream ! "
"video/x-raw,format=BGR ! "
"videoconvert ! "
"video/x-raw,format=NV12 ! "
"mpph265enc gop=25 rc-mode=fixqp qp-init=26 ! "
"queue max-size-buffers=2 leaky=downstream ! "
"video/x-raw,format=BGR ! " // OpenCV VideoWriter 输入 BGR 数据
"videoconvert ! " // <-- 使用 CPU 将 BGR 转换为 NV12
"video/x-raw,format=NV12 ! " // 明确指定 videoconvert 输出 NV12
"mpph265enc gop=25 rc-mode=fixqp qp-init=26 ! " // 硬件编码器接收 NV12 数据
"h265parse ! "
"rtspclientsink location=" + output_rtsp_url_ + " latency=0 protocols=tcp";
@ -131,18 +142,48 @@ void VideoService::stop() {
void VideoService::update_tracker(detect_result_group_t &detect_result_group, const cv::Size& frame_size)
{
if (intrusion_zone_.width == 0 || intrusion_zone_.height == 0) {
// 如果入侵区域无效,则设置为帧中心的 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);
}
std::vector<cv::Rect> current_detections;
// --- 缩放比例计算 ---
// !!! 重要: 请确保这里的 640.0f 与您 OpenCV resize 时的目标尺寸一致 !!!
const float model_input_width = 640.0f;
const float model_input_height = 640.0f;
float scale_x = (float)frame_size.width / model_input_width;
float scale_y = (float)frame_size.height / model_input_height;
// --- 结束缩放比例计算 ---
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]);
// 只处理 "person" 类别
if (strcmp(det_result->name, "person") == 0) {
current_detections.push_back(cv::Rect(
det_result->box.left, det_result->box.top,
det_result->box.right - det_result->box.left,
det_result->box.bottom - det_result->box.top));
// --- 将模型输出坐标按比例缩放回原始帧坐标 ---
int original_left = static_cast<int>(det_result->box.left * scale_x);
int original_top = static_cast<int>(det_result->box.top * scale_y);
int original_right = static_cast<int>(det_result->box.right * scale_x);
int original_bottom = static_cast<int>(det_result->box.bottom * scale_y);
// --- 边界检查与修正 ---
// 确保坐标不会超出原始图像边界
original_left = std::max(0, std::min(original_left, frame_size.width - 1));
original_top = std::max(0, std::min(original_top, frame_size.height - 1));
// 确保 right >= left, bottom >= top
original_right = std::max(original_left, std::min(original_right, frame_size.width));
original_bottom = std::max(original_top, std::min(original_bottom, frame_size.height));
// --- 结束边界检查 ---
// 只有当框有效时宽度和高度大于0才添加到检测列表
if (original_right > original_left && original_bottom > original_top) {
current_detections.push_back(cv::Rect(
original_left, original_top,
original_right - original_left, // width
original_bottom - original_top // height
));
}
// --- 结束坐标缩放 ---
}
}
@ -150,65 +191,86 @@ void VideoService::update_tracker(detect_result_group_t &detect_result_group, co
it->second.frames_unseen++;
}
for (const auto& det_box : current_detections) {
bool is_matched = false;
int best_match_id = -1;
double max_iou = 0.3; // IoU阈值
std::vector<int> matched_track_ids; // 记录本帧已匹配到的跟踪ID防止一个跟踪ID匹配多个检测框
for (auto const& [id, person] : this->tracked_persons_) {
// 尝试将当前检测框与已跟踪目标进行匹配
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; // 跳过已匹配的跟踪目标
}
// 计算 IoU (现在 det_box 和 person.box 都是原始坐标系)
double iou = (double)(det_box & person.box).area() / (double)(det_box | person.box).area();
if (iou > max_iou) {
max_iou = iou;
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;
is_matched = true;
matched_track_ids.push_back(best_match_id); // 记录已匹配
} else {
// 没有找到匹配,创建新的跟踪目标
TrackedPerson new_person;
new_person.id = this->next_track_id_++; // 使用成员变量
new_person.id = next_track_id_++; // 分配新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; // 使用成员变量
tracked_persons_[new_person.id] = new_person;
}
}
// 更新每个跟踪目标的区域状态和报警状态
double current_time = get_current_time_seconds();
for (auto it = tracked_persons_.begin(); it != tracked_persons_.end(); ++it) {
TrackedPerson& person = it->second;
// 使用成员变量
bool currently_in_zone = (this->intrusion_zone_ & person.box).area() > 0;
// 检查人与入侵区域的交集 (现在都是原始坐标系)
bool currently_in_zone = (intrusion_zone_ & person.box).area() > 0;
if (currently_in_zone) {
if (!person.is_in_zone) {
if (!person.is_in_zone) { // 刚进入区域
person.is_in_zone = true;
person.entry_time = current_time;
} else {
// 使用成员变量
if (!person.alarm_triggered && (current_time - person.entry_time) > this->intrusion_time_threshold_) {
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); // 调用成员函数
trigger_alarm(person.id, person.box); // 触发报警
}
}
} else {
person.is_in_zone = false;
person.entry_time = 0;
person.alarm_triggered = false;
} else { // 当前不在区域内
if (person.is_in_zone) { // 刚离开区域
person.is_in_zone = false;
person.entry_time = 0; // 重置进入时间
person.alarm_triggered = false; // 重置报警状态
}
}
}
for (auto it = tracked_persons_.begin(); it != tracked_persons_.end(); ) {
// (建议) 增加到50帧 (约2秒) 提高鲁棒性减少ID切换
if (it->second.frames_unseen > 50) {
it = tracked_persons_.erase(it);
for (auto it = tracked_persons_.begin(); it != tracked_persons_.end(); /* 无自增 */) {
if (it->second.frames_unseen > 50) { // 超过 50 帧未见则移除
it = tracked_persons_.erase(it); // erase 返回下一个有效迭代器
} else {
++it;
++it; // 手动移动到下一个
}
}
}
@ -265,8 +327,6 @@ void VideoService::processing_loop() {
detect_result_group_t detection_results;
while (running_) {
// auto t_start = std::chrono::high_resolution_clock::now();
{
std::unique_lock<std::mutex> lock(frame_mutex_);
@ -283,14 +343,17 @@ void VideoService::processing_loop() {
new_frame_available_ = false;
}
// auto t_read = std::chrono::high_resolution_clock::now();
if (frame.empty()) {
continue;
}
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();
}
// [保留] 后续的 AI 推理、跟踪、绘图、推流逻辑完全不变
if (rknn_pool_->put(frame) != 0) {
if (rknn_pool_->put(model_input_image) != 0) {
spdlog::error("VideoService: Failed to put frame into rknnPool. Stopping.");
running_ = false;
break;