This is the 29th day of my participation in the August Wenwen Challenge.More challenges in August
Python OpenCV 365 day learning plan, enter the graphics realm with eraser.
Foundation of basic knowledge
Image gradient is a method to calculate the speed of image change. For the edge part of the image, if the gray value changes greatly, the corresponding gradient value is also large; conversely, for the smooth part of the image, the gray value changes little, and the corresponding gradient value changes little.
With the above content, you can learn image gradient correlation calculation, which will be used to obtain image edge information related technology.
OpenCV offers three different gradient filters, or high-pass filters: Sobel, Scharr, and Laplacian.
In mathematics, Sobel, Scharr is the first or second derivative, Scharr is the optimization of Sobel (when solving gradient angles using small convolution kernels), and Laplacian is the second derivative.
The mathematics part we still do not unfold, after the first general studies, from the perspective of God to supplement.
Sobel operator and Scharr operator
Sobel operator description and use
Sobel operator is a combination of Gaussian smoothing and differential operation, so its anti-noise ability is very good.
In the process of use, you can set the direction of derivation (xorder or yorder), and also set the size of the convolution kernel (ksize).
The function prototype is as follows:
dst = cv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
Copy the code
The parameters are described as follows:
- src: Input image;
- ddepth: Image color depth. For different input images, output images have different depth.
- 1
The image depth is consistent; - dx: Derivative order in x direction;
- dy: Derivative order in y direction;
- ksize: kernel size, usually an odd number, 1,3,5,7;
- scale: Zoom size, default is 1;
- delta: Incremental value, default is 0;
- borderType: Boundary type, default value isBORDER_DEFAULT.
Note: If ksize=-1, a 3×3 Scharr filter will be used, which is better than a 3×3 Sobel filter (and has the same speed, so Scharr filter should be used as much as possible when using 3×3 filters).
The test code for this function is as follows (note that you select your own image and place it in the same folder) :
import cv2
import numpy as np
img = cv2.imread('./test.jpg')
dst = cv2.Sobel(img, cv2.CV_64F, 1.0, ksize=3)
dst = cv2.convertScaleAbs(dst)
cv2.imshow("dst", dst)
cv2.waitKey()
cv2.destroyAllWindows()
Copy the code
There is a lot to explain and learn from the above code, so let’s step through it.
The second parameter in the Sobel filter, set to cv2.cv_64f, searched the Internet for a common explanation
We generally deal with 8-bit images, and when the calculated gradient is less than zero, it will automatically change to 0, resulting in the loss of boundary information. The reason why the gradient is less than zero is that the boundary derivative from black to white is an integer, while the boundary derivative from white to black is negative. When the image depth is Np.uint8, the negative value will become 0. Based on this reason, it is necessary to set the data type of output image higher, such as Cv2.cv_16s, cv2.cv_64f equivalent.
And the easy way to remember it, of course, is to set it to -1.
After the processing is completed, it is converted back to the original uint8 format by the cv2.convertScaleAbs function. If it is not converted, the image obtained is not correct.
In contrast, conversion and no conversion twice run effect, the right side of the apparent loss of boundary values.
Note that there is one more detail that needs to be explained in the code above
dst = cv2.Sobel(img, cv2.CV_64F, 1.0, ksize=3)
Copy the code
- dx: Derivative order in x direction;
- dy: The derivative order in the y direction.
So dx is equal to 1,dy is equal to 0, which means you compute the horizontal direction, not the vertical direction, which direction is equal to 1, which direction you compute.
You can also compare different values of the ddepth parameter in the function to check the difference content. The following code uses the image fusion function Cv.addWeighted
import numpy as np
import cv2 as cv
def sobel_demo1(image) :
grad_x = cv.Sobel(image, cv.CV_16S, 1.0)
grad_y = cv.Sobel(image, cv.CV_16S, 0.1)
gradx = cv.convertScaleAbs(grad_x)
grady = cv.convertScaleAbs(grad_y)
cv.imshow('sobel_demo1_gradient_x', gradx)
cv.imshow('sobel_demo1_gradient_y', grady)
Merge the x and y gradients
add_image = cv.addWeighted(gradx, 0.5, grady, 0.5.0)
cv.imshow('sobel_demo1_addWeighted', add_image)
def sobel_demo2(image) :
grad_x = cv.Sobel(image, cv.CV_32F, 1.0)
grad_y = cv.Sobel(image, cv.CV_32F, 0.1)
gradx = cv.convertScaleAbs(grad_x)
grady = cv.convertScaleAbs(grad_y)
cv.imshow('sobel_demo2_gradient_x', gradx)
cv.imshow('sobel_demo2_gradient_y', grady)
Merge the x and y gradients
add_image = cv.addWeighted(gradx, 0.5, grady, 0.5.0)
cv.imshow('sobel_demo2_addWeighted', add_image)
src = cv.imread('./test.jpg'.1)
sobel_demo1(src)
sobel_demo2(src)
cv.waitKey(0)
cv.destroyAllWindows()
Copy the code
You can calculate the x and y derivatives directly using the Sobel function without using the image fusion function as follows:
def sobel_demo3(image) :
grad = cv.Sobel(image, cv.CV_16S, 1.1)
grad = cv.convertScaleAbs(grad)
cv.imshow('sobel_demo1_gradient', grad)
Copy the code
The result is much worse than the image fusion effect.
This comparison is not easy to see clearly, can be combined with the following code for transformation, the picture together to display.
def sobel_demo1(image) :
grad_x = cv.Sobel(image, cv.CV_16S, 1.0)
grad_y = cv.Sobel(image, cv.CV_16S, 0.1)
gradx = cv.convertScaleAbs(grad_x)
grady = cv.convertScaleAbs(grad_y)
Merge the x and y gradients
dst = cv.addWeighted(gradx, 0.5, grady, 0.5.0)
imgs1 = np.hstack([cv.cvtColor(image, cv.COLOR_BGR2RGB), gradx])
imgs2 = np.hstack([grady, dst])
img_all = np.vstack([imgs1, imgs2])
plt.figure(figsize=(20.10))
plt.imshow(img_all, cmap=plt.cm.gray)
plt.show()
Copy the code
The above code works as follows and is easy to compare.
Sobel operator algorithm has the advantages of simple calculation and fast speed.
However, because only the two-direction template is used, only the horizontal and vertical direction edges can be detected, so the edge detection effect of this algorithm is not very ideal for images with complex texture. The algorithm believes that all pixels with new gray value greater than or equal to the threshold are edge points. This judgment is not very reasonable, will cause the edge of the misjudgment, because many noise points gray value is also very large.
Description and use of Scharr operator
In the Sobel operator algorithm function, if ksize=-1, a 3×3 Scharr filter will be used.
It’s the same principle as the Sobel operator, but the convolution kernel is different, so it’s a little bit more precise.
The prototype of this function is as follows:
# Sobel operator algorithm
dst = cv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
# Scharr operator algorithm
dst = cv2.Scharr(src, ddepth, dx, dy[, dst[, scale[, delta[, borderType]]]])
Copy the code
Its parameters are basically the same as Sobel’s.
The test code is as follows to compare the differences between the two algorithms:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
def scharr_demo(image) :
grad_x = cv.Scharr(image, cv.CV_16S, 1.0)
grad_y = cv.Scharr(image, cv.CV_16S, 0.1)
gradx = cv.convertScaleAbs(grad_x)
grady = cv.convertScaleAbs(grad_y)
Merge the x and y gradients
dst = cv.addWeighted(gradx, 0.5, grady, 0.5.0)
return dst
def sobel_demo1(image) :
dst1 = scharr_demo(image)
grad_x = cv.Sobel(image, cv.CV_16S, 1.0)
grad_y = cv.Sobel(image, cv.CV_16S, 0.1)
gradx = cv.convertScaleAbs(grad_x)
grady = cv.convertScaleAbs(grad_y)
Merge the x and y gradients
dst = cv.addWeighted(gradx, 0.5, grady, 0.5.0)
imgs = np.hstack([dst, dst1])
plt.figure(figsize=(20.10))
plt.imshow(imgs, cmap=plt.cm.gray)
plt.show()
def sobel_demo3(image) :
grad = cv.Sobel(image, cv.CV_16S, 1.1)
grad = cv.convertScaleAbs(grad)
cv.imshow('sobel_demo1_gradient', grad)
def sobel_demo2(image) :
grad_x = cv.Sobel(image, cv.CV_32F, 1.0)
grad_y = cv.Sobel(image, cv.CV_32F, 0.1)
gradx = cv.convertScaleAbs(grad_x)
grady = cv.convertScaleAbs(grad_y)
cv.imshow('sobel_demo2_gradient_x', gradx)
cv.imshow('sobel_demo2_gradient_y', grady)
Merge the x and y gradients
add_image = cv.addWeighted(gradx, 0.5, grady, 0.5.0)
cv.imshow('sobel_demo2_addWeighted', add_image)
src = cv.imread('./test.jpg'.1)
sobel_demo1(src)
# sobel_demo2(src)
# sobel_demo3(src)
cv.waitKey(0)
cv.destroyAllWindows()
Copy the code
sobelOperator andscharrOperator differences
- sobelOperator coefficient: [1 2 1];scharrOperator [3 10 3];
- scharrOperator thansobelOperators have higher accuracy;
- scharrThe operator can detect the smaller boundary.
Laplacian operator
The method for realizing Laplace function is: Firstly, Sobel operator is used to calculate the second order x and y derivatives, and then sum them.
At the application level, let’s first look at the prototype of this function:
dst = cv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])
Copy the code
No special parameters, can directly enter the test code learning.
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
image = cv.imread('./test1.png',cv.IMREAD_GRAYSCALE)
laplacian = cv.Laplacian(image, cv.CV_64F)
laplacian = cv.convertScaleAbs(laplacian)
imgs = np.hstack([image, laplacian])
plt.figure(figsize=(20.10))
plt.imshow(imgs, cmap=plt.cm.gray)
plt.show()
Copy the code
I pre-converted the image to grayscale for testing convenience.
Laplace convolution kernel, you can define it yourself. Is used by default domain neighbourhood [[0, 1, 0], [1, 4, 1], [0, 1, 0]], modified to eight neighborhood [[1, 1, 1], [1, 8, 1], [1, 1, 1]].
The code of the custom convolution kernel is as follows, where the 2D convolution operation we learned before is used:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
image = cv.imread('./test1.png',cv.IMREAD_GRAYSCALE)
Define the convolution sum
kernel = np.array([[1.1.1], [1, -8.1], [1.1.1]])
dst = cv.filter2D(image, cv.CV_32F, kernel=kernel)
lapalian = cv.convertScaleAbs(dst)
imgs = np.hstack([image, lapalian])
plt.figure(figsize=(20.10))
plt.imshow(imgs, cmap=plt.cm.gray)
plt.show()
Copy the code
Laplace is sensitive to noise, produces a bilateral effect, but does not detect the direction of the edge. And it is not directly used for edge detection, but only plays an auxiliary role, detecting whether a pixel is on the bright side or the dark side of the edge, using zero crossing to determine the position of the edge
Gradient is simply derivative, that’s the key derivative, to learn math, this part after the first time, we will learn. Gradient is shown on the image is to extract the edge of the image (whether horizontal, vertical, oblique, etc.), all that is needed is a core template, different template results are different. Based on this, the operator functions mentioned above can be represented by the function cv2.filter2d (). Different methods give different kernel templates and then evolve into different operators.
Eraser section
I hope you found something useful in today’s hour. See you in our next blog
reading
If you have ideas or techniques you’d like to share, feel free to leave them in the comments section.
Blogger ID: Dream eraser, hope you like, comment, favorites.