def __init__(self, sliceLogic): super().__init__(sliceLogic) self.paintThreshold = 0 self.paintThresholdMin = 1 self.paintThresholdMax = 1 self.paintOver = 1 self.extractImage = None self.painter = slicer.vtkImageSlicePaint()
def __init__(self,sliceLogic): super(LabelEffectLogic,self).__init__(sliceLogic) self.paintThreshold = 0 self.paintThresholdMin = 1 self.paintThresholdMax = 1 self.paintOver = 1 self.extractImage = None self.painter = slicer.vtkImageSlicePaint()
def __init__(self,sliceLogic): self.sliceLogic = sliceLogic self.editUtil = EditUtil.EditUtil() # optionally set by users of the class self.undoRedo = None self.scope = 'All' # # instance variables used internally # - buffer for result of scoped editing self.scopedImageBuffer = vtk.vtkImageData() # - slice paint is used to extract/replace scoped regions self.scopedSlicePaint = slicer.vtkImageSlicePaint()
def __init__(self, sliceLogic): self.sliceLogic = sliceLogic self.editUtil = EditUtil() # Kept for backward compatibility # optionally set by users of the class self.undoRedo = None self.scope = 'All' # # instance variables used internally # - buffer for result of scoped editing self.scopedImageBuffer = vtk.vtkImageData() # - slice paint is used to extract/replace scoped regions self.scopedSlicePaint = slicer.vtkImageSlicePaint()
def paintBrush(self, x, y): """ paint with a brush that is circular (or optionally spherical) in XY space (could be stretched or rotate when transformed to IJK) - make sure to hit every pixel in IJK space - apply the threshold if selected """ sliceLogic = self.sliceWidget.sliceLogic() sliceNode = sliceLogic.GetSliceNode() labelLogic = sliceLogic.GetLabelLayer() labelNode = labelLogic.GetVolumeNode() labelImage = labelNode.GetImageData() backgroundLogic = sliceLogic.GetBackgroundLayer() backgroundNode = backgroundLogic.GetVolumeNode() backgroundImage = backgroundNode.GetImageData() if not labelNode: # if there's no label, we can't paint return # # get the brush bounding box in ijk coordinates # - get the xy bounds # - transform to ijk # - clamp the bounds to the dimensions of the label image # bounds = self.brush.GetPoints().GetBounds() left = x + bounds[0] right = x + bounds[1] bottom = y + bounds[2] top = y + bounds[3] xyToIJK = labelLogic.GetXYToIJKTransform() tlIJK = xyToIJK.TransformDoublePoint((left, top, 0)) trIJK = xyToIJK.TransformDoublePoint((right, top, 0)) blIJK = xyToIJK.TransformDoublePoint((left, bottom, 0)) brIJK = xyToIJK.TransformDoublePoint((right, bottom, 0)) dims = labelImage.GetDimensions() # clamp the top, bottom, left, right to the # valid dimensions of the label image tl = [0, 0, 0] tr = [0, 0, 0] bl = [0, 0, 0] br = [0, 0, 0] for i in xrange(3): tl[i] = int(round(tlIJK[i])) if tl[i] < 0: tl[i] = 0 if tl[i] >= dims[i]: tl[i] = dims[i] - 1 tr[i] = int(round(trIJK[i])) if tr[i] < 0: tr[i] = 0 if tr[i] >= dims[i]: tr[i] = dims[i] - 1 bl[i] = int(round(blIJK[i])) if bl[i] < 0: bl[i] = 0 if bl[i] >= dims[i]: bl[i] = dims[i] - 1 br[i] = int(round(brIJK[i])) if br[i] < 0: br[i] = 0 if br[i] >= dims[i]: br[i] = dims[i] - 1 # If the region is smaller than a pixel then paint it using paintPixel mode, # to make sure at least one pixel is filled on each click maxRowDelta = 0 maxColumnDelta = 0 for i in xrange(3): d = abs(tr[i] - tl[i]) if d > maxColumnDelta: maxColumnDelta = d d = abs(br[i] - bl[i]) if d > maxColumnDelta: maxColumnDelta = d d = abs(bl[i] - tl[i]) if d > maxRowDelta: maxRowDelta = d d = abs(br[i] - tr[i]) if d > maxRowDelta: maxRowDelta = d if maxRowDelta <= 1 or maxColumnDelta <= 1: self.paintPixel(x, y) return # # get the layers and nodes # and ijk to ras matrices including transforms # backgroundIJKToRAS = self.logic.getIJKToRASMatrix(backgroundNode) labelIJKToRAS = self.logic.getIJKToRASMatrix(labelNode) xyToRAS = sliceNode.GetXYToRAS() brushCenter = xyToRAS.MultiplyPoint((x, y, 0, 1))[:3] brushRadius = self.radius bSphere = self.sphere parameterNode = EditUtil.getParameterNode() paintLabel = int(parameterNode.GetParameter("label")) paintOver = int(parameterNode.GetParameter("LabelEffect,paintOver")) paintThreshold = int( parameterNode.GetParameter("LabelEffect,paintThreshold")) paintThresholdMin = float( parameterNode.GetParameter("LabelEffect,paintThresholdMin")) paintThresholdMax = float( parameterNode.GetParameter("LabelEffect,paintThresholdMax")) # # set up the painter class and let 'r rip! # if not hasattr(self, "painter"): self.painter = slicer.vtkImageSlicePaint() self.painter.SetBackgroundImage(backgroundImage) self.painter.SetBackgroundIJKToWorld(backgroundIJKToRAS) self.painter.SetWorkingImage(labelImage) self.painter.SetWorkingIJKToWorld(labelIJKToRAS) self.painter.SetTopLeft(tl[0], tl[1], tl[2]) self.painter.SetTopRight(tr[0], tr[1], tr[2]) self.painter.SetBottomLeft(bl[0], bl[1], bl[2]) self.painter.SetBottomRight(br[0], br[1], br[2]) self.painter.SetBrushCenter(brushCenter[0], brushCenter[1], brushCenter[2]) self.painter.SetBrushRadius(brushRadius) self.painter.SetPaintLabel(paintLabel) self.painter.SetPaintOver(paintOver) self.painter.SetThresholdPaint(paintThreshold) self.painter.SetThresholdPaintRange(paintThresholdMin, paintThresholdMax) if bSphere: # fill volume of a sphere rather than a circle on the currently displayed image slice # Algorithm: ########################### # Assume brushRadius is in mm # Estimate zVoxelSize # Compute number of slices spanned by sphere, that are still within the volume # For each spanned slice # reposition the brushCenter using xy(z)ToRAS transform: i.e. canvas to patient world coordinates # resize the radius: brushRadiusOffset=sqrt(brushRadius*brushRadius - zOffset_mm*zOffset_mm) # invoke Paint() # Finally paint on the center slice, leaving the gui on the center slice being most visibly edited #------------------ # Estimate zVoxelSize_mm brushCenter1 = xyToRAS.MultiplyPoint((x, y, 0, 1))[:3] brushCenter2 = xyToRAS.MultiplyPoint((x, y, 100, 1))[:3] dx1 = brushCenter1[0] - brushCenter2[0] dx2 = brushCenter1[1] - brushCenter2[1] dx3 = brushCenter1[2] - brushCenter2[2] distanceSpannedBy100Slices = sqrt(dx1 * dx1 + dx2 * dx2 + dx3 * dx3) # compute L2 norm if distanceSpannedBy100Slices == 0: zVoxelSize_mm = 1 else: zVoxelSize_mm = distanceSpannedBy100Slices / 100 # -- # Compute number of slices spanned by sphere nNumSlicesInEachDirection = brushRadius / zVoxelSize_mm nNumSlicesInEachDirection = nNumSlicesInEachDirection - 1 sliceOffsetArray = numpy.concatenate((-1 * numpy.arange( 1, nNumSlicesInEachDirection + 1, ), numpy.arange(1, nNumSlicesInEachDirection + 1))) for iSliceOffset in sliceOffsetArray: # x,y uses slice (canvas) coordinate system and actually has a 3rd z component (index into the slice you're looking at) # hence xyToRAS is really performing xyzToRAS. RAS is patient world coordinate system. Note the 1 is because the trasform uses homogeneous coordinates iBrushCenter = xyToRAS.MultiplyPoint( (x, y, iSliceOffset, 1))[:3] self.painter.SetBrushCenter(iBrushCenter[0], iBrushCenter[1], iBrushCenter[2]) # [ ] Need to just continue (pass this loop iteration if the brush center is not within the volume zOffset_mm = zVoxelSize_mm * iSliceOffset brushRadiusOffset = sqrt(brushRadius * brushRadius - zOffset_mm * zOffset_mm) self.painter.SetBrushRadius(brushRadiusOffset) # -- tlIJKtemp = xyToIJK.TransformDoublePoint( (left, top, iSliceOffset)) trIJKtemp = xyToIJK.TransformDoublePoint( (right, top, iSliceOffset)) blIJKtemp = xyToIJK.TransformDoublePoint( (left, bottom, iSliceOffset)) brIJKtemp = xyToIJK.TransformDoublePoint( (right, bottom, iSliceOffset)) # clamp the top, bottom, left, right to the # valid dimensions of the label image tltemp = [0, 0, 0] trtemp = [0, 0, 0] bltemp = [0, 0, 0] brtemp = [0, 0, 0] for i in xrange(3): tltemp[i] = int(round(tlIJKtemp[i])) if tltemp[i] < 0: tltemp[i] = 0 if tltemp[i] >= dims[i]: tltemp[i] = dims[i] - 1 trtemp[i] = int(round(trIJKtemp[i])) if trtemp[i] < 0: trtemp[i] = 0 if trtemp[i] > dims[i]: trtemp[i] = dims[i] - 1 bltemp[i] = int(round(blIJKtemp[i])) if bltemp[i] < 0: bltemp[i] = 0 if bltemp[i] > dims[i]: bltemp[i] = dims[i] - 1 brtemp[i] = int(round(brIJKtemp[i])) if brtemp[i] < 0: brtemp[i] = 0 if brtemp[i] > dims[i]: brtemp[i] = dims[i] - 1 self.painter.SetTopLeft(tltemp[0], tltemp[1], tltemp[2]) self.painter.SetTopRight(trtemp[0], trtemp[1], trtemp[2]) self.painter.SetBottomLeft(bltemp[0], bltemp[1], bltemp[2]) self.painter.SetBottomRight(brtemp[0], brtemp[1], brtemp[2]) self.painter.Paint() # paint the slice: same for circular and spherical brush modes self.painter.SetTopLeft(tl[0], tl[1], tl[2]) self.painter.SetTopRight(tr[0], tr[1], tr[2]) self.painter.SetBottomLeft(bl[0], bl[1], bl[2]) self.painter.SetBottomRight(br[0], br[1], br[2]) self.painter.SetBrushCenter(brushCenter[0], brushCenter[1], brushCenter[2]) self.painter.SetBrushRadius(brushRadius) self.painter.Paint()
def paintBrush(self, x, y): """ paint with a brush that is circular (or optionally spherical) in XY space (could be stretched or rotate when transformed to IJK) - make sure to hit every pixel in IJK space - apply the threshold if selected """ sliceLogic = self.sliceWidget.sliceLogic() sliceNode = sliceLogic.GetSliceNode() labelLogic = sliceLogic.GetLabelLayer() labelNode = labelLogic.GetVolumeNode() labelImage = labelNode.GetImageData() backgroundLogic = sliceLogic.GetBackgroundLayer() backgroundNode = backgroundLogic.GetVolumeNode() backgroundImage = backgroundNode.GetImageData() if not labelNode: # if there's no label, we can't paint return # # get the brush bounding box in ijk coordinates # - get the xy bounds # - transform to ijk # - clamp the bounds to the dimensions of the label image # bounds = self.brush.GetPoints().GetBounds() left = x + bounds[0] right = x + bounds[1] bottom = y + bounds[2] top = y + bounds[3] xyToIJK = labelLogic.GetXYToIJKTransform() tlIJK = xyToIJK.TransformDoublePoint( (left, top, 0) ) trIJK = xyToIJK.TransformDoublePoint( (right, top, 0) ) blIJK = xyToIJK.TransformDoublePoint( (left, bottom, 0) ) brIJK = xyToIJK.TransformDoublePoint( (right, bottom, 0) ) dims = labelImage.GetDimensions() # clamp the top, bottom, left, right to the # valid dimensions of the label image tl = [0,0,0] tr = [0,0,0] bl = [0,0,0] br = [0,0,0] for i in range(3): tl[i] = int(round(tlIJK[i])) if tl[i] < 0: tl[i] = 0 if tl[i] >= dims[i]: tl[i] = dims[i] - 1 tr[i] = int(round(trIJK[i])) if tr[i] < 0: tr[i] = 0 if tr[i] >= dims[i]: tr[i] = dims[i] - 1 bl[i] = int(round(blIJK[i])) if bl[i] < 0: bl[i] = 0 if bl[i] >= dims[i]: bl[i] = dims[i] - 1 br[i] = int(round(brIJK[i])) if br[i] < 0: br[i] = 0 if br[i] >= dims[i]: br[i] = dims[i] - 1 # If the region is smaller than a pixel then paint it using paintPixel mode, # to make sure at least one pixel is filled on each click maxRowDelta = 0 maxColumnDelta = 0 for i in range(3): d = abs(tr[i] - tl[i]) if d > maxColumnDelta: maxColumnDelta = d d = abs(br[i] - bl[i]) if d > maxColumnDelta: maxColumnDelta = d d = abs(bl[i] - tl[i]) if d > maxRowDelta: maxRowDelta = d d = abs(br[i] - tr[i]) if d > maxRowDelta: maxRowDelta = d if maxRowDelta<=1 or maxColumnDelta<=1 : self.paintPixel(x,y) return # # get the layers and nodes # and ijk to ras matrices including transforms # backgroundIJKToRAS = self.logic.getIJKToRASMatrix(backgroundNode) labelIJKToRAS = self.logic.getIJKToRASMatrix(labelNode) xyToRAS = sliceNode.GetXYToRAS() brushCenter = xyToRAS.MultiplyPoint( (x, y, 0, 1) )[:3] brushRadius = self.radius bSphere = self.sphere parameterNode = EditUtil.getParameterNode() paintLabel = int(parameterNode.GetParameter("label")) paintOver = int(parameterNode.GetParameter("LabelEffect,paintOver")) paintThreshold = int(parameterNode.GetParameter("LabelEffect,paintThreshold")) paintThresholdMin = float( parameterNode.GetParameter("LabelEffect,paintThresholdMin")) paintThresholdMax = float( parameterNode.GetParameter("LabelEffect,paintThresholdMax")) # # set up the painter class and let 'r rip! # if not hasattr(self,"painter"): self.painter = slicer.vtkImageSlicePaint() self.painter.SetBackgroundImage(backgroundImage) self.painter.SetBackgroundIJKToWorld(backgroundIJKToRAS) self.painter.SetWorkingImage(labelImage) self.painter.SetWorkingIJKToWorld(labelIJKToRAS) self.painter.SetTopLeft( tl[0], tl[1], tl[2] ) self.painter.SetTopRight( tr[0], tr[1], tr[2] ) self.painter.SetBottomLeft( bl[0], bl[1], bl[2] ) self.painter.SetBottomRight( br[0], br[1], br[2] ) self.painter.SetBrushCenter( brushCenter[0], brushCenter[1], brushCenter[2] ) self.painter.SetBrushRadius( brushRadius ) self.painter.SetPaintLabel(paintLabel) self.painter.SetPaintOver(paintOver) self.painter.SetThresholdPaint(paintThreshold) self.painter.SetThresholdPaintRange(paintThresholdMin, paintThresholdMax) if bSphere: # fill volume of a sphere rather than a circle on the currently displayed image slice # Algorithm: ########################### # Assume brushRadius is in mm # Estimate zVoxelSize # Compute number of slices spanned by sphere, that are still within the volume # For each spanned slice # reposition the brushCenter using xy(z)ToRAS transform: i.e. canvas to patient world coordinates # resize the radius: brushRadiusOffset=sqrt(brushRadius*brushRadius - zOffset_mm*zOffset_mm) # invoke Paint() # Finally paint on the center slice, leaving the gui on the center slice being most visibly edited #------------------ # Estimate zVoxelSize_mm brushCenter1 = xyToRAS.MultiplyPoint( (x, y, 0, 1) )[:3] brushCenter2 = xyToRAS.MultiplyPoint( (x, y, 100, 1) )[:3] dx1=brushCenter1[0]-brushCenter2[0] dx2=brushCenter1[1]-brushCenter2[1] dx3=brushCenter1[2]-brushCenter2[2] distanceSpannedBy100Slices = sqrt(dx1*dx1+dx2*dx2+dx3*dx3) # compute L2 norm if distanceSpannedBy100Slices==0: zVoxelSize_mm=1 else: zVoxelSize_mm = int(distanceSpannedBy100Slices / 100) # -- # Compute number of slices spanned by sphere nNumSlicesInEachDirection=int(brushRadius / zVoxelSize_mm); nNumSlicesInEachDirection=nNumSlicesInEachDirection-1 sliceOffsetArray=numpy.concatenate((-1*numpy.arange(1,nNumSlicesInEachDirection+1,), numpy.arange(1,nNumSlicesInEachDirection+1))) for iSliceOffset in sliceOffsetArray: # x,y uses slice (canvas) coordinate system and actually has a 3rd z component (index into the slice you're looking at) # hence xyToRAS is really performing xyzToRAS. RAS is patient world coordinate system. Note the 1 is because the trasform uses homogeneous coordinates iBrushCenter = xyToRAS.MultiplyPoint( (x, y, iSliceOffset, 1) )[:3] self.painter.SetBrushCenter( iBrushCenter[0], iBrushCenter[1], iBrushCenter[2] ) # [ ] Need to just continue (pass this loop iteration if the brush center is not within the volume zOffset_mm=zVoxelSize_mm*iSliceOffset; brushRadiusOffset=sqrt(brushRadius*brushRadius - zOffset_mm*zOffset_mm) self.painter.SetBrushRadius( brushRadiusOffset ) # -- tlIJKtemp = xyToIJK.TransformDoublePoint( (left, top, iSliceOffset) ) trIJKtemp = xyToIJK.TransformDoublePoint( (right, top, iSliceOffset) ) blIJKtemp = xyToIJK.TransformDoublePoint( (left, bottom, iSliceOffset) ) brIJKtemp = xyToIJK.TransformDoublePoint( (right, bottom, iSliceOffset) ) # clamp the top, bottom, left, right to the # valid dimensions of the label image tltemp = [0,0,0] trtemp = [0,0,0] bltemp = [0,0,0] brtemp = [0,0,0] for i in range(3): tltemp[i] = int(round(tlIJKtemp[i])) if tltemp[i] < 0: tltemp[i] = 0 if tltemp[i] >= dims[i]: tltemp[i] = dims[i] - 1 trtemp[i] = int(round(trIJKtemp[i])) if trtemp[i] < 0: trtemp[i] = 0 if trtemp[i] > dims[i]: trtemp[i] = dims[i] - 1 bltemp[i] = int(round(blIJKtemp[i])) if bltemp[i] < 0: bltemp[i] = 0 if bltemp[i] > dims[i]: bltemp[i] = dims[i] - 1 brtemp[i] = int(round(brIJKtemp[i])) if brtemp[i] < 0: brtemp[i] = 0 if brtemp[i] > dims[i]: brtemp[i] = dims[i] - 1 self.painter.SetTopLeft( tltemp[0], tltemp[1], tltemp[2] ) self.painter.SetTopRight( trtemp[0], trtemp[1], trtemp[2] ) self.painter.SetBottomLeft( bltemp[0], bltemp[1], bltemp[2] ) self.painter.SetBottomRight( brtemp[0], brtemp[1], brtemp[2] ) self.painter.Paint() # paint the slice: same for circular and spherical brush modes self.painter.SetTopLeft( tl[0], tl[1], tl[2] ) self.painter.SetTopRight( tr[0], tr[1], tr[2] ) self.painter.SetBottomLeft( bl[0], bl[1], bl[2] ) self.painter.SetBottomRight( br[0], br[1], br[2] ) self.painter.SetBrushCenter( brushCenter[0], brushCenter[1], brushCenter[2] ) self.painter.SetBrushRadius( brushRadius ) self.painter.Paint()