この記事では、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
クラスのインスタンスを作成しています。vtkCutter
はvtkDataSet
クラスを継承したクラスに使用することができます。例えば、代表的なものとして次のクラスがあります。
クラス名 | 説明 | 特徴 |
---|---|---|
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 | 円柱断面 |
vtkImplicitBoolean | vtkImplicitFunction の論理演算 |
次に、vtkCutter
クラスのSetInputData
メソッドで、スライスされる対象データを指定します。
既に述べたとおりvtkDataSet
クラスの派生クラス(vtkPolyData
、vtkStructuredGrid
、vtkUnstructuredGrid
、vtkImageData
など)を指定できます。
そして、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次元スカラー場を任意の平面でスライスし、その断面にスカラー値を色で表示する方法を紹介しました。
vtkPlane
とvtkCutter
によるスライス処理- スカラー値の位置に応じた設定(点 or セル)
- スライス結果を
vtkDataSetMapper
で色表示
3次元データはデータ量が多いため、断面にすることで理解の助けになります。ご参考になりましたら幸いです。