opencv+yolov8实现监控画面报警功能
项目背景
最近停在门前的车被人开走了,虽然有监控,但是看监控太麻烦了,于是想着框选一个区域用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>
#endifenum MODEL_TYPE {//FLOAT32 MODELYOLO_ORIGIN_V5 = 0,YOLO_ORIGIN_V8 = 1,//only support v8 detector currentlyYOLO_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 benchmarkDCSP_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; };
}
#endiftemplate<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_GRAY2BGR);}cv::cvtColor(oImg, oImg, cv::COLOR_BGR2RGB);return RET_OK;
}char *DCSP_CORE::CreateSession(DCSP_INIT_PARAM &iParams) {char *Ret = RET_OK;std::regex pattern("[\u4e00-\u9fa5]");bool result = std::regex_search(iParams.ModelPath, pattern);if (result) {Ret = "[DCSP_ONNX]:Model path error.Change your model path without chinese characters.";std::cout << Ret << std::endl;return Ret;}try {rectConfidenceThreshold = iParams.RectConfidenceThreshold;iouThreshold = iParams.iouThreshold;imgSize = iParams.imgSize;modelType = iParams.ModelType;env = Ort::Env(ORT_LOGGING_LEVEL_WARNING, "Yolo");Ort::SessionOptions sessionOption;if (iParams.CudaEnable) {cudaEnable = iParams.CudaEnable;OrtCUDAProviderOptions cudaOption;cudaOption.device_id = 0;sessionOption.AppendExecutionProvider_CUDA(cudaOption);}sessionOption.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);sessionOption.SetIntraOpNumThreads(iParams.IntraOpNumThreads);sessionOption.SetLogSeverityLevel(iParams.LogSeverityLevel);#ifdef _WIN32int ModelPathSize = MultiByteToWideChar(CP_UTF8, 0, iParams.ModelPath.c_str(), static_cast<int>(iParams.ModelPath.length()), nullptr, 0);wchar_t* wide_cstr = new wchar_t[ModelPathSize + 1];MultiByteToWideChar(CP_UTF8, 0, iParams.ModelPath.c_str(), static_cast<int>(iParams.ModelPath.length()), wide_cstr, ModelPathSize);wide_cstr[ModelPathSize] = L'\0';const wchar_t* modelPath = wide_cstr;
#elseconst char *modelPath = iParams.ModelPath.c_str();
#endif // _WIN32session = new Ort::Session(env, modelPath, sessionOption);Ort::AllocatorWithDefaultOptions allocator;size_t inputNodesNum = session->GetInputCount();for (size_t i = 0; i < inputNodesNum; i++) {Ort::AllocatedStringPtr input_node_name = session->GetInputNameAllocated(i, allocator);char *temp_buf = new char[50];strcpy(temp_buf, input_node_name.get());inputNodeNames.push_back(temp_buf);}size_t OutputNodesNum = session->GetOutputCount();for (size_t i = 0; i < OutputNodesNum; i++) {Ort::AllocatedStringPtr output_node_name = session->GetOutputNameAllocated(i, allocator);char *temp_buf = new char[10];strcpy(temp_buf, output_node_name.get());outputNodeNames.push_back(temp_buf);}options = Ort::RunOptions{nullptr};WarmUpSession();return RET_OK;}catch (const std::exception &e) {const char *str1 = "[DCSP_ONNX]:";const char *str2 = e.what();std::string result = std::string(str1) + std::string(str2);char *merged = new char[result.length() + 1];std::strcpy(merged, result.c_str());std::cout << merged << std::endl;delete[] merged;return "[DCSP_ONNX]:Create session failed.";}}char *DCSP_CORE::RunSession(cv::Mat &iImg, std::vector<DCSP_RESULT> &oResult) {
#ifdef benchmarkclock_t starttime_1 = clock();
#endif // benchmarkchar *Ret = RET_OK;cv::Mat processedImg;PostProcess(iImg, imgSize, processedImg);if (modelType < 4) {float *blob = new float[processedImg.total() * 3];BlobFromImage(processedImg, blob);std::vector<int64_t> inputNodeDims = {1, 3, imgSize.at(0), imgSize.at(1)};TensorProcess(starttime_1, iImg, blob, inputNodeDims, oResult);} else {
#ifdef USE_CUDAhalf* blob = new half[processedImg.total() * 3];BlobFromImage(processedImg, blob);std::vector<int64_t> inputNodeDims = { 1,3,imgSize.at(0),imgSize.at(1) };TensorProcess(starttime_1, iImg, blob, inputNodeDims, oResult);
#endif}return Ret;
}template<typename N>
char *DCSP_CORE::TensorProcess(clock_t &starttime_1, cv::Mat &iImg, N &blob, std::vector<int64_t> &inputNodeDims,std::vector<DCSP_RESULT> &oResult) {Ort::Value inputTensor = Ort::Value::CreateTensor<typename std::remove_pointer<N>::type>(Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU), blob, 3 * imgSize.at(0) * imgSize.at(1),inputNodeDims.data(), inputNodeDims.size());
#ifdef benchmarkclock_t starttime_2 = clock();
#endif // benchmarkauto outputTensor = session->Run(options, inputNodeNames.data(), &inputTensor, 1, outputNodeNames.data(),outputNodeNames.size());
#ifdef benchmarkclock_t starttime_3 = clock();
#endif // benchmarkOrt::TypeInfo typeInfo = outputTensor.front().GetTypeInfo();auto tensor_info = typeInfo.GetTensorTypeAndShapeInfo();std::vector<int64_t> outputNodeDims = tensor_info.GetShape();auto output = outputTensor.front().GetTensorMutableData<typename std::remove_pointer<N>::type>();delete blob;switch (modelType) {case 1://V8_ORIGIN_FP32case 4://V8_ORIGIN_FP16{int strideNum = outputNodeDims[2];int signalResultNum = outputNodeDims[1];std::vector<int> class_ids;std::vector<float> confidences;std::vector<cv::Rect> boxes;cv::Mat rawData;if (modelType == 1) {// FP32rawData = cv::Mat(signalResultNum, strideNum, CV_32F, output);} else {// FP16rawData = cv::Mat(signalResultNum, strideNum, CV_16F, output);rawData.convertTo(rawData, CV_32F);}rawData = rawData.t();float *data = (float *) rawData.data;float x_factor = iImg.cols / 640.;float y_factor = iImg.rows / 640.;for (int i = 0; i < strideNum; ++i) {float *classesScores = data + 4;cv::Mat scores(1, this->classes.size(), CV_32FC1, classesScores);cv::Point class_id;double maxClassScore;cv::minMaxLoc(scores, 0, &maxClassScore, 0, &class_id);if (maxClassScore > rectConfidenceThreshold) {confidences.push_back(maxClassScore);class_ids.push_back(class_id.x);float x = data[0];float y = data[1];float w = data[2];float h = data[3];int left = int((x - 0.5 * w) * x_factor);int top = int((y - 0.5 * h) * y_factor);int width = int(w * x_factor);int height = int(h * y_factor);boxes.emplace_back(left, top, width, height);}data += signalResultNum;}std::vector<int> nmsResult;cv::dnn::NMSBoxes(boxes, confidences, rectConfidenceThreshold, iouThreshold, nmsResult);for (int i = 0; i < nmsResult.size(); ++i) {int idx = nmsResult[i];DCSP_RESULT result;result.classId = class_ids[idx];result.confidence = confidences[idx];result.box = boxes[idx];oResult.push_back(result);}#ifdef benchmarkclock_t starttime_4 = clock();double pre_process_time = (double) (starttime_2 - starttime_1) / CLOCKS_PER_SEC * 1000;double process_time = (double) (starttime_3 - starttime_2) / CLOCKS_PER_SEC * 1000;double post_process_time = (double) (starttime_4 - starttime_3) / CLOCKS_PER_SEC * 1000;if (cudaEnable) {std::cout << "[DCSP_ONNX(CUDA)]: " << pre_process_time << "ms pre-process, " << process_time<< "ms inference, " << post_process_time << "ms post-process." << std::endl;} else {std::cout << "[DCSP_ONNX(CPU)]: " << pre_process_time << "ms pre-process, " << process_time<< "ms inference, " << post_process_time << "ms post-process." << std::endl;}
#endif // benchmarkbreak;}}return RET_OK;
}char *DCSP_CORE::WarmUpSession() {clock_t starttime_1 = clock();cv::Mat iImg = cv::Mat(cv::Size(imgSize.at(0), imgSize.at(1)), CV_8UC3);cv::Mat processedImg;PostProcess(iImg, imgSize, processedImg);if (modelType < 4) {float *blob = new float[iImg.total() * 3];BlobFromImage(processedImg, blob);std::vector<int64_t> YOLO_input_node_dims = {1, 3, imgSize.at(0), imgSize.at(1)};Ort::Value input_tensor = Ort::Value::CreateTensor<float>(Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU), blob, 3 * imgSize.at(0) * imgSize.at(1),YOLO_input_node_dims.data(), YOLO_input_node_dims.size());auto output_tensors = session->Run(options, inputNodeNames.data(), &input_tensor, 1, outputNodeNames.data(),outputNodeNames.size());delete[] blob;clock_t starttime_4 = clock();double post_process_time = (double) (starttime_4 - starttime_1) / CLOCKS_PER_SEC * 1000;if (cudaEnable) {std::cout << "[DCSP_ONNX(CUDA)]: " << "Cuda warm-up cost " << post_process_time << " ms. " << std::endl;}} else {
#ifdef USE_CUDAhalf* blob = new half[iImg.total() * 3];BlobFromImage(processedImg, blob);std::vector<int64_t> YOLO_input_node_dims = { 1,3,imgSize.at(0),imgSize.at(1) };Ort::Value input_tensor = Ort::Value::CreateTensor<half>(Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU), blob, 3 * imgSize.at(0) * imgSize.at(1), YOLO_input_node_dims.data(), YOLO_input_node_dims.size());auto output_tensors = session->Run(options, inputNodeNames.data(), &input_tensor, 1, outputNodeNames.data(), outputNodeNames.size());delete[] blob;clock_t starttime_4 = clock();double post_process_time = (double)(starttime_4 - starttime_1) / CLOCKS_PER_SEC * 1000;if (cudaEnable){std::cout << "[DCSP_ONNX(CUDA)]: " << "Cuda warm-up cost " << post_process_time << " ms. " << std::endl;}
#endif}return RET_OK;
}
main.cpp
int read_coco_yaml(DCSP_CORE *&p) {// Open the YAML filestd::ifstream file("coco.yaml");if (!file.is_open()) {std::cerr << "Failed to open file" << std::endl;return 1;}// Read the file line by linestd::string line;std::vector<std::string> lines;while (std::getline(file, line)) {lines.push_back(line);}// Find the start and end of the names sectionstd::size_t start = 0;std::size_t end = 0;for (std::size_t i = 0; i < lines.size(); i++) {if (lines[i].find("names:") != std::string::npos) {start = i + 1;} else if (start > 0 && lines[i].find(':') == std::string::npos) {end = i;break;}}// Extract the namesstd::vector<std::string> names;for (std::size_t i = start; i < end; i++) {std::stringstream ss(lines[i]);std::string name;std::getline(ss, name, ':'); // Extract the number before the delimiterstd::getline(ss, name); // Extract the string after the delimiternames.push_back(name);}p->classes = names;return 0;
}int main(int argc, char* argv[])
{DCSP_CORE *yoloDetector = new DCSP_CORE;//std::string model_path = "yolov8n.onnx";std::string model_path = argv[1];read_coco_yaml(yoloDetector);#ifdef USE_CUDA// GPU FP32 inferenceDCSP_INIT_PARAM params{ model_path, YOLO_ORIGIN_V8, {640, 640}, 0.1, 0.5, true };// GPU FP16 inference// DCSP_INIT_PARAM params{ model_path, YOLO_ORIGIN_V8_HALF, {640, 640}, 0.1, 0.5, true };#else// CPU inferenceDCSP_INIT_PARAM params{model_path, YOLO_ORIGIN_V8, {640, 640}, 0.1, 0.5, false};#endifyoloDetector->CreateSession(params);cv::VideoCapture vc;vc.open(argv[2]);if(vc.isOpened()){cv::Mat frame;vc >> frame;while(!frame.empty()){std::vector<DCSP_RESULT> res;yoloDetector->RunSession(frame, res);for (int i = 0; i < res.size(); ++i){DCSP_RESULT detection = res[i];cv::Rect box = detection.box;cv::RNG rng(cv::getTickCount());cv::Scalar color(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));;// Detection boxcv::rectangle(frame, box, color, 2);// Detection box textstd::string classString = yoloDetector->classes[detection.classId] + ' ' + std::to_string(detection.confidence).substr(0, 4);cv::Size textSize = cv::getTextSize(classString, cv::FONT_HERSHEY_DUPLEX, 1, 2, 0);cv::Rect textBox(box.x, box.y - 40, textSize.width + 10, textSize.height + 20);cv::rectangle(frame, textBox, color, cv::FILLED);cv::putText(frame, classString, cv::Point(box.x + 5, box.y - 10), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 0), 2, 0);}cv::rectangle(frame, g_rect, Scalar(0, 255, 0), 3, cv::LINE_AA);cv::imshow("image", frame);cv::waitKey(1);vc >> frame;}}
}
opencv的框选区域和yolov8检测目标框融合
#include <opencv2/opencv.hpp>
#include <fstream>
#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 read_coco_yaml(DCSP_CORE *&p) {// Open the YAML filestd::ifstream file("coco.yaml");if (!file.is_open()) {std::cerr << "Failed to open file" << std::endl;return 1;}// Read the file line by linestd::string line;std::vector<std::string> lines;while (std::getline(file, line)) {lines.push_back(line);}// Find the start and end of the names sectionstd::size_t start = 0;std::size_t end = 0;for (std::size_t i = 0; i < lines.size(); i++) {if (lines[i].find("names:") != std::string::npos) {start = i + 1;} else if (start > 0 && lines[i].find(':') == std::string::npos) {end = i;break;}}// Extract the namesstd::vector<std::string> names;for (std::size_t i = start; i < end; i++) {std::stringstream ss(lines[i]);std::string name;std::getline(ss, name, ':'); // Extract the number before the delimiterstd::getline(ss, name); // Extract the string after the delimiternames.push_back(name);}p->classes = names;return 0;
}int main(int argc, char* argv[])
{// 读取原始图像// Mat src = imread(argv[1]);DCSP_CORE *yoloDetector = new DCSP_CORE;//std::string model_path = "yolov8n.onnx";std::string model_path = argv[1];read_coco_yaml(yoloDetector);
#ifdef USE_CUDA// GPU FP32 inferenceDCSP_INIT_PARAM params{ model_path, YOLO_ORIGIN_V8, {640, 640}, 0.1, 0.5, true };// GPU FP16 inference// DCSP_INIT_PARAM params{ model_path, YOLO_ORIGIN_V8_HALF, {640, 640}, 0.1, 0.5, true };
#else// CPU inferenceDCSP_INIT_PARAM params{model_path, YOLO_ORIGIN_V8, {640, 640}, 0.1, 0.5, false};
#endifyoloDetector->CreateSession(params);cv::VideoCapture vc;vc.open(argv[2]);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()){std::vector<DCSP_RESULT> res;yoloDetector->RunSession(frame, res);for (int i = 0; i < res.size(); ++i){DCSP_RESULT detection = res[i];cv::Rect box = detection.box;cv::RNG rng(cv::getTickCount());cv::Scalar color(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));;// Detection boxcv::rectangle(frame, box, color, 2);// Detection box textstd::string classString = yoloDetector->classes[detection.classId] + ' ' + std::to_string(detection.confidence).substr(0, 4);cv::Size textSize = cv::getTextSize(classString, cv::FONT_HERSHEY_DUPLEX, 1, 2, 0);cv::Rect textBox(box.x, box.y - 40, textSize.width + 10, textSize.height + 20);cv::rectangle(frame, textBox, color, cv::FILLED);cv::putText(frame, classString, cv::Point(box.x + 5, box.y - 10), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 0), 2, 0);}cv::rectangle(frame, g_rect, Scalar(0, 255, 0), 3, cv::LINE_AA);cv::imshow("image", frame);cv::waitKey(1);vc >> frame;}}}return 0;
}
计算预警区域和目标框重合度
double calIou(const cv::Rect& rc1, const cv::Rect& rc2)
{cv::Rect intersection = rc1 & rc2;if (!intersection.empty()) {double intersectionArea = intersection.width * intersection.height;double rect1Area = rc1.width * rc1.height;double rect2Area = rc2.width * rc2.height;// 计算IOUdouble iou = intersectionArea / (rect1Area + rect2Area - intersectionArea);return iou;} else {// 没有重叠,IOU为0return 0.0;}
}
跟踪实现
不断的去循环激活的目标,来过滤掉重复的代码,这块以后实现
完整代码
#include <opencv2/opencv.hpp>
#include <fstream>
#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 read_coco_yaml(DCSP_CORE *&p) {// Open the YAML filestd::ifstream file("coco.yaml");if (!file.is_open()) {std::cerr << "Failed to open file" << std::endl;return 1;}// Read the file line by linestd::string line;std::vector<std::string> lines;while (std::getline(file, line)) {lines.push_back(line);}// Find the start and end of the names sectionstd::size_t start = 0;std::size_t end = 0;for (std::size_t i = 0; i < lines.size(); i++) {if (lines[i].find("names:") != std::string::npos) {start = i + 1;} else if (start > 0 && lines[i].find(':') == std::string::npos) {end = i;break;}}// Extract the namesstd::vector<std::string> names;for (std::size_t i = start; i < end; i++) {std::stringstream ss(lines[i]);std::string name;std::getline(ss, name, ':'); // Extract the number before the delimiterstd::getline(ss, name); // Extract the string after the delimiternames.push_back(name);}p->classes = names;return 0;
}double calIou(const cv::Rect& rc1, const cv::Rect& rc2)
{cv::Rect intersection = rc1 & rc2;if (!intersection.empty()) {double intersectionArea = intersection.width * intersection.height;double rect1Area = rc1.width * rc1.height;double rect2Area = rc2.width * rc2.height;// 计算IOUdouble iou = intersectionArea / (rect1Area + rect2Area - intersectionArea);return iou;} else {// 没有重叠,IOU为0return 0.0;}
}int main(int argc, char* argv[])
{// 读取原始图像// Mat src = imread(argv[1]);DCSP_CORE *yoloDetector = new DCSP_CORE;//std::string model_path = "yolov8n.onnx";std::string model_path = argv[1];read_coco_yaml(yoloDetector);
#ifdef USE_CUDA// GPU FP32 inferenceDCSP_INIT_PARAM params{ model_path, YOLO_ORIGIN_V8, {640, 640}, 0.1, 0.5, true };// GPU FP16 inference// DCSP_INIT_PARAM params{ model_path, YOLO_ORIGIN_V8_HALF, {640, 640}, 0.1, 0.5, true };
#else// CPU inferenceDCSP_INIT_PARAM params{model_path, YOLO_ORIGIN_V8, {640, 640}, 0.1, 0.5, false};
#endifyoloDetector->CreateSession(params);cv::VideoCapture vc;vc.open(argv[2]);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()){std::vector<DCSP_RESULT> res;yoloDetector->RunSession(frame, res);for (int i = 0; i < res.size(); ++i){DCSP_RESULT detection = res[i];cv::Rect box = detection.box;cv::RNG rng(cv::getTickCount());cv::Scalar color(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));;// Detection boxcv::rectangle(frame, box, color, 2);// Detection box textstd::string classString = yoloDetector->classes[detection.classId] + ' ' + std::to_string(detection.confidence).substr(0, 4);cv::Size textSize = cv::getTextSize(classString, cv::FONT_HERSHEY_DUPLEX, 1, 2, 0);cv::Rect textBox(box.x, box.y - 40, textSize.width + 10, textSize.height + 20);cv::rectangle(frame, textBox, color, cv::FILLED);cv::putText(frame, classString, cv::Point(box.x + 5, box.y - 10), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 0), 2, 0);double iou = calIou(g_rect, box);if(iou > 0)std::cout << "iou:" << iou << std::endl;}cv::rectangle(frame, g_rect, Scalar(0, 255, 0), 3, cv::LINE_AA);cv::imshow("image", frame);cv::waitKey(1);vc >> frame;}}}return 0;
}
参考
yolov8
相关文章:
opencv+yolov8实现监控画面报警功能
项目背景 最近停在门前的车被人开走了,虽然有监控,但是看监控太麻烦了,于是想着框选一个区域用yolov8直接检测闯入到这个区域的所有目标,这样1ms一帧,很快就可以跑完一天的视频 用到的技术 COpenCVYolov8 OnnxRunt…...
基于深度学习的单图像人群计数研究:网络设计、损失函数和监控信号
摘要 https://arxiv.org/pdf/2012.15685v2.pdf 单图像人群计数是一个具有挑战性的计算机视觉问题,在公共安全、城市规划、交通管理等领域有着广泛的应用。近年来,随着深度学习技术的发展,人群计数引起了广泛的关注并取得了巨大的成功。通过系统地回顾和总结2015年以来基于深…...
C++递归实现验证⼆叉搜索树
C递归实现验证⼆叉搜索树 文章目录 C递归实现验证⼆叉搜索树题目链接题目描述解题思路C算法代码: 题目链接 98. 验证二叉搜索树 - 力扣(LeetCode) 题目描述 给你⼀个⼆叉树的根节点root,判断其是否是⼀个有效的⼆叉搜索树。 有效⼆…...
♥ uniapp 环境搭建
♥ uniapp 环境搭建 开发uniapp需要用到的工具有两个: 1、用到的平台和地址: 需要了解的几个平台以及地址: (1)微信公众平台 https://mp.weixin.qq.com/ (2)微信开发文档 https://develo…...
京东商品链接获取京东商品评论数据(用 Python实现京东商品评论信息抓取),京东商品评论API接口,京东API接口
在网页抓取方面,可以使用 Python、Java 等编程语言编写程序,通过模拟 HTTP 请求,获取京东多网站上的商品详情页面评论内容。在数据提取方面,可以使用正则表达式、XPath 等方式从 HTML 代码中提取出有用的信息。值得注意的是&#…...
docker容器中安装ROS1/ROS2(不用配任何环境,10分钟搞定)
默认电脑已经安装了docker,没安装看这篇文章Docker 安装 (完整详细版) ROS和docker各种结合看官方文档 dockerTutorials 在OSRF中拉取想要的 ROS 版本 docker 镜像 网址为 拉取命令在这里 我是安装noetic版本,因为这个兼容比较多现有的工程 docker pul…...
如何解决ssh登录报错WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
原因: 当两个设备第一次进行链接时,会在~/.ssh/konwn_hosts 中将被连接设备的公钥信息进行保存,后续再次链接时OpenSSH会核对公钥来进行一个简单的验证 然而有时候被链接的那台设备系统被重装、IP 冲突等原因,会导致公钥信息没…...
Mysql5.7安装配置详细图文教程(msi版本)
博主介绍:✌全网粉丝5W,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验…...
运行dl4j-examples的主要一些依赖
直接从git获取dl4j-examples后本地无法用IJ直接运行样例,于是自己新建了一个springboot项目,主要使用了下面的一些依赖用来运行官方样例 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache…...
PSRAM伪静态RAM芯片APS6404L
PSRAM伪静态RAM能结合SRAM和DRAM的优点,即容量大,又接口驱动简单,PSRAM接口和SRAM一样简单,驱动简单;而存储形式则和DRAM一样,容量远大于SRAM,介于SRAM和DRAM之间。 PSRAM厂家也有很多,以AP用的最多。最常…...
低级语言汇编真的各个面不如汇编吗?
今日话题,低级语言汇编真的各个面不如C语言吗?C语言因其可移植性、开发效率和可读性而在各领域广泛使用,市场占有率极高。然而,汇编语言在特定场景下仍然具有独特优势,稳固地占据一席之地。如果你对这方面感兴趣&#…...
PyG edge index 转换回 邻接矩阵
PyG的edge index形式是 [ ( n o d e 1 , n o d e 2 ) , ( n o d e 1 , n o d e 3 ) . . . ] [(node_1,node_2), (node_1, node_3)...] [(node1,node2),(node1,node3)...]这种edge pair。 naive 直接for循环,吧edge index里面的位置填充1: imp…...
JavaSE19——file文件类
file文件类 在 Java File 类是 java.io 包中唯一代表磁盘文件本身的对象 File 类不能访问文件内容本身,如果需要访问文件内容本身,则需要使用输入/输出流。 File(String path):如果 path 是实际存在的路径,则该 File 对象表示的…...
mongodb记录
MongoDB导入导出和备份的命令工具从4.4版本开始不再自动跟随数据库一起安装,而是需要自己手动安装。 mongodump 不是内部或外部命令,也不是可运行的程序 下载mongodb命令工具 下载zip格式,解压后把bin目录下的文件全部复制粘贴到你MongoDB安…...
Go语言:数组和切片
Python中的数组(这里指的是List类型)及其切片Slice基本相同,但在Go语言中这两者差别很大。 1 数组 Go语言中的数组(Array)存放的是长度固定、类型固定并且存储位置连续的一系列元素。 1.1 声明 Go语言中数组的声明方式如下: arr1 : [5]string{"…...
OPENCV 闭运算实验示例代码morphologyEx()函数
void CrelaxMyFriendDlg::OnBnClickedOk() {hdc this->GetDC()->GetSafeHdc();// TODO: 在此添加控件通知处理程序代码string imAddr "c:/Users/actorsun/Pictures/";string imAddr1 imAddr"rice.png";Mat relax, positive;relax imread(imAddr1…...
UE4 体积云制作 学习笔记
首先Noise本来就是一张噪点图 云的扰动不能太大,将Scale调小,并将InputMin调整为0 形成这样一张扰动图 扰动需要根据材质在世界的位置进行调整,所以Position需要加上WorldPosition 材质在不同世界位置,噪点不同 除以一个数&#…...
visual studio编译QtAV
1.1 依赖环境 第一种方法: 下载编译好的ffmpeg-3.4.2-win64-dev和ffmpeg-3.4.2-win64-shared,解压得到 D:\qt-workspace\ffmpeg-3.4.2-win64-dev D:\qt-workspace\ffmpeg-3.4.2-win64-shared 第二种方法: QtAV官方有提供编译好的依赖库 QtAV-depends-windows-x86%2Bx64.7…...
喜报!CACTER邮件安全网关荣获2023鲲鹏应用创新大赛广东赛区三等奖
近期,2023鲲鹏应用创新大赛广东赛区暨广东省信息技术应用创新产业联盟创新大赛圆满落幕,Coremail凭借“基于鲲鹏CPU的邮件网关一体机解决方案”,荣获“金融行业方向”三等奖。 鲲鹏凌粤 展翅湾区 本届大赛广东区域赛以“鲲鹏凌粤 展翅湾…...
Spark On Hive原理和配置
目录 一、Spark On Hive原理 (1)为什么要让Spark On Hive? 二、MySQL安装配置(root用户) (1)安装MySQL (2)启动MySQL设置开机启动 (3)修改MySQL…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
OCR MLLM Evaluation
为什么需要评测体系?——背景与矛盾 能干的事: 看清楚发票、身份证上的字(准确率>90%),速度飞快(眨眼间完成)。干不了的事: 碰到复杂表格(合并单元…...
