PythonとOpenCVを使ってWebカメラ映像にリアルタイムで日本語を表示させる方法

2020年10月5日

今日は、Webカメラ映像にリアルタイムで日本語を表示させる方法を紹介します。

ソースコード

以下のソースコードを見てください。

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

# パラメータ
img_a = []

# 画面に表示する文字を指定
moji_1 = f"{'みつり'}:{'恋柱'}"
moji_2 = f"{'しのぶ'}:{'蟲柱'}"
moji_3 = f"{'鬼滅の刃'}"
moji_4 = f"{'合計'}:{2:d}{'人'}"

# 文字表示部分を関数化
def moji(a):
    global img_a
    
    # フォントファイルとサイズを指定
    font_1 = ImageFont.truetype("./fonts/meiryo.ttc", 32)
    font_2 = ImageFont.truetype("./fonts/meiryo.ttc", 50)

    # 文字の色を指定
    white = (255, 255, 255)  # 白色
    blue = (255, 0, 0)       # 青色
    red = (0, 0, 255)        # 赤色

    # ndarrayをImageに変換
    img_pil_en = Image.fromarray(a)

    # 先ほど変換したImageに文字を書き込む
    ImageDraw.Draw(img_pil_en).text((600, 650), moji_1, font = font_1 , fill = white)
    ImageDraw.Draw(img_pil_en).text((200, 650), moji_2, font = font_1 , fill = white)
    ImageDraw.Draw(img_pil_en).text((350, 30), moji_3, font = font_2 , fill = red)
    ImageDraw.Draw(img_pil_en).text((350, 750), moji_4, font = font_2 , fill = blue)

    # Imageをndarrayに変換
    img_a = np.array(img_pil_en)

    return img_a

# 画像保存部分を関数化
def img_write(image):
    d_today = datetime.date.today()
    dt_now = datetime.datetime.now()
    
    # ファイル名を日付にして上書き防止
    cv2.imwrite("./cnn_act/cap_temp/" +
                    str(d_today) + str("_") +
                    str(dt_now.hour) + str("_") +
                    str(dt_now.minute) + str("_") +
                    str(dt_now.second) + '.jpg', image)

# 画像の取り込み設定
cap = cv2.VideoCapture(1)
cap.set(3, 1920)
cap.set(4, 1080)
cap.set(5, 30)

if cap.isOpened() is False:
    raise("IO Error")

while True:
    ok, img = cap.read()

    if ok == False:
        continue

    # 画像の切り抜き(取り込み画像を1:1にする。座標は y,x)
    img_a = img[90:990, 510:1410]

    # img_a に日本語を表示させる(戻り値は img_a)
    moji(img_a)

    # 日本語を書き込んだ img_a を画面に表示
    cv2.imshow('test_img',img_a)

    # 画面表示位置の指定(Windowの名前, x, y)
    cv2.moveWindow('test_img', 500, 50)
    
    if cv2.waitKey(20) == ord('q'):
        break

    if cv2.waitKey(20) == ord('e'):

        # img_a の画像を img_write関数でファイルに書き込む
        img_write(img_a)

cap.release()
cv2.destroyAllWindows()

内容を順に説明します。

画面に表示する文字を指定

ソースコードの moji_1 ~ moji_4 の部分は、画面に表示する文字を指定している部分です。

f " { } " で表現されている部分は、Python 3.6 以降で追加された「リテラル文字列補完」と呼ばれる機能を使ったものです。

今回のような単純な文字表示のみの場合、あまり効果を発揮しませんが、この書き方にしておくと Python の埋め込み式を使用できるメリットがあります。

例えば、{ a + b } のようにカッコの中に数式を書くと、計算結果を表示することができます。

また、下記のようにカッコの中に変数を記載しておくと出力時に値が代入されて表示されます。

name = bob
print(f"{name} は元気です。")
> bob は元気です。

もっと詳しく知りたい方には下記の本をおすすめします。

具体的な記載例や使い方が乗っているのでとても参考になります。

値段はKindle版の方が安いですが、私は紙の本をおすすめします。

理由は端末や電池がなくてもサッと開いて見れるからです。これって凄く重要です。

文字表示部分を関数化

関数と言えば def です。

def moji(a) : の部分を説明します。

初めに、画面に表示する日本語文字のフォントを指定します。

ソースコードの以下の部分をみてください。

font_1 = ImageFont.truetype("./fonts/meiryo.ttc", 32)
font_2 = ImageFont.truetype("./fonts/meiryo.ttc", 50)

今回は文字サイズを変更するため、font_1 と font_2 の2つを用意しました。

日本語文字のフォントを指定するには、ImageFont.truetype ( ) を使います。

( ) カッコ内の第一引数には meiryo.ttc ファイルが置いてあるパスを記載します。

また、第二引数には日本語文字のサイズを指定します。

この辺は過去記事にも詳しく書いていますので、参考にしてください。

次に文字の色を指定します。色の順番は、BGRです。

ちなみに、(255, 255, 255)とすると白色、(0, 0, 0)は黒色、(128, 128, 128)は灰色の文字になります。

次に、Webカメラから取り込んだ画像データは ndarray 型のデータであるため、Image.fromarray ( ) で 一旦 Image データに変換します。

ソースコードの以下の部分をみてください。

img_pil_en = Image.fromarray(a)

次に、変換した Image データに文字を書き込みます。

ソースコードの以下の部分をみてください。

ImageDraw.Draw(img_pil_en).text((600, 650), moji_1, font = font_1 , fill = white)
ImageDraw.Draw(img_pil_en).text((200, 650), moji_2, font = font_1 , fill = white)
ImageDraw.Draw(img_pil_en).text((350, 30), moji_3, font = font_2 , fill = red)
ImageDraw.Draw(img_pil_en).text((350, 750), moji_4, font = font_2 , fill = blue)

ImageDraw.Draw( img_pil_en ).text( ) のカッコ内の第一引数は、画面上に文字を表示させる位置です。

座標は(x、y)です。

第二引数は、画面上に表示させる文字です。先に定義した moji_1 ~ moji_4 が入ります。

第三引数は、文字のフォントです。先に定義した font_1 、font_2 が入ります。

第四引数は、文字の色です。先に定義した white、red、blue が入ります。

次に、文字を書き込んだ Image データを再度 ndarray 型データに変換します。

ソースコードの以下の部分をみてください。

img_a = np.array(img_pil_en)

つまり、Webカメラ映像に日本語を表示させるためには、以下の流れでデータ処理すれば良いということです。

カメラ映像(ndarray型データ)→ Imageデータ → 文字書き込み → ndarray型データ → cv2.imshow( ) で画面表示。

画像保存部分を関数化

次に、def img_write(image): の部分を説明します。

以下のソースコードをみてください。

cv2.imwrite("./cnn_act/cap_temp/" +
                    str(d_today) + str("_") +
                    str(dt_now.hour) + str("_") +
                    str(dt_now.minute) + str("_") +
                    str(dt_now.second) + '.jpg', image)

画像を保存するためには、cv2.imwrite( ) を使います。

( ) カッコ内の ” ./ cnn_act/cap_temp/ ” は画像の保存先パスです。

また、str(d_today) + ~ + str( dt_now.second ) までがファイル名で ' .jpg ' が保存画像の拡張子になります。

次に、第二引数(ここでは image)は画像データを示しています。

つまり、def img_write( ) のカッコ内の変数は、cv2.imwrite( ) の第二引数の場所に入るということです。

なお、この image はただの記号です。

仮に def img_write( b ) として、cv2.imwrite( ) の第二引数の部分を b と書けば同じことができます。

image も b も代入するデータの入れ物に過ぎないので、どんな名前でも良いのですが、代入するデータを連想し易い名前(変数名)にしておく方がよいでしょう。

理由は、後からソースコードを見たときに、何だっけ?・・・となるからです。

ちなみに、ファイル名を日付にする理由は2つです。

一つはファイルの上書き防止、もう一つは時系列で保存し後から画像を探し易くするためです。

映像の取り込み

Webカメラでの映像の取り込み設定を行います。

ソースコードの以下の部分をみてください。

cap = cv2.VideoCapture(1)
cap.set(3, 1920)
cap.set(4, 1080)
cap.set(5, 30)

cv2.VideoCapture( ) でWebカメラから映像を取り込むことができます。

( ) カッコ内の数字はWebカメラの認識番号(ID番号)です。

通常は 0か1としておけばOKです。

もし、パソコンに何台もWebカメラを付けている場合、それ以外の数字の可能性もあるので試してみてください。

なお、今回はELP製の200万画素のカメラを使いました。

理由は、値段が安く(Amazonで9000円くらいで売られています)、比較的画質が良いからです。

また、画素数を欲張っていないので、設定画像サイズを大きくしてもフレームレートが高く設定できます。

今回は、取り込み画像サイズを 1920×1080ピクセル、フレームレートは 30fps としました。

画像サイズを640×480ピクセルにすれば、フレームレートは100fpsも可能です。

表示結果

以下の動画をみてください。

今流行りのアニメ「鬼滅の刃」から、しのぶとみつりの人形を映しています。

この映像にリアルタイムで日本語を表示させています。

Webカメラを動かしても画面に表示された日本語の位置は変わりません。

また、映像の途中からスーパーモグリン(バイキンまんが作ったモグラ型のメカ)が映りますが、日本語はスーパーモグリンの手前に映っています。

画面に表示される文字は、常に物体の手前に映ります。

まとめ

  1. 今回の方法を使えば、Pythonを使ってWebカメラから取り込んだ映像にリアルタイムで日本語を表示させることができる。
  2. ポイントはデータ変換(ndarray型データ → Imageデータ → ndarray型データ)である。