* Update plots for Chinese fonts * make is_chinese() non-str safe * Add global FONT * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update general.py Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>modifyDataloader
@@ -37,6 +37,7 @@ FILE = Path(__file__).resolve() | |||
ROOT = FILE.parents[1] # YOLOv5 root directory | |||
NUM_THREADS = min(8, max(1, os.cpu_count() - 1)) # number of YOLOv5 multiprocessing threads | |||
VERBOSE = str(os.getenv('YOLOv5_VERBOSE', True)).lower() == 'true' # global verbose mode | |||
FONT = 'Arial.ttf' # https://ultralytics.com/assets/Arial.ttf | |||
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 | |||
@@ -55,6 +56,21 @@ def is_kaggle(): | |||
return False | |||
def is_writeable(dir, test=False): | |||
# Return True if directory has write permissions, test opening a file with write permissions if test=True | |||
if test: # method 1 | |||
file = Path(dir) / 'tmp.txt' | |||
try: | |||
with open(file, 'w'): # open file with write permissions | |||
pass | |||
file.unlink() # remove file | |||
return True | |||
except OSError: | |||
return False | |||
else: # method 2 | |||
return os.access(dir, os.R_OK) # possible issues on Windows | |||
def set_logging(name=None, verbose=VERBOSE): | |||
# Sets level and returns logger | |||
if is_kaggle(): | |||
@@ -68,6 +84,22 @@ def set_logging(name=None, verbose=VERBOSE): | |||
LOGGER = set_logging('yolov5') # define globally (used in train.py, val.py, detect.py, etc.) | |||
def user_config_dir(dir='Ultralytics', env_var='YOLOV5_CONFIG_DIR'): | |||
# Return path of user configuration directory. Prefer environment variable if exists. Make dir if required. | |||
env = os.getenv(env_var) | |||
if env: | |||
path = Path(env) # use environment variable | |||
else: | |||
cfg = {'Windows': 'AppData/Roaming', 'Linux': '.config', 'Darwin': 'Library/Application Support'} # 3 OS dirs | |||
path = Path.home() / cfg.get(platform.system(), '') # OS-specific config dir | |||
path = (path if is_writeable(path) else Path('/tmp')) / dir # GCP and AWS lambda fix, only /tmp is writeable | |||
path.mkdir(exist_ok=True) # make if required | |||
return path | |||
CONFIG_DIR = user_config_dir() # Ultralytics settings dir | |||
class Profile(contextlib.ContextDecorator): | |||
# Usage: @Profile() decorator or 'with Profile():' context manager | |||
def __enter__(self): | |||
@@ -152,34 +184,6 @@ def get_latest_run(search_dir='.'): | |||
return max(last_list, key=os.path.getctime) if last_list else '' | |||
def user_config_dir(dir='Ultralytics', env_var='YOLOV5_CONFIG_DIR'): | |||
# Return path of user configuration directory. Prefer environment variable if exists. Make dir if required. | |||
env = os.getenv(env_var) | |||
if env: | |||
path = Path(env) # use environment variable | |||
else: | |||
cfg = {'Windows': 'AppData/Roaming', 'Linux': '.config', 'Darwin': 'Library/Application Support'} # 3 OS dirs | |||
path = Path.home() / cfg.get(platform.system(), '') # OS-specific config dir | |||
path = (path if is_writeable(path) else Path('/tmp')) / dir # GCP and AWS lambda fix, only /tmp is writeable | |||
path.mkdir(exist_ok=True) # make if required | |||
return path | |||
def is_writeable(dir, test=False): | |||
# Return True if directory has write permissions, test opening a file with write permissions if test=True | |||
if test: # method 1 | |||
file = Path(dir) / 'tmp.txt' | |||
try: | |||
with open(file, 'w'): # open file with write permissions | |||
pass | |||
file.unlink() # remove file | |||
return True | |||
except OSError: | |||
return False | |||
else: # method 2 | |||
return os.access(dir, os.R_OK) # possible issues on Windows | |||
def is_docker(): | |||
# Is environment a Docker container? | |||
return Path('/workspace').exists() # or Path('/.dockerenv').exists() | |||
@@ -207,7 +211,7 @@ def is_ascii(s=''): | |||
def is_chinese(s='人工智能'): | |||
# Is string composed of any Chinese characters? | |||
return re.search('[\u4e00-\u9fff]', s) | |||
return True if re.search('[\u4e00-\u9fff]', str(s)) else False | |||
def emojis(str=''): | |||
@@ -378,6 +382,15 @@ def check_file(file, suffix=''): | |||
return files[0] # return file | |||
def check_font(font=FONT): | |||
# Download font to CONFIG_DIR if necessary | |||
font = Path(font) | |||
if not font.exists() and not (CONFIG_DIR / font.name).exists(): | |||
url = "https://ultralytics.com/assets/" + font.name | |||
LOGGER.info(f'Downloading {url} to {CONFIG_DIR / font.name}...') | |||
torch.hub.download_url_to_file(url, str(font), progress=False) | |||
def check_dataset(data, autodownload=True): | |||
# Download and/or unzip dataset if not found locally | |||
# Usage: https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128_with_yaml.zip |
@@ -17,12 +17,11 @@ import seaborn as sn | |||
import torch | |||
from PIL import Image, ImageDraw, ImageFont | |||
from utils.general import (LOGGER, Timeout, check_requirements, clip_coords, increment_path, is_ascii, is_chinese, | |||
try_except, user_config_dir, xywh2xyxy, xyxy2xywh) | |||
from utils.general import (CONFIG_DIR, FONT, LOGGER, Timeout, check_font, check_requirements, clip_coords, | |||
increment_path, is_ascii, is_chinese, try_except, xywh2xyxy, xyxy2xywh) | |||
from utils.metrics import fitness | |||
# Settings | |||
CONFIG_DIR = user_config_dir() # Ultralytics settings dir | |||
RANK = int(os.getenv('RANK', -1)) | |||
matplotlib.rc('font', **{'size': 11}) | |||
matplotlib.use('Agg') # for writing to files only | |||
@@ -49,16 +48,14 @@ class Colors: | |||
colors = Colors() # create instance for 'from utils.plots import colors' | |||
def check_font(font='Arial.ttf', size=10): | |||
def check_pil_font(font=FONT, size=10): | |||
# Return a PIL TrueType Font, downloading to CONFIG_DIR if necessary | |||
font = Path(font) | |||
font = font if font.exists() else (CONFIG_DIR / font.name) | |||
try: | |||
return ImageFont.truetype(str(font) if font.exists() else font.name, size) | |||
except Exception as e: # download if missing | |||
url = "https://ultralytics.com/assets/" + font.name | |||
LOGGER.info(f'Downloading {url} to {font}...') | |||
torch.hub.download_url_to_file(url, str(font), progress=False) | |||
check_font(font) | |||
try: | |||
return ImageFont.truetype(str(font), size) | |||
except TypeError: | |||
@@ -67,7 +64,7 @@ def check_font(font='Arial.ttf', size=10): | |||
class Annotator: | |||
if RANK in (-1, 0): | |||
check_font() # download TTF if necessary | |||
check_pil_font() # download TTF if necessary | |||
# YOLOv5 Annotator for train/val mosaics and jpgs and detect/hub inference annotations | |||
def __init__(self, im, line_width=None, font_size=None, font='Arial.ttf', pil=False, example='abc'): | |||
@@ -76,8 +73,8 @@ class Annotator: | |||
if self.pil: # use PIL | |||
self.im = im if isinstance(im, Image.Image) else Image.fromarray(im) | |||
self.draw = ImageDraw.Draw(self.im) | |||
self.font = check_font(font='Arial.Unicode.ttf' if is_chinese(example) else font, | |||
size=font_size or max(round(sum(self.im.size) / 2 * 0.035), 12)) | |||
self.font = check_pil_font(font='Arial.Unicode.ttf' if is_chinese(example) else font, | |||
size=font_size or max(round(sum(self.im.size) / 2 * 0.035), 12)) | |||
else: # use cv2 | |||
self.im = im | |||
self.lw = line_width or max(round(sum(im.shape) / 2 * 0.003), 2) # line width | |||
@@ -89,10 +86,10 @@ class Annotator: | |||
if label: | |||
w, h = self.font.getsize(label) # text width, height | |||
outside = box[1] - h >= 0 # label fits outside box | |||
self.draw.rectangle([box[0], | |||
self.draw.rectangle((box[0], | |||
box[1] - h if outside else box[1], | |||
box[0] + w + 1, | |||
box[1] + 1 if outside else box[1] + h + 1], fill=color) | |||
box[1] + 1 if outside else box[1] + h + 1), fill=color) | |||
# self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls') # for PIL>8.0 | |||
self.draw.text((box[0], box[1] - h if outside else box[1]), label, fill=txt_color, font=self.font) | |||
else: # cv2 | |||
@@ -210,7 +207,7 @@ def plot_images(images, targets, paths=None, fname='images.jpg', names=None, max | |||
# Annotate | |||
fs = int((h + w) * ns * 0.01) # font size | |||
annotator = Annotator(mosaic, line_width=round(fs / 10), font_size=fs, pil=True) | |||
annotator = Annotator(mosaic, line_width=round(fs / 10), font_size=fs, pil=True, example=names) | |||
for i in range(i + 1): | |||
x, y = int(w * (i // ns)), int(h * (i % ns)) # block origin | |||
annotator.rectangle([x, y, x + w, y + h], None, (255, 255, 255), width=2) # borders |