OpenCV笔记整理【图像梯度之Sobel & Scharr & Laplacian】

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()

运行:
在这里插入图片描述

扫描二维码关注公众号,回复: 14808732 查看本文章

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…

猜你喜欢

转载自blog.csdn.net/qq_34699535/article/details/119141404