2DGSを用いたガウシアンのメッシュ化

# はじめに
3Dスキャン Advent Calendar 2024がついにスタートしました！

https://qiita.com/advent-calendar/2024/3d-scan

初日のテーマは、2D Gaussian Splatting（2DGS）を活用したガウシアンからメッシュ化までの手順について解説します。
この記事では、以下の投稿で紹介されている焚き火をメッシュ化した方法を基に、具体的なプロセスを紹介します。

https://x.com/VRTAKAYAN/status/1860073872315277351

## 3D Gaussian Splattingをメッシュ化する経緯
私は最近、VRゲームの一つであるVRChatで、現実の物を3DスキャンしてVR内に持ち込むことにハマっています。旅行やイベントの思い出の品、現実では動かせないもの、または解体などして無くなるものを、VRで保存・動かすことで、写真や映像では再現できない「3Dならではの表現」を実現する楽しさを追求しています。

作例はこちら

https://youtu.be/pKcQ5aizzQQ

そんな中でフォトグラメトリを活用してきましたが、水やガラスといった透明なもの、火のように動的なものはフォトグラメトリが不得意であることを痛感しました。そこで、これらに強みを持つガウシアンに興味を持つようになったのです。

### 現在の解決方法と課題
現状、VRChatでガウシアンを表現する方法としては、VoxelKeiさんが開発した独自のシェーダーが知られています。
現状はVoxelKeiさん自身が[こちら](https://github.com/cnlohr/slapsplat)を改良されたシェーダー？で実現されています。

https://x.com/VoxelKei/status/1784339485230284825

このシェーダー？はとても魅力的ですが、GPU負荷が高いため、別の方法を模索していました。そして、「ガウシアンをフォトグラメトリのようにメッシュ化できないか」と調べたところ、2DGS（2D Gaussian Splatting）に行き着いたのです。

https://surfsplatting.github.io/

:::note warn
警告
2024/12/1現在の構築方法です。
各ソフトのアップデートにより構築方法が変更になる可能性が高いので、この記事はあくまで参考にしていただき、各ソフトのREADMEをお読みください。
READMEから引用したところはこれと同じような警告があります。
:::

# 前提条件
- Ubuntuの多少の知識がある
- RealityCaptureのアライメント処理をしたことがある
- ~~VRAM 24GB以上のGPU~~？

# メッシュ化までの流れ
:::note info
この章について
記事を書いていたら、かなり長くなってしまったことに気がついたのでとりあえず全体の流れを知っていただこうと思い、この章を書きました。

流れを大まかに理解していただければ、読み飛ばしてもらって大丈夫です。
処理の流れがわからなくなったら、また読み返して貰えればOKです。
:::

## 1. 環境構築
- WSL2(Ubuntu 20.04)
- Anaconda
- 2d-gaussian-splatting
:::note info
WSL2を選んだ理由
私の前提条件を満たしているGPUがWindowsメインマシーンのみだったため、WSL2を使ってUbuntu 20.04をインストールしました。
もし、Ubuntuマシーンがある場合はそちらで問題ありません。
:::
## 2. 素材データ準備
正直このデータを準備するのが一番時間かかりました。
```mermaid
graph TD;

subgraph　アライメント処理
    B[RealityCapture];
end

subgraph 出力されたColmapデータ
    B-->|Colmap出力|C[(Colmap txtデータ
    （画像含む）)]
    C-->I[(Camera.txt)];
    C-->G[(Images.txt
    Points3D.txt)];
    C-->H[(画像素材（補正済み）)];
end

subgraph　Cameraタイプ変更
    I-->D[Cameraタイプ変更
    プログラム];
    D-->K[(Camera.txt)];
end

subgraph　binに変換
    K-->E[bin変換
    コマンド];
    E-->J[(Camera.bin
    Images.bin
    Points3D.bin)];
    G-->E;
end
    G-->F;
    H-->F;
    J-->F;
    K-->F[(素材データ)];
```

```:RealityCaptureから出力されたColmap txtデータ構造（画像含む）
<location>
|---cameras.txt
|---images.txt
|---points3D.txt
|---<image 0>
|---<image 1>
|---...
```
```:最終的な素材データ構造
<location>
|---images
|   |---<image 0>
|   |---<image 1>
|   |---...
|---sparse
    |---0
        |---cameras.bin
        |---images.bin
        |---points3D.bin
        |---cameras.txt
        |---images.txt
        |---points3D.txt
```
:::note warn
警告
2024/12/1現在のフォルダ構造です。
引用元
https://github.com/graphdeco-inria/gaussian-splatting?tab=readme-ov-file#processing-your-own-scenes
:::
## 3. Gaussian Splatting処理とメッシュ化
2DGSのREADMEに書かれている方法で、ガウシアン処理とメッシュ化を実行します。

# 環境構築
## 1. WSL2(Ubuntu 20.04)
:::note info
WSL2を選んだ理由
私の前提条件を満たしているGPUがWindowsメインマシーンのみだったため、WSL2を使ってUbuntu 20.04をインストールしました。
もし、Ubuntuマシーンや仮想環境がある場合はそちらで恐らく問題ありません。
:::

下記の記事を参考にインストールしてください。

注意点は
> 3.Linux ディストリビューションをインストール

で `Ubuntu-20.04` を指定するようにしてください。

https://qiita.com/ryome/items/240f36923f5cb989da27

## 2. Anaconda
下記の記事の 
> 1.公式サイトからインストーラのダウンロード
> 2.bashを用いてインストーラを実行

を実行していただければインストール完了です。

https://qiita.com/aochan50034/items/effbc1aca64cd3bfceda#anaconda3%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B

WSLで環境を構築している方は、この記事を参考にshファイルを移動させて実行させてください。

https://qiita.com/quzq/items/1096c638c0d86795be13

## 3. 2d-gaussian-splatting
```bash:bash
# download
git clone https://github.com/hbb1/2d-gaussian-splatting.git --recursive
cd 2d-gaussian-splatting/

# if you have an environment used for 3dgs, use it
# if not, create a new environment
conda env create --file environment.yml
```
:::note warn
警告
2024/12/1現在の方法です。
引用元
https://github.com/hbb1/2d-gaussian-splatting
:::

# 素材データ準備
## 1. RealityCaptureからColmapを出力
RealityCapture 1.5のアップデートでColmapの出力に対応しました。
今回はそれを活用します。

撮影した画像を用意
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212330/9e1d6af5-4b14-bf54-ea93-82d77d85c01f.png)
RealityCaptureに撮影した画像を入れてアライメント処理を実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212330/19d0b001-5168-2f5f-55a0-81d128e67e7b.png)
無事アライメントがうまくいったら
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212330/2aa13592-34d2-bcfa-91d5-f2443dd76b72.png)
ワークフローの3.OutputにあるExportを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212330/1a5fa7a2-754c-ab47-0e7e-be6bb30c0780.png)
Camera AlignmentにあるColmap Text Formatを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212330/ef5be6ef-a4f8-cafc-2098-5061d6f3f8f2.png)
エクスポート先を選択後にパラメータを選択する画面が出てくるので写真のように変更
画像のエクスポートを忘れずに
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212330/f7c9e330-171f-0a33-ecbb-77a0a39fcfea.png)
エクスポートされたデータ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212330/f68832d9-b4fd-0124-5480-96f265be4982.png)

## 2. Cameraタイプの変更
RealityCaptureから出力された`Camera.txt`に書いてあるCameraタイプ`SIMPLE_RADIAL`では2DGSで処理することは非対応
対応している`SIMPLE_PINHOLE`か`PINHOLE`に変更する

`SIMPLE_RADIAL`と`SIMPLE_PINHOLE`の違いは歪の値の有無らしい
RealityCaptureから出力された`Camera.txt`にはすべて歪0と書かれているので、
カメラタイプの変更と行の最後にある０を消せばいい

参考元

https://colmap.github.io/cameras.html

```:Camera.txt(一部)
# Camera list with one line of data per camera:
#   CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[]
# Number of cameras: 61
61 SIMPLE_RADIAL 9953 6469 4382.761334473909 4976.5 3234.5 0
46 SIMPLE_RADIAL 9097 6049 8985.572991559862 4548.5 3024.5 0
47 SIMPLE_RADIAL 9180 6082 8987.095179233622 4590 3041 0
58 SIMPLE_RADIAL 9119 6055 8983.253200124884 4559.5 3027.5 0
51 SIMPLE_RADIAL 9177 6073 9018.405000000001 4588.500000000001 3036.5 0
54 SIMPLE_RADIAL 9107 5958 9019.688203418091 4553.5 2979 0
60 SIMPLE_RADIAL 9119 6113 8990.707520891365 4559.5 3056.5 0
53 SIMPLE_RADIAL 9115 6109 8977.094300518134 4557.5 3054.5 0
56 SIMPLE_RADIAL 9141 6106 8985.974605140911 4570.5 3053 0
50 SIMPLE_RADIAL 9115 6135 8986.905055487054 4557.5 3067.5 0
59 SIMPLE_RADIAL 9127 6116 8992.640265394983 4563.5 3058 0
57 SIMPLE_RADIAL 9110 6132 9007.6404494382 4554.999999999999 3066 0
55 SIMPLE_RADIAL 9133 6095 9020.996882145086 4566.5 3047.5 0
48 SIMPLE_RADIAL 9155 6065 9003.711340206186 4577.5 3032.5 0
52 SIMPLE_RADIAL 9101 6048 8967.26864274571 4550.5 3024 0
49 SIMPLE_RADIAL 9120 6096 8991.336099585062 4559.999999999999 3048 0
37 SIMPLE_RADIAL 9115 6046 8990.13698630137 4557.5 3023 0
...
..
.

```
ChatGPTの力を借りて変更プログラムを作りました。
```bash:bash
vim convert_cameras.py
```

```python:convert_cameras.py
import sys

def convert_camera_file(input_file, output_file):
    with open(input_file, "r") as file:
        lines = file.readlines()

    with open(output_file, "w") as file:
        for line in lines:
            # コメントや空行はそのまま出力
            if line.startswith("#") or line.strip() == "":
                file.write(line)
                continue

            # カメラパラメータを解析
            parts = line.strip().split()
            if parts[1] == "SIMPLE_RADIAL":
                camera_id, model, width, height, fx, cx, cy, k1 = parts
                k1 = float(k1)  # k1 を数値に変換

                # k1 が 0 の場合は SIMPLE_PINHOLE に変換
                if k1 == 0.0:
                    file.write(f"{camera_id} SIMPLE_PINHOLE {width} {height} {fx} {cx} {cy}\n")
                else:
                    # k1 が 0 でない場合はそのまま書き出し
                    file.write(line)
            else:
                # 他のモデルはそのまま書き出し
                file.write(line)

# コマンドライン引数の処理
if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python convert_cameras.py <input_file> <output_file>")
        sys.exit(1)

    input_file = sys.argv[1]
    output_file = sys.argv[2]

    convert_camera_file(input_file, output_file)
    print(f"Conversion completed! Output saved to: {output_file}")
```
Windows・Ubuntuどちらでもいいので下記のコマンドを打ってプログラムを実行

```bash:bash
python convert_cameras.py Cameras.txt Cameras.txt
```

## 3. txtからbinに変換
`colmap`コマンドを実行すれば簡単にできます。
aptに登録されているらしいので簡単にインストールできます。
```bash:bash
sudo apt update
sudo apt install colmap
```

```bash:bash
colmap model_converter --input_path <path/to/colmap_txt> --output_path <path/to/colmap_bin> --output_type BIN
```

WSLで環境を構築している方は、この記事を参考に`<path/to/colmap_txt>`や`<path/to/colmap_bin>`を指定してください。

https://qiita.com/quzq/items/1096c638c0d86795be13

## 4. Colmapデータを準備
[ここ](https://github.com/graphdeco-inria/gaussian-splatting?tab=readme-ov-file#processing-your-own-scenes)に従ってデータを各ディレクトリに配置

何故か、公式のドキュメントにtxtデータの配置位置が書かれていませんでした？
```:素材データ構造
<location>
|---images
|   |---<image 0>
|   |---<image 1>
|   |---...
|---sparse
    |---0
        |---cameras.bin
        |---images.bin
        |---points3D.bin
        |---cameras.txt
        |---images.txt
        |---points3D.txt
```
:::note warn
警告
2024/12/1現在のフォルダ構造です。
引用元
https://github.com/graphdeco-inria/gaussian-splatting?tab=readme-ov-file#processing-your-own-scenes
:::

### データ配置方法

① `images`と`sparse`を作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212330/5c6c34f9-2c69-f53f-b601-78036a7b2356.png)
② `images`に画像データを移動
③ `sparse`フォルダの中に`0`フォルダを作成
④ `0`フォルダ内に以下のファイルを配置
```
0
|---cameras.bin
|---images.bin
|---points3D.bin
|---cameras.txt
|---images.txt
|---points3D.txt
```

# Gaussian Splatting処理とメッシュ化
環境構築のCondaコマンドで作成した仮想環境`surfel_splatting`をアクティブに
```bash:bash
conda activate surfel_splatting
```
`git clone`した`2d-gaussian-splatting`フォルダーに戻り下記のプログラムを実行
```bash:bash
cd 2d-gaussian-splatting/

python train.py -s <path/to/colmap> -m ./output/GS2mesh/ -r 3
python render.py -s <path/to/colmap> -m ./output/GS2mesh/ -r 3
```
素材の画像が10Kの解像度の場合、`-r 4`以下にすると3090TiのVRAM24GBではメモリが足りずできませんでした。
もし各プログラムのオプションを知りたい場合は下記のHelpオプションをつければ確認できます。
```bash:bash
python train.py -h
python render.py -h
```

:::note warn
警告
2024/12/1現在の方法です。
引用元
https://github.com/hbb1/2d-gaussian-splatting
:::

# 実行結果
下記のディレクトリにPlyファイルがあるので、それをBlenderなどで開きます。
```
output/GS2mesh/point_cloud/iteration_30000/point_cloud.ply
```

メッシュ化までなので正確にはわかりませんが、炎のようなものがありました！

https://x.com/VRTAKAYAN/status/1860073872315277351

# 最後に
ガウシアンからメッシ化する手順を解説しました。
フォトグラメトリと比べていくつもの工程があり、工数がかさみますが、
フォトグラメトリにできなかった表現ができるといいですね！
実際に炎のメッシュ？みたいなのが生成されており、これからの進歩に期待です。

モチベーションやリソースに余裕があれば、テクスチャ生成や他のメッシュ化方法についても取り上げていこうかなと考えています。

モチベーションにつながるのでいいねを押して頂けると助かります。





3Dスキャン Advent Calendar 2024に記事を投稿してくださる方を募集しています。
3Dスキャン関連であれば何でも大丈夫なのでよろしくお願いいたします！

https://qiita.com/advent-calendar/2024/3d-scan