def marr_hildreth_edge_detector(image, sigma):
    image_array = np.asarray(image)
    # Smooth the image by Gaussian filter
    smoothed_image_array = gc.gaussconvolve2d(image_array, sigma)
    # Apply Laplacian to smoothed image 
    laplacian_of_img = laplacian(smoothed_image_array)
    # Find zero crossings
    result = find_zero_crossings(laplacian_of_img)
    return result
def canny_edge_detector(image, sigma):

    # Convert to numpy array
    image_array = np.asarray(image)
    result_array = np.zeros((image_array.shape[0], image_array.shape[1]), dtype=np.int)

    # Smooth image by Gaussian filter
    smoothed_image_array = gc.gaussconvolve2d(image_array, sigma)

    # Compute derivative of filtered image
    dx = cid.xderivative(smoothed_image_array.copy())
    dy = cid.yderivative(smoothed_image_array.copy())

    # Find magnitude and orientation of gradient
    row = []
    gradient_array = []
    # Match up the rows
    for ix, iy in zip(dx, dy):
        # For each row, match up columns
        for ixx, iyy in zip(ix, iy):
            gradient_magnitude = math.sqrt((math.pow(ixx,2) + math.pow(iyy,2)))
            gradient_direction = np.arctan2(iyy, ixx)
            row.append(np.asarray([gradient_magnitude, gradient_direction]))
        gradient_array.append(row)
        row = []
    gradient_array = np.asarray(gradient_array)

    # Apply non-maximum suppression
    for row in range(0, len(gradient_array)):
        for col in range(0, len(gradient_array[0])):
            pixel = gradient_array[row][col]
            if(is_maximum(row, col, pixel, gradient_array)):
                result_array[row][col] = 120

    # Apply hysteresis thresholding
    result_array = apply_hysteresis_thresholding(gradient_array, result_array)
    return result_array