Python

Pythonでアイキャッチ画像を自動生成してみた

アイキャッチ画像を作るのが面倒なので、Pythonで自動生成するコードを書きました。

やっていることは下記です。

  • 目標のサイズまで縦横比を変えずに縮小
  • 縦横で長すぎてはみ出る分を切り取る
  • タイトルの文字を画像に書き出す(複数行対応)
  • 文字が見にくいので、白か黒の長方形で塗りつぶす

この記事の先頭に表示されているアイキャッチもこのプログラムで作成しています。

コードの補足

コードすぐに見たいという人は、最後まで読み飛ばしてください。
ここから最後の章までは、Pythonの画像で使ったライブラリやAPIの補足をしていきます。

PIL(pillow)

画像処理系のライブラリはpillowを使ってます。
インストール方法は下記です。

$ sudo pip3 install pillow

Image.thumbnail

img.thumbnail(x,y)

画像の縦横比を変えず、x,yに収まるように縮小します。
縦横比を変えないというところがポイントです。
また、インスタンスimgを変更するので注意です。

Image.crop

new_img = img.crop((left, upper, right, lower))

指定した範囲で切り取った画像を作成します。

ImageDraw.rectangle

draw = ImageDraw.Draw(img)
draw.rectangle((left, upper, right, lower), (255,255,255))

画像からImageDrawを作って、rectangleで領域と色を指定して描画します。

ImageDraw.text

font = ImageFont.truetype('./myfont.ttf', 36)
draw = ImageDraw.Draw(img)
draw.text((x, y), '書き出したい文字', font=font, fill='#000000')

画像からImageDrawを作って、textで(left,top)と色を指定して描画します。

塗りつぶしを透過で書き出すには

#オリジナルをRGBAに変える
img = img.convert('RGBA')
#塗りつぶしよう画像を作成する
rectangle_img = Image.new('RGBA', img.size)
#塗りつぶし(アルファ値の指定あり)
draw.rectangle((left, upper, right, lower), (255,255,255, 128))
#重ねる
img = Image.alpha_composite(img, rectangle_img)
#jpegで保存するためRGBに戻す
img = img.convert('RGB')

透過で重ねるためにはRGBAに変換してからImage.alpha_compositeを呼び出します。
RGBAなので、ImageDraw.rectangleもアルファ値の指定が必要です。

jupyterで動かすと画像も見れて良い

僕は今回作ったコードをjupyterで動かしてます。
最後で、plt.imshow()で表示して出来栄えを確認しています。

こんな感じです。

jupyterで画像表示

コード

綺麗じゃないかもしれませんが、全コードを貼り付けておきます。
黒文字で白バックと、白文字で黒バックの両方を同時に作ってます。

from PIL import Image, ImageDraw, ImageFont
import matplotlib.pyplot as plt
import numpy as np

# 設定
opt_base_path = '/home/dev/host/eyecatch/'
opt_text = ['Pythonを使って', 'アイキャッチを自動作成']
opt_img_name = 'test_'
opt_img_path = 'markus-spiske-207946-unsplash.jpg'
opt_img_q = 90
opt_img_size = (760, 428)
opt_text_size = (600, 200)
opt_text_margin = 30
opt_max_font_size = 100
opt_font_path = 'mgenplus-1pp-bold.ttf'
opt_text_colors = (('black', '#000000', (255, 255, 255, 192)), ('white', '#FFFFFF', (0, 0, 0, 192)))

#先に文字の描画領域決定。合わせて下記を計算しておく
#  フォントサイズfont_size, 描画幅area_width, 描画高さarea_height, lines_size
for font_size in range(opt_max_font_size, 9, -1):

    #フォント
    font = ImageFont.truetype(opt_base_path + opt_font_path, font_size)

    # 各行の[幅、高さ]を計算
    lines_size = [list(font.getsize(text)) for text in opt_text]

    # 幅の最大と高さの合計
    area_x = np.max(lines_size, 0)[0]
    area_y = np.sum(lines_size, 0)[1]

    if(area_x <= opt_text_size[0] and area_y <= opt_text_size[1]):
        break;

# 画像読み込み、サイズ取得
img = Image.open(opt_base_path + opt_img_path)
original_img_size = img.size

# 縮小
if original_img_size[0] / original_img_size[1] >= opt_img_size[0] / opt_img_size[1]:
    #目標より横長なら、縦横比を維持して、縦を目標まで縮める
    img.thumbnail((original_img_size[0], opt_img_size[1]))
    thumbnail_size = img.size
    #横幅を切り取るための計算をする
    crop_left = int((thumbnail_size[0] - opt_img_size[0]) / 2)
    crop_upper = 0
    crop_right = crop_left + opt_img_size[0]
    crop_lower = opt_img_size[1]
else:
    #目標より縦長なら、縦横比を維持して、横を目標まで縮める
    img.thumbnail((opt_img_size[0], original_img_size[1]))
    thumbnail_size = img.size
    #縦幅を切り取るための計算をする
    crop_left = 0
    crop_upper = int((thumbnail_size[1] - opt_img_size[1]) / 2)
    crop_right = opt_img_size[0]
    crop_lower = crop_upper + opt_img_size[1]

#計算した縦横で切り取る
img = img.crop((crop_left, crop_upper, crop_right, crop_lower))

#背景の塗りつぶしと文字書き込み準備
rectangle_y = area_y + opt_text_margin
rectangle_top = int((opt_img_size[1]-rectangle_y)/2)

#白黒2色分画像を作成
for (label, fg_color, bg_color) in opt_text_colors:
    #塗りつぶし領域作成
    rectangle_img = Image.new('RGBA', img.size)
    draw = ImageDraw.Draw(rectangle_img)
    draw.rectangle((0, rectangle_top, opt_img_size[0], rectangle_top+rectangle_y), bg_color)

    #塗りつぶし
    org_img = img.convert('RGBA')
    new_img = Image.alpha_composite(org_img, rectangle_img)

    draw = ImageDraw.Draw(new_img)
    _y = int((opt_img_size[1]-area_y)/2)
    for (text, line) in zip(opt_text, lines_size):
        _x = int((opt_img_size[0]-line[0])/2)
        draw.text((_x, _y), text, font=font, fill=fg_color)
        _y += line[1]

    new_img = new_img.convert("RGB")
    new_img.save(opt_base_path + opt_img_name + label + '.jpg', quality=opt_img_q)
    plt.figure()
    plt.imshow(new_img)
ABOUT ME
crz33
アラフォーのシステムエンジニアです。 それなりの規模のSI会社でいろんなプロジェクトを渡り歩き、 立場もプログラマー、アーキテクト、プロジェクトリーダーと経験してきました。 今はプロジェクトマネージャーとして活躍?中です。 早期リタイアを目指し、アフィリエイトで稼げないかと奮闘中です。

COMMENT

メールアドレスが公開されることはありません。