見出し画像

Python×DALL·E3 APIでブログ用アイキャッチ(文字入り)を爆速で量産する方法

※当記事は「Ateam LifeDesign Advent Calendar 2023」の参加コンテンツとなります。弊社メンバーのほか記事もぜひ、ご覧ください。

エイチームライフデザインでコンテンツマーケティング / SEO領域のマネージャーをしている河田 剛です。

この記事では、Pythonと画像生成AIのDALL·E3を用いて、文字の入ったブログ用のアイキャッチ画像をスピーディーに量産する手段を解説します。

できあがるアイキャッチのイメージはこんな感じです。

本記事に掲載している内容についての二次利用は、利用者の方々の自己責任となります。
予めご了承いただくようお願いいたします。

①事前準備:必要なものを揃える

後ほどすべての工程を一連化したいので、今回はPythonを用い、OpenAI API経由でDALL·E3を実行していきます。

事前準備は以下のとおり。

  1. Python実行環境の用意

  2. OpenAIライブラリのインストール

  3. GPT-4 APIのAPIキーの取得

OpenAIライブラリは、以下コマンドでPythonへインストールします。

pip install openai

GPT-4 APIキーはOpenAI Platformへログイン後、以下の画面で発行しましょう。

なお、GPT-4 APIは「GPT APIの使用による一定額の支払い履歴」が存在しないと使用できない(※23年12月現在)ので、他の方が書いている以下などを参考に導入してください。

これらの用意がないと本記事は読み進めてもムダになってしまいます。あらかじめご容赦ください。

②背景画像の生成:DALL·E3をAPIで実行する

まずは、アイキャッチの背景画像をDALL·E3で生成しましょう。

import os
import requests

# OpenAI APIキーの設定
os.environ["OPENAI_API_KEY"] = "{$APIキー}"

from openai import OpenAI
client = OpenAI()

# 画像生成のリクエスト
response = client.images.generate(
  model = "dall-e-3",
  prompt = "猫を飼う時に大事な3つの心構え",
  quality = "standard",
  n = 1,
)

# 画像のURLを取得
image_url = response.data[0].url
print(image_url)

# 画像を保存
response = requests.get(image_url)
if response.status_code == 200:
    with open("C:\\{$保存先のフォルダ}\\background_image.png", 'wb') as file:
        file.write(response.content)

これを実行すると、以下の画像が出力されました。

background_image.png

なかなか良いイメージですね。地味に「Patience(忍耐)」「Understanding(理解)」といった、本当に猫の飼い主に要求されそうな心構えが文字で出力されているのも優秀だな~と感じます。

ただ、「Commitiment」はおそらく、正しくは「Commitment(献身)」である気がするので、綴りが誤っていそうです。このあたり後ほど説明するのですが、このDALL·E3、まだ文字情報の取り扱いが完璧とは言えないレベルです。

*余談ですが、23年12月現時点ではDALL·E3で画像生成する場合、出力するサイズは1024x1024、1024x1792、1792x1024の3パターンしか選べない様子です。
 今回は画像生成にかかる時間や負荷を短縮する目的で、最小サイズである1024x1024を選択しています。

また、プロンプトを工夫すれば、様々な雰囲気の画像を生成することができます。

■写実的なイラスト

■アニメ調なイラスト

自身の好みや、運営するブログの雰囲気に合わせ、生成するイラストをプロンプトで調整していくと良いでしょう。

③文字画像の生成:PILで文字を画像に変換する

次に、先ほど作成した背景画像に重ねる、文字入れ用の画像をPILライブラリで生成します。

※こちらの記事を大いに参考にさせていただきました。感謝いたします。
https://qiita.com/implicit_none/items/a9bf7eebe125c9d773eb

import PIL.Image
import PIL.ImageDraw
import PIL.ImageFont

# フォントの設定
fontstyle = "C:\\Windows\\Fonts\\meiryob.ttc"
fontsize = 50
text = "猫を飼う時に大事な3つの心構え"

# キャンバスの設定
canvasSize = (1024, 1024)
backgroundRGB = (0, 0, 0)
textRGB = (255, 255, 255)

# キャンバスの作成
img = PIL.Image.new('RGB', canvasSize, backgroundRGB)
draw = PIL.ImageDraw.Draw(img)

# キャンバスに文字列をプロット
font = PIL.ImageFont.truetype(fontstyle, fontsize)
textWidth, textHeight = draw.textsize(text, font=font)
textTopLeft = ((canvasSize[0] - textWidth) // 2, (canvasSize[1] - textHeight) // 2)
draw.text(textTopLeft, text, fill=textRGB, font=font)

# 画像を保存
img.save("C:\\{$保存先のフォルダ}\\centered_text_image.png")

生成された画像はこちらです。

centered_text_image.png

fontstyleには、自身のPCにインストールされているフォントのパスを指定します。今回はメイリオボールド(meiryob)を使用します。
Windowsを用いている方であれば、エクスプローラーで C:\Windows\Fonts\ へ遷移すれば、他に指定できるフォントが一覧で見れるはずです。

画像の幅(canvasSize[0])と高さ(canvasSize[1])からテキストの幅と高さを引き、それぞれ2で割ることで、テキストが画像の中央に来るように座標を設定します。

画像キャンバスは1024x1024で、DALL·E3で生成した背景画像と同サイズです。同サイズを指定しない場合は、次工程である合成時にサイズ合わせが必要になります。

④アイキャッチの合成:PILで2枚の画像を重ねてトリミング

最後の過程として、②と③で作成した2つの画像を合成。アイキャッチ向けに16:9のアスペクト比へトリミングもします。

from PIL import Image, ImageOps

# 2枚の画像のパスを指定
img1_path = "C:\\{$保存先のフォルダ}\\centered_text_image.png"
img2_path = "C:\\{$保存先のフォルダ}\\background_image.png"
img1 = Image.open(img1_path)
img2 = Image.open(img2_path)

# 画像の合成
blended = Image.blend(img1, img2, alpha=0.2)

# 画像の切り抜き(800×400サイズ)
crop_width, crop_height = 800, 450
left = (blended.width - crop_width) / 2
top = (blended.height - crop_height) / 2
right = (blended.width + crop_width) / 2
bottom = (blended.height + crop_height) / 2
cropped_img = blended.crop((left, top, right, bottom))

# 画像を保存
cropped_img_path = "C:\\{$保存先のフォルダ}\\blended_image.png"
cropped_img.save(cropped_img_path)

この処理を実行後の画像は以下です。

blended_image.png

かなりアイキャッチっぽさが出てきました!

文字入れ画像をどこまで透過するかは、Image.blend(img1, img2, alpha=0.2) にあるalphaという値を変更することで調整できます。

⑤アイキャッチの完成:PILで画像周囲にボーダーを付与する

④まででも十分、アイキャッチらしさはあるのですが、最後に画像周囲へボーダーを設けることで、仕上がりを改善してみます。
ボーダーはここまでと同じく、PILに処理を追加して付けてもらいます。

from PIL import Image, ImageOps

# 2枚の画像のパスを指定
img1_path = "C:\\{$保存先のフォルダ}\\centered_text_image.png"
img2_path = "C:\\{$保存先のフォルダ}\\background_image.png"
img1 = Image.open(img1_path)
img2 = Image.open(img2_path)

# 画像の合成
blended = Image.blend(img1, img2, alpha=0.2)

# 切り抜きサイズの計算
border_width = 15
crop_width, crop_height = 800 - border_width * 2, 450 - border_width * 2
left = (blended.width - crop_width) / 2
top = (blended.height - crop_height) / 2
right = (blended.width + crop_width) / 2
bottom = (blended.height + crop_height) / 2

# 画像の切り抜き
cropped_img = blended.crop((left, top, right, bottom))

# ボーダーの追加
border_color = "#5F8670"  # Hex color
bordered_img = ImageOps.expand(cropped_img, border=border_width, fill=border_color)

# 画像を保存
bordered_img_path = "C:\\{$保存先のフォルダ}\\bordered_image.png"
bordered_img.save(bordered_img_path)

ボーダーを入れても16:9のアスペクト比を維持するようになっています。できあがりはこちら。

bordered_image.png

⑥すべての処理の一本化と連続実行

ここまでの一連の処理をすべて連続実行するようにしてみます。
はじめに、以下のように「アイキャッチを作成したい記事タイトルや見出し名」をtxtファイルでリストにします。

eye_catch_list.txt

このリストの順番どおりに、一連の処理を行わせるプログラムへ書き換えます。

import os
import requests
from PIL import Image, ImageOps, ImageDraw, ImageFont

# OpenAI APIキーを設定
os.environ["OPENAI_API_KEY"] = "{$APIキー}"

from openai import OpenAI
client = OpenAI()

# フォントの設定
fontstyle = "C:\\Windows\\Fonts\\meiryob.ttc"
fontsize = 40

# キャンバスサイズと色の設定
canvasSize = (1024, 1024)
backgroundRGB = (0, 0, 0)
textRGB = (255, 255, 255)

# ボーダー設定
border_width = 15
border_color = "#5F8670"  # Hex color

# テキストファイルの読み込み
with open("C:\\{$保存先のフォルダ}\\eye_catch_list.txt", "r", encoding="utf-8") as file:
    lines = file.readlines()

# 各行に対する処理
for listline in lines:
    # ① DALL-E APIを使って画像生成
    response = client.images.generate(
      model = "dall-e-3",
      prompt = listline.strip(),
      quality = "standard",
      n = 1,
    )
    image_url = response.data[0].url
    response = requests.get(image_url)
    if response.status_code == 200:
        with open("C:\\{$保存先のフォルダ}\\background_image.png", 'wb') as file:
            file.write(response.content)

    # ② PILを使ってテキスト画像を生成
    img = Image.new('RGB', canvasSize, backgroundRGB)
    draw = ImageDraw.Draw(img)
    font = ImageFont.truetype(fontstyle, fontsize)
    textWidth, textHeight = draw.textsize(listline, font=font)
    textTopLeft = ((canvasSize[0] - textWidth) // 2, (canvasSize[1] - textHeight) // 2)
    draw.text(textTopLeft, listline, fill=textRGB, font=font)
    img.save("C:\\{$保存先のフォルダ}\\centered_text_image.png")

    # ③ 生成された2つの画像を合成
    img1 = Image.open("C:\\{$保存先のフォルダ}\\centered_text_image.png")
    img2 = Image.open("C:\\{$保存先のフォルダ}\\background_image.png")
    blended = Image.blend(img1, img2, alpha=0.2)

    # 切り抜きとボーダーの追加
    crop_width, crop_height = 800 - border_width * 2, 450 - border_width * 2
    left = (blended.width - crop_width) / 2
    top = (blended.height - crop_height) / 2
    right = (blended.width + crop_width) / 2
    bottom = (blended.height + crop_height) / 2
    cropped_img = blended.crop((left, top, right, bottom))
    bordered_img = ImageOps.expand(cropped_img, border=border_width, fill=border_color)

    # 最終画像を保存
    bordered_img.save("C:\\{$保存先のフォルダ}\\{listline.strip()}_image.png")

# 全ての行の処理が完了
print("全完了")

さて、仕上がりは以下の連作になりました。

少し画像ごとのテイストが合いませんが、なかなか良い出来ではないでしょうか。DALL·E3に渡すプロンプトを工夫できれば、画像の雰囲気を統一することも可能でしょう。

ちなみに、私の環境下では、この計4枚の画像を生成するのに合計で1分ほどしかかかりませんでした。非常に素早くアイキャッチを量産できる展望が見えますね!

余談:DALL·E3だけで文字入れまで完結できないのか

DALL·E3は非常に優秀な画像生成AIであり、一定レベルで文字認識や処理も可能です。たとえば、以下のように「こういう文字が挿入された画像を作って」というプロンプトにも対応できるのですが…

これを書いている23年12月時点では、日本語の文字が上手く処理できないというウィークポイントがあるのです。

と、いうよりも
現時点では「英語(アルファベット)のみ上手く処理や生成ができる」
…といった状況であるのかなと思います。

いずれ、ひらがなやカタカナ、漢字などが正確に処理できるように進化することを祈っていますが、現在は本記事で紹介したような、「文字の画像」と「背景画像」を合成するという手段の実用性が高そうです。

まとめ

いかがでしたでしょうか。生成AIが世間を沸かせたこの1年でしたが、私を含め、SEOやコンテンツに携わる人間にとっても大きな転換期となった2023年だったと感じます。

テキスト生成・画像生成といったAI分野の目覚ましい発展に喰らいついていく覚悟を持って、本記事で紹介したようなTipsをこれからも継続発信したいと思います。

そんな、当エイチームライフデザインのマーケティングnoteでは、コンテンツマーケ・アドマーケ・ブランディングデザインなどのノウハウや社内事例を、今後もコンスタントに紹介していきます。ぜひ、フォローやスキをよろしくお願いします(一緒に働く仲間も募集中です)。

最後に、私がこれまで社内で執筆した他の記事も宣伝します^^

本記事をご覧いただきまして、ありがとうございました。

みんなにも読んでほしいですか?

オススメした記事はフォロワーのタイムラインに表示されます!