边缘检测
边缘检测的目的是标识数字图像中亮度变化明显的点,边缘检测是特征提取的重要领域。
1、检测方法
边缘检测的方法大致分为两类:基于搜索和基于零交叉
基于搜索的边缘检测方法首先计算边缘强度,通常用一阶导数表示,例如梯度模,然后计算估计边缘的局部方向,通常采用梯度的方向,并利用此方向找到梯度模的最大值。
基于零交叉的方法是找到由图像得到的二阶导数的零交叉点来定位边缘,通常用拉普拉斯算子或非线性微分方程的零交叉点。
2、Sobel边缘检测算子
Sobel边缘检测算法比较简单,实际应用中效率比canny边缘检测效率要高,但边缘不如Canny检测的准确,Socel算子是高斯平滑和微分操作的结合体,所以抗噪声能力强,尤其是效率要求较高,对细纹理不太关系的时候。
模板:
Sobel_x_or_y = cv2.Sobel(src, ddepth, dx, dy, dst, ksize, scale, delta, borderType)
参数:
- src:传入的图像
- ddepth:图像的深度
- dx、dy求导的阶数,0表示这个方向上没有求导,所填数一般为0、1、2.
- ksize:Sobel算子的大小,即卷积核的大小,必须为奇数
- scale:缩放倒数的比例常数,默认情况为没有伸缩系数
- borderType:判断图像边界的模式,默认值为cv2.BORDER_DEFAULT。
import numpy as np
import cv2
# Sobel边缘检测算子
img = cv2.imread('dog.jpg',0)
x = cv2.Sobel(img, cv2.CV_16S, 1, 0)
y = cv2.Sobel(img, cv2.CV_16S, 0, 1)
# cv2.convertScaleAbs(src[, dst[, alpha[, beta]]])
# 可选参数alpha是伸缩系数,beta是加到结果上的一个值,结果返回uint类型的图像
Scale_abX = cv2.convertScaleAbs(x)
Scale_abY = cv2.convertScaleAbs(y)
result = cv2.addWeighted(Scale_abX, 0.5, Scale_abY, 0.5, 0)
cv2.imshow('img', img)
cv2.imshow('Scale_absX', Scale_abX)
cv2.imshow('Scale_absY', Scale_abY)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Sobel算子是在两个方向计算的,最后还需要cv2.addWeighted()函数将其组合起来
result = cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, type]])
其中alpha是第一幅图片中元素的权重,beta是第二幅图像中元素的权重,gamma是加到最后结果上的一个值。
当Sobel()函数的参数ksize=-1时,就演变成了3x3的Scharr算子。算子的模板为:
3、拉普拉斯算子(Laplacian)
Laplacian函数实现的方法是先用Sobel算子计算二阶x和y导数,在求和:
laplacian = cv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])
- src:是需要处理的图像
- ddepth:是图像的深度,-1表示采用的是原图像相同的深度,目标图像的深度必须大于等于原图像的深度
- ksize:算子的大小,即卷积核的大小,必须为1,3,5,7,;默认为1.
import numpy as np
import cv2
# 拉普拉斯算子
img = cv2.imread('dog.jpg', 0)
laplacian = cv2.Laplacian(img, cv2.CV_16S, ksize=3)
dst = cv2.convertScaleAbs(laplacian)
cv2.imshow('img', img)
cv2.imshow('laplacian', laplacian)
cv2.waitKey(0)
cv2.destroyAllWindows()
当参数ksize越大即卷积核越大时,算子对图像梯度的变化越敏感,可以经过高斯模糊处理一下,去掉很多噪声。
blur = cv2.GaussianBlur(img, (3, 3), 0) laplacian = cv2.Laplacian(blur, cv2.CV_16S, ksize=3)
4、Canny算子
图像边缘检测必须满足两个条件:一是能有效地抑制噪声;而是必须尽量精确确定边缘的位置。
根据对信噪比与定位乘积进行测度,得到最优化逼近算子,这就是Canny边缘检测算子。
算法的基本步骤:
- 用高斯滤波器平滑图像
- 用一阶偏导的有限差分来计算梯度的幅值和方向
- 对梯度幅值进行非极大值抑制
- 用双阈值算法检测和连接边缘
canny = cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient ]]])
image:需要处理的原图像单通道的灰度图
threshold1:阈值1
threshold2:阈值2,较大的阈值2用于检测图像中明显的边缘,但一般情况下检测的效果不会那么完美,边缘检测出来是断断续续的,所以应用娇小的第一个阈值将这些间断的边缘连接起来。
apertureSize:卷积核的大小
L2gradient:是一个bool值,如果为true,则使用更精确的L2翻书进行计算(即两个方向的导数的平方和再平方),否则使用L2范数(直接将两个方向导数的绝对值相加)
import numpy as np
import cv2
img = cv2.imread('dog.jpg', 0)
blur = cv2.GaussianBlur(img, (3, 3), 0)# 用高斯滤波处理图像
canny = cv2.Canny(blur, 50, 150) # 50是最小阈值,150是最大阈值
cv2.imshow('img', img)
cv2.imshow('canny', canny)
cv2.waitKey(0)
cv2.destroyAllWindows()
在运行时可以调整阈值大小,对图像进行边缘提取:
import numpy as np
import cv2
lowThreshold = 0
maxThreshold = 100
ratio = 3
kernel_size = 3
img = cv2.imread('dog.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3, 3), 1)
cv2.namedWindow('Canny')
def CannyThreshold(x):
position = cv2.getTrackbarPos('CannBar','Canny')
canny = cv2.Canny(blur, position, position*2.5)
cv2.imshow('Canny', canny) cv2.createTrackbar('Min threshold', 'Canny', lowThreshold, maxThreshold, CannyThreshold)
CannyThreshold(0)
if cv2.waitKey(0) == 27:
cv2.destroyAllWindows()