OpenCV putText() new line character
Question:
I am using cv2.putText() to draw a text string on an image.
When I write:
cv2.putText(img, "This is n some text", (50,50), cv2.FONT_HERSHEY_SIMPLEX, 1, 2)
The text drawn on the image is:
This is ? some text
I was expecting the text to be printed in the new line as n
is an escape character for newline but it draws ?
on the image instead.
Why this is happening ? am I doing something wrong ?
Answers:
Unfortunately putText
doesn’t correctly handle n
symbols. See the relevant rejected pull request. You need to split your text yourself and make several putText
calls, something like:
text = "This is n some text"
y0, dy = 50, 4
for i, line in enumerate(text.split('n')):
y = y0 + i*dy
cv2.putText(img, line, (50, y ), cv2.FONT_HERSHEY_SIMPLEX, 1, 2)
Here’s another example:
https://gist.github.com/EricCousineau-TRI/596f04c83da9b82d0389d3ea1d782592
Basically, same as elyase’s answer, but using getTextSize()
as well (though I had to increase line size a bit larger than expected).
The approach posed by elyase worked well for me in OpenCV 4.5.1.38. I did verify that the text strings can still be joined with "+" and numeric values can be inserted using "str(value)". The start of the newline is just prepended with the "n" and everything behaved as expected.
y0, dy, text = 185,50, "FPS: "+str(framerate)+"nMIN: "+str(frMIN)+"nMAX: "+str(frMAX)+"nAVG: "+str(frAVG)
for i, line in enumerate(text.split('n')):
y = y0 + i*dy
cv2.putText(currStack, line, (50, y ), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA, False)
I’ve wrote a utility function for this porpuse:
from typing import Optional, Tuple
import cv2
import numpy as np
def add_text_to_image(
image_rgb: np.ndarray,
label: str,
top_left_xy: Tuple = (0, 0),
font_scale: float = 1,
font_thickness: float = 1,
font_face=cv2.FONT_HERSHEY_SIMPLEX,
font_color_rgb: Tuple = (0, 0, 255),
bg_color_rgb: Optional[Tuple] = None,
outline_color_rgb: Optional[Tuple] = None,
line_spacing: float = 1,
):
"""
Adds text (including multi line text) to images.
You can also control background color, outline color, and line spacing.
outline color and line spacing adopted from: https://gist.github.com/EricCousineau-TRI/596f04c83da9b82d0389d3ea1d782592
"""
OUTLINE_FONT_THICKNESS = 3 * font_thickness
im_h, im_w = image_rgb.shape[:2]
for line in label.splitlines():
x, y = top_left_xy
# ====== get text size
if outline_color_rgb is None:
get_text_size_font_thickness = font_thickness
else:
get_text_size_font_thickness = OUTLINE_FONT_THICKNESS
(line_width, line_height_no_baseline), baseline = cv2.getTextSize(
line,
font_face,
font_scale,
get_text_size_font_thickness,
)
line_height = line_height_no_baseline + baseline
if bg_color_rgb is not None and line:
# === get actual mask sizes with regard to image crop
if im_h - (y + line_height) <= 0:
sz_h = max(im_h - y, 0)
else:
sz_h = line_height
if im_w - (x + line_width) <= 0:
sz_w = max(im_w - x, 0)
else:
sz_w = line_width
# ==== add mask to image
if sz_h > 0 and sz_w > 0:
bg_mask = np.zeros((sz_h, sz_w, 3), np.uint8)
bg_mask[:, :] = np.array(bg_color_rgb)
image_rgb[
y : y + sz_h,
x : x + sz_w,
] = bg_mask
# === add outline text to image
if outline_color_rgb is not None:
image_rgb = cv2.putText(
image_rgb,
line,
(x, y + line_height_no_baseline), # putText start bottom-left
font_face,
font_scale,
outline_color_rgb,
OUTLINE_FONT_THICKNESS,
cv2.LINE_AA,
)
# === add text to image
image_rgb = cv2.putText(
image_rgb,
line,
(x, y + line_height_no_baseline), # putText start bottom-left
font_face,
font_scale,
font_color_rgb,
font_thickness,
cv2.LINE_AA,
)
top_left_xy = (x, y + int(line_height * line_spacing))
return image_rgb
Here are example results:
image = 200 * np.ones((550, 410, 3), dtype=np.uint8)
image = add_text_to_image(
image,
"New linenDouble new linennLine too longggggggggggggggggggggg",
top_left_xy=(0, 10),
)
image = add_text_to_image(
image,
"Different font scale",
font_scale=0.5,
font_color_rgb=(0, 255, 0),
top_left_xy=(0, 150),
)
image = add_text_to_image(
image,
"New line with bgnDouble new linennLine too longggggggggggggggggggggg",
bg_color_rgb=(255, 255, 255),
font_color_rgb=(255, 0, 0),
top_left_xy=(0, 200),
)
image = add_text_to_image(
image,
"Different line specing,noutline and font face",
font_color_rgb=(0, 255, 255),
outline_color_rgb=(0, 0, 0),
top_left_xy=(0, 350),
line_spacing=1.5,
font_face=cv2.FONT_HERSHEY_SCRIPT_SIMPLEX,
)
import matplotlib.pyplot as plt
plt.imshow(image)
plt.show()
I am using cv2.putText() to draw a text string on an image.
When I write:
cv2.putText(img, "This is n some text", (50,50), cv2.FONT_HERSHEY_SIMPLEX, 1, 2)
The text drawn on the image is:
This is ? some text
I was expecting the text to be printed in the new line as n
is an escape character for newline but it draws ?
on the image instead.
Why this is happening ? am I doing something wrong ?
Unfortunately putText
doesn’t correctly handle n
symbols. See the relevant rejected pull request. You need to split your text yourself and make several putText
calls, something like:
text = "This is n some text"
y0, dy = 50, 4
for i, line in enumerate(text.split('n')):
y = y0 + i*dy
cv2.putText(img, line, (50, y ), cv2.FONT_HERSHEY_SIMPLEX, 1, 2)
Here’s another example:
https://gist.github.com/EricCousineau-TRI/596f04c83da9b82d0389d3ea1d782592
Basically, same as elyase’s answer, but using getTextSize()
as well (though I had to increase line size a bit larger than expected).
The approach posed by elyase worked well for me in OpenCV 4.5.1.38. I did verify that the text strings can still be joined with "+" and numeric values can be inserted using "str(value)". The start of the newline is just prepended with the "n" and everything behaved as expected.
y0, dy, text = 185,50, "FPS: "+str(framerate)+"nMIN: "+str(frMIN)+"nMAX: "+str(frMAX)+"nAVG: "+str(frAVG)
for i, line in enumerate(text.split('n')):
y = y0 + i*dy
cv2.putText(currStack, line, (50, y ), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA, False)
I’ve wrote a utility function for this porpuse:
from typing import Optional, Tuple
import cv2
import numpy as np
def add_text_to_image(
image_rgb: np.ndarray,
label: str,
top_left_xy: Tuple = (0, 0),
font_scale: float = 1,
font_thickness: float = 1,
font_face=cv2.FONT_HERSHEY_SIMPLEX,
font_color_rgb: Tuple = (0, 0, 255),
bg_color_rgb: Optional[Tuple] = None,
outline_color_rgb: Optional[Tuple] = None,
line_spacing: float = 1,
):
"""
Adds text (including multi line text) to images.
You can also control background color, outline color, and line spacing.
outline color and line spacing adopted from: https://gist.github.com/EricCousineau-TRI/596f04c83da9b82d0389d3ea1d782592
"""
OUTLINE_FONT_THICKNESS = 3 * font_thickness
im_h, im_w = image_rgb.shape[:2]
for line in label.splitlines():
x, y = top_left_xy
# ====== get text size
if outline_color_rgb is None:
get_text_size_font_thickness = font_thickness
else:
get_text_size_font_thickness = OUTLINE_FONT_THICKNESS
(line_width, line_height_no_baseline), baseline = cv2.getTextSize(
line,
font_face,
font_scale,
get_text_size_font_thickness,
)
line_height = line_height_no_baseline + baseline
if bg_color_rgb is not None and line:
# === get actual mask sizes with regard to image crop
if im_h - (y + line_height) <= 0:
sz_h = max(im_h - y, 0)
else:
sz_h = line_height
if im_w - (x + line_width) <= 0:
sz_w = max(im_w - x, 0)
else:
sz_w = line_width
# ==== add mask to image
if sz_h > 0 and sz_w > 0:
bg_mask = np.zeros((sz_h, sz_w, 3), np.uint8)
bg_mask[:, :] = np.array(bg_color_rgb)
image_rgb[
y : y + sz_h,
x : x + sz_w,
] = bg_mask
# === add outline text to image
if outline_color_rgb is not None:
image_rgb = cv2.putText(
image_rgb,
line,
(x, y + line_height_no_baseline), # putText start bottom-left
font_face,
font_scale,
outline_color_rgb,
OUTLINE_FONT_THICKNESS,
cv2.LINE_AA,
)
# === add text to image
image_rgb = cv2.putText(
image_rgb,
line,
(x, y + line_height_no_baseline), # putText start bottom-left
font_face,
font_scale,
font_color_rgb,
font_thickness,
cv2.LINE_AA,
)
top_left_xy = (x, y + int(line_height * line_spacing))
return image_rgb
Here are example results:
image = 200 * np.ones((550, 410, 3), dtype=np.uint8)
image = add_text_to_image(
image,
"New linenDouble new linennLine too longggggggggggggggggggggg",
top_left_xy=(0, 10),
)
image = add_text_to_image(
image,
"Different font scale",
font_scale=0.5,
font_color_rgb=(0, 255, 0),
top_left_xy=(0, 150),
)
image = add_text_to_image(
image,
"New line with bgnDouble new linennLine too longggggggggggggggggggggg",
bg_color_rgb=(255, 255, 255),
font_color_rgb=(255, 0, 0),
top_left_xy=(0, 200),
)
image = add_text_to_image(
image,
"Different line specing,noutline and font face",
font_color_rgb=(0, 255, 255),
outline_color_rgb=(0, 0, 0),
top_left_xy=(0, 350),
line_spacing=1.5,
font_face=cv2.FONT_HERSHEY_SCRIPT_SIMPLEX,
)
import matplotlib.pyplot as plt
plt.imshow(image)
plt.show()