一:主要的知识点
1、说明
本文只是教程内容的一小段,因博客字数限制,故进行拆分。主教程链接:vtk教程------逐行解析官网所有Python示例-CSDN博客
2、知识点纪要
本段代码主要涉及的有①vtkMultiThreshold工作原理,②vtkSelectEnclosedPoints 判断一个数据集 (polyData1) 中的点是否位于另一个封闭曲面 (polyData2) 的内部
二:代码及注释
python
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonDataModel import vtkDataObject
from vtkmodules.vtkCommonTransforms import vtkTransform
from vtkmodules.vtkFiltersGeneral import vtkMultiThreshold, vtkTransformPolyDataFilter
from vtkmodules.vtkFiltersModeling import vtkSelectEnclosedPoints
from vtkmodules.vtkIOGeometry import (
vtkBYUReader,
vtkOBJReader,
vtkSTLReader
)
from vtkmodules.vtkIOLegacy import vtkPolyDataReader
from vtkmodules.vtkIOPLY import vtkPLYReader
from vtkmodules.vtkIOXML import vtkXMLPolyDataReader
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkDataSetMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
import os
def ReadPolyData(file_name):
path, extension = os.path.splitext(file_name)
extension = extension.lower()
if extension == '.ply':
reader = vtkPLYReader()
reader.SetFileName(file_name)
reader.Update()
poly_data = reader.GetOutput()
elif extension == '.vtp':
reader = vtkXMLPolyDataReader()
reader.SetFileName(file_name)
reader.Update()
poly_data = reader.GetOutput()
elif extension == '.obj':
reader = vtkOBJReader()
reader.SetFileName(file_name)
reader.Update()
poly_data = reader.GetOutput()
elif extension == '.stl':
reader = vtkSTLReader()
reader.SetFileName(file_name)
reader.Update()
poly_data = reader.GetOutput()
elif extension == '.vtk':
reader = vtkPolyDataReader()
reader.SetFileName(file_name)
reader.Update()
poly_data = reader.GetOutput()
elif extension == '.g':
reader = vtkBYUReader()
reader.SetGeometryFileName(file_name)
reader.Update()
poly_data = reader.GetOutput()
else:
# Return a None if the extension is unknown.
poly_data = None
return poly_data
def main():
fn1 = "Data/cow.g"
fn2 = 0
polyData1 = ReadPolyData(fn1)
# 根据polyData1进行旋转得到polyData2
center = polyData1.GetCenter()
transform = vtkTransform()
transform.Translate(center[0], center[1], center[2])
transform.RotateY(90.0)
transform.Translate(-center[0], -center[1], -center[2])
transformPD = vtkTransformPolyDataFilter()
transformPD.SetTransform(transform)
transformPD.SetInputData(polyData1)
transformPD.Update()
polyData2 = transformPD.GetOutput()
"""
vtkSelectEnclosedPoints 判断一个数据集 (polyData1) 中的点是否位于另一个封闭曲面 (polyData2) 的内部
"""
select = vtkSelectEnclosedPoints()
"""
SetInputData 设置输入数据集。这个数据集包含你想要进行判断的点。
"""
select.SetInputData(polyData1)
"""
SetSurfaceData 设置参考曲面。这个数据集必须是一个封闭的、流形的多边形网格
"""
select.SetSurfaceData(polyData2)
"""
vtkMultiThreshold 是 VTK 里一个 阈值过滤器 (threshold filter)
它的特点是 可以在一次操作里设置多个阈值条件,并根据这些条件把输入数据分成多个输出结果
普通的 vtkThreshold 只能设置一个阈值范围,比如提取标量在 [100, 200] 之间的单元格。
vtkMultiThreshold 则可以定义 多个规则,一次性生成 多个子数据集,省去了重复调用多个 vtkThreshold 的麻烦
工作原理:
你给它输入一个 数据集 (vtkDataSet),比如 vtkUnstructuredGrid 或 vtkImageData。
你定义多个阈值条件,比如:
规则 1:提取标量 < 50
规则 2:提取 50 ≤ 标量 < 100
规则 3:提取 标量 ≥ 100
它会输出多个数据集,每个对应一个规则。
"""
threshold = vtkMultiThreshold()
"""
AddBandpassIntervalSet 作用是定义一个过滤规则,用于从数据中提取出特定范围内的单元(cells)
这段代码的具体含义是:
threshold.AddBandpassIntervalSet(): 调用 vtkMultiThreshold 过滤器的这个方法,来创建一个"通带"区间集,即只保留满足特定条件的单元。
0, 0: 定义了数据值的范围。这里表示数据值必须等于 0。
vtkDataObject.FIELD_ASSOCIATION_POINTS: 指定要检查的数据是点数据(point data)。
'SelectedPoints': 指定要检查的数据数组的名称。这是 vtkSelectEnclosedPoints 过滤器在前面步骤中创建的那个数组。
0, 1: 定义了一个单元中点值满足条件的数量范围。这里表示,要被选中的单元,其所有点(0 到 1)的 'SelectedPoints' 值都必须在前面定义的 [0, 0] 范围内。
"""
outsideId = threshold.AddBandpassIntervalSet(0, 0, # 区间范围:下限=0,上限=0
vtkDataObject.FIELD_ASSOCIATION_POINTS, # 作用在点数据上
"SelectedPoints", # 要筛选的数组名字
0, 1) # includeLower=0(不包含下界),includeUpper=1(包含上界)
"""
具体含义
0, 0
→ 选取 "SelectedPoints" 数组值在 [0, 0] 之间的点,也就是值恰好等于 0 的点。
(因为 vtkSelectEnclosedPoints 的 "SelectedPoints" 标量:内部点 = 1 外部点 = 0)
vtkDataObject.FIELD_ASSOCIATION_POINTS
→ 指定这个筛选条件是作用在 点数据 (point data) 上,而不是单元(cell data)。
"SelectedPoints"
→ 要筛选的数组名字,就是 vtkSelectEnclosedPoints 生成的标量数组。
在这段代码里,它之所以是这个名字,是因为 vtkSelectEnclosedPoints 默认就会生成一个标量数组,名字叫 "SelectedPoints"
如果不知道数组名字,可以先查看数组里有哪些数组
pointData = polydata.GetPointData()
for i in range(pointData.GetNumberOfArrays()):
print(pointData.GetArrayName(i))
单元数据就是下面这样写
cellData = polydata.GetCellData()
for i in range(cellData.GetNumberOfArrays()):
print(cellData.GetArrayName(i))
component = 0
指定数组的第几个分量(例如 0 = x, 1 = y, 2 = z) 如果数组是标量,就写 0
allScalars = 1
当数组有多个分量时,是否要求所有分量都满足区间条件
0 → 只检查指定的 component 分量
1 → 要求数组的所有分量都在 [xmin, xmax] 内
上边界和下边界就是第0和第1个索引所代表的两个数
所以这里实际上是筛选出 "SelectedPoints" == 0 的点。
返回值 outsideId 返回的是这个阈值区间的 ID,可以用来后续控制(比如启用/禁用某个条件)
"""
insideId = threshold.AddBandpassIntervalSet(1, 1, vtkDataObject.FIELD_ASSOCIATION_POINTS, "SelectedPoints", 0, 1)
"""
AddIntervalSet 寻找那些至少有一个点在内部(值1),同时至少有一个点在外部(值0)的单元。这些单元恰好就是位于边界上的单元
0, 1: 定义了数据值范围。这个区间是 [0, 1],表示所有值为 0 或 1 的点都符合条件
OPEN参数:定义了区间的开闭类型,所以 [0, 1] 在这里实际上指的是所有大于 0 且小于 1 的值。但是,由于我们前面 vtkSelectEnclosedPoints 过滤器生成的 'SelectedPoints' 数组值只有 0 和 1,所以这里的 OPEN 会使我们定义的规则变得特殊
0, 0:当与 vtkMultiThreshold.OPEN, vtkMultiThreshold.OPEN 结合使用时,它会检查一个单元中既有值在 0-1 范围内的点(即 0 或 1),又有值不在 0-1 范围内的点。在这里,它用来捕捉那些**既有值为 0 的点(外部),又有值为 1 的点(内部)**的单元
"""
borderId = threshold.AddIntervalSet(0, 1, vtkMultiThreshold.OPEN, vtkMultiThreshold.OPEN,
vtkDataObject.FIELD_ASSOCIATION_POINTS, 'SelectedPoints', 0, 0)
threshold.SetInputConnection(select.GetOutputPort())
"""
OutputSet 设置最终要输出的数据集
"""
threshold.OutputSet(outsideId)
threshold.OutputSet(insideId)
threshold.OutputSet(borderId)
threshold.Update()
colors = vtkNamedColors()
outsideColor = colors.GetColor3d('Crimson')
insideColor = colors.GetColor3d('Banana')
borderColor = colors.GetColor3d('Mint')
surfaceColor = colors.GetColor3d('Peacock')
backgroundColor = colors.GetColor3d('Silver')
outsideMapper = vtkDataSetMapper()
outsideMapper.SetInputData(threshold.GetOutput().GetBlock(outsideId).GetBlock(0))
outsideMapper.ScalarVisibilityOff()
outsideActor = vtkActor()
outsideActor.SetMapper(outsideMapper)
outsideActor.GetProperty().SetDiffuseColor(outsideColor)
outsideActor.GetProperty().SetSpecular(.6)
outsideActor.GetProperty().SetSpecularPower(30)
insideMapper = vtkDataSetMapper()
insideMapper.SetInputData(threshold.GetOutput().GetBlock(insideId).GetBlock(0))
insideMapper.ScalarVisibilityOff()
insideActor = vtkActor()
insideActor.SetMapper(insideMapper)
insideActor.GetProperty().SetDiffuseColor(insideColor)
insideActor.GetProperty().SetSpecular(.6)
insideActor.GetProperty().SetSpecularPower(30)
insideActor.GetProperty().EdgeVisibilityOn()
borderMapper = vtkDataSetMapper()
borderMapper.SetInputData(threshold.GetOutput().GetBlock(borderId).GetBlock(0))
borderMapper.ScalarVisibilityOff()
borderActor = vtkActor()
borderActor.SetMapper(borderMapper)
borderActor.GetProperty().SetDiffuseColor(borderColor)
borderActor.GetProperty().SetSpecular(.6)
borderActor.GetProperty().SetSpecularPower(30)
borderActor.GetProperty().EdgeVisibilityOn()
surfaceMapper = vtkDataSetMapper()
surfaceMapper.SetInputData(polyData2)
surfaceMapper.ScalarVisibilityOff()
surfaceActor = vtkActor()
surfaceActor.SetMapper(surfaceMapper)
surfaceActor.GetProperty().SetDiffuseColor(surfaceColor)
surfaceActor.GetProperty().SetOpacity(.1)
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.AddRenderer(renderer)
renderWindow.SetSize(640, 480)
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderer.SetBackground(backgroundColor)
renderer.UseHiddenLineRemovalOn()
renderer.AddActor(surfaceActor)
renderer.AddActor(outsideActor)
renderer.AddActor(insideActor)
renderer.AddActor(borderActor)
renderWindow.SetWindowName('CellsInsideObject')
renderWindow.Render()
renderer.GetActiveCamera().Azimuth(30)
renderer.GetActiveCamera().Elevation(30)
renderer.GetActiveCamera().Dolly(1.25)
renderWindow.Render()
renderWindowInteractor.Start()
if __name__ == '__main__':
main()