【推し活×AI】好きなキャラ風に褒めてくれるミニアプリの作り方|Python×ChatGPT×Streamlit

【推し活×AI】好きなキャラ風に褒めてくれるミニアプリを自作する方法

画像を1枚アップすると、“好きなキャラ風の口調”で褒め言葉を自動生成し、吹き出し付き画像として返してくれるミニアプリを作ります。 PythonとChatGPT API、そしてStreamlitを使えば、初心者でも短時間で完成。推し活×AIで、毎日がちょっと楽しくなります。

1. 推し活とAIの相性が抜群な理由

1-1. 推し活の魅力を最大化

自分だけの“推しからのひと言”が手元に届く体験は、モチベーションを高める強いトリガーになります。

1-2. AIで生まれる新しいファン体験

ChatGPTの表現力を使えば、そのキャラらしい雰囲気のセリフを安全な範囲で生成できます(固有フレーズは避ける)。

2. 作るアプリのイメージと機能

2-1. 完成イメージ

推しの写真やグッズの画像をアップ → “◯◯主人公っぽく”などの指示 → 褒め言葉を吹き出しに合成してPNGでダウンロード。

2-2. 必要な機能

  • 画像アップロード(PNG/JPG)
  • 「◯◯風」テキスト入力+宛先名入力
  • ChatGPTで褒め言葉を生成(短文)
  • 吹き出し+テキストの画像合成
  • PNG保存(ダウンロード)

3. 準備するもの

  • Python 3.10 以上(推奨)
  • OpenAI APIキー(.envで安全に保存)
  • ライブラリ:streamlit, Pillow, python-dotenv, openai
# セットアップ(ターミナル)
pip install streamlit pillow python-dotenv openai

# .env(プロジェクト直下)
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx

4. アプリ制作手順(コード付き)

4-1. プロジェクトの構成

oshi-app/
├─ app.py
└─ .env

4-2. 基本UI(Streamlit)を作る

# app.py(1/3) - UIの基本
import os, io, textwrap
from dotenv import load_dotenv
from PIL import Image, ImageDraw, ImageFont
import streamlit as st
from openai import OpenAI

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

st.set_page_config(page_title="推しが褒めてくれるアプリ", page_icon="✨")
st.title("✨ 推しが“褒め言葉”を言ってくれる(吹き出し合成)")

uploaded = st.file_uploader("画像を1枚アップロード(PNG/JPG)", type=["png","jpg","jpeg"])
persona = st.text_input("キャラ/アイドル名(◯◯風・△△っぽく)", value="元気な主人公風")
to_name = st.text_input("宛先(あなた/友達の名前)", value="あなた")
lang = st.selectbox("言語", ["日本語", "English"], index=0)
generate_btn = st.button("褒め言葉を生成して合成する")

4-3. プロンプト設計(“◯◯風”の安全な寄せ方)

“雰囲気にやさしく寄せる”ことを明記し、固有フレーズや名台詞の使用は禁止します。

# app.py(2/3) - プロンプトと生成
def build_prompt(persona: str, to_name: str, lang: str = "日本語") -> str:
    return f"""
あなたは「{persona}」の雰囲気にやさしく寄せるコピーライターです。
短い褒め言葉を{lang}で1つだけ出力してください。

条件:
- 宛先: {to_name}
- 30〜60文字程度
- 前向き・具体的・行動を後押し
- 著名人/作品の固有フレーズ・名台詞は使わない
- 差別/中傷/医療・金融の断定助言は不可
出力はテキストのみ。余計な説明や記号は不要。
""".strip()

def gen_phrase(persona: str, to_name: str, lang: str) -> str:
    prompt = build_prompt(persona, to_name, lang)
    resp = client.chat.completions.create(
        model="gpt-5",     # プランに合わせてモデル名を調整
        messages=[{"role":"user","content": prompt}],
        temperature=0.6,
        max_tokens=80
    )
    return resp.choices[0].message.content.strip()

4-4. 吹き出し合成(Pillow)

右上に半透明の吹き出しを描いて、折り返しテキストを載せます。日本語フォントが無い環境ではデフォルトにフォールバック。

# app.py(3/3) - 画像合成とメイン処理
def draw_speech_bubble(base_img: Image.Image, text: str) -> Image.Image:
    img = base_img.convert("RGBA")
    W, H = img.size

    bubble_w, bubble_h = int(W * 0.70), int(H * 0.35)
    margin = int(min(W,H) * 0.03)
    x0, y0 = W - bubble_w - margin, margin
    x1, y1 = x0 + bubble_w, y0 + bubble_h

    overlay = Image.new("RGBA", img.size, (0,0,0,0))
    d = ImageDraw.Draw(overlay)
    radius = int(min(bubble_w, bubble_h) * 0.07)
    d.rounded_rectangle([x0,y0,x1,y1], radius=radius, fill=(255,255,255,230), outline=(0,0,0,100), width=2)
    tail = [(x1 - int(radius*1.2), y1 - int(radius*0.5)),
            (x1 - int(radius*0.3), y1 + int(radius*0.6)),
            (x1 - int(radius*2.2), y1 - int(radius*0.2))]
    d.polygon(tail, fill=(255,255,255,230), outline=(0,0,0,100))

    try:
        font = ImageFont.truetype("NotoSansJP-Regular.otf", size=int(bubble_h*0.13))
    except:
        font = ImageFont.load_default()

    wrapped = textwrap.fill(text, width=16)
    tw, th = d.multiline_textsize(wrapped, font=font, spacing=6)
    tx, ty = x0 + (bubble_w - tw)//2, y0 + (bubble_h - th)//2
    d.multiline_text((tx,ty), wrapped, font=font, fill=(20,20,20,255), spacing=6, align="left")

    return Image.alpha_composite(img, overlay)

# メイン
if generate_btn:
    if not uploaded:
        st.error("画像を1枚アップしてください。")
    else:
        try:
            base = Image.open(uploaded).convert("RGB")
            phrase = gen_phrase(persona, to_name, lang)
            result = draw_speech_bubble(base, phrase)
            st.image(result, caption="完成イメージ", use_column_width=True)

            buf = io.BytesIO()
            result.save(buf, format="PNG")
            st.download_button("画像をダウンロード(PNG)", data=buf.getvalue(),
                               file_name="oshi_phrase.png", mime="image/png")
            st.success("生成が完了しました!")
        except Exception as e:
            st.error(f"処理に失敗しました: {e}")

5. 実際に動かしてみる

5-1. 起動方法(ローカル)

# ターミナルでプロジェクト直下へ
streamlit run app.py
# ブラウザが開く → 画像アップ → 「◯◯風」を入力 → 生成 → PNG保存

5-2. うまくいかない時

  • APIキー未設定:.env の値や環境変数を再確認
  • 文字化け:UTF-8、フォント(Noto系)導入で改善
  • レート制限:時間をおいて再試行、出力量(max_tokens)を抑える

6. 公開・共有の方法

6-1. Streamlit Community Cloudで無料公開

  1. GitHubに app.pyrequirements.txt をpush
  2. StreamlitにGitHub連携してDeploy
  3. Secretsに OPENAI_API_KEY を登録

6-2. SNS共有のコツ

  • 完成画像を正方形(1080×1080)に近づけると映える
  • ハッシュタグ例:#推し活 #AI #ChatGPT #Python #自作アプリ

8. まとめ

  • 画像1枚&数行のコードで、“推しからの励まし”体験を自作できる
  • StreamlitでUI化すれば、友だちにも簡単に体験してもらえる
  • 次は「毎朝の自動生成」「SNS自動投稿」「多言語対応」などへ拡張

推し活×AIの第一歩として、今日から動かしてみましょう。ちょっとしたひと言が、毎日を少し前向きにしてくれます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です