BVH簡介
BVH是BioVision公司推出的一種人體動作捕捉文件格式。這種文件以節點為核心元素,記錄連續數幀內人體骨架的運動。
BVH=?
研究一個東西的時候我比較喜歡先研究它的名字。BVH可以認為是BioVision Hierarchy的縮寫,因為這類文件對節點的組織是按照樹形結構來的,也就是層次化(hierarchical)的。關於這個名字還有另一種可能的解釋:如果你去查詢Blender的文檔,對BVH的介紹則將其等同於BioVision Motion Capture. 我傾向於前者,因為hierarchy這個詞其實揭示了BVH的本質。
把這類文件叫做BVH有一個最單純最直接的原因,那就是.bvh是所有這類文件的統一后綴|·ω·)
文件結構
文件分為兩大部分,層級結構和運動信息。下面是BVH的一個示例:
HIERARCHY
ROOT Hips
{
OFFSET 0.00 0.00 0.00
CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
JOINT Chest
{
OFFSET 0.00 5.21 0.00
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT Neck
{
OFFSET 0.00 18.65 0.00
CHANNELS 3 Zrotation Xrotation Yrotation
JOINT Head
{
OFFSET 0.00 5.45 0.00
CHANNELS 3 Zrotation Xrotation Yrotation
End Site
{
OFFSET 0.00 3.87 0.00
}
}
}
JOINT LeftCollar
{
...
}
...
}
MOTION
Frames: 2
Frame Time: 0.033333
8.03 35.01 88.36 -3.41 14.78 -164.35 13.09 40.30 -24.60 7.88 43.80 0.00 -3.61 -41.45 5.82 10.08 0.00 10.21 97.95 -23.53 -2.14 -101.86 -80.77 -98.91 0.69 0.03 0.00 -14.04 0.00 -10.50 -85.52 -13.72 -102.93 61.91 -61.18 65.18 -1.57 0.69 0.02 15.00 22.78 -5.92 14.93 49.99 6.60 0.00 -1.14 0.00 -16.58 -10.51 -3.11 15.38 52.66 -21.80 0.00 -23.95 0.00
7.81 35.10 86.47 -3.78 12.94 -166.97 12.64 42.57 -22.34 7.67 43.61 0.00 -4.23 -41.41 4.89 19.10 0.00 4.16 93.12 -9.69 -9.43 132.67 -81.86 136.80 0.70 0.37 0.00 -8.62 0.00 -21.82 -87.31 -27.57 -100.09 56.17 -61.56 58.72 -1.63 0.95 0.03 13.16 15.44 -3.56 7.97 59.29 4.97 0.00 1.64 0.00 -17.18 -10.02 -3.08 13.56 53.38 -18.07 0.00 -25.93 0.00
該文件是刪減過的,為了顯示清楚格式。>點我< 看源文件。
層級結構
首先HIERARCHY標記了層級結構部分的開始。
可以跟着編譯器的思路來理解這一部分:
- 現在開始讀這個文件啦,首先讀到的是
ROOT,這表示第一個節點是根節點。第一個節點總是根節點; - 往下讀,讀到了
Hips這個詞,結合上一條的信息,我們就知道了,根節點的名字是'Hips'。對於人體骨架來說,根節點一般都取在Hips也就是臀部。 - 接着,出現了
{,左括號開始了,必然有右括號}與之閉合,而括號之間的部分,既有節點,也有信息。我們可以理解為括號括起來的部分從屬於剛剛讀到的節點,這里是根節點Hips。 - 下面讀到一個關鍵字
OFFSET。接下來會有三個數字,這里是0.00 0.00 0.00這表示該節點相對於父節點的偏移,也可以理解為從父節點到該節點連線所表征的向量。根節點沒有父節點,所以這里是0. - 再讀,讀到
CHANNELS這個關鍵詞,那么這一行剩下的部分就是幾個表示自由度的關鍵字,XrotationYrotationZrotation表示了三個旋轉自由度,XpositionYpositionZposition表示三個平移自由度。這一行表示剛才的根節點的自由度信息。- 一般來說,根節點有全部的6個自由度,而其余節點只有3個旋轉自由度。
- 這些自由度的具體含義會放在后一節說明。
- 接着往下,讀到了
JOINT這個關鍵字,這意味着這一行表示的是一個節點,后面的一個詞Chest則是這個節點的名字。這個節點和根節點唯一的區別就是標記是JOINT而不是ROOT。 - 下面又出現了
{,同樣的,括號內的信息和子節點都從屬於該節點。 - 直到
End Site出現前的部分都不需要贅述。End Site是一個無名節點,表示樹形結構到這里就是葉節點啦,后面再也沒有子節點了。End Site沒有子節點,所有也就沒有自由度信息,只有相對父節點的偏移信息。 - 接下來讀到了本文件第一個
},表示一個節點的所有范圍終結。 - 下面再出現
JOINT標記的節點時,我們回去找它在誰的括號內,它就是誰的子節點,以此類推,直到MOTION結束,因為這是下一部分開始的標志。
運動信息
緊承前節,看到了MOTION這個關鍵字,就代表着運動信息的部分開始了。
這一部分的結構要簡單的多,就不像上一節那樣介紹了:
- 第二行
Frames: 2表示幀數,這里是2幀;- 這里的幀數必須與后面的幀數據行數相等,因為這個數字決定了要讀取多少行,如果少了會造成數據浪費,多了就會出bug
- 第三行
Frame Time: 0.033333表示每一幀的持續時間,這里是\(\frac{1}{30}\text{s}\),也就是這個文件是30FPS - 后面有n行運動數據,每行為一幀。這些信息按照
HIERARCHY部分的自由度出現的順序一一對應。其中角度的單位是度(°)
運動計算
這一部分主要解決的問題是:如何通過BVH復現運動情況。這種旋轉還是很抽象,要轉化就要轉化成人最方便理解的形式,那就是三維坐標。
自由度理解
首先要明確的是,這些參數的含義究竟是什么。
如果在根節點ROOT建立全局坐標系,把OFFSET都作為相對這個全局坐標系的向量,那么僅憑HIERARCHY的信息是可以畫出一個基本的骨架的。把這個骨架圖起個名字叫\(G_0\),后面還要再提到它。這種生成\(G_0\)的方法理論上沒有問題,但是不能這么理解。BVH中每個節點都有自己的局部坐標系,所有的變換都是在局部坐標系完成的。只不過在\(G_0\)中,這些坐標系全部平行,局部坐標就等於全局坐標。
注意,雖然這里我們畫出了\(G_0\),但這並不是初始的那一幀,只是一個基本的參考。運動信息部分中的每一幀都可以由這一幀的信息和\(G_0\)唯一地生成,沒有記憶性,也就是說每一幀都是獨立的。
BVH的每個節點都有3個或6個自由度,這些自由度都是相對於初始坐標系(暨全局坐標系)而言的。平移自由度很好理解,就是沿着三個軸的偏移量;而旋轉自由度則需要牽扯到3個點:當前節點O、子節點C、父節點P。如下圖
這里要第一次強調hierarchy的含義了:每個節點的旋轉,在自己的坐標系旋轉的同時,帶動所有子節點和相關骨骼的旋轉。可以理解為按照層級從根到葉先后執行旋轉,一個節點的局部坐標在旋轉之前,始終和其父節點的局部坐標保持平行。
按照這樣一個過程,我們來理解一下旋轉自由度的含義。在節點\(O\)旋轉前,其局部坐標系\(Oxyz\)是和\(Pxyz\)完全平行的。三個自由度的含義是先后繞局部的\(Oz\), \(Ox\), \(Oy\)旋轉的角度(這里以ZXY順序為例)。注意,這里的單位是度(°).
旋轉的結果不會體現在節點\(O\)上,而是體現在\(C\)及其后續節點的位移,即向量\(\vec{OC}\)的旋轉。
遞歸計算
這一部分主要討論如何利用BVH中給出的信息,計算出每一幀所有節點的三維坐標序列。其核心是坐標變換的遞歸。
這里要再一次強調hierarchy這個詞。遞歸計算的核心就是對層級結構的利用。
我們知道,如果一個坐標系\(p\)旋轉之后得到了坐標系\(q\),這一旋轉過程的旋轉矩陣可以表示為\(R_{pq}\),而在這個空間內有一定點\(A\),它在兩個坐標系下的坐標分別是\(A_p\)和\(A_q\),那么有如下關系成立:
這就是坐標變換公式。
這里就有問題了。BVH中的旋轉是向量在旋轉,這個公式中\(A\)點根本就是一個定點,怎么能用來計算呢?
其實在前一節,我們賦予了每個點一個單獨的局部坐標系,並規定這個坐標系內,直接后繼的坐標是不變的。這就是為了將坐標變換和向量旋轉等同。可以分幾步來理解這個計算過程(以父節點的坐標系為基准):
-
轉之前的橙色坐標系(\(\vec{OC}\)所在的坐標系)記為\(p\), 轉之后的紅色坐標系(\(\vec{OC^\prime}\))記為\(q\)。
-
旋轉前,所有坐標系都與全局坐標系等同,這就使得\(\vec{OC^\prime_p}\)實質上是\(\vec{OC^\prime}\)在全局坐標系下的坐標,也就是我們直接可以從坐標序列得出的\(V_g\)。旋轉后的坐標系下,同一個向量\(\vec{OC^\prime}\)在\(q\)下的坐標實質上是基准幀內該向量的坐標,即\(V\)。
-
於是有了
\[\boldsymbol{V}_g=R\boldsymbol{V} \]
我們再來理解遞歸的過程:在基准幀,當前節點以其父節點為基准執行旋轉,得到向量\(R_n\boldsymbol{V}\),這一向量的坐標是相對其父節點而言的;那么以父節點作為待處理的節點,繼續執行這個過程,有了\(R_{n-1}R_n\boldsymbol{V}\),以此類推。於是,某一節點的局部坐標到全局坐標的轉化矩陣就可以寫為
為了不讓這個式子看起來太復雜,我沒在公式里顯式指明旋轉矩陣所在節點的從屬關系。但是他們之間的關系應為:\(R_{i+1}\)是\(R_i\)的子節點。
有了這個關系,\(R_n\)就可以遞歸求解。即,當我們知道\(R_n\)節點所有祖先節點的旋轉矩陣,他們按照上面的順序乘在一起得到的結果是\(R_m\), 則成立以下式子:
式中只有\(R_n\)是未知的。
The End
最近在學習人體的動作捕捉,BVH的旋轉還是挺繞的,但是這種層級結構確實是一種解決人體運動描述的很有用的思想,還是值得深入解讀的。
當然,文章內容是基於我個人理解,如有錯誤,萬望指正:)
