边缘定义及类型
边缘类型:简单分为 4 种类型,阶跃型、屋脊型、斜坡型、脉冲型,其中阶跃型和斜坡型是类似的,只是变化的快慢不同。
边缘检测算子类别
边缘检测算子:
- 一阶导数: Roberts、Sobel、Prewitt
- 二阶导数: Laplacian、Log/Marr、(Kirsch、Nevitia)
- 非微分边缘检测算子: Canny
关于算子详情请查看:https://blog.csdn.net/wsp_1138886114/article/details/81368890
OpenCV-Python 中 Canny() 参数
步骤:
- 彩色图像转换为灰度图像(以灰度图或者单通道图读入)
- 对图像进行高斯模糊(去噪)
- 计算图像梯度,根据梯度计算图像边缘幅值与角度
- 沿梯度方向进行非极大值抑制(边缘细化)
- 双阈值边缘连接处理
- 二值化图像输出结果
""" cv2.Canny(image, # 输入原图(必须为单通道图) threshold1, threshold2, # 较大的阈值2用于检测图像中明显的边缘 [, edges[, apertureSize[, # apertureSize:Sobel算子的大小 L2gradient ]]]) # 参数(布尔值): true: 使用更精确的L2范数进行计算(即两个方向的倒数的平方和再开放), false:使用L1范数(直接将两个方向导数的绝对值相加)。 """ import cv2 import numpy as np original_img = cv2.imread("qingwen.png", 0) # canny(): 边缘检测 img1 = cv2.GaussianBlur(original_img,(3,3),0) canny = cv2.Canny(img1, 50, 150) # 形态学:边缘检测 _,Thr_img = cv2.threshold(original_img,210,255,cv2.THRESH_BINARY)#设定红色通道阈值210(阈值影响梯度运算效果) kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5)) #定义矩形结构元素 gradient = cv2.morphologyEx(Thr_img, cv2.MORPH_GRADIENT, kernel) #梯度 cv2.imshow("original_img", original_img) cv2.imshow("gradient", gradient) cv2.imshow('Canny', canny) cv2.waitKey(0) cv2.destroyAllWindows()
可调整阈值大小的程序
import cv2 import numpy as np def CannyThreshold(lowThreshold): detected_edges = cv2.GaussianBlur(gray,(3,3),0) detected_edges = cv2.Canny(detected_edges, lowThreshold, lowThreshold*ratio, apertureSize = kernel_size) dst = cv2.bitwise_and(img,img,mask = detected_edges) # just add some colours to edges from original image. cv2.imshow('canny demo',dst) lowThreshold = 0 max_lowThreshold = 100 ratio = 3 kernel_size = 3 img = cv2.imread('qingwen.png') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) cv2.namedWindow('canny demo') cv2.createTrackbar('Min threshold','canny demo',lowThreshold, max_lowThreshold, CannyThreshold) CannyThreshold(0) # initialization if cv2.waitKey(0) == 27: cv2.destroyAllWindows()
教程
Canny 边缘检测方法常被誉为边缘检测的最优方法,废话不多说,先看个例子:
import cv2 import matplotlib.pyplot as plt import numpy as np p = '../11-Edge-Detection/handwriting.jpg' img = cv2.imread(p, 0) edges = cv2.Canny(img, 30, 70) plt.imshow(np.hstack((img, edges)), 'gray') plt.show()
cv2.Canny() 进行边缘检测,参数 2、3 表示最低、高阈值,下面来解释下具体原理。
经验之谈:之前我们用低通滤波的方式模糊了图片,那反过来,想得到物体的边缘,就需要用到高通滤波。推荐先阅读:番外篇:图像梯度。
Canny 边缘检测
Canny 边缘提取的具体步骤如下:
1,使用 5×5 高斯滤波消除噪声:
边缘检测本身属于锐化操作,对噪点比较敏感,所以需要进行平滑处理。
K= 1/256 [ 1 4 6 4 1
4 16 24 16 4
6 24 36 24 6
4 16 24 16 4
1 4 6 4 1]
2,计算图像梯度的方向:
首先使用 Sobel 算子计算两个方向上的梯度 G_x 和 G_y,然后算出梯度的方向:
保留这四个方向的梯度:0°/45°/90°/135°,有什么用呢?我们接着看。
3,取局部极大值:
梯度其实已经表示了轮廓,但为了进一步筛选,可以在上面的四个角度方向上再取局部极大值:
比如,A 点在 45° 方向上大于 B / C 点,那就保留它,把 B / C 设置为 0。
4,滞后阈值:
经过前面三步,就只剩下 0 和可能的边缘梯度值了,为了最终确定下来,需要设定高低阈值:
- 像素点的值大于最高阈值,那肯定是边缘(上图A)
- 同理像素值小于最低阈值,那肯定不是边缘
- 像素值介于两者之间,如果与高于最高阈值的点连接,也算边缘,所以上图中C算,B不算
Canny 推荐的高低阈值比在 2:1 到 3:1 之间。
先阈值分割后检测
其实很多情况下,阈值分割后再检测边缘,效果会更好:
import cv2 import matplotlib.pyplot as plt import numpy as np p = '../11-Edge-Detection/handwriting.jpg' img = cv2.imread(p, 0) _, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) edges = cv2.Canny(thresh, 30, 70) plt.imshow(np.hstack((img, edges)), 'gray') plt.show()
reference
https://blog.csdn.net/wsp_1138886114/article/details/82935839
http://codec.wang/#/opencv/basic/11-edge-detection