Stable Diffusionでキャラ用のプロンプトを作成するツール作った

sd-character-prompts-builderのサンプル画像

Caution

この記事はHexo(2023年4月17日以前)、またはGatsby(2024年4月13日以前)時代の記事だよ❗ 現在のブログとは見た目や機能が異なる可能性があるよ❗

Stable Diffusionでアニメキャラを作ってるときに、 しょっちゅうpromptを忘れることがある。 「あ〜前髪を指定し忘れた…」とか、「胸の大きさを指定し忘れた…」とか、そんなんばっかり。

メモ用紙に書いてチェックリスト化してたけど、書ききれなくなってきた。 ので、思い切ってWebアプリ作っちゃおうということに。

ご利用は↓から。

sd-character-prompts-builder-sample

「何これ❓」

ボタンをポチポチするだけで、promptを作成できるアプリ。

キャラクタの再現に必要なpromptを予め用意してあるので、 上から順番にポチポチしていけば、一定以上のクオリティのpromptが完成する。

「何が嬉しいの❓」

以下のメリットがある。

1. 入れるべきpromptを忘れずに指定できる

これは冒頭で述べた通り。 作ってわかったが、キャラの顔まわりだけでも考慮すべきpromptは300個近くあるんだよね。 それを全部覚えて、毎回忘れずに指定するのは無理。

swept bangs とか忘れるでしょ絶対。

必ずリストか何かが必要になる。 このアプリはリストの代わりになる。

2. promptの順番が保証される

これも結構大事で、キャラクタごとにpromptの順番がぐちゃぐちゃだと非常に管理し辛い。

「おっぱい大きくするか😇」と思っても胸の大きさのpromptを 長〜いテキストから探すの結構つらいんだよね。

このツールを使う限り、順番は常に同じになるので、↑の心配はなくなる。

あ、ちなみに胸の大きさはすぐ変えたくなるので、 一番最後に配置してあるw large breasts で物足りないから huge breasts に変更したりとか、1回はやったことあるだろ❓な❓

3. 親子関係のあるpromptをまとめて指定できる

「アンダーリムの眼鏡っ娘作るか…🥰」 と思ったときに under-rim eyewear だけ指定したりする。 が、これはもったいない。 某所のタグ付け的には、 under-rim eyewearsemi-rimless eyewear に属している(親子関係がある) ので、 semi-rimless eyewear もつけたほうが(おそらく)良い。

本アプリはこうした親子関係をUI上でグループ化して表示して、 親promptを有効化しないと子promptが表示されないようにしてある。 これで、子promptだけを指定しちゃったりすることはなくなる。

ただ、親子promptを指定したときと、子promptだけ指定したときとで、 どちらが生成結果が良くなるかは未知数。 私も生成結果を定性的に比較したわけではない…。 環境によっては、子promptだけのほうが良くなるかもね。 まぁ、その場合は手動で出力promptを削ってくれ〜。

「***のpromptがねぇんだけど💢」

追加するから連絡くれ。

あと、V2.1.0の現在、対象は顔周りと胸の大きさに関係するprompt (いわゆるコイカツで言うところの じゃなくて キャラ に該当する要素)に限られる。 服やポーズのpromptの実装は将来に期待してね❤️

「promptが英語で分かんねえ💢」

がんばって読んでくれ。どうせ覚える。

まぁ、見慣れないpromptがあるのは事実なので、 そういうのはサンプル画像を表示できるようにしたいとは思ってる。 これも将来だね。

「web UIの拡張じゃねえのかよ💢」

そうだよ()。あっちはpythonらしいので、技術的にハードルが高いんだよね。すまんな。

技術の話

ここから完全に別の話で、Webアプリの技術的な話。

Astro

今回はAstroを採用。 最近、流行ってる感じがしたし、 今度ブログを乗り換えるならAstroが良いなと思っているため。

第一印象としては、まず早い。 ビルドシステムがViteだから、開発サーバーが一瞬で立ち上がるのは素晴らしい。 このブログはGatsbyで作ってて、確かWebPackなんだけど、 開発サーバーは10sくらいかかったりするんだよね。フィーリングがぜんぜん違う。

また、特殊なシステムが少なくて、 VanillaなHTML、CSS、JSに寄ってるところが良い。 個人が趣味で作ってるブログやアプリに、 Reactはtoo muchってやつだよなって感じた。

ただ、当然、Reactでできて、Astroでできないこともある。 特に、関数をコンポーネント越しに渡すのは原則無理。

例えば、ボタン要素なんかをコンポーネント化する場合、 まず、コールバック関数を共通ライブラリあたりに切り出しておく。 で、ボタンコンポーネントにはキーになる文字列だけを渡す(関数は渡せないので)。 最後に、ボタンコンポーネント内で、キーを参照してコールバック関数を引っ張り出して…みたいなことになる。 超面倒くさい。

本アプリでもそういう場面多くて、めんどくさすぎた。 ので、親コンポーネント側で、ボタンコンポーネントを直接 querySelector でひっぱってきちゃうことにした。 でも、これ、コンポーネントが暗黙的に結合していて、良くない設計だと思うんだよなぁ…。

CSSでも↑と同じような問題が発生する。 親コンポーネントで迂闊に div button {color: red} とか指定しようものなら、 子や孫コンポーネントの button の文字が真っ赤になったりする。 影響ダダ漏れって感じ😭

ほかにもいろいろあるんだけど、続きはもっとまとめてQiitaに書こうかな。 この記事はここまで。

TypeScript

本格的にTypeScriptの型をいじくりまわすことになった。良い勉強になった。

思想が大事だね。 『データ定義して、そこから型を抽出しよう』は間違いで、 『型を定義して、そこに合うようにデータを定義しよう』が正しい。

プロンプトのデータ定義のときに、前者の考え方をしてたら、型情報がぐっちゃぐちゃになって書き直す羽目になった。 最初は「型の定義とデータの定義を別々にやってたら二重管理なるやん❗」とか思ってたけど違った。 型を定義したら、データの定義が制限されるので、片方が間違ってたらトランスパイル時にエラーになるんだよね。 そこで修正するんだから、2つの定義が不一致を起こすことはないんだ。

TSいじってる人には「何を当たり前のことを…」と思われるかもw

ともかく、「なるべく型は厳しくしろ。その型にデータを流し込んでエラーが出るなら、エラーを信じろ。」の精神が大事だと思いました。まる。