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()
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)