Pyvista读取VTK文件渲染三维云图并切片裁剪(1)

Pyvista官方文档:https://qtdocs.pyvista.org/index.html
下面的代码是本人查看官方文档API之后编写的,里面含有对官方API的翻译。注释写的很详细,作者就不多赘述了。
三维云图绘制效果:
表面:
表面
表面+网格:
表面+网格:
网格线:
网格线
表面点+右键选中显示数据:
表面点+右键选中显示数据
高斯点+右键选中显示数据:
鼠标右键单击选中点,进行其数据值的显示
在这里插入图片描述
切片和裁剪
盒子裁剪:
盒子所在的六面体,可以对每个面进行法向拖动,也可以旋转、移动盒子
在这里插入图片描述
在这里插入图片描述
平面裁剪:
箭头为法线所在的平面对模型进行裁剪,既可以拖动箭头和箭尾对平面的角度进行改变,也可以法向拖动平面
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
平面切片:
箭头为法线所在的平面对模型进行切片,既可以拖动箭头和箭尾对平面的角度进行改变,也可以法向拖动平面
在这里插入图片描述
三平面切片:
三个互相垂直的平面,均可在其法向拖动,进行切片
在这里插入图片描述
在这里插入图片描述
曲面切片:
小球组成的线所在的曲面进行切片,小球可以随意拖动
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
数据阈值裁剪:
拖动滑块改变阈值,默认滑块右侧(>=滑块值)的数据部分显示出来,可以反向
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

import numpy as np
import pyvista as pv
from pyvista.plotting.opts import PickerType

vtu_filename = "../Data/cylinderdemo4_out.vtu"
# 读取 VTK 文件
mesh = pv.read(vtu_filename)
# 读取节点数据
node_data_name = "Temperature"  # 替换为您的节点数据名称
node_data = mesh.point_data[node_data_name]



plotter = pv.Plotter(shape="1|1")  # TODO   shape="3|1", 分隔绘图窗口,左边3个,右边一个
print(plotter)
plotter.subplot(0, 0)   # TODO 第1个框里绘制
cylinder = plotter.add_mesh(mesh, scalars=node_data, cmap="coolwarm",
                 # show_scalar_bar=False,
                 scalar_bar_args={'title': 'Temperature',
                                  "vertical":"False",
                                  "height":0.65,
                                  "position_x":0.85,
                                  "position_y":0.1},
                 # show_vertices=True,  # TODO   显示点
                 point_size=3, # TODO   点的大小
                 # style='points_gaussian',
                 # show_edges=True,     # TODO   显示网格
                 style='surface', # TODO  显示样式默认:'surface'表面,'wireframe'线条,'points'表面的点配合point_size设置大小,
                 )   # TODO   圆柱

_ = plotter.add_axes(   # TODO 左下角坐标轴
    line_width=5,
    cone_radius=0.6,
    shaft_length=0.7,
    tip_length=0.3,
    ambient=0.5,
    label_size=(0.4, 0.16),
)
plotter.subplot(1, 0)
# TODO 添加切片盒子裁剪网格  剪裁的网格保存到plotter.box_clipped_meshes的属性
# plotter.add_mesh_clip_box(mesh,
#                           interaction_event='end',  # TODO 什么时候触发改变,'start'刚开始拖动, 'end'拖动松开, 'always'一直变
#                           show_scalar_bar=False,
#                           cmap="coolwarm",
#                           )  # TODO 切片
actor =plotter.add_mesh_clip_box(mesh,
                          invert=False, # TODO 是否翻转/反转剪辑的标志
                          rotation_enabled=True,    # TODO 如果False,框小部件不能旋转,并且严格正交于笛卡尔轴
                          widget_color=None,    # TODO 小工具的颜色。字符串、RGB序列或十六进制颜色字符串,as color='white'
                          outline_translation=True,    # TODO 如果False,平面小部件不能被转换,并且被严格地放置在给定的边界内
                          merge_points=True,    # TODO 如果True(默认),独立定义的网格元素的重合点将被合并
                          crinkle=False,    # TODO 通过沿剪辑提取整个单元格来使剪辑起皱
                          interaction_event='end',  # TODO 什么时候触发改变,'start'刚开始拖动, 'end'拖动松开, 'always'一直变
                          # **kwargs    # TODO 所有add_mesh()的参数都可以用
                          cmap="coolwarm",
                          )
# print(type(actor))
# print(actor)
# clip = plotter.box_clipped_meshes
# print(type(clip))

# TODO 使用平面裁剪网格 保留剩下的三维网格  剪裁的网格保存到plotter.plane_clipped_meshes的属性
# plotter.add_mesh_clip_plane(mesh,
#                             normal='x', # TODO 平面的起始法向量
#                             invert=False,   # TODO 是否翻转/反转剪辑的标志
#                             widget_color=None,  # TODO 组件的颜色 字符串、RGB列表或十六进制颜色字符串
#                             value=0.0,  # TODO 沿法线方向设置剪裁值。默认值为0.0
#                             assign_to_axis=None,    # TODO 指定平面的法线与给定的轴平行。选项有(0, 'x'), (1, 'y'),或者(2, 'z')
#                             tubing=False,   # TODO 当使用隐式平面wiget时,这将控制是否在平面边界周围显示管状体
#                             origin_translation=True,    # TODO 如果False,平面小部件不能通过其原点平移,而是严格放置在给定的原点。仅在使用隐式平面时有效
#                             outline_translation=False,  # TODO 如果False时,box小部件不能被翻译,并且被严格地放置在给定的边界内。
#                             implicit=True,  # TODO  当为True时,使用vtkImplicitPlaneWidget,当为False时,使用vtkPlaneWidget。
#                             normal_rotation=True,   # TODO 设置法向量箭头的不透明度为0,这样它就被有效地禁用了。这可以防止用户旋转法线。当设置assign_to_axis时,该值强制为False。
#                             crinkle=False,  # TODO 通过沿剪辑提取整个单元格来使剪辑起皱
#                             interaction_event='end',    # TODO 什么时候触发改变,'start'刚开始拖动, 'end'拖动松开, 'always'一直变
#                             origin=None,    # TODO 平面中心的起始坐标
#                             # **kwargs    # TODO 所有add_mesh()的参数都可以用
#                             cmap="coolwarm",
#                             )


# TODO 使用平面裁剪网格  保留平面所在的二维网格  切片网格被保存到plotter.plane_sliced_meshes的属性
# plotter.add_mesh_slice(mesh,
#                        normal='x',  # TODO 平面的起始法向量
#                        generate_triangles=False,    # TODO 如果这被启用(False默认情况下),输出将是三角形,否则,输出将是相交多边形
#                        widget_color=None,   # TODO 平面组件的颜色 字符串、RGB序列或十六进制颜色字符串。默认为'white'
#                        assign_to_axis=None, # TODO 指定平面的法线与给定的轴平行:选项有(0,' x ')、(1,' y ')或(2,' z ')
#                        tubing=False,    # TODO 当使用隐式平面wiget时,这将控制是否在平面边界周围显示管状体
#                        origin_translation=True, # TODO 如果False,平面小部件不能通过其原点平移,而是严格放置在给定的原点。仅在使用隐式平面时有效
#                        outline_translation=False,   # TODO 如果为False,则无法翻译框小部件,并严格放置在给定的边界上。
#                        implicit=True,   # TODO  当为True时,使用vtkImplicitPlanewidget,当为False时,使用vtkPlaneWidget。
#                        normal_rotation=True,    # TODO 设置法向量箭头的不透明度为0,这样它就被有效地禁用了。这可以防止用户旋转法线。当设置assign_to_axis时,该值强制为False。
#                        interaction_event="end",    # TODO 什么时候触发(事件id)45:结束交互 44:拖动时
#                        origin=None, # TODO  平面中心的起始坐标。
#                        # **kwargs   # TODO 所有add_mesh()的参数都可以用
#                        cmap="coolwarm",
#                        )


# TODO 用三个相互垂直的平面切割网格  保留三个平面所在的二维网格
# plotter.add_mesh_slice_orthogonal(mesh,
#                                   generate_triangles=False, # TODO 如果这被启用(False默认情况下),输出将是三角形,否则,输出将是相交多边形
#                                   widget_color=None,    # TODO 小工具的颜色。字符串、RGB序列或十六进制颜色字符串,as color='white'
#                                   tubing=False,     # TODO 当使用隐式平面wiget时,这将控制是否在平面边界周围显示管状体
#                                   interaction_event='end',     # TODO 什么时候触发改变,'start'刚开始拖动, 'end'拖动松开, 'always'一直变
#                                   # **kwargs    # TODO 所有add_mesh()的参数都可以用
#                                   cmap="coolwarm",
#                                   )

# TODO 使用样条线小部件(多点可拖动线)切割网格  保留线所组成的平面、曲面的网格 切片网格被保存到plotter.spline_sliced_meshes的属性
# plotter.add_mesh_slice_spline(mesh,
#                               generate_triangles=False, # TODO  如果这被启用(False默认情况下),输出将是三角形,否则,输出将是相交多边形
#                               n_handles=5,  # TODO  控制样条曲线参数函数的交互球体的数量
#                               resolution=25,    # TODO  要在样条上生成的点数
#                               widget_color=None,    # TODO  小工具的颜色。字符串、RGB序列或十六进制颜色字符串,as color='white'
#                               show_ribbon=True,    # TODO  如果True,也将显示用于切片的多边形平面
#                               ribbon_color='pink',  # TODO  丝带的颜色。字符串、RGB序列或十六进制颜色字符串
#                               ribbon_opacity=0.5,   # TODO  色带的不透明度。默认值为1.0,必须介于[0, 1]
#                               initial_points=None,  # TODO  初始化小部件位置的点。必须具有与相同数量的元素n_handles。如果第一个点和最后一个点相同,这将是一个封闭环样条
#                               closed=False, # TODO  使样条曲线成为闭合环
#                               interaction_event=44, # TODO 什么时候触发(事件id)45:结束交互 44:拖动时
#                               # **kwargs    # TODO 所有add_mesh()的参数都可以用
#                               cmap="coolwarm",
#                               )

# TODO 使用滑块在网格上应用阈值
# plotter.add_mesh_threshold(mesh,
#                           show_scalar_bar=False,
#                           cmap="coolwarm",
#                           )  # TODO
# plotter.add_mesh_threshold(mesh,
#                            scalars=None,    # TODO 要设定阈值和显示的网格上标量的字符串名称
#                            invert=False,    # TODO 反转阈值结果。也就是说,此选项关闭时输出中的像元将被排除,而输出中的像元将被包括
#                            widget_color=None,   # TODO 小工具的颜色。字符串、RGB序列或十六进制颜色字符串。as color='white'
#                            preference='cell',   # TODO 该参数设置标量如何映射到网格。默认'cell',导致标量与网格单元相关联。可以是'point'或者'cell'
#                            title=None,  # TODO 滑块小工具的字符串标签
#                            pointa=(0.4, 0.9),   # TODO 显示端口上滑块左侧点的相对坐标
#                            pointb=(0.9, 0.9),# TODO 显示端口上滑块右点的相对坐标
#                            continuous=False,    # TODO 如果启用了此选项(默认为False),使用连续音程[minimum cell scalar, maximum cell scalar]与阈值边界相交,而不是与顶点的离散标量值集合相交
#                            all_scalars=False,   # TODO 如果使用点数据的标量,当该值为True时,单元中的所有点必须满足阈值。当False的时候,具有满足阈值标准的标量值的像元的任何点将提取像元。使用单元格数据时无效
#                            method='upper',  # TODO 为单值设置阈值方法,定义要使用的阈值界限。如果value是一个范围,此参数将被忽略,提取两个值之间的数据。对于单个值,'lower'将提取低于value. 'upper'将提取大于value.
#                            # **kwargs    # TODO 所有add_mesh()的参数都可以用
#                            cmap="coolwarm",
#                            )



# TODO   对指定点   添加  label标签   后期可以尝试鼠标点击某个点 为其添加 值的标签
# point_A = pv.pyvista_ndarray([mesh.points[0]])
# label_A = pv.pyvista_ndarray([node_data[0]])
# print(point_A)
# print(type(point_A))
# plotter.add_point_labels(points=point_A, labels=label_A)

# TODO 将轮廓添加到场景中  可以在切割的时候使用,方便观察
# plotter.add_silhouette(mesh,
#                        color=None,  # TODO 轮廓线条的颜色
#                        line_width=None, # TODO  轮廓线条宽度
#                        opacity=None,    # TODO  之间的线条透明度0和1
#                        feature_angle=None,  # TODO  如果设置,显示超过该角度的锐边
#                        decimate=None,   # TODO  之间的抽取级别0和1。抽取将提高渲染性能。一个好的经验法则是尝试0.9直到达到所需的渲染性能
#                        )









# plotter.subplot(1, 0)   # TODO 第2个框里绘制
# plotter.add_mesh(mesh, scalars=node_data, cmap="coolwarm",
#                  show_scalar_bar=False,
#                  # scalar_bar_args={
#                  #                  'title': '',
#                  #                  "vertical":"False",
#                  #                  "height":0.65,
#                  #                  "position_x":0.85,
#                  #                  "position_y":0.1},
#                  # show_vertices=True,  # TODO   显示点
#                  # point_size=5, # TODO   点的大小
#                  # style='points_gaussian',
#                  show_edges=True,     # TODO   显示网格
#
#                  )   # TODO   圆柱
#
# _ = plotter.add_axes(   # TODO 左下角坐标轴
#     line_width=5,
#     cone_radius=0.6,
#     shaft_length=0.7,
#     tip_length=0.3,
#     ambient=0.5,
#     label_size=(0.4, 0.16),
# )

# plotter.subplot(2, 0)   # TODO 第3个框里绘制
# plotter.add_mesh(mesh, scalars=node_data, cmap="coolwarm",
#                  show_scalar_bar=False,
#                  # scalar_bar_args={
#                  #     'title': 't',
#                  #                  "vertical":"False",
#                  #                  "height":0.65,
#                  #                  "position_x":0.85,
#                  #                  "position_y":0.1},
#                  show_vertices=True,  # TODO   显示点
#                  # point_size=5, # TODO   点的大小
#                  # style='points_gaussian',
#                  # show_edges=True,     # TODO   显示网格
#
#                  )   # TODO   圆柱
#
# _ = plotter.add_axes(   # TODO 左下角坐标轴
#     line_width=5,
#     cone_radius=0.6,
#     shaft_length=0.7,
#     tip_length=0.3,
#     ambient=0.5,
#     label_size=(0.4, 0.16),
# )

# plotter.subplot(3, 0)   # TODO 第4个框里绘制
# # TODO 绘制高斯点
# plotter.add_mesh(pv.PolyData(mesh.points),scalars=node_data, cmap='coolwarm',
#                  scalar_bar_args={
#                      'title': 'Temperature',
#                                   "vertical": "False",
#                                   "height": 0.65,
#                                   "position_x": 0.85,
#                                   "position_y": 0.1},
#                  show_edges=False)


def fun_pick(point):
    points = mesh.points
    # 计算点与数组中每个点的距离
    distances = np.linalg.norm(points - point, axis=1)
    # 找到距离最接近的点的索引
    index = np.argmin(distances)
    plotter.add_point_labels(np.array([points[index]]), np.array([node_data[index]]),
                             point_color='black',
                             point_size=10,
                             font_size=20,
                             name='point_1' # TODO 添加的参与者的名称,以便于更新。如果此名称的参与者已经存在于呈现窗口中,它将被新参与者替换。
                             )

# TODO 拾取点
plotter.enable_point_picking(callback=fun_pick,   # TODO 回调函数,第一个参数是被点击的点坐标 [-33.262 -27.3781 -22.936]
        tolerance=0.025,    # TODO 拾取的误差,按照屏幕百分比
        left_clicking=False,     # TODO 默认鼠标右键选取,True则鼠标左键
        picker=PickerType.POINT,     # TODO 选取的类型 hardware、cell、point、volume
        show_message=True,       # TODO 显示关于如何使用点拾取工具的消息。如果这是一个字符串,那将是显示的消息。
        font_size=18,    # TODO 设置消息的大小。
        color='black',    # TODO 显示被选中后的颜色。
        point_size=10,   # TODO 如果' show_point ' '为' True',则所选点的大小。
        show_point=True,    # TODO  点击后显示选中点。
        use_picker=False,   # TODO  当' ' True ' '时,回调函数也将被传递给选择器。
        pickable_window=False,   # TODO 当“True”并且所选拾取器支持它时,3D窗口中的点是可拾取的。
        clear_on_no_selection=True,  # TODO 当没有选择任何点时清除所选内容。
        # **kwargs,  # TODO
        )






cent = np.random.random((1, 3))
direction = np.random.random((1, 3))
print(cent)
print(direction)
# _ = plotter.add_arrows(cent, direction, mag=1)  # TODO 箭头

_ = plotter.add_axes(   # TODO 左下角坐标轴
    line_width=5,
    cone_radius=0.6,
    shaft_length=0.7,
    tip_length=0.3,
    ambient=0.5,
    label_size=(0.4, 0.16),
)
# _ = plotter.add_axes_at_origin()    # TODO 中心位置添加坐标轴

_ = plotter.add_camera_orientation_widget() # TODO 右上角坐标轴带负轴

chart = pv.Chart2D()   # TODO 图表
_ = chart.plot(range(10), range(10))
# plotter.add_chart(chart)

# mesh = pyvista.Sphere()
# actor = plotter.add_mesh(mesh)
# def toggle_vis(flag):
#     actor.SetVisibility(flag)
# _ = plotter.add_checkbox_button_widget(toggle_vis, value=True) # TODO 左下角添加一个复选框CheckBox


# _ = plotter.add_cursor()    # TODO 添加光标  框起来  不是鼠标

# _ = plotter.add_legend(bcolor='w', face=None)

plotter.show()
<think>好的,我现在需要帮助用户解决关于使用PyVista读取VTK文件的问题。用户之前遇到了一个AttributeError,现在可能已经解决了,现在转向如何正确读取VTK文件。首先,我需要确保用户了解PyVista的基本操作,特别是读取不同格式的VTK文件。 首先,我应该考虑用户可能的背景。他们可能刚开始使用PyVista,或者对VTK文件的结构不太熟悉。需要分步骤解释,确保每个部分都清晰。同时,用户之前的问题涉及属性错误,所以在读取文件后可能需要检查数据是否正确加载,比如点、单元、标量或向量数据是否存在。 接下来,我应该回忆PyVista读取VTK文件的常用方法,比如使用pyvista.read()函数,这个函数支持多种VTK格式。需要提到不同VTK文件类型,如结构化网格和非结构化网格,以及Legacy VTK文件和XML VTK文件的区别。用户可能不清楚这些格式的区别,需要简要说明。 然后,需要考虑用户可能遇到的常见问题,比如文件路径错误、格式不支持,或者数据加载后的验证步骤。例如,读取后如何检查数据是否存在,如何可视化确认是否正确加载。此外,用户可能需要知道如何处理多块数据集,使用pyvista.MultiBlock来处理多个网格的情况。 还要注意代码示例的准确性和可执行性。比如,正确的导入语句,使用示例文件还是用户自己的文件。可能需要给出两种情况的示例:使用PyVista提供的示例文件和用户自定义文件路径。 另外,用户可能对如何访问数据中的具体属性感兴趣,比如点数据、单元数据,标量、向量等。需要解释如何通过point_data或cell_data来访问这些数据,提醒他们使用keys()方法查看可用数据。 最后,常见错误部分需要涵盖文件路径问题、格式不支持、编码问题(特别是Legacy VTK文件可能需要指定编码),以及多块数据集的处理。这些是用户在实际操作中可能会遇到的典型问题,提前解释可以帮助他们避免困惑。 需要确保整个回答结构清晰,分步骤说明,每个部分都有相应的代码示例和解释。同时,语言要简洁,避免使用过于专业的术语,或者在使用时进行适当解释。这样用户无论是新手还是有一定经验,都能理解应用提供的方法。</think>以下是使用 **PyVista** 读取 VTK 文件的详细步骤和注意事项: --- ### **1. 基本方法:`pyvista.read()`** PyVista 提供了通用的 `read()` 函数,支持多种 VTK 文件格式(`.vtk`, `.vtu`, `.vtp`, `.vtr` 等),自动识别文件类型。 ```python import pyvista as pv # 读取 VTK 文件 mesh = pv.read("your_file.vtk") # 查看网格信息 print(mesh) ``` --- ### **2. 处理不同 VTK 文件类型** #### **(1) 结构化网格(Structured Grid,`.vts`)** ```python grid = pv.read("structured_grid.vts") print("网格维度:", grid.dimensions) print("点坐标示例:", grid.points[0]) # 第一个点的坐标 ``` #### **(2) 非结构化网格(Unstructured Grid,`.vtu`)** ```python ugrid = pv.read("unstructured_grid.vtu") print("单元类型示例:", ugrid.cell_type(0)) # 第一个单元的类型 ``` #### **(3) PolyData 数据(表面/点云,`.vtp`)** ```python poly_data = pv.read("surface_mesh.vtp") print("顶点数:", poly_data.n_points) print("面数:", poly_data.n_cells) ``` --- ### **3. 读取 Legacy VTK 文件** 旧版 VTK 文件(如 ASCII 格式)可能需要指定参数: ```python # 读取 Legacy VTK 文件(如 ParaView 导出的 ASCII 格式) mesh = pv.read("legacy.vtk", file_format="legacy") ``` --- ### **4. 检查加载的数据** #### **(1) 查看数据属性** ```python # 检查标量/向量场(如温度、速度) print("点数据字段:", mesh.point_data.keys()) # 顶点上的数据 print("单元数据字段:", mesh.cell_data.keys()) # 单元上的数据 # 访问具体数据(例如名为 'temperature' 的标量场) temperature = mesh.point_data['temperature'] ``` #### **(2) 可视化验证** ```python mesh.plot(show_edges=True, cpos="xy") # 简单可视化网格 ``` --- ### **5. 处理多块数据集(MultiBlock)** 若 VTK 文件包含多个网格(如多区域数据),返回 `MultiBlock` 对象: ```python blocks = pv.read("multi_block.vtm") # .vtm 是多块数据集格式 # 遍历所有子网格 for i, block in enumerate(blocks): print(f"块 {i}:", block) block.plot() ``` --- ### **6. 示例:完整读取流程** ```python import pyvista as pv # 读取文件 filename = "example.vtk" data = pv.read(filename) # 检查数据 print("=== 网格信息 ===") print(f"类型: {type(data)}") print(f"顶点数: {data.n_points}") print(f"单元数: {data.n_cells}") # 检查数据字段 if data.point_data: print("\n=== 点数据字段 ===") for name in data.point_data.keys(): arr = data.point_data[name] print(f"{name}: {arr.shape}") # 可视化 data.plot(scalars='temperature', cmap='jet', show_edges=True) ``` --- ### **7. 常见问题与解决** #### **(1) 文件路径错误** - 使用绝对路径或确保相对路径正确: ```python import os file_path = os.path.abspath("data/example.vtk") # 避免路径歧义 ``` #### **(2) 文件格式不支持** - 手动指定读取器(如 `vtkXMLUnstructuredGridReader`): ```python reader = pv.get_reader("special_file.vtk") reader.set_active_time_point(0) # 若有时序数据 mesh = reader.read() ``` #### **(3) 数据编码问题** - Legacy VTK 文件可能需要指定编码: ```python mesh = pv.read("legacy_ascii.vtk", file_format="legacy", encoding="ascii") ``` --- ### **8. 扩展:使用 PyVista 示例文件** PyVista 内置了示例数据集,可用于测试: ```python from pyvista import examples # 下载读取示例 VTK 文件(如喷泉模型) mesh = examples.download_fountain() mesh.plot() ``` --- 如果遇到具体错误或需要进一步帮助,请提供 VTK 文件类型和 PyVista 版本!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值