如何使用VTK与PyQt5技术进行医学三维数据的渲染与交互展示?

2026-05-06 04:321阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2314个文字,预计阅读时间需要10分钟。

如何使用VTK与PyQt5技术进行医学三维数据的渲染与交互展示?

PyQt5和VTK结合使用介绍:本文简要介绍如何利用PyQt5和VTK来渲染三维体数据,并将其集成到PyQt的UI框架中。在医学相关项目中,有时可能需要渲染三维数据,以下是一些可能的场景:

简介:在一些医学相关的项目(可能包括学生的作业或毕业设计)中,经常需要处理和展示三维数据。例如,在医学影像处理、生物力学研究等领域,三维数据的可视化对于理解数据和分析结果至关重要。

场景:以下是一些可能需要渲染三维数据的场景:

1. 医学影像分析:例如,CT、MRI等医学影像的三维重建和可视化。

2.生物力学研究:例如,模拟人体器官或组织的力学行为。

3.学生作业或毕业设计:例如,展示三维模型或进行相关实验。

需求:

在某些情况下,可能需要以下功能:

- 渲染高质量的三维体数据。- 支持多种数据格式。- 集成到PyQt的UI框架中。- 提供交互式操作,如旋转、缩放、平移等。

本文简单介绍一下,如何利用PyQt5和VTK来渲染体数据(三维数据),并集成进PyQt的UI框架中。 简介

在一些医学相关的简单的项目(也许是学生的作业?毕业设计?)中,有时候可能需要集成一些可视化的功能,本文简单介绍一下,如何利用PyQt5和VTK来渲染体数据(三维数据),并集成进PyQt的UI框架中。

代码在仓库 github.com/MangoWAY/medicalImageScriptDemo

如何使用VTK与PyQt5技术进行医学三维数据的渲染与交互展示?

环境

主要依赖两个python的包

  • PyQt5
  • VTK

最好用 Anaconda 来管理你的 python 的环境,可以利用 pip 来安装上述的包,如何安装网上有许多教程,这里不介绍了。

功能展示
  • 添加体数据
  • 删除体数据
  • 选择合适的预制的颜色函数
  • 缩放、旋转
代码介绍
  • Ui_MainWindow 这个类是主要用来定义UI的一些布局,

  • VolumeRendering这个类主要用来处理一些渲染的逻辑,主要是用了VTK的一些现有的渲染方法。

  • readXML 函数用来读取 xml 文件,因为人体不同的组织、器官在医学影像中,呈现的强度不同,因此根据这些强度值可以设定一些渲染参数,来绘制不同的人体组织。这些渲染参数如何设置?是有现成的设定好的参数可以参考的,本文采用了 3DSlicer 这个软件中的设置方式,参数被保存在preset.xml 文件中,这个文件是我从 3DSlicer 中拷贝过来的。

  • buildProperty 这个函数,根据 preset.xml 中的参数,构建 VTK 的渲染参数,主要是各种传输函数,这里不过多的对传输函数进行解释了,例如颜色传输函数可以理解为,例如强度在0-100的区域用红色,100-200的用蓝色...这个意思。注意:这个函数里有一个常数值 + 1000,这个和我仓库里的test data 的数据分布有关,因为我的数据是从0开始的,0 代表正常数据的 -1000,所以如果你的数据是正常的范围的话,可以不加 1000,可以自己试试。

  • 渲染:这里用的是 vtkGPUVolumeRayCastMapper这个函数进行渲染,利用GPU加速,绘制的会比较快。

  • 数据:这里使用的 mha 数据的文件,其实用格式的文件都可以,这里为了方便采用了mha,如果需要读取别的格式,改一下读取数据部分就可以了,对格式不太了解的同学可以看看我之前写的一篇关于不同医学图像格式读写的文章。

  • 其他:其他的相关部分,可以直接看代码,代码并不复杂,只有 200+行,可能会一些小 bug,不过不要紧,这里主要的目的是为了展示:

    1. 如何在 python 中用 VTK 渲染体数据;
    2. 如何集成 VTK 渲染进 PyQt5;
    3. 如何构建合适的渲染参数(和3DSlicer一样)。
完整代码

import os from PyQt5 import QtCore from PyQt5 import QtGui from PyQt5 import QtCore, QtGui, QtWidgets from functools import partial from PyQt5.QtWidgets import QFrame,QVBoxLayout,QFileDialog, QMainWindow,QApplication from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor from vtkmodules.vtkCommonColor import vtkNamedColors from vtkmodules.vtkCommonDataModel import vtkPiecewiseFunction from vtkmodules.vtkIOImage import vtkMetaImageReader from vtkmodules.vtkRenderingCore import ( vtkColorTransferFunction, vtkRenderer, vtkVolume, vtkVolumeProperty ) from vtkmodules.vtkRenderingVolume import vtkGPUVolumeRayCastMapper from xml.dom.minidom import parse class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(1140, 887) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget) self.horizontalLayout.setObjectName("horizontalLayout") self.Tapw = QtWidgets.QTabWidget(self.centralwidget) font = QtGui.QFont() font.setPointSize(12) self.Tapw.setFont(font) self.Tapw.setObjectName("Tapw") self.Rendering = QtWidgets.QWidget() self.Rendering.setObjectName("Rendering") self.VTK = QtWidgets.QWidget(self.Rendering) self.VTK.setGeometry(QtCore.QRect(0, 10, 931, 771)) self.VTK.setObjectName("VTK") self.RenPreset = QtWidgets.QComboBox(self.Rendering) self.RenPreset.setGeometry(QtCore.QRect(950, 510, 161, 31)) self.RenPreset.setObjectName("RenPreset") self.RenList = QtWidgets.QListWidget(self.Rendering) self.RenList.setGeometry(QtCore.QRect(950, 160, 161, 281)) self.RenList.setObjectName("RenList") self.RenDelVolBtn = QtWidgets.QPushButton(self.Rendering) self.RenDelVolBtn.setGeometry(QtCore.QRect(940, 80, 171, 51)) self.RenDelVolBtn.setObjectName("RenDelVolBtn") self.RenLoadVolBtn = QtWidgets.QPushButton(self.Rendering) self.RenLoadVolBtn.setGeometry(QtCore.QRect(940, 20, 171, 51)) self.RenLoadVolBtn.setObjectName("RenLoadVolBtn") self.label_4 = QtWidgets.QLabel(self.Rendering) self.label_4.setGeometry(QtCore.QRect(990, 460, 101, 31)) self.label_4.setObjectName("label_4") self.RenChange = QtWidgets.QPushButton(self.Rendering) self.RenChange.setGeometry(QtCore.QRect(940, 570, 171, 51)) self.RenChange.setObjectName("RenChange") self.Tapw.addTab(self.Rendering, "") self.horizontalLayout.addWidget(self.Tapw) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 1140, 23)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) self.Tapw.setCurrentIndex(1) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "Volume Rendering")) self.RenDelVolBtn.setText(_translate("MainWindow", "Remove Volume")) self.RenLoadVolBtn.setText(_translate("MainWindow", "Add Volume")) self.label_4.setText(_translate("MainWindow", "Transfer Func")) self.RenChange.setText(_translate("MainWindow", "Change Transfer Func")) self.Tapw.setTabText(self.Tapw.indexOf(self.Rendering), _translate("MainWindow", "Volume Rendering")) class VolumeRendering(): def __init__(self,ui:Ui_MainWindow) -> None: self.frame = QFrame() self.ui = ui self.vl = QVBoxLayout() self.vtkWidget = QVTKRenderWindowInteractor(self.frame) self.vl.addWidget(self.vtkWidget) self.ren = vtkRenderer() self.vtkWidget.GetRenderWindow().AddRenderer(self.ren) self.iren = self.vtkWidget.GetRenderWindow().GetInteractor() self.colors = vtkNamedColors() self.vtkpros = {} self.colors.SetColor('BkgColor', [0, 0,0 , 255]) self.ren.SetBackground(self.colors.GetColor3d('BkgColor')) self.frame.setLayout(self.vl) self.iren.Initialize() self.volumes = {} self.volume_property = vtkVolumeProperty() self.volume_color = vtkColorTransferFunction() self.volume_scalar_opacity = vtkPiecewiseFunction() self.volume_gradient_opacity = vtkPiecewiseFunction() self.setDefaultProperty() ui.RenLoadVolBtn.clicked.connect(partial(self.addVolumeBtn)) ui.RenDelVolBtn.clicked.connect(partial(self.delVolumeBtn)) ui.RenChange.clicked.connect(partial(self.changeBtn)) self.readXML() self.volume_property = self.vtkpros["CT-AAA"] def readXML(self): # read preset property from preset.xml # the preset.xml is copied from 3D-Slicer cur_dir = os.path.dirname(os.path.abspath(__file__)) domtree = parse(os.path.join(cur_dir,"preset.xml")) data = domtree.documentElement propertys = data.getElementsByTagName('VolumeProperty') for pro in propertys: name = pro.getAttribute('name') go = pro.getAttribute('gradientOpacity') so = pro.getAttribute('scalarOpacity') ctrans = pro.getAttribute('colorTransfer') vtkpro = self.buildProperty(go,so,ctrans) self.vtkpros[name] = vtkpro self.ui.RenPreset.addItem(name) def buildProperty(self,go,so,ctrans): vtkcoltrans = vtkColorTransferFunction() data = ctrans.split() data = [float(x) for x in data] for i in range(int(data[0]/4)): base = 1 + i * 4 # add 1000 because the intensity of our test data is started from 0 # if you use the normal data, you can delete the const 1000. vtkcoltrans.AddRGBPoint(data[base] + 1000, data[base+1], data[base+2], data[base+3]) vtkgo = vtkPiecewiseFunction() data = go.split() data = [float(x) for x in data] for i in range(int(data[0]/2)): base = 1 + i * 2 vtkgo.AddPoint(data[base]+ 1000, data[base+1]) vtkso = vtkPiecewiseFunction() data = so.split() data = [float(x) for x in data] for i in range(int(data[0]/2)): base = 1 + i * 2 vtkso.AddPoint(data[base]+ 1000, data[base+1]) vtkpro = vtkVolumeProperty() vtkpro.SetColor(vtkcoltrans) vtkpro.SetScalarOpacity(vtkso) #vtkpro.SetGradientOpacity(vtkgo) vtkpro.SetInterpolationTypeToLinear() vtkpro.ShadeOn() vtkpro.SetAmbient(0.4) vtkpro.SetDiffuse(0.6) vtkpro.SetSpecular(0.2) return vtkpro # add volume to the scene def addVolumeBtn(self): file_name,_ =QFileDialog.getOpenFileName( self.ui.Rendering,"open file dialog",os.getcwd(),"Mha(*.mha)") if file_name == "": return self.addVolume(file_name) self.ui.RenList.addItem(file_name) # delete volume from scene def delVolumeBtn(self): if not self.ui.RenList.itemAt(0,0): return curVol = self.ui.RenList.currentItem() if not curVol: curVol = self.ui.RenList.itemAt(0,0) curVol = curVol.text() self.delVolume(curVol) print(curVol) #change transfer function def changeBtn(self): curVol = self.ui.RenList.currentItem() if not curVol: curVol = self.ui.RenList.itemAt(0,0) curVol = curVol.text() if curVol in self.volumes.keys(): self.volumes[curVol].SetProperty(self.vtkpros[self.ui.RenPreset.currentText()]) def setDefaultProperty(self): self.volume_color.AddRGBPoint(0, 0.0, 0.0, 0.0) self.volume_color.AddRGBPoint(500, 240.0 / 255.0, 184.0 / 255.0, 160.0 / 255.0) self.volume_color.AddRGBPoint(1000, 240.0 / 255.0, 184.0 / 255.0, 160.0 / 255.0) self.volume_color.AddRGBPoint(1150, 1.0, 1.0, 240.0 / 255.0) # Ivory self.volume_scalar_opacity = vtkPiecewiseFunction() self.volume_scalar_opacity.AddPoint(0, 0.00) self.volume_scalar_opacity.AddPoint(500, 0.15) self.volume_scalar_opacity.AddPoint(1000, 0.15) self.volume_scalar_opacity.AddPoint(1150, 0.85) self.volume_gradient_opacity = vtkPiecewiseFunction() self.volume_gradient_opacity.AddPoint(0, 0.0) self.volume_gradient_opacity.AddPoint(90, 0.5) self.volume_gradient_opacity.AddPoint(100, 1.0) self.volume_property = vtkVolumeProperty() self.volume_property.SetColor(self.volume_color) self.volume_property.SetScalarOpacity(self.volume_scalar_opacity) self.volume_property.SetGradientOpacity(self.volume_gradient_opacity) self.volume_property.SetInterpolationTypeToLinear() self.volume_property.ShadeOn() self.volume_property.SetAmbient(0.4) self.volume_property.SetDiffuse(0.6) self.volume_property.SetSpecular(0.2) def delVolume(self,path): self.ren.RemoveViewProp(self.volumes[path]) item = self.ui.RenList.findItems(path,QtCore.Qt.MatchExactly)[0] row = self.ui.RenList.row(item) self.ui.RenList.takeItem(row) self.volumes.pop(path) self.iren.Initialize() def addVolume(self,path): reader = vtkMetaImageReader() reader.SetFileName(path) volume_mapper = vtkGPUVolumeRayCastMapper() volume_mapper.SetInputConnection(reader.GetOutputPort()) volume = vtkVolume() volume.SetMapper(volume_mapper) volume.SetProperty(self.volume_property) self.ren.AddViewProp(volume) self.volumes[path] = volume camera = self.ren.GetActiveCamera() c = volume.GetCenter() camera.SetViewUp(0, 0, -1) camera.SetPosition(c[0], c[1] - 800, c[2]-200) camera.SetFocalPoint(c[0], c[1], c[2]) camera.Azimuth(30.0) camera.Elevation(30.0) self.iren.Initialize() def main(): app = QApplication([]) mainw = QMainWindow() ui = Ui_MainWindow() ui.setupUi(mainw) mvtk = VolumeRendering(ui) mvtk.frame.setGeometry(0,0,1000,1000) mvtk.frame.setParent(ui.VTK) mainw.show() app.exec_() if __name__ == "__main__": main()

本文共计2314个文字,预计阅读时间需要10分钟。

如何使用VTK与PyQt5技术进行医学三维数据的渲染与交互展示?

PyQt5和VTK结合使用介绍:本文简要介绍如何利用PyQt5和VTK来渲染三维体数据,并将其集成到PyQt的UI框架中。在医学相关项目中,有时可能需要渲染三维数据,以下是一些可能的场景:

简介:在一些医学相关的项目(可能包括学生的作业或毕业设计)中,经常需要处理和展示三维数据。例如,在医学影像处理、生物力学研究等领域,三维数据的可视化对于理解数据和分析结果至关重要。

场景:以下是一些可能需要渲染三维数据的场景:

1. 医学影像分析:例如,CT、MRI等医学影像的三维重建和可视化。

2.生物力学研究:例如,模拟人体器官或组织的力学行为。

3.学生作业或毕业设计:例如,展示三维模型或进行相关实验。

需求:

在某些情况下,可能需要以下功能:

- 渲染高质量的三维体数据。- 支持多种数据格式。- 集成到PyQt的UI框架中。- 提供交互式操作,如旋转、缩放、平移等。

本文简单介绍一下,如何利用PyQt5和VTK来渲染体数据(三维数据),并集成进PyQt的UI框架中。 简介

在一些医学相关的简单的项目(也许是学生的作业?毕业设计?)中,有时候可能需要集成一些可视化的功能,本文简单介绍一下,如何利用PyQt5和VTK来渲染体数据(三维数据),并集成进PyQt的UI框架中。

代码在仓库 github.com/MangoWAY/medicalImageScriptDemo

如何使用VTK与PyQt5技术进行医学三维数据的渲染与交互展示?

环境

主要依赖两个python的包

  • PyQt5
  • VTK

最好用 Anaconda 来管理你的 python 的环境,可以利用 pip 来安装上述的包,如何安装网上有许多教程,这里不介绍了。

功能展示
  • 添加体数据
  • 删除体数据
  • 选择合适的预制的颜色函数
  • 缩放、旋转
代码介绍
  • Ui_MainWindow 这个类是主要用来定义UI的一些布局,

  • VolumeRendering这个类主要用来处理一些渲染的逻辑,主要是用了VTK的一些现有的渲染方法。

  • readXML 函数用来读取 xml 文件,因为人体不同的组织、器官在医学影像中,呈现的强度不同,因此根据这些强度值可以设定一些渲染参数,来绘制不同的人体组织。这些渲染参数如何设置?是有现成的设定好的参数可以参考的,本文采用了 3DSlicer 这个软件中的设置方式,参数被保存在preset.xml 文件中,这个文件是我从 3DSlicer 中拷贝过来的。

  • buildProperty 这个函数,根据 preset.xml 中的参数,构建 VTK 的渲染参数,主要是各种传输函数,这里不过多的对传输函数进行解释了,例如颜色传输函数可以理解为,例如强度在0-100的区域用红色,100-200的用蓝色...这个意思。注意:这个函数里有一个常数值 + 1000,这个和我仓库里的test data 的数据分布有关,因为我的数据是从0开始的,0 代表正常数据的 -1000,所以如果你的数据是正常的范围的话,可以不加 1000,可以自己试试。

  • 渲染:这里用的是 vtkGPUVolumeRayCastMapper这个函数进行渲染,利用GPU加速,绘制的会比较快。

  • 数据:这里使用的 mha 数据的文件,其实用格式的文件都可以,这里为了方便采用了mha,如果需要读取别的格式,改一下读取数据部分就可以了,对格式不太了解的同学可以看看我之前写的一篇关于不同医学图像格式读写的文章。

  • 其他:其他的相关部分,可以直接看代码,代码并不复杂,只有 200+行,可能会一些小 bug,不过不要紧,这里主要的目的是为了展示:

    1. 如何在 python 中用 VTK 渲染体数据;
    2. 如何集成 VTK 渲染进 PyQt5;
    3. 如何构建合适的渲染参数(和3DSlicer一样)。
完整代码

import os from PyQt5 import QtCore from PyQt5 import QtGui from PyQt5 import QtCore, QtGui, QtWidgets from functools import partial from PyQt5.QtWidgets import QFrame,QVBoxLayout,QFileDialog, QMainWindow,QApplication from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor from vtkmodules.vtkCommonColor import vtkNamedColors from vtkmodules.vtkCommonDataModel import vtkPiecewiseFunction from vtkmodules.vtkIOImage import vtkMetaImageReader from vtkmodules.vtkRenderingCore import ( vtkColorTransferFunction, vtkRenderer, vtkVolume, vtkVolumeProperty ) from vtkmodules.vtkRenderingVolume import vtkGPUVolumeRayCastMapper from xml.dom.minidom import parse class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(1140, 887) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget) self.horizontalLayout.setObjectName("horizontalLayout") self.Tapw = QtWidgets.QTabWidget(self.centralwidget) font = QtGui.QFont() font.setPointSize(12) self.Tapw.setFont(font) self.Tapw.setObjectName("Tapw") self.Rendering = QtWidgets.QWidget() self.Rendering.setObjectName("Rendering") self.VTK = QtWidgets.QWidget(self.Rendering) self.VTK.setGeometry(QtCore.QRect(0, 10, 931, 771)) self.VTK.setObjectName("VTK") self.RenPreset = QtWidgets.QComboBox(self.Rendering) self.RenPreset.setGeometry(QtCore.QRect(950, 510, 161, 31)) self.RenPreset.setObjectName("RenPreset") self.RenList = QtWidgets.QListWidget(self.Rendering) self.RenList.setGeometry(QtCore.QRect(950, 160, 161, 281)) self.RenList.setObjectName("RenList") self.RenDelVolBtn = QtWidgets.QPushButton(self.Rendering) self.RenDelVolBtn.setGeometry(QtCore.QRect(940, 80, 171, 51)) self.RenDelVolBtn.setObjectName("RenDelVolBtn") self.RenLoadVolBtn = QtWidgets.QPushButton(self.Rendering) self.RenLoadVolBtn.setGeometry(QtCore.QRect(940, 20, 171, 51)) self.RenLoadVolBtn.setObjectName("RenLoadVolBtn") self.label_4 = QtWidgets.QLabel(self.Rendering) self.label_4.setGeometry(QtCore.QRect(990, 460, 101, 31)) self.label_4.setObjectName("label_4") self.RenChange = QtWidgets.QPushButton(self.Rendering) self.RenChange.setGeometry(QtCore.QRect(940, 570, 171, 51)) self.RenChange.setObjectName("RenChange") self.Tapw.addTab(self.Rendering, "") self.horizontalLayout.addWidget(self.Tapw) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 1140, 23)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) self.Tapw.setCurrentIndex(1) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "Volume Rendering")) self.RenDelVolBtn.setText(_translate("MainWindow", "Remove Volume")) self.RenLoadVolBtn.setText(_translate("MainWindow", "Add Volume")) self.label_4.setText(_translate("MainWindow", "Transfer Func")) self.RenChange.setText(_translate("MainWindow", "Change Transfer Func")) self.Tapw.setTabText(self.Tapw.indexOf(self.Rendering), _translate("MainWindow", "Volume Rendering")) class VolumeRendering(): def __init__(self,ui:Ui_MainWindow) -> None: self.frame = QFrame() self.ui = ui self.vl = QVBoxLayout() self.vtkWidget = QVTKRenderWindowInteractor(self.frame) self.vl.addWidget(self.vtkWidget) self.ren = vtkRenderer() self.vtkWidget.GetRenderWindow().AddRenderer(self.ren) self.iren = self.vtkWidget.GetRenderWindow().GetInteractor() self.colors = vtkNamedColors() self.vtkpros = {} self.colors.SetColor('BkgColor', [0, 0,0 , 255]) self.ren.SetBackground(self.colors.GetColor3d('BkgColor')) self.frame.setLayout(self.vl) self.iren.Initialize() self.volumes = {} self.volume_property = vtkVolumeProperty() self.volume_color = vtkColorTransferFunction() self.volume_scalar_opacity = vtkPiecewiseFunction() self.volume_gradient_opacity = vtkPiecewiseFunction() self.setDefaultProperty() ui.RenLoadVolBtn.clicked.connect(partial(self.addVolumeBtn)) ui.RenDelVolBtn.clicked.connect(partial(self.delVolumeBtn)) ui.RenChange.clicked.connect(partial(self.changeBtn)) self.readXML() self.volume_property = self.vtkpros["CT-AAA"] def readXML(self): # read preset property from preset.xml # the preset.xml is copied from 3D-Slicer cur_dir = os.path.dirname(os.path.abspath(__file__)) domtree = parse(os.path.join(cur_dir,"preset.xml")) data = domtree.documentElement propertys = data.getElementsByTagName('VolumeProperty') for pro in propertys: name = pro.getAttribute('name') go = pro.getAttribute('gradientOpacity') so = pro.getAttribute('scalarOpacity') ctrans = pro.getAttribute('colorTransfer') vtkpro = self.buildProperty(go,so,ctrans) self.vtkpros[name] = vtkpro self.ui.RenPreset.addItem(name) def buildProperty(self,go,so,ctrans): vtkcoltrans = vtkColorTransferFunction() data = ctrans.split() data = [float(x) for x in data] for i in range(int(data[0]/4)): base = 1 + i * 4 # add 1000 because the intensity of our test data is started from 0 # if you use the normal data, you can delete the const 1000. vtkcoltrans.AddRGBPoint(data[base] + 1000, data[base+1], data[base+2], data[base+3]) vtkgo = vtkPiecewiseFunction() data = go.split() data = [float(x) for x in data] for i in range(int(data[0]/2)): base = 1 + i * 2 vtkgo.AddPoint(data[base]+ 1000, data[base+1]) vtkso = vtkPiecewiseFunction() data = so.split() data = [float(x) for x in data] for i in range(int(data[0]/2)): base = 1 + i * 2 vtkso.AddPoint(data[base]+ 1000, data[base+1]) vtkpro = vtkVolumeProperty() vtkpro.SetColor(vtkcoltrans) vtkpro.SetScalarOpacity(vtkso) #vtkpro.SetGradientOpacity(vtkgo) vtkpro.SetInterpolationTypeToLinear() vtkpro.ShadeOn() vtkpro.SetAmbient(0.4) vtkpro.SetDiffuse(0.6) vtkpro.SetSpecular(0.2) return vtkpro # add volume to the scene def addVolumeBtn(self): file_name,_ =QFileDialog.getOpenFileName( self.ui.Rendering,"open file dialog",os.getcwd(),"Mha(*.mha)") if file_name == "": return self.addVolume(file_name) self.ui.RenList.addItem(file_name) # delete volume from scene def delVolumeBtn(self): if not self.ui.RenList.itemAt(0,0): return curVol = self.ui.RenList.currentItem() if not curVol: curVol = self.ui.RenList.itemAt(0,0) curVol = curVol.text() self.delVolume(curVol) print(curVol) #change transfer function def changeBtn(self): curVol = self.ui.RenList.currentItem() if not curVol: curVol = self.ui.RenList.itemAt(0,0) curVol = curVol.text() if curVol in self.volumes.keys(): self.volumes[curVol].SetProperty(self.vtkpros[self.ui.RenPreset.currentText()]) def setDefaultProperty(self): self.volume_color.AddRGBPoint(0, 0.0, 0.0, 0.0) self.volume_color.AddRGBPoint(500, 240.0 / 255.0, 184.0 / 255.0, 160.0 / 255.0) self.volume_color.AddRGBPoint(1000, 240.0 / 255.0, 184.0 / 255.0, 160.0 / 255.0) self.volume_color.AddRGBPoint(1150, 1.0, 1.0, 240.0 / 255.0) # Ivory self.volume_scalar_opacity = vtkPiecewiseFunction() self.volume_scalar_opacity.AddPoint(0, 0.00) self.volume_scalar_opacity.AddPoint(500, 0.15) self.volume_scalar_opacity.AddPoint(1000, 0.15) self.volume_scalar_opacity.AddPoint(1150, 0.85) self.volume_gradient_opacity = vtkPiecewiseFunction() self.volume_gradient_opacity.AddPoint(0, 0.0) self.volume_gradient_opacity.AddPoint(90, 0.5) self.volume_gradient_opacity.AddPoint(100, 1.0) self.volume_property = vtkVolumeProperty() self.volume_property.SetColor(self.volume_color) self.volume_property.SetScalarOpacity(self.volume_scalar_opacity) self.volume_property.SetGradientOpacity(self.volume_gradient_opacity) self.volume_property.SetInterpolationTypeToLinear() self.volume_property.ShadeOn() self.volume_property.SetAmbient(0.4) self.volume_property.SetDiffuse(0.6) self.volume_property.SetSpecular(0.2) def delVolume(self,path): self.ren.RemoveViewProp(self.volumes[path]) item = self.ui.RenList.findItems(path,QtCore.Qt.MatchExactly)[0] row = self.ui.RenList.row(item) self.ui.RenList.takeItem(row) self.volumes.pop(path) self.iren.Initialize() def addVolume(self,path): reader = vtkMetaImageReader() reader.SetFileName(path) volume_mapper = vtkGPUVolumeRayCastMapper() volume_mapper.SetInputConnection(reader.GetOutputPort()) volume = vtkVolume() volume.SetMapper(volume_mapper) volume.SetProperty(self.volume_property) self.ren.AddViewProp(volume) self.volumes[path] = volume camera = self.ren.GetActiveCamera() c = volume.GetCenter() camera.SetViewUp(0, 0, -1) camera.SetPosition(c[0], c[1] - 800, c[2]-200) camera.SetFocalPoint(c[0], c[1], c[2]) camera.Azimuth(30.0) camera.Elevation(30.0) self.iren.Initialize() def main(): app = QApplication([]) mainw = QMainWindow() ui = Ui_MainWindow() ui.setupUi(mainw) mvtk = VolumeRendering(ui) mvtk.frame.setGeometry(0,0,1000,1000) mvtk.frame.setParent(ui.VTK) mainw.show() app.exec_() if __name__ == "__main__": main()