在先前的RT-DETR
中,博主使用ONNX
模型文件进行了视频、图像的推理,在本章节,博主打算使用YOLOv8
模型进行推理,因此,我们除了需要获取YOLOv8
的ONNX
模型文件外,还需要进行一些额外的操作,如NMS
后处理过程,其详细实现过程如下:
YOLOv8模型导出
在YOLOv8
的官方项目中,新建export.py
,写入如下代码即可导出yolov8n,onnx
文件
Gradio推理UI设计
这里,我们先使用Gradio
进行推理界面的搭建,其输入与输出均为图像
YOLO目标检测推理
上面已经给出了YOLOv8
模型推理的函数,那么其具体是如何实现的呢?
上述函数的具体实现分别在YOLODet.py
与utils.py
文件中,具体实现过程如下:
YOLODet实例化
首先是YOLODet
对象的实例化,其通过__init__
初始化置信度等参数,并生成InferenceSession
的实例
初始化参数代码如下:
生成InferenceSession
的实例
读取模型的参数信息,YOLO
模型在训练时设置图像为640*640
,该信息作为参数保存在了ONNX
模型文件中,此时通过读取模型文件中的相关参数来为前处理任务设置参数
获取ONNX的输出值的名称
输出结果解析原理
这里的输出值为output0
,即只有一个值,我们可以通过下面代码来查看这个onnx
模型的输出结果具体是什么样子的
可以看到其名称即为output0
,其维度为3
维,即(1,7,8400)
这里为何是(7,8400)
呢,首先解释一下7的原因,前4
个元素是边界框的坐标(x, y, w, h)
。
剩下的3
个元素(car,truck,bus)
是类别得分。
博主也曾直接使用YOLOv8
训练好的pt
模型进行转换,得到的结果为(1,84,8400)
,其中84
便是4
个坐标加上COCO
数据集中的80个类别
那么,这个8400是如何来的呢,这是由于YOLO的三个不同尺度的检测头的原因:
(80×80+40×40+20×20)=(6400+1600+400)=8400
每个网格点产生一个预测结果,即有8400
个预测结果,同时,关于这一点我们可以通过后处理
过程来证实
注意,该后处理过程通过 np.amax
确定其所属类别,并将所有大于置信度的结果筛选出,但其数量依旧很多,因此需要进行非极大值抑制(NMS)
操作。
YOLO推理代码
推理的代码很简单,只要把图像输入加载好的模型中即可:
得到输出结果output0
后处理操作之结果解析
随后将输出结果进行后处理操作
上述的后处理过程实现较为繁杂,因此可以采用如下处理方式:
关于extract_boxes
函数,其作用为处理 bounding box
(预测框),主要分为两个过程,一个是将预测框结果恢复到与图像大小相匹配的大小(由于多尺度的关系,其输出的预测框的大小都是归一化后的)。
此外,还要将预测的(x,y,w,h)
转换为(x y x y)
形式
该两部分代码如下:恢复预测框大小
将(x,y,w,h)
转换为(x y x y)
后处理操作值NMS操作
经过上述类别置信度筛选后的结果还可以存在很多,为了让结果更加精确(为了防止一个目标有多个预测框),故进行非极大值抑制,即NMS
操作,其原理如下:
- 对于每个类别,按照预测框的置信度进行排序,将置信度最高的预测框作为基准。
- 从剩余的预测框中选择一个与基准框的重叠面积最大的框,如果其重叠面积大于一定的阈值,则将其删除。
- 对于剩余的预测框,重复步骤2,直到所有的重叠面积都小于阈值,或者没有被删除的框剩余为止
当然,由于YOLO
模型的性能较好,我们的置信度在设置为0.5
时,筛选后的结果便不多了,8400
个筛选完后仅还有21
个
代码如下,其过程便是按照每个类别计算保存下的预测框
具体的预测框计算使用如下nms
函数,这个是针对单个类别进行计算的
对于下面的代码
为何要加一呢,其实是由于前面计算IOU
时算的是与最大框的IOU
,经过keep_indices = np.where(ious < iou_threshold)[0]
筛选后得到小于阈值的(预测的不是同一个目标),但此时这个id
是去除了最大框的,要在sorted_indices
中选择就需要加一。
计算IOU的代码如下:
可视化操作
至此,便可以通过NMS找出最终的预测框了,随后便是在图像上标注出目标了:
最终的检测效果如下:
事实上,这套处理流程不仅可以应对YOLOv8的检测,博主还曾测试过YOLOv9的模型,依旧是可用的。
最终完整代码博主将在完成视频推理设计后公布在github
,尽情期待。