yolov8多batch推理,nms后处理

0. 背景

在高速公路监控视频场景下,图像分辨率大都是1920 * 1080或者2560 * 1440,远处的物体(车辆和行人等)都比较小。考虑需要对图像进行拆分,然后把拆分后的数据统一送入模型中,推理的结果然后再做nms,恢复到原始图片数据中。
这个过程中牵涉到两个方面的内容,一个是多batch推理,一个是nms。

1. 多batch

例如使用1920*1080分辨率的图片数据,把该分辨率的数据分为4份。
如果只是平均分车4份,当一个物体(比如车辆)正好处于图片中心时,则可能被拆分到4个区域,这样当后续这4个区域分别得到检测框后,无法对同一个拆分的物体进行完美覆盖。所以在拆分图片是,可用适当的大一些。

1.1 平均拆分

在这里插入图片描述

1.2 建议拆分

在这里插入图片描述
例如:1920*1080分辨率的图片,进行拆分

	h1 = int(1080 * 7 / 16) h2 = int(1080 * 9 / 16) w1 = int(1920 * 7 / 16)w2 = int(1920 * 9 / 16)img0 = frame[0:h_2, 0:w_2].copy()img1 = frame[0:h_2, w_1:w].copy()img2 = frame[h_1:h, 0:w_2].copy()img3 = frame[h_1:h, w_1:w].copy()

2. yolov8中多batch推理的方式

因为刚使用yolov8不久,推理过程中的数据加载逻辑重新梳理了一下,做个记录

1)推理调用入口
results = model(source=frame, save=False, conf=conf, iou=nms, save_txt=False,show=False)
2)model定义
model = YOLO(MODEL)
3)YOLO类 (ultralytics/models/yolo/model.py)
from ultralytics import YOLO
class YOLO(Model):
4) Model基类
class Model(nn.Module):
(4.1)super().__init__()self._load(model, task)
(4.2)def __call__(self, source=None, stream=False, **kwargs):"""Calls the 'predict' function with given arguments to perform object detection."""return self.predict(source, stream, **kwargs)
(4.3)def predict(self, source=None, stream=False, predictor=None, **kwargs):if not self.predictor:self.predictor = (predictor or self._smart_load('predictor'))(overrides=args, _callbacks=self.callbacks)self.predictor.setup_model(model=self.model, verbose=is_cli)
(4.4)def _smart_load(self, key):"""Load model/trainer/validator/predictor."""try:return self.task_map[self.task][key] #detect\predictor# 根据(3)中YOLO类中的task_mape'detect': {'model': DetectionModel,'trainer': yolo.detect.DetectionTrainer,'validator': yolo.detect.DetectionValidator,'predictor': yolo.detect.DetectionPredictor, },	#	调用接口为yolo.detect.DetectionPredictor   
5)class DetectionPredictor(BasePredictor)
6)BasePredictor#因为DetectionPredictor中只有后处理的方式,数据预处理的内容在BasePredictor基类中
(6.1)def __call__(self, source=None, model=None, stream=False, *args, **kwargs):"""Performs inference on an image or stream."""self.stream = streamif stream:return self.stream_inference(source, model, *args, **kwargs)else:return list(self.stream_inference(source, model, *args, **kwargs))  # merge list of Result into one    
(6.2)def stream_inference(self, source=None, model=None, *args, **kwargs):# Setup source every time predict is calledself.setup_source(source if source is not None else self.args.source) 
(6.3)def setup_source(self, source):self.dataset = load_inference_source(source=source,imgsz=self.imgsz,vid_stride=self.args.vid_stride,buffer=self.args.stream_buffer)
7) 数据处理方式
from ultralytics.data import load_inference_source 
def load_inference_source(source=None, imgsz=640, vid_stride=1, buffer=False):    dataset = LoadPilAndNumpy(source, imgsz=imgsz)8)最终的数据处理
class LoadPilAndNumpy:def __init__(self, im0, imgsz=640):"""Initialize PIL and Numpy Dataloader."""if not isinstance(im0, list):im0 = [im0]self.paths = [getattr(im, 'filename', f'image{i}.jpg') for i, im in enumerate(im0)]self.im0 = [self._single_check(im) for im in im0]#print(self.im0)self.imgsz = imgszself.mode = 'image'# Generate fake pathsself.bs = len(self.im0)

从上面的数据执行逻辑可用看出,推理入口的数据格式可以是一个列表形式。
这个列表中包含拆分后多个区域的数据

# 例如上面拆分为了4个部分,img0, img1, img2, img3
imgs = []
imgs.append(img0)
imgs.append(img1)
imgs.append(img2)
imgs.append(img3)
resultss = model(source=imgs, save=False, conf=conf, iou=nms, save_txt=False, show=False)

最后的结果为4个batch图片数据对应的检测结果

ress0 = resultss[0].tojson()
datas0 = json.loads(ress0)ress1 = resultss[1].tojson()
datas1 = json.loads(ress1)ress2 = resultss[2].tojson()
datas2 = json.loads(ress2)ress3 = resultss[3].tojson()
datas3 = json.loads(ress3)

3. nms处理

因为4部分区域拆分时,是有重合的部分,故在4部分区域推理出结果后,还需要进行一次NMS。

# results:为4个拆分区域检测结果的合集,iou_thresh: iou计算的阈值
def nms(results, iou_thresh):grouped_results = {}# 把results的内容,根据cls的分类情况组合。因为最终做nms时需要区分是不是同一个类别for cls_boxes in results:x1, y1, x2, y2, score, cls = cls_boxesif cls not in grouped_results:grouped_results[cls] = []grouped_results[cls].append([x1, y1, x2, y2, score])#print(grouped_results)keep_boxes = []# 遍历所有的键值对for cls, boxes_l in grouped_results.items():boxes = np.array(boxes_l)# 每个 box 的坐标和置信度x1 = boxes[:, 0]y1 = boxes[:, 1]x2 = boxes[:, 2]y2 = boxes[:, 3]scores = boxes[:, 4]# 每个 box 的面积areas = (y2 - y1 + 1) * (x2 - x1 + 1)# keep_boxes 用于存放执行 NMS 后剩余的 boxes# 取出置信度从大到小排列的索引,其中 scores.argsort() 返回的是数组值从小到大的索引index = scores.argsort()[::-1]while len(index) > 0:# 取出置信度最大的 box,将其放入 keep 中,并判断其他 box 是否可以与之合并i = index[0]boxes_l[i].append(cls)keep_boxes.append(boxes_l[i])# np.maximum(arr:list, x:int)表示计算arr中每一个元素与常数 x 之间的最大值x1_overlap = np.maximum(x1[i], x1[index[1:]])y1_overlap = np.maximum(y1[i], y1[index[1:]])x2_overlap = np.minimum(x2[i], x2[index[1:]])y2_overlap = np.minimum(y2[i], y2[index[1:]])# 计算重叠部分的面积,若没有不重叠部分则面积为 0w = np.maximum(0, x2_overlap - x1_overlap + 1)h = np.maximum(0, y2_overlap - y1_overlap + 1)overlap_area = w * h# 计算 iou(交并比)ious = overlap_area / (areas[i] + areas[index[1:]] - overlap_area)# 因为在拆分时,某个物体可能只有很小一部分,这个小的区域的检测框与正常物体的检测框的# 交集占比小于iou_thresh,则这边小的区域就不会被去除。所以添加下面两个iou数值,用于# 判断某个重叠部分的区域是不是跟检测框的大小类似,从而去除该检测框的值。iou1 = overlap_area / areas[i]iou2 = overlap_area / areas[index[1:]]# 合并重叠度最大的 box,即只保留 iou < iou_thresh 的 box# 因为 np.where(ious <= iou_thresh) 的数据结构是 tuple 里面包含了一个 list,所以要用 [0] 取出 list# 添加条件2,3是为了解决,同一个物体被分隔后,目标框只是物体的小一部分,nms时遗漏condition1 = ious <= iou_threshcondition2 = iou1 < 0.8condition3 = iou2 < 0.8idx = np.where(condition1 & condition2 & condition3)[0]# 这里将需要idx + 1,由于index 是 ious 的索引,而 ious 是去除掉 index 的第一个元素对应的 box 得到的,# 所以 ious 的索引 +1 对应的 box 才是 index 相同索引对应的 index# 因为 len(ious)<=len(index),所以 len(index[idx + 1])<=len(index),所以 while 循环中 index 的元素数量越来越少index = index[idx + 1]return keep_boxes

4. 示例

nms前
在这里插入图片描述
nms处理后
在这里插入图片描述
nms前
在这里插入图片描述
nms处理后
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/521681.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

看完不会来揍我 | 生存分析详解 | 从基础概念到生存曲线绘制 | 代码注释 + 结果解读

大名鼎鼎的生存分析来咯&#xff01;今天我就不叭叭叭了&#xff0c;咱们直接开始冲&#xff01;&#xff08;字有点多&#xff0c;希望大家不要嫌弃&#xff01;&#xff09; 提前说一句&#xff0c;我们今天介绍的K-M曲线主要用于比较不同组别生存曲线之间的差异&#xff0c;…

IOS开发0基础入门UIkit-1cocoapod安装、更新和使用 , 安装中出现的错误及解决方案 M1或者M2安装cocoapods

cocoapod是ios开发时常用的包管理工具 1.M1或者是M2系统安装cocoapods先操作一下两个设置 1、打开访达->应用->实用工具->终端->右键点击终端->显示简介->勾选使用 Rosetta 打开&#xff0c;关闭终端&#xff0c;重新打开。 2、打开访达->应用->Xcod…

【elasticsearch】ES的JAVA工具类完整版(待完成...)

springboot 的 elasticsearch 版本: 7.15.2 前情提要: 1.首先要理解 elasticsearch 对于【数据类型】很严格,如果字段类型不规范,在 检索/排序/聚合 时候类型不正确就会出现报错或者查不到数据的问题。所以在一般String类型插入结构如下: 这样的结构,不仅可以支持分词查…

实现QT中qDebug()的日志重定向

背景&#xff1a; 在项目开发过程中&#xff0c;为了方便分析和排查问题&#xff0c;我们需要将原本输出到控制台的调试信息写入日志文件&#xff0c;进行持久化存储&#xff0c;还可以实现日志分级等。 日志输出格式&#xff1a; 我们需要的格式包括以下内容&#xff1a; 1.…

【Qt】四种绘图设备详细使用

绘图设备有4个: **绘图设备是指继承QPainterDevice的子类————**QPixmap QImage QPicture QBitmap(黑白图片) QBitmap——父类QPixmapQPixmap图片类&#xff0c;主要用来显示&#xff0c;它针对于显示器显示做了特殊优化&#xff0c;依赖于平台的&#xff0c;只能在主线程…

SPFA找负环

2024-01-31&#xff08;最短路径&#xff09;-CSDN博客 求负环的常用方法&#xff0c;基于spfa&#xff1a; 1.统计每个点入队的次数&#xff0c;如果有个点入队n次&#xff0c;则说明存在负环 2.统计当前每个点的最短路中包含的边数&#xff0c;如果某个点的最短路的所包含的边…

C++初阶 类(上)

目录 1. 什么是类 2. 如何定义出一个类 3. 类的访问限定符 4. 类的作用域 5. 类的实例化 6. 类的大小 7. this指针 1.this指针的引出 2. this指针的特性 8. 面试题 1. 什么是类 在C语言中&#xff0c;不同类型的数据集合体是结构体。为了方便管理结构体&#xff0c;我…

使用Python快速提取PPT中的文本内容

直接提取PPT中的文本内容可以方便我们进行进一步处理或分析&#xff0c;也可以直接用于其他文档的编撰。通过使用Python程序&#xff0c;我们可以快速批量提取PPT中的文本内容&#xff0c;从而实现高效的信息收集或对其中的数据进行分析。本文将介绍如何使用Python程序提取Powe…

智能泵站智能运维系统

在现代化城市建设和工农业发展中&#xff0c;泵站作为关键的水利设施&#xff0c;其运行效率和稳定性至关重要。然而&#xff0c;传统的泵站运维方式往往依赖于人工巡检和定期维护&#xff0c;这种方式不仅效率低下&#xff0c;而且难以应对突发状况。随着物联网技术的飞速发展…

在表格中循环插入表单

<template><div class"key">{{ruleForm.casesRange}}<el-form label-position"top" :model"ruleForm" refruleForm><el-form-item label"这个表格怎么写"><el-table :data"tableData" border>…

深圳恒峰智慧|便携式森林灭火泵:森林安全的守护者

在自然环境中&#xff0c;森林是生态系统的重要组成部分&#xff0c;它们为我们提供氧气、净化空气、保持水源和防止土壤侵蚀等重要功能。然而&#xff0c;一旦森林发生火灾&#xff0c;这些宝贵的生态资源将面临巨大的破坏。为了保护森林资源&#xff0c;我们需要一种高效、便…

2024腾讯云服务器优惠价格99元一年,多配置报价表

腾讯云服务器99元一年是真的吗&#xff1f;真的&#xff0c;99元优惠购买入口 txybk.com/go/99 折合每天8元1个月&#xff0c;腾讯云99元服务器配置为2核2G3M带宽&#xff0c;2024年99元服务器配置最新报价为61元一年&#xff0c;如下图&#xff1a; 腾讯云服务器99元一年 腾讯…