From 7aaa5b58e5ccb0ed34ee6ac2e5538b5a14cdd4c0 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Mon, 27 Sep 2021 13:20:37 -0700 Subject: [PATCH 1/2] Automatic Chinese fonts plotting --- detect.py | 5 ++--- models/common.py | 13 ++++++------- utils/general.py | 7 +++---- utils/plots.py | 11 ++++++----- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/detect.py b/detect.py index 08e78bf64a3b..fae82833c5f6 100644 --- a/detect.py +++ b/detect.py @@ -23,7 +23,7 @@ from models.experimental import attempt_load from utils.datasets import LoadImages, LoadStreams from utils.general import apply_classifier, check_img_size, check_imshow, check_requirements, check_suffix, colorstr, \ - increment_path, is_ascii, non_max_suppression, print_args, save_one_box, scale_coords, set_logging, \ + increment_path, non_max_suppression, print_args, save_one_box, scale_coords, set_logging, \ strip_optimizer, xyxy2xywh from utils.plots import Annotator, colors from utils.torch_utils import load_classifier, select_device, time_sync @@ -108,7 +108,6 @@ def wrap_frozen_graph(gd, inputs, outputs): output_details = interpreter.get_output_details() # outputs int8 = input_details[0]['dtype'] == np.uint8 # is TFLite quantized uint8 model imgsz = check_img_size(imgsz, s=stride) # check image size - ascii = is_ascii(names) # names are ascii (use PIL for UTF-8) # Dataloader if webcam: @@ -190,7 +189,7 @@ def wrap_frozen_graph(gd, inputs, outputs): s += '%gx%g ' % img.shape[2:] # print string gn = torch.tensor(im0.shape)[[1, 0, 1, 0]] # normalization gain whwh imc = im0.copy() if save_crop else im0 # for save_crop - annotator = Annotator(im0, line_width=line_thickness, pil=not ascii) + annotator = Annotator(im0, line_width=line_thickness, example=str(names)) if len(det): # Rescale boxes from img_size to im0 size det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round() diff --git a/models/common.py b/models/common.py index 5305b03d5389..2acf6281f475 100644 --- a/models/common.py +++ b/models/common.py @@ -18,7 +18,7 @@ from torch.cuda import amp from utils.datasets import exif_transpose, letterbox -from utils.general import colorstr, increment_path, is_ascii, make_divisible, non_max_suppression, save_one_box, \ +from utils.general import colorstr, increment_path, make_divisible, non_max_suppression, save_one_box, \ scale_coords, xyxy2xywh from utils.plots import Annotator, colors from utils.torch_utils import time_sync @@ -356,7 +356,6 @@ def __init__(self, imgs, pred, files, times=None, names=None, shape=None): self.imgs = imgs # list of images as numpy arrays self.pred = pred # list of tensors pred[0] = (xyxy, conf, cls) self.names = names # class names - self.ascii = is_ascii(names) # names are ascii (use PIL for UTF-8) self.files = files # image filenames self.xyxy = pred # xyxy pixels self.xywh = [xyxy2xywh(x) for x in pred] # xywh pixels @@ -369,13 +368,13 @@ def __init__(self, imgs, pred, files, times=None, names=None, shape=None): def display(self, pprint=False, show=False, save=False, crop=False, render=False, save_dir=Path('')): crops = [] for i, (im, pred) in enumerate(zip(self.imgs, self.pred)): - str = f'image {i + 1}/{len(self.pred)}: {im.shape[0]}x{im.shape[1]} ' + s = f'image {i + 1}/{len(self.pred)}: {im.shape[0]}x{im.shape[1]} ' # string if pred.shape[0]: for c in pred[:, -1].unique(): n = (pred[:, -1] == c).sum() # detections per class - str += f"{n} {self.names[int(c)]}{'s' * (n > 1)}, " # add to string + s += f"{n} {self.names[int(c)]}{'s' * (n > 1)}, " # add to string if show or save or render or crop: - annotator = Annotator(im, pil=not self.ascii) + annotator = Annotator(im, example=str(self.names)) for *box, conf, cls in reversed(pred): # xyxy, confidence, class label = f'{self.names[int(cls)]} {conf:.2f}' if crop: @@ -386,11 +385,11 @@ def display(self, pprint=False, show=False, save=False, crop=False, render=False annotator.box_label(box, label, color=colors(cls)) im = annotator.im else: - str += '(no detections)' + s += '(no detections)' im = Image.fromarray(im.astype(np.uint8)) if isinstance(im, np.ndarray) else im # from np if pprint: - LOGGER.info(str.rstrip(', ')) + LOGGER.info(s.rstrip(', ')) if show: im.show(self.files[i]) # show if save: diff --git a/utils/general.py b/utils/general.py index 00bafb1e9537..8421981147f7 100755 --- a/utils/general.py +++ b/utils/general.py @@ -161,10 +161,9 @@ def is_pip(): return 'site-packages' in Path(__file__).resolve().parts -def is_ascii(s=''): - # Is string composed of all ASCII (no UTF) characters? - s = str(s) # convert list, tuple, None, etc. to str - return len(s.encode().decode('ascii', 'ignore')) == len(s) +def is_chinese(s='人工智能'): + # Is string composed of any Chinese characters? + return re.search('[\u4e00-\u9fff]', s) def emojis(str=''): diff --git a/utils/plots.py b/utils/plots.py index d8e7c07f39b1..c33a1cb9bbe2 100644 --- a/utils/plots.py +++ b/utils/plots.py @@ -17,7 +17,7 @@ import torch from PIL import Image, ImageDraw, ImageFont -from utils.general import user_config_dir, is_ascii, xywh2xyxy, xyxy2xywh +from utils.general import user_config_dir, is_chinese, xywh2xyxy, xyxy2xywh from utils.metrics import fitness # Settings @@ -66,20 +66,21 @@ class Annotator: check_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=True): + def __init__(self, im, line_width=None, font_size=None, font='Arial.ttf', pil=True, example='abc'): assert im.data.contiguous, 'Image not contiguous. Apply np.ascontiguousarray(im) to Annotator() input images.' - self.pil = pil + self.pil = pil or not example.isascii() or is_chinese(example) 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, size=font_size or max(round(sum(self.im.size) / 2 * 0.035), 12)) + 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)) else: # use cv2 self.im = im self.lw = line_width or max(round(sum(im.shape) / 2 * 0.003), 2) # line width def box_label(self, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255)): # Add one xyxy box to image with label - if self.pil or not is_ascii(label): + if self.pil or not label.isascii(): self.draw.rectangle(box, width=self.lw, outline=color) # box if label: w, h = self.font.getsize(label) # text width, height From 461e6dd639e5307efa25fb93b81596e239dbcbb3 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Mon, 27 Sep 2021 13:29:48 -0700 Subject: [PATCH 2/2] Default PIL=False --- utils/plots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/plots.py b/utils/plots.py index c33a1cb9bbe2..491c5704d67b 100644 --- a/utils/plots.py +++ b/utils/plots.py @@ -66,7 +66,7 @@ class Annotator: check_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=True, example='abc'): + def __init__(self, im, line_width=None, font_size=None, font='Arial.ttf', pil=False, example='abc'): assert im.data.contiguous, 'Image not contiguous. Apply np.ascontiguousarray(im) to Annotator() input images.' self.pil = pil or not example.isascii() or is_chinese(example) if self.pil: # use PIL @@ -178,7 +178,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) + annotator = Annotator(mosaic, line_width=round(fs / 10), font_size=fs, pil=True) 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