Esempio n. 1
0
    def onApply(self):
        # Make sure the user wants to do the operation, even if the segment is not visible
        if not self.scriptedEffect.confirmCurrentSegmentVisible():
            return

        self.scriptedEffect.saveStateForUndo()

        # Get modifier labelmap and parameters
        modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
        selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()

        # 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(selectedSegmentLabelmap)
        thresh.ThresholdByLower(0)
        thresh.SetInValue(backgroundValue)
        thresh.SetOutValue(labelValue)
        thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())

        shellMode = self.scriptedEffect.parameter("ShellMode")
        shellThicknessMM = abs(
            self.scriptedEffect.doubleParameter("ShellThicknessMm"))
        import vtkITK
        margin = vtkITK.vtkITKImageMargin()
        margin.SetInputConnection(thresh.GetOutputPort())
        margin.CalculateMarginInMMOn()

        spacing = selectedSegmentLabelmap.GetSpacing()
        voxelDiameter = min(selectedSegmentLabelmap.GetSpacing())
        if shellMode == MEDIAL_SURFACE:
            margin.SetOuterMarginMM(0.5 * shellThicknessMM)
            margin.SetInnerMarginMM(-0.5 * shellThicknessMM +
                                    0.5 * voxelDiameter)
        elif shellMode == INSIDE_SURFACE:
            margin.SetOuterMarginMM(shellThicknessMM + 0.1 * voxelDiameter)
            margin.SetInnerMarginMM(
                0.0 +
                0.1 * voxelDiameter)  # Don't include the original border (0.0)
        elif shellMode == OUTSIDE_SURFACE:
            margin.SetOuterMarginMM(0.0)
            margin.SetInnerMarginMM(-shellThicknessMM + voxelDiameter)

        modifierLabelmap.DeepCopy(margin.GetOutput())

        # This can be a long operation - indicate it to the user
        qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)

        margin.Update()
        modifierLabelmap.ShallowCopy(margin.GetOutput())

        # Apply changes
        self.scriptedEffect.modifySelectedSegmentByLabelmap(
            modifierLabelmap,
            slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)

        qt.QApplication.restoreOverrideCursor()
    def onApply(self):
        # Make sure the user wants to do the operation, even if the segment is not visible
        if not self.scriptedEffect.confirmCurrentSegmentVisible():
            return

        self.scriptedEffect.saveStateForUndo()

        # Get modifier labelmap and parameters
        modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
        selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()

        marginSizeMM = self.scriptedEffect.doubleParameter("MarginSizeMm")

        # 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(selectedSegmentLabelmap)
        thresh.ThresholdByLower(0)
        thresh.SetInValue(backgroundValue)
        thresh.SetOutValue(labelValue)
        thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
        if (marginSizeMM < 0):
            # The distance filter used in the margin filter starts at zero at the border voxels,
            # so if we need to shrink the margin, it is more accurate to invert the labelmap and
            # use positive distance when calculating the margin
            thresh.SetInValue(labelValue)
            thresh.SetOutValue(backgroundValue)

        import vtkITK
        margin = vtkITK.vtkITKImageMargin()
        margin.SetInputConnection(thresh.GetOutputPort())
        margin.CalculateMarginInMMOn()
        margin.SetOuterMarginMM(abs(marginSizeMM))
        margin.Update()

        if marginSizeMM >= 0:
            modifierLabelmap.ShallowCopy(margin.GetOutput())
        else:
            # If we are shrinking then the result needs to be inverted.
            thresh = vtk.vtkImageThreshold()
            thresh.SetInputData(margin.GetOutput())
            thresh.ThresholdByLower(0)
            thresh.SetInValue(labelValue)
            thresh.SetOutValue(backgroundValue)
            thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
            thresh.Update()
            modifierLabelmap.ShallowCopy(thresh.GetOutput())

        # Apply changes
        self.scriptedEffect.modifySelectedSegmentByLabelmap(
            modifierLabelmap,
            slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)

        qt.QApplication.restoreOverrideCursor()
    def processHollowing(self):
        # Get modifier labelmap and parameters
        modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
        selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
        # 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(selectedSegmentLabelmap)
        thresh.ThresholdByLower(0)
        thresh.SetInValue(backgroundValue)
        thresh.SetOutValue(labelValue)
        thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())

        shellMode = self.scriptedEffect.parameter("ShellMode")
        shellThicknessMM = abs(
            self.scriptedEffect.doubleParameter("ShellThicknessMm"))
        import vtkITK
        margin = vtkITK.vtkITKImageMargin()
        margin.SetInputConnection(thresh.GetOutputPort())
        margin.CalculateMarginInMMOn()

        spacing = selectedSegmentLabelmap.GetSpacing()
        voxelDiameter = min(selectedSegmentLabelmap.GetSpacing())
        if shellMode == MEDIAL_SURFACE:
            margin.SetOuterMarginMM(0.5 * shellThicknessMM)
            margin.SetInnerMarginMM(-0.5 * shellThicknessMM +
                                    0.5 * voxelDiameter)
        elif shellMode == INSIDE_SURFACE:
            margin.SetOuterMarginMM(shellThicknessMM + 0.1 * voxelDiameter)
            margin.SetInnerMarginMM(
                0.0 +
                0.1 * voxelDiameter)  # Don't include the original border (0.0)
        elif shellMode == OUTSIDE_SURFACE:
            margin.SetOuterMarginMM(0.0)
            margin.SetInnerMarginMM(-shellThicknessMM + voxelDiameter)

        modifierLabelmap.DeepCopy(margin.GetOutput())

        margin.Update()
        modifierLabelmap.ShallowCopy(margin.GetOutput())

        # Apply changes
        self.scriptedEffect.modifySelectedSegmentByLabelmap(
            modifierLabelmap,
            slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
Esempio n. 4
0
    def onApply(self):
        # Make sure the user wants to do the operation, even if the segment is not visible
        if not self.scriptedEffect.confirmCurrentSegmentVisible():
            return

        self.scriptedEffect.saveStateForUndo()

        # Get modifier labelmap and parameters
        modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
        selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()

        # 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(selectedSegmentLabelmap)
        thresh.ThresholdByLower(0)
        thresh.SetInValue(backgroundValue)
        thresh.SetOutValue(labelValue)
        thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())

        marginSizeMM = self.scriptedEffect.doubleParameter("MarginSizeMm")
        if (marginSizeMM < 0):
            # The border voxel starts at zero, if we need to account for this single voxel thickness in the margin size.
            # Currently this is done by reducing the magnitude of the margin size by 0.9 * the smallest spacing.
            voxelDiameter = min(selectedSegmentLabelmap.GetSpacing())
            marginSizeMM += 0.9 * voxelDiameter

        import vtkITK
        margin = vtkITK.vtkITKImageMargin()
        margin.SetInputConnection(thresh.GetOutputPort())
        margin.CalculateMarginInMMOn()
        margin.SetOuterMarginMM(marginSizeMM)
        margin.Update()
        modifierLabelmap.ShallowCopy(margin.GetOutput())

        # Apply changes
        self.scriptedEffect.modifySelectedSegmentByLabelmap(
            modifierLabelmap,
            slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)

        qt.QApplication.restoreOverrideCursor()
Esempio n. 5
0
    def _extractCavity(self, shrunkenPd):

        spacing = self._inputSpacing / self.remeshOversampling
        outsideObjectLabelmap = WrapSolidifyLogic._polydataToLabelmap(
            shrunkenPd, spacing)  # 0=outside, 1=inside

        inputLabelmap = WrapSolidifyLogic._polydataToLabelmap(
            self._inputPd, referenceImage=outsideObjectLabelmap)

        if self.splitCavities:
            # It is less accurate but more robust to dilate labelmap than grow polydata.
            # Since accuracy is not important here, we dilate labelmap.
            splitCavitiesRadius = self.splitCavitiesDiameter / 2.0
            # Dilate
            import vtkITK
            margin = vtkITK.vtkITKImageMargin()
            margin.SetInputData(inputLabelmap)
            margin.CalculateMarginInMMOn()
            margin.SetOuterMarginMM(splitCavitiesRadius)
            margin.Update()
            # extendedInputLabelmap: 255 near original inputLabelmap voxels, 0 elsewhere
            extendedInputLabelmap = margin.GetOutput()
            self._saveIntermediateResult(
                "SplitCavitiesGrown",
                WrapSolidifyLogic._labelmapToPolydata(extendedInputLabelmap,
                                                      255))
        else:
            extendedInputLabelmap = inputLabelmap

        outsideObjectLabelmapInverter = vtk.vtkImageThreshold()
        outsideObjectLabelmapInverter.SetInputData(outsideObjectLabelmap)
        outsideObjectLabelmapInverter.ThresholdByLower(0)
        outsideObjectLabelmapInverter.SetInValue(1)  # backgroundValue
        outsideObjectLabelmapInverter.SetOutValue(0)  # labelValue
        outsideObjectLabelmapInverter.SetOutputScalarType(
            outsideObjectLabelmap.GetScalarType())
        outsideObjectLabelmapInverter.Update()

        addImage = vtk.vtkImageMathematics()
        addImage.SetInput1Data(outsideObjectLabelmapInverter.GetOutput())
        addImage.SetInput2Data(extendedInputLabelmap)
        addImage.SetOperationToMax()
        addImage.Update()
        internalHolesLabelmap = addImage.GetOutput()
        # internal holes are 0, elsewhere >=1
        self._saveIntermediateResult(
            "SplitCavitiesAll",
            WrapSolidifyLogic._labelmapToPolydata(internalHolesLabelmap, 0))

        # Find largest internal hole
        largestHoleExtract = vtk.vtkImageConnectivityFilter()
        largestHoleExtract.SetScalarRange(-0.5, 0.5)
        largestHoleExtract.SetInputData(internalHolesLabelmap)
        largestHoleExtract.SetExtractionModeToLargestRegion()
        largestHoleExtract.Update()
        largestHolesLabelmap = largestHoleExtract.GetOutput()

        # Convert back to polydata
        initialRegionPd = vtk.vtkPolyData()
        initialRegionPd.DeepCopy(
            WrapSolidifyLogic._labelmapToPolydata(largestHolesLabelmap, 0))
        self._saveIntermediateResult("SplitCavitiesLargest", initialRegionPd)

        if self.splitCavities:
            return self._shrinkWrap(initialRegionPd)
        else:
            return initialRegionPd
Esempio n. 6
0
    def _getInitialRegionPd(self):
        """Get initial shape that will be snapped to closest point of the input segment"""

        spacing = self._inputSpacing / self.remeshOversampling

        if self.carveHolesInOuterSurface:
            # Grow input polydata to close holes between outer surface and internal cavities.

            # It is less accurate but more robust to dilate labelmap than grow polydata.
            # Since accuracy is not important here, we dilate labelmap.
            # Convert to labelmap
            carveHolesInOuterSurfaceRadius = self.carveHolesInOuterSurfaceDiameter / 2.0
            # add self.carveHolesInOuterSurfaceDiameter extra to bounds to ensure that the grown input still has an margin around
            inputLabelmap = WrapSolidifyLogic._polydataToLabelmap(
                self._inputPd,
                spacing,
                extraMarginToBounds=carveHolesInOuterSurfaceRadius)

            self._saveIntermediateResult(
                "InitialRegionResampled",
                WrapSolidifyLogic._labelmapToPolydata(inputLabelmap, 1))

            # Dilate
            import vtkITK
            margin = vtkITK.vtkITKImageMargin()
            margin.SetInputData(inputLabelmap)
            margin.CalculateMarginInMMOn()
            margin.SetOuterMarginMM(carveHolesInOuterSurfaceRadius)
            margin.Update()
            # extendedInputLabelmap: 255 near original inputLabelmap voxels, 0 elsewhere
            extendedInputLabelmap = margin.GetOutput()
            self._saveIntermediateResult(
                "InitialRegionGrown",
                WrapSolidifyLogic._labelmapToPolydata(extendedInputLabelmap,
                                                      255))

            # Region growing from a corner of the image
            seedPoints = vtk.vtkPoints()
            seedPoints.InsertNextPoint(
                inputLabelmap.GetOrigin()[0] + spacing / 2,
                inputLabelmap.GetOrigin()[1] + spacing / 2,
                inputLabelmap.GetOrigin()[2] + spacing / 2)
            seedScalars = vtk.vtkUnsignedCharArray()
            seedScalars.InsertNextValue(
                255)  # this will be the label value to the grown region
            seedData = vtk.vtkPolyData()
            seedData.SetPoints(seedPoints)
            seedData.GetPointData().SetScalars(seedScalars)

            regionGrowing = vtk.vtkImageConnectivityFilter()
            regionGrowing.SetSeedData(seedData)
            regionGrowing.SetScalarRange(-10, 10)
            regionGrowing.SetInputData(extendedInputLabelmap)
            regionGrowing.Update()

            # outsideObjectImage: 255 outside the object, 0 inside
            outsideObjectLabelmap = regionGrowing.GetOutput()

        # Cavity extraction and outer surface extraction starts from the same outer surface shrinkwrap
        if self.region == REGION_OUTER_SURFACE or self.region == REGION_LARGEST_CAVITY:
            if self.carveHolesInOuterSurface:
                # Convert back to polydata
                initialRegionPd = vtk.vtkPolyData()
                initialRegionPd.DeepCopy(
                    WrapSolidifyLogic._labelmapToPolydata(
                        outsideObjectLabelmap, 255))
            else:
                # create sphere that encloses entire segment content
                bounds = np.zeros(6)
                self._inputPd.GetBounds(bounds)
                diameters = np.array([
                    bounds[1] - bounds[0], bounds[3] - bounds[2],
                    bounds[5] - bounds[4]
                ])
                maxRadius = max(diameters) / 2.0
                sphereSource = vtk.vtkSphereSource()
                # to make sure the volume is fully included in the sphere, radius must be sqrt(2) times larger
                sphereSource.SetRadius(maxRadius * 1.5)

                # Set resolution to be about one magnitude lower than the final resolution
                # (by creating an initial surface element for about every 100th final element).
                sphereSurfaceArea = 4 * math.pi * maxRadius * maxRadius
                voxelSurfaceArea = spacing * spacing
                numberOfSurfaceElements = sphereSurfaceArea / voxelSurfaceArea
                numberOfIinitialSphereSurfaceElements = numberOfSurfaceElements / 100
                sphereResolution = math.sqrt(
                    numberOfIinitialSphereSurfaceElements)
                # Set resolution to minimum 10
                sphereResolution = max(int(sphereResolution), 10)
                sphereSource.SetPhiResolution(sphereResolution)
                sphereSource.SetThetaResolution(sphereResolution)
                sphereSource.SetCenter((bounds[0] + bounds[1]) / 2.0,
                                       (bounds[2] + bounds[3]) / 2.0,
                                       (bounds[4] + bounds[5]) / 2.0)
                sphereSource.Update()
                initialRegionPd = sphereSource.GetOutput()
        elif self.region == REGION_SEGMENT:
            # create initial region from segment (that will be grown)
            if not self.regionSegmentId:
                raise ValueError("Region segment is not set")
            if self.regionSegmentId == self.segmentId:
                raise ValueError(
                    "Region segment cannot be the same segment as the current segment"
                )
            initialRegionPd = vtk.vtkPolyData()
            self.segmentationNode.GetClosedSurfaceRepresentation(
                self.regionSegmentId, initialRegionPd)
            if not initialRegionPd or initialRegionPd.GetNumberOfPoints() == 0:
                raise ValueError("Region segment is empty")
            # initialRegionPd = self._remeshPolydata(initialRegionPd, self._inputSpacing*5.0)  # simplify the mesh
        else:
            raise ValueError("Invalid region: " + self.region)

        cleanPolyData = vtk.vtkCleanPolyData()
        cleanPolyData.SetInputData(initialRegionPd)
        cleanPolyData.Update()
        initialRegionPd = cleanPolyData.GetOutput()

        self._saveIntermediateResult("InitialRegion", initialRegionPd)
        return initialRegionPd