3D展示,nii
from vtkmodules.util.vtkImageImportFromArray import vtkImageImportFromArray
import vtk
import SimpleITK as sitk
import numpy as np
import cv2
# 读取 .nii.gz 文件并转换为 NumPy 数组
path = '/media/binz/YLJ/MZC/VoCo-main/Finetune/BTCV/outputs/lable_model/resampled_segmentation2.nii.gz' # segmentation volume
#path = '/media/binz/YLJ/MZC/VoCo-main/Finetune/BTCV/dataset/labelsTr/label0035.nii.gz'
#path = '/media/binz/YLJ/MZC/VoCo-main/Finetune/BTCV/dataset/imagesTr/img0035.nii.gz'
ds = sitk.ReadImage(path)# 读取指定路径的医学影像文件,返回一个 SimpleITK 图像对象。
data = sitk.GetArrayFromImage(ds)#将 SimpleITK 图像转换为 NumPy 数组。
print('ds: ', ds)
print('data: ', data)
print('shape_of_data', data.shape)
spacing = ds.GetSpacing() # 三维数据的间隔
print('spacing_of_data', spacing)
srange = [np.min(data), np.max(data)]
print('shape_of_data_changed', data.shape)
# 创建一个空的vtk类
img_arr = vtkImageImportFromArray()
print('img_arr: ', img_arr)
img_arr.SetArray(data) # 把array_data塞到vtkImageImportFromArray
img_arr.SetDataSpacing(spacing) # 设置spacing
origin = (0, 0, 0)
img_arr.SetDataOrigin(origin) # 设置vtk数据的坐标系原点
img_arr.Update()
print('spacing: ', spacing)
print('srange: ', srange)
def StartInteraction(obj, event):#用于控制渲染窗口的更新速率,以优化交互体验。
renWin.SetDesiredUpdateRate(10)
def EndInteraction(obj, event):
renWin.SetDesiredUpdateRate(0.001)
def ClipVolumeRender(obj, event):#用于根据用户通过交互式裁剪框定义的裁剪平面来裁剪体绘制。
obj.GetPlanes(planes)#获取裁剪框定义的裁剪平面。
volumeMapper.SetClippingPlanes(planes)#将这些裁剪平面应用到体绘制映射器上,实现裁剪效果。
renWin.Render()#触发渲染窗口重新渲染,以更新显示裁剪后的体绘制
def ToggleBoxWidgetVisibility(obj, event):#控制裁剪框的显示和隐藏
if boxWidget.GetEnabled():
boxWidget.Off()
else:
boxWidget.On()
renWin.Render()
# 创建渲染器、渲染窗口和交互器
ren = vtk.vtkRenderer()
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(ren)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
# 数据预处理
min_val = srange[0]
max_val = srange[1]
diff = max_val - min_val
inter = 4200 / diff#这个比例用于将图像数据的强度值线性变换到一个新的范围
shift = -min_val
print(f'min: {min_val}, max: {max_val}, inter: {inter}, shift: {shift}') # 打印图像数据(体素强度)的最小值、最大值、比例因子和偏移量
shifter = vtk.vtkImageShiftScale()#对图像数据进行比例缩放和偏移量调整,常用于调整图像数据的亮度和对比度。
shifter.SetShift(shift)#偏移量
shifter.SetScale(inter)#缩放比例
shifter.SetOutputScalarTypeToUnsignedShort()#设置输出数据的类型为无符号短整型
shifter.SetInputData(img_arr.GetOutput())# 设置输入数据为 img_arr 的输出,即之前从 NumPy 数组转换得到的 VTK 图像数据
shifter.ReleaseDataFlagOff()#关闭数据释放标志,这意味着在转换过程结束后,输入数据不会被自动释放,有助于管理内存使用。
shifter.Update()#更新 shifter 对象,执行数据转换操作。
# 设置传输函数
tfun = vtk.vtkPiecewiseFunction()#创建一个分段函数,用于定义标量不透明度映射,x:标量值。y:对应的不透明度
tfun.AddPoint(1129, 0)
tfun.AddPoint(1300.0, 0.1)
tfun.AddPoint(1600.0, 0.12)
tfun.AddPoint(2000.0, 0.13)
tfun.AddPoint(2200.0, 0.14)
tfun.AddPoint(2500.0, 0.16)
tfun.AddPoint(2800.0, 0.17)
tfun.AddPoint(3000.0, 0.18)
gradtfun = vtk.vtkPiecewiseFunction()#定义了体素梯度值到不透明度的映射。这在体绘制中用于增强边缘或减少噪声的可视化影响。x 是梯度值,y 是对应的不透明度
gradtfun.AddPoint(-1000, 9)
gradtfun.AddPoint(0.5, 9.9)
gradtfun.AddPoint(1, 10)
ctfun = vtk.vtkColorTransferFunction()#创建一个颜色传输函数,用于定义标量值到颜色的映射。
ctfun.AddRGBPoint(0.0, 0.5, 0.0, 0.0)
ctfun.AddRGBPoint(600.0, 1.0, 0.5, 0.5)
ctfun.AddRGBPoint(1280.0, 0.9, 0.2, 0.3)
ctfun.AddRGBPoint(1960.0, 0.81, 0.27, 0.1)
ctfun.AddRGBPoint(2200.0, 0.9, 0.2, 0.3)
ctfun.AddRGBPoint(2500.0, 1, 0.5, 0.5)
ctfun.AddRGBPoint(3024.0, 0.5, 0.5, 0.5)
# 设置体绘制属性和映射器
volumeMapper = vtk.vtkGPUVolumeRayCastMapper()
volumeMapper.SetInputData(shifter.GetOutput())
volumeProperty = vtk.vtkVolumeProperty()#设置体绘制的属性,如颜色、不透明度、插值类型等。
volumeProperty.SetColor(ctfun)
volumeProperty.SetScalarOpacity(tfun)
volumeProperty.SetInterpolationTypeToLinear()
volumeProperty.ShadeOn()
newvol = vtk.vtkVolume()
newvol.SetMapper(volumeMapper)
newvol.SetProperty(volumeProperty)
# 添加轮廓和体绘制到渲染器
outline = vtk.vtkOutlineFilter()
outline.SetInputConnection(shifter.GetOutputPort())
outlineMapper = vtk.vtkPolyDataMapper()
outlineMapper.SetInputConnection(outline.GetOutputPort())
outlineActor = vtk.vtkActor()
outlineActor.SetMapper(outlineMapper)
ren.AddActor(outlineActor)
ren.AddVolume(newvol)
ren.SetBackground(0, 0, 0)
renWin.SetSize(600, 600)
planes = vtk.vtkPlanes()
# 创建交互式裁剪框
boxWidget = vtk.vtkBoxWidget()# 创建裁剪框并设置交互器、裁剪区域等。
boxWidget.SetInteractor(iren)#将裁剪框与渲染窗口的交互器 (iren) 关联。这使得裁剪框能够响应用户的鼠标和键盘操作。
boxWidget.SetPlaceFactor(1.0)#设置裁剪框的大小相对于它所包围的体积的比例因子。1.0 表示裁剪框的大小将紧密地适应体积的边界。
boxWidget.PlaceWidget(newvol.GetBounds())#根据体积 (newvol) 的边界放置裁剪框。这个边界定义了裁剪框的初始位置和大小。
boxWidget.InsideOutOn()#启用“内外颠倒”模式。在这种模式下,裁剪框内的数据将被隐藏,而裁剪框外的数据将被显示。这对于去除感兴趣区域周围的数据很有用。
boxWidget.AddObserver("StartInteractionEvent", StartInteraction)#为裁剪框添加一个观察者,当用户开始与裁剪框交互时(例如,点击并开始拖动裁剪框),将调用 StartInteraction 函数。
boxWidget.AddObserver("InteractionEvent", ClipVolumeRender)#当用户正在与裁剪框交互时(例如,拖动裁剪框进行调整),将调用 ClipVolumeRender 函数。这个函数根据裁剪框的位置和大小更新体绘制的裁剪平面。
boxWidget.AddObserver("EndInteractionEvent", EndInteraction)
outlineProperty = boxWidget.GetOutlineProperty()#获取裁剪框轮廓的属性对象。这允许自定义裁剪框轮廓的外观。
outlineProperty.SetRepresentationToWireframe()#设置裁剪框轮廓的表示方式为线框模式。
outlineProperty.SetAmbient(1.0)#设置轮廓的环境光系数。这个值影响轮廓在不同光照条件下的显示效果。
outlineProperty.SetAmbientColor(1, 1, 1)#设置轮廓的环境光颜色为白色。
outlineProperty.SetLineWidth(9)#设置轮廓线的宽度。
selectedOutlineProperty = boxWidget.GetSelectedOutlineProperty()#获取裁剪框被选中时(例如,正在被用户调整时)轮廓的属性对象。
selectedOutlineProperty.SetRepresentationToWireframe()#设置被选中时裁剪框轮廓的表示方式为线框模式。
selectedOutlineProperty.SetAmbient(1.0)#设置被选中时轮廓的环境光系数
selectedOutlineProperty.SetAmbientColor(1, 0, 0)#设置被选中时轮廓的环境光颜色为红色,以区分普通状态。
selectedOutlineProperty.SetLineWidth(3)#设置被选中时轮廓线的宽度。
# 创建文本按钮
text_actor = vtk.vtkTextActor()
text_actor.SetInput("Show crop border")
text_actor.GetTextProperty().SetFontSize(18) # 设置字体大小
text_actor.GetTextProperty().SetColor(1.0, 0.0, 0.0) # 设置字体颜色
# 创建文本表示
text_representation = vtk.vtkTextRepresentation()
text_representation.GetPositionCoordinate().SetValue(0.01, 0.9) # 设置按钮位置
text_representation.GetPosition2Coordinate().SetValue(0.2, 0.1) # 设置按钮大小
text_representation.SetBorderThickness(2) # 设置边框厚度
text_representation.GetBorderProperty().SetColor(1.0, 1.0, 1.0) # 设置边框颜色
# 创建文本小部件
text_widget = vtk.vtkTextWidget()#创建一个 vtkTextWidget 实例。这个控件用于在渲染窗口中显示文本信息,并可以与之交互。
text_widget.SetInteractor(iren)#将文本控件与渲染窗口的交互器 (iren) 关联。这使得文本控件能够响应用户的交互操作,如鼠标点击。
text_widget.SetRepresentation(text_representation)#设置文本控件的表示方式
text_widget.SetTextActor(text_actor)#设置文本控件要显示的文本。
# 绑定文本按钮回调函数
text_widget.AddObserver(vtk.vtkCommand.EndInteractionEvent, ToggleBoxWidgetVisibility)#这个函数用于切换裁剪框的显示状态,即显示或隐藏裁剪框。
# 渲染并启动交互
ren.ResetCamera()#重置摄像机的位置和方向,使得渲染的场景完全适应渲染窗口。
iren.Initialize()#初始化渲染窗口的交互器。
renWin.Render()#触发渲染窗口的渲染过程。
boxWidget.On()#启用裁剪框控件。
text_widget.On()#启用文本控件
iren.Start()#启动渲染窗口的交互器。
dicom
import sys
import vtk
from PyQt5 import QtWidgets, QtCore, QtGui
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor # 注意这里使用的是 vtk.qt.QVTKRenderWindowInteractor
from vtkmodules.vtkRenderingOpenGL2 import vtkGenericOpenGLRenderWindow
class VTKRenderWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.vtk_widget = QVTKRenderWindowInteractor(self)
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.vtk_widget)
self.setLayout(self.layout)
self.ren = vtk.vtkRenderer()
self.vtk_widget.GetRenderWindow().AddRenderer(self.ren)
self.iren = self.vtk_widget.GetRenderWindow().GetInteractor()
self.vtk_widget.GetRenderWindow().SetInteractor(self.iren)
self.reader = vtk.vtkDICOMImageReader()
self.reader.SetDirectoryName('/media/binz/YLJ/MZC/brain')
self.reader.Update()
self.volume_mapper = vtk.vtkGPUVolumeRayCastMapper()
self.volume_mapper.SetInputData(self.reader.GetOutput())
self.volume_property = vtk.vtkVolumeProperty()
self.volume_property.ShadeOn()
self.volume_property.SetInterpolationTypeToLinear()
self.color_transfer_function = vtk.vtkColorTransferFunction()
self.color_transfer_function.AddRGBPoint(0, 0.0, 0.0, 0.0)
self.color_transfer_function.AddRGBPoint(500, 1.0, 0.5, 0.3)
self.color_transfer_function.AddRGBPoint(1000, 1.0, 0.5, 0.3)
self.color_transfer_function.AddRGBPoint(1150, 1.0, 1.0, 0.9)
self.opacity_transfer_function = vtk.vtkPiecewiseFunction()
self.opacity_transfer_function.AddPoint(0, 0.00)
self.opacity_transfer_function.AddPoint(200, 0.01)
self.opacity_transfer_function.AddPoint(500, 0.60)
self.opacity_transfer_function.AddPoint(800, 0.70)
self.opacity_transfer_function.AddPoint(1200, 1.0)
self.volume_property.SetColor(self.color_transfer_function)
self.volume_property.SetScalarOpacity(self.opacity_transfer_function)
self.volume = vtk.vtkVolume()
self.volume.SetMapper(self.volume_mapper)
self.volume.SetProperty(self.volume_property)
self.ren.AddVolume(self.volume)
self.iren.Initialize()
def start_interactor(self):
self.iren.Start()
class ControlPanel(QtWidgets.QWidget):
def __init__(self, vtk_window, parent=None):
super().__init__(parent)
self.vtk_window = vtk_window
self.layout = QtWidgets.QVBoxLayout()
self.sliders = []
self.textboxes = []
for i in range(5):
slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
slider.setRange(0, 100)
slider.setValue(50)
slider.valueChanged.connect(self.update_opacity)
self.layout.addWidget(slider)
self.sliders.append(slider)
textbox = QtWidgets.QLineEdit()
textbox.setValidator(QtGui.QIntValidator(0, 2000))
textbox.setText(str([0, 200, 500, 800, 1200][i]))
textbox.editingFinished.connect(self.update_opacity)
self.layout.addWidget(textbox)
self.textboxes.append(textbox)
self.setLayout(self.layout)
def update_opacity(self):
for i in range(5):
value = self.sliders[i].value() / 100
position = int(self.textboxes[i].text())
self.vtk_window.opacity_transfer_function.RemovePoint(position)
self.vtk_window.opacity_transfer_function.AddPoint(position, value)
self.vtk_window.vtk_widget.GetRenderWindow().Render()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle('VTK with PyQt5')
self.vtk_window = VTKRenderWindow(self)
self.setCentralWidget(self.vtk_window)
self.control_panel = ControlPanel(self.vtk_window, self)
self.dock_widget = QtWidgets.QDockWidget('Control Panel', self)
self.dock_widget.setWidget(self.control_panel)
self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.dock_widget)
def closeEvent(self, event):
self.vtk_window.iren.GetRenderWindow().Finalize() # 显式释放 VTK 资源
self.vtk_window.iren.TerminateApp()
event.accept()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
main_window.vtk_window.start_interactor() # 启动 VTK 交互器的事件循环
sys.exit(app.exec_())