PyMeshLabの使い方

# PyMeshLabとは

[Meshlab](https://www.meshlab.net/)をpythonのモジュールとして使えるようにしたものです．

詳細は[公式のドキュメント](https://pymeshlab.readthedocs.io/en/latest/index.html)を参照してください．


本記事では，以下の環境で動作確認しています．

- WSL2: Ubuntu 20.04.4 LTS
- Python 3.8.0
- pymeshlab: 2022.2.post2


# インストール


```sh
pip install pymeshlab
```

# サンプルデータのダウンロード

Meshlabのサイトからサンプルデータを[ダウンロード](https://www.meshlab.net/data/sample_meshes.zip)することができます．

本記事ではこのデータを使って説明します．

```python
import urllib
from pathlib import Path
import zipfile

url = "https://www.meshlab.net/data/sample_meshes.zip"
dst_path = Path(url).name

# ファイルダウンロード
with urllib.request.urlopen(url) as web_file, open(dst_path, 'wb') as local_file:
    local_file.write(web_file.read())

# zipファイル解凍
with zipfile.ZipFile(dst_path) as zf:
    zf.extractall(Path(dst_path).stem)
```


# 基本的な使い方

pymeshlabでは，`MeshSet`オブジェクトを作ってメッシュの処理を行います．

## データの読み込み・保存

```python
import pymeshlab

ms = pymeshlab.MeshSet()
ms.load_new_mesh('./sample_meshes/sample/bunny2.ply')

# メッシュデータを取得
m = ms.current_mesh()

# 頂点座標などをnumpy型で取得できる
v_matrix = m.vertex_matrix() # 頂点座標
f_matrix = m.face_matrix() # 面を構成する頂点ID
e_matrix = m.edge_matrix() # エッジを構成する頂点ID (このサンプルデータだと空)

# メッシュデータを保存
ms.save_current_mesh('./sample_meshes/sample/bunny2_save.ply')
```

## 複数データの読み込み

この`MeshSet`オブジェクトは，GUI版のMeshlabのProjectウィンドウに相当し，複数のメッシュデータを読み込むことができます．

```python
ms = pymeshlab.MeshSet()

# 1個目のメッシュデータ読み込み
ms.load_new_mesh('./sample_meshes/sample/bunny2.ply')
print(f"現在のメッシュID: {ms.current_mesh_id()}, データ数: {ms.number_meshes()}")

# 2個目のメッシュデータ読み込み
ms.load_new_mesh('./sample_meshes/sample/bunny2.ply')
print(f"現在のメッシュID: {ms.current_mesh_id()}, データ数: {ms.number_meshes()}")
```

```sh
現在のメッシュID: 0, データ数: 1
現在のメッシュID: 1, データ数: 2
```

カレントのメッシュIDは`set_current_mesh`で切り替えることができます．

```python
print(f"現在のメッシュID: {ms.current_mesh_id()}")
idx = 0
if ms.mesh_id_exists(idx):
    ms.set_current_mesh(idx)
    print(f"現在のメッシュID: {ms.current_mesh_id()}")
```

```sh
現在のメッシュID: 1
現在のメッシュID: 0
```

## フィルターの適用

Meshlabに実装されているデータ処理の機能をフィルターを適用する方法で使用することができます．

使用できるフィルターの一覧は[公式ドキュメントのフィルター一覧](https://pymeshlab.readthedocs.io/en/latest/filter_list.html)を参照してください．

例として，メッシュの凸包を生成する場合は，以下のようになります．

```python
ms = pymeshlab.MeshSet()
ms.load_new_mesh('./sample_meshes/sample/bunny2.ply')
ms.generate_convex_hull()
ms.save_current_mesh('./sample_meshes/sample//bunny2_convex_hull.ply')
```


## メッシュの表示

### 環境構築

[polyscope](https://polyscope.run/py/)を使用してメッシュを表示させることができます．

```sh
pip install polyscope
```

pipのバージョンが低いと以下のようなエラーが出ます。
```sh
ERROR: Command errored out with exit status 1: /home/ub20/.pyenv/versions/3.8.0/bin/python3.8 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-7u_oyqmv/polyscope/setup.py'"'"'; __file__='"'"'/tmp/pip-install-7u_oyqmv/polyscope/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-d8iiywde/install-record.txt --single-version-externally-managed --compile Check the logs for full command output.
```
この場合は、`pip install --upgrade pip`でバージョンを最新のものにしてください．
その他の詳細は[公式サイトのインストールマニュアル](https://polyscope.run/py/installing/)を参照してください．


また、WSL上で表示させようとした場合，ディスプレイの設定をしないと以下のようなエラーが出てしまいます．
```sh
>>> import polyscope as ps
>>> ps.init()
GLFW emitted error: X11: The DISPLAY environment variable is missing
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ub20/.pyenv/versions/3.8.0/lib/python3.8/site-packages/polyscope/core.py", line 14, in init
    psb.init(backend)
RuntimeError: [polyscope] ERROR: Failed to initialize glfw
```

[WSL2の導入とGUI環境の構築とsshfsしたものもgnome-openしたい!! [Ubuntu18.04/20.04] - Qiita](https://qiita.com/momomo_rimoto/items/51d533ae9529872696ce)の記事を参考にして，ディスプレイの設定を行ってください．

私の環境では以下の手順を実行して動作することが確認できました。

Windows上で
1. https://sourceforge.net/projects/vcxsrv/ からVcXsrvをダウンロード、デフォルトの設定でインストール
1. インストールしたXLaunchのソフトを起動、"Additional parameters for VcXsrv"に -acを追加
1. ファイアウォールの設定でパブリック側のアクセスを許可

WSL上で
1. `sudo apt install libgl1-mesa-dev xorg-dev`を実行
1. `~/.bashrc`ファイルに`export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0.0`のコマンドを追加して、ターミナル再起動
1. 以下のコマンドを実行し、xeyesのディスプレイが表示されることを確認
```sh
sudo apt install x11-apps
xeyes
```


### 表示

MeshSetにロードされているメッシュデータをまとめて表示できます．

```python
import pymeshlab

ms = pymeshlab.MeshSet()
ms.create_cube(size=3) # 立方体を作成するフィルタ
ms.create_sphere(radius=2, subdiv=5) # 球を作成するフィルタ
ms.show_polyscope()
```

GUIでメッシュデータごとに表示・非表示などの設定ができます．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/372008/0febdf54-af67-25b4-93ed-6b62d6506911.png)

### polyscopeのリセット

JupyterLabなどで繰り返しMeshSetの作成をしていると過去のメッシュデータがpolyscopeのビューワーにずっと残ってしまうことがあります．

その場合は，以下のようにpolyscopeに登録されたメッシュデータを一旦クリアしてから，再度MeshSetの作成をしてください．

```python
import polyscope as ps
ps.remove_all_structures() # polyscopeに登録されたメッシュデータがすべて削除される
```