?特別聲明:鑒于本人之前的文章在CSDN及博客園上被多次未署名抄襲及轉(zhuǎn)載且附加廣告,特此聲明,本文禁止抄襲或未經(jīng)作者許可下轉(zhuǎn)載,請(qǐng)各位作者自重!!!!!!!!!!!
?大致要求為從倒車時(shí)攝像頭所拍攝的照片中尋找到車道并將其連接為完整的車道,以平面圖形式呈現(xiàn)。
?方案概述:先將原圖片進(jìn)行投影變化,再運(yùn)用SURF算法匹配圖片間的特征點(diǎn),使用FLANN快速匹配圖片間特征,計(jì)算一張圖片可以變換到另一張圖片的變換矩陣 (homography 單應(yīng)性矩陣),用這個(gè)矩陣把那張圖片變換后放到另一張圖片相應(yīng)的位置 ( 就是相當(dāng)于把兩張圖片中定好的四個(gè)相似的點(diǎn)給重合在一起)。如此,就可以實(shí)現(xiàn)簡(jiǎn)單的全景拼接。這里我們主要利用opencv集成好的的stitching模塊進(jìn)行車道的拼接。
?以下我會(huì)從原理及利用庫(kù)兩方面講解此項(xiàng)目的實(shí)現(xiàn)方案
1.桶形變化
?倒車攝像頭與地面呈一定角度,所拍攝的圖片如果直接進(jìn)行全景拼接,會(huì)使圖片產(chǎn)生較大畸變,及范圍較大的黑邊。這里考慮到倒車攝像頭大都斜向下俯拍,且車道大都呈矩形,我們使用投影變化做圖片的桶形變化。
#讀取圖片
img1 = cv2.imread('/home/yc/Pictures/zuo.jpg')
img2 = cv2.imread('/home/yc/Pictures/you.jpg')
img3 = cv2.imread('/home/yc/Pictures/zuo2.jpg')
#取圖片的形態(tài)學(xué)坐標(biāo)信息
h, w = img1.shape[:2]
h2, w2 = img2.shape[:2]
h3, w3 = img3.shape[:2]
#取出所選變化前圖片的矩形角點(diǎn)
src=np.array([[380,7],[1432,320],[380,1904],[1432,1607]],np.float32)
#取出期望變化后圖片的矩形角點(diǎn)
dst=np.array([[230,0],[1424,1],[230,1912],[1424,1912]],np.float32)
#計(jì)算投影矩陣
P=cv2.getPerspectiveTransform(src,dst)
#對(duì)圖片做投影變化
img12=cv2.warpPerspective(img1,P,(w,h),borderValue=400)
src2=np.array([[0,0],[1432,320],[0,1904],[1432,1904]],np.float32)
dst2=np.array([[0,0],[1424,1],[0,1904],[1424,1912]],np.float32)
#計(jì)算投影矩陣
P2=cv2.getPerspectiveTransform(src2,dst2)
img22=cv2.warpPerspective(img2,P,(w2,h2),borderValue=400)
src3=np.array([[0,0],[1432,320],[0,1904],[1432,1904]],np.float32)
dst3=np.array([[0,0],[1424,1],[0,1904],[1424,1912]],np.float32)
P3=cv2.getPerspectiveTransform(src3,dst3)#計(jì)算投影矩陣
?桶形變化的作用主要就是縮減了邊緣圖像匹配的發(fā)散,盡量讓匹配點(diǎn)的縱坐標(biāo)的差值減小,縮減透視變換后的變形
2.特征檢測(cè)及特征匹配
?這里我們主要采用hessian角點(diǎn)檢測(cè)及surf特征點(diǎn)檢測(cè)算法,檢測(cè)到關(guān)鍵點(diǎn)使用FLANN快速匹配器進(jìn)行特征匹配,要注意,surf算法有版權(quán)限制,請(qǐng)下載舊版opencv依賴庫(kù)。
hessian=10
surf=cv2.cv2.xfeatures2d.SURF_create(hessian)
#將Hessian Threshold設(shè)置為400,閾值越大能檢測(cè)的特征就越少
kp1,des1=surf.detectAndCompute(leftgray,None) #查找關(guān)鍵點(diǎn)和描述符
kp2,des2=surf.detectAndCompute(rightgray,None)
FLANN_INDEX_KDTREE=0#建立FLANN匹配器的參數(shù)
indexParams=dict(algorithm=FLANN_INDEX_KDTREE,trees=5) #配置索引,密度樹的數(shù)量為5
searchParams=dict(checks=50)#指定遞歸次數(shù)
#FlannBasedMatcher:是目前最快的特征匹配算法(最近鄰搜索)
flann=cv2.FlannBasedMatcher(indexParams,searchParams) #建立匹配器
matches=flann.knnMatch(des1,des2,k=2) #得出匹配的關(guān)鍵點(diǎn)
good=[]#提取優(yōu)秀的特征點(diǎn)
for m,n in matches:
if m.distance < 0.7*n.distance: #如果第一個(gè)鄰近距離比第二個(gè)鄰近距離的0.7倍小,則保留
good.append(m)
?有關(guān)特征匹配算法有未曾了解的,可以參考我之前的文章http://www.itdecent.cn/p/bad83a08b1a8
3.利用匹配得到的變換矩陣,合并兩幅圖
?
H=cv2.findHomography(src_pts,dst_pts)#生成變換矩陣
h,w=leftgray.shape[:2]
h1,w1=rightgray.shape[:2]
shft=np.array([[1.0,0,w],[0,1.0,0],[0,0,1.0]])
M=np.dot(shft,H[0]) #獲取左邊圖像到右邊圖像的投影映射關(guān)系
dst_corners=cv2.warpPerspective(leftgray,M,(w*2,h))#透視變換,新圖像可容納完整的兩幅圖
cv2.namedWindow("tiledImg1", cv2.WINDOW_NORMAL)
cv2.imshow('tiledImg1',dst_corners) #顯示,第一幅圖已在標(biāo)準(zhǔn)位置
dst_corners[0:h,w:w*2]=rightgray #將第二幅圖放在右側(cè)
4.stitching模塊的應(yīng)用
Image Stitching 模塊下共包含七個(gè)子模塊,分別為:
.Features Finding and Images Matching 功能查找和圖像匹配
.Rotation Estimation 輪換估計(jì)
.Autocalibration 自動(dòng)校準(zhǔn)
.Images Warping 圖像變形
.Seam Estimation 接縫估計(jì)
.Exposure Compensation 曝光補(bǔ)償
.Image Blenders 圖像攪拌機(jī)
借用一下stitching的官方引導(dǎo)圖。

?基本上是之前三個(gè)步驟的完善版代碼,也很好的克服了合成圖片產(chǎn)生黑邊的問(wèn)題,但合成速度較慢,運(yùn)用在普通全景圖像拼接時(shí)略有小題大做。
?這里講解一下使用python調(diào)用opencv中的stitching模塊的具體應(yīng)用方法。
?因?yàn)轫?xiàng)目所用圖片的特殊性,我們依然需要進(jìn)行第一步的投影變化,糾正拍攝圖形的視角。此步驟參考第一步。
?接著我們調(diào)用兩個(gè)stitching庫(kù)中的函數(shù)cv2.createStitcher以及.stitch,第一個(gè)函數(shù)僅一個(gè)參數(shù)決定調(diào)用打開stitching庫(kù)的方式有try_gpu以及False,鑒于opencv對(duì)GPU的支持很有限,這里我們建議采用False,第二個(gè)函數(shù)即直接調(diào)用stitching拼接圖片,有以下四個(gè)參數(shù):
?OK = 0:圖像拼接成功。
?ERR_NEED_MORE_IMGS = 1:如果您收到此狀態(tài)代碼,則需要更多輸入圖像來(lái)構(gòu)建全景圖。通常,如果輸入圖像中檢測(cè)不到足夠的關(guān)鍵點(diǎn),則會(huì)發(fā)生此錯(cuò)誤。
?ERR_HOMOGRAPHY_EST_FAIL = 2:當(dāng)RANSAC單應(yīng)性估計(jì)失敗時(shí),會(huì)發(fā)生此錯(cuò)誤。同樣,您可能需要更多圖像,或者您的圖像沒(méi)有足夠的區(qū)別,獨(dú)特的紋理/對(duì)象,以便準(zhǔn)確匹配關(guān)鍵點(diǎn)。
?ERR_CAMERA_PARAMS_ADJUST_FAIL = 3:我之前從未遇到過(guò)這個(gè)錯(cuò)誤,所以我對(duì)它沒(méi)有多少了解,但要點(diǎn)是它與未能從輸入圖像中正確估計(jì)相機(jī)內(nèi)參/外參有關(guān)。如果遇到此錯(cuò)誤,您可能需要參考OpenCV文檔,甚至可以深入了解OpenCV C ++代碼。
#-*- coding:utf-8 -*-
import numpy as np
import cv2
from cv2 import Stitcher
import matplotlib.pyplot as plt
if __name__ == "__main__":
img1 = cv2.imread('/home/yc/Pictures/zuo.jpg')
img2 = cv2.imread('/home/yc/Pictures/you.jpg')
img3 = cv2.imread('/home/yc/Pictures/zuo2.jpg')
h, w = img1.shape[:2]
h2, w2 = img2.shape[:2]
h3, w3 = img3.shape[:2]
src=np.array([[380,7],[1432,320],[380,1904],[1432,1607]],np.float32)
dst=np.array([[230,0],[1424,1],[230,1912],[1424,1912]],np.float32)
P=cv2.getPerspectiveTransform(src,dst)#計(jì)算投影矩陣
img12=cv2.warpPerspective(img1,P,(w,h),borderValue=400)
src2=np.array([[0,0],[1432,320],[0,1904],[1432,1904]],np.float32)
dst2=np.array([[0,0],[1424,1],[0,1904],[1424,1912]],np.float32)
P2=cv2.getPerspectiveTransform(src2,dst2)#計(jì)算投影矩陣
img22=cv2.warpPerspective(img2,P,(w2,h2),borderValue=400)
src3=np.array([[0,0],[1432,320],[0,1904],[1432,1904]],np.float32)
dst3=np.array([[0,0],[1424,1],[0,1904],[1424,1912]],np.float32)
P3=cv2.getPerspectiveTransform(src3,dst3)#計(jì)算投影矩陣
img23=cv2.warpPerspective(img3,P,(w3,h3),borderValue=400)
stitcher = cv2.createStitcher(False)
#stitcher = cv2.Stitcher.create(cv2.Stitcher_PANORAMA)根據(jù)不同的OpenCV版本來(lái)調(diào)用
(_result, out) = stitcher.stitch((img12, img22, img23))
rows3, cols3 = out.shape[:2]
#旋轉(zhuǎn)圖片
M1 = cv2.getRotationMatrix2D((w / 2, h / 2), 90, 1)
M2 = cv2.getRotationMatrix2D((w2 / 2, h2 / 2), 90, 1)
M3 = cv2.getRotationMatrix2D((w3 / 2, h3 / 2), 90, 1)
#M4 = cv2.getRotationMatrix2D((cols3 / 2, rows3 / 2), 90, 1)
img31 = cv2.warpAffine(img1, M1, (w, h))
img32 = cv2.warpAffine(img2, M2, (w2, h2))
img33 = cv2.warpAffine(img3, M3, (w3, h3))
#imgout = cv2.warpAffine(out, M4, (cols3, rows3))
#拼接三張?jiān)瓐D
img4=np.vstack([img33,img32,img31])
titles = ['orgin', 'result']
imgs = [img4,out]
#畫出圖形
for i in range(2):
plt.subplot(1,2,i+1)
#plt.plot([0,10],[0,5])
#plt.subplots_adjust(left=0.09,right=1,wspace=0.25,hspace=0.25,bottom=0.13,top=0.91)
plt.imshow(imgs[i])
plt.title(titles[i])
plt.show()
以下是樣圖所呈現(xiàn)的效果,左邊為拍攝的原圖,右邊為所拼接的車道圖片

希望我的教程可以給一起學(xué)習(xí)opencv的同學(xué)一點(diǎn)幫助