目标
• 了解 Canny 边缘检测的概念
• 学习函数 cv2.Canny()算法
19.1 原理
Canny 边缘检测是一种很是流行的边缘检测算法,是 John F.Canny 在1986 年提出的。它是一个有不少步构成的算法,咱们接下来会逐步介绍。小程序
19.1.1 噪声去除
因为边缘检测很容易受到噪声影响,因此第一步是使用 5x5 的高斯滤波器去除噪声,这个前面咱们已经学过了。app
19.1.2 计算图像梯度
对平滑后的图像使用 Sobel 算子计算水平方向和竖直方向的一阶导数(图像梯度)(Gx 和 Gy)。根据获得的这两幅梯度图(Gx 和 Gy)找到边界的梯度和方向,公式以下:
梯度的方向通常老是与边界垂直。梯度方向被归为四类:垂直,水平,和两个对角线。函数
19.1.3 非极大值抑制
在得到梯度的方向和大小以后,应该对整幅图像作一个扫描,去除那些非边界上的点。对每个像素进行检查,看这个点的梯度是否是周围具备相同梯度方向的点中最大的。以下图所示:
post
如今你获得的是一个包含“窄边界”的二值图像。学习
19.1.4 滞后阈值
如今要肯定那些边界才是真正的边界。这时咱们须要设置两个阈值:minVal 和 maxVal。当图像的灰度梯度高于 maxVal 时被认为是真的边界,那些低于 minVal 的边界会被抛弃。若是介于二者之间的话,就要看这个点是否与某个被肯定为真正的边界点相连,若是是就认为它也是边界点,若是不是就抛弃。以下图:优化
A 高于阈值 maxVal 因此是真正的边界点,C 虽然低于 maxVal 但高于minVal 而且与 A 相连,因此也被认为是真正的边界点。而 B 就会被抛弃,由于他不只低于 maxVal 并且不与真正的边界点相连。因此选择合适的 maxVal和 minVal 对于可否获得好的结果很是重要。
在这一步一些小的噪声点也会被除去,由于咱们假设边界都是一些长的线段。spa
19.2 OpenCV 中的 Canny 边界检测
在 OpenCV 中只须要一个函数:cv2.Canny(),就能够完成以上几步。
让咱们看如何使用这个函数。这个函数的第一个参数是输入图像。第二和第三个分别是 minVal 和 maxVal。第三个参数设置用来计算图像梯度的 Sobel卷积核的大小,默认值为 3。最后一个参数是 L2gradient,它能够用来设定求梯度大小的方程。若是设为 True,就会使用咱们上面提到过的方程,不然使用方程:. 代替,默认值为 False。code
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('messi5.jpg',0) edges = cv2.Canny(img,100,200) plt.subplot(121),plt.imshow(img,cmap = 'gray') plt.title('Original Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(edges,cmap = 'gray') plt.title('Edge Image'), plt.xticks([]), plt.yticks([]) plt.show()
结果:
个人结果
官方结果
更多资源
1. Canny edge detector at Wikipedia
2. Canny Edge Detection Tutorial by Bill Green, 2002.
练习
1. 写一个小程序,能够经过调节滑动条来设置阈值 minVal 和 maxVal 进而来进行 Canny 边界检测。这样你就会理解阈值的重要性了。
目标
• 学习图像金字塔
• 使用图像建立一个新水果:“橘子苹果”
• 将要学习的函数有:cv2.pyrUp(),cv2.pyrDown()。
20.1 原理
通常状况下,咱们要处理是一副具备固定分辨率的图像。可是有些状况下,咱们须要对同一图像的不一样分辨率的子图像进行处理。好比,咱们要在一幅图像中查找某个目标,好比脸,咱们不知道目标在图像中的尺寸大小。这种状况下,咱们须要建立建立一组图像,这些图像是具备不一样分辨率的原始图像。咱们把这组图像叫作图像金字塔(简单来讲就是同一图像的不一样分辨率的子图集合)。若是咱们把最大的图像放在底部,最小的放在顶部,看起来像一座金字塔,故而得名图像金字塔。
有两类图像金字塔:高斯金字塔和拉普拉斯金字塔。
高斯金字塔的顶部是经过将底部图像中的连续的行和列去除获得的。顶部图像中的每一个像素值等于下一层图像中 5 个像素的高斯加权平均值。这样操做一次一个 MxN 的图像就变成了一个 M/2xN/2 的图像。因此这幅图像的面积就变为原来图像面积的四分之一。这被称为 Octave。连续进行这样的操做咱们就会获得一个分辨率不断降低的图像金字塔。咱们可使用函数cv2.pyrDown() 和 cv2.pyrUp() 构建图像金字塔。
函数 cv2.pyrDown() 从一个高分辨率大尺寸的图像向上构建一个金子塔(尺寸变小,分辨率下降)。
img = cv2.imread('messi5.jpg') lower_reso = cv2.pyrDown(higher_reso)
下图是一个四层的图像金字塔。
函数 cv2.pyrUp() 从一个低分辨率小尺寸的图像向下构建一个金子塔(尺寸变大,但分辨率不会增长)。
higher_reso2 = cv2.pyrUp(lower_reso)
你要记住的是是 higher_reso2 和 higher_reso 是不一样的。由于一旦使用 cv2.pyrDown(),图像的分辨率就会下降,信息就会被丢失。下图就是从 cv2.pyrDown() 产生的图像金字塔的(由下到上)第三层图像使用函数cv2.pyrUp() 获得的图像,与原图像相比分辨率差了不少。
拉普拉斯金字塔能够有高斯金字塔计算得来,公式以下:
拉普拉金字塔的图像看起来就像边界图,其中不少像素都是 0。他们常常被用在图像压缩中。下图就是一个三层的拉普拉斯金字塔:
20.2 使用金字塔进行图像融合
图像金字塔的一个应用是图像融合。例如,在图像缝合中,你须要将两幅图叠在一块儿,可是因为链接区域图像像素的不连续性,整幅图的效果看起来会不好。这时图像金字塔就能够排上用场了,他能够帮你实现无缝链接。这里的一个经典案例就是将两个水果融合成一个,看看下图也许你就明白我在讲什么了。
你能够经过阅读后边的更多资源来了解更多关于图像融合,拉普拉斯金字塔的细节。
实现上述效果的步骤以下:
1. 读入两幅图像,苹果和句子
2. 构建苹果和橘子的高斯金字塔(6 层)
3. 根据高斯金字塔计算拉普拉斯金字塔
4. 在拉普拉斯的每一层进行图像融合(苹果的左边与橘子的右边融合)
5. 根据融合后的图像金字塔重建原始图像。
下图是摘自《学习 OpenCV》展现了金子塔的构建,以及如何从金字塔重建原始图像的过程。
整个过程的代码以下。(为了简单,每一步都是独立完成的,这回消耗更多、的内存,若是你愿意的话能够对他进行优化)
import cv2 import numpy as np,sys A = cv2.imread('apple.jpg') B = cv2.imread('orange.jpg') # generate Gaussian pyramid for A G = A.copy() gpA = [G] for i in xrange(6): G = cv2.pyrDown(G) gpA.append(G) # generate Gaussian pyramid for B G = B.copy() gpB = [G] for i in xrange(6): G = cv2.pyrDown(G) gpB.append(G) # generate Laplacian Pyramid for A lpA = [gpA[5]] for i in xrange(5,0,-1): GE = cv2.pyrUp(gpA[i]) L = cv2.subtract(gpA[i-1],GE) lpA.append(L) # generate Laplacian Pyramid for B lpB = [gpB[5]] for i in xrange(5,0,-1): GE = cv2.pyrUp(gpB[i]) L = cv2.subtract(gpB[i-1],GE) lpB.append(L) # Now add left and right halves of images in each level LS = [] for la,lb in zip(lpA,lpB): rows,cols,dpt = la.shape ls = np.hstack((la[:,0:cols/2], lb[:,cols/2:])) LS.append(ls) # now reconstruct ls_ = LS[0] for i in xrange(1,6): ls_ = cv2.pyrUp(ls_) ls_ = cv2.add(ls_, LS[i]) # image with direct connecting each half real = np.hstack((A[:,:cols/2],B[:,cols/2:])) cv2.imwrite('Pyramid_blending2.jpg',ls_) cv2.imwrite('Direct_blending.jpg',real)