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/

效果

opencv android 目标检测 opencv实现目标检测_机器学习


opencv android 目标检测 opencv实现目标检测_opencv android 目标检测_02


opencv android 目标检测 opencv实现目标检测_ide_03


opencv android 目标检测 opencv实现目标检测_ide_04

前期准备

(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()

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