
@toc
边缘是像素值发生跃迁的位置,是图像的显著特征之一,在图像特征提取,对象检测,模式识别等方面都有重要的作用!
Sobel算子
sobel算子对图像求一阶导数。一阶导数越大,说明像素在该方向的变化越大,边缘信号越强。因为图像的灰度值都是离散的数字, sobel算子采用离散差分算子计算图像像素点亮度值的近似梯度.


soble x轴和y轴要分别计算,计算完用add添加在一起
cv2.soble(image, ddepth,dx,dy,ksize)
- image:需要处理的图片
- ddepth: 输出图像的深度(可以理解为数据类型),-1表示与原图像相同的深度。
- dx:0即为不计算,1即为计算
- dy:同dy,0即为不计算,1即为计算。
- ksize:卷积核的大小,为一个整数
值得注意的是ddepth在函数使用的时候经常会规定为cv2.CV_64F,与变化为绝对值函数cv2.convertScaleAbs一起配套使用。因为默认为-1的话,如果出现卷积核所在区域,左边为黑,右边为白,则会出现负数,opencv默认会进行截断操作,影响轮廓的显示。所有用cv2.CV_64F保留负数的像素 (如果此时直接imshow显示仍然会出现截断为0的情况) ,再用cv2.convertScaleAbs转化为正数,这样才能够保证算子的准确性。接下来我们比较ddepth为-1和cv2.CV_64F然后转化为绝对值这两种情况。
首先是默认-1的情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
""" @Project :Opencv计算机视觉 @File :12.sobel算子中ddepth为-1.py @IDE :PyCharm @Author :咋 @Date :2023/1/29 19:04 """ import cv2 import numpy as np image = cv2.imread("za.jpg") image = cv2.resize(image,(640,480))
dx = cv2.Sobel(image,-1,1,0,ksize = 3)
dy = cv2.Sobel(image,-1,0,1,ksize = 3)
sobel_img = cv2.addWeighted(dx,0.5,dy,0.5,0)
cv2.imshow("sobel_dx_dy",np.hstack((dx,dy))) cv2.imwrite("sobel_dx_dy.jpg",np.hstack((dx,dy))) cv2.imshow("sobel_img_1",np.hstack((image,sobel_img))) cv2.imwrite("sobel_img_1.jpg",np.hstack((image,sobel_img))) cv2.waitKey(0) cv2.destroyAllWindows()
|


上面左上是dx方向计算的结果,基本上都为竖直方向的轮廓。右上为dy方向计算的结果,基本上都为水平方向的轮廓。左下为原图,右下即为ddepth设置为-1时的边缘检测效果,基本的轮廓已经能够看出来了,但是中间有些地方还是没能检测出来。
ddepth设置为CV_64F:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
""" @Project :Opencv计算机视觉 @File :13.sobel算子中ddepth为cv2.CV_64F.py @IDE :PyCharm @Author :咋 @Date :2023/1/29 19:39 """ import cv2 import numpy as np image = cv2.imread("za.jpg") image = cv2.resize(image,(640,480))
dx = cv2.Sobel(image,cv2.CV_64F,1,0,ksize = 3) dx = cv2.convertScaleAbs(dx)
dy = cv2.Sobel(image,cv2.CV_64F,0,1,ksize = 3) dy = cv2.convertScaleAbs(dy)
sobel_img = cv2.addWeighted(dx,0.5,dy,0.5,0)
cv2.imshow("sobel_img",np.hstack((image,sobel_img))) cv2.imshow("sobel_dx_dy_64",np.hstack((dx,dy)))
cv2.imwrite("sobel_img_64.jpg",np.hstack((image,sobel_img))) cv2.imwrite("sobel_dx_dy_64.jpg",np.hstack((dx,dy))) cv2.waitKey(0) cv2.destroyAllWindows()
|


同样,左上为dx方向,右上为dy方向,左下为原图,右下为ddepth设置为CV_64F时的检测效果,可以看到检测效果比上面好很多,所有的边界轮廓都检测出来了,而且很清晰。实践证明CV_64F比-1的检测效果要更好。那么相信会有同学提问了:为什么要分开计算dx,dy,sobel算子计算时直接都设置为1,一起计算不是更省事吗?
我们一起看下分开计算dx,dy和一起计算dx,dy的对比效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
""" @Project :Opencv计算机视觉 @File :14.sobel算子合并运算与分开运算对比.py @IDE :PyCharm @Author :咋 @Date :2023/1/29 19:50 """ import cv2 import numpy as np image = cv2.imread("za.jpg") image = cv2.resize(image,(640,480))
dx = cv2.Sobel(image,cv2.CV_64F,1,0,ksize = 3) dx = cv2.convertScaleAbs(dx)
dy = cv2.Sobel(image,cv2.CV_64F,0,1,ksize = 3) dy = cv2.convertScaleAbs(dy)
apart_sobel = cv2.addWeighted(dx,0.5,dy,0.5,0)
all_sobel= cv2.Sobel(image,cv2.CV_64F,1,1,ksize = 3) all_sobel = cv2.convertScaleAbs(all_sobel)
cv2.imshow("contrast_img",np.hstack((apart_sobel,all_sobel)))
cv2.imwrite("contrast_img.jpg",np.hstack((apart_sobel,all_sobel))) cv2.waitKey(0) cv2.destroyAllWindows()
|

左侧为分开计算dxdy时的图片,轮廓非常清楚。右侧是一起计算,不仅会有轮廓的确实,而且有重影的感觉。所以在是用Sobel算子时,建议还是设置ddepth=cv2.CV_64F,再使用cv2.convertScaleAbs()取绝对值,这样的检测效果更好。
Scharr算子
在Opencv中直接提供了Scharr调用的API:cv2.Scharr()
cv2.Scharr() :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
""" @Project :Opencv计算机视觉 @File :15.Scharr算子.py @IDE :PyCharm @Author :咋 @Date :2023/1/29 20:09 """ import cv2 import numpy as np image = cv2.imread("za.jpg") image = cv2.resize(image,(640,480))
dx = cv2.Scharr(image,-1,1,0)
dy = cv2.Scharr(image,-1,0,1)
scharr_img = cv2.addWeighted(dx,0.5,dy,0.5,0)
cv2.imshow("scharr_img",np.hstack((image,scharr_img))) cv2.imwrite("scharr.jpg",np.hstack((image,scharr_img))) cv2.waitKey(0) cv2.destroyAllWindows()
|

和之前的sobel算子ddepth设置为-1时非常相似,在这里就不过多介绍。
laplacian算子
laplacian算子又称为拉普拉斯算子,在opencv中的API为cv2.Laplacian(),注意大小写。
cv2.Laplacian()


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
""" @Project :Opencv计算机视觉 @File :16.Laplacian算子.py @IDE :PyCharm @Author :咋 @Date :2023/1/29 20:14 """ import cv2 import numpy as np image = cv2.imread("za.jpg") image = cv2.resize(image,(640,480))
lap_image = cv2.Laplacian(image,-1,ksize = 5)
cv2.imshow("lap_image",np.hstack((image,lap_image)))
cv2.imwrite("lap_image.jpg",np.hstack((image,lap_image))) cv2.waitKey(0) cv2.destroyAllWindows()
|

虽然代码很简单,但是效果确非常好!轮廓非常清晰!
canny算子
canny算子称得上是最经典且强大的算子,其主要步骤如下:
使用高斯滤波器,以平滑图像,滤除噪声。
计算图像中每个像素点的梯度强度和方向。
应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。
应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
通过抑制孤立的弱边缘最终完成边缘检测。





1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
""" @Project :Opencv计算机视觉 @File :17.canny算子.py @IDE :PyCharm @Author :咋 @Date :2023/1/29 20:23 """ import cv2 import numpy as np image = cv2.imread("za.jpg",cv2.IMREAD_GRAYSCALE) image = cv2.resize(image,(640,480))
v1=cv2.Canny(image,50,100)
cv2.imshow("canny_image",np.hstack((image,v1)))
cv2.imwrite("canny_image.jpg",np.hstack((image,v1))) cv2.waitKey(0) cv2.destroyAllWindows()
|

从上图可以看出来,canny算子而是非常的清楚,能够很好的完成任务!
总结
本篇博客详细介绍了Opencv四大经典算子——sobel算子、scharr算子、laplacian算子、canny算子,通过实际的图片轮廓检测对不同算子的检测效果进行评估。虽然各算子名称不同,但是实际上是卷积核的使用不同,不同的卷积核会对图片产生不同的印象。卷积的存在也为计算机视觉提供了多种多样的可能性和无穷的创造性,人工智能依旧很广阔,计算机视觉的学习依旧在路上!继续加油,感兴趣的小伙伴欢迎私信博主!
✨$\textcolor{blue}{原创不易,还希望各位大佬支持一下}$
👍 $\textcolor{green}{点赞,你的认可是我创作的动力!}$
⭐️ $\textcolor{green}{收藏,你的青睐是我努力的方向!}$
✏️ $\textcolor{green}{评论,你的意见是我进步的财富!}$