项目背景
最近停在门前的车被人开走了,虽然有监控,但是看监控太麻烦了,于是想着框选一个区域用yolov8直接检测闯入到这个区域的所有目标,这样1ms一帧,很快就可以跑完一天的视频
用到的技术
- C++
- OpenCV
- Yolov8 + OnnxRuntime
yolov8介绍
- YOLOv8支持Pose和Segment,在使用TensorRT可以跑到1-2ms一帧
- YOLOv8提供了一个全新的SOTA模型,包括P5 640和P6 1280分辨率的目标检测网络和基于YOLACT的实例分割模型。
- YOLOv8和YOLOv5一样,基于缩放系数也提供了N/S/M/L/X尺度的不同大小模型,用于满足不同场景需求。
- YOLOv8骨干网络和Neck部分可能参考了YOLOv7 ELAN设计思想,将YOLOv5的C3结构换成了梯度流更丰富的C2f结构,并对不同尺度模型调整了不同的通道数。
- YOLOv8 Head部分相比YOLOv5改动较大,换成了目前主流的解耦头结构,将分类和检测头分离,同时也从Anchor-Based换成了Anchor-Free。
- YOLOv8 Loss计算方面采用了TaskAlignedAssigner正样本分配策略,并引入了Distribution Focal Loss。
- YOLOv8训练的数据增强部分引入了YOLOX中的最后10 epoch关闭Mosiac增强的操作,可以有效地提升精度。
实现步骤
- 首先打开视频第一帧,框选区域,我们直接使用opencv实现这个功能
- 加载模型检测画面中的所有对象
- 计算IOU,如果有重合就保存这一帧具体信息
- 跟踪闯入画面的目标,否则会重复保存信息
使用opencv打开视频,并框选区域
#include <opencv2/opencv.hpp>
#include "inference.h"
using namespace cv;
// 定义一个全局变量,用于存放鼠标框选的矩形区域
Rect g_rect;
// 定义一个全局变量,用于标记鼠标是否按下
bool g_bDrawingBox = false;
// 定义一个回调函数,用于处理鼠标事件
void on_MouseHandle(int event, int x, int y, int flags, void* param)
{
// 将param转换为Mat类型的指针
Mat& image = *(Mat*) param;
// 根据不同的鼠标事件进行处理
switch (event)
{
// 鼠标左键按下事件
case EVENT_LBUTTONDOWN:
{
// 标记鼠标已按下
g_bDrawingBox = true;
// 记录矩形框的起始点
g_rect.x = x;
g_rect.y = y;
break;
}
// 鼠标移动事件
case EVENT_MOUSEMOVE:
{
// 如果鼠标已按下,更新矩形框的宽度和高度
if (g_bDrawingBox)
{
g_rect.width = x - g_rect.x;
g_rect.height = y - g_rect.y;
}
break;
}
// 鼠标左键松开事件
case EVENT_LBUTTONUP:
{
// 标记鼠标已松开
g_bDrawingBox = false;
// 如果矩形框的宽度和高度为正,绘制矩形框到图像上
if (g_rect.width > 0 && g_rect.height > 0)
{
rectangle(image, g_rect, Scalar(0, 255, 0));
}
break;
}
}
}
int main(int argc, char* argv[])
{
// 读取视频文件
cv::VideoCapture vc;
vc.open(argv[1]);
if(vc.isOpened()){
cv::Mat frame;
vc >> frame;
if(!frame.empty()){
// 创建一个副本图像,用于显示框选过程
Mat temp;
frame.copyTo(temp);
// 创建一个窗口,显示图像
namedWindow("image");
// 设置鼠标回调函数,传入副本图像作为参数
setMouseCallback("image", on_MouseHandle, (void*)&temp);
while (1)
{
// 如果鼠标正在框选,绘制一个虚线矩形框到副本图像上,并显示框的大小和坐标
if (g_bDrawingBox)
{
temp.copyTo(frame);
rectangle(frame, g_rect, Scalar(0, 255, 0), 1, LINE_AA);
char text[32];
sprintf(text, "w=%d, h=%d", g_rect.width, g_rect.height);
putText(frame, text, Point(g_rect.x + 5, g_rect.y - 5), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0));
}
// 显示副本图像
imshow("image", frame);
// 等待按键,如果按下ESC键,退出循环
if (waitKey(10) == 27)
{
break;
}
}
while(!frame.empty()){
cv::imshow("image", frame);
cv::waitKey(1);
vc >> frame;
}
}
}
return 0;
}
使用YoloV8检测目标
inference.h
#pragma once
#define RET_OK nullptr
#ifdef _WIN32
#include <Windows.h>
#include <direct.h>
#include <io.h>
#endif
#include <string>
#include <vector>
#include <cstdio>
#include <opencv2/opencv.hpp>
#include "onnxruntime_cxx_api.h"
#ifdef USE_CUDA
#include <cuda_fp16.h>
#endif
enum MODEL_TYPE {
//FLOAT32 MODEL
YOLO_ORIGIN_V5 = 0,
YOLO_ORIGIN_V8 = 1,//only support v8 detector currently
YOLO_POSE_V8 = 2,
YOLO_CLS_V8 = 3,
YOLO_ORIGIN_V8_HALF = 4,
YOLO_POSE_V8_HALF = 5,
YOLO_CLS_V8_HALF = 6
};
typedef struct _DCSP_INIT_PARAM {
std::string ModelPath;
MODEL_TYPE ModelType = YOLO_ORIGIN_V8;
std::vector<int> imgSize = {640, 640};
float RectConfidenceThreshold = 0.6;
float iouThreshold = 0.5;
bool CudaEnable = false;
int LogSeverityLevel = 3;
int IntraOpNumThreads = 1;
} DCSP_INIT_PARAM;
typedef struct _DCSP_RESULT {
int classId;
float confidence;
cv::Rect box;
} DCSP_RESULT;
class DCSP_CORE {
public:
DCSP_CORE();
~DCSP_CORE();
public:
char *CreateSession(DCSP_INIT_PARAM &iParams);
char *RunSession(cv::Mat &iImg, std::vector<DCSP_RESULT> &oResult);
char *WarmUpSession();
template<typename N>
char *TensorProcess(clock_t &starttime_1, cv::Mat &iImg, N &blob, std::vector<int64_t> &inputNodeDims,
std::vector<DCSP_RESULT> &oResult);
std::vector<std::string> classes{};
private:
Ort::Env env;
Ort::Session *session;
bool cudaEnable;
Ort::RunOptions options;
std::vector<const char *> inputNodeNames;
std::vector<const char *> outputNodeNames;
MODEL_TYPE modelType;
std::vector<int> imgSize;
float rectConfidenceThreshold;
float iouThreshold;
};
inference.cpp
#include "inference.h"
#include <regex>
#define benchmark
DCSP_CORE::DCSP_CORE() {
}
DCSP_CORE::~DCSP_CORE() {
delete session;
}
#ifdef USE_CUDA
namespace Ort
{
template<>
struct TypeToTensorType<half> { static constexpr ONNXTensorElementDataType type = ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16; };
}
#endif
template<typename T>
char *BlobFromImage(cv::Mat &iImg, T &iBlob) {
int channels = iImg.channels();
int imgHeight = iImg.rows;
int imgWidth = iImg.cols;
for (int c = 0; c < channels; c++) {
for (int h = 0; h < imgHeight; h++) {
for (int w = 0; w < imgWidth; w++) {
iBlob[c * imgWidth * imgHeight + h * imgWidth + w] = typename std::remove_pointer<T>::type(
(iImg.at<cv::Vec3b>(h, w)[c]) / 255.0f);
}
}
}
return RET_OK;
}
char *PostProcess(cv::Mat &iImg, std::vector<int> iImgSize, cv::Mat &oImg) {
cv::Mat img = iImg.clone();
cv::resize(iImg, oImg, cv::Size(iImgSize.at(0), iImgSize.at(1)));
if (img.channels() == 1) {
cv::cvtColor(oImg, oImg, cv::COLOR

该博客围绕基于OpenCV和Yolov8的目标检测项目展开。因查看监控不便,项目旨在框选区域检测闯入目标。介绍了用到的C++、OpenCV、Yolov8 + OnnxRuntime技术,阐述了Yolov8特点,还给出使用OpenCV框选区域、Yolov8检测目标等实现步骤。
最低0.47元/天 解锁文章
2238

被折叠的 条评论
为什么被折叠?



