python+opencv边缘方法整理
#边缘检测
####基于搜索(一阶导数)
#Roberts算子
#交叉微分算法,它是基于交叉差分的梯度算法,通过局部差分计算检测边缘线条。
#常用来处理具有陡峭的低噪声图像,当图像边缘接近于正45度或负45度时,该算法处理效果更理想。
#其缺点是对边缘的定位不太准确,提取的边缘线条较粗。
def Roberts(gray):
kernelx = np.array([[-1, 0], [0, 1]], dtype=int)
kernely = np.array([[0, -1], [1, 0]], dtype=int)
x = cv2.filter2D(gray, cv2.CV_16S, kernelx)
y = cv2.filter2D(gray, cv2.CV_16S, kernely)
# 转uint8
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
return Roberts
#Prewitt算子
#采用33模板对区域内的像素值进行计算,而Robert算子的模板为22,
# 故Prewitt算子的边缘检测结果在水平方向和垂直方向均比Robert算子更加明显。Prewitt算子适合用来识别噪声较多、灰度渐变的图像
def Prewitt(gray):
kernelx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
kernely = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
x = cv2.filter2D(gray, cv2.CV_16S, kernelx)
y = cv2.filter2D(gray, cv2.CV_16S, kernely)
# 转uint8
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Prewitt = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
return Prewitt
#Sobel算子
#结合了高斯平滑和微分求导。该算子用于计算图像明暗程度近似值,
# 根据图像边缘旁边明暗程度把该区域内超过某个数的特定点记为边缘。
# Sobel算子在Prewitt算子的基础上增加了权重的概念,认为相邻点的距离远近对当前像素点的影响是不同的,距离越近的像素点对应当前像素的影响越大,从而实现图像锐化并突出边缘轮廓。
#Sobel算子的边缘定位更准确,常用于噪声较多、灰度渐变的图像。
def Sobel(gray):
x = cv2.Sobel(gray, cv2.CV_16S, 1, 0) # 对x求一阶导
y = cv2.Sobel(gray, cv2.CV_16S, 0, 1) # 对y求一阶导
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
return Sobel
#Scharr算子
# Scharr 算子是对 Sobel 算子差异性的增强,两者之间的在检测图像边缘的原理和使用方式上相同。
# Scharr 算子的主要思路是通过将模版中的权重系数放大来增大像素值间的差异。
# Scharr 算子又称为 Scharr 滤波器,也是计算 x 或 y 方向上的图像差分,在 OpenCV 中主要是配合 Sobel 算子的运算而存在的
def scharr(gray):
x = cv2.Scharr(gray, cv2.CV_16S, 1, 0) # X 方向
y = cv2.Scharr(gray, cv2.CV_16S, 0, 1) # Y 方向
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
scharr = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
return scharr
#Kirsch,Robinson算子,
# Kirsch边缘算子由八个方向的卷积核构成,这8个模板代表8个方向,对图像上的8个特定边缘方向作出最大响应,运算中取最大值作为图像的边缘输出
def Kirsch(gray):
# 定义Kirsch 卷积模板
m1 = np.array([[5, 5, 5], [-3, 0, -3], [-3, -3, -3]])
m2 = np.array([[-3, 5, 5], [-3, 0, 5], [-3, -3, -3]])
m3 = np.array([[-3, -3, 5], [-3, 0, 5], [-3, -3, 5]])
m4 = np.array([[-3, -3, -3], [-3, 0, 5], [-3, 5, 5]])
m5 = np.array([[-3, -3, -3], [-3, 0, -3], [5, 5, 5]])
m6 = np.array([[-3, -3, -3], [5, 0, -3], [5, 5, -3]])
m7 = np.array([[5, -3, -3], [5, 0, -3], [5, -3, -3]])
m8 = np.array([[5, 5, -3], [5, 0, -3], [-3, -3, -3]])
# 周围填充一圈
# 卷积时,必须在原图周围填充一个像素
graym = cv2.copyMakeBorder(gray, 1, 1, 1, 1, borderType=cv2.BORDER_REPLICATE)
temp = list(range(8))
gray1 = np.zeros(graym.shape) # 复制空间 此处必须的重新复制一块和原图像矩阵一样大小的矩阵,以保存计算后的结果
for i in range(1, gray.shape[0] - 1):
for j in range(1, gray.shape[1] - 1):
temp[0] = np.abs((np.dot(np.array([1, 1, 1]), (m1 * gray[i - 1:i + 2, j - 1:j + 2]))).dot(np.array([[1], [1], [1]])))
# 利用矩阵的二次型表达,可以计算出矩阵的各个元素之和
temp[1] = np.abs((np.dot(np.array([1, 1, 1]), (m2 * gray[i - 1:i + 2, j - 1:j + 2]))).dot(np.array([[1], [1], [1]])))
temp[2] = np.abs((np.dot(np.array([1, 1, 1]), (m1 * gray[i - 1:i + 2, j - 1:j + 2]))).dot(np.array([[1], [1], [1]])))
temp[3] = np.abs((np.dot(np.array([1, 1, 1]), (m3 * gray[i - 1:i + 2, j - 1:j + 2]))).dot(np.array([[1], [1], [1]])))
temp[4] = np.abs((np.dot(np.array([1, 1, 1]), (m4 * gray[i - 1:i + 2, j - 1:j + 2]))).dot(np.array([[1], [1], [1]])))
temp[5] = np.abs((np.dot(np.array([1, 1, 1]), (m5 * gray[i - 1:i + 2, j - 1:j + 2]))).dot(np.array([[1], [1], [1]])))
temp[6] = np.abs((np.dot(np.array([1, 1, 1]), (m6 * gray[i - 1:i + 2, j - 1:j + 2]))).dot(np.array([[1], [1], [1]])))
temp[7] = np.abs((np.dot(np.array([1, 1, 1]), (m7 * gray[i - 1:i + 2, j - 1:j + 2]))).dot(np.array([[1], [1], [1]])))
gray1[i, j] = np.max(temp)
if gray1[i, j] > 255:# 此处的阈值一般写255,根据实际情况选择0~255之间的值
gray1[i, j] = 255
else:
gray1[i, j] = 0
#print('Kirsch算子后图片尺寸',gray1.shape)
gray2= cv2.resize(gray1, (gray.shape[0], gray.shape[1]))
Kirsch=gray2
#print('gray2算子后图片尺寸', gray2.shape)
return Kirsch
#Canny算子
# Canny方法不容易受噪声干扰,能够检测到真正的弱边缘。
# 优点在于,使用两种不同的阈值分别检测强边缘和弱边缘,并且当弱边缘和强边缘相连时,才将弱边缘包含在输出图像中。
def canny(gray):
# 高斯滤波降噪
gaussian = cv2.GaussianBlur(gray, (3, 3), 0)
# Canny算子
canny = cv2.Canny(gaussian, 50, 180)
return canny
####基于零交叉(二阶导数)
#Laplacian算子
#n维欧几里德空间中的一个二阶微分算子,常用于图像增强领域和边缘提取
# Laplacian算子其实主要是利用Sobel算子的运算,通过加上Sobel算子运算出的图像x方向和y方向上的导数,得到输入图像的图像锐化结果。
# 同时,在进行Laplacian算子处理之后,还需要调用convertScaleAbs()函数计算绝对值,并将图像转换为8位图进行显示。
def laplacian(gray):
dst = cv2.Laplacian(gray, cv2.CV_16S, ksize = 3)
laplacian = cv2.convertScaleAbs(dst)
return laplacian
#LOG算子#Marr-Hildreth算子
# 根据图像的信噪比来求检测边缘的最优滤波器。
# 该算法首先对图像做高斯滤波,然后再求其拉普拉斯( Laplacian )二阶导数,
# 根据二阶导数的过零点来检测图像的边界,即通过检测滤波结果的零交叉( Zero crossings )来获得图像或物体的边缘。
# LOG 算子实际上是把 Gauss 滤波和 Laplacian 滤波结合了起来,先平滑掉噪声,再进行边缘检测。
# LOG 算子与视觉生理中的数学模型相似,因此在图像处理领域中得到了广泛的应用。
# 它具有抗干扰能力强,边界定位精度高,边缘连续性好,能有效提取对比度弱的边界等特点。
def log(gray):
# 先通过高斯滤波降噪
gaussian = cv2.GaussianBlur(gray, (3, 3), 0)
# 再通过拉普拉斯算子做边缘检测
dst = cv2.Laplacian(gaussian, cv2.CV_16S, ksize=3)
log = cv2.convertScaleAbs(dst)
return log
#DoG算子
# (1)灰度化图像
# (2)计算方差为2和3.2的两个高斯滤波后的图像
# (3)求两个之差——将二者之差再除以2(归一化)
def DoG(gray):
gimg1 = filters.gaussian(gray, sigma=2)
gimg2 = filters.gaussian(gray, sigma=1.6 * 2)
# 两个高斯运算的差分
dimg = gimg2 - gimg1
# 将差归一化
dimg /= 2
#二值化边缘
edge=np.copy(dimg)
edge[edge>0]=255
edge[edge <= 0] = 0
edge=edge.astype(np.uint8)
#图像边缘抽象化
asbstraction=-np.copy(dimg)
asbstraction=asbstraction.astype(np.float32)
asbstraction[asbstraction>=0]=0.1
asbstraction[asbstraction<0]=0.1+np.tanh(asbstraction[asbstraction<0])
DoG=asbstraction
#return edge
return DoG