如何用python实现实时目标检测?
11 个回答
导读
各位小伙伴早上好,昨天为大家解读了一篇最新发表在 MICCAI 2023 的医学图像语义分割工作,深受大家的喜爱。有粉丝后台留言希望分享一下 ICCV 2023 上有关目标检测转项的文章,于是小编加班加点肝了今天的文章,希望能满足小伙伴的需求。此外,如果你有好的研究工作想 share 给大家,欢迎在文末添加奶盖微信一起交流学习!
今天给大家介绍的这个工作更多的关注
边缘设备
的高效
目标检测器
如何通过设计探索在能耗和性能间的权衡。该研究引入了一个新的目标检测器
FemtoDet
,此检测器使用了低能耗组件,同时提出了一种
卷积优化的新方法
和一种
优化训练策略
。
关于研究的主要内容,原文写的优点啰嗦,我们大致可以从以下几个方面进行解读:
低能源架构的发
文章首先分析了各种卷积神经网络,以找出低能耗的架构。这包括选择 激活函数 、 卷积运算符 和 特征融合 结构。作者认为这些在过去的工作中被忽视的细节严重影响了检测器的能耗。
FemtoDet 的构建
基于上述低能耗架构的发现,作者设计并提出了一种名为
FemtoDet
的新型目标检测器。同昨天上海交大提出的
50K
大小的
EGE-UNet
类似,
FemtoDet
也仅有
68.77k
的参数,但却能够在
PASCAL VOC
测试集上达到了
46.3
的 AP50 得分;与此同时,该方法在
Qualcomm Snapdragon 865 CPU
平台上的功耗仅为
1.11
瓦,运行帧率达到惊人的
64.47
FPS。只能说在大模型横行的今天,也算是给广大低端玩家一丝慰藉与希望了,换个思路我们还能比比看谁更小,哈哈~~~
卷积优化与训练策略
正如我们前面提到的,这里还提出了一种名为
实例边界增强
(
Instance Boundary Enhancement,IBE
)的模块,以优化卷积并解决 CNN 有限容量与检测任务在多样空间表示中的矛盾。此外,为了解决轻量级检测器由于数据增强产生的数据偏移问题,作者提出了一种
递归启动
(
Recursive Warm-restart,RecWR
)的训练策略。这两种方法都在 PASCAL VOC 数据集上进行了评估,
结果表明 IBE 可以在不增加额外参数的情况下提高 FemtoDet 约 7.72% 的性能,RecWR 则通过逐步减弱数据增强强度,在多阶段学习中可以提高 FemtoDet 约 6.19% 的性能。
应用场景
FemtoDet 特别适用于层次化的智能芯片,可以实现快速的响应和警报。例如,它在 TJU-DHD 数据集的行人检测中表现出 85.8 的 AR20 和 76.3 的 AP20,同时在中大型物体检测上表现出色。同时,FemtoDet 在 AP50或AP20 和中等难度的数据场景(例如VOC)上表现良好,均能在一定成都反映其应用能力。
目标检测
其实,不知道大家发现没,近几年这种纯检测的文章已经很少了,我们就趁这个契机一起回顾下以前的知识,今天大家粗略的过一遍。
众所周知,基于深度学习的目标检测发展历程主要涉及两大类:二阶段检测器和一阶段检测器。
二阶段检测器首先从图像中生成区域提议,然后从这些提议中生成最终的预测框。虽然二阶段检测器相较于一阶段检测器在准确性上更高,但在边缘设备上实现低延迟仍有困难,所以除了打比赛等特殊情况,基本全是单阶段目标检测器的身影,特别是涉及实际应用。
一阶段检测器又可分为基于锚点和无锚点的两种类型,这取决于是否在整个图像中注入
Anchor
先验以实现框回归。例如,
SSD
就是一种典型的基于锚点的一阶段检测器,它将边界框的输出空间离散化为一组在每个特征图位置具有不同宽高比和尺度的默认锚点。YOLO 系列也是一种典型的基于锚点的检测器,这一块相信大家比奶盖还熟悉。其次,无锚点检测器则旨在消除预定义的锚点框集,如
CornerNet
通过预测物体的两个关键点(通过边界框的左上角和右下角)来检测物体,
CenterNet
类似,而
FCOS
则通过提出一个全卷积的一阶段目标检测器来消除锚点设置。
尽管一阶段和二阶段的目标检测方法在许多具有挑战性的公共数据集,如 COCO 和 TJU-DHD 上取得了高性能,但对于部署在边缘设备的检测器来说,检测的延迟和功耗是关键问题。为了解决检测延迟的问题,许多研究致力于实现准确性和效率之间的平衡,如
FastYOLO
、
YOLObite
以及不得不提的
NanoDet
。然而,无论是精度优先还是延迟优先的检测器都没有考虑到它们的
能量消耗
。高能耗的检测器对于部署在边缘的设备来说是不友好的。因此,本文的目标是开发一种轻量级检测器,可以实现能量和性能之间的权衡。
FemtoDet
FemtoDet
的设计主要关注两个部分,即低能耗检测器的基准设定以及有关能耗方面的相关探索。
首先,作者为设计低能耗检测器提供了基准,探索了激活函数、卷积运算符和检测器的
Neck
结构。激活函数、卷积运算符和解码器是构成目标检测的三大基本元素,对模型的性能和能耗有重要影响。例如,常用的激活函数如
ReLU
、
GELU
、
Swish
和
SiLU
等由于参数和浮点运算量较少,被广泛应用于目标检测中。所以通过在同一架构中计算不同激活函数的能耗,可以研究哪种激活函数更适合设计能源导向的检测器。
其次,基于低能耗检测器的设计基准,作者提供了一种名为
FemtoDet
的面向能源的轻量级检测器。FemtoDet 通过两种设计进行优化:实例边界增强(IBE)模块用于改善 FemtoDet 中的深度可分离卷积(DSC),克服了轻量级模型的表示优化瓶颈;递归热重启(RecWR)训练策略则是一种多阶段的递归热重启学习过程,可以克服由 strong 的数据增强产生的数据转移。后面我们会为大家一一介绍。
此外,为了全面评估模型的能耗和它们实现能源与性能权衡的能力,除了常用的 Top1-Acc(用于图像分类)和 mAP(用于目标检测)度量之外,作者还提出了 Power(能源成本)和 mEPT(平均能源与性能权衡)这两种评价指标。
实例边界增强模块
IBE
模块这是一种专为优化轻量级检测器而设计的模块。由于轻量级模型的
表征能力
有限,它们学习的特征往往较为分散。IBE 模块的目的是改进 FemtoDet 中的深度可分离卷积(Depthwise Separable Convolutions,DSC),从而克服轻量级模型表征优化的瓶颈。
IBE 模块是基于因式分解为深度和点状层的卷积层设计的。它还引入了一个双重标准化机制。具体地,该模块通过设计新的 局部描述符 、 语义投影器 和 双重标准化层 来增强 DSC。特别是, 1 \times 1 的局部描述符是通过集成梯度提示周围的线性变换生成的参数重用机制。因此,对象边界信息可以在局部描述符中找到。然后,我们利用这些对象边界信息来增强上述标准操作(如深度卷积)的噪声特征表示。
最后,我们将局部描述符和深度卷积之间的特征添加的对象边界提示结合起来,引导模型学习实例的有效表示。细化的结果大家可以从上面的可视化结果图(c)中看到。
递归热重启训练策略
强数据增强(Strong Augmentation,SA)广泛用于目前检测,但是作者发现,当前的训练策略不能充分利用 SA 产生的多样性训练表示,以提高在真实验证数据上的泛化能力。
因此,本文提出了一个有效的训练策略,即递归热重启(RecWR)。整个训练过程可以分为四个阶段。从第一阶段到第四阶段,图像增强的强度逐渐降低。具体来说,在第一阶段的训练中,将组合一些 SA 类型,如
MixUp
,
Mosaic
和
RandomAffine
。从第二阶段开始,上述 SA 类型在每个训练阶段都会逐渐减弱,直到第四阶段。
此外,在开始每个训练阶段之前,等待训练的检测器会加载前一个训练阶段的训练权重作为初始化。实验发现,在使用 RecWR 训练 FemtoDet 后,MixUp 也能帮助这些极小的检测器获得更好的性能。换句话说,RecWR 利用了 SA 学习的多样性特征,使 FemtoDet 摆脱了次优化困境。
实验
激活函数
卷积描述符
Neck
精度
功耗
总结
本文提出了一个新颖的实例边界增强模块(IBE)和递归热重启训练策略(RecWR),以克服极度轻量级检测器的优化问题。 实验结果表明,尽管提高性能可能会导致能耗增长,但利用ReLU等简单组件构建的能源导向检测器可以显著降低这种影响。 在VOC,COCO和TJU-DHD数据集上,该方法在消耗最少能量的同时,实现了与当前最先进技术相媲美的性能。
写在最后
对目标检测及相关应用领域感兴趣的童鞋也欢迎扫描屏幕下方二维码或者直接搜索微信号 cv_huber 添加小编好友,备注:学校/公司-研究方向-昵称,与万千学者专家一起交流探讨前沿AI科技!
换 yolov8 模型,检测一张图片最多 4 ms(GPU)
性能表下
使用也很简单,没几行就搞定了。
# 导入 YOLO 模型
from ultralytics import YOLO
# 从头开始创建新的 YOLO 模型
model = YOLO('yolov8n.yaml')
# 加载预训练的 YOLO 模型(推荐用于训练)
model = YOLO('yolov8n.pt')
# 使用 'coco128.yaml' 数据集进行 3 个 epoch 的模型训练
results = model.train(data='coco128.yaml', epochs=3)
# 在验证集上评估模型的性能
results = model.val()
# 使用模型在图像上执行目标检测
results = model('https://ultralytics.com/images/bus.jpg')
# 将模型导出为 ONNX 格式
success = model.export(format='onnx')
实时目标检测通常会涉及到摄像头或者视频流的处理。以下是一般的流程:
1. 数据集准备:收集和标注图像,将它们分为训练集和测试集。
2. 搭建CNN模型:使用框架构建CNN模型,包括卷积层、池化层和全连接层。
3. 训练模型:使用训练集对CNN模型进行训练。
4. 实时目标检测:
- 读取视频流或摄像头数据;
- 对每一帧图像进行目标检测;
- 将检测到的目标用矩形框标出,并显示在图像上;
- 将处理后的图像展示出来或保存为视频流。
以下是一个简单的代码示例,使用TensorFlow和OpenCV实现实时目标检测:
```python
import cv2
import tensorflow as tf
# 加载训练好的模型
model = tf.keras.models.load_model('model.h5')
# 打开摄像头
cap = cv2.VideoCapture(0)
while True:
# 读取摄像头数据
ret, frame = cap.read()
# 对每一帧图像进行目标检测
resized_frame = cv2.resize(frame, (224, 224))
input_data = tf.expand_dims(resized_frame, axis=0)
predictions = model.predict(input_data)
# 假设模型只检测一种类别的物体,比如人
for i, prediction in enumerate(predictions[0]):
if prediction > 0.5:
print('Person detected!')
cv2.rectangle(frame, (0, 0), (frame.shape[1], frame.shape[0]), color=(0, 0, 255), thickness=2)
break
# 将检测到的目标用矩形框标出
cv2.imshow('frame', frame)
# 按q键退出循环
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放摄像头资源
cap.release()
cv2.destroyAllWindows()
```
该代码通过读取摄像头数据,对每一帧图像进行目标检测,将检测到的目标用矩形框标出,并显示在图像上。按下q键可以退出循环。
要实现实时的目标检测,并保持与摄像头帧率相匹配的结果帧率,可以采用多线程的方式来处理。以下是一种可能的实现方法:
- 创建两个线程:一个用于读取摄像头帧,另一个用于目标检测和展示结果。
-
在读取摄像头帧的线程中,使用OpenCV的
VideoCapture
函数打开摄像头,然后在一个循环中连续读取摄像头帧。根据摄像头的帧率,设置一个适当的延时(例如40ms)来控制帧的读取速度。 - 在目标检测和展示结果的线程中,从读取摄像头帧的线程中获取最新的帧。然后,对该帧进行目标检测,并将结果绘制在帧上。在展示结果之前,根据目标检测函数的运行时间(例如100ms),等待适当的时间来保持结果帧的延迟。
- 使用适当的线程同步机制,例如锁或队列,以确保读取摄像头帧和目标检测展示线程之间的数据交换和同步。
下面是一个简化的示例代码,演示了上述实现方法:
pythonCopy code
import cv2
import threading
import time
# 摄像头帧读取线程函数
def read_frames():
while True:
ret, frame = cap.read() # 读取摄像头帧
if not ret:
break
frame_queue.put(frame) # 将帧放入队列
# 目标检测和展示结果线程函数
def detect_and_show():
while True:
frame = frame_queue.get() # 从队列中获取最新的帧
start_time = time.time() # 记录开始时间
# 进行目标检测和绘制结果
# detect_objects(frame)
# draw_results(frame)
end_time = time.time() # 记录结束时间
elapsed_time = end_time - start_time # 计算目标检测的运行时间
cv2.imshow("Result", frame) # 展示结果帧
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 控制结果帧的延迟时间
if elapsed_time < 0.1: # 目标检测时间小于100ms,延时相应时间
time.sleep(0.1 - elapsed_time)
# 创建摄像头对象
cap = cv2.VideoCapture(0) # 0表示默认摄像头
frame_queue = queue.Queue(maxsize=1) # 创建帧队列,用于存储摄像头帧
# 创建并启动读取摄像头帧的线程
read_thread = threading.Thread(target=read_frames)
read_thread.start()