Merge `develop` branch into `master` (#3518)
Browse files* update ci-testing.yml (#3322)
* update ci-testing.yml
* update greetings.yml
* bring back os matrix
* update ci-testing.yml (#3322)
* update ci-testing.yml
* update greetings.yml
* bring back os matrix
* Enable direct `--weights URL` definition (#3373)
* Enable direct `--weights URL` definition
@KalenMike this PR will enable direct --weights URL definition. Example use case:
```
python train.py --weights https://storage.googleapis.com/bucket/dir/model.pt
```
* cleanup
* bug fixes
* weights = attempt_download(weights)
* Update experimental.py
* Update hubconf.py
* return bug fix
* comment mirror
* min_bytes
* Update tutorial.ipynb (#3368)
add Open in Kaggle badge
* `cv2.imread(img, -1)` for IMREAD_UNCHANGED (#3379)
* Update datasets.py
* comment
Co-authored-by: Glenn Jocher <glenn.jocher@ultralytics.com>
* COCO evolution fix (#3388)
* COCO evolution fix
* cleanup
* update print
* print fix
* Create `is_pip()` function (#3391)
Returns `True` if file is part of pip package. Useful for contextual behavior modification.
```python
def is_pip():
# Is file in a pip package?
return 'site-packages' in Path(__file__).absolute().parts
```
* Revert "`cv2.imread(img, -1)` for IMREAD_UNCHANGED (#3379)" (#3395)
This reverts commit 21a9607e00f1365b21d8c4bd81bdbf5fc0efea24.
* Update FLOPs description (#3422)
* Update README.md
* Changing FLOPS to FLOPs.
Co-authored-by: BuildTools <unconfigured@null.spigotmc.org>
* Parse URL authentication (#3424)
* Parse URL authentication
* urllib.parse.unquote()
* improved error handling
* improved error handling
* remove %3F
* update check_file()
* Add FLOPs title to table (#3453)
* Suppress jit trace warning + graph once (#3454)
* Suppress jit trace warning + graph once
Suppress harmless jit trace warning on TensorBoard add_graph call. Also fix multiple add_graph() calls bug, now only on batch 0.
* Update train.py
* Update MixUp augmentation `alpha=beta=32.0` (#3455)
Per VOC empirical results https://github.com/ultralytics/yolov5/issues/3380#issuecomment-853001307 by
@developer0hye
* Add `timeout()` class (#3460)
* Add `timeout()` class
* rearrange order
* Faster HSV augmentation (#3462)
remove datatype conversion process that can be skipped
* Add `check_git_status()` 5 second timeout (#3464)
* Add check_git_status() 5 second timeout
This should prevent the SSH Git bug that we were discussing @KalenMike
* cleanup
* replace timeout with check_output built-in timeout
* Improved `check_requirements()` offline-handling (#3466)
Improve robustness of `check_requirements()` function to offline environments (do not attempt pip installs when offline).
* Add `output_names` argument for ONNX export with dynamic axes (#3456)
* Add output names & dynamic axes for onnx export
Add output_names and dynamic_axes names for all outputs in torch.onnx.export. The first four outputs of the model will have names output0, output1, output2, output3
* use first output only + cleanup
Co-authored-by: Samridha Shrestha <samridha.shrestha@g42.ai>
Co-authored-by: Glenn Jocher <glenn.jocher@ultralytics.com>
* Revert FP16 `test.py` and `detect.py` inference to FP32 default (#3423)
* fixed inference bug ,while use half precision
* replace --use-half with --half
* replace space and PEP8 in detect.py
* PEP8 detect.py
* update --half help comment
* Update test.py
* revert space
Co-authored-by: Glenn Jocher <glenn.jocher@ultralytics.com>
* Add additional links/resources to stale.yml message (#3467)
* Update stale.yml
* cleanup
* Update stale.yml
* reformat
* Update stale.yml HUB URL (#3468)
* Stale `github.actor` bug fix (#3483)
* Explicit `model.eval()` call `if opt.train=False` (#3475)
* call model.eval() when opt.train is False
call model.eval() when opt.train is False
* single-line if statement
* cleanup
Co-authored-by: Glenn Jocher <glenn.jocher@ultralytics.com>
* check_requirements() exclude `opencv-python` (#3495)
Fix for 3rd party or contrib versions of installed OpenCV as in https://github.com/ultralytics/yolov5/issues/3494.
* Earlier `assert` for cpu and half option (#3508)
* early assert for cpu and half option
early assert for cpu and half option
* Modified comment
Modified comment
* Update tutorial.ipynb (#3510)
* Reduce test.py results spacing (#3511)
* Update README.md (#3512)
* Update README.md
Minor modifications
* 850 width
* Update greetings.yml
revert greeting change as PRs will now merge to master.
Co-authored-by: Piotr Skalski <SkalskiP@users.noreply.github.com>
Co-authored-by: SkalskiP <piotr.skalski92@gmail.com>
Co-authored-by: Peretz Cohen <pizzaz93@users.noreply.github.com>
Co-authored-by: tudoulei <34886368+tudoulei@users.noreply.github.com>
Co-authored-by: chocosaj <chocosaj@users.noreply.github.com>
Co-authored-by: BuildTools <unconfigured@null.spigotmc.org>
Co-authored-by: Yonghye Kwon <developer.0hye@gmail.com>
Co-authored-by: Sam_S <SamSamhuns@users.noreply.github.com>
Co-authored-by: Samridha Shrestha <samridha.shrestha@g42.ai>
Co-authored-by: edificewang <609552430@qq.com>
- .github/workflows/ci-testing.yml +2 -4
- .github/workflows/stale.yml +20 -2
- README.md +13 -13
- detect.py +2 -1
- hubconf.py +1 -2
- models/experimental.py +1 -2
- models/export.py +9 -9
- models/yolo.py +3 -3
- requirements.txt +1 -1
- test.py +4 -2
- train.py +36 -36
- tutorial.ipynb +7 -6
- utils/datasets.py +3 -3
- utils/general.py +42 -12
- utils/google_utils.py +37 -21
- utils/torch_utils.py +6 -6
@@ -2,12 +2,10 @@ name: CI CPU testing
|
|
2 |
|
3 |
on: # https://help.github.com/en/actions/reference/events-that-trigger-workflows
|
4 |
push:
|
5 |
-
branches: [ master ]
|
6 |
pull_request:
|
7 |
# The branches below must be a subset of the branches above
|
8 |
-
branches: [ master ]
|
9 |
-
schedule:
|
10 |
-
- cron: '0 0 * * *' # Runs at 00:00 UTC every day
|
11 |
|
12 |
jobs:
|
13 |
cpu-tests:
|
|
|
2 |
|
3 |
on: # https://help.github.com/en/actions/reference/events-that-trigger-workflows
|
4 |
push:
|
5 |
+
branches: [ master, develop ]
|
6 |
pull_request:
|
7 |
# The branches below must be a subset of the branches above
|
8 |
+
branches: [ master, develop ]
|
|
|
|
|
9 |
|
10 |
jobs:
|
11 |
cpu-tests:
|
@@ -10,8 +10,26 @@ jobs:
|
|
10 |
- uses: actions/stale@v3
|
11 |
with:
|
12 |
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
13 |
-
stale-issue-message:
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
days-before-stale: 30
|
16 |
days-before-close: 5
|
17 |
exempt-issue-labels: 'documentation,tutorial'
|
|
|
10 |
- uses: actions/stale@v3
|
11 |
with:
|
12 |
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
13 |
+
stale-issue-message: |
|
14 |
+
👋 Hello, this issue has been automatically marked as stale because it has not had recent activity. Please note it will be closed if no further activity occurs.
|
15 |
+
|
16 |
+
Access additional [YOLOv5](https://ultralytics.com/yolov5) 🚀 resources:
|
17 |
+
- **Wiki** – https://github.com/ultralytics/yolov5/wiki
|
18 |
+
- **Tutorials** – https://github.com/ultralytics/yolov5#tutorials
|
19 |
+
- **Docs** – https://docs.ultralytics.com
|
20 |
+
|
21 |
+
Access additional [Ultralytics](https://ultralytics.com) ⚡ resources:
|
22 |
+
- **Ultralytics HUB** – https://ultralytics.com/pricing
|
23 |
+
- **Vision API** – https://ultralytics.com/yolov5
|
24 |
+
- **About Us** – https://ultralytics.com/about
|
25 |
+
- **Join Our Team** – https://ultralytics.com/work
|
26 |
+
- **Contact Us** – https://ultralytics.com/contact
|
27 |
+
|
28 |
+
Feel free to inform us of any other **issues** you discover or **feature requests** that come to mind in the future. Pull Requests (PRs) are also always welcomed!
|
29 |
+
|
30 |
+
Thank you for your contributions to YOLOv5 🚀 and Vision AI ⭐!
|
31 |
+
|
32 |
+
stale-pr-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions YOLOv5 🚀 and Vision AI ⭐.'
|
33 |
days-before-stale: 30
|
34 |
days-before-close: 5
|
35 |
exempt-issue-labels: 'documentation,tutorial'
|
@@ -1,5 +1,5 @@
|
|
1 |
<a align="left" href="https://apps.apple.com/app/id1452689527" target="_blank">
|
2 |
-
<img width="
|
3 |
 
|
4 |
|
5 |
<a href="https://github.com/ultralytics/yolov5/actions"><img src="https://github.com/ultralytics/yolov5/workflows/CI%20CPU%20testing/badge.svg" alt="CI CPU testing"></a>
|
@@ -30,19 +30,19 @@ This repository represents Ultralytics open-source research into future object d
|
|
30 |
|
31 |
[assets]: https://github.com/ultralytics/yolov5/releases
|
32 |
|
33 |
-
Model |size<br><sup>(pixels) |mAP<sup>val<br>0.5:0.95 |mAP<sup>test<br>0.5:0.95 |mAP<sup>val<br>0.5 |Speed<br><sup>V100 (ms) | |params<br><sup>(M) |
|
34 |
-
|
35 |
-
[YOLOv5s][assets] |640 |36.7 |36.7 |55.4 |**2.0** | |7.3 |17.0
|
36 |
-
[YOLOv5m][assets] |640 |44.5 |44.5 |63.1 |2.7 | |21.4 |51.3
|
37 |
-
[YOLOv5l][assets] |640 |48.2 |48.2 |66.9 |3.8 | |47.0 |115.4
|
38 |
-
[YOLOv5x][assets] |640 |**50.4** |**50.4** |**68.8** |6.1 | |87.7 |218.8
|
39 |
| | | | | | || |
|
40 |
-
[YOLOv5s6][assets] |1280 |43.3 |43.3 |61.9 |**4.3** | |12.7 |17.4
|
41 |
-
[YOLOv5m6][assets] |1280 |50.5 |50.5 |68.7 |8.4 | |35.9 |52.4
|
42 |
-
[YOLOv5l6][assets] |1280 |53.4 |53.4 |71.1 |12.3 | |77.2 |117.7
|
43 |
-
[YOLOv5x6][assets] |1280 |**54.4** |**54.4** |**72.0** |22.4 | |141.8 |222.9
|
44 |
| | | | | | || |
|
45 |
-
[YOLOv5x6][assets] TTA |1280 |**55.0** |**55.0** |**72.0** |70.8 | |- |-
|
46 |
|
47 |
<details>
|
48 |
<summary>Table Notes (click to expand)</summary>
|
@@ -112,7 +112,7 @@ Namespace(agnostic_nms=False, augment=False, classes=None, conf_thres=0.25, devi
|
|
112 |
YOLOv5 v4.0-96-g83dc1b4 torch 1.7.0+cu101 CUDA:0 (Tesla V100-SXM2-16GB, 16160.5MB)
|
113 |
|
114 |
Fusing layers...
|
115 |
-
Model Summary: 224 layers, 7266973 parameters, 0 gradients, 17.0
|
116 |
image 1/2 /content/yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, Done. (0.010s)
|
117 |
image 2/2 /content/yolov5/data/images/zidane.jpg: 384x640 2 persons, 1 tie, Done. (0.011s)
|
118 |
Results saved to runs/detect/exp2
|
|
|
1 |
<a align="left" href="https://apps.apple.com/app/id1452689527" target="_blank">
|
2 |
+
<img width="850" src="https://user-images.githubusercontent.com/26833433/121094150-72607500-c7ee-11eb-9f39-1d9e4ce89a9e.jpg"></a>
|
3 |
 
|
4 |
|
5 |
<a href="https://github.com/ultralytics/yolov5/actions"><img src="https://github.com/ultralytics/yolov5/workflows/CI%20CPU%20testing/badge.svg" alt="CI CPU testing"></a>
|
|
|
30 |
|
31 |
[assets]: https://github.com/ultralytics/yolov5/releases
|
32 |
|
33 |
+
|Model |size<br><sup>(pixels) |mAP<sup>val<br>0.5:0.95 |mAP<sup>test<br>0.5:0.95 |mAP<sup>val<br>0.5 |Speed<br><sup>V100 (ms) | |params<br><sup>(M) |FLOPs<br><sup>640 (B)
|
34 |
+
|--- |--- |--- |--- |--- |--- |---|--- |---
|
35 |
+
|[YOLOv5s][assets] |640 |36.7 |36.7 |55.4 |**2.0** | |7.3 |17.0
|
36 |
+
|[YOLOv5m][assets] |640 |44.5 |44.5 |63.1 |2.7 | |21.4 |51.3
|
37 |
+
|[YOLOv5l][assets] |640 |48.2 |48.2 |66.9 |3.8 | |47.0 |115.4
|
38 |
+
|[YOLOv5x][assets] |640 |**50.4** |**50.4** |**68.8** |6.1 | |87.7 |218.8
|
39 |
| | | | | | || |
|
40 |
+
|[YOLOv5s6][assets] |1280 |43.3 |43.3 |61.9 |**4.3** | |12.7 |17.4
|
41 |
+
|[YOLOv5m6][assets] |1280 |50.5 |50.5 |68.7 |8.4 | |35.9 |52.4
|
42 |
+
|[YOLOv5l6][assets] |1280 |53.4 |53.4 |71.1 |12.3 | |77.2 |117.7
|
43 |
+
|[YOLOv5x6][assets] |1280 |**54.4** |**54.4** |**72.0** |22.4 | |141.8 |222.9
|
44 |
| | | | | | || |
|
45 |
+
|[YOLOv5x6][assets] TTA |1280 |**55.0** |**55.0** |**72.0** |70.8 | |- |-
|
46 |
|
47 |
<details>
|
48 |
<summary>Table Notes (click to expand)</summary>
|
|
|
112 |
YOLOv5 v4.0-96-g83dc1b4 torch 1.7.0+cu101 CUDA:0 (Tesla V100-SXM2-16GB, 16160.5MB)
|
113 |
|
114 |
Fusing layers...
|
115 |
+
Model Summary: 224 layers, 7266973 parameters, 0 gradients, 17.0 GFLOPs
|
116 |
image 1/2 /content/yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, Done. (0.010s)
|
117 |
image 2/2 /content/yolov5/data/images/zidane.jpg: 384x640 2 persons, 1 tie, Done. (0.011s)
|
118 |
Results saved to runs/detect/exp2
|
@@ -28,7 +28,7 @@ def detect(opt):
|
|
28 |
# Initialize
|
29 |
set_logging()
|
30 |
device = select_device(opt.device)
|
31 |
-
half = device.type != 'cpu' # half precision only supported on CUDA
|
32 |
|
33 |
# Load model
|
34 |
model = attempt_load(weights, map_location=device) # load FP32 model
|
@@ -172,6 +172,7 @@ if __name__ == '__main__':
|
|
172 |
parser.add_argument('--line-thickness', default=3, type=int, help='bounding box thickness (pixels)')
|
173 |
parser.add_argument('--hide-labels', default=False, action='store_true', help='hide labels')
|
174 |
parser.add_argument('--hide-conf', default=False, action='store_true', help='hide confidences')
|
|
|
175 |
opt = parser.parse_args()
|
176 |
print(opt)
|
177 |
check_requirements(exclude=('tensorboard', 'pycocotools', 'thop'))
|
|
|
28 |
# Initialize
|
29 |
set_logging()
|
30 |
device = select_device(opt.device)
|
31 |
+
half = opt.half and device.type != 'cpu' # half precision only supported on CUDA
|
32 |
|
33 |
# Load model
|
34 |
model = attempt_load(weights, map_location=device) # load FP32 model
|
|
|
172 |
parser.add_argument('--line-thickness', default=3, type=int, help='bounding box thickness (pixels)')
|
173 |
parser.add_argument('--hide-labels', default=False, action='store_true', help='hide labels')
|
174 |
parser.add_argument('--hide-conf', default=False, action='store_true', help='hide confidences')
|
175 |
+
parser.add_argument('--half', type=bool, default=False, help='use FP16 half-precision inference')
|
176 |
opt = parser.parse_args()
|
177 |
print(opt)
|
178 |
check_requirements(exclude=('tensorboard', 'pycocotools', 'thop'))
|
@@ -42,8 +42,7 @@ def _create(name, pretrained=True, channels=3, classes=80, autoshape=True, verbo
|
|
42 |
cfg = list((Path(__file__).parent / 'models').rglob(f'{name}.yaml'))[0] # model.yaml path
|
43 |
model = Model(cfg, channels, classes) # create model
|
44 |
if pretrained:
|
45 |
-
attempt_download(fname) #
|
46 |
-
ckpt = torch.load(fname, map_location=torch.device('cpu')) # load
|
47 |
msd = model.state_dict() # model state_dict
|
48 |
csd = ckpt['model'].float().state_dict() # checkpoint state_dict as FP32
|
49 |
csd = {k: v for k, v in csd.items() if msd[k].shape == v.shape} # filter
|
|
|
42 |
cfg = list((Path(__file__).parent / 'models').rglob(f'{name}.yaml'))[0] # model.yaml path
|
43 |
model = Model(cfg, channels, classes) # create model
|
44 |
if pretrained:
|
45 |
+
ckpt = torch.load(attempt_download(fname), map_location=torch.device('cpu')) # load
|
|
|
46 |
msd = model.state_dict() # model state_dict
|
47 |
csd = ckpt['model'].float().state_dict() # checkpoint state_dict as FP32
|
48 |
csd = {k: v for k, v in csd.items() if msd[k].shape == v.shape} # filter
|
@@ -116,8 +116,7 @@ def attempt_load(weights, map_location=None, inplace=True):
|
|
116 |
# Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a
|
117 |
model = Ensemble()
|
118 |
for w in weights if isinstance(weights, list) else [weights]:
|
119 |
-
attempt_download(w)
|
120 |
-
ckpt = torch.load(w, map_location=map_location) # load
|
121 |
model.append(ckpt['ema' if ckpt.get('ema') else 'model'].float().fuse().eval()) # FP32 model
|
122 |
|
123 |
# Compatibility updates
|
|
|
116 |
# Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a
|
117 |
model = Ensemble()
|
118 |
for w in weights if isinstance(weights, list) else [weights]:
|
119 |
+
ckpt = torch.load(attempt_download(w), map_location=map_location) # load
|
|
|
120 |
model.append(ckpt['ema' if ckpt.get('ema') else 'model'].float().fuse().eval()) # FP32 model
|
121 |
|
122 |
# Compatibility updates
|
@@ -44,22 +44,19 @@ if __name__ == '__main__':
|
|
44 |
|
45 |
# Load PyTorch model
|
46 |
device = select_device(opt.device)
|
|
|
47 |
model = attempt_load(opt.weights, map_location=device) # load FP32 model
|
48 |
labels = model.names
|
49 |
|
50 |
-
#
|
51 |
gs = int(max(model.stride)) # grid size (max stride)
|
52 |
opt.img_size = [check_img_size(x, gs) for x in opt.img_size] # verify img_size are gs-multiples
|
53 |
-
assert not (opt.device.lower() == 'cpu' and opt.half), '--half only compatible with GPU export, i.e. use --device 0'
|
54 |
-
|
55 |
-
# Input
|
56 |
img = torch.zeros(opt.batch_size, 3, *opt.img_size).to(device) # image size(1,3,320,192) iDetection
|
57 |
|
58 |
# Update model
|
59 |
if opt.half:
|
60 |
img, model = img.half(), model.half() # to FP16
|
61 |
-
if opt.train
|
62 |
-
model.train() # training mode (no grid construction in Detect layer)
|
63 |
for k, m in model.named_modules():
|
64 |
m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility
|
65 |
if isinstance(m, models.common.Conv): # assign export-friendly activations
|
@@ -96,11 +93,14 @@ if __name__ == '__main__':
|
|
96 |
|
97 |
print(f'{prefix} starting export with onnx {onnx.__version__}...')
|
98 |
f = opt.weights.replace('.pt', '.onnx') # filename
|
99 |
-
torch.onnx.export(model, img, f, verbose=False, opset_version=opt.opset_version,
|
100 |
training=torch.onnx.TrainingMode.TRAINING if opt.train else torch.onnx.TrainingMode.EVAL,
|
101 |
do_constant_folding=not opt.train,
|
102 |
-
|
103 |
-
|
|
|
|
|
|
|
104 |
|
105 |
# Checks
|
106 |
model_onnx = onnx.load(f) # load onnx model
|
|
|
44 |
|
45 |
# Load PyTorch model
|
46 |
device = select_device(opt.device)
|
47 |
+
assert not (opt.device.lower() == 'cpu' and opt.half), '--half only compatible with GPU export, i.e. use --device 0'
|
48 |
model = attempt_load(opt.weights, map_location=device) # load FP32 model
|
49 |
labels = model.names
|
50 |
|
51 |
+
# Input
|
52 |
gs = int(max(model.stride)) # grid size (max stride)
|
53 |
opt.img_size = [check_img_size(x, gs) for x in opt.img_size] # verify img_size are gs-multiples
|
|
|
|
|
|
|
54 |
img = torch.zeros(opt.batch_size, 3, *opt.img_size).to(device) # image size(1,3,320,192) iDetection
|
55 |
|
56 |
# Update model
|
57 |
if opt.half:
|
58 |
img, model = img.half(), model.half() # to FP16
|
59 |
+
model.train() if opt.train else model.eval() # training mode = no Detect() layer grid construction
|
|
|
60 |
for k, m in model.named_modules():
|
61 |
m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility
|
62 |
if isinstance(m, models.common.Conv): # assign export-friendly activations
|
|
|
93 |
|
94 |
print(f'{prefix} starting export with onnx {onnx.__version__}...')
|
95 |
f = opt.weights.replace('.pt', '.onnx') # filename
|
96 |
+
torch.onnx.export(model, img, f, verbose=False, opset_version=opt.opset_version,
|
97 |
training=torch.onnx.TrainingMode.TRAINING if opt.train else torch.onnx.TrainingMode.EVAL,
|
98 |
do_constant_folding=not opt.train,
|
99 |
+
input_names=['images'],
|
100 |
+
output_names=['output'],
|
101 |
+
dynamic_axes={'images': {0: 'batch', 2: 'height', 3: 'width'}, # shape(1,3,640,640)
|
102 |
+
'output': {0: 'batch', 1: 'anchors'} # shape(1,25200,85)
|
103 |
+
} if opt.dynamic else None)
|
104 |
|
105 |
# Checks
|
106 |
model_onnx = onnx.load(f) # load onnx model
|
@@ -21,7 +21,7 @@ from utils.torch_utils import time_synchronized, fuse_conv_and_bn, model_info, s
|
|
21 |
select_device, copy_attr
|
22 |
|
23 |
try:
|
24 |
-
import thop # for
|
25 |
except ImportError:
|
26 |
thop = None
|
27 |
|
@@ -140,13 +140,13 @@ class Model(nn.Module):
|
|
140 |
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
|
141 |
|
142 |
if profile:
|
143 |
-
o = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2 if thop else 0 #
|
144 |
t = time_synchronized()
|
145 |
for _ in range(10):
|
146 |
_ = m(x)
|
147 |
dt.append((time_synchronized() - t) * 100)
|
148 |
if m == self.model[0]:
|
149 |
-
logger.info(f"{'time (ms)':>10s} {'
|
150 |
logger.info(f'{dt[-1]:10.2f} {o:10.2f} {m.np:10.0f} {m.type}')
|
151 |
|
152 |
x = m(x) # run
|
|
|
21 |
select_device, copy_attr
|
22 |
|
23 |
try:
|
24 |
+
import thop # for FLOPs computation
|
25 |
except ImportError:
|
26 |
thop = None
|
27 |
|
|
|
140 |
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
|
141 |
|
142 |
if profile:
|
143 |
+
o = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2 if thop else 0 # FLOPs
|
144 |
t = time_synchronized()
|
145 |
for _ in range(10):
|
146 |
_ = m(x)
|
147 |
dt.append((time_synchronized() - t) * 100)
|
148 |
if m == self.model[0]:
|
149 |
+
logger.info(f"{'time (ms)':>10s} {'GFLOPs':>10s} {'params':>10s} {'module'}")
|
150 |
logger.info(f'{dt[-1]:10.2f} {o:10.2f} {m.np:10.0f} {m.type}')
|
151 |
|
152 |
x = m(x) # run
|
@@ -27,4 +27,4 @@ pandas
|
|
27 |
# extras --------------------------------------
|
28 |
# Cython # for pycocotools https://github.com/cocodataset/cocoapi/issues/172
|
29 |
pycocotools>=2.0 # COCO mAP
|
30 |
-
thop #
|
|
|
27 |
# extras --------------------------------------
|
28 |
# Cython # for pycocotools https://github.com/cocodataset/cocoapi/issues/172
|
29 |
pycocotools>=2.0 # COCO mAP
|
30 |
+
thop # FLOPs computation
|
@@ -95,7 +95,7 @@ def test(data,
|
|
95 |
confusion_matrix = ConfusionMatrix(nc=nc)
|
96 |
names = {k: v for k, v in enumerate(model.names if hasattr(model, 'names') else model.module.names)}
|
97 |
coco91class = coco80_to_coco91_class()
|
98 |
-
s = ('%20s' + '%
|
99 |
p, r, f1, mp, mr, map50, map, t0, t1 = 0., 0., 0., 0., 0., 0., 0., 0., 0.
|
100 |
loss = torch.zeros(3, device=device)
|
101 |
jdict, stats, ap, ap_class, wandb_images = [], [], [], [], []
|
@@ -228,7 +228,7 @@ def test(data,
|
|
228 |
nt = torch.zeros(1)
|
229 |
|
230 |
# Print results
|
231 |
-
pf = '%20s' + '%
|
232 |
print(pf % ('all', seen, nt.sum(), mp, mr, map50, map))
|
233 |
|
234 |
# Print results per class
|
@@ -306,6 +306,7 @@ if __name__ == '__main__':
|
|
306 |
parser.add_argument('--project', default='runs/test', help='save to project/name')
|
307 |
parser.add_argument('--name', default='exp', help='save to project/name')
|
308 |
parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
|
|
|
309 |
opt = parser.parse_args()
|
310 |
opt.save_json |= opt.data.endswith('coco.yaml')
|
311 |
opt.data = check_file(opt.data) # check file
|
@@ -326,6 +327,7 @@ if __name__ == '__main__':
|
|
326 |
save_txt=opt.save_txt | opt.save_hybrid,
|
327 |
save_hybrid=opt.save_hybrid,
|
328 |
save_conf=opt.save_conf,
|
|
|
329 |
opt=opt
|
330 |
)
|
331 |
|
|
|
95 |
confusion_matrix = ConfusionMatrix(nc=nc)
|
96 |
names = {k: v for k, v in enumerate(model.names if hasattr(model, 'names') else model.module.names)}
|
97 |
coco91class = coco80_to_coco91_class()
|
98 |
+
s = ('%20s' + '%11s' * 6) % ('Class', 'Images', 'Labels', 'P', 'R', 'mAP@.5', 'mAP@.5:.95')
|
99 |
p, r, f1, mp, mr, map50, map, t0, t1 = 0., 0., 0., 0., 0., 0., 0., 0., 0.
|
100 |
loss = torch.zeros(3, device=device)
|
101 |
jdict, stats, ap, ap_class, wandb_images = [], [], [], [], []
|
|
|
228 |
nt = torch.zeros(1)
|
229 |
|
230 |
# Print results
|
231 |
+
pf = '%20s' + '%11i' * 2 + '%11.3g' * 4 # print format
|
232 |
print(pf % ('all', seen, nt.sum(), mp, mr, map50, map))
|
233 |
|
234 |
# Print results per class
|
|
|
306 |
parser.add_argument('--project', default='runs/test', help='save to project/name')
|
307 |
parser.add_argument('--name', default='exp', help='save to project/name')
|
308 |
parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
|
309 |
+
parser.add_argument('--half', type=bool, default=False, help='use FP16 half-precision inference')
|
310 |
opt = parser.parse_args()
|
311 |
opt.save_json |= opt.data.endswith('coco.yaml')
|
312 |
opt.data = check_file(opt.data) # check file
|
|
|
327 |
save_txt=opt.save_txt | opt.save_hybrid,
|
328 |
save_hybrid=opt.save_hybrid,
|
329 |
save_conf=opt.save_conf,
|
330 |
+
half_precision=opt.half,
|
331 |
opt=opt
|
332 |
)
|
333 |
|
@@ -4,6 +4,7 @@ import math
|
|
4 |
import os
|
5 |
import random
|
6 |
import time
|
|
|
7 |
from copy import deepcopy
|
8 |
from pathlib import Path
|
9 |
from threading import Thread
|
@@ -62,7 +63,6 @@ def train(hyp, opt, device, tb_writer=None):
|
|
62 |
init_seeds(2 + rank)
|
63 |
with open(opt.data) as f:
|
64 |
data_dict = yaml.safe_load(f) # data dict
|
65 |
-
is_coco = opt.data.endswith('coco.yaml')
|
66 |
|
67 |
# Logging- Doing this before checking the dataset. Might update data_dict
|
68 |
loggers = {'wandb': None} # loggers dict
|
@@ -78,12 +78,13 @@ def train(hyp, opt, device, tb_writer=None):
|
|
78 |
nc = 1 if opt.single_cls else int(data_dict['nc']) # number of classes
|
79 |
names = ['item'] if opt.single_cls and len(data_dict['names']) != 1 else data_dict['names'] # class names
|
80 |
assert len(names) == nc, '%g names found for nc=%g dataset in %s' % (len(names), nc, opt.data) # check
|
|
|
81 |
|
82 |
# Model
|
83 |
pretrained = weights.endswith('.pt')
|
84 |
if pretrained:
|
85 |
with torch_distributed_zero_first(rank):
|
86 |
-
attempt_download(weights) # download if not found locally
|
87 |
ckpt = torch.load(weights, map_location=device) # load checkpoint
|
88 |
model = Model(opt.cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # create
|
89 |
exclude = ['anchor'] if (opt.cfg or hyp.get('anchors')) and not opt.resume else [] # exclude keys
|
@@ -323,18 +324,19 @@ def train(hyp, opt, device, tb_writer=None):
|
|
323 |
mloss = (mloss * i + loss_items) / (i + 1) # update mean losses
|
324 |
mem = '%.3gG' % (torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0) # (GB)
|
325 |
s = ('%10s' * 2 + '%10.4g' * 6) % (
|
326 |
-
'
|
327 |
pbar.set_description(s)
|
328 |
|
329 |
# Plot
|
330 |
if plots and ni < 3:
|
331 |
f = save_dir / f'train_batch{ni}.jpg' # filename
|
332 |
Thread(target=plot_images, args=(imgs, targets, paths, f), daemon=True).start()
|
333 |
-
if tb_writer:
|
334 |
-
|
335 |
-
|
|
|
336 |
elif plots and ni == 10 and wandb_logger.wandb:
|
337 |
-
wandb_logger.log({
|
338 |
save_dir.glob('train*.jpg') if x.exists()]})
|
339 |
|
340 |
# end batch ------------------------------------------------------------------------------------------------
|
@@ -358,6 +360,7 @@ def train(hyp, opt, device, tb_writer=None):
|
|
358 |
single_cls=opt.single_cls,
|
359 |
dataloader=testloader,
|
360 |
save_dir=save_dir,
|
|
|
361 |
verbose=nc < 50 and final_epoch,
|
362 |
plots=plots and final_epoch,
|
363 |
wandb_logger=wandb_logger,
|
@@ -409,41 +412,38 @@ def train(hyp, opt, device, tb_writer=None):
|
|
409 |
# end epoch ----------------------------------------------------------------------------------------------------
|
410 |
# end training
|
411 |
if rank in [-1, 0]:
|
412 |
-
|
413 |
if plots:
|
414 |
plot_results(save_dir=save_dir) # save as results.png
|
415 |
if wandb_logger.wandb:
|
416 |
files = ['results.png', 'confusion_matrix.png', *[f'{x}_curve.png' for x in ('F1', 'PR', 'P', 'R')]]
|
417 |
wandb_logger.log({"Results": [wandb_logger.wandb.Image(str(save_dir / f), caption=f) for f in files
|
418 |
if (save_dir / f).exists()]})
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
wandb_logger.wandb.log_artifact(str(final), type='model',
|
445 |
-
name='run_' + wandb_logger.wandb_run.id + '_model',
|
446 |
-
aliases=['latest', 'best', 'stripped'])
|
447 |
wandb_logger.finish_run()
|
448 |
else:
|
449 |
dist.destroy_process_group()
|
|
|
4 |
import os
|
5 |
import random
|
6 |
import time
|
7 |
+
import warnings
|
8 |
from copy import deepcopy
|
9 |
from pathlib import Path
|
10 |
from threading import Thread
|
|
|
63 |
init_seeds(2 + rank)
|
64 |
with open(opt.data) as f:
|
65 |
data_dict = yaml.safe_load(f) # data dict
|
|
|
66 |
|
67 |
# Logging- Doing this before checking the dataset. Might update data_dict
|
68 |
loggers = {'wandb': None} # loggers dict
|
|
|
78 |
nc = 1 if opt.single_cls else int(data_dict['nc']) # number of classes
|
79 |
names = ['item'] if opt.single_cls and len(data_dict['names']) != 1 else data_dict['names'] # class names
|
80 |
assert len(names) == nc, '%g names found for nc=%g dataset in %s' % (len(names), nc, opt.data) # check
|
81 |
+
is_coco = opt.data.endswith('coco.yaml') and nc == 80 # COCO dataset
|
82 |
|
83 |
# Model
|
84 |
pretrained = weights.endswith('.pt')
|
85 |
if pretrained:
|
86 |
with torch_distributed_zero_first(rank):
|
87 |
+
weights = attempt_download(weights) # download if not found locally
|
88 |
ckpt = torch.load(weights, map_location=device) # load checkpoint
|
89 |
model = Model(opt.cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # create
|
90 |
exclude = ['anchor'] if (opt.cfg or hyp.get('anchors')) and not opt.resume else [] # exclude keys
|
|
|
324 |
mloss = (mloss * i + loss_items) / (i + 1) # update mean losses
|
325 |
mem = '%.3gG' % (torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0) # (GB)
|
326 |
s = ('%10s' * 2 + '%10.4g' * 6) % (
|
327 |
+
f'{epoch}/{epochs - 1}', mem, *mloss, targets.shape[0], imgs.shape[-1])
|
328 |
pbar.set_description(s)
|
329 |
|
330 |
# Plot
|
331 |
if plots and ni < 3:
|
332 |
f = save_dir / f'train_batch{ni}.jpg' # filename
|
333 |
Thread(target=plot_images, args=(imgs, targets, paths, f), daemon=True).start()
|
334 |
+
if tb_writer and ni == 0:
|
335 |
+
with warnings.catch_warnings():
|
336 |
+
warnings.simplefilter('ignore') # suppress jit trace warning
|
337 |
+
tb_writer.add_graph(torch.jit.trace(de_parallel(model), imgs, strict=False), []) # graph
|
338 |
elif plots and ni == 10 and wandb_logger.wandb:
|
339 |
+
wandb_logger.log({'Mosaics': [wandb_logger.wandb.Image(str(x), caption=x.name) for x in
|
340 |
save_dir.glob('train*.jpg') if x.exists()]})
|
341 |
|
342 |
# end batch ------------------------------------------------------------------------------------------------
|
|
|
360 |
single_cls=opt.single_cls,
|
361 |
dataloader=testloader,
|
362 |
save_dir=save_dir,
|
363 |
+
save_json=is_coco and final_epoch,
|
364 |
verbose=nc < 50 and final_epoch,
|
365 |
plots=plots and final_epoch,
|
366 |
wandb_logger=wandb_logger,
|
|
|
412 |
# end epoch ----------------------------------------------------------------------------------------------------
|
413 |
# end training
|
414 |
if rank in [-1, 0]:
|
415 |
+
logger.info(f'{epoch - start_epoch + 1} epochs completed in {(time.time() - t0) / 3600:.3f} hours.\n')
|
416 |
if plots:
|
417 |
plot_results(save_dir=save_dir) # save as results.png
|
418 |
if wandb_logger.wandb:
|
419 |
files = ['results.png', 'confusion_matrix.png', *[f'{x}_curve.png' for x in ('F1', 'PR', 'P', 'R')]]
|
420 |
wandb_logger.log({"Results": [wandb_logger.wandb.Image(str(save_dir / f), caption=f) for f in files
|
421 |
if (save_dir / f).exists()]})
|
422 |
+
|
423 |
+
if not opt.evolve:
|
424 |
+
if is_coco: # COCO dataset
|
425 |
+
for m in [last, best] if best.exists() else [last]: # speed, mAP tests
|
426 |
+
results, _, _ = test.test(opt.data,
|
427 |
+
batch_size=batch_size * 2,
|
428 |
+
imgsz=imgsz_test,
|
429 |
+
conf_thres=0.001,
|
430 |
+
iou_thres=0.7,
|
431 |
+
model=attempt_load(m, device).half(),
|
432 |
+
single_cls=opt.single_cls,
|
433 |
+
dataloader=testloader,
|
434 |
+
save_dir=save_dir,
|
435 |
+
save_json=True,
|
436 |
+
plots=False,
|
437 |
+
is_coco=is_coco)
|
438 |
+
|
439 |
+
# Strip optimizers
|
440 |
+
for f in last, best:
|
441 |
+
if f.exists():
|
442 |
+
strip_optimizer(f) # strip optimizers
|
443 |
+
if wandb_logger.wandb: # Log the stripped model
|
444 |
+
wandb_logger.wandb.log_artifact(str(best if best.exists() else last), type='model',
|
445 |
+
name='run_' + wandb_logger.wandb_run.id + '_model',
|
446 |
+
aliases=['latest', 'best', 'stripped'])
|
|
|
|
|
|
|
447 |
wandb_logger.finish_run()
|
448 |
else:
|
449 |
dist.destroy_process_group()
|
@@ -517,7 +517,8 @@
|
|
517 |
"colab_type": "text"
|
518 |
},
|
519 |
"source": [
|
520 |
-
"<a href=\"https://colab.research.google.com/github/ultralytics/yolov5/blob/master/tutorial.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
|
|
521 |
]
|
522 |
},
|
523 |
{
|
@@ -529,7 +530,7 @@
|
|
529 |
"<img src=\"https://user-images.githubusercontent.com/26833433/98702494-b71c4e80-237a-11eb-87ed-17fcd6b3f066.jpg\">\n",
|
530 |
"\n",
|
531 |
"This is the **official YOLOv5 🚀 notebook** authored by **Ultralytics**, and is freely available for redistribution under the [GPL-3.0 license](https://choosealicense.com/licenses/gpl-3.0/). \n",
|
532 |
-
"For more information please visit https://github.com/ultralytics/yolov5 and https://
|
533 |
]
|
534 |
},
|
535 |
{
|
@@ -610,7 +611,7 @@
|
|
610 |
"YOLOv5 🚀 v5.0-1-g0f395b3 torch 1.8.1+cu101 CUDA:0 (Tesla V100-SXM2-16GB, 16160.5MB)\n",
|
611 |
"\n",
|
612 |
"Fusing layers... \n",
|
613 |
-
"Model Summary: 224 layers, 7266973 parameters, 0 gradients, 17.0
|
614 |
"image 1/2 /content/yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, Done. (0.008s)\n",
|
615 |
"image 2/2 /content/yolov5/data/images/zidane.jpg: 384x640 2 persons, 2 ties, Done. (0.008s)\n",
|
616 |
"Results saved to runs/detect/exp\n",
|
@@ -733,7 +734,7 @@
|
|
733 |
"100% 168M/168M [00:05<00:00, 32.3MB/s]\n",
|
734 |
"\n",
|
735 |
"Fusing layers... \n",
|
736 |
-
"Model Summary: 476 layers, 87730285 parameters, 0 gradients, 218.8
|
737 |
"\u001b[34m\u001b[1mval: \u001b[0mScanning '../coco/val2017' images and labels... 4952 found, 48 missing, 0 empty, 0 corrupted: 100% 5000/5000 [00:01<00:00, 3102.29it/s]\n",
|
738 |
"\u001b[34m\u001b[1mval: \u001b[0mNew cache created: ../coco/val2017.cache\n",
|
739 |
" Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 157/157 [01:23<00:00, 1.87it/s]\n",
|
@@ -963,7 +964,7 @@
|
|
963 |
" 22 [-1, 10] 1 0 models.common.Concat [1] \n",
|
964 |
" 23 -1 1 1182720 models.common.C3 [512, 512, 1, False] \n",
|
965 |
" 24 [17, 20, 23] 1 229245 models.yolo.Detect [80, [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]], [128, 256, 512]]\n",
|
966 |
-
"Model Summary: 283 layers, 7276605 parameters, 7276605 gradients, 17.1
|
967 |
"\n",
|
968 |
"Transferred 362/362 items from yolov5s.pt\n",
|
969 |
"Scaled weight_decay = 0.0005\n",
|
@@ -1260,4 +1261,4 @@
|
|
1260 |
"outputs": []
|
1261 |
}
|
1262 |
]
|
1263 |
-
}
|
|
|
517 |
"colab_type": "text"
|
518 |
},
|
519 |
"source": [
|
520 |
+
"<a href=\"https://colab.research.google.com/github/ultralytics/yolov5/blob/master/tutorial.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>",
|
521 |
+
"<a href=\"https://kaggle.com/kernels/welcome?src=https://github.com/ultralytics/yolov5/blob/master/tutorial.ipynb\" target=\"_parent\"><img alt=\"Kaggle\" title=\"Open in Kaggle\" src=\"https://kaggle.com/static/images/open-in-kaggle.svg\"></a>"
|
522 |
]
|
523 |
},
|
524 |
{
|
|
|
530 |
"<img src=\"https://user-images.githubusercontent.com/26833433/98702494-b71c4e80-237a-11eb-87ed-17fcd6b3f066.jpg\">\n",
|
531 |
"\n",
|
532 |
"This is the **official YOLOv5 🚀 notebook** authored by **Ultralytics**, and is freely available for redistribution under the [GPL-3.0 license](https://choosealicense.com/licenses/gpl-3.0/). \n",
|
533 |
+
"For more information please visit https://github.com/ultralytics/yolov5 and https://ultralytics.com. Thank you!"
|
534 |
]
|
535 |
},
|
536 |
{
|
|
|
611 |
"YOLOv5 🚀 v5.0-1-g0f395b3 torch 1.8.1+cu101 CUDA:0 (Tesla V100-SXM2-16GB, 16160.5MB)\n",
|
612 |
"\n",
|
613 |
"Fusing layers... \n",
|
614 |
+
"Model Summary: 224 layers, 7266973 parameters, 0 gradients, 17.0 GFLOPs\n",
|
615 |
"image 1/2 /content/yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, Done. (0.008s)\n",
|
616 |
"image 2/2 /content/yolov5/data/images/zidane.jpg: 384x640 2 persons, 2 ties, Done. (0.008s)\n",
|
617 |
"Results saved to runs/detect/exp\n",
|
|
|
734 |
"100% 168M/168M [00:05<00:00, 32.3MB/s]\n",
|
735 |
"\n",
|
736 |
"Fusing layers... \n",
|
737 |
+
"Model Summary: 476 layers, 87730285 parameters, 0 gradients, 218.8 GFLOPs\n",
|
738 |
"\u001b[34m\u001b[1mval: \u001b[0mScanning '../coco/val2017' images and labels... 4952 found, 48 missing, 0 empty, 0 corrupted: 100% 5000/5000 [00:01<00:00, 3102.29it/s]\n",
|
739 |
"\u001b[34m\u001b[1mval: \u001b[0mNew cache created: ../coco/val2017.cache\n",
|
740 |
" Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 157/157 [01:23<00:00, 1.87it/s]\n",
|
|
|
964 |
" 22 [-1, 10] 1 0 models.common.Concat [1] \n",
|
965 |
" 23 -1 1 1182720 models.common.C3 [512, 512, 1, False] \n",
|
966 |
" 24 [17, 20, 23] 1 229245 models.yolo.Detect [80, [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]], [128, 256, 512]]\n",
|
967 |
+
"Model Summary: 283 layers, 7276605 parameters, 7276605 gradients, 17.1 GFLOPs\n",
|
968 |
"\n",
|
969 |
"Transferred 362/362 items from yolov5s.pt\n",
|
970 |
"Scaled weight_decay = 0.0005\n",
|
|
|
1261 |
"outputs": []
|
1262 |
}
|
1263 |
]
|
1264 |
+
}
|
@@ -535,7 +535,7 @@ class LoadImagesAndLabels(Dataset): # for training/testing
|
|
535 |
# MixUp https://arxiv.org/pdf/1710.09412.pdf
|
536 |
if random.random() < hyp['mixup']:
|
537 |
img2, labels2 = load_mosaic(self, random.randint(0, self.n - 1))
|
538 |
-
r = np.random.beta(
|
539 |
img = (img * r + img2 * (1 - r)).astype(np.uint8)
|
540 |
labels = np.concatenate((labels, labels2), 0)
|
541 |
|
@@ -655,12 +655,12 @@ def augment_hsv(img, hgain=0.5, sgain=0.5, vgain=0.5):
|
|
655 |
hue, sat, val = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV))
|
656 |
dtype = img.dtype # uint8
|
657 |
|
658 |
-
x = np.arange(0, 256, dtype=
|
659 |
lut_hue = ((x * r[0]) % 180).astype(dtype)
|
660 |
lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)
|
661 |
lut_val = np.clip(x * r[2], 0, 255).astype(dtype)
|
662 |
|
663 |
-
img_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val)))
|
664 |
cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR, dst=img) # no return needed
|
665 |
|
666 |
|
|
|
535 |
# MixUp https://arxiv.org/pdf/1710.09412.pdf
|
536 |
if random.random() < hyp['mixup']:
|
537 |
img2, labels2 = load_mosaic(self, random.randint(0, self.n - 1))
|
538 |
+
r = np.random.beta(32.0, 32.0) # mixup ratio, alpha=beta=32.0
|
539 |
img = (img * r + img2 * (1 - r)).astype(np.uint8)
|
540 |
labels = np.concatenate((labels, labels2), 0)
|
541 |
|
|
|
655 |
hue, sat, val = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV))
|
656 |
dtype = img.dtype # uint8
|
657 |
|
658 |
+
x = np.arange(0, 256, dtype=r.dtype)
|
659 |
lut_hue = ((x * r[0]) % 180).astype(dtype)
|
660 |
lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)
|
661 |
lut_val = np.clip(x * r[2], 0, 255).astype(dtype)
|
662 |
|
663 |
+
img_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val)))
|
664 |
cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR, dst=img) # no return needed
|
665 |
|
666 |
|
@@ -1,5 +1,6 @@
|
|
1 |
# YOLOv5 general utils
|
2 |
|
|
|
3 |
import glob
|
4 |
import logging
|
5 |
import math
|
@@ -7,11 +8,13 @@ import os
|
|
7 |
import platform
|
8 |
import random
|
9 |
import re
|
10 |
-
import
|
11 |
import time
|
|
|
12 |
from itertools import repeat
|
13 |
from multiprocessing.pool import ThreadPool
|
14 |
from pathlib import Path
|
|
|
15 |
|
16 |
import cv2
|
17 |
import numpy as np
|
@@ -33,6 +36,26 @@ cv2.setNumThreads(0) # prevent OpenCV from multithreading (incompatible with Py
|
|
33 |
os.environ['NUMEXPR_MAX_THREADS'] = str(min(os.cpu_count(), 8)) # NumExpr max threads
|
34 |
|
35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
def set_logging(rank=-1, verbose=True):
|
37 |
logging.basicConfig(
|
38 |
format="%(message)s",
|
@@ -53,12 +76,12 @@ def get_latest_run(search_dir='.'):
|
|
53 |
|
54 |
|
55 |
def is_docker():
|
56 |
-
# Is environment a Docker container
|
57 |
return Path('/workspace').exists() # or Path('/.dockerenv').exists()
|
58 |
|
59 |
|
60 |
def is_colab():
|
61 |
-
# Is environment a Google Colab instance
|
62 |
try:
|
63 |
import google.colab
|
64 |
return True
|
@@ -66,6 +89,11 @@ def is_colab():
|
|
66 |
return False
|
67 |
|
68 |
|
|
|
|
|
|
|
|
|
|
|
69 |
def emojis(str=''):
|
70 |
# Return platform-dependent emoji-safe version of string
|
71 |
return str.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else str
|
@@ -80,13 +108,13 @@ def check_online():
|
|
80 |
# Check internet connectivity
|
81 |
import socket
|
82 |
try:
|
83 |
-
socket.create_connection(("1.1.1.1", 443), 5) # check host
|
84 |
return True
|
85 |
except OSError:
|
86 |
return False
|
87 |
|
88 |
|
89 |
-
def check_git_status():
|
90 |
# Recommend 'git pull' if code is out of date
|
91 |
print(colorstr('github: '), end='')
|
92 |
try:
|
@@ -95,9 +123,9 @@ def check_git_status():
|
|
95 |
assert check_online(), 'skipping check (offline)'
|
96 |
|
97 |
cmd = 'git fetch && git config --get remote.origin.url'
|
98 |
-
url =
|
99 |
-
branch =
|
100 |
-
n = int(
|
101 |
if n > 0:
|
102 |
s = f"⚠️ WARNING: code is out of date by {n} commit{'s' * (n > 1)}. " \
|
103 |
f"Use 'git pull' to update or 'git clone {url}' to download latest."
|
@@ -105,7 +133,7 @@ def check_git_status():
|
|
105 |
s = f'up to date with {url} ✅'
|
106 |
print(emojis(s)) # emoji-safe
|
107 |
except Exception as e:
|
108 |
-
print(e)
|
109 |
|
110 |
|
111 |
def check_python(minimum='3.7.0', required=True):
|
@@ -135,10 +163,11 @@ def check_requirements(requirements='requirements.txt', exclude=()):
|
|
135 |
try:
|
136 |
pkg.require(r)
|
137 |
except Exception as e: # DistributionNotFound or VersionConflict if requirements not met
|
138 |
-
n += 1
|
139 |
print(f"{prefix} {r} not found and is required by YOLOv5, attempting auto-update...")
|
140 |
try:
|
141 |
-
|
|
|
|
|
142 |
except Exception as e:
|
143 |
print(f'{prefix} {e}')
|
144 |
|
@@ -178,7 +207,8 @@ def check_file(file):
|
|
178 |
if Path(file).is_file() or file == '': # exists
|
179 |
return file
|
180 |
elif file.startswith(('http://', 'https://')): # download
|
181 |
-
url, file = file, Path(file).name
|
|
|
182 |
print(f'Downloading {url} to {file}...')
|
183 |
torch.hub.download_url_to_file(url, file)
|
184 |
assert Path(file).exists() and Path(file).stat().st_size > 0, f'File download failed: {url}' # check
|
|
|
1 |
# YOLOv5 general utils
|
2 |
|
3 |
+
import contextlib
|
4 |
import glob
|
5 |
import logging
|
6 |
import math
|
|
|
8 |
import platform
|
9 |
import random
|
10 |
import re
|
11 |
+
import signal
|
12 |
import time
|
13 |
+
import urllib
|
14 |
from itertools import repeat
|
15 |
from multiprocessing.pool import ThreadPool
|
16 |
from pathlib import Path
|
17 |
+
from subprocess import check_output
|
18 |
|
19 |
import cv2
|
20 |
import numpy as np
|
|
|
36 |
os.environ['NUMEXPR_MAX_THREADS'] = str(min(os.cpu_count(), 8)) # NumExpr max threads
|
37 |
|
38 |
|
39 |
+
class timeout(contextlib.ContextDecorator):
|
40 |
+
# Usage: @timeout(seconds) decorator or 'with timeout(seconds):' context manager
|
41 |
+
def __init__(self, seconds, *, timeout_msg='', suppress_timeout_errors=True):
|
42 |
+
self.seconds = int(seconds)
|
43 |
+
self.timeout_message = timeout_msg
|
44 |
+
self.suppress = bool(suppress_timeout_errors)
|
45 |
+
|
46 |
+
def _timeout_handler(self, signum, frame):
|
47 |
+
raise TimeoutError(self.timeout_message)
|
48 |
+
|
49 |
+
def __enter__(self):
|
50 |
+
signal.signal(signal.SIGALRM, self._timeout_handler) # Set handler for SIGALRM
|
51 |
+
signal.alarm(self.seconds) # start countdown for SIGALRM to be raised
|
52 |
+
|
53 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
54 |
+
signal.alarm(0) # Cancel SIGALRM if it's scheduled
|
55 |
+
if self.suppress and exc_type is TimeoutError: # Suppress TimeoutError
|
56 |
+
return True
|
57 |
+
|
58 |
+
|
59 |
def set_logging(rank=-1, verbose=True):
|
60 |
logging.basicConfig(
|
61 |
format="%(message)s",
|
|
|
76 |
|
77 |
|
78 |
def is_docker():
|
79 |
+
# Is environment a Docker container?
|
80 |
return Path('/workspace').exists() # or Path('/.dockerenv').exists()
|
81 |
|
82 |
|
83 |
def is_colab():
|
84 |
+
# Is environment a Google Colab instance?
|
85 |
try:
|
86 |
import google.colab
|
87 |
return True
|
|
|
89 |
return False
|
90 |
|
91 |
|
92 |
+
def is_pip():
|
93 |
+
# Is file in a pip package?
|
94 |
+
return 'site-packages' in Path(__file__).absolute().parts
|
95 |
+
|
96 |
+
|
97 |
def emojis(str=''):
|
98 |
# Return platform-dependent emoji-safe version of string
|
99 |
return str.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else str
|
|
|
108 |
# Check internet connectivity
|
109 |
import socket
|
110 |
try:
|
111 |
+
socket.create_connection(("1.1.1.1", 443), 5) # check host accessibility
|
112 |
return True
|
113 |
except OSError:
|
114 |
return False
|
115 |
|
116 |
|
117 |
+
def check_git_status(err_msg=', for updates see https://github.com/ultralytics/yolov5'):
|
118 |
# Recommend 'git pull' if code is out of date
|
119 |
print(colorstr('github: '), end='')
|
120 |
try:
|
|
|
123 |
assert check_online(), 'skipping check (offline)'
|
124 |
|
125 |
cmd = 'git fetch && git config --get remote.origin.url'
|
126 |
+
url = check_output(cmd, shell=True, timeout=5).decode().strip().rstrip('.git') # git fetch
|
127 |
+
branch = check_output('git rev-parse --abbrev-ref HEAD', shell=True).decode().strip() # checked out
|
128 |
+
n = int(check_output(f'git rev-list {branch}..origin/master --count', shell=True)) # commits behind
|
129 |
if n > 0:
|
130 |
s = f"⚠️ WARNING: code is out of date by {n} commit{'s' * (n > 1)}. " \
|
131 |
f"Use 'git pull' to update or 'git clone {url}' to download latest."
|
|
|
133 |
s = f'up to date with {url} ✅'
|
134 |
print(emojis(s)) # emoji-safe
|
135 |
except Exception as e:
|
136 |
+
print(f'{e}{err_msg}')
|
137 |
|
138 |
|
139 |
def check_python(minimum='3.7.0', required=True):
|
|
|
163 |
try:
|
164 |
pkg.require(r)
|
165 |
except Exception as e: # DistributionNotFound or VersionConflict if requirements not met
|
|
|
166 |
print(f"{prefix} {r} not found and is required by YOLOv5, attempting auto-update...")
|
167 |
try:
|
168 |
+
assert check_online(), f"'pip install {r}' skipped (offline)"
|
169 |
+
print(check_output(f"pip install '{r}'", shell=True).decode())
|
170 |
+
n += 1
|
171 |
except Exception as e:
|
172 |
print(f'{prefix} {e}')
|
173 |
|
|
|
207 |
if Path(file).is_file() or file == '': # exists
|
208 |
return file
|
209 |
elif file.startswith(('http://', 'https://')): # download
|
210 |
+
url, file = file, Path(urllib.parse.unquote(str(file))).name # url, file (decode '%2F' to '/' etc.)
|
211 |
+
file = file.split('?')[0] # parse authentication https://url.com/file.txt?auth...
|
212 |
print(f'Downloading {url} to {file}...')
|
213 |
torch.hub.download_url_to_file(url, file)
|
214 |
assert Path(file).exists() and Path(file).stat().st_size > 0, f'File download failed: {url}' # check
|
@@ -4,6 +4,7 @@ import os
|
|
4 |
import platform
|
5 |
import subprocess
|
6 |
import time
|
|
|
7 |
from pathlib import Path
|
8 |
|
9 |
import requests
|
@@ -16,11 +17,39 @@ def gsutil_getsize(url=''):
|
|
16 |
return eval(s.split(' ')[0]) if len(s) else 0 # bytes
|
17 |
|
18 |
|
19 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
# Attempt file download if does not exist
|
21 |
file = Path(str(file).strip().replace("'", ''))
|
22 |
|
23 |
if not file.exists():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
file.parent.mkdir(parents=True, exist_ok=True) # make parent dir (if required)
|
25 |
try:
|
26 |
response = requests.get(f'https://api.github.com/repos/{repo}/releases/latest').json() # github api
|
@@ -34,27 +63,14 @@ def attempt_download(file, repo='ultralytics/yolov5'):
|
|
34 |
except:
|
35 |
tag = 'v5.0' # current release
|
36 |
|
37 |
-
name = file.name
|
38 |
if name in assets:
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
except Exception as e: # GCP
|
47 |
-
print(f'Download error: {e}')
|
48 |
-
assert redundant, 'No secondary mirror'
|
49 |
-
url = f'https://storage.googleapis.com/{repo}/ckpt/{name}'
|
50 |
-
print(f'Downloading {url} to {file}...')
|
51 |
-
os.system(f"curl -L '{url}' -o '{file}' --retry 3 -C -") # curl download, retry and resume on fail
|
52 |
-
finally:
|
53 |
-
if not file.exists() or file.stat().st_size < 1E6: # check
|
54 |
-
file.unlink(missing_ok=True) # remove partial downloads
|
55 |
-
print(f'ERROR: Download failure: {msg}')
|
56 |
-
print('')
|
57 |
-
return
|
58 |
|
59 |
|
60 |
def gdrive_download(id='16TiPfZj7htmTyhntwcZyEEAejOUxuT6m', file='tmp.zip'):
|
|
|
4 |
import platform
|
5 |
import subprocess
|
6 |
import time
|
7 |
+
import urllib
|
8 |
from pathlib import Path
|
9 |
|
10 |
import requests
|
|
|
17 |
return eval(s.split(' ')[0]) if len(s) else 0 # bytes
|
18 |
|
19 |
|
20 |
+
def safe_download(file, url, url2=None, min_bytes=1E0, error_msg=''):
|
21 |
+
# Attempts to download file from url or url2, checks and removes incomplete downloads < min_bytes
|
22 |
+
file = Path(file)
|
23 |
+
assert_msg = f"Downloaded file '{file}' does not exist or size is < min_bytes={min_bytes}"
|
24 |
+
try: # url1
|
25 |
+
print(f'Downloading {url} to {file}...')
|
26 |
+
torch.hub.download_url_to_file(url, str(file))
|
27 |
+
assert file.exists() and file.stat().st_size > min_bytes, assert_msg # check
|
28 |
+
except Exception as e: # url2
|
29 |
+
file.unlink(missing_ok=True) # remove partial downloads
|
30 |
+
print(f'ERROR: {e}\nRe-attempting {url2 or url} to {file}...')
|
31 |
+
os.system(f"curl -L '{url2 or url}' -o '{file}' --retry 3 -C -") # curl download, retry and resume on fail
|
32 |
+
finally:
|
33 |
+
if not file.exists() or file.stat().st_size < min_bytes: # check
|
34 |
+
file.unlink(missing_ok=True) # remove partial downloads
|
35 |
+
print(f"ERROR: {assert_msg}\n{error_msg}")
|
36 |
+
print('')
|
37 |
+
|
38 |
+
|
39 |
+
def attempt_download(file, repo='ultralytics/yolov5'): # from utils.google_utils import *; attempt_download()
|
40 |
# Attempt file download if does not exist
|
41 |
file = Path(str(file).strip().replace("'", ''))
|
42 |
|
43 |
if not file.exists():
|
44 |
+
# URL specified
|
45 |
+
name = Path(urllib.parse.unquote(str(file))).name # decode '%2F' to '/' etc.
|
46 |
+
if str(file).startswith(('http:/', 'https:/')): # download
|
47 |
+
url = str(file).replace(':/', '://') # Pathlib turns :// -> :/
|
48 |
+
name = name.split('?')[0] # parse authentication https://url.com/file.txt?auth...
|
49 |
+
safe_download(file=name, url=url, min_bytes=1E5)
|
50 |
+
return name
|
51 |
+
|
52 |
+
# GitHub assets
|
53 |
file.parent.mkdir(parents=True, exist_ok=True) # make parent dir (if required)
|
54 |
try:
|
55 |
response = requests.get(f'https://api.github.com/repos/{repo}/releases/latest').json() # github api
|
|
|
63 |
except:
|
64 |
tag = 'v5.0' # current release
|
65 |
|
|
|
66 |
if name in assets:
|
67 |
+
safe_download(file,
|
68 |
+
url=f'https://github.com/{repo}/releases/download/{tag}/{name}',
|
69 |
+
# url2=f'https://storage.googleapis.com/{repo}/ckpt/{name}', # backup url (optional)
|
70 |
+
min_bytes=1E5,
|
71 |
+
error_msg=f'{file} missing, try downloading from https://github.com/{repo}/releases/')
|
72 |
+
|
73 |
+
return str(file)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
|
75 |
|
76 |
def gdrive_download(id='16TiPfZj7htmTyhntwcZyEEAejOUxuT6m', file='tmp.zip'):
|
@@ -18,7 +18,7 @@ import torch.nn.functional as F
|
|
18 |
import torchvision
|
19 |
|
20 |
try:
|
21 |
-
import thop # for
|
22 |
except ImportError:
|
23 |
thop = None
|
24 |
logger = logging.getLogger(__name__)
|
@@ -105,13 +105,13 @@ def profile(x, ops, n=100, device=None):
|
|
105 |
x = x.to(device)
|
106 |
x.requires_grad = True
|
107 |
print(torch.__version__, device.type, torch.cuda.get_device_properties(0) if device.type == 'cuda' else '')
|
108 |
-
print(f"\n{'Params':>12s}{'
|
109 |
for m in ops if isinstance(ops, list) else [ops]:
|
110 |
m = m.to(device) if hasattr(m, 'to') else m # device
|
111 |
m = m.half() if hasattr(m, 'half') and isinstance(x, torch.Tensor) and x.dtype is torch.float16 else m # type
|
112 |
dtf, dtb, t = 0., 0., [0., 0., 0.] # dt forward, backward
|
113 |
try:
|
114 |
-
flops = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2 #
|
115 |
except:
|
116 |
flops = 0
|
117 |
|
@@ -219,13 +219,13 @@ def model_info(model, verbose=False, img_size=640):
|
|
219 |
print('%5g %40s %9s %12g %20s %10.3g %10.3g' %
|
220 |
(i, name, p.requires_grad, p.numel(), list(p.shape), p.mean(), p.std()))
|
221 |
|
222 |
-
try: #
|
223 |
from thop import profile
|
224 |
stride = max(int(model.stride.max()), 32) if hasattr(model, 'stride') else 32
|
225 |
img = torch.zeros((1, model.yaml.get('ch', 3), stride, stride), device=next(model.parameters()).device) # input
|
226 |
-
flops = profile(deepcopy(model), inputs=(img,), verbose=False)[0] / 1E9 * 2 # stride
|
227 |
img_size = img_size if isinstance(img_size, list) else [img_size, img_size] # expand if int/float
|
228 |
-
fs = ', %.1f
|
229 |
except (ImportError, Exception):
|
230 |
fs = ''
|
231 |
|
|
|
18 |
import torchvision
|
19 |
|
20 |
try:
|
21 |
+
import thop # for FLOPs computation
|
22 |
except ImportError:
|
23 |
thop = None
|
24 |
logger = logging.getLogger(__name__)
|
|
|
105 |
x = x.to(device)
|
106 |
x.requires_grad = True
|
107 |
print(torch.__version__, device.type, torch.cuda.get_device_properties(0) if device.type == 'cuda' else '')
|
108 |
+
print(f"\n{'Params':>12s}{'GFLOPs':>12s}{'forward (ms)':>16s}{'backward (ms)':>16s}{'input':>24s}{'output':>24s}")
|
109 |
for m in ops if isinstance(ops, list) else [ops]:
|
110 |
m = m.to(device) if hasattr(m, 'to') else m # device
|
111 |
m = m.half() if hasattr(m, 'half') and isinstance(x, torch.Tensor) and x.dtype is torch.float16 else m # type
|
112 |
dtf, dtb, t = 0., 0., [0., 0., 0.] # dt forward, backward
|
113 |
try:
|
114 |
+
flops = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2 # GFLOPs
|
115 |
except:
|
116 |
flops = 0
|
117 |
|
|
|
219 |
print('%5g %40s %9s %12g %20s %10.3g %10.3g' %
|
220 |
(i, name, p.requires_grad, p.numel(), list(p.shape), p.mean(), p.std()))
|
221 |
|
222 |
+
try: # FLOPs
|
223 |
from thop import profile
|
224 |
stride = max(int(model.stride.max()), 32) if hasattr(model, 'stride') else 32
|
225 |
img = torch.zeros((1, model.yaml.get('ch', 3), stride, stride), device=next(model.parameters()).device) # input
|
226 |
+
flops = profile(deepcopy(model), inputs=(img,), verbose=False)[0] / 1E9 * 2 # stride GFLOPs
|
227 |
img_size = img_size if isinstance(img_size, list) else [img_size, img_size] # expand if int/float
|
228 |
+
fs = ', %.1f GFLOPs' % (flops * img_size[0] / stride * img_size[1] / stride) # 640x640 GFLOPs
|
229 |
except (ImportError, Exception):
|
230 |
fs = ''
|
231 |
|