Search code examples
pythonnumpypython-imaging-libraryscikit-image

How to find the joints and endpoints of medial axis


I was able to get the medial axis using

image = Image.open('path/to/img.png')
image = (np.array(image) / 255).astype(np.uint8)
medial = skeletonize(image)
medial_x, medial_y = np.where(medial == 1)

But how can I find the endpoints and the joints of the medial axis using python as the coordinates I get from medial_x, medial_y = np.where(medial == 1) are not ordered in a way that I can easily get endpoints or joints?

enter image description here

The original silhouette is attached below.

enter image description here


Solution

  • I did this using wand - which is derived from ImageMagick. It has a skeletonise method and "Hit and Miss Morphology" for looking for specific shapes such as line-ends or junctions.

    There is an excellent discussion by Anthony Thyssen here, but if I may summarise, you are looking for the following shapes when trying to find line-ends:

    enter image description here

    and this when looking for junctions:

    enter image description here

    The black squares mean the image must be black at that location and are represented as zeroes in the kernel in the code. The white squares mean the image must be white at that location and are represented as ones in the kernel in the code. The blank squares mean we "don't care" what is at that location and are represented as dashes (minus signs) in the code.

    The code looks like this:

    #!/usr/bin/env python3
    
    import numpy as np
    from wand.image import Image
    
    # Use 'wand' to:
    # 1 skeletonize
    # 2 find line-ends using Top-Hat Morphology
    # 3 find line-junctions using Top-Hat Morphology
    
    with Image(filename='Q4J0l.png') as img:
        # Skeletonize
        img.morphology(method='thinning',
                       kernel='skeleton',
                       iterations=-1)
        img.save(filename='DEBUG-skeleton.png')
    
        # Find line-ends using Top-Hat Morphology
        # There are two kernels here, separated by a semi-colon
        # Each is rotated through 90 degress to form all 4 orientations
        # The first 3x3 kernel is the one tinted red in the diagram above.
        # The second 3x3 kernel is the one tinted green in the diagram above
        lineEnds = """
        3>:
            0,0,-
            0,1,1
            0,0,-;
        3>:
            0,0,0
            0,1,0
            0,0,1
        """
        # Clone the original image as we are about to destroy it
        with img.clone() as endsImage:
            endsImage.morphology(method='hit_and_miss', kernel=lineEnds)
            endsImage.save(filename='DEBUG-ends.png')
    
        # Find line-junctions using Top-Hat Morphology
        # There are three kernels here, separated by a semi-colon
        # Each is rotated through 90 degress to form all 4 orientations
        # The first 3x3 kernel is the one tinted yellow in the diagram above
        # The second 3x3 kernel is the one tinted magenta in the diagram above 
        # The third 3x3 kernel is the one tinted cyan in the diagram above
        lineJunctions = """
        3>:
            1,-,1
            -,1,-
            -,1,-;
        3>:
            -,1,-
            -,1,1
            1,-,-;
        3>:
            1,-,-
            -,1,-
            1,-,1
        """
        # Clone the original image as we are about to destroy it
        with img.clone() as junctionsImage:
            junctionsImage.morphology(method='hit_and_miss', kernel=lineJunctions)
            junctionsImage.save(filename='DEBUG-junctions.png')
    

    The DEBUG-images are as follows:

    DEBUG-skeleton

    enter image description here

    DEBUG-lineends

    enter image description here

    DEBUG-junctions

    enter image description here


    It's actually all a lot simpler in the Terminal with ImageMagick:

    magick Q4J0l.png -morphology Thinning:-1 Skeleton skeleton.png
    magick skeleton.png -morphology HMT lineends ends.png
    magick skeleton.png -morphology HMT linejunctions junctions.png
    

    Or you can produce all 3 images in a single command:

    magick Q4J0l.png \
        -morphology Thinning:-1 Skeleton -write S.png              \
        \( +clone -morphology HMT lineends -write E.png +delete \) \
        -morphology HMT linejunctions J.png