├── Files ├── plots.py └── predict.py ├── LICENSE ├── README.md ├── Single_Click_YOLOv7_Instance_Segmentation_withDistance_Estimation.ipynb ├── YOLOv7_Segmentation_With_DeepSORT_Tracking.ipynb ├── classify ├── predict.py ├── train.py └── val.py ├── data ├── coco.names ├── coco.yaml ├── hyps │ ├── hyp.scratch-high.yaml │ ├── hyp.scratch-low.yaml │ └── hyp.scratch-med.yaml └── scripts │ ├── get_coco.sh │ └── get_imagenet.sh ├── detect.py ├── export.py ├── figure ├── 1.png ├── 2.png ├── 3.png ├── Screenshot (350).png ├── Screenshot (351).png ├── Screenshot (352).png └── yolov7-seg-example.png ├── hubconf.py ├── models ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-310.pyc │ ├── common.cpython-310.pyc │ ├── experimental.cpython-310.pyc │ └── yolo.cpython-310.pyc ├── common.py ├── experimental.py ├── hub │ ├── yolov3-spp.yaml │ ├── yolov3-tiny.yaml │ └── yolov3.yaml ├── segment │ └── yolov7-seg.yaml ├── tf.py └── yolo.py ├── requirements.txt ├── runs └── predict-seg │ └── exp │ └── Demo.mp4 ├── segment ├── predict.py ├── train.py └── val.py ├── setup.cfg ├── train.py ├── utils ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-310.pyc │ ├── augmentations.cpython-310.pyc │ ├── autoanchor.cpython-310.pyc │ ├── dataloaders.cpython-310.pyc │ ├── downloads.cpython-310.pyc │ ├── general.cpython-310.pyc │ ├── metrics.cpython-310.pyc │ ├── plots.cpython-310.pyc │ └── torch_utils.cpython-310.pyc ├── activations.py ├── augmentations.py ├── autoanchor.py ├── autobatch.py ├── aws │ ├── __init__.py │ ├── mime.sh │ ├── resume.py │ └── userdata.sh ├── benchmarks.py ├── callbacks.py ├── dataloaders.py ├── docker │ ├── Dockerfile │ ├── Dockerfile-arm64 │ └── Dockerfile-cpu ├── downloads.py ├── flask_rest_api │ ├── README.md │ ├── example_request.py │ └── restapi.py ├── general.py ├── google_app_engine │ ├── Dockerfile │ ├── additional_requirements.txt │ └── app.yaml ├── loggers │ ├── __init__.py │ ├── clearml │ │ ├── README.md │ │ ├── __init__.py │ │ ├── clearml_utils.py │ │ └── hpo.py │ └── wandb │ │ ├── README.md │ │ ├── __init__.py │ │ ├── log_dataset.py │ │ ├── sweep.py │ │ ├── sweep.yaml │ │ └── wandb_utils.py ├── loss.py ├── metrics.py ├── plots.py ├── segment │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-310.pyc │ │ ├── general.cpython-310.pyc │ │ └── plots.cpython-310.pyc │ ├── augmentations.py │ ├── dataloaders.py │ ├── general.py │ ├── loss.py │ ├── metrics.py │ └── plots.py └── torch_utils.py └── val.py /README.md: -------------------------------------------------------------------------------- 1 |

2 | YOLOv7 Segmentation with DeepSORT Tracking

3 | 4 | 5 | I have implemented the YOLOv7 Segmantation with DeepSORT Tracking, please support by giving a star. 6 | 7 | [`yolov7-seg.pt`](https://github.com/WongKinYiu/yolov7/releases/download/v0.1/yolov7-seg.pt) 8 | 9 | ## Steps to run Code 10 | 11 | - Clone the repository 12 | ``` 13 | https://github.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking.git 14 | ``` 15 | - Goto the cloned folder. 16 | ``` 17 | cd yolov7-segmentation-with-DeepSORT-Tracking 18 | ``` 19 | - Create a virtual envirnoment (Recommended, If you dont want to disturb python packages) 20 | ``` 21 | ### For Linux Users 22 | python3 -m venv yolov7seg 23 | source yolov7seg/bin/activate 24 | 25 | ### For Window Users 26 | python3 -m venv yolov7seg 27 | cd yolov7seg 28 | cd Scripts 29 | activate 30 | cd .. 31 | cd .. 32 | ``` 33 | 34 | - Downloading the DeepSORT Files From The Google Drive 35 | ``` 36 | cd yolov7-segmentation-with-DeepSORT-Tracking 37 | gdown "https://drive.google.com/uc?id=1BNZ1S5yflbQpbHeRM1fNeu_2WfuP17hJ&confirm=t" 38 | unzip /content/yolov7-segmentation-with-DeepSORT-Tracking/deep_sort_pytorch.zip 39 | ``` 40 | 41 | - Downloading a Sample Video from the Google Drive 42 | ``` 43 | gdown "https://drive.google.com/uc?id=1o-G_Fs-XtF_Nn_fQdri6GZWYtD_3_Ckw&confirm=t" 44 | ``` 45 | 46 | - Upgrade pip with mentioned command below. 47 | ``` 48 | pip install --upgrade pip 49 | ``` 50 | - Install requirements with mentioned command below. 51 | ``` 52 | pip install -r requirements.txt 53 | ``` 54 | - Download weights from [link](https://github.com/RizwanMunawar/yolov7-segmentation/releases/download/yolov7-segmentation/yolov7-seg.pt) and store in "yolov7-segmentation-with-DeepSORT-Tracking" directory. 55 | 56 | - Run the code with mentioned command below. 57 | ``` 58 | #for segmentation with detection + Tracking 59 | python3 segment/predict.py --source videosfinal.mp4 --weights yolov7-seg.pt --track 60 | ``` 61 | 62 | - Output file will be created in the working directory with name runs/predict-seg/exp/"original-video-name.mp4" 63 | 64 | ## Colab File Link 65 | The google colab file link is provided below, you can check the implementation in Google Colab, and its a single click implementation, you just need to select the Run Time as GPU, and click on Run All. 66 | 67 | [`Google Colab File`](https://colab.research.google.com/drive/1I6_UpDniCsOrL5fj_bTX337Jo_UWhPGQ?usp=sharing) 68 | 69 | 70 | ## DeepSORT Files 71 | 72 | The DeepSORT files are uplaoded in the Google Drive Link Below. 73 | [`DeepSORT Files`](https://drive.google.com/drive/folders/1YjbfZL0n6mQH-hvD_DpoxBLVj9prJMpG) 74 | 75 | ### RESULTS 76 | 77 | #### Vehicles Semantic Segmentation 78 | ![](./figure/1.png) 79 | 80 | #### Vehicles Sematic Segmentation 81 | 82 | ![](./figure/2.png) 83 | 84 | #### Vehicles Semantic Segmentation 85 | 86 | ![](./figure/3.png) 87 | 88 | ## References 89 | - https://github.com/WongKinYiu/yolov7.git 90 | -------------------------------------------------------------------------------- /classify/predict.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Run YOLOv5 classification inference on images, videos, directories, globs, YouTube, webcam, streams, etc. 4 | 5 | Usage - sources: 6 | $ python classify/predict.py --weights yolov5s-cls.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 | Usage - formats: 15 | $ python classify/predict.py --weights yolov5s-cls.pt # PyTorch 16 | yolov5s-cls.torchscript # TorchScript 17 | yolov5s-cls.onnx # ONNX Runtime or OpenCV DNN with --dnn 18 | yolov5s-cls.xml # OpenVINO 19 | yolov5s-cls.engine # TensorRT 20 | yolov5s-cls.mlmodel # CoreML (macOS-only) 21 | yolov5s-cls_saved_model # TensorFlow SavedModel 22 | yolov5s-cls.pb # TensorFlow GraphDef 23 | yolov5s-cls.tflite # TensorFlow Lite 24 | yolov5s-cls_edgetpu.tflite # TensorFlow Edge TPU 25 | """ 26 | 27 | import argparse 28 | import os 29 | import platform 30 | import sys 31 | from pathlib import Path 32 | 33 | import torch 34 | import torch.backends.cudnn as cudnn 35 | import torch.nn.functional as F 36 | 37 | FILE = Path(__file__).resolve() 38 | ROOT = FILE.parents[1] # YOLOv5 root directory 39 | if str(ROOT) not in sys.path: 40 | sys.path.append(str(ROOT)) # add ROOT to PATH 41 | ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative 42 | 43 | from models.common import DetectMultiBackend 44 | from utils.augmentations import classify_transforms 45 | from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadStreams 46 | from utils.general import (LOGGER, Profile, check_file, check_img_size, check_imshow, check_requirements, colorstr, cv2, 47 | increment_path, print_args, strip_optimizer) 48 | from utils.plots import Annotator 49 | from utils.torch_utils import select_device, smart_inference_mode 50 | 51 | 52 | @smart_inference_mode() 53 | def run( 54 | weights=ROOT / 'yolov5s-cls.pt', # model.pt path(s) 55 | source=ROOT / 'data/images', # file/dir/URL/glob, 0 for webcam 56 | data=ROOT / 'data/coco128.yaml', # dataset.yaml path 57 | imgsz=(224, 224), # inference size (height, width) 58 | device='', # cuda device, i.e. 0 or 0,1,2,3 or cpu 59 | view_img=False, # show results 60 | save_txt=False, # save results to *.txt 61 | nosave=False, # do not save images/videos 62 | augment=False, # augmented inference 63 | visualize=False, # visualize features 64 | update=False, # update all models 65 | project=ROOT / 'runs/predict-cls', # save results to project/name 66 | name='exp', # save results to project/name 67 | exist_ok=False, # existing project/name ok, do not increment 68 | half=False, # use FP16 half-precision inference 69 | dnn=False, # use OpenCV DNN for ONNX inference 70 | ): 71 | source = str(source) 72 | save_img = not nosave and not source.endswith('.txt') # save inference images 73 | is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS) 74 | is_url = source.lower().startswith(('rtsp://', 'rtmp://', 'http://', 'https://')) 75 | webcam = source.isnumeric() or source.endswith('.txt') or (is_url and not is_file) 76 | if is_url and is_file: 77 | source = check_file(source) # download 78 | 79 | # Directories 80 | save_dir = increment_path(Path(project) / name, exist_ok=exist_ok) # increment run 81 | (save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir 82 | 83 | # Load model 84 | device = select_device(device) 85 | model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half) 86 | stride, names, pt = model.stride, model.names, model.pt 87 | imgsz = check_img_size(imgsz, s=stride) # check image size 88 | 89 | # Dataloader 90 | if webcam: 91 | view_img = check_imshow() 92 | cudnn.benchmark = True # set True to speed up constant image size inference 93 | dataset = LoadStreams(source, img_size=imgsz, transforms=classify_transforms(imgsz[0])) 94 | bs = len(dataset) # batch_size 95 | else: 96 | dataset = LoadImages(source, img_size=imgsz, transforms=classify_transforms(imgsz[0])) 97 | bs = 1 # batch_size 98 | vid_path, vid_writer = [None] * bs, [None] * bs 99 | 100 | # Run inference 101 | model.warmup(imgsz=(1 if pt else bs, 3, *imgsz)) # warmup 102 | seen, windows, dt = 0, [], (Profile(), Profile(), Profile()) 103 | for path, im, im0s, vid_cap, s in dataset: 104 | with dt[0]: 105 | im = torch.Tensor(im).to(device) 106 | im = im.half() if model.fp16 else im.float() # uint8 to fp16/32 107 | if len(im.shape) == 3: 108 | im = im[None] # expand for batch dim 109 | 110 | # Inference 111 | with dt[1]: 112 | results = model(im) 113 | 114 | # Post-process 115 | with dt[2]: 116 | pred = F.softmax(results, dim=1) # probabilities 117 | 118 | # Process predictions 119 | for i, prob in enumerate(pred): # per image 120 | seen += 1 121 | if webcam: # batch_size >= 1 122 | p, im0 = path[i], im0s[i].copy() 123 | s += f'{i}: ' 124 | else: 125 | p, im0 = path, im0s.copy() 126 | 127 | p = Path(p) # to Path 128 | save_path = str(save_dir / p.name) # im.jpg 129 | s += '%gx%g ' % im.shape[2:] # print string 130 | annotator = Annotator(im0, example=str(names), pil=True) 131 | 132 | # Print results 133 | top5i = prob.argsort(0, descending=True)[:5].tolist() # top 5 indices 134 | s += f"{', '.join(f'{names[j]} {prob[j]:.2f}' for j in top5i)}, " 135 | 136 | # Write results 137 | if save_img or view_img: # Add bbox to image 138 | text = '\n'.join(f'{prob[j]:.2f} {names[j]}' for j in top5i) 139 | annotator.text((32, 32), text, txt_color=(255, 255, 255)) 140 | 141 | # Stream results 142 | im0 = annotator.result() 143 | if view_img: 144 | if platform.system() == 'Linux' and p not in windows: 145 | windows.append(p) 146 | cv2.namedWindow(str(p), cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO) # allow window resize (Linux) 147 | cv2.resizeWindow(str(p), im0.shape[1], im0.shape[0]) 148 | cv2.imshow(str(p), im0) 149 | cv2.waitKey(1) # 1 millisecond 150 | 151 | # Save results (image with detections) 152 | if save_img: 153 | if dataset.mode == 'image': 154 | cv2.imwrite(save_path, im0) 155 | else: # 'video' or 'stream' 156 | if vid_path[i] != save_path: # new video 157 | vid_path[i] = save_path 158 | if isinstance(vid_writer[i], cv2.VideoWriter): 159 | vid_writer[i].release() # release previous video writer 160 | if vid_cap: # video 161 | fps = vid_cap.get(cv2.CAP_PROP_FPS) 162 | w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH)) 163 | h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) 164 | else: # stream 165 | fps, w, h = 30, im0.shape[1], im0.shape[0] 166 | save_path = str(Path(save_path).with_suffix('.mp4')) # force *.mp4 suffix on results videos 167 | vid_writer[i] = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h)) 168 | vid_writer[i].write(im0) 169 | 170 | # Print time (inference-only) 171 | LOGGER.info(f"{s}{dt[1].dt * 1E3:.1f}ms") 172 | 173 | # Print results 174 | t = tuple(x.t / seen * 1E3 for x in dt) # speeds per image 175 | LOGGER.info(f'Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {(1, 3, *imgsz)}' % t) 176 | if save_txt or save_img: 177 | s = f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else '' 178 | LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}{s}") 179 | if update: 180 | strip_optimizer(weights[0]) # update model (to fix SourceChangeWarning) 181 | 182 | 183 | def parse_opt(): 184 | parser = argparse.ArgumentParser() 185 | parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'yolov5s-cls.pt', help='model path(s)') 186 | parser.add_argument('--source', type=str, default=ROOT / 'data/images', help='file/dir/URL/glob, 0 for webcam') 187 | parser.add_argument('--data', type=str, default=ROOT / 'data/coco128.yaml', help='(optional) dataset.yaml path') 188 | parser.add_argument('--imgsz', '--img', '--img-size', nargs='+', type=int, default=[224], help='inference size h,w') 189 | parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') 190 | parser.add_argument('--view-img', action='store_true', help='show results') 191 | parser.add_argument('--save-txt', action='store_true', help='save results to *.txt') 192 | parser.add_argument('--nosave', action='store_true', help='do not save images/videos') 193 | parser.add_argument('--augment', action='store_true', help='augmented inference') 194 | parser.add_argument('--visualize', action='store_true', help='visualize features') 195 | parser.add_argument('--update', action='store_true', help='update all models') 196 | parser.add_argument('--project', default=ROOT / 'runs/predict-cls', help='save results to project/name') 197 | parser.add_argument('--name', default='exp', help='save results to project/name') 198 | parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment') 199 | parser.add_argument('--half', action='store_true', help='use FP16 half-precision inference') 200 | parser.add_argument('--dnn', action='store_true', help='use OpenCV DNN for ONNX inference') 201 | opt = parser.parse_args() 202 | opt.imgsz *= 2 if len(opt.imgsz) == 1 else 1 # expand 203 | print_args(vars(opt)) 204 | return opt 205 | 206 | 207 | def main(opt): 208 | check_requirements(exclude=('tensorboard', 'thop')) 209 | run(**vars(opt)) 210 | 211 | 212 | if __name__ == "__main__": 213 | opt = parse_opt() 214 | main(opt) 215 | -------------------------------------------------------------------------------- /classify/val.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Validate a trained YOLOv5 classification model on a classification dataset 4 | 5 | Usage: 6 | $ bash data/scripts/get_imagenet.sh --val # download ImageNet val split (6.3G, 50000 images) 7 | $ python classify/val.py --weights yolov5m-cls.pt --data ../datasets/imagenet --img 224 # validate ImageNet 8 | 9 | Usage - formats: 10 | $ python classify/val.py --weights yolov5s-cls.pt # PyTorch 11 | yolov5s-cls.torchscript # TorchScript 12 | yolov5s-cls.onnx # ONNX Runtime or OpenCV DNN with --dnn 13 | yolov5s-cls.xml # OpenVINO 14 | yolov5s-cls.engine # TensorRT 15 | yolov5s-cls.mlmodel # CoreML (macOS-only) 16 | yolov5s-cls_saved_model # TensorFlow SavedModel 17 | yolov5s-cls.pb # TensorFlow GraphDef 18 | yolov5s-cls.tflite # TensorFlow Lite 19 | yolov5s-cls_edgetpu.tflite # TensorFlow Edge TPU 20 | """ 21 | 22 | import argparse 23 | import os 24 | import sys 25 | from pathlib import Path 26 | 27 | import torch 28 | from tqdm import tqdm 29 | 30 | FILE = Path(__file__).resolve() 31 | ROOT = FILE.parents[1] # YOLOv5 root directory 32 | if str(ROOT) not in sys.path: 33 | sys.path.append(str(ROOT)) # add ROOT to PATH 34 | ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative 35 | 36 | from models.common import DetectMultiBackend 37 | from utils.dataloaders import create_classification_dataloader 38 | from utils.general import LOGGER, Profile, check_img_size, check_requirements, colorstr, increment_path, print_args 39 | from utils.torch_utils import select_device, smart_inference_mode 40 | 41 | 42 | @smart_inference_mode() 43 | def run( 44 | data=ROOT / '../datasets/mnist', # dataset dir 45 | weights=ROOT / 'yolov5s-cls.pt', # model.pt path(s) 46 | batch_size=128, # batch size 47 | imgsz=224, # inference size (pixels) 48 | device='', # cuda device, i.e. 0 or 0,1,2,3 or cpu 49 | workers=8, # max dataloader workers (per RANK in DDP mode) 50 | verbose=False, # verbose output 51 | project=ROOT / 'runs/val-cls', # save to project/name 52 | name='exp', # save to project/name 53 | exist_ok=False, # existing project/name ok, do not increment 54 | half=False, # use FP16 half-precision inference 55 | dnn=False, # use OpenCV DNN for ONNX inference 56 | model=None, 57 | dataloader=None, 58 | criterion=None, 59 | pbar=None, 60 | ): 61 | # Initialize/load model and set device 62 | training = model is not None 63 | if training: # called by train.py 64 | device, pt, jit, engine = next(model.parameters()).device, True, False, False # get model device, PyTorch model 65 | half &= device.type != 'cpu' # half precision only supported on CUDA 66 | model.half() if half else model.float() 67 | else: # called directly 68 | device = select_device(device, batch_size=batch_size) 69 | 70 | # Directories 71 | save_dir = increment_path(Path(project) / name, exist_ok=exist_ok) # increment run 72 | save_dir.mkdir(parents=True, exist_ok=True) # make dir 73 | 74 | # Load model 75 | model = DetectMultiBackend(weights, device=device, dnn=dnn, fp16=half) 76 | stride, pt, jit, engine = model.stride, model.pt, model.jit, model.engine 77 | imgsz = check_img_size(imgsz, s=stride) # check image size 78 | half = model.fp16 # FP16 supported on limited backends with CUDA 79 | if engine: 80 | batch_size = model.batch_size 81 | else: 82 | device = model.device 83 | if not (pt or jit): 84 | batch_size = 1 # export.py models default to batch-size 1 85 | LOGGER.info(f'Forcing --batch-size 1 square inference (1,3,{imgsz},{imgsz}) for non-PyTorch models') 86 | 87 | # Dataloader 88 | data = Path(data) 89 | test_dir = data / 'test' if (data / 'test').exists() else data / 'val' # data/test or data/val 90 | dataloader = create_classification_dataloader(path=test_dir, 91 | imgsz=imgsz, 92 | batch_size=batch_size, 93 | augment=False, 94 | rank=-1, 95 | workers=workers) 96 | 97 | model.eval() 98 | pred, targets, loss, dt = [], [], 0, (Profile(), Profile(), Profile()) 99 | n = len(dataloader) # number of batches 100 | action = 'validating' if dataloader.dataset.root.stem == 'val' else 'testing' 101 | desc = f"{pbar.desc[:-36]}{action:>36}" if pbar else f"{action}" 102 | bar = tqdm(dataloader, desc, n, not training, bar_format='{l_bar}{bar:10}{r_bar}{bar:-10b}', position=0) 103 | with torch.cuda.amp.autocast(enabled=device.type != 'cpu'): 104 | for images, labels in bar: 105 | with dt[0]: 106 | images, labels = images.to(device, non_blocking=True), labels.to(device) 107 | 108 | with dt[1]: 109 | y = model(images) 110 | 111 | with dt[2]: 112 | pred.append(y.argsort(1, descending=True)[:, :5]) 113 | targets.append(labels) 114 | if criterion: 115 | loss += criterion(y, labels) 116 | 117 | loss /= n 118 | pred, targets = torch.cat(pred), torch.cat(targets) 119 | correct = (targets[:, None] == pred).float() 120 | acc = torch.stack((correct[:, 0], correct.max(1).values), dim=1) # (top1, top5) accuracy 121 | top1, top5 = acc.mean(0).tolist() 122 | 123 | if pbar: 124 | pbar.desc = f"{pbar.desc[:-36]}{loss:>12.3g}{top1:>12.3g}{top5:>12.3g}" 125 | if verbose: # all classes 126 | LOGGER.info(f"{'Class':>24}{'Images':>12}{'top1_acc':>12}{'top5_acc':>12}") 127 | LOGGER.info(f"{'all':>24}{targets.shape[0]:>12}{top1:>12.3g}{top5:>12.3g}") 128 | for i, c in model.names.items(): 129 | aci = acc[targets == i] 130 | top1i, top5i = aci.mean(0).tolist() 131 | LOGGER.info(f"{c:>24}{aci.shape[0]:>12}{top1i:>12.3g}{top5i:>12.3g}") 132 | 133 | # Print results 134 | t = tuple(x.t / len(dataloader.dataset.samples) * 1E3 for x in dt) # speeds per image 135 | shape = (1, 3, imgsz, imgsz) 136 | LOGGER.info(f'Speed: %.1fms pre-process, %.1fms inference, %.1fms post-process per image at shape {shape}' % t) 137 | LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}") 138 | 139 | return top1, top5, loss 140 | 141 | 142 | def parse_opt(): 143 | parser = argparse.ArgumentParser() 144 | parser.add_argument('--data', type=str, default=ROOT / '../datasets/mnist', help='dataset path') 145 | parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'yolov5s-cls.pt', help='model.pt path(s)') 146 | parser.add_argument('--batch-size', type=int, default=128, help='batch size') 147 | parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=224, help='inference size (pixels)') 148 | parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') 149 | parser.add_argument('--workers', type=int, default=8, help='max dataloader workers (per RANK in DDP mode)') 150 | parser.add_argument('--verbose', nargs='?', const=True, default=True, help='verbose output') 151 | parser.add_argument('--project', default=ROOT / 'runs/val-cls', help='save to project/name') 152 | parser.add_argument('--name', default='exp', help='save to project/name') 153 | parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment') 154 | parser.add_argument('--half', action='store_true', help='use FP16 half-precision inference') 155 | parser.add_argument('--dnn', action='store_true', help='use OpenCV DNN for ONNX inference') 156 | opt = parser.parse_args() 157 | print_args(vars(opt)) 158 | return opt 159 | 160 | 161 | def main(opt): 162 | check_requirements(exclude=('tensorboard', 'thop')) 163 | run(**vars(opt)) 164 | 165 | 166 | if __name__ == "__main__": 167 | opt = parse_opt() 168 | main(opt) 169 | -------------------------------------------------------------------------------- /data/coco.names: -------------------------------------------------------------------------------- 1 | person 2 | bicycle 3 | car 4 | motorcycle 5 | airplane 6 | bus 7 | train 8 | truck 9 | boat 10 | traffic light 11 | fire hydrant 12 | stop sign 13 | parking meter 14 | bench 15 | bird 16 | cat 17 | dog 18 | horse 19 | sheep 20 | cow 21 | elephant 22 | bear 23 | zebra 24 | giraffe 25 | backpack 26 | umbrella 27 | handbag 28 | tie 29 | suitcase 30 | frisbee 31 | skis 32 | snowboard 33 | sports ball 34 | kite 35 | baseball bat 36 | baseball glove 37 | skateboard 38 | surfboard 39 | tennis racket 40 | bottle 41 | wine glass 42 | cup 43 | fork 44 | knife 45 | spoon 46 | bowl 47 | banana 48 | apple 49 | sandwich 50 | orange 51 | broccoli 52 | carrot 53 | hot dog 54 | pizza 55 | donut 56 | cake 57 | chair 58 | couch 59 | potted plant 60 | bed 61 | dining table 62 | toilet 63 | tv 64 | laptop 65 | mouse 66 | remote 67 | keyboard 68 | cell phone 69 | microwave 70 | oven 71 | toaster 72 | sink 73 | refrigerator 74 | book 75 | clock 76 | vase 77 | scissors 78 | teddy bear 79 | hair drier 80 | toothbrush 81 | -------------------------------------------------------------------------------- /data/coco.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | # COCO 2017 dataset http://cocodataset.org by Microsoft 3 | # Example usage: python train.py --data coco.yaml 4 | # parent 5 | # ├── yolov5 6 | # └── datasets 7 | # └── coco ← downloads here (20.1 GB) 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 # val 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 | names: 18 | 0: person 19 | 1: bicycle 20 | 2: car 21 | 3: motorcycle 22 | 4: airplane 23 | 5: bus 24 | 6: train 25 | 7: truck 26 | 8: boat 27 | 9: traffic light 28 | 10: fire hydrant 29 | 11: stop sign 30 | 12: parking meter 31 | 13: bench 32 | 14: bird 33 | 15: cat 34 | 16: dog 35 | 17: horse 36 | 18: sheep 37 | 19: cow 38 | 20: elephant 39 | 21: bear 40 | 22: zebra 41 | 23: giraffe 42 | 24: backpack 43 | 25: umbrella 44 | 26: handbag 45 | 27: tie 46 | 28: suitcase 47 | 29: frisbee 48 | 30: skis 49 | 31: snowboard 50 | 32: sports ball 51 | 33: kite 52 | 34: baseball bat 53 | 35: baseball glove 54 | 36: skateboard 55 | 37: surfboard 56 | 38: tennis racket 57 | 39: bottle 58 | 40: wine glass 59 | 41: cup 60 | 42: fork 61 | 43: knife 62 | 44: spoon 63 | 45: bowl 64 | 46: banana 65 | 47: apple 66 | 48: sandwich 67 | 49: orange 68 | 50: broccoli 69 | 51: carrot 70 | 52: hot dog 71 | 53: pizza 72 | 54: donut 73 | 55: cake 74 | 56: chair 75 | 57: couch 76 | 58: potted plant 77 | 59: bed 78 | 60: dining table 79 | 61: toilet 80 | 62: tv 81 | 63: laptop 82 | 64: mouse 83 | 65: remote 84 | 66: keyboard 85 | 67: cell phone 86 | 68: microwave 87 | 69: oven 88 | 70: toaster 89 | 71: sink 90 | 72: refrigerator 91 | 73: book 92 | 74: clock 93 | 75: vase 94 | 76: scissors 95 | 77: teddy bear 96 | 78: hair drier 97 | 79: toothbrush 98 | 99 | 100 | # Download script/URL (optional) 101 | download: | 102 | from utils.general import download, Path 103 | 104 | 105 | # Download labels 106 | segments = False # segment or box labels 107 | dir = Path(yaml['path']) # dataset root dir 108 | url = 'https://github.com/ultralytics/yolov5/releases/download/v1.0/' 109 | urls = [url + ('coco2017labels-segments.zip' if segments else 'coco2017labels.zip')] # labels 110 | download(urls, dir=dir.parent) 111 | 112 | # Download data 113 | urls = ['http://images.cocodataset.org/zips/train2017.zip', # 19G, 118k images 114 | 'http://images.cocodataset.org/zips/val2017.zip', # 1G, 5k images 115 | 'http://images.cocodataset.org/zips/test2017.zip'] # 7G, 41k images (optional) 116 | download(urls, dir=dir / 'images', threads=3) 117 | -------------------------------------------------------------------------------- /data/hyps/hyp.scratch-high.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 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.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.1 # segment copy-paste (probability) 35 | -------------------------------------------------------------------------------- /data/hyps/hyp.scratch-low.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 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 | -------------------------------------------------------------------------------- /data/hyps/hyp.scratch-med.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 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/scripts/get_coco.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 3 | # Download COCO 2017 dataset http://cocodataset.org 4 | # Example usage: bash data/scripts/get_coco.sh 5 | # parent 6 | # ├── yolov5 7 | # └── datasets 8 | # └── coco ← downloads here 9 | 10 | # Arguments (optional) Usage: bash data/scripts/get_coco.sh --train --val --test --segments 11 | if [ "$#" -gt 0 ]; then 12 | for opt in "$@"; do 13 | case "${opt}" in 14 | --train) train=true ;; 15 | --val) val=true ;; 16 | --test) test=true ;; 17 | --segments) segments=true ;; 18 | esac 19 | done 20 | else 21 | train=true 22 | val=true 23 | test=false 24 | segments=false 25 | fi 26 | 27 | # Download/unzip labels 28 | d='../datasets' # unzip directory 29 | url=https://github.com/ultralytics/yolov5/releases/download/v1.0/ 30 | if [ "$segments" == "true" ]; then 31 | f='coco2017labels-segments.zip' # 168 MB 32 | else 33 | f='coco2017labels.zip' # 168 MB 34 | fi 35 | echo 'Downloading' $url$f ' ...' 36 | curl -L $url$f -o $f -# && unzip -q $f -d $d && rm $f & 37 | 38 | # Download/unzip images 39 | d='../datasets/coco/images' # unzip directory 40 | url=http://images.cocodataset.org/zips/ 41 | if [ "$train" == "true" ]; then 42 | f='train2017.zip' # 19G, 118k images 43 | echo 'Downloading' $url$f '...' 44 | curl -L $url$f -o $f -# && unzip -q $f -d $d && rm $f & 45 | fi 46 | if [ "$val" == "true" ]; then 47 | f='val2017.zip' # 1G, 5k images 48 | echo 'Downloading' $url$f '...' 49 | curl -L $url$f -o $f -# && unzip -q $f -d $d && rm $f & 50 | fi 51 | if [ "$test" == "true" ]; then 52 | f='test2017.zip' # 7G, 41k images (optional) 53 | echo 'Downloading' $url$f '...' 54 | curl -L $url$f -o $f -# && unzip -q $f -d $d && rm $f & 55 | fi 56 | wait # finish background tasks 57 | -------------------------------------------------------------------------------- /data/scripts/get_imagenet.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 3 | # Download ILSVRC2012 ImageNet dataset https://image-net.org 4 | # Example usage: bash data/scripts/get_imagenet.sh 5 | # parent 6 | # ├── yolov5 7 | # └── datasets 8 | # └── imagenet ← downloads here 9 | 10 | # Arguments (optional) Usage: bash data/scripts/get_imagenet.sh --train --val 11 | if [ "$#" -gt 0 ]; then 12 | for opt in "$@"; do 13 | case "${opt}" in 14 | --train) train=true ;; 15 | --val) val=true ;; 16 | esac 17 | done 18 | else 19 | train=true 20 | val=true 21 | fi 22 | 23 | # Make dir 24 | d='../datasets/imagenet' # unzip directory 25 | mkdir -p $d && cd $d 26 | 27 | # Download/unzip train 28 | if [ "$train" == "true" ]; then 29 | wget https://image-net.org/data/ILSVRC/2012/ILSVRC2012_img_train.tar # download 138G, 1281167 images 30 | mkdir train && mv ILSVRC2012_img_train.tar train/ && cd train 31 | tar -xf ILSVRC2012_img_train.tar && rm -f ILSVRC2012_img_train.tar 32 | find . -name "*.tar" | while read NAME; do 33 | mkdir -p "${NAME%.tar}" 34 | tar -xf "${NAME}" -C "${NAME%.tar}" 35 | rm -f "${NAME}" 36 | done 37 | cd .. 38 | fi 39 | 40 | # Download/unzip val 41 | if [ "$val" == "true" ]; then 42 | wget https://image-net.org/data/ILSVRC/2012/ILSVRC2012_img_val.tar # download 6.3G, 50000 images 43 | mkdir val && mv ILSVRC2012_img_val.tar val/ && cd val && tar -xf ILSVRC2012_img_val.tar 44 | wget -qO- https://raw.githubusercontent.com/soumith/imagenetloader.torch/master/valprep.sh | bash # move into subdirs 45 | fi 46 | 47 | # Delete corrupted image (optional: PNG under JPEG name that may cause dataloaders to fail) 48 | # rm train/n04266014/n04266014_10835.JPEG 49 | 50 | # TFRecords (optional) 51 | # wget https://raw.githubusercontent.com/tensorflow/models/master/research/slim/datasets/imagenet_lsvrc_2015_synsets.txt 52 | -------------------------------------------------------------------------------- /detect.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Run YOLOv5 detection inference on images, videos, directories, globs, YouTube, webcam, streams, etc. 4 | 5 | Usage - sources: 6 | $ python detect.py --weights yolov5s.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 | Usage - formats: 15 | $ python detect.py --weights yolov5s.pt # PyTorch 16 | yolov5s.torchscript # TorchScript 17 | yolov5s.onnx # ONNX Runtime or OpenCV DNN with --dnn 18 | yolov5s.xml # OpenVINO 19 | yolov5s.engine # TensorRT 20 | yolov5s.mlmodel # CoreML (macOS-only) 21 | yolov5s_saved_model # TensorFlow SavedModel 22 | yolov5s.pb # TensorFlow GraphDef 23 | yolov5s.tflite # TensorFlow Lite 24 | yolov5s_edgetpu.tflite # TensorFlow Edge TPU 25 | """ 26 | 27 | import argparse 28 | import os 29 | import platform 30 | import sys 31 | from pathlib import Path 32 | 33 | import torch 34 | import torch.backends.cudnn as cudnn 35 | 36 | FILE = Path(__file__).resolve() 37 | ROOT = FILE.parents[0] # YOLOv5 root directory 38 | if str(ROOT) not in sys.path: 39 | sys.path.append(str(ROOT)) # add ROOT to PATH 40 | ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative 41 | 42 | from models.common import DetectMultiBackend 43 | from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadStreams 44 | from utils.general import (LOGGER, Profile, check_file, check_img_size, check_imshow, check_requirements, colorstr, cv2, 45 | increment_path, non_max_suppression, print_args, scale_coords, strip_optimizer, xyxy2xywh) 46 | from utils.plots import Annotator, colors, save_one_box 47 | from utils.torch_utils import select_device, smart_inference_mode 48 | 49 | 50 | @smart_inference_mode() 51 | def run( 52 | weights=ROOT / 'yolov5s.pt', # model.pt path(s) 53 | source=ROOT / 'data/images', # file/dir/URL/glob, 0 for webcam 54 | data=ROOT / 'data/coco128.yaml', # dataset.yaml path 55 | imgsz=(640, 640), # inference size (height, width) 56 | conf_thres=0.25, # confidence threshold 57 | iou_thres=0.45, # NMS IOU threshold 58 | max_det=1000, # maximum detections per image 59 | device='', # cuda device, i.e. 0 or 0,1,2,3 or cpu 60 | view_img=False, # show results 61 | save_txt=False, # save results to *.txt 62 | save_conf=False, # save confidences in --save-txt labels 63 | save_crop=False, # save cropped prediction boxes 64 | nosave=False, # do not save images/videos 65 | classes=None, # filter by class: --class 0, or --class 0 2 3 66 | agnostic_nms=False, # class-agnostic NMS 67 | augment=False, # augmented inference 68 | visualize=False, # visualize features 69 | update=False, # update all models 70 | project=ROOT / 'runs/detect', # save results to project/name 71 | name='exp', # save results to project/name 72 | exist_ok=False, # existing project/name ok, do not increment 73 | line_thickness=3, # bounding box thickness (pixels) 74 | hide_labels=False, # hide labels 75 | hide_conf=False, # hide confidences 76 | half=False, # use FP16 half-precision inference 77 | dnn=False, # use OpenCV DNN for ONNX inference 78 | ): 79 | source = str(source) 80 | save_img = not nosave and not source.endswith('.txt') # save inference images 81 | is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS) 82 | is_url = source.lower().startswith(('rtsp://', 'rtmp://', 'http://', 'https://')) 83 | webcam = source.isnumeric() or source.endswith('.txt') or (is_url and not is_file) 84 | if is_url and is_file: 85 | source = check_file(source) # download 86 | 87 | # Directories 88 | save_dir = increment_path(Path(project) / name, exist_ok=exist_ok) # increment run 89 | (save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir 90 | 91 | # Load model 92 | device = select_device(device) 93 | model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half) 94 | stride, names, pt = model.stride, model.names, model.pt 95 | imgsz = check_img_size(imgsz, s=stride) # check image size 96 | 97 | # Dataloader 98 | if webcam: 99 | view_img = check_imshow() 100 | cudnn.benchmark = True # set True to speed up constant image size inference 101 | dataset = LoadStreams(source, img_size=imgsz, stride=stride, auto=pt) 102 | bs = len(dataset) # batch_size 103 | else: 104 | dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt) 105 | bs = 1 # batch_size 106 | vid_path, vid_writer = [None] * bs, [None] * bs 107 | 108 | # Run inference 109 | model.warmup(imgsz=(1 if pt else bs, 3, *imgsz)) # warmup 110 | seen, windows, dt = 0, [], (Profile(), Profile(), Profile()) 111 | for path, im, im0s, vid_cap, s in dataset: 112 | with dt[0]: 113 | im = torch.from_numpy(im).to(device) 114 | im = im.half() if model.fp16 else im.float() # uint8 to fp16/32 115 | im /= 255 # 0 - 255 to 0.0 - 1.0 116 | if len(im.shape) == 3: 117 | im = im[None] # expand for batch dim 118 | 119 | # Inference 120 | with dt[1]: 121 | visualize = increment_path(save_dir / Path(path).stem, mkdir=True) if visualize else False 122 | pred = model(im, augment=augment, visualize=visualize) 123 | 124 | # NMS 125 | with dt[2]: 126 | pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det) 127 | 128 | # Second-stage classifier (optional) 129 | # pred = utils.general.apply_classifier(pred, classifier_model, im, im0s) 130 | 131 | # Process predictions 132 | for i, det in enumerate(pred): # per image 133 | seen += 1 134 | if webcam: # batch_size >= 1 135 | p, im0, frame = path[i], im0s[i].copy(), dataset.count 136 | s += f'{i}: ' 137 | else: 138 | p, im0, frame = path, im0s.copy(), getattr(dataset, 'frame', 0) 139 | 140 | p = Path(p) # to Path 141 | save_path = str(save_dir / p.name) # im.jpg 142 | txt_path = str(save_dir / 'labels' / p.stem) + ('' if dataset.mode == 'image' else f'_{frame}') # im.txt 143 | s += '%gx%g ' % im.shape[2:] # print string 144 | gn = torch.tensor(im0.shape)[[1, 0, 1, 0]] # normalization gain whwh 145 | imc = im0.copy() if save_crop else im0 # for save_crop 146 | annotator = Annotator(im0, line_width=line_thickness, example=str(names)) 147 | if len(det): 148 | # Rescale boxes from img_size to im0 size 149 | det[:, :4] = scale_coords(im.shape[2:], det[:, :4], im0.shape).round() 150 | 151 | # Print results 152 | for c in det[:, 5].unique(): 153 | n = (det[:, 5] == c).sum() # detections per class 154 | s += f"{n} {names[int(c)]}{'s' * (n > 1)}, " # add to string 155 | 156 | # Write results 157 | for *xyxy, conf, cls in reversed(det): 158 | if save_txt: # Write to file 159 | xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh 160 | line = (cls, *xywh, conf) if save_conf else (cls, *xywh) # label format 161 | with open(f'{txt_path}.txt', 'a') as f: 162 | f.write(('%g ' * len(line)).rstrip() % line + '\n') 163 | 164 | if save_img or save_crop or view_img: # Add bbox to image 165 | c = int(cls) # integer class 166 | label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf:.2f}') 167 | annotator.box_label(xyxy, label, color=colors(c, True)) 168 | if save_crop: 169 | save_one_box(xyxy, imc, file=save_dir / 'crops' / names[c] / f'{p.stem}.jpg', BGR=True) 170 | 171 | # Stream results 172 | im0 = annotator.result() 173 | if view_img: 174 | if platform.system() == 'Linux' and p not in windows: 175 | windows.append(p) 176 | cv2.namedWindow(str(p), cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO) # allow window resize (Linux) 177 | cv2.resizeWindow(str(p), im0.shape[1], im0.shape[0]) 178 | cv2.imshow(str(p), im0) 179 | cv2.waitKey(1) # 1 millisecond 180 | 181 | # Save results (image with detections) 182 | if save_img: 183 | if dataset.mode == 'image': 184 | cv2.imwrite(save_path, im0) 185 | else: # 'video' or 'stream' 186 | if vid_path[i] != save_path: # new video 187 | vid_path[i] = save_path 188 | if isinstance(vid_writer[i], cv2.VideoWriter): 189 | vid_writer[i].release() # release previous video writer 190 | if vid_cap: # video 191 | fps = vid_cap.get(cv2.CAP_PROP_FPS) 192 | w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH)) 193 | h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) 194 | else: # stream 195 | fps, w, h = 30, im0.shape[1], im0.shape[0] 196 | save_path = str(Path(save_path).with_suffix('.mp4')) # force *.mp4 suffix on results videos 197 | vid_writer[i] = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h)) 198 | vid_writer[i].write(im0) 199 | 200 | # Print time (inference-only) 201 | LOGGER.info(f"{s}{'' if len(det) else '(no detections), '}{dt[1].dt * 1E3:.1f}ms") 202 | 203 | # Print results 204 | t = tuple(x.t / seen * 1E3 for x in dt) # speeds per image 205 | LOGGER.info(f'Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {(1, 3, *imgsz)}' % t) 206 | if save_txt or save_img: 207 | s = f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else '' 208 | LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}{s}") 209 | if update: 210 | strip_optimizer(weights[0]) # update model (to fix SourceChangeWarning) 211 | 212 | 213 | def parse_opt(): 214 | parser = argparse.ArgumentParser() 215 | parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'yolov5s.pt', help='model path(s)') 216 | parser.add_argument('--source', type=str, default=ROOT / 'data/images', help='file/dir/URL/glob, 0 for webcam') 217 | parser.add_argument('--data', type=str, default=ROOT / 'data/coco128.yaml', help='(optional) dataset.yaml path') 218 | parser.add_argument('--imgsz', '--img', '--img-size', nargs='+', type=int, default=[640], help='inference size h,w') 219 | parser.add_argument('--conf-thres', type=float, default=0.25, help='confidence threshold') 220 | parser.add_argument('--iou-thres', type=float, default=0.45, help='NMS IoU threshold') 221 | parser.add_argument('--max-det', type=int, default=1000, help='maximum detections per image') 222 | parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') 223 | parser.add_argument('--view-img', action='store_true', help='show results') 224 | parser.add_argument('--save-txt', action='store_true', help='save results to *.txt') 225 | parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels') 226 | parser.add_argument('--save-crop', action='store_true', help='save cropped prediction boxes') 227 | parser.add_argument('--nosave', action='store_true', help='do not save images/videos') 228 | parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --classes 0, or --classes 0 2 3') 229 | parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS') 230 | parser.add_argument('--augment', action='store_true', help='augmented inference') 231 | parser.add_argument('--visualize', action='store_true', help='visualize features') 232 | parser.add_argument('--update', action='store_true', help='update all models') 233 | parser.add_argument('--project', default=ROOT / 'runs/detect', help='save results to project/name') 234 | parser.add_argument('--name', default='exp', help='save results to project/name') 235 | parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment') 236 | parser.add_argument('--line-thickness', default=3, type=int, help='bounding box thickness (pixels)') 237 | parser.add_argument('--hide-labels', default=False, action='store_true', help='hide labels') 238 | parser.add_argument('--hide-conf', default=False, action='store_true', help='hide confidences') 239 | parser.add_argument('--half', action='store_true', help='use FP16 half-precision inference') 240 | parser.add_argument('--dnn', action='store_true', help='use OpenCV DNN for ONNX inference') 241 | opt = parser.parse_args() 242 | opt.imgsz *= 2 if len(opt.imgsz) == 1 else 1 # expand 243 | print_args(vars(opt)) 244 | return opt 245 | 246 | 247 | def main(opt): 248 | check_requirements(exclude=('tensorboard', 'thop')) 249 | run(**vars(opt)) 250 | 251 | 252 | if __name__ == "__main__": 253 | opt = parse_opt() 254 | main(opt) 255 | -------------------------------------------------------------------------------- /figure/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/figure/1.png -------------------------------------------------------------------------------- /figure/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/figure/2.png -------------------------------------------------------------------------------- /figure/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/figure/3.png -------------------------------------------------------------------------------- /figure/Screenshot (350).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/figure/Screenshot (350).png -------------------------------------------------------------------------------- /figure/Screenshot (351).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/figure/Screenshot (351).png -------------------------------------------------------------------------------- /figure/Screenshot (352).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/figure/Screenshot (352).png -------------------------------------------------------------------------------- /figure/yolov7-seg-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/figure/yolov7-seg-example.png -------------------------------------------------------------------------------- /hubconf.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 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/yolov5', 'yolov5s') 8 | model = torch.hub.load('ultralytics/yolov5:master', 'custom', 'path/to/yolov5s.onnx') # custom model from branch 9 | """ 10 | 11 | import torch 12 | 13 | 14 | def _create(name, pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None): 15 | """Creates or loads a YOLOv5 model 16 | 17 | Arguments: 18 | name (str): model name 'yolov5s' or path 'path/to/best.pt' 19 | pretrained (bool): load pretrained weights into the model 20 | channels (int): number of input channels 21 | classes (int): number of model classes 22 | autoshape (bool): apply YOLOv5 .autoshape() wrapper to model 23 | verbose (bool): print all information to screen 24 | device (str, torch.device, None): device to use for model parameters 25 | 26 | Returns: 27 | YOLOv5 model 28 | """ 29 | from pathlib import Path 30 | 31 | from models.common import AutoShape, DetectMultiBackend 32 | from models.experimental import attempt_load 33 | from models.yolo import ClassificationModel, DetectionModel 34 | from utils.downloads import attempt_download 35 | from utils.general import LOGGER, check_requirements, intersect_dicts, logging 36 | from utils.torch_utils import select_device 37 | 38 | if not verbose: 39 | LOGGER.setLevel(logging.WARNING) 40 | check_requirements(exclude=('tensorboard', 'thop', 'opencv-python')) 41 | name = Path(name) 42 | path = name.with_suffix('.pt') if name.suffix == '' and not name.is_dir() else name # checkpoint path 43 | try: 44 | device = select_device(device) 45 | if pretrained and channels == 3 and classes == 80: 46 | try: 47 | model = DetectMultiBackend(path, device=device, fuse=autoshape) # detection model 48 | if autoshape: 49 | if model.pt and isinstance(model.model, ClassificationModel): 50 | LOGGER.warning('WARNING: YOLOv5 v6.2 ClassificationModel is not yet AutoShape compatible. ' 51 | 'You must pass torch tensors in BCHW to this model, i.e. shape(1,3,224,224).') 52 | else: 53 | model = AutoShape(model) # for file/URI/PIL/cv2/np inputs and NMS 54 | except Exception: 55 | model = attempt_load(path, device=device, fuse=False) # arbitrary model 56 | else: 57 | cfg = list((Path(__file__).parent / 'models').rglob(f'{path.stem}.yaml'))[0] # model.yaml path 58 | model = DetectionModel(cfg, channels, classes) # create model 59 | if pretrained: 60 | ckpt = torch.load(attempt_download(path), map_location=device) # load 61 | csd = ckpt['model'].float().state_dict() # checkpoint state_dict as FP32 62 | csd = intersect_dicts(csd, model.state_dict(), exclude=['anchors']) # intersect 63 | model.load_state_dict(csd, strict=False) # load 64 | if len(ckpt['model'].names) == classes: 65 | model.names = ckpt['model'].names # set class names attribute 66 | if not verbose: 67 | LOGGER.setLevel(logging.INFO) # reset to default 68 | return model.to(device) 69 | 70 | except Exception as e: 71 | help_url = 'https://github.com/ultralytics/yolov5/issues/36' 72 | s = f'{e}. Cache may be out of date, try `force_reload=True` or see {help_url} for help.' 73 | raise Exception(s) from e 74 | 75 | 76 | def custom(path='path/to/model.pt', autoshape=True, _verbose=True, device=None): 77 | # YOLOv5 custom or local model 78 | return _create(path, autoshape=autoshape, verbose=_verbose, device=device) 79 | 80 | 81 | def yolov5n(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None): 82 | # YOLOv5-nano model https://github.com/ultralytics/yolov5 83 | return _create('yolov5n', pretrained, channels, classes, autoshape, _verbose, device) 84 | 85 | 86 | def yolov5s(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None): 87 | # YOLOv5-small model https://github.com/ultralytics/yolov5 88 | return _create('yolov5s', pretrained, channels, classes, autoshape, _verbose, device) 89 | 90 | 91 | def yolov5m(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None): 92 | # YOLOv5-medium model https://github.com/ultralytics/yolov5 93 | return _create('yolov5m', pretrained, channels, classes, autoshape, _verbose, device) 94 | 95 | 96 | def yolov5l(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None): 97 | # YOLOv5-large model https://github.com/ultralytics/yolov5 98 | return _create('yolov5l', pretrained, channels, classes, autoshape, _verbose, device) 99 | 100 | 101 | def yolov5x(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None): 102 | # YOLOv5-xlarge model https://github.com/ultralytics/yolov5 103 | return _create('yolov5x', pretrained, channels, classes, autoshape, _verbose, device) 104 | 105 | 106 | def yolov5n6(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None): 107 | # YOLOv5-nano-P6 model https://github.com/ultralytics/yolov5 108 | return _create('yolov5n6', pretrained, channels, classes, autoshape, _verbose, device) 109 | 110 | 111 | def yolov5s6(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None): 112 | # YOLOv5-small-P6 model https://github.com/ultralytics/yolov5 113 | return _create('yolov5s6', pretrained, channels, classes, autoshape, _verbose, device) 114 | 115 | 116 | def yolov5m6(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None): 117 | # YOLOv5-medium-P6 model https://github.com/ultralytics/yolov5 118 | return _create('yolov5m6', pretrained, channels, classes, autoshape, _verbose, device) 119 | 120 | 121 | def yolov5l6(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None): 122 | # YOLOv5-large-P6 model https://github.com/ultralytics/yolov5 123 | return _create('yolov5l6', pretrained, channels, classes, autoshape, _verbose, device) 124 | 125 | 126 | def yolov5x6(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None): 127 | # YOLOv5-xlarge-P6 model https://github.com/ultralytics/yolov5 128 | return _create('yolov5x6', pretrained, channels, classes, autoshape, _verbose, device) 129 | 130 | 131 | if __name__ == '__main__': 132 | import argparse 133 | from pathlib import Path 134 | 135 | import numpy as np 136 | from PIL import Image 137 | 138 | from utils.general import cv2, print_args 139 | 140 | # Argparser 141 | parser = argparse.ArgumentParser() 142 | parser.add_argument('--model', type=str, default='yolov5s', help='model name') 143 | opt = parser.parse_args() 144 | print_args(vars(opt)) 145 | 146 | # Model 147 | model = _create(name=opt.model, pretrained=True, channels=3, classes=80, autoshape=True, verbose=True) 148 | # model = custom(path='path/to/model.pt') # custom 149 | 150 | # Images 151 | imgs = [ 152 | 'data/images/zidane.jpg', # filename 153 | Path('data/images/zidane.jpg'), # Path 154 | 'https://ultralytics.com/images/zidane.jpg', # URI 155 | cv2.imread('data/images/bus.jpg')[:, :, ::-1], # OpenCV 156 | Image.open('data/images/bus.jpg'), # PIL 157 | np.zeros((320, 640, 3))] # numpy 158 | 159 | # Inference 160 | results = model(imgs, size=320) # batched inference 161 | 162 | # Results 163 | results.print() 164 | results.save() 165 | -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- 1 | # init -------------------------------------------------------------------------------- /models/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/models/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /models/__pycache__/common.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/models/__pycache__/common.cpython-310.pyc -------------------------------------------------------------------------------- /models/__pycache__/experimental.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/models/__pycache__/experimental.cpython-310.pyc -------------------------------------------------------------------------------- /models/__pycache__/yolo.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/models/__pycache__/yolo.cpython-310.pyc -------------------------------------------------------------------------------- /models/experimental.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 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 utils.downloads import attempt_download 12 | 13 | 14 | class Sum(nn.Module): 15 | # Weighted sum of 2 or more layers https://arxiv.org/abs/1911.09070 16 | def __init__(self, n, weight=False): # n: number of inputs 17 | super().__init__() 18 | self.weight = weight # apply weights boolean 19 | self.iter = range(n - 1) # iter object 20 | if weight: 21 | self.w = nn.Parameter(-torch.arange(1.0, n) / 2, requires_grad=True) # layer weights 22 | 23 | def forward(self, x): 24 | y = x[0] # no weight 25 | if self.weight: 26 | w = torch.sigmoid(self.w) * 2 27 | for i in self.iter: 28 | y = y + x[i + 1] * w[i] 29 | else: 30 | for i in self.iter: 31 | y = y + x[i + 1] 32 | return y 33 | 34 | 35 | class MixConv2d(nn.Module): 36 | # Mixed Depth-wise Conv https://arxiv.org/abs/1907.09595 37 | def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True): # ch_in, ch_out, kernel, stride, ch_strategy 38 | super().__init__() 39 | n = len(k) # number of convolutions 40 | if equal_ch: # equal c_ per group 41 | i = torch.linspace(0, n - 1E-6, c2).floor() # c2 indices 42 | c_ = [(i == g).sum() for g in range(n)] # intermediate channels 43 | else: # equal weight.numel() per group 44 | b = [c2] + [0] * n 45 | a = np.eye(n + 1, n, k=-1) 46 | a -= np.roll(a, 1, axis=1) 47 | a *= np.array(k) ** 2 48 | a[0] = 1 49 | c_ = np.linalg.lstsq(a, b, rcond=None)[0].round() # solve for equal weight indices, ax = b 50 | 51 | self.m = nn.ModuleList([ 52 | nn.Conv2d(c1, int(c_), k, s, k // 2, groups=math.gcd(c1, int(c_)), bias=False) for k, c_ in zip(k, c_)]) 53 | self.bn = nn.BatchNorm2d(c2) 54 | self.act = nn.SiLU() 55 | 56 | def forward(self, x): 57 | return self.act(self.bn(torch.cat([m(x) for m in self.m], 1))) 58 | 59 | 60 | class Ensemble(nn.ModuleList): 61 | # Ensemble of models 62 | def __init__(self): 63 | super().__init__() 64 | 65 | def forward(self, x, augment=False, profile=False, visualize=False): 66 | y = [module(x, augment, profile, visualize)[0] for module in self] 67 | # y = torch.stack(y).max(0)[0] # max ensemble 68 | # y = torch.stack(y).mean(0) # mean ensemble 69 | y = torch.cat(y, 1) # nms ensemble 70 | return y, None # inference, train output 71 | 72 | 73 | def attempt_load(weights, device=None, inplace=True, fuse=True): 74 | # Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a 75 | from models.yolo import Detect, Model 76 | 77 | model = Ensemble() 78 | for w in weights if isinstance(weights, list) else [weights]: 79 | ckpt = torch.load(attempt_download(w), map_location='cpu') # load 80 | ckpt = (ckpt.get('ema') or ckpt['model']).to(device).float() # FP32 model 81 | 82 | # Model compatibility updates 83 | if not hasattr(ckpt, 'stride'): 84 | ckpt.stride = torch.tensor([32.]) 85 | if hasattr(ckpt, 'names') and isinstance(ckpt.names, (list, tuple)): 86 | ckpt.names = dict(enumerate(ckpt.names)) # convert to dict 87 | 88 | model.append(ckpt.fuse().eval() if fuse and hasattr(ckpt, 'fuse') else ckpt.eval()) # model in eval mode 89 | 90 | # Module compatibility updates 91 | for m in model.modules(): 92 | t = type(m) 93 | if t in (nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU, Detect, Model): 94 | m.inplace = inplace # torch 1.7.0 compatibility 95 | if t is Detect and not isinstance(m.anchor_grid, list): 96 | delattr(m, 'anchor_grid') 97 | setattr(m, 'anchor_grid', [torch.zeros(1)] * m.nl) 98 | elif t is nn.Upsample and not hasattr(m, 'recompute_scale_factor'): 99 | m.recompute_scale_factor = None # torch 1.11.0 compatibility 100 | 101 | # Return model 102 | if len(model) == 1: 103 | return model[-1] 104 | 105 | # Return detection ensemble 106 | print(f'Ensemble created with {weights}\n') 107 | for k in 'names', 'nc', 'yaml': 108 | setattr(model, k, getattr(model[0], k)) 109 | model.stride = model[torch.argmax(torch.tensor([m.stride.max() for m in model])).int()].stride # max stride 110 | assert all(model[0].nc == m.nc for m in model), f'Models have different class counts: {[m.nc for m in model]}' 111 | return model 112 | -------------------------------------------------------------------------------- /models/hub/yolov3-spp.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 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 | -------------------------------------------------------------------------------- /models/hub/yolov3-tiny.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 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 | -------------------------------------------------------------------------------- /models/hub/yolov3.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 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/segment/yolov7-seg.yaml: -------------------------------------------------------------------------------- 1 | # YOLOv7 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 | - [12,16, 19,36, 40,28] # P3/8 9 | - [36,75, 76,55, 72,146] # P4/16 10 | - [142,110, 192,243, 459,401] # P5/32 11 | 12 | # YOLOv7 backbone 13 | backbone: 14 | # [from, number, module, args] 15 | [[-1, 1, Conv, [32, 3, 1]], # 0 16 | 17 | [-1, 1, Conv, [64, 3, 2]], # 1-P1/2 18 | [-1, 1, Conv, [64, 3, 1]], 19 | 20 | [-1, 1, Conv, [128, 3, 2]], # 3-P2/4 21 | [-1, 1, Conv, [64, 1, 1]], 22 | [-2, 1, Conv, [64, 1, 1]], 23 | [-1, 1, Conv, [64, 3, 1]], 24 | [-1, 1, Conv, [64, 3, 1]], 25 | [-1, 1, Conv, [64, 3, 1]], 26 | [-1, 1, Conv, [64, 3, 1]], 27 | [[-1, -3, -5, -6], 1, Concat, [1]], 28 | [-1, 1, Conv, [256, 1, 1]], # 11 29 | 30 | [-1, 1, MP, []], 31 | [-1, 1, Conv, [128, 1, 1]], 32 | [-3, 1, Conv, [128, 1, 1]], 33 | [-1, 1, Conv, [128, 3, 2]], 34 | [[-1, -3], 1, Concat, [1]], # 16-P3/8 35 | [-1, 1, Conv, [128, 1, 1]], 36 | [-2, 1, Conv, [128, 1, 1]], 37 | [-1, 1, Conv, [128, 3, 1]], 38 | [-1, 1, Conv, [128, 3, 1]], 39 | [-1, 1, Conv, [128, 3, 1]], 40 | [-1, 1, Conv, [128, 3, 1]], 41 | [[-1, -3, -5, -6], 1, Concat, [1]], 42 | [-1, 1, Conv, [512, 1, 1]], # 24 43 | 44 | [-1, 1, MP, []], 45 | [-1, 1, Conv, [256, 1, 1]], 46 | [-3, 1, Conv, [256, 1, 1]], 47 | [-1, 1, Conv, [256, 3, 2]], 48 | [[-1, -3], 1, Concat, [1]], # 29-P4/16 49 | [-1, 1, Conv, [256, 1, 1]], 50 | [-2, 1, Conv, [256, 1, 1]], 51 | [-1, 1, Conv, [256, 3, 1]], 52 | [-1, 1, Conv, [256, 3, 1]], 53 | [-1, 1, Conv, [256, 3, 1]], 54 | [-1, 1, Conv, [256, 3, 1]], 55 | [[-1, -3, -5, -6], 1, Concat, [1]], 56 | [-1, 1, Conv, [1024, 1, 1]], # 37 57 | 58 | [-1, 1, MP, []], 59 | [-1, 1, Conv, [512, 1, 1]], 60 | [-3, 1, Conv, [512, 1, 1]], 61 | [-1, 1, Conv, [512, 3, 2]], 62 | [[-1, -3], 1, Concat, [1]], # 42-P5/32 63 | [-1, 1, Conv, [256, 1, 1]], 64 | [-2, 1, Conv, [256, 1, 1]], 65 | [-1, 1, Conv, [256, 3, 1]], 66 | [-1, 1, Conv, [256, 3, 1]], 67 | [-1, 1, Conv, [256, 3, 1]], 68 | [-1, 1, Conv, [256, 3, 1]], 69 | [[-1, -3, -5, -6], 1, Concat, [1]], 70 | [-1, 1, Conv, [1024, 1, 1]], # 50 71 | ] 72 | 73 | # yolov7 head 74 | head: 75 | [[-1, 1, SPPCSPC, [512]], # 51 76 | 77 | [-1, 1, Conv, [256, 1, 1]], 78 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 79 | [37, 1, Conv, [256, 1, 1]], # route backbone P4 80 | [[-1, -2], 1, Concat, [1]], 81 | 82 | [-1, 1, Conv, [256, 1, 1]], 83 | [-2, 1, Conv, [256, 1, 1]], 84 | [-1, 1, Conv, [128, 3, 1]], 85 | [-1, 1, Conv, [128, 3, 1]], 86 | [-1, 1, Conv, [128, 3, 1]], 87 | [-1, 1, Conv, [128, 3, 1]], 88 | [[-1, -2, -3, -4, -5, -6], 1, Concat, [1]], 89 | [-1, 1, Conv, [256, 1, 1]], # 63 90 | 91 | [-1, 1, Conv, [128, 1, 1]], 92 | [-1, 1, nn.Upsample, [None, 2, 'nearest']], 93 | [24, 1, Conv, [128, 1, 1]], # route backbone P3 94 | [[-1, -2], 1, Concat, [1]], 95 | 96 | [-1, 1, Conv, [128, 1, 1]], 97 | [-2, 1, Conv, [128, 1, 1]], 98 | [-1, 1, Conv, [64, 3, 1]], 99 | [-1, 1, Conv, [64, 3, 1]], 100 | [-1, 1, Conv, [64, 3, 1]], 101 | [-1, 1, Conv, [64, 3, 1]], 102 | [[-1, -2, -3, -4, -5, -6], 1, Concat, [1]], 103 | [-1, 1, Conv, [128, 1, 1]], # 75 104 | 105 | [-1, 1, MP, []], 106 | [-1, 1, Conv, [128, 1, 1]], 107 | [-3, 1, Conv, [128, 1, 1]], 108 | [-1, 1, Conv, [128, 3, 2]], 109 | [[-1, -3, 63], 1, Concat, [1]], 110 | 111 | [-1, 1, Conv, [256, 1, 1]], 112 | [-2, 1, Conv, [256, 1, 1]], 113 | [-1, 1, Conv, [128, 3, 1]], 114 | [-1, 1, Conv, [128, 3, 1]], 115 | [-1, 1, Conv, [128, 3, 1]], 116 | [-1, 1, Conv, [128, 3, 1]], 117 | [[-1, -2, -3, -4, -5, -6], 1, Concat, [1]], 118 | [-1, 1, Conv, [256, 1, 1]], # 88 119 | 120 | [-1, 1, MP, []], 121 | [-1, 1, Conv, [256, 1, 1]], 122 | [-3, 1, Conv, [256, 1, 1]], 123 | [-1, 1, Conv, [256, 3, 2]], 124 | [[-1, -3, 51], 1, Concat, [1]], 125 | 126 | [-1, 1, Conv, [512, 1, 1]], 127 | [-2, 1, Conv, [512, 1, 1]], 128 | [-1, 1, Conv, [256, 3, 1]], 129 | [-1, 1, Conv, [256, 3, 1]], 130 | [-1, 1, Conv, [256, 3, 1]], 131 | [-1, 1, Conv, [256, 3, 1]], 132 | [[-1, -2, -3, -4, -5, -6], 1, Concat, [1]], 133 | [-1, 1, Conv, [512, 1, 1]], # 101 134 | 135 | [75, 1, Conv, [256, 3, 1]], 136 | [88, 1, Conv, [512, 3, 1]], 137 | [101, 1, Conv, [1024, 3, 1]], 138 | 139 | [[102, 103, 104], 1, ISegment, [nc, anchors, 32, 256]], # Detect(P3, P4, P5) 140 | ] 141 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # YOLOv5 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>=5.2 # 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==0.19.2 # CoreML quantization 33 | # tensorflow>=2.4.1 # TFLite export (or tensorflow-cpu, tensorflow-aarch64) 34 | # tensorflowjs>=3.9.0 # TF.js export 35 | # openvino-dev # OpenVINO export 36 | 37 | # Extras -------------------------------------- 38 | ipython # interactive notebook 39 | psutil # system utilization 40 | thop>=0.1.1 # FLOPs computation 41 | # albumentations>=1.0.3 42 | # pycocotools>=2.0 # COCO mAP 43 | # roboflow 44 | -------------------------------------------------------------------------------- /runs/predict-seg/exp/Demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/runs/predict-seg/exp/Demo.mp4 -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # Project-wide configuration file, can be used for package metadata and other toll configurations 2 | # Example usage: global configuration for PEP8 (via flake8) setting or default pytest arguments 3 | # Local usage: pip install pre-commit, pre-commit run --all-files 4 | 5 | [metadata] 6 | license_file = LICENSE 7 | description_file = README.md 8 | 9 | 10 | [tool:pytest] 11 | norecursedirs = 12 | .git 13 | dist 14 | build 15 | addopts = 16 | --doctest-modules 17 | --durations=25 18 | --color=yes 19 | 20 | 21 | [flake8] 22 | max-line-length = 120 23 | exclude = .tox,*.egg,build,temp 24 | select = E,W,F 25 | doctests = True 26 | verbose = 2 27 | # https://pep8.readthedocs.io/en/latest/intro.html#error-codes 28 | format = pylint 29 | # see: https://www.flake8rules.com/ 30 | ignore = 31 | E731 # Do not assign a lambda expression, use a def 32 | F405 # name may be undefined, or defined from star imports: module 33 | E402 # module level import not at top of file 34 | F401 # module imported but unused 35 | W504 # line break after binary operator 36 | E127 # continuation line over-indented for visual indent 37 | W504 # line break after binary operator 38 | E231 # missing whitespace after ‘,’, ‘;’, or ‘:’ 39 | E501 # line too long 40 | F403 # ‘from module import *’ used; unable to detect undefined names 41 | 42 | 43 | [isort] 44 | # https://pycqa.github.io/isort/docs/configuration/options.html 45 | line_length = 120 46 | # see: https://pycqa.github.io/isort/docs/configuration/multi_line_output_modes.html 47 | multi_line_output = 0 48 | 49 | 50 | [yapf] 51 | based_on_style = pep8 52 | spaces_before_comment = 2 53 | COLUMN_LIMIT = 120 54 | COALESCE_BRACKETS = True 55 | SPACES_AROUND_POWER_OPERATOR = True 56 | SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET = False 57 | SPLIT_BEFORE_CLOSING_BRACKET = False 58 | SPLIT_BEFORE_FIRST_ARGUMENT = False 59 | # EACH_DICT_ENTRY_ON_SEPARATE_LINE = False 60 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | utils/initialization 4 | """ 5 | 6 | import contextlib 7 | import threading 8 | 9 | 10 | class TryExcept(contextlib.ContextDecorator): 11 | # YOLOv5 TryExcept class. Usage: @TryExcept() decorator or 'with TryExcept():' context manager 12 | def __init__(self, msg='default message here'): 13 | self.msg = msg 14 | 15 | def __enter__(self): 16 | pass 17 | 18 | def __exit__(self, exc_type, value, traceback): 19 | if value: 20 | print(f'{self.msg}: {value}') 21 | return True 22 | 23 | 24 | def threaded(func): 25 | # Multi-threads a target function and returns thread. Usage: @threaded decorator 26 | def wrapper(*args, **kwargs): 27 | thread = threading.Thread(target=func, args=args, kwargs=kwargs, daemon=True) 28 | thread.start() 29 | return thread 30 | 31 | return wrapper 32 | 33 | 34 | def notebook_init(verbose=True): 35 | # Check system software and hardware 36 | print('Checking setup...') 37 | 38 | import os 39 | import shutil 40 | 41 | from utils.general import check_requirements, emojis, is_colab 42 | from utils.torch_utils import select_device # imports 43 | 44 | check_requirements(('psutil', 'IPython')) 45 | import psutil 46 | from IPython import display # to display images and clear console output 47 | 48 | if is_colab(): 49 | shutil.rmtree('/content/sample_data', ignore_errors=True) # remove colab /sample_data directory 50 | 51 | # System info 52 | if verbose: 53 | gb = 1 << 30 # bytes to GiB (1024 ** 3) 54 | ram = psutil.virtual_memory().total 55 | total, used, free = shutil.disk_usage("/") 56 | display.clear_output() 57 | s = f'({os.cpu_count()} CPUs, {ram / gb:.1f} GB RAM, {(total - free) / gb:.1f}/{total / gb:.1f} GB disk)' 58 | else: 59 | s = '' 60 | 61 | select_device(newline=False) 62 | print(emojis(f'Setup complete ✅ {s}')) 63 | return display 64 | -------------------------------------------------------------------------------- /utils/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/utils/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/augmentations.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/utils/__pycache__/augmentations.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/autoanchor.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/utils/__pycache__/autoanchor.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/dataloaders.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/utils/__pycache__/dataloaders.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/downloads.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/utils/__pycache__/downloads.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/general.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/utils/__pycache__/general.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/metrics.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/utils/__pycache__/metrics.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/plots.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/utils/__pycache__/plots.cpython-310.pyc -------------------------------------------------------------------------------- /utils/__pycache__/torch_utils.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/utils/__pycache__/torch_utils.cpython-310.pyc -------------------------------------------------------------------------------- /utils/activations.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Activation functions 4 | """ 5 | 6 | import torch 7 | import torch.nn as nn 8 | import torch.nn.functional as F 9 | 10 | 11 | class SiLU(nn.Module): 12 | # SiLU activation https://arxiv.org/pdf/1606.08415.pdf 13 | @staticmethod 14 | def forward(x): 15 | return x * torch.sigmoid(x) 16 | 17 | 18 | class Hardswish(nn.Module): 19 | # Hard-SiLU activation 20 | @staticmethod 21 | def forward(x): 22 | # return x * F.hardsigmoid(x) # for TorchScript and CoreML 23 | return x * F.hardtanh(x + 3, 0.0, 6.0) / 6.0 # for TorchScript, CoreML and ONNX 24 | 25 | 26 | class Mish(nn.Module): 27 | # Mish activation https://github.com/digantamisra98/Mish 28 | @staticmethod 29 | def forward(x): 30 | return x * F.softplus(x).tanh() 31 | 32 | 33 | class MemoryEfficientMish(nn.Module): 34 | # Mish activation memory-efficient 35 | class F(torch.autograd.Function): 36 | 37 | @staticmethod 38 | def forward(ctx, x): 39 | ctx.save_for_backward(x) 40 | return x.mul(torch.tanh(F.softplus(x))) # x * tanh(ln(1 + exp(x))) 41 | 42 | @staticmethod 43 | def backward(ctx, grad_output): 44 | x = ctx.saved_tensors[0] 45 | sx = torch.sigmoid(x) 46 | fx = F.softplus(x).tanh() 47 | return grad_output * (fx + x * sx * (1 - fx * fx)) 48 | 49 | def forward(self, x): 50 | return self.F.apply(x) 51 | 52 | 53 | class FReLU(nn.Module): 54 | # FReLU activation https://arxiv.org/abs/2007.11824 55 | def __init__(self, c1, k=3): # ch_in, kernel 56 | super().__init__() 57 | self.conv = nn.Conv2d(c1, c1, k, 1, 1, groups=c1, bias=False) 58 | self.bn = nn.BatchNorm2d(c1) 59 | 60 | def forward(self, x): 61 | return torch.max(x, self.bn(self.conv(x))) 62 | 63 | 64 | class AconC(nn.Module): 65 | r""" ACON activation (activate or not) 66 | AconC: (p1*x-p2*x) * sigmoid(beta*(p1*x-p2*x)) + p2*x, beta is a learnable parameter 67 | according to "Activate or Not: Learning Customized Activation" . 68 | """ 69 | 70 | def __init__(self, c1): 71 | super().__init__() 72 | self.p1 = nn.Parameter(torch.randn(1, c1, 1, 1)) 73 | self.p2 = nn.Parameter(torch.randn(1, c1, 1, 1)) 74 | self.beta = nn.Parameter(torch.ones(1, c1, 1, 1)) 75 | 76 | def forward(self, x): 77 | dpx = (self.p1 - self.p2) * x 78 | return dpx * torch.sigmoid(self.beta * dpx) + self.p2 * x 79 | 80 | 81 | class MetaAconC(nn.Module): 82 | r""" ACON activation (activate or not) 83 | MetaAconC: (p1*x-p2*x) * sigmoid(beta*(p1*x-p2*x)) + p2*x, beta is generated by a small network 84 | according to "Activate or Not: Learning Customized Activation" . 85 | """ 86 | 87 | def __init__(self, c1, k=1, s=1, r=16): # ch_in, kernel, stride, r 88 | super().__init__() 89 | c2 = max(r, c1 // r) 90 | self.p1 = nn.Parameter(torch.randn(1, c1, 1, 1)) 91 | self.p2 = nn.Parameter(torch.randn(1, c1, 1, 1)) 92 | self.fc1 = nn.Conv2d(c1, c2, k, s, bias=True) 93 | self.fc2 = nn.Conv2d(c2, c1, k, s, bias=True) 94 | # self.bn1 = nn.BatchNorm2d(c2) 95 | # self.bn2 = nn.BatchNorm2d(c1) 96 | 97 | def forward(self, x): 98 | y = x.mean(dim=2, keepdims=True).mean(dim=3, keepdims=True) 99 | # batch-size 1 bug/instabilities https://github.com/ultralytics/yolov5/issues/2891 100 | # beta = torch.sigmoid(self.bn2(self.fc2(self.bn1(self.fc1(y))))) # bug/unstable 101 | beta = torch.sigmoid(self.fc2(self.fc1(y))) # bug patch BN layers removed 102 | dpx = (self.p1 - self.p2) * x 103 | return dpx * torch.sigmoid(beta * dpx) + self.p2 * x 104 | -------------------------------------------------------------------------------- /utils/autoanchor.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | AutoAnchor 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 14 | 15 | PREFIX = colorstr('AutoAnchor: ') 16 | 17 | 18 | def check_anchor_order(m): 19 | # Check anchor order against stride order for YOLOv5 Detect() module m, and correct if necessary 20 | a = m.anchors.prod(-1).mean(-1).view(-1) # mean anchor area per output layer 21 | da = a[-1] - a[0] # delta a 22 | ds = m.stride[-1] - m.stride[0] # delta s 23 | if da and (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 | stride = m.stride.to(m.anchors.device).view(-1, 1, 1) # model strides 44 | anchors = m.anchors.clone() * stride # current anchors 45 | bpr, aat = metric(anchors.cpu().view(-1, 2)) 46 | s = f'\n{PREFIX}{aat:.2f} anchors/target, {bpr:.3f} Best Possible Recall (BPR). ' 47 | if bpr > 0.98: # threshold to recompute 48 | LOGGER.info(f'{s}Current anchors are a good fit to dataset ✅') 49 | else: 50 | LOGGER.info(f'{s}Anchors are a poor fit to dataset ⚠️, attempting to improve...') 51 | na = m.anchors.numel() // 2 # number of anchors 52 | try: 53 | anchors = kmean_anchors(dataset, n=na, img_size=imgsz, thr=thr, gen=1000, verbose=False) 54 | except Exception as e: 55 | LOGGER.info(f'{PREFIX}ERROR: {e}') 56 | new_bpr = metric(anchors)[0] 57 | if new_bpr > bpr: # replace anchors 58 | anchors = torch.tensor(anchors, device=m.anchors.device).type_as(m.anchors) 59 | m.anchors[:] = anchors.clone().view_as(m.anchors) 60 | check_anchor_order(m) # must be in pixel-space (not grid-space) 61 | m.anchors /= stride 62 | s = f'{PREFIX}Done ✅ (optional: update model *.yaml to use these anchors in the future)' 63 | else: 64 | s = f'{PREFIX}Done ⚠️ (original anchors better than new anchors, proceeding with original anchors)' 65 | LOGGER.info(s) 66 | 67 | 68 | def kmean_anchors(dataset='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen=1000, verbose=True): 69 | """ Creates kmeans-evolved anchors from training dataset 70 | 71 | Arguments: 72 | dataset: path to data.yaml, or a loaded dataset 73 | n: number of anchors 74 | img_size: image size used for training 75 | thr: anchor-label wh ratio threshold hyperparameter hyp['anchor_t'] used for training, default=4.0 76 | gen: generations to evolve anchors using genetic algorithm 77 | verbose: print all results 78 | 79 | Return: 80 | k: kmeans evolved anchors 81 | 82 | Usage: 83 | from utils.autoanchor import *; _ = kmean_anchors() 84 | """ 85 | from scipy.cluster.vq import kmeans 86 | 87 | npr = np.random 88 | thr = 1 / thr 89 | 90 | def metric(k, wh): # compute metrics 91 | r = wh[:, None] / k[None] 92 | x = torch.min(r, 1 / r).min(2)[0] # ratio metric 93 | # x = wh_iou(wh, torch.tensor(k)) # iou metric 94 | return x, x.max(1)[0] # x, best_x 95 | 96 | def anchor_fitness(k): # mutation fitness 97 | _, best = metric(torch.tensor(k, dtype=torch.float32), wh) 98 | return (best * (best > thr).float()).mean() # fitness 99 | 100 | def print_results(k, verbose=True): 101 | k = k[np.argsort(k.prod(1))] # sort small to large 102 | x, best = metric(k, wh0) 103 | bpr, aat = (best > thr).float().mean(), (x > thr).float().mean() * n # best possible recall, anch > thr 104 | s = f'{PREFIX}thr={thr:.2f}: {bpr:.4f} best possible recall, {aat:.2f} anchors past thr\n' \ 105 | f'{PREFIX}n={n}, img_size={img_size}, metric_all={x.mean():.3f}/{best.mean():.3f}-mean/best, ' \ 106 | f'past_thr={x[x > thr].mean():.3f}-mean: ' 107 | for x in k: 108 | s += '%i,%i, ' % (round(x[0]), round(x[1])) 109 | if verbose: 110 | LOGGER.info(s[:-2]) 111 | return k 112 | 113 | if isinstance(dataset, str): # *.yaml file 114 | with open(dataset, errors='ignore') as f: 115 | data_dict = yaml.safe_load(f) # model dict 116 | from utils.dataloaders import LoadImagesAndLabels 117 | dataset = LoadImagesAndLabels(data_dict['train'], augment=True, rect=True) 118 | 119 | # Get label wh 120 | shapes = img_size * dataset.shapes / dataset.shapes.max(1, keepdims=True) 121 | wh0 = np.concatenate([l[:, 3:5] * s for s, l in zip(shapes, dataset.labels)]) # wh 122 | 123 | # Filter 124 | i = (wh0 < 3.0).any(1).sum() 125 | if i: 126 | LOGGER.info(f'{PREFIX}WARNING: Extremely small objects found: {i} of {len(wh0)} labels are < 3 pixels in size') 127 | wh = wh0[(wh0 >= 2.0).any(1)] # filter > 2 pixels 128 | # wh = wh * (npr.rand(wh.shape[0], 1) * 0.9 + 0.1) # multiply by random scale 0-1 129 | 130 | # Kmeans init 131 | try: 132 | LOGGER.info(f'{PREFIX}Running kmeans for {n} anchors on {len(wh)} points...') 133 | assert n <= len(wh) # apply overdetermined constraint 134 | s = wh.std(0) # sigmas for whitening 135 | k = kmeans(wh / s, n, iter=30)[0] * s # points 136 | assert n == len(k) # kmeans may return fewer points than requested if wh is insufficient or too similar 137 | except Exception: 138 | LOGGER.warning(f'{PREFIX}WARNING: switching strategies from kmeans to random init') 139 | k = np.sort(npr.rand(n * 2)).reshape(n, 2) * img_size # random init 140 | wh, wh0 = (torch.tensor(x, dtype=torch.float32) for x in (wh, wh0)) 141 | k = print_results(k, verbose=False) 142 | 143 | # Plot 144 | # k, d = [None] * 20, [None] * 20 145 | # for i in tqdm(range(1, 21)): 146 | # k[i-1], d[i-1] = kmeans(wh / s, i) # points, mean distance 147 | # fig, ax = plt.subplots(1, 2, figsize=(14, 7), tight_layout=True) 148 | # ax = ax.ravel() 149 | # ax[0].plot(np.arange(1, 21), np.array(d) ** 2, marker='.') 150 | # fig, ax = plt.subplots(1, 2, figsize=(14, 7)) # plot wh 151 | # ax[0].hist(wh[wh[:, 0]<100, 0],400) 152 | # ax[1].hist(wh[wh[:, 1]<100, 1],400) 153 | # fig.savefig('wh.png', dpi=200) 154 | 155 | # Evolve 156 | f, sh, mp, s = anchor_fitness(k), k.shape, 0.9, 0.1 # fitness, generations, mutation prob, sigma 157 | pbar = tqdm(range(gen), bar_format='{l_bar}{bar:10}{r_bar}{bar:-10b}') # progress bar 158 | for _ in pbar: 159 | v = np.ones(sh) 160 | while (v == 1).all(): # mutate until a change occurs (prevent duplicates) 161 | v = ((npr.random(sh) < mp) * random.random() * npr.randn(*sh) * s + 1).clip(0.3, 3.0) 162 | kg = (k.copy() * v).clip(min=2.0) 163 | fg = anchor_fitness(kg) 164 | if fg > f: 165 | f, k = fg, kg.copy() 166 | pbar.desc = f'{PREFIX}Evolving anchors with Genetic Algorithm: fitness = {f:.4f}' 167 | if verbose: 168 | print_results(k, verbose) 169 | 170 | return print_results(k) 171 | -------------------------------------------------------------------------------- /utils/autobatch.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 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 | 11 | from utils.general import LOGGER, colorstr 12 | from utils.torch_utils import profile 13 | 14 | 15 | def check_train_batch_size(model, imgsz=640, amp=True): 16 | # Check YOLOv5 training batch size 17 | with torch.cuda.amp.autocast(amp): 18 | return autobatch(deepcopy(model).train(), imgsz) # compute optimal batch size 19 | 20 | 21 | def autobatch(model, imgsz=640, fraction=0.9, batch_size=16): 22 | # Automatically estimate best batch size to use `fraction` of available CUDA memory 23 | # Usage: 24 | # import torch 25 | # from utils.autobatch import autobatch 26 | # model = torch.hub.load('ultralytics/yolov5', 'yolov5s', autoshape=False) 27 | # print(autobatch(model)) 28 | 29 | # Check device 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 | # Inspect CUDA memory 38 | gb = 1 << 30 # bytes to GiB (1024 ** 3) 39 | d = str(device).upper() # 'CUDA:0' 40 | properties = torch.cuda.get_device_properties(device) # device properties 41 | t = properties.total_memory / gb # GiB total 42 | r = torch.cuda.memory_reserved(device) / gb # GiB reserved 43 | a = torch.cuda.memory_allocated(device) / gb # GiB allocated 44 | f = t - (r + a) # GiB free 45 | LOGGER.info(f'{prefix}{d} ({properties.name}) {t:.2f}G total, {r:.2f}G reserved, {a:.2f}G allocated, {f:.2f}G free') 46 | 47 | # Profile batch sizes 48 | batch_sizes = [1, 2, 4, 8, 16] 49 | try: 50 | img = [torch.empty(b, 3, imgsz, imgsz) for b in batch_sizes] 51 | results = profile(img, model, n=3, device=device) 52 | except Exception as e: 53 | LOGGER.warning(f'{prefix}{e}') 54 | 55 | # Fit a solution 56 | y = [x[2] for x in results if x] # memory [2] 57 | p = np.polyfit(batch_sizes[:len(y)], y, deg=1) # first degree polynomial fit 58 | b = int((f * fraction - p[1]) / p[0]) # y intercept (optimal batch size) 59 | if None in results: # some sizes failed 60 | i = results.index(None) # first fail index 61 | if b >= batch_sizes[i]: # y intercept above failure point 62 | b = batch_sizes[max(i - 1, 0)] # select prior safe point 63 | if b < 1: # zero or negative batch size 64 | b = 16 65 | LOGGER.warning(f'{prefix}WARNING: ⚠️ CUDA anomaly detected, recommend restart environment and retry command.') 66 | 67 | fraction = np.polyval(p, b) / t # actual fraction predicted 68 | LOGGER.info(f'{prefix}Using batch-size {b} for {d} {t * fraction:.2f}G/{t:.2f}G ({fraction * 100:.0f}%) ✅') 69 | return b 70 | -------------------------------------------------------------------------------- /utils/aws/__init__.py: -------------------------------------------------------------------------------- 1 | # init -------------------------------------------------------------------------------- /utils/aws/mime.sh: -------------------------------------------------------------------------------- 1 | # AWS EC2 instance startup 'MIME' script https://aws.amazon.com/premiumsupport/knowledge-center/execute-user-data-ec2/ 2 | # This script will run on every instance restart, not only on first start 3 | # --- DO NOT COPY ABOVE COMMENTS WHEN PASTING INTO USERDATA --- 4 | 5 | Content-Type: multipart/mixed; boundary="//" 6 | MIME-Version: 1.0 7 | 8 | --// 9 | Content-Type: text/cloud-config; charset="us-ascii" 10 | MIME-Version: 1.0 11 | Content-Transfer-Encoding: 7bit 12 | Content-Disposition: attachment; filename="cloud-config.txt" 13 | 14 | #cloud-config 15 | cloud_final_modules: 16 | - [scripts-user, always] 17 | 18 | --// 19 | Content-Type: text/x-shellscript; charset="us-ascii" 20 | MIME-Version: 1.0 21 | Content-Transfer-Encoding: 7bit 22 | Content-Disposition: attachment; filename="userdata.txt" 23 | 24 | #!/bin/bash 25 | # --- paste contents of userdata.sh here --- 26 | --// 27 | -------------------------------------------------------------------------------- /utils/aws/resume.py: -------------------------------------------------------------------------------- 1 | # Resume all interrupted trainings in yolov5/ dir including DDP trainings 2 | # Usage: $ python utils/aws/resume.py 3 | 4 | import os 5 | import sys 6 | from pathlib import Path 7 | 8 | import torch 9 | import yaml 10 | 11 | FILE = Path(__file__).resolve() 12 | ROOT = FILE.parents[2] # YOLOv5 root directory 13 | if str(ROOT) not in sys.path: 14 | sys.path.append(str(ROOT)) # add ROOT to PATH 15 | 16 | port = 0 # --master_port 17 | path = Path('').resolve() 18 | for last in path.rglob('*/**/last.pt'): 19 | ckpt = torch.load(last) 20 | if ckpt['optimizer'] is None: 21 | continue 22 | 23 | # Load opt.yaml 24 | with open(last.parent.parent / 'opt.yaml', errors='ignore') as f: 25 | opt = yaml.safe_load(f) 26 | 27 | # Get device count 28 | d = opt['device'].split(',') # devices 29 | nd = len(d) # number of devices 30 | ddp = nd > 1 or (nd == 0 and torch.cuda.device_count() > 1) # distributed data parallel 31 | 32 | if ddp: # multi-GPU 33 | port += 1 34 | cmd = f'python -m torch.distributed.run --nproc_per_node {nd} --master_port {port} train.py --resume {last}' 35 | else: # single-GPU 36 | cmd = f'python train.py --resume {last}' 37 | 38 | cmd += ' > /dev/null 2>&1 &' # redirect output to dev/null and run in daemon thread 39 | print(cmd) 40 | os.system(cmd) 41 | -------------------------------------------------------------------------------- /utils/aws/userdata.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # AWS EC2 instance startup script https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html 3 | # This script will run only once on first instance start (for a re-start script see mime.sh) 4 | # /home/ubuntu (ubuntu) or /home/ec2-user (amazon-linux) is working dir 5 | # Use >300 GB SSD 6 | 7 | cd home/ubuntu 8 | if [ ! -d yolov5 ]; then 9 | echo "Running first-time script." # install dependencies, download COCO, pull Docker 10 | git clone https://github.com/ultralytics/yolov5 -b master && sudo chmod -R 777 yolov5 11 | cd yolov5 12 | bash data/scripts/get_coco.sh && echo "COCO done." & 13 | sudo docker pull ultralytics/yolov5:latest && echo "Docker done." & 14 | python -m pip install --upgrade pip && pip install -r requirements.txt && python detect.py && echo "Requirements done." & 15 | wait && echo "All tasks done." # finish background tasks 16 | else 17 | echo "Running re-start script." # resume interrupted runs 18 | i=0 19 | list=$(sudo docker ps -qa) # container list i.e. $'one\ntwo\nthree\nfour' 20 | while IFS= read -r id; do 21 | ((i++)) 22 | echo "restarting container $i: $id" 23 | sudo docker start $id 24 | # sudo docker exec -it $id python train.py --resume # single-GPU 25 | sudo docker exec -d $id python utils/aws/resume.py # multi-scenario 26 | done <<<"$list" 27 | fi 28 | -------------------------------------------------------------------------------- /utils/benchmarks.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Run YOLOv5 benchmarks on all supported export formats 4 | 5 | Format | `export.py --include` | Model 6 | --- | --- | --- 7 | PyTorch | - | yolov5s.pt 8 | TorchScript | `torchscript` | yolov5s.torchscript 9 | ONNX | `onnx` | yolov5s.onnx 10 | OpenVINO | `openvino` | yolov5s_openvino_model/ 11 | TensorRT | `engine` | yolov5s.engine 12 | CoreML | `coreml` | yolov5s.mlmodel 13 | TensorFlow SavedModel | `saved_model` | yolov5s_saved_model/ 14 | TensorFlow GraphDef | `pb` | yolov5s.pb 15 | TensorFlow Lite | `tflite` | yolov5s.tflite 16 | TensorFlow Edge TPU | `edgetpu` | yolov5s_edgetpu.tflite 17 | TensorFlow.js | `tfjs` | yolov5s_web_model/ 18 | 19 | Requirements: 20 | $ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime openvino-dev tensorflow-cpu # CPU 21 | $ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime-gpu openvino-dev tensorflow # GPU 22 | $ pip install -U nvidia-tensorrt --index-url https://pypi.ngc.nvidia.com # TensorRT 23 | 24 | Usage: 25 | $ python utils/benchmarks.py --weights yolov5s.pt --img 640 26 | """ 27 | 28 | import argparse 29 | import platform 30 | import sys 31 | import time 32 | from pathlib import Path 33 | 34 | import pandas as pd 35 | 36 | FILE = Path(__file__).resolve() 37 | ROOT = FILE.parents[1] # YOLOv5 root directory 38 | if str(ROOT) not in sys.path: 39 | sys.path.append(str(ROOT)) # add ROOT to PATH 40 | # ROOT = ROOT.relative_to(Path.cwd()) # relative 41 | 42 | import export 43 | import val 44 | from utils import notebook_init 45 | from utils.general import LOGGER, check_yaml, file_size, print_args 46 | from utils.torch_utils import select_device 47 | 48 | 49 | def run( 50 | weights=ROOT / 'yolov5s.pt', # weights path 51 | imgsz=640, # inference size (pixels) 52 | batch_size=1, # batch size 53 | data=ROOT / 'data/coco128.yaml', # dataset.yaml path 54 | device='', # cuda device, i.e. 0 or 0,1,2,3 or cpu 55 | half=False, # use FP16 half-precision inference 56 | test=False, # test exports only 57 | pt_only=False, # test PyTorch only 58 | hard_fail=False, # throw error on benchmark failure 59 | ): 60 | y, t = [], time.time() 61 | device = select_device(device) 62 | for i, (name, f, suffix, cpu, gpu) in export.export_formats().iterrows(): # index, (name, file, suffix, CPU, GPU) 63 | try: 64 | assert i not in (9, 10), 'inference not supported' # Edge TPU and TF.js are unsupported 65 | assert i != 5 or platform.system() == 'Darwin', 'inference only supported on macOS>=10.13' # CoreML 66 | if 'cpu' in device.type: 67 | assert cpu, 'inference not supported on CPU' 68 | if 'cuda' in device.type: 69 | assert gpu, 'inference not supported on GPU' 70 | 71 | # Export 72 | if f == '-': 73 | w = weights # PyTorch format 74 | else: 75 | w = export.run(weights=weights, imgsz=[imgsz], include=[f], device=device, half=half)[-1] # all others 76 | assert suffix in str(w), 'export failed' 77 | 78 | # Validate 79 | result = val.run(data, w, batch_size, imgsz, plots=False, device=device, task='benchmark', half=half) 80 | metrics = result[0] # metrics (mp, mr, map50, map, *losses(box, obj, cls)) 81 | speeds = result[2] # times (preprocess, inference, postprocess) 82 | y.append([name, round(file_size(w), 1), round(metrics[3], 4), round(speeds[1], 2)]) # MB, mAP, t_inference 83 | except Exception as e: 84 | if hard_fail: 85 | assert type(e) is AssertionError, f'Benchmark --hard-fail for {name}: {e}' 86 | LOGGER.warning(f'WARNING: Benchmark failure for {name}: {e}') 87 | y.append([name, None, None, None]) # mAP, t_inference 88 | if pt_only and i == 0: 89 | break # break after PyTorch 90 | 91 | # Print results 92 | LOGGER.info('\n') 93 | parse_opt() 94 | notebook_init() # print system info 95 | c = ['Format', 'Size (MB)', 'mAP@0.5:0.95', 'Inference time (ms)'] if map else ['Format', 'Export', '', ''] 96 | py = pd.DataFrame(y, columns=c) 97 | LOGGER.info(f'\nBenchmarks complete ({time.time() - t:.2f}s)') 98 | LOGGER.info(str(py if map else py.iloc[:, :2])) 99 | return py 100 | 101 | 102 | def test( 103 | weights=ROOT / 'yolov5s.pt', # weights path 104 | imgsz=640, # inference size (pixels) 105 | batch_size=1, # batch size 106 | data=ROOT / 'data/coco128.yaml', # dataset.yaml path 107 | device='', # cuda device, i.e. 0 or 0,1,2,3 or cpu 108 | half=False, # use FP16 half-precision inference 109 | test=False, # test exports only 110 | pt_only=False, # test PyTorch only 111 | hard_fail=False, # throw error on benchmark failure 112 | ): 113 | y, t = [], time.time() 114 | device = select_device(device) 115 | for i, (name, f, suffix, gpu) in export.export_formats().iterrows(): # index, (name, file, suffix, gpu-capable) 116 | try: 117 | w = weights if f == '-' else \ 118 | export.run(weights=weights, imgsz=[imgsz], include=[f], device=device, half=half)[-1] # weights 119 | assert suffix in str(w), 'export failed' 120 | y.append([name, True]) 121 | except Exception: 122 | y.append([name, False]) # mAP, t_inference 123 | 124 | # Print results 125 | LOGGER.info('\n') 126 | parse_opt() 127 | notebook_init() # print system info 128 | py = pd.DataFrame(y, columns=['Format', 'Export']) 129 | LOGGER.info(f'\nExports complete ({time.time() - t:.2f}s)') 130 | LOGGER.info(str(py)) 131 | return py 132 | 133 | 134 | def parse_opt(): 135 | parser = argparse.ArgumentParser() 136 | parser.add_argument('--weights', type=str, default=ROOT / 'yolov5s.pt', help='weights path') 137 | parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=640, help='inference size (pixels)') 138 | parser.add_argument('--batch-size', type=int, default=1, help='batch size') 139 | parser.add_argument('--data', type=str, default=ROOT / 'data/coco128.yaml', help='dataset.yaml path') 140 | parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') 141 | parser.add_argument('--half', action='store_true', help='use FP16 half-precision inference') 142 | parser.add_argument('--test', action='store_true', help='test exports only') 143 | parser.add_argument('--pt-only', action='store_true', help='test PyTorch only') 144 | parser.add_argument('--hard-fail', action='store_true', help='throw error on benchmark failure') 145 | opt = parser.parse_args() 146 | opt.data = check_yaml(opt.data) # check YAML 147 | print_args(vars(opt)) 148 | return opt 149 | 150 | 151 | def main(opt): 152 | test(**vars(opt)) if opt.test else run(**vars(opt)) 153 | 154 | 155 | if __name__ == "__main__": 156 | opt = parse_opt() 157 | main(opt) 158 | -------------------------------------------------------------------------------- /utils/callbacks.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Callback utils 4 | """ 5 | 6 | import threading 7 | 8 | 9 | class Callbacks: 10 | """" 11 | Handles all registered callbacks for YOLOv5 Hooks 12 | """ 13 | 14 | def __init__(self): 15 | # Define the available callbacks 16 | self._callbacks = { 17 | 'on_pretrain_routine_start': [], 18 | 'on_pretrain_routine_end': [], 19 | 'on_train_start': [], 20 | 'on_train_epoch_start': [], 21 | 'on_train_batch_start': [], 22 | 'optimizer_step': [], 23 | 'on_before_zero_grad': [], 24 | 'on_train_batch_end': [], 25 | 'on_train_epoch_end': [], 26 | 'on_val_start': [], 27 | 'on_val_batch_start': [], 28 | 'on_val_image_end': [], 29 | 'on_val_batch_end': [], 30 | 'on_val_end': [], 31 | 'on_fit_epoch_end': [], # fit = train + val 32 | 'on_model_save': [], 33 | 'on_train_end': [], 34 | 'on_params_update': [], 35 | 'teardown': [],} 36 | self.stop_training = False # set True to interrupt training 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 | return self._callbacks[hook] if hook else self._callbacks 59 | 60 | def run(self, hook, *args, thread=False, **kwargs): 61 | """ 62 | Loop through the registered actions and fire all callbacks on main thread 63 | 64 | Args: 65 | hook: The name of the hook to check, defaults to all 66 | args: Arguments to receive from YOLOv5 67 | thread: (boolean) Run callbacks in daemon thread 68 | kwargs: Keyword Arguments to receive from YOLOv5 69 | """ 70 | 71 | assert hook in self._callbacks, f"hook '{hook}' not found in callbacks {self._callbacks}" 72 | for logger in self._callbacks[hook]: 73 | if thread: 74 | threading.Thread(target=logger['callback'], args=args, kwargs=kwargs, daemon=True).start() 75 | else: 76 | logger['callback'](*args, **kwargs) 77 | -------------------------------------------------------------------------------- /utils/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | # Builds ultralytics/yolov5:latest image on DockerHub https://hub.docker.com/r/ultralytics/yolov5 3 | # Image is CUDA-optimized for YOLOv5 single/multi-GPU training and inference 4 | 5 | # Start FROM NVIDIA PyTorch image https://ngc.nvidia.com/catalog/containers/nvidia:pytorch 6 | FROM nvcr.io/nvidia/pytorch:22.07-py3 7 | RUN rm -rf /opt/pytorch # remove 1.2GB dir 8 | 9 | # Downloads to user config dir 10 | ADD https://ultralytics.com/assets/Arial.ttf https://ultralytics.com/assets/Arial.Unicode.ttf /root/.config/Ultralytics/ 11 | 12 | # Install linux packages 13 | RUN apt update && apt install --no-install-recommends -y zip htop screen libgl1-mesa-glx 14 | 15 | # Install pip packages 16 | COPY requirements.txt . 17 | RUN python -m pip install --upgrade pip wheel 18 | RUN pip uninstall -y Pillow torchtext torch torchvision 19 | RUN pip install --no-cache -r requirements.txt albumentations wandb gsutil notebook Pillow>=9.1.0 \ 20 | 'opencv-python<4.6.0.66' \ 21 | --extra-index-url https://download.pytorch.org/whl/cu113 22 | 23 | # Create working directory 24 | RUN mkdir -p /usr/src/app 25 | WORKDIR /usr/src/app 26 | 27 | # Copy contents 28 | # COPY . /usr/src/app (issues as not a .git directory) 29 | RUN git clone https://github.com/ultralytics/yolov5 /usr/src/app 30 | 31 | # Set environment variables 32 | ENV OMP_NUM_THREADS=8 33 | 34 | 35 | # Usage Examples ------------------------------------------------------------------------------------------------------- 36 | 37 | # Build and Push 38 | # t=ultralytics/yolov5:latest && sudo docker build -f utils/docker/Dockerfile -t $t . && sudo docker push $t 39 | 40 | # Pull and Run 41 | # t=ultralytics/yolov5:latest && sudo docker pull $t && sudo docker run -it --ipc=host --gpus all $t 42 | 43 | # Pull and Run with local directory access 44 | # t=ultralytics/yolov5:latest && sudo docker pull $t && sudo docker run -it --ipc=host --gpus all -v "$(pwd)"/datasets:/usr/src/datasets $t 45 | 46 | # Kill all 47 | # sudo docker kill $(sudo docker ps -q) 48 | 49 | # Kill all image-based 50 | # sudo docker kill $(sudo docker ps -qa --filter ancestor=ultralytics/yolov5:latest) 51 | 52 | # DockerHub tag update 53 | # t=ultralytics/yolov5:latest tnew=ultralytics/yolov5:v6.2 && sudo docker pull $t && sudo docker tag $t $tnew && sudo docker push $tnew 54 | 55 | # Clean up 56 | # docker system prune -a --volumes 57 | 58 | # Update Ubuntu drivers 59 | # https://www.maketecheasier.com/install-nvidia-drivers-ubuntu/ 60 | 61 | # DDP test 62 | # python -m torch.distributed.run --nproc_per_node 2 --master_port 1 train.py --epochs 3 63 | 64 | # GCP VM from Image 65 | # docker.io/ultralytics/yolov5:latest 66 | -------------------------------------------------------------------------------- /utils/docker/Dockerfile-arm64: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | # Builds ultralytics/yolov5:latest-arm64 image on DockerHub https://hub.docker.com/r/ultralytics/yolov5 3 | # Image is aarch64-compatible for Apple M1 and other ARM architectures i.e. Jetson Nano and Raspberry Pi 4 | 5 | # Start FROM Ubuntu image https://hub.docker.com/_/ubuntu 6 | FROM arm64v8/ubuntu:20.04 7 | 8 | # Downloads to user config dir 9 | ADD https://ultralytics.com/assets/Arial.ttf https://ultralytics.com/assets/Arial.Unicode.ttf /root/.config/Ultralytics/ 10 | 11 | # Install linux packages 12 | RUN apt update 13 | RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt install -y tzdata 14 | RUN apt install --no-install-recommends -y python3-pip git zip curl htop gcc \ 15 | libgl1-mesa-glx libglib2.0-0 libpython3.8-dev 16 | # RUN alias python=python3 17 | 18 | # Install pip packages 19 | COPY requirements.txt . 20 | RUN python3 -m pip install --upgrade pip wheel 21 | RUN pip install --no-cache -r requirements.txt gsutil notebook \ 22 | tensorflow-aarch64 23 | # tensorflowjs \ 24 | # onnx onnx-simplifier onnxruntime \ 25 | # coremltools openvino-dev \ 26 | 27 | # Create working directory 28 | RUN mkdir -p /usr/src/app 29 | WORKDIR /usr/src/app 30 | 31 | # Copy contents 32 | # COPY . /usr/src/app (issues as not a .git directory) 33 | RUN git clone https://github.com/ultralytics/yolov5 /usr/src/app 34 | 35 | 36 | # Usage Examples ------------------------------------------------------------------------------------------------------- 37 | 38 | # Build and Push 39 | # t=ultralytics/yolov5:latest-M1 && sudo docker build --platform linux/arm64 -f utils/docker/Dockerfile-arm64 -t $t . && sudo docker push $t 40 | 41 | # Pull and Run 42 | # t=ultralytics/yolov5:latest-M1 && sudo docker pull $t && sudo docker run -it --ipc=host -v "$(pwd)"/datasets:/usr/src/datasets $t 43 | -------------------------------------------------------------------------------- /utils/docker/Dockerfile-cpu: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | # Builds ultralytics/yolov5:latest-cpu image on DockerHub https://hub.docker.com/r/ultralytics/yolov5 3 | # Image is CPU-optimized for ONNX, OpenVINO and PyTorch YOLOv5 deployments 4 | 5 | # Start FROM Ubuntu image https://hub.docker.com/_/ubuntu 6 | FROM ubuntu:20.04 7 | 8 | # Downloads to user config dir 9 | ADD https://ultralytics.com/assets/Arial.ttf https://ultralytics.com/assets/Arial.Unicode.ttf /root/.config/Ultralytics/ 10 | 11 | # Install linux packages 12 | RUN apt update 13 | RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt install -y tzdata 14 | RUN apt install --no-install-recommends -y python3-pip git zip curl htop libgl1-mesa-glx libglib2.0-0 libpython3-dev 15 | # RUN alias python=python3 16 | 17 | # Install pip packages 18 | COPY requirements.txt . 19 | RUN python3 -m pip install --upgrade pip wheel 20 | RUN pip install --no-cache -r requirements.txt albumentations gsutil notebook \ 21 | coremltools onnx onnx-simplifier onnxruntime tensorflow-cpu tensorflowjs \ 22 | # openvino-dev \ 23 | --extra-index-url https://download.pytorch.org/whl/cpu 24 | 25 | # Create working directory 26 | RUN mkdir -p /usr/src/app 27 | WORKDIR /usr/src/app 28 | 29 | # Copy contents 30 | # COPY . /usr/src/app (issues as not a .git directory) 31 | RUN git clone https://github.com/ultralytics/yolov5 /usr/src/app 32 | 33 | 34 | # Usage Examples ------------------------------------------------------------------------------------------------------- 35 | 36 | # Build and Push 37 | # t=ultralytics/yolov5:latest-cpu && sudo docker build -f utils/docker/Dockerfile-cpu -t $t . && sudo docker push $t 38 | 39 | # Pull and Run 40 | # t=ultralytics/yolov5:latest-cpu && sudo docker pull $t && sudo docker run -it --ipc=host -v "$(pwd)"/datasets:/usr/src/datasets $t 41 | -------------------------------------------------------------------------------- /utils/downloads.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Download utils 4 | """ 5 | 6 | import logging 7 | import os 8 | import platform 9 | import subprocess 10 | import time 11 | import urllib 12 | from pathlib import Path 13 | from zipfile import ZipFile 14 | 15 | import requests 16 | import torch 17 | 18 | 19 | def is_url(url, check_online=True): 20 | # Check if online file exists 21 | try: 22 | url = str(url) 23 | result = urllib.parse.urlparse(url) 24 | assert all([result.scheme, result.netloc, result.path]) # check if is url 25 | return (urllib.request.urlopen(url).getcode() == 200) if check_online else True # check if exists online 26 | except (AssertionError, urllib.request.HTTPError): 27 | return False 28 | 29 | 30 | def gsutil_getsize(url=''): 31 | # gs://bucket/file size https://cloud.google.com/storage/docs/gsutil/commands/du 32 | s = subprocess.check_output(f'gsutil du {url}', shell=True).decode('utf-8') 33 | return eval(s.split(' ')[0]) if len(s) else 0 # bytes 34 | 35 | 36 | def safe_download(file, url, url2=None, min_bytes=1E0, error_msg=''): 37 | # Attempts to download file from url or url2, checks and removes incomplete downloads < min_bytes 38 | from utils.general import LOGGER 39 | 40 | file = Path(file) 41 | assert_msg = f"Downloaded file '{file}' does not exist or size is < min_bytes={min_bytes}" 42 | try: # url1 43 | LOGGER.info(f'Downloading {url} to {file}...') 44 | torch.hub.download_url_to_file(url, str(file), progress=LOGGER.level <= logging.INFO) 45 | assert file.exists() and file.stat().st_size > min_bytes, assert_msg # check 46 | except Exception as e: # url2 47 | file.unlink(missing_ok=True) # remove partial downloads 48 | LOGGER.info(f'ERROR: {e}\nRe-attempting {url2 or url} to {file}...') 49 | os.system(f"curl -# -L '{url2 or url}' -o '{file}' --retry 3 -C -") # curl download, retry and resume on fail 50 | finally: 51 | if not file.exists() or file.stat().st_size < min_bytes: # check 52 | file.unlink(missing_ok=True) # remove partial downloads 53 | LOGGER.info(f"ERROR: {assert_msg}\n{error_msg}") 54 | LOGGER.info('') 55 | 56 | 57 | def attempt_download(file, repo='ultralytics/yolov5', release='v6.2'): 58 | # Attempt file download from GitHub release assets if not found locally. release = 'latest', 'v6.2', etc. 59 | from utils.general import LOGGER 60 | 61 | def github_assets(repository, version='latest'): 62 | # Return GitHub repo tag (i.e. 'v6.2') and assets (i.e. ['yolov5s.pt', 'yolov5m.pt', ...]) 63 | if version != 'latest': 64 | version = f'tags/{version}' # i.e. tags/v6.2 65 | response = requests.get(f'https://api.github.com/repos/{repository}/releases/{version}').json() # github api 66 | return response['tag_name'], [x['name'] for x in response['assets']] # tag, assets 67 | 68 | file = Path(str(file).strip().replace("'", '')) 69 | if not file.exists(): 70 | # URL specified 71 | name = Path(urllib.parse.unquote(str(file))).name # decode '%2F' to '/' etc. 72 | if str(file).startswith(('http:/', 'https:/')): # download 73 | url = str(file).replace(':/', '://') # Pathlib turns :// -> :/ 74 | file = name.split('?')[0] # parse authentication https://url.com/file.txt?auth... 75 | if Path(file).is_file(): 76 | LOGGER.info(f'Found {url} locally at {file}') # file already exists 77 | else: 78 | safe_download(file=file, url=url, min_bytes=1E5) 79 | return file 80 | 81 | # GitHub assets 82 | assets = [ 83 | 'yolov5n.pt', 'yolov5s.pt', 'yolov5m.pt', 'yolov5l.pt', 'yolov5x.pt', 'yolov5n6.pt', 'yolov5s6.pt', 84 | 'yolov5m6.pt', 'yolov5l6.pt', 'yolov5x6.pt'] 85 | try: 86 | tag, assets = github_assets(repo, release) 87 | except Exception: 88 | try: 89 | tag, assets = github_assets(repo) # latest release 90 | except Exception: 91 | try: 92 | tag = subprocess.check_output('git tag', shell=True, stderr=subprocess.STDOUT).decode().split()[-1] 93 | except Exception: 94 | tag = release 95 | 96 | file.parent.mkdir(parents=True, exist_ok=True) # make parent dir (if required) 97 | if name in assets: 98 | url3 = 'https://drive.google.com/drive/folders/1EFQTEUeXWSFww0luse2jB9M1QNZQGwNl' # backup gdrive mirror 99 | safe_download( 100 | file, 101 | url=f'https://github.com/{repo}/releases/download/{tag}/{name}', 102 | url2=f'https://storage.googleapis.com/{repo}/{tag}/{name}', # backup url (optional) 103 | min_bytes=1E5, 104 | error_msg=f'{file} missing, try downloading from https://github.com/{repo}/releases/{tag} or {url3}') 105 | 106 | return str(file) 107 | 108 | 109 | def gdrive_download(id='16TiPfZj7htmTyhntwcZyEEAejOUxuT6m', file='tmp.zip'): 110 | # Downloads a file from Google Drive. from yolov5.utils.downloads import *; gdrive_download() 111 | t = time.time() 112 | file = Path(file) 113 | cookie = Path('cookie') # gdrive cookie 114 | print(f'Downloading https://drive.google.com/uc?export=download&id={id} as {file}... ', end='') 115 | file.unlink(missing_ok=True) # remove existing file 116 | cookie.unlink(missing_ok=True) # remove existing cookie 117 | 118 | # Attempt file download 119 | out = "NUL" if platform.system() == "Windows" else "/dev/null" 120 | os.system(f'curl -c ./cookie -s -L "drive.google.com/uc?export=download&id={id}" > {out}') 121 | if os.path.exists('cookie'): # large file 122 | s = f'curl -Lb ./cookie "drive.google.com/uc?export=download&confirm={get_token()}&id={id}" -o {file}' 123 | else: # small file 124 | s = f'curl -s -L -o {file} "drive.google.com/uc?export=download&id={id}"' 125 | r = os.system(s) # execute, capture return 126 | cookie.unlink(missing_ok=True) # remove existing cookie 127 | 128 | # Error check 129 | if r != 0: 130 | file.unlink(missing_ok=True) # remove partial 131 | print('Download error ') # raise Exception('Download error') 132 | return r 133 | 134 | # Unzip if archive 135 | if file.suffix == '.zip': 136 | print('unzipping... ', end='') 137 | ZipFile(file).extractall(path=file.parent) # unzip 138 | file.unlink() # remove zip 139 | 140 | print(f'Done ({time.time() - t:.1f}s)') 141 | return r 142 | 143 | 144 | def get_token(cookie="./cookie"): 145 | with open(cookie) as f: 146 | for line in f: 147 | if "download" in line: 148 | return line.split()[-1] 149 | return "" 150 | 151 | 152 | # Google utils: https://cloud.google.com/storage/docs/reference/libraries ---------------------------------------------- 153 | # 154 | # 155 | # def upload_blob(bucket_name, source_file_name, destination_blob_name): 156 | # # Uploads a file to a bucket 157 | # # https://cloud.google.com/storage/docs/uploading-objects#storage-upload-object-python 158 | # 159 | # storage_client = storage.Client() 160 | # bucket = storage_client.get_bucket(bucket_name) 161 | # blob = bucket.blob(destination_blob_name) 162 | # 163 | # blob.upload_from_filename(source_file_name) 164 | # 165 | # print('File {} uploaded to {}.'.format( 166 | # source_file_name, 167 | # destination_blob_name)) 168 | # 169 | # 170 | # def download_blob(bucket_name, source_blob_name, destination_file_name): 171 | # # Uploads a blob from a bucket 172 | # storage_client = storage.Client() 173 | # bucket = storage_client.get_bucket(bucket_name) 174 | # blob = bucket.blob(source_blob_name) 175 | # 176 | # blob.download_to_filename(destination_file_name) 177 | # 178 | # print('Blob {} downloaded to {}.'.format( 179 | # source_blob_name, 180 | # destination_file_name)) 181 | -------------------------------------------------------------------------------- /utils/flask_rest_api/README.md: -------------------------------------------------------------------------------- 1 | # Flask REST API 2 | 3 | [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) [API](https://en.wikipedia.org/wiki/API)s are 4 | commonly used to expose Machine Learning (ML) models to other services. This folder contains an example REST API 5 | created using Flask to expose the YOLOv5s model from [PyTorch Hub](https://pytorch.org/hub/ultralytics_yolov5/). 6 | 7 | ## Requirements 8 | 9 | [Flask](https://palletsprojects.com/p/flask/) is required. Install with: 10 | 11 | ```shell 12 | $ pip install Flask 13 | ``` 14 | 15 | ## Run 16 | 17 | After Flask installation run: 18 | 19 | ```shell 20 | $ python3 restapi.py --port 5000 21 | ``` 22 | 23 | Then use [curl](https://curl.se/) to perform a request: 24 | 25 | ```shell 26 | $ curl -X POST -F image=@zidane.jpg 'http://localhost:5000/v1/object-detection/yolov5s' 27 | ``` 28 | 29 | The model inference results are returned as a JSON response: 30 | 31 | ```json 32 | [ 33 | { 34 | "class": 0, 35 | "confidence": 0.8900438547, 36 | "height": 0.9318675399, 37 | "name": "person", 38 | "width": 0.3264600933, 39 | "xcenter": 0.7438579798, 40 | "ycenter": 0.5207948685 41 | }, 42 | { 43 | "class": 0, 44 | "confidence": 0.8440024257, 45 | "height": 0.7155083418, 46 | "name": "person", 47 | "width": 0.6546785235, 48 | "xcenter": 0.427829951, 49 | "ycenter": 0.6334488392 50 | }, 51 | { 52 | "class": 27, 53 | "confidence": 0.3771208823, 54 | "height": 0.3902671337, 55 | "name": "tie", 56 | "width": 0.0696444362, 57 | "xcenter": 0.3675483763, 58 | "ycenter": 0.7991207838 59 | }, 60 | { 61 | "class": 27, 62 | "confidence": 0.3527112305, 63 | "height": 0.1540903747, 64 | "name": "tie", 65 | "width": 0.0336618312, 66 | "xcenter": 0.7814827561, 67 | "ycenter": 0.5065554976 68 | } 69 | ] 70 | ``` 71 | 72 | An example python script to perform inference using [requests](https://docs.python-requests.org/en/master/) is given 73 | in `example_request.py` 74 | -------------------------------------------------------------------------------- /utils/flask_rest_api/example_request.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Perform test request 4 | """ 5 | 6 | import pprint 7 | 8 | import requests 9 | 10 | DETECTION_URL = "http://localhost:5000/v1/object-detection/yolov5s" 11 | IMAGE = "zidane.jpg" 12 | 13 | # Read image 14 | with open(IMAGE, "rb") as f: 15 | image_data = f.read() 16 | 17 | response = requests.post(DETECTION_URL, files={"image": image_data}).json() 18 | 19 | pprint.pprint(response) 20 | -------------------------------------------------------------------------------- /utils/flask_rest_api/restapi.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Run a Flask REST API exposing one or more YOLOv5s models 4 | """ 5 | 6 | import argparse 7 | import io 8 | 9 | import torch 10 | from flask import Flask, request 11 | from PIL import Image 12 | 13 | app = Flask(__name__) 14 | models = {} 15 | 16 | DETECTION_URL = "/v1/object-detection/" 17 | 18 | 19 | @app.route(DETECTION_URL, methods=["POST"]) 20 | def predict(model): 21 | if request.method != "POST": 22 | return 23 | 24 | if request.files.get("image"): 25 | # Method 1 26 | # with request.files["image"] as f: 27 | # im = Image.open(io.BytesIO(f.read())) 28 | 29 | # Method 2 30 | im_file = request.files["image"] 31 | im_bytes = im_file.read() 32 | im = Image.open(io.BytesIO(im_bytes)) 33 | 34 | if model in models: 35 | results = models[model](im, size=640) # reduce size=320 for faster inference 36 | return results.pandas().xyxy[0].to_json(orient="records") 37 | 38 | 39 | if __name__ == "__main__": 40 | parser = argparse.ArgumentParser(description="Flask API exposing YOLOv5 model") 41 | parser.add_argument("--port", default=5000, type=int, help="port number") 42 | parser.add_argument('--model', nargs='+', default=['yolov5s'], help='model(s) to run, i.e. --model yolov5n yolov5s') 43 | opt = parser.parse_args() 44 | 45 | for m in opt.model: 46 | models[m] = torch.hub.load("ultralytics/yolov5", m, force_reload=True, skip_validation=True) 47 | 48 | app.run(host="0.0.0.0", port=opt.port) # debug=True causes Restarting with stat 49 | -------------------------------------------------------------------------------- /utils/google_app_engine/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcr.io/google-appengine/python 2 | 3 | # Create a virtualenv for dependencies. This isolates these packages from 4 | # system-level packages. 5 | # Use -p python3 or -p python3.7 to select python version. Default is version 2. 6 | RUN virtualenv /env -p python3 7 | 8 | # Setting these environment variables are the same as running 9 | # source /env/bin/activate. 10 | ENV VIRTUAL_ENV /env 11 | ENV PATH /env/bin:$PATH 12 | 13 | RUN apt-get update && apt-get install -y python-opencv 14 | 15 | # Copy the application's requirements.txt and run pip to install all 16 | # dependencies into the virtualenv. 17 | ADD requirements.txt /app/requirements.txt 18 | RUN pip install -r /app/requirements.txt 19 | 20 | # Add the application source code. 21 | ADD . /app 22 | 23 | # Run a WSGI server to serve the application. gunicorn must be declared as 24 | # a dependency in requirements.txt. 25 | CMD gunicorn -b :$PORT main:app 26 | -------------------------------------------------------------------------------- /utils/google_app_engine/additional_requirements.txt: -------------------------------------------------------------------------------- 1 | # add these requirements in your app on top of the existing ones 2 | pip==21.1 3 | Flask==1.0.2 4 | gunicorn==19.9.0 5 | -------------------------------------------------------------------------------- /utils/google_app_engine/app.yaml: -------------------------------------------------------------------------------- 1 | runtime: custom 2 | env: flex 3 | 4 | service: yolov5app 5 | 6 | liveness_check: 7 | initial_delay_sec: 600 8 | 9 | manual_scaling: 10 | instances: 1 11 | resources: 12 | cpu: 1 13 | memory_gb: 4 14 | disk_size_gb: 20 15 | -------------------------------------------------------------------------------- /utils/loggers/clearml/README.md: -------------------------------------------------------------------------------- 1 | # ClearML Integration 2 | 3 | Clear|MLClear|ML 4 | 5 | ## About ClearML 6 | 7 | [ClearML](https://cutt.ly/yolov5-tutorial-clearml) is an [open-source](https://github.com/allegroai/clearml) toolbox designed to save you time ⏱️. 8 | 9 | 🔨 Track every YOLOv5 training run in the experiment manager 10 | 11 | 🔧 Version and easily access your custom training data with the integrated ClearML Data Versioning Tool 12 | 13 | 🔦 Remotely train and monitor your YOLOv5 training runs using ClearML Agent 14 | 15 | 🔬 Get the very best mAP using ClearML Hyperparameter Optimization 16 | 17 | 🔭 Turn your newly trained YOLOv5 model into an API with just a few commands using ClearML Serving 18 | 19 |
20 | And so much more. It's up to you how many of these tools you want to use, you can stick to the experiment manager, or chain them all together into an impressive pipeline! 21 |
22 |
23 | 24 | ![ClearML scalars dashboard](https://github.com/thepycoder/clearml_screenshots/raw/main/experiment_manager_with_compare.gif) 25 | 26 | 27 |
28 |
29 | 30 | ## 🦾 Setting Things Up 31 | 32 | To keep track of your experiments and/or data, ClearML needs to communicate to a server. You have 2 options to get one: 33 | 34 | Either sign up for free to the [ClearML Hosted Service](https://cutt.ly/yolov5-tutorial-clearml) or you can set up your own server, see [here](https://clear.ml/docs/latest/docs/deploying_clearml/clearml_server). Even the server is open-source, so even if you're dealing with sensitive data, you should be good to go! 35 | 36 | 1. Install the `clearml` python package: 37 | 38 | ```bash 39 | pip install clearml 40 | ``` 41 | 42 | 1. Connect the ClearML SDK to the server by [creating credentials](https://app.clear.ml/settings/workspace-configuration) (go right top to Settings -> Workspace -> Create new credentials), then execute the command below and follow the instructions: 43 | 44 | ```bash 45 | clearml-init 46 | ``` 47 | 48 | That's it! You're done 😎 49 | 50 |
51 | 52 | ## 🚀 Training YOLOv5 With ClearML 53 | 54 | To enable ClearML experiment tracking, simply install the ClearML pip package. 55 | 56 | ```bash 57 | pip install clearml 58 | ``` 59 | 60 | This will enable integration with the YOLOv5 training script. Every training run from now on, will be captured and stored by the ClearML experiment manager. If you want to change the `project_name` or `task_name`, head over to our custom logger, where you can change it: `utils/loggers/clearml/clearml_utils.py` 61 | 62 | ```bash 63 | python train.py --img 640 --batch 16 --epochs 3 --data coco128.yaml --weights yolov5s.pt --cache 64 | ``` 65 | 66 | This will capture: 67 | - Source code + uncommitted changes 68 | - Installed packages 69 | - (Hyper)parameters 70 | - Model files (use `--save-period n` to save a checkpoint every n epochs) 71 | - Console output 72 | - Scalars (mAP_0.5, mAP_0.5:0.95, precision, recall, losses, learning rates, ...) 73 | - General info such as machine details, runtime, creation date etc. 74 | - All produced plots such as label correlogram and confusion matrix 75 | - Images with bounding boxes per epoch 76 | - Mosaic per epoch 77 | - Validation images per epoch 78 | - ... 79 | 80 | That's a lot right? 🤯 81 | Now, we can visualize all of this information in the ClearML UI to get an overview of our training progress. Add custom columns to the table view (such as e.g. mAP_0.5) so you can easily sort on the best performing model. Or select multiple experiments and directly compare them! 82 | 83 | There even more we can do with all of this information, like hyperparameter optimization and remote execution, so keep reading if you want to see how that works! 84 | 85 |
86 | 87 | ## 🔗 Dataset Version Management 88 | 89 | Versioning your data separately from your code is generally a good idea and makes it easy to aqcuire the latest version too. This repository supports supplying a dataset version ID and it will make sure to get the data if it's not there yet. Next to that, this workflow also saves the used dataset ID as part of the task parameters, so you will always know for sure which data was used in which experiment! 90 | 91 | ![ClearML Dataset Interface](https://github.com/thepycoder/clearml_screenshots/raw/main/clearml_data.gif) 92 | 93 | ### Prepare Your Dataset 94 | 95 | The YOLOv5 repository supports a number of different datasets by using yaml files containing their information. By default datasets are downloaded to the `../datasets` folder in relation to the repository root folder. So if you downloaded the `coco128` dataset using the link in the yaml or with the scripts provided by yolov5, you get this folder structure: 96 | 97 | ``` 98 | .. 99 | |_ yolov5 100 | |_ datasets 101 | |_ coco128 102 | |_ images 103 | |_ labels 104 | |_ LICENSE 105 | |_ README.txt 106 | ``` 107 | But this can be any dataset you wish. Feel free to use your own, as long as you keep to this folder structure. 108 | 109 | Next, ⚠️**copy the corresponding yaml file to the root of the dataset folder**⚠️. This yaml files contains the information ClearML will need to properly use the dataset. You can make this yourself too, of course, just follow the structure of the example yamls. 110 | 111 | Basically we need the following keys: `path`, `train`, `test`, `val`, `nc`, `names`. 112 | 113 | ``` 114 | .. 115 | |_ yolov5 116 | |_ datasets 117 | |_ coco128 118 | |_ images 119 | |_ labels 120 | |_ coco128.yaml # <---- HERE! 121 | |_ LICENSE 122 | |_ README.txt 123 | ``` 124 | 125 | ### Upload Your Dataset 126 | 127 | To get this dataset into ClearML as a versionned dataset, go to the dataset root folder and run the following command: 128 | ```bash 129 | cd coco128 130 | clearml-data sync --project YOLOv5 --name coco128 --folder . 131 | ``` 132 | 133 | The command `clearml-data sync` is actually a shorthand command. You could also run these commands one after the other: 134 | ```bash 135 | # Optionally add --parent if you want to base 136 | # this version on another dataset version, so no duplicate files are uploaded! 137 | clearml-data create --name coco128 --project YOLOv5 138 | clearml-data add --files . 139 | clearml-data close 140 | ``` 141 | 142 | ### Run Training Using A ClearML Dataset 143 | 144 | Now that you have a ClearML dataset, you can very simply use it to train custom YOLOv5 🚀 models! 145 | 146 | ```bash 147 | python train.py --img 640 --batch 16 --epochs 3 --data clearml:// --weights yolov5s.pt --cache 148 | ``` 149 | 150 |
151 | 152 | ## 👀 Hyperparameter Optimization 153 | 154 | Now that we have our experiments and data versioned, it's time to take a look at what we can build on top! 155 | 156 | Using the code information, installed packages and environment details, the experiment itself is now **completely reproducible**. In fact, ClearML allows you to clone an experiment and even change its parameters. We can then just rerun it with these new parameters automatically, this is basically what HPO does! 157 | 158 | To **run hyperparameter optimization locally**, we've included a pre-made script for you. Just make sure a training task has been run at least once, so it is in the ClearML experiment manager, we will essentially clone it and change its hyperparameters. 159 | 160 | You'll need to fill in the ID of this `template task` in the script found at `utils/loggers/clearml/hpo.py` and then just run it :) You can change `task.execute_locally()` to `task.execute()` to put it in a ClearML queue and have a remote agent work on it instead. 161 | 162 | ```bash 163 | # To use optuna, install it first, otherwise you can change the optimizer to just be RandomSearch 164 | pip install optuna 165 | python utils/loggers/clearml/hpo.py 166 | ``` 167 | 168 | ![HPO](https://github.com/thepycoder/clearml_screenshots/raw/main/hpo.png) 169 | 170 | ## 🤯 Remote Execution (advanced) 171 | 172 | Running HPO locally is really handy, but what if we want to run our experiments on a remote machine instead? Maybe you have access to a very powerful GPU machine on-site or you have some budget to use cloud GPUs. 173 | This is where the ClearML Agent comes into play. Check out what the agent can do here: 174 | 175 | - [YouTube video](https://youtu.be/MX3BrXnaULs) 176 | - [Documentation](https://clear.ml/docs/latest/docs/clearml_agent) 177 | 178 | In short: every experiment tracked by the experiment manager contains enough information to reproduce it on a different machine (installed packages, uncommitted changes etc.). So a ClearML agent does just that: it listens to a queue for incoming tasks and when it finds one, it recreates the environment and runs it while still reporting scalars, plots etc. to the experiment manager. 179 | 180 | You can turn any machine (a cloud VM, a local GPU machine, your own laptop ... ) into a ClearML agent by simply running: 181 | ```bash 182 | clearml-agent daemon --queue [--docker] 183 | ``` 184 | 185 | ### Cloning, Editing And Enqueuing 186 | 187 | With our agent running, we can give it some work. Remember from the HPO section that we can clone a task and edit the hyperparameters? We can do that from the interface too! 188 | 189 | 🪄 Clone the experiment by right clicking it 190 | 191 | 🎯 Edit the hyperparameters to what you wish them to be 192 | 193 | ⏳ Enqueue the task to any of the queues by right clicking it 194 | 195 | ![Enqueue a task from the UI](https://github.com/thepycoder/clearml_screenshots/raw/main/enqueue.gif) 196 | 197 | ### Executing A Task Remotely 198 | 199 | Now you can clone a task like we explained above, or simply mark your current script by adding `task.execute_remotely()` and on execution it will be put into a queue, for the agent to start working on! 200 | 201 | To run the YOLOv5 training script remotely, all you have to do is add this line to the training.py script after the clearml logger has been instatiated: 202 | ```python 203 | # ... 204 | # Loggers 205 | data_dict = None 206 | if RANK in {-1, 0}: 207 | loggers = Loggers(save_dir, weights, opt, hyp, LOGGER) # loggers instance 208 | if loggers.clearml: 209 | loggers.clearml.task.execute_remotely(queue='my_queue') # <------ ADD THIS LINE 210 | # Data_dict is either None is user did not choose for ClearML dataset or is filled in by ClearML 211 | data_dict = loggers.clearml.data_dict 212 | # ... 213 | ``` 214 | When running the training script after this change, python will run the script up until that line, after which it will package the code and send it to the queue instead! 215 | 216 | ### Autoscaling workers 217 | 218 | ClearML comes with autoscalers too! This tool will automatically spin up new remote machines in the cloud of your choice (AWS, GCP, Azure) and turn them into ClearML agents for you whenever there are experiments detected in the queue. Once the tasks are processed, the autoscaler will automatically shut down the remote machines and you stop paying! 219 | 220 | Check out the autoscalers getting started video below. 221 | 222 | [![Watch the video](https://img.youtube.com/vi/j4XVMAaUt3E/0.jpg)](https://youtu.be/j4XVMAaUt3E) 223 | -------------------------------------------------------------------------------- /utils/loggers/clearml/__init__.py: -------------------------------------------------------------------------------- 1 | # init -------------------------------------------------------------------------------- /utils/loggers/clearml/clearml_utils.py: -------------------------------------------------------------------------------- 1 | """Main Logger class for ClearML experiment tracking.""" 2 | import glob 3 | import re 4 | from pathlib import Path 5 | 6 | import numpy as np 7 | import yaml 8 | 9 | from utils.plots import Annotator, colors 10 | 11 | try: 12 | import clearml 13 | from clearml import Dataset, Task 14 | assert hasattr(clearml, '__version__') # verify package import not local dir 15 | except (ImportError, AssertionError): 16 | clearml = None 17 | 18 | 19 | def construct_dataset(clearml_info_string): 20 | """Load in a clearml dataset and fill the internal data_dict with its contents. 21 | """ 22 | dataset_id = clearml_info_string.replace('clearml://', '') 23 | dataset = Dataset.get(dataset_id=dataset_id) 24 | dataset_root_path = Path(dataset.get_local_copy()) 25 | 26 | # We'll search for the yaml file definition in the dataset 27 | yaml_filenames = list(glob.glob(str(dataset_root_path / "*.yaml")) + glob.glob(str(dataset_root_path / "*.yml"))) 28 | if len(yaml_filenames) > 1: 29 | raise ValueError('More than one yaml file was found in the dataset root, cannot determine which one contains ' 30 | 'the dataset definition this way.') 31 | elif len(yaml_filenames) == 0: 32 | raise ValueError('No yaml definition found in dataset root path, check that there is a correct yaml file ' 33 | 'inside the dataset root path.') 34 | with open(yaml_filenames[0]) as f: 35 | dataset_definition = yaml.safe_load(f) 36 | 37 | assert set(dataset_definition.keys()).issuperset( 38 | {'train', 'test', 'val', 'nc', 'names'} 39 | ), "The right keys were not found in the yaml file, make sure it at least has the following keys: ('train', 'test', 'val', 'nc', 'names')" 40 | 41 | data_dict = dict() 42 | data_dict['train'] = str( 43 | (dataset_root_path / dataset_definition['train']).resolve()) if dataset_definition['train'] else None 44 | data_dict['test'] = str( 45 | (dataset_root_path / dataset_definition['test']).resolve()) if dataset_definition['test'] else None 46 | data_dict['val'] = str( 47 | (dataset_root_path / dataset_definition['val']).resolve()) if dataset_definition['val'] else None 48 | data_dict['nc'] = dataset_definition['nc'] 49 | data_dict['names'] = dataset_definition['names'] 50 | 51 | return data_dict 52 | 53 | 54 | class ClearmlLogger: 55 | """Log training runs, datasets, models, and predictions to ClearML. 56 | 57 | This logger sends information to ClearML at app.clear.ml or to your own hosted server. By default, 58 | this information includes hyperparameters, system configuration and metrics, model metrics, code information and 59 | basic data metrics and analyses. 60 | 61 | By providing additional command line arguments to train.py, datasets, 62 | models and predictions can also be logged. 63 | """ 64 | 65 | def __init__(self, opt, hyp): 66 | """ 67 | - Initialize ClearML Task, this object will capture the experiment 68 | - Upload dataset version to ClearML Data if opt.upload_dataset is True 69 | 70 | arguments: 71 | opt (namespace) -- Commandline arguments for this run 72 | hyp (dict) -- Hyperparameters for this run 73 | 74 | """ 75 | self.current_epoch = 0 76 | # Keep tracked of amount of logged images to enforce a limit 77 | self.current_epoch_logged_images = set() 78 | # Maximum number of images to log to clearML per epoch 79 | self.max_imgs_to_log_per_epoch = 16 80 | # Get the interval of epochs when bounding box images should be logged 81 | self.bbox_interval = opt.bbox_interval 82 | self.clearml = clearml 83 | self.task = None 84 | self.data_dict = None 85 | if self.clearml: 86 | self.task = Task.init( 87 | project_name='YOLOv5', 88 | task_name='training', 89 | tags=['YOLOv5'], 90 | output_uri=True, 91 | auto_connect_frameworks={'pytorch': False} 92 | # We disconnect pytorch auto-detection, because we added manual model save points in the code 93 | ) 94 | # ClearML's hooks will already grab all general parameters 95 | # Only the hyperparameters coming from the yaml config file 96 | # will have to be added manually! 97 | self.task.connect(hyp, name='Hyperparameters') 98 | 99 | # Get ClearML Dataset Version if requested 100 | if opt.data.startswith('clearml://'): 101 | # data_dict should have the following keys: 102 | # names, nc (number of classes), test, train, val (all three relative paths to ../datasets) 103 | self.data_dict = construct_dataset(opt.data) 104 | # Set data to data_dict because wandb will crash without this information and opt is the best way 105 | # to give it to them 106 | opt.data = self.data_dict 107 | 108 | def log_debug_samples(self, files, title='Debug Samples'): 109 | """ 110 | Log files (images) as debug samples in the ClearML task. 111 | 112 | arguments: 113 | files (List(PosixPath)) a list of file paths in PosixPath format 114 | title (str) A title that groups together images with the same values 115 | """ 116 | for f in files: 117 | if f.exists(): 118 | it = re.search(r'_batch(\d+)', f.name) 119 | iteration = int(it.groups()[0]) if it else 0 120 | self.task.get_logger().report_image(title=title, 121 | series=f.name.replace(it.group(), ''), 122 | local_path=str(f), 123 | iteration=iteration) 124 | 125 | def log_image_with_boxes(self, image_path, boxes, class_names, image, conf_threshold=0.25): 126 | """ 127 | Draw the bounding boxes on a single image and report the result as a ClearML debug sample. 128 | 129 | arguments: 130 | image_path (PosixPath) the path the original image file 131 | boxes (list): list of scaled predictions in the format - [xmin, ymin, xmax, ymax, confidence, class] 132 | class_names (dict): dict containing mapping of class int to class name 133 | image (Tensor): A torch tensor containing the actual image data 134 | """ 135 | if len(self.current_epoch_logged_images) < self.max_imgs_to_log_per_epoch and self.current_epoch >= 0: 136 | # Log every bbox_interval times and deduplicate for any intermittend extra eval runs 137 | if self.current_epoch % self.bbox_interval == 0 and image_path not in self.current_epoch_logged_images: 138 | im = np.ascontiguousarray(np.moveaxis(image.mul(255).clamp(0, 255).byte().cpu().numpy(), 0, 2)) 139 | annotator = Annotator(im=im, pil=True) 140 | for i, (conf, class_nr, box) in enumerate(zip(boxes[:, 4], boxes[:, 5], boxes[:, :4])): 141 | color = colors(i) 142 | 143 | class_name = class_names[int(class_nr)] 144 | confidence_percentage = round(float(conf) * 100, 2) 145 | label = f"{class_name}: {confidence_percentage}%" 146 | 147 | if conf > conf_threshold: 148 | annotator.rectangle(box.cpu().numpy(), outline=color) 149 | annotator.box_label(box.cpu().numpy(), label=label, color=color) 150 | 151 | annotated_image = annotator.result() 152 | self.task.get_logger().report_image(title='Bounding Boxes', 153 | series=image_path.name, 154 | iteration=self.current_epoch, 155 | image=annotated_image) 156 | self.current_epoch_logged_images.add(image_path) 157 | -------------------------------------------------------------------------------- /utils/loggers/clearml/hpo.py: -------------------------------------------------------------------------------- 1 | from clearml import Task 2 | # Connecting ClearML with the current process, 3 | # from here on everything is logged automatically 4 | from clearml.automation import HyperParameterOptimizer, UniformParameterRange 5 | from clearml.automation.optuna import OptimizerOptuna 6 | 7 | task = Task.init(project_name='Hyper-Parameter Optimization', 8 | task_name='YOLOv5', 9 | task_type=Task.TaskTypes.optimizer, 10 | reuse_last_task_id=False) 11 | 12 | # Example use case: 13 | optimizer = HyperParameterOptimizer( 14 | # This is the experiment we want to optimize 15 | base_task_id='', 16 | # here we define the hyper-parameters to optimize 17 | # Notice: The parameter name should exactly match what you see in the UI: / 18 | # For Example, here we see in the base experiment a section Named: "General" 19 | # under it a parameter named "batch_size", this becomes "General/batch_size" 20 | # If you have `argparse` for example, then arguments will appear under the "Args" section, 21 | # and you should instead pass "Args/batch_size" 22 | hyper_parameters=[ 23 | UniformParameterRange('Hyperparameters/lr0', min_value=1e-5, max_value=1e-1), 24 | UniformParameterRange('Hyperparameters/lrf', min_value=0.01, max_value=1.0), 25 | UniformParameterRange('Hyperparameters/momentum', min_value=0.6, max_value=0.98), 26 | UniformParameterRange('Hyperparameters/weight_decay', min_value=0.0, max_value=0.001), 27 | UniformParameterRange('Hyperparameters/warmup_epochs', min_value=0.0, max_value=5.0), 28 | UniformParameterRange('Hyperparameters/warmup_momentum', min_value=0.0, max_value=0.95), 29 | UniformParameterRange('Hyperparameters/warmup_bias_lr', min_value=0.0, max_value=0.2), 30 | UniformParameterRange('Hyperparameters/box', min_value=0.02, max_value=0.2), 31 | UniformParameterRange('Hyperparameters/cls', min_value=0.2, max_value=4.0), 32 | UniformParameterRange('Hyperparameters/cls_pw', min_value=0.5, max_value=2.0), 33 | UniformParameterRange('Hyperparameters/obj', min_value=0.2, max_value=4.0), 34 | UniformParameterRange('Hyperparameters/obj_pw', min_value=0.5, max_value=2.0), 35 | UniformParameterRange('Hyperparameters/iou_t', min_value=0.1, max_value=0.7), 36 | UniformParameterRange('Hyperparameters/anchor_t', min_value=2.0, max_value=8.0), 37 | UniformParameterRange('Hyperparameters/fl_gamma', min_value=0.0, max_value=4.0), 38 | UniformParameterRange('Hyperparameters/hsv_h', min_value=0.0, max_value=0.1), 39 | UniformParameterRange('Hyperparameters/hsv_s', min_value=0.0, max_value=0.9), 40 | UniformParameterRange('Hyperparameters/hsv_v', min_value=0.0, max_value=0.9), 41 | UniformParameterRange('Hyperparameters/degrees', min_value=0.0, max_value=45.0), 42 | UniformParameterRange('Hyperparameters/translate', min_value=0.0, max_value=0.9), 43 | UniformParameterRange('Hyperparameters/scale', min_value=0.0, max_value=0.9), 44 | UniformParameterRange('Hyperparameters/shear', min_value=0.0, max_value=10.0), 45 | UniformParameterRange('Hyperparameters/perspective', min_value=0.0, max_value=0.001), 46 | UniformParameterRange('Hyperparameters/flipud', min_value=0.0, max_value=1.0), 47 | UniformParameterRange('Hyperparameters/fliplr', min_value=0.0, max_value=1.0), 48 | UniformParameterRange('Hyperparameters/mosaic', min_value=0.0, max_value=1.0), 49 | UniformParameterRange('Hyperparameters/mixup', min_value=0.0, max_value=1.0), 50 | UniformParameterRange('Hyperparameters/copy_paste', min_value=0.0, max_value=1.0)], 51 | # this is the objective metric we want to maximize/minimize 52 | objective_metric_title='metrics', 53 | objective_metric_series='mAP_0.5', 54 | # now we decide if we want to maximize it or minimize it (accuracy we maximize) 55 | objective_metric_sign='max', 56 | # let us limit the number of concurrent experiments, 57 | # this in turn will make sure we do dont bombard the scheduler with experiments. 58 | # if we have an auto-scaler connected, this, by proxy, will limit the number of machine 59 | max_number_of_concurrent_tasks=1, 60 | # this is the optimizer class (actually doing the optimization) 61 | # Currently, we can choose from GridSearch, RandomSearch or OptimizerBOHB (Bayesian optimization Hyper-Band) 62 | optimizer_class=OptimizerOptuna, 63 | # If specified only the top K performing Tasks will be kept, the others will be automatically archived 64 | save_top_k_tasks_only=5, # 5, 65 | compute_time_limit=None, 66 | total_max_jobs=20, 67 | min_iteration_per_job=None, 68 | max_iteration_per_job=None, 69 | ) 70 | 71 | # report every 10 seconds, this is way too often, but we are testing here 72 | optimizer.set_report_period(10) 73 | # You can also use the line below instead to run all the optimizer tasks locally, without using queues or agent 74 | # an_optimizer.start_locally(job_complete_callback=job_complete_callback) 75 | # set the time limit for the optimization process (2 hours) 76 | optimizer.set_time_limit(in_minutes=120.0) 77 | # Start the optimization process in the local environment 78 | optimizer.start_locally() 79 | # wait until process is done (notice we are controlling the optimization process in the background) 80 | optimizer.wait() 81 | # make sure background optimization stopped 82 | optimizer.stop() 83 | 84 | print('We are done, good bye') 85 | -------------------------------------------------------------------------------- /utils/loggers/wandb/README.md: -------------------------------------------------------------------------------- 1 | 📚 This guide explains how to use **Weights & Biases** (W&B) with YOLOv5 🚀. UPDATED 29 September 2021. 2 | 3 | - [About Weights & Biases](#about-weights-&-biases) 4 | - [First-Time Setup](#first-time-setup) 5 | - [Viewing runs](#viewing-runs) 6 | - [Disabling wandb](#disabling-wandb) 7 | - [Advanced Usage: Dataset Versioning and Evaluation](#advanced-usage) 8 | - [Reports: Share your work with the world!](#reports) 9 | 10 | ## About Weights & Biases 11 | 12 | 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. 13 | 14 | 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: 15 | 16 | - [Debug](https://wandb.ai/wandb/getting-started/reports/Visualize-Debug-Machine-Learning-Models--VmlldzoyNzY5MDk#Free-2) model performance in real time 17 | - [GPU usage](https://wandb.ai/wandb/getting-started/reports/Visualize-Debug-Machine-Learning-Models--VmlldzoyNzY5MDk#System-4) visualized automatically 18 | - [Custom charts](https://wandb.ai/wandb/customizable-charts/reports/Powerful-Custom-Charts-To-Debug-Model-Peformance--VmlldzoyNzY4ODI) for powerful, extensible visualization 19 | - [Share insights](https://wandb.ai/wandb/getting-started/reports/Visualize-Debug-Machine-Learning-Models--VmlldzoyNzY5MDk#Share-8) interactively with collaborators 20 | - [Optimize hyperparameters](https://docs.wandb.com/sweeps) efficiently 21 | - [Track](https://docs.wandb.com/artifacts) datasets, pipelines, and production models 22 | 23 | ## First-Time Setup 24 | 25 |
26 | Toggle Details 27 | 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. 28 | 29 | W&B will create a cloud **project** (default is 'YOLOv5') 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: 30 | 31 | ```shell 32 | $ python train.py --project ... --name ... 33 | ``` 34 | 35 | YOLOv5 notebook example: Open In Colab Open In Kaggle 36 | Screen Shot 2021-09-29 at 10 23 13 PM 37 | 38 |
39 | 40 | ## Viewing Runs 41 | 42 |
43 | Toggle Details 44 | 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: 45 | 46 | - Training & Validation losses 47 | - Metrics: Precision, Recall, mAP@0.5, mAP@0.5:0.95 48 | - Learning Rate over time 49 | - A bounding box debugging panel, showing the training progress over time 50 | - GPU: Type, **GPU Utilization**, power, temperature, **CUDA memory usage** 51 | - System: Disk I/0, CPU utilization, RAM memory usage 52 | - Your trained model as W&B Artifact 53 | - Environment: OS and Python types, Git repository and state, **training command** 54 | 55 |

Weights & Biases dashboard

56 |
57 | 58 | ## Disabling wandb 59 | 60 | - training after running `wandb disabled` inside that directory creates no wandb run 61 | ![Screenshot (84)](https://user-images.githubusercontent.com/15766192/143441777-c780bdd7-7cb4-4404-9559-b4316030a985.png) 62 | 63 | - To enable wandb again, run `wandb online` 64 | ![Screenshot (85)](https://user-images.githubusercontent.com/15766192/143441866-7191b2cb-22f0-4e0f-ae64-2dc47dc13078.png) 65 | 66 | ## Advanced Usage 67 | 68 | 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. 69 | 70 |
71 |

1: Train and Log Evaluation simultaneousy

72 | This is an extension of the previous section, but it'll also training after uploading the dataset. This also evaluation Table 73 | Evaluation table compares your predictions and ground truths across the validation set for each epoch. It uses the references to the already uploaded datasets, 74 | so no images will be uploaded from your system more than once. 75 |
76 | Usage 77 | Code $ python train.py --upload_data val 78 | 79 | ![Screenshot from 2021-11-21 17-40-06](https://user-images.githubusercontent.com/15766192/142761183-c1696d8c-3f38-45ab-991a-bb0dfd98ae7d.png) 80 | 81 |
82 | 83 |

2. Visualize and Version Datasets

84 | 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. 85 |
86 | Usage 87 | Code $ python utils/logger/wandb/log_dataset.py --project ... --name ... --data .. 88 | 89 | ![Screenshot (64)](https://user-images.githubusercontent.com/15766192/128486078-d8433890-98a3-4d12-8986-b6c0e3fc64b9.png) 90 | 91 |
92 | 93 |

3: Train using dataset artifact

94 | 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 95 | can be used to train a model directly from the dataset artifact. This also logs evaluation 96 |
97 | Usage 98 | Code $ python train.py --data {data}_wandb.yaml 99 | 100 | ![Screenshot (72)](https://user-images.githubusercontent.com/15766192/128979739-4cf63aeb-a76f-483f-8861-1c0100b938a5.png) 101 | 102 |
103 | 104 |

4: Save model checkpoints as artifacts

105 | To enable saving and versioning checkpoints of your experiment, pass `--save_period n` with the base cammand, where `n` represents checkpoint interval. 106 | You can also log both the dataset and model checkpoints simultaneously. If not passed, only the final model will be logged 107 | 108 |
109 | Usage 110 | Code $ python train.py --save_period 1 111 | 112 | ![Screenshot (68)](https://user-images.githubusercontent.com/15766192/128726138-ec6c1f60-639d-437d-b4ee-3acd9de47ef3.png) 113 | 114 |
115 | 116 |
117 | 118 |

5: Resume runs from checkpoint artifacts.

119 | 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. 120 | 121 |
122 | Usage 123 | Code $ python train.py --resume wandb-artifact://{run_path} 124 | 125 | ![Screenshot (70)](https://user-images.githubusercontent.com/15766192/128728988-4e84b355-6c87-41ae-a591-14aecf45343e.png) 126 | 127 |
128 | 129 |

6: Resume runs from dataset artifact & checkpoint artifacts.

130 | Local dataset or model checkpoints are not required. This can be used to resume runs directly on a different device 131 | 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 132 | train from _wandb.yaml file and set --save_period 133 | 134 |
135 | Usage 136 | Code $ python train.py --resume wandb-artifact://{run_path} 137 | 138 | ![Screenshot (70)](https://user-images.githubusercontent.com/15766192/128728988-4e84b355-6c87-41ae-a591-14aecf45343e.png) 139 | 140 |
141 | 142 | 143 | 144 |

Reports

145 | 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 four YOLOv5 models ([link](https://wandb.ai/glenn-jocher/yolov5_tutorial/reports/YOLOv5-COCO128-Tutorial-Results--VmlldzozMDI5OTY)). 146 | 147 | Weights & Biases Reports 148 | 149 | ## Environments 150 | 151 | YOLOv5 may be run in any of the following up-to-date verified environments (with all dependencies including [CUDA](https://developer.nvidia.com/cuda)/[CUDNN](https://developer.nvidia.com/cudnn), [Python](https://www.python.org/) and [PyTorch](https://pytorch.org/) preinstalled): 152 | 153 | - **Google Colab and Kaggle** notebooks with free GPU: Open In Colab Open In Kaggle 154 | - **Google Cloud** Deep Learning VM. See [GCP Quickstart Guide](https://github.com/ultralytics/yolov5/wiki/GCP-Quickstart) 155 | - **Amazon** Deep Learning AMI. See [AWS Quickstart Guide](https://github.com/ultralytics/yolov5/wiki/AWS-Quickstart) 156 | - **Docker Image**. See [Docker Quickstart Guide](https://github.com/ultralytics/yolov5/wiki/Docker-Quickstart) Docker Pulls 157 | 158 | ## Status 159 | 160 | ![CI CPU testing](https://github.com/ultralytics/yolov5/workflows/CI%20CPU%20testing/badge.svg) 161 | 162 | If this badge is green, all [YOLOv5 GitHub Actions](https://github.com/ultralytics/yolov5/actions) Continuous Integration (CI) tests are currently passing. CI tests verify correct operation of YOLOv5 training ([train.py](https://github.com/ultralytics/yolov5/blob/master/train.py)), validation ([val.py](https://github.com/ultralytics/yolov5/blob/master/val.py)), inference ([detect.py](https://github.com/ultralytics/yolov5/blob/master/detect.py)) and export ([export.py](https://github.com/ultralytics/yolov5/blob/master/export.py)) on macOS, Windows, and Ubuntu every 24 hours and on every commit. 163 | -------------------------------------------------------------------------------- /utils/loggers/wandb/__init__.py: -------------------------------------------------------------------------------- 1 | # init -------------------------------------------------------------------------------- /utils/loggers/wandb/log_dataset.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | from wandb_utils import WandbLogger 4 | 5 | from utils.general import LOGGER 6 | 7 | WANDB_ARTIFACT_PREFIX = 'wandb-artifact://' 8 | 9 | 10 | def create_dataset_artifact(opt): 11 | logger = WandbLogger(opt, None, job_type='Dataset Creation') # TODO: return value unused 12 | if not logger.wandb: 13 | LOGGER.info("install wandb using `pip install wandb` to log the dataset") 14 | 15 | 16 | if __name__ == '__main__': 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument('--data', type=str, default='data/coco128.yaml', help='data.yaml path') 19 | parser.add_argument('--single-cls', action='store_true', help='train as single-class dataset') 20 | parser.add_argument('--project', type=str, default='YOLOv5', help='name of W&B Project') 21 | parser.add_argument('--entity', default=None, help='W&B entity') 22 | parser.add_argument('--name', type=str, default='log dataset', help='name of W&B run') 23 | 24 | opt = parser.parse_args() 25 | opt.resume = False # Explicitly disallow resume check for dataset upload job 26 | 27 | create_dataset_artifact(opt) 28 | -------------------------------------------------------------------------------- /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] # YOLOv5 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. Copy because train() modifies parameters which confused wandb. 20 | hyp_dict = vars(wandb.config).get("_items").copy() 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 | -------------------------------------------------------------------------------- /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: 4.0 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 | -------------------------------------------------------------------------------- /utils/loss.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 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/segment/__init__.py: -------------------------------------------------------------------------------- 1 | # init -------------------------------------------------------------------------------- /utils/segment/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/utils/segment/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /utils/segment/__pycache__/general.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/utils/segment/__pycache__/general.cpython-310.pyc -------------------------------------------------------------------------------- /utils/segment/__pycache__/plots.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhammadMoinFaisal/yolov7-segmentation-with-DeepSORT-Tracking/989fb3a9655ec5e263da760edc3c033045329fa3/utils/segment/__pycache__/plots.cpython-310.pyc -------------------------------------------------------------------------------- /utils/segment/augmentations.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 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 ..augmentations import box_candidates 13 | from ..general import resample_segments, segment2box 14 | 15 | 16 | def mixup(im, labels, segments, im2, labels2, segments2): 17 | # Applies MixUp augmentation https://arxiv.org/pdf/1710.09412.pdf 18 | r = np.random.beta(32.0, 32.0) # mixup ratio, alpha=beta=32.0 19 | im = (im * r + im2 * (1 - r)).astype(np.uint8) 20 | labels = np.concatenate((labels, labels2), 0) 21 | segments = np.concatenate((segments, segments2), 0) 22 | return im, labels, segments 23 | 24 | 25 | def random_perspective(im, 26 | targets=(), 27 | segments=(), 28 | degrees=10, 29 | translate=.1, 30 | scale=.1, 31 | shear=10, 32 | perspective=0.0, 33 | border=(0, 0)): 34 | # torchvision.transforms.RandomAffine(degrees=(-10, 10), translate=(.1, .1), scale=(.9, 1.1), shear=(-10, 10)) 35 | # targets = [cls, xyxy] 36 | 37 | height = im.shape[0] + border[0] * 2 # shape(h,w,c) 38 | width = im.shape[1] + border[1] * 2 39 | 40 | # Center 41 | C = np.eye(3) 42 | C[0, 2] = -im.shape[1] / 2 # x translation (pixels) 43 | C[1, 2] = -im.shape[0] / 2 # y translation (pixels) 44 | 45 | # Perspective 46 | P = np.eye(3) 47 | P[2, 0] = random.uniform(-perspective, perspective) # x perspective (about y) 48 | P[2, 1] = random.uniform(-perspective, perspective) # y perspective (about x) 49 | 50 | # Rotation and Scale 51 | R = np.eye(3) 52 | a = random.uniform(-degrees, degrees) 53 | # a += random.choice([-180, -90, 0, 90]) # add 90deg rotations to small rotations 54 | s = random.uniform(1 - scale, 1 + scale) 55 | # s = 2 ** random.uniform(-scale, scale) 56 | R[:2] = cv2.getRotationMatrix2D(angle=a, center=(0, 0), scale=s) 57 | 58 | # Shear 59 | S = np.eye(3) 60 | S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # x shear (deg) 61 | S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # y shear (deg) 62 | 63 | # Translation 64 | T = np.eye(3) 65 | T[0, 2] = (random.uniform(0.5 - translate, 0.5 + translate) * width) # x translation (pixels) 66 | T[1, 2] = (random.uniform(0.5 - translate, 0.5 + translate) * height) # y translation (pixels) 67 | 68 | # Combined rotation matrix 69 | M = T @ S @ R @ P @ C # order of operations (right to left) is IMPORTANT 70 | if (border[0] != 0) or (border[1] != 0) or (M != np.eye(3)).any(): # image changed 71 | if perspective: 72 | im = cv2.warpPerspective(im, M, dsize=(width, height), borderValue=(114, 114, 114)) 73 | else: # affine 74 | im = cv2.warpAffine(im, M[:2], dsize=(width, height), borderValue=(114, 114, 114)) 75 | 76 | # Visualize 77 | # import matplotlib.pyplot as plt 78 | # ax = plt.subplots(1, 2, figsize=(12, 6))[1].ravel() 79 | # ax[0].imshow(im[:, :, ::-1]) # base 80 | # ax[1].imshow(im2[:, :, ::-1]) # warped 81 | 82 | # Transform label coordinates 83 | n = len(targets) 84 | new_segments = [] 85 | if n: 86 | new = np.zeros((n, 4)) 87 | segments = resample_segments(segments) # upsample 88 | for i, segment in enumerate(segments): 89 | xy = np.ones((len(segment), 3)) 90 | xy[:, :2] = segment 91 | xy = xy @ M.T # transform 92 | xy = (xy[:, :2] / xy[:, 2:3] if perspective else xy[:, :2]) # perspective rescale or affine 93 | 94 | # clip 95 | new[i] = segment2box(xy, width, height) 96 | new_segments.append(xy) 97 | 98 | # filter candidates 99 | i = box_candidates(box1=targets[:, 1:5].T * s, box2=new.T, area_thr=0.01) 100 | targets = targets[i] 101 | targets[:, 1:5] = new[i] 102 | new_segments = np.array(new_segments)[i] 103 | 104 | return im, targets, new_segments 105 | -------------------------------------------------------------------------------- /utils/segment/general.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import torch 3 | import torch.nn.functional as F 4 | 5 | 6 | def crop(masks, boxes): 7 | """ 8 | "Crop" predicted masks by zeroing out everything not in the predicted bbox. 9 | Vectorized by Chong (thanks Chong). 10 | 11 | Args: 12 | - masks should be a size [h, w, n] tensor of masks 13 | - boxes should be a size [n, 4] tensor of bbox coords in relative point form 14 | """ 15 | 16 | n, h, w = masks.shape 17 | x1, y1, x2, y2 = torch.chunk(boxes[:, :, None], 4, 1) # x1 shape(1,1,n) 18 | r = torch.arange(w, device=masks.device, dtype=x1.dtype)[None, None, :] # rows shape(1,w,1) 19 | c = torch.arange(h, device=masks.device, dtype=x1.dtype)[None, :, None] # cols shape(h,1,1) 20 | 21 | return masks * ((r >= x1) * (r < x2) * (c >= y1) * (c < y2)) 22 | 23 | 24 | def process_mask_upsample(protos, masks_in, bboxes, shape): 25 | """ 26 | Crop after upsample. 27 | proto_out: [mask_dim, mask_h, mask_w] 28 | out_masks: [n, mask_dim], n is number of masks after nms 29 | bboxes: [n, 4], n is number of masks after nms 30 | shape:input_image_size, (h, w) 31 | 32 | return: h, w, n 33 | """ 34 | 35 | c, mh, mw = protos.shape # CHW 36 | masks = (masks_in @ protos.float().view(c, -1)).sigmoid().view(-1, mh, mw) 37 | masks = F.interpolate(masks[None], shape, mode='bilinear', align_corners=False)[0] # CHW 38 | masks = crop(masks, bboxes) # CHW 39 | return masks.gt_(0.5) 40 | 41 | 42 | def process_mask(protos, masks_in, bboxes, shape, upsample=False): 43 | """ 44 | Crop before upsample. 45 | proto_out: [mask_dim, mask_h, mask_w] 46 | out_masks: [n, mask_dim], n is number of masks after nms 47 | bboxes: [n, 4], n is number of masks after nms 48 | shape:input_image_size, (h, w) 49 | 50 | return: h, w, n 51 | """ 52 | 53 | c, mh, mw = protos.shape # CHW 54 | ih, iw = shape 55 | masks = (masks_in @ protos.float().view(c, -1)).sigmoid().view(-1, mh, mw) # CHW 56 | 57 | downsampled_bboxes = bboxes.clone() 58 | downsampled_bboxes[:, 0] *= mw / iw 59 | downsampled_bboxes[:, 2] *= mw / iw 60 | downsampled_bboxes[:, 3] *= mh / ih 61 | downsampled_bboxes[:, 1] *= mh / ih 62 | 63 | masks = crop(masks, downsampled_bboxes) # CHW 64 | if upsample: 65 | masks = F.interpolate(masks[None], shape, mode='bilinear', align_corners=False)[0] # CHW 66 | return masks.gt_(0.5) 67 | 68 | 69 | def scale_masks(img1_shape, masks, img0_shape, ratio_pad=None): 70 | """ 71 | img1_shape: model input shape, [h, w] 72 | img0_shape: origin pic shape, [h, w, 3] 73 | masks: [h, w, num] 74 | resize for the most time 75 | """ 76 | # Rescale coords (xyxy) from img1_shape to img0_shape 77 | if ratio_pad is None: # calculate from img0_shape 78 | gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / new 79 | pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding 80 | else: 81 | gain = ratio_pad[0][0] 82 | pad = ratio_pad[1] 83 | tl_pad = int(pad[1]), int(pad[0]) # y, x 84 | br_pad = int(img1_shape[0] - pad[1]), int(img1_shape[1] - pad[0]) 85 | 86 | if len(masks.shape) < 2: 87 | raise ValueError(f'"len of masks shape" should be 2 or 3, but got {len(masks.shape)}') 88 | # masks_h, masks_w, n 89 | masks = masks[tl_pad[0]:br_pad[0], tl_pad[1]:br_pad[1]] 90 | # 1, n, masks_h, masks_w 91 | # masks = masks.permute(2, 0, 1).contiguous()[None, :] 92 | # # shape = [1, n, masks_h, masks_w] after F.interpolate, so take first element 93 | # masks = F.interpolate(masks, img0_shape[:2], mode='bilinear', align_corners=False)[0] 94 | # masks = masks.permute(1, 2, 0).contiguous() 95 | # masks_h, masks_w, n 96 | masks = cv2.resize(masks, (img0_shape[1], img0_shape[0])) 97 | 98 | # keepdim 99 | if len(masks.shape) == 2: 100 | masks = masks[:, :, None] 101 | 102 | return masks 103 | 104 | 105 | def mask_iou(mask1, mask2, eps=1e-7): 106 | """ 107 | mask1: [N, n] m1 means number of predicted objects 108 | mask2: [M, n] m2 means number of gt objects 109 | Note: n means image_w x image_h 110 | 111 | return: masks iou, [N, M] 112 | """ 113 | intersection = torch.matmul(mask1, mask2.t()).clamp(0) 114 | union = (mask1.sum(1)[:, None] + mask2.sum(1)[None]) - intersection # (area1 + area2) - intersection 115 | return intersection / (union + eps) 116 | 117 | 118 | def masks_iou(mask1, mask2, eps=1e-7): 119 | """ 120 | mask1: [N, n] m1 means number of predicted objects 121 | mask2: [N, n] m2 means number of gt objects 122 | Note: n means image_w x image_h 123 | 124 | return: masks iou, (N, ) 125 | """ 126 | intersection = (mask1 * mask2).sum(1).clamp(0) # (N, ) 127 | union = (mask1.sum(1) + mask2.sum(1))[None] - intersection # (area1 + area2) - intersection 128 | return intersection / (union + eps) 129 | -------------------------------------------------------------------------------- /utils/segment/loss.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | 5 | from ..general import xywh2xyxy 6 | from ..loss import FocalLoss, smooth_BCE 7 | from ..metrics import bbox_iou 8 | from ..torch_utils import de_parallel 9 | from .general import crop 10 | 11 | 12 | class ComputeLoss: 13 | # Compute losses 14 | def __init__(self, model, autobalance=False, overlap=False): 15 | self.sort_obj_iou = False 16 | self.overlap = overlap 17 | device = next(model.parameters()).device # get model device 18 | h = model.hyp # hyperparameters 19 | self.device = device 20 | 21 | # Define criteria 22 | BCEcls = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['cls_pw']], device=device)) 23 | BCEobj = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['obj_pw']], device=device)) 24 | 25 | # Class label smoothing https://arxiv.org/pdf/1902.04103.pdf eqn 3 26 | self.cp, self.cn = smooth_BCE(eps=h.get('label_smoothing', 0.0)) # positive, negative BCE targets 27 | 28 | # Focal loss 29 | g = h['fl_gamma'] # focal loss gamma 30 | if g > 0: 31 | BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g) 32 | 33 | m = de_parallel(model).model[-1] # Detect() module 34 | self.balance = {3: [4.0, 1.0, 0.4]}.get(m.nl, [4.0, 1.0, 0.25, 0.06, 0.02]) # P3-P7 35 | self.ssi = list(m.stride).index(16) if autobalance else 0 # stride 16 index 36 | self.BCEcls, self.BCEobj, self.gr, self.hyp, self.autobalance = BCEcls, BCEobj, 1.0, h, autobalance 37 | self.na = m.na # number of anchors 38 | self.nc = m.nc # number of classes 39 | self.nl = m.nl # number of layers 40 | self.nm = m.nm # number of masks 41 | self.anchors = m.anchors 42 | self.device = device 43 | 44 | def __call__(self, preds, targets, masks): # predictions, targets, model 45 | p, proto = preds 46 | bs, nm, mask_h, mask_w = proto.shape # batch size, number of masks, mask height, mask width 47 | lcls = torch.zeros(1, device=self.device) 48 | lbox = torch.zeros(1, device=self.device) 49 | lobj = torch.zeros(1, device=self.device) 50 | lseg = torch.zeros(1, device=self.device) 51 | tcls, tbox, indices, anchors, tidxs, xywhn = self.build_targets(p, targets) # targets 52 | 53 | # Losses 54 | for i, pi in enumerate(p): # layer index, layer predictions 55 | b, a, gj, gi = indices[i] # image, anchor, gridy, gridx 56 | tobj = torch.zeros(pi.shape[:4], dtype=pi.dtype, device=self.device) # target obj 57 | 58 | n = b.shape[0] # number of targets 59 | if n: 60 | pxy, pwh, _, pcls, pmask = pi[b, a, gj, gi].split((2, 2, 1, self.nc, nm), 1) # subset of predictions 61 | 62 | # Box regression 63 | pxy = pxy.sigmoid() * 2 - 0.5 64 | pwh = (pwh.sigmoid() * 2) ** 2 * anchors[i] 65 | pbox = torch.cat((pxy, pwh), 1) # predicted box 66 | iou = bbox_iou(pbox, tbox[i], CIoU=True).squeeze() # iou(prediction, target) 67 | lbox += (1.0 - iou).mean() # iou loss 68 | 69 | # Objectness 70 | iou = iou.detach().clamp(0).type(tobj.dtype) 71 | if self.sort_obj_iou: 72 | j = iou.argsort() 73 | b, a, gj, gi, iou = b[j], a[j], gj[j], gi[j], iou[j] 74 | if self.gr < 1: 75 | iou = (1.0 - self.gr) + self.gr * iou 76 | tobj[b, a, gj, gi] = iou # iou ratio 77 | 78 | # Classification 79 | if self.nc > 1: # cls loss (only if multiple classes) 80 | t = torch.full_like(pcls, self.cn, device=self.device) # targets 81 | t[range(n), tcls[i]] = self.cp 82 | lcls += self.BCEcls(pcls, t) # BCE 83 | 84 | # Mask regression 85 | if tuple(masks.shape[-2:]) != (mask_h, mask_w): # downsample 86 | masks = F.interpolate(masks[None], (mask_h, mask_w), mode="bilinear", align_corners=False)[0] 87 | marea = xywhn[i][:, 2:].prod(1) # mask width, height normalized 88 | mxyxy = xywh2xyxy(xywhn[i] * torch.tensor([mask_w, mask_h, mask_w, mask_h], device=self.device)) 89 | for bi in b.unique(): 90 | j = b == bi # matching index 91 | if self.overlap: 92 | mask_gti = torch.where(masks[bi][None] == tidxs[i][j].view(-1, 1, 1), 1.0, 0.0) 93 | else: 94 | mask_gti = masks[tidxs[i]][j] 95 | lseg += self.single_mask_loss(mask_gti, pmask[j], proto[bi], mxyxy[j], marea[j]) 96 | 97 | obji = self.BCEobj(pi[..., 4], tobj) 98 | lobj += obji * self.balance[i] # obj loss 99 | if self.autobalance: 100 | self.balance[i] = self.balance[i] * 0.9999 + 0.0001 / obji.detach().item() 101 | 102 | if self.autobalance: 103 | self.balance = [x / self.balance[self.ssi] for x in self.balance] 104 | lbox *= self.hyp["box"] 105 | lobj *= self.hyp["obj"] 106 | lcls *= self.hyp["cls"] 107 | lseg *= self.hyp["box"] / bs 108 | 109 | loss = lbox + lobj + lcls + lseg 110 | return loss * bs, torch.cat((lbox, lseg, lobj, lcls)).detach() 111 | 112 | def single_mask_loss(self, gt_mask, pred, proto, xyxy, area): 113 | # Mask loss for one image 114 | pred_mask = (pred @ proto.view(self.nm, -1)).view(-1, *proto.shape[1:]) # (n,32) @ (32,80,80) -> (n,80,80) 115 | loss = F.binary_cross_entropy_with_logits(pred_mask, gt_mask, reduction="none") 116 | return (crop(loss, xyxy).mean(dim=(1, 2)) / area).mean() 117 | 118 | def build_targets(self, p, targets): 119 | # Build targets for compute_loss(), input targets(image,class,x,y,w,h) 120 | na, nt = self.na, targets.shape[0] # number of anchors, targets 121 | tcls, tbox, indices, anch, tidxs, xywhn = [], [], [], [], [], [] 122 | gain = torch.ones(8, device=self.device) # normalized to gridspace gain 123 | ai = torch.arange(na, device=self.device).float().view(na, 1).repeat(1, nt) # same as .repeat_interleave(nt) 124 | if self.overlap: 125 | batch = p[0].shape[0] 126 | ti = [] 127 | for i in range(batch): 128 | num = (targets[:, 0] == i).sum() # find number of targets of each image 129 | ti.append(torch.arange(num, device=self.device).float().view(1, num).repeat(na, 1) + 1) # (na, num) 130 | ti = torch.cat(ti, 1) # (na, nt) 131 | else: 132 | ti = torch.arange(nt, device=self.device).float().view(1, nt).repeat(na, 1) 133 | targets = torch.cat((targets.repeat(na, 1, 1), ai[..., None], ti[..., None]), 2) # append anchor indices 134 | 135 | g = 0.5 # bias 136 | off = torch.tensor( 137 | [ 138 | [0, 0], 139 | [1, 0], 140 | [0, 1], 141 | [-1, 0], 142 | [0, -1], # j,k,l,m 143 | # [1, 1], [1, -1], [-1, 1], [-1, -1], # jk,jm,lk,lm 144 | ], 145 | device=self.device).float() * g # offsets 146 | 147 | for i in range(self.nl): 148 | anchors, shape = self.anchors[i], p[i].shape 149 | gain[2:6] = torch.tensor(shape)[[3, 2, 3, 2]] # xyxy gain 150 | 151 | # Match targets to anchors 152 | t = targets * gain # shape(3,n,7) 153 | if nt: 154 | # Matches 155 | r = t[..., 4:6] / anchors[:, None] # wh ratio 156 | j = torch.max(r, 1 / r).max(2)[0] < self.hyp['anchor_t'] # compare 157 | # j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t'] # iou(3,n)=wh_iou(anchors(3,2), gwh(n,2)) 158 | t = t[j] # filter 159 | 160 | # Offsets 161 | gxy = t[:, 2:4] # grid xy 162 | gxi = gain[[2, 3]] - gxy # inverse 163 | j, k = ((gxy % 1 < g) & (gxy > 1)).T 164 | l, m = ((gxi % 1 < g) & (gxi > 1)).T 165 | j = torch.stack((torch.ones_like(j), j, k, l, m)) 166 | t = t.repeat((5, 1, 1))[j] 167 | offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j] 168 | else: 169 | t = targets[0] 170 | offsets = 0 171 | 172 | # Define 173 | bc, gxy, gwh, at = t.chunk(4, 1) # (image, class), grid xy, grid wh, anchors 174 | (a, tidx), (b, c) = at.long().T, bc.long().T # anchors, image, class 175 | gij = (gxy - offsets).long() 176 | gi, gj = gij.T # grid indices 177 | 178 | # Append 179 | indices.append((b, a, gj.clamp_(0, shape[2] - 1), gi.clamp_(0, shape[3] - 1))) # image, anchor, grid 180 | tbox.append(torch.cat((gxy - gij, gwh), 1)) # box 181 | anch.append(anchors[a]) # anchors 182 | tcls.append(c) # class 183 | tidxs.append(tidx) 184 | xywhn.append(torch.cat((gxy, gwh), 1) / gain[2:6]) # xywh normalized 185 | 186 | return tcls, tbox, indices, anch, tidxs, xywhn 187 | -------------------------------------------------------------------------------- /utils/segment/metrics.py: -------------------------------------------------------------------------------- 1 | # YOLOv5 🚀 by Ultralytics, GPL-3.0 license 2 | """ 3 | Model validation metrics 4 | """ 5 | 6 | import numpy as np 7 | 8 | from ..metrics import ap_per_class 9 | 10 | 11 | def fitness(x): 12 | # Model fitness as a weighted combination of metrics 13 | w = [0.0, 0.0, 0.1, 0.9, 0.0, 0.0, 0.1, 0.9] 14 | return (x[:, :8] * w).sum(1) 15 | 16 | 17 | def ap_per_class_box_and_mask( 18 | tp_m, 19 | tp_b, 20 | conf, 21 | pred_cls, 22 | target_cls, 23 | plot=False, 24 | save_dir=".", 25 | names=(), 26 | ): 27 | """ 28 | Args: 29 | tp_b: tp of boxes. 30 | tp_m: tp of masks. 31 | other arguments see `func: ap_per_class`. 32 | """ 33 | results_boxes = ap_per_class(tp_b, 34 | conf, 35 | pred_cls, 36 | target_cls, 37 | plot=plot, 38 | save_dir=save_dir, 39 | names=names, 40 | prefix="Box")[2:] 41 | results_masks = ap_per_class(tp_m, 42 | conf, 43 | pred_cls, 44 | target_cls, 45 | plot=plot, 46 | save_dir=save_dir, 47 | names=names, 48 | prefix="Mask")[2:] 49 | 50 | results = { 51 | "boxes": { 52 | "p": results_boxes[0], 53 | "r": results_boxes[1], 54 | "ap": results_boxes[3], 55 | "f1": results_boxes[2], 56 | "ap_class": results_boxes[4]}, 57 | "masks": { 58 | "p": results_masks[0], 59 | "r": results_masks[1], 60 | "ap": results_masks[3], 61 | "f1": results_masks[2], 62 | "ap_class": results_masks[4]}} 63 | return results 64 | 65 | 66 | class Metric: 67 | 68 | def __init__(self) -> None: 69 | self.p = [] # (nc, ) 70 | self.r = [] # (nc, ) 71 | self.f1 = [] # (nc, ) 72 | self.all_ap = [] # (nc, 10) 73 | self.ap_class_index = [] # (nc, ) 74 | 75 | @property 76 | def ap50(self): 77 | """AP@0.5 of all classes. 78 | Return: 79 | (nc, ) or []. 80 | """ 81 | return self.all_ap[:, 0] if len(self.all_ap) else [] 82 | 83 | @property 84 | def ap(self): 85 | """AP@0.5:0.95 86 | Return: 87 | (nc, ) or []. 88 | """ 89 | return self.all_ap.mean(1) if len(self.all_ap) else [] 90 | 91 | @property 92 | def mp(self): 93 | """mean precision of all classes. 94 | Return: 95 | float. 96 | """ 97 | return self.p.mean() if len(self.p) else 0.0 98 | 99 | @property 100 | def mr(self): 101 | """mean recall of all classes. 102 | Return: 103 | float. 104 | """ 105 | return self.r.mean() if len(self.r) else 0.0 106 | 107 | @property 108 | def map50(self): 109 | """Mean AP@0.5 of all classes. 110 | Return: 111 | float. 112 | """ 113 | return self.all_ap[:, 0].mean() if len(self.all_ap) else 0.0 114 | 115 | @property 116 | def map(self): 117 | """Mean AP@0.5:0.95 of all classes. 118 | Return: 119 | float. 120 | """ 121 | return self.all_ap.mean() if len(self.all_ap) else 0.0 122 | 123 | def mean_results(self): 124 | """Mean of results, return mp, mr, map50, map""" 125 | return (self.mp, self.mr, self.map50, self.map) 126 | 127 | def class_result(self, i): 128 | """class-aware result, return p[i], r[i], ap50[i], ap[i]""" 129 | return (self.p[i], self.r[i], self.ap50[i], self.ap[i]) 130 | 131 | def get_maps(self, nc): 132 | maps = np.zeros(nc) + self.map 133 | for i, c in enumerate(self.ap_class_index): 134 | maps[c] = self.ap[i] 135 | return maps 136 | 137 | def update(self, results): 138 | """ 139 | Args: 140 | results: tuple(p, r, ap, f1, ap_class) 141 | """ 142 | p, r, all_ap, f1, ap_class_index = results 143 | self.p = p 144 | self.r = r 145 | self.all_ap = all_ap 146 | self.f1 = f1 147 | self.ap_class_index = ap_class_index 148 | 149 | 150 | class Metrics: 151 | """Metric for boxes and masks.""" 152 | 153 | def __init__(self) -> None: 154 | self.metric_box = Metric() 155 | self.metric_mask = Metric() 156 | 157 | def update(self, results): 158 | """ 159 | Args: 160 | results: Dict{'boxes': Dict{}, 'masks': Dict{}} 161 | """ 162 | self.metric_box.update(list(results["boxes"].values())) 163 | self.metric_mask.update(list(results["masks"].values())) 164 | 165 | def mean_results(self): 166 | return self.metric_box.mean_results() + self.metric_mask.mean_results() 167 | 168 | def class_result(self, i): 169 | return self.metric_box.class_result(i) + self.metric_mask.class_result(i) 170 | 171 | def get_maps(self, nc): 172 | return self.metric_box.get_maps(nc) + self.metric_mask.get_maps(nc) 173 | 174 | @property 175 | def ap_class_index(self): 176 | # boxes and masks have the same ap_class_index 177 | return self.metric_box.ap_class_index 178 | 179 | 180 | KEYS = [ 181 | "train/box_loss", 182 | "train/seg_loss", # train loss 183 | "train/obj_loss", 184 | "train/cls_loss", 185 | "metrics/precision(B)", 186 | "metrics/recall(B)", 187 | "metrics/mAP_0.5(B)", 188 | "metrics/mAP_0.5:0.95(B)", # metrics 189 | "metrics/precision(M)", 190 | "metrics/recall(M)", 191 | "metrics/mAP_0.5(M)", 192 | "metrics/mAP_0.5:0.95(M)", # metrics 193 | "val/box_loss", 194 | "val/seg_loss", # val loss 195 | "val/obj_loss", 196 | "val/cls_loss", 197 | "x/lr0", 198 | "x/lr1", 199 | "x/lr2",] 200 | 201 | BEST_KEYS = [ 202 | "best/epoch", 203 | "best/precision(B)", 204 | "best/recall(B)", 205 | "best/mAP_0.5(B)", 206 | "best/mAP_0.5:0.95(B)", 207 | "best/precision(M)", 208 | "best/recall(M)", 209 | "best/mAP_0.5(M)", 210 | "best/mAP_0.5:0.95(M)",] 211 | -------------------------------------------------------------------------------- /utils/segment/plots.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import math 3 | from pathlib import Path 4 | 5 | import cv2 6 | import matplotlib.pyplot as plt 7 | import numpy as np 8 | import pandas as pd 9 | import torch 10 | 11 | from .. import threaded 12 | from ..general import xywh2xyxy 13 | from ..plots import Annotator, colors 14 | 15 | 16 | def plot_masks(img, masks, colors, alpha=0.5): 17 | """ 18 | Args: 19 | img (tensor): img is in cuda, shape: [3, h, w], range: [0, 1] 20 | masks (tensor): predicted masks on cuda, shape: [n, h, w] 21 | colors (List[List[Int]]): colors for predicted masks, [[r, g, b] * n] 22 | Return: 23 | ndarray: img after draw masks, shape: [h, w, 3] 24 | 25 | transform colors and send img_gpu to cpu for the most time. 26 | """ 27 | img_gpu = img.clone() 28 | num_masks = len(masks) 29 | if num_masks == 0: 30 | return img.permute(1, 2, 0).contiguous().cpu().numpy() * 255 31 | 32 | # [n, 1, 1, 3] 33 | # faster this way to transform colors 34 | colors = torch.tensor(colors, device=img.device).float() / 255.0 35 | colors = colors[:, None, None, :] 36 | # [n, h, w, 1] 37 | masks = masks[:, :, :, None] 38 | masks_color = masks.repeat(1, 1, 1, 3) * colors * alpha 39 | inv_alph_masks = masks * (-alpha) + 1 40 | masks_color_summand = masks_color[0] 41 | if num_masks > 1: 42 | inv_alph_cumul = inv_alph_masks[:(num_masks - 1)].cumprod(dim=0) 43 | masks_color_cumul = masks_color[1:] * inv_alph_cumul 44 | masks_color_summand += masks_color_cumul.sum(dim=0) 45 | 46 | # print(inv_alph_masks.prod(dim=0).shape) # [h, w, 1] 47 | img_gpu = img_gpu.flip(dims=[0]) # filp channel for opencv 48 | img_gpu = img_gpu.permute(1, 2, 0).contiguous() 49 | # [h, w, 3] 50 | img_gpu = img_gpu * inv_alph_masks.prod(dim=0) + masks_color_summand 51 | return (img_gpu * 255).byte().cpu().numpy() 52 | 53 | 54 | @threaded 55 | def plot_images_and_masks(images, targets, masks, paths=None, fname='images.jpg', names=None): 56 | # Plot image grid with labels 57 | if isinstance(images, torch.Tensor): 58 | images = images.cpu().float().numpy() 59 | if isinstance(targets, torch.Tensor): 60 | targets = targets.cpu().numpy() 61 | if isinstance(masks, torch.Tensor): 62 | masks = masks.cpu().numpy().astype(int) 63 | 64 | max_size = 1920 # max image size 65 | max_subplots = 16 # max image subplots, i.e. 4x4 66 | bs, _, h, w = images.shape # batch size, _, height, width 67 | bs = min(bs, max_subplots) # limit plot images 68 | ns = np.ceil(bs ** 0.5) # number of subplots (square) 69 | if np.max(images[0]) <= 1: 70 | images *= 255 # de-normalise (optional) 71 | 72 | # Build Image 73 | mosaic = np.full((int(ns * h), int(ns * w), 3), 255, dtype=np.uint8) # init 74 | for i, im in enumerate(images): 75 | if i == max_subplots: # if last batch has fewer images than we expect 76 | break 77 | x, y = int(w * (i // ns)), int(h * (i % ns)) # block origin 78 | im = im.transpose(1, 2, 0) 79 | mosaic[y:y + h, x:x + w, :] = im 80 | 81 | # Resize (optional) 82 | scale = max_size / ns / max(h, w) 83 | if scale < 1: 84 | h = math.ceil(scale * h) 85 | w = math.ceil(scale * w) 86 | mosaic = cv2.resize(mosaic, tuple(int(x * ns) for x in (w, h))) 87 | 88 | # Annotate 89 | fs = int((h + w) * ns * 0.01) # font size 90 | annotator = Annotator(mosaic, line_width=round(fs / 10), font_size=fs, pil=True, example=names) 91 | for i in range(i + 1): 92 | x, y = int(w * (i // ns)), int(h * (i % ns)) # block origin 93 | annotator.rectangle([x, y, x + w, y + h], None, (255, 255, 255), width=2) # borders 94 | if paths: 95 | annotator.text((x + 5, y + 5 + h), text=Path(paths[i]).name[:40], txt_color=(220, 220, 220)) # filenames 96 | if len(targets) > 0: 97 | idx = targets[:, 0] == i 98 | ti = targets[idx] # image targets 99 | 100 | boxes = xywh2xyxy(ti[:, 2:6]).T 101 | classes = ti[:, 1].astype('int') 102 | labels = ti.shape[1] == 6 # labels if no conf column 103 | conf = None if labels else ti[:, 6] # check for confidence presence (label vs pred) 104 | 105 | if boxes.shape[1]: 106 | if boxes.max() <= 1.01: # if normalized with tolerance 0.01 107 | boxes[[0, 2]] *= w # scale to pixels 108 | boxes[[1, 3]] *= h 109 | elif scale < 1: # absolute coords need scale if image scales 110 | boxes *= scale 111 | boxes[[0, 2]] += x 112 | boxes[[1, 3]] += y 113 | for j, box in enumerate(boxes.T.tolist()): 114 | cls = classes[j] 115 | color = colors(cls) 116 | cls = names[cls] if names else cls 117 | if labels or conf[j] > 0.25: # 0.25 conf thresh 118 | label = f'{cls}' if labels else f'{cls} {conf[j]:.1f}' 119 | annotator.box_label(box, label, color=color) 120 | 121 | # Plot masks 122 | if len(masks): 123 | if masks.max() > 1.0: # mean that masks are overlap 124 | image_masks = masks[[i]] # (1, 640, 640) 125 | nl = len(ti) 126 | index = np.arange(nl).reshape(nl, 1, 1) + 1 127 | image_masks = np.repeat(image_masks, nl, axis=0) 128 | image_masks = np.where(image_masks == index, 1.0, 0.0) 129 | else: 130 | image_masks = masks[idx] 131 | 132 | im = np.asarray(annotator.im).copy() 133 | for j, box in enumerate(boxes.T.tolist()): 134 | if labels or conf[j] > 0.25: # 0.25 conf thresh 135 | color = colors(classes[j]) 136 | mh, mw = image_masks[j].shape 137 | if mh != h or mw != w: 138 | mask = image_masks[j].astype(np.uint8) 139 | mask = cv2.resize(mask, (w, h)) 140 | mask = mask.astype(np.bool) 141 | else: 142 | mask = image_masks[j].astype(np.bool) 143 | with contextlib.suppress(Exception): 144 | im[y:y + h, x:x + w, :][mask] = im[y:y + h, x:x + w, :][mask] * 0.4 + np.array(color) * 0.6 145 | annotator.fromarray(im) 146 | annotator.im.save(fname) # save 147 | 148 | 149 | def plot_results_with_masks(file="path/to/results.csv", dir="", best=True): 150 | # Plot training results.csv. Usage: from utils.plots import *; plot_results('path/to/results.csv') 151 | save_dir = Path(file).parent if file else Path(dir) 152 | fig, ax = plt.subplots(2, 8, figsize=(18, 6), tight_layout=True) 153 | ax = ax.ravel() 154 | files = list(save_dir.glob("results*.csv")) 155 | assert len(files), f"No results.csv files found in {save_dir.resolve()}, nothing to plot." 156 | for f in files: 157 | try: 158 | data = pd.read_csv(f) 159 | index = np.argmax( 160 | 0.9 * data.values[:, 8] + 0.1 * data.values[:, 7] + 0.9 * data.values[:, 12] + 161 | 0.1 * data.values[:, 11],) 162 | s = [x.strip() for x in data.columns] 163 | x = data.values[:, 0] 164 | for i, j in enumerate([1, 2, 3, 4, 5, 6, 9, 10, 13, 14, 15, 16, 7, 8, 11, 12]): 165 | y = data.values[:, j] 166 | # y[y == 0] = np.nan # don't show zero values 167 | ax[i].plot(x, y, marker=".", label=f.stem, linewidth=2, markersize=2) 168 | if best: 169 | # best 170 | ax[i].scatter(index, y[index], color="r", label=f"best:{index}", marker="*", linewidth=3) 171 | ax[i].set_title(s[j] + f"\n{round(y[index], 5)}") 172 | else: 173 | # last 174 | ax[i].scatter(x[-1], y[-1], color="r", label="last", marker="*", linewidth=3) 175 | ax[i].set_title(s[j] + f"\n{round(y[-1], 5)}") 176 | # if j in [8, 9, 10]: # share train and val loss y axes 177 | # ax[i].get_shared_y_axes().join(ax[i], ax[i - 5]) 178 | except Exception as e: 179 | print(f"Warning: Plotting error for {f}: {e}") 180 | ax[1].legend() 181 | fig.savefig(save_dir / "results.png", dpi=200) 182 | plt.close() 183 | --------------------------------------------------------------------------------