简体   繁体   中英

OpenCV putText() new line character

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,
    "Testing \nDouble new line\n\nLine too longggggggggggggggggggggg",
)
image = add_text_to_image(
    image,
    "Testing with font scale",
    font_scale=0.5,
    font_color_rgb=(0, 255, 0),
    top_left_xy=(0, 150),
)
image = add_text_to_image(
    image,
    "Testing with bg \nDouble new line\n\nLine 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,
    "Testing with line specing\nand outline",
    font_color_rgb=(0, 255, 255),
    outline_color_rgb=(0, 0, 0),
    top_left_xy=(0, 350),
    line_spacing=1.5,
)
import matplotlib.pyplot as plt
plt.imshow(image)
plt.show()

代码结果

If you are going to add multiple lines data into the frames of the video then you have to add multiple lines of putText() even the above answer with the for loop is going to work only for images and not when you're creating a video through code.

What you can do is make use of "exec" function in python which will take string as an input and render them into the code.

So I suggest create a small python snippet as a string by appending all the lines using for loop and execute it at the place where you want and it will also show at different places. This case worked for me in adding data to the video.

stringexec = ""
m=0
for i in TextList:
    stringexec = steval+"cv2.putText(frame, TextList[" + str(TextList.index(i))+"], (100, 100+"+str(m)+"), cv2.FONT_HERSHEY_COMPLEX, 0.5, (200, 100, 100), 1, cv2.LINE_AA)\n"
    m += 100
exec(stringexec)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM