PythonでPillowを使ってOGP画像を作ろう

6 min read読了の目安(約5700字

なにかサービスを作ったときに、OGP画像を設定することで、そのサービスがSNSでシェアされたときなどにそれを見た人がそのサービスに訪れる確率が大きく上がります。

今回はPythonを使って簡単にOGP画像をいい感じに生成するものを書いていきます。

実装する内容は、

  • アイコン画像を円形に切り取って貼り付ける
  • 画像に文字を入れる
    です。

短めなのですぐに読み終わると思います!

サンプルを作成する

まずはじめにこんな感じの画像を作成したいという感じで適当にデザインします。
ちなみにTwitterではOGP画像のアスペクト比を 1.91:1 することを推奨してた気がします!


iPad Affinity Designerでデザインしました

サンプルを作成したら、次に動的に変わるところをなくした画像、つまり土台のみの画像にします。
それをPNGもしくはJPEGに書き出しましょう。

アイコン画像をベースの画像に貼り付ける

今回使うライブラリはPillowのみです。

なのでまずはPillowをインストールします。
方法は色々あると思うので適当に

pip install pillow

まずは、OGPの真ん中の上に配置したアイコン画像をベースに貼り付ける処理を書いていきます。

from PIL import Image

ogp_base_img_path = 'base image'
ogp_icon_img_path = 'icon image'


def paste_icon_image(base_img, icon_img):
    base_img.paste(icon_img.resize((150, 150), resample=Image.ANTIALIAS), (int(base_img.size[0] / 2 - 75), 150))

    return base_img


if __name__ == '__main__':
    base_img = Image.open(ogp_base_img_path).copy()
    icon_img = Image.open(ogp_icon_img_path).copy()

    img = paste_icon_image(base_img, icon_img)

    img.show()
    img.save('output.png')


出力結果

アイコンが貼り付けられているのがわかります。

ですがアイコン画像が四角いままで丸くなっていません

なのでひと工夫加えます。

from PIL import Image, ImageDraw, ImageFilter # 追加

def paste_icon_image(base_img, icon_img):
    ### 円形にアイコンを切り出す処理
    mask = Image.new("L", icon_img.size, 0)
    draw_mask = ImageDraw.Draw(mask)
    draw_mask.ellipse((0,0, icon_img.size[0], icon_img.size[1]), fill=255)
    mask = mask.filter(ImageFilter.GaussianBlur(1))
    icon_img.putalpha(mask)
    paste_img = Image.new("RGB", icon_img.size, (255,255,255))
    paste_img.paste(icon_img, mask=icon_img.convert("RGBA").split()[-1])
    ###

    base_img.paste(paste_img.resize((150, 150), resample=Image.BICUBIC), (int(base_img.size[0] / 2 - 75), 150))

    return base_img

こんな感じに円形のマスクを用意してそこにアイコン画像を貼り付けています。
またはみ出た部分を透過させる処理も加えています。

実行すると

このようにアイコン画像を円形に切り出せました。

画像に文字を入れる

画像に文字を入れる場合フォントファイルを用意する必要があるので
GoogleFont などから適当にDownloadしてください。

今回使ったフォントのリンク
今回使ったフォントのダウンロードリンク

また今回は文字を真ん中寄せで画像に入れたいと思います。

from PIL import Image, ImageDraw, ImageFilter, ImageFont

ogp_base_img_path = 'base image'
ogp_icon_img_path = 'icon image'

font_black_path = "black font path"
font_medium_path = "medium font path"

'''
省略
'''

def add_centered_text(base_img, text, font_path, font_size, font_color, height):
    font = ImageFont.truetype(font_path, font_size)
    draw = ImageDraw.Draw(base_img)
    
    # 文字がベース画像からはみ出ないように処理
    if draw.textsize(text, font=font)[0] > base_img.size[0] - 170:
        while draw.textsize(text + '…', font=font)[0] > base_img.size[0] - 170:
            text = text[:-1]
        text = text + '…'

    draw.text(((base_img.size[0] - draw.textsize(text, font=font)[0]) / 2, height), text, font_color, font=font)

    return base_img


if __name__ == '__main__':
    base_img = Image.open(ogp_base_img_path).copy()
    icon_img = Image.open(ogp_icon_img_path).copy()

    base_img = paste_icon_image(base_img, icon_img)
    base_img = add_centered_text(base_img, 'PythonでOGP画像を作成', font_black_path, 64, (64, 64, 64), 380)

    base_img.show()
    base_img.save('output.png')

こんな感じで真ん中寄せに文字を入れることができました!

ということで、最終的にはこんな感じになりました。

from PIL import Image, ImageDraw, ImageFilter, ImageFont

ogp_base_img_path = 'base image'
ogp_icon_img_path = 'icon image'

font_black_path = "black font path"
font_medium_path = "medium font path"


def paste_icon_image(base_img, icon_img):
    print(base_img.size)
    mask = Image.new("L", icon_img.size, 0)
    draw_mask = ImageDraw.Draw(mask)
    draw_mask.ellipse((0, 0, icon_img.size[0], icon_img.size[1]), fill=255)
    mask = mask.filter(ImageFilter.GaussianBlur(1))

    icon_img.putalpha(mask)

    paste_img = Image.new("RGB", icon_img.size, (255, 255, 255))
    paste_img.paste(icon_img, mask=icon_img.convert("RGBA").split()[-1])

    base_img.paste(paste_img.resize((150, 150), resample=Image.BICUBIC), (int(base_img.size[0] / 2 - 75), 150))

    return base_img


def add_centered_text(base_img, text, font_path, font_size, font_color, height):
    font = ImageFont.truetype(font_path, font_size)
    draw = ImageDraw.Draw(base_img)

    if draw.textsize(text, font=font)[0] > base_img.size[0] - 170:
        while draw.textsize(text + '…', font=font)[0] > base_img.size[0] - 170:
            text = text[:-1]
        text = text + '…'

    draw.text(((base_img.size[0] - draw.textsize(text, font=font)[0]) / 2, height), text, font_color, font=font)

    return base_img


if __name__ == '__main__':
    base_img = Image.open(ogp_base_img_path).copy()
    icon_img = Image.open(ogp_icon_img_path).copy()

    base_img = paste_icon_image(base_img, icon_img)
    base_img = add_centered_text(base_img, 'PythonでOGP画像を作成', font_black_path, 64, (64, 64, 64), 380)
    base_img = add_centered_text(base_img, 'Created by makiart', font_medium_path, 32, (120, 120, 120), 560)

    base_img.show()
    base_img.save('output.png')

以上になります。
ありがとうございました^-^