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?
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:
This is not the same as the magnitude of the gradient:
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