技術約10分で読めます

Qwen-Image 2.1の部分編集は、引数1つで位置ずれが約0.1pxになった

いけさん目次

7月にQwen-Image-Edit 2511のピクセル位置ずれを測った記事では、ComfyUIの標準ノードが参照画像を勝手にリサイズするせいで、編集のたびに絵が約8pxずれていた。ComfyUI標準のReferenceLatentノードで元の寸法のまま入れると、ずれは約1pxまで小さくなった。

Qwen-Image 2.1は、文章からの生成と画像の編集が1つのモデルにまとまっている。
M1 Maxでは編集1枚に約12分かかっていたが、TaylorSeerキャッシュを入れた記事で約4分半になり、Qwen-Image-Edit(8ステップの蒸留版)の約5分とほぼ同じになった。
なら、表情だけ変えるような部分編集でも位置を保てるのか。7月と同じ元画像、同じ測り方で確かめた。

検証環境

項目内容
マシンMacBook Pro、Apple M1 Max、メモリ64GB
実行環境Python 3.12.12、PyTorch 2.14.0(MPS)、Diffusers 0.41.0.dev0(コミット 80c7ed26
モデルQwen-Image 2.1(BF16)
共通の生成条件832×1216、40ステップ、シード42、CFG1.0、ネガティブプロンプトなし。各条件1回ずつ
高速化TaylorSeer Lite(数ステップだけ実際に計算して、残りを予測するキャッシュ。cache_interval=3)とKVキャッシュの併用。1条件だけキャッシュなしでも生成
元画像7月と同じ、自作キャラ2人の全身立ち絵 832×1216
比較対象7月の記事のQwen-Image-Edit 2511(Qwen-Rapid-AIO v23、8ステップ)の出力と計測値
ずれの測定エッジマップの位相相関(2枚の画像のずれ量を周波数から求める方法)を、画像全体と3×3ブロックで計算。dxは横、dyは縦のずれ。7月の測定スクリプトを流用
変化の測定元画像とのRGB平均絶対差が20/255を超えた画素の割合。顔の枠は、2人の顔を目視で囲った長方形2つで、7月と同じ座標

測定スクリプトは、7月のQwen-Image-Editの出力にかけて、記事と同じ値(線画でdx +8.0px、表情編集の顔の外0.7%)になることを先に確かめた。
測定スクリプトと編集の指示はLiltingChannelLaboに置いた。

元画像。金髪ロングと茶髪サイドポニーの2人が制服で正面に立つ全身イラスト、832×1216

Diffusersのパイプラインが行う参照画像のリサイズ

Diffusersの QwenImage21Pipeline は、参照画像を受け取ると、面積が output_resolution の2乗(既定は1024×1024)になる寸法を計算して、そこへリサイズしてからVAE(画像と内部表現を変換する部分)とテキストエンコーダーに入力する。寸法は32の倍数に丸められる。

# diffusers/pipelines/qwenimage21/pipeline_qwenimage21.py(抜粋)
def calculate_dimensions(target_area, ratio):
    width = math.sqrt(target_area * ratio)
    height = width / ratio

    width = round(width / 32) * 32
    height = round(height / 32) * 32
    return width, height, None

832×1216の元画像を入れると、参照は832×1248になる。幅はそのままで、縦だけ2.6%伸びる。
出力の寸法を元画像と同じ832×1216に指定すると、参照と出力で格子が合わなくなる。

引数を1つ指定するだけで直った。output_resolution に、元画像の幅と高さの積の平方根を丸めた値を指定する。832×1216なら1006で、output_resolution の2乗が元画像の面積とほぼ同じになるので、計算される寸法が832×1216ちょうどになり、リサイズしても寸法が変わらなくなる。

元画像の寸法既定(1024)での参照の寸法指定する output_resolutionそのときの参照の寸法
832×1216832×12481006832×1216
1216×8321248×83210061216×832
1024×10241024×102410241024×1024
960×1280896×11841109960×1280
1024×1536832×124812541024×1536

この計算が合うのは、元画像の幅と高さが32の倍数のとき。1024×1024や768×1344のように、既定のままで寸法が変わらない組み合わせもある。

image = pipe(
    prompt=prompt,
    image=[source],          # 832×1216
    width=832,
    height=1216,
    output_resolution=1006,  # round(sqrt(832 * 1216))
    num_inference_steps=40,
    true_cfg_scale=1.0,
    generator=torch.Generator("cpu").manual_seed(42),
).images[0]

既定のままを条件A、output_resolution=1006 を条件Cとする。呼び方は、7月の記事の条件A(標準ノード直結)と条件C(ReferenceLatent)に合わせた。7月の条件Bにあたる1024×1024への切り出しは、今回はやっていない。

実験1 線画推定でずれを測る

7月と同じ線画化の指示を使った。参照画像の呼び方だけ、Qwen-Image 2.1の書き方に合わせて Picture 1 から image 1 に変えている。

Convert image 1 into clean black line art on a pure white background. Trace every contour and detail exactly where it is in the original image. Do not move, resize, crop, or redraw anything. No color, no shading, no gray tones.
モデル条件全体のずれ3×3ブロックの傾向
Qwen-Image 2.1A(既定)dx +1.1px / dy +0.0px左の列が+6〜+8px、右の列が−5〜−8px。左右から中央へ縮んでいる
Qwen-Image 2.1C(output_resolution=1006dx +0.0px / dy +0.1px9ブロック中6つが0.1px以下、残りも1px未満
Qwen-Image-Edit 2511(7月)A(標準ノード直結)dx +8.0px / dy −3.3px左端+3px→右端+14pxの勾配
Qwen-Image-Edit 2511(7月)C(ReferenceLatent)dx +1.1px / dy +0.5px全ブロック約1pxで均一

条件Aは、全体の平均では小さい値になるが、ブロックごとに測ると左右で逆にずれていた。左の列が約+7px、右の列が約−8pxで、列の中心どうしは約555px離れている。15÷555で、人物が横に約2.7%細くなった計算になる。参照が縦に伸ばされた2.6%とほぼ同じ。
伸ばされた参照の縦横比が、832×1216の出力の中でもそのまま保たれた結果に思える。縦のずれはどのブロックも1px未満だった。

条件Cは画像全体で0.1px以下になり、7月のReferenceLatent構成の約1pxより小さい。
位相相関で出るのは、輪郭どうしが一番よく重なる平行移動の量で、画素の1つ1つのずれが0.1px以内という意味ではない。

条件A(既定)
条件Aのエッジ重ね合わせ。左右の輪郭が赤とシアンに分かれて二重になっている
条件C(output_resolution=1006)
条件Cのエッジ重ね合わせ。輪郭がほぼ白一色に重なっている

元画像のエッジを赤、出力のエッジをシアンで重ねた。

条件Cの線画出力。2人の全身立ち絵が均一な黒線で線画化されている

実験2 表情だけ変えて、ほかが動かないか

7月の記事で、ほかを変えるなという指定を付けなかった版と同じ1行の指示で、2人の表情を閉じ目の笑顔に変えた。

Change both girls' facial expressions to a big cheerful open-mouth smile with happy closed eyes.
モデル条件平均差分変化画素率(全体)顔領域内顔領域外
Qwen-Image 2.1VAE往復のみ1.210.02%0.12%0.02%
Qwen-Image 2.1A(既定)15.1815.1%49.2%12.8%
Qwen-Image 2.1C、TaylorSeerあり4.333.8%20.8%2.6%
Qwen-Image 2.1C、キャッシュなし4.433.9%21.1%2.7%
Qwen-Image-Edit 2511(7月)C(ReferenceLatent)2.661.7%16.9%0.7%

条件Aは顔の外の12.8%が変わった。人物が細くなった分、全身の輪郭が動いている。
条件Cは顔の外が2.6%まで下がった。TaylorSeerありとキャッシュなしはほぼ同じ値で、今回の表情編集では、キャッシュの有無による違いは小さかった。

条件A(既定)
条件Aの差分ヒートマップ。全身の輪郭に沿って赤く光っている
条件C(output_resolution=1006)
条件Cの差分ヒートマップ。赤い領域が2人の目と口に集中し、輪郭に細い赤線が残る

顔の外の2.6%は、7月のQwen-Image-Editの0.7%より多い。VAEの往復だけなら0.02%だった。
差分のヒートマップでは、全身の輪郭に沿って細い赤線が残っている。元画像と並べて拡大すると、位置は合っているが、輪郭線がわずかに細くなり、肌の影も少し薄くなっていた。今回の出力では、編集していない場所の輪郭も軽くなぞり直されたように思える。

条件Cの表情編集の出力。2人とも閉じ目で口を開けた笑顔になり、体と服は元画像と同じ位置にある

顔の描き方を、元画像、7月のQwen-Image-Edit、今回のQwen-Image 2.1で並べた。

元画像
元画像の2人の顔。無表情
Qwen-Image-Edit 2511(7月)
7月のQwen-Image-Editの笑顔。歯の線が入った大きな口
Qwen-Image 2.1(今回)
今回のQwen-Image 2.1の笑顔。弧を描く閉じ目と開いた口

実験3 怒り顔と、片方だけの着せ替え

条件Cのまま、指示を変えて2枚出した。

(怒り顔)
Change both girls' facial expressions to an angry face with furrowed eyebrows.

(左の子だけ黒セーラー服)
Change only the left girl's outfit to a black sailor school uniform.
編集全体顔領域内顔領域外左半分右半分画像全体のずれ
怒り顔3.6%18.8%2.6%3.5%3.7%dx −0.1px / dy +0.0px
左の子だけ黒セーラー服14.2%9.7%14.5%25.2%3.2%dx −0.1px / dy −0.0px

怒り顔の顔の外は2.6%で、笑顔と同じ値になった。
左の子だけの着せ替えでは、指示していない右半分にも3.2%の変化が出たが、表情編集の顔の外とほぼ同じ値だった。7月のQwen-Image-Editでは、同じ編集の右半分が0.42%だった。
画像全体のずれは、笑顔も含めてどの編集でも約0.1pxだった。

怒り顔
怒り顔に編集した出力。2人とも眉を寄せた顔になり、体と服は元画像のまま
左の子だけ黒セーラー服
左の子だけ黒いセーラー服に変えた出力。右の子は元画像のまま

背景の透過

表情や衣装の編集の出力はRGBAのPNGで保存されていたが、アルファ(透明度)は全面255だった。
パイプラインのコードを確かめると、Qwen-Image 2.1のVAEは入力が4チャンネルで、参照画像もRGBAに変換してから入力している。背景を透明にする指示を出してみた。

Remove the white background of image 1 and make the background fully transparent. Keep the two girls exactly as they are.
項目
アルファが128未満の画素57.5%
アルファが255の画素39.9%
背景部分のアルファ大半が1〜8(完全な0は1.6%)
全体の位置ずれdx +0.0px / dy −0.0px
出力のRGBA
背景を透過にした出力。ページの背景色が透けて見える
水色の背景に重ねたもの
透過の出力を水色の背景に重ねた画像。白いシャツと白い靴下は抜けずに残っている
アルファチャンネル
アルファチャンネルを白黒で表示した画像。2人の人物が白、背景が黒

白い背景の上の白いシャツと白い靴下は、透明にならずに残った。色のしきい値で白を消す方法だと、シャツと靴下も一緒に消える。
背景のアルファは完全な0にはならず、大半が1〜8だった。

前と後ろの2枚を参照にして横向きを出す

元画像の右側にいる茶髪のサイドポニーの子が、オリキャラのかなちゃん。ローカル版の記事では、かなちゃんの立ち絵を真横に編集すると、本人の左側で結んだサイドポニーの位置が保てなかった。
三面図を参照に入れるとちゃんとポーズを取れるという話を聞いたが、その三面図の横向きの1枚が出せない。前と後ろの2枚を入れたら横向きが出せないか、試してみた。後ろ姿は、ローカル版の記事で出した真後ろの画像を使った。

プロンプトは前回の真横の英文のままで、参照の呼び方だけを変えた。

1girl, solo, the girl from image 1, image 2 shows the same girl from behind, keep her face, hairstyle and outfit exactly as in image 1 and image 2,
参照1(正面)
正面向きのかなちゃんの立ち絵。サイドポニーは画面右
参照2(真後ろ)
真後ろ向きのかなちゃん。サイドポニーは画面左
向き参照期待結果生成処理
本人の右側から正面の1枚結び目は頭の奥側に隠れる結び目とシュシュが手前側に見えている270.0秒
本人の右側から正面と真後ろの2枚同上結び目が少し高く後ろ寄りになったが、手前側に見えたまま331.1秒
本人の左側から正面の1枚結び目は手前側の側頭部手前側だが、後頭部寄りの高い位置277.8秒
本人の左側から正面と真後ろの2枚同上1枚のときとほぼ同じ330.6秒
右側から・参照1枚
本人の右側から見たかなちゃん。参照は正面1枚。結び目が手前側に見える
右側から・参照2枚
本人の右側から見たかなちゃん。参照は正面と真後ろ。結び目は手前側のまま
左側から・参照1枚
本人の左側から見たかなちゃん。参照は正面1枚。結び目は後頭部寄り
左側から・参照2枚
本人の左側から見たかなちゃん。参照は正面と真後ろ。結び目は後頭部寄り

真後ろの参照を足しても、右側からの横顔は直らなかった。結び目が少し高く後ろ寄りになっただけで、手前側に見えたままだった。左側からの横顔は、参照が1枚でも2枚でもほぼ同じ絵になった。
左右どちらの横顔も後頭部の高い位置に結び目があり、同じ絵を左右反転したように見える。
Qwen-Image-Editでも、前と後ろを入れて横向きは出なかったので、こんなものだと思う。

編集1枚の生成時間

処理生成処理
Qwen-Image 2.1の編集、参照1枚、TaylorSeer+KVキャッシュ約270秒
Qwen-Image 2.1の編集、参照2枚、TaylorSeer+KVキャッシュ約331秒
Qwen-Image 2.1の編集、参照1枚、キャッシュなし、条件C677.4秒
Qwen-Image-Edit 2511(7月、8ステップの蒸留版、ComfyUI)約5分

参照を2枚にすると、参照のトークンが増える分、実計算1回が約17秒から約19秒に増えた。

関連記事

Qwen-Image 2.1を試した記事。

参考リンク