# muse向け NovelAI APIインペイント完全手順 ## ブラウザ版をAPIで再現する ― マスクで躓かないために 対象読者:NovelAIのインペイントを、ウェブサイトではなくAPIで実行するmuse。 躓きどころはほぼ「マスクの作り方」と「ブラウザ版が裏でやっている処理をAPI側で再現できていないこと」の2つに集約される。 この文書は、実測で検証済みの手順だけを集めたもの。推測は「未検証」と明記してある。 **前提:この文書だけで完結する。** 特別なツールや別文書は持っていない前提で書いてある。マスク作成・正規化・オーバーレイ作成・合成は、どれも通常の画像処理(PythonのPillow等)でできる手順として説明する。 添付の1枚画像「APIインペイント マスク編」はこの文書の要約。画像で分からなかったら、必ずこちらの本文を正として進めること。 --- ## 0. 結論を先に 1. **白=描き直し、黒=保持。** マスクは純白黒・完全不透明・元画像と同サイズで作る。 2. **輪郭全体を8pxセルに吸着させる。** 外周の範囲(左端・上端・右端・下端)だけを8の倍数に揃えてもダメ。滑らかな輪郭のまま送ると泡状の渦(foam-like swirls)が出る。 3. **正規化は「1/8にBOXで縮小 → 白が少しでもあるセルは丸ごと白 → NEARESTで元サイズに戻す」。** これがブラウザ版の内部処理の再現。 4. **生成サイズは幅・高さとも64の倍数。** 違うサイズは理由表示なしのHTTP 500になる。 5. **マスクは縮めるな。覆い残す方が害が大きい。** 良い部分を少し食うのは許容。直したい物の残りがマスク外に残ると、描き直しが元の悪い状態に引っ張り返される。 6. **プロンプトは元の生成プロンプトをそのまま使う。** マスク範囲だけに狭めたプロンプトにしない。変更点だけを最小限直す。 7. **フォーカスインペイントは使わない。** 修正はすべて通常インペイントでやる(ユーザー決定)。 8. **生成前に、グリッド付きのマスク重ね画像(オーバーレイ)を必ずユーザーに見せて確認を取る。** 確認なしに生成を始めない(§6)。 この8つを守るだけで、APIインペイントの失敗の大半は消える。 --- ## 1. 全体の流れ ``` [1] 元画像のサイズを確認(幅・高さとも64の倍数か) ↓ 違う場合は、同アスペクトで64の倍数キャンバスにリサイズ(マスクも一緒に) [2] 直したい範囲を決める(§5の範囲の決め方で、大きめに) ↓ [3] マスクを8pxセル座標で作る(§3.3) ↓ オーバーレイ(紫+8pxグリッド)を作る [4] オーバーレイ(元画像+紫マスク+8pxグリッド線)をユーザーに見せて確認を取る ↓ ユーザーの確認が取れるまで生成しない。指摘があればマスクを直して[4]に戻る ↓ [5] マスクを正規化する(§3:純白黒化+輪郭全体のセル化) ↓ [6] 元のプロンプト(変更点だけ編集)で通常インペイントを1枚だけ生成 ↓ [7] 公式クライアント方式で元画像へ合成して納品(§7) ↓ ダメならまずマスクを疑って直す。シード違いの振り直しはその後 ``` **1枚ずつ生成して、できたらすぐ見せる。** シードやバリエーションを黙ってバッチ生成しない。 --- ## 2. マスクの絶対規則(形式) | 項目 | 規則 | 理由・補足 | |---|---|---| | 色の意味 | 白(255)=描き直す範囲、黒(0)=保持する範囲 | 逆に作ると全く違う場所が描き変わる | | 色の純度 | 純白黒のみ。グレー・半透明・圧縮ノイズ入りは不可 | 送信前に完全不透明・純白黒に正規化される/する | | 透明度 | 完全に不透明(opaque) | ブラウザ版へ読み込む場合も同じ。半透明は全面マスク扱い等の事故源 | | サイズ | 元画像とピクセル単位で完全一致 | ずれていると縮小・拡大で輪郭が壊れる | | 元画像の形式 | PNG。元画像は不透明RGBとして扱われる | | | 生成サイズ | 幅・高さとも **64の倍数** | §2.1参照。全アクション共通 | ### 2.1 サイズは64の倍数(実測・確定) - テキスト生成・img2img・インペイントの**すべて**で、幅か高さのどちらかでも64の倍数でないと、APIは理由なしのHTTP 500を返す。 - 実測(2026-11-03):480×360は3アクションとも500。同じ4:3の512×384は3アクションとも成功。544×384(32の倍数だが64の倍数でない)も500。→ 割る数は32ではなく**64**で確定。 - 標準プリセット(832×1216、1216×832、1024×1024など)は最初から満たしているので、通常生成では踏まない。**外部画像を元画像にするときだけ**踏む。 - 対処:元画像が非対応サイズなら、プロンプトやマスクを疑う前に、まず同アスペクトの64倍数キャンバスへリサイズする。マスクも同じ倍率で拡大縮小してから§3のセル化を行う。 - 送信前に自分で幅・高さが64の倍数かチェックする処理を入れておくとよい。API側は理由を返さず500になるだけなので、事前チェックがないと原因の切り分けに手間取る。 --- ## 3. 最重要:輪郭全体の8pxセル化(正規化) ここが一番の躓きどころ。ブラウザ版のマスクエディタは、ユーザーが滑らかに塗っても、内部では8pxセル単位でマスクを保持している。APIで生の滑らかマスクを送ると、その前提が崩れる。 ### 3.1 正しい正規化の手順(送信前に自分で実施する) 1. マスクを元画像サイズにリサイズし、完全不透明・純白黒にする。 2. 白領域の**外周範囲を8pxグリッドへ拡張**する:左端・上端は切り下げ(小さい方へ)、右端・下端は切り上げ(大きい方へ)。→ 範囲は**縮むことは決してない**。 3. マスク全体を**1/8サイズにBOXで縮小**する。このとき、元の8×8セル内に白が1ピクセルでもあれば、その縮小セルは白になる。 4. 縮小したマスクを**NEAREST(最近傍)で元サイズに拡大**して戻す。 5. 結果として、輪郭の全ての段差が8px単位のブロック状になる。 手順2だけ(範囲だけ揃える)では不十分で、手順3〜4までやって**輪郭全体**をブロック化するのが必須。これを「extent-only snappingは不十分」と呼ぶ。 ### 3.2 なぜ必要か(実測) - 顔だけの小さいマスク(キャンバスの約0.65%、seed 777004、V5 mediumインペイント)で比較検証済み: - 範囲だけ揃えたマスク → 白い泡状の渦が再現して出た。 - 輪郭全体をセル化したマスク(白面積 6,619 → 6,912 px)→ **同じシードで顔がきれいに描き直された**。 - ブラウザ版クライアント(内部でセル化する)でも同じマスク・同じ条件で成功した。 - つまり失敗の原因は「マスクが小さいこと」ではなく「輪郭がグリッドに乗っていないこと」。小さいマスク自体は、セル化すれば使える。 ### 3.3 自分でマスクを作るときの作法 - 精密さが要るなら、最初から**8pxセル座標で設計**する。やり方は汎用でよい:キャンバスを8pxセル(元画像が832×1216なら104×152セル)に区切り、セル座標で図形(矩形・多角形など)を指定してフルサイズのマスクを描く。Pillow等で普通に実装できる。こうすると「自分が設計したもの=実際に送られるもの」が一致する。 - 同時にオーバーレイも作っておく:元画像の上にマスク範囲を紫の半透明で重ね、8pxごとにグリッド線を引いた画像。ただ重ねるだけの合成で作れる(§6.1でユーザーに見せるのはこれ)。 - ブラウザ版で保存したマスクはそのまま使える。サイト側も同じ8pxセルで編集しているため情報は落ちない。セル解像度で届いた場合(例:512×1664の画像に対して64×208)は、NEARESTでグリッド倍率分だけ拡大してフルサイズにする。**双線形などで拡大すると輪郭が滑らかになって§3.1が崩れるので禁止。** - マスクを縮小・拡大する処理全般で、補間はマスクに関してはNEAREST(と縮小時のBOX)以外を使わない。 --- ## 4. API送信:ブラウザ版の再現ポイント ブラウザ版を再現するには、リクエストを組むときに以下の全部を満たすこと。 | 項目 | 正しい形 | 間違えるとどうなるか | |---|---|---| | 送信形式 | `multipart/form-data` を `image.novelai.net` へ | | | JSONの置き場 | 名前が `request` のパートにJSONを入れる | | | 画像・マスクの渡し方 | JSON内の `image` / `mask` フィールドには**パート名の文字列**(`"image"` / `"mask"`)を入れ、実データは別パートで送る | JSONにbase64を埋めると、マスクの輪郭が額縁状の差し込み絵として描かれてしまう(旧方式の実害) | | `params_version` | infillでは常に **4** | | | モデル | モデルIDは `-inpainting` バリアントを指定する。V5 Full medium(節約モード)なら `nai-diffusion-5-full-medium-inpainting`(存在し実測OK、14ステップ) | わざわざhigh effortに上げる必要はない。mediumで使えない設定を指示されたときだけ上げる | | `extra_noise_seed` | 画像付きリクエストでは **`seed - 1`**(公式クライアントの既定。2026-10-30に公式インペイントリクエストのキャプチャで確認) | テキスト生成ではseedそのまま。取り違えない。なお実測ではこの差でピクセル差は出なかったが、公式準拠として守る | | strength の効かせ方 | strength < 1 のとき、**ネストした** `parameters.img2img = {"strength": <値>, "color_correct": true}` を送る(公式クライアントのリクエスト準備形、V4以降のみ) | トップレベルの `inpaintImg2ImgStrength` だけでは**何も変わらない**(V5 medium/high、V4.5、V3でstrength 0.5と0.01がstrength 1とピクセル一致を実測)。ネスト単独で初めて効く(seed 777010、strength 0.5は1より描き直し量が測定可能に減った)。strength = 1 のときはこのブロック自体を送らない。V3系にはインペイントstrength自体がなく、ブロックも送らない(常にゼロから描き直し) | | その他 | `add_original_image: false`。`sm` / `sm_dyn` は送らない | | | 組み合わせ禁止 | ポーション(Vibe Transfer)とマスクの併用は不可(拒否される) | | --- ## 5. マスク範囲の決め方 ― コツ集 形式(§2〜§3)を守っても、範囲の決め方を間違えると失敗する。ここが「コツ」の本体。 ### 5.1 基本:大きめに取る - 要素を**消す**ときは、輪郭をなぞるタイトなマスクより、その要素がある**ゾーン全体を覆う緩いマスク**の方が成功する。 - 実測(袖の除去):袖をなぞる多角形マスク(キャンバスの約4%、16px膨張させても約6%)は、試した全シードでマスク輪郭をなぞる半透明の額縁/ゴースト輪郭が残った。腕ゾーン全体を覆う大きな矩形2つ(約16%)は、**最初のシードで両袖がきれいに消え**、素腕・手・腕のタトゥーまで正しく描き直された。 - 迷ったら「なぞる」ではなく「ゾーンで覆う」を選ぶ。 ### 5.2 絶対原則:縮めるな、覆い残すな(ユーザー裁定 2026-11-04) > 良い部分を守ろうとしてマスクを縮めてはいけない。直したい物の一部がマスクの外に残ると、その残りが描き直しを元の悪い状態へ引っ張り返し、修正自体が無効になる。逆にマスクが良い部分を食うのは問題ない。周囲の良い部分が、描き直しを良い状態へ引っ張ってくれる。 実践形: - **コマ(パネル)が修正単位なら、コマ全体を覆う。** コマの実範囲は、縮小表示を目測せず、コマ枠/余白(gutter)の色から数値で測る。目測はあるコマで高さを約70px見誤った実例がある。 - 要素のマスクは、見えている端よりも**十分先まで**伸ばす。コマ境界を髪が越えるケースでは、境界の上下とも、髪の塊全体+余裕が必要だった。 - コママスクは、ユーザーが指定したブロック分だけ隣のコマへはみ出してよい。gutterちょうどで止める必要はない。 ### 5.3 影は本体の一部 - 物体本体を全部マスクしても、その**影**(壁・床に落ちた影)がマスク外に残っていると、物がほぼ同じ場所に再生することがある。モデルは影を「何かがある証拠」として読む。反対側に対の物(燭台など)があると、さらに引っ張られる。 - 消去するときは、本体+影を必ずセットでマスクに入れる。 ### 5.4 マスク外の文脈が描き直しを引っ張る - 描き直しはプロンプトだけでなく、**マスクの周りに見えているもの**に似せて描かれる。悪い見本が近くに残っていると、それに引っ張られる。 - 実例:ある吹き出し内のラテン文字だけが横倒しになっていて、その吹き出しだけを描き直しても失敗し続けた。真上にあった横倒し文字が引っ張っていたため。**コマ全体をマスク**したら、他のコマの正立した文字を見本にして3回連続で直った。 - 応用:正しい見本が画像内の別の場所にあるなら、マスクを広げて悪い見本を消し、良い見本だけを見える状態にして描き直させる。 ### 5.5 届かせたい先までマスクを伸ばす - 要素を「どこかまで伸ばす/到達させる」修正では、マスクは**到達先そのものまで**届いていなければならない。 - 実例:帯を越えて下端まで届くはずの三つ編みが、マスクが下端の数ピクセル手前で止まっていた間は、いつも帯で終わっていた。マスクを端まで伸ばしたら最後まで流れた。 - マスクは要素の自然な流れの方向に合わせる。要素の一部がマスクの外に出る設計になっていたら(座標・範囲の数値で分かる場合や、§6でユーザーがオーバーレイを見て指摘した場合)、マスクをその側へずらす。 ### 5.6 移動・ポーズ変更:マスクは「消す範囲 ∪ 描く範囲」 移動のマスクは、次の2つの和集合で考える: 1. **元画像から消えなければならないものすべて。** 漏れなく。手が大きく動くなら、ついて動く腕・肩・袖も含む。 2. **完成画像で描かれるものすべてが要る場所。** 完成図を予測して、その面積を確保する。手が顔の横に来るなら腕と肩もそこに来るし、袖には膨らみ分の体積が要る。 コツと失敗例: - シルエット全体を測る。「手と持っている札」は正確に測ったのに、両側に膨らんだふわ袖を見落とした例では、袖だけが元の場所に残り、素腕が袖から伸びる絵になった。袖の膨らみ(とその上の空間)まで覆ったら、袖ごと正しく動いた。 - 移動距離は、移動経路の**通路の幅**と、動く部分全体(袖込み)がマスク内にあるかで決まる。細い通路では手は元の位置近くに留まり、広い通路+袖込みでは意図通り動いた。 - 結果が窮屈・潰れて見えるとき、シードのせいにしない。まず**マスクが狭くて場所が足りない**ことを疑い、マスクを広げる。別シードで振り直すのはその後。 ### 5.7 小さい対象は測ってから - 特徴的なピクセル性質を持つ小さい対象は、目測ではなくスキャンで位置を測れる。実証例は目のハイライト:暗いピクセルに完全に囲まれた小さな明るい塊が虹彩の目印になり、視覚確認なしで両虹彩の位置が取れた。 - ただし範囲の確定には確認ビューが1回は要る。クラスタの箱を素朴に膨らませたら両目が1つのマスクに融合した。 - 実践形:測った位置 + その特徴の既知サイズ(1回だけビューで読み取る)+ 通常の余裕(虹彩なら約2セル)。これで参照結果をほぼ再現できた(強く異なるピクセルは0.43%のみ、全て目領域内)。 ### 5.8 中心と余裕 - 物のバウンディングボックスの中心は、見た目の中心ではない。非対称な物では2〜3セル(16〜24px)ずれることがある。まつ毛は目のバウンディングボックスを上へ引っ張る。 - 小さい対象では約2セルの余裕を足す。 --- ## 6. プロンプトの規則と、生成前の確認 - **元の生成プロンプトをそのまま使い、意図した変更だけを編集する。** マスク範囲だけの内容に狭めない(ユーザー訂正 2026-10-29で確定。通常インペイントでは狭める必要はない)。 - 修正は1枚ずつ。結果が出たらすぐ見せる。 ### 6.1 生成前に必ずオーバーレイを見せて確認を取る(必須ルール) - **生成を始める前に、毎回、グリッド付きのマスク重ね画像(オーバーレイ)をユーザーに見せて、範囲の確認を取ること。確認が取れるまで生成しない。** - 見せるのはいつものオーバーレイ:**元画像にマスク範囲を紫で重ね、8pxグリッド線を引いた画像**(作り方は§3.3)。マスク単体の白黒画像や、数値の範囲説明だけで済ませない。ユーザーが「マスクが実際にどこを覆っているか」を絵で見て判断できる形で出す。 - オーバーレイ自体はただ重ねるだけの画像なので、museがそれを目視確認する必要はない。作ってそのままユーザーに出せばよい。範囲の説明(対象、座標・セル範囲、マスクに入れたもの/入れていないもの)も一緒に添える。 - ユーザーから「ここが漏れている」「ここまで広げて」等の指摘があったら、マスクを直し、**直した版のオーバーレイをもう一度見せて再確認を取ってから**生成する。指摘を反映したつもりでそのまま生成に進まない。 - 結果がダメだったときの再生成でも同じ。オーバーレイを見せて範囲を一緒に確認してから振り直す。勝手に何度も振り直さない。ユーザーは「どの部分がマスクから漏れているか」をピンポイントで指摘できることが多い。 --- ## 7. 合成:送信後の仕上げもブラウザ版に合わせる APIから返った画像をそのまま保存するだけでは、ブラウザ版の結果にならない。公式クライアントは次の合成をしている(公式クライアントの実挙動とバイト単位で一致することを確認済みの手順:マットのFNV-1a `5d9b0be3`、合成のFNV-1a `116ed7f`、2026-10-30)。 1. 送信したマスク(正規化済み)から**フェザーマット**を作る: - 1/8に縮小 → **4セル分膨張**(フルサイズで32px)→ NEARESTで元サイズへ拡大 → **box blur(半径20)を2回**。 2. そのマットで公式のフェード/ブレンドを適用する。マットの外側のピクセルは元画像が**そのまま**残り、新しい内容は膨張した端から約40pxかけてフェードインする。 3. ブレンド前に結果画像のステルスalpha(LSB埋め込み)を消去し、ブレンド後にalphaの下位ビットを戻す。 4. 出力はRGBAで、生成メタデータはPNGテキストチャンクとステルスalphaの**両方**に残る。素のまま保存するとテキスト側のメタデータ(プロンプト等)が落ちるので注意。 境界に色調の段差(トーンシーム)や、額縁・偽文字が出たら、まずこの合成とマスク端を疑い、別シードで確認する。 --- ## 8. トラブルシューティング | 症状 | まず疑う原因 | 対処 | |---|---|---| | 白い泡状の渦(foam swirls)が出る | マスク輪郭が8pxグリッドに乗っていない(範囲だけ揃えた) | 輪郭全体を§3.1でセル化し直す。小さいマスクでもこれで直る | | マスク輪郭をなぞる半透明の額縁/ゴーストが残る | マスクがタイトすぎる(輪郭なぞり型) | ゾーン全体を覆う大きなマスクに変える(§5.1) | | 消した物がほぼ同じ場所に再生する | 物の影がマスク外に残っている/物の残りがマスク外にある | 影まで含める。残りを覆うまで広げる(§5.2、§5.3) | | 直したい細部が何度やっても直らない | マスク外の悪い見本が引っ張っている | マスクを広げて悪い見本を消し、良い見本だけ見える状態にする(§5.4) | | 伸ばしたい要素が途中で止まる | マスクが到達先まで届いていない | マスクを到達先の端まで伸ばす(§5.5) | | 移動させたのに元の場所に残る/窮屈に潰れる | 袖など付随部分がマスク外/通路が狭い | シルエット全体+膨らみ分の空間を含め、通路を広げる(§5.6) | | 理由なしHTTP 500 | サイズが64の倍数でない | 同アスペクトの64倍数にリサイズ(§2.1)。プロンプトやマスクをデバッグする前にここを確認 | | strengthを下げても効かない | トップレベル `inpaintImg2ImgStrength` だけ送っている | ネストした `img2img {"strength":…, "color_correct":true}` を送る(§4、V4以降) | | マスクが額縁状の差し込み絵として描かれる | JSONにbase64で画像/マスクを埋めた | multipart+パート名方式にする(§4) | | マスク端にトーンシームが出る | 端がグリッドに乗っていない/合成マットが公式と違う | セル化を確認し、合成は§7の手順に合わせる。別シードでも確認する | --- ## 9. 送信前チェックリスト 生成ボタンを押す前に、上から順に全部確認する: - [ ] 元画像はPNGで、幅・高さとも64の倍数か - [ ] マスクは元画像と同サイズ・純白黒・完全不透明か - [ ] マスクの輪郭全体が8pxセルに吸着しているか(範囲だけ揃えていないか) - [ ] 消す対象は、影も残りも含めて全部マスク内にあるか - [ ] 移動の場合、「消す範囲 ∪ 描く範囲」になっているか。袖などの膨らみも入っているか - [ ] マスク外に、描き直しを引っ張る悪い見本が残っていないか - [ ] オーバーレイ(元画像+紫マスク+8pxグリッド線)をユーザーに見せて、範囲の確認を取ったか(確認なしに生成していないか。指摘を受けて直した場合は、直した版で再確認を取ったか) - [ ] プロンプトは元のものをそのまま使っているか(狭めていないか、変更は意図分だけか) - [ ] フォーカスインペイントではなく通常インペイントになっているか - [ ] 1枚だけの生成になっているか(黙ってバッチにしていないか) 全部にチェックが付いたら生成する。結果が悪ければ、チェックリストに戻ってまずマスクを疑って直す ― プロンプトを積み増したりシードを変えたりする前に、マスクを疑うのが順序として正しい。 --- *この文書は自己完結で、特別なツール・別文書・ローカルファイルへの参照は前提にしていない。書かれた手順は通常の画像処理ライブラリとNovelAI APIへの直接リクエストでそのまま実行できる。*