opencv+yolov8实现监控画面报警功能

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

项目背景

最近停在门前的车被人开走了,虽然有监控,但是看监控太麻烦了,于是想着框选一个区域用yolov8直接检测闯入到这个区域的所有目标,这样1ms一帧,很快就可以跑完一天的视频

用到的技术

  1. C++
  2. OpenCV
  3. 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增强的操作,可以有效地提升精度。

实现步骤

  1. 首先打开视频第一帧,框选区域,我们直接使用opencv实现这个功能
  2. 加载模型检测画面中的所有对象
  3. 计算IOU,如果有重合就保存这一帧具体信息
  4. 跟踪闯入画面的目标,否则会重复保存信息

使用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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

telllong

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值