# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ This code is based on https://github.com/LCFractal/AIC21-MTMC/tree/main/reid/reid-matching/tools Note: The following codes are strongly related to camera parameters of the AIC21 test-set S06, so they can only be used in S06, and can not be used for other MTMCT datasets. """ import numpy as np try: from sklearn.cluster import AgglomerativeClustering except: print( 'Warning: Unable to use MTMCT in PP-Tracking, please install sklearn, for example: `pip install sklearn`' ) pass from .utils import get_dire, get_match, get_cid_tid, combin_feature, combin_cluster from .utils import normalize, intracam_ignore, visual_rerank __all__ = [ 'st_filter', 'get_labels_with_camera', ] CAM_DIST = [[0, 40, 55, 100, 120, 145], [40, 0, 15, 60, 80, 105], [55, 15, 0, 40, 65, 90], [100, 60, 40, 0, 20, 45], [120, 80, 65, 20, 0, 25], [145, 105, 90, 45, 25, 0]] def st_filter(st_mask, cid_tids, cid_tid_dict): count = len(cid_tids) for i in range(count): i_tracklet = cid_tid_dict[cid_tids[i]] i_cid = i_tracklet['cam'] i_dire = get_dire(i_tracklet['zone_list'], i_cid) i_iot = i_tracklet['io_time'] for j in range(count): j_tracklet = cid_tid_dict[cid_tids[j]] j_cid = j_tracklet['cam'] j_dire = get_dire(j_tracklet['zone_list'], j_cid) j_iot = j_tracklet['io_time'] match_dire = True cam_dist = CAM_DIST[i_cid - 41][j_cid - 41] # if time overlopped if i_iot[0] - cam_dist < j_iot[0] and j_iot[0] < i_iot[ 1] + cam_dist: match_dire = False if i_iot[0] - cam_dist < j_iot[1] and j_iot[1] < i_iot[ 1] + cam_dist: match_dire = False # not match after go out if i_dire[1] in [1, 2]: # i out if i_iot[0] < j_iot[1] + cam_dist: match_dire = False if i_dire[1] in [1, 2]: if i_dire[0] in [3] and i_cid > j_cid: match_dire = False if i_dire[0] in [4] and i_cid < j_cid: match_dire = False if i_cid in [41] and i_dire[1] in [4]: if i_iot[0] < j_iot[1] + cam_dist: match_dire = False if i_iot[1] > 199: match_dire = False if i_cid in [46] and i_dire[1] in [3]: if i_iot[0] < j_iot[1] + cam_dist: match_dire = False # match after come into if i_dire[0] in [1, 2]: if i_iot[1] > j_iot[0] - cam_dist: match_dire = False if i_dire[0] in [1, 2]: if i_dire[1] in [3] and i_cid > j_cid: match_dire = False if i_dire[1] in [4] and i_cid < j_cid: match_dire = False is_ignore = False if ((i_dire[0] == i_dire[1] and i_dire[0] in [3, 4]) or (j_dire[0] == j_dire[1] and j_dire[0] in [3, 4])): is_ignore = True if not is_ignore: # direction conflict if (i_dire[0] in [3] and j_dire[0] in [4]) or ( i_dire[1] in [3] and j_dire[1] in [4]): match_dire = False # filter before going next scene if i_dire[1] in [3] and i_cid < j_cid: if i_iot[1] > j_iot[1] - cam_dist: match_dire = False if i_dire[1] in [4] and i_cid > j_cid: if i_iot[1] > j_iot[1] - cam_dist: match_dire = False if i_dire[0] in [3] and i_cid < j_cid: if i_iot[0] < j_iot[0] + cam_dist: match_dire = False if i_dire[0] in [4] and i_cid > j_cid: if i_iot[0] < j_iot[0] + cam_dist: match_dire = False ## 3-30 ## 4-1 if i_dire[0] in [3] and i_cid > j_cid: if i_iot[1] > j_iot[0] - cam_dist: match_dire = False if i_dire[0] in [4] and i_cid < j_cid: if i_iot[1] > j_iot[0] - cam_dist: match_dire = False # filter before going next scene ## 4-7 if i_dire[1] in [3] and i_cid > j_cid: if i_iot[0] < j_iot[1] + cam_dist: match_dire = False if i_dire[1] in [4] and i_cid < j_cid: if i_iot[0] < j_iot[1] + cam_dist: match_dire = False else: if i_iot[1] > 199: if i_dire[0] in [3] and i_cid < j_cid: if i_iot[0] < j_iot[0] + cam_dist: match_dire = False if i_dire[0] in [4] and i_cid > j_cid: if i_iot[0] < j_iot[0] + cam_dist: match_dire = False if i_dire[0] in [3] and i_cid > j_cid: match_dire = False if i_dire[0] in [4] and i_cid < j_cid: match_dire = False if i_iot[0] < 1: if i_dire[1] in [3] and i_cid > j_cid: match_dire = False if i_dire[1] in [4] and i_cid < j_cid: match_dire = False if not match_dire: st_mask[i, j] = 0.0 st_mask[j, i] = 0.0 return st_mask def subcam_list(cid_tid_dict, cid_tids): sub_3_4 = dict() sub_4_3 = dict() for cid_tid in cid_tids: cid, tid = cid_tid tracklet = cid_tid_dict[cid_tid] zs, ze = get_dire(tracklet['zone_list'], cid) if zs in [3] and cid not in [46]: # 4 to 3 if not cid + 1 in sub_4_3: sub_4_3[cid + 1] = [] sub_4_3[cid + 1].append(cid_tid) if ze in [4] and cid not in [41]: # 4 to 3 if not cid in sub_4_3: sub_4_3[cid] = [] sub_4_3[cid].append(cid_tid) if zs in [4] and cid not in [41]: # 3 to 4 if not cid - 1 in sub_3_4: sub_3_4[cid - 1] = [] sub_3_4[cid - 1].append(cid_tid) if ze in [3] and cid not in [46]: # 3 to 4 if not cid in sub_3_4: sub_3_4[cid] = [] sub_3_4[cid].append(cid_tid) sub_cid_tids = dict() for i in sub_3_4: sub_cid_tids[(i, i + 1)] = sub_3_4[i] for i in sub_4_3: sub_cid_tids[(i, i - 1)] = sub_4_3[i] return sub_cid_tids def subcam_list2(cid_tid_dict, cid_tids): sub_dict = dict() for cid_tid in cid_tids: cid, tid = cid_tid if cid not in [41]: if not cid in sub_dict: sub_dict[cid] = [] sub_dict[cid].append(cid_tid) if cid not in [46]: if not cid + 1 in sub_dict: sub_dict[cid + 1] = [] sub_dict[cid + 1].append(cid_tid) return sub_dict def get_sim_matrix(cid_tid_dict, cid_tids, use_ff=True, use_rerank=True, use_st_filter=False): # Note: camera releated get_sim_matrix function, # which is different from the one in utils.py. count = len(cid_tids) q_arr = np.array( [cid_tid_dict[cid_tids[i]]['mean_feat'] for i in range(count)]) g_arr = np.array( [cid_tid_dict[cid_tids[i]]['mean_feat'] for i in range(count)]) q_arr = normalize(q_arr, axis=1) g_arr = normalize(g_arr, axis=1) st_mask = np.ones((count, count), dtype=np.float32) st_mask = intracam_ignore(st_mask, cid_tids) # different from utils.py if use_st_filter: st_mask = st_filter(st_mask, cid_tids, cid_tid_dict) visual_sim_matrix = visual_rerank( q_arr, g_arr, cid_tids, use_ff=use_ff, use_rerank=use_rerank) visual_sim_matrix = visual_sim_matrix.astype('float32') np.set_printoptions(precision=3) sim_matrix = visual_sim_matrix * st_mask np.fill_diagonal(sim_matrix, 0) return sim_matrix def get_labels_with_camera(cid_tid_dict, cid_tids, use_ff=True, use_rerank=True, use_st_filter=False): # 1st cluster sub_cid_tids = subcam_list(cid_tid_dict, cid_tids) sub_labels = dict() dis_thrs = [0.7, 0.5, 0.5, 0.5, 0.5, 0.7, 0.5, 0.5, 0.5, 0.5] for i, sub_c_to_c in enumerate(sub_cid_tids): sim_matrix = get_sim_matrix( cid_tid_dict, sub_cid_tids[sub_c_to_c], use_ff=use_ff, use_rerank=use_rerank, use_st_filter=use_st_filter) cluster_labels = AgglomerativeClustering( n_clusters=None, distance_threshold=1 - dis_thrs[i], affinity='precomputed', linkage='complete').fit_predict(1 - sim_matrix) labels = get_match(cluster_labels) cluster_cid_tids = get_cid_tid(labels, sub_cid_tids[sub_c_to_c]) sub_labels[sub_c_to_c] = cluster_cid_tids labels, sub_cluster = combin_cluster(sub_labels, cid_tids) # 2nd cluster cid_tid_dict_new = combin_feature(cid_tid_dict, sub_cluster) sub_cid_tids = subcam_list2(cid_tid_dict_new, cid_tids) sub_labels = dict() for i, sub_c_to_c in enumerate(sub_cid_tids): sim_matrix = get_sim_matrix( cid_tid_dict_new, sub_cid_tids[sub_c_to_c], use_ff=use_ff, use_rerank=use_rerank, use_st_filter=use_st_filter) cluster_labels = AgglomerativeClustering( n_clusters=None, distance_threshold=1 - 0.1, affinity='precomputed', linkage='complete').fit_predict(1 - sim_matrix) labels = get_match(cluster_labels) cluster_cid_tids = get_cid_tid(labels, sub_cid_tids[sub_c_to_c]) sub_labels[sub_c_to_c] = cluster_cid_tids labels, sub_cluster = combin_cluster(sub_labels, cid_tids) return labels