~/blog/minimax-h3-ref2va-two-characters

MiniMax-H3 on RTX 5090 · part 6

[Benchmark] 兩張人設圖餵進去,讓小販跟書生自己演完自相矛盾:MiniMax-H3 Ref2VA 入門

cat --toc

TL;DR

Ref2VA 是 MiniMax-H3 五個模式裡唯一吃得下多張參考圖的。兩張 z-image 人設圖(小販、書生)餵進 MiniMaxH3ReferenceToVideo,243 幀跑 437 秒,出一段 10.1 秒的雙角色對話,三句簡體台詞 ASR 逐字比對 CER 6.8%(三個錯字全是同音字)。兩張參考圖沒有互相污染。最反直覺的一條:<Subject 1> 是代號不是外觀,每次出現都要帶得出誰是誰(首次寫全、後續可簡寫)。

前言

我想拍一段自相矛盾。攤販舉著盾說什麼都刺不穿,又舉起矛說什麼盾都刺得穿,然後路過的書生問一句「那用你的矛刺你的盾呢」,攤販說不出話。

這故事的麻煩在於它至少要兩個人,而且兩個人要長得穩定。同系列 Part 3 用參考圖把楊過的臉鎖回來,那是一個角色;兩個角色同框是另一回事——你得讓模型知道哪張臉配哪個人,而且不能讓兩張臉互相沾染。

H3 有一個模式專門處理這件事,叫 Ref2VA。我先前一直沒接,因為它的提示詞格式跟我們平常寫的完全不一樣。

你會拿到什麼

一段 10.1 秒、1344×768、含同步音訊的影片,兩個角色都長得像各自的人設圖,三句中文台詞也對得上嘴。跑在一張 RTX 5090 上,243 幀花 437 秒。

起點是兩張圖:

左邊是赭黃袍的胖矮小販,右邊是青灰袍的清瘦書生,兩張都是 z-image 在灰底棚拍風格下生的正面全身人設圖

這兩張是 z-image 生的,各花 45 秒。我刻意把兩人的外觀差異做得很大——胖矮赭黃對上清瘦青灰——理由等下講。

Ref2VA 是唯一吃得下多張參考圖的模式

H3 有五個模式,差別在你手上有什麼、想釘住什麼:

模式你給什麼那張圖是什麼角色
T2VA只有文字
I2VA一張圖影片的第一幀
FL2VA兩張圖第一幀 + 最後一幀
L2VA一張圖最後一幀,開頭讓模型自己推
Ref2VA多張圖<Subject N> 是身分、<Picture N> 才是某一幀

前四個模式裡,圖片一定是時間軸上的某一格。Ref2VA 把這件事拆開:標成 <Subject N> 的是「這個人長這樣」,模型自己決定放哪一格;標成 <Picture N> 的還是可以當某一幀或分鏡錨點用。我這次只用 <Subject N>

所以要兩個角色同框,只有 Ref2VA 這條路。

六段格式:每一段在回答一個問題

Ref2VA 的提示詞不是我們平常那個三段式(描述 / 環境音 / 配樂),是六段:

Ref2VA 六段格式的結構圖:subject_definitions 定義誰會出現、summary 講劇情、retention_analysis 標保留程度、detailed_description 寫逐秒發生什麼、overall_soundscape 寫環境音、non_diegetic_music 寫配樂,右側標示 Subject 1 這個標籤在五段裡都出現

看起來多,其實整個設計只有一個重點:標籤在第一段定義一次,後面五段都用它

subject_definitions 是唯一寫外觀的地方,summary 用標籤講劇情,retention_analysis 用標籤說每張圖要保留多少,detailed_description 用標籤寫鏡頭。要換角色的長相,你只改第一段。

retention_analysis 的標記是固定字串,不能自己發明。視覺類<Subject N> / <Picture N> / <Video N>)用這四個:

  • fully_preserved — 參考圖定義的東西完整保留
  • partially_preserved — 還在用,但有些特徵改掉了
  • attribute_transfer — 特徵轉移到另一個角色身上
  • weak_reference — 只保留大致的風格或氛圍

音訊類(<Audio N>)是另外一組:fully_copy / partially_copy / reference / weak_reference。我這次沒用到音訊參考。

兩個角色都標 fully_preserved

標籤不帶身分:每次出現都要帶得出誰是誰

這是整篇最反直覺的一條,而且我是先在別的地方撞到才懂的。

左邊紅框:只寫 Subject 1 這個標籤,模型自己編一個人、臉服裝年齡全部漂走。右邊綠框:標籤加上逐項外觀描述,外觀鎖住而且兩張參考圖不互相污染

你會直覺以為,既然第一段已經定義了 <Subject 1> 是誰,後面寫 <Subject 1> 站在攤子後面舉起盾 就夠了。不夠。那個標籤是代號,不是外觀。

官方範例怎麼處理的?第一個鏡頭把角色從頭到腳寫完,後面的鏡頭改用簡寫,但簡寫裡一定還帶著一個認得出來的特徵

[Shot 2] … <Subject 4> (S2), the young man in the dark-grey hoodie from Shot 1, …
[Shot 3] … <Subject 3> (S1), the blonde woman in the light-pink shirt from Shot 1.

「穿深灰連帽衫的年輕男人」「穿淺粉襯衫的金髮女人」——不是完整重述,但也不是只有標籤。官方規格只要求每個鏡頭都交代清楚角色當下的外觀,沒規定要重寫幾次。

我是在生人設圖時撞到同一條的。local-image--reference 給了參考圖,但我 prompt 裡只寫「同一個男人」加場景描述,第二張直接生出一個穿深藍襯衫、西裝褲、戴著手錶的西方男人。人、世紀、大洲全錯。把頭巾、粗麻褐衣、腰帶逐項寫進 prompt 之後才鎖住。

不管生圖還是生影片,原則都一樣:參考圖管長相,文字管誰是誰,兩邊都不能省。

節點接線:比 ImageToVideo 多兩個欄位

ComfyUI 裡走 MiniMaxH3ReferenceToVideo,不是我們平常用的 MiniMaxH3ImageToVideo

MiniMaxH3ReferenceToVideo
  required: clip · vae · audio_vae · prompt · width · height · length · ref_image_size
  optional: ref_images · ref_videos · ref_video_audios · ref_audios
  output:   ['CONDITIONING', 'LATENT']

ImageToVideo 比,多了兩個必填:

audio_vae — 接 minimax_h3_audio_vae_fp32.safetensorsImageToVideo 只吃影像那顆 VAE,這裡兩顆都要。

ref_image_size — 兩檔。match 會把參考圖縮到跟輸出畫面一樣的像素面積,max 則把短邊設成 2048px。節點的 tooltip 說 max 最能保住角色長相,但「可能慢好幾倍」,因為每一步取樣都得處理參考圖的 token。

輸出型別跟 ImageToVideo 一樣,所以取樣鏈整條沿用,不用重接。

ref_images 的型別叫 COMFY_AUTOGROW_V3,名字唬人,API 格式就是普通的連結陣列:

"ref_images": [["20", 0], ["21", 0]]   # 兩個 LoadImage 節點

參考圖要先 POST /upload/image 上傳,再用 LoadImage 節點接進去。

還有一個藏在 schema 裡的事實:lengthstep 就是 17min 是 5。H3 的幀數格點 17k+5 不是慣例,是節點自己寫死的。

照著做:從人設圖到完成影片

一、生人設圖。 灰底、正面、全身、不要道具、不要文字。外觀寫得越具體越好,那串描述等下要原封不動搬進提示詞。

二、上傳參考圖。 POST /upload/image,記下回傳的檔名。

三、寫六段提示詞。 我這次的第一段長這樣:

subject_definitions:
<Subject 1> is the market vendor in <Picture 1>, a stout middle-aged Chinese man
with a round fleshy face, a thick drooping moustache, small shrewd eyes, a ruddy
complexion, an ochre-yellow coarse robe with wide sleeves and a dark-brown collar
band, a black cloth cap, and a wide cloth belt.
<Subject 2> is the young scholar in <Picture 2>, a slender young Chinese man in his
early twenties with a narrow clean-shaven face, a high forehead, calm arched
eyebrows, a pale complexion, a pale blue-grey silk robe with a white inner collar
and a dark sash, and hair gathered in a neat topknot with a wooden pin.

detailed_description 裡逐秒寫,而且每個角色第一次出現都要把上面那串外觀再寫一次

[Shot 1] A medium-wide shot frames <Subject 1> (S1), a stout middle-aged Chinese
man with a round fleshy face, a thick drooping moustache, ... standing behind a low
wooden stall with a bronze-rimmed wooden shield propped on it.
0-3s: he lifts the shield chest-high and turns it toward the camera, chin raised,
and the loud confident middle-aged vendor (S1) says:
<d>[Chinese] 我这面盾坚固无比,什么东西都刺不穿。</d>
3-6s: he lowers the shield and thrusts the spear forward instead, and (S1)
continues: <d>[Chinese] 我这支矛锋利无比,什么盾都刺得穿。</d>
6-8s: <Subject 2>, a slender young Chinese man ... steps into frame from the left
edge, and the calm even-toned young scholar (S2) asks:
<d>[Chinese] 那用你的矛,刺你的盾,会怎么样?</d>

這段有三個重點:

台詞放 <d>[語言] 原文</d>標籤裡只有語言標籤和台詞本身,誰在說、什麼語氣、什麼音色寫在標籤外面。

說話者編號 (S1) (S2) 全片固定,同一個人從頭到尾同一個號碼。

中文台詞一律用簡體。 這條不是風格偏好:我們做過同場景同 seed 的 ASR 逐字比對,繁體 0.667、簡體 1.000,繁體會讓句子中段崩壞。

四、送出。 ref_image_size 先用 matchlength 記得踩在 17k+5 的格點上。

會擋住你的四件事

幀數不在格點上會被擋。 length 必須是 17k+5(5, 22, 39, …, 124, …, 243, …, 362)。243 幀 = 10.125 秒,362 幀 = 15.08 秒是單次生成的上限。

audio_vae 忘了接會過不了驗證。ImageToVideo 的圖改過來時最容易漏這個。

每個角色出現時都要帶外觀。 首次寫全、後續可以簡寫,但不能只剩標籤。

中文台詞記得用簡體。 繁體會讓句子中段崩壞,而且不聽不會發現。

進階:四個未知數的實測

不讀這節也不影響操作。這裡記錄的是送出前的四個未知數,以及最後的實測結果。

這台機器之前從沒跑過 MiniMaxH3ReferenceToVideo,連節點怎麼接都是當場查 /object_info 才知道的。

未知數一:兩張參考圖會不會互相污染

我以為會。所以人設圖的差異才做得那麼大——胖矮赭黃對上清瘦青灰——就是要讓污染一眼看得出來:如果書生長出小販的鬍子,或小販被削瘦,那一定看得到。

結果沒有。赭黃袍配深褐領緣、青灰袍配白內領與髮簪,兩邊都跟人設圖對得上,沒有互換的特徵。

不過這只測了一次,而且只有兩個外觀差很多的角色;兩個角色長得像時會怎樣,我還沒測。

未知數二:說話者編號有沒有對到正確角色

(S1) 講兩句、(S2) 講一句,<d> 標籤裡只放語言標籤和原文,語氣與音色寫在標籤外——這是官方格式,我們從來沒實跑過雙人版本。

完成的影片裡,兩個角色的聲音有分開,也各自對得上人。

未知數三:能不能指定一個角色什麼時候出場

這是我最沒把握的一個。前幾次逐秒指令測的都是「已經在場的人做動作」,而這次要求書生在 6-8s 才從畫面左緣進來——前六秒他不該存在。模型很可能一開始就把兩個人都畫上去。

抽幀看:

四格抽幀:1.5 秒小販獨自舉盾說話、書生不在畫面;4.5 秒小販改推矛、書生仍不在;7 秒書生已從左緣進場站在攤前;9.5 秒小販低頭別開臉、矛垂下、書生平靜看著他

1.5 秒書生不在,4.5 秒還是不在,7 秒他站在攤前了。逐秒指令能管的不只是動作,還有進場時機。

未知數四:三句簡體台詞的準確度

用既有的音訊關卡跑 ASR 逐字比對(mlx-community/whisper-large-v3-turbo,opencc 繁轉簡正規化後用 jiwer 算 CER):

ASR:  我这面盾坚固无比什么东西都刺不穿我这只毛锋利无比什么盾都刺得穿那用你的毛刺你的盾会怎么样
期望: 我这面盾坚固无比什么东西都刺不穿我这支矛锋利无比什么盾都刺得穿那用你的矛刺你的盾会怎么样

CER = 6.8%    ASR 44 字 / 期望 44 字

三個錯字:支→只矛→毛 兩次。

三個全是同音字。 都是 máo, 都是 zhī。字數一字不差、句子結構完整,沒有崩壞,也沒有重複念。 是罕用字,whisper 挑了常見的那個。

但我不會因此把這次結果算成完全正確。那個關卡的紀錄裡,CER 大於 0 的四支片確實都被標過音訊問題——但那四支的錯誤型態不一樣:有單字走音、有機械音把「探討」聽成「湯頭」、有一支的 CER 高達 75%,是因為逐字稿直接記下了重複念的內容。同音字誤判和走音算出來的 CER 可能一樣,但用拼音一比就分得出來。

順帶,whisper 自己切的段落跟我寫的切點對得上:

whisper 切的我寫的
0.0–3.0s0-3s
3.0–6.3s3-6s
6.3–10.1s6-8s(拖長兩秒)

前兩句的時間幾乎完全對上,第三句則拖長了。

成本

243 幀、兩張參考圖、ref_image_size=match437 秒

對照組:同一台機器上 124 幀單張參考圖是 136 秒。幀數翻倍加第二張參考圖的 token 要跟著每一步跑,所以貴了三倍多。

環境:RTX 5090、ComfyUI 0.34.0、torch 2.11.0+cu128、1344×768、14 步、res_multistep + simple、shift 12/3、Spectrum 開著、MiniMaxLowVRAMAttention head_chunks=16 + MiniMaxChunkFeedForward chunks=8

還沒測的

ref_image_sizemax 檔我在另一組單角色的實驗裡測過,同 seed 只慢 22%(不是 tooltip 說的「several times slower」),但身分改善看不出來。兩張以上參考圖的情況沒測——參考 token 變多的時候那個取捨可能會不一樣。

ref_videos / ref_audios 兩個欄位完全沒碰。官方規格裡 <Audio N> 可以當音色參考,那條路我還沒走過。

相關

常見問題

MiniMax-H3 的 Ref2VA 跟 I2VA 差在哪?
I2VA 只吃一張圖,而且那張圖是影片的第一幀。Ref2VA 吃多張圖,每張都是身分錨點而不是某一幀畫面——所以它才做得到「兩個角色同框,各自長得像各自的人設圖」。ComfyUI 裡是兩個不同節點:MiniMaxH3ImageToVideo 對 MiniMaxH3ReferenceToVideo。
餵了參考圖,提示詞裡還要描述角色長相嗎?
要。`<Subject 1>` 是代號不是外觀,只寫標籤模型會自己編一個人。官方範例的做法是第一個鏡頭寫全,後面的鏡頭簡寫但仍帶著一個認得出來的特徵,例如「穿深灰連帽衫的年輕男人」。
H3 生中文台詞要用繁體還是簡體?
簡體。繁體會讓句子中段崩壞——我們做過同場景同 seed 的 ASR 逐字比對,繁體 0.667、簡體 1.000。我們這次三句簡體台詞的 ASR 逐字比對 CER 是 6.8%,而且三個錯字全是同音字(矛/毛、支/只),字數一字不差。
兩張參考圖會不會互相污染?
這次沒有。我刻意把兩個角色的外觀拉到最開(胖矮赭黃袍 vs 清瘦青灰袍),就是要讓污染一眼看得出來,結果兩邊都跟各自的人設圖對得上。但這是 n=1、兩個角色、外觀差異極大的情況;角色長得像的狀況我沒測。
Ref2VA 生一段雙角色影片要多久?
一張 RTX 5090 上,243 幀(10.125 秒)配兩張參考圖跑了 437 秒。同一台機器跑 124 幀、單張參考圖時是 136 秒。這次幀數翻倍,而且每一步取樣都得處理第二張參考圖的 token,所以總時間變成三倍多。
MiniMaxH3ReferenceToVideo 的幀數可以填哪些值?
必須落在 17k+5 的格點上(5, 22, 39, …, 124, …, 243, …, 362)。這不是慣例,節點 schema 裡 length 的 step 就是 17、min 是 5。362 幀等於 15.08 秒,是單次生成的上限。

接著讀

不想錯過新文章?

訂閱我確保不漏接!

隨時一鍵退訂。