多目标跟踪MOT项目在Github中比较完整有:BOXMOT , 由mikel brostrom提供。在以前的版本中,有yolov5+deepsort(版本v3-v5), yolov8+strongsort(版本v6-v9),直至演变到v10,名称BOXMOT。
BOXMOT提供三种对象检测器:yolov8, yolo_nas, yolox; 支持多个跟踪器:BoTSORT, DeepOCSORT, OCSORT, Hybridsort, ByteTrack, StrongSORT 。以前常见的DeepSort在此由增强型StrongSORT替代。
boxmot安装
安装环境:Ubuntu18.04,python 3.8,已建有虚拟环境。
将boxmot从github克隆到本地,建立yolo_tracking目录:
git clone https://github.com/mikel-brostrom/yolo_tracking.git
cd yolo_tracking
pip install -v -e .
第三个命令pip install -v -e. 相当于 python setup.py develop,即根据setup.py执行安装,其中“develop”参数将软件包以开发模式安装到Python环境中,以便在开发过程中能够即时反映源代码的修改。
完成BOXMOT安装后,看看yolo_tracking下面有什么:
图1 yolo_tracking目录
见图1, BOXMOT支持的三个对象检测器定义文件yolonas.py, yolov8.py, yolox.py在examples/detectors目录。跟踪器tracker在boxmot/trackers目录下。 boxmot/appearance目录是tracker所需使用的外观识别REID模块。boxmot/configs为tracker的参数构造文件。boxmot/motion目录是运动预测用Kalman滤波器。
下一步根据需要,安装三个对象检测器。
对象检测器yolov8需要安装ultralytics python库,需注意,BOXMOT适用ultralytics v8.0.146,而最新的版本不适用。
安装ultralytics到yolo_tracking目录,操作如下:
先删除虚拟环境下和系统中可能安装的ultralytics模块:
pip uninstall ultralytics
克隆ultralytics v8.0.146
git clone https://github.com/mikel-brostrom/ultralytics.git
此操作在home目录下产生ultralytics目录。我们需要将ultralytics二级目录:~/ultralytics/ultralytics移动到yolo_tracking目录下,完成安装ultralytics。这样在python程序调试时,可以跟踪到ultralytics模块。为了防止混淆,将examples/track.py和val.py中安装ultralytics语句注释掉:
#__tr = TestRequirements()
#__tr.check_packages(('ultralytics @ git+https://github.com/mikel-brostrom/ultralytics.git', )) # install ultralytics
现在,已实现基础对象检测器yolov8的运行环境,可执行跟踪track.py和评估val.py程序。
运行yolov8s+strongsort对输入视频进行车辆跟踪示例:
python examples/track.py \
--yolo-model yolov8s \
--reid-mode osnet_x0_25_market1501.pt \
--source ~/yolo_tracking/MOT16-13-h264.mp4 \
--save \
--show \
--classes 2 \
--tracking-method strongsort
若已下载yolov8s权重文件yolov8s.pt,可在–yolo-model 变量中指定文件路径,若没下载,则track.py根据"yolov8s"自动从网上下载。
yoloNAS需安装super-gradients:
pip install super-gradients
运行yoloNAS,实现的示例:
python examples/track.py \
--yolo-model yolo_nas_s \
--reid-mode osnet_x0_25_market1501.pt \
--source ~/yolo_tracking/MOT16-13-h264.mp4 \
--save \
--show \
--classes 2 \
--tracking-method strongsort
BOXMOT使用yoloNAS不能对跟踪对象类进行筛选,而把图像中所有符合COCO数据集类型的对象都提取,大数量目标对跟踪器造成很大负担,运行速度慢。
在yolonas.py的postprocess函数中增加类型过滤:
def postprocess(self, path, preds, im, im0s):
results = []
for i, pred in enumerate(preds):
if pred is None:
pred = torch.empty((0, 6))
r = Results(
path=path,
boxes=pred,
orig_img=im0s[i],
names=self.names
)
results.append(r)
else:
pred[:, :4] = ops.scale_boxes(im.shape[2:], pred[:, :4], im0s[i].shape)
# filter boxes by classes ############################################
pred = pred[torch.isin(pred[:, 5].cpu(), torch.as_tensor(self.args.classes))] # added by someone
r = Results(
path=path,
boxes=pred,
orig_img=im0s[i],
names=self.names
)
results.append(r)
return results
YOLOX官网克隆到本地,产生YOLOX目录。
git clone https://github.com/Megvii-BaseDetection/YOLOX.git
将YOLOX目录下三个子目录:yolox, tools, exps 复制到yolo_tracking,完成YOLOX环境。
实现YOLOX对象检测器的跟踪示例
python examples/track.py \
--yolo-model yolox_s \
--reid-mode osnet_x0_25_market1501.pt \
--source ~/yolo_tracking/MOT16-13-h264.mp4 \
--save \
--show \
--tracking-method strongsort
此BOXMOT版本v10.043在使用YOLOX上有限制,从track.py程序下载的权重文件yolox_s仅支持一个类型“person”的对象检测,不支持其他对象检测。从YOLOX Github下载权重文件yolox_s.pth支持COCO数据集的80个类型,这里需要修改:
1 YOLOX官网下载的权重文件yolox_s.pth,80个类型; BOXMOT,track.py下载的yolox_s.pt,1个person类型。在examples/detectors/yolox.py中如下修改:
def __init__(self, model, device, args):
self.args = args
self.pt = False
self.stride = 32 # max stride in YOLOX
# model_type one of: 'yolox_n', 'yolox_s', 'yolox_m', 'yolox_l', 'yolox_x'
model_type = self.get_model_from_weigths(YOLOX_ZOO.keys(), model)
if model_type == 'yolox_n':
exp = get_exp(None, 'yolox_nano')
else:
exp = get_exp(None, model_type)
LOGGER.info(f'Loading {model_type} with {str(model)}')
# download crowdhuman bytetrack models
if not model.exists() and model.stem == model_type:
LOGGER.info('Downloading pretrained weights...')
gdown.download(
url=YOLOX_ZOO[model_type + '.pt'],
output=str(model),
quiet=False
)
# "yolox_s.pt" 表示num_classes =1, "yolox_s.pth"表示num_classes = 80。
# boxmot下载的ckpt只处理“person”类型,
# github.com/Megvii-BaseDetection/YOLOX提供的ckpt用于num_classes,可处理多种类型。
# needed for bytetrack yolox people models
# update with your custom model needs
exp.num_classes = 1
self.num_classes = 1
elif model.stem == model_type:
exp.num_classes = 1
self.num_classes = 1
_, file_extension = os.path.splitext(str(model))
if file_extension == ".pth":
exp.num_classes = 80
self.num_classes = 80
if exp.num_classes ==1:
self.img_normal = True #num_classes = 1, BOXMOT format: 0.0-1.0
else:
self.img_normal = False #num_classes = 80, YOLOX website ckpt format: 0-255
ckpt = torch.load(
str(model),
map_location=torch.device('cpu')
)
self.model = exp.get_model()
self.model.eval()
self.model.load_state_dict(ckpt["model"])
self.model = fuse_model(self.model)
self.model.to(device)
self.model.eval()
这里,对 YoloXStrategy(YoloInterface)类增加类变量:self.num_classes,适应来自不同网站的yolox权重文件。
2 YOLOX官网yolox detector所处理的图像为0-255数据,而BOXMOT yolox detector所处理图像数据为0.0 - 1.0,需要针对不同权重文件对图像输入进行变换。对 YoloXStrategy(YoloInterface)类增加类变量:self.img_normal。
更改yolox.py postprocess
def postprocess(self, path, preds, im, im0s):
results = []
for i, pred in enumerate(preds):
pred = postprocess(
pred.unsqueeze(0), # YOLOX postprocessor expects 3D arary
self.num_classes, # 1 num_classes
conf_thre=0.5, #0.1
nms_thre=0.7, # 0.45
class_agnostic=True,
)[0]
更改ultralytics/engine/predictor.py preprocess函数
def preprocess(self, im):
"""Prepares input image before inference.
Args:
im (torch.Tensor | List(np.ndarray)): BCHW for tensor, [(HWC) x B] for list.
"""
not_tensor = not isinstance(im, torch.Tensor)
if not_tensor:
im = np.stack(self.pre_transform(im))
im = im[..., ::-1].transpose((0, 3, 1, 2)) # BGR to RGB, BHWC to BCHW, (n, 3, h, w)
im = np.ascontiguousarray(im) # contiguous
im = torch.from_numpy(im)
img = im.to(self.device)
img = img.half() if self.model.fp16 else img.float() # uint8 to fp16/32
if not_tensor: # -------------------------------------------------------------------------
if not self.model.img_normal:
return img # yolox num_classes = 80 , img 取值 0-255
img /= 255 # 0 - 255 to 0.0 - 1.0 # yolov8, yolo_nas and yolox num_classes = 1的ckpt,img 取值 0.0 - 1.0 。
return img
注:如果更改BOXMOT中默认的yolox单一类型处理模式,由于对ultralytics/engine/predictor.py preprocess方法做了修改,会涉及到yolov8和yoloNAS,所以:
yolov8: ultralytics/nn/autobackend.py, 类 AutoBackend, 增加类变量 self.img_normal = True
yoloNAS:examples/detectors/yolonas.py, 类 YoloNASStrategy, 增加类变量 self.img_normal = True
当然,如果不改变BOXMOT默认的yolox,则无需改变yolov8和yoloNAS。
对比BOXMOT 默认yolox_s.pt和YOLOX官网yolo_s.pth,两者分别运行val.py
python examples/val.py \
--yolo-model examples/weights/yolox_s.pt \
--tracking-method deepocsort \
--benchmark MOT17
python examples/val.py \
--yolo-model examples/weights/yolox_s.pth \
--tracking-method deepocsort \
--benchmark MOT17
得到如下结果(取MOT17 - FRCNN每个序列的前10帧):
HOTA MOTA MOTP IDF1
yolox_s.pt 68.294 67.268 81.267 81.403
yolox_s.pth 60.038 52.223 78.765 70.531
这因为yolox_s.pt是针对拥挤行人情况的权重,而yolox_s.pth则适用于COCO各种类型,所以评价指标有提高。