ControlNet は、画像生成のモデルに形を指定する画像を渡す仕組みです。渡す画像の 1 つが深度マップ(depth map)です。カメラからの距離を明るさで表した白黒の画像で、手前ほど明るくなります。3D の人形にポーズを付けて深度マップを書き出せば、文面(プロンプト)だけでは決まらない姿勢を指定できます。

深度マップは、ポーズを決めるための道具と思われがちです。しかし、本記事の実験では、人形が持つ脚の長さや服の厚みまで絵に写りました。下の図は、同じ文面と同じ乱数で、渡す深度マップだけを変えて鎧の女性を描いた結果です。脚の長い人形からは脚の長い人物が描かれ、厚みの無い裸の人形からは脚に貼り付く細いズボンが描かれました。

上段は深度マップ。左から、なし、標準体型で服の厚みなしの人形、標準体型で服の厚みありの人形、脚が長く頭が小さいアニメ体型の人形、それに服の厚みを付けた人形。下段は同じ文面と seed で描いた鎧の女性。人形を使った 4 枚は、人形とほぼ同じ脚の長さと太さで描かれ(右端の 1 枚は頭が大きく脚が短い)、厚みの無い人形からは細い黒のズボン、厚みのある人形からは太いズボンが描かれた

同じ文面と seed で、正面から撮った人形の深度マップだけを変えて描いた絵

上段の 4 体は、どれも同じ立ち姿です。違うのは体の比率と、服の分だけ体を膨らませたかどうかだけです。下段の絵は、ポーズだけでなく、脚の長さと太さも上段の人形に揃っています。右端の 1 枚だけは、人形より頭が大きく脚が短く描かれました。この外れは実験 1 で数えます。後の実験では、だぼだぼのカーゴパンツを文面で頼んでも、裸の人形からは細いズボンが描かれました。


本記事では、3D の人形で人物の絵を描くエンジニアに向けて、深度マップがポーズのほかに絵へ持ち込むものを手元の実験で検証します。人形の体型と服の厚みを変えて深度マップを渡し、生成した絵に描かれた体の比率は、人形と同じ方法で数えます。扱う疑問は次の通りです。

  • 人形の体型(頭身・脚の長さ)は、絵の体型にどこまで写るのか。
  • 裸の人形を使うと、文面で頼んだ服はどう変わるのか。
  • ControlNet の強さ(strength)を下げれば、体型と服は文面に任せられるのか。
  • 人形に体型と服の厚みを持たせるには、どうすれば良いのか。

ControlNet の仕組みと、骨格(OpenPose)と深度マップの違いは ControlNet のノートで説明しています。本記事はその続きです。


前提: 深度マップに入っているもの

深度マップは、画素ごとにカメラからの距離を明るさで表します。本記事の画像は、手前ほど明るく、背景を黒にしています。

この画像には、関節の位置だけでなく、体の輪郭と凹凸が入っています。肩幅、脚の長さ、太ももの太さは、そのまま白い領域の形です。骨格の画像は関節を点と棒で描くので、脚の太さを表せません。深度マップは、それも形として渡します。

ControlNet は、渡された輪郭を描く対象の外形として読みます。人物の絵なら、その外形は体型と服の外形です。つまり、ポーズのために用意した人形でも、その体型と服の厚みが意図せず絵に入ります。

体型を変える目的で、この性質を使う研究もあります。Odo(arXiv:2508.13065)は、写真に写った人物の体型を変える手法です。体型を少数の数値で表す 3D の人体モデル SMPL で目標とする体型の深度マップを作り、その深度マップを ControlNet に渡して体型だけを変えます。本記事では、意図せず入る分がどの程度かを数えます。


人形の作り方

人形は、3D の制作ツール Blender 4.5 と、人体と骨組みを作るアドオン MPFB 2.0.18 で、成人女性の体を 2 体作りました。1 体は MPFB が既定とする比率のまま、もう 1 体は脚を長くし、頭を小さくしています。Blender の頂点から測ると、既定の体は 7.6 頭身、もう 1 体は 9.6 頭身です。

黒地に白い人形の深度マップが 2 枚。左は標準体型の人形で、頭の高さごとの赤い目盛りが 7 つ余り。右は脚が長く頭が小さいアニメ体型の人形で、目盛りが 9 つ余り

2 体の人形と頭身(赤い目盛り 1 つが頭の高さ)

本記事では、左を標準体型、右をアニメ体型と呼びます。アニメ体型は、MPFB に入っている体の変形(target)から、太もも・すねを長くするものと、頭を縦・横・奥行きの方向に縮めるものを足して作りました。以下は lab/make_dolls.py の抜粋です。

# lab/make_dolls.py から抜粋。値は target の重み(0〜1)
ANIME = [("legs/upperlegs-height-incr.target.gz", 1.0), ("legs/lowerlegs-height-incr.target.gz", 1.0),
         ("head/head-scale-vert-decr.target.gz", 0.6), ("head/head-scale-horiz-decr.target.gz", 0.5),
         ("head/head-scale-depth-decr.target.gz", 0.5)]

for rel, w in ANIME:
    TargetService.load_target(body, os.path.join(TARGETS, rel), weight=w)
# 脚は下へ伸びるので、足の裏が z = 0 に戻るよう持ち上げてから位置を確定する
co, _ = world_verts(body)
body.location.z -= min(v.z for v in co)

脚を伸ばす target は、足の裏を床より下へ押し出します。持ち上げないと、2 体で足元の高さが揃わず、同じカメラで撮った時に、画面に写る立ち位置がずれます。


服の厚み

服の厚みは、体の表面を外側へ押し出して付けました。押し出す向きは、表面に垂直な外向き(法線の方向)です。MPFB の人体は、骨ごとに、その骨に付いて動く頂点の集まり(頂点グループ)を持っています。その頂点グループごとに、Blender の Displace という変形を掛けます。

標準体型の人形の深度マップが 2 枚。左は服の厚みなし。右は服の厚みありで、胴・肩・腕・脚・足が一回り太く、厚みなしの輪郭を赤い線で重ねている。頭と手は太らない

服の厚みなし(左)とあり(右)。頭と手には厚みを付けていない

押し出した量は、胸の辺りが 4.5 cm、鎖骨と上腕が 4 cm、骨盤とすねが 3.5 cm、太もも・足・腹が 3 cm、腰が 2.5 cm、前腕が 2 cm です。鎧と厚手のズボンを想定した目安で、値は lab/poses.json の _bulk にあります。以下は lab/render_depth.py で厚みを付ける部分です。

# lab/render_depth.py から抜粋。name は骨の名前(= 頂点グループの名前)、amount は m
for name, amount in POSES["_bulk"].items():
    m = body.modifiers.new("bulk_" + name, "DISPLACE")
    m.vertex_group, m.strength, m.mid_level, m.direction = name, 0.0, 0, "NORMAL"
    body.modifiers.move(body.modifiers.find(m.name), 0)  # 骨組み(Armature)より前に掛ける
    bulk[m.name] = amount  # 厚みありの回だけ strength に入れる

Displace を骨組みの変形より前に掛けているので、膨らんだ体がそのまま骨に付いて曲がります。肘や膝も、体と同じ重みで曲がります。

筆者は最初、服の代わりに筒や球を別の立体として骨に付けました。手足は IK で動かしていました。IK は、手先や足先の行き先を置くと、肘と膝の曲がりを自動で決める仕組みです。しかし、IK で動かした腕や脚では、付けた立体が体から外れました。原因は突き止めていません。頂点グループの Displace に変えてからは、どのポーズでも厚みが体に付いてきます。


生成と計測

ポーズは、正面の立ち姿と、それを 35 度回した斜め前の 2 つです。ベースモデルは、SDXL を元にしたイラスト向けのモデル WAI-illustrious-SDXL v17.0 です。ControlNet は、xinsir/controlnet-union-sdxl-1.0 に含まれる promax のモデルです。どちらも ControlNet のノートと同じで、LoRA は使っていません。

seed は、絵の元になる最初のノイズを決める乱数の種です。文面と seed が同じなら、ControlNet に渡す画像の違いだけが絵の違いになります。

項目値
解像度・ステップ数・CFG(文面に従わせる強さ)832 × 1216・28・5.5
seed20261112 / 20261113
文面(鎧)1girl, solo, adult woman, knight, silver plate armor, dark trousers, standing, full body ほか
文面(ゆったりした服)1girl, solo, adult woman, oversized grey hoodie, baggy cargo pants, white sneakers ほか
深度マップunion の depth、strength 0.7、end_percent 0.6

ステップ数は、ノイズを少しずつ取り除いて絵を作る処理を繰り返す回数です。end_percent は、ControlNet を掛け続ける範囲です。ComfyUI はこの値をノイズの大きさに直して範囲を決めるので、ステップ数の割合とは少し違います。本記事の設定では、0.6 なら 28 ステップのうち前の 17 ステップだけ深度マップを効かせ、残りは文面だけで細部を描きます。実験 3 では、strength だけを 0.4 と 1.0 に変えました。

文面の全体と、描かせたくないものを並べた打ち消しの語(ネガティブプロンプト)は lab/README.txt にあります。生成は、画像生成の処理をノードでつなぐツール ComfyUI に、lab/run_depth.py から 1 枚ずつ送り、44 枚を描きました。


比率の数え方

体の比率は、生成した絵と人形の深度マップを同じ方法で数えます。関節の位置は、人物を検出して姿勢を推定するモデルを動かすライブラリ rtmlib で取り、体の上端と下端と横幅は、背景と色が違う画素から取ります。

左は標準体型の人形の深度マップ、右はそれから描いた鎧の女性。どちらにも、頭頂・肩・股関節・膝・足首・足の裏の高さに横線を引き、股関節と膝の中間の高さに、左右の脚が占める横幅を赤い線で引いている

計測に使う線。人形の深度マップ(左)と生成した絵(右)に同じ方法で引く

上の図に引いた線から、次の 3 つの比を出します。

  • 脚の比: 股関節から足の裏まで ÷ 頭頂から足の裏まで
  • 肩より上の比: 頭頂から肩まで ÷ 頭頂から足の裏まで
  • 太ももの幅: 左右の脚にある赤い線を足した長さ ÷ 肩から足首まで

脚が長いほど脚の比が大きく、頭が大きいほど肩より上の比が大きくなります。肩より上には髪も含みます。太ももの幅だけは、頭頂と足の裏ではなく肩と足首の間で割ります。髪の盛り方や靴の描き方に左右されないためです。どれも比なので、絵の中で人物が大きく描かれても小さく描かれても、値は変わりません。

以下は lab/body_metrics.py の計算部分です。sh・hip・knee・ank は、肩・股関節・膝・足首の高さで、単位は画素、どれも左右の平均です。

# lab/body_metrics.py の measure() から抜粋
L = ank - sh                                   # 肩から足首までの縦の長さ
row = int(round((hip + knee) / 2))             # 股関節と膝の中間の高さ
mask = silhouette(img, is_depth)               # 体の画素(背景との色の差で判定)
rows = np.where(mask.any(axis=1))[0]
top, bottom = int(rows[0]), int(rows[-1])      # 体の上端(頭頂)と下端(足の裏)
H = bottom - top
return {"leg": round((bottom - hip) / H, 3), "above_shoulder": round((sh - top) / H, 3),
        "thigh_width": round(float(mask[row].sum()) / L, 3), ...}

実験 1: 人形の体型は絵の体型になる

鎧の文面で、4 体の人形 × 2 つのポーズ × 2 seed の 16 枚と、文面だけの 2 枚を描きました。下の図では、横線が人形の値、丸と四角が生成した絵の値です。各人形の左が正面、右が斜めのポーズです。

2 つの点の図。左は脚の比で、標準体型・服の厚みなしの人形は正面 0.513・斜め 0.514、生成した絵は 0.514〜0.531。アニメ体型・服の厚みなしの人形は正面 0.555・斜め 0.551、絵は 0.563〜0.570。文面だけの 2 枚は 0.528 と 0.550。右は肩より上の比で、標準体型・服の厚みなしの人形は 0.198、絵は 0.197〜0.213。アニメ体型・服の厚みなしの人形は 0.171、絵は 0.171〜0.186

脚の比と肩より上の比(横線は人形、丸は正面の絵、四角は斜めの絵)

脚の比は、標準体型の人形(服の厚みなし)で 0.513〜0.514、アニメ体型の人形で 0.551〜0.555 です。生成した絵は、それぞれ平均 0.524 と 0.567 になりました。人形どうしの差は 0.04 で、絵どうしの差も 0.04 です。肩より上の比も同じで、頭の小さい人形からは頭の小さい人物が描かれました。

人形を使った 16 枚と文面だけの 2 枚は、文面がすべて同じです。long legs のような体型の語は入れていません。体型を決めたのは人形です。文面だけで描いた 2 枚は、脚の比が 0.528 と 0.550 で、seed によって変わります。

服の厚みを付けた人形は、ポーズによって値が変わります。標準体型に厚みを付けた人形は、脚の比が正面で 0.517、斜めで 0.537 です。厚みなしの人形は正面 0.513・斜め 0.514 でほとんど変わらないので、膨らませた体の写り方が向きで変わるためと見ています。理由は検証していません。図の横線をポーズごとに分けたのは、このためです。

外れたのは、アニメ体型に服の厚みを付けた人形の、正面の 2 枚です。人形より頭が大きく描かれ、肩より上の比は 0.213 と 0.201 でした(人形は 0.158)。脚の比も 0.535 と 0.533 で、人形の 0.546 より短くなっています。同じ人形でも、斜めの 2 枚は人形に近い値です。正面だけが外れた原因は調べていません。


短足の原因になる

人形の体型が絵に出るということは、人形の体型が描きたい体型と違えば、それも絵に出るということです。筆者は以前、MPFB が既定とする体のまま、脚の長い画風の LoRA を掛けて描きました。結果は、画風は合っているのに脚の短い絵でした。

描きたい体型がある時は、ポーズを付ける前に、人形の頭身と脚の長さをその体型に合わせておく必要があります。


実験 2: 裸の人形は細い服を描かせる

実験 1 の 16 枚と、ゆったりした服の文面で描いた 8 枚で、太ももの幅を比べました。文面だけの絵は体の向きが決まらないので、幅を比べられません。この図からは外しています。

2 つの点の図。縦軸は太ももの幅。左は鎧の文面で、服の厚みなしの人形(標準体型 0.26、アニメ体型 0.22)から描いた絵は 0.23〜0.28、厚みありの人形(標準体型 0.32〜0.35、アニメ体型 0.29〜0.30)から描いた絵は 0.29〜0.36。右はゆったりした服の文面で、正面だけ。厚みなしの人形からの絵は 0.25〜0.30、厚みありは 0.33〜0.38

太ももの幅(横線は人形、丸は正面の絵、四角は斜めの絵)

鎧の文面では、絵の太ももの幅が人形の幅にほぼ一致しました。標準体型の正面では、厚みなしの人形の 0.262 に対して絵は 0.273 と 0.275、厚みありの人形の 0.347 に対して絵は 0.348 と 0.355 です。文面の dark trousers(黒いズボン)は、厚みの無い人形では脚に貼り付く黒いズボンとして描かれました。

ゆったりした服の文面は、体から離れる服を頼んだ時にどうなるかを見るために足しました。

ゆったりした服の文面で描いた 10 枚。左端の文面だけの 2 枚は、大きなグレーのパーカーと裾の溜まった太いカーゴパンツ。服の厚みなしの人形から描いた 4 枚は、丈の短いパーカーと脚の線に沿う細いカーゴパンツ。厚みありの人形から描いた 4 枚は、パーカーとズボンがやや太い

ゆったりした服の文面(左端は文面だけ、他は正面から撮った人形の深度マップを強さ 0.7 で渡した絵)

文面だけの 2 枚は、大きなパーカーと、裾の溜まった太いカーゴパンツです。厚みの無い人形を渡すと、同じ文面でもパーカーは体の大きさまで縮み、カーゴパンツは脚の線に沿う細いズボンに変わりました。厚みを付けた人形では、ズボンに少し余裕が戻ります。

見た目の差を数で確認するために、絵にタグを付けるモデル WD EVA02-Large Tagger v3 で、baggy pants(だぼだぼのズボン)が付く確率を出しました。数えたのは lab/clothes_tags.py です。

深度マップseed 20261112seed 20261113
文面だけ0.630.86
標準体型・服の厚みなし0.100.03
標準体型・服の厚みあり0.280.43
アニメ体型・服の厚みなし0.020.02
アニメ体型・服の厚みあり0.270.59

厚みの無い人形では、baggy cargo pants と書いてもタグはほぼ付きません。ControlNet から見ると、裸の人形が持つ輪郭は、体に貼り付く服を着た人物と区別が付きません。輪郭に従えば、服は体の線に沿います。

筆者が最初にこの問題に出会ったのは、裸の人形で鎧の人物を描いた時でした。鎧を頼んだのに、全身タイツのような体に貼り付く服が描かれました。服を描かせたいなら、服の厚みは文面ではなく人形に持たせる必要があります。


実験 3: 強さを下げても体型は残る

強さを下げれば、体型と服を文面に任せられるのではないか。これを検証するために、正面のポーズで強さを 0.4 と 1.0 にした 16 枚を足しました。0.7 は実験 1 の絵です。

2 つの折れ線の図。横軸は強さ 0.4、0.7、1.0。左の脚の比は、標準体型の 2 体が 0.52〜0.53、アニメ体型・服の厚みなしが 0.56〜0.57 で、強さ 0.4 でも人形の値(破線)の近くにある。アニメ体型・服の厚みありだけは 0.4 と 0.7 で人形より短く、1.0 で 0.565。右の太ももの幅は、服の厚みなしの 2 体が 0.4 で人形より太く(0.285 と 0.242)、強さを上げると人形の値(0.262 と 0.219)に近づく

強さと、脚の比・太ももの幅(正面。点は 2 seed の平均、破線は人形)

脚の比は、強さ 0.4 でも人形に近い値です。標準体型の人形(服の厚みなし)では、0.4 で 0.521、1.0 で 0.518 で、ほとんど動きません。体の大まかな比率は、弱い条件でも決まります。ただし、実験 1 で外れたアニメ体型に厚みを付けた人形は、0.4 と 0.7 でも外れたままで、1.0 では人形(0.546)を越えて 0.565 まで伸びました。

ControlNet のノートでは、突きのポーズで試したところ、強さ 0.3 で深度マップを渡しても効かず、文面だけの絵とほぼ同じになりました(1 seed の結果)。本記事の立ち姿では、0.4 で体型が人形に揃っています。ポーズも seed も違うので、0.3 と 0.4 の間に境目があるとは言えません。

太ももの幅は、強さで少し動きます。厚みの無い人形では、0.4 の絵は人形より太く(標準体型で 0.285、人形は 0.262)、強さを上げるほど人形に近づきます。差は 0.02 ほどで、脚の比ほどはっきりした傾向ではありません。弱い条件では、文面が頼んだ服の厚みが少し残るということです。

外れた 1 体を除けば、0.4 でも脚の比は人形に揃っています。強さを下げて、体型だけを文面に任せることはできません。体型と服の厚みは、人形の方で決める必要があります。


人形を用意する前に決めること

3D の人形で人物を描く時は、ポーズを付ける前に、人形の体型と服の厚みを決めておきます。どちらも、後から文面や強さで直すのは難しいからです。

体型は、描きたい人物の頭身と脚の長さに人形を合わせます。本記事の実験では、人形どうしで脚の比が違う分(0.04)が、そのまま絵どうしの差として出ています。強さを 0.4 に下げても、外れた 1 体を除いて体型は人形に揃いました。

服の厚みは、骨ごとに頂点グループの Displace で人形に付けます。裸の人形を使うと、文面で頼んだゆったりした服も、体の線に沿う細い服に変わります。

同じ人物を何枚も描く時は、体型と服の厚みを決めた人形を人物ごとに 1 つ作り、ポーズだけを変えて使い回します。髪や服の細部を揃える話は、Character Consistency のノートで扱っています。