pythonnumpyopencvimage-processing

understanding gradient combination strategy outputs with Sobel operators


I've attempted to implement a Sobel Edge detector, like so:

def sobel(img):
    img = cv2.GaussianBlur(img, (5, 5), 0)
    
    filter_x = np.array([
        [-1, 0, 1],
        [-2, 0, 2],
        [-1, 0, 1]
    ])
    
    filter_y = np.array([
        [-1, -2, -1],
        [0, 0, 0],
        [1, 2, 1]
    ])
    
    S_x = cv2.filter2D(img, -1, filter_x)
    S_y = cv2.filter2D(img, -1, filter_y)
    
#     S_x = cv2.convertScaleAbs(S_x)
#     S_y = cv2.convertScaleAbs(S_y)
#     grad = S_x * 0.5 + S_y * 0.5
    grad = np.sqrt(S_x ** 2 + S_y ** 2)
    grad = np.clip(grad, 0, 255)
    return grad.astype(np.uint8)

img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
plt.figure(figsize=(10, 10))
edge = sobel(img_gray)
plt.imshow(edge, cmap='gray')

The outputs I'm getting are very noisy. For example: Sobel output (L2)

This is happening because I'm computing the L2 Magnitude of the gradients in x and y directions. However, when I switch the strategy to use L1 norms (i.e, using `S_x * 0.5 + S_y * 0.5), I get a much less noisier output:

Sobel output (L1)

Why is this happening? According to various sources, both of these combinations are used, but I want to understand why the L2 variant is so noisy, and how I can avoid it.

Thank you!


Solution

  • cv2.filter2D produces an output of the same type as the input. If the input is uint8, then the Sobel filter will overflow and underflow (negative values will show up as large positive values).

    Convert the input image to a float type before filtering.