pythonopencvderivativesobel

How do I use 1D gradients to compute a 2D Sobel in OpenCV with a different vector norm?


OpenCV uses an implementation of a Sobel operator defined here (details here). In this implementation, the horizontal derivative is generated, then the vertical derivative is generated, then the gradient is computed as the L2 norm of the derivatives.

Let's say I wanted to use the L1 norm instead. In order to prove this out, I take an image and try to get the same result from OpenCV's Sobel() that I get from manually calculating the L2 norm of the gradients:

import cv2


z_img = cv2.imread(".\\some_image.tif", cv2.IMREAD_UNCHANGED)

z_px_rows = z_img.shape[0]
z_px_cols = z_img.shape[1]

print(f'Center pixel intensity (original): {z_img[z_px_rows // 2, z_px_cols // 2]}')

gx = cv2.Sobel(z_img, cv2.CV_32F, 1, 0, ksize=13)
print(f'Center pixel intensity (gx): {gx[z_px_rows // 2, z_px_cols // 2]}')

gy = cv2.Sobel(z_img, cv2.CV_32F, 0, 1, ksize=13)
print(f'Center pixel intensity (gy): {gy[z_px_rows // 2, z_px_cols // 2]}')

mag, _ = cv2.cartToPolar(gx, gy)
print(f'Center pixel intensity (homebrew sobel): {mag[z_px_rows // 2, z_px_cols // 2]}')

native_sobel = cv2.Sobel(z_img, cv2.CV_32F, 1, 1, ksize=13)
print(f'Center pixel intensity (native sobel): {native_sobel[z_px_rows // 2, z_px_cols // 2]}')

Here I'm using a 32-bit float image where the minimum is 0.0 and the maximum is around 600.0. The output of this is:

Center pixel intensity (original): 537.156982421875
Center pixel intensity (gx): -220087.90625
Center pixel intensity (gy): 350005.25
Center pixel intensity (homebrew sobel): 413451.78125
Center pixel intensity (native sobel): 16357.7548828125

Obviously, something is way off. I would expect those last two values to be the same (not exactly the same, but definitely close). I tried normalizing the pixels in the image to the range [0, 1], which didn't help. I tried converting the images to 8-bit unsigned, which also didn't help. What have I misunderstood about the implementation that would account for this discrepancy?


Solution

  • You are comparing "apples" to "oranges".

    In Python/OpenCV, cv2.Sobel() computes either the X directional derivative or the Y directional derivative or a mixed derivative as follows:

    enter image description here

    This is not the same as the magnitude of the gradient:

    enter image description here

    where x(I) = X directional derivative from the Sobel, y(I) = Y directional derivative from the Sobel with I = src and magnitude = dst

    If you want the L1 norm, then in place of the square root magnitude above, use

    magnitude(I) = |x(I)| + |y(I)|
    

    where x(I) = X directional derivative and y(I) = Y directional derivative (from Sobel) and I = src and magnitude = dot