作者實現的 ngp_pl
程式碼還存在的兩個問題:
- 收斂後的取樣點數比NGP多(這導致計算量變大,渲染幀率下降);
- 有些場景會失敗;
一、資料準備
程式碼都在 dataset
資料夾下面。
作者支援大部分演算法的資料集格式,包括:
- NSVF:講解的時候展示了
nsvf.py
這個檔案,但是自己沒找到它 - NeRF++
- COLMAP data
- RTMV: NeRF專用資料集,600個場景,但是因為下載賊慢,導致很少人用;
補充:建立自己的資料集可以參考 _synth_nerf_dataset_creator 這個專案。
補充說明
第一:作者預設所有相機的內參一致,如果需要支援多個相機,需要自己在資料集讀取程式碼中新增多個K。
第二:座標系的轉換。作者把NeRF的座標系改成了常見的相機座標系(都是右手系,只不過z軸的方向不同,如下圖左側所示)。
第三:範圍的修改。以NSVF的合成資料集(SYN)為例,合成資料集是指定了 bounding box 的範圍,但是物體不一定處於座標系正中心,所以需要使用 shift 操作使之置於原點,此外還需要使用 scale 操作把範圍值歸一化到 \([-0.5, 0.5]\) 內。如下圖所示:
但是真實場景(real)中,box 的範圍其實是不確定的,此時需要調整 scale 進行試錯(trial and error)達到最好的效果(也不一定能訓練成功,太大的場景需要借鑑 MipNeRF360 的 contraction 想法,把無限遠的場景“投影”到比較近的地方)。
第四:取樣策略。要麼從所有圖片中取取樣點,要麼只從一幅圖片中取,這兩種和NeRF一脈相承,分別是下面程式碼中的 all_images
和 same_image
兩種策略。
還有一種做法是:從一幅圖片中取某個小區域,因為所有點都在小區域內,所以它們之間是存在某種聯絡的,從而引入 perceptual similarity,這樣會對最後的 Loss 計算提供幫助(因為現階段所有的 Loss 計算都是二範數的形式,只是據說採取這種做法效果可能會更好,作者沒有實現這種做法)。
二、模型構造
(一)訓練
訓練部分程式碼在 models/networks.py
網路結構:總共可以分成三大部分。xyz 輸入到 HashGrid 中得到 16 維特徵向量,它的第一個位元使用 \(e^x\) 作為 \(\sigma\) ;方向向量透過Spherical harmonics(一系列的正餘弦計算),生成16維向量,二者 concat 後輸入神經網路,得到最後的 RGB 值。
如果得到/實時更新 occupancy grid ?
以訓練是否超過256步為界:前256步階段會取得所有的格子 get_all_cells
,從cell中隨機取一個點作為 xyz 投入上圖中,計算其 \(\sigma\) 值,以此判斷該 cell 為 0 or 1(threshold,預設值是 0.01);超過256步後,減少取樣的程度,uniform 是隨機取樣(取所有格子的 \(\frac{1}{4}\) 出來),或者只從有東西的 cell 取 \(\frac{1}{4}\) 出來。
(二)渲染
程式碼在 models/rendering.py
如何進行渲染?volume Rendering
首先計算射線和 box 的交點(有現成的方法,速度其實很快)。根據訓練和測試兩個階段的任務需求不同,具體做法也不同。
(1)render_ray_train
對於 syn 合成資料,box 的邊長是 1,步長是 \(\frac{\sqrt{3}}{1024}\)(對角線長度);對於 real 真實場景,離光心越遠,步長越大,這樣既加快計算,又把關注點放在最近的物體中,範圍是 \(\frac{\sqrt{3}}{1024}\) 到 \(\frac{1}{256}\)。並且,每走一步要根據 occupancy field 是否有東西,決定是否放置取樣點。
(2)render_ray_test
判斷射線是否和 box 有交點,逐步向前 marching,直到遇到 occupancy filed 中有東西的交點,如果沒有交點那麼說明這條射線不用渲染,就可以把計算資源分配給其他射線;其他射線便繼續往前 marching 重複這個過程,直到走完。並且,取樣點數是逐步分配、增加的。
如果 transimitance 已經很小了(幾乎所有的顏色都被決定了,沒必要再繼續了),就可以停止渲染。early ray termination
三、定義Loss
作者在 MSE 的基礎上(渲染的 RGB 和原始 RGB 的差值的平方),還引入了:
- opacity loss: 讓 opacity 的值要麼是 0 要麼是 1,避免出現小數值,而且還能減少漂浮物。這樣做是忽略了透明的物體,所以會對透明物體的效果不太好(Depth NeRF 針對透明物體做了最佳化);
- distortion Loss:MipNeRF360 中提出的新演算法。原始NeRF會更傾向於所有的都是半透明的,累加在一起成為不透明,導致有很多沒有用的“雲”(漂浮物);當加了這個Loss後,最後的結果更傾向於只有表面一層,而沒有後面那些半透明的雲,從而減少取樣、加快計算。