通过物体的边缘检测,我们可以完成很多的图像识别任务。科学研究发现,图像边缘检测的原理是检测出图像中所有灰度值变化较大的点,而且这些点连接起来就构成了若干线条,这些线条就可以称为图像的边缘。常见的边缘检测算法包括Soble边缘检测,拉普拉斯边缘检测和Canny边缘检测。
Sobel边缘检测
图像的彩色信息在进行边缘检测时通常是多余的,因此可以在进行边缘检测前先把彩色图像转换成灰度图像。直观的感觉,灰度图像中的边缘一定是变化相对比较剧烈的区域,那么这个区域灰度值的一阶导数便是取极大值或者极小值。Sobel边缘检测正是基于这一原理,在OpenCV中的函数定义如下:
Sobel(src,ddepth, dx, dy, ksize=None, scale=None, delta=None, borderType=None)
其中主要的参数如下:
- ddepth: 深度类型,通常用cv2.CV_64F表示64位浮点数即64 float。
- dx:x方向的导数,1表示取导,0表示不取导。
- dy: y方向的导数,1表示取导,0表示不取导。
- ksize: 卷积核的大小。
# 加载需要的库文件
import cv2
from matplotlib import pyplot as plt
import numpy as np
# 加载图片,并转换成灰度图像
img = cv2.imread("./pig.png")
gray_img = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
# Sobel边缘检测
sobel = cv2.Sobel(gray_img,cv2.CV_64F,0,1)
# 为了显示方便,对图像进行反转,黑盒和白色会颠倒
notsobel = cv2.bitwise_not(sobel)
# 展示效果
plt.subplot(131)
plt.imshow(gray_img,cmap=plt.cm.gray)
plt.title("(A)")
plt.subplot(132)
plt.imshow(sobel,cmap=plt.cm.gray)
plt.title("(B)")
plt.subplot(133)
plt.imshow(notsobel,cmap=plt.cm.gray)
plt.title("(C)")
plt.tight_layout()
Sobel边缘检测,区分不同的方向
import cv2
from matplotlib import pyplot as plt
import numpy as np
img = cv2.imread('pig.png')
gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
# Sobel边缘检测, dy=1 表示在y轴方向上进行边缘检测。
# 对y轴方向上取导,仅在y轴方向的边缘会被忽略。
sobel01 = cv2.Sobel(gray_img, cv2.CV_64F, 0, 1)
# 对二值图像进行反转 黑白颠倒
sebel01 = cv2.bitwise_not(sobel01)
# Sobel边缘检测,dx = 1 表示在x轴方向上进行边缘检测。
# 对x轴方向取导,仅在x轴方向的边缘会被忽略。
sobel10 = cv2.Sobel(gray_img, cv2.CV_64F, 1, 0)
# 对二值图像进行反转,黑白颠倒
sobel10 = cv2.bitwise_not(sobel10)
# Sobel边缘检测,dx = 1, dy = 1
sobel11 = cv2.Sobel(gray_img, cv2.CV_64F, 1, 1)
sobel11 = cv2.bitwise_not(sobel11)
plt.subplot(221)
plt.imshow(gray_img,cmap=plt.cm.gray)
plt.title("(A)")
plt.subplot(222)
plt.imshow(sobel01,cmap=plt.cm.gray)
plt.title("(B)")
plt.subplot(223)
plt.imshow(sobel10, cmap=plt.cm.gray)
plt.title("(C)")
plt.subplot(224)
plt.imshow(sobel11, cmap=plt.cm.gray)
plt.title("(D)")
plt.tight_layout()
拉普拉斯边缘检测
基于一阶导数的Sobel效果并不理想,人们在其基础上提出了取对应的二阶导数为0的点为边缘。拉普拉斯边缘检测正是利用了这一原理。
拉普拉斯在OpenCV中的函数定义如下:
Laplacian(src, ddepth, ksize=None, scale=None, delta=None, borderType=None)
其中主要的参数为:
- ddepth: 深度类型。
- ksize: 卷积核大小。
img = cv2.imread('pig.png')
gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
# 拉普拉斯边缘检测
lap = cv2.Laplacian(gray_img, cv2.CV_64F) # 拉普拉斯边缘检测
lap = np.uint8(np.absolute(lap)) # 对lap去绝对值
# 对二值图像进行反转,黑白颠倒
lap = cv2.bitwise_not(lap)
# Sobel边缘检测
sobel = cv2.Sobel(gray_img, cv2.CV_64F, 0, 1)
# 对二值图像进行反转,黑白颠倒
sobel = cv2.bitwise_not(sobel)
# 展示效果
plt.subplot(131)
plt.imshow(gray_img,cmap=plt.cm.gray)
plt.title("(A)")
plt.subplot(132)
plt.imshow(lap, cmap=plt.cm.gray)
plt.title("(B)")
plt.subplot(133)
plt.imshow(sobel, cmap=plt.cm.gray)
plt.title("(C)")
plt.tight_layout()
(A)图像表示原图,(B)图使用了拉普拉斯边缘检测,(C)图像使用了Sobel边缘检测。相对于Sobel,拉普拉斯对边缘的识别效果更好,但是拉普拉斯对噪声敏感,会产生双边效果,不能检测出边的方向,通常不直接用于边的检测,只起辅助的角色。
Canny边缘检测
Canny边缘检测是使用最广泛的边缘检测算法之一,可以有效减少噪音的影响,同时边缘处理的效果也不错。
Canny边缘检测算法可以分为以下5个步骤:
1) 使用高斯滤波器,以平滑图像,滤除噪声。
2) 计算图像中每个像素点的梯度强度和方向。
3) 应用非极大值抑制,以消除边缘检测带来的杂散响应。
4) 应用双阈值检测来确定真实的和潜在的边缘。
5) 通过抑制孤立的弱边缘最终完成边缘检测。
Canny算法应用双阈值的技术,即设定一个阈值上界和阈值下界,图像中的像素点如果大于阈值上界则认为必然是边缘,小于阈值下界则认为必然不是边缘,两者之间的则认为是候选项,需进一步处理。
Canny边缘检测在OpenCV中的函数定义如下:
Canny(image, threshold1, threshold2, edges=None, apertureSize=None, L2gradient=None)
其中主要的参数如下:
- threshold1: 阈值1,即低阈值。
- threshold2: 阈值2,即高阈值。
低阈值用来控制边缘连接,高阈值用来控制强边缘的初始分割,低于低阈值的会被判定为不是边缘,高于高阈值的会被判定为边缘,中间区间的会通过计算判断是否为边缘。
import cv2
import matplotlib.pyplot as plt
import numpy as np
import skimage
img = cv2.imread('pig.png')
img = cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY)
# 高斯噪声
noise_img = skimage.util.random_noise(img.copy(), mode='gaussian',
seed=None, clip=True, mean=0, var=0.0016)
noise_img = np.uint8(noise_img*255)
# 拉普拉斯边缘检测
lap = cv2.Laplacian(noise_img, cv2.CV_64F) # 拉普拉斯边缘检测
lap = np.uint8(np.absolute(lap)) # 对lap去绝对值
# 对二值图像进行反转,黑白颠倒
lap = cv2.bitwise_not(lap)
# Canny边缘检测
canny = cv2.Canny(noise_img, 150, 300)
# 对二值图像进行反转,黑白颠倒
canny = cv2.bitwise_not(canny)
# 展示图像
plt.subplot(221)
plt.imshow(img, cmap=plt.cm.gray)
plt.title("(A)")
plt.subplot(222)
plt.imshow(noise_img,cmap=plt.cm.gray)
plt.title("(B)")
plt.subplot(223)
plt.imshow(lap, cmap=plt.cm.gray)
plt.title("(C)")
plt.subplot(224)
plt.imshow(canny, cmap=plt.cm.gray)
plt.title("(D)")
plt.tight_layout()
(A)图像是原图像,(B)图像是叠加的高斯噪声图像,(C)图像是对混有高斯噪声的图像进行拉普拉斯边缘检测,(D)图像是对混有高斯噪声的图像进行Canny边缘检测。由上图可以看出,拉普拉斯边缘检测容易受到噪声干扰,Canny边缘检测对噪声具有一定的抵御能力。
Roberts 边缘检测算子
Roberts边缘检测算子根据任意一对互相垂直方向上的差分可用来计算梯度的原理,采用对角线方向相邻两像素之差,即:
然后根据式(1.9.2)计算出Roberts的梯度幅度值:
它们的卷积算子为:
Roberts检测器较为简单,但具有一些功能上的限制,例如,它是非对称的,而且不能检测诸如45°倍数的边缘。然而,它还是经常用于硬件实现中,因为它既简单又快速。
import cv2
import matplotlib.pyplot as plt
import numpy as np
#读取图像
image = cv2.imread('E:/pythonProject/12.png')
lena = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
#灰度转化处理
grayImage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#roberts算子
kernelx = np.array([[-1, 0], [0, 1]], dtype= int)
kernely = np.array([[0, -1], [1, 0]], dtype= int)
x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
#转uint8
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
#加权和
Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
#显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
#图像显示
titles = [u'原始图像', u'Robertes图像']
images = [lena, Roberts]
for i in range(2):
plt.subplot(1, 2, i+1), plt.imshow(images[i], 'gray')
plt.xticks([]), plt.yticks([])
plt.title(titles[i])
plt.show()
Prewitt 边缘检测算子
Prewitt 边缘检测算子模板如下:
图像中的每个像素都用这两个核作卷积,一个核对垂直边缘影响最大,另一个核对水平边缘影响最大。两个卷积的绝对值的最大值作为该点的输出值。不能简单的将小于0的值处理为0,这样会丢失信息。它比Sobel检测器在计算上要简单一些,但比较容易产生一些噪声。
import cv2
import numpy as np
import matplotlib.pyplot as plt
#输入图像
image = cv2.imread('E:/pythonProject/12.png')
lena = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
#灰度转化处理
grayImage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#kernel
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(grayImage, cv2.CV_16S, kernelX)
y = cv2.filter2D(grayImage, cv2.CV_16S, kernelY)
#转uint8
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
#加权和
Prewitt = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
#图象显示
plt.rcParams['font.sans-serif'] = ['SimHei']
titles = [u'原始图像', u'Prewitt图像']
images = [lena, Prewitt]
for i in range(2):
plt.subplot(1, 2, i+1), plt.imshow(images[i], 'gray')
plt.xticks([]), plt.yticks([])
plt.title(titles[i])
plt.show()
原文链接:https://blog.csdn.net/qq_44947220/article/details/112888538