Update val_batch*.jpg for Chinese fonts (#6526)
Browse files* 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>
- utils/general.py +42 -29
- utils/plots.py +10 -13
utils/general.py
CHANGED
@@ -37,6 +37,7 @@ FILE = Path(__file__).resolve()
|
|
37 |
ROOT = FILE.parents[1] # YOLOv5 root directory
|
38 |
NUM_THREADS = min(8, max(1, os.cpu_count() - 1)) # number of YOLOv5 multiprocessing threads
|
39 |
VERBOSE = str(os.getenv('YOLOv5_VERBOSE', True)).lower() == 'true' # global verbose mode
|
|
|
40 |
|
41 |
torch.set_printoptions(linewidth=320, precision=5, profile='long')
|
42 |
np.set_printoptions(linewidth=320, formatter={'float_kind': '{:11.5g}'.format}) # format short g, %precision=5
|
@@ -55,6 +56,21 @@ def is_kaggle():
|
|
55 |
return False
|
56 |
|
57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
def set_logging(name=None, verbose=VERBOSE):
|
59 |
# Sets level and returns logger
|
60 |
if is_kaggle():
|
@@ -68,6 +84,22 @@ def set_logging(name=None, verbose=VERBOSE):
|
|
68 |
LOGGER = set_logging('yolov5') # define globally (used in train.py, val.py, detect.py, etc.)
|
69 |
|
70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
class Profile(contextlib.ContextDecorator):
|
72 |
# Usage: @Profile() decorator or 'with Profile():' context manager
|
73 |
def __enter__(self):
|
@@ -152,34 +184,6 @@ def get_latest_run(search_dir='.'):
|
|
152 |
return max(last_list, key=os.path.getctime) if last_list else ''
|
153 |
|
154 |
|
155 |
-
def user_config_dir(dir='Ultralytics', env_var='YOLOV5_CONFIG_DIR'):
|
156 |
-
# Return path of user configuration directory. Prefer environment variable if exists. Make dir if required.
|
157 |
-
env = os.getenv(env_var)
|
158 |
-
if env:
|
159 |
-
path = Path(env) # use environment variable
|
160 |
-
else:
|
161 |
-
cfg = {'Windows': 'AppData/Roaming', 'Linux': '.config', 'Darwin': 'Library/Application Support'} # 3 OS dirs
|
162 |
-
path = Path.home() / cfg.get(platform.system(), '') # OS-specific config dir
|
163 |
-
path = (path if is_writeable(path) else Path('/tmp')) / dir # GCP and AWS lambda fix, only /tmp is writeable
|
164 |
-
path.mkdir(exist_ok=True) # make if required
|
165 |
-
return path
|
166 |
-
|
167 |
-
|
168 |
-
def is_writeable(dir, test=False):
|
169 |
-
# Return True if directory has write permissions, test opening a file with write permissions if test=True
|
170 |
-
if test: # method 1
|
171 |
-
file = Path(dir) / 'tmp.txt'
|
172 |
-
try:
|
173 |
-
with open(file, 'w'): # open file with write permissions
|
174 |
-
pass
|
175 |
-
file.unlink() # remove file
|
176 |
-
return True
|
177 |
-
except OSError:
|
178 |
-
return False
|
179 |
-
else: # method 2
|
180 |
-
return os.access(dir, os.R_OK) # possible issues on Windows
|
181 |
-
|
182 |
-
|
183 |
def is_docker():
|
184 |
# Is environment a Docker container?
|
185 |
return Path('/workspace').exists() # or Path('/.dockerenv').exists()
|
@@ -207,7 +211,7 @@ def is_ascii(s=''):
|
|
207 |
|
208 |
def is_chinese(s='人工智能'):
|
209 |
# Is string composed of any Chinese characters?
|
210 |
-
return re.search('[\u4e00-\u9fff]', s)
|
211 |
|
212 |
|
213 |
def emojis(str=''):
|
@@ -378,6 +382,15 @@ def check_file(file, suffix=''):
|
|
378 |
return files[0] # return file
|
379 |
|
380 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
381 |
def check_dataset(data, autodownload=True):
|
382 |
# Download and/or unzip dataset if not found locally
|
383 |
# Usage: https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128_with_yaml.zip
|
|
|
37 |
ROOT = FILE.parents[1] # YOLOv5 root directory
|
38 |
NUM_THREADS = min(8, max(1, os.cpu_count() - 1)) # number of YOLOv5 multiprocessing threads
|
39 |
VERBOSE = str(os.getenv('YOLOv5_VERBOSE', True)).lower() == 'true' # global verbose mode
|
40 |
+
FONT = 'Arial.ttf' # https://ultralytics.com/assets/Arial.ttf
|
41 |
|
42 |
torch.set_printoptions(linewidth=320, precision=5, profile='long')
|
43 |
np.set_printoptions(linewidth=320, formatter={'float_kind': '{:11.5g}'.format}) # format short g, %precision=5
|
|
|
56 |
return False
|
57 |
|
58 |
|
59 |
+
def is_writeable(dir, test=False):
|
60 |
+
# Return True if directory has write permissions, test opening a file with write permissions if test=True
|
61 |
+
if test: # method 1
|
62 |
+
file = Path(dir) / 'tmp.txt'
|
63 |
+
try:
|
64 |
+
with open(file, 'w'): # open file with write permissions
|
65 |
+
pass
|
66 |
+
file.unlink() # remove file
|
67 |
+
return True
|
68 |
+
except OSError:
|
69 |
+
return False
|
70 |
+
else: # method 2
|
71 |
+
return os.access(dir, os.R_OK) # possible issues on Windows
|
72 |
+
|
73 |
+
|
74 |
def set_logging(name=None, verbose=VERBOSE):
|
75 |
# Sets level and returns logger
|
76 |
if is_kaggle():
|
|
|
84 |
LOGGER = set_logging('yolov5') # define globally (used in train.py, val.py, detect.py, etc.)
|
85 |
|
86 |
|
87 |
+
def user_config_dir(dir='Ultralytics', env_var='YOLOV5_CONFIG_DIR'):
|
88 |
+
# Return path of user configuration directory. Prefer environment variable if exists. Make dir if required.
|
89 |
+
env = os.getenv(env_var)
|
90 |
+
if env:
|
91 |
+
path = Path(env) # use environment variable
|
92 |
+
else:
|
93 |
+
cfg = {'Windows': 'AppData/Roaming', 'Linux': '.config', 'Darwin': 'Library/Application Support'} # 3 OS dirs
|
94 |
+
path = Path.home() / cfg.get(platform.system(), '') # OS-specific config dir
|
95 |
+
path = (path if is_writeable(path) else Path('/tmp')) / dir # GCP and AWS lambda fix, only /tmp is writeable
|
96 |
+
path.mkdir(exist_ok=True) # make if required
|
97 |
+
return path
|
98 |
+
|
99 |
+
|
100 |
+
CONFIG_DIR = user_config_dir() # Ultralytics settings dir
|
101 |
+
|
102 |
+
|
103 |
class Profile(contextlib.ContextDecorator):
|
104 |
# Usage: @Profile() decorator or 'with Profile():' context manager
|
105 |
def __enter__(self):
|
|
|
184 |
return max(last_list, key=os.path.getctime) if last_list else ''
|
185 |
|
186 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
187 |
def is_docker():
|
188 |
# Is environment a Docker container?
|
189 |
return Path('/workspace').exists() # or Path('/.dockerenv').exists()
|
|
|
211 |
|
212 |
def is_chinese(s='人工智能'):
|
213 |
# Is string composed of any Chinese characters?
|
214 |
+
return True if re.search('[\u4e00-\u9fff]', str(s)) else False
|
215 |
|
216 |
|
217 |
def emojis(str=''):
|
|
|
382 |
return files[0] # return file
|
383 |
|
384 |
|
385 |
+
def check_font(font=FONT):
|
386 |
+
# Download font to CONFIG_DIR if necessary
|
387 |
+
font = Path(font)
|
388 |
+
if not font.exists() and not (CONFIG_DIR / font.name).exists():
|
389 |
+
url = "https://ultralytics.com/assets/" + font.name
|
390 |
+
LOGGER.info(f'Downloading {url} to {CONFIG_DIR / font.name}...')
|
391 |
+
torch.hub.download_url_to_file(url, str(font), progress=False)
|
392 |
+
|
393 |
+
|
394 |
def check_dataset(data, autodownload=True):
|
395 |
# Download and/or unzip dataset if not found locally
|
396 |
# Usage: https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128_with_yaml.zip
|
utils/plots.py
CHANGED
@@ -17,12 +17,11 @@ import seaborn as sn
|
|
17 |
import torch
|
18 |
from PIL import Image, ImageDraw, ImageFont
|
19 |
|
20 |
-
from utils.general import (
|
21 |
-
|
22 |
from utils.metrics import fitness
|
23 |
|
24 |
# Settings
|
25 |
-
CONFIG_DIR = user_config_dir() # Ultralytics settings dir
|
26 |
RANK = int(os.getenv('RANK', -1))
|
27 |
matplotlib.rc('font', **{'size': 11})
|
28 |
matplotlib.use('Agg') # for writing to files only
|
@@ -49,16 +48,14 @@ class Colors:
|
|
49 |
colors = Colors() # create instance for 'from utils.plots import colors'
|
50 |
|
51 |
|
52 |
-
def
|
53 |
# Return a PIL TrueType Font, downloading to CONFIG_DIR if necessary
|
54 |
font = Path(font)
|
55 |
font = font if font.exists() else (CONFIG_DIR / font.name)
|
56 |
try:
|
57 |
return ImageFont.truetype(str(font) if font.exists() else font.name, size)
|
58 |
except Exception as e: # download if missing
|
59 |
-
|
60 |
-
LOGGER.info(f'Downloading {url} to {font}...')
|
61 |
-
torch.hub.download_url_to_file(url, str(font), progress=False)
|
62 |
try:
|
63 |
return ImageFont.truetype(str(font), size)
|
64 |
except TypeError:
|
@@ -67,7 +64,7 @@ def check_font(font='Arial.ttf', size=10):
|
|
67 |
|
68 |
class Annotator:
|
69 |
if RANK in (-1, 0):
|
70 |
-
|
71 |
|
72 |
# YOLOv5 Annotator for train/val mosaics and jpgs and detect/hub inference annotations
|
73 |
def __init__(self, im, line_width=None, font_size=None, font='Arial.ttf', pil=False, example='abc'):
|
@@ -76,8 +73,8 @@ class Annotator:
|
|
76 |
if self.pil: # use PIL
|
77 |
self.im = im if isinstance(im, Image.Image) else Image.fromarray(im)
|
78 |
self.draw = ImageDraw.Draw(self.im)
|
79 |
-
self.font =
|
80 |
-
|
81 |
else: # use cv2
|
82 |
self.im = im
|
83 |
self.lw = line_width or max(round(sum(im.shape) / 2 * 0.003), 2) # line width
|
@@ -89,10 +86,10 @@ class Annotator:
|
|
89 |
if label:
|
90 |
w, h = self.font.getsize(label) # text width, height
|
91 |
outside = box[1] - h >= 0 # label fits outside box
|
92 |
-
self.draw.rectangle(
|
93 |
box[1] - h if outside else box[1],
|
94 |
box[0] + w + 1,
|
95 |
-
box[1] + 1 if outside else box[1] + h + 1
|
96 |
# self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls') # for PIL>8.0
|
97 |
self.draw.text((box[0], box[1] - h if outside else box[1]), label, fill=txt_color, font=self.font)
|
98 |
else: # cv2
|
@@ -210,7 +207,7 @@ def plot_images(images, targets, paths=None, fname='images.jpg', names=None, max
|
|
210 |
|
211 |
# Annotate
|
212 |
fs = int((h + w) * ns * 0.01) # font size
|
213 |
-
annotator = Annotator(mosaic, line_width=round(fs / 10), font_size=fs, pil=True)
|
214 |
for i in range(i + 1):
|
215 |
x, y = int(w * (i // ns)), int(h * (i % ns)) # block origin
|
216 |
annotator.rectangle([x, y, x + w, y + h], None, (255, 255, 255), width=2) # borders
|
|
|
17 |
import torch
|
18 |
from PIL import Image, ImageDraw, ImageFont
|
19 |
|
20 |
+
from utils.general import (CONFIG_DIR, FONT, LOGGER, Timeout, check_font, check_requirements, clip_coords,
|
21 |
+
increment_path, is_ascii, is_chinese, try_except, xywh2xyxy, xyxy2xywh)
|
22 |
from utils.metrics import fitness
|
23 |
|
24 |
# Settings
|
|
|
25 |
RANK = int(os.getenv('RANK', -1))
|
26 |
matplotlib.rc('font', **{'size': 11})
|
27 |
matplotlib.use('Agg') # for writing to files only
|
|
|
48 |
colors = Colors() # create instance for 'from utils.plots import colors'
|
49 |
|
50 |
|
51 |
+
def check_pil_font(font=FONT, size=10):
|
52 |
# Return a PIL TrueType Font, downloading to CONFIG_DIR if necessary
|
53 |
font = Path(font)
|
54 |
font = font if font.exists() else (CONFIG_DIR / font.name)
|
55 |
try:
|
56 |
return ImageFont.truetype(str(font) if font.exists() else font.name, size)
|
57 |
except Exception as e: # download if missing
|
58 |
+
check_font(font)
|
|
|
|
|
59 |
try:
|
60 |
return ImageFont.truetype(str(font), size)
|
61 |
except TypeError:
|
|
|
64 |
|
65 |
class Annotator:
|
66 |
if RANK in (-1, 0):
|
67 |
+
check_pil_font() # download TTF if necessary
|
68 |
|
69 |
# YOLOv5 Annotator for train/val mosaics and jpgs and detect/hub inference annotations
|
70 |
def __init__(self, im, line_width=None, font_size=None, font='Arial.ttf', pil=False, example='abc'):
|
|
|
73 |
if self.pil: # use PIL
|
74 |
self.im = im if isinstance(im, Image.Image) else Image.fromarray(im)
|
75 |
self.draw = ImageDraw.Draw(self.im)
|
76 |
+
self.font = check_pil_font(font='Arial.Unicode.ttf' if is_chinese(example) else font,
|
77 |
+
size=font_size or max(round(sum(self.im.size) / 2 * 0.035), 12))
|
78 |
else: # use cv2
|
79 |
self.im = im
|
80 |
self.lw = line_width or max(round(sum(im.shape) / 2 * 0.003), 2) # line width
|
|
|
86 |
if label:
|
87 |
w, h = self.font.getsize(label) # text width, height
|
88 |
outside = box[1] - h >= 0 # label fits outside box
|
89 |
+
self.draw.rectangle((box[0],
|
90 |
box[1] - h if outside else box[1],
|
91 |
box[0] + w + 1,
|
92 |
+
box[1] + 1 if outside else box[1] + h + 1), fill=color)
|
93 |
# self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls') # for PIL>8.0
|
94 |
self.draw.text((box[0], box[1] - h if outside else box[1]), label, fill=txt_color, font=self.font)
|
95 |
else: # cv2
|
|
|
207 |
|
208 |
# Annotate
|
209 |
fs = int((h + w) * ns * 0.01) # font size
|
210 |
+
annotator = Annotator(mosaic, line_width=round(fs / 10), font_size=fs, pil=True, example=names)
|
211 |
for i in range(i + 1):
|
212 |
x, y = int(w * (i // ns)), int(h * (i % ns)) # block origin
|
213 |
annotator.rectangle([x, y, x + w, y + h], None, (255, 255, 255), width=2) # borders
|