vtk展示

 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_())

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值