作者:蔣天園Date:2020-05-27
前言
筆者上一篇文章有介紹了3D目標檢測中比較重要的數據預處理的兩個方面的內容,其一是幾種representation的介紹,分別是point、voxel和grap三種主要的representation,具體的可以表示為如下(這里的grids即是voxel)。上一篇文章也分析了這三種representation的優缺點:(1)point-sets保留最原始的幾何特征,但是MLP感知能力不及CNN,同時encoder部分下采樣采用了FPS(最遠點采樣)(目前就采樣方法的研究也挺多,均勻采樣,隨機采樣或者特征空間采樣其異同都是值得思考研究的),FPS采樣對比voxel的方法會更加耗時(2)voxel的方法在精度和速度上都是獨樹一幟的,但是不可避免的會有信息丟失,同時對體素參數相對比較敏感。(3)grah的表示在3D目標檢測上,在CVPR20上才提出來,就Graph的backbone時間消耗比較久,比point的方法還要就更多,但是直觀上看graph的結構增加了邊信息更加容易機器感知。

本文的主要內容是就只其中representation的方法的發展和目前仍然存在的問題做一個概述。后續再結合筆者學習給介紹一點可能會不是很清晰的知識。
1. voxel representation 背景知識介紹
這一小節,筆者將按照網絡前饋過程逐個介紹如何將point信息轉化為voxel信息,再通過深度學習網絡特取到高維信息,再到二維rpn的設計。
1.1 point2voxle過程
上一篇文章有講到過,這里復制過來:1. 設置Voxelization參數(每個voxel可以存放點的個數(max_points_number),voxel長寬高的大小(whl))2. 對依次每一個點,根據其對應的坐標(x,y,z)得到該點在voxel的索引。3. 根據索引判斷該voxel種是否已經存在max_points_number個點,如果存在,則將該點直接丟棄,如果不滿足,則將該點加入到該voxel中。4. 計算voxel特征為了方便理解筆者做了一個簡單的示意,如下下圖的過程則是Voxelization的過程,如果max_points_number設置為3,那么紅色點如果是比較后加入到對應的voxel,那么就會被丟棄。

這里我們可以注意到筆者提到的信息損失則是來自於第三步的點的丟失。
1.2 voxel 特征提取
voxel特征提取的含義就是將point的特征轉化為voxel特征,是緊緊承接上一步的內容,上一步得到了很多的voxel,每一個voxel中包含了少於max_points_number個點,這一步就是如何根據voxel中的point特取得到voxel特征,這里筆者介紹兩種常用的特征提取方法:(1)MLP提取,即是對voxel中的點采用幾層全連接層將voxel中的Point信息映射到高維,最后再在每個特征維度上使用maxpooling()得到voxel的特征,但是這種方法必須保證每一個voxel中的點數一樣,所以所有的voxel就僅僅只有兩種狀態,要么是空的,要么是有max_points_number個點的(可以采用重采樣的方式保證每一個voxel的點數一樣多),一般來說這種特征提取的方式每一個voxel的點數比較多(2)均值特征。顧名思義,即是將voxel的point的特征(坐標+反射強度)直接取平均,目前這是比較優的point_fea2voxel_fea的方法
經過上述的voxle特征,我們就得到了每一個voxel的特征,就可以采用感知能力強大的CNN結構了。
1.3 voxel backbone
這一部分,在我們得到了voxel 特征后,就是進一步提取到更加全局的特征,上文中的voxel特征僅僅是在一個voxel中的特征,甚至連local feature都沒有很好的提取到,就voxel backbone的發展經歷了下面的兩個階段。
1.3.1 3DCNNbackbone
voxel-based的方法開始起步的時候,采用的voxel特征提取是上述中的第一種MLP提取方式,所對應的backbone的方法也就是采用比較容易實現的3D CNN,如下圖筆者做一了一個簡單的示意,通過voxel特征提取我們得到了point2voxel的特征,下圖中左邊表示的內容(C表示特征維度),其中空的voxel表示在原始空間沒有點的在該voxel內,3D CNN則是對這樣一個(H,W,L,C)的四維張量做3D空間中的卷積,筆者示意圖的kernel大小為(222)(一般情況下是333的大小),真實情況中空的voxel比示意圖中還要多得多,這我們在日常生活中也是可以體驗到的。下圖中的黃色kernel也是三維的,經過4次stride=2的卷積再加一次額外的高度維的stride=2的卷積后得到了右圖的feature map ,但是這個時候任然是三維的feature map.

經過下圖的操作,即將高度維的特征直接壓縮到特征維中,變成了二維的feature map。所以此后就可以直接采用二維RPN 網絡結構對三維物體進行目標檢測。這個會在后續的RPN Head的網絡中講到。

從上面的示意圖和介紹中,我們不難發現至少有如下的幾個問題:(1)3D卷積的kernel為三維kernel,因此會存在參數量巨大的問題,可能不好學習或者導致過擬合。(2)輸入的整個場景的voxels含有很多空的voxel,但是在卷積過程需要將其的特征填充為0,是很占顯存的,同時時間效率也降低。上述兩點是目前解決的比較好的問題了,就筆者的理解,至少還有如下的兩個問題有待解決,(3)backbone特征提取實際上是逐漸將H維度降低為2,最后再壓縮為1,所提取到的特征更加偏向於BEV視圖的特征,如何更好的利用點雲的整體信息是可以考慮的,(4)從上面的圖中可以看出,在輸入場景中是有很多空backbone的,這雖然對3D CNN是一種顯存的損失,但是卻維持了三維物體的幾何結構,但是經過Backbone的CNN過程,會導致原本是空的voxel變得有信息,從而丟失了幾何結構信息,這是一個很值得考慮的問題。
1.3.2 稀疏卷積 backbone
上文提到的內容中提到過3D CNN存在一個比較重要的問題是空的voxels會導致顯存的浪費,所以后續的研究者yanyan(SECOND(Sensors18))采用稀疏卷積減少顯存占用,具體的可以參看second原文(SECOND: Sparsely Embedded
Convolutional Detection)或者筆者覺得 講的也很清晰的文章3D backbone(3D Backbone Network for 3DObject Detection)。
其實稀疏卷積的含義就是只對含有點的voxel做卷積輸入,如果是空的voxel就直接不參與卷積計算,但是實際上隨着卷積過程的推進,由於卷積的膨脹性質,會出現更多的非空的voxel。這個問題后續引入了“流型卷積層”來緩解這個膨脹問題。
這里先給出SECOND中的結構圖如下,實際上可以看的出來就是將3D卷積充分利用二維 map映射關系來做。

筆者這里畫出一個簡化的內容如下,實際上就是先將原始空間中的非空的voxel的空間索引記錄起來,將其特征排成一列map,卷積操作也是通過計算索引來完成的,也就是說最終的結果僅僅是在二維中通過索引計算得到的,最后將final-feature-map通過最終的空間索引還回成voxel表達即可。一樣的,為了使用二維RPN網絡,一般的設計都是和上面一樣將H層直接壓縮到特征。

1.3.3 backbone 小結
上面介紹了兩種按照先后順序發展起來的backbone方法,其中3D CNN和voxel特征提取中提到的MLP特征提取相配合(因為但是3D CNN還沒有稀疏卷積表達形式,所以考慮到顯存消耗,在voxelization時參數設置比較粗糙,所以每一個voxel中的點比較多,采用MLP特征提取是比較合適的)。其中3D 稀疏卷積表達是當前流行的backbone設計基礎結構,極大的解放了顯存占用,因此可以在3D稀疏卷積上設計各種高效的Backbone結構。但是從CVPR19到CVPR20一段時間內的voxel -backbone都是采用如下的encoder的結構。這里直接截取PV-RCNN(CVPR20)的網絡結構的一部分,看的出來3D稀疏卷積的部分僅僅是一個下采樣卷積特征提取的過程,最后的To BEV也就是上面筆者所畫的將H層壓縮到特征維度的操作。

1.3.4 補充知識
前面提到了3Dbackbone至少存在着四個值得考慮的問題,其中最大的顯存占用問題已經通過稀疏表達的形式得到了比較好的解決,那么針對第四點問題,也就是卷積膨脹使得幾何結構模糊化的問題,筆者有兩點可以介紹,一點是今年的CVPR的文章SA-SSD,(在前面的文章中有細致的講解),如下圖所示,為了使得voxel-backbone結構能夠擁有原始幾何結構的感知能力,作者在原始點中添加了兩項附加任務,分別做語義分割和中心點預測,最后的目標檢測結構得到了很好的幾何結構的感知能力。

第二點是筆者在實驗中的思考,實際上在使用稀疏卷積的同時會配合使用‘流型卷積’結構,最原始的出處是SubmanifoldSparse Convolutional Networks(FAIR,NIPS17),具體的內容就是卷積過程帶着膨脹的性質,實際上會模糊掉邊界幾何信息,如下圖所示。所以研究者就提出卷積輸出的grid需要在有本來就有特征的grid才有輸出,換句話說,就是在原來本身不是空的voxle才是具有輸出的,而原本是空的,但是周圍kernel鄰域內存在非空的voxels是不可以輸出的,這樣就能一定上保持比較重要的幾何結構。

但是換個思考方式,卷積特征提取本身是在提取到更高維的特征,可能后續的feature-map在我們看來的邊界模糊再機器感知中是能夠被理解的也不一定。但是在3D卷積結構中,實驗表明加了流型學習層的效果都比較好。這里筆者給出代碼中的常用的設計格式如下,一般都是兩層的‘流型學習’層加上一層的‘稀疏卷積’層(這樣僅僅只做一個stride=2,所以一般下采樣會得到設計4個這樣的結構做到8×的下采樣)。
SubMConv3d(num_input_features,128, 3, indice_key="subm0"),
BatchNorm1d(128),
nn.ReLU(),
SubMConv3d(128, 128, 3,indice_key="subm0"),
BatchNorm1d(128),
nn.ReLU(),
SpConv3d(128, 64, 3, 2,
padding=1), # [41,1280,1056]→[21,640,528]
BatchNorm1d(64),
nn.ReLU(),
1.3 二維 RPN
為什么叫二維RPN,因為從上面的backbone中我們最后將H維度直接壓縮到特征維度中,直接將三維feature-map轉化為了二維特征圖,所以在二維特征圖上,我們可以進一步采用二維優秀的RPN結構做三維目標檢測任務,盡管voxel-representation的RPN結構有挺多的設計,但是就最基礎高效的設計都是采用Voxle-net(CVPR18)上的RPN設計。如下所示,非常清晰的一個下采樣然后上采樣concat的結構。就不多提。在做cls和reg之前,筆者假設我們的feature-map大小是[B,H,W,C],那么做cls的話和正真的二維檢測都是一樣的直接在特征維度上映射到1,即[B,H,W,1];我們知道在二維檢測中回歸只需要回歸Bbox的左上角和右下角的坐標即可,但是在3D目標檢測中,我們需要回歸七個維度,分別是中心點坐標(X,Y,Z),長寬高(W,H,L)以及朝向(dir),但是在實際實驗中,會回歸預定設置的朝向的倍數個參數量,即比如anchor預先設定的值為90°和180°,那么我們需要對每一個anchor都回歸這樣的七個維度,也就是14,同樣對cls也需要分類兩個。這些問題在一些文章中有一定的改進。ICCV19的STD采用球划分,就不會有方向性,更加優秀的anchor設計方式。

1.5 背景知識小結
這里就相當於把一個one-stage的3D目標檢測算法給講明白了,這其實也就是在19年上半段的研究者的研究工作,在19年的下半段出現了很多refine網絡結構,也就是精度更高的兩階段3D目標檢測方法,這里畫一個簡單的總結圖如下,除了refine階段,上面的內容就是最基礎的3D目標檢測one-stage的結構。可能會在后面的文章中介紹到。

2 voxel-representation文章發展
筆者這里想按照問題問題解的發展歷程,按照問題介紹發展。目標檢測主要可以分為兩個大的問題,一個是精度,一個個是速度,這也是為voxel-backbode的方法受歡迎,因為在速度和精度上都達到了比較好的效果。這一篇先介紹一下在速度上,前人研究者都做過哪些比較重要的嘗試。
2.1 速度上的改進
在第一小節中,筆者提到3DCNN時間消耗太大,因此在18年的SENSORS研究者提取采用稀疏卷積代替3DCNN,大大的減少了參數量和顯存消耗,這里不多介紹這篇文章。后續在19年CVPR上,pointpillars充分利用BEV視圖特性,如下圖,我們前文介紹到的都是划分voxel,這篇文章直接划分pillars,這樣的好處可以直接將3D CNN過程省略掉,FPS高達恐怖的60,但是不可避免的是這樣做信息感知能力變差,需要進一步做更多的優化工作。這也促使我們在思考這樣的一個問題,BEV視圖下的特征是否真的已經足夠做3D目標檢測?

進一步的 在NIPS19上有一篇出自韓松實驗的文章Point-Voxel CNN for Efficient 3DDeep Learning,想要通過voxel結構代替pointnet++特征提取,也就是希望借助voxel特征提取的快速性(好像不是很搭邊),如下圖可以看的出來,實際上也就是將pointnet++采用voxel-backbone代替掉,然后再采用插值的方式回到點中。最后也將該方法測試在pointR-CNN中得到了不錯的效果。

目前在voxel-representation上從速度着手的研究工作並不是很多(得益於稀疏卷積),但是在point-based的方法中是很多從速度方面着手的,比較好的有今年VPR20的3D-SSD,之前有寫過比較細致的研究文章,有興趣可以看一看研究者是如何思考這樣的問題的。
2.2 精度上的改進
這一部分比較空曠,內容比較多,所以這一篇文章就先開個頭,大概列舉一下筆者所看到過的文章中主要的方向:
(1)refine(2)loss(3)fusion(4)backboe -structure(5)others。paper -list內容看上去不少了,所以這個最核心的內容就做下次的分享內容吧。這次后續再介紹一點
3 補充知識
3.1. point和二維圖像的變換
很多研究工作采用了二維圖像和3D點雲融合的方式,以增大信息量,但是這其中如何確定一個點所對應的二維圖像的像素點的位置呢,就KITTI而言,可以做如下補充。
就KITTI的數據而言,有
(1)我們下載的點雲投影到相機平面的數據是calib_velo_to_cam.txt,表示的是點雲到相機的定位文件。在KITTI中還有文件calib_cam_to_cam.txt(相機到相機的標定)。
(2)相機和點雲的坐標定義:相機(x:右,y:下,z:前) 。點雲(x:前,y:左,z:上),也就證實了上文中投影在相機前面的點時采用z>0為判定條件。
(3)計算點雲到圖像的投影矩陣,如下展開說:
3.1.1核心思想
計算點雲到圖像的投影矩陣需要三個參數,分別是P_rect(相機內參矩陣)和R_rect(參考相機0到相機xx圖像平面的旋轉矩陣)以及Tr_velo_to_cam(點雲到相機的[RT]外參矩陣)。
3.1.2計算投影矩陣(matlab)
% 計算點雲到圖像平面的投影矩陣
R_cam_to_rect= eye(4);
R_cam_to_rect(1:3,1:3)= calib.R_rect{1}; % 參考相機0到相機xx圖像平面的旋轉矩陣
P_velo_to_img= calib.P_rect{cam+1}*R_cam_to_rect*Tr_velo_to_cam; % 內外參數
3.1.3點雲投影到圖像
投影矩陣乘以點雲坐標。在此之前需要把點雲填充到四維的齊次坐標,也就是加1。即把前三維作為輸入因此需要轉化為齊次坐標。投影矩陣是4* 4的。最后和point相乘就可以得到在二維圖像中的位置了
3.2 推薦項目
就筆這前半年的研究過程,發現了很多優秀的3D目標檢測的項目,筆者的起步是從SECOND的作者的項目出發,地址是:https://github.com/traveller59/second.pytorch。很多研究者的工作都是在這個基礎上的,但是代碼並不是很適合快速上手,筆者之前學習的時候有做過很多的記錄筆記,如果讀者都感興趣的話,筆者可以在后續出一個該項目的教程。
接下來的這一個項目時mmdetection風格的項目,作者是CVPR20文章PV-RCNN的作者,代碼風格和可改性就大很多,筆者目前也是在這個項目上實現自己的Idea,地址是:https://github.com/sshaoshuai/PCDet 此外還有很多優秀的項目,如曠世的det3D:https://github.com/poodarchu/Det3D。
推薦文獻
[1] 3D Backbone Network for 3D ObjectDetection
[2] PointPillars: Fast Encoders for Object Detection from Point Clouds
[3] SECOND: Sparsely Embedded Convolutional Detection
[4] Submanifold Sparse Convolutional Networks
[5] Point-Voxel CNN for Efficient 3D Deep Learning