You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

YOLOv7结合DeepSORT追踪时目标ID频繁变更问题求助

问题:YOLOv7+DeepSORT目标追踪时ID频繁变更

我使用YOLOv7和DeepSORT算法对特定区域内的目标进行检测追踪,但所有视频帧中的目标ID始终在变化,3帧截图示例如下:
视频帧1
视频帧2
视频帧3
可以看到每帧的目标ID都不相同。

我使用的代码如下:

#class base virtual zone tracking
import random
import torch
import numpy as np
from models.experimental import attempt_load
from utils.torch_utils import TracedModel
from utils.datasets import letterbox
from utils.plots import plot_one_box, plot_one_box_center
from utils.general import check_img_size, non_max_suppression, scale_coords
import cv2
import time

from google.colab.patches import cv2_imshow
from shapely.geometry import Point
from shapely.geometry.polygon import Polygon

#deep sort
import os
import tensorflow as tf

physical_devices = tf.config.experimental.list_physical_devices('GPU')
if len(physical_devices) > 0:
    tf.config.experimental.set_memory_growth(physical_devices[0], True)

from tensorflow.compat.v1 import ConfigProto
from deep_sort.tracker import Tracker
from deep_sort.detection import Detection
import matplotlib.pyplot as plt
from deep_sort import preprocessing, nn_matching
from tracking_helpers import read_class_names, create_box_encoder
from detection_helpers import *


class YOLOv7:
    def __init__(self, weights: str, image_size:int,device:str):
        self.device = device
        self.weights = weights
        self.model = attempt_load(self.weights, map_location=self.device) # Model Load FP32
        self.stride = int(self.model.stride.max())
        self.image_size = check_img_size(image_size, self.stride)

        if self.device != 'cpu':
            self.half = True
        else:
            self.half = False

        if self.half:
            self.model.half() # FP16
            
        self.names = self.model.module.names if hasattr(self.model , 'module') else self.model.names
        color_values = [[random.randint(0, 255) for _ in range(3)] for _ in range(len(self.names))]
        self.colors = {i:color_values[i] for i in range(len(self.names))}

    def detect(self, raw_image: np.ndarray, conf_thresh =0.45, iou_thresh =0.45, classes = [0]): #default class people
        # Run inference
        if self.device != 'cpu':
            self.model(torch.zeros(1, 3, self.image_size, self.image_size).to(self.device).type_as(next(self.model.parameters())))
        with torch.no_grad():
            image = letterbox(raw_image, self.image_size, stride=self.stride)[0]
            image = image[:, :, ::-1].transpose(2, 0, 1)
            image = np.ascontiguousarray(image)
            image = torch.from_numpy(image).to(self.device)
            image = image.half() if self.half else image.float()
            image /= 255.0
            if image.ndimension() == 3:
                image = image.unsqueeze(0)
            # Inference
            detections = self.model(image, augment=False)[0]
            # Apply NMS
            detections = non_max_suppression(detections, conf_thresh, iou_thresh, classes=classes, agnostic=False)[0]
               # Rescale boxes from img_size to raw image size
            detections[:, :4] = scale_coords(image.shape[2:], detections[:, :4], raw_image.shape).round()
            return detections


    def tracking(self, video_frame, yolo_dets, inside_poly = True, count_objects:bool=False,verbose=False, reID_model_path = "./deep_sort/model_weights/mars-small128.pb", nms_max_overlap:float=1.0, max_cosine_distance:float=0.4, nn_budget:float=None):
        class_names = read_class_names()
        encoder = create_box_encoder(reID_model_path, batch_size=1)
        nms_max_overlap = nms_max_overlap
        metric = nn_matching.NearestNeighborDistanceMetric("cosine", max_cosine_distance, nn_budget)
        tracker = Tracker(metric)
        *xyxy, conf, cls = yolo_dets
        frame = cv2.cvtColor(video_frame, cv2.COLOR_BGR2RGB)
        if yolo_dets is None:
            bboxes = []
            scores = []
            classes = []
            num_objects = 0
                
        else:
            bboxes = yolo_dets[:,:4]
            bboxes[:,2] = bboxes[:,2] - bboxes[:,0] # convert from xyxy to xywh
            bboxes[:,3] = bboxes[:,3] - bboxes[:,1]

            scores = yolo_dets[:,4]
            classes = yolo_dets[:,-1]
            num_objects = bboxes.shape[0]
        #how many object you track
        names = []
        for i in range(num_objects): # loop through objects and use class index to get class name
            class_indx = int(classes[i])
            class_name = class_names[class_indx]
            names.append(class_name)

        names = np.array(names)
        count = len(names)

        if count_objects:
            cv2.putText(frame, "both inside and outside the polygon detection: {}".format(count), (5, 35), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5, (0, 0, 0), 2)

        # DeepSORT tacker work starts here
        features = encoder(frame, bboxes) # encode detections and feed to tracker. [No of BB / detections per frame, embed_size]
        detections = [Detection(bbox, score, class_name, feature) for bbox, score, class_name, feature in zip(bboxes, scores, names, features)] # [No of BB per frame] deep_sort.detection.Detection object

        cmap = plt.get_cmap('tab20b') #initialize color map
        colors = [cmap(i)[:3] for i in np.linspace(0, 1, 20)]

        boxs = np.array([d.tlwh for d in detections])  # run non-maxima supression below
        scores = np.array([d.confidence for d in detections])
        classes = np.array([d.class_name for d in detections])
        indices = preprocessing.non_max_suppression(boxs, classes, nms_max_overlap, scores)
        detections = [detections[i] for i in indices]

        tracker.predict()  # Call the tracker
        tracker.update(detections) #  updtate using Kalman Gain

            
        for track in tracker.tracks:  # update new findings AKA tracks
            #if not track.is_confirmed() or track.time_since_update > 1:
                #continue 
            bbox = track.to_tlbr()
            class_name = track.get_class()
            color = colors[int(track.track_id) % len(colors)]  # draw bbox on screen
            color = [i * 255 for i in color]

            #drawing poly
            #pts = np.array([[6,449], [1052, 2], [1914, 6], [1766, 1074], [2, 1076]])
            #frame = cv2.polylines(frame, [pts], True, (0,0,255), 5)

            #creating poly
            #poli = Polygon([(6,449), (1052, 2), (1914, 6), (1766, 1074), (2, 1076)])
            #center = (int((bbox[0] + bbox[2]) / 2), int((bbox[1] + bbox[3]) / 2)) #center point  ( (x1 + x2) / 2, (y1 + y2) / 2 )
            #point = Point(center)       
            if inside_poly:
                #drawing poly
                pts = np.array([[6,449], [1052, 2], [1914, 6], [1766, 1074], [2, 1076]])
                frame = cv2.polylines(frame, [pts], True, (0,0,255), 5)

                #creating poly
                poli = Polygon([(6,449), (1052, 2), (1914, 6), (1766, 1074), (2, 1076)])
                center = (int((bbox[0] + bbox[2]) / 2), int((bbox[1] + bbox[3]) / 2)) #center point  ( (x1 + x2) / 2, (y1 + y2) / 2 )
                point = Point(center) 

                if poli.contains(point):
                    cv2.rectangle(frame, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), color, 2)
                    cv2.rectangle(frame, (int(bbox[0]), int(bbox[1]-30)), (int(bbox[0])+(len(class_name)+len(str(track.track_id)))*17, int(bbox[1])), color, -1)
                    cv2.putText(frame, class_name + " : " + str(track.track_id),(int(bbox[0]), int(bbox[1]-11)),0, 0.6, (255,255,255),1, lineType=cv2.LINE_AA)
                    cv2.putText(frame, "0", center,0, 0.6, (255,255,255),1, lineType=cv2.LINE_AA)
            else:
                cv2.rectangle(frame, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), color, 2)
                cv2.rectangle(frame, (int(bbox[0]), int(bbox[1]-30)), (int(bbox[0])+(len(class_name)+len(str(track.track_id)))*17, int(bbox[1])), color, -1)
                cv2.putText(frame, class_name + " : " + str(track.track_id),(int(bbox[0]), int(bbox[1]-11)),0, 0.6, (255,255,255),1, lineType=cv2.LINE_AA)

            if verbose == 2:
                print("Tracker ID: {}, Class: {},  BBox Coords (xmin, ymin, xmax, ymax): {}".format(str(track.track_id), class_name, (int(bbox[0]), int(bbox[1]), int(bbox[2]), int(bbox[3]))))
        result = np.asarray(frame)
        result = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
        return result

        

            
if __name__=='__main__':
    yolov7=YOLOv7(weights='yolov7x.pt', device='cpu', image_size=800)
    cap = cv2.VideoCapture('street5sn.mp4')
    torch.cuda.empty_cache()

    #writer
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))  # by default VideoCapture returns float instead of int
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = int(cap.get(cv2.CAP_PROP_FPS))
    codec = cv2.VideoWriter_fourcc(*"DIVX")
    out = cv2.VideoWriter("./output/video_out_track5sn-d2.mp4", codec, fps, (width, height))
    
    while True:
        
        t1 = time.time()
        ret, frame = cap.read()
        if not ret:
            break

        detections=yolov7.detect(frame)
        vir = yolov7.tracking(frame, detections, count_objects = True, inside_poly = False)
        out.write(vir)
        cv2_imshow(vir) #colab imshow kodu
        print("add frame ...")
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    out.release()
    cap.release()
    cv2.destroyAllWindows()        

我使用的是公开仓库代码,未对其他文件做任何修改,请求解决目标ID频繁变更的问题。


问题根源与修复方案

1. 核心错误:每帧重新初始化追踪器

你的tracking方法里,每次调用都会重新创建encoder、metric和Tracker实例:

encoder = create_box_encoder(reID_model_path, batch_size=1)
metric = nn_matching.NearestNeighborDistanceMetric("cosine", max_cosine_distance, nn_budget)
tracker = Tracker(metric)

这会导致每帧的追踪状态完全重置,追踪器无法记住上一帧的目标信息,自然会给每个目标分配新ID。

修复方法:把这些初始化代码移到类的__init__方法里,让追踪器在整个视频处理过程中只初始化一次,持续保留追踪状态。

修改后的类初始化方法:

class YOLOv7:
    def __init__(self, weights: str, image_size:int,device:str, reID_model_path = "./deep_sort/model_weights/mars-small128.pb", nms_max_overlap:float=1.0, max_cosine_distance:float=0.4, nn_budget:float=None):
        self.device = device
        self.weights = weights
        self.model = attempt_load(self.weights, map_location=self.device) # Model Load FP32
        self.stride = int(self.model.stride.max())
        self.image_size = check_img_size(image_size, self.stride)

        if self.device != 'cpu':
            self.half = True
        else:
            self.half = False

        if self.half:
            self.model.half() # FP16
            
        self.names = self.model.module.names if hasattr(self.model , 'module') else self.model.names
        color_values = [[random.randint(0, 255) for _ in range(3)] for _ in range(len(self.names))]
        self.colors = {i:color_values[i] for i in range(len(self.names))}

        # 初始化DeepSORT相关组件,仅执行一次
        self.class_names = read_class_names()
        self.encoder = create_box_encoder(reID_model_path, batch_size=1)
        self.nms_max_overlap = nms_max_overlap
        self.metric = nn_matching.NearestNeighborDistanceMetric("cosine", max_cosine_distance, nn_budget)
        self.tracker = Tracker(self.metric)
        # 初始化颜色映射
        self.cmap = plt.get_cmap('tab20b')
        self.track_colors = [self.cmap(i)[:3] for i in np.linspace(0, 1, 20)]

修改后的tracking方法(移除重复初始化代码,使用类实例属性):

def tracking(self, video_frame, yolo_dets, inside_poly = True, count_objects:bool=False,verbose=False):
    frame = cv2.cvtColor(video_frame, cv2.COLOR_BGR2RGB)
    if yolo_dets is None or len(yolo_dets) == 0:
        bboxes = []
        scores = []
        classes = []
        num_objects = 0
    else:
        bboxes = yolo_dets[:,:4]
        bboxes[:,2] = bboxes[:,2] - bboxes[:,0] # convert from xyxy to xywh
        bboxes[:,3] = bboxes[:,3] - bboxes[:,1]

        scores = yolo_dets[:,4]
        classes = yolo_dets[:,-1]
        num_objects = bboxes.shape[0]
    # 生成目标类别名称
    names = []
    for i in range(num_objects):
        class_indx = int(classes[i])
        class_name = self.class_names[class_indx]
        names.append(class_name)

    names = np.array(names)
    count = len(names)

    if count_objects:
        cv2.putText(frame, "both inside and outside the polygon detection: {}".format(count), (5, 35), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5, (0, 0, 0), 2)

    # DeepSORT追踪流程
    features = self.encoder(frame, bboxes)
    detections = [Detection(bbox, score, class_name, feature) for bbox, score, class_name, feature in zip(bboxes, scores, names, features)]

    boxs = np.array([d
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 07:15:34