本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
今天聊聊YOLO
框架。
我的文章有个特点,不选自己想写的,专挑大家想看的。就算内容对我陌生,我也有自信先把它吃透,再让你听懂。
你看,有朋友就提议写写YOLO
。
也巧,去年我还用过YOLO v5
,今天再去官网一看,已经升到v8
了。而且它在淡化YOLO
版本,主打Ultralytics
平台。
我感觉这个平台,能很好地解决图像领域的大部分问题。
一、Ultralytics平台
YOLO
原本是一种公开的目标检测
算法。它的优势是速度快,准确率还高。这很气人。一般情况下,两者是不能兼得的。
它之所以这么强,从它的名字中就能找到答案。YOLO
的全称是You Only Look Once (你仅需看一遍)。关于它原理解读的介绍非常多,属于街边知识,我就不多说了。反正,你知道它很火就好了。基本上,从画面中找物体的技术方案,选YOLO
就对了。
今年Ultralytics
公司在YOLO
之前版本基础上提出了v8
版本。这个版本,更像是一个AI视觉处理平台,它不但可以做检测,还可以做分类、分割、跟踪,甚至姿态估计。
然而它的调用和二次开发,也很方便。这太气人了,它不但好用,而且易用。
二、操作和原理指南
Github地址:github.com/ultralytics
依旧提示大家去读ReadMe.md
文件。我所说的,皆源于此。
基础环境要求:
Python >= 3.8
PyTorch >= 1.7
执行pip install ultralytics
安装平台支持库,后面就可以操作了。
2.1 命令行操作
它有多易用呢?易用到你啥都不用动,安装完就能直接用。
找来一个图片,例如下面这图,我们命名叫bus.jpg
,放到主目录下。
然后你在终端运行如下命令:
ini
yolo predict model=yolov8n.pt source=bus.jpg
这表示使用yolo
的yolov8n.pt
模型对bus.jpg
这张图进行预测。它会自动去下载yolov8n.pt
模型文件。随后,控制台打印如下:
arduino
(yolo)C:\tfboy\yolo> yolo predict model=yolov8n.pt source=bus.jpg
......
image 1/1 C:\tfboy\yolo\bus.jpg: 640x480 4 persons, 1 bus, 1 stop sign, 94.7ms
Speed: 0.0ms preprocess, 94.7ms inference, 0.0ms postprocess per image at shape (1, 3, 640, 480)
Results saved to runs\detect\predict
最后一句说,结果保存到了runs\detect\predict
目录。我们打开一看,发现了一张bus.jpg
。
嗯,这就是目标检测的结果。看标记上,它识别出了person
人,bus
公交车,还有左上角的stop sign
停车路标。我们发现person
的识别,左右两边都是半个人,它也识别出来了。
最简单的用法就是这样,不用打开IDE(程序员写代码的程序)就能处理图片。
你甚至都不用准备图片,直接运行命令也行。因为我从源码文件engin\model.py
中的predict
函数中看到这么一段:
python
if source is None:
source = ROOT / 'assets' if is_git_dir() else 'https://ultralytics.com/images/bus.jpg'
LOGGER.warning(f"WARNING 'source' is missing. Using 'source={source}'.")
如果素材为空,它会使用一个预置的本地目录。如果这个坏了,它还会从网上下载一个图片,并日志提示你"没有图,我顶了一下"。
这绝对是业界良心!
但是,我们想要拿到具体的识别数据,或者还想尝试其他功能怎么办?
继续往下看。
2.2 用代码处理
代码调用也极其简单。
ini
# 从平台库导入YOLO类
from ultralytics import YOLO
# 从模型文件构建model
model = YOLO("xx.pt")
# 对某张图片进行预测
results = model("bus.jpg")
# 打印识别结果
print(results)
开头我们说过,YOLOv8
功能强大,除了目标检测,还支持分割、追踪啥的。我也说过,它很易用。
现在就体现出来了。它执行所有功能的代码都是同一套,想换功能更换xx.pt
模型文件就行。它的分析结果,也都存在同一个results
结构中。
2.2.1 模型文件的调用
它有哪些模型文件呢?
名称 | 模型文件 | 家族 |
---|---|---|
检测 | yolov8n.pt | 8n、8s、8m、8l、8x |
分割 | yolov8n-seg.pt | 8n、8s、8m、8l、8x |
分类 | yolov8n-cls.pt | 8n、8s、8m、8l、8x |
姿态 | yolov8n-pose.pt | 8n、8s、8m、8l、8x |
每一类模型,还搞出一个家族。这就好比是同一款衣服的不同尺码。尺码不同,受众也不同。我们拿检测来做个对比。
类型 | 准确度 | 耗时长 | 运算次数/秒 |
---|---|---|---|
YOLOv8n | 37.3 | 80.4 | 8.7 |
YOLOv8s | 44.9 | 128.4 | 28.6 |
YOLOv8m | 50.2 | 234.7 | 78.9 |
YOLOv8l | 52.9 | 375.2 | 165.2 |
YOLOv8x | 53.9 | 479.1 | 257.8 |
我们看到从8n
到8x
,虽然准确值提高了,但是时间也提高了6倍。
因为它的算法,让它在竞争者中显得又快又准。但是回到同一个算法内,其实还是有取舍的。
我们更换模型文件就可以体验不同的功能。下面是我用代码尝试了yolov8n-pose.pt
和yolov8n-cls.pt
这俩模型的效果。
姿态模型能检测人体的姿势动作。分割模型可以将识别到的物体从背景中切分出来。
2.2.2 返回结果的解读
我们使用它,肯定是想让它帮我们处理图像。像上面那样,它给原图画出一个框,没法融入到我们产品中。这像极了有钱人来你家大把炫富,然后说并不打算给你或者借你。
Ultralytics
绝对不会这么做。它有好多种方式将结果给你!
首先,它那个results = model("bus.jpg")
的results
就包含着处理结果的数据。
其次,这是一个临时内存数据,它怕你不打印就丢了。因此又提供了一个持久化的保存方法。
看下面这段代码:
ini
from ultralytics import YOLO
from PIL import Image
model = YOLO('yolov8n-seg.pt')
image = Image.open("bus.jpg")
results = model.predict(source=image, save=True, save_txt=True)
这是另一种调用方式,我们标记为code-666
。
与之前的区别是构建完model
之后,调用了predict
方法,参数里面有save_txt=True
这项。这表示把数据结果保存到txt
文本中。
其实我们也看到入参也有变化。先通过Image.open("bus.jpg")
把图片包装一下,然后通过source=image
传入。除此之外,数据源也支持文件夹或者摄像头。
ini
# 识别来自文件夹的图像
results = model.predict(source="test/pics", ......)
# 识别来自摄像头的图像
results = model.predict(source="0", ......)
不管哪种输入方式,这个results
都很关键。想了解它的结构,你有3种途径:
- 上网搜,找解析文章或者官方文档。
- 直接
print
一下,看看它输出什么。 - 查看源码,了解其属性构成。
作为老程序员一般会直接看源码。通过按着ctrl
键点击方法名,我定位到它是engine/results.py
下的Results
类。
瞬间,豁然开朗的感觉。我拣重要的,给大家解释下:
- boxes: 检测出来物体的矩形框,就是目标检测的框。
- masks: 检测出来的遮罩层,调用图像分割时,这项有数据。
- keypoints: 检测出来的关键点,人体姿势估计时,身体的点就是这项。
- names: 分类数据的名称,比如
{0: 人,1: 狗}
这类索引。
想看具体的数值,可以自己调用results[0].boxes
或者results[0].masks
打印一下。
2.2.3 简单的抠图示例
下面我们做一个抠图的示例。我们将bus.jpg
采用yolov8n-seg.pt
模型做一个物体分割。它会把图像中检测到的物体分割出来,并将结果保存到results
中(code-666
就是这项操作)。
然后,我们打印一下结果:
scss
import numpy as np
pixel_xy = results[0].masks.xy[1]
points = np.array(pixel_xy, np.int32)
print(points)
results
是一个支持批量图片的结果集,因为我们只有一张图像,所以取results[0]
。masks.xy
是一张图里所有物体掩膜的轮廓坐标,我们只取一个,取索引为1
的物体。
points
的打印值是:
css
array([[113, 398],
[111, 399],
[106, 399],
...
[150, 399],
[148, 398]])
都是xy坐标点。我们可以把它画出来。
ini
import cv2
input_image = cv2.imread('bus.jpg')
cv2.drawContours(input_image, [points], -1, (0, 255, 0), 2)
cv2.imwrite('output.jpg', input_image)
原来这是穿羊皮袄的帅哥。既然已经知道了他的位置,那么把圈里的像素提取出来,让他到济南的街头走一走,感受一下车让人,感受一下超然楼。
同样,你提取谁都可以,或者给整体换一个背景也行。因为物体我们都拿到了。
在开源技术的加持下,这对程序员来说很简单。但是很多软件还收费。因为它们收的是电脑的使用费。
不要高兴太早,这个模型是受限的。
YOLO
是一种算法,它就好比是一种思想,本身啥也干不了。
上面的那些落地的模型文件仅仅是一个示例,是YOLOv8
针对COCO
数据集(一个很好的计算机视觉数据集)训练生成的。
这个数据集中有很多类物体:
你可以通过results[0].names
直接打印出来。
也就是说,它只认识上面列举的80
种物体。你拿一个它没见过的,比如请找出图中的老济南把子肉,它肯定识别不出来。需要你去训练数据集之外目标。
接下来就讲讲训练的事情。
2.3 训练数据
训练数据的操作依然很友好。只需要准备好训练集,然后启动训练就可以了。
这次我打算训练目标检测,来检测游戏角色的血量条。就是下面这样:
这有什么用呢?起码可以让我知道哪里出现活物了。
我们准备一些图片素材。条件好就多准备,条件差就少准备,起码得200张吧。
准备好素材后,下面就开始标记。
2.3.1 标记数据
我们使用labelImg
进行标记。
首先pip3 install labelImg
安装它。然后,在终端输入labelImg
就能启动它。如果有问题,可以下载源码启动。
你可以把它当成一个正常的软件来用。
一张图一张图地标记。标记完了之后,会产生一些文件。
classes.txt
是总分类,一个分类占一行。另外,每张图片还会配一个同名的txt
文件。里面记录了有几个框,都是什么类型,在哪个位置。
以0 0.386605 0.149837 0.091455 0.063518
举例:
第1个数字0表示物体的类别标签。
第2个数字0.386605表示边界框左上角的X坐标相对于图像宽度的比例。
第3个数字0.149837表示边界框左上角的Y坐标相对于图像高度的比例。
第4个数字0.091455表示边界框的宽度相对于图像宽度的比例。
第5个数字0.063518表示边界框的高度相对于图像高度的比例。
它们用比例表示位置,这样可以实现10×10
像素和1000×1000
像素一样处理。
标记完成之后,我们需要把众多的.png
和.txt
按照固定的格式整理好。
css
datasets
|--game
|--images
|--train
|--xx1.png
|--val
|--xx2.png
|--labels
|--train
|--xx1.txt
|--val
|--xx2.txt
下一步就是训练。
2.3.2 训练数据
yolo
提供很多种标记和训练方式。我选择的是比较传统的一种。
数据准备好了,我们需要给数据做个索引,告诉框架在哪儿、有啥。新建一个game.yaml
,内容如下:
makefile
# 训练集、验证集位置
train: game/images/train/
val: game/images/val/
# 几个分类
nc: 1
# 分类名称
names: ['blood']
然后就能训练了。可以采用命令行模式:
ini
(yolo)C:......\yolo> yolo task=detect mode=train model=yolov8n.pt data=game.yaml epochs=300
也可以采用代码模式:
ini
from ultralytics import YOLO
model = YOLO('yolov8n.pt')
results = model.train(data="game.yaml", epochs=300)
都很简单。全都是在yolov8n.pt
基础上进行训练,只要你标记的好,结果肯定也是很快很准确。
训练结束以后,会在runs\detect\train
下生成训练结果,有很多东西。
args.yaml
是训练的参数。你填的那点参数根本不够,其他都是默认的,这里面有记录。如果你想改,可以训练时就指定。
我们最期待的还是weights
里面的俩文件,一个best.pt
是效果最好的权重,另一个是last.pt
是最后一个权重。最后一个不一定是最好的,但是训练一通,最后一个不留着,用户会觉得吃亏了。所以保留俩。
results.png
是训练历史记录。
我取一点给大家看,其实从50轮开始就平稳了。我训练了300轮有些多余。从这个图中也可以看出,最后一个不一定是准确率最高的。
下面试试效果吧。
2.3.3 推理预测
为了方便调用,我们把best.pt
复制到项目根目录下。用法跟上面一样,只不过模型换成best.pt
。
去识别一个文件夹下的图片:
ini
from ultralytics import YOLO
model = YOLO("best.pt")
results = model.predict(source="game/images/val", save=True)
结果如下:
图片有点小,我们来搞一个视频看看,这次用命令行:
ini
(yolo)C:......\yolo> yolo task=detect mode=predict model=best.pt source="game.mp4"
它会一帧一帧地处理:
最后,输出到runs\detect\predict2
,我们打开它看看效果:
其实,实时摄像头或者视频流也能做到,只不过就是换个source
来源的问题。
至于这个识别能做什么?肯定是有想象空间的,比如不遮挡弹幕、焦点跟踪等。
三、更广阔的应用
YOLOv8
支持其他平台格式的导出。也就是说它的产物,可以跨平台、跨终端。
torchscript
和tf.js
可以在浏览器上跑。tflite
可以在Android
和Ios
上运行。它甚至也能在飞桨平台运行。
代码就3行:
ini
from ultralytics import YOLO
model = YOLO('best.pt')
model.export(format='tfjs')
看完赶紧尝试,起飞吧,少年!
四、参考
GitHub github.com/ultralytics...
labelImg github.com/HumanSignal...
我是掘金@TF男孩,一个思路开阔的程序员。