1. 程式人生 > 程式設計 >Python+OpenCV實現影象的全景拼接

Python+OpenCV實現影象的全景拼接

本文例項為大家分享了Python+OpenCV實現影象的全景拼接的具體程式碼,供大家參考,具體內容如下

環境:python3.5.2 + openCV3.4

1.演算法目的

將兩張相同場景的場景圖片進行全景拼接。

2.演算法步驟

本演算法基本步驟有以下幾步:

步驟1:將圖形先進行桶形矯正

沒有進行桶形變換的圖片效果可能會像以下這樣:

Python+OpenCV實現影象的全景拼接

圖片越多拼接可能就會越誇張。

Python+OpenCV實現影象的全景拼接

本演算法是將圖片進行桶形矯正。目的就是來縮減透視變換(Homography)之後圖片產生的變形,從而使拼接圖片變得畸形。

步驟2:特徵點匹配

本演算法使用的sift演算法匹配,它具有旋轉不變性和縮放不變性,具體原理在之後會補上一篇關於sift演算法的文章,這裡就不做詳細介紹。

在匹配特徵點的過程中,透視矩陣選取了4對特徵點計算,公式為

Python+OpenCV實現影象的全景拼接

點的齊次座標依賴於其尺度定義,因此矩陣H也僅依賴尺度定義,所以,單應性矩陣具有8個獨立的自由度。

如果在選取的不正確的特徵點,那麼透視矩陣就可能計算錯誤,所以為了提高結果的魯棒性,就要去除這些錯誤的特徵點,而RANSAC方法就是用來刪除這些錯誤的特徵點。

**RANSAC:**用來找到正確模型來擬合帶有噪聲資料的迭代方法。基本思想:資料中包含正確的點和噪聲點,合理的模型應該能夠在描述正確資料點的同時擯棄噪聲點。

RANSAC方法隨機獲取4對不同的特徵匹配座標,計算出透視矩陣H1,再將第二張圖的特徵匹配點經過這個矩陣H1對映到第一張圖的座標空間裡,通過計算來驗證這個H1矩陣是否滿足絕大部分的特徵點。

通過迭代多次,以滿足最多特徵匹配點的特徵矩陣H作為結果。

這樣正常情況就可以去除錯誤的特徵點了,除非匹配錯誤的特徵點比正確的還多。

下圖是我在嘉庚圖書館旁拍攝的照片的特徵點匹配。

Python+OpenCV實現影象的全景拼接

步驟3:利用得到的變換矩陣進行圖片的拼接。

可以看出基本做到了無縫拼接。只是在色差上還是看得出銜接的部分存在。

Python+OpenCV實現影象的全景拼接

實現結果

我在宿舍裡又多照了幾組照片來實驗:
室內宿舍場景的特徵點匹配:

Python+OpenCV實現影象的全景拼接

拼接結果:

Python+OpenCV實現影象的全景拼接

在室內的效果根據結果來看效果也還可以。

我測試了宿舍裡景深落差較大的兩張圖片:

特徵點匹配:

Python+OpenCV實現影象的全景拼接

雖然距離較遠,但是還是可以粗略的匹配到特徵點。

拼接結果:

Python+OpenCV實現影象的全景拼接

從結果上來看可以看得出來,兩張圖片依然可以正確而粗略地拼接再一起,可以看得出是同一個區域。只是由於特徵點不夠,在細節上景深落差較大的還是沒辦法完美地拼接。

import numpy as np
import cv2 as cv
import imutils

class Stitcher:
 def __init__(self):
 self.isv3 = imutils.is_cv3()

 def stitch(self,imgs,ratio = 0.75,reprojThresh = 4.0,showMatches = False):
 print('A')
 (img2,img1) = imgs
 #獲取關鍵點和描述符
 (kp1,des1) = self.detectAndDescribe(img1)
 (kp2,des2) = self.detectAndDescribe(img2)
 print(len(kp1),len(des1))
 print(len(kp2),len(des2))
 R = self.matchKeyPoints(kp1,kp2,des1,des2,ratio,reprojThresh)

 #如果沒有足夠的最佳匹配點,M為None
 if R is None:
  return None
 (good,M,mask) = R
 print(M)
 #對img1透視變換,M是ROI區域矩陣, 變換後的大小是(img1.w+img2.w,img1.h)
 result = cv.warpPerspective(img1,(img1.shape[1] + img2.shape[1],img1.shape[0]))
 #將img2的值賦給結果影象
 result[0:img2.shape[0],0:img2.shape[1]] = img2

 #是否需要顯示ROI區域
 if showMatches:
  vis = self.drawMatches1(img1,img2,kp1,good,mask)
  return (result,vis)

 return result


 def detectAndDescribe(self,img):
 print('B')
 gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)

 #檢查我們使用的是否是penCV3.x
 if self.isv3:
  sift = cv.xfeatures2d.SIFT_create()
  (kps,des) = sift.detectAndCompute(img,None)
 else:
  sift = cv.FastFeatureDetector_create('SIFT')
  kps = sift.detect(gray)
  des = sift.compute(gray,kps)

 kps = np.float32([kp.pt for kp in kps]) # **********************************
 #返回關鍵點和描述符
 return (kps,des)

 def matchKeyPoints(self,reprojThresh):
 print('C')
 #初始化BF,因為使用的是SIFT ,所以使用預設引數
 matcher = cv.DescriptorMatcher_create('BruteForce')
 # bf = cv.BFMatcher()
 # matches = bf.knnMatch(des1,k=2)
 matches = matcher.knnMatch(des1,2) #***********************************

 #獲取理想匹配
 good = []
 for m in matches:
  if len(m) == 2 and m[0].distance < ratio * m[1].distance:
  good.append((m[0].trainIdx,m[0].queryIdx))

 print(len(good))
 #最少要有四個點才能做透視變換
 if len(good) > 4:
  #獲取關鍵點的座標
  # 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,2)
  src_pts = np.float32([kp1[i] for (_,i) in good])
  dst_pts = np.float32([kp2[i] for (i,_) in good])

  #通過兩個影象的關鍵點計算變換矩陣
  (M,mask) = cv.findHomography(src_pts,dst_pts,cv.RANSAC,reprojThresh)

  #返回最佳匹配點、變換矩陣和掩模
  return (good,mask)
 #如果不滿足最少四個 就返回None
 return None

 def drawMatches(img1,matches,mask,M):
 # 獲得原影象的高和寬
 h,w = img1.shape[:2]
 # 使用得到的變換矩陣對原影象的四個角進行變換,獲得目標影象上對應的座標
 pts = np.float32([[0,0],[0,h-1],[w-1,0]]).reshape(-1,2)
 dst = cv.perspectiveTransform(pts,M)
 matchesMask = mask.ravel().tolist()

 draw_params = dict(matchColor = (0,255,0),singlePointColor = None,matchesMask = matchesMask,flags = 2)
 img = cv.drawMatches(img1,None,**draw_params)

 return img

 def drawMatches1(self,img1,metches,mask):
 print('D')
 (hA,wA) = img1.shape[:2]
 (hB,wB) = img2.shape[:2]
 vis = np.zeros((max(hA,hB),wA+wB,3),dtype='uint8')
 vis[0:hA,0:wA] = img1
 vis[0:hB,wA:] = img2
 for ((trainIdx,queryIdx),s) in zip(metches,mask):
  if s == 1:
  ptA = (int(kp1[queryIdx][0]),int(kp1[queryIdx][1]))
  ptB = (int(kp2[trainIdx][0])+wA,int(kp2[trainIdx][1]))
  cv.line(vis,ptA,ptB,(0,1)

 return vis

# def show():
# img1 = cv.imread('image/sedona_left_01.png')
# img2 = cv.imread('image/sedona_right_01.png')
# img1 = imutils.resize(img1,width=400)
# img2 = imutils.resize(img2,width=400)
#
# stitcher = cv.Stitcher()
# (result,vis) = stitcher.stitch([img1,img2])
# # (result,vis) = stitch([img1,img2],showMatches=True)
#
# cv.imshow('image A',img1)
# cv.imshow('image B',img2)
# cv.imshow('keyPoint Matches',vis)
# cv.imshow('Result',result)
#
# cv.waitKey(0)
# cv.destroyAllWindows()
# show()

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。