一、图像的梯度处理
1、Sobel算子
梯度可以按照x方向或者y方向求梯度,其实就是在看像素点的差异变化情况,比如黑白物体的交界,其像素值变化差异是非常大的。
求梯度计算使用的函数就叫做Sobel算子,可以分为水平梯度与竖直梯度。
简单点说,Sobel算子是一种特殊的卷积核,可以用于图像的边缘检测。自定义一个水平及竖直方向的sobel算子:
# 水平sobel算子 Sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]) # 竖直方向的sobel算子 Sobel_y = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])
可以看到使用sobel算子进行卷积计算的时候,就是将中间像素点的一圈像素进行相应的加减操作。
如果是位于同一颜色内的像素点进行卷积操作,那么运算结果就会等于0,因为左右值都是相同的。
opencv里的函数:
dst = cv.Sobel(src, ddepth, dx, dy, ksize)
ddepth:表示传入图像的深度(颜色通道个数)
src:传入图像
dx和dy分别表示水平方向和竖直方向
ksize:表示sobel算子的大小
cv.CV_64F:表示将运算结果使用更大的存储方式存储,因为可能出现负数
代码实现sobel算子:
def f_sobel(): img = cv.imread("car_red.jpg", cv.IMREAD_GRAYSCALE) # 水平sobel算子 Sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]) # 竖直方向的sobel算子 Sobel_y = np.array([[-1, 0, -2], [0, 0, 0], [1, 0, 2]]) dst = cv.Sobel(img, cv.CV_64F, dx=1, dy=0, ksize=3) cv.imshow("zero", dst) cv.waitKey(0) cv.destroyAllWindows()
运行结果如图:
结果并不理想,这是由于右值减去左值的结果并不是一直为正,有的时候就负数,opencv会自动把负数进行截断取0操作,因此这个时候就要把负数改变为正数。
def f_sobel(): img = cv.imread("car_red.jpg", cv.IMREAD_GRAYSCALE) # 水平sobel算子 Sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]) # 竖直方向的sobel算子 Sobel_y = np.array([[-1, 0, -2], [0, 0, 0], [1, 0, 2]]) dst = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=3) # 这条代码将正负取绝对值 dst = cv.convertScaleAbs(dst) cv.imshow("zero", dst) cv.waitKey(0) cv.destroyAllWindows()
边界比较清晰:
同时,使用水平或者竖直方向得到的结果也是有差异的。所以可以对水平方向和竖直方向都进行Sobel算子计算,然后将得到的两幅梯度图再进行相加,就可以得到完整的轮廓图像。
代码:
def f_sobel(): img = cv.imread("car_red.jpg", cv.IMREAD_GRAYSCALE) # 水平sobel算子 Sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]) # 竖直方向的sobel算子 Sobel_y = np.array([[-1, 0, -2], [0, 0, 0], [1, 0, 2]]) dst_x = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=3) dst_y = cv.Sobel(img, cv.CV_64F, 0, 1, ksize=3) dst_y = cv.convertScaleAbs(dst_y) dst_x = cv.convertScaleAbs(dst_x) dst = cv.addWeighted(dst_x, 0.5, dst_y, 0.5, 0) cv.imshow("zero", dst) cv.waitKey(0) cv.destroyAllWindows()
结果:
Scharr算子:与Sobel算子相比,数值更大,对结果的差异更加敏感。
laplacian算子:对噪音点更敏感
# 水平Scharr算子 Scharr = np.array([[-3, 0, 3], [-10, 0, 10], [-3, 0, 3]]) # laplacian算子 laplacian = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]])
函数体:
# 参数与Sobel算子类似 cv2.Scharr(src, dx, dy....) # 拉普拉斯算子不区分x, y方向。因此没有dx, dy参数 cv2.Laplacian(src...)