OpenCV/Pillow – 画像にテキストを描画する方法

目次

概要

OpenCV の cv2.putText() を使用して画像にテキストを描画する方法について解説します。 また、Pillow を使用して日本語のテキストを描画する方法についても記載しています。

cv2.putText

公式リファレンス: cv2.putText

img = cv2.putText(img, text, org, fontFace, fontScale,
                 color[, thickness[, lineType[, bottomLeftOrigin]]])
引数
名前 デフォルト値
img ndarray
入力画像
text str
文字列
org tuple of 2 ints
描画位置 (baseline の始点)
fontFace int
フォントの種類
fontScale float
フォントの倍率
color int / tuple of ints
thickness int 1
文字の太さ
line_type LineTypes cv2.LINE_8
線の描画方法
bottomLeftOrigin bool False
True の場合、左下を原点として扱う
返り値
名前 説明
img 出力画像

注意点として、日本語などの Ascii 文字以外は描画できないため、そのような文字を描画したい場合は Pillow を使います。

lineType=cv2.LINE_AA を指定すると、アンチエイリアスが有効になり、文字のジャギーが軽減します。

  • 色は color で指定します。1 チャンネル画像の場合は int、3 チャンネル画像の場合は (int, int, int) で指定します。(例: color=(255, 0, 0))
  • 線の太さは thickness で指定します。負の値を指定した場合は塗りつぶしになります。
  • 描画は引数に渡した配列を直接変更します。
  • 点の座標や大きさは float ではなく、int で指定します。
In [1]:
import cv2
from IPython.display import Image, display


def imshow(img):
    """ndarray 配列をインラインで Notebook 上に表示する。"""
    ret, encoded = cv2.imencode(".jpg", img)
    display(Image(encoded))
In [2]:
import cv2
import numpy as np

# 描画用の画像を生成する。
img = np.zeros((100, 300, 3), dtype=np.uint8)

cv2.putText(
    img,
    "Hello World",  # テキスト
    (0, 30),  # テキストの位置 (ベースラインの左端)
    fontFace=cv2.FONT_HERSHEY_SIMPLEX,  # フォントの種類
    fontScale=1.0,  # フォントのスケール
    color=(255, 255, 255),  # フォントの色
    thickness=2,  # フォントの太さ
    lineType=cv2.LINE_AA,  # 描画方法
)
imshow(img)
2024-07-29T01:00:56.171623 image/svg+xml Matplotlib v3.7.1, https://matplotlib.org/

テキストの大きさを取得する

retval, baseLine = cv2.getTextSize(text, fontFace, fontScale, thickness)

公式リファレンス: cv2.getTextSize

引数
名前 デフォルト値
text str
文字列
fontFace int
フォントの種類
fontScale float
フォントの倍率
thickness int 1
文字の太さ
返り値
名前 説明
(w, h) 文字の大きさ
baseLine ベースラインまでの距離

引数の解釈は以下のようになっています。

位置関係

文字を囲む矩形とベースラインを描画する例を紹介します。

In [3]:
img = np.zeros((100, 300, 3), dtype=np.uint8)

text = "Hello World"  # 描画する文字
fontface = cv2.FONT_HERSHEY_SIMPLEX  # フォントの種類
fontscale = 1.0  # 文字のスケール
thickness = 2  # 文字の太さ
x, y = 50, 50  # ベースラインの始点

# 文字列を描画した際の大きさを取得する。

(w, h), baseline = cv2.getTextSize(text, fontface, fontscale, thickness)
print(f"size: ({w}, {h}), baseline: {baseline}")

# 文字を囲む矩形を描画する。
cv2.rectangle(img, (x, y - h), (x + w, y + baseline), (0, 0, 255), thickness)

# ベースラインを描画する。
cv2.line(img, (x, y), (x + w, y), (0, 255, 255), thickness)

# 文字列を描画する。
cv2.putText(img, text, (x, y), fontface, fontscale, (255, 255, 255), thickness)
imshow(img)
size: (176, 22), baseline: 10

また、cv2.getTextSize() で指定した丁度収まる fontScale の値を取得できます。

retval, baseLine = cv2.getTextSize(text, fontFace, fontScale, thickness)

公式リファレンス: cv2.getFontScaleFromHeight

引数
名前 デフォルト値
fontFace int
フォントの種類
pixelHeight int
高さ
thickness int 1
文字の太さ
返り値
名前 説明
fontFace フォントの倍率
In [4]:
import cv2
import numpy as np

img = np.zeros((100, 300, 3), dtype=np.uint8)

text_h = 20
text = "Hello World"  # 描画する文字
fontface = cv2.FONT_HERSHEY_SIMPLEX  # フォントの種類
fontscale = cv2.getFontScaleFromHeight(fontface, text_h)  # 文字のスケール
thickness = 2  # 文字の太さ
x, y = 50, 50  # ベースラインの始点

# 文字列を描画した際の大きさを取得する。

(text_w, text_h), baseline = cv2.getTextSize(text, fontface, fontscale, thickness)
print(f"size: ({text_w}, {text_h}), baseline: {baseline}")

# 文字を囲む矩形を描画する。
cv2.rectangle(img, (x, y - text_h), (x + text_w, y + baseline), (0, 0, 255), thickness)

# ベースラインを描画する。
cv2.line(img, (x, y), (x + text_w, y), (0, 255, 255), thickness)

# 文字列を描画する。
cv2.putText(img, text, (x, y), fontface, fontscale, (255, 255, 255), thickness)
imshow(img)
size: (159, 20), baseline: 9

日本語のテキストを描画する

cv2.putText() は簡単にテキストの描画が行えますが、以下の欠点があります。

  • 日本語が描画できない
  • フォントの種類が用意されたもの以外選択できない
In [5]:
import cv2
import numpy as np

# 描画用の画像を生成する。
img = np.zeros((100, 300, 3), dtype=np.uint8)

cv2.putText(
    img,
    "日本語",  # テキスト
    (0, 30),  # テキストの左下の位置
    fontFace=cv2.FONT_HERSHEY_SIMPLEX,  # フォントの種類
    fontScale=1.0,  # フォントのスケール
    color=(255, 255, 255),  # フォントの色
    thickness=2,  # フォントの太さ
    lineType=cv2.LINE_AA,  # 描画方法
)
imshow(img)

そのため、日本語を描画したい場合、Pillow のテキストを描画する関数を使用することをおすすめします。

In [6]:
import cv2
import numpy as np
from PIL import Image as PILImage, ImageDraw, ImageFont


def put_text(
    img,
    text,
    org,
    fontname,
    fontsize=10,
    color=(0, 0, 0),
):
    # PIL の Image に変換する。
    img = PILImage.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

    font = ImageFont.truetype(fontname, size=fontsize)
    draw = ImageDraw.Draw(img, mode="RGBA")
    draw.text(org, text, fill=color, font=font)

    return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)


def get_text_size(img, text, org, fontname, fontsize=10):
    # PIL の Image に変換する。
    img = PILImage.fromarray(cv2.cvtColor(img, cv2.COLOR_RGB2BGR))

    font = ImageFont.truetype(fontname, size=fontsize)
    draw = ImageDraw.Draw(img, mode="RGBA")
    text_box = draw.textbbox(org, text, font=font)

    return tuple(text_box[2:])


# 描画用の画像を生成する。
img = np.zeros((100, 300, 3), dtype=np.uint8)

# Ubuntu の場合 (IPA フォント)
# インストールされていない場合は、sudo apt install -y fonts-ipafont でインストールする。
font_name = "ipagp.ttf"
# Windows の場合
# font_name = "meiryo.ttc"
text = "日本語"
position = (0, 0)
fontsize = 30

# テキストのサイズを取得する。
text_w, text_h = get_text_size(img, text, position, font_name, fontsize)
cv2.rectangle(img, position, (text_w, text_h), (255, 255, 255), -1)
print(f"text size: ({text_w}, {text_h})")


# テキストを描画する。
img = put_text(
    img,
    text,
    position,
    font_name,
    fontsize,
    color=(255, 0, 0),  # RGB で指定
)

imshow(img)
text size: (90, 29)

コメント

コメントする

目次