In the previous chapter, we learned about kernels and low-pass filters and their applications. We learned about and demonstrated how to use low-pass filters in blurring, smoothing, and de-noising images.
In this chapter, we will learn about and demonstrate the uses of high-pass filters. This includes their application in image processing and computer vision. First, we will explore the Laplacian, Scharr, and Sobel high-pass filters. Then, we will learn about the Canny edge detection algorithm. We will also demonstrate Hough transforms for circles and lines. We will conclude by looking at corner detection with the Harris algorithm.
The following is a list of the topics we will cover in this chapter:
After following this chapter, you will be able to use high-pass filters to detect the features in input images, such as edges, corners, lines, and circles.
The code files of this chapter can be found on GitHub at https://github.com/PacktPublishing/raspberry-pi-computer-vision-programming/tree/master/Chapter08/programs.
Check out the following video to see the Code in Action at https://bit.ly/2CFnpnD.
The concept of high-pass filters is exactly the opposite of low-pass filters. High-pass filters allow high-frequency components of information (such as signals and images) to pass through them. That is why they are known as high-pass filters. In an image, edges are high-frequency components. The kernels we use in high-pass filters boost the intense components in an image. That is why when we apply high-pass filters to images, we get the edges in the output.
Note:
You can read more about high-pass filters at https://diffractionlimited.com/help/maximdl/High-Pass_Filtering.htm. Another type of signal filter is band-pass filters, which allow signals in a range (or band) of frequencies to pass through them. These filters allow us to highlight the edges in images and reduce the noise by using blurring at the same time. You can read more about them at https://homepages.inf.ed.ac.uk/rbf/HIPR2/freqfilt.htm.
OpenCV has a lot of library functions that implement high-pass filters. We will look at how to use the Laplacian(), Sobel(), and Scharr() functions.
Note:
You can learn about the mathematical aspects of high-pass filtering in more detail by referring to the following web pages:
https://www.tutorialspoint.com/dip/Sobel_operator.htm
https://www.tutorialspoint.com/dip/Laplacian_Operator.htm
The following is a list of parameters commonly used by all of the high-pass filtering functions and their meanings:
Let's write some code to demonstrate the functionality of the Sobel(), Laplacian(), and Scarr() functions. In the following code, we are computing the Laplacian and the first-order derivative of X of the input image using the Scarr() and Sobel() functions:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('/home/pi/book/dataset/4.1.05.tiff', 0)
laplacian = cv2.Laplacian(img, ddepth=cv2.CV_32F, ksize=17,
scale=1, delta=0,
borderType=cv2.BORDER_DEFAULT)
sobel = cv2.Sobel(img, ddepth=cv2.CV_32F, dx=1, dy=0,
ksize=11, scale=1, delta=0,
borderType=cv2.BORDER_DEFAULT)
scharr = cv2.Scharr(img, ddepth=cv2.CV_32F, dx=1, dy=0,
scale=1, delta=0,
borderType=cv2.BORDER_DEFAULT)
images=[img, laplacian, sobel, scharr]
titles=['Original', 'Laplacian', 'Sobel', 'Scharr']
for i in range(4):
plt.subplot(2, 2, i+1)s
plt.imshow(images[i], cmap = 'gray')
plt.title(titles[i])
plt.axis('off')
plt.show()
The computation of the derivative of X of the image with the Laplacian(), Scharr(), and Sobel() functions returns the vertical edges in the input image. The following screenshot shows the output of the preceding code:
We can connect two push buttons to the 7 and 11 GPIO pins in pull-up configuration and program them to adjust the values of dx and dy. The following is the code to do this:
import RPi.GPIO as GPIO
import cv2
x = 0
y = 1
cap = cv2.VideoCapture(0)
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
button1 = 7
button2 = 11
GPIO.setup(button1, GPIO.IN, GPIO.PUD_UP)
GPIO.setup(button2, GPIO.IN, GPIO.PUD_UP)
while True:
print(x, y)
ret, frame = cap.read()
button1_state = GPIO.input(button1)
if button1_state == GPIO.LOW:
x = 0
y = 1
button2_state = GPIO.input(button2)
if button2_state == GPIO.LOW:
x = 1
y = 0
Now, let's compute the output image with the cv2.Scharr() function:
output = cv2.Scharr(frame, ddepth=cv2.CV_32F,
dx=x, dy=y,
scale=1, delta=0,
borderType=cv2.BORDER_DEFAULT)
cv2.imshow('Salt and pepper Noise App', output)
if cv2.waitKey(1) == 27:
break
cap.release()
cv2.destroyAllWindows()
Run the preceding program and observe the edge detection on the live video feed from the USB webcam connected to the Raspberry Pi board. We can also add the X derivative to the Y derivative (computed with Scharr) of the same live video feed, as follows:
import cv2
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
output1 = cv2.Scharr(frame, ddepth=cv2.CV_32F,
dx=0, dy=1,
scale=1, delta=0,
borderType=cv2.BORDER_DEFAULT)
The previous code segment computes the Scharr derivative of the Y axis. Now, let's write the code for the Scharr derivative of the X axis, as follows:
output2 = cv2.Scharr(frame, ddepth=cv2.CV_32F,
dx=1, dy=0,
scale=1, delta=0,
borderType=cv2.BORDER_DEFAULT)
cv2.imshow('Addition of Vertical and Horizontal',
cv2.add(output1, output2))
if cv2.waitKey(1) == 27:
break
cap.release()
cv2.destroyAllWindows()
Run the preceding program and observe the added X and Y Scharr derivatives. You can implement similar programs with Sobel derivatives. All of these filters are used for detecting edges in the image.
In the next section, we will see how to use high-pass filters to detect edges in an image with the Canny edge detection algorithm.
The Canny edge detection algorithm was developed by John Canny. Canny's algorithm heavily uses the concept of high-pass filters. It has multiple steps.
Note:
You can read more about the Canny edge detection algorithm at http://homepages.inf.ed.ac.uk/rbf/HIPR2/canny.htm.
OpenCV has the cv2.Canny() function, which offers Canny's algorithm. The following are the steps of the algorithm:
Note:
You can read more about the L1 and L2 norms and non-maximum suppression at http://www.chioka.in/differences-between-the-l1-norm-and-the-l2-norm-least-absolute-deviations-and-least-squares/ and https://towardsdatascience.com/non-maximum-suppression-nms-93ce178e177c.
The following is a list of parameters for the cv2.Canny() function:
This function computes and returns the set of detected edges in the source input image. The following code demonstrates this concept well:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('/home/pi/book/dataset/4.1.05.tiff', 0)
edges1 = cv2.Canny(img, 50, 300, L2gradient=False)
edges2 = cv2.Canny(img, 100, 150, L2gradient=True)
images = [img, edges1, edges2]
titles = ['Original', 'L1 Gradient', 'L2 Gradient']
for i in range(3):
plt.subplot(1, 3, i+1)
plt.imshow(images[i], cmap = 'gray')
plt.title(titles[i])
plt.axis('off')
plt.show()
The output of the preceding code is as follows:
We can make the preceding program more interesting by computing the edges in real time, such that the thresholds are adjustable by OpenCV's trackbars:
import cv2
cv2.namedWindow('Canny')
img = cv2.imread('/home/pi/book/dataset/4.1.05.tiff', 0)
def empty(z):
pass
cv2.createTrackbar('Threshold 1', 'Canny', 50, 100, empty)
cv2.createTrackbar('Threshold 2', 'Canny', 150, 300, empty)
while(True):
l1 = cv2.getTrackbarPos('Threshold 1', 'Canny')
l2 = cv2.getTrackbarPos('Threshold 2', 'Canny')
output = cv2.Canny(img, l1, l2, L2gradient=False)
cv2.imshow('Canny', output)
if cv2.waitKey(1) == 27:
break
cv2.destroyAllWindows()
In the previous code, we created two trackbars for the upper and lower thresholds of the Canny algorithm. We used the L1 norm to compute the edges. The output will be as follows:
We can apply this algorithm on real-life images, such as a live video feed from our webcam. In the next section, we will learn how to detect circles and lines with the Hough transform.
OpenCV offers a cv2.HoughCircles() function to detect circles in an image with Hough's method. This returns the centers and radii of the detected circles. It accepts an image, the (cv2.HOUGH_GRADIENT) method of detection, the inverse ratio of the resolution, the minimum distance between the centers of the circles to be detected, the highest threshold of the Canny method used internally, the threshold for the accumulator, and the maximum and minimum distances of the circles to be detected.
Note:
You can find more details about the mathematical aspects of Hough transforms for circles at https://www.cis.rit.edu/class/simg782/lectures/lecture_10/lec782_05_10.pdf.
In the following code, we accept the live video feed from a USB webcam as input. Then, we remove the noise by blurring the input frame, and then we pass the blurred frame to the call of the cv2.HoughCircles() function. Then, we visualize the detected circles with the cv2.Circle() function, as follows:
import cv2
cap = cv2.VideoCapture(0)
while (True):
ret , frame = cap.read()
grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
blur = cv2.blur(grey, (5, 5))
circles = cv2.HoughCircles(blur,
method=cv2.HOUGH_GRADIENT,
dp=1, minDist=200,
param1=50, param2=13,
minRadius=30, maxRadius=175)
if circles is not None:
for i in circles [0,:]:
cv2.circle(frame, (i[0], i[1]), i[2], (0, 255, 0), 2)
cv2.circle(frame, (i[0], i[1]), 2, (0, 0, 255), 3)
cv2.imshow('Detected', frame)
if cv2.waitKey(1) == 27:
break
cv2.destroyAllWindows()
cap.release()
Run the preceding program and observe the output. It should look as follows:
The OpenCV cv2.HoughLines() function detects lines in an image. It accepts a grayscale image, the value of rho (the distance accuracy of an accumulator), theta (the angle accuracy of the accumulator), and the parameter of the threshold for the accumulator as arguments. We will demonstrate this with a live USB webcam video feed. The returned output is in polar format, which must be converted into the X/Y coordinate system before visualization:
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
while True:
ret, img = cap.read()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 250, apertureSize=5,
L2gradient=True)
lines = cv2.HoughLines(edges, 1, np.pi/180, 200)
if lines is not None:
for rho,theta in lines[0]:
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
pts1 = (int(x0 + 1000*(-b)), int(y0 + 1000*(a)))
pts2 = (int(x0 - 1000*(-b)), int(y0 - 1000*(a)))
cv2.line(img, pts1, pts2, (0, 0, 255), 2)
cv2.imshow('Detected Lines', img)
if cv2.waitKey(1) == 27:
break
cv2.destroyAllWindows()
cap.release()
Run the preceding code and observe its output. The output is as follows:
The Hough transforms must be finely adjusted for the given input. This means that if we cannot see any lines or circles in the correct places, then we can try adjusting the value of the arguments passed to these Hough transform functions. Sometimes, it could produce false results, as in lines and circles will be visible even when there are none in the input frame. Again, for correct results, we must adjust the value of the arguments passed to these functions.
OpenCV has the cv2.cornerHarris() function for detecting corners. Its arguments are as follows:
The following is an example program that implements Harris corner detection:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('/home/pi/book/dataset/4.1.05.tiff', 0)
img = np.float32(img)
dst = cv2.cornerHarris(img, 2, 3, 0.04)
ret, dst = cv2.threshold(dst, 0.01*dst.max(), 255, 0)
dst = np.uint8(dst)
plt.imshow(dst, cmap='gray')
plt.axis('off')
plt.show()
In the preceding program, we coverted the image into 32-bit float format and then we fed it to the corner detection function. Then, we threshholded the image. We used 0.01*dst.max() as the value of the threshold to compute the binary image. Then, we converted the output into 8-bit integer format so that the output image could be displayed with matplotlib, as follows:
We can use this corner detection method in industrial and robotics applications to detect the corners of regular and predictable objects. It is very useful in real-world automation.
To practice what you have learned in this chapter, explore the HoughLinesP(), goodFeaturesToTrack(), and FastFeatureDetector() functions in OpenCV for detecting various features. Write programs using these functions to detect lines using probabilistic Hough transforms and other features.
In this chapter, we learned the concept and demonstration of high-pass filters. We applied high-pass filters on images to obtain various results. We also demonstrated the various techniques for detecting features, such as corners, lines, edges, and circles. All of these feature-detection algorithms rely on high-pass filtering. Canny's algorithm for edge detection uses Gaussian high-pass filters. The Harris corner detection algorithm uses Sobel spatial derivatives. All of these geometric feature-detection algorithms are routinely employed in real life in industrial automation, smart vehicles, and robotics.
In the next chapter of this book, we will learn the concepts and demonstrate the restoration of degraded images; the segmentation of images; k-means clustering of one-, two-, and multi-dimensional data; image quantization using k-means clustering; and the estimation of a depth map in detail.
3.144.252.201