Edge detection is a crucial step in image processing, as it helps to identify object boundaries within an image. It is a vital technique in computer vision, machine learning, and artificial intelligence applications. In this blog post, we will take a closer look at the Edge Detection techniques using OpenCV in Python.

Before we get into the technical details, let’s understand what edge detection is and its significance in image processing.

## What is Edge Detection?

Before we dive into the details of edge detection, let us briefly explain what edges are.

An edge is a boundary between two regions with different properties, such as intensity, texture, or color. Edges represent important features in an image that can be used to extract information or perform tasks, such as image segmentation, object recognition, or motion tracking.

Edge detection is the process of identifying the boundaries of objects within an image. These boundaries are the areas where the intensity of an image changes abruptly. Edge detection algorithms aim to identify these areas of abrupt intensity changes, which correspond to the boundaries of objects.

Edges can be of two types- **gradient** and **Laplacian**.

Gradient edges are detected by calculating the gradient of the image, while Laplacian edges are detected by finding the zero-crossings in the second derivative of the image.

## Why is Edge Detection Important?

Edge detection is a crucial step in many image processing applications, including object detection, segmentation, and tracking. It helps in identifying and extracting important features from images, such as corners, lines, and curves. These features are then used in subsequent processing steps, such as classification and recognition.

Now that we have a basic understanding of edge detection let’s dive into the Canny Edge Detection technique using OpenCV in Python.

## Edge Detection OpenCV Python Techniques

There are several edge detection techniques in OpenCV Python, including Canny, Sobel, Laplacian, and Prewitt. Each technique has its advantages and disadvantages, and the choice of the technique depends on the specific application requirements. In the following sections, we will discuss each technique in detail.

### Canny Edge Detection

The Canny Edge Detection technique is a multi-stage algorithm that aims to identify the edges in an image accurately. It was developed by John F. Canny in 1986 and has since become one of the most widely used edge detection techniques.

It is a multi-stage technique. It applies Gaussian smoothing to the image to remove noise, calculates the gradient magnitude and direction, performs non-maximum suppression to thin the edges, and applies hysteresis thresholding to determine the strong and weak edges.

The Canny algorithm has five main steps:

**Gaussian Smoothing**: This step aims to reduce the noise in the image by applying a Gaussian filter to the image.**Gradient Calculation**: This step calculates the gradient of the smoothed image using the Sobel operator.**Non-maximum Suppression**: This step aims to thin out the edges by suppressing all non-maximum pixels in the gradient direction.**Double Thresholding**: This step aims to identify potential edges by applying two thresholds to the gradient image.**Edge Tracking by Hysteresis**: This step aims to connect the edges by tracing along the edges above the high threshold and below the low threshold.

Let’s understand each of these steps in detail.

#### Gaussian Smoothing

The first step in the Canny Edge Detection algorithm is to apply a Gaussian filter to the image. The Gaussian filter helps to reduce the noise in the image by smoothing out the intensity changes. The amount of smoothing is controlled by the size of the Gaussian kernel.

The Gaussian filter is a weighted average of the neighboring pixels. The weights are calculated based on the distance of the neighboring pixel from the center pixel. The closer the neighboring pixel, the higher the weight.

In OpenCV, we can apply the Gaussian filter using the `cv2.GaussianBlur()`

function. The function takes the image and the kernel size as input parameters.

```
import cv2
import numpy as np
img = cv2.imread('image.jpg', 0)
# Apply Gaussian filter
blur = cv2.GaussianBlur(img, (5, 5), 0)
```

In the above code, we first read the image using the `cv2.imread()`

function. We then apply the Gaussian filter using the `cv2.GaussianBlur()`

function with a kernel size of 5×5.

#### Gradient Calculation

The next step in the Canny Edge Detection algorithm is to calculate the gradient of the smoothed image. The gradient represents the rate of change of intensity in the image. It can be calculated using the Sobel operator, which is a simple filter that highlights edges in the horizontal and vertical directions.

The Sobel operator is a 3×3 kernel that convolves with the image to calculate the gradient in the horizontal and vertical directions. The horizontal Sobel operator highlights the edges in the horizontal direction, while the vertical Sobel operator highlights the edges in the vertical direction.

In OpenCV, we can apply the Sobel operator using the `cv2.Sobel()`

function. The function takes the image, the data type, and the direction of the gradient as input parameters.

```
import cv2
import numpy as np
img = cv2.imread('image.jpg', 0)
# Apply Gaussian filter
blur = cv2.GaussianBlur(img, (5, 5), 0)
# Apply Sobel operator
sobelx = cv2.Sobel(blur, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(blur, cv2.CV_64F, 0, 1, ksize=3)
```

In the above code, we first apply the Gaussian filter to the image. We then apply the Sobel operator using the `cv2.Sobel()`

function with a kernel size of 3×3. We calculate the horizontal and vertical gradients separately using the `1`

and `0`

arguments, respectively.

#### Non-maximum Suppression

The third step in the Canny Edge Detection algorithm is to thin out the edges by suppressing all non-maximum pixels in the gradient direction. This step ensures that only the edges with maximum intensity are retained, and the other edges are eliminated.

In this step, we first determine the gradient direction at each pixel. We then compare the intensity of the pixel with the neighboring pixels in the gradient direction. If the intensity is maximum, the pixel is retained; otherwise, it is suppressed.

```
import cv2
import numpy as np
img = cv2.imread('image.jpg', 0)
# Apply Gaussian filter
blur = cv2.GaussianBlur(img, (5, 5), 0)
# Apply Sobel operator
sobelx = cv2.Sobel(blur, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(blur, cv2.CV_64F, 0, 1, ksize=3)
# Calculate gradient direction and magnitude
grad_direction = np.arctan2(sobely, sobelx)
grad_magnitude = np.sqrt(np.square(sobely) + np.square(sobelx))
# Non-maximum suppression
height, width = img.shape
suppressed = np.zeros((height, width))
for i in range(1, height - 1):
for j in range(1, width - 1):
angle = grad_direction[i, j] * 180 / np.pi
if angle < 0:
angle += 180
q = np.round(angle / 45)
p1 = grad_magnitude[i-1, j-q]
p2 = grad_magnitude[i, j]
p3 = grad_magnitude[i+1, j+q]
if p2 >= p1 and p2 >= p3:
suppressed[i, j] = p2
```

In the above code, we first calculate the gradient direction and magnitude using the horizontal and vertical gradients. We then perform non-maximum suppression by iterating over each pixel and comparing its intensity with the neighboring pixels in the gradient direction. If the intensity is maximum, the pixel is retained; otherwise, it is suppressed. The angle of the gradient direction is rounded to one of four possible directions (0, 45, 90, or 135 degrees) using the `np.round()`

function. This is done to simplify the comparison with the neighboring pixels.

#### Hysteresis Thresholding

The final step in the Canny Edge Detection algorithm is to apply hysteresis thresholding to determine the edges.

In this step, we set two threshold values: a high threshold value and a low threshold value. Pixels with intensity values above the high threshold are considered strong edges, while pixels with intensity values between the high and low thresholds are considered weak edges. Pixels with intensity values below the low threshold are considered non-edges.

We then perform edge linking to connect the weak edges to the strong edges. This is done by iterating over each weak edge pixel and checking if any of its neighboring pixels are strong edges. If a neighboring pixel is a strong edge, the weak edge pixel is promoted to a strong edge.

```
import cv2
import numpy as np
img = cv2.imread('image.jpg', 0)
# Apply Gaussian filter
blur = cv2.GaussianBlur(img, (5, 5), 0)
# Apply Sobel operator
sobelx = cv2.Sobel(blur, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(blur, cv2.CV_64F, 0, 1, ksize=3)
# Calculate gradient direction and magnitude
grad_direction = np.arctan2(sobely, sobelx)
grad_magnitude = np.sqrt(np.square(sobely) + np.square(sobelx))
# Non-maximum suppression
height, width = img.shape
suppressed = np.zeros((height, width))
for i in range(1, height - 1):
for j in range(1, width - 1):
angle = grad_direction[i, j] * 180 / np.pi
if angle < 0:
angle += 180
q = np.round(angle / 45)
p1 = grad_magnitude[i-1, j-q]
p2 = grad_magnitude[i, j]
p3 = grad_magnitude[i+1, j+q]
if p2 >= p1 and p2 >= p3:
suppressed[i, j] = p2
# Hysteresis thresholding
low_threshold = 0.1 * np.max(suppressed)
high_threshold = 0.3 * np.max(suppressed)
strong_edges = (suppressed > high_threshold)
weak_edges = (suppressed >= low_threshold) & (suppressed <= high_threshold)
edges = np.zeros((height, width))
for i in range(1, height - 1):
for j in range(1, width - 1):
if strong_edges[i, j]:
edges[i, j] = 255
elif weak_edges[i, j]:
if (strong_edges[i-1:i+2, j-1:j+2]).any():
edges[i, j] = 255
```

In the above code, we first set the high and low threshold values as a percentage of the maximum intensity in the suppressed image. We then identify the strong and weak edges based on their intensity values using Boolean masking. We then perform edge linking by iterating over each weak edge pixel and checking if any of its neighboring pixels are strong edges. If a neighboring pixel is a strong edge, the weak edge pixel is promoted to a strong edge.

### Sobel Edge Detection

The Sobel edge detection algorithm is another popular edge detection technique in OpenCV Python. It was developed by Irwin Sobel in 1968 and is widely used in computer vision applications. The Sobel operator calculates the gradient of the image intensity in the x and y directions. The gradient magnitude and direction can be calculated using the following formulas:

```
Gx = [-1 0 1; -2 0 2; -1 0 1] * A
Gy = [-1 -2 -1; 0 0 0; 1 2 1] * A
G = sqrt(Gx^2 + Gy^2)
```

The Sobel operator is implemented in OpenCV Python using the `cv2.Sobel()`

function. The function takes three arguments, the input image, the data type of the output image, and the derivative order in the x and y directions. The output of the function is a gradient image that represents the edges in the image.

Here’s an example of Sobel edge detection in OpenCV Python:

```
import cv2
import numpy as np
# Load the image
img = cv2.imread('image.jpg')
# Convert the image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply Gaussian smoothing
blur = cv2.GaussianBlur(gray, (3, 3), 0)
# Calculate the Sobel gradient in the x and y directions
sobelx = cv2.Sobel(blur, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(blur, cv2.CV_64F, 0, 1, ksize=3)
# Compute the gradient magnitude and direction
mag, angle = cv2.cartToPolar(sobelx, sobely, angleInDegrees=True)
# Threshold the magnitude to obtain the edges
edges = cv2.threshold(mag, 50, 255, cv2.THRESH_BINARY)[1]
# Display the result
cv2.imshow('Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
```

### Laplacian Edge Detection

The Laplacian edge detection algorithm is another popular technique for detecting edges in OpenCV Python.

It calculates the second-order derivative of the image intensity, which highlights the regions of significant intensity variations. The Laplacian operator can be implemented using the following formula:

`L = d^2I/dx^2 + d^2I/dy^2`

The Laplacian operator is implemented in OpenCV Python using the `cv2.Laplacian()`

function. The function takes two arguments, the input image and the data type of the output image. The output of the function is a gradient image that represents the edges in the image.

Here’s an example of Laplacian edge detection in OpenCV Python:

```
import cv2
import numpy as np
# Load the image
img = cv2.imread('image.jpg')
# Convert the image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply Gaussian smoothing
blur = cv2.GaussianBlur(gray, (3, 3), 0)
# Calculate the Laplacian gradient
laplacian = cv2.Laplacian(blur, cv2.CV_64F)
# Threshold the gradient to obtain the edges
edges = cv2.threshold(laplacian, 50, 255, cv2.THRESH_BINARY)[1]
# Display the result
cv2.imshow('Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
```

### Prewitt Edge Detection

The Prewitt edge detection algorithm is another popular technique for detecting edges in OpenCV Python.

It calculates the first-order derivative of the image intensity in the x and y directions. The Prewitt operator can be implemented using the following formulas:

```
Px = [-1 0 1; -1 0 1; -1 0 1] * A
Py = [-1 -1 -1; 0 0 0; 1 1 1] * A
G = sqrt(Px^2 + Py^2)
```

The Prewitt operator is implemented in OpenCV Python using the `cv2.filter2D`

function. The function takes three arguments, the input image, the data type of the output image, and the kernel used for the convolution operation. The output of the function is a gradient image that represents the edges in the image.

Here’s an example of Prewitt edge detection in OpenCV Python:

```
import cv2
import numpy as np
# Load the image
img = cv2.imread('image.jpg')
# Convert the image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply Gaussian smoothing
blur = cv2.GaussianBlur(gray, (3, 3), 0)
# Define the Prewitt kernel for the x and y directions
prewittx = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]])
prewitty = np.array([[-1, -1, -1], [0, 0, 0], [1, 1, 1]])
# Calculate the Prewitt gradient in the x and y directions
sobelx = cv2.filter2D(blur, -1, prewittx)
sobely = cv2.filter2D(blur, -1, prewitty)
# Compute the gradient magnitude and direction
mag, angle = cv2.cartToPolar(sobelx, sobely, angleInDegrees=True)
# Threshold the magnitude to obtain the edges
edges = cv2.threshold(mag, 50, 255, cv2.THRESH_BINARY)[1]
# Display the result
cv2.imshow('Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
```

## References

- Canny Edge Detection, OpenCV documentation. https://docs.opencv.org/master/da/d22/tutorial_py_canny.html
- Sobel Operator, Wikipedia. https://en.wikipedia.org/wiki/Sobel_operator
- Gaussian Blur, Wikipedia. https://en.wikipedia.org/wiki/Gaussian_blur
- Non-maximum Suppression, Wikipedia. https://en.wikipedia.org/wiki/Non-maximum_suppression
- Hysteresis Thresholding, Wikipedia. https://en.wikipedia.org/wiki/Hysteresis_thresholding