近期一直研究图像的拼接问题,所以和大家在这里分享一下。图像拼接前,找到各个图像的特征点是个非常关键的步骤。这期专栏,我将介绍两种较常用的特征匹配方法(基于OpenCV),Brute-Force匹配和FLANN匹配。
1
BF匹配
cv2.BFMatch(normType,crossCheck=True/False)
其中normType是用来指定要使用的距离测试类型。默认值为cv2.Norm_L2,适用于SIFT,SURF方法,还有一个参数为cv2.Norm_L1。如果是ORB,BRIEF,BRISK算法等,要是用cv2.NORM_HAMMING,如果ORB算法的参数设置为VTA_K==3或4,normType就应该设置为cv2.NORM_HAMMING2.
第二个参数是crossCheck,默认值是False。如果设置为True,匹配条件会更加严格。举例来说,如果A图像中的i点和B图像中的j点距离最近,并且B中的j点到A中的i点距离也最近,相互匹配,这个匹配结果才会返回。
import cv2 as cv
from matplotlib import pyplot as plt
img1=cv.imread("image/work1.jpg",0)
img2=cv.imread("image/work2.jpg",0)
orb=cv.ORB_create() #创建一个orb特征检测器
kp1,des1=orb.detectAndCompute(img1,None) #计算img1中的特征点和描述符
kp2,des2=orb.detectAndCompute(img2,None) #计算img2中的
bf = cv.BFMatcher(cv.NORM_HAMMING,crossCheck=True) #建立匹配关系
mathces=bf.match(des1,des2)# 匹配描述符
mathces=sorted(mathces,key=lambda x:x.distance) #据距离来排序
img3= cv.drawMatches(img1,kp1,img2,kp2,mathces[:40],img2,flags=2) #画出匹配关系
plt.imshow(img3),plt.show() #用matplotlib描绘出来
2
FLANN匹配
import cv2 as cv
from matplotlib import pyplot as plt
img1=cv.imread("image/work1.jpg",0)
img2=cv.imread("image/work2.jpg",0)
orb=cv.ORB_create() #创建一个orb特征检测器
kp1,des1=orb.detectAndCompute(img1,None) #计算img1中的特征点和描述符
kp2,des2=orb.detectAndCompute(img2,None) #计算img2中的特征点和描述符
bf = cv.BFMatcher(cv.NORM_HAMMING,crossCheck=True) #建立匹配关系
mathces=bf.match(des1,des2) #匹配描述符
mathces=sorted(mathces,key=lambda x:x.distance) #据距离来排序
img3= cv.drawMatches(img1,kp1,img2,kp2,mathces[:40],img2,flags=2)# 画出匹配关系
plt.imshow(img3),plt.show() #用matplotlib描绘出来
FLANN 匹配器有两个参数,一个是indexParams,另一个是searchParams,以字典的形式进行参数传递。为了计算匹配,FLANN内部会决定如何处理索引和搜索对象。
索引方法可以选择LinearIndex,KTreeIndex,KMeansIndex等,其中KTreeIndex配置索引简单,只需制定待处理核密度的数量即可,推荐值为1~16。SearchParams只包含一个字段,checks,表示制定索引树要被遍历的次数。经验值推荐,5 kd—trees,50 checks 可以取得较好的匹配精度,并且可以在较短的时间内完成。
在匹配结果中我们根据Lowe的论文,删除匹配结果距离大于0.7的。通过这种过滤,可以避免几乎90%的错误匹配。原文“Distinctive Image Feature from scale Scale-Invariant Keypoints”
匹配结果如下:
图2 FLANN匹配结果
3
FLANN单应性匹配
两幅不在一个平面角度的照片,通过其中一幅照片(小图像)的特征点,与第二幅(大图像)中的特征点可以确定这部分位置在第二幅中的位置。我们通过cv2.findHomography()函数,计算这对图像的透视变化矩阵,然后通过cv2.perspectiveTransform()找到在第二图像(大图像)中的第一幅图像的位置。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
MIN_MATCH_COUNT=10 #设置最低匹配数量为10
img1=cv.imread("image/pen.jpg",0) #读取第一个图像(小图像)
img2=cv.imread("image/work2.jpg",0) #读取第二个图像(大图像)
sift=cv.xfeatures2d.SIFT_create() #创建sift检测器
kp1,des1=sift.detectAndCompute(img1,None)
kp2,des2=sift.detectAndCompute(img2,None)
#创建设置FLAAN匹配
FLANN_INDEX_KDTREE=0
index_params=dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params=dict(checks=50)
flann=cv.FlannBasedMatcher(index_params,search_params)
mathces=flann.knnMatch(des1,des2,k=2)
good=[]
#过滤不合格的匹配结果,大于0.7的都舍弃
for m,n in mathces:
if m.distance
good.append(m)
#如果匹配结果大于10,则获取关键点的坐标,用于计算变换矩阵
if len(good)>MIN_MATCH_COUNT:
src_pts=np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
dst_pts =np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
#计算变换矩阵和掩膜
M,mask=cv.findHomography(src_pts,dst_pts,cv.RANSAC,10.0)
matchesMask=mask.ravel().tolist()
#根据变换矩阵进行计算,找到小图像在大图像中的位置
h,w=img1.shape
pts=np.float32([[0,0],[0,h-1],[w-1,h-1],[w-1,0]]).reshape(-1,1,2)
dst=cv.perspectiveTransform(pts,M)
cv.polylines(img2,[np.int32(dst)],True,0,5,cv.LINE_AA)
else:
print(" Not Enough matches are found")
matchesMask=None
#画出特征匹配线
draw_params=dict(matchColor=(0,255,0),singlePointColor=None,
matchesMask=matchesMask,flags=2)
#plt展示最终的结果
img3=cv.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params)
plt.imshow(img3),plt.show()
展示下最终的结果,其中白色和黑色的边框,就是通过变换矩阵计算得到的。
原始照片(大图像)
原始照片(小图像)
原始照片(小图像)
原始图像(大图像)
原始图像(小图像)
FLANN单应性匹配
关注我们
领取专属 10元无门槛券
私享最新 技术干货