6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

お題は不問!Qiita Engineer Festa 2023で記事投稿!

Stable Diffusionでの同じ顔の複数画像生成をPythonとWeb APIで試みた記録

Posted at

Supershipの名畑です。現在放送中のスキップとローファーの美津未、原作で新刊を読む度に2週間は引きずる宝石の国のフォス、もうすぐ特別編が劇場公開される響け! ユーフォニアムの久美子。私が好きな作品にいつもその声があったなあと思います。ご結婚おめでとうございます。

はじめに

Stable Diffusionでの画像生成をPythonとWeb APIで実装してみた記録」「Stable Diffusionにおける同一人物の別表情生成をPythonとWeb APIで試みた記録」と、Stable Diffusionでの画像生成を記事にしてきました。

前回は画像内の人物の表情を変えることを試みましたが、今回は同じ人物を用いた別画像の生成を試みます。要は同じ顔でたくさん画像を作ろうとしたというものです。

過去の記事同様generation APIの基本機能の範疇で挑みます。

元となる画像を用意

Stable DiffusionのAPIでは、学習もできないため、狙った構図の生成はかなり難しいと感じています。まあ、当然であろうとは思いつつ。

ですので「最終的に作成したい画像に近い画像」を用意した上で加工していくのが現実的と考えています。どこまで近しくできるかはコスト見合いですが(容易に近似な画像を用意できるならAIを使う必要はなくなるわけですし)。流行りのControlNetも構図指定のためには、なんらかの元画像を用いるようですかね。

私は絵を描くのはかなり苦手ですので、写真素材を元にした方が狙った構図の画像を生成しやすいのではと考えました。
極端な話、自分自身を写真に撮ってそれを加工するという手もとれるのではないかと。

今回は、手間を省くため、まずStable Diffusionで写真風の女性画像を5枚生成しました。
これをすべて同一な顔にすることを試みます。

コードは下記です。ほぼ過去記事のままですが、text_promptsrealisticを追加し、style_presetphotographicとしています。

import base64
import os
import requests
import time

engine_id = "stable-diffusion-xl-beta-v2-2-2"

# engine_id = "stable-diffusion-v1"
api_host = os.getenv('API_HOST', 'https://api.stability.ai')
api_key = os.getenv("STABILITY_API_KEY")

# API Keyの取得確認
if api_key is None:
    raise Exception("Missing Stability API key.")

# API呼び出し
response = requests.post(
    f"{api_host}/v1/generation/{engine_id}/text-to-image",
    headers={
        "Content-Type": "application/json",
        "Accept": "application/json",
        "Authorization": f"Bearer {api_key}"
    },
    json={
        "text_prompts": [
            {
                "text": "28-year-old-japanese-woman with black bobbed hair, realistic",
                "weight": 1.0
            },
            {
                "text": "text, letter, belongings",
                "weight": -1.0
            },
        ],
        "style_preset": "photographic",
        "samples": 5,
        "width": 512,
        "height": 512,
    },
)


# レスポンス確認
if response.status_code != 200:
    raise Exception("Non-200 response: " + str(response.text))

# レスポンス取得
data = response.json()

# 画像保存
for i, image in enumerate(data["artifacts"]):
    with open(f"./{engine_id}_{int(time.time())}_{i}.png", "wb") as f:
        f.write(base64.b64decode(image["base64"]))

生成された画像は下記の5枚。

雑コラを作る

同じ顔の画像を生成したいのであれば、同じ顔を貼り付ければいいのではないか。
そんな安易な考えが浮かびました。

ということで、前の記事で生成した下記の画像を使います。

ちなみに顔部分だけをくり抜くのはrembgを用いました。背景を取り除いて人物や動物だけを残してくれるライブラリです。

$ pip install rembg
Collecting rembg
  Downloading rembg-2.0.43-py3-none-any.whl (25 kB)
Collecting scipy
  Downloading scipy-1.10.1-cp310-cp310-
以下略
$ rembg i origin_img.png output_img.png

こちらの顔を生成済み画像にぺたぺたと貼っていきます。ここは人力でやりました。

元画像 雑コラ画像
image_a_0.png image_a_1.png
image_b_0.png image_b_1.png
image_c_0.png image_c_1.png
image_d_0.png image_d_1.png
image_e_0.png image_e_1.png

あまりに雑すぎますが、まあ、今回は実験なのでよしとします。

雑コラを自然にしてみる

まず、あまりにひどい雑コラ具合を軽減させましょう。

生成済みのコラ画像を元としてimage-to-imageで別画像を生成します。

先述のコードのrequests.postのみ変えています。
image_strength0.7として、元画像を強めに残しています。それとstyle_presetphotographicとしています。
また、ここから先のコードではseedで同一の値を指定することにします。

response = requests.post(
    f"{api_host}/v1/generation/{engine_id}/image-to-image",
    headers={
        "Accept": "application/json",
        "Authorization": f"Bearer {api_key}"
    },
    files={
        "init_image": open("./init_img.png", "rb")
    },
    data={
        "text_prompts[0][text]": "28-year-old-japanese-woman with black bobbed hair, realistic",
        "text_prompts[0][weight]": 1.0,
        "image_strength": 0.7,
        "style_preset": "photographic",
        "seed": 99999
    },
)

結果は下記です。左が元として使った雑コラ画像で、右がAPI経由で生成された画像です。
元が雑すぎて一部画像は特に首回りが不自然に思えますが、全体的に、かなり自然になりはしたでしょうか。コラが顔の向きとかもガン無視なので不自然さが残るのはやむなしですかね。

元画像 生成画像
image_a_1.png image_a_2.png
image_b_1.png image_b_2.png
image_c_1.png image_c_2.png
image_d_1.png image_d_2.png
image_e_1.png image_e_2.png

こうやって並べると気になるところもありますが、なんの前提知識もなしにこの生成画像5枚を「これ、田中さん(仮名)っていう人の写真だよ」と見せられたなら、田中さんを被写体とした5枚の写真として普通に受け入れてしまう気はします。4枚目だけ髪が長いから撮影時期が違うのかもしれないと思いつつ。

だって、現実に撮影した写真でも、いわゆる奇跡の1枚じゃないですが、同一人物だってわからないものが珍しくはないですし。

イラスト風にしてみる

せっかくなので写真風ではなくイラスト風にしてみます。

これらの画像を元として、今度はimage_strength0.5style_presetanimeとして画像を生成しました。プロンプトからrealisticを削除しています。
変更箇所はdataのみです。

data={
    "text_prompts[0][text]": "28-year-old-japanese-woman with black bobbed hair",
    "text_prompts[0][weight]": 1.0,
    "image_strength": 0.5,
    "style_preset": "anime",
    "seed": 99999
},

結果は下記です。
まったくもって同じ顔ではないですが、ある程度は近しいのではないかとも思います。

元画像 生成画像
image_a_2.png image_a_3.png
image_b_2.png image_b_3.png
image_c_2.png image_c_3.png
image_d_2.png image_d_3.png
image_e_2.png image_e_3.png

絵柄を軽くしてみる

moe illustrationを指定してみました。moeがそもそも通じるのか不明ではありますが。

data={
    "text_prompts[0][text]": "moe illustration",
    "text_prompts[0][weight]": 1.0,
    "image_strength": 0.4,
    "style_preset": "anime",
    "seed": 99999
},

結果は下記です。
工程を経すぎてよくわからなくなってきました。同じ顔のイラストを複数生成するという目的からは遠ざかりましたが、軽い絵柄にはなりましたかね。

元画像 生成画像
image_a_3.png image_a_4.png
image_b_3.png image_b_4.png
image_c_3.png image_c_4.png
image_d_3.png image_d_4.png
image_e_3.png image_e_4.png

最後に

丁寧にやればある程度はコントロールできそうだなとは感じました。
まあ、Stable Diffusionを使うメリットみたいなものは薄い方法だなとは思いつつですが。
ただただ楽しんでいます。

宣伝

SupershipのQiita Organizationを合わせてご覧いただけますと嬉しいです。他のメンバーの記事も多数あります。

Supershipではプロダクト開発やサービス開発に関わる方を絶賛募集しております。
興味がある方はSupership株式会社 採用サイトよりご確認ください。

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?