在介绍边缘检测之前,我们需要先了解什么是图像梯度?
图像梯度
函数可以用一阶导数来表示变化程度,而对于二维图像来说,其局部特性的显著变化可以用梯度来表示。梯度是函数变化的一种度量,定义为:
而对数数字图像而言,导数可用差分来近似。
以上就是图像梯度的简单介绍。下面进入本文正题:
边缘检测算子(edge detectors),是用于在亮度函数中定位变化的非常重要的局部图像预处理方法,边缘是亮度函数发生急剧变化的位置。
一阶微分边缘检测算子
1.Roberts边缘算子
Roberts边缘算子是利用局部差分算子寻找边缘的算子,差分公式如下:
2.Sobel边缘算子
Sobel边缘算子与Roberts边缘算子不同的是,它是在3*3领域内计算梯度值的,这样可以避免像素间内插点上计算梯度。
3.Prewitt边缘算子
Prewitt边缘算子与Sobel边缘算子形式上完全一样,只是c=1,Prewitt边缘算子没有将重点放在接近于模板中心的像素点。
4.Kirsch边缘算子
Kirsch边缘算子由八个方向的卷积核构成,
5.Canny边缘检测
Canny边缘检测的过程可以直接采用原始图像与平滑滤波脉冲响应一阶微分的卷积运算来实现。常用的平滑滤波器为高斯函数,图像经过高斯平滑后边缘变得模糊,因此计算的到的边缘就有一定的宽度,这种宽度边缘变细的方法叫做非极大点的抑制。
Canny边缘检测具体步骤如下:
这里滞后阈值化处理大家可以自行百度,该方法比较简单,相信聪明的你们看一遍就完全理解啦。
-图像锐化(sharpening)
目标是使边缘更陡峭,锐化的图像是供人观察的。锐化公式如下:
拉普拉斯(Laplacian)
拉普拉斯算子常用于这一目的,那么什么是拉普拉斯算子呢?
拉普拉斯(Laplacian)算子在二维图像中,它是常用的二阶微分边缘检测算子,是一种二阶导数算子。
Laplacian算子有一个缺点是它对图像中某些边缘产生双重响应。
同为二阶微分边缘检测算子的还有LOG算子,又称马尔算子,同时它还是二阶导数过零点的算子。
具体代码如下:
#自定义卷积滤波
import cv2
import numpy as np
# 加载图像
image = cv2.imread('D:\Pycharm/untitled/13.jpg',0)
image = cv2.resize(image,(800,800))
# 自定义卷积核
# Roberts边缘算子
kernel_Roberts_x = np.array([
[1, 0],
[0, -1]
])
kernel_Roberts_y = np.array([
[0, -1],
[1, 0]
])
# Sobel边缘算子
kernel_Sobel_x = np.array([
[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]])
kernel_Sobel_y = np.array([
[1, 2, 1],
[0, 0, 0],
[-1, -2, -1]])
# Prewitt边缘算子
kernel_Prewitt_x = np.array([
[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]])
kernel_Prewitt_y = np.array([
[1, 1, 1],
[0, 0, 0],
[-1, -1, -1]])
# Kirsch 边缘检测算子
def kirsch(image):
m,n = image.shape
list=[]
kirsch = np.zeros((m,n))
for i in range(2,m-1):
for j in range(2,n-1):
d1 = np.square(5 * image[i - 1, j - 1] + 5 * image[i - 1, j] + 5 * image[i - 1, j + 1] -
3 * image[i, j - 1] - 3 * image[i, j + 1] - 3 * image[i + 1, j - 1] -
3 * image[i + 1, j] - 3 * image[i + 1, j + 1])
d2 = np.square((-3) * image[i - 1, j - 1] + 5 * image[i - 1, j] + 5 * image[i - 1, j + 1] -
3 * image[i, j - 1] + 5 * image[i, j + 1] - 3 * image[i + 1, j - 1] -
3 * image[i + 1, j] - 3 * image[i + 1, j + 1])
d3 = np.square((-3) * image[i - 1, j - 1] - 3 * image[i - 1, j] + 5 * image[i - 1, j + 1] -
3 * image[i, j - 1] + 5 * image[i, j + 1] - 3 * image[i + 1, j - 1] -
3 * image[i + 1, j] + 5 * image[i + 1, j + 1])
d4 = np.square((-3) * image[i - 1, j - 1] - 3 * image[i - 1, j] - 3 * image[i - 1, j + 1] -
3 * image[i, j - 1] + 5 * image[i, j + 1] - 3 * image[i + 1, j - 1] +
5 * image[i + 1, j] + 5 * image[i + 1, j + 1])
d5 = np.square((-3) * image[i - 1, j - 1] - 3 * image[i - 1, j] - 3 * image[i - 1, j + 1] - 3
* image[i, j - 1] - 3 * image[i, j + 1] + 5 * image[i + 1, j - 1] +
5 * image[i + 1, j] + 5 * image[i + 1, j + 1])
d6 = np.square((-3) * image[i - 1, j - 1] - 3 * image[i - 1, j] - 3 * image[i - 1, j + 1] +
5 * image[i, j - 1] - 3 * image[i, j + 1] + 5 * image[i + 1, j - 1] +
5 * image[i + 1, j] - 3 * image[i + 1, j + 1])
d7 = np.square(5 * image[i - 1, j - 1] - 3 * image[i - 1, j] - 3 * image[i - 1, j + 1] +
5 * image[i, j - 1] - 3 * image[i, j + 1] + 5 * image[i + 1, j - 1] -
3 * image[i + 1, j] - 3 * image[i + 1, j + 1])
d8 = np.square(5 * image[i - 1, j - 1] + 5 * image[i - 1, j] - 3 * image[i - 1, j + 1] +
5 * image[i, j - 1] - 3 * image[i, j + 1] - 3 * image[i + 1, j - 1] -
3 * image[i + 1, j] - 3 * image[i + 1, j + 1])
# 第一种方法:取各个方向的最大值,效果并不好,采用另一种方法
list=[d1, d2, d3, d4, d5, d6, d7, d8]
kirsch[i,j]= int(np.sqrt(max(list)))
# 第二种方法:对各个方向的模长取整
#kirsch[i, j] =int(np.sqrt(d1+d2+d3+d4+d5+d6+d7+d8))
for i in range(m):
for j in range(n):
if kirsch[i,j]>127:
kirsch[i,j]=255
else:
kirsch[i,j]=0
return kirsch
# Canny边缘检测 k为高斯核大小,t1,t2为阈值大小
def Canny(image,k,t1,t2):
img = cv2.GaussianBlur(image, (k, k), 0)
canny = cv2.Canny(img, t1, t2)
return canny
# 拉普拉斯卷积核
kernel_Laplacian_1 = np.array([
[0, 1, 0],
[1, -4, 1],
[0, 1, 0]])
kernel_Laplacian_2 = np.array([
[1, 1, 1],
[1, -8, 1],
[1, 1, 1]])
#下面两个卷积核不具有旋转不变性
kernel_Laplacian_3 = np.array([
[2, -1, 2],
[-1, -4, -1],
[2, 1, 2]])
kernel_Laplacian_4 = np.array([
[-1, 2, -1],
[2, -4, 2],
[-1, 2, -1]])
# 5*5 LoG卷积模板
kernel_LoG = np.array([
[0, 0, -1, 0, 0],
[0, -1, -2, -1, 0],
[-1, -2, 16, -2, -1],
[0, -1, -2, -1, 0],
[0, 0, -1, 0, 0]])
# 卷积
output_1 = cv2.filter2D(image, -1, kernel_Prewitt_x)
output_2 = cv2.filter2D(image, -1, kernel_Sobel_x)
output_3 = cv2.filter2D(image, -1, kernel_Prewitt_x)
output_4 = cv2.filter2D(image, -1, kernel_Laplacian_1)
output_5 = Canny(image,3,50,150)
output_6 = kirsch(image)
# 显示锐化效果
image = cv2.resize(image, (800, 600))
output_1 = cv2.resize(output_1, (800, 600))
output_2 = cv2.resize(output_2, (800, 600))
output_3 = cv2.resize(output_3, (800, 600))
output_4 = cv2.resize(output_4, (800, 600))
output_5 = cv2.resize(output_5, (800, 600))
output_6 = cv2.resize(output_6, (800, 600))
cv2.imshow('Original Image', image)
cv2.imshow('sharpen_1 Image', output_1)
cv2.imshow('sharpen_2 Image', output_2)
cv2.imshow('sharpen_3 Image', output_3)
cv2.imshow('sharpen_4 Image', output_4)
cv2.imshow('sharpen_5 Image', output_5)
cv2.imshow('sharpen_6 Image', output_6)
# 停顿
if cv2.waitKey(0) & 0xFF == 27:
cv2.destroyAllWindows()
效果如图所示:
原图像
Roberts算子在x轴方向
Sobel算子在x轴方向
Prewitt算子在x轴方向
Laplacian算子在x轴方向
Canny边缘检测
kirsch边缘检测算子