glenn-jocher commited on
Commit
2da4e7a
1 Parent(s): 63e09fd

Merge PIL and OpenCV in `plot_one_box(use_pil=False)` (#4416)

Browse files

* Merge PIL and OpenCV box plotting functions

* Add ASCII check to plot_one_box

* Cleanup

* Cleanup2

Files changed (4) hide show
  1. detect.py +1 -1
  2. models/common.py +1 -1
  3. utils/general.py +6 -1
  4. utils/plots.py +27 -29
detect.py CHANGED
@@ -156,7 +156,7 @@ def run(weights='yolov5s.pt', # model.pt path(s)
156
  if save_img or save_crop or view_img: # Add bbox to image
157
  c = int(cls) # integer class
158
  label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf:.2f}')
159
- plot_one_box(xyxy, im0, label=label, color=colors(c, True), line_thickness=line_thickness)
160
  if save_crop:
161
  save_one_box(xyxy, imc, file=save_dir / 'crops' / names[c] / f'{p.stem}.jpg', BGR=True)
162
 
 
156
  if save_img or save_crop or view_img: # Add bbox to image
157
  c = int(cls) # integer class
158
  label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf:.2f}')
159
+ im0 = plot_one_box(xyxy, im0, label=label, color=colors(c, True), line_width=line_thickness)
160
  if save_crop:
161
  save_one_box(xyxy, imc, file=save_dir / 'crops' / names[c] / f'{p.stem}.jpg', BGR=True)
162
 
models/common.py CHANGED
@@ -354,7 +354,7 @@ class Detections:
354
  if crop:
355
  save_one_box(box, im, file=save_dir / 'crops' / self.names[int(cls)] / self.files[i])
356
  else: # all others
357
- plot_one_box(box, im, label=label, color=colors(cls))
358
  else:
359
  str += '(no detections)'
360
 
 
354
  if crop:
355
  save_one_box(box, im, file=save_dir / 'crops' / self.names[int(cls)] / self.files[i])
356
  else: # all others
357
+ im = plot_one_box(box, im, label=label, color=colors(cls))
358
  else:
359
  str += '(no detections)'
360
 
utils/general.py CHANGED
@@ -110,9 +110,14 @@ def is_pip():
110
  return 'site-packages' in Path(__file__).absolute().parts
111
 
112
 
 
 
 
 
 
113
  def emojis(str=''):
114
  # Return platform-dependent emoji-safe version of string
115
- return str.encode().decode(encoding='ascii', errors='ignore') if platform.system() == 'Windows' else str
116
 
117
 
118
  def file_size(file):
 
110
  return 'site-packages' in Path(__file__).absolute().parts
111
 
112
 
113
+ def is_ascii(str=''):
114
+ # Is string composed of all ASCII (no UTF) characters?
115
+ return len(str.encode().decode('ascii', 'ignore')) == len(str)
116
+
117
+
118
  def emojis(str=''):
119
  # Return platform-dependent emoji-safe version of string
120
+ return str.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else str
121
 
122
 
123
  def file_size(file):
utils/plots.py CHANGED
@@ -1,20 +1,19 @@
1
  # Plotting utils
2
 
 
3
  from copy import copy
4
  from pathlib import Path
5
 
6
  import cv2
7
- import math
8
  import matplotlib
9
  import matplotlib.pyplot as plt
10
  import numpy as np
11
  import pandas as pd
12
  import seaborn as sn
13
  import torch
14
- import yaml
15
  from PIL import Image, ImageDraw, ImageFont
16
 
17
- from utils.general import xywh2xyxy, xyxy2xywh
18
  from utils.metrics import fitness
19
 
20
  # Settings
@@ -65,32 +64,31 @@ def butter_lowpass_filtfilt(data, cutoff=1500, fs=50000, order=5):
65
  return filtfilt(b, a, data) # forward-backward filter
66
 
67
 
68
- def plot_one_box(x, im, color=(128, 128, 128), label=None, line_thickness=3):
69
- # Plots one bounding box on image 'im' using OpenCV
70
  assert im.data.contiguous, 'Image not contiguous. Apply np.ascontiguousarray(im) to plot_on_box() input image.'
71
- tl = line_thickness or round(0.002 * (im.shape[0] + im.shape[1]) / 2) + 1 # line/font thickness
72
- c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
73
- cv2.rectangle(im, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
74
- if label:
75
- tf = max(tl - 1, 1) # font thickness
76
- t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
77
- c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
78
- cv2.rectangle(im, c1, c2, color, -1, cv2.LINE_AA) # filled
79
- cv2.putText(im, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)
80
-
81
-
82
- def plot_one_box_PIL(box, im, color=(128, 128, 128), label=None, line_thickness=3):
83
- # Plots one bounding box on image 'im' using PIL
84
- im = Image.fromarray(im)
85
- draw = ImageDraw.Draw(im)
86
- line_thickness = line_thickness or max(int(min(im.size) / 200), 2)
87
- draw.rectangle(box, width=line_thickness, outline=color) # plot
88
- if label:
89
- font = ImageFont.truetype("Arial.ttf", size=max(round(max(im.size) / 40), 12))
90
- txt_width, txt_height = font.getsize(label)
91
- draw.rectangle([box[0], box[1] - txt_height + 4, box[0] + txt_width, box[1]], fill=color)
92
- draw.text((box[0], box[1] - txt_height + 1), label, fill=(255, 255, 255), font=font)
93
- return np.asarray(im)
94
 
95
 
96
  def plot_wh_methods(): # from utils.plots import *; plot_wh_methods()
@@ -180,7 +178,7 @@ def plot_images(images, targets, paths=None, fname='images.jpg', names=None, max
180
  cls = names[cls] if names else cls
181
  if labels or conf[j] > 0.25: # 0.25 conf thresh
182
  label = '%s' % cls if labels else '%s %.1f' % (cls, conf[j])
183
- plot_one_box(box, mosaic, label=label, color=color, line_thickness=tl)
184
 
185
  # Draw image filename labels
186
  if paths:
 
1
  # Plotting utils
2
 
3
+ import math
4
  from copy import copy
5
  from pathlib import Path
6
 
7
  import cv2
 
8
  import matplotlib
9
  import matplotlib.pyplot as plt
10
  import numpy as np
11
  import pandas as pd
12
  import seaborn as sn
13
  import torch
 
14
  from PIL import Image, ImageDraw, ImageFont
15
 
16
+ from utils.general import is_ascii, xyxy2xywh, xywh2xyxy
17
  from utils.metrics import fitness
18
 
19
  # Settings
 
64
  return filtfilt(b, a, data) # forward-backward filter
65
 
66
 
67
+ def plot_one_box(box, im, color=(128, 128, 128), txt_color=(255, 255, 255), label=None, line_width=3, use_pil=False):
68
+ # Plots one xyxy box on image im with label
69
  assert im.data.contiguous, 'Image not contiguous. Apply np.ascontiguousarray(im) to plot_on_box() input image.'
70
+ lw = line_width or max(int(min(im.size) / 200), 2) # line width
71
+
72
+ if use_pil or not is_ascii(label): # use PIL
73
+ im = Image.fromarray(im)
74
+ draw = ImageDraw.Draw(im)
75
+ draw.rectangle(box, width=lw + 1, outline=color) # plot
76
+ if label:
77
+ font = ImageFont.truetype("Arial.ttf", size=max(round(max(im.size) / 40), 12))
78
+ txt_width, txt_height = font.getsize(label)
79
+ draw.rectangle([box[0], box[1] - txt_height + 4, box[0] + txt_width, box[1]], fill=color)
80
+ draw.text((box[0], box[1] - txt_height + 1), label, fill=txt_color, font=font)
81
+ return np.asarray(im)
82
+ else: # use OpenCV
83
+ c1, c2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
84
+ cv2.rectangle(im, c1, c2, color, thickness=lw, lineType=cv2.LINE_AA)
85
+ if label:
86
+ tf = max(lw - 1, 1) # font thickness
87
+ txt_width, txt_height = cv2.getTextSize(label, 0, fontScale=lw / 3, thickness=tf)[0]
88
+ c2 = c1[0] + txt_width, c1[1] - txt_height - 3
89
+ cv2.rectangle(im, c1, c2, color, -1, cv2.LINE_AA) # filled
90
+ cv2.putText(im, label, (c1[0], c1[1] - 2), 0, lw / 3, txt_color, thickness=tf, lineType=cv2.LINE_AA)
91
+ return im
 
92
 
93
 
94
  def plot_wh_methods(): # from utils.plots import *; plot_wh_methods()
 
178
  cls = names[cls] if names else cls
179
  if labels or conf[j] > 0.25: # 0.25 conf thresh
180
  label = '%s' % cls if labels else '%s %.1f' % (cls, conf[j])
181
+ mosaic = plot_one_box(box, mosaic, label=label, color=color, line_width=tl)
182
 
183
  # Draw image filename labels
184
  if paths: