Merge develop branch into master (#3518)
* 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 21a9607e00.
* 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>
This commit is contained in:
16 files changed
+186
-122
No files matched your search
+3
-3
@@ -535,7 +535,7 @@ class LoadImagesAndLabels(Dataset): # for training/testing
|
||||
# MixUp https://arxiv.org/pdf/1710.09412.pdf
|
||||
if random.random() < hyp['mixup']:
|
||||
img2, labels2 = load_mosaic(self, random.randint(0, self.n - 1))
|
||||
r = np.random.beta(8.0, 8.0) # mixup ratio, alpha=beta=8.0
|
||||
r = np.random.beta(32.0, 32.0) # mixup ratio, alpha=beta=32.0
|
||||
img = (img * r + img2 * (1 - r)).astype(np.uint8)
|
||||
labels = np.concatenate((labels, labels2), 0)
|
||||
|
||||
@@ -655,12 +655,12 @@ def augment_hsv(img, hgain=0.5, sgain=0.5, vgain=0.5):
|
||||
hue, sat, val = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV))
|
||||
dtype = img.dtype # uint8
|
||||
|
||||
x = np.arange(0, 256, dtype=np.int16)
|
||||
x = np.arange(0, 256, dtype=r.dtype)
|
||||
lut_hue = ((x * r[0]) % 180).astype(dtype)
|
||||
lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)
|
||||
lut_val = np.clip(x * r[2], 0, 255).astype(dtype)
|
||||
|
||||
img_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val))).astype(dtype)
|
||||
img_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val)))
|
||||
cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR, dst=img) # no return needed
|
||||
|
||||
|
||||
|
||||
+42
-12
@@ -1,5 +1,6 @@
|
||||
# YOLOv5 general utils
|
||||
|
||||
import contextlib
|
||||
import glob
|
||||
import logging
|
||||
import math
|
||||
@@ -7,11 +8,13 @@ import os
|
||||
import platform
|
||||
import random
|
||||
import re
|
||||
import subprocess
|
||||
import signal
|
||||
import time
|
||||
import urllib
|
||||
from itertools import repeat
|
||||
from multiprocessing.pool import ThreadPool
|
||||
from pathlib import Path
|
||||
from subprocess import check_output
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -33,6 +36,26 @@ cv2.setNumThreads(0) # prevent OpenCV from multithreading (incompatible with Py
|
||||
os.environ['NUMEXPR_MAX_THREADS'] = str(min(os.cpu_count(), 8)) # NumExpr max threads
|
||||
|
||||
|
||||
class timeout(contextlib.ContextDecorator):
|
||||
# Usage: @timeout(seconds) decorator or 'with timeout(seconds):' context manager
|
||||
def __init__(self, seconds, *, timeout_msg='', suppress_timeout_errors=True):
|
||||
self.seconds = int(seconds)
|
||||
self.timeout_message = timeout_msg
|
||||
self.suppress = bool(suppress_timeout_errors)
|
||||
|
||||
def _timeout_handler(self, signum, frame):
|
||||
raise TimeoutError(self.timeout_message)
|
||||
|
||||
def __enter__(self):
|
||||
signal.signal(signal.SIGALRM, self._timeout_handler) # Set handler for SIGALRM
|
||||
signal.alarm(self.seconds) # start countdown for SIGALRM to be raised
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
signal.alarm(0) # Cancel SIGALRM if it's scheduled
|
||||
if self.suppress and exc_type is TimeoutError: # Suppress TimeoutError
|
||||
return True
|
||||
|
||||
|
||||
def set_logging(rank=-1, verbose=True):
|
||||
logging.basicConfig(
|
||||
format="%(message)s",
|
||||
@@ -53,12 +76,12 @@ def get_latest_run(search_dir='.'):
|
||||
|
||||
|
||||
def is_docker():
|
||||
# Is environment a Docker container
|
||||
# Is environment a Docker container?
|
||||
return Path('/workspace').exists() # or Path('/.dockerenv').exists()
|
||||
|
||||
|
||||
def is_colab():
|
||||
# Is environment a Google Colab instance
|
||||
# Is environment a Google Colab instance?
|
||||
try:
|
||||
import google.colab
|
||||
return True
|
||||
@@ -66,6 +89,11 @@ def is_colab():
|
||||
return False
|
||||
|
||||
|
||||
def is_pip():
|
||||
# Is file in a pip package?
|
||||
return 'site-packages' in Path(__file__).absolute().parts
|
||||
|
||||
|
||||
def emojis(str=''):
|
||||
# Return platform-dependent emoji-safe version of string
|
||||
return str.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else str
|
||||
@@ -80,13 +108,13 @@ def check_online():
|
||||
# Check internet connectivity
|
||||
import socket
|
||||
try:
|
||||
socket.create_connection(("1.1.1.1", 443), 5) # check host accesability
|
||||
socket.create_connection(("1.1.1.1", 443), 5) # check host accessibility
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def check_git_status():
|
||||
def check_git_status(err_msg=', for updates see https://github.com/ultralytics/yolov5'):
|
||||
# Recommend 'git pull' if code is out of date
|
||||
print(colorstr('github: '), end='')
|
||||
try:
|
||||
@@ -95,9 +123,9 @@ def check_git_status():
|
||||
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
|
||||
url = check_output(cmd, shell=True, timeout=5).decode().strip().rstrip('.git') # git fetch
|
||||
branch = check_output('git rev-parse --abbrev-ref HEAD', shell=True).decode().strip() # checked out
|
||||
n = int(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."
|
||||
@@ -105,7 +133,7 @@ def check_git_status():
|
||||
s = f'up to date with {url} ✅'
|
||||
print(emojis(s)) # emoji-safe
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(f'{e}{err_msg}')
|
||||
|
||||
|
||||
def check_python(minimum='3.7.0', required=True):
|
||||
@@ -135,10 +163,11 @@ def check_requirements(requirements='requirements.txt', exclude=()):
|
||||
try:
|
||||
pkg.require(r)
|
||||
except Exception as e: # DistributionNotFound or VersionConflict if requirements not met
|
||||
n += 1
|
||||
print(f"{prefix} {r} not found and is required by YOLOv5, attempting auto-update...")
|
||||
try:
|
||||
print(subprocess.check_output(f"pip install '{r}'", shell=True).decode())
|
||||
assert check_online(), f"'pip install {r}' skipped (offline)"
|
||||
print(check_output(f"pip install '{r}'", shell=True).decode())
|
||||
n += 1
|
||||
except Exception as e:
|
||||
print(f'{prefix} {e}')
|
||||
|
||||
@@ -178,7 +207,8 @@ def check_file(file):
|
||||
if Path(file).is_file() or file == '': # exists
|
||||
return file
|
||||
elif file.startswith(('http://', 'https://')): # download
|
||||
url, file = file, Path(file).name
|
||||
url, file = file, Path(urllib.parse.unquote(str(file))).name # url, file (decode '%2F' to '/' etc.)
|
||||
file = file.split('?')[0] # parse authentication https://url.com/file.txt?auth...
|
||||
print(f'Downloading {url} to {file}...')
|
||||
torch.hub.download_url_to_file(url, file)
|
||||
assert Path(file).exists() and Path(file).stat().st_size > 0, f'File download failed: {url}' # check
|
||||
|
||||
+37
-21
@@ -4,6 +4,7 @@ import os
|
||||
import platform
|
||||
import subprocess
|
||||
import time
|
||||
import urllib
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
@@ -16,11 +17,39 @@ def gsutil_getsize(url=''):
|
||||
return eval(s.split(' ')[0]) if len(s) else 0 # bytes
|
||||
|
||||
|
||||
def attempt_download(file, repo='ultralytics/yolov5'):
|
||||
def safe_download(file, url, url2=None, min_bytes=1E0, error_msg=''):
|
||||
# Attempts to download file from url or url2, checks and removes incomplete downloads < min_bytes
|
||||
file = Path(file)
|
||||
assert_msg = f"Downloaded file '{file}' does not exist or size is < min_bytes={min_bytes}"
|
||||
try: # url1
|
||||
print(f'Downloading {url} to {file}...')
|
||||
torch.hub.download_url_to_file(url, str(file))
|
||||
assert file.exists() and file.stat().st_size > min_bytes, assert_msg # check
|
||||
except Exception as e: # url2
|
||||
file.unlink(missing_ok=True) # remove partial downloads
|
||||
print(f'ERROR: {e}\nRe-attempting {url2 or url} to {file}...')
|
||||
os.system(f"curl -L '{url2 or url}' -o '{file}' --retry 3 -C -") # curl download, retry and resume on fail
|
||||
finally:
|
||||
if not file.exists() or file.stat().st_size < min_bytes: # check
|
||||
file.unlink(missing_ok=True) # remove partial downloads
|
||||
print(f"ERROR: {assert_msg}\n{error_msg}")
|
||||
print('')
|
||||
|
||||
|
||||
def attempt_download(file, repo='ultralytics/yolov5'): # from utils.google_utils import *; attempt_download()
|
||||
# Attempt file download if does not exist
|
||||
file = Path(str(file).strip().replace("'", ''))
|
||||
|
||||
if not file.exists():
|
||||
# URL specified
|
||||
name = Path(urllib.parse.unquote(str(file))).name # decode '%2F' to '/' etc.
|
||||
if str(file).startswith(('http:/', 'https:/')): # download
|
||||
url = str(file).replace(':/', '://') # Pathlib turns :// -> :/
|
||||
name = name.split('?')[0] # parse authentication https://url.com/file.txt?auth...
|
||||
safe_download(file=name, url=url, min_bytes=1E5)
|
||||
return name
|
||||
|
||||
# GitHub assets
|
||||
file.parent.mkdir(parents=True, exist_ok=True) # make parent dir (if required)
|
||||
try:
|
||||
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'):
|
||||
except:
|
||||
tag = 'v5.0' # current release
|
||||
|
||||
name = file.name
|
||||
if name in assets:
|
||||
msg = f'{file} missing, try downloading from https://github.com/{repo}/releases/'
|
||||
redundant = False # second download option
|
||||
try: # GitHub
|
||||
url = f'https://github.com/{repo}/releases/download/{tag}/{name}'
|
||||
print(f'Downloading {url} to {file}...')
|
||||
torch.hub.download_url_to_file(url, file)
|
||||
assert file.exists() and file.stat().st_size > 1E6 # check
|
||||
except Exception as e: # GCP
|
||||
print(f'Download error: {e}')
|
||||
assert redundant, 'No secondary mirror'
|
||||
url = f'https://storage.googleapis.com/{repo}/ckpt/{name}'
|
||||
print(f'Downloading {url} to {file}...')
|
||||
os.system(f"curl -L '{url}' -o '{file}' --retry 3 -C -") # curl download, retry and resume on fail
|
||||
finally:
|
||||
if not file.exists() or file.stat().st_size < 1E6: # check
|
||||
file.unlink(missing_ok=True) # remove partial downloads
|
||||
print(f'ERROR: Download failure: {msg}')
|
||||
print('')
|
||||
return
|
||||
safe_download(file,
|
||||
url=f'https://github.com/{repo}/releases/download/{tag}/{name}',
|
||||
# url2=f'https://storage.googleapis.com/{repo}/ckpt/{name}', # backup url (optional)
|
||||
min_bytes=1E5,
|
||||
error_msg=f'{file} missing, try downloading from https://github.com/{repo}/releases/')
|
||||
|
||||
return str(file)
|
||||
|
||||
|
||||
def gdrive_download(id='16TiPfZj7htmTyhntwcZyEEAejOUxuT6m', file='tmp.zip'):
|
||||
|
||||
@@ -18,7 +18,7 @@ import torch.nn.functional as F
|
||||
import torchvision
|
||||
|
||||
try:
|
||||
import thop # for FLOPS computation
|
||||
import thop # for FLOPs computation
|
||||
except ImportError:
|
||||
thop = None
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -105,13 +105,13 @@ def profile(x, ops, n=100, device=None):
|
||||
x = x.to(device)
|
||||
x.requires_grad = True
|
||||
print(torch.__version__, device.type, torch.cuda.get_device_properties(0) if device.type == 'cuda' else '')
|
||||
print(f"\n{'Params':>12s}{'GFLOPS':>12s}{'forward (ms)':>16s}{'backward (ms)':>16s}{'input':>24s}{'output':>24s}")
|
||||
print(f"\n{'Params':>12s}{'GFLOPs':>12s}{'forward (ms)':>16s}{'backward (ms)':>16s}{'input':>24s}{'output':>24s}")
|
||||
for m in ops if isinstance(ops, list) else [ops]:
|
||||
m = m.to(device) if hasattr(m, 'to') else m # device
|
||||
m = m.half() if hasattr(m, 'half') and isinstance(x, torch.Tensor) and x.dtype is torch.float16 else m # type
|
||||
dtf, dtb, t = 0., 0., [0., 0., 0.] # dt forward, backward
|
||||
try:
|
||||
flops = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2 # GFLOPS
|
||||
flops = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2 # GFLOPs
|
||||
except:
|
||||
flops = 0
|
||||
|
||||
@@ -219,13 +219,13 @@ def model_info(model, verbose=False, img_size=640):
|
||||
print('%5g %40s %9s %12g %20s %10.3g %10.3g' %
|
||||
(i, name, p.requires_grad, p.numel(), list(p.shape), p.mean(), p.std()))
|
||||
|
||||
try: # FLOPS
|
||||
try: # FLOPs
|
||||
from thop import profile
|
||||
stride = max(int(model.stride.max()), 32) if hasattr(model, 'stride') else 32
|
||||
img = torch.zeros((1, model.yaml.get('ch', 3), stride, stride), device=next(model.parameters()).device) # input
|
||||
flops = profile(deepcopy(model), inputs=(img,), verbose=False)[0] / 1E9 * 2 # stride GFLOPS
|
||||
flops = profile(deepcopy(model), inputs=(img,), verbose=False)[0] / 1E9 * 2 # stride GFLOPs
|
||||
img_size = img_size if isinstance(img_size, list) else [img_size, img_size] # expand if int/float
|
||||
fs = ', %.1f GFLOPS' % (flops * img_size[0] / stride * img_size[1] / stride) # 640x640 GFLOPS
|
||||
fs = ', %.1f GFLOPs' % (flops * img_size[0] / stride * img_size[1] / stride) # 640x640 GFLOPs
|
||||
except (ImportError, Exception):
|
||||
fs = ''
|
||||
|
||||
|
||||
Reference in new issue
Block a user