mot数据集_【多⽬标跟踪】搞不懂MOT数据集,会跑代码有
啥⽤!
基本数据集介绍
MOT 数据集
17 年的视频和 16 年⼀模⼀样,只是提供了三个检测器,相对来说更公平。也是现在论⽂的主流数据集。
19 年的是针对特别拥挤情形的数据集,只有 CVPR19 ⽐赛时才能提交。
KITTI 数据集
MOT16
针对 MOT16 数据集介绍⼀下,它与 MOT15 数据的部分标注信息可能存在差别,需要注意~
MOT16 数据集是在 2016 年提出来的⽤于衡量多⽬标跟踪检测和跟踪⽅法标准的数据集,专门⽤于⾏⼈跟踪。总共有 14 个视频,训练集和测试集各 7 个,这些视频每个都不⼀样,按照官⽹的说法,它们有
些是固定摄像机进⾏拍摄的,有些是移动摄像机进⾏拍摄的,⽽且拍摄的⾓度各不⼀样(低、中、⾼度进⾏拍摄),拍摄的条件不⼀样,包括不同的天⽓,⽩天或者夜晚等,还具有⾮常⾼的⼈密度,总之,这个数据集⾮常具有挑战性。
MOT16 数据集使⽤的检测器是 DPM,这个检测器在检测 “⼈” 这个类别上具有较好的性能。MOTChallenge 官⽹截图
这些视频的主要信息如下:包括 FPS、分辨率、视频时长、轨迹数、⽬标书、密度、静⽌或者移动拍摄、低中⾼⾓度拍摄、拍摄的天⽓条
件等。
这些视频的检测框的信息如下:
MOT16 数据的⽬录结构如下所⽰:包含训练集和测试集(各有 7 个视频)
每个⼦⽂件夹(如 MOT16-01)代表⼀个视频转换后的数据集,包含⼏个⽂件或者⽂件夹,其⽬录结构与具体含义如下:
MOT16/
├── test
│ ├── MOT16-01
│ │ ├── det
│ │ │ └──
│ │ ├── img1
│ │ │ ├── 000001.jpg
│ │ │ ├── xxxxxx.jpg
│ │ │ └── 000450.jpg
│ │ └── seqinfo.ini
│ ├── MOT16-03
│ ├── MOT16-06
│ ├── MOT16-07
│ ├── MOT16-08
│ ├── MOT16-12
│ └── MOT16-14
└── train
├── MOT16-02
│ ├── det
│ │ └──
│ ├── gt
│ │ └── gt.txt
│ ├── img1
│ │ ├── 000001.jpg
│ │ ├── xxxxxx.jpg
│ │ └── 000600.jpg
│ └── seqinfo.ini
├── MOT16-04
├── MOT16-05
├── MOT16-09
├── MOT16-10
├── MOT16-11
└── MOT16-13seqinfo.ini
⽂件内容如下,主要⽤于说明这个⽂件夹的⼀些信息,⽐如图⽚所在⽂件夹 img1,帧率,视频的长度,图⽚的长和宽,图⽚的后缀名。[Sequence]
name=MOT16-05
imDir=img1
frameRate=14
seqLength=837
imWidth=640
imHeight=480
imExt=.
这个⽂件中存储了图⽚的检测框的信息 (这⾥⽤ MOT16-05 ⽂件来说明,该⽂件下 img1 ⽂件下有 837 张图⽚,代表视频的每⼀帧)从左到右分别代表的意义是第 1 个值:frame: 第⼏帧图⽚
第 2 个值:id: 这个检测框分配的 id,(由于暂时未定,所以均为 - 1)
第 3-6 个值:bbox (四位): 分别是左上⾓坐标(top, left)和宽 (width) ⾼ (height)
第 7 个值:conf:这个 bbox 包含物体的置信度,可以看到并不是传统意义的 0-1,分数越⾼代表置信度越⾼
第 8、9、10 个值:MOT3D (x,y,z): 是在 MOT3D 中使⽤到的内容,这⾥关⼼的是 MOT2D,所以都设置为 - 1
可以看出以上内容主要提供的和⽬标检测的信息没有区别,所以也在⼀定程度上可以⽤于检测器的训练。
需要数据集或者模型的,扫码关注并回复【MOT】即可img1 ⽂件夹
这⾥⾯是将视频的每⼀帧抽取出来后的图⽚,图⽚格式是 jpg,按照视频流的顺序进⾏命名,如:xxxxxx. ⽂件(train 训练集才有)
从左到右代表的含义是:第 1 个值:frame: 第⼏帧图⽚
第 2 个值:ID: 也就是轨迹的 ID
第 3-6 个值:bbox: 分别是左上⾓坐标 (top, left) 和宽(width)⾼(height)
第 7 个值:是否忽略:0 代表忽略 (A value of 0 means that this particularinstance is ignored in the evaluation, while a value
of1 is used to mark it as active.)
第 8 个值:classes: ⽬标的类别个数(这⾥是驾驶场景包括 12 个类别),7 代表的是静⽌的⼈。第 8 个类代表错检,9-11 代表被遮挡的类别,12 代表反射,如下图中的第⼆列,可以看到店⾯的玻璃反射了路⼈的背景。第 9 个值:代表⽬标运动时被其他⽬标包含、覆盖、边缘裁剪的情况。(The last number shows the visibility ratio of each bounding box. This can be due to occlusion by
anotherstatic or moving object, or due to image border cropping.)
举个例⼦(该例⼦所在的视频是 MOT16-05),在该视频中,有⼀位穿着类似于⽜仔⾐的⽼奶奶出现在视频的 1-381 帧,其中第 1 和第376 帧视频截图如下,第 1 帧中这位⽼奶奶完全在视野中,所以,下⾯标注的第⼀⾏的最后⼀个值 1 代表没有被覆盖,在 376 帧的时候,⽼奶奶的⾝影部分已经超出视野了,所以下⾯标注第⼆⾏的最后⼀个值 0.56689 表⽰被遮挡 50% 左右。
1,1,17,150,77,191,1,1,1
376,1,459,-27,258,594,1,1,0.56689需要注意的是 ⽂件中的标注是按照轨迹顺序来标注的,什
么意思呢,如果你打开MOT16-05 的 gt.txt ⽂件你会发现从 1-381 ⾏都是 轨迹 1 (上图绿框中的⽼奶奶)的标注,382-717 ⾏ 是轨迹 2 的标注,以此类推...
FairMOT 项⽬数据加载
这⾥需要注意的是:MOT15 和 MOT16 的标志貌似有些差别,MOT15 在训练测试之前需要经过⼀步预处理,即在 FairMOT 项⽬中
src/gen_labels_15.py ⽂件。
FairMOT 项⽬加载数据的基本代码如下,最后通过 torch.utils.data.DataLoader 进⾏加载,简单的放出来,需要细细钻研~
class JointDataset(LoadImagesAndLabels): # for training
default_resolution = [1088, 608]
mean = None
std = None
num_classes = 1
def __init__(self, opt, root, paths, img_size=(1088, 608), augment=False, transforms=None):
self.opt = opt
dataset_names = paths.keys()
self.img_files = OrderedDict()
self.label_files = OrderedDict()
self.tid_num = OrderedDict()
self.tid_start_index = OrderedDict()
self.num_classes = 1
for ds, path in paths.items():
with open(path, 'r') as file:
self.img_files[ds] = adlines()
self.img_files[ds] = [osp.join(root, x.strip()) for x in self.img_files[ds]] self.img_files[ds] = list(filter(lambda x: len(x) > 0, self.img_files[ds])) self.label_files[ds] = [
for ds, label_paths in self.label_files.items():
max_index = -1
for lp in label_paths:
lb = np.loadtxt(lp)
if len(lb) < 1:
continue
if len(lb.shape) < 2:
img_max = lb[1]
else:
img_max = np.max(lb[:, 1])
if img_max > max_index:
max_index = img_max
self.tid_num[ds] = max_index + 1
last_index = 0
for i, (k, v) in enumerate(self.tid_num.items()):
self.tid_start_index[k] = last_index
last_index += v
self.nID = int(last_index + 1)
self.nds = [len(x) for x in self.img_files.values()]
self.cds = [sum(self.nds[:i]) for i in range(len(self.nds))]
self.nF = sum(self.nds)
self.width = img_size[0]
self.height = img_size[1]
self.max_objs = opt.K
self.augment = augment
print('=' * 80)
拍一拍设置后缀print('dataset summary')
print(self.tid_num)
print('total # identities:', self.nID)
print('start index')
print(self.tid_start_index)
print('=' * 80)
def __getitem__(self, files_index):
for i, c in enumerate(self.cds):
if files_index >= c:
ds = list(self.label_files.keys())[i]
start_index = c
img_path = self.img_files[ds][files_index - start_index]
label_path = self.label_files[ds][files_index - start_index]
imgs, labels, img_path, (input_h, input_w) = _data(img_path, label_path)
for i, _ in enumerate(labels):
if labels[i, 1] > -1:
labels[i, 1] += self.tid_start_index[ds]
output_h = imgs.shape[1] // self.opt.down_ratio
output_w = imgs.shape[2] // self.opt.down_ratio
num_classes = self.num_classes
num_objs = labels.shape[0]
hm = np.zeros((num_classes, output_h, output_w), dtype=np.float32)
wh = np.zeros((self.max_objs, 2), dtype=np.float32)
reg = np.zeros((self.max_objs, 2), dtype=np.float32)
ind = np.zeros((self.max_objs, ), dtype=np.int64)
reg_mask = np.zeros((self.max_objs, ), dtype=np.uint8)
ids = np.zeros((self.max_objs, ), dtype=np.int64)
draw_gaussian = draw_msra_gaussian if self.opt.mse_loss else draw_umich_gaussian for k in range(num_objs):
label = labels[k]
bbox = label[2:]
cls_id = int(label[0])
bbox[[0, 2]] = bbox[[0, 2]] * output_w
bbox[[1, 3]] = bbox[[1, 3]] * output_h