一:主要的知识点
1、说明
本文只是教程内容的一小段,因博客字数限制,故进行拆分。主教程链接:vtk教程------逐行解析官网所有Python示例-CSDN博客
2、知识点纪要
本段代码主要涉及的有①MarchingCubes等值面重建的原理展示
二:代码及注释
python
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkFloatArray, vtkIdList, vtkPoints
from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid, VTK_HEXAHEDRON
from vtkmodules.vtkCommonTransforms import vtkTransform
from vtkmodules.vtkFiltersCore import vtkContourFilter, vtkGlyph3D, vtkThresholdPoints, vtkTubeFilter
from vtkmodules.vtkFiltersCore import vtkExtractEdges
from vtkmodules.vtkFiltersGeneral import vtkShrinkPolyData, vtkTransformPolyDataFilter
from vtkmodules.vtkFiltersSources import (
vtkCubeSource,
vtkSphereSource
)
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
from vtkmodules.vtkRenderingFreeType import vtkVectorText
Scalars = vtkFloatArray()
def main():
cases = [case0, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13,
case14]
colors = vtkNamedColors()
rotation = 0
renderers = list()
gridSize = ((len(cases) + 3) // 4) * 4
renWin = vtkRenderWindow()
renWin.SetSize(640, 480)
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
for i in range(0, 16):
renderer = vtkRenderer()
renderers.append(renderer)
renderers[i].SetBackground(colors.GetColor3d("slate_grey"))
renWin.AddRenderer(renderer)
for i in range(0, len(cases)):
Scalars = vtkFloatArray()
Scalars.InsertNextValue(1.0)
Scalars.InsertNextValue(0.0)
Scalars.InsertNextValue(0.0)
Scalars.InsertNextValue(1.0)
Scalars.InsertNextValue(0.0)
Scalars.InsertNextValue(0.0)
Scalars.InsertNextValue(0.0)
Scalars.InsertNextValue(0.0)
Points = vtkPoints()
Points.InsertNextPoint(0, 0, 0)
Points.InsertNextPoint(1, 0, 0)
Points.InsertNextPoint(1, 1, 0)
Points.InsertNextPoint(0, 1, 0)
Points.InsertNextPoint(0, 0, 1)
Points.InsertNextPoint(1, 0, 1)
Points.InsertNextPoint(1, 1, 1)
Points.InsertNextPoint(0, 1, 1)
Ids = vtkIdList()
Ids.InsertNextId(0)
Ids.InsertNextId(1)
Ids.InsertNextId(2)
Ids.InsertNextId(3)
Ids.InsertNextId(4)
Ids.InsertNextId(5)
Ids.InsertNextId(6)
Ids.InsertNextId(7)
Grid = vtkUnstructuredGrid()
"""
第一个参数:插入10个单元格
第二个参数:每个单元格插入10个点
但是在这个按理中,每个立方体只有一个单元格 10>1
每个单元格只有8个点, 10 > 8
示例代码很明显是分配空间> 所需空间
"""
Grid.Allocate(10, 10)
Grid.InsertNextCell(VTK_HEXAHEDRON, Ids)
Grid.SetPoints(Points)
Grid.GetPointData().SetScalars(Scalars)
marching = vtkContourFilter()
marching.SetInputData(Grid)
marching.SetValue(0, 0.5)
marching.Update()
# 提取经由marching找到的等值面三角网格面的边缘
triangleEdges = vtkExtractEdges()
triangleEdges.SetInputConnection(marching.GetOutputPort())
# 将提取的边缘变粗,方便可视化
triangleEdgeTubes = vtkTubeFilter()
triangleEdgeTubes.SetInputConnection(triangleEdges.GetOutputPort())
triangleEdgeTubes.SetRadius(0.005)
triangleEdgeTubes.SetNumberOfSides(6)
triangleEdgeTubes.UseDefaultNormalOn() # 使用默认法线
triangleEdgeTubes.SetDefaultNormal(.577, .577, .577)
triangleEdgeMapper = vtkPolyDataMapper()
triangleEdgeMapper.SetInputConnection(triangleEdgeTubes.GetOutputPort())
triangleEdgeMapper.ScalarVisibilityOff()
triangleEdgeActor = vtkActor()
triangleEdgeActor.SetMapper(triangleEdgeMapper)
triangleEdgeActor.GetProperty().SetDiffuseColor(colors.GetColor3d("lamp_black"))
triangleEdgeActor.GetProperty().SetSpecular(.4) # 设置镜面反射
triangleEdgeActor.GetProperty().SetSpecularPower(10) # 设置镜面强度
aShrikner = vtkShrinkPolyData()
aShrikner.SetInputConnection(marching.GetOutputPort())
aShrikner.SetShrinkFactor(1.0)
aMapper = vtkPolyDataMapper()
aMapper.ScalarVisibilityOff()
aMapper.SetInputConnection(aShrikner.GetOutputPort())
Triangles = vtkActor()
Triangles.SetMapper(aMapper)
Triangles.GetProperty().SetDiffuseColor(colors.GetColor3d('banana'))
Triangles.GetProperty().SetOpacity(.6)
# 整体的立方体的构建
CubeModel = vtkCubeSource()
CubeModel.SetCenter(.5, .5, .5)
Edges = vtkExtractEdges()
Edges.SetInputConnection(CubeModel.GetOutputPort())
Tubes = vtkTubeFilter()
Tubes.SetInputConnection(Edges.GetOutputPort())
Tubes.SetRadius(.01)
Tubes.SetNumberOfSides(6)
Tubes.UseDefaultNormalOn()
Tubes.SetDefaultNormal(.577, .577, .577)
TubeMapper = vtkPolyDataMapper()
TubeMapper.SetInputConnection(Tubes.GetOutputPort())
CubeEdges = vtkActor()
CubeEdges.SetMapper(TubeMapper)
CubeEdges.GetProperty().SetDiffuseColor(
colors.GetColor3d('khaki'))
CubeEdges.GetProperty().SetSpecular(.4)
CubeEdges.GetProperty().SetSpecularPower(10)
# 新建球体,用来表示marching cubes哪里是1,哪里是0
sphere = vtkSphereSource()
sphere.SetRadius(0.04)
sphere.SetPhiResolution(20)
sphere.SetThetaResolution(20)
# 移除掉grid中点的scalar
ThresholdIn = vtkThresholdPoints()
ThresholdIn.SetInputData(Grid)
ThresholdIn.ThresholdByUpper(.5) # 只保留标量值小于或等于 0.5 的所有点
Vertices = vtkGlyph3D()
Vertices.SetInputConnection(ThresholdIn.GetOutputPort())
Vertices.SetSourceConnection(sphere.GetOutputPort())
SphereMapper = vtkPolyDataMapper()
SphereMapper.SetInputConnection(Vertices.GetOutputPort())
SphereMapper.ScalarVisibilityOff()
CubeVertices = vtkActor()
CubeVertices.SetMapper(SphereMapper)
CubeVertices.GetProperty().SetDiffuseColor(
colors.GetColor3d('tomato'))
caseLabel = vtkVectorText()
aLabelTransform = vtkTransform()
aLabelTransform.Identity()
aLabelTransform.Translate(-0.2, 0, 1.25)
aLabelTransform.Scale(.05, .05, .05)
# 将label这个数据移动到新的位置
labelTransform = vtkTransformPolyDataFilter()
labelTransform.SetTransform(aLabelTransform)
labelTransform.SetInputConnection(caseLabel.GetOutputPort())
labelMapper = vtkPolyDataMapper()
labelMapper.SetInputConnection(labelTransform.GetOutputPort())
labelActor = vtkActor()
labelActor.SetMapper(labelMapper)
# 设置一个基座,用来放置grid
baseModel = vtkCubeSource()
baseModel.SetXLength(1.5)
baseModel.SetYLength(0.01)
baseModel.SetZLength(1.5)
baseMapper = vtkPolyDataMapper()
baseMapper.SetInputConnection(baseModel.GetOutputPort())
base = vtkActor()
base.SetMapper(baseMapper)
base.SetPosition(.5, -0.09, .5)
"""
在这里,完成对于Scalars标量值的更新
"""
cases[i](Scalars, caseLabel, 1, 0)
"""
Grid对象进行数据更新
为什么Scalars这个对象的更新,能够影响到已经构建好了的Grid里面的标量的值??
Grid.GetPointData().SetScalars(Scalars) 这一行没有复制数据,只是建立起了一个引用
SetScalars() 的作用:建立联系,而非复制数据
你可以把 Scalars 想象成一个装满水的水桶,而 Grid 就像一个需要水的洒水器。
Grid.GetPointData().SetScalars(Scalars) 这句话的作用是:
"把洒水器的进水管,接到这个水桶上。"
这行代码执行之后,Grid(洒水器)就知道去哪里找它的水源(Scalars)。它本身没有自己的水,它只是引用着 Scalars 这个水桶里的数据。
当你循环中调用 caseX(Scalars, ...) 函数时,你实际上是在:
"直接往这个水桶里倒新水。"
因为 Grid 的进水管一直连接着同一个水桶,所以当你改变 Scalars 里数据的时候,Grid 立即就能"看到"这些变化。
Grid.Modified() 的作用就是:
"告诉洒水器:嘿,水桶里的水换了,记得重新检查一下!"
如果没有这句通知,洒水器可能懒得去检查,继续用它之前的水。有了这句通知,它就会去检查水桶,发现水变了,然后把新数据传递给下游的过滤器(vtkContourFilter)
"""
Grid.Modified()
renderers[i].AddActor(triangleEdgeActor)
renderers[i].AddActor(base)
renderers[i].AddActor(labelActor)
renderers[i].AddActor(CubeEdges)
renderers[i].AddActor(CubeVertices)
renderers[i].AddActor(Triangles)
renderers[i].GetActiveCamera().Azimuth(30)
renderers[i].GetActiveCamera().Elevation(20)
renderers[i].ResetCamera()
renderers[i].ResetCameraClippingRange()
"""
原本的示例代码是这样写的
if i > 0:
renderers[i].SetActiveCamera(renderers[0].GetActiveCamera())
这行代码的意思是,所有的render都使用索引为0的render的那个相机
这就意味着,窗口的所有render在鼠标进行操作时,因为是一个相机,所有的render都会一起变化
"""
rendererSize = 300
xGridDimensions = 4
yGridDimensions = (len(cases) - 1) // 4 + 1
print('Grid dimensions, (x, y): ({:d}, {:d})'.format(xGridDimensions, yGridDimensions))
renWin.SetSize(
rendererSize * xGridDimensions, rendererSize * yGridDimensions)
renWin.SetWindowName('MarchingCases')
"""
vtk的惰性执行,哪怕前面已经将render塞入到renderwindow中,这里再进行更改也是可以的
"""
for row in range(0, yGridDimensions):
for col in range(0, xGridDimensions):
index = row * xGridDimensions + col
# (xmin, ymin, xmax, ymax)
viewport = [
float(col) / xGridDimensions,
float(yGridDimensions - (row + 1)) / yGridDimensions,
float(col + 1) / xGridDimensions,
float(yGridDimensions - row) / yGridDimensions]
renderers[index].SetViewport(viewport)
iren.Initialize()
renWin.Render()
iren.Start()
def case0(scalars: vtkFloatArray,
caseLabel: vtkVectorText,
IN: float,
OUT: float):
# SetValue和InsertValue这两种写法都可以
scalars.SetValue(0, OUT)
scalars.SetValue(1, OUT)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 0 - 00000000')
else:
caseLabel.SetText('Case 0c - 11111111')
def case1(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, OUT)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 1 - 00000001')
else:
caseLabel.SetText('Case 1c - 11111110')
def case2(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 2 - 00000011')
else:
caseLabel.SetText('Case 2c - 11111100')
def case3(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, OUT)
scalars.InsertValue(2, IN)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 3 - 00000101')
else:
caseLabel.SetText('Case 3c - 11111010')
def case4(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, OUT)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 4 - 01000001')
else:
caseLabel.SetText('Case 4c - 10111110')
def case5(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, OUT)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, IN)
scalars.InsertValue(5, IN)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 5 - 00110010')
else:
caseLabel.SetText('Case 5c - 11001101')
def case6(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, OUT)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, IN)
scalars.InsertValue(4, IN)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 6 - 00011010')
else:
caseLabel.SetText('Case 6c - 11100101')
def case7(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 7 - 01000011')
else:
caseLabel.SetText('Case 7c - 10111100')
def case8(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, IN)
scalars.InsertValue(5, IN)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 8 - 00110011')
else:
caseLabel.SetText('Case 8c - 11001100')
def case9(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, OUT)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, IN)
scalars.InsertValue(3, IN)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 9 - 01001110')
else:
caseLabel.SetText('Case 9c - 10110001')
def case10(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, OUT)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, IN)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, IN)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 10 - 01101001')
else:
caseLabel.SetText('Case 10c - 10010110')
def case11(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, OUT)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, OUT)
scalars.InsertValue(4, IN)
scalars.InsertValue(5, IN)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 11 - 01110001')
else:
caseLabel.SetText('Case 11c - 10001110')
def case12(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, OUT)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, IN)
scalars.InsertValue(4, IN)
scalars.InsertValue(5, IN)
scalars.InsertValue(6, OUT)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 12 - 00111010')
else:
caseLabel.SetText('Case 12c - 11000101')
def case13(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, OUT)
scalars.InsertValue(1, IN)
scalars.InsertValue(2, OUT)
scalars.InsertValue(3, IN)
scalars.InsertValue(4, IN)
scalars.InsertValue(5, OUT)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, OUT)
if IN == 1:
caseLabel.SetText('Case 13 - 01011010')
else:
caseLabel.SetText('Case 13c - 10100101')
def case14(scalars, caseLabel, IN, OUT):
scalars.InsertValue(0, IN)
scalars.InsertValue(1, OUT)
scalars.InsertValue(2, IN)
scalars.InsertValue(3, IN)
scalars.InsertValue(4, OUT)
scalars.InsertValue(5, IN)
scalars.InsertValue(6, IN)
scalars.InsertValue(7, IN)
if IN == 1:
caseLabel.SetText('Case 14 - 11101101')
else:
caseLabel.SetText('Case 14c - 00010010')
if __name__ == '__main__':
main()