def updateWindowLevelRectangle(self, sliceLayer, xyBounds):
    # conversion as done in LabelEffect.py:applyImageMask
    xlo, xhi, ylo, yhi, zlo, zhi = xyBounds
    if xlo == xhi or ylo == yhi: return
    sliceNode = sliceLayer.GetVolumeNode()
    sliceImage = sliceNode.GetImageData()
    if not sliceImage:
      return [0,0,0,0]
    xyToIJK = sliceLayer.GetXYToIJKTransform()
    tlIJK = xyToIJK.TransformPoint( (xlo, yhi, 0) )
    trIJK = xyToIJK.TransformPoint( (xhi, yhi, 0) )
    blIJK = xyToIJK.TransformPoint( (xlo, ylo, 0) )
    brIJK = xyToIJK.TransformPoint( (xhi, ylo, 0) )
    #
    # get the mask bounding box in ijk coordinates
    # - get the xy bounds
    # - transform to ijk
    # - clamp the bounds to the dimensions of the label image
    #

    # do the clamping of the four corners
    dims = sliceImage.GetDimensions()
    tl = [0,] * 3
    tr = [0,] * 3
    bl = [0,] * 3
    br = [0,] * 3
    corners = ((tlIJK, tl),(trIJK, tr),(blIJK, bl),(brIJK, br))
    for corner,clampedCorner in corners:
      for d in xrange(3):
        clamped = int(round(corner[d]))
        if clamped < 0: clamped = 0
        if clamped >= dims[d]: clamped = dims[d]-1
        clampedCorner[d] = clamped

    # calculate the statistics for the selected region
    clip = vtk.vtkImageClip()
    extentMin = [min(tl[0],min(tr[0],min(bl[0],br[0]))),min(tl[1],min(tr[1],min(bl[1],br[1]))),min(tl[2],min(tr[2],min(bl[2],br[2])))]
    extentMax = [max(tl[0],max(tr[0],max(bl[0],br[0]))),max(tl[1],max(tr[1],max(bl[1],br[1]))),max(tl[2],max(tr[2],max(bl[2],br[2])))]
    clip.SetOutputWholeExtent(extentMin[0],extentMax[0],extentMin[1],extentMax[1],extentMin[2],extentMax[2])
    if vtk.VTK_MAJOR_VERSION <= 5:
      clip.SetInput(sliceNode.GetImageData())
    else:
      clip.SetInputData(sliceNode.GetImageData())
    clip.ClipDataOn()
    clip.Update()

    stats = vtk.vtkImageHistogramStatistics()

    if vtk.VTK_MAJOR_VERSION <= 5:
      stats.SetInput(clip.GetOutput())
    else:
      stats.SetInputData(clip.GetOutput())
    stats.Update()

    minIntensity = stats.GetMinimum()
    maxIntensity = stats.GetMaximum()

    sliceNode.GetDisplayNode().SetAutoWindowLevel(False)
    sliceNode.GetDisplayNode().SetAutoWindowLevel(0)
    sliceNode.GetDisplayNode().SetWindowLevel(maxIntensity-minIntensity, (maxIntensity-minIntensity)/2.+minIntensity)
    sliceNode.GetDisplayNode().Modified()
    def addSequenceFromImageData(self, imageData, tempFrameVolume, filePath,
                                 name, singleFileInLoadable):

        # Rotate 180deg, otherwise the image would appear upside down
        ijkToRas = vtk.vtkMatrix4x4()
        ijkToRas.SetElement(0, 0, -1.0)
        ijkToRas.SetElement(1, 1, -1.0)
        tempFrameVolume.SetIJKToRASMatrix(ijkToRas)
        # z axis is time
        [spacingX, spacingY, frameTimeMsec] = imageData.GetSpacing()
        imageData.SetSpacing(1.0, 1.0, 1.0)
        tempFrameVolume.SetSpacing(spacingX, spacingY, 1.0)

        # Create new sequence
        outputSequenceNode = slicer.mrmlScene.AddNewNodeByClass(
            "vtkMRMLSequenceNode")

        # Get sequence name
        if singleFileInLoadable:
            outputSequenceNode.SetName(name)
        else:
            ds = dicom.read_file(filePath, stop_before_pixels=True)
            if hasattr(ds, 'PositionerPrimaryAngle') and hasattr(
                    ds, 'PositionerSecondaryAngle'):
                outputSequenceNode.SetName(
                    f'{name} ({ds.PositionerPrimaryAngle}/{ds.PositionerSecondaryAngle})'
                )
            else:
                outputSequenceNode.SetName(name)

        if frameTimeMsec == 1.0:
            # frame time is not found, set it to 1.0fps
            frameTime = 1
            outputSequenceNode.SetIndexName("frame")
            outputSequenceNode.SetIndexUnit("")
            playbackRateFps = 10
        else:
            # frame time is set, use it
            frameTime = frameTimeMsec * 0.001
            outputSequenceNode.SetIndexName("time")
            outputSequenceNode.SetIndexUnit("s")
            playbackRateFps = 1.0 / frameTime

        # Add frames to the sequence
        numberOfFrames = imageData.GetDimensions()[2]
        extent = imageData.GetExtent()
        numberOfFrames = extent[5] - extent[4] + 1
        for frame in range(numberOfFrames):
            # get current frame from multiframe
            crop = vtk.vtkImageClip()
            crop.SetInputData(imageData)
            crop.SetOutputWholeExtent(extent[0], extent[1], extent[2],
                                      extent[3], extent[4] + frame,
                                      extent[4] + frame)
            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)
            tempFrameVolume.SetAndObserveImageData(croppedOutput)
            # get timestamp
            if type(frameTime) == int:
                timeStampSec = str(frame * frameTime)
            else:
                timeStampSec = f"{frame * frameTime:.3f}"
            outputSequenceNode.SetDataNodeAtValue(tempFrameVolume,
                                                  timeStampSec)

        # Create storage node that allows saving node as nrrd
        outputSequenceStorageNode = slicer.vtkMRMLVolumeSequenceStorageNode()
        slicer.mrmlScene.AddNode(outputSequenceStorageNode)
        outputSequenceNode.SetAndObserveStorageNodeID(
            outputSequenceStorageNode.GetID())

        return outputSequenceNode, playbackRateFps
  def load(self,loadable):
    """Load the selection
    """

    outputSequenceNodes = []
    for filePath in loadable.files:

      import vtkITK
      if loadable.grayscale:
        reader = vtkITK.vtkITKArchetypeImageSeriesScalarReader()
      else:
        reader = vtkITK.vtkITKArchetypeImageSeriesVectorReaderFile()
      reader.SetArchetype(filePath)
      reader.AddFileName(filePath)
      reader.SetSingleFile(True)
      reader.SetOutputScalarTypeToNative()
      reader.SetDesiredCoordinateOrientationToNative()
      reader.SetUseNativeOriginOn()
      # GDCM is not particularly better in this than DCMTK, we just select one explicitly
      # so that we know which one is used
      reader.SetDICOMImageIOApproachToGDCM()
      reader.Update()
      if reader.GetErrorCode() != vtk.vtkErrorCode.NoError:
          errorString = vtk.vtkErrorCode.GetStringFromErrorCode(reader.GetErrorCode())
          raise ValueError("Could not read image {0} from file {1}. Error is: {2}".format(loadable.name, filePath, errorString))

      volume = reader.GetOutput()

      [spacingX, spacingY, frameTimeMsec] = volume.GetSpacing()

      volume.SetSpacing(1.0, 1.0, 1.0)

      outputSequenceNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSequenceNode")
      if frameTimeMsec == 1.0:
        # frame time is not found, set it to 1.0fps
        frameTime = 1
        outputSequenceNode.SetIndexName("frame")
        outputSequenceNode.SetIndexUnit("")
        playbackRateFps = 10
      else:
        frameTime = frameTimeMsec * 0.001
        outputSequenceNode.SetIndexName("time")
        outputSequenceNode.SetIndexUnit("s")
        playbackRateFps = 1.0/frameTime

      if loadable.grayscale:
        tempFrameVolume = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLScalarVolumeNode")
      else:
        tempFrameVolume = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLVectorVolumeNode")

      # Rotate 180deg, otherwise the image would appear upside down
      ijkToRAS = vtk.vtkMatrix4x4()
      ijkToRAS.SetElement(0,0,-1.0)
      ijkToRAS.SetElement(1,1,-1.0)
      tempFrameVolume.SetIJKToRASMatrix(ijkToRAS)

      tempFrameVolume.SetSpacing(spacingX, spacingY, 1.0)

      numberOfFrames = volume.GetDimensions()[2]
      extent = volume.GetExtent()
      numberOfFrames = extent[5]-extent[4]+1
      for frame in range(numberOfFrames):

        crop = vtk.vtkImageClip()
        crop.SetInputData(volume)
        crop.SetOutputWholeExtent(extent[0], extent[1], extent[2], extent[3], extent[4]+frame, extent[4]+frame)
        crop.ClipDataOn()
        crop.Update()
        croppedOutput = crop.GetOutput()
        croppedOutput.SetExtent(extent[0], extent[1], extent[2], extent[3], 0, 0)
        tempFrameVolume.SetAndObserveImageData(croppedOutput)
        if type(frameTime)==int:
          timeStampSec = str(frame * frameTime)
        else:
          timeStampSec = "{:.3f}".format(frame * frameTime)
        outputSequenceNode.SetDataNodeAtValue(tempFrameVolume, timeStampSec)

      slicer.mrmlScene.RemoveNode(tempFrameVolume)

      if len(loadable.files) == 1:
        outputSequenceNode.SetName(loadable.name)
      else:
        ds = dicom.read_file(filePath, stop_before_pixels=True)
        if hasattr(ds, 'PositionerPrimaryAngle') and hasattr(ds, 'PositionerSecondaryAngle'):
          outputSequenceNode.SetName('{0} ({1}/{2})'.format(loadable.name, ds.PositionerPrimaryAngle,ds.PositionerSecondaryAngle))
        else:
          outputSequenceNode.SetName(loadable.name)

      slicer.mrmlScene.AddNode(outputSequenceNode)

      # Create storage node that allows saving node as nrrd
      outputSequenceStorageNode = slicer.vtkMRMLVolumeSequenceStorageNode()
      slicer.mrmlScene.AddNode(outputSequenceStorageNode)
      outputSequenceNode.SetAndObserveStorageNodeID(outputSequenceStorageNode.GetID())

      outputSequenceNodes.append(outputSequenceNode)

    if not hasattr(loadable, 'createBrowserNode') or loadable.createBrowserNode:
      # Add a browser node and show the volume in the slice viewer for user convenience
      outputSequenceBrowserNode = slicer.vtkMRMLSequenceBrowserNode()
      outputSequenceBrowserNode.SetName(slicer.mrmlScene.GenerateUniqueName(loadable.name+' browser'))
      outputSequenceBrowserNode.SetPlaybackRateFps(playbackRateFps)
      slicer.mrmlScene.AddNode(outputSequenceBrowserNode)

      # Add all sequences to the sequence browser
      first = True
      for outputSequenceNode in outputSequenceNodes:
        #outputSequenceBrowserNode.SetAndObserveMasterSequenceNodeID(outputSequenceNode.GetID())
        outputSequenceBrowserNode.AddSynchronizedSequenceNode(outputSequenceNode)
        proxyVolumeNode = outputSequenceBrowserNode.GetProxyNode(outputSequenceNode)
        # create Subject hierarchy nodes for the loaded series
        self.addSeriesInSubjectHierarchy(loadable, proxyVolumeNode)

        if first:
          first = False
          # Automatically select the volume to display
          appLogic = slicer.app.applicationLogic()
          selNode = appLogic.GetSelectionNode()
          selNode.SetReferenceActiveVolumeID(proxyVolumeNode.GetID())
          appLogic.PropagateVolumeSelection()
          appLogic.FitSliceToAll()
          slicer.modules.sequences.setToolBarActiveBrowserNode(outputSequenceBrowserNode)

      # Show sequence browser toolbar
      slicer.modules.sequences.showSequenceBrowser(outputSequenceBrowserNode)

    return outputSequenceNode
    def updateWindowLevelRectangle(self, sliceLayer, xyBounds):
        # conversion as done in LabelEffect.py:applyImageMask
        xlo, xhi, ylo, yhi, zlo, zhi = xyBounds
        if xlo == xhi or ylo == yhi:
            return
        sliceNode = sliceLayer.GetVolumeNode()
        sliceImage = sliceNode.GetImageData()
        if not sliceImage:
            return [0, 0, 0, 0]
        xyToIJK = sliceLayer.GetXYToIJKTransform()
        tlIJK = xyToIJK.TransformPoint((xlo, yhi, 0))
        trIJK = xyToIJK.TransformPoint((xhi, yhi, 0))
        blIJK = xyToIJK.TransformPoint((xlo, ylo, 0))
        brIJK = xyToIJK.TransformPoint((xhi, ylo, 0))
        #
        # get the mask bounding box in ijk coordinates
        # - get the xy bounds
        # - transform to ijk
        # - clamp the bounds to the dimensions of the label image
        #

        # do the clamping of the four corners
        dims = sliceImage.GetDimensions()
        tl = [0] * 3
        tr = [0] * 3
        bl = [0] * 3
        br = [0] * 3
        corners = ((tlIJK, tl), (trIJK, tr), (blIJK, bl), (brIJK, br))
        for corner, clampedCorner in corners:
            for d in xrange(3):
                clamped = int(round(corner[d]))
                if clamped < 0:
                    clamped = 0
                if clamped >= dims[d]:
                    clamped = dims[d] - 1
                clampedCorner[d] = clamped

        # calculate the statistics for the selected region
        clip = vtk.vtkImageClip()
        extentMin = [
            min(tl[0], min(tr[0], min(bl[0], br[0]))),
            min(tl[1], min(tr[1], min(bl[1], br[1]))),
            min(tl[2], min(tr[2], min(bl[2], br[2]))),
        ]
        extentMax = [
            max(tl[0], max(tr[0], max(bl[0], br[0]))),
            max(tl[1], max(tr[1], max(bl[1], br[1]))),
            max(tl[2], max(tr[2], max(bl[2], br[2]))),
        ]
        clip.SetOutputWholeExtent(extentMin[0], extentMax[0], extentMin[1], extentMax[1], extentMin[2], extentMax[2])
        if vtk.VTK_MAJOR_VERSION <= 5:
            clip.SetInput(sliceNode.GetImageData())
        else:
            clip.SetInputData(sliceNode.GetImageData())
        clip.ClipDataOn()
        clip.Update()

        stats = vtk.vtkImageHistogramStatistics()

        if vtk.VTK_MAJOR_VERSION <= 5:
            stats.SetInput(clip.GetOutput())
        else:
            stats.SetInputData(clip.GetOutput())
        stats.Update()

        minIntensity = stats.GetMinimum()
        maxIntensity = stats.GetMaximum()

        sliceNode.GetDisplayNode().SetAutoWindowLevel(False)
        sliceNode.GetDisplayNode().SetAutoWindowLevel(0)
        sliceNode.GetDisplayNode().SetWindowLevel(
            maxIntensity - minIntensity, (maxIntensity - minIntensity) / 2.0 + minIntensity
        )
        sliceNode.GetDisplayNode().Modified()
Exemple #5
0
    def clipImageWithPolyData(self,
                              inputImageData,
                              outputImageData,
                              clippingPolyData,
                              rasToModel,
                              inputIjkToRas,
                              outputIjkToRas,
                              clipOutsideSurface=True,
                              fillValue=0,
                              reduceExtent=False):
        """
    Fill voxels of the input volume inside/outside the clipping model with the provided fill value
    If reduceExtent is True then the extent of the volume will be reduced to the smallest possible box that still contains all the non-zero voxels.
    """

        # Determine the transform between the box and the image IJK coordinate systems

        ijkToModel = vtk.vtkMatrix4x4()
        vtk.vtkMatrix4x4.Multiply4x4(rasToModel, inputIjkToRas, ijkToModel)
        modelToIjkTransform = vtk.vtkTransform()
        modelToIjkTransform.SetMatrix(ijkToModel)
        modelToIjkTransform.Inverse()

        transformModelToIjk = vtk.vtkTransformPolyDataFilter()
        transformModelToIjk.SetTransform(modelToIjkTransform)
        transformModelToIjk.SetInputData(clippingPolyData)

        # Use the stencil to fill the volume

        # Convert model to stencil
        polyToStencil = vtk.vtkPolyDataToImageStencil()
        polyToStencil.SetInputConnection(transformModelToIjk.GetOutputPort())
        polyToStencil.SetOutputSpacing(inputImageData.GetSpacing())
        polyToStencil.SetOutputOrigin(inputImageData.GetOrigin())
        polyToStencil.SetOutputWholeExtent(inputImageData.GetExtent())

        # Apply the stencil to the volume
        stencilToImage = vtk.vtkImageStencil()
        stencilToImage.SetInputData(inputImageData)
        stencilToImage.SetStencilConnection(polyToStencil.GetOutputPort())
        if clipOutsideSurface:
            stencilToImage.ReverseStencilOff()
        else:
            stencilToImage.ReverseStencilOn()
        stencilToImage.SetBackgroundValue(fillValue)
        stencilToImage.Update()

        # Update the volume with the stencil operation result
        if reduceExtent:

            clippingPolyDataBounds_Ijk = [0, 0, 0, 0, 0, 0]
            transformModelToIjk.GetOutput().GetBounds(
                clippingPolyDataBounds_Ijk)
            inputVolumeExtent_Ijk = inputImageData.GetExtent()
            outputVolumeExtent_Ijk = list(inputVolumeExtent_Ijk)  # make a copy
            for i in range(3):
                a = int(math.floor(clippingPolyDataBounds_Ijk[i * 2]))
                if a > outputVolumeExtent_Ijk[i * 2]:
                    outputVolumeExtent_Ijk[i * 2] = a
                b = int(math.ceil(clippingPolyDataBounds_Ijk[i * 2 + 1]))
                if b < outputVolumeExtent_Ijk[i * 2 + 1]:
                    outputVolumeExtent_Ijk[i * 2 + 1] = b

            clipper = vtk.vtkImageClip()
            clipper.SetOutputWholeExtent(outputVolumeExtent_Ijk)
            clipper.ClipDataOn()
            clipper.SetInputConnection(stencilToImage.GetOutputPort())
            clipper.Update()
            outputImageData.DeepCopy(clipper.GetOutput())

            # Offset the extent to start at [0,0,0]
            # (maybe this is not needed, but we do it because some code may assume the image extent starts from zero)
            outputVolumeExtent_Ijk = list(outputImageData.GetExtent())
            outputIjkToInputIjk = vtk.vtkMatrix4x4()
            for i in range(3):
                outputVolumeExtentOffset = outputVolumeExtent_Ijk[i * 2]
                outputVolumeExtent_Ijk[i * 2] = outputVolumeExtent_Ijk[
                    i * 2] - outputVolumeExtentOffset
                outputVolumeExtent_Ijk[i * 2 + 1] = outputVolumeExtent_Ijk[
                    i * 2 + 1] - outputVolumeExtentOffset
                outputIjkToInputIjk.SetElement(i, 3, outputVolumeExtentOffset)
            outputImageData.SetExtent(outputVolumeExtent_Ijk)
            vtk.vtkMatrix4x4.Multiply4x4(inputIjkToRas, outputIjkToInputIjk,
                                         outputIjkToRas)

        else:

            outputImageData.DeepCopy(stencilToImage.GetOutput())
            outputIjkToRas.DeepCopy(inputIjkToRas)