deepsort可以跟踪图像中多个目标,某些应用下需要跟踪其中某个感兴趣的目标。此处对deepsort程序的变化是,用鼠标点击感兴趣目标,对此目标加特别标注,使其跟踪框加粗而明显,有助于单独提取感兴趣目标的坐标。
本博文用于记录实现过程,便于以后再用到此功能时,方便查找。python函数太多,留点记录防止遗忘。希望CSDN长命百岁。
deepsort中 track.py每帧处理图像过程:
TTS = Target_designation(title='', img =[], target_xy = np.array([0, 0]),
new_click = False, target_label = 0) # 指定目标参数类初始化
for frame_idx, (path, img, ...) in enumerate(dataset): # 帧处理起始点
pred = model(img, ...) # yolov5 detector
pred = non_max_suppression(pred, ...)
det = pred
annotator = Annotator(im0, line_width=1, pil=not ascii) #初始化Annotator类的实例。
outputs = deepsort.update(det, im0) # deepsort track
for j, (output, conf) in enumerate(zip(outputs, confs)):
bboxes = output[0:4] # each track box
id = output[4] # track box id
annotator.box_label(bboxes, id, ...) # 显示跟踪框
cv2.setMouseCallback(str(p), TTS.on_EVENT_LBUTTONDOWN) # 调用回调函数
cv2.imshow(str(p), im0)
cv2.waitKey(20)
对每帧图像img,yolov5检测出目标pred,再换成det,经deepsort跟踪过程,得到output,为det的跟踪框:bboxes和id。针对每个bbox,id,采用标注函数annotator.box_label在图像im0上画出。最后每帧图像显示:
cv2.imshow(title=str(p), img=im0)
cv2.imshow有两个参数,im0是显示的图像,title是显示窗口。
在bbox的循环中,插入鼠标回调函数cv2.setMouseCallback(),其中指定在哪个窗口,p是输入视频的路径,窗口title标题显示此路径。
在deepsort跟踪图像中指定目标。如图,用鼠标指定有跟踪框的目标,点击后该跟踪框显示为绿色粗线框。
回调函数cv2.setMouseCallback指定了鼠标响应函数TTS.on_EVENT_LBUTTONDOWN,TTS是类Target_designation的实例,将on_EVENT_LBUTTONDOWN作为类 Target_designation中的函数定义。
def on_EVENT_LBUTTONDOWN(self, event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN: # 鼠标左键按下时候的操作
xy = "%d,%d" % (x, y)
print(xy) # 控制台显示当前像素坐标
cv2.circle(self.img, (x, y), 10, (255, 0, 0), thickness=-1) # 画实心圆
self.target_xy=np.array([x, y])
self.new_click = True
cv2.imshow(self.title, self.img)
在此定义类Target_designation,作为指定目标参数保存。setMouseCallback的响应函数放到此类中,有助于将鼠标点击获得的图像窗口中指定目标的xy坐标存入类变量,供其他函数使用。
响应函数中,鼠标左键按下时,cv2.circle()画实心圆,后面需跟随imshow才能显示。imshow需要两个参数,而响应函数的参数中,只有param来传递一个参数。因此用类变量给出这两个参数更好,可以省略param参数的使用。
当然响应函数中可不画实心圆,只是为直观显示点击效果。当完成鼠标点击指定目标,显示绿色粗框的过程后,可以去除画实心圆语句。
Target_designation类的初始化:
class Target_designation:
def __init__(self, title, img, target_xy, new_click, target_label):
self.target_xy = np.array([0,0])
self.new_click = False
self.target_label = 0
self.title = " "
self.img = []
从外部对类变量的改变:
def save_target(self, title, img, target_xy, new_click, target_label ):
self.target_xy = target_xy #指定目标的坐标
self.new_click = new_click #鼠标左键按下指示
self.target_label = target_label #指定目标id
self.title = title # 窗口
self.img = img # 窗口中的图像
return self
判断当前目标框是否指定目标:
def target_juge(self, bbox, id ):
if self.new_click:
bbox_center = np.array([(bbox[2]-bbox[0])/2, (bbox[3]-bbox[1])/2])
target_center = self.target_xy-np.array([bbox[0], bbox[1]])
dif_center1 = bbox_center - target_center
dif_center2= hypot(dif_center1[0], dif_center1[1])
# p4 is the diagonal of det box
p1 = bbox[0:2]
p2 = bbox[2:4]
p3 = p1 - p2
p4 = hypot(p3[0], p3[1]) # hypot计算两个坐标点间的距离sqrt(x^2+y^2)
if dif_center2 <= 0.25*p4:
if id:
self.target_label = id
self.new_click = False
比较当前目标框中心与指定目标中心的距离,若小于当前目标框对角线的1/4,则认为所点击的指定目标就是当前目标框,判断成功则将当前目标框id保存为指定目标id,在标注函数annotator.box_label中,目标框id = 指定目标id时,将目标框显示为绿色粗框,与其他目标框相区别。
Target_designation类实例语句应在帧循环之外定义,否则每帧图像初始化后,将失去之前保存的指定目标参数值。