update
This commit is contained in:
160
.gitignore
vendored
Normal file
160
.gitignore
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
2
.idea/crystalfly.iml
generated
2
.idea/crystalfly.iml
generated
@ -2,7 +2,7 @@
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="jdk" jdkName="Poetry (crystalfly) (2)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -3,5 +3,5 @@
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Poetry (crystalfly)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Poetry (crystalfly)" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Poetry (crystalfly) (2)" project-jdk-type="Python SDK" />
|
||||
</project>
|
10
.idea/poetry.xml
generated
Normal file
10
.idea/poetry.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PoetryConfigService">
|
||||
<option name="poetryVirtualenvPaths">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$/../../../ApplicationData/pypoetry/Cache/virtualenvs/crystalfly-e61ASWpP-py3.11/Scripts/python.exe" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -1,15 +1,18 @@
|
||||
import ctypes
|
||||
import sys
|
||||
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
from crystalfly.ui.main_window import MainWindow
|
||||
|
||||
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("crystalfly")
|
||||
|
||||
|
||||
def main():
|
||||
app = QApplication(sys.argv)
|
||||
window = MainWindow()
|
||||
window.show()
|
||||
app.exec()
|
||||
sys.exit(app.exec())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
52
crystalfly/test.py
Normal file
52
crystalfly/test.py
Normal file
@ -0,0 +1,52 @@
|
||||
import sys
|
||||
|
||||
from PySide6.QtCore import QThread, Signal, Slot
|
||||
from PySide6.QtWidgets import QWidget, QPushButton, QApplication, QLabel, QVBoxLayout
|
||||
from vtkmodules.vtkIOImage import vtkImageReader2
|
||||
|
||||
|
||||
class TestThread(QThread):
|
||||
timeSignal = Signal(str)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def run(self):
|
||||
reader = vtkImageReader2()
|
||||
reader.SetFileName("D:/Downloads/christmas_tree_512x499x512_uint16.raw")
|
||||
reader.SetFileDimensionality(3)
|
||||
reader.SetDataSpacing(0.1, 0.1, 0.1)
|
||||
reader.SetDataExtent(0, 511, 0, 498, 0, 511)
|
||||
reader.SetDataScalarTypeToUnsignedShort()
|
||||
reader.Update()
|
||||
self.timeSignal.emit("success")
|
||||
# for i in range(10):
|
||||
# print(i)
|
||||
# self.timeSignal.emit(str(i))
|
||||
# time.sleep(1)
|
||||
|
||||
|
||||
class MainView(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.resize(800, 600)
|
||||
self.test_thread = TestThread()
|
||||
main_layout = QVBoxLayout()
|
||||
self.label = QLabel("0")
|
||||
self.button = QPushButton('TEST')
|
||||
self.button.clicked.connect(self.button_clicked)
|
||||
main_layout.addWidget(self.label)
|
||||
main_layout.addWidget(self.button)
|
||||
self.setLayout(main_layout)
|
||||
self.test_thread.timeSignal.connect(self.label.setText)
|
||||
|
||||
@Slot()
|
||||
def button_clicked(self):
|
||||
self.test_thread.start()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
window = MainView()
|
||||
window.show()
|
||||
app.exec()
|
58
crystalfly/test_volumn.py
Normal file
58
crystalfly/test_volumn.py
Normal file
@ -0,0 +1,58 @@
|
||||
import vtk
|
||||
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
|
||||
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
|
||||
|
||||
|
||||
class VolumeRenderingWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
reader = vtk.vtkImageReader2()
|
||||
reader.SetDataExtent(0, 511, 0, 499, 0, 511)
|
||||
reader.SetDataByteOrderToLittleEndian()
|
||||
reader.SetDataScalarTypeToUnsignedShort()
|
||||
reader.SetFileName("D:/Downloads/christmas_tree_512x499x512_uint16.raw") # Replace with your file path
|
||||
reader.Update()
|
||||
|
||||
volume_mapper = vtk.vtkGPUVolumeRayCastMapper() # You can also use vtkFixedPointVolumeRayCastMapper
|
||||
volume_mapper.SetInputData(reader.GetOutput())
|
||||
|
||||
volume_property = vtk.vtkVolumeProperty()
|
||||
color_transfer_function = vtk.vtkColorTransferFunction()
|
||||
color_transfer_function.AddRGBPoint(0, 0, 0, 0) # Black
|
||||
color_transfer_function.AddRGBPoint(255, 1, 1, 1) # White
|
||||
volume_property.SetColor(color_transfer_function)
|
||||
|
||||
gradient_opacity_transfer_function = vtk.vtkPiecewiseFunction()
|
||||
gradient_opacity_transfer_function.AddPoint(0, 0.0)
|
||||
gradient_opacity_transfer_function.AddPoint(90, 0.5)
|
||||
gradient_opacity_transfer_function.AddPoint(100, 1.0)
|
||||
volume_property.SetGradientOpacity(gradient_opacity_transfer_function)
|
||||
|
||||
volume_actor = vtk.vtkVolume()
|
||||
volume_actor.SetMapper(volume_mapper)
|
||||
volume_actor.SetProperty(volume_property)
|
||||
|
||||
renderer = vtk.vtkRenderer()
|
||||
renderer.AddVolume(volume_actor)
|
||||
|
||||
# Create a PySide6 widget to embed the VTK rendering window
|
||||
vtk_widget = QVTKRenderWindowInteractor()
|
||||
vtk_widget.SetRenderWindow(renderer.GetRenderWindow())
|
||||
|
||||
# Set up the main window layout
|
||||
central_widget = QWidget()
|
||||
layout = QVBoxLayout()
|
||||
layout.addWidget(vtk_widget)
|
||||
central_widget.setLayout(layout)
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
# Set background color
|
||||
renderer.SetBackground(0.1, 0.2, 0.4)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication([])
|
||||
window = VolumeRenderingWindow()
|
||||
window.show()
|
||||
app.exec()
|
Binary file not shown.
@ -6,11 +6,13 @@ from vtkmodules.vtkIOImage import vtkMedicalImageReader2
|
||||
from crystalfly.model.image_reader import image_reader_model
|
||||
|
||||
|
||||
# from vtkmodules.vtkCommonCore import vtkCommand, VTK_INT
|
||||
|
||||
class ImageReaderThread(QThread):
|
||||
loadedSignal = Signal(Any)
|
||||
|
||||
def __init__(self, file_data):
|
||||
QThread.__init__(self)
|
||||
def __init__(self, file_data, parent=None):
|
||||
QThread.__init__(self, parent)
|
||||
self.file_data = file_data
|
||||
|
||||
def run(self):
|
||||
@ -28,7 +30,7 @@ class ImageReaderThread(QThread):
|
||||
self.file_data["size"]["z"] - 1,
|
||||
)
|
||||
# self.file_data["data_type"]
|
||||
reader.SetDataScalarTypeToUnsignedShort()
|
||||
reader.SetDataScalarType(self.file_data["data_type"])
|
||||
reader.Update()
|
||||
image_reader_model.loaded(reader.GetOutput())
|
||||
print("success")
|
||||
|
Binary file not shown.
Binary file not shown.
BIN
crystalfly/ui/__pycache__/volume_viewer.cpython-311.pyc
Normal file
BIN
crystalfly/ui/__pycache__/volume_viewer.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
112
crystalfly/ui/ctk.cpp
Normal file
112
crystalfly/ui/ctk.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
#include <vtkImageData.h>
|
||||
#include <vtkDICOMImageReader.h>
|
||||
#include <vtkRenderWindow.h>
|
||||
#include <vtkRenderer.h>
|
||||
#include <vtkInteractorStyleTrackballCamera.h>
|
||||
#include <vtkRenderWindowInteractor.h>
|
||||
#include <vtkVolumeProperty.h>
|
||||
#include <vtkPiecewiseFunction.h>
|
||||
#include <vtkColorTransferFunction.h>
|
||||
#include <vtkGPUVolumeRayCastMapper.h>
|
||||
#include <vtkPointData.h>
|
||||
#include <vtkCamera.h>
|
||||
#include <vtkNew.h>
|
||||
#include <vtkFloatArray.h>
|
||||
#include <vtkImageReader.h>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
//#include <vtkAutoInit.h>
|
||||
|
||||
//VTK_MODULE_INIT( vtkRenderingOpenGL2 );
|
||||
//VTK_MODULE_INIT( vtkRenderingVolumeOpenGL2 );
|
||||
int main()
|
||||
{
|
||||
|
||||
//raw data reader
|
||||
|
||||
vtkSmartPointer<vtkImageReader> reader = vtkSmartPointer<vtkImageReader>::New();
|
||||
|
||||
reader->SetFileName("/home/wjm/code/p_volume_rendering/data/marschner_lobb_41x41x41_uint8.raw");
|
||||
reader->SetFileDimensionality(3);//设置显示图像的维数
|
||||
reader->SetDataScalarType(VTK_UNSIGNED_CHAR);//VTK_UNSIGNED_short将数据转换为unsigned char型
|
||||
reader->SetDataExtent(0, 40, 0, 40, 0, 40);
|
||||
reader->SetDataSpacing(1, 1, 1); //设置像素间间距
|
||||
//reader->SetDataOrigin(0.0, 0.0, 0.0);//设置基准点,(一般没有用)做虚拟切片时可能会用的上
|
||||
reader->Update();
|
||||
|
||||
|
||||
|
||||
|
||||
//visualize the raw data
|
||||
|
||||
|
||||
// properties options
|
||||
vtkNew<vtkVolumeProperty> volumeProperty;
|
||||
volumeProperty->ShadeOn();
|
||||
volumeProperty->SetInterpolationType(VTK_LINEAR_INTERPOLATION);
|
||||
|
||||
// get the real range in hounsfield
|
||||
vtkDataArray *arr = reader->GetOutput()->GetPointData()->GetScalars();
|
||||
double range[2];
|
||||
arr->GetRange(range);
|
||||
|
||||
// 1D transfer functions
|
||||
vtkNew<vtkColorTransferFunction> colorTF;
|
||||
colorTF->AddRGBPoint(-200, 0.0, 0.0, 0.0);
|
||||
colorTF->AddRGBPoint(110, 0.4, 0.4, 1.0);
|
||||
colorTF->AddRGBPoint(512, 1.0, 1.0, 1.0);
|
||||
colorTF->AddRGBPoint(range[1], 0.9, 0.1, 0.3);
|
||||
|
||||
vtkNew<vtkPiecewiseFunction> scalarTF;
|
||||
//scalarTF->AddPoint(-200, 0.00);
|
||||
scalarTF->AddPoint(-255, 0.00);
|
||||
scalarTF->AddPoint(110, 0.00);
|
||||
scalarTF->AddPoint(512, 0.5);
|
||||
scalarTF->AddPoint(range[1], 0.9);
|
||||
|
||||
vtkNew<vtkPiecewiseFunction> gradientTF;
|
||||
gradientTF->AddPoint(-200, 0.0);
|
||||
gradientTF->AddPoint(range[1] / 4.0, 1.0);
|
||||
|
||||
volumeProperty->SetScalarOpacity(scalarTF);
|
||||
volumeProperty->SetGradientOpacity(gradientTF);
|
||||
volumeProperty->SetColor(colorTF);
|
||||
|
||||
// setup rendering context
|
||||
vtkNew<vtkRenderWindow> renderWindow;
|
||||
renderWindow->SetSize(512, 512);
|
||||
renderWindow->SetMultiSamples(0);
|
||||
|
||||
// mapping data
|
||||
vtkNew<vtkGPUVolumeRayCastMapper> mapper;
|
||||
mapper->SetInputConnection(reader->GetOutputPort());
|
||||
mapper->SetBlendModeToComposite();
|
||||
mapper->SetUseJittering(1);
|
||||
|
||||
// renderer and volume
|
||||
vtkNew<vtkRenderer> renderer;
|
||||
renderWindow->AddRenderer(renderer);
|
||||
renderer->SetBackground(0.03, 0.33, 0.33);
|
||||
|
||||
vtkNew<vtkVolume> volume;
|
||||
volume->SetMapper(mapper);
|
||||
volume->SetProperty(volumeProperty);
|
||||
renderer->AddVolume(volume);
|
||||
|
||||
renderer->ResetCamera();
|
||||
renderer->GetActiveCamera()->Zoom(1.3);
|
||||
|
||||
vtkNew<vtkRenderWindowInteractor> interactor;
|
||||
interactor->SetRenderWindow(renderWindow);
|
||||
|
||||
vtkNew<vtkInteractorStyleTrackballCamera> style;
|
||||
interactor->SetInteractorStyle(style);
|
||||
|
||||
renderWindow->Render();
|
||||
|
||||
interactor->Start();
|
||||
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
@ -3,6 +3,8 @@ from pathlib import Path
|
||||
from PySide6.QtCore import Slot, QEvent
|
||||
from PySide6.QtUiTools import loadUiType
|
||||
from PySide6.QtWidgets import QDialog, QFileDialog
|
||||
from vtkmodules.vtkCommonCore import VTK_INT, VTK_SHORT, VTK_LONG, VTK_FLOAT, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_INT, \
|
||||
VTK_UNSIGNED_LONG
|
||||
|
||||
from crystalfly.thread.image_reader import ImageReaderThread
|
||||
|
||||
@ -12,7 +14,7 @@ ImportFileType, _ = loadUiType(str(Path(__file__).parent.joinpath("import_file.u
|
||||
class ImportFile(QDialog, ImportFileType):
|
||||
def __init__(self):
|
||||
super(ImportFile, self).__init__()
|
||||
self.image_read_thread = None
|
||||
# self.image_read_thread = None
|
||||
self.setupUi(self)
|
||||
|
||||
self.browse_file.clicked.connect(self.browse_file_handler)
|
||||
@ -30,19 +32,34 @@ class ImportFile(QDialog, ImportFileType):
|
||||
|
||||
@Slot()
|
||||
def load_image_file(self):
|
||||
data_type_text = self.data_type_combobox.currentText()
|
||||
data_type = None
|
||||
if data_type_text == "int 8":
|
||||
data_type = VTK_INT
|
||||
elif data_type_text == "uint 8":
|
||||
data_type = VTK_UNSIGNED_INT
|
||||
elif data_type_text == "int 16":
|
||||
data_type = VTK_SHORT
|
||||
elif data_type_text == "uint 16":
|
||||
data_type = VTK_UNSIGNED_SHORT
|
||||
elif data_type_text == "int 32":
|
||||
data_type = VTK_LONG
|
||||
elif data_type_text == "uint 32":
|
||||
data_type = VTK_UNSIGNED_LONG
|
||||
elif data_type_text == "float":
|
||||
data_type = VTK_FLOAT
|
||||
file_data = {
|
||||
"file_path": self.file_path_input.text(),
|
||||
"data_type": self.data_type_combobox.currentText(),
|
||||
"data_type": data_type,
|
||||
"size": {
|
||||
"x": self.size_x_spin.value(),
|
||||
"y": self.size_y_spin.value(),
|
||||
"z": self.size_z_spin.value(),
|
||||
}
|
||||
}
|
||||
self.image_read_thread = ImageReaderThread(file_data)
|
||||
image_read_thread = ImageReaderThread(file_data, self)
|
||||
# self.image_read_thread.loadedSignal.connect(self.set_image)
|
||||
self.image_read_thread.start()
|
||||
|
||||
image_read_thread.start()
|
||||
|
||||
@Slot()
|
||||
def browse_file_handler(self):
|
||||
|
@ -3,8 +3,10 @@ from pathlib import Path
|
||||
from PySide6.QtCore import Slot
|
||||
from PySide6.QtUiTools import loadUiType
|
||||
from PySide6.QtWidgets import QMainWindow
|
||||
from vtkmodules.vtkRenderingOpenGL2 import vtkGenericOpenGLRenderWindow
|
||||
|
||||
from crystalfly.ui.import_file import ImportFile
|
||||
from crystalfly.ui.volume_viewer import VolumeViewer
|
||||
from crystalfly.ui.vtk_viewer import VTKImageViewer
|
||||
|
||||
MainWindowType, _ = loadUiType(str(Path(__file__).parent.joinpath("main_window.ui")))
|
||||
@ -13,22 +15,60 @@ MainWindowType, _ = loadUiType(str(Path(__file__).parent.joinpath("main_window.u
|
||||
class MainWindow(QMainWindow, MainWindowType):
|
||||
def __init__(self):
|
||||
super(MainWindow, self).__init__()
|
||||
self.volume = None
|
||||
self.transverse = None
|
||||
self.sagittal = None
|
||||
self.coronal = None
|
||||
self.vtk_render_window = vtkGenericOpenGLRenderWindow()
|
||||
self.setupUi(self)
|
||||
self.file_open_dialog = ImportFile()
|
||||
self.file_open.triggered.connect(self.handle_file_open)
|
||||
self.init_four_pane()
|
||||
self.is_pane_maximum = False
|
||||
|
||||
def init_four_pane(self):
|
||||
coronal = VTKImageViewer()
|
||||
coronal.image_viewer.SetSliceOrientationToYZ()
|
||||
sagittal = VTKImageViewer()
|
||||
sagittal.image_viewer.SetSliceOrientationToXZ()
|
||||
transverse = VTKImageViewer()
|
||||
transverse.image_viewer.SetSliceOrientationToXY()
|
||||
self.coronal = VTKImageViewer(1)
|
||||
self.sagittal = VTKImageViewer(3)
|
||||
self.transverse = VTKImageViewer(4)
|
||||
self.volume = VolumeViewer(2)
|
||||
self.coronal.image_viewer.SetSliceOrientationToYZ()
|
||||
self.sagittal.image_viewer.SetSliceOrientationToXZ()
|
||||
self.transverse.image_viewer.SetSliceOrientationToXY()
|
||||
|
||||
self.volume.doubleClicked.connect(self.cell_max)
|
||||
self.coronal.doubleClicked.connect(self.cell_max)
|
||||
self.sagittal.doubleClicked.connect(self.cell_max)
|
||||
self.transverse.doubleClicked.connect(self.cell_max)
|
||||
# self.gridLayout.addWidget()
|
||||
self.gridLayout_2.addWidget(coronal)
|
||||
self.gridLayout_3.addWidget(sagittal)
|
||||
self.gridLayout_4.addWidget(transverse)
|
||||
self.volume_grid_layout.addWidget(self.volume)
|
||||
self.coronal_grid_layout.addWidget(self.coronal)
|
||||
self.gridLayout_3.addWidget(self.sagittal)
|
||||
self.gridLayout_4.addWidget(self.transverse)
|
||||
|
||||
@Slot(int)
|
||||
def cell_max(self, quadrant: int):
|
||||
print(quadrant)
|
||||
if self.is_pane_maximum:
|
||||
self.widget_3d.show()
|
||||
self.widget_coronal.show()
|
||||
self.widget_sagittal.show()
|
||||
self.widget_transverse.show()
|
||||
self.is_pane_maximum = False
|
||||
else:
|
||||
self.widget_3d.hide()
|
||||
self.widget_coronal.hide()
|
||||
self.widget_sagittal.hide()
|
||||
self.widget_transverse.hide()
|
||||
print(quadrant)
|
||||
if quadrant == 1:
|
||||
self.widget_coronal.show()
|
||||
elif quadrant == 2:
|
||||
self.widget_3d.show()
|
||||
elif quadrant == 3:
|
||||
self.widget_sagittal.show()
|
||||
elif quadrant == 4:
|
||||
self.widget_transverse.show()
|
||||
self.is_pane_maximum = True
|
||||
|
||||
@Slot()
|
||||
def cell_click(self):
|
||||
|
@ -40,11 +40,11 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QWidget" name="widget_2" native="true">
|
||||
<widget class="QWidget" name="widget_3d" native="true">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">border: 1px solid rgb(195, 195, 195);</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<layout class="QGridLayout" name="volume_grid_layout">
|
||||
<property name="leftMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
@ -61,12 +61,12 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QWidget" name="widget_3" native="true">
|
||||
<widget class="QWidget" name="widget_coronal" native="true">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">border: 1px solid rgb(195, 195, 195);
|
||||
margin-left: -1px;</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<layout class="QGridLayout" name="coronal_grid_layout">
|
||||
<property name="leftMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
@ -83,7 +83,7 @@ margin-left: -1px;</string>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QWidget" name="widget_5" native="true">
|
||||
<widget class="QWidget" name="widget_transverse" native="true">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">border: 1px solid rgb(195, 195, 195);
|
||||
margin-left: -1px;
|
||||
@ -106,7 +106,7 @@ margin-top: -1px;</string>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QWidget" name="widget_4" native="true">
|
||||
<widget class="QWidget" name="widget_sagittal" native="true">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">border: 1px solid rgb(195, 195, 195);
|
||||
margin-top: -1px;</string>
|
||||
|
77
crystalfly/ui/volume_viewer.py
Normal file
77
crystalfly/ui/volume_viewer.py
Normal file
@ -0,0 +1,77 @@
|
||||
# noinspection PyUnresolvedReferences
|
||||
import vtkmodules.vtkRenderingContextOpenGL2
|
||||
from PySide6.QtCore import Signal
|
||||
from PySide6.QtGui import QMouseEvent
|
||||
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
|
||||
from vtkmodules.vtkCommonDataModel import vtkPiecewiseFunction
|
||||
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballCamera
|
||||
from vtkmodules.vtkRenderingCore import vtkVolume, vtkVolumeProperty, vtkRenderer, vtkColorTransferFunction
|
||||
from vtkmodules.vtkRenderingVolume import vtkGPUVolumeRayCastMapper
|
||||
# noinspection PyUnresolvedReferences
|
||||
from vtkmodules.vtkRenderingVolumeOpenGL2 import vtkSmartVolumeMapper
|
||||
|
||||
from crystalfly.model.image_reader import image_reader_model
|
||||
|
||||
|
||||
class VolumeViewer(QVTKRenderWindowInteractor):
|
||||
doubleClicked = Signal(int)
|
||||
|
||||
def __init__(self, quadrant: int):
|
||||
super().__init__()
|
||||
self.quadrant = quadrant
|
||||
self.render_window = self.GetRenderWindow()
|
||||
self.render_window.SetMultiSamples(0)
|
||||
|
||||
self.render_window.AddRenderer(vtkRenderer())
|
||||
self.renderer = self.render_window.GetRenderers().GetFirstRenderer()
|
||||
# self.iren = vtkRenderWindowInteractor()
|
||||
# self.iren.SetRenderWindow(self.render_window)
|
||||
|
||||
self.volume_mapper = vtkGPUVolumeRayCastMapper()
|
||||
self.volume_mapper.SetBlendModeToComposite()
|
||||
self.volume_mapper.SetUseJittering(1)
|
||||
|
||||
self.colorTF = vtkColorTransferFunction()
|
||||
self.colorTF.AddRGBPoint(-200, 0.0, 0.0, 0.0)
|
||||
self.colorTF.AddRGBPoint(110, 0.4, 0.4, 1.0)
|
||||
self.colorTF.AddRGBPoint(512, 1.0, 1.0, 1.0)
|
||||
|
||||
self.scalarTF = vtkPiecewiseFunction()
|
||||
self.scalarTF.AddPoint(-255, 0.00)
|
||||
self.scalarTF.AddPoint(110, 0.00)
|
||||
self.scalarTF.AddPoint(512, 0.5)
|
||||
|
||||
self.volume_property = vtkVolumeProperty()
|
||||
self.volume_property.ShadeOn()
|
||||
self.volume_property.SetInterpolationTypeToLinear()
|
||||
self.volume_property.SetColor(self.colorTF)
|
||||
self.volume_property.SetScalarOpacity(self.scalarTF)
|
||||
|
||||
self.volume = vtkVolume()
|
||||
self.volume.SetMapper(self.volume_mapper)
|
||||
self.volume.SetProperty(self.volume_property)
|
||||
|
||||
self.renderer.ResetCamera()
|
||||
self.renderer.GetActiveCamera().Zoom(1.3)
|
||||
|
||||
self.interactor_style = vtkInteractorStyleTrackballCamera()
|
||||
self.iren = self.render_window.GetInteractor()
|
||||
# self.iren.SetRenderWindow(self.render_window)
|
||||
self.iren.SetInteractorStyle(self.interactor_style)
|
||||
self.iren.Initialize()
|
||||
|
||||
self.renderer.AddVolume(self.volume)
|
||||
image_reader_model.loadedSignal.connect(self.set_image)
|
||||
self.renderer.SetBackground(0.3, 0.3, 0.3)
|
||||
self.renderer.SetBackgroundAlpha(0.4)
|
||||
|
||||
def set_image(self, image):
|
||||
self.volume_mapper.SetInputData(image)
|
||||
self.iren.SetRenderWindow(self.render_window)
|
||||
self.render_window.Render()
|
||||
self.volume.Update()
|
||||
# self.volume_mapper.Render()
|
||||
|
||||
def mouseDoubleClickEvent(self, evt: QMouseEvent):
|
||||
print(evt, self.quadrant)
|
||||
self.doubleClicked.emit(self.quadrant)
|
@ -1,21 +1,27 @@
|
||||
# noinspection PyUnresolvedReferences
|
||||
# import vtkmodules.vtkRenderingContextOpenGL2
|
||||
from PySide6.QtGui import QCloseEvent
|
||||
import vtkmodules.vtkRenderingContextOpenGL2
|
||||
from PySide6.QtCore import Signal
|
||||
from PySide6.QtGui import QCloseEvent, QMouseEvent
|
||||
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
|
||||
from vtkmodules.vtkInteractionImage import vtkImageViewer2
|
||||
from vtkmodules.vtkInteractionImage import vtkResliceImageViewer, vtkImageViewer2
|
||||
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleImage
|
||||
from vtkmodules.vtkInteractionWidgets import vtkResliceCursorLineRepresentation, vtkImagePlaneWidget
|
||||
|
||||
from crystalfly.model.image_reader import image_reader_model
|
||||
|
||||
|
||||
class VTKImageViewer(QVTKRenderWindowInteractor):
|
||||
def __init__(self):
|
||||
doubleClicked = Signal(int)
|
||||
|
||||
def __init__(self, quadrant: int):
|
||||
super().__init__()
|
||||
self.quadrant = quadrant
|
||||
# self.render_window = render_window
|
||||
self.render_window = self.GetRenderWindow()
|
||||
|
||||
# image viewer
|
||||
# self.image_viewer = vtkResliceImageViewer()
|
||||
self.image_viewer = vtkImageViewer2()
|
||||
# self.image_viewer.SetRenderWindow(self.render_window)
|
||||
|
||||
# 交互
|
||||
self.interactor_style = vtkInteractorStyleImage()
|
||||
@ -25,6 +31,14 @@ class VTKImageViewer(QVTKRenderWindowInteractor):
|
||||
self.interactor_style.AddObserver("MouseWheelBackwardEvent", self.mouse_wheel_backward_event)
|
||||
self.iren.Initialize()
|
||||
|
||||
# rep = vtkResliceCursorLineRepresentation.SafeDownCast(
|
||||
# self.image_viewer.GetResliceCursorWidget().GetRepresentation())
|
||||
# # self.image_viewer.
|
||||
# rep.GetResliceCursorActor().GetCursorAlgorithm().SetReslicePlaneNormal(0)
|
||||
#
|
||||
# plane_widget = vtkImagePlaneWidget()
|
||||
# plane_widget.SetInteractor(self.iren)
|
||||
# plane_widget.On()
|
||||
image_reader_model.loadedSignal.connect(self.set_image)
|
||||
|
||||
def set_image(self, image):
|
||||
@ -49,3 +63,6 @@ class VTKImageViewer(QVTKRenderWindowInteractor):
|
||||
def closeEvent(self, evt: QCloseEvent):
|
||||
super().closeEvent(evt)
|
||||
self.Finalize()
|
||||
|
||||
def mouseDoubleClickEvent(self, evt: QMouseEvent):
|
||||
self.doubleClicked.emit(self.quadrant)
|
||||
|
Reference in New Issue
Block a user