Import Annotator class from ultralytics package (#11930)
* Import Annotator class from `ultralytics` package * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Import Annotator class from `ultralytics` package * Update requirements.txt Signed-off-by: Glenn Jocher <glenn.jocher@ultralytics.com> --------- Signed-off-by: Glenn Jocher <glenn.jocher@ultralytics.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
05e4c0543b
commit
2270f0d1e7
8 files changed
+15
-129
No files matched your search
+3
-2
@@ -43,12 +43,13 @@ if str(ROOT) not in sys.path:
|
|||||||
sys.path.append(str(ROOT)) # add ROOT to PATH
|
sys.path.append(str(ROOT)) # add ROOT to PATH
|
||||||
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
|
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
|
||||||
|
|
||||||
|
from ultralytics.utils.plotting import Annotator
|
||||||
|
|
||||||
from models.common import DetectMultiBackend
|
from models.common import DetectMultiBackend
|
||||||
from utils.augmentations import classify_transforms
|
from utils.augmentations import classify_transforms
|
||||||
from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadScreenshots, LoadStreams
|
from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadScreenshots, LoadStreams
|
||||||
from utils.general import (LOGGER, Profile, check_file, check_img_size, check_imshow, check_requirements, colorstr, cv2,
|
from utils.general import (LOGGER, Profile, check_file, check_img_size, check_imshow, check_requirements, colorstr, cv2,
|
||||||
increment_path, print_args, strip_optimizer)
|
increment_path, print_args, strip_optimizer)
|
||||||
from utils.plots import Annotator
|
|
||||||
from utils.torch_utils import select_device, smart_inference_mode
|
from utils.torch_utils import select_device, smart_inference_mode
|
||||||
|
|
||||||
|
|
||||||
@@ -144,7 +145,7 @@ def run(
|
|||||||
# Write results
|
# Write results
|
||||||
text = '\n'.join(f'{prob[j]:.2f} {names[j]}' for j in top5i)
|
text = '\n'.join(f'{prob[j]:.2f} {names[j]}' for j in top5i)
|
||||||
if save_img or view_img: # Add bbox to image
|
if save_img or view_img: # Add bbox to image
|
||||||
annotator.text((32, 32), text, txt_color=(255, 255, 255))
|
annotator.text([32, 32], text, txt_color=(255, 255, 255))
|
||||||
if save_txt: # Write to file
|
if save_txt: # Write to file
|
||||||
with open(f'{txt_path}.txt', 'a') as f:
|
with open(f'{txt_path}.txt', 'a') as f:
|
||||||
f.write(text + '\n')
|
f.write(text + '\n')
|
||||||
|
|||||||
@@ -42,11 +42,12 @@ if str(ROOT) not in sys.path:
|
|||||||
sys.path.append(str(ROOT)) # add ROOT to PATH
|
sys.path.append(str(ROOT)) # add ROOT to PATH
|
||||||
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
|
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
|
||||||
|
|
||||||
|
from ultralytics.utils.plotting import Annotator, colors, save_one_box
|
||||||
|
|
||||||
from models.common import DetectMultiBackend
|
from models.common import DetectMultiBackend
|
||||||
from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadScreenshots, LoadStreams
|
from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadScreenshots, LoadStreams
|
||||||
from utils.general import (LOGGER, Profile, check_file, check_img_size, check_imshow, check_requirements, colorstr, cv2,
|
from utils.general import (LOGGER, Profile, check_file, check_img_size, check_imshow, check_requirements, colorstr, cv2,
|
||||||
increment_path, non_max_suppression, print_args, scale_boxes, strip_optimizer, xyxy2xywh)
|
increment_path, non_max_suppression, print_args, scale_boxes, strip_optimizer, xyxy2xywh)
|
||||||
from utils.plots import Annotator, colors, save_one_box
|
|
||||||
from utils.torch_utils import select_device, smart_inference_mode
|
from utils.torch_utils import select_device, smart_inference_mode
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -23,13 +23,13 @@ import torch
|
|||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from torch.cuda import amp
|
from torch.cuda import amp
|
||||||
|
from ultralytics.utils.plotting import Annotator, colors, save_one_box
|
||||||
|
|
||||||
from utils import TryExcept
|
from utils import TryExcept
|
||||||
from utils.dataloaders import exif_transpose, letterbox
|
from utils.dataloaders import exif_transpose, letterbox
|
||||||
from utils.general import (LOGGER, ROOT, Profile, check_requirements, check_suffix, check_version, colorstr,
|
from utils.general import (LOGGER, ROOT, Profile, check_requirements, check_suffix, check_version, colorstr,
|
||||||
increment_path, is_jupyter, make_divisible, non_max_suppression, scale_boxes, xywh2xyxy,
|
increment_path, is_jupyter, make_divisible, non_max_suppression, scale_boxes, xywh2xyxy,
|
||||||
xyxy2xywh, yaml_load)
|
xyxy2xywh, yaml_load)
|
||||||
from utils.plots import Annotator, colors, save_one_box
|
|
||||||
from utils.torch_utils import copy_attr, smart_inference_mode
|
from utils.torch_utils import copy_attr, smart_inference_mode
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ thop>=0.1.1 # FLOPs computation
|
|||||||
torch>=1.7.0 # see https://pytorch.org/get-started/locally (recommended)
|
torch>=1.7.0 # see https://pytorch.org/get-started/locally (recommended)
|
||||||
torchvision>=0.8.1
|
torchvision>=0.8.1
|
||||||
tqdm>=4.64.0
|
tqdm>=4.64.0
|
||||||
ultralytics>=8.0.145
|
ultralytics>=8.0.146
|
||||||
# protobuf<=3.20.1 # https://github.com/ultralytics/yolov5/issues/8012
|
# protobuf<=3.20.1 # https://github.com/ultralytics/yolov5/issues/8012
|
||||||
|
|
||||||
# Logging ---------------------------------------------------------------------
|
# Logging ---------------------------------------------------------------------
|
||||||
|
|||||||
+2
-1
@@ -42,12 +42,13 @@ if str(ROOT) not in sys.path:
|
|||||||
sys.path.append(str(ROOT)) # add ROOT to PATH
|
sys.path.append(str(ROOT)) # add ROOT to PATH
|
||||||
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
|
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
|
||||||
|
|
||||||
|
from ultralytics.utils.plotting import Annotator, colors, save_one_box
|
||||||
|
|
||||||
from models.common import DetectMultiBackend
|
from models.common import DetectMultiBackend
|
||||||
from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadScreenshots, LoadStreams
|
from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadScreenshots, LoadStreams
|
||||||
from utils.general import (LOGGER, Profile, check_file, check_img_size, check_imshow, check_requirements, colorstr, cv2,
|
from utils.general import (LOGGER, Profile, check_file, check_img_size, check_imshow, check_requirements, colorstr, cv2,
|
||||||
increment_path, non_max_suppression, print_args, scale_boxes, scale_segments,
|
increment_path, non_max_suppression, print_args, scale_boxes, scale_segments,
|
||||||
strip_optimizer)
|
strip_optimizer)
|
||||||
from utils.plots import Annotator, colors, save_one_box
|
|
||||||
from utils.segment.general import masks2segments, process_mask, process_mask_native
|
from utils.segment.general import masks2segments, process_mask, process_mask_native
|
||||||
from utils.torch_utils import select_device, smart_inference_mode
|
from utils.torch_utils import select_device, smart_inference_mode
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import yaml
|
import yaml
|
||||||
|
from ultralytics.utils.plotting import Annotator, colors
|
||||||
from utils.plots import Annotator, colors
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import clearml
|
import clearml
|
||||||
|
|||||||
+4
-120
@@ -8,7 +8,6 @@ import math
|
|||||||
import os
|
import os
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from urllib.error import URLError
|
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import matplotlib
|
import matplotlib
|
||||||
@@ -17,14 +16,13 @@ import numpy as np
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
import seaborn as sn
|
import seaborn as sn
|
||||||
import torch
|
import torch
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw
|
||||||
from scipy.ndimage.filters import gaussian_filter1d
|
from scipy.ndimage.filters import gaussian_filter1d
|
||||||
|
from ultralytics.utils.plotting import Annotator
|
||||||
|
|
||||||
from utils import TryExcept, threaded
|
from utils import TryExcept, threaded
|
||||||
from utils.general import (CONFIG_DIR, FONT, LOGGER, check_font, check_requirements, clip_boxes, increment_path,
|
from utils.general import LOGGER, clip_boxes, increment_path, xywh2xyxy, xyxy2xywh
|
||||||
is_ascii, xywh2xyxy, xyxy2xywh)
|
|
||||||
from utils.metrics import fitness
|
from utils.metrics import fitness
|
||||||
from utils.segment.general import scale_image
|
|
||||||
|
|
||||||
# Settings
|
# Settings
|
||||||
RANK = int(os.getenv('RANK', -1))
|
RANK = int(os.getenv('RANK', -1))
|
||||||
@@ -53,120 +51,6 @@ class Colors:
|
|||||||
colors = Colors() # create instance for 'from utils.plots import colors'
|
colors = Colors() # create instance for 'from utils.plots import colors'
|
||||||
|
|
||||||
|
|
||||||
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: # download if missing
|
|
||||||
try:
|
|
||||||
check_font(font)
|
|
||||||
return ImageFont.truetype(str(font), size)
|
|
||||||
except TypeError:
|
|
||||||
check_requirements('Pillow>=8.4.0') # known issue https://github.com/ultralytics/yolov5/issues/5374
|
|
||||||
except URLError: # not online
|
|
||||||
return ImageFont.load_default()
|
|
||||||
|
|
||||||
|
|
||||||
class Annotator:
|
|
||||||
# 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'):
|
|
||||||
assert im.data.contiguous, 'Image not contiguous. Apply np.ascontiguousarray(im) to Annotator() input images.'
|
|
||||||
non_ascii = not is_ascii(example) # non-latin labels, i.e. asian, arabic, cyrillic
|
|
||||||
self.pil = pil or non_ascii
|
|
||||||
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_pil_font(font='Arial.Unicode.ttf' if non_ascii 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):
|
|
||||||
self.draw.rectangle(box, width=self.lw, outline=color) # box
|
|
||||||
if label:
|
|
||||||
w, h = self.font.getsize(label) # text width, height (WARNING: deprecated) in 9.2.0
|
|
||||||
# _, _, w, h = self.font.getbbox(label) # text width, height (New)
|
|
||||||
outside = box[1] - h >= 0 # label fits outside box
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
# 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
|
|
||||||
p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
|
|
||||||
cv2.rectangle(self.im, p1, p2, color, thickness=self.lw, lineType=cv2.LINE_AA)
|
|
||||||
if label:
|
|
||||||
tf = max(self.lw - 1, 1) # font thickness
|
|
||||||
w, h = cv2.getTextSize(label, 0, fontScale=self.lw / 3, thickness=tf)[0] # text width, height
|
|
||||||
outside = p1[1] - h >= 3
|
|
||||||
p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3
|
|
||||||
cv2.rectangle(self.im, p1, p2, color, -1, cv2.LINE_AA) # filled
|
|
||||||
cv2.putText(self.im,
|
|
||||||
label, (p1[0], p1[1] - 2 if outside else p1[1] + h + 2),
|
|
||||||
0,
|
|
||||||
self.lw / 3,
|
|
||||||
txt_color,
|
|
||||||
thickness=tf,
|
|
||||||
lineType=cv2.LINE_AA)
|
|
||||||
|
|
||||||
def masks(self, masks, colors, im_gpu, alpha=0.5, retina_masks=False):
|
|
||||||
"""Plot masks at once.
|
|
||||||
Args:
|
|
||||||
masks (tensor): predicted masks on cuda, shape: [n, h, w]
|
|
||||||
colors (List[List[Int]]): colors for predicted masks, [[r, g, b] * n]
|
|
||||||
im_gpu (tensor): img is in cuda, shape: [3, h, w], range: [0, 1]
|
|
||||||
alpha (float): mask transparency: 0.0 fully transparent, 1.0 opaque
|
|
||||||
"""
|
|
||||||
if self.pil:
|
|
||||||
# convert to numpy first
|
|
||||||
self.im = np.asarray(self.im).copy()
|
|
||||||
if len(masks) == 0:
|
|
||||||
self.im[:] = im_gpu.permute(1, 2, 0).contiguous().cpu().numpy() * 255
|
|
||||||
colors = torch.tensor(colors, device=im_gpu.device, dtype=torch.float32) / 255.0
|
|
||||||
colors = colors[:, None, None] # shape(n,1,1,3)
|
|
||||||
masks = masks.unsqueeze(3) # shape(n,h,w,1)
|
|
||||||
masks_color = masks * (colors * alpha) # shape(n,h,w,3)
|
|
||||||
|
|
||||||
inv_alph_masks = (1 - masks * alpha).cumprod(0) # shape(n,h,w,1)
|
|
||||||
mcs = (masks_color * inv_alph_masks).sum(0) * 2 # mask color summand shape(n,h,w,3)
|
|
||||||
|
|
||||||
im_gpu = im_gpu.flip(dims=[0]) # flip channel
|
|
||||||
im_gpu = im_gpu.permute(1, 2, 0).contiguous() # shape(h,w,3)
|
|
||||||
im_gpu = im_gpu * inv_alph_masks[-1] + mcs
|
|
||||||
im_mask = (im_gpu * 255).byte().cpu().numpy()
|
|
||||||
self.im[:] = im_mask if retina_masks else scale_image(im_gpu.shape, im_mask, self.im.shape)
|
|
||||||
if self.pil:
|
|
||||||
# convert im back to PIL and update draw
|
|
||||||
self.fromarray(self.im)
|
|
||||||
|
|
||||||
def rectangle(self, xy, fill=None, outline=None, width=1):
|
|
||||||
# Add rectangle to image (PIL-only)
|
|
||||||
self.draw.rectangle(xy, fill, outline, width)
|
|
||||||
|
|
||||||
def text(self, xy, text, txt_color=(255, 255, 255), anchor='top'):
|
|
||||||
# Add text to image (PIL-only)
|
|
||||||
if anchor == 'bottom': # start y from font bottom
|
|
||||||
w, h = self.font.getsize(text) # text width, height
|
|
||||||
xy[1] += 1 - h
|
|
||||||
self.draw.text(xy, text, fill=txt_color, font=self.font)
|
|
||||||
|
|
||||||
def fromarray(self, im):
|
|
||||||
# Update self.im from a numpy array
|
|
||||||
self.im = im if isinstance(im, Image.Image) else Image.fromarray(im)
|
|
||||||
self.draw = ImageDraw.Draw(self.im)
|
|
||||||
|
|
||||||
def result(self):
|
|
||||||
# Return annotated image as array
|
|
||||||
return np.asarray(self.im)
|
|
||||||
|
|
||||||
|
|
||||||
def feature_visualization(x, module_type, stage, n=32, save_dir=Path('runs/detect/exp')):
|
def feature_visualization(x, module_type, stage, n=32, save_dir=Path('runs/detect/exp')):
|
||||||
"""
|
"""
|
||||||
x: Features to be visualized
|
x: Features to be visualized
|
||||||
@@ -266,7 +150,7 @@ def plot_images(images, targets, paths=None, fname='images.jpg', names=None):
|
|||||||
x, y = int(w * (i // ns)), int(h * (i % ns)) # block origin
|
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
|
annotator.rectangle([x, y, x + w, y + h], None, (255, 255, 255), width=2) # borders
|
||||||
if paths:
|
if paths:
|
||||||
annotator.text((x + 5, y + 5), text=Path(paths[i]).name[:40], txt_color=(220, 220, 220)) # filenames
|
annotator.text([x + 5, y + 5], text=Path(paths[i]).name[:40], txt_color=(220, 220, 220)) # filenames
|
||||||
if len(targets) > 0:
|
if len(targets) > 0:
|
||||||
ti = targets[targets[:, 0] == i] # image targets
|
ti = targets[targets[:, 0] == i] # image targets
|
||||||
boxes = xywh2xyxy(ti[:, 2:6]).T
|
boxes = xywh2xyxy(ti[:, 2:6]).T
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ def plot_images_and_masks(images, targets, masks, paths=None, fname='images.jpg'
|
|||||||
x, y = int(w * (i // ns)), int(h * (i % ns)) # block origin
|
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
|
annotator.rectangle([x, y, x + w, y + h], None, (255, 255, 255), width=2) # borders
|
||||||
if paths:
|
if paths:
|
||||||
annotator.text((x + 5, y + 5), text=Path(paths[i]).name[:40], txt_color=(220, 220, 220)) # filenames
|
annotator.text([x + 5, y + 5], text=Path(paths[i]).name[:40], txt_color=(220, 220, 220)) # filenames
|
||||||
if len(targets) > 0:
|
if len(targets) > 0:
|
||||||
idx = targets[:, 0] == i
|
idx = targets[:, 0] == i
|
||||||
ti = targets[idx] # image targets
|
ti = targets[idx] # image targets
|
||||||
|
|||||||
Reference in new issue
Block a user