【Pytorch3D】介紹

帽捲
Maochinn
Published in
18 min readFeb 1, 2021

--

Pytorch3D是用來幫助做一些3D資料的pytorch,簡單來說就是讓我們可以對3D資料做一些常見的operation。那為甚麼FAIR(Facebook AI Research)要特別做這個東西呢,舉例來說,我們對2D的資料,例如圖片做convolution非常直覺,但是如果要對3D的資料,例如一個mesh要做convolution呢?。另外如果在取batch的時候mesh又要怎麼取呢?Pytorch3D就是對於這些3D operator提出一個解決方案。

官網上面有一系列的教學可以直接在Google Colab上面執行,還有Doc可以看,那這邊我就會依照我認為比較有趣的部分展開來講。

在開始講之前,可以一提的是NV在2019就發布了一個有部分類似的工具kaolin,後面會提到為什麼我會選擇Pytorch3D。

我要講的東西大致分為兩個部分,Data跟renderer,renderer的部分我會著墨比較多。在講之前附上Pytorch3D的Paper。因為蠻多東西都是在Paper裡面的。

首先,我們可以先看看abstract

We believe that some of this disparity is due to the engineering challenges involved in 3D deep learning, such as efficiently processing heterogeneous data and reframing graphics operations to be differentiable.

從這段可以知道他要解決的問題就是heterogeneous data(異質資料),跟differentiable renderer(可微分渲染器)。所以這邊就拆成兩個部分介紹。

Data

網站上其實寫的很清楚,heterogeneous data的問題是每個batch裡面的mesh的大小都不一樣,簡單來說每個mesh的vertex或是face的數量會不一樣,那heterogeneous data的相反就是homogeneous data(同質資料),總之,heterogeneous data會造成平行化之類的問題(batch的好處以及限制歡迎補充),總之,Pytorch3D用不同的表示來解決這個問題。

List, Padded, Packed

針對不同的Operator來使用不一樣的表示方式,例如我們需要每個mesh的vertex數量一致,那我們就使用padded,也就是把多出來的部分都補0。

Data部分我還沒有研究很多,歡迎補充。

Renderer

為什麼Pytorch要自己重寫一次rendering pipeline呢?這邊就要提到前面所說的DR(Differentiable Renderer),那甚麼是DR?,顧名思義就是可以微分的renderer,那這邊兩個問題就是,為什麼traditional renderer不能微分,跟微分可以拿來幹嘛?

Differentiable Renderer

簡單來說,如果可以微分,那我們就可以從pixel做backward pass,也就是back propagation,如果對這些名詞敏感的人應該就會知道這麼做的重要性,我們可以透過在render出來的2D image計算loss,進而計算場景的gradient,例如vertex的位置,face的屬性。

另外,為甚麼renderer通常不能微分,這個我們可以回想一下微積分的對可微的條件,其中之一就是連續,但是我們都知道我們的render出來的圖處處都充滿了不連續,究其原因就是在rasterize的時候我們會用depth-testing,而face的邊緣自然也會產生不連續,所以整個render的過程是有部分不可微的。

那當然,在目前有很多跟mesh, pointcloud等3D data有關的NN,但是他們提到了一點。

Though sucessful, these methods suffer from a common shortcoming: they process 2D snapshots and ignore the true 3D nature of the world.

簡單來說就是它們仍然是在2D上面操作,並沒有真正的直接與3D連結,所以DR算是2D與3D的bridge,其他DR相關的操作這邊就不提了,之後會在開一篇來講。

A differentiable renderer can also propagate gradients backward from rendered images to scene information , allowing rendering to be embedded into deep learning pipelines.

Rendering Overview

DR這件事情其實在很久以前就有人在做,包含OpenDR、NMR等等,但是每篇paper都會提出自己的架構,這當然就是因為目前還沒有一個完美的答案,但這也就造成大量的人力浪費在定義一些基本架構,所以2019 NV release了Kaolin,2020 FAIR release了 Pytorch3D,所以目的就是希望能夠省下人力,當然也是因為有人提出了相對優秀的解決方案,但是後者對於DR有比較好的表現(我覺得啦),所以我就選擇Pytorch3D來介紹(當然還有相對友善的文件跟教學)。

PyTorch3D rendering pipeline

那為什麼選擇用Pytorch當作基底呢?因為Pytorch提供了auto differential(自動微分),簡單來說就是需要計算gradient的data每進行一次operate就會被Pytorch紀錄在一個operator tree裡頭,直到要跑backward()的時候就可以快速依照所記錄的操作計算gradient,反過來說,每個operator都要可微,如果不可微則要人工定義backward。

所以選擇Pytorch就是因為大部分的operate(transformation, blending...)都是一些加減乘除或是矩陣的運算,這些在pytorch已經都被寫好了,我們只需要關注一些沒辦法微分的地方,也就是Rasterization,所以你可以看到流程圖在rasterization是用CUDA或是CPU下去做的,因為他們會自己定義backward。

這是另一張流程圖,寫的比較詳細,我們就一個一個來講吧。

首先renderer被分成兩塊,一個是rasterizer一個是shader,前者又分成兩步驟,transformation跟rasterize,後者則是shading,這邊如果有學過OpenGL就知道transformation就類似於vertex shader要做的事,rasterize則是OpenGL幫我們做掉的事,也就是不可微的部分,最後shading則是fragment shader做的事。而Pytorch3D則自己把整段流程刻出來。

Transformation

這個部分概念跟OpenGL非常像,只是坐標系的定義有些不一樣,但是應該可以理解,總之,transformation就是一堆矩陣運算,這個是Pytorch本身就支援可微的部分,所以這邊就不用多著墨。

Shading

Problems with differentiability in rendering

在講Rasterization前,我們要先提到Shading這邊就講講Pytorch3D範例使用的方法,就是採用Soft Rasterizer的方法。

簡單來說就是把邊緣blur(模糊),然後blend(混合),而貢獻就在於他用可微的數學式來時做出這樣的forward,並且可以透過參數的調整做出於我們傳統render的效果。

We take a different perspective that the rasterization can be viewed as binary masking that is determined by the relative positions between the pixels and triangles, while z-buffering merges the rasterization results F in a pixel-wise one-hot manner based on the relative depths of triangles.

簡單來說就是利用sigmoid來控制邊緣的模糊程度。

然後用softmax來做blend。

回到Pytorch3D,我們可以看看他提供的rendering method。

總之就是每個pixel會被好幾個face影響,Shilhouette blending就是用pixel中心到face的距離來決定probability,然後再算成alpha。這邊可以找到Pytorch3D的實作。

Softmax blending就是上述提到的方法,利用深度來計算權重,距離相機躍進權重越高,然後再用權重來做softmax得到pixel的值,

Rasterization

最後我們再回頭來看看Rasterization,因為我們知道shading每個pixel要使用多個face的資訊才能計算,所以再官網上可以看到有rasterizer的輸出。

其中pix_dists就是pixel中心到face的距離,也就是要拿來計算模糊程度的。

Our rasterizer returns Fragment data about the K nearest faces to each pixel:
face ID, barycentric coordinates of the pixel in the face, and (signed) pixel-to-face distances along

我們可以回到最前頭的overview會發現他只使用K個face來計算這個pixel,當然,這個K是可以自己設定的,相對來說,Soft Rasterizer則會使用所有的face來計算pixel,另一個角度來說,Soft Rasterizer的pixel會對所有face產生影響(Paper其實是說backward的時候可以選擇前K個計算gradient),而Pytorch3D則只會對前K個產生影響,想當然,這就會造成效能差異,但也會造成Pytorch3D在這邊的不連續(可以想像是一個有K大小的depth-testing),所以你可以發現Pytorch3D這個部分用CUDA或是C++來人工實作這個部分。

Differentiable point cloud renderer

這邊可以補充如果要做point cloud的rendering大體上跟mesh一樣,但是沒有了face,而是把每個point當成圓在screen space然後再進行blending。

Each point is splatted to a circular region in screen-space whose opacity decreases away from the region’s center. The value of each pixel is computed by blending information for the K-nearest points in the z-axis whose splatted regions overlap the pixel.

那blending又提供兩個方法,當然也都是可微的。

Alpha-compositing uses the depth ordering of points so that nearer points contribute more, while Norm ignores the depth order.

Alpha-compositing這個就是傳統的方法,越近的影響越大,Norm則是直接平均。

3D operators

這個看到paper有寫我就順編寫一下好了。Pytorch3D提供一些寫好的3D operators,當然這些operator是可以微分的。

Chamfer loss

Chamfer loss is a common metric that quantifies agreement between point clouds P and Q

簡單來說就是對兩個數量不一致的點雲計算loss,而每一對p q都會是距離最近的一對。

Graph convolution

這個不太確定,好像是跟他們另一篇Mesh-RCNN有關(一樣是FAIR發的),等我看完在補吧。看起來就是拿周遭的鄰居做加權平均,而這個權重就是要訓練的東西,(以下mur mur,可是鄰居的數量是一致的嗎?還是因為是voxel cubify成的mesh,所以鄰居數量是一樣的?)

這邊介紹一下Mesh RCNN

簡單來說就是先利用Mask-RCNN做2D辨識,然後在把他轉成voxels,最後在轉變成mesh,這邊要這樣做是因為大部分做mesh的NN都是拿一個template mesh去變形,這會造成topology遭受很大的限制,換句話說就是複雜的東西很難產生,所以這邊先用voxel來表示,在cubify, refine成mesh。

所以這邊看到的Graph conv就是Pytorch3D實作的Operator。

KNN

K Nearest Neighbors for D-dimensional points are used in Chamfer loss, normal estimation, and other point cloud operations.

Pytorch3D有實作KNN,主要就是他們寫的會比較快。

Cubify

這個我猜一樣是因為前面提到的Mesh-RCNN裡面會用到所以才把他實作出來的。

最後可以來看看他們宣稱的點

Differentiability, meaning that it computes gradients with respect to all inputs; efficiency, meaning that it runs quickly and scales to large meshes and images; and modularity, meaning that users can easily replace components of the renderer to customize its functionality to their use case and experiment with alternate formulations.

Mesh prediction with differentiable rendering

Train time

In this task, we predict 3D object meshes from 2D image inputs with 2D silhouette supervision. We use a 2-view training setup: for each object image on the minibatch we include its corresponding view under a random known transformation

簡單來說,就是NN+DR,先透過NN將圖片輸出成mesh,然後在拿這個mesh用DR render成圖,然後跟Ground Truth計算loss,因為DR是可微的,所以我們可以將loss一路back propagate到NN進而訓練。

那這邊的CNN Backbone就是負責提取特徵,3D shape Network就負責利用特徵產生mesh,paper使用三種方式來實驗。這邊我也沒有詳細下去看,反正只要可以達到一樣的效果應該是可以替換的。

Sphere FC
Sphere GCN
Voxel GCN

Loss

All models minimize the objective L = Ls + λl Ll + λe Le. Ls is the negative intersection over union between rendered and ground truth 2D silhouette, as in [31]. Ll and Le are Laplacian and edge length mesh regularizers, respectively, ensuring that meshes are smooth. We set λl=19 and λe=0.2.

Silhouette loss,這個東西其實簡單來說就是IOU,分子用乘法代替交集,分母則是先相加在減掉交集來代替聯集,這個其實還蠻簡單的國中數學。其他兩個regularization分別是Laplacian跟 edge length,前者在mesh的領域常常出現,就是讓mesh保持一定的平滑,後者則是希望不要有邊太長或太短。

--

--