python opencv進(jìn)行圖像拼接
本文實(shí)例為大家分享了python opencv進(jìn)行圖像拼接的具體代碼,供大家參考,具體內(nèi)容如下
思路和方法
思路
1、提取要拼接的兩張圖片的特征點(diǎn)、特征描述符;
2、將兩張圖片中對(duì)應(yīng)的位置點(diǎn)找到,匹配起來(lái);
3、如果找到了足夠多的匹配點(diǎn),就能將兩幅圖拼接起來(lái),拼接前,可能需要將第二幅圖透視旋轉(zhuǎn)一下,利用找到的關(guān)鍵點(diǎn),將第二幅圖透視旋轉(zhuǎn)到一個(gè)與第一幅圖相同的可以拼接的角度;
4、進(jìn)行拼接;
5、進(jìn)行拼接后的一些處理,讓效果看上去更好。
實(shí)現(xiàn)方法
1、提取圖片的特征點(diǎn)、描述符,可以使用opencv創(chuàng)建一個(gè)SIFT對(duì)象,SIFT對(duì)象使用DoG方法檢測(cè)關(guān)鍵點(diǎn),并對(duì)每個(gè)關(guān)鍵點(diǎn)周圍的區(qū)域計(jì)算特征向量。在實(shí)現(xiàn)時(shí),可以使用比SIFT快的SURF方法,使用Hessian算法檢測(cè)關(guān)鍵點(diǎn)。因?yàn)橹皇沁M(jìn)行全景圖拼接,在使用SURF時(shí),還可以調(diào)節(jié)它的參數(shù),減少一些關(guān)鍵點(diǎn),只獲取64維而不是128維的向量等,加快速度。
2、在分別提取好了兩張圖片的關(guān)鍵點(diǎn)和特征向量以后,可以利用它們進(jìn)行兩張圖片的匹配。在拼接圖片中,可以使用Knn進(jìn)行匹配,但是使用FLANN快速匹配庫(kù)更快,圖片拼接,需要用到FLANN的單應(yīng)性匹配。
3、單應(yīng)性匹配完之后可以獲得透視變換H矩陣,用這個(gè)的逆矩陣來(lái)對(duì)第二幅圖片進(jìn)行透視變換,將其轉(zhuǎn)到和第一張圖一樣的視角,為下一步拼接做準(zhǔn)備。
4、透視變換完的圖片,其大小就是最后全景圖的大小,它的右邊是透視變換以后的圖片,左邊是黑色沒(méi)有信息。拼接時(shí)可以比較簡(jiǎn)單地處理,通過(guò)numpy數(shù)組選擇直接把第一張圖加到它的左邊,覆蓋掉重疊部分,得到拼接圖片,這樣做非常快,但是最后效果不是很好,中間有一條分割痕跡非常明顯。使用opencv指南中圖像金字塔的代碼對(duì)拼接好的圖片進(jìn)行處理,整個(gè)圖片平滑了,中間的縫還是特別突兀。
5、直接拼效果不是很好,可以把第一張圖疊在左邊,但是對(duì)第一張圖和它的重疊區(qū)做一些加權(quán)處理,重疊部分,離左邊圖近的,左邊圖的權(quán)重就高一些,離右邊近的,右邊旋轉(zhuǎn)圖的權(quán)重就高一些,然后兩者相加,使得過(guò)渡是平滑地,這樣看上去效果好一些,速度就比較慢。如果是用SURF來(lái)做,時(shí)間主要畫(huà)在平滑處理上而不是特征點(diǎn)提取和匹配。
python_opencv中主要使用的函數(shù)
0、基于python 3.7和對(duì)應(yīng)的python-opencv
1、cv2.xfeatures2d.SURF_create ([hessianThreshold[, nOctaves[, nOctaveLayers[, extended[, upright]]]]])
該函數(shù)用于生成一個(gè)SURF對(duì)象,在使用時(shí),為提高速度,可以適當(dāng)提高h(yuǎn)essianThreshold,以減少檢測(cè)的關(guān)鍵點(diǎn)的數(shù)量,可以extended=False,只生成64維的描述符而不是128維,令upright=True,不檢測(cè)關(guān)鍵點(diǎn)的方向。
2、cv2.SURF.detectAndCompute(image, mask[, descriptors[, useProvidedKeypoints]])
該函數(shù)用于計(jì)算圖片的關(guān)鍵點(diǎn)和描述符,需要對(duì)兩幅圖都進(jìn)行計(jì)算。
3、flann=cv2.FlannBasedMatcher(indexParams,searchParams)
match=flann.knnMatch(descrip1,descrip2,k=2)
flann快速匹配器有兩個(gè)參數(shù),一個(gè)是indexParams,一個(gè)是searchParams,都用手冊(cè)上建議的值就可以。在創(chuàng)建了匹配器得到匹配數(shù)組match以后,就可以參考Lowe給出的參數(shù),對(duì)匹配進(jìn)行過(guò)濾,過(guò)濾掉不好的匹配。其中返回值match包括了兩張圖的描述符距離distance 、訓(xùn)練圖(第二張)的描述符索引trainIdx 、查詢的圖(第一張)的描述符索引queryIdx 這幾個(gè)屬性。
4、M,mask=cv2.findHomography(srcPoints, dstPoints[, method[, ransacReprojThreshold[, mask]]])
這個(gè)函數(shù)實(shí)現(xiàn)單應(yīng)性匹配,返回的M是一個(gè)矩陣,即對(duì)關(guān)鍵點(diǎn)srcPoints做M變換能變到dstPoints的位置。
5、warpImg=cv2.warpPerspective(src,np.linalg.inv(M),dsize[,dst[,flags[,borderMode[,borderValue]]]])
用這個(gè)函數(shù)進(jìn)行透視變換,變換視角。src是要變換的圖片,np.linalg.inv(M)是④中M的逆矩陣,得到方向一致的圖片。
6、a=b.copy() 實(shí)現(xiàn)深度復(fù)制,Python中默認(rèn)是按引用復(fù)制,a=b是a指向b的內(nèi)存。
7、draw_params = dict(matchColor = (0,255,0),singlePointColor = (255,0,0),matchesMask = matchMask,flags = 2),img3 = cv2.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params)
使用drawMatches可以畫(huà)出匹配的好的關(guān)鍵點(diǎn),matchMask是比較好的匹配點(diǎn),之間用綠色線連接起來(lái)。
核心代碼
import cv2
import numpy as np
from matplotlib import pyplot as plt
import time
MIN = 10
starttime=time.time()
img1 = cv2.imread('1.jpg') #query
img2 = cv2.imread('2.jpg') #train
#img1gray=cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
#img2gray=cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
surf=cv2.xfeatures2d.SURF_create(10000,nOctaves=4,extended=False,upright=True)
#surf=cv2.xfeatures2d.SIFT_create()#可以改為SIFT
kp1,descrip1=surf.detectAndCompute(img1,None)
kp2,descrip2=surf.detectAndCompute(img2,None)
FLANN_INDEX_KDTREE = 0
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=[]
for i,(m,n) in enumerate(match):
if(m.distance<0.75*n.distance):
good.append(m)
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(img2, np.linalg.inv(M), (img1.shape[1]+img2.shape[1], img2.shape[0]))
direct=warpImg.copy()
direct[0:img1.shape[0], 0:img1.shape[1]] =img1
simple=time.time()
#cv2.namedWindow("Result", cv2.WINDOW_NORMAL)
#cv2.imshow("Result",warpImg)
rows,cols=img1.shape[:2]
for col in range(0,cols):
if img1[:, col].any() and warpImg[:, col].any():#開(kāi)始重疊的最左端
left = col
break
for col in range(cols-1, 0, -1):
if img1[:, col].any() and warpImg[:, col].any():#重疊的最右一列
right = col
break
res = np.zeros([rows, cols, 3], np.uint8)
for row in range(0, rows):
for col in range(0, cols):
if not img1[row, col].any():#如果沒(méi)有原圖,用旋轉(zhuǎn)的填充
res[row, col] = warpImg[row, col]
elif not warpImg[row, col].any():
res[row, col] = img1[row, col]
else:
srcImgLen = float(abs(col - left))
testImgLen = float(abs(col - right))
alpha = srcImgLen / (srcImgLen + testImgLen)
res[row, col] = np.clip(img1[row, col] * (1-alpha) + warpImg[row, col] * alpha, 0, 255)
warpImg[0:img1.shape[0], 0:img1.shape[1]]=res
final=time.time()
img3=cv2.cvtColor(direct,cv2.COLOR_BGR2RGB)
plt.imshow(img3,),plt.show()
img4=cv2.cvtColor(warpImg,cv2.COLOR_BGR2RGB)
plt.imshow(img4,),plt.show()
print("simple stich cost %f"%(simple-starttime))
print("\ntotal cost %f"%(final-starttime))
cv2.imwrite("simplepanorma.png",direct)
cv2.imwrite("bestpanorma.png",warpImg)
else:
print("not enough matches!")
運(yùn)行結(jié)果
原圖1.jpg

原圖2.jpg

特征點(diǎn)匹配

直接拼接和平滑對(duì)比

效果

本文已被收錄到專題《python圖片處理操作》 ,歡迎大家點(diǎn)擊學(xué)習(xí)更多精彩內(nèi)容。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Python中關(guān)于字符串對(duì)象的一些基礎(chǔ)知識(shí)
這篇文章主要介紹了詳解Python中的字符串對(duì)象,關(guān)于字符串的操作和特性是Python學(xué)習(xí)當(dāng)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-04-04
python使用多線程+socket實(shí)現(xiàn)端口掃描
這篇文章主要為大家詳細(xì)介紹了python使用多線程+socket實(shí)現(xiàn)端口掃描,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
一文詳解凱撒密碼的原理及Python實(shí)現(xiàn)
凱撒密碼是古羅馬愷撒大帝用來(lái)對(duì)軍事情報(bào)進(jìn)行加密的算法,它采用了替換方法對(duì)信息中的每一個(gè)英文字符循環(huán)替換為字母表序列該字符后面第三個(gè)字符。本文主要為大家講解了凱撒密碼的原理及實(shí)現(xiàn),需要的可以參考一下2022-08-08
Python中的高級(jí)函數(shù)map/reduce使用實(shí)例
這篇文章主要介紹了Python中的高級(jí)函數(shù)map/reduce使用實(shí)例,Python內(nèi)建了map()和reduce()函數(shù),本文就講解如何使用它,需要的朋友可以參考下2015-04-04
Python編程使用tkinter模塊實(shí)現(xiàn)計(jì)算器軟件完整代碼示例
這篇文章主要介紹了Python編程實(shí)現(xiàn)一個(gè)計(jì)算器軟件完整代碼示例,簡(jiǎn)單介紹了Tkinter的相關(guān)內(nèi)容,然后分享了通過(guò)tkinter模塊開(kāi)發(fā)一個(gè)計(jì)算器的完整Python代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-11-11
Linux安裝Python3如何和系統(tǒng)自帶的Python2并存
這篇文章主要介紹了Linux安裝Python3如何和系統(tǒng)自帶的Python2并存,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
用Python實(shí)現(xiàn)一個(gè)簡(jiǎn)單的能夠上傳下載的HTTP服務(wù)器
這篇文章主要介紹了用Python實(shí)現(xiàn)一個(gè)簡(jiǎn)單的能夠上傳下載的HTTP服務(wù)器,是Python網(wǎng)絡(luò)編程學(xué)習(xí)當(dāng)中的基礎(chǔ),本文示例基于Windows操作系統(tǒng)實(shí)現(xiàn),需要的朋友可以參考下2015-05-05
PyQt5學(xué)習(xí)之QThread類的使用詳解
QThread是Qt線程類中最核心的底層類。要使用QThrea開(kāi)始一個(gè)線程,可以創(chuàng)建它的一個(gè)子類,然后覆蓋其QThread.run()函數(shù)。這篇文章就來(lái)和大家聊聊QThread類的使用,感興趣的可以學(xué)習(xí)一下2022-12-12

