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