Search code examples
pythonopencvcomputer-visionsobel

Sobel Filter giving poor results


I was trying to implement the sobel filter in python, but the output was poor and full of noise. The Output image i got was: enter image description here

The Input I gave was(after blurring):

enter image description here

The code for sobel filter is below:

def sobel_filters(img):
  Kx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
  Ky = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])

  Ix = ndimage.filters.convolve(img, Kx)
  Iy = ndimage.filters.convolve(img, Ky)

  G = np.sqrt(np.square(Ix) + np.square(Iy))
  G *= 255.0 / G.max()
  

  return G

I blurred the results using sigma=1.3. The input image size is 512 by 512. I expected the output to be similar to what is shown in here:https://www.adeveloperdiary.com/data-science/computer-vision/how-to-implement-sobel-edge-detection-using-python-from-scratch/


Solution

  • Here is one way to do that in Python/OpenCV. Your issue is that your derivatives are not properly normalized and should be processed as floats. Your normalization also does not take into account negative values. In this answer, I make use of OpenCV built-in Sobel and other methods. So there is no need to introduce scipy.ndimage

    Input:

    enter image description here

    import cv2
    import numpy as np
    import skimage.exposure as exposure
    
    # read the image
    img = cv2.imread('gray_lena.png')
    
    # convert to gray
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    
    # blur
    blur = cv2.GaussianBlur(gray, (0,0), 1.3, 1.3)
    
    # apply sobel derivatives
    sobelx = cv2.Sobel(blur,cv2.CV_64F,1,0,ksize=3)
    sobely = cv2.Sobel(blur,cv2.CV_64F,0,1,ksize=3)
    
    # optionally normalize to range 0 to 255 for proper display
    sobelx_norm= exposure.rescale_intensity(sobelx, in_range='image', out_range=(0,255)).clip(0,255).astype(np.uint8)
    sobely_norm= exposure.rescale_intensity(sobelx, in_range='image', out_range=(0,255)).clip(0,255).astype(np.uint8)
    
    # square 
    sobelx2 = cv2.multiply(sobelx,sobelx)
    sobely2 = cv2.multiply(sobely,sobely)
    
    # add together and take square root
    sobel_magnitude = cv2.sqrt(sobelx2 + sobely2)
    
    # normalize to range 0 to 255 and clip negatives
    sobel_magnitude = exposure.rescale_intensity(sobel_magnitude, in_range='image', out_range=(0,255)).clip(0,255).astype(np.uint8)
    
    # save results
    cv2.imwrite('gray_lena_sobelx_norm.jpg', sobelx_norm)
    cv2.imwrite('gray_lena_sobely_norm.jpg', sobely_norm)
    cv2.imwrite('gray_lena_sobel_magnitude.jpg', sobel_magnitude)
    
    # show results
    cv2.imshow('sobelx_norm', sobelx_norm)  
    cv2.imshow('sobely_norm', sobely_norm)  
    cv2.imshow('sobel_magnitude', sobel_magnitude)  
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    Sobel X (normalized):

    enter image description here

    Sobel Y (normalized):

    enter image description here

    Sobel Magnitude:

    enter image description here