Python Equivalent for bwmorph

前端 未结 2 2064
-上瘾入骨i
-上瘾入骨i 2021-01-06 01:26

I am still coding a fingerprint image preprocessor on Python. I see in MATLAB there is a special function to remove H breaks and spurs:

bwmorph(a , \'hbreak\         


        
相关标签:
2条回答
  • 2021-01-06 01:39

    Edit October 2017

    the skimage module now has at least 2 options: skeletonize and thin

    Example with comparison

    from skimage.morphology import thin, skeletonize
    import numpy as np
    import matplotlib.pyplot as plt
    
    square = np.zeros((7, 7), dtype=np.uint8)
    square[1:-1, 2:-2] = 1
    square[0, 1] =  1
    thinned = thin(square)
    skel = skeletonize(square)
    
    f, ax = plt.subplots(2, 2)
    ax[0,0].imshow(square)
    ax[0,0].set_title('original')
    ax[0,0].get_xaxis().set_visible(False)
    ax[0,1].axis('off')
    ax[1,0].imshow(thinned)
    ax[1,0].set_title('morphology.thin')
    ax[1,1].imshow(skel)
    ax[1,1].set_title('morphology.skeletonize')
    plt.show()
    

    Original post

    I have found this solution by joefutrelle on github.

    It seems (visually) to give similar results as the Matlab version.

    Hope that helps!

    Edit:

    As it was pointed out in the comments, I'll extend my initial post as the mentioned link might change:

    Looking for a substitute in Python for bwmorph from Matlab I stumbled upon the following code from joefutrelle on Github (at the end of this post as it's very long).

    I have figured out two ways to implement this into my script (I'm a beginner and I'm sure there are better ways!):

    1) copy the whole code into your script and then call the function (but this makes the script harder to read)

    2) copy the code it in a new python file 'foo' and save it. Now copy it in the Python\Lib (eg. C:\Program Files\Python35\Lib) folder. In your original script you can call the function by writing:

    from foo import bwmorph_thin

    Then you'll feed the function with your binary image:

    skeleton = bwmorph_thin(foo_image, n_iter = math.inf)
    

    import numpy as np
    from scipy import ndimage as ndi
    
    # lookup tables for bwmorph_thin
    
    G123_LUT = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1,
           0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0,
           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0,
           1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
           0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0,
           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1,
           0, 0, 0], dtype=np.bool)
    
    G123P_LUT = np.array([0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
           0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,
           1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0,
           0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,
           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0,
           1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1,
           0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           0, 0, 0], dtype=np.bool)
    
    def bwmorph_thin(image, n_iter=None):
        """
        Perform morphological thinning of a binary image
    
        Parameters
        ----------
        image : binary (M, N) ndarray
            The image to be thinned.
    
        n_iter : int, number of iterations, optional
            Regardless of the value of this parameter, the thinned image
            is returned immediately if an iteration produces no change.
            If this parameter is specified it thus sets an upper bound on
            the number of iterations performed.
    
        Returns
        -------
        out : ndarray of bools
            Thinned image.
    
        See also
        --------
        skeletonize
    
        Notes
        -----
        This algorithm [1]_ works by making multiple passes over the image,
        removing pixels matching a set of criteria designed to thin
        connected regions while preserving eight-connected components and
        2 x 2 squares [2]_. In each of the two sub-iterations the algorithm
        correlates the intermediate skeleton image with a neighborhood mask,
        then looks up each neighborhood in a lookup table indicating whether
        the central pixel should be deleted in that sub-iteration.
    
        References
        ----------
        .. [1] Z. Guo and R. W. Hall, "Parallel thinning with
               two-subiteration algorithms," Comm. ACM, vol. 32, no. 3,
               pp. 359-373, 1989.
        .. [2] Lam, L., Seong-Whan Lee, and Ching Y. Suen, "Thinning
               Methodologies-A Comprehensive Survey," IEEE Transactions on
               Pattern Analysis and Machine Intelligence, Vol 14, No. 9,
               September 1992, p. 879
    
        Examples
        --------
        >>> square = np.zeros((7, 7), dtype=np.uint8)
        >>> square[1:-1, 2:-2] = 1
        >>> square[0,1] =  1
        >>> square
        array([[0, 1, 0, 0, 0, 0, 0],
               [0, 0, 1, 1, 1, 0, 0],
               [0, 0, 1, 1, 1, 0, 0],
               [0, 0, 1, 1, 1, 0, 0],
               [0, 0, 1, 1, 1, 0, 0],
               [0, 0, 1, 1, 1, 0, 0],
               [0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
        >>> skel = bwmorph_thin(square)
        >>> skel.astype(np.uint8)
        array([[0, 1, 0, 0, 0, 0, 0],
               [0, 0, 1, 0, 0, 0, 0],
               [0, 0, 0, 1, 0, 0, 0],
               [0, 0, 0, 1, 0, 0, 0],
               [0, 0, 0, 1, 0, 0, 0],
               [0, 0, 0, 0, 0, 0, 0],
               [0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
        """
        # check parameters
        if n_iter is None:
            n = -1
        elif n_iter <= 0:
            raise ValueError('n_iter must be > 0')
        else:
            n = n_iter
    
        # check that we have a 2d binary image, and convert it
        # to uint8
        skel = np.array(image).astype(np.uint8)
    
        if skel.ndim != 2:
            raise ValueError('2D array required')
        if not np.all(np.in1d(image.flat,(0,1))):
            raise ValueError('Image contains values other than 0 and 1')
    
        # neighborhood mask
        mask = np.array([[ 8,  4,  2],
                         [16,  0,  1],
                         [32, 64,128]],dtype=np.uint8)
    
        # iterate either 1) indefinitely or 2) up to iteration limit
        while n != 0:
            before = np.sum(skel) # count points before thinning
    
            # for each subiteration
            for lut in [G123_LUT, G123P_LUT]:
                # correlate image with neighborhood mask
                N = ndi.correlate(skel, mask, mode='constant')
                # take deletion decision from this subiteration's LUT
                D = np.take(lut, N)
                # perform deletion
                skel[D] = 0
    
            after = np.sum(skel) # coint points after thinning
    
            if before == after:
                # iteration had no effect: finish
                break
    
            # count down to iteration limit (or endlessly negative)
            n -= 1
    
        return skel.astype(np.bool)
    
    """
    # here's how to make the LUTs
    
    def nabe(n):
        return np.array([n>>i&1 for i in range(0,9)]).astype(np.bool)
    
    def hood(n):
        return np.take(nabe(n), np.array([[3, 2, 1],
                                          [4, 8, 0],
                                          [5, 6, 7]]))
    def G1(n):
        s = 0
        bits = nabe(n)
        for i in (0,2,4,6):
            if not(bits[i]) and (bits[i+1] or bits[(i+2) % 8]):
                s += 1
        return s==1
    
    g1_lut = np.array([G1(n) for n in range(256)])
    
    def G2(n):
        n1, n2 = 0, 0
        bits = nabe(n)
        for k in (1,3,5,7):
            if bits[k] or bits[k-1]:
                n1 += 1
            if bits[k] or bits[(k+1) % 8]:
                n2 += 1
        return min(n1,n2) in [2,3]
    
    g2_lut = np.array([G2(n) for n in range(256)])
    
    g12_lut = g1_lut & g2_lut
    
    def G3(n):
        bits = nabe(n)
        return not((bits[1] or bits[2] or not(bits[7])) and bits[0])
    
    def G3p(n):
        bits = nabe(n)
        return not((bits[5] or bits[6] or not(bits[3])) and bits[4])
    
    g3_lut = np.array([G3(n) for n in range(256)])
    g3p_lut = np.array([G3p(n) for n in range(256)])
    
    g123_lut  = g12_lut & g3_lut
    g123p_lut = g12_lut & g3p_lut
    """`
    
    0 讨论(0)
  • 2021-01-06 01:54

    You will have to implement those on your own since they aren't present in OpenCV or skimage as far as I know. However, it should be straightforward to check MATLAB's code on how it works and write your own version in Python/NumPy.

    Here is a guide describing in detail NumPy functions exclusively for MATLAB users, with hints on equivalent functions in MATLAB and NumPy: http://wiki.scipy.org/NumPy_for_Matlab_Users

    0 讨论(0)
提交回复
热议问题