Python Pillow - 画像に重ねて文字を描く

ブログやサムネイル作成など、画像に文字を重ねたいことがあります。

GIMPやペイントなどのグラフィックスソフトを使い、手作業で文字を入れることもできますが、コピーライトなど同じ文字を何度も入れたい場合は自動化すると便利です。

今回はPillowというライブラリを使って画像にテキストを追加する処理を自動化していきます。

インストール

まずは、Pillowをインストールします。
1
pip install pillow

コーディング

Pythonで下記のようにコーディングし、実行します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from PIL import Image, ImageDraw, ImageFont

text = 'Sample' # 画像に追加する文字列を指定
img = Image.open('owl.png') # 入力ファイルを指定

imagesize = img.size # img.size[0]は幅、img.size[1]は高さを表す
draw = ImageDraw.Draw(img) # ImageDrawオブジェクトを作成

font = ImageFont.truetype("arial.ttf", 64) # フォントを指定、64はサイズでピクセル単位
size = font.getsize(text)

# 画像右下に'Sampleと表示' #FFFは文字色(白)
draw.text((imagesize[0] - size[0], imagesize[1] - size[1]), text, font=font, fill='#FFF')
# ファイルを保存
img.save('out.png', 'PNG', quality=100, optimize=True)

入力に指定した画像ファイルは下記となります。

入力画像

出力画像

正常に処理ができると次のように文字が追加された画像ファイルが出力されます。

出力画像

Python OpenCV - 動画のサムネイルを作る

動画を管理するときにはサムネイルがあると便利です。

サムネイルは、動画を再生してキャプチャしたり動画編集ソフトで切り出したりすることで作成できます。ただ数が多いと大変な作業となります。

PythonとOpenCVを使えば、動画からサムネイルを簡単に作ることができます。


インストール

まずは、OpenCVをインストールします。
1
pip install opencv_python

コーディング

Pythonでコーディングします。
1
2
3
4
5
6
7
import cv2
file = 'samp.mp4' # 動画のファイル名(パス)を指定
cap = cv2.VideoCapture(file)

cap.set(cv2.CAP_PROP_POS_MSEC, 1000) # 再生位置を第2引数にミリ秒で指定
res, img = cap.read()
cv2.imwrite('cap.png', img)

5行目ではキャプチャしたい再生位置をミリ秒で指定します。

再生位置を変えて5~7行目の処理を繰り返せば、サムネイルを繰り返し作成することができます。
(ファイル名も変更する必要がありますので気を付けてください。)


よく使うプロパティ

上記のサンプルコードでは、再生位置を指定する場合「CAP_PROP_POS_MSEC」プロパティを使いましたが、それ以外でもよく使うプロパティを表にまとめておきます。

cap.getメソッドを使ってプロパティを指定すれば、動画の各種情報が取得できます。

プロパティ名説明
CAP_PROP_POS_MSEC0再生位置を時間で表したもの(ミリ秒単位)
CAP_PROP_POS_FRAMES1再生中のフレーム番号
CAP_PROP_FRAME_WIDTH3フレームの幅
CAP_PROP_FRAME_HEIGHT4フレームの高さ
CAP_PROP_FPS5フレームレート
CAP_PROP_FRAME_COUNT7全フレーム数

画像OCR(連続文字の認識)

数字がどこに書かれているかを見つける処理を実装してみます。
まずは数字が10個表示されている下記の画像を入力ファイルとします。
10個の数字(numbers.png)

実装する処理は下記の通りです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import sys
import numpy as np
import cv2

# 画像の読み込み
im = cv2.imread('numbers.png')
# グレイスケールに変換しぼかした上で二値化する
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.adaptiveThreshold(blur, 255, 1, 1, 11, 2)

# 輪郭を抽出
contours = cv2.findContours(
thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[1]

# 抽出した領域を繰り返し処理する
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
if h < 20: # 小さすぎるのは飛ばす
continue
red = (0, 0, 255)
cv2.rectangle(im, (x, y), (x+w, y+h), red, 2)

cv2.imwrite('result1.png', im)

結果1(result1.png)
問題なく見つけた数字を赤枠で囲むことができています。

次に100個の数字が表示されている画像を入力にしてみます。
6行目の入力ファイル名を変更するだけです。
100個の数字(numbers100.png)

結果2
結果は上記のようになり数字の中まで赤枠で囲ってしまっています。
これを改善するためには、cv2.RETR_LISTというパラメータをcv2.RETR_EXTERNAL(15行目)に変更します。
このパラメータは領域の一番外側だけを検出するという意味になります。
下記に修正したソースの全体を記載しておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import sys
import numpy as np
import cv2

# 画像の読み込み
im = cv2.imread('numbers100.png')
# グレイスケールに変換しぼかした上で二値化する
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.adaptiveThreshold(blur, 255, 1, 1, 11, 2)

# 輪郭を抽出
contours = cv2.findContours(
thresh,
cv2.RETR_EXTERNAL, # ☆☆☆領域の一番外側だけを検出☆☆☆
cv2.CHAIN_APPROX_SIMPLE)[1]

# 抽出した領域を繰り返し処理する
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
if h < 20: # 小さすぎるのは飛ばす
continue
cv2.rectangle(im, (x, y), (x+w, y+h), (0,0,255), 2)
cv2.imwrite('result3.png', im)

結果3(result3.png)

今度は適切に全ての数字を認識することができました。

(Google Colaboratoryで動作確認しています。)

OpenCVで顔認識

OpenCVを使って顔認識を試してみます。
まずは、画像に写っている顔の範囲を調べます。

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
30
31
32
33
import cv2
import sys

# 入力ファイルを指定する
image_file = "./face1.jpg"

# カスケードファイルのパスを指定 ---OpenCVをインストールするとshareディレクりに格納される
cascade_file = "haarcascade_frontalface_alt.xml"

# 画像の読み込み
image = cv2.imread(image_file)
# グレースケールに変換
image_gs = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 顔認識用特徴量ファイルを読み込む
cascade = cv2.CascadeClassifier(cascade_file)
# 顔認識の実行
face_list = cascade.detectMultiScale(image_gs,
scaleFactor=1.1,
minNeighbors=1,
minSize=(150,150)) # 顔認識の最小範囲(これ以下は無視)

if len(face_list) > 0:
# 認識した部分を囲む
print(face_list)
color = (0, 0, 255)
for face in face_list:
x,y,w,h = face
cv2.rectangle(image, (x,y), (x+w, y+h), color, thickness=8)
# 描画結果をファイルに書き込む
cv2.imwrite("facedetect-output.png", image)
else:
print("no face")

入力ファイル
顔認識結果

正常に顔の範囲が認識できました。
次に顔にモザイクをかけてみます。

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
30
31
32
33
34
35
36
37
38
39
40
41
42
import cv2, sys, re

# 入力ファイル
image_file = "./face1.jpg"

# 出力ファイル名
output_file = re.sub(r'\.jpg|jpeg|png$', '-mosaic.jpg', image_file)
mosaic_rate = 30

# カスケードファイルのパスを指定
cascade_file = "haarcascade_frontalface_alt.xml"

# 画像の読み込み
image = cv2.imread(image_file)
image_gs = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # グレイスケール変換

# 顔認識を実行
cascade = cv2.CascadeClassifier(cascade_file)
face_list = cascade.detectMultiScale(image_gs,
scaleFactor=1.1,
minNeighbors=1,
minSize=(100,100))

if len(face_list) == 0:
print("no face")
quit()

# 認識した部分にモザイクをかける
print(face_list)
color = (0, 0, 255)
for (x,y,w,h) in face_list:
# 顔を切り抜く
face_img = image[y:y+h, x:x+w]
# 切り抜いた画像を指定倍率で縮小
face_img = cv2.resize(face_img, (w//mosaic_rate, h//mosaic_rate))
# 縮小した画像を元のサイズに戻す
face_img = cv2.resize(face_img, (w, h),
interpolation=cv2.INTER_AREA)
# 元の画像に貼り付ける
image[y:y+h, x:x+w] = face_img
# 描画結果をファイルに書き込む
cv2.imwrite(output_file, image)

モザイク結果

(Google Colaboratoryで動作確認しています。)


Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×