Pythonで画像や動画にテロップを入れる時、テキストの配置位置を制御したい時はテキストのピクセルサイズを把握する必要があります。ここではPythonのPillowを使ってテキストのピクセルサイズ情報を取得する方法を紹介します。
こんにちは。wat(@watlablog)です。
文字入れは意外とやることが沢山あります。ここでは画像内
に入れる文字のピクセルサイズを取得する方法を紹介します!
文字のピクセルサイズを取得する必要性
画像の中にテキストを描画し、さらに画像内の位置を中央揃え等に自動で制御する場合は、文字列を画像にした時のピクセルサイズを取得する必要があります。
この時、このサイズは文章の長さ以外にも、フォントの設定の影響も受けます。
「Pythonで画像に日本語文字を入れる方法」では、Pillowという外部パッケージを使って画像に文字を入れる方法を紹介しました。
この記事内では、フォントの種類やフォントサイズといった情報を設定していました。
OpenCVでは日本語の文字を入れることが出来ないので、今回も日本語に対応したPillowを使います。
以下では、フォントの設定によって変化する文字列のピクセルサイズを取得する方法を紹介します。
Pillowで文字のピクセルサイズを取得する方法【コードで説明】
前準備
ピクセルサイズ取得方法の部分を説明する前に、検証に使うコードの前準備を行います。
以下がパッケージインポートから描画インスタンス生成部分までのコードです。matplotlibをインポートしているのは、検証用にグラフを使いたいからです。
サンプルとして、np.zerosを使って黒一色の画像を作っています。
文章は「aa」と半角英文字を設定しています。
そして次にフォントの種類や大きさの設定を行っていますが、ここは「Pythonで画像に日本語文字を入れる方法」に詳細を書きましたので、是非こちらも併せてご確認下さい。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from PIL import Image, ImageFont, ImageDraw import numpy as np from matplotlib import pyplot as plt from matplotlib.ticker import AutoMinorLocator img = np.zeros((30, 50, 3), np.uint8) # 新規黒塗画像を作成 message = 'aa' # 文字列を定義 font_path = 'C:\Windows\Fonts\meiryo.ttc' # Windowsのフォントファイルへのパス font_size = 24 # フォントサイズ font = ImageFont.truetype(font_path, font_size) # PILでフォントを定義 img = Image.fromarray(img) # cv2(NumPy)型の画像をPIL型に変換 draw = ImageDraw.Draw(img) # 描画用のDraw関数を用意 |
文字列のピクセルサイズを表示
文字列のピクセルサイズ取得は、.textsizeで行います。.textsizeは上で設定したフォントを読み込むことが出来ます。
1 2 3 4 5 |
# .textsizeで文字列のピクセルサイズを取得 w, h = draw.textsize(message, font) # サイズの表示 print(w, h) |
テキストを描画してピクセルサイズの検証
最後に、生成したdrawインスタンスを使って、テキストを画像に描画します。matplotlibのグラフに文字入りの画像を表示させることでピクセル単位で文字の大きさを見ていきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# テキストを描画(位置、文章、フォント、文字色(BGR+α)を指定) draw.text((0, 0), message, font=font, fill=(255, 255, 255, 0)) # PIL型の画像をcv2(NumPy)型に変換 img = np.array(img) # ここからグラフ設定 # フォントの種類とサイズを設定する。 plt.rcParams['font.size'] = 14 plt.rcParams['font.family'] = 'Times New Roman' fig = plt.figure() ax1 = fig.add_subplot(111) # 画像をプロット ax1.imshow(img) # 軸のラベルを設定する。 ax1.set_xlabel('x [pix]') ax1.set_ylabel('y [pix]') ax1.set_xticks(np.arange(0, 100, 5)) ax1.set_yticks(np.arange(0, 100, 5)) ax1.set_xlim(0, 50) ax1.set_ylim(30, 0) ax1.xaxis.set_minor_locator(AutoMinorLocator(5)) ax1.yaxis.set_minor_locator(AutoMinorLocator(5)) fig.tight_layout() plt.show() plt.close() |
半角英文字「aa」の場合:文字には余白がある
font_size=24, message='aa'としてプログラムを実行すると、コンソールに「28 26」と結果が表示されます。これはテキストの横幅が28pixで高さが26pixであることを意味します。
そしてグラフには以下の画像が表示されます。文字入れの位置は(0, 0)です。高さをグラフの目盛で見てみると、y=26pix目まで白色がきていることがわかりますね。
同じ文字2つの大きさが28pixであるため、1文字当り14pixになりますが、横軸の目盛を見ると、文字の右側に2pixほど余白があることがわかります。
半角英文字「AA」の場合:同じ半角でも文字サイズが違う
次に同じ半角英文字ですが、大文字である「AA」はどうでしょう?
message='AA'と変更してプログラムを実行すると、コンソールには「33 26」と表示されます。
33?偶数じゃないぞ!?
グラフを見ると2文字目が1文字目とくっついて見えます。同じ半角英文字でも小文字のaと大文字のAではちょっと違うみたいですね。それにしても、なぜ横幅が33pixなんだろう。
ちなみに、message='A'とすると横幅は17pixになります。単純に2倍にすると34になるはずですが、オーバーラップしているのでしょうか?この辺は謎です。フォントによって違う?
全角日本語「ああ」の場合:日本語の横幅はフォントサイズと同じ
日本語としてmessage='ああ'とすると、コンソールには「48 27」と表示されます。縦のピクセルが先ほどの英文字と比べ1pix増えました。余白が2pix程度あるのは半角英文字のaと同じですね。
横幅は1文字だと24なので、丁度フォントサイズと同じになっています。
全角日本語「漢字」の場合:ひらがなと同じ
続いてmessage='漢字'の場合です。サイズは「48 27」とひらがなの場合と同じということがわかりました。
半角記号「--」の場合:横幅が小さい
記号の中でもよく使うハイフン「-」の場合は2文字で22, 1文字で11なのでかなり小さいこともわかります。
まとめ
本記事では文字列を画像に描画する際のピクセルサイズを取得する方法を紹介し、様々な文字に対して適用してみました。
その結果、半角なら〇〇pix、全角なら〇〇pix…とはなっていないことを確認できました。
.textsizeを使わないで文字列のピクセルサイズを予測するのは難しいということがよくわかりましたね。
「Pythonで文字列長さ算出!全角半角判定をする方法」を執筆した段階では、単純にテキストの全角と半角を判別するだけでなんとかなると思っていたのですが、この判別を使っても正確なピクセルサイズは割り出せませんでしたので、今回の方法を調べて記事にしてみました。
ちなみに、Pillowのバージョンがとても古いと(2.x.xくらい)、.textsizeで正確なテキストサイズを取得できないことがあったそうですが、僕が使っている6.0.0では特に問題無いようです。
バグが発生していたバージョンでは.textsizeよりも.getsizeで工夫していた人が多いようでしたが、w = font.getsize(message)[0]、height = font.getsize(message)[1]と文も長く、フォントの設定をどう渡すかもよくわからなかったので、今回は.textsizeの方法を選択しました。
意外と奥が深い文字入れの世界!もうちょっと深堀していきましょう!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!
コメント