human
3x3 Image Kernels
Many image processing filters can be expressed as 3x3 matrices. This more sophisticated example demonstrates how numba can be used to compile kernels for arbitrary 3x3 kernels, and then provides several predefined kernels for the user to experiment with. 
The UI presents the image to process (along with a dropdown to select a different image) as well as a dropdown that lets the user select which kernel to apply. Additionally there are sliders the permit adjustment to the bias and scale of the final greyscale image. 
Note: Right now, adjusting the scale and bias are not as efficient as possible, because the update function always also applies the kernel (even if it has not changed). A better implementation might have a class that keeps track of the current kernel and output image so that bias and scale can be applied by themselves."
Please write code following the instructions in jupyter notebook style.
 What is a code to 3x3 Image Kernels and the other steps required?
assistant
#Here is code to 3x3 Image Kernels and the other steps required.

@jit
def getitem(img, x, y):
    w, h = img.shape
    if x >= w:
        x = w - 1 - (x - w)
    if y >= h:
        y = h - 1 - (y - h)
    return img[x, y]
 
def filter_factory(kernel):
    ksum = np.sum(kernel)
    if ksum == 0:
        ksum = 1
    k9 = kernel / ksum
 
    @jit
    def kernel_apply(img, out, x, y):
        tmp = 0
        for i in range(3):
            for j in range(3):
                tmp += img[x+i-1, y+j-1] * k9[i, j]
        out[x, y] = tmp
 
    @jit
    def kernel_apply_edge(img, out, x, y):
        tmp = 0
        for i in range(3):
            for j in range(3):
                tmp += getitem(img, x+i-1, y+j-1) * k9[i, j]
        out[x, y] = tmp
 
    @jit
    def kernel_k9(img, out):
        # Loop through all internals
        for x in range(1, img.shape[0] -1):
            for y in range(1, img.shape[1] -1):
                kernel_apply(img, out, x, y)
 
        # Loop through all the edges
        for x in range(img.shape[0]):
            kernel_apply_edge(img, out, x, 0)
            kernel_apply_edge(img, out, x, img.shape[1] - 1)
 
        for y in range(img.shape[1]):
            kernel_apply_edge(img, out, 0, y)
            kernel_apply_edge(img, out, img.shape[0] - 1, y)
 
    return kernel_k9

average = np.array([
    [1, 1, 1],
    [1, 1, 1],
    [1, 1, 1],
], dtype=np.float32)

sharpen = np.array([
    [-1, -1, -1],
    [-1, 12, -1],
    [-1, -1, -1],
], dtype=np.float32)

edge = np.array([
    [ 0, -1,  0],
    [-1,  4, -1],
    [ 0, -1,  0],
], dtype=np.float32)

edge_h = np.array([
    [ 0,  0,  0],
    [-1,  2, -1],
    [ 0,  0,  0],
], dtype=np.float32)

edge_v = np.array([
    [0, -1, 0],
    [0,  2, 0],
    [0, -1, 0],
], dtype=np.float32)

gradient_h = np.array([
    [-1, -1, -1],
    [ 0,  0,  0],
    [ 1,  1,  1],
], dtype=np.float32)

gradient_v = np.array([
    [-1, 0, 1],
    [-1, 0, 1],
    [-1, 0, 1],
], dtype=np.float32)

sobol_h = np.array([
    [ 1,  2,  1],
    [ 0,  0,  0],
    [-1, -2, -1],
], dtype=np.float32)

sobol_v = np.array([
    [-1, 0,...