Example #1
0
def fill_arr(a, win=(3, 3)):
    """try filling an array"""
    fd = np.array([[32, 64, 128], [16, 0, 1], [8, 4, 2]])  # flow direction
#    if (zone < a.min()) or (zone > a.max()) or (zone is None):
#        print("\nYou need a zone that is within the range of values.")
#        return a, None
    if win[0] == 3:
        pr = 1
    else:
        pr = 0
    ap = np.pad(a, pad_width=(1, pr), mode="constant", constant_values=(0, 0))
    if win == (2, 2):
        a_c = ap[1:, 1:]  # for 2x2 even
        w, h = win
    elif win == (3, 3):
        w, h = win
        a_c = ap[1:-1, 1:-1]   # for 3x3 odd
    a_s = stride(a_c, win=win)  # stride the array
    r, c = a_s.shape[:2]
    out = []
    x = a_s.shape[0]
    y = a_s.shape[1]
    for i in range(x):
        for j in range(y):
            # do stuff
            sub = a_s[i, j].ravel()
            edges = np.asarray([sub[:4], sub[5:]]).ravel()
            e_min = edges[np.argmin(edges)]
            if sub[4] < e_min:
                out.append(e_min)
            else:
                out.append(sub[4])
    out = np.asarray(out).reshape(r, c)
    return out  # , a_s, ap, a_c
Example #2
0
def fill_arr(a, win=(3, 3)):
    """try filling an array
    as in fill, sinks
    """
    #fd = np.array([[32, 64, 128], [16, 0, 1], [8, 4, 2]])  # flow direction
#    if (zone < a.min()) or (zone > a.max()) or (zone is None):
#        print("\nYou need a zone that is within the range of values.")
#        return a, None
    if win[0] == 3:
        pr = 1
    else:
        pr = 0
    ap = np.pad(a, pad_width=(1, pr), mode="constant", constant_values=(0, 0))
    if win == (2, 2):
        a_c = ap[1:, 1:]  # for 2x2 even
    elif win == (3, 3):
        a_c = ap[1:-1, 1:-1]   # for 3x3 odd
    a_s = stride(a_c, win=win)  # stride the array
    r, c = a_s.shape[:2]
    out = []
    x = a_s.shape[0]
    y = a_s.shape[1]
    for i in range(x):
        for j in range(y):
            # do stuff
            sub = a_s[i, j].ravel()
            edges = np.asarray([sub[:4], sub[5:]]).ravel()
            e_min = edges[np.argmax(edges)]  # argmax or argmin???
            if sub[4] < e_min:
                out.append(e_min)
            else:
                out.append(sub[4])
    out = np.asarray(out).reshape(r, c)
    return out  # , a_s, ap, a_c
Example #3
0
def _slope_aspect_demo_(cell_size=2):
    """Demo of the data set below"""
    a = np.array([[0, 1, 2, 3, 3, 3, 2, 1, 0], [1, 2, 3, 4, 4, 4, 3, 2, 1],
                  [2, 3, 4, 5, 5, 5, 4, 3, 2], [3, 4, 5, 5, 5, 5, 5, 4, 3],
                  [3, 4, 5, 5, 5, 5, 5, 4, 3], [3, 4, 5, 5, 5, 5, 5, 4, 3],
                  [2, 3, 4, 5, 5, 5, 4, 3, 2], [1, 2, 3, 4, 4, 4, 3, 2, 1],
                  [0, 1, 2, 3, 3, 3, 2, 1, 0]])
    r, c = a.shape
    data = stride(a, win=(3, 3), stepby=(1, 1))  # produce strided array
    slope = slope_a(a, cell_size=cell_size)
    slope = np.array(slope)
    aspect = [
        aspect_a(data[i][j]) for i in range(data.shape[0])
        for j in range(data.shape[1])
    ]
    aspect = np.array(aspect).reshape((r - 2, c - 2))
    frmt = """
    :---- Slope, Aspect Demo ------------------------------------------------
    :Sample DEM with a cell size of {} units.
    {}\n
    Slope (degrees) ...
    {}\n
    Aspect (degrees) ...
    {}
    """
    args = [cell_size]
    pre = '  ..'
    args.extend([indent(str(i), pre) for i in [a, slope, aspect]])
    print(dedent(frmt).format(*args))
    return a, slope, aspect
Example #4
0
def _slope_aspect_demo_(cell_size=2):
    """Demo of the data set below"""
    a = np.array([[0, 1, 2, 3, 3, 3, 2, 1, 0],
                  [1, 2, 3, 4, 4, 4, 3, 2, 1],
                  [2, 3, 4, 5, 5, 5, 4, 3, 2],
                  [3, 4, 5, 5, 5, 5, 5, 4, 3],
                  [3, 4, 5, 5, 5, 5, 5, 4, 3],
                  [3, 4, 5, 5, 5, 5, 5, 4, 3],
                  [2, 3, 4, 5, 5, 5, 4, 3, 2],
                  [1, 2, 3, 4, 4, 4, 3, 2, 1],
                  [0, 1, 2, 3, 3, 3, 2, 1, 0]])
    r, c = a.shape
    data = stride(a, win=(3, 3), stepby=(1, 1))  # produce strided array
    slope = slope_a(a, cell_size=cell_size)
    slope = np.array(slope)
    aspect = [aspect_a(data[i][j])
              for i in range(data.shape[0])
              for j in range(data.shape[1])]
    aspect = np.array(aspect).reshape((r-2, c-2))
    frmt = """
    :---- Slope, Aspect Demo ------------------------------------------------
    :Sample DEM with a cell size of {} units.
    {}\n
    Slope (degrees) ...
    {}\n
    Aspect (degrees) ...
    {}
    """
    args = [cell_size]
    pre = '  ..'
    args.extend([indent(str(i), pre) for i in [a, slope, aspect]])
    print(dedent(frmt).format(*args))
    return a, slope, aspect
Example #5
0
def aspect_a(a, cell_size=1, flat=0.1, degrees=True, keepdims=False):
    """Return the aspect of a slope in degrees from North.
    :Requires:
    :--------
    : a - an input 2d array. X and Y represent coordinates of the Z values
    : cell_size - needed to proper flat calculation
    : flat - degree value, e.g. flat surface <= 0.05 deg
    :        0.05 deg => 8.7e-04 rad   0.10 deg => 1.7e-02 rad
    :
    """
    if not isinstance(flat, (int, float)):
        flat = 0.1
    a_s = stride(a, win=(3, 3), stepby=(1, 1))
    if a_s.ndim < 4:
        new_shape = (1, ) * (4 - len(a_s.shape)) + a_s.shape
        a_s = a_s.reshape(new_shape)
    f_dxyz = np.array([[1, 2, 1], [2, 0, 2], [1, 2, 1]], dtype="float64")
    a_s = a_s * f_dxyz
    #
    dz_dx, dz_dy = filter_a(a_s, a_filter=f_dxyz, cell_size=1)
    #
    asp = np.arctan2(dz_dy, -dz_dx)  # relative to East
    # get the slope
    s = np.sqrt((dz_dx * cell_size)**2 + (dz_dy * cell_size)**2)
    asp = np.rad2deg(asp)
    asp = np.mod((450.0 - asp), 360.)  # simplest way to get azimuth
    asp = np.where(s <= flat, -1, asp)
    if not keepdims:
        asp = np.squeeze(asp)
    if not degrees:
        asp = np.deg2rad(asp)
    return asp
Example #6
0
def slope_a(a, cell_size=1, kern=None, degrees=True, verb=False, keep=False):
    """Return slope in degrees for an input array using 3rd order
    finite difference method for a 3x3 moing window view into the array.

    Requires:
    ---------
    - a : an input 2d array. X and Y represent coordinates of the Z values
    - cell_size : cell size, must be in the same units as X and Y
    - kern : kernel to use
    - degrees : True, returns degrees otherwise radians
    - verb : True, to print results
    - keep : False, to remove/squeeze extra dimensions
    - filter :
        np.array([[1, 2, 1], [2, 0, 2], [1, 2, 1]]) **current default

    Notes:
    ------

    ::

        dzdx: sum(col2 - col0)/8*cellsize
        dzdy: sum(row2 - row0)/8*celsize
        Assert the array is ndim=4 even if (1,z,y,x)
        general         dzdx      +    dzdy     =    dxyz
        [[a, b, c],  [[1, 0, 1],   [[1, 2, 1]       [[1, 2, 1]
         [d, e, f]    [2, 0, 2], +  [0, 0, 0]   =    [2, 0, 2],
         [g, h, i]    [1, 0, 1]]    [1, 2, 1]]       [1, 2, 1]]

    """
    frmt = """\n    :----------------------------------------:
    :{}\n    :input array...\n    {}\n    :slope values...\n    {!r:}
    :----------------------------------------:
    """
    # ---- stride the data and calculate slope for 3x3 sliding windows ----
    np.set_printoptions(edgeitems=10, linewidth=100, precision=1)
    a_s = stride(a, win=(3, 3), stepby=(1, 1))
    if a_s.ndim < 4:
        new_shape = (1,) * (4-len(a_s.shape)) + a_s.shape
        a_s = a_s.reshape(new_shape)
    #
    kern = kernels(kern)  # return the kernel if specified
    # ---- default filter, apply the filter to the array ----
    #
    dz_dx, dz_dy = filter_a(a_s, a_filter=kern, cell_size=cell_size)
    #
    s = np.sqrt(dz_dx**2 + dz_dy**2)
    if degrees:
        s = np.rad2deg(np.arctan(s))
    if not keep:
        s = np.squeeze(s)
    if verb:
        p = "    "
        args = ["Results for slope_a... ",
                indent(str(a), p), indent(str(s), p)]
        print(dedent(frmt).format(*args))
    return s
Example #7
0
def slope_a(a, cell_size=1, degrees=True, verbose=False, keepdims=False):
    """Return slope in degrees for an input array using 3rd order
    :  finite difference method for a 3x3 moing window view into the array.
    :
    :Requires:
    :--------
    : a - an input 2d array. X and Y represent coordinates of the Z values
    : cell_size - must be in the same units as X and Y
    : degrees - True, returns degrees otherwise radians
    : verbose - True, to print results
    : keepdims - False, to remove/squeeze extra dimensions
    : filter - np.array([[1, 2, 1], [2, 0, 2], [1, 2, 1]]) **current default
    :
    :Notes:    dzdx: sum(col2 - col0)/8*cellsize
    :-----     dzdy: sum(row2 - row0)/8*celsize
    : Assert the array is ndim=4 even if (1,z,y,x)
    :   general         dzdx      +    dzdy     =    dxyz
    :   [[a, b, c],  [[1, 0, 1],   [[1, 2, 1]       [[1, 2, 1]
    :    [d, e, f]    [2, 0, 2], +  [0, 0, 0]   =    [2, 0, 2],
    :    [g, h, i]    [1, 0, 1]]    [1, 2, 1]]       [1, 2, 1]]
    :
    """
    frmt = """\n    :----------------------------------------:
    :{}\n    :input array...\n    {}\n    :slope values...\n    {!r:}
    :----------------------------------------:
    """
    # ---- stride the data and calculate slope for 3x3 sliding windows ----
    np.set_printoptions(edgeitems=10, linewidth=100, precision=1)
    a_s = stride(a, win=(3, 3), stepby=(1, 1))
    if a_s.ndim < 4:
        new_shape = (1, ) * (4 - len(a_s.shape)) + a_s.shape
        a_s = a_s.reshape(new_shape)
    f_dxyz = np.array([[1, 2, 1], [2, 0, 2], [1, 2, 1]], dtype="float64")
    # ---- default filter, apply the filter to the array ----
    #
    dz_dx, dz_dy = filter_a(a_s, a_filter=f_dxyz, cell_size=cell_size)
    #
    s = np.sqrt(dz_dx**2 + dz_dy**2)
    if degrees:
        s = np.rad2deg(np.arctan(s))
    if not keepdims:
        s = np.squeeze(s)
    if verbose:
        from textwrap import indent, dedent  # if not imported
        p = "    "
        args = [
            "Results for slope_a... ",
            indent(str(a), p),
            indent(str(s), p)
        ]
        print(dedent(frmt).format(*args))
    return s
Example #8
0
def expand_zone(a, zone=None, win=2):
    """Expand a value (zone) in a 2D array, normally assumed to represent a
    raster surface.

    zone : number
        The value/class to expand into the surrounding cells
    win : list/tuple
        select a (2, 2) or (3, 3) moving window
    """
    msg = "\nYou need a zone that is within the range of values."
    if zone is None:
        print(msg)
        return a, None
    if (zone < a.min()) or (zone > a.max()):
        print(msg)
        return a, None
    if win not in (2, 3):
        win = 2
    p = [1, 0][win == 2]  # check for 2 or 3 in win
    ap = np.pad(a, pad_width=(1, p), mode="constant", constant_values=(0, 0))
    # n, m = ap.shape
    if win == 2:
        a_c = ap[1:, 1:]  # for 2x2 even
    elif win == 3:
        a_c = ap[1:-1, 1:-1]  # for 3x3 odd
    a_s = stride(ap, win=(win, win), stepby=(win, win))  # stride the array
    r, c = a_s.shape[:2]
    out = []
    x = a_s.shape[0]
    y = a_s.shape[1]
    for i in range(x):
        for j in range(y):
            if zone in a_s[i, j]:
                out.append(1)
            else:
                out.append(0)
    out1 = np.asarray(out).reshape(r, c)
    out = np.repeat(np.repeat(out1, 2, axis=1), 2, axis=0)
    dx, dy = np.array(out.shape) - np.array(a.shape)
    if dx != 0:
        out = out[:dx, :dy]
    final = np.where(out == 1, zone, a_c)
    return final
Example #9
0
def expand_zone(a, zone=None, win=2):
    """Expand a value (zone) in a 2D array, normally assumed to represent a
    raster surface.

    zone : number
        The value/class to expand into the surrounding cells
    win : list/tuple
        select a (2, 2) or (3, 3) moving window
    """
    msg = "\nYou need a zone that is within the range of values."
    if zone is None:
        print(msg)
        return a, None
    if (zone < a.min()) or (zone > a.max()):
        print(msg)
        return a, None
    if win not in (2, 3):
        win = 2
    p = [1, 0][win == 2]  # check for 2 or 3 in win
    ap = np.pad(a, pad_width=(1, p), mode="constant", constant_values=(0, 0))
    # n, m = ap.shape
    if win == 2:
        a_c = ap[1:, 1:]  # for 2x2 even
    elif win == 3:
        a_c = ap[1:-1, 1:-1]  # for 3x3 odd
    a_s = stride(ap, win=(win, win), stepby=(win, win))  # stride the array
    r, c = a_s.shape[:2]
    out = []
    x = a_s.shape[0]
    y = a_s.shape[1]
    for i in range(x):
        for j in range(y):
            if zone in a_s[i, j]:
                out.append(1)
            else:
                out.append(0)
    out1 = np.asarray(out).reshape(r, c)
    out = np.repeat(np.repeat(out1, 2, axis=1), 2, axis=0)
    dx, dy = np.array(out.shape) - np.array(a.shape)
    if dx != 0:
        out = out[:dx, :dy]
    final = np.where(out == 1, zone, a_c)
    return final
Example #10
0
def aspect_a(a, cell_size=1, flat=0.1, degrees=True, keepdims=False):
    """Return the aspect of a slope in degrees from North.

    Requires:
    --------
    - a :
        an input 2d array. X and Y represent coordinates of the Z values
    - cell_size :
        needed to proper flat calculation
    - flat :
        degree value, e.g. flat surface <= 0.05 deg

        0.05 deg => 8.7e-04 rad   0.10 deg => 1.7e-02 rad
    """
    if not isinstance(flat, (int, float)):
        flat = 0.1
    a_s = stride(a, win=(3, 3), stepby=(1, 1))
    if a_s.ndim < 4:
        new_shape = (1,) * (4-len(a_s.shape)) + a_s.shape
        a_s = a_s.reshape(new_shape)
    f_dxyz = np.array([[1, 2, 1], [2, 0, 2], [1, 2, 1]], dtype="float64")
    a_s = a_s * f_dxyz
    #
    dz_dx, dz_dy = filter_a(a_s, a_filter=f_dxyz, cell_size=1)
    #
    asp = np.arctan2(dz_dy, -dz_dx)     # relative to East
    # get the slope
    s = np.sqrt((dz_dx*cell_size)**2 + (dz_dy*cell_size)**2)
    asp = np.rad2deg(asp)
    asp = np.mod((450.0 - asp), 360.)   # simplest way to get azimuth
    asp = np.where(s <= flat, -1, asp)
    if not keepdims:
        asp = np.squeeze(asp)
    if not degrees:
        asp = np.deg2rad(asp)
    return asp
Example #11
0
def a_filter(a, mode=1, pad_output=True, ignore_nodata=True, nodata=None):
    """Various filters applied to an array.

    Requires:
    --------
    stride : function
        the `stride` function is used internally in this function
    a : array
        an array that will be strided using a 3x3 window
    pad_output : boolean
        True, produces a masked array padded so that the shape
        is the same as the input
    ignore_nodata : boolean
        True, all values used, False, array contains nodata
    nodata : None or number
        None :
            max int or float used
        value :
            use this value in integer or float form otherwise


    mode :
        a dictionary containing a choice from
    ::

        1.  `all_f`    : all 1's
        2.  `no_cnt`   : no center
        3.  `cross_f`  : cross filter, corners
        4.  `plus_f`   : up down, left right
        5.  `gradient` : `6 7 8 9 10` directional gradients
        11. `lap_33`   : laplacian
        12. `line_h`   : line detection, horizonal
        13. `line_ld`  : line detection, left diagonal
        14. `line_rd`  : line detection, right diagonal
        15. `line_v`   : line detection, vertical
        16. `high`     :
        17. `sob_hor`  : Sobel horizontal
        18. `sob_vert` : Sobel vertical
        19. `emboss`   :
        20. `sharp1`   : sharpen 1
        22. `sharp2`   : sharpen 2
        23. `sharp3`   : sharpen 3 highpass 3x3
        24. `lowpass`  : lowpass filter

    Notes:
    -----
        Only 3x3 filters covered here.  The output array is padded with np.nan
        and the array is returned as a masked array.

    >>> a0 = pyramid(core=4, steps=5, incr=(1, 1))
    >>> a0 = a0 * 2  # multiply by a number to increase slope
    >>> a0 = (pyramid(core=4, steps=5, incr=(1, 1)) + 1) * 2  # is also good!

    References:
    ----------
    ..
    [1]
    http://desktop.arcgis.com/en/arcmap/latest/tools/spatial-analyst-toolbox/how-filter-works.htm

    [2]
    http://desktop.arcgis.com/en/arcmap/latest/manage-data/raster-and-images/convolution-function.htm

    [3]
    https://github.com/scikit-image/scikit-image/tree/master/skimage/filters
    """
    n = np.nan
    all_f = [1, 1, 1, 1, 1, 1, 1, 1, 1]
    no_cnt = [1, 1, 1, 1, n, 1, 1, 1, 1]
    cross_f = [1, 0, 1, 0, 0, 0, 1, 0, 1]
    plus_f = [0, 1, 0, 1, 0, 1, 0, 1, 0]
    grad_e = [1, 0, -1, 2, 0, -2, 1, 0, -1]
    grad_n = [-1, -2, -1, 0, 0, 0, 1, 2, 1]
    grad_ne = [0, -1, -2, 1, 0, -1, 2, 1, 0]
    grad_nw = [2, -1, 0, -1, 0, 1, 0, 1, 2]
    grad_s = [1, 2, 1, 0, 0, 0, -1, -2, -1]
    grad_w = [-1, 0, 1, -2, 0, 2, -1, 0, 1]
    lap_33 = [0, -1, 0, -1, 4, -1, 0, -1, 0]
    line_h = [-1, -1, -1, 2, 2, 2, -1, -1, -1]
    line_ld = [2, -1, -1, -1, 2, -1, -1, -1, 2]
    line_rd = [-1, -1, 2, -1, 2, -1, 2, -1, -1]
    line_v = [-1, 0, -1, -1, 2, -1, -1, 2, -1]
    high = [-0.7, -1.0, -0.7, -1.0, 6.8, -1.0, -0.7, -1.0, -0.7]  # arc
    sob_hor = [1, 2, 1, 0, 0, 0, -1, -2, -1]   # sobel y  /4.0 weights
    sob_vert = [1, 0, -1, 2, 0, -2, 1, 0, -1]  # sobel x  /4
    emboss = [-1, -1, 0, -1, 0, 1, 0, 1, 1]
    sharp1 = [0., -0.25, 0., -0.25, 2.0, -0.25, 0., -0.25, 0.]  # arc
    sharp2 = [-0.25, -0.25, -0.25, -0.25, 3.0, -0.25, -0.25, -0.25, -0.25]
    sharp3 = [-1, -1, -1, -1, 9, -1, -1, -1, -1]  # arc
    lowpass = [1, 2, 1, 2, 4, 2, 1, 2, 1]  # arc
    # ---- assemble the dictionary ----
    d = {1: all_f, 2: no_cnt, 3: cross_f, 4: plus_f, 5: grad_e,
         6: grad_n, 7: grad_ne, 8: grad_nw, 9: grad_s, 10: grad_w,
         11: lap_33, 12: line_h, 13: line_ld, 14: line_rd, 15: line_v,
         16: high, 17: sob_hor, 18: sob_vert, 19: emboss, 20: sharp1,
         21: sharp2, 23: sharp3, 24: lowpass}
    filter_ = np.array(d[mode]).reshape(3, 3)
    # ---- stride the input array ----
    a_strided = stride(a)
    if ignore_nodata:
        c = np.sum(a_strided * filter_, axis=(2, 3))
    else:
        c = np.nansum(a_strided * filter_, axis=(2, 3))
    if pad_output:
        pad_ = nodata
        if nodata is None:
            if c.dtype.name in ('int', 'int32', 'int64'):
                pad_ = min([0, -1, a.min()-1])
            else:
                pad_ = min([0.0, -1.0, a.min()-1])
        c = np.lib.pad(c, (1, 1), "constant", constant_values=(pad_, pad_))
        m = np.where(c == pad_, 1, 0)
        c = np.ma.array(c, mask=m, fill_value=None)
    return c
Example #12
0
def a_filter(a, mode=1, pad_output=True, ignore_nodata=True, nodata=None):
    """Various filters applied to an array.

    Requires:
    --------
    stride : function
        the `stride` function is used internally in this function
    a : array
        an array that will be strided using a 3x3 window
    pad_output : boolean
        True, produces a masked array padded so that the shape
        is the same as the input
    ignore_nodata : boolean
        True, all values used, False, array contains nodata
    nodata : None or number
        None :
            max int or float used
        value :
            use this value in integer or float form otherwise


    mode :
        a dictionary containing a choice from
    ::

        1.  `all_f`    : all 1's
        2.  `no_cnt`   : no center
        3.  `cross_f`  : cross filter, corners
        4.  `plus_f`   : up down, left right
        5.  `gradient` : `6 7 8 9 10` directional gradients
        11. `lap_33`   : laplacian
        12. `line_h`   : line detection, horizonal
        13. `line_ld`  : line detection, left diagonal
        14. `line_rd`  : line detection, right diagonal
        15. `line_v`   : line detection, vertical
        16. `high`     :
        17. `sob_hor`  : Sobel horizontal
        18. `sob_vert` : Sobel vertical
        19. `emboss`   :
        20. `sharp1`   : sharpen 1
        22. `sharp2`   : sharpen 2
        23. `sharp3`   : sharpen 3 highpass 3x3
        24. `lowpass`  : lowpass filter

    Notes:
    -----
        Only 3x3 filters covered here.  The output array is padded with np.nan
        and the array is returned as a masked array.

    >>> a0 = pyramid(core=4, steps=5, incr=(1, 1))
    >>> a0 = a0 * 2  # multiply by a number to increase slope
    >>> a0 = (pyramid(core=4, steps=5, incr=(1, 1)) + 1) * 2  # is also good!

    References:
    ----------
    ..
    [1]
    http://desktop.arcgis.com/en/arcmap/latest/tools/spatial-analyst-toolbox/how-filter-works.htm

    [2]
    http://desktop.arcgis.com/en/arcmap/latest/manage-data/raster-and-images/convolution-function.htm

    [3]
    https://github.com/scikit-image/scikit-image/tree/master/skimage/filters
    """
    n = np.nan
    all_f = [1, 1, 1, 1, 1, 1, 1, 1, 1]
    no_cnt = [1, 1, 1, 1, n, 1, 1, 1, 1]
    cross_f = [1, 0, 1, 0, 0, 0, 1, 0, 1]
    plus_f = [0, 1, 0, 1, 0, 1, 0, 1, 0]
    grad_e = [1, 0, -1, 2, 0, -2, 1, 0, -1]
    grad_n = [-1, -2, -1, 0, 0, 0, 1, 2, 1]
    grad_ne = [0, -1, -2, 1, 0, -1, 2, 1, 0]
    grad_nw = [2, -1, 0, -1, 0, 1, 0, 1, 2]
    grad_s = [1, 2, 1, 0, 0, 0, -1, -2, -1]
    grad_w = [-1, 0, 1, -2, 0, 2, -1, 0, 1]
    lap_33 = [0, -1, 0, -1, 4, -1, 0, -1, 0]
    line_h = [-1, -1, -1, 2, 2, 2, -1, -1, -1]
    line_ld = [2, -1, -1, -1, 2, -1, -1, -1, 2]
    line_rd = [-1, -1, 2, -1, 2, -1, 2, -1, -1]
    line_v = [-1, 0, -1, -1, 2, -1, -1, 2, -1]
    high = [-0.7, -1.0, -0.7, -1.0, 6.8, -1.0, -0.7, -1.0, -0.7]  # arc
    sob_hor = [1, 2, 1, 0, 0, 0, -1, -2, -1]  # sobel y  /4.0 weights
    sob_vert = [1, 0, -1, 2, 0, -2, 1, 0, -1]  # sobel x  /4
    emboss = [-1, -1, 0, -1, 0, 1, 0, 1, 1]
    sharp1 = [0., -0.25, 0., -0.25, 2.0, -0.25, 0., -0.25, 0.]  # arc
    sharp2 = [-0.25, -0.25, -0.25, -0.25, 3.0, -0.25, -0.25, -0.25, -0.25]
    sharp3 = [-1, -1, -1, -1, 9, -1, -1, -1, -1]  # arc
    lowpass = [1, 2, 1, 2, 4, 2, 1, 2, 1]  # arc
    # ---- assemble the dictionary ----
    d = {
        1: all_f,
        2: no_cnt,
        3: cross_f,
        4: plus_f,
        5: grad_e,
        6: grad_n,
        7: grad_ne,
        8: grad_nw,
        9: grad_s,
        10: grad_w,
        11: lap_33,
        12: line_h,
        13: line_ld,
        14: line_rd,
        15: line_v,
        16: high,
        17: sob_hor,
        18: sob_vert,
        19: emboss,
        20: sharp1,
        21: sharp2,
        23: sharp3,
        24: lowpass
    }
    filter_ = np.array(d[mode]).reshape(3, 3)
    # ---- stride the input array ----
    a_strided = stride(a)
    if ignore_nodata:
        c = np.sum(a_strided * filter_, axis=(2, 3))
    else:
        c = np.nansum(a_strided * filter_, axis=(2, 3))
    if pad_output:
        pad_ = nodata
        if nodata is None:
            if c.dtype.name in ('int', 'int32', 'int64'):
                pad_ = min([0, -1, a.min() - 1])
            else:
                pad_ = min([0.0, -1.0, a.min() - 1])
        c = np.lib.pad(c, (1, 1), "constant", constant_values=(pad_, pad_))
        m = np.where(c == pad_, 1, 0)
        c = np.ma.array(c, mask=m, fill_value=None)
    return c