本文實例為大家分享了Python+OpenCV實現(xiàn)圖像的全景拼接的具體代碼,供大家參考,具體內(nèi)容如下
成都創(chuàng)新互聯(lián)公司服務(wù)項目包括旌德網(wǎng)站建設(shè)、旌德網(wǎng)站制作、旌德網(wǎng)頁制作以及旌德網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,旌德網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到旌德省份的部分城市,未來相信會繼續(xù)擴大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!環(huán)境:python3.5.2 + openCV3.4
1.算法目的
將兩張相同場景的場景圖片進行全景拼接。
2.算法步驟
本算法基本步驟有以下幾步:
步驟1:將圖形先進行桶形矯正
沒有進行桶形變換的圖片效果可能會像以下這樣:
圖片越多拼接可能就會越夸張。
本算法是將圖片進行桶形矯正。目的就是來縮減透視變換(Homography)之后圖片產(chǎn)生的變形,從而使拼接圖片變得畸形。
步驟2:特征點匹配
本算法使用的sift算法匹配,它具有旋轉(zhuǎn)不變性和縮放不變性,具體原理在之后會補上一篇關(guān)于sift算法的文章,這里就不做詳細(xì)介紹。
在匹配特征點的過程中,透視矩陣選取了4對特征點計算,公式為
點的齊次坐標(biāo)依賴于其尺度定義,因此矩陣H也僅依賴尺度定義,所以,單應(yīng)性矩陣具有8個獨立的自由度。
如果在選取的不正確的特征點,那么透視矩陣就可能計算錯誤,所以為了提高結(jié)果的魯棒性,就要去除這些錯誤的特征點,而RANSAC方法就是用來刪除這些錯誤的特征點。
**RANSAC:**用來找到正確模型來擬合帶有噪聲數(shù)據(jù)的迭代方法。基本思想:數(shù)據(jù)中包含正確的點和噪聲點,合理的模型應(yīng)該能夠在描述正確數(shù)據(jù)點的同時擯棄噪聲點。
RANSAC方法隨機獲取4對不同的特征匹配坐標(biāo),計算出透視矩陣H1,再將第二張圖的特征匹配點經(jīng)過這個矩陣H1映射到第一張圖的坐標(biāo)空間里,通過計算來驗證這個H1矩陣是否滿足絕大部分的特征點。
通過迭代多次,以滿足最多特征匹配點的特征矩陣H作為結(jié)果。
這樣正常情況就可以去除錯誤的特征點了,除非匹配錯誤的特征點比正確的還多。
下圖是我在嘉庚圖書館旁拍攝的照片的特征點匹配。
步驟3:利用得到的變換矩陣進行圖片的拼接。
可以看出基本做到了無縫拼接。只是在色差上還是看得出銜接的部分存在。
實現(xiàn)結(jié)果
我在宿舍里又多照了幾組照片來實驗:
室內(nèi)宿舍場景的特征點匹配:
拼接結(jié)果:
在室內(nèi)的效果根據(jù)結(jié)果來看效果也還可以。
我測試了宿舍里景深落差較大的兩張圖片:
特征點匹配:
雖然距離較遠,但是還是可以粗略的匹配到特征點。
拼接結(jié)果:
從結(jié)果上來看可以看得出來,兩張圖片依然可以正確而粗略地拼接再一起,可以看得出是同一個區(qū)域。只是由于特征點不夠,在細(xì)節(jié)上景深落差較大的還是沒辦法完美地拼接。
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)鍵點和描述符 (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區(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)鍵點和描述符 return (kps, des) def matchKeyPoints(self,kp1, kp2, des1, des2, ratio, reprojThresh): print('C') #初始化BF,因為使用的是SIFT ,所以使用默認(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)) #最少要有四個點才能做透視變換 if len(good) > 4: #獲取關(guā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ān)鍵點計算變換矩陣 (M, mask) = cv.findHomography(src_pts, dst_pts, cv.RANSAC, reprojThresh) #返回最佳匹配點、變換矩陣和掩模 return (good, M, mask) #如果不滿足最少四個 就返回None return None def drawMatches(img1, img2, kp1, kp2, matches, mask, M): # 獲得原圖像的高和寬 h, w = img1.shape[:2] # 使用得到的變換矩陣對原圖像的四個角進行變換,獲得目標(biāo)圖像上對應(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()
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
分享文章:Python+OpenCV實現(xiàn)圖像的全景拼接-創(chuàng)新互聯(lián)
網(wǎng)址分享:http://muchs.cn/article34/ceegpe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)建站、企業(yè)網(wǎng)站制作、網(wǎng)站改版、面包屑導(dǎo)航、移動網(wǎng)站建設(shè)、品牌網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容