3Dndarrayのボクセルを3Dメッシュに保存する

下のような3次元のNdarrayのボクセルデータをstlなどの3Dデータに変換する

# ボクセルデータ
```
np.array(
    [[[0, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 1]],
     [[0, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 1],
      [0, 0, 1, 2]],
     [[0, 0, 0, 0],
      [0, 0, 0, 1],
      [0, 0, 1, 2],
      [0, 1, 2, 2]],
     [[0, 0, 0, 1],
      [0, 0, 1, 2],
      [0, 1, 2, 2],
      [1, 2, 2, 1]],
     [[0, 0, 1, 2],
      [0, 1, 2, 2],
      [1, 2, 2, 1],
      [2, 2, 1, 0]],
     [[0, 1, 2, 2],
      [1, 2, 2, 1],
      [2, 2, 1, 0],
      [2, 1, 0, 0]],
     [[1, 2, 2, 1],
      [2, 2, 1, 0],
      [2, 1, 0, 0],
      [1, 0, 0, 0]],
     [[2, 2, 1, 0],
      [2, 1, 0, 0],
      [1, 0, 0, 0],
      [0, 0, 0, 0]],
     [[2, 1, 0, 0],
      [1, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 0]],
     [[1, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 0]]])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1680015/b4bfb23f-252f-60e4-d7fe-11c5f833f2fc.png)

# コード
```voxcel2mesh.py
import numpy as np
import pyvista as pv


voxcel_data = np.array(
    [[[0, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 1]],
     [[0, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 1],
      [0, 0, 1, 2]],
     [[0, 0, 0, 0],
      [0, 0, 0, 1],
      [0, 0, 1, 2],
      [0, 1, 2, 2]],
     [[0, 0, 0, 1],
      [0, 0, 1, 2],
      [0, 1, 2, 2],
      [1, 2, 2, 1]],
     [[0, 0, 1, 2],
      [0, 1, 2, 2],
      [1, 2, 2, 1],
      [2, 2, 1, 0]],
     [[0, 1, 2, 2],
      [1, 2, 2, 1],
      [2, 2, 1, 0],
      [2, 1, 0, 0]],
     [[1, 2, 2, 1],
      [2, 2, 1, 0],
      [2, 1, 0, 0],
      [1, 0, 0, 0]],
     [[2, 2, 1, 0],
      [2, 1, 0, 0],
      [1, 0, 0, 0],
      [0, 0, 0, 0]],
     [[2, 1, 0, 0],
      [1, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 0]],
     [[1, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 0]]])

grid = pv.ImageData()
grid.dimensions = np.array(voxcel_data.shape) + 1
grid.origin = (0, 0, 0)
grid.spacing = (1, 1, 1)
grid.cell_data["values"] = voxcel_data.flatten(order="F")  

threshed = grid.threshold(value=1)

surf = threshed.extract_surface()

surf.save("mesh.stl")

pl = pv.Plotter()
pl.add_mesh(grid, style = 'wireframe')
pl.add_mesh(surf)
pl.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1680015/7ca52063-c72b-f208-d4e8-fbc140a08845.png)

メッシュになりました

# 解説
```.py
voxcel_data = np.array(
~~
)
```
3次元ボクセルデータ好きな形状を定義してください

```.py
grid = pv.ImageData()
grid.dimensions = np.array(voxcel_data.shape) + 1
grid.origin = (0, 0, 0)
grid.spacing = (1, 1, 1)
```
グリッドデータのクラスの定義、縦横奥行、原点、ボクセルサイズの指定

```.py
grid.cell_data["values"] = voxcel_data.flatten(order="F")  
```
グリッドのセルに3Dndarrayの数値データを格納
```.py
threshed = grid.threshold(value=1)
```
value以下の値のセルを削除
今回のデータの場合
value = 0
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1680015/25ba0651-1297-c11a-abf4-e0cbb3767fb9.png)
value = 1
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1680015/0e533071-cf46-644f-bd80-2a3f5c5ad67b.png)
value = 2
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1680015/156123c9-cfe0-4e49-d39c-5eab42570343.png)
```.py
surf = threshed.extract_surface()
```
ボクセルデータからメッシュを作成します

```.py
surf.save("mesh.stl")
```
メッシュデータを保存します

```.py
pl = pv.Plotter()
pl.add_mesh(grid, style = 'wireframe')
pl.add_mesh(surf)
pl.show()
```
3Dモデルをプロットします


以上

# 参考文献
https://docs.pyvista.org/examples/00-load/create-uniform-grid

https://docs.pyvista.org/api/core/_autosummary/pyvista.datasetfilters.threshold