目标追踪 FairMOT

FairMOT是一个多目标跟踪系统,基于centernet思想,包括目标类别分类、宽高回归和中心点偏移等分支。训练过程中使用多种损失函数,如heatmap分类loss、宽高回归loss等。文章还介绍了数据集处理、环境配置、预训练模型的使用,以及训练和评估过程中的关键指标如MOTA、MOTP和IDF1。
摘要由CSDN通过智能技术生成

主体思想

FairMOT采用centernet思想,检测网络有三个分支:目标类别分类(hm),目标的宽高(wh),目标中心点的偏置(xy)。

(1)目标类别分类(hm):通过C通道的heatmap来预测,heatmap的大小是原图下采样4倍之后的大小,如果图像中有某个类别的目标,那么这个目标的中心点在heatmap上的概率值为1,其余周围概率成高斯分布逐渐衰减;

(2)wh和xy通过回归得到,目标ID通过交叉熵分类得到。

训练

数据集类JointDataset在src\lib\datasets\dataset\jde.py文件中,__getitem__方法中读取图片和对应的标签,做简单的数据增强

损失函数

损失包括:heatmap分类loss,宽高回归loss,目标中心点偏移回归loss,目标ID分类loss。src\lib\trains\mot.py中MotTrainer类可查看loss名:

MOTloss类定义了这几个loss:

class MotLoss(torch.nn.Module):
    def __init__(self, opt):
        super(MotLoss, self).__init__()
        self.crit = torch.nn.MSELoss() if opt.mse_loss else FocalLoss()         #分类loss
        self.crit_reg = RegL1Loss() if opt.reg_loss == 'l1' else \
            RegLoss() if opt.reg_loss == 'sl1' else None                        #中心点x,y的offset
        self.crit_wh = torch.nn.L1Loss(reduction='sum') if opt.dense_wh else \
            NormRegL1Loss() if opt.norm_wh else \
                RegWeightedL1Loss() if opt.cat_spec_wh else self.crit_reg       #宽高回归loss
        self.opt = opt
        self.emb_dim = opt.reid_dim #reid特征长度
        self.nID = opt.nID          #所有目标的ID数
        self.classifier = nn.Linear(self.emb_dim, self.nID)
        self.IDLoss = nn.CrossEntropyLoss(ignore_index=-1)  #ID loss
        #self.TriLoss = TripletLoss()
        self.emb_scale = math.sqrt(2) * math.log(self.nID - 1)   
        self.s_det = nn.Parameter(-1.85 * torch.ones(1))
        self.s_id = nn.Parameter(-1.05 * torch.ones(1))
 
    def forward(self, outputs, batch):
        opt = self.opt
        hm_loss, wh_loss, off_loss, id_loss = 0, 0, 0, 0
        for s in range(opt.num_stacks):
            output = outputs[s]
            if not opt.mse_loss:
                output['hm'] = _sigmoid(output['hm'])
 
            hm_loss += self.crit(output['hm'], batch['hm']) / opt.num_stacks    #修改过后的focal loss
            #wh_loss
            if opt.wh_weight > 0:
                if opt.dense_wh:
                    mask_weight = batch['dense_wh_mask'].sum() + 1e-4
                    wh_loss += (
                                   self.crit_wh(output['wh'] * batch['dense_wh_mask'],
                                                batch['dense_wh'] * batch['dense_wh_mask']) /
                                   mask_weight) / opt.num_stacks
                else:
                    wh_loss += self.crit_reg(
                        output['wh'], batch['reg_mask'],
                        batch['ind'], batch['wh']) / opt.num_stacks
            #xyoffset_loss
            if opt.reg_offset and opt.off_weight > 0:
                off_loss += self.crit_reg(output['reg'], batch['reg_mask'],
                                          batch['ind'], batch['reg']) / opt.num_stacks
            #id loss
            if opt.id_weight > 0:
                id_head = _tranpose_and_gather_feat(output['id'], batch['ind'])
                id_head = id_head[batch['reg_mask'] > 0].contiguous()
                id_head = self.emb_scale * F.normalize(id_head)
                id_target = batch['ids'][batch['reg_mask'] > 0]
                id_output = self.classifier(id_head).contiguous()
                id_loss += self.IDLoss(id_output, id_target)
                #id_loss += self.IDLoss(id_output, id_target) + self.TriLoss(id_head, id_target)
 
        #loss = opt.hm_weight * hm_loss + opt.wh_weight * wh_loss + opt.off_weight * off_loss + opt.id_weight * id_loss
 
        det_loss = opt.hm_weight * hm_loss + opt.wh_weight * wh_loss + opt.off_weight * off_loss  
 
        loss = torch.exp(-self.s_det) * det_loss + torch.exp(-self.s_id) * id_loss + (self.s_det + self.s_id)
        loss *= 0.5
 
        #print(loss, hm_loss, wh_loss, off_loss, id_loss)
 
        loss_stats = {'loss': loss, 'hm_loss': hm_loss,
                      'wh_loss': wh_loss, 'off_loss': off_loss, 'id_loss': id_loss}
        return loss, loss_stats

实战

环境配置

conda create -n FairMOT python=3.8
conda activate FairMOT
# conda
conda install pytorch==1.7.0 torchvision==0.8.0 cudatoolkit=10.2 -c pytorch
# pip+CPU
pip install torch==1.7.0+cpu torchvision==0.8.0+cpu torchaudio==0.7.0 -f https://download.pytorch.org/whl/torch_stable.html
# pip+CUDA 10.1
pip install torch==1.7.0+cu101 torchvision==0.8.0+cu101 torchaudio==0.7.0 -f https://download.pytorch.org/whl/torch_stable.html
pip install -r requirements.txt  # cython-bbox注掉单独装

# 装cython_bbox
pip install cython
# pip装
pip install git+https://github.com/yanfengliu/cython\_bbox.git # 失败的话源码装
# 源码装
git clone https://github.com/yanfengliu/cython_bbox.git
cd cython_bbox-master
python setup.py install

# 装ffmpeg, src/demo.py中将所有帧存成视频时用到
# linux
apt update
apt install ffmpeg
# windows 
参考:https://blog.csdn.net/gaowenhui2008/article/details/131003056

编译项目

cd src/lib/models/networks
git clone -b pytorch_1.7 https://github.com/ifzhang/DCNv2.git
cd DCNv2
sh make.sh

编译可能遇到的问题见:FairMOT配置(VS2019+Win10+CUDA11.0)_importerror: cannot import name '_nt_quote_args' f_星空下的仰望者的博客-CSDN博客

下载预训练模型

链接:https://pan.baidu.com/s/1MlXrSGiXYF2cV07Arbw7EQ
提取码:zwh9

跑通demo

运行以下代码,详细参数见`src/lib/opts.py`,默认视频放在../videos中

cd src

# CPU
python demo.py --task mot --load_model ../models/all_dla34.pth --conf_thres 0.4 --gpus -1 --output-root ../results

# GPU
python demo.py --task mot --load_model ../models/all_dla34.pth --conf_thres 0.4  --output-root ../results

cd ../

<frame>, <id>, <bb_left>, <bb_top>, <bb_width>, <bb_height>, <conf>, <x>, <y>, <z>

每行代表一个检测的物体。第一列代表第几帧,第二个代表轨迹编号(或者是物体ID),bb开头的4个数代表物体框的左上角坐标及长宽。conf代表置信度,最后3个是MOT3D用到的内容,2D检测总是为-1.

训练数据集准备

以CUHKSYSU数据集为例,项目文件夹下新建`dataset/MOT`,将CUHK-SYSU数据集解压到这里,重命名为`CUHKSYSU`,目录结构如下(训练自己的数据集也要弄成这样的格式,images和labels_with_ids的名称不能改):

 

参考:CVPR 2020 多目标跟踪算法 FairMOT代码demo运行及训练_fairmot运行_村民的菜篮子的博客-CSDN博客

 train

修改`src/lib/cfg/data.json`文件。一会儿运行`experiments/all_dla34.sh`会进入到`src`目录下,所以这里的root用`../dataset`(除了train其他的貌似都没用到)

{
    "root":"../dataset/MOT",
    "train":
    {
        "cuhksysu":"./data/cuhksysu.train"
    },
    "test_emb":
    {
        "mot15":"./data/cuhksysu.val"
    },
    "test":
    {
        "mot15":"./data/cuhksysu.val"
    }
}

 `src/lib/opts.py`中修改数据集位置:

--data_cfg '../src/lib/cfg/data.json'

--data_dir "../datset"

运行以下代码开启训练:

cd src

python train.py --task mot --exp_id all_dla34 --num_epochs 10 --gpus 0 --batch_size 2 --load_model ../models/ctdet_coco_dla_2x.pth --num_workers 8

cd ..

开启训练之后如下:

训练log解读: [5]表示第5轮训练,500表示batch的数量(总图片为1000,bachsize为2),loss是总损失,hm是类别损失,wh是宽高损失,off是中心点偏移损失,id_loss是id损失,time是总时间。

模型与日志参数默认保存在exp/mot/all_dla34下, log.txt中记录了损失函数变化:

val

将`mot16`数据集解压到`dataset/MOT16`目录下,包含train和test两个文件夹,运行以下命令。若想保存视频,将`track.py`最后一行的`save_videos=False`改成`save_videos=True`,结果保存在`dataset/MOT16/outputs`下。数据集介绍参考:多目标跟踪数据集 :mot16、mot17数据集介绍以及多目标跟踪指标评测_一个小呆苗的博客-CSDN博客

cd src
python track.py --task mot --val_mot16 True --load_model ../exp/mot/all_dla34/model_last.pth --conf_thres 0.6 --save_all 
cd ..

启动之后:

 评价指标

MOTA: 跟踪的准确度,和出现FN,FP,IDs的数量负相关,可能出现负值。1为最佳情况,数值越高代表跟踪精确度越好

MOTP:衡量目标位置的精确程度;

IDF1:IDF1指标代表被检测和跟踪的目标中获取正确的ID的检测目标的比例,综合考虑ID准确率和ID召回率,代表两者的调和均值。

其中,IDP代表ID跟踪的准确率,IDR代表ID跟踪的召回率。IDF1指标更聚焦于跟踪算法跟踪某个目标的时间长短,考察跟踪的连续性和重识别的准确性。IDF1以1为最佳情况,数值越高代表跟踪特定目标的精度越好。

IDS: ID改变的总数量;

MT:代表在80%的帧中被正确跟踪的轨迹占所有轨迹的比重;

ML:代表在80%的帧中没有被正确跟踪的轨迹占所有轨迹的比重。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值