1. 环境
Ubuntu 20.04,Python 3.10,torch1.13 + cuda 11.6,BoxMOT 11.0.8 + ultralytics 8.2.0代码 + ultralytics 8.3.86 + ultralytics-thop 2.0.14 ,代码选择更高版本也是可以的,只是我在检测部分的改进是基于8.2.0
2. 代码准备
2.1 BoxMOT 11.0.8
- git clone https://github.com/mikel-brostrom/boxmot.git 或者直接下载压缩包
- cd boxmot
- pip install poetry
- poetry install --with yolo # installed boxmot + yolo dependencies
- poetry shell # activates the newly created environment with the installed dependencies
注意:
- 在第四步安装 boxmot 和 yolo 依赖的时候 ,会出现Runtime Error,通过更换源解决,设置清华源:poetry config repositories.pypi https://pypi.tuna.tsinghua.edu.cn/simple ,再重新poetry install --with yolo 即可解决,或者 poetry install --with yolo --no-cache
- 默认安装torch 2.2.2 + cuda12.x,可能与服务器的cuda环境冲突,重新安装版本即可,如我的是torch1.13 + cuda 11.6,pip install torch==1.13.0+cu116 torchvision==0.14.0+cu116 torchaudio==0.13.0 --extra-index-url https://download.pytorch.org/whl/cu116
- 默认下载 ultralytics 最新版本,这个影响不是很大,如果需要指定版本,在安装所有依赖后,通过 pip install ultralytics == 8.2.0 指定版本
- 官方的第五步 poetry shell 不是必须的,第四步安装完成就可以使用了
2.2 ultralytics 8.2.0或者其他版本
- git clone GitHub - ultralytics/ultralytics: Ultralytics YOLO11 🚀 或者直接下载压缩包
- 只需要准备 ultralytics 里面的 ultralytics 文件夹,即包含assets,cfg,nn等文件夹的ultralytics,这部分也是检测阶段可能涉及改进的代码部分
- 将这个 ultralytics 文件夹放在boxmot/tracking目录下,以便后续代码的调用
最终的文件目录结构是这样的
3. 修改代码
修改ultralytics-trackers-track.py代码,防止 reset() Attributor Error 和 Boxes 错误,找到函数def on_predict_postprocess_end(predictor: object, persist: bool = False) -> None:
具体的错误信息我也忘了,建议先运行track.py进行测试,如果出现相同的错误,可以修改和我一样的,注意:ultralytics不同版本的track.py代码可能不一样,我8.2.0是这样的
def on_predict_postprocess_end(predictor: object, persist: bool = False) -> None:
"""
Postprocess detected boxes and update with object tracking.
Args:
predictor (object): The predictor object containing the predictions.
persist (bool, optional): Whether to persist the trackers if they already exist. Defaults to False.
"""
path, im0s = predictor.batch[:2]
is_obb = predictor.args.task == "obb"
is_stream = predictor.dataset.mode == "stream"
for i in range(len(im0s)):
tracker = predictor.trackers[i if is_stream else 0]
vid_path = predictor.save_dir / Path(path[i]).name
if not persist and predictor.vid_path[i if is_stream else 0] != vid_path:
# 添加判断
if hasattr(tracker, "reset") and callable(tracker.reset):
tracker.reset()
predictor.vid_path[i if is_stream else 0] = vid_path
# 获取检测结果,注释原有的
# det = (predictor.results[i].obb if is_obb else predictor.results[i].boxes).cpu().numpy()
det = predictor.results[i].obb if is_obb else predictor.results[i].boxes
# 确保 det 是 numpy 数组,而不是 Boxes 对象
if isinstance(det, torch.Tensor):
det = det.cpu().numpy()
elif hasattr(det, 'data') and isinstance(det.data, torch.Tensor):
det = det.data.cpu().numpy()
else:
raise TypeError(f"Unsupported detection type: {type(det)}")
if len(det) == 0:
continue
# 更新跟踪器
tracks = tracker.update(det, im0s[i])
if len(tracks) == 0:
continue
# 更新结果
idx = tracks[:, -1].astype(int)
predictor.results[i] = predictor.results[i][idx]
update_args = dict()
update_args["obb" if is_obb else "boxes"] = torch.as_tensor(tracks[:, :-1])
predictor.results[i].update(**update_args)
4. 运行track.py进行测试
python tracking/track.py --scource xxx.mp4 --yolo-model weights/yolov8n.pt --reid-model weights/osnet_x0_25_msmt17.pt --tracking-method botsort --save
具体参数可以在 tracking/track.py 中修改,检测和跟踪的 pt 文件在运行时会默认下载放到 tracking/weights 文件夹下,建议先下载再运行防止运行过慢,跟踪模型pt下载地址:Model Zoo — torchreid 1.4.0 documentation
经过上面的修改后,应该可以运行除了 hybridsort 和 strongsort 的其他跟踪器
5. 运行 hybridsort 和 strongsort 跟踪器
5.1 解决 --tracking-method hybridsort 时,可能出现 ValueError: too many values to unpack (expected 4),修改 boxmot-motion-kalman_filters-aabb-xysr_kf.py 为如下:
def unfreeze(self):
if self.attr_saved is not None:
new_history = deepcopy(list(self.history_obs))
self.__dict__ = self.attr_saved
self.history_obs = deque(list(self.history_obs)[:-1], maxlen=self.max_obs)
occur = [int(d is None) for d in new_history]
indices = np.where(np.array(occur) == 0)[0]
if len(indices) < 2:
# 如果没有足够的观测值,提前返回
return
index1, index2 = indices[-2], indices[-1]
box1, box2 = new_history[index1], new_history[index2]
# 输出调试信息
# print("box1:", box1)
# print("box2:", box2)
# 确保有正确数量的元素用于解包
# 注意:需要保留所有5个元素用于update,但只使用前4个用于计算
# 提取前4个元素用于计算
x1, y1, s1, r1 = box1.flatten()[:4]
x2, y2, s2, r2 = box2.flatten()[:4]
# 如果有第5个元素,提取它
conf1 = box1.flatten()[4] if box1.shape[0] >= 5 else 0
conf2 = box2.flatten()[4] if box2.shape[0] >= 5 else 0
w1, h1 = np.sqrt(s1 * r1), np.sqrt(s1 / r1)
w2, h2 = np.sqrt(s2 * r2), np.sqrt(s2 / r2)
time_gap = index2 - index1
dx, dy = (x2 - x1) / time_gap, (y2 - y1) / time_gap
dw, dh = (w2 - w1) / time_gap, (h2 - h1) / time_gap
dconf = (conf2 - conf1) / time_gap if conf1 != 0 and conf2 != 0 else 0
for i in range(index2 - index1):
x, y = x1 + (i + 1) * dx, y1 + (i + 1) * dy
w, h = w1 + (i + 1) * dw, h1 + (i + 1) * dh
s, r = w * h, w / float(h)
# 计算置信度(如果有的话)
conf = conf1 + (i + 1) * dconf if conf1 != 0 and conf2 != 0 else 0
# 创建包含所有必要元素的new_box
if conf != 0:
new_box = np.array([x, y, s, r, conf]).reshape((5, 1))
else:
new_box = np.array([x, y, s, r]).reshape((4, 1))
self.update(new_box)
if not i == (index2 - index1 - 1):
self.predict()
self.history_obs.pop()
self.history_obs.pop()
5.2 解决 --tracking-method strongsort 时,无法运行,原因是strongsort 没有添加可视化功能,修改track.py :
# for r in results:
# img = yolo.predictor.trackers[0].plot_results(r.orig_img, args.show_trajectories)
# if args.show is True:
# cv2.imshow('BoxMOT', img)
# key = cv2.waitKey(1) & 0xFF
# if key == ord(' ') or key == ord('q'):
# break
for r in results:
if hasattr(yolo.predictor.trackers[0], 'plot_results'):
img = yolo.predictor.trackers[0].plot_results(r.orig_img, args.show_trajectories)
else:
# Use an alternative plotting method or skip plotting
img = r.orig_img # or r.plot() if available
if args.show is True:
cv2.imshow('BoxMOT', img)
key = cv2.waitKey(1) & 0xFF
if key == ord(' ') or key == ord('q'):
break