opencv +python實(shí)現(xiàn)倒車車道的檢測(cè)及擬合

?特別聲明:鑒于本人之前的文章在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)圖。
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)幫助

還請(qǐng)各網(wǎng)站水積分的網(wǎng)友不要隨意抄襲轉(zhuǎn)載,請(qǐng)事先經(jīng)過(guò)我的同意謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容