I am generating and saving Saggital Reconstuction of the Dicom CBCT images by using Vtk and PyQt application. During the Flow of Program it open the Vtk PyQT win32 Open GL window for some seconds and then save the file in png. But I donot want to open that window during the flow of program. I just want to save the image, donot want to render it.
import sys
import vtkmodules.all as vtk
from PyQt5 import QtCore, QtWidgets
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
import pydicom
import os
from vtkmodules.vtkIOImage import (
vtkBMPWriter,
vtkJPEGWriter,
vtkPNGWriter,
vtkPNMWriter,
vtkPostScriptWriter,
vtkTIFFWriter
)
from datetime import datetime
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer,
vtkWindowToImageFilter
)
from flask import Flask, request, send_file
import base64
app = Flask(__name__)
savePath = r'F:\Nixaam\Jan 2023\Saggital View'
savePath = savePath + '\\'
def create_reader(dir):
reader = vtk.vtkDICOMImageReader()
reader.SetDirectoryName(dir)
reader.Update()
return reader
def create_center(reader):
(xMin, xMax, yMin, yMax, zMin, zMax) = reader.GetExecutive().GetWholeExtent(reader.GetOutputInformation(0))
(xSpacing, ySpacing, zSpacing) = reader.GetOutput().GetSpacing()
(x0, y0, z0) = reader.GetOutput().GetOrigin()
center = [x0 + xSpacing * 0.5 * (xMin + xMax),
y0 + ySpacing * 0.5 * (yMin + yMax),
z0 + zSpacing * 0.5 * (zMin + zMax)]
return center
def create_sagittal_slice_matrix(center):
#Create Sagittal Slice Matrix
sagittal = vtk.vtkMatrix4x4()
sagittal.DeepCopy((0, 0, -1, center[0],
1, 0, 0, center[1],
0, -1, 0, center[2],
0, 0, 0, 1))
return sagittal
def create_resliced_image(reader, sagittal, frame):
# Reslice image
widget = QVTKRenderWindowInteractor(frame)
slice = vtk.vtkImageReslice()
slice.SetInputConnection(reader.GetOutputPort())
slice.SetOutputDimensionality(2)
slice.SetResliceAxes(sagittal)
slice.SetInterpolationModeToLinear()
return widget, slice
def create_display_image_actor(slice):
# Display the image
actor = vtk.vtkImageActor()
actor.GetMapper().SetInputConnection(slice.GetOutputPort())
# renderer = vtk.vtkRenderer()
return actor
def adjust_renderer_settings(renderer, widget, actor):
# Remove Renderer And Reset
renderer.RemoveAllViewProps()
renderer.ResetCamera()
widget.GetRenderWindow().Render()
renderer.AddActor(actor)
widget.GetRenderWindow().AddRenderer(renderer)
return widget
def setup_interaction(widget):
# Set up the interaction
slice_interactorStyle = vtk.vtkInteractorStyleImage()
slice_interactor = widget.GetRenderWindow().GetInteractor()
slice_interactor.SetInteractorStyle(slice_interactorStyle)
widget.GetRenderWindow().SetInteractor(slice_interactor)
widget.GetRenderWindow().Render()
return slice_interactor
# Start interaction
# slice_interactor.Start()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, path, parent = None):
QtWidgets.QMainWindow.__init__(self, parent)
self.frame = QtWidgets.QFrame()
self.vl = QtWidgets.QVBoxLayout()
reader = create_reader(path)
center = create_center(reader)
sagittal = create_sagittal_slice_matrix(center)
self.vtkWidget, slice = create_resliced_image(reader, sagittal, self.frame)
self.vl.addWidget(self.vtkWidget)
vtk_out = vtk.vtkOutputWindow()
vtk_out.SetInstance(vtk_out)
# create renderer
renderer = vtk.vtkRenderer()
# create actor
actor = create_display_image_actor(slice)
self.vtkWidget = adjust_renderer_settings(renderer, self.vtkWidget, actor)
slice_interactor = setup_interaction(self.vtkWidget)
renWin = vtkRenderWindow()
renWin.AddRenderer(renderer)
slice_interactor.SetRenderWindow(renWin)
renWin.SetSize(1366,768)
# renWin.SetWindowName('Window')
# renWin.Render()
windowto_image_filter = vtkWindowToImageFilter()
windowto_image_filter.SetInput(renWin)
windowto_image_filter.SetScale(1) # image quality
windowto_image_filter.SetInputBufferTypeToRGB()
# # Read from the front buffer.
# windowto_image_filter.ReadFrontBufferOff()
# windowto_image_filter.Update()
writer = vtkPNGWriter()
path = r'F:\Nixaam\Jan 2023\Saggital View'
path = path + '\\'
writer.SetFileName(f'{path}saggital_view.png')
writer.SetInputConnection(windowto_image_filter.GetOutputPort())
writer.Write()
seriesFilePath = r'F:\Nixaam\Jan 2023\Saggital View\Decompressed\2023-01-13 21-10-32'
app = QtWidgets.QApplication(sys.argv)
window = MainWindow(seriesFilePath)
I donot want this window during the program.
Related
I want to generate 2D panarmoic view from the series of 3D cbct dental images by python.
I am converting the series of 3D Cbct images to 2D panaromic view by python. The code is working fine but the output is not complete.
import sys
import vtkmodules.all as vtk
from PyQt5 import QtCore, QtWidgets
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
def create_reader(dir):
reader = vtk.vtkDICOMImageReader()
reader.SetDirectoryName(dir)
reader.Update()
return reader
def create_center(reader):
(xMin, xMax, yMin, yMax, zMin, zMax) = reader.GetExecutive().GetWholeExtent(reader.GetOutputInformation(0))
(xSpacing, ySpacing, zSpacing) = reader.GetOutput().GetSpacing()
(x0, y0, z0) = reader.GetOutput().GetOrigin()
center = [x0 + xSpacing * 0.5 * (xMin + xMax),
y0 + ySpacing * 0.5 * (yMin + yMax),
z0 + zSpacing * 0.5 * (zMin + zMax)]
return center
def create_sagittal_slice_matrix(center):
#Create Sagittal Slice Matrix
sagittal = vtk.vtkMatrix4x4()
sagittal.DeepCopy((0, 0, -1, center[0],
1, 0, 0, center[1],
0, -1, 0, center[2],
0, 0, 0, 1))
return sagittal
def create_resliced_image(reader, sagittal, frame):
# Reslice image
widget = QVTKRenderWindowInteractor(frame)
slice = vtk.vtkImageReslice()
slice.SetInputConnection(reader.GetOutputPort())
slice.SetOutputDimensionality(2)
slice.SetResliceAxes(sagittal)
slice.SetInterpolationModeToLinear()
return widget, slice
def create_display_image_actor(slice):
# Display the image
actor = vtk.vtkImageActor()
actor.GetMapper().SetInputConnection(slice.GetOutputPort())
# renderer = vtk.vtkRenderer()
return actor
def adjust_renderer_settings(renderer, widget, actor):
# Remove Renderer And Reset
renderer.RemoveAllViewProps()
renderer.ResetCamera()
widget.GetRenderWindow().Render()
renderer.AddActor(actor)
widget.GetRenderWindow().AddRenderer(renderer)
return widget
def setup_interaction(widget):
# Set up the interaction
slice_interactorStyle = vtk.vtkInteractorStyleImage()
slice_interactor = widget.GetRenderWindow().GetInteractor()
slice_interactor.SetInteractorStyle(slice_interactorStyle)
widget.GetRenderWindow().SetInteractor(slice_interactor)
widget.GetRenderWindow().Render()
return slice_interactor
# Start interaction
# slice_interactor.Start()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent = None):
QtWidgets.QMainWindow.__init__(self, parent)
self.frame = QtWidgets.QFrame()
self.vl = QtWidgets.QVBoxLayout()
reader = create_reader(r"C:\work.ateeb\2.16.840.114421.10241.9572018034.9603554034\DECOMPRESSED\One")
center = create_center(reader)
sagittal = create_sagittal_slice_matrix(center)
self.vtkWidget, slice = create_resliced_image(reader, sagittal, self.frame)
self.vl.addWidget(self.vtkWidget)
# create renderer
renderer = vtk.vtkRenderer()
# create actor
actor = create_display_image_actor(slice)
self.vtkWidget = adjust_renderer_settings(renderer, self.vtkWidget, actor)
slice_interactor = setup_interaction(self.vtkWidget)
self.show()
self.frame.setLayout(self.vl)
self.setCentralWidget(self.frame)
slice_interactor.Initialize()
slice_interactor.Start()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
I was expecting the output like this
enter image description here
But the output the code is generating is:
enter image description here
I'm learning PySide6, and my goal is to use Qt3DCore to draw some 3D wireframe entities (points, lines, bezier curves ..uso..), in a 3D viewer. I like that high level interface.
I used the nice basic torus sphere example (https://doc.qt.io/qtforpython/examples/example_3d__simple3d.html) of the documentation, and added some QLineF lines, but I don't see the lines in the viewer scene.
I think it's because I don't have the correct graphics setting for such wireframe entities... But I don't find any other example or documentation on the subject :-(.
As requested by musicamante, to be as simple as possible, I add the QT example with only 2 modifications : QLine import, and QLine statement in line 121 :
"""PySide6 port of the qt3d/simple-cpp example from Qt v5.x"""
import sys
from PySide6.QtCore import (Property, QObject, QPropertyAnimation, Signal,QPoint,QLine,QLineF)
from PySide6.QtGui import (QGuiApplication, QMatrix4x4, QQuaternion, QVector3D)
from PySide6.Qt3DCore import (Qt3DCore)
from PySide6.Qt3DExtras import (Qt3DExtras)
from PySide6.Qt3DRender import (Qt3DRender)
class OrbitTransformController(QObject):
def __init__(self, parent):
super().__init__(parent)
self._target = None
self._matrix = QMatrix4x4()
self._radius = 1
self._angle = 0
def setTarget(self, t):
self._target = t
def getTarget(self):
return self._target
def setRadius(self, radius):
if self._radius != radius:
self._radius = radius
self.updateMatrix()
self.radiusChanged.emit()
def getRadius(self):
return self._radius
def setAngle(self, angle):
if self._angle != angle:
self._angle = angle
self.updateMatrix()
self.angleChanged.emit()
def getAngle(self):
return self._angle
def updateMatrix(self):
self._matrix.setToIdentity()
self._matrix.rotate(self._angle, QVector3D(0, 1, 0))
self._matrix.translate(self._radius, 0, 0)
if self._target is not None:
self._target.setMatrix(self._matrix)
angleChanged = Signal()
radiusChanged = Signal()
angle = Property(float, getAngle, setAngle, notify=angleChanged)
radius = Property(float, getRadius, setRadius, notify=radiusChanged)
class Window(Qt3DExtras.Qt3DWindow):
def __init__(self):
super().__init__()
# Camera
self.camera().lens().setPerspectiveProjection(45, 16 / 9, 0.1, 1000)
self.camera().setPosition(QVector3D(0, 0, 40))
self.camera().setViewCenter(QVector3D(0, 0, 0))
# For camera controls
self.createScene()
self.camController = Qt3DExtras.QOrbitCameraController(self.rootEntity)
self.camController.setLinearSpeed(50)
self.camController.setLookSpeed(180)
self.camController.setCamera(self.camera())
self.setRootEntity(self.rootEntity)
def createScene(self):
# Root entity
self.rootEntity = Qt3DCore.QEntity()
# Material
self.material = Qt3DExtras.QPhongMaterial(self.rootEntity)
# Torus
self.torusEntity = Qt3DCore.QEntity(self.rootEntity)
self.torusMesh = Qt3DExtras.QTorusMesh()
self.torusMesh.setRadius(5)
self.torusMesh.setMinorRadius(1)
self.torusMesh.setRings(100)
self.torusMesh.setSlices(20)
self.torusTransform = Qt3DCore.QTransform()
self.torusTransform.setScale3D(QVector3D(1.5, 1, 0.5))
self.torusTransform.setRotation(QQuaternion.fromAxisAndAngle(QVector3D(1, 0, 0), 45))
self.torusEntity.addComponent(self.torusMesh)
self.torusEntity.addComponent(self.torusTransform)
self.torusEntity.addComponent(self.material)
# Sphere
self.sphereEntity = Qt3DCore.QEntity(self.rootEntity)
self.sphereMesh = Qt3DExtras.QSphereMesh()
self.sphereMesh.setRadius(3)
self.sphereTransform = Qt3DCore.QTransform()
self.controller = OrbitTransformController(self.sphereTransform)
self.controller.setTarget(self.sphereTransform)
self.controller.setRadius(20)
self.sphereRotateTransformAnimation = QPropertyAnimation(self.sphereTransform)
self.sphereRotateTransformAnimation.setTargetObject(self.controller)
self.sphereRotateTransformAnimation.setPropertyName(b"angle")
self.sphereRotateTransformAnimation.setStartValue(0)
self.sphereRotateTransformAnimation.setEndValue(360)
self.sphereRotateTransformAnimation.setDuration(10000)
self.sphereRotateTransformAnimation.setLoopCount(-1)
self.sphereRotateTransformAnimation.start()
self.sphereEntity.addComponent(self.sphereMesh)
self.sphereEntity.addComponent(self.sphereTransform)
self.sphereEntity.addComponent(self.material)
self.lineEntity = QLineF(-2000.0, -2000.0, 2000.0, 2000.0)
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
view = Window()
view.show()
sys.exit(app.exec
())
I'd like to plot the horizontal distance between 2 points on an image with PyQtGraph, but I can't draw it.
I think it a way of doing this would be to use 3 instances of LineSegmentROI and make them look connected as one right arc, because they already have many features that would be great for this idea.
Like being draggable, which could be very useful to measure a different distance by simply dragging a side.
The problem are the handles, that can't be removed, or even hidden.
Has anyone done something like this?
# import the necessary packages
from pyqtgraph.graphicsItems.ImageItem import ImageItem
from pyqtgraph.graphicsItems.LinearRegionItem import LinearRegionItem
import requests
import numpy as np
import cv2
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
image = cv2.imread('example.png') # Change if you save the image with a different name
image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)
app = QtGui.QApplication([])
## Create window with GraphicsView widget
w = pg.GraphicsView()
w.show()
w.resize(image.shape[0], image.shape[1]) # Depending on the picture you may need to resize
w.setWindowTitle('Test')
view = pg.ViewBox()
view.setLimits(xMin=-image.shape[0]*0.05, xMax=image.shape[0]*1.05,
minXRange=100, maxXRange=2000,
yMin=-image.shape[1]*0.05, yMax=image.shape[1]*1.05,
minYRange=100, maxYRange=2000)
w.setCentralItem(view)
## lock the aspect ratio
view.setAspectLocked(True)
## Add image item
item = ImageItem(image)
view.addItem(item)
# Add Line
line = pg.QtGui.QGraphicsLineItem(200, -100, 400, -100, view)
line.setPen(pg.mkPen(color=(255, 0, 0), width=10))
view.addItem(line)
def mouseClicked(evt):
pos = evt[0]
print(pos)
proxyClicked = pg.SignalProxy(w.scene().sigMouseClicked, rateLimit=60, slot=mouseClicked)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
I ended up borrowing RectItem from drawing a rectangle in pyqtgraph and using its code for LineItem.
With three LineItems I draw the distance meter between the 2 points.
I still have to add some signals and slots to handle resizing, but I'm working on it.
However the core of the solution is here and I'll follow up with my improvements
# import the necessary packages
from PySide2.QtCore import QLineF, Qt, Signal, Slot, QObject, QPointF, QRectF, QSizeF
from PySide2.QtGui import QRegion
from PySide2.QtWidgets import QGraphicsItem, QLabel, QWidget
from pyqtgraph.graphicsItems.ImageItem import ImageItem
from pyqtgraph.graphicsItems.LinearRegionItem import LinearRegionItem
import numpy as np
import cv2
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
from pyqtgraph.graphicsItems.ViewBox.ViewBox import ViewBox
image = cv2.imread('image.jpg')
image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)
app = QtGui.QApplication([])
## Create window with GraphicsView widget
w = pg.GraphicsView()
w.show()
w.setWindowTitle('Test')
view = pg.ViewBox()
view.setLimits(xMin=0, xMax=image.shape[0],
minXRange=100, maxXRange=2000,
yMin=0, yMax=image.shape[1],
minYRange=100, maxYRange=2000)
w.setCentralItem(view)
## lock the aspect ratio
view.setAspectLocked(True)
## Add image item
item = ImageItem(image)
view.addItem(item)
class LineItem(pg.UIGraphicsItem):
moved = Signal(QPointF)
def __init__(self, line, extend=0, horizontal=False, parent=None):
super().__init__(parent)
self.initialPos = QLineF(line)
self._line = line
self.extend = extend
self.horizontal = horizontal
self._extendLine()
self.picture = QtGui.QPicture()
self._generate_picture()
self.setFlag(QGraphicsItem.ItemIsSelectable)
self.setFlag(QGraphicsItem.ItemIsMovable)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
#property
def line(self):
return self._line
def _extendLine(self):
if (self.extend != 0 and not self.horizontal):
self._line.setP1( QPointF(self._line.x1(), self._line.y1() - abs(self.extend)) )
# if (self.horizontal):
# self.extend = 0
# self._line.setP1( QPointF(self._line.x1(), self._line.y1() - abs(self.extend)) )
def _generate_picture(self):
painter = QtGui.QPainter(self.picture)
painter.setPen(pg.mkPen(color="y", width=2))
painter.drawLine(self.line)
painter.end()
def paint(self, painter, option, widget=None):
painter.drawPicture(0, 0, self.picture)
def boundingRect(self):
lineShape = self.picture.boundingRect()
lineShape.adjust(-10, -10, 10, 10)
return QtCore.QRectF(lineShape)
def itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemPositionChange:
# value is the new position.
if self.horizontal:
if value.x() != 0:
value = QPointF(0, value.y())
else:
if value.y() != 0:
value = QPointF(value.x(), 0)
self.moved.emit(value)
return pg.UIGraphicsItem.itemChange(self, change, value)
class Distance(QObject):
def __init__(self, A: QPointF, B: QPointF, view: ViewBox, parent: QWidget=None):
super().__init__(parent)
self.A = A
self.B = B
if A.x() > B.x():
self.A, self.B = B, A
self.distance = abs(B.x() - A.x())
print(self.A)
print(self.B)
extend = 50
top = max(self.A.y(), self.B.y()) + 200
self.left = LineItem(QtCore.QLineF(self.A.x(), self.A.y(), self.A.x(), top), extend)
self.right = LineItem(QtCore.QLineF(self.B.x(), self.B.y(), self.B.x(), top), extend)
self.top = LineItem(QtCore.QLineF(self.A.x(), top, self.B.x(), top), horizontal=True)
self.top.setPos(0, 0)
self.left.moved.connect(self.onLeftSegmentMoved)
self.right.moved.connect(self.onRightSegmentMoved)
self.top.moved.connect(self.onTopSegmentMoved)
self.label = pg.TextItem(str(round(self.distance, 2)), color=(0xFF, 0xFF, 0x00), anchor=(1, 1))
# self.label.setParentItem(self.top)
self.label.setPos(self.A.x()+self.distance/2, top + 5)
view.addItem(self.label)
view.addItem(self.left)
view.addItem(self.top)
view.addItem(self.right)
#Slot(QPointF)
def onLeftSegmentMoved(self, delta: QPointF):
topLeft = self.top.initialPos.p1()
newX = topLeft.x() + delta.x()
newTopLeft = QPointF(newX, topLeft.y())
self.top.line.setP1(newTopLeft)
self.top._generate_picture()
pos = self.label.pos()
self.distance = abs(self.top.line.x2() - self.top.line.x1())
self.label.setPos(newX + (self.top.line.x2() - self.top.line.x1())/2, pos.y())
self.label.setText(str(round(self.distance, 2)))
#Slot(QPointF)
def onTopSegmentMoved(self, delta: QPointF):
leftTop = self.top.initialPos.p1()
newY = leftTop.y() + delta.y()
newLeftTop = QPointF(leftTop.x(), newY)
self.left.line.setP2(newLeftTop)
self.left._generate_picture()
rightTop = self.top.initialPos.p2()
newY = rightTop.y() + delta.y()
newRightTop = QPointF(rightTop.x(), newY)
self.right.line.setP2(newRightTop)
self.right._generate_picture()
pos = self.label.pos()
self.label.setPos(pos.x(), newY)
#Slot(QPointF)
def onRightSegmentMoved(self, delta: QPointF):
topRight = self.top.initialPos.p2()
newX = topRight.x() + delta.x()
newTopRight = QPointF(newX, topRight.y())
self.top.line.setP2(newTopRight)
self.top._generate_picture()
pos = self.label.pos()
self.distance = abs(self.top.line.x2() - self.top.line.x1())
self.label.setPos(newX - (self.top.line.x2() - self.top.line.x1())/2, pos.y())
self.label.setText(str(round(self.distance, 2)))
distance = Distance(QPointF(925, 425), QPointF(138, 500), view)
In VTK, I have a surface and a line, and the line is included by the surface. Then, I need to pick one point in the line. I implement myself interactor and get the world coordinate by a right button click. I hope the select point could be located in the line. I show the selected line in the renderer when right button release. However, I find I can not select a point in the line. My code is:
import vtk, os, sys
import numpy as np
from PyQt5.QtWidgets import *
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from vtk.util.numpy_support import vtk_to_numpy, numpy_to_vtk
def numpyToVtk(data, type=vtk.VTK_FLOAT):
flat_data_array = data.transpose(2,1,0).flatten()
vtk_data_array = numpy_to_vtk(flat_data_array)
vtk_data = numpy_to_vtk(num_array=vtk_data_array, deep=True, array_type=type)
img = vtk.vtkImageData()
img.GetPointData().SetScalars(vtk_data)
img.SetDimensions(data.shape)
img.SetOrigin(0, 0, 0)
img.SetSpacing(1, 1, 1)
return img
class ourInteractor(vtk.vtkInteractorStyleTrackballCamera):
def __init__(self, renderer=None, renWindow=None):
super(ourInteractor, self).__init__()
self.AddObserver("RightButtonReleaseEvent", self.OnRightButtonUp)
self.ren = renderer
self.renWin = renWindow
def OnRightButtonUp(self, obj, event):
super(ourInteractor, self).OnRightButtonUp()
pos = self.GetInteractor().GetEventPosition()
coordinate = vtk.vtkCoordinate()
coordinate.SetCoordinateSystemToDisplay()
coordinate.SetValue(pos[0], pos[1], 0)
worldCoor = coordinate.GetComputedWorldValue(
self.GetInteractor().GetRenderWindow().GetRenderers().GetFirstRenderer())
print('screen coor: ', pos, 'world coor: ', worldCoor)
points = vtk.vtkPoints()
vertices = vtk.vtkCellArray()
id = points.InsertNextPoint(worldCoor[0], worldCoor[1], worldCoor[2])
vertices.InsertNextCell(1)
vertices.InsertCellPoint(id)
point = vtk.vtkPolyData()
point.SetPoints(points)
point.SetVerts(vertices)
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(point)
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetPointSize(10)
actor.GetProperty().SetColor(0, 1, 0)
self.ren.AddActor(actor)
self.renWin.Render()
class AirwaySkeleton(QMainWindow):
def __init__(self, parent=None):
super(AirwaySkeleton, self).__init__(parent=parent)
self.setWindowTitle("Airway Skeleton")
widget = QWidget()
self.setCentralWidget(widget)
layout = QHBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
widget.setLayout(layout)
self.mainLayout = layout
frame = QFrame()
vtkWidget = QVTKRenderWindowInteractor(frame)
self.mainLayout.addWidget(vtkWidget)
ren = vtk.vtkRenderer()
vtkWidget.GetRenderWindow().AddRenderer(ren)
iren = vtkWidget.GetRenderWindow().GetInteractor()
style = ourInteractor(renderer=ren, renWindow=vtkWidget.GetRenderWindow())
iren.SetInteractorStyle(style)
ren.SetBackground(0, 0, 0)
self.ren = ren
mask = np.zeros(shape=[200, 200, 200], dtype=np.uint8)
mask[20:80, 50:150, 50:150] = 1
mask[80:150, 80:120, 80:120] = 1
mask[150:170, 50:150, 50:150] = 1
xs = np.arange(20, 170, 0.1)
line = []
for x in xs:
line.append([x, 100, 100])
actors = self.createActorsForLines([np.array(line)])
vtkMask = numpyToVtk(data=mask, type=vtk.VTK_CHAR)
mesh = self.maskToMesh(vtkMask)
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(mesh.GetOutputPort())
mapper.ScalarVisibilityOff()
actor = vtk.vtkLODActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(1, 1, 1)
actor.GetProperty().SetOpacity(0.4)
self.ren.AddActor(actor)
for lineActor in actors:
self.ren.AddActor(lineActor)
self.renWin = vtkWidget.GetRenderWindow()
iren.Initialize()
self.iren = iren
def maskToMesh(self, mask):
contour = vtk.vtkDiscreteMarchingCubes()
contour.SetInputData(mask)
contour.SetValue(0, 1)
contour.Update()
smoother = vtk.vtkWindowedSincPolyDataFilter()
smoother.SetInputConnection(contour.GetOutputPort())
smoother.SetNumberOfIterations(30)
smoother.BoundarySmoothingOff()
smoother.NonManifoldSmoothingOn()
smoother.NormalizeCoordinatesOn()
smoother.Update()
triangleCellNormals = vtk.vtkPolyDataNormals()
triangleCellNormals.SetInputConnection(smoother.GetOutputPort())
triangleCellNormals.ComputeCellNormalsOn()
triangleCellNormals.ComputePointNormalsOff()
triangleCellNormals.ConsistencyOn()
triangleCellNormals.AutoOrientNormalsOn()
triangleCellNormals.Update()
return triangleCellNormals
def createActorsForLines(self, lines):
actors = []
endPoints = vtk.vtkPoints()
for line in lines:
n = line.shape[0]
endPoints.InsertNextPoint(line[0, 0], line[0, 1], line[0, 2])
endPoints.InsertNextPoint(line[-1, 0], line[-1, 1], line[-1, 2])
points = vtk.vtkPoints()
vtkLines = vtk.vtkCellArray()
vtkLines.InsertNextCell(n)
for i in range(n):
points.InsertNextPoint(line[i, 0], line[i, 1], line[i, 2])
vtkLines.InsertCellPoint(i)
polygonPolyData = vtk.vtkPolyData()
polygonPolyData.SetPoints(points)
polygonPolyData.SetLines(vtkLines)
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(polygonPolyData)
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(1, 0, 0)
actors.append(actor)
polyData = vtk.vtkPolyData()
polyData.SetPoints(endPoints)
sphereSource = vtk.vtkSphereSource()
sphereSource.SetRadius(1)
glyph3D = vtk.vtkGlyph3D()
glyph3D.SetSourceConnection(sphereSource.GetOutputPort())
glyph3D.SetInputData(polyData)
glyph3D.Update()
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(glyph3D.GetOutputPort())
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(0, 0, 1)
actors.append(actor)
return actors
if __name__ == '__main__':
app = QApplication(sys.argv)
window = AirwaySkeleton()
window.show()
sys.exit(app.exec_())
What's wrong with my code? Any suggestion is appreciated!
In addition, how can I pick the point in the surface?
Solution with vtkplotter is simply:
from vtkplotter import *
import numpy as np
mask = np.zeros(shape=[200,200,200], dtype=np.uint8)
mask[ 20:80, 50:150, 50:150] = 1
mask[ 80:150, 80:120, 80:120] = 1
mask[150:170, 50:150, 50:150] = 1
vol = Volume(mask) # returns vtkVolume
iso = vol.isosurface(threshold=1).c('grey').alpha(0.3).pickable(0)
smoothed_iso = iso.smoothLaplacian(niter=30)
aline = Line((20,100,100), (170,100,100), lw=10) # vtkActor
def onLeftClick(mesh):
printc("clicked 3D point:", mesh.picked3d, c='red')
vp.add(Sphere(pos=mesh.picked3d, r=2, c="green"))
vp = Plotter(verbose=0, axes=8, bg='black')
vp.mouseLeftClickFunction = onLeftClick
vp.show(smoothed_iso, aline)
can be embedded in Qt following examples here.
I'm trying to load two exr files and load them into labels on the gui so I can view the two files side by side. I can get one to work but when I try to load both python crashes. Below is my code:
def exrToJpgGamma(exrfile):
file = OpenEXR.InputFile(exrfile)
pt = Imath.PixelType(Imath.PixelType.FLOAT)
dw = file.header()['dataWindow']
size = (dw.max.x - dw.min.x + 1, dw.max.y - dw.min.y + 1)
RedStr = file.channel('R', pt)
GreenStr = file.channel('G', pt)
BlueStr = file.channel('B', pt)
Red = array.array('f', RedStr)
Green = array.array('f', GreenStr)
Blue = array.array('f', BlueStr)
def EncodeToSRGB(v):
if (v <= 0.0031308):
return (v * 12.92) * 255.0
else:
return (1.055*(v**(1.0/2.2))-0.055) * 255.0
for I in range(len(Red)):
Red[I] = EncodeToSRGB(Red[I])
for I in range(len(Green)):
Green[I] = EncodeToSRGB(Green[I])
for I in range(len(Blue)):
Blue[I] = EncodeToSRGB(Blue[I])
rgbf = [Image.frombytes("F", size, Red.tobytes())]
rgbf.append(Image.frombytes("F", size, Green.tobytes()))
rgbf.append(Image.frombytes("F", size, Blue.tobytes()))
rgb8 = [im.convert("L") for im in rgbf]
myqimage = Image.merge("RGB", rgb8)
return myqimage
def showEXR(self):
width = 480
height = 360
imageq = PilImageQt(exrToJpgGamma(chip.exr))
qimage = QtGui.QImage(imageq)
pixmap = QtGui.QPixmap.fromImage(qimage)
ScaledPixmap = pixmap.scaled(width, height, QtCore.Qt.KeepAspectRatio, QtCore.Qt.FastTransformation)
self.chip_img.setPixmap(ScaledPixmap)
imageq = PilImageQt(exrToJpgGamma(panel.exr))
qimage = QtGui.QImage(imageq)
pixmap = QtGui.QPixmap.fromImage(qimage)
ScaledPixmap = pixmap.scaled(width, height, QtCore.Qt.KeepAspectRatio, QtCore.Qt.FastTransformation)
self.panel_img.setPixmap(ScaledPixmap)
return
showEXR(self)
Let me know if you need any additional details. Thanks in advance.
Here are the image files
http://www.mediafire.com/file/emm0vhhuwpwdx6v/exr_files.zip/file
Since I can not reproduce the problem since you do not provide an MCVE, then I will not be able to point out the error, so I will show you a working code, in this case I will assume that the images are on the side of the .py:
├── chip.exr
├── main.py
└── panel.exr
Code:
import OpenEXR, Imath
from PIL import Image
import array
from PIL.ImageQt import ImageQt as PilImageQt
from PyQt5 import QtCore, QtGui, QtWidgets
def exrToJpgGamma(exrfile):
file = OpenEXR.InputFile(exrfile)
pt = Imath.PixelType(Imath.PixelType.FLOAT)
dw = file.header()['dataWindow']
size = (dw.max.x - dw.min.x + 1, dw.max.y - dw.min.y + 1)
RedStr = file.channel('R', pt)
GreenStr = file.channel('G', pt)
BlueStr = file.channel('B', pt)
Red = array.array('f', RedStr)
Green = array.array('f', GreenStr)
Blue = array.array('f', BlueStr)
def EncodeToSRGB(v):
if v <= 0.0031308:
return (v * 12.92) * 255.0
else:
return (1.055*(v**(1.0/2.2))-0.055) * 255.0
for I, value in enumerate(Red):
Red[I] = EncodeToSRGB(value)
for I, value in enumerate(Green):
Green[I] = EncodeToSRGB(value)
for I, value in enumerate(Blue):
Blue[I] = EncodeToSRGB(value)
rgbf = [Image.frombytes("F", size, channel.tobytes()) for channel in (Red, Green, Blue)]
rgb8 = [im.convert("L") for im in rgbf]
myqimage = Image.merge("RGB", rgb8)
return myqimage
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.chip_img = QtWidgets.QLabel()
self.panel_img = QtWidgets.QLabel()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.chip_img)
lay.addWidget(self.panel_img)
self.load_exr('panel.exr', self.panel_img)
self.load_exr('chip.exr', self.chip_img)
def load_exr(self, filename, label):
width, height = 480, 360
imageq = PilImageQt(exrToJpgGamma(filename))
pixmap = QtGui.QPixmap.fromImage(imageq.copy())
scaled_pixmap = pixmap.scaled(width, height,
QtCore.Qt.KeepAspectRatio,
QtCore.Qt.FastTransformation)
label.setPixmap(scaled_pixmap)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())