Segmentation of Mitral valve

You can reconstruct volume from these 2D ultrasound rotational sweep very similarly to the cine MRI above.

Result:

Script that reorganizes the volume into a sequence of frames, adds position&orientation information to each frame, and reconstructs a volume:

# Input 3D volume that contains each frame as a slice
inputFrameVolumeNode = slicer.mrmlScene.GetFirstNodeByClass('vtkMRMLVolumeNode')
imageSpacingMm = 0.2  # this needs to be replaced with the actual spacing
outputSpacingMm = imageSpacingMm * 1.0  # Make the reconstructed volume spacing larger to reduce memory usage and make computations faster

# Get volume size
inputFrameVolume = inputFrameVolumeNode.GetImageData()
extent = inputFrameVolume.GetExtent()
numberOfFrames = extent[5]-extent[4]+1

# Set up frame geometry and rotation
centerOfRotationIJK = [(extent[0]+extent[1])/2.0, extent[2], 0]
rotationAxis = [0, 1, 0]
rotationDegreesPerFrame = 180.0/numberOfFrames

# Convert RGB/RGBA volume to grayscale volume
if inputFrameVolume.GetNumberOfScalarComponents() > 1:
    componentToExtract = 0
    print(f"Using scalar component {componentToExtract} of the image")
    extract = vtk.vtkImageExtractComponents()
    extract.SetInputData(inputFrameVolume)
    extract.SetComponents(componentToExtract)
    extract.Update()
    inputFrameVolume = extract.GetOutput()

# Create an image sequence that contains the frames as a time sequence
# and also contains position/orientation for each frame.
outputSequenceNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSequenceNode", inputFrameVolumeNode.GetName()+"_frames")
outputSequenceNode.SetIndexName("frame")
outputSequenceNode.SetIndexUnit("")

# This temporary node will be used to add frames to the image sequence
tempFrameVolumeNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLScalarVolumeNode")

for frameIndex in range(numberOfFrames):
    # set current image from multiframe
    crop = vtk.vtkImageClip()
    crop.SetInputData(inputFrameVolume)
    crop.SetOutputWholeExtent(extent[0], extent[1], extent[2], extent[3], extent[4] + frameIndex, extent[4] + frameIndex)
    crop.ClipDataOn()
    crop.Update()
    croppedOutput = crop.GetOutput()
    croppedOutput.SetExtent(extent[0], extent[1], extent[2], extent[3], 0, 0)
    croppedOutput.SetOrigin(0.0, 0.0, 0.0)
    tempFrameVolumeNode.SetAndObserveImageData(croppedOutput)
    # set current transform
    ijkToRasTransform = vtk.vtkTransform()
    ijkToRasTransform.Scale(imageSpacingMm, imageSpacingMm, imageSpacingMm)
    ijkToRasTransform.RotateWXYZ(frameIndex * rotationDegreesPerFrame, *rotationAxis)
    ijkToRasTransform.Translate(-centerOfRotationIJK[0], -centerOfRotationIJK[1], -centerOfRotationIJK[2])
    tempFrameVolumeNode.SetIJKToRASMatrix(ijkToRasTransform.GetMatrix())
    # add to sequence
    added = outputSequenceNode.SetDataNodeAtValue(tempFrameVolumeNode, str(frameIndex))

slicer.mrmlScene.RemoveNode(tempFrameVolumeNode)

# Create a sequence browser node for the reconstructed volume sequence
outputSequenceBrowserNode = slicer.mrmlScene.AddNewNodeByClass(
    'vtkMRMLSequenceBrowserNode', outputSequenceNode.GetName() + '_browser')
outputSequenceBrowserNode.AddSynchronizedSequenceNode(outputSequenceNode)
slicer.modules.sequences.logic().UpdateAllProxyNodes()  # ensure that proxy node is created
outputSequenceProxyNode = outputSequenceBrowserNode.GetProxyNode(outputSequenceNode)
slicer.util.setSliceViewerLayers(background=outputSequenceProxyNode)
slicer.modules.sequences.showSequenceBrowser(outputSequenceBrowserNode)

# Make slice view move with the image (just for visualization)
driver = slicer.modules.volumereslicedriver.logic()
redSliceNode = slicer.util.getFirstNodeByClassByName("vtkMRMLSliceNode", "Red")
driver.SetModeForSlice(driver.MODE_TRANSVERSE, redSliceNode)
driver.SetDriverForSlice(outputSequenceProxyNode.GetID(), redSliceNode)


# Reconstruct
volumeReconstructionNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLVolumeReconstructionNode")
volumeReconstructionNode.SetAndObserveInputSequenceBrowserNode(outputSequenceBrowserNode)
volumeReconstructionNode.SetAndObserveInputVolumeNode(outputSequenceProxyNode)
volumeReconstructionNode.SetOutputSpacing(outputSpacingMm, outputSpacingMm, outputSpacingMm)
volumeReconstructionNode.SetFillHoles(True)
slicer.modules.volumereconstruction.logic().ReconstructVolumeFromSequence(volumeReconstructionNode)
reconstructedVolume = volumeReconstructionNode.GetOutputVolumeNode()
reconstructedVolume.SetName(outputSequenceProxyNode.GetName()+"_recon")
roiNode = volumeReconstructionNode.GetInputROINode()
# Cleanup
slicer.mrmlScene.RemoveNode(volumeReconstructionNode)
# Show reconstruction result
roiNode.SetDisplayVisibility(False)
slicer.util.setSliceViewerLayers(background=reconstructedVolume,fit=True)
2 Likes