1.0
This commit is contained in:
commit
431d2a1b4b
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,55 @@
|
||||||
|
# Global Wheat 2020 dataset http://www.global-wheat.com/
|
||||||
|
# Train command: python train.py --data GlobalWheat2020.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /datasets/GlobalWheat2020
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
|
||||||
|
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
|
||||||
|
train: # 3422 images
|
||||||
|
- ../datasets/GlobalWheat2020/images/arvalis_1
|
||||||
|
- ../datasets/GlobalWheat2020/images/arvalis_2
|
||||||
|
- ../datasets/GlobalWheat2020/images/arvalis_3
|
||||||
|
- ../datasets/GlobalWheat2020/images/ethz_1
|
||||||
|
- ../datasets/GlobalWheat2020/images/rres_1
|
||||||
|
- ../datasets/GlobalWheat2020/images/inrae_1
|
||||||
|
- ../datasets/GlobalWheat2020/images/usask_1
|
||||||
|
|
||||||
|
val: # 748 images (WARNING: train set contains ethz_1)
|
||||||
|
- ../datasets/GlobalWheat2020/images/ethz_1
|
||||||
|
|
||||||
|
test: # 1276 images
|
||||||
|
- ../datasets/GlobalWheat2020/images/utokyo_1
|
||||||
|
- ../datasets/GlobalWheat2020/images/utokyo_2
|
||||||
|
- ../datasets/GlobalWheat2020/images/nau_1
|
||||||
|
- ../datasets/GlobalWheat2020/images/uq_1
|
||||||
|
|
||||||
|
# number of classes
|
||||||
|
nc: 1
|
||||||
|
|
||||||
|
# class names
|
||||||
|
names: [ 'wheat_head' ]
|
||||||
|
|
||||||
|
|
||||||
|
# download command/URL (optional) --------------------------------------------------------------------------------------
|
||||||
|
download: |
|
||||||
|
from utils.general import download, Path
|
||||||
|
|
||||||
|
# Download
|
||||||
|
dir = Path('../datasets/GlobalWheat2020') # dataset directory
|
||||||
|
urls = ['https://zenodo.org/record/4298502/files/global-wheat-codalab-official.zip',
|
||||||
|
'https://github.com/ultralytics/yolov5/releases/download/v1.0/GlobalWheat2020_labels.zip']
|
||||||
|
download(urls, dir=dir)
|
||||||
|
|
||||||
|
# Make Directories
|
||||||
|
for p in 'annotations', 'images', 'labels':
|
||||||
|
(dir / p).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Move
|
||||||
|
for p in 'arvalis_1', 'arvalis_2', 'arvalis_3', 'ethz_1', 'rres_1', 'inrae_1', 'usask_1', \
|
||||||
|
'utokyo_1', 'utokyo_2', 'nau_1', 'uq_1':
|
||||||
|
(dir / p).rename(dir / 'images' / p) # move to /images
|
||||||
|
f = (dir / p).with_suffix('.json') # json file
|
||||||
|
if f.exists():
|
||||||
|
f.rename((dir / 'annotations' / p).with_suffix('.json')) # move to /annotations
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
# VisDrone2019-DET dataset https://github.com/VisDrone/VisDrone-Dataset
|
||||||
|
# Train command: python train.py --data VisDrone.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /VisDrone
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
|
||||||
|
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
|
||||||
|
train: ../VisDrone/VisDrone2019-DET-train/images # 6471 images
|
||||||
|
val: ../VisDrone/VisDrone2019-DET-val/images # 548 images
|
||||||
|
test: ../VisDrone/VisDrone2019-DET-test-dev/images # 1610 images
|
||||||
|
|
||||||
|
# number of classes
|
||||||
|
nc: 10
|
||||||
|
|
||||||
|
# class names
|
||||||
|
names: [ 'pedestrian', 'people', 'bicycle', 'car', 'van', 'truck', 'tricycle', 'awning-tricycle', 'bus', 'motor' ]
|
||||||
|
|
||||||
|
|
||||||
|
# download command/URL (optional) --------------------------------------------------------------------------------------
|
||||||
|
download: |
|
||||||
|
from utils.general import download, os, Path
|
||||||
|
|
||||||
|
def visdrone2yolo(dir):
|
||||||
|
from PIL import Image
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
def convert_box(size, box):
|
||||||
|
# Convert VisDrone box to YOLO xywh box
|
||||||
|
dw = 1. / size[0]
|
||||||
|
dh = 1. / size[1]
|
||||||
|
return (box[0] + box[2] / 2) * dw, (box[1] + box[3] / 2) * dh, box[2] * dw, box[3] * dh
|
||||||
|
|
||||||
|
(dir / 'labels').mkdir(parents=True, exist_ok=True) # make labels directory
|
||||||
|
pbar = tqdm((dir / 'annotations').glob('*.txt'), desc=f'Converting {dir}')
|
||||||
|
for f in pbar:
|
||||||
|
img_size = Image.open((dir / 'images' / f.name).with_suffix('.jpg')).size
|
||||||
|
lines = []
|
||||||
|
with open(f, 'r') as file: # read annotation.txt
|
||||||
|
for row in [x.split(',') for x in file.read().strip().splitlines()]:
|
||||||
|
if row[4] == '0': # VisDrone 'ignored regions' class 0
|
||||||
|
continue
|
||||||
|
cls = int(row[5]) - 1
|
||||||
|
box = convert_box(img_size, tuple(map(int, row[:4])))
|
||||||
|
lines.append(f"{cls} {' '.join(f'{x:.6f}' for x in box)}\n")
|
||||||
|
with open(str(f).replace(os.sep + 'annotations' + os.sep, os.sep + 'labels' + os.sep), 'w') as fl:
|
||||||
|
fl.writelines(lines) # write label.txt
|
||||||
|
|
||||||
|
|
||||||
|
# Download
|
||||||
|
dir = Path('../VisDrone') # dataset directory
|
||||||
|
urls = ['https://github.com/ultralytics/yolov5/releases/download/v1.0/VisDrone2019-DET-train.zip',
|
||||||
|
'https://github.com/ultralytics/yolov5/releases/download/v1.0/VisDrone2019-DET-val.zip',
|
||||||
|
'https://github.com/ultralytics/yolov5/releases/download/v1.0/VisDrone2019-DET-test-dev.zip',
|
||||||
|
'https://github.com/ultralytics/yolov5/releases/download/v1.0/VisDrone2019-DET-test-challenge.zip']
|
||||||
|
download(urls, dir=dir)
|
||||||
|
|
||||||
|
# Convert
|
||||||
|
for d in 'VisDrone2019-DET-train', 'VisDrone2019-DET-val', 'VisDrone2019-DET-test-dev':
|
||||||
|
visdrone2yolo(dir / d) # convert VisDrone annotations to YOLO labels
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Argoverse-HD dataset (ring-front-center camera) http://www.cs.cmu.edu/~mengtial/proj/streaming/
|
||||||
|
# Train command: python train.py --data argoverse_hd.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /argoverse
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
|
||||||
|
# download command/URL (optional)
|
||||||
|
download: bash data/scripts/get_argoverse_hd.sh
|
||||||
|
|
||||||
|
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
|
||||||
|
train: ../argoverse/Argoverse-1.1/images/train/ # 39384 images
|
||||||
|
val: ../argoverse/Argoverse-1.1/images/val/ # 15062 iamges
|
||||||
|
test: ../argoverse/Argoverse-1.1/images/test/ # Submit to: https://eval.ai/web/challenges/challenge-page/800/overview
|
||||||
|
|
||||||
|
# number of classes
|
||||||
|
nc: 8
|
||||||
|
|
||||||
|
# class names
|
||||||
|
names: [ 'person', 'bicycle', 'car', 'motorcycle', 'bus', 'truck', 'traffic_light', 'stop_sign' ]
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
# COCO 2017 dataset http://cocodataset.org
|
||||||
|
# Train command: python train.py --data coco.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /coco
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
|
||||||
|
# download command/URL (optional)
|
||||||
|
download: bash data/scripts/get_coco.sh
|
||||||
|
|
||||||
|
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
|
||||||
|
train: ../coco/train2017.txt # 118287 images
|
||||||
|
val: ../coco/val2017.txt # 5000 images
|
||||||
|
test: ../coco/test-dev2017.txt # 20288 of 40670 images, submit to https://competitions.codalab.org/competitions/20794
|
||||||
|
|
||||||
|
# number of classes
|
||||||
|
nc: 80
|
||||||
|
|
||||||
|
# class names
|
||||||
|
names: [ 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',
|
||||||
|
'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',
|
||||||
|
'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',
|
||||||
|
'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard',
|
||||||
|
'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
|
||||||
|
'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
|
||||||
|
'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone',
|
||||||
|
'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear',
|
||||||
|
'hair drier', 'toothbrush' ]
|
||||||
|
|
||||||
|
# Print classes
|
||||||
|
# with open('data/coco.yaml') as f:
|
||||||
|
# d = yaml.safe_load(f) # dict
|
||||||
|
# for i, x in enumerate(d['names']):
|
||||||
|
# print(i, x)
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
# COCO 2017 dataset http://cocodataset.org - first 128 training images
|
||||||
|
# Train command: python train.py --data coco128.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /coco128
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
|
||||||
|
# download command/URL (optional)
|
||||||
|
download: https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128.zip
|
||||||
|
|
||||||
|
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
|
||||||
|
train: ../coco128/images/train2017/ # 128 images
|
||||||
|
val: ../coco128/images/train2017/ # 128 images
|
||||||
|
|
||||||
|
# number of classes
|
||||||
|
nc: 80
|
||||||
|
|
||||||
|
# class names
|
||||||
|
names: [ 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',
|
||||||
|
'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',
|
||||||
|
'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',
|
||||||
|
'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard',
|
||||||
|
'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
|
||||||
|
'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
|
||||||
|
'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone',
|
||||||
|
'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear',
|
||||||
|
'hair drier', 'toothbrush' ]
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Global Wheat 2020 dataset http://www.global-wheat.com/
|
||||||
|
# Train command: python train.py --data GlobalWheat2020.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /datasets/GlobalWheat2020
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
|
||||||
|
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
|
||||||
|
train: # 3422 images
|
||||||
|
- ../../data/exitPP/train/images/
|
||||||
|
|
||||||
|
val: # 748 images (WARNING: train set contains ethz_1)
|
||||||
|
- ../../data/exitPP/val/images/
|
||||||
|
|
||||||
|
test: # 1276 images
|
||||||
|
- ../../data/exitPP/val/images/
|
||||||
|
|
||||||
|
|
||||||
|
# number of classes
|
||||||
|
nc: 5
|
||||||
|
|
||||||
|
# class names
|
||||||
|
names: [ 'ex','dEx','qV','wF','oth']
|
||||||
|
|
||||||
|
|
||||||
|
# download command/URL (optional) --------------------------------------------------------------------------------------
|
||||||
|
# download: |
|
||||||
|
# from utils.general import download, Path
|
||||||
|
|
||||||
|
# # Download
|
||||||
|
# dir = Path('../datasets/GlobalWheat2020') # dataset directory
|
||||||
|
# urls = ['https://zenodo.org/record/4298502/files/global-wheat-codalab-official.zip',
|
||||||
|
# 'https://github.com/ultralytics/yolov5/releases/download/v1.0/GlobalWheat2020_labels.zip']
|
||||||
|
# download(urls, dir=dir)
|
||||||
|
|
||||||
|
# # Make Directories
|
||||||
|
# for p in 'annotations', 'images', 'labels':
|
||||||
|
# (dir / p).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# # Move
|
||||||
|
# for p in 'arvalis_1', 'arvalis_2', 'arvalis_3', 'ethz_1', 'rres_1', 'inrae_1', 'usask_1', \
|
||||||
|
# 'utokyo_1', 'utokyo_2', 'nau_1', 'uq_1':
|
||||||
|
# (dir / p).rename(dir / 'images' / p) # move to /images
|
||||||
|
# f = (dir / p).with_suffix('.json') # json file
|
||||||
|
# if f.exists():
|
||||||
|
# f.rename((dir / 'annotations' / p).with_suffix('.json')) # move to /annotations
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Global Wheat 2020 dataset http://www.global-wheat.com/
|
||||||
|
# Train command: python train.py --data GlobalWheat2020.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /datasets/GlobalWheat2020
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
|
||||||
|
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
|
||||||
|
train: # 3422 images
|
||||||
|
- ../../data/exitPPS/train/images/
|
||||||
|
|
||||||
|
val: # 748 images (WARNING: train set contains ethz_1)
|
||||||
|
- ../../data/exitPPS/val/images/
|
||||||
|
|
||||||
|
test: # 1276 images
|
||||||
|
- ../../data/exitPPS/val/images/
|
||||||
|
|
||||||
|
|
||||||
|
# number of classes
|
||||||
|
nc: 9
|
||||||
|
|
||||||
|
# class names
|
||||||
|
names: [ 'ex','dEx','qV','wF','oth','bdg','rsh','fam','st']
|
||||||
|
|
||||||
|
|
||||||
|
# download command/URL (optional) --------------------------------------------------------------------------------------
|
||||||
|
# download: |
|
||||||
|
# from utils.general import download, Path
|
||||||
|
|
||||||
|
# # Download
|
||||||
|
# dir = Path('../datasets/GlobalWheat2020') # dataset directory
|
||||||
|
# urls = ['https://zenodo.org/record/4298502/files/global-wheat-codalab-official.zip',
|
||||||
|
# 'https://github.com/ultralytics/yolov5/releases/download/v1.0/GlobalWheat2020_labels.zip']
|
||||||
|
# download(urls, dir=dir)
|
||||||
|
|
||||||
|
# # Make Directories
|
||||||
|
# for p in 'annotations', 'images', 'labels':
|
||||||
|
# (dir / p).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# # Move
|
||||||
|
# for p in 'arvalis_1', 'arvalis_2', 'arvalis_3', 'ethz_1', 'rres_1', 'inrae_1', 'usask_1', \
|
||||||
|
# 'utokyo_1', 'utokyo_2', 'nau_1', 'uq_1':
|
||||||
|
# (dir / p).rename(dir / 'images' / p) # move to /images
|
||||||
|
# f = (dir / p).with_suffix('.json') # json file
|
||||||
|
# if f.exists():
|
||||||
|
# f.rename((dir / 'annotations' / p).with_suffix('.json')) # move to /annotations
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Global Wheat 2020 dataset http://www.global-wheat.com/
|
||||||
|
# Train command: python train.py --data GlobalWheat2020.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /datasets/GlobalWheat2020
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
|
||||||
|
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
|
||||||
|
train: # 3422 images
|
||||||
|
- ../../data/exitPlus/train/images/
|
||||||
|
|
||||||
|
val: # 748 images (WARNING: train set contains ethz_1)
|
||||||
|
- ../../data/exitPlus/val/images/
|
||||||
|
|
||||||
|
test: # 1276 images
|
||||||
|
- ../../data/exitPlus/val/images/
|
||||||
|
|
||||||
|
|
||||||
|
# number of classes
|
||||||
|
nc: 4
|
||||||
|
|
||||||
|
# class names
|
||||||
|
names: [ 'exit','dirtyExit','quaticVegetation','waterFloater']
|
||||||
|
|
||||||
|
|
||||||
|
# download command/URL (optional) --------------------------------------------------------------------------------------
|
||||||
|
# download: |
|
||||||
|
# from utils.general import download, Path
|
||||||
|
|
||||||
|
# # Download
|
||||||
|
# dir = Path('../datasets/GlobalWheat2020') # dataset directory
|
||||||
|
# urls = ['https://zenodo.org/record/4298502/files/global-wheat-codalab-official.zip',
|
||||||
|
# 'https://github.com/ultralytics/yolov5/releases/download/v1.0/GlobalWheat2020_labels.zip']
|
||||||
|
# download(urls, dir=dir)
|
||||||
|
|
||||||
|
# # Make Directories
|
||||||
|
# for p in 'annotations', 'images', 'labels':
|
||||||
|
# (dir / p).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# # Move
|
||||||
|
# for p in 'arvalis_1', 'arvalis_2', 'arvalis_3', 'ethz_1', 'rres_1', 'inrae_1', 'usask_1', \
|
||||||
|
# 'utokyo_1', 'utokyo_2', 'nau_1', 'uq_1':
|
||||||
|
# (dir / p).rename(dir / 'images' / p) # move to /images
|
||||||
|
# f = (dir / p).with_suffix('.json') # json file
|
||||||
|
# if f.exists():
|
||||||
|
# f.rename((dir / 'annotations' / p).with_suffix('.json')) # move to /annotations
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Global Wheat 2020 dataset http://www.global-wheat.com/
|
||||||
|
# Train command: python train.py --data GlobalWheat2020.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /datasets/GlobalWheat2020
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
|
||||||
|
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
|
||||||
|
train: # 3422 images
|
||||||
|
data/train.txt
|
||||||
|
val: # 748 images (WARNING: train set contains ethz_1)
|
||||||
|
data/val.txt
|
||||||
|
test: # 1276 images
|
||||||
|
data/val.txt
|
||||||
|
|
||||||
|
# number of classes
|
||||||
|
nc: 9
|
||||||
|
|
||||||
|
# class names
|
||||||
|
names: [ 'ex','dEx','qV','wF','oth','bdg','rsh','fam','st']
|
||||||
|
|
||||||
|
|
||||||
|
# download command/URL (optional) --------------------------------------------------------------------------------------
|
||||||
|
# download: |
|
||||||
|
# from utils.general import download, Path
|
||||||
|
|
||||||
|
# # Download
|
||||||
|
# dir = Path('../datasets/GlobalWheat2020') # dataset directory
|
||||||
|
# urls = ['https://zenodo.org/record/4298502/files/global-wheat-codalab-official.zip',
|
||||||
|
# 'https://github.com/ultralytics/yolov5/releases/download/v1.0/GlobalWheat2020_labels.zip']
|
||||||
|
# download(urls, dir=dir)
|
||||||
|
|
||||||
|
# # Make Directories
|
||||||
|
# for p in 'annotations', 'images', 'labels':
|
||||||
|
# (dir / p).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# # Move
|
||||||
|
# for p in 'arvalis_1', 'arvalis_2', 'arvalis_3', 'ethz_1', 'rres_1', 'inrae_1', 'usask_1', \
|
||||||
|
# 'utokyo_1', 'utokyo_2', 'nau_1', 'uq_1':
|
||||||
|
# (dir / p).rename(dir / 'images' / p) # move to /images
|
||||||
|
# f = (dir / p).with_suffix('.json') # json file
|
||||||
|
# if f.exists():
|
||||||
|
# f.rename((dir / 'annotations' / p).with_suffix('.json')) # move to /annotations
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Hyperparameters for VOC finetuning
|
||||||
|
# python train.py --batch 64 --weights yolov5m.pt --data voc.yaml --img 512 --epochs 50
|
||||||
|
# See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials
|
||||||
|
|
||||||
|
|
||||||
|
# Hyperparameter Evolution Results
|
||||||
|
# Generations: 306
|
||||||
|
# P R mAP.5 mAP.5:.95 box obj cls
|
||||||
|
# Metrics: 0.6 0.936 0.896 0.684 0.0115 0.00805 0.00146
|
||||||
|
|
||||||
|
lr0: 0.0032
|
||||||
|
lrf: 0.12
|
||||||
|
momentum: 0.843
|
||||||
|
weight_decay: 0.00036
|
||||||
|
warmup_epochs: 2.0
|
||||||
|
warmup_momentum: 0.5
|
||||||
|
warmup_bias_lr: 0.05
|
||||||
|
box: 0.0296
|
||||||
|
cls: 0.243
|
||||||
|
cls_pw: 0.631
|
||||||
|
obj: 0.301
|
||||||
|
obj_pw: 0.911
|
||||||
|
iou_t: 0.2
|
||||||
|
anchor_t: 2.91
|
||||||
|
# anchors: 3.63
|
||||||
|
fl_gamma: 0.0
|
||||||
|
hsv_h: 0.0138
|
||||||
|
hsv_s: 0.664
|
||||||
|
hsv_v: 0.464
|
||||||
|
degrees: 0.373
|
||||||
|
translate: 0.245
|
||||||
|
scale: 0.898
|
||||||
|
shear: 0.602
|
||||||
|
perspective: 0.0
|
||||||
|
flipud: 0.00856
|
||||||
|
fliplr: 0.5
|
||||||
|
mosaic: 1.0
|
||||||
|
mixup: 0.243
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
lr0: 0.00258
|
||||||
|
lrf: 0.17
|
||||||
|
momentum: 0.779
|
||||||
|
weight_decay: 0.00058
|
||||||
|
warmup_epochs: 1.33
|
||||||
|
warmup_momentum: 0.86
|
||||||
|
warmup_bias_lr: 0.0711
|
||||||
|
box: 0.0539
|
||||||
|
cls: 0.299
|
||||||
|
cls_pw: 0.825
|
||||||
|
obj: 0.632
|
||||||
|
obj_pw: 1.0
|
||||||
|
iou_t: 0.2
|
||||||
|
anchor_t: 3.44
|
||||||
|
anchors: 3.2
|
||||||
|
fl_gamma: 0.0
|
||||||
|
hsv_h: 0.0188
|
||||||
|
hsv_s: 0.704
|
||||||
|
hsv_v: 0.36
|
||||||
|
degrees: 0.0
|
||||||
|
translate: 0.0902
|
||||||
|
scale: 0.491
|
||||||
|
shear: 0.0
|
||||||
|
perspective: 0.0
|
||||||
|
flipud: 0.0
|
||||||
|
fliplr: 0.5
|
||||||
|
mosaic: 1.0
|
||||||
|
mixup: 0.0
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Hyperparameters for COCO training from scratch
|
||||||
|
# python train.py --batch 40 --cfg yolov5m.yaml --weights '' --data coco.yaml --img 640 --epochs 300
|
||||||
|
# See tutorials for hyperparameter evolution https://github.com/ultralytics/yolov5#tutorials
|
||||||
|
|
||||||
|
|
||||||
|
lr0: 0.01 # initial learning rate (SGD=1E-2, Adam=1E-3)
|
||||||
|
lrf: 0.2 # final OneCycleLR learning rate (lr0 * lrf)
|
||||||
|
momentum: 0.937 # SGD momentum/Adam beta1
|
||||||
|
weight_decay: 0.0005 # optimizer weight decay 5e-4
|
||||||
|
warmup_epochs: 3.0 # warmup epochs (fractions ok)
|
||||||
|
warmup_momentum: 0.8 # warmup initial momentum
|
||||||
|
warmup_bias_lr: 0.1 # warmup initial bias lr
|
||||||
|
box: 0.05 # box loss gain
|
||||||
|
cls: 0.5 # cls loss gain
|
||||||
|
cls_pw: 1.0 # cls BCELoss positive_weight
|
||||||
|
obj: 1.0 # obj loss gain (scale with pixels)
|
||||||
|
obj_pw: 1.0 # obj BCELoss positive_weight
|
||||||
|
iou_t: 0.20 # IoU training threshold
|
||||||
|
anchor_t: 4.0 # anchor-multiple threshold
|
||||||
|
# anchors: 3 # anchors per output layer (0 to ignore)
|
||||||
|
fl_gamma: 0.0 # focal loss gamma (efficientDet default gamma=1.5)
|
||||||
|
hsv_h: 0.015 # image HSV-Hue augmentation (fraction)
|
||||||
|
hsv_s: 0.7 # image HSV-Saturation augmentation (fraction)
|
||||||
|
hsv_v: 0.4 # image HSV-Value augmentation (fraction)
|
||||||
|
degrees: 0.0 # image rotation (+/- deg)
|
||||||
|
translate: 0.1 # image translation (+/- fraction)
|
||||||
|
scale: 0.5 # image scale (+/- gain)
|
||||||
|
shear: 0.0 # image shear (+/- deg)
|
||||||
|
perspective: 0.0 # image perspective (+/- fraction), range 0-0.001
|
||||||
|
flipud: 0.0 # image flip up-down (probability)
|
||||||
|
fliplr: 0.5 # image flip left-right (probability)
|
||||||
|
mosaic: 1.0 # image mosaic (probability)
|
||||||
|
mixup: 0.0 # image mixup (probability)
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,102 @@
|
||||||
|
# Objects365 dataset https://www.objects365.org/
|
||||||
|
# Train command: python train.py --data objects365.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /datasets/objects365
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
|
||||||
|
train: ../datasets/objects365/images/train # 1742289 images
|
||||||
|
val: ../datasets/objects365/images/val # 5570 images
|
||||||
|
|
||||||
|
# number of classes
|
||||||
|
nc: 365
|
||||||
|
|
||||||
|
# class names
|
||||||
|
names: [ 'Person', 'Sneakers', 'Chair', 'Other Shoes', 'Hat', 'Car', 'Lamp', 'Glasses', 'Bottle', 'Desk', 'Cup',
|
||||||
|
'Street Lights', 'Cabinet/shelf', 'Handbag/Satchel', 'Bracelet', 'Plate', 'Picture/Frame', 'Helmet', 'Book',
|
||||||
|
'Gloves', 'Storage box', 'Boat', 'Leather Shoes', 'Flower', 'Bench', 'Potted Plant', 'Bowl/Basin', 'Flag',
|
||||||
|
'Pillow', 'Boots', 'Vase', 'Microphone', 'Necklace', 'Ring', 'SUV', 'Wine Glass', 'Belt', 'Monitor/TV',
|
||||||
|
'Backpack', 'Umbrella', 'Traffic Light', 'Speaker', 'Watch', 'Tie', 'Trash bin Can', 'Slippers', 'Bicycle',
|
||||||
|
'Stool', 'Barrel/bucket', 'Van', 'Couch', 'Sandals', 'Basket', 'Drum', 'Pen/Pencil', 'Bus', 'Wild Bird',
|
||||||
|
'High Heels', 'Motorcycle', 'Guitar', 'Carpet', 'Cell Phone', 'Bread', 'Camera', 'Canned', 'Truck',
|
||||||
|
'Traffic cone', 'Cymbal', 'Lifesaver', 'Towel', 'Stuffed Toy', 'Candle', 'Sailboat', 'Laptop', 'Awning',
|
||||||
|
'Bed', 'Faucet', 'Tent', 'Horse', 'Mirror', 'Power outlet', 'Sink', 'Apple', 'Air Conditioner', 'Knife',
|
||||||
|
'Hockey Stick', 'Paddle', 'Pickup Truck', 'Fork', 'Traffic Sign', 'Balloon', 'Tripod', 'Dog', 'Spoon', 'Clock',
|
||||||
|
'Pot', 'Cow', 'Cake', 'Dinning Table', 'Sheep', 'Hanger', 'Blackboard/Whiteboard', 'Napkin', 'Other Fish',
|
||||||
|
'Orange/Tangerine', 'Toiletry', 'Keyboard', 'Tomato', 'Lantern', 'Machinery Vehicle', 'Fan',
|
||||||
|
'Green Vegetables', 'Banana', 'Baseball Glove', 'Airplane', 'Mouse', 'Train', 'Pumpkin', 'Soccer', 'Skiboard',
|
||||||
|
'Luggage', 'Nightstand', 'Tea pot', 'Telephone', 'Trolley', 'Head Phone', 'Sports Car', 'Stop Sign',
|
||||||
|
'Dessert', 'Scooter', 'Stroller', 'Crane', 'Remote', 'Refrigerator', 'Oven', 'Lemon', 'Duck', 'Baseball Bat',
|
||||||
|
'Surveillance Camera', 'Cat', 'Jug', 'Broccoli', 'Piano', 'Pizza', 'Elephant', 'Skateboard', 'Surfboard',
|
||||||
|
'Gun', 'Skating and Skiing shoes', 'Gas stove', 'Donut', 'Bow Tie', 'Carrot', 'Toilet', 'Kite', 'Strawberry',
|
||||||
|
'Other Balls', 'Shovel', 'Pepper', 'Computer Box', 'Toilet Paper', 'Cleaning Products', 'Chopsticks',
|
||||||
|
'Microwave', 'Pigeon', 'Baseball', 'Cutting/chopping Board', 'Coffee Table', 'Side Table', 'Scissors',
|
||||||
|
'Marker', 'Pie', 'Ladder', 'Snowboard', 'Cookies', 'Radiator', 'Fire Hydrant', 'Basketball', 'Zebra', 'Grape',
|
||||||
|
'Giraffe', 'Potato', 'Sausage', 'Tricycle', 'Violin', 'Egg', 'Fire Extinguisher', 'Candy', 'Fire Truck',
|
||||||
|
'Billiards', 'Converter', 'Bathtub', 'Wheelchair', 'Golf Club', 'Briefcase', 'Cucumber', 'Cigar/Cigarette',
|
||||||
|
'Paint Brush', 'Pear', 'Heavy Truck', 'Hamburger', 'Extractor', 'Extension Cord', 'Tong', 'Tennis Racket',
|
||||||
|
'Folder', 'American Football', 'earphone', 'Mask', 'Kettle', 'Tennis', 'Ship', 'Swing', 'Coffee Machine',
|
||||||
|
'Slide', 'Carriage', 'Onion', 'Green beans', 'Projector', 'Frisbee', 'Washing Machine/Drying Machine',
|
||||||
|
'Chicken', 'Printer', 'Watermelon', 'Saxophone', 'Tissue', 'Toothbrush', 'Ice cream', 'Hot-air balloon',
|
||||||
|
'Cello', 'French Fries', 'Scale', 'Trophy', 'Cabbage', 'Hot dog', 'Blender', 'Peach', 'Rice', 'Wallet/Purse',
|
||||||
|
'Volleyball', 'Deer', 'Goose', 'Tape', 'Tablet', 'Cosmetics', 'Trumpet', 'Pineapple', 'Golf Ball',
|
||||||
|
'Ambulance', 'Parking meter', 'Mango', 'Key', 'Hurdle', 'Fishing Rod', 'Medal', 'Flute', 'Brush', 'Penguin',
|
||||||
|
'Megaphone', 'Corn', 'Lettuce', 'Garlic', 'Swan', 'Helicopter', 'Green Onion', 'Sandwich', 'Nuts',
|
||||||
|
'Speed Limit Sign', 'Induction Cooker', 'Broom', 'Trombone', 'Plum', 'Rickshaw', 'Goldfish', 'Kiwi fruit',
|
||||||
|
'Router/modem', 'Poker Card', 'Toaster', 'Shrimp', 'Sushi', 'Cheese', 'Notepaper', 'Cherry', 'Pliers', 'CD',
|
||||||
|
'Pasta', 'Hammer', 'Cue', 'Avocado', 'Hamimelon', 'Flask', 'Mushroom', 'Screwdriver', 'Soap', 'Recorder',
|
||||||
|
'Bear', 'Eggplant', 'Board Eraser', 'Coconut', 'Tape Measure/Ruler', 'Pig', 'Showerhead', 'Globe', 'Chips',
|
||||||
|
'Steak', 'Crosswalk Sign', 'Stapler', 'Camel', 'Formula 1', 'Pomegranate', 'Dishwasher', 'Crab',
|
||||||
|
'Hoverboard', 'Meat ball', 'Rice Cooker', 'Tuba', 'Calculator', 'Papaya', 'Antelope', 'Parrot', 'Seal',
|
||||||
|
'Butterfly', 'Dumbbell', 'Donkey', 'Lion', 'Urinal', 'Dolphin', 'Electric Drill', 'Hair Dryer', 'Egg tart',
|
||||||
|
'Jellyfish', 'Treadmill', 'Lighter', 'Grapefruit', 'Game board', 'Mop', 'Radish', 'Baozi', 'Target', 'French',
|
||||||
|
'Spring Rolls', 'Monkey', 'Rabbit', 'Pencil Case', 'Yak', 'Red Cabbage', 'Binoculars', 'Asparagus', 'Barbell',
|
||||||
|
'Scallop', 'Noddles', 'Comb', 'Dumpling', 'Oyster', 'Table Tennis paddle', 'Cosmetics Brush/Eyeliner Pencil',
|
||||||
|
'Chainsaw', 'Eraser', 'Lobster', 'Durian', 'Okra', 'Lipstick', 'Cosmetics Mirror', 'Curling', 'Table Tennis' ]
|
||||||
|
|
||||||
|
|
||||||
|
# download command/URL (optional) --------------------------------------------------------------------------------------
|
||||||
|
download: |
|
||||||
|
from pycocotools.coco import COCO
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
from utils.general import download, Path
|
||||||
|
|
||||||
|
# Make Directories
|
||||||
|
dir = Path('../datasets/objects365') # dataset directory
|
||||||
|
for p in 'images', 'labels':
|
||||||
|
(dir / p).mkdir(parents=True, exist_ok=True)
|
||||||
|
for q in 'train', 'val':
|
||||||
|
(dir / p / q).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Download
|
||||||
|
url = "https://dorc.ks3-cn-beijing.ksyun.com/data-set/2020Objects365%E6%95%B0%E6%8D%AE%E9%9B%86/train/"
|
||||||
|
download([url + 'zhiyuan_objv2_train.tar.gz'], dir=dir, delete=False) # annotations json
|
||||||
|
download([url + f for f in [f'patch{i}.tar.gz' for i in range(51)]], dir=dir / 'images' / 'train',
|
||||||
|
curl=True, delete=False, threads=8)
|
||||||
|
|
||||||
|
# Move
|
||||||
|
train = dir / 'images' / 'train'
|
||||||
|
for f in tqdm(train.rglob('*.jpg'), desc=f'Moving images'):
|
||||||
|
f.rename(train / f.name) # move to /images/train
|
||||||
|
|
||||||
|
# Labels
|
||||||
|
coco = COCO(dir / 'zhiyuan_objv2_train.json')
|
||||||
|
names = [x["name"] for x in coco.loadCats(coco.getCatIds())]
|
||||||
|
for cid, cat in enumerate(names):
|
||||||
|
catIds = coco.getCatIds(catNms=[cat])
|
||||||
|
imgIds = coco.getImgIds(catIds=catIds)
|
||||||
|
for im in tqdm(coco.loadImgs(imgIds), desc=f'Class {cid + 1}/{len(names)} {cat}'):
|
||||||
|
width, height = im["width"], im["height"]
|
||||||
|
path = Path(im["file_name"]) # image filename
|
||||||
|
try:
|
||||||
|
with open(dir / 'labels' / 'train' / path.with_suffix('.txt').name, 'a') as file:
|
||||||
|
annIds = coco.getAnnIds(imgIds=im["id"], catIds=catIds, iscrowd=None)
|
||||||
|
for a in coco.loadAnns(annIds):
|
||||||
|
x, y, w, h = a['bbox'] # bounding box in xywh (xy top-left corner)
|
||||||
|
x, y = x + w / 2, y + h / 2 # xy to center
|
||||||
|
file.write(f"{cid} {x / width:.5f} {y / height:.5f} {w / width:.5f} {h / height:.5f}\n")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Global Wheat 2020 dataset http://www.global-wheat.com/
|
||||||
|
# Train command: python train.py --data GlobalWheat2020.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /datasets/GlobalWheat2020
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
|
||||||
|
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
|
||||||
|
train: # 3422 images
|
||||||
|
- ../../data/THexit/train/images/
|
||||||
|
|
||||||
|
val: # 748 images (WARNING: train set contains ethz_1)
|
||||||
|
- ../../data/THexit/val/images/
|
||||||
|
|
||||||
|
test: # 1276 images
|
||||||
|
- ../../data/THexit/val/images/
|
||||||
|
|
||||||
|
|
||||||
|
# number of classes
|
||||||
|
nc: 2
|
||||||
|
|
||||||
|
# class names
|
||||||
|
names: [ 'outlet','no' ]
|
||||||
|
|
||||||
|
|
||||||
|
# download command/URL (optional) --------------------------------------------------------------------------------------
|
||||||
|
# download: |
|
||||||
|
# from utils.general import download, Path
|
||||||
|
|
||||||
|
# # Download
|
||||||
|
# dir = Path('../datasets/GlobalWheat2020') # dataset directory
|
||||||
|
# urls = ['https://zenodo.org/record/4298502/files/global-wheat-codalab-official.zip',
|
||||||
|
# 'https://github.com/ultralytics/yolov5/releases/download/v1.0/GlobalWheat2020_labels.zip']
|
||||||
|
# download(urls, dir=dir)
|
||||||
|
|
||||||
|
# # Make Directories
|
||||||
|
# for p in 'annotations', 'images', 'labels':
|
||||||
|
# (dir / p).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# # Move
|
||||||
|
# for p in 'arvalis_1', 'arvalis_2', 'arvalis_3', 'ethz_1', 'rres_1', 'inrae_1', 'usask_1', \
|
||||||
|
# 'utokyo_1', 'utokyo_2', 'nau_1', 'uq_1':
|
||||||
|
# (dir / p).rename(dir / 'images' / p) # move to /images
|
||||||
|
# f = (dir / p).with_suffix('.json') # json file
|
||||||
|
# if f.exists():
|
||||||
|
# f.rename((dir / 'annotations' / p).with_suffix('.json')) # move to /annotations
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Argoverse-HD dataset (ring-front-center camera) http://www.cs.cmu.edu/~mengtial/proj/streaming/
|
||||||
|
# Download command: bash data/scripts/get_argoverse_hd.sh
|
||||||
|
# Train command: python train.py --data argoverse_hd.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /argoverse
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
# Download/unzip images
|
||||||
|
d='../argoverse/' # unzip directory
|
||||||
|
mkdir $d
|
||||||
|
url=https://argoverse-hd.s3.us-east-2.amazonaws.com/
|
||||||
|
f=Argoverse-HD-Full.zip
|
||||||
|
curl -L $url$f -o $f && unzip -q $f -d $d && rm $f &# download, unzip, remove in background
|
||||||
|
wait # finish background tasks
|
||||||
|
|
||||||
|
cd ../argoverse/Argoverse-1.1/
|
||||||
|
ln -s tracking images
|
||||||
|
|
||||||
|
cd ../Argoverse-HD/annotations/
|
||||||
|
|
||||||
|
python3 - "$@" <<END
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
annotation_files = ["train.json", "val.json"]
|
||||||
|
print("Converting annotations to YOLOv5 format...")
|
||||||
|
|
||||||
|
for val in annotation_files:
|
||||||
|
a = json.load(open(val, "rb"))
|
||||||
|
|
||||||
|
label_dict = {}
|
||||||
|
for annot in a['annotations']:
|
||||||
|
img_id = annot['image_id']
|
||||||
|
img_name = a['images'][img_id]['name']
|
||||||
|
img_label_name = img_name[:-3] + "txt"
|
||||||
|
|
||||||
|
cls = annot['category_id'] # instance class id
|
||||||
|
x_center, y_center, width, height = annot['bbox']
|
||||||
|
x_center = (x_center + width / 2) / 1920. # offset and scale
|
||||||
|
y_center = (y_center + height / 2) / 1200. # offset and scale
|
||||||
|
width /= 1920. # scale
|
||||||
|
height /= 1200. # scale
|
||||||
|
|
||||||
|
img_dir = "./labels/" + a['seq_dirs'][a['images'][annot['image_id']]['sid']]
|
||||||
|
|
||||||
|
Path(img_dir).mkdir(parents=True, exist_ok=True)
|
||||||
|
if img_dir + "/" + img_label_name not in label_dict:
|
||||||
|
label_dict[img_dir + "/" + img_label_name] = []
|
||||||
|
|
||||||
|
label_dict[img_dir + "/" + img_label_name].append(f"{cls} {x_center} {y_center} {width} {height}\n")
|
||||||
|
|
||||||
|
for filename in label_dict:
|
||||||
|
with open(filename, "w") as file:
|
||||||
|
for string in label_dict[filename]:
|
||||||
|
file.write(string)
|
||||||
|
|
||||||
|
END
|
||||||
|
|
||||||
|
mv ./labels ../../Argoverse-1.1/
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# COCO 2017 dataset http://cocodataset.org
|
||||||
|
# Download command: bash data/scripts/get_coco.sh
|
||||||
|
# Train command: python train.py --data coco.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /coco
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
# Download/unzip labels
|
||||||
|
d='../' # unzip directory
|
||||||
|
url=https://github.com/ultralytics/yolov5/releases/download/v1.0/
|
||||||
|
f='coco2017labels.zip' # or 'coco2017labels-segments.zip', 68 MB
|
||||||
|
echo 'Downloading' $url$f ' ...'
|
||||||
|
curl -L $url$f -o $f && unzip -q $f -d $d && rm $f & # download, unzip, remove in background
|
||||||
|
|
||||||
|
# Download/unzip images
|
||||||
|
d='../coco/images' # unzip directory
|
||||||
|
url=http://images.cocodataset.org/zips/
|
||||||
|
f1='train2017.zip' # 19G, 118k images
|
||||||
|
f2='val2017.zip' # 1G, 5k images
|
||||||
|
f3='test2017.zip' # 7G, 41k images (optional)
|
||||||
|
for f in $f1 $f2; do
|
||||||
|
echo 'Downloading' $url$f '...'
|
||||||
|
curl -L $url$f -o $f && unzip -q $f -d $d && rm $f & # download, unzip, remove in background
|
||||||
|
done
|
||||||
|
wait # finish background tasks
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# COCO128 dataset https://www.kaggle.com/ultralytics/coco128
|
||||||
|
# Download command: bash data/scripts/get_coco128.sh
|
||||||
|
# Train command: python train.py --data coco128.yaml
|
||||||
|
# Default dataset location is next to /yolov5:
|
||||||
|
# /parent_folder
|
||||||
|
# /coco128
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
# Download/unzip images and labels
|
||||||
|
d='../' # unzip directory
|
||||||
|
url=https://github.com/ultralytics/yolov5/releases/download/v1.0/
|
||||||
|
f='coco128.zip' # or 'coco2017labels-segments.zip', 68 MB
|
||||||
|
echo 'Downloading' $url$f ' ...'
|
||||||
|
curl -L $url$f -o $f && unzip -q $f -d $d && rm $f & # download, unzip, remove in background
|
||||||
|
|
||||||
|
wait # finish background tasks
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# PASCAL VOC dataset http://host.robots.ox.ac.uk/pascal/VOC/
|
||||||
|
# Download command: bash data/scripts/get_voc.sh
|
||||||
|
# Train command: python train.py --data voc.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /VOC
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
start=$(date +%s)
|
||||||
|
mkdir -p ../tmp
|
||||||
|
cd ../tmp/
|
||||||
|
|
||||||
|
# Download/unzip images and labels
|
||||||
|
d='.' # unzip directory
|
||||||
|
url=https://github.com/ultralytics/yolov5/releases/download/v1.0/
|
||||||
|
f1=VOCtrainval_06-Nov-2007.zip # 446MB, 5012 images
|
||||||
|
f2=VOCtest_06-Nov-2007.zip # 438MB, 4953 images
|
||||||
|
f3=VOCtrainval_11-May-2012.zip # 1.95GB, 17126 images
|
||||||
|
for f in $f3 $f2 $f1; do
|
||||||
|
echo 'Downloading' $url$f '...'
|
||||||
|
curl -L $url$f -o $f && unzip -q $f -d $d && rm $f & # download, unzip, remove in background
|
||||||
|
done
|
||||||
|
wait # finish background tasks
|
||||||
|
|
||||||
|
end=$(date +%s)
|
||||||
|
runtime=$((end - start))
|
||||||
|
echo "Completed in" $runtime "seconds"
|
||||||
|
|
||||||
|
echo "Splitting dataset..."
|
||||||
|
python3 - "$@" <<END
|
||||||
|
import os
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from os import getcwd
|
||||||
|
|
||||||
|
sets = [('2012', 'train'), ('2012', 'val'), ('2007', 'train'), ('2007', 'val'), ('2007', 'test')]
|
||||||
|
|
||||||
|
classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog",
|
||||||
|
"horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]
|
||||||
|
|
||||||
|
|
||||||
|
def convert_box(size, box):
|
||||||
|
dw = 1. / (size[0])
|
||||||
|
dh = 1. / (size[1])
|
||||||
|
x, y, w, h = (box[0] + box[1]) / 2.0 - 1, (box[2] + box[3]) / 2.0 - 1, box[1] - box[0], box[3] - box[2]
|
||||||
|
return x * dw, y * dh, w * dw, h * dh
|
||||||
|
|
||||||
|
|
||||||
|
def convert_annotation(year, image_id):
|
||||||
|
in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml' % (year, image_id))
|
||||||
|
out_file = open('VOCdevkit/VOC%s/labels/%s.txt' % (year, image_id), 'w')
|
||||||
|
tree = ET.parse(in_file)
|
||||||
|
root = tree.getroot()
|
||||||
|
size = root.find('size')
|
||||||
|
w = int(size.find('width').text)
|
||||||
|
h = int(size.find('height').text)
|
||||||
|
|
||||||
|
for obj in root.iter('object'):
|
||||||
|
difficult = obj.find('difficult').text
|
||||||
|
cls = obj.find('name').text
|
||||||
|
if cls not in classes or int(difficult) == 1:
|
||||||
|
continue
|
||||||
|
cls_id = classes.index(cls)
|
||||||
|
xmlbox = obj.find('bndbox')
|
||||||
|
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
|
||||||
|
float(xmlbox.find('ymax').text))
|
||||||
|
bb = convert_box((w, h), b)
|
||||||
|
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
|
||||||
|
|
||||||
|
|
||||||
|
cwd = getcwd()
|
||||||
|
for year, image_set in sets:
|
||||||
|
if not os.path.exists('VOCdevkit/VOC%s/labels/' % year):
|
||||||
|
os.makedirs('VOCdevkit/VOC%s/labels/' % year)
|
||||||
|
image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt' % (year, image_set)).read().strip().split()
|
||||||
|
list_file = open('%s_%s.txt' % (year, image_set), 'w')
|
||||||
|
for image_id in image_ids:
|
||||||
|
list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n' % (cwd, year, image_id))
|
||||||
|
convert_annotation(year, image_id)
|
||||||
|
list_file.close()
|
||||||
|
END
|
||||||
|
|
||||||
|
cat 2007_train.txt 2007_val.txt 2012_train.txt 2012_val.txt >train.txt
|
||||||
|
cat 2007_train.txt 2007_val.txt 2007_test.txt 2012_train.txt 2012_val.txt >train.all.txt
|
||||||
|
|
||||||
|
mkdir ../VOC ../VOC/images ../VOC/images/train ../VOC/images/val
|
||||||
|
mkdir ../VOC/labels ../VOC/labels/train ../VOC/labels/val
|
||||||
|
|
||||||
|
python3 - "$@" <<END
|
||||||
|
import os
|
||||||
|
|
||||||
|
print(os.path.exists('../tmp/train.txt'))
|
||||||
|
with open('../tmp/train.txt', 'r') as f:
|
||||||
|
for line in f.readlines():
|
||||||
|
line = "/".join(line.split('/')[-5:]).strip()
|
||||||
|
if os.path.exists("../" + line):
|
||||||
|
os.system("cp ../" + line + " ../VOC/images/train")
|
||||||
|
|
||||||
|
line = line.replace('JPEGImages', 'labels').replace('jpg', 'txt')
|
||||||
|
if os.path.exists("../" + line):
|
||||||
|
os.system("cp ../" + line + " ../VOC/labels/train")
|
||||||
|
|
||||||
|
print(os.path.exists('../tmp/2007_test.txt'))
|
||||||
|
with open('../tmp/2007_test.txt', 'r') as f:
|
||||||
|
for line in f.readlines():
|
||||||
|
line = "/".join(line.split('/')[-5:]).strip()
|
||||||
|
if os.path.exists("../" + line):
|
||||||
|
os.system("cp ../" + line + " ../VOC/images/val")
|
||||||
|
|
||||||
|
line = line.replace('JPEGImages', 'labels').replace('jpg', 'txt')
|
||||||
|
if os.path.exists("../" + line):
|
||||||
|
os.system("cp ../" + line + " ../VOC/labels/val")
|
||||||
|
END
|
||||||
|
|
||||||
|
rm -rf ../tmp # remove temporary directory
|
||||||
|
echo "VOC download done."
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
|
@ -0,0 +1,263 @@
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000626.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000480.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/30.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000882.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000749.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/79.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000085.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000754.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000837.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001005.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000744.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000147.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000170.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000264.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000747.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000379.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000451.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001140.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000488.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001167.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000231.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000528.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000743.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000535.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000718.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/63.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000157.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/16.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000629.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000905.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001065.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/18.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001014.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000025.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000700.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000822.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000674.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000361.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000220.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000549.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000909.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000438.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000097.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000250.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001078.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/39.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000112.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001194.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000037.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001181.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/15.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000015.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001097.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000079.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000892.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000374.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000984.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000785.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000440.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000107.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000918.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000920.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000054.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000228.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000824.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/51.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000478.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000763.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000242.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000757.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000617.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001122.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/72.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000960.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000871.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000122.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000782.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000603.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000843.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000742.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000795.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000049.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001161.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000985.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000452.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001093.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000521.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000269.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000391.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/5.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000935.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000006.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000132.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000434.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000302.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000354.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000551.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001143.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000355.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000968.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000411.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/37.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000858.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000162.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000343.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/32.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000919.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000336.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000857.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000787.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001050.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000796.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000155.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000115.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000750.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000694.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000249.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000697.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000675.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000460.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000459.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001164.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000324.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000292.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001125.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001004.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000772.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001104.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000518.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000377.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000262.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000522.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000316.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000550.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000143.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000876.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000225.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000712.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000561.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000650.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/2.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000828.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000863.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000356.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/74.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000619.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000152.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000600.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001062.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/75.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000720.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000903.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000375.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000400.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000010.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001128.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000094.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000586.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000078.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000313.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000270.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000719.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000332.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000031.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000676.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001114.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000707.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000976.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000055.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/102.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000738.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000666.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000422.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000116.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001153.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000353.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000430.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001012.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000021.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000065.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000166.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000544.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000593.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001134.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/93.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000516.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000279.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000360.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001045.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000059.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/55.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000877.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000267.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000042.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000627.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/65.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000853.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000511.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000043.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000383.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000739.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000182.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001060.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/107.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000253.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001082.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000952.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000427.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000624.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000599.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000622.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000883.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000380.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000731.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000180.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000204.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000653.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000542.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000118.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001141.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001032.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000789.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000477.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000068.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000221.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001071.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000408.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000385.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000734.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000436.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/78.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001142.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000886.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000523.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/34.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000321.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000163.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000818.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000656.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000347.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000084.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000202.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000474.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000168.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/67.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001041.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000340.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000668.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000604.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000187.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000456.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001150.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001025.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000206.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000533.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000441.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000873.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000490.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001086.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001096.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00001008.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000426.jpg
|
||||||
|
/home/thsw/WJ/data/exitPPS/images/00000388.jpg
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# PASCAL VOC dataset http://host.robots.ox.ac.uk/pascal/VOC/
|
||||||
|
# Train command: python train.py --data voc.yaml
|
||||||
|
# Default dataset location is next to YOLOv5:
|
||||||
|
# /parent_folder
|
||||||
|
# /VOC
|
||||||
|
# /yolov5
|
||||||
|
|
||||||
|
|
||||||
|
# download command/URL (optional)
|
||||||
|
download: bash data/scripts/get_voc.sh
|
||||||
|
|
||||||
|
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
|
||||||
|
train: ../VOC/images/train/ # 16551 images
|
||||||
|
val: ../VOC/images/val/ # 4952 images
|
||||||
|
|
||||||
|
# number of classes
|
||||||
|
nc: 20
|
||||||
|
|
||||||
|
# class names
|
||||||
|
names: [ 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog',
|
||||||
|
'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor' ]
|
||||||
|
|
@ -0,0 +1,443 @@
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
import random,string
|
||||||
|
import cv2
|
||||||
|
import torch
|
||||||
|
import torch.backends.cudnn as cudnn
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
from models.experimental import attempt_load
|
||||||
|
from utils.datasets import LoadStreams, LoadImages
|
||||||
|
from utils.general import check_img_size, check_requirements, check_imshow, non_max_suppression, apply_classifier, \
|
||||||
|
scale_coords, xyxy2xywh, strip_optimizer, set_logging, increment_path
|
||||||
|
from utils.plots import plot_one_box,plot_one_box_PIL,draw_painting_joint,get_label_arrays,get_websource,smooth_outline_auto
|
||||||
|
from utils.get_offline_url import update_websource_offAndLive,platurlToJsonfile, get_websource_fromTxt
|
||||||
|
from utils.torch_utils import select_device, load_classifier, time_synchronized
|
||||||
|
import cv2
|
||||||
|
import queue
|
||||||
|
import os,json,sys
|
||||||
|
import numpy as np
|
||||||
|
from threading import Thread
|
||||||
|
import datetime,_thread
|
||||||
|
import subprocess as sp
|
||||||
|
import time
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
from segutils.segmodel import SegModel,get_largest_contours
|
||||||
|
sys.path.extend(['/home/thsw2/WJ/src/yolov5/segutils'])
|
||||||
|
#from segutils.segWaterBuilding import SegModel,get_largest_contours,illBuildings
|
||||||
|
from segutils.core.models.bisenet import BiSeNet_MultiOutput
|
||||||
|
from collections import Counter
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
platform_query_url='http://47.96.182.154:9051/api/suanfa/getPlatformInfo'
|
||||||
|
offlineFile='mintors/offlines/doneCodes.txt'
|
||||||
|
|
||||||
|
def get_cls(array):
|
||||||
|
dcs = Counter(array)
|
||||||
|
keys = list(dcs.keys())
|
||||||
|
values = list(dcs.values())
|
||||||
|
max_index = values.index(max(values))
|
||||||
|
cls = int(keys[max_index])
|
||||||
|
return cls
|
||||||
|
|
||||||
|
##9月3日之后,需要新增语义分割模型。
|
||||||
|
##10月22日, source 改成文件输入
|
||||||
|
##12月31日,支持从platform读取离线视频
|
||||||
|
# 使用线程锁,防止线程死锁
|
||||||
|
mutex = _thread.allocate_lock()
|
||||||
|
# 存图片的队列
|
||||||
|
frame_queue = queue.Queue()
|
||||||
|
|
||||||
|
# 推流的地址,前端通过这个地址拉流,主机的IP,2019是ffmpeg在nginx中设置的端口号
|
||||||
|
|
||||||
|
|
||||||
|
#camera_path='rtmp://58.200.131.2:1935/livetv/cctv1'
|
||||||
|
camera_path='/data/WJ/data/THexit/vedio/XiYuDaiHe4.MP4'
|
||||||
|
###lables colors (BGR)#####
|
||||||
|
rainbows=[
|
||||||
|
(0,0,255),(0,255,0),(255,0,0),(255,0,255),(255,255,0),(255,127,0),(255,0,127),
|
||||||
|
(127,255,0),(0,255,127),(0,127,255),(127,0,255),(255,127,255),(255,255,127),
|
||||||
|
(127,255,255),(0,255,255),(255,127,255),(127,255,255),
|
||||||
|
(0,127,0),(0,0,127),(0,255,255)
|
||||||
|
]
|
||||||
|
|
||||||
|
def detect(save_img=False):
|
||||||
|
rtmpUrl = "rtmp://127.0.0.1:1935/live/test"
|
||||||
|
OutVideoW,OutVideoH,OutVideoFps=int(opt.OutVideoW),int(opt.OutVideoH),int(opt.OutVideoFps)
|
||||||
|
command=['ffmpeg',
|
||||||
|
'-y',
|
||||||
|
#'-re',' ',
|
||||||
|
'-f', 'rawvideo',
|
||||||
|
'-vcodec','rawvideo',
|
||||||
|
'-pix_fmt', 'bgr24',
|
||||||
|
'-s', "{}x{}".format(OutVideoW,OutVideoH),# 图片分辨率
|
||||||
|
#'-vcodec','libx264',
|
||||||
|
#'-b','2500k',
|
||||||
|
'-r', str(OutVideoFps),# 视频帧率
|
||||||
|
'-i', '-',
|
||||||
|
'-c:v', 'libx264',
|
||||||
|
'-pix_fmt', 'yuv420p',
|
||||||
|
#'-preset', 'ultrafast',
|
||||||
|
'-f', 'flv',
|
||||||
|
rtmpUrl]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
source, weights, view_img, save_txt, imgsz = opt.source, opt.weights, opt.view_img, opt.save_txt, opt.img_size
|
||||||
|
#save_img = not opt.nosave and not source.endswith('.txt') # save inference images
|
||||||
|
save_img = not opt.nosave
|
||||||
|
webcam = source.isnumeric() or source.endswith('.txt') or source.lower().startswith(
|
||||||
|
('rtsp://', 'rtmp://', 'http://', 'https://'))
|
||||||
|
sourceTxt=source
|
||||||
|
if source.endswith('.txt'):
|
||||||
|
#source_list,port_list,streamName_list = get_websource(source)
|
||||||
|
source_infos = get_websource_fromTxt(source)
|
||||||
|
else:
|
||||||
|
#source_list,port_list,streamName_list = [source],[1935],['demo']
|
||||||
|
source_infos = [{'url':source,'port':1935,'name':'demo' }]
|
||||||
|
# Directories
|
||||||
|
save_dir = Path(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok)) # increment run
|
||||||
|
(save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir
|
||||||
|
|
||||||
|
# Initialize
|
||||||
|
set_logging()
|
||||||
|
device = select_device(opt.device)
|
||||||
|
half = device.type != 'cpu' # half precision only supported on CUDA
|
||||||
|
# Load model
|
||||||
|
model = attempt_load(weights, map_location=device) # load FP32 model
|
||||||
|
stride = int(model.stride.max()) # model stride
|
||||||
|
imgsz = check_img_size(imgsz, s=stride) # check img_size
|
||||||
|
#print('###'*20,imgsz,stride)
|
||||||
|
|
||||||
|
##加载分割模型###
|
||||||
|
seg_nclass = 2
|
||||||
|
weights = 'weights/segmentation/BiSeNet/checkpoint.pth'
|
||||||
|
segmodel = SegModel(nclass=seg_nclass,weights=weights,device=device)
|
||||||
|
|
||||||
|
'''nclass = [2,2]
|
||||||
|
Segmodel = BiSeNet_MultiOutput(nclass)
|
||||||
|
weights='weights/segmentation/WaterBuilding.pth'
|
||||||
|
segmodel = SegModel(model=Segmodel,nclass=nclass,weights=weights,device='cuda:0',multiOutput=True)'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if half:
|
||||||
|
model.half() # to FP16
|
||||||
|
# Second-stage classifier
|
||||||
|
classify = False
|
||||||
|
if classify:
|
||||||
|
modelc = load_classifier(name='resnet101', n=2) # initialize
|
||||||
|
modelc.load_state_dict(torch.load('weights/resnet101.pt', map_location=device)['model']).to(device).eval()
|
||||||
|
if webcam:
|
||||||
|
# create file pointer
|
||||||
|
fp_out=open('mintors/%s.txt'%(time.strftime("Start-%Y-%m-%d-%H-%M-%S", time.localtime())) ,'w')
|
||||||
|
# Set Dataloader
|
||||||
|
vid_path, vid_writer = None, None
|
||||||
|
stream_id = 0
|
||||||
|
platurlToJsonfile(platform_query_url)
|
||||||
|
while True:
|
||||||
|
#for isource in range(len(source_list)):
|
||||||
|
for isource in range(len(source_infos)):
|
||||||
|
#source , port ,streamName = source_list[isource],port_list[isource],streamName_list[isource]
|
||||||
|
source , port ,streamName = source_infos[isource]['url'],source_infos[isource]['port'],source_infos[isource]['name']
|
||||||
|
print('########## detect.py line129 souce informations:',isource, source , port ,streamName,webcam )
|
||||||
|
Push_Flag = False
|
||||||
|
#for wang in ['test']:
|
||||||
|
try:
|
||||||
|
if webcam:
|
||||||
|
#view_img = check_imshow()
|
||||||
|
cudnn.benchmark = True # set True to speed up constant image size inference
|
||||||
|
print('#########Using web cam#################')
|
||||||
|
dataset = LoadStreams(source, img_size=imgsz, stride=stride)
|
||||||
|
|
||||||
|
# Get names and colors,fp_log,fp_out都是日志文件
|
||||||
|
fp_log=open('mintors/detection/stream_%s_%d-%s.txt'%(streamName,stream_id,time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())) ,'w')
|
||||||
|
fp_out.write(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())+ ' rtmp stream-%s-%d starts \n'%(streamName,stream_id) )
|
||||||
|
fp_out.flush()
|
||||||
|
problem_image = [[],[],[],[],[]];
|
||||||
|
else:
|
||||||
|
dataset = LoadImages(source, img_size=imgsz, stride=stride)
|
||||||
|
|
||||||
|
|
||||||
|
####
|
||||||
|
iimage_cnt = 0
|
||||||
|
names = model.module.names if hasattr(model, 'module') else model.names
|
||||||
|
EngLish_label = True
|
||||||
|
if os.path.exists(opt.labelnames):
|
||||||
|
with open(opt.labelnames,'r') as fp:
|
||||||
|
namesjson=json.load(fp)
|
||||||
|
names_fromfile=namesjson['labelnames']
|
||||||
|
if len(names_fromfile) == len(names):
|
||||||
|
names = names_fromfile
|
||||||
|
EngLish_label = False
|
||||||
|
else:
|
||||||
|
print('******Warning 文件:%s读取的类别数目与模型中的数目不一致,使用模型的类别********'%(opt.labelnames))
|
||||||
|
|
||||||
|
colors = rainbows
|
||||||
|
label_arraylist = get_label_arrays(names,colors,outfontsize=40)
|
||||||
|
|
||||||
|
# Run inference
|
||||||
|
if device.type != 'cpu':
|
||||||
|
model(torch.zeros(1, 3, imgsz, imgsz).to(device).type_as(next(model.parameters()))) # run once
|
||||||
|
t00 = time.time();
|
||||||
|
|
||||||
|
|
||||||
|
if webcam:
|
||||||
|
while True:
|
||||||
|
if len(command) > 0:
|
||||||
|
rtmpUrl = "rtmp://127.0.0.1:%s/live/test"%(port)
|
||||||
|
command[-1] = rtmpUrl
|
||||||
|
# 管道配置,其中用到管道
|
||||||
|
print(command)
|
||||||
|
ppipe = sp.Popen(command, stdin=sp.PIPE)
|
||||||
|
Push_Flag = True
|
||||||
|
break
|
||||||
|
time00=time.time()
|
||||||
|
|
||||||
|
for path, img, im0s, vid_cap in dataset:
|
||||||
|
t0= time_synchronized()
|
||||||
|
img = torch.from_numpy(img).to(device)
|
||||||
|
img = img.half() if half else img.float() # uint8 to fp16/32
|
||||||
|
img /= 255.0 # 0 - 255 to 0.0 - 1.0
|
||||||
|
timeseg0 = time.time()
|
||||||
|
if segmodel:
|
||||||
|
if webcam:
|
||||||
|
seg_pred,segstr = segmodel.eval(im0s[0] )
|
||||||
|
#seg_pred = segmodel.eval(im0s[0],outsize=None,smooth_kernel=20)
|
||||||
|
else:
|
||||||
|
seg_pred,segstr = segmodel.eval(im0s )
|
||||||
|
#seg_pred = segmodel.eval(im0s[0],outsize=None,smooth_kernel=20)
|
||||||
|
|
||||||
|
|
||||||
|
timeseg1 = time.time()
|
||||||
|
if img.ndimension() == 3:
|
||||||
|
img = img.unsqueeze(0)
|
||||||
|
|
||||||
|
# Inference
|
||||||
|
t1 = time_synchronized()
|
||||||
|
|
||||||
|
pred = model(img, augment=opt.augment)[0]
|
||||||
|
#print('###','line197:',img.shape,opt.augment,opt.conf_thres, opt.iou_thres, opt.classes, opt.agnostic_nms)
|
||||||
|
# Apply NMS
|
||||||
|
pred = non_max_suppression(pred, opt.conf_thres, opt.iou_thres, classes=opt.classes, agnostic=opt.agnostic_nms)
|
||||||
|
t2 = time_synchronized()
|
||||||
|
# Apply Classifier
|
||||||
|
if classify:
|
||||||
|
pred = apply_classifier(pred, modelc, img, im0s)
|
||||||
|
|
||||||
|
# Process detections
|
||||||
|
for i, det in enumerate(pred): # detections per image
|
||||||
|
if webcam: # batch_size >= 1
|
||||||
|
p, s, im0, frame = path[i], '%g: ' % i, im0s[i].copy(), dataset.count
|
||||||
|
im0_bak = im0.copy()
|
||||||
|
else:
|
||||||
|
p, s, im0, frame = path, '', im0s, getattr(dataset, 'frame', 0)
|
||||||
|
|
||||||
|
iimage_cnt += 1
|
||||||
|
p = Path(p) # to Path
|
||||||
|
save_path = str(save_dir / p.name) # img.jpg
|
||||||
|
|
||||||
|
txt_path = str(save_dir / 'labels' / p.stem) + ('' if dataset.mode == 'image' else f'_{frame}') # img.txt
|
||||||
|
s += '%gx%g ' % img.shape[2:] # print string
|
||||||
|
gn = torch.tensor(im0.shape)[[1, 0, 1, 0]] # normalization gain whwh
|
||||||
|
|
||||||
|
|
||||||
|
if segmodel:
|
||||||
|
contours, hierarchy = cv2.findContours(seg_pred,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
|
||||||
|
water = seg_pred.copy();
|
||||||
|
if len(contours)>0:
|
||||||
|
max_id = get_largest_contours(contours)
|
||||||
|
water[:,:]=0
|
||||||
|
cv2.fillPoly(water, [contours[max_id][:,0,:]], 1)
|
||||||
|
|
||||||
|
cv2.drawContours(im0,contours,max_id,(0,255,255),3)
|
||||||
|
|
||||||
|
else:
|
||||||
|
water[:,:] = 0
|
||||||
|
#im0,water = illBuildings(seg_pred,im0)
|
||||||
|
|
||||||
|
if len(det):
|
||||||
|
# Rescale boxes from img_size to im0 size
|
||||||
|
det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()
|
||||||
|
#check weather the box is inside of water area
|
||||||
|
if segmodel:
|
||||||
|
det_c = det.clone(); det_c=det_c.cpu().numpy()
|
||||||
|
#area_factors = np.array([np.sum(water[int(x[1]):int(x[3]), int(x[0]):int(x[2])] )/((x[2]-x[0])*(x[3]-x[1])) for x in det] )
|
||||||
|
area_factors = np.array([np.sum(water[int(x[1]):int(x[3]), int(x[0]):int(x[2])] )/((x[2]-x[0])*(x[3]-x[1])) for x in det_c] )
|
||||||
|
#det = det[area_factors>0.1]
|
||||||
|
det = det[area_factors>0.03]
|
||||||
|
###联通要求,临时屏蔽掉水生植被
|
||||||
|
'''if len(det):
|
||||||
|
clss = det[:,5]
|
||||||
|
det = det[clss!=2]'''
|
||||||
|
|
||||||
|
if len(det)>0:
|
||||||
|
# Print results
|
||||||
|
for c in det[:, -1].unique():
|
||||||
|
n = (det[:, -1] == c).sum() # detections per class
|
||||||
|
s += f"{n} {names[int(c)]}{'s' * (n > 1)}, " # add to string
|
||||||
|
|
||||||
|
# Write results
|
||||||
|
for *xyxy, conf, cls in reversed(det):
|
||||||
|
if save_txt: # Write to file
|
||||||
|
xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh
|
||||||
|
line = (cls, *xywh, conf) if opt.save_conf else (cls, *xywh) # label format
|
||||||
|
with open(txt_path + '.txt', 'a') as f:
|
||||||
|
f.write(('%g ' * len(line)).rstrip() % line + '\n')
|
||||||
|
|
||||||
|
if save_img or view_img: # Add bbox to image
|
||||||
|
label = f'{names[int(cls)]} {conf:.2f}'
|
||||||
|
if EngLish_label:
|
||||||
|
plot_one_box(xyxy, im0, label=label, color=colors[int(cls)%20], line_thickness=3)
|
||||||
|
else:
|
||||||
|
#im0=plot_one_box_PIL(xyxy, im0, label=label, color=colors[int(cls)%20], line_thickness=3)
|
||||||
|
im0 = draw_painting_joint(xyxy,im0,label_arraylist[int(cls)],score=conf,color=rainbows[int(cls)%20],line_thickness=None)
|
||||||
|
|
||||||
|
###处理问题图片,每fpsample帧,上报一张图片。以平均得分最大的为最佳图片
|
||||||
|
if webcam:
|
||||||
|
problem_image[0].append( det[:,4].mean()); problem_image[1].append(det);
|
||||||
|
problem_image[2].append(im0);problem_image[3].append(iimage_cnt);problem_image[4].append(im0_bak)
|
||||||
|
|
||||||
|
if webcam & (iimage_cnt % opt.fpsample == 0) & (dataset.mode != 'image') & (len(problem_image[0])>0):
|
||||||
|
best_index = problem_image[0].index(max(problem_image[0]))
|
||||||
|
best_frame = problem_image[3][ best_index]
|
||||||
|
img_send = problem_image[2][ best_index]
|
||||||
|
img_bak = problem_image[4][ best_index]
|
||||||
|
dets = problem_image[1][best_index]
|
||||||
|
cls_max = get_cls(dets[:,5].cpu().detach().numpy())
|
||||||
|
|
||||||
|
time_str = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
|
||||||
|
uid=''.join(random.sample(string.ascii_letters + string.digits, 16))
|
||||||
|
AIUrl='problems/images_tmp/%s_frame-%d-%d_type-%s_%s_s-%s_AI.jpg'%(time_str,best_frame,iimage_cnt,names[cls_max],uid,streamName)
|
||||||
|
ORIUrl='problems/images_tmp/%s_frame-%d-%d_type-%s_%s_s-%s_OR.jpg'%(time_str,best_frame,iimage_cnt,names[cls_max],uid,streamName)
|
||||||
|
cv2.imwrite(AIUrl,img_send); cv2.imwrite(ORIUrl,img_bak)
|
||||||
|
outstr='%s save images to %s \n'%(time_str, AIUrl)
|
||||||
|
fp_log.write(outstr )
|
||||||
|
problem_image=[[],[],[],[],[]]
|
||||||
|
|
||||||
|
# Print time (inference + NMS)
|
||||||
|
t3 = time_synchronized()
|
||||||
|
|
||||||
|
# Save results (image with detections)
|
||||||
|
if save_img:
|
||||||
|
if dataset.mode == 'image':
|
||||||
|
cv2.imwrite(save_path, im0)
|
||||||
|
else: # 'video' or 'stream'
|
||||||
|
if vid_path != save_path: # new video
|
||||||
|
vid_path = save_path
|
||||||
|
if isinstance(vid_writer, cv2.VideoWriter):
|
||||||
|
vid_writer.release() # release previous video writer
|
||||||
|
if vid_cap: # video
|
||||||
|
fps = vid_cap.get(cv2.CAP_PROP_FPS)
|
||||||
|
w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
||||||
|
h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
||||||
|
else: # stream
|
||||||
|
fps, w, h = 30, im0.shape[1], im0.shape[0]
|
||||||
|
save_path += '.mp4'
|
||||||
|
vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
|
||||||
|
im0 = cv2.resize(im0,(OutVideoW,OutVideoH))
|
||||||
|
|
||||||
|
if dataset.mode == 'stream':
|
||||||
|
ppipe.stdin.write(im0.tostring())
|
||||||
|
|
||||||
|
t4 = time_synchronized()
|
||||||
|
#outstr='%s Done.read:%.1f ms, infer:%.1f ms, seginfer:%.1f ms,draw:%.1f ms, save:%.1f ms total:%.1f ms \n'%(s,(t1 - t0)*1000, (t2 - t1)*1000,(timeseg1-timeseg0)*1000, (t3 - t2)*1000,(t4-t3)*1000, (t4-t00)*1000)
|
||||||
|
timestr=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||||
|
outstr='%s ,%s,iframe:%d,,read:%.1f ms,copy:%.1f, infer:%.1f ms, detinfer:%.1f ms,draw:%.1f ms, save:%.1f ms total:%.1f ms \n'%(s,timestr,iimage_cnt,(t0 - t00)*1000,(timeseg0-t0)*1000, (t1 - timeseg0)*1000,(t2-t1)*1000, (t3 - t2)*1000,(t4-t3)*1000, (t4-t00)*1000)
|
||||||
|
if webcam:
|
||||||
|
if len(det):
|
||||||
|
fp_log.write(outstr )
|
||||||
|
else:
|
||||||
|
print(outstr)
|
||||||
|
print(segstr)
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
t00=t4
|
||||||
|
|
||||||
|
|
||||||
|
if save_txt or save_img:
|
||||||
|
s = f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else ''
|
||||||
|
print(f"Results saved to {save_dir}{s}")
|
||||||
|
|
||||||
|
print(f'Done. ({time.time() - t0:.3f}s)')
|
||||||
|
if not webcam:
|
||||||
|
break;
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print('#######reading souce:%s ,error :%s:'%(source,e ))
|
||||||
|
if Push_Flag and webcam:
|
||||||
|
####source end 推流or视频结束 ###
|
||||||
|
ppipe.kill()
|
||||||
|
|
||||||
|
fp_out.write(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) +' rtmp stream-%s-%d ends \n'%(streamName,stream_id) );fp_out.flush()
|
||||||
|
stream_id += 1
|
||||||
|
time_str = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
|
||||||
|
if 'off' in streamName:##只有离线视频时,才会写结束文件。
|
||||||
|
EndUrl='problems/images_tmp/%s_frame-9999-9999_type-结束_9999999999999999_s-%s_AI.jpg'%(time_str,streamName)
|
||||||
|
img_end=np.zeros((100,100),dtype=np.uint8);cv2.imwrite(EndUrl,img_end)
|
||||||
|
EndUrl='problems/images_tmp/%s_frame-9999-9999_type-结束_9999999999999999_s-%s_OR.jpg'%(time_str,streamName)
|
||||||
|
cv2.imwrite(EndUrl,img_end)
|
||||||
|
time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||||
|
with open( 'mintors/offlines/doneCodes.txt','a+' ) as fp:
|
||||||
|
fp.write('%s %s\n'%(time_str,streamName ))
|
||||||
|
|
||||||
|
#source_infos=update_websource_offAndLive(platform_query_url,sourceTxt,offlineFile)
|
||||||
|
fp_log.close()
|
||||||
|
if webcam:
|
||||||
|
fp_out.write( time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())+' GPU server sleep 10s \n' ) ;fp_out.flush()
|
||||||
|
time.sleep(10)
|
||||||
|
|
||||||
|
if not webcam:
|
||||||
|
break;
|
||||||
|
|
||||||
|
###update source (online or offline)
|
||||||
|
source_infos=update_websource_offAndLive(platform_query_url,sourceTxt,offlineFile)
|
||||||
|
if len(source_infos)==0:
|
||||||
|
print('######NO valid source sleep 10s#####')
|
||||||
|
time.sleep(10)
|
||||||
|
if webcam:
|
||||||
|
fp_out.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--weights', nargs='+', type=str, default='yolov5s.pt', help='model.pt path(s)')
|
||||||
|
parser.add_argument('--source', type=str, default='data/images', help='source') # file/folder, 0 for webcam
|
||||||
|
parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
|
||||||
|
parser.add_argument('--conf-thres', type=float, default=0.25, help='object confidence threshold')
|
||||||
|
parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS')
|
||||||
|
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
|
||||||
|
parser.add_argument('--view-img', action='store_true', help='display results')
|
||||||
|
parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
|
||||||
|
parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
|
||||||
|
parser.add_argument('--nosave', action='store_true', help='do not save images/videos')
|
||||||
|
parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')
|
||||||
|
parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
|
||||||
|
parser.add_argument('--augment', action='store_true', help='augmented inference')
|
||||||
|
parser.add_argument('--update', action='store_true', help='update all models')
|
||||||
|
parser.add_argument('--project', default='runs/detect', help='save results to project/name')
|
||||||
|
parser.add_argument('--name', default='exp', help='save results to project/name')
|
||||||
|
parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
|
||||||
|
parser.add_argument('--labelnames', type=str,default=None, help='labes nams')
|
||||||
|
|
||||||
|
parser.add_argument('--fpsample', type=int, default=240, help='fpsample')
|
||||||
|
parser.add_argument('--OutVideoW', type=int, default=1920, help='out video width size')
|
||||||
|
parser.add_argument('--OutVideoH', type=int, default=1080, help='out video height size')
|
||||||
|
parser.add_argument('--OutVideoFps', type=int, default=30, help='out video fps ')
|
||||||
|
|
||||||
|
opt = parser.parse_args()
|
||||||
|
print(opt)
|
||||||
|
check_requirements(exclude=('pycocotools', 'thop'))
|
||||||
|
|
||||||
|
with torch.no_grad():
|
||||||
|
if opt.update: # update all models (to fix SourceChangeWarning)
|
||||||
|
for opt.weights in ['yolov5s.pt', 'yolov5m.pt', 'yolov5l.pt', 'yolov5x.pt']:
|
||||||
|
detect()
|
||||||
|
strip_optimizer(opt.weights)
|
||||||
|
else:
|
||||||
|
detect()
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
#OutVideoW=3840 OutVideoH=2160 OutVideoFps=30 source=/home/thsw2/WJ/data/video3840
|
||||||
|
#OutVideoW=1920 OutVideoH=1080 OutVideoFps=30 source=/home/thsw2/WJ/data/video1920
|
||||||
|
#OutVideoW=3840 OutVideoH=2160
|
||||||
|
OutVideoW=1920 OutVideoH=1080
|
||||||
|
OutVideoFps=30
|
||||||
|
#source='rtmp://demoplay.yunhengzhizao.cn/live/test' #address2
|
||||||
|
#source='rtmp://liveplay.yunhengzhizao.cn/live/demo' #address1
|
||||||
|
#source='rtmp://liveplay.yunhengzhizao.cn/live/demo_HD5M' #address1_HD5M
|
||||||
|
|
||||||
|
source=config/source.txt
|
||||||
|
#source='/home/thsw2/WJ/data/THexit/val/images/'
|
||||||
|
|
||||||
|
#model=runs/train/exp10/weights/best.pt
|
||||||
|
#model=weights/best_5classes.pt ###before 1228
|
||||||
|
#model=weights/best_5classes_1228.pt
|
||||||
|
model=weights/1230_last.pt
|
||||||
|
labelnames=config/labelnames.json
|
||||||
|
fpsample=720 confthres=0.4
|
||||||
|
|
||||||
|
#python detect_zhuanbo.py --weights ${model} --source ${source} --OutVideoW ${OutVideoW} --OutVideoH ${OutVideoH} --OutVideoFps ${OutVideoFps} --labelnames ${labelnames} --fpsample ${fpsample} --conf-thres ${confthres}
|
||||||
|
|
||||||
|
python detect.py --weights ${model} --source ${source} --OutVideoW ${OutVideoW} --OutVideoH ${OutVideoH} --OutVideoFps ${OutVideoFps} --labelnames ${labelnames} --fpsample ${fpsample} --conf-thres ${confthres} &
|
||||||
|
python Send_tranfer.py &
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
"""YOLOv5 PyTorch Hub models https://pytorch.org/hub/ultralytics_yolov5/
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
import torch
|
||||||
|
model = torch.hub.load('ultralytics/yolov5', 'yolov5s')
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import torch
|
||||||
|
|
||||||
|
from models.yolo import Model
|
||||||
|
from utils.general import check_requirements, set_logging
|
||||||
|
from utils.google_utils import attempt_download
|
||||||
|
from utils.torch_utils import select_device
|
||||||
|
|
||||||
|
dependencies = ['torch', 'yaml']
|
||||||
|
check_requirements(Path(__file__).parent / 'requirements.txt', exclude=('pycocotools', 'thop'))
|
||||||
|
set_logging()
|
||||||
|
|
||||||
|
|
||||||
|
def create(name, pretrained, channels, classes, autoshape):
|
||||||
|
"""Creates a specified YOLOv5 model
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
name (str): name of model, i.e. 'yolov5s'
|
||||||
|
pretrained (bool): load pretrained weights into the model
|
||||||
|
channels (int): number of input channels
|
||||||
|
classes (int): number of model classes
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
pytorch model
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cfg = list((Path(__file__).parent / 'models').rglob(f'{name}.yaml'))[0] # model.yaml path
|
||||||
|
model = Model(cfg, channels, classes)
|
||||||
|
if pretrained:
|
||||||
|
fname = f'{name}.pt' # checkpoint filename
|
||||||
|
attempt_download(fname) # download if not found locally
|
||||||
|
ckpt = torch.load(fname, map_location=torch.device('cpu')) # load
|
||||||
|
msd = model.state_dict() # model state_dict
|
||||||
|
csd = ckpt['model'].float().state_dict() # checkpoint state_dict as FP32
|
||||||
|
csd = {k: v for k, v in csd.items() if msd[k].shape == v.shape} # filter
|
||||||
|
model.load_state_dict(csd, strict=False) # load
|
||||||
|
if len(ckpt['model'].names) == classes:
|
||||||
|
model.names = ckpt['model'].names # set class names attribute
|
||||||
|
if autoshape:
|
||||||
|
model = model.autoshape() # for file/URI/PIL/cv2/np inputs and NMS
|
||||||
|
device = select_device('0' if torch.cuda.is_available() else 'cpu') # default to GPU if available
|
||||||
|
return model.to(device)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
help_url = 'https://github.com/ultralytics/yolov5/issues/36'
|
||||||
|
s = 'Cache maybe be out of date, try force_reload=True. See %s for help.' % help_url
|
||||||
|
raise Exception(s) from e
|
||||||
|
|
||||||
|
|
||||||
|
def custom(path_or_model='path/to/model.pt', autoshape=True):
|
||||||
|
"""YOLOv5-custom model https://github.com/ultralytics/yolov5
|
||||||
|
|
||||||
|
Arguments (3 options):
|
||||||
|
path_or_model (str): 'path/to/model.pt'
|
||||||
|
path_or_model (dict): torch.load('path/to/model.pt')
|
||||||
|
path_or_model (nn.Module): torch.load('path/to/model.pt')['model']
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
pytorch model
|
||||||
|
"""
|
||||||
|
model = torch.load(path_or_model) if isinstance(path_or_model, str) else path_or_model # load checkpoint
|
||||||
|
if isinstance(model, dict):
|
||||||
|
model = model['ema' if model.get('ema') else 'model'] # load model
|
||||||
|
|
||||||
|
hub_model = Model(model.yaml).to(next(model.parameters()).device) # create
|
||||||
|
hub_model.load_state_dict(model.float().state_dict()) # load state_dict
|
||||||
|
hub_model.names = model.names # class names
|
||||||
|
if autoshape:
|
||||||
|
hub_model = hub_model.autoshape() # for file/URI/PIL/cv2/np inputs and NMS
|
||||||
|
device = select_device('0' if torch.cuda.is_available() else 'cpu') # default to GPU if available
|
||||||
|
return hub_model.to(device)
|
||||||
|
|
||||||
|
|
||||||
|
def yolov5s(pretrained=True, channels=3, classes=80, autoshape=True):
|
||||||
|
# YOLOv5-small model https://github.com/ultralytics/yolov5
|
||||||
|
return create('yolov5s', pretrained, channels, classes, autoshape)
|
||||||
|
|
||||||
|
|
||||||
|
def yolov5m(pretrained=True, channels=3, classes=80, autoshape=True):
|
||||||
|
# YOLOv5-medium model https://github.com/ultralytics/yolov5
|
||||||
|
return create('yolov5m', pretrained, channels, classes, autoshape)
|
||||||
|
|
||||||
|
|
||||||
|
def yolov5l(pretrained=True, channels=3, classes=80, autoshape=True):
|
||||||
|
# YOLOv5-large model https://github.com/ultralytics/yolov5
|
||||||
|
return create('yolov5l', pretrained, channels, classes, autoshape)
|
||||||
|
|
||||||
|
|
||||||
|
def yolov5x(pretrained=True, channels=3, classes=80, autoshape=True):
|
||||||
|
# YOLOv5-xlarge model https://github.com/ultralytics/yolov5
|
||||||
|
return create('yolov5x', pretrained, channels, classes, autoshape)
|
||||||
|
|
||||||
|
|
||||||
|
def yolov5s6(pretrained=True, channels=3, classes=80, autoshape=True):
|
||||||
|
# YOLOv5-small model https://github.com/ultralytics/yolov5
|
||||||
|
return create('yolov5s6', pretrained, channels, classes, autoshape)
|
||||||
|
|
||||||
|
|
||||||
|
def yolov5m6(pretrained=True, channels=3, classes=80, autoshape=True):
|
||||||
|
# YOLOv5-medium model https://github.com/ultralytics/yolov5
|
||||||
|
return create('yolov5m6', pretrained, channels, classes, autoshape)
|
||||||
|
|
||||||
|
|
||||||
|
def yolov5l6(pretrained=True, channels=3, classes=80, autoshape=True):
|
||||||
|
# YOLOv5-large model https://github.com/ultralytics/yolov5
|
||||||
|
return create('yolov5l6', pretrained, channels, classes, autoshape)
|
||||||
|
|
||||||
|
|
||||||
|
def yolov5x6(pretrained=True, channels=3, classes=80, autoshape=True):
|
||||||
|
# YOLOv5-xlarge model https://github.com/ultralytics/yolov5
|
||||||
|
return create('yolov5x6', pretrained, channels, classes, autoshape)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
model = create(name='yolov5s', pretrained=True, channels=3, classes=80, autoshape=True) # pretrained example
|
||||||
|
# model = custom(path_or_model='path/to/model.pt') # custom example
|
||||||
|
|
||||||
|
# Verify inference
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
imgs = ['data/images/zidane.jpg', # filename
|
||||||
|
'https://github.com/ultralytics/yolov5/releases/download/v1.0/zidane.jpg', # URI
|
||||||
|
cv2.imread('data/images/bus.jpg')[:, :, ::-1], # OpenCV
|
||||||
|
Image.open('data/images/bus.jpg'), # PIL
|
||||||
|
np.zeros((320, 640, 3))] # numpy
|
||||||
|
|
||||||
|
results = model(imgs) # batched inference
|
||||||
|
results.print()
|
||||||
|
results.save()
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,386 @@
|
||||||
|
# YOLOv5 common modules
|
||||||
|
|
||||||
|
import math
|
||||||
|
from copy import copy
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import requests
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
from PIL import Image
|
||||||
|
from torch.cuda import amp
|
||||||
|
|
||||||
|
from utils.datasets import letterbox
|
||||||
|
from utils.general import non_max_suppression, make_divisible, scale_coords, increment_path, xyxy2xywh
|
||||||
|
from utils.plots import color_list, plot_one_box
|
||||||
|
from utils.torch_utils import time_synchronized
|
||||||
|
|
||||||
|
|
||||||
|
def autopad(k, p=None): # kernel, padding
|
||||||
|
# Pad to 'same'
|
||||||
|
if p is None:
|
||||||
|
p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad
|
||||||
|
return p
|
||||||
|
|
||||||
|
|
||||||
|
def DWConv(c1, c2, k=1, s=1, act=True):
|
||||||
|
# Depthwise convolution
|
||||||
|
return Conv(c1, c2, k, s, g=math.gcd(c1, c2), act=act)
|
||||||
|
|
||||||
|
|
||||||
|
class Conv(nn.Module):
|
||||||
|
# Standard convolution
|
||||||
|
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
|
||||||
|
super(Conv, self).__init__()
|
||||||
|
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
|
||||||
|
self.bn = nn.BatchNorm2d(c2)
|
||||||
|
self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return self.act(self.bn(self.conv(x)))
|
||||||
|
|
||||||
|
def fuseforward(self, x):
|
||||||
|
return self.act(self.conv(x))
|
||||||
|
|
||||||
|
|
||||||
|
class TransformerLayer(nn.Module):
|
||||||
|
# Transformer layer https://arxiv.org/abs/2010.11929 (LayerNorm layers removed for better performance)
|
||||||
|
def __init__(self, c, num_heads):
|
||||||
|
super().__init__()
|
||||||
|
self.q = nn.Linear(c, c, bias=False)
|
||||||
|
self.k = nn.Linear(c, c, bias=False)
|
||||||
|
self.v = nn.Linear(c, c, bias=False)
|
||||||
|
self.ma = nn.MultiheadAttention(embed_dim=c, num_heads=num_heads)
|
||||||
|
self.fc1 = nn.Linear(c, c, bias=False)
|
||||||
|
self.fc2 = nn.Linear(c, c, bias=False)
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
x = self.ma(self.q(x), self.k(x), self.v(x))[0] + x
|
||||||
|
x = self.fc2(self.fc1(x)) + x
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
class TransformerBlock(nn.Module):
|
||||||
|
# Vision Transformer https://arxiv.org/abs/2010.11929
|
||||||
|
def __init__(self, c1, c2, num_heads, num_layers):
|
||||||
|
super().__init__()
|
||||||
|
self.conv = None
|
||||||
|
if c1 != c2:
|
||||||
|
self.conv = Conv(c1, c2)
|
||||||
|
self.linear = nn.Linear(c2, c2) # learnable position embedding
|
||||||
|
self.tr = nn.Sequential(*[TransformerLayer(c2, num_heads) for _ in range(num_layers)])
|
||||||
|
self.c2 = c2
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
if self.conv is not None:
|
||||||
|
x = self.conv(x)
|
||||||
|
b, _, w, h = x.shape
|
||||||
|
p = x.flatten(2)
|
||||||
|
p = p.unsqueeze(0)
|
||||||
|
p = p.transpose(0, 3)
|
||||||
|
p = p.squeeze(3)
|
||||||
|
e = self.linear(p)
|
||||||
|
x = p + e
|
||||||
|
|
||||||
|
x = self.tr(x)
|
||||||
|
x = x.unsqueeze(3)
|
||||||
|
x = x.transpose(0, 3)
|
||||||
|
x = x.reshape(b, self.c2, w, h)
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
class Bottleneck(nn.Module):
|
||||||
|
# Standard bottleneck
|
||||||
|
def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion
|
||||||
|
super(Bottleneck, self).__init__()
|
||||||
|
c_ = int(c2 * e) # hidden channels
|
||||||
|
self.cv1 = Conv(c1, c_, 1, 1)
|
||||||
|
self.cv2 = Conv(c_, c2, 3, 1, g=g)
|
||||||
|
self.add = shortcut and c1 == c2
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
|
||||||
|
|
||||||
|
|
||||||
|
class BottleneckCSP(nn.Module):
|
||||||
|
# CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
|
||||||
|
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
|
||||||
|
super(BottleneckCSP, self).__init__()
|
||||||
|
c_ = int(c2 * e) # hidden channels
|
||||||
|
self.cv1 = Conv(c1, c_, 1, 1)
|
||||||
|
self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False)
|
||||||
|
self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False)
|
||||||
|
self.cv4 = Conv(2 * c_, c2, 1, 1)
|
||||||
|
self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3)
|
||||||
|
self.act = nn.LeakyReLU(0.1, inplace=True)
|
||||||
|
self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
y1 = self.cv3(self.m(self.cv1(x)))
|
||||||
|
y2 = self.cv2(x)
|
||||||
|
return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1))))
|
||||||
|
|
||||||
|
|
||||||
|
class C3(nn.Module):
|
||||||
|
# CSP Bottleneck with 3 convolutions
|
||||||
|
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
|
||||||
|
super(C3, self).__init__()
|
||||||
|
c_ = int(c2 * e) # hidden channels
|
||||||
|
self.cv1 = Conv(c1, c_, 1, 1)
|
||||||
|
self.cv2 = Conv(c1, c_, 1, 1)
|
||||||
|
self.cv3 = Conv(2 * c_, c2, 1) # act=FReLU(c2)
|
||||||
|
self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
|
||||||
|
# self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)])
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))
|
||||||
|
|
||||||
|
|
||||||
|
class C3TR(C3):
|
||||||
|
# C3 module with TransformerBlock()
|
||||||
|
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
|
||||||
|
super().__init__(c1, c2, n, shortcut, g, e)
|
||||||
|
c_ = int(c2 * e)
|
||||||
|
self.m = TransformerBlock(c_, c_, 4, n)
|
||||||
|
|
||||||
|
|
||||||
|
class SPP(nn.Module):
|
||||||
|
# Spatial pyramid pooling layer used in YOLOv3-SPP
|
||||||
|
def __init__(self, c1, c2, k=(5, 9, 13)):
|
||||||
|
super(SPP, self).__init__()
|
||||||
|
c_ = c1 // 2 # hidden channels
|
||||||
|
self.cv1 = Conv(c1, c_, 1, 1)
|
||||||
|
self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1)
|
||||||
|
self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
x = self.cv1(x)
|
||||||
|
return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))
|
||||||
|
|
||||||
|
|
||||||
|
class Focus(nn.Module):
|
||||||
|
# Focus wh information into c-space
|
||||||
|
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
|
||||||
|
super(Focus, self).__init__()
|
||||||
|
self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
|
||||||
|
# self.contract = Contract(gain=2)
|
||||||
|
|
||||||
|
def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2)
|
||||||
|
return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))
|
||||||
|
# return self.conv(self.contract(x))
|
||||||
|
|
||||||
|
|
||||||
|
class Contract(nn.Module):
|
||||||
|
# Contract width-height into channels, i.e. x(1,64,80,80) to x(1,256,40,40)
|
||||||
|
def __init__(self, gain=2):
|
||||||
|
super().__init__()
|
||||||
|
self.gain = gain
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
N, C, H, W = x.size() # assert (H / s == 0) and (W / s == 0), 'Indivisible gain'
|
||||||
|
s = self.gain
|
||||||
|
x = x.view(N, C, H // s, s, W // s, s) # x(1,64,40,2,40,2)
|
||||||
|
x = x.permute(0, 3, 5, 1, 2, 4).contiguous() # x(1,2,2,64,40,40)
|
||||||
|
return x.view(N, C * s * s, H // s, W // s) # x(1,256,40,40)
|
||||||
|
|
||||||
|
|
||||||
|
class Expand(nn.Module):
|
||||||
|
# Expand channels into width-height, i.e. x(1,64,80,80) to x(1,16,160,160)
|
||||||
|
def __init__(self, gain=2):
|
||||||
|
super().__init__()
|
||||||
|
self.gain = gain
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
N, C, H, W = x.size() # assert C / s ** 2 == 0, 'Indivisible gain'
|
||||||
|
s = self.gain
|
||||||
|
x = x.view(N, s, s, C // s ** 2, H, W) # x(1,2,2,16,80,80)
|
||||||
|
x = x.permute(0, 3, 4, 1, 5, 2).contiguous() # x(1,16,80,2,80,2)
|
||||||
|
return x.view(N, C // s ** 2, H * s, W * s) # x(1,16,160,160)
|
||||||
|
|
||||||
|
|
||||||
|
class Concat(nn.Module):
|
||||||
|
# Concatenate a list of tensors along dimension
|
||||||
|
def __init__(self, dimension=1):
|
||||||
|
super(Concat, self).__init__()
|
||||||
|
self.d = dimension
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return torch.cat(x, self.d)
|
||||||
|
|
||||||
|
|
||||||
|
class NMS(nn.Module):
|
||||||
|
# Non-Maximum Suppression (NMS) module
|
||||||
|
conf = 0.25 # confidence threshold
|
||||||
|
iou = 0.45 # IoU threshold
|
||||||
|
classes = None # (optional list) filter by class
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(NMS, self).__init__()
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return non_max_suppression(x[0], conf_thres=self.conf, iou_thres=self.iou, classes=self.classes)
|
||||||
|
|
||||||
|
|
||||||
|
class autoShape(nn.Module):
|
||||||
|
# input-robust model wrapper for passing cv2/np/PIL/torch inputs. Includes preprocessing, inference and NMS
|
||||||
|
conf = 0.25 # NMS confidence threshold
|
||||||
|
iou = 0.45 # NMS IoU threshold
|
||||||
|
classes = None # (optional list) filter by class
|
||||||
|
|
||||||
|
def __init__(self, model):
|
||||||
|
super(autoShape, self).__init__()
|
||||||
|
self.model = model.eval()
|
||||||
|
|
||||||
|
def autoshape(self):
|
||||||
|
print('autoShape already enabled, skipping... ') # model already converted to model.autoshape()
|
||||||
|
return self
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
|
def forward(self, imgs, size=640, augment=False, profile=False):
|
||||||
|
# Inference from various sources. For height=640, width=1280, RGB images example inputs are:
|
||||||
|
# filename: imgs = 'data/images/zidane.jpg'
|
||||||
|
# URI: = 'https://github.com/ultralytics/yolov5/releases/download/v1.0/zidane.jpg'
|
||||||
|
# OpenCV: = cv2.imread('image.jpg')[:,:,::-1] # HWC BGR to RGB x(640,1280,3)
|
||||||
|
# PIL: = Image.open('image.jpg') # HWC x(640,1280,3)
|
||||||
|
# numpy: = np.zeros((640,1280,3)) # HWC
|
||||||
|
# torch: = torch.zeros(16,3,320,640) # BCHW (scaled to size=640, 0-1 values)
|
||||||
|
# multiple: = [Image.open('image1.jpg'), Image.open('image2.jpg'), ...] # list of images
|
||||||
|
|
||||||
|
t = [time_synchronized()]
|
||||||
|
p = next(self.model.parameters()) # for device and type
|
||||||
|
if isinstance(imgs, torch.Tensor): # torch
|
||||||
|
with amp.autocast(enabled=p.device.type != 'cpu'):
|
||||||
|
return self.model(imgs.to(p.device).type_as(p), augment, profile) # inference
|
||||||
|
|
||||||
|
# Pre-process
|
||||||
|
n, imgs = (len(imgs), imgs) if isinstance(imgs, list) else (1, [imgs]) # number of images, list of images
|
||||||
|
shape0, shape1, files = [], [], [] # image and inference shapes, filenames
|
||||||
|
for i, im in enumerate(imgs):
|
||||||
|
f = f'image{i}' # filename
|
||||||
|
if isinstance(im, str): # filename or uri
|
||||||
|
im, f = np.asarray(Image.open(requests.get(im, stream=True).raw if im.startswith('http') else im)), im
|
||||||
|
elif isinstance(im, Image.Image): # PIL Image
|
||||||
|
im, f = np.asarray(im), getattr(im, 'filename', f) or f
|
||||||
|
files.append(Path(f).with_suffix('.jpg').name)
|
||||||
|
if im.shape[0] < 5: # image in CHW
|
||||||
|
im = im.transpose((1, 2, 0)) # reverse dataloader .transpose(2, 0, 1)
|
||||||
|
im = im[:, :, :3] if im.ndim == 3 else np.tile(im[:, :, None], 3) # enforce 3ch input
|
||||||
|
s = im.shape[:2] # HWC
|
||||||
|
shape0.append(s) # image shape
|
||||||
|
g = (size / max(s)) # gain
|
||||||
|
shape1.append([y * g for y in s])
|
||||||
|
imgs[i] = im if im.data.contiguous else np.ascontiguousarray(im) # update
|
||||||
|
shape1 = [make_divisible(x, int(self.stride.max())) for x in np.stack(shape1, 0).max(0)] # inference shape
|
||||||
|
x = [letterbox(im, new_shape=shape1, auto=False)[0] for im in imgs] # pad
|
||||||
|
x = np.stack(x, 0) if n > 1 else x[0][None] # stack
|
||||||
|
x = np.ascontiguousarray(x.transpose((0, 3, 1, 2))) # BHWC to BCHW
|
||||||
|
x = torch.from_numpy(x).to(p.device).type_as(p) / 255. # uint8 to fp16/32
|
||||||
|
t.append(time_synchronized())
|
||||||
|
|
||||||
|
with amp.autocast(enabled=p.device.type != 'cpu'):
|
||||||
|
# Inference
|
||||||
|
y = self.model(x, augment, profile)[0] # forward
|
||||||
|
t.append(time_synchronized())
|
||||||
|
|
||||||
|
# Post-process
|
||||||
|
y = non_max_suppression(y, conf_thres=self.conf, iou_thres=self.iou, classes=self.classes) # NMS
|
||||||
|
for i in range(n):
|
||||||
|
scale_coords(shape1, y[i][:, :4], shape0[i])
|
||||||
|
|
||||||
|
t.append(time_synchronized())
|
||||||
|
return Detections(imgs, y, files, t, self.names, x.shape)
|
||||||
|
|
||||||
|
|
||||||
|
class Detections:
|
||||||
|
# detections class for YOLOv5 inference results
|
||||||
|
def __init__(self, imgs, pred, files, times=None, names=None, shape=None):
|
||||||
|
super(Detections, self).__init__()
|
||||||
|
d = pred[0].device # device
|
||||||
|
gn = [torch.tensor([*[im.shape[i] for i in [1, 0, 1, 0]], 1., 1.], device=d) for im in imgs] # normalizations
|
||||||
|
self.imgs = imgs # list of images as numpy arrays
|
||||||
|
self.pred = pred # list of tensors pred[0] = (xyxy, conf, cls)
|
||||||
|
self.names = names # class names
|
||||||
|
self.files = files # image filenames
|
||||||
|
self.xyxy = pred # xyxy pixels
|
||||||
|
self.xywh = [xyxy2xywh(x) for x in pred] # xywh pixels
|
||||||
|
self.xyxyn = [x / g for x, g in zip(self.xyxy, gn)] # xyxy normalized
|
||||||
|
self.xywhn = [x / g for x, g in zip(self.xywh, gn)] # xywh normalized
|
||||||
|
self.n = len(self.pred) # number of images (batch size)
|
||||||
|
self.t = tuple((times[i + 1] - times[i]) * 1000 / self.n for i in range(3)) # timestamps (ms)
|
||||||
|
self.s = shape # inference BCHW shape
|
||||||
|
|
||||||
|
def display(self, pprint=False, show=False, save=False, render=False, save_dir=''):
|
||||||
|
colors = color_list()
|
||||||
|
for i, (img, pred) in enumerate(zip(self.imgs, self.pred)):
|
||||||
|
str = f'image {i + 1}/{len(self.pred)}: {img.shape[0]}x{img.shape[1]} '
|
||||||
|
if pred is not None:
|
||||||
|
for c in pred[:, -1].unique():
|
||||||
|
n = (pred[:, -1] == c).sum() # detections per class
|
||||||
|
str += f"{n} {self.names[int(c)]}{'s' * (n > 1)}, " # add to string
|
||||||
|
if show or save or render:
|
||||||
|
for *box, conf, cls in pred: # xyxy, confidence, class
|
||||||
|
label = f'{self.names[int(cls)]} {conf:.2f}'
|
||||||
|
plot_one_box(box, img, label=label, color=colors[int(cls) % 10])
|
||||||
|
img = Image.fromarray(img.astype(np.uint8)) if isinstance(img, np.ndarray) else img # from np
|
||||||
|
if pprint:
|
||||||
|
print(str.rstrip(', '))
|
||||||
|
if show:
|
||||||
|
img.show(self.files[i]) # show
|
||||||
|
if save:
|
||||||
|
f = self.files[i]
|
||||||
|
img.save(Path(save_dir) / f) # save
|
||||||
|
print(f"{'Saved' * (i == 0)} {f}", end=',' if i < self.n - 1 else f' to {save_dir}\n')
|
||||||
|
if render:
|
||||||
|
self.imgs[i] = np.asarray(img)
|
||||||
|
|
||||||
|
def print(self):
|
||||||
|
self.display(pprint=True) # print results
|
||||||
|
print(f'Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {tuple(self.s)}' % self.t)
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
self.display(show=True) # show results
|
||||||
|
|
||||||
|
def save(self, save_dir='runs/hub/exp'):
|
||||||
|
save_dir = increment_path(save_dir, exist_ok=save_dir != 'runs/hub/exp') # increment save_dir
|
||||||
|
Path(save_dir).mkdir(parents=True, exist_ok=True)
|
||||||
|
self.display(save=True, save_dir=save_dir) # save results
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
self.display(render=True) # render results
|
||||||
|
return self.imgs
|
||||||
|
|
||||||
|
def pandas(self):
|
||||||
|
# return detections as pandas DataFrames, i.e. print(results.pandas().xyxy[0])
|
||||||
|
new = copy(self) # return copy
|
||||||
|
ca = 'xmin', 'ymin', 'xmax', 'ymax', 'confidence', 'class', 'name' # xyxy columns
|
||||||
|
cb = 'xcenter', 'ycenter', 'width', 'height', 'confidence', 'class', 'name' # xywh columns
|
||||||
|
for k, c in zip(['xyxy', 'xyxyn', 'xywh', 'xywhn'], [ca, ca, cb, cb]):
|
||||||
|
a = [[x[:5] + [int(x[5]), self.names[int(x[5])]] for x in x.tolist()] for x in getattr(self, k)] # update
|
||||||
|
setattr(new, k, [pd.DataFrame(x, columns=c) for x in a])
|
||||||
|
return new
|
||||||
|
|
||||||
|
def tolist(self):
|
||||||
|
# return a list of Detections objects, i.e. 'for result in results.tolist():'
|
||||||
|
x = [Detections([self.imgs[i]], [self.pred[i]], self.names, self.s) for i in range(self.n)]
|
||||||
|
for d in x:
|
||||||
|
for k in ['imgs', 'pred', 'xyxy', 'xyxyn', 'xywh', 'xywhn']:
|
||||||
|
setattr(d, k, getattr(d, k)[0]) # pop out of list
|
||||||
|
return x
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self.n
|
||||||
|
|
||||||
|
|
||||||
|
class Classify(nn.Module):
|
||||||
|
# Classification head, i.e. x(b,c1,20,20) to x(b,c2)
|
||||||
|
def __init__(self, c1, c2, k=1, s=1, p=None, g=1): # ch_in, ch_out, kernel, stride, padding, groups
|
||||||
|
super(Classify, self).__init__()
|
||||||
|
self.aap = nn.AdaptiveAvgPool2d(1) # to x(b,c1,1,1)
|
||||||
|
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g) # to x(b,c2,1,1)
|
||||||
|
self.flat = nn.Flatten()
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
z = torch.cat([self.aap(y) for y in (x if isinstance(x, list) else [x])], 1) # cat if list
|
||||||
|
return self.flat(self.conv(z)) # flatten to x(b,c2)
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
# YOLOv5 experimental modules
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
|
||||||
|
from models.common import Conv, DWConv
|
||||||
|
from utils.google_utils import attempt_download
|
||||||
|
|
||||||
|
|
||||||
|
class CrossConv(nn.Module):
|
||||||
|
# Cross Convolution Downsample
|
||||||
|
def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False):
|
||||||
|
# ch_in, ch_out, kernel, stride, groups, expansion, shortcut
|
||||||
|
super(CrossConv, self).__init__()
|
||||||
|
c_ = int(c2 * e) # hidden channels
|
||||||
|
self.cv1 = Conv(c1, c_, (1, k), (1, s))
|
||||||
|
self.cv2 = Conv(c_, c2, (k, 1), (s, 1), g=g)
|
||||||
|
self.add = shortcut and c1 == c2
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
|
||||||
|
|
||||||
|
|
||||||
|
class Sum(nn.Module):
|
||||||
|
# Weighted sum of 2 or more layers https://arxiv.org/abs/1911.09070
|
||||||
|
def __init__(self, n, weight=False): # n: number of inputs
|
||||||
|
super(Sum, self).__init__()
|
||||||
|
self.weight = weight # apply weights boolean
|
||||||
|
self.iter = range(n - 1) # iter object
|
||||||
|
if weight:
|
||||||
|
self.w = nn.Parameter(-torch.arange(1., n) / 2, requires_grad=True) # layer weights
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
y = x[0] # no weight
|
||||||
|
if self.weight:
|
||||||
|
w = torch.sigmoid(self.w) * 2
|
||||||
|
for i in self.iter:
|
||||||
|
y = y + x[i + 1] * w[i]
|
||||||
|
else:
|
||||||
|
for i in self.iter:
|
||||||
|
y = y + x[i + 1]
|
||||||
|
return y
|
||||||
|
|
||||||
|
|
||||||
|
class GhostConv(nn.Module):
|
||||||
|
# Ghost Convolution https://github.com/huawei-noah/ghostnet
|
||||||
|
def __init__(self, c1, c2, k=1, s=1, g=1, act=True): # ch_in, ch_out, kernel, stride, groups
|
||||||
|
super(GhostConv, self).__init__()
|
||||||
|
c_ = c2 // 2 # hidden channels
|
||||||
|
self.cv1 = Conv(c1, c_, k, s, None, g, act)
|
||||||
|
self.cv2 = Conv(c_, c_, 5, 1, None, c_, act)
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
y = self.cv1(x)
|
||||||
|
return torch.cat([y, self.cv2(y)], 1)
|
||||||
|
|
||||||
|
|
||||||
|
class GhostBottleneck(nn.Module):
|
||||||
|
# Ghost Bottleneck https://github.com/huawei-noah/ghostnet
|
||||||
|
def __init__(self, c1, c2, k=3, s=1): # ch_in, ch_out, kernel, stride
|
||||||
|
super(GhostBottleneck, self).__init__()
|
||||||
|
c_ = c2 // 2
|
||||||
|
self.conv = nn.Sequential(GhostConv(c1, c_, 1, 1), # pw
|
||||||
|
DWConv(c_, c_, k, s, act=False) if s == 2 else nn.Identity(), # dw
|
||||||
|
GhostConv(c_, c2, 1, 1, act=False)) # pw-linear
|
||||||
|
self.shortcut = nn.Sequential(DWConv(c1, c1, k, s, act=False),
|
||||||
|
Conv(c1, c2, 1, 1, act=False)) if s == 2 else nn.Identity()
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return self.conv(x) + self.shortcut(x)
|
||||||
|
|
||||||
|
|
||||||
|
class MixConv2d(nn.Module):
|
||||||
|
# Mixed Depthwise Conv https://arxiv.org/abs/1907.09595
|
||||||
|
def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True):
|
||||||
|
super(MixConv2d, self).__init__()
|
||||||
|
groups = len(k)
|
||||||
|
if equal_ch: # equal c_ per group
|
||||||
|
i = torch.linspace(0, groups - 1E-6, c2).floor() # c2 indices
|
||||||
|
c_ = [(i == g).sum() for g in range(groups)] # intermediate channels
|
||||||
|
else: # equal weight.numel() per group
|
||||||
|
b = [c2] + [0] * groups
|
||||||
|
a = np.eye(groups + 1, groups, k=-1)
|
||||||
|
a -= np.roll(a, 1, axis=1)
|
||||||
|
a *= np.array(k) ** 2
|
||||||
|
a[0] = 1
|
||||||
|
c_ = np.linalg.lstsq(a, b, rcond=None)[0].round() # solve for equal weight indices, ax = b
|
||||||
|
|
||||||
|
self.m = nn.ModuleList([nn.Conv2d(c1, int(c_[g]), k[g], s, k[g] // 2, bias=False) for g in range(groups)])
|
||||||
|
self.bn = nn.BatchNorm2d(c2)
|
||||||
|
self.act = nn.LeakyReLU(0.1, inplace=True)
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return x + self.act(self.bn(torch.cat([m(x) for m in self.m], 1)))
|
||||||
|
|
||||||
|
|
||||||
|
class Ensemble(nn.ModuleList):
|
||||||
|
# Ensemble of models
|
||||||
|
def __init__(self):
|
||||||
|
super(Ensemble, self).__init__()
|
||||||
|
|
||||||
|
def forward(self, x, augment=False):
|
||||||
|
y = []
|
||||||
|
for module in self:
|
||||||
|
y.append(module(x, augment)[0])
|
||||||
|
# y = torch.stack(y).max(0)[0] # max ensemble
|
||||||
|
# y = torch.stack(y).mean(0) # mean ensemble
|
||||||
|
y = torch.cat(y, 1) # nms ensemble
|
||||||
|
return y, None # inference, train output
|
||||||
|
|
||||||
|
|
||||||
|
def attempt_load(weights, map_location=None):
|
||||||
|
# Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a
|
||||||
|
model = Ensemble()
|
||||||
|
for w in weights if isinstance(weights, list) else [weights]:
|
||||||
|
attempt_download(w)
|
||||||
|
ckpt = torch.load(w, map_location=map_location) # load
|
||||||
|
model.append(ckpt['ema' if ckpt.get('ema') else 'model'].float().fuse().eval()) # FP32 model
|
||||||
|
|
||||||
|
# Compatibility updates
|
||||||
|
for m in model.modules():
|
||||||
|
if type(m) in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU]:
|
||||||
|
m.inplace = True # pytorch 1.7.0 compatibility
|
||||||
|
elif type(m) is Conv:
|
||||||
|
m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility
|
||||||
|
|
||||||
|
if len(model) == 1:
|
||||||
|
return model[-1] # return model
|
||||||
|
else:
|
||||||
|
print('Ensemble created with %s\n' % weights)
|
||||||
|
for k in ['names', 'stride']:
|
||||||
|
setattr(model, k, getattr(model[-1], k))
|
||||||
|
return model # return ensemble
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
"""Exports a YOLOv5 *.pt model to ONNX and TorchScript formats
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
$ export PYTHONPATH="$PWD" && python models/export.py --weights yolov5s.pt --img 640 --batch 1
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
sys.path.append('./') # to run '$ python *.py' files in subdirectories
|
||||||
|
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
|
||||||
|
import models
|
||||||
|
from models.experimental import attempt_load
|
||||||
|
from utils.activations import Hardswish, SiLU
|
||||||
|
from utils.general import colorstr, check_img_size, check_requirements, set_logging
|
||||||
|
from utils.torch_utils import select_device
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--weights', type=str, default='./yolov5s.pt', help='weights path')
|
||||||
|
parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='image size') # height, width
|
||||||
|
parser.add_argument('--batch-size', type=int, default=1, help='batch size')
|
||||||
|
parser.add_argument('--grid', action='store_true', help='export Detect() layer grid')
|
||||||
|
parser.add_argument('--device', default='cpu', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
|
||||||
|
parser.add_argument('--dynamic', action='store_true', help='dynamic ONNX axes') # ONNX-only
|
||||||
|
parser.add_argument('--simplify', action='store_true', help='simplify ONNX model') # ONNX-only
|
||||||
|
opt = parser.parse_args()
|
||||||
|
opt.img_size *= 2 if len(opt.img_size) == 1 else 1 # expand
|
||||||
|
print(opt)
|
||||||
|
set_logging()
|
||||||
|
t = time.time()
|
||||||
|
|
||||||
|
# Load PyTorch model
|
||||||
|
device = select_device(opt.device)
|
||||||
|
model = attempt_load(opt.weights, map_location=device) # load FP32 model
|
||||||
|
labels = model.names
|
||||||
|
|
||||||
|
# Checks
|
||||||
|
gs = int(max(model.stride)) # grid size (max stride)
|
||||||
|
opt.img_size = [check_img_size(x, gs) for x in opt.img_size] # verify img_size are gs-multiples
|
||||||
|
|
||||||
|
# Input
|
||||||
|
img = torch.zeros(opt.batch_size, 3, *opt.img_size).to(device) # image size(1,3,320,192) iDetection
|
||||||
|
|
||||||
|
# Update model
|
||||||
|
for k, m in model.named_modules():
|
||||||
|
m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility
|
||||||
|
if isinstance(m, models.common.Conv): # assign export-friendly activations
|
||||||
|
if isinstance(m.act, nn.Hardswish):
|
||||||
|
m.act = Hardswish()
|
||||||
|
elif isinstance(m.act, nn.SiLU):
|
||||||
|
m.act = SiLU()
|
||||||
|
# elif isinstance(m, models.yolo.Detect):
|
||||||
|
# m.forward = m.forward_export # assign forward (optional)
|
||||||
|
model.model[-1].export = not opt.grid # set Detect() layer grid export
|
||||||
|
y = model(img) # dry run
|
||||||
|
|
||||||
|
# TorchScript export -----------------------------------------------------------------------------------------------
|
||||||
|
prefix = colorstr('TorchScript:')
|
||||||
|
try:
|
||||||
|
print(f'\n{prefix} starting export with torch {torch.__version__}...')
|
||||||
|
f = opt.weights.replace('.pt', '.torchscript.pt') # filename
|
||||||
|
ts = torch.jit.trace(model, img, strict=False)
|
||||||
|
ts.save(f)
|
||||||
|
print(f'{prefix} export success, saved as {f}')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'{prefix} export failure: {e}')
|
||||||
|
|
||||||
|
# ONNX export ------------------------------------------------------------------------------------------------------
|
||||||
|
prefix = colorstr('ONNX:')
|
||||||
|
try:
|
||||||
|
import onnx
|
||||||
|
|
||||||
|
print(f'{prefix} starting export with onnx {onnx.__version__}...')
|
||||||
|
f = opt.weights.replace('.pt', '.onnx') # filename
|
||||||
|
torch.onnx.export(model, img, f, verbose=False, opset_version=12, input_names=['images'],
|
||||||
|
output_names=['classes', 'boxes'] if y is None else ['output'],
|
||||||
|
dynamic_axes={'images': {0: 'batch', 2: 'height', 3: 'width'}, # size(1,3,640,640)
|
||||||
|
'output': {0: 'batch', 2: 'y', 3: 'x'}} if opt.dynamic else None)
|
||||||
|
|
||||||
|
# Checks
|
||||||
|
model_onnx = onnx.load(f) # load onnx model
|
||||||
|
onnx.checker.check_model(model_onnx) # check onnx model
|
||||||
|
# print(onnx.helper.printable_graph(model_onnx.graph)) # print
|
||||||
|
|
||||||
|
# Simplify
|
||||||
|
if opt.simplify:
|
||||||
|
try:
|
||||||
|
check_requirements(['onnx-simplifier'])
|
||||||
|
import onnxsim
|
||||||
|
|
||||||
|
print(f'{prefix} simplifying with onnx-simplifier {onnxsim.__version__}...')
|
||||||
|
model_onnx, check = onnxsim.simplify(model_onnx,
|
||||||
|
dynamic_input_shape=opt.dynamic,
|
||||||
|
input_shapes={'images': list(img.shape)} if opt.dynamic else None)
|
||||||
|
assert check, 'assert check failed'
|
||||||
|
onnx.save(model_onnx, f)
|
||||||
|
except Exception as e:
|
||||||
|
print(f'{prefix} simplifier failure: {e}')
|
||||||
|
print(f'{prefix} export success, saved as {f}')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'{prefix} export failure: {e}')
|
||||||
|
|
||||||
|
# CoreML export ----------------------------------------------------------------------------------------------------
|
||||||
|
prefix = colorstr('CoreML:')
|
||||||
|
try:
|
||||||
|
import coremltools as ct
|
||||||
|
|
||||||
|
print(f'{prefix} starting export with coremltools {onnx.__version__}...')
|
||||||
|
# convert model from torchscript and apply pixel scaling as per detect.py
|
||||||
|
model = ct.convert(ts, inputs=[ct.ImageType(name='image', shape=img.shape, scale=1 / 255.0, bias=[0, 0, 0])])
|
||||||
|
f = opt.weights.replace('.pt', '.mlmodel') # filename
|
||||||
|
model.save(f)
|
||||||
|
print(f'{prefix} export success, saved as {f}')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'{prefix} export failure: {e}')
|
||||||
|
|
||||||
|
# Finish
|
||||||
|
print(f'\nExport complete ({time.time() - t:.2f}s). Visualize with https://github.com/lutzroeder/netron.')
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
# Default YOLOv5 anchors for COCO data
|
||||||
|
|
||||||
|
|
||||||
|
# P5 -------------------------------------------------------------------------------------------------------------------
|
||||||
|
# P5-640:
|
||||||
|
anchors_p5_640:
|
||||||
|
- [ 10,13, 16,30, 33,23 ] # P3/8
|
||||||
|
- [ 30,61, 62,45, 59,119 ] # P4/16
|
||||||
|
- [ 116,90, 156,198, 373,326 ] # P5/32
|
||||||
|
|
||||||
|
|
||||||
|
# P6 -------------------------------------------------------------------------------------------------------------------
|
||||||
|
# P6-640: thr=0.25: 0.9964 BPR, 5.54 anchors past thr, n=12, img_size=640, metric_all=0.281/0.716-mean/best, past_thr=0.469-mean: 9,11, 21,19, 17,41, 43,32, 39,70, 86,64, 65,131, 134,130, 120,265, 282,180, 247,354, 512,387
|
||||||
|
anchors_p6_640:
|
||||||
|
- [ 9,11, 21,19, 17,41 ] # P3/8
|
||||||
|
- [ 43,32, 39,70, 86,64 ] # P4/16
|
||||||
|
- [ 65,131, 134,130, 120,265 ] # P5/32
|
||||||
|
- [ 282,180, 247,354, 512,387 ] # P6/64
|
||||||
|
|
||||||
|
# P6-1280: thr=0.25: 0.9950 BPR, 5.55 anchors past thr, n=12, img_size=1280, metric_all=0.281/0.714-mean/best, past_thr=0.468-mean: 19,27, 44,40, 38,94, 96,68, 86,152, 180,137, 140,301, 303,264, 238,542, 436,615, 739,380, 925,792
|
||||||
|
anchors_p6_1280:
|
||||||
|
- [ 19,27, 44,40, 38,94 ] # P3/8
|
||||||
|
- [ 96,68, 86,152, 180,137 ] # P4/16
|
||||||
|
- [ 140,301, 303,264, 238,542 ] # P5/32
|
||||||
|
- [ 436,615, 739,380, 925,792 ] # P6/64
|
||||||
|
|
||||||
|
# P6-1920: thr=0.25: 0.9950 BPR, 5.55 anchors past thr, n=12, img_size=1920, metric_all=0.281/0.714-mean/best, past_thr=0.468-mean: 28,41, 67,59, 57,141, 144,103, 129,227, 270,205, 209,452, 455,396, 358,812, 653,922, 1109,570, 1387,1187
|
||||||
|
anchors_p6_1920:
|
||||||
|
- [ 28,41, 67,59, 57,141 ] # P3/8
|
||||||
|
- [ 144,103, 129,227, 270,205 ] # P4/16
|
||||||
|
- [ 209,452, 455,396, 358,812 ] # P5/32
|
||||||
|
- [ 653,922, 1109,570, 1387,1187 ] # P6/64
|
||||||
|
|
||||||
|
|
||||||
|
# P7 -------------------------------------------------------------------------------------------------------------------
|
||||||
|
# P7-640: thr=0.25: 0.9962 BPR, 6.76 anchors past thr, n=15, img_size=640, metric_all=0.275/0.733-mean/best, past_thr=0.466-mean: 11,11, 13,30, 29,20, 30,46, 61,38, 39,92, 78,80, 146,66, 79,163, 149,150, 321,143, 157,303, 257,402, 359,290, 524,372
|
||||||
|
anchors_p7_640:
|
||||||
|
- [ 11,11, 13,30, 29,20 ] # P3/8
|
||||||
|
- [ 30,46, 61,38, 39,92 ] # P4/16
|
||||||
|
- [ 78,80, 146,66, 79,163 ] # P5/32
|
||||||
|
- [ 149,150, 321,143, 157,303 ] # P6/64
|
||||||
|
- [ 257,402, 359,290, 524,372 ] # P7/128
|
||||||
|
|
||||||
|
# P7-1280: thr=0.25: 0.9968 BPR, 6.71 anchors past thr, n=15, img_size=1280, metric_all=0.273/0.732-mean/best, past_thr=0.463-mean: 19,22, 54,36, 32,77, 70,83, 138,71, 75,173, 165,159, 148,334, 375,151, 334,317, 251,626, 499,474, 750,326, 534,814, 1079,818
|
||||||
|
anchors_p7_1280:
|
||||||
|
- [ 19,22, 54,36, 32,77 ] # P3/8
|
||||||
|
- [ 70,83, 138,71, 75,173 ] # P4/16
|
||||||
|
- [ 165,159, 148,334, 375,151 ] # P5/32
|
||||||
|
- [ 334,317, 251,626, 499,474 ] # P6/64
|
||||||
|
- [ 750,326, 534,814, 1079,818 ] # P7/128
|
||||||
|
|
||||||
|
# P7-1920: thr=0.25: 0.9968 BPR, 6.71 anchors past thr, n=15, img_size=1920, metric_all=0.273/0.732-mean/best, past_thr=0.463-mean: 29,34, 81,55, 47,115, 105,124, 207,107, 113,259, 247,238, 222,500, 563,227, 501,476, 376,939, 749,711, 1126,489, 801,1222, 1618,1227
|
||||||
|
anchors_p7_1920:
|
||||||
|
- [ 29,34, 81,55, 47,115 ] # P3/8
|
||||||
|
- [ 105,124, 207,107, 113,259 ] # P4/16
|
||||||
|
- [ 247,238, 222,500, 563,227 ] # P5/32
|
||||||
|
- [ 501,476, 376,939, 749,711 ] # P6/64
|
||||||
|
- [ 1126,489, 801,1222, 1618,1227 ] # P7/128
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 1.0 # model depth multiple
|
||||||
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
# darknet53 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Conv, [32, 3, 1]], # 0
|
||||||
|
[-1, 1, Conv, [64, 3, 2]], # 1-P1/2
|
||||||
|
[-1, 1, Bottleneck, [64]],
|
||||||
|
[-1, 1, Conv, [128, 3, 2]], # 3-P2/4
|
||||||
|
[-1, 2, Bottleneck, [128]],
|
||||||
|
[-1, 1, Conv, [256, 3, 2]], # 5-P3/8
|
||||||
|
[-1, 8, Bottleneck, [256]],
|
||||||
|
[-1, 1, Conv, [512, 3, 2]], # 7-P4/16
|
||||||
|
[-1, 8, Bottleneck, [512]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 2]], # 9-P5/32
|
||||||
|
[-1, 4, Bottleneck, [1024]], # 10
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv3-SPP head
|
||||||
|
head:
|
||||||
|
[[-1, 1, Bottleneck, [1024, False]],
|
||||||
|
[-1, 1, SPP, [512, [5, 9, 13]]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 1]],
|
||||||
|
[-1, 1, Conv, [512, 1, 1]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 1]], # 15 (P5/32-large)
|
||||||
|
|
||||||
|
[-2, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 8], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 1, Bottleneck, [512, False]],
|
||||||
|
[-1, 1, Bottleneck, [512, False]],
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, Conv, [512, 3, 1]], # 22 (P4/16-medium)
|
||||||
|
|
||||||
|
[-2, 1, Conv, [128, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 1, Bottleneck, [256, False]],
|
||||||
|
[-1, 2, Bottleneck, [256, False]], # 27 (P3/8-small)
|
||||||
|
|
||||||
|
[[27, 22, 15], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 1.0 # model depth multiple
|
||||||
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [10,14, 23,27, 37,58] # P4/16
|
||||||
|
- [81,82, 135,169, 344,319] # P5/32
|
||||||
|
|
||||||
|
# YOLOv3-tiny backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Conv, [16, 3, 1]], # 0
|
||||||
|
[-1, 1, nn.MaxPool2d, [2, 2, 0]], # 1-P1/2
|
||||||
|
[-1, 1, Conv, [32, 3, 1]],
|
||||||
|
[-1, 1, nn.MaxPool2d, [2, 2, 0]], # 3-P2/4
|
||||||
|
[-1, 1, Conv, [64, 3, 1]],
|
||||||
|
[-1, 1, nn.MaxPool2d, [2, 2, 0]], # 5-P3/8
|
||||||
|
[-1, 1, Conv, [128, 3, 1]],
|
||||||
|
[-1, 1, nn.MaxPool2d, [2, 2, 0]], # 7-P4/16
|
||||||
|
[-1, 1, Conv, [256, 3, 1]],
|
||||||
|
[-1, 1, nn.MaxPool2d, [2, 2, 0]], # 9-P5/32
|
||||||
|
[-1, 1, Conv, [512, 3, 1]],
|
||||||
|
[-1, 1, nn.ZeroPad2d, [[0, 1, 0, 1]]], # 11
|
||||||
|
[-1, 1, nn.MaxPool2d, [2, 1, 0]], # 12
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv3-tiny head
|
||||||
|
head:
|
||||||
|
[[-1, 1, Conv, [1024, 3, 1]],
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, Conv, [512, 3, 1]], # 15 (P5/32-large)
|
||||||
|
|
||||||
|
[-2, 1, Conv, [128, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 8], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 1, Conv, [256, 3, 1]], # 19 (P4/16-medium)
|
||||||
|
|
||||||
|
[[19, 15], 1, Detect, [nc, anchors]], # Detect(P4, P5)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 1.0 # model depth multiple
|
||||||
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
# darknet53 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Conv, [32, 3, 1]], # 0
|
||||||
|
[-1, 1, Conv, [64, 3, 2]], # 1-P1/2
|
||||||
|
[-1, 1, Bottleneck, [64]],
|
||||||
|
[-1, 1, Conv, [128, 3, 2]], # 3-P2/4
|
||||||
|
[-1, 2, Bottleneck, [128]],
|
||||||
|
[-1, 1, Conv, [256, 3, 2]], # 5-P3/8
|
||||||
|
[-1, 8, Bottleneck, [256]],
|
||||||
|
[-1, 1, Conv, [512, 3, 2]], # 7-P4/16
|
||||||
|
[-1, 8, Bottleneck, [512]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 2]], # 9-P5/32
|
||||||
|
[-1, 4, Bottleneck, [1024]], # 10
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv3 head
|
||||||
|
head:
|
||||||
|
[[-1, 1, Bottleneck, [1024, False]],
|
||||||
|
[-1, 1, Conv, [512, [1, 1]]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 1]],
|
||||||
|
[-1, 1, Conv, [512, 1, 1]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 1]], # 15 (P5/32-large)
|
||||||
|
|
||||||
|
[-2, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 8], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 1, Bottleneck, [512, False]],
|
||||||
|
[-1, 1, Bottleneck, [512, False]],
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, Conv, [512, 3, 1]], # 22 (P4/16-medium)
|
||||||
|
|
||||||
|
[-2, 1, Conv, [128, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 1, Bottleneck, [256, False]],
|
||||||
|
[-1, 2, Bottleneck, [256, False]], # 27 (P3/8-small)
|
||||||
|
|
||||||
|
[[27, 22, 15], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 1.0 # model depth multiple
|
||||||
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
||||||
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
|
[-1, 3, Bottleneck, [128]],
|
||||||
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
|
[-1, 9, BottleneckCSP, [256]],
|
||||||
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
|
[-1, 9, BottleneckCSP, [512]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
|
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
||||||
|
[-1, 6, BottleneckCSP, [1024]], # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 FPN head
|
||||||
|
head:
|
||||||
|
[[-1, 3, BottleneckCSP, [1024, False]], # 10 (P5/32-large)
|
||||||
|
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 1, Conv, [512, 1, 1]],
|
||||||
|
[-1, 3, BottleneckCSP, [512, False]], # 14 (P4/16-medium)
|
||||||
|
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 3, BottleneckCSP, [256, False]], # 18 (P3/8-small)
|
||||||
|
|
||||||
|
[[18, 14, 10], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 1.0 # model depth multiple
|
||||||
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors: 3
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
||||||
|
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
||||||
|
[ -1, 3, C3, [ 128 ] ],
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
||||||
|
[ -1, 9, C3, [ 256 ] ],
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
||||||
|
[ -1, 9, C3, [ 512 ] ],
|
||||||
|
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 7-P5/32
|
||||||
|
[ -1, 1, SPP, [ 1024, [ 5, 9, 13 ] ] ],
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 head
|
||||||
|
head:
|
||||||
|
[ [ -1, 1, Conv, [ 512, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 13
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
||||||
|
[ -1, 3, C3, [ 256, False ] ], # 17 (P3/8-small)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 128, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 2 ], 1, Concat, [ 1 ] ], # cat backbone P2
|
||||||
|
[ -1, 1, C3, [ 128, False ] ], # 21 (P2/4-xsmall)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 128, 3, 2 ] ],
|
||||||
|
[ [ -1, 18 ], 1, Concat, [ 1 ] ], # cat head P3
|
||||||
|
[ -1, 3, C3, [ 256, False ] ], # 24 (P3/8-small)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
||||||
|
[ [ -1, 14 ], 1, Concat, [ 1 ] ], # cat head P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 27 (P4/16-medium)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
||||||
|
[ [ -1, 10 ], 1, Concat, [ 1 ] ], # cat head P5
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 30 (P5/32-large)
|
||||||
|
|
||||||
|
[ [ 24, 27, 30 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 1.0 # model depth multiple
|
||||||
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors: 3
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
||||||
|
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
||||||
|
[ -1, 3, C3, [ 128 ] ],
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
||||||
|
[ -1, 9, C3, [ 256 ] ],
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
||||||
|
[ -1, 9, C3, [ 512 ] ],
|
||||||
|
[ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32
|
||||||
|
[ -1, 3, C3, [ 768 ] ],
|
||||||
|
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64
|
||||||
|
[ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ],
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 11
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 head
|
||||||
|
head:
|
||||||
|
[ [ -1, 1, Conv, [ 768, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5
|
||||||
|
[ -1, 3, C3, [ 768, False ] ], # 15
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 512, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 19
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
||||||
|
[ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
||||||
|
[ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
||||||
|
[ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5
|
||||||
|
[ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 768, 3, 2 ] ],
|
||||||
|
[ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 32 (P5/64-xlarge)
|
||||||
|
|
||||||
|
[ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 1.0 # model depth multiple
|
||||||
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors: 3
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
||||||
|
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
||||||
|
[ -1, 3, C3, [ 128 ] ],
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
||||||
|
[ -1, 9, C3, [ 256 ] ],
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
||||||
|
[ -1, 9, C3, [ 512 ] ],
|
||||||
|
[ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32
|
||||||
|
[ -1, 3, C3, [ 768 ] ],
|
||||||
|
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64
|
||||||
|
[ -1, 3, C3, [ 1024 ] ],
|
||||||
|
[ -1, 1, Conv, [ 1280, 3, 2 ] ], # 11-P7/128
|
||||||
|
[ -1, 1, SPP, [ 1280, [ 3, 5 ] ] ],
|
||||||
|
[ -1, 3, C3, [ 1280, False ] ], # 13
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 head
|
||||||
|
head:
|
||||||
|
[ [ -1, 1, Conv, [ 1024, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 10 ], 1, Concat, [ 1 ] ], # cat backbone P6
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 17
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 768, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5
|
||||||
|
[ -1, 3, C3, [ 768, False ] ], # 21
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 512, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 25
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
||||||
|
[ -1, 3, C3, [ 256, False ] ], # 29 (P3/8-small)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
||||||
|
[ [ -1, 26 ], 1, Concat, [ 1 ] ], # cat head P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 32 (P4/16-medium)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
||||||
|
[ [ -1, 22 ], 1, Concat, [ 1 ] ], # cat head P5
|
||||||
|
[ -1, 3, C3, [ 768, False ] ], # 35 (P5/32-large)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 768, 3, 2 ] ],
|
||||||
|
[ [ -1, 18 ], 1, Concat, [ 1 ] ], # cat head P6
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 38 (P6/64-xlarge)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 1024, 3, 2 ] ],
|
||||||
|
[ [ -1, 14 ], 1, Concat, [ 1 ] ], # cat head P7
|
||||||
|
[ -1, 3, C3, [ 1280, False ] ], # 41 (P7/128-xxlarge)
|
||||||
|
|
||||||
|
[ [ 29, 32, 35, 38, 41 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6, P7)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 1.0 # model depth multiple
|
||||||
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
||||||
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
|
[-1, 3, BottleneckCSP, [128]],
|
||||||
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
|
[-1, 9, BottleneckCSP, [256]],
|
||||||
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
|
[-1, 9, BottleneckCSP, [512]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
|
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
||||||
|
[-1, 3, BottleneckCSP, [1024, False]], # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 PANet head
|
||||||
|
head:
|
||||||
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 3, BottleneckCSP, [512, False]], # 13
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 3, BottleneckCSP, [256, False]], # 17 (P3/8-small)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
|
[[-1, 14], 1, Concat, [1]], # cat head P4
|
||||||
|
[-1, 3, BottleneckCSP, [512, False]], # 20 (P4/16-medium)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
|
[[-1, 10], 1, Concat, [1]], # cat head P5
|
||||||
|
[-1, 3, BottleneckCSP, [1024, False]], # 23 (P5/32-large)
|
||||||
|
|
||||||
|
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 1.0 # model depth multiple
|
||||||
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [ 19,27, 44,40, 38,94 ] # P3/8
|
||||||
|
- [ 96,68, 86,152, 180,137 ] # P4/16
|
||||||
|
- [ 140,301, 303,264, 238,542 ] # P5/32
|
||||||
|
- [ 436,615, 739,380, 925,792 ] # P6/64
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
||||||
|
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
||||||
|
[ -1, 3, C3, [ 128 ] ],
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
||||||
|
[ -1, 9, C3, [ 256 ] ],
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
||||||
|
[ -1, 9, C3, [ 512 ] ],
|
||||||
|
[ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32
|
||||||
|
[ -1, 3, C3, [ 768 ] ],
|
||||||
|
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64
|
||||||
|
[ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ],
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 11
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 head
|
||||||
|
head:
|
||||||
|
[ [ -1, 1, Conv, [ 768, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5
|
||||||
|
[ -1, 3, C3, [ 768, False ] ], # 15
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 512, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 19
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
||||||
|
[ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
||||||
|
[ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
||||||
|
[ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5
|
||||||
|
[ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 768, 3, 2 ] ],
|
||||||
|
[ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 32 (P6/64-xlarge)
|
||||||
|
|
||||||
|
[ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 0.67 # model depth multiple
|
||||||
|
width_multiple: 0.75 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [ 19,27, 44,40, 38,94 ] # P3/8
|
||||||
|
- [ 96,68, 86,152, 180,137 ] # P4/16
|
||||||
|
- [ 140,301, 303,264, 238,542 ] # P5/32
|
||||||
|
- [ 436,615, 739,380, 925,792 ] # P6/64
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
||||||
|
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
||||||
|
[ -1, 3, C3, [ 128 ] ],
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
||||||
|
[ -1, 9, C3, [ 256 ] ],
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
||||||
|
[ -1, 9, C3, [ 512 ] ],
|
||||||
|
[ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32
|
||||||
|
[ -1, 3, C3, [ 768 ] ],
|
||||||
|
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64
|
||||||
|
[ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ],
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 11
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 head
|
||||||
|
head:
|
||||||
|
[ [ -1, 1, Conv, [ 768, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5
|
||||||
|
[ -1, 3, C3, [ 768, False ] ], # 15
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 512, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 19
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
||||||
|
[ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
||||||
|
[ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
||||||
|
[ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5
|
||||||
|
[ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 768, 3, 2 ] ],
|
||||||
|
[ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 32 (P6/64-xlarge)
|
||||||
|
|
||||||
|
[ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 0.33 # model depth multiple
|
||||||
|
width_multiple: 0.50 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
||||||
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
|
[-1, 3, C3, [128]],
|
||||||
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
|
[-1, 9, C3, [256]],
|
||||||
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
|
[-1, 9, C3, [512]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
|
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
||||||
|
[-1, 3, C3TR, [1024, False]], # 9 <-------- C3TR() Transformer module
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 head
|
||||||
|
head:
|
||||||
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 3, C3, [512, False]], # 13
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 3, C3, [256, False]], # 17 (P3/8-small)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
|
[[-1, 14], 1, Concat, [1]], # cat head P4
|
||||||
|
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
|
[[-1, 10], 1, Concat, [1]], # cat head P5
|
||||||
|
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
|
||||||
|
|
||||||
|
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 0.33 # model depth multiple
|
||||||
|
width_multiple: 0.50 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [ 19,27, 44,40, 38,94 ] # P3/8
|
||||||
|
- [ 96,68, 86,152, 180,137 ] # P4/16
|
||||||
|
- [ 140,301, 303,264, 238,542 ] # P5/32
|
||||||
|
- [ 436,615, 739,380, 925,792 ] # P6/64
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
||||||
|
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
||||||
|
[ -1, 3, C3, [ 128 ] ],
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
||||||
|
[ -1, 9, C3, [ 256 ] ],
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
||||||
|
[ -1, 9, C3, [ 512 ] ],
|
||||||
|
[ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32
|
||||||
|
[ -1, 3, C3, [ 768 ] ],
|
||||||
|
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64
|
||||||
|
[ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ],
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 11
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 head
|
||||||
|
head:
|
||||||
|
[ [ -1, 1, Conv, [ 768, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5
|
||||||
|
[ -1, 3, C3, [ 768, False ] ], # 15
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 512, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 19
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
||||||
|
[ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
||||||
|
[ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
||||||
|
[ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5
|
||||||
|
[ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 768, 3, 2 ] ],
|
||||||
|
[ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 32 (P6/64-xlarge)
|
||||||
|
|
||||||
|
[ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 1.33 # model depth multiple
|
||||||
|
width_multiple: 1.25 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [ 19,27, 44,40, 38,94 ] # P3/8
|
||||||
|
- [ 96,68, 86,152, 180,137 ] # P4/16
|
||||||
|
- [ 140,301, 303,264, 238,542 ] # P5/32
|
||||||
|
- [ 436,615, 739,380, 925,792 ] # P6/64
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
||||||
|
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
||||||
|
[ -1, 3, C3, [ 128 ] ],
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
||||||
|
[ -1, 9, C3, [ 256 ] ],
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
||||||
|
[ -1, 9, C3, [ 512 ] ],
|
||||||
|
[ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32
|
||||||
|
[ -1, 3, C3, [ 768 ] ],
|
||||||
|
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64
|
||||||
|
[ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ],
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 11
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 head
|
||||||
|
head:
|
||||||
|
[ [ -1, 1, Conv, [ 768, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5
|
||||||
|
[ -1, 3, C3, [ 768, False ] ], # 15
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 512, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 19
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
||||||
|
[ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
||||||
|
[ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
||||||
|
[ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5
|
||||||
|
[ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 768, 3, 2 ] ],
|
||||||
|
[ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6
|
||||||
|
[ -1, 3, C3, [ 1024, False ] ], # 32 (P6/64-xlarge)
|
||||||
|
|
||||||
|
[ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,277 @@
|
||||||
|
# YOLOv5 YOLO-specific modules
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
sys.path.append('./') # to run '$ python *.py' files in subdirectories
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
from models.common import *
|
||||||
|
from models.experimental import *
|
||||||
|
from utils.autoanchor import check_anchor_order
|
||||||
|
from utils.general import make_divisible, check_file, set_logging
|
||||||
|
from utils.torch_utils import time_synchronized, fuse_conv_and_bn, model_info, scale_img, initialize_weights, \
|
||||||
|
select_device, copy_attr
|
||||||
|
|
||||||
|
try:
|
||||||
|
import thop # for FLOPS computation
|
||||||
|
except ImportError:
|
||||||
|
thop = None
|
||||||
|
|
||||||
|
|
||||||
|
class Detect(nn.Module):
|
||||||
|
stride = None # strides computed during build
|
||||||
|
export = False # onnx export
|
||||||
|
|
||||||
|
def __init__(self, nc=80, anchors=(), ch=()): # detection layer
|
||||||
|
super(Detect, self).__init__()
|
||||||
|
self.nc = nc # number of classes
|
||||||
|
self.no = nc + 5 # number of outputs per anchor
|
||||||
|
self.nl = len(anchors) # number of detection layers
|
||||||
|
self.na = len(anchors[0]) // 2 # number of anchors
|
||||||
|
self.grid = [torch.zeros(1)] * self.nl # init grid
|
||||||
|
a = torch.tensor(anchors).float().view(self.nl, -1, 2)
|
||||||
|
self.register_buffer('anchors', a) # shape(nl,na,2)
|
||||||
|
self.register_buffer('anchor_grid', a.clone().view(self.nl, 1, -1, 1, 1, 2)) # shape(nl,1,na,1,1,2)
|
||||||
|
self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
# x = x.copy() # for profiling
|
||||||
|
z = [] # inference output
|
||||||
|
self.training |= self.export
|
||||||
|
for i in range(self.nl):
|
||||||
|
x[i] = self.m[i](x[i]) # conv
|
||||||
|
bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85)
|
||||||
|
x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
|
||||||
|
|
||||||
|
if not self.training: # inference
|
||||||
|
if self.grid[i].shape[2:4] != x[i].shape[2:4]:
|
||||||
|
self.grid[i] = self._make_grid(nx, ny).to(x[i].device)
|
||||||
|
|
||||||
|
y = x[i].sigmoid()
|
||||||
|
y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy
|
||||||
|
y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
|
||||||
|
z.append(y.view(bs, -1, self.no))
|
||||||
|
|
||||||
|
return x if self.training else (torch.cat(z, 1), x)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _make_grid(nx=20, ny=20):
|
||||||
|
yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)])
|
||||||
|
return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float()
|
||||||
|
|
||||||
|
|
||||||
|
class Model(nn.Module):
|
||||||
|
def __init__(self, cfg='yolov5s.yaml', ch=3, nc=None, anchors=None): # model, input channels, number of classes
|
||||||
|
super(Model, self).__init__()
|
||||||
|
if isinstance(cfg, dict):
|
||||||
|
self.yaml = cfg # model dict
|
||||||
|
else: # is *.yaml
|
||||||
|
import yaml # for torch hub
|
||||||
|
self.yaml_file = Path(cfg).name
|
||||||
|
with open(cfg) as f:
|
||||||
|
self.yaml = yaml.load(f, Loader=yaml.SafeLoader) # model dict
|
||||||
|
|
||||||
|
# Define model
|
||||||
|
ch = self.yaml['ch'] = self.yaml.get('ch', ch) # input channels
|
||||||
|
if nc and nc != self.yaml['nc']:
|
||||||
|
logger.info(f"Overriding model.yaml nc={self.yaml['nc']} with nc={nc}")
|
||||||
|
self.yaml['nc'] = nc # override yaml value
|
||||||
|
if anchors:
|
||||||
|
logger.info(f'Overriding model.yaml anchors with anchors={anchors}')
|
||||||
|
self.yaml['anchors'] = round(anchors) # override yaml value
|
||||||
|
self.model, self.save = parse_model(deepcopy(self.yaml), ch=[ch]) # model, savelist
|
||||||
|
self.names = [str(i) for i in range(self.yaml['nc'])] # default names
|
||||||
|
# print([x.shape for x in self.forward(torch.zeros(1, ch, 64, 64))])
|
||||||
|
|
||||||
|
# Build strides, anchors
|
||||||
|
m = self.model[-1] # Detect()
|
||||||
|
if isinstance(m, Detect):
|
||||||
|
s = 256 # 2x min stride
|
||||||
|
m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))]) # forward
|
||||||
|
m.anchors /= m.stride.view(-1, 1, 1)
|
||||||
|
check_anchor_order(m)
|
||||||
|
self.stride = m.stride
|
||||||
|
self._initialize_biases() # only run once
|
||||||
|
# print('Strides: %s' % m.stride.tolist())
|
||||||
|
|
||||||
|
# Init weights, biases
|
||||||
|
initialize_weights(self)
|
||||||
|
self.info()
|
||||||
|
logger.info('')
|
||||||
|
|
||||||
|
def forward(self, x, augment=False, profile=False):
|
||||||
|
if augment:
|
||||||
|
img_size = x.shape[-2:] # height, width
|
||||||
|
s = [1, 0.83, 0.67] # scales
|
||||||
|
f = [None, 3, None] # flips (2-ud, 3-lr)
|
||||||
|
y = [] # outputs
|
||||||
|
for si, fi in zip(s, f):
|
||||||
|
xi = scale_img(x.flip(fi) if fi else x, si, gs=int(self.stride.max()))
|
||||||
|
yi = self.forward_once(xi)[0] # forward
|
||||||
|
# cv2.imwrite(f'img_{si}.jpg', 255 * xi[0].cpu().numpy().transpose((1, 2, 0))[:, :, ::-1]) # save
|
||||||
|
yi[..., :4] /= si # de-scale
|
||||||
|
if fi == 2:
|
||||||
|
yi[..., 1] = img_size[0] - yi[..., 1] # de-flip ud
|
||||||
|
elif fi == 3:
|
||||||
|
yi[..., 0] = img_size[1] - yi[..., 0] # de-flip lr
|
||||||
|
y.append(yi)
|
||||||
|
return torch.cat(y, 1), None # augmented inference, train
|
||||||
|
else:
|
||||||
|
return self.forward_once(x, profile) # single-scale inference, train
|
||||||
|
|
||||||
|
def forward_once(self, x, profile=False):
|
||||||
|
y, dt = [], [] # outputs
|
||||||
|
for m in self.model:
|
||||||
|
if m.f != -1: # if not from previous layer
|
||||||
|
x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f] # from earlier layers
|
||||||
|
|
||||||
|
if profile:
|
||||||
|
o = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2 if thop else 0 # FLOPS
|
||||||
|
t = time_synchronized()
|
||||||
|
for _ in range(10):
|
||||||
|
_ = m(x)
|
||||||
|
dt.append((time_synchronized() - t) * 100)
|
||||||
|
print('%10.1f%10.0f%10.1fms %-40s' % (o, m.np, dt[-1], m.type))
|
||||||
|
|
||||||
|
x = m(x) # run
|
||||||
|
y.append(x if m.i in self.save else None) # save output
|
||||||
|
|
||||||
|
if profile:
|
||||||
|
print('%.1fms total' % sum(dt))
|
||||||
|
return x
|
||||||
|
|
||||||
|
def _initialize_biases(self, cf=None): # initialize biases into Detect(), cf is class frequency
|
||||||
|
# https://arxiv.org/abs/1708.02002 section 3.3
|
||||||
|
# cf = torch.bincount(torch.tensor(np.concatenate(dataset.labels, 0)[:, 0]).long(), minlength=nc) + 1.
|
||||||
|
m = self.model[-1] # Detect() module
|
||||||
|
for mi, s in zip(m.m, m.stride): # from
|
||||||
|
b = mi.bias.view(m.na, -1) # conv.bias(255) to (3,85)
|
||||||
|
b.data[:, 4] += math.log(8 / (640 / s) ** 2) # obj (8 objects per 640 image)
|
||||||
|
b.data[:, 5:] += math.log(0.6 / (m.nc - 0.99)) if cf is None else torch.log(cf / cf.sum()) # cls
|
||||||
|
mi.bias = torch.nn.Parameter(b.view(-1), requires_grad=True)
|
||||||
|
|
||||||
|
def _print_biases(self):
|
||||||
|
m = self.model[-1] # Detect() module
|
||||||
|
for mi in m.m: # from
|
||||||
|
b = mi.bias.detach().view(m.na, -1).T # conv.bias(255) to (3,85)
|
||||||
|
print(('%6g Conv2d.bias:' + '%10.3g' * 6) % (mi.weight.shape[1], *b[:5].mean(1).tolist(), b[5:].mean()))
|
||||||
|
|
||||||
|
# def _print_weights(self):
|
||||||
|
# for m in self.model.modules():
|
||||||
|
# if type(m) is Bottleneck:
|
||||||
|
# print('%10.3g' % (m.w.detach().sigmoid() * 2)) # shortcut weights
|
||||||
|
|
||||||
|
def fuse(self): # fuse model Conv2d() + BatchNorm2d() layers
|
||||||
|
print('Fusing layers... ')
|
||||||
|
for m in self.model.modules():
|
||||||
|
if type(m) is Conv and hasattr(m, 'bn'):
|
||||||
|
m.conv = fuse_conv_and_bn(m.conv, m.bn) # update conv
|
||||||
|
delattr(m, 'bn') # remove batchnorm
|
||||||
|
m.forward = m.fuseforward # update forward
|
||||||
|
self.info()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def nms(self, mode=True): # add or remove NMS module
|
||||||
|
present = type(self.model[-1]) is NMS # last layer is NMS
|
||||||
|
if mode and not present:
|
||||||
|
print('Adding NMS... ')
|
||||||
|
m = NMS() # module
|
||||||
|
m.f = -1 # from
|
||||||
|
m.i = self.model[-1].i + 1 # index
|
||||||
|
self.model.add_module(name='%s' % m.i, module=m) # add
|
||||||
|
self.eval()
|
||||||
|
elif not mode and present:
|
||||||
|
print('Removing NMS... ')
|
||||||
|
self.model = self.model[:-1] # remove
|
||||||
|
return self
|
||||||
|
|
||||||
|
def autoshape(self): # add autoShape module
|
||||||
|
print('Adding autoShape... ')
|
||||||
|
m = autoShape(self) # wrap model
|
||||||
|
copy_attr(m, self, include=('yaml', 'nc', 'hyp', 'names', 'stride'), exclude=()) # copy attributes
|
||||||
|
return m
|
||||||
|
|
||||||
|
def info(self, verbose=False, img_size=640): # print model information
|
||||||
|
model_info(self, verbose, img_size)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_model(d, ch): # model_dict, input_channels(3)
|
||||||
|
logger.info('\n%3s%18s%3s%10s %-40s%-30s' % ('', 'from', 'n', 'params', 'module', 'arguments'))
|
||||||
|
anchors, nc, gd, gw = d['anchors'], d['nc'], d['depth_multiple'], d['width_multiple']
|
||||||
|
na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors # number of anchors
|
||||||
|
no = na * (nc + 5) # number of outputs = anchors * (classes + 5)
|
||||||
|
|
||||||
|
layers, save, c2 = [], [], ch[-1] # layers, savelist, ch out
|
||||||
|
for i, (f, n, m, args) in enumerate(d['backbone'] + d['head']): # from, number, module, args
|
||||||
|
m = eval(m) if isinstance(m, str) else m # eval strings
|
||||||
|
for j, a in enumerate(args):
|
||||||
|
try:
|
||||||
|
args[j] = eval(a) if isinstance(a, str) else a # eval strings
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
n = max(round(n * gd), 1) if n > 1 else n # depth gain
|
||||||
|
if m in [Conv, GhostConv, Bottleneck, GhostBottleneck, SPP, DWConv, MixConv2d, Focus, CrossConv, BottleneckCSP,
|
||||||
|
C3, C3TR]:
|
||||||
|
c1, c2 = ch[f], args[0]
|
||||||
|
if c2 != no: # if not output
|
||||||
|
c2 = make_divisible(c2 * gw, 8)
|
||||||
|
|
||||||
|
args = [c1, c2, *args[1:]]
|
||||||
|
if m in [BottleneckCSP, C3, C3TR]:
|
||||||
|
args.insert(2, n) # number of repeats
|
||||||
|
n = 1
|
||||||
|
elif m is nn.BatchNorm2d:
|
||||||
|
args = [ch[f]]
|
||||||
|
elif m is Concat:
|
||||||
|
c2 = sum([ch[x] for x in f])
|
||||||
|
elif m is Detect:
|
||||||
|
args.append([ch[x] for x in f])
|
||||||
|
if isinstance(args[1], int): # number of anchors
|
||||||
|
args[1] = [list(range(args[1] * 2))] * len(f)
|
||||||
|
elif m is Contract:
|
||||||
|
c2 = ch[f] * args[0] ** 2
|
||||||
|
elif m is Expand:
|
||||||
|
c2 = ch[f] // args[0] ** 2
|
||||||
|
else:
|
||||||
|
c2 = ch[f]
|
||||||
|
|
||||||
|
m_ = nn.Sequential(*[m(*args) for _ in range(n)]) if n > 1 else m(*args) # module
|
||||||
|
t = str(m)[8:-2].replace('__main__.', '') # module type
|
||||||
|
np = sum([x.numel() for x in m_.parameters()]) # number params
|
||||||
|
m_.i, m_.f, m_.type, m_.np = i, f, t, np # attach index, 'from' index, type, number params
|
||||||
|
logger.info('%3s%18s%3s%10.0f %-40s%-30s' % (i, f, n, np, t, args)) # print
|
||||||
|
save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist
|
||||||
|
layers.append(m_)
|
||||||
|
if i == 0:
|
||||||
|
ch = []
|
||||||
|
ch.append(c2)
|
||||||
|
return nn.Sequential(*layers), sorted(save)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--cfg', type=str, default='yolov5s.yaml', help='model.yaml')
|
||||||
|
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
|
||||||
|
opt = parser.parse_args()
|
||||||
|
opt.cfg = check_file(opt.cfg) # check file
|
||||||
|
set_logging()
|
||||||
|
device = select_device(opt.device)
|
||||||
|
|
||||||
|
# Create model
|
||||||
|
model = Model(opt.cfg).to(device)
|
||||||
|
model.train()
|
||||||
|
|
||||||
|
# Profile
|
||||||
|
# img = torch.rand(8 if torch.cuda.is_available() else 1, 3, 640, 640).to(device)
|
||||||
|
# y = model(img, profile=True)
|
||||||
|
|
||||||
|
# Tensorboard
|
||||||
|
# from torch.utils.tensorboard import SummaryWriter
|
||||||
|
# tb_writer = SummaryWriter()
|
||||||
|
# print("Run 'tensorboard --logdir=models/runs' to view tensorboard at http://localhost:6006/")
|
||||||
|
# tb_writer.add_graph(model.model, img) # add model to tensorboard
|
||||||
|
# tb_writer.add_image('test', img[0], dataformats='CWH') # add model to tensorboard
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 1.0 # model depth multiple
|
||||||
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
||||||
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
|
[-1, 3, C3, [128]],
|
||||||
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
|
[-1, 9, C3, [256]],
|
||||||
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
|
[-1, 9, C3, [512]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
|
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
||||||
|
[-1, 3, C3, [1024, False]], # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 head
|
||||||
|
head:
|
||||||
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 3, C3, [512, False]], # 13
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 3, C3, [256, False]], # 17 (P3/8-small)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
|
[[-1, 14], 1, Concat, [1]], # cat head P4
|
||||||
|
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
|
[[-1, 10], 1, Concat, [1]], # cat head P5
|
||||||
|
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
|
||||||
|
|
||||||
|
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 0.67 # model depth multiple
|
||||||
|
width_multiple: 0.75 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
||||||
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
|
[-1, 3, C3, [128]],
|
||||||
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
|
[-1, 9, C3, [256]],
|
||||||
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
|
[-1, 9, C3, [512]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
|
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
||||||
|
[-1, 3, C3, [1024, False]], # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 head
|
||||||
|
head:
|
||||||
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 3, C3, [512, False]], # 13
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 3, C3, [256, False]], # 17 (P3/8-small)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
|
[[-1, 14], 1, Concat, [1]], # cat head P4
|
||||||
|
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
|
[[-1, 10], 1, Concat, [1]], # cat head P5
|
||||||
|
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
|
||||||
|
|
||||||
|
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 0.33 # model depth multiple
|
||||||
|
width_multiple: 0.50 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
||||||
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
|
[-1, 3, C3, [128]],
|
||||||
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
|
[-1, 9, C3, [256]],
|
||||||
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
|
[-1, 9, C3, [512]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
|
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
||||||
|
[-1, 3, C3, [1024, False]], # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 head
|
||||||
|
head:
|
||||||
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 3, C3, [512, False]], # 13
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 3, C3, [256, False]], # 17 (P3/8-small)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
|
[[-1, 14], 1, Concat, [1]], # cat head P4
|
||||||
|
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
|
[[-1, 10], 1, Concat, [1]], # cat head P5
|
||||||
|
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
|
||||||
|
|
||||||
|
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
# parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 1.33 # model depth multiple
|
||||||
|
width_multiple: 1.25 # layer channel multiple
|
||||||
|
|
||||||
|
# anchors
|
||||||
|
anchors:
|
||||||
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
# YOLOv5 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
||||||
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
|
[-1, 3, C3, [128]],
|
||||||
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
|
[-1, 9, C3, [256]],
|
||||||
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
|
[-1, 9, C3, [512]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
|
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
||||||
|
[-1, 3, C3, [1024, False]], # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 head
|
||||||
|
head:
|
||||||
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 3, C3, [512, False]], # 13
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 3, C3, [256, False]], # 17 (P3/8-small)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
|
[[-1, 14], 1, Concat, [1]], # cat head P4
|
||||||
|
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
|
[[-1, 10], 1, Concat, [1]], # cat head P5
|
||||||
|
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
|
||||||
|
|
||||||
|
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,2 @@
|
||||||
|
python train.py --img 640 --batch 32 --epochs 100 --data exitStone.yaml --weights yolov5s.pt
|
||||||
|
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,27 @@
|
||||||
|
lr0: 0.01
|
||||||
|
lrf: 0.2
|
||||||
|
momentum: 0.937
|
||||||
|
weight_decay: 0.0005
|
||||||
|
warmup_epochs: 3.0
|
||||||
|
warmup_momentum: 0.8
|
||||||
|
warmup_bias_lr: 0.1
|
||||||
|
box: 0.05
|
||||||
|
cls: 0.5
|
||||||
|
cls_pw: 1.0
|
||||||
|
obj: 1.0
|
||||||
|
obj_pw: 1.0
|
||||||
|
iou_t: 0.2
|
||||||
|
anchor_t: 4.0
|
||||||
|
fl_gamma: 0.0
|
||||||
|
hsv_h: 0.015
|
||||||
|
hsv_s: 0.7
|
||||||
|
hsv_v: 0.4
|
||||||
|
degrees: 0.0
|
||||||
|
translate: 0.1
|
||||||
|
scale: 0.5
|
||||||
|
shear: 0.0
|
||||||
|
perspective: 0.0
|
||||||
|
flipud: 0.0
|
||||||
|
fliplr: 0.5
|
||||||
|
mosaic: 1.0
|
||||||
|
mixup: 0.0
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,40 @@
|
||||||
|
weights: yolov5s.pt
|
||||||
|
cfg: ''
|
||||||
|
data: ./data/exitStone.yaml
|
||||||
|
hyp: data/hyp.scratch.yaml
|
||||||
|
epochs: 100
|
||||||
|
batch_size: 32
|
||||||
|
img_size:
|
||||||
|
- 640
|
||||||
|
- 640
|
||||||
|
rect: false
|
||||||
|
resume: false
|
||||||
|
nosave: false
|
||||||
|
notest: false
|
||||||
|
noautoanchor: false
|
||||||
|
evolve: false
|
||||||
|
bucket: ''
|
||||||
|
cache_images: false
|
||||||
|
image_weights: false
|
||||||
|
device: ''
|
||||||
|
multi_scale: false
|
||||||
|
single_cls: false
|
||||||
|
adam: false
|
||||||
|
sync_bn: false
|
||||||
|
local_rank: -1
|
||||||
|
workers: 8
|
||||||
|
project: runs/train
|
||||||
|
entity: null
|
||||||
|
name: exp
|
||||||
|
exist_ok: false
|
||||||
|
quad: false
|
||||||
|
linear_lr: false
|
||||||
|
label_smoothing: 0.0
|
||||||
|
upload_dataset: false
|
||||||
|
bbox_interval: -1
|
||||||
|
save_period: -1
|
||||||
|
artifact_alias: latest
|
||||||
|
world_size: 1
|
||||||
|
global_rank: -1
|
||||||
|
save_dir: runs/train/exp
|
||||||
|
total_batch_size: 32
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,345 @@
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
import yaml
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
from models.experimental import attempt_load
|
||||||
|
from utils.datasets import create_dataloader
|
||||||
|
from utils.general import coco80_to_coco91_class, check_dataset, check_file, check_img_size, check_requirements, \
|
||||||
|
box_iou, non_max_suppression, scale_coords, xyxy2xywh, xywh2xyxy, set_logging, increment_path, colorstr
|
||||||
|
from utils.metrics import ap_per_class, ConfusionMatrix
|
||||||
|
from utils.plots import plot_images, output_to_target, plot_study_txt
|
||||||
|
from utils.torch_utils import select_device, time_synchronized
|
||||||
|
|
||||||
|
|
||||||
|
def test(data,
|
||||||
|
weights=None,
|
||||||
|
batch_size=32,
|
||||||
|
imgsz=640,
|
||||||
|
conf_thres=0.001,
|
||||||
|
iou_thres=0.6, # for NMS
|
||||||
|
save_json=False,
|
||||||
|
single_cls=False,
|
||||||
|
augment=False,
|
||||||
|
verbose=False,
|
||||||
|
model=None,
|
||||||
|
dataloader=None,
|
||||||
|
save_dir=Path(''), # for saving images
|
||||||
|
save_txt=False, # for auto-labelling
|
||||||
|
save_hybrid=False, # for hybrid auto-labelling
|
||||||
|
save_conf=False, # save auto-label confidences
|
||||||
|
plots=True,
|
||||||
|
wandb_logger=None,
|
||||||
|
compute_loss=None,
|
||||||
|
half_precision=True,
|
||||||
|
is_coco=False):
|
||||||
|
# Initialize/load model and set device
|
||||||
|
training = model is not None
|
||||||
|
if training: # called by train.py
|
||||||
|
device = next(model.parameters()).device # get model device
|
||||||
|
|
||||||
|
else: # called directly
|
||||||
|
set_logging()
|
||||||
|
device = select_device(opt.device, batch_size=batch_size)
|
||||||
|
|
||||||
|
# Directories
|
||||||
|
save_dir = Path(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok)) # increment run
|
||||||
|
(save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir
|
||||||
|
|
||||||
|
# Load model
|
||||||
|
model = attempt_load(weights, map_location=device) # load FP32 model
|
||||||
|
gs = max(int(model.stride.max()), 32) # grid size (max stride)
|
||||||
|
imgsz = check_img_size(imgsz, s=gs) # check img_size
|
||||||
|
|
||||||
|
# Multi-GPU disabled, incompatible with .half() https://github.com/ultralytics/yolov5/issues/99
|
||||||
|
# if device.type != 'cpu' and torch.cuda.device_count() > 1:
|
||||||
|
# model = nn.DataParallel(model)
|
||||||
|
|
||||||
|
# Half
|
||||||
|
half = device.type != 'cpu' and half_precision # half precision only supported on CUDA
|
||||||
|
if half:
|
||||||
|
model.half()
|
||||||
|
|
||||||
|
# Configure
|
||||||
|
model.eval()
|
||||||
|
if isinstance(data, str):
|
||||||
|
is_coco = data.endswith('coco.yaml')
|
||||||
|
with open(data) as f:
|
||||||
|
data = yaml.load(f, Loader=yaml.SafeLoader)
|
||||||
|
check_dataset(data) # check
|
||||||
|
nc = 1 if single_cls else int(data['nc']) # number of classes
|
||||||
|
iouv = torch.linspace(0.5, 0.95, 10).to(device) # iou vector for mAP@0.5:0.95
|
||||||
|
niou = iouv.numel()
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
log_imgs = 0
|
||||||
|
if wandb_logger and wandb_logger.wandb:
|
||||||
|
log_imgs = min(wandb_logger.log_imgs, 100)
|
||||||
|
# Dataloader
|
||||||
|
if not training:
|
||||||
|
if device.type != 'cpu':
|
||||||
|
model(torch.zeros(1, 3, imgsz, imgsz).to(device).type_as(next(model.parameters()))) # run once
|
||||||
|
task = opt.task if opt.task in ('train', 'val', 'test') else 'val' # path to train/val/test images
|
||||||
|
dataloader = create_dataloader(data[task], imgsz, batch_size, gs, opt, pad=0.5, rect=True,
|
||||||
|
prefix=colorstr(f'{task}: '))[0]
|
||||||
|
|
||||||
|
seen = 0
|
||||||
|
confusion_matrix = ConfusionMatrix(nc=nc)
|
||||||
|
names = {k: v for k, v in enumerate(model.names if hasattr(model, 'names') else model.module.names)}
|
||||||
|
coco91class = coco80_to_coco91_class()
|
||||||
|
s = ('%20s' + '%12s' * 6) % ('Class', 'Images', 'Labels', 'P', 'R', 'mAP@.5', 'mAP@.5:.95')
|
||||||
|
p, r, f1, mp, mr, map50, map, t0, t1 = 0., 0., 0., 0., 0., 0., 0., 0., 0.
|
||||||
|
loss = torch.zeros(3, device=device)
|
||||||
|
jdict, stats, ap, ap_class, wandb_images = [], [], [], [], []
|
||||||
|
for batch_i, (img, targets, paths, shapes) in enumerate(tqdm(dataloader, desc=s)):
|
||||||
|
img = img.to(device, non_blocking=True)
|
||||||
|
img = img.half() if half else img.float() # uint8 to fp16/32
|
||||||
|
img /= 255.0 # 0 - 255 to 0.0 - 1.0
|
||||||
|
targets = targets.to(device)
|
||||||
|
nb, _, height, width = img.shape # batch size, channels, height, width
|
||||||
|
|
||||||
|
with torch.no_grad():
|
||||||
|
# Run model
|
||||||
|
t = time_synchronized()
|
||||||
|
out, train_out = model(img, augment=augment) # inference and training outputs
|
||||||
|
t0 += time_synchronized() - t
|
||||||
|
|
||||||
|
# Compute loss
|
||||||
|
if compute_loss:
|
||||||
|
loss += compute_loss([x.float() for x in train_out], targets)[1][:3] # box, obj, cls
|
||||||
|
|
||||||
|
# Run NMS
|
||||||
|
targets[:, 2:] *= torch.Tensor([width, height, width, height]).to(device) # to pixels
|
||||||
|
lb = [targets[targets[:, 0] == i, 1:] for i in range(nb)] if save_hybrid else [] # for autolabelling
|
||||||
|
t = time_synchronized()
|
||||||
|
out = non_max_suppression(out, conf_thres=conf_thres, iou_thres=iou_thres, labels=lb, multi_label=True)
|
||||||
|
t1 += time_synchronized() - t
|
||||||
|
|
||||||
|
# Statistics per image
|
||||||
|
for si, pred in enumerate(out):
|
||||||
|
labels = targets[targets[:, 0] == si, 1:]
|
||||||
|
nl = len(labels)
|
||||||
|
tcls = labels[:, 0].tolist() if nl else [] # target class
|
||||||
|
path = Path(paths[si])
|
||||||
|
seen += 1
|
||||||
|
|
||||||
|
if len(pred) == 0:
|
||||||
|
if nl:
|
||||||
|
stats.append((torch.zeros(0, niou, dtype=torch.bool), torch.Tensor(), torch.Tensor(), tcls))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Predictions
|
||||||
|
predn = pred.clone()
|
||||||
|
scale_coords(img[si].shape[1:], predn[:, :4], shapes[si][0], shapes[si][1]) # native-space pred
|
||||||
|
|
||||||
|
# Append to text file
|
||||||
|
if save_txt:
|
||||||
|
gn = torch.tensor(shapes[si][0])[[1, 0, 1, 0]] # normalization gain whwh
|
||||||
|
for *xyxy, conf, cls in predn.tolist():
|
||||||
|
xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh
|
||||||
|
line = (cls, *xywh, conf) if save_conf else (cls, *xywh) # label format
|
||||||
|
with open(save_dir / 'labels' / (path.stem + '.txt'), 'a') as f:
|
||||||
|
f.write(('%g ' * len(line)).rstrip() % line + '\n')
|
||||||
|
|
||||||
|
# W&B logging - Media Panel Plots
|
||||||
|
if len(wandb_images) < log_imgs and wandb_logger.current_epoch > 0: # Check for test operation
|
||||||
|
if wandb_logger.current_epoch % wandb_logger.bbox_interval == 0:
|
||||||
|
box_data = [{"position": {"minX": xyxy[0], "minY": xyxy[1], "maxX": xyxy[2], "maxY": xyxy[3]},
|
||||||
|
"class_id": int(cls),
|
||||||
|
"box_caption": "%s %.3f" % (names[cls], conf),
|
||||||
|
"scores": {"class_score": conf},
|
||||||
|
"domain": "pixel"} for *xyxy, conf, cls in pred.tolist()]
|
||||||
|
boxes = {"predictions": {"box_data": box_data, "class_labels": names}} # inference-space
|
||||||
|
wandb_images.append(wandb_logger.wandb.Image(img[si], boxes=boxes, caption=path.name))
|
||||||
|
wandb_logger.log_training_progress(predn, path, names) if wandb_logger and wandb_logger.wandb_run else None
|
||||||
|
|
||||||
|
# Append to pycocotools JSON dictionary
|
||||||
|
if save_json:
|
||||||
|
# [{"image_id": 42, "category_id": 18, "bbox": [258.15, 41.29, 348.26, 243.78], "score": 0.236}, ...
|
||||||
|
image_id = int(path.stem) if path.stem.isnumeric() else path.stem
|
||||||
|
box = xyxy2xywh(predn[:, :4]) # xywh
|
||||||
|
box[:, :2] -= box[:, 2:] / 2 # xy center to top-left corner
|
||||||
|
for p, b in zip(pred.tolist(), box.tolist()):
|
||||||
|
jdict.append({'image_id': image_id,
|
||||||
|
'category_id': coco91class[int(p[5])] if is_coco else int(p[5]),
|
||||||
|
'bbox': [round(x, 3) for x in b],
|
||||||
|
'score': round(p[4], 5)})
|
||||||
|
|
||||||
|
# Assign all predictions as incorrect
|
||||||
|
correct = torch.zeros(pred.shape[0], niou, dtype=torch.bool, device=device)
|
||||||
|
if nl:
|
||||||
|
detected = [] # target indices
|
||||||
|
tcls_tensor = labels[:, 0]
|
||||||
|
|
||||||
|
# target boxes
|
||||||
|
tbox = xywh2xyxy(labels[:, 1:5])
|
||||||
|
scale_coords(img[si].shape[1:], tbox, shapes[si][0], shapes[si][1]) # native-space labels
|
||||||
|
if plots:
|
||||||
|
confusion_matrix.process_batch(predn, torch.cat((labels[:, 0:1], tbox), 1))
|
||||||
|
|
||||||
|
# Per target class
|
||||||
|
for cls in torch.unique(tcls_tensor):
|
||||||
|
ti = (cls == tcls_tensor).nonzero(as_tuple=False).view(-1) # prediction indices
|
||||||
|
pi = (cls == pred[:, 5]).nonzero(as_tuple=False).view(-1) # target indices
|
||||||
|
|
||||||
|
# Search for detections
|
||||||
|
if pi.shape[0]:
|
||||||
|
# Prediction to target ious
|
||||||
|
ious, i = box_iou(predn[pi, :4], tbox[ti]).max(1) # best ious, indices
|
||||||
|
|
||||||
|
# Append detections
|
||||||
|
detected_set = set()
|
||||||
|
for j in (ious > iouv[0]).nonzero(as_tuple=False):
|
||||||
|
d = ti[i[j]] # detected target
|
||||||
|
if d.item() not in detected_set:
|
||||||
|
detected_set.add(d.item())
|
||||||
|
detected.append(d)
|
||||||
|
correct[pi[j]] = ious[j] > iouv # iou_thres is 1xn
|
||||||
|
if len(detected) == nl: # all targets already located in image
|
||||||
|
break
|
||||||
|
|
||||||
|
# Append statistics (correct, conf, pcls, tcls)
|
||||||
|
stats.append((correct.cpu(), pred[:, 4].cpu(), pred[:, 5].cpu(), tcls))
|
||||||
|
|
||||||
|
# Plot images
|
||||||
|
if plots and batch_i < 3:
|
||||||
|
f = save_dir / f'test_batch{batch_i}_labels.jpg' # labels
|
||||||
|
Thread(target=plot_images, args=(img, targets, paths, f, names), daemon=True).start()
|
||||||
|
f = save_dir / f'test_batch{batch_i}_pred.jpg' # predictions
|
||||||
|
Thread(target=plot_images, args=(img, output_to_target(out), paths, f, names), daemon=True).start()
|
||||||
|
|
||||||
|
# Compute statistics
|
||||||
|
stats = [np.concatenate(x, 0) for x in zip(*stats)] # to numpy
|
||||||
|
if len(stats) and stats[0].any():
|
||||||
|
p, r, ap, f1, ap_class = ap_per_class(*stats, plot=plots, save_dir=save_dir, names=names)
|
||||||
|
ap50, ap = ap[:, 0], ap.mean(1) # AP@0.5, AP@0.5:0.95
|
||||||
|
mp, mr, map50, map = p.mean(), r.mean(), ap50.mean(), ap.mean()
|
||||||
|
nt = np.bincount(stats[3].astype(np.int64), minlength=nc) # number of targets per class
|
||||||
|
else:
|
||||||
|
nt = torch.zeros(1)
|
||||||
|
|
||||||
|
# Print results
|
||||||
|
pf = '%20s' + '%12i' * 2 + '%12.3g' * 4 # print format
|
||||||
|
print(pf % ('all', seen, nt.sum(), mp, mr, map50, map))
|
||||||
|
|
||||||
|
# Print results per class
|
||||||
|
if (verbose or (nc < 50 and not training)) and nc > 1 and len(stats):
|
||||||
|
for i, c in enumerate(ap_class):
|
||||||
|
print(pf % (names[c], seen, nt[c], p[i], r[i], ap50[i], ap[i]))
|
||||||
|
|
||||||
|
# Print speeds
|
||||||
|
t = tuple(x / seen * 1E3 for x in (t0, t1, t0 + t1)) + (imgsz, imgsz, batch_size) # tuple
|
||||||
|
if not training:
|
||||||
|
print('Speed: %.1f/%.1f/%.1f ms inference/NMS/total per %gx%g image at batch-size %g' % t)
|
||||||
|
|
||||||
|
# Plots
|
||||||
|
if plots:
|
||||||
|
confusion_matrix.plot(save_dir=save_dir, names=list(names.values()))
|
||||||
|
if wandb_logger and wandb_logger.wandb:
|
||||||
|
val_batches = [wandb_logger.wandb.Image(str(f), caption=f.name) for f in sorted(save_dir.glob('test*.jpg'))]
|
||||||
|
wandb_logger.log({"Validation": val_batches})
|
||||||
|
if wandb_images:
|
||||||
|
wandb_logger.log({"Bounding Box Debugger/Images": wandb_images})
|
||||||
|
|
||||||
|
# Save JSON
|
||||||
|
if save_json and len(jdict):
|
||||||
|
w = Path(weights[0] if isinstance(weights, list) else weights).stem if weights is not None else '' # weights
|
||||||
|
anno_json = '../coco/annotations/instances_val2017.json' # annotations json
|
||||||
|
pred_json = str(save_dir / f"{w}_predictions.json") # predictions json
|
||||||
|
print('\nEvaluating pycocotools mAP... saving %s...' % pred_json)
|
||||||
|
with open(pred_json, 'w') as f:
|
||||||
|
json.dump(jdict, f)
|
||||||
|
|
||||||
|
try: # https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb
|
||||||
|
from pycocotools.coco import COCO
|
||||||
|
from pycocotools.cocoeval import COCOeval
|
||||||
|
|
||||||
|
anno = COCO(anno_json) # init annotations api
|
||||||
|
pred = anno.loadRes(pred_json) # init predictions api
|
||||||
|
eval = COCOeval(anno, pred, 'bbox')
|
||||||
|
if is_coco:
|
||||||
|
eval.params.imgIds = [int(Path(x).stem) for x in dataloader.dataset.img_files] # image IDs to evaluate
|
||||||
|
eval.evaluate()
|
||||||
|
eval.accumulate()
|
||||||
|
eval.summarize()
|
||||||
|
map, map50 = eval.stats[:2] # update results (mAP@0.5:0.95, mAP@0.5)
|
||||||
|
except Exception as e:
|
||||||
|
print(f'pycocotools unable to run: {e}')
|
||||||
|
|
||||||
|
# Return results
|
||||||
|
model.float() # for training
|
||||||
|
if not training:
|
||||||
|
s = f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else ''
|
||||||
|
print(f"Results saved to {save_dir}{s}")
|
||||||
|
maps = np.zeros(nc) + map
|
||||||
|
for i, c in enumerate(ap_class):
|
||||||
|
maps[c] = ap[i]
|
||||||
|
return (mp, mr, map50, map, *(loss.cpu() / len(dataloader)).tolist()), maps, t
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(prog='test.py')
|
||||||
|
parser.add_argument('--weights', nargs='+', type=str, default='yolov5s.pt', help='model.pt path(s)')
|
||||||
|
parser.add_argument('--data', type=str, default='data/coco128.yaml', help='*.data path')
|
||||||
|
parser.add_argument('--batch-size', type=int, default=32, help='size of each image batch')
|
||||||
|
parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
|
||||||
|
parser.add_argument('--conf-thres', type=float, default=0.001, help='object confidence threshold')
|
||||||
|
parser.add_argument('--iou-thres', type=float, default=0.6, help='IOU threshold for NMS')
|
||||||
|
parser.add_argument('--task', default='val', help='train, val, test, speed or study')
|
||||||
|
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
|
||||||
|
parser.add_argument('--single-cls', action='store_true', help='treat as single-class dataset')
|
||||||
|
parser.add_argument('--augment', action='store_true', help='augmented inference')
|
||||||
|
parser.add_argument('--verbose', action='store_true', help='report mAP by class')
|
||||||
|
parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
|
||||||
|
parser.add_argument('--save-hybrid', action='store_true', help='save label+prediction hybrid results to *.txt')
|
||||||
|
parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
|
||||||
|
parser.add_argument('--save-json', action='store_true', help='save a cocoapi-compatible JSON results file')
|
||||||
|
parser.add_argument('--project', default='runs/test', help='save to project/name')
|
||||||
|
parser.add_argument('--name', default='exp', help='save to project/name')
|
||||||
|
parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
|
||||||
|
opt = parser.parse_args()
|
||||||
|
opt.save_json |= opt.data.endswith('coco.yaml')
|
||||||
|
opt.data = check_file(opt.data) # check file
|
||||||
|
print(opt)
|
||||||
|
check_requirements()
|
||||||
|
|
||||||
|
if opt.task in ('train', 'val', 'test'): # run normally
|
||||||
|
test(opt.data,
|
||||||
|
opt.weights,
|
||||||
|
opt.batch_size,
|
||||||
|
opt.img_size,
|
||||||
|
opt.conf_thres,
|
||||||
|
opt.iou_thres,
|
||||||
|
opt.save_json,
|
||||||
|
opt.single_cls,
|
||||||
|
opt.augment,
|
||||||
|
opt.verbose,
|
||||||
|
save_txt=opt.save_txt | opt.save_hybrid,
|
||||||
|
save_hybrid=opt.save_hybrid,
|
||||||
|
save_conf=opt.save_conf,
|
||||||
|
)
|
||||||
|
|
||||||
|
elif opt.task == 'speed': # speed benchmarks
|
||||||
|
for w in opt.weights:
|
||||||
|
test(opt.data, w, opt.batch_size, opt.img_size, 0.25, 0.45, save_json=False, plots=False)
|
||||||
|
|
||||||
|
elif opt.task == 'study': # run over a range of settings and save/plot
|
||||||
|
# python test.py --task study --data coco.yaml --iou 0.7 --weights yolov5s.pt yolov5m.pt yolov5l.pt yolov5x.pt
|
||||||
|
x = list(range(256, 1536 + 128, 128)) # x axis (image sizes)
|
||||||
|
for w in opt.weights:
|
||||||
|
f = f'study_{Path(opt.data).stem}_{Path(w).stem}.txt' # filename to save to
|
||||||
|
y = [] # y axis
|
||||||
|
for i in x: # img-size
|
||||||
|
print(f'\nRunning {f} point {i}...')
|
||||||
|
r, _, t = test(opt.data, w, opt.batch_size, i, opt.conf_thres, opt.iou_thres, opt.save_json,
|
||||||
|
plots=False)
|
||||||
|
y.append(r + t) # results and times
|
||||||
|
np.savetxt(f, y, fmt='%10.4g') # save
|
||||||
|
os.system('zip -r study.zip study_*.txt')
|
||||||
|
plot_study_txt(x=x) # plot
|
||||||
|
|
@ -0,0 +1,627 @@
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
from copy import deepcopy
|
||||||
|
from pathlib import Path
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import torch.distributed as dist
|
||||||
|
import torch.nn as nn
|
||||||
|
import torch.nn.functional as F
|
||||||
|
import torch.optim as optim
|
||||||
|
import torch.optim.lr_scheduler as lr_scheduler
|
||||||
|
import torch.utils.data
|
||||||
|
import yaml
|
||||||
|
from torch.cuda import amp
|
||||||
|
from torch.nn.parallel import DistributedDataParallel as DDP
|
||||||
|
from torch.utils.tensorboard import SummaryWriter
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
import test # import test.py to get mAP after each epoch
|
||||||
|
from models.experimental import attempt_load
|
||||||
|
from models.yolo import Model
|
||||||
|
from utils.autoanchor import check_anchors
|
||||||
|
from utils.datasets import create_dataloader
|
||||||
|
from utils.general import labels_to_class_weights, increment_path, labels_to_image_weights, init_seeds, \
|
||||||
|
fitness, strip_optimizer, get_latest_run, check_dataset, check_file, check_git_status, check_img_size, \
|
||||||
|
check_requirements, print_mutation, set_logging, one_cycle, colorstr
|
||||||
|
from utils.google_utils import attempt_download
|
||||||
|
from utils.loss import ComputeLoss
|
||||||
|
from utils.plots import plot_images, plot_labels, plot_results, plot_evolution
|
||||||
|
from utils.torch_utils import ModelEMA, select_device, intersect_dicts, torch_distributed_zero_first, is_parallel
|
||||||
|
from utils.wandb_logging.wandb_utils import WandbLogger, check_wandb_resume
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def train(hyp, opt, device, tb_writer=None):
|
||||||
|
logger.info(colorstr('hyperparameters: ') + ', '.join(f'{k}={v}' for k, v in hyp.items()))
|
||||||
|
save_dir, epochs, batch_size, total_batch_size, weights, rank = \
|
||||||
|
Path(opt.save_dir), opt.epochs, opt.batch_size, opt.total_batch_size, opt.weights, opt.global_rank
|
||||||
|
|
||||||
|
# Directories
|
||||||
|
wdir = save_dir / 'weights'
|
||||||
|
wdir.mkdir(parents=True, exist_ok=True) # make dir
|
||||||
|
last = wdir / 'last.pt'
|
||||||
|
best = wdir / 'best.pt'
|
||||||
|
results_file = save_dir / 'results.txt'
|
||||||
|
|
||||||
|
# Save run settings
|
||||||
|
with open(save_dir / 'hyp.yaml', 'w') as f:
|
||||||
|
yaml.dump(hyp, f, sort_keys=False)
|
||||||
|
with open(save_dir / 'opt.yaml', 'w') as f:
|
||||||
|
yaml.dump(vars(opt), f, sort_keys=False)
|
||||||
|
|
||||||
|
# Configure
|
||||||
|
plots = not opt.evolve # create plots
|
||||||
|
cuda = device.type != 'cpu'
|
||||||
|
init_seeds(2 + rank)
|
||||||
|
with open(opt.data) as f:
|
||||||
|
data_dict = yaml.load(f, Loader=yaml.SafeLoader) # data dict
|
||||||
|
is_coco = opt.data.endswith('coco.yaml')
|
||||||
|
|
||||||
|
# Logging- Doing this before checking the dataset. Might update data_dict
|
||||||
|
loggers = {'wandb': None} # loggers dict
|
||||||
|
if rank in [-1, 0]:
|
||||||
|
opt.hyp = hyp # add hyperparameters
|
||||||
|
run_id = torch.load(weights).get('wandb_id') if weights.endswith('.pt') and os.path.isfile(weights) else None
|
||||||
|
wandb_logger = WandbLogger(opt, Path(opt.save_dir).stem, run_id, data_dict)
|
||||||
|
loggers['wandb'] = wandb_logger.wandb
|
||||||
|
data_dict = wandb_logger.data_dict
|
||||||
|
if wandb_logger.wandb:
|
||||||
|
weights, epochs, hyp = opt.weights, opt.epochs, opt.hyp # WandbLogger might update weights, epochs if resuming
|
||||||
|
|
||||||
|
nc = 1 if opt.single_cls else int(data_dict['nc']) # number of classes
|
||||||
|
names = ['item'] if opt.single_cls and len(data_dict['names']) != 1 else data_dict['names'] # class names
|
||||||
|
assert len(names) == nc, '%g names found for nc=%g dataset in %s' % (len(names), nc, opt.data) # check
|
||||||
|
|
||||||
|
# Model
|
||||||
|
pretrained = weights.endswith('.pt')
|
||||||
|
if pretrained:
|
||||||
|
with torch_distributed_zero_first(rank):
|
||||||
|
attempt_download(weights) # download if not found locally
|
||||||
|
ckpt = torch.load(weights, map_location=device) # load checkpoint
|
||||||
|
model = Model(opt.cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # create
|
||||||
|
exclude = ['anchor'] if (opt.cfg or hyp.get('anchors')) and not opt.resume else [] # exclude keys
|
||||||
|
state_dict = ckpt['model'].float().state_dict() # to FP32
|
||||||
|
state_dict = intersect_dicts(state_dict, model.state_dict(), exclude=exclude) # intersect
|
||||||
|
model.load_state_dict(state_dict, strict=False) # load
|
||||||
|
logger.info('Transferred %g/%g items from %s' % (len(state_dict), len(model.state_dict()), weights)) # report
|
||||||
|
else:
|
||||||
|
model = Model(opt.cfg, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # create
|
||||||
|
with torch_distributed_zero_first(rank):
|
||||||
|
check_dataset(data_dict) # check
|
||||||
|
train_path = data_dict['train']
|
||||||
|
test_path = data_dict['val']
|
||||||
|
|
||||||
|
# Freeze
|
||||||
|
freeze = [] # parameter names to freeze (full or partial)
|
||||||
|
for k, v in model.named_parameters():
|
||||||
|
v.requires_grad = True # train all layers
|
||||||
|
if any(x in k for x in freeze):
|
||||||
|
print('freezing %s' % k)
|
||||||
|
v.requires_grad = False
|
||||||
|
|
||||||
|
# Optimizer
|
||||||
|
nbs = 64 # nominal batch size
|
||||||
|
accumulate = max(round(nbs / total_batch_size), 1) # accumulate loss before optimizing
|
||||||
|
hyp['weight_decay'] *= total_batch_size * accumulate / nbs # scale weight_decay
|
||||||
|
logger.info(f"Scaled weight_decay = {hyp['weight_decay']}")
|
||||||
|
|
||||||
|
pg0, pg1, pg2 = [], [], [] # optimizer parameter groups
|
||||||
|
for k, v in model.named_modules():
|
||||||
|
if hasattr(v, 'bias') and isinstance(v.bias, nn.Parameter):
|
||||||
|
pg2.append(v.bias) # biases
|
||||||
|
if isinstance(v, nn.BatchNorm2d):
|
||||||
|
pg0.append(v.weight) # no decay
|
||||||
|
elif hasattr(v, 'weight') and isinstance(v.weight, nn.Parameter):
|
||||||
|
pg1.append(v.weight) # apply decay
|
||||||
|
|
||||||
|
if opt.adam:
|
||||||
|
optimizer = optim.Adam(pg0, lr=hyp['lr0'], betas=(hyp['momentum'], 0.999)) # adjust beta1 to momentum
|
||||||
|
else:
|
||||||
|
optimizer = optim.SGD(pg0, lr=hyp['lr0'], momentum=hyp['momentum'], nesterov=True)
|
||||||
|
|
||||||
|
optimizer.add_param_group({'params': pg1, 'weight_decay': hyp['weight_decay']}) # add pg1 with weight_decay
|
||||||
|
optimizer.add_param_group({'params': pg2}) # add pg2 (biases)
|
||||||
|
logger.info('Optimizer groups: %g .bias, %g conv.weight, %g other' % (len(pg2), len(pg1), len(pg0)))
|
||||||
|
del pg0, pg1, pg2
|
||||||
|
|
||||||
|
# Scheduler https://arxiv.org/pdf/1812.01187.pdf
|
||||||
|
# https://pytorch.org/docs/stable/_modules/torch/optim/lr_scheduler.html#OneCycleLR
|
||||||
|
if opt.linear_lr:
|
||||||
|
lf = lambda x: (1 - x / (epochs - 1)) * (1.0 - hyp['lrf']) + hyp['lrf'] # linear
|
||||||
|
else:
|
||||||
|
lf = one_cycle(1, hyp['lrf'], epochs) # cosine 1->hyp['lrf']
|
||||||
|
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf)
|
||||||
|
# plot_lr_scheduler(optimizer, scheduler, epochs)
|
||||||
|
|
||||||
|
# EMA
|
||||||
|
ema = ModelEMA(model) if rank in [-1, 0] else None
|
||||||
|
|
||||||
|
# Resume
|
||||||
|
start_epoch, best_fitness = 0, 0.0
|
||||||
|
if pretrained:
|
||||||
|
# Optimizer
|
||||||
|
if ckpt['optimizer'] is not None:
|
||||||
|
optimizer.load_state_dict(ckpt['optimizer'])
|
||||||
|
best_fitness = ckpt['best_fitness']
|
||||||
|
|
||||||
|
# EMA
|
||||||
|
if ema and ckpt.get('ema'):
|
||||||
|
ema.ema.load_state_dict(ckpt['ema'].float().state_dict())
|
||||||
|
ema.updates = ckpt['updates']
|
||||||
|
|
||||||
|
# Results
|
||||||
|
if ckpt.get('training_results') is not None:
|
||||||
|
results_file.write_text(ckpt['training_results']) # write results.txt
|
||||||
|
|
||||||
|
# Epochs
|
||||||
|
start_epoch = ckpt['epoch'] + 1
|
||||||
|
if opt.resume:
|
||||||
|
assert start_epoch > 0, '%s training to %g epochs is finished, nothing to resume.' % (weights, epochs)
|
||||||
|
if epochs < start_epoch:
|
||||||
|
logger.info('%s has been trained for %g epochs. Fine-tuning for %g additional epochs.' %
|
||||||
|
(weights, ckpt['epoch'], epochs))
|
||||||
|
epochs += ckpt['epoch'] # finetune additional epochs
|
||||||
|
|
||||||
|
del ckpt, state_dict
|
||||||
|
|
||||||
|
# Image sizes
|
||||||
|
gs = max(int(model.stride.max()), 32) # grid size (max stride)
|
||||||
|
nl = model.model[-1].nl # number of detection layers (used for scaling hyp['obj'])
|
||||||
|
imgsz, imgsz_test = [check_img_size(x, gs) for x in opt.img_size] # verify imgsz are gs-multiples
|
||||||
|
|
||||||
|
# DP mode
|
||||||
|
if cuda and rank == -1 and torch.cuda.device_count() > 1:
|
||||||
|
model = torch.nn.DataParallel(model)
|
||||||
|
|
||||||
|
# SyncBatchNorm
|
||||||
|
if opt.sync_bn and cuda and rank != -1:
|
||||||
|
model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model).to(device)
|
||||||
|
logger.info('Using SyncBatchNorm()')
|
||||||
|
|
||||||
|
# Trainloader
|
||||||
|
dataloader, dataset = create_dataloader(train_path, imgsz, batch_size, gs, opt,
|
||||||
|
hyp=hyp, augment=True, cache=opt.cache_images, rect=opt.rect, rank=rank,
|
||||||
|
world_size=opt.world_size, workers=opt.workers,
|
||||||
|
image_weights=opt.image_weights, quad=opt.quad, prefix=colorstr('train: '))
|
||||||
|
mlc = np.concatenate(dataset.labels, 0)[:, 0].max() # max label class
|
||||||
|
nb = len(dataloader) # number of batches
|
||||||
|
logger.info('#####bs:%d steps:%d ' % (batch_size, nb))
|
||||||
|
|
||||||
|
assert mlc < nc, 'Label class %g exceeds nc=%g in %s. Possible class labels are 0-%g' % (mlc, nc, opt.data, nc - 1)
|
||||||
|
|
||||||
|
# Process 0
|
||||||
|
if rank in [-1, 0]:
|
||||||
|
testloader = create_dataloader(test_path, imgsz_test, batch_size * 2, gs, opt, # testloader
|
||||||
|
hyp=hyp, cache=opt.cache_images and not opt.notest, rect=True, rank=-1,
|
||||||
|
world_size=opt.world_size, workers=opt.workers,
|
||||||
|
pad=0.5, prefix=colorstr('val: '))[0]
|
||||||
|
|
||||||
|
if not opt.resume:
|
||||||
|
labels = np.concatenate(dataset.labels, 0)
|
||||||
|
c = torch.tensor(labels[:, 0]) # classes
|
||||||
|
# cf = torch.bincount(c.long(), minlength=nc) + 1. # frequency
|
||||||
|
# model._initialize_biases(cf.to(device))
|
||||||
|
if plots:
|
||||||
|
plot_labels(labels, names, save_dir, loggers)
|
||||||
|
if tb_writer:
|
||||||
|
tb_writer.add_histogram('classes', c, 0)
|
||||||
|
|
||||||
|
# Anchors
|
||||||
|
if not opt.noautoanchor:
|
||||||
|
check_anchors(dataset, model=model, thr=hyp['anchor_t'], imgsz=imgsz)
|
||||||
|
model.half().float() # pre-reduce anchor precision
|
||||||
|
|
||||||
|
# DDP mode
|
||||||
|
if cuda and rank != -1:
|
||||||
|
model = DDP(model, device_ids=[opt.local_rank], output_device=opt.local_rank,
|
||||||
|
# nn.MultiheadAttention incompatibility with DDP https://github.com/pytorch/pytorch/issues/26698
|
||||||
|
find_unused_parameters=any(isinstance(layer, nn.MultiheadAttention) for layer in model.modules()))
|
||||||
|
|
||||||
|
# Model parameters
|
||||||
|
hyp['box'] *= 3. / nl # scale to layers
|
||||||
|
hyp['cls'] *= nc / 80. * 3. / nl # scale to classes and layers
|
||||||
|
hyp['obj'] *= (imgsz / 640) ** 2 * 3. / nl # scale to image size and layers
|
||||||
|
hyp['label_smoothing'] = opt.label_smoothing
|
||||||
|
model.nc = nc # attach number of classes to model
|
||||||
|
model.hyp = hyp # attach hyperparameters to model
|
||||||
|
model.gr = 1.0 # iou loss ratio (obj_loss = 1.0 or iou)
|
||||||
|
model.class_weights = labels_to_class_weights(dataset.labels, nc).to(device) * nc # attach class weights
|
||||||
|
model.names = names
|
||||||
|
|
||||||
|
# Start training
|
||||||
|
t0 = time.time()
|
||||||
|
nw = max(round(hyp['warmup_epochs'] * nb), 1000) # number of warmup iterations, max(3 epochs, 1k iterations)
|
||||||
|
# nw = min(nw, (epochs - start_epoch) / 2 * nb) # limit warmup to < 1/2 of training
|
||||||
|
maps = np.zeros(nc) # mAP per class
|
||||||
|
results = (0, 0, 0, 0, 0, 0, 0) # P, R, mAP@.5, mAP@.5-.95, val_loss(box, obj, cls)
|
||||||
|
scheduler.last_epoch = start_epoch - 1 # do not move
|
||||||
|
scaler = amp.GradScaler(enabled=cuda)
|
||||||
|
compute_loss = ComputeLoss(model) # init loss class
|
||||||
|
logger.info(f'Image sizes {imgsz} train, {imgsz_test} test\n'
|
||||||
|
f'Using {dataloader.num_workers} dataloader workers\n'
|
||||||
|
f'Logging results to {save_dir}\n'
|
||||||
|
f'Starting training for {epochs} epochs...')
|
||||||
|
for epoch in range(start_epoch, epochs): # epoch ------------------------------------------------------------------
|
||||||
|
model.train()
|
||||||
|
|
||||||
|
# Update image weights (optional)
|
||||||
|
if opt.image_weights:
|
||||||
|
# Generate indices
|
||||||
|
if rank in [-1, 0]:
|
||||||
|
cw = model.class_weights.cpu().numpy() * (1 - maps) ** 2 / nc # class weights
|
||||||
|
iw = labels_to_image_weights(dataset.labels, nc=nc, class_weights=cw) # image weights
|
||||||
|
dataset.indices = random.choices(range(dataset.n), weights=iw, k=dataset.n) # rand weighted idx
|
||||||
|
# Broadcast if DDP
|
||||||
|
if rank != -1:
|
||||||
|
indices = (torch.tensor(dataset.indices) if rank == 0 else torch.zeros(dataset.n)).int()
|
||||||
|
dist.broadcast(indices, 0)
|
||||||
|
if rank != 0:
|
||||||
|
dataset.indices = indices.cpu().numpy()
|
||||||
|
|
||||||
|
# Update mosaic border
|
||||||
|
# b = int(random.uniform(0.25 * imgsz, 0.75 * imgsz + gs) // gs * gs)
|
||||||
|
# dataset.mosaic_border = [b - imgsz, -b] # height, width borders
|
||||||
|
|
||||||
|
mloss = torch.zeros(4, device=device) # mean losses
|
||||||
|
if rank != -1:
|
||||||
|
dataloader.sampler.set_epoch(epoch)
|
||||||
|
pbar = enumerate(dataloader)
|
||||||
|
logger.info(('\n' + '%10s' * 8) % ('Epoch', 'gpu_mem', 'box', 'obj', 'cls', 'total', 'labels', 'img_size'))
|
||||||
|
if rank in [-1, 0]:
|
||||||
|
pbar = tqdm(pbar, total=nb) # progress bar
|
||||||
|
optimizer.zero_grad()
|
||||||
|
for i, (imgs, targets, paths, _) in pbar: # batch -------------------------------------------------------------
|
||||||
|
ni = i + nb * epoch # number integrated batches (since train start)
|
||||||
|
imgs = imgs.to(device, non_blocking=True).float() / 255.0 # uint8 to float32, 0-255 to 0.0-1.0
|
||||||
|
|
||||||
|
# Warmup
|
||||||
|
if ni <= nw:
|
||||||
|
xi = [0, nw] # x interp
|
||||||
|
# model.gr = np.interp(ni, xi, [0.0, 1.0]) # iou loss ratio (obj_loss = 1.0 or iou)
|
||||||
|
accumulate = max(1, np.interp(ni, xi, [1, nbs / total_batch_size]).round())
|
||||||
|
for j, x in enumerate(optimizer.param_groups):
|
||||||
|
# bias lr falls from 0.1 to lr0, all other lrs rise from 0.0 to lr0
|
||||||
|
x['lr'] = np.interp(ni, xi, [hyp['warmup_bias_lr'] if j == 2 else 0.0, x['initial_lr'] * lf(epoch)])
|
||||||
|
if 'momentum' in x:
|
||||||
|
x['momentum'] = np.interp(ni, xi, [hyp['warmup_momentum'], hyp['momentum']])
|
||||||
|
|
||||||
|
# Multi-scale
|
||||||
|
if opt.multi_scale:
|
||||||
|
sz = random.randrange(imgsz * 0.5, imgsz * 1.5 + gs) // gs * gs # size
|
||||||
|
sf = sz / max(imgs.shape[2:]) # scale factor
|
||||||
|
if sf != 1:
|
||||||
|
ns = [math.ceil(x * sf / gs) * gs for x in imgs.shape[2:]] # new shape (stretched to gs-multiple)
|
||||||
|
imgs = F.interpolate(imgs, size=ns, mode='bilinear', align_corners=False)
|
||||||
|
|
||||||
|
# Forward
|
||||||
|
with amp.autocast(enabled=cuda):
|
||||||
|
pred = model(imgs) # forward
|
||||||
|
loss, loss_items = compute_loss(pred, targets.to(device)) # loss scaled by batch_size
|
||||||
|
if rank != -1:
|
||||||
|
loss *= opt.world_size # gradient averaged between devices in DDP mode
|
||||||
|
if opt.quad:
|
||||||
|
loss *= 4.
|
||||||
|
|
||||||
|
# Backward
|
||||||
|
scaler.scale(loss).backward()
|
||||||
|
|
||||||
|
# Optimize
|
||||||
|
if ni % accumulate == 0:
|
||||||
|
scaler.step(optimizer) # optimizer.step
|
||||||
|
scaler.update()
|
||||||
|
optimizer.zero_grad()
|
||||||
|
if ema:
|
||||||
|
ema.update(model)
|
||||||
|
|
||||||
|
# Print
|
||||||
|
if rank in [-1, 0]:
|
||||||
|
mloss = (mloss * i + loss_items) / (i + 1) # update mean losses
|
||||||
|
mem = '%.3gG' % (torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0) # (GB)
|
||||||
|
s = ('%10s' * 2 + '%10.4g' * 6) % (
|
||||||
|
'%g/%g' % (epoch, epochs - 1), mem, *mloss, targets.shape[0], imgs.shape[-1])
|
||||||
|
pbar.set_description(s)
|
||||||
|
|
||||||
|
# Plot
|
||||||
|
if plots and ni < 3:
|
||||||
|
f = save_dir / f'train_batch{ni}.jpg' # filename
|
||||||
|
Thread(target=plot_images, args=(imgs, targets, paths, f), daemon=True).start()
|
||||||
|
# if tb_writer:
|
||||||
|
# tb_writer.add_image(f, result, dataformats='HWC', global_step=epoch)
|
||||||
|
# tb_writer.add_graph(torch.jit.trace(model, imgs, strict=False), []) # add model graph
|
||||||
|
elif plots and ni == 10 and wandb_logger.wandb:
|
||||||
|
wandb_logger.log({"Mosaics": [wandb_logger.wandb.Image(str(x), caption=x.name) for x in
|
||||||
|
save_dir.glob('train*.jpg') if x.exists()]})
|
||||||
|
|
||||||
|
# end batch ------------------------------------------------------------------------------------------------
|
||||||
|
# end epoch ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Scheduler
|
||||||
|
lr = [x['lr'] for x in optimizer.param_groups] # for tensorboard
|
||||||
|
scheduler.step()
|
||||||
|
|
||||||
|
# DDP process 0 or single-GPU
|
||||||
|
if rank in [-1, 0]:
|
||||||
|
# mAP
|
||||||
|
ema.update_attr(model, include=['yaml', 'nc', 'hyp', 'gr', 'names', 'stride', 'class_weights'])
|
||||||
|
final_epoch = epoch + 1 == epochs
|
||||||
|
if not opt.notest or final_epoch: # Calculate mAP
|
||||||
|
wandb_logger.current_epoch = epoch + 1
|
||||||
|
results, maps, times = test.test(data_dict,
|
||||||
|
batch_size=batch_size * 2,
|
||||||
|
imgsz=imgsz_test,
|
||||||
|
model=ema.ema,
|
||||||
|
single_cls=opt.single_cls,
|
||||||
|
dataloader=testloader,
|
||||||
|
save_dir=save_dir,
|
||||||
|
verbose=nc < 50 and final_epoch,
|
||||||
|
plots=plots and final_epoch,
|
||||||
|
wandb_logger=wandb_logger,
|
||||||
|
compute_loss=compute_loss,
|
||||||
|
is_coco=is_coco)
|
||||||
|
|
||||||
|
# Write
|
||||||
|
with open(results_file, 'a') as f:
|
||||||
|
f.write(s + '%10.4g' * 7 % results + '\n') # append metrics, val_loss
|
||||||
|
if len(opt.name) and opt.bucket:
|
||||||
|
os.system('gsutil cp %s gs://%s/results/results%s.txt' % (results_file, opt.bucket, opt.name))
|
||||||
|
|
||||||
|
# Log
|
||||||
|
tags = ['train/box_loss', 'train/obj_loss', 'train/cls_loss', # train loss
|
||||||
|
'metrics/precision', 'metrics/recall', 'metrics/mAP_0.5', 'metrics/mAP_0.5:0.95',
|
||||||
|
'val/box_loss', 'val/obj_loss', 'val/cls_loss', # val loss
|
||||||
|
'x/lr0', 'x/lr1', 'x/lr2'] # params
|
||||||
|
for x, tag in zip(list(mloss[:-1]) + list(results) + lr, tags):
|
||||||
|
if tb_writer:
|
||||||
|
tb_writer.add_scalar(tag, x, epoch) # tensorboard
|
||||||
|
if wandb_logger.wandb:
|
||||||
|
wandb_logger.log({tag: x}) # W&B
|
||||||
|
|
||||||
|
# Update best mAP
|
||||||
|
fi = fitness(np.array(results).reshape(1, -1)) # weighted combination of [P, R, mAP@.5, mAP@.5-.95]
|
||||||
|
if fi > best_fitness:
|
||||||
|
best_fitness = fi
|
||||||
|
wandb_logger.end_epoch(best_result=best_fitness == fi)
|
||||||
|
|
||||||
|
# Save model
|
||||||
|
if (not opt.nosave) or (final_epoch and not opt.evolve): # if save
|
||||||
|
ckpt = {'epoch': epoch,
|
||||||
|
'best_fitness': best_fitness,
|
||||||
|
'training_results': results_file.read_text(),
|
||||||
|
'model': deepcopy(model.module if is_parallel(model) else model).half(),
|
||||||
|
'ema': deepcopy(ema.ema).half(),
|
||||||
|
'updates': ema.updates,
|
||||||
|
'optimizer': optimizer.state_dict(),
|
||||||
|
'wandb_id': wandb_logger.wandb_run.id if wandb_logger.wandb else None}
|
||||||
|
|
||||||
|
# Save last, best and delete
|
||||||
|
torch.save(ckpt, last)
|
||||||
|
if best_fitness == fi:
|
||||||
|
torch.save(ckpt, best)
|
||||||
|
if wandb_logger.wandb:
|
||||||
|
if ((epoch + 1) % opt.save_period == 0 and not final_epoch) and opt.save_period != -1:
|
||||||
|
wandb_logger.log_model(
|
||||||
|
last.parent, opt, epoch, fi, best_model=best_fitness == fi)
|
||||||
|
del ckpt
|
||||||
|
|
||||||
|
# end epoch ----------------------------------------------------------------------------------------------------
|
||||||
|
# end training
|
||||||
|
if rank in [-1, 0]:
|
||||||
|
# Plots
|
||||||
|
if plots:
|
||||||
|
plot_results(save_dir=save_dir) # save as results.png
|
||||||
|
if wandb_logger.wandb:
|
||||||
|
files = ['results.png', 'confusion_matrix.png', *[f'{x}_curve.png' for x in ('F1', 'PR', 'P', 'R')]]
|
||||||
|
wandb_logger.log({"Results": [wandb_logger.wandb.Image(str(save_dir / f), caption=f) for f in files
|
||||||
|
if (save_dir / f).exists()]})
|
||||||
|
# Test best.pt
|
||||||
|
logger.info('%g epochs completed in %.3f hours.\n' % (epoch - start_epoch + 1, (time.time() - t0) / 3600))
|
||||||
|
if opt.data.endswith('coco.yaml') and nc == 80: # if COCO
|
||||||
|
for m in (last, best) if best.exists() else (last): # speed, mAP tests
|
||||||
|
results, _, _ = test.test(opt.data,
|
||||||
|
batch_size=batch_size * 2,
|
||||||
|
imgsz=imgsz_test,
|
||||||
|
conf_thres=0.001,
|
||||||
|
iou_thres=0.7,
|
||||||
|
model=attempt_load(m, device).half(),
|
||||||
|
single_cls=opt.single_cls,
|
||||||
|
dataloader=testloader,
|
||||||
|
save_dir=save_dir,
|
||||||
|
save_json=True,
|
||||||
|
plots=False,
|
||||||
|
is_coco=is_coco)
|
||||||
|
|
||||||
|
# Strip optimizers
|
||||||
|
final = best if best.exists() else last # final model
|
||||||
|
for f in last, best:
|
||||||
|
if f.exists():
|
||||||
|
strip_optimizer(f) # strip optimizers
|
||||||
|
if opt.bucket:
|
||||||
|
os.system(f'gsutil cp {final} gs://{opt.bucket}/weights') # upload
|
||||||
|
if wandb_logger.wandb and not opt.evolve: # Log the stripped model
|
||||||
|
wandb_logger.wandb.log_artifact(str(final), type='model',
|
||||||
|
name='run_' + wandb_logger.wandb_run.id + '_model',
|
||||||
|
aliases=['last', 'best', 'stripped'])
|
||||||
|
wandb_logger.finish_run()
|
||||||
|
else:
|
||||||
|
dist.destroy_process_group()
|
||||||
|
torch.cuda.empty_cache()
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--weights', type=str, default='yolov5s.pt', help='initial weights path')
|
||||||
|
parser.add_argument('--cfg', type=str, default='', help='model.yaml path')
|
||||||
|
parser.add_argument('--data', type=str, default='data/coco128.yaml', help='data.yaml path')
|
||||||
|
parser.add_argument('--hyp', type=str, default='data/hyp.scratch.yaml', help='hyperparameters path')
|
||||||
|
parser.add_argument('--epochs', type=int, default=300)
|
||||||
|
parser.add_argument('--batch-size', type=int, default=16, help='total batch size for all GPUs')
|
||||||
|
parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='[train, test] image sizes')
|
||||||
|
parser.add_argument('--rect', action='store_true', help='rectangular training')
|
||||||
|
parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')
|
||||||
|
parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')
|
||||||
|
parser.add_argument('--notest', action='store_true', help='only test final epoch')
|
||||||
|
parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')
|
||||||
|
parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters')
|
||||||
|
parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')
|
||||||
|
parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')
|
||||||
|
parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')
|
||||||
|
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
|
||||||
|
parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')
|
||||||
|
parser.add_argument('--single-cls', action='store_true', help='train multi-class data as single-class')
|
||||||
|
parser.add_argument('--adam', action='store_true', help='use torch.optim.Adam() optimizer')
|
||||||
|
parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')
|
||||||
|
parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify')
|
||||||
|
parser.add_argument('--workers', type=int, default=8, help='maximum number of dataloader workers')
|
||||||
|
parser.add_argument('--project', default='runs/train', help='save to project/name')
|
||||||
|
parser.add_argument('--entity', default=None, help='W&B entity')
|
||||||
|
parser.add_argument('--name', default='exp', help='save to project/name')
|
||||||
|
parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
|
||||||
|
parser.add_argument('--quad', action='store_true', help='quad dataloader')
|
||||||
|
parser.add_argument('--linear-lr', action='store_true', help='linear LR')
|
||||||
|
parser.add_argument('--label-smoothing', type=float, default=0.0, help='Label smoothing epsilon')
|
||||||
|
parser.add_argument('--upload_dataset', action='store_true', help='Upload dataset as W&B artifact table')
|
||||||
|
parser.add_argument('--bbox_interval', type=int, default=-1, help='Set bounding-box image logging interval for W&B')
|
||||||
|
parser.add_argument('--save_period', type=int, default=-1, help='Log model after every "save_period" epoch')
|
||||||
|
parser.add_argument('--artifact_alias', type=str, default="latest", help='version of dataset artifact to be used')
|
||||||
|
opt = parser.parse_args()
|
||||||
|
|
||||||
|
# Set DDP variables
|
||||||
|
opt.world_size = int(os.environ['WORLD_SIZE']) if 'WORLD_SIZE' in os.environ else 1
|
||||||
|
opt.global_rank = int(os.environ['RANK']) if 'RANK' in os.environ else -1
|
||||||
|
set_logging(opt.global_rank)
|
||||||
|
if opt.global_rank in [-1, 0]:
|
||||||
|
check_git_status()
|
||||||
|
check_requirements()
|
||||||
|
|
||||||
|
# Resume
|
||||||
|
wandb_run = check_wandb_resume(opt)
|
||||||
|
if opt.resume and not wandb_run: # resume an interrupted run
|
||||||
|
ckpt = opt.resume if isinstance(opt.resume, str) else get_latest_run() # specified or most recent path
|
||||||
|
assert os.path.isfile(ckpt), 'ERROR: --resume checkpoint does not exist'
|
||||||
|
apriori = opt.global_rank, opt.local_rank
|
||||||
|
with open(Path(ckpt).parent.parent / 'opt.yaml') as f:
|
||||||
|
opt = argparse.Namespace(**yaml.load(f, Loader=yaml.SafeLoader)) # replace
|
||||||
|
opt.cfg, opt.weights, opt.resume, opt.batch_size, opt.global_rank, opt.local_rank = '', ckpt, True, opt.total_batch_size, *apriori # reinstate
|
||||||
|
logger.info('Resuming training from %s' % ckpt)
|
||||||
|
else:
|
||||||
|
# opt.hyp = opt.hyp or ('hyp.finetune.yaml' if opt.weights else 'hyp.scratch.yaml')
|
||||||
|
opt.data, opt.cfg, opt.hyp = check_file(opt.data), check_file(opt.cfg), check_file(opt.hyp) # check files
|
||||||
|
assert len(opt.cfg) or len(opt.weights), 'either --cfg or --weights must be specified'
|
||||||
|
opt.img_size.extend([opt.img_size[-1]] * (2 - len(opt.img_size))) # extend to 2 sizes (train, test)
|
||||||
|
opt.name = 'evolve' if opt.evolve else opt.name
|
||||||
|
opt.save_dir = increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok | opt.evolve) # increment run
|
||||||
|
|
||||||
|
# DDP mode
|
||||||
|
opt.total_batch_size = opt.batch_size
|
||||||
|
device = select_device(opt.device, batch_size=opt.batch_size)
|
||||||
|
if opt.local_rank != -1:
|
||||||
|
assert torch.cuda.device_count() > opt.local_rank
|
||||||
|
torch.cuda.set_device(opt.local_rank)
|
||||||
|
device = torch.device('cuda', opt.local_rank)
|
||||||
|
dist.init_process_group(backend='nccl', init_method='env://') # distributed backend
|
||||||
|
assert opt.batch_size % opt.world_size == 0, '--batch-size must be multiple of CUDA device count'
|
||||||
|
opt.batch_size = opt.total_batch_size // opt.world_size
|
||||||
|
|
||||||
|
# Hyperparameters
|
||||||
|
with open(opt.hyp) as f:
|
||||||
|
hyp = yaml.load(f, Loader=yaml.SafeLoader) # load hyps
|
||||||
|
|
||||||
|
# Train
|
||||||
|
logger.info(opt)
|
||||||
|
if not opt.evolve:
|
||||||
|
tb_writer = None # init loggers
|
||||||
|
if opt.global_rank in [-1, 0]:
|
||||||
|
prefix = colorstr('tensorboard: ')
|
||||||
|
logger.info(f"{prefix}Start with 'tensorboard --logdir {opt.project}', view at http://localhost:6006/")
|
||||||
|
tb_writer = SummaryWriter(opt.save_dir) # Tensorboard
|
||||||
|
train(hyp, opt, device, tb_writer)
|
||||||
|
|
||||||
|
# Evolve hyperparameters (optional)
|
||||||
|
else:
|
||||||
|
# Hyperparameter evolution metadata (mutation scale 0-1, lower_limit, upper_limit)
|
||||||
|
meta = {'lr0': (1, 1e-5, 1e-1), # initial learning rate (SGD=1E-2, Adam=1E-3)
|
||||||
|
'lrf': (1, 0.01, 1.0), # final OneCycleLR learning rate (lr0 * lrf)
|
||||||
|
'momentum': (0.3, 0.6, 0.98), # SGD momentum/Adam beta1
|
||||||
|
'weight_decay': (1, 0.0, 0.001), # optimizer weight decay
|
||||||
|
'warmup_epochs': (1, 0.0, 5.0), # warmup epochs (fractions ok)
|
||||||
|
'warmup_momentum': (1, 0.0, 0.95), # warmup initial momentum
|
||||||
|
'warmup_bias_lr': (1, 0.0, 0.2), # warmup initial bias lr
|
||||||
|
'box': (1, 0.02, 0.2), # box loss gain
|
||||||
|
'cls': (1, 0.2, 4.0), # cls loss gain
|
||||||
|
'cls_pw': (1, 0.5, 2.0), # cls BCELoss positive_weight
|
||||||
|
'obj': (1, 0.2, 4.0), # obj loss gain (scale with pixels)
|
||||||
|
'obj_pw': (1, 0.5, 2.0), # obj BCELoss positive_weight
|
||||||
|
'iou_t': (0, 0.1, 0.7), # IoU training threshold
|
||||||
|
'anchor_t': (1, 2.0, 8.0), # anchor-multiple threshold
|
||||||
|
'anchors': (2, 2.0, 10.0), # anchors per output grid (0 to ignore)
|
||||||
|
'fl_gamma': (0, 0.0, 2.0), # focal loss gamma (efficientDet default gamma=1.5)
|
||||||
|
'hsv_h': (1, 0.0, 0.1), # image HSV-Hue augmentation (fraction)
|
||||||
|
'hsv_s': (1, 0.0, 0.9), # image HSV-Saturation augmentation (fraction)
|
||||||
|
'hsv_v': (1, 0.0, 0.9), # image HSV-Value augmentation (fraction)
|
||||||
|
'degrees': (1, 0.0, 45.0), # image rotation (+/- deg)
|
||||||
|
'translate': (1, 0.0, 0.9), # image translation (+/- fraction)
|
||||||
|
'scale': (1, 0.0, 0.9), # image scale (+/- gain)
|
||||||
|
'shear': (1, 0.0, 10.0), # image shear (+/- deg)
|
||||||
|
'perspective': (0, 0.0, 0.001), # image perspective (+/- fraction), range 0-0.001
|
||||||
|
'flipud': (1, 0.0, 1.0), # image flip up-down (probability)
|
||||||
|
'fliplr': (0, 0.0, 1.0), # image flip left-right (probability)
|
||||||
|
'mosaic': (1, 0.0, 1.0), # image mixup (probability)
|
||||||
|
'mixup': (1, 0.0, 1.0)} # image mixup (probability)
|
||||||
|
|
||||||
|
assert opt.local_rank == -1, 'DDP mode not implemented for --evolve'
|
||||||
|
opt.notest, opt.nosave = True, True # only test/save final epoch
|
||||||
|
# ei = [isinstance(x, (int, float)) for x in hyp.values()] # evolvable indices
|
||||||
|
yaml_file = Path(opt.save_dir) / 'hyp_evolved.yaml' # save best result here
|
||||||
|
if opt.bucket:
|
||||||
|
os.system('gsutil cp gs://%s/evolve.txt .' % opt.bucket) # download evolve.txt if exists
|
||||||
|
|
||||||
|
for _ in range(300): # generations to evolve
|
||||||
|
if Path('evolve.txt').exists(): # if evolve.txt exists: select best hyps and mutate
|
||||||
|
# Select parent(s)
|
||||||
|
parent = 'single' # parent selection method: 'single' or 'weighted'
|
||||||
|
x = np.loadtxt('evolve.txt', ndmin=2)
|
||||||
|
n = min(5, len(x)) # number of previous results to consider
|
||||||
|
x = x[np.argsort(-fitness(x))][:n] # top n mutations
|
||||||
|
w = fitness(x) - fitness(x).min() # weights
|
||||||
|
if parent == 'single' or len(x) == 1:
|
||||||
|
# x = x[random.randint(0, n - 1)] # random selection
|
||||||
|
x = x[random.choices(range(n), weights=w)[0]] # weighted selection
|
||||||
|
elif parent == 'weighted':
|
||||||
|
x = (x * w.reshape(n, 1)).sum(0) / w.sum() # weighted combination
|
||||||
|
|
||||||
|
# Mutate
|
||||||
|
mp, s = 0.8, 0.2 # mutation probability, sigma
|
||||||
|
npr = np.random
|
||||||
|
npr.seed(int(time.time()))
|
||||||
|
g = np.array([x[0] for x in meta.values()]) # gains 0-1
|
||||||
|
ng = len(meta)
|
||||||
|
v = np.ones(ng)
|
||||||
|
while all(v == 1): # mutate until a change occurs (prevent duplicates)
|
||||||
|
v = (g * (npr.random(ng) < mp) * npr.randn(ng) * npr.random() * s + 1).clip(0.3, 3.0)
|
||||||
|
for i, k in enumerate(hyp.keys()): # plt.hist(v.ravel(), 300)
|
||||||
|
hyp[k] = float(x[i + 7] * v[i]) # mutate
|
||||||
|
|
||||||
|
# Constrain to limits
|
||||||
|
for k, v in meta.items():
|
||||||
|
hyp[k] = max(hyp[k], v[1]) # lower limit
|
||||||
|
hyp[k] = min(hyp[k], v[2]) # upper limit
|
||||||
|
hyp[k] = round(hyp[k], 5) # significant digits
|
||||||
|
|
||||||
|
# Train mutation
|
||||||
|
results = train(hyp.copy(), opt, device)
|
||||||
|
|
||||||
|
# Write mutation results
|
||||||
|
print_mutation(hyp.copy(), results, yaml_file, opt.bucket)
|
||||||
|
|
||||||
|
# Plot results
|
||||||
|
plot_evolution(yaml_file)
|
||||||
|
print(f'Hyperparameter evolution complete. Best results saved as: {yaml_file}\n'
|
||||||
|
f'Command to train a new model with these hyperparameters: $ python train.py --hyp {yaml_file}')
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
python train.py --img 640 --batch 64 --epochs 100 --data exitPPS.yaml --resume ../../cby/yolov5-master/runs/train/outlet-x11/weights/best.pt
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
{"code": 0, "msg": "操作成功", "data": []}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,72 @@
|
||||||
|
# Activation functions
|
||||||
|
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
import torch.nn.functional as F
|
||||||
|
|
||||||
|
|
||||||
|
# SiLU https://arxiv.org/pdf/1606.08415.pdf ----------------------------------------------------------------------------
|
||||||
|
class SiLU(nn.Module): # export-friendly version of nn.SiLU()
|
||||||
|
@staticmethod
|
||||||
|
def forward(x):
|
||||||
|
return x * torch.sigmoid(x)
|
||||||
|
|
||||||
|
|
||||||
|
class Hardswish(nn.Module): # export-friendly version of nn.Hardswish()
|
||||||
|
@staticmethod
|
||||||
|
def forward(x):
|
||||||
|
# return x * F.hardsigmoid(x) # for torchscript and CoreML
|
||||||
|
return x * F.hardtanh(x + 3, 0., 6.) / 6. # for torchscript, CoreML and ONNX
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryEfficientSwish(nn.Module):
|
||||||
|
class F(torch.autograd.Function):
|
||||||
|
@staticmethod
|
||||||
|
def forward(ctx, x):
|
||||||
|
ctx.save_for_backward(x)
|
||||||
|
return x * torch.sigmoid(x)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def backward(ctx, grad_output):
|
||||||
|
x = ctx.saved_tensors[0]
|
||||||
|
sx = torch.sigmoid(x)
|
||||||
|
return grad_output * (sx * (1 + x * (1 - sx)))
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return self.F.apply(x)
|
||||||
|
|
||||||
|
|
||||||
|
# Mish https://github.com/digantamisra98/Mish --------------------------------------------------------------------------
|
||||||
|
class Mish(nn.Module):
|
||||||
|
@staticmethod
|
||||||
|
def forward(x):
|
||||||
|
return x * F.softplus(x).tanh()
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryEfficientMish(nn.Module):
|
||||||
|
class F(torch.autograd.Function):
|
||||||
|
@staticmethod
|
||||||
|
def forward(ctx, x):
|
||||||
|
ctx.save_for_backward(x)
|
||||||
|
return x.mul(torch.tanh(F.softplus(x))) # x * tanh(ln(1 + exp(x)))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def backward(ctx, grad_output):
|
||||||
|
x = ctx.saved_tensors[0]
|
||||||
|
sx = torch.sigmoid(x)
|
||||||
|
fx = F.softplus(x).tanh()
|
||||||
|
return grad_output * (fx + x * sx * (1 - fx * fx))
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return self.F.apply(x)
|
||||||
|
|
||||||
|
|
||||||
|
# FReLU https://arxiv.org/abs/2007.11824 -------------------------------------------------------------------------------
|
||||||
|
class FReLU(nn.Module):
|
||||||
|
def __init__(self, c1, k=3): # ch_in, kernel
|
||||||
|
super().__init__()
|
||||||
|
self.conv = nn.Conv2d(c1, c1, k, 1, 1, groups=c1, bias=False)
|
||||||
|
self.bn = nn.BatchNorm2d(c1)
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return torch.max(x, self.bn(self.conv(x)))
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
# Auto-anchor utils
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
import yaml
|
||||||
|
from scipy.cluster.vq import kmeans
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
from utils.general import colorstr
|
||||||
|
|
||||||
|
|
||||||
|
def check_anchor_order(m):
|
||||||
|
# Check anchor order against stride order for YOLOv5 Detect() module m, and correct if necessary
|
||||||
|
a = m.anchor_grid.prod(-1).view(-1) # anchor area
|
||||||
|
da = a[-1] - a[0] # delta a
|
||||||
|
ds = m.stride[-1] - m.stride[0] # delta s
|
||||||
|
if da.sign() != ds.sign(): # same order
|
||||||
|
print('Reversing anchor order')
|
||||||
|
m.anchors[:] = m.anchors.flip(0)
|
||||||
|
m.anchor_grid[:] = m.anchor_grid.flip(0)
|
||||||
|
|
||||||
|
|
||||||
|
def check_anchors(dataset, model, thr=4.0, imgsz=640):
|
||||||
|
# Check anchor fit to data, recompute if necessary
|
||||||
|
prefix = colorstr('autoanchor: ')
|
||||||
|
print(f'\n{prefix}Analyzing anchors... ', end='')
|
||||||
|
m = model.module.model[-1] if hasattr(model, 'module') else model.model[-1] # Detect()
|
||||||
|
shapes = imgsz * dataset.shapes / dataset.shapes.max(1, keepdims=True)
|
||||||
|
scale = np.random.uniform(0.9, 1.1, size=(shapes.shape[0], 1)) # augment scale
|
||||||
|
wh = torch.tensor(np.concatenate([l[:, 3:5] * s for s, l in zip(shapes * scale, dataset.labels)])).float() # wh
|
||||||
|
|
||||||
|
def metric(k): # compute metric
|
||||||
|
r = wh[:, None] / k[None]
|
||||||
|
x = torch.min(r, 1. / r).min(2)[0] # ratio metric
|
||||||
|
best = x.max(1)[0] # best_x
|
||||||
|
aat = (x > 1. / thr).float().sum(1).mean() # anchors above threshold
|
||||||
|
bpr = (best > 1. / thr).float().mean() # best possible recall
|
||||||
|
return bpr, aat
|
||||||
|
|
||||||
|
anchors = m.anchor_grid.clone().cpu().view(-1, 2) # current anchors
|
||||||
|
bpr, aat = metric(anchors)
|
||||||
|
print(f'anchors/target = {aat:.2f}, Best Possible Recall (BPR) = {bpr:.4f}', end='')
|
||||||
|
if bpr < 0.98: # threshold to recompute
|
||||||
|
print('. Attempting to improve anchors, please wait...')
|
||||||
|
na = m.anchor_grid.numel() // 2 # number of anchors
|
||||||
|
try:
|
||||||
|
anchors = kmean_anchors(dataset, n=na, img_size=imgsz, thr=thr, gen=1000, verbose=False)
|
||||||
|
except Exception as e:
|
||||||
|
print(f'{prefix}ERROR: {e}')
|
||||||
|
new_bpr = metric(anchors)[0]
|
||||||
|
if new_bpr > bpr: # replace anchors
|
||||||
|
anchors = torch.tensor(anchors, device=m.anchors.device).type_as(m.anchors)
|
||||||
|
m.anchor_grid[:] = anchors.clone().view_as(m.anchor_grid) # for inference
|
||||||
|
m.anchors[:] = anchors.clone().view_as(m.anchors) / m.stride.to(m.anchors.device).view(-1, 1, 1) # loss
|
||||||
|
check_anchor_order(m)
|
||||||
|
print(f'{prefix}New anchors saved to model. Update model *.yaml to use these anchors in the future.')
|
||||||
|
else:
|
||||||
|
print(f'{prefix}Original anchors better than new anchors. Proceeding with original anchors.')
|
||||||
|
print('') # newline
|
||||||
|
|
||||||
|
|
||||||
|
def kmean_anchors(path='./data/coco128.yaml', n=9, img_size=640, thr=4.0, gen=1000, verbose=True):
|
||||||
|
""" Creates kmeans-evolved anchors from training dataset
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
path: path to dataset *.yaml, or a loaded dataset
|
||||||
|
n: number of anchors
|
||||||
|
img_size: image size used for training
|
||||||
|
thr: anchor-label wh ratio threshold hyperparameter hyp['anchor_t'] used for training, default=4.0
|
||||||
|
gen: generations to evolve anchors using genetic algorithm
|
||||||
|
verbose: print all results
|
||||||
|
|
||||||
|
Return:
|
||||||
|
k: kmeans evolved anchors
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
from utils.autoanchor import *; _ = kmean_anchors()
|
||||||
|
"""
|
||||||
|
thr = 1. / thr
|
||||||
|
prefix = colorstr('autoanchor: ')
|
||||||
|
|
||||||
|
def metric(k, wh): # compute metrics
|
||||||
|
r = wh[:, None] / k[None]
|
||||||
|
x = torch.min(r, 1. / r).min(2)[0] # ratio metric
|
||||||
|
# x = wh_iou(wh, torch.tensor(k)) # iou metric
|
||||||
|
return x, x.max(1)[0] # x, best_x
|
||||||
|
|
||||||
|
def anchor_fitness(k): # mutation fitness
|
||||||
|
_, best = metric(torch.tensor(k, dtype=torch.float32), wh)
|
||||||
|
return (best * (best > thr).float()).mean() # fitness
|
||||||
|
|
||||||
|
def print_results(k):
|
||||||
|
k = k[np.argsort(k.prod(1))] # sort small to large
|
||||||
|
x, best = metric(k, wh0)
|
||||||
|
bpr, aat = (best > thr).float().mean(), (x > thr).float().mean() * n # best possible recall, anch > thr
|
||||||
|
print(f'{prefix}thr={thr:.2f}: {bpr:.4f} best possible recall, {aat:.2f} anchors past thr')
|
||||||
|
print(f'{prefix}n={n}, img_size={img_size}, metric_all={x.mean():.3f}/{best.mean():.3f}-mean/best, '
|
||||||
|
f'past_thr={x[x > thr].mean():.3f}-mean: ', end='')
|
||||||
|
for i, x in enumerate(k):
|
||||||
|
print('%i,%i' % (round(x[0]), round(x[1])), end=', ' if i < len(k) - 1 else '\n') # use in *.cfg
|
||||||
|
return k
|
||||||
|
|
||||||
|
if isinstance(path, str): # *.yaml file
|
||||||
|
with open(path) as f:
|
||||||
|
data_dict = yaml.load(f, Loader=yaml.SafeLoader) # model dict
|
||||||
|
from utils.datasets import LoadImagesAndLabels
|
||||||
|
dataset = LoadImagesAndLabels(data_dict['train'], augment=True, rect=True)
|
||||||
|
else:
|
||||||
|
dataset = path # dataset
|
||||||
|
|
||||||
|
# Get label wh
|
||||||
|
shapes = img_size * dataset.shapes / dataset.shapes.max(1, keepdims=True)
|
||||||
|
wh0 = np.concatenate([l[:, 3:5] * s for s, l in zip(shapes, dataset.labels)]) # wh
|
||||||
|
|
||||||
|
# Filter
|
||||||
|
i = (wh0 < 3.0).any(1).sum()
|
||||||
|
if i:
|
||||||
|
print(f'{prefix}WARNING: Extremely small objects found. {i} of {len(wh0)} labels are < 3 pixels in size.')
|
||||||
|
wh = wh0[(wh0 >= 2.0).any(1)] # filter > 2 pixels
|
||||||
|
# wh = wh * (np.random.rand(wh.shape[0], 1) * 0.9 + 0.1) # multiply by random scale 0-1
|
||||||
|
|
||||||
|
# Kmeans calculation
|
||||||
|
print(f'{prefix}Running kmeans for {n} anchors on {len(wh)} points...')
|
||||||
|
s = wh.std(0) # sigmas for whitening
|
||||||
|
k, dist = kmeans(wh / s, n, iter=30) # points, mean distance
|
||||||
|
assert len(k) == n, print(f'{prefix}ERROR: scipy.cluster.vq.kmeans requested {n} points but returned only {len(k)}')
|
||||||
|
k *= s
|
||||||
|
wh = torch.tensor(wh, dtype=torch.float32) # filtered
|
||||||
|
wh0 = torch.tensor(wh0, dtype=torch.float32) # unfiltered
|
||||||
|
k = print_results(k)
|
||||||
|
|
||||||
|
# Plot
|
||||||
|
# k, d = [None] * 20, [None] * 20
|
||||||
|
# for i in tqdm(range(1, 21)):
|
||||||
|
# k[i-1], d[i-1] = kmeans(wh / s, i) # points, mean distance
|
||||||
|
# fig, ax = plt.subplots(1, 2, figsize=(14, 7), tight_layout=True)
|
||||||
|
# ax = ax.ravel()
|
||||||
|
# ax[0].plot(np.arange(1, 21), np.array(d) ** 2, marker='.')
|
||||||
|
# fig, ax = plt.subplots(1, 2, figsize=(14, 7)) # plot wh
|
||||||
|
# ax[0].hist(wh[wh[:, 0]<100, 0],400)
|
||||||
|
# ax[1].hist(wh[wh[:, 1]<100, 1],400)
|
||||||
|
# fig.savefig('wh.png', dpi=200)
|
||||||
|
|
||||||
|
# Evolve
|
||||||
|
npr = np.random
|
||||||
|
f, sh, mp, s = anchor_fitness(k), k.shape, 0.9, 0.1 # fitness, generations, mutation prob, sigma
|
||||||
|
pbar = tqdm(range(gen), desc=f'{prefix}Evolving anchors with Genetic Algorithm:') # progress bar
|
||||||
|
for _ in pbar:
|
||||||
|
v = np.ones(sh)
|
||||||
|
while (v == 1).all(): # mutate until a change occurs (prevent duplicates)
|
||||||
|
v = ((npr.random(sh) < mp) * npr.random() * npr.randn(*sh) * s + 1).clip(0.3, 3.0)
|
||||||
|
kg = (k.copy() * v).clip(min=2.0)
|
||||||
|
fg = anchor_fitness(kg)
|
||||||
|
if fg > f:
|
||||||
|
f, k = fg, kg.copy()
|
||||||
|
pbar.desc = f'{prefix}Evolving anchors with Genetic Algorithm: fitness = {f:.4f}'
|
||||||
|
if verbose:
|
||||||
|
print_results(k)
|
||||||
|
|
||||||
|
return print_results(k)
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
# AWS EC2 instance startup 'MIME' script https://aws.amazon.com/premiumsupport/knowledge-center/execute-user-data-ec2/
|
||||||
|
# This script will run on every instance restart, not only on first start
|
||||||
|
# --- DO NOT COPY ABOVE COMMENTS WHEN PASTING INTO USERDATA ---
|
||||||
|
|
||||||
|
Content-Type: multipart/mixed; boundary="//"
|
||||||
|
MIME-Version: 1.0
|
||||||
|
|
||||||
|
--//
|
||||||
|
Content-Type: text/cloud-config; charset="us-ascii"
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
Content-Disposition: attachment; filename="cloud-config.txt"
|
||||||
|
|
||||||
|
#cloud-config
|
||||||
|
cloud_final_modules:
|
||||||
|
- [scripts-user, always]
|
||||||
|
|
||||||
|
--//
|
||||||
|
Content-Type: text/x-shellscript; charset="us-ascii"
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
Content-Disposition: attachment; filename="userdata.txt"
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
# --- paste contents of userdata.sh here ---
|
||||||
|
--//
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Resume all interrupted trainings in yolov5/ dir including DDP trainings
|
||||||
|
# Usage: $ python utils/aws/resume.py
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import torch
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
sys.path.append('./') # to run '$ python *.py' files in subdirectories
|
||||||
|
|
||||||
|
port = 0 # --master_port
|
||||||
|
path = Path('').resolve()
|
||||||
|
for last in path.rglob('*/**/last.pt'):
|
||||||
|
ckpt = torch.load(last)
|
||||||
|
if ckpt['optimizer'] is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Load opt.yaml
|
||||||
|
with open(last.parent.parent / 'opt.yaml') as f:
|
||||||
|
opt = yaml.load(f, Loader=yaml.SafeLoader)
|
||||||
|
|
||||||
|
# Get device count
|
||||||
|
d = opt['device'].split(',') # devices
|
||||||
|
nd = len(d) # number of devices
|
||||||
|
ddp = nd > 1 or (nd == 0 and torch.cuda.device_count() > 1) # distributed data parallel
|
||||||
|
|
||||||
|
if ddp: # multi-GPU
|
||||||
|
port += 1
|
||||||
|
cmd = f'python -m torch.distributed.launch --nproc_per_node {nd} --master_port {port} train.py --resume {last}'
|
||||||
|
else: # single-GPU
|
||||||
|
cmd = f'python train.py --resume {last}'
|
||||||
|
|
||||||
|
cmd += ' > /dev/null 2>&1 &' # redirect output to dev/null and run in daemon thread
|
||||||
|
print(cmd)
|
||||||
|
os.system(cmd)
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# AWS EC2 instance startup script https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html
|
||||||
|
# This script will run only once on first instance start (for a re-start script see mime.sh)
|
||||||
|
# /home/ubuntu (ubuntu) or /home/ec2-user (amazon-linux) is working dir
|
||||||
|
# Use >300 GB SSD
|
||||||
|
|
||||||
|
cd home/ubuntu
|
||||||
|
if [ ! -d yolov5 ]; then
|
||||||
|
echo "Running first-time script." # install dependencies, download COCO, pull Docker
|
||||||
|
git clone https://github.com/ultralytics/yolov5 && sudo chmod -R 777 yolov5
|
||||||
|
cd yolov5
|
||||||
|
bash data/scripts/get_coco.sh && echo "Data done." &
|
||||||
|
sudo docker pull ultralytics/yolov5:latest && echo "Docker done." &
|
||||||
|
python -m pip install --upgrade pip && pip install -r requirements.txt && python detect.py && echo "Requirements done." &
|
||||||
|
wait && echo "All tasks done." # finish background tasks
|
||||||
|
else
|
||||||
|
echo "Running re-start script." # resume interrupted runs
|
||||||
|
i=0
|
||||||
|
list=$(sudo docker ps -qa) # container list i.e. $'one\ntwo\nthree\nfour'
|
||||||
|
while IFS= read -r id; do
|
||||||
|
((i++))
|
||||||
|
echo "restarting container $i: $id"
|
||||||
|
sudo docker start $id
|
||||||
|
# sudo docker exec -it $id python train.py --resume # single-GPU
|
||||||
|
sudo docker exec -d $id python utils/aws/resume.py # multi-scenario
|
||||||
|
done <<<"$list"
|
||||||
|
fi
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,51 @@
|
||||||
|
# Flask REST API
|
||||||
|
[REST](https://en.wikipedia.org/wiki/Representational_state_transfer) [API](https://en.wikipedia.org/wiki/API)s are commonly used to expose Machine Learning (ML) models to other services. This folder contains an example REST API created using Flask to expose the `yolov5s` model from [PyTorch Hub](https://pytorch.org/hub/ultralytics_yolov5/).
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
[Flask](https://palletsprojects.com/p/flask/) is required. Install with:
|
||||||
|
```shell
|
||||||
|
$ pip install Flask
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
After Flask installation run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ python3 restapi.py --port 5000
|
||||||
|
```
|
||||||
|
|
||||||
|
Then use [curl](https://curl.se/) to perform a request:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ curl -X POST -F image=@zidane.jpg 'http://localhost:5000/v1/object-detection/yolov5s'`
|
||||||
|
```
|
||||||
|
|
||||||
|
The model inference results are returned:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
[{'class': 0,
|
||||||
|
'confidence': 0.8197850585,
|
||||||
|
'name': 'person',
|
||||||
|
'xmax': 1159.1403808594,
|
||||||
|
'xmin': 750.912902832,
|
||||||
|
'ymax': 711.2583007812,
|
||||||
|
'ymin': 44.0350036621},
|
||||||
|
{'class': 0,
|
||||||
|
'confidence': 0.5667674541,
|
||||||
|
'name': 'person',
|
||||||
|
'xmax': 1065.5523681641,
|
||||||
|
'xmin': 116.0448303223,
|
||||||
|
'ymax': 713.8904418945,
|
||||||
|
'ymin': 198.4603881836},
|
||||||
|
{'class': 27,
|
||||||
|
'confidence': 0.5661227107,
|
||||||
|
'name': 'tie',
|
||||||
|
'xmax': 516.7975463867,
|
||||||
|
'xmin': 416.6880187988,
|
||||||
|
'ymax': 717.0524902344,
|
||||||
|
'ymin': 429.2020568848}]
|
||||||
|
```
|
||||||
|
|
||||||
|
An example python script to perform inference using [requests](https://docs.python-requests.org/en/master/) is given in `example_request.py`
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
"""Perform test request"""
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
DETECTION_URL = "http://localhost:5000/v1/object-detection/yolov5s"
|
||||||
|
TEST_IMAGE = "zidane.jpg"
|
||||||
|
|
||||||
|
image_data = open(TEST_IMAGE, "rb").read()
|
||||||
|
|
||||||
|
response = requests.post(DETECTION_URL, files={"image": image_data}).json()
|
||||||
|
|
||||||
|
pprint.pprint(response)
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
"""
|
||||||
|
Run a rest API exposing the yolov5s object detection model
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import io
|
||||||
|
|
||||||
|
import torch
|
||||||
|
from PIL import Image
|
||||||
|
from flask import Flask, request
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
DETECTION_URL = "/v1/object-detection/yolov5s"
|
||||||
|
|
||||||
|
|
||||||
|
@app.route(DETECTION_URL, methods=["POST"])
|
||||||
|
def predict():
|
||||||
|
if not request.method == "POST":
|
||||||
|
return
|
||||||
|
|
||||||
|
if request.files.get("image"):
|
||||||
|
image_file = request.files["image"]
|
||||||
|
image_bytes = image_file.read()
|
||||||
|
|
||||||
|
img = Image.open(io.BytesIO(image_bytes))
|
||||||
|
|
||||||
|
results = model(img, size=640)
|
||||||
|
data = results.pandas().xyxy[0].to_json(orient="records")
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Flask api exposing yolov5 model")
|
||||||
|
parser.add_argument("--port", default=5000, type=int, help="port number")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
model = torch.hub.load("ultralytics/yolov5", "yolov5s", force_reload=True).autoshape() # force_reload to recache
|
||||||
|
app.run(host="0.0.0.0", port=args.port) # debug=True causes Restarting with stat
|
||||||
|
|
@ -0,0 +1,604 @@
|
||||||
|
# YOLOv5 general utils
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import random
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import torch
|
||||||
|
import torchvision
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from utils.google_utils import gsutil_getsize
|
||||||
|
from utils.metrics import fitness
|
||||||
|
from utils.torch_utils import init_torch_seeds
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
torch.set_printoptions(linewidth=320, precision=5, profile='long')
|
||||||
|
np.set_printoptions(linewidth=320, formatter={'float_kind': '{:11.5g}'.format}) # format short g, %precision=5
|
||||||
|
pd.options.display.max_columns = 10
|
||||||
|
cv2.setNumThreads(0) # prevent OpenCV from multithreading (incompatible with PyTorch DataLoader)
|
||||||
|
os.environ['NUMEXPR_MAX_THREADS'] = str(min(os.cpu_count(), 8)) # NumExpr max threads
|
||||||
|
|
||||||
|
|
||||||
|
def set_logging(rank=-1):
|
||||||
|
logging.basicConfig(
|
||||||
|
format="%(message)s",
|
||||||
|
level=logging.INFO if rank in [-1, 0] else logging.WARN)
|
||||||
|
|
||||||
|
|
||||||
|
def init_seeds(seed=0):
|
||||||
|
# Initialize random number generator (RNG) seeds
|
||||||
|
random.seed(seed)
|
||||||
|
np.random.seed(seed)
|
||||||
|
init_torch_seeds(seed)
|
||||||
|
|
||||||
|
|
||||||
|
def get_latest_run(search_dir='.'):
|
||||||
|
# Return path to most recent 'last.pt' in /runs (i.e. to --resume from)
|
||||||
|
last_list = glob.glob(f'{search_dir}/**/last*.pt', recursive=True)
|
||||||
|
return max(last_list, key=os.path.getctime) if last_list else ''
|
||||||
|
|
||||||
|
|
||||||
|
def isdocker():
|
||||||
|
# Is environment a Docker container
|
||||||
|
return Path('/workspace').exists() # or Path('/.dockerenv').exists()
|
||||||
|
|
||||||
|
|
||||||
|
def emojis(str=''):
|
||||||
|
# Return platform-dependent emoji-safe version of string
|
||||||
|
return str.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else str
|
||||||
|
|
||||||
|
|
||||||
|
def check_online():
|
||||||
|
# Check internet connectivity
|
||||||
|
import socket
|
||||||
|
try:
|
||||||
|
socket.create_connection(("1.1.1.1", 443), 5) # check host accesability
|
||||||
|
return True
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def check_git_status():
|
||||||
|
# Recommend 'git pull' if code is out of date
|
||||||
|
print(colorstr('github: '), end='')
|
||||||
|
try:
|
||||||
|
assert Path('.git').exists(), 'skipping check (not a git repository)'
|
||||||
|
assert not isdocker(), 'skipping check (Docker image)'
|
||||||
|
assert check_online(), 'skipping check (offline)'
|
||||||
|
|
||||||
|
cmd = 'git fetch && git config --get remote.origin.url'
|
||||||
|
url = subprocess.check_output(cmd, shell=True).decode().strip().rstrip('.git') # github repo url
|
||||||
|
branch = subprocess.check_output('git rev-parse --abbrev-ref HEAD', shell=True).decode().strip() # checked out
|
||||||
|
n = int(subprocess.check_output(f'git rev-list {branch}..origin/master --count', shell=True)) # commits behind
|
||||||
|
if n > 0:
|
||||||
|
s = f"⚠️ WARNING: code is out of date by {n} commit{'s' * (n > 1)}. " \
|
||||||
|
f"Use 'git pull' to update or 'git clone {url}' to download latest."
|
||||||
|
else:
|
||||||
|
s = f'up to date with {url} ✅'
|
||||||
|
print(emojis(s)) # emoji-safe
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
def check_requirements(requirements='requirements.txt', exclude=()):
|
||||||
|
# Check installed dependencies meet requirements (pass *.txt file or list of packages)
|
||||||
|
import pkg_resources as pkg
|
||||||
|
prefix = colorstr('red', 'bold', 'requirements:')
|
||||||
|
if isinstance(requirements, (str, Path)): # requirements.txt file
|
||||||
|
file = Path(requirements)
|
||||||
|
if not file.exists():
|
||||||
|
print(f"{prefix} {file.resolve()} not found, check failed.")
|
||||||
|
return
|
||||||
|
requirements = [f'{x.name}{x.specifier}' for x in pkg.parse_requirements(file.open()) if x.name not in exclude]
|
||||||
|
else: # list or tuple of packages
|
||||||
|
requirements = [x for x in requirements if x not in exclude]
|
||||||
|
|
||||||
|
n = 0 # number of packages updates
|
||||||
|
for r in requirements:
|
||||||
|
try:
|
||||||
|
pkg.require(r)
|
||||||
|
except Exception as e: # DistributionNotFound or VersionConflict if requirements not met
|
||||||
|
n += 1
|
||||||
|
print(f"{prefix} {e.req} not found and is required by YOLOv5, attempting auto-update...")
|
||||||
|
print(subprocess.check_output(f"pip install {e.req}", shell=True).decode())
|
||||||
|
|
||||||
|
if n: # if packages updated
|
||||||
|
source = file.resolve() if 'file' in locals() else requirements
|
||||||
|
s = f"{prefix} {n} package{'s' * (n > 1)} updated per {source}\n" \
|
||||||
|
f"{prefix} ⚠️ {colorstr('bold', 'Restart runtime or rerun command for updates to take effect')}\n"
|
||||||
|
print(emojis(s)) # emoji-safe
|
||||||
|
|
||||||
|
|
||||||
|
def check_img_size(img_size, s=32):
|
||||||
|
# Verify img_size is a multiple of stride s
|
||||||
|
new_size = make_divisible(img_size, int(s)) # ceil gs-multiple
|
||||||
|
if new_size != img_size:
|
||||||
|
print('WARNING: --img-size %g must be multiple of max stride %g, updating to %g' % (img_size, s, new_size))
|
||||||
|
return new_size
|
||||||
|
|
||||||
|
|
||||||
|
def check_imshow():
|
||||||
|
# Check if environment supports image displays
|
||||||
|
try:
|
||||||
|
assert not isdocker(), 'cv2.imshow() is disabled in Docker environments'
|
||||||
|
cv2.imshow('test', np.zeros((1, 1, 3)))
|
||||||
|
cv2.waitKey(1)
|
||||||
|
cv2.destroyAllWindows()
|
||||||
|
cv2.waitKey(1)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f'WARNING: Environment does not support cv2.imshow() or PIL Image.show() image displays\n{e}')
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def check_file(file):
|
||||||
|
# Search for file if not found
|
||||||
|
if Path(file).is_file() or file == '':
|
||||||
|
return file
|
||||||
|
else:
|
||||||
|
files = glob.glob('./**/' + file, recursive=True) # find file
|
||||||
|
assert len(files), f'File Not Found: {file}' # assert file was found
|
||||||
|
assert len(files) == 1, f"Multiple files match '{file}', specify exact path: {files}" # assert unique
|
||||||
|
return files[0] # return file
|
||||||
|
|
||||||
|
|
||||||
|
def check_dataset(dict):
|
||||||
|
# Download dataset if not found locally
|
||||||
|
val, s = dict.get('val'), dict.get('download')
|
||||||
|
if val and len(val):
|
||||||
|
val = [Path(x).resolve() for x in (val if isinstance(val, list) else [val])] # val path
|
||||||
|
if not all(x.exists() for x in val):
|
||||||
|
print('\nWARNING: Dataset not found, nonexistent paths: %s' % [str(x) for x in val if not x.exists()])
|
||||||
|
if s and len(s): # download script
|
||||||
|
print('Downloading %s ...' % s)
|
||||||
|
if s.startswith('http') and s.endswith('.zip'): # URL
|
||||||
|
f = Path(s).name # filename
|
||||||
|
torch.hub.download_url_to_file(s, f)
|
||||||
|
r = os.system('unzip -q %s -d ../ && rm %s' % (f, f)) # unzip
|
||||||
|
else: # bash script
|
||||||
|
r = os.system(s)
|
||||||
|
print('Dataset autodownload %s\n' % ('success' if r == 0 else 'failure')) # analyze return value
|
||||||
|
else:
|
||||||
|
raise Exception('Dataset not found.')
|
||||||
|
|
||||||
|
|
||||||
|
def make_divisible(x, divisor):
|
||||||
|
# Returns x evenly divisible by divisor
|
||||||
|
return math.ceil(x / divisor) * divisor
|
||||||
|
|
||||||
|
|
||||||
|
def clean_str(s):
|
||||||
|
# Cleans a string by replacing special characters with underscore _
|
||||||
|
return re.sub(pattern="[|@#!¡·$€%&()=?¿^*;:,¨´><+]", repl="_", string=s)
|
||||||
|
|
||||||
|
|
||||||
|
def one_cycle(y1=0.0, y2=1.0, steps=100):
|
||||||
|
# lambda function for sinusoidal ramp from y1 to y2
|
||||||
|
return lambda x: ((1 - math.cos(x * math.pi / steps)) / 2) * (y2 - y1) + y1
|
||||||
|
|
||||||
|
|
||||||
|
def colorstr(*input):
|
||||||
|
# Colors a string https://en.wikipedia.org/wiki/ANSI_escape_code, i.e. colorstr('blue', 'hello world')
|
||||||
|
*args, string = input if len(input) > 1 else ('blue', 'bold', input[0]) # color arguments, string
|
||||||
|
colors = {'black': '\033[30m', # basic colors
|
||||||
|
'red': '\033[31m',
|
||||||
|
'green': '\033[32m',
|
||||||
|
'yellow': '\033[33m',
|
||||||
|
'blue': '\033[34m',
|
||||||
|
'magenta': '\033[35m',
|
||||||
|
'cyan': '\033[36m',
|
||||||
|
'white': '\033[37m',
|
||||||
|
'bright_black': '\033[90m', # bright colors
|
||||||
|
'bright_red': '\033[91m',
|
||||||
|
'bright_green': '\033[92m',
|
||||||
|
'bright_yellow': '\033[93m',
|
||||||
|
'bright_blue': '\033[94m',
|
||||||
|
'bright_magenta': '\033[95m',
|
||||||
|
'bright_cyan': '\033[96m',
|
||||||
|
'bright_white': '\033[97m',
|
||||||
|
'end': '\033[0m', # misc
|
||||||
|
'bold': '\033[1m',
|
||||||
|
'underline': '\033[4m'}
|
||||||
|
return ''.join(colors[x] for x in args) + f'{string}' + colors['end']
|
||||||
|
|
||||||
|
|
||||||
|
def labels_to_class_weights(labels, nc=80):
|
||||||
|
# Get class weights (inverse frequency) from training labels
|
||||||
|
if labels[0] is None: # no labels loaded
|
||||||
|
return torch.Tensor()
|
||||||
|
|
||||||
|
labels = np.concatenate(labels, 0) # labels.shape = (866643, 5) for COCO
|
||||||
|
classes = labels[:, 0].astype(np.int) # labels = [class xywh]
|
||||||
|
weights = np.bincount(classes, minlength=nc) # occurrences per class
|
||||||
|
|
||||||
|
# Prepend gridpoint count (for uCE training)
|
||||||
|
# gpi = ((320 / 32 * np.array([1, 2, 4])) ** 2 * 3).sum() # gridpoints per image
|
||||||
|
# weights = np.hstack([gpi * len(labels) - weights.sum() * 9, weights * 9]) ** 0.5 # prepend gridpoints to start
|
||||||
|
|
||||||
|
weights[weights == 0] = 1 # replace empty bins with 1
|
||||||
|
weights = 1 / weights # number of targets per class
|
||||||
|
weights /= weights.sum() # normalize
|
||||||
|
return torch.from_numpy(weights)
|
||||||
|
|
||||||
|
|
||||||
|
def labels_to_image_weights(labels, nc=80, class_weights=np.ones(80)):
|
||||||
|
# Produces image weights based on class_weights and image contents
|
||||||
|
class_counts = np.array([np.bincount(x[:, 0].astype(np.int), minlength=nc) for x in labels])
|
||||||
|
image_weights = (class_weights.reshape(1, nc) * class_counts).sum(1)
|
||||||
|
# index = random.choices(range(n), weights=image_weights, k=1) # weight image sample
|
||||||
|
return image_weights
|
||||||
|
|
||||||
|
|
||||||
|
def coco80_to_coco91_class(): # converts 80-index (val2014) to 91-index (paper)
|
||||||
|
# https://tech.amikelive.com/node-718/what-object-categories-labels-are-in-coco-dataset/
|
||||||
|
# a = np.loadtxt('data/coco.names', dtype='str', delimiter='\n')
|
||||||
|
# b = np.loadtxt('data/coco_paper.names', dtype='str', delimiter='\n')
|
||||||
|
# x1 = [list(a[i] == b).index(True) + 1 for i in range(80)] # darknet to coco
|
||||||
|
# x2 = [list(b[i] == a).index(True) if any(b[i] == a) else None for i in range(91)] # coco to darknet
|
||||||
|
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 31, 32, 33, 34,
|
||||||
|
35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||||
|
64, 65, 67, 70, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 84, 85, 86, 87, 88, 89, 90]
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def xyxy2xywh(x):
|
||||||
|
# Convert nx4 boxes from [x1, y1, x2, y2] to [x, y, w, h] where xy1=top-left, xy2=bottom-right
|
||||||
|
y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
|
||||||
|
y[:, 0] = (x[:, 0] + x[:, 2]) / 2 # x center
|
||||||
|
y[:, 1] = (x[:, 1] + x[:, 3]) / 2 # y center
|
||||||
|
y[:, 2] = x[:, 2] - x[:, 0] # width
|
||||||
|
y[:, 3] = x[:, 3] - x[:, 1] # height
|
||||||
|
return y
|
||||||
|
|
||||||
|
|
||||||
|
def xywh2xyxy(x):
|
||||||
|
# Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right
|
||||||
|
y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
|
||||||
|
y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x
|
||||||
|
y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y
|
||||||
|
y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x
|
||||||
|
y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y
|
||||||
|
return y
|
||||||
|
|
||||||
|
|
||||||
|
def xywhn2xyxy(x, w=640, h=640, padw=0, padh=0):
|
||||||
|
# Convert nx4 boxes from [x, y, w, h] normalized to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right
|
||||||
|
y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
|
||||||
|
y[:, 0] = w * (x[:, 0] - x[:, 2] / 2) + padw # top left x
|
||||||
|
y[:, 1] = h * (x[:, 1] - x[:, 3] / 2) + padh # top left y
|
||||||
|
y[:, 2] = w * (x[:, 0] + x[:, 2] / 2) + padw # bottom right x
|
||||||
|
y[:, 3] = h * (x[:, 1] + x[:, 3] / 2) + padh # bottom right y
|
||||||
|
return y
|
||||||
|
|
||||||
|
|
||||||
|
def xyn2xy(x, w=640, h=640, padw=0, padh=0):
|
||||||
|
# Convert normalized segments into pixel segments, shape (n,2)
|
||||||
|
y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
|
||||||
|
y[:, 0] = w * x[:, 0] + padw # top left x
|
||||||
|
y[:, 1] = h * x[:, 1] + padh # top left y
|
||||||
|
return y
|
||||||
|
|
||||||
|
|
||||||
|
def segment2box(segment, width=640, height=640):
|
||||||
|
# Convert 1 segment label to 1 box label, applying inside-image constraint, i.e. (xy1, xy2, ...) to (xyxy)
|
||||||
|
x, y = segment.T # segment xy
|
||||||
|
inside = (x >= 0) & (y >= 0) & (x <= width) & (y <= height)
|
||||||
|
x, y, = x[inside], y[inside]
|
||||||
|
return np.array([x.min(), y.min(), x.max(), y.max()]) if any(x) else np.zeros((1, 4)) # xyxy
|
||||||
|
|
||||||
|
|
||||||
|
def segments2boxes(segments):
|
||||||
|
# Convert segment labels to box labels, i.e. (cls, xy1, xy2, ...) to (cls, xywh)
|
||||||
|
boxes = []
|
||||||
|
for s in segments:
|
||||||
|
x, y = s.T # segment xy
|
||||||
|
boxes.append([x.min(), y.min(), x.max(), y.max()]) # cls, xyxy
|
||||||
|
return xyxy2xywh(np.array(boxes)) # cls, xywh
|
||||||
|
|
||||||
|
|
||||||
|
def resample_segments(segments, n=1000):
|
||||||
|
# Up-sample an (n,2) segment
|
||||||
|
for i, s in enumerate(segments):
|
||||||
|
x = np.linspace(0, len(s) - 1, n)
|
||||||
|
xp = np.arange(len(s))
|
||||||
|
segments[i] = np.concatenate([np.interp(x, xp, s[:, i]) for i in range(2)]).reshape(2, -1).T # segment xy
|
||||||
|
return segments
|
||||||
|
|
||||||
|
|
||||||
|
def scale_coords(img1_shape, coords, img0_shape, ratio_pad=None):
|
||||||
|
# Rescale coords (xyxy) from img1_shape to img0_shape
|
||||||
|
if ratio_pad is None: # calculate from img0_shape
|
||||||
|
gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / new
|
||||||
|
pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding
|
||||||
|
else:
|
||||||
|
gain = ratio_pad[0][0]
|
||||||
|
pad = ratio_pad[1]
|
||||||
|
|
||||||
|
coords[:, [0, 2]] -= pad[0] # x padding
|
||||||
|
coords[:, [1, 3]] -= pad[1] # y padding
|
||||||
|
coords[:, :4] /= gain
|
||||||
|
clip_coords(coords, img0_shape)
|
||||||
|
return coords
|
||||||
|
|
||||||
|
|
||||||
|
def clip_coords(boxes, img_shape):
|
||||||
|
# Clip bounding xyxy bounding boxes to image shape (height, width)
|
||||||
|
boxes[:, 0].clamp_(0, img_shape[1]) # x1
|
||||||
|
boxes[:, 1].clamp_(0, img_shape[0]) # y1
|
||||||
|
boxes[:, 2].clamp_(0, img_shape[1]) # x2
|
||||||
|
boxes[:, 3].clamp_(0, img_shape[0]) # y2
|
||||||
|
|
||||||
|
|
||||||
|
def bbox_iou(box1, box2, x1y1x2y2=True, GIoU=False, DIoU=False, CIoU=False, eps=1e-7):
|
||||||
|
# Returns the IoU of box1 to box2. box1 is 4, box2 is nx4
|
||||||
|
box2 = box2.T
|
||||||
|
|
||||||
|
# Get the coordinates of bounding boxes
|
||||||
|
if x1y1x2y2: # x1, y1, x2, y2 = box1
|
||||||
|
b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3]
|
||||||
|
b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3]
|
||||||
|
else: # transform from xywh to xyxy
|
||||||
|
b1_x1, b1_x2 = box1[0] - box1[2] / 2, box1[0] + box1[2] / 2
|
||||||
|
b1_y1, b1_y2 = box1[1] - box1[3] / 2, box1[1] + box1[3] / 2
|
||||||
|
b2_x1, b2_x2 = box2[0] - box2[2] / 2, box2[0] + box2[2] / 2
|
||||||
|
b2_y1, b2_y2 = box2[1] - box2[3] / 2, box2[1] + box2[3] / 2
|
||||||
|
|
||||||
|
# Intersection area
|
||||||
|
inter = (torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0) * \
|
||||||
|
(torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1)).clamp(0)
|
||||||
|
|
||||||
|
# Union Area
|
||||||
|
w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps
|
||||||
|
w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps
|
||||||
|
union = w1 * h1 + w2 * h2 - inter + eps
|
||||||
|
|
||||||
|
iou = inter / union
|
||||||
|
if GIoU or DIoU or CIoU:
|
||||||
|
cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1) # convex (smallest enclosing box) width
|
||||||
|
ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1) # convex height
|
||||||
|
if CIoU or DIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1
|
||||||
|
c2 = cw ** 2 + ch ** 2 + eps # convex diagonal squared
|
||||||
|
rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 +
|
||||||
|
(b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4 # center distance squared
|
||||||
|
if DIoU:
|
||||||
|
return iou - rho2 / c2 # DIoU
|
||||||
|
elif CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47
|
||||||
|
v = (4 / math.pi ** 2) * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2)
|
||||||
|
with torch.no_grad():
|
||||||
|
alpha = v / (v - iou + (1 + eps))
|
||||||
|
return iou - (rho2 / c2 + v * alpha) # CIoU
|
||||||
|
else: # GIoU https://arxiv.org/pdf/1902.09630.pdf
|
||||||
|
c_area = cw * ch + eps # convex area
|
||||||
|
return iou - (c_area - union) / c_area # GIoU
|
||||||
|
else:
|
||||||
|
return iou # IoU
|
||||||
|
|
||||||
|
|
||||||
|
def box_iou(box1, box2):
|
||||||
|
# https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.py
|
||||||
|
"""
|
||||||
|
Return intersection-over-union (Jaccard index) of boxes.
|
||||||
|
Both sets of boxes are expected to be in (x1, y1, x2, y2) format.
|
||||||
|
Arguments:
|
||||||
|
box1 (Tensor[N, 4])
|
||||||
|
box2 (Tensor[M, 4])
|
||||||
|
Returns:
|
||||||
|
iou (Tensor[N, M]): the NxM matrix containing the pairwise
|
||||||
|
IoU values for every element in boxes1 and boxes2
|
||||||
|
"""
|
||||||
|
|
||||||
|
def box_area(box):
|
||||||
|
# box = 4xn
|
||||||
|
return (box[2] - box[0]) * (box[3] - box[1])
|
||||||
|
|
||||||
|
area1 = box_area(box1.T)
|
||||||
|
area2 = box_area(box2.T)
|
||||||
|
|
||||||
|
# inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2)
|
||||||
|
inter = (torch.min(box1[:, None, 2:], box2[:, 2:]) - torch.max(box1[:, None, :2], box2[:, :2])).clamp(0).prod(2)
|
||||||
|
return inter / (area1[:, None] + area2 - inter) # iou = inter / (area1 + area2 - inter)
|
||||||
|
|
||||||
|
|
||||||
|
def wh_iou(wh1, wh2):
|
||||||
|
# Returns the nxm IoU matrix. wh1 is nx2, wh2 is mx2
|
||||||
|
wh1 = wh1[:, None] # [N,1,2]
|
||||||
|
wh2 = wh2[None] # [1,M,2]
|
||||||
|
inter = torch.min(wh1, wh2).prod(2) # [N,M]
|
||||||
|
return inter / (wh1.prod(2) + wh2.prod(2) - inter) # iou = inter / (area1 + area2 - inter)
|
||||||
|
|
||||||
|
|
||||||
|
def non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, multi_label=False,
|
||||||
|
labels=()):
|
||||||
|
"""Runs Non-Maximum Suppression (NMS) on inference results
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list of detections, on (n,6) tensor per image [xyxy, conf, cls]
|
||||||
|
"""
|
||||||
|
|
||||||
|
nc = prediction.shape[2] - 5 # number of classes
|
||||||
|
xc = prediction[..., 4] > conf_thres # candidates
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
min_wh, max_wh = 2, 4096 # (pixels) minimum and maximum box width and height
|
||||||
|
max_det = 300 # maximum number of detections per image
|
||||||
|
max_nms = 30000 # maximum number of boxes into torchvision.ops.nms()
|
||||||
|
time_limit = 10.0 # seconds to quit after
|
||||||
|
redundant = True # require redundant detections
|
||||||
|
multi_label &= nc > 1 # multiple labels per box (adds 0.5ms/img)
|
||||||
|
merge = False # use merge-NMS
|
||||||
|
|
||||||
|
t = time.time()
|
||||||
|
output = [torch.zeros((0, 6), device=prediction.device)] * prediction.shape[0]
|
||||||
|
for xi, x in enumerate(prediction): # image index, image inference
|
||||||
|
# Apply constraints
|
||||||
|
# x[((x[..., 2:4] < min_wh) | (x[..., 2:4] > max_wh)).any(1), 4] = 0 # width-height
|
||||||
|
x = x[xc[xi]] # confidence
|
||||||
|
|
||||||
|
# Cat apriori labels if autolabelling
|
||||||
|
if labels and len(labels[xi]):
|
||||||
|
l = labels[xi]
|
||||||
|
v = torch.zeros((len(l), nc + 5), device=x.device)
|
||||||
|
v[:, :4] = l[:, 1:5] # box
|
||||||
|
v[:, 4] = 1.0 # conf
|
||||||
|
v[range(len(l)), l[:, 0].long() + 5] = 1.0 # cls
|
||||||
|
x = torch.cat((x, v), 0)
|
||||||
|
|
||||||
|
# If none remain process next image
|
||||||
|
if not x.shape[0]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Compute conf
|
||||||
|
x[:, 5:] *= x[:, 4:5] # conf = obj_conf * cls_conf
|
||||||
|
|
||||||
|
# Box (center x, center y, width, height) to (x1, y1, x2, y2)
|
||||||
|
box = xywh2xyxy(x[:, :4])
|
||||||
|
|
||||||
|
# Detections matrix nx6 (xyxy, conf, cls)
|
||||||
|
if multi_label:
|
||||||
|
i, j = (x[:, 5:] > conf_thres).nonzero(as_tuple=False).T
|
||||||
|
x = torch.cat((box[i], x[i, j + 5, None], j[:, None].float()), 1)
|
||||||
|
else: # best class only
|
||||||
|
conf, j = x[:, 5:].max(1, keepdim=True)
|
||||||
|
x = torch.cat((box, conf, j.float()), 1)[conf.view(-1) > conf_thres]
|
||||||
|
|
||||||
|
# Filter by class
|
||||||
|
if classes is not None:
|
||||||
|
x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)]
|
||||||
|
|
||||||
|
# Apply finite constraint
|
||||||
|
# if not torch.isfinite(x).all():
|
||||||
|
# x = x[torch.isfinite(x).all(1)]
|
||||||
|
|
||||||
|
# Check shape
|
||||||
|
n = x.shape[0] # number of boxes
|
||||||
|
if not n: # no boxes
|
||||||
|
continue
|
||||||
|
elif n > max_nms: # excess boxes
|
||||||
|
x = x[x[:, 4].argsort(descending=True)[:max_nms]] # sort by confidence
|
||||||
|
|
||||||
|
# Batched NMS
|
||||||
|
c = x[:, 5:6] * (0 if agnostic else max_wh) # classes
|
||||||
|
boxes, scores = x[:, :4] + c, x[:, 4] # boxes (offset by class), scores
|
||||||
|
i = torchvision.ops.nms(boxes, scores, iou_thres) # NMS
|
||||||
|
if i.shape[0] > max_det: # limit detections
|
||||||
|
i = i[:max_det]
|
||||||
|
if merge and (1 < n < 3E3): # Merge NMS (boxes merged using weighted mean)
|
||||||
|
# update boxes as boxes(i,4) = weights(i,n) * boxes(n,4)
|
||||||
|
iou = box_iou(boxes[i], boxes) > iou_thres # iou matrix
|
||||||
|
weights = iou * scores[None] # box weights
|
||||||
|
x[i, :4] = torch.mm(weights, x[:, :4]).float() / weights.sum(1, keepdim=True) # merged boxes
|
||||||
|
if redundant:
|
||||||
|
i = i[iou.sum(1) > 1] # require redundancy
|
||||||
|
|
||||||
|
output[xi] = x[i]
|
||||||
|
if (time.time() - t) > time_limit:
|
||||||
|
print(f'WARNING: NMS time limit {time_limit}s exceeded')
|
||||||
|
break # time limit exceeded
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def strip_optimizer(f='best.pt', s=''): # from utils.general import *; strip_optimizer()
|
||||||
|
# Strip optimizer from 'f' to finalize training, optionally save as 's'
|
||||||
|
x = torch.load(f, map_location=torch.device('cpu'))
|
||||||
|
if x.get('ema'):
|
||||||
|
x['model'] = x['ema'] # replace model with ema
|
||||||
|
for k in 'optimizer', 'training_results', 'wandb_id', 'ema', 'updates': # keys
|
||||||
|
x[k] = None
|
||||||
|
x['epoch'] = -1
|
||||||
|
x['model'].half() # to FP16
|
||||||
|
for p in x['model'].parameters():
|
||||||
|
p.requires_grad = False
|
||||||
|
torch.save(x, s or f)
|
||||||
|
mb = os.path.getsize(s or f) / 1E6 # filesize
|
||||||
|
print(f"Optimizer stripped from {f},{(' saved as %s,' % s) if s else ''} {mb:.1f}MB")
|
||||||
|
|
||||||
|
|
||||||
|
def print_mutation(hyp, results, yaml_file='hyp_evolved.yaml', bucket=''):
|
||||||
|
# Print mutation results to evolve.txt (for use with train.py --evolve)
|
||||||
|
a = '%10s' * len(hyp) % tuple(hyp.keys()) # hyperparam keys
|
||||||
|
b = '%10.3g' * len(hyp) % tuple(hyp.values()) # hyperparam values
|
||||||
|
c = '%10.4g' * len(results) % results # results (P, R, mAP@0.5, mAP@0.5:0.95, val_losses x 3)
|
||||||
|
print('\n%s\n%s\nEvolved fitness: %s\n' % (a, b, c))
|
||||||
|
|
||||||
|
if bucket:
|
||||||
|
url = 'gs://%s/evolve.txt' % bucket
|
||||||
|
if gsutil_getsize(url) > (os.path.getsize('evolve.txt') if os.path.exists('evolve.txt') else 0):
|
||||||
|
os.system('gsutil cp %s .' % url) # download evolve.txt if larger than local
|
||||||
|
|
||||||
|
with open('evolve.txt', 'a') as f: # append result
|
||||||
|
f.write(c + b + '\n')
|
||||||
|
x = np.unique(np.loadtxt('evolve.txt', ndmin=2), axis=0) # load unique rows
|
||||||
|
x = x[np.argsort(-fitness(x))] # sort
|
||||||
|
np.savetxt('evolve.txt', x, '%10.3g') # save sort by fitness
|
||||||
|
|
||||||
|
# Save yaml
|
||||||
|
for i, k in enumerate(hyp.keys()):
|
||||||
|
hyp[k] = float(x[0, i + 7])
|
||||||
|
with open(yaml_file, 'w') as f:
|
||||||
|
results = tuple(x[0, :7])
|
||||||
|
c = '%10.4g' * len(results) % results # results (P, R, mAP@0.5, mAP@0.5:0.95, val_losses x 3)
|
||||||
|
f.write('# Hyperparameter Evolution Results\n# Generations: %g\n# Metrics: ' % len(x) + c + '\n\n')
|
||||||
|
yaml.dump(hyp, f, sort_keys=False)
|
||||||
|
|
||||||
|
if bucket:
|
||||||
|
os.system('gsutil cp evolve.txt %s gs://%s' % (yaml_file, bucket)) # upload
|
||||||
|
|
||||||
|
|
||||||
|
def apply_classifier(x, model, img, im0):
|
||||||
|
# applies a second stage classifier to yolo outputs
|
||||||
|
im0 = [im0] if isinstance(im0, np.ndarray) else im0
|
||||||
|
for i, d in enumerate(x): # per image
|
||||||
|
if d is not None and len(d):
|
||||||
|
d = d.clone()
|
||||||
|
|
||||||
|
# Reshape and pad cutouts
|
||||||
|
b = xyxy2xywh(d[:, :4]) # boxes
|
||||||
|
b[:, 2:] = b[:, 2:].max(1)[0].unsqueeze(1) # rectangle to square
|
||||||
|
b[:, 2:] = b[:, 2:] * 1.3 + 30 # pad
|
||||||
|
d[:, :4] = xywh2xyxy(b).long()
|
||||||
|
|
||||||
|
# Rescale boxes from img_size to im0 size
|
||||||
|
scale_coords(img.shape[2:], d[:, :4], im0[i].shape)
|
||||||
|
|
||||||
|
# Classes
|
||||||
|
pred_cls1 = d[:, 5].long()
|
||||||
|
ims = []
|
||||||
|
for j, a in enumerate(d): # per item
|
||||||
|
cutout = im0[i][int(a[1]):int(a[3]), int(a[0]):int(a[2])]
|
||||||
|
im = cv2.resize(cutout, (224, 224)) # BGR
|
||||||
|
# cv2.imwrite('test%i.jpg' % j, cutout)
|
||||||
|
|
||||||
|
im = im[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416
|
||||||
|
im = np.ascontiguousarray(im, dtype=np.float32) # uint8 to float32
|
||||||
|
im /= 255.0 # 0 - 255 to 0.0 - 1.0
|
||||||
|
ims.append(im)
|
||||||
|
|
||||||
|
pred_cls2 = model(torch.Tensor(ims).to(d.device)).argmax(1) # classifier prediction
|
||||||
|
x[i] = x[i][pred_cls1 == pred_cls2] # retain matching class detections
|
||||||
|
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def increment_path(path, exist_ok=True, sep=''):
|
||||||
|
# Increment path, i.e. runs/exp --> runs/exp{sep}0, runs/exp{sep}1 etc.
|
||||||
|
path = Path(path) # os-agnostic
|
||||||
|
if (path.exists() and exist_ok) or (not path.exists()):
|
||||||
|
return str(path)
|
||||||
|
else:
|
||||||
|
dirs = glob.glob(f"{path}{sep}*") # similar paths
|
||||||
|
matches = [re.search(rf"%s{sep}(\d+)" % path.stem, d) for d in dirs]
|
||||||
|
i = [int(m.groups()[0]) for m in matches if m] # indices
|
||||||
|
n = max(i) + 1 if i else 2 # increment number
|
||||||
|
return f"{path}{sep}{n}" # update path
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue