一、前言
yolo3也是目标检测的新兴算法之一。它的发展是基于HOG->CNN ->RCNN->YOLO
。
(图源于网络,侵删)
图像检测发展史:
- HOG阶段(一步法):边缘检测+卷积神经网络。
- CNN ~ R-CNN阶段(两步法):基于图像分类加上滑动窗口。
- 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.下载
现在用官网示例的模型进行实验:
-
下载:
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
-
说明:
-
coco.names:训练模型的所有类别名。
-
yolov3.weights:预训练的权重。
-
yolov3.cfg:配置文件。
-
-
coco.names包含:
人 自行车 汽车 摩托车 飞机 巴士 火车 卡车 船 红绿灯 消防栓 站牌 停车咪表 板凳 鸟 猫 狗 马 羊 牛 象 熊 斑马 长颈鹿 背包 雨伞 手袋 领带 手提箱 飞碟 滑雪 单板滑雪 运动的球 风筝 棒球棒 棒球手套 滑板 冲浪板 网球拍 瓶 酒杯 杯 叉 刀 勺 碗 香蕉 苹果 三明治 橙 花椰菜 胡萝卜 热狗 披萨 甜甜圈 蛋糕 椅子 沙发 盆栽植物 床 餐桌 厕所 电视 笔记本 鼠标 遥控 键盘 手机 微波 烤箱 烤面包 片 冰箱 本书 时钟 花瓶 剪刀 泰迪熊 吹风机 牙刷
-
参考教程:
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
全部源码:
- 图片部分
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)
- 视频部分
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()