insecta / khandy /draw_utils.py
admin
sync
9d1ee0a
raw
history blame
No virus
5.6 kB
import numpy as np
import PIL
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
from PIL import ImageColor
def _is_legal_color(color):
if color is None:
return True
if isinstance(color, str):
return True
return isinstance(color, (tuple, list)) and len(color) == 3
def _normalize_color(color, pil_mode, swap_rgb=False):
if color is None:
return color
if isinstance(color, str):
color = ImageColor.getrgb(color)
gray = color[0]
if swap_rgb:
color = (color[2], color[1], color[0])
if pil_mode == 'L':
color = gray
return color
def draw_text(image, text, position, color=(255,0,0), font=None, font_size=15):
"""Draws text on given image.
Args:
image (ndarray).
text (str): text to be drawn.
position (Tuple[int, int]): position where to be drawn.
color (List[Union[str, Tuple[int, int, int]]]): text color.
font (str): A filename or file-like object containing a TrueType font. If the file is not found in this
filename, the loader may also search in other directories, such as the `fonts/` directory on Windows
or `/Library/Fonts/`, `/System/Library/Fonts/` and `~/Library/Fonts/` on macOS.
font_size (int): The requested font size in points.
References:
torchvision.utils.draw_bounding_boxes
"""
if isinstance(image, np.ndarray):
# PIL.Image.fromarray fails with uint16 arrays
# https://github.com/python-pillow/Pillow/issues/1514
if (image.dtype == np.uint16) and (image.ndim != 2):
image = (image / 256).astype(np.uint8)
pil_image = Image.fromarray(image)
elif isinstance(image, PIL.Image.Image):
pil_image = image
else:
raise TypeError('Unsupported image type!')
assert pil_image.mode in ['L', 'RGB', 'RGBA']
assert _is_legal_color(color)
color = _normalize_color(color, pil_image.mode, isinstance(image, np.ndarray))
if font is None:
font_object = ImageFont.load_default()
else:
font_object = ImageFont.truetype(font, size=font_size)
draw = ImageDraw.Draw(pil_image)
draw.text((position[0], position[1]), text,
fill=color, font=font_object)
if isinstance(image, np.ndarray):
return np.asarray(pil_image)
return pil_image
def draw_bounding_boxes(image, boxes, labels=None, colors=None,
fill=False, width=1, font=None, font_size=15):
"""Draws bounding boxes on given image.
Args:
image (ndarray).
boxes (ndarray): ndarray of size (N, 4) containing bounding boxes in (xmin, ymin, xmax, ymax) format.
labels (List[str]): List containing the labels of bounding boxes.
colors (List[Union[str, Tuple[int, int, int]]]): List containing the colors of bounding boxes or labels.
fill (bool): If `True` fills the bounding box with specified color.
width (int): Width of bounding box.
font (str): A filename or file-like object containing a TrueType font. If the file is not found in this
filename, the loader may also search in other directories, such as the `fonts/` directory on Windows
or `/Library/Fonts/`, `/System/Library/Fonts/` and `~/Library/Fonts/` on macOS.
font_size (int): The requested font size in points.
References:
torchvision.utils.draw_bounding_boxes
"""
if isinstance(image, np.ndarray):
# PIL.Image.fromarray fails with uint16 arrays
# https://github.com/python-pillow/Pillow/issues/1514
if (image.dtype == np.uint16) and (image.ndim != 2):
image = (image / 256).astype(np.uint8)
pil_image = Image.fromarray(image)
elif isinstance(image, PIL.Image.Image):
pil_image = image
else:
raise TypeError('Unsupported image type!')
pil_image = pil_image.convert('RGB')
if font is None:
font_object = ImageFont.load_default()
else:
font_object = ImageFont.truetype(font, size=font_size)
if fill:
draw = ImageDraw.Draw(pil_image, "RGBA")
else:
draw = ImageDraw.Draw(pil_image)
for i, bbox in enumerate(boxes):
if colors is None:
color = None
else:
color = colors[i]
assert _is_legal_color(color)
color = _normalize_color(color, pil_image.mode, isinstance(image, np.ndarray))
if fill:
if color is None:
fill_color = (255, 255, 255, 100)
elif isinstance(color, str):
# This will automatically raise Error if rgb cannot be parsed.
fill_color = ImageColor.getrgb(color) + (100,)
elif isinstance(color, tuple):
fill_color = color + (100,)
# the first argument of ImageDraw.rectangle:
# in old version only supports [(x0, y0), (x1, y1)]
# in new version supports either [(x0, y0), (x1, y1)] or [x0, y0, x1, y1]
draw.rectangle([(bbox[0], bbox[1]), (bbox[2], bbox[3])], width=width, outline=color, fill=fill_color)
else:
draw.rectangle([(bbox[0], bbox[1]), (bbox[2], bbox[3])], width=width, outline=color)
if labels is not None:
margin = width + 1
draw.text((bbox[0] + margin, bbox[1] + margin), labels[i], fill=color, font=font_object)
if isinstance(image, np.ndarray):
return np.asarray(pil_image)
return pil_image