OpenCV中的图像处理3.7-3.8(五)边缘检测、图像金字塔

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

目录


翻译及二次校对cvtutorials.com

编辑者廿瓶鲸和鲸社区Siby团队成员

3.7 边缘检测

目标

在本章中我们将了解到:

  • Canny边缘检测的概念
  • 用于检测的OpenCV函数cv.Canny()

理论

Canny边缘检测是一种流行的边缘检测算法。它是由John F. Canny在2006年开发的。

1.它是一个多阶段的算法我们将对每个阶段进行分析。

2.减少噪音

由于边缘检测容易受到图像中噪音的影响第一步是用5x5高斯滤波器去除图像中的噪音。我们已经在前几章中看到了这一点。

3.寻找图像的灰度梯度

然后用Sobel核对水平和垂直方向的平滑图像进行过滤得到水平方向Gx和垂直方向Gy的第一导数。从这两幅图像中我们可以找到每个像素的边缘梯度和方向如下所示。
E d g e _ G r a d i e n t    ( G ) = G x 2 + G y 2 A n g l e    ( θ ) = tan ⁡ − 1 ( G y G x ) Edge\_Gradient \; (G) = \sqrt{G_x^2 + G_y^2} \\ Angle \; (\theta) = \tan^{-1} \bigg(\frac{G_y}{G_x}\bigg) Edge_Gradient(G)=Gx2+Gy2 Angle(θ)=tan1(GxGy)

E d g e _ G r a d i e n t    ( G ) = G x 2 + G y 2 A n g l e    ( θ ) = tan ⁡ − 1 ( G y G x ) Edge\_Gradient \; (G) = \sqrt{G_x^2 + G_y^2} \\ Angle \; (\theta) = \tan^{-1} \bigg(\frac{G_y}{G_x}\bigg) Edge_Gradient(G)=Gx2+Gy2 Angle(θ)=tan1(GxGy)

梯度方向总是垂直于边缘。它被圆整为四个角度之一代表垂直、水平和两个对角线方向。

4.非极大值抑制

在得到梯度大小和方向后对图像进行全面扫描以去除任何可能不构成边缘的不必要的像素。为此在每一个像素点上检查该像素点是否是梯度方向上其附近的局部最大值。请看下面的图片。

Image Name

A点在边缘上垂直方向。梯度方向是对边缘的法线。B点和C点在梯度方向上。因此A点与B点和C点一起被检查看它是否形成一个局部最大值。如果是它将被考虑到下一阶段否则它将被抑制归为零。

简而言之你得到的结果是一个具有 "薄边缘 "的二进制图像。

5.滞后阈值处理

这个阶段决定哪些是真正的边缘哪些不是。为此我们需要两个阈值minVal和maxVal。任何灰度梯度大于maxVal的边缘都肯定是边缘而那些低于minVal的边缘肯定是非边缘所以被丢弃。那些位于这两个阈值之间的边根据它们的连接性被分为边和非边。如果它们与 "确定的边缘 "像素相连它们就被认为是边缘的一部分。否则它们也会被丢弃。请看下面的图片。

Image Name

边缘A高于maxVal所以被认为是 “确定边缘”。虽然边C低于maxVal但它与边A相连所以也被认为是有效的边我们得到了那个完整的曲线。但是边B尽管它高于minVal并且与边C在同一区域但它没有与任何 "确定的边 "相连所以它被丢弃了。因此我们必须相应地选择minVal和maxVal以获得正确的结果这一点非常重要。

这个阶段还在假设边缘是长线的基础上去除小像素的噪音。

所以我们最终得到的是图像中的强边缘。

OpenCV中的Canny边缘检测

OpenCV把上述所有的东西都放在一个函数中即cv.Canny()。我们将看到如何使用它。第一个参数是我们的输入图像。第二个和第三个参数分别是我们的minVal和maxVal。第三个参数是aperture_size。它是用于寻找图像梯度的Sobel核的大小。最后一个参数是L2gradient它指定了用于寻找梯度大小的方程式。如果它是True它使用上面提到的更精确的方程否则它使用这个函数。Edge_Gradient(G)=|Gx|+|Gy|。默认情况下它是False。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg',0)
edges = cv.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()

结果如下

Image Name

其他资源

1.维基百科上的Canny边缘检测器
2.《Canny边缘检测教程》作者Bill Green2002年。

练习

写一个小程序来寻找Canny边缘检测其阈值可以用两个trackbar来改变。这样你就可以了解阈值的影响。

3.8 图像金字塔

目标

在本章中

  • 我们将学习图像金字塔的知识
  • 我们将使用图像金字塔来创建一个新的水果“Orapple”。
  • 我们将看到这些函数cv.pyrUp(), cv.pyrDown()

理论

通常情况下我们习惯于使用一个恒定大小的图像。但在某些情况下我们需要处理不同分辨率的相同图像。例如当我们在图像中搜索某个东西时比如人脸我们不确定该物体会以何种尺寸出现在所述图像中。在这种情况下我们需要创建一组具有不同分辨率的相同图像并在所有这些图像中搜索物体。这些具有不同分辨率的图像集被称为图像金字塔因为当它们被保存在一个堆栈中最高分辨率的图像在底部最低分辨率的图像在顶部它看起来像一个金字塔。

有两种图像金字塔。1高斯金字塔和2拉普拉斯金字塔

高斯金字塔中的高层次低分辨率是通过去除低层次高分辨率图像中的连续行和列而形成的。然后高层的每个像素由底层的5个像素贡献高斯权重形成。通过这样做M×N的图像变成了M/2×N/2的图像。因此面积减少到原始面积的四分之一。这就是所谓的Octave。当我们在金字塔中往上走时同样的模式会继续下去即分辨率下降。同样地在扩展时每一级的面积都会变成4倍。我们可以使用cv.pyrDown()和cv.pyrUp()函数找到高斯金字塔。

img = cv.imread('messi5.jpg')
lower_reso = cv.pyrDown(higher_reso)

下面是图像金字塔中的4个层次。

Image Name

现在你可以用cv.pyrUp()函数往下看图像金字塔。

higher_reso2 = cv.pyrUp(lower_reso)

记住higher_reso2不等于higher_reso因为一旦你降低分辨率你就失去了信息。下面的图片是在前面的情况下从最小的图片创建的金字塔的3级。将其与原始图像进行比较。

Image Name

拉普拉斯金字塔是由高斯金字塔形成的。这方面没有专属函数。拉普拉斯金字塔图像只像边缘图像。它的大部分元素都是零。它们被用于图像压缩。拉普拉斯金字塔中的一个层次是由高斯金字塔中该层次与高斯金字塔中其上层的扩展版本之间的差异形成的。一个拉普拉斯金字塔的三个层次看起来如下对比度被调整以增强内容。

Image Name

使用金字塔进行图像混合

金字塔的一个应用是图像混合。例如在图像拼接中你需要将两幅图像堆叠在一起但由于图像之间的不连续性可能看起来不好看。在这种情况下用Pyramids进行图像混合可以让你实现无缝混合而不会在图像中留下很多数据。一个经典的例子是两个水果的混合橙子和苹果的混合。请看现在的结果本身以理解我所说的内容。

Image Name

请查看附加资源中的第一个参考资料它有关于图像混合、拉普拉斯金字塔等的完整图示细节。简单地说它是这样做的:

1.加载苹果和橙子的两张图片
2.找到苹果和橙子的高斯金字塔在这个特定的例子中级别数为6。
3.从高斯金字塔中找到它们的拉普拉斯金字塔
4.现在将苹果的左半边和橙子的右半边分别加入到拉普拉斯金字塔的各个层次中。
5.最后从这个联合图像金字塔中重建原始图像。

下面是完整的代码。(为了简单起见每个步骤都是单独完成的这可能会占用更多的内存。如果你愿意的话你可以优化它

import cv2 as cv
import numpy as np,sys
A = cv.imread('apple.jpg')
B = cv.imread('orange.jpg')
# 为A生成高斯金字塔
G = A.copy()
gpA = [G]
for i in range(6):
    G = cv.pyrDown(G)
    gpA.append(G)
# 为B生成高斯金字塔
G = B.copy()
gpB = [G]
for i in range(6):
    G = cv.pyrDown(G)
    gpB.append(G)
# 为A生成拉普拉斯金字塔
lpA = [gpA[5]]for i in range(5,0,-1):
    GE = cv.pyrUp(gpA[i])
    L = cv.subtract(gpA[i-1],GE)
    lpA.append(L)
# 为B生成拉普拉斯金字塔
lpB = [gpB[5]]
for i in range(5,0,-1):
    GE = cv.pyrUp(gpB[i])
    L = cv.subtract(gpB[i-1],GE)
    lpB.append(L)
# 现在在每个层次中添加左右两半的图像
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)
# 现在重构
ls_ = LS[0]
for i in range(1,6):
    ls_ = cv.pyrUp(ls_)
    ls_ = cv.add(ls_, LS[i])
# 将图像的两半拼接在一起
real = np.hstack((A[:,:cols/2],B[:, cols/2:])
cv.imwrite('Pyramid_blending2.jpg',ls_)
cv.imwrite('Direct_blending.jpg',real)

其他资源

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6