一篇文章简单介绍YOLO v1到v8的演变

本文探讨了YOLO目标检测库从V1到V8的发展历程,包括模型的改进、OpenCV对Darknet的支持变化,以及代码从低级到高级的简化过程,展示了数据科学工具在实时检测领域的演变。
摘要由CSDN通过智能技术生成

大家好,YOLO(You Only Look Once)是一种流行的目标检测库,它的第一个版本在2015年发布。YOLO工作速度很快,提供了良好的结果,而且预训练模型是公开可用的。该模型迅速变得流行,该项目至今仍在积极改进,这使我们有机会看到数据科学工具和库如何在多年间演变。本文将测试不同版本的YOLO,从最初的V1到最新的V8。

为了进行进一步的测试,将使用这张图片:

YOLO V1到V3

关于YOLO的第一篇论文,“You Only Look Once: Unified, Real-Time Object Detection”,于2015年发布。YOLO v1仍然可以下载,正如原始论文的作者之一Redmon所写的,他保留了这个版本“出于历史目的”。该模型以两个文件的形式分发,配置文件“yolo.cfg”包含有关神经网络模型的详细信息:

[net]batch=1height=448width=448channels=3momentum=0.9decay=0.0005...
[convolutional]batch_normalize=1filters=64size=7stride=2pad=1activation=leaky

第二个文件“yolov1.weights”,顾名思义,包含了预训练模型的权重。

这种格式不是来自PyTorch或Keras,该模型是使用Darknet创建的,Darknet是一种用C编写的开源神经网络框架。这个项目仍然可以在GitHub上找到,但它看起来已经被抛弃。在撰写本文时,有164个拉取请求和1794个未解决的问题,最后一次提交是在2018年,之后只有README.md文件有所更改。

原始的Darknet项目被抛弃了,不过readNetFromDarknet方法仍然在OpenCV中可用,甚至在最新的OpenCV版本中也存在。因此可以尝试使用Python环境加载原始的YOLO v1模型:

import cv2
model = cv2.dnn.readNetFromDarknet("yolo.cfg", "yolov1.weights")

运行得到如下错误:

darknet_io.cpp:902: error: 
(-212:Parsing error) Unknown layer type: local in function 'ReadDarknetFromCfgStream'

原来“yolo.cfg”中有一个名为“local”的层,这是OpenCV不支持的。YOLO v2配置中不再有这个层,可以成功在OpenCV中加载该模型:

import cv2
model = cv2.dnn.readNetFromDarknet("yolov2.cfg", "yolov2.weights")

使用模型并不像期望的那样简单,首先需要找到模型的输出层:

ln = model.getLayerNames()
output_layers = [ln[i - 1] for i in model.getUnconnectedOutLayers()]

然后需要加载图像并将其转换为模型能够理解的二进制格式:​​​​​​​

img = cv2.imread('test.jpg')
H, W = img.shape[:2]

blob = cv2.dnn.blobFromImage(img, 1/255.0, (608, 608), swapRB=True, crop=False)

最后运行正向传播,使用“forward”方法将运行计算并返回所请求的层输出:​​​​​​​

model.setInput(blob)
outputs = model.forward(output_layers)

执行正向传播很简单,但解析输出可能有点棘手。该模型产生85维特征向量作为输出,其中前4个数字表示对象矩形,第5个数字是对象存在的概率,最后80个数字包含模型训练的80个类别的概率信息。有了这些信息,可以在原始图像上绘制标签:

threshold = 0.5
boxes, confidences, class_ids = [], [], []

# Get all boxes and labels
for output in outputs:
    for detection in output:
        scores = detection[5:]
        class_id = np.argmax(scores)
        confidence = scores[class_id]
        if confidence > threshold:
            center_x, center_y = int(detection[0] * W), int(detection[1] * H)
            width, height = int(detection[2] * W), int(detection[3] * H)
            left = center_x - width//2
            top = center_y - height//2
            boxes.append([left, top, width, height])
            class_ids.append(class_id)
            confidences.append(float(confidence))

# Combine boxes together using non-maximum suppression
indices = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)

# All COCO classes
classes = "person;bicycle;car;motorbike;aeroplane;bus;train;truck;boat;traffic light;fire hydrant;stop sign;parking meter;bench;bird;" \
          "cat;dog;horse;sheep;cow;elephant;bear;zebra;giraffe;backpack;umbrella;handbag;tie;suitcase;frisbee;skis;snowboard;sports ball;kite;" \
          "baseball bat;baseball glove;skateboard;surfboard;tennis racket;bottle;wine glass;cup;fork;knife;spoon;bowl;banana;apple;sandwich;" \
          "orange;broccoli;carrot;hot dog;pizza;donut;cake;chair;sofa;pottedplant;bed;diningtable;toilet;tvmonitor;laptop;mouse;remote;keyboard;" \
          "cell phone;microwave;oven;toaster;sink;refrigerator;book;clock;vase;scissors;teddy bear;hair dryer;toothbrush".split(";")

# Draw rectangles on image
colors = np.random.randint(0, 255, size=(len(classes), 3), dtype='uint8')
for i in indices.flatten():
    x, y, w, h = boxes[i]
    color = [int(c) for c in colors[class_ids[i]]]
    cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
    text = f"{classes[class_ids[i]]}: {confidences[i]:.2f}"
    cv2.putText(img, text, (x + 2, y - 6), cv2.FONT_HERSHEY_COMPLEX, 0.5, color, 1)

# Show
cv2.imshow('window', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

这里使用`np.argmax`来找到具有最大概率的类别ID。YOLO模型是使用COCO(Common Objects in Context,知识共享署名4.0许可)数据集进行训练的,为了简化起见,直接将所有80个标签名称放入了代码中,还使用了OpenCV的`NMSBoxes`方法将嵌套的矩形组合在一起。最终结果如下:

 下一个版本YOLO v3,在两年后的2018年发布,也可以使用相同的代码运行它(权重和配置文件可以在线获取)。正如作者在论文中所写的,新模型更精确,可以轻松验证这一点:

YOLO V5到V7

使用`readNetFromDarknet`方法加载的模型有效,但所需的代码相当“低级”且繁琐。OpenCV的开发者决定让生活变得更轻松,在2019年的4.1.2版本中添加了一个新的`DetectionModel`类。可以通过以下方式加载YOLO模型,总体逻辑仍然相同,但所需的代码量要小得多。该模型直接在一个方法调用中返回类别ID、置信度值和矩形框:

import cv2

model = cv2.dnn_DetectionModel("yolov7.cfg", "yolov7.weights")
model.setInputParams(size=(640, 640), scale=1/255, mean=(127.5, 127.5, 127.5), swapRB=True)

class_ids, confidences, boxes = model.detect(img, confThreshold=0.5)

# Combine boxes together using non-maximum suppression
indices = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)

# All COCO classes
classes = "person;bicycle;car;motorbike;aeroplane;bus;train;truck;boat;traffic light;fire hydrant;stop sign;parking meter;bench;bird;" \
          "cat;dog;horse;sheep;cow;elephant;bear;zebra;giraffe;backpack;umbrella;handbag;tie;suitcase;frisbee;skis;snowboard;sports ball;kite;" \
          "baseball bat;baseball glove;skateboard;surfboard;tennis racket;bottle;wine glass;cup;fork;knife;spoon;bowl;banana;apple;sandwich;" \
          "orange;broccoli;carrot;hot dog;pizza;donut;cake;chair;sofa;pottedplant;bed;diningtable;toilet;tvmonitor;laptop;mouse;remote;keyboard;" \
          "cell phone;microwave;oven;toaster;sink;refrigerator;book;clock;vase;scissors;teddy bear;hair dryer;toothbrush".split(";")

# Draw rectangles on image
colors = np.random.randint(0, 255, size=(len(classes), 3), dtype='uint8')
for i in indices.flatten():
    x, y, w, h = boxes[i]
    color = [int(c) for c in colors[class_ids[i]]]
    cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
    text = f"{classes[class_ids[i]]}: {confidences[i]:.2f}"
    cv2.putText(img, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1)

# Show
cv2.imshow('window', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

正如所看到的,不再需要从模型输出中提取框和置信度值的所有低级代码。运行YOLO v7的结果,总体上是相同的:

图片

YOLO V8

第8版于2023年发布,为了比较结果,看一下现在运行YOLO所需的代码:

from ultralytics import YOLO
import supervision as sv


model = YOLO('yolov8m.pt')
results = model.predict(source=img, save=False, save_txt=False, verbose=False)
detections = sv.Detections.from_yolov8(results[0])

# Create list of labels
labels = []
for ind, class_id in enumerate(detections.class_id):
    labels.append(f"{model.model.names[class_id]}: {detections.confidence[ind]:.2f}")

# Draw rectangles on image
box_annotator = sv.BoxAnnotator(thickness=2, text_thickness=1, text_scale=0.4)
box_annotator.annotate(scene=img, detections=detections, labels=labels)

# Show
cv2.imshow('window', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

正如所看到的,代码变得更加紧凑,不需要关心数据集标签名称(模型提供了“names”属性)或如何在图像上绘制矩形和标签,结果非常准确:

本文测试了从2016年到2023年制作的几乎所有YOLO模型,多年来流行的数据科学工具和库演变是很有趣的,从低级代码到高级方法的趋势,这些方法可以做任何事情。OpenCV“本地”能够运行深度学习模型是很重要的,这使得可以在不仅在PyTorch或Keras等大型框架中使用神经网络模型,而且还可以在纯Python甚至是C ++应用程序中使用。并非每个应用程序都在拥有几乎无限资源的云中运行,物联网市场正在增长,这对在低功耗设备上运行神经网络(如机器人、监控摄像头或智能门铃)尤其重要。

  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

python慕遥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值