Supershipの名畑です。キンプリが再始動とのことで、プリズムの煌めきで満ちた夏になりそうです。
はじめに
画像生成モデルStable Diffusionの展開元であるStability AIは、様々な開発者向けAPIも同様に展開しています。
そのうちの一つとして、まだBetaではあるのですがSearch and Replaceというものが今月リリースされました。
The Search and Replace service is a specific version of inpainting that does not require a mask. Instead, users can leverage a search_prompt to identify an object in simple language to be replaced. The service will automatically segment the object and replace it with the object requested in the prompt.
要は画像に対して「変換元」と「変換後」を言葉(プロンプト)で指定すると、その通りに反映してくれるというものになります。
単純に同じ結果だけを求めるならInpaintingを使うという手もあります。あるいはPhotoshopで、生成塗りつぶしを使うまでもなく、フィルタを活用してより精度高く行えます。ただ、Web APIでしかも文字のみで完結するという気軽さが個人的にはかなり印象深い体験だったため、ここでコードを含め紹介させていただきます。
ちなみに女性の髪色変化をやろうと思ったのは、公式が「Stability AI Developer Platform の画像サービス」というページで挙げていたサンプルがこれだったからです。
注意
- 実際の解像度は元画像の1024x1024、生成画像は2048x2048なのですが、記事容量を抑えるために本記事では512x512に変換した上でアップロードしています
- 本APIは有料です。現在は1回の呼び出しにつき4クレジットとなります。10ドルで1000クレジットなので、0.04ドルです。1ドルが150円とすると6円です。
API Key
Developer PlatformでAPI Keyを発行しておいてください。
ハードコーディングを避けるため、環境変数にでも保存しておきましょう。今回はSTABILITY_API_KEYという名称で保存されていると仮定して進めます。
export STABILITY_API_KEY =ここに取得したAPI keyを書く
コード
今回用いたPythonのコードは以下です。見ての通りですが、promptに「どのように変換するか」を、search_promptに「何を変換するか」を書きます。元画像はoriginal.pngというファイル名で保存してあるとします。今回はしていませんがシード値の指定も可能です。
import os
import requests
import time
api_host = "https://api.stability.ai"
api_key = os.getenv("STABILITY_API_KEY") # 環境変数にAPIキーを保存済み
# API Keyの取得確認
if api_key is None:
raise Exception("Missing Stability API key.")
response = requests.post(
f"{api_host}/v2beta/stable-image/edit/search-and-replace",
headers={
"Authorization": f"Bearer {api_key}",
"accept": "image/*"
},
files={
"image": open("./original.png", "rb") # 元画像
},
data={
"prompt": "変換後プロンプト",
"search_prompt": "変換元プロンプト",
"output_format": "png",
}
)
# 画像の保存
if response.status_code == 200:
with open(f"./{int(time.time())}.png", "wb") as file:
file.write(response.content)
else:
raise Exception(str(response.json()))
黒髪の女性を茶髪にする
元画像は以下です。Dall-E 3で生成したものです。
プロンプトは以下にしました。black hair(黒髪)をbrown hair(茶髪)に変えてみます。実際はあえてblackと書かなくても他に髪がないので同じような結果になるとは思いますが今回は明示的に書きました。
"prompt": "brown hair",
"search_prompt": "black hair",
生成された画像は以下です。
確かに、茶髪になっている。
並べてみます。
変換前 | 変換後 |
---|---|
よく見ると髪との距離が近い目や眉も変わってしまっていますが、かなり綺麗な変換です。鈍感な私なら、言われなければ髪色以外の変化に気づかない気もする。いや、流石に気づくか。
なんにせよ、ここまで綺麗にやってくれるものなのだなと驚きです。
黒髪の女性を金髪にする
さらに以下のプロンプトで金髪にもしてみました。
"prompt": "blonde hair",
"search_prompt": "black hair",
変換前 | 変換後 |
---|---|
これも右の目や眉の変化により顔自体の印象も異なりますが、ちゃんと元画像を生かした金髪です。
ウェーブをかけてみる
"prompt": "black wavy hair",
"search_prompt": "black hair",
「black wavy hair」つまり「ウェーブのかかった黒い髪」を指定してみました。
変換前 | 変換後 |
---|---|
指示に忠実に従ってはいます。
ただ、やっぱり髪に近い部分の変化があり、髪型以上の印象変化を受けます。
目が顔の印象に与える影響って大きいものですね。
目を閉じてみる
表情を変えてみましょう。
以下のプロンプトでeyes(目)をclosed eyes(閉じた目)にしてみます。
"prompt": "closed eyes",
"search_prompt": "eyes",
変換前 | 変換後 |
---|---|
ここまでで最も自然な気がします。変更箇所が狭いからでしょうか。
口を開けてみる
さらに試してみます。
mouth(口)をopen mouth(開いた口)にしてみます。
"prompt": "open mouth",
"search_prompt": "mouth",
変換前 | 変換後 |
---|---|
閉じた唇と開いた唇を並べると色味など違いもあるとは思いますが、まあ、自然ではあるかな。
不自然だろうか。
見すぎててよくわからなくなってきました。
服を着物にしてみる
顔以外も変えてみましょう。
clothes(服)をkimono(着物)にしてみました。
"prompt": "kimono",
"search_prompt": "clothes",
変換前 | 変換後 |
---|---|
磯丸水産の制服みたいになってしまいましたが、きちんと服だけが変わっています。インナーも変えてほしかったですが、別物扱いですかね。
変わりすぎてしまうこともある
プロンプトが雑なせいか、あるいは画像の質によるものか、意図通りにいかないケースもやはりありました。
ということで、同じく若い女性ではあるものの、別画像で試した結果も紹介します。
元画像は以下です。過去記事で生成したものの流用です。
この画像に対して同様に「茶髪」「金髪」「ウェーブのかかった黒い髪」への変換を行った結果が以下です。
髪のかかった範囲が先ほどよりも広いためか、顔の変化が大きいです。耳にピアスがつくという変化も生じました。
それでもかなり元画像に近くはありますが。
別の過去記事から持ってきた以下の画像でも試してみました。
black hair(黒髪)をbrown hair(茶髪)に変えてみます。
目もかなり変わってしまっています。
別人に見える。
ちなみに同じ画像でeyes(目)をclosed eyes(閉じた目)にしてみた結果は以下です。
対象範囲が狭いからか、かなり自然でした。
ただしそれは単体での話で、やはり元画像と並べてよく見るとまつ毛を中心に違和感があります。
変換前 | 変換後 |
---|---|
この違いも、鈍感な私なら気づかないですね。
最後に
使う上でのハードルがどんどんと下がっていることを実感します。
あと、人間の顔って少しの変化が大きな印象の差につながるのだと当たり前のことを学びました。
宣伝
SupershipのQiita Organizationを合わせてご覧いただけますと嬉しいです。他のメンバーの記事も多数あります。
Supershipではプロダクト開発やサービス開発に関わる方を絶賛募集しております。
興味がある方はSupership株式会社 採用サイトよりご確認ください。