本文不光展示SIFT算法的匹配效果,更重要的是记录下搜集信息以及处理信息的过程,方便以后按照这个行动思路去解决问题,以及优化行动思路
其实早在学习双目视觉的时候就了解过 特征匹配 算法,其中就使用过SIFT算法,以及众多SIFT算法的变种,这时只是知道名字,最近因为课题需要使用,所以开展从原理到实践的研究,首先了解一个算法,首先要直观的看到他的运行结果,不管是自己跑的还是别人跑的,第一个数据来源是opencv-python的中文文档
单张图片特征查找效果如下:
两张图片匹配效果如下:
看到这里就激起了我的兴趣,接下来就进行第二步
首先去找的是视频资源,当然是先去B站康康,没想到真的有!
视频详解
看完这些之后大概了解了SIFT的流程以及部分数学知识,但是具体的细节还需要文字资料配合食用:
SIFT的宏观描述(大体流程)
SIFT的微观描述(数学细节)
此时再回去看带你入门的视频,会发现首尾呼应,有些不懂的地方也明白了,但是这是一个不断反复多次理解的过程,没有人能一天把别人长时间的研究成果搞懂,所以不会以及困惑很正常,不要灰心,多找资料,反复看几遍就会懂了。
到这里相信你已经明白了SIFT是什么原理,匹配出来的效果大概是什么样的,所以你也想做出和示例中一样的效果,甚至做得更好,或者你想用SIFT帮助你的科研项目,这些都没有问题,以下是我的代码学习历程以及成果。
在最初阶段,我只想让代码运行起来,因为SIFT算法是受专利保护的,所以在opencv3.4之后被放到了一个单独的收费库里,只能自己在本地机器编译后才能运行,
windows编译方法1
windows编译方法2
当然这个过程会费时大概一天,只有真正的强者才能完成编译这个过程,你会遇上非常多的莫名其妙的问题,需要你自己一个一个去解决,但是只要肯用功,总会解决的
我在这补充一个编译之后在本地python使用的方法,网上没找到,是自己研究出来的,当你完成编译后会有下图这样的文件夹
找到其中的python_loader,然后打开anaconda prompt,如果你没有anaconda,那么你就打开cmd,并保证输入python后出现python的交互界面(把python放到环境变量的path里)接下来操作如下
1.首先卸载你的 opencv-python
pip uninstall opencv-python
pip uninstall opencv-contrib-python
2.定位到你的setu.py的目录
切换目录方法
3.然后分别输入
python setup.py build
python setup.py install
下面就是代码实践,我首先尝试了官网教程给出的算法例子
import numpy as np
import cv2 as cv
img = cv.imread('home.jpg')
gray= cv.cvtColor(img,cv.COLOR_BGR2GRAY)
'''
下面是创建sift,以及使用sift进行计算的方法
'''
sift = cv.xfeatures2d.SIFT_create()
kp = sift.detect(gray,None)
'''
下面是绘图部分
'''
img=cv.drawKeypoints(gray,kp,img)
plt.imshow(img),plt.title('sift-gray-ver1'),plt.show()
官网给出的版本特征点画得非常小,没有尺度和方向的描述,并且是个黑白的,影响视觉体验。经过一番资料的查询,找到了如何画彩色和清晰特征点的方法。
import numpy as np
import cv2 as cv
img = cv.imread('home.jpg')73
'''
下面是创建sift,以及使用sift进行计算的方法
'''
sift = cv2.xfeatures2d.SIFT_create()
kp,des = sift.detectAndCompute(img1,None)
'''
下面是绘图部分
'''
img = cv.drawKeypoints(img1,kp,None,(100,100,255),4)
plt.imshow(img[:,:,::-1]),plt.title('sift-rgb-ver2'),plt.show()
这下就看的很清楚了,那么除了SIFT对单个图像的特征挖掘,还要进行两个图片的特征连接,先上代码
'''
构造函数
'''
def my_match(img1,img2,kp1,kp2,des1,des2,mc_type):
bf = cv.BFMatcher()
matches = bf.knnMatch(des1,des2,k=2)
#此处的2就是每个匹配装两个对应的点位
good = []
#不难发现,m,n就是对应刚才的2
for m,n in matches:
if m.distance < 0.70*n.distance:
print(m.distance)
print('---end---')
# print(n.imgIdx)
good.append([m])
img3 = cv.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=mc_type)
cv.imshow('match',img3)
cv.waitKey(10000000)
cv.destroyAllWindows()
cv.waitKey(1)
'''
调用函数
'''
my_match(img1,
img2,sift_kp_1,sift_kp_2,sift_des_1,sift_des_2,
cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
这个学生证是我朋友和我出去玩落在我车上的,正好在手边就拿来拍照了,可以看到匹配的效果不错,大部分博客也是到此为止了,但是我发现有些点依然对应的不是很好,所以下一部分是模型内的参数调优以及算法优化。
首先是加入Ransac算法部分,Ransac算法接收的是由SIFT特征经过BF匹配后生成的 [左图特征点] 和[右图特征点] , 然后对这些匹配后的特征点进行选择,删掉误匹配点,只取正确匹配点,实际代码如下。
def my_match(img1,img2,kp1,kp2,des1,des2,mc_type):
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks = 100)
flann = cv2.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1, des2, k = 2)
good = []
#不难发现,m,n就是对应刚才的2
for m,n in matches:
if m.distance < 0.95 *n.distance:
print(m.distance)
print(m.queryIdx)
print('---end---')
# print(n.imgIdx)
good.append(m)
if len(good) > 10:
src_pts = np.float32([sift_kp_1[i.queryIdx].pt for i in good])#.\reshape(-1, 1, 2)
dst_pts = np.float32([sift_kp_2[i.trainIdx].pt for i in good])#.reshape(-1, 1, 2)
# print(src_pts.shape)
# print(dst_pts.shape)
ransacReprojThreshold = 10.0
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, ransacReprojThreshold)
matchesMask = mask.ravel().tolist()
h, w, mode = img1.shape
pts = np.float32([[0, 0], [0, h -1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
#透视变换函数cv2.perspectiveTransform: 输入的参数是两种数组,并返回dst矩阵——扭转矩阵
dst = cv2.perspectiveTransform(pts, M)
img2 = cv2.polylines(img2, [np.int32(dst)], True, (127,255,0), 3, cv2.LINE_AA)
draw_params = dict(singlePointColor = None,
matchesMask = mask,
flags = 2)
plt.plot(dst_pts[:,0],dst_pts[:,1],"ro")
dots=dst_pts[mask[:,0]==1]
plt.plot(dots[:,0],dots[:,1],"bo")
plt.show()
plt.plot(src_pts[:,0],src_pts[:,1],"go")
dots=src_pts[mask[:,0]==1]
plt.plot(dots[:,0],dots[:,1],"bo")
plt.show()
# print(dst_pts[mask,0])
img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params)
cv.imshow('match',img3)
cv.waitKey(10000000)
cv.destroyAllWindows()
cv.waitKey(1)
上图是图一,描述了在左图所有的特征点中,rasac选择的正确匹配特征点
上图是图二,描述了在右图所有的特征点中,ransac选择的正确匹配特征点
上图是图三,最终生成的结果
在最后的代码中,知识点主要有以下几个
dots=dst_pts[mask[:,0]==1]
for m,n in matches:
if m.distance < 0.95 *n.distance:
print(m.distance)
print(m.queryIdx)
print('---end---')
# print(n.imgIdx)
good.append(m)