Location>code7788 >text

Target Tracking ByteTrack Algorithm Detailed Flow Analysis

Popularity:336 ℃/2024-08-21 17:58:56

Introduction to the principle

ByteTrack is a new multi-object tracking algorithm made public by ByteTrack with October 2021, the original paper is "ByteTrack: Multi-Object Tracking by Associating Every Detection Box".
ByteTrak achieves better performance on metrics such as MOTA and FPS than most existing MOT (multi-target tracking) algorithms.

github address:/ifzhang/ByteTrack

Demo:

Introduction to ByteTrack

Previous multi-target tracking algorithms generally only retain the detection frames with higher confidence for target tracking after completing the target detection for the current frame, such as the target frames with confidence levels of 0.9 and 0.8 in Fig.
In ByteTrack, the authors keep all the detection frames and classify them into high and low confidence detection frames by thresholding.ByteTrack can effectively solve some occlusions and keep the ID Switch low, because the confidence of the target detection decreases due to occlusion, and increases when it reappears. The algorithm is characterized by:

  1. When the target is gradually occluded, the tracked target is matched to the low confidence detection target.
  2. When target occlusion is gradually reproduced, the tracked target matches the high-confidence detection target.

algorithmic principle

Rather than connecting all the detection frames to form a single track, ByteTrack determines the track by a prediction and validation method. For each track use theKalman filterto predict the next position of the trajectory (prediction frame), then calculate the IOUs of the detection and prediction frames, and finally pass theHungarian algorithmMatch IOUs and return the trajectories of successful and failed matches.

Detailed steps of the tracking algorithm:

  1. Create tracking trajectories for each target before starting tracking
  2. pass (a bill or inspection etc)Kalman filterPredicts the next frame bounding box for each tracking trajectory
  3. The detection frame of the target is obtained through the detector, which is categorized into high and low scoring frames according to the confidence level
  4. in the first placehigh scoring boxto compute the IOUs for the high score box and the prediction box using theHungarian algorithmMatch IOUs to get 3 results: matched tracks with high score boxes, unsuccessfully matched tracks, and unsuccessfully matched high score boxes. Matching successfully updates the box in the tracked trajectory to the high score detection box
  5. after thatlow-scoring frame, calculates the IOUs of the low-scoring box and the predicted box that was not matched on in the previous step, and uses the Hungarian algorithm to match the IOUs to obtain 3 results: matched trajectories with low-scoring boxes, unsuccessfully matched trajectories, and unsuccessfully matched low-scoring boxes. Update the box in the tracked trajectory to the detected box after successful matching
  6. ultimateFor high scoring detection frames on unmatched, which is matched to the track with inactive status, obtaining 3 results: match, unmatched track, and unmatched detection box. For match update the status, for unmatched trajectory mark it as deleted, and for unmatched detection box, confidence greater than a high threshold +0.1 create a new track, less than it is discarded.

code implementation

Before introducing the code, we should first understand the basic concepts. ByteTrack consists of two main classes, a track management class STrack, and a track matching logic processing class BYTETracker.

  • STrack: creation, update, deletion of tracks
  • BYTETracker: Matching process processing, confidence level division, track matching, etc.

STrack

Strack is the class of tracks, each instance of which is a track.Strack has core methods including:

  1. multi_predict Kalman filter prediction
  2. activate Trajectory Creation
  3. re_activate Trajectory reactivation
  4. update Track update
# This class is used to hold trajectories,Each track has some properties of its own,for exampleid、bounding box、prediction frame、Status, etc.
class STrack(BaseTrack):
    # singleton model
    shared_kalman = KalmanFilter()

    def __init__(self, tlwh, score):
        self._tlwh = (tlwh, dtype=)
        self.kalman_filter = None

        # Save the mean and covariance of the Kalman filter for this trajectory
        , = None, None

        # Whether it is active or not
        self.is_activated = False
        
        # track score
         = score

        # Frames for trajectory tracking,Each successful trace will+1
        self.tracklet_len = 0

    def predict(self):
        mean_state = ()
        if != :
            mean_state[7] = 0
        , = self.kalman_filter.predict(mean_state, )

    @staticmethod
    def multi_predict(stracks):
        if len(stracks) > 0:
            multi_mean = ([() for st in stracks])
            multi_covariance = ([ for st in stracks])
            for i, st in enumerate(stracks):
                if != :
                    multi_mean[i][7] = 0
            multi_mean, multi_covariance = STrack.shared_kalman.multi_predict(multi_mean, multi_covariance)
            for i, (mean, cov) in enumerate(zip(multi_mean, multi_covariance)):
                stracks[i].mean = mean
                stracks[i].covariance = cov

    def activate(self, kalman_filter, frame_id):
        """Start a new tracklet"""
        self.kalman_filter = kalman_filter
        self.track_id = self.next_id()
        , = self.kalman_filter.initiate(self.tlwh_to_xyah(self._tlwh))

        self.tracklet_len = 0
         =
        if frame_id == 1:
            self.is_activated = True
        # self.is_activated = True
        self.frame_id = frame_id
        self.start_frame = frame_id

    def re_activate(self, new_track, frame_id, new_id=False):
        , = self.kalman_filter.update(
            , , self.tlwh_to_xyah(new_track.tlwh)
        )
        self.tracklet_len = 0
         =
        self.is_activated = True
        self.frame_id = frame_id
        if new_id:
            self.track_id = self.next_id()
         = new_track.score

    def update(self, new_track, frame_id):
        """
        Update a matched track
        :type new_track: STrack
        :type frame_id: int
        :type update_feature: bool
        :return:
        """
        self.frame_id = frame_id
        self.tracklet_len += 1

        new_tlwh = new_track.tlwh
        , = self.kalman_filter.update(
            , , self.tlwh_to_xyah(new_tlwh))
         =
        self.is_activated = True

         = new_track.score

    @property
    # @jit(nopython=True)
    def tlwh(self):
        """Get current position in bounding box format `(top left x, top left y,
                width, height)`.
        """
        if is None:
            return self._tlwh.copy()
        ret = [:4].copy()
        ret[2] *= ret[3]
        ret[:2] -= ret[2:] / 2
        return ret

    @property
    # @jit(nopython=True)
    def tlbr(self):
        """Convert bounding box to format `(min x, min y, max x, max y)`, .,
        `(top left, bottom right)`.
        """
        ret = ()
        ret[2:] += ret[:2]
        return ret

    @staticmethod
    # @jit(nopython=True)
    def tlwh_to_xyah(tlwh):
        """Convert bounding box to format `(center x, center y, aspect ratio,
        height)`, where the aspect ratio is `width / height`.
        """
        ret = (tlwh).copy()
        ret[:2] += ret[2:] / 2
        ret[2] /= ret[3]
        return ret

    def to_xyah(self):
        return self.tlwh_to_xyah()

    @staticmethod
    # @jit(nopython=True)
    def tlbr_to_tlwh(tlbr):
        ret = (tlbr).copy()
        ret[2:] -= ret[:2]
        return ret

    @staticmethod
    # @jit(nopython=True)
    def tlwh_to_tlbr(tlwh):
        ret = (tlwh).copy()
        ret[2:] += ret[:2]
        return ret

    def __repr__(self):
        return 'OT_{}_({}-{})'.format(self.track_id, self.start_frame, self.end_frame)

BYTETracker

BYTETracker is a class for the process of creating, updating and deleting tracks. It has one main method, which is update, which constantly updates the track routes through the operation of the prediction box and the already existing tracks. The flowchart of the code implementation is shown below:

The following analyzes the code implementation flow
Initialization parameters, including tracking trajectories to be saved, lost trajectories, deleted trajectories, high and low score thresholds, and so on. The working principle of tracking is to judge the processing of each frame, and also need to judge the consecutive frames, so what is defined here is the globally saved trajectory.

class BYTETracker(object):
    def __init__(self, args, frame_rate=30):

    # Tracks tracked
    self.tracked_stracks = [] # type: list[STrack]
    
    # Lost Tracks
    self.lost_stracks = [] # type: list[STrack]
    
    # Deleted tracks
    self.removed_stracks = [] # type: list[STrack]
    
    
    self.frame_id = 0
    
     = args
    #self.det_thresh = args.track_thresh
    
    # Thresholds for distinguishing between high and low score detection frames
    self.det_thresh = args.track_thresh + 0.1
    
    # Maximum loss time
    self.buffer_size = int(frame_rate / 30.0 * args.track_buffer)
    self.max_time_lost = self.buffer_size
    
    # Kalman filter
    self.kalman_filter = KalmanFilter()

Define the results after each frame of process processing, including tracked trajectories, re-tracked trajectories, lost trajectories, etc.

def update(self, output_results): self.
        self.frame_id += 1

        # The following list is the list of temporary results stored in this round of matching
        # Save the current frame marked as tracked to the trace
        activated_starcks = []

        # Save the current frame marked as tracked to a previously lost track
        refind_stracks = []

        # Save the current frame marked as a lost target track
        lost_stracks = []

        # Save the current frame marked as lost tracks
        removed_stracks = []

Filter out high and low scoring boxes

        # high scoring frame
        remain_inds = scores > .track_thresh

        # low scoring frame0.1-Between Thresholds
        inds_low = scores > 0.1
        inds_high = scores < .track_thresh


        # Screening scores are at0.1<mark<thresholds
        inds_second = np.logical_and(inds_low, inds_high)
        dets_second = bboxes[inds_second]
        scores_second = scores[inds_second]


        # 筛选mark超过thresholds
        dets = bboxes[remain_inds]
        scores_keep = scores[remain_inds]

Filtering out normal and first-time tracking traces

        # Those that are in the tracking phase but not activated, i.e. those that have only caught up with the target for one frame. That is, the first track tracked
        unconfirmed = []
        tracked_stracks = [] # type: list[STrack]

        for track in self.tracked_stracks.
            if not track.is_activated.
                (track)
            track.is_activated: (track)
                tracked_stracks.append(track)

High-scoring matches and different processing based on each of the three matches

         """------------ first match, high score detection box match -----------"""
        # Combine tracks with tracked status active and marked as lost to get track_pool
        strack_pool = joint_stracks(tracked_stracks, self.lost_stracks)

        # Feed strack_pool into muti_predict for prediction (Kalman filter)
        STrack.multi_predict(strack_pool)


        # Calculate iou_distance (cost matrix) for prediction and detections detection frames for tracks in strack_pool
        dists = matching.iou_distance(strack_pool, detections)
        if not .mot20.
            dists = matching.fuse_score(dists, detections)

        # Filter smaller iou's with matching threshold match_thresh=0.8, use Hungarian algorithm to match, get successful tracks, unsuccessful tracks, unsuccessful detection frames
        matches, u_track, u_detection = matching.linear_assignment(dists, thresh=.match_thresh)


        # Iterate over matches, track's previous state is Tracked, call update method and add to activated_stracks.
        # Tracks whose previous state was not Tracked, call re_activate and add to reflect_stracks

        # matches = [itracked, idet] itracked is the index of the track, idet is the index of the current detection frame, meaning the first track matches the first target frame
        for itracked, idet in matches.
            track = strack_pool[itracked]
            det = detections[idet]
            if == .
                # update the track prediction box to the current detection box
                (detections[idet], self.frame_id)
                activated_starcks.append(track)
            else.
                track.re_activate(detections, self.frame_id, new_id=False)
                # refind_stracks are re-tracked tracks
                refind_stracks.append(track)

Low scores are matched and treated differently.

"""------------- second match: low-score detection frame match -----------------"""
        if len(detections_second) > 0:.
            detections_second = [STrack(STrack.tlbr_to_tlwh(tlbr), s) for
                          (tlbr, s) in zip(dets_second, scores_second)]
        else.
            detections_second = []


        # Find the tracks that didn't match after the first match
        r_tracked_stracks = [strack_pool[i] for i in u_track if strack_pool[i].state == ]


        # Match the first unmatched track with the low confidence detection frame
        dists = matching.iou_distance(r_tracked_stracks, detections_second)

        # Filter smaller iou's with matching threshold match_thresh = 0.5, match using Hungarian algorithm to get matches, u_track, u_detection
        matches, u_track, u_detection_second = matching.linear_assignment(dists, thresh=0.5)

        # Iterate over matches, track previous state is Tracked, call update method and add to activated_stracks.
        # Iterate over matches, if the track's previous state is not Tracked, call re_activate and add to reflect_stracks
        for itracked, idet in matches.
            track = r_tracked_stracks[itracked]
            det = detections_second[idet]
            if == .
                (det, self.frame_id)
                activated_stracks.append(track)
            else.
                track.re_activate(det, self.frame_id, new_id=False)
                refind_stracks.append(track)


        # Iterate over u_tracks (tracks that didn't match even on the second match), take the tracks whose state is not Lost, call the mark_losk method and add them to lost_stracks and wait for the next frame to be matched (tracked state tracks and lost state tracks are merged at the start of this process).
        # A track is neither matched by the high score detection frame nor by the low score detection frame, then it is treated as a lost track
        for it in u_track.
            track = r_tracked_stracks[it]
            if not == :
                track.mark_lost()
                # lost_stracks: tracks that were continuously tracked in the previous frame but could not be matched twice in the current frame
                lost_stracks.append(track)

For the third match, match the unmatched high score box with the first tracked trajectory.

        """"---------- Third match to handle high score detection boxes that didn't match before -------------"""

        # High-scoring detector boxes that didn't match
        detections = [detections[i] for i in u_detection]


        # unconfirmed is the first occurrence of a target that generates a trajectory but is not active. Calculate the iou_distance between first time targets and high scoring detection frames that are not matched
        dists = matching.iou_distance(unconfirmed, detections)
        if not .mot20.
            dists = matching.fuse_score(dists, detections)


        # Filter smaller iou's with threshold match_thresh=0.8, match using Hungarian algorithm, get matches, u_track, u_detection
        matches, u_unconfirmed, u_detection = matching.linear_assignment(dists, thresh=0.8)

        # Iterate over the matched tracks, update the status to active, and save the track to the currently active tracks
        for itracked, idet in matches.
            unconfirmed[itracked].update(detections[idet], self.frame_id)
            activated_starcks.append(unconfirmed[itracked])


        # Iterate over the unmatched tracks, call the mark_removd method, and add removed_stracks
        for it in u_unconfirmed.
            # Tracks that appear once in the middle and fail to match the current target box, remove the track and consider it a detector misclassification
            track = unconfirmed[it]
            track.mark_removed()
            removed_stracks.append(track)

Finally, the unmatched high score detection box will be judged again and set as a new track if it meets the condition

        # Iterate over unmatched high score detection boxes, for scores greater than self.det_thresh, call the activate method and add activated_stracks
        for inew in u_detection.
            track = detections[inew]
            if < self.det_thresh.
                continue

            # Create a new track and call activate. activate state of the new track: if the track is created in the first frame of the video, then the activation state is True, for the rest of the frames, the activation state of the new track is False.
            (self.kalman_filter, self.frame_id)

            # Add the new track to the currently active tracks.
            activated_starcks.append(track)

The last thing is to update the results of each frame match to the global results and then do a round of matching.

wrap-up

As can be seen on the flowchart of matching, ByteTrack is not only interested in the high-scoring box, but also has concerns about the low-scoring box. The low-scoring frame is treated as a suspected occluded target, and the high-scoring frame is treated as a matched track or a new track, so that the occluded object is effectively tracked and the accuracy of tracking is improved.

Full Code:


class BYTETracker(object):
    def __init__(self, args, frame_rate=30):

        # Tracks tracked
        self.tracked_stracks = []  # type: list[STrack]

        # Lost Tracks
        self.lost_stracks = []  # type: list[STrack]

        # Deleted tracks
        self.removed_stracks = []  # type: list[STrack]


        self.frame_id = 0

         = args
        #self.det_thresh = args.track_thresh

        # Thresholds for distinguishing between high and low score detection frames
        self.det_thresh = args.track_thresh + 0.1

        # Maximum loss time
        self.buffer_size = int(frame_rate / 30.0 * args.track_buffer)
        self.max_time_lost = self.buffer_size

        # Kalman filter
        self.kalman_filter = KalmanFilter()



    def update(self, output_results):
        self.frame_id += 1

        # The following list is the list of temporary results stored in this round of matching
        # Saves the current frame marked as track-to-track
        activated_starcks = [] 

        # 保存当前帧标记because of追踪到之前Lost Tracks
        refind_stracks = [] 

        # Save the current frame marked as a lost target track
        lost_stracks = [] 

        # 保存当前帧标记because ofLost Tracks
        removed_stracks = [] 


        # initial step:commander-in-chief (military)objectsconvert tox1,y1,x2,y2,scoreformats,parallel constructionstrack
        if output_results.shape[1] == 5:
            scores = output_results[:, 4]
            bboxes = output_results[:, :4]
        else:
            output_results = output_results.cpu().numpy()
            scores = output_results[:, 4] * output_results[:, 5]
            bboxes = output_results[:, :4]  # x1y1x2y2

        # 根据传入(used form a nominal expression)thresholdscommander-in-chief (military)检测框分because ofhigh scoring framecap (a poem)low scoring frame

        # high scoring frame
        remain_inds = scores > .track_thresh

        # low scoring frame0.1-Between Thresholds
        inds_low = scores > 0.1
        inds_high = scores < .track_thresh


        # Screening scores are at0.1<mark<thresholds
        inds_second = np.logical_and(inds_low, inds_high)   
        dets_second = bboxes[inds_second]
        scores_second = scores[inds_second]


        # 筛选mark超过thresholds
        dets = bboxes[remain_inds]
        scores_keep = scores[remain_inds]


        # Initialize tracking traces
        if len(dets) > 0:
            '''Detections'''
            detections = [STrack(STrack.tlbr_to_tlwh(tlbr), s) for (tlbr, s) in zip(dets, scores_keep)]
        else:
            detections = []

        ''' Add newly detected tracklets to tracked_stracks'''

        # Iteration preservationstatebecause ofTrackedarraysself.tracked_stracks,Each element represents a track,Each trajectory of thestatebecause ofTracked,But there is a distinction between active states
        # is_activated: 追踪state of affairsbecause of激活(used form a nominal expression)轨迹,That is, catching up on at least two frames of trajectory

        # Tracked but not activated,That is, it's only caught up with the target for one frame.。That's the first track.
        unconfirmed = []
        tracked_stracks = []  # type: list[STrack]

        for track in self.tracked_stracks:
            if not track.is_activated:
                (track)
            else:
                tracked_stracks.append(track)


        """------------First Match,High Score Detection Frame Matching-----------"""
        # commander-in-chief (military)追踪state of affairsbecause of激活cap (a poem)标记because ofLost Tracks合并capturetrack_pool
        strack_pool = joint_stracks(tracked_stracks, self.lost_stracks)

        # commander-in-chief (military)strack_poolfed intomuti_predictcarry out forecasting(Kalman filter)
        STrack.multi_predict(strack_pool)


        # countstrack_poolThe prediction frames for trajectories in anddetectionsdetection frame of theiou_distance(cost matrix)
        dists = matching.iou_distance(strack_pool, detections)
        if not .mot20:
            dists = matching.fuse_score(dists, detections)
        
        # 用匹配thresholdsmatch_thresh=0.8Filtration of smalleriou,Matching using the Hungarian algorithm,Getting the track of a successful match, Unsuccessfully matched trajectories, Unsuccessful match detection box
        matches, u_track, u_detection = matching.linear_assignment(dists, thresh=.match_thresh)
        

        # (math.) ergodicmatches,轨迹之前state of affairsbecause ofTracked,call (programming)updatemethodologies,and add toactivated_stracks。
        # The state before the trajectory is notTracked(used form a nominal expression),call (programming)re_activate,incorporaterefind_stracks

        # matches = [itracked, idet] itracked是轨迹(used form a nominal expression)索引,idet是当前detection frame of the索引,It means that the first track matches the first target box.
        for itracked, idet in matches:
            track = strack_pool[itracked]
            det = detections[idet]
            if  == :
                # update轨迹预测框because of当前检测框
                (detections[idet], self.frame_id)
                activated_starcks.append(track)
            else:
                track.re_activate(det, self.frame_id, new_id=False)
                # refind_stracks是重新追踪到(used form a nominal expression)轨迹
                refind_stracks.append(track)



        """-------------Second match:Low Score Detection Frame Matching-----------------"""
        if len(dets_second) > 0:
            detections_second = [STrack(STrack.tlbr_to_tlwh(tlbr), s) for
                          (tlbr, s) in zip(dets_second, scores_second)]
        else:
            detections_second = []


        # 找出First Match后没匹配到(used form a nominal expression)轨迹
        r_tracked_stracks = [strack_pool[i] for i in u_track if strack_pool[i].state == ]
        

        # 第一次未匹配(used form a nominal expression)轨迹cap (a poem)低置信度detection frame of the匹配
        dists = matching.iou_distance(r_tracked_stracks, detections_second)

        # 用匹配thresholdsmatch_thresh = 0.5 Filtration of smalleriou,Matching using the Hungarian algorithm,capturematches, u_track, u_detection
        matches, u_track, u_detection_second = matching.linear_assignment(dists, thresh=0.5) 

        # (math.) ergodicmatches,轨迹之前state of affairsbecause ofTracked,call (programming)updatemethodologies,and add toactivated_stracks。
        # The state before the trajectory is notTracked(used form a nominal expression),call (programming)re_activate,incorporaterefind_stracks
        for itracked, idet in matches:
            track = r_tracked_stracks[itracked]
            det = detections_second[idet]
            if  == :
                (det, self.frame_id)
                activated_starcks.append(track)
            else:
                track.re_activate(det, self.frame_id, new_id=False)
                refind_stracks.append(track)


        # (math.) ergodicu_track(Second match也没匹配到(used form a nominal expression)轨迹),commander-in-chief (military)statefaultLost(used form a nominal expression)轨迹,call (programming)mark_loskmethodologies,incorporatelost_stracks,Waiting for the next frame to match(This process starts by merging the tracked state trace and the lost state trace.)。
        # 一个轨迹既没有被High Score Detection Frame Matching,也没有被Low Score Detection Frame Matching,Then it will be treated as a lost track
        for it in u_track:
            track = r_tracked_stracks[it]
            if not  == :
                track.mark_lost()
                # lost_stracks:上一帧还在持续追踪但是这一帧两次匹配不到(used form a nominal expression)轨迹
                lost_stracks.append(track)



        """----------Third match,处理之前没有匹配上(used form a nominal expression)高分检测框-------------"""
        
        # 没有匹配上(used form a nominal expression)高分检测框
        detections = [detections[i] for i in u_detection]

        
        # unconfirmed是第一次出现(used form a nominal expression)目标,生成了轨迹但是fault激活state of affairs。count第一次出现(used form a nominal expression)目标cap (a poem)没有匹配上(used form a nominal expression)高分detection frame of theiou_distance
        dists = matching.iou_distance(unconfirmed, detections)
        if not .mot20:
            dists = matching.fuse_score(dists, detections)


        # 用thresholdsmatch_thresh=0.8Filtration of smalleriou,Matching using the Hungarian algorithm,capturematches, u_track, u_detection
        matches, u_unconfirmed, u_detection = matching.linear_assignment(dists, thresh=0.8)
        
        # (math.) ergodic匹配(used form a nominal expression)轨迹,updatestate of affairsbecause of激活,并commander-in-chief (military)轨迹Save to当前活跃轨迹中
        for itracked, idet in matches:
            unconfirmed[itracked].update(detections[idet], self.frame_id)
            activated_starcks.append(unconfirmed[itracked])


        # (math.) ergodic未匹配轨迹,call (programming)mark_removdmethodologies,incorporateremoved_stracks
        for it in u_unconfirmed:
            # 中途出现一次(used form a nominal expression)轨迹cap (a poem)当前目标框匹配失败,Delete this track,认because of是检测器误判
            track = unconfirmed[it]
            track.mark_removed()
            removed_stracks.append(track)


        # (math.) ergodic未匹配上(used form a nominal expression)高分检测框,insofar asscoremore thanself.det_thresh,call (programming)activatemethodologies,incorporateactivated_stracks
        for inew in u_detection:
            track = detections[inew]
            if  < self.det_thresh:
                continue

            # 新建轨迹并call (programming)activate。新建轨迹(used form a nominal expression)激活state of affairs:如果视频开始第一帧就新建轨迹那么激活state of affairsbecause ofTrue,The remaining frames of the new trajectory activation state areFalse
            (self.kalman_filter, self.frame_id)

            #把新(used form a nominal expression)轨迹加入到当前活跃轨迹中
            activated_starcks.append(track)


        # (math.) ergodiclost_stracks,insofar as丢失超过max_time_lost(30)(used form a nominal expression)轨迹,call (programming)mark_removedmethodologies,incorporateremoved_stracks
        for track in self.lost_stracks:
            if self.frame_id - track.end_frame > self.max_time_lost:
                track.mark_removed()
                removed_stracks.append(track)


        # 收集所有state of affairsbecause ofTracked(used form a nominal expression)轨迹,包括本轮update(used form a nominal expression)activated_starckscap (a poem)refind_stracks,以及上一轮(used form a nominal expression)self.tracked_stracks
        # (math.) ergodictracked_stracks,shortliststatebecause ofTracked(used form a nominal expression)轨迹,Save totracked_stracks。self.tracked_stracks中在本轮匹配中如果两轮都没有匹配上会被置because ofloststate of affairs
        self.tracked_stracks = [t for t in self.tracked_stracks if  == ]
        # commander-in-chief (military)activated_stracks,refind_stracksMerge totrack_stracks
        self.tracked_stracks = joint_stracks(self.tracked_stracks, activated_starcks)
        self.tracked_stracks = joint_stracks(self.tracked_stracks, refind_stracks)


        # updateself.lost_stracks保存(used form a nominal expression)轨迹,through (a gap)self.lost_stracks中剔除变成追踪state of affairs(used form a nominal expression)轨迹
        self.lost_stracks = sub_stracks(self.lost_stracks, self.tracked_stracks)
        self.lost_stracks.extend(lost_stracks)

        # updateself.removed_stracks保存(used form a nominal expression)轨迹, through (a gap)self.removed_stracks中剔除变成追踪state of affairs(used form a nominal expression)轨迹
        self.lost_stracks = sub_stracks(self.lost_stracks, self.removed_stracks)
        self.removed_stracks.extend(removed_stracks)


        # call (programming)remove_duplicate_stracksfunction (math.),counttracked_stracks,lost_stracks(used form a nominal expression)iou_distance,insofar asiou_distance<0.15(used form a nominal expression)认because of是同一个轨迹,
        # Compare this trajectory intrack_strackscap (a poem)lost_stracks(used form a nominal expression)跟踪帧数cap (a poem)长短,仅保留长(used form a nominal expression)那个
        self.tracked_stracks, self.lost_stracks = remove_duplicate_stracks(self.tracked_stracks, self.lost_stracks)


        # (math.) ergodicself.tracked_stracks,commander-in-chief (military)所有(used form a nominal expression)is_activatedbecause oftrue(used form a nominal expression)轨迹输出
        output_stracks = [track for track in self.tracked_stracks if track.is_activated]

        return output_stracks


def joint_stracks(tlista, tlistb):
    exists = {}
    res = []
    for t in tlista:
        exists[t.track_id] = 1
        (t)
    for t in tlistb:
        tid = t.track_id
        if not (tid, 0):
            exists[tid] = 1
            (t)
    return res


def sub_stracks(tlista, tlistb):
    stracks = {}
    for t in tlista:
        stracks[t.track_id] = t
    for t in tlistb:
        tid = t.track_id
        if (tid, 0):
            del stracks[tid]
    return list(())


def remove_duplicate_stracks(stracksa, stracksb):
    pdist = matching.iou_distance(stracksa, stracksb)
    pairs = (pdist < 0.15)
    dupa, dupb = list(), list()
    for p, q in zip(*pairs):
        timep = stracksa[p].frame_id - stracksa[p].start_frame
        timeq = stracksb[q].frame_id - stracksb[q].start_frame
        if timep > timeq:
            (q)
        else:
            (p)
    resa = [t for i, t in enumerate(stracksa) if not i in dupa]
    resb = [t for i, t in enumerate(stracksb) if not i in dupb]
    return resa, resb

Reference:
/wentinghappyday/article/details/128376299
/weixin_43731103/article/details/123665507
Target tracking of MOT classic algorithm: ByteTrack algorithm principle and multi-category tracking - CSDN Blog.pdf
Kalman filter and Hungarian algorithm in multi-target tracking (MOT)_mot Kalman filter-CSDN Blog.pdf