
先日、「FixableFlow」というプロダクトをリリースしました。
このFixableFlowは、タイトルにもある編集/レイヤー分け可能なイラスト生成AIの作成を目指して開発し、実用的であると判断した部分までを利用可能にしたものになります。
この記事ではFixableFlowの技術解説と、今後どのように発展させていくつもりかについて触れていければと思います。
ちなみに、編集/レイヤー分け可能なイラスト生成AIってなんぞや出会ったりとか、そもそも何でこれ作ろうと思ったの?等の制作背景については以下の記事で書いていますので、気になる方はこちらもどうぞ。
また、この仕組み自体は1年前にある程度原型ができており、本当はその時点でのリリースも考えていました。
なぜ公開までに1年かけたのか?1年前のデモ制作時と何が変わったのか?という点についても少し触れられればと思います。

FixableFlowでは、イラストの制作工程毎に専用の画像生成モデルを用意することで中間生成物の出力を可能にし、生成した中間生成物を層状に重ね合わせることで一枚の完成したイラストを生成できる枠組みを目指しています。

上記の枠組みを実現する上で一番の課題となるのが、前工程の出力結果を完全に維持したまま次の工程に持ち込む必要があるという点です。
例えば、線画生成工程で生成した線画に対し、次の工程で下塗りをした結果、入力された線画と下塗り後の線画が違うものになってしまっていると困るわけですね。
しかし、愚直に画像生成AIだけを使って工程分割をしようとすると、前工程の入力を完璧に維持したまま画像を生成するのが困難という問題に直面します。
なぜなら、画像生成AIを使った画像編集は多くの場合、編集対象として渡された画像をそのまま編集するのではなく、非常に良く似た全く別の画像を一から生成しなおす、といった工程を辿るためです。
なのでこの課題を画像処理やモデルの改造等を組み合わせてどうにか改善し、前工程の出力を完璧に維持したまま次の工程に引き継ごうというのが技術開発のメインテーマとなります。
それでは本題。
現在公開しているバージョンでは、①線画生成 ②下塗り生成 ③1影生成の3つの工程に対応しているので、この3つで使っている技術に関して解説していきます。
この工程で使っているのは基本的な画像生成と大差ありません。
使っているモデルもSDXL(animagine-XL 3.1)をベースとしており、特に追加学習なども行っていません。
(安定して線画を生成するために月諏訪さん作の線画化loraは使用しています)
この工程でAIに求められるのは白背景 + 線のみで構成されたイラストを生成できれば良いので、最近の性能であればnanobananaとかでも代用できるかなと思います。
注意すべきなのは、現状の画像生成AIは一部の例外を除きアルファチャンネルを生成できないので、白背景を抜く(透明化する)のは自分でやってあげる必要があるという点です。
白背景を透明化する方法はいくつかあるのですが、雑に生成した線画をグレスケ変換し、グレスケ値をそのままアルファチャンネルに突っ込むだけでもそれっぽい透過線画が出ます。
fixableflowの場合は上記に加え、一定以上のグレースケール値を持つピクセル(白に近いピクセル)は強制的にアルファ値を0にするという方法で透過線画を作成しています。
将来的にはアルファチャンネルも生成できる画像生成が主流になっていくと思うので、そのタイミングで切り替える予定です。
下塗り生成は二つの工程を組み合わせて実現しています。
一つは、線画を入力してバケツ塗りを行う、いわゆる着彩と呼ばれる工程。
もう一つが、着彩されたイラストから線画を取り除き下塗りく工程です。

着彩工程に関しては、これも目新しい技術は使っていません。
SDXL(animagine-XL 3.1)とControlNet(かたらぎさん作のSDXL用lineartモデル)の組み合わせだけで実現しています。
これに関しても、今だとSDXLを使わずQwen Image Editなどでも代用可能かと思います。
(高速&軽量で動くこと、Qwen Image Editはピクセルシフト問題を抱えていることから現時点ではSDXLを採用)
一方で工夫が必要となるのが、着彩イラストから線画を削除する工程です。
この工程では、「AIで線画着彩を行うと入力線画が変容するため取り除くべき線画領域を検出するのが困難」という問題と「下塗り生成では線画の下にも色を塗る必要がある」という2つの点が問題になります。

これらの問題を解決する方法として二つのアプローチを考えていたので、ここではその両方を紹介します。
①古典的な画像処理を用いる場合
まず最初に考案したのが、入力線画を2値変換し白黒(線画部分のピクセルは白、それ以外の部分は黒)画像にした後、白ピクセルで囲われた領域(閉域)を検出し、検出した閉域と着彩画像を照らし合わせ、各閉域内で最も出現頻度が多かった色(RGB値)で塗り潰すという方法です。
考え方はバケツ塗りそのものですね。
ただし上記方法の問題点は、閉域検出に使った線画に該当するピクセルには色が割り当てられない事、バケツ塗り同様に線の繋ぎが甘いと本来別の色で塗って欲しい部分も同じ色として塗られてしまうという問題があります。
そこで前者に関しては、閉域検出に使った2値画像の全ての白ピクセルに対し最近傍探索(BFS)を用いて最も近い黒ピクセルの座標を検出・マッピングした後、閉域による塗りつぶし処理を行った画像にマッピング結果を割り当て、まだ塗り潰しが行われていない線画領域を最も近いベタ塗り領域の色で置換する、といった操作を行うことで対応しました。

しかし、入力線画が太すぎると近傍探索を行った際に本来期待される領域とは異なる領域に属するピクセルが検出されてしまい、結果として色が滲んだような下塗り画像になってしまう等、精度面で課題を抱えていました。
②動画生成AIを用いた高精度な画像編集モデルを用いる場合
2024年のデモ版と現在公開しているfixableflowで最も異なるのが動画生成モデル(Framepack)の採用になります。
前述の通り、古典的画像処理だけでもある程度線画除去タスクは実現できていたのですが、今ひとつ精度に物足りなさを感じていました。
この当時、もう一つ考えていた方法として動画生成AIを画像編集タスクに転用し、着彩画像から徐々に線画が消えていく動画を生成することでその最終フレームを抜き出せば線画除去タスクができるのでは?というものがありました。
しかし2024年時点では有力な動画生成モデルがCogVideoくらいしかなく、学習の困難さ + 動画モデルをローカル環境で動かす難易度の高さからこの方法を見送っており、まずは一つ目の古典的画像処理を試す方向で進めていました。
しかし、今年の4月にFramepackが登場したことで状況が一変します。
Framepackは非常に軽量かつ生成速度が早かったため、上記で述べたような画像編集に転用するにはうってつけの動画生成モデルでした。
加えて、kohyaさんによるLoRAの学習・推論環境(musubi-tuner)も充実しており、Framepackを画像編集モデルとして使用する1フレーム推論などが実装されていた事も大きな後押しでした。
ただし、線画除去タスクを行う場合、学習なしで元々の1フレーム推論を行うだけでは(おそらく大元の動画生成であるFramepackおよびHunyuan Videoが着彩イラストから線画を取り除くような動画を学習していないため)線画除去は実現できませんでした。
そこで、1フレーム推論時に特定のタスクに特化した編集が行えるようなLoRAの学習機構を追加し、さらにkisekaeichiを参考に初期フレームだけではなく複数の画像を編集条件として入力できる1f-mcを開発しました。
(現在ではmusubi-tunerに統合されています)
さて、ここまでやってあとは線画除去モデルを作るだけだな〜と思っていたところ、作ろうと思っていたのとほぼ同じモデルをとりにくさんが作成してくださったため、結局線画除去モデルは自分では作らず、とりにくさんのモデルを利用しています。
結局どちらの手法が良いのか
ここまで線画除去の方法として二つの手法を紹介してきましたが、これには両者メリデメがあり、現状では用途によって使い分けることが求められます。
(今リリースしているfixableflowはFramepack方式を採用)
まず、古典的画像処理を用いる場合のデメリットは先に述べたとおり、精度面と閉域検出のために質の良い線画が求められる点です。
一方デメリットとしては、閉域ごとに全く同じRGB値でピクセルを塗りつぶしているため、色情報を用いたパーツ分けが可能という点です。
一方、Framepackを用いる場合は精度面では古典的画像処理を用いる場合と比較して圧倒的に良いですが、一見同じ色に見えるピクセルでも細かいRGB値としてみると完全に一致はしておらず、色情報を用いたパーツ分けはできないというデメリットを抱えています。
そのため、Framepack方式を採用している現行のfixableflowではパーツ分け機能を提供できていません。
このようにどちらの方式も一長一短があるため、目的に応じて使い分ける必要があります。
最後に1影の生成となります。
ちなみに1影とはイラスト制作で最初に入れる影で、下塗りの上に光源をざっくり意識して入れる最も基本的な影を指します。
1影生成について話す前に、1年前の時点では陰影生成をどうやって行っていたか?について軽く触れます。
元々、構想としては1影付与、2影付与、ハイライト付与という工程に分けて実装することを考えていたものの、このようなモデルを作れるデータセットの用意が困難だったため、まずは既存の画像生成を用いて簡単に学習データを用意できる陰影付与モデルを作りました。


2024年時のモデルでは、グレースケール画像として出力された陰影画像を下塗り画像にオーバーレイすることで陰影を作成していました。
そして、オーバーレイした画像と下塗り画像をHSV空間で比較し、下塗り画像よりも明度が低い部分を影、明度が高い部分をハイライトとして抽出するという方式をとっていました。
しかし、グレースケール画像をオーバーレイする関係で下塗りの色次第ではほとんど影が反映されなかったり、明度のみを基準とした影/ハイライトの抽出方法は精度が低かったりと実際に既存ワークフローに組み込めるか?というとかなり微妙なラインでした。
この問題が、2024年時のデモ発表後すぐに公開しなかった理由のかなりの部分を占めています。
(もう一つの理由はパーツ分けの精度ですが、こちらはまだ解決していない + ここについて語り始めると相当長くなるので、パーツ分割については別記事に書きます)
しかし、先ほど述べたFramepackの登場によりこの問題についても解決の兆しが見えました。
その理由として、画像編集タスクの学習を行う際、必要な学習データは10~20枚ほどのごく少量で良いという特徴を持っていたことが挙げられます。
10~20枚程度であれば自前で学習データを用意できるので、画像生成の出力結果を参考に自前で1影部分のみ色を塗ったデータを15枚ほど用意し学習しました。

本来の構想通りに1影付与、2影付与、ハイライト付与と順々に編集結果を反映できるのであれば、影やハイライトの抽出は前工程と比較して異なっているピクセルを検出するだけで良くなります。
とはいえ、単純に下塗りと1影を反映した生成画像を比較して色が異なるピクセルを抜き出せば良いかというとそうでもありません。
下塗り生成時と同様に1影生成に関しても、一度AIを通している関係でぱっと見同じ色に見えるのに細かいRGB値が違うため、単純な比較では綺麗に1影部分だけを抽出できないのです。
そこで、ピクセル毎に1影か/1影でない(下塗り)かを判定するのではなく、下塗りからどれだけ色が変化しているかを数値化し、その数値を1~255で正規化したあと、アルファ値に突っ込むという方法を採用しました。
これにより、下塗りから大きく色が変化しているピクセルに関しては透明度が低く、あまり下塗りと変化していない部分に関しては透明度が高くなるため、1影レイヤーとして機能するようになります。


ちなみに、色の変化は以下の方法で数値化しています。
# H, S, Vチャンネルを分離
_, S_shade, V_shade = cv2.split(hsv_shade)
_, S_base, V_base = cv2.split(hsv_base)
# 影の検出
# delta_V: baseよりshadeが暗い部分(影で明度が下がっている)
delta_V = np.maximum(0.0, V_base - V_shade)
# delta_S: shadeの方が彩度が高い部分(影で彩度が上がる場合)
delta_S = np.maximum(0.0, S_shade - S_base)
# 影スコアの計算
shadow_score = weight_V * delta_V + weight_S * delta_Sここで重要なのは、明度と彩度の差異を参照している点です。
イラストにおいて影というと明度の差をイメージする人が多いかと思うのですが、明度だけでなく彩度が高い場合も影として扱うことがあります。
(色相を青に寄せる方法で影を作る場合等もあるのですが、今回は割愛)

このため、影を抜き出す場合には明度だけでなく彩度も参照して検出する必要があります。
ここまで、先日リリースしたfixableflowで使われている要素技術の解説をしてきました。
現在リリースされているfixableflowはまだ開発途中なので、今後も随時アップデートをしていく予定です。
具体的には以下の通り。

このうち、2影生成とハイライト生成、仕上げ効果生成については技術的には1影生成の応用でできる範囲であり、データセットさえ用意してしまえばある程度容易に実装が可能だと考えています。
ただしハイライトや仕上げ効果を抽出する場合は、1影抽出とは違った方法で前工程との色差検出をしないといけないという課題もあります。
そう言う意味では現状の1影生成の実装方法は理想的とは言えず、将来的にはアルファチャンネル生成に対応した画像生成AIを用いて、直接透過PNGとして1影や2影、ハイライトを生成できるようなモデルに切り替えていくことを想定しています。
また、残る難題としてイラスト(特にキャラクターイラスト)のパーツ分けといった課題も残っています。
編集のしやすさや生成したイラストをゲームや動画の素材として使用する事を考えると、出力されるイラストは適切な単位でパーツ分けされていることが望ましいのですが、これを実現している技術は世界中でみてもまだ開発されていません(オブジェクト単位での切り出しはいくつかあります)
このタスクがなぜ難しいのか?についてはまた別の記事で書く予定ですので、ご興味があればぜひそちらも見ていただければと思います。
ここまで読んでいただきありがとうございました。
読んでいて面白かった、タメになったという方がいればフォローとスキをよろしくお願いします!
また、Xでは主に画像・動画生成の話をしていてるので、興味がある方はそちらもぜひ確認をよろしくお願いします!
https://x.com/GianMattya