├── models ├── __init__.py ├── yolov3-tiny.yaml ├── yolov3.yaml ├── yolov3-spp.yaml └── experimental.py ├── utils ├── loggers │ ├── wandb │ │ ├── __init__.py │ │ ├── log_dataset.py │ │ ├── sweep.py │ │ ├── sweep.yaml │ │ └── README.md │ └── __init__.py ├── __init__.py ├── autobatch.py ├── callbacks.py ├── activations.py ├── downloads.py ├── autoanchor.py ├── loss.py ├── augmentations.py └── torch_utils.py ├── .gitattributes ├── data ├── images │ ├── bus.jpg │ └── zidane.jpg ├── scripts │ ├── download_weights.sh │ ├── get_coco128.sh │ └── get_coco.sh ├── coco128.yaml ├── hyps │ ├── hyp.scratch.yaml │ ├── hyp.scratch-high.yaml │ ├── hyp.scratch-med.yaml │ └── hyp.scratch-low.yaml ├── GlobalWheat2020.yaml ├── coco.yaml ├── SKU-110K.yaml ├── Argoverse.yaml ├── VisDrone.yaml ├── voc.yaml ├── xView.yaml └── objects365.yaml ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── question.yml │ ├── feature-request.yml │ └── bug-report.yml ├── dependabot.yml └── workflows │ ├── rebase.yml │ ├── stale.yml │ ├── codeql-analysis.yml │ ├── ci-testing.yml │ └── greetings.yml ├── setup.cfg ├── requirements.txt ├── .pre-commit-config.yaml ├── Dockerfile ├── .dockerignore ├── hubconf.py ├── .gitignore ├── CONTRIBUTING.md ├── detect.py └── README.md /models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /utils/loggers/wandb/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # this drop notebooks from GitHub language stats 2 | *.ipynb linguist-vendored 3 | -------------------------------------------------------------------------------- /data/images/bus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facedetection/yolov3_ultralytics/master/data/images/bus.jpg -------------------------------------------------------------------------------- /data/images/zidane.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facedetection/yolov3_ultralytics/master/data/images/zidane.jpg -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: glenn-jocher 4 | patreon: ultralytics 5 | open_collective: ultralytics 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: Slack 4 | url: https://join.slack.com/t/ultralytics/shared_invite/zt-w29ei8bp-jczz7QYUmDtgo6r6KcMIAg 5 | about: Ask on Ultralytics Slack Forum 6 | - name: Stack Overflow 7 | url: https://stackoverflow.com/search?q=YOLOv3 8 | about: Ask on Stack Overflow with 'YOLOv3' tag 9 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | utils/initialization 4 | """ 5 | 6 | 7 | def notebook_init(): 8 | # For notebooks 9 | print('Checking setup...') 10 | from IPython import display # to display images and clear console output 11 | 12 | from utils.general import emojis 13 | from utils.torch_utils import select_device # imports 14 | 15 | display.clear_output() 16 | select_device(newline=False) 17 | print(emojis('Setup complete ✅')) 18 | return display 19 | -------------------------------------------------------------------------------- /data/scripts/download_weights.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 3 | # Download latest models from https://github.com/ultralytics/yolov3/releases 4 | # Example usage: bash path/to/download_weights.sh 5 | # parent 6 | # └── yolov3 7 | # ├── yolov3.pt ← downloads here 8 | # ├── yolov3-spp.pt 9 | # └── ... 10 | 11 | python - < 15 | Please search the [issues](https://github.com/ultralytics/yolov3/issues) and [discussions](https://github.com/ultralytics/yolov3/discussions) to see if a similar question already exists. 16 | options: 17 | - label: > 18 | I have searched the YOLOv3 [issues](https://github.com/ultralytics/yolov3/issues) and [discussions](https://github.com/ultralytics/yolov3/discussions) and found no similar questions. 19 | required: true 20 | 21 | - type: textarea 22 | attributes: 23 | label: Question 24 | description: What is your question? 25 | placeholder: | 26 | 💡 ProTip! Include as much information as possible (screenshots, logs, tracebacks etc.) to receive the most helpful response. 27 | validations: 28 | required: true 29 | 30 | - type: textarea 31 | attributes: 32 | label: Additional 33 | description: Anything else you would like to share? 34 | -------------------------------------------------------------------------------- /utils/loggers/wandb/sweep.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | import wandb 5 | 6 | FILE = Path(__file__).resolve() 7 | ROOT = FILE.parents[3] # root directory 8 | if str(ROOT) not in sys.path: 9 | sys.path.append(str(ROOT)) # add ROOT to PATH 10 | 11 | from train import parse_opt, train 12 | from utils.callbacks import Callbacks 13 | from utils.general import increment_path 14 | from utils.torch_utils import select_device 15 | 16 | 17 | def sweep(): 18 | wandb.init() 19 | # Get hyp dict from sweep agent 20 | hyp_dict = vars(wandb.config).get("_items") 21 | 22 | # Workaround: get necessary opt args 23 | opt = parse_opt(known=True) 24 | opt.batch_size = hyp_dict.get("batch_size") 25 | opt.save_dir = str(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok or opt.evolve)) 26 | opt.epochs = hyp_dict.get("epochs") 27 | opt.nosave = True 28 | opt.data = hyp_dict.get("data") 29 | opt.weights = str(opt.weights) 30 | opt.cfg = str(opt.cfg) 31 | opt.data = str(opt.data) 32 | opt.hyp = str(opt.hyp) 33 | opt.project = str(opt.project) 34 | device = select_device(opt.device, batch_size=opt.batch_size) 35 | 36 | # train 37 | train(hyp_dict, opt, device, callbacks=Callbacks()) 38 | 39 | 40 | if __name__ == "__main__": 41 | sweep() 42 | -------------------------------------------------------------------------------- /models/yolov3-tiny.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 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 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # YOLOv3 requirements 2 | # Usage: pip install -r requirements.txt 3 | 4 | # Base ---------------------------------------- 5 | matplotlib>=3.2.2 6 | numpy>=1.18.5 7 | opencv-python>=4.1.1 8 | Pillow>=7.1.2 9 | PyYAML>=5.3.1 10 | requests>=2.23.0 11 | scipy>=1.4.1 12 | torch>=1.7.0 13 | torchvision>=0.8.1 14 | tqdm>=4.64.0 15 | # protobuf<=3.20.1 # https://github.com/ultralytics/yolov5/issues/8012 16 | 17 | # Logging ------------------------------------- 18 | tensorboard>=2.4.1 19 | # wandb 20 | # clearml 21 | 22 | # Plotting ------------------------------------ 23 | pandas>=1.1.4 24 | seaborn>=0.11.0 25 | 26 | # Export -------------------------------------- 27 | # coremltools>=6.0 # CoreML export 28 | # onnx>=1.9.0 # ONNX export 29 | # onnx-simplifier>=0.4.1 # ONNX simplifier 30 | # nvidia-pyindex # TensorRT export 31 | # nvidia-tensorrt # TensorRT export 32 | # scikit-learn<=1.1.2 # CoreML quantization 33 | # tensorflow>=2.4.1 # TF exports (-cpu, -aarch64, -macos) 34 | # tensorflowjs>=3.9.0 # TF.js export 35 | # openvino-dev # OpenVINO export 36 | 37 | # Deploy -------------------------------------- 38 | # tritonclient[all]~=2.24.0 39 | 40 | # Extras -------------------------------------- 41 | ipython # interactive notebook 42 | psutil # system utilization 43 | thop>=0.1.1 # FLOPs computation 44 | # mss # screenshots 45 | # albumentations>=1.0.3 46 | # pycocotools>=2.0 # COCO mAP 47 | # roboflow 48 | -------------------------------------------------------------------------------- /models/yolov3.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 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 | -------------------------------------------------------------------------------- /models/yolov3-spp.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Parameters 4 | nc: 80 # number of classes 5 | depth_multiple: 1.0 # model depth multiple 6 | width_multiple: 1.0 # layer channel multiple 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 | -------------------------------------------------------------------------------- /data/coco128.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | # COCO128 dataset https://www.kaggle.com/ultralytics/coco128 (first 128 images from COCO train2017) 3 | # Example usage: python train.py --data coco128.yaml 4 | # parent 5 | # ├── yolov3 6 | # └── datasets 7 | # └── coco128 ← downloads here 8 | 9 | 10 | # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] 11 | path: ../datasets/coco128 # dataset root dir 12 | train: images/train2017 # train images (relative to 'path') 128 images 13 | val: images/train2017 # val images (relative to 'path') 128 images 14 | test: # test images (optional) 15 | 16 | # Classes 17 | nc: 80 # number of classes 18 | names: ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 19 | 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 20 | 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 21 | 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 22 | 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 23 | 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 24 | 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 25 | 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 26 | 'hair drier', 'toothbrush'] # class names 27 | 28 | 29 | # Download script/URL (optional) 30 | download: https://ultralytics.com/assets/coco128.zip 31 | -------------------------------------------------------------------------------- /data/hyps/hyp.scratch.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | # Hyperparameters for COCO training from scratch 3 | # python train.py --batch 40 --cfg yolov5m.yaml --weights '' --data coco.yaml --img 640 --epochs 300 4 | # See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials 5 | 6 | lr0: 0.01 # initial learning rate (SGD=1E-2, Adam=1E-3) 7 | lrf: 0.1 # final OneCycleLR learning rate (lr0 * lrf) 8 | momentum: 0.937 # SGD momentum/Adam beta1 9 | weight_decay: 0.0005 # optimizer weight decay 5e-4 10 | warmup_epochs: 3.0 # warmup epochs (fractions ok) 11 | warmup_momentum: 0.8 # warmup initial momentum 12 | warmup_bias_lr: 0.1 # warmup initial bias lr 13 | box: 0.05 # box loss gain 14 | cls: 0.5 # cls loss gain 15 | cls_pw: 1.0 # cls BCELoss positive_weight 16 | obj: 1.0 # obj loss gain (scale with pixels) 17 | obj_pw: 1.0 # obj BCELoss positive_weight 18 | iou_t: 0.20 # IoU training threshold 19 | anchor_t: 4.0 # anchor-multiple threshold 20 | # anchors: 3 # anchors per output layer (0 to ignore) 21 | fl_gamma: 0.0 # focal loss gamma (efficientDet default gamma=1.5) 22 | hsv_h: 0.015 # image HSV-Hue augmentation (fraction) 23 | hsv_s: 0.7 # image HSV-Saturation augmentation (fraction) 24 | hsv_v: 0.4 # image HSV-Value augmentation (fraction) 25 | degrees: 0.0 # image rotation (+/- deg) 26 | translate: 0.1 # image translation (+/- fraction) 27 | scale: 0.5 # image scale (+/- gain) 28 | shear: 0.0 # image shear (+/- deg) 29 | perspective: 0.0 # image perspective (+/- fraction), range 0-0.001 30 | flipud: 0.0 # image flip up-down (probability) 31 | fliplr: 0.5 # image flip left-right (probability) 32 | mosaic: 1.0 # image mosaic (probability) 33 | mixup: 0.0 # image mixup (probability) 34 | copy_paste: 0.0 # segment copy-paste (probability) 35 | -------------------------------------------------------------------------------- /data/hyps/hyp.scratch-high.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | # Hyperparameters for high-augmentation COCO training from scratch 3 | # python train.py --batch 32 --cfg yolov5m6.yaml --weights '' --data coco.yaml --img 1280 --epochs 300 4 | # See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials 5 | 6 | lr0: 0.01 # initial learning rate (SGD=1E-2, Adam=1E-3) 7 | lrf: 0.2 # final OneCycleLR learning rate (lr0 * lrf) 8 | momentum: 0.937 # SGD momentum/Adam beta1 9 | weight_decay: 0.0005 # optimizer weight decay 5e-4 10 | warmup_epochs: 3.0 # warmup epochs (fractions ok) 11 | warmup_momentum: 0.8 # warmup initial momentum 12 | warmup_bias_lr: 0.1 # warmup initial bias lr 13 | box: 0.05 # box loss gain 14 | cls: 0.3 # cls loss gain 15 | cls_pw: 1.0 # cls BCELoss positive_weight 16 | obj: 0.7 # obj loss gain (scale with pixels) 17 | obj_pw: 1.0 # obj BCELoss positive_weight 18 | iou_t: 0.20 # IoU training threshold 19 | anchor_t: 4.0 # anchor-multiple threshold 20 | # anchors: 3 # anchors per output layer (0 to ignore) 21 | fl_gamma: 0.0 # focal loss gamma (efficientDet default gamma=1.5) 22 | hsv_h: 0.015 # image HSV-Hue augmentation (fraction) 23 | hsv_s: 0.7 # image HSV-Saturation augmentation (fraction) 24 | hsv_v: 0.4 # image HSV-Value augmentation (fraction) 25 | degrees: 0.0 # image rotation (+/- deg) 26 | translate: 0.1 # image translation (+/- fraction) 27 | scale: 0.9 # image scale (+/- gain) 28 | shear: 0.0 # image shear (+/- deg) 29 | perspective: 0.0 # image perspective (+/- fraction), range 0-0.001 30 | flipud: 0.0 # image flip up-down (probability) 31 | fliplr: 0.5 # image flip left-right (probability) 32 | mosaic: 1.0 # image mosaic (probability) 33 | mixup: 0.1 # image mixup (probability) 34 | copy_paste: 0.1 # segment copy-paste (probability) 35 | -------------------------------------------------------------------------------- /data/hyps/hyp.scratch-med.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | # Hyperparameters for medium-augmentation COCO training from scratch 3 | # python train.py --batch 32 --cfg yolov5m6.yaml --weights '' --data coco.yaml --img 1280 --epochs 300 4 | # See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials 5 | 6 | lr0: 0.01 # initial learning rate (SGD=1E-2, Adam=1E-3) 7 | lrf: 0.1 # final OneCycleLR learning rate (lr0 * lrf) 8 | momentum: 0.937 # SGD momentum/Adam beta1 9 | weight_decay: 0.0005 # optimizer weight decay 5e-4 10 | warmup_epochs: 3.0 # warmup epochs (fractions ok) 11 | warmup_momentum: 0.8 # warmup initial momentum 12 | warmup_bias_lr: 0.1 # warmup initial bias lr 13 | box: 0.05 # box loss gain 14 | cls: 0.3 # cls loss gain 15 | cls_pw: 1.0 # cls BCELoss positive_weight 16 | obj: 0.7 # obj loss gain (scale with pixels) 17 | obj_pw: 1.0 # obj BCELoss positive_weight 18 | iou_t: 0.20 # IoU training threshold 19 | anchor_t: 4.0 # anchor-multiple threshold 20 | # anchors: 3 # anchors per output layer (0 to ignore) 21 | fl_gamma: 0.0 # focal loss gamma (efficientDet default gamma=1.5) 22 | hsv_h: 0.015 # image HSV-Hue augmentation (fraction) 23 | hsv_s: 0.7 # image HSV-Saturation augmentation (fraction) 24 | hsv_v: 0.4 # image HSV-Value augmentation (fraction) 25 | degrees: 0.0 # image rotation (+/- deg) 26 | translate: 0.1 # image translation (+/- fraction) 27 | scale: 0.9 # image scale (+/- gain) 28 | shear: 0.0 # image shear (+/- deg) 29 | perspective: 0.0 # image perspective (+/- fraction), range 0-0.001 30 | flipud: 0.0 # image flip up-down (probability) 31 | fliplr: 0.5 # image flip left-right (probability) 32 | mosaic: 1.0 # image mosaic (probability) 33 | mixup: 0.1 # image mixup (probability) 34 | copy_paste: 0.0 # segment copy-paste (probability) 35 | -------------------------------------------------------------------------------- /data/hyps/hyp.scratch-low.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | # Hyperparameters for low-augmentation COCO training from scratch 3 | # python train.py --batch 64 --cfg yolov5n6.yaml --weights '' --data coco.yaml --img 640 --epochs 300 --linear 4 | # See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials 5 | 6 | lr0: 0.01 # initial learning rate (SGD=1E-2, Adam=1E-3) 7 | lrf: 0.01 # final OneCycleLR learning rate (lr0 * lrf) 8 | momentum: 0.937 # SGD momentum/Adam beta1 9 | weight_decay: 0.0005 # optimizer weight decay 5e-4 10 | warmup_epochs: 3.0 # warmup epochs (fractions ok) 11 | warmup_momentum: 0.8 # warmup initial momentum 12 | warmup_bias_lr: 0.1 # warmup initial bias lr 13 | box: 0.05 # box loss gain 14 | cls: 0.5 # cls loss gain 15 | cls_pw: 1.0 # cls BCELoss positive_weight 16 | obj: 1.0 # obj loss gain (scale with pixels) 17 | obj_pw: 1.0 # obj BCELoss positive_weight 18 | iou_t: 0.20 # IoU training threshold 19 | anchor_t: 4.0 # anchor-multiple threshold 20 | # anchors: 3 # anchors per output layer (0 to ignore) 21 | fl_gamma: 0.0 # focal loss gamma (efficientDet default gamma=1.5) 22 | hsv_h: 0.015 # image HSV-Hue augmentation (fraction) 23 | hsv_s: 0.7 # image HSV-Saturation augmentation (fraction) 24 | hsv_v: 0.4 # image HSV-Value augmentation (fraction) 25 | degrees: 0.0 # image rotation (+/- deg) 26 | translate: 0.1 # image translation (+/- fraction) 27 | scale: 0.5 # image scale (+/- gain) 28 | shear: 0.0 # image shear (+/- deg) 29 | perspective: 0.0 # image perspective (+/- fraction), range 0-0.001 30 | flipud: 0.0 # image flip up-down (probability) 31 | fliplr: 0.5 # image flip left-right (probability) 32 | mosaic: 1.0 # image mosaic (probability) 33 | mixup: 0.0 # image mixup (probability) 34 | copy_paste: 0.0 # segment copy-paste (probability) 35 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Define hooks for code formations 2 | # Will be applied on any updated commit files if a user has installed and linked commit hook 3 | 4 | default_language_version: 5 | python: python3.8 6 | 7 | # Define bot property if installed via https://github.com/marketplace/pre-commit-ci 8 | ci: 9 | autofix_prs: true 10 | autoupdate_commit_msg: '[pre-commit.ci] pre-commit suggestions' 11 | autoupdate_schedule: quarterly 12 | # submodules: true 13 | 14 | repos: 15 | - repo: https://github.com/pre-commit/pre-commit-hooks 16 | rev: v4.3.0 17 | hooks: 18 | - id: end-of-file-fixer 19 | - id: trailing-whitespace 20 | - id: check-case-conflict 21 | - id: check-yaml 22 | - id: check-toml 23 | - id: pretty-format-json 24 | - id: check-docstring-first 25 | 26 | - repo: https://github.com/asottile/pyupgrade 27 | rev: v2.34.0 28 | hooks: 29 | - id: pyupgrade 30 | args: [--py36-plus] 31 | name: Upgrade code 32 | 33 | - repo: https://github.com/PyCQA/isort 34 | rev: 5.10.1 35 | hooks: 36 | - id: isort 37 | name: Sort imports 38 | 39 | # TODO 40 | #- repo: https://github.com/pre-commit/mirrors-yapf 41 | # rev: v0.31.0 42 | # hooks: 43 | # - id: yapf 44 | # name: formatting 45 | 46 | # TODO 47 | #- repo: https://github.com/executablebooks/mdformat 48 | # rev: 0.7.7 49 | # hooks: 50 | # - id: mdformat 51 | # additional_dependencies: 52 | # - mdformat-gfm 53 | # - mdformat-black 54 | # - mdformat_frontmatter 55 | 56 | # TODO 57 | #- repo: https://github.com/asottile/yesqa 58 | # rev: v1.2.3 59 | # hooks: 60 | # - id: yesqa 61 | 62 | - repo: https://github.com/PyCQA/flake8 63 | rev: 4.0.1 64 | hooks: 65 | - id: flake8 66 | name: PEP8 67 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | name: Close stale issues 4 | on: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | 8 | jobs: 9 | stale: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/stale@v6 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | stale-issue-message: | 16 | 👋 Hello, this issue has been automatically marked as stale because it has not had recent activity. Please note it will be closed if no further activity occurs. 17 | 18 | Access additional [YOLOv3](https://ultralytics.com/yolov3) 🚀 resources: 19 | - **Wiki** – https://github.com/ultralytics/yolov3/wiki 20 | - **Tutorials** – https://github.com/ultralytics/yolov3#tutorials 21 | - **Docs** – https://docs.ultralytics.com 22 | 23 | Access additional [Ultralytics](https://ultralytics.com) ⚡ resources: 24 | - **Ultralytics HUB** – https://ultralytics.com/hub 25 | - **Vision API** – https://ultralytics.com/yolov5 26 | - **About Us** – https://ultralytics.com/about 27 | - **Join Our Team** – https://ultralytics.com/work 28 | - **Contact Us** – https://ultralytics.com/contact 29 | 30 | Feel free to inform us of any other **issues** you discover or **feature requests** that come to mind in the future. Pull Requests (PRs) are also always welcomed! 31 | 32 | Thank you for your contributions to YOLOv3 🚀 and Vision AI ⭐! 33 | 34 | stale-pr-message: 'This pull request 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 YOLOv3 🚀 and Vision AI ⭐.' 35 | days-before-stale: 30 36 | days-before-close: 5 37 | exempt-issue-labels: 'documentation,tutorial' 38 | operations-per-run: 100 # The maximum number of operations per run, used to control rate limiting. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: 🚀 Feature Request 2 | description: Suggest a YOLOv3 idea 3 | # title: " " 4 | labels: [enhancement] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thank you for submitting a YOLOv3 🚀 Feature Request! 10 | 11 | - type: checkboxes 12 | attributes: 13 | label: Search before asking 14 | description: > 15 | Please search the [issues](https://github.com/ultralytics/yolov3/issues) to see if a similar feature request already exists. 16 | options: 17 | - label: > 18 | I have searched the YOLOv3 [issues](https://github.com/ultralytics/yolov3/issues) and found no similar feature requests. 19 | required: true 20 | 21 | - type: textarea 22 | attributes: 23 | label: Description 24 | description: A short description of your feature. 25 | placeholder: | 26 | What new feature would you like to see in YOLOv3? 27 | validations: 28 | required: true 29 | 30 | - type: textarea 31 | attributes: 32 | label: Use case 33 | description: | 34 | Describe the use case of your feature request. It will help us understand and prioritize the feature request. 35 | placeholder: | 36 | How would this feature be used, and who would use it? 37 | 38 | - type: textarea 39 | attributes: 40 | label: Additional 41 | description: Anything else you would like to share? 42 | 43 | - type: checkboxes 44 | attributes: 45 | label: Are you willing to submit a PR? 46 | description: > 47 | (Optional) We encourage you to submit a [Pull Request](https://github.com/ultralytics/yolov3/pulls) (PR) to help improve YOLOv3 for everyone, especially if you have a good understanding of how to implement a fix or feature. 48 | See the YOLOv3 [Contributing Guide](https://github.com/ultralytics/yolov3/blob/master/CONTRIBUTING.md) to get started. 49 | options: 50 | - label: Yes I'd like to help by submitting a PR! 51 | -------------------------------------------------------------------------------- /data/GlobalWheat2020.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | # Global Wheat 2020 dataset http://www.global-wheat.com/ 3 | # Example usage: python train.py --data GlobalWheat2020.yaml 4 | # parent 5 | # ├── yolov3 6 | # └── datasets 7 | # └── GlobalWheat2020 ← downloads here 8 | 9 | 10 | # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] 11 | path: ../datasets/GlobalWheat2020 # dataset root dir 12 | train: # train images (relative to 'path') 3422 images 13 | - images/arvalis_1 14 | - images/arvalis_2 15 | - images/arvalis_3 16 | - images/ethz_1 17 | - images/rres_1 18 | - images/inrae_1 19 | - images/usask_1 20 | val: # val images (relative to 'path') 748 images (WARNING: train set contains ethz_1) 21 | - images/ethz_1 22 | test: # test images (optional) 1276 images 23 | - images/utokyo_1 24 | - images/utokyo_2 25 | - images/nau_1 26 | - images/uq_1 27 | 28 | # Classes 29 | nc: 1 # number of classes 30 | names: ['wheat_head'] # class names 31 | 32 | 33 | # Download script/URL (optional) --------------------------------------------------------------------------------------- 34 | download: | 35 | from utils.general import download, Path 36 | 37 | # Download 38 | dir = Path(yaml['path']) # dataset root dir 39 | urls = ['https://zenodo.org/record/4298502/files/global-wheat-codalab-official.zip', 40 | 'https://github.com/ultralytics/yolov5/releases/download/v1.0/GlobalWheat2020_labels.zip'] 41 | download(urls, dir=dir) 42 | 43 | # Make Directories 44 | for p in 'annotations', 'images', 'labels': 45 | (dir / p).mkdir(parents=True, exist_ok=True) 46 | 47 | # Move 48 | for p in 'arvalis_1', 'arvalis_2', 'arvalis_3', 'ethz_1', 'rres_1', 'inrae_1', 'usask_1', \ 49 | 'utokyo_1', 'utokyo_2', 'nau_1', 'uq_1': 50 | (dir / p).rename(dir / 'images' / p) # move to /images 51 | f = (dir / p).with_suffix('.json') # json file 52 | if f.exists(): 53 | f.rename((dir / 'annotations' / p).with_suffix('.json')) # move to /annotations 54 | -------------------------------------------------------------------------------- /.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@v3 26 | 27 | # Initializes the CodeQL tools for scanning. 28 | - name: Initialize CodeQL 29 | uses: github/codeql-action/init@v2 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@v2 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@v2 55 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | # Start FROM Nvidia PyTorch image https://ngc.nvidia.com/catalog/containers/nvidia:pytorch 4 | FROM nvcr.io/nvidia/pytorch:21.10-py3 5 | 6 | # Install linux packages 7 | RUN apt update && apt install -y zip htop screen libgl1-mesa-glx 8 | 9 | # Install python dependencies 10 | COPY requirements.txt . 11 | RUN python -m pip install --upgrade pip 12 | RUN pip uninstall -y nvidia-tensorboard nvidia-tensorboard-plugin-dlprof 13 | RUN pip install --no-cache -r requirements.txt coremltools onnx gsutil notebook wandb>=0.12.2 14 | RUN pip install --no-cache -U torch torchvision numpy Pillow 15 | # RUN pip install --no-cache torch==1.10.0+cu113 torchvision==0.11.1+cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html 16 | 17 | # Create working directory 18 | RUN mkdir -p /usr/src/app 19 | WORKDIR /usr/src/app 20 | 21 | # Copy contents 22 | COPY . /usr/src/app 23 | 24 | # Downloads to user config dir 25 | ADD https://ultralytics.com/assets/Arial.ttf /root/.config/Ultralytics/ 26 | 27 | # Set environment variables 28 | # ENV HOME=/usr/src/app 29 | 30 | 31 | # Usage Examples ------------------------------------------------------------------------------------------------------- 32 | 33 | # Build and Push 34 | # t=ultralytics/yolov3:latest && sudo docker build -t $t . && sudo docker push $t 35 | 36 | # Pull and Run 37 | # t=ultralytics/yolov3:latest && sudo docker pull $t && sudo docker run -it --ipc=host --gpus all $t 38 | 39 | # Pull and Run with local directory access 40 | # t=ultralytics/yolov3:latest && sudo docker pull $t && sudo docker run -it --ipc=host --gpus all -v "$(pwd)"/datasets:/usr/src/datasets $t 41 | 42 | # Kill all 43 | # sudo docker kill $(sudo docker ps -q) 44 | 45 | # Kill all image-based 46 | # sudo docker kill $(sudo docker ps -qa --filter ancestor=ultralytics/yolov3:latest) 47 | 48 | # Bash into running container 49 | # sudo docker exec -it 5a9b5863d93d bash 50 | 51 | # Bash into stopped container 52 | # id=$(sudo docker ps -qa) && sudo docker start $id && sudo docker exec -it $id bash 53 | 54 | # Clean up 55 | # docker system prune -a --volumes 56 | 57 | # Update Ubuntu drivers 58 | # https://www.maketecheasier.com/install-nvidia-drivers-ubuntu/ 59 | 60 | # DDP test 61 | # python -m torch.distributed.run --nproc_per_node 2 --master_port 1 train.py --epochs 3 62 | -------------------------------------------------------------------------------- /utils/autobatch.py: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Auto-batch utils 4 | """ 5 | 6 | from copy import deepcopy 7 | 8 | import numpy as np 9 | import torch 10 | from torch.cuda import amp 11 | 12 | from utils.general import LOGGER, colorstr 13 | from utils.torch_utils import profile 14 | 15 | 16 | def check_train_batch_size(model, imgsz=640): 17 | # Check training batch size 18 | with amp.autocast(): 19 | return autobatch(deepcopy(model).train(), imgsz) # compute optimal batch size 20 | 21 | 22 | def autobatch(model, imgsz=640, fraction=0.9, batch_size=16): 23 | # Automatically estimate best batch size to use `fraction` of available CUDA memory 24 | # Usage: 25 | # import torch 26 | # from utils.autobatch import autobatch 27 | # model = torch.hub.load('ultralytics/yolov3', 'yolov3', autoshape=False) 28 | # print(autobatch(model)) 29 | 30 | prefix = colorstr('AutoBatch: ') 31 | LOGGER.info(f'{prefix}Computing optimal batch size for --imgsz {imgsz}') 32 | device = next(model.parameters()).device # get model device 33 | if device.type == 'cpu': 34 | LOGGER.info(f'{prefix}CUDA not detected, using default CPU batch-size {batch_size}') 35 | return batch_size 36 | 37 | d = str(device).upper() # 'CUDA:0' 38 | properties = torch.cuda.get_device_properties(device) # device properties 39 | t = properties.total_memory / 1024 ** 3 # (GiB) 40 | r = torch.cuda.memory_reserved(device) / 1024 ** 3 # (GiB) 41 | a = torch.cuda.memory_allocated(device) / 1024 ** 3 # (GiB) 42 | f = t - (r + a) # free inside reserved 43 | LOGGER.info(f'{prefix}{d} ({properties.name}) {t:.2f}G total, {r:.2f}G reserved, {a:.2f}G allocated, {f:.2f}G free') 44 | 45 | batch_sizes = [1, 2, 4, 8, 16] 46 | try: 47 | img = [torch.zeros(b, 3, imgsz, imgsz) for b in batch_sizes] 48 | y = profile(img, model, n=3, device=device) 49 | except Exception as e: 50 | LOGGER.warning(f'{prefix}{e}') 51 | 52 | y = [x[2] for x in y if x] # memory [2] 53 | batch_sizes = batch_sizes[:len(y)] 54 | p = np.polyfit(batch_sizes, y, deg=1) # first degree polynomial fit 55 | b = int((f * fraction - p[1]) / p[0]) # y intercept (optimal batch size) 56 | LOGGER.info(f'{prefix}Using batch-size {b} for {d} {t * fraction:.2f}G/{t:.2f}G ({fraction * 100:.0f}%)') 57 | return b 58 | -------------------------------------------------------------------------------- /data/coco.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | # COCO 2017 dataset http://cocodataset.org 3 | # Example usage: python train.py --data coco.yaml 4 | # parent 5 | # ├── yolov3 6 | # └── datasets 7 | # └── coco ← downloads here 8 | 9 | 10 | # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] 11 | path: ../datasets/coco # dataset root dir 12 | train: train2017.txt # train images (relative to 'path') 118287 images 13 | val: val2017.txt # train images (relative to 'path') 5000 images 14 | test: test-dev2017.txt # 20288 of 40670 images, submit to https://competitions.codalab.org/competitions/20794 15 | 16 | # Classes 17 | nc: 80 # number of classes 18 | names: ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 19 | 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 20 | 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 21 | 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 22 | 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 23 | 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 24 | 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 25 | 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 26 | 'hair drier', 'toothbrush'] # class names 27 | 28 | 29 | # Download script/URL (optional) 30 | download: | 31 | from utils.general import download, Path 32 | 33 | # Download labels 34 | segments = False # segment or box labels 35 | dir = Path(yaml['path']) # dataset root dir 36 | url = 'https://github.com/ultralytics/yolov5/releases/download/v1.0/' 37 | urls = [url + ('coco2017labels-segments.zip' if segments else 'coco2017labels.zip')] # labels 38 | download(urls, dir=dir.parent) 39 | 40 | # Download data 41 | urls = ['http://images.cocodataset.org/zips/train2017.zip', # 19G, 118k images 42 | 'http://images.cocodataset.org/zips/val2017.zip', # 1G, 5k images 43 | 'http://images.cocodataset.org/zips/test2017.zip'] # 7G, 41k images (optional) 44 | download(urls, dir=dir / 'images', threads=3) 45 | -------------------------------------------------------------------------------- /data/SKU-110K.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | # SKU-110K retail items dataset https://github.com/eg4000/SKU110K_CVPR19 3 | # Example usage: python train.py --data SKU-110K.yaml 4 | # parent 5 | # ├── yolov3 6 | # └── datasets 7 | # └── SKU-110K ← downloads here 8 | 9 | 10 | # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] 11 | path: ../datasets/SKU-110K # dataset root dir 12 | train: train.txt # train images (relative to 'path') 8219 images 13 | val: val.txt # val images (relative to 'path') 588 images 14 | test: test.txt # test images (optional) 2936 images 15 | 16 | # Classes 17 | nc: 1 # number of classes 18 | names: ['object'] # class names 19 | 20 | 21 | # Download script/URL (optional) --------------------------------------------------------------------------------------- 22 | download: | 23 | import shutil 24 | from tqdm import tqdm 25 | from utils.general import np, pd, Path, download, xyxy2xywh 26 | 27 | # Download 28 | dir = Path(yaml['path']) # dataset root dir 29 | parent = Path(dir.parent) # download dir 30 | urls = ['http://trax-geometry.s3.amazonaws.com/cvpr_challenge/SKU110K_fixed.tar.gz'] 31 | download(urls, dir=parent, delete=False) 32 | 33 | # Rename directories 34 | if dir.exists(): 35 | shutil.rmtree(dir) 36 | (parent / 'SKU110K_fixed').rename(dir) # rename dir 37 | (dir / 'labels').mkdir(parents=True, exist_ok=True) # create labels dir 38 | 39 | # Convert labels 40 | names = 'image', 'x1', 'y1', 'x2', 'y2', 'class', 'image_width', 'image_height' # column names 41 | for d in 'annotations_train.csv', 'annotations_val.csv', 'annotations_test.csv': 42 | x = pd.read_csv(dir / 'annotations' / d, names=names).values # annotations 43 | images, unique_images = x[:, 0], np.unique(x[:, 0]) 44 | with open((dir / d).with_suffix('.txt').__str__().replace('annotations_', ''), 'w') as f: 45 | f.writelines(f'./images/{s}\n' for s in unique_images) 46 | for im in tqdm(unique_images, desc=f'Converting {dir / d}'): 47 | cls = 0 # single-class dataset 48 | with open((dir / 'labels' / im).with_suffix('.txt'), 'a') as f: 49 | for r in x[images == im]: 50 | w, h = r[6], r[7] # image width, height 51 | xywh = xyxy2xywh(np.array([[r[1] / w, r[2] / h, r[3] / w, r[4] / h]]))[0] # instance 52 | f.write(f"{cls} {xywh[0]:.5f} {xywh[1]:.5f} {xywh[2]:.5f} {xywh[3]:.5f}\n") # write label 53 | -------------------------------------------------------------------------------- /utils/callbacks.py: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Callback utils 4 | """ 5 | 6 | 7 | class Callbacks: 8 | """" 9 | Handles all registered callbacks for Hooks 10 | """ 11 | 12 | # Define the available callbacks 13 | _callbacks = { 14 | 'on_pretrain_routine_start': [], 15 | 'on_pretrain_routine_end': [], 16 | 17 | 'on_train_start': [], 18 | 'on_train_epoch_start': [], 19 | 'on_train_batch_start': [], 20 | 'optimizer_step': [], 21 | 'on_before_zero_grad': [], 22 | 'on_train_batch_end': [], 23 | 'on_train_epoch_end': [], 24 | 25 | 'on_val_start': [], 26 | 'on_val_batch_start': [], 27 | 'on_val_image_end': [], 28 | 'on_val_batch_end': [], 29 | 'on_val_end': [], 30 | 31 | 'on_fit_epoch_end': [], # fit = train + val 32 | 'on_model_save': [], 33 | 'on_train_end': [], 34 | 35 | 'teardown': [], 36 | } 37 | 38 | def register_action(self, hook, name='', callback=None): 39 | """ 40 | Register a new action to a callback hook 41 | 42 | Args: 43 | hook The callback hook name to register the action to 44 | name The name of the action for later reference 45 | callback The callback to fire 46 | """ 47 | assert hook in self._callbacks, f"hook '{hook}' not found in callbacks {self._callbacks}" 48 | assert callable(callback), f"callback '{callback}' is not callable" 49 | self._callbacks[hook].append({'name': name, 'callback': callback}) 50 | 51 | def get_registered_actions(self, hook=None): 52 | """" 53 | Returns all the registered actions by callback hook 54 | 55 | Args: 56 | hook The name of the hook to check, defaults to all 57 | """ 58 | if hook: 59 | return self._callbacks[hook] 60 | else: 61 | return self._callbacks 62 | 63 | def run(self, hook, *args, **kwargs): 64 | """ 65 | Loop through the registered actions and fire all callbacks 66 | 67 | Args: 68 | hook The name of the hook to check, defaults to all 69 | args Arguments to receive from 70 | kwargs Keyword Arguments to receive from 71 | """ 72 | 73 | assert hook in self._callbacks, f"hook '{hook}' not found in callbacks {self._callbacks}" 74 | 75 | for logger in self._callbacks[hook]: 76 | logger['callback'](*args, **kwargs) 77 | -------------------------------------------------------------------------------- /data/Argoverse.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | # Argoverse-HD dataset (ring-front-center camera) http://www.cs.cmu.edu/~mengtial/proj/streaming/ 3 | # Example usage: python train.py --data Argoverse.yaml 4 | # parent 5 | # ├── yolov3 6 | # └── datasets 7 | # └── Argoverse ← downloads here 8 | 9 | 10 | # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] 11 | path: ../datasets/Argoverse # dataset root dir 12 | train: Argoverse-1.1/images/train/ # train images (relative to 'path') 39384 images 13 | val: Argoverse-1.1/images/val/ # val images (relative to 'path') 15062 images 14 | test: Argoverse-1.1/images/test/ # test images (optional) https://eval.ai/web/challenges/challenge-page/800/overview 15 | 16 | # Classes 17 | nc: 8 # number of classes 18 | names: ['person', 'bicycle', 'car', 'motorcycle', 'bus', 'truck', 'traffic_light', 'stop_sign'] # class names 19 | 20 | 21 | # Download script/URL (optional) --------------------------------------------------------------------------------------- 22 | download: | 23 | import json 24 | 25 | from tqdm import tqdm 26 | from utils.general import download, Path 27 | 28 | 29 | def argoverse2yolo(set): 30 | labels = {} 31 | a = json.load(open(set, "rb")) 32 | for annot in tqdm(a['annotations'], desc=f"Converting {set} to YOLOv3 format..."): 33 | img_id = annot['image_id'] 34 | img_name = a['images'][img_id]['name'] 35 | img_label_name = img_name[:-3] + "txt" 36 | 37 | cls = annot['category_id'] # instance class id 38 | x_center, y_center, width, height = annot['bbox'] 39 | x_center = (x_center + width / 2) / 1920.0 # offset and scale 40 | y_center = (y_center + height / 2) / 1200.0 # offset and scale 41 | width /= 1920.0 # scale 42 | height /= 1200.0 # scale 43 | 44 | img_dir = set.parents[2] / 'Argoverse-1.1' / 'labels' / a['seq_dirs'][a['images'][annot['image_id']]['sid']] 45 | if not img_dir.exists(): 46 | img_dir.mkdir(parents=True, exist_ok=True) 47 | 48 | k = str(img_dir / img_label_name) 49 | if k not in labels: 50 | labels[k] = [] 51 | labels[k].append(f"{cls} {x_center} {y_center} {width} {height}\n") 52 | 53 | for k in labels: 54 | with open(k, "w") as f: 55 | f.writelines(labels[k]) 56 | 57 | 58 | # Download 59 | dir = Path('../datasets/Argoverse') # dataset root dir 60 | urls = ['https://argoverse-hd.s3.us-east-2.amazonaws.com/Argoverse-HD-Full.zip'] 61 | download(urls, dir=dir, delete=False) 62 | 63 | # Convert 64 | annotations_dir = 'Argoverse-HD/annotations/' 65 | (dir / 'Argoverse-1.1' / 'tracking').rename(dir / 'Argoverse-1.1' / 'images') # rename 'tracking' to 'images' 66 | for d in "train.json", "val.json": 67 | argoverse2yolo(dir / annotations_dir / d) # convert VisDrone annotations to YOLO labels 68 | -------------------------------------------------------------------------------- /data/VisDrone.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | # VisDrone2019-DET dataset https://github.com/VisDrone/VisDrone-Dataset 3 | # Example usage: python train.py --data VisDrone.yaml 4 | # parent 5 | # ├── yolov3 6 | # └── datasets 7 | # └── VisDrone ← downloads here 8 | 9 | 10 | # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] 11 | path: ../datasets/VisDrone # dataset root dir 12 | train: VisDrone2019-DET-train/images # train images (relative to 'path') 6471 images 13 | val: VisDrone2019-DET-val/images # val images (relative to 'path') 548 images 14 | test: VisDrone2019-DET-test-dev/images # test images (optional) 1610 images 15 | 16 | # Classes 17 | nc: 10 # number of classes 18 | names: ['pedestrian', 'people', 'bicycle', 'car', 'van', 'truck', 'tricycle', 'awning-tricycle', 'bus', 'motor'] 19 | 20 | 21 | # Download script/URL (optional) --------------------------------------------------------------------------------------- 22 | download: | 23 | from utils.general import download, os, Path 24 | 25 | def visdrone2yolo(dir): 26 | from PIL import Image 27 | from tqdm import tqdm 28 | 29 | def convert_box(size, box): 30 | # Convert VisDrone box to YOLO xywh box 31 | dw = 1. / size[0] 32 | dh = 1. / size[1] 33 | return (box[0] + box[2] / 2) * dw, (box[1] + box[3] / 2) * dh, box[2] * dw, box[3] * dh 34 | 35 | (dir / 'labels').mkdir(parents=True, exist_ok=True) # make labels directory 36 | pbar = tqdm((dir / 'annotations').glob('*.txt'), desc=f'Converting {dir}') 37 | for f in pbar: 38 | img_size = Image.open((dir / 'images' / f.name).with_suffix('.jpg')).size 39 | lines = [] 40 | with open(f, 'r') as file: # read annotation.txt 41 | for row in [x.split(',') for x in file.read().strip().splitlines()]: 42 | if row[4] == '0': # VisDrone 'ignored regions' class 0 43 | continue 44 | cls = int(row[5]) - 1 45 | box = convert_box(img_size, tuple(map(int, row[:4]))) 46 | lines.append(f"{cls} {' '.join(f'{x:.6f}' for x in box)}\n") 47 | with open(str(f).replace(os.sep + 'annotations' + os.sep, os.sep + 'labels' + os.sep), 'w') as fl: 48 | fl.writelines(lines) # write label.txt 49 | 50 | 51 | # Download 52 | dir = Path(yaml['path']) # dataset root dir 53 | urls = ['https://github.com/ultralytics/yolov5/releases/download/v1.0/VisDrone2019-DET-train.zip', 54 | 'https://github.com/ultralytics/yolov5/releases/download/v1.0/VisDrone2019-DET-val.zip', 55 | 'https://github.com/ultralytics/yolov5/releases/download/v1.0/VisDrone2019-DET-test-dev.zip', 56 | 'https://github.com/ultralytics/yolov5/releases/download/v1.0/VisDrone2019-DET-test-challenge.zip'] 57 | download(urls, dir=dir) 58 | 59 | # Convert 60 | for d in 'VisDrone2019-DET-train', 'VisDrone2019-DET-val', 'VisDrone2019-DET-test-dev': 61 | visdrone2yolo(dir / d) # convert VisDrone annotations to YOLO labels 62 | -------------------------------------------------------------------------------- /utils/loggers/wandb/sweep.yaml: -------------------------------------------------------------------------------- 1 | # Hyperparameters for training 2 | # To set range- 3 | # Provide min and max values as: 4 | # parameter: 5 | # 6 | # min: scalar 7 | # max: scalar 8 | # OR 9 | # 10 | # Set a specific list of search space- 11 | # parameter: 12 | # values: [scalar1, scalar2, scalar3...] 13 | # 14 | # You can use grid, bayesian and hyperopt search strategy 15 | # For more info on configuring sweeps visit - https://docs.wandb.ai/guides/sweeps/configuration 16 | 17 | program: utils/loggers/wandb/sweep.py 18 | method: random 19 | metric: 20 | name: metrics/mAP_0.5 21 | goal: maximize 22 | 23 | parameters: 24 | # hyperparameters: set either min, max range or values list 25 | data: 26 | value: "data/coco128.yaml" 27 | batch_size: 28 | values: [64] 29 | epochs: 30 | values: [10] 31 | 32 | lr0: 33 | distribution: uniform 34 | min: 1e-5 35 | max: 1e-1 36 | lrf: 37 | distribution: uniform 38 | min: 0.01 39 | max: 1.0 40 | momentum: 41 | distribution: uniform 42 | min: 0.6 43 | max: 0.98 44 | weight_decay: 45 | distribution: uniform 46 | min: 0.0 47 | max: 0.001 48 | warmup_epochs: 49 | distribution: uniform 50 | min: 0.0 51 | max: 5.0 52 | warmup_momentum: 53 | distribution: uniform 54 | min: 0.0 55 | max: 0.95 56 | warmup_bias_lr: 57 | distribution: uniform 58 | min: 0.0 59 | max: 0.2 60 | box: 61 | distribution: uniform 62 | min: 0.02 63 | max: 0.2 64 | cls: 65 | distribution: uniform 66 | min: 0.2 67 | max: 4.0 68 | cls_pw: 69 | distribution: uniform 70 | min: 0.5 71 | max: 2.0 72 | obj: 73 | distribution: uniform 74 | min: 0.2 75 | max: 4.0 76 | obj_pw: 77 | distribution: uniform 78 | min: 0.5 79 | max: 2.0 80 | iou_t: 81 | distribution: uniform 82 | min: 0.1 83 | max: 0.7 84 | anchor_t: 85 | distribution: uniform 86 | min: 2.0 87 | max: 8.0 88 | fl_gamma: 89 | distribution: uniform 90 | min: 0.0 91 | max: 0.1 92 | hsv_h: 93 | distribution: uniform 94 | min: 0.0 95 | max: 0.1 96 | hsv_s: 97 | distribution: uniform 98 | min: 0.0 99 | max: 0.9 100 | hsv_v: 101 | distribution: uniform 102 | min: 0.0 103 | max: 0.9 104 | degrees: 105 | distribution: uniform 106 | min: 0.0 107 | max: 45.0 108 | translate: 109 | distribution: uniform 110 | min: 0.0 111 | max: 0.9 112 | scale: 113 | distribution: uniform 114 | min: 0.0 115 | max: 0.9 116 | shear: 117 | distribution: uniform 118 | min: 0.0 119 | max: 10.0 120 | perspective: 121 | distribution: uniform 122 | min: 0.0 123 | max: 0.001 124 | flipud: 125 | distribution: uniform 126 | min: 0.0 127 | max: 1.0 128 | fliplr: 129 | distribution: uniform 130 | min: 0.0 131 | max: 1.0 132 | mosaic: 133 | distribution: uniform 134 | min: 0.0 135 | max: 1.0 136 | mixup: 137 | distribution: uniform 138 | min: 0.0 139 | max: 1.0 140 | copy_paste: 141 | distribution: uniform 142 | min: 0.0 143 | max: 1.0 144 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: 🐛 Bug Report 2 | # title: " " 3 | description: Problems with YOLOv3 4 | labels: [bug, triage] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thank you for submitting a YOLOv3 🐛 Bug Report! 10 | 11 | - type: checkboxes 12 | attributes: 13 | label: Search before asking 14 | description: > 15 | Please search the [issues](https://github.com/ultralytics/yolov3/issues) to see if a similar bug report already exists. 16 | options: 17 | - label: > 18 | I have searched the YOLOv3 [issues](https://github.com/ultralytics/yolov3/issues) and found no similar bug report. 19 | required: true 20 | 21 | - type: dropdown 22 | attributes: 23 | label: YOLOv3 Component 24 | description: | 25 | Please select the part of YOLOv3 where you found the bug. 26 | multiple: true 27 | options: 28 | - "Training" 29 | - "Validation" 30 | - "Detection" 31 | - "Export" 32 | - "PyTorch Hub" 33 | - "Multi-GPU" 34 | - "Evolution" 35 | - "Integrations" 36 | - "Other" 37 | validations: 38 | required: false 39 | 40 | - type: textarea 41 | attributes: 42 | label: Bug 43 | description: Provide console output with error messages and/or screenshots of the bug. 44 | placeholder: | 45 | 💡 ProTip! Include as much information as possible (screenshots, logs, tracebacks etc.) to receive the most helpful response. 46 | validations: 47 | required: true 48 | 49 | - type: textarea 50 | attributes: 51 | label: Environment 52 | description: Please specify the software and hardware you used to produce the bug. 53 | placeholder: | 54 | - YOLO: YOLOv3 🚀 v6.0-67-g60e42e1 torch 1.9.0+cu111 CUDA:0 (A100-SXM4-40GB, 40536MiB) 55 | - OS: Ubuntu 20.04 56 | - Python: 3.9.0 57 | validations: 58 | required: false 59 | 60 | - type: textarea 61 | attributes: 62 | label: Minimal Reproducible Example 63 | description: > 64 | When asking a question, people will be better able to provide help if you provide code that they can easily understand and use to **reproduce** the problem. 65 | This is referred to by community members as creating a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). 66 | placeholder: | 67 | ``` 68 | # Code to reproduce your issue here 69 | ``` 70 | validations: 71 | required: false 72 | 73 | - type: textarea 74 | attributes: 75 | label: Additional 76 | description: Anything else you would like to share? 77 | 78 | - type: checkboxes 79 | attributes: 80 | label: Are you willing to submit a PR? 81 | description: > 82 | (Optional) We encourage you to submit a [Pull Request](https://github.com/ultralytics/yolov3/pulls) (PR) to help improve YOLOv3 for everyone, especially if you have a good understanding of how to implement a fix or feature. 83 | See the YOLOv3 [Contributing Guide](https://github.com/ultralytics/yolov3/blob/master/CONTRIBUTING.md) to get started. 84 | options: 85 | - label: Yes I'd like to help by submitting a PR! 86 | -------------------------------------------------------------------------------- /data/voc.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | # PASCAL VOC dataset http://host.robots.ox.ac.uk/pascal/VOC 3 | # Example usage: python train.py --data VOC.yaml 4 | # parent 5 | # ├── yolov3 6 | # └── datasets 7 | # └── VOC ← downloads here 8 | 9 | 10 | # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] 11 | path: ../datasets/VOC 12 | train: # train images (relative to 'path') 16551 images 13 | - images/train2012 14 | - images/train2007 15 | - images/val2012 16 | - images/val2007 17 | val: # val images (relative to 'path') 4952 images 18 | - images/test2007 19 | test: # test images (optional) 20 | - images/test2007 21 | 22 | # Classes 23 | nc: 20 # number of classes 24 | names: ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 25 | 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor'] # class names 26 | 27 | 28 | # Download script/URL (optional) --------------------------------------------------------------------------------------- 29 | download: | 30 | import xml.etree.ElementTree as ET 31 | 32 | from tqdm import tqdm 33 | from utils.general import download, Path 34 | 35 | 36 | def convert_label(path, lb_path, year, image_id): 37 | def convert_box(size, box): 38 | dw, dh = 1. / size[0], 1. / size[1] 39 | x, y, w, h = (box[0] + box[1]) / 2.0 - 1, (box[2] + box[3]) / 2.0 - 1, box[1] - box[0], box[3] - box[2] 40 | return x * dw, y * dh, w * dw, h * dh 41 | 42 | in_file = open(path / f'VOC{year}/Annotations/{image_id}.xml') 43 | out_file = open(lb_path, 'w') 44 | tree = ET.parse(in_file) 45 | root = tree.getroot() 46 | size = root.find('size') 47 | w = int(size.find('width').text) 48 | h = int(size.find('height').text) 49 | 50 | for obj in root.iter('object'): 51 | cls = obj.find('name').text 52 | if cls in yaml['names'] and not int(obj.find('difficult').text) == 1: 53 | xmlbox = obj.find('bndbox') 54 | bb = convert_box((w, h), [float(xmlbox.find(x).text) for x in ('xmin', 'xmax', 'ymin', 'ymax')]) 55 | cls_id = yaml['names'].index(cls) # class id 56 | out_file.write(" ".join([str(a) for a in (cls_id, *bb)]) + '\n') 57 | 58 | 59 | # Download 60 | dir = Path(yaml['path']) # dataset root dir 61 | url = 'https://github.com/ultralytics/yolov5/releases/download/v1.0/' 62 | urls = [url + 'VOCtrainval_06-Nov-2007.zip', # 446MB, 5012 images 63 | url + 'VOCtest_06-Nov-2007.zip', # 438MB, 4953 images 64 | url + 'VOCtrainval_11-May-2012.zip'] # 1.95GB, 17126 images 65 | download(urls, dir=dir / 'images', delete=False) 66 | 67 | # Convert 68 | path = dir / f'images/VOCdevkit' 69 | for year, image_set in ('2012', 'train'), ('2012', 'val'), ('2007', 'train'), ('2007', 'val'), ('2007', 'test'): 70 | imgs_path = dir / 'images' / f'{image_set}{year}' 71 | lbs_path = dir / 'labels' / f'{image_set}{year}' 72 | imgs_path.mkdir(exist_ok=True, parents=True) 73 | lbs_path.mkdir(exist_ok=True, parents=True) 74 | 75 | image_ids = open(path / f'VOC{year}/ImageSets/Main/{image_set}.txt').read().strip().split() 76 | for id in tqdm(image_ids, desc=f'{image_set}{year}'): 77 | f = path / f'VOC{year}/JPEGImages/{id}.jpg' # old img path 78 | lb_path = (lbs_path / f.name).with_suffix('.txt') # new label path 79 | f.rename(imgs_path / f.name) # move image 80 | convert_label(path, lb_path, year, id) # convert labels to YOLO format 81 | -------------------------------------------------------------------------------- /.github/workflows/ci-testing.yml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | name: CI CPU testing 4 | 5 | on: # https://help.github.com/en/actions/reference/events-that-trigger-workflows 6 | push: 7 | branches: [ master ] 8 | pull_request: 9 | # The branches below must be a subset of the branches above 10 | branches: [ master ] 11 | schedule: 12 | - cron: '0 0 * * *' # Runs at 00:00 UTC every day 13 | 14 | jobs: 15 | cpu-tests: 16 | 17 | runs-on: ${{ matrix.os }} 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: [ ubuntu-latest, windows-latest ] 22 | python-version: [ 3.9 ] 23 | model: [ 'yolov3-tiny' ] # models to test 24 | 25 | # Timeout: https://stackoverflow.com/a/59076067/4521646 26 | timeout-minutes: 50 27 | steps: 28 | - uses: actions/checkout@v3 29 | - name: Set up Python ${{ matrix.python-version }} 30 | uses: actions/setup-python@v4 31 | with: 32 | python-version: ${{ matrix.python-version }} 33 | 34 | # Note: This uses an internal pip API and may not always work 35 | # https://github.com/actions/cache/blob/master/examples.md#multiple-oss-in-a-workflow 36 | - name: Get pip cache 37 | id: pip-cache 38 | run: | 39 | python -c "from pip._internal.locations import USER_CACHE_DIR; print('::set-output name=dir::' + USER_CACHE_DIR)" 40 | 41 | - name: Cache pip 42 | uses: actions/cache@v3 43 | with: 44 | path: ${{ steps.pip-cache.outputs.dir }} 45 | key: ${{ runner.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('requirements.txt') }} 46 | restore-keys: | 47 | ${{ runner.os }}-${{ matrix.python-version }}-pip- 48 | 49 | # Known Keras 2.7.0 issue: https://github.com/ultralytics/yolov5/pull/5486 50 | - name: Install dependencies 51 | run: | 52 | python -m pip install --upgrade pip 53 | pip install -qr requirements.txt -f https://download.pytorch.org/whl/cpu/torch_stable.html 54 | pip install -q onnx tensorflow-cpu keras==2.6.0 # wandb # extras 55 | python --version 56 | pip --version 57 | pip list 58 | shell: bash 59 | 60 | # - name: W&B login 61 | # run: wandb login 345011b3fb26dc8337fd9b20e53857c1d403f2aa 62 | 63 | - name: Download data 64 | run: | 65 | # curl -L -o tmp.zip https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128.zip 66 | # unzip -q tmp.zip -d ../ 67 | # rm tmp.zip 68 | 69 | - name: Tests workflow 70 | run: | 71 | # export PYTHONPATH="$PWD" # to run '$ python *.py' files in subdirectories 72 | di=cpu # device 73 | 74 | # Train 75 | python train.py --img 64 --batch 32 --weights ${{ matrix.model }}.pt --cfg ${{ matrix.model }}.yaml --epochs 1 --device $di 76 | # Val 77 | python val.py --img 64 --batch 32 --weights ${{ matrix.model }}.pt --device $di 78 | python val.py --img 64 --batch 32 --weights runs/train/exp/weights/last.pt --device $di 79 | # Detect 80 | python detect.py --weights ${{ matrix.model }}.pt --device $di 81 | python detect.py --weights runs/train/exp/weights/last.pt --device $di 82 | python hubconf.py # hub 83 | # Export 84 | python models/yolo.py --cfg ${{ matrix.model }}.yaml # build PyTorch model 85 | # python models/tf.py --weights ${{ matrix.model }}.pt # build TensorFlow model (YOLOv3 not supported) 86 | python export.py --img 64 --batch 1 --weights runs/train/exp/weights/last.pt --include torchscript onnx # export 87 | # Python 88 | python - <. 66 | """ 67 | 68 | def __init__(self, c1): 69 | super().__init__() 70 | self.p1 = nn.Parameter(torch.randn(1, c1, 1, 1)) 71 | self.p2 = nn.Parameter(torch.randn(1, c1, 1, 1)) 72 | self.beta = nn.Parameter(torch.ones(1, c1, 1, 1)) 73 | 74 | def forward(self, x): 75 | dpx = (self.p1 - self.p2) * x 76 | return dpx * torch.sigmoid(self.beta * dpx) + self.p2 * x 77 | 78 | 79 | class MetaAconC(nn.Module): 80 | r""" ACON activation (activate or not). 81 | MetaAconC: (p1*x-p2*x) * sigmoid(beta*(p1*x-p2*x)) + p2*x, beta is generated by a small network 82 | according to "Activate or Not: Learning Customized Activation" . 83 | """ 84 | 85 | def __init__(self, c1, k=1, s=1, r=16): # ch_in, kernel, stride, r 86 | super().__init__() 87 | c2 = max(r, c1 // r) 88 | self.p1 = nn.Parameter(torch.randn(1, c1, 1, 1)) 89 | self.p2 = nn.Parameter(torch.randn(1, c1, 1, 1)) 90 | self.fc1 = nn.Conv2d(c1, c2, k, s, bias=True) 91 | self.fc2 = nn.Conv2d(c2, c1, k, s, bias=True) 92 | # self.bn1 = nn.BatchNorm2d(c2) 93 | # self.bn2 = nn.BatchNorm2d(c1) 94 | 95 | def forward(self, x): 96 | y = x.mean(dim=2, keepdims=True).mean(dim=3, keepdims=True) 97 | # batch-size 1 bug/instabilities https://github.com/ultralytics/yolov5/issues/2891 98 | # beta = torch.sigmoid(self.bn2(self.fc2(self.bn1(self.fc1(y))))) # bug/unstable 99 | beta = torch.sigmoid(self.fc2(self.fc1(y))) # bug patch BN layers removed 100 | dpx = (self.p1 - self.p2) * x 101 | return dpx * torch.sigmoid(beta * dpx) + self.p2 * x 102 | -------------------------------------------------------------------------------- /.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*.csv 12 | *.jpg 13 | 14 | # Neural Network weights ----------------------------------------------------------------------------------------------- 15 | **/*.pt 16 | **/*.pth 17 | **/*.onnx 18 | **/*.mlmodel 19 | **/*.torchscript 20 | **/*.torchscript.pt 21 | **/*.tflite 22 | **/*.h5 23 | **/*.pb 24 | *_saved_model/ 25 | *_web_model/ 26 | 27 | # Below Copied From .gitignore ----------------------------------------------------------------------------------------- 28 | # Below Copied From .gitignore ----------------------------------------------------------------------------------------- 29 | 30 | 31 | # GitHub Python GitIgnore ---------------------------------------------------------------------------------------------- 32 | # Byte-compiled / optimized / DLL files 33 | __pycache__/ 34 | *.py[cod] 35 | *$py.class 36 | 37 | # C extensions 38 | *.so 39 | 40 | # Distribution / packaging 41 | .Python 42 | env/ 43 | build/ 44 | develop-eggs/ 45 | dist/ 46 | downloads/ 47 | eggs/ 48 | .eggs/ 49 | lib/ 50 | lib64/ 51 | parts/ 52 | sdist/ 53 | var/ 54 | wheels/ 55 | *.egg-info/ 56 | wandb/ 57 | .installed.cfg 58 | *.egg 59 | 60 | # PyInstaller 61 | # Usually these files are written by a python script from a template 62 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 63 | *.manifest 64 | *.spec 65 | 66 | # Installer logs 67 | pip-log.txt 68 | pip-delete-this-directory.txt 69 | 70 | # Unit test / coverage reports 71 | htmlcov/ 72 | .tox/ 73 | .coverage 74 | .coverage.* 75 | .cache 76 | nosetests.xml 77 | coverage.xml 78 | *.cover 79 | .hypothesis/ 80 | 81 | # Translations 82 | *.mo 83 | *.pot 84 | 85 | # Django stuff: 86 | *.log 87 | local_settings.py 88 | 89 | # Flask stuff: 90 | instance/ 91 | .webassets-cache 92 | 93 | # Scrapy stuff: 94 | .scrapy 95 | 96 | # Sphinx documentation 97 | docs/_build/ 98 | 99 | # PyBuilder 100 | target/ 101 | 102 | # Jupyter Notebook 103 | .ipynb_checkpoints 104 | 105 | # pyenv 106 | .python-version 107 | 108 | # celery beat schedule file 109 | celerybeat-schedule 110 | 111 | # SageMath parsed files 112 | *.sage.py 113 | 114 | # dotenv 115 | .env 116 | 117 | # virtualenv 118 | .venv* 119 | venv*/ 120 | ENV*/ 121 | 122 | # Spyder project settings 123 | .spyderproject 124 | .spyproject 125 | 126 | # Rope project settings 127 | .ropeproject 128 | 129 | # mkdocs documentation 130 | /site 131 | 132 | # mypy 133 | .mypy_cache/ 134 | 135 | 136 | # https://github.com/github/gitignore/blob/master/Global/macOS.gitignore ----------------------------------------------- 137 | 138 | # General 139 | .DS_Store 140 | .AppleDouble 141 | .LSOverride 142 | 143 | # Icon must end with two \r 144 | Icon 145 | Icon? 146 | 147 | # Thumbnails 148 | ._* 149 | 150 | # Files that might appear in the root of a volume 151 | .DocumentRevisions-V100 152 | .fseventsd 153 | .Spotlight-V100 154 | .TemporaryItems 155 | .Trashes 156 | .VolumeIcon.icns 157 | .com.apple.timemachine.donotpresent 158 | 159 | # Directories potentially created on remote AFP share 160 | .AppleDB 161 | .AppleDesktop 162 | Network Trash Folder 163 | Temporary Items 164 | .apdisk 165 | 166 | 167 | # https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore 168 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 169 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 170 | 171 | # User-specific stuff: 172 | .idea/* 173 | .idea/**/workspace.xml 174 | .idea/**/tasks.xml 175 | .idea/dictionaries 176 | .html # Bokeh Plots 177 | .pg # TensorFlow Frozen Graphs 178 | .avi # videos 179 | 180 | # Sensitive or high-churn files: 181 | .idea/**/dataSources/ 182 | .idea/**/dataSources.ids 183 | .idea/**/dataSources.local.xml 184 | .idea/**/sqlDataSources.xml 185 | .idea/**/dynamic.xml 186 | .idea/**/uiDesigner.xml 187 | 188 | # Gradle: 189 | .idea/**/gradle.xml 190 | .idea/**/libraries 191 | 192 | # CMake 193 | cmake-build-debug/ 194 | cmake-build-release/ 195 | 196 | # Mongo Explorer plugin: 197 | .idea/**/mongoSettings.xml 198 | 199 | ## File-based project format: 200 | *.iws 201 | 202 | ## Plugin-specific files: 203 | 204 | # IntelliJ 205 | out/ 206 | 207 | # mpeltonen/sbt-idea plugin 208 | .idea_modules/ 209 | 210 | # JIRA plugin 211 | atlassian-ide-plugin.xml 212 | 213 | # Cursive Clojure plugin 214 | .idea/replstate.xml 215 | 216 | # Crashlytics plugin (for Android Studio and IntelliJ) 217 | com_crashlytics_export_strings.xml 218 | crashlytics.properties 219 | crashlytics-build.properties 220 | fabric.properties 221 | -------------------------------------------------------------------------------- /hubconf.py: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | PyTorch Hub models https://pytorch.org/hub/ultralytics_yolov5/ 4 | 5 | Usage: 6 | import torch 7 | model = torch.hub.load('ultralytics/yolov3', 'yolov3') 8 | """ 9 | 10 | import torch 11 | 12 | 13 | def _create(name, pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 14 | """Creates a specified model 15 | 16 | Arguments: 17 | name (str): name of model, i.e. 'yolov3' 18 | pretrained (bool): load pretrained weights into the model 19 | channels (int): number of input channels 20 | classes (int): number of model classes 21 | autoshape (bool): apply .autoshape() wrapper to model 22 | verbose (bool): print all information to screen 23 | device (str, torch.device, None): device to use for model parameters 24 | 25 | Returns: 26 | pytorch model 27 | """ 28 | from pathlib import Path 29 | 30 | from models.experimental import attempt_load 31 | from models.yolo import Model 32 | from utils.downloads import attempt_download 33 | from utils.general import check_requirements, intersect_dicts, set_logging 34 | from utils.torch_utils import select_device 35 | 36 | file = Path(__file__).resolve() 37 | check_requirements(exclude=('tensorboard', 'thop', 'opencv-python')) 38 | set_logging(verbose=verbose) 39 | 40 | save_dir = Path('') if str(name).endswith('.pt') else file.parent 41 | path = (save_dir / name).with_suffix('.pt') # checkpoint path 42 | try: 43 | device = select_device(('0' if torch.cuda.is_available() else 'cpu') if device is None else device) 44 | 45 | if pretrained and channels == 3 and classes == 80: 46 | model = attempt_load(path, map_location=device) # download/load FP32 model 47 | else: 48 | cfg = list((Path(__file__).parent / 'models').rglob(f'{name}.yaml'))[0] # model.yaml path 49 | model = Model(cfg, channels, classes) # create model 50 | if pretrained: 51 | ckpt = torch.load(attempt_download(path), map_location=device) # load 52 | csd = ckpt['model'].float().state_dict() # checkpoint state_dict as FP32 53 | csd = intersect_dicts(csd, model.state_dict(), exclude=['anchors']) # intersect 54 | model.load_state_dict(csd, strict=False) # load 55 | if len(ckpt['model'].names) == classes: 56 | model.names = ckpt['model'].names # set class names attribute 57 | if autoshape: 58 | model = model.autoshape() # for file/URI/PIL/cv2/np inputs and NMS 59 | return model.to(device) 60 | 61 | except Exception as e: 62 | help_url = 'https://github.com/ultralytics/yolov5/issues/36' 63 | s = 'Cache may be out of date, try `force_reload=True`. See %s for help.' % help_url 64 | raise Exception(s) from e 65 | 66 | 67 | def custom(path='path/to/model.pt', autoshape=True, verbose=True, device=None): 68 | # custom or local model 69 | return _create(path, autoshape=autoshape, verbose=verbose, device=device) 70 | 71 | 72 | def yolov3(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 73 | # YOLOv3 model https://github.com/ultralytics/yolov3 74 | return _create('yolov3', pretrained, channels, classes, autoshape, verbose, device) 75 | 76 | 77 | def yolov3_spp(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 78 | # YOLOv3-SPP model https://github.com/ultralytics/yolov3 79 | return _create('yolov3-spp', pretrained, channels, classes, autoshape, verbose, device) 80 | 81 | 82 | def yolov3_tiny(pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 83 | # YOLOv3-tiny model https://github.com/ultralytics/yolov3 84 | return _create('yolov3-tiny', pretrained, channels, classes, autoshape, verbose, device) 85 | 86 | 87 | if __name__ == '__main__': 88 | model = _create(name='yolov3-tiny', pretrained=True, channels=3, classes=80, autoshape=True, verbose=True) # pretrained 89 | # model = custom(path='path/to/model.pt') # custom 90 | 91 | # Verify inference 92 | from pathlib import Path 93 | 94 | import cv2 95 | import numpy as np 96 | from PIL import Image 97 | 98 | imgs = ['data/images/zidane.jpg', # filename 99 | Path('data/images/zidane.jpg'), # Path 100 | 'https://ultralytics.com/images/zidane.jpg', # URI 101 | cv2.imread('data/images/bus.jpg')[:, :, ::-1], # OpenCV 102 | Image.open('data/images/bus.jpg'), # PIL 103 | np.zeros((320, 640, 3))] # numpy 104 | 105 | results = model(imgs) # batched inference 106 | results.print() 107 | results.save() 108 | -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | 3 | name: Greetings 4 | 5 | on: [pull_request_target, issues] 6 | 7 | jobs: 8 | greeting: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/first-interaction@v1 12 | with: 13 | repo-token: ${{ secrets.GITHUB_TOKEN }} 14 | pr-message: | 15 | 👋 Hello @${{ github.actor }}, thank you for submitting a 🚀 PR! To allow your work to be integrated as seamlessly as possible, we advise you to: 16 | - ✅ Verify your PR is **up-to-date with upstream/master.** If your PR is behind upstream/master an automatic [GitHub actions](https://github.com/ultralytics/yolov3/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: 17 | ```bash 18 | git remote add upstream https://github.com/ultralytics/yolov3.git 19 | git fetch upstream 20 | git checkout feature # <----- replace 'feature' with local branch name 21 | git merge upstream/master 22 | git push -u origin -f 23 | ``` 24 | - ✅ Verify all Continuous Integration (CI) **checks are passing**. 25 | - ✅ 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 26 | 27 | issue-message: | 28 | 👋 Hello @${{ github.actor }}, thank you for your interest in YOLOv3 🚀! Please visit our ⭐️ [Tutorials](https://github.com/ultralytics/yolov3/wiki#tutorials) to get started, where you can find quickstart guides for simple tasks like [Custom Data Training](https://github.com/ultralytics/yolov3/wiki/Train-Custom-Data) all the way to advanced concepts like [Hyperparameter Evolution](https://github.com/ultralytics/yolov5/issues/607). 29 | 30 | If this is a 🐛 Bug Report, please provide screenshots and **minimum viable code to reproduce your issue**, otherwise we can not help you. 31 | 32 | 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/yolov3/wiki/Train-Custom-Data#visualize) if available. 33 | 34 | For business inquiries or professional support requests please visit https://ultralytics.com or email Glenn Jocher at glenn.jocher@ultralytics.com. 35 | 36 | ## Requirements 37 | 38 | [**Python>=3.6.0**](https://www.python.org/) with all [requirements.txt](https://github.com/ultralytics/yolov3/blob/master/requirements.txt) installed including [**PyTorch>=1.7**](https://pytorch.org/get-started/locally/). To get started: 39 | ```bash 40 | $ git clone https://github.com/ultralytics/yolov3 41 | $ cd yolov3 42 | $ pip install -r requirements.txt 43 | ``` 44 | 45 | ## Environments 46 | 47 | YOLOv3 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): 48 | 49 | - **Google Colab and Kaggle** notebooks with free GPU: Open In Colab Open In Kaggle 50 | - **Google Cloud** Deep Learning VM. See [GCP Quickstart Guide](https://github.com/ultralytics/yolov3/wiki/GCP-Quickstart) 51 | - **Amazon** Deep Learning AMI. See [AWS Quickstart Guide](https://github.com/ultralytics/yolov3/wiki/AWS-Quickstart) 52 | - **Docker Image**. See [Docker Quickstart Guide](https://github.com/ultralytics/yolov3/wiki/Docker-Quickstart) Docker Pulls 53 | 54 | 55 | ## Status 56 | 57 | CI CPU testing 58 | 59 | If this badge is green, all [YOLOv3 GitHub Actions](https://github.com/ultralytics/yolov3/actions) Continuous Integration (CI) tests are currently passing. CI tests verify correct operation of YOLOv3 training ([train.py](https://github.com/ultralytics/yolov3/blob/master/train.py)), validation ([val.py](https://github.com/ultralytics/yolov3/blob/master/val.py)), inference ([detect.py](https://github.com/ultralytics/yolov3/blob/master/detect.py)) and export ([export.py](https://github.com/ultralytics/yolov3/blob/master/export.py)) on MacOS, Windows, and Ubuntu every 24 hours and on every commit. 60 | -------------------------------------------------------------------------------- /models/experimental.py: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Experimental modules 4 | """ 5 | import math 6 | 7 | import numpy as np 8 | import torch 9 | import torch.nn as nn 10 | 11 | from models.common import Conv 12 | from utils.downloads import attempt_download 13 | 14 | 15 | class CrossConv(nn.Module): 16 | # Cross Convolution Downsample 17 | def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False): 18 | # ch_in, ch_out, kernel, stride, groups, expansion, shortcut 19 | super().__init__() 20 | c_ = int(c2 * e) # hidden channels 21 | self.cv1 = Conv(c1, c_, (1, k), (1, s)) 22 | self.cv2 = Conv(c_, c2, (k, 1), (s, 1), g=g) 23 | self.add = shortcut and c1 == c2 24 | 25 | def forward(self, x): 26 | return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) 27 | 28 | 29 | class Sum(nn.Module): 30 | # Weighted sum of 2 or more layers https://arxiv.org/abs/1911.09070 31 | def __init__(self, n, weight=False): # n: number of inputs 32 | super().__init__() 33 | self.weight = weight # apply weights boolean 34 | self.iter = range(n - 1) # iter object 35 | if weight: 36 | self.w = nn.Parameter(-torch.arange(1.0, n) / 2, requires_grad=True) # layer weights 37 | 38 | def forward(self, x): 39 | y = x[0] # no weight 40 | if self.weight: 41 | w = torch.sigmoid(self.w) * 2 42 | for i in self.iter: 43 | y = y + x[i + 1] * w[i] 44 | else: 45 | for i in self.iter: 46 | y = y + x[i + 1] 47 | return y 48 | 49 | 50 | class MixConv2d(nn.Module): 51 | # Mixed Depth-wise Conv https://arxiv.org/abs/1907.09595 52 | def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True): # ch_in, ch_out, kernel, stride, ch_strategy 53 | super().__init__() 54 | n = len(k) # number of convolutions 55 | if equal_ch: # equal c_ per group 56 | i = torch.linspace(0, n - 1E-6, c2).floor() # c2 indices 57 | c_ = [(i == g).sum() for g in range(n)] # intermediate channels 58 | else: # equal weight.numel() per group 59 | b = [c2] + [0] * n 60 | a = np.eye(n + 1, n, k=-1) 61 | a -= np.roll(a, 1, axis=1) 62 | a *= np.array(k) ** 2 63 | a[0] = 1 64 | c_ = np.linalg.lstsq(a, b, rcond=None)[0].round() # solve for equal weight indices, ax = b 65 | 66 | self.m = nn.ModuleList( 67 | [nn.Conv2d(c1, int(c_), k, s, k // 2, groups=math.gcd(c1, int(c_)), bias=False) for k, c_ in zip(k, c_)]) 68 | self.bn = nn.BatchNorm2d(c2) 69 | self.act = nn.SiLU() 70 | 71 | def forward(self, x): 72 | return self.act(self.bn(torch.cat([m(x) for m in self.m], 1))) 73 | 74 | 75 | class Ensemble(nn.ModuleList): 76 | # Ensemble of models 77 | def __init__(self): 78 | super().__init__() 79 | 80 | def forward(self, x, augment=False, profile=False, visualize=False): 81 | y = [] 82 | for module in self: 83 | y.append(module(x, augment, profile, visualize)[0]) 84 | # y = torch.stack(y).max(0)[0] # max ensemble 85 | # y = torch.stack(y).mean(0) # mean ensemble 86 | y = torch.cat(y, 1) # nms ensemble 87 | return y, None # inference, train output 88 | 89 | 90 | def attempt_load(weights, map_location=None, inplace=True, fuse=True): 91 | from models.yolo import Detect, Model 92 | 93 | # Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a 94 | model = Ensemble() 95 | for w in weights if isinstance(weights, list) else [weights]: 96 | ckpt = torch.load(attempt_download(w), map_location=map_location) # load 97 | ckpt = (ckpt['ema'] or ckpt['model']).float() # FP32 model 98 | model.append(ckpt.fuse().eval() if fuse else ckpt.eval()) # fused or un-fused model in eval mode 99 | 100 | # Compatibility updates 101 | for m in model.modules(): 102 | t = type(m) 103 | if t in (nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU, Detect, Model): 104 | m.inplace = inplace # torch 1.7.0 compatibility 105 | if t is Detect: 106 | if not isinstance(m.anchor_grid, list): # new Detect Layer compatibility 107 | delattr(m, 'anchor_grid') 108 | setattr(m, 'anchor_grid', [torch.zeros(1)] * m.nl) 109 | elif t is Conv: 110 | m._non_persistent_buffers_set = set() # torch 1.6.0 compatibility 111 | elif t is nn.Upsample and not hasattr(m, 'recompute_scale_factor'): 112 | m.recompute_scale_factor = None # torch 1.11.0 compatibility 113 | 114 | if len(model) == 1: 115 | return model[-1] # return model 116 | else: 117 | print(f'Ensemble created with {weights}\n') 118 | for k in ['names']: 119 | setattr(model, k, getattr(model[-1], k)) 120 | model.stride = model[torch.argmax(torch.tensor([m.stride.max() for m in model])).int()].stride # max stride 121 | return model # return ensemble 122 | -------------------------------------------------------------------------------- /.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 | *.cfg 23 | !setup.cfg 24 | !cfg/yolov3*.cfg 25 | 26 | storage.googleapis.com 27 | runs/* 28 | data/* 29 | !data/hyps/* 30 | !data/images/zidane.jpg 31 | !data/images/bus.jpg 32 | !data/*.sh 33 | 34 | results*.csv 35 | 36 | # Datasets ------------------------------------------------------------------------------------------------------------- 37 | coco/ 38 | coco128/ 39 | VOC/ 40 | 41 | # MATLAB GitIgnore ----------------------------------------------------------------------------------------------------- 42 | *.m~ 43 | *.mat 44 | !targets*.mat 45 | 46 | # Neural Network weights ----------------------------------------------------------------------------------------------- 47 | *.weights 48 | *.pt 49 | *.pb 50 | *.onnx 51 | *.mlmodel 52 | *.torchscript 53 | *.tflite 54 | *.h5 55 | *_saved_model/ 56 | *_web_model/ 57 | darknet53.conv.74 58 | yolov3-tiny.conv.15 59 | 60 | # GitHub Python GitIgnore ---------------------------------------------------------------------------------------------- 61 | # Byte-compiled / optimized / DLL files 62 | __pycache__/ 63 | *.py[cod] 64 | *$py.class 65 | 66 | # C extensions 67 | *.so 68 | 69 | # Distribution / packaging 70 | .Python 71 | env/ 72 | build/ 73 | develop-eggs/ 74 | dist/ 75 | downloads/ 76 | eggs/ 77 | .eggs/ 78 | lib/ 79 | lib64/ 80 | parts/ 81 | sdist/ 82 | var/ 83 | wheels/ 84 | *.egg-info/ 85 | /wandb/ 86 | .installed.cfg 87 | *.egg 88 | 89 | 90 | # PyInstaller 91 | # Usually these files are written by a python script from a template 92 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 93 | *.manifest 94 | *.spec 95 | 96 | # Installer logs 97 | pip-log.txt 98 | pip-delete-this-directory.txt 99 | 100 | # Unit test / coverage reports 101 | htmlcov/ 102 | .tox/ 103 | .coverage 104 | .coverage.* 105 | .cache 106 | nosetests.xml 107 | coverage.xml 108 | *.cover 109 | .hypothesis/ 110 | 111 | # Translations 112 | *.mo 113 | *.pot 114 | 115 | # Django stuff: 116 | *.log 117 | local_settings.py 118 | 119 | # Flask stuff: 120 | instance/ 121 | .webassets-cache 122 | 123 | # Scrapy stuff: 124 | .scrapy 125 | 126 | # Sphinx documentation 127 | docs/_build/ 128 | 129 | # PyBuilder 130 | target/ 131 | 132 | # Jupyter Notebook 133 | .ipynb_checkpoints 134 | 135 | # pyenv 136 | .python-version 137 | 138 | # celery beat schedule file 139 | celerybeat-schedule 140 | 141 | # SageMath parsed files 142 | *.sage.py 143 | 144 | # dotenv 145 | .env 146 | 147 | # virtualenv 148 | .venv* 149 | venv*/ 150 | ENV*/ 151 | 152 | # Spyder project settings 153 | .spyderproject 154 | .spyproject 155 | 156 | # Rope project settings 157 | .ropeproject 158 | 159 | # mkdocs documentation 160 | /site 161 | 162 | # mypy 163 | .mypy_cache/ 164 | 165 | 166 | # https://github.com/github/gitignore/blob/master/Global/macOS.gitignore ----------------------------------------------- 167 | 168 | # General 169 | .DS_Store 170 | .AppleDouble 171 | .LSOverride 172 | 173 | # Icon must end with two \r 174 | Icon 175 | Icon? 176 | 177 | # Thumbnails 178 | ._* 179 | 180 | # Files that might appear in the root of a volume 181 | .DocumentRevisions-V100 182 | .fseventsd 183 | .Spotlight-V100 184 | .TemporaryItems 185 | .Trashes 186 | .VolumeIcon.icns 187 | .com.apple.timemachine.donotpresent 188 | 189 | # Directories potentially created on remote AFP share 190 | .AppleDB 191 | .AppleDesktop 192 | Network Trash Folder 193 | Temporary Items 194 | .apdisk 195 | 196 | 197 | # https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore 198 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 199 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 200 | 201 | # User-specific stuff: 202 | .idea/* 203 | .idea/**/workspace.xml 204 | .idea/**/tasks.xml 205 | .idea/dictionaries 206 | .html # Bokeh Plots 207 | .pg # TensorFlow Frozen Graphs 208 | .avi # videos 209 | 210 | # Sensitive or high-churn files: 211 | .idea/**/dataSources/ 212 | .idea/**/dataSources.ids 213 | .idea/**/dataSources.local.xml 214 | .idea/**/sqlDataSources.xml 215 | .idea/**/dynamic.xml 216 | .idea/**/uiDesigner.xml 217 | 218 | # Gradle: 219 | .idea/**/gradle.xml 220 | .idea/**/libraries 221 | 222 | # CMake 223 | cmake-build-debug/ 224 | cmake-build-release/ 225 | 226 | # Mongo Explorer plugin: 227 | .idea/**/mongoSettings.xml 228 | 229 | ## File-based project format: 230 | *.iws 231 | 232 | ## Plugin-specific files: 233 | 234 | # IntelliJ 235 | out/ 236 | 237 | # mpeltonen/sbt-idea plugin 238 | .idea_modules/ 239 | 240 | # JIRA plugin 241 | atlassian-ide-plugin.xml 242 | 243 | # Cursive Clojure plugin 244 | .idea/replstate.xml 245 | 246 | # Crashlytics plugin (for Android Studio and IntelliJ) 247 | com_crashlytics_export_strings.xml 248 | crashlytics.properties 249 | crashlytics-build.properties 250 | fabric.properties 251 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to YOLOv3 🚀 2 | 3 | We love your input! We want to make contributing to YOLOv3 as easy and transparent as possible, whether it's: 4 | 5 | - Reporting a bug 6 | - Discussing the current state of the code 7 | - Submitting a fix 8 | - Proposing a new feature 9 | - Becoming a maintainer 10 | 11 | YOLOv3 works so well due to our combined community effort, and for every small improvement you contribute you will be 12 | helping push the frontiers of what's possible in AI 😃! 13 | 14 | ## Submitting a Pull Request (PR) 🛠️ 15 | 16 | Submitting a PR is easy! This example shows how to submit a PR for updating `requirements.txt` in 4 steps: 17 | 18 | ### 1. Select File to Update 19 | 20 | Select `requirements.txt` to update by clicking on it in GitHub. 21 |

PR_step1

22 | 23 | ### 2. Click 'Edit this file' 24 | 25 | Button is in top-right corner. 26 |

PR_step2

27 | 28 | ### 3. Make Changes 29 | 30 | Change `matplotlib` version from `3.2.2` to `3.3`. 31 |

PR_step3

32 | 33 | ### 4. Preview Changes and Submit PR 34 | 35 | Click on the **Preview changes** tab to verify your updates. At the bottom of the screen select 'Create a **new branch** 36 | for this commit', assign your branch a descriptive name such as `fix/matplotlib_version` and click the green **Propose 37 | changes** button. All done, your PR is now submitted to YOLOv3 for review and approval 😃! 38 |

PR_step4

39 | 40 | ### PR recommendations 41 | 42 | To allow your work to be integrated as seamlessly as possible, we advise you to: 43 | 44 | - ✅ Verify your PR is **up-to-date with upstream/master.** If your PR is behind upstream/master an 45 | automatic [GitHub actions](https://github.com/ultralytics/yolov3/blob/master/.github/workflows/rebase.yml) rebase may 46 | be attempted by including the /rebase command in a comment body, or by running the following code, replacing 'feature' 47 | with the name of your local branch: 48 | 49 | ```bash 50 | git remote add upstream https://github.com/ultralytics/yolov3.git 51 | git fetch upstream 52 | git checkout feature # <----- replace 'feature' with local branch name 53 | git merge upstream/master 54 | git push -u origin -f 55 | ``` 56 | 57 | - ✅ Verify all Continuous Integration (CI) **checks are passing**. 58 | - ✅ Reduce changes to the absolute **minimum** required for your bug fix or feature addition. _"It is not daily increase 59 | but daily decrease, hack away the unessential. The closer to the source, the less wastage there is."_ — Bruce Lee 60 | 61 | ## Submitting a Bug Report 🐛 62 | 63 | If you spot a problem with YOLOv3 please submit a Bug Report! 64 | 65 | For us to start investigating a possible problem we need to be able to reproduce it ourselves first. We've created a few 66 | short guidelines below to help users provide what we need in order to get started. 67 | 68 | When asking a question, people will be better able to provide help if you provide **code** that they can easily 69 | understand and use to **reproduce** the problem. This is referred to by community members as creating 70 | a [minimum reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). Your code that reproduces 71 | the problem should be: 72 | 73 | * ✅ **Minimal** – Use as little code as possible that still produces the same problem 74 | * ✅ **Complete** – Provide **all** parts someone else needs to reproduce your problem in the question itself 75 | * ✅ **Reproducible** – Test the code you're about to provide to make sure it reproduces the problem 76 | 77 | In addition to the above requirements, for [Ultralytics](https://ultralytics.com/) to provide assistance your code 78 | should be: 79 | 80 | * ✅ **Current** – Verify that your code is up-to-date with current 81 | GitHub [master](https://github.com/ultralytics/yolov3/tree/master), and if necessary `git pull` or `git clone` a new 82 | copy to ensure your problem has not already been resolved by previous commits. 83 | * ✅ **Unmodified** – Your problem must be reproducible without any modifications to the codebase in this 84 | repository. [Ultralytics](https://ultralytics.com/) does not provide support for custom code ⚠️. 85 | 86 | If you believe your problem meets all of the above criteria, please close this issue and raise a new one using the 🐛 ** 87 | Bug Report** [template](https://github.com/ultralytics/yolov3/issues/new/choose) and providing 88 | a [minimum reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) to help us better 89 | understand and diagnose your problem. 90 | 91 | ## License 92 | 93 | By contributing, you agree that your contributions will be licensed under 94 | the [GPL-3.0 license](https://choosealicense.com/licenses/gpl-3.0/) 95 | -------------------------------------------------------------------------------- /data/xView.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | # xView 2018 dataset https://challenge.xviewdataset.org 3 | # -------- DOWNLOAD DATA MANUALLY from URL above and unzip to 'datasets/xView' before running train command! -------- 4 | # Example usage: python train.py --data xView.yaml 5 | # parent 6 | # ├── yolov3 7 | # └── datasets 8 | # └── xView ← downloads here 9 | 10 | 11 | # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] 12 | path: ../datasets/xView # dataset root dir 13 | train: images/autosplit_train.txt # train images (relative to 'path') 90% of 847 train images 14 | val: images/autosplit_val.txt # train images (relative to 'path') 10% of 847 train images 15 | 16 | # Classes 17 | nc: 60 # number of classes 18 | names: ['Fixed-wing Aircraft', 'Small Aircraft', 'Cargo Plane', 'Helicopter', 'Passenger Vehicle', 'Small Car', 'Bus', 19 | 'Pickup Truck', 'Utility Truck', 'Truck', 'Cargo Truck', 'Truck w/Box', 'Truck Tractor', 'Trailer', 20 | 'Truck w/Flatbed', 'Truck w/Liquid', 'Crane Truck', 'Railway Vehicle', 'Passenger Car', 'Cargo Car', 21 | 'Flat Car', 'Tank car', 'Locomotive', 'Maritime Vessel', 'Motorboat', 'Sailboat', 'Tugboat', 'Barge', 22 | 'Fishing Vessel', 'Ferry', 'Yacht', 'Container Ship', 'Oil Tanker', 'Engineering Vehicle', 'Tower crane', 23 | 'Container Crane', 'Reach Stacker', 'Straddle Carrier', 'Mobile Crane', 'Dump Truck', 'Haul Truck', 24 | 'Scraper/Tractor', 'Front loader/Bulldozer', 'Excavator', 'Cement Mixer', 'Ground Grader', 'Hut/Tent', 'Shed', 25 | 'Building', 'Aircraft Hangar', 'Damaged Building', 'Facility', 'Construction Site', 'Vehicle Lot', 'Helipad', 26 | 'Storage Tank', 'Shipping container lot', 'Shipping Container', 'Pylon', 'Tower'] # class names 27 | 28 | 29 | # Download script/URL (optional) --------------------------------------------------------------------------------------- 30 | download: | 31 | import json 32 | import os 33 | from pathlib import Path 34 | 35 | import numpy as np 36 | from PIL import Image 37 | from tqdm import tqdm 38 | 39 | from utils.datasets import autosplit 40 | from utils.general import download, xyxy2xywhn 41 | 42 | 43 | def convert_labels(fname=Path('xView/xView_train.geojson')): 44 | # Convert xView geoJSON labels to YOLO format 45 | path = fname.parent 46 | with open(fname) as f: 47 | print(f'Loading {fname}...') 48 | data = json.load(f) 49 | 50 | # Make dirs 51 | labels = Path(path / 'labels' / 'train') 52 | os.system(f'rm -rf {labels}') 53 | labels.mkdir(parents=True, exist_ok=True) 54 | 55 | # xView classes 11-94 to 0-59 56 | xview_class2index = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, 8, -1, 9, 10, 11, 57 | 12, 13, 14, 15, -1, -1, 16, 17, 18, 19, 20, 21, 22, -1, 23, 24, 25, -1, 26, 27, -1, 28, -1, 58 | 29, 30, 31, 32, 33, 34, 35, 36, 37, -1, 38, 39, 40, 41, 42, 43, 44, 45, -1, -1, -1, -1, 46, 59 | 47, 48, 49, -1, 50, 51, -1, 52, -1, -1, -1, 53, 54, -1, 55, -1, -1, 56, -1, 57, -1, 58, 59] 60 | 61 | shapes = {} 62 | for feature in tqdm(data['features'], desc=f'Converting {fname}'): 63 | p = feature['properties'] 64 | if p['bounds_imcoords']: 65 | id = p['image_id'] 66 | file = path / 'train_images' / id 67 | if file.exists(): # 1395.tif missing 68 | try: 69 | box = np.array([int(num) for num in p['bounds_imcoords'].split(",")]) 70 | assert box.shape[0] == 4, f'incorrect box shape {box.shape[0]}' 71 | cls = p['type_id'] 72 | cls = xview_class2index[int(cls)] # xView class to 0-60 73 | assert 59 >= cls >= 0, f'incorrect class index {cls}' 74 | 75 | # Write YOLO label 76 | if id not in shapes: 77 | shapes[id] = Image.open(file).size 78 | box = xyxy2xywhn(box[None].astype(np.float), w=shapes[id][0], h=shapes[id][1], clip=True) 79 | with open((labels / id).with_suffix('.txt'), 'a') as f: 80 | f.write(f"{cls} {' '.join(f'{x:.6f}' for x in box[0])}\n") # write label.txt 81 | except Exception as e: 82 | print(f'WARNING: skipping one label for {file}: {e}') 83 | 84 | 85 | # Download manually from https://challenge.xviewdataset.org 86 | dir = Path(yaml['path']) # dataset root dir 87 | # urls = ['https://d307kc0mrhucc3.cloudfront.net/train_labels.zip', # train labels 88 | # 'https://d307kc0mrhucc3.cloudfront.net/train_images.zip', # 15G, 847 train images 89 | # 'https://d307kc0mrhucc3.cloudfront.net/val_images.zip'] # 5G, 282 val images (no labels) 90 | # download(urls, dir=dir, delete=False) 91 | 92 | # Convert labels 93 | convert_labels(dir / 'xView_train.geojson') 94 | 95 | # Move images 96 | images = Path(dir / 'images') 97 | images.mkdir(parents=True, exist_ok=True) 98 | Path(dir / 'train_images').rename(dir / 'images' / 'train') 99 | Path(dir / 'val_images').rename(dir / 'images' / 'val') 100 | 101 | # Split 102 | autosplit(dir / 'images' / 'train') 103 | -------------------------------------------------------------------------------- /utils/downloads.py: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Download utils 4 | """ 5 | 6 | import os 7 | import platform 8 | import subprocess 9 | import time 10 | import urllib 11 | from pathlib import Path 12 | from zipfile import ZipFile 13 | 14 | import requests 15 | import torch 16 | 17 | 18 | def gsutil_getsize(url=''): 19 | # gs://bucket/file size https://cloud.google.com/storage/docs/gsutil/commands/du 20 | s = subprocess.check_output(f'gsutil du {url}', shell=True).decode('utf-8') 21 | return eval(s.split(' ')[0]) if len(s) else 0 # bytes 22 | 23 | 24 | def safe_download(file, url, url2=None, min_bytes=1E0, error_msg=''): 25 | # Attempts to download file from url or url2, checks and removes incomplete downloads < min_bytes 26 | file = Path(file) 27 | assert_msg = f"Downloaded file '{file}' does not exist or size is < min_bytes={min_bytes}" 28 | try: # url1 29 | print(f'Downloading {url} to {file}...') 30 | torch.hub.download_url_to_file(url, str(file)) 31 | assert file.exists() and file.stat().st_size > min_bytes, assert_msg # check 32 | except Exception as e: # url2 33 | file.unlink(missing_ok=True) # remove partial downloads 34 | print(f'ERROR: {e}\nRe-attempting {url2 or url} to {file}...') 35 | os.system(f"curl -L '{url2 or url}' -o '{file}' --retry 3 -C -") # curl download, retry and resume on fail 36 | finally: 37 | if not file.exists() or file.stat().st_size < min_bytes: # check 38 | file.unlink(missing_ok=True) # remove partial downloads 39 | print(f"ERROR: {assert_msg}\n{error_msg}") 40 | print('') 41 | 42 | 43 | def attempt_download(file, repo='ultralytics/yolov3'): # from utils.downloads import *; attempt_download() 44 | # Attempt file download if does not exist 45 | file = Path(str(file).strip().replace("'", '')) 46 | 47 | if not file.exists(): 48 | # URL specified 49 | name = Path(urllib.parse.unquote(str(file))).name # decode '%2F' to '/' etc. 50 | if str(file).startswith(('http:/', 'https:/')): # download 51 | url = str(file).replace(':/', '://') # Pathlib turns :// -> :/ 52 | name = name.split('?')[0] # parse authentication https://url.com/file.txt?auth... 53 | safe_download(file=name, url=url, min_bytes=1E5) 54 | return name 55 | 56 | # GitHub assets 57 | file.parent.mkdir(parents=True, exist_ok=True) # make parent dir (if required) 58 | try: 59 | response = requests.get(f'https://api.github.com/repos/{repo}/releases/latest').json() # github api 60 | assets = [x['name'] for x in response['assets']] # release assets, i.e. ['yolov3.pt'...] 61 | tag = response['tag_name'] # i.e. 'v1.0' 62 | except: # fallback plan 63 | assets = ['yolov3.pt', 'yolov3-spp.pt', 'yolov3-tiny.pt'] 64 | try: 65 | tag = subprocess.check_output('git tag', shell=True, stderr=subprocess.STDOUT).decode().split()[-1] 66 | except: 67 | tag = 'v9.5.0' # current release 68 | 69 | if name in assets: 70 | safe_download(file, 71 | url=f'https://github.com/{repo}/releases/download/{tag}/{name}', 72 | # url2=f'https://storage.googleapis.com/{repo}/ckpt/{name}', # backup url (optional) 73 | min_bytes=1E5, 74 | error_msg=f'{file} missing, try downloading from https://github.com/{repo}/releases/') 75 | 76 | return str(file) 77 | 78 | 79 | def gdrive_download(id='16TiPfZj7htmTyhntwcZyEEAejOUxuT6m', file='tmp.zip'): 80 | # Downloads a file from Google Drive. from yolov3.utils.downloads import *; gdrive_download() 81 | t = time.time() 82 | file = Path(file) 83 | cookie = Path('cookie') # gdrive cookie 84 | print(f'Downloading https://drive.google.com/uc?export=download&id={id} as {file}... ', end='') 85 | file.unlink(missing_ok=True) # remove existing file 86 | cookie.unlink(missing_ok=True) # remove existing cookie 87 | 88 | # Attempt file download 89 | out = "NUL" if platform.system() == "Windows" else "/dev/null" 90 | os.system(f'curl -c ./cookie -s -L "drive.google.com/uc?export=download&id={id}" > {out}') 91 | if os.path.exists('cookie'): # large file 92 | s = f'curl -Lb ./cookie "drive.google.com/uc?export=download&confirm={get_token()}&id={id}" -o {file}' 93 | else: # small file 94 | s = f'curl -s -L -o {file} "drive.google.com/uc?export=download&id={id}"' 95 | r = os.system(s) # execute, capture return 96 | cookie.unlink(missing_ok=True) # remove existing cookie 97 | 98 | # Error check 99 | if r != 0: 100 | file.unlink(missing_ok=True) # remove partial 101 | print('Download error ') # raise Exception('Download error') 102 | return r 103 | 104 | # Unzip if archive 105 | if file.suffix == '.zip': 106 | print('unzipping... ', end='') 107 | ZipFile(file).extractall(path=file.parent) # unzip 108 | file.unlink() # remove zip 109 | 110 | print(f'Done ({time.time() - t:.1f}s)') 111 | return r 112 | 113 | 114 | def get_token(cookie="./cookie"): 115 | with open(cookie) as f: 116 | for line in f: 117 | if "download" in line: 118 | return line.split()[-1] 119 | return "" 120 | 121 | # Google utils: https://cloud.google.com/storage/docs/reference/libraries ---------------------------------------------- 122 | # 123 | # 124 | # def upload_blob(bucket_name, source_file_name, destination_blob_name): 125 | # # Uploads a file to a bucket 126 | # # https://cloud.google.com/storage/docs/uploading-objects#storage-upload-object-python 127 | # 128 | # storage_client = storage.Client() 129 | # bucket = storage_client.get_bucket(bucket_name) 130 | # blob = bucket.blob(destination_blob_name) 131 | # 132 | # blob.upload_from_filename(source_file_name) 133 | # 134 | # print('File {} uploaded to {}.'.format( 135 | # source_file_name, 136 | # destination_blob_name)) 137 | # 138 | # 139 | # def download_blob(bucket_name, source_blob_name, destination_file_name): 140 | # # Uploads a blob from a bucket 141 | # storage_client = storage.Client() 142 | # bucket = storage_client.get_bucket(bucket_name) 143 | # blob = bucket.blob(source_blob_name) 144 | # 145 | # blob.download_to_filename(destination_file_name) 146 | # 147 | # print('Blob {} downloaded to {}.'.format( 148 | # source_blob_name, 149 | # destination_file_name)) 150 | -------------------------------------------------------------------------------- /utils/loggers/__init__.py: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Logging utils 4 | """ 5 | 6 | import os 7 | import warnings 8 | from threading import Thread 9 | 10 | import pkg_resources as pkg 11 | import torch 12 | from torch.utils.tensorboard import SummaryWriter 13 | 14 | from utils.general import colorstr, emojis 15 | from utils.loggers.wandb.wandb_utils import WandbLogger 16 | from utils.plots import plot_images, plot_results 17 | from utils.torch_utils import de_parallel 18 | 19 | LOGGERS = ('csv', 'tb', 'wandb') # text-file, TensorBoard, Weights & Biases 20 | RANK = int(os.getenv('RANK', -1)) 21 | 22 | try: 23 | import wandb 24 | 25 | assert hasattr(wandb, '__version__') # verify package import not local dir 26 | if pkg.parse_version(wandb.__version__) >= pkg.parse_version('0.12.2') and RANK in [0, -1]: 27 | try: 28 | wandb_login_success = wandb.login(timeout=30) 29 | except wandb.errors.UsageError: # known non-TTY terminal issue 30 | wandb_login_success = False 31 | if not wandb_login_success: 32 | wandb = None 33 | except (ImportError, AssertionError): 34 | wandb = None 35 | 36 | 37 | class Loggers(): 38 | # Loggers class 39 | def __init__(self, save_dir=None, weights=None, opt=None, hyp=None, logger=None, include=LOGGERS): 40 | self.save_dir = save_dir 41 | self.weights = weights 42 | self.opt = opt 43 | self.hyp = hyp 44 | self.logger = logger # for printing results to console 45 | self.include = include 46 | self.keys = ['train/box_loss', 'train/obj_loss', 'train/cls_loss', # train loss 47 | 'metrics/precision', 'metrics/recall', 'metrics/mAP_0.5', 'metrics/mAP_0.5:0.95', # metrics 48 | 'val/box_loss', 'val/obj_loss', 'val/cls_loss', # val loss 49 | 'x/lr0', 'x/lr1', 'x/lr2'] # params 50 | for k in LOGGERS: 51 | setattr(self, k, None) # init empty logger dictionary 52 | self.csv = True # always log to csv 53 | 54 | # Message 55 | if not wandb: 56 | prefix = colorstr('Weights & Biases: ') 57 | s = f"{prefix}run 'pip install wandb' to automatically track and visualize YOLOv3 🚀 runs (RECOMMENDED)" 58 | print(emojis(s)) 59 | 60 | # TensorBoard 61 | s = self.save_dir 62 | if 'tb' in self.include and not self.opt.evolve: 63 | prefix = colorstr('TensorBoard: ') 64 | self.logger.info(f"{prefix}Start with 'tensorboard --logdir {s.parent}', view at http://localhost:6006/") 65 | self.tb = SummaryWriter(str(s)) 66 | 67 | # W&B 68 | if wandb and 'wandb' in self.include: 69 | wandb_artifact_resume = isinstance(self.opt.resume, str) and self.opt.resume.startswith('wandb-artifact://') 70 | run_id = torch.load(self.weights).get('wandb_id') if self.opt.resume and not wandb_artifact_resume else None 71 | self.opt.hyp = self.hyp # add hyperparameters 72 | self.wandb = WandbLogger(self.opt, run_id) 73 | else: 74 | self.wandb = None 75 | 76 | def on_pretrain_routine_end(self): 77 | # Callback runs on pre-train routine end 78 | paths = self.save_dir.glob('*labels*.jpg') # training labels 79 | if self.wandb: 80 | self.wandb.log({"Labels": [wandb.Image(str(x), caption=x.name) for x in paths]}) 81 | 82 | def on_train_batch_end(self, ni, model, imgs, targets, paths, plots, sync_bn): 83 | # Callback runs on train batch end 84 | if plots: 85 | if ni == 0: 86 | if not sync_bn: # tb.add_graph() --sync known issue https://github.com/ultralytics/yolov5/issues/3754 87 | with warnings.catch_warnings(): 88 | warnings.simplefilter('ignore') # suppress jit trace warning 89 | self.tb.add_graph(torch.jit.trace(de_parallel(model), imgs[0:1], strict=False), []) 90 | if ni < 3: 91 | f = self.save_dir / f'train_batch{ni}.jpg' # filename 92 | Thread(target=plot_images, args=(imgs, targets, paths, f), daemon=True).start() 93 | if self.wandb and ni == 10: 94 | files = sorted(self.save_dir.glob('train*.jpg')) 95 | self.wandb.log({'Mosaics': [wandb.Image(str(f), caption=f.name) for f in files if f.exists()]}) 96 | 97 | def on_train_epoch_end(self, epoch): 98 | # Callback runs on train epoch end 99 | if self.wandb: 100 | self.wandb.current_epoch = epoch + 1 101 | 102 | def on_val_image_end(self, pred, predn, path, names, im): 103 | # Callback runs on val image end 104 | if self.wandb: 105 | self.wandb.val_one_image(pred, predn, path, names, im) 106 | 107 | def on_val_end(self): 108 | # Callback runs on val end 109 | if self.wandb: 110 | files = sorted(self.save_dir.glob('val*.jpg')) 111 | self.wandb.log({"Validation": [wandb.Image(str(f), caption=f.name) for f in files]}) 112 | 113 | def on_fit_epoch_end(self, vals, epoch, best_fitness, fi): 114 | # Callback runs at the end of each fit (train+val) epoch 115 | x = {k: v for k, v in zip(self.keys, vals)} # dict 116 | if self.csv: 117 | file = self.save_dir / 'results.csv' 118 | n = len(x) + 1 # number of cols 119 | s = '' if file.exists() else (('%20s,' * n % tuple(['epoch'] + self.keys)).rstrip(',') + '\n') # add header 120 | with open(file, 'a') as f: 121 | f.write(s + ('%20.5g,' * n % tuple([epoch] + vals)).rstrip(',') + '\n') 122 | 123 | if self.tb: 124 | for k, v in x.items(): 125 | self.tb.add_scalar(k, v, epoch) 126 | 127 | if self.wandb: 128 | self.wandb.log(x) 129 | self.wandb.end_epoch(best_result=best_fitness == fi) 130 | 131 | def on_model_save(self, last, epoch, final_epoch, best_fitness, fi): 132 | # Callback runs on model save event 133 | if self.wandb: 134 | if ((epoch + 1) % self.opt.save_period == 0 and not final_epoch) and self.opt.save_period != -1: 135 | self.wandb.log_model(last.parent, self.opt, epoch, fi, best_model=best_fitness == fi) 136 | 137 | def on_train_end(self, last, best, plots, epoch, results): 138 | # Callback runs on training end 139 | if plots: 140 | plot_results(file=self.save_dir / 'results.csv') # save results.png 141 | files = ['results.png', 'confusion_matrix.png', *(f'{x}_curve.png' for x in ('F1', 'PR', 'P', 'R'))] 142 | files = [(self.save_dir / f) for f in files if (self.save_dir / f).exists()] # filter 143 | 144 | if self.tb: 145 | import cv2 146 | for f in files: 147 | self.tb.add_image(f.stem, cv2.imread(str(f))[..., ::-1], epoch, dataformats='HWC') 148 | 149 | if self.wandb: 150 | self.wandb.log({"Results": [wandb.Image(str(f), caption=f.name) for f in files]}) 151 | # Calling wandb.log. TODO: Refactor this into WandbLogger.log_model 152 | if not self.opt.evolve: 153 | wandb.log_artifact(str(best if best.exists() else last), type='model', 154 | name='run_' + self.wandb.wandb_run.id + '_model', 155 | aliases=['latest', 'best', 'stripped']) 156 | self.wandb.finish_run() 157 | else: 158 | self.wandb.finish_run() 159 | self.wandb = WandbLogger(self.opt) 160 | -------------------------------------------------------------------------------- /utils/autoanchor.py: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Auto-anchor utils 4 | """ 5 | 6 | import random 7 | 8 | import numpy as np 9 | import torch 10 | import yaml 11 | from tqdm import tqdm 12 | 13 | from utils.general import LOGGER, colorstr, emojis 14 | 15 | PREFIX = colorstr('AutoAnchor: ') 16 | 17 | 18 | def check_anchor_order(m): 19 | # Check anchor order against stride order for Detect() module m, and correct if necessary 20 | a = m.anchors.prod(-1).view(-1) # anchor area 21 | da = a[-1] - a[0] # delta a 22 | ds = m.stride[-1] - m.stride[0] # delta s 23 | if da.sign() != ds.sign(): # same order 24 | LOGGER.info(f'{PREFIX}Reversing anchor order') 25 | m.anchors[:] = m.anchors.flip(0) 26 | 27 | 28 | def check_anchors(dataset, model, thr=4.0, imgsz=640): 29 | # Check anchor fit to data, recompute if necessary 30 | m = model.module.model[-1] if hasattr(model, 'module') else model.model[-1] # Detect() 31 | shapes = imgsz * dataset.shapes / dataset.shapes.max(1, keepdims=True) 32 | scale = np.random.uniform(0.9, 1.1, size=(shapes.shape[0], 1)) # augment scale 33 | wh = torch.tensor(np.concatenate([l[:, 3:5] * s for s, l in zip(shapes * scale, dataset.labels)])).float() # wh 34 | 35 | def metric(k): # compute metric 36 | r = wh[:, None] / k[None] 37 | x = torch.min(r, 1 / r).min(2)[0] # ratio metric 38 | best = x.max(1)[0] # best_x 39 | aat = (x > 1 / thr).float().sum(1).mean() # anchors above threshold 40 | bpr = (best > 1 / thr).float().mean() # best possible recall 41 | return bpr, aat 42 | 43 | anchors = m.anchors.clone() * m.stride.to(m.anchors.device).view(-1, 1, 1) # current anchors 44 | bpr, aat = metric(anchors.cpu().view(-1, 2)) 45 | s = f'\n{PREFIX}{aat:.2f} anchors/target, {bpr:.3f} Best Possible Recall (BPR). ' 46 | if bpr > 0.98: # threshold to recompute 47 | LOGGER.info(emojis(f'{s}Current anchors are a good fit to dataset ✅')) 48 | else: 49 | LOGGER.info(emojis(f'{s}Anchors are a poor fit to dataset ⚠️, attempting to improve...')) 50 | na = m.anchors.numel() // 2 # number of anchors 51 | try: 52 | anchors = kmean_anchors(dataset, n=na, img_size=imgsz, thr=thr, gen=1000, verbose=False) 53 | except Exception as e: 54 | LOGGER.info(f'{PREFIX}ERROR: {e}') 55 | new_bpr = metric(anchors)[0] 56 | if new_bpr > bpr: # replace anchors 57 | anchors = torch.tensor(anchors, device=m.anchors.device).type_as(m.anchors) 58 | m.anchors[:] = anchors.clone().view_as(m.anchors) / m.stride.to(m.anchors.device).view(-1, 1, 1) # loss 59 | check_anchor_order(m) 60 | LOGGER.info(f'{PREFIX}New anchors saved to model. Update model *.yaml to use these anchors in the future.') 61 | else: 62 | LOGGER.info(f'{PREFIX}Original anchors better than new anchors. Proceeding with original anchors.') 63 | 64 | 65 | def kmean_anchors(dataset='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen=1000, verbose=True): 66 | """ Creates kmeans-evolved anchors from training dataset 67 | 68 | Arguments: 69 | dataset: path to data.yaml, or a loaded dataset 70 | n: number of anchors 71 | img_size: image size used for training 72 | thr: anchor-label wh ratio threshold hyperparameter hyp['anchor_t'] used for training, default=4.0 73 | gen: generations to evolve anchors using genetic algorithm 74 | verbose: print all results 75 | 76 | Return: 77 | k: kmeans evolved anchors 78 | 79 | Usage: 80 | from utils.autoanchor import *; _ = kmean_anchors() 81 | """ 82 | from scipy.cluster.vq import kmeans 83 | 84 | thr = 1 / thr 85 | 86 | def metric(k, wh): # compute metrics 87 | r = wh[:, None] / k[None] 88 | x = torch.min(r, 1 / r).min(2)[0] # ratio metric 89 | # x = wh_iou(wh, torch.tensor(k)) # iou metric 90 | return x, x.max(1)[0] # x, best_x 91 | 92 | def anchor_fitness(k): # mutation fitness 93 | _, best = metric(torch.tensor(k, dtype=torch.float32), wh) 94 | return (best * (best > thr).float()).mean() # fitness 95 | 96 | def print_results(k, verbose=True): 97 | k = k[np.argsort(k.prod(1))] # sort small to large 98 | x, best = metric(k, wh0) 99 | bpr, aat = (best > thr).float().mean(), (x > thr).float().mean() * n # best possible recall, anch > thr 100 | s = f'{PREFIX}thr={thr:.2f}: {bpr:.4f} best possible recall, {aat:.2f} anchors past thr\n' \ 101 | f'{PREFIX}n={n}, img_size={img_size}, metric_all={x.mean():.3f}/{best.mean():.3f}-mean/best, ' \ 102 | f'past_thr={x[x > thr].mean():.3f}-mean: ' 103 | for i, x in enumerate(k): 104 | s += '%i,%i, ' % (round(x[0]), round(x[1])) 105 | if verbose: 106 | LOGGER.info(s[:-2]) 107 | return k 108 | 109 | if isinstance(dataset, str): # *.yaml file 110 | with open(dataset, errors='ignore') as f: 111 | data_dict = yaml.safe_load(f) # model dict 112 | from utils.datasets import LoadImagesAndLabels 113 | dataset = LoadImagesAndLabels(data_dict['train'], augment=True, rect=True) 114 | 115 | # Get label wh 116 | shapes = img_size * dataset.shapes / dataset.shapes.max(1, keepdims=True) 117 | wh0 = np.concatenate([l[:, 3:5] * s for s, l in zip(shapes, dataset.labels)]) # wh 118 | 119 | # Filter 120 | i = (wh0 < 3.0).any(1).sum() 121 | if i: 122 | LOGGER.info(f'{PREFIX}WARNING: Extremely small objects found. {i} of {len(wh0)} labels are < 3 pixels in size.') 123 | wh = wh0[(wh0 >= 2.0).any(1)] # filter > 2 pixels 124 | # wh = wh * (np.random.rand(wh.shape[0], 1) * 0.9 + 0.1) # multiply by random scale 0-1 125 | 126 | # Kmeans calculation 127 | LOGGER.info(f'{PREFIX}Running kmeans for {n} anchors on {len(wh)} points...') 128 | s = wh.std(0) # sigmas for whitening 129 | k, dist = kmeans(wh / s, n, iter=30) # points, mean distance 130 | assert len(k) == n, f'{PREFIX}ERROR: scipy.cluster.vq.kmeans requested {n} points but returned only {len(k)}' 131 | k *= s 132 | wh = torch.tensor(wh, dtype=torch.float32) # filtered 133 | wh0 = torch.tensor(wh0, dtype=torch.float32) # unfiltered 134 | k = print_results(k, verbose=False) 135 | 136 | # Plot 137 | # k, d = [None] * 20, [None] * 20 138 | # for i in tqdm(range(1, 21)): 139 | # k[i-1], d[i-1] = kmeans(wh / s, i) # points, mean distance 140 | # fig, ax = plt.subplots(1, 2, figsize=(14, 7), tight_layout=True) 141 | # ax = ax.ravel() 142 | # ax[0].plot(np.arange(1, 21), np.array(d) ** 2, marker='.') 143 | # fig, ax = plt.subplots(1, 2, figsize=(14, 7)) # plot wh 144 | # ax[0].hist(wh[wh[:, 0]<100, 0],400) 145 | # ax[1].hist(wh[wh[:, 1]<100, 1],400) 146 | # fig.savefig('wh.png', dpi=200) 147 | 148 | # Evolve 149 | npr = np.random 150 | f, sh, mp, s = anchor_fitness(k), k.shape, 0.9, 0.1 # fitness, generations, mutation prob, sigma 151 | pbar = tqdm(range(gen), desc=f'{PREFIX}Evolving anchors with Genetic Algorithm:') # progress bar 152 | for _ in pbar: 153 | v = np.ones(sh) 154 | while (v == 1).all(): # mutate until a change occurs (prevent duplicates) 155 | v = ((npr.random(sh) < mp) * random.random() * npr.randn(*sh) * s + 1).clip(0.3, 3.0) 156 | kg = (k.copy() * v).clip(min=2.0) 157 | fg = anchor_fitness(kg) 158 | if fg > f: 159 | f, k = fg, kg.copy() 160 | pbar.desc = f'{PREFIX}Evolving anchors with Genetic Algorithm: fitness = {f:.4f}' 161 | if verbose: 162 | print_results(k, verbose) 163 | 164 | return print_results(k) 165 | -------------------------------------------------------------------------------- /data/objects365.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | # Objects365 dataset https://www.objects365.org/ 3 | # Example usage: python train.py --data Objects365.yaml 4 | # parent 5 | # ├── yolov3 6 | # └── datasets 7 | # └── Objects365 ← downloads here 8 | 9 | 10 | # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] 11 | path: ../datasets/Objects365 # dataset root dir 12 | train: images/train # train images (relative to 'path') 1742289 images 13 | val: images/val # val images (relative to 'path') 80000 images 14 | test: # test images (optional) 15 | 16 | # Classes 17 | nc: 365 # number of classes 18 | names: ['Person', 'Sneakers', 'Chair', 'Other Shoes', 'Hat', 'Car', 'Lamp', 'Glasses', 'Bottle', 'Desk', 'Cup', 19 | 'Street Lights', 'Cabinet/shelf', 'Handbag/Satchel', 'Bracelet', 'Plate', 'Picture/Frame', 'Helmet', 'Book', 20 | 'Gloves', 'Storage box', 'Boat', 'Leather Shoes', 'Flower', 'Bench', 'Potted Plant', 'Bowl/Basin', 'Flag', 21 | 'Pillow', 'Boots', 'Vase', 'Microphone', 'Necklace', 'Ring', 'SUV', 'Wine Glass', 'Belt', 'Monitor/TV', 22 | 'Backpack', 'Umbrella', 'Traffic Light', 'Speaker', 'Watch', 'Tie', 'Trash bin Can', 'Slippers', 'Bicycle', 23 | 'Stool', 'Barrel/bucket', 'Van', 'Couch', 'Sandals', 'Basket', 'Drum', 'Pen/Pencil', 'Bus', 'Wild Bird', 24 | 'High Heels', 'Motorcycle', 'Guitar', 'Carpet', 'Cell Phone', 'Bread', 'Camera', 'Canned', 'Truck', 25 | 'Traffic cone', 'Cymbal', 'Lifesaver', 'Towel', 'Stuffed Toy', 'Candle', 'Sailboat', 'Laptop', 'Awning', 26 | 'Bed', 'Faucet', 'Tent', 'Horse', 'Mirror', 'Power outlet', 'Sink', 'Apple', 'Air Conditioner', 'Knife', 27 | 'Hockey Stick', 'Paddle', 'Pickup Truck', 'Fork', 'Traffic Sign', 'Balloon', 'Tripod', 'Dog', 'Spoon', 'Clock', 28 | 'Pot', 'Cow', 'Cake', 'Dinning Table', 'Sheep', 'Hanger', 'Blackboard/Whiteboard', 'Napkin', 'Other Fish', 29 | 'Orange/Tangerine', 'Toiletry', 'Keyboard', 'Tomato', 'Lantern', 'Machinery Vehicle', 'Fan', 30 | 'Green Vegetables', 'Banana', 'Baseball Glove', 'Airplane', 'Mouse', 'Train', 'Pumpkin', 'Soccer', 'Skiboard', 31 | 'Luggage', 'Nightstand', 'Tea pot', 'Telephone', 'Trolley', 'Head Phone', 'Sports Car', 'Stop Sign', 32 | 'Dessert', 'Scooter', 'Stroller', 'Crane', 'Remote', 'Refrigerator', 'Oven', 'Lemon', 'Duck', 'Baseball Bat', 33 | 'Surveillance Camera', 'Cat', 'Jug', 'Broccoli', 'Piano', 'Pizza', 'Elephant', 'Skateboard', 'Surfboard', 34 | 'Gun', 'Skating and Skiing shoes', 'Gas stove', 'Donut', 'Bow Tie', 'Carrot', 'Toilet', 'Kite', 'Strawberry', 35 | 'Other Balls', 'Shovel', 'Pepper', 'Computer Box', 'Toilet Paper', 'Cleaning Products', 'Chopsticks', 36 | 'Microwave', 'Pigeon', 'Baseball', 'Cutting/chopping Board', 'Coffee Table', 'Side Table', 'Scissors', 37 | 'Marker', 'Pie', 'Ladder', 'Snowboard', 'Cookies', 'Radiator', 'Fire Hydrant', 'Basketball', 'Zebra', 'Grape', 38 | 'Giraffe', 'Potato', 'Sausage', 'Tricycle', 'Violin', 'Egg', 'Fire Extinguisher', 'Candy', 'Fire Truck', 39 | 'Billiards', 'Converter', 'Bathtub', 'Wheelchair', 'Golf Club', 'Briefcase', 'Cucumber', 'Cigar/Cigarette', 40 | 'Paint Brush', 'Pear', 'Heavy Truck', 'Hamburger', 'Extractor', 'Extension Cord', 'Tong', 'Tennis Racket', 41 | 'Folder', 'American Football', 'earphone', 'Mask', 'Kettle', 'Tennis', 'Ship', 'Swing', 'Coffee Machine', 42 | 'Slide', 'Carriage', 'Onion', 'Green beans', 'Projector', 'Frisbee', 'Washing Machine/Drying Machine', 43 | 'Chicken', 'Printer', 'Watermelon', 'Saxophone', 'Tissue', 'Toothbrush', 'Ice cream', 'Hot-air balloon', 44 | 'Cello', 'French Fries', 'Scale', 'Trophy', 'Cabbage', 'Hot dog', 'Blender', 'Peach', 'Rice', 'Wallet/Purse', 45 | 'Volleyball', 'Deer', 'Goose', 'Tape', 'Tablet', 'Cosmetics', 'Trumpet', 'Pineapple', 'Golf Ball', 46 | 'Ambulance', 'Parking meter', 'Mango', 'Key', 'Hurdle', 'Fishing Rod', 'Medal', 'Flute', 'Brush', 'Penguin', 47 | 'Megaphone', 'Corn', 'Lettuce', 'Garlic', 'Swan', 'Helicopter', 'Green Onion', 'Sandwich', 'Nuts', 48 | 'Speed Limit Sign', 'Induction Cooker', 'Broom', 'Trombone', 'Plum', 'Rickshaw', 'Goldfish', 'Kiwi fruit', 49 | 'Router/modem', 'Poker Card', 'Toaster', 'Shrimp', 'Sushi', 'Cheese', 'Notepaper', 'Cherry', 'Pliers', 'CD', 50 | 'Pasta', 'Hammer', 'Cue', 'Avocado', 'Hamimelon', 'Flask', 'Mushroom', 'Screwdriver', 'Soap', 'Recorder', 51 | 'Bear', 'Eggplant', 'Board Eraser', 'Coconut', 'Tape Measure/Ruler', 'Pig', 'Showerhead', 'Globe', 'Chips', 52 | 'Steak', 'Crosswalk Sign', 'Stapler', 'Camel', 'Formula 1', 'Pomegranate', 'Dishwasher', 'Crab', 53 | 'Hoverboard', 'Meat ball', 'Rice Cooker', 'Tuba', 'Calculator', 'Papaya', 'Antelope', 'Parrot', 'Seal', 54 | 'Butterfly', 'Dumbbell', 'Donkey', 'Lion', 'Urinal', 'Dolphin', 'Electric Drill', 'Hair Dryer', 'Egg tart', 55 | 'Jellyfish', 'Treadmill', 'Lighter', 'Grapefruit', 'Game board', 'Mop', 'Radish', 'Baozi', 'Target', 'French', 56 | 'Spring Rolls', 'Monkey', 'Rabbit', 'Pencil Case', 'Yak', 'Red Cabbage', 'Binoculars', 'Asparagus', 'Barbell', 57 | 'Scallop', 'Noddles', 'Comb', 'Dumpling', 'Oyster', 'Table Tennis paddle', 'Cosmetics Brush/Eyeliner Pencil', 58 | 'Chainsaw', 'Eraser', 'Lobster', 'Durian', 'Okra', 'Lipstick', 'Cosmetics Mirror', 'Curling', 'Table Tennis'] 59 | 60 | 61 | # Download script/URL (optional) --------------------------------------------------------------------------------------- 62 | download: | 63 | from pycocotools.coco import COCO 64 | from tqdm import tqdm 65 | 66 | from utils.general import Path, download, np, xyxy2xywhn 67 | 68 | # Make Directories 69 | dir = Path(yaml['path']) # dataset root dir 70 | for p in 'images', 'labels': 71 | (dir / p).mkdir(parents=True, exist_ok=True) 72 | for q in 'train', 'val': 73 | (dir / p / q).mkdir(parents=True, exist_ok=True) 74 | 75 | # Train, Val Splits 76 | for split, patches in [('train', 50 + 1), ('val', 43 + 1)]: 77 | print(f"Processing {split} in {patches} patches ...") 78 | images, labels = dir / 'images' / split, dir / 'labels' / split 79 | 80 | # Download 81 | url = f"https://dorc.ks3-cn-beijing.ksyun.com/data-set/2020Objects365%E6%95%B0%E6%8D%AE%E9%9B%86/{split}/" 82 | if split == 'train': 83 | download([f'{url}zhiyuan_objv2_{split}.tar.gz'], dir=dir, delete=False) # annotations json 84 | download([f'{url}patch{i}.tar.gz' for i in range(patches)], dir=images, curl=True, delete=False, threads=8) 85 | elif split == 'val': 86 | download([f'{url}zhiyuan_objv2_{split}.json'], dir=dir, delete=False) # annotations json 87 | download([f'{url}images/v1/patch{i}.tar.gz' for i in range(15 + 1)], dir=images, curl=True, delete=False, threads=8) 88 | download([f'{url}images/v2/patch{i}.tar.gz' for i in range(16, patches)], dir=images, curl=True, delete=False, threads=8) 89 | 90 | # Move 91 | for f in tqdm(images.rglob('*.jpg'), desc=f'Moving {split} images'): 92 | f.rename(images / f.name) # move to /images/{split} 93 | 94 | # Labels 95 | coco = COCO(dir / f'zhiyuan_objv2_{split}.json') 96 | names = [x["name"] for x in coco.loadCats(coco.getCatIds())] 97 | for cid, cat in enumerate(names): 98 | catIds = coco.getCatIds(catNms=[cat]) 99 | imgIds = coco.getImgIds(catIds=catIds) 100 | for im in tqdm(coco.loadImgs(imgIds), desc=f'Class {cid + 1}/{len(names)} {cat}'): 101 | width, height = im["width"], im["height"] 102 | path = Path(im["file_name"]) # image filename 103 | try: 104 | with open(labels / path.with_suffix('.txt').name, 'a') as file: 105 | annIds = coco.getAnnIds(imgIds=im["id"], catIds=catIds, iscrowd=None) 106 | for a in coco.loadAnns(annIds): 107 | x, y, w, h = a['bbox'] # bounding box in xywh (xy top-left corner) 108 | xyxy = np.array([x, y, x + w, y + h])[None] # pixels(1,4) 109 | x, y, w, h = xyxy2xywhn(xyxy, w=width, h=height, clip=True)[0] # normalized and clipped 110 | file.write(f"{cid} {x:.5f} {y:.5f} {w:.5f} {h:.5f}\n") 111 | except Exception as e: 112 | print(e) 113 | -------------------------------------------------------------------------------- /utils/loggers/wandb/README.md: -------------------------------------------------------------------------------- 1 | 📚 This guide explains how to use **Weights & Biases** (W&B) with YOLOv3 🚀. UPDATED 29 September 2021. 2 | * [About Weights & Biases](#about-weights-&-biases) 3 | * [First-Time Setup](#first-time-setup) 4 | * [Viewing runs](#viewing-runs) 5 | * [Advanced Usage: Dataset Versioning and Evaluation](#advanced-usage) 6 | * [Reports: Share your work with the world!](#reports) 7 | 8 | ## About Weights & Biases 9 | Think of [W&B](https://wandb.ai/site?utm_campaign=repo_yolo_wandbtutorial) like GitHub for machine learning models. With a few lines of code, save everything you need to debug, compare and reproduce your models — architecture, hyperparameters, git commits, model weights, GPU usage, and even datasets and predictions. 10 | 11 | Used by top researchers including teams at OpenAI, Lyft, Github, and MILA, W&B is part of the new standard of best practices for machine learning. How W&B can help you optimize your machine learning workflows: 12 | 13 | * [Debug](https://wandb.ai/wandb/getting-started/reports/Visualize-Debug-Machine-Learning-Models--VmlldzoyNzY5MDk#Free-2) model performance in real time 14 | * [GPU usage](https://wandb.ai/wandb/getting-started/reports/Visualize-Debug-Machine-Learning-Models--VmlldzoyNzY5MDk#System-4) visualized automatically 15 | * [Custom charts](https://wandb.ai/wandb/customizable-charts/reports/Powerful-Custom-Charts-To-Debug-Model-Peformance--VmlldzoyNzY4ODI) for powerful, extensible visualization 16 | * [Share insights](https://wandb.ai/wandb/getting-started/reports/Visualize-Debug-Machine-Learning-Models--VmlldzoyNzY5MDk#Share-8) interactively with collaborators 17 | * [Optimize hyperparameters](https://docs.wandb.com/sweeps) efficiently 18 | * [Track](https://docs.wandb.com/artifacts) datasets, pipelines, and production models 19 | 20 | ## First-Time Setup 21 |
22 | Toggle Details 23 | When you first train, W&B will prompt you to create a new account and will generate an **API key** for you. If you are an existing user you can retrieve your key from https://wandb.ai/authorize. This key is used to tell W&B where to log your data. You only need to supply your key once, and then it is remembered on the same device. 24 | 25 | W&B will create a cloud **project** (default is 'YOLOv3') for your training runs, and each new training run will be provided a unique run **name** within that project as project/name. You can also manually set your project and run name as: 26 | 27 | ```shell 28 | $ python train.py --project ... --name ... 29 | ``` 30 | 31 | YOLOv3 notebook example: Open In Colab Open In Kaggle 32 | Screen Shot 2021-09-29 at 10 23 13 PM 33 | 34 | 35 |
36 | 37 | ## Viewing Runs 38 |
39 | Toggle Details 40 | Run information streams from your environment to the W&B cloud console as you train. This allows you to monitor and even cancel runs in realtime . All important information is logged: 41 | 42 | * Training & Validation losses 43 | * Metrics: Precision, Recall, mAP@0.5, mAP@0.5:0.95 44 | * Learning Rate over time 45 | * A bounding box debugging panel, showing the training progress over time 46 | * GPU: Type, **GPU Utilization**, power, temperature, **CUDA memory usage** 47 | * System: Disk I/0, CPU utilization, RAM memory usage 48 | * Your trained model as W&B Artifact 49 | * Environment: OS and Python types, Git repository and state, **training command** 50 | 51 |

Weights & Biases dashboard

52 | 53 | 54 |
55 | 56 | ## Advanced Usage 57 | You can leverage W&B artifacts and Tables integration to easily visualize and manage your datasets, models and training evaluations. Here are some quick examples to get you started. 58 |
59 |

1. Visualize and Version Datasets

60 | Log, visualize, dynamically query, and understand your data with W&B Tables. You can use the following command to log your dataset as a W&B Table. This will generate a {dataset}_wandb.yaml file which can be used to train from dataset artifact. 61 |
62 | Usage 63 | Code $ python utils/logger/wandb/log_dataset.py --project ... --name ... --data .. 64 | 65 | ![Screenshot (64)](https://user-images.githubusercontent.com/15766192/128486078-d8433890-98a3-4d12-8986-b6c0e3fc64b9.png) 66 |
67 | 68 |

2: Train and Log Evaluation simultaneousy

69 | This is an extension of the previous section, but it'll also training after uploading the dataset. This also evaluation Table 70 | Evaluation table compares your predictions and ground truths across the validation set for each epoch. It uses the references to the already uploaded datasets, 71 | so no images will be uploaded from your system more than once. 72 |
73 | Usage 74 | Code $ python utils/logger/wandb/log_dataset.py --data .. --upload_data 75 | 76 | ![Screenshot (72)](https://user-images.githubusercontent.com/15766192/128979739-4cf63aeb-a76f-483f-8861-1c0100b938a5.png) 77 |
78 | 79 |

3: Train using dataset artifact

80 | When you upload a dataset as described in the first section, you get a new config file with an added `_wandb` to its name. This file contains the information that 81 | can be used to train a model directly from the dataset artifact. This also logs evaluation 82 |
83 | Usage 84 | Code $ python utils/logger/wandb/log_dataset.py --data {data}_wandb.yaml 85 | 86 | ![Screenshot (72)](https://user-images.githubusercontent.com/15766192/128979739-4cf63aeb-a76f-483f-8861-1c0100b938a5.png) 87 |
88 | 89 |

4: Save model checkpoints as artifacts

90 | To enable saving and versioning checkpoints of your experiment, pass `--save_period n` with the base cammand, where `n` represents checkpoint interval. 91 | You can also log both the dataset and model checkpoints simultaneously. If not passed, only the final model will be logged 92 | 93 |
94 | Usage 95 | Code $ python train.py --save_period 1 96 | 97 | ![Screenshot (68)](https://user-images.githubusercontent.com/15766192/128726138-ec6c1f60-639d-437d-b4ee-3acd9de47ef3.png) 98 |
99 | 100 |
101 | 102 |

5: Resume runs from checkpoint artifacts.

103 | Any run can be resumed using artifacts if the --resume argument starts with wandb-artifact:// prefix followed by the run path, i.e, wandb-artifact://username/project/runid . This doesn't require the model checkpoint to be present on the local system. 104 | 105 |
106 | Usage 107 | Code $ python train.py --resume wandb-artifact://{run_path} 108 | 109 | ![Screenshot (70)](https://user-images.githubusercontent.com/15766192/128728988-4e84b355-6c87-41ae-a591-14aecf45343e.png) 110 |
111 | 112 |

6: Resume runs from dataset artifact & checkpoint artifacts.

113 | Local dataset or model checkpoints are not required. This can be used to resume runs directly on a different device 114 | The syntax is same as the previous section, but you'll need to lof both the dataset and model checkpoints as artifacts, i.e, set bot --upload_dataset or 115 | train from _wandb.yaml file and set --save_period 116 | 117 |
118 | Usage 119 | Code $ python train.py --resume wandb-artifact://{run_path} 120 | 121 | ![Screenshot (70)](https://user-images.githubusercontent.com/15766192/128728988-4e84b355-6c87-41ae-a591-14aecf45343e.png) 122 |
123 | 124 | 125 | 126 | 127 |

Reports

128 | W&B Reports can be created from your saved runs for sharing online. Once a report is created you will receive a link you can use to publically share your results. Here is an example report created from the COCO128 tutorial trainings of all YOLOv5 models ([link](https://wandb.ai/glenn-jocher/yolov5_tutorial/reports/YOLOv5-COCO128-Tutorial-Results--VmlldzozMDI5OTY)). 129 | 130 | Weights & Biases Reports 131 | 132 | 133 | ## Environments 134 | 135 | YOLOv3 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): 136 | 137 | - **Google Colab and Kaggle** notebooks with free GPU: Open In Colab Open In Kaggle 138 | - **Google Cloud** Deep Learning VM. See [GCP Quickstart Guide](https://github.com/ultralytics/yolov3/wiki/GCP-Quickstart) 139 | - **Amazon** Deep Learning AMI. See [AWS Quickstart Guide](https://github.com/ultralytics/yolov3/wiki/AWS-Quickstart) 140 | - **Docker Image**. See [Docker Quickstart Guide](https://github.com/ultralytics/yolov3/wiki/Docker-Quickstart) Docker Pulls 141 | 142 | 143 | ## Status 144 | 145 | ![CI CPU testing](https://github.com/ultralytics/yolov3/workflows/CI%20CPU%20testing/badge.svg) 146 | 147 | If this badge is green, all [YOLOv3 GitHub Actions](https://github.com/ultralytics/yolov3/actions) Continuous Integration (CI) tests are currently passing. CI tests verify correct operation of YOLOv3 training ([train.py](https://github.com/ultralytics/yolov3/blob/master/train.py)), validation ([val.py](https://github.com/ultralytics/yolov3/blob/master/val.py)), inference ([detect.py](https://github.com/ultralytics/yolov3/blob/master/detect.py)) and export ([export.py](https://github.com/ultralytics/yolov3/blob/master/export.py)) on MacOS, Windows, and Ubuntu every 24 hours and on every commit. 148 | -------------------------------------------------------------------------------- /utils/loss.py: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Loss functions 4 | """ 5 | 6 | import torch 7 | import torch.nn as nn 8 | 9 | from utils.metrics import bbox_iou 10 | from utils.torch_utils import de_parallel 11 | 12 | 13 | def smooth_BCE(eps=0.1): # https://github.com/ultralytics/yolov3/issues/238#issuecomment-598028441 14 | # return positive, negative label smoothing BCE targets 15 | return 1.0 - 0.5 * eps, 0.5 * eps 16 | 17 | 18 | class BCEBlurWithLogitsLoss(nn.Module): 19 | # BCEwithLogitLoss() with reduced missing label effects. 20 | def __init__(self, alpha=0.05): 21 | super().__init__() 22 | self.loss_fcn = nn.BCEWithLogitsLoss(reduction='none') # must be nn.BCEWithLogitsLoss() 23 | self.alpha = alpha 24 | 25 | def forward(self, pred, true): 26 | loss = self.loss_fcn(pred, true) 27 | pred = torch.sigmoid(pred) # prob from logits 28 | dx = pred - true # reduce only missing label effects 29 | # dx = (pred - true).abs() # reduce missing label and false label effects 30 | alpha_factor = 1 - torch.exp((dx - 1) / (self.alpha + 1e-4)) 31 | loss *= alpha_factor 32 | return loss.mean() 33 | 34 | 35 | class FocalLoss(nn.Module): 36 | # Wraps focal loss around existing loss_fcn(), i.e. criteria = FocalLoss(nn.BCEWithLogitsLoss(), gamma=1.5) 37 | def __init__(self, loss_fcn, gamma=1.5, alpha=0.25): 38 | super().__init__() 39 | self.loss_fcn = loss_fcn # must be nn.BCEWithLogitsLoss() 40 | self.gamma = gamma 41 | self.alpha = alpha 42 | self.reduction = loss_fcn.reduction 43 | self.loss_fcn.reduction = 'none' # required to apply FL to each element 44 | 45 | def forward(self, pred, true): 46 | loss = self.loss_fcn(pred, true) 47 | # p_t = torch.exp(-loss) 48 | # loss *= self.alpha * (1.000001 - p_t) ** self.gamma # non-zero power for gradient stability 49 | 50 | # TF implementation https://github.com/tensorflow/addons/blob/v0.7.1/tensorflow_addons/losses/focal_loss.py 51 | pred_prob = torch.sigmoid(pred) # prob from logits 52 | p_t = true * pred_prob + (1 - true) * (1 - pred_prob) 53 | alpha_factor = true * self.alpha + (1 - true) * (1 - self.alpha) 54 | modulating_factor = (1.0 - p_t) ** self.gamma 55 | loss *= alpha_factor * modulating_factor 56 | 57 | if self.reduction == 'mean': 58 | return loss.mean() 59 | elif self.reduction == 'sum': 60 | return loss.sum() 61 | else: # 'none' 62 | return loss 63 | 64 | 65 | class QFocalLoss(nn.Module): 66 | # Wraps Quality focal loss around existing loss_fcn(), i.e. criteria = FocalLoss(nn.BCEWithLogitsLoss(), gamma=1.5) 67 | def __init__(self, loss_fcn, gamma=1.5, alpha=0.25): 68 | super().__init__() 69 | self.loss_fcn = loss_fcn # must be nn.BCEWithLogitsLoss() 70 | self.gamma = gamma 71 | self.alpha = alpha 72 | self.reduction = loss_fcn.reduction 73 | self.loss_fcn.reduction = 'none' # required to apply FL to each element 74 | 75 | def forward(self, pred, true): 76 | loss = self.loss_fcn(pred, true) 77 | 78 | pred_prob = torch.sigmoid(pred) # prob from logits 79 | alpha_factor = true * self.alpha + (1 - true) * (1 - self.alpha) 80 | modulating_factor = torch.abs(true - pred_prob) ** self.gamma 81 | loss *= alpha_factor * modulating_factor 82 | 83 | if self.reduction == 'mean': 84 | return loss.mean() 85 | elif self.reduction == 'sum': 86 | return loss.sum() 87 | else: # 'none' 88 | return loss 89 | 90 | 91 | class ComputeLoss: 92 | sort_obj_iou = False 93 | 94 | # Compute losses 95 | def __init__(self, model, autobalance=False): 96 | device = next(model.parameters()).device # get model device 97 | h = model.hyp # hyperparameters 98 | 99 | # Define criteria 100 | BCEcls = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['cls_pw']], device=device)) 101 | BCEobj = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['obj_pw']], device=device)) 102 | 103 | # Class label smoothing https://arxiv.org/pdf/1902.04103.pdf eqn 3 104 | self.cp, self.cn = smooth_BCE(eps=h.get('label_smoothing', 0.0)) # positive, negative BCE targets 105 | 106 | # Focal loss 107 | g = h['fl_gamma'] # focal loss gamma 108 | if g > 0: 109 | BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g) 110 | 111 | m = de_parallel(model).model[-1] # Detect() module 112 | self.balance = {3: [4.0, 1.0, 0.4]}.get(m.nl, [4.0, 1.0, 0.25, 0.06, 0.02]) # P3-P7 113 | self.ssi = list(m.stride).index(16) if autobalance else 0 # stride 16 index 114 | self.BCEcls, self.BCEobj, self.gr, self.hyp, self.autobalance = BCEcls, BCEobj, 1.0, h, autobalance 115 | self.na = m.na # number of anchors 116 | self.nc = m.nc # number of classes 117 | self.nl = m.nl # number of layers 118 | self.anchors = m.anchors 119 | self.device = device 120 | 121 | def __call__(self, p, targets): # predictions, targets 122 | lcls = torch.zeros(1, device=self.device) # class loss 123 | lbox = torch.zeros(1, device=self.device) # box loss 124 | lobj = torch.zeros(1, device=self.device) # object loss 125 | tcls, tbox, indices, anchors = self.build_targets(p, targets) # targets 126 | 127 | # Losses 128 | for i, pi in enumerate(p): # layer index, layer predictions 129 | b, a, gj, gi = indices[i] # image, anchor, gridy, gridx 130 | tobj = torch.zeros(pi.shape[:4], dtype=pi.dtype, device=self.device) # target obj 131 | 132 | n = b.shape[0] # number of targets 133 | if n: 134 | # pxy, pwh, _, pcls = pi[b, a, gj, gi].tensor_split((2, 4, 5), dim=1) # faster, requires torch 1.8.0 135 | pxy, pwh, _, pcls = pi[b, a, gj, gi].split((2, 2, 1, self.nc), 1) # target-subset of predictions 136 | 137 | # Regression 138 | pxy = pxy.sigmoid() * 2 - 0.5 139 | pwh = (pwh.sigmoid() * 2) ** 2 * anchors[i] 140 | pbox = torch.cat((pxy, pwh), 1) # predicted box 141 | iou = bbox_iou(pbox, tbox[i], CIoU=True).squeeze() # iou(prediction, target) 142 | lbox += (1.0 - iou).mean() # iou loss 143 | 144 | # Objectness 145 | iou = iou.detach().clamp(0).type(tobj.dtype) 146 | if self.sort_obj_iou: 147 | j = iou.argsort() 148 | b, a, gj, gi, iou = b[j], a[j], gj[j], gi[j], iou[j] 149 | if self.gr < 1: 150 | iou = (1.0 - self.gr) + self.gr * iou 151 | tobj[b, a, gj, gi] = iou # iou ratio 152 | 153 | # Classification 154 | if self.nc > 1: # cls loss (only if multiple classes) 155 | t = torch.full_like(pcls, self.cn, device=self.device) # targets 156 | t[range(n), tcls[i]] = self.cp 157 | lcls += self.BCEcls(pcls, t) # BCE 158 | 159 | # Append targets to text file 160 | # with open('targets.txt', 'a') as file: 161 | # [file.write('%11.5g ' * 4 % tuple(x) + '\n') for x in torch.cat((txy[i], twh[i]), 1)] 162 | 163 | obji = self.BCEobj(pi[..., 4], tobj) 164 | lobj += obji * self.balance[i] # obj loss 165 | if self.autobalance: 166 | self.balance[i] = self.balance[i] * 0.9999 + 0.0001 / obji.detach().item() 167 | 168 | if self.autobalance: 169 | self.balance = [x / self.balance[self.ssi] for x in self.balance] 170 | lbox *= self.hyp['box'] 171 | lobj *= self.hyp['obj'] 172 | lcls *= self.hyp['cls'] 173 | bs = tobj.shape[0] # batch size 174 | 175 | return (lbox + lobj + lcls) * bs, torch.cat((lbox, lobj, lcls)).detach() 176 | 177 | def build_targets(self, p, targets): 178 | # Build targets for compute_loss(), input targets(image,class,x,y,w,h) 179 | na, nt = self.na, targets.shape[0] # number of anchors, targets 180 | tcls, tbox, indices, anch = [], [], [], [] 181 | gain = torch.ones(7, device=self.device) # normalized to gridspace gain 182 | ai = torch.arange(na, device=self.device).float().view(na, 1).repeat(1, nt) # same as .repeat_interleave(nt) 183 | targets = torch.cat((targets.repeat(na, 1, 1), ai[..., None]), 2) # append anchor indices 184 | 185 | g = 0.5 # bias 186 | off = torch.tensor( 187 | [ 188 | [0, 0], 189 | [1, 0], 190 | [0, 1], 191 | [-1, 0], 192 | [0, -1], # j,k,l,m 193 | # [1, 1], [1, -1], [-1, 1], [-1, -1], # jk,jm,lk,lm 194 | ], 195 | device=self.device).float() * g # offsets 196 | 197 | for i in range(self.nl): 198 | anchors, shape = self.anchors[i], p[i].shape 199 | gain[2:6] = torch.tensor(shape)[[3, 2, 3, 2]] # xyxy gain 200 | 201 | # Match targets to anchors 202 | t = targets * gain # shape(3,n,7) 203 | if nt: 204 | # Matches 205 | r = t[..., 4:6] / anchors[:, None] # wh ratio 206 | j = torch.max(r, 1 / r).max(2)[0] < self.hyp['anchor_t'] # compare 207 | # j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t'] # iou(3,n)=wh_iou(anchors(3,2), gwh(n,2)) 208 | t = t[j] # filter 209 | 210 | # Offsets 211 | gxy = t[:, 2:4] # grid xy 212 | gxi = gain[[2, 3]] - gxy # inverse 213 | j, k = ((gxy % 1 < g) & (gxy > 1)).T 214 | l, m = ((gxi % 1 < g) & (gxi > 1)).T 215 | j = torch.stack((torch.ones_like(j), j, k, l, m)) 216 | t = t.repeat((5, 1, 1))[j] 217 | offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j] 218 | else: 219 | t = targets[0] 220 | offsets = 0 221 | 222 | # Define 223 | bc, gxy, gwh, a = t.chunk(4, 1) # (image, class), grid xy, grid wh, anchors 224 | a, (b, c) = a.long().view(-1), bc.long().T # anchors, image, class 225 | gij = (gxy - offsets).long() 226 | gi, gj = gij.T # grid indices 227 | 228 | # Append 229 | indices.append((b, a, gj.clamp_(0, shape[2] - 1), gi.clamp_(0, shape[3] - 1))) # image, anchor, grid 230 | tbox.append(torch.cat((gxy - gij, gwh), 1)) # box 231 | anch.append(anchors[a]) # anchors 232 | tcls.append(c) # class 233 | 234 | return tcls, tbox, indices, anch 235 | -------------------------------------------------------------------------------- /utils/augmentations.py: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Image augmentation functions 4 | """ 5 | 6 | import math 7 | import random 8 | 9 | import cv2 10 | import numpy as np 11 | 12 | from utils.general import LOGGER, check_version, colorstr, resample_segments, segment2box 13 | from utils.metrics import bbox_ioa 14 | 15 | 16 | class Albumentations: 17 | # Albumentations class (optional, only used if package is installed) 18 | def __init__(self): 19 | self.transform = None 20 | try: 21 | import albumentations as A 22 | check_version(A.__version__, '1.0.3', hard=True) # version requirement 23 | 24 | self.transform = A.Compose([ 25 | A.Blur(p=0.01), 26 | A.MedianBlur(p=0.01), 27 | A.ToGray(p=0.01), 28 | A.CLAHE(p=0.01), 29 | A.RandomBrightnessContrast(p=0.0), 30 | A.RandomGamma(p=0.0), 31 | A.ImageCompression(quality_lower=75, p=0.0)], 32 | bbox_params=A.BboxParams(format='yolo', label_fields=['class_labels'])) 33 | 34 | LOGGER.info(colorstr('albumentations: ') + ', '.join(f'{x}' for x in self.transform.transforms if x.p)) 35 | except ImportError: # package not installed, skip 36 | pass 37 | except Exception as e: 38 | LOGGER.info(colorstr('albumentations: ') + f'{e}') 39 | 40 | def __call__(self, im, labels, p=1.0): 41 | if self.transform and random.random() < p: 42 | new = self.transform(image=im, bboxes=labels[:, 1:], class_labels=labels[:, 0]) # transformed 43 | im, labels = new['image'], np.array([[c, *b] for c, b in zip(new['class_labels'], new['bboxes'])]) 44 | return im, labels 45 | 46 | 47 | def augment_hsv(im, hgain=0.5, sgain=0.5, vgain=0.5): 48 | # HSV color-space augmentation 49 | if hgain or sgain or vgain: 50 | r = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain] + 1 # random gains 51 | hue, sat, val = cv2.split(cv2.cvtColor(im, cv2.COLOR_BGR2HSV)) 52 | dtype = im.dtype # uint8 53 | 54 | x = np.arange(0, 256, dtype=r.dtype) 55 | lut_hue = ((x * r[0]) % 180).astype(dtype) 56 | lut_sat = np.clip(x * r[1], 0, 255).astype(dtype) 57 | lut_val = np.clip(x * r[2], 0, 255).astype(dtype) 58 | 59 | im_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val))) 60 | cv2.cvtColor(im_hsv, cv2.COLOR_HSV2BGR, dst=im) # no return needed 61 | 62 | 63 | def hist_equalize(im, clahe=True, bgr=False): 64 | # Equalize histogram on BGR image 'im' with im.shape(n,m,3) and range 0-255 65 | yuv = cv2.cvtColor(im, cv2.COLOR_BGR2YUV if bgr else cv2.COLOR_RGB2YUV) 66 | if clahe: 67 | c = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) 68 | yuv[:, :, 0] = c.apply(yuv[:, :, 0]) 69 | else: 70 | yuv[:, :, 0] = cv2.equalizeHist(yuv[:, :, 0]) # equalize Y channel histogram 71 | return cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR if bgr else cv2.COLOR_YUV2RGB) # convert YUV image to RGB 72 | 73 | 74 | def replicate(im, labels): 75 | # Replicate labels 76 | h, w = im.shape[:2] 77 | boxes = labels[:, 1:].astype(int) 78 | x1, y1, x2, y2 = boxes.T 79 | s = ((x2 - x1) + (y2 - y1)) / 2 # side length (pixels) 80 | for i in s.argsort()[:round(s.size * 0.5)]: # smallest indices 81 | x1b, y1b, x2b, y2b = boxes[i] 82 | bh, bw = y2b - y1b, x2b - x1b 83 | yc, xc = int(random.uniform(0, h - bh)), int(random.uniform(0, w - bw)) # offset x, y 84 | x1a, y1a, x2a, y2a = [xc, yc, xc + bw, yc + bh] 85 | im[y1a:y2a, x1a:x2a] = im[y1b:y2b, x1b:x2b] # im4[ymin:ymax, xmin:xmax] 86 | labels = np.append(labels, [[labels[i, 0], x1a, y1a, x2a, y2a]], axis=0) 87 | 88 | return im, labels 89 | 90 | 91 | def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32): 92 | # Resize and pad image while meeting stride-multiple constraints 93 | shape = im.shape[:2] # current shape [height, width] 94 | if isinstance(new_shape, int): 95 | new_shape = (new_shape, new_shape) 96 | 97 | # Scale ratio (new / old) 98 | r = min(new_shape[0] / shape[0], new_shape[1] / shape[1]) 99 | if not scaleup: # only scale down, do not scale up (for better val mAP) 100 | r = min(r, 1.0) 101 | 102 | # Compute padding 103 | ratio = r, r # width, height ratios 104 | new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r)) 105 | dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding 106 | if auto: # minimum rectangle 107 | dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding 108 | elif scaleFill: # stretch 109 | dw, dh = 0.0, 0.0 110 | new_unpad = (new_shape[1], new_shape[0]) 111 | ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratios 112 | 113 | dw /= 2 # divide padding into 2 sides 114 | dh /= 2 115 | 116 | if shape[::-1] != new_unpad: # resize 117 | im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR) 118 | top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) 119 | left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) 120 | im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border 121 | return im, ratio, (dw, dh) 122 | 123 | 124 | def random_perspective(im, targets=(), segments=(), degrees=10, translate=.1, scale=.1, shear=10, perspective=0.0, 125 | border=(0, 0)): 126 | # torchvision.transforms.RandomAffine(degrees=(-10, 10), translate=(0.1, 0.1), scale=(0.9, 1.1), shear=(-10, 10)) 127 | # targets = [cls, xyxy] 128 | 129 | height = im.shape[0] + border[0] * 2 # shape(h,w,c) 130 | width = im.shape[1] + border[1] * 2 131 | 132 | # Center 133 | C = np.eye(3) 134 | C[0, 2] = -im.shape[1] / 2 # x translation (pixels) 135 | C[1, 2] = -im.shape[0] / 2 # y translation (pixels) 136 | 137 | # Perspective 138 | P = np.eye(3) 139 | P[2, 0] = random.uniform(-perspective, perspective) # x perspective (about y) 140 | P[2, 1] = random.uniform(-perspective, perspective) # y perspective (about x) 141 | 142 | # Rotation and Scale 143 | R = np.eye(3) 144 | a = random.uniform(-degrees, degrees) 145 | # a += random.choice([-180, -90, 0, 90]) # add 90deg rotations to small rotations 146 | s = random.uniform(1 - scale, 1 + scale) 147 | # s = 2 ** random.uniform(-scale, scale) 148 | R[:2] = cv2.getRotationMatrix2D(angle=a, center=(0, 0), scale=s) 149 | 150 | # Shear 151 | S = np.eye(3) 152 | S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # x shear (deg) 153 | S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # y shear (deg) 154 | 155 | # Translation 156 | T = np.eye(3) 157 | T[0, 2] = random.uniform(0.5 - translate, 0.5 + translate) * width # x translation (pixels) 158 | T[1, 2] = random.uniform(0.5 - translate, 0.5 + translate) * height # y translation (pixels) 159 | 160 | # Combined rotation matrix 161 | M = T @ S @ R @ P @ C # order of operations (right to left) is IMPORTANT 162 | if (border[0] != 0) or (border[1] != 0) or (M != np.eye(3)).any(): # image changed 163 | if perspective: 164 | im = cv2.warpPerspective(im, M, dsize=(width, height), borderValue=(114, 114, 114)) 165 | else: # affine 166 | im = cv2.warpAffine(im, M[:2], dsize=(width, height), borderValue=(114, 114, 114)) 167 | 168 | # Visualize 169 | # import matplotlib.pyplot as plt 170 | # ax = plt.subplots(1, 2, figsize=(12, 6))[1].ravel() 171 | # ax[0].imshow(im[:, :, ::-1]) # base 172 | # ax[1].imshow(im2[:, :, ::-1]) # warped 173 | 174 | # Transform label coordinates 175 | n = len(targets) 176 | if n: 177 | use_segments = any(x.any() for x in segments) 178 | new = np.zeros((n, 4)) 179 | if use_segments: # warp segments 180 | segments = resample_segments(segments) # upsample 181 | for i, segment in enumerate(segments): 182 | xy = np.ones((len(segment), 3)) 183 | xy[:, :2] = segment 184 | xy = xy @ M.T # transform 185 | xy = xy[:, :2] / xy[:, 2:3] if perspective else xy[:, :2] # perspective rescale or affine 186 | 187 | # clip 188 | new[i] = segment2box(xy, width, height) 189 | 190 | else: # warp boxes 191 | xy = np.ones((n * 4, 3)) 192 | xy[:, :2] = targets[:, [1, 2, 3, 4, 1, 4, 3, 2]].reshape(n * 4, 2) # x1y1, x2y2, x1y2, x2y1 193 | xy = xy @ M.T # transform 194 | xy = (xy[:, :2] / xy[:, 2:3] if perspective else xy[:, :2]).reshape(n, 8) # perspective rescale or affine 195 | 196 | # create new boxes 197 | x = xy[:, [0, 2, 4, 6]] 198 | y = xy[:, [1, 3, 5, 7]] 199 | new = np.concatenate((x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T 200 | 201 | # clip 202 | new[:, [0, 2]] = new[:, [0, 2]].clip(0, width) 203 | new[:, [1, 3]] = new[:, [1, 3]].clip(0, height) 204 | 205 | # filter candidates 206 | i = box_candidates(box1=targets[:, 1:5].T * s, box2=new.T, area_thr=0.01 if use_segments else 0.10) 207 | targets = targets[i] 208 | targets[:, 1:5] = new[i] 209 | 210 | return im, targets 211 | 212 | 213 | def copy_paste(im, labels, segments, p=0.5): 214 | # Implement Copy-Paste augmentation https://arxiv.org/abs/2012.07177, labels as nx5 np.array(cls, xyxy) 215 | n = len(segments) 216 | if p and n: 217 | h, w, c = im.shape # height, width, channels 218 | im_new = np.zeros(im.shape, np.uint8) 219 | for j in random.sample(range(n), k=round(p * n)): 220 | l, s = labels[j], segments[j] 221 | box = w - l[3], l[2], w - l[1], l[4] 222 | ioa = bbox_ioa(box, labels[:, 1:5]) # intersection over area 223 | if (ioa < 0.30).all(): # allow 30% obscuration of existing labels 224 | labels = np.concatenate((labels, [[l[0], *box]]), 0) 225 | segments.append(np.concatenate((w - s[:, 0:1], s[:, 1:2]), 1)) 226 | cv2.drawContours(im_new, [segments[j].astype(np.int32)], -1, (255, 255, 255), cv2.FILLED) 227 | 228 | result = cv2.bitwise_and(src1=im, src2=im_new) 229 | result = cv2.flip(result, 1) # augment segments (flip left-right) 230 | i = result > 0 # pixels to replace 231 | # i[:, :] = result.max(2).reshape(h, w, 1) # act over ch 232 | im[i] = result[i] # cv2.imwrite('debug.jpg', im) # debug 233 | 234 | return im, labels, segments 235 | 236 | 237 | def cutout(im, labels, p=0.5): 238 | # Applies image cutout augmentation https://arxiv.org/abs/1708.04552 239 | if random.random() < p: 240 | h, w = im.shape[:2] 241 | scales = [0.5] * 1 + [0.25] * 2 + [0.125] * 4 + [0.0625] * 8 + [0.03125] * 16 # image size fraction 242 | for s in scales: 243 | mask_h = random.randint(1, int(h * s)) # create random masks 244 | mask_w = random.randint(1, int(w * s)) 245 | 246 | # box 247 | xmin = max(0, random.randint(0, w) - mask_w // 2) 248 | ymin = max(0, random.randint(0, h) - mask_h // 2) 249 | xmax = min(w, xmin + mask_w) 250 | ymax = min(h, ymin + mask_h) 251 | 252 | # apply random color mask 253 | im[ymin:ymax, xmin:xmax] = [random.randint(64, 191) for _ in range(3)] 254 | 255 | # return unobscured labels 256 | if len(labels) and s > 0.03: 257 | box = np.array([xmin, ymin, xmax, ymax], dtype=np.float32) 258 | ioa = bbox_ioa(box, labels[:, 1:5]) # intersection over area 259 | labels = labels[ioa < 0.60] # remove >60% obscured labels 260 | 261 | return labels 262 | 263 | 264 | def mixup(im, labels, im2, labels2): 265 | # Applies MixUp augmentation https://arxiv.org/pdf/1710.09412.pdf 266 | r = np.random.beta(32.0, 32.0) # mixup ratio, alpha=beta=32.0 267 | im = (im * r + im2 * (1 - r)).astype(np.uint8) 268 | labels = np.concatenate((labels, labels2), 0) 269 | return im, labels 270 | 271 | 272 | def box_candidates(box1, box2, wh_thr=2, ar_thr=20, area_thr=0.1, eps=1e-16): # box1(4,n), box2(4,n) 273 | # Compute candidate boxes: box1 before augment, box2 after augment, wh_thr (pixels), aspect_ratio_thr, area_ratio 274 | w1, h1 = box1[2] - box1[0], box1[3] - box1[1] 275 | w2, h2 = box2[2] - box2[0], box2[3] - box2[1] 276 | ar = np.maximum(w2 / (h2 + eps), h2 / (w2 + eps)) # aspect ratio 277 | return (w2 > wh_thr) & (h2 > wh_thr) & (w2 * h2 / (w1 * h1 + eps) > area_thr) & (ar < ar_thr) # candidates 278 | -------------------------------------------------------------------------------- /detect.py: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Run inference on images, videos, directories, streams, etc. 4 | 5 | Usage: 6 | $ python path/to/detect.py --weights yolov3.pt --source 0 # webcam 7 | img.jpg # image 8 | vid.mp4 # video 9 | path/ # directory 10 | path/*.jpg # glob 11 | 'https://youtu.be/Zgi9g1ksQHc' # YouTube 12 | 'rtsp://example.com/media.mp4' # RTSP, RTMP, HTTP stream 13 | """ 14 | 15 | import argparse 16 | import os 17 | import sys 18 | from pathlib import Path 19 | 20 | import cv2 21 | import torch 22 | import torch.backends.cudnn as cudnn 23 | 24 | FILE = Path(__file__).resolve() 25 | ROOT = FILE.parents[0] # root directory 26 | if str(ROOT) not in sys.path: 27 | sys.path.append(str(ROOT)) # add ROOT to PATH 28 | ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative 29 | 30 | from models.common import DetectMultiBackend 31 | from utils.datasets import IMG_FORMATS, VID_FORMATS, LoadImages, LoadStreams 32 | from utils.general import (LOGGER, check_file, check_img_size, check_imshow, check_requirements, colorstr, 33 | increment_path, non_max_suppression, print_args, scale_coords, strip_optimizer, xyxy2xywh) 34 | from utils.plots import Annotator, colors, save_one_box 35 | from utils.torch_utils import select_device, time_sync 36 | 37 | 38 | @torch.no_grad() 39 | def run(weights=ROOT / 'yolov3.pt', # model.pt path(s) 40 | source=ROOT / 'data/images', # file/dir/URL/glob, 0 for webcam 41 | imgsz=640, # inference size (pixels) 42 | conf_thres=0.25, # confidence threshold 43 | iou_thres=0.45, # NMS IOU threshold 44 | max_det=1000, # maximum detections per image 45 | device='', # cuda device, i.e. 0 or 0,1,2,3 or cpu 46 | view_img=False, # show results 47 | save_txt=False, # save results to *.txt 48 | save_conf=False, # save confidences in --save-txt labels 49 | save_crop=False, # save cropped prediction boxes 50 | nosave=False, # do not save images/videos 51 | classes=None, # filter by class: --class 0, or --class 0 2 3 52 | agnostic_nms=False, # class-agnostic NMS 53 | augment=False, # augmented inference 54 | visualize=False, # visualize features 55 | update=False, # update all models 56 | project=ROOT / 'runs/detect', # save results to project/name 57 | name='exp', # save results to project/name 58 | exist_ok=False, # existing project/name ok, do not increment 59 | line_thickness=3, # bounding box thickness (pixels) 60 | hide_labels=False, # hide labels 61 | hide_conf=False, # hide confidences 62 | half=False, # use FP16 half-precision inference 63 | dnn=False, # use OpenCV DNN for ONNX inference 64 | ): 65 | source = str(source) 66 | save_img = not nosave and not source.endswith('.txt') # save inference images 67 | is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS) 68 | is_url = source.lower().startswith(('rtsp://', 'rtmp://', 'http://', 'https://')) 69 | webcam = source.isnumeric() or source.endswith('.txt') or (is_url and not is_file) 70 | if is_url and is_file: 71 | source = check_file(source) # download 72 | 73 | # Directories 74 | save_dir = increment_path(Path(project) / name, exist_ok=exist_ok) # increment run 75 | (save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir 76 | 77 | # Load model 78 | device = select_device(device) 79 | model = DetectMultiBackend(weights, device=device, dnn=dnn) 80 | stride, names, pt, jit, onnx = model.stride, model.names, model.pt, model.jit, model.onnx 81 | imgsz = check_img_size(imgsz, s=stride) # check image size 82 | 83 | # Half 84 | half &= pt and device.type != 'cpu' # half precision only supported by PyTorch on CUDA 85 | if pt: 86 | model.model.half() if half else model.model.float() 87 | 88 | # Dataloader 89 | if webcam: 90 | view_img = check_imshow() 91 | cudnn.benchmark = True # set True to speed up constant image size inference 92 | dataset = LoadStreams(source, img_size=imgsz, stride=stride, auto=pt and not jit) 93 | bs = len(dataset) # batch_size 94 | else: 95 | dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt and not jit) 96 | bs = 1 # batch_size 97 | vid_path, vid_writer = [None] * bs, [None] * bs 98 | 99 | # Run inference 100 | if pt and device.type != 'cpu': 101 | model(torch.zeros(1, 3, *imgsz).to(device).type_as(next(model.model.parameters()))) # warmup 102 | dt, seen = [0.0, 0.0, 0.0], 0 103 | for path, im, im0s, vid_cap, s in dataset: 104 | t1 = time_sync() 105 | im = torch.from_numpy(im).to(device) 106 | im = im.half() if half else im.float() # uint8 to fp16/32 107 | im /= 255 # 0 - 255 to 0.0 - 1.0 108 | if len(im.shape) == 3: 109 | im = im[None] # expand for batch dim 110 | t2 = time_sync() 111 | dt[0] += t2 - t1 112 | 113 | # Inference 114 | visualize = increment_path(save_dir / Path(path).stem, mkdir=True) if visualize else False 115 | pred = model(im, augment=augment, visualize=visualize) 116 | t3 = time_sync() 117 | dt[1] += t3 - t2 118 | 119 | # NMS 120 | pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det) 121 | dt[2] += time_sync() - t3 122 | 123 | # Second-stage classifier (optional) 124 | # pred = utils.general.apply_classifier(pred, classifier_model, im, im0s) 125 | 126 | # Process predictions 127 | for i, det in enumerate(pred): # per image 128 | seen += 1 129 | if webcam: # batch_size >= 1 130 | p, im0, frame = path[i], im0s[i].copy(), dataset.count 131 | s += f'{i}: ' 132 | else: 133 | p, im0, frame = path, im0s.copy(), getattr(dataset, 'frame', 0) 134 | 135 | p = Path(p) # to Path 136 | save_path = str(save_dir / p.name) # im.jpg 137 | txt_path = str(save_dir / 'labels' / p.stem) + ('' if dataset.mode == 'image' else f'_{frame}') # im.txt 138 | s += '%gx%g ' % im.shape[2:] # print string 139 | gn = torch.tensor(im0.shape)[[1, 0, 1, 0]] # normalization gain whwh 140 | imc = im0.copy() if save_crop else im0 # for save_crop 141 | annotator = Annotator(im0, line_width=line_thickness, example=str(names)) 142 | if len(det): 143 | # Rescale boxes from img_size to im0 size 144 | det[:, :4] = scale_coords(im.shape[2:], det[:, :4], im0.shape).round() 145 | 146 | # Print results 147 | for c in det[:, -1].unique(): 148 | n = (det[:, -1] == c).sum() # detections per class 149 | s += f"{n} {names[int(c)]}{'s' * (n > 1)}, " # add to string 150 | 151 | # Write results 152 | for *xyxy, conf, cls in reversed(det): 153 | if save_txt: # Write to file 154 | xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh 155 | line = (cls, *xywh, conf) if save_conf else (cls, *xywh) # label format 156 | with open(txt_path + '.txt', 'a') as f: 157 | f.write(('%g ' * len(line)).rstrip() % line + '\n') 158 | 159 | if save_img or save_crop or view_img: # Add bbox to image 160 | c = int(cls) # integer class 161 | label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf:.2f}') 162 | annotator.box_label(xyxy, label, color=colors(c, True)) 163 | if save_crop: 164 | save_one_box(xyxy, imc, file=save_dir / 'crops' / names[c] / f'{p.stem}.jpg', BGR=True) 165 | 166 | # Print time (inference-only) 167 | LOGGER.info(f'{s}Done. ({t3 - t2:.3f}s)') 168 | 169 | # Stream results 170 | im0 = annotator.result() 171 | if view_img: 172 | cv2.imshow(str(p), im0) 173 | cv2.waitKey(1) # 1 millisecond 174 | 175 | # Save results (image with detections) 176 | if save_img: 177 | if dataset.mode == 'image': 178 | cv2.imwrite(save_path, im0) 179 | else: # 'video' or 'stream' 180 | if vid_path[i] != save_path: # new video 181 | vid_path[i] = save_path 182 | if isinstance(vid_writer[i], cv2.VideoWriter): 183 | vid_writer[i].release() # release previous video writer 184 | if vid_cap: # video 185 | fps = vid_cap.get(cv2.CAP_PROP_FPS) 186 | w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH)) 187 | h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) 188 | else: # stream 189 | fps, w, h = 30, im0.shape[1], im0.shape[0] 190 | save_path += '.mp4' 191 | vid_writer[i] = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h)) 192 | vid_writer[i].write(im0) 193 | 194 | # Print results 195 | t = tuple(x / seen * 1E3 for x in dt) # speeds per image 196 | LOGGER.info(f'Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {(1, 3, *imgsz)}' % t) 197 | if save_txt or save_img: 198 | s = f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else '' 199 | LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}{s}") 200 | if update: 201 | strip_optimizer(weights) # update model (to fix SourceChangeWarning) 202 | 203 | 204 | def parse_opt(): 205 | parser = argparse.ArgumentParser() 206 | parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'yolov3.pt', help='model path(s)') 207 | parser.add_argument('--source', type=str, default=ROOT / 'data/images', help='file/dir/URL/glob, 0 for webcam') 208 | parser.add_argument('--imgsz', '--img', '--img-size', nargs='+', type=int, default=[640], help='inference size h,w') 209 | parser.add_argument('--conf-thres', type=float, default=0.25, help='confidence threshold') 210 | parser.add_argument('--iou-thres', type=float, default=0.45, help='NMS IoU threshold') 211 | parser.add_argument('--max-det', type=int, default=1000, help='maximum detections per image') 212 | parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') 213 | parser.add_argument('--view-img', action='store_true', help='show results') 214 | parser.add_argument('--save-txt', action='store_true', help='save results to *.txt') 215 | parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels') 216 | parser.add_argument('--save-crop', action='store_true', help='save cropped prediction boxes') 217 | parser.add_argument('--nosave', action='store_true', help='do not save images/videos') 218 | parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --classes 0, or --classes 0 2 3') 219 | parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS') 220 | parser.add_argument('--augment', action='store_true', help='augmented inference') 221 | parser.add_argument('--visualize', action='store_true', help='visualize features') 222 | parser.add_argument('--update', action='store_true', help='update all models') 223 | parser.add_argument('--project', default=ROOT / 'runs/detect', help='save results to project/name') 224 | parser.add_argument('--name', default='exp', help='save results to project/name') 225 | parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment') 226 | parser.add_argument('--line-thickness', default=3, type=int, help='bounding box thickness (pixels)') 227 | parser.add_argument('--hide-labels', default=False, action='store_true', help='hide labels') 228 | parser.add_argument('--hide-conf', default=False, action='store_true', help='hide confidences') 229 | parser.add_argument('--half', action='store_true', help='use FP16 half-precision inference') 230 | parser.add_argument('--dnn', action='store_true', help='use OpenCV DNN for ONNX inference') 231 | opt = parser.parse_args() 232 | opt.imgsz *= 2 if len(opt.imgsz) == 1 else 1 # expand 233 | print_args(FILE.stem, opt) 234 | return opt 235 | 236 | 237 | def main(opt): 238 | check_requirements(exclude=('tensorboard', 'thop')) 239 | run(**vars(opt)) 240 | 241 | 242 | if __name__ == "__main__": 243 | opt = parse_opt() 244 | main(opt) 245 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 | 5 |

6 |
7 |
8 | CI CPU testing 9 | YOLOv3 Citation 10 | Docker Pulls 11 |
12 | Open In Colab 13 | Open In Kaggle 14 | Join Forum 15 |
16 |
17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 | 43 |
44 |

45 | YOLOv3 🚀 is a family of object detection architectures and models pretrained on the COCO dataset, and represents Ultralytics 46 | open-source research into future vision AI methods, incorporating lessons learned and best practices evolved over thousands of hours of research and development. 47 |

48 | 49 | 53 | 54 |
55 | 56 | ##
Documentation
57 | 58 | See the [YOLOv3 Docs](https://docs.ultralytics.com) for full documentation on training, testing and deployment. 59 | 60 | ##
Quick Start Examples
61 | 62 |
63 | Install 64 | 65 | [**Python>=3.6.0**](https://www.python.org/) is required with all 66 | [requirements.txt](https://github.com/ultralytics/yolov3/blob/master/requirements.txt) installed including 67 | [**PyTorch>=1.7**](https://pytorch.org/get-started/locally/): 68 | 69 | 70 | ```bash 71 | $ git clone https://github.com/ultralytics/yolov3 72 | $ cd yolov3 73 | $ pip install -r requirements.txt 74 | ``` 75 | 76 |
77 | 78 |
79 | Inference 80 | 81 | Inference with YOLOv3 and [PyTorch Hub](https://github.com/ultralytics/yolov5/issues/36). Models automatically download 82 | from the [latest YOLOv3 release](https://github.com/ultralytics/yolov3/releases). 83 | 84 | ```python 85 | import torch 86 | 87 | # Model 88 | model = torch.hub.load('ultralytics/yolov3', 'yolov3') # or yolov3-spp, yolov3-tiny, custom 89 | 90 | # Images 91 | img = 'https://ultralytics.com/images/zidane.jpg' # or file, Path, PIL, OpenCV, numpy, list 92 | 93 | # Inference 94 | results = model(img) 95 | 96 | # Results 97 | results.print() # or .show(), .save(), .crop(), .pandas(), etc. 98 | ``` 99 | 100 |
101 | 102 | 103 | 104 |
105 | Inference with detect.py 106 | 107 | `detect.py` runs inference on a variety of sources, downloading models automatically from 108 | the [latest YOLOv3 release](https://github.com/ultralytics/yolov3/releases) and saving results to `runs/detect`. 109 | 110 | ```bash 111 | $ python detect.py --source 0 # webcam 112 | img.jpg # image 113 | vid.mp4 # video 114 | path/ # directory 115 | path/*.jpg # glob 116 | 'https://youtu.be/Zgi9g1ksQHc' # YouTube 117 | 'rtsp://example.com/media.mp4' # RTSP, RTMP, HTTP stream 118 | ``` 119 | 120 |
121 | 122 |
123 | Training 124 | 125 | 126 | 127 | 128 | 129 |
130 | 131 |
132 | Tutorials 133 | 134 | * [Train Custom Data](https://github.com/ultralytics/yolov3/wiki/Train-Custom-Data)  🚀 RECOMMENDED 135 | * [Tips for Best Training Results](https://github.com/ultralytics/yolov3/wiki/Tips-for-Best-Training-Results)  ☘️ 136 | RECOMMENDED 137 | * [Weights & Biases Logging](https://github.com/ultralytics/yolov5/issues/1289)  🌟 NEW 138 | * [Roboflow for Datasets, Labeling, and Active Learning](https://github.com/ultralytics/yolov5/issues/4975)  🌟 NEW 139 | * [Multi-GPU Training](https://github.com/ultralytics/yolov5/issues/475) 140 | * [PyTorch Hub](https://github.com/ultralytics/yolov5/issues/36)  ⭐ NEW 141 | * [TorchScript, ONNX, CoreML Export](https://github.com/ultralytics/yolov5/issues/251) 🚀 142 | * [Test-Time Augmentation (TTA)](https://github.com/ultralytics/yolov5/issues/303) 143 | * [Model Ensembling](https://github.com/ultralytics/yolov5/issues/318) 144 | * [Model Pruning/Sparsity](https://github.com/ultralytics/yolov5/issues/304) 145 | * [Hyperparameter Evolution](https://github.com/ultralytics/yolov5/issues/607) 146 | * [Transfer Learning with Frozen Layers](https://github.com/ultralytics/yolov5/issues/1314)  ⭐ NEW 147 | * [TensorRT Deployment](https://github.com/wang-xinyu/tensorrtx) 148 | 149 |
150 | 151 | ##
Environments
152 | 153 | Get started in seconds with our verified environments. Click each icon below for details. 154 | 155 |
156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 |
172 | 173 | ##
Integrations
174 | 175 |
176 | 177 | 178 | 179 | 180 | 181 | 182 |
183 | 184 | |Weights and Biases|Roboflow ⭐ NEW| 185 | |:-:|:-:| 186 | |Automatically track and visualize all your YOLOv3 training runs in the cloud with [Weights & Biases](https://wandb.ai/site?utm_campaign=repo_yolo_readme)|Label and export your custom datasets directly to YOLOv3 for training with [Roboflow](https://roboflow.com/?ref=ultralytics) | 187 | 188 | 189 | ##
Why YOLOv5
190 | 191 |

192 |
193 | YOLOv3-P5 640 Figure (click to expand) 194 | 195 |

196 |
197 |
198 | Figure Notes (click to expand) 199 | 200 | * **COCO AP val** denotes mAP@0.5:0.95 metric measured on the 5000-image [COCO val2017](http://cocodataset.org) dataset over various inference sizes from 256 to 1536. 201 | * **GPU Speed** measures average inference time per image on [COCO val2017](http://cocodataset.org) dataset using a [AWS p3.2xlarge](https://aws.amazon.com/ec2/instance-types/p3/) V100 instance at batch-size 32. 202 | * **EfficientDet** data from [google/automl](https://github.com/google/automl) at batch size 8. 203 | * **Reproduce** by `python val.py --task study --data coco.yaml --iou 0.7 --weights yolov5n6.pt yolov5s6.pt yolov5m6.pt yolov5l6.pt yolov5x6.pt` 204 |
205 | 206 | ### Pretrained Checkpoints 207 | 208 | [assets]: https://github.com/ultralytics/yolov5/releases 209 | [TTA]: https://github.com/ultralytics/yolov5/issues/303 210 | 211 | |Model |size
(pixels) |mAPval
0.5:0.95 |mAPval
0.5 |Speed
CPU b1
(ms) |Speed
V100 b1
(ms) |Speed
V100 b32
(ms) |params
(M) |FLOPs
@640 (B) 212 | |--- |--- |--- |--- |--- |--- |--- |--- |--- 213 | |[YOLOv5n][assets] |640 |28.4 |46.0 |**45** |**6.3**|**0.6**|**1.9**|**4.5** 214 | |[YOLOv5s][assets] |640 |37.2 |56.0 |98 |6.4 |0.9 |7.2 |16.5 215 | |[YOLOv5m][assets] |640 |45.2 |63.9 |224 |8.2 |1.7 |21.2 |49.0 216 | |[YOLOv5l][assets] |640 |48.8 |67.2 |430 |10.1 |2.7 |46.5 |109.1 217 | |[YOLOv5x][assets] |640 |50.7 |68.9 |766 |12.1 |4.8 |86.7 |205.7 218 | | | | | | | | | | 219 | |[YOLOv5n6][assets] |1280 |34.0 |50.7 |153 |8.1 |2.1 |3.2 |4.6 220 | |[YOLOv5s6][assets] |1280 |44.5 |63.0 |385 |8.2 |3.6 |16.8 |12.6 221 | |[YOLOv5m6][assets] |1280 |51.0 |69.0 |887 |11.1 |6.8 |35.7 |50.0 222 | |[YOLOv5l6][assets] |1280 |53.6 |71.6 |1784 |15.8 |10.5 |76.8 |111.4 223 | |[YOLOv5x6][assets]
+ [TTA][TTA]|1280
1536 |54.7
**55.4** |**72.4**
72.3 |3136
- |26.2
- |19.4
- |140.7
- |209.8
- 224 | 225 |
226 | Table Notes (click to expand) 227 | 228 | * All checkpoints are trained to 300 epochs with default settings and hyperparameters. 229 | * **mAPval** values are for single-model single-scale on [COCO val2017](http://cocodataset.org) dataset.
Reproduce by `python val.py --data coco.yaml --img 640 --conf 0.001 --iou 0.65` 230 | * **Speed** averaged over COCO val images using a [AWS p3.2xlarge](https://aws.amazon.com/ec2/instance-types/p3/) instance. NMS times (~1 ms/img) not included.
Reproduce by `python val.py --data coco.yaml --img 640 --conf 0.25 --iou 0.45` 231 | * **TTA** [Test Time Augmentation](https://github.com/ultralytics/yolov5/issues/303) includes reflection and scale augmentations.
Reproduce by `python val.py --data coco.yaml --img 1536 --iou 0.7 --augment` 232 | 233 |
234 | 235 | ##
Contribute
236 | 237 | We love your input! We want to make contributing to YOLOv3 as easy and transparent as possible. Please see our [Contributing Guide](CONTRIBUTING.md) to get started, and fill out the [YOLOv3 Survey](https://ultralytics.com/survey?utm_source=github&utm_medium=social&utm_campaign=Survey) to send us feedback on your experiences. Thank you to all our contributors! 238 | 239 | 240 | 241 | 242 | ##
Contact
243 | 244 | For YOLOv3 bugs and feature requests please visit [GitHub Issues](https://github.com/ultralytics/yolov3/issues). For business inquiries or 245 | professional support requests please visit [https://ultralytics.com/contact](https://ultralytics.com/contact). 246 | 247 |
248 | 249 |
250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 |
274 | -------------------------------------------------------------------------------- /utils/torch_utils.py: -------------------------------------------------------------------------------- 1 | # YOLOv3 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | PyTorch utils 4 | """ 5 | 6 | import datetime 7 | import math 8 | import os 9 | import platform 10 | import subprocess 11 | import time 12 | from contextlib import contextmanager 13 | from copy import deepcopy 14 | from pathlib import Path 15 | 16 | import torch 17 | import torch.distributed as dist 18 | import torch.nn as nn 19 | import torch.nn.functional as F 20 | 21 | from utils.general import LOGGER 22 | 23 | try: 24 | import thop # for FLOPs computation 25 | except ImportError: 26 | thop = None 27 | 28 | 29 | @contextmanager 30 | def torch_distributed_zero_first(local_rank: int): 31 | """ 32 | Decorator to make all processes in distributed training wait for each local_master to do something. 33 | """ 34 | if local_rank not in [-1, 0]: 35 | dist.barrier(device_ids=[local_rank]) 36 | yield 37 | if local_rank == 0: 38 | dist.barrier(device_ids=[0]) 39 | 40 | 41 | def date_modified(path=__file__): 42 | # return human-readable file modification date, i.e. '2021-3-26' 43 | t = datetime.datetime.fromtimestamp(Path(path).stat().st_mtime) 44 | return f'{t.year}-{t.month}-{t.day}' 45 | 46 | 47 | def git_describe(path=Path(__file__).parent): # path must be a directory 48 | # return human-readable git description, i.e. v5.0-5-g3e25f1e https://git-scm.com/docs/git-describe 49 | s = f'git -C {path} describe --tags --long --always' 50 | try: 51 | return subprocess.check_output(s, shell=True, stderr=subprocess.STDOUT).decode()[:-1] 52 | except subprocess.CalledProcessError as e: 53 | return '' # not a git repository 54 | 55 | 56 | def select_device(device='', batch_size=None, newline=True): 57 | # device = 'cpu' or '0' or '0,1,2,3' 58 | s = f'YOLOv3 🚀 {git_describe() or date_modified()} torch {torch.__version__} ' # string 59 | device = str(device).strip().lower().replace('cuda:', '') # to string, 'cuda:0' to '0' 60 | cpu = device == 'cpu' 61 | if cpu: 62 | os.environ['CUDA_VISIBLE_DEVICES'] = '-1' # force torch.cuda.is_available() = False 63 | elif device: # non-cpu device requested 64 | os.environ['CUDA_VISIBLE_DEVICES'] = device # set environment variable 65 | assert torch.cuda.is_available(), f'CUDA unavailable, invalid device {device} requested' # check availability 66 | 67 | cuda = not cpu and torch.cuda.is_available() 68 | if cuda: 69 | devices = device.split(',') if device else '0' # range(torch.cuda.device_count()) # i.e. 0,1,6,7 70 | n = len(devices) # device count 71 | if n > 1 and batch_size: # check batch_size is divisible by device_count 72 | assert batch_size % n == 0, f'batch-size {batch_size} not multiple of GPU count {n}' 73 | space = ' ' * (len(s) + 1) 74 | for i, d in enumerate(devices): 75 | p = torch.cuda.get_device_properties(i) 76 | s += f"{'' if i == 0 else space}CUDA:{d} ({p.name}, {p.total_memory / 1024 ** 2:.0f}MiB)\n" # bytes to MB 77 | else: 78 | s += 'CPU\n' 79 | 80 | if not newline: 81 | s = s.rstrip() 82 | LOGGER.info(s.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else s) # emoji-safe 83 | return torch.device('cuda:0' if cuda else 'cpu') 84 | 85 | 86 | def time_sync(): 87 | # pytorch-accurate time 88 | if torch.cuda.is_available(): 89 | torch.cuda.synchronize() 90 | return time.time() 91 | 92 | 93 | def profile(input, ops, n=10, device=None): 94 | # speed/memory/FLOPs profiler 95 | # 96 | # Usage: 97 | # input = torch.randn(16, 3, 640, 640) 98 | # m1 = lambda x: x * torch.sigmoid(x) 99 | # m2 = nn.SiLU() 100 | # profile(input, [m1, m2], n=100) # profile over 100 iterations 101 | 102 | results = [] 103 | device = device or select_device() 104 | print(f"{'Params':>12s}{'GFLOPs':>12s}{'GPU_mem (GB)':>14s}{'forward (ms)':>14s}{'backward (ms)':>14s}" 105 | f"{'input':>24s}{'output':>24s}") 106 | 107 | for x in input if isinstance(input, list) else [input]: 108 | x = x.to(device) 109 | x.requires_grad = True 110 | for m in ops if isinstance(ops, list) else [ops]: 111 | m = m.to(device) if hasattr(m, 'to') else m # device 112 | m = m.half() if hasattr(m, 'half') and isinstance(x, torch.Tensor) and x.dtype is torch.float16 else m 113 | tf, tb, t = 0, 0, [0, 0, 0] # dt forward, backward 114 | try: 115 | flops = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2 # GFLOPs 116 | except: 117 | flops = 0 118 | 119 | try: 120 | for _ in range(n): 121 | t[0] = time_sync() 122 | y = m(x) 123 | t[1] = time_sync() 124 | try: 125 | _ = (sum(yi.sum() for yi in y) if isinstance(y, list) else y).sum().backward() 126 | t[2] = time_sync() 127 | except Exception as e: # no backward method 128 | # print(e) # for debug 129 | t[2] = float('nan') 130 | tf += (t[1] - t[0]) * 1000 / n # ms per op forward 131 | tb += (t[2] - t[1]) * 1000 / n # ms per op backward 132 | mem = torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0 # (GB) 133 | s_in = tuple(x.shape) if isinstance(x, torch.Tensor) else 'list' 134 | s_out = tuple(y.shape) if isinstance(y, torch.Tensor) else 'list' 135 | p = sum(list(x.numel() for x in m.parameters())) if isinstance(m, nn.Module) else 0 # parameters 136 | print(f'{p:12}{flops:12.4g}{mem:>14.3f}{tf:14.4g}{tb:14.4g}{str(s_in):>24s}{str(s_out):>24s}') 137 | results.append([p, flops, mem, tf, tb, s_in, s_out]) 138 | except Exception as e: 139 | print(e) 140 | results.append(None) 141 | torch.cuda.empty_cache() 142 | return results 143 | 144 | 145 | def is_parallel(model): 146 | # Returns True if model is of type DP or DDP 147 | return type(model) in (nn.parallel.DataParallel, nn.parallel.DistributedDataParallel) 148 | 149 | 150 | def de_parallel(model): 151 | # De-parallelize a model: returns single-GPU model if model is of type DP or DDP 152 | return model.module if is_parallel(model) else model 153 | 154 | 155 | def initialize_weights(model): 156 | for m in model.modules(): 157 | t = type(m) 158 | if t is nn.Conv2d: 159 | pass # nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') 160 | elif t is nn.BatchNorm2d: 161 | m.eps = 1e-3 162 | m.momentum = 0.03 163 | elif t in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU]: 164 | m.inplace = True 165 | 166 | 167 | def find_modules(model, mclass=nn.Conv2d): 168 | # Finds layer indices matching module class 'mclass' 169 | return [i for i, m in enumerate(model.module_list) if isinstance(m, mclass)] 170 | 171 | 172 | def sparsity(model): 173 | # Return global model sparsity 174 | a, b = 0, 0 175 | for p in model.parameters(): 176 | a += p.numel() 177 | b += (p == 0).sum() 178 | return b / a 179 | 180 | 181 | def prune(model, amount=0.3): 182 | # Prune model to requested global sparsity 183 | import torch.nn.utils.prune as prune 184 | print('Pruning model... ', end='') 185 | for name, m in model.named_modules(): 186 | if isinstance(m, nn.Conv2d): 187 | prune.l1_unstructured(m, name='weight', amount=amount) # prune 188 | prune.remove(m, 'weight') # make permanent 189 | print(' %.3g global sparsity' % sparsity(model)) 190 | 191 | 192 | def fuse_conv_and_bn(conv, bn): 193 | # Fuse convolution and batchnorm layers https://tehnokv.com/posts/fusing-batchnorm-and-conv/ 194 | fusedconv = nn.Conv2d(conv.in_channels, 195 | conv.out_channels, 196 | kernel_size=conv.kernel_size, 197 | stride=conv.stride, 198 | padding=conv.padding, 199 | groups=conv.groups, 200 | bias=True).requires_grad_(False).to(conv.weight.device) 201 | 202 | # prepare filters 203 | w_conv = conv.weight.clone().view(conv.out_channels, -1) 204 | w_bn = torch.diag(bn.weight.div(torch.sqrt(bn.eps + bn.running_var))) 205 | fusedconv.weight.copy_(torch.mm(w_bn, w_conv).view(fusedconv.weight.shape)) 206 | 207 | # prepare spatial bias 208 | b_conv = torch.zeros(conv.weight.size(0), device=conv.weight.device) if conv.bias is None else conv.bias 209 | b_bn = bn.bias - bn.weight.mul(bn.running_mean).div(torch.sqrt(bn.running_var + bn.eps)) 210 | fusedconv.bias.copy_(torch.mm(w_bn, b_conv.reshape(-1, 1)).reshape(-1) + b_bn) 211 | 212 | return fusedconv 213 | 214 | 215 | def model_info(model, verbose=False, img_size=640): 216 | # Model information. img_size may be int or list, i.e. img_size=640 or img_size=[640, 320] 217 | n_p = sum(x.numel() for x in model.parameters()) # number parameters 218 | n_g = sum(x.numel() for x in model.parameters() if x.requires_grad) # number gradients 219 | if verbose: 220 | print(f"{'layer':>5} {'name':>40} {'gradient':>9} {'parameters':>12} {'shape':>20} {'mu':>10} {'sigma':>10}") 221 | for i, (name, p) in enumerate(model.named_parameters()): 222 | name = name.replace('module_list.', '') 223 | print('%5g %40s %9s %12g %20s %10.3g %10.3g' % 224 | (i, name, p.requires_grad, p.numel(), list(p.shape), p.mean(), p.std())) 225 | 226 | try: # FLOPs 227 | from thop import profile 228 | stride = max(int(model.stride.max()), 32) if hasattr(model, 'stride') else 32 229 | img = torch.zeros((1, model.yaml.get('ch', 3), stride, stride), device=next(model.parameters()).device) # input 230 | flops = profile(deepcopy(model), inputs=(img,), verbose=False)[0] / 1E9 * 2 # stride GFLOPs 231 | img_size = img_size if isinstance(img_size, list) else [img_size, img_size] # expand if int/float 232 | fs = ', %.1f GFLOPs' % (flops * img_size[0] / stride * img_size[1] / stride) # 640x640 GFLOPs 233 | except (ImportError, Exception): 234 | fs = '' 235 | 236 | LOGGER.info(f"Model Summary: {len(list(model.modules()))} layers, {n_p} parameters, {n_g} gradients{fs}") 237 | 238 | 239 | def scale_img(img, ratio=1.0, same_shape=False, gs=32): # img(16,3,256,416) 240 | # scales img(bs,3,y,x) by ratio constrained to gs-multiple 241 | if ratio == 1.0: 242 | return img 243 | else: 244 | h, w = img.shape[2:] 245 | s = (int(h * ratio), int(w * ratio)) # new size 246 | img = F.interpolate(img, size=s, mode='bilinear', align_corners=False) # resize 247 | if not same_shape: # pad/crop img 248 | h, w = (math.ceil(x * ratio / gs) * gs for x in (h, w)) 249 | return F.pad(img, [0, w - s[1], 0, h - s[0]], value=0.447) # value = imagenet mean 250 | 251 | 252 | def copy_attr(a, b, include=(), exclude=()): 253 | # Copy attributes from b to a, options to only include [...] and to exclude [...] 254 | for k, v in b.__dict__.items(): 255 | if (len(include) and k not in include) or k.startswith('_') or k in exclude: 256 | continue 257 | else: 258 | setattr(a, k, v) 259 | 260 | 261 | class EarlyStopping: 262 | # simple early stopper 263 | def __init__(self, patience=30): 264 | self.best_fitness = 0.0 # i.e. mAP 265 | self.best_epoch = 0 266 | self.patience = patience or float('inf') # epochs to wait after fitness stops improving to stop 267 | self.possible_stop = False # possible stop may occur next epoch 268 | 269 | def __call__(self, epoch, fitness): 270 | if fitness >= self.best_fitness: # >= 0 to allow for early zero-fitness stage of training 271 | self.best_epoch = epoch 272 | self.best_fitness = fitness 273 | delta = epoch - self.best_epoch # epochs without improvement 274 | self.possible_stop = delta >= (self.patience - 1) # possible stop may occur next epoch 275 | stop = delta >= self.patience # stop training if patience exceeded 276 | if stop: 277 | LOGGER.info(f'Stopping training early as no improvement observed in last {self.patience} epochs. ' 278 | f'Best results observed at epoch {self.best_epoch}, best model saved as best.pt.\n' 279 | f'To update EarlyStopping(patience={self.patience}) pass a new patience value, ' 280 | f'i.e. `python train.py --patience 300` or use `--patience 0` to disable EarlyStopping.') 281 | return stop 282 | 283 | 284 | class ModelEMA: 285 | """ Model Exponential Moving Average from https://github.com/rwightman/pytorch-image-models 286 | Keep a moving average of everything in the model state_dict (parameters and buffers). 287 | This is intended to allow functionality like 288 | https://www.tensorflow.org/api_docs/python/tf/train/ExponentialMovingAverage 289 | A smoothed version of the weights is necessary for some training schemes to perform well. 290 | This class is sensitive where it is initialized in the sequence of model init, 291 | GPU assignment and distributed training wrappers. 292 | """ 293 | 294 | def __init__(self, model, decay=0.9999, updates=0): 295 | # Create EMA 296 | self.ema = deepcopy(model.module if is_parallel(model) else model).eval() # FP32 EMA 297 | # if next(model.parameters()).device.type != 'cpu': 298 | # self.ema.half() # FP16 EMA 299 | self.updates = updates # number of EMA updates 300 | self.decay = lambda x: decay * (1 - math.exp(-x / 2000)) # decay exponential ramp (to help early epochs) 301 | for p in self.ema.parameters(): 302 | p.requires_grad_(False) 303 | 304 | def update(self, model): 305 | # Update EMA parameters 306 | with torch.no_grad(): 307 | self.updates += 1 308 | d = self.decay(self.updates) 309 | 310 | msd = model.module.state_dict() if is_parallel(model) else model.state_dict() # model state_dict 311 | for k, v in self.ema.state_dict().items(): 312 | if v.dtype.is_floating_point: 313 | v *= d 314 | v += (1 - d) * msd[k].detach() 315 | 316 | def update_attr(self, model, include=(), exclude=('process_group', 'reducer')): 317 | # Update EMA attributes 318 | copy_attr(self.ema, model, include, exclude) 319 | --------------------------------------------------------------------------------