python - 如何使用vtk选择直线上的点?

标签 python pyqt vtk

在VTK中,我有一个面和一条线,线包含在面内。然后,我需要在直线上选取一个点。我实现了自己的交互器,并通过单击右键获取世界坐标。我希望选择点可以位于该线上。当右键释放时,我在渲染器中显示选定的行。但是,我发现我无法选择线上的点。我的代码是:

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

我的代码有什么问题吗?任何建议表示赞赏! 另外,如何在曲面上选取点?

最佳答案

解决方案 vtkplotter很简单:

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)

可以嵌入到 Qt 中,如下示例 here .

enter image description here

关于python - 如何使用vtk选择直线上的点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59469468/

相关文章:

python - 在 Python 3(也许是 matplotlib)中绘制革命实体

python - 从 PIL 中绘制点线或虚线矩形

python - 将参数传递给结构任务

python - 如何跨线程发送无信号?

c++ - 如何自动关闭打开的 vtk 窗口?

linux - 在 Linux 上构建 VTK 时出现错误 "GLintptr has not been declared"

python - 如何改进python后缀数组中的lambda表达式

python - 无法发送另一个线程中对象的已发布事件

c++ - 如何使用QVTKWidget

python - Pdfjs 打印按钮不适用于 PyQt5