opencv项目9---利用YOLOv3实现对物体的实时检测

#利用yolov3的模型结构和权重参数实现对物体的实时检测,正确率挺高的,其主要原理是利用神经网络去将我们的图像不断的进行处理,最后利用图像处理中的金字塔思想,做了3次采样变化,得到不同的特征图,通过用3种不同的方式进行预测,判断处最合适的预测,并将结果返回。有兴趣的可以去看看相关的论文。

1.代码运行后展示不同物体的结果(使用手机的图片)

 

 

 总的来说,检测的效果不错,但在我运行过程中,出现了实时视频的卡顿,不流畅的问题,可能是因为我使用的是CPU,处理的速度跟不上,有兴趣的可以用GPU试一下。官方网站显示的有相关的fps标准,可能是设备问题,所以帧率没有达到45fps.关于权重文件和模型配置,可以去官网下载,网址:

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

你使用对应的yolov3d的model就要下载对应的权重文件和模型配置,不一样的模型内部的神经网络层也不太一样,以yolov3-320,对应的输出特征图像是3个,而yolov3-tiny对应的输出特征图像是2个。当然精度和速度不能权衡,两个之间一个好,另一个就必然下降。

如图:

我自己尝试了320和tiny的模型,有兴趣的可以尝试不一样的model。

当下载好对应的权重参数和模型配置后,还要下载coco.name数据集,里面有80个常见的物体种类的名字,模型会根据预测返回对每个种类的概率。

coco.name数据集我已经上传到github上:

https://github.com/Drift-Of-Little-Forest/opencv-practice.git

下面就是代码的复现了:

通过以下代码,实现对数据集的读取,并以列表形式展示出来,方便我们进行索引

## 导入coco数据集,里面有各种80多种类别
classesFile = "opencv_data_ku/coco.names"
classNames = []
with open(classesFile, 'rt') as f:
    classNames = f.read().rstrip('\n').split('\n')
print(classNames)

然后就是导入神经网络模型:

"===============引入我们的yolo3模块=========="
## Model Files
#可以去yolo官网自己搜下面相关的文件参数
modelConfiguration = "yolov3-320.cfg"
modelWeights = "opencv_data_ku/yolov3.weights"
#调用函数进行模型的配置和权重的配置
net = cv.dnn.readNetFromDarknet(modelConfiguration, modelWeights)
#要求网络使用其支持的特定计算后
net.setPreferableBackend(cv.dnn.DNN_BACKEND_OPENCV)
#使用CPU进行计算
net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU)

紧接着就是调动相机,进行获取实时图像:

cap = cv.VideoCapture(0)
while True:
    success, img = cap.read()
    img = cv2.flip(img, 1)
    cv.imshow('Image', img)
    cv.waitKey(1)

上面的代码中:

img = cv2.flip(img, 1)

是让图像实现镜像,左右跟我们实时的一致。

然后就是将实时获取的图像传输就我们的模型当中:

 #神经网络的输入图像需要采用称为blob的特定格式。用需要检测的原始图像image构造一个blob图像,
    #对原图像进行像素归一化1 / 255.0,缩放尺寸
    # (320, 320),交换了R与B通道
    blob = cv.dnn.blobFromImage(img, 1 / 255, (320, 320), 1, crop=False)
    #将blob变成网络的输入
    net.setInput(blob)
    #获取神经网络所有层的名称
    layersNames = net.getLayerNames()
    print('所有层:',layersNames)
    #我们不需要改变其他层,只需要找到未连接层,也就是网络的最后一层,在最后一层的基础上进行前向传播就可以了
    print('三个输出层的索引号',net.getUnconnectedOutLayers())
    for i in net.getUnconnectedOutLayers():
        outputNames = [layersNames[i - 1]]
        print('输出层名字',outputNames)
        #前向传播
        outputs = net.forward(outputNames)
        print(outputs)
        #了解每个输出层的形状
        print(outputs[0].shape)

通过上面的操作,我们已经可以获得3个输出层的内容,例如(300,85),(1200,85),(4800,85),就是对不同特征图像的每一个特征像素的判断,85列中的内容大概如下:

 可以清楚看到,前5项,分别是(x,y,w,h,conf),也就是预测框的中心点和矩形的长宽,后面一个是置信度。后面的是对种类的预测概率,我们可以通过对列表的索引,切片等操作,把这些信息给提取出来,有了这些我们就可以用opencv 中的

cv.rectangle,cv.putText

在图像中画出对应的框和标注对应的内容。需要注意的是opencv中画矩形框,是需要定义框的起始位置和窗高的,所以我们要进行转换:(前5项,分别是(x,y,w,h,conf),也就是预测框的中心点和矩形的长宽,后面一个是置信度,这是在一个像素点上的,如果是放在整张图像上呢?,肯定要乘以图像的宽和高,可以看我画的示意图:)

具体代码操作:

                w, h = int(det[2] * wT), int(det[3] * hT)
                x, y = int((det[0] * wT) - w / 2), int((det[1] * hT) - h / 2)

这样就可以画出我们对预测对象的框了,但是我们数据是300行,1200行,4800行,所以我们要进行遍历.........具体代码如下:

def findObjects(outputs, img):
    hT, wT, cT = img.shape#输出照片的宽高通道数
    bbox = []
    classIds = []
    confs = []
    #对检测处的结果进行对比处理
    for output in outputs:
        for det in output:
            #可以看一下输出的内容
            scores = det[5:]#这里的意思是输出是某个种类的概率,前五个是框的位置以及置信度
            classId = np.argmax(scores)#找到是最大的种类编号
            confidence = scores[classId]#找到置信度
            if confidence > 0.5:#设立阈值
                w, h = int(det[2] * wT), int(det[3] * hT)
                x, y = int((det[0] * wT) - w / 2), int((det[1] * hT) - h / 2)
                #更新新的框
                bbox.append([x, y, w, h])
                #将索引加入到列表里面去
                classIds.append(classId)
                #置信度加入到创建的列表当中去
                confs.append(float(confidence))
    #对于这个函数,可以在目标检测中筛选置信度低于阈值的,还进行Nms筛选,
    # 至于参数,第一个参数是输入的预测框的尺寸,注意这里得尺寸是预测框左上角和右下角的尺寸,类似yolov3这种最后预测的坐标是中心点和长宽
    #第二个参数是预测中的的置信度得分
    #其实这个函数做了两件事情,对预测框和置信度设定阈值,进行筛选
    indices = cv.dnn.NMSBoxes(bbox, confs, 0.5, 0.6)
    #将框画出来
    for i in indices:
        i = i
        box = bbox[i]
        x, y, w, h = box[0], box[1], box[2], box[3]
        # print(x,y,w,h)
        cv.rectangle(img, (x, y), (x + w, y + h), (255, 0, 255), 2)
        cv.putText(img, f'{classNames[classIds[i]].upper()} {int(confs[i] * 100)}%',(x, y - 10), cv.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 2)

将实现这一系列操作后,就可以完成整个物体检测的工作了,其中yolov3的模型的工作原理是:

 

完整代码如下:(每一步都有注释方便大家更好的理解代码的含义)

import numpy as np
import cv2 as cv
cap = cv.VideoCapture(0)


## 导入coco数据集,里面有各种80多种类别
classesFile = "opencv_data_ku/coco.names"
classNames = []
with open(classesFile, 'rt') as f:
    classNames = f.read().rstrip('\n').split('\n')
print(classNames)

"===============引入我们的yolo3模块=========="
## Model Files
#可以去yolo官网自己搜下面相关的文件参数
modelConfiguration = "yolov3-320.cfg"
modelWeights = "opencv_data_ku/yolov3.weights"
#调用函数进行模型的配置和权重的配置
net = cv.dnn.readNetFromDarknet(modelConfiguration, modelWeights)
#要求网络使用其支持的特定计算后
net.setPreferableBackend(cv.dnn.DNN_BACKEND_OPENCV)
#使用CPU进行计算
net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU)


def findObjects(outputs, img):
    hT, wT, cT = img.shape#输出照片的宽高通道数
    bbox = []
    classIds = []
    confs = []
    #对检测处的结果进行对比处理
    for output in outputs:
        for det in output:
            #可以看一下输出的内容
            scores = det[5:]#这里的意思是输出是某个种类的概率,前五个是框的位置以及置信度
            classId = np.argmax(scores)#找到是最大的种类编号
            confidence = scores[classId]#找到置信度
            if confidence > 0.5:#设立阈值
                w, h = int(det[2] * wT), int(det[3] * hT)
                x, y = int((det[0] * wT) - w / 2), int((det[1] * hT) - h / 2)
                #更新新的框
                bbox.append([x, y, w, h])
                #将索引加入到列表里面去
                classIds.append(classId)
                #置信度加入到创建的列表当中去
                confs.append(float(confidence))
    #对于这个函数,可以在目标检测中筛选置信度低于阈值的,还进行Nms筛选,
    # 至于参数,第一个参数是输入的预测框的尺寸,注意这里得尺寸是预测框左上角和右下角的尺寸,类似yolov3这种最后预测的坐标是中心点和长宽
    #第二个参数是预测中的的置信度得分
    #其实这个函数做了两件事情,对预测框和置信度设定阈值,进行筛选
    indices = cv.dnn.NMSBoxes(bbox, confs, 0.5, 0.6)
    #将框画出来
    for i in indices:
        i = i
        box = bbox[i]
        x, y, w, h = box[0], box[1], box[2], box[3]
        # print(x,y,w,h)
        cv.rectangle(img, (x, y), (x + w, y + h), (255, 0, 255), 2)
        cv.putText(img, f'{classNames[classIds[i]].upper()} {int(confs[i] * 100)}%',(x, y - 10), cv.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 2)


while True:
    success, img = cap.read()
    #神经网络的输入图像需要采用称为blob的特定格式。用需要检测的原始图像image构造一个blob图像,
    #对原图像进行像素归一化1 / 255.0,缩放尺寸
    # (320, 320),交换了R与B通道
    blob = cv.dnn.blobFromImage(img, 1 / 255, (320, 320), 1, crop=False)
    #将blob变成网络的输入
    net.setInput(blob)
    #获取神经网络所有层的名称
    layersNames = net.getLayerNames()
    print('所有层:',layersNames)
    #我们不需要改变其他层,只需要找到未连接层,也就是网络的最后一层,在最后一层的基础上进行前向传播就可以了
    print('三个输出层的索引号',net.getUnconnectedOutLayers())
    for i in net.getUnconnectedOutLayers():
        outputNames = [layersNames[i - 1]]
        print('输出层名字',outputNames)
    #前向传播
        outputs = net.forward(outputNames)
        print(outputs)
        #了解每个输出层的形状
        print(outputs[0].shape)
    #调用框函数寻找对象的框
        findObjects(outputs, img)

    cv.imshow('Image', img)
    cv.waitKey(1)

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值