一:主要的知识点
1、说明
本文只是教程内容的一小段,因博客字数限制,故进行拆分。主教程链接:vtk教程——逐行解析官网所有Python示例-CSDN博客
2、知识点纪要
本段代码主要涉及的有①vtkColorTransferFunction颜色映射,②vtkExtractVOI裁剪体素数据,③vtkLODActor的作用
二:代码及注释
from vtkmodules.vtkCommonColor import vtkNamedColors import vtkmodules.vtkRenderingOpenGL2 from vtkmodules.vtkCommonCore import vtkLookupTable from vtkmodules.vtkIOImage import vtkMetaImageReader from vtkmodules.vtkImagingCore import vtkExtractVOI from vtkmodules.vtkFiltersGeneral import vtkDiscreteFlyingEdges3D, vtkDiscreteMarchingCubes from vtkmodules.vtkFiltersCore import vtkWindowedSincPolyDataFilter, vtkPolyDataNormals from vtkmodules.vtkRenderingCore import vtkColorTransferFunction, vtkPolyDataMapper, vtkRenderWindow, \ vtkRenderWindowInteractor, vtkRenderer from vtkmodules.vtkRenderingLOD import vtkLODActor def get_diverging_lut(ct): # 构建颜色查找表 cm = {} """ cm中存储了五种预定义的发散颜色方案 每种方案由一个包含三个点的列表定义:起点 (0.0)、中点 (0.5) 和 终点 (1.0) 每个点是一个列表 [Value, R, G, B],其中 Value 是数据范围中的归一化位置,R, G, B 是该位置的颜色值(范围 0.0 到 1.0) 发散特性:所有方案的中点(0.5)都被设置为一个中性色(如浅灰色 [0.865, 0.865, 0.865]), 这使得数据零点或中心值能被中性地表示,而正负或高低极端值则被不同的饱和色(如蓝色和红色)突出显示 """ cm[0] = [[0.0, 0.230, 0.299, 0.754], [0.5, 0.865, 0.865, 0.865], [1.0, 0.706, 0.016, 0.150]] cm[1] = [[0.0, 0.436, 0.308, 0.631], [0.5, 0.865, 0.865, 0.865], [1.0, 0.759, 0.334, 0.046]] cm[2] = [[0.0, 0.085, 0.532, 0.201], [0.5, 0.865, 0.865, 0.865], [1.0, 0.436, 0.308, 0.631]] cm[3] = [[0.0, 0.217, 0.525, 0.910], [0.5, 0.865, 0.865, 0.865], [1.0, 0.677, 0.492, 0.093]] cm[4] = [[0.0, 0.085, 0.532, 0.201], [0.5, 0.865, 0.865, 0.865], [1.0, 0.758, 0.214, 0.233]] ct = abs(ct) if ct > len(cm) - 1: ct = 0 ctf = vtkColorTransferFunction() """ vtkColorTransferFunction 用于定义连续的颜色映射规则 简单来说,它是一个数学函数, 它接收一个输入标量值(比如温度、压力、或者你代码中的平滑误差),然后输出一个对应的 RGB 颜色值 核心功能:定义颜色映射的“路径” vtkColorTransferFunction 的作用是将一个一维的数据范围(例如,平滑误差从 −5.0 到 +5.0)映射到一个三维的颜色空间(R、G、B)。 它通过在数据范围内定义一系列的**关键点(Control Points)**来实现颜色映射的精确控制。 1. AddRGBPoint(value, R, G, B) 这是定义颜色映射路径的主要方法: value: 输入数据空间中的一个特定数值(例如,如果你想让误差为 0.0 的地方显示为白色,value 就是 0.0)。 R, G, B: 对应这个 value 应该具有的颜色。 当你添加了多个关键点后,vtkColorTransferFunction 就会在这些关键点之间自动进行插值(Interpolation),从而形成一条平滑的颜色路径。 示例: AddRGBPoint(0.0, 0.0, 0.0, 1.0): 数据为 0.0 时是蓝色。 AddRGBPoint(1.0, 1.0, 0.0, 0.0): 数据为 1.0 时是红色。 那么,数据为 0.5 时,函数会插值得到一个接近紫色的颜色。 2. SetColorSpaceToDiverging() 这是在你的 get_diverging_lut 函数中使用的关键设置: 它告诉函数,颜色变化应该遵循一个**发散(Diverging)**的颜色模型。 作用:它优化了颜色空间中的插值,特别是确保在中心值(通常是 0.5 或数据中心)附近的颜色变化更加平滑和中性,从而更好地可视化具有正负意义或围绕中心点变化的数据(如平滑误差)。 3. 与 vtkLookupTable 的关系 vtkColorTransferFunction 定义了连续的颜色规则,而 vtkLookupTable (LUT) 是一个离散的颜色表(通常是 256 个颜色)。 LUT 是渲染器实际使用的对象,因为它速度更快。 vtkColorTransferFunction 用于生成高质量的 LUT。在你的代码中,正是通过循环调用 ctf.GetColor(i / table_size),将连续的颜色函数采样,并填充到 vtkLookupTable 中。 """ ctf.SetColorSpaceToDiverging() for scheme in cm[ct]: ctf.AddRGBPoint(*scheme) table_size = 256 lut = vtkLookupTable() lut.SetNumberOfColors(table_size) lut.Build() for i in range(0, table_size): rgba = list(ctf.GetColor(float(i) / table_size)) rgba.append(1) lut.SetTableValue(i, rgba) return lut def main(): colors = vtkNamedColors() use_flying_edges = True ifn = "Data/labels.mhd" index = 31 # 读取相关文件 reader_volume = vtkMetaImageReader() reader_volume.SetFileName(ifn) reader_volume.Update() """ vtkExtractVOI 作用是从一个体数据(Volume Data)或图像数据(Image Data)中提取一个指定子区域(子集) 可用于裁剪和降采样 """ voi = vtkExtractVOI() voi.SetInputConnection(reader_volume.GetOutputPort()) voi.SetVOI(0, 517, 0, 228, 0, 392) # 裁剪范围 voi.SetSampleRate(1, 1, 1) # 表示不降采样,保留原始分辨率 voi.SetSampleRate(2, 2, 2) # 表示每隔一个体素取一个,这将把数据的分辨率减半 voi.Update() srange = voi.GetOutput().GetScalarRange() # 获取体素数值大小的范围(0.0, 705.0) print("Range: ", srange) # 体素模型的表面重建 contour = vtkDiscreteFlyingEdges3D() contour.SetInputConnection(voi.GetOutputPort()) contour.SetValue(0, index) contour.Update() smoother = vtkWindowedSincPolyDataFilter() smoother.SetInputConnection(contour.GetOutputPort()) smoother.SetNumberOfIterations(30) smoother.NonManifoldSmoothingOn() smoother.NormalizeCoordinatesOn() # 启用坐标归一化,在应用 Sinc 滤波器之前执行的预处理步骤,对平滑过程的稳定性和结果质量至关重要 smoother.GenerateErrorScalarsOn() # 计算并输出一个表示“几何误差”的标量数组 """ GenerateErrorScalarsOn 启用这个功能后,平滑器在移动网格上的每个顶点时,会记录该顶点相对于其原始位置的距离 这个误差值(一个浮点数)被存储为新的输出网格(vtkPolyData)的点数据(Point Data)中的一个标量数组 """ smoother.Update() se_range = smoother.GetOutput().GetPointData().GetScalars().GetRange() print('Smoother error range:', se_range) if se_range[1] > 1: print('Big smoother error: min/max:', se_range[0], se_range[1]) lut = get_diverging_lut(4) normals = vtkPolyDataNormals() """ vtkPolyDataNormals 是一个 滤波器(filter)根据几何形状(顶点和面)计算法向量(normals),并把它们添加到 vtkPolyData 的点或面上 """ normals.SetInputConnection(smoother.GetOutputPort()) normals.ComputeCellNormalsOn() normals.ComputePointNormalsOff() normals.ConsistencyOn() # 尝试保持法向一致(方向不乱) normals.AutoOrientNormalsOn() # 根据整体外形自动确定法向朝外或朝内 normals.SetFeatureAngle(60.0) # 当两面夹角大于该角度时,不平均法线,保留边缘 normals.Update() mapper = vtkPolyDataMapper() # mapper.SetInputConnection(smoother.GetOutputPort()) mapper.SetInputConnection(normals.GetOutputPort()) # 使用含有法线的数据,渲染效果会更好 mapper.ScalarVisibilityOn() # 使用模型的标量数据进行着色,而非统一颜色 mapper.SetScalarRange(se_range) # mapper.SetScalarModeToUseCellData() # Contains the label eg. 31 mapper.SetScalarModeToUsePointData() # The smoother error relates to the verts. mapper.SetLookupTable(lut) """ vtkLODActor vtkLODActor 和 vtkActor 的关系非常紧密,但它多了一个关键功能 —— “LOD(Level of Detail,细节层次)控制” vtkActor 只显示一种模型; vtkLODActor 能同时保存多种简化版本的模型,并根据渲染速度自动选择合适的那个来绘制。 在 VTK 的早期(还没有 GPU 硬件加速的时代),如果场景中包含 大量复杂几何体(例如几万个多边形),实时旋转或交互就会非常卡。 于是 VTK 提供了 vtkLODActor,它能在: 你静止观察模型时 用高精度几何; 你旋转/缩放时 自动切换到低精度版本(比如抽稀点或包围盒); 从而保持交互流畅 """ actor = vtkLODActor() """ SetNumberOfCloudPoints vtkLODActor 会在交互(比如拖动、旋转)时自动切换到“低分辨率”的显示模式,以保证流畅性。 在这种低分辨率模式下,它可能用以下几种方式来替代原几何: 用 点云(Point Cloud) 表示原模型; 用 抽稀网格; 用 包围盒 或其他简化几何。 而这其中的“点云模式”,就是通过本行代码控制的 意思是:当 vtkLODActor 进入“点云模式(Point Cloud Mode)”时,最多使用 100,000 个点 来代表整个模型。 """ actor.SetNumberOfCloudPoints(100000) actor.SetMapper(mapper) # Create the renderer. ren = vtkRenderer() ren.SetBackground(colors.GetColor3d('DimGray')) ren.AddActor(actor) # Create a window for the renderer of size 600X600 ren_win = vtkRenderWindow() ren_win.AddRenderer(ren) ren_win.SetSize(600, 600) ren_win.SetWindowName('MeshLabelImageColor') ren_win.Render() # Set a user interface interactor for the render window. iren = vtkRenderWindowInteractor() iren.SetRenderWindow(ren_win) # Start the initialization and rendering. iren.Initialize() iren.Start() if __name__ == '__main__': main()