├── README.md ├── coco_classes.txt ├── count.py ├── deep_sort_pytorch ├── .gitignore ├── LICENSE ├── README.md ├── configs │ └── deep_sort.yaml ├── deep_sort │ ├── README.md │ ├── __init__.py │ ├── deep │ │ ├── __init__.py │ │ ├── checkpoint │ │ │ └── .gitkeep │ │ ├── evaluate.py │ │ ├── feature_extractor.py │ │ ├── model.py │ │ ├── original_model.py │ │ ├── test.py │ │ ├── train.jpg │ │ └── train.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 └── utils │ ├── __init__.py │ ├── asserts.py │ ├── draw.py │ ├── evaluation.py │ ├── io.py │ ├── json_logger.py │ ├── log.py │ ├── parser.py │ └── tools.py ├── inference ├── input │ ├── test.mp4 │ ├── test2.mp4 │ └── test3.mp4 └── output │ ├── number.txt │ ├── results.txt │ └── test3.mp4 ├── requirements.txt ├── test.gif ├── test3.gif ├── track.py └── yolov5 ├── .dockerignore ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── feature-request.md │ └── question.md ├── dependabot.yml └── workflows │ ├── ci-testing.yml │ ├── codeql-analysis.yml │ ├── greetings.yml │ ├── rebase.yml │ └── stale.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── detect.py ├── hubconf.py ├── models ├── __init__.py ├── common.py ├── experimental.py ├── export.py ├── hub │ ├── anchors.yaml │ ├── yolov3-spp.yaml │ ├── yolov3-tiny.yaml │ ├── yolov3.yaml │ ├── yolov5-fpn.yaml │ ├── yolov5-p2.yaml │ ├── yolov5-p6.yaml │ ├── yolov5-p7.yaml │ ├── yolov5-panet.yaml │ ├── yolov5l6.yaml │ ├── yolov5m6.yaml │ ├── yolov5s-transformer.yaml │ ├── yolov5s6.yaml │ └── yolov5x6.yaml ├── yolo.py ├── yolov5l.yaml ├── yolov5m.yaml ├── yolov5s.yaml └── yolov5x.yaml ├── requirements.txt ├── test.py ├── train.py ├── tutorial.ipynb ├── utils ├── __init__.py ├── activations.py ├── autoanchor.py ├── aws │ ├── __init__.py │ ├── mime.sh │ ├── resume.py │ └── userdata.sh ├── datasets.py ├── flask_rest_api │ ├── README.md │ ├── example_request.py │ └── restapi.py ├── general.py ├── google_app_engine │ ├── Dockerfile │ ├── additional_requirements.txt │ └── app.yaml ├── google_utils.py ├── loss.py ├── metrics.py ├── plots.py ├── torch_utils.py └── wandb_logging │ ├── __init__.py │ ├── log_dataset.py │ └── wandb_utils.py ├── weights └── yolov5s.pt └── yolov5-b8be76f915207ef0759bfb0f1c0707c79877b763.zip /README.md: -------------------------------------------------------------------------------- 1 | # Yolov5_DeepSort_Traffic-counter 2 | 基于Yolov5_DeepSort的移动物体计数器,可以统计车流或人流量等 3 | 本作品基于此项目实现:https://github.com/mikel-brostrom/Yolov5_DeepSort_Pytorch 4 | 实现了统计画面中通过检测线的物体数量的功能,包括车流、人群等。 5 | 6 | ![image](https://github.com/owo12321/Yolov5_DeepSort_Traffic-counter/blob/main/test3.gif) 7 | 测试素材来源:https://www.bilibili.com/video/BV1yL4y1q7yb 8 | 9 | ## 0、更新 10 | 1.可以绘制多条检测线 11 | 2.每条检测线可以同时统计两个跨线方向的流量 12 | 13 | ## 1、环境配置 14 | python3.8、cuda10.2 15 | 下载项目文件夹后,在命令行中进入项目文件夹,执行以下代码配置环境: 16 | ``` 17 | pip install -r requirements.txt 18 | ``` 19 | 此环境能够正常运行,其它包版本请自行测试 20 | 21 | 在./Yolov5_DeepSort_Traffic-counter/deep_sort_pytorch/deep_sort/deep/checkpoint路径下需要下载一个文件 22 | ``` 23 | 链接:https://pan.baidu.com/s/1BwMUM9JGRhMQgmjTu_HXcw?pwd=bwux 24 | 提取码:bwux 25 | ``` 26 | 默认使用Yolov5的5.0版本的yolov5s.pt模型文件,位于./yolov5/weights/yolov5s.pt,建议训练自己的数据集,参考 27 | https://github.com/ultralytics/yolov5/wiki/Train-Custom-Data 28 | 29 | 30 | ## 2、检测原理 31 | Yolov5_DeepSort会跟踪画面上检测出来的物体,并给每个框标上了序号,当有一个方框跨过检测线时,计数器就会+1 32 | 用户可以定义多条检测线,也可以指定框的四个顶点或中心点哪一个作为检测点 33 | 具体的参数设定见第3点 34 | 35 | 36 | ## 3、参数设置 37 | 在count.py中,设置以下参数 38 | ``` 39 | source_dir : 要打开的视频文件。若要调用摄像头,需要设置为字符串'0',而不是数字0,按q退出播放 40 | output_dir : 要保存到的文件夹 41 | show_video : 运行时是否显示 42 | save_video : 是否保存运行结果视频 43 | save_text : 是否保存结果数据到txt文件中,将会保存两个文本文件:result.txt和number.txt。result.txt的格式是(帧序号,框序号,框到左边距离,框到顶上距离,框横长,框竖高,-1,-1,-1,-1),number.txt的内容是统计到第几帧时每条线沿两个方向的跨线物体数 44 | 45 | class_list : 要检测的类别序号,在coco_classes.txt中查看(注意是序号不是行号),可以有一个或多个类别 46 | 47 | lines : 定义检测线的两个端点的xy坐标、颜色、粗细,可以定义多条检测线 48 | point_idx : 方框的检测点位置(0, 1, 2, 3, 4),看下边的图,当一个方框的检测点跨过检测线时,统计数会+1 49 | ``` 50 | 51 | 检测线的画法:给出两个端点的坐标,确定一条检测线,画布的坐标方向如下 52 | ``` 53 | |-------> x轴 54 | | 55 | | 56 | V 57 | y轴 58 | ``` 59 | 60 | 方框的检测编号:当一个框的检测点跨过检测线时,计数器会+1,检测点的编号如下 61 | ``` 62 | 1__________________2 63 | | | 64 | | | 65 | | 0(中心点) | 66 | | | 67 | |__________________| 68 | 4 3 69 | ``` 70 | 71 | ## 4、运行 72 | 设置好参数后,python运行count.py文件即可 73 | ``` 74 | python count.py 75 | ``` 76 | -------------------------------------------------------------------------------- /coco_classes.txt: -------------------------------------------------------------------------------- 1 | coco数据集,总共80个类别,类别序号等于行号 - 3 2 | 3 | person(人) 4 | bicycle(自行车) 5 | car(汽车) 6 | motorbike(摩托车) 7 | aeroplane(飞机) 8 | bus(公共汽车) 9 | train(火车) 10 | truck(卡车) 11 | boat(船) 12 | traffic light(信号灯 13 | fire hydrant(消防栓) 14 | stop sign(停车标志) 15 | parking meter(停车计费器) 16 | bench(长凳) 17 | bird(鸟) 18 | cat(猫) 19 | dog(狗) 20 | horse(马) 21 | sheep(羊) 22 | cow(牛) 23 | elephant(大象) 24 | bear(熊) 25 | zebra(斑马) 26 | giraffe(长颈鹿) 27 | backpack(背包) 28 | umbrella(雨伞) 29 | handbag(手提包) 30 | tie(领带) 31 | suitcase(手提箱) 32 | frisbee(飞盘) 33 | skis(滑雪板双脚) 34 | snowboard(滑雪板) 35 | sports ball(运动球) 36 | kite(风筝) 37 | baseball bat(棒球棒) 38 | baseball glove(棒球手套) 39 | skateboard(滑板) 40 | surfboard(冲浪板) 41 | tennis racket(网球拍) 42 | bottle(瓶子) 43 | wine glass(高脚杯) 44 | cup(茶杯) 45 | fork(叉子) 46 | knife(刀) 47 | spoon(勺子) 48 | bowl(碗) 49 | banana(香蕉) 50 | apple(苹果) 51 | sandwich(三明治) 52 | orange(橘子) 53 | broccoli(西兰花) 54 | carrot(胡萝卜) 55 | hot dog(热狗) 56 | pizza(披萨) 57 | donut(甜甜圈) 58 | cake(蛋糕) 59 | chair(椅子) 60 | sofa(沙发) 61 | pottedplant(盆栽植物) 62 | bed(床) 63 | diningtable(餐桌) 64 | toilet(厕所) 65 | tvmonitor(电视机) 66 | laptop(笔记本) 67 | mouse(鼠标) 68 | remote(遥控器) 69 | keyboard(键盘) 70 | cell phone(电话) 71 | microwave(微波炉) 72 | oven(烤箱) 73 | toaster(烤面包器) 74 | sink(水槽) 75 | refrigerator(冰箱) 76 | book(书) 77 | clock(闹钟) 78 | vase(花瓶) 79 | scissors(剪刀) 80 | teddy bear(泰迪熊) 81 | hair drier(吹风机) 82 | toothbrush(牙刷) 83 | -------------------------------------------------------------------------------- /deep_sort_pytorch/.gitignore: -------------------------------------------------------------------------------- 1 | # Folders 2 | __pycache__/ 3 | build/ 4 | *.egg-info 5 | 6 | 7 | # Files 8 | *.weights 9 | *.t7 10 | *.mp4 11 | *.avi 12 | *.so 13 | *.txt 14 | -------------------------------------------------------------------------------- /deep_sort_pytorch/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ziqiang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /deep_sort_pytorch/README.md: -------------------------------------------------------------------------------- 1 | # Deep Sort with PyTorch 2 | 3 | ![](demo/demo.gif) 4 | 5 | ## Update(1-1-2020) 6 | Changes 7 | - fix bugs 8 | - refactor code 9 | - accerate detection by adding nms on gpu 10 | 11 | ## Latest Update(07-22) 12 | Changes 13 | - bug fix (Thanks @JieChen91 and @yingsen1 for bug reporting). 14 | - using batch for feature extracting for each frame, which lead to a small speed up. 15 | - code improvement. 16 | 17 | Futher improvement direction 18 | - Train detector on specific dataset rather than the official one. 19 | - Retrain REID model on pedestrain dataset for better performance. 20 | - Replace YOLOv3 detector with advanced ones. 21 | 22 | **Any contributions to this repository is welcome!** 23 | 24 | 25 | ## Introduction 26 | This is an implement of MOT tracking algorithm deep sort. Deep sort is basicly the same with sort but added a CNN model to extract features in image of human part bounded by a detector. This CNN model is indeed a RE-ID model and the detector used in [PAPER](https://arxiv.org/abs/1703.07402) is FasterRCNN , and the original source code is [HERE](https://github.com/nwojke/deep_sort). 27 | However in original code, the CNN model is implemented with tensorflow, which I'm not familier with. SO I re-implemented the CNN feature extraction model with PyTorch, and changed the CNN model a little bit. Also, I use **YOLOv3** to generate bboxes instead of FasterRCNN. 28 | 29 | ## Dependencies 30 | - python 3 (python2 not sure) 31 | - numpy 32 | - scipy 33 | - opencv-python 34 | - sklearn 35 | - torch >= 0.4 36 | - torchvision >= 0.1 37 | - pillow 38 | - vizer 39 | - edict 40 | 41 | ## Quick Start 42 | 0. Check all dependencies installed 43 | ```bash 44 | pip install -r requirements.txt 45 | ``` 46 | for user in china, you can specify pypi source to accelerate install like: 47 | ```bash 48 | pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple 49 | ``` 50 | 51 | 1. Clone this repository 52 | ``` 53 | git clone git@github.com:ZQPei/deep_sort_pytorch.git 54 | ``` 55 | 56 | 2. Download YOLOv3 parameters 57 | ``` 58 | cd detector/YOLOv3/weight/ 59 | wget https://pjreddie.com/media/files/yolov3.weights 60 | wget https://pjreddie.com/media/files/yolov3-tiny.weights 61 | cd ../../../ 62 | ``` 63 | 64 | 3. Download deepsort parameters ckpt.t7 65 | ``` 66 | cd deep_sort/deep/checkpoint 67 | # download ckpt.t7 from 68 | https://drive.google.com/drive/folders/1xhG0kRH1EX5B9_Iz8gQJb7UNnn_riXi6 to this folder 69 | cd ../../../ 70 | ``` 71 | 72 | 4. Compile nms module 73 | ```bash 74 | cd detector/YOLOv3/nms 75 | sh build.sh 76 | cd ../../.. 77 | ``` 78 | 79 | Notice: 80 | If compiling failed, the simplist way is to **Upgrade your pytorch >= 1.1 and torchvision >= 0.3" and you can avoid the troublesome compiling problems which are most likely caused by either `gcc version too low` or `libraries missing`. 81 | 82 | 5. Run demo 83 | ``` 84 | usage: python yolov3_deepsort.py VIDEO_PATH 85 | [--help] 86 | [--frame_interval FRAME_INTERVAL] 87 | [--config_detection CONFIG_DETECTION] 88 | [--config_deepsort CONFIG_DEEPSORT] 89 | [--display] 90 | [--display_width DISPLAY_WIDTH] 91 | [--display_height DISPLAY_HEIGHT] 92 | [--save_path SAVE_PATH] 93 | [--cpu] 94 | 95 | # yolov3 + deepsort 96 | python yolov3_deepsort.py [VIDEO_PATH] 97 | 98 | # yolov3_tiny + deepsort 99 | python yolov3_deepsort.py [VIDEO_PATH] --config_detection ./configs/yolov3_tiny.yaml 100 | 101 | # yolov3 + deepsort on webcam 102 | python3 yolov3_deepsort.py /dev/video0 --camera 0 103 | 104 | # yolov3_tiny + deepsort on webcam 105 | python3 yolov3_deepsort.py /dev/video0 --config_detection ./configs/yolov3_tiny.yaml --camera 0 106 | ``` 107 | Use `--display` to enable display. 108 | Results will be saved to `./output/results.avi` and `./output/results.txt`. 109 | 110 | All files above can also be accessed from BaiduDisk! 111 | linker:[BaiduDisk](https://pan.baidu.com/s/1YJ1iPpdFTlUyLFoonYvozg) 112 | passwd:fbuw 113 | 114 | ## Training the RE-ID model 115 | The original model used in paper is in original_model.py, and its parameter here [original_ckpt.t7](https://drive.google.com/drive/folders/1xhG0kRH1EX5B9_Iz8gQJb7UNnn_riXi6). 116 | 117 | To train the model, first you need download [Market1501](http://www.liangzheng.com.cn/Project/project_reid.html) dataset or [Mars](http://www.liangzheng.com.cn/Project/project_mars.html) dataset. 118 | 119 | Then you can try [train.py](deep_sort/deep/train.py) to train your own parameter and evaluate it using [test.py](deep_sort/deep/test.py) and [evaluate.py](deep_sort/deep/evalute.py). 120 | ![train.jpg](deep_sort/deep/train.jpg) 121 | 122 | ## Demo videos and images 123 | [demo.avi](https://drive.google.com/drive/folders/1xhG0kRH1EX5B9_Iz8gQJb7UNnn_riXi6) 124 | [demo2.avi](https://drive.google.com/drive/folders/1xhG0kRH1EX5B9_Iz8gQJb7UNnn_riXi6) 125 | 126 | ![1.jpg](demo/1.jpg) 127 | ![2.jpg](demo/2.jpg) 128 | 129 | 130 | ## References 131 | - paper: [Simple Online and Realtime Tracking with a Deep Association Metric](https://arxiv.org/abs/1703.07402) 132 | 133 | - code: [nwojke/deep_sort](https://github.com/nwojke/deep_sort) 134 | 135 | - paper: [YOLOv3](https://pjreddie.com/media/files/papers/YOLOv3.pdf) 136 | 137 | - code: [Joseph Redmon/yolov3](https://pjreddie.com/darknet/yolo/) 138 | -------------------------------------------------------------------------------- /deep_sort_pytorch/configs/deep_sort.yaml: -------------------------------------------------------------------------------- 1 | DEEPSORT: 2 | REID_CKPT: "deep_sort_pytorch/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 | 11 | -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/README.md: -------------------------------------------------------------------------------- 1 | # Deep Sort 2 | 3 | This is the implemention of deep sort with pytorch. -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/__init__.py: -------------------------------------------------------------------------------- 1 | from .deep_sort import DeepSort 2 | 3 | 4 | __all__ = ['DeepSort', 'build_tracker'] 5 | 6 | 7 | def build_tracker(cfg, use_cuda): 8 | return DeepSort(cfg.DEEPSORT.REID_CKPT, 9 | max_dist=cfg.DEEPSORT.MAX_DIST, min_confidence=cfg.DEEPSORT.MIN_CONFIDENCE, 10 | nms_max_overlap=cfg.DEEPSORT.NMS_MAX_OVERLAP, max_iou_distance=cfg.DEEPSORT.MAX_IOU_DISTANCE, 11 | max_age=cfg.DEEPSORT.MAX_AGE, n_init=cfg.DEEPSORT.N_INIT, nn_budget=cfg.DEEPSORT.NN_BUDGET, use_cuda=use_cuda) 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/deep/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/deep_sort_pytorch/deep_sort/deep/__init__.py -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/deep/checkpoint/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/deep_sort_pytorch/deep_sort/deep/checkpoint/.gitkeep -------------------------------------------------------------------------------- /deep_sort_pytorch/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 | -------------------------------------------------------------------------------- /deep_sort_pytorch/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 | 10 | class Extractor(object): 11 | def __init__(self, model_path, use_cuda=True): 12 | self.net = Net(reid=True) 13 | self.device = "cuda" if torch.cuda.is_available() and use_cuda else "cpu" 14 | state_dict = torch.load(model_path, map_location=torch.device(self.device))[ 15 | 'net_dict'] 16 | self.net.load_state_dict(state_dict) 17 | logger = logging.getLogger("root.tracker") 18 | logger.info("Loading weights from {}... Done!".format(model_path)) 19 | self.net.to(self.device) 20 | self.size = (64, 128) 21 | self.norm = transforms.Compose([ 22 | transforms.ToTensor(), 23 | transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), 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( 39 | 0) for im in im_crops], dim=0).float() 40 | return im_batch 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 | -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/deep/model.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | 5 | 6 | class BasicBlock(nn.Module): 7 | def __init__(self, c_in, c_out, is_downsample=False): 8 | super(BasicBlock, self).__init__() 9 | self.is_downsample = is_downsample 10 | if is_downsample: 11 | self.conv1 = nn.Conv2d( 12 | c_in, c_out, 3, stride=2, padding=1, bias=False) 13 | else: 14 | self.conv1 = nn.Conv2d( 15 | c_in, c_out, 3, stride=1, padding=1, bias=False) 16 | self.bn1 = nn.BatchNorm2d(c_out) 17 | self.relu = nn.ReLU(True) 18 | self.conv2 = nn.Conv2d(c_out, c_out, 3, stride=1, 19 | padding=1, bias=False) 20 | self.bn2 = nn.BatchNorm2d(c_out) 21 | if is_downsample: 22 | self.downsample = nn.Sequential( 23 | nn.Conv2d(c_in, c_out, 1, stride=2, bias=False), 24 | nn.BatchNorm2d(c_out) 25 | ) 26 | elif c_in != c_out: 27 | self.downsample = nn.Sequential( 28 | nn.Conv2d(c_in, c_out, 1, stride=1, bias=False), 29 | nn.BatchNorm2d(c_out) 30 | ) 31 | self.is_downsample = True 32 | 33 | def forward(self, x): 34 | y = self.conv1(x) 35 | y = self.bn1(y) 36 | y = self.relu(y) 37 | y = self.conv2(y) 38 | y = self.bn2(y) 39 | if self.is_downsample: 40 | x = self.downsample(x) 41 | return F.relu(x.add(y), True) 42 | 43 | 44 | def make_layers(c_in, c_out, repeat_times, is_downsample=False): 45 | blocks = [] 46 | for i in range(repeat_times): 47 | if i == 0: 48 | blocks += [BasicBlock(c_in, c_out, is_downsample=is_downsample), ] 49 | else: 50 | blocks += [BasicBlock(c_out, c_out), ] 51 | return nn.Sequential(*blocks) 52 | 53 | 54 | class Net(nn.Module): 55 | def __init__(self, num_classes=751, reid=False): 56 | super(Net, self).__init__() 57 | # 3 128 64 58 | self.conv = nn.Sequential( 59 | nn.Conv2d(3, 64, 3, stride=1, padding=1), 60 | nn.BatchNorm2d(64), 61 | nn.ReLU(inplace=True), 62 | # nn.Conv2d(32,32,3,stride=1,padding=1), 63 | # nn.BatchNorm2d(32), 64 | # nn.ReLU(inplace=True), 65 | nn.MaxPool2d(3, 2, padding=1), 66 | ) 67 | # 32 64 32 68 | self.layer1 = make_layers(64, 64, 2, False) 69 | # 32 64 32 70 | self.layer2 = make_layers(64, 128, 2, True) 71 | # 64 32 16 72 | self.layer3 = make_layers(128, 256, 2, True) 73 | # 128 16 8 74 | self.layer4 = make_layers(256, 512, 2, True) 75 | # 256 8 4 76 | self.avgpool = nn.AvgPool2d((8, 4), 1) 77 | # 256 1 1 78 | self.reid = reid 79 | self.classifier = nn.Sequential( 80 | nn.Linear(512, 256), 81 | nn.BatchNorm1d(256), 82 | nn.ReLU(inplace=True), 83 | nn.Dropout(), 84 | nn.Linear(256, num_classes), 85 | ) 86 | 87 | def forward(self, x): 88 | x = self.conv(x) 89 | x = self.layer1(x) 90 | x = self.layer2(x) 91 | x = self.layer3(x) 92 | x = self.layer4(x) 93 | x = self.avgpool(x) 94 | x = x.view(x.size(0), -1) 95 | # B x 128 96 | if self.reid: 97 | x = x.div(x.norm(p=2, dim=1, keepdim=True)) 98 | return x 99 | # classifier 100 | x = self.classifier(x) 101 | return x 102 | 103 | 104 | if __name__ == '__main__': 105 | net = Net() 106 | x = torch.randn(4, 3, 128, 64) 107 | y = net(x) 108 | import ipdb 109 | ipdb.set_trace() 110 | -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/deep/original_model.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | 5 | 6 | class BasicBlock(nn.Module): 7 | def __init__(self, c_in, c_out, is_downsample=False): 8 | super(BasicBlock, self).__init__() 9 | self.is_downsample = is_downsample 10 | if is_downsample: 11 | self.conv1 = nn.Conv2d( 12 | c_in, c_out, 3, stride=2, padding=1, bias=False) 13 | else: 14 | self.conv1 = nn.Conv2d( 15 | c_in, c_out, 3, stride=1, padding=1, bias=False) 16 | self.bn1 = nn.BatchNorm2d(c_out) 17 | self.relu = nn.ReLU(True) 18 | self.conv2 = nn.Conv2d(c_out, c_out, 3, stride=1, 19 | padding=1, bias=False) 20 | self.bn2 = nn.BatchNorm2d(c_out) 21 | if is_downsample: 22 | self.downsample = nn.Sequential( 23 | nn.Conv2d(c_in, c_out, 1, stride=2, bias=False), 24 | nn.BatchNorm2d(c_out) 25 | ) 26 | elif c_in != c_out: 27 | self.downsample = nn.Sequential( 28 | nn.Conv2d(c_in, c_out, 1, stride=1, bias=False), 29 | nn.BatchNorm2d(c_out) 30 | ) 31 | self.is_downsample = True 32 | 33 | def forward(self, x): 34 | y = self.conv1(x) 35 | y = self.bn1(y) 36 | y = self.relu(y) 37 | y = self.conv2(y) 38 | y = self.bn2(y) 39 | if self.is_downsample: 40 | x = self.downsample(x) 41 | return F.relu(x.add(y), True) 42 | 43 | 44 | def make_layers(c_in, c_out, repeat_times, is_downsample=False): 45 | blocks = [] 46 | for i in range(repeat_times): 47 | if i == 0: 48 | blocks += [BasicBlock(c_in, c_out, is_downsample=is_downsample), ] 49 | else: 50 | blocks += [BasicBlock(c_out, c_out), ] 51 | return nn.Sequential(*blocks) 52 | 53 | 54 | class Net(nn.Module): 55 | def __init__(self, num_classes=625, reid=False): 56 | super(Net, self).__init__() 57 | # 3 128 64 58 | self.conv = nn.Sequential( 59 | nn.Conv2d(3, 32, 3, stride=1, padding=1), 60 | nn.BatchNorm2d(32), 61 | nn.ELU(inplace=True), 62 | nn.Conv2d(32, 32, 3, stride=1, padding=1), 63 | nn.BatchNorm2d(32), 64 | nn.ELU(inplace=True), 65 | nn.MaxPool2d(3, 2, padding=1), 66 | ) 67 | # 32 64 32 68 | self.layer1 = make_layers(32, 32, 2, False) 69 | # 32 64 32 70 | self.layer2 = make_layers(32, 64, 2, True) 71 | # 64 32 16 72 | self.layer3 = make_layers(64, 128, 2, True) 73 | # 128 16 8 74 | self.dense = nn.Sequential( 75 | nn.Dropout(p=0.6), 76 | nn.Linear(128*16*8, 128), 77 | nn.BatchNorm1d(128), 78 | nn.ELU(inplace=True) 79 | ) 80 | # 256 1 1 81 | self.reid = reid 82 | self.batch_norm = nn.BatchNorm1d(128) 83 | self.classifier = nn.Sequential( 84 | nn.Linear(128, num_classes), 85 | ) 86 | 87 | def forward(self, x): 88 | x = self.conv(x) 89 | x = self.layer1(x) 90 | x = self.layer2(x) 91 | x = self.layer3(x) 92 | 93 | x = x.view(x.size(0), -1) 94 | if self.reid: 95 | x = self.dense[0](x) 96 | x = self.dense[1](x) 97 | x = x.div(x.norm(p=2, dim=1, keepdim=True)) 98 | return x 99 | x = self.dense(x) 100 | # B x 128 101 | # classifier 102 | x = self.classifier(x) 103 | return x 104 | 105 | 106 | if __name__ == '__main__': 107 | net = Net(reid=True) 108 | x = torch.randn(4, 3, 128, 64) 109 | y = net(x) 110 | import ipdb 111 | ipdb.set_trace() 112 | -------------------------------------------------------------------------------- /deep_sort_pytorch/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( 18 | args.gpu_id) if torch.cuda.is_available() and not args.no_cuda else "cpu" 19 | if torch.cuda.is_available() and not args.no_cuda: 20 | cudnn.benchmark = True 21 | 22 | # data loader 23 | root = args.data_dir 24 | query_dir = os.path.join(root, "query") 25 | gallery_dir = os.path.join(root, "gallery") 26 | transform = torchvision.transforms.Compose([ 27 | torchvision.transforms.Resize((128, 64)), 28 | torchvision.transforms.ToTensor(), 29 | torchvision.transforms.Normalize( 30 | [0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) 31 | ]) 32 | queryloader = torch.utils.data.DataLoader( 33 | torchvision.datasets.ImageFolder(query_dir, transform=transform), 34 | batch_size=64, shuffle=False 35 | ) 36 | galleryloader = torch.utils.data.DataLoader( 37 | torchvision.datasets.ImageFolder(gallery_dir, transform=transform), 38 | batch_size=64, shuffle=False 39 | ) 40 | 41 | # net definition 42 | net = Net(reid=True) 43 | assert os.path.isfile( 44 | "./checkpoint/ckpt.t7"), "Error: no checkpoint file found!" 45 | print('Loading from checkpoint/ckpt.t7') 46 | checkpoint = torch.load("./checkpoint/ckpt.t7") 47 | net_dict = checkpoint['net_dict'] 48 | net.load_state_dict(net_dict, strict=False) 49 | net.eval() 50 | net.to(device) 51 | 52 | # compute features 53 | query_features = torch.tensor([]).float() 54 | query_labels = torch.tensor([]).long() 55 | gallery_features = torch.tensor([]).float() 56 | gallery_labels = torch.tensor([]).long() 57 | 58 | with torch.no_grad(): 59 | for idx, (inputs, labels) in enumerate(queryloader): 60 | inputs = inputs.to(device) 61 | features = net(inputs).cpu() 62 | query_features = torch.cat((query_features, features), dim=0) 63 | query_labels = torch.cat((query_labels, labels)) 64 | 65 | for idx, (inputs, labels) in enumerate(galleryloader): 66 | inputs = inputs.to(device) 67 | features = net(inputs).cpu() 68 | gallery_features = torch.cat((gallery_features, features), dim=0) 69 | gallery_labels = torch.cat((gallery_labels, labels)) 70 | 71 | gallery_labels -= 2 72 | 73 | # save features 74 | features = { 75 | "qf": query_features, 76 | "ql": query_labels, 77 | "gf": gallery_features, 78 | "gl": gallery_labels 79 | } 80 | torch.save(features, "features.pth") 81 | -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/deep/train.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/deep_sort_pytorch/deep_sort/deep/train.jpg -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/deep/train.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import time 4 | 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | import torch 8 | import torch.backends.cudnn as cudnn 9 | import torchvision 10 | 11 | from model import Net 12 | 13 | parser = argparse.ArgumentParser(description="Train on market1501") 14 | parser.add_argument("--data-dir", default='data', type=str) 15 | parser.add_argument("--no-cuda", action="store_true") 16 | parser.add_argument("--gpu-id", default=0, type=int) 17 | parser.add_argument("--lr", default=0.1, type=float) 18 | parser.add_argument("--interval", '-i', default=20, type=int) 19 | parser.add_argument('--resume', '-r', action='store_true') 20 | args = parser.parse_args() 21 | 22 | # device 23 | device = "cuda:{}".format( 24 | args.gpu_id) if torch.cuda.is_available() and not args.no_cuda else "cpu" 25 | if torch.cuda.is_available() and not args.no_cuda: 26 | cudnn.benchmark = True 27 | 28 | # data loading 29 | root = args.data_dir 30 | train_dir = os.path.join(root, "train") 31 | test_dir = os.path.join(root, "test") 32 | transform_train = torchvision.transforms.Compose([ 33 | torchvision.transforms.RandomCrop((128, 64), padding=4), 34 | torchvision.transforms.RandomHorizontalFlip(), 35 | torchvision.transforms.ToTensor(), 36 | torchvision.transforms.Normalize( 37 | [0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) 38 | ]) 39 | transform_test = torchvision.transforms.Compose([ 40 | torchvision.transforms.Resize((128, 64)), 41 | torchvision.transforms.ToTensor(), 42 | torchvision.transforms.Normalize( 43 | [0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) 44 | ]) 45 | trainloader = torch.utils.data.DataLoader( 46 | torchvision.datasets.ImageFolder(train_dir, transform=transform_train), 47 | batch_size=64, shuffle=True 48 | ) 49 | testloader = torch.utils.data.DataLoader( 50 | torchvision.datasets.ImageFolder(test_dir, transform=transform_test), 51 | batch_size=64, shuffle=True 52 | ) 53 | num_classes = max(len(trainloader.dataset.classes), 54 | len(testloader.dataset.classes)) 55 | 56 | # net definition 57 | start_epoch = 0 58 | net = Net(num_classes=num_classes) 59 | if args.resume: 60 | assert os.path.isfile( 61 | "./checkpoint/ckpt.t7"), "Error: no checkpoint file found!" 62 | print('Loading from checkpoint/ckpt.t7') 63 | checkpoint = torch.load("./checkpoint/ckpt.t7") 64 | # import ipdb; ipdb.set_trace() 65 | net_dict = checkpoint['net_dict'] 66 | net.load_state_dict(net_dict) 67 | best_acc = checkpoint['acc'] 68 | start_epoch = checkpoint['epoch'] 69 | net.to(device) 70 | 71 | # loss and optimizer 72 | criterion = torch.nn.CrossEntropyLoss() 73 | optimizer = torch.optim.SGD( 74 | net.parameters(), args.lr, momentum=0.9, weight_decay=5e-4) 75 | best_acc = 0. 76 | 77 | # train function for each epoch 78 | 79 | 80 | def train(epoch): 81 | print("\nEpoch : %d" % (epoch+1)) 82 | net.train() 83 | training_loss = 0. 84 | train_loss = 0. 85 | correct = 0 86 | total = 0 87 | interval = args.interval 88 | start = time.time() 89 | for idx, (inputs, labels) in enumerate(trainloader): 90 | # forward 91 | inputs, labels = inputs.to(device), labels.to(device) 92 | outputs = net(inputs) 93 | loss = criterion(outputs, labels) 94 | 95 | # backward 96 | optimizer.zero_grad() 97 | loss.backward() 98 | optimizer.step() 99 | 100 | # accumurating 101 | training_loss += loss.item() 102 | train_loss += loss.item() 103 | correct += outputs.max(dim=1)[1].eq(labels).sum().item() 104 | total += labels.size(0) 105 | 106 | # print 107 | if (idx+1) % interval == 0: 108 | end = time.time() 109 | print("[progress:{:.1f}%]time:{:.2f}s Loss:{:.5f} Correct:{}/{} Acc:{:.3f}%".format( 110 | 100.*(idx+1)/len(trainloader), end-start, training_loss / 111 | interval, correct, total, 100.*correct/total 112 | )) 113 | training_loss = 0. 114 | start = time.time() 115 | 116 | return train_loss/len(trainloader), 1. - correct/total 117 | 118 | 119 | def test(epoch): 120 | global best_acc 121 | net.eval() 122 | test_loss = 0. 123 | correct = 0 124 | total = 0 125 | start = time.time() 126 | with torch.no_grad(): 127 | for idx, (inputs, labels) in enumerate(testloader): 128 | inputs, labels = inputs.to(device), labels.to(device) 129 | outputs = net(inputs) 130 | loss = criterion(outputs, labels) 131 | 132 | test_loss += loss.item() 133 | correct += outputs.max(dim=1)[1].eq(labels).sum().item() 134 | total += labels.size(0) 135 | 136 | print("Testing ...") 137 | end = time.time() 138 | print("[progress:{:.1f}%]time:{:.2f}s Loss:{:.5f} Correct:{}/{} Acc:{:.3f}%".format( 139 | 100.*(idx+1)/len(testloader), end-start, test_loss / 140 | len(testloader), correct, total, 100.*correct/total 141 | )) 142 | 143 | # saving checkpoint 144 | acc = 100.*correct/total 145 | if acc > best_acc: 146 | best_acc = acc 147 | print("Saving parameters to checkpoint/ckpt.t7") 148 | checkpoint = { 149 | 'net_dict': net.state_dict(), 150 | 'acc': acc, 151 | 'epoch': epoch, 152 | } 153 | if not os.path.isdir('checkpoint'): 154 | os.mkdir('checkpoint') 155 | torch.save(checkpoint, './checkpoint/ckpt.t7') 156 | 157 | return test_loss/len(testloader), 1. - correct/total 158 | 159 | 160 | # plot figure 161 | x_epoch = [] 162 | record = {'train_loss': [], 'train_err': [], 'test_loss': [], 'test_err': []} 163 | fig = plt.figure() 164 | ax0 = fig.add_subplot(121, title="loss") 165 | ax1 = fig.add_subplot(122, title="top1err") 166 | 167 | 168 | def draw_curve(epoch, train_loss, train_err, test_loss, test_err): 169 | global record 170 | record['train_loss'].append(train_loss) 171 | record['train_err'].append(train_err) 172 | record['test_loss'].append(test_loss) 173 | record['test_err'].append(test_err) 174 | 175 | x_epoch.append(epoch) 176 | ax0.plot(x_epoch, record['train_loss'], 'bo-', label='train') 177 | ax0.plot(x_epoch, record['test_loss'], 'ro-', label='val') 178 | ax1.plot(x_epoch, record['train_err'], 'bo-', label='train') 179 | ax1.plot(x_epoch, record['test_err'], 'ro-', label='val') 180 | if epoch == 0: 181 | ax0.legend() 182 | ax1.legend() 183 | fig.savefig("train.jpg") 184 | 185 | # lr decay 186 | 187 | 188 | def lr_decay(): 189 | global optimizer 190 | for params in optimizer.param_groups: 191 | params['lr'] *= 0.1 192 | lr = params['lr'] 193 | print("Learning rate adjusted to {}".format(lr)) 194 | 195 | 196 | def main(): 197 | for epoch in range(start_epoch, start_epoch+40): 198 | train_loss, train_err = train(epoch) 199 | test_loss, test_err = test(epoch) 200 | draw_curve(epoch, train_loss, train_err, test_loss, test_err) 201 | if (epoch+1) % 20 == 0: 202 | lr_decay() 203 | 204 | 205 | if __name__ == '__main__': 206 | main() 207 | -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/deep_sort.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | 4 | from .deep.feature_extractor import Extractor 5 | from .sort.nn_matching import NearestNeighborDistanceMetric 6 | from .sort.detection import Detection 7 | from .sort.tracker import Tracker 8 | 9 | 10 | __all__ = ['DeepSort'] 11 | 12 | 13 | class DeepSort(object): 14 | def __init__(self, model_path, max_dist=0.2, min_confidence=0.3, nms_max_overlap=1.0, max_iou_distance=0.7, max_age=70, n_init=3, nn_budget=100, use_cuda=True): 15 | self.min_confidence = min_confidence 16 | self.nms_max_overlap = nms_max_overlap 17 | 18 | self.extractor = Extractor(model_path, use_cuda=use_cuda) 19 | 20 | max_cosine_distance = max_dist 21 | metric = NearestNeighborDistanceMetric( 22 | "cosine", max_cosine_distance, nn_budget) 23 | self.tracker = Tracker( 24 | metric, max_iou_distance=max_iou_distance, max_age=max_age, n_init=n_init) 25 | 26 | def update(self, bbox_xywh, confidences, ori_img): 27 | self.height, self.width = ori_img.shape[:2] 28 | # generate detections 29 | features = self._get_features(bbox_xywh, ori_img) 30 | bbox_tlwh = self._xywh_to_tlwh(bbox_xywh) 31 | detections = [Detection(bbox_tlwh[i], conf, features[i]) for i, conf in enumerate( 32 | confidences) if conf > self.min_confidence] 33 | 34 | # run on non-maximum supression 35 | boxes = np.array([d.tlwh for d in detections]) 36 | scores = np.array([d.confidence for d in detections]) 37 | 38 | # update tracker 39 | self.tracker.predict() 40 | self.tracker.update(detections) 41 | 42 | # output bbox identities 43 | outputs = [] 44 | for track in self.tracker.tracks: 45 | if not track.is_confirmed() or track.time_since_update > 1: 46 | continue 47 | box = track.to_tlwh() 48 | x1, y1, x2, y2 = self._tlwh_to_xyxy(box) 49 | track_id = track.track_id 50 | outputs.append(np.array([x1, y1, x2, y2, track_id], dtype=np.int)) 51 | if len(outputs) > 0: 52 | outputs = np.stack(outputs, axis=0) 53 | return outputs 54 | 55 | """ 56 | TODO: 57 | Convert bbox from xc_yc_w_h to xtl_ytl_w_h 58 | Thanks JieChen91@github.com for reporting this bug! 59 | """ 60 | @staticmethod 61 | def _xywh_to_tlwh(bbox_xywh): 62 | if isinstance(bbox_xywh, np.ndarray): 63 | bbox_tlwh = bbox_xywh.copy() 64 | elif isinstance(bbox_xywh, torch.Tensor): 65 | bbox_tlwh = bbox_xywh.clone() 66 | bbox_tlwh[:, 0] = bbox_xywh[:, 0] - bbox_xywh[:, 2] / 2. 67 | bbox_tlwh[:, 1] = bbox_xywh[:, 1] - bbox_xywh[:, 3] / 2. 68 | return bbox_tlwh 69 | 70 | def _xywh_to_xyxy(self, bbox_xywh): 71 | x, y, w, h = bbox_xywh 72 | x1 = max(int(x - w / 2), 0) 73 | x2 = min(int(x + w / 2), self.width - 1) 74 | y1 = max(int(y - h / 2), 0) 75 | y2 = min(int(y + h / 2), self.height - 1) 76 | return x1, y1, x2, y2 77 | 78 | def _tlwh_to_xyxy(self, bbox_tlwh): 79 | """ 80 | TODO: 81 | Convert bbox from xtl_ytl_w_h to xc_yc_w_h 82 | Thanks JieChen91@github.com for reporting this bug! 83 | """ 84 | x, y, w, h = bbox_tlwh 85 | x1 = max(int(x), 0) 86 | x2 = min(int(x+w), self.width - 1) 87 | y1 = max(int(y), 0) 88 | y2 = min(int(y+h), self.height - 1) 89 | return x1, y1, x2, y2 90 | 91 | def increment_ages(self): 92 | self.tracker.increment_ages() 93 | 94 | def _xyxy_to_tlwh(self, bbox_xyxy): 95 | x1, y1, x2, y2 = bbox_xyxy 96 | 97 | t = x1 98 | l = y1 99 | w = int(x2 - x1) 100 | h = int(y2 - y1) 101 | return t, l, w, h 102 | 103 | def _get_features(self, bbox_xywh, ori_img): 104 | im_crops = [] 105 | for box in bbox_xywh: 106 | x1, y1, x2, y2 = self._xywh_to_xyxy(box) 107 | im = ori_img[y1:y2, x1:x2] 108 | im_crops.append(im) 109 | if im_crops: 110 | features = self.extractor(im_crops) 111 | else: 112 | features = np.array([]) 113 | return features 114 | -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/sort/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/deep_sort_pytorch/deep_sort/sort/__init__.py -------------------------------------------------------------------------------- /deep_sort_pytorch/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_pytorch/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( 80 | [detections[i].tlwh for i in detection_indices]) 81 | cost_matrix[row, :] = 1. - iou(bbox, candidates) 82 | return cost_matrix 83 | -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/sort/kalman_filter.py: -------------------------------------------------------------------------------- 1 | # vim: expandtab:ts=4:sw=4 2 | import numpy as np 3 | import scipy.linalg 4 | 5 | 6 | """ 7 | Table for the 0.95 quantile of the chi-square distribution with N degrees of 8 | freedom (contains values for N=1, ..., 9). Taken from MATLAB/Octave's chi2inv 9 | function and used as Mahalanobis gating threshold. 10 | """ 11 | chi2inv95 = { 12 | 1: 3.8415, 13 | 2: 5.9915, 14 | 3: 7.8147, 15 | 4: 9.4877, 16 | 5: 11.070, 17 | 6: 12.592, 18 | 7: 14.067, 19 | 8: 15.507, 20 | 9: 16.919} 21 | 22 | 23 | class KalmanFilter(object): 24 | """ 25 | A simple Kalman filter for tracking bounding boxes in image space. 26 | 27 | The 8-dimensional state space 28 | 29 | x, y, a, h, vx, vy, va, vh 30 | 31 | contains the bounding box center position (x, y), aspect ratio a, height h, 32 | and their respective velocities. 33 | 34 | Object motion follows a constant velocity model. The bounding box location 35 | (x, y, a, h) is taken as direct observation of the state space (linear 36 | observation model). 37 | 38 | """ 39 | 40 | def __init__(self): 41 | ndim, dt = 4, 1. 42 | 43 | # Create Kalman filter model matrices. 44 | self._motion_mat = np.eye(2 * ndim, 2 * ndim) 45 | for i in range(ndim): 46 | self._motion_mat[i, ndim + i] = dt 47 | self._update_mat = np.eye(ndim, 2 * ndim) 48 | 49 | # Motion and observation uncertainty are chosen relative to the current 50 | # state estimate. These weights control the amount of uncertainty in 51 | # the model. This is a bit hacky. 52 | self._std_weight_position = 1. / 20 53 | self._std_weight_velocity = 1. / 160 54 | 55 | def initiate(self, measurement): 56 | """Create track from unassociated measurement. 57 | 58 | Parameters 59 | ---------- 60 | measurement : ndarray 61 | Bounding box coordinates (x, y, a, h) with center position (x, y), 62 | aspect ratio a, and height h. 63 | 64 | Returns 65 | ------- 66 | (ndarray, ndarray) 67 | Returns the mean vector (8 dimensional) and covariance matrix (8x8 68 | dimensional) of the new track. Unobserved velocities are initialized 69 | to 0 mean. 70 | 71 | """ 72 | mean_pos = measurement 73 | mean_vel = np.zeros_like(mean_pos) 74 | mean = np.r_[mean_pos, mean_vel] 75 | 76 | std = [ 77 | 2 * self._std_weight_position * measurement[3], 78 | 2 * self._std_weight_position * measurement[3], 79 | 1e-2, 80 | 2 * self._std_weight_position * measurement[3], 81 | 10 * self._std_weight_velocity * measurement[3], 82 | 10 * self._std_weight_velocity * measurement[3], 83 | 1e-5, 84 | 10 * self._std_weight_velocity * measurement[3]] 85 | covariance = np.diag(np.square(std)) 86 | return mean, covariance 87 | 88 | def predict(self, mean, covariance): 89 | """Run Kalman filter prediction step. 90 | 91 | Parameters 92 | ---------- 93 | mean : ndarray 94 | The 8 dimensional mean vector of the object state at the previous 95 | time step. 96 | covariance : ndarray 97 | The 8x8 dimensional covariance matrix of the object state at the 98 | previous time step. 99 | 100 | Returns 101 | ------- 102 | (ndarray, ndarray) 103 | Returns the mean vector and covariance matrix of the predicted 104 | state. Unobserved velocities are initialized to 0 mean. 105 | 106 | """ 107 | std_pos = [ 108 | self._std_weight_position * mean[3], 109 | self._std_weight_position * mean[3], 110 | 1e-2, 111 | self._std_weight_position * mean[3]] 112 | std_vel = [ 113 | self._std_weight_velocity * mean[3], 114 | self._std_weight_velocity * mean[3], 115 | 1e-5, 116 | self._std_weight_velocity * mean[3]] 117 | motion_cov = np.diag(np.square(np.r_[std_pos, std_vel])) 118 | 119 | mean = np.dot(self._motion_mat, mean) 120 | covariance = np.linalg.multi_dot(( 121 | self._motion_mat, covariance, self._motion_mat.T)) + motion_cov 122 | 123 | return mean, covariance 124 | 125 | def project(self, mean, covariance): 126 | """Project state distribution to measurement space. 127 | 128 | Parameters 129 | ---------- 130 | mean : ndarray 131 | The state's mean vector (8 dimensional array). 132 | covariance : ndarray 133 | The state's covariance matrix (8x8 dimensional). 134 | 135 | Returns 136 | ------- 137 | (ndarray, ndarray) 138 | Returns the projected mean and covariance matrix of the given state 139 | estimate. 140 | 141 | """ 142 | std = [ 143 | self._std_weight_position * mean[3], 144 | self._std_weight_position * mean[3], 145 | 1e-1, 146 | self._std_weight_position * mean[3]] 147 | innovation_cov = np.diag(np.square(std)) 148 | 149 | mean = np.dot(self._update_mat, mean) 150 | covariance = np.linalg.multi_dot(( 151 | self._update_mat, covariance, self._update_mat.T)) 152 | return mean, covariance + innovation_cov 153 | 154 | def update(self, mean, covariance, measurement): 155 | """Run Kalman filter correction step. 156 | 157 | Parameters 158 | ---------- 159 | mean : ndarray 160 | The predicted state's mean vector (8 dimensional). 161 | covariance : ndarray 162 | The state's covariance matrix (8x8 dimensional). 163 | measurement : ndarray 164 | The 4 dimensional measurement vector (x, y, a, h), where (x, y) 165 | is the center position, a the aspect ratio, and h the height of the 166 | bounding box. 167 | 168 | Returns 169 | ------- 170 | (ndarray, ndarray) 171 | Returns the measurement-corrected state distribution. 172 | 173 | """ 174 | projected_mean, projected_cov = self.project(mean, covariance) 175 | 176 | chol_factor, lower = scipy.linalg.cho_factor( 177 | projected_cov, lower=True, check_finite=False) 178 | kalman_gain = scipy.linalg.cho_solve( 179 | (chol_factor, lower), np.dot(covariance, self._update_mat.T).T, 180 | check_finite=False).T 181 | innovation = measurement - projected_mean 182 | 183 | new_mean = mean + np.dot(innovation, kalman_gain.T) 184 | new_covariance = covariance - np.linalg.multi_dot(( 185 | kalman_gain, projected_cov, kalman_gain.T)) 186 | return new_mean, new_covariance 187 | 188 | def gating_distance(self, mean, covariance, measurements, 189 | only_position=False): 190 | """Compute gating distance between state distribution and measurements. 191 | 192 | A suitable distance threshold can be obtained from `chi2inv95`. If 193 | `only_position` is False, the chi-square distribution has 4 degrees of 194 | freedom, otherwise 2. 195 | 196 | Parameters 197 | ---------- 198 | mean : ndarray 199 | Mean vector over the state distribution (8 dimensional). 200 | covariance : ndarray 201 | Covariance of the state distribution (8x8 dimensional). 202 | measurements : ndarray 203 | An Nx4 dimensional matrix of N measurements, each in 204 | format (x, y, a, h) where (x, y) is the bounding box center 205 | position, a the aspect ratio, and h the height. 206 | only_position : Optional[bool] 207 | If True, distance computation is done with respect to the bounding 208 | box center position only. 209 | 210 | Returns 211 | ------- 212 | ndarray 213 | Returns an array of length N, where the i-th element contains the 214 | squared Mahalanobis distance between (mean, covariance) and 215 | `measurements[i]`. 216 | 217 | """ 218 | mean, covariance = self.project(mean, covariance) 219 | if only_position: 220 | mean, covariance = mean[:2], covariance[:2, :2] 221 | measurements = measurements[:, :2] 222 | 223 | cholesky_factor = np.linalg.cholesky(covariance) 224 | d = measurements - mean 225 | z = scipy.linalg.solve_triangular( 226 | cholesky_factor, d.T, lower=True, check_finite=False, 227 | overwrite_b=True) 228 | squared_maha = np.sum(z * z, axis=0) 229 | return squared_maha 230 | -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/sort/linear_assignment.py: -------------------------------------------------------------------------------- 1 | # vim: expandtab:ts=4:sw=4 2 | from __future__ import absolute_import 3 | import numpy as np 4 | # from sklearn.utils.linear_assignment_ import linear_assignment 5 | from scipy.optimize import linear_sum_assignment as linear_assignment 6 | from . import kalman_filter 7 | 8 | 9 | INFTY_COST = 1e+5 10 | 11 | 12 | def min_cost_matching( 13 | distance_metric, max_distance, tracks, detections, track_indices=None, 14 | detection_indices=None): 15 | """Solve linear assignment problem. 16 | 17 | Parameters 18 | ---------- 19 | distance_metric : Callable[List[Track], List[Detection], List[int], List[int]) -> ndarray 20 | The distance metric is given a list of tracks and detections as well as 21 | a list of N track indices and M detection indices. The metric should 22 | return the NxM dimensional cost matrix, where element (i, j) is the 23 | association cost between the i-th track in the given track indices and 24 | the j-th detection in the given detection_indices. 25 | max_distance : float 26 | Gating threshold. Associations with cost larger than this value are 27 | disregarded. 28 | tracks : List[track.Track] 29 | A list of predicted tracks at the current time step. 30 | detections : List[detection.Detection] 31 | A list of detections at the current time step. 32 | track_indices : List[int] 33 | List of track indices that maps rows in `cost_matrix` to tracks in 34 | `tracks` (see description above). 35 | detection_indices : List[int] 36 | List of detection indices that maps columns in `cost_matrix` to 37 | detections in `detections` (see description above). 38 | 39 | Returns 40 | ------- 41 | (List[(int, int)], List[int], List[int]) 42 | Returns a tuple with the following three entries: 43 | * A list of matched track and detection indices. 44 | * A list of unmatched track indices. 45 | * A list of unmatched detection indices. 46 | 47 | """ 48 | if track_indices is None: 49 | track_indices = np.arange(len(tracks)) 50 | if detection_indices is None: 51 | detection_indices = np.arange(len(detections)) 52 | 53 | if len(detection_indices) == 0 or len(track_indices) == 0: 54 | return [], track_indices, detection_indices # Nothing to match. 55 | 56 | cost_matrix = distance_metric( 57 | tracks, detections, track_indices, detection_indices) 58 | cost_matrix[cost_matrix > max_distance] = max_distance + 1e-5 59 | 60 | row_indices, col_indices = linear_assignment(cost_matrix) 61 | 62 | matches, unmatched_tracks, unmatched_detections = [], [], [] 63 | for col, detection_idx in enumerate(detection_indices): 64 | if col not in col_indices: 65 | unmatched_detections.append(detection_idx) 66 | for row, track_idx in enumerate(track_indices): 67 | if row not in row_indices: 68 | unmatched_tracks.append(track_idx) 69 | for row, col in zip(row_indices, col_indices): 70 | track_idx = track_indices[row] 71 | detection_idx = detection_indices[col] 72 | if cost_matrix[row, col] > max_distance: 73 | unmatched_tracks.append(track_idx) 74 | unmatched_detections.append(detection_idx) 75 | else: 76 | matches.append((track_idx, detection_idx)) 77 | return matches, unmatched_tracks, unmatched_detections 78 | 79 | 80 | def matching_cascade( 81 | distance_metric, max_distance, cascade_depth, tracks, detections, 82 | track_indices=None, detection_indices=None): 83 | """Run matching cascade. 84 | 85 | Parameters 86 | ---------- 87 | distance_metric : Callable[List[Track], List[Detection], List[int], List[int]) -> ndarray 88 | The distance metric is given a list of tracks and detections as well as 89 | a list of N track indices and M detection indices. The metric should 90 | return the NxM dimensional cost matrix, where element (i, j) is the 91 | association cost between the i-th track in the given track indices and 92 | the j-th detection in the given detection indices. 93 | max_distance : float 94 | Gating threshold. Associations with cost larger than this value are 95 | disregarded. 96 | cascade_depth: int 97 | The cascade depth, should be se to the maximum track age. 98 | tracks : List[track.Track] 99 | A list of predicted tracks at the current time step. 100 | detections : List[detection.Detection] 101 | A list of detections at the current time step. 102 | track_indices : Optional[List[int]] 103 | List of track indices that maps rows in `cost_matrix` to tracks in 104 | `tracks` (see description above). Defaults to all tracks. 105 | detection_indices : Optional[List[int]] 106 | List of detection indices that maps columns in `cost_matrix` to 107 | detections in `detections` (see description above). Defaults to all 108 | detections. 109 | 110 | Returns 111 | ------- 112 | (List[(int, int)], List[int], List[int]) 113 | Returns a tuple with the following three entries: 114 | * A list of matched track and detection indices. 115 | * A list of unmatched track indices. 116 | * A list of unmatched detection indices. 117 | 118 | """ 119 | if track_indices is None: 120 | track_indices = list(range(len(tracks))) 121 | if detection_indices is None: 122 | detection_indices = list(range(len(detections))) 123 | 124 | unmatched_detections = detection_indices 125 | matches = [] 126 | for level in range(cascade_depth): 127 | if len(unmatched_detections) == 0: # No detections left 128 | break 129 | 130 | track_indices_l = [ 131 | k for k in track_indices 132 | if tracks[k].time_since_update == 1 + level 133 | ] 134 | if len(track_indices_l) == 0: # Nothing to match at this level 135 | continue 136 | 137 | matches_l, _, unmatched_detections = \ 138 | min_cost_matching( 139 | distance_metric, max_distance, tracks, detections, 140 | track_indices_l, unmatched_detections) 141 | matches += matches_l 142 | unmatched_tracks = list(set(track_indices) - set(k for k, _ in matches)) 143 | return matches, unmatched_tracks, unmatched_detections 144 | 145 | 146 | def gate_cost_matrix( 147 | kf, cost_matrix, tracks, detections, track_indices, detection_indices, 148 | gated_cost=INFTY_COST, only_position=False): 149 | """Invalidate infeasible entries in cost matrix based on the state 150 | distributions obtained by Kalman filtering. 151 | 152 | Parameters 153 | ---------- 154 | kf : The Kalman filter. 155 | cost_matrix : ndarray 156 | The NxM dimensional cost matrix, where N is the number of track indices 157 | and M is the number of detection indices, such that entry (i, j) is the 158 | association cost between `tracks[track_indices[i]]` and 159 | `detections[detection_indices[j]]`. 160 | tracks : List[track.Track] 161 | A list of predicted tracks at the current time step. 162 | detections : List[detection.Detection] 163 | A list of detections at the current time step. 164 | track_indices : List[int] 165 | List of track indices that maps rows in `cost_matrix` to tracks in 166 | `tracks` (see description above). 167 | detection_indices : List[int] 168 | List of detection indices that maps columns in `cost_matrix` to 169 | detections in `detections` (see description above). 170 | gated_cost : Optional[float] 171 | Entries in the cost matrix corresponding to infeasible associations are 172 | set this value. Defaults to a very large value. 173 | only_position : Optional[bool] 174 | If True, only the x, y position of the state distribution is considered 175 | during gating. Defaults to False. 176 | 177 | Returns 178 | ------- 179 | ndarray 180 | Returns the modified cost matrix. 181 | 182 | """ 183 | gating_dim = 2 if only_position else 4 184 | gating_threshold = kalman_filter.chi2inv95[gating_dim] 185 | measurements = np.asarray( 186 | [detections[i].to_xyah() for i in detection_indices]) 187 | for row, track_idx in enumerate(track_indices): 188 | track = tracks[track_idx] 189 | gating_distance = kf.gating_distance( 190 | track.mean, track.covariance, measurements, only_position) 191 | cost_matrix[row, gating_distance > gating_threshold] = gated_cost 192 | return cost_matrix 193 | -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/sort/nn_matching.py: -------------------------------------------------------------------------------- 1 | # vim: expandtab:ts=4:sw=4 2 | import numpy as np 3 | 4 | 5 | def _pdist(a, b): 6 | """Compute pair-wise squared distance between points in `a` and `b`. 7 | 8 | Parameters 9 | ---------- 10 | a : array_like 11 | An NxM matrix of N samples of dimensionality M. 12 | b : array_like 13 | An LxM matrix of L samples of dimensionality M. 14 | 15 | Returns 16 | ------- 17 | ndarray 18 | Returns a matrix of size len(a), len(b) such that eleement (i, j) 19 | contains the squared distance between `a[i]` and `b[j]`. 20 | 21 | """ 22 | a, b = np.asarray(a), np.asarray(b) 23 | if len(a) == 0 or len(b) == 0: 24 | return np.zeros((len(a), len(b))) 25 | a2, b2 = np.square(a).sum(axis=1), np.square(b).sum(axis=1) 26 | r2 = -2. * np.dot(a, b.T) + a2[:, None] + b2[None, :] 27 | r2 = np.clip(r2, 0., float(np.inf)) 28 | return r2 29 | 30 | 31 | def _cosine_distance(a, b, data_is_normalized=False): 32 | """Compute pair-wise cosine distance between points in `a` and `b`. 33 | 34 | Parameters 35 | ---------- 36 | a : array_like 37 | An NxM matrix of N samples of dimensionality M. 38 | b : array_like 39 | An LxM matrix of L samples of dimensionality M. 40 | data_is_normalized : Optional[bool] 41 | If True, assumes rows in a and b are unit length vectors. 42 | Otherwise, a and b are explicitly normalized to lenght 1. 43 | 44 | Returns 45 | ------- 46 | ndarray 47 | Returns a matrix of size len(a), len(b) such that eleement (i, j) 48 | contains the squared distance between `a[i]` and `b[j]`. 49 | 50 | """ 51 | if not data_is_normalized: 52 | a = np.asarray(a) / np.linalg.norm(a, axis=1, keepdims=True) 53 | b = np.asarray(b) / np.linalg.norm(b, axis=1, keepdims=True) 54 | return 1. - np.dot(a, b.T) 55 | 56 | 57 | def _nn_euclidean_distance(x, y): 58 | """ Helper function for nearest neighbor distance metric (Euclidean). 59 | 60 | Parameters 61 | ---------- 62 | x : ndarray 63 | A matrix of N row-vectors (sample points). 64 | y : ndarray 65 | A matrix of M row-vectors (query points). 66 | 67 | Returns 68 | ------- 69 | ndarray 70 | A vector of length M that contains for each entry in `y` the 71 | smallest Euclidean distance to a sample in `x`. 72 | 73 | """ 74 | distances = _pdist(x, y) 75 | return np.maximum(0.0, distances.min(axis=0)) 76 | 77 | 78 | def _nn_cosine_distance(x, y): 79 | """ Helper function for nearest neighbor distance metric (cosine). 80 | 81 | Parameters 82 | ---------- 83 | x : ndarray 84 | A matrix of N row-vectors (sample points). 85 | y : ndarray 86 | A matrix of M row-vectors (query points). 87 | 88 | Returns 89 | ------- 90 | ndarray 91 | A vector of length M that contains for each entry in `y` the 92 | smallest cosine distance to a sample in `x`. 93 | 94 | """ 95 | distances = _cosine_distance(x, y) 96 | return distances.min(axis=0) 97 | 98 | 99 | class NearestNeighborDistanceMetric(object): 100 | """ 101 | A nearest neighbor distance metric that, for each target, returns 102 | the closest distance to any sample that has been observed so far. 103 | 104 | Parameters 105 | ---------- 106 | metric : str 107 | Either "euclidean" or "cosine". 108 | matching_threshold: float 109 | The matching threshold. Samples with larger distance are considered an 110 | invalid match. 111 | budget : Optional[int] 112 | If not None, fix samples per class to at most this number. Removes 113 | the oldest samples when the budget is reached. 114 | 115 | Attributes 116 | ---------- 117 | samples : Dict[int -> List[ndarray]] 118 | A dictionary that maps from target identities to the list of samples 119 | that have been observed so far. 120 | 121 | """ 122 | 123 | def __init__(self, metric, matching_threshold, budget=None): 124 | 125 | if metric == "euclidean": 126 | self._metric = _nn_euclidean_distance 127 | elif metric == "cosine": 128 | self._metric = _nn_cosine_distance 129 | else: 130 | raise ValueError( 131 | "Invalid metric; must be either 'euclidean' or 'cosine'") 132 | self.matching_threshold = matching_threshold 133 | self.budget = budget 134 | self.samples = {} 135 | 136 | def partial_fit(self, features, targets, active_targets): 137 | """Update the distance metric with new data. 138 | 139 | Parameters 140 | ---------- 141 | features : ndarray 142 | An NxM matrix of N features of dimensionality M. 143 | targets : ndarray 144 | An integer array of associated target identities. 145 | active_targets : List[int] 146 | A list of targets that are currently present in the scene. 147 | 148 | """ 149 | for feature, target in zip(features, targets): 150 | self.samples.setdefault(target, []).append(feature) 151 | if self.budget is not None: 152 | self.samples[target] = self.samples[target][-self.budget:] 153 | self.samples = {k: self.samples[k] for k in active_targets} 154 | 155 | def distance(self, features, targets): 156 | """Compute distance between features and targets. 157 | 158 | Parameters 159 | ---------- 160 | features : ndarray 161 | An NxM matrix of N features of dimensionality M. 162 | targets : List[int] 163 | A list of targets to match the given `features` against. 164 | 165 | Returns 166 | ------- 167 | ndarray 168 | Returns a cost matrix of shape len(targets), len(features), where 169 | element (i, j) contains the closest squared distance between 170 | `targets[i]` and `features[j]`. 171 | 172 | """ 173 | cost_matrix = np.zeros((len(targets), len(features))) 174 | for i, target in enumerate(targets): 175 | cost_matrix[i, :] = self._metric(self.samples[target], features) 176 | return cost_matrix 177 | -------------------------------------------------------------------------------- /deep_sort_pytorch/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 | -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/sort/track.py: -------------------------------------------------------------------------------- 1 | # vim: expandtab:ts=4:sw=4 2 | 3 | 4 | class TrackState: 5 | """ 6 | Enumeration type for the single target track state. Newly created tracks are 7 | classified as `tentative` until enough evidence has been collected. Then, 8 | the track state is changed to `confirmed`. Tracks that are no longer alive 9 | are classified as `deleted` to mark them for removal from the set of active 10 | tracks. 11 | 12 | """ 13 | 14 | Tentative = 1 15 | Confirmed = 2 16 | Deleted = 3 17 | 18 | 19 | class Track: 20 | """ 21 | A single target track with state space `(x, y, a, h)` and associated 22 | velocities, where `(x, y)` is the center of the bounding box, `a` is the 23 | aspect ratio and `h` is the height. 24 | 25 | Parameters 26 | ---------- 27 | mean : ndarray 28 | Mean vector of the initial state distribution. 29 | covariance : ndarray 30 | Covariance matrix of the initial state distribution. 31 | track_id : int 32 | A unique track identifier. 33 | n_init : int 34 | Number of consecutive detections before the track is confirmed. The 35 | track state is set to `Deleted` if a miss occurs within the first 36 | `n_init` frames. 37 | max_age : int 38 | The maximum number of consecutive misses before the track state is 39 | set to `Deleted`. 40 | feature : Optional[ndarray] 41 | Feature vector of the detection this track originates from. If not None, 42 | this feature is added to the `features` cache. 43 | 44 | Attributes 45 | ---------- 46 | mean : ndarray 47 | Mean vector of the initial state distribution. 48 | covariance : ndarray 49 | Covariance matrix of the initial state distribution. 50 | track_id : int 51 | A unique track identifier. 52 | hits : int 53 | Total number of measurement updates. 54 | age : int 55 | Total number of frames since first occurance. 56 | time_since_update : int 57 | Total number of frames since last measurement update. 58 | state : TrackState 59 | The current track state. 60 | features : List[ndarray] 61 | A cache of features. On each measurement update, the associated feature 62 | vector is added to this list. 63 | 64 | """ 65 | 66 | def __init__(self, mean, covariance, track_id, n_init, max_age, 67 | feature=None): 68 | self.mean = mean 69 | self.covariance = covariance 70 | self.track_id = track_id 71 | self.hits = 1 72 | self.age = 1 73 | self.time_since_update = 0 74 | 75 | self.state = TrackState.Tentative 76 | self.features = [] 77 | if feature is not None: 78 | self.features.append(feature) 79 | 80 | self._n_init = n_init 81 | self._max_age = max_age 82 | 83 | def to_tlwh(self): 84 | """Get current position in bounding box format `(top left x, top left y, 85 | width, height)`. 86 | 87 | Returns 88 | ------- 89 | ndarray 90 | The bounding box. 91 | 92 | """ 93 | ret = self.mean[:4].copy() 94 | ret[2] *= ret[3] 95 | ret[:2] -= ret[2:] / 2 96 | return ret 97 | 98 | def to_tlbr(self): 99 | """Get current position in bounding box format `(min x, miny, max x, 100 | max y)`. 101 | 102 | Returns 103 | ------- 104 | ndarray 105 | The bounding box. 106 | 107 | """ 108 | ret = self.to_tlwh() 109 | ret[2:] = ret[:2] + ret[2:] 110 | return ret 111 | 112 | def increment_age(self): 113 | self.age += 1 114 | self.time_since_update += 1 115 | 116 | def predict(self, kf): 117 | """Propagate the state distribution to the current time step using a 118 | Kalman filter prediction step. 119 | 120 | Parameters 121 | ---------- 122 | kf : kalman_filter.KalmanFilter 123 | The Kalman filter. 124 | 125 | """ 126 | self.mean, self.covariance = kf.predict(self.mean, self.covariance) 127 | self.increment_age() 128 | 129 | def update(self, kf, detection): 130 | """Perform Kalman filter measurement update step and update the feature 131 | cache. 132 | 133 | Parameters 134 | ---------- 135 | kf : kalman_filter.KalmanFilter 136 | The Kalman filter. 137 | detection : Detection 138 | The associated detection. 139 | 140 | """ 141 | self.mean, self.covariance = kf.update( 142 | self.mean, self.covariance, detection.to_xyah()) 143 | self.features.append(detection.feature) 144 | 145 | self.hits += 1 146 | self.time_since_update = 0 147 | if self.state == TrackState.Tentative and self.hits >= self._n_init: 148 | self.state = TrackState.Confirmed 149 | 150 | def mark_missed(self): 151 | """Mark this track as missed (no association at the current time step). 152 | """ 153 | if self.state == TrackState.Tentative: 154 | self.state = TrackState.Deleted 155 | elif self.time_since_update > self._max_age: 156 | self.state = TrackState.Deleted 157 | 158 | def is_tentative(self): 159 | """Returns True if this track is tentative (unconfirmed). 160 | """ 161 | return self.state == TrackState.Tentative 162 | 163 | def is_confirmed(self): 164 | """Returns True if this track is confirmed.""" 165 | return self.state == TrackState.Confirmed 166 | 167 | def is_deleted(self): 168 | """Returns True if this track is dead and should be deleted.""" 169 | return self.state == TrackState.Deleted 170 | -------------------------------------------------------------------------------- /deep_sort_pytorch/deep_sort/sort/tracker.py: -------------------------------------------------------------------------------- 1 | # vim: expandtab:ts=4:sw=4 2 | from __future__ import absolute_import 3 | import numpy as np 4 | from . import kalman_filter 5 | from . import linear_assignment 6 | from . import iou_matching 7 | from .track import Track 8 | 9 | 10 | class Tracker: 11 | """ 12 | This is the multi-target tracker. 13 | 14 | Parameters 15 | ---------- 16 | metric : nn_matching.NearestNeighborDistanceMetric 17 | A distance metric for measurement-to-track association. 18 | max_age : int 19 | Maximum number of missed misses before a track is deleted. 20 | n_init : int 21 | Number of consecutive detections before the track is confirmed. The 22 | track state is set to `Deleted` if a miss occurs within the first 23 | `n_init` frames. 24 | 25 | Attributes 26 | ---------- 27 | metric : nn_matching.NearestNeighborDistanceMetric 28 | The distance metric used for measurement to track association. 29 | max_age : int 30 | Maximum number of missed misses before a track is deleted. 31 | n_init : int 32 | Number of frames that a track remains in initialization phase. 33 | kf : kalman_filter.KalmanFilter 34 | A Kalman filter to filter target trajectories in image space. 35 | tracks : List[Track] 36 | The list of active tracks at the current time step. 37 | 38 | """ 39 | 40 | def __init__(self, metric, max_iou_distance=0.7, max_age=70, n_init=3): 41 | self.metric = metric 42 | self.max_iou_distance = max_iou_distance 43 | self.max_age = max_age 44 | self.n_init = n_init 45 | 46 | self.kf = kalman_filter.KalmanFilter() 47 | self.tracks = [] 48 | self._next_id = 1 49 | 50 | def predict(self): 51 | """Propagate track state distributions one time step forward. 52 | 53 | This function should be called once every time step, before `update`. 54 | """ 55 | for track in self.tracks: 56 | track.predict(self.kf) 57 | 58 | def increment_ages(self): 59 | for track in self.tracks: 60 | track.increment_age() 61 | track.mark_missed() 62 | 63 | def update(self, detections): 64 | """Perform measurement update and track management. 65 | 66 | Parameters 67 | ---------- 68 | detections : List[deep_sort.detection.Detection] 69 | A list of detections at the current time step. 70 | 71 | """ 72 | # Run matching cascade. 73 | matches, unmatched_tracks, unmatched_detections = \ 74 | self._match(detections) 75 | 76 | # Update track set. 77 | for track_idx, detection_idx in matches: 78 | self.tracks[track_idx].update( 79 | self.kf, detections[detection_idx]) 80 | for track_idx in unmatched_tracks: 81 | self.tracks[track_idx].mark_missed() 82 | for detection_idx in unmatched_detections: 83 | self._initiate_track(detections[detection_idx]) 84 | self.tracks = [t for t in self.tracks if not t.is_deleted()] 85 | 86 | # Update distance metric. 87 | active_targets = [t.track_id for t in self.tracks if t.is_confirmed()] 88 | features, targets = [], [] 89 | for track in self.tracks: 90 | if not track.is_confirmed(): 91 | continue 92 | features += track.features 93 | targets += [track.track_id for _ in track.features] 94 | track.features = [] 95 | self.metric.partial_fit( 96 | np.asarray(features), np.asarray(targets), active_targets) 97 | 98 | def _match(self, detections): 99 | 100 | def gated_metric(tracks, dets, track_indices, detection_indices): 101 | features = np.array([dets[i].feature for i in detection_indices]) 102 | targets = np.array([tracks[i].track_id for i in track_indices]) 103 | cost_matrix = self.metric.distance(features, targets) 104 | cost_matrix = linear_assignment.gate_cost_matrix( 105 | self.kf, cost_matrix, tracks, dets, track_indices, 106 | detection_indices) 107 | 108 | return cost_matrix 109 | 110 | # Split track set into confirmed and unconfirmed tracks. 111 | confirmed_tracks = [ 112 | i for i, t in enumerate(self.tracks) if t.is_confirmed()] 113 | unconfirmed_tracks = [ 114 | i for i, t in enumerate(self.tracks) if not t.is_confirmed()] 115 | 116 | # Associate confirmed tracks using appearance features. 117 | matches_a, unmatched_tracks_a, unmatched_detections = \ 118 | linear_assignment.matching_cascade( 119 | gated_metric, self.metric.matching_threshold, self.max_age, 120 | self.tracks, detections, confirmed_tracks) 121 | 122 | # Associate remaining tracks together with unconfirmed tracks using IOU. 123 | iou_track_candidates = unconfirmed_tracks + [ 124 | k for k in unmatched_tracks_a if 125 | self.tracks[k].time_since_update == 1] 126 | unmatched_tracks_a = [ 127 | k for k in unmatched_tracks_a if 128 | self.tracks[k].time_since_update != 1] 129 | matches_b, unmatched_tracks_b, unmatched_detections = \ 130 | linear_assignment.min_cost_matching( 131 | iou_matching.iou_cost, self.max_iou_distance, self.tracks, 132 | detections, iou_track_candidates, unmatched_detections) 133 | 134 | matches = matches_a + matches_b 135 | unmatched_tracks = list(set(unmatched_tracks_a + unmatched_tracks_b)) 136 | return matches, unmatched_tracks, unmatched_detections 137 | 138 | def _initiate_track(self, detection): 139 | mean, covariance = self.kf.initiate(detection.to_xyah()) 140 | self.tracks.append(Track( 141 | mean, covariance, self._next_id, self.n_init, self.max_age, 142 | detection.feature)) 143 | self._next_id += 1 144 | -------------------------------------------------------------------------------- /deep_sort_pytorch/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/deep_sort_pytorch/utils/__init__.py -------------------------------------------------------------------------------- /deep_sort_pytorch/utils/asserts.py: -------------------------------------------------------------------------------- 1 | from os import environ 2 | 3 | 4 | def assert_in(file, files_to_check): 5 | if file not in files_to_check: 6 | raise AssertionError("{} does not exist in the list".format(str(file))) 7 | return True 8 | 9 | 10 | def assert_in_env(check_list: list): 11 | for item in check_list: 12 | assert_in(item, environ.keys()) 13 | return True 14 | -------------------------------------------------------------------------------- /deep_sort_pytorch/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) 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(img,(x1, y1),(x1+t_size[0]+3,y1+t_size[1]+4), color,-1) 29 | cv2.putText(img,label,(x1,y1+t_size[1]+4), cv2.FONT_HERSHEY_PLAIN, 2, [255,255,255], 2) 30 | return img 31 | 32 | 33 | 34 | if __name__ == '__main__': 35 | for i in range(82): 36 | print(compute_color_for_labels(i)) 37 | -------------------------------------------------------------------------------- /deep_sort_pytorch/utils/evaluation.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import copy 4 | import motmetrics as mm 5 | mm.lap.default_solver = 'lap' 6 | from utils.io import read_results, unzip_objs 7 | 8 | 9 | class Evaluator(object): 10 | 11 | def __init__(self, data_root, seq_name, data_type): 12 | self.data_root = data_root 13 | self.seq_name = seq_name 14 | self.data_type = data_type 15 | 16 | self.load_annotations() 17 | self.reset_accumulator() 18 | 19 | def load_annotations(self): 20 | assert self.data_type == 'mot' 21 | 22 | gt_filename = os.path.join(self.data_root, self.seq_name, 'gt', 'gt.txt') 23 | self.gt_frame_dict = read_results(gt_filename, self.data_type, is_gt=True) 24 | self.gt_ignore_frame_dict = read_results(gt_filename, self.data_type, is_ignore=True) 25 | 26 | def reset_accumulator(self): 27 | self.acc = mm.MOTAccumulator(auto_id=True) 28 | 29 | def eval_frame(self, frame_id, trk_tlwhs, trk_ids, rtn_events=False): 30 | # results 31 | trk_tlwhs = np.copy(trk_tlwhs) 32 | trk_ids = np.copy(trk_ids) 33 | 34 | # gts 35 | gt_objs = self.gt_frame_dict.get(frame_id, []) 36 | gt_tlwhs, gt_ids = unzip_objs(gt_objs)[:2] 37 | 38 | # ignore boxes 39 | ignore_objs = self.gt_ignore_frame_dict.get(frame_id, []) 40 | ignore_tlwhs = unzip_objs(ignore_objs)[0] 41 | 42 | 43 | # remove ignored results 44 | keep = np.ones(len(trk_tlwhs), dtype=bool) 45 | iou_distance = mm.distances.iou_matrix(ignore_tlwhs, trk_tlwhs, max_iou=0.5) 46 | if len(iou_distance) > 0: 47 | match_is, match_js = mm.lap.linear_sum_assignment(iou_distance) 48 | match_is, match_js = map(lambda a: np.asarray(a, dtype=int), [match_is, match_js]) 49 | match_ious = iou_distance[match_is, match_js] 50 | 51 | match_js = np.asarray(match_js, dtype=int) 52 | match_js = match_js[np.logical_not(np.isnan(match_ious))] 53 | keep[match_js] = False 54 | trk_tlwhs = trk_tlwhs[keep] 55 | trk_ids = trk_ids[keep] 56 | 57 | # get distance matrix 58 | iou_distance = mm.distances.iou_matrix(gt_tlwhs, trk_tlwhs, max_iou=0.5) 59 | 60 | # acc 61 | self.acc.update(gt_ids, trk_ids, iou_distance) 62 | 63 | if rtn_events and iou_distance.size > 0 and hasattr(self.acc, 'last_mot_events'): 64 | events = self.acc.last_mot_events # only supported by https://github.com/longcw/py-motmetrics 65 | else: 66 | events = None 67 | return events 68 | 69 | def eval_file(self, filename): 70 | self.reset_accumulator() 71 | 72 | result_frame_dict = read_results(filename, self.data_type, is_gt=False) 73 | frames = sorted(list(set(self.gt_frame_dict.keys()) | set(result_frame_dict.keys()))) 74 | for frame_id in frames: 75 | trk_objs = result_frame_dict.get(frame_id, []) 76 | trk_tlwhs, trk_ids = unzip_objs(trk_objs)[:2] 77 | self.eval_frame(frame_id, trk_tlwhs, trk_ids, rtn_events=False) 78 | 79 | return self.acc 80 | 81 | @staticmethod 82 | def get_summary(accs, names, metrics=('mota', 'num_switches', 'idp', 'idr', 'idf1', 'precision', 'recall')): 83 | names = copy.deepcopy(names) 84 | if metrics is None: 85 | metrics = mm.metrics.motchallenge_metrics 86 | metrics = copy.deepcopy(metrics) 87 | 88 | mh = mm.metrics.create() 89 | summary = mh.compute_many( 90 | accs, 91 | metrics=metrics, 92 | names=names, 93 | generate_overall=True 94 | ) 95 | 96 | return summary 97 | 98 | @staticmethod 99 | def save_summary(summary, filename): 100 | import pandas as pd 101 | writer = pd.ExcelWriter(filename) 102 | summary.to_excel(writer) 103 | writer.save() 104 | -------------------------------------------------------------------------------- /deep_sort_pytorch/utils/io.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import Dict 3 | import numpy as np 4 | 5 | # from utils.log import get_logger 6 | 7 | 8 | def write_results(filename, results, data_type): 9 | if data_type == 'mot': 10 | save_format = '{frame},{id},{x1},{y1},{w},{h},-1,-1,-1,-1\n' 11 | elif data_type == 'kitti': 12 | save_format = '{frame} {id} pedestrian 0 0 -10 {x1} {y1} {x2} {y2} -10 -10 -10 -1000 -1000 -1000 -10\n' 13 | else: 14 | raise ValueError(data_type) 15 | 16 | with open(filename, 'w') as f: 17 | for frame_id, tlwhs, track_ids in results: 18 | if data_type == 'kitti': 19 | frame_id -= 1 20 | for tlwh, track_id in zip(tlwhs, track_ids): 21 | if track_id < 0: 22 | continue 23 | x1, y1, w, h = tlwh 24 | x2, y2 = x1 + w, y1 + h 25 | line = save_format.format(frame=frame_id, id=track_id, x1=x1, y1=y1, x2=x2, y2=y2, w=w, h=h) 26 | f.write(line) 27 | 28 | 29 | # def write_results(filename, results_dict: Dict, data_type: str): 30 | # if not filename: 31 | # return 32 | # path = os.path.dirname(filename) 33 | # if not os.path.exists(path): 34 | # os.makedirs(path) 35 | 36 | # if data_type in ('mot', 'mcmot', 'lab'): 37 | # save_format = '{frame},{id},{x1},{y1},{w},{h},1,-1,-1,-1\n' 38 | # elif data_type == 'kitti': 39 | # save_format = '{frame} {id} pedestrian -1 -1 -10 {x1} {y1} {x2} {y2} -1 -1 -1 -1000 -1000 -1000 -10 {score}\n' 40 | # else: 41 | # raise ValueError(data_type) 42 | 43 | # with open(filename, 'w') as f: 44 | # for frame_id, frame_data in results_dict.items(): 45 | # if data_type == 'kitti': 46 | # frame_id -= 1 47 | # for tlwh, track_id in frame_data: 48 | # if track_id < 0: 49 | # continue 50 | # x1, y1, w, h = tlwh 51 | # x2, y2 = x1 + w, y1 + h 52 | # line = save_format.format(frame=frame_id, id=track_id, x1=x1, y1=y1, x2=x2, y2=y2, w=w, h=h, score=1.0) 53 | # f.write(line) 54 | # logger.info('Save results to {}'.format(filename)) 55 | 56 | 57 | def read_results(filename, data_type: str, is_gt=False, is_ignore=False): 58 | if data_type in ('mot', 'lab'): 59 | read_fun = read_mot_results 60 | else: 61 | raise ValueError('Unknown data type: {}'.format(data_type)) 62 | 63 | return read_fun(filename, is_gt, is_ignore) 64 | 65 | 66 | """ 67 | labels={'ped', ... % 1 68 | 'person_on_vhcl', ... % 2 69 | 'car', ... % 3 70 | 'bicycle', ... % 4 71 | 'mbike', ... % 5 72 | 'non_mot_vhcl', ... % 6 73 | 'static_person', ... % 7 74 | 'distractor', ... % 8 75 | 'occluder', ... % 9 76 | 'occluder_on_grnd', ... %10 77 | 'occluder_full', ... % 11 78 | 'reflection', ... % 12 79 | 'crowd' ... % 13 80 | }; 81 | """ 82 | 83 | 84 | def read_mot_results(filename, is_gt, is_ignore): 85 | valid_labels = {1} 86 | ignore_labels = {2, 7, 8, 12} 87 | results_dict = dict() 88 | if os.path.isfile(filename): 89 | with open(filename, 'r') as f: 90 | for line in f.readlines(): 91 | linelist = line.split(',') 92 | if len(linelist) < 7: 93 | continue 94 | fid = int(linelist[0]) 95 | if fid < 1: 96 | continue 97 | results_dict.setdefault(fid, list()) 98 | 99 | if is_gt: 100 | if 'MOT16-' in filename or 'MOT17-' in filename: 101 | label = int(float(linelist[7])) 102 | mark = int(float(linelist[6])) 103 | if mark == 0 or label not in valid_labels: 104 | continue 105 | score = 1 106 | elif is_ignore: 107 | if 'MOT16-' in filename or 'MOT17-' in filename: 108 | label = int(float(linelist[7])) 109 | vis_ratio = float(linelist[8]) 110 | if label not in ignore_labels and vis_ratio >= 0: 111 | continue 112 | else: 113 | continue 114 | score = 1 115 | else: 116 | score = float(linelist[6]) 117 | 118 | tlwh = tuple(map(float, linelist[2:6])) 119 | target_id = int(linelist[1]) 120 | 121 | results_dict[fid].append((tlwh, target_id, score)) 122 | 123 | return results_dict 124 | 125 | 126 | def unzip_objs(objs): 127 | if len(objs) > 0: 128 | tlwhs, ids, scores = zip(*objs) 129 | else: 130 | tlwhs, ids, scores = [], [], [] 131 | tlwhs = np.asarray(tlwhs, dtype=float).reshape(-1, 4) 132 | 133 | return tlwhs, ids, scores -------------------------------------------------------------------------------- /deep_sort_pytorch/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 | -------------------------------------------------------------------------------- /deep_sort_pytorch/utils/parser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import yaml 3 | from easydict import EasyDict as edict 4 | 5 | 6 | class YamlParser(edict): 7 | """ 8 | This is yaml parser based on EasyDict. 9 | """ 10 | 11 | def __init__(self, cfg_dict=None, config_file=None): 12 | if cfg_dict is None: 13 | cfg_dict = {} 14 | 15 | if config_file is not None: 16 | assert(os.path.isfile(config_file)) 17 | with open(config_file, 'r') as fo: 18 | yaml_ = yaml.load(fo.read(), Loader=yaml.FullLoader) 19 | cfg_dict.update(yaml_) 20 | 21 | super(YamlParser, self).__init__(cfg_dict) 22 | 23 | def merge_from_file(self, config_file): 24 | with open(config_file, 'r') as fo: 25 | yaml_ = yaml.load(fo.read(), Loader=yaml.FullLoader) 26 | self.update(yaml_) 27 | 28 | def merge_from_dict(self, config_dict): 29 | self.update(config_dict) 30 | 31 | 32 | def get_config(config_file=None): 33 | return YamlParser(config_file=config_file) 34 | 35 | 36 | if __name__ == "__main__": 37 | cfg = YamlParser(config_file="../configs/yolov3.yaml") 38 | cfg.merge_from_file("../configs/deep_sort.yaml") 39 | 40 | import ipdb 41 | ipdb.set_trace() 42 | -------------------------------------------------------------------------------- /deep_sort_pytorch/utils/tools.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | from time import time 3 | 4 | 5 | def is_video(ext: str): 6 | """ 7 | Returns true if ext exists in 8 | allowed_exts for video files. 9 | 10 | Args: 11 | ext: 12 | 13 | Returns: 14 | 15 | """ 16 | 17 | allowed_exts = ('.mp4', '.webm', '.ogg', '.avi', '.wmv', '.mkv', '.3gp') 18 | return any((ext.endswith(x) for x in allowed_exts)) 19 | 20 | 21 | def tik_tok(func): 22 | """ 23 | keep track of time for each process. 24 | Args: 25 | func: 26 | 27 | Returns: 28 | 29 | """ 30 | @wraps(func) 31 | def _time_it(*args, **kwargs): 32 | start = time() 33 | try: 34 | return func(*args, **kwargs) 35 | finally: 36 | end_ = time() 37 | print("time: {:.03f}s, fps: {:.03f}".format(end_ - start, 1 / (end_ - start))) 38 | 39 | return _time_it 40 | -------------------------------------------------------------------------------- /inference/input/test.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/inference/input/test.mp4 -------------------------------------------------------------------------------- /inference/input/test2.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/inference/input/test2.mp4 -------------------------------------------------------------------------------- /inference/input/test3.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/inference/input/test3.mp4 -------------------------------------------------------------------------------- /inference/output/test3.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/inference/output/test3.mp4 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # python3.8.18 CUDA10.2 2 | 3 | # pip install -r requirements.txt 4 | 5 | # base ---------------------------------------- 6 | 7 | matplotlib==3.4.3 8 | numpy==1.21.6 9 | opencv-python==4.5.3.56 10 | Pillow==9.2.0 11 | PyYAML==5.4.1 12 | scipy==1.7.1 13 | torch==1.7.1 14 | torchvision==0.8.2 15 | tqdm==4.64.1 16 | 17 | # plotting ------------------------------------ 18 | 19 | seaborn==0.11.2 20 | pandas==1.3.2 21 | 22 | # deep_sort ----------------------------------- 23 | 24 | easydict==1.9 25 | 26 | requests==2.26.0 27 | -------------------------------------------------------------------------------- /test.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/test.gif -------------------------------------------------------------------------------- /test3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/test3.gif -------------------------------------------------------------------------------- /yolov5/.dockerignore: -------------------------------------------------------------------------------- 1 | # Repo-specific DockerIgnore ------------------------------------------------------------------------------------------- 2 | #.git 3 | .cache 4 | .idea 5 | runs 6 | output 7 | coco 8 | storage.googleapis.com 9 | 10 | data/samples/* 11 | **/results*.txt 12 | *.jpg 13 | 14 | # Neural Network weights ----------------------------------------------------------------------------------------------- 15 | **/*.weights 16 | **/*.pt 17 | **/*.pth 18 | **/*.onnx 19 | **/*.mlmodel 20 | **/*.torchscript 21 | 22 | 23 | # Below Copied From .gitignore ----------------------------------------------------------------------------------------- 24 | # Below Copied From .gitignore ----------------------------------------------------------------------------------------- 25 | 26 | 27 | # GitHub Python GitIgnore ---------------------------------------------------------------------------------------------- 28 | # Byte-compiled / optimized / DLL files 29 | __pycache__/ 30 | *.py[cod] 31 | *$py.class 32 | 33 | # C extensions 34 | *.so 35 | 36 | # Distribution / packaging 37 | .Python 38 | env/ 39 | build/ 40 | develop-eggs/ 41 | dist/ 42 | downloads/ 43 | eggs/ 44 | .eggs/ 45 | lib/ 46 | lib64/ 47 | parts/ 48 | sdist/ 49 | var/ 50 | wheels/ 51 | *.egg-info/ 52 | wandb/ 53 | .installed.cfg 54 | *.egg 55 | 56 | # PyInstaller 57 | # Usually these files are written by a python script from a template 58 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 59 | *.manifest 60 | *.spec 61 | 62 | # Installer logs 63 | pip-log.txt 64 | pip-delete-this-directory.txt 65 | 66 | # Unit test / coverage reports 67 | htmlcov/ 68 | .tox/ 69 | .coverage 70 | .coverage.* 71 | .cache 72 | nosetests.xml 73 | coverage.xml 74 | *.cover 75 | .hypothesis/ 76 | 77 | # Translations 78 | *.mo 79 | *.pot 80 | 81 | # Django stuff: 82 | *.log 83 | local_settings.py 84 | 85 | # Flask stuff: 86 | instance/ 87 | .webassets-cache 88 | 89 | # Scrapy stuff: 90 | .scrapy 91 | 92 | # Sphinx documentation 93 | docs/_build/ 94 | 95 | # PyBuilder 96 | target/ 97 | 98 | # Jupyter Notebook 99 | .ipynb_checkpoints 100 | 101 | # pyenv 102 | .python-version 103 | 104 | # celery beat schedule file 105 | celerybeat-schedule 106 | 107 | # SageMath parsed files 108 | *.sage.py 109 | 110 | # dotenv 111 | .env 112 | 113 | # virtualenv 114 | .venv* 115 | venv*/ 116 | ENV*/ 117 | 118 | # Spyder project settings 119 | .spyderproject 120 | .spyproject 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | 131 | 132 | # https://github.com/github/gitignore/blob/master/Global/macOS.gitignore ----------------------------------------------- 133 | 134 | # General 135 | .DS_Store 136 | .AppleDouble 137 | .LSOverride 138 | 139 | # Icon must end with two \r 140 | Icon 141 | Icon? 142 | 143 | # Thumbnails 144 | ._* 145 | 146 | # Files that might appear in the root of a volume 147 | .DocumentRevisions-V100 148 | .fseventsd 149 | .Spotlight-V100 150 | .TemporaryItems 151 | .Trashes 152 | .VolumeIcon.icns 153 | .com.apple.timemachine.donotpresent 154 | 155 | # Directories potentially created on remote AFP share 156 | .AppleDB 157 | .AppleDesktop 158 | Network Trash Folder 159 | Temporary Items 160 | .apdisk 161 | 162 | 163 | # https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore 164 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 165 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 166 | 167 | # User-specific stuff: 168 | .idea/* 169 | .idea/**/workspace.xml 170 | .idea/**/tasks.xml 171 | .idea/dictionaries 172 | .html # Bokeh Plots 173 | .pg # TensorFlow Frozen Graphs 174 | .avi # videos 175 | 176 | # Sensitive or high-churn files: 177 | .idea/**/dataSources/ 178 | .idea/**/dataSources.ids 179 | .idea/**/dataSources.local.xml 180 | .idea/**/sqlDataSources.xml 181 | .idea/**/dynamic.xml 182 | .idea/**/uiDesigner.xml 183 | 184 | # Gradle: 185 | .idea/**/gradle.xml 186 | .idea/**/libraries 187 | 188 | # CMake 189 | cmake-build-debug/ 190 | cmake-build-release/ 191 | 192 | # Mongo Explorer plugin: 193 | .idea/**/mongoSettings.xml 194 | 195 | ## File-based project format: 196 | *.iws 197 | 198 | ## Plugin-specific files: 199 | 200 | # IntelliJ 201 | out/ 202 | 203 | # mpeltonen/sbt-idea plugin 204 | .idea_modules/ 205 | 206 | # JIRA plugin 207 | atlassian-ide-plugin.xml 208 | 209 | # Cursive Clojure plugin 210 | .idea/replstate.xml 211 | 212 | # Crashlytics plugin (for Android Studio and IntelliJ) 213 | com_crashlytics_export_strings.xml 214 | crashlytics.properties 215 | crashlytics-build.properties 216 | fabric.properties 217 | -------------------------------------------------------------------------------- /yolov5/.gitattributes: -------------------------------------------------------------------------------- 1 | # this drop notebooks from GitHub language stats 2 | *.ipynb linguist-vendored 3 | -------------------------------------------------------------------------------- /yolov5/.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: glenn-jocher 4 | patreon: ultralytics 5 | open_collective: ultralytics 6 | -------------------------------------------------------------------------------- /yolov5/.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🐛 Bug report" 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | Before submitting a bug report, please be aware that your issue **must be reproducible** with all of the following, otherwise it is non-actionable, and we can not help you: 11 | - **Current repo**: run `git fetch && git status -uno` to check and `git pull` to update repo 12 | - **Common dataset**: coco.yaml or coco128.yaml 13 | - **Common environment**: Colab, Google Cloud, or Docker image. See https://github.com/ultralytics/yolov5#environments 14 | 15 | If this is a custom dataset/training question you **must include** your `train*.jpg`, `test*.jpg` and `results.png` figures, or we can not help you. You can generate these with `utils.plot_results()`. 16 | 17 | 18 | ## 🐛 Bug 19 | A clear and concise description of what the bug is. 20 | 21 | 22 | ## To Reproduce (REQUIRED) 23 | 24 | Input: 25 | ``` 26 | import torch 27 | 28 | a = torch.tensor([5]) 29 | c = a / 0 30 | ``` 31 | 32 | Output: 33 | ``` 34 | Traceback (most recent call last): 35 | File "/Users/glennjocher/opt/anaconda3/envs/env1/lib/python3.7/site-packages/IPython/core/interactiveshell.py", line 3331, in run_code 36 | exec(code_obj, self.user_global_ns, self.user_ns) 37 | File "", line 5, in 38 | c = a / 0 39 | RuntimeError: ZeroDivisionError 40 | ``` 41 | 42 | 43 | ## Expected behavior 44 | A clear and concise description of what you expected to happen. 45 | 46 | 47 | ## Environment 48 | If applicable, add screenshots to help explain your problem. 49 | 50 | - OS: [e.g. Ubuntu] 51 | - GPU [e.g. 2080 Ti] 52 | 53 | 54 | ## Additional context 55 | Add any other context about the problem here. 56 | -------------------------------------------------------------------------------- /yolov5/.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🚀 Feature request" 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 🚀 Feature 11 | 12 | 13 | ## Motivation 14 | 15 | 16 | 17 | ## Pitch 18 | 19 | 20 | 21 | ## Alternatives 22 | 23 | 24 | 25 | ## Additional context 26 | 27 | 28 | -------------------------------------------------------------------------------- /yolov5/.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "❓Question" 3 | about: Ask a general question 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## ❔Question 11 | 12 | 13 | ## Additional context 14 | -------------------------------------------------------------------------------- /yolov5/.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | reviewers: 10 | - glenn-jocher 11 | labels: 12 | - dependencies 13 | -------------------------------------------------------------------------------- /yolov5/.github/workflows/ci-testing.yml: -------------------------------------------------------------------------------- 1 | name: CI CPU testing 2 | 3 | on: # https://help.github.com/en/actions/reference/events-that-trigger-workflows 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [ master ] 9 | schedule: 10 | - cron: '0 0 * * *' # Runs at 00:00 UTC every day 11 | 12 | jobs: 13 | cpu-tests: 14 | 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | os: [ubuntu-latest, macos-latest, windows-latest] 20 | python-version: [3.8] 21 | model: ['yolov5s'] # models to test 22 | 23 | # Timeout: https://stackoverflow.com/a/59076067/4521646 24 | timeout-minutes: 50 25 | steps: 26 | - uses: actions/checkout@v2 27 | - name: Set up Python ${{ matrix.python-version }} 28 | uses: actions/setup-python@v2 29 | with: 30 | python-version: ${{ matrix.python-version }} 31 | 32 | # Note: This uses an internal pip API and may not always work 33 | # https://github.com/actions/cache/blob/master/examples.md#multiple-oss-in-a-workflow 34 | - name: Get pip cache 35 | id: pip-cache 36 | run: | 37 | python -c "from pip._internal.locations import USER_CACHE_DIR; print('::set-output name=dir::' + USER_CACHE_DIR)" 38 | 39 | - name: Cache pip 40 | uses: actions/cache@v1 41 | with: 42 | path: ${{ steps.pip-cache.outputs.dir }} 43 | key: ${{ runner.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('requirements.txt') }} 44 | restore-keys: | 45 | ${{ runner.os }}-${{ matrix.python-version }}-pip- 46 | 47 | - name: Install dependencies 48 | run: | 49 | python -m pip install --upgrade pip 50 | pip install -qr requirements.txt -f https://download.pytorch.org/whl/cpu/torch_stable.html 51 | pip install -q onnx 52 | python --version 53 | pip --version 54 | pip list 55 | shell: bash 56 | 57 | - name: Download data 58 | run: | 59 | # curl -L -o tmp.zip https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128.zip 60 | # unzip -q tmp.zip -d ../ 61 | # rm tmp.zip 62 | 63 | - name: Tests workflow 64 | run: | 65 | # export PYTHONPATH="$PWD" # to run '$ python *.py' files in subdirectories 66 | di=cpu # inference devices # define device 67 | 68 | # train 69 | python train.py --img 128 --batch 16 --weights weights/${{ matrix.model }}.pt --cfg models/${{ matrix.model }}.yaml --epochs 1 --device $di 70 | # detect 71 | python detect.py --weights weights/${{ matrix.model }}.pt --device $di 72 | python detect.py --weights runs/train/exp/weights/last.pt --device $di 73 | # test 74 | python test.py --img 128 --batch 16 --weights weights/${{ matrix.model }}.pt --device $di 75 | python test.py --img 128 --batch 16 --weights runs/train/exp/weights/last.pt --device $di 76 | 77 | python hubconf.py # hub 78 | python models/yolo.py --cfg models/${{ matrix.model }}.yaml # inspect 79 | python models/export.py --img 128 --batch 1 --weights weights/${{ matrix.model }}.pt # export 80 | shell: bash 81 | -------------------------------------------------------------------------------- /yolov5/.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # This action runs GitHub's industry-leading static analysis engine, CodeQL, against a repository's source code to find security vulnerabilities. 2 | # https://github.com/github/codeql-action 3 | 4 | name: "CodeQL" 5 | 6 | on: 7 | schedule: 8 | - cron: '0 0 1 * *' # Runs at 00:00 UTC on the 1st of every month 9 | 10 | jobs: 11 | analyze: 12 | name: Analyze 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | language: [ 'python' ] 19 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 20 | # Learn more: 21 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 22 | 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@v2 26 | 27 | # Initializes the CodeQL tools for scanning. 28 | - name: Initialize CodeQL 29 | uses: github/codeql-action/init@v1 30 | with: 31 | languages: ${{ matrix.language }} 32 | # If you wish to specify custom queries, you can do so here or in a config file. 33 | # By default, queries listed here will override any specified in a config file. 34 | # Prefix the list here with "+" to use these queries and those in the config file. 35 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 36 | 37 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 38 | # If this step fails, then you should remove it and run the build manually (see below) 39 | - name: Autobuild 40 | uses: github/codeql-action/autobuild@v1 41 | 42 | # ℹ️ Command-line programs to run using the OS shell. 43 | # 📚 https://git.io/JvXDl 44 | 45 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 46 | # and modify them (or add more) to build your code if your project 47 | # uses a compiled language 48 | 49 | #- run: | 50 | # make bootstrap 51 | # make release 52 | 53 | - name: Perform CodeQL Analysis 54 | uses: github/codeql-action/analyze@v1 55 | -------------------------------------------------------------------------------- /yolov5/.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request_target, issues] 4 | 5 | jobs: 6 | greeting: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/first-interaction@v1 10 | with: 11 | repo-token: ${{ secrets.GITHUB_TOKEN }} 12 | pr-message: | 13 | 👋 Hello @${{ github.actor }}, thank you for submitting a 🚀 PR! To allow your work to be integrated as seamlessly as possible, we advise you to: 14 | - ✅ Verify your PR is **up-to-date with origin/master.** If your PR is behind origin/master an automatic [GitHub actions](https://github.com/ultralytics/yolov5/blob/master/.github/workflows/rebase.yml) rebase may be attempted by including the /rebase command in a comment body, or by running the following code, replacing 'feature' with the name of your local branch: 15 | ```bash 16 | git remote add upstream https://github.com/ultralytics/yolov5.git 17 | git fetch upstream 18 | git checkout feature # <----- replace 'feature' with local branch name 19 | git rebase upstream/master 20 | git push -u origin -f 21 | ``` 22 | - ✅ Verify all Continuous Integration (CI) **checks are passing**. 23 | - ✅ Reduce changes to the absolute **minimum** required for your bug fix or feature addition. _"It is not daily increase but daily decrease, hack away the unessential. The closer to the source, the less wastage there is."_ -Bruce Lee 24 | 25 | issue-message: | 26 | 👋 Hello @${{ github.actor }}, thank you for your interest in 🚀 YOLOv5! Please visit our ⭐️ [Tutorials](https://github.com/ultralytics/yolov5/wiki#tutorials) to get started, where you can find quickstart guides for simple tasks like [Custom Data Training](https://github.com/ultralytics/yolov5/wiki/Train-Custom-Data) all the way to advanced concepts like [Hyperparameter Evolution](https://github.com/ultralytics/yolov5/issues/607). 27 | 28 | If this is a 🐛 Bug Report, please provide screenshots and **minimum viable code to reproduce your issue**, otherwise we can not help you. 29 | 30 | If this is a custom training ❓ Question, please provide as much information as possible, including dataset images, training logs, screenshots, and a public link to online [W&B logging](https://github.com/ultralytics/yolov5/wiki/Train-Custom-Data#visualize) if available. 31 | 32 | For business inquiries or professional support requests please visit https://www.ultralytics.com or email Glenn Jocher at glenn.jocher@ultralytics.com. 33 | 34 | ## Requirements 35 | 36 | Python 3.8 or later with all [requirements.txt](https://github.com/ultralytics/yolov5/blob/master/requirements.txt) dependencies installed, including `torch>=1.7`. To install run: 37 | ```bash 38 | $ pip install -r requirements.txt 39 | ``` 40 | 41 | ## Environments 42 | 43 | YOLOv5 may be run in any of the following up-to-date verified environments (with all dependencies including [CUDA](https://developer.nvidia.com/cuda)/[CUDNN](https://developer.nvidia.com/cudnn), [Python](https://www.python.org/) and [PyTorch](https://pytorch.org/) preinstalled): 44 | 45 | - **Google Colab and Kaggle** notebooks with free GPU: Open In Colab Open In Kaggle 46 | - **Google Cloud** Deep Learning VM. See [GCP Quickstart Guide](https://github.com/ultralytics/yolov5/wiki/GCP-Quickstart) 47 | - **Amazon** Deep Learning AMI. See [AWS Quickstart Guide](https://github.com/ultralytics/yolov5/wiki/AWS-Quickstart) 48 | - **Docker Image**. See [Docker Quickstart Guide](https://github.com/ultralytics/yolov5/wiki/Docker-Quickstart) Docker Pulls 49 | 50 | 51 | ## Status 52 | 53 | ![CI CPU testing](https://github.com/ultralytics/yolov5/workflows/CI%20CPU%20testing/badge.svg) 54 | 55 | If this badge is green, all [YOLOv5 GitHub Actions](https://github.com/ultralytics/yolov5/actions) Continuous Integration (CI) tests are currently passing. CI tests verify correct operation of YOLOv5 training ([train.py](https://github.com/ultralytics/yolov5/blob/master/train.py)), testing ([test.py](https://github.com/ultralytics/yolov5/blob/master/test.py)), inference ([detect.py](https://github.com/ultralytics/yolov5/blob/master/detect.py)) and export ([export.py](https://github.com/ultralytics/yolov5/blob/master/models/export.py)) on MacOS, Windows, and Ubuntu every 24 hours and on every commit. 56 | 57 | -------------------------------------------------------------------------------- /yolov5/.github/workflows/rebase.yml: -------------------------------------------------------------------------------- 1 | name: Automatic Rebase 2 | # https://github.com/marketplace/actions/automatic-rebase 3 | 4 | on: 5 | issue_comment: 6 | types: [created] 7 | 8 | jobs: 9 | rebase: 10 | name: Rebase 11 | if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout the latest code 15 | uses: actions/checkout@v2 16 | with: 17 | fetch-depth: 0 18 | - name: Automatic Rebase 19 | uses: cirrus-actions/rebase@1.3.1 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /yolov5/.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close stale issues 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v3 11 | with: 12 | repo-token: ${{ secrets.GITHUB_TOKEN }} 13 | stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.' 14 | stale-pr-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.' 15 | days-before-stale: 30 16 | days-before-close: 5 17 | exempt-issue-labels: 'documentation,tutorial' 18 | operations-per-run: 100 # The maximum number of operations per run, used to control rate limiting. 19 | -------------------------------------------------------------------------------- /yolov5/.gitignore: -------------------------------------------------------------------------------- 1 | # Repo-specific GitIgnore ---------------------------------------------------------------------------------------------- 2 | *.jpg 3 | *.jpeg 4 | *.png 5 | *.bmp 6 | *.tif 7 | *.tiff 8 | *.heic 9 | *.JPG 10 | *.JPEG 11 | *.PNG 12 | *.BMP 13 | *.TIF 14 | *.TIFF 15 | *.HEIC 16 | *.mp4 17 | *.mov 18 | *.MOV 19 | *.avi 20 | *.data 21 | *.json 22 | 23 | *.cfg 24 | !cfg/yolov3*.cfg 25 | 26 | storage.googleapis.com 27 | runs/* 28 | data/* 29 | !data/images/zidane.jpg 30 | !data/images/bus.jpg 31 | !data/coco.names 32 | !data/coco_paper.names 33 | !data/coco.data 34 | !data/coco_*.data 35 | !data/coco_*.txt 36 | !data/trainvalno5k.shapes 37 | !data/*.sh 38 | 39 | pycocotools/* 40 | results*.txt 41 | gcp_test*.sh 42 | 43 | # Datasets ------------------------------------------------------------------------------------------------------------- 44 | coco/ 45 | coco128/ 46 | VOC/ 47 | 48 | # MATLAB GitIgnore ----------------------------------------------------------------------------------------------------- 49 | *.m~ 50 | *.mat 51 | !targets*.mat 52 | 53 | # Neural Network weights ----------------------------------------------------------------------------------------------- 54 | *.weights 55 | *.pt 56 | *.onnx 57 | *.mlmodel 58 | *.torchscript 59 | darknet53.conv.74 60 | yolov3-tiny.conv.15 61 | 62 | # GitHub Python GitIgnore ---------------------------------------------------------------------------------------------- 63 | # Byte-compiled / optimized / DLL files 64 | __pycache__/ 65 | *.py[cod] 66 | *$py.class 67 | 68 | # C extensions 69 | *.so 70 | 71 | # Distribution / packaging 72 | .Python 73 | env/ 74 | build/ 75 | develop-eggs/ 76 | dist/ 77 | downloads/ 78 | eggs/ 79 | .eggs/ 80 | lib/ 81 | lib64/ 82 | parts/ 83 | sdist/ 84 | var/ 85 | wheels/ 86 | *.egg-info/ 87 | wandb/ 88 | .installed.cfg 89 | *.egg 90 | 91 | 92 | # PyInstaller 93 | # Usually these files are written by a python script from a template 94 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 95 | *.manifest 96 | *.spec 97 | 98 | # Installer logs 99 | pip-log.txt 100 | pip-delete-this-directory.txt 101 | 102 | # Unit test / coverage reports 103 | htmlcov/ 104 | .tox/ 105 | .coverage 106 | .coverage.* 107 | .cache 108 | nosetests.xml 109 | coverage.xml 110 | *.cover 111 | .hypothesis/ 112 | 113 | # Translations 114 | *.mo 115 | *.pot 116 | 117 | # Django stuff: 118 | *.log 119 | local_settings.py 120 | 121 | # Flask stuff: 122 | instance/ 123 | .webassets-cache 124 | 125 | # Scrapy stuff: 126 | .scrapy 127 | 128 | # Sphinx documentation 129 | docs/_build/ 130 | 131 | # PyBuilder 132 | target/ 133 | 134 | # Jupyter Notebook 135 | .ipynb_checkpoints 136 | 137 | # pyenv 138 | .python-version 139 | 140 | # celery beat schedule file 141 | celerybeat-schedule 142 | 143 | # SageMath parsed files 144 | *.sage.py 145 | 146 | # dotenv 147 | .env 148 | 149 | # virtualenv 150 | .venv* 151 | venv*/ 152 | ENV*/ 153 | 154 | # Spyder project settings 155 | .spyderproject 156 | .spyproject 157 | 158 | # Rope project settings 159 | .ropeproject 160 | 161 | # mkdocs documentation 162 | /site 163 | 164 | # mypy 165 | .mypy_cache/ 166 | 167 | 168 | # https://github.com/github/gitignore/blob/master/Global/macOS.gitignore ----------------------------------------------- 169 | 170 | # General 171 | .DS_Store 172 | .AppleDouble 173 | .LSOverride 174 | 175 | # Icon must end with two \r 176 | Icon 177 | Icon? 178 | 179 | # Thumbnails 180 | ._* 181 | 182 | # Files that might appear in the root of a volume 183 | .DocumentRevisions-V100 184 | .fseventsd 185 | .Spotlight-V100 186 | .TemporaryItems 187 | .Trashes 188 | .VolumeIcon.icns 189 | .com.apple.timemachine.donotpresent 190 | 191 | # Directories potentially created on remote AFP share 192 | .AppleDB 193 | .AppleDesktop 194 | Network Trash Folder 195 | Temporary Items 196 | .apdisk 197 | 198 | 199 | # https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore 200 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 201 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 202 | 203 | # User-specific stuff: 204 | .idea/* 205 | .idea/**/workspace.xml 206 | .idea/**/tasks.xml 207 | .idea/dictionaries 208 | .html # Bokeh Plots 209 | .pg # TensorFlow Frozen Graphs 210 | .avi # videos 211 | 212 | # Sensitive or high-churn files: 213 | .idea/**/dataSources/ 214 | .idea/**/dataSources.ids 215 | .idea/**/dataSources.local.xml 216 | .idea/**/sqlDataSources.xml 217 | .idea/**/dynamic.xml 218 | .idea/**/uiDesigner.xml 219 | 220 | # Gradle: 221 | .idea/**/gradle.xml 222 | .idea/**/libraries 223 | 224 | # CMake 225 | cmake-build-debug/ 226 | cmake-build-release/ 227 | 228 | # Mongo Explorer plugin: 229 | .idea/**/mongoSettings.xml 230 | 231 | ## File-based project format: 232 | *.iws 233 | 234 | ## Plugin-specific files: 235 | 236 | # IntelliJ 237 | out/ 238 | 239 | # mpeltonen/sbt-idea plugin 240 | .idea_modules/ 241 | 242 | # JIRA plugin 243 | atlassian-ide-plugin.xml 244 | 245 | # Cursive Clojure plugin 246 | .idea/replstate.xml 247 | 248 | # Crashlytics plugin (for Android Studio and IntelliJ) 249 | com_crashlytics_export_strings.xml 250 | crashlytics.properties 251 | crashlytics-build.properties 252 | fabric.properties 253 | -------------------------------------------------------------------------------- /yolov5/Dockerfile: -------------------------------------------------------------------------------- 1 | # Start FROM Nvidia PyTorch image https://ngc.nvidia.com/catalog/containers/nvidia:pytorch 2 | FROM nvcr.io/nvidia/pytorch:21.03-py3 3 | 4 | # Install linux packages 5 | RUN apt update && apt install -y zip htop screen libgl1-mesa-glx 6 | 7 | # Install python dependencies 8 | COPY requirements.txt . 9 | RUN python -m pip install --upgrade pip 10 | RUN pip uninstall -y nvidia-tensorboard nvidia-tensorboard-plugin-dlprof 11 | RUN pip install --no-cache -r requirements.txt coremltools onnx gsutil notebook 12 | 13 | # Create working directory 14 | RUN mkdir -p /usr/src/app 15 | WORKDIR /usr/src/app 16 | 17 | # Copy contents 18 | COPY . /usr/src/app 19 | 20 | # Set environment variables 21 | ENV HOME=/usr/src/app 22 | 23 | 24 | # --------------------------------------------------- Extras Below --------------------------------------------------- 25 | 26 | # Build and Push 27 | # t=ultralytics/yolov5:latest && sudo docker build -t $t . && sudo docker push $t 28 | # for v in {300..303}; do t=ultralytics/coco:v$v && sudo docker build -t $t . && sudo docker push $t; done 29 | 30 | # Pull and Run 31 | # t=ultralytics/yolov5:latest && sudo docker pull $t && sudo docker run -it --ipc=host --gpus all $t 32 | 33 | # Pull and Run with local directory access 34 | # t=ultralytics/yolov5:latest && sudo docker pull $t && sudo docker run -it --ipc=host --gpus all -v "$(pwd)"/coco:/usr/src/coco $t 35 | 36 | # Kill all 37 | # sudo docker kill $(sudo docker ps -q) 38 | 39 | # Kill all image-based 40 | # sudo docker kill $(sudo docker ps -qa --filter ancestor=ultralytics/yolov5:latest) 41 | 42 | # Bash into running container 43 | # sudo docker exec -it 5a9b5863d93d bash 44 | 45 | # Bash into stopped container 46 | # id=$(sudo docker ps -qa) && sudo docker start $id && sudo docker exec -it $id bash 47 | 48 | # Send weights to GCP 49 | # python -c "from utils.general import *; strip_optimizer('runs/train/exp0_*/weights/best.pt', 'tmp.pt')" && gsutil cp tmp.pt gs://*.pt 50 | 51 | # Clean up 52 | # docker system prune -a --volumes 53 | -------------------------------------------------------------------------------- /yolov5/hubconf.py: -------------------------------------------------------------------------------- 1 | """YOLOv5 PyTorch Hub models https://pytorch.org/hub/ultralytics_yolov5/ 2 | 3 | Usage: 4 | import torch 5 | model = torch.hub.load('ultralytics/yolov5', 'yolov5s') 6 | """ 7 | 8 | from pathlib import Path 9 | 10 | import torch 11 | 12 | from models.yolo import Model, attempt_load 13 | from utils.general import check_requirements, set_logging 14 | from utils.google_utils import attempt_download 15 | from utils.torch_utils import select_device 16 | 17 | dependencies = ['torch', 'yaml'] 18 | check_requirements(Path(__file__).parent / 'requirements.txt', exclude=('tensorboard', 'pycocotools', 'thop')) 19 | 20 | 21 | def create(name, pretrained=True, channels=3, classes=80, autoshape=True, verbose=True): 22 | """Creates a specified YOLOv5 model 23 | 24 | Arguments: 25 | name (str): name of model, i.e. 'yolov5s' 26 | pretrained (bool): load pretrained weights into the model 27 | channels (int): number of input channels 28 | classes (int): number of model classes 29 | autoshape (bool): apply YOLOv5 .autoshape() wrapper to model 30 | verbose (bool): print all information to screen 31 | 32 | Returns: 33 | YOLOv5 pytorch model 34 | """ 35 | set_logging(verbose=verbose) 36 | fname = Path(name).with_suffix('.pt') # checkpoint filename 37 | try: 38 | if pretrained and channels == 3 and classes == 80: 39 | model = attempt_load(fname, map_location=torch.device('cpu')) # download/load FP32 model 40 | else: 41 | cfg = list((Path(__file__).parent / 'models').rglob(f'{name}.yaml'))[0] # model.yaml path 42 | model = Model(cfg, channels, classes) # create model 43 | if pretrained: 44 | attempt_download(fname) # download if not found locally 45 | ckpt = torch.load(fname, map_location=torch.device('cpu')) # load 46 | msd = model.state_dict() # model state_dict 47 | csd = ckpt['model'].float().state_dict() # checkpoint state_dict as FP32 48 | csd = {k: v for k, v in csd.items() if msd[k].shape == v.shape} # filter 49 | model.load_state_dict(csd, strict=False) # load 50 | if len(ckpt['model'].names) == classes: 51 | model.names = ckpt['model'].names # set class names attribute 52 | if autoshape: 53 | model = model.autoshape() # for file/URI/PIL/cv2/np inputs and NMS 54 | device = select_device('0' if torch.cuda.is_available() else 'cpu') # default to GPU if available 55 | return model.to(device) 56 | 57 | except Exception as e: 58 | help_url = 'https://github.com/ultralytics/yolov5/issues/36' 59 | s = 'Cache may be out of date, try `force_reload=True`. See %s for help.' % help_url 60 | raise Exception(s) from e 61 | 62 | 63 | def custom(path='path/to/model.pt', autoshape=True, verbose=True): 64 | # YOLOv5 custom or local model 65 | return create(path, autoshape=autoshape, verbose=verbose) 66 | 67 | 68 | def yolov5s(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True): 69 | # YOLOv5-small model https://github.com/ultralytics/yolov5 70 | return create('yolov5s', pretrained, channels, classes, autoshape, verbose) 71 | 72 | 73 | def yolov5m(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True): 74 | # YOLOv5-medium model https://github.com/ultralytics/yolov5 75 | return create('yolov5m', pretrained, channels, classes, autoshape, verbose) 76 | 77 | 78 | def yolov5l(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True): 79 | # YOLOv5-large model https://github.com/ultralytics/yolov5 80 | return create('yolov5l', pretrained, channels, classes, autoshape, verbose) 81 | 82 | 83 | def yolov5x(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True): 84 | # YOLOv5-xlarge model https://github.com/ultralytics/yolov5 85 | return create('yolov5x', pretrained, channels, classes, autoshape, verbose) 86 | 87 | 88 | def yolov5s6(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True): 89 | # YOLOv5-small-P6 model https://github.com/ultralytics/yolov5 90 | return create('yolov5s6', pretrained, channels, classes, autoshape, verbose) 91 | 92 | 93 | def yolov5m6(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True): 94 | # YOLOv5-medium-P6 model https://github.com/ultralytics/yolov5 95 | return create('yolov5m6', pretrained, channels, classes, autoshape, verbose) 96 | 97 | 98 | def yolov5l6(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True): 99 | # YOLOv5-large-P6 model https://github.com/ultralytics/yolov5 100 | return create('yolov5l6', pretrained, channels, classes, autoshape, verbose) 101 | 102 | 103 | def yolov5x6(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True): 104 | # YOLOv5-xlarge-P6 model https://github.com/ultralytics/yolov5 105 | return create('yolov5x6', pretrained, channels, classes, autoshape, verbose) 106 | 107 | 108 | if __name__ == '__main__': 109 | model = create(name='yolov5s', pretrained=True, channels=3, classes=80, autoshape=True, verbose=True) # pretrained 110 | # model = custom(path='path/to/model.pt') # custom 111 | 112 | # Verify inference 113 | import cv2 114 | import numpy as np 115 | from PIL import Image 116 | 117 | imgs = ['data/images/zidane.jpg', # filename 118 | 'https://github.com/ultralytics/yolov5/releases/download/v1.0/zidane.jpg', # URI 119 | cv2.imread('data/images/bus.jpg')[:, :, ::-1], # OpenCV 120 | Image.open('data/images/bus.jpg'), # PIL 121 | np.zeros((320, 640, 3))] # numpy 122 | 123 | results = model(imgs) # batched inference 124 | results.print() 125 | results.save() 126 | -------------------------------------------------------------------------------- /yolov5/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/yolov5/models/__init__.py -------------------------------------------------------------------------------- /yolov5/models/experimental.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 experimental modules 2 | 3 | import numpy as np 4 | import torch 5 | import torch.nn as nn 6 | 7 | from models.common import Conv, DWConv 8 | from utils.google_utils import attempt_download 9 | 10 | 11 | class CrossConv(nn.Module): 12 | # Cross Convolution Downsample 13 | def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False): 14 | # ch_in, ch_out, kernel, stride, groups, expansion, shortcut 15 | super(CrossConv, self).__init__() 16 | c_ = int(c2 * e) # hidden channels 17 | self.cv1 = Conv(c1, c_, (1, k), (1, s)) 18 | self.cv2 = Conv(c_, c2, (k, 1), (s, 1), g=g) 19 | self.add = shortcut and c1 == c2 20 | 21 | def forward(self, x): 22 | return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) 23 | 24 | 25 | class Sum(nn.Module): 26 | # Weighted sum of 2 or more layers https://arxiv.org/abs/1911.09070 27 | def __init__(self, n, weight=False): # n: number of inputs 28 | super(Sum, self).__init__() 29 | self.weight = weight # apply weights boolean 30 | self.iter = range(n - 1) # iter object 31 | if weight: 32 | self.w = nn.Parameter(-torch.arange(1., n) / 2, requires_grad=True) # layer weights 33 | 34 | def forward(self, x): 35 | y = x[0] # no weight 36 | if self.weight: 37 | w = torch.sigmoid(self.w) * 2 38 | for i in self.iter: 39 | y = y + x[i + 1] * w[i] 40 | else: 41 | for i in self.iter: 42 | y = y + x[i + 1] 43 | return y 44 | 45 | 46 | class GhostConv(nn.Module): 47 | # Ghost Convolution https://github.com/huawei-noah/ghostnet 48 | def __init__(self, c1, c2, k=1, s=1, g=1, act=True): # ch_in, ch_out, kernel, stride, groups 49 | super(GhostConv, self).__init__() 50 | c_ = c2 // 2 # hidden channels 51 | self.cv1 = Conv(c1, c_, k, s, None, g, act) 52 | self.cv2 = Conv(c_, c_, 5, 1, None, c_, act) 53 | 54 | def forward(self, x): 55 | y = self.cv1(x) 56 | return torch.cat([y, self.cv2(y)], 1) 57 | 58 | 59 | class GhostBottleneck(nn.Module): 60 | # Ghost Bottleneck https://github.com/huawei-noah/ghostnet 61 | def __init__(self, c1, c2, k=3, s=1): # ch_in, ch_out, kernel, stride 62 | super(GhostBottleneck, self).__init__() 63 | c_ = c2 // 2 64 | self.conv = nn.Sequential(GhostConv(c1, c_, 1, 1), # pw 65 | DWConv(c_, c_, k, s, act=False) if s == 2 else nn.Identity(), # dw 66 | GhostConv(c_, c2, 1, 1, act=False)) # pw-linear 67 | self.shortcut = nn.Sequential(DWConv(c1, c1, k, s, act=False), 68 | Conv(c1, c2, 1, 1, act=False)) if s == 2 else nn.Identity() 69 | 70 | def forward(self, x): 71 | return self.conv(x) + self.shortcut(x) 72 | 73 | 74 | class MixConv2d(nn.Module): 75 | # Mixed Depthwise Conv https://arxiv.org/abs/1907.09595 76 | def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True): 77 | super(MixConv2d, self).__init__() 78 | groups = len(k) 79 | if equal_ch: # equal c_ per group 80 | i = torch.linspace(0, groups - 1E-6, c2).floor() # c2 indices 81 | c_ = [(i == g).sum() for g in range(groups)] # intermediate channels 82 | else: # equal weight.numel() per group 83 | b = [c2] + [0] * groups 84 | a = np.eye(groups + 1, groups, k=-1) 85 | a -= np.roll(a, 1, axis=1) 86 | a *= np.array(k) ** 2 87 | a[0] = 1 88 | c_ = np.linalg.lstsq(a, b, rcond=None)[0].round() # solve for equal weight indices, ax = b 89 | 90 | self.m = nn.ModuleList([nn.Conv2d(c1, int(c_[g]), k[g], s, k[g] // 2, bias=False) for g in range(groups)]) 91 | self.bn = nn.BatchNorm2d(c2) 92 | self.act = nn.LeakyReLU(0.1, inplace=True) 93 | 94 | def forward(self, x): 95 | return x + self.act(self.bn(torch.cat([m(x) for m in self.m], 1))) 96 | 97 | 98 | class Ensemble(nn.ModuleList): 99 | # Ensemble of models 100 | def __init__(self): 101 | super(Ensemble, self).__init__() 102 | 103 | def forward(self, x, augment=False): 104 | y = [] 105 | for module in self: 106 | y.append(module(x, augment)[0]) 107 | # y = torch.stack(y).max(0)[0] # max ensemble 108 | # y = torch.stack(y).mean(0) # mean ensemble 109 | y = torch.cat(y, 1) # nms ensemble 110 | return y, None # inference, train output 111 | 112 | 113 | def attempt_load(weights, map_location=None, inplace=True): 114 | from models.yolo import Detect, Model 115 | 116 | # Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a 117 | model = Ensemble() 118 | for w in weights if isinstance(weights, list) else [weights]: 119 | attempt_download(w) 120 | ckpt = torch.load(w, map_location=map_location) # load 121 | model.append(ckpt['ema' if ckpt.get('ema') else 'model'].float().fuse().eval()) # FP32 model 122 | 123 | # Compatibility updates 124 | for m in model.modules(): 125 | if type(m) in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU, Detect, Model]: 126 | m.inplace = inplace # pytorch 1.7.0 compatibility 127 | elif type(m) is Conv: 128 | m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility 129 | 130 | if len(model) == 1: 131 | return model[-1] # return model 132 | else: 133 | print('Ensemble created with %s\n' % weights) 134 | for k in ['names', 'stride']: 135 | setattr(model, k, getattr(model[-1], k)) 136 | return model # return ensemble 137 | -------------------------------------------------------------------------------- /yolov5/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 yolov5s.pt --img 640 --batch 1 5 | """ 6 | 7 | import argparse 8 | import sys 9 | import time 10 | from pathlib import Path 11 | 12 | sys.path.append(Path(__file__).parent.parent.absolute().__str__()) # to run '$ python *.py' files in subdirectories 13 | 14 | import torch 15 | import torch.nn as nn 16 | from torch.utils.mobile_optimizer import optimize_for_mobile 17 | 18 | import models 19 | from models.experimental import attempt_load 20 | from utils.activations import Hardswish, SiLU 21 | from utils.general import colorstr, check_img_size, check_requirements, file_size, set_logging 22 | from utils.torch_utils import select_device 23 | 24 | if __name__ == '__main__': 25 | parser = argparse.ArgumentParser() 26 | parser.add_argument('--weights', type=str, default='./yolov5s.pt', help='weights path') 27 | parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='image size') # height, width 28 | parser.add_argument('--batch-size', type=int, default=1, help='batch size') 29 | parser.add_argument('--device', default='cpu', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') 30 | parser.add_argument('--half', action='store_true', help='FP16 half-precision export') 31 | parser.add_argument('--inplace', action='store_true', help='set YOLOv5 Detect() inplace=True') 32 | parser.add_argument('--dynamic', action='store_true', help='dynamic ONNX axes') # ONNX-only 33 | parser.add_argument('--simplify', action='store_true', help='simplify ONNX model') # ONNX-only 34 | opt = parser.parse_args() 35 | opt.img_size *= 2 if len(opt.img_size) == 1 else 1 # expand 36 | print(opt) 37 | set_logging() 38 | t = time.time() 39 | 40 | # Load PyTorch model 41 | device = select_device(opt.device) 42 | model = attempt_load(opt.weights, map_location=device) # load FP32 model 43 | labels = model.names 44 | 45 | # Checks 46 | gs = int(max(model.stride)) # grid size (max stride) 47 | opt.img_size = [check_img_size(x, gs) for x in opt.img_size] # verify img_size are gs-multiples 48 | assert not (opt.device.lower() == "cpu" and opt.half), '--half only compatible with GPU export, i.e. use --device 0' 49 | 50 | # Input 51 | img = torch.zeros(opt.batch_size, 3, *opt.img_size).to(device) # image size(1,3,320,192) iDetection 52 | 53 | # Update model 54 | if opt.half: 55 | img, model = img.half(), model.half() # to FP16 56 | for k, m in model.named_modules(): 57 | m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility 58 | if isinstance(m, models.common.Conv): # assign export-friendly activations 59 | if isinstance(m.act, nn.Hardswish): 60 | m.act = Hardswish() 61 | elif isinstance(m.act, nn.SiLU): 62 | m.act = SiLU() 63 | elif isinstance(m, models.yolo.Detect): 64 | m.inplace = opt.inplace 65 | m.onnx_dynamic = opt.dynamic 66 | # m.forward = m.forward_export # assign forward (optional) 67 | 68 | for _ in range(2): 69 | y = model(img) # dry runs 70 | print(f"\n{colorstr('PyTorch:')} starting from {opt.weights} ({file_size(opt.weights):.1f} MB)") 71 | 72 | # TorchScript export ----------------------------------------------------------------------------------------------- 73 | prefix = colorstr('TorchScript:') 74 | try: 75 | print(f'\n{prefix} starting export with torch {torch.__version__}...') 76 | f = opt.weights.replace('.pt', '.torchscript.pt') # filename 77 | ts = torch.jit.trace(model, img, strict=False) 78 | ts = optimize_for_mobile(ts) # https://pytorch.org/tutorials/recipes/script_optimized.html 79 | ts.save(f) 80 | print(f'{prefix} export success, saved as {f} ({file_size(f):.1f} MB)') 81 | except Exception as e: 82 | print(f'{prefix} export failure: {e}') 83 | 84 | # ONNX export ------------------------------------------------------------------------------------------------------ 85 | prefix = colorstr('ONNX:') 86 | try: 87 | import onnx 88 | 89 | print(f'{prefix} starting export with onnx {onnx.__version__}...') 90 | f = opt.weights.replace('.pt', '.onnx') # filename 91 | torch.onnx.export(model, img, f, verbose=False, opset_version=12, input_names=['images'], 92 | dynamic_axes={'images': {0: 'batch', 2: 'height', 3: 'width'}, # size(1,3,640,640) 93 | 'output': {0: 'batch', 2: 'y', 3: 'x'}} if opt.dynamic else None) 94 | 95 | # Checks 96 | model_onnx = onnx.load(f) # load onnx model 97 | onnx.checker.check_model(model_onnx) # check onnx model 98 | # print(onnx.helper.printable_graph(model_onnx.graph)) # print 99 | 100 | # Simplify 101 | if opt.simplify: 102 | try: 103 | check_requirements(['onnx-simplifier']) 104 | import onnxsim 105 | 106 | print(f'{prefix} simplifying with onnx-simplifier {onnxsim.__version__}...') 107 | model_onnx, check = onnxsim.simplify(model_onnx, 108 | dynamic_input_shape=opt.dynamic, 109 | input_shapes={'images': list(img.shape)} if opt.dynamic else None) 110 | assert check, 'assert check failed' 111 | onnx.save(model_onnx, f) 112 | except Exception as e: 113 | print(f'{prefix} simplifier failure: {e}') 114 | print(f'{prefix} export success, saved as {f} ({file_size(f):.1f} MB)') 115 | except Exception as e: 116 | print(f'{prefix} export failure: {e}') 117 | 118 | # CoreML export ---------------------------------------------------------------------------------------------------- 119 | prefix = colorstr('CoreML:') 120 | try: 121 | import coremltools as ct 122 | 123 | print(f'{prefix} starting export with coremltools {ct.__version__}...') 124 | # convert model from torchscript and apply pixel scaling as per detect.py 125 | model = ct.convert(ts, inputs=[ct.ImageType(name='image', shape=img.shape, scale=1 / 255.0, bias=[0, 0, 0])]) 126 | f = opt.weights.replace('.pt', '.mlmodel') # filename 127 | model.save(f) 128 | print(f'{prefix} export success, saved as {f} ({file_size(f):.1f} MB)') 129 | except Exception as e: 130 | print(f'{prefix} export failure: {e}') 131 | 132 | # Finish 133 | print(f'\nExport complete ({time.time() - t:.2f}s). Visualize with https://github.com/lutzroeder/netron.') 134 | -------------------------------------------------------------------------------- /yolov5/models/hub/anchors.yaml: -------------------------------------------------------------------------------- 1 | # Default YOLOv5 anchors for COCO data 2 | 3 | 4 | # P5 ------------------------------------------------------------------------------------------------------------------- 5 | # P5-640: 6 | anchors_p5_640: 7 | - [ 10,13, 16,30, 33,23 ] # P3/8 8 | - [ 30,61, 62,45, 59,119 ] # P4/16 9 | - [ 116,90, 156,198, 373,326 ] # P5/32 10 | 11 | 12 | # P6 ------------------------------------------------------------------------------------------------------------------- 13 | # P6-640: thr=0.25: 0.9964 BPR, 5.54 anchors past thr, n=12, img_size=640, metric_all=0.281/0.716-mean/best, past_thr=0.469-mean: 9,11, 21,19, 17,41, 43,32, 39,70, 86,64, 65,131, 134,130, 120,265, 282,180, 247,354, 512,387 14 | anchors_p6_640: 15 | - [ 9,11, 21,19, 17,41 ] # P3/8 16 | - [ 43,32, 39,70, 86,64 ] # P4/16 17 | - [ 65,131, 134,130, 120,265 ] # P5/32 18 | - [ 282,180, 247,354, 512,387 ] # P6/64 19 | 20 | # P6-1280: thr=0.25: 0.9950 BPR, 5.55 anchors past thr, n=12, img_size=1280, metric_all=0.281/0.714-mean/best, past_thr=0.468-mean: 19,27, 44,40, 38,94, 96,68, 86,152, 180,137, 140,301, 303,264, 238,542, 436,615, 739,380, 925,792 21 | anchors_p6_1280: 22 | - [ 19,27, 44,40, 38,94 ] # P3/8 23 | - [ 96,68, 86,152, 180,137 ] # P4/16 24 | - [ 140,301, 303,264, 238,542 ] # P5/32 25 | - [ 436,615, 739,380, 925,792 ] # P6/64 26 | 27 | # P6-1920: thr=0.25: 0.9950 BPR, 5.55 anchors past thr, n=12, img_size=1920, metric_all=0.281/0.714-mean/best, past_thr=0.468-mean: 28,41, 67,59, 57,141, 144,103, 129,227, 270,205, 209,452, 455,396, 358,812, 653,922, 1109,570, 1387,1187 28 | anchors_p6_1920: 29 | - [ 28,41, 67,59, 57,141 ] # P3/8 30 | - [ 144,103, 129,227, 270,205 ] # P4/16 31 | - [ 209,452, 455,396, 358,812 ] # P5/32 32 | - [ 653,922, 1109,570, 1387,1187 ] # P6/64 33 | 34 | 35 | # P7 ------------------------------------------------------------------------------------------------------------------- 36 | # P7-640: thr=0.25: 0.9962 BPR, 6.76 anchors past thr, n=15, img_size=640, metric_all=0.275/0.733-mean/best, past_thr=0.466-mean: 11,11, 13,30, 29,20, 30,46, 61,38, 39,92, 78,80, 146,66, 79,163, 149,150, 321,143, 157,303, 257,402, 359,290, 524,372 37 | anchors_p7_640: 38 | - [ 11,11, 13,30, 29,20 ] # P3/8 39 | - [ 30,46, 61,38, 39,92 ] # P4/16 40 | - [ 78,80, 146,66, 79,163 ] # P5/32 41 | - [ 149,150, 321,143, 157,303 ] # P6/64 42 | - [ 257,402, 359,290, 524,372 ] # P7/128 43 | 44 | # P7-1280: thr=0.25: 0.9968 BPR, 6.71 anchors past thr, n=15, img_size=1280, metric_all=0.273/0.732-mean/best, past_thr=0.463-mean: 19,22, 54,36, 32,77, 70,83, 138,71, 75,173, 165,159, 148,334, 375,151, 334,317, 251,626, 499,474, 750,326, 534,814, 1079,818 45 | anchors_p7_1280: 46 | - [ 19,22, 54,36, 32,77 ] # P3/8 47 | - [ 70,83, 138,71, 75,173 ] # P4/16 48 | - [ 165,159, 148,334, 375,151 ] # P5/32 49 | - [ 334,317, 251,626, 499,474 ] # P6/64 50 | - [ 750,326, 534,814, 1079,818 ] # P7/128 51 | 52 | # P7-1920: thr=0.25: 0.9968 BPR, 6.71 anchors past thr, n=15, img_size=1920, metric_all=0.273/0.732-mean/best, past_thr=0.463-mean: 29,34, 81,55, 47,115, 105,124, 207,107, 113,259, 247,238, 222,500, 563,227, 501,476, 376,939, 749,711, 1126,489, 801,1222, 1618,1227 53 | anchors_p7_1920: 54 | - [ 29,34, 81,55, 47,115 ] # P3/8 55 | - [ 105,124, 207,107, 113,259 ] # P4/16 56 | - [ 247,238, 222,500, 563,227 ] # P5/32 57 | - [ 501,476, 376,939, 749,711 ] # P6/64 58 | - [ 1126,489, 801,1222, 1618,1227 ] # P7/128 59 | -------------------------------------------------------------------------------- /yolov5/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 | -------------------------------------------------------------------------------- /yolov5/models/hub/yolov3-tiny.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,14, 23,27, 37,58] # P4/16 9 | - [81,82, 135,169, 344,319] # P5/32 10 | 11 | # YOLOv3-tiny backbone 12 | backbone: 13 | # [from, number, module, args] 14 | [[-1, 1, Conv, [16, 3, 1]], # 0 15 | [-1, 1, nn.MaxPool2d, [2, 2, 0]], # 1-P1/2 16 | [-1, 1, Conv, [32, 3, 1]], 17 | [-1, 1, nn.MaxPool2d, [2, 2, 0]], # 3-P2/4 18 | [-1, 1, Conv, [64, 3, 1]], 19 | [-1, 1, nn.MaxPool2d, [2, 2, 0]], # 5-P3/8 20 | [-1, 1, Conv, [128, 3, 1]], 21 | [-1, 1, nn.MaxPool2d, [2, 2, 0]], # 7-P4/16 22 | [-1, 1, Conv, [256, 3, 1]], 23 | [-1, 1, nn.MaxPool2d, [2, 2, 0]], # 9-P5/32 24 | [-1, 1, Conv, [512, 3, 1]], 25 | [-1, 1, nn.ZeroPad2d, [[0, 1, 0, 1]]], # 11 26 | [-1, 1, nn.MaxPool2d, [2, 1, 0]], # 12 27 | ] 28 | 29 | # YOLOv3-tiny head 30 | head: 31 | [[-1, 1, Conv, [1024, 3, 1]], 32 | [-1, 1, Conv, [256, 1, 1]], 33 | [-1, 1, Conv, [512, 3, 1]], # 15 (P5/32-large) 34 | 35 | [-2, 1, Conv, [128, 1, 1]], 36 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 37 | [[-1, 8], 1, Concat, [1]], # cat backbone P4 38 | [-1, 1, Conv, [256, 3, 1]], # 19 (P4/16-medium) 39 | 40 | [[19, 15], 1, Detect, [nc, anchors]], # Detect(P4, P5) 41 | ] 42 | -------------------------------------------------------------------------------- /yolov5/models/hub/yolov3.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 head 29 | head: 30 | [[-1, 1, Bottleneck, [1024, False]], 31 | [-1, 1, Conv, [512, [1, 1]]], 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 | -------------------------------------------------------------------------------- /yolov5/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 | -------------------------------------------------------------------------------- /yolov5/models/hub/yolov5-p2.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: 3 8 | 9 | # YOLOv5 backbone 10 | backbone: 11 | # [from, number, module, args] 12 | [ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2 13 | [ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4 14 | [ -1, 3, C3, [ 128 ] ], 15 | [ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8 16 | [ -1, 9, C3, [ 256 ] ], 17 | [ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16 18 | [ -1, 9, C3, [ 512 ] ], 19 | [ -1, 1, Conv, [ 1024, 3, 2 ] ], # 7-P5/32 20 | [ -1, 1, SPP, [ 1024, [ 5, 9, 13 ] ] ], 21 | [ -1, 3, C3, [ 1024, False ] ], # 9 22 | ] 23 | 24 | # YOLOv5 head 25 | head: 26 | [ [ -1, 1, Conv, [ 512, 1, 1 ] ], 27 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 28 | [ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4 29 | [ -1, 3, C3, [ 512, False ] ], # 13 30 | 31 | [ -1, 1, Conv, [ 256, 1, 1 ] ], 32 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 33 | [ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3 34 | [ -1, 3, C3, [ 256, False ] ], # 17 (P3/8-small) 35 | 36 | [ -1, 1, Conv, [ 128, 1, 1 ] ], 37 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 38 | [ [ -1, 2 ], 1, Concat, [ 1 ] ], # cat backbone P2 39 | [ -1, 1, C3, [ 128, False ] ], # 21 (P2/4-xsmall) 40 | 41 | [ -1, 1, Conv, [ 128, 3, 2 ] ], 42 | [ [ -1, 18 ], 1, Concat, [ 1 ] ], # cat head P3 43 | [ -1, 3, C3, [ 256, False ] ], # 24 (P3/8-small) 44 | 45 | [ -1, 1, Conv, [ 256, 3, 2 ] ], 46 | [ [ -1, 14 ], 1, Concat, [ 1 ] ], # cat head P4 47 | [ -1, 3, C3, [ 512, False ] ], # 27 (P4/16-medium) 48 | 49 | [ -1, 1, Conv, [ 512, 3, 2 ] ], 50 | [ [ -1, 10 ], 1, Concat, [ 1 ] ], # cat head P5 51 | [ -1, 3, C3, [ 1024, False ] ], # 30 (P5/32-large) 52 | 53 | [ [ 24, 27, 30 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5) 54 | ] 55 | -------------------------------------------------------------------------------- /yolov5/models/hub/yolov5-p6.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: 3 8 | 9 | # YOLOv5 backbone 10 | backbone: 11 | # [from, number, module, args] 12 | [ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2 13 | [ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4 14 | [ -1, 3, C3, [ 128 ] ], 15 | [ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8 16 | [ -1, 9, C3, [ 256 ] ], 17 | [ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16 18 | [ -1, 9, C3, [ 512 ] ], 19 | [ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32 20 | [ -1, 3, C3, [ 768 ] ], 21 | [ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64 22 | [ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ], 23 | [ -1, 3, C3, [ 1024, False ] ], # 11 24 | ] 25 | 26 | # YOLOv5 head 27 | head: 28 | [ [ -1, 1, Conv, [ 768, 1, 1 ] ], 29 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 30 | [ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5 31 | [ -1, 3, C3, [ 768, False ] ], # 15 32 | 33 | [ -1, 1, Conv, [ 512, 1, 1 ] ], 34 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 35 | [ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4 36 | [ -1, 3, C3, [ 512, False ] ], # 19 37 | 38 | [ -1, 1, Conv, [ 256, 1, 1 ] ], 39 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 40 | [ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3 41 | [ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small) 42 | 43 | [ -1, 1, Conv, [ 256, 3, 2 ] ], 44 | [ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4 45 | [ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium) 46 | 47 | [ -1, 1, Conv, [ 512, 3, 2 ] ], 48 | [ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5 49 | [ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large) 50 | 51 | [ -1, 1, Conv, [ 768, 3, 2 ] ], 52 | [ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6 53 | [ -1, 3, C3, [ 1024, False ] ], # 32 (P5/64-xlarge) 54 | 55 | [ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6) 56 | ] 57 | -------------------------------------------------------------------------------- /yolov5/models/hub/yolov5-p7.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: 3 8 | 9 | # YOLOv5 backbone 10 | backbone: 11 | # [from, number, module, args] 12 | [ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2 13 | [ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4 14 | [ -1, 3, C3, [ 128 ] ], 15 | [ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8 16 | [ -1, 9, C3, [ 256 ] ], 17 | [ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16 18 | [ -1, 9, C3, [ 512 ] ], 19 | [ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32 20 | [ -1, 3, C3, [ 768 ] ], 21 | [ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64 22 | [ -1, 3, C3, [ 1024 ] ], 23 | [ -1, 1, Conv, [ 1280, 3, 2 ] ], # 11-P7/128 24 | [ -1, 1, SPP, [ 1280, [ 3, 5 ] ] ], 25 | [ -1, 3, C3, [ 1280, False ] ], # 13 26 | ] 27 | 28 | # YOLOv5 head 29 | head: 30 | [ [ -1, 1, Conv, [ 1024, 1, 1 ] ], 31 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 32 | [ [ -1, 10 ], 1, Concat, [ 1 ] ], # cat backbone P6 33 | [ -1, 3, C3, [ 1024, False ] ], # 17 34 | 35 | [ -1, 1, Conv, [ 768, 1, 1 ] ], 36 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 37 | [ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5 38 | [ -1, 3, C3, [ 768, False ] ], # 21 39 | 40 | [ -1, 1, Conv, [ 512, 1, 1 ] ], 41 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 42 | [ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4 43 | [ -1, 3, C3, [ 512, False ] ], # 25 44 | 45 | [ -1, 1, Conv, [ 256, 1, 1 ] ], 46 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 47 | [ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3 48 | [ -1, 3, C3, [ 256, False ] ], # 29 (P3/8-small) 49 | 50 | [ -1, 1, Conv, [ 256, 3, 2 ] ], 51 | [ [ -1, 26 ], 1, Concat, [ 1 ] ], # cat head P4 52 | [ -1, 3, C3, [ 512, False ] ], # 32 (P4/16-medium) 53 | 54 | [ -1, 1, Conv, [ 512, 3, 2 ] ], 55 | [ [ -1, 22 ], 1, Concat, [ 1 ] ], # cat head P5 56 | [ -1, 3, C3, [ 768, False ] ], # 35 (P5/32-large) 57 | 58 | [ -1, 1, Conv, [ 768, 3, 2 ] ], 59 | [ [ -1, 18 ], 1, Concat, [ 1 ] ], # cat head P6 60 | [ -1, 3, C3, [ 1024, False ] ], # 38 (P6/64-xlarge) 61 | 62 | [ -1, 1, Conv, [ 1024, 3, 2 ] ], 63 | [ [ -1, 14 ], 1, Concat, [ 1 ] ], # cat head P7 64 | [ -1, 3, C3, [ 1280, False ] ], # 41 (P7/128-xxlarge) 65 | 66 | [ [ 29, 32, 35, 38, 41 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6, P7) 67 | ] 68 | -------------------------------------------------------------------------------- /yolov5/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 | - [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 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(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /yolov5/models/hub/yolov5l6.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 | - [ 19,27, 44,40, 38,94 ] # P3/8 9 | - [ 96,68, 86,152, 180,137 ] # P4/16 10 | - [ 140,301, 303,264, 238,542 ] # P5/32 11 | - [ 436,615, 739,380, 925,792 ] # P6/64 12 | 13 | # YOLOv5 backbone 14 | backbone: 15 | # [from, number, module, args] 16 | [ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2 17 | [ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4 18 | [ -1, 3, C3, [ 128 ] ], 19 | [ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8 20 | [ -1, 9, C3, [ 256 ] ], 21 | [ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16 22 | [ -1, 9, C3, [ 512 ] ], 23 | [ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32 24 | [ -1, 3, C3, [ 768 ] ], 25 | [ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64 26 | [ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ], 27 | [ -1, 3, C3, [ 1024, False ] ], # 11 28 | ] 29 | 30 | # YOLOv5 head 31 | head: 32 | [ [ -1, 1, Conv, [ 768, 1, 1 ] ], 33 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 34 | [ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5 35 | [ -1, 3, C3, [ 768, False ] ], # 15 36 | 37 | [ -1, 1, Conv, [ 512, 1, 1 ] ], 38 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 39 | [ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4 40 | [ -1, 3, C3, [ 512, False ] ], # 19 41 | 42 | [ -1, 1, Conv, [ 256, 1, 1 ] ], 43 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 44 | [ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3 45 | [ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small) 46 | 47 | [ -1, 1, Conv, [ 256, 3, 2 ] ], 48 | [ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4 49 | [ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium) 50 | 51 | [ -1, 1, Conv, [ 512, 3, 2 ] ], 52 | [ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5 53 | [ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large) 54 | 55 | [ -1, 1, Conv, [ 768, 3, 2 ] ], 56 | [ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6 57 | [ -1, 3, C3, [ 1024, False ] ], # 32 (P6/64-xlarge) 58 | 59 | [ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6) 60 | ] 61 | -------------------------------------------------------------------------------- /yolov5/models/hub/yolov5m6.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 | - [ 19,27, 44,40, 38,94 ] # P3/8 9 | - [ 96,68, 86,152, 180,137 ] # P4/16 10 | - [ 140,301, 303,264, 238,542 ] # P5/32 11 | - [ 436,615, 739,380, 925,792 ] # P6/64 12 | 13 | # YOLOv5 backbone 14 | backbone: 15 | # [from, number, module, args] 16 | [ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2 17 | [ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4 18 | [ -1, 3, C3, [ 128 ] ], 19 | [ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8 20 | [ -1, 9, C3, [ 256 ] ], 21 | [ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16 22 | [ -1, 9, C3, [ 512 ] ], 23 | [ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32 24 | [ -1, 3, C3, [ 768 ] ], 25 | [ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64 26 | [ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ], 27 | [ -1, 3, C3, [ 1024, False ] ], # 11 28 | ] 29 | 30 | # YOLOv5 head 31 | head: 32 | [ [ -1, 1, Conv, [ 768, 1, 1 ] ], 33 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 34 | [ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5 35 | [ -1, 3, C3, [ 768, False ] ], # 15 36 | 37 | [ -1, 1, Conv, [ 512, 1, 1 ] ], 38 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 39 | [ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4 40 | [ -1, 3, C3, [ 512, False ] ], # 19 41 | 42 | [ -1, 1, Conv, [ 256, 1, 1 ] ], 43 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 44 | [ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3 45 | [ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small) 46 | 47 | [ -1, 1, Conv, [ 256, 3, 2 ] ], 48 | [ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4 49 | [ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium) 50 | 51 | [ -1, 1, Conv, [ 512, 3, 2 ] ], 52 | [ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5 53 | [ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large) 54 | 55 | [ -1, 1, Conv, [ 768, 3, 2 ] ], 56 | [ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6 57 | [ -1, 3, C3, [ 1024, False ] ], # 32 (P6/64-xlarge) 58 | 59 | [ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6) 60 | ] 61 | -------------------------------------------------------------------------------- /yolov5/models/hub/yolov5s-transformer.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, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, C3TR, [1024, False]], # 9 <-------- C3TR() Transformer module 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, C3, [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, C3, [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, C3, [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, C3, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /yolov5/models/hub/yolov5s6.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 | - [ 19,27, 44,40, 38,94 ] # P3/8 9 | - [ 96,68, 86,152, 180,137 ] # P4/16 10 | - [ 140,301, 303,264, 238,542 ] # P5/32 11 | - [ 436,615, 739,380, 925,792 ] # P6/64 12 | 13 | # YOLOv5 backbone 14 | backbone: 15 | # [from, number, module, args] 16 | [ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2 17 | [ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4 18 | [ -1, 3, C3, [ 128 ] ], 19 | [ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8 20 | [ -1, 9, C3, [ 256 ] ], 21 | [ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16 22 | [ -1, 9, C3, [ 512 ] ], 23 | [ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32 24 | [ -1, 3, C3, [ 768 ] ], 25 | [ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64 26 | [ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ], 27 | [ -1, 3, C3, [ 1024, False ] ], # 11 28 | ] 29 | 30 | # YOLOv5 head 31 | head: 32 | [ [ -1, 1, Conv, [ 768, 1, 1 ] ], 33 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 34 | [ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5 35 | [ -1, 3, C3, [ 768, False ] ], # 15 36 | 37 | [ -1, 1, Conv, [ 512, 1, 1 ] ], 38 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 39 | [ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4 40 | [ -1, 3, C3, [ 512, False ] ], # 19 41 | 42 | [ -1, 1, Conv, [ 256, 1, 1 ] ], 43 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 44 | [ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3 45 | [ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small) 46 | 47 | [ -1, 1, Conv, [ 256, 3, 2 ] ], 48 | [ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4 49 | [ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium) 50 | 51 | [ -1, 1, Conv, [ 512, 3, 2 ] ], 52 | [ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5 53 | [ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large) 54 | 55 | [ -1, 1, Conv, [ 768, 3, 2 ] ], 56 | [ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6 57 | [ -1, 3, C3, [ 1024, False ] ], # 32 (P6/64-xlarge) 58 | 59 | [ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6) 60 | ] 61 | -------------------------------------------------------------------------------- /yolov5/models/hub/yolov5x6.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 | - [ 19,27, 44,40, 38,94 ] # P3/8 9 | - [ 96,68, 86,152, 180,137 ] # P4/16 10 | - [ 140,301, 303,264, 238,542 ] # P5/32 11 | - [ 436,615, 739,380, 925,792 ] # P6/64 12 | 13 | # YOLOv5 backbone 14 | backbone: 15 | # [from, number, module, args] 16 | [ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2 17 | [ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4 18 | [ -1, 3, C3, [ 128 ] ], 19 | [ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8 20 | [ -1, 9, C3, [ 256 ] ], 21 | [ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16 22 | [ -1, 9, C3, [ 512 ] ], 23 | [ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32 24 | [ -1, 3, C3, [ 768 ] ], 25 | [ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64 26 | [ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ], 27 | [ -1, 3, C3, [ 1024, False ] ], # 11 28 | ] 29 | 30 | # YOLOv5 head 31 | head: 32 | [ [ -1, 1, Conv, [ 768, 1, 1 ] ], 33 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 34 | [ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5 35 | [ -1, 3, C3, [ 768, False ] ], # 15 36 | 37 | [ -1, 1, Conv, [ 512, 1, 1 ] ], 38 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 39 | [ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4 40 | [ -1, 3, C3, [ 512, False ] ], # 19 41 | 42 | [ -1, 1, Conv, [ 256, 1, 1 ] ], 43 | [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ], 44 | [ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3 45 | [ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small) 46 | 47 | [ -1, 1, Conv, [ 256, 3, 2 ] ], 48 | [ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4 49 | [ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium) 50 | 51 | [ -1, 1, Conv, [ 512, 3, 2 ] ], 52 | [ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5 53 | [ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large) 54 | 55 | [ -1, 1, Conv, [ 768, 3, 2 ] ], 56 | [ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6 57 | [ -1, 3, C3, [ 1024, False ] ], # 32 (P6/64-xlarge) 58 | 59 | [ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6) 60 | ] 61 | -------------------------------------------------------------------------------- /yolov5/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, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, C3, [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, C3, [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, C3, [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, C3, [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, C3, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /yolov5/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, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, C3, [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, C3, [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, C3, [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, C3, [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, C3, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /yolov5/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, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, C3, [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, C3, [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, C3, [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, C3, [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, C3, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /yolov5/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, C3, [128]], 18 | [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 19 | [-1, 9, C3, [256]], 20 | [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 21 | [-1, 9, C3, [512]], 22 | [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 23 | [-1, 1, SPP, [1024, [5, 9, 13]]], 24 | [-1, 3, C3, [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, C3, [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, C3, [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, C3, [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, C3, [1024, False]], # 23 (P5/32-large) 46 | 47 | [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) 48 | ] 49 | -------------------------------------------------------------------------------- /yolov5/requirements.txt: -------------------------------------------------------------------------------- 1 | # pip install -r requirements.txt 2 | 3 | # base ---------------------------------------- 4 | matplotlib>=3.2.2 5 | numpy>=1.18.5 6 | opencv-python>=4.1.2 7 | Pillow 8 | PyYAML>=5.3.1 9 | scipy>=1.4.1 10 | torch>=1.7.0 11 | torchvision>=0.8.1 12 | tqdm>=4.41.0 13 | 14 | # logging ------------------------------------- 15 | tensorboard>=2.4.1 16 | # wandb 17 | 18 | # plotting ------------------------------------ 19 | seaborn>=0.11.0 20 | pandas 21 | 22 | # export -------------------------------------- 23 | # coremltools>=4.1 24 | # onnx>=1.8.1 25 | # scikit-learn==0.19.2 # for coreml quantization 26 | 27 | # extras -------------------------------------- 28 | thop # FLOPS computation 29 | pycocotools>=2.0 # COCO mAP 30 | -------------------------------------------------------------------------------- /yolov5/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/yolov5/utils/__init__.py -------------------------------------------------------------------------------- /yolov5/utils/activations.py: -------------------------------------------------------------------------------- 1 | # Activation functions 2 | 3 | import torch 4 | import torch.nn as nn 5 | import torch.nn.functional as F 6 | 7 | 8 | # SiLU https://arxiv.org/pdf/1606.08415.pdf ---------------------------------------------------------------------------- 9 | class SiLU(nn.Module): # export-friendly version of nn.SiLU() 10 | @staticmethod 11 | def forward(x): 12 | return x * torch.sigmoid(x) 13 | 14 | 15 | class Hardswish(nn.Module): # export-friendly version of nn.Hardswish() 16 | @staticmethod 17 | def forward(x): 18 | # return x * F.hardsigmoid(x) # for torchscript and CoreML 19 | return x * F.hardtanh(x + 3, 0., 6.) / 6. # for torchscript, CoreML and ONNX 20 | 21 | 22 | # Mish https://github.com/digantamisra98/Mish -------------------------------------------------------------------------- 23 | class Mish(nn.Module): 24 | @staticmethod 25 | def forward(x): 26 | return x * F.softplus(x).tanh() 27 | 28 | 29 | class MemoryEfficientMish(nn.Module): 30 | class F(torch.autograd.Function): 31 | @staticmethod 32 | def forward(ctx, x): 33 | ctx.save_for_backward(x) 34 | return x.mul(torch.tanh(F.softplus(x))) # x * tanh(ln(1 + exp(x))) 35 | 36 | @staticmethod 37 | def backward(ctx, grad_output): 38 | x = ctx.saved_tensors[0] 39 | sx = torch.sigmoid(x) 40 | fx = F.softplus(x).tanh() 41 | return grad_output * (fx + x * sx * (1 - fx * fx)) 42 | 43 | def forward(self, x): 44 | return self.F.apply(x) 45 | 46 | 47 | # FReLU https://arxiv.org/abs/2007.11824 ------------------------------------------------------------------------------- 48 | class FReLU(nn.Module): 49 | def __init__(self, c1, k=3): # ch_in, kernel 50 | super().__init__() 51 | self.conv = nn.Conv2d(c1, c1, k, 1, 1, groups=c1, bias=False) 52 | self.bn = nn.BatchNorm2d(c1) 53 | 54 | def forward(self, x): 55 | return torch.max(x, self.bn(self.conv(x))) 56 | 57 | 58 | # ACON https://arxiv.org/pdf/2009.04759.pdf ---------------------------------------------------------------------------- 59 | class AconC(nn.Module): 60 | r""" ACON activation (activate or not). 61 | AconC: (p1*x-p2*x) * sigmoid(beta*(p1*x-p2*x)) + p2*x, beta is a learnable parameter 62 | according to "Activate or Not: Learning Customized Activation" . 63 | """ 64 | 65 | def __init__(self, c1): 66 | super().__init__() 67 | self.p1 = nn.Parameter(torch.randn(1, c1, 1, 1)) 68 | self.p2 = nn.Parameter(torch.randn(1, c1, 1, 1)) 69 | self.beta = nn.Parameter(torch.ones(1, c1, 1, 1)) 70 | 71 | def forward(self, x): 72 | dpx = (self.p1 - self.p2) * x 73 | return dpx * torch.sigmoid(self.beta * dpx) + self.p2 * x 74 | 75 | 76 | class MetaAconC(nn.Module): 77 | r""" ACON activation (activate or not). 78 | MetaAconC: (p1*x-p2*x) * sigmoid(beta*(p1*x-p2*x)) + p2*x, beta is generated by a small network 79 | according to "Activate or Not: Learning Customized Activation" . 80 | """ 81 | 82 | def __init__(self, c1, k=1, s=1, r=16): # ch_in, kernel, stride, r 83 | super().__init__() 84 | c2 = max(r, c1 // r) 85 | self.p1 = nn.Parameter(torch.randn(1, c1, 1, 1)) 86 | self.p2 = nn.Parameter(torch.randn(1, c1, 1, 1)) 87 | self.fc1 = nn.Conv2d(c1, c2, k, s, bias=True) 88 | self.fc2 = nn.Conv2d(c2, c1, k, s, bias=True) 89 | # self.bn1 = nn.BatchNorm2d(c2) 90 | # self.bn2 = nn.BatchNorm2d(c1) 91 | 92 | def forward(self, x): 93 | y = x.mean(dim=2, keepdims=True).mean(dim=3, keepdims=True) 94 | # batch-size 1 bug/instabilities https://github.com/ultralytics/yolov5/issues/2891 95 | # beta = torch.sigmoid(self.bn2(self.fc2(self.bn1(self.fc1(y))))) # bug/unstable 96 | beta = torch.sigmoid(self.fc2(self.fc1(y))) # bug patch BN layers removed 97 | dpx = (self.p1 - self.p2) * x 98 | return dpx * torch.sigmoid(beta * dpx) + self.p2 * x 99 | -------------------------------------------------------------------------------- /yolov5/utils/autoanchor.py: -------------------------------------------------------------------------------- 1 | # Auto-anchor utils 2 | 3 | import numpy as np 4 | import torch 5 | import yaml 6 | from tqdm import tqdm 7 | 8 | from utils.general import colorstr 9 | 10 | 11 | def check_anchor_order(m): 12 | # Check anchor order against stride order for YOLOv5 Detect() module m, and correct if necessary 13 | a = m.anchor_grid.prod(-1).view(-1) # anchor area 14 | da = a[-1] - a[0] # delta a 15 | ds = m.stride[-1] - m.stride[0] # delta s 16 | if da.sign() != ds.sign(): # same order 17 | print('Reversing anchor order') 18 | m.anchors[:] = m.anchors.flip(0) 19 | m.anchor_grid[:] = m.anchor_grid.flip(0) 20 | 21 | 22 | def check_anchors(dataset, model, thr=4.0, imgsz=640): 23 | # Check anchor fit to data, recompute if necessary 24 | prefix = colorstr('autoanchor: ') 25 | print(f'\n{prefix}Analyzing anchors... ', end='') 26 | m = model.module.model[-1] if hasattr(model, 'module') else model.model[-1] # Detect() 27 | shapes = imgsz * dataset.shapes / dataset.shapes.max(1, keepdims=True) 28 | scale = np.random.uniform(0.9, 1.1, size=(shapes.shape[0], 1)) # augment scale 29 | wh = torch.tensor(np.concatenate([l[:, 3:5] * s for s, l in zip(shapes * scale, dataset.labels)])).float() # wh 30 | 31 | def metric(k): # compute metric 32 | r = wh[:, None] / k[None] 33 | x = torch.min(r, 1. / r).min(2)[0] # ratio metric 34 | best = x.max(1)[0] # best_x 35 | aat = (x > 1. / thr).float().sum(1).mean() # anchors above threshold 36 | bpr = (best > 1. / thr).float().mean() # best possible recall 37 | return bpr, aat 38 | 39 | anchors = m.anchor_grid.clone().cpu().view(-1, 2) # current anchors 40 | bpr, aat = metric(anchors) 41 | print(f'anchors/target = {aat:.2f}, Best Possible Recall (BPR) = {bpr:.4f}', end='') 42 | if bpr < 0.98: # threshold to recompute 43 | print('. Attempting to improve anchors, please wait...') 44 | na = m.anchor_grid.numel() // 2 # number of anchors 45 | try: 46 | anchors = kmean_anchors(dataset, n=na, img_size=imgsz, thr=thr, gen=1000, verbose=False) 47 | except Exception as e: 48 | print(f'{prefix}ERROR: {e}') 49 | new_bpr = metric(anchors)[0] 50 | if new_bpr > bpr: # replace anchors 51 | anchors = torch.tensor(anchors, device=m.anchors.device).type_as(m.anchors) 52 | m.anchor_grid[:] = anchors.clone().view_as(m.anchor_grid) # for inference 53 | m.anchors[:] = anchors.clone().view_as(m.anchors) / m.stride.to(m.anchors.device).view(-1, 1, 1) # loss 54 | check_anchor_order(m) 55 | print(f'{prefix}New anchors saved to model. Update model *.yaml to use these anchors in the future.') 56 | else: 57 | print(f'{prefix}Original anchors better than new anchors. Proceeding with original anchors.') 58 | print('') # newline 59 | 60 | 61 | def kmean_anchors(path='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen=1000, verbose=True): 62 | """ Creates kmeans-evolved anchors from training dataset 63 | 64 | Arguments: 65 | path: path to dataset *.yaml, or a loaded dataset 66 | n: number of anchors 67 | img_size: image size used for training 68 | thr: anchor-label wh ratio threshold hyperparameter hyp['anchor_t'] used for training, default=4.0 69 | gen: generations to evolve anchors using genetic algorithm 70 | verbose: print all results 71 | 72 | Return: 73 | k: kmeans evolved anchors 74 | 75 | Usage: 76 | from utils.autoanchor import *; _ = kmean_anchors() 77 | """ 78 | from scipy.cluster.vq import kmeans 79 | 80 | thr = 1. / thr 81 | prefix = colorstr('autoanchor: ') 82 | 83 | def metric(k, wh): # compute metrics 84 | r = wh[:, None] / k[None] 85 | x = torch.min(r, 1. / r).min(2)[0] # ratio metric 86 | # x = wh_iou(wh, torch.tensor(k)) # iou metric 87 | return x, x.max(1)[0] # x, best_x 88 | 89 | def anchor_fitness(k): # mutation fitness 90 | _, best = metric(torch.tensor(k, dtype=torch.float32), wh) 91 | return (best * (best > thr).float()).mean() # fitness 92 | 93 | def print_results(k): 94 | k = k[np.argsort(k.prod(1))] # sort small to large 95 | x, best = metric(k, wh0) 96 | bpr, aat = (best > thr).float().mean(), (x > thr).float().mean() * n # best possible recall, anch > thr 97 | print(f'{prefix}thr={thr:.2f}: {bpr:.4f} best possible recall, {aat:.2f} anchors past thr') 98 | print(f'{prefix}n={n}, img_size={img_size}, metric_all={x.mean():.3f}/{best.mean():.3f}-mean/best, ' 99 | f'past_thr={x[x > thr].mean():.3f}-mean: ', end='') 100 | for i, x in enumerate(k): 101 | print('%i,%i' % (round(x[0]), round(x[1])), end=', ' if i < len(k) - 1 else '\n') # use in *.cfg 102 | return k 103 | 104 | if isinstance(path, str): # *.yaml file 105 | with open(path) as f: 106 | data_dict = yaml.safe_load(f) # model dict 107 | from utils.datasets import LoadImagesAndLabels 108 | dataset = LoadImagesAndLabels(data_dict['train'], augment=True, rect=True) 109 | else: 110 | dataset = path # dataset 111 | 112 | # Get label wh 113 | shapes = img_size * dataset.shapes / dataset.shapes.max(1, keepdims=True) 114 | wh0 = np.concatenate([l[:, 3:5] * s for s, l in zip(shapes, dataset.labels)]) # wh 115 | 116 | # Filter 117 | i = (wh0 < 3.0).any(1).sum() 118 | if i: 119 | print(f'{prefix}WARNING: Extremely small objects found. {i} of {len(wh0)} labels are < 3 pixels in size.') 120 | wh = wh0[(wh0 >= 2.0).any(1)] # filter > 2 pixels 121 | # wh = wh * (np.random.rand(wh.shape[0], 1) * 0.9 + 0.1) # multiply by random scale 0-1 122 | 123 | # Kmeans calculation 124 | print(f'{prefix}Running kmeans for {n} anchors on {len(wh)} points...') 125 | s = wh.std(0) # sigmas for whitening 126 | k, dist = kmeans(wh / s, n, iter=30) # points, mean distance 127 | assert len(k) == n, print(f'{prefix}ERROR: scipy.cluster.vq.kmeans requested {n} points but returned only {len(k)}') 128 | k *= s 129 | wh = torch.tensor(wh, dtype=torch.float32) # filtered 130 | wh0 = torch.tensor(wh0, dtype=torch.float32) # unfiltered 131 | k = print_results(k) 132 | 133 | # Plot 134 | # k, d = [None] * 20, [None] * 20 135 | # for i in tqdm(range(1, 21)): 136 | # k[i-1], d[i-1] = kmeans(wh / s, i) # points, mean distance 137 | # fig, ax = plt.subplots(1, 2, figsize=(14, 7), tight_layout=True) 138 | # ax = ax.ravel() 139 | # ax[0].plot(np.arange(1, 21), np.array(d) ** 2, marker='.') 140 | # fig, ax = plt.subplots(1, 2, figsize=(14, 7)) # plot wh 141 | # ax[0].hist(wh[wh[:, 0]<100, 0],400) 142 | # ax[1].hist(wh[wh[:, 1]<100, 1],400) 143 | # fig.savefig('wh.png', dpi=200) 144 | 145 | # Evolve 146 | npr = np.random 147 | f, sh, mp, s = anchor_fitness(k), k.shape, 0.9, 0.1 # fitness, generations, mutation prob, sigma 148 | pbar = tqdm(range(gen), desc=f'{prefix}Evolving anchors with Genetic Algorithm:') # progress bar 149 | for _ in pbar: 150 | v = np.ones(sh) 151 | while (v == 1).all(): # mutate until a change occurs (prevent duplicates) 152 | v = ((npr.random(sh) < mp) * npr.random() * npr.randn(*sh) * s + 1).clip(0.3, 3.0) 153 | kg = (k.copy() * v).clip(min=2.0) 154 | fg = anchor_fitness(kg) 155 | if fg > f: 156 | f, k = fg, kg.copy() 157 | pbar.desc = f'{prefix}Evolving anchors with Genetic Algorithm: fitness = {f:.4f}' 158 | if verbose: 159 | print_results(k) 160 | 161 | return print_results(k) 162 | -------------------------------------------------------------------------------- /yolov5/utils/aws/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/yolov5/utils/aws/__init__.py -------------------------------------------------------------------------------- /yolov5/utils/aws/mime.sh: -------------------------------------------------------------------------------- 1 | # AWS EC2 instance startup 'MIME' script https://aws.amazon.com/premiumsupport/knowledge-center/execute-user-data-ec2/ 2 | # This script will run on every instance restart, not only on first start 3 | # --- DO NOT COPY ABOVE COMMENTS WHEN PASTING INTO USERDATA --- 4 | 5 | Content-Type: multipart/mixed; boundary="//" 6 | MIME-Version: 1.0 7 | 8 | --// 9 | Content-Type: text/cloud-config; charset="us-ascii" 10 | MIME-Version: 1.0 11 | Content-Transfer-Encoding: 7bit 12 | Content-Disposition: attachment; filename="cloud-config.txt" 13 | 14 | #cloud-config 15 | cloud_final_modules: 16 | - [scripts-user, always] 17 | 18 | --// 19 | Content-Type: text/x-shellscript; charset="us-ascii" 20 | MIME-Version: 1.0 21 | Content-Transfer-Encoding: 7bit 22 | Content-Disposition: attachment; filename="userdata.txt" 23 | 24 | #!/bin/bash 25 | # --- paste contents of userdata.sh here --- 26 | --// 27 | -------------------------------------------------------------------------------- /yolov5/utils/aws/resume.py: -------------------------------------------------------------------------------- 1 | # Resume all interrupted trainings in yolov5/ dir including DDP trainings 2 | # Usage: $ python utils/aws/resume.py 3 | 4 | import os 5 | import sys 6 | from pathlib import Path 7 | 8 | import torch 9 | import yaml 10 | 11 | sys.path.append('./') # to run '$ python *.py' files in subdirectories 12 | 13 | port = 0 # --master_port 14 | path = Path('').resolve() 15 | for last in path.rglob('*/**/last.pt'): 16 | ckpt = torch.load(last) 17 | if ckpt['optimizer'] is None: 18 | continue 19 | 20 | # Load opt.yaml 21 | with open(last.parent.parent / 'opt.yaml') as f: 22 | opt = yaml.safe_load(f) 23 | 24 | # Get device count 25 | d = opt['device'].split(',') # devices 26 | nd = len(d) # number of devices 27 | ddp = nd > 1 or (nd == 0 and torch.cuda.device_count() > 1) # distributed data parallel 28 | 29 | if ddp: # multi-GPU 30 | port += 1 31 | cmd = f'python -m torch.distributed.launch --nproc_per_node {nd} --master_port {port} train.py --resume {last}' 32 | else: # single-GPU 33 | cmd = f'python train.py --resume {last}' 34 | 35 | cmd += ' > /dev/null 2>&1 &' # redirect output to dev/null and run in daemon thread 36 | print(cmd) 37 | os.system(cmd) 38 | -------------------------------------------------------------------------------- /yolov5/utils/aws/userdata.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # AWS EC2 instance startup script https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html 3 | # This script will run only once on first instance start (for a re-start script see mime.sh) 4 | # /home/ubuntu (ubuntu) or /home/ec2-user (amazon-linux) is working dir 5 | # Use >300 GB SSD 6 | 7 | cd home/ubuntu 8 | if [ ! -d yolov5 ]; then 9 | echo "Running first-time script." # install dependencies, download COCO, pull Docker 10 | git clone https://github.com/ultralytics/yolov5 && sudo chmod -R 777 yolov5 11 | cd yolov5 12 | bash data/scripts/get_coco.sh && echo "Data done." & 13 | sudo docker pull ultralytics/yolov5:latest && echo "Docker done." & 14 | python -m pip install --upgrade pip && pip install -r requirements.txt && python detect.py && echo "Requirements done." & 15 | wait && echo "All tasks done." # finish background tasks 16 | else 17 | echo "Running re-start script." # resume interrupted runs 18 | i=0 19 | list=$(sudo docker ps -qa) # container list i.e. $'one\ntwo\nthree\nfour' 20 | while IFS= read -r id; do 21 | ((i++)) 22 | echo "restarting container $i: $id" 23 | sudo docker start $id 24 | # sudo docker exec -it $id python train.py --resume # single-GPU 25 | sudo docker exec -d $id python utils/aws/resume.py # multi-scenario 26 | done <<<"$list" 27 | fi 28 | -------------------------------------------------------------------------------- /yolov5/utils/flask_rest_api/README.md: -------------------------------------------------------------------------------- 1 | # Flask REST API 2 | [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) [API](https://en.wikipedia.org/wiki/API)s are commonly used to expose Machine Learning (ML) models to other services. This folder contains an example REST API created using Flask to expose the `yolov5s` model from [PyTorch Hub](https://pytorch.org/hub/ultralytics_yolov5/). 3 | 4 | ## Requirements 5 | 6 | [Flask](https://palletsprojects.com/p/flask/) is required. Install with: 7 | ```shell 8 | $ pip install Flask 9 | ``` 10 | 11 | ## Run 12 | 13 | After Flask installation run: 14 | 15 | ```shell 16 | $ python3 restapi.py --port 5000 17 | ``` 18 | 19 | Then use [curl](https://curl.se/) to perform a request: 20 | 21 | ```shell 22 | $ curl -X POST -F image=@zidane.jpg 'http://localhost:5000/v1/object-detection/yolov5s'` 23 | ``` 24 | 25 | The model inference results are returned: 26 | 27 | ```shell 28 | [{'class': 0, 29 | 'confidence': 0.8197850585, 30 | 'name': 'person', 31 | 'xmax': 1159.1403808594, 32 | 'xmin': 750.912902832, 33 | 'ymax': 711.2583007812, 34 | 'ymin': 44.0350036621}, 35 | {'class': 0, 36 | 'confidence': 0.5667674541, 37 | 'name': 'person', 38 | 'xmax': 1065.5523681641, 39 | 'xmin': 116.0448303223, 40 | 'ymax': 713.8904418945, 41 | 'ymin': 198.4603881836}, 42 | {'class': 27, 43 | 'confidence': 0.5661227107, 44 | 'name': 'tie', 45 | 'xmax': 516.7975463867, 46 | 'xmin': 416.6880187988, 47 | 'ymax': 717.0524902344, 48 | 'ymin': 429.2020568848}] 49 | ``` 50 | 51 | An example python script to perform inference using [requests](https://docs.python-requests.org/en/master/) is given in `example_request.py` 52 | -------------------------------------------------------------------------------- /yolov5/utils/flask_rest_api/example_request.py: -------------------------------------------------------------------------------- 1 | """Perform test request""" 2 | import pprint 3 | 4 | import requests 5 | 6 | DETECTION_URL = "http://localhost:5000/v1/object-detection/yolov5s" 7 | TEST_IMAGE = "zidane.jpg" 8 | 9 | image_data = open(TEST_IMAGE, "rb").read() 10 | 11 | response = requests.post(DETECTION_URL, files={"image": image_data}).json() 12 | 13 | pprint.pprint(response) 14 | -------------------------------------------------------------------------------- /yolov5/utils/flask_rest_api/restapi.py: -------------------------------------------------------------------------------- 1 | """ 2 | Run a rest API exposing the yolov5s object detection model 3 | """ 4 | import argparse 5 | import io 6 | 7 | import torch 8 | from PIL import Image 9 | from flask import Flask, request 10 | 11 | app = Flask(__name__) 12 | 13 | DETECTION_URL = "/v1/object-detection/yolov5s" 14 | 15 | 16 | @app.route(DETECTION_URL, methods=["POST"]) 17 | def predict(): 18 | if not request.method == "POST": 19 | return 20 | 21 | if request.files.get("image"): 22 | image_file = request.files["image"] 23 | image_bytes = image_file.read() 24 | 25 | img = Image.open(io.BytesIO(image_bytes)) 26 | 27 | results = model(img, size=640) # reduce size=320 for faster inference 28 | return results.pandas().xyxy[0].to_json(orient="records") 29 | 30 | 31 | if __name__ == "__main__": 32 | parser = argparse.ArgumentParser(description="Flask API exposing YOLOv5 model") 33 | parser.add_argument("--port", default=5000, type=int, help="port number") 34 | args = parser.parse_args() 35 | 36 | model = torch.hub.load("ultralytics/yolov5", "yolov5s", force_reload=True) # force_reload to recache 37 | app.run(host="0.0.0.0", port=args.port) # debug=True causes Restarting with stat 38 | -------------------------------------------------------------------------------- /yolov5/utils/google_app_engine/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcr.io/google-appengine/python 2 | 3 | # Create a virtualenv for dependencies. This isolates these packages from 4 | # system-level packages. 5 | # Use -p python3 or -p python3.7 to select python version. Default is version 2. 6 | RUN virtualenv /env -p python3 7 | 8 | # Setting these environment variables are the same as running 9 | # source /env/bin/activate. 10 | ENV VIRTUAL_ENV /env 11 | ENV PATH /env/bin:$PATH 12 | 13 | RUN apt-get update && apt-get install -y python-opencv 14 | 15 | # Copy the application's requirements.txt and run pip to install all 16 | # dependencies into the virtualenv. 17 | ADD requirements.txt /app/requirements.txt 18 | RUN pip install -r /app/requirements.txt 19 | 20 | # Add the application source code. 21 | ADD . /app 22 | 23 | # Run a WSGI server to serve the application. gunicorn must be declared as 24 | # a dependency in requirements.txt. 25 | CMD gunicorn -b :$PORT main:app 26 | -------------------------------------------------------------------------------- /yolov5/utils/google_app_engine/additional_requirements.txt: -------------------------------------------------------------------------------- 1 | # add these requirements in your app on top of the existing ones 2 | pip==18.1 3 | Flask==1.0.2 4 | gunicorn==19.9.0 5 | -------------------------------------------------------------------------------- /yolov5/utils/google_app_engine/app.yaml: -------------------------------------------------------------------------------- 1 | runtime: custom 2 | env: flex 3 | 4 | service: yolov5app 5 | 6 | liveness_check: 7 | initial_delay_sec: 600 8 | 9 | manual_scaling: 10 | instances: 1 11 | resources: 12 | cpu: 1 13 | memory_gb: 4 14 | disk_size_gb: 20 -------------------------------------------------------------------------------- /yolov5/utils/google_utils.py: -------------------------------------------------------------------------------- 1 | # Google utils: https://cloud.google.com/storage/docs/reference/libraries 2 | 3 | import os 4 | import platform 5 | import subprocess 6 | import time 7 | from pathlib import Path 8 | 9 | import requests 10 | import torch 11 | 12 | 13 | def gsutil_getsize(url=''): 14 | # gs://bucket/file size https://cloud.google.com/storage/docs/gsutil/commands/du 15 | s = subprocess.check_output(f'gsutil du {url}', shell=True).decode('utf-8') 16 | return eval(s.split(' ')[0]) if len(s) else 0 # bytes 17 | 18 | 19 | def attempt_download(file, repo='ultralytics/yolov5'): 20 | # Attempt file download if does not exist 21 | file = Path(str(file).strip().replace("'", '')) 22 | 23 | if not file.exists(): 24 | file.parent.mkdir(parents=True, exist_ok=True) # make parent dir (if required) 25 | try: 26 | response = requests.get(f'https://api.github.com/repos/{repo}/releases/latest').json() # github api 27 | assets = [x['name'] for x in response['assets']] # release assets, i.e. ['yolov5s.pt', 'yolov5m.pt', ...] 28 | tag = response['tag_name'] # i.e. 'v1.0' 29 | except: # fallback plan 30 | assets = ['yolov5s.pt', 'yolov5m.pt', 'yolov5l.pt', 'yolov5x.pt', 31 | 'yolov5s6.pt', 'yolov5m6.pt', 'yolov5l6.pt', 'yolov5x6.pt'] 32 | try: 33 | tag = subprocess.check_output('git tag', shell=True, stderr=subprocess.STDOUT).decode().split()[-1] 34 | except: 35 | tag = 'v5.0' # current release 36 | 37 | name = file.name 38 | if name in assets: 39 | msg = f'{file} missing, try downloading from https://github.com/{repo}/releases/' 40 | redundant = False # second download option 41 | try: # GitHub 42 | url = f'https://github.com/{repo}/releases/download/{tag}/{name}' 43 | print(f'Downloading {url} to {file}...') 44 | torch.hub.download_url_to_file(url, file) 45 | assert file.exists() and file.stat().st_size > 1E6 # check 46 | except Exception as e: # GCP 47 | print(f'Download error: {e}') 48 | assert redundant, 'No secondary mirror' 49 | url = f'https://storage.googleapis.com/{repo}/ckpt/{name}' 50 | print(f'Downloading {url} to {file}...') 51 | os.system(f"curl -L '{url}' -o '{file}' --retry 3 -C -") # curl download, retry and resume on fail 52 | finally: 53 | if not file.exists() or file.stat().st_size < 1E6: # check 54 | file.unlink(missing_ok=True) # remove partial downloads 55 | print(f'ERROR: Download failure: {msg}') 56 | print('') 57 | return 58 | 59 | 60 | def gdrive_download(id='16TiPfZj7htmTyhntwcZyEEAejOUxuT6m', file='tmp.zip'): 61 | # Downloads a file from Google Drive. from yolov5.utils.google_utils import *; gdrive_download() 62 | t = time.time() 63 | file = Path(file) 64 | cookie = Path('cookie') # gdrive cookie 65 | print(f'Downloading https://drive.google.com/uc?export=download&id={id} as {file}... ', end='') 66 | file.unlink(missing_ok=True) # remove existing file 67 | cookie.unlink(missing_ok=True) # remove existing cookie 68 | 69 | # Attempt file download 70 | out = "NUL" if platform.system() == "Windows" else "/dev/null" 71 | os.system(f'curl -c ./cookie -s -L "drive.google.com/uc?export=download&id={id}" > {out}') 72 | if os.path.exists('cookie'): # large file 73 | s = f'curl -Lb ./cookie "drive.google.com/uc?export=download&confirm={get_token()}&id={id}" -o {file}' 74 | else: # small file 75 | s = f'curl -s -L -o {file} "drive.google.com/uc?export=download&id={id}"' 76 | r = os.system(s) # execute, capture return 77 | cookie.unlink(missing_ok=True) # remove existing cookie 78 | 79 | # Error check 80 | if r != 0: 81 | file.unlink(missing_ok=True) # remove partial 82 | print('Download error ') # raise Exception('Download error') 83 | return r 84 | 85 | # Unzip if archive 86 | if file.suffix == '.zip': 87 | print('unzipping... ', end='') 88 | os.system(f'unzip -q {file}') # unzip 89 | file.unlink() # remove zip to free space 90 | 91 | print(f'Done ({time.time() - t:.1f}s)') 92 | return r 93 | 94 | 95 | def get_token(cookie="./cookie"): 96 | with open(cookie) as f: 97 | for line in f: 98 | if "download" in line: 99 | return line.split()[-1] 100 | return "" 101 | 102 | # def upload_blob(bucket_name, source_file_name, destination_blob_name): 103 | # # Uploads a file to a bucket 104 | # # https://cloud.google.com/storage/docs/uploading-objects#storage-upload-object-python 105 | # 106 | # storage_client = storage.Client() 107 | # bucket = storage_client.get_bucket(bucket_name) 108 | # blob = bucket.blob(destination_blob_name) 109 | # 110 | # blob.upload_from_filename(source_file_name) 111 | # 112 | # print('File {} uploaded to {}.'.format( 113 | # source_file_name, 114 | # destination_blob_name)) 115 | # 116 | # 117 | # def download_blob(bucket_name, source_blob_name, destination_file_name): 118 | # # Uploads a blob from a bucket 119 | # storage_client = storage.Client() 120 | # bucket = storage_client.get_bucket(bucket_name) 121 | # blob = bucket.blob(source_blob_name) 122 | # 123 | # blob.download_to_filename(destination_file_name) 124 | # 125 | # print('Blob {} downloaded to {}.'.format( 126 | # source_blob_name, 127 | # destination_file_name)) 128 | -------------------------------------------------------------------------------- /yolov5/utils/metrics.py: -------------------------------------------------------------------------------- 1 | # Model validation metrics 2 | 3 | from pathlib import Path 4 | 5 | import matplotlib.pyplot as plt 6 | import numpy as np 7 | import torch 8 | 9 | from . import general 10 | 11 | 12 | def fitness(x): 13 | # Model fitness as a weighted combination of metrics 14 | w = [0.0, 0.0, 0.1, 0.9] # weights for [P, R, mAP@0.5, mAP@0.5:0.95] 15 | return (x[:, :4] * w).sum(1) 16 | 17 | 18 | def ap_per_class(tp, conf, pred_cls, target_cls, plot=False, save_dir='.', names=()): 19 | """ Compute the average precision, given the recall and precision curves. 20 | Source: https://github.com/rafaelpadilla/Object-Detection-Metrics. 21 | # Arguments 22 | tp: True positives (nparray, nx1 or nx10). 23 | conf: Objectness value from 0-1 (nparray). 24 | pred_cls: Predicted object classes (nparray). 25 | target_cls: True object classes (nparray). 26 | plot: Plot precision-recall curve at mAP@0.5 27 | save_dir: Plot save directory 28 | # Returns 29 | The average precision as computed in py-faster-rcnn. 30 | """ 31 | 32 | # Sort by objectness 33 | i = np.argsort(-conf) 34 | tp, conf, pred_cls = tp[i], conf[i], pred_cls[i] 35 | 36 | # Find unique classes 37 | unique_classes = np.unique(target_cls) 38 | nc = unique_classes.shape[0] # number of classes, number of detections 39 | 40 | # Create Precision-Recall curve and compute AP for each class 41 | px, py = np.linspace(0, 1, 1000), [] # for plotting 42 | ap, p, r = np.zeros((nc, tp.shape[1])), np.zeros((nc, 1000)), np.zeros((nc, 1000)) 43 | for ci, c in enumerate(unique_classes): 44 | i = pred_cls == c 45 | n_l = (target_cls == c).sum() # number of labels 46 | n_p = i.sum() # number of predictions 47 | 48 | if n_p == 0 or n_l == 0: 49 | continue 50 | else: 51 | # Accumulate FPs and TPs 52 | fpc = (1 - tp[i]).cumsum(0) 53 | tpc = tp[i].cumsum(0) 54 | 55 | # Recall 56 | recall = tpc / (n_l + 1e-16) # recall curve 57 | r[ci] = np.interp(-px, -conf[i], recall[:, 0], left=0) # negative x, xp because xp decreases 58 | 59 | # Precision 60 | precision = tpc / (tpc + fpc) # precision curve 61 | p[ci] = np.interp(-px, -conf[i], precision[:, 0], left=1) # p at pr_score 62 | 63 | # AP from recall-precision curve 64 | for j in range(tp.shape[1]): 65 | ap[ci, j], mpre, mrec = compute_ap(recall[:, j], precision[:, j]) 66 | if plot and j == 0: 67 | py.append(np.interp(px, mrec, mpre)) # precision at mAP@0.5 68 | 69 | # Compute F1 (harmonic mean of precision and recall) 70 | f1 = 2 * p * r / (p + r + 1e-16) 71 | if plot: 72 | plot_pr_curve(px, py, ap, Path(save_dir) / 'PR_curve.png', names) 73 | plot_mc_curve(px, f1, Path(save_dir) / 'F1_curve.png', names, ylabel='F1') 74 | plot_mc_curve(px, p, Path(save_dir) / 'P_curve.png', names, ylabel='Precision') 75 | plot_mc_curve(px, r, Path(save_dir) / 'R_curve.png', names, ylabel='Recall') 76 | 77 | i = f1.mean(0).argmax() # max F1 index 78 | return p[:, i], r[:, i], ap, f1[:, i], unique_classes.astype('int32') 79 | 80 | 81 | def compute_ap(recall, precision): 82 | """ Compute the average precision, given the recall and precision curves 83 | # Arguments 84 | recall: The recall curve (list) 85 | precision: The precision curve (list) 86 | # Returns 87 | Average precision, precision curve, recall curve 88 | """ 89 | 90 | # Append sentinel values to beginning and end 91 | mrec = np.concatenate(([0.], recall, [recall[-1] + 0.01])) 92 | mpre = np.concatenate(([1.], precision, [0.])) 93 | 94 | # Compute the precision envelope 95 | mpre = np.flip(np.maximum.accumulate(np.flip(mpre))) 96 | 97 | # Integrate area under curve 98 | method = 'interp' # methods: 'continuous', 'interp' 99 | if method == 'interp': 100 | x = np.linspace(0, 1, 101) # 101-point interp (COCO) 101 | ap = np.trapz(np.interp(x, mrec, mpre), x) # integrate 102 | else: # 'continuous' 103 | i = np.where(mrec[1:] != mrec[:-1])[0] # points where x axis (recall) changes 104 | ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1]) # area under curve 105 | 106 | return ap, mpre, mrec 107 | 108 | 109 | class ConfusionMatrix: 110 | # Updated version of https://github.com/kaanakan/object_detection_confusion_matrix 111 | def __init__(self, nc, conf=0.25, iou_thres=0.45): 112 | self.matrix = np.zeros((nc + 1, nc + 1)) 113 | self.nc = nc # number of classes 114 | self.conf = conf 115 | self.iou_thres = iou_thres 116 | 117 | def process_batch(self, detections, labels): 118 | """ 119 | Return intersection-over-union (Jaccard index) of boxes. 120 | Both sets of boxes are expected to be in (x1, y1, x2, y2) format. 121 | Arguments: 122 | detections (Array[N, 6]), x1, y1, x2, y2, conf, class 123 | labels (Array[M, 5]), class, x1, y1, x2, y2 124 | Returns: 125 | None, updates confusion matrix accordingly 126 | """ 127 | detections = detections[detections[:, 4] > self.conf] 128 | gt_classes = labels[:, 0].int() 129 | detection_classes = detections[:, 5].int() 130 | iou = general.box_iou(labels[:, 1:], detections[:, :4]) 131 | 132 | x = torch.where(iou > self.iou_thres) 133 | if x[0].shape[0]: 134 | matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy() 135 | if x[0].shape[0] > 1: 136 | matches = matches[matches[:, 2].argsort()[::-1]] 137 | matches = matches[np.unique(matches[:, 1], return_index=True)[1]] 138 | matches = matches[matches[:, 2].argsort()[::-1]] 139 | matches = matches[np.unique(matches[:, 0], return_index=True)[1]] 140 | else: 141 | matches = np.zeros((0, 3)) 142 | 143 | n = matches.shape[0] > 0 144 | m0, m1, _ = matches.transpose().astype(np.int16) 145 | for i, gc in enumerate(gt_classes): 146 | j = m0 == i 147 | if n and sum(j) == 1: 148 | self.matrix[detection_classes[m1[j]], gc] += 1 # correct 149 | else: 150 | self.matrix[self.nc, gc] += 1 # background FP 151 | 152 | if n: 153 | for i, dc in enumerate(detection_classes): 154 | if not any(m1 == i): 155 | self.matrix[dc, self.nc] += 1 # background FN 156 | 157 | def matrix(self): 158 | return self.matrix 159 | 160 | def plot(self, save_dir='', names=()): 161 | try: 162 | import seaborn as sn 163 | 164 | array = self.matrix / (self.matrix.sum(0).reshape(1, self.nc + 1) + 1E-6) # normalize 165 | array[array < 0.005] = np.nan # don't annotate (would appear as 0.00) 166 | 167 | fig = plt.figure(figsize=(12, 9), tight_layout=True) 168 | sn.set(font_scale=1.0 if self.nc < 50 else 0.8) # for label size 169 | labels = (0 < len(names) < 99) and len(names) == self.nc # apply names to ticklabels 170 | sn.heatmap(array, annot=self.nc < 30, annot_kws={"size": 8}, cmap='Blues', fmt='.2f', square=True, 171 | xticklabels=names + ['background FP'] if labels else "auto", 172 | yticklabels=names + ['background FN'] if labels else "auto").set_facecolor((1, 1, 1)) 173 | fig.axes[0].set_xlabel('True') 174 | fig.axes[0].set_ylabel('Predicted') 175 | fig.savefig(Path(save_dir) / 'confusion_matrix.png', dpi=250) 176 | except Exception as e: 177 | pass 178 | 179 | def print(self): 180 | for i in range(self.nc + 1): 181 | print(' '.join(map(str, self.matrix[i]))) 182 | 183 | 184 | # Plots ---------------------------------------------------------------------------------------------------------------- 185 | 186 | def plot_pr_curve(px, py, ap, save_dir='pr_curve.png', names=()): 187 | # Precision-recall curve 188 | fig, ax = plt.subplots(1, 1, figsize=(9, 6), tight_layout=True) 189 | py = np.stack(py, axis=1) 190 | 191 | if 0 < len(names) < 21: # display per-class legend if < 21 classes 192 | for i, y in enumerate(py.T): 193 | ax.plot(px, y, linewidth=1, label=f'{names[i]} {ap[i, 0]:.3f}') # plot(recall, precision) 194 | else: 195 | ax.plot(px, py, linewidth=1, color='grey') # plot(recall, precision) 196 | 197 | ax.plot(px, py.mean(1), linewidth=3, color='blue', label='all classes %.3f mAP@0.5' % ap[:, 0].mean()) 198 | ax.set_xlabel('Recall') 199 | ax.set_ylabel('Precision') 200 | ax.set_xlim(0, 1) 201 | ax.set_ylim(0, 1) 202 | plt.legend(bbox_to_anchor=(1.04, 1), loc="upper left") 203 | fig.savefig(Path(save_dir), dpi=250) 204 | 205 | 206 | def plot_mc_curve(px, py, save_dir='mc_curve.png', names=(), xlabel='Confidence', ylabel='Metric'): 207 | # Metric-confidence curve 208 | fig, ax = plt.subplots(1, 1, figsize=(9, 6), tight_layout=True) 209 | 210 | if 0 < len(names) < 21: # display per-class legend if < 21 classes 211 | for i, y in enumerate(py): 212 | ax.plot(px, y, linewidth=1, label=f'{names[i]}') # plot(confidence, metric) 213 | else: 214 | ax.plot(px, py.T, linewidth=1, color='grey') # plot(confidence, metric) 215 | 216 | y = py.mean(0) 217 | ax.plot(px, y, linewidth=3, color='blue', label=f'all classes {y.max():.2f} at {px[y.argmax()]:.3f}') 218 | ax.set_xlabel(xlabel) 219 | ax.set_ylabel(ylabel) 220 | ax.set_xlim(0, 1) 221 | ax.set_ylim(0, 1) 222 | plt.legend(bbox_to_anchor=(1.04, 1), loc="upper left") 223 | fig.savefig(Path(save_dir), dpi=250) 224 | -------------------------------------------------------------------------------- /yolov5/utils/wandb_logging/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/yolov5/utils/wandb_logging/__init__.py -------------------------------------------------------------------------------- /yolov5/utils/wandb_logging/log_dataset.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | import yaml 4 | 5 | from wandb_utils import WandbLogger 6 | 7 | WANDB_ARTIFACT_PREFIX = 'wandb-artifact://' 8 | 9 | 10 | def create_dataset_artifact(opt): 11 | with open(opt.data) as f: 12 | data = yaml.safe_load(f) # data dict 13 | logger = WandbLogger(opt, '', None, data, job_type='Dataset Creation') 14 | 15 | 16 | if __name__ == '__main__': 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument('--data', type=str, default='data/coco128.yaml', help='data.yaml path') 19 | parser.add_argument('--single-cls', action='store_true', help='train as single-class dataset') 20 | parser.add_argument('--project', type=str, default='YOLOv5', help='name of W&B Project') 21 | opt = parser.parse_args() 22 | opt.resume = False # Explicitly disallow resume check for dataset upload job 23 | 24 | create_dataset_artifact(opt) 25 | -------------------------------------------------------------------------------- /yolov5/weights/yolov5s.pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/yolov5/weights/yolov5s.pt -------------------------------------------------------------------------------- /yolov5/yolov5-b8be76f915207ef0759bfb0f1c0707c79877b763.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owo12321/Yolov5_DeepSort_Traffic-counter/a6e0f34293610109e630c21f44c14c7010fe56e5/yolov5/yolov5-b8be76f915207ef0759bfb0f1c0707c79877b763.zip --------------------------------------------------------------------------------