1. 程式人生 > >影象拼接—SIFT+Flann匹配+估計單應矩陣—(全景圖panorama)

影象拼接—SIFT+Flann匹配+估計單應矩陣—(全景圖panorama)

開發環境: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