【教程】opencv-python+yolov3实现目标检测
<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>【教程】opencv-python+yolov3实现目标检测<ul><li>目标检测概况<ul><li>目标检测是?</li><li>目标检测算法?</li></ul></li><li>yolov3模型简介<ul><li>性能介绍</li><li>架构介绍</li></ul></li><li>opencv-python实现<ul><li>why opencv?</li><li>正文<ul><li>先</li></ul></li><li>再</li></ul></li></ul></li></ul></div><p></p><p>话说我发现自己之前辛辛苦苦写的一篇被人爬了............所以为了应对那种情况,我把自己的博客地址贴上吧...<br>
本博客地址:小塞https://www.cnblogs.com/hesse-summer/</p>
<h1 id="教程opencv-pythonyolov3实现目标检测">【教程】opencv-python+yolov3实现目标检测</h1>
<p>因为最近的任务有用到目标检测,所以昨天晚上、今天上午搞了一下,快速地了解了目标检测这一任务,并且实现了使用opencv进行目标检测。</p>
<p>网上资料挺乱的,感觉在搜资源上浪费了我不少时间,所以我写这篇博客,把我这段时间了解到的东西整理起来,供有缘的读者参考学习。</p>
<h2 id="目标检测概况">目标检测概况</h2>
<h3 id="目标检测是">目标检测是?</h3>
<p>目标检测,粗略来说就是:输入图片/视频,经过处理,得到:目标的位置信息(比如左上角和右下角的坐标)、目标的预测类别、目标的预测置信度(confidence)。</p>
<p>拿Faster R-CNN这个算法举例:输入一个batch(batch size也可以为1)的图片或者视频,网络直接的outputs是这样的:<br>
,batchId, classId, confidence, left, top, right, bottom都是标量。<br>
batchId表示这一个batch中,这张图片的id(也即index),后四个标量即目标的位置信息:左上角像素点和右下角像素点的坐标。</p>
<h3 id="目标检测算法">目标检测算法?</h3>
<p>按照历史脉络来谈:</p>
<ol>
<li>
<p>手工特征提取算法,如VJ、HOG、DPM</p>
</li>
<li>
<p>R-CNN算法(2014),最早的基于深度学习的目标检测器之一,其结构是两级网络:1)首先需要诸如<strong>选择性搜索</strong>之类的算法来<strong>提出可能包含对象的候选边界框</strong>;2)然后<strong>将这些区域传递到CNN算法进行分类</strong>;</p>
<p>R-CNN算法存在的问题是其仿真很慢,并且不是完整的端到端的目标检测器。</p>
</li>
<li>
<p>Fast R-CNN算法(2014末),对原始R-CNN进行了相当大的改进:提高<strong>准确度</strong>,并减少执行正向传递所花费的<strong>时间</strong>。</p>
<p>但是,该模型仍然依赖于外部区域搜索算法。</p>
</li>
<li>
<p>faster R-CNN算法(2015),真正的<strong>端到端</strong>深度学习目标检测器。<strong>删除了选择性搜索</strong>的要求,而是依赖于<br>
(1)完全卷积的<strong>区域提议网络</strong>(<strong>RPN</strong>, Region Purpose Network),可以预测对象边界框和<strong>“对象”分数</strong>(量化它是一个区域的可能性的分数)。<br>
(2)然后将RPN的输出传递到R-CNN组件以进行最终分类和标记。</p>
</li>
<li>
<p>R-CNN系列算法,都采取了two-stage策略。特点是:虽然检测结果一般都非常准确,但仿真速度非常慢,即使是在GPU上也仅获得5 FPS。</p>
</li>
<li>
<p>one-stage方法有:yolo(2015)、SSD(2015末),以及在这两个算法基础上改进的各论文提出的算法。这些算法的基本思路是:<strong>均匀地</strong>在图片的不同位置进行<strong>密集抽样</strong>,抽样时可以采用不同尺度和长宽比,然后利用CNN<strong>提取特征后直接进行分类与回归</strong>。<br>
整个过程只需要一步,所以其优势是速度快,但是<strong>训练比较困难</strong>。</p>
</li>
<li>
<p>yolov3(2018)是yolo作者提出的第三个版本(之前还提过yolov2和它们的tinny版本,tinny版本经过压缩更快但是也降低了准确率)。yolov3支持80类物体的目标检测,完整列表[戳这里]: https://github.com/pjreddie/darknet/blob/master/data/coco.names</p>
</li>
</ol>
<p>时间线:</p>
<p><img src="https://img2018.cnblogs.com/blog/1281706/201908/1281706-20190811175714201-1344617013.png"></p>
<h2 id="yolov3模型简介">yolov3模型简介</h2>
<h3 id="性能介绍">性能介绍</h3>
<p>首先,套路,yolov3很强大(不强大我用它干啥呢)。速度上,它比 R-CNN 快 1000 倍,比 Fast R-CNN 快 100 倍。检测准确率上,它不是最准的:YOLOv3-608比 DSSD 更高,接近 FPN。但是它的速度不到后二者的1/3。</p>
<p>从下图也可以看出:</p>
<p><img src="https://image.jiqizhixin.com/uploads/editor/41c569c7-ebcc-41e1-9129-1bb58c6f8083/1526265435857.png"></p>
<h3 id="架构介绍">架构介绍</h3>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/urgCdYOG5QduzP2223H3zyCQ3Myib5tk2Vt4POWMtyGYeibQqYpNmUDiaDEVTIVVkobiaSYPs8DzB6LdfhcITNaMiaQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1"></p>
<p>可以看出,他是一系列卷积、残差、上采样组成的。特点在于,它将<strong>预测分在三个尺度</strong>(Scale)进行(见图中三个彩色框),也<strong>在三个scale分别输出</strong>。</p>
<h2 id="opencv-python实现">opencv-python实现</h2>
<h3 id="why-opencv">why opencv?</h3>
<p>opencv( <strong>3.4.2+版本</strong>)的dnn(Deep Neural Network-DNN)模块封装了Darknet框架,这个框架是</p>
<p>自己写的,它由封装了yolo算法。因为这么一层关系,我们可以使用opencv方便地使用yolo的各个版本,而且有数据(见下)证明OpenCV的DNN模块在 CPU的实现速度比使用 OpenML 的 Darknet 快9倍。</p>
<p><img src="https://img2018.cnblogs.com/blog/1281706/201908/1281706-20190811175820676-842400452.png"></p>
<h3 id="正文">正文</h3>
<p>我会<strong>先</strong>结合脚本片段讲解,<strong>再</strong>给出该脚本的完整代码,讲解。</p>
<h4 id="先">先</h4>
<p>引库</p>
<pre><code class="language-pyt">import numpy as np
import cv2 as cv
import os
import time
</code></pre>
<hr>
<p>参数:</p>
<pre><code class="language-python">yolo_dir = '/home/hessesummer/github/NTS-Net-my/yolov3'# YOLO文件路径
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名称
imgPath = os.path.join(yolo_dir, 'test.jpg')# 测试图像
CONFIDENCE = 0.5# 过滤弱检测的最小概率
THRESHOLD = 0.4# 非最大值抑制阈值
</code></pre>
<p>权重文件、配置文件、label名称的下载地址:</p>
<pre><code class="language-bash">wget https://pjreddie.com/media/files/yolov3.weights
wget https://github.com/pjreddie/darknet/blob/master/cfg/yolov3.cfg
wget https://github.com/pjreddie/darknet/blob/master/data/coco.names
</code></pre>
<p>简单来说:</p>
<p>过滤弱检测的最小概率:置信度小于这个值的输出都不要了;<br>
非最大值抑制阈值:允许框框重叠的程度(多框框检测同一个物体),供下面的NMS算法使用,该算法会根据该值将有重叠的框框合并。值为0时,不允许框框重叠。默认值是0.3。</p>
<p>详细来说:</p>
<p>我没查。您自己感兴趣再了解吧。</p>
<hr>
<p>重头戏<strong>1</strong>:</p>
<pre><code class="language-python"># 加载网络、配置权重
net = cv.dnn.readNetFromDarknet(configPath, weightsPath)## 利用下载的文件
# print(" 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(" YOLO took {:.6f} seconds".format(end - start)) ## 可以打印下信息
</code></pre>
<p><code>layerOutputs</code>是二维结构,第0维代表哪个输出层,第1维代表各个检测框。</p>
<p>其他的我都在注释里讲解了。</p>
<hr>
<p>重头戏<strong>2</strong>:</p>
<pre><code class="language-python"># 拿到图片尺寸
(H, W) = img.shape[:2]
</code></pre>
<p>供下面使用:</p>
<pre><code class="language-python"># 过滤layerOutputs
# layerOutputs的第1维的元素内容:
# 过滤后的结果放入:
boxes = [] # 所有边界框(各层结果放一起)
confidences = [] # 所有置信度
classIDs = [] # 所有分类ID
# # 1)过滤掉置信度低的框框
for out in layerOutputs:# 各个输出层
for detection in out:# 各个框框
# 拿到置信度
scores = detection# 各个类别的置信度
classID = np.argmax(scores)# 最高置信度的id即为分类id
confidence = scores# 拿到置信度
# 根据置信度筛查
if confidence > CONFIDENCE:
box = detection * np.array()# 将边界框放会图片尺寸
(centerX, centerY, width, height) = box.astype("int")
x = int(centerX - (width / 2))
y = int(centerY - (height / 2))
boxes.append()
confidences.append(float(confidence))
classIDs.append(classID)
# # 2)应用非最大值抑制(non-maxima suppression,nms)进一步筛掉
idxs = cv.dnn.NMSBoxes(boxes, confidences, CONFIDENCE, THRESHOLD) # boxes中,保留的box的索引index存入idxs
</code></pre>
<p>这里的NMS算法就是前面提到的NMS算法。</p>
<hr>
<p>应用检测结果,这里是画出框框。</p>
<pre><code class="language-python"># 得到labels列表
with open(labelsPath, 'rt') as f:
labels = f.read().rstrip('\n').split('\n')
</code></pre>
<p>供下面使用:</p>
<pre><code class="language-python"># 应用检测结果
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, boxes)
(w, h) = (boxes, boxes)
color = ]]
cv.rectangle(img, (x, y), (x+w, y+h), color, 2)# 线条粗细为2px
text = "{}: {:.4f}".format(labels], confidences)
cv.putText(img, text, (x, y-5), cv.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)# cv.FONT_HERSHEY_SIMPLEX字体风格、0.5字体大小、粗细2px
cv.imshow('目标检测结果', img)
cv.waitKey(0)
</code></pre>
<p>第一部分讲解结束,下面放完整代码:</p>
<h3 id="再">再</h3>
<pre><code class="language-python">import numpy as np
import cv2 as cv
import os
import time
yolo_dir = '/home/hessesummer/github/NTS-Net-my/yolov3'# YOLO文件路径
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名称
imgPath = os.path.join(yolo_dir, 'test.jpg')# 测试图像
CONFIDENCE = 0.5# 过滤弱检测的最小概率
THRESHOLD = 0.4# 非最大值抑制阈值
# 加载网络、配置权重
net = cv.dnn.readNetFromDarknet(configPath, weightsPath)# #利用下载的文件
print(" 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(" YOLO took {:.6f} seconds".format(end - start))# # 可以打印下信息
# 拿到图片尺寸
(H, W) = img.shape[:2]
# 过滤layerOutputs
# layerOutputs的第1维的元素内容:
# 过滤后的结果放入:
boxes = [] # 所有边界框(各层结果放一起)
confidences = [] # 所有置信度
classIDs = [] # 所有分类ID
# # 1)过滤掉置信度低的框框
for out in layerOutputs:# 各个输出层
for detection in out:# 各个框框
# 拿到置信度
scores = detection# 各个类别的置信度
classID = np.argmax(scores)# 最高置信度的id即为分类id
confidence = scores# 拿到置信度
# 根据置信度筛查
if confidence > CONFIDENCE:
box = detection * np.array()# 将边界框放会图片尺寸
(centerX, centerY, width, height) = box.astype("int")
x = int(centerX - (width / 2))
y = int(centerY - (height / 2))
boxes.append()
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, boxes)
(w, h) = (boxes, boxes)
color = ]]
cv.rectangle(img, (x, y), (x+w, y+h), color, 2)# 线条粗细为2px
text = "{}: {:.4f}".format(labels], confidences)
cv.putText(img, text, (x, y-5), cv.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)# cv.FONT_HERSHEY_SIMPLEX字体风格、0.5字体大小、粗细2px
cv.imshow('detected image', img)
cv.waitKey(0)
</code></pre>
<p>结果:</p>
<p><img src="https://img2018.cnblogs.com/blog/1281706/201908/1281706-20190811175844270-1337724732.png"></p>
<p>到此结束~</p>
<p>写博客真累,花太多时间了哎~</p><br><br>
来源:https://www.cnblogs.com/hesse-summer/p/11335865.html
頁:
[1]