[OpenCV-Python] OpenCV 中图像特征提取与描述 部分 V (一)

部分 V
图像特征提取与描述

 OpenCV-Python 中文教程(搬运)目录

 

29 理解图像特征


目标
本节我会试着帮你理解什么是图像特征,为什么图像特征很重要,为什么角点很重要等。
29.1 解释
  我相信你们大多数人都玩过拼图游戏吧。首先你们拿到一张图片的一堆碎片,要做的就是把这些碎片以正确的方式排列起来从而重建这幅图像。问题是,你怎样做到的呢?如果把你做游戏的原理写成计算机程序,那计算机就也会玩拼图游戏了。如果计算机可以玩拼图,我们就可以给计算机一大堆自然图片,然后就可以让计算机把它拼成一张大图了。如果计算机可以自动拼接自然图片,那我们是不是可以给计算机关于一个建筑的的大量图片,然后让计算机给我们创建一个 3D 的的模型呢?
问题和联想可以无边无际。但是所有的这些问题都是建立在一个基础问题之上的。这个问题就是:我们是如何玩拼图的?我们是如何把一堆碎片拼在一起的?我们有时如何把一个个自然场景拼接成一个单独图像的?
答案就是:我们要寻找一些唯一的特征,这些特征要适于被跟踪,容易被比较。如果我们要定义这样一种特征,虽然我们知道它是什么但很难用语言来描述。如果让你找出一个可以在不同图片之间相互比较的好的特征,你肯定能搞定。这就是为什么小孩子也会玩拼图的原因。我们在一副图像中搜索这样的特征,我们能找到它们,而且也能在其他图像中找到这些特征,然后再把它们拼接到一块。(在拼图游戏中,我们更注重的是图片之间的连续性)。我们的这些能力都是天生的。
所以我们的一个问题现在扩展成了几个,但是更加确切了。这些特征是什么呢?(我们的答案必须也能被计算机理解)。
好吧,很难说人是怎样找出这些特征的。这些能力已经刻在我们的大脑中了。但是如果我们深入的观察一些图像并搜索不同的 pattern,我们会发现一些有趣的事。一下图为例:

    Understanding features

图像很简单。在图像的上方给出了六个小图。你要做的就是找到这些小图在原始图像中的位置。你能找到多少正确结果呢?
A 和 B 是平面,而且它们的图像中很多地方都存在。很难找到这些小图的准确位置。
C 和 D 更简单。它们是建筑的边缘。你可以找到它们的近似位置,但是准确位置还是很难找到。这是因为:沿着边缘,所有的地方都一样。所以边缘是比平面更好的特征,但是还不够好(在拼图游戏中要找连续的边缘)。
最后 E 和 F 是建筑的一些角点。它们能很容易的被找到。因为在角点的地方,无论你向哪个方向移动小图,结果都会有很大的不同。所以可以把它们当成一个好的特征。为了更好的理解这个概念我们举个更简单的例子。

    Features
如上图所示,蓝色框中的区域是一个平面很难被找到和跟踪。无论你向那个方向移动蓝色框,长的都一样。对于黑色框中的区域,它是一个边缘。如果你沿垂直方向移动,它会改变。但是如果沿水平方向移动就不会改变。而红色框中的角点,无论你向那个方向移动,得到的结果都不同,这说明它是唯一的。

所以,基本上来说角点是一个好的图像特征。(不仅仅是角点,有些情况斑点也是好的图像特征)。
现在我们终于回答了前面的问题了,“这些特征是什么?”。但是下一个问题又来了。我们怎样找到它们?或者说我们怎样找到角点?我们也已经用一种直观的方式做了回答,比如在图像中找一些区域,无论你想那个方向移动这些区域变化都很大。在下一节中我们会用计算机语言来实现这个想法。所以找到图像特征的技术被称为 特征检测。
现在我们找到了图像特征(假设你已经搞定)。在找到这些之后,你应该在其他图像中也找到同样的特征。我们应该怎么做呢?我们选择特征周围的一个区域,然后用我们自己的语言来描述它,比如“上边是蓝天,下边是建筑,在建筑上有很多玻璃等”,你就可以在其他图片中搜索相同的区域了。基本上看来,你是在描述特征。同样,计算机也要对特征周围的区域进行描述,这样它才能在其他图像中找到相同的特征。我们把这种描述称为 特征描述。当你有了特征很它们的描述后,你就可以在所有的图像中找这个相同的特征了,找到之后你就可以做任何你想做的了。
本章我们就是要使用 OpenCV 中的各种算法来查找图像的特征,然后描述它们,对它们进行匹配等。
更多资源
练习


30 Harris 角点检测


目标
  • 理解 Harris 角点检测的概念
  • 学习函数:cv2.cornerHarris(),cv2.cornerSubPix()
原理
  在上一节我们已经知道了角点的一个特性:向任何方向移动变化都很大。Chris_Harris 和 Mike_Stephens 早在 1988 年的文章《A CombinedCorner and Edge Detector》中就已经提出了焦点检测的方法,被称为Harris 角点检测。他把这个简单的想法转换成了数学形式。将窗口向各个方向移动(u,v)然后计算所有差异的总和。表达式如下:

      E(u,v) = \sum_{x,y} \underbrace{w(x,y)}_\text{window function} \, [\underbrace{I(x+u,y+v)}_\text{shifted intensity}-\underbrace{I(x,y)}_\text{intensity}]^2
窗口函数可以是正常的矩形窗口也可以是对每一个像素给予不同权重的高斯窗口
角点检测中要使 E (µ,ν) 的值最大。这就是说必须使方程右侧的第二项的取值最大。对上面的等式进行泰勒级数展开然后再通过几步数学换算(可以参考其他标准教材),我们得到下面的等式:

      E(u,v) \approx \begin{bmatrix} u & v \end{bmatrix} M \begin{bmatrix} u \\ v \end{bmatrix}
其中

      M = \sum_{x,y} w(x,y) \begin{bmatrix}I_x I_x & I_x I_y \\
                                     I_x I_y & I_y I_y \end{bmatrix}
这里 I x 和 I y 是图像在 x 和 y 方向的导数。(可以使用函数 cv2.Sobel()
计算得到)。
然后就是主要部分了。他们根据一个用来判定窗口内是否包含角点的等式进行打分。

      R = det(M) - k(trace(M))^2
其中

  • det(M) = \lambda_1 \lambda_2
  • trace(M) = \lambda_1 + \lambda_2

  • λ 1 和 λ 2 是矩阵 M 的特征值所以根据这些特征中我们可以判断一个区域是否是角点,边界或者是平面。
  • 当 λ 1 和 λ 2 都小时,|R| 也小,这个区域就是一个平坦区域。
  • 当 λ 1 ≫ λ 2 或者 λ 1 ≪ λ 2 ,时 R 小于 0,这个区域是边缘
  • 当 λ 1 和 λ 2 都很大,并且 λ 1 ~λ 2 中的时,R 也很大,(λ 1 和 λ 2 中的最小值都大于阈值)说明这个区域是角点。
可以用下图来表示我们的结论:

    Classification of Image Points
所以 Harris 角点检测的结果是一个由角点分数构成的灰度图像。选取适当的阈值对结果图像进行二值化我们就检测到了图像中的角点。我们将用一个简单的图片来演示一下。


30.1 OpenCV 中的 Harris 角点检测
 Open 中的函数 cv2.cornerHarris() 可以用来进行角点检测。参数如下:
  • img - 数据类型为 float32 的输入图像。
  • blockSize - 角点检测中要考虑的领域大小。
  • ksize - Sobel 求导中使用的窗口大小
  • k - Harris 角点检测方程中的自由参数,取值参数为 [0,04,0.06].
例子如下:

import cv2
import numpy as np

filename = 'chessboard.jpg'
img = cv2.imread(filename)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

gray = np.float32(gray)
dst = cv2.cornerHarris(gray,2,3,0.04)

#result is dilated for marking the corners, not important
dst = cv2.dilate(dst,None)

# Threshold for an optimal value, it may vary depending on the image.
img[dst>0.01*dst.max()]=[0,0,255]

cv2.imshow('dst',img)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

结果如下:

    Harris Corner Detection

30.2 亚像素级精确度的角点
  有时我们需要最大精度的角点检测。OpenCV 为我们提供了函数 cv2.cornerSubPix(),它可以提供亚像素级别的角点检测。下面是一个例子。首先我们要找到 Harris角点,然后将角点的重心传给这个函数进行修正。Harris 角点用红色像素标出,绿色像素是修正后的像素。在使用这个函数是我们要定义一个迭代停止条件。当迭代次数达到或者精度条件满足后迭代就会停止。我们同样需要定义进行角点搜索的邻域大小。

import cv2
import numpy as np

filename = 'chessboard2.jpg'
img = cv2.imread(filename)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# find Harris corners
gray = np.float32(gray)
dst = cv2.cornerHarris(gray,2,3,0.04)
dst = cv2.dilate(dst,None)
ret, dst = cv2.threshold(dst,0.01*dst.max(),255,0)
dst = np.uint8(dst)

# find centroids
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)

# define the criteria to stop and refine the corners
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
corners = cv2.cornerSubPix(gray,np.float32(centroids),(5,5),(-1,-1),criteria)

# Now draw them
res = np.hstack((centroids,corners))
res = np.int0(res)
img[res[:,1],res[:,0]]=[0,0,255]
img[res[:,3],res[:,2]] = [0,255,0]

cv2.imwrite('subpixel5.png',img)

结果如下,为了方便查看我们对角点的部分进行了放大:

    Corner Detection with SubPixel Accuracy


31 Shi-Tomasi 角点检测 & 适合于跟踪的图像特征


目标
 本节我们将要学习:
  • 另外一个角点检测技术:Shi-Tomasi 焦点检测
  • 函数:cv2.goodFeatureToTrack()
原理
  上一节我们学习了 Harris 角点检测,后来 1994 年,J.Shi 和 C.Tomasi在他们的文章《Good_Features_to_Track》中对这个算法做了一个小小的修改,并得到了更好的结果。我们知道 Harris 角点检测的打分公式为:
      R = \lambda_1 \lambda_2 - k(\lambda_1+\lambda_2)^2
但 Shi-Tomasi 使用的打分函数为:
      R = min(\lambda_1, \lambda_2)
如果打分超过阈值,我们就认为它是一个角点。我们可以把它绘制到 λ 1 ~λ 2 空间中,就会得到下图:

    Shi-Tomasi Corner Space
从这幅图中,我们可以看出来只有当 λ 1 和 λ 2 都大于最小值时,才被认为是角点(绿色区域)。


31.1 代码
  OpenCV 提供了函数:cv2.goodFeaturesToTrack()。这个函数可以帮我们使用 Shi-Tomasi 方法获取图像中 N 个最好的角点(如果你愿意的话,187
www.linuxidc.com
也可以通过改变参数来使用 Harris 角点检测算法)。通常情况下,输入的应该是灰度图像。然后确定你想要检测到的角点数目。再设置角点的质量水平,0到 1 之间。它代表了角点的最低质量,低于这个数的所有角点都会被忽略。最后在设置两个角点之间的最短欧式距离。
根据这些信息,函数就能在图像上找到角点。所有低于质量水平的角点都会被忽略。然后再把合格角点按角点质量进行降序排列。函数会采用角点质量最高的那个角点(排序后的第一个),然后将它附近(最小距离之内)的角点都删掉。按着这样的方式最后返回 N 个最佳角点。
在下面的例子中,我们试着找出 25 个最佳角点:

import numpy as np
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('simple.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

corners = cv2.goodFeaturesToTrack(gray,25,0.01,10)
corners = np.int0(corners)

for i in corners:
    x,y = i.ravel()
    cv2.circle(img,(x,y),3,255,-1)

plt.imshow(img),plt.show()

结果如下:
    Shi-Tomasi Corners
我们以后会发现这个函数很适合在目标跟踪中使用。


32 介绍 SIFT(Scale-Invariant Feature Trans-form)


目标
  • 学习 SIFT 算法的概念
  • 学习在图像中查找 SIFT 关键点和描述符
原理
  在前面两节我们学习了一些角点检测技术,比如 Harris 等。它们具有旋转不变特性,即使图片发生了旋转,我们也能找到同样的角点。很明显即使图像发生旋转之后角点还是角点。那如果我们对图像进行缩放呢?角点可能就不再是角点了。以下图为例,在一副小图中使用一个小的窗口可以检测到一个角点,但是如果图像被放大,再使用同样的窗口就检测不到角点了。

    Scale-Invariance
所以在 2004 年,D.Lowe 提出了一个新的算法:尺度不变特征变换(SIFT),这个算法可以帮助我们提取图像中的关键点并计算它们的描述符。
SIFT 算法主要由四步构成。我们来逐步进行学习。
尺度空间极值检测
  从上图我们可以很明显的看出来在不同的尺度空间不能使用相同的窗口检测极值点。对小的角点要用小的窗口,对大的角点只能使用大的窗口。为了达到这个目的我们要使用尺度空间滤波器。(尺度空间滤波器可以使用一些列具有不同方差 σ 的高斯卷积核构成)。使用具有不同方差值 σ 的高斯拉普拉斯算子(LoG)对图像进行卷积,LoG 由于具有不同的方差值 σ 所以可以用来检测不同大小的斑点(当 LoG 的方差 σ 与斑点直径相等时能够使斑点完全平滑)。简单来说方差 σ 就是一个尺度变换因子。例如,上图中使用一个小方差 σ 的高斯卷积核是可以很好的检测出小的角点,而使用大方差 σ 的高斯卷积核时可以很好的检测除大的角点。所以我们可以在尺度空间和二维平面中检测到局部最大值,如(x,y,σ), 这表示在 σ 尺度中(x,y)点可能是一个关键点。(高斯方差的大小与窗口的大小存在一个倍数关系:窗口大小等于 6 倍方差加 1,所以方差的大小也决定了窗口大小)但是这个 LoG 的计算量非常大,所以 SIFT 算法使用高斯差分算子(DoG)来对 LoG 做近似。这里需要再解释一下图像金字塔,我们可以通过减少采样(如只取奇数行或奇数列)来构成一组图像尺寸(1,0.5,0.25 等)不同的金字塔,然后对这一组图像中的每一张图像使用具有不同方差 σ 的高斯卷积核构建出具有不同分辨率的图像金字塔(不同的尺度空间)。DoG 就是这组具有不同分辨率的图像金字塔中相邻的两层之间的差值。如下图所示:

    Difference of Gaussian


在 DoG 搞定之后,就可以在不同的尺度空间和 2D 平面中搜索局部最大值了。对于图像中的一个像素点而言,它需要与自己周围的 8 邻域,以及尺度空间中上下两层中的相邻的 18(2x9)个点相比。如果是局部最大值,它就可能是一个关键点。基本上来说关键点是图像在相应尺度空间中的最好代表。如下图所示:

    Difference of Gaussian
该算法的作者在文章中给出了 SIFT 参数的经验值:octaves=4(通过降低采样从而减小图像尺寸,构成尺寸减小的图像金字塔(4 层)?),尺度空间为 5,也就是每个尺寸使用 5 个不同方差的高斯核进行卷积,初始方差是 1.6,\sigma=1.6k=\sqrt{2}  等。
关键点(极值点)定位
  一旦找到关键点,我们就要对它们进行修正从而得到更准确的结果。
作者使用尺度空间的泰勒级数展开来获得极值的准确位置,如果极值点的灰度值小于阈值(0.03)就会被忽略掉。在 OpenCV 中这种阈值被称为contrastThreshold。
DoG 算法对边界非常敏感,所以我们必须要把边界去除。前面我们讲的Harris 算法除了可以用于角点检测之外还可以用于检测边界。作者就是使用了同样的思路。作者使用 2x2 的 Hessian 矩阵计算主曲率。从 Harris 角点检测的算法中,我们知道当一个特征值远远大于另外一个特征值时检测到的是边界。所以他们使用了一个简单的函数,如果比例高于阈值(OpenCV 中称为边界阈值),这个关键点就会被忽略。文章中给出的边界阈值为 10。
所以低对比度的关键点和边界关键点都会被去除掉,剩下的就是我们感兴趣的关键点了。
为关键点(极值点)指定方向参数
  现在我们要为每一个关键点赋予一个反向参数,这样它才会具有旋转不变性。获取关键点(所在尺度空间)的邻域,然后计算这个区域的梯度级和方向。根据计算得到的结果创建一个含有 36 个 bins(每 10 度一个 bin)的方向直方图。(使用当前尺度空间 σ 值的 1.5 倍为方差的圆形高斯窗口和梯度级做权重)。直方图中的峰值为主方向参数,如果其他的任何柱子的高度高于峰值的80% 被认为是辅方向。这就会在相同的尺度空间相同的位置构建除具有不同方向的关键点。这对于匹配的稳定性会有所帮助。
关键点描述符
  新的关键点描述符被创建了。选取与关键点周围一个 16x16 的邻域,把它分成 16 个 4x4 的小方块,为每个小方块创建一个具有 8 个 bin 的方向直方图。总共加起来有 128 个 bin。由此组成长为 128 的向量就构成了关键点描述符。除此之外还要进行几个测量以达到对光照变化,旋转等的稳定性。
关键点匹配
下一步就可以采用关键点特征向量的欧式距离来作为两幅图像中关键点的相似性判定度量。取第一个图的某个关键点,通过遍历找到第二幅图像中的距离最近的那个关键点。但有些情况下,第二个距离最近的关键点与第一个距离最近的关键点靠的太近。这可能是由于噪声等引起的。此时要计算最近距离与第二近距离的比值。如果比值大于 0.8,就忽略掉。这会去除 90% 的错误匹配,同时只去除 5% 的正确匹配。如文章所说。
这就是 SIFT 算法的摘要。非常推荐你阅读原始文献,这会加深你对算法的理解。请记住这个算法是受专利保护的。所以这个算法包含在 OpenCV 中的收费模块中。
OpenCV 中的 SIFT
现在让我们来看看 OpenCV 中关于 SIFT 的函数吧。让我们从关键点检测和绘制开始吧。首先我们要创建对象。我们可以使用不同的参数,这并不是必须的,关于参数的解释可以查看文档。

import cv2
import numpy as np

img = cv2.imread('home.jpg')
gray= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

sift = cv2.SIFT()
kp = sift.detect(gray,None)

img=cv2.drawKeypoints(gray,kp)

cv2.imwrite('sift_keypoints.jpg',img)

函数 sift.detect() 可以在图像中找到关键点。如果你只想在图像中的一个区域搜索的话,也可以创建一个掩模图像作为参数使用。返回的关键点是一个带有很多不同属性的特殊结构体,这些属性中包含它的坐标(x,y),有意义的邻域大小,确定其方向的角度等。
OpenCV 也提供了绘制关键点的函数:cv2.drawKeyPoints(),它可以在关键点的部位绘制一个小圆圈。如果你设置参数为 cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS,
就会绘制代表关键点大小的圆圈甚至可以绘制除关键点的方向。

img=cv2.drawKeypoints(gray,kp,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imwrite('sift_keypoints.jpg',img)

结果如下:
    SIFT Keypoints
现在来计算关键点描述符,OpenCV 提供了两种方法。
1. 由于我们已经找到了关键点,我们可以使用函数 sift.compute() 来计算这些关键点的描述符。例如:kp,des = sift.compute(gray,kp)。
2. 如果还没有找到关键点,可以使用函数 sift.detectAndCompute()一步到位直接找到关键点并计算出其描述符。
这里我们来看看第二个方法:

sift = cv2.SIFT()
kp, des = sift.detectAndCompute(gray,None)

这里 kp 是一个关键点列表。des 是一个 Numpy 数组,其大小是关键点数目乘以 128。
所以我们得到了关键点和描述符等。现在我们想看看如何在不同图像之间进行关键点匹配,这就是我们在接下来的章节将要学习的内容。


33 介绍 SURF(Speeded-Up Robust Features)


目标
 本节我们将要学习:
  • SUFR 的基础是什么?
  • OpenCV 中的 SURF
原理
  在上一节中我们学习了使用 SIFT 算法进行关键点检测和描述。但是这种算法的执行速度比较慢,人们需要速度更快的算法。在 2006 年Bay,H.,Tuytelaars,T. 和 Van Gool,L 共同提出了 SURF(加速稳健特征)算法。跟它的名字一样,这是个算法是加速版的 SIFT。
在 SIFT 中,Lowe 在构建尺度空间时使用 DoG 对 LoG 进行近似。SURF使用盒子滤波器(box_filter)对 LoG 进行近似。下图显示了这种近似。在进行卷积计算时可以利用积分图像(积分图像的一大特点是:计算图像中某个窗口内所有像素和时,计算量的大小与窗口大小无关),是盒子滤波器的一大优点。而且这种计算可以在不同尺度空间同时进行。同样 SURF 算法计算关键点的尺度和位置是也是依赖与 Hessian 矩阵行列式的。

    Box Filter approximation of Laplacian
为了保证特征矢量具有选装不变形,需要对于每一个特征点分配一个主要方向。需要以特征点为中心,以 6s(s 为特征点的尺度)为半径的圆形区域内,对图像进行 Harr 小波相应运算。这样做实际就是对图像进行梯度运算,但是利用积分图像,可以提高计算图像梯度的效率,为了求取主方向值,需哟啊设计一个以方向为中心,张角为 60 度的扇形滑动窗口,以步长为 0.2 弧度左右旋转这个滑动窗口,并对窗口内的图像 Haar 小波的响应值进行累加。主方向为最大的 Haar 响应累加值对应的方向。在很多应用中根本就不需要旋转不变性,所以没有必要确定它们的方向,如果不计算方向的话,又可以使算法提速。SURF 提供了成为 U-SURF 的功能,它具有更快的速度,同时保持了对 +/-15 度旋转的稳定性。OpenCV 对这两种模式同样支持,只需要对参数upright 进行设置,当 upright 为 0 时计算方向,为 1 时不计算方向,同时速度更快。

    Orientation Assignment in SURF
生成特征点的特征矢量需要计算图像的 Haar 小波响应。在一个矩形的区域内,以特征点为中心,沿主方向将 20s*20s 的图像划分成 4*4 个子块,每个子块利用尺寸 2s 的 Haar 小波模版进行响应计算,然后对响应值进行统计,组成向量

v=( \sum{d_x}, \sum{d_y}, \sum{|d_x|}, \sum{|d_y|})。这个描述符的长度为 64。降低的
维度可以加速计算和匹配,但又能提供更容易区分的特征。
为了增加特征点的独特性,SURF 还提供了一个加强版 128 维的特征描述符。当 d y 大于 0 和小于 0 时分别对 d x 和 |d x | 的和进行计算,计算 d y和 |d y | 时也进行区分,这样获得特征就会加倍,但又不会增加计算的复杂度。

For more distinctiveness, SURF feature descriptor has an extended 128 dimension version. The sums of d_x and |d_x| are computed separately for d_y < 0 and d_y \geq 0. Similarly, the sums of d_y and |d_y| are split up according to the sign of d_x , thereby doubling the number of features. 


OpenCV 同样提供了这种功能,当参数 extended 设置为 1 时为 128 维,当参数为 0 时为 64 维,默认情况为 128 维。
在检测特征点的过程中计算了 Hessian 矩阵的行列式,与此同时,计算得到了 Hessian 矩阵的迹,矩阵的迹为对角元素之和。
按照亮度的不同,可以将特征点分为两种,第一种为特征点迹其周围小邻域的亮度比背景区域要亮,Hessian 矩阵的迹为正;另外一种为特征点迹其周围小邻域的亮度比背景区域要暗,Hessian 矩阵为负值。根据这个特性,首先对两个特征点的 Hessian 的迹进行比较。如果同号,说明两个特征点具有相同的对比度;如果异号的话,说明两个特征点的对比度不同,放弃特征点之间的后续的相似性度量。
对于两个特征点描述子的相似性度量,我们采用欧式距离进行计算。

    Fast Indexing for Matching
简单来说 SURF 算法采用了很多方法来对每一步进行优化从而提高速度。
分析显示在结果效果相当的情况下 SURF 的速度是 SIFT 的 3 倍。SURF 善于处理具有模糊和旋转的图像,但是不善于处理视角变化和关照变化。

 

33.1 OpenCV 中的 SURF


与 SIFT 相同 OpenCV 也提供了 SURF 的相关函数。首先我们要初始化一个 SURF 对象,同时设置好可选参数:64/128 维描述符,Upright/Normal 模式等。所有的细节都已经在文档中解释的很明白了。就像我们在SIFT 中一样,我们可以使用函数 SURF.detect(),SURF.compute() 等来进行关键点搀着和描述。
首先从查找描述绘制关键点开始。由于和 SIFT 一样所以我们的示例都在Python 终端中演示。

>>> img = cv2.imread('fly.png',0)

# Create SURF object. You can specify params here or later.
# Here I set Hessian Threshold to 400
>>> surf = cv2.SURF(400)

# Find keypoints and descriptors directly
>>> kp, des = surf.detectAndCompute(img,None)

>>> len(kp)
 699

在一幅图像中显示 699 个关键点太多了。我们把它缩减到 50 个再绘制到图片上。在匹配时,我们可能需要所有的这些特征,不过现在还不需要。所以我们现在提高 Hessian 的阈值。

# Check present Hessian threshold
>>> print surf.hessianThreshold
400.0

# We set it to some 50000. Remember, it is just for representing in picture.
# In actual cases, it is better to have a value 300-500
>>> surf.hessianThreshold = 50000

# Again compute keypoints and check its number.
>>> kp, des = surf.detectAndCompute(img,None)

>>> print len(kp)
47

现在低于 50 了,把它们绘制到图像中吧。

>>> img2 = cv2.drawKeypoints(img,kp,None,(255,0,0),4)

>>> plt.imshow(img2),plt.show()

结果如下。你会发现 SURF 很像一个斑点检测器。它可以检测到蝴蝶翅膀上的白班。你可以在其他图片中测试一下。

    SURF Keypoints with Orientation
现在我们试一下 U-SURF,它不会检测关键点的方向。

# Check upright flag, if it False, set it to True
>>> print surf.upright
False

>>> surf.upright = True

# Recompute the feature points and draw it
>>> kp = surf.detect(img,None)
>>> img2 = cv2.drawKeypoints(img,kp,None,(255,0,0),4)

>>> plt.imshow(img2),plt.show()

结果如下。所有的关键点的朝向都是一致的。它比前面的快很多。如果你的工作对关键点的朝向没有特别的要求(如全景图拼接)等,这种方法会更快。

    Upright-SURF

最后我们再看看关键点描述符的大小,如果是 64 维的就改成 128 维。
# Find size of descriptor

# Find size of descriptor
>>> print surf.descriptorSize()
64

# That means flag, "extended" is False.
>>> surf.extended
 False

# So we make it to True to get 128-dim descriptors.
>>> surf.extended = True
>>> kp, des = surf.detectAndCompute(img,None)
>>> print surf.descriptorSize()
128
>>> print des.shape
(47, 128)

接下来要做的就是匹配了,我们会在后面讨论。

posted @ 2018-02-13 23:40 _Undo 阅读(...) 评论(...) 编辑 收藏