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
Related
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.
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've a PyQt code which keeps on crashes giving me error message QPixmap::fromImage: QPixmap cannot be created without a QGuiApplication QPixmap: Must construct a QGuiApplication before a QPixmap
It's a fairly simple application in which I read frames from one class named CameraWidget and apply a function def transform_perspective(self, frame, points) to get the transform perspective of that frame. I've divided my screen in two equal haves, the right half section displays the frame as seen by the camera and the left half displays the perspective transformation of it (for which I get the coordinates from another class named Canvas).
There is one more issue: the left half doesn't occupy it's entire region. A lot of portion just appears black. Here is a pic for your reference
It's a fairly long program so below I'm including the class where I believe the problem lies.
class TopView(QtWidgets.QWidget):
def __init__(self, parent=None):
super(TopView, self).__init__(parent)
self.original_frame = CameraWidget('Abc.ts')
# Layouts and frames
self.frame = QtWidgets.QFrame()
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.frame)
layout.setContentsMargins(0,0,0,0)
layout.setSpacing(0)
self.setLayout(layout)
# frame left
self.frame_left = QtWidgets.QFrame()
self.get_frame_thread = Thread(target=self.transform_frame, args=())
self.get_frame_thread.daemon = True
self.get_frame_thread.start()
self.top_view_label = QtWidgets.QLabel()
self.top_view_label.setScaledContents(True)
self.layout_left = QtWidgets.QVBoxLayout()
self.layout_left.addWidget(self.top_view_label)
self.layout_left.setContentsMargins(0,0,0,0)
self.layout_left.setSpacing(0)
self.frame_left.setLayout(self.layout_left)
# frame right
self.frame_right = QtWidgets.QFrame()
self.frame_right.setStyleSheet("background-color: rgb(153, 187, 255)")
self.video_frame_1 = self.original_frame
# Create camera widgets
print('Creating Camera Widgets...')
self.layout_right = QtWidgets.QVBoxLayout()
self.layout_right.addWidget(self.video_frame_1)
self.layout_right.setContentsMargins(5,5,5,5)
self.frame_right.setLayout(self.layout_right)
self.layout_inner = QtWidgets.QHBoxLayout()
self.layout_inner.addWidget(self.frame_left, 50)
self.layout_inner.addWidget(self.frame_right, 50)
self.layout_inner.setContentsMargins(0,0,0,0)
self.layout_inner.setSpacing(0)
self.frame.setLayout(self.layout_inner)
self.setLayout(layout)
sizeObject = QtWidgets.QDesktopWidget().screenGeometry(0)
self.screen_width = int(0.7*sizeObject.width())
self.screen_height = int(0.7*sizeObject.height())
def event(self, e):
if e.type() in (QtCore.QEvent.Show, QtCore.QEvent.Resize):
print('')
return QtWidgets.QWidget.event(self, e)
def transform_frame(self):
while True:
try:
self.top_view_frame = self.transform_perspective(self.original_frame.get_video_frame(), self.original_frame.canvas.mapped_list)
h, w, ch = self.top_view_frame.shape
bytesPerLine = ch * w
self.img = QtGui.QImage(self.top_view_frame, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
self.pix = QtGui.QPixmap.fromImage(self.img)
if not sip.isdeleted(self.top_view_label):
self.top_view_label.setPixmap(self.pix)
except Exception as e:
print(e)
def transform_perspective(self, frame, points):
points = np.float32(points)
p2 = np.float32([[0, 0], [600, 0], [0, 600], [600, 600]])
self.matrix = cv2.getPerspectiveTransform(points, p2)
frame = cv2.warpPerspective(frame, self.matrix, (400, 600))
return frame
Although the OP does not provide an MRE it is easy to notice that the error is that it is creating a QPixmap in a secondary thread which is prohibited. Instead you should send the QImage to the GUI thread, and in the GUI thread just convert it to QPixmap:
class ImageProcessor(QtCore.QObject):
imageChanged = QtCore.pyqtSignal(QtGui.QImage)
def process(self, video_frame, mapped_list):
thread = Thread(
target=self._execute,
args=(
video_frame,
mapped_list,
),
)
thread.daemon = True
thread.start()
def _execute(self, video_frame, mapped_list):
top_view_frame = self.transform_perspective(video_frame, mapped_list)
qimage = self.convert_np_to_qimage(top_view_frame)
self.imageChanged.emit(qimage.copy())
def transform_perspective(self, frame, points):
points = np.float32(points)
p2 = np.float32([[0, 0], [600, 0], [0, 600], [600, 600]])
matrix = cv2.getPerspectiveTransform(points, p2)
frame = cv2.warpPerspective(frame, matrix, (400, 600))
return frame
def convert_np_to_qimage(self, array):
h, w, ch = array.shape
bytesPerLine = ch * w
img = QtGui.QImage(array.data, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
return img.copy()
class TopView(QtWidgets.QWidget):
def __init__(self, parent=None):
super(TopView, self).__init__(parent)
self.original_frame = CameraWidget("Abc.ts")
# Layouts and frames
self.frame = QtWidgets.QFrame()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.frame)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
# frame left
self.frame_left = QtWidgets.QFrame()
self.top_view_label = QtWidgets.QLabel()
self.top_view_label.setScaledContents(True)
self.layout_left = QtWidgets.QVBoxLayout(self.frame_left)
self.layout_left.addWidget(self.top_view_label)
self.layout_left.setContentsMargins(0, 0, 0, 0)
self.layout_left.setSpacing(0)
# frame right
self.frame_right = QtWidgets.QFrame()
self.frame_right.setStyleSheet("background-color: rgb(153, 187, 255)")
self.video_frame_1 = self.original_frame
# Create camera widgets
print("Creating Camera Widgets...")
self.layout_right = QtWidgets.QVBoxLayout(self.frame_right)
self.layout_right.addWidget(self.video_frame_1)
self.layout_right.setContentsMargins(5, 5, 5, 5)
self.layout_inner = QtWidgets.QHBoxLayout(self.frame)
self.layout_inner.addWidget(self.frame_left, 50)
self.layout_inner.addWidget(self.frame_right, 50)
self.layout_inner.setContentsMargins(0, 0, 0, 0)
self.layout_inner.setSpacing(0)
sizeObject = QtWidgets.QDesktopWidget().screenGeometry(0)
self.screen_width = int(0.7 * sizeObject.width())
self.screen_height = int(0.7 * sizeObject.height())
self.processor = ImageProcessor()
self.processor.imageChanged.connect(self.handle_imageChanged)
self.launch_processor()
def launch_processor(self):
self.processor.process(
self.original_frame.get_video_frame(),
self.original_frame.canvas.mapped_list,
)
#QtCore.pyqtSlot(QtGui.QImage)
def handle_imageChanged(self, qimage):
qpixmap = QtGui.QPixmap.fromImage(qimage)
self.top_view_label.setPixmap(qpixmap)
QtCore.QTimer.singleShot(0, self.launch_processor)
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 am making a custom QWidget in which I have a QGridLayout, and draw a rectangle on a particular element in the grid. I also manually draw lines to delineate the location of the grid elements (with QPainter.DrawLines).
After drawing the lines, I then paint the rectangle within one of the grid elements, with its location specified using the QGridLayout coordinate system .
The problem is, the rectangle does not stay confined to its grid element. For instance, in the example below, the blue rectangle and black grid lines get out of alignment, so I end up with a blue box floating around in space.
I have not found explicit discussion of this issue via Google or SO.
Edit:
Note as pointed out in the accepted answer, the mistake was using grid coordinates to draw on the grid, when I should have been using point coordinates (i.e., column, row). That is, the mistake in the code below is that the element in the grid has its x- and y- coordinates reversed.
from PySide import QtGui, QtCore
class HighlightSquare(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent=None)
self.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding))
self.setMinimumSize(self.minimumSizeHint())
layout = QtGui.QGridLayout()
layout.addItem(QtGui.QSpacerItem(10,10), 0, 0)
layout.addItem(QtGui.QSpacerItem(10,10), 0, 1)
layout.addItem(QtGui.QSpacerItem(10,10), 1, 0)
layout.addItem(QtGui.QSpacerItem(10,10), 1, 1)
self.setLayout(layout)
self.resize(150, 150)
self.update()
def paintEvent(self, event = None):
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
winHeight=self.size().height(); heightStep=winHeight/2
winWidth=self.size().width(); widthStep=winWidth/2
#Draw lines
painter.setPen(QtCore.Qt.black)
for i in range(4):
#vertical lines
painter.drawLine(QtCore.QPoint(i*widthStep,0), QtCore.QPoint(i*widthStep, winHeight))
#horizontal lines
painter.drawLine(QtCore.QPoint(0,heightStep*i), QtCore.QPoint(winWidth, heightStep*i))
#Draw blue outline around box 1,1
highlightCoordinate=(1,1)
pen=QtGui.QPen(QtCore.Qt.blue, 3)
painter.setPen(pen)
coordHighlight=[QtCore.QPoint(highlightCoordinate[1]*heightStep, highlightCoordinate[0]*widthStep),\
QtCore.QPoint(highlightCoordinate[1]*heightStep, (highlightCoordinate[0]+1)*widthStep),\
QtCore.QPoint((highlightCoordinate[1]+1)*heightStep, (highlightCoordinate[0]+1)*widthStep),\
QtCore.QPoint((highlightCoordinate[1]+1)*heightStep, highlightCoordinate[0]*widthStep),\
QtCore.QPoint(highlightCoordinate[1]*heightStep, highlightCoordinate[0]*widthStep)]
#print coordHighlight
painter.drawPolyline(coordHighlight)
def minimumSizeHint(self):
return QtCore.QSize(120,120)
if __name__=="__main__":
import sys
app=QtGui.QApplication(sys.argv)
myLight = HighlightSquare()
myLight.show()
sys.exit(app.exec_())
Have you read the definition of the constructor of class QtCore.QPoint? At method QPoint.__init__ (self, int xpos, int ypos) your code is reversed (ypos, xpos). I fixed it.
import sys
from PyQt4 import QtGui, QtCore
class QHighlightSquareWidget (QtGui.QWidget):
def __init__ (self, parent = None):
QtGui.QWidget.__init__(self, parent = None)
self.setSizePolicy (
QtGui.QSizePolicy (
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding))
self.setMinimumSize(self.minimumSizeHint())
allQGridLayout = QtGui.QGridLayout()
allQGridLayout.addItem(QtGui.QSpacerItem(10,10), 0, 0)
allQGridLayout.addItem(QtGui.QSpacerItem(10,10), 0, 1)
allQGridLayout.addItem(QtGui.QSpacerItem(10,10), 1, 0)
allQGridLayout.addItem(QtGui.QSpacerItem(10,10), 1, 1)
self.setLayout(allQGridLayout)
self.resize(150, 150)
self.update()
def paintEvent (self, eventQPaintEvent):
myQPainter = QtGui.QPainter(self)
myQPainter.setRenderHint(QtGui.QPainter.Antialiasing)
winHeight = self.size().height()
heightStep = winHeight / 2
winWidth = self.size().width()
widthStep = winWidth / 2
myQPainter.setPen(QtCore.Qt.black)
for i in range(4):
myQPainter.drawLine(QtCore.QPoint(i * widthStep, 0 ), QtCore.QPoint(i * widthStep, winHeight ))
myQPainter.drawLine(QtCore.QPoint(0, heightStep * i), QtCore.QPoint(winWidth, heightStep * i))
highlightCoordinate = (1, 1)
myQPen = QtGui.QPen(QtCore.Qt.blue, 3)
myQPainter.setPen(myQPen)
coordHighlight = [
QtCore.QPoint( highlightCoordinate[0] * widthStep, highlightCoordinate[1] * heightStep),
QtCore.QPoint((highlightCoordinate[0] + 1) * widthStep, highlightCoordinate[1] * heightStep),
QtCore.QPoint((highlightCoordinate[0] + 1) * widthStep, (highlightCoordinate[1] + 1) * heightStep),
QtCore.QPoint( highlightCoordinate[0] * widthStep, (highlightCoordinate[1] + 1) * heightStep),
QtCore.QPoint( highlightCoordinate[0] * widthStep, highlightCoordinate[1] * heightStep)]
myQPainter.drawPolyline(*coordHighlight)
def minimumSizeHint (self):
return QtCore.QSize(120, 120)
if __name__=="__main__":
myQApplication = QtGui.QApplication(sys.argv)
myQHighlightSquareWidget = QHighlightSquareWidget()
myQHighlightSquareWidget.show()
sys.exit(myQApplication.exec_())