当前位置: 首页 > news >正文

【YOLOv8 追踪检测器】

YOLOV8追踪检测类

detector.py

# 导入必要的库importnumpyasnpfromultralyticsimportYOLO# 导入 ultralytics 库中的 YOLO 类,这是 YOLOv8 的官方高级 API# 定义 YOLOv8 检测器类classYOLO_Detetor:def__init__(self):# 初始化时加载 YOLOv8s 预训练模型权重# ultralytics 会自动处理模型结构的加载和权重的下载/缓存self.model=YOLO("yolov8s.pt")defget_result(self,frame):""" 对输入图像进行目标检测,并返回格式化的检测结果 :param frame: 输入图像 (numpy array, BGR格式) :return: 包含检测框、置信度、类别的字典列表,若无检测结果则返回 None """out_results=[]# 用于存储最终格式化后的检测结果# 执行模型预测# conf=0.4: 置信度阈值,低于此值的框会被过滤# iou=0.45: NMS(非极大值抑制)的 IoU 阈值,用于去除重叠的冗余框# verbose=False: 关闭预测过程中的控制台打印信息# [0]: predict 返回的是一个列表,这里取第一个(也是唯一一个)图像的结果result=self.model.predict(frame,conf=0.4,iou=0.45,verbose=False)[0]# 从结果对象中提取边界框、置信度和类别索引,并转移到 CPU 转为 numpy 数组# xyxy: 返回 [x1, y1, x2, y2] 格式的左上角和右下角绝对坐标bboxes=result.boxes.xyxy.cpu().numpy()confs=result.boxes.conf.cpu().numpy()# 每个框的置信度分数cls_idxs=result.boxes.cls.cpu().numpy()# 每个框对应的类别索引 (如 0 代表 person)# 遍历每一个检测到的目标框fori,bboxinenumerate(bboxes):# 将浮点型坐标转换为整数,方便后续绘图或计算x1,y1,x2,y2=map(int,bbox)# 获取当前框的置信度,保留两位小数conf=round(float(confs[i]),2)# 获取当前框的类别索引,转为整数cls_=int(cls_idxs[i])# 将当前目标的检测结果打包成字典,追加到结果列表中out_results.append({"bbox":[x1,y1,x2,y2],# 边界框坐标 [左上x, 左上y, 右下x, 右下y]"conf":conf,# 置信度"cls_":cls_# 类别索引})# 如果检测到了目标,返回结果列表;否则返回 Noneiflen(out_results)>0:returnout_resultselse:returnNone# 导入 OpenCV 用于图像读取importcv2# 主程序入口if__name__=="__main__":# 使用 OpenCV 读取待检测的图片 (traffic.png)imdata=cv2.imread("traffic.png")# 实例化 YOLOv8 检测器yolo_detetor=YOLO_Detetor()# 调用检测器获取结果result=yolo_detetor.get_result(imdata)# 打印检测结果到控制台print(result)

结果如图

iou_tracker.py

importnumpyasnpfromdetectorimportYOLO_Detecor# 导入之前封装的 YOLOv8 检测器importcv2classIOU_Tracker:def__init__(self):# 初始化 YOLOv8 检测器self.detector=YOLO_Detecor()# 全局跟踪 ID 计数器,每分配一个新目标就自增self.tracker_id=1# 存储上一帧的活跃轨迹,每个元素是一个字典,包含 tracker_id, bbox, conf, cls_self.old_tracker=[]def__iou(self,bbox1,bbox2):""" 计算两个边界框的交并比 (Intersection over Union) :param bbox1: [x1, y1, x2, y2] 格式 :param bbox2: [x1, y1, x2, y2] 格式 :return: IOU 值 (0~1) """# 计算两个框的面积b1_area=(bbox1[2]-bbox1[0])*(bbox1[3]-bbox1[1])b2_area=(bbox2[2]-bbox2[0])*(bbox2[3]-bbox2[1])# 计算交集区域的左上角和右下角坐标l_x=np.maximum(bbox1[0],bbox2[0])l_y=np.maximum(bbox1[1],bbox2[1])r_x=np.minimum(bbox1[2],bbox2[2])r_y=np.minimum(bbox1[3],bbox2[3])# 计算交集的宽和高,如果无交集则为 0w=np.maximum(0,r_x-l_x)h=np.maximum(0,r_y-l_y)inter=w*h# 返回交并比,分母为并集面积returninter/(b1_area+b2_area-inter)defgenerate_tracker(self,frame_data):""" 核心跟踪逻辑:将当前帧的检测结果与历史轨迹进行匹配 """# 1. 获取当前帧的检测结果 (列表,若未检测到则为 None)new_det=self.detector.get_result(frame_data)# 【关键修复】如果当前帧没有检测到任何目标,直接清空历史轨迹并返回ifnew_detisNone:self.old_tracker=[]self.__vis(frame_data)returnkeep=[]# 存储当前帧匹配成功的轨迹# 2. 遍历历史轨迹,尝试在当前帧的检测结果中寻找匹配foroldinself.old_tracker:max_iou=-1best_det=None# 贪心匹配:找到与当前历史轨迹 IOU 最大的检测框fornewinnew_det:iou=self.__iou(old["bbox"],new["bbox"])ifiou>max_iou:max_iou=iou best_det=new# 如果找到了匹配且 IOU 大于阈值 (0.5),则认为是同一个目标ifbest_detandmax_iou>0.5:# 保留原有的 tracker_id,更新为当前帧的坐标和置信度old["bbox"]=best_det["bbox"]old["conf"]=best_det["conf"]keep.append(old)# 从当前检测结果列表中移除已匹配的框,防止重复匹配new_det.remove(best_det)# 3. 处理剩余的未匹配检测框,将它们作为新目标初始化fordetinnew_det:save_det={"tracker_id":self.tracker_id,"bbox":det["bbox"],"conf":det["conf"],"cls_":det["cls_"],}keep.append(save_det)self.tracker_id+=1# 新目标分配新的 ID# 4. 更新历史轨迹为当前帧的活跃轨迹self.old_tracker=keep# 5. 在当前帧上绘制跟踪结果self.__vis(frame_data)def__vis(self,frame_):""" 可视化函数:在图像上绘制边界框和跟踪 ID """fordetinself.old_tracker:x1,y1,x2,y2=det["bbox"]tr_id=det["tracker_id"]# 绘制绿色边界框cv2.rectangle(frame_,(x1,y1),(x2,y2),(0,255,0),1)# 在框上方绘制跟踪 IDcv2.putText(frame_,f"{tr_id}",(x1,y1-10),cv2.FONT_HERSHEY_COMPLEX,1,(0,255,255),1,)defrun(self,video_path):""" 运行视频跟踪主循环 """cap=cv2.VideoCapture(video_path)whileTrue:ret,frame=cap.read()ifnotret:# 视频读取完毕breakself.generate_tracker(frame)cv2.imshow("img",frame)# 等待 10ms,按 'q' 键可提前退出 (优化建议)ifcv2.waitKey(10)&0xFF==ord('q'):breakcap.release()cv2.destroyAllWindows()if__name__=="__main__":video_path="a.mp4"iou_tracker=IOU_Tracker()iou_tracker.run(video_path)

效果图

添加实时统计功能

importnumpyasnpfromdetectorimportYOLO_Detecor# 导入自定义的 YOLO 检测器importcv2# 定义需要追踪的 COCO 数据集类别名称映射COCO_NAMES={0:"person",1:"bicycle",2:"car",3:"motorcycle",5:"bus",7:"truck",}# 实时统计与追踪类classIOU_Tracker:def__init__(self):# 初始化 YOLO 检测器self.detector=YOLO_Detecor()# 存储上一帧的追踪结果(历史轨迹)self.old_tracker=[]# 追踪 ID 计数器,每出现一个新目标就自增self.tracker_id=1# 初始化各类别的计数统计字典self.cls_cout={"person":0,"bicycle":0,"car":0,"motorcycle":0,"bus":0,"truck":0,}def__iou(self,bbox1,bbox2):""" 计算两个边界框(bbox)的 IOU(交并比) 参数格式均为 [x1, y1, x2, y2](左上角和右下角坐标) 返回值:交集面积 / 并集面积 """# 计算两个框的面积b1_area=(bbox1[2]-bbox1[0])*(bbox1[3]-bbox1[1])b2_area=(bbox2[2]-bbox2[0])*(bbox2[3]-bbox2[1])# 计算交集区域的左上角和右下角坐标l_x=np.maximum(bbox1[0],bbox2[0])l_y=np.maximum(bbox1[1],bbox2[1])r_x=np.minimum(bbox1[2],bbox2[2])r_y=np.minimum(bbox1[3],bbox2[3])# 计算交集的宽和高(如果为负数则取0,表示不相交)w=np.maximum(0,r_x-l_x)h=np.maximum(0,r_y-l_y)inter=w*h# 交集面积# 返回 IOU 值returninter/(b1_area+b2_area-inter)defgenerate_tracker(self,frame_data):""" 核心追踪逻辑:将当前帧的检测结果与历史轨迹进行匹配 """# 1. 获取当前帧的检测结果new_det=self.detector.get_result(frame_data)# 如果当前帧没有检测到目标,置为空列表new_det=new_detifnew_detisnotNoneelse[]# 用于存储当前帧匹配成功或新生成的轨迹keep=[]# --- 第一步:匹配历史轨迹 ---foroldinself.old_tracker:max_iou=-1# 记录当前历史目标与所有新检测框的最大 IOUbest_det=None# 记录最大 IOU 对应的新检测框# 遍历当前帧的所有新检测框fornewinnew_det:iou=self.__iou(old["bbox"],new["bbox"])# 计算 IOUifiou>max_iou:max_iou=iou best_det=new# 如果最大 IOU 大于阈值(0.5),说明是同一个目标ifbest_detandmax_iou>0.5:# 保留原有的 track_id,仅更新其坐标为最新检测框的坐标old["bbox"]=best_det["bbox"]keep.append(old)# 从新检测列表中移除已匹配的目标,避免重复分配new_det.remove(best_det)# --- 第二步:处理剩余的新检测框(即新出现的目标) ---fordetinnew_det:# 为新目标分配新的 tracker_idsave_det={"tracker_id":self.tracker_id,"bbox":det["bbox"],"conf":det["conf"],"cls_":det["cls_"],}keep.append(save_det)# 统计新出现的目标类别数量cls_name=COCO_NAMES.get(det["cls_"],0)ifcls_name==0:# 如果是 person(索引为0),跳过计数continueself.cls_cout[f"{cls_name}"]+=1# 对应类别计数 +1self.tracker_id+=1# 全局追踪 ID 自增# 更新历史轨迹为当前帧的最终结果self.old_tracker=keep# 在当前帧画面上绘制追踪结果和统计信息self._vis(frame_data)def_vis(self,frame_):""" 可视化函数:在画面上绘制追踪框、ID 以及类别统计信息 """# 绘制每个追踪目标的框和 IDfordetinself.old_tracker:x1,y1,x2,y2=det["bbox"]tr_id=det["tracker_id"]# 画红色矩形框cv2.rectangle(frame_,(x1,y1),(x2,y2),(0,0,255),1)# 在框上方显示追踪 IDcv2.putText(frame_,f"{tr_id}",(x1,y1-10),cv2.FONT_HERSHEY_COMPLEX,1,(0,255,255),1,)# 在画面左上角显示各类别的累计计数y=22# 初始文本纵坐标forkey,valueinself.cls_cout.items():key_name=key values_=value cv2.putText(frame_,f"{key_name}:{values_}",(10,y),cv2.FONT_HERSHEY_COMPLEX,1,(0,0,255),1,)y+=22# 每显示一行,纵坐标下移defrun(self,video_path):""" 主运行函数:读取视频流并逐帧进行追踪 """cap=cv2.VideoCapture(video_path)whileTrue:ret,frame=cap.read()ifnotret:# 视频读取结束break# 对当前帧执行追踪逻辑self.generate_tracker(frame)# 显示画面,等待 10mscv2.imshow("img",frame)cv2.waitKey(10)# 释放视频捕获对象并关闭所有窗口cap.release()cv2.destroyAllWindows()# 程序入口if__name__=="__main__":video_path="b.mp4"# 指定视频文件路径iou_tracker=IOU_Tracker()# 创建追踪器实例iou_tracker.run(video_path)# 开始运行

结果如图

yolov8追踪推理并计数

if__name__=="__main__":""" 追踪不需要训练 直接调用的模型 """importcv2fromultralyticsimportYOLO COCO_NAMES={0:"person",1:"bicycle",2:"car",3:"motorcycle",5:"bus",7:"truck",}model=YOLO(r"yolov8s.pt")cap=cv2.VideoCapture("a.mp4")# 2. 初始化全局计数器( {类别ID: set([track_id_1, track_id_2, ...])})tracked_ids={}whileTrue:ret,frame=cap.read()ifnotret:breakresults=model.track(frame,persist=True,# 跨帧保持追踪 ID 不重置verbose=False,tracker="bytetrack.yaml",# 选择追踪器配置# tracker="botsort.yaml",conf=0.5,# 检测置信度阈值iou=0.7,# NMS 的 IoU 阈值)# 提取当前帧的 Track ID 并更新全局计数ifresults[0].boxes.idisnotNone:# 获取当前帧所有目标的 track_id和 类别ID (转为 int 列表)current_ids=results[0].boxes.id.int().tolist()current_cls=results[0].boxes.cls.int().tolist()# 遍历当前帧检测到的每一个目标forcls_id,track_idinzip(current_cls,current_ids):# 如果该类别第一次出现,先初始化一个空集合ifcls_idnotintracked_ids:tracked_ids[cls_id]=set()# 将当前的 track_id 加入对应类别的集合中(自动去重)tracked_ids[cls_id].add(track_id)# 绘制追踪框和轨迹annotated=results[0].plot()# 在左上角绘制分类别实时计数结果y_offset=40# 初始文本纵坐标forcls_id,ids_setintracked_ids.items():# 获取类别名称,如果不在 COCO_NAMES 中则显示未知cls_name=COCO_NAMES.get(cls_id,"unknown")count=len(ids_set)# 集合的长度即为该类别的唯一目标数cv2.putText(annotated,f"{cls_name}:{count}",(10,y_offset),# 左上角坐标 (x, y)cv2.FONT_HERSHEY_SIMPLEX,1.0,# 字体大小(0,255,0),# 绿色字体1,# 字体粗细))y_offset+=22# 每显示一行,纵坐标下移 35 像素cv2.imshow("YOLOv8 Track",annotated)ifcv2.waitKey(10)&0xFF==ord("q"):# 按 'q' 键可提前退出breakcap.release()cv2.destroyAllWindows()

结果如图

http://www.jsqmd.com/news/1397323/

相关文章:

  • LazyVim中配置C/C++自动格式化:clang-format与none-ls实战指南
  • 【leetcode复健-12】56. 合并区间-排序
  • 在线Windows鼠标主题转换器:ANI到XCUR格式转换原理与实现
  • 数学建模期末复习:攻克多选题的策略与核心考点解析
  • 把碧蓝航线的每日重复劳动交给托管脚本,一个月能省下多少时间
  • 神经网络激活函数全解析:从ReLU到GELU,如何选择与优化
  • Lenovo Legion Toolkit上手路线图:拯救者性能优化与硬件调校完整教程
  • Next.js 全栈实战:拆解 RealWorld 项目——React SSR 入门
  • 2026-2027大模型五大技术趋势:智能体、推理优化与垂域模型
  • C++ STL六大组件深度解析:从容器算法到内存管理实战指南
  • 因为漏掉了一个错误导致浪费了6个小时
  • 奇点大会的可持续AI议题,技术团队能做什么
  • 宜宾装修公司怎么选?宜宾老胡装饰等本土装企综合参考与家装避坑指南 - 收录优先
  • 2026 年一禾装饰|淮北装修工作室怎么选,家装挑选要点解析 - 收录优先
  • SAP Gateway 的 Payload Trace 使用实战
  • B站视频下载完整指南:DownKyi免费开源工具,一键搞定批量下载与格式转换
  • 2026 年莱山装修公司怎么选?智云装饰聊聊本地家装避坑要点 - 收录优先
  • 苏州特证黄金回收优势,资质齐全交易可溯源维权 - 资讯早知道
  • macOS开发Python程序打包成Windows可执行文件全攻略
  • 手机取消内存卡背后的技术演进与商业逻辑
  • 手机都在自动工作-----真无聊
  • 把 OData 测试变成可重复回归测试,深入理解 SAP Gateway Client 的 Response Validation
  • NCM转MP3还要在线传文件?这款免费离线工具三步搞定批量转换
  • 告别后台偷跑:用LenovoLegionToolkit一键关闭Vantage与Legion Zone冗余进程
  • 杭州7城区黄金回收门店汇总,多家连锁实体比价,处置闲置黄金不踩坑 - 一刻涨新知
  • AI实战 : Numpy图像处理与深度学习框架
  • 【HTB-CPTS】第22章 Command Injections(测验部分)
  • Agent意图识别分层架构:从规则到大模型的完整设计与落地
  • 暑假一周总结:其六
  • 楼家泰防水分享:南宁多雨环境下防水修缮注意事项 - 收录优先