目录
在本系列的前几篇文章中,我们从头开始实现了对象检测。我们观察到从头训练模型需要大量的计算资源和时间。这些算法训练和测试都很慢,几乎不可能实时有效地实现它们。将我们的注意力转移到面向速度的解决方案上,“一次只看一次(YOLO)”是当今最流行的对象检测方法。
YOLO简介
顾名思义,YOLO只需要向前传播即可检测给定图像中的物体。相同的属性使YOLO能够以最小的延迟处理实时视频,同时保持合理的准确性。
我们在本系列前面讨论的所有对象检测算法都使用区域来在图像中定位对象。它们拍摄的图像很有可能包含感兴趣的对象,并且看不到完整的图像。另一方面,YOLO拍摄图像并将其拆分为网格。然后,我们在每个网格单元上运行本地化算法,从而得出m个边界框。网络将为每个边界框输出类别概率,并为边界框输出偏移值。最后,算法选择类别概率高于阈值的边界框,然后在图像中定位对象。让我们看一下YOLO3的简单实现,以了解检测是如何发生的。
创建和加载模型
让我们从导入必要的库开始。我们将使用Keras进行实现。
import cv2
import numpy as np
由于我们正在使用预先训练的模型,因此我们需要下载某些权重和配置文件。在这里获取权重文件,并在这里配置文件。在MS COCO数据集上训练了指定的模型,该数据集可以识别80类对象。您可以从此处下载或复制coco.names。
一旦下载了所需的文件,我们就可以继续创建和加载模型。OpenCV提供了一个readNet函数,用于将深度学习网络加载到系统中。深度学习网络必须以受支持的格式之一表示,例如xml和prototxt。加载模型后,我们将为模型提供类以从coco.names文件中进行识别。然后,我们使用getLayerNames函数检索输出图层,并使用getUnConnectedOutLayers将它们存储为输出图层列表。
# Use readNet to load Yolo
net=cv2.dnn.readNet("./yolov3.weights", "./yolov3.cfg")
# Classes
Classes = []
with open("coco.names","r") as f:
classes = [line.strip() for line in f.readlines()]
# Define Output Layers
layer_names =net.getLayerNames()
output_layers = [layer_names[i[0]-1] for i in net.getUnconnectedOutLayers()]
预处理输入
现在我们可以通过模型传递图像,但是模型希望图像具有特定的形状。为了使图像的形状与模型输入的形状兼容,我们将使用blobFromImage。该函数将重塑图像并以适当的顺序重新排列图像的颜色通道。然后,我们将图像提供给模型以执行前向传递。前向通过输出检测列表。
#reshape, normalize and re-order colors of image using blob
blob = cv2.dnn.blobFromImage(img,1/255,(416,416),(0,0,0),True,crop=False)
# blob as input
net.setInput(blob)
#Object Detection
results = net.forward(outputlayers)
获取边界框
我们可以使用此输出结果来获取每个检测到的对象的边界框的坐标。如前所述,然后我们使用阈值来获取准确的边界框。
# Evaluate class ids, confidence score and bounding boxes for detected objects
class_ids=[]
confidences=[]
boxes=[]
confidence_threshold = 0.5
for result in results:
for detection in result:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence> confidence_threshold:
# Object Detected
center_x=int(detection[0]*width)
center_y=int(detection[1]*height)
w=int(detection[2]*width)
h=int(detection[3]*height)
# Boundry box Co-ordinates
x=int(center_x-w/2)
y=int(center_y-h/2)
boxes.append([x,y,w,h])
confidences.append(float(confidence))
class_ids.append(class_id)
现在我们有了边界框的坐标,我们可以继续将框绘制在检测到的对象上并标记它。但是我们事先不知道任何给定对象的出现次数。为了克服这个问题,我们将使用采用非最大抑制的NMSBoxes函数。之后,我们最终显示图像。
# Non-max Suppression
indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.4, 0.6)
# Draw final bounding boxes
font = cv2.FONT_HERSHEY_PLAIN
count = 0
for i in range(len(boxes)):
if i in indexes:
x,y,w,h = boxes[i]
label = str(classes[class_ids[i]])
color = COLORS[i]
cv2.rectangle(img, (x,y), (x+w, y+h), color, 2)
cv2.putText(img, label, (x, y-5), font, 1, color, 1)
最后一步是将所有片段放在一起,并显示带有检测到的物体的最终图像。
cv2.imshow("Detected_Images",img)
那很容易,对吧?但是,等等——我们可以修改相同的代码来计算图像中的人数吗?让我们来看看。
MS COCO数据集可以识别80种不同的类别,包括人员。通过计算“人物”类别的出现次数,我们可以轻松地计算图像中出现的人数。
if int(class_ids[i] == 0):
count +=1
上面的小修改返回了图像中存在的人数。前一张图片返回的人数是2,下一张图片的计数是18:
下一步是什么?
在本文中,我们学习了用于对象检测的YOLO的基本实现。我们看到,使用预先训练的模型可以节省时间和计算资源,这相当容易。在接下来的文章中,我们将看看我们是否能对队列长度检测视频供稿实显著YOLO。作为结尾文章,我们还将讨论要使用自定义训练模型而不是预先训练模型的场景。