TECHNOLOGY

PythonでVTKファイルの3次元データの断面(スライス)を取得する方法(スカラー値)

この記事では、VTK形式の3Dデータファイルを読み込み、特定の位置・方向で切断(スライス)して、その断面に含まれるスカラー値の分布を色付きで表示する方法を解説します。

目次

はじめに

VTK形式の3次元データのスライス処理にはvtkCutterを使用します。次の流れで処理を行います。

内容説明
VTKファイルの読み込みVTK形式のファイルをPythonで読み込みます。
データの取得メッシュに付随する「点」や「セル」のスカラー値(例えば、温度や圧力)をファイルから取得します。
任意のスライス平面の定義XYZ空間中の任意の位置、角度の平面を定義します。
スライス処理定義した平面で、3Dデータを2Dの断面として切り出します。
VTKによる描画用のパイプラインの構成VTKをGUIで描画するためのパイプラインを構成します。
可視化GUI上の断面画像を描画します。

pipコマンドで次のパッケージをインストールします。

pip install vtk

VTKファイルの描画の流れ

VTKファイルの描画の流れについては、こちらの記事を参照ください。

参考
VTKの概要とPythonによる読み込み手順、可視化例

3次元データの可視化に広く使用されているVTKファイルの概要と可視化の流れ(パイプライン)について説明します。そして、このパイプラインをPythonコードで表現し、VTKファイルを読み込む方法とウィンドウ上に可視化する方法、画像ファイルとして保存する方法を示します。

続きを見る

平面の定義とスライスの実行方法

VTKでは、3次元空間中のデータ「任意の平面でスライス」するには、次の2つのクラスを使います。

クラス説明
vtkPlane断面の位置と方向を定義します。具体的には、平面が通る座標とその法線ベクトルを決めると、その平面を定義できます。
vtkCutter定義された平面で実際にデータを切り取ります。

次のように平面を定義します。

plane = vtk.vtkPlane()
plane.SetOrigin(0, 0, 25)  # 通る点(平面の位置)
plane.SetNormal(0, 1, 1)   # 法線ベクトル(平面の傾き)

SetOriginメソッドにより平面が通る一点を設定しています。3つの引数はそれぞれ座標(X, Y, Z)です。そして、SetNormalメソッドにより平面の法線ベクトルを与えることで、平面の傾きを決めています。3つの引数はそれぞれ法線ベクトルの成分(Nx, Ny, Nz)です。

スライスの実行は次のように実施します。

cutter = vtk.vtkCutter() # vtkCutterクラスのインスタンスを作成する
cutter.SetCutFunction(plane) # 定義した平面をセットする
cutter.SetInputData(dataset) # カットする元データ(3次元データ)をセットする
cutter.Update()
sliced_output = cutter.GetOutput()

vtkCutterクラスのインスタンスを作成しています。vtkCuttervtkDataSetクラスを継承したクラスに使用することができます。例えば、代表的なものとして次のクラスがあります。

クラス名説明特徴
vtkPolyData点、線、ポリゴンの集合体表面メッシュ、点群、境界線などの可視化に使用されます。
vtkImageData等間隔の格子状データ(ボクセル)CTやMRIなどの医用画像、ボリュームデータに対応しています。
vtkStructuredGrid格子状だが点の位置は自由に変形可能(構造格子)流体解析などで空間が変形した格子に使用されます。
vtkUnstructuredGridセルの形状がバラバラなメッシュ(非構造格子)有限要素法(FEM)や流体の複雑形状のシミュレーションに使われます。

vtkCutterが使えるかどうかは、次のように確認できます。

if dataset.IsA("vtkDataSet"):
    print("このデータはvtkCutterでスライス可能です!")
else:
    print("このデータはvtkCutterには使えません。")

次に、vtkCutterクラスのsetCutFunctionメソッドは、どこを切るかを定義します。

ここでは、vtkPlaneで定義した平面を設定します。その他にもvtkImplicitFunctionの派生クラスであれば引数に設定できます。例えば、次のようなクラスがあります。あくまで表面でスライスされるイメージで内側に含まれた部分を抽出するわけではありません。

クラス名説明
vtkPlane一般的な平面スライス
vtkSphere球状断面
vtkBox直方体の面でカット
vtkCylinder円柱断面
vtkImplicitBooleanvtkImplicitFunctionの論理演算

次に、vtkCutterクラスのSetInputDataメソッドで、スライスされる対象データを指定します。

既に述べたとおりvtkDataSetクラスの派生クラス(vtkPolyDatavtkStructuredGridvtkUnstructuredGridvtkImageDataなど)を指定できます。

そして、vtkCutterクラスのUpdateメソッドで、パイプラインを更新し、スライス処理を実行します。

最後に、vtkCutterクラスのGetOutputメソッドで、スライス結果を取得します。返ってくるのはvtkPolyData(点・線・三角形などの集合)になります。スライスの結果に含まれる情報は以下になります。

項目内容
断面上にある座標点
セル断面を構成するポリゴン(三角形など)
属性(スカラー値/ベクトル値)元データの点/セルに付随していたスカラーやベクトル(自動的に引き継がれる)

これをvtkDataSetMapperに渡せば、描画することができます。

サンプルファイルの作成

スライスをテストするためのVTKファイルを作成するためのサンプルコードを以下に示します。numpyパッケージを使っているため、必要に応じてpip install numpyでインストールしてください。実行すると、sample.vtkという名前のファイルが作成されます。

import vtk
import numpy as np

# 構造格子のサイズ
nx, ny, nz = 50, 50, 50

# スカラー値(例:中心からの距離の2乗)
scalars = np.zeros((nx, ny, nz))
for k in range(nz):
    for j in range(ny):
        for i in range(nx):
            x = i - nx / 2
            y = j - ny / 2
            z = k - nz / 2
            scalars[i, j, k] = x**2 + y**2 + z**2

# vtkImageDataに変換
image_data = vtk.vtkImageData()
image_data.SetDimensions(nx, ny, nz)
image_data.SetSpacing(1.0, 1.0, 1.0)

# VTK用スカラー配列に変換
flat_scalars = scalars.ravel(order="F")  # VTKはFortran順
vtk_array = vtk.vtkFloatArray()
vtk_array.SetName("Distance2")
vtk_array.SetNumberOfValues(len(flat_scalars))
for i, val in enumerate(flat_scalars):
    vtk_array.SetValue(i, val)

# スカラーをセット
image_data.GetPointData().SetScalars(vtk_array)

# ファイルに保存
writer = vtk.vtkStructuredPointsWriter()
writer.SetFileName("sample.vtk")
writer.SetInputData(image_data)
writer.Write()

sample.vtkファイルは、(50, 50, 50)のサイズの構造格子で、中心からの距離の二乗を計算したDistance2という名前のスカラー値を各点に保存しています。

3次元データの断面(スライス)の可視化

最後に、サンプルのVTKファイルsample.vtkを使って、スライス断面の可視化を行ってみます。

本記事では説明していない部分(パイプラインの流れなど)についてはこちらの記事に記載しています。

import vtk

# VTKファイルのパスを指定
vtk_file_path = "sample.vtk"

# 可視化に使うスカラー値のフィールド名(VTK内に定義された名前)
field_name = "Distance2"

# スカラー値の定義場所:点(point)またはセル(cell)に対する値かを指定
# サンプルファイルでは、点にスカラー値が定義されている
field_position = "point"
# field_position = "cell"

# VTKファイルを読み込むためのリーダーを作成し、ファイルを読み込む
reader = vtk.vtkDataSetReader()
reader.SetFileName(vtk_file_path)
reader.Update()
dataset = reader.GetOutput() # 読み込まれたデータセット(メッシュ+属性)を取得

# 指定したフィールド名に対応するスカラー配列を取得
if field_position == "cell":
    field = dataset.GetCellData().GetArray(field_name)
elif field_position == "point":
    field = dataset.GetPointData().GetArray(field_name)
else:
    assert 0 # エラーチェック("point" か "cell" 以外は不正)

# スカラー値の最小値・最大値(カラーマップの範囲)を取得
field_range = field.GetRange()

# スライスする平面を定義:ここではZ=25の位置で、(0,1,1)方向に垂直な面
plane = vtk.vtkPlane()
plane.SetOrigin(0, 0, 25)  # 平面が通る座標
plane.SetNormal(0, 1, 1)  # 平面の傾き(法線ベクトルで指定)

# vtkCutter を使って、定義した平面でデータをスライス
cutter = vtk.vtkCutter()
cutter.SetCutFunction(plane) # 定義した平面をセットする
cutter.SetInputData(dataset) # カットする元データ(3次元データ)をセットする
cutter.Update()
sliced_output = cutter.GetOutput()

# スカラー値に応じて色をつけるマッパー(描画設定)
mapper = vtk.vtkDataSetMapper()
mapper.SetInputData(sliced_output)
mapper.ScalarVisibilityOn()  # スカラー値による色分けを有効に

# スカラー値の位置(点 or セル)に応じてマッピング設定を切り替え
if field_position == "cell":
    mapper.SetScalarModeToUseCellData()
elif field_position == "point":
    mapper.SetScalarModeToUsePointData()
else:
    assert 0
# カラーマップの範囲を指定(最小値~最大値)
mapper.SetScalarRange(field_range)
mapper.SetColorModeToMapScalars() # スカラー値を色にマッピングするように設定

# Actorを設定
actor = vtk.vtkActor()
actor.SetMapper(mapper) # マッパーを関連づける
actor.RotateX(0.0) # 必要に応じて方向調整も可能
actor.RotateY(0.0)
actor.RotateZ(0.0)

# レンダラー(描画担当)を作成し、背景色と表示内容を設定
renderer = vtk.vtkRenderer()
renderer.SetBackground(1, 1, 1) # 背景は白
renderer.AddActor(actor)

# レンダリングウィンドウを作成し、サイズを指定
window = vtk.vtkRenderWindow()
window.AddRenderer(renderer)
window.SetSize(600, 500)

# インタラクション(マウス操作など)を可能にするインタラクターを作成
interactor = vtk.vtkRenderWindowInteractor()
interactor.SetRenderWindow(window)

window.Render() # 初期描画を実行
interactor.Start() # インタラクション(回転・ズームなど)を開始

実行すると、次の結果が得られます。

まとめ

この記事では、PythonとVTKを使って、3次元スカラー場を任意の平面でスライスし、その断面にスカラー値を色で表示する方法を紹介しました。

  • vtkPlanevtkCutter によるスライス処理
  • スカラー値の位置に応じた設定(点 or セル)
  • スライス結果を vtkDataSetMapper で色表示

3次元データはデータ量が多いため、断面にすることで理解の助けになります。ご参考になりましたら幸いです。

-TECHNOLOGY
-,