この記事では、3次元データの可視化に広く使用されているVTKファイルの概要と可視化の流れ(パイプライン)について説明します。そして、このパイプラインをPythonコードで表現し、VTKファイルを読み込む方法とウィンドウ上に可視化する方法、画像ファイルとして保存する方法を示します。
目次
VTKの概要
VTK(Visualization Toolkit)は、高度な3次元可視化やデータ解析を行うためのオープンソースのソフトウェアライブラリで、BSDライセンスとなります。開発言語はC++で、Java, Python, Tclでも使用することができます。VTKはKitware社によって継続的な改良と拡張が行われ、サンディア国立研究所などが支援者および共同開発者となっています。また、科学、エンジニアリング、医学など様々な分野で広く使われるようになり、数値シミュレーションや医療画像処理、地理情報システム、コンピュータグラフィックスなどに応用されています。VTKの特徴として、以下のものがあります。
特徴 | 内容 |
多様なデータセットのサポート | 構造格子、非構造格子、ポリゴン、スカラーフィールド、ベクトルフィールドなど様々な形状やデータを効果的に可視化できます。また、3D CGプログラミングに関する専門知識や経験がなくても容易に使用でき、高速な描画が可能です。 |
カスタマイズ可能な可視化 | カラーマップ、透明度、光源、カメラの視点などのパラメータをカスタマイズすることができます。これにより、可視化結果を柔軟に調整してより洗練された可視化を行えます。 |
プラットフォームのサポート | クロスプラットフォームで動作し、Windows、macOS、Linuxなどのさまざまなオペレーションシステムで利用できます。 |
豊富な可視化手法 | カラープロット、ベクトル図、等値面、スライス表示などのさまざまな可視化手法が提供されており、これによりデータの特性を効果的に表現できます。 |
拡張性と柔軟性 | オープンソースのライブラリであり、コードを自由にカスタマイズし、拡張することができます。また、PythonやC++などのプログラミング言語から利用できるため、利用者の好みに合わせて柔軟に開発することができます。 |
VTKの基本的な概念
VTKが提供する機能は、各オブジェクトを連結してパイプラインを形成することで可視化を行います。この各オブジェクトについて説明します。
DataSet
描画対象となるデータの本体を意味します。データセットは、描画したいデータにより色々なタイプがあり、図形の部品となる形状と情報を含んでいます。例えば、格子状に並んだデータセットや、点の集まりで表されるデータセットがあります。VTKでは、これらの様々なタイプのデータセットをサポートできるようにしています。
Filter
DataSetに対して処理を行う機能を適用します。回転・並進移動、データの切断、等値面の抽出などの加工処理を行い、その結果を出力します。
Mapper
Mapperは、Datasetをグラフィックスプリミティブ(点、線、三角形など)に変換します。DataSetの情報を描画可能な形式に変換することで可視化を行う準備をします。また、DataSetをカラーマップなどの描画時の表現に対応付けます。
Actor
Actorは、Mapperにより変換されたデータを実際に描画するためのオブジェクトです。描画位置や透明度などを設定します。
Renderer
可視化ウィンドウにアクターを描画するための領域を管理し、レンダリングを行う描画エンジンになります。1つのRendererに複数のActorを追加することで、異なるデータを同じウィンドウに表示することができます。
RenderWindow
描画ウィンドウになります。Actorが描画されるウィンドウを提供します。Rendererに配置されたActorは、RenderWindow内で描画され、可視化結果が表示されます。
これらの基本的な概念を組み合わせることで、VTKを使ってデータセットの可視化や処理を行うことができます。VTKの処理は、データセットをフィルタで処理し、マッパーで描画形式に変換、アクターを通じてレンダラーを配置し、最終的にウィンドウに表示するというのが一般的です。
VTKライブラリのインストール
PythonでVTKファイルを取り扱うために、VTKライブラリをインストールします。次のコマンドをコマンドラインまたはPowerShellで実行します。
pip install vtk
Pythonの環境が構築されていない場合は、以下の記事をご参照ください。
-
WindowsのPython開発環境構築:インストールから仮想環境、Dockerの活用まで
WindowsでPythonを使用するための開発環境の構築手順について説明します。Pythonは公式ページで配布されているものを用います。そして、パッケージ管理、仮想環境、Dockerイメージ作成とプログラムの実行例を示します。
続きを見る
VTKファイルの読み込みと可視化
サンプルファイル
VTKの各オブジェクトをつなげてパイプラインを構築して可視化する手順をPythonを用いて記述します。
VTKファイルのサンプルとして、次のファイル「sample.vtk」を使用します。
このvtkファイルは、非構造格子を用いて1つの四角形を表現しています。
# vtk DataFile Version 2.0
scalar
ASCII
DATASET UNSTRUCTURED_GRID
POINTS 4 float
0 0 0
1 0 0
1 1 0
0 1 0
CELLS 1 5
4 0 1 2 3
CELL_TYPES 1
9
POINT_DATA 4
SCALARS point_scalars float
LOOKUP_TABLE default
1
2
3
4
CELL_DATA 1
SCALARS cell_scalars float
LOOKUP_TABLE default
5
ファイルの読み込みと可視化
VTKファイルを読み込んで、ウィンドウに表示するプログラムを以下に示します。
import vtk
# VTKファイルのパスを指定
vtk_file_path = "sample.vtk"
# フィールド名を指定
field_name = "point_scalars"
# フィールドの定義点を指定
field_position = "point"
#field_position = "cell"
# VTKリーダーを作成し、ファイルを読み込み
reader = vtk.vtkDataSetReader()
reader.SetFileName(vtk_file_path)
reader.Update()
# データセットを取得(vtkUnstructuredGrid, vtkPolyData, vtkStructuredGridなど)
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)
# スカラー値の値の範囲を取得します
field_range = field.GetRange()
print(field_range)
# Mapperを設定
mapper = vtk.vtkDataSetMapper()
mapper.SetInputData(dataset)
mapper.ScalarVisibilityOn()
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.)
actor.RotateY(0.)
actor.RotateZ(0.)
# Rendererを設定
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()
その結果、次のような図がウィンドウ上に表示されます。
各行について簡単に解説します。
コード | 解説 |
import vtk | vtkモジュールをインポートしてVTKライブラリを利用します。 |
vtk_file_path = "sample.vtk" | 可視化したいVTKファイルのパスを指定します。 |
field_name = "point_scalars" | 可視化したいフィールド(スカラーデータ)の名前を指定します。 フィールドの名前はVTKファイルの中で定義されています。 |
field_position = "point" | 可視化したいフィールドの定義点を指定します。 "point"の場合は格子点、"cell"の場合はセル(要素)で定義されたフィールドに対応します。 フィールドがどこで定義されているかはVTKファイルの中に記述されています。 |
reader = vtk.vtkDataSetReader() | VTKファイルを読み込むためのvtkDataSetReaderクラスのインスタンスを作成します。 |
reader.SetFileName(vtk_file_path) | ファイルパスをvtkDataSetReaderに設定します。 |
reader.Update() | vtkDataSetReaderを使用してデータセットを読み込みます。 |
dataset = reader.GetOutput() | 読み込んだデータセットを取得します。 |
field = dataset.GetCellData().GetArray(field_name) | セル(要素)で定義された指定された名前(field_name)をもつフィールドを読み込みます。 |
field = dataset.GetPointData().GetArray(field_name) | 格子点で定義された指定された名前(field_name)をもつフィールドを読み込みます。 |
field_range = field.GetRange() | スカラーフィールドの値の範囲を取得します。 |
mapper = vtk.vtkDataSetMapper() | データセットを可視化するためのvtkDataSetMapperクラスのインスタンスを作成します。 |
mapper.SetInputData(dataset) | データセットをvtkDataSetMapperに設定します。 |
mapper.ScalarVisibilityOn() | データセットのスカラーデータを可視化するようにvtkDataSetMapperに指示します。 |
mapper.SetScalarModeToUseCellData() | スカラーモードの設定(セルデータ)に応じて、 vtkDataSetMapperのスカラーモードを設定します。 |
mapper.SetScalarModeToUsePointData() | スカラーモードの設定(格子点データ)に応じて、 vtkDataSetMapperのスカラーモードを設定します。 |
mapper.SetScalarRange(field_range) | vtkDataSetMapperに対してスカラーデータの値の範囲を指定します。 field_rangeはスカラーデータの値の範囲を表す2つの値(最小値と最大値)を持つタプルです。 スカラーデータの値の範囲を設定することにより、 可視化時に使用されるカラーマッピングが正しく行われます。 |
mapper.SetColorModeToMapScalars() | vtkDataSetMapperに対してスカラーデータをマッピングして色を付けるカラーモードを指定します。 SetColorModeToMapScalars()を呼び出すことにより、 スカラーデータの値に応じてカラーマッピングが行われ、 3Dデータの可視化でスカラーデータを視覚的に表現することが可能になります。 |
actor = vtk.vtkActor() | 可視化するデータのアクター(3Dオブジェクト)を作成します。 |
actor.SetMapper(mapper) | アクターにMapperを設定します。 |
actor.RotateX(0.), actor.RotateY(0.), actor.RotateZ(0.) | アクターをX軸、Y軸、Z軸周りに回転させることができますが、 この例では回転は行っていません。 |
renderer = vtk.vtkRenderer() | 可視化を行うためのvtkRendererクラスのインスタンスを作成します。 |
renderer.SetBackground(1, 1, 1) | 背景色を白に設定します(R,G,Bの値を0から1の範囲で指定)。 |
renderer.AddActor(actor) | アクターをレンダラーに追加します。 |
window = vtk.vtkRenderWindow() | 可視化ウィンドウを作成します。 |
window.AddRenderer(renderer) | レンダラーをウィンドウに追加します。 |
window.SetSize(600, 500) | ウィンドウのサイズを指定します。 |
interactor = vtk.vtkRenderWindowInteractor() | インタラクター(マウスやキーボードイベントの処理など)を作成します。 |
interactor.SetRenderWindow(window) | インタラクターにウィンドウを設定します。 |
window.Render() | ウィンドウを描画します。 |
カラーバーの追加
カラーバーを表示します。変更部分は<< HERE ... >> HEREで囲んでいます。
import vtk
# VTKファイルのパスを指定
vtk_file_path = "sample.vtk"
# フィールド名を指定
field_name = "point_scalars"
# フィールドの定義点を指定
field_position = "point"
#field_position = "cell"
# VTKリーダーを作成し、ファイルを読み込み
reader = vtk.vtkDataSetReader()
reader.SetFileName(vtk_file_path)
reader.Update()
# データセットを取得(vtkUnstructuredGrid, vtkPolyData, vtkStructuredGridなど)
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)
# スカラー値の値の範囲を取得します
field_range = field.GetRange()
print(field_range)
# Mapperを設定
mapper = vtk.vtkDataSetMapper()
mapper.SetInputData(dataset)
mapper.ScalarVisibilityOn()
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.)
actor.RotateY(0.)
actor.RotateZ(0.)
# Rendererを設定
renderer = vtk.vtkRenderer()
renderer.SetBackground(1, 1, 1)
renderer.AddActor(actor)
# 可視化ウィンドウを作成してデータを表示します
window = vtk.vtkRenderWindow()
window.AddRenderer(renderer)
window.SetSize(600, 500)
# カラーバーを表示する << HERE
scalar_bar = vtk.vtkScalarBarActor()
scalar_bar.SetOrientationToVertical()
scalar_bar.SetLookupTable(mapper.GetLookupTable())
scalar_bar.SetWidth(0.1)
scalar_bar.SetHeight(0.5)
scalar_bar.SetPosition(0.85, 0.3)
scalar_bar_prop = vtk.vtkTextProperty()
scalar_bar_prop.SetColor(0, 0, 0)
scalar_bar_prop.SetFontSize(15)
scalar_bar_prop.SetFontFamilyToArial()
scalar_bar_prop.ItalicOff()
scalar_bar_prop.BoldOff()
scalar_bar.UnconstrainedFontSizeOn()
scalar_bar.SetTitleTextProperty(scalar_bar_prop)
scalar_bar.SetLabelTextProperty(scalar_bar_prop)
scalar_bar.SetLabelFormat("%5.2f")
scalar_bar.SetTitle(field_name)
scalar_bar.SetNumberOfLabels(5)
renderer.AddActor(scalar_bar)
# >> HERE
interactor = vtk.vtkRenderWindowInteractor()
interactor.SetRenderWindow(window)
window.Render()
interactor.Start()
結果として、次のようにカラーバーが追加されました。
変更部分について簡単に解説します。
コード | 解説 |
scalar_bar = vtk.vtkScalarBarActor() | vtkScalarBarActorのインスタンスを作成します。 これは、カラーバーを可視化するためのアクターです。 |
scalar_bar.SetOrientationToVertical() | カラーバーの方向を垂直に設定します。 これにより、カラーバーが垂直方向に表示されます。 |
scalar_bar.SetLookupTable(mapper.GetLookupTable()) | カラーバーに使用するカラーマップをセットします。 mapper.GetLookupTable()は、vtkDataSetMapperに関連付けられたカラーマップを取得します。 |
scalar_bar.SetWidth(0.1) | カラーバーの幅を設定します。 値はウィンドウの幅に対する相対的な割合で指定されます。 |
scalar_bar.SetHeight(0.5) | カラーバーの高さを設定します。 値はウィンドウの高さに対する相対的な割合で指定されます。 |
scalar_bar.SetPosition(0.85, 0.3) | カラーバーの位置をウィンドウ内で指定します。 値はウィンドウの幅と高さに対する相対的な割合で指定されます。この行では、カラーバーを右上に表示しています。 |
scalar_bar_prop = vtk.vtkTextProperty() | カラーバーのテキストプロパティを設定するためのvtkTextPropertyのインスタンスを作成します。 |
scalar_bar_prop.SetColor(0, 0, 0) | カラーバーのテキストの色を設定します。 ここでは黒色に設定しています。 |
scalar_bar_prop.SetFontSize(15) | カラーバーのテキストのフォントサイズを設定します。 ここでは15ポイントに設定しています。 |
scalar_bar_prop.SetFontFamilyToArial() | カラーバーのテキストのフォントをArialに設定します。 |
scalar_bar_prop.ItalicOff() | カラーバーのテキストのイタリックスをオフに設定します。 |
scalar_bar_prop.BoldOff() | カラーバーのテキストの太字をオフに設定します。 |
scalar_bar.UnconstrainedFontSizeOn() | カラーバーのテキストのフォントサイズを ウィンドウのサイズに依存しないように設定します。 |
scalar_bar.SetTitleTextProperty(scalar_bar_prop) | カラーバーのタイトルテキストに先に設定したvtkTextPropertyを適用します。 |
scalar_bar.SetLabelTextProperty(scalar_bar_prop) | カラーバーのラベルテキストに先に設定したvtkTextPropertyを適用します。 |
scalar_bar.SetLabelFormat("%5.2f") | カラーバーのラベルの表示フォーマットを設定します。 ここでは浮動小数点数を2桁の小数点まで表示するように設定しています。 |
scalar_bar.SetTitle(field_name) | カラーバーのタイトルを設定します。 ここではfield_name変数の値を使用して、フィールド名をタイトルとして表示しています。 |
scalar_bar.SetNumberOfLabels(5) | カラーバーに指定した数の数値ラベルを均等に表示します。 表示される数値は、カラーマップの最小値から最大値までの範囲を均等に区切った値に基づきます。 |
renderer.AddActor(scalar_bar) | カラーバーをレンダラーに追加します。 これにより、可視化ウィンドウ内にカラーバーが表示されます。 |
カラーマップの配色と透明度の指定
カラーマップのユーザー指定の方法を示します。変更部分は<< HERE ... >> HEREで囲んでいます。
import vtk
# VTKファイルのパスを指定
vtk_file_path = "sample.vtk"
# フィールド名を指定
field_name = "point_scalars"
# フィールドの定義点を指定
field_position = "point"
#field_position = "cell"
# VTKリーダーを作成し、ファイルを読み込み
reader = vtk.vtkDataSetReader()
reader.SetFileName(vtk_file_path)
reader.Update()
# データセットを取得(vtkUnstructuredGrid, vtkPolyData, vtkStructuredGridなど)
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)
# スカラー値の値の範囲を取得します
field_range = field.GetRange()
print(field_range)
# カラーバーを設定 << HERE
color_map = vtk.vtkColorTransferFunction()
color_map.SetColorSpaceToLab()
f0 = field_range[0]
f1 = field_range[1]
x = [0]*4
for count, rate in enumerate((0.0, 0.333, 0.666, 1.0), 0):
x[count] = field_range[0] + rate * (f1 - f0)
color_map.AddRGBPoint(x[0], 65.0 / 256.0, 129.0 / 256.0, 239.0 / 256.0)
color_map.AddRGBPoint(x[1], 51.0 / 256.0, 163.0 / 256.0, 83.0 / 256.0)
color_map.AddRGBPoint(x[2], 244.0 / 256.0, 184.0 / 256.0, 6.0 / 256.0)
color_map.AddRGBPoint(x[3], 228.0 / 256.0, 64.0 / 256.0, 52.0 / 256.0)
color_map.Build()
# >> HERE
# Mapperを設定
mapper = vtk.vtkDataSetMapper()
mapper.SetInputData(dataset)
mapper.ScalarVisibilityOn()
if field_position == "cell":
mapper.SetScalarModeToUseCellData()
elif field_position == "point":
mapper.SetScalarModeToUsePointData()
else:
assert(0)
mapper.SetScalarRange(field_range)
# << HERE
mapper.SetLookupTable(color_map)
# >> HERE
mapper.SetColorModeToMapScalars()
# Actorを設定
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.RotateX(0.)
actor.RotateY(0.)
actor.RotateZ(0.)
# << HERE
actor.GetProperty().SetOpacity(0.5)
# >> HERE
# Rendererを設定
renderer = vtk.vtkRenderer()
renderer.SetBackground(1, 1, 1)
renderer.AddActor(actor)
# 可視化ウィンドウを作成してデータを表示します
window = vtk.vtkRenderWindow()
window.AddRenderer(renderer)
window.SetSize(600, 500)
# カラーバーを表示する
scalar_bar = vtk.vtkScalarBarActor()
scalar_bar.SetOrientationToVertical()
# << HERE
scalar_bar.SetLookupTable(color_map)
# >> HERE
scalar_bar.SetWidth(0.1)
scalar_bar.SetHeight(0.5)
scalar_bar.SetPosition(0.85, 0.3)
scalar_bar_prop = vtk.vtkTextProperty()
scalar_bar_prop.SetColor(0, 0, 0)
scalar_bar_prop.SetFontSize(15)
scalar_bar_prop.SetFontFamilyToArial()
scalar_bar_prop.ItalicOff()
scalar_bar_prop.BoldOff()
scalar_bar.UnconstrainedFontSizeOn()
scalar_bar.SetTitleTextProperty(scalar_bar_prop)
scalar_bar.SetLabelTextProperty(scalar_bar_prop)
scalar_bar.SetLabelFormat("%5.2f")
scalar_bar.SetTitle(field_name)
scalar_bar.SetNumberOfLabels(5)
renderer.AddActor(scalar_bar)
interactor = vtk.vtkRenderWindowInteractor()
interactor.SetRenderWindow(window)
window.Render()
interactor.Start()
結果として、カラーマップの配色と透明度が変わりました。
変更部分について簡単に解説します。
コード | 解説 |
color_map = vtk.vtkColorTransferFunction() | vtkColorTransferFunctionクラスのインスタンスを作成して、 カラーマップを定義するためのオブジェクトを生成しています。 |
color_map.SetColorSpaceToLab() | カラーマップの色空間をCIE Lab色空間に設定します。 これにより、色の変化がより自然な見た目になります。 |
f0 = field_range[0] | スカラー値の範囲の最小値をf0として保存します。 |
f1 = field_range[1] | スカラー値の範囲の最大値をf1として保存します。 |
color_map.AddRGBPoint(...) | カラーマップに新しい色ポイントを追加します。 第1引数にスカラー値を、第2引数から第4引数にR、G、B成分の色情報を0.0から1.0の範囲で指定します。 |
color_map.Build() | カラーマップを構築して最終的な色のマッピングを確定します。 |
mapper.SetLookupTable(color_map) | mapperオブジェクトにカラーマップを設定します。 これにより、データセットのスカラー値がカラーマップに基づいて色に変換されます。 |
scalar_bar.SetLookupTable(color_map) | scalar_barオブジェクトにも同じカラーマップを設定します。 これにより、カラーバーの色もデータセットのスカラー値に基づいて対応する色になります。 |
actor.GetProperty().SetOpacity(0.5) | actorオブジェクトの透明度を0.5に設定します。 度が1.0の場合、図が完全に不透明になります。 透明度を0.0に設定すると、図が完全に透明になります。 0.5の場合は半透明になります。 |
PNG画像として保存
以下の行を追加すると、レンダリングされた結果をPNG形式の画像ファイルとして保存します。
...
window.Render()
# << HERE
# ウィンドウ内のレンダリングを画像ファイルに保存する
window_to_image_filter = vtk.vtkWindowToImageFilter()
window_to_image_filter.SetInput(window)
window_to_image_filter.Update()
# PNGファイルとして保存
png_writer = vtk.vtkPNGWriter()
png_writer.SetFileName("output.png")
png_writer.SetInputConnection(window_to_image_filter.GetOutputPort())
png_writer.Write()
# >> HERE
interactor.Start()
まとめ
本記事では、はじめにVTKの概要と基本オブジェクトから構成するパイプラインについて説明し、その後にPythonを用いて実際にVTKファイルを可視化するプログラムを示しました。
PythonでVTKファイルを読み込むと、Pythonで公開されている多数のライブラリを用いてデータを加工することができますので応用範囲が広がります。
ぜひご活用いただけましたら幸いです。