OpenCV之YOLOv2-tiny目标检测

  • 💂 个人主页:风间琉璃
  • 🤟 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主
  • 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)订阅专栏

目录

前言

一、YOLOv2-tiny介绍

二、预处理

三、模型加载与推理

四、解析输出


前言

YOLO(You Only Look Once)是一种基于深度神经网络的目标对象识别和定位算法,其特点是运行速度快、实时性高。这里我们将使用Tiny YOLOv2版本的YOLO算法。

YOLO算法创造性地将R-CNN目标检测中的选择候选区和识别候选区对象两个阶段合二为一,这也是YOLO名字的来由(只需看一眼就知道图片的哪些位置有什么对象)。

一、YOLOv2-tiny介绍

YOLOv2-tiny,轻量版的YOLOv2,即使用Tiny YOLOv2来实现目标检测。Tiny YOLOv2包含9个卷积层和6个最大池化层,如图所示。

Tiny YOLOv2目标检测算法具有预处理、网络推导和后处理三个步骤:

(1)预处理:对输入的任意分辨率的RGB图像,将各通道像素点的像素值归一化到[0, 1]区间,并按原图的长宽比例,将图像的尺寸缩放至416×416(以0.5填充);

(2)网络推导:将归一化后的416×416×3图像输入到Tiny YOLOv2网络进行前向推导,得到
13×13×5×25的输出张量;

(3)后处理:根据输出张量的格式,得到每个边框的中心点坐标以及长和宽,并根据各边框的覆盖度和置信度等信息,对所有13×13×5个边框进行NMS处理,得到最可能包含目标对象的候选框。最后根据1)中的缩放比率,将得到的候选边框放大并在原图中显示,即可得到目标对象的位置和类别信息。

yolov2-tiny网络模型每个Cell需要检测5个BOX,对每个BOX来说,包含如下数据:

  • 4个位置信息x、y、w、h

  • 1个置信分数

  • 基于VOC数据集的20个目标类别

  所以对每个BOX来说,每个BOX有5+20=25个参数,5个BOX共有 5x25=125个参数。所以,tiny-YOLO网络模型最后一层卷积层深度是125。 

yolov2参考:目标检测之YOLOv1-v3_风间琉璃•的博客-CSDN博客

资源下载:

yolov2-tiny-voc.weights:https://pjreddie.com/media/files/yolov2-tiny-voc.weights

yolov2-tiny-voc.cfg:https://github.com/pjreddie/darknet/blob/master/cfg/yolov2-voc.cfg

voc.names:https://github.com/pjreddie/darknet/blob/master/data/voc.names

更多版本下载:YOLO: Real-Time Object Detection

二、预处理

数据集采用的voc数据集,需要将voc.names包含训练模型的所有类名称加载到内存中。

String classespath = "F:/data/CQU/VS/yolov2-tiny/voc.names";//得到网络对应的标签
vector<string> getclasses(string classespath)
{ifstream ifs(classespath);//分类名vector<string> classes;if (ifs.is_open()){string line;while (getline(ifs, line)){classes.push_back(line);}}return classes;
}

神经网络的输入图像需要采用称为blob的特定格式。从输入图像或视频流中读取帧后,将通过blobFromImage函数将其转换为神经网络的输入blob。

在此过程中,它使用比例因子1/255将图像像素值缩放到0到1的目标范围。它还将图像的大小调整为给定大小(416,416)而不进行裁剪。

//图像预处理
Mat blob = blobFromImage(frame, 1 / 255.0, Size(416, 416), Scalar(), true, false);

三、模型加载与推理

加载网络直接使用readNetFromDarknet。

String config = "F:/data/CQU/VS/yolov2-tiny/yolov2-tiny-voc.cfg";
String weights = "F:/data/CQU/VS/yolov2-tiny/yolov2-tiny-voc.weights";//加载网络模型Net net = readNetFromDarknet(config, weights);if (net.empty()){printf("Could not load net...\n");return;}#if 1//cpu推理net.setPreferableBackend(DNN_BACKEND_OPENCV);net.setPreferableTarget(DNN_TARGET_CPU);#elif 0//使用cuda加速net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);#endif

这里可以根据个人情况是否使用CUDA加速。

预处理和网络模型都加载完成后可以进行图像的预测。

//设置输入
net.setInput(blob,"data");//推理预测
Mat detectionMat = net.forward("detection_out");

预测的结果保存在detectionMat的Mat类型矩阵中。接下来就需要对这个预测结果进行后处理。  

四、解析输出

yolov2的输出包含:4个位置信息x、y、w、h,1个置信分数以及基于VOC数据集的20个目标类别。注意这里的x,y是边框中心的坐标,所以要将目标边框绘制出来,需要根据这个四个参数推算出左顶点的坐标和高,宽。

for (int i = 0; i < detectionMat.rows;i++)
{Mat scores = detectionMat.row(i).colRange(5, detectionMat.cols);Point classIdPoint;double confidence;minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);int classid = classIdPoint.x;if (confidence > 0){printf("confide:%.2f\n", confidence);}if (confidence > confidenceThreshold){printf("confide2:%.2f\n", confidence);//x,y,w,h:中心坐标,边框w,hfloat x = detectionMat.at<float>(i, 0) * image.cols;float y = detectionMat.at<float>(i, 1) * image.rows;float width = detectionMat.at<float>(i, 2) * image.cols;float height = detectionMat.at<float>(i, 3) * image.rows;//左上角坐标int xLeftBottom = static_cast<int>((x - width / 2));int yLeftBottom = static_cast<int>((y - height / 2));//获取矩形框x,y,w,hRect object(xLeftBottom, yLeftBottom, static_cast<int>(width), static_cast<int>(height));//绘制矩形框rectangle(image, object, Scalar(0, 255, 0), 2);if (classid < classNames.size()){//获取类别名称及其置信度string conf = format("%.2f", confidence);String label = String(classNames[classid]) + ": " + conf;int baseLine = 0;//在图像上添加标签Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);rectangle(image, Rect(Point(xLeftBottom, yLeftBottom), Size(labelSize.width, labelSize.height + baseLine)), Scalar(255, 255, 255), FILLED);putText(image, label, Point(xLeftBottom, yLeftBottom + labelSize.height), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0));}}
}

运行结果:

图片:

视频:

OpenCV yolov2-tiny

源码:资源下载:https://download.csdn.net/download/qq_53144843/88354502

// yolov2-tiny.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>
#include <fstream>
#include <algorithm>
#include <cstdlib>#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>using namespace std;
using namespace cv;
using namespace cv::dnn;//置信度阈值
float confidenceThreshold = 0.25;//得到网络对应的标签
vector<string> getclasses(string classespath)
{ifstream ifs(classespath);//分类名vector<string> classes;if (ifs.is_open()){string line;while (getline(ifs, line)){classes.push_back(line);}}return classes;
}void detection(string config, string weights, string classespath, string video_path)
{//加载网络模型Net net = readNetFromDarknet(config, weights);if (net.empty()){printf("Could not load net...\n");return;}#if 0//cpu推理net.setPreferableBackend(DNN_BACKEND_OPENCV);net.setPreferableTarget(DNN_TARGET_CPU);#elif 1//使用cuda加速net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);#endif//获得分类名vector<string> classNames = getclasses(classespath);//打开视频流VideoCapture capture;capture.open(video_path); //传入0:读取摄像头if (!capture.isOpened()){printf("could not open the video...\n");return;}//读取视频流Mat frame;while (capture.read(frame)){if (frame.channels() == 4){cvtColor(frame, frame, COLOR_BGRA2BGR);}//图像预处理Mat blob = blobFromImage(frame, 1 / 255.0, Size(416, 416), Scalar(), true, false);//设置输入net.setInput(blob,"data");//获得当前系统的计时间周期数,求FPSdouble t = (double)getTickCount();//推理预测Mat detectionMat = net.forward("detection_out");for (int i = 0; i < detectionMat.rows; i++){//获取每一行从第5列起的分类类别的概率const int probability_index = 5;//概率的总列数const int probability_size = detectionMat.cols - probability_index;//用于查找最大概率的类别float* prob_array_ptr = &detectionMat.at<float>(i, probability_index);//查找概率数组中的最大值,并返回最大值的索引size_t objectClass = max_element(prob_array_ptr, prob_array_ptr + probability_size) - prob_array_ptr;//获取置信度值float confidence = detectionMat.at<float>(i, (int)objectClass + probability_index);if (confidence > confidenceThreshold){printf("confide:%.2f\n", confidence);float x = detectionMat.at<float>(i, 0);float y = detectionMat.at<float>(i, 1);float width = detectionMat.at<float>(i, 2);float height = detectionMat.at<float>(i, 3);int xLeftBottom = static_cast<int>((x - width / 2) * frame.cols);int yLeftBottom = static_cast<int>((y - height / 2) * frame.rows);int xRightTop = static_cast<int>((x + width / 2) * frame.cols);int yRightTop = static_cast<int>((y + height / 2) * frame.rows);Rect object(xLeftBottom, yLeftBottom,xRightTop - xLeftBottom,yRightTop - yLeftBottom);rectangle(frame, object, Scalar(0, 255, 0),2);if (objectClass < classNames.size()){//获取类别名称及其置信度string conf = format("%.2f", confidence);String label = String(classNames[objectClass]) + ": " + conf;int baseLine = 0;Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);rectangle(frame, Rect(Point(xLeftBottom, yLeftBottom),Size(labelSize.width, labelSize.height + baseLine)),Scalar(255, 255, 255), FILLED);putText(frame, label, Point(xLeftBottom, yLeftBottom + labelSize.height), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0));}}}//FPS计算t = ((double)getTickCount() - t) / getTickFrequency();//求输入帧后经过的周期数/每秒系统计的周期数=一帧用时多少秒double fps = 1.0 / t;//求倒数得到每秒经过多少帧,即帧率string text = format("FPS:%.2f", fps);cv::putText(frame, text, Point(10, 50), FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 0), 2, 8, 0);//FPS计算imshow("YOLOv2", frame);int c = cv::waitKey(1);if (c == 27){break;}}
}void image_detection(string config, string weights, string classespath, string image_path)
{//读取图片Mat image = imread(image_path);if (image.channels() == 4){cvtColor(image, image, COLOR_BGRA2BGR);}//预处理Mat blob = blobFromImage(image, 1 / 255.0, Size(416, 416), Scalar());//加载网络Net net = readNetFromDarknet(config, weights);if (net.empty()){printf("Could not load net...\n");return;}#if 0//cpu推理net.setPreferableBackend(DNN_BACKEND_OPENCV);net.setPreferableTarget(DNN_TARGET_CPU);#elif 1//使用cuda加速net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);#endif//获得分类名vector<string> classNames = getclasses(classespath);net.setInput(blob, "data");Mat detectionMat = net.forward("detection_out");//推理时间vector<double> layersTimings;double freq = getTickFrequency() / 1000;double time = net.getPerfProfile(layersTimings) / freq;ostringstream ss;ss << "FPS: " << 1000 / time << " ; time: " << time << " ms";putText(image, ss.str(), Point(10, 30), 0, 0.5, Scalar(0, 0, 255));for (int i = 0; i < detectionMat.rows;i++){Mat scores = detectionMat.row(i).colRange(5, detectionMat.cols);Point classIdPoint;double confidence;minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);int classid = classIdPoint.x;if (confidence > 0){printf("confide:%.2f\n", confidence);}if (confidence > confidenceThreshold){printf("confide2:%.2f\n", confidence);//x,y,w,h:中心坐标,边框w,hfloat x = detectionMat.at<float>(i, 0) * image.cols;float y = detectionMat.at<float>(i, 1) * image.rows;float width = detectionMat.at<float>(i, 2) * image.cols;float height = detectionMat.at<float>(i, 3) * image.rows;//左上角坐标int xLeftBottom = static_cast<int>((x - width / 2));int yLeftBottom = static_cast<int>((y - height / 2));//获取矩形框x,y,w,hRect object(xLeftBottom, yLeftBottom, static_cast<int>(width), static_cast<int>(height));//绘制矩形框rectangle(image, object, Scalar(0, 255, 0), 2);if (classid < classNames.size()){//获取类别名称及其置信度string conf = format("%.2f", confidence);String label = String(classNames[classid]) + ": " + conf;int baseLine = 0;//在图像上添加标签Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);rectangle(image, Rect(Point(xLeftBottom, yLeftBottom), Size(labelSize.width, labelSize.height + baseLine)), Scalar(255, 255, 255), FILLED);putText(image, label, Point(xLeftBottom, yLeftBottom + labelSize.height), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0));}}}imshow("YOLOv2", image);cv::waitKey(0);
}int main()
{String config = "F:/data/CQU/VS/yolov2-tiny/yolov2-tiny-voc.cfg";String weights = "F:/data/CQU/VS/yolov2-tiny/yolov2-tiny-voc.weights";String classespath = "F:/data/CQU/VS/yolov2-tiny/voc.names";String video_path = "F:/data/CQU/VS/yolov2-tiny/1.mp4";String image_path = "F:/data/CQU/VS/yolov2-tiny/dog_cat.jpg";//image_detection(config, weights, classespath, image_path);detection(config, weights, classespath, video_path);}

结束语
感谢你观看我的文章呐~本次航班到这里就结束啦 🛬

希望本篇文章有对你带来帮助 🎉,有学习到一点知识~

躲起来的星星🍥也在努力发光,你也要努力加油(让我们一起努力叭)。

最后,博主要一下你们的三连呀(点赞、评论、收藏),不要钱的还是可以搞一搞的嘛~

不知道评论啥的,即使扣个666也是对博主的鼓舞吖 💞 感谢 💐

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/111986.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ros----发布者和订阅者模型

话题模型&#xff1a; 如何自定义话题消息 1.定义msg文件 2.在package.xml中添加功能包依赖 <build_depend>message_generation</build_depend> <exec_depend>message_runtime</exec_depend>3.在CMakeList.txt文件中添加编译选项 4.编译生成语言的相…

Android 系统中适配OAID获取

一、OAID概念 OAID&#xff08;Open Anonymous Identification&#xff09;是一种匿名身份识别标识符&#xff0c; 用于在移动设备上进行广告追踪和个性化广告投放。它是由中国移动通信集 团、中国电信集团和中国联通集团共同推出的一项行业标准 OAID值为一个64位的数字 二、…

Pytorch Advanced(二) Variational Auto-Encoder

自编码说白了就是一个特征提取器&#xff0c;也可以看作是一个降维器。下面找了一张很丑的图来说明自编码的过程。 自编码分为压缩和解码两个过程。从图中可以看出来&#xff0c;压缩过程就是将一组数据特征进行提取&#xff0c; 得到更深层次的特征。解码的过程就是利用之前的…

详细介绍下路由器中的WAN口

路由器的 WAN 口&#xff08;Wide Area Network port&#xff09;是指用于连接广域网&#xff08;WAN&#xff09;的接口。它是路由器与外部网络&#xff08;如互联网&#xff09;之间的物理连接点&#xff0c;允许路由器与互联网服务提供商&#xff08;ISP&#xff09;或其他广…

风车时间锁管理 - 构建IPA文件加锁+签名+管理一站式解决方案

时间锁管理&#xff1a;是一种用于控制对某些资源、功能或操作的访问权限的机制&#xff0c;它通过设定时间限制来限制对特定内容、系统或功能的访问或执行&#xff0c;以提高安全性和控制性&#xff0c;时间锁管理常见于以下场景&#xff1a; 1. 文件或文档的保密性&#xff…

浏览器代理解决方案

当谈到网络浏览器&#xff0c; 浏览器 无疑是最受欢迎和广泛使用的选项之一。然而&#xff0c;你可能已经注意到&#xff0c; 浏览器并不原生支持 SOCKS5 代理协议。不过&#xff0c;别担心&#xff01;在本文中&#xff0c;我将与你分享一些解决方案&#xff0c;让你能够在 浏…

【C++初阶】动态内存管理

​&#x1f47b;内容专栏&#xff1a; C/C编程 &#x1f428;本文概括&#xff1a; C/C内存分布、C语言动态内存管理、C动态内存管理、operator new与operator delete函数、new和delete的实现原理、定位new表达式、常见面试问题等。 &#x1f43c;本文作者&#xff1a; 阿四啊 …

Django系列:Django的项目结构与配置解析

Django系列 Django的项目结构与配置解析 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/132893616 【介…

简单介绍神经网络中不同优化器的数学原理及使用特性【含规律总结】

当涉及到优化器时&#xff0c;我们通常是在解决一个参数优化问题&#xff0c;也就是寻找能够使损失函数最小化的一组参数。当我们在无脑用adam时&#xff0c;有没有斟酌过用这个是否合适&#xff0c;或者说凭经验能够有目的性换用不同的优化器&#xff1f;是否用其他的优化器可…

【分布式】分布式事务:2PC

分布式事务的问题可以分为两部分&#xff1a; 并发控制 concurrency control原子提交 atomic commit 分布式事务问题的产生场景&#xff1a;一份数据被分片存在多台服务器上&#xff0c;那么每次事务处理都涉及到了多台机器。 可序列化&#xff08;并发控制&#xff09;&…

SQL优化--分页优化(limit)

在数据量比较大时&#xff0c;如果进行limit分页查询&#xff0c;在查询时&#xff0c;越往后&#xff0c;分页查询效率越低。 通过测试我们会看到&#xff0c;越往后&#xff0c;分页查询效率越低&#xff0c;这就是分页查询的问题所在。 因为&#xff0c;当在进行分页查询时&…

帆软BI开发-Day2-趋势图的多种变形

前言&#xff1a; 在BI数据展示中&#xff0c;条形图、趋势图无疑是使用场景非常多的两种图形。与条形图不同的是&#xff0c;趋势图更能反馈出一定的客观规律和未来的趋势走向&#xff0c;因此用于作为预警和判异的业务场景&#xff0c;但实际业务场景的趋势图可没你想的那么简…