基于OpenCV-DNN的YOLOv9目标检测实现

news2024/11/17 0:05:49

⚠申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址。 全文共计3077字,阅读大概需要3分钟
🌈更多学习内容, 欢迎👏关注👀【文末】我的个人微信公众号:不懂开发的程序猿
个人网站:https://jerry-jy.co/

❗❗❗知识付费,🈲止白嫖,有需要请后台私信或【文末】个人微信公众号联系我

基于OpenCV-DNN的YOLOv9目标检测实现

  • 基于OpenCV-DNN的YOLOv9目标检测实现
    • 任务需求
    • 任务目标
      • 1、掌握OpenCV的dnn模块加载YOLOv9网络模型
      • 2、掌握基于OpenCV进行神经网络的预测
      • 3、掌握设置阈值过滤预测的检测框
    • 任务环境
      • 1、jupyter开发环境
      • 2、OpenCv
      • 3、python3.6
    • 任务实施过程
      • 一、YOLOv9目标检测
        • 1.导入所需要的工具包
        • 2.加载YOLOv9网络模型
        • 3.加载分类信息
        • 4.加载测试图像
        • 5.设置阈值过滤检测结果
        • 6.检测结果可视化
        • 7.YOLOv9目标检测
      • 二、任务小结
  • 说明

基于OpenCV-DNN的YOLOv9目标检测实现

任务需求

YOLO算法将物体检测作为回归问题求解。基于一个单独的end-to-end网络,完成从原始图像的输入到物体位置和类别的输出。
YOLOv9主要的特点是它可以在三种不同的尺度上进行检测,利用多尺度特征进行目标检测。
本次实验使用OpenCV的DNN模块,使用根据COCO数据集训练好的YOLOv9模型的权重,对test.jpg图片进行目标检测。

在这里插入图片描述

任务目标

1、掌握OpenCV的dnn模块加载YOLOv9网络模型

2、掌握基于OpenCV进行神经网络的预测

3、掌握设置阈值过滤预测的检测框

任务环境

1、jupyter开发环境

2、OpenCv

3、python3.6

任务实施过程

一、YOLOv9目标检测

1.导入所需要的工具包
import tensorflow as tf # 导入tensorflow
from matplotlib import pyplot as plt # 导入绘图模块
import numpy as np # 导入numpy
from utils import im_show # 导入显示图像函数
import cv2 # 导入opencv
# 绘制图像直接展示,不用调用plt.show()
%matplotlib inline 
# 用来正常显示中文标签
plt.rc('font',family="SimHei")
import time
2.加载YOLOv9网络模型

在OpenCV3.4.1版本之后版本,cv2.dnn模块支持调用Caffe、TensorFlow、Torch、PyTorch、DarkNet等深度学习框架。

这里使用的是基于COCO数据集训练好的YOLOv9模型,yolov9.weights,yolov9.cfg和coco.names文件保存在/experiment/data/文件夹下。

  • yolov9.weights:基于COCO数据集训练好的YOLOv9模型的权重文件
  • yolov9.cfg:模型配置文件
  • coco.names:COCO数据集类别标签文件(80种标签)

cv2.dnn.readNetFromDarknet(model, config=None) 加载YOLOv9网络模型,Darknet框架是YOLO论文中使用的框架

  • model: 模型权重文件
  • config: 模型配置文件

OpenCV的网络类中的前向功能需要结束层,直到它在网络中运行。因为我们需要运行整个网络,所以我们需要识别网络中的最后一层。我们通过使用getUnconnectedOutLayers()获得未连接的输出层的名字,该层基本就是网络的最后层。然后我们运行前向网络,得到输出。

  • net.getLayerName():获取每一层的名称,返回一个列表,如:[conv_0, bn_0, relu_0, conv_1,…, permut_106, yolo_106]
  • net.getUnconnectedOutLayers():以列表的形式返回具有未连接输出的图层索引
# 设置文件的文件路径
weightsPath = './experiment/data/yolov9.weights'
configPath = './experiment/data/yolov9.cfg'
labelsPath = './experiment/data/coco.names'
# 加载YOLOv9网络、配置权重
net = cv2.dnn.readNetFromDarknet(configPath, weightsPath) 
# getLayerNames() 是获取网络各层名称
layer_names = net.getLayerNames()
print('网络一共有{}层'.format(len(layer_names)))
# getUnconnectedOutLayers() 是返回具有未连接输出的图层索引
#获取索引后,对网络各层名称使用索引获取网络最后层的名称
output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
print('网络输出层名称',output_layers)
# yolov9在每个scale都有输出,output_layers是每个scale的输出层名称,供net.forward()使用

在这里插入图片描述

3.加载分类信息

coco.names文件保存COCO数据集类别标签,从 coco.names 导入类别并存储为列表

# 创建一个空列表,用来保存类别标签
classes = []
# 以只读方式打开coco.names文件
# file.readlines()返回文件中行内容的列表
# line.strip()删除字符串首尾的空格
with open(labelsPath, "r") as f:
    # 获取文件中每一行的标签
    classes = [line.strip() for line in f.readlines()]
print('一共有{}种标签'.format(len(classes)))

在这里插入图片描述

4.加载测试图像

blobFromImage(image, scalefactor=1.0, ize = Size(),swapRB = false, crop = false,ddepth = CV_32F)函数对图像进行预处理,返回一个4通道的blob(blob可以简单理解为一个N维的数组,用于神经网络的输入)

  • image:输入图像
  • scalefactor: 像素值尺度缩放比例
  • size:指的不是输入图像的尺寸,是神经网络在训练的时候要求输入的图片尺寸。
  • swapRB:是否交换R和B分量,OpenCV中的图片通道顺序是BGR
  • crop:输入图像大小与size不符的时候,是否需要裁剪

setInput()函数中输入blobFromImage()函数的返回值

# 读取想要进行目标检测的图片
img = cv2.imread("./experiment/data/test.jpg")
# 获取图片的高和宽
H, W, _ = img.shape
# 先创建blobdnn.blobFromImage()放入输入图片,设置神经网络图片输入为416*416,交换R和B分量
blob = cv2.dnn.blobFromImage(img,1/255.0,
                                   size=(416, 416),
                                   swapRB=True,
                                   crop=False)
# 调用setInput函数将图片送入输入层
net.setInput(blob)
# net.forward()向前预测,得到检测结果out(有三个输出层)
layerOutputs = net.forward(output_layers)
# 得到的layerOutputs是包含三组数据的列表
print(layerOutputs[0].shape,layerOutputs[1].shape,layerOutputs[2].shape)

在这里插入图片描述

YOLOv9网络的输出为矩形框,每个矩形框由一个向量表示,所有矩形框组成一个向量组。每个向量的长度为类别数+5个参数,这五个参数的前四个分别是矩形框在图像上的位置center_x, center_y, width, height(均为比例,范围在0-1之间),第五个参数是该矩形框包含一个物体的置信度(可信程度)。

从上面模型输出可以看到,三个输出层,分别检测到507、2028和8112个矩形框,每个矩形框有85个参数,其中包括位置参数4个,背景置信度参数1个,类别置信度参数80个(数据集一共有80个种类)

5.设置阈值过滤检测结果
#设置置信度阈值和非极大值抑制阈值
confidence_thre = 0.5
nms_thre = 0.4
# 初始化边界框,置信度(概率)以及类别
boxes = []  # 所有边界框(各层结果放一起)
confidences = [] # 所有置信度
classIDs = [] # 所有分类ID
# 1)过滤掉置信度低的矩形框
# 迭代每个输出层,总共三个
for out in layerOutputs: 
    # 迭代每个输出层的检测结果
    for detection in out:  
        # 获取类别概率(结果从第6个开始到最后一个值)
        scores = detection[5:]  
        # 获取类别置信度最高的索引为classID 
        classID = np.argmax(scores) 
        # 根据索引classID得到最高的类别置信度confidence
        confidence = scores[classID]  # 拿到置信度

        # 根据置信度筛选
        if confidence > confidence_thre:
            # 根据边界框比例缩放回照片尺寸
            box = detection[0:4] * np.array([W, H, W, H])  
            # 将结果转换成int整型
            (centerX, centerY, width, height) = box.astype("int")
            # 得到矩形框的中心点坐标和宽度高度
             # 计算边界框的左上角位置
            x = int(centerX - (width / 2))
            y = int(centerY - (height / 2))
            # 更新边界框,类别置信度(概率)以及分类类别
            boxes.append([x, y, int(width), int(height)])
            confidences.append(float(confidence))
            classIDs.append(classID)
# 2)使用非极大值抑制方法抑制弱、重叠边界框
# 使用NMSBoxes()函数,放入预测框的尺寸,放入分类置信度得分
idxs = cv2.dnn.NMSBoxes(boxes, confidences, confidence_thre, nms_thre)
# 返回的是输入的boxes的符合要求的下标,维度为[2, 1],有两个矩形框保存了下来
print('有{}个预测的矩形框通过筛选保留了下来'.format(idxs.shape[0]))

在这里插入图片描述

6.检测结果可视化
# 设置随机种子,为了保证每次运行结果相同
np.random.seed(42)
# 框框显示颜色,每一类有不同的颜色,每种颜色都是由RGB三个值组成的,所以size为(len(labels), 3)
COLORS = np.random.randint(0, 255, size=(len(classes), 3), dtype="uint8")  
if len(idxs) > 0:
    # indxs是二维的,第0维是输出层,所以这里把它展平成1维
    for i in idxs.flatten(): 
        # 根据索引获取矩形框左上角坐标和宽高
        (x, y) = (boxes[i][0], boxes[i][1])
        (w, h) = (boxes[i][2], boxes[i][3])
        # 设置绘制的检测框颜色
        color = [int(c) for c in COLORS[classIDs[i]]]
        # 根据检测框的起始x,y坐标和终点x+w,y+h坐标在原图中绘制一个正方形的框
        cv2.rectangle(img, (x, y), (x+w, y+h), color, 2)  
        # 设置文字为检测框的类别和分类置信度
        text = "{}: {:.4f}".format(classes[classIDs[i]], confidences[i])
        # putText()为图像添加文字,放入图像,文字内容,坐标,字体风格,大小,颜色,字体厚度
        cv2.putText(img, text, (x, y+15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) 
# 绘制添加检测框和标签置信度后的图像
plt.figure(figsize = (12,12))
im_show('目标检测结果', img)

在这里插入图片描述

7.YOLOv9目标检测
# 将上述求解结果封装成yolo_detect()函数
# 创建一个函数detection放入之前的所有步骤
def YOLO_detect(pathIn="",
                label_path='./experiment/data/coco.names',
                config_path='./experiment/data/yolov9.cfg',
                weights_path='./experiment/data/yolov9.weights',
                confidence_thre=0.5,
                nms_thre=0.3):
    '''
    pathIn:原始图片的路径
    label_path:类别标签文件的路径
    config_path:模型配置文件的路径
    weights_path:模型权重文件的路径
    confidence_thre:0-1,置信度(概率/打分)阈值,即保留概率大于这个值的边界框,默认为0.5
    nms_thre:非极大值抑制的阈值,默认为0.3
    '''
    print('加载YOLO模型......')
    # 加载YOLOv9网络、配置权重
    net = cv2.dnn.readNetFromDarknet(config_path, weights_path) 
    # getLayerNames() 是获取网络各层名称
    layer_names = net.getLayerNames()
    #获取索引后,对网络各层名称使用索引获取网络最后层的名称
    output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
    # yolov9在每个scale都有输出,output_layers是每个scale的输出层名称,供net.forward()使用
    
    # 创建一个空列表,用来保存类别标签
    classes = []
    with open(label_path, "r") as f:
        # 获取文件中每一行的标签
        classes = [line.strip() for line in f.readlines()]

    # 读取想要进行目标检测的图片
    img = cv2.imread(pathIn)
    # 获取图片的高和宽
    H, W, _ = img.shape
    # 先创建blobdnn.blobFromImage()放入输入图片,设置神经网络图片输入为416*416,交换R和B分量
    blob = cv2.dnn.blobFromImage(img,1/255.0,
                                   size=(416, 416),
                                   swapRB=True,
                                   crop=False)
    # 调用setInput函数将图片送入输入层
    net.setInput(blob)
    start = time.time()
    # net.forward()向前预测,得到检测结果out(有三个输出层)
    layerOutputs = net.forward(output_layers)
    # 得到的layerOutputs是包含三组数据的列表
    end = time.time()
    # 显示预测所花费时间
    print('YOLO模型花费 {:.2f} 秒来预测一张图片'.format(end - start))

    # 初始化边界框,置信度(概率)以及类别
    boxes = []  # 所有边界框(各层结果放一起)
    confidences = [] # 所有置信度
    classIDs = [] # 所有分类ID
    # 1)过滤掉置信度低的矩形框
    # 迭代每个输出层,总共三个
    for out in layerOutputs: 
        # 迭代每个输出层的检测结果
        for detection in out:  
            # 获取类别概率(结果从第6个开始到最后一个值)
            scores = detection[5:]  
            # 获取类别置信度最高的索引为classID 
            classID = np.argmax(scores) 
            # 根据索引classID得到最高的类别置信度confidence
            confidence = scores[classID]  # 拿到置信度

            # 根据置信度筛选
            if confidence > confidence_thre:
                # 根据边界框比例缩放回照片尺寸
                box = detection[0:4] * np.array([W, H, W, H])  
                # 将结果转换成int整型
                (centerX, centerY, width, height) = box.astype("int")
                # 得到矩形框的中心点坐标和宽度高度
                # 计算边界框的左上角位置
                x = int(centerX - (width / 2))
                y = int(centerY - (height / 2))
                # 更新边界框,类别置信度(概率)以及分类类别
                boxes.append([x, y, int(width), int(height)])
                confidences.append(float(confidence))
                classIDs.append(classID)
    # 2)使用非极大值抑制方法抑制弱、重叠边界框
    # 使用NMSBoxes()函数,放入预测框的尺寸,放入分类置信度得分
    idxs = cv2.dnn.NMSBoxes(boxes, confidences, confidence_thre, nms_thre)
    # 返回的是输入的boxes的符合要求的下标

    # 设置随机种子,为了保证每次运行结果相同
    np.random.seed(42)
    # 框框显示颜色,每一类有不同的颜色,每种颜色都是由RGB三个值组成的,所以size为(len(labels), 3)
    COLORS = np.random.randint(0, 255, size=(len(classes), 3), dtype="uint8")  
    if len(idxs) > 0:
        # indxs是二维的,第0维是输出层,所以这里把它展平成1维
        for i in idxs.flatten(): 
            # 根据索引获取矩形框左上角坐标和宽高
            (x, y) = (boxes[i][0], boxes[i][1])
            (w, h) = (boxes[i][2], boxes[i][3])
            # 设置绘制的检测框颜色
            color = [int(c) for c in COLORS[classIDs[i]]]
            # 根据检测框的起始x,y坐标和终点x+w,y+h坐标在原图中绘制一个正方形的框
            cv2.rectangle(img, (x, y), (x+w, y+h), color, 2)  
            # 设置文字为检测框的类别和分类置信度
            text = "{}: {:.4f}".format(classes[classIDs[i]], confidences[i])
            # putText()为图像添加文字,放入图像,文字内容,坐标,字体风格,大小,颜色,字体厚度
            cv2.putText(img, text, (x, y+15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) 
    # 绘制添加检测框和标签置信度后的图像
    plt.figure(figsize = (12,12))
    im_show('目标检测结果', img)
# 放入图像和设置阈值
YOLO_detect(pathIn="./experiment/data/test1.jpg")

在这里插入图片描述

二、任务小结

本次实验主要完成基于OpenCv的YOLOv9目标检测,使用OpenCV的DNN模块,使用根据COCO数据集训练好的模型的权重,对图像进行目标检测。

通过本次实验需要掌握以下内容:

  • 掌握OpenCV的dnn模块加载YOLOv9网络模型
  • 掌握基于OpenCV进行神经网络的预测
  • 掌握设置阈值过滤预测的检测框

–end–

说明

本实验(项目)/论文若有需要,请后台私信或【文末】个人微信公众号联系我

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣打卡第二天

206. 反转链表 class Solution { public:ListNode* reverseList(ListNode* head) {// //迭代法// ListNode *pre nullptr;// ListNode *curr head;// while(curr){// ListNode *next curr -> next;// curr -> next pre;// pre curr;// curr next;/…

【实时数仓架构】方法论

笔者不是专业的实时数仓架构,这是笔者从其他人经验和网上资料整理而来,仅供参考。写此文章意义,加深对实时数仓理解。 一、实时数仓架构技术演进 1.1 四种架构演进 1)离线大数据架构 一种批处理离线数据分析架构,…

SFOS1:开发环境搭建

一、简介 最近在学习sailfish os的应用开发,主要内容是QmlPython。所以,在开发之前需要对开发环境(virtualBox官方SDKcmake编译器python)进行搭建。值得注意的是,我的开发环境是ubuntu22.04。如果是windows可能大同小异…

ZooKeeper以及DolphinScheduler的用法

目录 一、ZooKeeper的介绍 数据模型 ​编辑 操作使用 ①登录客户端 ​编辑 ②可以查看下面节点有哪些 ③创建新的节点,并指定数据 ④查看节点内的数据 ⑤、删除节点及数据 特殊点: 运行机制: 二、DolphinScheduler的介绍 架构&#…

回溯法——(1)装载问题(C语言讲解)

目录 一、装载问题 1.问题概括: 2.解决方案(思路): 3.图片讲解(超详细): 4.代码分析: 二、算法改进:引入上界函数 1.问题概念: 2.图片讲解&#xff1a…

Room简单实操

1. Room介绍,直接Copy官网介绍: Room 持久性库在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库。具体来说,Room 具有以下优势: 提供针对 SQL 查询的编译时验…

深入理解分布式事务⑧ ---->MySQL 事务的实现原理 之 MySQL 事务流程(MySQL 事务执行流程 和 恢复流程)详解

目录 MySQL 事务的实现原理 之 MySQL 事务流程(MySQL 事务执行流程 和 恢复流程)详解MySQL 事务流程1、MySQL 事务执行流程1-1:MySQL 事务执行流程如图: 2、MySQL 事务恢复流程2-1:事务恢复流程如下图: MyS…

基于点灯Blinker的ESP8266远程网络遥控LED

本文介绍基于ESP8266模块实现的远程点灯操作,手机侧APP选用的是点灯-Blinker,完整资料及软件见文末链接 一、ESP8266模块简介 ESP8266是智能家居等物联网场景下常用的数传模块,具有强大的功能,通过串口转WIFI的方式可实现远距离…

区块链扩容:水平扩展 vs.垂直扩展

1. 引言 随着Rollups 的兴起,区块链扩容一直集中在模块化(modular)vs. 整体式(monolithic)之争。 如今,模块化与整体式这种一分为二的心理模型,已不适合于当前的扩容场景。本文,将展…

【C语言回顾】字符函数、字符串函数,内存函数

前言1. 字符函数1.1 字符分类函数1.2 字符转换函数1.2.1 tolower(将大写字母转化为小写字母)1.2.2 toupper(将小写字母转化为大写字母) 2. 字符串函数2.1 求字符串长度函数 strlen2.2 字符串输入函数 gets()&fgets()2.2.1 get…

虚拟机网络实现桥接模式

虚拟机网络实现桥接模式 虚拟化软件:VMware 17 Linux:rocky8_9 主机:Win10 文章目录 虚拟机网络实现桥接模式1. 桥接模式介绍2. 查看Win本机的网络信息(以笔记本电脑以WiFi联网为例&#x…

vue快速入门(五十五)插槽基本用法

注释很详细&#xff0c;直接上代码 上一篇 新增内容 当传输内容只有一种时的基础写法 源码 App.vue <template><div id"app"><h1>被淡化的背景内容</h1><my-dialog><!-- 插槽内容:文字以及dom结构都可以传 --><span>你确…

【LLM 论文】背诵增强 LLM:Recitation-Augmented LM

论文&#xff1a;Recitation-Augmented Language Models ⭐⭐⭐ ICLR 2023, Google Research, arXiv:2210.01296 Code&#xff1a;github.com/Edward-Sun/RECITE 文章目录 论文速读 论文速读 论文的整体思路还是挺简单的&#xff0c;就是让 LLM 面对一个 question&#xff0c;…

蓝桥杯-路径之谜

题目描述 小明冒充X星球的骑士&#xff0c;进入了一个奇怪的城堡。城堡里面什么都没有&#xff0c;只有方形石头铺成的地面。 假设城堡的地面时n*n个方格。如下图所示。 按习俗&#xff0c;骑士要从西北角走到东南角。可以横向或者纵向移动&#xff0c;但是不能斜着走&#x…

详解SDRAM基本原理以及FPGA实现读写控制(一)

文章目录 一、SDRAM简介二、SDRAM存取结构以及原理2.1 BANK以及存储单元结构2.2 功能框图2.3 SDRAM速度等级以及容量计算 三、SDRAM操作命令3.1 禁止命令&#xff1a; 4b1xxx3.2 空操作命令&#xff1a;4b01113.3 激活命令&#xff1a;4b00113.4 读命令&#xff1a;4b01013.5 写…

使用docker-compose编排Lnmp(dockerfile) 完成Wordpress

目录 一、 Docker-Compose 1.1Docker-Compose介绍 1.2环境准备 1.2.1准备容器目录及相关文件 1.2.2关闭防火墙关闭防护 1.2.3下载centos:7镜像 1.3Docker-Compose 编排nginx 1.3.1切换工作目录 1.3.2编写 Dockerfile 文件 1.3.3修改nginx.conf配置文件 1.4Docker-Co…

GDPU Java 天码行空10

&#xff08;一&#xff09;实验目的 1、掌握JAVA中文件、IO类及其构造方法&#xff1b; 2、重点掌握文件类型所具有的文件操作方法&#xff1b; 3、重点掌握IO中类所具有的IO操作方法&#xff1b; 4、熟悉递归调用的思想及应用&#xff1b; 5、掌握IO中读写常用方法。 &…

鸿蒙UI复用

鸿蒙UI复用 简介BuilderBuilder的使用方式一Builder的使用方式二Builder的使用方式三 Component使用Component复用UI 简介 在页面开发过程中&#xff0c;会遇到有UI相似的结构&#xff0c;如果每个UI都单独声明一份&#xff0c;会产生大量冗余代码&#xff0c;不利于阅读。遇到…

CSS浮动(如果想知道CSS有关浮动的知识点,那么只看这一篇就足够了!)

前言&#xff1a;在学习CSS排版的时候&#xff0c;浮动是我们必须要知道的知识点&#xff0c;浮动在设计之初是为了实现文字环绕效果的&#xff0c;但是后来被人们发现浮动在CSS排版中有着很好的实用价值&#xff0c;所以浮动便成为了CSS排版的利器之一。 ✨✨✨这里是秋刀鱼不…

论文辅助笔记:Tempo 之 model.py

0 导入库 import math from dataclasses import dataclass, asdictimport torch import torch.nn as nnfrom src.modules.transformer import Block from src.modules.prompt import Prompt from src.modules.utils import (FlattenHead,PoolingHead,RevIN, )1TEMPOConfig 1.…