Difference between revisions of "Documentation/Nightly/ScriptRepository"

From Slicer Wiki
Jump to: navigation, search
(Moved page to readthedocs)
Tags: 2017 source edit, Replaced
 
(173 intermediate revisions by 6 users not shown)
Line 1: Line 1:
 
<noinclude>{{documentation/versioncheck}}</noinclude>
 
<noinclude>{{documentation/versioncheck}}</noinclude>
__TOC__
 
  
 
+
{{documentation/banner
=Community-contributed modules=
+
| text  = [https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html This page has been moved to read-the-docs.]
 
+
| background-color = 8FBC8F }}
The examples in this section are [[Documentation/{{documentation/version}}/Developers/Modules#Scripted_Modules| Scripted Modules]] that provide a user interface in the module panel along with specialized implementation logic.
 
 
 
Usage: save the .py file to a directory, add the directory to the additional module paths in the Slicer application settings (choose in the menu: Edit / Application settings, click Modules, click >> next to Additional module paths, click Add, and choose the .py file's location).
 
 
 
More information about python scripted modules and more usage examples can be found in the [[Documentation/{{documentation/version}}/Developers/Python_scripting | Python scripting]] wiki page.
 
 
 
==Filters==
 
* [https://raw.github.com/pieper/VolumeMasker/master/VolumeMasker.py VolumeMasker.py]: Update a target volume with the results of setting all input volume voxels to 0 except for those that correspond to a selected label value in an input label map (Used for example in the volume rendering in [https://www.youtube.com/watch?v=dfu2gugHLHs this video).
 
 
 
==DICOM==
 
* [https://gist.github.com/pieper/6186477 dicom header browser] to easily scroll through dicom files using dcmdump.
 
* [https://github.com/SlicerRt/SlicerRT/tree/master/BatchProcessing SlicerRT batch processing] to batch convert RT structure sets to labelmap NRRD files.
 
 
 
==Informatics==
 
* [https://gist.github.com/lassoan/bf0954d93cacc8cbe27cd4a3ad503f2f MarkupsInfo.py]: Compute the total length between all the points of a markup list.
 
* [https://github.com/lassoan/SlicerLineProfile/blob/master/LineProfile/LineProfile.py LineProfile.py]: Compute intensity profile in a volume along a line.
 
 
 
=Community-contributed examples=
 
 
 
Usage: Copy-paste the shown code lines or linked .py file contents into Python console in Slicer.  Or save them to a file and run them using execfile.
 
 
 
==Capture==
 
* Capture the full Slicer screen and save it into a file
 
  img = qt.QPixmap.grabWidget(slicer.util.mainWindow()).toImage()
 
  img.save('c:/tmp/test.png')
 
* Capture all the views save it into a file:
 
<pre>
 
import ScreenCapture
 
cap = ScreenCapture.ScreenCaptureLogic()
 
cap.showViewControllers(False)
 
cap.captureImageFromView(None,'c:/tmp/test.png')
 
cap.showViewControllers(True)
 
</pre>
 
* Capture a single view:
 
<pre>
 
viewNodeID = 'vtkMRMLViewNode1'
 
import ScreenCapture
 
cap = ScreenCapture.ScreenCaptureLogic()
 
view = cap.viewFromNode(slicer.mrmlScene.GetNodeByID(viewNodeID))
 
cap.captureImageFromView(view,'c:/tmp/test.png')
 
</pre>
 
Common values for viewNodeID: vtkMRMLSliceNodeRed, vtkMRMLSliceNodeYellow, vtkMRMLSliceNodeGreen, vtkMRMLViewNode1, vtkMRMLViewNode2.
 
The ScreenCapture module can also create video animations of rotating views, slice sweeps, etc.
 
 
 
* Capture a slice view sweep into a series of PNG files - for example, Red slice view, 30 images, from position -125.0 to 75.0, into c:/tmp folder, with name image_00001.png, image_00002.png, ...
 
 
 
<pre>
 
import ScreenCapture
 
ScreenCapture.ScreenCaptureLogic().captureSliceSweep(getNode('vtkMRMLSliceNodeRed'), -125.0, 75.0, 30, "c:/tmp", "image_%05d.png")
 
</pre>
 
 
 
* Capture 3D view into PNG file with transparent background
 
<pre>
 
renderWindow = slicer.app.layoutManager().threeDWidget(0).threeDView().renderWindow()
 
renderWindow.SetAlphaBitPlanes(1)
 
wti = vtk.vtkWindowToImageFilter()
 
wti.SetInputBufferTypeToRGBA()
 
wti.SetInput(renderWindow)
 
writer = vtk.vtkPNGWriter()
 
writer.SetFileName("c:/tmp/screenshot.png")
 
writer.SetInputConnection(wti.GetOutputPort())
 
writer.Write()
 
</pre>
 
 
 
==Launching Slicer==
 
* How to open an .mrb file with Slicer at the command line?
 
  Slicer.exe --python-code "slicer.util.loadScene( 'f:/2013-08-23-Scene.mrb' )"
 
* How to run a script in the Slicer environment in batch mode (without showing any graphical user interface)?
 
  Slicer.exe --python-code "doSomething; doSomethingElse; etc." --testing --no-splash --no-main-window
 
 
 
==Load volume from file==
 
When loading a volume from file, it is recommended to set returnNode=True to retrieve the loaded volume node.
 
<pre>
 
[success, loadedVolumeNode] = slicer.util.loadVolume('c:/Users/abc/Documents/MRHead.nrrd', returnNode=True)
 
</pre>
 
 
 
* Get a MRML node in the scene based on the node name and call methods of that object. For the MRHead sample data:
 
  vol=slicer.util.getNode('MR*')
 
  vol.GetImageData().GetDimensions()
 
 
 
 
 
==Show volume rendering automatically when a volume is loaded==
 
 
 
To show volume rendering of a volume automatically when it is loaded, add the lines below to your
 
[[Documentation/{{documentation/version}}/Developers/Python_scripting#How_to_systematically_execute_custom_python_code_at_startup_.3F|.slicerrc file]].
 
 
 
<pre>
 
@vtk.calldata_type(vtk.VTK_OBJECT)
 
def onNodeAdded(caller, event, calldata):
 
  node = calldata
 
  if isinstance(node, slicer.vtkMRMLVolumeNode):
 
    # Call showVolumeRendering using a timer instead of calling it directly
 
    # to allow the volume loading to fully complete.
 
    qt.QTimer.singleShot(0, lambda: showVolumeRendering(node))
 
 
 
def showVolumeRendering(volumeNode):
 
  print("Show volume rendering of node "+volumeNode.GetName())
 
  volRenLogic = slicer.modules.volumerendering.logic()
 
  displayNode = volRenLogic.CreateDefaultVolumeRenderingNodes(volumeNode)
 
  displayNode.SetVisibility(True)
 
  scalarRange = volumeNode.GetImageData().GetScalarRange()
 
  if scalarRange[1]-scalarRange[0] < 1500:
 
    # small dynamic range, probably MRI
 
    displayNode.GetVolumePropertyNode().Copy(volRenLogic.GetPresetByName('MR-Default'))
 
  else:
 
    # larger dynamic range, probably CT
 
    displayNode.GetVolumePropertyNode().Copy(volRenLogic.GetPresetByName('CT-Chest-Contrast-Enhanced'))
 
   
 
slicer.mrmlScene.AddObserver(slicer.vtkMRMLScene.NodeAddedEvent, onNodeAdded)
 
</pre>
 
 
 
==DICOM==
 
=== How to access tags of DICOM images imported into Slicer? For example, to print the first patient's first study's first series' "0020,0032" field:===
 
  db=slicer.dicomDatabase
 
  patientList=db.patients()
 
  studyList=db.studiesForPatient(patientList[0])
 
  seriesList=db.seriesForStudy(studyList[0])
 
  fileList=db.filesForSeries(seriesList[0])
 
  print(db.fileValue(fileList[0],'0020,0032'))
 
 
 
=== How to access tag of a volume loaded from DICOM? For example, get the patient position stored in a volume:===
 
  volumeName='2: ENT IMRT'
 
  n=slicer.util.getNode(volumeName)
 
  instUids=n.GetAttribute('DICOM.instanceUIDs').split()
 
  filename=slicer.dicomDatabase.fileForInstance(instUids[0])
 
  print(slicer.dicomDatabase.fileValue(filename,'0018,5100'))
 
 
 
=== How to access tag of an item in the Subject Hierachy tree? For example, get the content time tag of a structure set:===
 
  rtStructName = '3: RTSTRUCT: PROS'
 
  rtStructNode = slicer.util.getNode(rtStructName)
 
  shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
 
  rtStructShItemID = shNode.GetItemByDataNode(rtStructNode)
 
  ctSliceInstanceUids = shNode.GetItemAttribute(rtStructShItemID, 'DICOM.ReferencedInstanceUIDs').split()
 
  filename = slicer.dicomDatabase.fileForInstance(ctSliceInstanceUids[0])
 
  print(slicer.dicomDatabase.fileValue(filename,'0008,0033'))
 
 
 
=== How to get path and filename of a loaded DICOM volume?===
 
  def pathFromNode(node):
 
    storageNode=node.GetStorageNode()
 
    if storageNode is not None: # loaded via drag-drop
 
        filepath=storageNode.GetFullNameFromFileName()
 
    else: # loaded via DICOM browser
 
        instanceUIDs=node.GetAttribute('DICOM.instanceUIDs').split()
 
        filepath=slicer.dicomDatabase.fileForInstance(instUids[0])
 
    return filepath
 
 
 
  # example:
 
  node=slicer.util.getNode('volume1')
 
  path=self.pathFromNode(node)
 
  print("DICOM path=%s" % path)
 
 
 
=== How can I convert DICOM to NRRD on the command line?===
 
 
 
/Applications/Slicer-4.6.2.app/Contents/MacOS/Slicer --no-main-window --python-code "node=slicer.util.loadVolume('/tmp/series/im0.dcm', returnNode=True)[1]; slicer.util.saveNode(node, '/tmp/output.nrrd'); exit()"
 
 
 
The same can be done on windows by using the top level Slicer.exe.  Be sure to use forward slashes in the pathnames within quotes on the command line.
 
 
 
=== Export a volume to DICOM file format ===
 
 
 
<pre>
 
volumeNode = getNode('CTChest')
 
outputFolder = "c:/tmp/dicom-output"
 
 
 
# Create patient and study and put the volume under the study
 
shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
 
patientItemID = shNode.CreateSubjectItem(shNode.GetSceneItemID(), "test patient")
 
studyItemID = shNode.CreateStudyItem(patientItemID, "test study")
 
volumeShItemID = shNode.GetItemByDataNode(volumeNode)
 
shNode.SetItemParent(volumeShItemID, studyItemID)
 
 
 
import DICOMScalarVolumePlugin
 
exporter = DICOMScalarVolumePlugin.DICOMScalarVolumePluginClass()
 
exportables = exporter.examineForExport(volumeShItemID)
 
for exp in exportables:
 
  exp.directory = outputFolder
 
 
 
exporter.export(exportables)
 
</pre>
 
 
 
==Toolbar functions==
 
* How to turn on slice intersections in the crosshair menu on the toolbar:
 
  viewNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLSliceCompositeNode')
 
  viewNodes.UnRegister(slicer.mrmlScene)
 
  viewNodes.InitTraversal()
 
  viewNode = viewNodes.GetNextItemAsObject()
 
  while viewNode:
 
    viewNode.SetSliceIntersectionVisibility(1)
 
    viewNode = viewNodes.GetNextItemAsObject()
 
 
 
How to find similar functions? For this one I searched for "slice intersections" text in the whole slicer source code, found that the function is implemented in Base\QTGUI\qSlicerViewersToolBar.cxx, then translated the qSlicerViewersToolBarPrivate::setSliceIntersectionVisible(bool visible) method to Python.
 
 
 
==Manipulating objects in the slice viewer==
 
* How to define/edit a circular region of interest in a slice viewer?
 
 
 
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.
 
 
 
<pre>
 
# Update the sphere from the fiducial points
 
def UpdateSphere(param1, param2): 
 
  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
 
markups.AddObserver("ModifiedEvent", UpdateSphere, 2)
 
</pre>
 
 
 
 
 
==Set slice position and orientation from 3 markup fiducials==
 
 
 
Drop 3 markup points in the scene and copy-paste the code below into the Python console. After this, as you move the markups you’ll see the red slice view position and orientation will be set to make it fit to the 3 points.
 
 
 
<pre>
 
# Update plane from fiducial points
 
def UpdateSlicePlane(param1=None, param2=None):
 
  # Get point positions as numpy array
 
  import numpy as np
 
  nOfFiduciallPoints = markups.GetNumberOfFiducials()
 
  if nOfFiduciallPoints < 3:
 
    return  # not enough points
 
  points = np.zeros([3,nOfFiduciallPoints])
 
  for i in range(0, nOfFiduciallPoints):
 
    markups.GetNthFiducialPosition(i, points[:,i])
 
  # Compute plane position and normal
 
  planePosition = points.mean(axis=1)
 
  planeNormal = np.cross(points[:,1] - points[:,0], points[:,2] - points[:,0])
 
  planeX = points[:,1] - points[:,0]
 
  sliceNode.SetSliceToRASByNTP(planeNormal[0], planeNormal[1], planeNormal[2],
 
    planeX[0], planeX[1], planeX[2],
 
    planePosition[0], planePosition[1], planePosition[2], 0)
 
 
 
# Get markup node from scene
 
sliceNode = slicer.app.layoutManager().sliceWidget('Red').mrmlSliceNode()
 
markups = slicer.util.getNode('F')
 
 
 
# Update slice plane manually
 
UpdateSlicePlane()
 
 
 
# Update slice plane automatically whenever points are changed
 
markupObservation = [markups, markups.AddObserver("ModifiedEvent", UpdateSlicePlane, 2)]
 
</pre>
 
 
 
To stop automatic updates, run this:
 
<pre>
 
markupObservation[0].RemoveObserver(markupObservation[1])
 
</pre>
 
 
 
== Switching to markup fiducial placement mode ==
 
 
 
To activate a fiducial placement mode, both interaction mode has to be set and a fiducial node has to be selected:
 
 
 
<pre>
 
interactionNode = slicer.app.applicationLogic().GetInteractionNode()
 
selectionNode = slicer.app.applicationLogic().GetSelectionNode()
 
selectionNode.SetReferenceActivePlaceNodeClassName("vtkMRMLMarkupsFiducialNode")
 
fiducialNode = slicer.vtkMRMLMarkupsFiducialNode()
 
slicer.mrmlScene.AddNode(fiducialNode)
 
fiducialNode.CreateDefaultDisplayNodes()
 
selectionNode.SetActivePlaceNodeID(fiducialNode.GetID())
 
interactionNode.SetCurrentInteractionMode(interactionNode.Place)
 
</pre>
 
 
 
== Change markup fiducial display properties ==
 
 
 
Display properties are stored in display node(s) associated with the fiducial node.
 
 
 
<pre>
 
fiducialNode = getNode('F')
 
fiducialDisplayNode = fiducialNode.GetDisplayNode()
 
fiducialDisplayNode.SetVisibility(False) # Hide all points
 
fiducialDisplayNode.SetVisibility(True) # Show all points
 
fiducialDisplayNode.SetSelectedColor(1,1,0) # Set color to yellow
 
fiducialDisplayNode.SetViewNodeIDs(["vtkMRMLSliceNodeRed", "vtkMRMLViewNode1"]) # Only show in red slice view and first 3D view
 
</pre>
 
 
 
== Get a notification if a markup point position is modified ==
 
 
 
<pre>
 
def onMarkupsNodeModified(markupsNode, unusedArg2=None, unusedArg3=None):
 
  sliceView = markupsNode.GetAttribute('Markups.MovingInSliceView')
 
  if not sliceView:
 
    print("Markup list was modified")
 
    return
 
  movingMarkupIndex = markupsNode.GetAttribute('Markups.MovingMarkupIndex')
 
  pos = [0,0,0]
 
  markupsNode.GetNthFiducialPosition(int(movingMarkupIndex), pos) 
 
  print("Markup {0} was moved in slice view {1} to {2}".format(movingMarkupIndex, sliceView, pos))
 
 
 
markupsNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode")
 
markupsNode.CreateDefaultDisplayNodes()
 
markupsNode.AddFiducial(0,0,0)
 
markupsNode.AddObserver(vtk.vtkCommand.ModifiedEvent, onMarkupsNodeModified)
 
</pre>
 
 
 
 
 
== Get a notification if a transform is modified ==
 
 
 
<pre>
 
def onTransformNodeModified(transformNode, unusedArg2=None, unusedArg3=None):
 
  transformMatrix = vtk.vtkMatrix4x4()
 
  transformNode.GetMatrixTransformToWorld(transformMatrix)
 
  print("Position: [{0}, {1}, {2}]".format(transformMatrix.GetElement(0,3), transformMatrix.GetElement(1,3), transformMatrix.GetElement(2,3)))
 
 
 
transformNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLTransformNode")
 
transformNode.AddObserver(slicer.vtkMRMLTransformNode.TransformModifiedEvent, onTransformNodeModified)
 
</pre>
 
 
 
== Show a context menu when a markup point is clicked in a slice or 3D view ==
 
 
 
<pre>
 
 
 
# Example actions to perform
 
 
 
def action1():
 
  print('Action1 on markup '+str(slicer.clickedMarkupIndex))
 
 
 
def action2():
 
  print('Action2 on markup '+str(slicer.clickedMarkupIndex))
 
 
 
def action3():
 
  print('Action3 on markup '+str(slicer.clickedMarkupIndex))
 
 
 
# Clicked markup index is saved here to let the action
 
# know which markup needs to be manipulated.
 
slicer.clickedMarkupIndex = -1
 
 
 
# Create a simple menu
 
 
 
menu = qt.QMenu()
 
a1 = qt.QAction("Test", slicer.util.mainWindow())
 
a1.connect('triggered()', action1)
 
menu.addAction(a1)
 
a2 = qt.QAction("Action", slicer.util.mainWindow())
 
a2.connect('triggered()', action1)
 
menu.addAction(a2)
 
a3 = qt.QAction("Here", slicer.util.mainWindow())
 
a3.connect('triggered()', action1)
 
menu.addAction(a3)
 
 
 
# Add observer to a markup fiducial list
 
 
 
@vtk.calldata_type(vtk.VTK_INT)
 
def markupClickedCallback(caller, eventId, callData):
 
  slicer.clickedMarkupIndex = callData
 
  print('Open menu on markup '+str(slicer.clickedMarkupIndex))
 
  menu.move(qt.QCursor.pos())
 
  menu.show()
 
 
 
markupsNode = getNode('F')
 
observerTag = markupsNode.AddObserver(slicer.vtkMRMLMarkupsNode.PointClickedEvent, markupClickedCallback)
 
 
 
</pre>
 
 
 
== Write markup positions to JSON file ==
 
 
 
<pre>
 
markupNode = getNode('F')
 
outputFileName = 'c:/tmp/test.json'
 
 
 
# Get markup positions
 
data = []
 
for fidIndex in range(markupNode.GetNumberOfFiducials()):
 
  coords=[0,0,0]
 
  markupNode.GetNthFiducialPosition(fidIndex,coords)
 
  data.append({'label': markupNode.GetNthFiducialLabel(), 'position': coords})
 
 
 
import json
 
with open(outputFileName, 'w') as outfile:
 
  json.dump(data, outfile)
 
</pre>
 
 
 
== Write annotation ROI to JSON file ==
 
 
 
<pre>
 
roiNode = getNode('R')
 
outputFileName = "c:/tmp/test.json"
 
 
 
# Get annotation ROI data
 
center = [0,0,0]
 
radius = [0,0,0]
 
roiNode.GetControlPointWorldCoordinates(0, center)
 
roiNode.GetControlPointWorldCoordinates(1, radius)
 
data = {'center': radius, 'radius': radius}
 
 
 
# Write to json file
 
import json
 
with open(outputFileName, 'w') as outfile:
 
  json.dump(data, outfile)
 
</pre>
 
 
 
== Add a texture mapped plane to the scene as a model ==
 
Note that model textures are not exposed in the GUI and are not saved in the scene
 
<pre>
 
# use dummy image data here
 
e = vtk.vtkImageEllipsoidSource()
 
 
 
scene = slicer.mrmlScene
 
 
 
# Create model node
 
model = slicer.vtkMRMLModelNode()
 
model.SetScene(scene)
 
model.SetName(scene.GenerateUniqueName("2DImageModel"))
 
 
 
planeSource = vtk.vtkPlaneSource()
 
model.SetAndObservePolyData(planeSource.GetOutput())
 
 
 
# Create display node
 
modelDisplay = slicer.vtkMRMLModelDisplayNode()
 
modelDisplay.SetColor(1,1,0) # yellow
 
modelDisplay.SetBackfaceCulling(0)
 
modelDisplay.SetScene(scene)
 
scene.AddNode(modelDisplay)
 
model.SetAndObserveDisplayNodeID(modelDisplay.GetID())
 
 
 
# Add to scene
 
modelDisplay.SetAndObserveTextureImageData(e.GetOutput())
 
scene.AddNode(model)
 
 
 
 
 
transform = slicer.vtkMRMLLinearTransformNode()
 
scene.AddNode(transform)
 
model.SetAndObserveTransformNodeID(transform.GetID())
 
 
 
vTransform = vtk.vtkTransform()
 
vTransform.Scale(50,50,50)
 
vTransform.RotateX(30)
 
transform.SetAndObserveMatrixTransformToParent(vTransform.GetMatrix())
 
</pre>
 
 
 
 
 
== Export entire scene as VRML ==
 
 
 
Save all surface meshes displayed in the scene (models, markups, etc). Solid colors and coloring by scalar is preserved. Textures are not supported.
 
 
 
<pre>
 
exporter = vtk.vtkVRMLExporter()
 
exporter.SetRenderWindow(slicer.app.layoutManager().threeDWidget(0).threeDView().renderWindow())
 
exporter.SetFileName('C:/tmp/something.wrl')
 
exporter.Write()
 
</pre>
 
 
 
== Export model to Blender, including color by scalar ==
 
 
 
<pre>
 
modelNode = getNode("Model")
 
plyFilePath = "c:/tmp/model.ply"
 
 
 
modelDisplayNode = modelNode.GetDisplayNode()
 
triangles = vtk.vtkTriangleFilter()
 
triangles.SetInputConnection(modelDisplayNode.GetOutputPolyDataConnection())
 
 
 
plyWriter = vtk.vtkPLYWriter()
 
plyWriter.SetInputConnection(triangles.GetOutputPort())
 
lut = vtk.vtkLookupTable()
 
lut.DeepCopy(modelDisplayNode.GetColorNode().GetLookupTable())
 
lut.SetRange(modelDisplayNode.GetScalarRange())
 
plyWriter.SetLookupTable(lut)
 
plyWriter.SetArrayName(modelDisplayNode.GetActiveScalarName())
 
 
 
plyWriter.SetFileName(plyFilePath)
 
plyWriter.Write()
 
</pre>
 
 
 
== Export a tract (FiberBundle) to Blender, including color ==
 
<div id="Export_a_fiber_tracts_to_Blender.2C_including_color"></div>
 
Note: an interactive version of this script is now included in the [http://dmri.slicer.org/ SlicerDMRI extension] ([https://github.com/SlicerDMRI/SlicerDMRI/tree/master/Modules/Scripted/TractographyExportPLY module code]).
 
After installing SlicerDMRI, go to ''Modules -> Diffusion -> Import and Export -> Export tractography to PLY (mesh)''.
 
 
 
The example below shows how to export a tractography "FiberBundleNode" to a PLY file:
 
 
 
<pre>
 
lineDisplayNode = getNode("*LineDisplay*")
 
plyFilePath = "/tmp/fibers.ply"
 
 
 
tuber = vtk.vtkTubeFilter()
 
tuber.SetInputData(lineDisplayNode.GetOutputPolyData())
 
tuber.Update()
 
tubes = tuber.GetOutputDataObject(0)
 
scalars = tubes.GetPointData().GetArray(0)
 
scalars.SetName("scalars")
 
 
 
triangles = vtk.vtkTriangleFilter()
 
triangles.SetInputData(tubes)
 
triangles.Update()
 
 
 
colorNode = lineDisplayNode.GetColorNode()
 
lookupTable = vtk.vtkLookupTable()
 
lookupTable.DeepCopy(colorNode.GetLookupTable())
 
lookupTable.SetTableRange(0,1)
 
 
 
plyWriter = vtk.vtkPLYWriter()
 
plyWriter.SetInputData(triangles.GetOutput())
 
plyWriter.SetLookupTable(lookupTable)
 
plyWriter.SetArrayName("scalars")
 
 
 
plyWriter.SetFileName(plyFilePath)
 
plyWriter.Write()
 
</pre>
 
 
 
== Iterate over tract (FiberBundle) streamline points ==
 
 
 
This example shows how to access the points in each line of a FiberBundle as a numpy array (view).
 
 
 
<pre>
 
from vtk.util.numpy_support import vtk_to_numpy
 
 
 
fb = getNode("FiberBundle_F") # <- fill in node ID here
 
 
 
# get point data as 1d array
 
points = slicer.util.arrayFromModelPoints(fb)
 
 
 
# get line cell ids as 1d array
 
line_ids = vtk_to_numpy(fb.GetPolyData().GetLines().GetData())
 
 
 
# VTK cell ids are stored as
 
#  [ N0 c0_id0 ... c0_id0
 
#    N1 c1_id0 ... c1_idN1 ]
 
# so we need to
 
# - read point count for each line (cell)
 
# - grab the ids in that range from `line_ids` array defined above
 
# - index the `points` array by those ids
 
cur_idx = 1
 
for _ in range(pd.GetLines().GetNumberOfCells()):
 
    # - read point count for this line (cell)
 
    count = lines[cur_idx - 1]
 
 
 
    # - grab the ids in that range from `lines`
 
    index_array = line_ids[ cur_idx : cur_idx + count]
 
    # update to the next range
 
    cur_idx += count + 1
 
 
 
    # - index the point array by those ids
 
    line_points = points[index_array]
 
 
 
    # do work here
 
</pre>
 
 
 
== Clone a node ==
 
 
 
This example shows how to make a copy of any node that appears in Subject Hierarchy (in Data module).
 
 
 
<pre>
 
# Get a node from SampleData that we will clone
 
import SampleData
 
nodeToClone = SampleData.SampleDataLogic().downloadMRHead()
 
 
 
# Clone the node
 
shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
 
itemIDToClone = shNode.GetItemByDataNode(nodeToClone)
 
clonedItemID = slicer.modules.subjecthierarchy.logic().CloneSubjectHierarchyItem(shNode, itemIDToClone)
 
clonedNode = shNode.GetItemDataNode(clonedItemID)
 
</pre>
 
 
 
== Clone a volume ==
 
This example shows how to clone the MRHead sample volume, including its pixel data and display settings.
 
<pre>
 
sourceVolumeNode = slicer.util.getNode('MRHead')
 
volumesLogic = slicer.modules.volumes.logic()
 
clonedVolumeNode = volumesLogic.CloneVolume(slicer.mrmlScene, sourceVolumeNode, 'Cloned volume')
 
</pre>
 
 
 
== Create a new volume ==
 
This example shows how to create a new empty volume.
 
<pre>
 
nodeName = "MyNewVolume"
 
imageSize = [512, 512, 512]
 
voxelType=vtk.VTK_UNSIGNED_CHAR
 
imageOrigin = [0.0, 0.0, 0.0]
 
imageSpacing = [1.0, 1.0, 1.0]
 
imageDirections = [[1,0,0], [0,1,0], [0,0,1]]
 
fillVoxelValue = 0
 
 
 
# Create an empty image volume, filled with fillVoxelValue
 
imageData = vtk.vtkImageData()
 
imageData.SetDimensions(imageSize)
 
imageData.AllocateScalars(voxelType, 1)
 
thresholder = vtk.vtkImageThreshold()
 
thresholder.SetInputData(imageData)
 
thresholder.SetInValue(fillVoxelValue)
 
thresholder.SetOutValue(fillVoxelValue)
 
thresholder.Update()
 
# Create volume node
 
volumeNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLScalarVolumeNode", nodeName)
 
volumeNode.SetOrigin(imageOrigin)
 
volumeNode.SetSpacing(imageSpacing)
 
volumeNode.SetIJKToRASDirections(imageDirections)
 
volumeNode.SetAndObserveImageData(thresholder.GetOutput())
 
volumeNode.CreateDefaultDisplayNodes()
 
volumeNode.CreateDefaultStorageNode()
 
</pre>
 
 
 
== Modify voxels in a volume ==
 
 
 
Typically the fastest and simplest way of modifying voxels is by using numpy operators. Voxels can be retrieved in a numpy array using the `array` method and modified using standard numpy methods. For example, threshold a volume:
 
 
 
<pre>
 
nodeName = 'MRHead'
 
thresholdValue = 100
 
voxelArray = array(nodeName) # get voxels as numpy array
 
voxelArray[voxelArray < thresholdValue] = 0 # modify voxel values
 
getNode(nodeName).Modified() # at the end of all processing, notify Slicer that the image modification is completed
 
</pre>
 
 
 
This example shows how to change voxels values of the MRHead sample volume.
 
The values will be computed by function f(r,a,s,) = (r-10)*(r-10)+(a+15)*(a+15)+s*s.
 
<pre>
 
volumeNode=slicer.util.getNode('MRHead')
 
ijkToRas = vtk.vtkMatrix4x4()
 
volumeNode.GetIJKToRASMatrix(ijkToRas)
 
imageData=volumeNode.GetImageData()
 
extent = imageData.GetExtent()
 
for k in xrange(extent[4], extent[5]+1):
 
  for j in xrange(extent[2], extent[3]+1):
 
    for i in xrange(extent[0], extent[1]+1):
 
      position_Ijk=[i, j, k, 1]
 
      position_Ras=ijkToRas.MultiplyPoint(position_Ijk)
 
      r=position_Ras[0]
 
      a=position_Ras[1]
 
      s=position_Ras[2]     
 
      functionValue=(r-10)*(r-10)+(a+15)*(a+15)+s*s
 
      imageData.SetScalarComponentFromDouble(i,j,k,0,functionValue)
 
imageData.SetScalarComponentFromFloat(distortionVectorPosition_Ijk[0], distortionVectorPosition_Ijk[1], distortionVectorPosition_Ijk[2], 0, fillValue)
 
imageData.Modified()
 
</pre>
 
 
 
== Get volume voxel coordinates from markup fiducial RAS coordinates ==
 
 
 
This example shows how to get voxel coordinate of a volume corresponding to a markup fiducial point position.
 
 
 
<pre>
 
# Inputs
 
volumeNode = getNode('MRHead')
 
markupsNode = getNode('F')
 
markupsIndex = 0
 
 
 
# Get point coordinate in RAS
 
point_Ras = [0, 0, 0, 1]
 
markupsNode.GetNthFiducialWorldCoordinates(markupsIndex, point_Ras)
 
 
 
# If volume node is transformed, apply that transform to get volume's RAS coordinates
 
transformRasToVolumeRas = vtk.vtkGeneralTransform()
 
slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(None, volumeNode.GetParentTransformNode(), transformRasToVolumeRas)
 
point_VolumeRas = transformRasToVolumeRas.TransformPoint(point_Ras[0:3])
 
 
 
# Get voxel coordinates from physical coordinates
 
volumeRasToIjk = vtk.vtkMatrix4x4()
 
volumeNode.GetRASToIJKMatrix(volumeRasToIjk)
 
point_Ijk = [0, 0, 0, 1]
 
volumeRasToIjk.MultiplyPoint(np.append(point_VolumeRas,1.0), point_Ijk)
 
point_Ijk = [ int(round(c)) for c in point_Ijk[0:3] ]
 
 
 
# Print output
 
print(point_Ijk)
 
</pre>
 
 
 
== Get markup fiducial RAS coordinates from volume voxel coordinates ==
 
 
 
This example shows how to get position of maximum intensity voxel of a volume (determined by numpy, in IJK coordinates) in RAS coordinates so that it can be marked with a markup fiducial.
 
 
 
<pre>
 
# Inputs
 
volumeNode = getNode('MRHead')
 
markupsNode = getNode('F')
 
 
 
# Get voxel position in IJK coordinate system
 
import numpy as np
 
volumeArray = slicer.util.arrayFromVolume(volumeNode)
 
# Get position of highest voxel value
 
point_Kji = np.where(volumeArray == volumeArray.max())
 
point_Ijk = [point_Kji[2][0], point_Kji[1][0], point_Kji[0][0]]
 
 
 
# Get physical coordinates from voxel coordinates
 
volumeIjkToRas = vtk.vtkMatrix4x4()
 
volumeNode.GetIJKToRASMatrix(volumeIjkToRas)
 
point_VolumeRas = [0, 0, 0, 1]
 
volumeIjkToRas.MultiplyPoint(np.append(point_Ijk,1.0), point_VolumeRas)
 
 
 
# If volume node is transformed, apply that transform to get volume's RAS coordinates
 
transformVolumeRasToRas = vtk.vtkGeneralTransform()
 
slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(volumeNode.GetParentTransformNode(), None, transformVolumeRasToRas)
 
point_Ras = transformVolumeRasToRas.TransformPoint(point_VolumeRas[0:3])
 
 
 
# Add a markup at the computed position and print its coordinates
 
markupsNode.AddFiducial(point_Ras[0], point_Ras[1], point_Ras[2], "max")
 
print(point_Ras)
 
</pre>
 
 
 
== Get the values of all voxels for a label value  ==
 
 
 
If you have a background image called ‘Volume’ and a mask called ‘Volume-label’ created with the Editor you could do something like this:
 
 
 
<pre>
 
 
 
import numpy
 
volume = array(‘Volume’)
 
label = array(‘Volume-label’)
 
points  = numpy.where( label == 1 )  # or use another label number depending on what you segmented
 
values  = volume[points] # this will be a list of the label values
 
values.mean() # should match the mean value of LabelStatistics calculation as a double-check
 
numpy.savetxt(‘values.txt’, values)
 
</pre>
 
 
 
== Access values in a DTI tensor volume ==
 
This example shows how to access individual tensors at the voxel level.
 
 
 
First load your DWI volume and estimate tensors to produce a DTI volume called ‘Output DTI Volume’
 
 
 
Then open the python window: View->Python interactor
 
 
 
Use this command to access tensors through numpy:
 
 
 
<pre>
 
tensors = array('Output DTI Volume')
 
</pre>
 
 
 
Type the following code into the Python window to access all tensor components using vtk commands:
 
 
 
<pre>
 
volumeNode=slicer.util.getNode('Output DTI Volume')
 
imageData=volumeNode.GetImageData()
 
tensors = imageData.GetPointData().GetTensors()
 
extent = imageData.GetExtent()
 
idx = 0
 
for k in xrange(extent[4], extent[5]+1):
 
  for j in xrange(extent[2], extent[3]+1):
 
    for i in xrange(extent[0], extent[1]+1):
 
      tensors.GetTuple9(idx)
 
      idx += 1
 
</pre>
 
 
 
== Change window/level (brightness/contrast) or colormap of a volume ==
 
This example shows how to change window/level of the MRHead sample volume.
 
<pre>
 
volumeNode = getNode('MRHead')
 
displayNode = volumeNode.GetDisplayNode()
 
displayNode.AutoWindowLevelOff()
 
displayNode.SetWindow(50)
 
displayNode.SetLevel(100)
 
</pre>
 
 
 
Change color mapping from grayscale to rainbow:
 
<pre>
 
displayNode.SetAndObserveColorNodeID('vtkMRMLColorTableNodeRainbow')
 
</pre>
 
 
 
== Create custom color table ==
 
This example shows how to create a new color table, for example with inverted color range from the default Ocean color table.
 
<pre>
 
invertedocean = slicer.vtkMRMLColorTableNode()
 
invertedocean.SetTypeToUser()
 
invertedocean.SetNumberOfColors(256)
 
invertedocean.SetName("InvertedOcean")
 
 
 
for i in range(0,255):
 
    invertedocean.SetColor(i, 0.0, 1 - (i+1e-16)/255.0, 1.0, 1.0)
 
 
 
slicer.mrmlScene.AddNode(invertedocean)
 
</pre>
 
 
 
== Manipulate a Slice View ==
 
 
 
=== Change the slice offset ===
 
 
 
Equivalent to moving the slider in slice view controller.
 
 
 
<pre>
 
layoutManager = slicer.app.layoutManager()
 
red = layoutManager.sliceWidget('Red')
 
redLogic = red.sliceLogic()
 
# Print current slice offset position
 
print(redLogic.GetSliceOffset())
 
# Change slice position
 
redLogic.SetSliceOffset(20)
 
</pre>
 
 
 
=== Show slice views in 3D window ===
 
 
 
Equivalent to clicking 'eye' icon in the slice view controller.
 
 
 
<pre>
 
layoutManager = slicer.app.layoutManager()
 
for sliceViewName in layoutManager.sliceViewNames():
 
  controller = layoutManager.sliceWidget(sliceViewName).sliceController()
 
  controller.setSliceVisible(True)
 
</pre>
 
 
 
=== Reset field of view to show background volume maximized ===
 
 
 
Equivalent to click small rectangle button ("Adjust the slice viewer's field of view...") in the slice view controller.
 
 
 
<pre>
 
slicer.util.resetSliceViews()
 
</pre>
 
 
 
=== Rotate slice views to volume plane ===
 
 
 
Aligns slice views to volume axes, shows original image acquisition planes in slice views.
 
 
 
<pre>
 
volumeNode = slicer.util.getNode('MRHead')
 
layoutManager = slicer.app.layoutManager()
 
for sliceViewName in layoutManager.sliceViewNames():
 
  layoutManager.sliceWidget(sliceViewName).mrmlSliceNode().RotateToVolumePlane(volumeNode)
 
</pre>
 
 
 
=== Iterate over current visible slice views, and set foreground and background images ===
 
 
 
<pre>
 
slicer.util.setSliceViewerLayers(background=mrVolume, foreground=ctVolume)
 
</pre>
 
 
 
Internally, this method performs something like this:
 
 
 
<pre>
 
for sliceViewName in layoutManager.sliceViewNames():
 
    sliceWidget = layoutManager.sliceWidget(sliceViewName)
 
    # setup background volume
 
    compositeNode.SetBackgroundVolumeID(mrVolume.GetID())
 
    # setup foreground volume
 
    compositeNode.SetForegroundVolumeID(ctVolume.GetID())
 
    # change opacity
 
    compositeNode.SetForegroundOpacity(0.3)
 
</pre>
 
 
 
== Synchronize zoom factor between slice views ==
 
 
 
<pre>
 
slicer.sliceNodes = [slicer.app.layoutManager().sliceWidget(viewName).mrmlSliceNode()
 
    for viewName in slicer.app.layoutManager().sliceViewNames()]
 
 
 
slicer.updatingSliceNodes = False
 
 
 
def sliceModified(caller, event):
 
    if slicer.updatingSliceNodes:
 
        # prevent infinite loop of slice node updates triggering slice node updates
 
        return
 
    slicer.updatingSliceNodes = True
 
    fov = caller.GetFieldOfView()
 
    for sliceNode in slicer.sliceNodes:
 
        if sliceNode != caller:
 
            sliceNode.SetFieldOfView(*fov)
 
    slicer.updatingSliceNodes = False
 
 
 
for sliceNode in slicer.sliceNodes:
 
    sliceNode.AddObserver(vtk.vtkCommand.ModifiedEvent, sliceModified)
 
</pre>
 
 
 
== Show a volume in slice views ==
 
 
 
Recommended:
 
 
 
<pre>
 
volumeNode = slicer.util.getNode('YourVolumeNode')
 
slicer.util.setSliceViewerLayers(background=volumeNode)
 
</pre>
 
 
 
or
 
 
 
Show volume in all visible views where volume selection propagation is enabled:
 
 
 
<pre>
 
volumeNode = slicer.util.getNode('YourVolumeNode')
 
applicationLogic = slicer.app.applicationLogic()
 
selectionNode = applicationLogic.GetSelectionNode()
 
selectionNode.SetSecondaryVolumeID(volumeNode.GetID())
 
applicationLogic.PropagateForegroundVolumeSelection(0)
 
</pre>
 
 
 
or
 
 
 
Show volume in selected views:
 
 
 
<pre>
 
n =  slicer.util.getNode('YourVolumeNode')
 
for color in ['Red', 'Yellow', 'Green']:
 
    slicer.app.layoutManager().sliceWidget(color).sliceLogic().GetSliceCompositeNode().SetForegroundVolumeID(n.GetID())
 
</pre>
 
 
 
== Change opacity of foreground volume in slice views ==
 
 
 
<pre>
 
slicer.util.setSliceViewerLayers(foregroundOpacity=0.4)
 
</pre>
 
 
 
or
 
 
 
Change opacity in a selected view
 
 
 
<pre>
 
lm = slicer.app.layoutManager()
 
sliceLogic = lm.sliceWidget('Red').sliceLogic()
 
compositeNode = sliceLogic.GetSliceCompositeNode()
 
compositeNode.SetForegroundOpacity(0.4)
 
</pre>
 
 
 
== Fit slice plane to markup fiducials ==
 
 
 
<pre>
 
sliceNode = slicer.mrmlScene.GetNodeByID("vtkMRMLSliceNodeRed")
 
markupsNode = slicer.mrmlScene.GetFirstNodeByName("F")
 
# Get markup point positions as numpy arrays
 
import numpy as np
 
p1 = np.array([0,0,0])
 
p2 = np.array([0,0,0])
 
p3 = np.array([0,0,0])
 
markupsNode.GetNthFiducialPosition(0, p1)
 
markupsNode.GetNthFiducialPosition(1, p2)
 
markupsNode.GetNthFiducialPosition(2, p3)
 
# Get plane axis directions
 
n = np.cross(p2-p1, p2-p3) # plane normal direction
 
n = n/np.linalg.norm(n)
 
t = np.cross([0, 0, 1], n) # plane transverse direction
 
t = t/np.linalg.norm(t)
 
# Set slice plane orientation and position
 
sliceNode.SetSliceToRASByNTP(n[0], n[1], n[2], t[0], t[1], t[2], p1[0], p1[1], p1[2], 0)
 
</pre>
 
 
 
== Save a series of images from a Slice View ==
 
 
 
You can use ScreenCapture module to capture series of images. To do it programmatically, save the following into a file such as '/tmp/record.py' and then in the slicer python console type "execfile('/tmp/record.py')"
 
 
 
<pre>
 
layoutName = 'Green'
 
imagePathPattern = '/tmp/image-%03d.png'
 
steps = 10
 
 
 
widget = slicer.app.layoutManager().sliceWidget(layoutName)
 
view = widget.sliceView()
 
logic = widget.sliceLogic()
 
bounds = [0,]*6
 
logic.GetSliceBounds(bounds)
 
 
 
for step in range(steps):
 
    offset = bounds[4] + step/(1.*steps) * (bounds[5]-bounds[4])
 
    logic.SetSliceOffset(offset)
 
    view.forceRender()
 
    image = qt.QPixmap.grabWidget(view).toImage()
 
    image.save(imagePathPattern % step)
 
</pre>
 
 
 
== Save the scene into a new directory ==
 
 
 
<pre>
 
# Create a new directory where the scene will be saved into
 
import time
 
sceneSaveDirectory = slicer.app.temporaryPath + "/saved-scene-" + time.strftime("%Y%m%d-%H%M%S")
 
if not os.access(sceneSaveDirectory, os.F_OK):
 
  os.makedirs(sceneSaveDirectory)
 
 
 
# Save the scene
 
if slicer.app.applicationLogic().SaveSceneToSlicerDataBundleDirectory(sceneSaveDirectory, None):
 
  logging.info("Scene saved to: {0}".format(sceneSaveDirectory))
 
else:
 
  logging.error("Scene saving failed")
 
</pre>
 
 
 
== Save the scene into a single MRB file ==
 
<pre>
 
# Generate file name
 
import time
 
sceneSaveFilename = slicer.app.temporaryPath + "/saved-scene-" + time.strftime("%Y%m%d-%H%M%S") + ".mrb"
 
 
 
# Save scene
 
if slicer.util.saveScene(sceneSaveFilename):
 
  logging.info("Scene saved to: {0}".format(sceneSaveFilename))
 
else:
 
  logging.error("Scene saving failed")
 
</pre>
 
 
 
== Save a node to file ==
 
 
 
Save a transform node to file (should work with any other node type, if file extension is set to a supported one):
 
 
 
<pre>
 
myNode = getNode("LinearTransform_3")
 
 
 
myStorageNode = myNode.CreateDefaultStorageNode()
 
myStorageNode.SetFileName("c:/tmp/something.tfm")
 
myStorageNode.WriteData(myNode)
 
</pre>
 
 
 
== Center the 3D View on the Scene ==
 
<pre>
 
layoutManager = slicer.app.layoutManager()
 
threeDWidget = layoutManager.threeDWidget(0)
 
threeDView = threeDWidget.threeDView()
 
threeDView.resetFocalPoint()
 
</pre>
 
 
 
==Rotate the 3D View==
 
 
 
<pre>
 
layoutManager = slicer.app.layoutManager()
 
threeDWidget = layoutManager.threeDWidget(0)
 
threeDView = threeDWidget.threeDView()
 
threeDView.yaw()
 
</pre>
 
 
 
== Display text in a 3D view or slice view ==
 
 
 
The easiest way to show information overlaid on a viewer is to use corner annotations.
 
 
 
<pre>
 
view=slicer.app.layoutManager().threeDWidget(0).threeDView()
 
# Set text to "Something"
 
view.cornerAnnotation().SetText(vtk.vtkCornerAnnotation.UpperRight,"Something")
 
# Set color to red
 
view.cornerAnnotation().GetTextProperty().SetColor(1,0,0)
 
# Update the view
 
view.forceRender()
 
</pre>
 
 
 
== Hide slice view annotations (DataProbe) ==
 
 
 
<pre>
 
# Disable slice annotations immediately
 
slicer.modules.DataProbeInstance.infoWidget.sliceAnnotations.sliceViewAnnotationsEnabled=False
 
slicer.modules.DataProbeInstance.infoWidget.sliceAnnotations.updateSliceViewFromGUI()
 
# Disable slice annotations persistently (after Slicer restarts)
 
settings = qt.QSettings()
 
settings.setValue('DataProbe/sliceViewAnnotations.enabled', 0)
 
</pre>
 
 
 
== Turning off interpolation ==
 
 
 
You can turn off interpolation for newly loaded volumes with this script from Steve Pieper.
 
 
 
<pre>
 
def NoInterpolate(caller,event):
 
  for node in slicer.util.getNodes('*').values():
 
    if node.IsA('vtkMRMLScalarVolumeDisplayNode'):
 
      node.SetInterpolate(0)
 
 
slicer.mrmlScene.AddObserver(slicer.mrmlScene.NodeAddedEvent, NoInterpolate)
 
</pre>
 
 
 
The below link explains how to put this in your startup script.
 
 
 
http://www.na-mic.org/Wiki/index.php/AHM2012-Slicer-Python#Refining_the_code_and_UI_with_slicerrc
 
 
 
 
 
== Customize viewer layout ==
 
 
 
Show a custom layout of a 3D view on top of the red slice view:
 
 
 
<pre>
 
customLayout = ("<layout type=\"vertical\" split=\"true\" >"
 
  " <item>"
 
  "  <view class=\"vtkMRMLViewNode\" singletontag=\"1\">"
 
  "    <property name=\"viewlabel\" action=\"default\">1</property>"
 
  "  </view>"
 
  " </item>"
 
  " <item>"
 
  "  <view class=\"vtkMRMLSliceNode\" singletontag=\"Red\">"
 
  "  <property name=\"orientation\" action=\"default\">Axial</property>"
 
  "  <property name=\"viewlabel\" action=\"default\">R</property>"
 
  "  <property name=\"viewcolor\" action=\"default\">#F34A33</property>"
 
  "  </view>"
 
  " </item>"
 
  "</layout>")
 
 
 
customLayoutId=501
 
 
 
layoutManager = slicer.app.layoutManager()
 
layoutManager.layoutLogic().GetLayoutNode().AddLayoutDescription(customLayoutId, customLayout)                                       
 
layoutManager.setLayout(customLayoutId)
 
</pre>
 
 
 
See description of standard layouts (that can be used as examples) here:
 
https://github.com/Slicer/Slicer/blob/master/Libs/MRML/Logic/vtkMRMLLayoutLogic.cxx
 
 
 
== Customize keyboard shortcuts ==
 
 
 
Keyboard shortcuts can be specified for activating any Slicer feature by adding a couple of lines to your
 
[[Documentation/{{documentation/version}}/Developers/Python_scripting#How_to_systematically_execute_custom_python_code_at_startup_.3F|.slicerrc file]].
 
 
 
For example, this script registers ''Ctrl+b'', ''Ctrl+n'', ''Ctrl+m'', ''Ctrl+,'' keyboard shortcuts to switch between red, yellow, green, and 4-up view layouts.
 
 
 
<pre>
 
shortcuts = [
 
    ('Ctrl+b', lambda: slicer.app.layoutManager().setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpRedSliceView)),
 
    ('Ctrl+n', lambda: slicer.app.layoutManager().setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpYellowSliceView)),
 
    ('Ctrl+m', lambda: slicer.app.layoutManager().setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpGreenSliceView)),
 
    ('Ctrl+,', lambda: slicer.app.layoutManager().setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpView))
 
    ]
 
 
 
for (shortcutKey, callback) in shortcuts:
 
    shortcut = qt.QShortcut(slicer.util.mainWindow())
 
    shortcut.setKey(qt.QKeySequence(shortcutKey))
 
    shortcut.connect( 'activated()', callback)
 
</pre>
 
 
 
== Disable certain user interactions in slice views ==
 
 
 
For example, disable slice browsing using mouse wheel and keyboard shortcuts in the red slice viewer:
 
 
 
<pre>
 
interactorStyle = slicer.app.layoutManager().sliceWidget('Red').sliceView().sliceViewInteractorStyle()
 
interactorStyle.SetActionEnabled(interactorStyle.BrowseSlice, False)
 
</pre>
 
 
 
Hide all slice view controllers:
 
<pre>
 
lm = slicer.app.layoutManager()
 
for sliceViewName in lm.sliceViewNames():
 
  lm.sliceWidget(sliceViewName).sliceController().setVisible(False)
 
</pre>
 
 
 
Hide all 3D view controllers:
 
<pre>
 
lm = slicer.app.layoutManager()
 
for viewIndex in range(slicer.app.layoutManager().threeDViewCount):
 
  lm.threeDWidget(0).threeDController().setVisible(False)
 
</pre>
 
 
 
== Change default slice view orientation ==
 
 
 
You can left-right "flip" slice view orientation presets (show patient left side on left/right side of the screen) by copy-pasting the script below to your [[Documentation/{{documentation/version}}/Developers/FAQ/Python_Scripting#How_to_systematically_execute_custom_python_code_at_startup_.3F| .slicerrc.py file]].
 
 
 
<pre>
 
# Axial slice axes:
 
#  1 0 0
 
#  0 1 0
 
#  0 0 1
 
axialSliceToRas=vtk.vtkMatrix3x3()
 
 
 
# Coronal slice axes:
 
#  1 0 0
 
#  0 0 -1
 
#  0 1 0
 
coronalSliceToRas=vtk.vtkMatrix3x3()
 
coronalSliceToRas.SetElement(1,1, 0)
 
coronalSliceToRas.SetElement(1,2, -1)
 
coronalSliceToRas.SetElement(2,1, 1)
 
coronalSliceToRas.SetElement(2,2, 0)
 
 
 
# Replace orientation presets in all existing slice nodes and in the default slice node
 
sliceNodes = slicer.util.getNodesByClass('vtkMRMLSliceNode')
 
sliceNodes.append(slicer.mrmlScene.GetDefaultNodeByClass('vtkMRMLSliceNode'))
 
for sliceNode in sliceNodes:
 
  orientationPresetName = sliceNode.GetOrientation()
 
  sliceNode.RemoveSliceOrientationPreset("Axial")
 
  sliceNode.AddSliceOrientationPreset("Axial", axialSliceToRas)
 
  sliceNode.RemoveSliceOrientationPreset("Coronal")
 
  sliceNode.AddSliceOrientationPreset("Coronal", coronalSliceToRas)
 
  sliceNode.SetOrientation(orientationPresetName)
 
</pre>
 
 
 
 
 
== Set all slice views linked by default ==
 
 
 
You can make slice views linked by default (when application starts or the scene is cleared) by copy-pasting the script below to your [[Documentation/{{documentation/version}}/Developers/FAQ/Python_Scripting#How_to_systematically_execute_custom_python_code_at_startup_.3F| .slicerrc.py file]].
 
 
 
<pre>
 
# Set linked slice views  in all existing slice composite nodes and in the default node
 
sliceCompositeNodes = slicer.util.getNodesByClass('vtkMRMLSliceCompositeNode')
 
defaultSliceCompositeNode = slicer.mrmlScene.GetDefaultNodeByClass('vtkMRMLSliceCompositeNode')
 
if not defaultSliceCompositeNode:
 
  defaultSliceCompositeNode = slicer.mrmlScene.CreateNodeByClass('vtkMRMLSliceCompositeNode')
 
  slicer.mrmlScene.AddDefaultNode(defaultSliceCompositeNode)
 
sliceCompositeNodes.append(defaultSliceCompositeNode)
 
for sliceCompositeNode in sliceCompositeNodes:
 
  sliceCompositeNode.SetLinkedControl(True)
 
</pre>
 
 
 
== Set up custom units in slice view ruler ==
 
 
 
For microscopy or micro-CT images you may want to switch unit to micrometer instead of the default mm. To do that, 1. change the unit in Application settings / Units and 2. update ruler display settings using the script below (it can be copied to your Application startup script):
 
 
 
<pre>
 
lm = slicer.app.layoutManager()
 
for sliceViewName in lm.sliceViewNames():
 
  sliceView = lm.sliceWidget(sliceViewName).sliceView()
 
  displayableManagerCollection = vtk.vtkCollection()
 
  sliceView.getDisplayableManagers(displayableManagerCollection)
 
  for dmIndex in xrange(displayableManagerCollection.GetNumberOfItems()):
 
    displayableManager = displayableManagerCollection.GetItemAsObject(dmIndex)
 
    if not displayableManager.IsA("vtkMRMLRulerDisplayableManager"):
 
      continue
 
    displayableManager.RemoveAllRulerScalePresets()
 
    displayableManager.AddRulerScalePreset(  0.001, 5, 2, "nm", 1000.0)
 
    displayableManager.AddRulerScalePreset(  0.010, 5, 2, "nm", 1000.0)
 
    displayableManager.AddRulerScalePreset(  0.100, 5, 2, "nm", 1000.0)
 
    displayableManager.AddRulerScalePreset(  0.500, 5, 1, "nm", 1000.0)
 
    displayableManager.AddRulerScalePreset(  1.0,  5, 2, "um",    1.0)
 
    displayableManager.AddRulerScalePreset(  5.0,  5, 1, "um",    1.0)
 
    displayableManager.AddRulerScalePreset(  10.0,  5, 2, "um",    1.0)
 
    displayableManager.AddRulerScalePreset(  50.0,  5, 1, "um",    1.0)
 
    displayableManager.AddRulerScalePreset( 100.0,  5, 2, "um",    1.0)
 
    displayableManager.AddRulerScalePreset( 500.0,  5, 1, "um",    1.0)
 
    displayableManager.AddRulerScalePreset(1000.0,  5, 2, "mm",    0.001)
 
</pre>
 
 
 
== Show a slice view outside the view layout ==
 
 
 
<pre>
 
sliceLayoutName = "TestSlice"
 
sliceLayoutLabel = "T"
 
# ownerNode manages this view instead of the layout manager (it can be any node in the scene)
 
viewOwnerNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLScriptedModuleNode")
 
 
 
# Create MRML nodes
 
sliceNode = slicer.vtkMRMLSliceNode()
 
sliceNode.SetName(sliceLayoutName)
 
sliceNode.SetLayoutName(sliceLayoutName)
 
sliceNode.SetLayoutLabel(sliceLayoutLabel)
 
sliceNode.SetLayoutColor(1, 1, 0)
 
sliceNode.SetAndObserveParentLayoutNodeID(viewOwnerNode.GetID())
 
sliceNode = slicer.mrmlScene.AddNode(sliceNode)
 
sliceCompositeNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSliceCompositeNode")
 
sliceCompositeNode.SetLayoutName(sliceLayoutName)
 
 
 
# Create widget
 
sliceWidget = slicer.qMRMLSliceWidget()
 
sliceWidget.sliceViewName = sliceLayoutName
 
sliceWidget.sliceViewLabel = sliceLayoutLabel
 
c = sliceNode.GetLayoutColor()
 
sliceWidget.sliceViewColor = qt.QColor.fromRgbF(c[0],c[1],c[2])
 
sliceWidget.setMRMLScene(slicer.mrmlScene)
 
sliceWidget.setMRMLSliceNode(sliceNode)
 
sliceWidget.show()
 
</pre>
 
 
 
== Running an ITK filter in Python using SimpleITK ==
 
Open the "Sample Data" module and download "MR Head", then paste the following snippet in Python interactor:
 
<pre>
 
import SimpleITK as sitk
 
import sitkUtils
 
inputImage = sitkUtils.PullFromSlicer('MRHead')
 
filter = sitk.SignedMaurerDistanceMapImageFilter()
 
outputImage = filter.Execute(inputImage)
 
sitkUtils.PushToSlicer(outputImage,'outputImage')
 
</pre>
 
 
 
More information:
 
* See the SimpleITK documentation for SimpleITK examples: http://www.itk.org/SimpleITKDoxygen/html/examples.html
 
* sitkUtils in Slicer is used for pushing and pulling images from Slicer to SimpleITK: https://github.com/Slicer/Slicer/blob/master/Base/Python/sitkUtils.py
 
 
 
== Get current mouse coordinates in a slice view ==
 
 
 
You can get 3D (RAS) coordinates of the current mouse cursor from the crosshair singleton node as shown in the example below:
 
 
 
<pre>
 
def onMouseMoved(observer,eventid): 
 
  ras=[0,0,0]
 
  crosshairNode.GetCursorPositionRAS(ras)
 
  print(ras)
 
 
 
crosshairNode=slicer.util.getNode('Crosshair')
 
crosshairNode.AddObserver(slicer.vtkMRMLCrosshairNode.CursorPositionModifiedEvent, onMouseMoved)
 
</pre>
 
 
 
== Get DataProbe text ==
 
 
 
You can get the mouse location in pixel coordinates along with the pixel value at the mouse by hitting the '.' (period) key in a slice view after pasting in the following code.
 
 
 
<pre>
 
def printDataProbe():
 
  infoWidget = slicer.modules.DataProbeInstance.infoWidget
 
  for layer in ('B', 'F', 'L'):
 
    print(infoWidget.layerNames[layer].text, infoWidget.layerIJKs[layer].text, infoWidget.layerValues[layer].text)
 
 
 
s = qt.QShortcut(qt.QKeySequence('.'), mainWindow())
 
s.connect('activated()', printDataProbe)
 
</pre>
 
 
 
== Thick slab reconstruction and maximum/minimum intensity volume projections ==
 
 
 
Set up 'red' slice viewer to show thick slab reconstructed from 3 slices:
 
<pre>
 
sliceNode = slicer.mrmlScene.GetNodeByID('vtkMRMLSliceNodeRed')
 
appLogic = slicer.app.applicationLogic()
 
sliceLogic = appLogic.GetSliceLogic(sliceNode)
 
sliceLayerLogic = sliceLogic.GetBackgroundLayer()
 
reslice = sliceLayerLogic.GetReslice()
 
reslice.SetSlabModeToMean()
 
reslice.SetSlabNumberOfSlices(10) # mean of 10 slices will computed
 
reslice.SetSlabSliceSpacingFraction(0.3) # spacing between each slice is 0.3 pixel (total 10 * 0.3 = 3 pixel neighborhood)
 
sliceNode.Modified()
 
</pre>
 
 
 
Set up 'red' slice viewer to show maximum intensity projection (MIP):
 
<pre>
 
sliceNode = slicer.mrmlScene.GetNodeByID('vtkMRMLSliceNodeRed')
 
appLogic = slicer.app.applicationLogic()
 
sliceLogic = appLogic.GetSliceLogic(sliceNode)
 
sliceLayerLogic = sliceLogic.GetBackgroundLayer()
 
reslice = sliceLayerLogic.GetReslice()
 
reslice.SetSlabModeToMax()
 
reslice.SetSlabNumberOfSlices(600) # use a large number of slices (600) to cover the entire volume
 
reslice.SetSlabSliceSpacingFraction(0.5) # spacing between slices are 0.5 pixel (supersampling is useful to reduce interpolation artifacts)
 
sliceNode.Modified()
 
</pre>
 
 
 
The projected image is available in a ''vtkImageData'' object by calling ''reslice.GetOutput()''.
 
 
 
== Change default file type for nodes (that have never been saved yet) ==
 
Default node can be specified that will be used as a basis of all new storage nodes. This can be used for setting default file extension. For example, change file format to STL for model nodes:
 
<pre>
 
defaultModelStorageNode = slicer.vtkMRMLModelStorageNode()
 
defaultModelStorageNode.SetDefaultWriteFileExtension('stl')
 
slicer.mrmlScene.AddDefaultNode(defaultModelStorageNode)
 
</pre>
 
 
 
To permanently change default file extension on your computer, copy-paste the code above into your application startup script (you can find its location in menu: Edit / Application settings / General / Application startup script).
 
 
 
== Change file type for saving for all volumes (with already existing storage nodes) ==
 
 
 
If it is not necessary to preserve file paths then the simplest is to configure default storage node (as shown in the example above), then delete all existing storage nodes. When save dialog is opened, default storage nodes will be recreated.
 
 
 
<pre>
 
# Delete existing model storage nodes so that they will be recreated with default settings
 
existingModelStorageNodes = slicer.util.getNodesByClass('vtkMRMLModelStorageNode')
 
for modelStorageNode in existingModelStorageNodes:
 
  slicer.mrmlScene.RemoveNode(modelStorageNode)
 
</pre>
 
 
 
To update existing storage nodes to use new file extension (but keep all other parameters unchanged) you can use this approach (example is for volume storage):
 
 
 
<pre>
 
requiredFileExtension = '.nia'
 
originalFileExtension = '.nrrd'
 
volumeNodes = slicer.util.getNodesByClass('vtkMRMLScalarVolumeNode')
 
for volumeNode in volumeNodes:
 
  volumeStorageNode = volumeNode.GetStorageNode()
 
  if not volumeStorageNode:
 
    volumeNode.AddDefaultStorageNode()
 
    volumeStorageNode = volumeNode.GetStorageNode()
 
    volumeStorageNode.SetFileName(volumeNode.GetName()+requiredFileExtension)
 
  else:
 
    volumeStorageNode.SetFileName(volumeStorageNode.GetFileName().replace(originalFileExtension, requiredFileExtension))
 
</pre>
 
 
 
To set all volume nodes to save uncompressed by default (add this to .slicerrc.py so it takes effect for the whole session):
 
<pre>
 
#set the default volume storage to not compress by default
 
defaultVolumeStorageNode = slicer.vtkMRMLVolumeArchetypeStorageNode()
 
defaultVolumeStorageNode.SetUseCompression(0)
 
slicer.mrmlScene.AddDefaultNode(defaultVolumeStorageNode)
 
logging.info("Volume nodes will be stored uncompressed by default")
 
</pre>
 
 
 
Same thing as above, but applied to all  segmentations instead of volumes:
 
<pre>
 
#set the default volume storage to not compress by default
 
defaultVolumeStorageNode = slicer.vtkMRMLSegmentationStorageNode()
 
defaultVolumeStorageNode.SetUseCompression(0)
 
slicer.mrmlScene.AddDefaultNode(defaultVolumeStorageNode)
 
logging.info("Segmentation nodes will be stored uncompressed
 
</pre>
 
 
 
== Segmentations ==
 
 
 
=== Create a segmentation from a labelmap volume and display in 3D ===
 
 
 
<pre>
 
labelmapVolumeNode = getNode('label')
 
seg = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLSegmentationNode')
 
slicer.modules.segmentations.logic().ImportLabelmapToSegmentationNode(labelmapVolumeNode, seg)
 
seg.CreateClosedSurfaceRepresentation()
 
slicer.mrmlScene.RemoveNode(labelmapVolumeNode)
 
</pre>
 
 
 
The last line is optional. It removes the original labelmap volume so that the same information is not shown twice.
 
 
 
=== Export labelmap node from segmentation node ===
 
 
 
<pre>
 
seg = getNode('Segmentation')
 
labelmapVolumeNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLLabelMapVolumeNode')
 
slicer.modules.segmentations.logic().ExportAllSegmentsToLabelmapNode(seg, labelmapVolumeNode)
 
</pre>
 
 
 
=== Export model nodes from segmentation node ===
 
 
 
<pre>
 
seg = getNode('Segmentation')
 
exportedModelsNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLModelHierarchyNode')
 
slicer.modules.segmentations.logic().ExportAllSegmentsToModelHierarchy(seg, exportedModelsNode)
 
</pre>
 
 
 
=== Show a segmentation in 3D ===
 
Segmentation can only be shown in 3D if closed surface representation (or other 3D-displayable representation) is available. To create closed surface representation:
 
<pre>
 
segmentation.CreateClosedSurfaceRepresentation()
 
</pre>
 
 
 
=== Get a representation of a segment ===
 
Access binary labelmap stored in a segmentation node (without exporting it to a volume node) - if it does not exist, it will return None:
 
<pre>
 
image = segmentationNode.GetBinaryLabelmapRepresentation(segmentID)
 
</pre>
 
Get closed surface, if it does not exist, it will return None:
 
<pre>
 
polydata = segmentationNode.GetClosedSurfaceRepresentation(segmentID)
 
</pre>
 
Get binary labelmap representation. If it does not exist then it will be created for that single segment. Applies parent transforms by default (if not desired, another argument needs to be added to the end: false):
 
<pre>
 
import vtkSegmentationCorePython as vtkSegmentationCore
 
outputOrientedImageData = vtkSegmentationCore.vtkOrientedImageData()
 
slicer.vtkSlicerSegmentationsModuleLogic.GetSegmentBinaryLabelmapRepresentation(segmentationNode, segmentID, outputOrientedImageData)
 
</pre>
 
Same as above, for closed surface representation:
 
<pre>
 
outputPolyData = vtk.vtkPolyData()
 
slicer.vtkSlicerSegmentationsModuleLogic.GetSegmentClosedSurfaceRepresentation(segmentationNode, segmentID, outputPolyData)
 
</pre>
 
 
 
=== Convert all segments using default path and conversion parameters ===
 
<pre>
 
segmentationNode.CreateBinaryLabelmapRepresentation()
 
</pre>
 
 
 
=== Convert all segments using custom path or conversion parameters ===
 
Change reference image geometry parameter based on an existing referenceImageData image:
 
<pre>
 
import vtkSegmentationCorePython as vtkSegmentationCore
 
referenceGeometry = vtkSegmentationCore.vtkSegmentationConverter.SerializeImageGeometry(referenceImageData)
 
segmentation.SetConversionParameter(vtkSegmentationCore.vtkSegmentationConverter.GetReferenceImageGeometryParameterName(), referenceGeometry)
 
</pre>
 
 
 
=== Re-convert using a modified conversion parameter ===
 
Changing smoothing factor for closed surface generation:
 
<pre>
 
import vtkSegmentationCorePython as vtkSegmentationCore
 
segmentation = getNode('Segmentation').GetSegmentation()
 
 
 
# Turn of surface smoothing
 
segmentation.SetConversionParameter('Smoothing factor','0.0')
 
 
 
# Recreate representation using modified parameters (and default conversion path)
 
segmentation.RemoveRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName())
 
segmentation.CreateRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName())
 
</pre>
 
 
 
=== Get centroid of a segment in world (RAS) coordinates ===
 
 
 
This example shows how to get centroid of a segment in world coordinates and show that position in all slice views.
 
 
 
<pre>
 
segmentationNode = getNode('Segmentation')
 
segmentId = 'Segment_1'
 
 
 
# Get array voxel coordinates
 
import numpy as np
 
seg=arrayFromSegment(segmentation_node, segmentId)
 
# numpy array has voxel coordinates in reverse order (KJI instead of IJK)
 
# and the array is cropped to minimum size in the segmentation
 
mean_KjiCropped = [coords.mean() for coords in np.nonzero(seg)]
 
 
 
# Get segmentation voxel coordinates
 
segImage = segmentationNode.GetBinaryLabelmapRepresentation(segmentId)
 
segImageExtent = segImage.GetExtent()
 
# origin of the array in voxel coordinates is determined by the start extent
 
mean_Ijk = [mean_KjiCropped[2], mean_KjiCropped[1], mean_KjiCropped[0]] + np.array([segImageExtent[0], segImageExtent[2], segImageExtent[4]])
 
 
 
# Get segmentation physical coordinates
 
ijkToWorld = vtk.vtkMatrix4x4()
 
segImage.GetImageToWorldMatrix(ijkToWorld)
 
mean_World = [0, 0, 0, 1]
 
ijkToRas.MultiplyPoint(np.append(mean_Ijk,1.0), mean_World)
 
mean_World = mean_World[0:3]
 
 
 
# If segmentation node is transformed, apply that transform to get RAS coordinates
 
transformWorldToRas = vtk.vtkGeneralTransform()
 
slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(segmentationNode.GetParentTransformNode(), None, transformWorldToRas)
 
mean_Ras = transformWorldToRas.TransformPoint(mean_World)
 
 
 
# Show mean position value and jump to it in all slice viewers
 
print(mean_Ras)
 
slicer.modules.markups.logic().JumpSlicesToLocation(mean_Ras[0], mean_Ras[1], mean_Ras[2], True)
 
</pre>
 
 
 
=== How to run segment editor effects from a script ===
 
 
 
Editor effects are complex because they need to handle changing master volumes, undo/redo, masking operations, etc. Therefore, instead of using a segment editor effect, it is simpler to run the underlying filters directly from script.
 
 
 
This example demonstrates how to use Segment editor effects (without GUI, using qMRMLSegmentEditorWidget):
 
 
 
* [https://gist.github.com/lassoan/2d5a5b73645f65a5eb6f8d5f97abf31b brain tumor segmentation using grow from seeds effect]
 
* [https://gist.github.com/lassoan/1673b25d8e7913cbc245b4f09ed853f9 skin surface extraction using thresholding and smoothing]
 
* [https://gist.github.com/lassoan/2f5071c562108dac8efe277c78f2620f mask a volume with segments and compute histogram for each region]
 
 
 
This example shows how to perform operations on segmentations using VTK filters:
 
* [https://gist.github.com/lassoan/7c94c334653010696b2bf96abc0ac8e7 brain tumor segmentation using grow from seeds effect]
 
 
 
== Accessing views, renderers, and cameras ==
 
 
 
Iterate through all 3D views in current layout:
 
 
 
<pre>
 
layoutManager = slicer.app.layoutManager()
 
for threeDViewIndex in range(layoutManager.threeDViewCount) :
 
  view = layoutManager.threeDWidget(threeDViewIndex).threeDView()
 
  threeDViewNode = view.mrmlViewNode()
 
  cameraNode = slicer.modules.cameras.logic().GetViewActiveCameraNode(threeDViewNode)
 
  print('View node for 3D widget ' + str(threeDViewIndex))
 
  print('  Name: ' + threeDViewNode .GetName())
 
  print('  ID: ' + threeDViewNode .GetID())
 
  print('  Camera ID: ' + cameraNode.GetID())
 
</pre>
 
 
 
Iterate through all slice views in current layout:
 
 
 
<pre>
 
layoutManager = slicer.app.layoutManager()
 
for sliceViewName in layoutManager.sliceViewNames():
 
  view = layoutManager.sliceWidget(sliceViewName).sliceView()
 
  sliceNode = view.mrmlSliceNode()
 
  sliceLogic = slicer.app.applicationLogic().GetSliceLogic(sliceNode)
 
  compositeNode = sliceLogic.GetSliceCompositeNode()
 
  print('Slice view ' + str(sliceViewName))
 
  print('  Name: ' + sliceNode.GetName())
 
  print('  ID: ' + sliceNode.GetID())
 
  print('  Background volume: {0}'.format(compositeNode.GetBackgroundVolumeID()))
 
  print('  Foreground volume: {0} (opacity: {1})'.format(compositeNode.GetForegroundVolumeID(), compositeNode.GetForegroundOpacity()))
 
  print('  Label volume: {0} (opacity: {1})'.format(compositeNode.GetLabelVolumeID(), compositeNode.GetLabelOpacity()))
 
</pre>
 
 
 
For low-level manipulation of views, it is possible to access VTK render windows, renderers and cameras of views in the current layout.
 
<pre>
 
renderWindow = view.renderWindow()
 
renderers = renderWindow.GetRenderers()
 
renderer = renderers.GetItemAsObject(0)
 
camera = cameraNode.GetCamera()
 
</pre>
 
 
 
== Change 3D view background color ==
 
 
 
<pre>
 
renderWindow = slicer.app.layoutManager().threeDWidget(0).threeDView().renderWindow()
 
renderer = renderWindow.GetRenderers().GetFirstRenderer()
 
renderer.SetBackground(1,0,0)
 
renderer.SetBackground2(1,0,0)
 
renderWindow.Render()
 
</pre>
 
 
 
== Subject hierarchy ==
 
==== Get the pseudo-singleton subject hierarchy node ====
 
It manages the whole hierarchy and provides functions to access and manipulate
 
  shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
 
 
 
==== Create subject hierarchy item ====
 
  # If it is for a data node, it is automatically created, but the create function can be used to set parent:
 
  shNode.CreateItem(parentItemID, dataNode)
 
  # If it is a hierarchy item without a data node, then the create function must be used:
 
  shNode.CreateSubjectItem(parentItemID, name)
 
  shNode.CreateFolderItem(parentItemID, name)
 
  shNode.CreateHierarchyItem(parentItemID, name, level) # Advanced method to set level attribute manually (usually subject, study, or folder, but it can be a virtual branch for example)
 
 
 
==== Get subject hierarchy item ====
 
Items in subject hierarchy are uniquely identified by integer IDs
 
  # Get scene item ID first because it is the root item:
 
  sceneItemID = shNode.GetSceneItemID()
 
  # Get direct child by name
 
  subjectItemID = shNode.GetItemChildWithName(sceneItemID, 'Subject_1')
 
  # Get item for data node
 
  itemID = shNode.GetItemByDataNode(dataNode)
 
  # Get item by UID (such as DICOM)
 
  itemID = shNode.GetItemByUID(slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMUIDName(), seriesInstanceUid)
 
  itemID = shNode.GetItemByUIDList(slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMInstanceUIDName(), instanceUID)
 
  # Invalid item ID for checking validity of a given ID (most functions return the invalid ID when item is not found)
 
  invalidItemID = slicer.vtkMRMLSubjectHierarchyNode.GetInvalidItemID()
 
 
 
==== Traverse children of a subject hierarchy item ====
 
  children = vtk.vtkIdList()
 
  shNode.GetItemChildren(parent, children)
 
  for i in xrange(children.GetNumberOfIds()):
 
    child = children.GetId(i)
 
    ...
 
 
 
==== Manipulate subject hierarchy item ====
 
Instead of node operations on the individual subject hierarchy nodes, item operations are performed on the one subject hierarchy node.
 
  # Set item name
 
  shNode.SetItemName(itemID, 'NewName')
 
  # Set item parent (reparent)
 
  shNode.SetItemParent(itemID, newParentItemID)
 
  # Set visibility of data nodes associated to items in a branch (or a leaf item)
 
  shNode.SetDisplayVisibilityForBranch(itemID, 1)
 
 
 
==== Filter items in TreeView or ComboBox ====
 
Displayed items can be filtered using ''setAttributeFilter'' method. An example of the usage can be found in the  [https://github.com/Slicer/Slicer/blob/e66e3b08e35384526528e6ae678e9ec9f079f286/Applications/SlicerApp/Testing/Python/SubjectHierarchyGenericSelfTest.py#L352-L360 unit test]. Modified version here:
 
    print(shTreeView.displayedItemCount()) # 5
 
    shTreeView.setAttributeFilter('DICOM.Modality') # Nodes must have this attribute
 
    print(shTreeView.displayedItemCount()) # 3
 
    shTreeView.setAttributeFilter('DICOM.Modality','CT') # Have attribute and equal 'CT'
 
    print(shTreeView.displayedItemCount()) # 1
 
    shTreeView.removeAttributeFilter()
 
    print(shTreeView.displayedItemCount()) # 5
 
 
 
=== Listen to subject hierarchy item events ===
 
The subject hierarchy node sends the node item id as calldata. Item IDs are vtkIdType, which are NOT vtkObjects. You need to use vtk.calldata_type(vtk.VTK_LONG) (otherwise the application crashes).
 
 
 
  class MyListenerClass(VTKObservationMixin):
 
    def __init__(self):
 
      VTKObservationMixin.__init__(self)
 
     
 
      shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
 
      self.addObserver(shNode, shNode.SubjectHierarchyItemModifiedEvent, self.shItemModifiedEvent)
 
   
 
    @vtk.calldata_type(vtk.VTK_LONG)
 
    def shItemModifiedEvent(self, caller, eventId, callData):
 
      print("SH Node modified")
 
      print("SH item ID: {0}".format(callData))
 
 
 
== Plotting ==
 
 
 
=== Create histogram plot of a volume ===
 
 
 
<pre>
 
# Get a volume from SampleData
 
import SampleData
 
volumeNode = SampleData.SampleDataLogic().downloadMRHead()
 
 
 
# Compute histogram values
 
import numpy as np
 
histogram = np.histogram(arrayFromVolume(volumeNode), bins=50)
 
 
 
# Save results to a new table node
 
tableNode=slicer.mrmlScene.AddNewNodeByClass("vtkMRMLTableNode")
 
updateTableFromArray(tableNode, histogram)
 
tableNode.GetTable().GetColumn(0).SetName("Count")
 
tableNode.GetTable().GetColumn(1).SetName("Intensity")
 
 
 
# Create plot
 
plotSeriesNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLPlotSeriesNode", volumeNode.GetName() + ' histogram')
 
plotSeriesNode.SetAndObserveTableNodeID(tableNode.GetID())
 
plotSeriesNode.SetXColumnName("Intensity")
 
plotSeriesNode.SetYColumnName("Count")
 
plotSeriesNode.SetPlotType(plotSeriesNode.PlotTypeScatterBar)
 
plotSeriesNode.SetColor(0, 0.6, 1.0)
 
 
 
# Create chart and add plot
 
plotChartNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLPlotChartNode")
 
plotChartNode.AddAndObservePlotSeriesNodeID(plotSeriesNode.GetID())
 
plotChartNode.YAxisRangeAutoOff()
 
plotChartNode.SetYAxisRange(0, 500000)
 
 
 
# Show plot in layout
 
slicer.modules.plots.logic().ShowChartInLayout(plotChartNode)
 
 
 
</pre>
 
 
 
== Execute external applications ==
 
 
 
How to run external applications from Slicer.
 
 
 
=== Run process in default environment ===
 
 
 
When a process is launched from Slicer then by default Slicer's ITK, VTK, Qt, etc. libraries are used. If an external application has its own version of these libraries, then the application is expected to crash. To prevent crashing, the application must be run in the environment where Slicer started up (without all Slicer-specific library paths). This startup environment can be retrieved using ''slicer.util.startupEnvironment()''.
 
 
 
Example: run Python3 script from Slicer:
 
 
 
<pre>
 
command_to_execute = ["/usr/bin/python3", "-c", "print('hola')"]
 
from subprocess import check_output
 
check_output(
 
  command_to_execute,
 
  env=slicer.util.startupEnvironment()
 
  )
 
</pre>
 
 
 
will output:
 
<pre>
 
'hola\n'
 
</pre>
 
 
 
On some systems, ''shell=True'' must be specified as well.
 

Latest revision as of 04:06, 24 April 2021

Home < Documentation < Nightly < ScriptRepository