Ejemplo n.º 1
0
def extract_largest_connected_region(vtk_im, label_id):
    """
    Extrac the largest connected region of a vtk image

    Args:
        vtk_im: vtk image
        label_id: id of the label
    Return:
        new_im: processed vtk image
    """

    fltr = vtk.vtkImageConnectivityFilter()
    fltr.SetScalarRange(label_id, label_id)
    fltr.SetExtractionModeToLargestRegion()
    fltr.SetInputData(vtk_im)
    fltr.Update()
    new_im = fltr.GetOutput()
    from vtk.util.numpy_support import vtk_to_numpy, numpy_to_vtk
    py_im = vtk_to_numpy(vtk_im.GetPointData().GetScalars())
    py_mask = vtk_to_numpy(new_im.GetPointData().GetScalars())
    mask = np.logical_and(py_im == label_id, py_mask == 0)
    py_im[mask] = 0
    vtk_im.GetPointData().SetScalars(numpy_to_vtk(py_im))
    return vtk_im
Ejemplo n.º 2
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
Ejemplo n.º 3
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
thresh.ReplaceOutOn()
thresh.SetInValue(1.0)
thresh.SetOutValue(0.0)
thresh.ThresholdByUpper(float(args.threshold))
print('Thresholding at {}'.format(float(args.threshold)))
thresh.Update()

dil = vtk.vtkImageContinuousDilate3D()
dil.SetInputConnection(thresh.GetOutputPort())
dil.SetKernelSize(args.kernelSize, args.kernelSize, args.kernelSize)
dil.SetNumberOfThreads(args.nThreads)
print('Dilating...')
dil.Update()

# Extract largest Component
cc = vtk.vtkImageConnectivityFilter()
cc.SetInputConnection(dil.GetOutputPort())
cc.SetExtractionModeToLargestRegion()
cc.SetScalarRange(1, 1)
print('Component labelling for bones')
cc.Update()

# Extract largest Component
cc2 = vtk.vtkImageConnectivityFilter()
cc2.SetInputConnection(cc.GetOutputPort())
cc2.SetExtractionModeToLargestRegion()
cc2.SetScalarRange(0, 0)
cc2.SetLabelModeToConstantValue()
cc2.SetLabelConstantValue(2)
print('Component labelling for background')
cc2.Update()
Ejemplo n.º 5
0
    os.sys.exit('Must have atleast one threads, asked for {}. Exiting...'.format(args.nThreads))

# Read input
reader = vtk.vtkNIFTIImageReader()
reader.SetFileName(args.inputFilename)
print('Reading in \"{inputFilename}\"'.format(inputFilename=args.inputFilename))
reader.Update()

# Get scalar range for CC
scalarRange = reader.GetOutput().GetScalarRange()
tempPixelValue = 2
if tempPixelValue == scalarRange[1]:
    tempPixelValue = tempPixelValue + 1

# Make sure any floating points are removed (i.e. missed in segmentation)
cc = vtk.vtkImageConnectivityFilter()
cc.SetInputConnection(reader.GetOutputPort())
cc.SetExtractionModeToLargestRegion()
cc.SetScalarRange(scalarRange[1], scalarRange[1])
cc.SetLabelModeToConstantValue()
cc.SetLabelConstantValue(1)
print('Performing first connected component')
cc.Update()

# Dilate and erode (background-close) the image
dil = vtk.vtkImageContinuousDilate3D()
dil.SetInputConnection(cc.GetOutputPort())
dil.SetKernelSize(args.kernelSize,args.kernelSize,args.kernelSize)
dil.SetNumberOfThreads(args.nThreads)
print('Dilating with {} threads'.format(args.nThreads))
dil.Update()
Ejemplo n.º 6
0
def autocontour_buie(img, args):

    # these hard-coded constants just make sure that the connectivity filter
    # actually works, they don't need to be configuable in my opinion
    CONN_SCALAR_RANGE = [1, args.in_value + 1]
    CONN_SIZE_RANGE = [0, int(1E10)]

    # Step 1 was just loading the AIM

    # Step 2: Threshold
    s2_threshold = vtk.vtkImageThreshold()
    s2_threshold.ThresholdByUpper(args.threshold_1)
    s2_threshold.SetInValue(args.in_value)
    s2_threshold.SetOutValue(args.out_value)
    s2_threshold.SetInputData(img)

    # Step 3: Median
    s3_median = vtk.vtkImageMedian3D()
    s3_median.SetKernelSize(*args.step_3_median_kernel)
    s3_median.SetInputConnection(s2_threshold.GetOutputPort())

    # Step 4: Dilate
    s4_dilate = vtk.vtkImageDilateErode3D()
    s4_dilate.SetDilateValue(args.in_value)
    s4_dilate.SetErodeValue(args.out_value)
    s4_dilate.SetKernelSize(*args.step_4_and_6_dilate_erode_kernel)
    s4_dilate.SetInputConnection(s3_median.GetOutputPort())

    # Step 5: Connectivity (applied to non-bone)
    # first flip 0 <-> 127
    s5a_invert = vtk.vtkImageThreshold()
    s5a_invert.ThresholdByLower(args.in_value / 2)
    s5a_invert.SetInValue(args.in_value)
    s5a_invert.SetOutValue(args.out_value)
    s5a_invert.SetInputConnection(s4_dilate.GetOutputPort())

    # then connectivity
    s5b_connectivity = vtk.vtkImageConnectivityFilter()
    s5b_connectivity.SetExtractionModeToLargestRegion()
    s5b_connectivity.SetScalarRange(*CONN_SCALAR_RANGE)
    s5b_connectivity.SetSizeRange(*CONN_SIZE_RANGE)
    s5b_connectivity.SetInputConnection(s5a_invert.GetOutputPort())

    # then flip 0 <-> 127 again
    s5c_invert = vtk.vtkImageThreshold()
    s5c_invert.ThresholdByLower(0.5)
    s5c_invert.SetInValue(args.in_value)
    s5c_invert.SetOutValue(args.out_value)
    s5c_invert.SetInputConnection(s5b_connectivity.GetOutputPort())

    # Step 6: Erode
    s6_erode = vtk.vtkImageDilateErode3D()
    s6_erode.SetDilateValue(args.out_value)
    s6_erode.SetErodeValue(args.in_value)
    s6_erode.SetKernelSize(*args.step_4_and_6_dilate_erode_kernel)
    s6_erode.SetInputConnection(s5c_invert.GetOutputPort())

    # Step 7: Threshold
    s7_threshold = vtk.vtkImageThreshold()
    s7_threshold.ThresholdByLower(args.threshold_2)
    s7_threshold.SetInValue(args.in_value)
    s7_threshold.SetOutValue(args.out_value)
    s7_threshold.SetInputData(img)

    # Step 8: Mask
    s8a_mask = vtk.vtkImageMask()
    s8a_mask.SetInputConnection(0, s7_threshold.GetOutputPort())
    s8a_mask.SetInputConnection(1, s6_erode.GetOutputPort())

    s8b_invert = vtk.vtkImageThreshold()
    s8b_invert.ThresholdByLower(args.in_value / 2)
    s8b_invert.SetInValue(args.in_value)
    s8b_invert.SetOutValue(args.out_value)
    s8b_invert.SetInputConnection(s8a_mask.GetOutputPort())

    # Step 9: Dilate
    s9_dilate = vtk.vtkImageDilateErode3D()
    s9_dilate.SetDilateValue(args.out_value)
    s9_dilate.SetErodeValue(args.in_value)
    s9_dilate.SetKernelSize(*args.step_9_and_11_dilate_erode_kernel)
    s9_dilate.SetInputConnection(s8b_invert.GetOutputPort())

    # Step 10: Connectivity
    # connectivity
    s10a_connectivity = vtk.vtkImageConnectivityFilter()
    s10a_connectivity.SetExtractionModeToLargestRegion()
    s10a_connectivity.SetScalarRange(*CONN_SCALAR_RANGE)
    s10a_connectivity.SetSizeRange(*CONN_SIZE_RANGE)
    s10a_connectivity.SetInputConnection(s9_dilate.GetOutputPort())

    # then convert to 127 and 0 again
    s10b_convert = vtk.vtkImageThreshold()
    s10b_convert.ThresholdByLower(0.5)
    s10b_convert.SetInValue(args.out_value)
    s10b_convert.SetOutValue(args.in_value)
    s10b_convert.SetInputConnection(s10a_connectivity.GetOutputPort())

    # Step 11: Erode
    s11_erode = vtk.vtkImageDilateErode3D()
    s11_erode.SetDilateValue(args.in_value)
    s11_erode.SetErodeValue(args.out_value)
    s11_erode.SetKernelSize(*args.step_9_and_11_dilate_erode_kernel)
    s11_erode.SetInputConnection(s10b_convert.GetOutputPort())

    # Step 12: Gaussian Smooth
    s12_gauss = vtk.vtkImageGaussianSmooth()
    s12_gauss.SetStandardDeviation(args.step_12_gaussian_std)
    s12_gauss.SetRadiusFactors(*args.step_12_gaussian_kernel)
    s12_gauss.SetInputConnection(s11_erode.GetOutputPort())

    # Step 13: Threshold
    s13_threshold = vtk.vtkImageThreshold()
    s13_threshold.ThresholdByLower(S13_THRESH)
    s13_threshold.SetInValue(args.out_value)
    s13_threshold.SetOutValue(args.in_value)
    s13_threshold.SetInputConnection(s12_gauss.GetOutputPort())

    # Step 14: Mask
    s14_mask = vtk.vtkImageMask()
    s14_mask.SetInputConnection(0, s6_erode.GetOutputPort())
    s14_mask.SetInputConnection(1, s13_threshold.GetOutputPort())

    # Step 15: Invert the Trabecular Mask
    s15_invert = vtk.vtkImageThreshold()
    s15_invert.ThresholdByLower(args.in_value / 2)
    s15_invert.SetInValue(args.in_value)
    s15_invert.SetOutValue(args.out_value)
    s15_invert.SetInputConnection(s13_threshold.GetOutputPort())

    # update the pipeline
    s14_mask.Update()
    s15_invert.Update()

    # get masks
    cort_mask = s14_mask.GetOutput()
    trab_mask = s15_invert.GetOutput()

    return cort_mask, trab_mask