Pythonのpillow(PIL)を使って矩形(長方形)の上にクラス名などのテキストを描画する

Pythonの画像ライブラリであるpillow(PIL)パッケージを使って、画像にテキストを描画する方法を紹介します。特に、矩形(長方形)の上にテキストを表示する方法を紹介しています。

下記の記事では画像に矩形を描画し、表示する方法を紹介しました。今回は、SSDやYOLOのような物体検出AIの検出結果を表示する際に、矩形の上に識別クラス名や出力信頼度(confidenseやscoreと呼ばれる)も表示する方法を紹介します。

この記事でできること

画像を読み込み、矩形を描画し、さらに矩形の上にクラス名(今回の例では、男: man, 女: woman)や、信頼度(confidenseやscoreと呼ばれる物体検出AIが出力する矩形の確からしさを表す確率のような値)を表示します。

いらすとやの「人脈・コネがない人のイラスト」
いらすとやの「人脈・コネがない人のイラスト」
顔を矩形で囲い、クラス名や信頼度を表示した画像
顔を矩形で囲い、クラス名や信頼度を表示した画像

この例では、いらすとやの「人脈・コネがない人のイラスト」の人の顔を赤い矩形(長方形)で囲い、さらに矩形の上にクラス名(man, woman)や信頼度(0~1の数値)を表示しています。クラス名と信頼度のテキストは赤色で塗りつぶした矩形の上に白色で表示しています。

画像内の人の顔の座標やクラス名、信頼度は手入力です。顔を自動検出する方法はこの記事の内容に含みません。

pillowパッケージのインストール

準備として、次の以前紹介した記事と同様に、pillowをインストールします。

テキスト(文字列)を画像に描画

まずは、この節で説明するサンプルコード全体を。

from PIL import Image
img = Image.open("img_file.png") # 画像ファイル読み込み

from PIL import ImageDraw
draw = ImageDraw.Draw(img) # 矩形の描画の準備

rectcolor = (255, 0, 0) # 矩形の色(RGB)。red
linewidth = 4 # 線の太さ
draw.rectangle([(161, 50), (260, 162)], \
               outline=rectcolor, width=linewidth) # 矩形の描画

from PIL import ImageFont
textcolor = (255, 255, 255) # テキストの色(RGB)。今回は白色です。
textsize = 14 # 描画するテキストの大きさ。今回は14px。

# テキストの描画の準備。"arial.ttf"はフォント名。
font = ImageFont.truetype("arial.ttf", size=textsize)

text = " woman "+str(0.51) + " "

left, top = (161, 50) # 矩形の左上の座標(x, y)をleft, topという変数に格納

txpos = (left, top-textsize-linewidth//2) # テキストの描画を開始する座標
# x座標はleftと同じ。
# y座標はtopよりテキストの大きさと矩形の線の太さの半分だけ上にする。
# テキストの大きさ(=textsize)。矩形の線の太さの半分(=linewidth//2)。

txw, txh = draw.textsize(text, font=font)
# 文字列"text"が占める領域のサイズを取得

draw.rectangle([txpos, (left+txw, top)], \
               outline=rectcolor, fill=rectcolor, width=linewidth)
# テキストを描画する領域を"rectcolor"で塗りつぶし。
# 左上座標をtxpos、右下座標を (left+txw, top)とする矩形をrectcolor(=赤色)で塗りつぶし。

draw.text(txpos, text, font=font, fill=textcolor)
# テキストをtextcolor(=白色)で描画

img.save("img_file_out2.png") # ファイルの保存

このソースコードを順番に説明していきます。最初は画像の読み込みです。Image.openというメソッドで画像ファイル名を指定し、imgという変数に画像を読み込みます。

from PIL import Image
img = Image.open("img_file.png") # 画像ファイル読み込み

次に、矩形描画の準備をします。見慣れないメソッドは、そういうもんなんだと思って流して頂ければ問題ないです。矩形の色や、矩形の線の太さ、矩形の座標を指定します。

from PIL import ImageDraw
draw = ImageDraw.Draw(img) # 矩形の描画の準備
rectcolor = (255, 0, 0) # 矩形の色(RGB)。red
linewidth = 4 # 線の太さ
draw.rectangle([(161, 50), (260, 162)], outline=rectcolor, width=linewidth) # 矩形の描画

ここからがテキストの描画の説明です。まずはテキスト描画の準備をします。

from PIL import ImageFont
textcolor = (255, 255, 255) # テキストの色(RGB)。今回は白色です。
textsize = 14 # 描画するテキストの大きさ。今回は14px。
font = ImageFont.truetype("arial.ttf", size=textsize) # テキストの描画の準備。"arial.ttf"はフォント名。

表示するテキストを文字列として変数”text”に格納します。”woman”という文字列と”0.51″という数値を表示します。

text = " woman "+str(0.51) + " "

ここからがテキストの描画の本番です。

left, top = (161, 50) # 矩形の左上の座標(x, y)をleft, topという変数に格納

txpos = (left, top-textsize-linewidth//2) # テキストの描画を開始する座標
# x座標はleftと同じ。
# y座標はtopよりテキストの大きさと矩形の線の太さの半分だけ上にする。
# テキストの大きさ(=textsize)。矩形の線の太さの半分(=linewidth//2)。

txw, txh = draw.textsize(text, font=font)
# 文字列"text"が占める領域のサイズを取得

draw.rectangle([txpos, (left+txw, top)], outline=rectcolor, fill=rectcolor, width=linewidth)
# テキストを描画する領域を"rectcolor"で塗りつぶし。
# 左上座標をtxpos、右下座標を (left+txw, top)とする矩形をrectcolor(=赤色)で塗りつぶし。

draw.text(txpos, text, font=font, fill=textcolor)
# テキストをtextcolor(=白色)で描画

テキストと矩形の描画が終わったので、新規の画像ファイルとして保存します。

img.save("img_file_out2.png") # ファイルの保存

保存されたファイル”img_file_out2.png”は次のようになります。

矩形を一つ描画した画像
矩形を一つ描画した画像

複数の矩形にクラス名や信頼度のテキストを描画

これまでに説明したことを使って複数の矩形を描画し、それぞれにクラス名や信頼度を描画します。

from PIL import Image

def draw_rect(img, rects, scores, classes):
    from PIL import ImageDraw
    from PIL import ImageFont
    rectcolor = (255, 0, 0) # 矩形の色(RGB)。red
    linewidth = 4 # 線の太さ
    draw = ImageDraw.Draw(img) # 矩形の描画の準備

    textcolor = (255, 255, 255) # テキストの色(RGB)。今回は白色です。
    textsize = 14 # 描画するテキストの大きさ。今回は14px。
    font = ImageFont.truetype("arial.ttf", size=textsize) # テキストの描画の準備。"arial.ttf"はフォント名。

    for i, rect in enumerate(rects):
        rect_tup = [(rect[0], rect[1]), (rect[2], rect[3])]
        draw.rectangle(rect_tup, outline=rectcolor, width=linewidth) # 矩形の描画

        text = " "
        text += classes[i] + " "
        text += str(scores[i]) + " "
        left, top = rect[0], rect[1] # 矩形の左上の座標(x, y)をleft, topという変数に格納

        txpos = (left, top-textsize-linewidth//2) # テキストの描画を開始する座標
        # x座標はleftと同じ。
        # y座標はtopよりテキストの大きさと矩形の線の太さの半分だけ上にする。
        # テキストの大きさ(=textsize)。矩形の線の太さの半分(=linewidth//2)。 

        txw, txh = draw.textsize(text, font=font) # 文字列"text"が占める領域のサイズを取得

        draw.rectangle([txpos, (left+txw, top)], \
                outline=rectcolor, fill=rectcolor, width=linewidth)
        # テキストを描画する領域を"rectcolor"で塗りつぶし。
        # 左上座標をtxpos、右下座標を (left+txw, top)とする矩形をrectcolor(=赤色)で塗りつぶし。

        draw.text(txpos, text, font=font, fill=textcolor) # テキストをtextcolor(=白色)で描画

    return img

if __name__ == '__main__':
    img = Image.open("img_file.png") # 画像読み込み

    rects = [[161,50,260,161], 
             [318,135,418,243], 
             [46,267,142,373], 
             [181,244,275,352], 
             [340,351,440,455], 
             [500,257,598,371], 
             [648,33,742,146]] # 矩形の座標リスト
    classes = ["woman", "woman", "man", "man", "woman", "man", "man"] # 各矩形のクラス名
    scores = [0.51, 0.61, 0.71, 0.75, 0.81, 0.85, 0.91] # 各矩形の信頼度

    img = draw_rect(img, rects, scores, classes) # 矩形とクラス名、信頼度の描画
    img.save("img_file_out2.png") # 画像のファイル出力

変数rectsに複数の矩形座標のリストを指定し、classes、scoresに各矩形のクラス名と信頼度をリストとして指定しています。関数draw_rect()は、矩形とクラス名と信頼度を描画する関数です。 forループ内で関数draw_rect()が実行される度に、矩形とテキストが上書きの形で描画されます。

出力画像"img_file_out2.png"
出力画像”img_file_out2.png”

おわりに

今回は画像へのテキストの描画方法を記載しました。矩形やクラス名、信頼度の描画は物体検出AI(object detection)の検出結果を表示する際によく使います。今後も、コンピュータビジョンや、SSDやYOLOなどの物体検出AIの関連記事を書いていく予定です。

コメント

タイトルとURLをコピーしました