1. Sobel 算子:通过计算局部差分寻找边缘,计算得到一个梯度的近似值。
滤波器(核):由一幅图像根据像素点(x,y)临近的区域计算得到另外一张图像的算法。滤波的目标像素的值等于原始像素及其周围像素的加权和,这种基于线性核的滤波,就是卷积。
1. 计算水平方向偏导数的近似值
计算公式为:P5的水平方向偏导数 = (P3-P1) + 2(P6-P4) + (P9-P7) 为了避免计算出负数造成信息丢失,需要时取绝对值。
计算过程为:最左边的像素值 - 最右边的像素值,但是中间一行的权重是最高的,乘以2。
2. 计算垂直方向的偏导数的近似值:
与上面唯一不同的是:1. 最下面的像素值 - 最上面的像素值,中间一行的权重是最高的,乘以2。
3. 算子介绍: dst=cv2.Sobel(src,outputImgDepth,dx,dy)
示例代码:X方向Sobel处理
import cv2
o = cv2.imread('sobel4.bmp',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(o,-1,1,0) # 1,0表示X方向,反之0,1表示Y方向
cv2.imshow("original",o)
cv2.imshow("x",sobelx)
cv2.waitKey()
cv2.destroyAllWindows()
运行:
上面的结果可以看到,只能计算出255到0过渡的边缘,但是如果需要得到两条边缘,就需要取绝对值,将-255转化为255
需要用到convertScaleAbs函数转成绝对值。
import cv2
o = cv2.imread('sobel4.bmp',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(o,cv2.CV_64F,1,0)
sobelx = cv2.convertScaleAbs(sobelx) # 转回uint8
cv2.imshow("original",o)
cv2.imshow("x",sobelx)
cv2.waitKey()
cv2.destroyAllWindows()
运行:
上面只是X方向的,Y方向一样,只要将Sobel函数中的xy调换顺序即可。但是如果需要得到xy两个方向的叠加边缘,就需要使用addWeighted函数实现叠加。
示例代码:
import cv2
o = cv2.imread('sobel4.bmp',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(o,cv2.CV_64F,1,0) # 先找x方向边缘
sobely = cv2.Sobel(o,cv2.CV_64F,0,1) # 再找y方向边缘
sobelx = cv2.convertScaleAbs(sobelx) # 转回uint8
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0) #叠加边缘(src1,src1Weighted,src2,src2Weighted,0)
cv2.imshow("original",o)
cv2.imshow("xy",sobelxy)
cv2.waitKey()
cv2.destroyAllWindows()
运行:
图像示例:
import cv2
a=cv2.imread("./gril.png",cv2.IMREAD_GRAYSCALE)
sobel=cv2.Sobel(a,-1,1,0)
sobelx=cv2.Sobel(a,cv2.CV_64F,1,0)
sobely=cv2.Sobel(a,cv2.CV_64F,0,1)
abs_sobelx=cv2.convertScaleAbs(sobelx)
abs_sobely=cv2.convertScaleAbs(sobely)
abs_sobelxy=cv2.addWeighted(abs_sobelx,1,abs_sobely,1,0) # 1= weighted
cv2.imshow("src",a)
cv2.imshow("abs_sobelx",abs_sobelx)
cv2.imshow("abs_sobely",abs_sobely)
cv2.imshow("both xy",abs_sobelxy)
cv2.waitKey()
cv2.destroyAllWindows()
运行:
2. Scharr算子:
用法和Sobel基本一致,唯一的区别就是卷积核不同,Scharr的细节更加丰富,精度更高。另外Scharr算子xy不能同时为1,否则会报异常。
下面示例将Sobel核Scharr算子做了比较:
import cv2
o = cv2.imread('lena.bmp',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(o,cv2.CV_64F,1,0,ksize=3)
sobely = cv2.Sobel(o,cv2.CV_64F,0,1,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx) # 转回uint8
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
scharrx = cv2.Scharr(o,cv2.CV_64F,1,0)
scharry = cv2.Scharr(o,cv2.CV_64F,0,1)
scharrx = cv2.convertScaleAbs(scharrx) # 转回uint8
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx,0.5,scharry,0.5,0)
cv2.imshow("original",o)
cv2.imshow("sobelxy",sobelxy)
cv2.imshow("scharrxy",scharrxy)
cv2.waitKey()
cv2.destroyAllWindows()
运行:
3. Laplacian(拉普拉斯)算子:
类似于二阶Sobel导数,需要计算两个方向的梯度值。计算方式:P5 = (P2+P4+P6+P8) - 4 * P5,同样需要取绝对值。
三种算子对比示例:
import cv2
o = cv2.imread('lena.bmp',cv2.IMREAD_GRAYSCALE)
# Sobel
sobelx = cv2.Sobel(o,cv2.CV_64F,1,0,ksize=3)
sobely = cv2.Sobel(o,cv2.CV_64F,0,1,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx) # 转回uint8
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
# Scharr
scharrx = cv2.Scharr(o,cv2.CV_64F,1,0)
scharry = cv2.Scharr(o,cv2.CV_64F,0,1)
scharrx = cv2.convertScaleAbs(scharrx) # 转回uint8
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx,0.5,scharry,0.5,0)
# Laplacion
lap = cv2.Laplacian(o,cv2.CV_64F)
lapxy = cv2.convertScaleAbs(lap) # 转回uint8
cv2.imshow("original",o)
cv2.imshow("sobelxy",sobelxy)
cv2.imshow("scharrxy",scharrxy)
cv2.imshow("lapxy",lapxy)
cv2.waitKey()
cv2.destroyAllWindows()
运行:
4. 总结:
Sobel 和 Scharr 算子都是分xy方向的,Scharr 的加权值比 Sobel 高,所以相邻区域的像素值对其影响较大。但是同时细节更丰富,精度高。
Laplacian算子不分xy,对噪声比较敏感,对边缘也敏感。
OVER…