3D Gaussian Splattingの派生版 "EDGS"を試してみる

# はじめに
最近、X（旧Twitter）で3DGSの派生版であるEDGS（Eliminating Densification for Efficient Convergence of 3DGS）を見かけました。通常の3DGSより、トレーニング時間の短縮と品質の向上を達成したらしいので、自分のPCで検証してみました。

:::note info
コードや開発に疎い方（自分のこと）でも読んで実践できるように、なるべく詳細に、わかりやすく書きます。
:::
:::note info
ローカルPCで処理する場合、GPUが必要になります。GPUがない場合は、Colabでも動作可能です。詳しくはEDGSのプロジェクトページを参照してみてください。
:::

# EDGSとは（ChatGPTによる要約）
> EDGS（Eliminating Densification for Efficient Convergence of 3DGS）は、3D Gaussian Splatting（3DGS）の高速化と高品質化を目的とした手法です。従来の3DGSでは、初期段階で粗い点群を生成し、複数回の「密化（densification）」を経て詳細を補完していましたが、EDGSではこの密化プロセスを排除しています。
EDGSは、複数の画像間の密な対応点を三角測量することで、シーンの幾何構造を一度に近似します。これにより、各ガウス点に対して位置、色、スケールなどの情報を初期段階で割り当てることが可能となり、最適化の経路が大幅に短縮されます。結果として、従来の3DGSに比べて10倍の高速収束を実現し、使用するガウス点の数も半減します。
この手法は、特に高周波領域での詳細な描写に強みを持ち、既存の3DGSアクセラレーション技術とも互換性があります。EDGSは、効率的で高品質な3D再構築を可能にする、柔軟性の高いソリューションです。

要は、通常の3DGSでは初めに荒い状態でのGaussianを生成し、そこから詳細化していくアプローチですが、EDGSは初めから詳細なGaussianを生成してしまい、そこからEliminate（削減）していくというアプローチで、処理の高速化・高品質化を達成したということのようです。

# 使用したPC環境
特にスペックの要件についての記載はなかったですが、参考までに。
OS：Windows 11  
CPU：Ryzen7 5800X  
RAM：48GB  
GPU：Nvidia RTX3070  

# 論文
ミュンヘン大学のコンピューター・ビジョンの研究機関であるCompVisのDmytro Kotovenko, Olga Grebenkova, Björn Ommerが発表した論文『Eliminating Densification for Efficient Convergence of 3DGS』に基づくオープンソースソフトウェアを利用しています。  
詳細は下記を参照してください。  
[Eliminating Densification for Efficient Convergence of 3DGS](https://compvis.github.io/EDGS)

# 環境構築

### Python
コードを実行するのに、Pythonの環境が必要です。Anacondaの最新版をインストールしておきましょう。またPathを通しておきましょう。  
[Anaconda](https://www.anaconda.com)  
Pathがわからない方はこちらの方の記事をみてください。  
https://qiita.com/GaneDev/items/7183362046dc5d557a22

### CUDA Toolkit
Compute Unified Device Architectureというもので、GPUで並列処理を行うために必要です。  
[CUDA-Toolkit](https://developer.nvidia.com/cuda-toolkit)  
インストールは、デフォルトのまま進めていけばOKです。

### COLMAP
COLMAPは、Structure-from-Motion（SfM）とMulti-View Stereo（MVS）を組み合わせた、写真から3Dモデルを自動生成できるオープンソースツールです。EDGSのインプットデータは、通常の3DGSと同様のデータを使用します。COLMAPと呼ばれるソフトウェアで写真を処理して作ります。  
[COLMAP Github](https://github.com/colmap/colmap)  
ソースコードではなく、GUIで操作したかったので、こちらを利用しました。  
https://github.com/colmap/colmap/releases

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/76c0e0c2-c528-4f06-b15c-12a85d2249dc.png">

`colmap-x64-windows-cuda.zip`をダウンロードします。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/c0919075-f351-4867-a195-6fdbd3cabd50.png">

ダウンロードしたファイルを解凍し、中に入っているCOLMAPをダブルクリックするとGUIが起動します。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/6ff119e8-dc7d-40ff-9361-f4bfcc8892b3.png">

上記の画面が出てくればOKです。

### Wandb（Optional）
こちらは、ガウシアンのトレーニング状況やPCリソースの使用状況をリアルタイムにモニタリングできるツールです。  
[Wandb](https://wandb.ai)

# EDGSの構築
EDGSは3ステップで構築します。  
1. 写真を撮る  
2. COLMAPでカメラの位置と点群を推定する  
3. EDGSでガウシアンを作成する  

## 1. 写真を撮る
今回は家にあった子供のおもちゃを3D化してみます。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/51e159b3-58f0-4e09-a768-9fe836271a52.jpeg">

対象物を、高さを変えながら取り囲むように3周回って撮影しました。全部で90枚になりました。カメラはiPhone12 Proを使用しています。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/6a0ce6d8-a6a8-4bb3-a540-afe02afa6a72.png">

撮った写真は適当なフォルダに保存しておきましょう。

## 2. COLMAPでカメラの位置と点群を推定する

1\. COLMAPを実行します。  
* File → New ProjectでDatabaseを作成、撮影した画像のフォルダを選択します。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/64539565-b7fb-4e40-9114-5e63bdd996dc.png">

* Database: New → 適当な名前で保存します。（処理結果を保存しておくためのデータベースを新規作成しました。）  
* Images：Selectで撮影した写真のフォルダを選択します。  
* 「save」を押します。

2\. Feature Extractionを実行します。  
* Processing → Feature Extractionを選択します。  
* Camera Modelを「PINHOLE[^1]」に変更します。  
* Shared for all images[^2]にチェックを入れます。  
* first_octaveを"0"[^3]にします。  
* 上記の設定が完了したら、下部のExtractを押して実行します。（この処理は数秒で完了します。）

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/3560577f-ef9a-4b38-9abe-5da107c5a13f.png">

[^1]: Camera Modelは、実際のカメラが3Dの世界をどう2D画像に投影しているかを数式で表現したものです。今回のEDGSはPINHOLEのみ対応なので、そのパラメータに合わせて歪み補正をします。  
[^2]: このパラメータは、各写真の焦点距離が均一であることを示しています。  
[^3]: first_octaveは、特徴点の抽出を小さい縮尺から始めるか、大きな縮尺から始めるかを設定するパラメータです。-1の場合はより高解像度ですが遅く、1の場合は逆に速いが精度が落ちる、0はその中間です。

3\. Feature Matchingを実行します。  
* Processing → Feature Matchingを選択します  
* 出てきたウィンドウでSequential[^4]のタブを開いてから「Run」を押します。（この処理も1分以内に終了します。）

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/f01bbeba-7a36-46ee-959a-0f6264ad9edd.png">

[^4]: Sequentialは、位置関係が連続して撮影された写真である場合に用いる処理方法です。

4\. Reconstructionを実行します。  
* Reconstruction → Start Reconstructionを選択します。  
* うまくいっていると、GUI上にカメラが順番にアライン（整列）されます。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/50c9a489-ddac-4856-b485-629abd0912ee.png">

* デフォルトだとカメラの表示が大きいので、Alt + マウスホイールでカメラの大きさを小さくし、自分が撮影した時とカメラの位置が概ね合っていることを確認します。

5\. COLMAPで推定したカメラと点群のデータを出力する  
* File → Export Modelで出力先フォルダを選択します。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/0eeb4e84-60b0-4a81-84ae-fd883795914c.png">

* 下記の3つのバイナリファイルが保存されていれば成功です。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/32cfa809-890c-4c17-bc79-980b326199b1.png">

## 3. EDGSでGasussianを作成する
1\. EDGSをインストールする。
* EDGSの[Githubリポジトリ](https://github.com/CompVis/EDGS)にアクセスし、「install.sh」のシェルスクリプトファイルを入手します。（全体をクローンする必要はありません）。
* Download Law Fileで入手します。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/1a96adb7-db16-4c93-8b11-cebb9f5f5f81.png">

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/fc646ac3-3196-4033-88c7-0c5e267ab432.png">

* 入手したファイルはCドライブ直下など、適当なところに置きます。
* 「install.sh」を実行すると、Githubリポジトリがクローンされると同時に必要なパッケージ類がインストールされます。

2\. 処理の下準備をする。
* コードエディタ（VSCodeやCursor等）でEDGSフォルダを開きます。
* ESGS直下に「scene_folder」というフォルダを作成します。フォルダの中身は下記のような構造にしてください。
```plaintext
scene_folder/
├── images/ ここにインプットとなる写真を配置します。
│   ├── 00000.png
│   ├── 00001.png
│   └── 00002.png
│
└── sparse/
    └── 0/　ここにCOLMAPから出力したカメラ・点群の情報が入っている3つのファイルを配置します。
        ├── Cameras.txt
        ├── Images.txt
        └── Points3D.txt
```
* EDGS直下に「output」というフォルダを作成します。

3\.トレーニングを実行する。
* ターミナルでカレントディレクトリをEDGSにし、下記のpythonスクリプトを実行します。

```python
python train.py \
  train.gs_epochs=30000 \
  train.no_densify=True \
  gs.dataset.source_path=scene_folder \
  gs.dataset.model_path=output \
  init_wC.matches_per_ref=20000 \
  init_wC.nns_per_ref=3 \
  init_wC.num_refs=180
```
* 実行前に、Wandbを利用するかが聞かれますので、利用したい方はセットアップを行います。（ここでは詳しくは説明しません。）利用しない方は、その選択肢を選択します。
* トレーニングが開始されるとターミナル上の進捗は下記のように示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/f52369f2-8b3b-4b34-8522-97497c4acd9b.png)

* Wandbを利用した場合、webのURLが表示され、アクセスすると次のような画面で進捗やリソースの使用状況が把握できます。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/bc851f2d-95fb-44e5-8d18-b9ee476ab27f.png)

*トレーニングが完了すると、「Training complete.」と表示され、outputフォルダの中に「.ply」のファイルが生成されます。これがGaussian Splattingのファイルとなります。

# 結果
通常の3DGS（Postshotを使用）と比較して、EDGSは処理時間が短くなったものの、品質は下がりました。
| 手法 | 品質 | 処理時間 |
|:-:|:-:|:-:|
| EDGS | ○ | 58分 |
| 3DGS | ◎ | 1時間37分 |

### EDGSで生成したもの：イテレーション30000回、処理時間：58分  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/556537c5-a1cd-47ca-aa6b-6d7747523b75.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/c14d879c-b3cc-4b77-a26c-4981464fd71d.png)

### 通常の3DGS（Postshot Splat3）で生成したもの：イテレーション30000回、処理時間：1時間37分  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/e928124e-35f7-4b4a-a726-72574d15cecb.png)  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4090236/4973f100-2110-4e16-9a95-a0d73c57d130.png)

### なぜEDGSの品質が落ちたのか?（仮説）
* EDGSと3DGSで、カメラのアライメントは別のソフトを利用したため、その処理結果の品質がダイレクトに反映してしまった?（現状EDGSはCOLMAP形式しか受け入れず、Reality Captureでは対応した形式でエクスポートできなかった）
* 単純にSplat3の品質が高い?