影象拼接—SIFT+Flann匹配+估計單應矩陣—(全景圖panorama)
阿新 • • 發佈:2019-01-03
開發環境:python+opencv3
筆者拼接影象的步驟如下:
step1: 利用特徵運算元檢測並描述,常見的特徵運算元在cv2.xfeatures2中都有,比如角點harris,斑點surf,sift等,這些運算元的原理不再過多闡述。
step1:對於描述子,要進行匹配,在cv2中匹配可以是暴力匹配也可以選擇flann,這是優化過的匹配演算法,至於暴力匹配就是sift 的作者lowe 提出的NNDR方法,遍歷所有點。
step3: 對於匹配點,我們首先要檢查是否存在誤點,可以利用基本矩陣(即極線約束的方式來剔除),在筆者原始碼中,沒有這一步。對於提純後的同名點,我們可以估計一個單應矩陣,即點對點的變換方式(如果h33=1),那麼單應矩陣也可稱為“投影矩陣”,根據《計算機視覺在多檢視幾何中的應用》那本書可以知道,投影矩陣具有保線性,即可以消除射影失真(影像上的平行線在看起來不平行,它們都有消影點(vanishing point))。單應矩陣常用語拼接。->基本矩陣和單應矩陣的區別見筆者之前的文章。
step 4: 利用opencv 中的warpPerspective 函式來實現全景圖。
下面附上程式碼和效果圖:(程式碼都加了註釋)
# -*- coding: utf-8 -*-
#Panorama Stitcher
import cv2
import numpy as np
class macthing(object):
def matchIMG(self,im1,im2,kp1,kp2,des1,des2):
FLANN_INDEX_KDTREE=0
index_p=dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
searth_p=dict(checks=50)
flann=cv2.FlannBasedMatcher (index_p,searth_p)
matches=flann.knnMatch(des1,des2,k=2)
good =[]
pts1=[]
pts2=[]
for i,(m,n) in enumerate(matches):
if m.distance<0.6*n.distance:
good.append(m)
pts1.append(kp1[m.queryIdx].pt)
pts2.append(kp2[m.trainIdx].pt)
pts1=np.float32(pts1)
pts2=np.float32(pts2)
H ,mask=cv2.findHomography(pts1,pts2,cv2.RANSAC,0.01)
pts1_1=pts1[mask.ravel()==1]
pts2_2=pts2[mask.ravel()==1]
return pts1_1,pts2_2,H
def appendimage(self,im1,im2):
r1=im1.shape[0]
r2=im2.shape[0]
if r1<r2:
img=np.zeros((r2-r1,im1.shape[1]),np.uint8)
im1_1=np.vstack((im1,img))
im3=np.hstack((im1_1,im2))
else:
img=np.zeros((r1-r2,im2.shape[1]),np.uint8)
im2_2=np.vstack((im2,img))
im3=np.hstack((im1,im2_2))
return im3
# def panorama_get(self,im1,im2,H):
# if im1.shape[0]>=im2.shape[0]:
# result=cv2.warpPerspective(im1,H,(im1.shape[1]+im2.shape[1],im1.shape[0]))
# result[0:im2.shape[0],0:im2.shape[1]]=im2
# else:
# result=cv2.warpPerspective(im1,H,(im1.shape[1]+im2.shape[1],im2.shape[0]))
# result[0:im2.shape[0],0:im2.shape[1]]=im2
# return result
def panorama_get(self,im1,im2,H):
h1,w1=im1.shape[:2]
h2,w2=im2.shape[:2]
pts1=np.float32([[0,0],[0,h1],[w1,h1],[w1,0]]).reshape(-1,1,2)#轉為3維座標
pts2=np.float32([[0,0],[0,h2],[w2,h2],[w2,0]]).reshape(-1,1,2)
pts1_=cv2.perspectiveTransform(pts1,H)#H:3*3 矩陣,所以pts1也該為3維座標
pts=np.concatenate((pts1_,pts2),axis=0)#列連線
#np.min 是行優先
[xmin,ymin]=np.int32(pts.min(axis=0).ravel()-0.5)
[xmax,ymax]=np.int32(pts.max(axis=0).ravel()+0.5)
t=[-xmin,-ymin]# 左加右減
Ht = np.array([[1,0,t[0]],[0,1,t[1]],[0,0,1]]) #相當於一個向右平移
result = cv2.warpPerspective(im1, Ht.dot(H), (xmax-xmin, ymax-ymin))#最後一個引數是輸出影象的寬、高
result[t[1]:h2+t[1],t[0]:w2+t[0]] = im2
return result
if __name__=="__main__":
M=macthing()
im1_=cv2.imread(r"C:\Users\Y\Desktop\3.jpg")
im2_=cv2.imread(r"C:\Users\Y\Desktop\4.jpg")
im1=cv2.cvtColor(im1_,cv2.COLOR_BGR2GRAY)
im2=cv2.cvtColor(im2_,cv2.COLOR_BGR2GRAY)
sift=cv2.xfeatures2d.SIFT_create()
kp1,des1=sift.detectAndCompute(im1,None)
kp2,des2=sift.detectAndCompute(im2,None)
pts1_1,pts2_2,H=M.matchIMG(im1,im2,kp1,kp2,des1,des2)
im3=M.appendimage(im1,im2)
pts2_new=pts2_2.copy()
for i in range(len(pts2_2)):
pts2_new[i,0]=pts2_new[i,0]+np.float32(im1.shape[1])
for i in range(len(pts1_1)):
cv2.line(im3,tuple(pts1_1[i]),tuple(pts2_new[i]),255,2)
# cv cv2\Y\Desktop\45.jpg",result)
cv2.namedWindow("panorma ,cv2.WINDOW_NORMAL)
cv2.imshow("panorama",result),cv2.waitKey(0)
_]UI