OpenCV实现yolov3实时目标检测
前言
这是小白第一次写博客,有什么错误和不严谨的地方还希望大家多多斧正。
最近在B站看了一个小哥从youtube搬来的一个视频,自己就照虎画猫跟着敲起了代码,接下来我就给大家介绍一下基本流程步骤,自己也学习学习。
OpenCV是一个十分强大的开源跨平台计算机视觉库,同时提供了Python、Ruby、MATLAB等语言的接口,可以运行在Linux、Windows、Android和Mac OS操作系统上,实现了图像处理和计算机视觉方面的很多通用算法。
官网:https://opencv.org/
YOLOv3是一种基于深度学习的端到端实时目标检测方法,OLOv3非常快速和准确。在mAP值为0.5 IOU时,YOLOv3与Focal Loss相而且,您只需更改模型的大小即可轻松在速度和精度之间进行权衡,而无需重新培训!Darknet 是一个用 c 和 CUDA 编写的开源神经网络框架。 它快速,易于安装,并支持 CPU 和 GPU 计算
官网:https://pjreddie.com/darknet/yolo/
效果
前期准备
(1)下载yolov3.cfg配置文件
https://github.com/pjreddie/darknet/blob/master/cfg/yolov3.cfg 注意:可以先保存为txt文件然后改后缀名为.cfg
(2)下载coco.names,也就是标签
https://github.com/pjreddie/darknet/blob/master/data/coco.names 注意:可以先保存为txt文件然后改后缀名为.names
(3)下载yolov3.weights,也就是训练好的权重文件
https://pjreddie.com/media/files/yolov3.weights 注意:后缀名为.weights
代码编写
# 参数初始化,YOLOv3算法生成边界框作为预测的检测输出。每个预测框都与一个置信度得分相关联。
在第一阶段,置信度阈值参数以下的所有框都将被忽略以进行进一步处理。
其余的框会受到非最大抑制,从而消除了多余的重叠边界框。非最大抑制由参数nmsThreshold控制。
您可以尝试更改这些值,并查看输出预测框的数量如何变化。
接下来,设置网络输入图像的输入宽度(inpWidth)和高度(inpHeight)的默认值。我们将每个参数设置为416,
以便可以将运行与YOLOv3的作者提供的Darknet的C代码进行比较。
您也可以将它们都更改为320以获取更快的结果,或将其更改为608以获取更准确的结果。
confThreshold = 0.25
nmsThresshold = 0.40
inpWidth = 416
inpHeight = 416
#获取输出层的名称,OpenCV的Net类中的forward函数需要终结层,直到它在网络中运行。
由于我们要遍历整个网络,因此需要确定网络的最后一层。
通过使用函数getUnconnectedOutLayers()可以做到这一点,
该函数给出未连接的输出层的名称,这些名称实际上是网络的最后一层。
然后,像前面的代码片段(net.forward(getOutputsNames(net)))一样,
运行网络的前向传递以从输出层获取输出。
def getOutputsNames(net):
layerNames = net.getLayerNames() #
return [layerNames[i[0] - 1] for i in net.getUnconnectedOutLayers()]
#处理网络的输出
前四个元素表示center_x,center_y,width和height。第五个元素表示边界框包围对象的置信度。
其余元素是与每个类(即对象类型)关联的置信度。将该框分配给与该框的最高分数相对应的类别。
盒子的最高分数也称为信心。如果框的置信度小于给定的阈值,则删除边界框,不考虑进一步处理。
然后将置信度等于或大于置信度阈值的框置于非最大抑制状态。这将减少重叠框的数量。
def postprocess(frame, outs):
frameHeight = frame.shape[0] #获取图像的高
frameWidth = frame.shape[1] #获取图像的款宽
classIDs = []
confidences = []
boxes = []
for out in outs:
for detection in out:
scores = detection[5:]
classID = np.argmax([scores]) #取出a中元素最大值所对应的索引,也是标签的索引
confidence = scores[classID]
if confidence > confThreshold:
centerX = int(detection[0] * frameWidth)
centerY = int(detection[1] * frameHeight)
width = int(detection[2] * frameWidth)
height = int(detection[3] * frameHeight)
left = int(centerX - width / 2)
top = int(centerY - height / 2)
classIDs.append(classID)
confidences.append(float(confidence))
boxes.append([left, top, width, height])
indices = cv.dnn.NMSBoxes(boxes, confidences, confThreshold, nmsThresshold)
for i in indices:
i = i[0]
box = boxes[i]
left = box[0]
top = box[1]
width = box[2]
height = box[3]
drawPred(classIDs[i], confidences[i], left, top, left + width, top + height)
#在目标出画矩形
def drawPred(classId, conf, left, top, right, bottom):
cv.rectangle(frame, (left, top), (right, bottom), (255, 178, 50),2 )
label = "%.2f" % conf
if classes:
assert (classId < len(classes))
label = '%s:%s' % (classes[classId], label)
cv.putText(frame, label, (left, top), cv.FONT_HERSHEY_COMPLEX, 0.5, (255,255,255), 1) #添加标签及置信度
classFile = "C:\\Users\\a1409\\PycharmProjects\\YOLOV3\\coco.names"
classes = None
with open(classFile, 'rt') as f:
classes = f.read().rstrip('\n').split('\n') #处理标签,返回一个列表
modelConf = 'C:\\Users\\a1409\\PycharmProjects\\YOLOV3\\yolov3.cfg'
modelWeights = 'C:\\Users\\a1409\\PycharmProjects\\YOLOV3\\yolov3.weights'
net = cv.dnn.readNetFromDarknet(modelConf, modelWeights) #读取存储在Darknet模型文件中的网络模型,cfgFile .cfg文件的路径,带有网络体系结构的文本描述。darknet模型 具有学习网络的.weights文件的路径。
net.setPreferableBackend(cv.dnn.DNN_BACKEND_OPENCV) # 要求网络在支持的地方使用特定的计算后端,这里是OPEN CV后端
net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU) # 要求网络在特定目标设备上进行计算,这里是CPU
winName = 'DLOD with OpenCV'
cv.namedWindow(winName, cv.WINDOW_NORMAL) #创建一个窗口
cv.resizeWindow(winName, 500, 500) #指定窗口大小
cap = cv.VideoCapture('C:\\Users\\a1409\\PycharmProjects\\YOLOV3\\person.mp4')
cap = cv.VideoCapture(0,cv.CAP_DSHOW) # 创建一个视频获取对象,0表示电脑摄像头,0可以换成视频路径
fourcc=cv.VideoWriter_fourcc(*'XVID') # 创建一个视频存储对象,编码格式为XVID
out=cv.VideoWriter('C:\\Users\\a1409\\PycharmProjects\\YOLOV3\\Out_Video.avi',fourcc,20.0,(640,480))
#处理每一帧图像
while (1):
hasFrame, frame = cap.read() #cap.read()按帧读取视频,hasFrame,frame是获cap.read()方法的两个返回值。其中hashFrame是布尔值,如果读取帧是正确的则返回True,如果文件读取到结尾,它的返回值就为False。frame就是每一帧的图像,是个三维矩阵。
blob = cv.dnn.blobFromImage(frame, 1 / 255, (inpWidth, inpHeight), [0, 0, 0], 1, crop=False) # 对图像进行预处理,包括减均值,比例缩放,裁剪,交换通道等,返回一个4通道的blob(blob可以简单理解为一个N维的数组,用于神经网络的输入
net.setInput(blob)
outs = net.forward(getOutputsNames(net)) #计算层的输出
postprocess(frame, outs)
cv.imshow(winName, frame)
out.write(frame)
k=cv.waitKey(27)
if k==27:
break
cv.destroyAllWindows()
注:部分图片及文字来自网络,侵删