目录
一、引言
二、SIFT算法的各个步骤
1、尺度空间极值检测
2、特征方向赋值
三、代码运行
上篇文章我们说到,Harris角点检测能够识别出图像中最有特点的点,使电脑实现一部分图像识别的能力。相信不少读者都是这么想的:如果电脑真的能识别出“特征”,感受到图像的特点,那这也太智能了吧!但是亲爱的读者们能够准确的描述一下,我们所期待的特征,是什么样子呢?
我们当然是期望,“化成灰都能认得出图像的内容是谁”。说的更直白一点:把图片模糊个很多倍,或者是从远处观看这张图片,电脑依旧可以找到这张图片的特征点在哪里。据此,我们引出了这篇文章的第一个重要概念-------尺度空间:即试图模拟我们眼睛的一种方法,即满足眼睛在物体靠近或者远离时,仍然能够识别出物体。如果我们希望在大的尺度下观察,我们会选择站的更远一点,去观察整体;如果我们期待在小的尺度下去观察,我们会靠得更近一点,去观察细节。
上篇文章的结尾,我们讲到了harris角点检测的一个缺陷,那就是当图像缩放之后,原先被认为是特征点的位置会被电脑“嫌弃”,大概率会被当成边缘之类的。并且,如果在匹配那些缺乏高频细节的图像(比如说白云)或者有一定模糊程度的图片时,电脑通过Harris角点检测法很难检测出它的“角点”。所以,用Harris角点算法并不能满足我们所期待的检测效果。于是乎,机智的科学家们想到了另一种检测特征点的方式,这也是我们今天的主角:SIFT描述子。
SIFT描述子算法大抵分为了四步:尺度空间极值检测与特征方向赋值两个步骤,其中尺度空间极值检测最为复杂与重要,小编会侧重讲解这一块的知识。
我们刚刚说过,我们期待在不同尺寸和不同模糊程度的条件下,仍然可以找到特征点。对于模糊,相信大家会想到高斯滤波,也叫做高斯平滑,不理解平滑原理的小伙伴们可以去看看小编之前的文章。而对于缩放尺寸,相信大家则会想到之前小编写的一篇文章中所描述的图像金字塔。那么,我们就很容易的想到,能不能把这两者相结合,达到双剑合璧的效果?
诶嘿,还真有这样的双剑合璧,我们把这两者的双剑合璧称之为高斯金字塔。高斯金字塔是在传统的图像金字塔的基础上,对每一层用不同的做高斯模糊,使每一层金字塔有很多张高斯模糊的图像。这样一组图像,我们称之为octave。且每一个octave的最清晰的图片(也就是最下面的那张图片),是由它上面一个octave的倒数第三张图片经过下采样得到的。
想要找到特征点,其实我们可以把某一尺度下的“点”看作是一个边围成了一个圆圈所构成的。只不过这个“点”有大有小,并不统一为一个像素的大小。(因为在大尺度下大圆会变成一个“点”)比如说下图,我们把这一个个圆包含的部分称为团块blob。
另外,我们知道,“边缘检测”的一种方式就是进行高斯拉普拉斯LoG运算,高斯拉普拉斯计算有这么一个结论,大家可以了解一下:不同尺寸Blob响应不同,当高斯函数尺度与Blob直径基本一致时,响应最大。
不过使用高斯拉普拉斯算子可能会使计算量显著提升。所以我们用DoG代替LoG运算,二者的效果类似。所谓DoG,即相同图片在不同的下做差所形成的图片,有兴趣的读者可以自行验算一下(LoG检测边缘,模糊程度不同的图片做差值,显然得到的也是边缘)。
那么把上面的高斯金字塔和DoG相结合,我们又能得到一种新玩意儿--------高斯差分金字塔。即高斯金字塔中,每一个尺度中相邻的两张图片做差值运算,得到几张新的图片,重新构成一个新的金子塔的一个octave。
接下来就好办了,找出每一个尺度下(最上面和最下面的两张图以及边缘的一圈像素除外),比自己周围8个像素以及上下两层同位置及其附近的8个像素都大或者都小的点,一共8+9+9=26个点。该点即为极大值点或者极小值点。
此时大致能找到关键点的位置,但并不完全正确,还需要用三阶的泰勒展开公式进行展开,考虑到这不是SIFT的重点知识,所以小编在此一笔带过啦。
我们之前说到了尺度以及模糊程度不同,电脑依然能够找到特征点的问题。但我们还没有考虑到如何让SIFT具有旋转不变性。为了实现这一性质,我们采用了特征方向计算与赋值这一步骤,而这一步骤大致又分为四小步:
1、围绕检测的关键点取 16x16 方形窗
这个很好理解,就是找到我们刚刚计算得到的关键点,并且以这个点为中心,放置一个16x16的边框。
2、对每个像素计算边缘方向 (梯度幅值及其角度)
这个也很好操作,计算梯度的模就行了。但是考虑到每个像素离关键点的距离不同,我们还要乘上一个高斯核。
3、扔掉较弱的边缘 (通过对梯度幅值进行阈值判断)
看字面意思应该就能明白,设定一个阈值,梯度幅值高于这个阈值的我们会保留,低于这个阈值的我们会扔掉。
4、生成保留的边缘方向的直方图
根据方向的不同,绘制直方图,峰值最高的我们会认定为主方向。这样的话就算图片旋转了,但主方向并不会因此改变,电脑仍然能够找到。如下图,SIFT算子与旋转不变性。
一共有三个特别重要的步骤,分别是创建SIFT对象、使用detect函数找到关键点以及将点标记到图像上去。
创建SIFT对象:sift = cv2.xfeatures2d.SIFT_create()
找到关键点:kp = sift.detect(img) #img为图像在opencv里的名字;kp是一个列表,封装了KeyPoint对象
标记关键点:cv2.detect(img,kp,img)
import cv2
#注意还需要下载opencv-contrib-python这个扩展包
img=cv2.imread("p.jpg",-1)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#创建SIFT对象
sift=cv2.xfeatures2d.SIFT_create()
#在灰度图像上找关键点
kp=sift.detect(gray)
#将找到的点标记到原图像去
cv2.drawKeypoints(img,kp,img)
cv2.imshow("corner",img)
cv2.imwrite("corner.jpg",img)
cv2.waitKey()
cv2.destroyAllWindows()
电脑找到的关键点如下图:
好的,本期的计算机视觉知识就到此为止啦,收藏我,带你学习更多计算机视觉的知识!