예제 #1
0
    def installVolumeRenderActorPipeline(self):

        self.volume_mapper.SetInputData(self.img3D)

        ia = vtk.vtkImageHistogramStatistics()
        ia.SetInputData(self.img3D)
        ia.SetAutoRangePercentiles(90., 99.)
        ia.Update()

        cmin, cmax = ia.GetAutoRange()
        print("viewer: cmin cmax", cmin, cmax)
        # cmin, cmax = (1000,2000)
        # probably the level could be the median of the image within
        # the percentiles
        median = ia.GetMedian()
        # accomodates all values between the level an the percentiles
        #window = 2*max(abs(median-cmin),abs(median-cmax))
        window = cmax - cmin
        viridis = colormaps.CILColorMaps.get_color_transfer_function(
            'viridis', (cmin, cmax))

        x = numpy.linspace(ia.GetMinimum(), ia.GetMaximum(), num=255)
        scaling = 0.1
        opacity = colormaps.CILColorMaps.get_opacity_transfer_function(
            x, colormaps.relu, cmin, cmax, scaling)

        self.volume_property.SetColor(viridis)
        self.volume_property.SetScalarOpacity(opacity)
        self.volume_property.ShadeOn()
        self.volume_property.SetInterpolationTypeToLinear()

        self.ren.AddVolume(self.volume)
        self.volume_colormap_limits = (cmin, cmax)
        self.volume_render_initialised = True
        self.volume.VisibilityOff()
예제 #2
0
    def __init__(self, image_data, **kw):

        # for zope
        self.__component_name__ = u''

        self._algorithm = None
        self._image_data_object = image_data
        self.__reference_count = 1
        self.__x_values = None
        self.__y_values = None
        self.__z_values = None

        self._header = {}  # header values that don't map to DICOM easily
        self._dimensions = []
        self.__stencil_data = None
        self.__stencil_owner = None
        self._filename = kw.get('filename', None)
        self.__histogram_stats = vtk.vtkImageHistogramStatistics()

        # DICOM-related stuff
        datadir = '/'
        if self._filename:
            datadir = os.path.dirname(self._filename)
        self.dicom_converter = BaseDicomConverter(datadir=datadir)
        self.station_id = '0001'
        self.parallax_base_uid = '1.2.826.0.1.3680043.9.1613'
        self._coordinate_system = CoordinateSystem.vtk_coords
        self._dicom_header = None
        self._dicom_slice_headers = []  # slice-by-slice dicom headers (buffer)
        self._dicom_mtime = -1

        # make sure image_data is correct type
        if isinstance(image_data, vtk.vtkImageData):
            self.SetInputData(image_data)
        elif isinstance(image_data, vtk.vtkAlgorithmOutput):
            self.SetInputConnection(image_data)
        else:
            raise TypeError("MVImage must be created with a VTK image object")

        # Don't do this here - image may not have been loaded yet
        # Create default DICOM header
        #self.ClearDICOMHeader()

        # Create default dimensions
        self.SetDefaultDimensionInformation()

        if ('input' in kw) and isinstance(kw['input'], MVImage):
            ci = kw['input']
            self.CopyInfo(ci)
            if ci.GetXSlicePositions() is not None:
                self.__x_values = ci.GetXSlicePositions().copy()
            if ci.GetYSlicePositions() is not None:
                self.__y_values = ci.GetYSlicePositions().copy()
            if ci.GetZSlicePositions() is not None:
                self.__z_values = ci.GetZSlicePositions().copy()

        if 'slice_headers' in kw:
            self.SetSliceDICOMHeaders(kw['slice_headers'])
def auto_image_range(image, percentiles=(1, 99)):
    """Compute range for color transfer function."""
    stats = vtk.vtkImageHistogramStatistics()
    stats.SetInputData(image)
    stats.AutomaticBinningOn()
    stats.SetMaximumNumberOfBins(512)
    stats.SetAutoRangePercentiles(percentiles)
    stats.UpdateWholeExtent()
    return tuple(stats.GetAutoRange())
    def computeStatistics(self, segmentID):
        requestedKeys = self.getRequestedKeys()

        segmentationNode = slicer.mrmlScene.GetNodeByID(
            self.getParameterNode().GetParameter("Segmentation"))
        grayscaleNode = slicer.mrmlScene.GetNodeByID(
            self.getParameterNode().GetParameter("ScalarVolume"))

        if len(requestedKeys) == 0:
            return {}

        stencil = self.getStencilForVolume(segmentationNode, segmentID,
                                           grayscaleNode)
        if not stencil:
            return {}

        cubicMMPerVoxel = reduce(lambda x, y: x * y,
                                 grayscaleNode.GetSpacing())
        ccPerCubicMM = 0.001

        stat = vtk.vtkImageAccumulate()
        stat.SetInputData(grayscaleNode.GetImageData())
        stat.SetStencilData(stencil.GetOutput())
        stat.Update()

        medians = vtk.vtkImageHistogramStatistics()
        medians.SetInputData(grayscaleNode.GetImageData())
        medians.SetStencilData(stencil.GetOutput())
        medians.Update()

        # create statistics list
        stats = {}
        if "voxel_count" in requestedKeys:
            stats["voxel_count"] = stat.GetVoxelCount()
        if "volume_mm3" in requestedKeys:
            stats["volume_mm3"] = stat.GetVoxelCount() * cubicMMPerVoxel
        if "volume_cm3" in requestedKeys:
            stats["volume_cm3"] = stat.GetVoxelCount(
            ) * cubicMMPerVoxel * ccPerCubicMM
        if stat.GetVoxelCount() > 0:
            if "min" in requestedKeys:
                stats["min"] = stat.GetMin()[0]
            if "max" in requestedKeys:
                stats["max"] = stat.GetMax()[0]
            if "mean" in requestedKeys:
                stats["mean"] = stat.GetMean()[0]
            if "stdev" in requestedKeys:
                stats["stdev"] = stat.GetStandardDeviation()[0]
            if "median" in requestedKeys:
                stats["median"] = medians.GetMedian()
        return stats
예제 #5
0
def compare_stats(sitkimg, vtkimg):
    """ Compare the statistics of a SimpleITK image and a VTK image. """

    # Compute the VTK image histogram statistics
    histo = vtk.vtkImageHistogramStatistics()
    histo.SetInputData(vtkimg)
    histo.Update()
    print(histo.GetStandardDeviation())

    vtkstats = [
        histo.GetMinimum(),
        histo.GetMaximum(),
        histo.GetMean(),
        histo.GetStandardDeviation()
    ]

    print("\nvtk median = ", histo.GetMedian())

    print("\nVTK source image stats")
    printStats(vtkstats)

    # Compute the SimpleITK image statistics
    stats = sitk.StatisticsImageFilter()
    stats.Execute(sitkimg)

    sitkstats = [
        stats.GetMinimum(),
        stats.GetMaximum(),
        stats.GetMean(),
        stats.GetSigma()
    ]

    print("\nSimpleITK image stats")
    printStats(sitkstats)

    # compare the statistics of the VTK and SimpleITK images
    ok = True
    for v, s in zip(vtkstats, sitkstats):
        x = v - s
        if v != 0.0:
            y = abs(x / v)
        else:
            y = abs(x)

        if (y > .0001):
            print("Bad!", v, s, "\terror =", y)
            ok = False
    return ok
예제 #6
0
    def SetInputData(self, data_object):

        if self._algorithm is None:
            self._algorithm = vtk.vtkTrivialProducer()
            self._algorithm.SetOutput(data_object)

        self._image_data_object = data_object

        if self.__histogram_stats is None:
            self.__histogram_stats = vtk.vtkImageHistogramStatistics()

        # VTK-6
        if vtk.vtkVersion().GetVTKMajorVersion() > 5:
            self.__histogram_stats.SetInputConnection(self._algorithm)
        else:
            self.__histogram_stats.SetInput(self._image_data_object)
예제 #7
0
    def run(self, enableScreenshots=0, screenshotScaleFactor=1):
        """
    Run the actual algorithm
    """

        slicer.util.delayDisplay('Running the algorithm', 500)

        self.enableScreenshots = enableScreenshots
        self.screenshotScaleFactor = screenshotScaleFactor

        # Start in conventional layout
        lm = slicer.app.layoutManager()
        lm.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView)
        # without this delayed display, when running from the cmd line Slicer starts
        # up in a different layout and the seed won't get rendered in the right spot
        slicer.util.delayDisplay("Conventional view", 500)

        # Download MRHead from sample data
        import SampleData
        sampleDataLogic = SampleData.SampleDataLogic()
        print("Getting MR Head Volume")
        mrHeadVolume = sampleDataLogic.downloadMRHead()

        # Place a fiducial on the red slice
        markupsLogic = slicer.modules.markups.logic()
        eye = [33.4975, 79.4042, -10.2143]
        fidIndex = markupsLogic.AddFiducial(eye[0], eye[1], eye[2])
        fidID = markupsLogic.GetActiveListID()
        fidNode = slicer.mrmlScene.GetNodeByID(fidID)
        slicer.util.delayDisplay(
            "Placed a fiducial at %g, %g, %g" % (eye[0], eye[1], eye[2]), 500)

        # Pan and zoom
        sliceWidget = slicer.app.layoutManager().sliceWidget('Red')
        sliceLogic = sliceWidget.sliceLogic()
        compositeNode = sliceLogic.GetSliceCompositeNode()
        sliceNode = sliceLogic.GetSliceNode()
        sliceNode.SetXYZOrigin(-71.7, 129.7, 0.0)
        sliceNode.SetFieldOfView(98.3, 130.5, 1.0)
        slicer.util.delayDisplay("Panned and zoomed", 500)

        # Get the seed widget seed location
        startingSeedDisplayCoords = [0.0, 0.0, 0.0]
        helper = self.getFiducialSliceDisplayableManagerHelper('Red')
        if helper is not None:
            seedWidget = helper.GetWidget(fidNode)
            seedRepresentation = seedWidget.GetSeedRepresentation()
            handleRep = seedRepresentation.GetHandleRepresentation(fidIndex)
            startingSeedDisplayCoords = handleRep.GetDisplayPosition()
            print('Starting seed display coords = %d, %d, %d' %
                  (startingSeedDisplayCoords[0], startingSeedDisplayCoords[1],
                   startingSeedDisplayCoords[2]))
        self.takeScreenshot('FiducialLayoutSwitchBug1914-StartingPosition',
                            'Fiducial starting position',
                            slicer.qMRMLScreenShotDialog.Red)

        # Switch to red slice only
        lm.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpRedSliceView)
        slicer.util.delayDisplay("Red Slice only", 500)

        # Switch to conventional layout
        print 'Calling set layout back to conventional'
        lm.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView)
        print 'Done calling set layout back to conventional'
        slicer.util.delayDisplay("Conventional layout", 500)

        # Get the current seed widget seed location
        endingSeedDisplayCoords = [0.0, 0.0, 0.0]
        helper = self.getFiducialSliceDisplayableManagerHelper('Red')
        if helper is not None:
            seedWidget = helper.GetWidget(fidNode)
            seedRepresentation = seedWidget.GetSeedRepresentation()
            handleRep = seedRepresentation.GetHandleRepresentation(fidIndex)
            endingSeedDisplayCoords = handleRep.GetDisplayPosition()
            print('Ending seed display coords = %d, %d, %d' %
                  (endingSeedDisplayCoords[0], endingSeedDisplayCoords[1],
                   endingSeedDisplayCoords[2]))
        self.takeScreenshot('FiducialLayoutSwitchBug1914-EndingPosition',
                            'Fiducial ending position',
                            slicer.qMRMLScreenShotDialog.Red)

        # Compare to original seed widget location
        diff = math.pow(
            (startingSeedDisplayCoords[0] - endingSeedDisplayCoords[0]),
            2) + math.pow(
                (startingSeedDisplayCoords[1] - endingSeedDisplayCoords[1]),
                2) + math.pow((startingSeedDisplayCoords[2] -
                               endingSeedDisplayCoords[2]), 2)
        if diff != 0.0:
            diff = math.sqrt(diff)
        slicer.util.delayDisplay(
            "Difference between starting and ending seed display coordinates = %g"
            % diff)

        if diff > self.maximumDisplayDifference:
            # double check against the RAS coordinates of the underlying volume since the display could have changed with a FOV adjustment.
            sliceView = sliceWidget.sliceView()
            volumeRAS = sliceView.convertXYZToRAS(endingSeedDisplayCoords)
            seedRAS = [0, 0, 0]
            fidNode.GetNthFiducialPosition(0, seedRAS)
            rasDiff = math.pow((seedRAS[0] - volumeRAS[0]), 2) + math.pow(
                (seedRAS[1] - volumeRAS[1]), 2) + math.pow(
                    (seedRAS[2] - volumeRAS[2]), 2)
            if rasDiff != 0.0:
                rasDiff = math.sqrt(rasDiff)
            print 'Checking the difference between fiducial RAS position', seedRAS, 'and volume RAS as derived from the fiducial display position', volumeRAS, ': ', rasDiff
            if rasDiff > self.maximumRASDifference:
                slicer.util.delayDisplay(
                    "RAS coordinate difference is too large as well!\nExpected < %g but got %g"
                    % (self.maximumRASDifference, rasDiff))
                return False
            else:
                slicer.util.delayDisplay(
                    "RAS coordinate difference is %g which is < %g, test passes."
                    % (rasDiff, self.maximumRASDifference))

        if enableScreenshots == 1:
            # compare the screen snapshots
            startView = slicer.mrmlScene.GetFirstNodeByName(
                'FiducialLayoutSwitchBug1914-StartingPosition')
            startShot = startView.GetScreenShot()
            endView = slicer.mrmlScene.GetFirstNodeByName(
                'FiducialLayoutSwitchBug1914-EndingPosition')
            endShot = endView.GetScreenShot()
            imageMath = vtk.vtkImageMathematics()
            imageMath.SetOperationToSubtract()
            imageMath.SetInput1(startShot)
            imageMath.SetInput2(endShot)
            imageMath.Update()
            shotDiff = imageMath.GetOutput()
            # save it as a scene view
            annotationLogic = slicer.modules.annotations.logic()
            annotationLogic.CreateSnapShot(
                "FiducialLayoutSwitchBug1914-Diff",
                "Difference between starting and ending fiducial seed positions",
                slicer.qMRMLScreenShotDialog.Red, screenshotScaleFactor,
                shotDiff)
            # calculate the image difference
            imageStats = vtk.vtkImageHistogramStatistics()
            imageStats.SetInput(shotDiff)
            imageStats.GenerateHistogramImageOff()
            imageStats.Update()
            meanVal = imageStats.GetMean()
            slicer.util.delayDisplay("Mean of image difference = %g" % meanVal)
            if meanVal > 5.0:
                # raise Exception("Image difference is too great!\nExpected <= 5.0, but got %g" % (meanVal))
                print(
                    "Image difference is too great!\nExpected <= 5.0, but got %g"
                    % (meanVal))
                return False

        slicer.util.delayDisplay('Test passed!')
        return True
예제 #8
0
    def test_FiducialLayoutSwitchBug1914(self):
        # test difference in display location and then in RAS if this is too fine
        maximumDisplayDifference = 1.0
        # for future testing: take into account the volume voxel size
        maximumRASDifference = 1.0

        enableScreenshots = 0
        screenshotScaleFactor = 1

        logic = FiducialLayoutSwitchBug1914Logic()
        logging.info(
            "ctest, please don't truncate my output: CTEST_FULL_OUTPUT")

        self.delayDisplay('Running the algorithm')
        # Start in conventional layout
        lm = slicer.app.layoutManager()
        lm.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView)
        # without this delayed display, when running from the cmd line Slicer starts
        # up in a different layout and the seed won't get rendered in the right spot
        self.delayDisplay("Conventional view")

        # Download MRHead from sample data
        import SampleData
        mrHeadVolume = SampleData.downloadSample("MRHead")

        # Place a point on the red slice
        eye = [33.4975, 79.4042, -10.2143]
        markupNode = slicer.mrmlScene.AddNewNodeByClass(
            "vtkMRMLMarkupsFiducialNode")
        markupNode.AddControlPoint(eye)
        self.delayDisplay(
            f"Placed a point at {eye[0]:g}, {eye[1]:g}, {eye[2]:g}")

        # Pan and zoom
        sliceWidget = slicer.app.layoutManager().sliceWidget('Red')
        sliceLogic = sliceWidget.sliceLogic()
        compositeNode = sliceLogic.GetSliceCompositeNode()
        sliceNode = sliceLogic.GetSliceNode()
        sliceNode.SetXYZOrigin(-71.7, 129.7, 0.0)
        sliceNode.SetFieldOfView(98.3, 130.5, 1.0)
        self.delayDisplay("Panned and zoomed")

        # Get the seed widget seed location
        startingSeedDisplayCoords = [0.0, 0.0, 0.0]
        helper = logic.getPointSliceDisplayableManagerHelper('Red')
        if helper is not None:
            seedWidget = helper.GetWidget(markupNode)
            seedRepresentation = seedWidget.GetSeedRepresentation()
            handleRep = seedRepresentation.GetHandleRepresentation(fidIndex)
            startingSeedDisplayCoords = handleRep.GetDisplayPosition()
            print('Starting seed display coords = %d, %d, %d' %
                  (startingSeedDisplayCoords[0], startingSeedDisplayCoords[1],
                   startingSeedDisplayCoords[2]))
        self.takeScreenshot('FiducialLayoutSwitchBug1914-StartingPosition',
                            'Point starting position',
                            slicer.qMRMLScreenShotDialog.Red)

        # Switch to red slice only
        lm.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpRedSliceView)
        self.delayDisplay("Red Slice only")

        # Switch to conventional layout
        print('Calling set layout back to conventional')
        lm.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView)
        print('Done calling set layout back to conventional')
        self.delayDisplay("Conventional layout")

        # Get the current seed widget seed location
        endingSeedDisplayCoords = [0.0, 0.0, 0.0]
        helper = logic.getPointSliceDisplayableManagerHelper('Red')
        if helper is not None:
            seedWidget = helper.GetWidget(markupNode)
            seedRepresentation = seedWidget.GetSeedRepresentation()
            handleRep = seedRepresentation.GetHandleRepresentation(fidIndex)
            endingSeedDisplayCoords = handleRep.GetDisplayPosition()
            print('Ending seed display coords = %d, %d, %d' %
                  (endingSeedDisplayCoords[0], endingSeedDisplayCoords[1],
                   endingSeedDisplayCoords[2]))
        self.takeScreenshot('FiducialLayoutSwitchBug1914-EndingPosition',
                            'Point ending position',
                            slicer.qMRMLScreenShotDialog.Red)

        # Compare to original seed widget location
        diff = math.pow(
            (startingSeedDisplayCoords[0] - endingSeedDisplayCoords[0]),
            2) + math.pow(
                (startingSeedDisplayCoords[1] - endingSeedDisplayCoords[1]),
                2) + math.pow((startingSeedDisplayCoords[2] -
                               endingSeedDisplayCoords[2]), 2)
        if diff != 0.0:
            diff = math.sqrt(diff)
        self.delayDisplay(
            "Difference between starting and ending seed display coordinates = %g"
            % diff)

        if diff > maximumDisplayDifference:
            # double check against the RAS coordinates of the underlying volume since the display could have changed with a FOV adjustment.
            sliceView = sliceWidget.sliceView()
            volumeRAS = sliceView.convertXYZToRAS(endingSeedDisplayCoords)
            seedRAS = [0, 0, 0]
            markupNode.GetNthControlPointPosition(0, seedRAS)
            rasDiff = math.pow((seedRAS[0] - volumeRAS[0]), 2) + math.pow(
                (seedRAS[1] - volumeRAS[1]), 2) + math.pow(
                    (seedRAS[2] - volumeRAS[2]), 2)
            if rasDiff != 0.0:
                rasDiff = math.sqrt(rasDiff)
            print('Checking the difference between point RAS position',
                  seedRAS,
                  'and volume RAS as derived from the point display position',
                  volumeRAS, ': ', rasDiff)
            if rasDiff > maximumRASDifference:
                raise Exception(
                    f"RAS coordinate difference is too large as well!\nExpected < {maximumRASDifference:g} but got {rasDiff:g}"
                )
            else:
                self.delayDisplay(
                    f"RAS coordinate difference is {rasDiff:g} which is < {maximumRASDifference:g}, test passes."
                )

        if enableScreenshots == 1:
            # compare the screen snapshots
            startView = slicer.mrmlScene.GetFirstNodeByName(
                'FiducialLayoutSwitchBug1914-StartingPosition')
            startShot = startView.GetScreenShot()
            endView = slicer.mrmlScene.GetFirstNodeByName(
                'FiducialLayoutSwitchBug1914-EndingPosition')
            endShot = endView.GetScreenShot()
            imageMath = vtk.vtkImageMathematics()
            imageMath.SetOperationToSubtract()
            imageMath.SetInput1(startShot)
            imageMath.SetInput2(endShot)
            imageMath.Update()
            shotDiff = imageMath.GetOutput()
            # save it as a scene view
            annotationLogic = slicer.modules.annotations.logic()
            annotationLogic.CreateSnapShot(
                "FiducialLayoutSwitchBug1914-Diff",
                "Difference between starting and ending point positions",
                slicer.qMRMLScreenShotDialog.Red, screenshotScaleFactor,
                shotDiff)
            # calculate the image difference
            imageStats = vtk.vtkImageHistogramStatistics()
            imageStats.SetInput(shotDiff)
            imageStats.GenerateHistogramImageOff()
            imageStats.Update()
            meanVal = imageStats.GetMean()
            self.delayDisplay("Mean of image difference = %g" % meanVal)
            if meanVal > 5.0:
                raise Exception(
                    "Image difference is too great!\nExpected <= 5.0, but got %g"
                    % (meanVal))

        self.delayDisplay('Test passed!')
예제 #9
0
    def __init__(self,
                 dimx=600,
                 dimy=600,
                 renWin=None,
                 iren=None,
                 ren=None,
                 debug=False):
        '''creates the rendering pipeline'''

        # Handle arguments
        if renWin is not None:
            self.renWin = renWin
        else:
            self.renWin = vtk.vtkRenderWindow()

        if iren is not None:
            self.iren = iren
        else:
            self.iren = vtk.vtkRenderWindowInteractor()

        # create a rendering window and renderer
        if ren is not None:
            self.ren = ren
        else:
            self.ren = vtk.vtkRenderer()
        self.renWin.SetSize(dimx, dimy)
        self.renWin.AddRenderer(self.ren)

        # img 3D as slice
        self.img3D = None
        self.slicenos = [0, 0, 0]
        self.sliceOrientation = SLICE_ORIENTATION_XY
        self.sliceActor = vtk.vtkImageActor()
        self.voi = vtk.vtkExtractVOI()
        self.wl = vtk.vtkImageMapToWindowLevelColors()
        self.ia = vtk.vtkImageHistogramStatistics()
        self.sliceActorNo = 0

        # Viewer Event manager
        self.event = ViewerEventManager()

        # create a renderwindowinteractor
        self.style = CILInteractorStyle(self)
        self.iren.SetInteractorStyle(self.style)
        self.iren.SetRenderWindow(self.renWin)

        # Render decimation
        self.decimate = vtk.vtkDecimatePro()

        self.ren.SetBackground(.1, .2, .4)

        self.actors = {}

        # Help text
        self.helpActor = vtk.vtkActor2D()
        self.helpActor.GetPositionCoordinate(
        ).SetCoordinateSystemToNormalizedDisplay()
        self.helpActor.GetPositionCoordinate().SetValue(0.1, 0.5)
        self.helpActor.VisibilityOff()
        self.ren.AddActor(self.helpActor)

        # volume render
        volumeMapper = vtk.vtkSmartVolumeMapper()
        #volumeMapper = vtk.vtkFixedPointVolumeRayCastMapper()
        self.volume_mapper = volumeMapper

        volumeProperty = vtk.vtkVolumeProperty()
        self.volume_property = volumeProperty

        # The volume holds the mapper and the property and
        # can be used to position/orient the volume.
        volume = vtk.vtkVolume()
        volume.SetMapper(volumeMapper)
        volume.SetProperty(volumeProperty)
        self.volume = volume
        self.volume_render_initialised = False

        # axis orientation widget
        om = vtk.vtkAxesActor()
        ori = vtk.vtkOrientationMarkerWidget()
        ori.SetOutlineColor(0.9300, 0.5700, 0.1300)
        ori.SetInteractor(self.iren)
        ori.SetOrientationMarker(om)
        ori.SetViewport(0.0, 0.0, 0.4, 0.4)
        ori.SetEnabled(1)
        ori.InteractiveOff()
        self.orientation_marker = ori

        self.iren.Initialize()
  def run(self,enableScreenshots=0,screenshotScaleFactor=1):
    """
    Run the actual algorithm
    """

    slicer.util.delayDisplay('Running the algorithm',500)

    self.enableScreenshots = enableScreenshots
    self.screenshotScaleFactor = screenshotScaleFactor

    # Start in conventional layout
    lm = slicer.app.layoutManager()
    lm.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView)
    # without this delayed display, when running from the cmd line Slicer starts
    # up in a different layout and the seed won't get rendered in the right spot
    slicer.util.delayDisplay("Conventional view",500)

    # Download MRHead from sample data
    import SampleData
    sampleDataLogic = SampleData.SampleDataLogic()
    print("Getting MR Head Volume")
    mrHeadVolume = sampleDataLogic.downloadMRHead()

    # Place a fiducial on the red slice
    markupsLogic = slicer.modules.markups.logic()
    eye = [33.4975, 79.4042, -10.2143]
    fidIndex = markupsLogic.AddFiducial(eye[0], eye[1], eye[2])
    fidID = markupsLogic.GetActiveListID()
    fidNode = slicer.mrmlScene.GetNodeByID(fidID)
    slicer.util.delayDisplay("Placed a fiducial at %g, %g, %g" % (eye[0], eye[1], eye[2]),500)

    # Pan and zoom
    sliceWidget = slicer.app.layoutManager().sliceWidget('Red')
    sliceLogic = sliceWidget.sliceLogic()
    compositeNode = sliceLogic.GetSliceCompositeNode()
    sliceNode = sliceLogic.GetSliceNode()
    sliceNode.SetXYZOrigin(-71.7, 129.7, 0.0)
    sliceNode.SetFieldOfView(98.3, 130.5, 1.0)
    slicer.util.delayDisplay("Panned and zoomed",500)

    # Get the seed widget seed location
    startingSeedDisplayCoords = [0.0, 0.0, 0.0]
    helper = self.getFiducialSliceDisplayableManagerHelper('Red')
    if helper != None:
     seedWidget = helper.GetWidget(fidNode)
     seedRepresentation = seedWidget.GetSeedRepresentation()
     handleRep = seedRepresentation.GetHandleRepresentation(fidIndex)
     startingSeedDisplayCoords = handleRep.GetDisplayPosition()
     print('Starting seed display coords = %d, %d, %d' % (startingSeedDisplayCoords[0], startingSeedDisplayCoords[1], startingSeedDisplayCoords[2]))
    self.takeScreenshot('FiducialLayoutSwitchBug1914-StartingPosition','Fiducial starting position',slicer.qMRMLScreenShotDialog.Red)

    # Switch to red slice only
    lm.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpRedSliceView)
    slicer.util.delayDisplay("Red Slice only",500)

    # Switch to conventional layout
    print 'Calling set layout back to conventional'
    lm.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView)
    print 'Done calling set layout back to conventional'
    slicer.util.delayDisplay("Conventional layout",500)

    # Get the current seed widget seed location
    endingSeedDisplayCoords = [0.0, 0.0, 0.0]
    helper = self.getFiducialSliceDisplayableManagerHelper('Red')
    if helper != None:
     seedWidget = helper.GetWidget(fidNode)
     seedRepresentation = seedWidget.GetSeedRepresentation()
     handleRep = seedRepresentation.GetHandleRepresentation(fidIndex)
     endingSeedDisplayCoords = handleRep.GetDisplayPosition()
     print('Ending seed display coords = %d, %d, %d' % (endingSeedDisplayCoords[0], endingSeedDisplayCoords[1], endingSeedDisplayCoords[2]))
    self.takeScreenshot('FiducialLayoutSwitchBug1914-EndingPosition','Fiducial ending position',slicer.qMRMLScreenShotDialog.Red)

    # Compare to original seed widget location
    diff = math.pow((startingSeedDisplayCoords[0] - endingSeedDisplayCoords[0]),2) + math.pow((startingSeedDisplayCoords[1] - endingSeedDisplayCoords[1]),2) + math.pow((startingSeedDisplayCoords[2] - endingSeedDisplayCoords[2]),2)
    if diff != 0.0:
      diff = math.sqrt(diff)
    slicer.util.delayDisplay("Difference between starting and ending seed display coordinates = %g" % diff)

    if diff > self.maximumDisplayDifference:
      # double check against the RAS coordinates of the underlying volume since the display could have changed with a FOV adjustment.
      sliceView = sliceWidget.sliceView()
      volumeRAS = sliceView.convertXYZToRAS(endingSeedDisplayCoords)
      seedRAS = [0,0,0]
      fidNode.GetNthFiducialPosition(0,seedRAS)
      rasDiff = math.pow((seedRAS[0] - volumeRAS[0]),2) + math.pow((seedRAS[1] - volumeRAS[1]),2) + math.pow((seedRAS[2] - volumeRAS[2]),2)
      if rasDiff != 0.0:
        rasDiff = math.sqrt(rasDiff)
      print 'Checking the difference between fiducial RAS position',seedRAS,'and volume RAS as derived from the fiducial display position',volumeRAS,': ',rasDiff
      if rasDiff > self.maximumRASDifference:
        slicer.util.delayDisplay("RAS coordinate difference is too large as well!\nExpected < %g but got %g" % (self.maximumRASDifference, rasDiff))
        return False
      else:
        slicer.util.delayDisplay("RAS coordinate difference is %g which is < %g, test passes." % (rasDiff, self.maximumRASDifference))

    if enableScreenshots == 1:
      # compare the screen snapshots
      startView = slicer.mrmlScene.GetFirstNodeByName('FiducialLayoutSwitchBug1914-StartingPosition')
      startShot = startView.GetScreenShot()
      endView = slicer.mrmlScene.GetFirstNodeByName('FiducialLayoutSwitchBug1914-EndingPosition')
      endShot = endView.GetScreenShot()
      imageMath = vtk.vtkImageMathematics()
      imageMath.SetOperationToSubtract()
      imageMath.SetInput1(startShot)
      imageMath.SetInput2(endShot)
      imageMath.Update()
      shotDiff = imageMath.GetOutput()
      # save it as a scene view
      annotationLogic = slicer.modules.annotations.logic()
      annotationLogic.CreateSnapShot("FiducialLayoutSwitchBug1914-Diff", "Difference between starting and ending fiducial seed positions",slicer.qMRMLScreenShotDialog.Red, screenshotScaleFactor, shotDiff)
      # calculate the image difference
      imageStats = vtk.vtkImageHistogramStatistics()
      imageStats.SetInput(shotDiff)
      imageStats.GenerateHistogramImageOff()
      imageStats.Update()
      meanVal = imageStats.GetMean()
      slicer.util.delayDisplay("Mean of image difference = %g" % meanVal)
      if meanVal > 5.0:
        # raise Exception("Image difference is too great!\nExpected <= 5.0, but got %g" % (meanVal))
        print("Image difference is too great!\nExpected <= 5.0, but got %g" % (meanVal))
        return False

    slicer.util.delayDisplay('Test passed!')
    return True
예제 #11
0
    def __init__(self,
                 grayscaleNode,
                 labelNode,
                 colorNode=None,
                 nodeBaseName=None,
                 fileName=None):
        #import numpy

        self.keys = ("Index", "Count", "Volume mm^3", "Volume cc", "Min",
                     "Max", "Mean", "Median", "StdDev")
        cubicMMPerVoxel = reduce(lambda x, y: x * y, labelNode.GetSpacing())
        ccPerCubicMM = 0.001

        # TODO: progress and status updates
        # this->InvokeEvent(vtkLabelStatisticsLogic::StartLabelStats, (void*)"start label stats")

        self.labelNode = labelNode
        self.colorNode = colorNode

        self.nodeBaseName = nodeBaseName
        if not self.nodeBaseName:
            self.nodeBaseName = labelNode.GetName() if labelNode.GetName(
            ) else 'Labels'

        self.labelStats = {}
        self.labelStats['Labels'] = []

        if (not labelNode.GetImageData()
                or not labelNode.GetImageData().GetPointData()
                or not labelNode.GetImageData().GetPointData().GetScalars()):
            # No input label data
            return

        if (not grayscaleNode.GetImageData()
                or not grayscaleNode.GetImageData().GetPointData() or
                not grayscaleNode.GetImageData().GetPointData().GetScalars()):
            # No input grayscale image data
            return

        stataccum = vtk.vtkImageAccumulate()
        stataccum.SetInputConnection(labelNode.GetImageDataConnection())
        stataccum.Update()
        lo = int(stataccum.GetMin()[0])
        hi = int(stataccum.GetMax()[0])

        for i in range(lo, hi + 1):

            # this->SetProgress((float)i/hi);
            # std::string event_message = "Label "; std::stringstream s; s << i; event_message.append(s.str());
            # this->InvokeEvent(vtkLabelStatisticsLogic::LabelStatsOuterLoop, (void*)event_message.c_str());

            # logic copied from slicer3 LabelStatistics
            # to create the binary volume of the label
            # //logic copied from slicer2 LabelStatistics MaskStat
            # // create the binary volume of the label
            thresholder = vtk.vtkImageThreshold()
            thresholder.SetInputConnection(labelNode.GetImageDataConnection())
            thresholder.SetInValue(1)
            thresholder.SetOutValue(0)
            thresholder.ReplaceOutOn()
            thresholder.ThresholdBetween(i, i)
            thresholder.SetOutputScalarType(
                grayscaleNode.GetImageData().GetScalarType())
            thresholder.Update()

            # this.InvokeEvent(vtkLabelStatisticsLogic::LabelStatsInnerLoop, (void*)"0.25");

            #  use vtk's statistics class with the binary labelmap as a stencil
            stencil = vtk.vtkImageToImageStencil()
            stencil.SetInputConnection(thresholder.GetOutputPort())
            stencil.ThresholdBetween(1, 1)

            # this.InvokeEvent(vtkLabelStatisticsLogic::LabelStatsInnerLoop, (void*)"0.5")

            stat1 = vtk.vtkImageAccumulate()
            stat1.SetInputConnection(grayscaleNode.GetImageDataConnection())
            stencil.Update()
            stat1.SetStencilData(stencil.GetOutput())

            stat1.Update()

            medians = vtk.vtkImageHistogramStatistics()
            medians.SetInputConnection(grayscaleNode.GetImageDataConnection())
            stencil.Update()
            medians.SetStencilData(stencil.GetOutput())

            medians.Update()

            # this.InvokeEvent(vtkLabelStatisticsLogic::LabelStatsInnerLoop, (void*)"0.75")

            if stat1.GetVoxelCount() > 0:
                # add an entry to the LabelStats list
                self.labelStats["Labels"].append(i)
                self.labelStats[i, "Index"] = i
                self.labelStats[i, "Count"] = stat1.GetVoxelCount()
                self.labelStats[i, "Volume mm^3"] = self.labelStats[
                    i, "Count"] * cubicMMPerVoxel
                self.labelStats[i, "Volume cc"] = self.labelStats[
                    i, "Volume mm^3"] * ccPerCubicMM
                self.labelStats[i, "Min"] = stat1.GetMin()[0]
                self.labelStats[i, "Max"] = stat1.GetMax()[0]
                self.labelStats[i, "Mean"] = stat1.GetMean()[0]
                self.labelStats[i, "Median"] = medians.GetMedian()
                self.labelStats[i, "StdDev"] = stat1.GetStandardDeviation()[0]
예제 #12
0
  def computeStatistics(self, segmentID):
    import vtkSegmentationCorePython as vtkSegmentationCore
    requestedKeys = self.getRequestedKeys()

    segmentationNode = slicer.mrmlScene.GetNodeByID(self.getParameterNode().GetParameter("Segmentation"))
    grayscaleNode = slicer.mrmlScene.GetNodeByID(self.getParameterNode().GetParameter("ScalarVolume"))

    if len(requestedKeys)==0:
      return {}

    containsLabelmapRepresentation = segmentationNode.GetSegmentation().ContainsRepresentation(
      vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName())
    if not containsLabelmapRepresentation:
      return {}

    if (not grayscaleNode
      or not grayscaleNode.GetImageData()
      or not grayscaleNode.GetImageData().GetPointData()
      or not grayscaleNode.GetImageData().GetPointData().GetScalars()):
      # Input grayscale node does not contain valid image data
      return {}

    # Get geometry of grayscale volume node as oriented image data
    # reference geometry in reference node coordinate system
    referenceGeometry_Reference = vtkSegmentationCore.vtkOrientedImageData()
    referenceGeometry_Reference.SetExtent(grayscaleNode.GetImageData().GetExtent())
    ijkToRasMatrix = vtk.vtkMatrix4x4()
    grayscaleNode.GetIJKToRASMatrix(ijkToRasMatrix)
    referenceGeometry_Reference.SetGeometryFromImageToWorldMatrix(ijkToRasMatrix)

    # Get transform between grayscale volume and segmentation
    segmentationToReferenceGeometryTransform = vtk.vtkGeneralTransform()
    slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(segmentationNode.GetParentTransformNode(),
      grayscaleNode.GetParentTransformNode(), segmentationToReferenceGeometryTransform)

    cubicMMPerVoxel = reduce(lambda x,y: x*y, referenceGeometry_Reference.GetSpacing())
    ccPerCubicMM = 0.001

    segmentLabelmap = vtkSegmentationCore.vtkOrientedImageData()
    segmentationNode.GetBinaryLabelmapRepresentation(segmentID, segmentLabelmap)
    if (not segmentLabelmap
      or not segmentLabelmap.GetPointData()
      or not segmentLabelmap.GetPointData().GetScalars()):
      # No input label data
      return {}

    segmentLabelmap_Reference = vtkSegmentationCore.vtkOrientedImageData()
    vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
      segmentLabelmap, referenceGeometry_Reference, segmentLabelmap_Reference,
      False, # nearest neighbor interpolation
      False, # no padding
      segmentationToReferenceGeometryTransform)

    # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value
    labelValue = 1
    backgroundValue = 0
    thresh = vtk.vtkImageThreshold()
    thresh.SetInputData(segmentLabelmap_Reference)
    thresh.ThresholdByLower(0)
    thresh.SetInValue(backgroundValue)
    thresh.SetOutValue(labelValue)
    thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
    thresh.Update()

    #  Use binary labelmap as a stencil
    stencil = vtk.vtkImageToImageStencil()
    stencil.SetInputData(thresh.GetOutput())
    stencil.ThresholdByUpper(labelValue)
    stencil.Update()

    stat = vtk.vtkImageAccumulate()
    stat.SetInputData(grayscaleNode.GetImageData())
    stat.SetStencilData(stencil.GetOutput())
    stat.Update()

    medians = vtk.vtkImageHistogramStatistics()
    medians.SetInputData(grayscaleNode.GetImageData())
    medians.SetStencilData(stencil.GetOutput())
    medians.Update()

    # create statistics list
    stats = {}
    if "voxel_count" in requestedKeys:
      stats["voxel_count"] = stat.GetVoxelCount()
    if "volume_mm3" in requestedKeys:
      stats["volume_mm3"] = stat.GetVoxelCount() * cubicMMPerVoxel
    if "volume_cm3" in requestedKeys:
      stats["volume_cm3"] = stat.GetVoxelCount() * cubicMMPerVoxel * ccPerCubicMM
    if stat.GetVoxelCount()>0:
      if "min" in requestedKeys:
        stats["min"] = stat.GetMin()[0]
      if "max" in requestedKeys:
        stats["max"] = stat.GetMax()[0]
      if "mean" in requestedKeys:
        stats["mean"] = stat.GetMean()[0]
      if "stdev" in requestedKeys:
        stats["stdev"] = stat.GetStandardDeviation()[0]
      if "median" in requestedKeys:
        stats["median"] = medians.GetMedian()
    return stats
예제 #13
0
  def __init__(self, grayscaleNode, labelNode, colorNode=None, nodeBaseName=None, fileName=None):
    #import numpy

    self.keys = ("Index", "Count", "Volume mm^3", "Volume cc", "Min", "Max", "Mean", "Median", "StdDev")
    cubicMMPerVoxel = reduce(lambda x,y: x*y, labelNode.GetSpacing())
    ccPerCubicMM = 0.001

    # TODO: progress and status updates
    # this->InvokeEvent(vtkLabelStatisticsLogic::StartLabelStats, (void*)"start label stats")

    self.labelNode = labelNode
    self.colorNode = colorNode

    self.nodeBaseName = nodeBaseName
    if not self.nodeBaseName:
      self.nodeBaseName = labelNode.GetName() if labelNode.GetName() else 'Labels'

    self.labelStats = {}
    self.labelStats['Labels'] = []

    if (not labelNode.GetImageData()
      or not labelNode.GetImageData().GetPointData()
      or not labelNode.GetImageData().GetPointData().GetScalars()):
      # No input label data
      return

    if (not grayscaleNode.GetImageData()
      or not grayscaleNode.GetImageData().GetPointData()
      or not grayscaleNode.GetImageData().GetPointData().GetScalars()):
      # No input grayscale image data
      return

    stataccum = vtk.vtkImageAccumulate()
    stataccum.SetInputConnection(labelNode.GetImageDataConnection())
    stataccum.Update()
    lo = int(stataccum.GetMin()[0])
    hi = int(stataccum.GetMax()[0])

    for i in range(lo,hi+1):

      # this->SetProgress((float)i/hi);
      # std::string event_message = "Label "; std::stringstream s; s << i; event_message.append(s.str());
      # this->InvokeEvent(vtkLabelStatisticsLogic::LabelStatsOuterLoop, (void*)event_message.c_str());

      # logic copied from slicer3 LabelStatistics
      # to create the binary volume of the label
      # //logic copied from slicer2 LabelStatistics MaskStat
      # // create the binary volume of the label
      thresholder = vtk.vtkImageThreshold()
      thresholder.SetInputConnection(labelNode.GetImageDataConnection())
      thresholder.SetInValue(1)
      thresholder.SetOutValue(0)
      thresholder.ReplaceOutOn()
      thresholder.ThresholdBetween(i,i)
      thresholder.SetOutputScalarType(grayscaleNode.GetImageData().GetScalarType())
      thresholder.Update()

      # this.InvokeEvent(vtkLabelStatisticsLogic::LabelStatsInnerLoop, (void*)"0.25");

      #  use vtk's statistics class with the binary labelmap as a stencil
      stencil = vtk.vtkImageToImageStencil()
      stencil.SetInputConnection(thresholder.GetOutputPort())
      stencil.ThresholdBetween(1, 1)

      # this.InvokeEvent(vtkLabelStatisticsLogic::LabelStatsInnerLoop, (void*)"0.5")

      stat1 = vtk.vtkImageAccumulate()
      stat1.SetInputConnection(grayscaleNode.GetImageDataConnection())
      stencil.Update()
      stat1.SetStencilData(stencil.GetOutput())

      stat1.Update()

      medians = vtk.vtkImageHistogramStatistics()
      medians.SetInputConnection(grayscaleNode.GetImageDataConnection())
      stencil.Update()
      medians.SetStencilData(stencil.GetOutput())

      medians.Update()

      # this.InvokeEvent(vtkLabelStatisticsLogic::LabelStatsInnerLoop, (void*)"0.75")

      if stat1.GetVoxelCount() > 0:
        # add an entry to the LabelStats list
        self.labelStats["Labels"].append(i)
        self.labelStats[i,"Index"] = i
        self.labelStats[i,"Count"] = stat1.GetVoxelCount()
        self.labelStats[i,"Volume mm^3"] = self.labelStats[i,"Count"] * cubicMMPerVoxel
        self.labelStats[i,"Volume cc"] = self.labelStats[i,"Volume mm^3"] * ccPerCubicMM
        self.labelStats[i,"Min"] = stat1.GetMin()[0]
        self.labelStats[i,"Max"] = stat1.GetMax()[0]
        self.labelStats[i,"Mean"] = stat1.GetMean()[0]
        self.labelStats[i,"Median"] = medians.GetMedian()
        self.labelStats[i,"StdDev"] = stat1.GetStandardDeviation()[0]
  def computeStatistics(self, segmentID):
    import vtkSegmentationCorePython as vtkSegmentationCore
    requestedKeys = self.getRequestedKeys()

    segmentationNode = slicer.mrmlScene.GetNodeByID(self.getParameterNode().GetParameter("Segmentation"))
    grayscaleNode = slicer.mrmlScene.GetNodeByID(self.getParameterNode().GetParameter("ScalarVolume"))

    if len(requestedKeys)==0:
      return {}

    containsLabelmapRepresentation = segmentationNode.GetSegmentation().ContainsRepresentation(
      vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName())
    if not containsLabelmapRepresentation:
      return {}

    if grayscaleNode is None or grayscaleNode.GetImageData() is None:
      return {}

    # Get geometry of grayscale volume node as oriented image data
    # reference geometry in reference node coordinate system
    referenceGeometry_Reference = vtkSegmentationCore.vtkOrientedImageData()
    referenceGeometry_Reference.SetExtent(grayscaleNode.GetImageData().GetExtent())
    ijkToRasMatrix = vtk.vtkMatrix4x4()
    grayscaleNode.GetIJKToRASMatrix(ijkToRasMatrix)
    referenceGeometry_Reference.SetGeometryFromImageToWorldMatrix(ijkToRasMatrix)

    # Get transform between grayscale volume and segmentation
    segmentationToReferenceGeometryTransform = vtk.vtkGeneralTransform()
    slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(segmentationNode.GetParentTransformNode(),
      grayscaleNode.GetParentTransformNode(), segmentationToReferenceGeometryTransform)

    cubicMMPerVoxel = reduce(lambda x,y: x*y, referenceGeometry_Reference.GetSpacing())
    ccPerCubicMM = 0.001

    segment = segmentationNode.GetSegmentation().GetSegment(segmentID)
    segBinaryLabelName = vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName()
    segmentLabelmap = segment.GetRepresentation(segBinaryLabelName)

    segmentLabelmap_Reference = vtkSegmentationCore.vtkOrientedImageData()
    vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
      segmentLabelmap, referenceGeometry_Reference, segmentLabelmap_Reference,
      False, # nearest neighbor interpolation
      False, # no padding
      segmentationToReferenceGeometryTransform)

    # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value
    labelValue = 1
    backgroundValue = 0
    thresh = vtk.vtkImageThreshold()
    thresh.SetInputData(segmentLabelmap_Reference)
    thresh.ThresholdByLower(0)
    thresh.SetInValue(backgroundValue)
    thresh.SetOutValue(labelValue)
    thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
    thresh.Update()

    #  Use binary labelmap as a stencil
    stencil = vtk.vtkImageToImageStencil()
    stencil.SetInputData(thresh.GetOutput())
    stencil.ThresholdByUpper(labelValue)
    stencil.Update()

    stat = vtk.vtkImageAccumulate()
    stat.SetInputData(grayscaleNode.GetImageData())
    stat.SetStencilData(stencil.GetOutput())
    stat.Update()

    medians = vtk.vtkImageHistogramStatistics()
    medians.SetInputData(grayscaleNode.GetImageData())
    medians.SetStencilData(stencil.GetOutput())
    medians.Update()

    # create statistics list
    stats = {}
    if "voxel_count" in requestedKeys:
      stats["voxel_count"] = stat.GetVoxelCount()
    if "volume_mm3" in requestedKeys:
      stats["volume_mm3"] = stat.GetVoxelCount() * cubicMMPerVoxel
    if "volume_cm3" in requestedKeys:
      stats["volume_cm3"] = stat.GetVoxelCount() * cubicMMPerVoxel * ccPerCubicMM
    if stat.GetVoxelCount()>0:
      if "min" in requestedKeys:
        stats["min"] = stat.GetMin()[0]
      if "max" in requestedKeys:
        stats["max"] = stat.GetMax()[0]
      if "mean" in requestedKeys:
        stats["mean"] = stat.GetMean()[0]
      if "stdev" in requestedKeys:
        stats["stdev"] = stat.GetStandardDeviation()[0]
      if "median" in requestedKeys:
        stats["median"] = medians.GetMedian()
    return stats