メインコンテンツへスキップ
見出し画像

「画像生成どうやってるの?」に答えてみる|おはようカノジョ#32

    先日、noteでこんなコメントをもらった。

    個人的には「おはようカノジョ」より 画像生成:Gemini(gemini-2.5-flash-image)をどうやってるのか? の方が興味はありますね。

    正直、技術的な話はnoteで書かないつもりだった。でも「どうやってるのか気になる」と言ってもらえたので、少しだけ裏側を書いてみる。

    全体像

    「おはようカノジョ」がどういう流れで動いているか、以前図にしたことがある。

    画像
    全体像

    今回はこの中の「Gemini APIで画像を生成している部分」を話す。

    参照画像の詳しい話は過去記事で書いたので、興味があればこちらも。

    なぜAPIで画像生成しているか

    「おはようカノジョ」は毎朝7:30に自動投稿するのがコンセプト。自動で動かすには、GitHub Actionsなどのクラウド環境で実行する必要がある。

    Stable Diffusionを動かすにはGPUが必要だけど、クラウドでGPU環境を用意するのはコストも手間もかかる。毎日1枚生成するだけなのに、そこまでやるのは割に合わない。

    API経由なら、GPU環境を気にせずコードを書くだけで動く。毎日続けるには、精度より「疲れないこと」が大事だと思っている。

    実際のコード

    実装自体はシンプルで、google-genaiライブラリを使ってAPIを叩いているだけ。

    シンプルな例

    from google import genai
    from google.genai import types
    ​
    client = genai.Client(api_key="YOUR_API_KEY")
    ​
    response = client.models.generate_content(
        model="gemini-2.5-flash-image",
        contents=["アニメ風の女の子、朝の挨拶、笑顔"],
        config=types.GenerateContentConfig(
            response_modalities=["IMAGE", "TEXT"],
        ),
    )
    ​
    # 画像データを取得
    image_data = response.candidates[0].content.parts[0].inline_data.data

    ポイントは response_modalities=["IMAGE", "TEXT"] の指定。これがないと画像が返ってこない。

    参照画像を使う例

    実際の運用では、キャラの一貫性を保つために参照画像を渡している。

    from google import genai
    from google.genai import types
    ​
    client = genai.Client(api_key="YOUR_API_KEY")
    ​
    # 参照画像を読み込み
    with open("reference.jpg", "rb") as f:
        ref_image = types.Part.from_bytes(data=f.read(), mime_type="image/jpeg")
    ​
    response = client.models.generate_content(
        model="gemini-2.5-flash-image",
        contents=[
            ref_image,  # 参照画像を先に渡す
            "この画像と同じキャラで、手を振っているポーズを生成して",
        ],
        config=types.GenerateContentConfig(
            response_modalities=["IMAGE", "TEXT"],
        ),
    )

    参照画像を先に渡して、その後にプロンプトを書く。これでキャラの見た目をある程度維持できる。

    本質はこれだけ。実際はもう少し複雑だけど、骨格はこのコードと変わらない。

    プロンプトの構造

    コードはシンプルだけど、プロンプトは工夫している。キャラごとの設定をYAMLで管理し、選択されたシーンの定義を使って毎回組み立てている。

    character:
      name: るな
      personality: 活発で明るい。週末前のワクワク感を一緒に楽しんでくれる彼女。
      colors:
        personal: emerald green
      appearance:
        hair: high ponytail, warm golden blonde hair, fluffy ponytail
        eyes: bright green eyes, sparkling eyes, energetic eyes
        face: bright smile, cheerful expression
    ​
    outfit:
      base: oversized white button-up shirt, boyfriend shirt
      detail: emerald green plain knit cropped cardigan over shirt
    ​
    scene:
      location: indoor, inside apartment
      lighting: soft lighting, natural light
      pose: standing, one hand raised in small wave
      framing: upper body, eye level
    ​
    negative_prompt: black hair, dark hair, outdoor background, ...

    appearance でキャラの見た目、outfit で衣装、scene で構図を指定。negative_prompt には「これはやめて」を書いている。

    このYAMLをPythonで読み込んで、プロンプト文字列に組み立てている。

    ハマったポイント

    コードはシンプルでも、運用すると色々起きる。

    髪の色が安定しない

    「るな」というキャラは金髪なんだけど、生成すると黒髪になることがあった。

    プロンプトに「golden hair」と書いても黒くなる。参照画像を渡しても黒くなる。

    解決策はnegative promptだった。「black hair」を明示的に禁止することで、ようやく金髪が安定した。

    「こうしてほしい」より「これはやめて」の方が効くことがある。

    キャラの雰囲気がブレる

    クリスマスイベントで「しずく」にサンタ衣装を着せたら、「雪の女王」みたいになったことがある。

    原因は衣装の小物だった。「氷の結晶ティアラ」を指定したら、全体が冷たい印象になってしまった。サンタ帽に変更したら、ようやく「しずく」らしい柔らかさが戻った。

    プロンプトの一部が全体の雰囲気を引っ張ることがある。小物ひとつで印象が変わるのは面白くもあり、難しくもある。

    おわりに

    完璧じゃないけど、毎日動いている。

    「安定しない」「ブレる」と書いたけど、それでも毎朝7:30に投稿できているのは、割り切って運用しているからだと思う。

    技術の話、需要があればまた書くかもしれない。


    👉️「おはようカノジョ」は毎朝7:30に投稿中。 https://x.com/ohayo_kanojo

    この記事は以下の共同マガジンに参加しています。

    この記事が参加している募集

    あなたへのおすすめ