Exemplo n.º 1
0
def extract_smooth_mesh(imageVTK,
                        label_range,
                        smoothing_iterations=30,
                        pass_band_param=0.01,
                        target_reduction=0.95):
    '''Extract mesh/contour for labels in imageVTK, smooth and decimate.
    
    Multiple labels can be extracted at once, however touching labels 
    will share vertices and the label ids are lost during smoothing/decimation.
    Processing is slow for small objects in a large volume and should be cropped beforehand.
    
    Args:
        imageVTK: vtk image data
        label_range: range of labels to extract. A tuple (l,l) will extract 
            a mesh for a single label id l
        smoothing_iterations: number of iterations for vtkWindowedSincPolyDataFilter
        pass_band_param: pass band param in range [0.,2.] for vtkWindowedSincPolyDataFilter.
            Lower value remove higher frequencies.
        target_reduction: target reduction for vtkQuadricDecimation
    '''

    n_contours = label_range[1] - label_range[0] + 1

    # alternative vtkDiscreteMarchingCubes is slower and creates some weird missalignment lines when applied to tight crops
    dfe = vtk.vtkDiscreteFlyingEdges3D()
    dfe.SetInputData(imageVTK)
    dfe.ComputeScalarsOff(
    )  # numpy image labels --> cells (faces) scalar values
    dfe.ComputeNormalsOff()
    dfe.ComputeGradientsOff()
    dfe.InterpolateAttributesOff()
    dfe.GenerateValues(n_contours, label_range[0],
                       label_range[1])  # numContours, rangeStart, rangeEnd
    dfe.Update()

    smoother = vtk.vtkWindowedSincPolyDataFilter()
    smoother.SetInputConnection(dfe.GetOutputPort())
    smoother.SetNumberOfIterations(
        smoothing_iterations)  #this has little effect on the error!
    smoother.BoundarySmoothingOff()
    smoother.FeatureEdgeSmoothingOff()
    # smoother.SetFeatureAngle(120)
    smoother.SetPassBand(
        pass_band_param)  # from 0 to 2, 2 keeps high frequencies
    smoother.NonManifoldSmoothingOn()
    smoother.NormalizeCoordinatesOn()
    smoother.GenerateErrorScalarsOff()
    smoother.GenerateErrorVectorsOff()
    smoother.Update()

    # vtkQuadricDecimation looks cleaner than vtkDecimatePro (no unexpected sharp edges)
    # but drop points scalar value --> can be added back if doing one instance a time
    decimate = vtk.vtkQuadricDecimation()
    decimate.SetInputConnection(smoother.GetOutputPort())
    decimate.SetTargetReduction(target_reduction)
    decimate.VolumePreservationOn()
    decimate.Update()

    return decimate.GetOutput()
Exemplo n.º 2
0
def main():
    # vtkDiscreteFlyingEdges3D was introduced in VTK >= 8.2
    use_flying_edges = vtk_version_ok(8, 2, 0)

    n = 20
    radius = 8
    blob = make_blob(n, radius)

    if use_flying_edges:
        try:
            discrete = vtk.vtkDiscreteFlyingEdges3D()
        except AttributeError:
            discrete = vtk.vtkDiscreteMarchingCubes()
    else:
        discrete = vtk.vtkDiscreteMarchingCubes()
    discrete.SetInputData(blob)
    discrete.GenerateValues(n, 1, n)

    lut = make_colors(n)

    mapper = vtk.vtkPolyDataMapper()
    mapper.SetInputConnection(discrete.GetOutputPort())
    mapper.SetLookupTable(lut)
    mapper.SetScalarRange(0, lut.GetNumberOfColors())

    # Create the RenderWindow, Renderer and both Actors
    #
    ren = vtk.vtkRenderer()
    ren_win = vtk.vtkRenderWindow()
    ren_win.AddRenderer(ren)
    ren_win.SetWindowName('DiscreteMarchingCubes')

    iren = vtk.vtkRenderWindowInteractor()
    iren.SetRenderWindow(ren_win)

    actor = vtk.vtkActor()
    actor.SetMapper(mapper)

    ren.AddActor(actor)

    colors = vtk.vtkNamedColors()
    ren.SetBackground(colors.GetColor3d('Burlywood'))

    ren_win.Render()

    iren.Start()
    thres.Update()
    if (i == 0):
        blobImage.DeepCopy(thres.GetOutput())

    maxValue = vtk.vtkImageMathematics()
    maxValue.SetInputData(0, blobImage)
    maxValue.SetInputData(1, thres.GetOutput())
    maxValue.SetOperationToMax()
    maxValue.Modified()
    maxValue.Update()

    blobImage.DeepCopy(maxValue.GetOutput())

    i += 1

discrete = vtk.vtkDiscreteFlyingEdges3D()
discrete.SetInputData(blobImage)
discrete.GenerateValues(n, 1, n)

mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(discrete.GetOutputPort())
mapper.SetLookupTable(lut)
mapper.SetScalarRange(0, lut.GetNumberOfColors())

actor = vtk.vtkActor()
actor.SetMapper(mapper)

ren1.AddActor(actor)

renWin.Render()
iren.Start()
Exemplo n.º 4
0
    thres.Update()
    if (i == 0):
        blobImage.DeepCopy(thres.GetOutput())

    maxValue = vtk.vtkImageMathematics()
    maxValue.SetInputData(0, blobImage)
    maxValue.SetInputData(1, thres.GetOutput())
    maxValue.SetOperationToMax()
    maxValue.Modified()
    maxValue.Update()

    blobImage.DeepCopy(maxValue.GetOutput())

    i += 1

discrete = vtk.vtkDiscreteFlyingEdges3D()
discrete.SetInputData(blobImage)
discrete.GenerateValues(n, 1, n)

mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(discrete.GetOutputPort())
mapper.SetLookupTable(lut)
mapper.SetScalarRange(0, lut.GetNumberOfColors())

actor = vtk.vtkActor()
actor.SetMapper(mapper)

ren1.AddActor(actor)

renWin.Render()
iren.Start()
Exemplo n.º 5
0
    def smoothMultipleSegments(self):
        import vtkSegmentationCorePython as vtkSegmentationCore

        # Generate merged labelmap of all visible segments
        segmentationNode = self.scriptedEffect.parameterSetNode(
        ).GetSegmentationNode()
        visibleSegmentIds = vtk.vtkStringArray()
        segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(
            visibleSegmentIds)
        if visibleSegmentIds.GetNumberOfValues() == 0:
            logging.info(
                "Smoothing operation skipped: there are no visible segments")
            return

        mergedImage = vtkSegmentationCore.vtkOrientedImageData()
        if not segmentationNode.GenerateMergedLabelmapForAllSegments(
                mergedImage, vtkSegmentationCore.vtkSegmentation.
                EXTENT_UNION_OF_SEGMENTS_PADDED, None, visibleSegmentIds):
            logging.error(
                'Failed to apply smoothing: cannot get list of visible segments'
            )
            return

        segmentLabelValues = []  # list of [segmentId, labelValue]
        for i in range(visibleSegmentIds.GetNumberOfValues()):
            segmentId = visibleSegmentIds.GetValue(i)
            segmentLabelValues.append([segmentId, i + 1])

        # Perform smoothing in voxel space
        ici = vtk.vtkImageChangeInformation()
        ici.SetInputData(mergedImage)
        ici.SetOutputSpacing(1, 1, 1)
        ici.SetOutputOrigin(0, 0, 0)

        # Convert labelmap to combined polydata
        if vtk.VTK_MAJOR_VERSION >= 9:
            convertToPolyData = vtk.vtkDiscreteFlyingEdges3D()
            convertToPolyData.ComputeGradientsOff()
            convertToPolyData.ComputeNormalsOff()
            convertToPolyData.ComputeScalarsOff()
        else:
            convertToPolyData = vtk.vtkDiscreteMarchingCubes()

        convertToPolyData.SetInputConnection(ici.GetOutputPort())
        convertToPolyData.SetNumberOfContours(len(segmentLabelValues))

        contourIndex = 0
        for segmentId, labelValue in segmentLabelValues:
            convertToPolyData.SetValue(contourIndex, labelValue)
            contourIndex += 1

        # Low-pass filtering using Taubin's method
        smoothingFactor = self.scriptedEffect.doubleParameter(
            "JointTaubinSmoothingFactor")
        smoothingIterations = 100  #  according to VTK documentation 10-20 iterations could be enough but we use a higher value to reduce chance of shrinking
        passBand = pow(
            10.0, -4.0 * smoothingFactor
        )  # gives a nice range of 1-0.0001 from a user input of 0-1
        smoother = vtk.vtkWindowedSincPolyDataFilter()
        smoother.SetInputConnection(convertToPolyData.GetOutputPort())
        smoother.SetNumberOfIterations(smoothingIterations)
        smoother.BoundarySmoothingOff()
        smoother.FeatureEdgeSmoothingOff()
        smoother.SetFeatureAngle(90.0)
        smoother.SetPassBand(passBand)
        smoother.NonManifoldSmoothingOn()
        smoother.NormalizeCoordinatesOn()

        # Extract a label
        threshold = vtk.vtkThreshold()
        threshold.SetInputConnection(smoother.GetOutputPort())

        # Convert to polydata
        geometryFilter = vtk.vtkGeometryFilter()
        geometryFilter.SetInputConnection(threshold.GetOutputPort())

        # Convert polydata to stencil
        polyDataToImageStencil = vtk.vtkPolyDataToImageStencil()
        polyDataToImageStencil.SetInputConnection(
            geometryFilter.GetOutputPort())
        polyDataToImageStencil.SetOutputSpacing(1, 1, 1)
        polyDataToImageStencil.SetOutputOrigin(0, 0, 0)
        polyDataToImageStencil.SetOutputWholeExtent(mergedImage.GetExtent())

        # Convert stencil to image
        stencil = vtk.vtkImageStencil()
        emptyBinaryLabelMap = vtk.vtkImageData()
        emptyBinaryLabelMap.SetExtent(mergedImage.GetExtent())
        emptyBinaryLabelMap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1)
        vtkSegmentationCore.vtkOrientedImageDataResample.FillImage(
            emptyBinaryLabelMap, 0)
        stencil.SetInputData(emptyBinaryLabelMap)
        stencil.SetStencilConnection(polyDataToImageStencil.GetOutputPort())
        stencil.ReverseStencilOn()
        stencil.SetBackgroundValue(
            1
        )  # General foreground value is 1 (background value because of reverse stencil)

        imageToWorldMatrix = vtk.vtkMatrix4x4()
        mergedImage.GetImageToWorldMatrix(imageToWorldMatrix)

        for segmentId, labelValue in segmentLabelValues:
            threshold.ThresholdBetween(labelValue, labelValue)
            stencil.Update()
            smoothedBinaryLabelMap = vtkSegmentationCore.vtkOrientedImageData()
            smoothedBinaryLabelMap.ShallowCopy(stencil.GetOutput())
            smoothedBinaryLabelMap.SetImageToWorldMatrix(imageToWorldMatrix)
            # Write results to segments directly, bypassing masking
            slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(
                smoothedBinaryLabelMap, segmentationNode, segmentId,
                slicer.vtkSlicerSegmentationsModuleLogic.MODE_REPLACE,
                smoothedBinaryLabelMap.GetExtent())
def main():
    # vtkDiscreteFlyingEdges3D was introduced in VTK >= 8.2
    use_flying_edges = vtk_version_ok(8, 2, 0)

    file_name, start_label, end_label = get_program_parameters()
    if start_label > end_label:
        end_label, start_label = start_label, end_label

    # Create all of the classes we will need
    reader = vtk.vtkMetaImageReader()
    histogram = vtk.vtkImageAccumulate()
    if use_flying_edges:
        try:
            using_marching_cubes = False
            discrete_cubes = vtk.vtkDiscreteFlyingEdges3D()
        except AttributeError:
            using_marching_cubes = True
            discrete_cubes = vtk.vtkDiscreteMarchingCubes()
    else:
        using_marching_cubes = True
        discrete_cubes = vtk.vtkDiscreteMarchingCubes()
    smoother = vtk.vtkWindowedSincPolyDataFilter()
    selector = vtk.vtkThreshold()
    scalars_off = vtk.vtkMaskFields()
    geometry = vtk.vtkGeometryFilter()
    writer = vtk.vtkXMLPolyDataWriter()

    # Define all of the variables
    file_prefix = 'Label'
    smoothing_iterations = 15
    pass_band = 0.001
    feature_angle = 120.0

    # Generate models from labels
    # 1) Read the meta file
    # 2) Generate a histogram of the labels
    # 3) Generate models from the labeled volume
    # 4) Smooth the models
    # 5) Output each model into a separate file

    reader.SetFileName(file_name)

    histogram.SetInputConnection(reader.GetOutputPort())
    histogram.SetComponentExtent(0, end_label, 0, 0, 0, 0)
    histogram.SetComponentOrigin(0, 0, 0)
    histogram.SetComponentSpacing(1, 1, 1)
    histogram.Update()

    discrete_cubes.SetInputConnection(reader.GetOutputPort())
    discrete_cubes.GenerateValues(end_label - start_label + 1, start_label, end_label)

    smoother.SetInputConnection(discrete_cubes.GetOutputPort())
    smoother.SetNumberOfIterations(smoothing_iterations)
    smoother.BoundarySmoothingOff()
    smoother.FeatureEdgeSmoothingOff()
    smoother.SetFeatureAngle(feature_angle)
    smoother.SetPassBand(pass_band)
    smoother.NonManifoldSmoothingOn()
    smoother.NormalizeCoordinatesOn()
    smoother.Update()

    selector.SetInputConnection(smoother.GetOutputPort())
    if use_flying_edges:
        if using_marching_cubes:
            selector.SetInputArrayToProcess(0, 0, 0, vtk.vtkDataObject().FIELD_ASSOCIATION_CELLS,
                                            vtk.vtkDataSetAttributes().SCALARS)
        else:
            selector.SetInputArrayToProcess(0, 0, 0, vtk.vtkDataObject().FIELD_ASSOCIATION_POINTS,
                                            vtk.vtkDataSetAttributes().SCALARS)
    else:
        selector.SetInputArrayToProcess(0, 0, 0, vtk.vtkDataObject().FIELD_ASSOCIATION_CELLS,
                                        vtk.vtkDataSetAttributes().SCALARS)

    # Strip the scalars from the output
    scalars_off.SetInputConnection(selector.GetOutputPort())
    scalars_off.CopyAttributeOff(vtk.vtkMaskFields().POINT_DATA,
                                 vtk.vtkDataSetAttributes().SCALARS)
    scalars_off.CopyAttributeOff(vtk.vtkMaskFields().CELL_DATA,
                                 vtk.vtkDataSetAttributes().SCALARS)

    geometry.SetInputConnection(scalars_off.GetOutputPort())

    writer.SetInputConnection(geometry.GetOutputPort())

    for i in range(start_label, end_label + 1):
        # see if the label exists, if not skip it
        frequency = histogram.GetOutput().GetPointData().GetScalars().GetTuple1(i)
        if frequency == 0.0:
            continue

        # select the cells for a given label
        selector.ThresholdBetween(i, i)

        # output the polydata
        output_fn = '{:s}{:d}.vtp'.format(file_prefix, i)
        print('{:s} writing {:s}'.format(os.path.basename(sys.argv[0]), output_fn))

        writer.SetFileName(output_fn)
        writer.Write()
def main():
    # vtkFlyingEdges3D was introduced in VTK >= 8.2
    use_flying_edges = vtk_version_ok(8, 2, 0)

    colors = vtk.vtkNamedColors()
    ifn, index = get_program_parameters()

    # Prepare to read the file.
    reader_volume = vtk.vtkMetaImageReader()
    reader_volume.SetFileName(ifn)
    reader_volume.Update()

    # Extract the region of interest.
    voi = vtk.vtkExtractVOI()
    voi.SetInputConnection(reader_volume.GetOutputPort())
    voi.SetVOI(0, 517, 0, 228, 0, 392)
    voi.SetSampleRate(1, 1, 1)
    voi.Update()  # Necessary for GetScalarRange().
    srange = voi.GetOutput().GetScalarRange()  # Needs Update() before!
    print('Range', srange)

    # Prepare surface generation.
    # For label images.
    if use_flying_edges:
        try:
            contour = vtk.vtkDiscreteFlyingEdges3D()
        except AttributeError:
            contour = vtk.vtkDiscreteMarchingCubes()
    else:
        contour = vtk.vtkDiscreteMarchingCubes()
    contour.SetInputConnection(voi.GetOutputPort())
    # contour.ComputeNormalsOn()

    print('Doing label', index)

    contour.SetValue(0, index)
    contour.Update()  # Needed for GetNumberOfPolys()!!!

    smoother = vtk.vtkWindowedSincPolyDataFilter()
    smoother.SetInputConnection(contour.GetOutputPort())
    smoother.SetNumberOfIterations(30)  # This has little effect on the error!
    # smoother.BoundarySmoothingOff()
    # smoother.FeatureEdgeSmoothingOff()
    # smoother.SetFeatureAngle(120.0)
    # smoother.SetPassBand(0.001)        # This increases the error a lot!
    smoother.NonManifoldSmoothingOn()
    smoother.NormalizeCoordinatesOn()
    smoother.GenerateErrorScalarsOn()
    # smoother.GenerateErrorVectorsOn()
    smoother.Update()

    smoothed_polys = smoother.GetOutput()
    smoother_error = smoothed_polys.GetPointData().GetScalars()

    # Find min and max z.
    se_range = smoother_error.GetRange()
    print('Smoother error range:', se_range)
    minz = se_range[0]  # min(smoother_error)
    maxz = se_range[1]  # max(smoother_error)
    if maxz > 1:
        print('Big smoother error: min/max:', minz, maxz)
    # minz = 0.3  # This way colours of different particles are comparable.
    # maxz = 1
    # minz = 0.3
    # maxz = 0.6
    minz = 3.25
    maxz = 3.85

    # Create the color map.
    lut = vtk.vtkLookupTable()
    lut.SetTableRange(
        minz,
        maxz)  # This does nothing, use mapper.SetScalarRange(minz, maxz).
    lut.SetHueRange(2 / 3.0, 1)
    # lut.SetSaturationRange(0, 0)
    # lut.SetValueRange(1, 0)
    # lut.SetNumberOfColors(256) #256 default
    lut.Build()

    # Calculate cell normals.
    triangle_cell_normals = vtk.vtkPolyDataNormals()
    triangle_cell_normals.SetInputData(smoothed_polys)
    triangle_cell_normals.ComputeCellNormalsOn()
    triangle_cell_normals.ComputePointNormalsOff()
    triangle_cell_normals.ConsistencyOn()
    triangle_cell_normals.AutoOrientNormalsOn()
    triangle_cell_normals.Update()  # Creates vtkPolyData.

    mapper = vtk.vtkPolyDataMapper()
    # mapper.SetInput(smoothed_polys) # This has no normals.
    mapper.SetInputConnection(
        triangle_cell_normals.GetOutputPort())  # this is better for vis;-)
    mapper.ScalarVisibilityOn()  # Show colour.
    mapper.SetScalarRange(minz, maxz)
    # mapper.SetScalarModeToUseCellData() # Contains the label eg. 31
    mapper.SetScalarModeToUsePointData(
    )  # The smoother error relates to the verts.
    mapper.SetLookupTable(lut)

    # Take the isosurface data and create geometry.
    actor = vtk.vtkLODActor()
    actor.SetNumberOfCloudPoints(100000)
    actor.SetMapper(mapper)

    # Create the renderer.
    ren = vtk.vtkRenderer()
    ren.SetBackground(colors.GetColor3d('DimGray'))
    ren.AddActor(actor)

    # Create a window for the renderer of size 600X600
    ren_win = vtk.vtkRenderWindow()
    ren_win.AddRenderer(ren)
    ren_win.SetSize(600, 600)

    # Set a user interface interactor for the render window.
    iren = vtk.vtkRenderWindowInteractor()
    iren.SetRenderWindow(ren_win)

    # Start the initialization and rendering.
    iren.Initialize()
    ren.GetActiveCamera().SetPosition(-0.004332, -1.771289, -0.754580)
    ren.GetActiveCamera().SetFocalPoint(0.000271, -0.001974, 0.006892)
    ren.GetActiveCamera().SetViewUp(0.790211, -0.243999, 0.562166)
    ren.ResetCameraClippingRange()
    ren_win.Render()
    ren_win.SetWindowName('MeshLabelImageColor')
    ren_win.Render()

    iren.Start()
Exemplo n.º 8
0
def main():
    # vtkFlyingEdges3D was introduced in VTK >= 8.2
    use_flying_edges = vtk_version_ok(8, 2, 0)

    file_name = get_program_parameters()

    colors = vtk.vtkNamedColors()

    # Create the RenderWindow, Renderer and Interactor.
    #

    ren = vtk.vtkRenderer()

    ren_win = vtk.vtkRenderWindow()
    ren_win.AddRenderer(ren)

    iren = vtk.vtkRenderWindowInteractor()
    iren.SetRenderWindow(ren_win)

    # Create the pipeline.
    #

    reader = vtk.vtkMetaImageReader()
    reader.SetFileName(file_name)
    reader.Update()

    locator = vtk.vtkMergePoints()
    locator.SetDivisions(64, 64, 92)
    locator.SetNumberOfPointsPerBucket(2)
    locator.AutomaticOff()

    if use_flying_edges:
        try:
            using_marching_cubes = False
            iso = vtk.vtkDiscreteFlyingEdges3D()
        except AttributeError:
            using_marching_cubes = True
            iso = vtk.vtkDiscreteMarchingCubes()
    else:
        using_marching_cubes = True
        iso = vtk.vtkDiscreteMarchingCubes()
    iso.SetInputConnection(reader.GetOutputPort())
    iso.ComputeGradientsOn()
    iso.ComputeScalarsOff()
    iso.SetValue(0, 1150)
    if using_marching_cubes:
        iso.SetLocator(locator)

    iso_mapper = vtk.vtkPolyDataMapper()
    iso_mapper.SetInputConnection(iso.GetOutputPort())
    iso_mapper.ScalarVisibilityOff()

    iso_actor = vtk.vtkActor()
    iso_actor.SetMapper(iso_mapper)
    iso_actor.GetProperty().SetColor(colors.GetColor3d('Ivory'))

    outline = vtk.vtkOutlineFilter()
    outline.SetInputConnection(reader.GetOutputPort())

    outline_mapper = vtk.vtkPolyDataMapper()
    outline_mapper.SetInputConnection(outline.GetOutputPort())

    outline_actor = vtk.vtkActor()
    outline_actor.SetMapper(outline_mapper)

    # Add the actors to the renderer, set the background and size.
    #
    ren.AddActor(outline_actor)
    ren.AddActor(iso_actor)
    ren.SetBackground(colors.GetColor3d('SlateGray'))
    ren.GetActiveCamera().SetFocalPoint(0, 0, 0)
    ren.GetActiveCamera().SetPosition(0, -1, 0)
    ren.GetActiveCamera().SetViewUp(0, 0, -1)
    ren.ResetCamera()
    ren.GetActiveCamera().Dolly(1.5)
    ren.ResetCameraClippingRange()

    ren_win.SetSize(640, 480)
    ren_win.SetWindowName('HeadBone')

    ren_win.Render()
    iren.Start()