SIFT算法可以应用于图像拼接的例子上。通过SIFT,可以将多张图片拼接成全景图片。
Sift(尺度不变特征变换),全称是Scale Invariant Feature Transform
Sift提取图像的局部特征,在尺度空间寻找极值点,并提取出其位置、尺度、方向信息。
Sfit的应用范围包括物体辨别、机器人地图感知与导航、影像拼接、3D模型建立、手势识别、影像追踪等。
Note:SIFT函数注册了专利,在商业用途上是收费的。将在opencv > 3.4.3中,不再提供。【可以降低python版本或者交钱】
1.生成高斯差分金字塔(DOG金字塔),尺度空间构建
2. 空间极值点检测(关键点的初步查探)
3. 稳定关键点的精确定位
4. 稳定关键点方向信息分配
5. 关键点描述
6. 特征点匹配
获得图像金字塔一般包括二个步骤:
*主要思想是通过对原始图像进行尺度变换,获得图像多尺度下的尺度空间表示序列,对这些序列进行尺度空间主轮廓的提取,并以该主轮廓作为一种特征向量,实现边缘、角点检测不同分辨率上的关键点提取等。各尺度下图像的模糊度逐渐变大,能够模拟人在距离目标由近到远时目标物体在视网膜上的形成过程。
尺度空间构建的基础是DOG金字塔,DOG金字塔构建的基础是高斯金字塔。
高斯金字塔式在Sift算子中提出来的概念,首先高斯金字塔并不是一个金字塔,而是有很多组(Octave)金字塔构成,并且每组金字塔都包含若干层(Interval)。
高斯金字塔构建过程:
对于参数σ,在Sift算子中取的是固定值1.6。
将σ乘以一个比例系数k,等到一个新的平滑因子σ=k*σ,用它来平滑第1组第2层图像,结果图像作为第3层。
如此这般重复,最后得到L层图像,在同一组中,每一层图像的尺寸都是一样的,只是平滑系数不一样。它们对应的平滑系数分别为:0,σ,kσ,k2σ,k3σ……k^(L-2)σ。
将第1组倒数第三层图像作比例因子为2的降采样,得到的图像作为第2组的第1层,然后对第2组的第1层图像做平滑因子为σ的高斯平滑,得到第2组的第2层,就像步骤2中一样,如此得到第2组的L层图像,同组内它们的尺寸是一样的,对应的平滑系数分别为:0,σ,kσ,k2σ,k3σ……k^(L-2)σ。但是在尺寸方面第2组是第1组图像的一半。
这样反复执行,就可以得到一共O组,每组L层,共计O*L个图像,这些图像一起就构成了高斯金字塔,结构如下:
不同人站在不同地方看同一颗树的感觉。
在同一组内,不同层图像的尺寸是一样的,后一层图像的高斯平滑因子σ是前一层图像平滑因子的k倍;
在不同组内,后一组第一个图像是前一组倒数第三个图像的二分之一采样,图像大小是前一组的一半;
图像的尺度空间解决的问题是如何对图像在所有尺度下描述的问题。
在高斯金字塔中一共生成O组L层不同尺度的图像,这两个量合起来(O,L)就构成了高斯金字塔的尺度空间,也就是说以高斯金字塔的组O作为二维坐标系的一个坐标,不同层L作为另一个坐标,则给定的一组坐标(O,L)就可以唯一确定高斯金字塔中的一幅图像。
差分金字塔,DOG(Difference of Gaussian)金字塔是在高斯金字塔的基础上构建起来的,其实生成高斯金字塔的目的就是为了构建DOG金字塔。
DOG金字塔的第1组第1层是由高斯金字塔的第1组第2层减第1组第1层得到的。以此类推,逐组逐层生成每一个差分图像,所有差分图像构成差分金字塔。概括为DOG金字塔的第o组第l层图像是有高斯金字塔的第o组第l+1层减第o组第l层得到的。
DOG金字塔的构建可以用下图描述:
s:每组图像中检测s个尺度的极值点。(例如,第一幅图中就是检测3个尺度的极值点)
对应关系:如果我们要检测3个尺度的极值点,那么DOG就要5层(3+2),高斯图像就要比DOG多1层,所以高斯金字塔要6层(举个例子就会了)
DOG值对噪声和边缘比较敏感,所以在第2步的尺度空间中检测到的局部极值点还要经过进一步的筛
选,去除不稳定和错误检测出的极值点。
利用阈值的方法来限制,在opencv中为contrastThreshold。
SIFT关键点:
import cv2
import numpy as np
img = cv2.imread("lenna.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#创建sift检测器,这个sift检测器主要是用于检测模板和待匹配图像的特征关键点点
sift = cv2.xfeatures2d.SIFT_create()
#利用创建好的特征点检测器去检测两幅图像的特征关键点,
# 其中keypoints含有角度、关键点坐标等多个信息,提取出坐标点的坐标
# descriptor是特征描述符,每一个特征点对应了一个特征描述符,由一维特征向量构成
keypoints, descriptor = sift.detectAndCompute(gray, None)
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS对图像的每个关键点都绘制了圆圈和方向。
img = cv2.drawKeypoints(image=img, outImage=img, keypoints=keypoints,
flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS,
color=(51, 163, 236))
# img=cv2.drawKeypoints(gray,keypoints,img)
cv2.imshow('sift_keypoints', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
SIFT特征匹配:
import cv2
import numpy as np
def drawMatchesKnn_cv2(img1_gray,kp1,img2_gray,kp2,goodMatch):
h1, w1 = img1_gray.shape[:2]
h2, w2 = img2_gray.shape[:2]
vis = np.zeros((max(h1, h2), w1 + w2, 3), np.uint8)
vis[:h1, :w1] = img1_gray
vis[:h2, w1:w1 + w2] = img2_gray
p1 = [kpp.queryIdx for kpp in goodMatch]
p2 = [kpp.trainIdx for kpp in goodMatch]
post1 = np.int32([kp1[pp].pt for pp in p1])
post2 = np.int32([kp2[pp].pt for pp in p2]) + (w1, 0)
for (x1, y1), (x2, y2) in zip(post1, post2):
cv2.line(vis, (x1, y1), (x2, y2), (0,0,255))
cv2.namedWindow("match",cv2.WINDOW_NORMAL)
cv2.imshow("match", vis)
img1_gray = cv2.imread("iphone1.png")
img2_gray = cv2.imread("iphone2.png")
#sift = cv2.SIFT()
sift = cv2.xfeatures2d.SIFT_create()
#sift = cv2.SURF()
kp1, des1 = sift.detectAndCompute(img1_gray, None)
kp2, des2 = sift.detectAndCompute(img2_gray, None)
# BFmatcher with default parms
bf = cv2.BFMatcher(cv2.NORM_L2) #建立匹配关系
matches = bf.knnMatch(des1, des2, k = 2)#匹配描述子
goodMatch = []
for m,n in matches:
if m.distance < 0.50*n.distance:
goodMatch.append(m)
#cv2.drawMatchesKnn()使用的点对集good是一维的,(N,1);画出good中前几个点对连线;
drawMatchesKnn_cv2(img1_gray,kp1,img2_gray,kp2,goodMatch[:20])
cv2.waitKey(0)
cv2.destroyAllWindows()