立體匹配是計(jì)算機(jī)視覺(jué)中的一個(gè)重要領(lǐng)域,旨在將從不同角度拍攝的圖像匹配起來(lái),以創(chuàng)建類似人類視覺(jué)的3D效果。實(shí)現(xiàn)立體匹配的過(guò)程需要涉及許多步驟,包括雙目標(biāo)定、立體校正、視差計(jì)算等。在這篇文章中,將介紹如何使用Python實(shí)現(xiàn)立體匹配的基本步驟和技巧。
下面的代碼實(shí)現(xiàn)了從相機(jī)標(biāo)定到立體匹配的完整流程,下面將分別介紹各個(gè)函數(shù)的參數(shù)和輸出。
標(biāo)定
首先,該程序需要用到以下庫(kù):
numpy
cv2(OpenCV)
os
在程序開(kāi)頭,需要定義一些變量來(lái)存儲(chǔ)標(biāo)定圖片的路徑、棋盤格參數(shù)、角點(diǎn)坐標(biāo)等等。具體介紹如下:
path_left="./data/left/"
path_right="./data/right/"
path_left和path_right是左右相機(jī)標(biāo)定圖片文件夾的路徑。
CHESSBOARD_SIZE=(8,11)
CHESSBOARD_SQUARE_SIZE=15#mm
CHESSBOARD_SIZE是棋盤格內(nèi)部角點(diǎn)的行列數(shù),CHESSBOARD_SQUARE_SIZE是棋盤格內(nèi)部每個(gè)小正方形的大小(單位為毫米)。
objp=np.zeros((CHESSBOARD_SIZE[0]*CHESSBOARD_SIZE[1],3),np.float32)
objp[:,:2]=np.mgrid[0:CHESSBOARD_SIZE[0],0:CHESSBOARD_SIZE[1]].T.reshape(-1,2)*CHESSBOARD_SQUARE_SIZE
objp是物理坐標(biāo)系下每個(gè)角點(diǎn)的三維坐標(biāo),即棋盤格的位置。該變量在后續(xù)的相機(jī)標(biāo)定以及立體匹配中都會(huì)被用到。
criteria=(cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER,30,0.001)
criteria是角點(diǎn)檢測(cè)的終止準(zhǔn)則,一般都使用這個(gè)默認(rèn)值。
img_list_left=sorted(os.listdir(path_left))
img_list_right=sorted(os.listdir(path_right))
img_list_left和img_list_right分別是左、右圖像的文件名列表,使用os.listdir()函數(shù)獲取。
obj_points=[]
img_points_left=[]
img_points_right=[]
obj_points、img_points_left和img_points_right分別是存儲(chǔ)每個(gè)標(biāo)定圖片對(duì)應(yīng)的物理坐標(biāo)系下的角點(diǎn)坐標(biāo)、左相機(jī)的像素坐標(biāo)和右相機(jī)的像素坐標(biāo)。這些變量同樣在后續(xù)的相機(jī)標(biāo)定和立體匹配中用到。
接下來(lái),程序讀取標(biāo)定圖片并檢測(cè)角點(diǎn)。對(duì)于每幅圖片,程序執(zhí)行以下操作:
img_l=cv2.imread(path_left+img_list_left[i])
img_r=cv2.imread(path_right+img_list_right[i])
gray_l=cv2.cvtColor(img_l,cv2.COLOR_BGR2GRAY)
gray_r=cv2.cvtColor(img_r,cv2.COLOR_BGR2GRAY)
首先讀取左右圖像,然后將它們轉(zhuǎn)換為灰度圖像。
ret_l,corners_l=cv2.findChessboardCorners(gray_l,CHESSBOARD_SIZE,None)
ret_r,corners_r=cv2.findChessboardCorners(gray_r,CHESSBOARD_SIZE,None)
通過(guò)OpenCV的cv2.findChessboardCorners()函數(shù)檢測(cè)左右圖像上的棋盤格角點(diǎn)。這個(gè)函數(shù)的參數(shù)包括:
image:需要檢測(cè)角點(diǎn)的灰度圖像。patternSize:內(nèi)部角點(diǎn)的行列數(shù),即(CHESSBOARD_SIZE[1]-1, CHESSBOARD_SIZE[0]-1)。corners:用于存儲(chǔ)檢測(cè)到的角點(diǎn)坐標(biāo)的數(shù)組。如果檢測(cè)失敗,則該參數(shù)為空(None)。flags:檢測(cè)時(shí)使用的可選標(biāo)志。這個(gè)函數(shù)的返回值包括:
ret:一個(gè)布爾值,用于指示檢測(cè)是否成功。如果檢測(cè)成功,則為True,否則為False。corners:用于存儲(chǔ)檢測(cè)到的角點(diǎn)坐標(biāo)的數(shù)組。接下來(lái)是亞像素級(jí)別的角點(diǎn)檢測(cè)。
cv2.cornerSubPix(gray_l,corners_l,(11,11),(-1,-1),criteria)
cv2.cornerSubPix(gray_r,corners_r,(11,11),(-1,-1),criteria)
這里使用了OpenCV的cv2.cornerSubPix()函數(shù)來(lái)進(jìn)行亞像素級(jí)別的角點(diǎn)檢測(cè)。這個(gè)函數(shù)的參數(shù)包括:
image:輸入的灰度圖像。
corners:用于存儲(chǔ)檢測(cè)到的角點(diǎn)坐標(biāo)的數(shù)組。
winSize:每次迭代中搜索窗口的大小,即每個(gè)像素周圍的搜索范圍大小。通常為11x11。
zeroZone:死區(qū)大小,表示怎樣的對(duì)稱性(如果有的話)不考慮。通常為(-1,-1)。
criteria:定義迭代停止的誤差范圍、迭代次數(shù)等標(biāo)準(zhǔn),和以上的criteria一樣。
img_points_left.append(corners_l)
img_points_right.append(corners_r)
如果檢測(cè)到了左右圖像上的角點(diǎn),則將這些角點(diǎn)的坐標(biāo)存儲(chǔ)到img_points_left和img_points_right中。
cv2.drawChessboardCorners(img_l,CHESSBOARD_SIZE,corners_l,ret_l)
cv2.imshow("ChessboardCorners-Left",cv2.resize(img_l,(img_l.shape[1]//2,img_l.shape[0]//2)))
cv2.waitKey(50)
cv2.drawChessboardCorners(img_r,CHESSBOARD_SIZE,corners_r,ret_r)
cv2.imshow("ChessboardCorners-Right",cv2.resize(img_r,(img_r.shape[1]//2,img_r.shape[0]//2)))
cv2.waitKey(50)
在圖片上標(biāo)出檢測(cè)到的角點(diǎn),并在窗口中顯示。這里使用了cv2.drawChessboardCorners()函數(shù),該函數(shù)的參數(shù)包括:
img:需要標(biāo)定角點(diǎn)的圖像。patternSize:內(nèi)部角點(diǎn)的行列數(shù),即(CHESSBOARD_SIZE[1]-1, CHESSBOARD_SIZE[0]-1)。
corners:存儲(chǔ)檢測(cè)到的角點(diǎn)坐標(biāo)的數(shù)組。patternfound:檢測(cè)到角點(diǎn)的標(biāo)記,即ret。
程序接下來(lái)對(duì)雙目攝像機(jī)進(jìn)行標(biāo)定。
ret_l,mtx_l,dist_l,rvecs_l,tvecs_l=cv2.calibrateCamera(obj_points,img_points_left,gray_l.shape[::-1],None,None)
ret_r,mtx_r,dist_r,rvecs_r,tvecs_r=cv2.calibrateCamera(obj_points,img_points_right,gray_r.shape[::-1],None,None)
flags=0
flags|=cv2.CALIB_FIX_INTRINSIC
criteria=(cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER,30,0.001)
ret,M1,d1,M2,d2,R,T,E,F=cv2.stereoCalibrate(
obj_points,img_points_left,img_points_right,
mtx_l,dist_l,mtx_r,dist_r,
gray_l.shape[::-1],criteria=criteria,flags=flags)
這段代碼首先對(duì)左右相機(jī)進(jìn)行單獨(dú)標(biāo)定:
ret_l,mtx_l,dist_l,rvecs_l,tvecs_l=cv2.calibrateCamera(obj_points,img_points_left,gray_l.shape[::-1],None,None)
ret_r,mtx_r,dist_r,rvecs_r,tvecs_r=cv2.calibrateCamera(obj_points,img_points_right,gray_r.shape[::-1],None,None)
這里使用了OpenCV的cv2.calibrateCamera()函數(shù)對(duì)左右相機(jī)進(jìn)行標(biāo)定。這個(gè)函數(shù)的參數(shù)包括:
objectPoints:每幅標(biāo)定圖片對(duì)應(yīng)的物理坐標(biāo)系下的角點(diǎn)坐標(biāo)。
imagePoints:每幅標(biāo)定圖片上檢測(cè)到的像素坐標(biāo)。
imageSize:標(biāo)定圖片的尺寸。
cameraMatrix:用于存儲(chǔ)標(biāo)定結(jié)果的內(nèi)參數(shù)矩陣。
distCoeffs:用于存儲(chǔ)標(biāo)定結(jié)果的畸變系數(shù)。
rvecs:每幅標(biāo)定圖片的外參數(shù)矩陣中的旋轉(zhuǎn)向量。
tvecs:每幅標(biāo)定圖片的外參數(shù)矩陣中的平移向量。
這個(gè)函數(shù)的返回值包括:
ret:一個(gè)標(biāo)志位,表示標(biāo)定是否成功。
cameraMatrix:用于存儲(chǔ)標(biāo)定結(jié)果的內(nèi)參數(shù)矩陣。
distCoeffs:用于存儲(chǔ)標(biāo)定結(jié)果的畸變系數(shù)。
rvecs:每幅標(biāo)定圖片的外參數(shù)矩陣中的旋轉(zhuǎn)向量。
tvecs:每幅標(biāo)定圖片的外參數(shù)矩陣中的平移向量。
然后對(duì)雙目攝像機(jī)進(jìn)行標(biāo)定:
flags=0
flags|=cv2.CALIB_FIX_INTRINSIC
criteria=(cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER,30,0.001)
ret,M1,d1,M2,d2,R,T,E,F=cv2.stereoCalibrate(
obj_points,img_points_left,img_points_right,
mtx_l,dist_l,mtx_r,dist_r,
gray_l.shape[::-1],criteria=criteria,flags=flags)
這里使用了OpenCV的cv2.stereoCalibrate()函數(shù)進(jìn)行雙目攝像機(jī)標(biāo)定。這個(gè)函數(shù)的參數(shù)包括:
objectPoints:每幅標(biāo)定圖片對(duì)應(yīng)的物理坐標(biāo)系下的角點(diǎn)坐標(biāo)。
imagePoints1:每幅標(biāo)定圖片的左相機(jī)上檢測(cè)到的像素坐標(biāo)。
imagePoints2:每幅標(biāo)定圖片的右相機(jī)上檢測(cè)到的像素坐標(biāo)。
cameraMatrix1:左相機(jī)的內(nèi)參數(shù)矩陣。
distCoeffs1:左相機(jī)的畸變系數(shù)。
cameraMatrix2:右相機(jī)的內(nèi)參數(shù)矩陣。
distCoeffs2:右相機(jī)的畸變系數(shù)。
imageSize:標(biāo)定圖片的尺寸。
criteria:定義迭代停止的誤差范圍、迭代次數(shù)等標(biāo)準(zhǔn)。
flags:標(biāo)定的可選標(biāo)志。
這個(gè)函數(shù)的返回值包括:
ret:一個(gè)標(biāo)志,表示標(biāo)定是否成功。
cameraMatrix1:左相機(jī)的內(nèi)參數(shù)矩陣。
distCoeffs1:左相機(jī)的畸變系數(shù)。
cameraMatrix2:右相機(jī)的內(nèi)參數(shù)矩陣。
distCoeffs2:右相機(jī)的畸變系數(shù)。
R:旋轉(zhuǎn)矩陣。
T:平移向量。
E:本質(zhì)矩陣。
F:基礎(chǔ)矩陣。
立體匹配
通過(guò)圖像標(biāo)定得到的參數(shù)進(jìn)行立體匹配的整個(gè)流程,如下:
首先,我們需要讀取左右兩張圖像:
img_left=cv2.imread("./left.png")
img_right=cv2.imread("./right.png")
其中,"./left.png" 和 "./right.png" 是放置左右圖像的路徑。這兩幅圖像是未經(jīng)校正和矯正的圖像。
接下來(lái),通過(guò)圖像標(biāo)定得到相機(jī)的參數(shù),根據(jù)得到的參數(shù),將圖像進(jìn)行去畸變:
img_left_undistort=cv2.undistort(img_left,M1,d1)
img_right_undistort=cv2.undistort(img_right,M2,d2)
在上述代碼中,M1、M2、d1、d2 是從雙目相機(jī)標(biāo)定中獲得的參數(shù)。去畸變后的圖像 img_left_undistort 和 img_right_undistort 可供之后的操作使用。
然后,進(jìn)行極線校正,以實(shí)現(xiàn)左右圖像在幾何上的一致性:
R1,R2,P1,P2,Q,roi1,roi2=cv2.stereoRectify(M1,d1,M2,d2,(width,height),R,T,alpha=1)
map1x,map1y=cv2.initUndistortRectifyMap(M1,d1,R1,P1,(width,height),cv2.CV_32FC1)
map2x,map2y=cv2.initUndistortRectifyMap(M2,d2,R2,P2,(width,height),cv2.CV_32FC1)
img_left_rectified=cv2.remap(img_left_undistort,map1x,map1y,cv2.INTER_LINEAR)
img_right_rectified=cv2.remap(img_right_undistort,map2x,map2y,cv2.INTER_LINEAR)
其中,R、T 是雙目相機(jī)標(biāo)定得到的旋轉(zhuǎn)和平移矩陣, (width, height)是左右圖像的尺寸。R1、R2 是左右圖像的旋轉(zhuǎn)矩陣,P1、P2 是左右圖像的投影矩陣,Q 是視差轉(zhuǎn)換矩陣,roi1、roi2 是矯正后的圖像中可以使用的區(qū)域。
然后,將左右圖像拼接在一起以方便觀察:
img_stereo=cv2.hconcat([img_left_rectified,img_right_rectified])
接下來(lái),需要計(jì)算視差圖:
minDisparity=0
numDisparities=256
blockSize=9
P1=1200
P2=4800
disp12MaxDiff=10
preFilterCap=63
uniquenessRatio=5
speckleWindowSize=100
speckleRange=32
sgbm=cv2.StereoSGBM_create(minDisparity=minDisparity,numDisparities=numDisparities,blockSize=blockSize,
P1=P1,P2=P2,disp12MaxDiff=disp12MaxDiff,preFilterCap=preFilterCap,
uniquenessRatio=uniquenessRatio,speckleWindowSize=speckleWindowSize,
speckleRange=speckleRange,mode=cv2.STEREO_SGBM_MODE_SGBM_3WAY)
disparity=sgbm.compute(img_left_rectified,img_right_rectified)
上面的代碼塊定義了使用的視差算法的參數(shù),并使用了 SGBM(Semi Global Block Matching)算法計(jì)算了原始的視差圖。注意,由于使用的是16位的 SGBM 輸出,因此需要將它除以16。接下來(lái),可以對(duì)視差圖進(jìn)行 WLS 濾波,減少視差空洞:
#定義WLS濾波參數(shù)
lambda_val=4000
sigma_val=1.5
#運(yùn)行WLS濾波
wls_filter=cv2.ximgproc.createDisparityWLSFilterGeneric(False)
wls_filter.setLambda(lambda_val)
wls_filter.setSigmaColor(sigma_val)
filtered_disp=wls_filter.filter(disparity,img_left_rectified,None,img_right_rectified)
filtered_disp_nor=cv2.normalize(filtered_disp,filtered_disp,alpha=0,beta=255,norm_type=cv2.NORM_MINMAX,dtype=cv2.CV_8U)
上述代碼塊中,WLS 濾波為視差圖降噪,并進(jìn)行平滑處理。這里使用了 cv2.ximgproc.createDisparityWLSFilterGeneric 函數(shù),創(chuàng)建一個(gè)生成 WLS 濾波器的對(duì)象 wls_filter,然后設(shè)置了濾波參數(shù) lambda_val 和 sigma_val。filtered_disp 是經(jīng)過(guò)濾波后的視差圖。filtered_disp_nor 是經(jīng)過(guò)歸一化處理后的、用于顯示的視差圖。
最后,可以在窗口中顯示原始視差圖、預(yù)處理后的 WLS 濾波器的視差圖:
cv2.imshow("disparity",cv2.resize(disparity_nor,(disparity_nor.shape[1]//2,disparity_nor.shape[0]//2)))
cv2.imshow("filtered_disparity",cv2.resize(filtered_disp_nor,(filtered_disp_nor.shape[1]//2,filtered_disp_nor.shape[0]//2)))
cv2.waitKey()
cv2.destroyAllWindows()
-
3D
+關(guān)注
關(guān)注
9文章
2910瀏覽量
107796 -
計(jì)算機(jī)視覺(jué)
+關(guān)注
關(guān)注
8文章
1700瀏覽量
46075 -
python
+關(guān)注
關(guān)注
56文章
4807瀏覽量
84951
原文標(biāo)題:從雙目標(biāo)定到立體匹配:Python實(shí)踐指南
文章出處:【微信號(hào):3D視覺(jué)工坊,微信公眾號(hào):3D視覺(jué)工坊】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論