deepsort用来跟踪被检测对象。网上常见的yolov5+deepsort,是pytorch版。此版本由ZQPei Github提供,源自nwojke Github ,将其修改为pytorch,是pytorch粉的福音。
ZQPei提供的ReID:deep模型文件ckpt.t7为行人特征,由market1501数据集训练获得。本文在此基础上,针对车辆特征,使用veri-wild车辆数据集,对deep网络进行训练,提取车辆外观特征,使其适合车辆对象跟踪。
在此感谢ZQPei的贡献!

本博文的模型文件可从百度网盘下载
提取码:gchf

1 deep ReID网络定义

ReID personal Re-identification,针对不同对象的重识别。deepsort中deep就是一个ReID模型。

deepsort从Extractor获得跟踪图像的分类特征,其ReID由Net类定义,在deep_sort_pytorch/deep_sort/deep/model.py中。

论文“deep learning in video multi-object tracking: a survey”描述多目标跟踪MOT的主要处理步骤:

(1)给定视频原始帧
(2)目标检测器(Faster R-CNN, yolov5, SSD等)获取目标检测框
(3)将目标框中对应的目标抠出来,进行特征提取(包括外观特征和运动特征)
(4)相似度计算,计算前后两帧目标间的匹配程度(前后属于同一目标的距离小,不同目标的距离较大)。
(5)数据关联,为每个对象分配目标ID。

ReID:DeepSort中采用了一个简单的CNN来提取被检测框中物体的外观特征,在每次(每帧)检测+追踪后,进行一次物体外观特征的提取并保存。后面每执行一步时,都要执行一次当前帧被检测物体外观特征与之前存储的外观特征的相似度计算,这个相似度将作为一个重要的判别依据(将运动特征与外观特征结合作为判别依据,运动特征就是Sort中卡尔曼滤波做的事)。
这个小型CNN网络在论文SIMPLE ONLINE AND REALTIME TRACKING WITH A DEEP ASSOCIATION METRIC 给出,Net网络如下:

ZQPei给出代码实现(deep_sort_pytorch/deep_sort/deep/model.py)

class Net(nn.Module):def __init__(self, num_classes=751 ,reid=False):super(Net,self).__init__()self.conv = nn.Sequential(nn.Conv2d(3,64,3,stride=1,padding=1),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.MaxPool2d(3,2,padding=1), )self.layer1 = make_layers(64,64,2,False) self.layer2 = make_layers(64,128,2,True)   self.layer3 = make_layers(128,256,2,True)  self.layer4 = make_layers(256,512,2,True)self.avgpool = nn.AvgPool2d((8,4),1)self.reid = reidself.classifier = nn.Sequential(nn.Linear(512, 256),nn.BatchNorm1d(256),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(256, num_classes),)def forward(self, x):x = self.conv(x)        #  in 128x64, out 64x32, channel=64x = self.layer1(x)      #  in 64x32,  out 64x32, channel=64   x = self.layer2(x)      #  in 64x32,  out 32x16, channel=128x = self.layer3(x)      #  in 32x16,  out 16x8,  channel=256x = self.layer4(x)      #  in 16x8,    out 8x4,   channel=512x = self.avgpool(x)     #  in  8x4,    out 1x1,   channel=512x = x.view(x.size(0),-1)if self.reid:x = x.div(x.norm(p=2,dim=1,keepdim=True))return x# classifierx = self.classifier(x)return x

注1: 此Net网络在训练时,reid=False,变量x进入分类网络,产生对比的分类特征。在跟踪时,reid=True,变量x不进入分类网络,直接返回输入图像的分类特征。reid参数由Extractor导入,训练时用不到Extractor,故为默认值reid=False; 跟踪时Extractor导入参数reid=True。
注2:deepsort原作者的ReID是2个卷积层+6层残差网络(12个卷积层),特征维度128。ZQPei ReID则是1个卷积层+2x4层残差网络(16个卷积层),特征维度512。特征维度大则分类较细,而网络参数增多(ckpt.t7=46MB)。按原作者实现的ReID则有特征维度128,ckpt.t7约3.3MB。从运行速度看,两者相差不大,且跟踪效果也没有多少区别。

2 修改适合于车辆目标的图像尺寸

由于DeepSort主要被用来做行人追踪的,输入图像的大小为128(h)x 64(w)的矩形框。若针对车辆等其他物体追踪,需要把网络模型的输入进行修改。车辆目标和行人相比,其矩形框应为64(h)x128(w)。
于是对Net网络参数的修改
(1)平均池化avg.pool修改如下:

self.avgpool = nn.AvgPool2d((4,8),1)

即把原来的kernal size从8x4改成4x8。因为,经前面卷积和规划层等,输入的车辆图像从64x128变成4x8(行人图像则是128x64变成8x4),经avg.pool layer缩减成1x1特征项。512个1x1特征项进入分类器self.classifier进行分类判决,得到检测框目标的外观特征分类。

(2)修改分类数量

class Net(nn.Module):def __init__(self, num_classes=537, reid=False):

将num_classes=751修改成num_classes=车辆数据集ID数。

(3)另外,在feature_extractor.py中,

class Extractor(object):def __init__(self, model_path, use_cuda=True):... ...self.size = (128,64)

原代码为:self.size=(64, 128),此处self.size=(width,height) 要注意。

3 车辆训练数据集

训练数据集采用veri-wild,此数据集提供了25GB车辆图片,其中image文件夹有23个压缩项目,每个1GB。这数据量实在太大,训练很辛苦。此处用了5个压缩项,从中选出537辆车的图片,每辆车图片数量在16~20之间,构成train_537目录。另外建立test_537目录,从train_537目录中,每辆车的子目录中取出4个图像放入test_537目录。

由此完成车辆训练数据集train_537和test_537,两个目录下,00029, 00030,…等目录分别是车辆ID号,也是ReID的外观特征分类号(号码是什么数字不重要),车辆ID号目录下存放对应的车辆图片。一个小程序完成训练和测试数据集生成。

import os
from shutil import copyfile, copytree, rmtreesrc_dir = "/home/your_name/AI_dataset/veri_wild_images01_05"
train_dir="dataset/train_list015"
test_dir="dataset/test_list015"
if  os.path.isdir(train_dir):rmtree(train_dir)   #删除文件夹,包含文件夹及文件夹下的所有文件
if  os.path.isdir(test_dir):    rmtree(test_dir)
os.mkdir(train_dir)
os.mkdir(test_dir)# 提取符合要求的ID子目录文件
for subdir in os.listdir( src_dir ):   # ID子目录名称src=src_dir+"/"+subdirfile_num = sum([os.path.isfile(os.path.join(src, listx)) for listx in os.listdir(src)])  #从ID子目录中获得文件数if file_num>=16 and file_num<20:         # 提取 ID子目录中文件数>=16且<20的子目录print(src,"  :",  file_num)train_ID_dir=train_dir+"/"+subdirtest_ID_dir=test_dir+"/"+subdirif  os.path.isdir(train_ID_dir):rmtree(train_ID_dir)if  os.path.isdir(test_ID_dir):rmtree(test_ID_dir)os.mkdir(train_ID_dir)os.mkdir(test_ID_dir)
# copy src 4个文件到test,  其他剩余文件到trainID1=0for file_name in os.listdir(src):  # 提取ID子目录中文件名称if ID1<4:           copyfile(src+"/"+file_name, test_ID_dir+"/"+file_name)  # 拷贝4个文件到test_ID_dirID1=ID1+1else:copyfile(src+"/"+file_name, train_ID_dir+"/"+file_name)  # 拷贝其他文件到train_ID_dir

完成train目录和test目录后,将这两个目录放到your_training_dir目录下。
注:经训练后得知,每个ID下车辆图片多一些有助于提高特征分类识别准确度,故要得到较好的结果,每个ID下车辆图片应在40-60之间,且每个ID的图片数尽可能均衡。若某些ID图片数只有10张,而其他ID图片数在50,则图片数少的ID特征提取将不显著。

4 开始训练

运行 deep_sort_pytorch/deep_sort/deep/train.py,输入参数中填入自己训练数据集目录。

train.py --data-dir your_training_dir

训练之前,对train.py做一点修改:
(1)输入train目录和test目录


(2)修改图像变换

将图像尺寸从原来的(128,64)改为适合车辆形状的(64,128),并对transform_train中增加一项:
torchvision.transforms.Resize((64, 128))
据说增加此项可以防止模型过拟合,加快训练收敛速度。训练过程如下:

cls537_ep120.png 训练结果, ~/deep_sort_pytorch/cls537_ep120.png

图片尺寸64x128, 数据集ID_num=537, epoch=120,
train: loss=0.18, acc=96.2%
test: loss=0.65, acc=80.2%
增大epoch,加大训练时间,则匹配精度会得到提高,但从曲线看,这个过程将很漫长。
epoch=120大约需要50分钟(RTX2060)。

5 修改学习率,加快loss收敛过程

原文初始学习率lr=0.1, 且经过20epoch,减小一次,0.1*lr。发现这种收敛速度不够快,需修改学习率变化过程,采用warm up + CosineAnnealingLR的余弦退火学习率衰减方法。先预热模型(warm up), 以一个很小的学习率逐步上升到设定的初始学习率,而后按照余弦曲线下降,这会使模型的最终收敛效果更好。

(1)构造WarmUpLR,继承类_LRScheduler类

from torch.optim.lr_scheduler import _LRScheduler, CosineAnnealingLRclass WarmUpLR(_LRScheduler):
"""warmup_training learning rate scheduler
Args:
optimizer: optimzier(e.g. SGD)
total_iters: totoal_iters of warmup phase
"""def __init__(self, optimizer, total_iters, last_epoch=-1):self.total_iters = total_iterssuper().__init__(optimizer, last_epoch)def get_lr(self):"""we will use the first m batches, and set the learningrate to base_lr * m / total_iters"""return [base_lr * self.last_epoch / (self.total_iters + 1e-8) for base_lr in self.base_lrs]

且,在参数输入中增加一项

parser.add_argument('-warm', type=int, default=5, help='warm up training phase')

设定warm up步距为5,即epoch大于5时,将学习率提升到初始学习率。

(2)在训练程序 train.py增加:

warmup_epoch = args.warm
iter_per_epoch = len(trainloader)
warmup_scheduler = WarmUpLR(optimizer, iter_per_epoch * args.warm)
train_scheduler = CosineAnnealingLR(optimizer, 130 ) #100 - warmup_scheduler

(3)对训练函数train(epoch)中增加:

def train(epoch):......if epoch <= args.warm:warmup_scheduler.step() warm_lr = warmup_scheduler.get_lr()print("warm_lr:%s" % warm_lr)......

(4)最后,在主函数main()中增加

if epoch > args.warm:train_scheduler.step(epoch)lr = train_scheduler.get_last_lr()print("Learn_rate:%s" % lr)

经过此法调整学习率,明显加快了loss收敛过程
所调整学习率的代码,在运行过程中给出警告:

  • optimizer.step()应先于scheduler.step()执行
  • scheduler.step(epoch)中的epoch参数不需要
    这两条警告为保证torch.optimizer按照原定学习率调整,而此处因warm up过程,打乱了optimizer原定顺序,因此报警。可无视。
    训练结果如下,比原先的训练过程好很多


epoch=80比原来epoch=120的训练效果还好。
train acc=99.8%, test acc=84.7% 。
注:此训练看出,train和val精度存在差距,这是所谓过拟合现象。通过增大每个ID下的图片数量,可以解决过拟合现象,在下面的后记中可得到验证。

6 跟踪检验

训练结束,得到ReID模型ckpt.t7,可以放到deep_sort_pytorch/deep_sort/deep/checkpoint目录下,替换掉原来的ckpt.t7,进行跟踪试验验证。验证过程可见本博另一篇:
pytorch yolo5+Deepsort实现目标检测和跟踪
试验结果不太理想,所训练得到的ckpt.t7与原ZQPei提供的ckpt.t7略有不同,区别并不大。并且此车辆ReID与原有的行人ReID模型ckpt.t7,对行人跟踪效果也相同,并未因车辆特征而破坏对行人特征的跟踪效果,难以理解。
进一步试验发现,调整deep的参数(deep_sort_pytorch/configs/deep_sort.yaml)会获得较好的跟踪效果。

DEEPSORT:REID_CKPT: "deep_sort_pytorch/deep_sort/deep/checkpoint/ckpt.t7"MAX_DIST: 0.1   # 0.2  最大余弦距离 MIN_CONFIDENCE: 0.5   #0.3  YOLOv5最小检测置信度,增大置信度可去除杂散干扰。NMS_MAX_OVERLAP: 1.  # 0.5MAX_IOU_DISTANCE: 0.5 # 0.7 IOU最大距离,此值小则不易匹配,将产生新的ID。MAX_AGE: 70   # 70N_INIT: 6     # 3 track连续confirm数量,增大有助于减少新ID出现。NN_BUDGET: 100 # track最大feature数量。

[修改- 2021-10-21]
其中,减小最大余弦距离(MAX_DIST),减小最大IOU距离(MAX_IOU_DISTANCE=0.5),可提高跟踪稳定性,跟踪目标被遮挡时仍可实现目标ID保持不变。其中,IOU距离影响效果较大,余弦距离的作用较小。余弦距离是比较yolov5检测目标和sort已跟踪目标间外观特征的相似度指标,若这个指标对跟踪效果的影响小,说明外观特征对持续跟踪所起的作用不大。这可能表明,目前所用到的ReID模型不好,也许更换另一种ReID才能提高车辆特征的识别能力。
IOU距离的作用表现在,当出现遮挡时,IOU距离起作用,可从下图看出:

发生遮挡时,IOU匹配比较上一帧轨迹检测框与目标框相似度,决定是否可匹配,以保持遮挡后的继续跟踪。IOU距离阈值大则易于匹配,不产生新的轨迹ID;IOU阈值小则不易匹配成功而产生新的轨迹ID,便破坏了跟踪的延续性。此外,IOU阈值过小则持续发生无匹配,不断产生新ID但继续无匹配,在跟踪视频中就看不到跟踪框。

总结:

本文针对车辆目标,对deepsort中ReID目标外观特征进行训练。
(1)抽取veri-wild车辆数据集部分数据,构造deep ReID网络训练数据集。
(2)修改deep Net网络参数,以适应车辆的长宽比。
(3)变更学习率调整方法,用warm up + CosineAnnealingLR替换原文的简单学习率调整方案。
其训练过程较为满意,获得收敛结果。但所得到的车辆外观分类特征模型并没有多大的跟踪性能改善,而修改deep参数却得到较好的跟踪性能。

后记 2021-10-23

在试验ZQPei提供的ReID模型(8层残差网络,16层卷积,特征维度512,ckpt.t7=46MB)后,还训练了OSnet(特征维度512, ),模型收敛很好,但跟踪效果不佳,还不如ZQPei模型。在粗略研究deepsort后,发现跟踪目标被遮挡后,要恢复遮挡前的目标ID,主要靠外观特征匹配。于是决定试试加深残差网络,看看特征匹配是否有效:
(1) Resnet 残差网络,12层残差网络,24层卷积,特征维度256, ckpt.t7=12.9MB
(2) Resnet残差网络,16层残差网络,32层卷积,特征维度256, ckpt.t7 = 15.9MB
(3) Resnet残差网络, 18层残差网络,36层卷积,特征维度512, ckpt.t7 = 49.8MB
其结果是一个比一个略有改善,目标遮挡后基本都能恢复原来的目标ID,看来加深卷积层深度,有助于强化ReID特征识别能力,在deepsort特征匹配上获得较为满意的试验结果。
如下为ReID网络(3),可以认为是ResNet36, 特征维度512。并且将原来的平均池化AvgPool2d更改为自适应平均池化 AdaptiveAvgPool2d,这样做可以修改进入ReID的图像尺寸,如变更为128x128, 或256x256, 128x256等,且变更输入图片尺寸时,模型不需要重新训练。

class Net(nn.Module):def __init__(self, num_classes=586 ,reid=False):     # num_classes=751.  change to 586  by 2021-9-30super(Net,self).__init__()self.conv = nn.Sequential(nn.Conv2d(3,32,3,stride=1,padding=1),nn.BatchNorm2d(32),nn.ReLU(inplace=True),nn.Conv2d(32,32,3,stride=1,padding=1),nn.BatchNorm2d(32),nn.ReLU(inplace=True),nn.MaxPool2d(3,2,padding=1),        # 64x64)self.layer1 = make_layers(32,32,2,False)    #64x64self.layer2 = make_layers(32,32,2,False)    #64x64self.layer3 = make_layers(32,64,2,True)  # 32x32self.layer4 = make_layers(64,64,2,False) # 32x32self.layer5 = make_layers(64,64, 2, False)  # 32x32self.layer6 = make_layers(64, 128, 2, True)  # 16x16self.layer7 = make_layers(128, 128, 2, False)  # 16x16self.layer8 = make_layers(128, 256, 2, True)  # 8x8self.layer9 = make_layers(256, 512, 2, False)  #8x8#self.avgpool = nn.AvgPool2d((8,8),1)    #  1x1  ------------------  nn.AvgPool2d((8,4),1)self.adaptiveavgpool = nn.AdaptiveAvgPool2d(1)  #添加自适应平均池化self.reid = reidself.classifier = nn.Sequential(nn.Linear(512, 256),nn.BatchNorm1d(256),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(256, num_classes),)def forward(self, x):             # ResNet 36+2    dim=512x = self.conv(x)x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x = self.layer5(x)x = self.layer6(x)x = self.layer7(x)x = self.layer8(x)x = self.layer9(x)#x = self.avgpool(x)x = self.adaptiveavgpool(x)   # 变更为自适应平均池化,适应不同尺寸输入图像。x = x.view(x.size(0),-1)if self.reid:x = x.div(x.norm(p=2,dim=1,keepdim=True))return x# classifierx = self.classifier(x)return x

试验结果,用ReID网络(3):epoch = 80, train ACC = 99.8%, test ACC = 95.5%
ReID训练时,尽可能增大每个子目录下车辆图片数量,如数量为40-60,则可有效提高测试精度,见下图,train和val精度基本重合,没有出现过拟合现象。

目标遮挡后重新跟踪效果

从下面动图可看出,检测目标被遮挡后,产生新的目标-轨迹ID,而随后由于该目标与保存的轨迹目标特征相同,就可以切换到原来的目标-轨迹ID,保持原目标跟踪。

deepsort训练车辆特征参数相关推荐

  1. 【小白CV教程】YOLOv5+Deepsort实现车辆行人的检测、追踪和计数

    [小白CV教程]YOLOv5+Deepsort实现车辆行人的检测.追踪和计数 本文禁止转载! 项目简介: YOLOv5检测器: DeepSort追踪器: 运行demo: 训练自己的模型: 调用接口: ...

  2. 车辆搜索 -使用triplet loss 训练车辆识别模型

    最近读了LEARNING A REPRESSION NETWORK FOR PRECISE VEHICLE SEARCH 论文,将从中所了解的信息在此记录下来. 背景及模型介绍 此论文主要是讨论车辆的 ...

  3. 利用行人重识别代码训练车辆重识别

    一.参考 行人重识别代码:Person_reID_baseline_pytorch-master 代码链接:https://github.com/layumi/Person_reID_baseline ...

  4. yolov5 deepsort 行人车辆 双向计数 跟踪检测

    点上方计算机视觉联盟获取更多干货 仅作学术分享,不代表本公众号立场,侵权联系删除 转载于:AI算法与图像处理 AI博士笔记系列推荐 周志华<机器学习>手推笔记正式开源!可打印版本附pdf下 ...

  5. 复现yolov5+Deepsort实现车辆行人的检测、追踪和计数

    源码地址: https://github.com/dyh/unbox_yolov5_deepsort_counting https://github.com/Sharpiless/Yolov5-dee ...

  6. 【CV-tracking】多目标跟踪-实战(OpenCV+YOLO+DeepSORT)

    [CV-tracking]多目标跟踪-实战(OpenCV+YOLO+DeepSORT) 文章目录 1. 参考文献 1.1. 数据集 1.2. 可复现的代码 1.3. YOLO教程 1.4. DeepS ...

  7. YOLOV5+Deepsort行人跟踪初探

    文章目录 1. YOLOV5+Deepsort 实现多目标跟踪 2. sort 目标跟踪 3. Deepsort 目标跟踪 3. Deepsort 代码讲解 1. YOLOV5+Deepsort 实现 ...

  8. VehicleNet: Learning Robust Visual Representation for Vehicle Re-identification(车辆网络:学习用于车辆再识别的鲁棒视觉)

    摘要:车辆再识别(re-id)的一个基本挑战是学习鲁棒的和有区别的视觉表示,考虑到不同相机视图的显著的类内车辆变化.由于现有的车辆数据集在训练图像和视点等方面的局限性,我们建议利用四个公共车辆数据集构 ...

  9. 记录在VeRi-776训练【pytorch】deep_sort中的深度ReID特征

    pytorch版deep sort训练特征 开场白 第一步:下载项目相关资料 1.pytorch版的yolov3+deepsort项目 2.VeRi-776数据集 第二步:修改数据集格式及程序运行 1 ...

最新文章

  1. JDBC+Servlet+JSP整合开发之30-JDBC、Servlet、JSP的MVC
  2. 《Effective C#》读书笔记——条目11:理解短小方法的优势C#语言习惯
  3. JAVA实现HTTPserver端
  4. ffmpeg rtp时间戳
  5. 【Paper】2015_Song_Consensus of Heterogeneous Agents with Linear Discrete Dynamics
  6. 购物车模块如何进行测试?
  7. 如果你20秒钟还看不懂这个短信的话,说明你还纯洁!?
  8. 从月薪2300女工到年薪70万谷歌程序员:人生,永远不要给自己设限
  9. 关闭Windows 2008下面应用程序出错后的提示
  10. excel_applications
  11. mac下解压war包
  12. 64位计算机很慢,win7 64位旗舰版电脑网速太慢怎么解决
  13. Java求抛物线输入角度速度_知道初速度和抛物线的角度,怎么计算落点
  14. 神舟 linux背光驱动,【linux】暂时解决sis m672(神舟F4000 D9) linux驱动 宽屏分辨率的问题?...
  15. python多线程中join()的理解
  16. 多传感器融合定位十四-基于图优化的定位方法
  17. 入门避雷需知:如何选择关于Python机器学习的书
  18. maven conf
  19. 体验篇 - Zcash
  20. matlab面板数据处理程序,勒沙杰空间计量MATLAB(可做面板数据)

热门文章

  1. 一、ECMAScript 6/7/8简介
  2. HTML5练习——布局练习01(内含5个基本的css命名规则)
  3. oracle快速了解法,快速掌握Oracle基本语法,让你来去自如
  4. 关于谷歌浏览器页面出现光标闪动,鼠标的焦点没有消失解决
  5. c语言cache,高速C/C++编译工具ccache
  6. 工业物联网RTU与DTU的区别
  7. ping pong buffer
  8. 洛谷 P1135奇怪的电梯
  9. 转:使用memc-nginx和srcache-nginx模块构建高效透明的缓存机制
  10. 一般拓扑学--From百度(看了这个,终于明白什么是拓扑了)