⚠申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址。 全文共计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–
说明
本实验(项目)/论文若有需要,请后台私信或【文末】个人微信公众号联系我