Documentation/Nightly/Modules/PlusRemote
For the latest Slicer documentation, visit the read-the-docs. |
Contents |
Introduction and Acknowledgements
| |||
|
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
- 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
- vtkMRMLScriptedModuleNode saves all parameters and get them back when selecting a parameter set
- vtkMRMLIGTLConnectorNode is OpenIGTLink connector node
- vtkMRMLScalarVolumeNode contains the resulting volume
- vtkMRMLScalarVolumeDisplayNode changes the display properties of the node
- vtkSlicerVolumeRenderingLogic to create the volume rendering display node
- vtkMRMLAnnotationROINode controls the ROI widget
- vtkMRMLAnnotationTextNode represents the command reply text
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 if self.roiNode: self.roiNode.SetXYZ(roiCenterInit[0], roiCenterInit[2], roiCenterInit[4]) self.roiNode.SetRadiusXYZ(roiRadiusInit[0], roiRadiusInit[2], roiRadiusInit[4]) else: 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)