Difference between revisions of "Documentation/Nightly/Modules/PlusRemote"

From Slicer Wiki
Jump to: navigation, search
Line 126: Line 126:
 
* [http://slicer.org/doc/html/classvtkMRMLAnnotationROINode.html vtkMRMLAnnotationROINode] controls the ROI widget
 
* [http://slicer.org/doc/html/classvtkMRMLAnnotationROINode.html vtkMRMLAnnotationROINode] controls the ROI widget
 
* [http://slicer.org/doc/html/classvtkMRMLAnnotationTextNode.html vtkMRMLAnnotationTextNode] represents the command reply text
 
* [http://slicer.org/doc/html/classvtkMRMLAnnotationTextNode.html vtkMRMLAnnotationTextNode] represents the command reply text
 +
 +
===Module===
 +
* '''Use of base classes:'''
 +
Use of base classes for Python scripted modules to simplify the code. See module [https://github.com/Slicer/Slicer/blob/master/Extensions/Testing/ScriptedLoadableExtensionTemplate/ScriptedLoadableModuleTemplate/ScriptedLoadableModuleTemplate.py template] and [https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py source code] for base classes.
  
 
===Parameters set===
 
===Parameters set===

Revision as of 20:40, 25 August 2014

Home < Documentation < Nightly < Modules < PlusRemote


For the latest Slicer documentation, visit the read-the-docs.


Introduction and Acknowledgements


Author: Franklin King (PerkLab, Queen's University)
Contributors: Tamas Ungi (PerkLab, Queen's University), Andras Lasso (PerkLab, Queen's University), Amélie Meyer (Télécom Physique Strasbourg, Université de Strasbourg)
Contact: Tamas Ungi, <email>ungi@cs.queensu.ca</email>

Cancer Care Ontario  

Module Description

Convenience module for sending commands through OpenIGTLink Remote to PLUS. Requires OpenIGTLink Remote module available in the SlicerIGT extension. See PLUS user guide and manual for more information about PLUS server commands.

The module provides the following features:

  • Recording of 2D tracked ultrasound frames
  • Volume reconstruction of recorded frames
  • Live ultrasound volume reconstruction using scout scanning
  • Sending Transform Update command to PLUS server

Use Cases

Some ultrasound-guided interventions require a general view of an entire area followed by more detailed images of a specific region. One example is spinal injection procedures. The problem is that, for elderly patients, the epidural area is not directly visible in ultrasound images because the intervertebral space is often compressed or deformed. In this case, physicians use four vertebrae landmarks to estimate the position of the target. Visualization can be improved using reconstructed 3D ultrasound volumes, but often there is not enough time for high resolution reconstruction of a large area during interventions.

The live ultrasound volume reconstruction workflow using scout scanning is then adapted for these procedures. The two-step method allows user to perform quick low resolution scout scan followed by high resolution volume reconstruction of a selected region of interest, in real-time. Low resolution scout scanning offers visualization of the global area and selection of the region of interest, and then high resolution reconstruction provides a good image quality of the targeted region.

Tutorials

The SlicerIGT website provides a tutorial for ultrasound volume reconstruction in Slicer using Plus Remote (U35).

Panels and their use

Plus Remote panel
Plus Remote panel with advanced parameters
  • Parameters sets: saving of all parameters
    • Create a new parameter set to save your parameters
    • Select previously saved ones
    • Default parameter set is selected when entering the module

This is useful when user knows optimal parameters for a specific procedure. The parameters set is saved when saving the scene and is reloaded when opening a saved scene.


  • Parameters: parameters for communication with PLUS server, recording and reconstruction
    • OpenIGTLinkConnector
    • Capture Device ID
    • Volume Reconstructor ID

These parameters are essential. Therefore all commands are disabled when these parameters are not set. List of available OpenIGTLinkConnector is available once you created connections in OpenIGTLinkIF module. When a connector is selected, capture device ID and volume reconstruction ID lists are automatically filled.


  • Recording:
    • Start and stop recording
    • Advanced parameters:
      • Modify default output filename "Recording.mha"
      • Add timestamp (YYYYMMDD_HHMMSS) to filename


  • Offline reconstruction of recorded volume:
    • Offline reconstruction
    • Advanced parameters:
      • Spacing (default value is 3mm)
      • Output volume device name
      • Volume to reconstruct filename (automatically filled when user record a file, but user can also edit this line and reconstruct previously saved files)
      • Show results on completion (display reconstructed volume in the green, yellow and 3D views)


  • Scout scan and live reconstruction:
    • Start and stop scout scan
    • Advanced parameters:
      • Spacing (default value is 3mm as we expect a low resolution scout scan)
      • Modify default output filename "ScoutScanRecording.mha"
      • Add timestamp (YYYYMMDD_HHMMSS) to filename
      • Show results on completion (display reconstructed volume in the green, yellow and 3D views)

Scout scan button record a file and automatically performs the volume reconstruction once the recording is over. The result of scout scan volume reconstruction is saved in "scoutFile.mha"

    • Start and stop live volume reconstruction:
    • Display and modify ROI using eye icon (initialized to fit scout scan volume)
    • Spacing (default value is 1mm for high resolution reconstruction)
    • Advanced parameters:
      • Output volume device name (read only)
      • Modify default output filename "LiveReconstructedVolume.mha"
      • Add timestamp (YYYYMMDD_HHMMSS) to filename
      • Display volume extent values (updated when clicking on scout scan or live reconstruction command buttons)
      • Display snapshots during live volume reconstruction (if 0, no snapshots)
      • Show results on completion (display reconstructed volume in the green, yellow and 3D views)


  • Transform Update: Update transform from what is set in the PLUS config file; Requires name of transform node to match an existing transform in the PLUS config file
  • Reply: Feedback message from PLUS server (also visible when moving the mouse on blue information icons)

Similar Modules

N/A

References

Information for Developers

Limitations

  • Extent values are updated only when clicking on scout scan or live reconstruction command buttons, not in "real-time" when user modifies the ROI
  • Foreground volume contrast and brightness is set programmatically using scout scan values, because it cannot be modified in Slicer views

Key nodes and classes

Module

  • Use of base classes:

Use of base classes for Python scripted modules to simplify the code. See module template and source code for base classes.

Parameters set

  • Parameters set observer:

Need to add an observer to save the new parameter value when user changes it, or to update the GUI when user select another parameter set.

self.parameterNodeObserver = self.parameterNode.AddObserver('currentNodeChanged(vtkMRMLNode*)', functionToCall)
  • Saving parameters:
self.parameterValueList = {'parameterName': GUIObject, 'ScoutScanSpacing': self.outputVolumeSpacingBox,...}
for parameter in self.parameterValueList:
  if self.parameterNode.GetParameter(parameter):
    #Block Signal to avoid call of updateParameterNodeFromGui when parameter is set programmatically - we only want to catch when user change values
      self.parameterValueList[parameter].blockSignals(True)
      self.parameterValueList[parameter].setValue(float(self.parameterNode.GetParameter(parameter)))
      self.parameterValueList[parameter].blockSignals(False)

For the ROI node it has to be done using reference:

self.roiNode = self.parameterNode.GetNthNodeReference('ROI', 0)
  • Getting a saved parameter and updating the GUI when selecting a parameter set:
self.parametersList = {'parameterNode': GUIObject.currentObject, 'ScoutScanSpacing': self.outputVolumeSpacingBox.value,...}
for parameter in self.parametersList:
  self.parameterNode.SetParameter(parameter, str(self.parametersList[parameter]))

For ROI node:

roiNodeID = self.roiNode.GetID()
self.parameterNode.SetNthNodeReferenceID('ROI', 0, roiNodeID)

OpenIGTLinkConnector

  • OpenIGTLinkConnector observer:

Need to add observers to be informed when the connector is connected/disconnected to enable/disable command buttons.

events = [[slicer.vtkMRMLIGTLConnectorNode.ConnectedEvent, self.onConnectorNodeConnected], [slicer.vtkMRMLIGTLConnectorNode.DisconnectedEvent, self.onConnectorNodeDisconnected]]
for tagEventHandler in events:
  connectorNodeObserverTag = self.connectorNode.AddObserver(tagEventHandler[0], tagEventHandler[1])
  self.connectorNodeObserverTagList.append(connectorNodeObserverTag)

ROI widget

  • ROI initialization:

ROI widget is created after the scout scan and it is initialized to fit the scout scan reconstructed volume, which is in the RAS coordinate system. RAS origin is the corner of the cube whereas ROI origin has to be the center of the cube. Radius is half of the distance from one side to another side of the cube.

reconstructedVolumeNode.GetRASBounds(bounds)
for i in range(0,5,2):
  roiCenterInit[i] = (bounds[i+1] + bounds[i])/2
  roiRadiusInit[i] = (bounds[i+1] - bounds[i])/2
...
  self.roiNode = slicer.vtkMRMLAnnotationROINode()
  self.roiNode.SetXYZ(roiCenterInit[0], roiCenterInit[2], roiCenterInit[4])
  self.roiNode.SetRadiusXYZ(roiRadiusInit[0], roiRadiusInit[2], roiRadiusInit[4])
  self.roiNode.Initialize(slicer.mrmlScene)
  • Update extent values:

Need to update volume extent values each time user modifies the ROI, as we want the volume to fit ROI for live reconstruction. Origin of the cube is computed using the ROI values. For each direction extent value is twice the radius divided by the spacing, as radius is given in millimetres and extent in pixels

self.roiNode.GetXYZ(roiCenter)
self.roiNode.GetRadiusXYZ(roiRadius)
for i in range(0,len(roiCenter)):
  outputOrigin[i] = roiCenter[i] - roiRadius[i]
#Radius in mm, extent in pixel
self.outputExtentValue = [0, int((2*roiRadius[0])/self.outputSpacingLiveReconstructionBox.value), 0, int((2*roiRadius[1])/self.outputSpacingLiveReconstructionBox.value), 0, int((2*roiRadius[2])/self.outputSpacingLiveReconstructionBox.value)]

Multiple commands processing

  • Hash table for multiple commands processing:

Need to be able to send multiple commands and call functions when the reply is received. Use of hash table to store the command ID and the associated function to call.

self.commandToMethodHashtable[commandId]={'responseCallback': responseCallback, 'connectorNodeId': connectorNodeId, 'remainingTime': self.defaultCommandTimeoutSec/self.timerIntervalSec}
...
for commandId in self.commandToMethodHashtable.keys():
...
if textNode or remainingTime<=0:
  # We received a response or timed out waiting for a response
  commandToMethodItem = self.commandToMethodHashtable.pop(commandId)
  responseCallback = commandToMethodItem['responseCallback']
  responseCallback(commandId, textNode)