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://blog.csdn.net/Oswald_liu/article/details/94380597

官网:https://pjreddie.com/darknet/yolo/

效果

前期准备

(1)下载yolov3.cfg配置文件

(2)下载coco.names,也就是标签

(3)下载yolov3.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()

注:部分图片及文字来自网络,侵删

原文链接:https://blog.csdn.net/xia_hua_yan/article/details/105040526

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐