In the previous blog, we briefly discussed that an edge can be detected by
- First derivative (local maximum or minimum)
- Second derivative (zero crossings)
In this blog, let’s discuss in detail how we can detect edges using the first order derivative.
Remember that derivatives only exists for continuous functions but the image is a discrete 2D light intensity function. Thus in the last blog, we approximated the image gradients using finite approximation as
For the edge detection case, we will prefer the central difference as shown above. Using this central difference, we can obtain the derivative filter in x and y directions as shown below
Here, we have assumed that the x-coordinate is increasing in the “right”-direction, and y-coordinate in the “down”-direction. By weighting these x and y derivatives, we can obtain different edge detection filters. Let’s see how
1. Sobel Operator
This is obtained by multiplying the x, and y-derivative filters obtained above with some smoothing filter(1D) in the other direction. For example, a 3×3 Sobel-x and Sobel-y filter can be obtained as
As we know that the Gaussian filter is used for blurring thus, the Sobel operator computes the gradient with smoothing. Thus this is less sensitive to noise. Because of separability property of the kernel, the Sobel operator is computationally efficient.
Note: Using larger Sobel kernels leads to more edge blurring, thus some form of edge thinning must be applied to counter this.
When we convolve these Sobel operators with the image, they estimate the gradients in the x, and y-directions(say Gx and Gy). For each point, we can calculate the gradient magnitude and direction as
We can easily infer that the edge direction or the angle will be positive for the transition from dark to white and negative otherwise. Now, let’s see how to do this using OpenCV-Python
OpenCV has a builtin function that calculates the image derivatives using the Sobel operator. Its basic syntax is shown below. You can read more about it here.
1 2 3 4 5 |
cv2.Sobel(src, ddepth, dx, dy[, ksize[, scale[, delta[, borderType]]]]]) # src - input image # ddepth - depth of the output image # dx and dy specify whether Sobel-x or Sobel -y is to be used # ksize - kernel size |
Note: Earlier we have assumed that white to black transition yields negative values. Thus if our output datatype is cv2.CV_8U or np.uint8, this will make all negative values 0. To prevent this, we specify the output datatype to some higher forms, like cv2.CV_16S, cv2.CV_64F etc, take its absolute value and then convert back to cv2.CV_8U.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import cv2 import numpy as np # Create a image with black background and white circle img = np.zeros((500,500),dtype='uint8') cv2.circle(img,(250,250), 150, (255,255,255), -1) # Output dtype = cv2.CV_8U sob_8u = cv2.Sobel(img,cv2.CV_8U,1,0,ksize=3) # Output dtype = cv2.CV_64F. Then take its absolute and convert to cv2.CV_8U sobel_64 = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3) abs_64 = np.absolute(sobel_64) sobel_8u = np.uint8(abs_64) cv2.imshow('a',sob_8u) cv2.imshow('a1',sobel_8u) cv2.waitKey(0) |
The output is shown below
2. Scharr Operator
This operator tries to achieve the perfect rotational symmetry. The 3×3 Scharr filter is shown below
OpenCV provides a builtin function for this
1 |
cv2.Scharr(src, ddepth, dx, dy[, scale[, delta[, borderType]]]]) |
3. Prewitt Operator
In this, the x, and y-derivative filters are weighted with the standard averaging filter as shown below
Here, we discussed only the most common filters. Hope you enjoy reading.
If you have any doubt/suggestion please feel free to ask and I will do my best to help or improve myself. Good-bye until next time.