Python+OpenCV實(shí)現(xiàn)圖像的全景拼接
本文實(shí)例為大家分享了Python+OpenCV實(shí)現(xiàn)圖像的全景拼接的具體代碼,供大家參考,具體內(nèi)容如下
環(huán)境:python3.5.2 + openCV3.4
1.算法目的
將兩張相同場(chǎng)景的場(chǎng)景圖片進(jìn)行全景拼接。
2.算法步驟
本算法基本步驟有以下幾步:
步驟1:將圖形先進(jìn)行桶形矯正
沒(méi)有進(jìn)行桶形變換的圖片效果可能會(huì)像以下這樣:

圖片越多拼接可能就會(huì)越夸張。

本算法是將圖片進(jìn)行桶形矯正。目的就是來(lái)縮減透視變換(Homography)之后圖片產(chǎn)生的變形,從而使拼接圖片變得畸形。
步驟2:特征點(diǎn)匹配
本算法使用的sift算法匹配,它具有旋轉(zhuǎn)不變性和縮放不變性,具體原理在之后會(huì)補(bǔ)上一篇關(guān)于sift算法的文章,這里就不做詳細(xì)介紹。
在匹配特征點(diǎn)的過(guò)程中,透視矩陣選取了4對(duì)特征點(diǎn)計(jì)算,公式為

點(diǎn)的齊次坐標(biāo)依賴于其尺度定義,因此矩陣H也僅依賴尺度定義,所以,單應(yīng)性矩陣具有8個(gè)獨(dú)立的自由度。
如果在選取的不正確的特征點(diǎn),那么透視矩陣就可能計(jì)算錯(cuò)誤,所以為了提高結(jié)果的魯棒性,就要去除這些錯(cuò)誤的特征點(diǎn),而RANSAC方法就是用來(lái)刪除這些錯(cuò)誤的特征點(diǎn)。
**RANSAC:**用來(lái)找到正確模型來(lái)擬合帶有噪聲數(shù)據(jù)的迭代方法?;舅枷耄簲?shù)據(jù)中包含正確的點(diǎn)和噪聲點(diǎn),合理的模型應(yīng)該能夠在描述正確數(shù)據(jù)點(diǎn)的同時(shí)擯棄噪聲點(diǎn)。
RANSAC方法隨機(jī)獲取4對(duì)不同的特征匹配坐標(biāo),計(jì)算出透視矩陣H1,再將第二張圖的特征匹配點(diǎn)經(jīng)過(guò)這個(gè)矩陣H1映射到第一張圖的坐標(biāo)空間里,通過(guò)計(jì)算來(lái)驗(yàn)證這個(gè)H1矩陣是否滿足絕大部分的特征點(diǎn)。
通過(guò)迭代多次,以滿足最多特征匹配點(diǎn)的特征矩陣H作為結(jié)果。
這樣正常情況就可以去除錯(cuò)誤的特征點(diǎn)了,除非匹配錯(cuò)誤的特征點(diǎn)比正確的還多。
下圖是我在嘉庚圖書館旁拍攝的照片的特征點(diǎn)匹配。

步驟3:利用得到的變換矩陣進(jìn)行圖片的拼接。
可以看出基本做到了無(wú)縫拼接。只是在色差上還是看得出銜接的部分存在。

實(shí)現(xiàn)結(jié)果
我在宿舍里又多照了幾組照片來(lái)實(shí)驗(yàn):
室內(nèi)宿舍場(chǎng)景的特征點(diǎn)匹配:

拼接結(jié)果:

在室內(nèi)的效果根據(jù)結(jié)果來(lái)看效果也還可以。
我測(cè)試了宿舍里景深落差較大的兩張圖片:
特征點(diǎn)匹配:

雖然距離較遠(yuǎn),但是還是可以粗略的匹配到特征點(diǎn)。
拼接結(jié)果:

從結(jié)果上來(lái)看可以看得出來(lái),兩張圖片依然可以正確而粗略地拼接再一起,可以看得出是同一個(gè)區(qū)域。只是由于特征點(diǎn)不夠,在細(xì)節(jié)上景深落差較大的還是沒(méi)辦法完美地拼接。
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
#獲取關(guān)鍵點(diǎn)和描述符
(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éi)有足夠的最佳匹配點(diǎn),M為None
if R is None:
return None
(good, M, mask) = R
print(M)
#對(duì)img1透視變換,M是ROI區(qū)域矩陣, 變換后的大小是(img1.w+img2.w, img1.h)
result = cv.warpPerspective(img1, M, (img1.shape[1] + img2.shape[1], img1.shape[0]))
#將img2的值賦給結(jié)果圖像
result[0:img2.shape[0], 0:img2.shape[1]] = img2
#是否需要顯示ROI區(qū)域
if showMatches:
vis = self.drawMatches1(img1, img2, kp1, kp2, 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]) # **********************************
#返回關(guān)鍵點(diǎn)和描述符
return (kps, des)
def matchKeyPoints(self,kp1, kp2, des1, des2, ratio, reprojThresh):
print('C')
#初始化BF,因?yàn)槭褂玫氖荢IFT ,所以使用默認(rèn)參數(shù)
matcher = cv.DescriptorMatcher_create('BruteForce')
# bf = cv.BFMatcher()
# matches = bf.knnMatch(des1, des2, k=2)
matches = matcher.knnMatch(des1, des2, 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))
#最少要有四個(gè)點(diǎn)才能做透視變換
if len(good) > 4:
#獲取關(guān)鍵點(diǎn)的坐標(biāo)
# 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)
src_pts = np.float32([kp1[i] for (_, i) in good])
dst_pts = np.float32([kp2[i] for (i, _) in good])
#通過(guò)兩個(gè)圖像的關(guān)鍵點(diǎn)計(jì)算變換矩陣
(M, mask) = cv.findHomography(src_pts, dst_pts, cv.RANSAC, reprojThresh)
#返回最佳匹配點(diǎn)、變換矩陣和掩模
return (good, M, mask)
#如果不滿足最少四個(gè) 就返回None
return None
def drawMatches(img1, img2, kp1, kp2, matches, mask, M):
# 獲得原圖像的高和寬
h, w = img1.shape[:2]
# 使用得到的變換矩陣對(duì)原圖像的四個(gè)角進(jìn)行變換,獲得目標(biāo)圖像上對(duì)應(yīng)的坐標(biāo)
pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 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, kp1, img2, kp2, matches, None, **draw_params)
return img
def drawMatches1(self,img1, img2, kp1, kp2, 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, 255, 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()
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
mac 安裝python網(wǎng)絡(luò)請(qǐng)求包requests方法
今天小編就為大家分享一篇mac 安裝python網(wǎng)絡(luò)請(qǐng)求包requests方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06
結(jié)合Python工具使用TfidfVectorizer進(jìn)行文本特征提取方式
在自然語(yǔ)言處理中,TF-IDF是一種重要的特征提取方法,本文介紹了如何使用Python的sklearn庫(kù)中的TfidfVectorizer進(jìn)行文本特征提取,首先,需要安裝sklearn庫(kù),TfidfVectorizer能將文本文檔集合轉(zhuǎn)換為TF-IDF特征矩陣2024-10-10
python模塊詳解之pywin32使用文檔(python操作windowsAPI)
pywin32是一個(gè)第三方模塊庫(kù),主要的作用是方便python開(kāi)發(fā)者快速調(diào)用windows API的一個(gè)模塊庫(kù),這篇文章主要給大家介紹了關(guān)于python模塊詳解之pywin32使用文檔的相關(guān)資料,文中將python操作windowsAPI介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01
Django用戶認(rèn)證系統(tǒng)如何實(shí)現(xiàn)自定義
這篇文章主要介紹了Django用戶認(rèn)證系統(tǒng)如何實(shí)現(xiàn)自定義,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11
Python實(shí)現(xiàn)返回?cái)?shù)組中第i小元素的方法示例
這篇文章主要介紹了Python實(shí)現(xiàn)返回?cái)?shù)組中第i小元素的方法,結(jié)合實(shí)例形式分析了Python針對(duì)數(shù)組的遍歷、排序、運(yùn)算等相關(guān)操作技巧,需要的朋友可以參考下2017-12-12
使用Django+Pytest搭建在線自動(dòng)化測(cè)試平臺(tái)
最近由于公司的發(fā)展安排本人實(shí)現(xiàn)公司項(xiàng)目的自動(dòng)化測(cè)試,下面這篇文章主要給大家介紹了關(guān)于如何Django?+?Pytest搭建在線自動(dòng)化測(cè)試平臺(tái)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07
python3個(gè)性簽名設(shè)計(jì)實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了python3個(gè)性簽名設(shè)計(jì)的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06

