The main content of this article comes from the image processing part of OpenCV in the OpenCV-Python tutorial . The main content of this part is as follows:
-
Learn to change images between different color spaces. Also learn to track colorful objects in videos.
-
Geometric transformation of images
Learn to apply different geometric transformations to images, such as rotation, translation, etc.
-
Learn to convert images into binary images using global thresholding, adaptive thresholding, Otsu's binarization, and more.
-
Learn to blur images, filter images with custom kernels, and more.
-
Understand morphological transformations such as erosion, expansion, opening, closing, etc.
-
Learn to find image gradients, edges, and more.
-
Learn to find edges with Canny edge detection.
-
Learn about image pyramids and how to use them for image blending.
-
All about contours in OpenCV.
-
All about histograms in OpenCV.
-
Image transformation in OpenCV
Different image transformations are encountered in OpenCV, such as Fourier transform, cosine transform, etc.
-
Learn to use template matching to search for objects in images.
-
Learn to detect lines in an image.
-
Learn to detect circles in an image.
-
Image segmentation using watershed algorithm
Learn to segment images using the watershed segmentation algorithm.
-
Interactive foreground extraction using GrabCut algorithm
Learn to use GrabCut algorithm to extract foreground
Target
In this chapter we will learn:
- Find gradients, edges, etc. of an image.
- We will look at the following functions: cv.Sobel() , cv.Scharr() , cv.Laplacian() , etc.
theory
OpenCV provides three types of gradient filters or high-pass filters, Sobel, Scharr and Laplacian. We'll look at each of them.
1. Sobel and Scharr derivatives
The Sobel operator is a combined Gaussian smoothing plus differential operation, so it has stronger noise resistance. We can specify the direction of the derivative to be taken, vertical or horizontal (via the parameters yorder
and respectively xorder
). You can also ksize
specify the kernel size via parameters. If ksize = -1
, then a 3x3 Scharr filter will be used, which gives better results than a 3x3 Sobel filter. Please refer to the documentation for the kernel you are using.
2. Laplacian derivative
It is calculated by the relation Δ src = ∂ 2 src ∂ x 2 + ∂ 2 src ∂ y 2 \Delta src = \frac{\partial ^2{src}}{\partial x^2} + \frac{\partial ^2 {src}}{\partial y^2}Δsrc=∂x2∂2src+∂y2∂2srcThe Laplacian of the given image, where each derivative is found using the Sobel derivative. If ksize = 1
, the following kernel will be used for filtering
kernel = [ 0 1 0 1 − 4 1 0 1 0 ] kernel = \begin{bmatrix} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{bmatrix}kernel=⎣⎡0101−41010⎦⎤
code
The code below shows all these operations in one picture. All cores are 5x5 in size. Pass -1 for the depth of the output image to get np.uint8
results of type .
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
def image_gradients():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('sudoku.png'), 0)
print(img.shape)
img = cv.resize(img, (359, 361))
laplacian = cv.Laplacian(img, cv.CV_64F)
sobelx = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=5)
sobely = cv.Sobel(img, cv.CV_64F, 0, 1, ksize=5)
cv.imshow("Original", img)
cv.imshow("Laplacian", laplacian)
cv.imshow("Sobel X", sobelx)
cv.imshow("Sobel Y", sobely)
cv.waitKey(-1)
cv.destroyAllWindows()
if __name__ == "__main__":
image_gradients()
The results are as follows:
[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-qZrKoTJl-1651362541111)(https://upload-images.jianshu.io/upload_images/1315506- 5e604eab748f2c88.png)]
an important question
In our previous example, the output data type was cv.CV_8U
or np.uint8
. But there's a small problem. A black to white transition is considered a positive slope (it has a positive value), while a white to black transition is considered a negative slope (it has a negative value). So when you convert the data to np.uint8
, all negative slopes are set to 0. Simply put, the edge is lost.
If we want to detect two edges, a better option is to keep the output data type to some higher form, like cv.CV_16S
, cv.CV_64F
etc., take its absolute value and then convert it back cv.CV_8U
. The code below demonstrates this process and the difference in results for a horizontal Sobel filter.
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
def image_gradients2():
img = np.zeros((600, 600))
cv.rectangle(img, (150, 100), (450, 500), (255), -1)
# Output dtype = cv.CV_8U
sobelx8u = cv.Sobel(img, cv.CV_8U, 1, 0, ksize=5)
# Output dtype = cv.CV_64F. Then take its absolute and convert to cv.CV_8U
sobelx64f = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)
plt.subplot(1, 3, 1), plt.imshow(img, cmap='gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 2), plt.imshow(sobelx8u, cmap='gray')
plt.title('Sobel CV_8U'), plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 3), plt.imshow(sobel_8u, cmap='gray')
plt.title('Sobel abs(CV_64F)'), plt.xticks([]), plt.yticks([])
plt.show()
if __name__ == "__main__":
image_gradients2()
The inspection results are as follows:
Other resources
practise
Reference documentation
Done.