草鸡详细的canny()函数理解和代码实现
一个电脑小白的自我成长之路^_&。
canny()边缘检测过程
1.Canny边缘检测算子是John F. Canny于 1986 年开发出来的一个多级边缘检测算法。
2.Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:
好的检测- 算法能够尽可能多地标识出图像中的实际边缘。
好的定位- 标识出的边缘要尽可能与实际图像中的实际边缘尽可能接近。
最小响应- 图像中的边缘只能标识一次,并且可能存在的图像噪声不应标识为边缘。
3.算法步骤:
①高斯模糊 - GaussianBlur。以平滑图像,滤除噪声。
②灰度转换 - cvtColor
③计算梯度 – Sobel/Scharr
④非最大信号抑制,以消除边缘检测带来的杂散响应。
⑤高低阈值输出二值图像,检测来确定真实的和潜在的边缘。
最后,通过抑制孤立的弱边缘最终完成边缘检测。
4****高斯平滑滤波
为了尽可能减少噪声对边缘检测结果的影响,所以必须滤除噪声以防止由噪声引起的错误检测。为了平滑图像,使用高斯滤波器与图像进行卷积,该步骤将平滑图像,以减少边缘检测器上明显的噪声影响。大小为(2k+1)x(2k+1)(如果是3*3的卷积核,k为1)的高斯滤波器核的生成方程式由下式给出:
这个卷积核是这样的,第一行i为0,第一列j为0,sigma=1.4(2π后面那个σ),然后代入上式计算,得到值为0.0924,依次计算得到其他的值:
然后,对这个矩阵进行归一化处理,处理过程就是,将每行每列的值除以这9个数字的和,譬如0.0487/(0.0487+0.0629+0.0487+0.0629+0.0812+0.0629+0.0487+0.0629+0.0487)=0.0924,将0.0924放在第一行第一列的位置,依次计算其他的值,得到归一化后的矩阵如下:
若图像中一个3x3的窗口为A,要滤波的像素点为e,则经过高斯滤波之后,像素点e的灰度值为:
即对两个矩阵进行卷积,卷积就是两个矩阵对应行列上的数进行相乘,然后求和。类似于两个向量的内积(点乘)
高斯卷积核大小的选择将影响Canny检测器的性能。尺寸越大,检测器对噪声的敏感度越低,但是边缘检测的定位误差也将略有增加。一般5x5
5 计算梯度强度和方向
图像中的边缘可以指向各个方向,因此Canny算法使用四个算子来检测图像中的水平、垂直和对角缘。边缘检测的算子(如Roberts,Prewitt,Sobel等)返回水平Gx和垂直Gy方向的一阶导数值,由此便可以确定像素点的梯度G和方向θ 。
其中G为梯度强度, theta表示梯度方向,arctan为反正切函数。下面以Sobel算子为例讲述如何计算梯度强度和方向。x和y方向的Sobel算子分别为:
若图像中一个3x3的窗口为A,要计算梯度的像素点为e,则和Sobel算子进行卷积之后,像素点e在x和y方向的梯度值分别为:
6非极大值抑制
非极大值抑制是一种边缘稀疏技术,非极大值抑制的作用在于“瘦”边。对图像进行梯度计算后,仅仅基于梯度值提取的边缘仍然很模糊。对于标准3,对边缘有且应当只有一个准确的响应。而非极大值抑制则可以帮助将局部最大值之外的所有梯度值抑制为0,对梯度图像中每个像素进行非极大值抑制的算法是:
将当前像素的梯度强度与沿正负梯度方向上的两个像素进行比较。
如果当前像素的梯度强度与另外两个像素相比最大,则该像素点保留为边缘点,否则该像素点将被抑制
7双阈值检测
在施加非极大值抑制之后,剩余的像素可以更准确地表示图像中的实际边缘。然而,仍然存在由于噪声和颜色变化引起的一些边缘像素。为了解决这些杂散响应,必须用弱梯度值过滤边缘像素,并保留具有高梯度值的边缘像素,可以通过选择高低阈值来实现。如果边缘像素的梯度值高于高阈值,则将其标记为强边缘像素;如果边缘像素的梯度值小于高阈值并且大于低阈值,则将其标记为弱边缘像素;如果边缘像素的梯度值小于低阈值,则会被抑制。阈值的选择取决于给定输入图像的内容。
8抑制孤立低阈值点
到目前为止,被划分为强边缘的像素点已经被确定为边缘,因为它们是从图像中的真实边缘中提取出来的。然而,对于弱边缘像素,将会有一些争论,因为这些像素可以从真实边缘提取也可以是因噪声或颜色变化引起的。为了获得准确的结果,应该抑制由后者引起的弱边缘。通常,由真实边缘引起的弱边缘像素将连接到强边缘像素,而噪声响应未连接。为了跟踪边缘连接,通过查看弱边缘像素及其8个邻域像素,只要其中一个为强边缘像素,则该弱边缘点就可以保留为真实的边缘。
python实现代码如下:
## canny()函数理解
## author:wuming
## email:[email protected]
## Time:2020.8.4
import cv2 as cv
import numpy as np
def edge_demo(image):
blurred = cv.GaussianBlur(image, (3, 3), 0) # 高斯模糊 输入图片image keral大小3*3,
# sigmax,sigmay设置为0,使用默认形式
gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY) # 灰度转换 将图片进行灰色转换 后面canny需图片是8位的
# X Gradient 计算梯度
xgrad = cv.Sobel(gray, cv.CV_16SC1, 1, 0) # 采用sobel函数对图片纵方向梯度计算
# Y Gradient
ygrad = cv.Sobel(gray, cv.CV_16SC1, 0, 1) # 采用sobel函数对图片横方向梯度计算
# edge
edge_outputx = cv.Canny(xgrad, ygrad, 50, 150) # canny()检测,设置双阈值。低阈值50,高阈值150
edge_outputy = cv.Canny(gray, 50, 150) # 注意这一步和上面一行一样都是做边缘检测,
# cv.Canny()算法重载了多种入参方式,会有不同的结果
cv.imshow("Canny Edge x", edge_outputx)
cv.imshow("Canny Edge y", edge_outputy)
dst = cv.bitwise_and(image, image, mask=edge_outputx)
cv.imshow("Color Edge", dst)
print("--------- Python OpenCV Tutorial ---------")
src = cv.imread("lena.jpg") # 加载图片,这里图片是相对路径,需把图片放在程序文件夹中
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE) # 图片窗口和原图自动保持一致
#cv.namedWindow("input image", cv.WINDOW_NORMAL)
cv.imshow("input image", src) # 显示原图,第一个参数是窗口title,第二个参数是图片
edge_demo(src) # 调用edge_demo函数,将原图src作为参数传入
cv.waitKey(0)
cv.destroyAllWindows()
有什么不理解的,留言吧。博主看到会第一时间回复的