Last active
October 6, 2020 20:44
-
-
Save Xonxt/685b15c07a667dd8121ee44399266cfb to your computer and use it in GitHub Desktop.
A function, that allows more flexibility when rendering text with OpenCV in Python. You can add text outline, draw a list of strings with just one call, and even align the text block horizontally or vertically relative to an anchor point.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
import cv2 | |
from enum import Enum | |
class Align(Enum): | |
LEFT = 0 | |
CENTER = 1 | |
RIGHT = 2 | |
class Valign(Enum): | |
TOP = 0 | |
MIDDLE = 1 | |
BOTTOM = 2 | |
class Sorting(Enum): | |
NONE = 0 | |
DSCN = 1 | |
ASCN = 2 | |
def draw_text(canvas, string, org=(0,0), align=Align.LEFT, valign=Valign.TOP, inner_pad=0, outer_pad=[0,0], | |
font_face=cv2.FONT_HERSHEY_PLAIN, font_size=2, font_color=(255, 255, 255), font_width=1, line_type=cv2.LINE_4, | |
outline=False, outline_color=(0, 0, 0), outline_width=2, darken_background=-1, background_color=None, sort=Sorting.NONE): | |
""" | |
A function for a more sophisticated and flexible text rendering. Using this instead of the standard cv2.putText() may result | |
in a small performance loss. | |
""" | |
max_width = 0 | |
max_height = outer_pad[1] * 2 | |
if not isinstance(string, list): | |
string = [string] | |
str_quantity = len(string) | |
# list of dimensions | |
dims_array = list() | |
# iterate through strings to calculate the box dimensions | |
for i, s in enumerate(string): | |
dims, _ = cv2.getTextSize(s, font_face, font_size, font_width) | |
dims_array.append(dims) | |
# calculate text block dimensions: | |
if dims[0] > max_width: | |
max_width = dims[0] | |
max_height += dims[1] | |
if i > 0 and i < str_quantity: | |
max_height += inner_pad | |
# set the maximum width of the text block | |
max_width = max_width + outer_pad[0] * 2 | |
# if no image was supplied, just return the text block size | |
if canvas is None: | |
return (max_width, max_height) | |
# calculate the top-left point for the text block | |
tl = [org[0], org[1]] | |
br = [tl[0] + max_width, tl[1] + max_height] | |
# horizontal align: | |
if align == Align.RIGHT: | |
tl[0] = org[0] - max_width | |
br[0] = org[0] | |
elif align == Align.CENTER: | |
tl[0] = org[0] - max_width // 2 | |
br[0] = org[0] + max_width // 2 | |
# vertical align: | |
if valign == Valign.MIDDLE: | |
tl[1] = org[1] - max_height // 2 | |
br[1] = org[1] + max_height // 2 | |
elif valign == Valign.BOTTOM: | |
tl[1] = org[1] - max_height | |
br[1] = org[1] | |
if darken_background >= 0: | |
darken_background = np.clip(darken_background, 0, 1) | |
# if necessary, draw a background color | |
if background_color is not None and isinstance(background_color, tuple): | |
if darken_background < 0: | |
cv2.rectangle(canvas, (tl[0], tl[1]), (br[0], br[1]), background_color, outline_width) | |
cv2.rectangle(canvas, (tl[0], tl[1]), (br[0], br[1]), background_color, -1) | |
else: | |
overlay = np.zeros(canvas.shape, dtype=canvas.dtype) | |
cv2.rectangle(overlay, (tl[0], tl[1]), (br[0], br[1]), background_color, outline_width) | |
cv2.rectangle(overlay, (tl[0], tl[1]), (br[0], br[1]), background_color, -1) | |
canvas[:] = cv2.addWeighted(canvas, 1.0, overlay, darken_background, 1.0) | |
else: | |
# if necessary, darken the background of the text by the specified value | |
if 0 <= darken_background <= 1: | |
canvas[tl[1]:br[1], tl[0]:br[0]] = np.clip(np.uint8(canvas[tl[1]:br[1], tl[0]:br[0]]) * darken_background, 0, 255) | |
# sort the text by width, if necessary: | |
if sort == Sorting.NONE: | |
dims_array = enumerate(dims_array) | |
else: | |
dims_array = sorted(enumerate(dims_array), key=lambda x: x[1][0], reverse=(sort == Sorting.DSCN)) | |
# iterate through text lines and draw them: | |
for i, (j, dim) in enumerate(dims_array): | |
dims = dim | |
s = string[j] | |
pt = (outer_pad[0] + int(tl[0]), outer_pad[1] + int(tl[1]) + (i+1) * dims[1] + i * inner_pad) | |
# draw the outline, when necessary | |
if outline: | |
cv2.putText(canvas, s, pt, font_face, font_size, outline_color, font_width + outline_width, line_type) | |
# draw the string | |
cv2.putText(canvas, s, pt, font_face, font_size, font_color[i] if isinstance(font_color, list) else font_color, | |
font_width, line_type) | |
# return the total width and height of the text block | |
return (max_width, max_height) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment