예제 #1
0
def skeletonize_river_mask(I, es, npad=20):
    """
    Skeletonize a binary river mask. Crops mask to river extents, reflects the
    mask at the appropriate borders, skeletonizes, then un-crops the mask.
    
    INPUTS:
        I - binary river mask to skeletonize
        es - NSEW "exit sides" corresponding to the upstream and downstream 
             sides of the image that intersect the river. e.g. 'NS', 'EN', 'WS'
        npad - (Optional) number of pixels to reflect I before skeletonization
               to remove edge effects of skeletonization
    """

    Ic, crop_pads = iu.crop_binary_im(I)

    # Number of pixels to mirror - maximum of 2% of each dimension or npad pixels
    n_vert = max(int(I.shape[0] * 0.02 / 2), npad)
    n_horiz = max(int(I.shape[1] * 0.02 / 2), npad)

    pads = [[0, 0], [0, 0]]
    if 'N' in es:
        pads[0][0] = n_vert
    if 'E' in es:
        pads[1][1] = n_horiz
    if 'S' in es:
        pads[0][1] = n_vert
    if 'W' in es:
        pads[1][0] = n_horiz
    pads = tuple([tuple(pads[0]), tuple(pads[1])])

    # Generate padded image
    Ip = np.pad(Ic, pads, mode='reflect')

    # Skeletonize padded image
    Iskel = morphology.skeletonize(Ip)

    # Remove padding
    Iskel = Iskel[pads[0][0]:Iskel.shape[0] - pads[0][1],
                  pads[1][0]:Iskel.shape[1] - pads[1][1]]
    # Add back what was cropped so skeleton image is original size
    crop_pads_add = ((crop_pads[1], crop_pads[3]), (crop_pads[0],
                                                    crop_pads[2]))
    Iskel = np.pad(Iskel,
                   crop_pads_add,
                   mode='constant',
                   constant_values=(0, 0))

    # Ensure skeleton is prepared for analysis by RivGraph
    # Simplify the skeleton (i.e. remove pixels that don't alter connectivity)
    Iskel = skel_simplify(Iskel)

    # Fill small skeleton holes, re-skeletonize, and re-simplify
    Iskel = iu.fill_holes(Iskel, maxholesize=4)
    Iskel = morphology.skeletonize(Iskel)
    Iskel = skel_simplify(Iskel)

    # Fill single pixel holes
    Iskel = iu.fill_holes(Iskel, maxholesize=1)

    return Iskel
예제 #2
0
def test_crop_binary_im():
    """Test crop_binary_im()."""
    I = np.zeros((5, 5))
    I[0, 0] = 1
    I[1, 2:5] = 1
    Icrop, pads = im_utils.crop_binary_im(I)
    # assertions
    assert np.shape(Icrop) == (2, 5)
    assert np.all(pads == [0, 0, 0, 3])
예제 #3
0
def skeletonize_river_mask(I, es, padscale=2):
    """
    Skeletonizes a binary mask of a river channel network. Differs from
    skeletonize mask above by using knowledge of the exit sides of the river
    with respect to the mask (I) to avoid edge effects of skeletonization by
    mirroring the mask at its ends, then trimming it after processing. As with
    skeletonize_mask, skeleton simplification is performed.

    Parameters
    ----------
    I : np.array
        Binary river mask to skeletonize.
    es : str
        A two-character string (from N, E, S, or W) that denotes which sides
        of the image the river intersects (upstream first) -- e.g. 'NS', 'EW',
        'NW', etc.
    padscale : int, optional
        Pad multiplier that sets the size of the padding. Multplies the blob
        size along the axis of the image that the blob intersect to determine
        the padding distance. The default is 2.

    Returns
    -------
    Iskel : np.array
        The skeletonization of I.

    """
    # Crop image
    Ic, crop_pads = imu.crop_binary_im(I)

    # Pad image (reflects channels at image edges)
    Ip, pads = pad_river_im(Ic, es, pm=padscale)

    # Skeletonize padded image
    Iskel = morphology.skeletonize(Ip)

    # Remove padding
    Iskel = Iskel[pads[0]:Iskel.shape[0] - pads[1],
                  pads[3]:Iskel.shape[1] - pads[2]]
    # Add back what was cropped so skeleton image is original size
    crop_pads_add = ((crop_pads[1], crop_pads[3]), (crop_pads[0],
                                                    crop_pads[2]))
    Iskel = np.pad(Iskel,
                   crop_pads_add,
                   mode='constant',
                   constant_values=(0, 0))

    # Ensure skeleton is prepared for analysis by RivGraph
    # Simplify the skeleton (i.e. remove pixels that don't alter connectivity)
    Iskel = simplify_skel(Iskel)

    # Fill small skeleton holes, re-skeletonize, and re-simplify
    Iskel = imu.fill_holes(Iskel, maxholesize=4)
    Iskel = morphology.skeletonize(Iskel)
    Iskel = simplify_skel(Iskel)

    # Fill single pixel holes
    Iskel = imu.fill_holes(Iskel, maxholesize=1)

    # The handling of edges can leave pieces of the skeleton stranded (i.e.
    # disconnected from the main skeleton). Remove those here by keeping the
    # largest blob.
    Iskel = imu.largest_blobs(Iskel, nlargest=1, action='keep')

    return Iskel