def __init__(self, opt, frame_rate=30): # 帧率的意义 self.opt = opt if opt.gpus[0] >= 0: opt.device = torch.device('cuda') else: opt.device = torch.device('cpu') print('Creating model...') self.model = create_model(opt.arch, opt.heads, opt.head_conv) # 加载模型, self.model = load_model(self.model, opt.load_model) self.model = self.model.to(opt.device) self.model.eval() self.tracked_stracks = [] # type: list[STrack] # 保存一系列追踪中的轨迹 self.lost_stracks = [] # type: list[STrack] # 保存已经丢失的轨迹 self.removed_stracks = [] # type: list[STrack] # 保存已经移除的轨迹 self.frame_id = 0 self.det_thresh = opt.conf_thres # 检测框阈值,这里设置为与tracking的置信度阈值相同 self.buffer_size = int(frame_rate / 30.0 * opt.track_buffer) # 还是等于输入视频的真实帧率 self.max_time_lost = self.buffer_size # 最大连续self.buffer_size次没有匹配到目标时,表示该轨迹丢失 self.max_per_image = 128 self.mean = np.array(opt.mean, dtype=np.float32).reshape(1, 1, 3) self.std = np.array(opt.std, dtype=np.float32).reshape(1, 1, 3) self.kalman_filter = KalmanFilter() # 预测,根据上一帧目标的检测位置和速度,预测当前帧目标的检测位置和速度
def kalman_predict_out_line(track,line,out_direction): # print(track.track_id) # print(line) # print(out_direction) # print(track.tlbr) if box_line_relative(track.tlbr,line)==out_direction: return 0 predict_num_out=0 prev_mean,prev_cov=track.mean,track.covariance kal_man=KalmanFilter() predict_thres=0 if out_direction=='up' else 0 max_long_predict=5 if out_direction=='up' else 2 if track.infer_type() in ['person','motorcycle','biycycle'] else 8 while box_line_relative(mean_to_tlbr(prev_mean),line) !=out_direction: predict_num_out+=1 cur_mean=prev_mean #of t mean,cov=kal_man.predict(prev_mean,prev_cov) if predict_num_out>predict_thres: new_mean,new_cov=mean,cov else: new_mean,new_cov= kal_man.update(prev_mean,prev_cov,mean[:4]) prev_mean,prev_cov=new_mean,new_cov #of t+1 if predict_num_out>=max_long_predict or np.sum(np.abs(cur_mean-mean))==0: break # print(mean_to_tlbr(mean)) return predict_num_out
def __init__(self, opt, frame_rate=30): self.opt = opt if opt.gpus[0] >= 0: opt.device = torch.device('cuda') else: opt.device = torch.device('cpu') print('Creating model...') self.model = create_model(opt.arch, opt.heads, opt.head_conv) self.model = load_model(self.model, opt.load_model) self.model = self.model.to(opt.device) self.model.eval() self.tracked_stracks = [] # type: list[STrack] self.lost_stracks = [] # type: list[STrack] self.removed_stracks = [] # type: list[STrack] self.frame_id = 0 self.det_thresh = opt.conf_thres self.buffer_size = int(frame_rate / 30.0 * opt.track_buffer) self.max_time_lost = self.buffer_size self.max_per_image = opt.K self.mean = np.array(opt.mean, dtype=np.float32).reshape(1, 1, 3) self.std = np.array(opt.std, dtype=np.float32).reshape(1, 1, 3) self.kalman_filter = KalmanFilter() self.roi_align = RoIAlign(7, 7) cfg = get_config() cfg.merge_from_file( "/home/hongwei/track-human/FairMOT/src/lib/tracker/deep_configs/yolov3.yaml" ) cfg.merge_from_file( "/home/hongwei/track-human/FairMOT/src/lib/tracker/deep_configs/deep_sort.yaml" ) self.detector = build_detector(cfg, True)
def __init__(self, opt, frame_rate=30): self.opt = opt if opt.gpus[0] >= 0: opt.device = torch.device('cuda') else: opt.device = torch.device('cpu') print('Creating model...') self.model = create_model(opt.arch, opt.heads, opt.head_conv) self.model = load_model(self.model, opt.load_model) self.model = self.model.to(opt.device) self.model.eval() # convert to onnx # input_names = ["input0"] # output_names = ["hm", "wh", "id", "reg"] # inputs = torch.randn(1, 3, 480, 640).to('cpu') # torch_out = torch.onnx._export(self.model, inputs, 'pruned.onnx', export_params=True, verbose=False, # input_names=input_names, output_names=output_names) # print("export onnx sucess") self.tracked_stracks = [] # type: list[STrack] self.lost_stracks = [] # type: list[STrack] self.removed_stracks = [] # type: list[STrack] self.frame_id = 0 self.det_thresh = opt.conf_thres self.buffer_size = int(frame_rate / 30.0 * opt.track_buffer) self.max_time_lost = self.buffer_size self.max_per_image = opt.K self.mean = np.array(opt.mean, dtype=np.float32).reshape(1, 1, 3) self.std = np.array(opt.std, dtype=np.float32).reshape(1, 1, 3) self.kalman_filter = KalmanFilter()
def __init__(self, opt, frame_rate=30): self.opt = opt if opt.gpus[0] >= 0: opt.device = torch.device('cuda') else: opt.device = torch.device('cpu') print('Creating model...') self.model = create_model(opt.arch, opt.heads, opt.head_conv) self.model = load_model(self.model, opt.load_model) self.model = self.model.to(opt.device) self.model.eval() # input = torch.randn(1, 3, 640, 640, requires_grad=True) # input=input.to(opt.device) # out = self.model(input) # torch.onnx.export(self.model, # model being run # input, # model input (or a tuple for multiple inputs) # "./test.onnx", # where to save the model (can be a file or file-like object) # export_params=True, # store the trained parameter weights inside the model file# ) # opset_version=9 # ) self.tracked_stracks = [] # type: list[STrack] self.lost_stracks = [] # type: list[STrack] self.removed_stracks = [] # type: list[STrack] self.frame_id = 0 self.det_thresh = opt.conf_thres self.buffer_size = int(frame_rate / 30.0 * opt.track_buffer) self.max_time_lost = self.buffer_size self.max_per_image = opt.K self.mean = np.array(opt.mean, dtype=np.float32).reshape(1, 1, 3) self.std = np.array(opt.std, dtype=np.float32).reshape(1, 1, 3) self.kalman_filter = KalmanFilter()
def __init__(self, opt, frame_rate=30): self.opt = opt if opt.gpus[0] >= 0: opt.device = torch.device('cuda') else: opt.device = torch.device('cpu') print('Creating model...') self.model = create_model(opt.arch, opt.heads, opt.head_conv) self.model = load_model(self.model, opt.load_model) self.model = self.model.to(opt.device) self.model.eval() self.tracked_stracks = [] # type: list[STrack] self.lost_stracks = [] # type: list[STrack] self.removed_stracks = [] # type: list[STrack] self.frame_id = 0 self.det_thresh = opt.conf_thres self.buffer_size = int(frame_rate / 30.0 * opt.track_buffer) self.max_time_lost = self.buffer_size self.max_per_image = 128 self.mean = np.array(opt.mean, dtype=np.float32).reshape(1, 1, 3) self.std = np.array(opt.std, dtype=np.float32).reshape(1, 1, 3) self.kalman_filter = KalmanFilter()
def kalman_predict_out_line(track, line, out_direction, predict_long=None): # print(track.track_id) # print(line) # print(out_direction) # print(track.tlbr) if box_line_relative(track.tlbr, line) == out_direction: return 0 predict_num_out = 0 prev_mean, prev_cov = track.mean, track.covariance kal_man = KalmanFilter() predict_thres = 15 if out_direction == 'up' else 0 if predict_long is not None: max_long_predict = predict_long else: max_long_predict = 50 if out_direction == 'up' else 4 while box_line_relative(mean_to_tlbr(prev_mean), line) != out_direction: predict_num_out += 1 cur_mean = prev_mean #of t mean, cov = kal_man.predict(prev_mean, prev_cov) if predict_num_out > predict_thres: new_mean, new_cov = mean, cov else: new_mean, new_cov = kal_man.update(prev_mean, prev_cov, mean[:4]) prev_mean, prev_cov = new_mean, new_cov #of t+1 if predict_num_out >= max_long_predict or np.sum( np.abs(cur_mean - mean)) == 0: break # print(mean_to_tlbr(mean)) return predict_num_out
def __init__(self, opt, frame_rate=30): self.opt = opt if opt.gpus[0] >= 0: opt.device = torch.device('cuda') else: opt.device = torch.device('cpu') print('Creating model...') self.model = create_model(opt.arch, opt.heads, opt.head_conv, num_gnn_layers=opt.num_gnn_layers, gnn_type=opt.gnn_type, use_residual=opt.use_residual, return_pre_gnn_layer_outputs=opt.return_pre_gnn_layer_outputs, heads_share_params=opt.heads_share_params, omit_gnn=opt.omit_gnn, use_roi_align=opt.use_roi_align, viz_attention=opt.viz_attention) self.model = load_model(self.model, opt.load_model, distributed=True, copy_head_weights=False) self.model = self.model.to(opt.device) self.model.eval() self.tracked_stracks = [] # type: list[STrack] self.lost_stracks = [] # type: list[STrack] self.removed_stracks = [] # type: list[STrack] self.frame_id = 0 # self.det_thresh = opt.conf_thres self.buffer_size = int(frame_rate / 30.0 * opt.track_buffer) self.max_time_lost = self.buffer_size self.max_per_image = opt.K self.mean = np.array(opt.mean, dtype=np.float32).reshape(1, 1, 3) self.std = np.array(opt.std, dtype=np.float32).reshape(1, 1, 3) self.kalman_filter = KalmanFilter() self.viz_attention = opt.viz_attention
def __init__(self, opt, frame_rate=30): self.opt = opt if opt.use_hog_reid: print('USE HOG AS FEATURE EXTRACTION !!!!') self.re_im_h, self.re_im_w = 300, 120 cell_size = (50, 24) block_size = (6, 5) nbins = 9 self.reid_model = cv2.HOGDescriptor( _winSize=(self.re_im_w // cell_size[1] * cell_size[1], self.re_im_h // cell_size[0] * cell_size[0]), _blockSize=(block_size[1] * cell_size[1], block_size[0] * cell_size[0]), _blockStride=(cell_size[1], cell_size[0]), _cellSize=(cell_size[1], cell_size[0]), _nbins=nbins) self.tracked_stracks = [] # type: list[STrack] self.lost_stracks = [] # type: list[STrack] self.removed_stracks = [] # type: list[STrack] self.frame_id = 0 self.buffer_size = int(frame_rate / 30.0 * opt.track_buffer) self.max_time_lost = self.buffer_size self.prev_img = None self.kalman_filter = KalmanFilter()
def reset(self): """ :return: """ # Reset tracks dict self.tracked_tracks_dict = defaultdict(list) # value type: list[Track] self.lost_tracks_dict = defaultdict(list) # value type: list[Track] self.removed_tracks_dict = defaultdict(list) # value type: list[Track] # Reset frame id self.frame_id = 0 # Reset kalman filter to stabilize tracking self.kalman_filter = KalmanFilter()
def __init__(self, opt): self.opt = opt # ---------- Init model max_ids_dict = { 0: 330, 1: 102, 2: 104, 3: 312, 4: 53 } # cls_id -> track id number for traning device = opt.device # model in track mode(do detection and reid feature vector extraction) self.model = Darknet(opt.cfg, opt.img_size, False, max_ids_dict, 128, 'track').to(device) # Load checkpoint if opt.weights.endswith('.pt'): # pytorch format ckpt = torch.load(opt.weights, map_location=device) self.model.load_state_dict(ckpt['model']) if 'epoch' in ckpt.keys(): print('Checkpoint of epoch {} loaded.'.format(ckpt['epoch'])) else: # darknet format load_darknet_weights(self.model, opt.weights) # Put model to device and set eval mode self.model.to(device).eval() # ---------- # Define tracks dict self.tracked_tracks_dict = defaultdict(list) # value type: list[Track] self.lost_tracks_dict = defaultdict(list) # value type: list[Track] self.removed_tracks_dict = defaultdict(list) # value type: list[Track] # init frame index self.frame_id = 0 # init hyp self.det_thresh = opt.conf_thres self.buffer_size = int(opt.track_buffer) self.max_time_lost = self.buffer_size # self.mean = np.array([0.408, 0.447, 0.470]).reshape(1, 1, 3) # self.std = np.array([0.289, 0.274, 0.278]).reshape(1, 1, 3) # init kalman filter(to stabilize tracking) self.kalman_filter = KalmanFilter()
def __init__(self, opt, model, frame_rate=30): self.opt = opt print('Creating model...') self.model = model self.tracked_stracks = [] # type: list[STrack] self.lost_stracks = [] # type: list[STrack] self.removed_stracks = [] # type: list[STrack] self.frame_id = 0 self.det_thresh = opt.conf_thres self.buffer_size = int(frame_rate / 30.0 * opt.track_buffer) self.max_time_lost = self.buffer_size self.max_per_image = opt.K self.mean = np.array(opt.mean, dtype=np.float32).reshape(1, 1, 3) self.std = np.array(opt.std, dtype=np.float32).reshape(1, 1, 3) self.kalman_filter = KalmanFilter()
def __init__(self, opt, frame_rate=30): self.opt = opt print('Creating model...') self.model = create_model('dla_34', opt.heads, opt.head_conv) if opt.multi_load_model != '': self.model = load_model(self.model, opt.multi_load_model) self.model = self.model.to(opt.device) self.model.eval() self.tracked_stracks = [] # type: list[STrack] self.lost_stracks = [] # type: list[STrack] self.removed_stracks = [] # type: list[STrack] self.frame_id = 0 self.det_thresh = opt.conf_thres self.buffer_size = int(frame_rate) self.max_time_lost = self.buffer_size self.max_per_image = opt.K self.kalman_filter = KalmanFilter()
def __init__(self, gpus, load_model_name, frame_rate=30): # self.opt = opt # if opt.gpus[0] >= 0: # opt.device = torch.device('cuda') # else: # opt.device = torch.device('cpu') if len(gpus) > 0: self.device = torch.device('cuda') else: self.device = torch.device('cpu') print('Creating model...') heads = {'hm': 1, 'wh': 4, 'id': 128, 'reg': 2} head_conv = 256 self.model = create_model(heads, head_conv) self.model = load_model(self.model, load_model_name) self.model = self.model.to(self.device) self.model.eval() self.tracked_stracks = [] # type: list[STrack] self.lost_stracks = [] # type: list[STrack] self.removed_stracks = [] # type: list[STrack] self.frame_id = 0 self.det_thresh = 0.4 self.buffer_size = int(frame_rate / 30.0 * 30) self.max_time_lost = self.buffer_size self.max_per_image = 500 self.mean = np.array([0.408, 0.447, 0.47], dtype=np.float32).reshape(1, 1, 3) self.std = np.array([0.289, 0.274, 0.278], dtype=np.float32).reshape(1, 1, 3) self.kalman_filter = KalmanFilter() self.num_classes = 1 self.down_ratio = 4 self.reg_offset = True self.ltrb = True self.K = 500 self.conf_thres = 0.4
class STrack(BaseTrack): shared_kalman = KalmanFilter() def __init__(self, tlwh, score, temp_feat, buffer_size=30): # wait activate self._tlwh = np.asarray(tlwh, dtype=np.float) self.kalman_filter = None self.mean, self.covariance = None, None self.is_activated = False self.score = score self.tracklet_len = 0 self.smooth_feat = None self.update_features(temp_feat) self.features = deque([], maxlen=buffer_size) self.alpha = 0.9 self.history = [] def update_features(self, feat): feat /= np.linalg.norm(feat) self.curr_feat = feat if self.smooth_feat is None: self.smooth_feat = feat else: self.smooth_feat = self.alpha * self.smooth_feat + (1 - self.alpha) * feat self.features.append(feat) self.smooth_feat /= np.linalg.norm(self.smooth_feat) def predict(self): mean_state = self.mean.copy() if self.state != TrackState.Tracked: mean_state[7] = 0 self.mean, self.covariance = self.kalman_filter.predict(mean_state, self.covariance) @staticmethod def multi_predict(stracks): if len(stracks) > 0: multi_mean = np.asarray([st.mean.copy() for st in stracks]) multi_covariance = np.asarray([st.covariance for st in stracks]) for i, st in enumerate(stracks): if st.state != TrackState.Tracked: 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.mean, self.covariance = self.kalman_filter.initiate(self.tlwh_to_xyah(self._tlwh)) self.tracklet_len = 0 self.state = TrackState.Tracked 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.mean, self.covariance = self.kalman_filter.update( self.mean, self.covariance, self.tlwh_to_xyah(new_track.tlwh) ) self.update_features(new_track.curr_feat) self.tracklet_len = 0 self.state = TrackState.Tracked self.is_activated = True self.frame_id = frame_id if new_id: self.track_id = self.next_id() def update(self, new_track, frame_id, update_feature=True): """ 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 # log historical track up to previous 10 frames if len(self.history) == 10: self.history = self.history[1:] self.history.append(new_tlwh.copy()) self.mean, self.covariance = self.kalman_filter.update( self.mean, self.covariance, self.tlwh_to_xyah(new_tlwh)) self.state = TrackState.Tracked self.is_activated = True self.score = new_track.score if update_feature: self.update_features(new_track.curr_feat) @property def tlwh_raw(self): if len(self.history) > 0: return self.history[-1] return self._tlwh.copy() @property # @jit(nopython=True) def tlwh(self): """Get current position in bounding box format `(top left x, top left y, width, height)`. """ if self.mean is None: return self._tlwh.copy() ret = self.mean[: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)`, i.e., `(top left, bottom right)`. """ ret = self.tlwh.copy() 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 = np.asarray(tlwh).copy() ret[:2] += ret[2:] / 2 ret[2] /= ret[3] return ret def to_xyah(self): return self.tlwh_to_xyah(self.tlwh) @staticmethod # @jit(nopython=True) def tlbr_to_tlwh(tlbr): ret = np.asarray(tlbr).copy() ret[2:] -= ret[:2] return ret @staticmethod # @jit(nopython=True) def tlwh_to_tlbr(tlwh): ret = np.asarray(tlwh).copy() ret[2:] += ret[:2] return ret def __repr__(self): return 'OT_{}_({}-{})'.format(self.track_id, self.start_frame, self.end_frame)
def __init__(self, opt, polygon, paths, polygon2=None, frame_rate=30): self.opt = opt if opt.gpus[0] >= 0: opt.device = torch.device('cuda') else: opt.device = torch.device('cpu') print('Creating model...') anchor_ratios = [(1.0, 1.0), (1.4, 0.7), (0.7, 1.4)] anchor_scales = [2**0, 2**(1.0 / 3.0), 2**(2.0 / 3.0)] input_sizes = [512, 640, 768, 896, 1024, 1280, 1280, 1536] self.input_size = input_sizes[opt.compound_coef] self.obj_list = [ 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', '', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', '', 'backpack', 'umbrella', '', '', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket', 'bottle', '', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', '', 'dining table', '', '', 'toilet', '', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', '', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush' ] self.person_or_motorcycle = ['motorcycle', 'bicycle'] self.obj_interest = [ 'motorcycle', 'bicycle', 'bus', 'truck', 'car' ] if self.person_or_motorcycle[0] != 'person' else [ 'person', 'bus', 'truck', 'car' ] print(self.obj_interest) self.detetection_model = EfficientDetBackbone( compound_coef=opt.compound_coef, num_classes=len(self.obj_list), ratios=anchor_ratios, scales=anchor_scales) self.detetection_model.load_state_dict( torch.load( f'EfficientDet/weights/efficientdet-d{opt.compound_coef}.pth')) self.detetection_model.eval() device = torch.device('cuda:0') self.detetection_model = self.detetection_model.to(device) self.tracked_stracks = [] # type: list[STrack] self.lost_stracks = [] # type: list[STrack] self.removed_stracks = [] # type: list[STrack] self.frame_id = 0 self.det_thresh = opt.conf_thres self.buffer_size = int(frame_rate / 30.0 * opt.track_buffer) self.max_time_lost = self.buffer_size self.max_per_image = opt.K self.kalman_filter = KalmanFilter() self.polygon = polygon self.paths = paths self.polygon2 = polygon2 self.line2 = [self.polygon[1], self.polygon[2]] self.line1 = [self.polygon[4], self.polygon[3]] if len( self.polygon) == 5 else [self.polygon[0], self.polygon[3]] if len( self.polygon) == 4 else None self.two_polygon_system = False self.warmup_frame = 6 if self.two_polygon_system else 0 self.virtual_polygon = [[0, 573], [0, 109], [1270, 109], [1270, 573]]
class STrack(BaseTrack): shared_kalman = KalmanFilter() out_of_frame_patience=5 num_cluster=5 type_infer_patience=4 score_infer_type_thres=0.6 def __init__(self, tlwh, score, vehicle_type, buffer_size=30,temp_feat=None,huge_vehicle=False): # wait activate self._tlwh = np.asarray(tlwh, dtype=np.float) self.kalman_filter = None self.mean, self.covariance = None, None self.is_activated = False self.score = score self.tracklet_len = 0 self.smooth_feat = None # self.update_features(temp_feat,None) self.features = deque([], maxlen=buffer_size) self.alpha = 0.6 self.num_out_frame=0 self.cluster_features={'centers':[],'cluster':[]} self.track_frames=[] self.w_hs=[] self.occlusion_status=False # use for bbox only self.iou_box=None #use for bbox only self.box_hist=[] self.vehicle_types_list=[] self.vehicle_types_list.append(vehicle_type) self.track_trajectory=[] self.track_trajectory.append(self.tlwh_to_tlbr(tlwh)) self.huge_vehicle=huge_vehicle def update_cluster(self,feat): feat /= np.linalg.norm(feat) if len(self.cluster_features['cluster'])<STrack.num_cluster: self.cluster_features['cluster'].append([feat]) self.cluster_features['centers'].append(feat) else: min_center=np.argmin(np.squeeze(cdist(self.cluster_features['centers'], [feat], metric='cosine'))) self.cluster_features['cluster'][min_center].append(feat) self.cluster_features['centers'][min_center]=np.mean(self.cluster_features['cluster'][min_center],axis=0) self.cluster_features['centers']/=np.linalg.norm( self.cluster_features['centers']) def update_features(self, feat,iou_box): #feat /= np.linalg.norm(feat) self.curr_feat = feat if self.smooth_feat is None: self.smooth_feat = feat else: if iou_box==None: iou_box=0 update_param=(1-self.alpha)*iou_box+self.alpha #self.smooth_feat = self.alpha * self.smooth_feat + (1 - self.alpha) * feat self.smooth_feat = update_param * self.smooth_feat + (1 -update_param) * feat self.box_hist.append(update_param) self.features.append(feat) #self.smooth_feat /= np.linalg.norm(self.smooth_feat) def predict(self): mean_state = self.mean.copy() if self.state != TrackState.Tracked: mean_state[7] = 0 self.mean, self.covariance = self.kalman_filter.predict(mean_state, self.covariance) @staticmethod def multi_predict(stracks): if len(stracks) > 0: multi_mean = np.asarray([st.mean.copy() for st in stracks]) multi_covariance = np.asarray([st.covariance for st in stracks]) for i, st in enumerate(stracks): if st.state != TrackState.Tracked: 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.track_trajectory.append(self.tlbr) self.kalman_filter = kalman_filter self.track_id = self.next_id() self.mean, self.covariance = self.kalman_filter.initiate(self.tlwh_to_xyah(self._tlwh)) self.tracklet_len = 0 self.state = TrackState.Tracked #self.is_activated = True self.frame_id = frame_id self.start_frame = frame_id self.track_frames.append(frame_id) def re_activate(self, new_track, frame_id, new_id=False): new_tlwh = new_track.tlwh self.track_trajectory.append(self.tlwh_to_tlbr(new_tlwh)) self.mean, self.covariance = self.kalman_filter.update( self.mean, self.covariance, self.tlwh_to_xyah(new_track.tlwh) ) #self.update_features(new_track.curr_feat,new_track.iou_box) #self.update_cluster(new_track.curr_feat) self.tracklet_len = 0 self.state = TrackState.Tracked self.is_activated = True self.frame_id = frame_id if new_id: self.track_id = self.next_id() self.track_frames.append(frame_id) def update(self, new_track, frame_id, update_feature=False): """ 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 self.vehicle_types_list.append(new_track.vehicle_types_list[-1]) new_tlwh = new_track.tlwh self.track_trajectory.append(self.tlwh_to_tlbr(new_tlwh)) self.mean, self.covariance = self.kalman_filter.update( self.mean, self.covariance, self.tlwh_to_xyah(new_tlwh)) self.state = TrackState.Tracked self.is_activated = True self.score = new_track.score if update_feature: self.update_features(new_track.curr_feat,new_track.iou_box) ########### #self.update_cluster(new_track.curr_feat) self.track_frames.append(frame_id) def infer_type(self): def most_frequent(List): return max(set(List), key = List.count) types=most_frequent(self.vehicle_types_list) return types # if classes in ['bicycle', 'motorcycle']: # return 1 # elif classes =='car': # return 2 # elif classes=='bus': # return 3 # else: # return 4 @property def vehicle_type(self): def most_frequent(List): return max(set(List), key = List.count) if len(self.track_frames)>=self.type_infer_patience: return most_frequent(self.vehicle_types_list) elif self.score >=self.score_infer_type_thres: return most_frequent(self.vehicle_types_list) else: return 'Undetermine' @property # @jit(nopython=True) def tlwh(self): """Get current position in bounding box format `(top left x, top left y, width, height)`. """ if self.mean is None: return self._tlwh.copy() ret = self.mean[: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)`, i.e., `(top left, bottom right)`. """ ret = self.tlwh.copy() 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 = np.asarray(tlwh).copy() ret[:2] += ret[2:] / 2 ret[2] /= ret[3] return ret def to_xyah(self): return self.tlwh_to_xyah(self.tlwh) @staticmethod # @jit(nopython=True) def tlbr_to_tlwh(tlbr): ret = np.asarray(tlbr).copy() ret[2:] -= ret[:2] return ret @staticmethod # @jit(nopython=True) def tlwh_to_tlbr(tlwh): ret = np.asarray(tlwh).copy() ret[2:] += ret[:2] return ret def __repr__(self): return 'OT_{}_({}-{})'.format(self.track_id, self.start_frame, self.end_frame)
class Track(BaseTrack): shared_kalman = KalmanFilter() def __init__(self, tlwh, score, temp_feat, buff_size=30): """ :param tlwh: :param score: :param temp_feat: :param buff_size: """ # wait activate self._tlwh = np.asarray(tlwh, dtype=np.float) self.kalman_filter = None self.mean, self.covariance = None, None self.is_activated = False self.score = score self.track_len = 0 self.smooth_feat = None self.update_features(temp_feat) self.features = deque([], maxlen=buff_size) # 指定了限制长度 self.alpha = 0.9 def update_features(self, feat): # L2 normalizing feat /= np.linalg.norm(feat) self.curr_feat = feat if self.smooth_feat is None: self.smooth_feat = feat else: self.smooth_feat = self.alpha * self.smooth_feat + (1 - self.alpha) * feat self.features.append(feat) self.smooth_feat /= np.linalg.norm(self.smooth_feat) def predict(self): mean_state = self.mean.copy() if self.state != TrackState.Tracked: mean_state[7] = 0 self.mean, self.covariance = self.kalman_filter.predict(mean_state, self.covariance) @staticmethod def multi_predict(tracks): if len(tracks) > 0: multi_mean = np.asarray([track.mean.copy() for track in tracks]) multi_covariance = np.asarray([track.covariance for track in tracks]) for i, st in enumerate(tracks): if st.state != TrackState.Tracked: multi_mean[i][7] = 0 multi_mean, multi_covariance = Track.shared_kalman.multi_predict(multi_mean, multi_covariance) for i, (mean, cov) in enumerate(zip(multi_mean, multi_covariance)): tracks[i].mean = mean tracks[i].covariance = cov def reset_track_id(self): self.reset_track_count() def activate(self, kalman_filter, frame_id): """Start a new tracklet""" self.kalman_filter = kalman_filter # assign a filter to each tracklet? # update the track id self.track_id = self.next_id() self.mean, self.covariance = self.kalman_filter.initiate(self.tlwh_to_xyah(self._tlwh)) self.track_len = 0 self.state = TrackState.Tracked # set flag 'tracked' # self.is_activated = True if frame_id == 1: # to record the first frame's detection result 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.mean, self.covariance = self.kalman_filter.update(self.mean, self.covariance, self.tlwh_to_xyah(new_track.tlwh)) self.update_features(new_track.curr_feat) self.track_len = 0 self.state = TrackState.Tracked # set flag 'tracked' self.is_activated = True self.frame_id = frame_id if new_id: # update the track id self.track_id = self.next_id() def update(self, new_track, frame_id, update_feature=True): """ Update a matched track :type new_track: Track :type frame_id: int :type update_feature: bool :return: """ self.frame_id = frame_id self.track_len += 1 new_tlwh = new_track.tlwh self.mean, self.covariance = self.kalman_filter.update(self.mean, self.covariance, self.tlwh_to_xyah(new_tlwh)) self.state = TrackState.Tracked # set flag 'tracked' self.is_activated = True # set flag 'activated' self.score = new_track.score if update_feature: self.update_features(new_track.curr_feat) @property # @jit(nopython=True) def tlwh(self): """Get current position in bounding box format `(top left x, top left y, width, height)`. """ if self.mean is None: return self._tlwh.copy() ret = self.mean[: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)`, i.e., `(top left, bottom right)`. """ ret = self.tlwh.copy() 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 = np.asarray(tlwh).copy() ret[:2] += ret[2:] / 2 ret[2] /= ret[3] return ret def to_xyah(self): return self.tlwh_to_xyah(self.tlwh) @staticmethod # @jit(nopython=True) def tlbr_to_tlwh(tlbr): ret = np.asarray(tlbr).copy() # numpy中的.copy()是深拷贝 ret[2:] -= ret[:2] return ret @staticmethod # @jit(nopython=True) def tlwh_to_tlbr(tlwh): ret = np.asarray(tlwh).copy() ret[2:] += ret[:2] return ret def __repr__(self): return 'OT_{}_({}-{})'.format(self.track_id, self.start_frame, self.end_frame)
def __init__(self, opt,polygon, paths, polygon2=None,frame_rate=30): self.opt = opt if opt.gpus[0] >= 0: opt.device = torch.device('cuda') else: opt.device = torch.device('cpu') print('Creating model...') anchor_ratios = [(1.0, 1.0), (1.4, 0.7), (0.7, 1.4)] anchor_scales = [2 ** 0, 2 ** (1.0 / 3.0), 2 ** (2.0 / 3.0)] input_sizes = [512, 640, 768, 896, 1024, 1280, 1280, 1536] self.input_size = input_sizes[opt.compound_coef] if opt.detection_model=='Efficient' : self.obj_list =['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', '', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', '', 'backpack', 'umbrella', '', '', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket', 'bottle', '', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', '', 'dining table', '', '', 'toilet', '', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', '', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush'] self.person_or_motorcycle=['person'] self.obj_interest=[ 'motorcycle','bicycle', 'bus', 'truck','car'] if self.person_or_motorcycle[0]!='person' else [ 'person', 'bus', 'truck','car'] self.detetection_model= EfficientDetBackbone(compound_coef=opt.compound_coef, num_classes=len(self.obj_list), ratios=anchor_ratios, scales=anchor_scales) self.detetection_model.load_state_dict(torch.load(f'EfficientDet/weights/efficientdet-d{opt.compound_coef}.pth')) self.detetection_model.eval() device = torch.device('cuda:0') self.detetection_model = self.detetection_model.to(device) elif opt.detection_model=='FasterRcnn' : config_file = "Drone_FasterRCNN/drone_demo/e2e_faster_rcnn_X_101_32x8d_FPN_1x_visdrone.yaml" cfg.merge_from_file(config_file) cfg.merge_from_list(["MODEL.WEIGHT", "Drone_FasterRCNN/drone_demo/visdrone_model_0360000.pth"]) self.detetection_model = COCODemo( cfg, min_image_size=opt.min_img_size, confidence_threshold=opt.conf_thres, ) label_of_interest=[ # "__background", # "unused", # "pedestrian", # "person", # "bicycle", "car", "van", "truck", # "tricycle", # "awning-tricycle", "bus", "motor" ] self.person_or_motorcycle=["motor"] #'bicycle' self.obj_interest=[ 'motor', 'bus', 'truck','car','van', "tricycle"] if self.person_or_motorcycle[0]!='person' else [ 'person', 'bus', 'truck','car','van', "tricycle"] else: raise('Not supported detector model') self.tracked_stracks = [] # type: list[STrack] self.lost_stracks = [] # type: list[STrack] self.removed_stracks = [] # type: list[STrack] self.frame_id = 0 self.det_thresh = opt.conf_thres self.buffer_size = int(frame_rate / 30.0 * opt.track_buffer) self.max_time_lost = self.buffer_size self.max_per_image = opt.K self.kalman_filter = KalmanFilter() self.polygon=polygon self.paths=paths self.polygon2=polygon2 self.line1=[polygon[0],polygon[1]] self.line2=[polygon[1],polygon[2]] self.line3=[polygon[2],polygon[3]] self.line4=[polygon[0],polygon[4]] self.two_polygon_system=True self.warmup_frame=0 self.virtual_polygon= [ [ 0, 680 ], [ 0, 149 ], [ 1270, 149 ], [ 1270, 680 ] ]
def __init__(self, opt): self.opt = opt self.tracked_stracks = [] # type: list[STrack] self.frame_id = 0 self.kalman_filter = KalmanFilter()
class STrack(BaseTrack): shared_kalman = KalmanFilter() out_of_frame_patience = 5 num_cluster = 5 def __init__(self, tlwh, score, temp_feat, buffer_size=30): # wait activate self._tlwh = np.asarray(tlwh, dtype=np.float) self.kalman_filter = None self.mean, self.covariance = None, None self.is_activated = False self.score = score self.tracklet_len = 0 self.smooth_feat = None self.update_features(temp_feat, None) self.alpha = 0.9 self.occlusion_status = False # use for bbox only self.iou_box = None #use for bbox only self.num_out_frame = 0 def update_features(self, feat, new_track): feat /= np.linalg.norm(feat) self.curr_feat = feat if self.smooth_feat is None: self.smooth_feat = feat else: self.smooth_feat = self.alpha * self.smooth_feat + ( 1 - self.alpha) * feat self.smooth_feat /= np.linalg.norm(self.smooth_feat) @staticmethod def warp_predict(mean, cov, warp_matrix, warp_mode): if warp_matrix is None: return mean, cov track_xyah = mean[:4] track_tlwh = STrack.xyah_to_tlwh(track_xyah) track_tlbr = STrack.tlwh_to_tlbr(track_tlwh) t, l, b, r = track_tlbr if warp_mode == cv2.MOTION_HOMOGRAPHY: warp_tlbr = cv2.perspectiveTransform(np.array([[[t, l], [b, r]]]), warp_matrix)[0].flatten() else: warp_tlbr = cv2.transform(np.array([[[t, l], [b, r]]]), warp_matrix)[0].flatten() warp_tlwh = STrack.tlbr_to_tlwh(warp_tlbr) warp_xyah = STrack.tlwh_to_xyah(warp_tlwh) track_mean, track_cov = list(warp_xyah) + list(mean[4:]), cov return np.array(track_mean), track_cov @staticmethod def get_camera_intension(warp_matrix, warp_mode): if warp_matrix is None: return 0 warp_matrix_flattern = warp_matrix.flatten() if warp_mode == cv2.MOTION_HOMOGRAPHY: non_change_warp = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) else: non_change_warp = np.array([1, 0, 0, 0, 1, 0]) similarity = np.dot(warp_matrix_flattern, non_change_warp) / ( np.sqrt(np.sum(warp_matrix_flattern**2)) * np.sqrt(np.sum(non_change_warp**2))) return 1 - similarity def predict(self, warp_matrix, warp_mode, smooth=0.0): mean_state = self.mean.copy() if self.state != TrackState.Tracked: mean_state[7] = 0 motion_intensity = STrack.get_camera_intension(warp_matrix, warp_mode) * smooth self.mean, self.covariance = self.kalman_filter.predict( mean_state, self.covariance, motion_intensity=motion_intensity) self.mean, self.covariance = STrack.warp_predict( self.mean, self.covariance, warp_matrix, warp_mode) @staticmethod def multi_predict(stracks, warp_matrix, warp_mode, smooth=0.0): if len(stracks) > 0: multi_mean = np.asarray([st.mean.copy() for st in stracks]) multi_covariance = np.asarray([st.covariance for st in stracks]) for i, st in enumerate(stracks): if st.state != TrackState.Tracked: multi_mean[i][7] = 0 motion_intensity = STrack.get_camera_intension( warp_matrix, warp_mode) * smooth multi_mean, multi_covariance = STrack.shared_kalman.multi_predict( multi_mean, multi_covariance, motion_intensity=motion_intensity) for i, (mean, cov) in enumerate(zip(multi_mean, multi_covariance)): stracks[i].mean = mean stracks[i].covariance = cov stracks[i].mean, stracks[i].covariance = STrack.warp_predict( stracks[i].mean, stracks[i].covariance, warp_matrix, warp_mode) def activate(self, kalman_filter, frame_id): """Start a new tracklet""" self.kalman_filter = kalman_filter self.track_id = self.next_id() self.mean, self.covariance = self.kalman_filter.initiate( self.tlwh_to_xyah(self._tlwh)) self.tracklet_len = 0 self.state = TrackState.Tracked #self.is_activated = True self.frame_id = frame_id self.start_frame = frame_id # self.box_hist.append(self.tlwh) # self.track_frames.append(frame_id) def re_activate(self, new_track, frame_id, new_id=False): self.mean, self.covariance = self.kalman_filter.update( self.mean, self.covariance, self.tlwh_to_xyah(new_track.tlwh)) self.update_features(new_track.curr_feat, new_track) #self.update_cluster(new_track.curr_feat) self.tracklet_len = 0 self.state = TrackState.Tracked self.is_activated = True self.frame_id = frame_id if new_id: self.track_id = self.next_id() def update(self, new_track, frame_id, update_feature=True): """ 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.mean, self.covariance = self.kalman_filter.update( self.mean, self.covariance, self.tlwh_to_xyah(new_tlwh)) self.state = TrackState.Tracked self.is_activated = True self.score = new_track.score if update_feature: self.update_features(new_track.curr_feat, new_track) @property # @jit(nopython=True) def tlwh(self): """Get current position in bounding box format `(top left x, top left y, width, height)`. """ if self.mean is None: return self._tlwh.copy() ret = self.mean[: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)`, i.e., `(top left, bottom right)`. """ ret = self.tlwh.copy() 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 = np.asarray(tlwh).copy() ret[:2] += ret[2:] / 2 ret[2] /= ret[3] return ret @staticmethod # @jit(nopython=True) def xyah_to_tlwh(xyah): w, h = xyah[2] * xyah[3], xyah[3] x, y = xyah[0], xyah[1] t, l = x - w / 2, y - h / 2 return [t, l, w, h] def to_xyah(self): return self.tlwh_to_xyah(self.tlwh) @staticmethod # @jit(nopython=True) def tlbr_to_tlwh(tlbr): ret = np.asarray(tlbr).copy() ret[2:] -= ret[:2] return ret @staticmethod # @jit(nopython=True) def tlwh_to_tlbr(tlwh): ret = np.asarray(tlwh).copy() ret[2:] += ret[:2] return ret def __repr__(self): return 'OT_{}_({}-{})'.format(self.track_id, self.start_frame, self.end_frame)
class STrack(BaseTrack): shared_kalman = KalmanFilter() # 类变量 def __init__(self, tlwh, score, temp_feat, buffer_size=30): # wait activate self._tlwh = np.asarray(tlwh, dtype=np.float) self.kalman_filter = None self.mean, self.covariance = None, None self.is_activated = False self.score = score self.tracklet_len = 0 self.smooth_feat = None self.update_features(temp_feat) self.features = deque([], maxlen=buffer_size) self.alpha = 0.9 def update_features(self, feat): # 增加新的feature,添加到self.feature、self.curr_feat feat /= np.linalg.norm(feat) self.curr_feat = feat if self.smooth_feat is None: self.smooth_feat = feat else: self.smooth_feat = self.alpha * self.smooth_feat + (1 - self.alpha) * feat # 指数移动加权平均,self.smooth_feat综合了过去一定量的feature self.features.append(feat) self.smooth_feat /= np.linalg.norm(self.smooth_feat) # 归一化处理 def predict(self): mean_state = self.mean.copy() if self.state != TrackState.Tracked: mean_state[7] = 0 self.mean, self.covariance = self.kalman_filter.predict(mean_state, self.covariance) @staticmethod def multi_predict(stracks): if len(stracks) > 0: multi_mean = np.asarray([st.mean.copy() for st in stracks]) multi_covariance = np.asarray([st.covariance for st in stracks]) for i, st in enumerate(stracks): if st.state != TrackState.Tracked: 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 stracks[i].mean = mean stracks[i].covariance = cov def activate(self, kalman_filter, frame_id): # 创建一个新的 track """Start a new tracklet""" self.kalman_filter = kalman_filter self.track_id = self.next_id() self.mean, self.covariance = self.kalman_filter.initiate(self.tlwh_to_xyah(self._tlwh)) # 使用当前位置状态进行初始化 self.tracklet_len = 0 self.state = TrackState.Tracked #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): # 新创建一个 track self.mean, self.covariance = self.kalman_filter.update( self.mean, self.covariance, self.tlwh_to_xyah(new_track.tlwh) ) self.update_features(new_track.curr_feat) self.tracklet_len = 0 self.state = TrackState.Tracked self.is_activated = True self.frame_id = frame_id if new_id: self.track_id = self.next_id() def update(self, new_track, frame_id, update_feature=True): # 更新对应的 KF 中的均值向量、协方差矩阵 """ Update a matched track :type new_track: STrack :type frame_id: int :type update_feature: bool :return: """ self.frame_id = frame_id # 当前的帧id self.tracklet_len += 1 # 追踪片段长度增加 1 new_tlwh = new_track.tlwh self.mean, self.covariance = self.kalman_filter.update( # 卡尔曼滤波器 self.mean, self.covariance, self.tlwh_to_xyah(new_tlwh)) self.state = TrackState.Tracked self.is_activated = True self.score = new_track.score if update_feature: self.update_features(new_track.curr_feat) @property # @jit(nopython=True) def tlwh(self): """Get current position in bounding box format `(top left x, top left y, width, height)`. """ if self.mean is None: return self._tlwh.copy() ret = self.mean[: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)`, i.e., `(top left, bottom right)`. """ ret = self.tlwh.copy() 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 = np.asarray(tlwh).copy() ret[:2] += ret[2:] / 2 ret[2] /= ret[3] return ret def to_xyah(self): return self.tlwh_to_xyah(self.tlwh) @staticmethod # @jit(nopython=True) def tlbr_to_tlwh(tlbr): ret = np.asarray(tlbr).copy() ret[2:] -= ret[:2] return ret @staticmethod # @jit(nopython=True) def tlwh_to_tlbr(tlwh): ret = np.asarray(tlwh).copy() ret[2:] += ret[:2] return ret def __repr__(self): return 'OT_{}_({}-{})'.format(self.track_id, self.start_frame, self.end_frame)