├── datasets ├── .gitkeep └── AIC19 │ └── .gitkeep ├── models └── .gitkeep ├── results └── .gitkeep ├── network ├── __init__.py ├── __pycache__ │ ├── vgg.cpython-37.pyc │ ├── __init__.cpython-37.pyc │ ├── net_ristani.cpython-37.pyc │ ├── resnet_elg.cpython-37.pyc │ ├── joined_network.cpython-37.pyc │ └── net_id_classifier.cpython-37.pyc ├── net_ristani.py ├── net_id_classifier.py ├── joined_network.py ├── resnet_elg.py └── vgg.py ├── thirdparty ├── __init__.py ├── dtw │ ├── version.py │ ├── __init__.py │ ├── __pycache__ │ │ ├── dtw.cpython-37.pyc │ │ ├── version.cpython-37.pyc │ │ └── __init__.cpython-37.pyc │ └── dtw.py ├── __pycache__ │ ├── bbox.cpython-37.pyc │ ├── __init__.cpython-37.pyc │ └── sklearn_dunn.cpython-37.pyc ├── bbox.py └── sklearn_dunn.py ├── libs ├── __pycache__ │ ├── sct.cpython-37.pyc │ ├── camera.cpython-37.pyc │ ├── colors.cpython-37.pyc │ ├── dataset.cpython-37.pyc │ ├── display.cpython-37.pyc │ ├── features.cpython-37.pyc │ ├── tracking.cpython-37.pyc │ └── clustering.cpython-37.pyc ├── display.py ├── dataset.py ├── camera.py ├── clustering.py ├── sct.py ├── features.py ├── colors.py └── tracking.py ├── misc ├── __pycache__ │ ├── nms.cpython-37.pyc │ ├── utils.cpython-37.pyc │ └── normalize.cpython-37.pyc ├── normalize.py ├── crop_img.py ├── crop_img_big.py ├── utils.py └── nms.py ├── preprocessing_data ├── __pycache__ │ └── preprocess_data.cpython-37.pyc ├── rename_files.py ├── preprocess_veri_dataset.py ├── preprocess_data.py ├── divide_datasets_per_ids.py └── divide_subset_train_test_ids.py ├── config └── config.yaml ├── scripts └── run_tests.sh ├── README.md ├── postprocessing └── unsync.py ├── env_MTMC.yaml └── main.py /datasets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /results/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /datasets/AIC19/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /network/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /thirdparty/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /thirdparty/dtw/version.py: -------------------------------------------------------------------------------- 1 | version = '1.4.0' 2 | -------------------------------------------------------------------------------- /thirdparty/dtw/__init__.py: -------------------------------------------------------------------------------- 1 | from .dtw import dtw, accelerated_dtw 2 | from .version import version as __version__ 3 | -------------------------------------------------------------------------------- /libs/__pycache__/sct.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/libs/__pycache__/sct.cpython-37.pyc -------------------------------------------------------------------------------- /misc/__pycache__/nms.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/misc/__pycache__/nms.cpython-37.pyc -------------------------------------------------------------------------------- /libs/__pycache__/camera.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/libs/__pycache__/camera.cpython-37.pyc -------------------------------------------------------------------------------- /libs/__pycache__/colors.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/libs/__pycache__/colors.cpython-37.pyc -------------------------------------------------------------------------------- /libs/__pycache__/dataset.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/libs/__pycache__/dataset.cpython-37.pyc -------------------------------------------------------------------------------- /libs/__pycache__/display.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/libs/__pycache__/display.cpython-37.pyc -------------------------------------------------------------------------------- /misc/__pycache__/utils.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/misc/__pycache__/utils.cpython-37.pyc -------------------------------------------------------------------------------- /network/__pycache__/vgg.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/network/__pycache__/vgg.cpython-37.pyc -------------------------------------------------------------------------------- /libs/__pycache__/features.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/libs/__pycache__/features.cpython-37.pyc -------------------------------------------------------------------------------- /libs/__pycache__/tracking.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/libs/__pycache__/tracking.cpython-37.pyc -------------------------------------------------------------------------------- /misc/__pycache__/normalize.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/misc/__pycache__/normalize.cpython-37.pyc -------------------------------------------------------------------------------- /libs/__pycache__/clustering.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/libs/__pycache__/clustering.cpython-37.pyc -------------------------------------------------------------------------------- /network/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/network/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /thirdparty/__pycache__/bbox.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/thirdparty/__pycache__/bbox.cpython-37.pyc -------------------------------------------------------------------------------- /network/__pycache__/net_ristani.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/network/__pycache__/net_ristani.cpython-37.pyc -------------------------------------------------------------------------------- /network/__pycache__/resnet_elg.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/network/__pycache__/resnet_elg.cpython-37.pyc -------------------------------------------------------------------------------- /thirdparty/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/thirdparty/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /thirdparty/dtw/__pycache__/dtw.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/thirdparty/dtw/__pycache__/dtw.cpython-37.pyc -------------------------------------------------------------------------------- /network/__pycache__/joined_network.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/network/__pycache__/joined_network.cpython-37.pyc -------------------------------------------------------------------------------- /thirdparty/dtw/__pycache__/version.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/thirdparty/dtw/__pycache__/version.cpython-37.pyc -------------------------------------------------------------------------------- /thirdparty/__pycache__/sklearn_dunn.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/thirdparty/__pycache__/sklearn_dunn.cpython-37.pyc -------------------------------------------------------------------------------- /thirdparty/dtw/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/thirdparty/dtw/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /network/__pycache__/net_id_classifier.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/network/__pycache__/net_id_classifier.cpython-37.pyc -------------------------------------------------------------------------------- /preprocessing_data/__pycache__/preprocess_data.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpulab/Online-Clustering-based-MTMC-Tracking/HEAD/preprocessing_data/__pycache__/preprocess_data.cpython-37.pyc -------------------------------------------------------------------------------- /misc/normalize.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ################################ 3 | # L2 NORMALIZATION # 4 | ################################ 5 | ''' 6 | 7 | def l2_norm(x): 8 | 9 | 10 | norm = x.pow(2).sum().pow(1. /2) 11 | out = x.div(norm) 12 | 13 | return out 14 | -------------------------------------------------------------------------------- /config/config.yaml: -------------------------------------------------------------------------------- 1 | ID: prueba 2 | DATASET_PATH: ./datasets/AIC19/ 3 | DETECTOR: det/det_mask_rcnn 4 | SCORE_TH: 0.2 5 | NUM_IDS: 903 6 | FLAG_FILTER_SIZE: False 7 | MODE: both 8 | MODEL: ResNet50_AIC20_VERI_layer5_imaugclassifier_latest.pth.tar 9 | DIST_TH: 0.00008 10 | BLIND_OCCLUSION: True 11 | REPROJECTION: True 12 | SIZE_FC: 2048 13 | NMS: False 14 | AUG_SIZE: 0.2 -------------------------------------------------------------------------------- /scripts/run_tests.sh: -------------------------------------------------------------------------------- 1 | # This file should be executed from terminal in Folder 'Scripts' with the following command: 2 | # source Run_Tests.sh 3 | 4 | #!/bin/bash 5 | 6 | source activate env_MTMC 7 | 8 | 9 | 10 | cd .. 11 | chmod +x main.py 12 | python main.py --ConfigPath "config/config.yaml" 13 | cd scripts/ 14 | 15 | cd .. 16 | chmod +x main.py 17 | python main.py --ConfigPath "config/config2.yaml" 18 | cd scripts/ 19 | 20 | cd .. 21 | chmod +x main.py 22 | python main.py --ConfigPath "config/config3.yaml" 23 | cd scripts/ 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /preprocessing_data/rename_files.py: -------------------------------------------------------------------------------- 1 | import os 2 | import cv2 3 | import time 4 | import numpy as np 5 | from sklearn.model_selection import train_test_split 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | if __name__ == '__main__': 14 | 15 | dir = '/home/vpu/Datasets/AICC_veri/subset_train_aicc_veri' 16 | 17 | list_files = os.listdir(dir) 18 | num_files= list_files.__len__() 19 | 20 | for i in range(num_files): 21 | new_name = '0' + list_files[i] 22 | src = os.path.join(dir,list_files[i]) 23 | dst = os.path.join(dir,new_name) 24 | os.rename(src, dst) 25 | 26 | -------------------------------------------------------------------------------- /libs/display.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ################################ 3 | # Display # 4 | ################################ 5 | ''' 6 | 7 | import os 8 | import sys 9 | import cv2 10 | import math 11 | import time 12 | import numpy as np 13 | from PIL import Image 14 | from matplotlib import colors as mcolors 15 | from matplotlib.patches import Rectangle 16 | import matplotlib.pyplot as plt 17 | 18 | class display(): 19 | 20 | def __init__(self,flag): 21 | self.flag = flag 22 | 23 | def show_frame(self, img, c): 24 | if self.flag: 25 | plt.figure() 26 | plt.imshow(img) 27 | plt.title('Camera ' + str(c)) 28 | 29 | def draw_bbox(self,x,y,w,h): 30 | if self.flag: 31 | plt.gca().add_patch(Rectangle((x, y), w, h, linewidth=2, edgecolor='r', facecolor='none')) 32 | -------------------------------------------------------------------------------- /misc/crop_img.py: -------------------------------------------------------------------------------- 1 | import os 2 | import cv2 3 | import sys 4 | import numpy as np 5 | import os.path as osp 6 | 7 | FILE_LEN = 10 8 | 9 | lev1s = ["./S02/", "./S05/"] 10 | #lev1s = [ "./S05/"] 11 | 12 | OUT_DIR = "./track1_test_img/" 13 | 14 | 15 | for lev1 in lev1s: 16 | lev2s = os.listdir(lev1) 17 | for lev2 in lev2s: 18 | print(lev2) 19 | camera_path = osp.join(lev1, lev2) 20 | path_to_vid = osp.join(camera_path, "vdo.avi") 21 | 22 | vid = cv2.VideoCapture(path_to_vid) 23 | 24 | suc = True 25 | img = None 26 | 27 | count = 1 28 | 29 | out_path = osp.join(OUT_DIR, lev2) 30 | if not osp.isdir(out_path): 31 | os.makedirs(out_path) 32 | 33 | while suc: 34 | suc, img = vid.read() 35 | if img is None: 36 | break 37 | 38 | f_name = osp.join(out_path, str(count).zfill(10) + ".jpg") 39 | 40 | cv2.imwrite(f_name, img) 41 | count += 1 42 | -------------------------------------------------------------------------------- /misc/crop_img_big.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import os 3 | import os.path as osp 4 | 5 | IMG_DIR = "./track1_test_img/" 6 | OUT_DIR = "./track1_sct_img_test_big/" 7 | 8 | for res_f in os.listdir("/home/vpu/Repositories/SCT/TrackletNet/AIC19/txt_GPS_new/"): 9 | print(res_f) 10 | camid = res_f.split(".")[0] 11 | cam_img_path = osp.join(IMG_DIR, camid) 12 | out_cam_path = osp.join(OUT_DIR, camid) 13 | 14 | if not osp.isdir(out_cam_path): 15 | os.makedirs(out_cam_path) 16 | 17 | for line in open(osp.join("/home/vpu/Repositories/SCT/TrackletNet/AIC19/txt_GPS_new/", res_f)).readlines(): 18 | tmp = line.strip("\n").split(",") 19 | f_id = tmp[0] 20 | obj_id = tmp[1] 21 | 22 | img_f = osp.join(cam_img_path, f_id.zfill(10) + ".jpg") 23 | img = cv2.imread(img_f) 24 | 25 | height, width = img.shape[:2] 26 | 27 | 28 | left = int(tmp[2])-20 29 | top = int(tmp[3])-20 30 | w = int(tmp[4])+40 31 | h = int(tmp[5])+40 32 | 33 | right = left + w 34 | bot = top + h 35 | 36 | if left<0: 37 | left = 0 38 | if top<0: 39 | top=0 40 | 41 | if right>width: 42 | right = width 43 | if bot>height: 44 | bot=height 45 | 46 | 47 | 48 | crop_img = img[top: bot, left:right] 49 | 50 | out_obj_path = osp.join(out_cam_path, obj_id) 51 | if not osp.isdir(out_obj_path): 52 | os.makedirs(out_obj_path) 53 | 54 | out_path = osp.join(out_obj_path, f_id.zfill(10) + ".jpg") 55 | cv2.imwrite(out_path, crop_img) 56 | 57 | -------------------------------------------------------------------------------- /network/net_ristani.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | import torch 4 | import argparse 5 | import torch.nn as nn 6 | import torchvision 7 | from torchvision.models import resnet 8 | 9 | 10 | 11 | 12 | class net(nn.Module): 13 | 14 | def __init__(self, architecture): 15 | 16 | super(net, self).__init__() 17 | 18 | if architecture == 'ResNet50': 19 | 20 | base = resnet.resnet50(pretrained=True) 21 | 22 | # First initial block 23 | self.in_block = nn.Sequential( 24 | nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False), 25 | nn.BatchNorm2d(64), 26 | nn.ReLU(inplace=True), 27 | nn.MaxPool2d(kernel_size=3, stride=2, padding=1, return_indices=True)) 28 | 29 | self.encoder1 = base.layer1 30 | self.encoder2 = base.layer2 31 | self.encoder3 = base.layer3 32 | self.encoder4 = base.layer4 33 | self.avgpool7 = nn.AvgPool2d(7, stride=1) 34 | 35 | self.fc = nn.Linear(2048, 1024) 36 | self.BN = nn.BatchNorm1d(1024) 37 | self.ReLU= nn.ReLU(inplace=True) 38 | 39 | 40 | self.fc2 = nn.Linear(1024, 128) 41 | 42 | 43 | 44 | def forward(self, x): 45 | """ 46 | Network forward 47 | :param x: RGB Image 48 | :param sem: Semantic Segmentation score tensor 49 | :retu 50 | """ 51 | 52 | x, pool_indices = self.in_block(x) 53 | e1 = self.encoder1(x) 54 | e2 = self.encoder2(e1) 55 | e3 = self.encoder3(e2) 56 | e4 = self.encoder4(e3) 57 | 58 | x2 = self.avgpool7(e4) 59 | x = x2.view(x2.size(0), -1) 60 | 61 | x = self.fc(x) 62 | x = self.BN(x) 63 | x = self.ReLU(x) 64 | feat = self.fc2(x) 65 | 66 | 67 | 68 | 69 | return feat 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /libs/dataset.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ################################ 3 | # Dataset # 4 | ################################ 5 | ''' 6 | 7 | import os 8 | import sys 9 | import cv2 10 | import math 11 | import time 12 | import numpy as np 13 | from PIL import Image 14 | from scipy import interpolate 15 | from scipy.spatial.distance import cdist 16 | from torchvision import transforms 17 | 18 | 19 | class dataset(): 20 | 21 | def __init__(self): 22 | self.data_transform = transforms.Compose([ 23 | transforms.Resize((224,224)), 24 | transforms.ToTensor(), 25 | transforms.Normalize( mean=[0.485, 0.456, 0.406], 26 | std=[0.229, 0.224, 0.225]) 27 | ]) 28 | 29 | def pad(self,img, value): 30 | 31 | h = img.height 32 | w = img.width 33 | index, v = max(enumerate([h, w])) 34 | max_value = [h, w][index] 35 | 36 | # amount of pixels being replicated at each direction 37 | if index == 0: # h > w no need to pad in top and bottom 38 | top = 0 39 | bottom = 0 40 | left, right = int(round((h - w) / 2)), int(round((h - w) / 2)) 41 | elif index == 1: # w > h no need to pad in right and left 42 | left = 0 43 | right = 0 44 | top, bottom = int(round((w - h) / 2)), int(round((w - h) / 2)), 45 | 46 | padded = transforms.functional.pad(img,(left,top, right, bottom),value, 'constant') 47 | 48 | return padded 49 | 50 | def square(self, bbox_img, frame_img, x,y): 51 | 52 | h = bbox_img.height 53 | w = bbox_img.width 54 | 55 | # Center of the bbox 56 | cx = round(x + round(w / 2)) 57 | cy = round(y + round(h / 2)) 58 | 59 | # Square bbox 60 | 61 | square_size = max(w, h) 62 | sx = int(cx - square_size / 2) 63 | sy = int(cy - square_size / 2) 64 | 65 | square_bbox = transforms.functional.crop(frame_img, sy, sx, square_size, square_size) 66 | return square_bbox -------------------------------------------------------------------------------- /libs/camera.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ################################ 3 | # Camera # 4 | ################################ 5 | ''' 6 | 7 | import os 8 | import sys 9 | import cv2 10 | import math 11 | import time 12 | import numpy as np 13 | from PIL import Image 14 | from scipy import interpolate 15 | from scipy.spatial.distance import cdist 16 | 17 | 18 | class camera(): 19 | 20 | def __init__(self,calibration_dir): 21 | self.homography_matrix = {} 22 | self.calibration_dir = calibration_dir 23 | 24 | def apply_homography_image_to_world(self,xi, yi, H_world_to_image): 25 | # Spatial vector xi, yi, 1 26 | S = np.array([xi, yi, 1]).reshape(3, 1) 27 | # Get 3 x 3 matrix and compute inverse 28 | H_world_to_image = np.array(H_world_to_image).reshape(3, 3) 29 | H_image_to_world = np.linalg.inv(H_world_to_image) 30 | # Dot product 31 | prj = np.dot(H_image_to_world, S) 32 | # Get world coordinates 33 | xw = (prj[0] / prj[2]).item() # latitude 34 | yw = (prj[1] / prj[2]).item() # longitude 35 | return xw, yw 36 | 37 | 38 | def apply_homography_world_to_image(self,xi, yi, H_world_to_image): 39 | # Spatial vector xi, yi, 1 40 | S = np.array([xi, yi, 1]).reshape(3, 1) 41 | # Get 3 x 3 matrix and compute inverse 42 | H_world_to_image = np.array(H_world_to_image).reshape(3, 3) 43 | 44 | # Dot product 45 | prj = np.dot(H_world_to_image, S) 46 | # Get world coordinates 47 | xw = (prj[0] / prj[2]).item() # latitude 48 | yw = (prj[1] / prj[2]).item() # longitude 49 | return xw, yw 50 | 51 | def load_homography_matrix(self,s,c): 52 | 53 | file = os.path.join(self.calibration_dir ,s, c,'calibration.txt') 54 | idf = open(file, 'r') 55 | line = idf.readline() 56 | line2 = idf.readline() 57 | idf.close 58 | s = line.replace(';', ' ') 59 | s = s[-(s.__len__() - line.find(':') - 1):-1] 60 | floats = [float(x) for x in s.split()] 61 | H = np.array(floats).reshape(3, 3) 62 | 63 | self.homography_matrix[c] = H -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Online-MTMC-vehicle-tracking 2 | 3 | Paper: https://link.springer.com/article/10.1007/s11042-022-11923-2 4 | 5 | 6 | 7 | # Setup & Running 8 | **Requirements** 9 | 10 | The repository has been tested in the following software. 11 | * Ubuntu 16.04 12 | * Python 3.7 13 | * Anaconda 14 | * Pycharm 15 | 16 | **Anaconda environment** 17 | 18 | To create and setup the Anaconda Envirmorent run the following terminal command from the repository folder: 19 | ``` 20 | $ conda env create -f env_MTMC.yaml 21 | $ conda activate env_MTMC 22 | ``` 23 | 24 | **Clone repository** 25 | 26 | ``` 27 | $ git clone https://github.com/elun15/Online-MTMC-vehicle-tracking.git 28 | ``` 29 | 30 | **Download AIC19 dataset** 31 | 32 | The dataset can be downloaded at https://www.aicitychallenge.org/track1-download/ 33 | 34 | **Prepare AIC19 dataset** 35 | 36 | Move the downloaded folders *aic19-track1-mtmc/train* and *aic19-track1-mtmc/test* to the *./datasets/AIC19/* repository folder. 37 | 38 | Preprocess the data to extract the images from the .avi files by running: 39 | 40 | ``` 41 | python preprocess_data.py 42 | ``` 43 | 44 | 45 | The set of data can be changed, by default it will preprocess */test/S02* scenario. 46 | 47 | 48 | **Download pretrained model** 49 | 50 | The model weights trained on AIC19 S01 scenario can be downloaded at: 51 | http://www-vpu.eps.uam.es/publications/Online-MTMC-Tracking/ResNet50_AIC20_VERI_layer5_imaugclassifier_latest.pth.tar 52 | 53 | 54 | Place the weights file under *./models/* 55 | 56 | Training details can be found in the paper. 57 | 58 | 59 | **Running** 60 | 61 | To run the tracking algorithm over the S02 scenario run: 62 | 63 | ``` 64 | python main.py --ConfigPath ./config/config.yaml 65 | ``` 66 | 67 | 68 | 69 | # Citation 70 | 71 | If you find this code and work useful, please consider citing: 72 | ``` 73 | @article{luna2022online, 74 | title={Online clustering-based multi-camera vehicle tracking in scenarios with overlapping FOVs}, 75 | author={Luna, Elena and SanMiguel, Juan C and Mart{\'\i}nez, Jos{\'e} M and Escudero-Vi{\~n}olo, Marcos}, 76 | journal={Multimedia Tools and Applications}, 77 | pages={1--21}, 78 | year={2022}, 79 | publisher={Springer} 80 | } 81 | ``` 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /misc/utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import shutil 3 | 4 | class AverageMeter(object): 5 | """Computes and stores the average and current value""" 6 | def __init__(self, name, fmt=':f'): 7 | self.name = name 8 | self.fmt = fmt 9 | self.reset() 10 | 11 | def reset(self): 12 | self.val = 0 13 | self.avg = 0 14 | self.sum = 0 15 | self.count = 0 16 | self.std = 0 17 | 18 | def update(self, val, n=1): 19 | self.val = val 20 | self.sum += val * n 21 | self.count += n 22 | self.avg = self.sum / self.count 23 | 24 | def __str__(self): 25 | fmtstr = '{name} {val' + self.fmt + '} ({avg' + self.fmt + '})' 26 | return fmtstr.format(**self.__dict__) 27 | 28 | 29 | class ProgressMeter(object): 30 | def __init__(self, num_batches, meters, prefix=""): 31 | self.batch_fmtstr = self._get_batch_fmtstr(num_batches) 32 | self.meters = meters 33 | self.prefix = prefix 34 | 35 | def display(self, batch): 36 | entries = [self.prefix + self.batch_fmtstr.format(batch)] 37 | entries += [str(meter) for meter in self.meters] 38 | print('\t'.join(entries)) 39 | 40 | def _get_batch_fmtstr(self, num_batches): 41 | num_digits = len(str(num_batches // 1)) 42 | fmt = '{:' + str(num_digits) + 'd}' 43 | return '[' + fmt + '/' + fmt.format(num_batches) + ']' 44 | 45 | 46 | 47 | def accuracy(output, target, topk=(1,)): 48 | """Computes the accuracy over the k top predictions for the specified values of k""" 49 | with torch.no_grad(): 50 | maxk = max(topk) 51 | batch_size = target.size(0) 52 | 53 | _, pred = output.topk(maxk, 1, True, True) 54 | pred = pred.t() 55 | correct = pred.eq(target.view(1, -1).expand_as(pred)) 56 | 57 | res = [] 58 | for k in topk: 59 | correct_k = correct[:k].view(-1).float().sum(0, keepdim=True) 60 | res.append(correct_k.mul_(100.0 / batch_size)) 61 | return res 62 | 63 | def save_checkpoint(state, is_best, filename): 64 | 65 | torch.save(state, 'files/' + filename + '_latest.pth.tar') 66 | if is_best: 67 | print('Best model updated.') 68 | shutil.copyfile('files/' + filename + '_latest.pth.tar', 'files/' + filename + '_best.pth.tar') -------------------------------------------------------------------------------- /misc/nms.py: -------------------------------------------------------------------------------- 1 | # import the necessary packages 2 | import numpy as np 3 | 4 | 5 | def non_max_suppression(boxes, probs=None, overlapThresh=0.3): 6 | # if there are no boxes, return an empty list 7 | if len(boxes) == 0: 8 | return [] 9 | 10 | # if the bounding boxes are integers, convert them to floats -- this 11 | # is important since we'll be doing a bunch of divisions 12 | if boxes.dtype.kind == "i": 13 | boxes = boxes.astype("float") 14 | 15 | # initialize the list of picked indexes 16 | pick = [] 17 | 18 | # grab the coordinates of the bounding boxes 19 | x1 = boxes[:, 2] 20 | y1 = boxes[:, 3] 21 | x2 = x1+boxes[:, 4] 22 | y2 = y1+boxes[:, 5] 23 | 24 | # compute the area of the bounding boxes and grab the indexes to sort 25 | # (in the case that no probabilities are provided, simply sort on the 26 | # bottom-left y-coordinate) 27 | area = (x2 - x1 + 1) * (y2 - y1 + 1) 28 | idxs = y2 29 | 30 | # if probabilities are provided, sort on them instead 31 | if probs is not None: 32 | idxs = probs 33 | 34 | # sort the indexes 35 | idxs = np.argsort(idxs) 36 | 37 | # keep looping while some indexes still remain in the indexes list 38 | while len(idxs) > 0: 39 | # grab the last index in the indexes list and add the index value 40 | # to the list of picked indexes 41 | last = len(idxs) - 1 42 | i = idxs[last] 43 | pick.append(i) 44 | 45 | # find the largest (x, y) coordinates for the start of the bounding 46 | # box and the smallest (x, y) coordinates for the end of the bounding 47 | # box 48 | xx1 = np.maximum(x1[i], x1[idxs[:last]]) 49 | yy1 = np.maximum(y1[i], y1[idxs[:last]]) 50 | xx2 = np.minimum(x2[i], x2[idxs[:last]]) 51 | yy2 = np.minimum(y2[i], y2[idxs[:last]]) 52 | 53 | # compute the width and height of the bounding box 54 | w = np.maximum(0, xx2 - xx1 + 1) 55 | h = np.maximum(0, yy2 - yy1 + 1) 56 | 57 | # compute the ratio of overlap 58 | overlap = (w * h) / area[idxs[:last]] 59 | 60 | # delete all indexes from the index list that have overlap greater 61 | # than the provided overlap threshold 62 | idxs = np.delete(idxs, np.concatenate(([last], 63 | np.where(overlap > overlapThresh)[0]))) 64 | 65 | # return only the bounding boxes that were picked 66 | return boxes[pick] 67 | -------------------------------------------------------------------------------- /network/net_id_classifier.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | import torch 4 | import argparse 5 | import torch.nn as nn 6 | import torchvision 7 | from torchvision.models import resnet 8 | 9 | 10 | 11 | class net_id_classifier(nn.Module): 12 | 13 | def __init__(self, architecture, num_classes,size_fc): 14 | 15 | super(net_id_classifier, self).__init__() 16 | 17 | if architecture == 'ResNet50': 18 | 19 | base = resnet.resnet50(pretrained=True) 20 | 21 | # First initial block 22 | self.in_block = nn.Sequential( 23 | nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False), 24 | nn.BatchNorm2d(64), 25 | nn.ReLU(inplace=True), 26 | nn.MaxPool2d(kernel_size=3, stride=2, padding=1, return_indices=True)) 27 | 28 | self.encoder1 = base.layer1 29 | self.encoder2 = base.layer2 30 | self.encoder3 = base.layer3 31 | self.encoder4 = base.layer4 32 | self.avgpool7 = nn.AvgPool2d(7, stride=1) 33 | 34 | # self.fc1 = nn.Linear(2048, size_fc) 35 | # self.BN = nn.BatchNorm1d(size_fc) 36 | # self.ReLU = nn.ReLU(inplace=True) 37 | 38 | # self.fc = nn.Linear(2048, num_classes) 39 | self.fc = nn.Linear(size_fc, num_classes) 40 | 41 | 42 | 43 | if architecture == 'ResNet101': 44 | base = resnet.resnet101(pretrained=True) 45 | 46 | # First initial block 47 | self.in_block = nn.Sequential( 48 | nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False), 49 | nn.BatchNorm2d(64), 50 | nn.ReLU(inplace=True), 51 | nn.MaxPool2d(kernel_size=3, stride=2, padding=1, return_indices=True)) 52 | 53 | self.encoder1 = base.layer1 54 | self.encoder2 = base.layer2 55 | self.encoder3 = base.layer3 56 | self.encoder4 = base.layer4 57 | self.avgpool7 = nn.AvgPool2d(7, stride=1) 58 | 59 | 60 | self.fc = nn.Linear(2048, num_classes) 61 | 62 | 63 | 64 | def forward(self, x): 65 | """ 66 | Network forward 67 | :param x: RGB Image 68 | :param sem: Semantic Segmentation score tensor 69 | :retu 70 | """ 71 | 72 | x, pool_indices = self.in_block(x) 73 | e1 = self.encoder1(x) 74 | e2 = self.encoder2(e1) 75 | e3 = self.encoder3(e2) 76 | e4 = self.encoder4(e3) 77 | # 78 | conv_features_pool = self.avgpool7(e4) 79 | conv_features_pool = conv_features_pool.view(conv_features_pool.size(0), -1) 80 | 81 | 82 | # 83 | # x2 = self.avgpool7(e4) 84 | # x = x2.view(x2.size(0), -1) 85 | # 86 | # x = self.fc1(x) 87 | # 88 | # conv_features_pool = x.view(x.size(0), -1) 89 | # 90 | # # cls = self.fc(x) 91 | 92 | 93 | return conv_features_pool 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /network/joined_network.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | import torch 4 | import argparse 5 | import torch.nn as nn 6 | import torchvision 7 | from torchvision.models import resnet 8 | 9 | class net_features_cars(nn.Module): 10 | 11 | def __init__(self, architecture): 12 | """ 13 | Initialization of the network 14 | :param arch: Desired backbone for RGB branch. Either ResNet-18 or ResNet-50 15 | 16 | """ 17 | super(net_features_cars, self).__init__() 18 | if architecture == 'ResNet50': 19 | 20 | base = resnet.resnet50(pretrained=True) 21 | 22 | # First initial block 23 | self.in_block = nn.Sequential( 24 | nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False), 25 | nn.BatchNorm2d(64), 26 | nn.ReLU(inplace=True), 27 | nn.MaxPool2d(kernel_size=3, stride=2, padding=1, return_indices=True)) 28 | 29 | self.encoder1 = base.layer1 30 | self.encoder2 = base.layer2 31 | self.encoder3 = base.layer3 32 | self.encoder4 = base.layer4 33 | self.avgpool7 = nn.AvgPool2d(7, stride=1) 34 | # self.fc = nn.Linear(2048, 2) 35 | 36 | # terminar falta la fc 37 | 38 | def forward_once(self, x): 39 | """ 40 | Network forward 41 | :param x: RGB Image 42 | :param sem: Semantic Segmentation score tensor 43 | :retu 44 | """ 45 | 46 | x, pool_indices = self.in_block(x) 47 | e1 = self.encoder1(x) 48 | e2 = self.encoder2(e1) 49 | e3 = self.encoder3(e2) 50 | e4 = self.encoder4(e3) 51 | 52 | conv_features_pool = self.avgpool7(e4) 53 | 54 | conv_features_pool = conv_features_pool.view(conv_features_pool.size(0), -1) 55 | # cls = self.fc(conv_features_pool) 56 | 57 | 58 | return conv_features_pool 59 | 60 | def forward(self, input1, input2): 61 | output1 = self.forward_once(input1) 62 | output2 = self.forward_once(input2) 63 | 64 | return output1, output2 65 | 66 | 67 | class net_classifier(nn.Module): 68 | 69 | def __init__(self): 70 | 71 | super(net_classifier, self).__init__() 72 | self.fc1 = nn.Linear(4096, 1024) 73 | self.relu = nn.ReLU(inplace=True) 74 | self.bn1 = nn.BatchNorm1d(1024) 75 | self.fc2 = nn.Linear(1024, 1) 76 | self.drop = nn.Dropout(0.2) 77 | 78 | def forward(self, input1,flag_dropout): 79 | output1 = self.fc1(input1) 80 | output1 = self.bn1(output1) 81 | output1 = self.relu(output1) 82 | 83 | if flag_dropout: 84 | output1 = self.drop(output1) 85 | 86 | output1 = self.fc2(output1) 87 | return output1 88 | 89 | class joined_network(nn.Module): 90 | 91 | def __init__(self, architecture, flag_dropout): 92 | 93 | super(joined_network, self).__init__() 94 | self.features = net_features_cars(architecture) 95 | self.classifier = net_classifier() 96 | self.flag_dropout = flag_dropout 97 | 98 | def forward(self, input1, input2): 99 | output1, output2 = self.features(input1, input2) 100 | 101 | features_cat = torch.cat([output1, output2], 1) 102 | 103 | output_cls = self.classifier(features_cat, self.flag_dropout) 104 | 105 | return output_cls 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /preprocessing_data/preprocess_veri_dataset.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ################################ 3 | # Read Veri dataset and 4 | # Need to be executed with 5 | # the root folder only with 6 | # AICC data until id 325 7 | ################################ 8 | ''' 9 | 10 | 11 | 12 | import os 13 | import cv2 14 | import time 15 | import numpy as np 16 | from sklearn.model_selection import train_test_split 17 | # importing shutil module 18 | import shutil 19 | 20 | 21 | 22 | 23 | if __name__ == '__main__': 24 | 25 | 26 | 27 | # Original dataset directory 28 | veri_train_root_dir = '/home/vpu/Datasets/VeRi_with_plate/image_train/' 29 | files_veri_train = os.listdir(veri_train_root_dir) 30 | num_files_train= files_veri_train.__len__() 31 | 32 | veri_test_root_dir = '/home/vpu/Datasets/VeRi_with_plate/image_test/' 33 | files_veri_test = os.listdir(veri_test_root_dir) 34 | num_files_test = files_veri_test.__len__() 35 | 36 | destination_dir = '/home/vpu/Datasets/AICC_veri/subset_train_aicc_veri' 37 | files_dest = os.listdir(destination_dir) 38 | max_num_id = np.max(((np.sort((np.asarray(files_dest))))).astype(int)) 39 | next_id = max_num_id + 1 40 | 41 | 42 | # Theese 2 loops calculate the new correspondent ids od the veri vechicles 43 | # veri_id = [1 2 3 4 6 ] e.g. 44 | # new_id = [326 327 328 329 330 ] 45 | 46 | # veri train 47 | 48 | ids_veri_train = [] 49 | for i in range(num_files_train): 50 | name = files_veri_train[i] 51 | ids_veri_train.append(int(name[0:4])) 52 | 53 | ids_veri_train_unique = np.unique(np.asarray(ids_veri_train)) 54 | num_ids_train_veri = ids_veri_train_unique.__len__() 55 | 56 | correspondent_id_train = np.asarray(range(num_ids_train_veri)) + next_id 57 | next_id = np.max(correspondent_id_train) + 1 58 | 59 | # veri test 60 | ids_veri_test = [] 61 | for i in range(num_files_test): 62 | name = files_veri_test[i] 63 | ids_veri_test.append(int(name[0:4])) 64 | 65 | ids_veri_test_unique = np.unique(np.asarray(ids_veri_test)) 66 | num_ids_test_veri = ids_veri_test_unique.__len__() 67 | 68 | correspondent_id_test = np.asarray(range(num_ids_test_veri)) + next_id 69 | 70 | 71 | for i in range(num_files_train): 72 | name = files_veri_train[i] 73 | veri_id = int(name[0:4]) 74 | idx = np.where(ids_veri_train_unique == veri_id)[0] 75 | new_id = correspondent_id_train[idx] 76 | 77 | #check if exist the correspondent folder 78 | 79 | id_path = os.path.join(destination_dir, '%04d' % new_id) 80 | if not os.path.exists(id_path): 81 | os.makedirs(id_path) 82 | 83 | im_src = os.path.join(veri_train_root_dir, name) 84 | im_dst = os.path.join(id_path, name) 85 | shutil.copyfile(im_src, im_dst) 86 | 87 | 88 | for i in range(num_files_test): 89 | name = files_veri_test[i] 90 | veri_id = int(name[0:4]) 91 | idx = np.where(ids_veri_test_unique == veri_id)[0] 92 | new_id = correspondent_id_test[idx] 93 | 94 | # check if exist the correspondent folder 95 | 96 | id_path = os.path.join(destination_dir, '%04d' % new_id) 97 | if not os.path.exists(id_path): 98 | os.makedirs(id_path) 99 | 100 | im_src = os.path.join(veri_test_root_dir, name) 101 | im_dst = os.path.join(id_path, name) 102 | shutil.copyfile(im_src, im_dst) 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /thirdparty/bbox.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import torch 4 | import random 5 | 6 | import numpy as np 7 | import cv2 8 | 9 | 10 | def confidence_filter(result, confidence): 11 | conf_mask = (result[:, :, 4] > confidence).float().unsqueeze(2) 12 | result = result * conf_mask 13 | 14 | return result 15 | 16 | 17 | def confidence_filter_cls(result, confidence): 18 | max_scores = torch.max(result[:, :, 5:25], 2)[0] 19 | res = torch.cat((result, max_scores), 2) 20 | print(res.shape) 21 | 22 | cond_1 = (res[:, :, 4] > confidence).float() 23 | cond_2 = (res[:, :, 25] > 0.995).float() 24 | 25 | conf = cond_1 + cond_2 26 | conf = torch.clamp(conf, 0.0, 1.0) 27 | conf = conf.unsqueeze(2) 28 | result = result * conf 29 | return result 30 | 31 | 32 | def get_abs_coord(box): 33 | box[2], box[3] = abs(box[2]), abs(box[3]) 34 | x1 = (box[0] - box[2] / 2) - 1 35 | y1 = (box[1] - box[3] / 2) - 1 36 | x2 = (box[0] + box[2] / 2) - 1 37 | y2 = (box[1] + box[3] / 2) - 1 38 | return x1, y1, x2, y2 39 | 40 | 41 | def sanity_fix(box): 42 | if (box[0] > box[2]): 43 | box[0], box[2] = box[2], box[0] 44 | 45 | if (box[1] > box[3]): 46 | box[1], box[3] = box[3], box[1] 47 | 48 | return box 49 | 50 | 51 | def bbox_iou(box1, box2): 52 | """ 53 | Returns the IoU of two bounding boxes 54 | 55 | 56 | """ 57 | # Get the coordinates of bounding boxes 58 | b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3] 59 | b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3] 60 | 61 | # get the corrdinates of the intersection rectangle 62 | inter_rect_x1 = torch.max(b1_x1, b2_x1) 63 | inter_rect_y1 = torch.max(b1_y1, b2_y1) 64 | inter_rect_x2 = torch.min(b1_x2, b2_x2) 65 | inter_rect_y2 = torch.min(b1_y2, b2_y2) 66 | 67 | # Intersection area 68 | if torch.cuda.is_available(): 69 | inter_area = torch.max(inter_rect_x2.float() - inter_rect_x1.float() + 1, torch.zeros(inter_rect_x2.shape).cuda()) * torch.max( 70 | inter_rect_y2.float() - inter_rect_y1.float() + 1, torch.zeros(inter_rect_x2.shape).cuda()) 71 | else: 72 | inter_area = torch.max(inter_rect_x2.float() - inter_rect_x1.float() + 1, torch.zeros(inter_rect_x2.shape)) * torch.max( 73 | inter_rect_y2.float() - inter_rect_y1.float() + 1, torch.zeros(inter_rect_x2.shape)) 74 | 75 | # Union Area 76 | b1_area = (b1_x2 - b1_x1 + 1) * (b1_y2 - b1_y1 + 1) 77 | b2_area = (b2_x2 - b2_x1 + 1) * (b2_y2 - b2_y1 + 1) 78 | 79 | iou = inter_area / (b1_area + b2_area - inter_area) 80 | 81 | return iou 82 | 83 | 84 | def pred_corner_coord(prediction): 85 | # Get indices of non-zero confidence bboxes 86 | ind_nz = torch.nonzero(prediction[:, :, 4]).transpose(0, 1).contiguous() 87 | 88 | box = prediction[ind_nz[0], ind_nz[1]] 89 | 90 | box_a = box.new(box.shape) 91 | box_a[:, 0] = (box[:, 0] - box[:, 2] / 2) 92 | box_a[:, 1] = (box[:, 1] - box[:, 3] / 2) 93 | box_a[:, 2] = (box[:, 0] + box[:, 2] / 2) 94 | box_a[:, 3] = (box[:, 1] + box[:, 3] / 2) 95 | box[:, :4] = box_a[:, :4] 96 | 97 | prediction[ind_nz[0], ind_nz[1]] = box 98 | 99 | return prediction 100 | 101 | 102 | def write(x, batches, results, colors, classes): 103 | c1 = tuple(x[1:3].int()) 104 | c2 = tuple(x[3:5].int()) 105 | img = results[int(x[0])] 106 | cls = int(x[-1]) 107 | label = "{0}".format(classes[cls]) 108 | color = random.choice(colors) 109 | cv2.rectangle(img, c1, c2, color, 1) 110 | t_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_PLAIN, 1, 1)[0] 111 | c2 = c1[0] + t_size[0] + 3, c1[1] + t_size[1] + 4 112 | cv2.rectangle(img, c1, c2, color, -1) 113 | cv2.putText(img, label, (c1[0], c1[1] + t_size[1] + 4), cv2.FONT_HERSHEY_PLAIN, 1, [225, 255, 255], 1); 114 | return img -------------------------------------------------------------------------------- /postprocessing/unsync.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ################################ 3 | # Pre-process AICC19 data # 4 | ################################ 5 | ''' 6 | 7 | import os 8 | import cv2 9 | import time 10 | import numpy as np 11 | 12 | 13 | 14 | offset= {'S01': {'c001': 0, 15 | 'c002': 1.640, 16 | 'c003': 2.049, 17 | 'c004': 2.177, 18 | 'c005': 2.235}, 19 | 'S02': {'c006': 0, 20 | 'c007': 0.061, 21 | 'c008': 0.421, 22 | 'c009': 0.660}, 23 | 'S03': { 'c010': 8.715, 24 | 'c011': 8.457, 25 | 'c012': 5.879, 26 | 'c013': 0, 27 | 'c014': 5.042, 28 | 'c015':8.492}, 29 | 'S04':{ 'c016': 0, 30 | 'c017': 14.318, 31 | 'c018': 29.955, 32 | 'c019': 26.979, 33 | 'c020': 25.905, 34 | 'c021': 39.973, 35 | 'c022': 49.422, 36 | 'c023': 45.716, 37 | 'c024': 50.853, 38 | 'c025': 50.263, 39 | 'c026': 70.450, 40 | 'c027': 85.097, 41 | 'c028': 100.110, 42 | 'c029': 125.788, 43 | 'c030': 124.319, 44 | 'c031': 125.033, 45 | 'c032': 125.199, 46 | 'c033': 150.893, 47 | 'c034': 140.218, 48 | 'c035': 165.568, 49 | 'c036': 170.797, 50 | 'c037': 170.567, 51 | 'c038': 175.426, 52 | 'c039': 175.644, 53 | 'c040': 175.838}, 54 | 'S05':{ 'c010': 0, 55 | 'c016': 0, 56 | 'c017': 0, 57 | 'c018': 0, 58 | 'c019': 0, 59 | 'c020': 0, 60 | 'c021': 0, 61 | 'c022': 0, 62 | 'c023': 0, 63 | 'c024': 0, 64 | 'c025': 0, 65 | 'c026': 0, 66 | 'c027': 0, 67 | 'c028': 0, 68 | 'c029': 0, 69 | 'c033': 0, 70 | 'c034': 0, 71 | 'c035': 0, 72 | 'c036': 0}} 73 | 74 | max_frame={'S01': 2110, 75 | 'S02': 2110, 76 | 'S03': 2422, 77 | 'S04': 710, 78 | 'S05': 4299, 79 | } 80 | 81 | 82 | def process(file,offset, scenario, dataset_dir, output_file,i): 83 | 84 | res = np.loadtxt(file) 85 | 86 | cameras = os.listdir(os.path.join(dataset_dir, scenario)) 87 | 88 | for c in cameras: 89 | 90 | tStart = time.time() 91 | print('Processing ' + scenario + ' ' + c + ' with offset = ' + str(offset[scenario][c])) 92 | 93 | vdo_dir = os.path.join(dataset_dir, scenario, c, 'vdo.avi') 94 | video = cv2.VideoCapture(vdo_dir) 95 | 96 | # num_frames = video.get(cv2.CAP_PROP_FRAME_COUNT) 97 | fps = video.get(cv2.CAP_PROP_FPS) 98 | 99 | offset_frames = int(round((offset[scenario][c]) * fps)) 100 | 101 | c_rows = res[:,0] == int(c[1:]) 102 | res[c_rows, 2] = res[c_rows, 2] - offset_frames 103 | 104 | tEnd = time.time() 105 | print("It cost %f sec" % (tEnd - tStart)) 106 | 107 | np.savetxt(output_file, res, delimiter=',') 108 | 109 | #os.makedirs('/home/vpu/AICityChallengeTrack/amilan-motchallenge-devkit/res/AIC20/ablation_study/v'+ str(i),exist_ok=True) 110 | #np.savetxt('/home/vpu/AICityChallengeTrack/amilan-motchallenge-devkit/res/AIC20/ablation_study/v'+ str(i) + '/S02.txt', res, delimiter=',') 111 | 112 | if __name__ == '__main__': 113 | 114 | scenario = 'S02' 115 | 116 | results_file = '.path to the results file'.txt' 117 | unsync_file = '.path for the saved unsync results .txt' 118 | 119 | dataset_dir = './../datasets/AIC19/validation' 120 | 121 | process(results_file,offset, scenario, dataset_dir, unsync_file,i) 122 | 123 | 124 | -------------------------------------------------------------------------------- /libs/clustering.py: -------------------------------------------------------------------------------- 1 | '''l 2 | ################################ 3 | # Spatial 4 | Clustering # 5 | ################################ 6 | ''' 7 | 8 | import numpy as np 9 | from scipy.cluster import hierarchy 10 | from thirdparty import sklearn_dunn 11 | import matplotlib.pyplot as plt 12 | from sklearn.cluster import AgglomerativeClustering 13 | 14 | class clustering(): 15 | 16 | def __init__(self, mtmc): 17 | self.clusters_frame = list() 18 | self.colors = mtmc.colors.list 19 | self.trajectories_f_w = list() 20 | 21 | 22 | def new_cluster(self): 23 | cluster = { 24 | 'id': None, 25 | 'xw': None, 26 | 'yw': None, 27 | 'det': list()} 28 | return cluster 29 | 30 | def new_detection(self): 31 | det = { 32 | 'id_cam': None, 33 | 'x': None, 34 | 'y': None, 35 | 'w': None, 36 | 'h': None, 37 | 'id': None, 38 | 'id_global': None} 39 | return det 40 | 41 | def new_trajectory(self): 42 | det = { 43 | 'id_cam': None, 44 | 'id': None, 45 | 'xw': None, 46 | 'yy': None, 47 | 'feature_descriptor': None} 48 | return det 49 | 50 | 51 | def compute_clusters(self, distance_matrix, association_matrix): 52 | # flat_tril = np.ravel((np.tril(distance_matrix)), order='F') 53 | # vec_dist_features = flat_tril[flat_tril != 0] 54 | num_det_f= distance_matrix.shape[0] 55 | # iu1 = np.triu_indices(num_det_f) 56 | # 57 | # distance_matrix2 = np.copy(distance_matrix) 58 | # distance_matrix2[iu1] = np.inf 59 | # flat_tril = np.ravel(distance_matrix2, order='F') 60 | # vec_dist_features = flat_tril[flat_tril != np.inf] 61 | # 62 | # Z = hierarchy.linkage(vec_dist_features, method='complete', metric='euclidean') 63 | # plt.figure() 64 | # dn = hierarchy.dendrogram(Z) 65 | # plt.show() 66 | 67 | max_num_clusters = num_det_f - 1 68 | min_num_clusters = max((num_det_f - (np.where(association_matrix == 1))[0].__len__() + 1), 2) 69 | 70 | 71 | if min_num_clusters > max_num_clusters: #It can occur when there are some pairs of close detections being from the same cameras by pairs 72 | 73 | optimal_clusters = num_det_f 74 | idx_clusters = np.array(range(0, optimal_clusters)) 75 | 76 | else: 77 | 78 | clusters = np.array(range(min_num_clusters, max_num_clusters + 1)) 79 | num_clusters = (max_num_clusters - min_num_clusters + 1) 80 | 81 | labels = list() 82 | indices = np.zeros(num_clusters) 83 | 84 | for k in range(num_clusters): 85 | 86 | # Metodo 1 87 | # labels.append(hierarchy.fcluster(Z, clusters[k], criterion='maxclust')) 88 | 89 | # Metodo 2 90 | cluster = AgglomerativeClustering(n_clusters=clusters[k], affinity='precomputed', linkage='complete') 91 | labels.append(cluster.fit_predict(distance_matrix)) 92 | 93 | 94 | indices[k] = sklearn_dunn.dunn(labels[-1], distance_matrix) 95 | 96 | # max_index = indices.tolist().index(min(indices)) 97 | 98 | if indices.__len__() == 1: # If min_num_clusters == max_num_clusters 99 | optimal_clusters = clusters[0] 100 | idx_clusters = labels[0] # -1 so clusters labels start in 0 101 | else: 102 | derivative = np.diff(indices) 103 | pos = derivative.tolist().index(max(derivative)) + 1 104 | optimal_clusters = clusters[pos] 105 | idx_clusters = labels[pos] # -1 so clusters labels start in 0 106 | 107 | return idx_clusters, optimal_clusters 108 | 109 | def display_detections_cluster(self,sct_f, det_in_cluster,cl): 110 | 111 | for d in range(det_in_cluster.__len__()): 112 | det = det_in_cluster[d] 113 | xw = sct_f['xw'][det] 114 | yw = sct_f['yw'][det] 115 | plt.plot(xw, yw, 'x', lineWidth = 1, markerSize = 10, color = self.colors[cl]) 116 | 117 | def display_centroid_cluster(self, mean_xw, mean_yw, cl): 118 | 119 | plt.scatter(mean_xw, mean_yw, s = 80, facecolors='none', edgecolors=self.colors[cl]) 120 | plt.text(mean_xw+0.000005, mean_yw+0.000005, str(cl), fontsize=15, color='black') 121 | plt.title('Tracks') 122 | -------------------------------------------------------------------------------- /libs/sct.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ################################ 3 | # Load Single Camera Tracking # 4 | ################################ 5 | ''' 6 | 7 | import os 8 | import sys 9 | import cv2 10 | import math 11 | import time 12 | import numpy as np 13 | from PIL import Image 14 | from scipy import interpolate 15 | from scipy.spatial.distance import cdist 16 | from misc import nms 17 | 18 | 19 | class sct(): 20 | 21 | def __init__(self, mtmc): 22 | self.data = {} 23 | self.detector = mtmc.detector 24 | self.dataset_root_dir = mtmc.dataset_root_dir 25 | self.fps = 10 # For AICC19 26 | 27 | def new_frame_data(self): 28 | struct={} 29 | struct['id_cam'] = [] 30 | struct['id'] = [] 31 | struct['x'] = [] 32 | struct['y'] = [] 33 | struct['w'] = [] 34 | struct['h'] = [] 35 | struct['xw'] = [] 36 | struct['yw'] = [] 37 | struct['features'] = [] 38 | struct['bbox'] = [] 39 | return struct 40 | 41 | def new_frame_w_data(self): 42 | struct={} 43 | struct['f'] = [] 44 | struct['id_cam'] = [] 45 | struct['id'] = [] 46 | struct['x'] = [] 47 | struct['y'] = [] 48 | struct['w'] = [] 49 | struct['h'] = [] 50 | struct['xw'] = [] 51 | struct['yw'] = [] 52 | struct['features'] = [] 53 | return struct 54 | 55 | 56 | def new(self,scene): 57 | self.data[scene] = {} 58 | 59 | def load(self,set,scene,offset, flag_filter_size,score_th): 60 | 61 | cameras = os.listdir(os.path.join(self.dataset_root_dir, set, scene)) 62 | for c in cameras: 63 | # Directory 64 | dir_file_sct = os.path.join(self.dataset_root_dir, set, scene, c, self.detector + '.txt') 65 | data = np.loadtxt(dir_file_sct, delimiter=',') 66 | 67 | #Filter detections by score 68 | data = data[data[:, 6] > score_th , :] 69 | 70 | 71 | if flag_filter_size == True: 72 | data = data[(data[:, 4] * data[:, 5]) > 2000, :] 73 | 74 | 75 | 76 | # Load ROI and filter sct before saving into class 77 | roi = cv2.imread(os.path.join(self.dataset_root_dir, set, scene, c,'roi.jpg')) 78 | assert roi is not None , " Cannot read ROI image " 79 | 80 | if data.size == 0: 81 | self.data[scene][c] = data 82 | else: 83 | data_sync = self.synchronize(data, scene, c, offset) 84 | self.data[scene][c] = self.filter_roi(data_sync,roi) 85 | 86 | 87 | def synchronize(self,data, scene, c, offset): 88 | # frame, id, x, y, w, h , 1, -1, -1, -1 89 | offset_frames = int(round((offset[scene][c]) * self.fps)) 90 | data[:,0] = data[:,0] + offset_frames 91 | 92 | return data 93 | 94 | 95 | 96 | def filter_roi(self,data,roi): 97 | 98 | img_h, img_w = roi.shape[0], roi.shape[1] 99 | 100 | filtered_data =[] 101 | 102 | for line in data: 103 | 104 | #Get base point of the bbox 105 | # frame, id, x, y, w, h , 1, -1, -1, -1 106 | x = int(line[2]) 107 | y = int(line[3]) 108 | width = int(line[4]) 109 | height = int(line[5]) 110 | 111 | # Check if some value is wrong & Check bboxes lying outside 112 | if width <= 0 or height <= 0: 113 | continue 114 | 115 | if x < 0: 116 | 117 | line[2] = 0 118 | width = width - abs(x) 119 | line[4] = width 120 | x = 0 121 | 122 | if y < 0: 123 | line[3] = 0 124 | height = height - abs(y) 125 | line[5] = height 126 | y = 0 127 | 128 | 129 | if (x+width) > img_w: 130 | w_exceed = x + width - img_w 131 | line[4] = width - w_exceed 132 | width = width - w_exceed 133 | 134 | if (y+height) > img_h: 135 | h_exceed = y + height - img_h 136 | line[5] = height - h_exceed 137 | height = height - h_exceed 138 | 139 | # if x < 0.3 * img_w or x > 0.7 * img_w or y < 0.2 * img_h or y > 0.8 * img_h: 140 | # continue 141 | 142 | # If base of the bbox is not in the ROI, not saving 143 | bx = x + round(width / 2) 144 | by = y + height 145 | 146 | if roi[by-1, bx-1, 0] == 0: 147 | continue 148 | 149 | filtered_data.append(line) 150 | 151 | return filtered_data 152 | 153 | 154 | if __name__ == '__main__': 155 | 156 | sct() 157 | 158 | set = ['train', 'test'] 159 | # for s in set: 160 | 161 | 162 | -------------------------------------------------------------------------------- /preprocessing_data/preprocess_data.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ################################ 3 | # Pre-process AICC19 data # 4 | ################################ 5 | ''' 6 | 7 | import os 8 | import cv2 9 | import time 10 | import numpy as np 11 | 12 | 13 | #offset = dict() 14 | offset= {'S01': {'c001': 0, 15 | 'c002': 1.640, 16 | 'c003': 2.049, 17 | 'c004': 2.177, 18 | 'c005': 2.235}, 19 | 'S02': {'c006': 0, 20 | 'c007': 0.061, 21 | 'c008': 0.421, 22 | 'c009': 0.660}, 23 | 'S03': { 'c010': 8.715, 24 | 'c011': 8.457, 25 | 'c012': 5.879, 26 | 'c013': 0, 27 | 'c014': 5.042, 28 | 'c015':8.492}, 29 | 'S04':{ 'c016': 0, 30 | 'c017': 14.318, 31 | 'c018': 29.955, 32 | 'c019': 26.979, 33 | 'c020': 25.905, 34 | 'c021': 39.973, 35 | 'c022': 49.422, 36 | 'c023': 45.716, 37 | 'c024': 50.853, 38 | 'c025': 50.263, 39 | 'c026': 70.450, 40 | 'c027': 85.097, 41 | 'c028': 100.110, 42 | 'c029': 125.788, 43 | 'c030': 124.319, 44 | 'c031': 125.033, 45 | 'c032': 125.199, 46 | 'c033': 150.893, 47 | 'c034': 140.218, 48 | 'c035': 165.568, 49 | 'c036': 170.797, 50 | 'c037': 170.567, 51 | 'c038': 175.426, 52 | 'c039': 175.644, 53 | 'c040': 175.838}, 54 | 'S05':{ 'c010': 0, 55 | 'c016': 0, 56 | 'c017': 0, 57 | 'c018': 0, 58 | 'c019': 0, 59 | 'c020': 0, 60 | 'c021': 0, 61 | 'c022': 0, 62 | 'c023': 0, 63 | 'c024': 0, 64 | 'c025': 0, 65 | 'c026': 0, 66 | 'c027': 0, 67 | 'c028': 0, 68 | 'c029': 0, 69 | 'c033': 0, 70 | 'c034': 0, 71 | 'c035': 0, 72 | 'c036': 0}, 73 | 'S06':{ 'c041': 0, 74 | 'c042': 0, 75 | 'c043': 0, 76 | 'c044': 0, 77 | 'c045': 0, 78 | 'c046': 0 }} 79 | 80 | max_frame={'S01': 2110, 81 | 'S02': 2110, 82 | 'S03': 2422, 83 | 'S04': 710, 84 | 'S05': 4299, 85 | 'S06': 100 86 | } 87 | 88 | 89 | def process(mode,offset): 90 | # Current root directory 91 | root_dir = os.path.dirname(os.path.abspath(__file__)) 92 | 93 | # Original dataset directory 94 | dataset_dir = os.path.join('./../datasets/AIC19', mode) 95 | 96 | scenarios = ['S02'] 97 | for s in scenarios: 98 | 99 | cameras = os.listdir(os.path.join(dataset_dir, s)) 100 | 101 | for c in cameras: 102 | 103 | tStart = time.time() 104 | print('Processing ' + s + ' ' + c + ' with offset = ' + str(offset[s][c])) 105 | 106 | vdo_dir = os.path.join(dataset_dir, s, c, 'vdo.avi') 107 | video = cv2.VideoCapture(vdo_dir) 108 | 109 | num_frames = video.get(cv2.CAP_PROP_FRAME_COUNT) 110 | fps = video.get(cv2.CAP_PROP_FPS) 111 | h = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)) 112 | w = int(video.get(cv2.CAP_PROP_FRAME_WIDTH)) 113 | 114 | blank_image = np.zeros((h, w, 3), np.uint8) 115 | 116 | output_dir = os.path.join(dataset_dir, s, c, 'img') 117 | if not os.path.exists(output_dir): 118 | os.makedirs(output_dir) 119 | 120 | offset_frames = int(round((offset[s][c]) * fps)) 121 | frame_counter = 1 122 | 123 | # If offset > 0 : fill with blank images at the begining of the sequence 124 | if offset_frames > 0: 125 | for f in range(1, offset_frames + 1): 126 | frame_name = os.path.join(output_dir, str(frame_counter).zfill(6) + ".jpg") 127 | cv2.imwrite(frame_name, blank_image) 128 | 129 | frame_counter += 1 130 | 131 | # Read video file and save image frames 132 | while video.isOpened(): 133 | 134 | ret, frame = video.read() 135 | frame_name = os.path.join(output_dir, str(frame_counter).zfill(6) + ".jpg") 136 | 137 | # print(video.get(cv2.CAP_PROP_POS_FRAMES)) 138 | 139 | if not ret: 140 | print("End of video file.") 141 | a = 1 142 | break 143 | cv2.imwrite(frame_name, frame) 144 | frame_counter += 1 145 | 146 | if frame_counter < max_frame[s]: 147 | # Fill at the end with black frames to reach max number of frames 148 | for f in range(frame_counter, max_frame[s] + 1): 149 | frame_name = os.path.join(output_dir, str(frame_counter).zfill(6) + ".jpg") 150 | cv2.imwrite(frame_name, blank_image) 151 | frame_counter += 1 152 | 153 | tEnd = time.time() 154 | print("It cost %f sec" % (tEnd - tStart)) 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | if __name__ == '__main__': 163 | 164 | mode = 'test/' 165 | process(mode,offset) 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /env_MTMC.yaml: -------------------------------------------------------------------------------- 1 | name: env_MTMC 2 | channels: 3 | - pytorch 4 | - cannylab 5 | - anaconda 6 | - conda-forge 7 | - defaults 8 | dependencies: 9 | - _libgcc_mutex=0.1=main 10 | - absl-py=0.7.1=py37_0 11 | - astor=0.7.1=py_0 12 | - binutils_impl_linux-64=2.31.1=h6176602_1 13 | - binutils_linux-64=2.31.1=h6176602_7 14 | - blas=1.0=mkl 15 | - bzip2=1.0.6=h14c3975_1002 16 | - c-ares=1.15.0=h516909a_1001 17 | - ca-certificates=2020.10.14=0 18 | - cairo=1.16.0=h18b612c_1001 19 | - certifi=2020.6.20=py37_0 20 | - cffi=1.12.3=py37h8022711_0 21 | - cloudpickle=1.3.0=py_0 22 | - cuda100=1.0=hb0b9368_0 23 | - cudatoolkit=10.0.130=0 24 | - cudnn=7.6.0=cuda10.0_0 25 | - cycler=0.10.0=py_1 26 | - cytoolz=0.10.1=py37h516909a_0 27 | - dask-core=2.13.0=py_0 28 | - dbus=1.13.6=he372182_0 29 | - decorator=4.4.2=py_0 30 | - expat=2.2.5=he1b5a44_1003 31 | - ffmpeg=4.1.3=h167e202_0 32 | - filterpy=1.4.5=py_0 33 | - fontconfig=2.13.1=he4413a7_1000 34 | - freetype=2.10.0=he983fc9_0 35 | - gast=0.2.2=py_0 36 | - gcc_impl_linux-64=7.3.0=habb00fd_1 37 | - gcc_linux-64=7.3.0=h553295d_7 38 | - geos=3.8.1=he1b5a44_0 39 | - gettext=0.19.8.1=hc5be6a0_1002 40 | - giflib=5.1.9=h516909a_0 41 | - glib=2.58.3=h6f030ca_1001 42 | - gmp=6.1.2=hf484d3e_1000 43 | - gnutls=3.6.5=hd3a4fd2_1002 44 | - graphite2=1.3.13=hf484d3e_1000 45 | - grpcio=1.16.1=py37hf8bcb03_1 46 | - gst-plugins-base=1.14.5=h0935bb2_0 47 | - gstreamer=1.14.5=h36ae1b5_0 48 | - gxx_impl_linux-64=7.3.0=hdf63c60_1 49 | - gxx_linux-64=7.3.0=h553295d_7 50 | - h5py=2.9.0=nompi_hf008753_1101 51 | - harfbuzz=2.4.0=h37c48d4_1 52 | - hdf5=1.10.4=nompi_h3c11f04_1106 53 | - icu=58.2=hf484d3e_1000 54 | - imageio=2.8.0=py_0 55 | - imgaug=0.4.0=py_1 56 | - intel-openmp=2019.4=243 57 | - jasper=1.900.1=h07fcdf6_1006 58 | - joblib=0.13.2=py_0 59 | - jpeg=9c=h14c3975_1001 60 | - keras=2.2.4=py37_1 61 | - keras-applications=1.0.7=py_1 62 | - keras-preprocessing=1.0.9=py_1 63 | - kiwisolver=1.1.0=py37hc9558a2_0 64 | - lame=3.100=h14c3975_1001 65 | - libblas=3.8.0=10_mkl 66 | - libcblas=3.8.0=10_mkl 67 | - libffi=3.2.1=he1b5a44_1006 68 | - libgcc-ng=9.1.0=hdf63c60_0 69 | - libgfortran-ng=7.3.0=hdf63c60_0 70 | - libgpuarray=0.7.6=h14c3975_1003 71 | - libiconv=1.15=h516909a_1005 72 | - liblapack=3.8.0=10_mkl 73 | - liblapacke=3.8.0=10_mkl 74 | - libllvm8=8.0.1=hc9558a2_0 75 | - libopenblas=0.3.6=h6e990d7_4 76 | - libpng=1.6.37=hed695b0_0 77 | - libprotobuf=3.9.0=h8b12597_0 78 | - libstdcxx-ng=9.1.0=hdf63c60_0 79 | - libtiff=4.0.10=h57b8799_1003 80 | - libuuid=2.32.1=h14c3975_1000 81 | - libwebp=1.0.2=h576950b_1 82 | - libxcb=1.13=h14c3975_1002 83 | - libxml2=2.9.9=h13577e0_1 84 | - llvmlite=0.31.0=py37h8b12597_0 85 | - lz4-c=1.8.3=he1b5a44_1001 86 | - mako=1.0.10=py_0 87 | - markdown=3.1.1=py_0 88 | - markupsafe=1.1.1=py37h14c3975_0 89 | - matplotlib=3.1.1=py37_0 90 | - matplotlib-base=3.1.1=py37hfd891ef_0 91 | - mkl=2019.4=243 92 | - mock=3.0.5=py37_0 93 | - more-itertools=8.5.0=py_0 94 | - munkres=1.0.12=py_0 95 | - ncurses=6.1=hf484d3e_1002 96 | - nettle=3.4.1=h1bed415_1002 97 | - networkx=2.4=py_1 98 | - ninja=1.9.0=h6bb024c_0 99 | - numba=0.47.0=py37hb3f55d8_0 100 | - numpy=1.16.4=py37h95a1406_0 101 | - olefile=0.46=py_0 102 | - onnx=1.6.0=py37he1b5a44_0 103 | - openblas=0.3.6=h6e990d7_4 104 | - opencv=4.1.0=py37h5517eff_4 105 | - openh264=1.8.0=hdbcaa40_1000 106 | - openssl=1.1.1h=h7b6447c_0 107 | - pandas=0.25.3=py37hb3f55d8_0 108 | - pcre=8.41=hf484d3e_1003 109 | - pillow=6.1.0=py37he7afcd5_0 110 | - pip=19.1.1=py37_0 111 | - pixman=0.38.0=h516909a_1003 112 | - progressbar=2.5=py_0 113 | - protobuf=3.9.0=py37he1b5a44_0 114 | - psutil=5.7.2=py37h7b6447c_0 115 | - pthread-stubs=0.4=h14c3975_1001 116 | - pycparser=2.19=py37_1 117 | - pygpu=0.7.6=py37h3010b51_1000 118 | - pyparsing=2.4.0=py_0 119 | - pyqt=5.9.2=py37hcca6a23_0 120 | - python=3.7.3=h33d41f4_1 121 | - python-dateutil=2.8.0=py_0 122 | - python-utils=2.3.0=py_1 123 | - python_abi=3.7=1_cp37m 124 | - pytorch=1.4.0=py3.7_cuda10.0.130_cudnn7.6.3_0 125 | - pytz=2021.1=pyhd8ed1ab_0 126 | - pywavelets=1.1.1=py37hc1659b7_0 127 | - pyyaml=5.1.1=py37h516909a_0 128 | - qt=5.9.7=h52cfd70_2 129 | - readline=8.0=hf8c457e_0 130 | - scikit-image=0.16.2=py37hb3f55d8_0 131 | - scikit-learn=0.21.2=py37hcdab131_1 132 | - scipy=1.3.0=py37h921218d_0 133 | - setuptools=41.0.1=py37_0 134 | - shapely=1.7.0=py37hc88ce51_2 135 | - sip=4.19.8=py37hf484d3e_1000 136 | - six=1.12.0=py37_1000 137 | - sqlite=3.28.0=hcee41ef_1 138 | - tensorboard=1.13.1=py37_0 139 | - tensorflow=1.13.1=py37_0 140 | - tensorflow-estimator=1.13.0=py_0 141 | - termcolor=1.1.0=py_2 142 | - theano=1.0.4=py37hf484d3e_1000 143 | - tk=8.6.9=hed695b0_1002 144 | - toolz=0.10.0=py_0 145 | - torchfile=0.1.0=py_0 146 | - torchvision=0.5.0=py37_cu100 147 | - tornado=6.0.3=py37h516909a_0 148 | - tqdm=4.36.1=py_0 149 | - werkzeug=0.15.4=py_0 150 | - wheel=0.33.4=py37_0 151 | - x264=1!152.20180806=h14c3975_0 152 | - xorg-kbproto=1.0.7=h14c3975_1002 153 | - xorg-libice=1.0.9=h516909a_1004 154 | - xorg-libsm=1.2.3=h84519dc_1000 155 | - xorg-libx11=1.6.7=h14c3975_1000 156 | - xorg-libxau=1.0.9=h14c3975_0 157 | - xorg-libxdmcp=1.1.3=h516909a_0 158 | - xorg-libxext=1.3.4=h516909a_0 159 | - xorg-libxrender=0.9.10=h516909a_1002 160 | - xorg-renderproto=0.11.1=h14c3975_1002 161 | - xorg-xextproto=7.3.0=h14c3975_1002 162 | - xorg-xproto=7.0.31=h14c3975_1007 163 | - xz=5.2.4=h14c3975_1001 164 | - yaml=0.1.7=h14c3975_1001 165 | - zlib=1.2.11=h14c3975_1004 166 | - zstd=1.4.0=h3b9ef0a_0 167 | 168 | prefix: /opt/miniconda2/envs/env_elg_37 169 | 170 | -------------------------------------------------------------------------------- /thirdparty/sklearn_dunn.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from sklearn.preprocessing import LabelEncoder 3 | 4 | DIAMETER_METHODS = ['mean_cluster', 'farthest'] 5 | CLUSTER_DISTANCE_METHODS = ['nearest', 'farthest'] 6 | 7 | 8 | def inter_cluster_distances(labels, distances, method='nearest'): 9 | """Calculates the distances between the two nearest points of each cluster. 10 | 11 | :param labels: a list containing cluster labels for each of the n elements 12 | :param distances: an n x n numpy.array containing the pairwise distances between elements 13 | :param method: `nearest` for the distances between the two nearest points in each cluster, or `farthest` 14 | """ 15 | if method not in CLUSTER_DISTANCE_METHODS: 16 | raise ValueError( 17 | 'method must be one of {}'.format(CLUSTER_DISTANCE_METHODS)) 18 | 19 | if method == 'nearest': 20 | return __cluster_distances_by_points(labels, distances) 21 | elif method == 'farthest': 22 | return __cluster_distances_by_points(labels, distances, farthest=True) 23 | 24 | 25 | def __cluster_distances_by_points(labels, distances, farthest=False): 26 | n_unique_labels = len(np.unique(labels)) 27 | cluster_distances = np.full((n_unique_labels, n_unique_labels), 28 | float('inf') if not farthest else 0) 29 | 30 | np.fill_diagonal(cluster_distances, 0) 31 | 32 | for i in np.arange(0, len(labels) - 1): 33 | for ii in np.arange(i, len(labels)): 34 | if labels[i] != labels[ii] and ( 35 | (not farthest and 36 | distances[i, ii] < cluster_distances[labels[i], labels[ii]]) 37 | or 38 | (farthest and 39 | distances[i, ii] > cluster_distances[labels[i], labels[ii]])): 40 | cluster_distances[labels[i], labels[ii]] = cluster_distances[ 41 | labels[ii], labels[i]] = distances[i, ii] 42 | return cluster_distances 43 | 44 | 45 | def diameter(labels, distances, method='farthest'): 46 | """Calculates cluster diameters 47 | 48 | :param labels: a list containing cluster labels for each of the n elements 49 | :param distances: an n x n numpy.array containing the pairwise distances between elements 50 | :param method: either `mean_cluster` for the mean distance between all elements in each cluster, or `farthest` for the distance between the two points furthest from each other 51 | """ 52 | if method not in DIAMETER_METHODS: 53 | raise ValueError('method must be one of {}'.format(DIAMETER_METHODS)) 54 | 55 | n_clusters = len(np.unique(labels)) 56 | diameters = np.zeros(n_clusters) 57 | 58 | if method == 'mean_cluster': 59 | for i in range(0, len(labels) - 1): 60 | for ii in range(i + 1, len(labels)): 61 | if labels[i] == labels[ii]: 62 | diameters[labels[i]] += distances[i, ii] 63 | 64 | for i in range(len(diameters)): 65 | diameters[i] /= sum(labels == i) 66 | 67 | elif method == 'farthest': 68 | for i in range(0, len(labels) - 1): 69 | for ii in range(i + 1, len(labels)): 70 | if labels[i] == labels[ii] and distances[i, ii] > diameters[ 71 | labels[i]]: 72 | diameters[labels[i]] = distances[i, ii] 73 | return diameters 74 | 75 | 76 | def dunn(labels, distances, diameter_method='farthest', 77 | cdist_method='nearest'): 78 | """ 79 | Dunn index for cluster validation (larger is better). 80 | 81 | .. math:: D = \\min_{i = 1 \\ldots n_c; j = i + 1\ldots n_c} \\left\\lbrace \\frac{d \\left( c_i,c_j \\right)}{\\max_{k = 1 \\ldots n_c} \\left(diam \\left(c_k \\right) \\right)} \\right\\rbrace 82 | 83 | where :math:`d(c_i,c_j)` represents the distance between 84 | clusters :math:`c_i` and :math:`c_j`, and :math:`diam(c_k)` is the diameter of cluster :math:`c_k`. 85 | 86 | Inter-cluster distance can be defined in many ways, such as the distance between cluster centroids or between their closest elements. Cluster diameter can be defined as the mean distance between all elements in the cluster, between all elements to the cluster centroid, or as the distance between the two furthest elements. 87 | 88 | The higher the value of the resulting Dunn index, the better the clustering 89 | result is considered, since higher values indicate that clusters are 90 | compact (small :math:`diam(c_k)`) and far apart (large :math:`d \\left( c_i,c_j \\right)`). 91 | 92 | :param labels: a list containing cluster labels for each of the n elements 93 | :param distances: an n x n numpy.array containing the pairwise distances between elements 94 | :param diameter_method: see :py:function:`diameter` `method` parameter 95 | :param cdist_method: see :py:function:`diameter` `method` parameter 96 | 97 | .. [Kovacs2005] Kovács, F., Legány, C., & Babos, A. (2005). Cluster validity measurement techniques. 6th International Symposium of Hungarian Researchers on Computational Intelligence. 98 | """ 99 | 100 | labels = LabelEncoder().fit(labels).transform(labels) 101 | 102 | ic_distances = inter_cluster_distances(labels, distances, cdist_method) 103 | min_distance = min(ic_distances[ic_distances.nonzero()]) 104 | max_diameter = max(diameter(labels, distances, diameter_method)) 105 | 106 | return min_distance / max_diameter 107 | 108 | 109 | if __name__ == '__main__': 110 | from sklearn.metrics.pairwise import euclidean_distances 111 | from sklearn.datasets import load_iris 112 | from sklearn.cluster import KMeans 113 | 114 | data = load_iris() 115 | kmeans = KMeans(n_clusters=3) 116 | c = data['target'] 117 | x = data['data'] 118 | k = kmeans.fit_predict(x) 119 | d = euclidean_distances(x) 120 | 121 | for diameter_method in DIAMETER_METHODS: 122 | for cdist_method in CLUSTER_DISTANCE_METHODS: 123 | dund = dunn(c, d, diameter_method, cdist_method) 124 | dunk = dunn(k, d, diameter_method, cdist_method) 125 | print(diameter_method, cdist_method, dund, dunk) 126 | -------------------------------------------------------------------------------- /preprocessing_data/divide_datasets_per_ids.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ################################ 3 | # Pre-process AICC19 data # 4 | ################################ 5 | ''' 6 | 7 | import os 8 | import cv2 9 | import time 10 | import numpy as np 11 | from sklearn.model_selection import train_test_split 12 | from PIL import Image 13 | import matplotlib.pyplot as plt 14 | 15 | class dataset_parameters(): 16 | def __init__(self): 17 | 18 | self.fps = 10 19 | #offset = dict() 20 | self.offset= {'S01': {'c001': 0, 21 | 'c002': 1.640, 22 | 'c003': 2.049, 23 | 'c004': 2.177, 24 | 'c005': 2.235}, 25 | 'S02': {'c006': 0, 26 | 'c007': 0.061, 27 | 'c008': 0.421, 28 | 'c009': 0.660}, 29 | 'S03': { 'c010': 8.715, 30 | 'c011': 8.457, 31 | 'c012': 5.879, 32 | 'c013': 0, 33 | 'c014': 5.042, 34 | 'c015':8.492}, 35 | 'S04':{ 'c016': 0, 36 | 'c017': 14.318, 37 | 'c018': 29.955, 38 | 'c019': 26.979, 39 | 'c020': 25.905, 40 | 'c021': 39.973, 41 | 'c022': 49.422, 42 | 'c023': 45.716, 43 | 'c024': 50.853, 44 | 'c025': 50.263, 45 | 'c026': 70.450, 46 | 'c027': 85.097, 47 | 'c028': 100.110, 48 | 'c029': 125.788, 49 | 'c030': 124.319, 50 | 'c031': 125.033, 51 | 'c032': 125.199, 52 | 'c033': 150.893, 53 | 'c034': 140.218, 54 | 'c035': 165.568, 55 | 'c036': 170.797, 56 | 'c037': 170.567, 57 | 'c038': 175.426, 58 | 'c039': 175.644, 59 | 'c040': 175.838}, 60 | 'S05':{ 'c010': 0, 61 | 'c016': 0, 62 | 'c017': 0, 63 | 'c018': 0, 64 | 'c019': 0, 65 | 'c020': 0, 66 | 'c021': 0, 67 | 'c022': 0, 68 | 'c023': 0, 69 | 'c024': 0, 70 | 'c025': 0, 71 | 'c026': 0, 72 | 'c027': 0, 73 | 'c028': 0, 74 | 'c029': 0, 75 | 'c033': 0, 76 | 'c034': 0, 77 | 'c035': 0, 78 | 'c036': 0}} 79 | 80 | self.max_frame={'S01': 2110, 81 | 'S02': 2110, 82 | 'S03': 2422, 83 | 'S04': 710, 84 | 'S05': 4299, 85 | } 86 | 87 | def Union(lst1, lst2): 88 | final_list = list(set(lst1) | set(lst2)) 89 | return final_list 90 | 91 | if __name__ == '__main__': 92 | 93 | 94 | # Current root directory 95 | root_dir = os.path.dirname(os.path.abspath(__file__)) 96 | 97 | params = dataset_parameters() 98 | mode = 'validation_S02' 99 | # Original dataset directory 100 | dataset_root_dir = os.path.join('/home/vpu/Datasets/AIC20', mode) 101 | subset_dir = '/home/vpu/Datasets/AIC20/S02_per_id' 102 | 103 | scenarios = os.listdir(dataset_root_dir) 104 | 105 | ids_all =[] 106 | 107 | for s in scenarios: 108 | 109 | cameras = os.listdir(os.path.join(dataset_root_dir, s)) 110 | 111 | for c in cameras: 112 | 113 | tStart = time.time() 114 | print('Processing ' + s + ' ' + c + ' with offset = ' + str(params.offset[s][c])) 115 | 116 | gt_file = os.path.os.path.join(dataset_root_dir, s, c, 'gt/gt.txt') 117 | 118 | sync_gt_dir = os.path.os.path.join(dataset_root_dir, s, c, 'gt_sync') 119 | gt_file_sync = os.path.os.path.join(sync_gt_dir, 'gt.txt') 120 | 121 | 122 | # Sincronizar GT con los offsets 123 | if not os.path.exists(sync_gt_dir): 124 | 125 | os.makedirs(sync_gt_dir) 126 | f = open(gt_file, 'r+') 127 | gt_data = np.loadtxt(f, delimiter=',') 128 | 129 | offset_frames = int(round((params.offset[s][c]) * params.fps)) 130 | sync_gt = np.copy(gt_data) 131 | 132 | 133 | sync_gt[:, 0] = sync_gt[:, 0] + offset_frames 134 | np.savetxt(gt_file_sync, sync_gt, delimiter=',',fmt='%d') 135 | gt_sync_data = np.copy(sync_gt) 136 | tEnd = time.time() 137 | print("It cost %f sec" % (tEnd - tStart)) 138 | else: 139 | f = open(gt_file_sync, 'r+') 140 | gt_sync_data = np.loadtxt(f, delimiter=',') 141 | ids = gt_sync_data[:,1] 142 | ids_unique = np.unique(ids) 143 | ids_all = Union(list(ids_unique), list(ids_all)) 144 | 145 | 146 | 147 | for f in range(1, params.max_frame[s] + 1): 148 | 149 | bboxes = gt_sync_data[gt_sync_data[:, 0] == f, :] 150 | 151 | frame_img = Image.open(os.path.join(dataset_root_dir, s, c, 'img', '%06d.jpg' % f)) 152 | for b in range(0,bboxes.__len__()): 153 | 154 | box = frame_img.crop((bboxes[b,2], bboxes[b,3], bboxes[b,2]+ bboxes[b,4], bboxes[b,3]+bboxes[b,5])) 155 | box_id = bboxes[b,1] 156 | # plt.imshow(box) 157 | # plt.show(block = False) 158 | # plt.close() 159 | path_box = os.path.join(subset_dir,'%04d' % (int(box_id))) 160 | if not os.path.exists(path_box): 161 | os.makedirs(path_box) 162 | num_box = os.listdir(path_box).__len__() 163 | box.save(os.path.join(path_box, '%s_%04d.jpg' % (c , num_box))) 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /thirdparty/dtw/dtw.py: -------------------------------------------------------------------------------- 1 | from numpy import array, zeros, full, argmin, inf, ndim 2 | from scipy.spatial.distance import cdist 3 | from math import isinf 4 | 5 | 6 | def dtw(x, y, dist, warp=1, w=inf, s=1.0): 7 | """ 8 | Computes Dynamic Time Warping (DTW) of two sequences. 9 | 10 | :param array x: N1*M array 11 | :param array y: N2*M array 12 | :param func dist: distance used as cost measure 13 | :param int warp: how many shifts are computed. 14 | :param int w: window size limiting the maximal distance between indices of matched entries |i,j|. 15 | :param float s: weight applied on off-diagonal moves of the path. As s gets larger, the warping path is increasingly biased towards the diagonal 16 | Returns the minimum distance, the cost matrix, the accumulated cost matrix, and the wrap path. 17 | """ 18 | assert len(x) 19 | assert len(y) 20 | assert isinf(w) or (w >= abs(len(x) - len(y))) 21 | assert s > 0 22 | r, c = len(x), len(y) 23 | if not isinf(w): 24 | D0 = full((r + 1, c + 1), inf) 25 | for i in range(1, r + 1): 26 | D0[i, max(1, i - w):min(c + 1, i + w + 1)] = 0 27 | D0[0, 0] = 0 28 | else: 29 | D0 = zeros((r + 1, c + 1)) 30 | D0[0, 1:] = inf 31 | D0[1:, 0] = inf 32 | D1 = D0[1:, 1:] # view 33 | for i in range(r): 34 | for j in range(c): 35 | if (isinf(w) or (max(0, i - w) <= j <= min(c, i + w))): 36 | D1[i, j] = dist(x[i], y[j]) 37 | C = D1.copy() 38 | jrange = range(c) 39 | for i in range(r): 40 | if not isinf(w): 41 | jrange = range(max(0, i - w), min(c, i + w + 1)) 42 | for j in jrange: 43 | min_list = [D0[i, j]] 44 | for k in range(1, warp + 1): 45 | i_k = min(i + k, r) 46 | j_k = min(j + k, c) 47 | min_list += [D0[i_k, j] * s, D0[i, j_k] * s] 48 | D1[i, j] += min(min_list) 49 | if len(x) == 1: 50 | path = zeros(len(y)), range(len(y)) 51 | elif len(y) == 1: 52 | path = range(len(x)), zeros(len(x)) 53 | else: 54 | path = _traceback(D0) 55 | return D1[-1, -1], C, D1, path 56 | 57 | 58 | def accelerated_dtw(x, y, dist, warp=1): 59 | """ 60 | Computes Dynamic Time Warping (DTW) of two sequences in a faster way. 61 | Instead of iterating through each element and calculating each distance, 62 | this uses the cdist function from scipy (https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.cdist.html) 63 | 64 | :param array x: N1*M array 65 | :param array y: N2*M array 66 | :param string or func dist: distance parameter for cdist. When string is given, cdist uses optimized functions for the distance metrics. 67 | If a string is passed, the distance function can be 'braycurtis', 'canberra', 'chebyshev', 'cityblock', 'correlation', 'cosine', 'dice', 'euclidean', 'hamming', 'jaccard', 'kulsinski', 'mahalanobis', 'matching', 'minkowski', 'rogerstanimoto', 'russellrao', 'seuclidean', 'sokalmichener', 'sokalsneath', 'sqeuclidean', 'wminkowski', 'yule'. 68 | :param int warp: how many shifts are computed. 69 | Returns the minimum distance, the cost matrix, the accumulated cost matrix, and the wrap path. 70 | """ 71 | assert len(x) 72 | assert len(y) 73 | if ndim(x) == 1: 74 | x = x.reshape(-1, 1) 75 | if ndim(y) == 1: 76 | y = y.reshape(-1, 1) 77 | r, c = len(x), len(y) 78 | D0 = zeros((r + 1, c + 1)) 79 | D0[0, 1:] = inf 80 | D0[1:, 0] = inf 81 | D1 = D0[1:, 1:] 82 | D0[1:, 1:] = cdist(x, y, dist) 83 | C = D1.copy() 84 | for i in range(r): 85 | for j in range(c): 86 | min_list = [D0[i, j]] 87 | for k in range(1, warp + 1): 88 | min_list += [D0[min(i + k, r), j], 89 | D0[i, min(j + k, c)]] 90 | D1[i, j] += min(min_list) 91 | if len(x) == 1: 92 | path = zeros(len(y)), range(len(y)) 93 | elif len(y) == 1: 94 | path = range(len(x)), zeros(len(x)) 95 | else: 96 | path = _traceback(D0) 97 | return D1[-1, -1], C, D1, path 98 | 99 | 100 | def _traceback(D): 101 | i, j = array(D.shape) - 2 102 | p, q = [i], [j] 103 | while (i > 0) or (j > 0): 104 | tb = argmin((D[i, j], D[i, j + 1], D[i + 1, j])) 105 | if tb == 0: 106 | i -= 1 107 | j -= 1 108 | elif tb == 1: 109 | i -= 1 110 | else: # (tb == 2): 111 | j -= 1 112 | p.insert(0, i) 113 | q.insert(0, j) 114 | return array(p), array(q) 115 | 116 | 117 | if __name__ == '__main__': 118 | w = inf 119 | s = 1.0 120 | if 1: # 1-D numeric 121 | from sklearn.metrics.pairwise import manhattan_distances 122 | x = [0, 0, 1, 1, 2, 4, 2, 1, 2, 0] 123 | y = [1, 1, 1, 2, 2, 2, 2, 3, 2, 0] 124 | dist_fun = manhattan_distances 125 | w = 1 126 | # s = 1.2 127 | elif 0: # 2-D numeric 128 | from sklearn.metrics.pairwise import euclidean_distances 129 | x = [[0, 0], [0, 1], [1, 1], [1, 2], [2, 2], [4, 3], [2, 3], [1, 1], [2, 2], [0, 1]] 130 | y = [[1, 0], [1, 1], [1, 1], [2, 1], [4, 3], [4, 3], [2, 3], [3, 1], [1, 2], [1, 0]] 131 | dist_fun = euclidean_distances 132 | else: # 1-D list of strings 133 | from nltk.metrics.distance import edit_distance 134 | # x = ['we', 'shelled', 'clams', 'for', 'the', 'chowder'] 135 | # y = ['class', 'too'] 136 | x = ['i', 'soon', 'found', 'myself', 'muttering', 'to', 'the', 'walls'] 137 | y = ['see', 'drown', 'himself'] 138 | # x = 'we talked about the situation'.split() 139 | # y = 'we talked about the situation'.split() 140 | dist_fun = edit_distance 141 | dist, cost, acc, path = dtw(x, y, dist_fun, w=w, s=s) 142 | 143 | # Vizualize 144 | from matplotlib import pyplot as plt 145 | plt.imshow(cost.T, origin='lower', cmap=plt.cm.Reds, interpolation='nearest') 146 | plt.plot(path[0], path[1], '-o') # relation 147 | plt.xticks(range(len(x)), x) 148 | plt.yticks(range(len(y)), y) 149 | plt.xlabel('x') 150 | plt.ylabel('y') 151 | plt.axis('tight') 152 | if isinf(w): 153 | plt.title('Minimum distance: {}, slope weight: {}'.format(dist, s)) 154 | else: 155 | plt.title('Minimum distance: {}, window widht: {}, slope weight: {}'.format(dist, w, s)) 156 | plt.show() 157 | -------------------------------------------------------------------------------- /preprocessing_data/divide_subset_train_test_ids.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ################################ 3 | # Pre-process AICC19 data # 4 | ################################ 5 | ''' 6 | 7 | import os 8 | import cv2 9 | import time 10 | import numpy as np 11 | from sklearn.model_selection import train_test_split 12 | # importing shutil module 13 | import shutil 14 | 15 | class dataset_parameters(): 16 | def __init__(self): 17 | 18 | self.fps = 10 19 | #offset = dict() 20 | self.offset= {'S01': {'c001': 0, 21 | 'c002': 1.640, 22 | 'c003': 2.049, 23 | 'c004': 2.177, 24 | 'c005': 2.235}, 25 | 'S02': {'c006': 0, 26 | 'c007': 0.061, 27 | 'c008': 0.421, 28 | 'c009': 0.660}, 29 | 'S03': { 'c010': 8.715, 30 | 'c011': 8.457, 31 | 'c012': 5.879, 32 | 'c013': 0, 33 | 'c014': 5.042, 34 | 'c015':8.492}, 35 | 'S04':{ 'c016': 0, 36 | 'c017': 14.318, 37 | 'c018': 29.955, 38 | 'c019': 26.979, 39 | 'c020': 25.905, 40 | 'c021': 39.973, 41 | 'c022': 49.422, 42 | 'c023': 45.716, 43 | 'c024': 50.853, 44 | 'c025': 50.263, 45 | 'c026': 70.450, 46 | 'c027': 85.097, 47 | 'c028': 100.110, 48 | 'c029': 125.788, 49 | 'c030': 124.319, 50 | 'c031': 125.033, 51 | 'c032': 125.199, 52 | 'c033': 150.893, 53 | 'c034': 140.218, 54 | 'c035': 165.568, 55 | 'c036': 170.797, 56 | 'c037': 170.567, 57 | 'c038': 175.426, 58 | 'c039': 175.644, 59 | 'c040': 175.838}, 60 | 'S05':{ 'c010': 0, 61 | 'c016': 0, 62 | 'c017': 0, 63 | 'c018': 0, 64 | 'c019': 0, 65 | 'c020': 0, 66 | 'c021': 0, 67 | 'c022': 0, 68 | 'c023': 0, 69 | 'c024': 0, 70 | 'c025': 0, 71 | 'c026': 0, 72 | 'c027': 0, 73 | 'c028': 0, 74 | 'c029': 0, 75 | 'c033': 0, 76 | 'c034': 0, 77 | 'c035': 0, 78 | 'c036': 0}} 79 | 80 | self.max_frame={'S01': 2110, 81 | 'S02': 2110, 82 | 'S03': 2422, 83 | 'S04': 710, 84 | 'S05': 4299, 85 | } 86 | 87 | def Union(lst1, lst2): 88 | final_list = list(set(lst1) | set(lst2)) 89 | return final_list 90 | 91 | if __name__ == '__main__': 92 | 93 | 94 | 95 | params = dataset_parameters() 96 | 97 | # Original dataset directory 98 | dataset_root_dir = '/home/vpu/Datasets/AIC20/' 99 | 100 | subset_dir = '/home/vpu/Datasets/AIC20/validation_per_id' 101 | ids = os.listdir(subset_dir) 102 | 103 | 104 | # subset_train_dir = os.path.join(dataset_root_dir, 'subset_train' ) 105 | # train_txt = os.path.join(subset_train_dir, 'train.txt') 106 | # f_train = open(train_txt, 'w+') 107 | 108 | subset_test_dir = os.path.join(dataset_root_dir, 'validation_quarter') 109 | # test_txt = os.path.join(subset_test_dir, 'test.txt') 110 | # f_test = open(test_txt, 'w+') 111 | 112 | ids_train, ids_test = train_test_split(ids, test_size=0.25, shuffle=True, random_state=5) 113 | 114 | # for i_train in ids_train: 115 | # 116 | # folder_src = os.path.join(subset_dir, i_train) 117 | # folder_dst = os.path.join(subset_train_dir, i_train) 118 | # 119 | # shutil.copytree(folder_src,folder_dst) 120 | 121 | for i_test in ids_test: 122 | 123 | folder_src = os.path.join(subset_dir, i_test) 124 | folder_dst = os.path.join(subset_test_dir, i_test) 125 | 126 | shutil.copytree(folder_src, folder_dst) 127 | 128 | 129 | 130 | # # print('Dividing id ' + str(i) + ' into train and test sets') 131 | # num_images = os.listdir(os.path.join(subset_dir, i)).__len__() 132 | # images_all = np.array(range(0, num_images)) 133 | # images_all_paths = os.listdir(os.path.join(subset_dir, i)) 134 | # 135 | # for i_train in images_train: 136 | # path_id_train = os.path.join(subset_train_dir,str(int(i))) 137 | # if not os.path.exists(path_id_train): 138 | # os.makedirs(path_id_train) 139 | # 140 | # im_src = os.path.join(subset_dir, i, '%04d.jpg' % i_train) 141 | # im_dst = os.path.join(subset_train_dir, i, '%04d.jpg' % i_train) 142 | # shutil.copyfile(im_src, im_dst) 143 | # 144 | # head_tail = os.path.split(subset_train_dir) 145 | # path_in_txt = os.path.join(head_tail[1], i, '%04d.jpg' % i_train) 146 | # 147 | # f_train.write("%s\n" % path_in_txt) 148 | # 149 | 150 | 151 | # 152 | # for i_test in images_test: 153 | # path_id_test = os.path.join(subset_test_dir, str(int(i))) 154 | # if not os.path.exists(path_id_test): 155 | # os.makedirs(path_id_test) 156 | # 157 | # im_src = os.path.join(subset_dir, i, '%04d.jpg' % i_test) 158 | # im_dst = os.path.join(subset_test_dir, i, '%04d.jpg' % i_test) 159 | # shutil.copyfile(im_src, im_dst) 160 | # 161 | # head_tail = os.path.split(subset_test_dir) 162 | # path_in_txt = os.path.join(head_tail[1], i, '%04d.jpg' % i_test) 163 | # f_test.write("%s\n" % path_in_txt) 164 | 165 | 166 | 167 | 168 | 169 | 170 | # tStart = time.time() 171 | # print('Processing ' + s + ' ' + c + ' with offset = ' + str(params.offset[s][c])) 172 | # 173 | # gt_file = os.path.os.path.join(dataset_root_dir, s, c, 'gt/gt.txt') 174 | # 175 | # sync_gt_dir = os.path.os.path.join(dataset_root_dir, s, c, 'gt_sync') 176 | # gt_file_sync = os.path.os.path.join(sync_gt_dir, 'gt.txt') 177 | 178 | 179 | 180 | 181 | 182 | 183 | # frame_img.crop((1, 10, 100, 200)) 184 | 185 | 186 | # ids_train, ids_test = train_test_split(ids_all, test_size=0.2, shuffle=True) 187 | # for i in ids_train: 188 | # os.makedirs(os.path.join('/home/vpu/Datasets/AICC19/sub_train',str(int(i)))) 189 | # 190 | # 191 | # for i in ids_test: 192 | # os.makedirs(os.path.join('/home/vpu/Datasets/AICC19/sub_test',str(int(i)))) 193 | a=1 194 | 195 | -------------------------------------------------------------------------------- /network/resnet_elg.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | import math 3 | import torch.utils.model_zoo as model_zoo 4 | 5 | 6 | __all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', 7 | 'resnet152'] 8 | 9 | 10 | model_urls = { 11 | 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', 12 | 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth', 13 | 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth', 14 | 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth', 15 | 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth', 16 | } 17 | 18 | 19 | def conv3x3(in_planes, out_planes, stride=1): 20 | """3x3 convolution with padding""" 21 | return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, 22 | padding=1, bias=False) 23 | 24 | 25 | class BasicBlock(nn.Module): 26 | expansion = 1 27 | 28 | def __init__(self, inplanes, planes, stride=1, downsample=None): 29 | super(BasicBlock, self).__init__() 30 | self.conv1 = conv3x3(inplanes, planes, stride) 31 | self.bn1 = nn.BatchNorm2d(planes) 32 | self.relu = nn.ReLU(inplace=True) 33 | self.conv2 = conv3x3(planes, planes) 34 | self.bn2 = nn.BatchNorm2d(planes) 35 | self.downsample = downsample 36 | self.stride = stride 37 | 38 | def forward(self, x): 39 | residual = x 40 | 41 | out = self.conv1(x) 42 | out = self.bn1(out) 43 | out = self.relu(out) 44 | 45 | out = self.conv2(out) 46 | out = self.bn2(out) 47 | 48 | if self.downsample is not None: 49 | residual = self.downsample(x) 50 | 51 | out += residual 52 | out = self.relu(out) 53 | 54 | return out 55 | 56 | 57 | class Bottleneck(nn.Module): 58 | expansion = 4 59 | 60 | def __init__(self, inplanes, planes, stride=1, downsample=None): 61 | super(Bottleneck, self).__init__() 62 | self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) 63 | self.bn1 = nn.BatchNorm2d(planes) 64 | self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, 65 | padding=1, bias=False) 66 | self.bn2 = nn.BatchNorm2d(planes) 67 | self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) 68 | self.bn3 = nn.BatchNorm2d(planes * 4) 69 | self.relu = nn.ReLU(inplace=True) 70 | self.downsample = downsample 71 | self.stride = stride 72 | 73 | def forward(self, x): 74 | residual = x 75 | 76 | out = self.conv1(x) 77 | out = self.bn1(out) 78 | out = self.relu(out) 79 | 80 | out = self.conv2(out) 81 | out = self.bn2(out) 82 | out = self.relu(out) 83 | 84 | out = self.conv3(out) 85 | out = self.bn3(out) 86 | 87 | if self.downsample is not None: 88 | residual = self.downsample(x) 89 | 90 | out += residual 91 | out = self.relu(out) 92 | 93 | return out 94 | 95 | 96 | class ResNet(nn.Module): 97 | 98 | def __init__(self, block, layers, num_classes=1000): 99 | self.inplanes = 64 100 | super(ResNet, self).__init__() 101 | self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, 102 | bias=False) 103 | self.bn1 = nn.BatchNorm2d(64) 104 | self.relu = nn.ReLU(inplace=True) 105 | self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) 106 | self.layer1 = self._make_layer(block, 64, layers[0]) 107 | self.layer2 = self._make_layer(block, 128, layers[1], stride=2) 108 | self.layer3 = self._make_layer(block, 256, layers[2], stride=2) 109 | self.layer4 = self._make_layer(block, 512, layers[3], stride=2) 110 | self.avgpool = nn.AvgPool2d(7, stride=1) 111 | self.fc = nn.Linear(512 * block.expansion, num_classes) 112 | 113 | for m in self.modules(): 114 | if isinstance(m, nn.Conv2d): 115 | n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels 116 | m.weight.data.normal_(0, math.sqrt(2. / n)) 117 | elif isinstance(m, nn.BatchNorm2d): 118 | m.weight.data.fill_(1) 119 | m.bias.data.zero_() 120 | 121 | def _make_layer(self, block, planes, blocks, stride=1): 122 | downsample = None 123 | if stride != 1 or self.inplanes != planes * block.expansion: 124 | downsample = nn.Sequential( 125 | nn.Conv2d(self.inplanes, planes * block.expansion, 126 | kernel_size=1, stride=stride, bias=False), 127 | nn.BatchNorm2d(planes * block.expansion), 128 | ) 129 | 130 | layers = [] 131 | layers.append(block(self.inplanes, planes, stride, downsample)) 132 | self.inplanes = planes * block.expansion 133 | for i in range(1, blocks): 134 | layers.append(block(self.inplanes, planes)) 135 | 136 | return nn.Sequential(*layers) 137 | 138 | def forward(self, x): 139 | x = self.conv1(x) 140 | x = self.bn1(x) 141 | x = self.relu(x) 142 | x = self.maxpool(x) 143 | 144 | x = self.layer1(x) 145 | x = self.layer2(x) 146 | x = self.layer3(x) 147 | x = self.layer4(x) 148 | 149 | x2 = self.avgpool(x) 150 | x = x2.view(x2.size(0), -1) 151 | x = self.fc(x) 152 | return x2 153 | 154 | 155 | def resnet18(pretrained=False, **kwargs): 156 | """Constructs a ResNet-18 model. 157 | 158 | Args: 159 | pretrained (bool): If True, returns a model pre-trained on ImageNet 160 | """ 161 | model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) 162 | if pretrained: 163 | model.load_state_dict(model_zoo.load_url(model_urls['resnet18'])) 164 | return model 165 | 166 | 167 | def resnet34(pretrained=False, **kwargs): 168 | """Constructs a ResNet-34 model. 169 | 170 | Args: 171 | pretrained (bool): If True, returns a model pre-trained on ImageNet 172 | """ 173 | model = ResNet(BasicBlock, [3, 4, 6, 3], **kwargs) 174 | if pretrained: 175 | model.load_state_dict(model_zoo.load_url(model_urls['resnet34'])) 176 | return model 177 | 178 | 179 | def resnet50(pretrained=False, **kwargs): 180 | """Constructs a ResNet-50 model. 181 | 182 | Args: 183 | pretrained (bool): If True, returns a model pre-trained on ImageNet 184 | """ 185 | model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs) 186 | if pretrained: 187 | model.load_state_dict(model_zoo.load_url(model_urls['resnet50'])) 188 | return model 189 | 190 | 191 | def resnet101(pretrained=False, **kwargs): 192 | """Constructs a ResNet-101 model. 193 | 194 | Args: 195 | pretrained (bool): If True, returns a model pre-trained on ImageNet 196 | """ 197 | model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) 198 | if pretrained: 199 | model.load_state_dict(model_zoo.load_url(model_urls['resnet101'])) 200 | return model 201 | 202 | 203 | def resnet152(pretrained=False, **kwargs): 204 | """Constructs a ResNet-152 model. 205 | 206 | Args: 207 | pretrained (bool): If True, returns a model pre-trained on ImageNet 208 | """ 209 | model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs) 210 | if pretrained: 211 | model.load_state_dict(model_zoo.load_url(model_urls['resnet152'])) 212 | return model 213 | -------------------------------------------------------------------------------- /network/vgg.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from torch.utils.model_zoo import load_url as load_state_dict_from_url 4 | 5 | __all__ = [ 6 | 'VGG', 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn', 7 | 'vgg19_bn', 'vgg19', 8 | ] 9 | 10 | 11 | model_urls = { 12 | 'vgg11': 'https://download.pytorch.org/models/vgg11-bbd30ac9.pth', 13 | 'vgg13': 'https://download.pytorch.org/models/vgg13-c768596a.pth', 14 | 'vgg16': 'https://download.pytorch.org/models/vgg16-397923af.pth', 15 | 'vgg19': 'https://download.pytorch.org/models/vgg19-dcbb9e9d.pth', 16 | 'vgg11_bn': 'https://download.pytorch.org/models/vgg11_bn-6002323d.pth', 17 | 'vgg13_bn': 'https://download.pytorch.org/models/vgg13_bn-abd245e5.pth', 18 | 'vgg16_bn': 'https://download.pytorch.org/models/vgg16_bn-6c64b313.pth', 19 | 'vgg19_bn': 'https://download.pytorch.org/models/vgg19_bn-c79401a0.pth', 20 | } 21 | 22 | 23 | class VGG(nn.Module): 24 | 25 | def __init__(self, features, num_classes=1000, init_weights=True): 26 | super(VGG, self).__init__() 27 | self.features = features 28 | self.avgpool = nn.AdaptiveAvgPool2d((7, 7)) 29 | self.classifier = nn.Sequential( 30 | nn.Linear(512 * 7 * 7, 4096), 31 | nn.ReLU(True), 32 | nn.Dropout(), 33 | nn.Linear(4096, 4096), 34 | nn.ReLU(True), 35 | nn.Dropout(), 36 | nn.Linear(4096, num_classes), 37 | ) 38 | if init_weights: 39 | self._initialize_weights() 40 | 41 | def forward(self, x): 42 | x = self.features(x) 43 | x = self.avgpool(x) 44 | x = torch.flatten(x, 1) 45 | x = self.classifier(x) 46 | return x 47 | 48 | def _initialize_weights(self): 49 | for m in self.modules(): 50 | if isinstance(m, nn.Conv2d): 51 | nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') 52 | if m.bias is not None: 53 | nn.init.constant_(m.bias, 0) 54 | elif isinstance(m, nn.BatchNorm2d): 55 | nn.init.constant_(m.weight, 1) 56 | nn.init.constant_(m.bias, 0) 57 | elif isinstance(m, nn.Linear): 58 | nn.init.normal_(m.weight, 0, 0.01) 59 | nn.init.constant_(m.bias, 0) 60 | 61 | 62 | def make_layers(cfg, batch_norm=False): 63 | layers = [] 64 | in_channels = 3 65 | for v in cfg: 66 | if v == 'M': 67 | layers += [nn.MaxPool2d(kernel_size=2, stride=2)] 68 | else: 69 | conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1) 70 | if batch_norm: 71 | layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)] 72 | else: 73 | layers += [conv2d, nn.ReLU(inplace=True)] 74 | in_channels = v 75 | return nn.Sequential(*layers) 76 | 77 | 78 | cfgs = { 79 | 'A': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], 80 | 'B': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], 81 | 'D': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'], 82 | 'E': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'], 83 | } 84 | 85 | 86 | def _vgg(arch, cfg, batch_norm, pretrained, progress, **kwargs): 87 | if pretrained: 88 | kwargs['init_weights'] = False 89 | model = VGG(make_layers(cfgs[cfg], batch_norm=batch_norm), **kwargs) 90 | if pretrained: 91 | state_dict = load_state_dict_from_url(model_urls[arch], 92 | progress=progress) 93 | model.load_state_dict(state_dict) 94 | return model 95 | 96 | 97 | def vgg11(pretrained=False, progress=True, **kwargs): 98 | r"""VGG 11-layer model (configuration "A") from 99 | `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `_ 100 | Args: 101 | pretrained (bool): If True, returns a model pre-trained on ImageNet 102 | progress (bool): If True, displays a progress bar of the download to stderr 103 | """ 104 | return _vgg('vgg11', 'A', False, pretrained, progress, **kwargs) 105 | 106 | 107 | def vgg11_bn(pretrained=False, progress=True, **kwargs): 108 | r"""VGG 11-layer model (configuration "A") with batch normalization 109 | `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `_ 110 | Args: 111 | pretrained (bool): If True, returns a model pre-trained on ImageNet 112 | progress (bool): If True, displays a progress bar of the download to stderr 113 | """ 114 | return _vgg('vgg11_bn', 'A', True, pretrained, progress, **kwargs) 115 | 116 | 117 | def vgg13(pretrained=False, progress=True, **kwargs): 118 | r"""VGG 13-layer model (configuration "B") 119 | `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `_ 120 | Args: 121 | pretrained (bool): If True, returns a model pre-trained on ImageNet 122 | progress (bool): If True, displays a progress bar of the download to stderr 123 | """ 124 | return _vgg('vgg13', 'B', False, pretrained, progress, **kwargs) 125 | 126 | 127 | def vgg13_bn(pretrained=False, progress=True, **kwargs): 128 | r"""VGG 13-layer model (configuration "B") with batch normalization 129 | `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `_ 130 | Args: 131 | pretrained (bool): If True, returns a model pre-trained on ImageNet 132 | progress (bool): If True, displays a progress bar of the download to stderr 133 | """ 134 | return _vgg('vgg13_bn', 'B', True, pretrained, progress, **kwargs) 135 | 136 | 137 | def vgg16(pretrained=False, progress=True, **kwargs): 138 | r"""VGG 16-layer model (configuration "D") 139 | `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `_ 140 | Args: 141 | pretrained (bool): If True, returns a model pre-trained on ImageNet 142 | progress (bool): If True, displays a progress bar of the download to stderr 143 | """ 144 | return _vgg('vgg16', 'D', False, pretrained, progress, **kwargs) 145 | 146 | 147 | def vgg16_bn(pretrained=False, progress=True, **kwargs): 148 | r"""VGG 16-layer model (configuration "D") with batch normalization 149 | `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `_ 150 | Args: 151 | pretrained (bool): If True, returns a model pre-trained on ImageNet 152 | progress (bool): If True, displays a progress bar of the download to stderr 153 | """ 154 | return _vgg('vgg16_bn', 'D', True, pretrained, progress, **kwargs) 155 | 156 | 157 | def vgg19(pretrained=False, progress=True, **kwargs): 158 | r"""VGG 19-layer model (configuration "E") 159 | `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `_ 160 | Args: 161 | pretrained (bool): If True, returns a model pre-trained on ImageNet 162 | progress (bool): If True, displays a progress bar of the download to stderr 163 | """ 164 | return _vgg('vgg19', 'E', False, pretrained, progress, **kwargs) 165 | 166 | 167 | def vgg19_bn(pretrained=False, progress=True, **kwargs): 168 | r"""VGG 19-layer model (configuration 'E') with batch normalization 169 | `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `_ 170 | Args: 171 | pretrained (bool): If True, returns a model pre-trained on ImageNet 172 | progress (bool): If True, displays a progress bar of the download to stderr 173 | """ 174 | return _vgg('vgg19_bn', 'E', True, pretrained, progress, **kwargs) -------------------------------------------------------------------------------- /libs/features.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ################################ 3 | # Features 4 | Extraction # 5 | ################################ 6 | ''' 7 | 8 | import torch 9 | import numpy as np 10 | from torchvision import transforms 11 | from misc import normalize as norm 12 | import torch.nn.functional as F 13 | 14 | 15 | class features(): 16 | 17 | def __init__(self,dataset, net, characteristic): 18 | self.dataset = dataset 19 | self.characteristic = characteristic 20 | self.net = net 21 | 22 | def load_model(self, model_path, model_name): 23 | 24 | model_dict = self.net.state_dict() 25 | list_dict = list(model_dict) 26 | 27 | 28 | file_layers = '/home/vpu/Clustering-based-Multi-Target-Multi-Camera-Tracking/layers.txt' 29 | 30 | f_id = open(file_layers, 'w+') 31 | 32 | if model_name == 'reid.pth': # Gotten from team 12 BUPT 33 | loaded_model = torch.load(model_path) 34 | loaded_list = list(loaded_model.items()) 35 | count = 0 36 | print('Layers: ') 37 | for name, value in model_dict.items(): 38 | # if name not in ['fc.weight', "fc.bias"] and name.find('num_batches_tracked') == -1: 39 | layer_name, weights = loaded_list[count] 40 | if weights.size() == value.size(): 41 | model_dict[name] = weights 42 | print(str(count) + ". Weigths loaded for " + name) 43 | else: 44 | print(str(count) + ". Weigths NOT loaded for " + name) 45 | # else: 46 | # print(str(count) + ". Weigths NOT loaded for " + name) 47 | count += 1 48 | 49 | if model_name == 'net_last.pth': 50 | loaded_model = torch.load(model_path) 51 | loaded_list = list(loaded_model.items()) 52 | count = 0 53 | print('Layers: ') 54 | for name, value in model_dict.items(): 55 | if name.find('add_block') == -1: 56 | layer_name, weights = loaded_list[count] 57 | if weights.size() == value.size(): 58 | model_dict[name] = weights 59 | print(str(count) + ". Weigths loaded for " + name) 60 | else: 61 | print(str(count) + ". Weigths NOT loaded for " + name) 62 | 63 | count += 1 64 | 65 | if model_name == 'resnet50_model_120.pth': 66 | loaded_model = torch.load(model_path) 67 | loaded_list = list(loaded_model.items()) 68 | count = 0 69 | print('Layers: ') 70 | for name, value in model_dict.items(): 71 | if name.find('add_block') == -1: 72 | layer_name, weights = loaded_list[count] 73 | if weights.size() == value.size(): 74 | model_dict[name] = weights 75 | print(str(count) + ". Weigths loaded for " + name) 76 | else: 77 | print(str(count) + ". Weigths NOT loaded for " + name) 78 | # else: 79 | # print(str(count) + ". Weigths NOT loaded for " + name) 80 | count += 1 81 | 82 | if model_name == 'best_checkpoint.pth.tar': 83 | loaded_model = torch.load(model_path) 84 | loaded_list = list(loaded_model['net_state_dict'].items()) 85 | count = 0 86 | print('Layers: ') 87 | for name, value in model_dict.items(): 88 | 89 | layer_name, weights = loaded_list[count] 90 | if weights.size() == value.size(): 91 | model_dict[name] = weights 92 | print(str(count) + ". Weigths loaded for " + name) 93 | else: 94 | print(str(count) + ". Weigths NOT loaded for " + name) 95 | # else: 96 | # print(str(count) + ". Weigths NOT loaded for " + name) 97 | count += 1 98 | 99 | count = 0 100 | for name, value in loaded_model.items(): 101 | # if name not in ['fc.weight', "fc.bias"] and name.find('num_batches_tracked') == -1: 102 | layer_name, weights = list_dict[count] 103 | 104 | f_id.write("%d %s %s \n" % (count, name, layer_name)) 105 | 106 | count += 1 107 | f_id.close() 108 | 109 | self.net.load_state_dict(model_dict) 110 | 111 | # self.net.load_state_dict(loaded_model, strict=True) 112 | 113 | 114 | def extract(self, img): 115 | # Apply network 116 | # bbox_padded_tensor = self.dataset.data_transform((img)) 117 | bbox_padded_tensor = torch.unsqueeze(img, dim=0) 118 | self.net.cuda() 119 | features = self.net(bbox_padded_tensor.cuda()) 120 | features = torch.squeeze(features) 121 | 122 | # features_np = features.detach().cpu().numpy() 123 | # features__norm = norm.l2_norm(features) 124 | 125 | features_norm = F.normalize(features, p=2, dim = 0) 126 | features_np = features.detach().cpu().numpy() 127 | return features_np 128 | 129 | def apply_restrictions(self, dist_features, dist_spatial, sct, dist_th,mode): 130 | 131 | association_matrix = np.zeros((sct['id'].__len__(),sct['id'].__len__())) 132 | 133 | 134 | if mode == 'appearance': 135 | for i in range(sct['id'].__len__()): 136 | for j in range(sct['id'].__len__()): 137 | 138 | # Only half of the symmetric matrix 139 | if (i != j) & (i > j): 140 | 141 | if (sct['id_cam'][i] == sct['id_cam'][j]): 142 | association_matrix[i, j] = 100 143 | association_matrix[j, i] = 100 144 | else: 145 | association_matrix[i, j] = 1 146 | association_matrix[j, i] = 1 147 | 148 | else: 149 | for i in range(sct['id'].__len__()): 150 | for j in range(sct['id'].__len__()): 151 | 152 | # Only half of the symmetric matrix 153 | if (i != j) & (i > j): 154 | distance = dist_spatial[i, j] 155 | 156 | # Possible association if both points are close and from different cameras 157 | if distance <= dist_th : 158 | association_matrix[i, j] = 1 159 | association_matrix[j, i] = 1 160 | 161 | else: 162 | association_matrix[i, j] = 10 163 | association_matrix[j, i] = 10 164 | 165 | if (sct['id_cam'][i] == sct['id_cam'][j]): 166 | association_matrix[i, j] = 100 167 | association_matrix[j, i] = 100 168 | 169 | 170 | 171 | 172 | # association_matrix = 1 - association_matrix 173 | # association_matrix = 10. * association_matrix 174 | # association_matrix[association_matrix == 0] = 1 175 | 176 | restricted_dist_features = dist_features * association_matrix 177 | 178 | return restricted_dist_features,association_matrix 179 | 180 | 181 | def apply_restrictions_logits(self, dist_features, dist_spatial, sct, dist_th,mode): 182 | 183 | association_matrix = np.zeros((sct['id'].__len__(),sct['id'].__len__())) 184 | 185 | 186 | if mode == 'appearance_only': 187 | for i in range(sct['id'].__len__()): 188 | for j in range(sct['id'].__len__()): 189 | 190 | # Only half of the symmetric matrix 191 | if (i != j) & (i > j): 192 | 193 | if (sct['id_cam'][i] == sct['id_cam'][j]): 194 | association_matrix[i, j] = 100 195 | association_matrix[j, i] = 100 196 | else: 197 | association_matrix[i, j] = 1 198 | association_matrix[j, i] = 1 199 | 200 | else: 201 | for i in range(sct['id'].__len__()): 202 | for j in range(sct['id'].__len__()): 203 | 204 | # Only half of the symmetric matrix 205 | if (i != j) & (i > j): 206 | distance = dist_spatial[i, j] 207 | 208 | # Possible association if both points are close and from different cameras 209 | if distance > dist_th : 210 | dist_features[i, j] = min(1, dist_features[i, j] + 0.5) 211 | dist_features[j, i] = min(1, dist_features[j, i] + 0.5) 212 | association_matrix[i, j] = 10 213 | association_matrix[j, i] = 10 214 | else: 215 | association_matrix[i, j] = 1 216 | association_matrix[j, i] = 1 217 | 218 | 219 | if (sct['id_cam'][i] == sct['id_cam'][j]): 220 | dist_features[i, j] = 1 221 | dist_features[j, i] = 1 222 | association_matrix[i, j] = 100 223 | association_matrix[j, i] = 100 224 | 225 | # association_matrix = 1 - association_matrix 226 | # association_matrix = 10. * association_matrix 227 | # association_matrix[association_matrix == 0] = 1 228 | 229 | return dist_features, association_matrix 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | -------------------------------------------------------------------------------- /libs/colors.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class distinguishable_colors(): 4 | 5 | def __init__(self): 6 | self.list = (["#000000", "#FFFF00", "#1CE6FF", "#FF34FF", "#FF4A46", "#008941", "#006FA6", "#A30059", 7 | "#FFDBE5", "#7A4900", "#0000A6", "#63FFAC", "#B79762", "#004D43", "#8FB0FF", "#997D87", 8 | "#5A0007", "#809693", "#FEFFE6", "#1B4400", "#4FC601", "#3B5DFF", "#4A3B53", "#FF2F80", 9 | "#61615A", "#BA0900", "#6B7900", "#00C2A0", "#FFAA92", "#FF90C9", "#B903AA", "#D16100", 10 | "#DDEFFF", "#000035", "#7B4F4B", "#A1C299", "#300018", "#0AA6D8", "#013349", "#00846F", 11 | "#372101", "#FFB500", "#C2FFED", "#A079BF", "#CC0744", "#C0B9B2", "#C2FF99", "#001E09", 12 | "#00489C", "#6F0062", "#0CBD66", "#EEC3FF", "#456D75", "#B77B68", "#7A87A1", "#788D66", 13 | "#885578", "#FAD09F", "#FF8A9A", "#D157A0", "#BEC459", "#456648", "#0086ED", "#886F4C", 14 | "#34362D", "#B4A8BD", "#00A6AA", "#452C2C", "#636375", "#A3C8C9", "#FF913F", "#938A81", 15 | "#575329", "#00FECF", "#B05B6F", "#8CD0FF", "#3B9700", "#04F757", "#C8A1A1", "#1E6E00", 16 | "#7900D7", "#A77500", "#6367A9", "#A05837", "#6B002C", "#772600", "#D790FF", "#9B9700", 17 | "#549E79", "#FFF69F", "#201625", "#72418F", "#BC23FF", "#99ADC0", "#3A2465", "#922329", 18 | "#5B4534", "#FDE8DC", "#404E55", "#0089A3", "#CB7E98", "#A4E804", "#324E72", "#6A3A4C", 19 | "#83AB58", "#001C1E", "#D1F7CE", "#004B28", "#C8D0F6", "#A3A489", "#806C66", "#222800", 20 | "#BF5650", "#E83000", "#66796D", "#DA007C", "#FF1A59", "#8ADBB4", "#1E0200", "#5B4E51", 21 | "#C895C5", "#320033", "#FF6832", "#66E1D3", "#CFCDAC", "#D0AC94", "#7ED379", "#012C58", 22 | "#7A7BFF", "#D68E01", "#353339", "#78AFA1", "#FEB2C6", "#75797C", "#837393", "#943A4D", 23 | "#B5F4FF", "#D2DCD5", "#9556BD", "#6A714A", "#001325", "#02525F", "#0AA3F7", "#E98176", 24 | "#DBD5DD", "#5EBCD1", "#3D4F44", "#7E6405", "#02684E", "#962B75", "#8D8546", "#9695C5", 25 | "#E773CE", "#D86A78", "#3E89BE", "#CA834E", "#518A87", "#5B113C", "#55813B", "#E704C4", 26 | "#00005F", "#A97399", "#4B8160", "#59738A", "#FF5DA7", "#F7C9BF", "#643127", "#513A01", 27 | "#6B94AA", "#51A058", "#A45B02", "#1D1702", "#E20027", "#E7AB63", "#4C6001", "#9C6966", 28 | "#64547B", "#97979E", "#006A66", "#391406", "#F4D749", "#0045D2", "#006C31", "#DDB6D0", 29 | "#7C6571", "#9FB2A4", "#00D891", "#15A08A", "#BC65E9", "#FFFFFE", "#C6DC99", "#203B3C", 30 | "#671190", "#6B3A64", "#F5E1FF", "#FFA0F2", "#CCAA35", "#374527", "#8BB400", "#797868", 31 | "#C6005A", "#3B000A", "#C86240", "#29607C", "#402334", "#7D5A44", "#CCB87C", "#B88183", 32 | "#AA5199", "#B5D6C3", "#A38469", "#9F94F0", "#A74571", "#B894A6", "#71BB8C", "#00B433", 33 | "#789EC9", "#6D80BA", "#953F00", "#5EFF03", "#E4FFFC", "#1BE177", "#BCB1E5", "#76912F", 34 | "#003109", "#0060CD", "#D20096", "#895563", "#29201D", "#5B3213", "#A76F42", "#89412E", 35 | "#1A3A2A", "#494B5A", "#A88C85", "#F4ABAA", "#A3F3AB", "#00C6C8", "#EA8B66", "#958A9F", 36 | "#BDC9D2", "#9FA064", "#BE4700", "#658188", "#83A485", "#453C23", "#47675D", "#3A3F00", 37 | "#061203", "#DFFB71", "#868E7E", "#98D058", "#6C8F7D", "#D7BFC2", "#3C3E6E", "#D83D66", 38 | "#2F5D9B", "#6C5E46", "#D25B88", "#5B656C", "#00B57F", "#545C46", "#866097", "#365D25", 39 | "#252F99", "#00CCFF", "#674E60", "#FC009C", "#92896B", "#1E2324", "#DEC9B2", "#9D4948", 40 | "#85ABB4", "#342142", "#D09685", "#A4ACAC", "#00FFFF", "#AE9C86", "#742A33", "#0E72C5", 41 | "#AFD8EC", "#C064B9", "#91028C", "#FEEDBF", "#FFB789", "#9CB8E4", "#AFFFD1", "#2A364C", 42 | "#4F4A43", "#647095", "#34BBFF", "#807781", "#920003", "#B3A5A7", "#018615", "#F1FFC8", 43 | "#976F5C", "#FF3BC1", "#FF5F6B", "#077D84", "#F56D93", "#5771DA", "#4E1E2A", "#830055", 44 | "#02D346", "#BE452D", "#00905E", "#BE0028", "#6E96E3", "#007699", "#FEC96D", "#9C6A7D", 45 | "#3FA1B8", "#893DE3", "#79B4D6", "#7FD4D9", "#6751BB", "#B28D2D", "#E27A05", "#DD9CB8", 46 | "#AABC7A", "#980034", "#561A02", "#8F7F00", "#635000", "#CD7DAE", "#8A5E2D", "#FFB3E1", 47 | "#6B6466", "#C6D300", "#0100E2", "#88EC69", "#8FCCBE", "#21001C", "#511F4D", "#E3F6E3", 48 | "#FF8EB1", "#6B4F29", "#A37F46", "#6A5950", "#1F2A1A", "#04784D", "#101835", "#E6E0D0", 49 | "#FF74FE", "#00A45F", "#8F5DF8", "#4B0059", "#412F23", "#D8939E", "#DB9D72", "#604143", 50 | "#B5BACE", "#989EB7", "#D2C4DB", "#A587AF", "#77D796", "#7F8C94", "#FF9B03", "#555196", 51 | "#31DDAE", "#74B671", "#802647", "#2A373F", "#014A68", "#696628", "#4C7B6D", "#002C27", 52 | "#7A4522", "#3B5859", "#E5D381", "#FFF3FF", "#679FA0", "#261300", "#2C5742", "#9131AF", 53 | "#AF5D88", "#C7706A", "#61AB1F", "#8CF2D4", "#C5D9B8", "#9FFFFB", "#BF45CC", "#493941", 54 | "#863B60", "#B90076", "#003177", "#C582D2", "#C1B394", "#602B70", "#887868", "#BABFB0", 55 | "#030012", "#D1ACFE", "#7FDEFE", "#4B5C71", "#A3A097", "#E66D53", "#637B5D", "#92BEA5", 56 | "#00F8B3", "#BEDDFF", "#3DB5A7", "#DD3248", "#B6E4DE", "#427745", "#598C5A", "#B94C59", 57 | "#8181D5", "#94888B", "#FED6BD", "#536D31", "#6EFF92", "#E4E8FF", "#20E200", "#FFD0F2", 58 | "#4C83A1", "#BD7322", "#915C4E", "#8C4787", "#025117", "#A2AA45", "#2D1B21", "#A9DDB0", 59 | "#FF4F78", "#528500", "#009A2E", "#17FCE4", "#71555A", "#525D82", "#00195A", "#967874", 60 | "#555558", "#0B212C", "#1E202B", "#EFBFC4", "#6F9755", "#6F7586", "#501D1D", "#372D00", 61 | "#741D16", "#5EB393", "#B5B400", "#DD4A38", "#363DFF", "#AD6552", "#6635AF", "#836BBA", 62 | "#98AA7F", "#464836", "#322C3E", "#7CB9BA", "#5B6965", "#707D3D", "#7A001D", "#6E4636", 63 | "#443A38", "#AE81FF", "#489079", "#897334", "#009087", "#DA713C", "#361618", "#FF6F01", 64 | "#006679", "#370E77", "#4B3A83", "#C9E2E6", "#C44170", "#FF4526", "#73BE54", "#C4DF72", 65 | "#ADFF60", "#00447D", "#DCCEC9", "#BD9479", "#656E5B", "#EC5200", "#FF6EC2", "#7A617E", 66 | "#DDAEA2", "#77837F", "#A53327", "#608EFF", "#B599D7", "#A50149", "#4E0025", "#C9B1A9", 67 | "#03919A", "#1B2A25", "#E500F1", "#982E0B", "#B67180", "#E05859", "#006039", "#578F9B", 68 | "#305230", "#CE934C", "#B3C2BE", "#C0BAC0", "#B506D3", "#170C10", "#4C534F", "#224451", 69 | "#3E4141", "#78726D", "#B6602B", "#200441", "#DDB588", "#497200", "#C5AAB6", "#033C61", 70 | "#71B2F5", "#A9E088", "#4979B0", "#A2C3DF", "#784149", "#2D2B17", "#3E0E2F", "#57344C", 71 | "#0091BE", "#E451D1", "#4B4B6A", "#5C011A", "#7C8060", "#FF9491", "#4C325D", "#005C8B", 72 | "#E5FDA4", "#68D1B6", "#032641", "#140023", "#8683A9", "#CFFF00", "#A72C3E", "#34475A", 73 | "#B1BB9A", "#B4A04F", "#8D918E", "#A168A6", "#813D3A", "#425218", "#DA8386", "#776133", 74 | "#563930", "#8498AE", "#90C1D3", "#B5666B", "#9B585E", "#856465", "#AD7C90", "#E2BC00", 75 | "#E3AAE0", "#B2C2FE", "#FD0039", "#009B75", "#FFF46D", "#E87EAC", "#DFE3E6", "#848590", 76 | "#AA9297", "#83A193", "#577977", "#3E7158", "#C64289", "#EA0072", "#C4A8CB", "#55C899", 77 | "#E78FCF", "#004547", "#F6E2E3", "#966716", "#378FDB", "#435E6A", "#DA0004", "#1B000F", 78 | "#5B9C8F", "#6E2B52", "#011115", "#E3E8C4", "#AE3B85", "#EA1CA9", "#FF9E6B", "#457D8B", 79 | "#92678B", "#00CDBB", "#9CCC04", "#002E38", "#96C57F", "#CFF6B4", "#492818", "#766E52", 80 | "#20370E", "#E3D19F", "#2E3C30", "#B2EACE", "#F3BDA4", "#A24E3D", "#976FD9", "#8C9FA8", 81 | "#7C2B73", "#4E5F37", "#5D5462", "#90956F", "#6AA776", "#DBCBF6", "#DA71FF", "#987C95", 82 | "#52323C", "#BB3C42", "#584D39", "#4FC15F", "#A2B9C1", "#79DB21", "#1D5958", "#BD744E", 83 | "#160B00", "#20221A", "#6B8295", "#00E0E4", "#102401", "#1B782A", "#DAA9B5", "#B0415D", 84 | "#859253", "#97A094", "#06E3C4", "#47688C", "#7C6755", "#075C00", "#7560D5", "#7D9F00", 85 | "#C36D96", "#4D913E", "#5F4276", "#FCE4C8", "#303052", "#4F381B", "#E5A532", "#706690", 86 | "#AA9A92", "#237363", "#73013E", "#FF9079", "#A79A74", "#029BDB", "#FF0169", "#C7D2E7", 87 | "#CA8869", "#80FFCD", "#BB1F69", "#90B0AB", "#7D74A9", "#FCC7DB", "#99375B", "#00AB4D", 88 | "#ABAED1", "#BE9D91", "#E6E5A7", "#332C22", "#DD587B", "#F5FFF7", "#5D3033", "#6D3800", 89 | "#FF0020", "#B57BB3", "#D7FFE6", "#C535A9", "#260009", "#6A8781", "#A8ABB4", "#D45262", 90 | "#794B61", "#4621B2", "#8DA4DB", "#C7C890", "#6FE9AD", "#A243A7", "#B2B081", "#181B00", 91 | "#286154", "#4CA43B", "#6A9573", "#A8441D", "#5C727B", "#738671", "#D0CFCB", "#897B77", 92 | "#1F3F22", "#4145A7", "#DA9894", "#A1757A", "#63243C", "#ADAAFF", "#00CDE2", "#DDBC62", 93 | "#698EB1", "#208462", "#00B7E0", "#614A44", "#9BBB57", "#7A5C54", "#857A50", "#766B7E", 94 | "#014833", "#FF8347", "#7A8EBA", "#274740", "#946444", "#EBD8E6", "#646241", "#373917", 95 | "#6AD450", "#81817B", "#D499E3", "#979440", "#011A12", "#526554", "#B5885C", "#A499A5", 96 | "#03AD89", "#B3008B", "#E3C4B5", "#96531F", "#867175", "#74569E", "#617D9F", "#E70452", 97 | "#067EAF", "#A697B6", "#B787A8", "#9CFF93", "#311D19", "#3A9459", "#6E746E", "#B0C5AE", 98 | "#84EDF7", "#ED3488", "#754C78", "#384644", "#C7847B", "#00B6C5", "#7FA670", "#C1AF9E", 99 | "#2A7FFF", "#72A58C", "#FFC07F", "#9DEBDD", "#D97C8E", "#7E7C93", "#62E674", "#B5639E", 100 | "#FFA861", "#C2A580", "#8D9C83", "#B70546", "#372B2E", "#0098FF", "#985975", "#20204C", 101 | "#FF6C60", "#445083", "#8502AA", "#72361F", "#9676A3", "#484449", "#CED6C2", "#3B164A", 102 | "#CCA763", "#2C7F77", "#02227B", "#A37E6F", "#CDE6DC", "#CDFFFB", "#BE811A", "#F77183", 103 | "#EDE6E2", "#CDC6B4", "#FFE09E", "#3A7271", "#FF7B59", "#4E4E01", "#4AC684", "#8BC891", 104 | "#BC8A96", "#CF6353", "#DCDE5C", "#5EAADD", "#F6A0AD", "#E269AA", "#A3DAE4", "#436E83", 105 | "#002E17", "#ECFBFF", "#A1C2B6", "#50003F", "#71695B", "#67C4BB", "#536EFF", "#5D5A48", 106 | "#890039", "#969381", "#371521", "#5E4665", "#AA62C3", "#8D6F81", "#2C6135", "#410601", 107 | "#564620", "#E69034", "#6DA6BD", "#E58E56", "#E3A68B", "#48B176", "#D27D67", "#B5B268", 108 | "#7F8427", "#FF84E6", "#435740", "#EAE408", "#F4F5FF", "#325800", "#4B6BA5", "#ADCEFF", 109 | "#9B8ACC", "#885138", "#5875C1", "#7E7311", "#FEA5CA", "#9F8B5B", "#A55B54", "#89006A", 110 | "#AF756F", "#2A2000", "#576E4A", "#7F9EFF", "#7499A1", "#FFB550", "#00011E", "#D1511C", 111 | "#688151", "#BC908A", "#78C8EB", "#8502FF", "#483D30", "#C42221", "#5EA7FF", "#785715", 112 | "#0CEA91", "#FFFAED", "#B3AF9D", "#3E3D52", "#5A9BC2", "#9C2F90", "#8D5700", "#ADD79C", 113 | "#00768B", "#337D00", "#C59700", "#3156DC", "#944575", "#ECFFDC", "#D24CB2", "#97703C", 114 | "#4C257F", "#9E0366", "#88FFEC", "#B56481", "#396D2B", "#56735F", "#988376", "#9BB195", 115 | "#A9795C", "#E4C5D3", "#9F4F67", "#1E2B39", "#664327", "#AFCE78", "#322EDF", "#86B487", 116 | "#C23000", "#ABE86B", "#96656D", "#250E35", "#A60019", "#0080CF", "#CAEFFF", "#323F61", 117 | "#A449DC", "#6A9D3B", "#FF5AE4", "#636A01", "#D16CDA", "#736060", "#FFBAAD", "#D369B4", 118 | "#FFDED6", "#6C6D74", "#927D5E", "#845D70", "#5B62C1", "#2F4A36", "#E45F35", "#FF3B53", 119 | "#AC84DD", "#762988", "#70EC98", "#408543", "#2C3533", "#2E182D", "#323925", "#19181B", 120 | "#2F2E2C", "#023C32", "#9B9EE2", "#58AFAD", "#5C424D", "#7AC5A6", "#685D75", "#B9BCBD", 121 | "#834357", "#1A7B42", "#2E57AA", "#E55199", "#316E47", "#CD00C5", "#6A004D", "#7FBBEC", 122 | "#F35691", "#D7C54A", "#62ACB7", "#CBA1BC", "#A28A9A", "#6C3F3B", "#FFE47D", "#DCBAE3", 123 | "#5F816D", "#3A404A", "#7DBF32", "#E6ECDC", "#852C19", "#285366", "#B8CB9C", "#0E0D00", 124 | "#4B5D56", "#6B543F", "#E27172", "#0568EC", "#2EB500", "#D21656", "#EFAFFF", "#682021", 125 | "#2D2011", "#DA4CFF", "#70968E", "#FF7B7D", "#4A1930", "#E8C282", "#E7DBBC", "#A68486", 126 | "#1F263C", "#36574E", "#52CE79", "#ADAAA9", "#8A9F45", "#6542D2", "#00FB8C", "#5D697B", 127 | "#CCD27F", "#94A5A1", "#790229", "#E383E6", "#7EA4C1", "#4E4452", "#4B2C00", "#620B70", 128 | "#314C1E", "#874AA6", "#E30091", "#66460A", "#EB9A8B", "#EAC3A3", "#98EAB3", "#AB9180", 129 | "#B8552F", "#1A2B2F", "#94DDC5", "#9D8C76", "#9C8333", "#94A9C9", "#392935", "#8C675E", 130 | "#CCE93A", "#917100", "#01400B", "#449896", "#1CA370", "#E08DA7", "#8B4A4E", "#667776", 131 | "#4692AD", "#67BDA8", "#69255C", "#D3BFFF", "#4A5132", "#7E9285", "#77733C", "#E7A0CC", 132 | "#51A288", "#2C656A", "#4D5C5E", "#C9403A", "#DDD7F3", "#005844", "#B4A200", "#488F69", 133 | "#858182", "#D4E9B9", "#3D7397", "#CAE8CE", "#D60034", "#AA6746", "#9E5585", "#BA6200"]) -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ################################ 3 | # Cluster-based MTMC tracking # 4 | ################################ 5 | ''' 6 | 7 | # Python modules 8 | import os 9 | import time 10 | import numpy as np 11 | from PIL import Image 12 | 13 | import torch 14 | 15 | 16 | from sklearn.metrics import pairwise_distances 17 | 18 | 19 | # Own modules 20 | from preprocessing_data import preprocess_data 21 | from libs import camera, colors, display, dataset, features, sct, tracking, clustering 22 | from network import resnet_elg 23 | from network import net_id_classifier 24 | import torchvision.transforms as transforms 25 | 26 | import argparse 27 | import yaml 28 | from misc import nms 29 | 30 | 31 | parser = argparse.ArgumentParser(description='Training classifier pair of cars') 32 | 33 | parser.add_argument('--ConfigPath', metavar='DIR', help='Configuration file path') 34 | 35 | 36 | global CONFIG 37 | 38 | # from torch.utils.serialization import load_lua 39 | 40 | class mtmc(): 41 | def __init__(self, dataset_dir, detector): 42 | self.dataset_root_dir = dataset_dir 43 | self.detector = detector 44 | 45 | self.max_frame = {'S01': 2132, 46 | 'S02': 2110, 47 | 'S03': 2422, 48 | 'S04': 710, 49 | 'S05': 4299, 50 | } 51 | 52 | self.offset = {'S01': {'c001': 0, 53 | 'c002': 1.640, 54 | 'c003': 2.049, 55 | 'c004': 2.177, 56 | 'c005': 2.235}, 57 | 'S02': {'c006': 0, 58 | 'c007': 0.061, 59 | 'c008': 0.421, 60 | 'c009': 0.660}, 61 | 'S03': {'c010': 8.715, 62 | 'c011': 8.457, 63 | 'c012': 5.879, 64 | 'c013': 0, 65 | 'c014': 5.042, 66 | 'c015': 8.492}, 67 | 'S04': {'c016': 0, 68 | 'c017': 14.318, 69 | 'c018': 29.955, 70 | 'c019': 26.979, 71 | 'c020': 25.905, 72 | 'c021': 39.973, 73 | 'c022': 49.422, 74 | 'c023': 45.716, 75 | 'c024': 50.853, 76 | 'c025': 50.263, 77 | 'c026': 70.450, 78 | 'c027': 85.097, 79 | 'c028': 100.110, 80 | 'c029': 125.788, 81 | 'c030': 124.319, 82 | 'c031': 125.033, 83 | 'c032': 125.199, 84 | 'c033': 150.893, 85 | 'c034': 140.218, 86 | 'c035': 165.568, 87 | 'c036': 170.797, 88 | 'c037': 170.567, 89 | 'c038': 175.426, 90 | 'c039': 175.644, 91 | 'c040': 175.838}, 92 | 'S05': {'c010': 0, 93 | 'c016': 0, 94 | 'c017': 0, 95 | 'c018': 0, 96 | 'c019': 0, 97 | 'c020': 0, 98 | 'c021': 0, 99 | 'c022': 0, 100 | 'c023': 0, 101 | 'c024': 0, 102 | 'c025': 0, 103 | 'c026': 0, 104 | 'c027': 0, 105 | 'c028': 0, 106 | 'c029': 0, 107 | 'c033': 0, 108 | 'c034': 0, 109 | 'c035': 0, 110 | 'c036': 0}} 111 | 112 | self.colors = colors.distinguishable_colors() 113 | 114 | self.preprocess_flag = False 115 | self.display = False 116 | self.dist_th = CONFIG['DIST_TH'] 117 | self.global_tracks = list(list()) 118 | 119 | self.global_tracks.append(list()) 120 | 121 | # frame ,time, cam_id ,SCT_id ,latitude ,longitude, start_x, start_y , 122 | # end_x, end_y, start_time, end_time, left, top, width, heigth 123 | # def __init__(self, scene): 124 | 125 | 126 | if __name__ == '__main__': 127 | 128 | # Decode CONFIG file information 129 | tic1 = time.time() 130 | args = parser.parse_args() 131 | CONFIG = yaml.safe_load(open(args.ConfigPath, 'r')) 132 | 133 | ''' 134 | Train set: S01, S03, S04 135 | Test set: S02, S05 136 | ''' 137 | dataset_dir = CONFIG['DATASET_PATH'] 138 | results_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'results') 139 | detector = CONFIG['DETECTOR'] 140 | 141 | set = 'test' # 'test' 'train' 142 | 143 | # Initialize global mtmc class 144 | mtmc = mtmc(dataset_dir, detector) 145 | 146 | # Inicialize cam class 147 | cam = camera.camera(os.path.join(mtmc.dataset_root_dir, set)) 148 | 149 | # Dataset class 150 | aicc = dataset.dataset() 151 | 152 | # Display class 153 | display = display.display(mtmc.display) 154 | 155 | ### LOAD NET 156 | 157 | if CONFIG['MODEL'] == "Imagenet": 158 | # Features model pretrined 159 | net = resnet_elg.resnet50(pretrained=True) 160 | 161 | else: 162 | model = CONFIG['MODEL'] 163 | model_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'models/' + model) 164 | 165 | net = net_id_classifier.net_id_classifier('ResNet50', CONFIG['NUM_IDS'], CONFIG['SIZE_FC']) 166 | weights = torch.load(model_path)['state_dict'] 167 | net.load_state_dict(weights, strict=True) 168 | 169 | net.cuda() 170 | net.eval() 171 | 172 | feat = features.features(aicc, net, CONFIG['MODE']) 173 | 174 | 175 | # # Tracking class 176 | track = tracking.tracking(mtmc, CONFIG) 177 | 178 | 179 | # Pre-processing needs to be executed only once after downloading the AICC19 dataset 180 | if mtmc.preprocess_flag: 181 | 182 | print('Preprocessing data from ' + set + 'set' + '\n') 183 | preprocess_data.process(set, mtmc.offset) 184 | 185 | # Load Single Camera Tracking data 186 | 187 | # Initialize sct strucure 188 | sct = sct.sct(mtmc) 189 | 190 | toc2 = time.time() 191 | print(toc2 - tic1, ' latency sec Elapsed') 192 | 193 | print('Loading SCT and homographies...') 194 | 195 | 196 | 197 | # For each scenario in the set 198 | for s in ['S02']: #change if proceed 199 | 200 | # Create new data dictionary in sct class 201 | sct.new(s) 202 | 203 | # Fill it with sct data: e.g. sct.data[scene][camera] -> [ndarray] 204 | sct.load(set, s, mtmc.offset, flag_filter_size=CONFIG['FLAG_FILTER_SIZE'], score_th=CONFIG['SCORE_TH']) 205 | 206 | # Load homography matrices 207 | cameras = os.listdir(os.path.join(mtmc.dataset_root_dir, set, s)) 208 | 209 | for c in cameras: 210 | cam.load_homography_matrix(s,c) 211 | 212 | print('Done.') 213 | 214 | # MTMC - Main Loop 215 | 216 | 217 | # Results file 218 | file_results = os.path.join(results_dir, s, CONFIG['ID'] + '.txt') 219 | f_id = open(file_results, 'w+') 220 | 221 | # Scenarios 222 | for s in ['S02']: 223 | 224 | cameras = os.listdir(os.path.join(mtmc.dataset_root_dir, set, s)) 225 | cameras.sort() 226 | tic = time.time() 227 | 228 | # Frames 229 | for f in range(1,mtmc.max_frame[s] + 1): #mtmc.max_frame[s] + 1 230 | # print(['Frame ' + str(f)]) 231 | mtmc.global_tracks.append(list()) 232 | 233 | # Create empty dictionary for this frame sct 234 | sct_f = sct.new_frame_data() 235 | 236 | # Cameras 237 | for c in cameras: 238 | 239 | print('Processing ' + str(s) + ' frame ' + str(f) + ' camera ' + str(c)) 240 | 241 | frame_img = Image.open(os.path.join(mtmc.dataset_root_dir, set, s, c, 'img', '%06d.jpg' % f)) 242 | # display.show_frame(frame_img,c) 243 | 244 | sct_array = np.array(sct.data[s][c]) 245 | sct_f_data = sct_array[sct_array[:, 0] == f, :] 246 | 247 | #NMS 248 | if CONFIG['NMS'] == True: 249 | if sct_f_data.shape[0] != 0: 250 | sct_f_data = nms.non_max_suppression(sct_f_data, sct_f_data[:, 6]) 251 | 252 | 253 | # Fill sct_f dictionary with current frame information 254 | for i in range(sct_f_data.shape[0]): 255 | sct_f['id_cam'].append(int(c[-3:])) 256 | sct_f['id'].append(int(sct_f_data[i][1])) 257 | 258 | x = int(round(sct_f_data[i][2])) 259 | y = int(round(sct_f_data[i][3])) 260 | w = int(round(sct_f_data[i][4])) 261 | h = int(round(sct_f_data[i][5])) 262 | sct_f['x'].append(x) 263 | sct_f['y'].append(y) 264 | sct_f['w'].append(w) 265 | sct_f['h'].append(h) 266 | 267 | # draw bbox 268 | #display.draw_bbox(x, y, w, h) 269 | 270 | # Crop bbox 271 | bbox_img = transforms.functional.crop(frame_img, y, x, h, w) 272 | 273 | # Get a square bbox to not to change the aspect ratio 274 | # square_bbox = aicc.square(bbox_img,frame_img, x, y) 275 | # bbox_padded = aicc.pad(bbox_img, (0, 0, 0)) 276 | 277 | bbox_img_norm = aicc.data_transform((bbox_img)) 278 | sct_f['bbox'].append(bbox_img_norm) 279 | 280 | # Base of the bounding box to projection 281 | bx = round(x + round(w / 2)) 282 | by = round(y + h) 283 | xw, yw = cam.apply_homography_image_to_world(bx, by, cam.homography_matrix[c]) 284 | sct_f['xw'].append(xw) 285 | sct_f['yw'].append(-yw) # IMPORTANT: changed sign to positive coordinate 286 | 287 | # Feature extraction 288 | 289 | # plt.figure() 290 | # plt.imshow(bbox_padded) 291 | 292 | features_np = feat.extract(bbox_img_norm) 293 | sct_f['features'].append(features_np) 294 | 295 | 296 | num_det_f = sct_f['id_cam'].__len__() 297 | 298 | if num_det_f != 0: 299 | 300 | 301 | 302 | # Clustering mode 303 | 304 | # Spatial distance 305 | xy = np.transpose(np.stack((np.array(sct_f['xw']), np.array(sct_f['yw'])), axis=0)) 306 | 307 | dist_spatial = pairwise_distances(xy, xy, metric='euclidean') # dist2 = pdist(xy,metric= metric) #euclidean cosine cityblock 308 | 309 | # Set diagonal to 1 to avoid zeros 310 | dist_spatial = dist_spatial + (np.eye(dist_spatial.shape[0])) 311 | 312 | # Flag matrix with 1 when sct detections are closer than threshold 313 | dist_flag = (dist_spatial < mtmc.dist_th) * 1 314 | 315 | # norm = normalize(dist, norm='l2', axis = 0, copy = True, return_norm = False) 316 | 317 | # Initialize clustering class. New clusters structure each frame 318 | clust = clustering.clustering(mtmc) 319 | 320 | # If there are some close detections and more than 1 camera 321 | if (sum(sum(dist_flag)) != 0) and ((np.unique(sct_f['id_cam'])).size > 1): 322 | 323 | # Perform clustering using features 324 | 325 | features_all = np.array(sct_f['features']) 326 | dist_features = pairwise_distances(features_all, features_all, metric='euclidean') 327 | 328 | # 329 | if feat.characteristic == 'distance': 330 | restricted_dist_features, association_matrix = feat.apply_restrictions(dist_spatial, 331 | dist_spatial, 332 | sct_f, 333 | mtmc.dist_th, 334 | feat.characteristic) 335 | idx, optimal_clusters = clust.compute_clusters(restricted_dist_features, association_matrix) 336 | 337 | 338 | elif feat.characteristic == 'appearance': 339 | restricted_dist_features, association_matrix = feat.apply_restrictions( 340 | dist_features, dist_spatial, sct_f, mtmc.dist_th, feat.characteristic) 341 | 342 | idx, optimal_clusters = clust.compute_clusters(restricted_dist_features, association_matrix) 343 | 344 | else: 345 | 346 | # Clustering 347 | restricted_dist_features, association_matrix = feat.apply_restrictions( 348 | dist_features, dist_spatial, sct_f, mtmc.dist_th, feat.characteristic) 349 | idx, optimal_clusters = clust.compute_clusters(restricted_dist_features, association_matrix) 350 | 351 | 352 | 353 | else: # All detections are alone, no need to cluster 354 | 355 | optimal_clusters = num_det_f 356 | idx = np.array(range(0, optimal_clusters)) 357 | association_matrix = np.array([]) 358 | dist_features = [] 359 | 360 | 361 | for cl in range(optimal_clusters): 362 | 363 | # Initialize empty structure of the cluster 364 | clust.clusters_frame.append(clust.new_cluster()) 365 | 366 | # Extract detection in each cluster 367 | det_in_cluster = np.where(idx == cl)[0] 368 | 369 | # Plot detections in cluster 370 | # clust.display_detections_cluster(sct_f,det_in_cluster,cl) 371 | 372 | # Get centroid of the cluster, mean position of every detectionin the cluster 373 | mean_xw = np.mean((np.array(sct_f['xw']))[det_in_cluster]) 374 | mean_yw = np.mean((np.array(sct_f['yw']))[det_in_cluster]) 375 | 376 | clust.clusters_frame[-1]['xw'] = mean_xw 377 | clust.clusters_frame[-1]['yw'] = mean_yw 378 | 379 | # Plot centroid 380 | # clust.display_centroid_cluster(mean_xw, mean_yw, cl) 381 | 382 | for d in range(det_in_cluster.__len__()): 383 | idx_det = det_in_cluster[d] 384 | clust.clusters_frame[-1]['det'].append(clust.new_detection()) 385 | new_w = round(sct_f['w'][idx_det] + sct_f['w'][idx_det] * 0) 386 | new_h = round(sct_f['h'][idx_det] + sct_f['h'][idx_det] * 0) 387 | # c_x = sct_f['x'][idx_det] + round(sct_f['w'][idx_det] / 2 388 | # c_y = sct_f['y'][idx_det] + round(sct_f['h'][idx_det] / 2 ) 389 | clust.clusters_frame[-1]['det'][-1]['x'] = sct_f['x'][idx_det] + round(sct_f['w'][idx_det] / 2 ) - round(new_w / 2) 390 | clust.clusters_frame[-1]['det'][-1]['y'] = sct_f['y'][idx_det] + round(sct_f['h'][idx_det] / 2 ) - round(new_h / 2) 391 | clust.clusters_frame[-1]['det'][-1]['w'] = new_w 392 | clust.clusters_frame[-1]['det'][-1]['h'] = new_h 393 | clust.clusters_frame[-1]['det'][-1]['id_cam'] = sct_f['id_cam'][idx_det] 394 | clust.clusters_frame[-1]['det'][-1]['id_global'] = int(idx_det) 395 | 396 | 397 | # clust.clusters_frame[-1]['det'][-1]['features'] = sct_f['features'][idx_det] 398 | 399 | 400 | 401 | # CLUSTERS - TRACKS ASSOCIATION 402 | 403 | track.predict_new_locations() 404 | 405 | track.cluster_track_assignment(clust.clusters_frame, 1) 406 | 407 | # Update each assigned track with the corresponding detection.It calls the correct method of vision.KalmanFilter to correct the location estimate. 408 | # Next, it stores the new bounding box, and increases the age of the track and the total visible count by 1. 409 | # Finally, the function sets the invisible count to 0. 410 | 411 | track.update_assigned_tracks(clust.clusters_frame) 412 | 413 | #Mark each unassigned track as invisible and increase its age by 1 414 | 415 | track.update_unassigned_tracks() 416 | 417 | # Delete tracks that have been invisible for too many frames 418 | 419 | track.delete_lost_tracks() 420 | 421 | track.check_unassigned_clusters(clust.clusters_frame, association_matrix, dist_features, dist_spatial) 422 | 423 | # Create new tracks from unassigned detections. Assume that any unassigned detection is a start of a new track. 424 | # In practice you can use other cues to eliminate nnoisy detections such as size, location, or appearance 425 | 426 | 427 | track.create_new_tracks_KF(clust.clusters_frame) 428 | 429 | track.save_global_tracking_data(clust.clusters_frame,f,mtmc.global_tracks,cam) 430 | 431 | 432 | # WRITTING RESULTS 433 | 434 | if track.updated_flag: 435 | 436 | num_tracks_f = mtmc.global_tracks[f].__len__() 437 | for i in range(num_tracks_f): 438 | 439 | for det in range(mtmc.global_tracks[f][i]['det'].__len__()): 440 | 441 | new_w = round(mtmc.global_tracks[f][i]['det'][det]['w'] + mtmc.global_tracks[f][i]['det'][det]['w']* CONFIG['AUG_SIZE']) 442 | new_h = round(mtmc.global_tracks[f][i]['det'][det]['h'] + mtmc.global_tracks[f][i]['det'][det]['h']* CONFIG['AUG_SIZE']) 443 | 444 | 445 | arg1 = mtmc.global_tracks[f][i]['det'][det]['id_cam'] 446 | arg2 = mtmc.global_tracks[f][i]['id'] 447 | arg3 = f 448 | arg4 = mtmc.global_tracks[f][i]['det'][det]['x'] + round(mtmc.global_tracks[f][i]['det'][det]['w'] / 2) - round(new_w / 2) 449 | arg5 = mtmc.global_tracks[f][i]['det'][det]['y'] + round(mtmc.global_tracks[f][i]['det'][det]['h'] / 2) - round(new_h / 2) 450 | arg6 = new_w 451 | arg7 = new_h 452 | 453 | 454 | f_id.write("%d %d %d %d %d %d %d -1 -1\n" % (arg1, arg2, arg3, arg4, arg5, arg6, arg7)) 455 | 456 | f_id.close() 457 | 458 | 459 | toc = time.time() 460 | print(toc - tic, 'sec Elapsed total time' ) 461 | 462 | 463 | 464 | -------------------------------------------------------------------------------- /libs/tracking.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ################################ 3 | # Spatial 4 | Tracking # 5 | ################################ 6 | ''' 7 | 8 | 9 | import numpy as np 10 | import numpy.matlib 11 | import math 12 | 13 | from filterpy.kalman import KalmanFilter 14 | from filterpy.common import Q_discrete_white_noise 15 | from scipy.linalg import block_diag 16 | from scipy.optimize import linear_sum_assignment 17 | from thirdparty import bbox 18 | import torch 19 | 20 | from scipy.cluster import hierarchy 21 | from thirdparty import sklearn_dunn 22 | import matplotlib.pyplot as plt 23 | from munkres import Munkres, print_matrix, make_cost_matrix, DISALLOWED 24 | 25 | 26 | 27 | class tracking(): 28 | 29 | def __init__(self, mtmc,CONFIG): 30 | self.tracks_KF = list() 31 | self.id_track = 1 32 | self.unmatched_tracks, self.unmatched_clusters = [], [] 33 | self.matches = None 34 | self.updated_flag = 0 35 | self.CONFIG = CONFIG 36 | 37 | def new_track(self, id, centroid, kalman, cluster_id ): 38 | track = { 39 | 'id': id, 40 | 'xw': centroid[0], 41 | 'yw': centroid[1], 42 | 'kalmanFilter': kalman, 43 | 'age': 1, 44 | 'state': 0, 45 | 'totalVisibleCount': 1, 46 | 'consecutiveInvisibleCount': 0, 47 | 'fromcluster': np.asarray([cluster_id]) 48 | } 49 | 50 | return track 51 | 52 | def new_global_track(self): 53 | 54 | global_track = { 55 | 'id': [], 56 | 57 | 'xw': [], 58 | 'yw': [], 59 | 'det': []} 60 | 61 | return global_track 62 | 63 | 64 | def create_new_tracks_KF(self,clusters): 65 | 66 | 67 | centroids_xw = [clusters[item]['xw'] for item in self.unmatched_clusters] 68 | centroids_yw = [clusters[item]['yw'] for item in self.unmatched_clusters] 69 | 70 | num_centroids = centroids_xw.__len__() 71 | centroids = np.zeros((num_centroids, 2)) 72 | centroids[:, 0] = centroids_xw 73 | centroids[:, 1] = centroids_yw 74 | 75 | for i in (range(self.unmatched_clusters.__len__())): # 76 | 77 | # idx = self.unmatched_clusters[i] 78 | centroid = centroids[i,:] 79 | 80 | # Create Kalman Filter object 81 | kalman_filter = KalmanFilter(dim_x=4, dim_z=2) 82 | 83 | dt = 1. # time step 1 second 84 | 85 | # State transition matrix 86 | kalman_filter.F = np.array([[1, dt, 0, 0], 87 | [0, 1, 0, 0], 88 | [0, 0, 1, dt], 89 | [0, 0, 0, 1]]) 90 | 91 | 92 | # Assuming noise is discrete, and constant. noise in x and y are independent, so the covariance should be zero 93 | q = Q_discrete_white_noise(dim=2, dt=dt, var=0.05) 94 | kalman_filter.Q = block_diag(q, q) 95 | 96 | # initial value for the state (position and velocity) 97 | kalman_filter.x = np.array([[centroid[0], 0, centroid[1], 0]]).T #CENTROIDE 98 | 99 | # Measurement function 100 | kalman_filter.H = np.array([[1 , 0, 0, 0], 101 | [0, 0, 1 , 0]]) 102 | 103 | # Measurement noise 104 | kalman_filter.R = np.array([[5, 0], 105 | [0, 5]]) 106 | 107 | 108 | # Covariance Matrix 109 | kalman_filter.P *= 500. 110 | 111 | 112 | self.tracks_KF.append(self.new_track(self.id_track,centroid, kalman_filter,self.unmatched_clusters[i])) 113 | 114 | self.id_track = self.id_track + 1 115 | 116 | 117 | 118 | def predict_new_locations(self): 119 | 120 | for i in (range(self.tracks_KF.__len__())): 121 | 122 | # Predict the next location of the track 123 | self.tracks_KF[i]['kalmanFilter'].predict() 124 | 125 | # update centroid 126 | 127 | self.tracks_KF[i]['xw'] = self.tracks_KF[i]['kalmanFilter'].x[0] 128 | self.tracks_KF[i]['yw'] = self.tracks_KF[i]['kalmanFilter'].x[2] 129 | 130 | 131 | def assign_detections_to_tracks(self, cost,clusters, pos_track_HA, ids_track_HA,tracks_id, pos_track_to_remove, pos_cluster_HA, ids_cluster_HA, clusters_id, pos_cluster_to_remove ): 132 | 133 | self.unmatched_tracks, self.unmatched_clusters = [], [] 134 | 135 | # Hungarian algorithm (also known as Munkres algorithm) 136 | 137 | 138 | self.matches = linear_sum_assignment(cost) 139 | self.matches = list(self.matches) 140 | tracks_unassigned_HA = [] 141 | clusters_unassigned_HA = [] 142 | # check unassigmnents during Hungarian Algorithm 143 | 144 | for t in pos_track_HA: 145 | if (t not in self.matches[0]): 146 | tracks_unassigned_HA.append(np.int64(t)) 147 | 148 | for c in pos_cluster_HA: 149 | if (c not in self.matches[1]): 150 | clusters_unassigned_HA.append(np.int64(c)) 151 | 152 | 153 | 154 | # Update unassigments with filtered clusters 155 | 156 | clusters_assigned_HA = self.matches[1] 157 | ids_clusters_assigned_HA = ids_cluster_HA[clusters_assigned_HA] 158 | 159 | real_pos_clusters_matched = np.squeeze(np.array([np.where(clusters_id == id)[0] for id in ids_clusters_assigned_HA])) 160 | self.matches[1] = real_pos_clusters_matched 161 | 162 | ids_clusters_unassigned_HA = ids_cluster_HA[clusters_unassigned_HA] 163 | real_pos_clusters_unmatched = ([np.where(clusters_id == id)[0] for id in ids_clusters_unassigned_HA]) 164 | if real_pos_clusters_unmatched.__len__() == 0: 165 | self.unmatched_clusters.clear() 166 | 167 | else: 168 | self.unmatched_clusters.clear() 169 | 170 | for i in real_pos_clusters_unmatched: 171 | self.unmatched_clusters.append(np.int64(i[0])) 172 | 173 | 174 | # Update unassigments with filtered tracks 175 | tracks_assigned_HA = self.matches[0] 176 | ids_tracks_assigned_HA = ids_track_HA[tracks_assigned_HA] 177 | 178 | real_pos_tracks_matched = np.squeeze(np.array([np.where(tracks_id == id)[0] for id in ids_tracks_assigned_HA])) 179 | self.matches[0] = real_pos_tracks_matched 180 | 181 | ids_tracks_unassigned_HA = ids_track_HA[tracks_unassigned_HA] 182 | real_pos_tracks_unmatched = ([np.where(tracks_id == id)[0] for id in ids_tracks_unassigned_HA]) 183 | if real_pos_tracks_unmatched.__len__() == 0: 184 | self.unmatched_tracks.clear() 185 | 186 | else: 187 | self.unmatched_tracks.clear() 188 | for i in real_pos_tracks_unmatched: 189 | self.unmatched_tracks.append(np.int64(i[0])) 190 | 191 | 192 | 193 | for i in pos_track_to_remove: 194 | self.unmatched_tracks.append(np.int64(i)) 195 | 196 | for i in pos_cluster_to_remove: 197 | self.unmatched_clusters.append(np.int64(i)) 198 | 199 | 200 | # for t, trk in enumerate(self.tracks_KF): 201 | # if (t not in self.matches[0]): 202 | # self.unmatched_tracks.append(np.int64(t)) 203 | # 204 | # for d, det in enumerate(clusters): 205 | # if (d not in self.matches[1]): 206 | # self.unmatched_clusters.append(np.int64(d)) 207 | 208 | 209 | 210 | 211 | 212 | 213 | def cluster_track_assignment(self, clusters, display): 214 | 215 | # Clusters 216 | clusters_xw = np.array(list(item['xw'] for item in clusters)) 217 | clusters_yw = np.array(list(item['yw'] for item in clusters)) 218 | 219 | num_clusters = clusters_xw.size 220 | clusters_position = np.zeros((num_clusters,2)) 221 | clusters_position[:,0] = clusters_xw 222 | clusters_position[:,1] = clusters_yw 223 | clusters_id = np.array(range(num_clusters)) 224 | # Tracks 225 | tracks_xw = np.array(list(item['xw'] for item in self.tracks_KF)) 226 | tracks_yw = np.array(list(item['yw'] for item in self.tracks_KF)) 227 | tracks_id = np.array(list(item['id'] for item in self.tracks_KF)) 228 | 229 | 230 | num_tracks = tracks_xw.size 231 | tracks_position = np.zeros((num_tracks, 2)) 232 | tracks_position[:, 0] = tracks_xw.T 233 | tracks_position[:, 1] = tracks_yw.T 234 | 235 | # Cost matrix 236 | cost = np.zeros((num_tracks, num_clusters)) 237 | 238 | if num_clusters != 0: 239 | 240 | 241 | for i in range(num_tracks): 242 | 243 | # difference = clusters_position - np.matlib.repmat(tracks_position[i, :], num_clusters, 1) 244 | # cost[i,:] = (np.sum(pow(difference,2),axis = 1)).T #comprobar que la suma se hace bien 245 | cost[i, :] = np.linalg.norm((clusters_position - np.matlib.repmat(tracks_position[i, :], num_clusters, 1)), axis=1) 246 | 247 | pos_track_to_remove = np.where(np.sum((cost < self.CONFIG['DIST_TH']) * 1, axis=1) == 0)[0] 248 | if len(pos_track_to_remove) > 0: 249 | a = 1 250 | 251 | ids_track_HA = np.delete(tracks_id, pos_track_to_remove) 252 | pos_track_HA = np.array(range(num_tracks - len(pos_track_to_remove))) 253 | 254 | cost_filtered = np.delete(cost, pos_track_to_remove, axis = 0) 255 | 256 | #Add filtering clusters that are close to all tracks 257 | 258 | pos_cluster_to_remove = np.where(np.sum((cost < (self.CONFIG['DIST_TH']+0.00001)) * 1, axis=0) == 0)[0] 259 | 260 | if len(pos_cluster_to_remove) > 0: 261 | a = 1 262 | ids_cluster_HA = np.delete(clusters_id, pos_cluster_to_remove) 263 | pos_cluster_HA = np.array(range(num_clusters - len(pos_cluster_to_remove))) 264 | 265 | cost_filtered = np.delete(cost_filtered, pos_cluster_to_remove, axis=1) 266 | 267 | # SEGUIR CNO LA SIGUIENTE FUNCION 268 | 269 | if num_clusters != 0 and num_tracks != 0: 270 | 271 | self.assign_detections_to_tracks(cost_filtered,clusters, pos_track_HA, ids_track_HA,tracks_id, pos_track_to_remove, pos_cluster_HA, ids_cluster_HA, clusters_id, pos_cluster_to_remove) 272 | a=1 273 | 274 | 275 | 276 | else: 277 | 278 | self.matches = [] 279 | 280 | if num_clusters == 0: 281 | self.unmatched_clusters = [] 282 | self.unmatched_tracks = np.array(range(0,num_tracks)) 283 | 284 | if num_tracks == 0: 285 | self.unmatched_tracks = [] 286 | self.unmatched_clusters = np.array(range(0, num_clusters)) 287 | 288 | 289 | 290 | def update_assigned_tracks(self,clusters): 291 | # update tracks with assignments 292 | if self.matches.__len__() == 0: 293 | num_matched_tracks = 0 294 | else: 295 | num_matched_tracks = self.matches[0].__len__() 296 | 297 | for i in range(num_matched_tracks): 298 | 299 | track_id = self.matches[0][i] 300 | cluster_id = self.matches[1][i] 301 | 302 | #Correct the estimation of the object's location using the new detection. Update new centroid 303 | 304 | z = np.array([clusters[cluster_id]['xw'], clusters[cluster_id]['yw']]) 305 | 306 | self.tracks_KF[track_id]['kalmanFilter'].update(z) 307 | 308 | # Update state, 1 = confirmed NOT USED 309 | if (self.tracks_KF[track_id]['totalVisibleCount'] >= 3) and (self.tracks_KF[track_id]['consecutiveInvisibleCount'] == 0): 310 | self.tracks_KF[track_id]['state'] = 1 311 | 312 | #Update track's age 313 | self.tracks_KF[track_id]['age'] = self.tracks_KF[track_id]['age'] + 1 314 | 315 | #Update visibility 316 | self.tracks_KF[track_id]['totalVisibleCount'] = self.tracks_KF[track_id]['totalVisibleCount'] + 1 317 | self.tracks_KF[track_id]['consecutiveInvisibleCount'] = 0 318 | 319 | self.tracks_KF[track_id]['fromcluster'] = np.array([cluster_id]) 320 | 321 | def update_unassigned_tracks(self): 322 | 323 | num_unmatched_tracks = self.unmatched_tracks.__len__() 324 | 325 | for i in range(num_unmatched_tracks): 326 | 327 | track_id = self.unmatched_tracks[i] 328 | self.tracks_KF[track_id]['consecutiveInvisibleCount'] = self.tracks_KF[track_id]['consecutiveInvisibleCount'] +1 329 | self.tracks_KF[track_id]['age'] = self.tracks_KF[track_id]['age'] + 1 330 | 331 | self.tracks_KF[track_id]['fromcluster'] = np.array([]) 332 | 333 | 334 | def check_unassigned_clusters(self, clusters, association_matrix,dist_features, dist_spatial): 335 | 336 | if self.unmatched_clusters.__len__() != 0 and self.tracks_KF.__len__() != 0 and association_matrix.shape[0] != 0 : 337 | 338 | matched_clusters = self.matches[1] 339 | # num_matched_clusters = len(matched_clusters) 340 | # 341 | # clusters_matched_xw = np.array(list(clusters[i]['xw'] for i in matched_clusters)) 342 | # clusters_matched_yw = np.array(list(clusters[i]['yw'] for i in matched_clusters)) 343 | # 344 | # 345 | # matched_clusters_position = np.zeros((num_matched_clusters, 2)) 346 | # matched_clusters_position[:, 0] = clusters_matched_xw.T 347 | # matched_clusters_position[:, 1] = clusters_matched_yw.T 348 | unmatched_clusters = self.unmatched_clusters.copy() 349 | 350 | for u_cl in unmatched_clusters: 351 | 352 | 353 | if clusters[u_cl]['det'].__len__() ==1: 354 | 355 | 356 | valid_matches = np.where(association_matrix[clusters[u_cl]['det'][0]['id_global'], :] == 1)[0] 357 | 358 | 359 | if len(valid_matches) != 0: 360 | posible_features = dist_features[u_cl, valid_matches] 361 | #CAMBIAR 362 | # posible_features = dist_spatial[u_cl, valid_matches] 363 | 364 | 365 | 366 | #detection_to_join = valid_matches[np.where(posible_features == np.amin(posible_features))[0]] 367 | # cambio debido a que EfficientDet puede sacar el mismo bbxos de dif clases me quedo con la primera ocurrencia 368 | detection_to_join = valid_matches[np.argmin(posible_features)] 369 | cluster_to_join = np.array([]) 370 | # look for the cluster containing this detection 371 | "ANTIGUO funcionando bien hasta uso de det en vez de " 372 | # for cl in range(0, clusters.__len__()): 373 | # for det in clusters[cl]['det']: 374 | # if det['id_global'] == detection_to_join: 375 | # dets_in_cluster_to_join = [i['id_global'] for i in clusters[cl]['det']] 376 | # if 100 not in association_matrix[clusters[u_cl]['det'][0]['id_global'],dets_in_cluster_to_join] : 377 | # cluster_to_join = cl 378 | # break 379 | "Nuevo a raiz de un error 7/10" 380 | for cl in range(0, clusters.__len__()): 381 | for det in clusters[cl]['det']: 382 | if det['id_global'] == detection_to_join: 383 | dets_in_cluster_to_join = [i['id_global'] for i in clusters[cl]['det']] 384 | if 100 not in association_matrix[ 385 | clusters[u_cl]['det'][0]['id_global'], dets_in_cluster_to_join]: 386 | cluster_to_join = cl 387 | break 388 | 389 | 390 | # look for track containing this cluster 391 | 392 | for t in range(0, self.tracks_KF.__len__()): 393 | 394 | if cluster_to_join in np.array(self.tracks_KF[t]['fromcluster']): 395 | 396 | # self.tracks_KF[t]['fromcluster'] = (self.tracks_KF[t]['fromcluster'], u_cl) 397 | self.tracks_KF[t]['fromcluster'] = np.concatenate((self.tracks_KF[t]['fromcluster'], np.array([u_cl])), axis=0) 398 | self.unmatched_clusters.remove(u_cl) 399 | 400 | else: 401 | a=1 402 | # en este caso el unmatched cluster lejano tiene ms de una deteccion 403 | 404 | def delete_lost_tracks(self): 405 | 406 | 407 | if self.CONFIG['BLIND_OCCLUSION']: 408 | invisible_for_too_long = 10 409 | else: 410 | invisible_for_too_long = 2 411 | 412 | 413 | age_threshold = 8 414 | 415 | 416 | ages = np.array(list(item['age'] for item in self.tracks_KF)) 417 | 418 | if ages.size != 0: 419 | totalVisibleCounts = np.array(list(item['totalVisibleCount'] for item in self.tracks_KF)) 420 | visibility = totalVisibleCounts / ages 421 | 422 | consecutiveInvisibleCount = np.array(list(item['consecutiveInvisibleCount'] for item in self.tracks_KF)) 423 | 424 | lostInds = np.bitwise_or(np.bitwise_and(ages < age_threshold, visibility < 0.6), consecutiveInvisibleCount >= invisible_for_too_long) 425 | 426 | if len(np.where(lostInds == True)[0]) != 0: 427 | something_removed = 1 428 | 429 | tracks_KF_clean = [item for item in self.tracks_KF if lostInds[self.tracks_KF.index(item)] == False] 430 | 431 | 432 | self.tracks_KF = tracks_KF_clean 433 | # lost_ids = consecutiveInvisibleCount >= invisible_for_too_long 434 | 435 | 436 | 437 | def save_global_tracking_data(self,clusters, f , global_tracks,cam): 438 | 439 | num_tracks = self.tracks_KF.__len__() 440 | 441 | # check overlap 442 | clusters_id = np.array(list(item['fromcluster'] for item in self.tracks_KF)) 443 | dets = list() 444 | ids_cam = list() 445 | ids_track = list() 446 | id_tracks_to_clean = list() 447 | # np.array(list(item['fromcluster'] for item in clusters)) 448 | # a = [item['det'] for item in clusters if clusters.index(item) in clusters_id] 449 | 450 | # 451 | if self.CONFIG['BLIND_OCCLUSION'] == True: 452 | invisible = 2 453 | else: 454 | invisible = 1 455 | 456 | 457 | if num_tracks != 0: 458 | self.updated_flag = 1 459 | 460 | for i in range(num_tracks): 461 | 462 | if self.tracks_KF[i]['consecutiveInvisibleCount'] < invisible: #1 2 463 | 464 | global_tracks[f].append(self.new_global_track()) 465 | global_tracks[f][-1]['id'] = self.tracks_KF[i]['id'] 466 | global_tracks[f][-1]['xw'] = self.tracks_KF[i]['xw'] 467 | global_tracks[f][-1]['yw'] = self.tracks_KF[i]['yw'] 468 | 469 | from_cluster = self.tracks_KF[i]['fromcluster'] 470 | 471 | for s in range(from_cluster.size): 472 | 473 | for c in range((clusters[int(from_cluster[s])]['det']).__len__()): 474 | 475 | global_tracks[f][-1]['det'].append(clusters[int(from_cluster[s])]['det'][c]) 476 | 477 | 478 | 479 | if self.CONFIG['REPROJECTION']== True: 480 | if from_cluster.size == 0: 481 | 482 | pos_prev = [global_tracks[f-1].index(item) for item in global_tracks[f-1] if item['id'] == global_tracks[f][-1]['id']] 483 | global_tracks[f][-1]['det'] = global_tracks[f-1][pos_prev[0]]['det'] 484 | prev_bbox = global_tracks[f - 1][pos_prev[0]]['det'][0] 485 | # prev_basex = prev_bbox['x'] + (prev_bbox['w']/2) 486 | # prev_basey = prev_bbox['y'] + (prev_bbox['h']) 487 | 488 | centroid_x = self.tracks_KF[i]['xw'] 489 | centroid_y = self.tracks_KF[i]['yw'] 490 | 491 | base_x, base_y = cam.apply_homography_world_to_image(centroid_x, -centroid_y, 492 | cam.homography_matrix[ 493 | 'c00' + str(prev_bbox['id_cam'])]) 494 | 495 | x = np.round(base_x - (prev_bbox['w'] / 2)) 496 | y = np.round(base_y - (prev_bbox['h'])) 497 | global_tracks[f][-1]['det'][0]['x'] = int(x) 498 | global_tracks[f][-1]['det'][0]['y'] = int(y) 499 | 500 | for item in global_tracks[f]: 501 | for item2 in item['det']: 502 | dets.append([item2['x'],item2['y'],item2['w'],item2['h']]) 503 | ids_cam.append(item2['id_cam']) 504 | ids_track.append(item['id']) 505 | 506 | 507 | for det1 in dets: 508 | for det2 in dets: 509 | if det1 != det2 and dets.index(det2)>dets.index(det1): 510 | 511 | # print(dets.index(det1)) 512 | # print(' and ') 513 | # print(dets.index(det2)) 514 | # # enter bbox like x1 x2 y1 y 515 | 516 | box1 = np.array((det1[0],det1[1],det1[0]+det1[2],det1[1]+det1[3])) 517 | box2 = np.array((det2[0], det2[1], det2[0] + det2[2], det2[1] + det2[3])) # 518 | iou = bbox.bbox_iou(torch.from_numpy(box1).cuda(),torch.from_numpy(box2).cuda()) 519 | if iou.item() > 0.8: 520 | 521 | index1 = dets.index(det1) 522 | index2 = dets.index(det2) 523 | id_track1 = ids_track[index1] 524 | id_track2 = ids_track[index2] 525 | id_cam1 = ids_cam[index1] 526 | id_cam2 = ids_cam[index2] 527 | 528 | if id_cam1 == id_cam2: 529 | age1 = [item['age'] for item in self.tracks_KF if item['id'] == id_track1] 530 | age2 = [item['age'] for item in self.tracks_KF if item['id'] == id_track2] 531 | 532 | if age1[0] > age2[0]: 533 | # delete track2 of globl and track_KF 534 | id_tracks_to_clean.append(id_track2) 535 | 536 | else: 537 | id_tracks_to_clean.append(id_track1) 538 | 539 | tracks_KF_clean = [item for item in self.tracks_KF if item['id'] not in id_tracks_to_clean] 540 | self.tracks_KF = tracks_KF_clean 541 | 542 | global_tracks_clean = [item for item in global_tracks[f] if item['id'] not in id_tracks_to_clean] 543 | global_tracks[f] = global_tracks_clean 544 | 545 | 546 | else: 547 | self.updated_flag = 0 548 | global_tracks[f] = [] 549 | 550 | return global_tracks 551 | 552 | 553 | 554 | def display_tracks(self): 555 | if self.tracks_KF.__len__() > 0: 556 | # plt.figure() 557 | 558 | for t in self.tracks_KF: 559 | 560 | plt.plot(t['xw'], t['yw'], '*', lineWidth=1, markerSize=10, color='red') 561 | plt.text(t['xw'] - 0.000005, t['yw'] + 0.000005, str(t['id']), fontsize=15, color='red') 562 | 563 | 564 | 565 | 566 | 567 | 568 | --------------------------------------------------------------------------------