?一.簡介
圖像拼接(Image Stitching)是一種利用實景圖像組成全景空間的技術(shù),它將多幅圖像拼接成一幅大尺度圖像或360度全景圖,接可以看做是場景重建的一種特殊情況,其中圖像僅通過平面單應性進行關(guān)聯(lián)。圖像拼接在運動檢測和跟蹤,增強現(xiàn)實,分辨率增強,視頻壓縮和圖像穩(wěn)定等機器視覺領(lǐng)域有很大的應用。圖像拼接的輸出是兩個輸入圖像的并集。通常用到四個步驟:(1)特征提取(Feature Extraction):檢測輸入圖像中的特征點。(2)圖像配準(Image Registration):建立了圖像之間的幾何對應關(guān)系,使它們可以在一個共同的參照系中進行變換、比較和分析。(3)圖像變形(Warping):圖像變形是指將其中一幅圖像的圖像重投影,并將圖像放置在更大的畫布上。(4)圖像融合(Blending):圖像融合是通過改變邊界附近的圖像灰度級,去除這些縫隙,創(chuàng)建混合圖像,從而在圖像之間實現(xiàn)平滑過渡?;旌夏J?Blend modes)用于將兩層融合到一起。
二.實現(xiàn)方法
(1)用SIFT提取圖像中的特征點,并對每個關(guān)鍵點周圍的區(qū)域計算特征向量??梢允褂帽萐IFT快的SURF方法,但是我的opencv版本為最新版,不知道是專利的原因還是什么原因用SURF = cv2.xfeatures2D.SURF_create ()實例化的時候會報錯,網(wǎng)上說可以退opencv版本,但是我這里沒有嘗試,就用了sift = cv2.SIFT_create()。
(2)在分別提取好了兩張圖片的關(guān)鍵點和特征向量以后,可以利用它們進行兩張圖片的匹配。在拼接圖片中,可以使用Knn進行匹配,但是使用FLANN快速匹配庫更快,圖片拼接,需要用到FLANN的單應性匹配。
(3)單應性匹配完之后可以獲得透視變換H矩陣,用這個的逆矩陣來對第二幅圖片進行透視變換,將其轉(zhuǎn)到和第一張圖一樣的視角,為下一步拼接做準備。
(4)透視變化完后就可以直接拼接圖片了,將圖片通過numpy直接加到透視變化完成的圖像的左邊,覆蓋掉重合的部分,得到拼接圖片,但是這樣拼接得圖片中間會有一條很明顯的縫隙,可以通過加權(quán)平均法,界線的兩側(cè)各取一定的比例來融合縫隙,速度快,但不自然?;蛘哂鸹ǎ蛘呃绽菇鹱炙诤希Ч詈?。在這里用的是加權(quán)平均法,可以把第一張圖疊在左邊,但是對第一張圖和它的重疊區(qū)做一些加權(quán)處理,重疊部分,離左邊圖近的,左邊圖的權(quán)重就高一些,離右邊近的,右邊旋轉(zhuǎn)圖的權(quán)重就高一些,然后兩者相加,使得過渡是平滑地,這樣看上去效果好一些,速度就比較慢。
三.實驗圖片
?
?
四.實驗
4.1 直接拼接
代碼如下:
#導入庫
import cv2
import numpy as np
import sys
from PIL import Image
#圖像顯示函數(shù)
def show(name,img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
#讀取輸入圖片
ima = cv2.imread("p2.jpg")
imb = cv2.imread("p1.jpg")
A = ima.copy()
B = imb.copy()
imageA = cv2.resize(A,(0,0),fx=0.2,fy=0.2)
imageB = cv2.resize(B,(0,0),fx=0.2,fy=0.2)
#檢測A、B圖片的SIFT關(guān)鍵特征點,并計算特征描述子
def detectAndDescribe(image):
# 建立SIFT生成器
sift = cv2.SIFT_create()
# 檢測SIFT特征點,并計算描述子
(kps, features) = sift.detectAndCompute(image, None)
# 將結(jié)果轉(zhuǎn)換成NumPy數(shù)組
kps = np.float32([kp.pt for kp in kps])
# 返回特征點集,及對應的描述特征
return (kps, features)
#檢測A、B圖片的SIFT關(guān)鍵特征點,并計算特征描述子
kpsA, featuresA = detectAndDescribe(imageA)
kpsB, featuresB = detectAndDescribe(imageB)
# 建立暴力匹配器
bf = cv2.BFMatcher()
# 使用KNN檢測來自A、B圖的SIFT特征匹配對,K=2
matches = bf.knnMatch(featuresA, featuresB, 2)
good = []
for m in matches:
# 當最近距離跟次近距離的比值小于ratio值時,保留此匹配對
if len(m) == 2 and m[0].distance < m[1].distance * 0.75:
# 存儲兩個點在featuresA, featuresB中的索引值
good.append((m[0].trainIdx, m[0].queryIdx))
# 當篩選后的匹配對大于4時,計算視角變換矩陣
if len(good) > 4:
# 獲取匹配對的點坐標
ptsA = np.float32([kpsA[i] for (_, i) in good])
ptsB = np.float32([kpsB[i] for (i, _) in good])
# 計算視角變換矩陣
H, status = cv2.findHomography(ptsA, ptsB, cv2.RANSAC,4.0)
# 匹配兩張圖片的所有特征點,返回匹配結(jié)果
M = (matches, H, status)
# 如果返回結(jié)果為空,沒有匹配成功的特征點,退出程序
if M is None:
print("無匹配結(jié)果")
sys.exit()
# 否則,提取匹配結(jié)果
# H是3x3視角變換矩陣
(matches, H, status) = M
# 將圖片A進行視角變換,result是變換后圖片
result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
# 將圖片B傳入result圖片最左端
result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB
show('res',result)
print(result.shape)
直接憑借得到的結(jié)果如下:
我們可以看到在圖像拼接出有明顯的縫隙:
4.2 進行Multi-band Blending處理縫隙
代碼如下:
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(r'D:\software\pycharm\PycharmProjects\computer-version\data\p1.jpg') #query
img2 = cv2.imread(r'D:\software\pycharm\PycharmProjects\computer-version\data\p2.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()
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=[]
#過濾特征點
for i,(m,n) in enumerate(match):
if(m.distance<0.75*n.distance):
good.append(m)
# 當篩選后的匹配對大于10時,計算視角變換矩陣
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
# Multi-band Blending算法
levels = 6
gaussian = cv2.getGaussianKernel(5, 0)
gaussian_pyramid_imageA = [imageA]
gaussian_pyramid_imageB = [warpImg]
laplacian_pyramid_imageA = [imageA]
laplacian_pyramid_imageB = [warpImg]
for i in range(levels):
gaussian_imageA = cv2.pyrDown(gaussian_pyramid_imageA[i])
gaussian_imageB = cv2.pyrDown(gaussian_pyramid_imageB[i])
gaussian_pyramid_imageA.append(gaussian_imageA)
gaussian_pyramid_imageB.append(gaussian_imageB)
for i in range(levels, 0, -1):
laplacian_imageA = cv2.subtract(gaussian_pyramid_imageA[i-1], cv2.pyrUp(gaussian_pyramid_imageA[i], dstsize=gaussian_pyramid_imageA[i-1].shape[:2]))
laplacian_imageB = cv2.subtract(gaussian_pyramid_imageB[i-1], cv2.pyrUp(gaussian_pyramid_imageB[i], dstsize=gaussian_pyramid_imageB[i-1].shape[:2]))
laplacian_pyramid_imageA.append(laplacian_imageA)
laplacian_pyramid_imageB.append(laplacian_imageB)
gaussian_pyramid_mask = [np.ones((imageA.shape[0]//(2**levels), imageA.shape[1]//(2**levels)), np.float32)]
for i in range(levels):
gaussian_mask = cv2.pyrDown(gaussian_pyramid_mask[i])
gaussian_pyramid_mask.append(gaussian_mask)
laplacian_pyramid = []
n = 0
for laplacian_imageA, laplacian_imageB, gaussian_mask in zip(laplacian_pyramid_imageA, laplacian_pyramid_imageB, gaussian_pyramid_mask[::-1]):
rows, cols, dpt = laplacian_imageA.shape
n += 1
laplacian = np.zeros((rows, cols, dpt), np.float32)
for row in range(rows):
for col in range(cols):
if gaussian_mask[row, col] == 1:
laplacian[row, col] = laplacian_imageA[row, col]
else:
laplacian[row, col] = laplacian_imageB[row, col]
laplacian_pyramid.append(laplacian)
#重建圖像
image_reconstruct = laplacian_pyramid[0]
for i in range(1, levels):
image_reconstruct = cv2.pyrUp(image_reconstruct, dstsize=laplacian_pyramid[i].shape[:2])
image_reconstruct = cv2.add(image_reconstruct, laplacian_pyramid[i])
for row in range(0, imageA.shape[0]):
for col in range(0, left):
if image_reconstruct[row, col].all() == 0:
image_reconstruct[row, col] = imageA[row, col]
cv2.imshow('result', image_reconstruct)
cv2.waitKey(0)
cv2.destroyAllWindows()
結(jié)果如下:
?文章來源:http://www.zghlxwxcb.cn/news/detail-697613.html
?分析:從圖中可以看出使用Multi-band Blending進行圖像平滑后,拼接處的縫隙得到了改善。但是不知道什么原因,左邊圖像是黑色的。查閱資料發(fā)現(xiàn):如果圖片旁出現(xiàn)了黑色部分,是因為圖片無法填充滿,可以試試拍攝時角度變化大一些??梢試L試通過blending進行融合來達到更好的效果。但是經(jīng)過試驗也沒有解決。文章來源地址http://www.zghlxwxcb.cn/news/detail-697613.html
到了這里,關(guān)于計算機視覺----圖像拼接的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!