Jayhello
Jayhello

Reputation: 6602

How to draw Chinese text on the image using `cv2.putText`correctly? (Python+OpenCV)

I use python OpenCV (Windows 10, Python 2.7) to write text in image, when the text is English it works, but when I use Chinese text it write messy code in the image.

Below is my code:

# coding=utf-8
import cv2
import numpy as np

text = "Hello world"   # just work
# text = "内容理解团队"  # messy text in the image

cv2.putText(img, text,
            cord,
            font,
            fontScale,
            fontColor,
            lineType)

# Display the image
cv2.imshow("img", img)

cv2.waitKey(0)
cv2.destroyAllWindows()

When text = "Hello world" # just work, below is the output image:

enter image description here

When text = "内容理解团队" # Chinese text, draw messy text in the image, below is the output image:

enter image description here

What's wrong? Does opencv putText don't support other language text?

Upvotes: 24

Views: 34078

Answers (4)

Jonah TZ
Jonah TZ

Reputation: 21

A redundant response from me "OPENCV (I use 4.7.0) still don't support Chinese characters (non-ascii encoding), but PIL does.

Code below separate the workflow for utf-8 and ascii encoding. FYI, PIL also support ascii encoding, therefore, you are not locked at OPENCV.

PS: Thanks Kinght 金, most of code attached below is referenced from your example.

import numpy as np
from PIL import ImageFont, ImageDraw, Image
import cv2
import charade

def _add_watermark_ascii(img, content: str, color: tuple, export_path: str = ""):
    h, w, channels = img.shape
    pos = (20, h - 25)
    # position to place the content (width_offset, height_offset)
    # I decided to place it at bottom-left
    cv2.putText(
        img,
        content,
        pos,
        fontFace=cv2.FONT_HERSHEY_COMPLEX,
        fontScale=1.0,
        color=color,
        thickness=1,
        lineType=cv2.LINE_AA,
    )
    # Export
    if export_path != "":
        cv2.imwrite(export_path, img)
    return img

def _add_watermark_unicode(img, content: str, color: tuple, export_path: str = ""):
    h, w, channels = img.shape
    # position to place the content (width_offset, height_offset)
    # I decided to place it at bottom-left
    pos = (20, h - 50) # expect slight difference on different font-family
    # Download the font you would like to try out
    # kaiu: https://fontsdata.com/135874/kaiu.htm (my-personal-choice)
    # unzip and add it to your project folder
    fontpath = "./kaiu/kaiu.ttf"
    font = ImageFont.truetype(fontpath, 32) # second arg is the font-size
    img_pil = Image.fromarray(img)
    draw = ImageDraw.Draw(img_pil)
    draw.text(
        pos,
        content,
        font=font,
        fill=(*color, 0)
    )
    img = np.array(img_pil)
    # Export
    if export_path != "":
        cv2.imwrite(export_path, img)
    return img

def get_encoding(x):
    return charade.detect(x.encode())["encoding"]

def add_watermark(img, text: str, color: tuple, output_path: str):
    assert len(color) == 3, "3-channels required"

    if get_encoding(text) == "ascii":
        return _add_watermark_ascii(img, text, color, output_path)
    # utf-8
    return _add_watermark_unicode(img, text, color, output_path)

Upvotes: 0

Carson
Carson

Reputation: 7958

Quick Start

np_img = np.ones((64, 32, 3), dtype=np.uint8) * 255  # background with white color
draw_text = init_parameters(cv2_img_add_text, text_size=32, text_rgb_color=(0, 0, 255), font='kaiu.ttf', replace=True)
draw_text(np_img, '您', (0, 0))
draw_text(np_img, '好', (0, 32))
cv2.imshow('demo', np_img), cv2.waitKey(0)

enter image description here

what is init_parameters() and cv2_img_add_text()?

see as below:

EXAMPLE

from typing import Tuple
import numpy as np
import cv2
from PIL import Image, ImageDraw, ImageFont


# define decorator
def init_parameters(fun, **init_dict):
    """
    help you to set the parameters in one's habits
    """
    def job(*args, **option):
        option.update(init_dict)
        return fun(*args, **option)
    return job


def cv2_img_add_text(img, text, left_corner: Tuple[int, int],
                     text_rgb_color=(255, 0, 0), text_size=24, font='mingliu.ttc', **option):
    """
    USAGE:
        cv2_img_add_text(img, '中文', (0, 0), text_rgb_color=(0, 255, 0), text_size=12, font='mingliu.ttc')
    """
    pil_img = img
    if isinstance(pil_img, np.ndarray):
        pil_img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(pil_img)
    font_text = ImageFont.truetype(font=font, size=text_size, encoding=option.get('encoding', 'utf-8'))
    draw.text(left_corner, text, text_rgb_color, font=font_text)
    cv2_img = cv2.cvtColor(np.asarray(pil_img), cv2.COLOR_RGB2BGR)
    if option.get('replace'):
        img[:] = cv2_img[:]
        return None
    return cv2_img


def main():
    np_img = np.ones(IMG_SHAPE, dtype=np.uint8) * 255  # background with white color

    np_img = cv2_img_add_text(np_img, 'Hello\nWorld', (0, 0), text_rgb_color=(255, 0, 0), text_size=TEXT_SIZE)
    np_img = cv2_img_add_text(np_img, '中文', (0, LINE_HEIGHT * 2), text_rgb_color=(0, 255, 0), text_size=TEXT_SIZE)

    cur_y = LINE_HEIGHT * 3
    draw_text = init_parameters(cv2_img_add_text, text_size=TEXT_SIZE, text_rgb_color=(0, 128, 255), font='kaiu.ttf', replace=True)
    for msg in ('笑傲江湖', '滄海一聲笑'):
        draw_text(np_img, msg, (0, cur_y))
        cur_y += LINE_HEIGHT + 1
    draw_text(np_img,
              """123
456
789
""", (0, cur_y))
    cv2.imshow('demo', np_img), cv2.waitKey(0)


if __name__ == '__main__':
    IMG_HEIGHT, IMG_WIDTH, CHANNEL = IMG_SHAPE = (250, 160, 3)
    TEXT_SIZE = LINE_HEIGHT = 32
    main()

enter image description here

Upvotes: 2

Kinght 金
Kinght 金

Reputation: 18331

The cv2.putText don't support no-ascii char in my knowledge. Try to use PIL to draw NO-ASCII(such Chinese) on the image.

import numpy as np
from PIL import ImageFont, ImageDraw, Image
import cv2
import time

## Make canvas and set the color
img = np.zeros((200,400,3),np.uint8)
b,g,r,a = 0,255,0,0

## Use cv2.FONT_HERSHEY_XXX to write English.
text = time.strftime("%Y/%m/%d %H:%M:%S %Z", time.localtime()) 
cv2.putText(img,  text, (50,50), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (b,g,r), 1, cv2.LINE_AA)


## Use simsum.ttc to write Chinese.
fontpath = "./simsun.ttc" # <== 这里是宋体路径 
font = ImageFont.truetype(fontpath, 32)
img_pil = Image.fromarray(img)
draw = ImageDraw.Draw(img_pil)
draw.text((50, 80),  "端午节就要到了。。。", font = font, fill = (b, g, r, a))
img = np.array(img_pil)

cv2.putText(img,  "--- by Silencer", (200,150), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (b,g,r), 1, cv2.LINE_AA)


## Display 
cv2.imshow("res", img);cv2.waitKey();cv2.destroyAllWindows()
#cv2.imwrite("res.png", img)

enter image description here


Refer to my another answer:

Load TrueType Font to OpenCV

Upvotes: 34

TMK
TMK

Reputation: 760

According to this opencv forum, putText is only able to support a small ascii subset of characters and does not support unicode characters which are other symboles like chinese and arabic characters.

However, you can try to use PIL instead and follow the answer posted here and see if it works out for you.

Upvotes: 5

Related Questions