LIDAR点群から高速で３D物体検出！ PointPillarsを解説するっ！



今回は、３D点群から物体を見つけるアルゴ、PointPillarsを解説するぞ。
え、なんのためにするかって？

自動運転に使うためさ！（そしてWaymoデータセットで訓練したいZE・ω・）
Waymoデータセット知りたい方はこちら
[Waymo自動運転車のデータセットを可視化するんだよ！](https://qiita.com/chin_self_driving_car/items/6ca4c8c6960d3dc4d77d)

Waymo大先生のデータセットつかって、物体検出したいけど、何を使えばいいのやら...
そしたら3D物体検出のState-of-the-artを調べると、PointPillarsがでてきて気になるぞ！
[PointPillars](https://paperswithcode.com/paper/pointpillars-fast-encoders-for-object)

タピオカとPointPillarsどちらが素晴らしいのか！
それではPointPillarsの世界へようこそ！

* PointPillarsの仕組みは？
* 点群のData augmentationはどうやる？
* 計算の高速化テクニックは？

気になった人はLet's DIVE IN!!!!
(間違っているところあったら教えてね）
![Screenshot from 2019-10-04 10-45-50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/c8c3fec0-3575-e21d-fa2c-e707d0452c1b.png)



## この記事ですること
* PointPillarsの解説
    * Abstract
    * Introduction
    * PointPillars Network
    * Implementation Details
    * Experimental Setup
    * Realtime Inference
    * まとめ

※論文に沿って原理、仕組みを解説していきます、実装関連は次回にします

論文リンク　発表は2018年末
[PointPillars: Fast Encoders for Object Detection from Point Clouds](https://arxiv.org/abs/1812.05784)


## Abstract
PointPillarsの特徴

* 3Dの点群から物体検出を行う
* PointNetsと呼ばれるネットワーク構造を使用して、点群を縦の柱の集合体にみたてる
* LIDAR点群のみ使ったアルゴだが、フュージョンアルゴと比べてもいい性能が出る
* 動作が早い、KITTIベンチマーク（３D,bird's Eye view)を６２Hz

![Screenshot from 2019-10-04 11-02-47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/af5c3b2d-36fe-76c4-db6c-4a8928b931b8.png)

上図では”PP"（PointPillars)がKITTIテストデータの各種物体検出において
検出性能と検出速度の両方において優れているのがわかる。
対抗馬は
M: MV3D
A: AVOD
C: ContFuse
V: VoxelNet
F: Frunstum PointNet
S: SECOND
P+: PIXOR++

## Introduction
DeepLearningの技術が３D点群を処理する方法は過去に考えられてきた。
過去の点群から物体検出する論文に共通する点は

* 点群はスパースであること、画像は非常に密であること
* 点群は３Dで、画像は２Dであること

点群を２Dに見立てる研究は過去にもされており、下記のような種類がある

* 点群を鳥瞰視点に変換して物体検出するもの
* 点群を主観視点に変換して物体検出するもの

最近では鳥瞰視点を用いたものが流行っている
長所：

* サイズがはっきりわかる
* 近傍ではオクルージョンが少ない

短所：

* 非常に点がスパースであるため、単純にCNNに入れるのではうまくいかない

上記の短所を解決するために、
VoxelNetやPointNetという手法がある、
しかしそれらは処理速度が非常に遅いのが問題点であった。

上記問題点を解決するために、PointPillarsを提案する。
下記は特徴：

* ２D convolution layerを用いたend2end 3D物体検出、２Dベースのため計算が高速
* 点群を縦柱（pillar)に見立てて３Dで物体の検出をする
* pillarで扱うことによりvoxelのように点群の高さを区切るビンの高さ設定が不要になった

## Point Pillars Network
![Screenshot from 2019-10-04 14-34-40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/cdb48e5b-3da4-e773-9df7-1cf05a9f8468.png)

PointPillarsの入出力
入力：３D点群
出力：３Dバウンディングボックス（車、歩行者、サイクリスト）

３つのメインステージ

* Pillar Feature Net: 点群をスパースなpseudo imageに変換する
* Backbone (2D CNN): pseudo imageをハイレベル特徴量へ変換する
* Detection Head(SSD): 検出と３Dボックスへの回帰を行う

### PointCloud to Pseudo-Image
2D CNNで点群を扱うためには、まず点群をpseudo-imageへ変換する。
![Screenshot from 2019-10-04 15-53-30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/4c077cd8-2e35-f3e5-c51b-5fe29a0833dc.png)


l:点群の中の点
x,y,z,r: 点に付随する位置と、反射強度の値

xy平面上をpillar, Pで分割する。（pillarで表現するため、Z側の高さは必要ない）
（例：0.16m * 0.16m等）

pillar内の点は下記の値を計算する
x<sub>c</sub>, y<sub>c</sub>, z<sub>c</sub>: pillar内のすべての点の座標の平均からの距離
x<sub>p</sub>, y<sub>p</sub>: pillar中心からのオフセット

上記の計算を終えて、点ｌは９次元の値を持つ。![Screenshot from 2019-10-04 23-19-51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/9c38b66a-57ee-802f-a4bc-ce54798afd95.png)


これにより、tensor sizeは、size(D,P,N)となる
```D x P x N = 単点が保有する次元数（9) x 点の存在するpillarの数 x pillarに存在する点の数```

(D, P, N) tensorをPointNetにかけて（Linear layer→Batch-Norm→ReLu)、(C, P, N) tensorを取得
CはPointNet出力の全結合層のレイヤ数に依存する
Cを取得した後、（C,H,W)のpseudo-imageに変換する（H,W）は鳥瞰視点でみたキャンバスサイズ。

(C,H,W)にすることで、通常の色画像と似た扱いが可能になる。

### Backbone(2D CNN)
![Screenshot from 2019-10-04 15-54-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/2c51b5f0-08ab-f874-678a-df63df3d4536.png)
Backboneは主に２つのネットワークで構成されている

* top-down network: 段階的に次元数を下げていき、各段階で特徴抽出する。
* second network: top-downの各段階の特徴量のupsampling, concatenationを行う

（C,H,W）のpsuedo画像を、(6C, H/2, W/2)のtensorに変換する

### Detection Head(SSD)
ここではsingle shot detectorを使用する。
SSDのprior boxと、真値2D BoxのIoUを使用してマッチングさせる。
マッチング結果から、クラス分類、物体の位置と高さを推論する


## Implementation Details

### Loss Functions
ロス関数は、SECONDの論文に準拠する
３つの要素のロス関数がある

* 位置
* クラス
* 向き

3D boxは、ground truthとanchor box含め下記で表現する
![Screenshot from 2019-10-04 23-16-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/c0cdaabb-3d52-b892-e968-8ea6d242f80a.png)

各要素のerrorは下記で計算する

![Screenshot from 2019-10-04 23-14-43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/91d63ac5-4fea-fb47-fee6-56f3f1005382.png)

gtはground truth, aはanchor boxを示す
![Screenshot from 2019-10-04 23-26-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/1b6354f6-e5dd-1d67-a645-e284467c21b1.png)


【位置のロス関数】
![Screenshot from 2019-10-04 23-19-44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/8f5bb805-29ce-4fe6-5d81-7190da8c9369.png)

【向きのロス関数】
![Screenshot from 2019-10-04 23-30-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/cf8f828b-6297-b6c9-79e9-9dbd432ba33d.png)

方位角をクラス化したものの、分類ロス計算する

【クラス分類のロス関数】
![Screenshot from 2019-10-04 23-19-51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/085f39a4-f6ee-b65d-fcdf-d81ca7f130d3.png)

$p^a$は、anchor boxのクラス確率
$\alpha$ = 0.25
$\gamma$ = 0.2

【総合のロス関数】
![Screenshot from 2019-10-04 23-19-58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/dde4ea12-a324-3fa9-17f4-5b129dce3955.png)

 $\beta$<sub>loc</sub> = 2
 $\beta$<sub>cls</sub> = 1
 $\beta$<sub>dir</sub> = 0.2

## Experimental Setup

### Setting
pillarの寸法：0.16m * 0.16m 
pillarの最大数：12000
ひとつのpillarにおける最大の点数： 100

### Data Augmentation
訓練はKITTIデータ・セットを用いて行う
augmentationはどうやるのか？

以下の手順で行う
1. 各クラスに所属する点群リストのテーブルをデータセット全体から作成する
    * 例：車、というクラスに対して、フレーム0~100の車属性の物体が並び、それぞれに点群が紐付いている
2. 訓練するフレームに、ランダムで上記テーブルから物体を呼び出す
3. さらにフレームに存在するground truthと、付け加えた物体の位置をずらす、回転を加える
4. 車両進行方向へのミラーリング、点群全体の回転、スケーリング
5. 点群全体の並進移動

## Realtime Inference
計算のステップと処理時間は下記の通り
実装はpytorchで行われている

* 点群の読み込み、距離のクロッピング 1.4ms
* 点をピラーに分割する 2.7ms
* PointPillar tensorがGPUへアップロードされる　2.9ms
* encoding 1.3ms
* pseudo-imageへ変換する 0.1ms
* backbone & detection head 7.7ms
* non maximum suppression 0.1ms

PointPillarsはi7 Intel CPU + GTX1080Tiにて、16.2ms/frameの処理速度（車両前方の点群のみ）
高速化のテクニックは？

### Encoding
前述のPillar feature netの処理にかかる時間が1.3ms
(厳密には（D,P,N）tensorを(C,H,W)tensorに変換するまでの時間）

VoxelNet(190ms)やSECOND（50ms）のエンコード処理時間を大幅に短縮

### Slimmer Design
PointPillarsはPointNetの使用を一回に抑えている。
VoxelNetはPointNetを２回連続で使用しているのに対して高速。

また、PointsNetの出力である(C,H,W)tensor のC項を64に設定することで高速化、
さらに、2D CNN back boneではアップサンプリングを128に設定することで高速化。
この値はどれもオリジナルの半分以下だが、検出性能の低下がなかった。

### TensorRT
前述のPointPillarsの処理パイプライン：

* Pillar Feature Net: 点群をスパースなpseudo imageに変換する
* Backbone (2D CNN): pseudo imageをハイレベル特徴量へ変換する
* Detection Head(SSD): 検出と３Dボックスへの回帰を行う

これらGPUを使用する処理をTensorRT(GPU推論に最適化されたライブラリ)を使用して高速化。
これにより５０％の高速化に成功。

## まとめ
以上がPointPillars論文の解説でした、
簡単にまとめると

* 3D点群から３D物体ボックスを出力するアルゴリズム
* 3D点群を、pillarで分割し、2D CNNに入力できるように畳み込み、クラス分類と検出を行う
* 2Dでの取扱いを行うエンコーダと、PointNetの使用回数を１回に留めること、TensorRTの利用で非常に高速な処理が可能になった

３D点群の処理は非常に重く、どうやって高速化しているのかが非常に気になっていました。
３Dを柱に分割して、畳み込んで２Dのフレームワークに落とし込む、というのがPointPillarsのキモみたい。
また点群のdata augmentationも、他のフレームの物体を召喚する！感じでなかなか面白い！
デスクトップPCで62Hz~105Hz出るアルゴなので、車両搭載では、組み込み端末であること、全周囲の点群があることを差し引いて
どれくらいの速度で動くのかカギになりますね

２Dに畳み込むというアイデアを応用すれば、
Waymoデータセットに付随しているLIDARのelongationなんかも、足してみると検出性能あがったり...?
夢が膨らむよね。（処理速度落ちるんだろうな...)

![Screenshot from 2019-10-05 00-43-58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388647/8e96b136-a886-5e28-8bc4-1ca516f52c57.png)



次回はPointPillars実装を見ていくぞ！（Waymo datasetで使えるようになるのを目指すぞ！）
それでは〜

次記事:[PointPillars　訓練から評価まで](https://qiita.com/chin_self_driving_car/items/df61448ef4abda1a71a3)