Skip to content

CannyEdgeDetector

Repository source: CannyEdgeDetector

Other languages

See (Cxx)

Question

If you have a question about this example, please use the VTK Discourse Forum

Code

CannyEdgeDetector.py

#!/usr/bin/env python3

from dataclasses import dataclass
from pathlib import Path

# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonExecutionModel import vtkImageToStructuredPoints
from vtkmodules.vtkFiltersCore import (
    vtkStripper,
    vtkThreshold)
from vtkmodules.vtkFiltersGeneral import (
    vtkLinkEdgels,
    vtkSubPixelPositionEdgels
)
from vtkmodules.vtkFiltersGeometry import vtkGeometryFilter
from vtkmodules.vtkIOImage import vtkPNGReader
from vtkmodules.vtkImagingColor import vtkImageLuminance
from vtkmodules.vtkImagingCore import (
    vtkImageCast,
    vtkImageConstantPad
)
from vtkmodules.vtkImagingGeneral import (
    vtkImageGaussianSmooth,
    vtkImageGradient
)
from vtkmodules.vtkImagingMath import vtkImageMagnitude
from vtkmodules.vtkImagingMorphological import vtkImageNonMaximumSuppression
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkImageActor,
    vtkPolyDataMapper,
    vtkRenderer,
    vtkRenderWindow,
    vtkRenderWindowInteractor
)


def get_program_parameters():
    import argparse
    description = 'CannyEdgeDetector.'
    epilogue = '''
    '''
    parser = argparse.ArgumentParser(description=description, epilog=epilogue,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('file_name', help='The image file name to use e.g. Gourds.png.')
    args = parser.parse_args()
    return args.file_name


def main():
    fn = get_program_parameters()
    fp = Path(fn)
    file_check = True
    if not fp.is_file():
        print(f'Missing image file: {fp}.')
        file_check = False
    if not file_check:
        return

    colors = vtkNamedColors()

    # Define viewport ranges (x_min, y_min, x_max, y_max)
    original_viewport = (0.0, 0.0, 0.5, 1.0)
    edge_viewport = (0.5, 0.0, 1.0, 1.0)

    original_renderer = vtkRenderer(viewport=original_viewport, background=colors.GetColor3d('SlateGray'))
    edge_renderer = vtkRenderer(viewport=edge_viewport, background=colors.GetColor3d('LightSlateGray'))

    render_window = vtkRenderWindow(multi_samples=0, size=(600, 300), window_name='CannyEdgeDetector')
    render_window.AddRenderer(original_renderer)
    render_window.AddRenderer(edge_renderer)

    interactor = vtkRenderWindowInteractor()
    interactor.render_window = render_window

    image_in = vtkPNGReader(file_name=fp)

    image_actor = vtkImageActor(input_data=image_in.output)

    original_renderer.AddActor(image_actor)

    il = vtkImageLuminance()

    ic = vtkImageCast(output_scalar_type=ImageCast.OutputScalarType.VTK_FLOAT)

    # Smooth the image.
    gs = vtkImageGaussianSmooth(dimensionality=2, radius_factors=(1, 1, 0))

    # Gradient the image.
    img_gradient = vtkImageGradient(dimensionality=2)

    img_magnitude = vtkImageMagnitude()
    image_in >> il >> ic >> gs >> img_gradient >> img_magnitude

    # Non maximum suppression.
    non_max = vtkImageNonMaximumSuppression(dimensionality=2,
                                            input_data=img_magnitude.output,
                                            vector_input_data=img_gradient.update().output)

    pad = vtkImageConstantPad(output_number_of_scalar_components=3, constant=0)
    image_in >> il >> ic >> gs >> img_gradient >> pad
    pad.update()

    i2sp1 = vtkImageToStructuredPoints(vector_input_data=pad.output)

    # Link edgles.
    img_link = vtkLinkEdgels(gradient_threshold=2)

    # Threshold links.
    threshold_edges = vtkThreshold(threshold_function=vtkThreshold.THRESHOLD_UPPER, upper_threshold=10,
                                   all_scalars=False)

    gf = vtkGeometryFilter()
    non_max >> i2sp1 >> img_link >> threshold_edges >> gf

    i2sp = vtkImageToStructuredPoints(vector_input_data=pad.output)
    image_in >> il >> ic >> gs >> img_gradient >> img_magnitude >> i2sp

    # Subpixel them.
    spe = vtkSubPixelPositionEdgels(grad_maps_data=i2sp.structured_points_output)
    gf >> spe
    i2sp.update()

    strip = vtkStripper()

    dsm = vtkPolyDataMapper(scalar_visibility=False)
    spe >> strip >> dsm

    plane_actor = vtkActor(mapper=dsm)
    plane_actor.property.ambient = 1.0
    plane_actor.property.diffuse = 0.0
    plane_actor.property.color = colors.GetColor3d('GhostWhite')

    # Add the actors to the renderer, set the background and size.
    edge_renderer.AddActor(plane_actor)

    # Render the image.
    interactor.Initialize()
    render_window.Render()
    render_window.Render()

    interactor.Start()


@dataclass(frozen=True)
class ImageCast:
    @dataclass(frozen=True)
    class OutputScalarType:
        VTK_CHAR: int = 2
        VTK_UNSIGNED_CHAR: int = 3
        VTK_SHORT: int = 4
        VTK_UNSIGNED_SHORT: int = 5
        VTK_INT: int = 6
        VTK_UNSIGNED_INT: int = 7
        VTK_LONG: int = 8
        VTK_UNSIGNED_LONG: int = 9
        VTK_FLOAT: int = 10
        VTK_DOUBLE: int = 11


if __name__ == '__main__':
    main()