目錄
1,主要步驟
1.1??導(dǎo)入需要的包和模塊,并讀取兩張待拼接的圖片,這里我們假設(shè)它們?yōu)?left.jpg 和 right.jpg。
1.2? 創(chuàng)建SIFT檢測(cè)器
1.3?創(chuàng)建一個(gè)基于 FLANN 的匹配器
1.4??篩選過程刪除掉一些不合適的匹配點(diǎn),只保留最好的匹配點(diǎn)
1.5透視變換
1.6??消除重疊的效果,對(duì)兩張圖片進(jìn)行加權(quán)處理
2,代碼展示
3,效果展示
應(yīng)用場(chǎng)景主要有兩個(gè)方面:
- 風(fēng)景或建筑物的拍攝
對(duì)于一些風(fēng)景或建筑物的拍攝,有時(shí)候需要的畫面寬度超出了單張圖片所能提供的視野范圍。這時(shí)可以通過拍攝多張圖片并將它們拼接成一張更加寬闊的全景圖來達(dá)到所需的效果。
- 科學(xué)研究
在一些科學(xué)研究中,需要對(duì)一定的區(qū)域進(jìn)行高精度測(cè)量,例如地形測(cè)量、海洋測(cè)量等。這時(shí)候就需要一些寬視野相機(jī)來實(shí)現(xiàn)拍攝。但是,由于一張圖片所能覆蓋的區(qū)域有限,因此通常還需要將多張圖片拼接成一張更大的全景圖像,方便科學(xué)家們進(jìn)行研究和分析。
1,主要步驟
- 讀入待拼接的圖片并調(diào)整大??;
- 使用 SIFT 或 SURF 算法提取圖片的關(guān)鍵點(diǎn)和描述符;
- 使用基于 FLANN 的匹配器進(jìn)行關(guān)鍵點(diǎn)匹配,并篩選出較好的匹配點(diǎn);
- 計(jì)算視角變換矩陣,并使用透視變換對(duì)右邊的圖片進(jìn)行變換;
- 消除重疊的效果,對(duì)兩張圖片進(jìn)行加權(quán)處理;
- 輸出拼接后的結(jié)果。
1.1??導(dǎo)入需要的包和模塊,并讀取兩張待拼接的圖片,這里我們假設(shè)它們?yōu)?left.jpg 和 right.jpg。
左視圖:
右視圖:
1.2? 創(chuàng)建SIFT檢測(cè)器
cv2.xfeatures2d.SIFT_create()
創(chuàng)建一個(gè) SIFT 檢測(cè)器。也可以選擇使用
cv2.SIFT_create()
不過前者是更新的版本,可能會(huì)更好一些
然后,在兩張圖片上分別使用這個(gè)檢測(cè)器進(jìn)行關(guān)鍵點(diǎn)檢測(cè)和特征提取,獲得關(guān)鍵點(diǎn)集合和描述符集合。
surf=cv2.xfeatures2d.SIFT_create()#可以改為SIFT
#sift = cv2.SIFT_create()
sift = cv2.xfeatures2d.SIFT_create()
kp1,descrip1 = sift.detectAndCompute(imageA,None)
kp2,descrip2 = sift.detectAndCompute(imageB,None)
1.3?創(chuàng)建一個(gè)基于 FLANN 的匹配器
調(diào)用
cv2.FlannBasedMatcher()
創(chuàng)建一個(gè)基于 FLANN 的匹配器,并使用knnMatch()
處理兩張圖片的特征描述符,得到最佳匹配。
indexParams = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
searchParams = dict(checks=50)
flann=cv2.FlannBasedMatcher(indexParams,searchParams)
match=flann.knnMatch(descrip1,descrip2,k=2)
good=[]
1.4??篩選過程刪除掉一些不合適的匹配點(diǎn),只保留最好的匹配點(diǎn)
for i,(m,n) in enumerate(match):
if(m.distance<0.75*n.distance):
good.append(m)
1.5透視變換
判斷滿足條件的匹配點(diǎn)數(shù)量是否大于閾值
MIN
,如果大于,則進(jìn)行視角變換矩陣的計(jì)算,將右邊的圖片imageB
對(duì)其進(jìn)行透視變換,得到warpImg
。
if len(good) > MIN:
src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
ano_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)
M,mask = cv2.findHomography(src_pts,ano_pts,cv2.RANSAC,5.0)
warpImg = cv2.warpPerspective(imageB, np.linalg.inv(M), (imageA.shape[1]+imageB.shape[1], imageB.shape[0]))
direct=warpImg.copy()
direct[0:imageA.shape[0], 0:imageB.shape[1]] =imageA
simple=time.time()
show('res',warpImg)
同時(shí),將左邊的圖片覆蓋在變換后的圖片上,得到 direct
。最后,顯示結(jié)果。
print(rows)
print(cols)
for col in range(0,cols):
# 開始重疊的最左端
if imageA[:, col].any() and warpImg[:, col].any():
left = col
print(left)
break
for col in range(cols-1, 0, -1):
#重疊的最右一列
if imageA[:, col].any() and warpImg[:, col].any():
right = col
print(right)
break
1.6??消除重疊的效果,對(duì)兩張圖片進(jìn)行加權(quán)處理
根據(jù)圖片相對(duì)位置的不同,左邊的圖片和右邊的圖片有可能會(huì)在某些列出現(xiàn)重疊部分,為了消除這種不自然的效果,需要實(shí)現(xiàn)像素級(jí)的混合。首先找到左右圖片開始重疊的位置和結(jié)束的位置,然后對(duì)兩張圖片進(jìn)行加權(quán)處理,最后將加權(quán)后的圖片輸出。文章來源:http://www.zghlxwxcb.cn/news/detail-742256.html
#加權(quán)處理
res = np.zeros([rows, cols, 3], np.uint8)
for row in range(0, rows):
for col in range(0, cols):
if not imageA[row, col].any(): # 如果沒有原圖,用旋轉(zhuǎn)的填充
res[row, col] = warpImg[row, col]
elif not warpImg[row, col].any():
res[row, col] = imageA[row, col]
else:
srcImgLen = float(abs(col - left))
testImgLen = float(abs(col - right))
alpha = srcImgLen / (srcImgLen + testImgLen)
res[row, col] = np.clip(imageA[row, col] * (1 - alpha) + warpImg[row, col] * alpha, 0, 255)
warpImg[0:imageA.shape[0], 0:imageA.shape[1]]=res
show('res',warpImg)
final=time.time()
print(final-starttime)
2,代碼展示
import cv2
import numpy as np
from matplotlib import pyplot as plt
import time
def show(name,img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
MIN = 10
FLANN_INDEX_KDTREE = 0
starttime = time.time()
img1 = cv2.imread('left.jpg') #query
img2 = cv2.imread('right.jpg') #train
imageA = cv2.resize(img1,(0,0),fx=0.2,fy=0.2)
imageB = cv2.resize(img2,(0,0),fx=0.2,fy=0.2)
surf=cv2.xfeatures2d.SIFT_create()#可以改為SIFT
#sift = cv2.SIFT_create()
sift = cv2.xfeatures2d.SIFT_create()
kp1,descrip1 = sift.detectAndCompute(imageA,None)
kp2,descrip2 = sift.detectAndCompute(imageB,None)
#創(chuàng)建字典
indexParams = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
searchParams = dict(checks=50)
flann=cv2.FlannBasedMatcher(indexParams,searchParams)
match=flann.knnMatch(descrip1,descrip2,k=2)
good=[]
#過濾特征點(diǎn)
for i,(m,n) in enumerate(match):
if(m.distance<0.75*n.distance):
good.append(m)
# 當(dāng)篩選后的匹配對(duì)大于10時(shí),計(jì)算視角變換矩陣
if len(good) > MIN:
src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
ano_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)
M,mask = cv2.findHomography(src_pts,ano_pts,cv2.RANSAC,5.0)
warpImg = cv2.warpPerspective(imageB, np.linalg.inv(M), (imageA.shape[1]+imageB.shape[1], imageB.shape[0]))
direct=warpImg.copy()
direct[0:imageA.shape[0], 0:imageB.shape[1]] =imageA
simple=time.time()
show('res',warpImg)
rows,cols=imageA.shape[:2]
print(rows)
print(cols)
for col in range(0,cols):
# 開始重疊的最左端
if imageA[:, col].any() and warpImg[:, col].any():
left = col
print(left)
break
for col in range(cols-1, 0, -1):
#重疊的最右一列
if imageA[:, col].any() and warpImg[:, col].any():
right = col
print(right)
break
#加權(quán)處理
res = np.zeros([rows, cols, 3], np.uint8)
for row in range(0, rows):
for col in range(0, cols):
if not imageA[row, col].any(): # 如果沒有原圖,用旋轉(zhuǎn)的填充
res[row, col] = warpImg[row, col]
elif not warpImg[row, col].any():
res[row, col] = imageA[row, col]
else:
srcImgLen = float(abs(col - left))
testImgLen = float(abs(col - right))
alpha = srcImgLen / (srcImgLen + testImgLen)
res[row, col] = np.clip(imageA[row, col] * (1 - alpha) + warpImg[row, col] * alpha, 0, 255)
warpImg[0:imageA.shape[0], 0:imageA.shape[1]]=res
show('res',warpImg)
final=time.time()
print(final-starttime)
3,效果展示
文章來源地址http://www.zghlxwxcb.cn/news/detail-742256.html
到了這里,關(guān)于OpenCV完美實(shí)現(xiàn)兩張圖片的全景拼接(詳細(xì)教程)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!