【Yolov5+Deepsort】训练自己的数据集(3)| 目标检测追踪 | 轨迹绘制 | 报错分析解决

📢前言:本篇是关于如何使用YoloV5+Deepsort训练自己的数据集,从而实现目标检测与目标追踪,并绘制出物体的运动轨迹。本章讲解的为第三部分内容:数据集的制作、Deepsort模型的训练以及动物运动轨迹的绘制。本文中用到的数据集均为自采,实验动物为斑马鱼。

💻环境&配置:RTX 3060、CUDA Version: 11.1、torch_version:1.9.1+cu111、python:3.8

 💬源码如下:

GitHub - mikel-brostrom/yolo_tracking: A collection of SOTA real-time, multi-object tracking algorithms for object detectors

GitHub - Sharpiless/Yolov5-Deepsort: 最新版本yolov5+deepsort目标检测和追踪,能够显示目标类别,支持5.0版本可训练自己数据集

如果想进一步了解Yolov5+Deepsort中的算法,猛戳这里:

【Yolov5+Deepsort】训练自己的数据集(1)| 目标检测&追踪 | 轨迹绘制

如果想要实现训练集的采集与划分,Yolov5模型的训练,猛戳这里:

 Ⅰ Deepsort模型训练

0x00 数据集准备

Deepsort所需要的的数据集与前面Yolov5目标检测的有所不同。

这里需要借助labelimg工具手动做出标定生成xml文件,再撰写脚本把图像中的检测目标扣出来,作为我们的数据集。

import cv2
import xml.etree.ElementTree as ET
import numpy as npimport xml.dom.minidom
import os
import argparsedef main():# JPG文件的地址img_path = 'path'# XML文件的地址anno_path = 'path'# 存结果的文件夹cut_path = '/home/zqy/Desktop/yolov5-master/nxm_data/crops/'if not os.path.exists(cut_path):os.makedirs(cut_path)# 获取文件夹中的文件imagelist = os.listdir(img_path)# print(imagelistfor image in imagelist:image_pre, ext = os.path.splitext(image)img_file = img_path + imageimg = cv2.imread(img_file)xml_file = anno_path + image_pre + '.xml'# DOMTree = xml.dom.minidom.parse(xml_file)# collection = DOMTree.documentElement# objects = collection.getElementsByTagName("object")tree = ET.parse(xml_file)root = tree.getroot()# if root.find('object') == None:#     returnobj_i = 0for obj in root.iter('object'):obj_i += 1print(obj_i)cls = obj.find('name').textxmlbox = obj.find('bndbox')b = [int(float(xmlbox.find('xmin').text)), int(float(xmlbox.find('ymin').text)),int(float(xmlbox.find('xmax').text)),int(float(xmlbox.find('ymax').text))]img_cut = img[b[1]:b[3], b[0]:b[2], :]path = os.path.join(cut_path, cls)# 目录是否存在,不存在则创建mkdirlambda = lambda x: os.makedirs(x) if not os.path.exists(x) else Truemkdirlambda(path)try:cv2.imwrite(os.path.join(cut_path, cls, '{}_{:0>2d}.jpg'.format(image_pre, obj_i)), img_cut)except:continueprint("&&&&")if __name__ == '__main__':main()

得到完整的数据集后,我们对数据集进行划分 :

import os
from PIL import Image
from shutil import copyfile, copytree, rmtree, movePATH_DATASET = 'path'  # 需要处理的文件夹
PATH_NEW_DATASET = 'path'  # 处理后的文件夹
PATH_ALL_IMAGES = PATH_NEW_DATASET + '/all_images'
PATH_TRAIN = PATH_NEW_DATASET + '/train'
PATH_TEST = PATH_NEW_DATASET + '/test'# 定义创建目录函数
def mymkdir(path):path = path.strip()  # 去除首位空格path = path.rstrip("\\")  # 去除尾部 \ 符号isExists = os.path.exists(path)  # 判断路径是否存在if not isExists:os.makedirs(path)  # 如果不存在则创建目录print(path + ' 创建成功')return Trueelse:# 如果目录存在则不创建,并提示目录已存在print(path + ' 目录已存在')return Falseclass BatchRename():'''批量重命名文件夹中的图片文件'''def __init__(self):self.path = PATH_DATASET  # 表示需要命名处理的文件夹# 修改图像尺寸def resize(self):for aroot, dirs, files in os.walk(self.path):# aroot是self.path目录下的所有子目录(含self.path),dir是self.path下所有的文件夹的列表.filelist = files  # 注意此处仅是该路径下的其中一个列表# print('list', list)# filelist = os.listdir(self.path) #获取文件路径total_num = len(filelist)  # 获取文件长度(个数)for item in filelist:if item.endswith('.jpg'):  # 初始的图片的格式为jpg格式的(或者源文件是png格式及其他格式,后面的转换格式就可以调整为自己需要的格式即可)src = os.path.join(os.path.abspath(aroot), item)# 修改图片尺寸到128宽*256高im = Image.open(src)out = im.resize((128, 256), Image.ANTIALIAS)  # resize image with high-qualityout.save(src)  # 原路径保存def rename(self):for aroot, dirs, files in os.walk(self.path):# aroot是self.path目录下的所有子目录(含self.path),dir是self.path下所有的文件夹的列表.filelist = files  # 注意此处仅是该路径下的其中一个列表# print('list', list)# filelist = os.listdir(self.path) #获取文件路径total_num = len(filelist)  # 获取文件长度(个数)i = 1  # 表示文件的命名是从1开始的for item in filelist:if item.endswith('.jpg'):  # 初始的图片的格式为jpg格式的(或者源文件是png格式及其他格式,后面的转换格式就可以调整为自己需要的格式即可)src = os.path.join(os.path.abspath(aroot), item)# 根据图片名创建图片目录dirname = str(item.split('_')[0])# 为相同车辆创建目录# new_dir = os.path.join(self.path, '..', 'bbox_all', dirname)new_dir = os.path.join(PATH_ALL_IMAGES, dirname)if not os.path.isdir(new_dir):mymkdir(new_dir)# 获得new_dir中的图片数num_pic = len(os.listdir(new_dir))dst = os.path.join(os.path.abspath(new_dir),dirname + 'C1T0001F' + str(num_pic + 1) + '.jpg')# 处理后的格式也为jpg格式的,当然这里可以改成png格式    C1T0001F见mars.py filenames 相机ID,跟踪指数# dst = os.path.join(os.path.abspath(self.path), '0000' + format(str(i), '0>3s') + '.jpg')    这种情况下的命名格式为0000000.jpg形式,可以自主定义想要的格式try:copyfile(src, dst)  # os.rename(src, dst)print('converting %s to %s ...' % (src, dst))i = i + 1except:continueprint('total %d to rename & converted %d jpgs' % (total_num, i))def split(self):# ---------------------------------------# train_testimages_path = PATH_ALL_IMAGEStrain_save_path = PATH_TRAINtest_save_path = PATH_TESTif not os.path.isdir(train_save_path):os.mkdir(train_save_path)os.mkdir(test_save_path)for _, dirs, _ in os.walk(images_path, topdown=True):for i, dir in enumerate(dirs):for root, _, files in os.walk(images_path + '/' + dir, topdown=True):for j, file in enumerate(files):if (j == 0):  # test dataset;每个车辆的第一幅图片print("序号:%s  文件夹: %s  图片:%s 归为测试集" % (i + 1, root, file))src_path = root + '/' + filedst_dir = test_save_path + '/' + dirif not os.path.isdir(dst_dir):os.mkdir(dst_dir)dst_path = dst_dir + '/' + filemove(src_path, dst_path)else:src_path = root + '/' + filedst_dir = train_save_path + '/' + dirif not os.path.isdir(dst_dir):os.mkdir(dst_dir)dst_path = dst_dir + '/' + filemove(src_path, dst_path)rmtree(PATH_ALL_IMAGES)if __name__ == '__main__':demo = BatchRename()demo.resize()demo.rename()demo.split()

0x01 参数调整

1.修改model.py

根据数据集中的类别,修改num_classes:

🚩注:

数据集划分好后train和test文件夹下分别有多少个子文件夹,就代表有多少个类别。

即num_classes的数量。

2.修改train.py

 --data-dir:数据集文件,修改数据集的路径。

--lr:学习率,可以不用修改。

根据需求修改epoches的次数:

 可以修改权重保存的位置以及命名,以免发生覆盖:

修改dataset的预处理:

修改完成后,运行train.py开始训练,最终得到的权重结果保存在deep/checkpoint中。

至此,Deepsort部分已经全部结束。

Ⅱ 生成视频&轨迹绘制

0x00 参数设置

 将之前yolov5训练后得到的best.pt和Deepsort训练后得到的权重替换到track.py中:

修改视频的地址: 

运行track.py,得到最终视频,并在视频中显示运动轨迹。

Ⅲ 常见报错分析

为了方便新手小白快速上手,解决报错,暂不讲解报错的具体原因,只给出如何解决报错(给出最简单的解决办法),若想进一步了解报错的具体原因,可以在评论区一起交流。

0x00 未修改num_classes

报错:

解决方法:

在model.py中修改num_classes

 0x01 梯度问题

 报错:

 这个错误是由于在计算梯度的过程中,对一个叶子节点(leaf Variable)进行了原地操作(in-place operation),导致了运行时错误。PyTorch中默认情况下,autograd不支持对叶子节点进行原地操作,因为这会导致梯度计算不正确。

解决方法:

在models文件夹下的yolo.py文件中:

 添加代码:

with torch.no_grad():

0x02 显存不足

报错:

解决方法(这里提供一个最简单的方法):

更改batch_size的大小和epoch的次数。

 或者释放内存:

if hasattr(torch.cuda, 'empty_cache'):torch.cuda.empty_cache()

 ❓有更多报错大家可以写在评论区,博主看到后会尽力帮助大家。

0x03 Wandb问题

报错:

解决方法:

直接关闭wandb。

在wandb_utils.py中,将开头部分的代码:

 try:import wandbfrom wandb import init, finish
except ImportError:wandb = None

 改为:

try:import wandbfrom wandb import init, finish
except ImportError:wandb = None
wandb = None

0x04 权重pt文件不匹配

报错:

权重pt文件和新环境的YOLOv5的小版本不相同

报错代码:

YoloV5:AttributeError: Can‘t get attribute ‘C3‘ on <module ‘models.common‘ from

解决方法:在common.py中加入C3SPPF模块:

#在最上面需要引入warnings库
import warningsclass C3(nn.Module):# CSP Bottleneck with 3 convolutionsdef __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansionsuper(C3, self).__init__()c_ = int(c2 * e)  # hidden channelsself.cv1 = Conv(c1, c_, 1, 1)self.cv2 = Conv(c1, c_, 1, 1)self.cv3 = Conv(2 * c_, c2, 1)  # act=FReLU(c2)self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])# self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)])def forward(self, x):return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))class SPPF(nn.Module):# Spatial Pyramid Pooling - Fast (SPPF) layer for YOLOv5 by Glenn Jocherdef __init__(self, c1, c2, k=5):  # equivalent to SPP(k=(5, 9, 13))super().__init__()c_ = c1 // 2  # hidden channelsself.cv1 = Conv(c1, c_, 1, 1)self.cv2 = Conv(c_ * 4, c2, 1, 1)self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)def forward(self, x):x = self.cv1(x)with warnings.catch_warnings():warnings.simplefilter('ignore')  # suppress torch 1.9.0 max_pool2d() warningy1 = self.m(x)y2 = self.m(y1)return self.cv2(torch.cat([x, y1, y2, self.m(y2)], 1))

0x05 YOLOv5断后继续训练 

YOLOv5自带断点保存,可以恢复训练。

在train.py中,把

 改为:

parser.add_argument('--resume', nargs='?', const=True, default=True, help='resume most recent training')

default 后改为True。

运行程序,可以看到从上次中断得到地方继续训练了。

  END


📝因为作者的能力有限,所以文章可能会存在一些错误和不准确之处,恳请大家指出!

 📃参考文献:

[1] Simple Online and Realtime Tracking with a Deep Association Metric

[1703.07402] Simple Online and Realtime Tracking with a Deep Association Metric (arxiv.org)

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

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

相关文章

PHP旅游管理系统Dreamweaver开发mysql数据库web结构php编程计算机网页

一、源码特点 PHP 旅游管理系统是一套完善的web设计系统,对理解php编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。 PHP 旅游管理系统 源码下载地址: https://download.csdn.net/download/qq_41…

LeetCode 46题:全排列

题目 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1: 输入:nums [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]示例 2: 输入:…

gerrit 如何提交进行review

前言 本文主要介绍如何使用gerrit进行review。 下述所有流程都是参考: https://gerrit-review.googlesource.com/Documentation/intro-gerrit-walkthrough.html 先给一个commit后但是还没有push上去的一个办法: git reset --hard HEAD^可以多次reset.…

小白学go基础03-了解Go项目的项目结构

我们先来看看第一个Go项目——Go语言自身——的项目结构是什么样的。Go项目的项目结构自1.0版本发布以来一直十分稳定,直到现在Go项目的顶层结构基本没有大的改变。 截至Go项目commit 1e3ffb0c(2019.5.14),Go1.0 项目结构如下&am…

VirtualBox7+Ubuntu22集群规划

1. 目的: 新入手了一台小主机(8核 / Intel(R) Xeon(R) W-10885M CPU 2.40GHz 2.40 GHz, 16vCpu / 64G RAM / 系统类型 64 位操作系统, 基于 x64 的处理器),原装了一套Win11专业版,打算用VirtualBox 虚拟一个集群。 2. …

Vue2项目练手——通用后台管理项目第四节

Vue2项目练手——通用后台管理项目 数据的请求mock数据模拟实战文件目录src/api/mock.jssrc/api/mockServeData/home.jsmain.js 首页组件布局可视化图表可视化图表布局Home.vue echarts表Home.vue 数据的请求 mock数据模拟实战 mock官方文档 前端用来模拟后端接口的工具&…

UNIX网络编程卷一 学习笔记 第三十章 客户/服务器程序设计范式

开发一个Unix服务器程序时,我们本书做过的进程控制: 1.迭代服务器(iterative server),它的适用情形极为有限,因为这样的服务器在完成对当前客户的服务前无法处理已等待服务的新客户。 2.并发服务器&#x…

SquirrelMail实现Web方式收发邮件_xionglling的博客-CSDN博客

SquirrelMail实现Web方式收发邮件_xionglling的博客-CSDN博客小松鼠实现Web邮件服务SquirrelMail 是一个用PHP开发的Web邮件系统。它内置纯PHP支持的IMAP和SMTP协议,所有页面都遵循 HTML 4.0标准(没有使用任何 JavaScript 代码),以便最大限度兼容各种多浏…

Qt之事件过滤—筛选处理对象

文章目录 事件过滤完整代码 事件过滤 事件过滤是当事件发生时,可以对不同对象,实现不同操作,以达到筛选的效果。 步骤: 1、首先安装一个事件过滤器,为对象安装事件过滤,指定“谁”来监控这些事件对象 //给…

vue3+ts+uniapp实现小程序端input获取焦点计算上推页面距离

vue3tsuniapp实现小程序端input获取焦点计算上推页面距离 input获取焦点计算上推页面距离 1.先说我这边的需求2.发现问题3.解决思路4.代码展示 自我记录 1.先说我这边的需求 需求 1.给键盘同级添加一个按钮例如’下一步’ or ‘确认’ 这种按钮 2.初步想法就是获取input焦点时…

【LeetCode-中等题】146. LRU 缓存

文章目录 题目方法一:直接继承LinkedHashMap调用api方法二:自定义LinkedHashMap HashMap ListNode LinkedHashMap 题目 LRU缓存是什么:LRU缓存机制,你想知道的这里都有 实现 LRU 缓存算法 方法一:直接继承Linked…

单臂路由实验:通过Trunk和子接口实现VLAN互通

文章目录 一、实验背景与目的二、实验拓扑三、实验需求四、实验解法1. PC 配置 IP 地址2. PC3 属于 Vlan10,PC4 属于 Vlan20,配置单臂路由实现 Vlan10 和 Vlan20 三层互通3. 测试在 PC3 上 Ping PC4 ,可以 Ping 通 PC4 摘要: 本文…

数学建模:回归分析

🔆 文章首发于我的个人博客:欢迎大佬们来逛逛 数学建模:回归分析 文章目录 数学建模:回归分析回归分析多元线性回归案例 多项式回归一元多项式回归多元二项式回归 非线性回归逐步回归 回归分析 多元线性回归 案例 首先进行回归分…

ASP.NET Core 中的 MVC架构

MVC 架构 MVC架构把 App 按照逻辑分成三层: Controllers,接收 http request,配合 model,通过http response 返回 view,尽量不做别的事Models, 负责业务逻辑,App 的状态,以及数据处理Views&…

JVM的故事——虚拟机字节码执行引擎

虚拟机字节码执行引擎 文章目录 虚拟机字节码执行引擎一、概述二、运行时栈帧结构三、方法调用 一、概述 执行引擎Java虚拟机的核心组成之一,它是由软件自行实现的,能够执行那些不被硬件直接支持的指令集格式。 对于不同的虚拟机实现,执行引…

【网络安全带你练爬虫-100练】第20练:数据处理-并写入到指定文档位置

目录 一、目标1:解码去标签 二、目标2:提取标签内内容 三、目标3:处理后的数据插入原位置 四、目标4:将指定的内容插入指定的位置 五、目标5:设置上下文字体格式 六、目标6:向多个不同位置插入不同的…

企业如何充分借助大数据下精准营销?

技术的发展和智能终端的普及移动互联网用户的大规模增长使移动互联网快速发展,使中国移动互联网软件进入移动互联网时代越来越多地涉及到改变生活大家习惯。移动互联网时代的到来也意味着大数据时代的到来。精准营销数据方法,移动互联网和大数据的兴起不…

ZLMediaKit 重建docker包

1.下载容器到本地服务器并运行 #此镜像为github持续集成自动编译推送,跟代码(master分支)保持最新状态 docker run -id -p 1935:1935 -p 8080:80 -p 8443:443 -p 8554:554 -p 10000:10000 -p 10000:10000/udp -p 8000:8000/udp -p 9000:9000/udp zlmediakit/zlmedi…

App自动化测试持续集成效率提高50%

持续集成是一种开发实践,它倡导团队成员需要频繁的集成他们的工作,每次集成都通过自动化构建(包括编译、构建、自动化测试)来验证,从而尽快地发现集成中的错误。让正在开发的软件始终处于可工作状态,让产品…

拦截器和异常处理器

拦截器和异常处理器 拦截器 拦截器(Interceptor)&#xff0c;主要完成请求参数的解析、将页面表单参数赋给值栈中相应属性、执行功能检验、程序异常调试等工作。 准备 创建模块 如下为完整的项目结构 web.xml <web-app xmlns"http://xmlns.jcp.org/xml/ns/javaee&qu…