【Yolo3】入门目标检测实验--Python+Opencv2+dnn

一、前言

yolo3也是目标检测的新兴算法之一。它的发展是基于HOG->CNN ->RCNN->YOLO
在这里插入图片描述
(图源于网络,侵删)

图像检测发展史

  1. HOG阶段(一步法):边缘检测+卷积神经网络。
  2. CNN ~ R-CNN阶段(两步法):基于图像分类加上滑动窗口。
  3. YOLO:区域推荐(RP)。

1 , 2 方法缺点:计算量比较大,导致性能低下无法实时;YOLO采样了一种完全不同的方法,达到对图像每个区域只计算一次(You Look at Once - YOLO)。

YOLO3实现过程

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述
图1:YOLO把图像分为13x13的Cell(网格);

图2:每个Cell预测5个BOX,同时YOLO也会生成一个置信分数,告诉每个BOX包含某个对象的可能性是多少;

(注意置信分数不会直接说明BOX内是检测到何种对象,最终那些得分高的BOX被加粗显示)

图3:对于每个BOX来说,Cell会预测检测对象类别,这部分的工作就像是一个分类器一样;

(基于VOC数据集20中对象检测,YOLO结合分数与分类信息对每个BOX给出一个最终可能对象类型的可能性值,黄色标注区域85%可能性是狗)

因为总数是13x13的网格,每个网格预言5个BOX,所以最终有854个BOX,证据表明绝大多数的BOX得分会很低,我们只要保留30%BOX即可(取决于你自己的阈值设置),最终输出:
在这里插入图片描述

此部分引用出处:
OpenCV DNN之YOLO实时对象检测

YOLO3训练原理

参考:YOLOv3 深入理解

后面我会标注训练自己的手语数据集,这里不扩展。

二、入门实验coco数据集

1.下载

现在用官网示例的模型进行实验:

  1. 下载:

    https://pjreddie.com/media/files/yolov3.weights
    (官网很慢的,可以在 百度网盘 中下载,好心人一生平安!)

    https://github.com/pjreddie/darknet/blob/master/cfg/yolov3.cfg

    https://github.com/pjreddie/darknet/blob/master/data/coco.names

  2. 说明:

    • coco.names:训练模型的所有类别名。

    • yolov3.weights:预训练的权重。

    • yolov3.cfg:配置文件。

  3. coco.names包含:

    人 自行车 汽车 摩托车 飞机 巴士 火车 卡车 船 红绿灯 消防栓 站牌 停车咪表 板凳 鸟 猫 狗 
    马 羊 牛 象 熊 斑马 长颈鹿 背包 雨伞 手袋 领带 手提箱 飞碟 滑雪 单板滑雪 运动的球 
    风筝 棒球棒 棒球手套 滑板 冲浪板 网球拍 瓶 酒杯 杯 叉 刀 勺 碗 香蕉 苹果 三明治 橙 
    花椰菜 胡萝卜 热狗 披萨 甜甜圈 蛋糕 椅子 沙发 盆栽植物  床 餐桌  厕所 电视 笔记本
    鼠标 遥控 键盘 手机  微波 烤箱 烤面包 片 冰箱 本书 时钟 花瓶 剪刀 泰迪熊 吹风机 牙刷
    
  4. 参考教程:

    https://www.learnopencv.com/deep-learning-based-object-detection-using-yolov3-with-opencv-python-c/

2.食用

单图测试:识别率99.9%,惊人!
在这里插入图片描述
测试图2:
狗:98%、猫:67%(识别错误,这应该是狗狗)
在这里插入图片描述在这里插入图片描述
鸟:57%、
在这里插入图片描述
其他动物:太小,角度等,未能识别,这都是我们在制作模型,标注图像需要注意的地方

视频检测:
在这里插入图片描述
原谅我睡衣出境,不过person和cellphone的检测效果挺好的。

3.源码

相关笔记:
https://gitee.com/cungudafa/Python-notes/blob/master/yolov3/yolotest.ipynb

全部源码:

  1. 图片部分

参考:【教程】opencv-python+yolov3实现目标检测

import numpy as np
import cv2 as cv
import os
import time
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号

#参数
yolo_dir = 'D:/myworkspace/dataset/yolov3/'  # 你下载YOLO权重的文件路径
CONFIDENCE = 0.5  # 过滤弱检测的最小概率
THRESHOLD = 0.4  # 非最大值抑制阈值

def yolov3(imgPath,yolo_dir,CONFIDENCE,THRESHOLD):
    weightsPath = os.path.join(yolo_dir, 'yolov3.weights')  # 权重文件
    configPath = os.path.join(yolo_dir, 'yolov3.cfg')  # 配置文件
    labelsPath = os.path.join(yolo_dir, 'coco.names')  # label名称
    # 加载网络、配置权重
    net = cv.dnn.readNetFromDarknet(configPath, weightsPath)  ## 利用下载的文件
    print("[INFO] loading YOLO from disk...") ## 可以打印下信息

    # 加载图片、转为blob格式、送入网络输入层
    img = cv.imread(imgPath)
    blobImg = cv.dnn.blobFromImage(img, 1.0/255.0, (416, 416), None, True, False)  ## net需要的输入是blob格式的,用blobFromImage这个函数来转格式
    net.setInput(blobImg)  ## 调用setInput函数将图片送入输入层

    # 获取网络输出层信息(所有输出层的名字),设定并前向传播
    outInfo = net.getUnconnectedOutLayersNames()  ## 前面的yolov3架构也讲了,yolo在每个scale都有输出,outInfo是每个scale的名字信息,供net.forward使用
    start = time.time()
    layerOutputs = net.forward(outInfo)  # 得到各个输出层的、各个检测框等信息,是二维结构。
    end = time.time()
    print("[INFO] YOLO took {:.6f} seconds".format(end - start)) ## 可以打印下信息

    # 拿到图片尺寸
    (H, W) = img.shape[:2]
    # 过滤layerOutputs
    # layerOutputs的第1维的元素内容: [center_x, center_y, width, height, objectness, N-class score data]
    # 过滤后的结果放入:
    boxes = [] # 所有边界框(各层结果放一起)
    confidences = [] # 所有置信度
    classIDs = [] # 所有分类ID

    # # 1)过滤掉置信度低的框框
    for out in layerOutputs:  # 各个输出层
        for detection in out:  # 各个框框
            # 拿到置信度
            scores = detection[5:]  # 各个类别的置信度
            classID = np.argmax(scores)  # 最高置信度的id即为分类id
            confidence = scores[classID]  # 拿到置信度

            # 根据置信度筛查
            if confidence > CONFIDENCE:
                box = detection[0:4] * np.array([W, H, W, H])  # 将边界框放会图片尺寸
                (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)应用非最大值抑制(non-maxima suppression,nms)进一步筛掉
    idxs = cv.dnn.NMSBoxes(boxes, confidences, CONFIDENCE, THRESHOLD) # boxes中,保留的box的索引index存入idxs

    # 得到labels列表
    with open(labelsPath, 'rt') as f:
        labels = f.read().rstrip('\n').split('\n')

    # 应用检测结果
    np.random.seed(42)
    COLORS = np.random.randint(0, 255, size=(len(labels), 3), dtype="uint8")  # 框框显示颜色,每一类有不同的颜色,每种颜色都是由RGB三个值组成的,所以size为(len(labels), 3)
    if len(idxs) > 0:
        for i in idxs.flatten(): # indxs是二维的,第0维是输出层,所以这里把它展平成1维
            (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]]]
            cv.rectangle(img, (x, y), (x+w, y+h), color, 2)  # 线条粗细为2px
            text = "{}: {:.4f}".format(labels[classIDs[i]], confidences[i])
            print("[INFO] predect result is: ",text)
            (text_w, text_h), baseline = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2)
            cv2.rectangle(img, (x, y-text_h-baseline), (x + text_w, y), color, -1)
            cv.putText(img, text, (x, y-5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,0), 2)  # cv.FONT_HERSHEY_SIMPLEX字体风格、0.5字体大小、粗细2px
            # cv.imshow('detected image', img)
            # cv.waitKey(0)
            plt.figure(figsize=[10, 10])
            plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
            plt.axis("off")
            plt.show()
            

imgPath = os.path.join(yolo_dir, 'test.jpg')  # 测试图像
yolov3(imgPath,yolo_dir,CONFIDENCE,THRESHOLD)


imgPath = os.path.join(yolo_dir, 'test2.jpg')  # 测试图像
yolov3(imgPath,yolo_dir,CONFIDENCE,THRESHOLD)

  1. 视频部分
import numpy as np
import cv2 as cv
import os
import time
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号

#参数
yolo_dir = 'D:/myworkspace/dataset/yolov3/'  # YOLO文件路径
CONFIDENCE = 0.5  # 过滤弱检测的最小概率
THRESHOLD = 0.4  # 非最大值抑制阈值

def yolov3_vedio(img,yolo_dir,CONFIDENCE,THRESHOLD):
    weightsPath = os.path.join(yolo_dir, 'yolov3.weights')  # 权重文件
    configPath = os.path.join(yolo_dir, 'yolov3.cfg')  # 配置文件
    labelsPath = os.path.join(yolo_dir, 'coco.names')  # label名称
    # 加载网络、配置权重
    net = cv.dnn.readNetFromDarknet(configPath, weightsPath)  ## 利用下载的文件
    print("[INFO] loading YOLO from disk...") ## 可以打印下信息

    blobImg = cv.dnn.blobFromImage(img, 1.0/255.0, (416, 416), None, True, False)  ## net需要的输入是blob格式的,用blobFromImage这个函数来转格式
    net.setInput(blobImg)  ## 调用setInput函数将图片送入输入层

    # 获取网络输出层信息(所有输出层的名字),设定并前向传播
    outInfo = net.getUnconnectedOutLayersNames()  ## 前面的yolov3架构也讲了,yolo在每个scale都有输出,outInfo是每个scale的名字信息,供net.forward使用
    start = time.time()
    layerOutputs = net.forward(outInfo)  # 得到各个输出层的、各个检测框等信息,是二维结构。
    end = time.time()
    print("[INFO] YOLO took {:.6f} seconds".format(end - start)) ## 可以打印下信息

    # 拿到图片尺寸
    (H, W) = img.shape[:2]
    # 过滤layerOutputs
    # layerOutputs的第1维的元素内容: [center_x, center_y, width, height, objectness, N-class score data]
    # 过滤后的结果放入:
    boxes = [] # 所有边界框(各层结果放一起)
    confidences = [] # 所有置信度
    classIDs = [] # 所有分类ID

    # # 1)过滤掉置信度低的框框
    for out in layerOutputs:  # 各个输出层
        for detection in out:  # 各个框框
            # 拿到置信度
            scores = detection[5:]  # 各个类别的置信度
            classID = np.argmax(scores)  # 最高置信度的id即为分类id
            confidence = scores[classID]  # 拿到置信度

            # 根据置信度筛查
            if confidence > CONFIDENCE:
                box = detection[0:4] * np.array([W, H, W, H])  # 将边界框放会图片尺寸
                (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)应用非最大值抑制(non-maxima suppression,nms)进一步筛掉
    idxs = cv.dnn.NMSBoxes(boxes, confidences, CONFIDENCE, THRESHOLD) # boxes中,保留的box的索引index存入idxs

    # 得到labels列表
    with open(labelsPath, 'rt') as f:
        labels = f.read().rstrip('\n').split('\n')

    # 应用检测结果
    np.random.seed(42)
    COLORS = np.random.randint(0, 255, size=(len(labels), 3), dtype="uint8")  # 框框显示颜色,每一类有不同的颜色,每种颜色都是由RGB三个值组成的,所以size为(len(labels), 3)
    if len(idxs) > 0:
        for i in idxs.flatten(): # indxs是二维的,第0维是输出层,所以这里把它展平成1维
            (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]]]
            cv.rectangle(img, (x, y), (x+w, y+h), color, 2)  # 线条粗细为2px
            text = "{}: {:.4f}".format(labels[classIDs[i]], confidences[i])
            print("[INFO] predect result is: ",text)
            (text_w, text_h), baseline = cv.getTextSize(text, cv.FONT_HERSHEY_SIMPLEX, 0.5, 2)
            cv.rectangle(img, (x, y-text_h-baseline), (x + text_w, y), color, -1)
            cv.putText(img, text, (x, y-5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,0), 2)  # cv.FONT_HERSHEY_SIMPLEX字体风格、0.5字体大小、粗细2px
        
    return img

cap = cv.VideoCapture(0)

while(True):
    ret, frame = cap.read()                        #读取帧
    yolov3_vedio(frame,yolo_dir,CONFIDENCE,THRESHOLD)
    
    cv.imshow('frame',frame)
    
    if cv.waitKey(1) & 0xFF == ord('q'):          #按‘q’退出
        break

#释放资源并关闭窗口
cap.release()
cv.destroyAllWindows()


发布了246 篇原创文章 · 获赞 485 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/cungudafa/article/details/105028034