一、理论分析
图像的边缘信息通俗来讲变化较大。基于此特征和数字图像的离散信号,我们可以计算图片的差分或梯度。
图像处理中有多种边缘检测的算电子,包括普通一阶差分,Sobel算子,Scharr算子等等,是基于寻找梯度强度。而普通二阶差分中,Laplacian算子其思想是基于过零点检测。
二、代码分析
2.1 边缘检测
2.1.1 Sobel算子
对每一个像素点px,用有右减左侧的特征值。因为对于边缘特征来说,px的值较大。那么值越大的说明其越有可能是边缘特征。
如下图所示,表示计算图像的水平特征。右侧减去左侧数值。
from cv2 import cv2 as cv
import numpy as np
img = cv.imread(r"./CV-Pictures/sobel.bmp",0)
cv.imshow("img",img)
sobel_x = cv.Sobel(img,-1,1,0) #-1处理结果代表与原图一致,1代表x,0代表y
cv.imshow("sobel_x",sobel_x)
cv.waitKey(0)
cv.destroyAllWindows()
结果如下,可以看到图像边缘x轴方向,采集只有右侧,因为sobel算法是,右侧减去左侧。那么左侧的点就被算为负值,直接被赋值为0,所以要对处理结果,取绝对值。让负值变为正值。
那么修改代码为:
from cv2 import cv2 as cv
import numpy as np
img = cv.imread(r"./CV-Pictures/sobel.bmp",0)
cv.imshow("img",img)
sobel_x = cv.Sobel(img,-1,1,0)
sobel_x = cv.convertScaleAbs(sobel_x)
cv.imshow("sobel_x",sobel_x)
cv.waitKey(0)
cv.destroyAllWindows()
代码运行结果如下:
可以看到还是如此,为什么呢?因为Sobel函数默认采用np.uint8数据类型,也就是说没有负值。都自动取0了。所以我们一般不用-1,而用cv.CV_64F
from cv2 import cv2 as cv
import numpy as np
img = cv.imread(r"./CV-Pictures/sobel.bmp",0)
cv.imshow("img",img)
sobel_x = cv.Sobel(img,cv.CV_64F,0,1)
sobel_x = cv.convertScaleAbs(sobel_x)
sobel_y = cv.Sobel(img,cv.CV_64F,1,0)
sobel_y = cv.convertScaleAbs(sobel_y)
sobel_add_xy = cv.addWeighted(sobel_x,0.5,sobel_y,0.5,0)
cv.imshow("sobel_xy",sobel_xy)
cv.waitKey(0)
cv.destroyAllWindows()
代码运行结果如下:
2.1.2 Scharr算子
Scharr算子和Sobel算子类似,不同的是,Scharr算子,同一列或同一行的数值差异更大。
from cv2 import cv2 as cv
import numpy as np
img = cv.imread(r"CV-Pictures/010.jpg",0)
cv.imshow("img",img)
Scharr_x = cv.Scharr(img,cv.CV_64F,0,1)
Scharr_x = cv.convertScaleAbs(Scharr_x)
Scharr_y = cv.Scharr(img,cv.CV_64F,1,0)
Scharr_y = cv.convertScaleAbs(Scharr_y)
Scharr_add_xy = cv.addWeighted(Scharr_x,0.5,Scharr_y,0.5,0)
cv.imshow("Scharr_x",Scharr_x)
cv.imshow("Scharr_y",Scharr_y)
cv.imshow("Scharr_xy",Scharr_add_xy)
cv.waitKey(0)
cv.destroyAllWindows()
可以对比一下Sobel检测,如下如所示
2.1.3 Laplacian算子
我们可以看到,简单的算子是要分别计算x方向和y方向,为了防止数值溢出,采用加权和。而拉普拉斯算子实现了同时计算两个方向的算子。
from cv2 import cv2 as cv
import numpy as np
img = cv.imread(r"CV-Pictures/015.jpg",0)
laplacian_img = cv.Laplacian(img,cv.CV_64F)
laplacian_img = cv.convertScaleAbs(laplacian_img)
cv.imshow("img",img)
cv.imshow("laplacian",laplacian_img)
cv.waitKey(0)
cv.destroyAllWindows()
运行结果如下
2.1.4 Canny算子
这个方法是传统的边缘检测方法中最为有效,效果最好的方法。其主要包括四个步骤,分别为:
- 去噪
- 梯度
- 非极大值抑制
- 滞后阈值
去噪
边缘检测已收到噪声的影响。因此,在进行边缘检测前,通常需要先进行去噪。去噪的方法一般是采用高斯滤波器去噪
且一个像素点的临近像素具有更高的重要度,对周围的像素计算加权平均值。其中的邻近的像素具有更大的权重
梯度
对平滑后的图像采用Sobal算子计算梯度和方向。
其中梯度的方向一般总是与边界垂直。梯度的方向分为四类,垂直、水平、两个对角线方向。
非极大值抑制
获得梯度的大小和方向后,逐个遍历像素点,判断当前像素点是否是周围像素点中具有相同梯度的最大值。
假设A,B,C三点具有相同的方向,梯度方向垂直于边缘。判断是否极大值,是的话保留,不是就抑制。
滞后阈值
我们提供一个最小阈值和最大阈值,也就是说所有检测出来的边缘值,都要存在于我的阈值之间,在阈值范围内舍弃,超出阈值的舍弃。只保留边界在阈值的边界之间的检测线。
from cv2 import cv2 as cv
import numpy as np
img = cv.imread(r"CV-Pictures/lena2.jpg")
cv.imshow("img",img)
dst1 = cv.Canny(img,100,200)
dst2 = cv.Canny(img,64,128)
cv.imshow("canny",dst1)
cv.imshow("canny1",dst2)
cv.waitKey(0)
cv.destroyAllWindows()
代码运行结果如下:
2.2 特征检测
特征检测检测出的主要是图像的轮廓。那么轮廓相比于不连续的边缘是一个整体。
图像的轮廓处理要注意以下问题:
- 对象是二值图像。
- 查找轮廓需要更改原始图像,所以要把原始图像拷贝一份。
- 在OpenCV中,是从黑色背景中找白色对象。因此,背景必须为黑,对象必须位白。
在OpenCV中,我们使用findContours()和drawContours()分别来查找图像的轮廓与绘制图像的轮廓。
- contours,hierarchy = cv2.findContours(image,mode,method)
其中:
(1)mode一般选择cv2.RETR_EXTERNEL 只检测外轮廓和cv2.RETR_TREE 建立一个等级树结构的轮廓。
(2)method一般采用cv2.CHAIN_APPROX_SIMPLE。
(3)返回值中contours代表图像的轮廓,hierarchy代表图像的拓扑信息(轮廓层次)
- r = cv2.drawContours(o,contours,contoursIdx,color[,thickness])
from cv2 import cv2 as cv
import numpy as np
img = cv.imread(r"./CV-Pictures/contours.bmp")
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
#img = cv.imread("2021-12-01--image/2021-12-01--image/contours.bmp",0) #直接获取灰度图像
ret,binary = cv.threshold(gray,127,255,cv.THRESH_BINARY) #获得二值图
counters,hierarchy = cv.findContours(binary,cv.RETR_TREE,cv.CHAIN_APPROX_SIMPLE) #参数分别为图像轮廓检测模式,图像的轮廓近似方法。
co = img.copy()
r = cv.drawContours(co,counters,3,(0,0,255),3) #counters 表示需要绘制的边缘数组,(-1代表全部,0开始依次从外到内,从右到左),颜色,线条宽度
print(img.shape)
cv.imshow("img",img)
cv.imshow("draw",r)
cv.waitKey(0)
cv.destroyAllWindows()
代码运行结果如下
三、代码文件
小程序员将代码文件和相关素材整理到了百度网盘里,因为文件大小基本不大,大家也不用担心限速问题。后期小程序员有能力的话,将在gitee或者github上上传相关素材。
链接:https://pan.baidu.com/s/1Ce14ZQYEYWJxhpNEP1ERhg?pwd=7mvf
提取码:7mvf