相机校准
學(xué)習(xí)內(nèi)容:
- 由相機(jī)引起的失真類型,
- 如何找到相機(jī)的固有和非固有特性
- 如何根據(jù)這些特性使圖像不失真。
一些針孔相機(jī)會(huì)給圖像帶來(lái)明顯的失真。兩種主要的變形是徑向變形和切向變形。 徑向變形會(huì)導(dǎo)致直線出現(xiàn)彎曲。
距圖像中心越遠(yuǎn),徑向畸變?cè)酱蟆@?#xff0c;下面顯示一個(gè)圖像,其中棋盤(pán)的兩個(gè)邊緣用紅線標(biāo)記。但是,您會(huì)看到棋盤(pán)的邊框不是直線,并且與紅線不匹配。所有預(yù)期的直線都凸出。
徑向變形可以表示成如下:
xdistorted=x(1+k1r2+k2r4+k3r6)ydistorted=y(1+k1r2+k2r4+k3r6)x_{distorted} = x( 1 + k_1 r^2 + k_2 r^4 + k_3 r^6) \ y_{distorted} = y( 1 + k_1 r^2 + k_2 r^4 + k_3 r^6) xdistorted?=x(1+k1?r2+k2?r4+k3?r6)?ydistorted?=y(1+k1?r2+k2?r4+k3?r6)
同樣,由于攝像鏡頭未完全平行于成像平面對(duì)齊,因此會(huì)發(fā)生切向畸變。因此,圖像中的某些區(qū)域看起來(lái)可能比預(yù)期的要近。切向畸變的量可以表示為:
xdistorted=x+[2p1xy+p2(r2+2x2)]ydistorted=y+[p1(r2+2y2)+2p2xy]x_{distorted} = x + [ 2p_1xy + p_2(r^2+2x^2)] \ y_{distorted} = y + [ p_1(r^2+ 2y^2)+ 2p_2xy] xdistorted?=x+[2p1?xy+p2?(r2+2x2)]?ydistorted?=y+[p1?(r2+2y2)+2p2?xy]
簡(jiǎn)而言之,我們需要找到五個(gè)參數(shù),稱為失真系數(shù),公式如下:
Distortioncoefficients=(k1k2p1p2k3)Distortion \; coefficients=(k_1 \hspace{10pt} k_2 \hspace{10pt} p_1 \hspace{10pt} p_2 \hspace{10pt} k_3) Distortioncoefficients=(k1?k2?p1?p2?k3?)
除此之外,我們還需要其他一些信息,例如相機(jī)的內(nèi)在和外在參數(shù)。內(nèi)部參數(shù)特定于攝像機(jī)。它們包括諸如焦距(fx,fy)(f_x,f_y)(fx?,fy?)和光學(xué)中心(cx,cy)(c_x,c_y)(cx?,cy?)之類的信息。焦距和光學(xué)中心可用于創(chuàng)建相機(jī)矩陣,該相機(jī)矩陣可用于消除由于特定相機(jī)鏡頭而引起的畸變。相機(jī)矩陣對(duì)于特定相機(jī)而言是唯一的,因此一旦計(jì)算出,就可以在同一相機(jī)拍攝的其他圖像上重復(fù)使用。它表示為3x3矩陣:
cameramatrix=[fx0cx0fycy001]camera \; matrix = \left [ \begin{matrix} f_x & 0 & c_x \ 0 & f_y & c_y \ 0 & 0 & 1 \end{matrix} \right ] cameramatrix=[fx??0?cx??0?fy??cy??0?0?1?]
外在參數(shù)對(duì)應(yīng)于旋轉(zhuǎn)和平移矢量,其將3D點(diǎn)的坐標(biāo)平移為坐標(biāo)系。
對(duì)于立體聲應(yīng)用,首先需要糾正這些失真。要找到這些參數(shù),我們必須提供一些定義良好的圖案的示例圖像(例如國(guó)際象棋棋盤(pán))。我們找到一些已經(jīng)知道其相對(duì)位置的特定點(diǎn)(例如棋盤(pán)上的四角)。我們知道現(xiàn)實(shí)世界空間中這些點(diǎn)的坐標(biāo),也知道圖像中的坐標(biāo),因此我們可以求解失真系數(shù)。為了獲得更好的結(jié)果,我們至少需要10個(gè)測(cè)試模式。
代碼
如上所述,相機(jī)校準(zhǔn)至少需要10個(gè)測(cè)試圖案。OpenCV附帶了一些國(guó)際象棋棋盤(pán)的圖像(請(qǐng)參見(jiàn)samples / data / left01.jpg – left14.jpg),因此我們將利用這些圖像。考慮棋盤(pán)的圖像。相機(jī)校準(zhǔn)所需的重要輸入數(shù)據(jù)是3D現(xiàn)實(shí)世界點(diǎn)集以及圖像中這些點(diǎn)的相應(yīng)2D坐標(biāo)。可以從圖像中輕松找到2D圖像點(diǎn)。(這些圖像點(diǎn)是國(guó)際象棋棋盤(pán)中兩個(gè)黑色正方形相互接觸的位置)
真實(shí)世界中的3D點(diǎn)如何處理?這些圖像是從靜態(tài)相機(jī)拍攝的,而國(guó)際象棋棋盤(pán)放置在不同的位置和方向。因此,我們需要知道(X,Y,Z)(X,Y,Z)(X,Y,Z)值。但是為簡(jiǎn)單起見(jiàn),我們可以說(shuō)棋盤(pán)在XY平面上保持靜止(因此Z始終為0),并且照相機(jī)也相應(yīng)地移動(dòng)了。這種考慮有助于我們僅找到X,Y值。現(xiàn)在對(duì)于X,Y值,我們可以簡(jiǎn)單地將點(diǎn)傳遞為(0,0),(1,0),(2,0),…,這表示點(diǎn)的位置。在這種情況下,我們得到的結(jié)果將是棋盤(pán)正方形的大小比例。但是,如果我們知道正方形大小(例如30毫米),則可以將值傳遞為(0,0),(30,0),(60,0),…。因此,我們得到的結(jié)果以毫米為單位。(在這種情況下,我們不知道正方形的大小,因?yàn)槲覀儧](méi)有拍攝那些圖像,因此我們以正方形的大小進(jìn)行傳遞)。
3D點(diǎn)稱為對(duì)象點(diǎn),而2D圖像點(diǎn)稱為圖像點(diǎn)。
完整代碼示例:
以下是其步驟:
首先
**要在國(guó)際象棋棋盤(pán)中查找圖案,我們可以使用函數(shù)cv.findChessboardCorners()。**我們還需要傳遞所需的圖案,例如8x8網(wǎng)格,5x5網(wǎng)格等。在此示例中,我們使用7x6網(wǎng)格。(通常,棋盤(pán)有8x8的正方形和7x7的內(nèi)部角)。它返回角點(diǎn)和retval,如果獲得圖案,則為T(mén)rue。這些角將按順序放置(從左到右,從上到下)
此功能可能無(wú)法在所有圖像中找到所需的圖案。因此,一個(gè)不錯(cuò)的選擇是編寫(xiě)代碼,使它啟動(dòng)相機(jī)并檢查每幀所需的圖案。獲得圖案后,找到角并將其存儲(chǔ)在列表中。另外,在閱讀下一幀之前請(qǐng)?zhí)峁┮恍r(shí)間間隔,以便我們可以在不同方向上調(diào)整棋盤(pán)。繼續(xù)此過(guò)程,直到獲得所需數(shù)量的良好圖案為止。即使在此處提供的示例中,我們也不確定給出的14張圖像中有多少?gòu)埵呛玫摹?/p>
因此,我們必須閱讀所有圖像并僅拍攝好圖像。 除了棋盤(pán),我們還可以使用圓形網(wǎng)格。 在這種情況下,我們必須使用函數(shù)cv.findCirclesGrid()來(lái)找到模式。 較少的圖像足以使用圓形網(wǎng)格執(zhí)行相機(jī)校準(zhǔn)。
其次,
一旦找到拐角,就可以使用cv.cornerSubPix()來(lái)提高其精度。我們還可以使用cv.drawChessboardCorners()繪制圖案。所有這些步驟都包含在以下代碼中:
運(yùn)行結(jié)果:
校準(zhǔn)
現(xiàn)在我們有了目標(biāo)點(diǎn)和圖像點(diǎn),現(xiàn)在可以進(jìn)行校準(zhǔn)了。我們可以使用函數(shù)cv.calibrateCamera()返回相機(jī)矩陣,失真系數(shù),旋轉(zhuǎn)和平移矢量等。
ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
現(xiàn)在,我們可以拍攝圖像并對(duì)其進(jìn)行扭曲。OpenCV提供了兩種方法來(lái)執(zhí)行此操作。但是,首先,我們可以使用**cv.getOptimalNewCameraMatrix()基于自由縮放參數(shù)來(lái)優(yōu)化相機(jī)矩陣。**如果縮放參數(shù)alpha = 0,則返回具有最少不需要像素的未失真圖像。因此,它甚至可能會(huì)刪除圖像角落的一些像素。如果alpha = 1,則所有像素都保留有一些額外的黑色圖像。此函數(shù)還返回可用于裁剪結(jié)果的圖像ROI。
因此,我們拍攝一張新圖像(在本例中為left12.jpg。這是本章的第一張圖像)
img = cv.imread(‘left12.jpg’)
h, w = img.shape[:2]
newcameramtx, roi = cv.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))
以下是求解結(jié)果的幾種方式:
1.使用cv.undistort(),調(diào)用該函數(shù)并使用上面獲得的ROI裁剪結(jié)果。
dst = cv.undistort(img, mtx, dist, None, newcameramtx)
剪裁圖像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv.imwrite(‘calibresult.png’, dst)
2. 使用remapping
首先,找到從扭曲圖像到未扭曲圖像的映射函數(shù)。然后使用重映射功能。
mapx, mapy = cv.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w,h), 5)
dst = cv.remap(img, mapx, mapy, cv.INTER_LINEAR)
x, y, w, h = roi # 裁剪圖像
dst = dst[y:y+h, x:x+w]
cv.imwrite(‘calibresult.png’, dst)
盡管如此,兩種方法都給出相同的結(jié)果。看到下面的結(jié)果:
您可以看到所有邊緣都是筆直的。 現(xiàn)在,您可以使用NumPy中的寫(xiě)入功能(np.savez,np.savetxt等)存儲(chǔ)相機(jī)矩陣和失真系數(shù),以備將來(lái)使用。
重投影誤差
重投影誤差可以很好地估計(jì)找到的參數(shù)的精確程度。重投影誤差越接近零,我們發(fā)現(xiàn)的參數(shù)越準(zhǔn)確。給定固有,失真,旋轉(zhuǎn)和平移矩陣,我們必須首先使用**cv.projectPoints()將對(duì)象點(diǎn)轉(zhuǎn)換為圖像點(diǎn)。**然后,我們可以計(jì)算出通過(guò)變換得到的絕對(duì)值和拐角發(fā)現(xiàn)算法之間的絕對(duì)值范數(shù)。為了找到平均誤差,我們計(jì)算為所有校準(zhǔn)圖像計(jì)算的誤差的算術(shù)平均值。
mean_error = 0
for i in xrange(len(objpoints)):
imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2)/len(imgpoints2)
mean_error += error
print( “total error: {}”.format(mean_error/len(objpoints)) )
總結(jié)
- 上一篇: Python ValueError: c
- 下一篇: SSD(based on Caffe)环