)
去年年中我接手了一個讓人又愛又恨的任務(wù)給定一張普通照片要恢復(fù)出照片里那個人和TA手中、身下、腳邊物體的三維形態(tài)。人好辦物體也還好辦難的是“人和物體疊在一起”這件事本身。你一眼能看出一個人坐在椅子上、伸手夠杯子但讓算法從一張二維圖像里同時猜出人體姿態(tài)、物體形狀、雙方接觸關(guān)系還得保證兩者不互相穿透這中間有一堆靠拍腦袋解決不了的細(xì)節(jié)。這個項目我們內(nèi)部代號叫PICO主題就是單張圖像重建HOIHuman-Object Interaction人與物體交互。在往下展開之前先說清楚這篇文章適合誰。如果你在做三維視覺、AR/VR內(nèi)容制作、具身智能仿真或者只是對單目3D重建感興趣都可以參考。我不會只貼一張效果圖完事而是把pipeline怎么設(shè)計、損失函數(shù)怎么調(diào)、數(shù)據(jù)怎么搞、坑怎么踩從頭到尾講一遍。想看理論和想看實操的人都能找到對應(yīng)段落。1. 單張圖像重建HOI到底難在哪1.1 單目重建的家底人體和物體各自都有成熟方案單目三維人體重建的路子近十年基本穩(wěn)定先用2D關(guān)鍵點檢測器從圖像里找出人體關(guān)鍵點再拿一個參數(shù)化人體模型去擬合讓渲染出來的形狀投回2D時和檢測到的2D關(guān)鍵點、人體分割掩碼對得上同時加姿態(tài)先驗防止出現(xiàn)反人類的扭曲。這套“2D觀測約束參數(shù)化模型先驗”的組合在單人、無遮擋、畫面干凈的場景下已經(jīng)相當(dāng)能打。單目物體重建也有自己的套路給定物體類別從圖像中估計類別、位姿和尺寸再套上模板網(wǎng)格或者生成式模型。甚至對于室內(nèi)場景還有一批基于預(yù)訓(xùn)練深度大模型的方法直接回歸出整個房間的三維結(jié)構(gòu)。但這兩條線通常各玩各的人體方法輸出的就是一個人體mesh物體方法輸出的是一個物體mesh。它們之間沒有關(guān)系沒有接觸沒有遮擋沒有誰支撐誰。單獨跑起來沒問題一旦要求輸出“人和物體在同一個三維空間里、以合理的交互狀態(tài)共存的場景”麻煩立刻成倍增加。1.2 交互帶來的三個麻煩遮擋、深度歧義、物理合理性人和物體一接觸三個麻煩就同時冒出來。第一個是相互遮擋。人的手臂可能被杯子擋住膝蓋可能被椅子擋住反過來物體也可能被人的身體擋掉一大半。關(guān)鍵點檢測一旦漏了局部單獨擬合人體就容易飄。更麻煩的是物體被遮擋的部分在2D圖像上完全沒有信息記住“這里缺了一塊”本身就是一個需要模型顯式處理的事情但大多數(shù)單目方法默認(rèn)輸入是完整可見的。第二個是深度歧義。同一個2D畫面里物體可以在人身前也可以在人身后。純幾何投影損失無法區(qū)分“手在杯子前面”和“手在杯子后面”。很多方法收斂到的結(jié)果從自己的相機(jī)視角看完全正確但換個視角就露餡——杯子跑到人的背后去了可圖像里明明看到杯沿壓著手臂。第三個是物理合理性。坐在椅子上的人臀部必須落在椅面上抱著的球手掌必須包著球的外表面。如果你只是分別重建人和物體出來的很可能是“人懸空坐在椅子上方”或者“手掌和球互相交叉”。所謂合理性不只是說幾何上不穿透還包括接觸部位符合日常經(jīng)驗。只要有一個手指插進(jìn)杯子里人眼一眼就能發(fā)現(xiàn)根本不需要專業(yè)訓(xùn)練。所以我一直覺得HOI重建的核心不只是“把人和物體各自重建出來”而是“把交互關(guān)系也重建出來”。后者才是真正拉開難度的地方。1.3 把交互拆成接觸、遮擋、支撐三類可計算約束交互是一個很模糊的詞落地到代碼必須把它拆成可以被數(shù)學(xué)表達(dá)的東西。我們習(xí)慣拆成三件接觸、遮擋、支撐。接觸解決“哪些幾何區(qū)域應(yīng)該貼在一起”遮擋解決“深度上誰在前誰在后”支撐可以看作接觸的一種特殊形式額外帶上力學(xué)語義——比如坐著時人的重量通過臀部傳遞到椅面。好消息是對大多數(shù)常見交互支撐性和接觸性高度相關(guān)所以很多方法并不顯式建模力學(xué)而是用接觸約束把兩者“拉近”。PICO也沿用了這個思路用一個接觸感知的約束頭預(yù)測人體mesh表面和物體mesh表面之間的接觸區(qū)域再把這部分拉近其余部分推開。遮擋則單獨用一個深度順序一致性約束處理后面我會詳細(xì)展開。先記住一個結(jié)論HOI問題的求解本質(zhì)上就是這三組約束的聯(lián)合求解。2. 為什么PICO選擇“先獨立再聯(lián)合”這條路線2.1 端到端體積回歸的誘惑與陷阱設(shè)計一個HOI重建方案第一個擺在桌面上的問題是用什么形式表達(dá)結(jié)果有兩個大方向直接回歸體素或者TSDF場或者用參數(shù)化模型擬合。端到端回歸體素看起來更酷一張圖進(jìn)去一個三維場出來中間不需要任何人類先驗。我見過不少團(tuán)隊一開始都走這條路覺得“讓模型自己學(xué)到交互模式”。但實際效果被兩個問題卡死。第一個是標(biāo)注數(shù)據(jù)量。HOI的三維真值標(biāo)注極其稀缺因為要同時標(biāo)注人體姿態(tài)、物體形狀、接觸關(guān)系成本遠(yuǎn)高于標(biāo)2D框。體素回歸是像素級預(yù)測任務(wù)沒有海量帶真值的數(shù)據(jù)根本學(xué)不動。第二個是數(shù)據(jù)分布太稀疏。交互場景變化極大椅子可以出現(xiàn)在客廳、辦公室、街頭坐著的人姿態(tài)千奇百怪抱貓、拿書、撐傘、騎車……在高維空間里這些都是非常稀疏的樣本點。直接學(xué)一個體素生成器很容易在訓(xùn)練集上過擬合測試時稍微遇到一種新交互形式就崩。參數(shù)化方案則把問題分層人體姿態(tài)用SMPL這類體模型表達(dá)物體用類別模板位姿表達(dá)。這樣雖然上限受限于模板庫但每個子問題都有成熟的2D監(jiān)督可以利用魯棒性明顯好。PICO最終采用的是“先獨立初始化、再聯(lián)合優(yōu)化”的混合思路第一步把人體和物體各自估計出來第二步在同一個優(yōu)化問題里同時改進(jìn)兩者讓接觸約束和遮擋關(guān)系參與進(jìn)來。這個選擇不是因為它看起來新穎而是因為它在實際算力、數(shù)據(jù)、穩(wěn)定性的三重約束下是性價比最高的方案。2.2 人體側(cè)參數(shù)化體模型為什么繞不開做人體重建繞不開SMPL這類參數(shù)化體模型。可以把SMPL理解成一個可微分的人體變形器輸入姿態(tài)參數(shù)θ和形狀參數(shù)β輸出一個帶頂點坐標(biāo)的人體三角網(wǎng)格。θ控制關(guān)節(jié)旋轉(zhuǎn)β控制高矮胖瘦。一個普通成年人的人體表面有幾萬個頂點直接預(yù)測每個頂點的坐標(biāo)是浪費自由度而且很容易預(yù)測出非人的形狀但通過θ和β你能把這些頂點壓縮到一個低維流形里而且這個流形上的每個點都對應(yīng)一個合理的、像人的mesh。之所以大家都用這類參數(shù)化模型另一個關(guān)鍵原因是它天然連著梯度。θ、β經(jīng)過mesh生成、頂點變換、可微渲染、投影誤差整個鏈條都是可微的梯度可以從2D像素一路傳回姿態(tài)參數(shù)。你在做接觸損失、碰撞懲罰時也是在直接優(yōu)化這個低維姿態(tài)空間比起優(yōu)化幾萬個頂點坐標(biāo)穩(wěn)定性和收斂速度都不可同日而語。PICO在人體初始化階段用的是一個多任務(wù)的人體估計器同時輸出2D關(guān)鍵點、分割掩碼和初步的θ、β。拿到初始θ、β后把mesh渲染出來和原圖做對齊這一步主要是為了給后面的聯(lián)合優(yōu)化提供一個不差的起點。這里有個經(jīng)驗初始化的質(zhì)量決定后面一切。如果初始化姿態(tài)偏了30度后面的接觸損失會非常激進(jìn)地把手臂拽向物體最后得到一個扭曲的中間值。所以寧可初始化弱一點也要先在2D層面保證大致的布局。2.3 物體側(cè)類別模板加六自由度位姿不追求形狀精細(xì)物體沒有像SMPL那樣統(tǒng)一的人體先驗因為物體形狀千差萬別。但觀察HOI常見場景會發(fā)現(xiàn)交互中出現(xiàn)的物體類別其實相對集中椅子、桌子、滑板、球、杯子、箱子、手機(jī)、書本……這就給類別級模板方案提供了空間。對圖像中的物體先做2D檢測和類別分類再在該類別的模板網(wǎng)格上估計一個全局位姿也就是旋轉(zhuǎn)矩陣R和平移向量t再加上一個統(tǒng)一的尺度縮放s。這里要潑一盆冷水物體模板的形狀不需要太精細(xì)。因為單目圖像本身丟失了大量三維細(xì)節(jié)杯子把手有沒有、椅子扶手曲線什么樣在像素層面很難恢復(fù)。PICO用的是相對簡化的模板重點放在位姿和尺度上——換句話說先把“這個物體在哪里、有多大、朝向什么方向”搞對形狀細(xì)節(jié)留給后續(xù)的網(wǎng)格細(xì)化模塊去做。這個細(xì)化模塊可以是一個獨立的形狀變形網(wǎng)絡(luò)輸入初始位姿和圖像特征輸出頂點偏移量。實驗下來對比直接預(yù)測精細(xì)形狀先粗定位再細(xì)化的方式在接觸準(zhǔn)確率上提升了接近三成因為初始位姿對了接觸約束才能生效細(xì)化才有正確的前置條件。物體尺度估計是HOI最容易翻車的一環(huán)。單目圖像里一個遠(yuǎn)處的籃球和近處的網(wǎng)球在2D上可能一樣大前者需要的平移深度和尺度完全不同。PICO的處理是引入人體作為尺度錨點利用已經(jīng)在圖像上檢出的2D人體框和估計出的身體尺寸約束物體尺度躲在一個與人類常識匹配的范圍內(nèi)。試想一個成年人身側(cè)的包被重建得比人還大一眼就不對勁。這個先驗簡單粗暴但在實際項目里幫我們擋住了大量離譜結(jié)果。2.4 遮擋推理單目里最容易被忽視的一環(huán)遮擋關(guān)系在單目里是隱式的因為圖像只記錄了從相機(jī)視角看到的可見表面。但我們可以從圖像觀測中提取線索誰在誰的輪廓邊緣上切過誰在畫面上被誰蓋住。PICO的做法是把人體和物體分別渲染出深度圖對比兩者在重疊區(qū)域的深度順序再和圖像里觀測到的遮擋模式做比對。如果圖像里人的手遮擋了杯子的下沿而當(dāng)前重建結(jié)果里杯子深度在人前面那就會被一個遮擋一致性損失糾正。這個模塊看著小實際非常關(guān)鍵。我們做過消融實驗把遮擋項從損失里拿掉之后聯(lián)合優(yōu)化比之前更容易收斂到“物體的真實位置在人的另一側(cè)”這種自洽但錯誤的解。原因是接觸損失和投影損失在優(yōu)化過程中協(xié)同作用下可能找到一個在2D上完美一致、但3D上是左右翻轉(zhuǎn)的鏡像解。有了遮擋約束模型被迫選擇與實際可見性一致的深度排序這類錯誤解基本被壓掉。3. 損失函數(shù)設(shè)計接觸、穿透、遮擋怎么平衡3.1 三大損失線數(shù)據(jù)項、先驗項、交互項聯(lián)合優(yōu)化的整體目標(biāo)可以拆成三組。第一組是數(shù)據(jù)一致項人體關(guān)鍵點的2D重投影誤差、人體掩碼誤差、物體2D框與掩碼誤差。第二組是正則項姿態(tài)先驗、形狀先驗、物體尺度的均勻先驗。第三組是交互項接觸、穿透、遮擋。一個典型的優(yōu)化目標(biāo)大致是L_total λ1*L_kp2d λ2*L_mask λ3*L_contact λ4*L_penetration λ5*L_prior用這組損失之前要做幾件事配好每個損失的權(quán)重確定優(yōu)化順序先人體后物體還是聯(lián)合優(yōu)化控制學(xué)習(xí)率防止某一項梯度太大把姿態(tài)拉飛。我們實驗里最常見的失敗是λ4穿透懲罰開太大直接把人的膝蓋懟出椅子表面代價是姿態(tài)扭曲成詭異角度。權(quán)重該怎么定我有個笨辦法先在測試圖上把每一項單獨加上觀察它對輸出mesh的影響方向和幅度。比如單獨加λ4如果輸出立刻“僵住”——所有人都不敢靠近物體——那就說明它太大了。反過來單獨加λ3如果輸出里人和物體幾乎貼成一體說明接觸權(quán)重過大出現(xiàn)了“過度擁抱”現(xiàn)象。把每個單項調(diào)到“有作用但不搶戲”的狀態(tài)再組合起來微調(diào)。這個流程大概需要一到兩周。3.2 接觸損失的具體實現(xiàn)與加權(quán)技巧接觸損失的本質(zhì)是“預(yù)測哪些點對應(yīng)該接近再把它們拉近”。PICO里有一個接觸映射預(yù)測頭輸入人體mesh頂點特征和物體mesh頂點特征輸出一個接觸置信度矩陣表示人體頂點i與物體頂點j在交互中發(fā)生接觸的概率。有了置信度接觸損失就可以寫成加權(quán)距離def contact_loss(verts_h, verts_o, confidence): # verts_h: [Vh, 3], verts_o: [Vo, 3] dist torch.cdist(verts_h, verts_o) # [Vh, Vo] # 只懲罰高置信度且距離過大的點對 mask (confidence 0.5) (dist 0.03) if mask.sum() 0: return torch.tensor(0.0, requires_gradTrue) loss (confidence * dist)[mask].mean() return loss這段代碼看著簡單但有一個非常實際的坑如果直接用所有點對的距離均值會被大量無關(guān)點對稀釋。一個杯子和一個完整的人體之間可能有幾萬個點對真正接觸的點也許只占百分之幾。如果不做篩選損失會被絕大多數(shù)無關(guān)點對的零距離淹沒訓(xùn)練時梯度信號非常弱。我們實驗里試過兩種篩選方式。一種是閾值篩選只保留置信度大于0.5的點對參與計算收斂穩(wěn)定但初期慢因為初始置信度普遍低另一種是top-k采樣每次取距離最近的前k個點對參與計算初期收斂快因為梯度集中在最有希望的候選對上。兩個方案最終效果接近但top-k在調(diào)試初期更友好——你能直觀看到“模型正在嘗試抓住哪一塊區(qū)域”。后期建議切回閾值加權(quán)精度更細(xì)膩。還有一個細(xì)節(jié)值得提接觸距離的閾值0.03米不是拍腦袋寫的它取決于mesh的頂點密度。如果人體mesh平均邊長接近2厘米那么兩個mesh表面點距離小于3厘米就可以認(rèn)為已經(jīng)“貼上了”。你要是用一個頂點稀疏的模板閾值要相應(yīng)放大否則模型永遠(yuǎn)追不上目標(biāo)損耗就一直在。3.3 穿透懲罰法向“推出去”和SDF兩種做法對比接觸是把該貼的貼上穿透則是把不該穿的推開。判斷“穿了沒有”不能只看頂點距離要看物體表面的法向。一個常見做法是對每個穿透的人體頂點計算物體表面最近點以及該點法向如果人體頂點在物體內(nèi)部就沿著法向推出去懲罰量正比于侵入深度。我推薦用符號距離函數(shù)也就是把物體表達(dá)成一個SDF場查詢?nèi)我庖粋€三維點輸出它的有符號距離正數(shù)表示在物體外部負(fù)數(shù)表示在物體內(nèi)部絕對值是到表面的距離。穿透損失就可以非常自然地寫成def penetration_loss(verts_h, sdf_query_fn): # 把人體頂點輸入物體的SDF返回負(fù)數(shù)表示內(nèi)部 sdf_vals sdf_query_fn(verts_h) # [Vh] inside sdf_vals 0 if inside.sum() 0: return torch.tensor(0.0, requires_gradTrue) loss (-sdf_vals[inside]).square().mean() return loss這里的SDF不需要真的訓(xùn)一個網(wǎng)絡(luò)實踐中用模板mesh的最近點距離近似即可把每個頂點投影到物體mesh表面用射線和法向的符號判斷頂點在表面哪一側(cè)。這個近似足夠用來懲罰典型的大尺度穿透比如整只手插入杯壁、半個臀部陷入椅面。但對于指尖嵌入表面這種毫米級穿透效果有限建議后期換用更精確的幾何碰撞庫做精修。說到底接觸和穿透天生是一對矛盾接觸項在推近穿透項在推遠(yuǎn)。如果兩個損失的權(quán)重匹配不好模型就會在“差一點點沒貼上”和“剛好貼上但深入了”之間來回震蕩。我們最后采用的策略是給穿透項加一個軟邊際只懲罰深入超過1厘米的部分1厘米以內(nèi)不管。這樣既允許輕微的、不可見的侵入又能壓制大的幾何錯誤訓(xùn)練穩(wěn)定非常多。3.4 交替優(yōu)化與聯(lián)合優(yōu)化的節(jié)奏控制這是實操里繞不開的問題。聯(lián)合優(yōu)化同時更新人體θ、β和物體R、t、s理論上更優(yōu)雅但梯度來源復(fù)雜容易陷入局部最優(yōu)尤其當(dāng)初始姿態(tài)偏差較大時接觸損失會把整個手臂拽到杯子上姿態(tài)瞬間崩壞。交替優(yōu)化則把問題拆成兩步固定物體優(yōu)化人體固定人體優(yōu)化物體循環(huán)若干輪。交替更穩(wěn)因為每一步的子問題更簡單、梯度更干凈。PICO實測下來初始化的那幾輪必須用交替優(yōu)化等姿態(tài)和物體位姿大致合理后再用聯(lián)合優(yōu)化做精調(diào)。這也是很多這類方法的通用套路如果你自己搭方案建議別一上來就端到端聯(lián)合優(yōu)化。具體節(jié)奏我們是這樣調(diào)的前20步固定物體優(yōu)化人體姿態(tài)讓人的手、臀、腳去夠物體的相應(yīng)區(qū)域接著固定人體優(yōu)化物體位姿和尺度讓物體反過來貼合人的接觸部位如此交替5輪每輪30步左右。粗調(diào)階段結(jié)束后解除固定用聯(lián)合優(yōu)化再跑100步此時接觸和穿透損失的權(quán)重可以適當(dāng)調(diào)高讓表面細(xì)節(jié)收斂。整張圖的推理時間大約在10秒內(nèi)主要花在可微渲染和迭代優(yōu)化上。很多人以為優(yōu)化步數(shù)越多越好實際上不是。單目問題本身存在模糊性優(yōu)化的步數(shù)一旦過長模型會開始在2D掩碼上過度擬合丟失姿態(tài)的合理性。我們發(fā)現(xiàn)150步以內(nèi)是甜點區(qū)超過300步后穿透深度反而上升。這個現(xiàn)象后來我們理解了投影損失在整個目標(biāo)里占比高為了讓渲染出來的輪廓和圖像完全重合模型傾向于把人體姿態(tài)擰成極端角度以補償物體遮擋帶來的信息缺失。所以適當(dāng)提前停止反而是正則化的一種手段。4. 實操過程模型訓(xùn)練、推理與評測全流程4.1 環(huán)境搭建與渲染管線選型我們實驗環(huán)境比較樸素PyTorch為基礎(chǔ)渲染用Pyrender或自研的軟光柵化器做可微渲染幾何處理用trimesh額外用了一個輕量的SDF查詢庫處理穿透計算。顯卡用的是中高端消費級卡單張圖從初始化到精調(diào)完成大概幾秒到十幾秒推理時不需要非常大的顯存。提示可微渲染是這類項目的重頭。Pyrender本身不可微我們一般接一個soft rasterizer作為前向渲染器或者用PyTorch3D的可微渲染管道。如果你只想驗證損失設(shè)計先用物理渲染器渲染出固定視角的圖單獨觀察mesh形狀是否合理再把渲染接到優(yōu)化循環(huán)里。環(huán)境配置有一個容易踩的坑可微渲染庫對CUDA版本和PyTorch版本的綁定非常嚴(yán)格版本不匹配會直接編譯失敗。我的建議是鎖定一套已經(jīng)互相驗證過的版本組合不要在有多個顯卡驅(qū)動的工作站上隨便升級。我們團(tuán)隊有同事因為在一個版本雜亂的集群上裝PyTorch3D前后折騰了兩天最后在容器環(huán)境里才跑通。這個問題雖然看起來和算法無關(guān)但實際會吃掉大量項目時間。4.2 合成數(shù)據(jù)準(zhǔn)備讓接觸標(biāo)注免費HOI的真實標(biāo)注數(shù)據(jù)非常貴。手動標(biāo)注一個三維接觸關(guān)系比標(biāo)2D框要難得多所以在項目里我們主要依賴兩條腿一部分是在公開的人-物交互數(shù)據(jù)集上訓(xùn)練另一部分是自制的渲染合成數(shù)據(jù)——把人體mesh和物體mesh擺成接觸姿態(tài)用隨機(jī)材質(zhì)、隨機(jī)光照、隨機(jī)相機(jī)角度渲染出一批圖像同時天然帶有精確的接觸標(biāo)注和深度標(biāo)注。合成數(shù)據(jù)解決標(biāo)注問題真實數(shù)據(jù)解決域差異問題。做合成數(shù)據(jù)的細(xì)節(jié)值得說一句隨機(jī)擺放時要刻意讓一部分樣本存在輕微穿透一部分樣本存在輕微分離否則模型只見過完美接觸的樣本一旦真實圖像里接觸點略微錯位接觸推理就不穩(wěn)。這其實是一個數(shù)據(jù)增強思想——人為引入一定范圍的接觸誤差讓模型學(xué)會在微擾下仍然輸出正確的接觸區(qū)域。我們最多的時候把一個交互動作渲染出200組不同視角、光照和背景的圖像模型的泛化能力提升非常明顯。在生成接觸標(biāo)注時要注意標(biāo)注的粒度。接觸不是一個二值屬性而是有區(qū)域的手的整個掌面可能接觸杯子但只有指腹接觸杯把。我們的標(biāo)注做法是給每個接觸事件記錄一個接觸面積用于訓(xùn)練接觸置信度的連續(xù)值版本而不是簡單分類。這樣接觸頭輸出的置信度矩陣就有了平滑的梯度訓(xùn)練起來比0/1標(biāo)簽穩(wěn)定得多。4.3 接觸頭的訓(xùn)練與端到端微調(diào)訓(xùn)練分成兩個階段。階段一訓(xùn)練接觸映射預(yù)測頭輸入人體mesh頂點特征和物體點云特征輸出接觸置信度用渲染合成數(shù)據(jù)的接觸標(biāo)注做監(jiān)督。階段二凍結(jié)接觸頭的部分層端到端微調(diào)整個重建模型讓接觸信息反過來影響人體姿態(tài)和物體位姿的預(yù)測。一個關(guān)鍵點是接觸頭的輸入特征不只是幾何坐標(biāo)還包含2D圖像上對應(yīng)位置的顏色特征。為什么因為單靠幾何無法區(qū)分“手在杯子前面還是后面”而顏色和紋理能提供遮擋線索——杯子遮擋手的那段邊緣在圖像上有清晰的輪廓特征。把2D特征對齊到3D頂點上再輸入接觸頭是一個很有效的小技巧。具體做法是把圖像放進(jìn)一個淺層CNN得到每像素的特征向量然后根據(jù)相機(jī)投影把人體頂點和物體頂點分別投到圖像上采樣對應(yīng)位置的特征拼接在坐標(biāo)特征后面。實測這個操作能讓接觸預(yù)測準(zhǔn)確率提升近10個百分點。訓(xùn)練時損失函數(shù)除了常規(guī)的交叉熵或MSE還要加一個空間平滑項。因為接觸區(qū)域在mesh表面是成片出現(xiàn)的如果不對置信度做平滑輸出往往像斑點一樣零散優(yōu)化時梯度雜亂。加入一個簡單的拉普拉斯平滑項后接觸區(qū)域變成連貫的塊狀后續(xù)的接觸損失和穿透懲罰也能更穩(wěn)定地作用。4.4 推理流程、參數(shù)配置與評測指標(biāo)推理時輸入一張圖流程是檢測人體和物體2D框 → 獨立估計人體姿態(tài)和物體位姿 → 接觸推理 → 聯(lián)合優(yōu)化若干輪 → 輸出人體mesh和物體mesh附帶接觸標(biāo)注。參數(shù)配置我整理了一個基線供參考人體初始化學(xué)習(xí)率1e-3聯(lián)合優(yōu)化階段降到5e-4物體位姿學(xué)習(xí)率3e-3物體尺度學(xué)習(xí)率5e-4接觸權(quán)重λ30.8穿透權(quán)重λ41.2優(yōu)化輪次交替階段5輪聯(lián)合階段100步掩碼損失用L1而不是L2邊界敏感度更高評測不能只看肉眼效果。我們常用的指標(biāo)有四個指標(biāo)計算方式說明Chamfer距離重建mesh與真值mesh頂點間雙向最近點距離均值衡量幾何精度掩碼IoU重建mesh投影到2D與真值掩碼的IoU衡量投影一致性接觸準(zhǔn)確率預(yù)測接觸對的精度/召回衡量交互語義穿透深度人體頂點侵入物體內(nèi)部的平均深度衡量物理合理性評測集要額外標(biāo)注接觸真值這個環(huán)節(jié)我們花了一周多最后發(fā)現(xiàn)最好的辦法是找一批置信度高的合成測試圖加人工抽檢真實圖兼顧自動化和可信度。人工抽檢時我們總結(jié)了一套規(guī)則先看投影掩碼是否貼合再看身體四肢是否出現(xiàn)反向折疊最后看手和物體的關(guān)系——手是懸空還是接觸接觸位置是否符合物體類別常識。這套規(guī)則后來被寫成了團(tuán)隊的評測checklist新來的同學(xué)上手也快。5. 常見問題與排查實錄5.1 關(guān)鍵點被遮擋姿態(tài)初始化崩壞把真實圖片喂進(jìn)去最常遇到的問題就是2D關(guān)鍵點檢測在遮擋處失效尤其是“人坐椅子”這種場景臀部和膝蓋有大半被椅面擋住檢測器給出的置信度低初始化姿態(tài)直接跑偏。排查思路先單獨跑人體姿態(tài)估計把2D關(guān)鍵點和投影后的3D骨架可視化確認(rèn)哪幾個關(guān)節(jié)置信度低。如果是遮擋導(dǎo)致的可以調(diào)整優(yōu)化權(quán)重把低置信度關(guān)鍵點的監(jiān)督權(quán)重降下來同時增加姿態(tài)先驗的權(quán)重讓模型更多依賴先驗而不是被帶偏的2D觀測。如果實在不行有個土辦法用檢測器輸出多個候選姿態(tài)假設(shè)每個假設(shè)各自走一遍聯(lián)合優(yōu)化最后選投影誤差最小的結(jié)果。有點費算力但確實能救回一些難例。我們在PICO里還加了一個針對性的技巧用一個輕量的遮擋分割網(wǎng)絡(luò)先標(biāo)出圖像里“什么東西擋住了人體的哪部分”然后在優(yōu)化時把被遮擋區(qū)域的掩碼損失權(quán)重直接降到接近0。這么做的好處是模型不會為了擬合被遮擋部分的輪廓而扭曲姿態(tài)它只需要保證可見部分的投影一致其余由姿態(tài)先驗和接觸約束兜底。5.2 物體尺度來回跳越調(diào)越離譜聯(lián)合優(yōu)化時物體尺度s和平移t經(jīng)常互相補償尺度變大一點平移朝遠(yuǎn)處挪一點投影出來的2D大小不變但3D結(jié)果變成一個“遠(yuǎn)處的大物體”。這是單目尺度-深度耦合的典型癥狀也叫scale-depth ambiguity。我們的解決辦法是加了一個人體尺度錨定項。具體做法根據(jù)當(dāng)前重建的人體mesh計算出關(guān)鍵的尺寸比如肩寬和手臂長作為參照系。然后把物體的尺度限制在與參照系合理匹配的區(qū)間內(nèi)。比如一個人肩膀?qū)?0厘米他手邊的物體橫跨尺寸超過1米那基本可以判定尺度預(yù)測錯了直接把這個樣本判為低置信度或者把物體尺度拉回區(qū)間內(nèi)。再不行就在優(yōu)化里固定物體尺度只優(yōu)化位姿等人體姿態(tài)穩(wěn)定后再放開尺度。這種“放棄一部分自由度換取穩(wěn)定性”的思路在單目任務(wù)里是值得反復(fù)使用的。還有一個容易忽略的點物體檢測的2D框不準(zhǔn)會直接傳導(dǎo)到尺度估計。我們曾經(jīng)在測試集上一半物體的尺度過大最后排查發(fā)現(xiàn)是檢測器對部分遮擋物體的邊界框偏向?qū)捤蓪?dǎo)致初始尺度偏大。后來在初始化時對物體框做了一次邊緣緊縮——比例大約3%——尺度誤差立刻降下來。這種問題不看數(shù)據(jù)分布光調(diào)損失是無法發(fā)現(xiàn)的。5.3 接觸部位張冠李戴手里沒抓到該抓的地方眼看接觸項在起作用人和物體也不再穿透但仔細(xì)看手沒放在杯柄上而是放在杯口上——接觸置信度矩陣給出的語義區(qū)域完全錯位。這通常是接觸頭的訓(xùn)練數(shù)據(jù)有問題合成數(shù)據(jù)里“手抓杯子”的接觸區(qū)域太單一模型只記住了抓杯口這一類模式。對策是擴(kuò)大合成數(shù)據(jù)的接觸多樣性同一個杯子讓手從側(cè)面、上方、斜下方都去抓一遍同一個姿態(tài)換不同形狀的物體。我們還試過在接觸置信度上加入類別約束比如屬于“握柄類”的物體優(yōu)先把手部頂點映射到手接觸區(qū)域。效果有但有限核心還是數(shù)據(jù)多樣性。調(diào)試這類問題最快的方法是直接把接觸置信度矩陣可視化按置信度值給mesh頂點上色看預(yù)測的“熱點”到底分布在哪些區(qū)域。有一次我們發(fā)現(xiàn)熱點集中在大腿前側(cè)而真值接觸區(qū)在臀部下方原因就出在合成數(shù)據(jù)里坐姿樣本生成時人體和椅面的接觸采樣是從mesh全局最近點出發(fā)的最近點落在大腿前側(cè)而不是臀部。修正采樣邏輯之后這個問題就消失了?!翱梢暬佑|熱點”這一招我們后來一直在用凡是接觸相關(guān)的bug先上色看圖再找原因。5.4 損失一直降但渲染出來還是糊成一團(tuán)還有一種情況是損失一直在降但mesh投影出去和原圖對不上說明優(yōu)化陷入了某個自洽但不合觀測的局部最優(yōu)。這時候先檢查渲染的相機(jī)參數(shù)和檢測框是否對得上再檢查掩碼損失是否真的在起作用。很多次問題出在一個小地方掩碼損失用的是L2對邊緣極其不敏感。一個只差兩個像素的輪廓和一個完全對不上的輪廓L2值差別不大梯度也差不多模型學(xué)不到“往邊上挪一點”的信號。換成一個邊緣加權(quán)的距離項或者干脆只用輪廓邊界像素計算距離效果會好非常多。另外相機(jī)內(nèi)參錯誤是一個隱蔽的大坑。如果你的初始化代碼里把焦距估計錯了10%投影的物體輪廓會比實際偏大或偏小聯(lián)合優(yōu)化會努力用“錯誤尺度錯誤深度”去補償最后收斂到一個數(shù)值上合理但視覺上完全不對的結(jié)果。所以每次跑新數(shù)據(jù)集先把一張已知真實深度分布的測試圖走完整個流程確認(rèn)相機(jī)內(nèi)參對齊再開始大規(guī)模評測。這個檢查只需要幾分鐘能省掉后面一整天的返工。5.5 一個容易被忽視的工程問題mesh頂點數(shù)量不一致接觸損失和穿透損失都要計算人體mesh頂點與物體mesh頂點之間的關(guān)系而人體模板和物體模板的頂點數(shù)、面片密度往往差異巨大。比如人體模板有2萬多個頂點一個簡化的椅子模板可能只有2000個頂點。直接計算點對距離時人體側(cè)的點密度遠(yuǎn)高于物體側(cè)導(dǎo)致接觸區(qū)域“人體一個點對物體多個點”的情況梯度傳播時會偏向人體側(cè)。我們的解決辦法是對物體mesh做一次自適應(yīng)細(xì)分讓兩個mesh在接觸區(qū)域的頂點密度大致匹配。更規(guī)范的做法是用最近鄰雙向匹配并給每個點對乘上一個面積加權(quán)因子。如果你只是自己搭一個快速demo最簡單的辦法是先把兩個mesh都采樣成相同數(shù)量的點云再做后續(xù)計算。這個細(xì)節(jié)不解決接觸損失會一直抖動而且越訓(xùn)越差表現(xiàn)非常迷惑。6. 一點個人體會跑這種項目最大的體會是真正耗時的不是網(wǎng)絡(luò)結(jié)構(gòu)而是數(shù)據(jù)和調(diào)試。HOI的合成數(shù)據(jù)生成看起來簡單實際涉及接觸采樣、姿態(tài)合理過濾、遮擋多樣性、域隨機(jī)化每一步都要反復(fù)試損失函數(shù)那幾項權(quán)重更是玄學(xué)我們調(diào)了一個多月才找到相對穩(wěn)的組合。如果讓我給后來人一個建議那就是別急著上完整方案先把最小閉環(huán)跑通單張簡單圖一個固定類別物體一個接觸損失一把椅子先讓它在不穿透的前提下把接觸做對。把這一步做實了再往復(fù)雜場景、多類別、多樣交互擴(kuò)展路會順很多。這個項目我們后續(xù)還在往視頻序列上延伸加上時間一致性之后很多單目里的深度歧義會被自然消解那是另一個有趣的故事了。