Search code examples
pythonopencvimage-processingfilteringderivative

What is the Sobel operator?


I tried 5 different implementations of the Sobel operator in Python, one of which I implemented myself, and the results are radically different.

My question is similar to this one, but there are still differences I don't understand with the other implementations.

Is there any agreed on definition of the Sobel operator, and is it always synonymous to "image gradient"?

Even the definition of the Sobel kernel is different from source to source, according to Wikipedia it is [[1, 0, -1],[2, 0, -2],[1, 0, -1]], but according to other sources it is [[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]].

Here is my code where I tried the different techniques:

from scipy import ndimage
import numpy as np
import cv2 as cv
from scipy import ndimage
from PIL import Image, ImageFilter

img = np.random.randint(0, 255, [10, 10]).astype(np.uint8)

def sobel_x(img) :
    return ndimage.convolve(img, np.array([[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]]))

my_sobel = sobel_x(img)
_, numpy_sobel = np.gradient(img)
opencv_sobel = cv.Sobel(img, cv.CV_8UC1, 1, 0)
ndimage_sobel = ndimage.sobel(img, axis=0, mode="constant")
pil_sobel = np.array(Image.fromarray(img).filter(ImageFilter.Kernel((3, 3), (-1, 0, 1, -2, 0, 2, -1, 0, 1), 1, 0)))

print(my_sobel)
print(numpy_sobel)
print(opencv_sobel)
print(ndimage_sobel)
print(pil_sobel)

enter image description here


Solution

  • The Sobel operator estimates the derivative.

    The correct definition of the Sobel operator to estimate the horizontal derivative is:

      | 1  0 -1 |
      | 2  0 -2 | / 8
      | 1  0 -1 |
    

    The division by 8 is important to get the right magnitude. People often leave it out because they don't care about the actual derivative, they care about comparing the gradient in different places of the same image. Multiplying everything by 8 makes no difference there, and so leaving out the /8 keeps things simple.

    You will see the kernel defined with the inverse signs some places. These are cases where the kernel is applied by correlation instead of convolution (which differ by a mirroring of the kernel), such as the case of OpenCV. These can also be cases where people copy stuff without understanding them, resulting in a gradient with the wrong sign.

    But then again, the Sobel operator is mostly applied to obtain the gradient magnitude (the square root of the sum of the squares of the horizontal and vertical derivatives). In this case, reversing the signs doesn't matter any more.


    Note that np.gradient(img) is comparable to convolving with [1,0,-1]/2. This is another way to estimate the derivative. Sobel adds a regularization (==smoothing) in the perpendicular direction.


    You will get a better understanding of each implementation if you use a more meaningful test image. Try for example a black image with a white square in the middle. You will be able to compare the strength of the estimated gradients, their direction (I assume some libraries use a different definition of x and y axes), and you will be able to see the effect of the regularization.