**3DSlicer**之Python脚本储存库翻译_10.Markups(1)

发布于:2023-01-16 ⋅ 阅读:(500) ⋅ 点赞:(0)

Script repository 译:脚本存储库

Note 译:注意

Usage: Copy-paste the code lines displayed below or the linked .py file contents into Python console in Slicer. Or save them to a .py file and run them using execfile. 译:**用法::将下面显示的代码行或链接的.py文件内容复制粘贴到Slicer中的Python控制台中。或者将它们保存到.py文件中,然后使用execfile运行它们。

译者的几点说明:

  • 今天开始学习这一章,包括每段代码的结果,我是用JupyterLab操作的,相应的.ipynb我会放到下载区
  • 主要感兴趣的内容,重点分析
  • 最近在做一个有关医学影像3d重建经皮定位的项目,希望有大神合作.

Markups 译: 标记

Load markups fiducial list from file 译:从文件加载标记Fiducial列表

Markups fiducials can be loaded from file: 译:标记Fiducial可以从文件中加载:

slicer.util.loadMarkupsFiducialList("data/F.fcsv")
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)

In  [3]:
Line 1:     


File /Applications/Slicer.app/Contents/bin/Python/slicer/util.py, in loadMarkupsFiducialList:
Line 746:   node = loadMarkups(filename)


File /Applications/Slicer.app/Contents/bin/Python/slicer/util.py, in loadMarkups:
Line 771:   return loadNodeFromFile(filename, 'MarkupsFile')


File /Applications/Slicer.app/Contents/bin/Python/slicer/util.py, in loadNodeFromFile:
Line 650:   raise RuntimeError(errorMessage)


RuntimeError: Failed to load node from file: data/F.fcsv
---------------------------------------------------------------------------
  • ✎✎✎ :

新版的文件为下面这个格式mrk.json,用fcsv出错

slicer.util.loadMarkupsFiducialList("data/F.mrk.json")
[True, (vtkSlicerMarkupsModuleMRMLPython.vtkMRMLMarkupsFiducialNode)0x16f2ecd68]

Adding Fiducials Programatically 译: 以编程方式添加Fiducial

Markups fiducials can be added to the currently active list from the python console by using the following module logic command: 译:使用以下模块逻辑命令,可以从python控制台将标记Fiducial点添加到当前活动列表中:

slicer.modules.markups.logic().AddFiducial() # 默认原点加到F

The command with no arguments will place a new fiducial at the origin. You can also pass it an initial location: 译:不带参数的命令将在原点放置一个新Fiducial。您还可以将其传递给初始位置:

slicer.modules.markups.logic().AddFiducial(1.0, -2.0, 3.3)
0
  • ✎✎✎ :

如何加到指定的列表中

我们将上面那个点加到F-1列表中
加到F里了可是标签却:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qg4zHqMa-1620137485954)(attachment:4e9a83d1-f414-463a-8fcc-bc4aa9aa05a9.png)]

知道为啥吗?

[^1]Python vtkMRMLMarkupsFiducialNode Examples, __main__slicer.vtkMRMLMarkupsFiducialNode Python Examples - HotExamples

# 译者代码
fixedLRS = slicer.util.getNode('F')    
fixedLRS.AddFiducial(-100.0, 0.0, 0.0)
fixedLRS.SetNthFiducialLabel(0, 'FixedL')

How to draw a curve using control points stored in a numpy array 译: 如何使用存储在numpy数组中的控制点绘制曲线

# Create random numpy array to use as input 译:创建随机的numpy数组以用作输入 
import numpy as np
pointPositions = np.random.uniform(-50,50,size=[15,3])

# Create curve from numpy array 译:从numpy数组创建曲线 
curveNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsCurveNode")
slicer.util.updateMarkupsControlPointsFromArray(curveNode, pointPositions)
  • ✎✎✎ :
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tuJpmBS6-1620137485959)(attachment:824a1ef0-6e28-45de-b571-2d67f4edf1a2.png)]

Add a button to module GUI to activate fiducial placement 译:向模块GUI添加按钮以激活Fiducial放置

This code snippet creates a toggle button, which activates fiducial placement when pressed (and deactivates when released). 译:此代码段创建一个切换按钮,按下该按钮可激活Fiducial位置(释放时将其禁用)。

The qSlicerMarkupsPlaceWidget widget can automatically activate placement of multiple points and can show buttons for deleting points, changing colors, lock, and hide points. 译:[qSlicerMarkupsPlaceWidget小部件](http://apidocs.slicer.org/master/classqSlicerMarkupsPlaceWidget.html)可以自动激活多个点的放置,并可以显示用于删除点,更改颜色,锁定和隐藏点的按钮。

w=slicer.qSlicerMarkupsPlaceWidget()
w.setMRMLScene(slicer.mrmlScene)
markupsNodeID = slicer.modules.markups.logic().AddNewFiducialNode()
w.setCurrentNode(slicer.mrmlScene.GetNodeByID(markupsNodeID))
# Hide all buttons and only show place button 译:隐藏所有按钮,仅显示位置按钮 
w.buttonsVisible=False
w.placeButton().show()
w.show()
  • ✎✎✎ :

在这里插入图片描述

如图

Adding Fiducials via mouse clicks 译: 通过鼠标单击添加Fiducial

You can also set the mouse mode into Markups fiducial placement by calling: 译:您还可以通过调用以下命令将鼠标模式设置为标记Fiducial位置:

placeModePersistence = 1
slicer.modules.markups.logic().StartPlaceMode(placeModePersistence)

A lower level way to do this is via the selection and interaction nodes: 译:一种较低级别的方法是通过选择和交互节点:

selectionNode = slicer.mrmlScene.GetNodeByID("vtkMRMLSelectionNodeSingleton")
selectionNode.SetReferenceActivePlaceNodeClassName("vtkMRMLMarkupsFiducialNode")
interactionNode = slicer.mrmlScene.GetNodeByID("vtkMRMLInteractionNodeSingleton")
placeModePersistence = 1
interactionNode.SetPlaceModePersistence(placeModePersistence)
# mode 1 is Place, can also be accessed via slicer.vtkMRMLInteractionNode().Place 译:模式1为Place,也可以通过slicer.vtkMRMLInteractionNode()访问。 
interactionNode.SetCurrentInteractionMode(1)

To switch back to view transform once you’re done placing fiducials: 译:完成Fiducial放置后,要切换回视图转换:

interactionNode = slicer.mrmlScene.GetNodeByID("vtkMRMLInteractionNodeSingleton")
interactionNode.SwitchToViewTransformMode()
# also turn off place mode persistence if required 译:如果需要,还可以关闭场所模式持久性 
interactionNode.SetPlaceModePersistence(0)

Access to Fiducial Properties 译:访问Fiducial属性

Each vtkMRMLMarkupsFiducialNode has a vector of points in it which can be accessed from python: 译:每个vtkMRMLMarkupsFiducialNode中都有一个点矢量,可以从python访问这些点:

fidNode = getNode("vtkMRMLMarkupsFiducialNode1")
n = fidNode.AddFiducial(4.0, 5.5, -6.0)
fidNode.SetNthFiducialLabel(n, "new label")
# each markup is given a unique id which can be accessed from the superclass level 译:每个标记都有一个唯一的ID,可以从超类级别访问 
id1 = fidNode.GetNthMarkupID(n)
# manually set the position 译:手动设置位置 
fidNode.SetNthFiducialPosition(n, 6.0, 7.0, 8.0)
# set the label 译:设置标签 
fidNode.SetNthFiducialLabel(n, "New label")
# set the selected flag, only selected = 1 fiducials will be passed to CLIs 译:设置选定的标志,只有选定的= 1个Fiducial将被传递到CLI 
fidNode.SetNthFiducialSelected(n, 1)
# set the visibility flag 译:设置可见性标志 
fidNode.SetNthFiducialVisibility(n, 0)
  • ✎✎✎ :

这组代码比我前面的👍太多…

You can loop over the fiducials in a list and get the coordinates: 译:您可以遍历列表中的Fiducial并获取坐标:

fidList = slicer.util.getNode("F")
numFids = fidList.GetNumberOfFiducials()
for i in range(numFids):
  ras = [0,0,0]
  fidList.GetNthFiducialPosition(i,ras)
  # the world position is the RAS position with any transform matrices applied
  world = [0,0,0,0]
  fidList.GetNthFiducialWorldCoordinates(0,world)
  print(i,": RAS =",ras,", world =",world)
0 : RAS = [-75.94607190857545, -37.56009742901087, -2.1938657726656743] , world = [-75.94607190857545, -37.56009742901087, -2.1938657726656743, 0.0]
1 : RAS = [14.155074704408154, -75.46471772966771, -2.194670949981628] , world = [-75.94607190857545, -37.56009742901087, -2.1938657726656743, 0.0]
2 : RAS = [-95.76510768417384, 85.85852034072332, -2.19344815423176] , world = [-75.94607190857545, -37.56009742901087, -2.1938657726656743, 0.0]
3 : RAS = [-81.11312212763353, -96.62529981603525, -2.1939475814752427] , world = [-75.94607190857545, -37.56009742901087, -2.1938657726656743, 0.0]
4 : RAS = [124.01467571038447, 129.81447702084876, -2.195127093658982] , world = [-75.94607190857545, -37.56009742901087, -2.1938657726656743, 0.0]
  • ✎✎✎ :
0 : RAS = [-75.94607190857545, -37.56009742901087, -2.1938657726656743] , world = [-75.94607190857545, -37.56009742901087, -2.1938657726656743, 0.0]
1 : RAS = [14.155074704408154, -75.46471772966771, -2.194670949981628] , world = [-75.94607190857545, -37.56009742901087, -2.1938657726656743, 0.0]
2 : RAS = [-95.76510768417384, 85.85852034072332, -2.19344815423176] , world = [-75.94607190857545, -37.56009742901087, -2.1938657726656743, 0.0]
3 : RAS = [-81.11312212763353, -96.62529981603525, -2.1939475814752427] , world = [-75.94607190857545, -37.56009742901087, -2.1938657726656743, 0.0]
4 : RAS = [124.01467571038447, 129.81447702084876, -2.195127093658982] , world = [-75.94607190857545, -37.56009742901087, -2.1938657726656743, 0.0]

比较一下坐标系的差别…

You can also look at the sample code in the Endoscopy module to see how python is used to access fiducials from a scripted module. 译:您还可以查看[内窥镜模块]中的示例代码(https://github.com/Slicer/Slicer/blob/master/Modules/Scripted/Endoscopy/Endoscopy.pyL287)以查看python的运行方式用于从脚本模块访问Fiducial。

Define/edit a circular region of interest in a slice viewer 译:在Slicer查看器中定义/编辑感兴趣的圆形区域

Drop two markup points on a slice view and copy-paste the code below into the Python console. After this, as you move the markups you’ll see a circle following the markups. 译:在Slicer视图上放置两个标记点,然后将以下代码复制粘贴到Python控制台中。此后,当您移动标记时,标记后面会出现一个圆圈。

# Update the sphere from the fiducial points 译:从Fiducial点更新球体 
def UpdateSphere(param1, param2):
  """Update the sphere from the fiducial points
  """
  import math
  centerPointCoord = [0.0, 0.0, 0.0]
  markups.GetNthFiducialPosition(0,centerPointCoord)
  circumferencePointCoord = [0.0, 0.0, 0.0]
  markups.GetNthFiducialPosition(1,circumferencePointCoord)
  sphere.SetCenter(centerPointCoord)
  radius=math.sqrt((centerPointCoord[0]-circumferencePointCoord[0])**2+(centerPointCoord[1]-circumferencePointCoord[1])**2+(centerPointCoord[2]-circumferencePointCoord[2])**2)
  sphere.SetRadius(radius)
  sphere.SetPhiResolution(30)
  sphere.SetThetaResolution(30)
  sphere.Update()

# Get markup node from scene 译:从场景中获取标记节点 
markups=slicer.util.getNode("F")
sphere = vtk.vtkSphereSource()
UpdateSphere(0,0)

# Create model node and add to scene 译:创建模型节点并添加到场景 
modelsLogic = slicer.modules.models.logic()
model = modelsLogic.AddModel(sphere.GetOutput())
model.GetDisplayNode().SetSliceIntersectionVisibility(True)
model.GetDisplayNode().SetSliceIntersectionThickness(3)
model.GetDisplayNode().SetColor(1,1,0)

# Call UpdateSphere whenever the fiducials are changed 译:每当Fiducial点更改时调用UpdateSphere 
markups.AddObserver(slicer.vtkMRMLMarkupsNode.PointModifiedEvent, UpdateSphere, 2)
178
  • ✎✎✎ :

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LBDJv4ym-1620137485965)(attachment:fb6c119a-3219-4102-b716-d08e002f4b84.png)]

Specify a sphere by multiple of markups points 译:通过多个标记点指定一个球体

Drop multiple markup points at the boundary of the spherical object and and copy-paste the code below into the Python console to get best-fit sphere. Minimum 4 points are required, it is recommended to place the points far from each other for most accurate fit. 译:在球形对象的边界处放置多个标记点,然后将以下代码复制粘贴到Python控制台中,以获得最合适的球形。至少需要4个点,建议将这些点彼此分开以实现最精确的拟合。

# Get markup node from scene 译:从场景中获取标记节点 
markups = slicer.util.getNode("F")

from scipy.optimize import least_squares
import numpy

def fit_sphere_least_squares(x_values, y_values, z_values, initial_parameters, bounds=((-numpy.inf, -numpy.inf, -numpy.inf, -numpy.inf),(numpy.inf, numpy.inf, numpy.inf, numpy.inf))):
    """
    Source: https://github.com/thompson318/scikit-surgery-sphere-fitting/blob/master/sksurgeryspherefitting/algorithms/sphere_fitting.py
    Uses scipy's least squares optimisor to fit a sphere to a set
    of 3D Points
    :return: x: an array containing the four fitted parameters
    :return: ier: int An integer flag. If it is equal to 1, 2, 3 or 4, the
             solution was found.
    :param: (x,y,z) three arrays of equal length containing the x, y, and z
            coordinates.
    :param: an array containing four initial values (centre, and radius)
    """
    return least_squares(_calculate_residual_sphere, initial_parameters, bounds=bounds, method="trf", jac="3-point", args=(x_values, y_values, z_values))

def _calculate_residual_sphere(parameters, x_values, y_values, z_values):
    """
    Source: https://github.com/thompson318/scikit-surgery-sphere-fitting/blob/master/sksurgeryspherefitting/algorithms/sphere_fitting.py
    Calculates the residual error for an x,y,z coordinates, fitted
    to a sphere with centre and radius defined by the parameters tuple
    :return: The residual error
    :param: A tuple of the parameters to be optimised, should contain [x_centre, y_centre, z_centre, radius]
    :param: arrays containing the x,y, and z coordinates.
    """
    #extract the parameters
    x_centre, y_centre, z_centre, radius = parameters
    #use numpy's sqrt function here, which works by element on arrays
    distance_from_centre = numpy.sqrt((x_values - x_centre)**2 + (y_values - y_centre)**2 + (z_values - z_centre)**2)
    return distance_from_centre - radius

# Fit a sphere to the markups fidicual points 译:使球形适应标记的折点 
markupsPositions = slicer.util.arrayFromMarkupsControlPoints(markups)
import numpy as np
# initial guess 译:初步猜测 
center0 = np.mean(markupsPositions, 0)
radius0 = np.linalg.norm(np.amin(markupsPositions,0)-np.amax(markupsPositions,0))/2.0
fittingResult = fit_sphere_least_squares(markupsPositions[:,0], markupsPositions[:,1], markupsPositions[:,2], [center0[0], center0[1], center0[2], radius0])
[centerX, centerY, centerZ, radius] = fittingResult["x"]

# Create a sphere using the fitted parameters 译:使用拟合的参数创建一个球体 
sphere = vtk.vtkSphereSource()
sphere.SetPhiResolution(30)
sphere.SetThetaResolution(30)
sphere.SetCenter(centerX, centerY, centerZ)
sphere.SetRadius(radius)
sphere.Update()

# Add the sphere to the scene 译:将球体添加到场景 
modelsLogic = slicer.modules.models.logic()
model = modelsLogic.AddModel(sphere.GetOutput())
model.GetDisplayNode().SetSliceIntersectionVisibility(True)
model.GetDisplayNode().SetSliceIntersectionThickness(3)
model.GetDisplayNode().SetColor(1,1,0)
  • ✎✎✎ :

在这里插入图片描述
参阅: 翻译:Slicer脚本存储库
<未完待续>