返回系列

3D 与 4D:VTK 如何处理体渲染、表面渲染和时间维度

解释 DicomVision 如何把 NumPy volume 交给 VTK,完成体渲染、表面渲染以及 4D phase 播放。

问题引入

3D 医学影像渲染不适合从零手写。体渲染要处理沿视线采样、颜色传递函数、透明度、梯度、不透明度累积、相机和光照;表面渲染要提取等值面、平滑网格、简化面片、计算法线。用 NumPy 可以描述体数据,但不适合承担完整 3D 渲染引擎的角色。

DicomVision 选择把三维渲染放在后端 VTK 中。NumPy 负责准备 volume,VTK 负责渲染,后端把渲染结果截成二维图像再推给前端。这个模型牺牲了前端 GPU 直接交互的一部分潜力,但换来了弱显卡、Web Demo、桌面端打包时更一致的运行方式。

背景概念

体渲染和表面渲染是两类不同方法。体渲染不先生成表面,而是沿射线穿过体数据,根据 transfer function 决定每个采样点的颜色和透明度。它适合看软组织、骨骼和半透明结构的整体关系。表面渲染则先提取某个阈值的等值面,例如骨阈值,然后渲染这个三角网格。它轮廓清晰,交互负担通常更可控。

VTK 是成熟的科学可视化工具包。项目里最重要的桥接点是 vtkImageData 和 numpy_to_vtk。NumPy volume 需要被整理成 VTK 能理解的连续标量数组,同时设置 dimensions 和 spacing。之后体渲染走 vtkSmartVolumeMapper,表面渲染走 vtkFlyingEdges3D。

4D 在这里不是四维体渲染的神秘算法,而是多 phase 的三维数据组织。每个 phase 可以理解为一个时间点上的 CT volume。播放时切换 phase,同时复用 MPR/3D 的视口逻辑。这样 4D 的复杂度主要落在 phase 识别、虚拟序列注册和播放调度上。

项目实现

体渲染路径会把 volume 转成 vtkImageData,创建 vtkSmartVolumeMapper,再配置 vtkColorTransferFunction、vtkPiecewiseFunction 和 vtkVolumeProperty。不同 preset,如骨骼、组织、Composite、MIP,本质上是不同的颜色和透明度映射策略。相机旋转、平移、缩放会更新 VTK session 中的 camera,最后用 vtkWindowToImageFilter 把离屏窗口取回 NumPy/RGB 图像。

表面渲染路径则会先构建 vtkImageData,通过 vtkFlyingEdges3D 按阈值提取等值面。如果开启平滑,会使用 vtkWindowedSincPolyDataFilter;如果需要减少面片,可用 vtkQuadricDecimation 或 vtkDecimatePro;最后通过 vtkPolyDataNormals 生成法线,交给 vtkPolyDataMapper 和 vtkActor 渲染。

VTK 渲染在项目里使用独立 executor 和 session。这样做可以避免多个 3D 操作同时打进同一个渲染上下文。交互时可以先走快速预览,再补最终帧,减少拖拽时的卡顿感。

关键函数表

VTK 类或函数作用在项目中的意义
vtkImageDataVTK 的规则体数据容器承接 NumPy volume 和 spacing
numpy_to_vtkNumPy 数组转 VTK 标量Python 影像计算和 VTK 渲染的桥
vtkSmartVolumeMapper体渲染 mapper根据环境选择合适渲染路径
vtkColorTransferFunction标量到颜色的映射决定骨骼、组织等颜色表现
vtkPiecewiseFunction标量到透明度的映射决定哪些 HU 区间可见
vtkFlyingEdges3D快速等值面提取表面渲染的核心
vtkWindowToImageFilter渲染窗口转图像后端把 3D 结果推给前端

自绘图表

NumPy volume (z, y, x) float32 连续内存 ascontiguous numpy_to_vtk 标量桥接 vtkImageData dimensions + spacing VTK 不直接理解 DICOM,它理解规则体数据 pydicom 和 NumPy 负责把 DICOM 整理成 VTK 可消费的 volume。
3D 渲染前必须完成从 DICOM 语义到 VTK 规则网格的转换。
Volume Rendering 沿视线采样,按 transfer function 累积颜色和透明度
<rect x="525" y="80" width="390" height="270" rx="18" fill="var(--surface)" stroke="var(--line)" />
<text x="720" y="122" text-anchor="middle" fill="currentColor" font-size="18" font-weight="900">Surface Rendering</text>
<path d="M620 250 C610 180 665 150 720 168 C785 190 818 230 780 270 C740 315 650 305 620 250 Z" fill="color-mix(in srgb, var(--accent) 15%, transparent)" stroke="var(--accent)" stroke-width="3" />
<path d="M640 250 L695 182 L760 265 M695 182 L790 232 M640 250 L760 265" fill="none" stroke="var(--line-strong)" />
<text x="720" y="310" text-anchor="middle" fill="var(--muted)" font-size="14">先提取等值面,再渲染三角网格、法线和材质</text>
体渲染看内部密度分布,表面渲染看阈值对应的几何边界。
Phase 00 Phase 10 Phase 20 Phase 30 Phase 40 播放循环

小结

VTK 让 DicomVision 不必从零实现 3D 渲染器。项目真正要做好的,是把 DICOM 序列整理成正确的 volume,把 spacing、方向、dtype 和连续内存准备好,再把交互状态稳定映射到 VTK session。4D 则是在这个基础上增加时间 phase,而不是推翻已有模型。