├── .dockerignore ├── .gitattributes ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── configs ├── deep_sort.yaml └── simkai.ttf ├── deep_sort ├── README.md ├── __init__.py ├── deep │ ├── __init__.py │ ├── checkpoint │ │ ├── .gitkeep │ │ ├── ckpt.t7 │ │ └── original_ckpt.t7 │ ├── evaluate.py │ ├── feature_extractor.py │ ├── model.py │ ├── original_model.py │ ├── test.py │ └── train.py ├── deep_reid.py ├── deep_sort.py └── sort │ ├── __init__.py │ ├── detection.py │ ├── iou_matching.py │ ├── kalman_filter.py │ ├── linear_assignment.py │ ├── nn_matching.py │ ├── preprocessing.py │ ├── track.py │ └── tracker.py ├── detect.py ├── fast_reid ├── demo │ ├── README.md │ ├── demo.py │ ├── person_bank.py │ ├── plot_roc_with_pickle.py │ ├── predictor.py │ ├── run_demo.sh │ └── visualize_result.py ├── fastreid │ ├── __init__.py │ ├── config │ │ ├── __init__.py │ │ ├── config.py │ │ └── defaults.py │ ├── data │ │ ├── __init__.py │ │ ├── build.py │ │ ├── common.py │ │ ├── data_utils.py │ │ ├── datasets │ │ │ ├── AirportALERT.py │ │ │ ├── __init__.py │ │ │ ├── bases.py │ │ │ ├── caviara.py │ │ │ ├── cuhk03.py │ │ │ ├── cuhk_sysu.py │ │ │ ├── dukemtmcreid.py │ │ │ ├── iLIDS.py │ │ │ ├── lpw.py │ │ │ ├── market1501.py │ │ │ ├── msmt17.py │ │ │ ├── pes3d.py │ │ │ ├── pku.py │ │ │ ├── prai.py │ │ │ ├── saivt.py │ │ │ ├── sensereid.py │ │ │ ├── shinpuhkan.py │ │ │ ├── sysu_mm.py │ │ │ ├── thermalworld.py │ │ │ ├── vehicleid.py │ │ │ ├── veri.py │ │ │ ├── veriwild.py │ │ │ ├── viper.py │ │ │ └── wildtracker.py │ │ ├── samplers │ │ │ ├── __init__.py │ │ │ ├── data_sampler.py │ │ │ └── triplet_sampler.py │ │ └── transforms │ │ │ ├── __init__.py │ │ │ ├── autoaugment.py │ │ │ ├── build.py │ │ │ ├── functional.py │ │ │ └── transforms.py │ ├── engine │ │ ├── __init__.py │ │ ├── defaults.py │ │ ├── hooks.py │ │ ├── launch.py │ │ └── train_loop.py │ ├── evaluation │ │ ├── __init__.py │ │ ├── evaluator.py │ │ ├── query_expansion.py │ │ ├── rank.py │ │ ├── rank_cylib │ │ │ ├── Makefile │ │ │ ├── __init__.py │ │ │ ├── rank_cy.pyx │ │ │ ├── roc_cy.pyx │ │ │ ├── setup.py │ │ │ └── test_cython.py │ │ ├── reid_evaluation.py │ │ ├── rerank.py │ │ ├── roc.py │ │ └── testing.py │ ├── layers │ │ ├── __init__.py │ │ ├── activation.py │ │ ├── arc_softmax.py │ │ ├── batch_drop.py │ │ ├── batch_norm.py │ │ ├── circle_softmax.py │ │ ├── context_block.py │ │ ├── cos_softmax.py │ │ ├── frn.py │ │ ├── gather_layer.py │ │ ├── non_local.py │ │ ├── pooling.py │ │ ├── se_layer.py │ │ └── splat.py │ ├── modeling │ │ ├── __init__.py │ │ ├── backbones │ │ │ ├── __init__.py │ │ │ ├── build.py │ │ │ ├── osnet.py │ │ │ ├── regnet │ │ │ │ ├── __init__.py │ │ │ │ ├── config.py │ │ │ │ ├── effnet.py │ │ │ │ ├── effnet │ │ │ │ │ ├── EN-B0_dds_8gpu.yaml │ │ │ │ │ ├── EN-B1_dds_8gpu.yaml │ │ │ │ │ ├── EN-B2_dds_8gpu.yaml │ │ │ │ │ ├── EN-B3_dds_8gpu.yaml │ │ │ │ │ ├── EN-B4_dds_8gpu.yaml │ │ │ │ │ └── EN-B5_dds_8gpu.yaml │ │ │ │ ├── regnet.py │ │ │ │ ├── regnetx │ │ │ │ │ ├── RegNetX-1.6GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetX-12GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetX-16GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetX-200MF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetX-3.2GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetX-32GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetX-4.0GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetX-400MF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetX-6.4GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetX-600MF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetX-8.0GF_dds_8gpu.yaml │ │ │ │ │ └── RegNetX-800MF_dds_8gpu.yaml │ │ │ │ └── regnety │ │ │ │ │ ├── RegNetY-1.6GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetY-12GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetY-16GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetY-200MF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetY-3.2GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetY-32GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetY-4.0GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetY-400MF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetY-6.4GF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetY-600MF_dds_8gpu.yaml │ │ │ │ │ ├── RegNetY-8.0GF_dds_8gpu.yaml │ │ │ │ │ └── RegNetY-800MF_dds_8gpu.yaml │ │ │ ├── resnest.py │ │ │ ├── resnet.py │ │ │ ├── resnet_distill.py │ │ │ ├── resnext.py │ │ │ └── shufflenet.py │ │ ├── heads │ │ │ ├── __init__.py │ │ │ ├── build.py │ │ │ └── embedding_head.py │ │ ├── losses │ │ │ ├── __init__.py │ │ │ ├── circle_loss.py │ │ │ ├── cross_entroy_loss.py │ │ │ ├── focal_loss.py │ │ │ ├── triplet_loss.py │ │ │ └── utils.py │ │ └── meta_arch │ │ │ ├── __init__.py │ │ │ ├── baseline.py │ │ │ ├── build.py │ │ │ ├── distiller.py │ │ │ ├── mgn.py │ │ │ └── moco.py │ ├── solver │ │ ├── __init__.py │ │ ├── build.py │ │ ├── lr_scheduler.py │ │ └── optim │ │ │ ├── __init__.py │ │ │ ├── lamb.py │ │ │ └── swa.py │ └── utils │ │ ├── __init__.py │ │ ├── checkpoint.py │ │ ├── collect_env.py │ │ ├── comm.py │ │ ├── compute_dist.py │ │ ├── env.py │ │ ├── events.py │ │ ├── faiss_utils.py │ │ ├── file_io.py │ │ ├── history_buffer.py │ │ ├── logger.py │ │ ├── precision_bn.py │ │ ├── registry.py │ │ ├── summary.py │ │ ├── timer.py │ │ ├── visualizer.py │ │ └── weight_init.py └── query │ ├── names.npy │ └── query_features.npy ├── hubconf.py ├── kd-r34-r101_ibn └── config.yaml ├── models ├── __init__.py ├── common.py ├── experimental.py ├── export.py ├── hub │ ├── yolov3-spp.yaml │ ├── yolov5-fpn.yaml │ └── yolov5-panet.yaml ├── yolo.py ├── yolov5l.yaml ├── yolov5m.yaml ├── yolov5s.yaml └── yolov5x.yaml ├── pedestrians-cluster ├── .idea │ ├── deployment.xml │ ├── face-cluster-framework.iml │ ├── inspectionProfiles │ │ └── profiles_settings.xml │ ├── misc.xml │ ├── modules.xml │ ├── remote-mappings.xml │ ├── vcs.xml │ ├── webServers.xml │ └── workspace.xml ├── README-zh.md ├── README.md ├── bin │ └── README.md ├── data │ ├── input_pictures │ │ ├── README.md │ │ └── alldata │ ├── output_pictures │ │ └── README.md │ └── tmp │ │ └── README.md ├── evaluation │ ├── __init__.py │ ├── evaluate.py │ └── metrics.py ├── face_cluster │ ├── __init__.py │ └── face_cluster_by_infomap.py ├── face_feature_extract │ ├── __init__.py │ ├── datasets │ │ ├── __init__.py │ │ ├── bin_dataset.py │ │ ├── filelist_dataset.py │ │ └── sampler.py │ ├── extract_feature.py │ ├── models │ │ ├── __init__.py │ │ ├── classifier.py │ │ ├── ext_layers │ │ │ ├── __init__.py │ │ │ ├── hf_sampler.py │ │ │ ├── hnsw_sampler.py │ │ │ ├── ident.py │ │ │ ├── paramclient.py │ │ │ └── test_ps.py │ │ ├── hynet.py │ │ ├── ir.py │ │ └── resnet.py │ └── pic_path ├── main.py ├── main_fastreid.py ├── pred_label_path.txt ├── pretrain_models │ ├── README.md │ └── __init__.py ├── requirements.txt └── tools │ ├── __init__.py │ ├── pic_path │ └── utils.py ├── person_count.py ├── person_detect_yolov5.py ├── person_search_reid.py ├── requirements.txt ├── test.py ├── train.py ├── tutorial.ipynb ├── utils ├── __init__.py ├── activations.py ├── datasets.py ├── draw.py ├── general.py ├── google_utils.py ├── log.py ├── parser.py └── torch_utils.py └── weights └── download_weights.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | # this drop notebooks from GitHub language stats 2 | *.ipynb linguist-vendored 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Start FROM Nvidia PyTorch image https://ngc.nvidia.com/catalog/containers/nvidia:pytorch 2 | FROM nvcr.io/nvidia/pytorch:20.03-py3 3 | 4 | # Install dependencies 5 | COPY requirements.txt . 6 | RUN pip install -r requirements.txt gsutil 7 | 8 | # Create working directory 9 | RUN mkdir -p /usr/src/app 10 | WORKDIR /usr/src/app 11 | 12 | # Copy contents 13 | COPY . /usr/src/app 14 | 15 | # Copy weights 16 | #RUN python3 -c "from models import *; \ 17 | #attempt_download('weights/yolov5s.pt'); \ 18 | #attempt_download('weights/yolov5m.pt'); \ 19 | #attempt_download('weights/yolov5l.pt')" 20 | 21 | 22 | # --------------------------------------------------- Extras Below --------------------------------------------------- 23 | 24 | # Build and Push 25 | # t=ultralytics/yolov5:latest && sudo docker build -t $t . && sudo docker push $t 26 | 27 | # Pull and Run 28 | # t=ultralytics/yolov5:latest && sudo docker pull $t && sudo docker run -it --ipc=host $t 29 | 30 | # Pull and Run with local directory access 31 | # t=ultralytics/yolov5:latest && sudo docker pull $t && sudo docker run -it --ipc=host --gpus all -v "$(pwd)"/coco:/usr/src/coco $t 32 | 33 | # Kill all 34 | # sudo docker kill "$(sudo docker ps -q)" 35 | 36 | # Kill all image-based 37 | # sudo docker kill $(sudo docker ps -a -q --filter ancestor=ultralytics/yolov5:latest) 38 | 39 | # Bash into running container 40 | # sudo docker container exec -it ba65811811ab bash 41 | 42 | # Bash into stopped container 43 | # sudo docker commit 092b16b25c5b usr/resume && sudo docker run -it --gpus all --ipc=host -v "$(pwd)"/coco:/usr/src/coco --entrypoint=sh usr/resume 44 | 45 | # Send weights to GCP 46 | # python -c "from utils.general import *; strip_optimizer('runs/exp0_*/weights/last.pt', 'tmp.pt')" && gsutil cp tmp.pt gs://* 47 | 48 | # Clean up 49 | # docker system prune -a --volumes 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 详细请看博客:https://blog.csdn.net/zengwubbb/article/details/113422048 2 | reid模型为fast-reid框架训练,resnet101蒸馏出来的resnet34,因为模型保存了FC层和优化器等参数所以很大,去除这些只保留resnet34的话模型30多MB,整个流程在2070GPU下能达到实时效果 3 | reid模型:链接: https://pan.baidu.com/s/1bMG3qy7npecCh6AzNO-Zyw 提取码: hy1m 4 | -------------------------------------------------------------------------------- /configs/deep_sort.yaml: -------------------------------------------------------------------------------- 1 | DEEPSORT: 2 | REID_CKPT: "./deep_sort/deep/checkpoint/ckpt.t7" 3 | MAX_DIST: 0.2 4 | MIN_CONFIDENCE: 0.3 5 | NMS_MAX_OVERLAP: 0.5 6 | MAX_IOU_DISTANCE: 0.7 7 | MAX_AGE: 70 8 | N_INIT: 3 9 | NN_BUDGET: 100 10 | -------------------------------------------------------------------------------- /configs/simkai.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/configs/simkai.ttf -------------------------------------------------------------------------------- /deep_sort/README.md: -------------------------------------------------------------------------------- 1 | # Deep Sort 2 | 3 | This is the implemention of deep sort with pytorch. -------------------------------------------------------------------------------- /deep_sort/__init__.py: -------------------------------------------------------------------------------- 1 | from .deep_sort import DeepSort 2 | from .deep_reid import DeepReid 3 | 4 | 5 | __all__ = ['DeepSort', 'build_tracker', 'DeepReid'] 6 | 7 | 8 | def build_tracker(cfg, sort, use_cuda): 9 | if sort: 10 | return DeepSort(cfg.DEEPSORT.REID_CKPT, 11 | max_dist=cfg.DEEPSORT.MAX_DIST, min_confidence=cfg.DEEPSORT.MIN_CONFIDENCE, 12 | nms_max_overlap=cfg.DEEPSORT.NMS_MAX_OVERLAP, max_iou_distance=cfg.DEEPSORT.MAX_IOU_DISTANCE, 13 | max_age=cfg.DEEPSORT.MAX_AGE, n_init=cfg.DEEPSORT.N_INIT, nn_budget=cfg.DEEPSORT.NN_BUDGET, use_cuda=use_cuda) 14 | else: 15 | return DeepReid(cfg.DEEPSORT.REID_CKPT, 16 | max_dist=cfg.DEEPSORT.MAX_DIST, min_confidence=cfg.DEEPSORT.MIN_CONFIDENCE, 17 | nms_max_overlap=cfg.DEEPSORT.NMS_MAX_OVERLAP, max_iou_distance=cfg.DEEPSORT.MAX_IOU_DISTANCE, 18 | max_age=cfg.DEEPSORT.MAX_AGE, n_init=cfg.DEEPSORT.N_INIT, nn_budget=cfg.DEEPSORT.NN_BUDGET, use_cuda=use_cuda) 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /deep_sort/deep/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/deep_sort/deep/__init__.py -------------------------------------------------------------------------------- /deep_sort/deep/checkpoint/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/deep_sort/deep/checkpoint/.gitkeep -------------------------------------------------------------------------------- /deep_sort/deep/checkpoint/ckpt.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/deep_sort/deep/checkpoint/ckpt.t7 -------------------------------------------------------------------------------- /deep_sort/deep/checkpoint/original_ckpt.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/deep_sort/deep/checkpoint/original_ckpt.t7 -------------------------------------------------------------------------------- /deep_sort/deep/evaluate.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | features = torch.load("features.pth") 4 | qf = features["qf"] 5 | ql = features["ql"] 6 | gf = features["gf"] 7 | gl = features["gl"] 8 | 9 | scores = qf.mm(gf.t()) 10 | res = scores.topk(5, dim=1)[1][:,0] 11 | top1correct = gl[res].eq(ql).sum().item() 12 | 13 | print("Acc top1:{:.3f}".format(top1correct/ql.size(0))) 14 | 15 | 16 | -------------------------------------------------------------------------------- /deep_sort/deep/feature_extractor.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torchvision.transforms as transforms 3 | import numpy as np 4 | import cv2 5 | import logging 6 | 7 | from .model import Net 8 | 9 | class Extractor(object): 10 | def __init__(self, model_path, use_cuda=True): 11 | self.net = Net(reid=True) 12 | self.device = "cuda" if torch.cuda.is_available() and use_cuda else "cpu" 13 | state_dict = torch.load(model_path, map_location=lambda storage, loc: storage)['net_dict'] 14 | self.net.load_state_dict(state_dict) 15 | logger = logging.getLogger("root.tracker") 16 | logger.info("Loading weights from {}... Done!".format(model_path)) 17 | self.net.to(self.device) 18 | self.size = (64, 128) 19 | self.norm = transforms.Compose([ 20 | transforms.ToTensor(), 21 | transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), 22 | ]) 23 | 24 | 25 | 26 | def _preprocess(self, im_crops): 27 | """ 28 | TODO: 29 | 1. to float with scale from 0 to 1 30 | 2. resize to (64, 128) as Market1501 dataset did 31 | 3. concatenate to a numpy array 32 | 3. to torch Tensor 33 | 4. normalize 34 | """ 35 | def _resize(im, size): 36 | return cv2.resize(im.astype(np.float32)/255., size) 37 | 38 | im_batch = torch.cat([self.norm(_resize(im, self.size)).unsqueeze(0) for im in im_crops], dim=0).float() 39 | return im_batch 40 | 41 | 42 | def __call__(self, im_crops): 43 | im_batch = self._preprocess(im_crops) 44 | with torch.no_grad(): 45 | im_batch = im_batch.to(self.device) 46 | features = self.net(im_batch) 47 | return features.cpu().numpy() 48 | 49 | 50 | if __name__ == '__main__': 51 | img = cv2.imread("demo.jpg")[:,:,(2,1,0)] 52 | extr = Extractor("checkpoint/ckpt.t7") 53 | feature = extr(img) 54 | print(feature.shape) 55 | 56 | -------------------------------------------------------------------------------- /deep_sort/deep/model.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | 5 | class BasicBlock(nn.Module): 6 | def __init__(self, c_in, c_out,is_downsample=False): 7 | super(BasicBlock,self).__init__() 8 | self.is_downsample = is_downsample 9 | if is_downsample: 10 | self.conv1 = nn.Conv2d(c_in, c_out, 3, stride=2, padding=1, bias=False) 11 | else: 12 | self.conv1 = nn.Conv2d(c_in, c_out, 3, stride=1, padding=1, bias=False) 13 | self.bn1 = nn.BatchNorm2d(c_out) 14 | self.relu = nn.ReLU(True) 15 | self.conv2 = nn.Conv2d(c_out,c_out,3,stride=1,padding=1, bias=False) 16 | self.bn2 = nn.BatchNorm2d(c_out) 17 | if is_downsample: 18 | self.downsample = nn.Sequential( 19 | nn.Conv2d(c_in, c_out, 1, stride=2, bias=False), 20 | nn.BatchNorm2d(c_out) 21 | ) 22 | elif c_in != c_out: 23 | self.downsample = nn.Sequential( 24 | nn.Conv2d(c_in, c_out, 1, stride=1, bias=False), 25 | nn.BatchNorm2d(c_out) 26 | ) 27 | self.is_downsample = True 28 | 29 | def forward(self,x): 30 | y = self.conv1(x) 31 | y = self.bn1(y) 32 | y = self.relu(y) 33 | y = self.conv2(y) 34 | y = self.bn2(y) 35 | if self.is_downsample: 36 | x = self.downsample(x) 37 | return F.relu(x.add(y),True) 38 | 39 | def make_layers(c_in,c_out,repeat_times, is_downsample=False): 40 | blocks = [] 41 | for i in range(repeat_times): 42 | if i ==0: 43 | blocks += [BasicBlock(c_in,c_out, is_downsample=is_downsample),] 44 | else: 45 | blocks += [BasicBlock(c_out,c_out),] 46 | return nn.Sequential(*blocks) 47 | 48 | class Net(nn.Module): 49 | def __init__(self, num_classes=751 ,reid=False): 50 | super(Net,self).__init__() 51 | # 3 128 64 52 | self.conv = nn.Sequential( 53 | nn.Conv2d(3,64,3,stride=1,padding=1), 54 | nn.BatchNorm2d(64), 55 | nn.ReLU(inplace=True), 56 | # nn.Conv2d(32,32,3,stride=1,padding=1), 57 | # nn.BatchNorm2d(32), 58 | # nn.ReLU(inplace=True), 59 | nn.MaxPool2d(3,2,padding=1), 60 | ) 61 | # 32 64 32 62 | self.layer1 = make_layers(64,64,2,False) 63 | # 32 64 32 64 | self.layer2 = make_layers(64,128,2,True) 65 | # 64 32 16 66 | self.layer3 = make_layers(128,256,2,True) 67 | # 128 16 8 68 | self.layer4 = make_layers(256,512,2,True) 69 | # 256 8 4 70 | self.avgpool = nn.AvgPool2d((8,4),1) 71 | # 256 1 1 72 | self.reid = reid 73 | self.classifier = nn.Sequential( 74 | nn.Linear(512, 256), 75 | nn.BatchNorm1d(256), 76 | nn.ReLU(inplace=True), 77 | nn.Dropout(), 78 | nn.Linear(256, num_classes), 79 | ) 80 | 81 | def forward(self, x): 82 | x = self.conv(x) 83 | x = self.layer1(x) 84 | x = self.layer2(x) 85 | x = self.layer3(x) 86 | x = self.layer4(x) 87 | x = self.avgpool(x) 88 | x = x.view(x.size(0),-1) 89 | # B x 128 90 | if self.reid: 91 | x = x.div(x.norm(p=2,dim=1,keepdim=True)) 92 | return x 93 | # classifier 94 | x = self.classifier(x) 95 | return x 96 | 97 | 98 | if __name__ == '__main__': 99 | net = Net() 100 | x = torch.randn(4,3,128,64) 101 | y = net(x) 102 | import ipdb; ipdb.set_trace() 103 | 104 | 105 | -------------------------------------------------------------------------------- /deep_sort/deep/original_model.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | 5 | class BasicBlock(nn.Module): 6 | def __init__(self, c_in, c_out,is_downsample=False): 7 | super(BasicBlock,self).__init__() 8 | self.is_downsample = is_downsample 9 | if is_downsample: 10 | self.conv1 = nn.Conv2d(c_in, c_out, 3, stride=2, padding=1, bias=False) 11 | else: 12 | self.conv1 = nn.Conv2d(c_in, c_out, 3, stride=1, padding=1, bias=False) 13 | self.bn1 = nn.BatchNorm2d(c_out) 14 | self.relu = nn.ReLU(True) 15 | self.conv2 = nn.Conv2d(c_out,c_out,3,stride=1,padding=1, bias=False) 16 | self.bn2 = nn.BatchNorm2d(c_out) 17 | if is_downsample: 18 | self.downsample = nn.Sequential( 19 | nn.Conv2d(c_in, c_out, 1, stride=2, bias=False), 20 | nn.BatchNorm2d(c_out) 21 | ) 22 | elif c_in != c_out: 23 | self.downsample = nn.Sequential( 24 | nn.Conv2d(c_in, c_out, 1, stride=1, bias=False), 25 | nn.BatchNorm2d(c_out) 26 | ) 27 | self.is_downsample = True 28 | 29 | def forward(self,x): 30 | y = self.conv1(x) 31 | y = self.bn1(y) 32 | y = self.relu(y) 33 | y = self.conv2(y) 34 | y = self.bn2(y) 35 | if self.is_downsample: 36 | x = self.downsample(x) 37 | return F.relu(x.add(y),True) 38 | 39 | def make_layers(c_in,c_out,repeat_times, is_downsample=False): 40 | blocks = [] 41 | for i in range(repeat_times): 42 | if i ==0: 43 | blocks += [BasicBlock(c_in,c_out, is_downsample=is_downsample),] 44 | else: 45 | blocks += [BasicBlock(c_out,c_out),] 46 | return nn.Sequential(*blocks) 47 | 48 | class Net(nn.Module): 49 | def __init__(self, num_classes=625 ,reid=False): 50 | super(Net,self).__init__() 51 | # 3 128 64 52 | self.conv = nn.Sequential( 53 | nn.Conv2d(3,32,3,stride=1,padding=1), 54 | nn.BatchNorm2d(32), 55 | nn.ELU(inplace=True), 56 | nn.Conv2d(32,32,3,stride=1,padding=1), 57 | nn.BatchNorm2d(32), 58 | nn.ELU(inplace=True), 59 | nn.MaxPool2d(3,2,padding=1), 60 | ) 61 | # 32 64 32 62 | self.layer1 = make_layers(32,32,2,False) 63 | # 32 64 32 64 | self.layer2 = make_layers(32,64,2,True) 65 | # 64 32 16 66 | self.layer3 = make_layers(64,128,2,True) 67 | # 128 16 8 68 | self.dense = nn.Sequential( 69 | nn.Dropout(p=0.6), 70 | nn.Linear(128*16*8, 128), 71 | nn.BatchNorm1d(128), 72 | nn.ELU(inplace=True) 73 | ) 74 | # 256 1 1 75 | self.reid = reid 76 | self.batch_norm = nn.BatchNorm1d(128) 77 | self.classifier = nn.Sequential( 78 | nn.Linear(128, num_classes), 79 | ) 80 | 81 | def forward(self, x): 82 | x = self.conv(x) 83 | x = self.layer1(x) 84 | x = self.layer2(x) 85 | x = self.layer3(x) 86 | 87 | x = x.view(x.size(0),-1) 88 | if self.reid: 89 | x = self.dense[0](x) 90 | x = self.dense[1](x) 91 | x = x.div(x.norm(p=2,dim=1,keepdim=True)) 92 | return x 93 | x = self.dense(x) 94 | # B x 128 95 | # classifier 96 | x = self.classifier(x) 97 | return x 98 | 99 | 100 | if __name__ == '__main__': 101 | net = Net(reid=True) 102 | x = torch.randn(4,3,128,64) 103 | y = net(x) 104 | import ipdb; ipdb.set_trace() 105 | 106 | 107 | -------------------------------------------------------------------------------- /deep_sort/deep/test.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.backends.cudnn as cudnn 3 | import torchvision 4 | 5 | import argparse 6 | import os 7 | 8 | from model import Net 9 | 10 | parser = argparse.ArgumentParser(description="Train on market1501") 11 | parser.add_argument("--data-dir",default='data',type=str) 12 | parser.add_argument("--no-cuda",action="store_true") 13 | parser.add_argument("--gpu-id",default=0,type=int) 14 | args = parser.parse_args() 15 | 16 | # device 17 | device = "cuda:{}".format(args.gpu_id) if torch.cuda.is_available() and not args.no_cuda else "cpu" 18 | if torch.cuda.is_available() and not args.no_cuda: 19 | cudnn.benchmark = True 20 | 21 | # data loader 22 | root = args.data_dir 23 | query_dir = os.path.join(root,"query") 24 | gallery_dir = os.path.join(root,"gallery") 25 | transform = torchvision.transforms.Compose([ 26 | torchvision.transforms.Resize((128,64)), 27 | torchvision.transforms.ToTensor(), 28 | torchvision.transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) 29 | ]) 30 | queryloader = torch.utils.data.DataLoader( 31 | torchvision.datasets.ImageFolder(query_dir, transform=transform), 32 | batch_size=64, shuffle=False 33 | ) 34 | galleryloader = torch.utils.data.DataLoader( 35 | torchvision.datasets.ImageFolder(gallery_dir, transform=transform), 36 | batch_size=64, shuffle=False 37 | ) 38 | 39 | # net definition 40 | net = Net(reid=True) 41 | assert os.path.isfile("./checkpoint/ckpt.t7"), "Error: no checkpoint file found!" 42 | print('Loading from checkpoint/ckpt.t7') 43 | checkpoint = torch.load("./checkpoint/ckpt.t7") 44 | net_dict = checkpoint['net_dict'] 45 | net.load_state_dict(net_dict, strict=False) 46 | net.eval() 47 | net.to(device) 48 | 49 | # compute features 50 | query_features = torch.tensor([]).float() 51 | query_labels = torch.tensor([]).long() 52 | gallery_features = torch.tensor([]).float() 53 | gallery_labels = torch.tensor([]).long() 54 | 55 | with torch.no_grad(): 56 | for idx,(inputs,labels) in enumerate(queryloader): 57 | inputs = inputs.to(device) 58 | features = net(inputs).cpu() 59 | query_features = torch.cat((query_features, features), dim=0) 60 | query_labels = torch.cat((query_labels, labels)) 61 | 62 | for idx,(inputs,labels) in enumerate(galleryloader): 63 | inputs = inputs.to(device) 64 | features = net(inputs).cpu() 65 | gallery_features = torch.cat((gallery_features, features), dim=0) 66 | gallery_labels = torch.cat((gallery_labels, labels)) 67 | 68 | gallery_labels -= 2 69 | 70 | # save features 71 | features = { 72 | "qf": query_features, 73 | "ql": query_labels, 74 | "gf": gallery_features, 75 | "gl": gallery_labels 76 | } 77 | torch.save(features,"features.pth") -------------------------------------------------------------------------------- /deep_sort/sort/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/deep_sort/sort/__init__.py -------------------------------------------------------------------------------- /deep_sort/sort/detection.py: -------------------------------------------------------------------------------- 1 | # vim: expandtab:ts=4:sw=4 2 | import numpy as np 3 | 4 | 5 | class Detection(object): 6 | """ 7 | This class represents a bounding box detection in a single image. 8 | 9 | Parameters 10 | ---------- 11 | tlwh : array_like 12 | Bounding box in format `(x, y, w, h)`. 13 | confidence : float 14 | Detector confidence score. 15 | feature : array_like 16 | A feature vector that describes the object contained in this image. 17 | 18 | Attributes 19 | ---------- 20 | tlwh : ndarray 21 | Bounding box in format `(top left x, top left y, width, height)`. 22 | confidence : ndarray 23 | Detector confidence score. 24 | feature : ndarray | NoneType 25 | A feature vector that describes the object contained in this image. 26 | 27 | """ 28 | 29 | def __init__(self, tlwh, confidence, feature): 30 | self.tlwh = np.asarray(tlwh, dtype=np.float) 31 | self.confidence = float(confidence) 32 | self.feature = np.asarray(feature, dtype=np.float32) 33 | 34 | def to_tlbr(self): 35 | """Convert bounding box to format `(min x, min y, max x, max y)`, i.e., 36 | `(top left, bottom right)`. 37 | """ 38 | ret = self.tlwh.copy() 39 | ret[2:] += ret[:2] 40 | return ret 41 | 42 | def to_xyah(self): 43 | """Convert bounding box to format `(center x, center y, aspect ratio, 44 | height)`, where the aspect ratio is `width / height`. 45 | """ 46 | ret = self.tlwh.copy() 47 | ret[:2] += ret[2:] / 2 48 | ret[2] /= ret[3] 49 | return ret 50 | -------------------------------------------------------------------------------- /deep_sort/sort/iou_matching.py: -------------------------------------------------------------------------------- 1 | # vim: expandtab:ts=4:sw=4 2 | from __future__ import absolute_import 3 | import numpy as np 4 | from . import linear_assignment 5 | 6 | 7 | def iou(bbox, candidates): 8 | """Computer intersection over union. 9 | 10 | Parameters 11 | ---------- 12 | bbox : ndarray 13 | A bounding box in format `(top left x, top left y, width, height)`. 14 | candidates : ndarray 15 | A matrix of candidate bounding boxes (one per row) in the same format 16 | as `bbox`. 17 | 18 | Returns 19 | ------- 20 | ndarray 21 | The intersection over union in [0, 1] between the `bbox` and each 22 | candidate. A higher score means a larger fraction of the `bbox` is 23 | occluded by the candidate. 24 | 25 | """ 26 | bbox_tl, bbox_br = bbox[:2], bbox[:2] + bbox[2:] 27 | candidates_tl = candidates[:, :2] 28 | candidates_br = candidates[:, :2] + candidates[:, 2:] 29 | 30 | tl = np.c_[np.maximum(bbox_tl[0], candidates_tl[:, 0])[:, np.newaxis], 31 | np.maximum(bbox_tl[1], candidates_tl[:, 1])[:, np.newaxis]] 32 | br = np.c_[np.minimum(bbox_br[0], candidates_br[:, 0])[:, np.newaxis], 33 | np.minimum(bbox_br[1], candidates_br[:, 1])[:, np.newaxis]] 34 | wh = np.maximum(0., br - tl) 35 | 36 | area_intersection = wh.prod(axis=1) 37 | area_bbox = bbox[2:].prod() 38 | area_candidates = candidates[:, 2:].prod(axis=1) 39 | return area_intersection / (area_bbox + area_candidates - area_intersection) 40 | 41 | 42 | def iou_cost(tracks, detections, track_indices=None, 43 | detection_indices=None): 44 | """An intersection over union distance metric. 45 | 46 | Parameters 47 | ---------- 48 | tracks : List[deep_sort.track.Track] 49 | A list of tracks. 50 | detections : List[deep_sort.detection.Detection] 51 | A list of detections. 52 | track_indices : Optional[List[int]] 53 | A list of indices to tracks that should be matched. Defaults to 54 | all `tracks`. 55 | detection_indices : Optional[List[int]] 56 | A list of indices to detections that should be matched. Defaults 57 | to all `detections`. 58 | 59 | Returns 60 | ------- 61 | ndarray 62 | Returns a cost matrix of shape 63 | len(track_indices), len(detection_indices) where entry (i, j) is 64 | `1 - iou(tracks[track_indices[i]], detections[detection_indices[j]])`. 65 | 66 | """ 67 | if track_indices is None: 68 | track_indices = np.arange(len(tracks)) 69 | if detection_indices is None: 70 | detection_indices = np.arange(len(detections)) 71 | 72 | cost_matrix = np.zeros((len(track_indices), len(detection_indices))) 73 | for row, track_idx in enumerate(track_indices): 74 | if tracks[track_idx].time_since_update > 1: 75 | cost_matrix[row, :] = linear_assignment.INFTY_COST 76 | continue 77 | 78 | bbox = tracks[track_idx].to_tlwh() 79 | candidates = np.asarray([detections[i].tlwh for i in detection_indices]) 80 | cost_matrix[row, :] = 1. - iou(bbox, candidates) 81 | return cost_matrix 82 | -------------------------------------------------------------------------------- /deep_sort/sort/preprocessing.py: -------------------------------------------------------------------------------- 1 | # vim: expandtab:ts=4:sw=4 2 | import numpy as np 3 | import cv2 4 | 5 | 6 | def non_max_suppression(boxes, max_bbox_overlap, scores=None): 7 | """Suppress overlapping detections. 8 | 9 | Original code from [1]_ has been adapted to include confidence score. 10 | 11 | .. [1] http://www.pyimagesearch.com/2015/02/16/ 12 | faster-non-maximum-suppression-python/ 13 | 14 | Examples 15 | -------- 16 | 17 | >>> boxes = [d.roi for d in detections] 18 | >>> scores = [d.confidence for d in detections] 19 | >>> indices = non_max_suppression(boxes, max_bbox_overlap, scores) 20 | >>> detections = [detections[i] for i in indices] 21 | 22 | Parameters 23 | ---------- 24 | boxes : ndarray 25 | Array of ROIs (x, y, width, height). 26 | max_bbox_overlap : float 27 | ROIs that overlap more than this values are suppressed. 28 | scores : Optional[array_like] 29 | Detector confidence score. 30 | 31 | Returns 32 | ------- 33 | List[int] 34 | Returns indices of detections that have survived non-maxima suppression. 35 | 36 | """ 37 | if len(boxes) == 0: 38 | return [] 39 | 40 | boxes = boxes.astype(np.float) 41 | pick = [] 42 | 43 | x1 = boxes[:, 0] 44 | y1 = boxes[:, 1] 45 | x2 = boxes[:, 2] + boxes[:, 0] 46 | y2 = boxes[:, 3] + boxes[:, 1] 47 | 48 | area = (x2 - x1 + 1) * (y2 - y1 + 1) 49 | if scores is not None: 50 | idxs = np.argsort(scores) 51 | else: 52 | idxs = np.argsort(y2) 53 | 54 | while len(idxs) > 0: 55 | last = len(idxs) - 1 56 | i = idxs[last] 57 | pick.append(i) 58 | 59 | xx1 = np.maximum(x1[i], x1[idxs[:last]]) 60 | yy1 = np.maximum(y1[i], y1[idxs[:last]]) 61 | xx2 = np.minimum(x2[i], x2[idxs[:last]]) 62 | yy2 = np.minimum(y2[i], y2[idxs[:last]]) 63 | 64 | w = np.maximum(0, xx2 - xx1 + 1) 65 | h = np.maximum(0, yy2 - yy1 + 1) 66 | 67 | overlap = (w * h) / area[idxs[:last]] 68 | 69 | idxs = np.delete( 70 | idxs, np.concatenate( 71 | ([last], np.where(overlap > max_bbox_overlap)[0]))) 72 | 73 | return pick 74 | -------------------------------------------------------------------------------- /fast_reid/demo/README.md: -------------------------------------------------------------------------------- 1 | # FastReID Demo 2 | 3 | We provide a command line tool to run a simple demo of builtin models. 4 | 5 | You can run this command to get cosine similarites between different images 6 | 7 | ```bash 8 | cd demo/ 9 | sh run_demo.sh 10 | ``` -------------------------------------------------------------------------------- /fast_reid/demo/plot_roc_with_pickle.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import matplotlib.pyplot as plt 8 | import sys 9 | 10 | sys.path.append('.') 11 | from fastreid.utils.visualizer import Visualizer 12 | 13 | if __name__ == "__main__": 14 | baseline_res = Visualizer.load_roc_info("logs/duke_vis/roc_info.pickle") 15 | mgn_res = Visualizer.load_roc_info("logs/mgn_duke_vis/roc_info.pickle") 16 | 17 | fig = Visualizer.plot_roc_curve(baseline_res['fpr'], baseline_res['tpr'], name='baseline') 18 | Visualizer.plot_roc_curve(mgn_res['fpr'], mgn_res['tpr'], name='mgn', fig=fig) 19 | plt.savefig('roc.jpg') 20 | 21 | fig = Visualizer.plot_distribution(baseline_res['pos'], baseline_res['neg'], name='baseline') 22 | Visualizer.plot_distribution(mgn_res['pos'], mgn_res['neg'], name='mgn', fig=fig) 23 | plt.savefig('dist.jpg') 24 | -------------------------------------------------------------------------------- /fast_reid/demo/run_demo.sh: -------------------------------------------------------------------------------- 1 | python demo/visualize_result.py --config-file logs/dukemtmc/mgn_R50-ibn/config.yaml \ 2 | --parallel --vis-label --dataset-name 'DukeMTMC' --output logs/mgn_duke_vis \ 3 | --opts MODEL.WEIGHTS logs/dukemtmc/mgn_R50-ibn/model_final.pth 4 | -------------------------------------------------------------------------------- /fast_reid/fastreid/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | 8 | __version__ = "0.2.0" 9 | -------------------------------------------------------------------------------- /fast_reid/fastreid/config/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: l1aoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .config import CfgNode, get_cfg 8 | from .defaults import _C as cfg 9 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .build import build_reid_train_loader, build_reid_test_loader 8 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/common.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from torch.utils.data import Dataset 8 | 9 | from .data_utils import read_image 10 | 11 | 12 | class CommDataset(Dataset): 13 | """Image Person ReID Dataset""" 14 | 15 | def __init__(self, img_items, transform=None, relabel=True): 16 | self.img_items = img_items 17 | self.transform = transform 18 | self.relabel = relabel 19 | 20 | pid_set = set() 21 | cam_set = set() 22 | for i in img_items: 23 | pid_set.add(i[1]) 24 | cam_set.add(i[2]) 25 | 26 | self.pids = sorted(list(pid_set)) 27 | self.cams = sorted(list(cam_set)) 28 | if relabel: 29 | self.pid_dict = dict([(p, i) for i, p in enumerate(self.pids)]) 30 | self.cam_dict = dict([(p, i) for i, p in enumerate(self.cams)]) 31 | 32 | def __len__(self): 33 | return len(self.img_items) 34 | 35 | def __getitem__(self, index): 36 | img_item = self.img_items[index] 37 | img_path = img_item[0] 38 | pid = img_item[1] 39 | camid = img_item[2] 40 | img = read_image(img_path) 41 | if self.transform is not None: img = self.transform(img) 42 | if self.relabel: 43 | pid = self.pid_dict[pid] 44 | camid = self.cam_dict[camid] 45 | return { 46 | "images": img, 47 | "targets": pid, 48 | "camids": camid, 49 | "img_paths": img_path, 50 | } 51 | 52 | @property 53 | def num_classes(self): 54 | return len(self.pids) 55 | 56 | @property 57 | def num_cameras(self): 58 | return len(self.cams) 59 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/data_utils.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | import numpy as np 7 | from PIL import Image, ImageOps 8 | 9 | from fastreid.utils.file_io import PathManager 10 | 11 | 12 | def read_image(file_name, format=None): 13 | """ 14 | Read an image into the given format. 15 | Will apply rotation and flipping if the image has such exif information. 16 | Args: 17 | file_name (str): image file path 18 | format (str): one of the supported image modes in PIL, or "BGR" 19 | Returns: 20 | image (np.ndarray): an HWC image 21 | """ 22 | with PathManager.open(file_name, "rb") as f: 23 | image = Image.open(f) 24 | 25 | # work around this bug: https://github.com/python-pillow/Pillow/issues/3973 26 | try: 27 | image = ImageOps.exif_transpose(image) 28 | except Exception: 29 | pass 30 | 31 | if format is not None: 32 | # PIL only supports RGB, so convert to RGB and flip channels over below 33 | conversion_format = format 34 | if format == "BGR": 35 | conversion_format = "RGB" 36 | image = image.convert(conversion_format) 37 | image = np.asarray(image) 38 | 39 | # PIL squeezes out the channel dimension for "L", so make it HWC 40 | if format == "L": 41 | image = np.expand_dims(image, -1) 42 | 43 | # handle formats not supported by PIL 44 | elif format == "BGR": 45 | # flip channels if needed 46 | image = image[:, :, ::-1] 47 | 48 | # handle grayscale mixed in RGB images 49 | elif len(image.shape) == 2: 50 | image = np.repeat(image[..., np.newaxis], 3, axis=-1) 51 | 52 | image = Image.fromarray(image) 53 | 54 | return image 55 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/AirportALERT.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os 8 | 9 | from fastreid.data.datasets import DATASET_REGISTRY 10 | from fastreid.data.datasets.bases import ImageDataset 11 | 12 | __all__ = ['AirportALERT', ] 13 | 14 | 15 | @DATASET_REGISTRY.register() 16 | class AirportALERT(ImageDataset): 17 | dataset_dir = "AirportALERT" 18 | dataset_name = "airport" 19 | 20 | def __init__(self, root='datasets', **kwargs): 21 | self.root = root 22 | self.train_path = os.path.join(self.root, self.dataset_dir) 23 | self.train_file = os.path.join(self.root, self.dataset_dir, 'filepath.txt') 24 | 25 | required_files = [self.train_file, self.train_path] 26 | self.check_before_run(required_files) 27 | 28 | train = self.process_train(self.train_path, self.train_file) 29 | 30 | super().__init__(train, [], [], **kwargs) 31 | 32 | def process_train(self, dir_path, train_file): 33 | data = [] 34 | with open(train_file, "r") as f: 35 | img_paths = [line.strip('\n') for line in f.readlines()] 36 | 37 | for path in img_paths: 38 | split_path = path.split('\\') 39 | img_path = '/'.join(split_path) 40 | camid = self.dataset_name + "_" + split_path[0] 41 | pid = self.dataset_name + "_" + split_path[1] 42 | img_path = os.path.join(dir_path, img_path) 43 | # if 11001 <= int(split_path[1]) <= 401999: 44 | if 11001 <= int(split_path[1]): 45 | data.append([img_path, pid, camid]) 46 | 47 | return data 48 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from ...utils.registry import Registry 8 | 9 | DATASET_REGISTRY = Registry("DATASET") 10 | DATASET_REGISTRY.__doc__ = """ 11 | Registry for datasets 12 | It must returns an instance of :class:`Backbone`. 13 | """ 14 | 15 | # Person re-id datasets 16 | from .cuhk03 import CUHK03 17 | from .dukemtmcreid import DukeMTMC 18 | from .market1501 import Market1501 19 | from .msmt17 import MSMT17 20 | from .AirportALERT import AirportALERT 21 | from .iLIDS import iLIDS 22 | from .pku import PKU 23 | from .prai import PRAI 24 | from .saivt import SAIVT 25 | from .sensereid import SenseReID 26 | from .sysu_mm import SYSU_mm 27 | from .thermalworld import Thermalworld 28 | from .pes3d import PeS3D 29 | from .caviara import CAVIARa 30 | from .viper import VIPeR 31 | from .lpw import LPW 32 | from .shinpuhkan import Shinpuhkan 33 | from .wildtracker import WildTrackCrop 34 | from .cuhk_sysu import cuhkSYSU 35 | 36 | # Vehicle re-id datasets 37 | from .veri import VeRi 38 | from .vehicleid import VehicleID, SmallVehicleID, MediumVehicleID, LargeVehicleID 39 | from .veriwild import VeRiWild, SmallVeRiWild, MediumVeRiWild, LargeVeRiWild 40 | 41 | 42 | __all__ = [k for k in globals().keys() if "builtin" not in k and not k.startswith("_")] 43 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/caviara.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os 8 | from scipy.io import loadmat 9 | from glob import glob 10 | 11 | from fastreid.data.datasets import DATASET_REGISTRY 12 | from fastreid.data.datasets.bases import ImageDataset 13 | import pdb 14 | import random 15 | import numpy as np 16 | 17 | __all__ = ['CAVIARa',] 18 | 19 | 20 | @DATASET_REGISTRY.register() 21 | class CAVIARa(ImageDataset): 22 | dataset_dir = "CAVIARa" 23 | dataset_name = "caviara" 24 | 25 | def __init__(self, root='datasets', **kwargs): 26 | self.root = root 27 | self.train_path = os.path.join(self.root, self.dataset_dir) 28 | 29 | required_files = [self.train_path] 30 | self.check_before_run(required_files) 31 | 32 | train = self.process_train(self.train_path) 33 | 34 | super().__init__(train, [], [], **kwargs) 35 | 36 | def process_train(self, train_path): 37 | data = [] 38 | 39 | img_list = glob(os.path.join(train_path, "*.jpg")) 40 | for img_path in img_list: 41 | img_name = img_path.split('/')[-1] 42 | pid = self.dataset_name + "_" + img_name[:4] 43 | camid = self.dataset_name + "_cam0" 44 | data.append([img_path, pid, camid]) 45 | 46 | return data 47 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/cuhk_sysu.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import glob 8 | import os.path as osp 9 | import re 10 | import warnings 11 | 12 | from .bases import ImageDataset 13 | from ..datasets import DATASET_REGISTRY 14 | 15 | 16 | @DATASET_REGISTRY.register() 17 | class cuhkSYSU(ImageDataset): 18 | r"""CUHK SYSU datasets. 19 | 20 | The dataset is collected from two sources: street snap and movie. 21 | In street snap, 12,490 images and 6,057 query persons were collected 22 | with movable cameras across hundreds of scenes while 5,694 images and 23 | 2,375 query persons were selected from movies and TV dramas. 24 | 25 | Dataset statistics: 26 | - identities: xxx. 27 | - images: 12936 (train). 28 | """ 29 | dataset_dir = 'cuhk_sysu' 30 | dataset_name = "cuhksysu" 31 | 32 | def __init__(self, root='datasets', **kwargs): 33 | self.root = root 34 | self.dataset_dir = osp.join(self.root, self.dataset_dir) 35 | 36 | self.data_dir = osp.join(self.dataset_dir, "cropped_images") 37 | 38 | required_files = [self.data_dir] 39 | self.check_before_run(required_files) 40 | 41 | train = self.process_dir(self.data_dir) 42 | query = [] 43 | gallery = [] 44 | 45 | super(cuhkSYSU, self).__init__(train, query, gallery, **kwargs) 46 | 47 | def process_dir(self, dir_path): 48 | img_paths = glob.glob(osp.join(dir_path, '*.jpg')) 49 | pattern = re.compile(r'p([-\d]+)_s(\d)') 50 | 51 | data = [] 52 | for img_path in img_paths: 53 | pid, _ = map(int, pattern.search(img_path).groups()) 54 | pid = self.dataset_name + "_" + str(pid) 55 | camid = self.dataset_name + "_0" 56 | data.append((img_path, pid, camid)) 57 | 58 | return data 59 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/dukemtmcreid.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: liaoxingyu2@jd.com 5 | """ 6 | 7 | import glob 8 | import os.path as osp 9 | import re 10 | 11 | from .bases import ImageDataset 12 | from ..datasets import DATASET_REGISTRY 13 | 14 | 15 | @DATASET_REGISTRY.register() 16 | class DukeMTMC(ImageDataset): 17 | """DukeMTMC-reID. 18 | 19 | Reference: 20 | - Ristani et al. Performance Measures and a Data Set for Multi-Target, Multi-Camera Tracking. ECCVW 2016. 21 | - Zheng et al. Unlabeled Samples Generated by GAN Improve the Person Re-identification Baseline in vitro. ICCV 2017. 22 | 23 | URL: ``_ 24 | 25 | Dataset statistics: 26 | - identities: 1404 (train + query). 27 | - images:16522 (train) + 2228 (query) + 17661 (gallery). 28 | - cameras: 8. 29 | """ 30 | dataset_dir = 'DukeMTMC-reID' 31 | dataset_url = 'http://vision.cs.duke.edu/DukeMTMC/data/misc/DukeMTMC-reID.zip' 32 | dataset_name = "dukemtmc" 33 | 34 | def __init__(self, root='datasets', **kwargs): 35 | # self.root = osp.abspath(osp.expanduser(root)) 36 | self.root = root 37 | self.dataset_dir = osp.join(self.root, self.dataset_dir) 38 | self.train_dir = osp.join(self.dataset_dir, 'bounding_box_train') 39 | self.query_dir = osp.join(self.dataset_dir, 'query') 40 | self.gallery_dir = osp.join(self.dataset_dir, 'bounding_box_test') 41 | 42 | required_files = [ 43 | self.dataset_dir, 44 | self.train_dir, 45 | self.query_dir, 46 | self.gallery_dir, 47 | ] 48 | self.check_before_run(required_files) 49 | 50 | train = self.process_dir(self.train_dir) 51 | query = self.process_dir(self.query_dir, is_train=False) 52 | gallery = self.process_dir(self.gallery_dir, is_train=False) 53 | 54 | super(DukeMTMC, self).__init__(train, query, gallery, **kwargs) 55 | 56 | def process_dir(self, dir_path, is_train=True): 57 | img_paths = glob.glob(osp.join(dir_path, '*.jpg')) 58 | pattern = re.compile(r'([-\d]+)_c(\d)') 59 | 60 | data = [] 61 | for img_path in img_paths: 62 | pid, camid = map(int, pattern.search(img_path).groups()) 63 | assert 1 <= camid <= 8 64 | camid -= 1 # index starts from 0 65 | if is_train: 66 | pid = self.dataset_name + "_" + str(pid) 67 | camid = self.dataset_name + "_" + str(camid) 68 | data.append((img_path, pid, camid)) 69 | 70 | return data 71 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/iLIDS.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os 8 | from glob import glob 9 | 10 | from fastreid.data.datasets import DATASET_REGISTRY 11 | from fastreid.data.datasets.bases import ImageDataset 12 | 13 | __all__ = ['iLIDS', ] 14 | 15 | 16 | @DATASET_REGISTRY.register() 17 | class iLIDS(ImageDataset): 18 | dataset_dir = "iLIDS" 19 | dataset_name = "ilids" 20 | 21 | def __init__(self, root='datasets', **kwargs): 22 | self.root = root 23 | self.train_path = os.path.join(self.root, self.dataset_dir) 24 | 25 | required_files = [self.train_path] 26 | self.check_before_run(required_files) 27 | 28 | train = self.process_train(self.train_path) 29 | 30 | super().__init__(train, [], [], **kwargs) 31 | 32 | def process_train(self, train_path): 33 | data = [] 34 | file_path = os.listdir(train_path) 35 | for pid_dir in file_path: 36 | img_file = os.path.join(train_path, pid_dir) 37 | img_paths = glob(os.path.join(img_file, "*.png")) 38 | for img_path in img_paths: 39 | split_path = img_path.split('/') 40 | pid = self.dataset_name + "_" + split_path[-2] 41 | camid = self.dataset_name + "_" + split_path[-1].split('_')[0] 42 | data.append([img_path, pid, camid]) 43 | return data 44 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/lpw.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os 8 | from glob import glob 9 | 10 | from fastreid.data.datasets import DATASET_REGISTRY 11 | from fastreid.data.datasets.bases import ImageDataset 12 | 13 | __all__ = ['LPW', ] 14 | 15 | 16 | @DATASET_REGISTRY.register() 17 | class LPW(ImageDataset): 18 | dataset_dir = "pep_256x128" 19 | dataset_name = "lpw" 20 | 21 | def __init__(self, root='datasets', **kwargs): 22 | self.root = root 23 | self.train_path = os.path.join(self.root, self.dataset_dir) 24 | 25 | required_files = [self.train_path] 26 | self.check_before_run(required_files) 27 | 28 | train = self.process_train(self.train_path) 29 | 30 | super().__init__(train, [], [], **kwargs) 31 | 32 | def process_train(self, train_path): 33 | data = [] 34 | 35 | file_path_list = ['scen1', 'scen2', 'scen3'] 36 | 37 | for scene in file_path_list: 38 | cam_list = os.listdir(os.path.join(train_path, scene)) 39 | for cam in cam_list: 40 | camid = self.dataset_name + "_" + cam 41 | pid_list = os.listdir(os.path.join(train_path, scene, cam)) 42 | for pid_dir in pid_list: 43 | img_paths = glob(os.path.join(train_path, scene, cam, pid_dir, "*.jpg")) 44 | for img_path in img_paths: 45 | pid = self.dataset_name + "_" + scene + "-" + pid_dir 46 | data.append([img_path, pid, camid]) 47 | return data 48 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/market1501.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import glob 8 | import os.path as osp 9 | import re 10 | import warnings 11 | 12 | from .bases import ImageDataset 13 | from ..datasets import DATASET_REGISTRY 14 | 15 | 16 | @DATASET_REGISTRY.register() 17 | class Market1501(ImageDataset): 18 | """Market1501. 19 | 20 | Reference: 21 | Zheng et al. Scalable Person Re-identification: A Benchmark. ICCV 2015. 22 | 23 | URL: ``_ 24 | 25 | Dataset statistics: 26 | - identities: 1501 (+1 for background). 27 | - images: 12936 (train) + 3368 (query) + 15913 (gallery). 28 | """ 29 | _junk_pids = [0, -1] 30 | dataset_dir = '' 31 | dataset_url = 'http://188.138.127.15:81/Datasets/Market-1501-v15.09.15.zip' 32 | dataset_name = "market1501" 33 | 34 | def __init__(self, root='datasets', market1501_500k=False, **kwargs): 35 | # self.root = osp.abspath(osp.expanduser(root)) 36 | self.root = root 37 | self.dataset_dir = osp.join(self.root, self.dataset_dir) 38 | 39 | # allow alternative directory structure 40 | self.data_dir = self.dataset_dir 41 | data_dir = osp.join(self.data_dir, 'Market-1501-v15.09.15') 42 | if osp.isdir(data_dir): 43 | self.data_dir = data_dir 44 | else: 45 | warnings.warn('The current data structure is deprecated. Please ' 46 | 'put data folders such as "bounding_box_train" under ' 47 | '"Market-1501-v15.09.15".') 48 | 49 | self.train_dir = osp.join(self.data_dir, 'bounding_box_train') 50 | self.query_dir = osp.join(self.data_dir, 'query') 51 | self.gallery_dir = osp.join(self.data_dir, 'bounding_box_test') 52 | self.extra_gallery_dir = osp.join(self.data_dir, 'images') 53 | self.market1501_500k = market1501_500k 54 | 55 | required_files = [ 56 | self.data_dir, 57 | self.train_dir, 58 | self.query_dir, 59 | self.gallery_dir, 60 | ] 61 | if self.market1501_500k: 62 | required_files.append(self.extra_gallery_dir) 63 | self.check_before_run(required_files) 64 | 65 | train = self.process_dir(self.train_dir) 66 | query = self.process_dir(self.query_dir, is_train=False) 67 | gallery = self.process_dir(self.gallery_dir, is_train=False) 68 | if self.market1501_500k: 69 | gallery += self.process_dir(self.extra_gallery_dir, is_train=False) 70 | 71 | super(Market1501, self).__init__(train, query, gallery, **kwargs) 72 | 73 | def process_dir(self, dir_path, is_train=True): 74 | img_paths = glob.glob(osp.join(dir_path, '*.jpg')) 75 | pattern = re.compile(r'([-\d]+)_c(\d)') 76 | 77 | data = [] 78 | for img_path in img_paths: 79 | pid, camid = map(int, pattern.search(img_path).groups()) 80 | if pid == -1: 81 | continue # junk images are just ignored 82 | assert 0 <= pid <= 1501 # pid == 0 means background 83 | assert 1 <= camid <= 6 84 | camid -= 1 # index starts from 0 85 | if is_train: 86 | pid = self.dataset_name + "_" + str(pid) 87 | camid = self.dataset_name + "_" + str(camid) 88 | data.append((img_path, pid, camid)) 89 | 90 | return data 91 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/pes3d.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os 8 | from glob import glob 9 | 10 | from fastreid.data.datasets import DATASET_REGISTRY 11 | from fastreid.data.datasets.bases import ImageDataset 12 | 13 | __all__ = ['PeS3D',] 14 | 15 | 16 | @DATASET_REGISTRY.register() 17 | class PeS3D(ImageDataset): 18 | dataset_dir = "3DPeS" 19 | dataset_name = "pes3d" 20 | 21 | def __init__(self, root='datasets', **kwargs): 22 | self.root = root 23 | self.train_path = os.path.join(self.root, self.dataset_dir) 24 | 25 | required_files = [self.train_path] 26 | self.check_before_run(required_files) 27 | 28 | train = self.process_train(self.train_path) 29 | 30 | super().__init__(train, [], [], **kwargs) 31 | 32 | def process_train(self, train_path): 33 | data = [] 34 | 35 | pid_list = os.listdir(train_path) 36 | for pid_dir in pid_list: 37 | pid = self.dataset_name + "_" + pid_dir 38 | img_list = glob(os.path.join(train_path, pid_dir, "*.bmp")) 39 | for img_path in img_list: 40 | camid = self.dataset_name + "_cam0" 41 | data.append([img_path, pid, camid]) 42 | return data 43 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/pku.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os 8 | from glob import glob 9 | 10 | from fastreid.data.datasets import DATASET_REGISTRY 11 | from fastreid.data.datasets.bases import ImageDataset 12 | 13 | __all__ = ['PKU', ] 14 | 15 | 16 | @DATASET_REGISTRY.register() 17 | class PKU(ImageDataset): 18 | dataset_dir = "PKUv1a_128x48" 19 | dataset_name = 'pku' 20 | 21 | def __init__(self, root='datasets', **kwargs): 22 | self.root = root 23 | self.train_path = os.path.join(self.root, self.dataset_dir) 24 | 25 | required_files = [self.train_path] 26 | self.check_before_run(required_files) 27 | 28 | train = self.process_train(self.train_path) 29 | 30 | super().__init__(train, [], [], **kwargs) 31 | 32 | def process_train(self, train_path): 33 | data = [] 34 | img_paths = glob(os.path.join(train_path, "*.png")) 35 | 36 | for img_path in img_paths: 37 | split_path = img_path.split('/') 38 | img_info = split_path[-1].split('_') 39 | pid = self.dataset_name + "_" + img_info[0] 40 | camid = self.dataset_name + "_" + img_info[1] 41 | data.append([img_path, pid, camid]) 42 | return data 43 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/prai.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os 8 | from scipy.io import loadmat 9 | from glob import glob 10 | 11 | from fastreid.data.datasets import DATASET_REGISTRY 12 | from fastreid.data.datasets.bases import ImageDataset 13 | import pdb 14 | 15 | __all__ = ['PRAI',] 16 | 17 | 18 | @DATASET_REGISTRY.register() 19 | class PRAI(ImageDataset): 20 | dataset_dir = "PRAI-1581" 21 | dataset_name = 'prai' 22 | 23 | def __init__(self, root='datasets', **kwargs): 24 | self.root = root 25 | self.train_path = os.path.join(self.root, self.dataset_dir, 'images') 26 | 27 | required_files = [self.train_path] 28 | self.check_before_run(required_files) 29 | 30 | train = self.process_train(self.train_path) 31 | 32 | super().__init__(train, [], [], **kwargs) 33 | 34 | def process_train(self, train_path): 35 | data = [] 36 | img_paths = glob(os.path.join(train_path, "*.jpg")) 37 | for img_path in img_paths: 38 | split_path = img_path.split('/') 39 | img_info = split_path[-1].split('_') 40 | pid = self.dataset_name + "_" + img_info[0] 41 | camid = self.dataset_name + "_" + img_info[1] 42 | data.append([img_path, pid, camid]) 43 | return data 44 | 45 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/saivt.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os 8 | from glob import glob 9 | 10 | from fastreid.data.datasets import DATASET_REGISTRY 11 | from fastreid.data.datasets.bases import ImageDataset 12 | 13 | __all__ = ['SAIVT', ] 14 | 15 | 16 | @DATASET_REGISTRY.register() 17 | class SAIVT(ImageDataset): 18 | dataset_dir = "SAIVT-SoftBio" 19 | dataset_name = "saivt" 20 | 21 | def __init__(self, root='datasets', **kwargs): 22 | self.root = root 23 | self.train_path = os.path.join(self.root, self.dataset_dir) 24 | 25 | required_files = [self.train_path] 26 | self.check_before_run(required_files) 27 | 28 | train = self.process_train(self.train_path) 29 | 30 | super().__init__(train, [], [], **kwargs) 31 | 32 | def process_train(self, train_path): 33 | data = [] 34 | 35 | pid_path = os.path.join(train_path, "cropped_images") 36 | pid_list = os.listdir(pid_path) 37 | 38 | for pid_name in pid_list: 39 | pid = self.dataset_name + '_' + pid_name 40 | img_list = glob(os.path.join(pid_path, pid_name, "*.jpeg")) 41 | for img_path in img_list: 42 | img_name = os.path.basename(img_path) 43 | camid = self.dataset_name + '_' + img_name.split('-')[2] 44 | data.append([img_path, pid, camid]) 45 | return data 46 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/sensereid.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os 8 | from glob import glob 9 | 10 | from fastreid.data.datasets import DATASET_REGISTRY 11 | from fastreid.data.datasets.bases import ImageDataset 12 | 13 | __all__ = ['SenseReID', ] 14 | 15 | 16 | @DATASET_REGISTRY.register() 17 | class SenseReID(ImageDataset): 18 | dataset_dir = "SenseReID" 19 | dataset_name = "senseid" 20 | 21 | def __init__(self, root='datasets', **kwargs): 22 | self.root = root 23 | self.train_path = os.path.join(self.root, self.dataset_dir) 24 | 25 | required_files = [self.train_path] 26 | self.check_before_run(required_files) 27 | 28 | train = self.process_train(self.train_path) 29 | 30 | super().__init__(train, [], [], **kwargs) 31 | 32 | def process_train(self, train_path): 33 | data = [] 34 | file_path_list = ['test_gallery', 'test_prob'] 35 | 36 | for file_path in file_path_list: 37 | sub_file = os.path.join(train_path, file_path) 38 | img_name = glob(os.path.join(sub_file, "*.jpg")) 39 | for img_path in img_name: 40 | img_name = img_path.split('/')[-1] 41 | img_info = img_name.split('_') 42 | pid = self.dataset_name + "_" + img_info[0] 43 | camid = self.dataset_name + "_" + img_info[1].split('.')[0] 44 | data.append([img_path, pid, camid]) 45 | return data 46 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/shinpuhkan.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os 8 | 9 | from fastreid.data.datasets import DATASET_REGISTRY 10 | from fastreid.data.datasets.bases import ImageDataset 11 | 12 | __all__ = ['Shinpuhkan', ] 13 | 14 | 15 | @DATASET_REGISTRY.register() 16 | class Shinpuhkan(ImageDataset): 17 | dataset_dir = "shinpuhkan" 18 | dataset_name = 'shinpuhkan' 19 | 20 | def __init__(self, root='datasets', **kwargs): 21 | self.root = root 22 | self.train_path = os.path.join(self.root, self.dataset_dir) 23 | 24 | required_files = [self.train_path] 25 | self.check_before_run(required_files) 26 | 27 | train = self.process_train(self.train_path) 28 | 29 | super().__init__(train, [], [], **kwargs) 30 | 31 | def process_train(self, train_path): 32 | data = [] 33 | 34 | for root, dirs, files in os.walk(train_path): 35 | img_names = list(filter(lambda x: x.endswith(".jpg"), files)) 36 | # fmt: off 37 | if len(img_names) == 0: continue 38 | # fmt: on 39 | for img_name in img_names: 40 | img_path = os.path.join(root, img_name) 41 | split_path = img_name.split('_') 42 | pid = self.dataset_name + "_" + split_path[0] 43 | camid = self.dataset_name + "_" + split_path[2] 44 | data.append((img_path, pid, camid)) 45 | 46 | return data 47 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/sysu_mm.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os 8 | from scipy.io import loadmat 9 | from glob import glob 10 | 11 | from fastreid.data.datasets import DATASET_REGISTRY 12 | from fastreid.data.datasets.bases import ImageDataset 13 | import pdb 14 | 15 | __all__ = ['SYSU_mm', ] 16 | 17 | 18 | @DATASET_REGISTRY.register() 19 | class SYSU_mm(ImageDataset): 20 | dataset_dir = "SYSU-MM01" 21 | dataset_name = "sysumm01" 22 | 23 | def __init__(self, root='datasets', **kwargs): 24 | self.root = root 25 | self.train_path = os.path.join(self.root, self.dataset_dir) 26 | 27 | required_files = [self.train_path] 28 | self.check_before_run(required_files) 29 | 30 | train = self.process_train(self.train_path) 31 | 32 | super().__init__(train, [], [], **kwargs) 33 | 34 | def process_train(self, train_path): 35 | data = [] 36 | 37 | file_path_list = ['cam1', 'cam2', 'cam4', 'cam5'] 38 | 39 | for file_path in file_path_list: 40 | camid = self.dataset_name + "_" + file_path 41 | pid_list = os.listdir(os.path.join(train_path, file_path)) 42 | for pid_dir in pid_list: 43 | pid = self.dataset_name + "_" + pid_dir 44 | img_list = glob(os.path.join(train_path, file_path, pid_dir, "*.jpg")) 45 | for img_path in img_list: 46 | data.append([img_path, pid, camid]) 47 | return data 48 | 49 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/thermalworld.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os 8 | from scipy.io import loadmat 9 | from glob import glob 10 | 11 | from fastreid.data.datasets import DATASET_REGISTRY 12 | from fastreid.data.datasets.bases import ImageDataset 13 | import pdb 14 | import random 15 | import numpy as np 16 | 17 | __all__ = ['Thermalworld',] 18 | 19 | 20 | @DATASET_REGISTRY.register() 21 | class Thermalworld(ImageDataset): 22 | dataset_dir = "thermalworld_rgb" 23 | dataset_name = "thermalworld" 24 | 25 | def __init__(self, root='datasets', **kwargs): 26 | self.root = root 27 | self.train_path = os.path.join(self.root, self.dataset_dir) 28 | 29 | required_files = [self.train_path] 30 | self.check_before_run(required_files) 31 | 32 | train = self.process_train(self.train_path) 33 | 34 | super().__init__(train, [], [], **kwargs) 35 | 36 | def process_train(self, train_path): 37 | data = [] 38 | pid_list = os.listdir(train_path) 39 | for pid_dir in pid_list: 40 | pid = self.dataset_name + "_" + pid_dir 41 | img_list = glob(os.path.join(train_path, pid_dir, "*.jpg")) 42 | for img_path in img_list: 43 | camid = self.dataset_name + "_cam0" 44 | data.append([img_path, pid, camid]) 45 | return data 46 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/veri.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: Jinkai Zheng 4 | @contact: 1315673509@qq.com 5 | """ 6 | 7 | import glob 8 | import os.path as osp 9 | import re 10 | 11 | from .bases import ImageDataset 12 | from ..datasets import DATASET_REGISTRY 13 | 14 | 15 | @DATASET_REGISTRY.register() 16 | class VeRi(ImageDataset): 17 | """VeRi. 18 | 19 | Reference: 20 | Xinchen Liu et al. A Deep Learning based Approach for Progressive Vehicle Re-Identification. ECCV 2016. 21 | Xinchen Liu et al. PROVID: Progressive and Multimodal Vehicle Reidentification for Large-Scale Urban Surveillance. IEEE TMM 2018. 22 | 23 | URL: ``_ 24 | 25 | Dataset statistics: 26 | - identities: 775. 27 | - images: 37778 (train) + 1678 (query) + 11579 (gallery). 28 | """ 29 | dataset_dir = "veri" 30 | dataset_name = "veri" 31 | 32 | def __init__(self, root='datasets', **kwargs): 33 | self.dataset_dir = osp.join(root, self.dataset_dir) 34 | 35 | self.train_dir = osp.join(self.dataset_dir, 'image_train') 36 | self.query_dir = osp.join(self.dataset_dir, 'image_query') 37 | self.gallery_dir = osp.join(self.dataset_dir, 'image_test') 38 | 39 | required_files = [ 40 | self.dataset_dir, 41 | self.train_dir, 42 | self.query_dir, 43 | self.gallery_dir, 44 | ] 45 | self.check_before_run(required_files) 46 | 47 | train = self.process_dir(self.train_dir) 48 | query = self.process_dir(self.query_dir, is_train=False) 49 | gallery = self.process_dir(self.gallery_dir, is_train=False) 50 | 51 | super(VeRi, self).__init__(train, query, gallery, **kwargs) 52 | 53 | def process_dir(self, dir_path, is_train=True): 54 | img_paths = glob.glob(osp.join(dir_path, '*.jpg')) 55 | pattern = re.compile(r'([\d]+)_c(\d\d\d)') 56 | 57 | data = [] 58 | for img_path in img_paths: 59 | pid, camid = map(int, pattern.search(img_path).groups()) 60 | if pid == -1: continue # junk images are just ignored 61 | assert 0 <= pid <= 776 62 | assert 1 <= camid <= 20 63 | camid -= 1 # index starts from 0 64 | if is_train: 65 | pid = self.dataset_name + "_" + str(pid) 66 | camid = self.dataset_name + "_" + str(camid) 67 | data.append((img_path, pid, camid)) 68 | 69 | return data 70 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/viper.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import os 8 | from glob import glob 9 | 10 | from fastreid.data.datasets import DATASET_REGISTRY 11 | from fastreid.data.datasets.bases import ImageDataset 12 | 13 | __all__ = ['VIPeR', ] 14 | 15 | 16 | @DATASET_REGISTRY.register() 17 | class VIPeR(ImageDataset): 18 | dataset_dir = "VIPeR" 19 | dataset_name = "viper" 20 | 21 | def __init__(self, root='datasets', **kwargs): 22 | self.root = root 23 | self.train_path = os.path.join(self.root, self.dataset_dir) 24 | 25 | required_files = [self.train_path] 26 | self.check_before_run(required_files) 27 | 28 | train = self.process_train(self.train_path) 29 | 30 | super().__init__(train, [], [], **kwargs) 31 | 32 | def process_train(self, train_path): 33 | data = [] 34 | 35 | file_path_list = ['cam_a', 'cam_b'] 36 | 37 | for file_path in file_path_list: 38 | camid = self.dataset_name + "_" + file_path 39 | img_list = glob(os.path.join(train_path, file_path, "*.bmp")) 40 | for img_path in img_list: 41 | img_name = img_path.split('/')[-1] 42 | pid = self.dataset_name + "_" + img_name.split('_')[0] 43 | data.append([img_path, pid, camid]) 44 | 45 | return data 46 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/datasets/wildtracker.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: wangguanan 4 | @contact: guan.wang0706@gmail.com 5 | """ 6 | 7 | import glob 8 | import os 9 | 10 | from .bases import ImageDataset 11 | from ..datasets import DATASET_REGISTRY 12 | 13 | 14 | @DATASET_REGISTRY.register() 15 | class WildTrackCrop(ImageDataset): 16 | """WildTrack. 17 | Reference: 18 | WILDTRACK: A Multi-camera HD Dataset for Dense Unscripted Pedestrian Detection 19 | T. Chavdarova; P. Baqué; A. Maksai; S. Bouquet; C. Jose et al. 20 | URL: ``_ 21 | Dataset statistics: 22 | - identities: 313 23 | - images: 33979 (train only) 24 | - cameras: 7 25 | Args: 26 | data_path(str): path to WildTrackCrop dataset 27 | combineall(bool): combine train and test sets as train set if True 28 | """ 29 | dataset_url = None 30 | dataset_dir = 'Wildtrack_crop_dataset' 31 | dataset_name = 'wildtrack' 32 | 33 | def __init__(self, root='datasets', **kwargs): 34 | self.root = root 35 | self.dataset_dir = os.path.join(self.root, self.dataset_dir) 36 | 37 | self.train_dir = os.path.join(self.dataset_dir, "crop") 38 | 39 | train = self.process_dir(self.train_dir) 40 | query = [] 41 | gallery = [] 42 | 43 | super(WildTrackCrop, self).__init__(train, query, gallery, **kwargs) 44 | 45 | def process_dir(self, dir_path): 46 | r""" 47 | :param dir_path: directory path saving images 48 | Returns 49 | data(list) = [img_path, pid, camid] 50 | """ 51 | data = [] 52 | for dir_name in os.listdir(dir_path): 53 | img_lists = glob.glob(os.path.join(dir_path, dir_name, "*.png")) 54 | for img_path in img_lists: 55 | pid = self.dataset_name + "_" + dir_name 56 | camid = img_path.split('/')[-1].split('_')[0] 57 | camid = self.dataset_name + "_" + camid 58 | data.append([img_path, pid, camid]) 59 | return data 60 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/samplers/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .triplet_sampler import BalancedIdentitySampler, NaiveIdentitySampler 8 | from .data_sampler import TrainingSampler, InferenceSampler 9 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/samplers/data_sampler.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: l1aoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | import itertools 7 | from typing import Optional 8 | 9 | import numpy as np 10 | from torch.utils.data import Sampler 11 | 12 | from fastreid.utils import comm 13 | 14 | 15 | class TrainingSampler(Sampler): 16 | """ 17 | In training, we only care about the "infinite stream" of training data. 18 | So this sampler produces an infinite stream of indices and 19 | all workers cooperate to correctly shuffle the indices and sample different indices. 20 | The samplers in each worker effectively produces `indices[worker_id::num_workers]` 21 | where `indices` is an infinite stream of indices consisting of 22 | `shuffle(range(size)) + shuffle(range(size)) + ...` (if shuffle is True) 23 | or `range(size) + range(size) + ...` (if shuffle is False) 24 | """ 25 | 26 | def __init__(self, size: int, shuffle: bool = True, seed: Optional[int] = None): 27 | """ 28 | Args: 29 | size (int): the total number of data of the underlying dataset to sample from 30 | shuffle (bool): whether to shuffle the indices or not 31 | seed (int): the initial seed of the shuffle. Must be the same 32 | across all workers. If None, will use a random seed shared 33 | among workers (require synchronization among all workers). 34 | """ 35 | self._size = size 36 | assert size > 0 37 | self._shuffle = shuffle 38 | if seed is None: 39 | seed = comm.shared_random_seed() 40 | self._seed = int(seed) 41 | 42 | self._rank = comm.get_rank() 43 | self._world_size = comm.get_world_size() 44 | 45 | def __iter__(self): 46 | start = self._rank 47 | yield from itertools.islice(self._infinite_indices(), start, None, self._world_size) 48 | 49 | def _infinite_indices(self): 50 | np.random.seed(self._seed) 51 | while True: 52 | if self._shuffle: 53 | yield from np.random.permutation(self._size) 54 | else: 55 | yield from np.arange(self._size) 56 | 57 | 58 | class InferenceSampler(Sampler): 59 | """ 60 | Produce indices for inference. 61 | Inference needs to run on the __exact__ set of samples, 62 | therefore when the total number of samples is not divisible by the number of workers, 63 | this sampler produces different number of samples on different workers. 64 | """ 65 | 66 | def __init__(self, size: int): 67 | """ 68 | Args: 69 | size (int): the total number of data of the underlying dataset to sample from 70 | """ 71 | self._size = size 72 | assert size > 0 73 | self._rank = comm.get_rank() 74 | self._world_size = comm.get_world_size() 75 | 76 | shard_size = (self._size - 1) // self._world_size + 1 77 | begin = shard_size * self._rank 78 | end = min(shard_size * (self._rank + 1), self._size) 79 | self._local_indices = range(begin, end) 80 | 81 | def __iter__(self): 82 | yield from self._local_indices 83 | 84 | def __len__(self): 85 | return len(self._local_indices) 86 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/transforms/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .autoaugment import * 8 | from .build import build_transforms 9 | from .transforms import * 10 | -------------------------------------------------------------------------------- /fast_reid/fastreid/data/transforms/build.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import torchvision.transforms as T 8 | 9 | from .transforms import * 10 | from .autoaugment import AutoAugment 11 | 12 | 13 | def build_transforms(cfg, is_train=True): 14 | res = [] 15 | 16 | if is_train: 17 | size_train = cfg.INPUT.SIZE_TRAIN 18 | 19 | # augmix augmentation 20 | do_augmix = cfg.INPUT.DO_AUGMIX 21 | augmix_prob = cfg.INPUT.AUGMIX_PROB 22 | 23 | # auto augmentation 24 | do_autoaug = cfg.INPUT.DO_AUTOAUG 25 | autoaug_prob = cfg.INPUT.AUTOAUG_PROB 26 | 27 | # horizontal filp 28 | do_flip = cfg.INPUT.DO_FLIP 29 | flip_prob = cfg.INPUT.FLIP_PROB 30 | 31 | # padding 32 | do_pad = cfg.INPUT.DO_PAD 33 | padding = cfg.INPUT.PADDING 34 | padding_mode = cfg.INPUT.PADDING_MODE 35 | 36 | # color jitter 37 | do_cj = cfg.INPUT.CJ.ENABLED 38 | cj_prob = cfg.INPUT.CJ.PROB 39 | cj_brightness = cfg.INPUT.CJ.BRIGHTNESS 40 | cj_contrast = cfg.INPUT.CJ.CONTRAST 41 | cj_saturation = cfg.INPUT.CJ.SATURATION 42 | cj_hue = cfg.INPUT.CJ.HUE 43 | 44 | # random affine 45 | do_affine = cfg.INPUT.DO_AFFINE 46 | 47 | # random erasing 48 | do_rea = cfg.INPUT.REA.ENABLED 49 | rea_prob = cfg.INPUT.REA.PROB 50 | rea_value = cfg.INPUT.REA.VALUE 51 | 52 | # random patch 53 | do_rpt = cfg.INPUT.RPT.ENABLED 54 | rpt_prob = cfg.INPUT.RPT.PROB 55 | 56 | if do_autoaug: 57 | res.append(T.RandomApply([AutoAugment()], p=autoaug_prob)) 58 | 59 | res.append(T.Resize(size_train, interpolation=3)) 60 | if do_flip: 61 | res.append(T.RandomHorizontalFlip(p=flip_prob)) 62 | if do_pad: 63 | res.extend([T.Pad(padding, padding_mode=padding_mode), T.RandomCrop(size_train)]) 64 | if do_cj: 65 | res.append(T.RandomApply([T.ColorJitter(cj_brightness, cj_contrast, cj_saturation, cj_hue)], p=cj_prob)) 66 | if do_affine: 67 | res.append(T.RandomAffine(degrees=0, translate=None, scale=[0.9, 1.1], shear=None, resample=False, 68 | fillcolor=128)) 69 | if do_augmix: 70 | res.append(AugMix(prob=augmix_prob)) 71 | res.append(ToTensor()) 72 | if do_rea: 73 | res.append(T.RandomErasing(p=rea_prob, value=rea_value)) 74 | if do_rpt: 75 | res.append(RandomPatch(prob_happen=rpt_prob)) 76 | else: 77 | size_test = cfg.INPUT.SIZE_TEST 78 | res.append(T.Resize(size_test, interpolation=3)) 79 | res.append(ToTensor()) 80 | return T.Compose(res) 81 | -------------------------------------------------------------------------------- /fast_reid/fastreid/engine/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | from .train_loop import * 7 | 8 | __all__ = [k for k in globals().keys() if not k.startswith("_")] 9 | 10 | 11 | # prefer to let hooks and defaults live in separate namespaces (therefore not in __all__) 12 | # but still make them available here 13 | from .hooks import * 14 | from .defaults import * 15 | from .launch import * 16 | -------------------------------------------------------------------------------- /fast_reid/fastreid/evaluation/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved 2 | from .evaluator import DatasetEvaluator, inference_context, inference_on_dataset 3 | from .rank import evaluate_rank 4 | from .roc import evaluate_roc 5 | from .reid_evaluation import ReidEvaluator 6 | from .testing import print_csv_format, verify_results 7 | 8 | __all__ = [k for k in globals().keys() if not k.startswith("_")] 9 | -------------------------------------------------------------------------------- /fast_reid/fastreid/evaluation/query_expansion.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | # based on 8 | # https://github.com/PyRetri/PyRetri/blob/master/pyretri/index/re_ranker/re_ranker_impl/query_expansion.py 9 | 10 | import numpy as np 11 | import torch 12 | import torch.nn.functional as F 13 | 14 | 15 | def aqe(query_feat: torch.tensor, gallery_feat: torch.tensor, 16 | qe_times: int = 1, qe_k: int = 10, alpha: float = 3.0): 17 | """ 18 | Combining the retrieved topk nearest neighbors with the original query and doing another retrieval. 19 | c.f. https://www.robots.ox.ac.uk/~vgg/publications/papers/chum07b.pdf 20 | Args : 21 | query_feat (torch.tensor): 22 | gallery_feat (torch.tensor): 23 | qe_times (int): number of query expansion times. 24 | qe_k (int): number of the neighbors to be combined. 25 | alpha (float): 26 | """ 27 | num_query = query_feat.shape[0] 28 | all_feat = torch.cat((query_feat, gallery_feat), dim=0) 29 | norm_feat = F.normalize(all_feat, p=2, dim=1) 30 | 31 | all_feat = all_feat.numpy() 32 | for i in range(qe_times): 33 | all_feat_list = [] 34 | sims = torch.mm(norm_feat, norm_feat.t()) 35 | sims = sims.data.cpu().numpy() 36 | for sim in sims: 37 | init_rank = np.argpartition(-sim, range(1, qe_k + 1)) 38 | weights = sim[init_rank[:qe_k]].reshape((-1, 1)) 39 | weights = np.power(weights, alpha) 40 | all_feat_list.append(np.mean(all_feat[init_rank[:qe_k], :] * weights, axis=0)) 41 | all_feat = np.stack(all_feat_list, axis=0) 42 | norm_feat = F.normalize(torch.from_numpy(all_feat), p=2, dim=1) 43 | 44 | query_feat = torch.from_numpy(all_feat[:num_query]) 45 | gallery_feat = torch.from_numpy(all_feat[num_query:]) 46 | return query_feat, gallery_feat 47 | -------------------------------------------------------------------------------- /fast_reid/fastreid/evaluation/rank_cylib/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | python3 setup.py build_ext --inplace 3 | rm -rf build 4 | python3 test_cython.py 5 | clean: 6 | rm -rf build 7 | rm -f rank_cy.c *.so 8 | -------------------------------------------------------------------------------- /fast_reid/fastreid/evaluation/rank_cylib/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ -------------------------------------------------------------------------------- /fast_reid/fastreid/evaluation/rank_cylib/roc_cy.pyx: -------------------------------------------------------------------------------- 1 | # cython: boundscheck=False, wraparound=False, nonecheck=False, cdivision=True 2 | # credits: https://github.com/KaiyangZhou/deep-person-reid/blob/master/torchreid/metrics/rank_cylib/rank_cy.pyx 3 | 4 | import cython 5 | import faiss 6 | import numpy as np 7 | cimport numpy as np 8 | 9 | 10 | """ 11 | Compiler directives: 12 | https://github.com/cython/cython/wiki/enhancements-compilerdirectives 13 | Cython tutorial: 14 | https://cython.readthedocs.io/en/latest/src/userguide/numpy_tutorial.html 15 | Credit to https://github.com/luzai 16 | """ 17 | 18 | 19 | # Main interface 20 | cpdef evaluate_roc_cy(float[:,:] distmat, long[:] q_pids, long[:]g_pids, 21 | long[:]q_camids, long[:]g_camids): 22 | 23 | distmat = np.asarray(distmat, dtype=np.float32) 24 | q_pids = np.asarray(q_pids, dtype=np.int64) 25 | g_pids = np.asarray(g_pids, dtype=np.int64) 26 | q_camids = np.asarray(q_camids, dtype=np.int64) 27 | g_camids = np.asarray(g_camids, dtype=np.int64) 28 | 29 | cdef long num_q = distmat.shape[0] 30 | cdef long num_g = distmat.shape[1] 31 | 32 | cdef: 33 | long[:,:] indices = np.argsort(distmat, axis=1) 34 | long[:,:] matches = (np.asarray(g_pids)[np.asarray(indices)] == np.asarray(q_pids)[:, np.newaxis]).astype(np.int64) 35 | 36 | float[:] pos = np.zeros(num_q*num_g, dtype=np.float32) 37 | float[:] neg = np.zeros(num_q*num_g, dtype=np.float32) 38 | 39 | long valid_pos = 0 40 | long valid_neg = 0 41 | long ind 42 | 43 | long q_idx, q_pid, q_camid, g_idx 44 | long[:] order = np.zeros(num_g, dtype=np.int64) 45 | 46 | float[:] raw_cmc = np.zeros(num_g, dtype=np.float32) # binary vector, positions with value 1 are correct matches 47 | long[:] sort_idx = np.zeros(num_g, dtype=np.int64) 48 | 49 | long idx 50 | 51 | for q_idx in range(num_q): 52 | # get query pid and camid 53 | q_pid = q_pids[q_idx] 54 | q_camid = q_camids[q_idx] 55 | 56 | for g_idx in range(num_g): 57 | order[g_idx] = indices[q_idx, g_idx] 58 | num_g_real = 0 59 | 60 | # remove gallery samples that have the same pid and camid with query 61 | for g_idx in range(num_g): 62 | if (g_pids[order[g_idx]] != q_pid) or (g_camids[order[g_idx]] != q_camid): 63 | raw_cmc[num_g_real] = matches[q_idx][g_idx] 64 | sort_idx[num_g_real] = order[g_idx] 65 | num_g_real += 1 66 | 67 | q_dist = distmat[q_idx] 68 | 69 | for valid_idx in range(num_g_real): 70 | if raw_cmc[valid_idx] == 1: 71 | pos[valid_pos] = q_dist[sort_idx[valid_idx]] 72 | valid_pos += 1 73 | elif raw_cmc[valid_idx] == 0: 74 | neg[valid_neg] = q_dist[sort_idx[valid_idx]] 75 | valid_neg += 1 76 | 77 | cdef float[:] scores = np.hstack((pos[:valid_pos], neg[:valid_neg])) 78 | cdef float[:] labels = np.hstack((np.zeros(valid_pos, dtype=np.float32), 79 | np.ones(valid_neg, dtype=np.float32))) 80 | return np.asarray(scores), np.asarray(labels) 81 | 82 | 83 | # Compute the cumulative sum 84 | cdef void function_cumsum(cython.numeric[:] src, cython.numeric[:] dst, long n): 85 | cdef long i 86 | dst[0] = src[0] 87 | for i in range(1, n): 88 | dst[i] = src[i] + dst[i - 1] -------------------------------------------------------------------------------- /fast_reid/fastreid/evaluation/rank_cylib/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from distutils.extension import Extension 3 | 4 | import numpy as np 5 | from Cython.Build import cythonize 6 | 7 | 8 | def numpy_include(): 9 | try: 10 | numpy_include = np.get_include() 11 | except AttributeError: 12 | numpy_include = np.get_numpy_include() 13 | return numpy_include 14 | 15 | 16 | ext_modules = [ 17 | Extension( 18 | 'rank_cy', 19 | ['rank_cy.pyx'], 20 | include_dirs=[numpy_include()], 21 | ), 22 | Extension( 23 | 'roc_cy', 24 | ['roc_cy.pyx'], 25 | include_dirs=[numpy_include()], 26 | ) 27 | ] 28 | 29 | setup( 30 | name='Cython-based reid evaluation code', 31 | ext_modules=cythonize(ext_modules) 32 | ) 33 | -------------------------------------------------------------------------------- /fast_reid/fastreid/evaluation/rerank.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # based on: 4 | # https://github.com/zhunzhong07/person-re-ranking 5 | 6 | __all__ = ['re_ranking'] 7 | 8 | import numpy as np 9 | 10 | 11 | def re_ranking(q_g_dist, q_q_dist, g_g_dist, k1: int = 20, k2: int = 6, lambda_value: float = 0.3): 12 | original_dist = np.concatenate( 13 | [np.concatenate([q_q_dist, q_g_dist], axis=1), 14 | np.concatenate([q_g_dist.T, g_g_dist], axis=1)], 15 | axis=0) 16 | original_dist = np.power(original_dist, 2).astype(np.float32) 17 | original_dist = np.transpose(1. * original_dist / np.max(original_dist, axis=0)) 18 | V = np.zeros_like(original_dist).astype(np.float32) 19 | initial_rank = np.argsort(original_dist).astype(np.int32) 20 | 21 | query_num = q_g_dist.shape[0] 22 | gallery_num = q_g_dist.shape[0] + q_g_dist.shape[1] 23 | all_num = gallery_num 24 | 25 | for i in range(all_num): 26 | # k-reciprocal neighbors 27 | forward_k_neigh_index = initial_rank[i, :k1 + 1] 28 | backward_k_neigh_index = initial_rank[forward_k_neigh_index, :k1 + 1] 29 | fi = np.where(backward_k_neigh_index == i)[0] 30 | k_reciprocal_index = forward_k_neigh_index[fi] 31 | k_reciprocal_expansion_index = k_reciprocal_index 32 | for j in range(len(k_reciprocal_index)): 33 | candidate = k_reciprocal_index[j] 34 | candidate_forward_k_neigh_index = initial_rank[candidate, 35 | :int(np.around(k1 / 2.)) + 1] 36 | candidate_backward_k_neigh_index = initial_rank[candidate_forward_k_neigh_index, 37 | :int(np.around(k1 / 2.)) + 1] 38 | fi_candidate = np.where(candidate_backward_k_neigh_index == candidate)[0] 39 | candidate_k_reciprocal_index = candidate_forward_k_neigh_index[fi_candidate] 40 | if len(np.intersect1d(candidate_k_reciprocal_index, k_reciprocal_index)) > 2. / 3 * len( 41 | candidate_k_reciprocal_index): 42 | k_reciprocal_expansion_index = np.append(k_reciprocal_expansion_index, candidate_k_reciprocal_index) 43 | 44 | k_reciprocal_expansion_index = np.unique(k_reciprocal_expansion_index) 45 | weight = np.exp(-original_dist[i, k_reciprocal_expansion_index]) 46 | V[i, k_reciprocal_expansion_index] = 1. * weight / np.sum(weight) 47 | original_dist = original_dist[:query_num, ] 48 | if k2 != 1: 49 | V_qe = np.zeros_like(V, dtype=np.float32) 50 | for i in range(all_num): 51 | V_qe[i, :] = np.mean(V[initial_rank[i, :k2], :], axis=0) 52 | V = V_qe 53 | del V_qe 54 | del initial_rank 55 | invIndex = [] 56 | for i in range(gallery_num): 57 | invIndex.append(np.where(V[:, i] != 0)[0]) 58 | 59 | jaccard_dist = np.zeros_like(original_dist, dtype=np.float32) 60 | 61 | for i in range(query_num): 62 | temp_min = np.zeros(shape=[1, gallery_num], dtype=np.float32) 63 | indNonZero = np.where(V[i, :] != 0)[0] 64 | indImages = [invIndex[ind] for ind in indNonZero] 65 | for j in range(len(indNonZero)): 66 | temp_min[0, indImages[j]] = temp_min[0, indImages[j]] + np.minimum(V[i, indNonZero[j]], 67 | V[indImages[j], indNonZero[j]]) 68 | jaccard_dist[i] = 1 - temp_min / (2. - temp_min) 69 | 70 | final_dist = jaccard_dist * (1 - lambda_value) + original_dist * lambda_value 71 | del original_dist, V, jaccard_dist 72 | final_dist = final_dist[:query_num, query_num:] 73 | return final_dist 74 | -------------------------------------------------------------------------------- /fast_reid/fastreid/evaluation/roc.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: l1aoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import warnings 8 | 9 | import faiss 10 | import numpy as np 11 | 12 | try: 13 | from .rank_cylib.roc_cy import evaluate_roc_cy 14 | 15 | IS_CYTHON_AVAI = True 16 | except ImportError: 17 | IS_CYTHON_AVAI = False 18 | warnings.warn( 19 | 'Cython roc evaluation (very fast so highly recommended) is ' 20 | 'unavailable, now use python evaluation.' 21 | ) 22 | 23 | 24 | def evaluate_roc_py(distmat, q_pids, g_pids, q_camids, g_camids): 25 | r"""Evaluation with ROC curve. 26 | Key: for each query identity, its gallery images from the same camera view are discarded. 27 | 28 | Args: 29 | distmat (np.ndarray): cosine distance matrix 30 | """ 31 | num_q, num_g = distmat.shape 32 | 33 | indices = np.argsort(distmat, axis=1) 34 | matches = (g_pids[indices] == q_pids[:, np.newaxis]).astype(np.int32) 35 | 36 | pos = [] 37 | neg = [] 38 | for q_idx in range(num_q): 39 | # get query pid and camid 40 | q_pid = q_pids[q_idx] 41 | q_camid = q_camids[q_idx] 42 | 43 | # Remove gallery samples that have the same pid and camid with query 44 | order = indices[q_idx] 45 | remove = (g_pids[order] == q_pid) & (g_camids[order] == q_camid) 46 | keep = np.invert(remove) 47 | raw_cmc = matches[q_idx][keep] 48 | 49 | sort_idx = order[keep] 50 | 51 | q_dist = distmat[q_idx] 52 | ind_pos = np.where(raw_cmc == 1)[0] 53 | pos.extend(q_dist[sort_idx[ind_pos]]) 54 | 55 | ind_neg = np.where(raw_cmc == 0)[0] 56 | neg.extend(q_dist[sort_idx[ind_neg]]) 57 | 58 | scores = np.hstack((pos, neg)) 59 | 60 | labels = np.hstack((np.zeros(len(pos)), np.ones(len(neg)))) 61 | return scores, labels 62 | 63 | 64 | def evaluate_roc( 65 | distmat, 66 | q_pids, 67 | g_pids, 68 | q_camids, 69 | g_camids, 70 | use_cython=True 71 | ): 72 | """Evaluates CMC rank. 73 | Args: 74 | distmat (numpy.ndarray): distance matrix of shape (num_query, num_gallery). 75 | q_pids (numpy.ndarray): 1-D array containing person identities 76 | of each query instance. 77 | g_pids (numpy.ndarray): 1-D array containing person identities 78 | of each gallery instance. 79 | q_camids (numpy.ndarray): 1-D array containing camera views under 80 | which each query instance is captured. 81 | g_camids (numpy.ndarray): 1-D array containing camera views under 82 | which each gallery instance is captured. 83 | use_cython (bool, optional): use cython code for evaluation. Default is True. 84 | This is highly recommended as the cython code can speed up the cmc computation 85 | by more than 10x. This requires Cython to be installed. 86 | """ 87 | if use_cython and IS_CYTHON_AVAI: 88 | return evaluate_roc_cy(distmat, q_pids, g_pids, q_camids, g_camids) 89 | else: 90 | return evaluate_roc_py(distmat, q_pids, g_pids, q_camids, g_camids) 91 | -------------------------------------------------------------------------------- /fast_reid/fastreid/evaluation/testing.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved 2 | import logging 3 | import pprint 4 | import sys 5 | from collections import Mapping, OrderedDict 6 | 7 | import numpy as np 8 | from tabulate import tabulate 9 | from termcolor import colored 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | 14 | def print_csv_format(results): 15 | """ 16 | Print main metrics in a format similar to Detectron, 17 | so that they are easy to copypaste into a spreadsheet. 18 | Args: 19 | results (OrderedDict[dict]): task_name -> {metric -> score} 20 | """ 21 | assert isinstance(results, OrderedDict), results # unordered results cannot be properly printed 22 | task = list(results.keys())[0] 23 | metrics = ["Datasets"] + [k for k in results[task]] 24 | 25 | csv_results = [] 26 | for task, res in results.items(): 27 | csv_results.append((task, *list(res.values()))) 28 | 29 | # tabulate it 30 | table = tabulate( 31 | csv_results, 32 | tablefmt="pipe", 33 | floatfmt=".2f", 34 | headers=metrics, 35 | numalign="left", 36 | ) 37 | 38 | logger.info("Evaluation results in csv format: \n" + colored(table, "cyan")) 39 | 40 | 41 | def verify_results(cfg, results): 42 | """ 43 | Args: 44 | results (OrderedDict[dict]): task_name -> {metric -> score} 45 | Returns: 46 | bool: whether the verification succeeds or not 47 | """ 48 | expected_results = cfg.TEST.EXPECTED_RESULTS 49 | if not len(expected_results): 50 | return True 51 | 52 | ok = True 53 | for task, metric, expected, tolerance in expected_results: 54 | actual = results[task][metric] 55 | if not np.isfinite(actual): 56 | ok = False 57 | diff = abs(actual - expected) 58 | if diff > tolerance: 59 | ok = False 60 | 61 | logger = logging.getLogger(__name__) 62 | if not ok: 63 | logger.error("Result verification failed!") 64 | logger.error("Expected Results: " + str(expected_results)) 65 | logger.error("Actual Results: " + pprint.pformat(results)) 66 | 67 | sys.exit(1) 68 | else: 69 | logger.info("Results verification passed.") 70 | return ok 71 | 72 | 73 | def flatten_results_dict(results): 74 | """ 75 | Expand a hierarchical dict of scalars into a flat dict of scalars. 76 | If results[k1][k2][k3] = v, the returned dict will have the entry 77 | {"k1/k2/k3": v}. 78 | Args: 79 | results (dict): 80 | """ 81 | r = {} 82 | for k, v in results.items(): 83 | if isinstance(v, Mapping): 84 | v = flatten_results_dict(v) 85 | for kk, vv in v.items(): 86 | r[k + "/" + kk] = vv 87 | else: 88 | r[k] = v 89 | return r 90 | -------------------------------------------------------------------------------- /fast_reid/fastreid/layers/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .activation import * 8 | from .arc_softmax import ArcSoftmax 9 | from .circle_softmax import CircleSoftmax 10 | from .cos_softmax import CosSoftmax 11 | from .batch_drop import BatchDrop 12 | from .batch_norm import * 13 | from .context_block import ContextBlock 14 | from .frn import FRN, TLU 15 | from .non_local import Non_local 16 | from .pooling import * 17 | from .se_layer import SELayer 18 | from .splat import SplAtConv2d, DropBlock2D 19 | from .gather_layer import GatherLayer 20 | -------------------------------------------------------------------------------- /fast_reid/fastreid/layers/activation.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import math 8 | 9 | import torch 10 | import torch.nn as nn 11 | import torch.nn.functional as F 12 | 13 | __all__ = [ 14 | 'Mish', 15 | 'Swish', 16 | 'MemoryEfficientSwish', 17 | 'GELU'] 18 | 19 | 20 | class Mish(nn.Module): 21 | def __init__(self): 22 | super().__init__() 23 | 24 | def forward(self, x): 25 | # inlining this saves 1 second per epoch (V100 GPU) vs having a temp x and then returning x(!) 26 | return x * (torch.tanh(F.softplus(x))) 27 | 28 | 29 | class Swish(nn.Module): 30 | def forward(self, x): 31 | return x * torch.sigmoid(x) 32 | 33 | 34 | class SwishImplementation(torch.autograd.Function): 35 | @staticmethod 36 | def forward(ctx, i): 37 | result = i * torch.sigmoid(i) 38 | ctx.save_for_backward(i) 39 | return result 40 | 41 | @staticmethod 42 | def backward(ctx, grad_output): 43 | i = ctx.saved_variables[0] 44 | sigmoid_i = torch.sigmoid(i) 45 | return grad_output * (sigmoid_i * (1 + i * (1 - sigmoid_i))) 46 | 47 | 48 | class MemoryEfficientSwish(nn.Module): 49 | def forward(self, x): 50 | return SwishImplementation.apply(x) 51 | 52 | 53 | class GELU(nn.Module): 54 | """ 55 | Paper Section 3.4, last paragraph notice that BERT used the GELU instead of RELU 56 | """ 57 | 58 | def forward(self, x): 59 | return 0.5 * x * (1 + torch.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * torch.pow(x, 3)))) 60 | -------------------------------------------------------------------------------- /fast_reid/fastreid/layers/arc_softmax.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import math 8 | 9 | import torch 10 | import torch.nn as nn 11 | import torch.nn.functional as F 12 | from torch.nn import Parameter 13 | 14 | 15 | class ArcSoftmax(nn.Module): 16 | def __init__(self, cfg, in_feat, num_classes): 17 | super().__init__() 18 | self.in_feat = in_feat 19 | self._num_classes = num_classes 20 | self.s = cfg.MODEL.HEADS.SCALE 21 | self.m = cfg.MODEL.HEADS.MARGIN 22 | 23 | self.easy_margin = False 24 | 25 | self.cos_m = math.cos(self.m) 26 | self.sin_m = math.sin(self.m) 27 | self.threshold = math.cos(math.pi - self.m) 28 | self.mm = math.sin(math.pi - self.m) * self.m 29 | 30 | self.weight = Parameter(torch.Tensor(num_classes, in_feat)) 31 | nn.init.xavier_uniform_(self.weight) 32 | self.register_buffer('t', torch.zeros(1)) 33 | 34 | def forward(self, features, targets): 35 | cosine = F.linear(F.normalize(features), F.normalize(self.weight)) 36 | sine = torch.sqrt(1.0 - torch.pow(cosine, 2)) 37 | phi = cosine * self.cos_m - sine * self.sin_m # cos(theta + m) 38 | if self.easy_margin: 39 | phi = torch.where(cosine > 0, phi, cosine) 40 | else: 41 | phi = torch.where(cosine > self.threshold, phi, cosine - self.mm) 42 | one_hot = torch.zeros(cosine.size(), device=cosine.device) 43 | one_hot.scatter_(1, targets.view(-1, 1).long(), 1) 44 | output = (one_hot * phi) + ((1.0 - one_hot) * cosine) 45 | output *= self.s 46 | return output 47 | 48 | def extra_repr(self): 49 | return 'in_features={}, num_classes={}, scale={}, margin={}'.format( 50 | self.in_feat, self._num_classes, self.s, self.m 51 | ) 52 | -------------------------------------------------------------------------------- /fast_reid/fastreid/layers/batch_drop.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import random 8 | 9 | from torch import nn 10 | 11 | 12 | class BatchDrop(nn.Module): 13 | """ref: https://github.com/daizuozhuo/batch-dropblock-network/blob/master/models/networks.py 14 | batch drop mask 15 | """ 16 | 17 | def __init__(self, h_ratio, w_ratio): 18 | super(BatchDrop, self).__init__() 19 | self.h_ratio = h_ratio 20 | self.w_ratio = w_ratio 21 | 22 | def forward(self, x): 23 | if self.training: 24 | h, w = x.size()[-2:] 25 | rh = round(self.h_ratio * h) 26 | rw = round(self.w_ratio * w) 27 | sx = random.randint(0, h - rh) 28 | sy = random.randint(0, w - rw) 29 | mask = x.new_ones(x.size()) 30 | mask[:, :, sx:sx + rh, sy:sy + rw] = 0 31 | x = x * mask 32 | return x 33 | -------------------------------------------------------------------------------- /fast_reid/fastreid/layers/circle_softmax.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import math 8 | 9 | import torch 10 | import torch.nn as nn 11 | import torch.nn.functional as F 12 | from torch.nn import Parameter 13 | 14 | 15 | class CircleSoftmax(nn.Module): 16 | def __init__(self, cfg, in_feat, num_classes): 17 | super().__init__() 18 | self.in_feat = in_feat 19 | self._num_classes = num_classes 20 | self.s = cfg.MODEL.HEADS.SCALE 21 | self.m = cfg.MODEL.HEADS.MARGIN 22 | 23 | self.weight = Parameter(torch.Tensor(num_classes, in_feat)) 24 | nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5)) 25 | 26 | def forward(self, features, targets): 27 | sim_mat = F.linear(F.normalize(features), F.normalize(self.weight)) 28 | alpha_p = torch.clamp_min(-sim_mat.detach() + 1 + self.m, min=0.) 29 | alpha_n = torch.clamp_min(sim_mat.detach() + self.m, min=0.) 30 | delta_p = 1 - self.m 31 | delta_n = self.m 32 | 33 | s_p = self.s * alpha_p * (sim_mat - delta_p) 34 | s_n = self.s * alpha_n * (sim_mat - delta_n) 35 | 36 | targets = F.one_hot(targets, num_classes=self._num_classes) 37 | 38 | pred_class_logits = targets * s_p + (1.0 - targets) * s_n 39 | 40 | return pred_class_logits 41 | 42 | def extra_repr(self): 43 | return 'in_features={}, num_classes={}, scale={}, margin={}'.format( 44 | self.in_feat, self._num_classes, self.s, self.m 45 | ) 46 | -------------------------------------------------------------------------------- /fast_reid/fastreid/layers/cos_softmax.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import torch 8 | from torch import nn 9 | import torch.nn.functional as F 10 | from torch.nn import Parameter 11 | 12 | 13 | class CosSoftmax(nn.Module): 14 | r"""Implement of large margin cosine distance: 15 | Args: 16 | in_feat: size of each input sample 17 | num_classes: size of each output sample 18 | """ 19 | 20 | def __init__(self, cfg, in_feat, num_classes): 21 | super().__init__() 22 | self.in_features = in_feat 23 | self._num_classes = num_classes 24 | self.s = cfg.MODEL.HEADS.SCALE 25 | self.m = cfg.MODEL.HEADS.MARGIN 26 | self.weight = Parameter(torch.Tensor(num_classes, in_feat)) 27 | nn.init.xavier_uniform_(self.weight) 28 | 29 | def forward(self, features, targets): 30 | # --------------------------- cos(theta) & phi(theta) --------------------------- 31 | cosine = F.linear(F.normalize(features), F.normalize(self.weight)) 32 | phi = cosine - self.m 33 | # --------------------------- convert label to one-hot --------------------------- 34 | targets = F.one_hot(targets, num_classes=self._num_classes) 35 | output = (targets * phi) + ((1.0 - targets) * cosine) 36 | output *= self.s 37 | 38 | return output 39 | 40 | def extra_repr(self): 41 | return 'in_features={}, num_classes={}, scale={}, margin={}'.format( 42 | self.in_feat, self._num_classes, self.s, self.m 43 | ) 44 | -------------------------------------------------------------------------------- /fast_reid/fastreid/layers/gather_layer.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | # based on: https://github.com/open-mmlab/OpenSelfSup/blob/master/openselfsup/models/utils/gather_layer.py 8 | 9 | import torch 10 | import torch.distributed as dist 11 | 12 | 13 | class GatherLayer(torch.autograd.Function): 14 | """Gather tensors from all process, supporting backward propagation. 15 | """ 16 | 17 | @staticmethod 18 | def forward(ctx, input): 19 | ctx.save_for_backward(input) 20 | output = [torch.zeros_like(input) \ 21 | for _ in range(dist.get_world_size())] 22 | dist.all_gather(output, input) 23 | return tuple(output) 24 | 25 | @staticmethod 26 | def backward(ctx, *grads): 27 | input, = ctx.saved_tensors 28 | grad_out = torch.zeros_like(input) 29 | grad_out[:] = grads[dist.get_rank()] 30 | return grad_out 31 | -------------------------------------------------------------------------------- /fast_reid/fastreid/layers/non_local.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | 4 | import torch 5 | from torch import nn 6 | from .batch_norm import get_norm 7 | 8 | 9 | class Non_local(nn.Module): 10 | def __init__(self, in_channels, bn_norm, reduc_ratio=2): 11 | super(Non_local, self).__init__() 12 | 13 | self.in_channels = in_channels 14 | self.inter_channels = reduc_ratio // reduc_ratio 15 | 16 | self.g = nn.Conv2d(in_channels=self.in_channels, out_channels=self.inter_channels, 17 | kernel_size=1, stride=1, padding=0) 18 | 19 | self.W = nn.Sequential( 20 | nn.Conv2d(in_channels=self.inter_channels, out_channels=self.in_channels, 21 | kernel_size=1, stride=1, padding=0), 22 | get_norm(bn_norm, self.in_channels), 23 | ) 24 | nn.init.constant_(self.W[1].weight, 0.0) 25 | nn.init.constant_(self.W[1].bias, 0.0) 26 | 27 | self.theta = nn.Conv2d(in_channels=self.in_channels, out_channels=self.inter_channels, 28 | kernel_size=1, stride=1, padding=0) 29 | 30 | self.phi = nn.Conv2d(in_channels=self.in_channels, out_channels=self.inter_channels, 31 | kernel_size=1, stride=1, padding=0) 32 | 33 | def forward(self, x): 34 | """ 35 | :param x: (b, t, h, w) 36 | :return x: (b, t, h, w) 37 | """ 38 | batch_size = x.size(0) 39 | g_x = self.g(x).view(batch_size, self.inter_channels, -1) 40 | g_x = g_x.permute(0, 2, 1) 41 | 42 | theta_x = self.theta(x).view(batch_size, self.inter_channels, -1) 43 | theta_x = theta_x.permute(0, 2, 1) 44 | phi_x = self.phi(x).view(batch_size, self.inter_channels, -1) 45 | f = torch.matmul(theta_x, phi_x) 46 | N = f.size(-1) 47 | f_div_C = f / N 48 | 49 | y = torch.matmul(f_div_C, g_x) 50 | y = y.permute(0, 2, 1).contiguous() 51 | y = y.view(batch_size, self.inter_channels, *x.size()[2:]) 52 | W_y = self.W(y) 53 | z = W_y + x 54 | return z 55 | -------------------------------------------------------------------------------- /fast_reid/fastreid/layers/pooling.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: l1aoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import torch 8 | import torch.nn.functional as F 9 | from torch import nn 10 | 11 | __all__ = ["Flatten", 12 | "GeneralizedMeanPooling", 13 | "GeneralizedMeanPoolingP", 14 | "FastGlobalAvgPool2d", 15 | "AdaptiveAvgMaxPool2d", 16 | "ClipGlobalAvgPool2d", 17 | ] 18 | 19 | 20 | class Flatten(nn.Module): 21 | def forward(self, input): 22 | return input.view(input.size(0), -1, 1, 1) 23 | 24 | 25 | class GeneralizedMeanPooling(nn.Module): 26 | r"""Applies a 2D power-average adaptive pooling over an input signal composed of several input planes. 27 | The function computed is: :math:`f(X) = pow(sum(pow(X, p)), 1/p)` 28 | - At p = infinity, one gets Max Pooling 29 | - At p = 1, one gets Average Pooling 30 | The output is of size H x W, for any input size. 31 | The number of output features is equal to the number of input planes. 32 | Args: 33 | output_size: the target output size of the image of the form H x W. 34 | Can be a tuple (H, W) or a single H for a square image H x H 35 | H and W can be either a ``int``, or ``None`` which means the size will 36 | be the same as that of the input. 37 | """ 38 | 39 | def __init__(self, norm=3, output_size=1, eps=1e-6): 40 | super(GeneralizedMeanPooling, self).__init__() 41 | assert norm > 0 42 | self.p = float(norm) 43 | self.output_size = output_size 44 | self.eps = eps 45 | 46 | def forward(self, x): 47 | x = x.clamp(min=self.eps).pow(self.p) 48 | return torch.nn.functional.adaptive_avg_pool2d(x, self.output_size).pow(1. / self.p) 49 | 50 | def __repr__(self): 51 | return self.__class__.__name__ + '(' \ 52 | + str(self.p) + ', ' \ 53 | + 'output_size=' + str(self.output_size) + ')' 54 | 55 | 56 | class GeneralizedMeanPoolingP(GeneralizedMeanPooling): 57 | """ Same, but norm is trainable 58 | """ 59 | 60 | def __init__(self, norm=3, output_size=1, eps=1e-6): 61 | super(GeneralizedMeanPoolingP, self).__init__(norm, output_size, eps) 62 | self.p = nn.Parameter(torch.ones(1) * norm) 63 | 64 | 65 | class AdaptiveAvgMaxPool2d(nn.Module): 66 | def __init__(self): 67 | super(AdaptiveAvgMaxPool2d, self).__init__() 68 | self.gap = FastGlobalAvgPool2d() 69 | self.gmp = nn.AdaptiveMaxPool2d(1) 70 | 71 | def forward(self, x): 72 | avg_feat = self.gap(x) 73 | max_feat = self.gmp(x) 74 | feat = avg_feat + max_feat 75 | return feat 76 | 77 | 78 | class FastGlobalAvgPool2d(nn.Module): 79 | def __init__(self, flatten=False): 80 | super(FastGlobalAvgPool2d, self).__init__() 81 | self.flatten = flatten 82 | 83 | def forward(self, x): 84 | if self.flatten: 85 | in_size = x.size() 86 | return x.view((in_size[0], in_size[1], -1)).mean(dim=2) 87 | else: 88 | return x.view(x.size(0), x.size(1), -1).mean(-1).view(x.size(0), x.size(1), 1, 1) 89 | 90 | 91 | class ClipGlobalAvgPool2d(nn.Module): 92 | def __init__(self): 93 | super().__init__() 94 | self.avgpool = FastGlobalAvgPool2d() 95 | 96 | def forward(self, x): 97 | x = self.avgpool(x) 98 | x = torch.clamp(x, min=0., max=1.) 99 | return x 100 | -------------------------------------------------------------------------------- /fast_reid/fastreid/layers/se_layer.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from torch import nn 8 | 9 | 10 | class SELayer(nn.Module): 11 | def __init__(self, channel, reduction=16): 12 | super(SELayer, self).__init__() 13 | self.avg_pool = nn.AdaptiveAvgPool2d(1) 14 | self.fc = nn.Sequential( 15 | nn.Linear(channel, int(channel / reduction), bias=False), 16 | nn.ReLU(inplace=True), 17 | nn.Linear(int(channel / reduction), channel, bias=False), 18 | nn.Sigmoid() 19 | ) 20 | 21 | def forward(self, x): 22 | b, c, _, _ = x.size() 23 | y = self.avg_pool(x).view(b, c) 24 | y = self.fc(y).view(b, c, 1, 1) 25 | return x * y.expand_as(x) 26 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .meta_arch import build_model 8 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .build import build_backbone, BACKBONE_REGISTRY 8 | 9 | from .resnet import build_resnet_backbone 10 | from .resnet_distill import build_resnet_backbone_distill 11 | from .osnet import build_osnet_backbone 12 | from .resnest import build_resnest_backbone 13 | from .resnext import build_resnext_backbone 14 | from .regnet import build_regnet_backbone, build_effnet_backbone 15 | from .shufflenet import build_shufflenetv2_backbone 16 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/build.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from ...utils.registry import Registry 8 | 9 | BACKBONE_REGISTRY = Registry("BACKBONE") 10 | BACKBONE_REGISTRY.__doc__ = """ 11 | Registry for backbones, which extract feature maps from images 12 | The registered object must be a callable that accepts two arguments: 13 | 1. A :class:`detectron2.config.CfgNode` 14 | 2. A :class:`detectron2.layers.ShapeSpec`, which contains the input shape specification. 15 | It must returns an instance of :class:`Backbone`. 16 | """ 17 | 18 | 19 | def build_backbone(cfg): 20 | """ 21 | Build a backbone from `cfg.MODEL.BACKBONE.NAME`. 22 | Returns: 23 | an instance of :class:`Backbone` 24 | """ 25 | 26 | backbone_name = cfg.MODEL.BACKBONE.NAME 27 | backbone = BACKBONE_REGISTRY.get(backbone_name)(cfg) 28 | return backbone 29 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from .regnet import build_regnet_backbone 4 | from .effnet import build_effnet_backbone 5 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/effnet/EN-B0_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: effnet 3 | NUM_CLASSES: 1000 4 | EN: 5 | STEM_W: 32 6 | STRIDES: [1, 2, 2, 2, 1, 2, 1] 7 | DEPTHS: [1, 2, 2, 3, 3, 4, 1] 8 | WIDTHS: [16, 24, 40, 80, 112, 192, 320] 9 | EXP_RATIOS: [1, 6, 6, 6, 6, 6, 6] 10 | KERNELS: [3, 3, 5, 3, 5, 5, 3] 11 | HEAD_W: 1280 12 | OPTIM: 13 | LR_POLICY: cos 14 | BASE_LR: 0.4 15 | MAX_EPOCH: 100 16 | MOMENTUM: 0.9 17 | WEIGHT_DECAY: 1e-5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 224 21 | BATCH_SIZE: 256 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 256 25 | BATCH_SIZE: 200 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/effnet/EN-B1_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: effnet 3 | NUM_CLASSES: 1000 4 | EN: 5 | STEM_W: 32 6 | STRIDES: [1, 2, 2, 2, 1, 2, 1] 7 | DEPTHS: [2, 3, 3, 4, 4, 5, 2] 8 | WIDTHS: [16, 24, 40, 80, 112, 192, 320] 9 | EXP_RATIOS: [1, 6, 6, 6, 6, 6, 6] 10 | KERNELS: [3, 3, 5, 3, 5, 5, 3] 11 | HEAD_W: 1280 12 | OPTIM: 13 | LR_POLICY: cos 14 | BASE_LR: 0.4 15 | MAX_EPOCH: 100 16 | MOMENTUM: 0.9 17 | WEIGHT_DECAY: 1e-5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 240 21 | BATCH_SIZE: 256 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 274 25 | BATCH_SIZE: 200 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/effnet/EN-B2_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: effnet 3 | NUM_CLASSES: 1000 4 | EN: 5 | STEM_W: 32 6 | STRIDES: [1, 2, 2, 2, 1, 2, 1] 7 | DEPTHS: [2, 3, 3, 4, 4, 5, 2] 8 | WIDTHS: [16, 24, 48, 88, 120, 208, 352] 9 | EXP_RATIOS: [1, 6, 6, 6, 6, 6, 6] 10 | KERNELS: [3, 3, 5, 3, 5, 5, 3] 11 | HEAD_W: 1408 12 | OPTIM: 13 | LR_POLICY: cos 14 | BASE_LR: 0.4 15 | MAX_EPOCH: 100 16 | MOMENTUM: 0.9 17 | WEIGHT_DECAY: 1e-5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 260 21 | BATCH_SIZE: 256 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 298 25 | BATCH_SIZE: 200 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/effnet/EN-B3_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: effnet 3 | NUM_CLASSES: 1000 4 | EN: 5 | STEM_W: 40 6 | STRIDES: [1, 2, 2, 2, 1, 2, 1] 7 | DEPTHS: [2, 3, 3, 5, 5, 6, 2] 8 | WIDTHS: [24, 32, 48, 96, 136, 232, 384] 9 | EXP_RATIOS: [1, 6, 6, 6, 6, 6, 6] 10 | KERNELS: [3, 3, 5, 3, 5, 5, 3] 11 | HEAD_W: 1536 12 | OPTIM: 13 | LR_POLICY: cos 14 | BASE_LR: 0.4 15 | MAX_EPOCH: 100 16 | MOMENTUM: 0.9 17 | WEIGHT_DECAY: 1e-5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 300 21 | BATCH_SIZE: 256 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 342 25 | BATCH_SIZE: 200 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/effnet/EN-B4_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: effnet 3 | NUM_CLASSES: 1000 4 | EN: 5 | STEM_W: 48 6 | STRIDES: [1, 2, 2, 2, 1, 2, 1] 7 | DEPTHS: [2, 4, 4, 6, 6, 8, 2] 8 | WIDTHS: [24, 32, 56, 112, 160, 272, 448] 9 | EXP_RATIOS: [1, 6, 6, 6, 6, 6, 6] 10 | KERNELS: [3, 3, 5, 3, 5, 5, 3] 11 | HEAD_W: 1792 12 | OPTIM: 13 | LR_POLICY: cos 14 | BASE_LR: 0.2 15 | MAX_EPOCH: 100 16 | MOMENTUM: 0.9 17 | WEIGHT_DECAY: 1e-5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 380 21 | BATCH_SIZE: 128 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 434 25 | BATCH_SIZE: 104 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/effnet/EN-B5_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: effnet 3 | NUM_CLASSES: 1000 4 | EN: 5 | STEM_W: 48 6 | STRIDES: [1, 2, 2, 2, 1, 2, 1] 7 | DEPTHS: [3, 5, 5, 7, 7, 9, 3] 8 | WIDTHS: [24, 40, 64, 128, 176, 304, 512] 9 | EXP_RATIOS: [1, 6, 6, 6, 6, 6, 6] 10 | KERNELS: [3, 3, 5, 3, 5, 5, 3] 11 | HEAD_W: 2048 12 | OPTIM: 13 | LR_POLICY: cos 14 | BASE_LR: 0.1 15 | MAX_EPOCH: 100 16 | MOMENTUM: 0.9 17 | WEIGHT_DECAY: 1e-5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 456 21 | BATCH_SIZE: 64 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 522 25 | BATCH_SIZE: 48 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnetx/RegNetX-1.6GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | DEPTH: 18 6 | W0: 80 7 | WA: 34.01 8 | WM: 2.25 9 | GROUP_W: 24 10 | OPTIM: 11 | LR_POLICY: cos 12 | BASE_LR: 0.8 13 | MAX_EPOCH: 100 14 | MOMENTUM: 0.9 15 | WEIGHT_DECAY: 5e-5 16 | WARMUP_EPOCHS: 5 17 | TRAIN: 18 | DATASET: imagenet 19 | IM_SIZE: 224 20 | BATCH_SIZE: 1024 21 | TEST: 22 | DATASET: imagenet 23 | IM_SIZE: 256 24 | BATCH_SIZE: 800 25 | NUM_GPUS: 8 26 | OUT_DIR: . 27 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnetx/RegNetX-12GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | DEPTH: 19 6 | W0: 168 7 | WA: 73.36 8 | WM: 2.37 9 | GROUP_W: 112 10 | OPTIM: 11 | LR_POLICY: cos 12 | BASE_LR: 0.4 13 | MAX_EPOCH: 100 14 | MOMENTUM: 0.9 15 | WEIGHT_DECAY: 5e-5 16 | WARMUP_EPOCHS: 5 17 | TRAIN: 18 | DATASET: imagenet 19 | IM_SIZE: 224 20 | BATCH_SIZE: 512 21 | TEST: 22 | DATASET: imagenet 23 | IM_SIZE: 256 24 | BATCH_SIZE: 400 25 | NUM_GPUS: 8 26 | OUT_DIR: . 27 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnetx/RegNetX-16GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | DEPTH: 22 6 | W0: 216 7 | WA: 55.59 8 | WM: 2.1 9 | GROUP_W: 128 10 | OPTIM: 11 | LR_POLICY: cos 12 | BASE_LR: 0.4 13 | MAX_EPOCH: 100 14 | MOMENTUM: 0.9 15 | WEIGHT_DECAY: 5e-5 16 | WARMUP_EPOCHS: 5 17 | TRAIN: 18 | DATASET: imagenet 19 | IM_SIZE: 224 20 | BATCH_SIZE: 512 21 | TEST: 22 | DATASET: imagenet 23 | IM_SIZE: 256 24 | BATCH_SIZE: 400 25 | NUM_GPUS: 8 26 | OUT_DIR: . 27 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnetx/RegNetX-200MF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | DEPTH: 13 6 | W0: 24 7 | WA: 36.44 8 | WM: 2.49 9 | GROUP_W: 8 10 | OPTIM: 11 | LR_POLICY: cos 12 | BASE_LR: 0.8 13 | MAX_EPOCH: 100 14 | MOMENTUM: 0.9 15 | WEIGHT_DECAY: 5e-5 16 | WARMUP_EPOCHS: 5 17 | TRAIN: 18 | DATASET: imagenet 19 | IM_SIZE: 224 20 | BATCH_SIZE: 1024 21 | TEST: 22 | DATASET: imagenet 23 | IM_SIZE: 256 24 | BATCH_SIZE: 800 25 | NUM_GPUS: 8 26 | OUT_DIR: . 27 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnetx/RegNetX-3.2GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | DEPTH: 25 6 | W0: 88 7 | WA: 26.31 8 | WM: 2.25 9 | GROUP_W: 48 10 | OPTIM: 11 | LR_POLICY: cos 12 | BASE_LR: 0.4 13 | MAX_EPOCH: 100 14 | MOMENTUM: 0.9 15 | WEIGHT_DECAY: 5e-5 16 | WARMUP_EPOCHS: 5 17 | TRAIN: 18 | DATASET: imagenet 19 | IM_SIZE: 224 20 | BATCH_SIZE: 512 21 | TEST: 22 | DATASET: imagenet 23 | IM_SIZE: 256 24 | BATCH_SIZE: 400 25 | NUM_GPUS: 8 26 | OUT_DIR: . 27 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnetx/RegNetX-32GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | DEPTH: 23 6 | W0: 320 7 | WA: 69.86 8 | WM: 2.0 9 | GROUP_W: 168 10 | OPTIM: 11 | LR_POLICY: cos 12 | BASE_LR: 0.2 13 | MAX_EPOCH: 100 14 | MOMENTUM: 0.9 15 | WEIGHT_DECAY: 5e-5 16 | WARMUP_EPOCHS: 5 17 | TRAIN: 18 | DATASET: imagenet 19 | IM_SIZE: 224 20 | BATCH_SIZE: 256 21 | TEST: 22 | DATASET: imagenet 23 | IM_SIZE: 256 24 | BATCH_SIZE: 200 25 | NUM_GPUS: 8 26 | OUT_DIR: . 27 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnetx/RegNetX-4.0GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | DEPTH: 23 6 | W0: 96 7 | WA: 38.65 8 | WM: 2.43 9 | GROUP_W: 40 10 | OPTIM: 11 | LR_POLICY: cos 12 | BASE_LR: 0.4 13 | MAX_EPOCH: 100 14 | MOMENTUM: 0.9 15 | WEIGHT_DECAY: 5e-5 16 | WARMUP_EPOCHS: 5 17 | TRAIN: 18 | DATASET: imagenet 19 | IM_SIZE: 224 20 | BATCH_SIZE: 512 21 | TEST: 22 | DATASET: imagenet 23 | IM_SIZE: 256 24 | BATCH_SIZE: 400 25 | NUM_GPUS: 8 26 | OUT_DIR: . 27 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnetx/RegNetX-400MF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | DEPTH: 22 6 | W0: 24 7 | WA: 24.48 8 | WM: 2.54 9 | GROUP_W: 16 10 | OPTIM: 11 | LR_POLICY: cos 12 | BASE_LR: 0.8 13 | MAX_EPOCH: 100 14 | MOMENTUM: 0.9 15 | WEIGHT_DECAY: 5e-5 16 | WARMUP_EPOCHS: 5 17 | TRAIN: 18 | DATASET: imagenet 19 | IM_SIZE: 224 20 | BATCH_SIZE: 1024 21 | TEST: 22 | DATASET: imagenet 23 | IM_SIZE: 256 24 | BATCH_SIZE: 800 25 | NUM_GPUS: 8 26 | OUT_DIR: . 27 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnetx/RegNetX-6.4GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | DEPTH: 17 6 | W0: 184 7 | WA: 60.83 8 | WM: 2.07 9 | GROUP_W: 56 10 | OPTIM: 11 | LR_POLICY: cos 12 | BASE_LR: 0.4 13 | MAX_EPOCH: 100 14 | MOMENTUM: 0.9 15 | WEIGHT_DECAY: 5e-5 16 | WARMUP_EPOCHS: 5 17 | TRAIN: 18 | DATASET: imagenet 19 | IM_SIZE: 224 20 | BATCH_SIZE: 512 21 | TEST: 22 | DATASET: imagenet 23 | IM_SIZE: 256 24 | BATCH_SIZE: 400 25 | NUM_GPUS: 8 26 | OUT_DIR: . 27 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnetx/RegNetX-600MF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | DEPTH: 16 6 | W0: 48 7 | WA: 36.97 8 | WM: 2.24 9 | GROUP_W: 24 10 | OPTIM: 11 | LR_POLICY: cos 12 | BASE_LR: 0.8 13 | MAX_EPOCH: 100 14 | MOMENTUM: 0.9 15 | WEIGHT_DECAY: 5e-5 16 | WARMUP_EPOCHS: 5 17 | TRAIN: 18 | DATASET: imagenet 19 | IM_SIZE: 224 20 | BATCH_SIZE: 1024 21 | TEST: 22 | DATASET: imagenet 23 | IM_SIZE: 256 24 | BATCH_SIZE: 800 25 | NUM_GPUS: 8 26 | OUT_DIR: . 27 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnetx/RegNetX-8.0GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | DEPTH: 23 6 | W0: 80 7 | WA: 49.56 8 | WM: 2.88 9 | GROUP_W: 120 10 | OPTIM: 11 | LR_POLICY: cos 12 | BASE_LR: 0.4 13 | MAX_EPOCH: 100 14 | MOMENTUM: 0.9 15 | WEIGHT_DECAY: 5e-5 16 | WARMUP_EPOCHS: 5 17 | TRAIN: 18 | DATASET: imagenet 19 | IM_SIZE: 224 20 | BATCH_SIZE: 512 21 | TEST: 22 | DATASET: imagenet 23 | IM_SIZE: 256 24 | BATCH_SIZE: 400 25 | NUM_GPUS: 8 26 | OUT_DIR: . 27 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnetx/RegNetX-800MF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | DEPTH: 16 6 | W0: 56 7 | WA: 35.73 8 | WM: 2.28 9 | GROUP_W: 16 10 | OPTIM: 11 | LR_POLICY: cos 12 | BASE_LR: 0.8 13 | MAX_EPOCH: 100 14 | MOMENTUM: 0.9 15 | WEIGHT_DECAY: 5e-5 16 | WARMUP_EPOCHS: 5 17 | TRAIN: 18 | DATASET: imagenet 19 | IM_SIZE: 224 20 | BATCH_SIZE: 1024 21 | TEST: 22 | DATASET: imagenet 23 | IM_SIZE: 256 24 | BATCH_SIZE: 800 25 | NUM_GPUS: 8 26 | OUT_DIR: . 27 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnety/RegNetY-1.6GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | SE_ON: True 6 | DEPTH: 27 7 | W0: 48 8 | WA: 20.71 9 | WM: 2.65 10 | GROUP_W: 24 11 | OPTIM: 12 | LR_POLICY: cos 13 | BASE_LR: 0.8 14 | MAX_EPOCH: 100 15 | MOMENTUM: 0.9 16 | WEIGHT_DECAY: 5e-5 17 | WARMUP_EPOCHS: 5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 224 21 | BATCH_SIZE: 1024 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 256 25 | BATCH_SIZE: 800 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnety/RegNetY-12GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | SE_ON: True 6 | DEPTH: 19 7 | W0: 168 8 | WA: 73.36 9 | WM: 2.37 10 | GROUP_W: 112 11 | OPTIM: 12 | LR_POLICY: cos 13 | BASE_LR: 0.4 14 | MAX_EPOCH: 100 15 | MOMENTUM: 0.9 16 | WEIGHT_DECAY: 5e-5 17 | WARMUP_EPOCHS: 5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 224 21 | BATCH_SIZE: 512 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 256 25 | BATCH_SIZE: 400 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnety/RegNetY-16GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | SE_ON: True 6 | DEPTH: 18 7 | W0: 200 8 | WA: 106.23 9 | WM: 2.48 10 | GROUP_W: 112 11 | OPTIM: 12 | LR_POLICY: cos 13 | BASE_LR: 0.2 14 | MAX_EPOCH: 100 15 | MOMENTUM: 0.9 16 | WEIGHT_DECAY: 5e-5 17 | WARMUP_EPOCHS: 5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 224 21 | BATCH_SIZE: 256 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 256 25 | BATCH_SIZE: 200 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnety/RegNetY-200MF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | SE_ON: True 6 | DEPTH: 13 7 | W0: 24 8 | WA: 36.44 9 | WM: 2.49 10 | GROUP_W: 8 11 | OPTIM: 12 | LR_POLICY: cos 13 | BASE_LR: 0.8 14 | MAX_EPOCH: 100 15 | MOMENTUM: 0.9 16 | WEIGHT_DECAY: 5e-5 17 | WARMUP_EPOCHS: 5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 224 21 | BATCH_SIZE: 1024 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 256 25 | BATCH_SIZE: 800 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnety/RegNetY-3.2GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | SE_ON: True 6 | DEPTH: 21 7 | W0: 80 8 | WA: 42.63 9 | WM: 2.66 10 | GROUP_W: 24 11 | OPTIM: 12 | LR_POLICY: cos 13 | BASE_LR: 0.4 14 | MAX_EPOCH: 100 15 | MOMENTUM: 0.9 16 | WEIGHT_DECAY: 5e-5 17 | WARMUP_EPOCHS: 5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 224 21 | BATCH_SIZE: 512 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 256 25 | BATCH_SIZE: 400 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnety/RegNetY-32GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | SE_ON: True 6 | DEPTH: 20 7 | W0: 232 8 | WA: 115.89 9 | WM: 2.53 10 | GROUP_W: 232 11 | OPTIM: 12 | LR_POLICY: cos 13 | BASE_LR: 0.2 14 | MAX_EPOCH: 100 15 | MOMENTUM: 0.9 16 | WEIGHT_DECAY: 5e-5 17 | WARMUP_EPOCHS: 5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 224 21 | BATCH_SIZE: 256 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 256 25 | BATCH_SIZE: 200 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnety/RegNetY-4.0GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | SE_ON: True 6 | DEPTH: 22 7 | W0: 96 8 | WA: 31.41 9 | WM: 2.24 10 | GROUP_W: 64 11 | OPTIM: 12 | LR_POLICY: cos 13 | BASE_LR: 0.4 14 | MAX_EPOCH: 100 15 | MOMENTUM: 0.9 16 | WEIGHT_DECAY: 5e-5 17 | WARMUP_EPOCHS: 5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 224 21 | BATCH_SIZE: 512 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 256 25 | BATCH_SIZE: 400 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnety/RegNetY-400MF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | SE_ON: True 6 | DEPTH: 16 7 | W0: 48 8 | WA: 27.89 9 | WM: 2.09 10 | GROUP_W: 8 11 | OPTIM: 12 | LR_POLICY: cos 13 | BASE_LR: 0.8 14 | MAX_EPOCH: 100 15 | MOMENTUM: 0.9 16 | WEIGHT_DECAY: 5e-5 17 | WARMUP_EPOCHS: 5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 224 21 | BATCH_SIZE: 1024 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 256 25 | BATCH_SIZE: 800 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnety/RegNetY-6.4GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | SE_ON: True 6 | DEPTH: 25 7 | W0: 112 8 | WA: 33.22 9 | WM: 2.27 10 | GROUP_W: 72 11 | OPTIM: 12 | LR_POLICY: cos 13 | BASE_LR: 0.4 14 | MAX_EPOCH: 100 15 | MOMENTUM: 0.9 16 | WEIGHT_DECAY: 5e-5 17 | WARMUP_EPOCHS: 5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 224 21 | BATCH_SIZE: 512 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 256 25 | BATCH_SIZE: 400 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnety/RegNetY-600MF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | SE_ON: True 6 | DEPTH: 15 7 | W0: 48 8 | WA: 32.54 9 | WM: 2.32 10 | GROUP_W: 16 11 | OPTIM: 12 | LR_POLICY: cos 13 | BASE_LR: 0.8 14 | MAX_EPOCH: 100 15 | MOMENTUM: 0.9 16 | WEIGHT_DECAY: 5e-5 17 | WARMUP_EPOCHS: 5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 224 21 | BATCH_SIZE: 1024 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 256 25 | BATCH_SIZE: 800 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnety/RegNetY-8.0GF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | SE_ON: true 6 | DEPTH: 17 7 | W0: 192 8 | WA: 76.82 9 | WM: 2.19 10 | GROUP_W: 56 11 | OPTIM: 12 | LR_POLICY: cos 13 | BASE_LR: 0.4 14 | MAX_EPOCH: 100 15 | MOMENTUM: 0.9 16 | WEIGHT_DECAY: 5e-5 17 | WARMUP_EPOCHS: 5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 224 21 | BATCH_SIZE: 512 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 256 25 | BATCH_SIZE: 400 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/backbones/regnet/regnety/RegNetY-800MF_dds_8gpu.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | TYPE: regnet 3 | NUM_CLASSES: 1000 4 | REGNET: 5 | SE_ON: True 6 | DEPTH: 14 7 | W0: 56 8 | WA: 38.84 9 | WM: 2.4 10 | GROUP_W: 16 11 | OPTIM: 12 | LR_POLICY: cos 13 | BASE_LR: 0.8 14 | MAX_EPOCH: 100 15 | MOMENTUM: 0.9 16 | WEIGHT_DECAY: 5e-5 17 | WARMUP_EPOCHS: 5 18 | TRAIN: 19 | DATASET: imagenet 20 | IM_SIZE: 224 21 | BATCH_SIZE: 1024 22 | TEST: 23 | DATASET: imagenet 24 | IM_SIZE: 256 25 | BATCH_SIZE: 800 26 | NUM_GPUS: 8 27 | OUT_DIR: . 28 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/heads/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .build import REID_HEADS_REGISTRY, build_heads 8 | 9 | # import all the meta_arch, so they will be registered 10 | from .embedding_head import EmbeddingHead 11 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/heads/build.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from ...utils.registry import Registry 8 | 9 | REID_HEADS_REGISTRY = Registry("HEADS") 10 | REID_HEADS_REGISTRY.__doc__ = """ 11 | Registry for ROI heads in a generalized R-CNN model. 12 | ROIHeads take feature maps and region proposals, and 13 | perform per-region computation. 14 | The registered object will be called with `obj(cfg, input_shape)`. 15 | The call is expected to return an :class:`ROIHeads`. 16 | """ 17 | 18 | 19 | def build_heads(cfg, **kwargs): 20 | """ 21 | Build REIDHeads defined by `cfg.MODEL.REID_HEADS.NAME`. 22 | """ 23 | head = cfg.MODEL.HEADS.NAME 24 | return REID_HEADS_REGISTRY.get(head)(cfg, **kwargs) 25 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/losses/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: l1aoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .cross_entroy_loss import cross_entropy_loss, log_accuracy 8 | from .focal_loss import focal_loss 9 | from .triplet_loss import triplet_loss 10 | from .circle_loss import * 11 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/losses/circle_loss.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import torch 8 | import torch.nn.functional as F 9 | 10 | __all__ = ["pairwise_circleloss", "pairwise_cosface"] 11 | 12 | 13 | def pairwise_circleloss( 14 | embedding: torch.Tensor, 15 | targets: torch.Tensor, 16 | margin: float, 17 | gamma: float, ) -> torch.Tensor: 18 | embedding = F.normalize(embedding, dim=1) 19 | 20 | dist_mat = torch.matmul(embedding, embedding.t()) 21 | 22 | N = dist_mat.size(0) 23 | 24 | is_pos = targets.view(N, 1).expand(N, N).eq(targets.view(N, 1).expand(N, N).t()).float() 25 | is_neg = targets.view(N, 1).expand(N, N).ne(targets.view(N, 1).expand(N, N).t()).float() 26 | 27 | # Mask scores related to itself 28 | is_pos = is_pos - torch.eye(N, N, device=is_pos.device) 29 | 30 | s_p = dist_mat * is_pos 31 | s_n = dist_mat * is_neg 32 | 33 | alpha_p = torch.clamp_min(-s_p.detach() + 1 + margin, min=0.) 34 | alpha_n = torch.clamp_min(s_n.detach() + margin, min=0.) 35 | delta_p = 1 - margin 36 | delta_n = margin 37 | 38 | logit_p = - gamma * alpha_p * (s_p - delta_p) + (-99999999.) * (1 - is_pos) 39 | logit_n = gamma * alpha_n * (s_n - delta_n) + (-99999999.) * (1 - is_neg) 40 | 41 | loss = F.softplus(torch.logsumexp(logit_p, dim=1) + torch.logsumexp(logit_n, dim=1)).mean() 42 | 43 | return loss 44 | 45 | 46 | def pairwise_cosface( 47 | embedding: torch.Tensor, 48 | targets: torch.Tensor, 49 | margin: float, 50 | gamma: float, ) -> torch.Tensor: 51 | # Normalize embedding features 52 | embedding = F.normalize(embedding, dim=1) 53 | 54 | dist_mat = torch.matmul(embedding, embedding.t()) 55 | 56 | N = dist_mat.size(0) 57 | is_pos = targets.view(N, 1).expand(N, N).eq(targets.view(N, 1).expand(N, N).t()).float() 58 | is_neg = targets.view(N, 1).expand(N, N).ne(targets.view(N, 1).expand(N, N).t()).float() 59 | 60 | # Mask scores related to itself 61 | is_pos = is_pos - torch.eye(N, N, device=is_pos.device) 62 | 63 | s_p = dist_mat * is_pos 64 | s_n = dist_mat * is_neg 65 | 66 | logit_p = -gamma * s_p + (-99999999.) * (1 - is_pos) 67 | logit_n = gamma * (s_n + margin) + (-99999999.) * (1 - is_neg) 68 | 69 | loss = F.softplus(torch.logsumexp(logit_p, dim=1) + torch.logsumexp(logit_n, dim=1)).mean() 70 | 71 | return loss 72 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/losses/cross_entroy_loss.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: l1aoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | import torch 7 | import torch.nn.functional as F 8 | 9 | from fastreid.utils.events import get_event_storage 10 | 11 | 12 | def log_accuracy(pred_class_logits, gt_classes, topk=(1,)): 13 | """ 14 | Log the accuracy metrics to EventStorage. 15 | """ 16 | bsz = pred_class_logits.size(0) 17 | maxk = max(topk) 18 | _, pred_class = pred_class_logits.topk(maxk, 1, True, True) 19 | pred_class = pred_class.t() 20 | correct = pred_class.eq(gt_classes.view(1, -1).expand_as(pred_class)) 21 | 22 | ret = [] 23 | for k in topk: 24 | correct_k = correct[:k].view(-1).float().sum(dim=0, keepdim=True) 25 | ret.append(correct_k.mul_(1. / bsz)) 26 | 27 | storage = get_event_storage() 28 | storage.put_scalar("cls_accuracy", ret[0]) 29 | 30 | 31 | def cross_entropy_loss(pred_class_outputs, gt_classes, eps, alpha=0.2): 32 | num_classes = pred_class_outputs.size(1) 33 | 34 | if eps >= 0: 35 | smooth_param = eps 36 | else: 37 | # Adaptive label smooth regularization 38 | soft_label = F.softmax(pred_class_outputs, dim=1) 39 | smooth_param = alpha * soft_label[torch.arange(soft_label.size(0)), gt_classes].unsqueeze(1) 40 | 41 | log_probs = F.log_softmax(pred_class_outputs, dim=1) 42 | with torch.no_grad(): 43 | targets = torch.ones_like(log_probs) 44 | targets *= smooth_param / (num_classes - 1) 45 | targets.scatter_(1, gt_classes.data.unsqueeze(1), (1 - smooth_param)) 46 | 47 | loss = (-targets * log_probs).sum(dim=1) 48 | 49 | with torch.no_grad(): 50 | non_zero_cnt = max(loss.nonzero(as_tuple=False).size(0), 1) 51 | 52 | loss = loss.sum() / non_zero_cnt 53 | 54 | return loss 55 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/losses/focal_loss.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import torch 8 | import torch.nn.functional as F 9 | 10 | 11 | # based on: 12 | # https://github.com/kornia/kornia/blob/master/kornia/losses/focal.py 13 | 14 | def focal_loss( 15 | input: torch.Tensor, 16 | target: torch.Tensor, 17 | alpha: float, 18 | gamma: float = 2.0, 19 | reduction: str = 'mean') -> torch.Tensor: 20 | r"""Criterion that computes Focal loss. 21 | See :class:`fastreid.modeling.losses.FocalLoss` for details. 22 | According to [1], the Focal loss is computed as follows: 23 | .. math:: 24 | \text{FL}(p_t) = -\alpha_t (1 - p_t)^{\gamma} \, \text{log}(p_t) 25 | where: 26 | - :math:`p_t` is the model's estimated probability for each class. 27 | Arguments: 28 | alpha (float): Weighting factor :math:`\alpha \in [0, 1]`. 29 | gamma (float): Focusing parameter :math:`\gamma >= 0`. 30 | reduction (str, optional): Specifies the reduction to apply to the 31 | output: ‘none’ | ‘mean’ | ‘sum’. ‘none’: no reduction will be applied, 32 | ‘mean’: the sum of the output will be divided by the number of elements 33 | in the output, ‘sum’: the output will be summed. Default: ‘none’. 34 | Shape: 35 | - Input: :math:`(N, C, *)` where C = number of classes. 36 | - Target: :math:`(N, *)` where each value is 37 | :math:`0 ≤ targets[i] ≤ C−1`. 38 | Examples: 39 | >>> N = 5 # num_classes 40 | >>> loss = FocalLoss(cfg) 41 | >>> input = torch.randn(1, N, 3, 5, requires_grad=True) 42 | >>> target = torch.empty(1, 3, 5, dtype=torch.long).random_(N) 43 | >>> output = loss(input, target) 44 | >>> output.backward() 45 | References: 46 | [1] https://arxiv.org/abs/1708.02002 47 | """ 48 | if not torch.is_tensor(input): 49 | raise TypeError("Input type is not a torch.Tensor. Got {}" 50 | .format(type(input))) 51 | 52 | if not len(input.shape) >= 2: 53 | raise ValueError("Invalid input shape, we expect BxCx*. Got: {}" 54 | .format(input.shape)) 55 | 56 | if input.size(0) != target.size(0): 57 | raise ValueError('Expected input batch_size ({}) to match target batch_size ({}).' 58 | .format(input.size(0), target.size(0))) 59 | 60 | n = input.size(0) 61 | out_size = (n,) + input.size()[2:] 62 | if target.size()[1:] != input.size()[2:]: 63 | raise ValueError('Expected target size {}, got {}'.format( 64 | out_size, target.size())) 65 | 66 | if not input.device == target.device: 67 | raise ValueError( 68 | "input and target must be in the same device. Got: {}".format( 69 | input.device, target.device)) 70 | 71 | # compute softmax over the classes axis 72 | input_soft = F.softmax(input, dim=1) 73 | 74 | # create the labels one hot tensor 75 | target_one_hot = F.one_hot(target, num_classes=input.shape[1]) 76 | 77 | # compute the actual focal loss 78 | weight = torch.pow(-input_soft + 1., gamma) 79 | 80 | focal = -alpha * weight * torch.log(input_soft) 81 | loss_tmp = torch.sum(target_one_hot * focal, dim=1) 82 | 83 | if reduction == 'none': 84 | loss = loss_tmp 85 | elif reduction == 'mean': 86 | loss = torch.mean(loss_tmp) 87 | elif reduction == 'sum': 88 | loss = torch.sum(loss_tmp) 89 | else: 90 | raise NotImplementedError("Invalid reduction mode: {}" 91 | .format(reduction)) 92 | return loss 93 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/losses/utils.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import torch 8 | import torch.nn.functional as F 9 | 10 | 11 | def concat_all_gather(tensor): 12 | """ 13 | Performs all_gather operation on the provided tensors. 14 | *** Warning ***: torch.distributed.all_gather has no gradient. 15 | """ 16 | tensors_gather = [torch.ones_like(tensor) 17 | for _ in range(torch.distributed.get_world_size())] 18 | torch.distributed.all_gather(tensors_gather, tensor, async_op=False) 19 | 20 | output = torch.cat(tensors_gather, dim=0) 21 | return output 22 | 23 | 24 | def normalize(x, axis=-1): 25 | """Normalizing to unit length along the specified dimension. 26 | Args: 27 | x: pytorch Variable 28 | Returns: 29 | x: pytorch Variable, same shape as input 30 | """ 31 | x = 1. * x / (torch.norm(x, 2, axis, keepdim=True).expand_as(x) + 1e-12) 32 | return x 33 | 34 | 35 | def euclidean_dist(x, y): 36 | m, n = x.size(0), y.size(0) 37 | xx = torch.pow(x, 2).sum(1, keepdim=True).expand(m, n) 38 | yy = torch.pow(y, 2).sum(1, keepdim=True).expand(n, m).t() 39 | dist = xx + yy - 2 * torch.matmul(x, y.t()) 40 | dist = dist.clamp(min=1e-12).sqrt() # for numerical stability 41 | return dist 42 | 43 | 44 | def cosine_dist(x, y): 45 | x = F.normalize(x, dim=1) 46 | y = F.normalize(y, dim=1) 47 | dist = 2 - 2 * torch.mm(x, y.t()) 48 | return dist 49 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/meta_arch/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .build import META_ARCH_REGISTRY, build_model 8 | 9 | 10 | # import all the meta_arch, so they will be registered 11 | from .baseline import Baseline 12 | from .mgn import MGN 13 | from .moco import MoCo 14 | from .distiller import Distiller 15 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/meta_arch/build.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | import torch 7 | 8 | from fastreid.utils.registry import Registry 9 | 10 | META_ARCH_REGISTRY = Registry("META_ARCH") # noqa F401 isort:skip 11 | META_ARCH_REGISTRY.__doc__ = """ 12 | Registry for meta-architectures, i.e. the whole model. 13 | The registered object will be called with `obj(cfg)` 14 | and expected to return a `nn.Module` object. 15 | """ 16 | 17 | 18 | def build_model(cfg, **kwargs): 19 | """ 20 | Build the whole model architecture, defined by ``cfg.MODEL.META_ARCHITECTURE``. 21 | Note that it does not load any weights from ``cfg``. 22 | """ 23 | meta_arch = cfg.MODEL.META_ARCHITECTURE 24 | model = META_ARCH_REGISTRY.get(meta_arch)(cfg, **kwargs) 25 | model.to(torch.device(cfg.MODEL.DEVICE)) 26 | return model 27 | -------------------------------------------------------------------------------- /fast_reid/fastreid/modeling/meta_arch/distiller.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: l1aoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | import logging 8 | 9 | import torch 10 | import torch.nn.functional as F 11 | 12 | from fastreid.config import get_cfg 13 | from fastreid.modeling.meta_arch import META_ARCH_REGISTRY, build_model, Baseline 14 | from fastreid.utils.checkpoint import Checkpointer 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | 19 | @META_ARCH_REGISTRY.register() 20 | class Distiller(Baseline): 21 | def __init__(self, cfg): 22 | super(Distiller, self).__init__(cfg) 23 | 24 | # Get teacher model config 25 | cfg_t = get_cfg() 26 | cfg_t.merge_from_file(cfg.KD.MODEL_CONFIG) 27 | 28 | model_t = build_model(cfg_t) 29 | logger.info("Teacher model:\n{}".format(model_t)) 30 | 31 | # No gradients for teacher model 32 | for param in model_t.parameters(): 33 | param.requires_grad_(False) 34 | 35 | logger.info("Loading teacher model weights ...") 36 | Checkpointer(model_t).load(cfg.KD.MODEL_WEIGHTS) 37 | 38 | # Not register teacher model as `nn.Module`, this is 39 | # make sure teacher model weights not saved 40 | self.model_t = [model_t.backbone, model_t.heads] 41 | 42 | def forward(self, batched_inputs): 43 | if self.training: 44 | images = self.preprocess_image(batched_inputs) 45 | # student model forward 46 | s_feat = self.backbone(images) 47 | assert "targets" in batched_inputs, "Labels are missing in training!" 48 | targets = batched_inputs["targets"].to(self.device) 49 | 50 | if targets.sum() < 0: targets.zero_() 51 | 52 | s_outputs = self.heads(s_feat, targets) 53 | 54 | # teacher model forward 55 | with torch.no_grad(): 56 | t_feat = self.model_t[0](images) 57 | t_outputs = self.model_t[1](t_feat, targets) 58 | 59 | losses = self.losses(s_outputs, t_outputs, targets) 60 | return losses 61 | 62 | # Eval mode, just conventional reid feature extraction 63 | else: 64 | return super(Distiller, self).forward(batched_inputs) 65 | 66 | def losses(self, s_outputs, t_outputs, gt_labels): 67 | r""" 68 | Compute loss from modeling's outputs, the loss function input arguments 69 | must be the same as the outputs of the model forwarding. 70 | """ 71 | loss_dict = super(Distiller, self).losses(s_outputs, gt_labels) 72 | 73 | s_logits = s_outputs["pred_class_logits"] 74 | t_logits = t_outputs["pred_class_logits"].detach() 75 | loss_dict["loss_jsdiv"] = self.jsdiv_loss(s_logits, t_logits) 76 | 77 | return loss_dict 78 | 79 | @staticmethod 80 | def _kldiv(y_s, y_t, t): 81 | p_s = F.log_softmax(y_s / t, dim=1) 82 | p_t = F.softmax(y_t / t, dim=1) 83 | loss = F.kl_div(p_s, p_t, reduction="sum") * (t ** 2) / y_s.shape[0] 84 | return loss 85 | 86 | def jsdiv_loss(self, y_s, y_t, t=16): 87 | loss = (self._kldiv(y_s, y_t, t) + self._kldiv(y_t, y_s, t)) / 2 88 | return loss 89 | -------------------------------------------------------------------------------- /fast_reid/fastreid/solver/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | 8 | from .build import build_lr_scheduler, build_optimizer -------------------------------------------------------------------------------- /fast_reid/fastreid/solver/build.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from . import lr_scheduler 8 | from . import optim 9 | 10 | 11 | def build_optimizer(cfg, model): 12 | params = [] 13 | for key, value in model.named_parameters(): 14 | if not value.requires_grad: continue 15 | 16 | lr = cfg.SOLVER.BASE_LR 17 | weight_decay = cfg.SOLVER.WEIGHT_DECAY 18 | if "heads" in key: 19 | lr *= cfg.SOLVER.HEADS_LR_FACTOR 20 | if "bias" in key: 21 | lr *= cfg.SOLVER.BIAS_LR_FACTOR 22 | weight_decay = cfg.SOLVER.WEIGHT_DECAY_BIAS 23 | params += [{"name": key, "params": [value], "lr": lr, "weight_decay": weight_decay}] 24 | 25 | solver_opt = cfg.SOLVER.OPT 26 | if solver_opt == "SGD": 27 | opt_fns = getattr(optim, solver_opt)( 28 | params, 29 | momentum=cfg.SOLVER.MOMENTUM, 30 | nesterov=True if cfg.SOLVER.MOMENTUM and cfg.SOLVER.NESTEROV else False 31 | ) 32 | else: 33 | opt_fns = getattr(optim, solver_opt)(params) 34 | return opt_fns 35 | 36 | 37 | def build_lr_scheduler(cfg, optimizer): 38 | cfg = cfg.clone() 39 | cfg.defrost() 40 | cfg.SOLVER.MAX_EPOCH = cfg.SOLVER.MAX_EPOCH - max( 41 | cfg.SOLVER.WARMUP_EPOCHS + 1, cfg.SOLVER.DELAY_EPOCHS) 42 | 43 | scheduler_dict = {} 44 | 45 | scheduler_args = { 46 | "MultiStepLR": { 47 | "optimizer": optimizer, 48 | # multi-step lr scheduler options 49 | "milestones": cfg.SOLVER.STEPS, 50 | "gamma": cfg.SOLVER.GAMMA, 51 | }, 52 | "CosineAnnealingLR": { 53 | "optimizer": optimizer, 54 | # cosine annealing lr scheduler options 55 | "T_max": cfg.SOLVER.MAX_EPOCH, 56 | "eta_min": cfg.SOLVER.ETA_MIN_LR, 57 | }, 58 | 59 | } 60 | 61 | scheduler_dict["lr_sched"] = getattr(lr_scheduler, cfg.SOLVER.SCHED)( 62 | **scheduler_args[cfg.SOLVER.SCHED]) 63 | 64 | if cfg.SOLVER.WARMUP_EPOCHS > 0: 65 | warmup_args = { 66 | "optimizer": optimizer, 67 | 68 | # warmup options 69 | "warmup_factor": cfg.SOLVER.WARMUP_FACTOR, 70 | "warmup_epochs": cfg.SOLVER.WARMUP_EPOCHS, 71 | "warmup_method": cfg.SOLVER.WARMUP_METHOD, 72 | } 73 | scheduler_dict["warmup_sched"] = lr_scheduler.WarmupLR(**warmup_args) 74 | 75 | return scheduler_dict 76 | -------------------------------------------------------------------------------- /fast_reid/fastreid/solver/lr_scheduler.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: liaoxingyu 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from typing import List 8 | 9 | import torch 10 | from torch.optim.lr_scheduler import * 11 | 12 | 13 | class WarmupLR(torch.optim.lr_scheduler._LRScheduler): 14 | def __init__( 15 | self, 16 | optimizer: torch.optim.Optimizer, 17 | warmup_factor: float = 0.1, 18 | warmup_epochs: int = 10, 19 | warmup_method: str = "linear", 20 | last_epoch: int = -1, 21 | ): 22 | self.warmup_factor = warmup_factor 23 | self.warmup_epochs = warmup_epochs 24 | self.warmup_method = warmup_method 25 | super().__init__(optimizer, last_epoch) 26 | 27 | def get_lr(self) -> List[float]: 28 | warmup_factor = _get_warmup_factor_at_epoch( 29 | self.warmup_method, self.last_epoch, self.warmup_epochs, self.warmup_factor 30 | ) 31 | return [ 32 | base_lr * warmup_factor for base_lr in self.base_lrs 33 | ] 34 | 35 | def _compute_values(self) -> List[float]: 36 | # The new interface 37 | return self.get_lr() 38 | 39 | 40 | def _get_warmup_factor_at_epoch( 41 | method: str, epoch: int, warmup_epochs: int, warmup_factor: float 42 | ) -> float: 43 | """ 44 | Return the learning rate warmup factor at a specific iteration. 45 | See https://arxiv.org/abs/1706.02677 for more details. 46 | Args: 47 | method (str): warmup method; either "constant" or "linear". 48 | epoch (int): epoch at which to calculate the warmup factor. 49 | warmup_epochs (int): the number of warmup epochs. 50 | warmup_factor (float): the base warmup factor (the meaning changes according 51 | to the method used). 52 | Returns: 53 | float: the effective warmup factor at the given iteration. 54 | """ 55 | if epoch >= warmup_epochs: 56 | return 1.0 57 | 58 | if method == "constant": 59 | return warmup_factor 60 | elif method == "linear": 61 | alpha = epoch / warmup_epochs 62 | return warmup_factor * (1 - alpha) + alpha 63 | elif method == "exp": 64 | return warmup_factor ** (1 - epoch / warmup_epochs) 65 | else: 66 | raise ValueError("Unknown warmup method: {}".format(method)) 67 | -------------------------------------------------------------------------------- /fast_reid/fastreid/solver/optim/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from .lamb import Lamb 8 | from .swa import SWA 9 | from torch.optim import * 10 | -------------------------------------------------------------------------------- /fast_reid/fastreid/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: sherlock 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | -------------------------------------------------------------------------------- /fast_reid/fastreid/utils/history_buffer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 3 | 4 | import numpy as np 5 | from typing import List, Tuple 6 | 7 | 8 | class HistoryBuffer: 9 | """ 10 | Track a series of scalar values and provide access to smoothed values over a 11 | window or the global average of the series. 12 | """ 13 | 14 | def __init__(self, max_length: int = 1000000): 15 | """ 16 | Args: 17 | max_length: maximal number of values that can be stored in the 18 | buffer. When the capacity of the buffer is exhausted, old 19 | values will be removed. 20 | """ 21 | self._max_length: int = max_length 22 | self._data: List[Tuple[float, float]] = [] # (value, iteration) pairs 23 | self._count: int = 0 24 | self._global_avg: float = 0 25 | 26 | def update(self, value: float, iteration: float = None): 27 | """ 28 | Add a new scalar value produced at certain iteration. If the length 29 | of the buffer exceeds self._max_length, the oldest element will be 30 | removed from the buffer. 31 | """ 32 | if iteration is None: 33 | iteration = self._count 34 | if len(self._data) == self._max_length: 35 | self._data.pop(0) 36 | self._data.append((value, iteration)) 37 | 38 | self._count += 1 39 | self._global_avg += (value - self._global_avg) / self._count 40 | 41 | def latest(self): 42 | """ 43 | Return the latest scalar value added to the buffer. 44 | """ 45 | return self._data[-1][0] 46 | 47 | def median(self, window_size: int): 48 | """ 49 | Return the median of the latest `window_size` values in the buffer. 50 | """ 51 | return np.median([x[0] for x in self._data[-window_size:]]) 52 | 53 | def avg(self, window_size: int): 54 | """ 55 | Return the mean of the latest `window_size` values in the buffer. 56 | """ 57 | return np.mean([x[0] for x in self._data[-window_size:]]) 58 | 59 | def global_avg(self): 60 | """ 61 | Return the mean of all the elements in the buffer. Note that this 62 | includes those getting removed due to limited buffer storage. 63 | """ 64 | return self._global_avg 65 | 66 | def values(self): 67 | """ 68 | Returns: 69 | list[(number, iteration)]: content of the current buffer. 70 | """ 71 | return self._data 72 | -------------------------------------------------------------------------------- /fast_reid/fastreid/utils/registry.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved 3 | 4 | from typing import Dict, Optional 5 | 6 | 7 | class Registry(object): 8 | """ 9 | The registry that provides name -> object mapping, to support third-party 10 | users' custom modules. 11 | To create a registry (e.g. a backbone registry): 12 | .. code-block:: python 13 | BACKBONE_REGISTRY = Registry('BACKBONE') 14 | To register an object: 15 | .. code-block:: python 16 | @BACKBONE_REGISTRY.register() 17 | class MyBackbone(): 18 | ... 19 | Or: 20 | .. code-block:: python 21 | BACKBONE_REGISTRY.register(MyBackbone) 22 | """ 23 | 24 | def __init__(self, name: str) -> None: 25 | """ 26 | Args: 27 | name (str): the name of this registry 28 | """ 29 | self._name: str = name 30 | self._obj_map: Dict[str, object] = {} 31 | 32 | def _do_register(self, name: str, obj: object) -> None: 33 | assert ( 34 | name not in self._obj_map 35 | ), "An object named '{}' was already registered in '{}' registry!".format( 36 | name, self._name 37 | ) 38 | self._obj_map[name] = obj 39 | 40 | def register(self, obj: object = None) -> Optional[object]: 41 | """ 42 | Register the given object under the the name `obj.__name__`. 43 | Can be used as either a decorator or not. See docstring of this class for usage. 44 | """ 45 | if obj is None: 46 | # used as a decorator 47 | def deco(func_or_class: object) -> object: 48 | name = func_or_class.__name__ # pyre-ignore 49 | self._do_register(name, func_or_class) 50 | return func_or_class 51 | 52 | return deco 53 | 54 | # used as a function call 55 | name = obj.__name__ # pyre-ignore 56 | self._do_register(name, obj) 57 | 58 | def get(self, name: str) -> object: 59 | ret = self._obj_map.get(name) 60 | if ret is None: 61 | raise KeyError( 62 | "No object named '{}' found in '{}' registry!".format( 63 | name, self._name 64 | ) 65 | ) 66 | return ret 67 | -------------------------------------------------------------------------------- /fast_reid/fastreid/utils/timer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 2 | # -*- coding: utf-8 -*- 3 | 4 | from time import perf_counter 5 | from typing import Optional 6 | 7 | 8 | class Timer: 9 | """ 10 | A timer which computes the time elapsed since the start/reset of the timer. 11 | """ 12 | 13 | def __init__(self): 14 | self.reset() 15 | 16 | def reset(self): 17 | """ 18 | Reset the timer. 19 | """ 20 | self._start = perf_counter() 21 | self._paused: Optional[float] = None 22 | self._total_paused = 0 23 | self._count_start = 1 24 | 25 | def pause(self): 26 | """ 27 | Pause the timer. 28 | """ 29 | if self._paused is not None: 30 | raise ValueError("Trying to pause a Timer that is already paused!") 31 | self._paused = perf_counter() 32 | 33 | def is_paused(self) -> bool: 34 | """ 35 | Returns: 36 | bool: whether the timer is currently paused 37 | """ 38 | return self._paused is not None 39 | 40 | def resume(self): 41 | """ 42 | Resume the timer. 43 | """ 44 | if self._paused is None: 45 | raise ValueError("Trying to resume a Timer that is not paused!") 46 | self._total_paused += perf_counter() - self._paused 47 | self._paused = None 48 | self._count_start += 1 49 | 50 | def seconds(self) -> float: 51 | """ 52 | Returns: 53 | (float): the total number of seconds since the start/reset of the 54 | timer, excluding the time when the timer is paused. 55 | """ 56 | if self._paused is not None: 57 | end_time: float = self._paused # type: ignore 58 | else: 59 | end_time = perf_counter() 60 | return end_time - self._start - self._total_paused 61 | 62 | def avg_seconds(self) -> float: 63 | """ 64 | Returns: 65 | (float): the average number of seconds between every start/reset and 66 | pause. 67 | """ 68 | return self.seconds() / self._count_start 69 | -------------------------------------------------------------------------------- /fast_reid/fastreid/utils/weight_init.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | @author: xingyu liao 4 | @contact: sherlockliao01@gmail.com 5 | """ 6 | 7 | from torch import nn 8 | 9 | __all__ = [ 10 | 'weights_init_classifier', 11 | 'weights_init_kaiming', 12 | ] 13 | 14 | 15 | def weights_init_kaiming(m): 16 | classname = m.__class__.__name__ 17 | if classname.find('Linear') != -1: 18 | nn.init.normal_(m.weight, 0, 0.01) 19 | if m.bias is not None: 20 | nn.init.constant_(m.bias, 0.0) 21 | elif classname.find('Conv') != -1: 22 | nn.init.kaiming_normal_(m.weight, mode='fan_out') 23 | if m.bias is not None: 24 | nn.init.constant_(m.bias, 0.0) 25 | elif classname.find('BatchNorm') != -1: 26 | if m.affine: 27 | nn.init.constant_(m.weight, 1.0) 28 | nn.init.constant_(m.bias, 0.0) 29 | 30 | 31 | def weights_init_classifier(m): 32 | classname = m.__class__.__name__ 33 | if classname.find('Linear') != -1: 34 | nn.init.normal_(m.weight, std=0.001) 35 | if m.bias is not None: 36 | nn.init.constant_(m.bias, 0.0) 37 | -------------------------------------------------------------------------------- /fast_reid/query/names.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/fast_reid/query/names.npy -------------------------------------------------------------------------------- /fast_reid/query/query_features.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/fast_reid/query/query_features.npy -------------------------------------------------------------------------------- /hubconf.py: -------------------------------------------------------------------------------- 1 | """File for accessing YOLOv5 via PyTorch Hub https://pytorch.org/hub/ 2 | 3 | Usage: 4 | import torch 5 | model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True, channels=3, classes=80) 6 | """ 7 | 8 | dependencies = ['torch', 'yaml'] 9 | import os 10 | 11 | import torch 12 | 13 | from models.yolo import Model 14 | from utils.google_utils import attempt_download 15 | 16 | 17 | def create(name, pretrained, channels, classes): 18 | """Creates a specified YOLOv5 model 19 | 20 | Arguments: 21 | name (str): name of model, i.e. 'yolov5s' 22 | pretrained (bool): load pretrained weights into the model 23 | channels (int): number of input channels 24 | classes (int): number of model classes 25 | 26 | Returns: 27 | pytorch model 28 | """ 29 | config = os.path.join(os.path.dirname(__file__), 'models', '%s.yaml' % name) # model.yaml path 30 | try: 31 | model = Model(config, channels, classes) 32 | if pretrained: 33 | ckpt = '%s.pt' % name # checkpoint filename 34 | attempt_download(ckpt) # download if not found locally 35 | state_dict = torch.load(ckpt, map_location=torch.device('cpu'))['model'].float().state_dict() # to FP32 36 | state_dict = {k: v for k, v in state_dict.items() if model.state_dict()[k].shape == v.shape} # filter 37 | model.load_state_dict(state_dict, strict=False) # load 38 | return model 39 | 40 | except Exception as e: 41 | help_url = 'https://github.com/ultralytics/yolov5/issues/36' 42 | s = 'Cache maybe be out of date, deleting cache and retrying may solve this. See %s for help.' % help_url 43 | raise Exception(s) from e 44 | 45 | 46 | def yolov5s(pretrained=False, channels=3, classes=80): 47 | """YOLOv5-small model from https://github.com/ultralytics/yolov5 48 | 49 | Arguments: 50 | pretrained (bool): load pretrained weights into the model, default=False 51 | channels (int): number of input channels, default=3 52 | classes (int): number of model classes, default=80 53 | 54 | Returns: 55 | pytorch model 56 | """ 57 | return create('yolov5s', pretrained, channels, classes) 58 | 59 | 60 | def yolov5m(pretrained=False, channels=3, classes=80): 61 | """YOLOv5-medium model from https://github.com/ultralytics/yolov5 62 | 63 | Arguments: 64 | pretrained (bool): load pretrained weights into the model, default=False 65 | channels (int): number of input channels, default=3 66 | classes (int): number of model classes, default=80 67 | 68 | Returns: 69 | pytorch model 70 | """ 71 | return create('yolov5m', pretrained, channels, classes) 72 | 73 | 74 | def yolov5l(pretrained=False, channels=3, classes=80): 75 | """YOLOv5-large model from https://github.com/ultralytics/yolov5 76 | 77 | Arguments: 78 | pretrained (bool): load pretrained weights into the model, default=False 79 | channels (int): number of input channels, default=3 80 | classes (int): number of model classes, default=80 81 | 82 | Returns: 83 | pytorch model 84 | """ 85 | return create('yolov5l', pretrained, channels, classes) 86 | 87 | 88 | def yolov5x(pretrained=False, channels=3, classes=80): 89 | """YOLOv5-xlarge model from https://github.com/ultralytics/yolov5 90 | 91 | Arguments: 92 | pretrained (bool): load pretrained weights into the model, default=False 93 | channels (int): number of input channels, default=3 94 | classes (int): number of model classes, default=80 95 | 96 | Returns: 97 | pytorch model 98 | """ 99 | return create('yolov5x', pretrained, channels, classes) 100 | -------------------------------------------------------------------------------- /kd-r34-r101_ibn/config.yaml: -------------------------------------------------------------------------------- 1 | CUDNN_BENCHMARK: true 2 | DATALOADER: 3 | NAIVE_WAY: true 4 | NUM_INSTANCE: 16 5 | NUM_WORKERS: 8 6 | PK_SAMPLER: true 7 | DATASETS: 8 | COMBINEALL: false 9 | NAMES: 10 | - DukeMTMC 11 | TESTS: 12 | - DukeMTMC 13 | INPUT: 14 | AUGMIX_PROB: 0.0 15 | AUTOAUG_PROB: 0.1 16 | CJ: 17 | BRIGHTNESS: 0.15 18 | CONTRAST: 0.15 19 | ENABLED: false 20 | HUE: 0.1 21 | PROB: 0.5 22 | SATURATION: 0.1 23 | DO_AFFINE: false 24 | DO_AUGMIX: false 25 | DO_AUTOAUG: true 26 | DO_FLIP: true 27 | DO_PAD: true 28 | FLIP_PROB: 0.5 29 | PADDING: 10 30 | PADDING_MODE: constant 31 | REA: 32 | ENABLED: true 33 | PROB: 0.5 34 | VALUE: 35 | - 123.675 36 | - 116.28 37 | - 103.53 38 | RPT: 39 | ENABLED: false 40 | PROB: 0.5 41 | SIZE_TEST: 42 | - 256 43 | - 128 44 | SIZE_TRAIN: 45 | - 256 46 | - 128 47 | KD: 48 | MODEL_CONFIG: ../logs/r101_ibn_19w/config.yaml 49 | MODEL_WEIGHTS: ../logs/r101_ibn_19w/model_best.pth 50 | MODEL: 51 | BACKBONE: 52 | DEPTH: 34x 53 | FEAT_DIM: 512 54 | LAST_STRIDE: 1 55 | NAME: build_resnet_backbone_distill 56 | NORM: BN 57 | PRETRAIN: false 58 | PRETRAIN_PATH: '' 59 | WITH_IBN: false 60 | WITH_NL: false 61 | WITH_SE: false 62 | DEVICE: cuda 63 | FREEZE_LAYERS: 64 | - backbone 65 | HEADS: 66 | CLS_LAYER: circleSoftmax 67 | EMBEDDING_DIM: 0 68 | MARGIN: 0.35 69 | NAME: EmbeddingHead 70 | NECK_FEAT: after 71 | NORM: BN 72 | NUM_CLASSES: 7360 73 | POOL_LAYER: gempoolP 74 | SCALE: 64 75 | WITH_BNNECK: true 76 | LOSSES: 77 | CE: 78 | ALPHA: 0.2 79 | EPSILON: 0.1 80 | SCALE: 1.0 81 | CIRCLE: 82 | GAMMA: 128 83 | MARGIN: 0.25 84 | SCALE: 1.0 85 | COSFACE: 86 | GAMMA: 128 87 | MARGIN: 0.25 88 | SCALE: 1.0 89 | FL: 90 | ALPHA: 0.25 91 | GAMMA: 2 92 | SCALE: 1.0 93 | NAME: 94 | - CrossEntropyLoss 95 | - TripletLoss 96 | TRI: 97 | HARD_MINING: true 98 | MARGIN: 0.0 99 | NORM_FEAT: false 100 | SCALE: 1.0 101 | META_ARCHITECTURE: DistillerOverhaul 102 | PIXEL_MEAN: 103 | - 123.675 104 | - 116.28 105 | - 103.53 106 | PIXEL_STD: 107 | - 58.395 108 | - 57.120000000000005 109 | - 57.375 110 | QUEUE_SIZE: 8192 111 | WEIGHTS: projects/FastDistill/logs/dukemtmc/kd-r34-r101_ibn/model_final.pth 112 | OUTPUT_DIR: projects/FastDistill/logs/dukemtmc/kd-r34-r101_ibn 113 | SOLVER: 114 | BASE_LR: 0.0004 115 | BIAS_LR_FACTOR: 1.0 116 | CHECKPOINT_PERIOD: 5 117 | DELAY_EPOCHS: 5 118 | ETA_MIN_LR: 7.0e-07 119 | FP16_ENABLED: true 120 | FREEZE_FC_ITERS: 0 121 | FREEZE_ITERS: 1000 122 | GAMMA: 0.1 123 | HEADS_LR_FACTOR: 1.0 124 | IMS_PER_BATCH: 256 125 | MAX_EPOCH: 60 126 | MOMENTUM: 0.9 127 | NESTEROV: true 128 | OPT: Adam 129 | SCHED: CosineAnnealingLR 130 | STEPS: 131 | - 40 132 | - 90 133 | WARMUP_EPOCHS: 5 134 | WARMUP_FACTOR: 0.1 135 | WARMUP_METHOD: linear 136 | WEIGHT_DECAY: 0.0005 137 | WEIGHT_DECAY_BIAS: 0.0005 138 | TEST: 139 | AQE: 140 | ALPHA: 3.0 141 | ENABLED: false 142 | QE_K: 5 143 | QE_TIME: 1 144 | EVAL_PERIOD: 5 145 | FLIP_ENABLED: false 146 | IMS_PER_BATCH: 128 147 | METRIC: cosine 148 | PRECISE_BN: 149 | DATASET: Market1501 150 | ENABLED: false 151 | NUM_ITER: 300 152 | RERANK: 153 | ENABLED: false 154 | K1: 20 155 | K2: 6 156 | LAMBDA: 0.3 157 | ROC_ENABLED: false 158 | -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/models/__init__.py -------------------------------------------------------------------------------- /models/export.py: -------------------------------------------------------------------------------- 1 | """Exports a YOLOv5 *.pt model to ONNX and TorchScript formats 2 | 3 | Usage: 4 | $ export PYTHONPATH="$PWD" && python models/export.py --weights ./weights/yolov5s.pt --img 640 --batch 1 5 | """ 6 | 7 | import argparse 8 | 9 | import torch 10 | 11 | from utils.google_utils import attempt_download 12 | from utils.general import set_logging 13 | 14 | if __name__ == '__main__': 15 | parser = argparse.ArgumentParser() 16 | parser.add_argument('--weights', type=str, default='./yolov5s.pt', help='weights path') 17 | parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='image size') 18 | parser.add_argument('--batch-size', type=int, default=1, help='batch size') 19 | opt = parser.parse_args() 20 | opt.img_size *= 2 if len(opt.img_size) == 1 else 1 # expand 21 | print(opt) 22 | set_logging() 23 | 24 | # Input 25 | img = torch.zeros((opt.batch_size, 3, *opt.img_size)) # image size(1,3,320,192) iDetection 26 | 27 | # Load PyTorch model 28 | attempt_download(opt.weights) 29 | model = torch.load(opt.weights, map_location=torch.device('cpu'))['model'].float() 30 | model.eval() 31 | model.model[-1].export = True # set Detect() layer export=True 32 | y = model(img) # dry run 33 | 34 | # TorchScript export 35 | try: 36 | print('\nStarting TorchScript export with torch %s...' % torch.__version__) 37 | f = opt.weights.replace('.pt', '.torchscript.pt') # filename 38 | ts = torch.jit.trace(model, img) 39 | ts.save(f) 40 | print('TorchScript export success, saved as %s' % f) 41 | except Exception as e: 42 | print('TorchScript export failure: %s' % e) 43 | 44 | # ONNX export 45 | try: 46 | import onnx 47 | 48 | print('\nStarting ONNX export with onnx %s...' % onnx.__version__) 49 | f = opt.weights.replace('.pt', '.onnx') # filename 50 | model.fuse() # only for ONNX 51 | torch.onnx.export(model, img, f, verbose=False, opset_version=12, input_names=['images'], 52 | output_names=['classes', 'boxes'] if y is None else ['output']) 53 | 54 | # Checks 55 | onnx_model = onnx.load(f) # load onnx model 56 | onnx.checker.check_model(onnx_model) # check onnx model 57 | print(onnx.helper.printable_graph(onnx_model.graph)) # print a human readable model 58 | print('ONNX export success, saved as %s' % f) 59 | except Exception as e: 60 | print('ONNX export failure: %s' % e) 61 | 62 | # CoreML export 63 | try: 64 | import coremltools as ct 65 | 66 | print('\nStarting CoreML export with coremltools %s...' % ct.__version__) 67 | # convert model from torchscript and apply pixel scaling as per detect.py 68 | model = ct.convert(ts, inputs=[ct.ImageType(name='images', shape=img.shape, scale=1 / 255.0, bias=[0, 0, 0])]) 69 | f = opt.weights.replace('.pt', '.mlmodel') # filename 70 | model.save(f) 71 | print('CoreML export success, saved as %s' % f) 72 | except Exception as e: 73 | print('CoreML export failure: %s' % e) 74 | 75 | # Finish 76 | print('\nExport complete. Visualize with https://github.com/lutzroeder/netron.') 77 | -------------------------------------------------------------------------------- /models/hub/yolov3-spp.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 80 # number of classes 3 | depth_multiple: 1.0 # model depth multiple 4 | width_multiple: 1.0 # layer channel multiple 5 | 6 | # anchors 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # darknet53 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [32, 3, 1]], # 0 16 | [-1, 1, Conv, [64, 3, 2]], # 1-P1/2 17 | [-1, 1, Bottleneck, [64]], 18 | [-1, 1, Conv, [128, 3, 2]], # 3-P2/4 19 | [-1, 2, Bottleneck, [128]], 20 | [-1, 1, Conv, [256, 3, 2]], # 5-P3/8 21 | [-1, 8, Bottleneck, [256]], 22 | [-1, 1, Conv, [512, 3, 2]], # 7-P4/16 23 | [-1, 8, Bottleneck, [512]], 24 | [-1, 1, Conv, [1024, 3, 2]], # 9-P5/32 25 | [-1, 4, Bottleneck, [1024]], # 10 26 | ] 27 | 28 | # YOLOv3-SPP head 29 | head: 30 | [[-1, 1, Bottleneck, [1024, False]], 31 | [-1, 1, SPP, [512, [5, 9, 13]]], 32 | [-1, 1, Conv, [1024, 3, 1]], 33 | [-1, 1, Conv, [512, 1, 1]], 34 | [-1, 1, Conv, [1024, 3, 1]], # 15 (P5/32-large) 35 | 36 | [-2, 1, Conv, [256, 1, 1]], 37 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 38 | [[-1, 8], 1, Concat, [1]], # cat backbone P4 39 | [-1, 1, Bottleneck, [512, False]], 40 | [-1, 1, Bottleneck, [512, False]], 41 | [-1, 1, Conv, [256, 1, 1]], 42 | [-1, 1, Conv, [512, 3, 1]], # 22 (P4/16-medium) 43 | 44 | [-2, 1, Conv, [128, 1, 1]], 45 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 46 | [[-1, 6], 1, Concat, [1]], # cat backbone P3 47 | [-1, 1, Bottleneck, [256, False]], 48 | [-1, 2, Bottleneck, [256, False]], # 27 (P3/8-small) 49 | 50 | [[27, 22, 15], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 51 | ] 52 | -------------------------------------------------------------------------------- /models/hub/yolov5-fpn.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 80 # number of classes 3 | depth_multiple: 1.0 # model depth multiple 4 | width_multiple: 1.0 # layer channel multiple 5 | 6 | # anchors 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Focus, [64, 3]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, Bottleneck, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, BottleneckCSP, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, BottleneckCSP, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 6, BottleneckCSP, [1024]], # 9 25 | ] 26 | 27 | # YOLOv5 FPN head 28 | head: 29 | [[-1, 3, BottleneckCSP, [1024, False]], # 10 (P5/32-large) 30 | 31 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 32 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 33 | [-1, 1, Conv, [512, 1, 1]], 34 | [-1, 3, BottleneckCSP, [512, False]], # 14 (P4/16-medium) 35 | 36 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 37 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 38 | [-1, 1, Conv, [256, 1, 1]], 39 | [-1, 3, BottleneckCSP, [256, False]], # 18 (P3/8-small) 40 | 41 | [[18, 14, 10], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 42 | ] 43 | -------------------------------------------------------------------------------- /models/hub/yolov5-panet.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 80 # number of classes 3 | depth_multiple: 1.0 # model depth multiple 4 | width_multiple: 1.0 # layer channel multiple 5 | 6 | # anchors 7 | anchors: 8 | - [116,90, 156,198, 373,326] # P5/32 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [10,13, 16,30, 33,23] # P3/8 11 | 12 | # YOLOv5 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Focus, [64, 3]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, BottleneckCSP, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, BottleneckCSP, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, BottleneckCSP, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, BottleneckCSP, [1024, False]], # 9 25 | ] 26 | 27 | # YOLOv5 PANet head 28 | head: 29 | [[-1, 1, Conv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, BottleneckCSP, [512, False]], # 13 33 | 34 | [-1, 1, Conv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, BottleneckCSP, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, Conv, [256, 3, 2]], 40 | [[-1, 14], 1, Concat, [1]], # cat head P4 41 | [-1, 3, BottleneckCSP, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, Conv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, BottleneckCSP, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P5, P4, P3) 48 | ] 49 | -------------------------------------------------------------------------------- /models/yolov5l.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 80 # number of classes 3 | depth_multiple: 1.0 # model depth multiple 4 | width_multiple: 1.0 # layer channel multiple 5 | 6 | # anchors 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Focus, [64, 3]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, BottleneckCSP, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, BottleneckCSP, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, BottleneckCSP, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, BottleneckCSP, [1024, False]], # 9 25 | ] 26 | 27 | # YOLOv5 head 28 | head: 29 | [[-1, 1, Conv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, BottleneckCSP, [512, False]], # 13 33 | 34 | [-1, 1, Conv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, BottleneckCSP, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, Conv, [256, 3, 2]], 40 | [[-1, 14], 1, Concat, [1]], # cat head P4 41 | [-1, 3, BottleneckCSP, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, Conv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, BottleneckCSP, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /models/yolov5m.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 80 # number of classes 3 | depth_multiple: 0.67 # model depth multiple 4 | width_multiple: 0.75 # layer channel multiple 5 | 6 | # anchors 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Focus, [64, 3]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, BottleneckCSP, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, BottleneckCSP, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, BottleneckCSP, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, BottleneckCSP, [1024, False]], # 9 25 | ] 26 | 27 | # YOLOv5 head 28 | head: 29 | [[-1, 1, Conv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, BottleneckCSP, [512, False]], # 13 33 | 34 | [-1, 1, Conv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, BottleneckCSP, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, Conv, [256, 3, 2]], 40 | [[-1, 14], 1, Concat, [1]], # cat head P4 41 | [-1, 3, BottleneckCSP, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, Conv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, BottleneckCSP, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /models/yolov5s.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 80 # number of classes 3 | depth_multiple: 0.33 # model depth multiple 4 | width_multiple: 0.50 # layer channel multiple 5 | 6 | # anchors 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Focus, [64, 3]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, BottleneckCSP, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, BottleneckCSP, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, BottleneckCSP, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, BottleneckCSP, [1024, False]], # 9 25 | ] 26 | 27 | # YOLOv5 head 28 | head: 29 | [[-1, 1, Conv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, BottleneckCSP, [512, False]], # 13 33 | 34 | [-1, 1, Conv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, BottleneckCSP, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, Conv, [256, 3, 2]], 40 | [[-1, 14], 1, Concat, [1]], # cat head P4 41 | [-1, 3, BottleneckCSP, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, Conv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, BottleneckCSP, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /models/yolov5x.yaml: -------------------------------------------------------------------------------- 1 | # parameters 2 | nc: 80 # number of classes 3 | depth_multiple: 1.33 # model depth multiple 4 | width_multiple: 1.25 # layer channel multiple 5 | 6 | # anchors 7 | anchors: 8 | - [10,13, 16,30, 33,23] # P3/8 9 | - [30,61, 62,45, 59,119] # P4/16 10 | - [116,90, 156,198, 373,326] # P5/32 11 | 12 | # YOLOv5 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Focus, [64, 3]], # 0-P1/2 16 | [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 17 | [-1, 3, BottleneckCSP, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, BottleneckCSP, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, BottleneckCSP, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, BottleneckCSP, [1024, False]], # 9 25 | ] 26 | 27 | # YOLOv5 head 28 | head: 29 | [[-1, 1, Conv, [512, 1, 1]], 30 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 31 | [[-1, 6], 1, Concat, [1]], # cat backbone P4 32 | [-1, 3, BottleneckCSP, [512, False]], # 13 33 | 34 | [-1, 1, Conv, [256, 1, 1]], 35 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 36 | [[-1, 4], 1, Concat, [1]], # cat backbone P3 37 | [-1, 3, BottleneckCSP, [256, False]], # 17 (P3/8-small) 38 | 39 | [-1, 1, Conv, [256, 3, 2]], 40 | [[-1, 14], 1, Concat, [1]], # cat head P4 41 | [-1, 3, BottleneckCSP, [512, False]], # 20 (P4/16-medium) 42 | 43 | [-1, 1, Conv, [512, 3, 2]], 44 | [[-1, 10], 1, Concat, [1]], # cat head P5 45 | [-1, 3, BottleneckCSP, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /pedestrians-cluster/.idea/deployment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /pedestrians-cluster/.idea/face-cluster-framework.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /pedestrians-cluster/.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /pedestrians-cluster/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pedestrians-cluster/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /pedestrians-cluster/.idea/remote-mappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /pedestrians-cluster/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /pedestrians-cluster/.idea/webServers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /pedestrians-cluster/README-zh.md: -------------------------------------------------------------------------------- 1 | # Face Cluster Framework (人脸聚类框架) 2 | [English Version](https://github.com/xiaoxiong74/face-cluster-framework/blob/master/README.md) | 中文版 [Blog](https://blog.csdn.net/qq_42189083/article/details/110449238) 3 | 4 | 5 | ## Intorduction 6 | 7 | 一个人脸图片聚类框架 8 | 9 | 对于给定的大量待聚类人脸图片,利用人脸特征抽取组件(face_feature_extract)进行人脸特征抽取,并对用抽取的人脸特征进行人脸聚类并进行图片归档。 10 | 采用的人脸聚类算法较当前主流人脸聚类算法效果更优,具体测评效果详见[基于infomap的人脸聚类](https://blog.csdn.net/qq_42189083/article/details/110002878) 11 | 12 | ## Cluster Result 13 | 14 | * 输入数据: 15 | ![](data/tmp/input.png) 16 | 17 | * 部分聚类效果: 18 | 19 | ![](data/tmp/result_0.png) 20 | ![](data/tmp/result_1.png) 21 | 22 | ![](data/tmp/result_2.png) 23 | ![](data/tmp/result_3.png) 24 | 25 | 26 | ## Requirements 27 | * Python >= 3.6 28 | * sklearn 29 | * infomap 30 | * numpy 31 | * faiss-gpu(or faiss-cpu) 32 | * torch >= 1.2 33 | * torchvision 34 | 35 | ## Datasets and Pretrain_models 36 | * 可用测试人脸图片数据10000张(data_sample), [下载地址 BaiduYun](https://pan.baidu.com/s/19Ho011j_ZpIT93aS1gSdrg)(passwd: trka) 37 | * 人脸特征抽取的预训练模型(pretrain_model), [下载地址 BaiduYun](https://pan.baidu.com/s/19Ho011j_ZpIT93aS1gSdrg)(passwd: trka) 38 | * 归档标注后的人脸图片数据10000张(labeled_data_sample), [下载地址 BaiduYun](https://pan.baidu.com/s/19Ho011j_ZpIT93aS1gSdrg)(passwd: trka) 39 | 40 | 41 | ## Run 42 | 1. 将待聚档图片放入到 'data/input_pictures' 目录下 43 | 2. 下载人脸特征抽取的预训练模型,将2个tar文件放到 'pretrain_models' 目录下 44 | 3. 运行: 45 | ```bash 46 | python main.py 47 | ``` 48 | 4. 人脸图片聚类结果目录 'data/output_pictures',每个数字子目录下为同一个人的人脸图片,格式如下: 49 | 50 | ![](data/tmp/output_all.png) 51 | 52 | 53 | ## Evaluate 54 | 55 | 如果想测评聚类效果,可以利用归档标注后的人脸图片数据(如上述下载数据的labeled_data_sample),放到'data/input_pictures'目录下, 56 | 并在main.py中设置is_evaluate=True即可测评聚类效果。不同人脸数据集可以通过调整main.py中的min_sim与k值获得最优参数 57 | 58 | labeled_data_sample数据的聚类指标(调整参数还能提高): 59 | ![](data/tmp/evaluation.png) 60 | 61 | 此外,可以通过利用自己的数据(如戴口罩的人脸数据)进行人脸特征解析模型训练,训练可以参考[hfsoftmax](https://github.com/yl-1993/hfsoftmax) 62 | 63 | ## References 64 | 65 | * [face-cluster](https://github.com/xiaoxiong74/face-cluster-by-infomap) 66 | * [face_feature_extract](https://github.com/yl-1993/hfsoftmax) -------------------------------------------------------------------------------- /pedestrians-cluster/README.md: -------------------------------------------------------------------------------- 1 | # Face Cluster Framework (人脸聚类框架) 2 | 3 | English Version | [中文版](https://blog.csdn.net/qq_42189083/article/details/110449238) 4 | 5 | ## Intorduction 6 | 7 | For a large number of given face images, face feature extraction component is used to extract face features, 8 | and then face clustering model is used for face clustering and archiving. 9 | 10 | ## Requirements 11 | * Python >= 3.6 12 | * sklearn 13 | * infomap 14 | * numpy 15 | * faiss-gpu(or faiss-cpu) 16 | * torch >= 1.2 17 | * torchvision 18 | 19 | ## Datasets and Pretrain_models 20 | 21 | [download test data and pretrain model BaiduYun](https://pan.baidu.com/s/19Ho011j_ZpIT93aS1gSdrg)(passwd: trka) 22 | 23 | Put face pictures in the file directory 'data/input_pictures/'. The format as follow: 24 | 25 | ![](data/tmp/input.png) 26 | 27 | Put the pretrain models in the file directory 'pretrain_models/' 28 | 29 | ``` 30 | 'data_sample': all pictures in a file directory 31 | 32 | 'labeled_data_sample': this data you can evaluate the cluster result with set is_evaluate=True. 33 | 34 | 'pretrain_model': the feature extract pretraind model, you can retrain the model on your data(eg: masked face feature) with the method [hfsoftmax](https://github.com/yl-1993/hfsoftmax) 35 | ``` 36 | 37 | ## Run 38 | 39 | ```bash 40 | python main.py 41 | ``` 42 | 43 | ## Results 44 | 45 | The results in the file directory 'data/output_pictures' with default. 46 | 47 | ![](data/tmp/output_all.png) 48 | 49 | The output directory is constucted as follows: 50 | ``` 51 | . 52 | ├── data 53 | | ├── output_pictures 54 | | ├── ├── 0 55 | | | | └── 1.jpg 56 | | | | └── 2.jpg 57 | | | | └── 3.jpg 58 | | | | └── x.jpg 59 | | ├── ├── 1 60 | | | | └── 1.jpg 61 | | | | └── 2.jpg 62 | | | | └── 3.jpg 63 | | | | └── 4.jpg 64 | | ├── ├── ... 65 | | ├── ├── n 66 | | | | └── 1.jpg 67 | | | | └── 2.jpg 68 | | | | └── 3.jpg 69 | 70 | all pictures in n file directory are the same person. 71 | ``` 72 | ![](data/tmp/result_0.png) 73 | 74 | ![](data/tmp/result_1.png) 75 | 76 | ![](data/tmp/result_2.png) 77 | 78 | ![](data/tmp/result_3.png) 79 | 80 | ## Evaluate 81 | 82 | If you want evaluate the cluster result, you should label and organize the input pictures like the data 'labeled_data_sample' with the format as follow: 83 | ``` 84 | . 85 | ├── data 86 | | ├── input_pictures 87 | | ├── ├── people_0 88 | | | | └── 1.jpg 89 | | | | └── 2.jpg 90 | | | | └── 3.jpg 91 | | | | └── x.jpg 92 | | ├── ├── people_2 93 | | | | └── 1.jpg 94 | | | | └── 2.jpg 95 | | | | └── 3.jpg 96 | | | | └── 4.jpg 97 | | ├── ├── ... 98 | | ├── ├── people_n 99 | | | | └── 1.jpg 100 | | | | └── 2.jpg 101 | | | | └── 3.jpg 102 | 103 | all pictures in people_n file directory are the same person. 104 | 105 | In addition, you should set is_evaluate=True. 106 | ``` 107 | 108 | ## References 109 | 110 | * [face-cluster](https://github.com/xiaoxiong74/face-cluster-by-infomap) 111 | * [face_feature_extract](https://github.com/yl-1993/hfsoftmax) -------------------------------------------------------------------------------- /pedestrians-cluster/bin/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/pedestrians-cluster/bin/README.md -------------------------------------------------------------------------------- /pedestrians-cluster/data/input_pictures/README.md: -------------------------------------------------------------------------------- 1 | ## Datasets 2 | 3 | [download test data BaiduYun](https://pan.baidu.com/s/19Ho011j_ZpIT93aS1gSdrg)(passwd: trka) -------------------------------------------------------------------------------- /pedestrians-cluster/data/input_pictures/alldata: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/pedestrians-cluster/data/input_pictures/alldata -------------------------------------------------------------------------------- /pedestrians-cluster/data/output_pictures/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/pedestrians-cluster/data/output_pictures/README.md -------------------------------------------------------------------------------- /pedestrians-cluster/data/tmp/README.md: -------------------------------------------------------------------------------- 1 | ## save some temp result -------------------------------------------------------------------------------- /pedestrians-cluster/evaluation/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from .metrics import * 5 | from .evaluate import evaluate 6 | -------------------------------------------------------------------------------- /pedestrians-cluster/evaluation/evaluate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import inspect 5 | import argparse 6 | import numpy as np 7 | 8 | from evaluation import metrics 9 | from tools.utils import Timer, TextColors 10 | 11 | 12 | def _read_meta(fn): 13 | labels = list() 14 | lb_set = set() 15 | with open(fn) as f: 16 | for lb in f.readlines(): 17 | lb = int(lb.strip()) 18 | labels.append(lb) 19 | lb_set.add(lb) 20 | return np.array(labels), lb_set 21 | 22 | 23 | def evaluate(gt_labels, pred_labels, metric='pairwise'): 24 | if isinstance(gt_labels, str) and isinstance(pred_labels, str): 25 | print('[gt_labels] {}'.format(gt_labels)) 26 | print('[pred_labels] {}'.format(pred_labels)) 27 | gt_labels, gt_lb_set = _read_meta(gt_labels) 28 | pred_labels, pred_lb_set = _read_meta(pred_labels) 29 | 30 | print('#inst: gt({}) vs pred({})'.format(len(gt_labels), 31 | len(pred_labels))) 32 | print('#cls: gt({}) vs pred({})'.format(len(gt_lb_set), 33 | len(pred_lb_set))) 34 | 35 | metric_func = metrics.__dict__[metric] 36 | 37 | with Timer('evaluate with {}{}{}'.format(TextColors.FATAL, metric, 38 | TextColors.ENDC)): 39 | result = metric_func(gt_labels, pred_labels) 40 | if isinstance(result, np.float): 41 | print('{}{}: {:.4f}{}'.format(TextColors.OKGREEN, metric, result, 42 | TextColors.ENDC)) 43 | else: 44 | ave_pre, ave_rec, fscore = result 45 | print('{}ave_pre: {:.4f}, ave_rec: {:.4f}, fscore: {:.4f}{}'.format( 46 | TextColors.OKGREEN, ave_pre, ave_rec, fscore, TextColors.ENDC)) 47 | 48 | 49 | if __name__ == '__main__': 50 | metric_funcs = inspect.getmembers(metrics, inspect.isfunction) 51 | metric_names = [n for n, _ in metric_funcs] 52 | 53 | parser = argparse.ArgumentParser(description='Evaluate Cluster') 54 | parser.add_argument('--gt_labels', type=str, required=True) 55 | parser.add_argument('--pred_labels', type=str, required=True) 56 | parser.add_argument('--metric', default='pairwise', choices=metric_names) 57 | args = parser.parse_args() 58 | 59 | evaluate(args.gt_labels, args.pred_labels, args.metric) 60 | -------------------------------------------------------------------------------- /pedestrians-cluster/face_cluster/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/pedestrians-cluster/face_cluster/__init__.py -------------------------------------------------------------------------------- /pedestrians-cluster/face_feature_extract/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/pedestrians-cluster/face_feature_extract/__init__.py -------------------------------------------------------------------------------- /pedestrians-cluster/face_feature_extract/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | from .filelist_dataset import FileListDataset 2 | from .bin_dataset import BinDataset, GenDataset 3 | from .sampler import * 4 | -------------------------------------------------------------------------------- /pedestrians-cluster/face_feature_extract/datasets/bin_dataset.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from tools.utils import bin_loader 3 | from torch.utils.data import Dataset 4 | from tools.utils import get_img 5 | 6 | 7 | class BinDataset(Dataset): 8 | def __init__(self, bin_file, transform=None): 9 | self.img_lst, _ = bin_loader(bin_file) 10 | self.num = len(self.img_lst) 11 | self.transform = transform 12 | 13 | def __len__(self): 14 | return self.num 15 | 16 | def _read(self, idx=None): 17 | if idx == None: 18 | idx = np.random.randint(self.num) 19 | try: 20 | img = self.img_lst[idx] 21 | return img 22 | except Exception as err: 23 | print('Read image[{}, {}] failed ({})'.format(idx, fn, err)) 24 | return self._read() 25 | 26 | def __getitem__(self, idx): 27 | img = self._read(idx) 28 | if self.transform is not None: 29 | img = self.transform(img) 30 | return img 31 | 32 | 33 | class GenDataset(Dataset): 34 | def __init__(self, filepath='../data/input_pictures', is_evaluate=False, transform=None): 35 | self.img_lst = get_img(filepath, is_evaluate) 36 | self.num = len(self.img_lst) 37 | self.transform = transform 38 | 39 | def __len__(self): 40 | return self.num 41 | 42 | def _read(self, idx=None): 43 | if idx == None: 44 | idx = np.random.randint(self.num) 45 | try: 46 | img = self.img_lst[idx] 47 | return img 48 | except Exception as err: 49 | print('Read image[{}, {}] failed ({})'.format(idx, fn, err)) 50 | return self._read() 51 | 52 | def __getitem__(self, idx): 53 | img = self._read(idx) 54 | if self.transform is not None: 55 | img = self.transform(img) 56 | return img 57 | -------------------------------------------------------------------------------- /pedestrians-cluster/face_feature_extract/datasets/filelist_dataset.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | from tools.utils import pil_loader 4 | from torch.utils.data import Dataset 5 | 6 | 7 | def build_dataset(filelist): 8 | img_lst = [] 9 | lb_lst = [] 10 | lb_max = -1 11 | with open(filelist) as f: 12 | for x in f.readlines(): 13 | try: 14 | n, lb = x.strip().split(' ') 15 | lb = int(lb) 16 | except: 17 | n = x.strip() 18 | lb = -1 19 | lb_max = max(lb_max, lb) 20 | img_lst.append(n) 21 | lb_lst.append(lb) 22 | assert len(img_lst) == len(lb_lst) 23 | return img_lst, lb_lst, lb_max 24 | 25 | 26 | class FileListDataset(Dataset): 27 | def __init__(self, filelist, prefix, transform=None): 28 | self.img_lst, self.lb_lst, self.num_classes = build_dataset( 29 | filelist) 30 | self.num = len(self.img_lst) 31 | self.prefix = prefix 32 | self.transform = transform 33 | 34 | def __len__(self): 35 | return self.num 36 | 37 | def _read(self, idx=None): 38 | if idx is None: 39 | idx = np.random.randint(self.num) 40 | fn = os.path.join(self.prefix, self.img_lst[idx]) 41 | lb = self.lb_lst[idx] 42 | try: 43 | img = pil_loader(open(fn, 'rb').read()) 44 | return img, lb 45 | except Exception as err: 46 | print('Read image[{}, {}] failed ({})'.format(idx, fn, err)) 47 | return self._read() 48 | 49 | def __getitem__(self, idx): 50 | img, lb = self._read(idx) 51 | if self.transform is not None: 52 | img = self.transform(img) 53 | if self.num_classes > -1: 54 | return img, lb 55 | else: 56 | return img 57 | -------------------------------------------------------------------------------- /pedestrians-cluster/face_feature_extract/datasets/sampler.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | import torch 4 | from torch.utils.data.sampler import Sampler 5 | 6 | 7 | __all__ = ["DistSequentialSampler"] 8 | 9 | 10 | class DistSequentialSampler(Sampler): 11 | def __init__(self, dataset, world_size, rank): 12 | assert rank >= 0 13 | assert dataset.num >= world_size, '{} vs {}'.format(dataset.size, world_size) 14 | sub_num = int(math.ceil(1. * dataset.num / world_size)) 15 | # add extra samples to make it evenly divisible 16 | tot_num = sub_num * world_size 17 | self.dsize = dataset.num 18 | self.beg = sub_num * rank 19 | self.end = min(self.beg + sub_num, tot_num) 20 | 21 | def __iter__(self): 22 | indices = [i % self.dsize for i in range(self.beg, self.end)] 23 | return iter(indices) 24 | 25 | def __len__(self): 26 | return self.end - self.beg 27 | -------------------------------------------------------------------------------- /pedestrians-cluster/face_feature_extract/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .resnet import * 2 | from .hynet import * 3 | from .ir import * 4 | from .classifier import * 5 | from .ext_layers import ParameterClient 6 | 7 | __factory_classifier__ = { 8 | 'linear': Classifier, 9 | 'cosface': CosFaceClassifier, 10 | 'hf': HFClassifier, 11 | 'hnsw': HNSWClassifier, 12 | } 13 | 14 | 15 | def build_classifier(name, model, **kwargs): 16 | if name not in __factory_classifier__: 17 | raise KeyError("Unknown classifier:", name) 18 | return __factory_classifier__[name](model, **kwargs) 19 | -------------------------------------------------------------------------------- /pedestrians-cluster/face_feature_extract/models/classifier.py: -------------------------------------------------------------------------------- 1 | import math 2 | import torch 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | from torch.nn.parameter import Parameter 6 | 7 | from .ext_layers import HFSampler, HNSWSampler, Ident 8 | 9 | __all__ = ['Classifier', 'CosFaceClassifier', 'HFClassifier', 'HNSWClassifier'] 10 | 11 | 12 | class Classifier(nn.Module): 13 | def __init__(self, base, feature_dim, num_classes, **kwargs): 14 | super(Classifier, self).__init__() 15 | self.base = base 16 | self.dropout = nn.Dropout(p=0.5) 17 | self.logits = nn.Linear(feature_dim, num_classes) 18 | 19 | def forward(self, x, label): 20 | # input label here maintain the same api, 21 | # which is actually useless 22 | x = self.base(x) 23 | x = self.dropout(x) 24 | x = self.logits(x) 25 | return x 26 | 27 | 28 | class CosFaceClassifier(nn.Module): 29 | def __init__(self, base, feature_dim, num_classes, s=64, m=0.2, **kwargs): 30 | super(CosFaceClassifier, self).__init__() 31 | self.base = base 32 | self.feature_dim = feature_dim 33 | self.num_classes = num_classes 34 | self.s = s 35 | self.m = m 36 | assert s > 0 37 | assert 0 < m <= 1 38 | self.weight = Parameter(torch.Tensor(num_classes, feature_dim)) 39 | self.reset_parameters() 40 | 41 | def reset_parameters(self): 42 | stdv = 1. / math.sqrt(self.weight.size(1)) 43 | self.weight.data.uniform_(-stdv, stdv) 44 | 45 | def __repr__(self): 46 | return ('feature_dim={}, num_classes={}, s={}, m={}'.format( 47 | self.feature_dim, self.num_classes, self.s, self.m)) 48 | 49 | def forward(self, x, label): 50 | embed = self.base(x) 51 | n_weight = F.normalize(self.weight, p=2, dim=1) 52 | n_embed = F.normalize(embed, p=2, dim=1) 53 | out = F.linear(n_embed, n_weight) 54 | zero = torch.cuda.FloatTensor(out.shape).fill_(0) 55 | out -= zero.scatter_(1, label.view(-1, 1), self.m) 56 | out *= self.s 57 | return out 58 | 59 | 60 | def var_hook(grad): 61 | # hook can be used to send grad to ParameterServer 62 | return grad 63 | 64 | 65 | class HFClassifier(nn.Module): 66 | def __init__(self, base, rank, feature_dim, sampler_num, num_classes): 67 | super(HFClassifier, self).__init__() 68 | self.base = base 69 | self.dropout = nn.Dropout(p=0.5) 70 | self.hf_sampler = HFSampler(rank, feature_dim, sampler_num, 71 | num_classes) 72 | 73 | def forward(self, x, labels): 74 | x = self.base(x) 75 | x = self.dropout(x) 76 | w, b, labels = self.hf_sampler(x, labels) 77 | labels = labels.detach() 78 | # w.register_hook(var_hook) 79 | # asssert w.requires_grad == True 80 | x = torch.mm(x, w.t()) + b 81 | return x, labels 82 | 83 | 84 | class HNSWClassifier(nn.Module): 85 | def __init__(self, base, rank, feature_dim, sampler_num, num_classes): 86 | super(HNSWClassifier, self).__init__() 87 | self.base = base 88 | self.dropout = nn.Dropout(p=0.5) 89 | self.hnsw_sampler = HNSWSampler(rank, feature_dim, sampler_num, 90 | num_classes) 91 | 92 | def forward(self, x, labels): 93 | x = self.base(x) 94 | x = self.dropout(x) 95 | w, b, labels = self.hnsw_sampler(x, labels) 96 | labels = labels.detach() 97 | x = torch.mm(x, w.t()) + b 98 | return x, labels 99 | -------------------------------------------------------------------------------- /pedestrians-cluster/face_feature_extract/models/ext_layers/__init__.py: -------------------------------------------------------------------------------- 1 | from .hf_sampler import HFSampler 2 | from .hnsw_sampler import HNSWSampler 3 | from .ident import Ident 4 | from .paramclient import ParameterClient 5 | -------------------------------------------------------------------------------- /pedestrians-cluster/face_feature_extract/models/ext_layers/ident.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch.autograd import Function 3 | from torch.nn.modules.module import Module 4 | 5 | 6 | class IdentFunc(Function): 7 | def __init__(self): 8 | pass 9 | 10 | def forward(self, features): 11 | return features 12 | 13 | def backward(self, grad): 14 | return grad 15 | 16 | 17 | class Ident(Module): 18 | def __init__(self): 19 | super(Ident, self).__init__() 20 | self.ident = IdentFunc() 21 | 22 | def __repr__(self): 23 | return ('{name}'.format(name=self.__class__.__name__)) 24 | 25 | def forward(self, features): 26 | return IdentFunc()(features) 27 | -------------------------------------------------------------------------------- /pedestrians-cluster/face_feature_extract/models/hynet.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | __all__ = ['hynet'] 4 | 5 | 6 | cfg = { 7 | '24642': [32, 64, 'M', \ 8 | 64, 64, 96, 128, 'M', \ 9 | 128, 128, 160, 160, 192, 256, 'M', \ 10 | 256, 256, 256, 512, 'M', \ 11 | 512] 12 | } 13 | 14 | 15 | class Hynet(nn.Module): 16 | def __init__(self, base, feature_dim=256): 17 | super(Hynet, self).__init__() 18 | # TODO: enable different types of fully-connected 19 | self.base = base 20 | self.dropout = nn.Dropout(p=0.5) 21 | self.fc = nn.Linear(256 * 7 * 7, feature_dim) 22 | 23 | for m in self.modules(): 24 | if isinstance(m, nn.Conv2d): 25 | nn.init.kaiming_normal_(m.weight, 26 | mode='fan_out', 27 | nonlinearity='relu') 28 | elif isinstance(m, nn.BatchNorm2d): 29 | nn.init.constant_(m.weight, 1) 30 | nn.init.constant_(m.bias, 0) 31 | 32 | def forward(self, x): 33 | x = self.base(x) 34 | x = x.view(x.size(0), -1) 35 | x = self.dropout(x) 36 | x = self.fc(x) 37 | 38 | return x 39 | 40 | 41 | def make_layers(cfg): 42 | layers = [] 43 | in_channels = 3 44 | out_channels = 256 45 | for v in cfg: 46 | if v == 'M': 47 | layers += [nn.MaxPool2d(kernel_size=2, stride=2)] 48 | else: 49 | conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1) 50 | layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)] 51 | in_channels = v 52 | layers += [nn.Conv2d(in_channels, out_channels, 1, bias=False), \ 53 | nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True)] 54 | return nn.Sequential(*layers) 55 | 56 | 57 | def hynet(**kwargs): 58 | """Constructs a hynet model. 59 | """ 60 | model = Hynet(make_layers(cfg['24642']), **kwargs) 61 | return model 62 | -------------------------------------------------------------------------------- /pedestrians-cluster/pretrain_models/README.md: -------------------------------------------------------------------------------- 1 | ## pretain models 2 | 3 | [download pretrain model BaiduYun](https://pan.baidu.com/s/19Ho011j_ZpIT93aS1gSdrg)(passwd: trka) -------------------------------------------------------------------------------- /pedestrians-cluster/pretrain_models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/pedestrians-cluster/pretrain_models/__init__.py -------------------------------------------------------------------------------- /pedestrians-cluster/requirements.txt: -------------------------------------------------------------------------------- 1 | tqdm 2 | numpy>=1.18.1 3 | scipy 4 | sklearn 5 | infomap 6 | faiss-gpu 7 | torch>=1.2 8 | torchvision -------------------------------------------------------------------------------- /pedestrians-cluster/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/pedestrians-cluster/tools/__init__.py -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # pip install -r requirements.txt 2 | Cython 3 | matplotlib>=3.2.2 4 | numpy>=1.18.5 5 | opencv-python>=4.1.2 6 | pillow 7 | # pycocotools>=2.0 8 | PyYAML>=5.3 9 | scipy>=1.4.1 10 | tensorboard>=2.2 11 | torch>=1.6.0 12 | torchvision>=0.7.0 13 | tqdm>=4.41.0 14 | 15 | # Conda commands (in place of pip) --------------------------------------------- 16 | # conda update -yn base -c defaults conda 17 | # conda install -yc anaconda numpy opencv matplotlib tqdm pillow ipython 18 | # conda install -yc conda-forge scikit-image pycocotools tensorboard 19 | # conda install -yc spyder-ide spyder-line-profiler 20 | # conda install -yc pytorch pytorch torchvision 21 | # conda install -yc conda-forge protobuf numpy && pip install onnx==1.6.0 # https://github.com/onnx/onnx#linux-and-macos 22 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengwb-lx/Yolov5-Deepsort-Fastreid/f01a7b9ec856fda5dc6dedb3384e24e0ca6b311e/utils/__init__.py -------------------------------------------------------------------------------- /utils/activations.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | 5 | 6 | # Swish https://arxiv.org/pdf/1905.02244.pdf --------------------------------------------------------------------------- 7 | class Swish(nn.Module): # 8 | @staticmethod 9 | def forward(x): 10 | return x * torch.sigmoid(x) 11 | 12 | 13 | class HardSwish(nn.Module): 14 | @staticmethod 15 | def forward(x): 16 | return x * F.hardtanh(x + 3, 0., 6., True) / 6. 17 | 18 | 19 | class MemoryEfficientSwish(nn.Module): 20 | class F(torch.autograd.Function): 21 | @staticmethod 22 | def forward(ctx, x): 23 | ctx.save_for_backward(x) 24 | return x * torch.sigmoid(x) 25 | 26 | @staticmethod 27 | def backward(ctx, grad_output): 28 | x = ctx.saved_tensors[0] 29 | sx = torch.sigmoid(x) 30 | return grad_output * (sx * (1 + x * (1 - sx))) 31 | 32 | def forward(self, x): 33 | return self.F.apply(x) 34 | 35 | 36 | # Mish https://github.com/digantamisra98/Mish -------------------------------------------------------------------------- 37 | class Mish(nn.Module): 38 | @staticmethod 39 | def forward(x): 40 | return x * F.softplus(x).tanh() 41 | 42 | 43 | class MemoryEfficientMish(nn.Module): 44 | class F(torch.autograd.Function): 45 | @staticmethod 46 | def forward(ctx, x): 47 | ctx.save_for_backward(x) 48 | return x.mul(torch.tanh(F.softplus(x))) # x * tanh(ln(1 + exp(x))) 49 | 50 | @staticmethod 51 | def backward(ctx, grad_output): 52 | x = ctx.saved_tensors[0] 53 | sx = torch.sigmoid(x) 54 | fx = F.softplus(x).tanh() 55 | return grad_output * (fx + x * sx * (1 - fx * fx)) 56 | 57 | def forward(self, x): 58 | return self.F.apply(x) 59 | 60 | 61 | # FReLU https://arxiv.org/abs/2007.11824 ------------------------------------------------------------------------------- 62 | class FReLU(nn.Module): 63 | def __init__(self, c1, k=3): # ch_in, kernel 64 | super().__init__() 65 | self.conv = nn.Conv2d(c1, c1, k, 1, 1, groups=c1) 66 | self.bn = nn.BatchNorm2d(c1) 67 | 68 | def forward(self, x): 69 | return torch.max(x, self.bn(self.conv(x))) 70 | -------------------------------------------------------------------------------- /utils/draw.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | 4 | palette = (2 ** 11 - 1, 2 ** 15 - 1, 2 ** 20 - 1) 5 | 6 | 7 | def compute_color_for_labels(label): 8 | """ 9 | Simple function that adds fixed color depending on the class 10 | """ 11 | color = [int((p * (label ** 2 - label + 1)) % 255) for p in palette] 12 | return tuple(color) 13 | 14 | 15 | def draw_boxes(img, bbox, identities=None, offset=(0, 0)): 16 | for i, box in enumerate(bbox): 17 | x1, y1, x2, y2 = [int(i) for i in box] 18 | x1 += offset[0] 19 | x2 += offset[0] 20 | y1 += offset[1] 21 | y2 += offset[1] 22 | # box text and bar 23 | id = int(identities[i]) if identities is not None else 0 24 | color = compute_color_for_labels(id) 25 | label = '{}{:d}'.format("ID:", id) 26 | t_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_PLAIN, 2, 2)[0] 27 | cv2.rectangle(img, (x1, y1), (x2, y2), color, 3) 28 | cv2.rectangle( 29 | img, (x1, y1), (x1 + t_size[0] + 3, y1 + t_size[1] + 4), color, -1) 30 | cv2.putText(img, label, (x1, y1 + 31 | t_size[1] + 4), cv2.FONT_HERSHEY_PLAIN, 2, [255, 255, 255], 2) 32 | return img 33 | 34 | 35 | def draw_person(img, bbox_xyxy, reid_results, names, identities=None, offset=(0, 0)): 36 | for i, x in enumerate(bbox_xyxy): 37 | person_name = names[reid_results[i]] 38 | t_size = cv2.getTextSize(person_name, cv2.FONT_HERSHEY_PLAIN, 1, 1)[0] 39 | color = compute_color_for_labels(0) 40 | c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3])) 41 | # 截取行人用来erson_bank.py获取行人特征 42 | ''' 43 | p = img[int(x[1]):int(x[3]), int(x[0]):int(x[2])] 44 | cv2.imwrite('/home/zengwb/Documents/yolov5-fastreid/fast_reid/query/a1/a1.jpg', p) 45 | cv2.imshow('p', p) 46 | cv2.waitKey(0) 47 | ''' 48 | cv2.rectangle(img, c1, c2, color, lineType=cv2.LINE_AA) 49 | cv2.rectangle( 50 | img, (c2[0] - t_size[0]-3, c2[1]-t_size[1] - 4), c2, color, -1) 51 | cv2.putText(img, person_name, (c2[0] - t_size[0]-3, c2[1]), cv2.FONT_HERSHEY_PLAIN, 2, [255, 255, 255], 2) 52 | # cv2.rectangle( 53 | # img, (c1[0] + 2*t_size[0], c1[1]), (c1[0] + 2*t_size[0] + 3, c1[1] + t_size[1] + 4), color, -1) 54 | # cv2.putText(img, person_name, (c1[0], c1[1] + 55 | # t_size[1] + 4), cv2.FONT_HERSHEY_PLAIN, 2, [255, 255, 255], 2) 56 | # cv2.rectangle(img, (c2[0]+t_size[0] +3, c2[1]+t_size[1]+4), (c2[0]+2*t_size[0] +3, c2[1]+2*t_size[1]+4), color, -1) 57 | # cv2.putText(img, person_name, (c2[0]+t_size[0] +3, c2[1]+2*t_size[1]+4), cv2.FONT_HERSHEY_PLAIN, 2, [255, 255, 255], 2) 58 | # if label: 59 | # tf = max(tl - 1, 1) # font thickness 60 | # t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0] 61 | # c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3 62 | # cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA) # filled 63 | # cv2.putText(img, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA) 64 | return img 65 | 66 | 67 | if __name__ == '__main__': 68 | for i in range(82): 69 | print(compute_color_for_labels(i)) 70 | -------------------------------------------------------------------------------- /utils/log.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | 4 | def get_logger(name='root'): 5 | formatter = logging.Formatter( 6 | # fmt='%(asctime)s [%(levelname)s]: %(filename)s(%(funcName)s:%(lineno)s) >> %(message)s') 7 | fmt='%(asctime)s [%(levelname)s]: %(message)s', datefmt='%Y-%m-%d %H:%M:%S') 8 | 9 | handler = logging.StreamHandler() 10 | handler.setFormatter(formatter) 11 | 12 | logger = logging.getLogger(name) 13 | logger.setLevel(logging.INFO) 14 | logger.addHandler(handler) 15 | return logger 16 | 17 | 18 | -------------------------------------------------------------------------------- /utils/parser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import yaml 3 | from easydict import EasyDict as edict 4 | 5 | class YamlParser(edict): 6 | """ 7 | This is yaml parser based on EasyDict. 8 | """ 9 | def __init__(self, cfg_dict=None, config_file=None): 10 | if cfg_dict is None: 11 | cfg_dict = {} 12 | 13 | if config_file is not None: 14 | assert(os.path.isfile(config_file)) 15 | with open(config_file, 'r') as fo: 16 | cfg_dict.update(yaml.load(fo.read())) 17 | 18 | super(YamlParser, self).__init__(cfg_dict) 19 | 20 | 21 | def merge_from_file(self, config_file): 22 | with open(config_file, 'r') as fo: 23 | self.update(yaml.load(fo.read())) 24 | 25 | 26 | def merge_from_dict(self, config_dict): 27 | self.update(config_dict) 28 | 29 | 30 | def get_config(config_file=None): 31 | return YamlParser(config_file=config_file) 32 | 33 | 34 | if __name__ == "__main__": 35 | cfg = YamlParser(config_file="../configs/yolov3.yaml") 36 | cfg.merge_from_file("../configs/deep_sort.yaml") 37 | 38 | import ipdb; ipdb.set_trace() -------------------------------------------------------------------------------- /weights/download_weights.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Download common models 3 | 4 | python -c " 5 | from utils.google_utils import *; 6 | attempt_download('weights/yolov5s.pt'); 7 | attempt_download('weights/yolov5m.pt'); 8 | attempt_download('weights/yolov5l.pt'); 9 | attempt_download('weights/yolov5x.pt') 10 | " 11 | --------------------------------------------------------------------------------