OpenCV-14-相機(jī)模型與標(biāo)定

1 前言

對現(xiàn)實(shí)世界的光線感受最終形成視覺乖杠,這些光線從一些光源發(fā)出匣缘,如燈泡和太陽瘦棋,它們穿越空間直至碰到一些物體稀火。然后大多數(shù)光被物體吸收,剩下的光反射進(jìn)入了我們的眼睛或者相機(jī)赌朋,最終被視網(wǎng)膜或者成像器收集凰狞,從而形成圖像。這些過程中的幾何學(xué)沛慢,尤其是光的傳播對于計算機(jī)視覺而言尤其重要赡若。

一個演示該過程的簡單有效模型是針孔相機(jī)(Pinhole Camera)模型,針孔相機(jī)基本模型是一個在中心有著很小孔洞的虛擬墻壁团甲,光線只能通過小孔透過逾冬。本章我們首先會通過該模型介紹投影光線的基本幾何學(xué)知識。遺憾的是躺苦,真正的針孔并不是一個很好的成像方式身腻,因?yàn)樗荒軌蚴占阶銐虻墓饩€。這也是為什么我們的眼睛和相機(jī)都使用了透鏡收集更多的光線匹厘。但是這種方式也有缺點(diǎn)嘀趟,它會使用到更復(fù)雜的幾何學(xué)知識,并且還會因?yàn)橥哥R本身引入畸變(Distoritions)現(xiàn)象愈诚。

本章我們會學(xué)習(xí)通過數(shù)學(xué)手段她按,如何使用相機(jī)校準(zhǔn)技術(shù)(Mathematically)消除因使用透鏡導(dǎo)致的,和使用簡單針孔模型的主要誤差炕柔。相機(jī)校準(zhǔn)對于聯(lián)系相機(jī)測量和真實(shí)三維世界中的測量也很重要酌泰,因?yàn)閳鼍安粌H僅是三維的,并且它們還是有著物理單位的物理空間汗唱。因此相機(jī)自然單位(像素)和物理世界單位(如米)是重建三維場景的關(guān)鍵組成部分。

相機(jī)校準(zhǔn)的過程不僅給出了相機(jī)的幾何模型丈攒,還給出了透鏡的畸變模型(Distortion Model)哩罪。這兩個包含豐富信息的模型定義了相機(jī)的內(nèi)在參數(shù)(Intrinsic Parameters)。本章我們將使用這些模型來校準(zhǔn)透鏡畸變巡验。下一章講使用它們來闡述物理場景的整個幾何結(jié)構(gòu)际插。

首先我們會討論相機(jī)模型和透鏡畸變的原因,然后會講到單應(yīng)變換显设,它是能夠反應(yīng)相機(jī)基本行為框弛、畸變和校準(zhǔn)特征的數(shù)學(xué)工具。接下來會仔細(xì)討論這種能夠歸納特定相機(jī)特征的變換是如何通過數(shù)學(xué)方法被計算出來的捕捂。最后會介紹OpenCV中提供的相關(guān)函數(shù)瑟枫,以及如何使用它們來完成特定任務(wù)斗搞。

本章闡述的所有知識都是為了讓你有足夠的理論知識儲備,從而能夠真正理解函數(shù)cv::calibrateCamera()慷妙,以及它內(nèi)部的原理僻焚。如果你想要正確的使用該函數(shù),這些知識是必要的膝擂。當(dāng)然如果你已經(jīng)是這方面的專家虑啤,并且只想知道OpenCV中是如何實(shí)現(xiàn)這些你已經(jīng)了解到知識的話,那么你可以直接跳到Calibration Function部分架馋。另外在附錄B的ccalib部分還介紹了更多的校準(zhǔn)模式和技術(shù)狞山。

2 相機(jī)模型

首先介紹最簡單的針孔相機(jī)模型,物體每個點(diǎn)只發(fā)出一條光線穿過針孔叉寂,并投影到成像平面(Image Plane萍启,也稱Projective Plane)上。圖像平面和光軸的交點(diǎn)稱為焦點(diǎn)办绝,需要這里的焦點(diǎn)和后文講到的透鏡的焦點(diǎn)含義并不相同伊约。下圖是一個理想的針孔相機(jī)。

則針孔到成像平面的距離f正好是相機(jī)的焦距(Focal Length)(其實(shí)應(yīng)該稱為投影距離, Projection Distance)孕蝉,Z是物體到相機(jī)的距離即物距屡律,X和x分別表示物體和圖像的長度,則他們之間滿足如下關(guān)系降淮。

接下來使用一個等效的模型來演示針孔成像超埋,從而讓我們更容易理解其中的數(shù)學(xué)原理。在下圖中佳鳖,將成像平面放在小孔平面和物體之間霍殴,仍然假定物體的每個點(diǎn)只有一束光線射向針孔,即投影中心系吩,這樣光線和成像平面點(diǎn)交點(diǎn)就能顯示出圖像来庭。圖像平面和光軸點(diǎn)交點(diǎn)稱為主點(diǎn)(Principal Point)。顯然在該模型中運(yùn)用相似三角形原理更容易推導(dǎo)出公式x/f = X/Z穿挨。

圖中點(diǎn)Q(X,YZ)經(jīng)過投影后月弛,在圖像平面上的到點(diǎn)q(x,y,z)。通常物體的位置參數(shù)以米等物理度量為單位科盛,而成像平面上的點(diǎn)在表示時使用的單位為像素帽衙,即點(diǎn)Point(xscreen, yscreen)。另外通常相機(jī)的成像平面即內(nèi)部的感光原件贞绵,的中心并不是嚴(yán)格和光軸對齊的厉萝,可能存在偏移,偏移量使用Dis(cx, cy)表示,其單位為像素谴垫。結(jié)合上文的成像公式章母,則點(diǎn)Point的計算公式如下。

上式中fx等于f??sx弹渔,fy等于f??sy胳施,其中f為相機(jī)焦距,而sx和sy分別是x方向和y方向上的像素和距離比例肢专,即每毫米啊包含多少個像素舞肆。這里使用sx和sy兩個變量的原因是有些低成本的相機(jī)感光原件在x和y軸方向上的像素密度并不相同。需要注意焦距f和像素密度系數(shù)(sx, sy)并不能在相機(jī)校準(zhǔn)過程中直接測量博杖,但是fx和和fy可以直接計算椿胯。

2.1 投影幾何

將現(xiàn)實(shí)空間中的一系列點(diǎn)集Q(Xi, Yi, Zi)映射到屏幕上的點(diǎn)集q(xi, yi)的過程稱為投影變換(Projective Transform)。在這個過程中需要使用齊次坐標(biāo)是非常方便的剃根,它將n維投影空間中的點(diǎn)坐標(biāo)擴(kuò)展到n+1維上哩盲,如三位投影空間中的點(diǎn)p(x, y, z)使用點(diǎn)pn(x, y, z, w),并認(rèn)為當(dāng)兩點(diǎn)的坐標(biāo)成比例時它們實(shí)際上是同一個點(diǎn)狈醉。

在上述的例子中廉油,投影平面為二維圖像平面,因此我們使用三維向量q=(q1, q2, q3)來表示其中的點(diǎn)苗傅∈阆撸考慮到最終的坐標(biāo)為qf=(q1/q3, q2/q3, 1),因此上文的乘法公式可以表示為如下矩陣形式渣慕。其中Q為現(xiàn)實(shí)世界坐標(biāo)嘶炭,q為投影變換后的坐標(biāo),矩陣M稱為相機(jī)內(nèi)參矩陣逊桦。需要注意為qf=(q1/q3, q2/q3, 1)才是最終坐標(biāo)眨猎。

OpenCV內(nèi)部提供的坐標(biāo)轉(zhuǎn)換函數(shù)如下。

// 從笛卡爾坐標(biāo)轉(zhuǎn)換為齊次坐標(biāo)
// src:N維笛卡兒坐標(biāo)
// dst:N+1維齊次坐標(biāo)强经,第N+1維度用1補(bǔ)齊
void cv::convertPointsToHomogeneous(cv::InputArray src, cv::OutputArray dst);

// 從齊次坐標(biāo)轉(zhuǎn)換為笛卡爾坐標(biāo)
// src:N維齊次坐標(biāo)
// dst:N-1維笛卡兒坐標(biāo)睡陪,前N-1維分量都除以第N維分量,最后再丟棄最后一維
void cv::convertPointsFromHomogeneous(cv::InputArray src, cv::OutputArray dst);

上述兩個函數(shù)內(nèi)部使用的數(shù)學(xué)公式如下匿情。

盡管理想的針孔是一個可用的視覺三維幾何模型兰迫,但是因?yàn)獒樋淄ㄟ^的光線很有限,因此在實(shí)際使用時成像很慢码秉,傳感器需要時間收集到足夠的光線逮矛。為了使相機(jī)成像更快鸡号,我們需要在更寬的區(qū)域收集光線,并將這些光線彎曲并匯聚到投影平面的同一個點(diǎn)上,因此我們使用透鏡劳坑,但是透鏡同時也會產(chǎn)生扭曲毕谴。

2.2 Rodrigues變換

在處理三維空間中的向量旋轉(zhuǎn)時,大多數(shù)時候都是用3??3的矩陣完成距芬。但是矩陣形式不利于直觀理解涝开,另外一張旋轉(zhuǎn)的表現(xiàn)形式是是用一個向量,向量的方向表示旋轉(zhuǎn)軸的方向框仔,向量的模表示繞軸逆時針旋轉(zhuǎn)的角度舀武。

向量形式和矩陣形式的關(guān)系用Rodrigues變換表示,假定一個旋轉(zhuǎn)過程是用香料形式表示為r(rx, ry, rz)存和,它的方向是旋轉(zhuǎn)軸的正方向奕剃,這里使用右手螺旋法則,其長度θ為逆時針旋轉(zhuǎn)的角度捐腿,旋轉(zhuǎn)矩陣計算公式如下纵朋。這里省略了推導(dǎo)過程,其中rrt表示使用標(biāo)準(zhǔn)化后的r向量構(gòu)建一個矩陣茄袖,矩陣的每個元素都是操软,對應(yīng)行列序號在原向量內(nèi)部元素的值的乘積。

類似的通過旋轉(zhuǎn)矩陣我們也能計算出向量形式宪祥。

OpenCV提供如下函數(shù)用于處理這兩種形式的轉(zhuǎn)換聂薪。

// 在向量形式和矩陣形式之間轉(zhuǎn)換,輸入和輸出一定為不同的形式// src:輸入旋轉(zhuǎn)向量(3??1)或者矩陣(3??3)
// dst:輸出旋轉(zhuǎn)矩陣(3??3)或者向量(3??1)
// jacobian:輸出矩陣元素相對于輸入矩陣元素的偏導(dǎo)數(shù)蝗羊,尺寸為3??9或者9??3
// 該矩陣用于函數(shù)cv::solvePnP()和函數(shù)cv::calibrateCamera()內(nèi)部優(yōu)化
// 大多數(shù)時間我們的使用場景都是將上述兩個函數(shù)的輸出數(shù)據(jù)藏澳,即向量形式轉(zhuǎn)換為矩陣形式,此時該參數(shù)使用默認(rèn)值即可
void cv::Rodrigues(cv::InputArray src, cv::OutputArray dst,
                   cv::OutputArray jacobian = cv::noArray());

2.3 透鏡畸變

理論上透鏡可以是沒有扭曲的耀找,但是實(shí)際上制造出的濾鏡并不完美翔悠。這主要是因?yàn)橹圃旃に嚨脑蛞笛拢a(chǎn)球面透鏡比制造數(shù)學(xué)上更理想的非球面透鏡,拋物面透鏡)更容易蓄愁。同樣將透鏡的中心和成像傳感器的中心嚴(yán)格對準(zhǔn)也很困難双炕。這里介紹兩種主要的透鏡扭曲,以及如何對其建模撮抓。徑向扭曲(Radial Distoritions)是由透鏡的形狀造成的妇斤,而切向扭曲(Tangential Distorations)是由相機(jī)組裝過程中的瑕疵造成的。

2.3.1 徑向扭曲

首先介紹徑向扭曲丹拯,相機(jī)透鏡經(jīng)常會明顯扭曲位于成像傳感器邊緣的圖像站超。這種膨脹現(xiàn)象是桶裝和魚眼效應(yīng)的主要原因。下圖演示了徑向扭曲產(chǎn)生的原因乖酬。由于透鏡從中心到邊緣的形狀變化顷编,因此其折射率也隨著某點(diǎn)到透鏡中心的距離變換而改變,遠(yuǎn)離中心的光線會被扭曲得更厲害剑刑,從而使得穿過透鏡的平行光線都被聚焦到一點(diǎn)媳纬。但是對于市場上廣泛使用廉價透鏡而言,特別是球面透鏡施掏,折射率的變化過快钮惠,導(dǎo)致平行光不能聚焦到一點(diǎn),發(fā)生球面扭曲七芭。即光線扭曲超過合理值英遭,從而使得圖中的正方形直邊成像后被彎曲卒煞。這在網(wǎng)絡(luò)攝像頭中很常見捺檬,答案是在高端相機(jī)中少見役耕,因?yàn)檫@些相機(jī)對透鏡系統(tǒng)做了很多優(yōu)化從而最大程度消除徑向扭曲。

在成像傳感器的光學(xué)中心處徑向扭曲為0耙箍,隨著向其邊緣移動撰糠,扭曲不斷加大。實(shí)際上這個扭曲量是很低的辩昆,可以通過麥克勞林級數(shù)阅酪。其實(shí)麥克勞林級數(shù)是泰勒級數(shù)的特殊情況,這里其展開式為f(r)=a0+a1r+a2r2+…汁针,由于r=0時术辐,f(r)也等于0,因此這里a0=0施无。又因?yàn)樵摵瘮?shù)關(guān)于y軸對稱辉词,因此奇次項的系數(shù)都為0。因此校正公式可以表示為如下猾骡,其中點(diǎn)(x, y)表示實(shí)際的投影坐標(biāo)瑞躺,而(xcorrected, ycorrected)表示校準(zhǔn)后的坐標(biāo)隧魄。對于如全景攝像機(jī)等更復(fù)雜的相機(jī)校準(zhǔn)請參考o(jì)pencv_contrib/modules/ccalib/samples。

對于廉價的網(wǎng)絡(luò)相機(jī)而言隘蝎,通常只需要使用到前面兩項,即k1和k2襟企,對于魚眼透鏡這類高扭曲相機(jī)而言可以使用到第三項嘱么,即k3。下圖演示了矩形網(wǎng)格點(diǎn)在一個特定相機(jī)上的扭曲情況顽悼,其中的箭頭起點(diǎn)表示正常的矩形網(wǎng)格曼振,箭頭的方向和長度分別表示成像后扭曲的方向和程度∥盗可以明顯看到隨著離光學(xué)中心距離的增加冰评,扭曲的程度也不斷增加。

2.3.1 切向扭曲

第二種較大的常見扭曲是切向扭曲(Tangential Distortion)木羹,它是由于透鏡和成像傳感器并不嚴(yán)格平行導(dǎo)致的甲雅。如下圖所示,當(dāng)成像平面傾斜時坑填,則首先垂直或者平行方向上兩點(diǎn)到成像平面的距離不一樣會導(dǎo)致它在成像平面上的投影在對應(yīng)方向上的距離大于或小于正確距離抛人,導(dǎo)致右圖的變形。

切向扭曲的校準(zhǔn)公式如下脐瑰,這類不具體討論推導(dǎo)過程妖枚,如有興趣請參考《Decentering Distortion of Lenses》。

對于一個特定相機(jī)苍在,在拍攝矩形網(wǎng)格時绝页,其切向扭曲示意圖如下,其中的箭頭起點(diǎn)表示正常的矩形網(wǎng)格寂恬,箭頭的方向和長度分別表示成像后扭曲的方向和程度续誉。

因此相機(jī)的校準(zhǔn)需要5個參數(shù),分別是k1初肉,k2屈芜,p1,p2和k3朴译,大多數(shù)涉及相機(jī)校準(zhǔn)等OpenCV的函數(shù)中井佑,這些參數(shù)都是必須等,因此它們按這種順序表示為扭曲向量(Distortion Vector)眠寿。實(shí)際上成像系統(tǒng)中還有一些其他類型的畸變躬翁,但是它們的影響并沒有徑向畸變和切向畸變嚴(yán)重,因此OpenCV并未考慮這些情況盯拱。

3 標(biāo)定

關(guān)于攝像頭校準(zhǔn)在Jean-Yues Bouguet的校準(zhǔn)網(wǎng)站上還能找到更多資料盒发,OpenCV中提供的函數(shù)是cv::calibrateCamera()例嘱。該校準(zhǔn)方法將相機(jī)對準(zhǔn)一個具有很多獨(dú)立標(biāo)識點(diǎn)的已知結(jié)構(gòu)。通過從不同的角度觀察該結(jié)構(gòu)宁舰,可以計算出在每個拍攝圖片的時刻相機(jī)的相對位置和相機(jī)的方向拼卵,以及相機(jī)的內(nèi)部參數(shù)。在本章的后面部分講到函數(shù)cv::findChessboardCorners()中會有更詳細(xì)的介紹蛮艰。為了提供多個視角腋腮,需要旋轉(zhuǎn)和平移目標(biāo),因此接下來先花一點(diǎn)時間了解關(guān)于旋轉(zhuǎn)和平移的相關(guān)數(shù)學(xué)知識壤蚜。

OpenCV在不斷提高校準(zhǔn)技術(shù)即寡,現(xiàn)在已經(jīng)有很多不同類型的校準(zhǔn)模式,在后面小節(jié)講到校準(zhǔn)板時會詳細(xì)介紹袜刷。對于那些特殊相機(jī)聪富,也有特殊的校準(zhǔn)技術(shù)。對于魚眼透鏡著蟹,需要使用類cv::fisheye的相關(guān)方法墩蔓。對于全景相機(jī)(Omnidrectional Camera)和多重相機(jī)(Multicamera Calibration)校準(zhǔn),參考官方示例opencv_contrib/modules/ccalib/samples萧豆,opencv_contrib/modules/ccalib/tutorial/omnidir_tutorial.markdown钢拧,opencv_contrib/modules/ccalib/tutorial/multi_camera_tutorial.markdown】缓幔或者在官方文檔中搜索omnidir和multiCameraCalibration源内。

一個全景相機(jī)的扭曲示意圖如下,其內(nèi)部的數(shù)學(xué)知識已經(jīng)超出了本文的討論范圍份殿。

3.1 旋轉(zhuǎn)矩陣和平移向量

對于相機(jī)拍攝特定目標(biāo)的每個圖像而言膜钓,如下圖所示我們可以通過旋轉(zhuǎn)和平移的方式來描述目標(biāo)相對于相機(jī)的位置。圖中Q點(diǎn)是相機(jī)坐標(biāo)系中的點(diǎn)卿嘲,而q點(diǎn)是該點(diǎn)在模型坐標(biāo)系內(nèi)的點(diǎn)在成像平面的投影颂斜。則可以通過旋轉(zhuǎn)矩陣R和平移向量t,以及前文介紹過的相機(jī)內(nèi)參矩陣就可以計算出p拾枣。

通常情況下任意維度的旋轉(zhuǎn)都可以通過方針和向量相乘的方式表示沃疮。另外旋轉(zhuǎn)點(diǎn)等同于反方向旋轉(zhuǎn)坐標(biāo)系,即點(diǎn)旋轉(zhuǎn)后的坐標(biāo)等于在這個新坐標(biāo)系內(nèi)的坐標(biāo)梅肤。一個二維系統(tǒng)下的旋轉(zhuǎn)示意圖如下司蔬,其中點(diǎn)P(x, y)是在坐標(biāo)系(X-Y)中的某個點(diǎn),點(diǎn)繞圓心逆時針旋轉(zhuǎn)等同于順時針旋轉(zhuǎn)坐標(biāo)系至(X’Y‘)姨蝴,旋轉(zhuǎn)后的點(diǎn)坐標(biāo)為p(x’,y’)俊啼。

三維空間內(nèi)的旋轉(zhuǎn)可以被分解為繞每個固定軸的二維旋轉(zhuǎn),如果模型坐標(biāo)系(這里是相機(jī)坐標(biāo)系)為參照左医,分別以繞xyz軸的順序旋轉(zhuǎn)ψ授帕、φ和θ度同木,則三個分步的旋轉(zhuǎn)矩陣可以表示如下。

則將點(diǎn)從世界(相機(jī))坐標(biāo)系轉(zhuǎn)換為模型坐標(biāo)系的矩陣是跛十,將坐標(biāo)從模型坐標(biāo)系轉(zhuǎn)換到世界(相機(jī))坐標(biāo)系的逆變換彤路,即R=R(ψ)R(φ)R(θ)。旋轉(zhuǎn)矩陣的轉(zhuǎn)至矩陣為逆變化矩陣芥映,即RTR=I洲尊,此處RT為矩陣R的轉(zhuǎn)置矩陣,I為單位矩陣屏轰。

在應(yīng)用旋轉(zhuǎn)矩陣之前,我們應(yīng)當(dāng)將模型坐標(biāo)系先平移至世界(相機(jī))坐標(biāo)系憋飞,則借助平移向量可以完成坐標(biāo)變換霎苗。平移向量可以通過兩個坐標(biāo)系的原點(diǎn)坐標(biāo)計算,即T=OriginObject - OriginCamera榛做,則模型坐標(biāo)系中的點(diǎn)PC坐標(biāo)和世界坐標(biāo)系中點(diǎn)PO的計算公式為PC=R(P0-T)唁盏。

結(jié)合PC的計算公式和前文的相機(jī)內(nèi)參-校正可以組成方程組,OpenCV可以對這些方程組求解检眯,這些方程的解將會包含我們需要的相機(jī)校準(zhǔn)參數(shù)厘擂。三維旋轉(zhuǎn)可以通過三個角度參數(shù)確定,三維平移可以通過向量(x,y,z)確定锰瘸,這樣就得到6個參數(shù)刽严。OpenCV的內(nèi)參矩陣包含(fx, fy, cx, cy)四個參數(shù),這樣在每個圖片中總共需要求解10個參數(shù)避凝,但是需要注意的是4個相機(jī)內(nèi)參矩陣的參數(shù)對于不同的圖片而言是相同的舞萄。使用一個平面目標(biāo)能夠很快的每個圖片能夠固定8個參數(shù)。因?yàn)樵诓煌瑘D片中旋轉(zhuǎn)和平移相關(guān)6個參數(shù)是變換的管削,對于每個圖片我們需要限制額外的兩個參數(shù)倒脓,通過它們來求解相機(jī)內(nèi)參矩陣。因此我們至少需要兩個視圖來求解所有的幾何參數(shù)含思。

在本章的后續(xù)部分會對這些參數(shù)的細(xì)節(jié)以及它們的限制進(jìn)行深入介紹崎弃,在這之前我們需要先認(rèn)識校準(zhǔn)目標(biāo)(Calibration Object)。

3.2 標(biāo)定板

理論上含潘,任何具有合適特征的對象都可以是校準(zhǔn)目標(biāo)饲做,但是實(shí)際上通常使用位于平面上的規(guī)則圖形。例如棋盤(Chessboard)遏弱、圓網(wǎng)格(CircleGrid)艇炎,隨機(jī)圖案(Randpattern),ArUco和ChArUco圖案腾窝。其中ArUco和ChArUco圖案和二維碼相關(guān)缀踪,建議使用ChArUco居砖,詳細(xì)使用方式參考OpenCV官方文檔,教程和代碼位于OpenCV_Contrib/modules/aruco目錄中驴娃。附錄C中包含所有OpenCV可用校準(zhǔn)模式的示例奏候。在文獻(xiàn)中使用的一些校準(zhǔn)方法依賴于三維目標(biāo),如覆蓋有標(biāo)記的盒子唇敞,但是平面的棋盤模型更容易處理蔗草,而且制造、存儲和分發(fā)精確的三維校準(zhǔn)對象也是比較困難的疆柔。

OpenCV選擇使用一個平面對象的多個不同角度視圖咒精,而不是具有特別構(gòu)造的三維校準(zhǔn)目標(biāo)的單個視圖。目前我們主要討論棋盤模式旷档。使用黑白交替的方塊確保來在測量時不會偏向某一側(cè)模叙。另外得到的網(wǎng)格角點(diǎn)也可以利用在【關(guān)鍵點(diǎn)和描述子】章節(jié)中介紹到的亞像素坐標(biāo)的計算函數(shù)。我們也會討論圓網(wǎng)格校準(zhǔn)板鞋屈,它具有一些期待的性質(zhì)范咨,在某些情況下能夠給出比棋盤更好的結(jié)果。對于其他模式厂庇,請參考圖中引用的相關(guān)文檔渠啊。

下圖是使用棋盤校準(zhǔn)板的示意圖,通過各種姿勢手持棋盤得到的圖像能夠提供足夠的信息定位它們在世界(相機(jī))坐標(biāo)系中的位置权旷,并計算出相機(jī)的內(nèi)在參數(shù)替蛉。

下圖是由高度紋理化的隨機(jī)圖案組成的校準(zhǔn)板示例,參考o(jì)pencv_contrib/modules/ccalib/tutorial目錄中的multicamera校準(zhǔn)教程拄氯。

下圖是由ArUco(二維碼)方格組成的校準(zhǔn)圖案灭返。因?yàn)槊總€方塊都通過自己的ArUco圖案標(biāo)識,當(dāng)校準(zhǔn)板的大部分被遮擋時坤邪,仍然包含足夠的空間標(biāo)記點(diǎn)用于校準(zhǔn)計算熙含。參考OpenCV官方文檔中的ArUco標(biāo)記檢測(aruco)模塊。

ChArUco是棋盤校準(zhǔn)板和ArUco校準(zhǔn)板的組合艇纺,其示意圖如下怎静。這種方式不僅在校準(zhǔn)板部分遮擋的情況下仍能很好工作,同時在計算角度位置時也能保持更高的精度黔衡。參考OpenCV相關(guān)文檔Aruco模塊中的ArUco標(biāo)記檢測蚓聘。

3.2.1 棋盤角點(diǎn)檢測函數(shù)

提供一張棋盤圖片,也可以是由人手持的棋盤圖片盟劫,或者是任何包含無干擾背景的棋盤圖片夜牡,都可以通過如下函數(shù)尋找棋盤的角點(diǎn)。

// 返回值:是否檢測到所有角點(diǎn)并排序
// image:輸入棋盤圖片,元素格式為8UC1或者8UC3
// patternSize:每行和每列的內(nèi)角點(diǎn)數(shù)量cv::Size(cols, rows)
// corners:檢測到的角點(diǎn)
// flags:算法策略塘装,下文介紹
bool cv::findChessboardCorners(cv::InputArray image, cv::Size patternSize,
                               cv::OutputArray corners,
                               int flags = cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE);

對于標(biāo)準(zhǔn)的國際象棋棋盤而言急迂,內(nèi)角點(diǎn)為7,因此參數(shù)patternSize應(yīng)該傳入cv::Size(7, 7)蹦肴,但是使用行列不同的奇偶棋盤更方便僚碎,因?yàn)榇藭r棋盤只有一個對稱軸,因此棋盤的方向更容易確定阴幌。參數(shù)flags指定了算法幫助尋找角點(diǎn)的過濾策略勺阐,可選參數(shù)如下,當(dāng)然也可以使用邏輯與符號選擇它們之中的任意項組合矛双。

cv::CALIB_CB_ADAPTIVE_THRESH渊抽,默認(rèn)該函數(shù)基于亮度均值對圖像進(jìn)行閾值處理,但是當(dāng)該項被設(shè)置時议忽,算法內(nèi)部會使用自適應(yīng)閾值技術(shù)處理圖像懒闷。

cv::CALIB_CB_NORMALIZE_IMAGE,在應(yīng)用閾值處理之前徙瓶,如果該值被設(shè)定毛雇,則會使用函數(shù)cv::equalizeHist()預(yù)處理圖像嫉称,從而讓有限的顏色帶寬分布更廣侦镇,提高圖像對比度。

cv::CALIB_CB_FILTER_QUADS织阅,在圖像經(jīng)過閾值處理后壳繁,算法會嘗試定位棋盤上黑色方塊的投影視圖中的四邊形。這是一個近似四邊形荔棉,因?yàn)樗倪呅蔚倪呍瓌t上都應(yīng)該是直線闹炉,但是當(dāng)圖像中存在徑向畸變時可能并不會這樣。如果該標(biāo)記被設(shè)置润樱,則一系列額外的限制將被附加到這些四邊形上渣触,從而避免出現(xiàn)錯誤四邊形。

cv::CALIB_CB_CV_FAST_CHECK壹若,當(dāng)該選項被設(shè)置時嗅钻,算法首先會快速掃描圖像判斷內(nèi)部是否包含任何角點(diǎn),如果不包含則不會進(jìn)行后續(xù)處理店展。當(dāng)不確定圖像中是否包含棋盤時养篓,將該選項打開能夠節(jié)約大量時間,但是當(dāng)確定圖像一定包含棋盤時赂蕴,建議不要設(shè)置該值柳弄。

函數(shù)的返回值表示是否檢測到角點(diǎn)并排序,這里排序意味著可以為尋找到的點(diǎn)構(gòu)建一個模型概说,該模型和它們實(shí)際上時平面上的共線點(diǎn)集這個命題一致碧注。該函數(shù)內(nèi)部會調(diào)用函數(shù)cv::cornerSubPix()計算出較精確的角點(diǎn)坐標(biāo)嚣伐,但是當(dāng)你需要更高精度的時候可以對其結(jié)果再次調(diào)用函數(shù)cv::cornerSubPix(),并設(shè)置更嚴(yán)格的參數(shù)应闯。

3.2.2 繪制棋盤角點(diǎn)

OpenCV提供如下函數(shù)用于繪制找到的棋盤角點(diǎn)纤控,這在調(diào)試時很方便。

// image:繪制底圖碉纺,元素格式為8UC3
// patternSize:每行和每列的內(nèi)角點(diǎn)數(shù)量cv::Size(cols, rows)
// corners:通過函數(shù)findChessboardCorners()找到的需要繪制的角點(diǎn)
// patternWasFound:是否找到了所有的角點(diǎn)船万,并排序,函數(shù)findChessboardCorners()返回值
void cv::drawChessboardCorners(cv::InputOutputArray image, cv::Size patternSize,
                               cv::InputArray corners, bool patternWasFound);

如果參數(shù)patternWasFound設(shè)置為false骨田,則表示未找到所有的角點(diǎn)耿导,則該函數(shù)會講找到的角點(diǎn)繪制為紅圈。如果該參數(shù)為yes态贤,則表示找到了所有的角點(diǎn)舱呻,則每一行的角點(diǎn)都會被繪制為不同的顏色,并且用線連接以表示它們之間的順序悠汽。

下圖是該函數(shù)的一個使用示例箱吕。

現(xiàn)在可能會好奇這些角點(diǎn)對相機(jī)校準(zhǔn)的作用,的當(dāng)通過透鏡觀察這些被找到的角點(diǎn)時柿冲,可以使用3??3的齊次矩陣來完成透視變換進(jìn)行坐標(biāo)轉(zhuǎn)換茬高。但是在我們討論這個話題之前,先簡單討論下另外方形網(wǎng)格的一種替代圖案假抄。

3.2.3 圓形網(wǎng)格

圓形網(wǎng)格是棋盤校準(zhǔn)板的一種替代方案怎栽,概念上圓形網(wǎng)格和棋盤類似,和棋盤使用黑白相間方塊不同宿饱,圓形網(wǎng)格在白色背景上熏瞄,使用了一組黑色圓。OpenCV中實(shí)現(xiàn)尋找圓形網(wǎng)格校準(zhǔn)板的圓形中心點(diǎn)坐標(biāo)函數(shù)定義如下谬以。

// 返回值:是否尋找到了圓心
// image:用于尋找圓形網(wǎng)格圓心坐標(biāo)的圖像强饮,元素格式為8UC1或者8UC3
// patternSize:每行和每列的圓數(shù)量cv::Size(cols, rows)
// centers:檢測到的圓心坐標(biāo)
// flags:算法策略,下文介紹
// blobDetector:用于尋找圓形圖案的特征檢測器
bool cv::findCirclesGrid(cv::InputArray image, cv::Size patternSize,
                         cv::OutputArray centers, int flags = cv::CALIB_CB_SYMMETRIC_GRID,
                         const cv::Ptr<cv::FeatureDetector>& blobDetector = new SimpleBlobDetector());

參數(shù)flags用于指定使用的圓形網(wǎng)格校準(zhǔn)板是對稱網(wǎng)格(Symmetric Grid)還是非對稱網(wǎng)格(Asymmetric Grid)为黎,前者使用值cv::CALIB_CB_SYMMETRIC_GRID邮丰,后者使用值cv::CALIB_CB_ASYMMETRIC_GRID。對稱網(wǎng)格指的是網(wǎng)格的排列上每行和每列的圓圈是對齊的碍舍,而非對稱網(wǎng)格是交錯排列的柠座。下圖是一個非對稱網(wǎng)格校準(zhǔn)板的示意圖。其中左上角圖像是平視的畫面片橡,而右下圖是通過相機(jī)拍攝到的透視圖示意效果妈经。

在使用非對稱網(wǎng)格時需要仔細(xì)確定網(wǎng)格的行列,如上圖由于非對稱網(wǎng)格的行上的園是交錯排列的,因此其中行數(shù)和列數(shù)分別為4和11吹泡。參數(shù)flag的另外一個可選值是cv::CALIB_CB_CLUSTERING骤星,他可以和另外兩個選項的任意值通過邏輯與符號組合。如果該值被指定爆哑,算法會使用一個略微不同的算法尋找圓洞难。這個備選算法對于透視變形更穩(wěn)定,但是缺點(diǎn)是對背景的干擾更敏感揭朝。當(dāng)你嘗試校準(zhǔn)一個視野非常寬的相機(jī)時队贱,該值是一個不錯的選擇。

通常情況下你會發(fā)現(xiàn)非對稱網(wǎng)格相對于棋盤而言潭袱,無論是在結(jié)果的質(zhì)量上蛔外,還是多次運(yùn)行結(jié)果的穩(wěn)定性上都更好酿愧。因此它也逐漸成為相機(jī)校準(zhǔn)標(biāo)準(zhǔn)工具的一部分声邦。當(dāng)然在最近ChArUco(參考庫的contrib部分示例代碼)等模式同樣也很受歡迎挽牢。

3.3 單應(yīng)性

數(shù)學(xué)上的單應(yīng)性具有更廣泛的定義,但是這里定義的平面單應(yīng)性(Planar Homography)是從一個平面到另外一個平面的投影映射彤悔,則二維平面上的點(diǎn)投影到相機(jī)成像傳感器上的過程就是一個很好的例子嘉抓。使用如下齊次坐標(biāo)表示模型坐標(biāo)系中的點(diǎn)Q以及投影到成像平面的點(diǎn)q,則它們可以通過矩陣關(guān)系表示晕窑。

這里引入了一個縮放參數(shù)s抑片,它通常是從矩陣H中分離而出,從而確保計算得到的向量是正確的投影幕屹。使用一些幾何學(xué)和矩陣代數(shù)的知識蓝丙,就可以求解該仿射矩陣级遭。矩陣H包含兩個部分望拖,第一部分是定位我們觀察到的校準(zhǔn)板平面,即通過一個旋轉(zhuǎn)矩陣W來將點(diǎn)坐標(biāo)從模型坐標(biāo)系轉(zhuǎn)換到相機(jī)坐標(biāo)系挫鸽。第二部分和投影相關(guān)说敏,這里會使用到之前介紹的相機(jī)內(nèi)參矩陣。單應(yīng)性的示意圖如下丢郊,其中Q‘是模型坐標(biāo)系的點(diǎn)盔沫,q是在投影平面的點(diǎn)。

物理變換部分是旋轉(zhuǎn)矩陣和平移向量的結(jié)合枫匾,該部分的變換矩陣可以表示如下架诞。

則結(jié)合投影矩陣M,投影平面上的點(diǎn)q和模型坐標(biāo)系上點(diǎn)點(diǎn)Q可以表示為如下公式干茉。

實(shí)際上點(diǎn)Q的z軸分量一直為0谴忧,因此我們可以通過如下方式對等式進(jìn)行簡化。需要注意這里r3向量被移除后,縮放的部分被疊加到的參數(shù)s中沾谓,即兩個等號右側(cè)的s并不相同委造。

使用H表示單應(yīng)矩陣,Q‘表示移除Z軸分量的模型坐標(biāo)系中的點(diǎn)均驶。則坐標(biāo)變換公式可以表示如下昏兆,其中H為3??3矩陣。

OpenCV使用上述公式計算單應(yīng)矩陣妇穴,它通過使用同一個目標(biāo)對象的多個不同角度視圖得到足夠的方程組爬虱,來計算每個視圖的旋轉(zhuǎn)、平移矩陣腾它,以及它們共同的內(nèi)參矩陣饮潦。旋轉(zhuǎn)矩陣可以通過三個角度定義,平移矩陣可以通過一個三維向量定義携狭,因此對于每個視圖而言有6個未知變量继蜡。這沒有關(guān)系,因?yàn)橐粋€已知的平面逛腿,如前文提到過的棋盤稀并,能夠給出8個等式。也就是說將一個正方形映射為一個四邊形可以通過4個頂點(diǎn)(x, y)描述单默。則每個圖像得到的八個方程碘举,但是引入了6個新的未知變量,再加上已有的內(nèi)參矩陣變量搁廓,因此只要有足夠的圖像引颈,我們就能計算出任意數(shù)量內(nèi)在參數(shù)。

位于物體空間的點(diǎn)Psrc和位于圖像平面點(diǎn)Pdst它們之間的計算公式如下境蜕。

實(shí)際上OpneCV中是使用多個視圖來計算出多個單應(yīng)矩陣蝙场,OpenCV提供如下函數(shù)來完成該計算。他通過一系列由源坐標(biāo)和目標(biāo)坐標(biāo)構(gòu)成的點(diǎn)對粱年,至少是4組售滤,來計算單應(yīng)矩陣。當(dāng)然也可以提供更多的點(diǎn)對台诗,因?yàn)樵肼暫筒淮_定因數(shù)總是存在完箩,更多的數(shù)據(jù)能夠消除這些影響。當(dāng)然有些點(diǎn)可能不會參與計算拉队,詳見下文RANSAC的介紹弊知。

// 返回值:計算得到的單應(yīng)矩陣
// srcPoints:點(diǎn)對的原始二維坐標(biāo)
// dstPoints:點(diǎn)對的目標(biāo)二維坐標(biāo)
// method:算法策略,0粱快、cv::RANSAC秩彤、cv::LMEDS等夺鲜,下文介紹
// ransacReprojThreshold:參數(shù)method選擇cv::RANSAC時生效的額外參數(shù),最大的重投影距離呐舔,下文介紹
// mask:再計算單應(yīng)矩陣H時币励,實(shí)際使用的點(diǎn)對
cv::Mat cv::findHomography(cv::InputArray srcPoints, cv::InputArray dstPoints,
                           cv::int method = 0, double ransacReprojThreshold = 3,
                           cv::OutputArray mask = cv::noArray());

輸入?yún)?shù)srcPointsdstPoints可以是N??2的矩陣,或者元素格式為CV_32FC2的N??1矩陣珊拼,或者是元素類型為cv::Point2f的STL向量食呻。

參數(shù)method的默認(rèn)值為0,當(dāng)使用默認(rèn)值時所有的點(diǎn)對都會參與運(yùn)算澎现,并且得到的是使重投影誤差(Reprojection Error)最小的單應(yīng)矩陣仅胞。這里的重投影誤差指的是對于所有點(diǎn)對,矩陣H乘以源坐標(biāo)和目標(biāo)坐標(biāo)之間的歐式距離平方之和剑辫。方便的是干旧,再使用這種誤差度量的情況下,存在快速算法可以計算單應(yīng)矩陣妹蔽。不幸的是這種誤差度量方式椎眯,會使得離群點(diǎn)單個點(diǎn)的解和主要解差異較大的那些點(diǎn),對最終的解造成很大的影響胳岂。在實(shí)際情況下编整,如對于相機(jī)校準(zhǔn)而言,測量誤差產(chǎn)生離群點(diǎn)是一個很常見的現(xiàn)象乳丰,因此得到的解經(jīng)常很大程度的偏離正確答案掌测。為此OpenCV提供了三個穩(wěn)定的擬合方法(Robust Fitting Methos)作為備選方案,通常它們在存在噪聲的情況下表現(xiàn)更好产园。

第一個方法是一致性隨機(jī)抽樣法(Random Sampling with Consensus, RANSAC)汞斧,對應(yīng)的method選項是cv::RANSAC。該方法只會使用部分點(diǎn)對進(jìn)行計算什燕,首先隨機(jī)選擇樣本的多個子集粘勒,并計算每個子集點(diǎn)單應(yīng)矩陣,然后使用除該子集外的所有和最初估計大值一致的數(shù)據(jù)點(diǎn)對最初的估計進(jìn)行優(yōu)化秋冰。內(nèi)部點(diǎn)指的是那些一致的點(diǎn)仲义,而外部點(diǎn)指的是那些不一致的點(diǎn)婶熬。算法對多個子集計算后只會保留對應(yīng)子集剩余點(diǎn)中內(nèi)部點(diǎn)比例最高的一個子集的計算結(jié)果剑勾。該方法在實(shí)踐中有效的避免了離群點(diǎn)的干擾,并得到了正確的答案赵颅。

第二個方法是最小平方中值(Least Median of Squares, LMeDS)算法虽另,對應(yīng)的method選項是cv::LMEDS。正如該算法的名字饺谬,它的目的是使中位數(shù)誤差最小捂刺,而不是默認(rèn)的使平方誤差最小谣拣。算法的具體邏輯超出本文探討的范圍,請參考對應(yīng)的論文族展。

LMeDS的優(yōu)點(diǎn)是它不需要任何的信息或者參數(shù)就能運(yùn)行森缠,但是它的缺點(diǎn)是只有在內(nèi)部點(diǎn)占整個數(shù)據(jù)集點(diǎn)大部分時才能得到較好結(jié)果。而RANSAC則對任何離群點(diǎn)比例的數(shù)據(jù)集合都能正常工作仪缸,并取得滿意的答案贵涵,但是需要通過參數(shù)ransacReprojThreshold指定多大范圍內(nèi)的點(diǎn)被認(rèn)為是內(nèi)部點(diǎn)。該參數(shù)的單位為像素恰画,大多數(shù)情況下宾茂,該參數(shù)設(shè)置為一個較小的整型值即可,如小于10拴还。但是對于高分辨率的圖像跨晴,則必須使用更大的值。

另外還有RHO算法片林,它是通過加權(quán)的方式對RANSAC算法進(jìn)行改進(jìn)端盆,被稱為PROSAC,在存在很多異常點(diǎn)的情況下該算法運(yùn)行速度更快费封,關(guān)于算法的具體邏輯請參考相關(guān)論文爱谁。

返回值是一個3??3矩陣,因?yàn)樗挥?個自由參數(shù)孝偎,因此我們可以對齊進(jìn)行標(biāo)準(zhǔn)化處理使得H33=1访敌,除了極其特殊的情況未標(biāo)準(zhǔn)化的矩陣H33=0外,這種處理都是可行的衣盾。這種方式和前文的等式中分離出系數(shù)s是等效的寺旺。

3.4 相機(jī)標(biāo)定

本節(jié)會介紹如何使用函數(shù)cv::calibrateCamera()計算相機(jī)的內(nèi)在參數(shù)和畸變參數(shù),以及如何使用這些模型去校準(zhǔn)已經(jīng)標(biāo)定好的相機(jī)得到的圖像中的畸變势决。首先說明為了求解這些參數(shù)需要多少個棋盤模型的視圖阻塑,然后會介紹具體的代碼之前會對OpenCV內(nèi)部是如何求解該系統(tǒng)進(jìn)行概述。

3.4.1 計算需要的角點(diǎn)數(shù)量

前文提到過相機(jī)內(nèi)參矩陣(Camera Intrinsic Matrix)相關(guān)的參數(shù)有4個(fx, fy, cx, cy)果复,扭曲參數(shù)有5個或者更多陈莽,其中徑向扭曲參數(shù)有3個或者更多(k1, k2, k3[,k4, k5, k6]),切向扭曲參數(shù)有兩個(p1, p2)虽抄。內(nèi)參矩陣相關(guān)參數(shù)控制了從模型坐標(biāo)系到相機(jī)坐標(biāo)系的投影變換走搁,有時也稱為線性固有參數(shù)。而畸變參數(shù)與點(diǎn)在最終圖像中扭曲的二維幾何學(xué)相關(guān)迈窟,也被稱為非線性固有參數(shù)私植。

理論上已知圖案點(diǎn)三個角點(diǎn)能夠產(chǎn)生6則信息得到點(diǎn)方程組能夠求解5個畸變參數(shù),這樣一張棋盤校準(zhǔn)視圖就足夠了车酣。然而由于內(nèi)在參數(shù)和外在參數(shù)之間存在耦合曲稼,事實(shí)證明一張圖片是不夠的索绪。外部參數(shù)包含3個旋轉(zhuǎn)參數(shù)(ψ、φ贫悄、θ)瑞驱,和三個平移參數(shù)(Tx, Ty, Tz),它們和相機(jī)矩陣的4個內(nèi)部參數(shù)組成10個未知變量窄坦,并且每增加一幅圖就會增加6個額外的參數(shù)钱烟。

假定每個圖像存在N個角點(diǎn),K個不同視角的棋盤圖像嫡丙。則:

  • K張棋盤圖像提供了2NK條約束拴袭。這里乘2的原因是每個角點(diǎn)x和y坐標(biāo)分量分別提供1條約束
  • 暫時不考慮畸變參數(shù),此時總共包含4個內(nèi)部參數(shù)和6K個外部參數(shù)
  • 系統(tǒng)有解的條件是2NK >= 6K+4曙博,即(N-3)K >= 2

看上去當(dāng)N=5點(diǎn)時候拥刻,k=1就滿足條件,即我們只需要一張視圖父泳。但是注意??般哼,K的值必須大于1。原因是在使用校準(zhǔn)棋盤擬合單應(yīng)矩陣時惠窄,四個角點(diǎn)能夠產(chǎn)生最多8個參數(shù)蒸眠。這是因?yàn)槠矫嫱敢晥D可以做的一切只需要4個點(diǎn)就能夠表達(dá),可以在正方形的四個不同方向延伸從而得到任意的四邊形杆融。因此無論在一個平面中檢測到多少個頂點(diǎn)楞卡,只能得到4個點(diǎn)點(diǎn)有效信息。因此每個棋盤視圖能夠得到的有效的角點(diǎn)信息脾歇,即N-4蒋腮,則(4-3)K>1,這意味著K必須大于1藕各。也就是說兩張不同角度的3??3大小(只算內(nèi)部角點(diǎn))的棋盤是求解校準(zhǔn)問題的最低要求池摧。考慮到噪聲和數(shù)值穩(wěn)定性問題激况,通常需要收集更多更大的不同角度棋盤圖像作彤。實(shí)際上,想要得到高質(zhì)量的結(jié)果乌逐,你需要至少10張不同角度的7??8或者更大尺寸的棋盤圖像竭讳,不同圖像的棋盤位置移動足夠大才能得到豐富的信息。實(shí)際需要的圖像數(shù)遠(yuǎn)大于理論圖像數(shù)的原因是因?yàn)橄鄼C(jī)的內(nèi)部參數(shù)即使對很小的噪聲也非常敏感黔帕。

3.4.2 底層數(shù)學(xué)知識

本小節(jié)將會深入討論相機(jī)校準(zhǔn)函數(shù)底層的數(shù)學(xué)原理代咸,如果對此部分不感興趣,請直接跳轉(zhuǎn)至下一小節(jié)成黄。OpenCV處理焦距和偏移的算法基于論文《A flexible new technique for camera calibration》呐芥,計算扭曲參數(shù)的算法則是基于論文《Close-range camera calibration》。

首先我們假定沒有任何扭曲奋岁,并計算其他的相機(jī)校準(zhǔn)參數(shù)思瘟。對于棋盤的每個視圖,我們收集一個單應(yīng)矩陣H闻伶,正如前文描述的那樣滨攻,該矩陣表示坐標(biāo)從物理空間到成像的映射關(guān)系。我們使用列向量的方式表示矩陣H蓝翰,即H = [h1, h2, h3]光绕,其中hi為3??1的向量。根據(jù)前文對單應(yīng)性矩陣的討論畜份,它可以改寫成如下公式诞帐。其中r1和r2為兩個控制旋轉(zhuǎn)的列向量,而t為控制平移的列向量爆雹,s為縮放因子停蕉。

進(jìn)一步分解可得如下4個等式。

旋轉(zhuǎn)向量在構(gòu)建的時候就是正交的钙态,因?yàn)槲覀兲崛×丝s放因子慧起,因此可以認(rèn)為r1和r2向量是標(biāo)準(zhǔn)正交向量。這意味著它們的點(diǎn)積為0册倒,并且它們的模相等蚓挤。根據(jù)點(diǎn)積為0,可以得到如下等式驻子。

對于任意向量a和b屈尼,存在公式(ab)T = bTaT,因此由上式可以推導(dǎo)出如下等式拴孤。其中M-T是((M)-1)T的簡寫脾歧。

由向量的模相等,可以得到如下等式演熟。

同樣通過公式(ab)T = bTaT替換其中的變量可以得到如下等式鞭执。

為了使等式看上去更簡潔,我們定義如下矩陣B芒粹。

通過計算可以得到矩陣B的值如下兄纺。

前文得到的兩個約束等式中都包含和矩陣B相關(guān)的通用形式(hi)TBhj,這里我們計算出它的值化漆。因?yàn)锽是對稱矩陣估脆,因此該等式可以寫為兩個六維向量的點(diǎn)積,即可以表示為如下等式座云。

使用定義(Vij)T可將兩個約束等式改寫為如下形式疙赠。

如果我們采集到K個棋盤圖像付材,則可以將它們寫成復(fù)合方程Vb = 0,其中V是2K??6的矩陣圃阳。正如前文描述那樣厌衔,當(dāng)K大于等于2時,計算出向量V的值后(可以通過OpenCV的函數(shù)計算)捍岳,通過該等式能夠得到向量b(B11富寿,B12,B22锣夹,B13页徐,B23,B33)的解银萍。則可以計算出 相機(jī)的內(nèi)在參數(shù)如下变勇。

通過前文提到的單應(yīng)矩陣約束條件,可以計算出外部參數(shù)(旋轉(zhuǎn)和平移向量)如下砖顷。

其中縮放系數(shù)λ可以通過標(biāo)準(zhǔn)正交條件λ = 1 / || (M)-1 h ||計算得到(這里的(M)-1是M的逆矩陣)贰锁。在計算的時候需要仔細(xì)處理旋轉(zhuǎn)矩陣,因?yàn)楫?dāng)使用真實(shí)數(shù)據(jù)計算得到旋轉(zhuǎn)矩陣R(r1, r2, r3)時滤蝠,由于精度誤差的累積豌熄,可能得到一個不是很準(zhǔn)確的矩陣,即不滿足RT??R = R??RT = I3(這里I3是尺寸為3??3的單位矩陣)物咳。

為了處理這個問題锣险,通常的方式是對矩陣R使用奇異值分解(Singular Value Decomposition)。將R矩陣分解為兩個標(biāo)準(zhǔn)正交矩陣U和V览闰,以及一個只在對角線上包含有效元素的縮放矩陣芯肤,即R = UDVT(這里VT是V的轉(zhuǎn)置矩陣),由于R本身是標(biāo)準(zhǔn)正交矩陣压鉴,因此可以使用單位矩陣I3重新計算R的值崖咨,即R = UI3VT(這里I3是尺寸為3??3的單位矩陣)。

到目前為止我們已經(jīng)初步計算出了相機(jī)的內(nèi)在參數(shù)和外在參數(shù)油吭,接下來我們考慮相機(jī)的畸變參數(shù)击蹲。在理想情況下點(diǎn)經(jīng)過不包含任何畸變的完美針孔相機(jī)后,其物理空間內(nèi)的坐標(biāo)P(Xw, Yw)和成像平面內(nèi)的坐標(biāo)P(Xp, Yp)之間的計算公式如下婉宰。

考慮相機(jī)畸變的情況下歌豺,成像器上的坐標(biāo)P(Xd, Yd)是被扭曲的結(jié)果,其計算公式如下心包。

通過收集一系列這樣的等式組成方程組类咧,就能夠計算出相機(jī)的畸變參數(shù)。然后在重新估計相機(jī)的內(nèi)在參數(shù)和外在參數(shù)。OpenCV提供函數(shù)cv::calibrateCamera()來負(fù)責(zé)處理這些復(fù)雜的工作痕惋。

3.4.3 標(biāo)定函數(shù)

當(dāng)準(zhǔn)備好多張圖片的角點(diǎn)后区宇,就可以調(diào)用函數(shù)cv::calibrateCamera()計算相機(jī)的內(nèi)參矩陣(Camera Intrinsics Matrix),扭曲系數(shù)(Distortion Coefficients)血巍,旋轉(zhuǎn)向量(Rotation Vectors)和平移向量(Translation Vectors)萧锉。前兩者構(gòu)成了相機(jī)的內(nèi)在參數(shù)珊随,后兩者為外部測量結(jié)果述寡,它們告訴我們物體(如前文例子中的棋盤)在空間內(nèi)的位置,以及它們的方向叶洞。扭曲系數(shù)(k1, k2, p1, p2以及更高階的k)來自于前文提到過的徑向和切向扭曲等式鲫凶,它們用于校準(zhǔn)扭曲誤差。相機(jī)的內(nèi)參矩陣可能是我們最感興趣的計算結(jié)果衩辟,因?yàn)橥ㄟ^它可以將現(xiàn)實(shí)空間中的三維坐標(biāo)映射到圖像中的二維坐標(biāo)螟炫。當(dāng)然我們也可以使用它來做相反的操作,但是圖像中的點(diǎn)投影到三位空間只能是一條線艺晴,稍后我們會再次介紹這個點(diǎn)≈缱辏現(xiàn)在讓我們正式了解下相機(jī)校準(zhǔn)函數(shù)。

// objectPoints:K維度向量封寞,每個向量包含特定圖像的某個校準(zhǔn)模式下N個點(diǎn)在模型空間內(nèi)的坐標(biāo)
//   (例如以棋盤原點(diǎn)建立模型坐標(biāo)系然评,在計算成像坐標(biāo)點(diǎn)時通過旋轉(zhuǎn)和平移向量移動至世界坐標(biāo)系,
//    再通過內(nèi)參矩陣投影到圖像坐標(biāo)系)
// imagePoints:K維度向量狈究,每個向量包含特定圖像的某個校準(zhǔn)模式下N個點(diǎn)在圖像空間內(nèi)的坐標(biāo)
// imageSize:輸入圖像的大小碗淌,單位為像素,即imagePoints的數(shù)據(jù)取樣的圖像大小
// cameraMatrix:3??3的相機(jī)內(nèi)參矩陣
// distCoeffs:元素數(shù)為4抖锥、5或者8的相機(jī)扭曲系數(shù)
// rvecs:K維度向量亿眠,每個向量都包含1個旋轉(zhuǎn)矩陣,表示為Rodrigues形式磅废,即一個三元素向量
// tvecs:K維度向量纳像,每個向量都包含1個平移矩陣
// flags:算法內(nèi)部策略,下文講解
// criteria:算法內(nèi)部迭代的終止條件
double cv::calibrateCamera(cv::InputArrayOfArrays objectPoints,
                           cv::InputArrayOfArrays imagePoints,
                           cv::Size imageSize,
                           cv::InputOutputArray cameraMatrix,
                           cv::InputOutputArray distCoeffs,
                           cv::OutputArrayOfArrays rvecs,
                           cv::OutputArrayOfArrays tvecs,
                           int flags = 0,
                           cv::TermCriteria criteria = cv::TermCriteria(
                               cv::TermCriteria::COUNT | cv::TermCriteria::EPS, 30, DBL_EPSILON));

參數(shù)objectPoints可以使用簡單的整型數(shù)據(jù)表示其在x和y軸上的坐標(biāo)分量拯勉,z軸上的分量可以直接寫為0竟趾,詳細(xì)原因參考前文。并且理論上K個向量可以是不同校準(zhǔn)圖像的坐標(biāo)谜喊,但是實(shí)際中通常使用同一個校準(zhǔn)圖像K個視角下相同的點(diǎn)集坐標(biāo)潭兽。參數(shù)imagePoints類似肌稻,但是它是在圖像坐標(biāo)系下的坐標(biāo)拱烁,并且當(dāng)使用棋盤時悠砚,每個向量都是對應(yīng)圖像的角點(diǎn)輸出矩陣丙唧。

需要注意參數(shù)objectPoints的輸入?yún)?shù)的格式會影響輸出參數(shù)tvecs的格式微宝。即檔期為整型數(shù)據(jù)時,即例如輸入數(shù)據(jù)為[[0, 0, 0], [0, 1, 0], [0, 2, 0]等]時表示度量單位為格子單位籍茧,即第幾格钞啸。假如每個格子的寬高為25毫米樹,如輸入的數(shù)據(jù)為[[0, 0, 0], [0, 0.025, 0], [0, 0.050, 0]等]時表示度量單位為米铸本。而相機(jī)的內(nèi)參矩陣的度量單位總是像素肮雨。

當(dāng)參數(shù)distCoeffs包含4個元素時,其中每個元素依次為k1, k2, p1, p2箱玷。當(dāng)包含5個元素時怨规,依次為k1, k2, p1, p2, k3。當(dāng)包含8個元素時锡足,依次為k1, k2, p1, p2, k3, k4, k5, k6波丰。通常5元素的模式只適用于魚眼透鏡,而8元素模式只適用于高精度的透鏡舶得,并且在參數(shù)flags中必須設(shè)置屬性cv::CALIB_RATIONAL_MODEL掰烟。注意,計算所需要的圖像數(shù)量隨著待求解參數(shù)的數(shù)量增加而急劇增加沐批。

注意纫骑,由于相機(jī)校準(zhǔn)過程中精度極為重要,即使你在最開始時為參數(shù)cameraMatrixdistCoeffs分配內(nèi)存時使用的數(shù)據(jù)格式不是double類型九孩,它們都將以該類型計算和返回先馆。

通過優(yōu)化確定參數(shù)的解是一種藝術(shù),有時嘗試一次求解所有的參數(shù)可能得到不正確或者不穩(wěn)定的結(jié)果捻撑,特別是當(dāng)你在參數(shù)空間中選擇的起始值偏離實(shí)際解時磨隘。因此通常通過分段計算較優(yōu)的參數(shù)初始值不斷的逼近實(shí)際解是一個更優(yōu)的方法。為此顾患,我們通常固定一些參數(shù)番捂,解決其他參數(shù),然后保持其他參數(shù)固定再求解原來的那些參數(shù)江解,并多次迭代设预。最終當(dāng)我們認(rèn)為所有的參數(shù)都接近真實(shí)的值后,我們再一次計算出所有需要的變量犁河。OpenCV通過參數(shù)flags控制這些行為鳖枕。

參數(shù)flags的取值如下,不同取值之間可以通過邏輯與符合多重選擇桨螺。

cv::CALIB_USE_INTRINSIC_GUESS
當(dāng)改值被設(shè)置時宾符,算法內(nèi)部會使用cameraMatrix內(nèi)部的值作為初始的相機(jī)內(nèi)參矩陣。在很多實(shí)際的應(yīng)用中灭翔,我們可以通過從透鏡旁邊的信息讀取出焦距魏烫,但是需要注意通常設(shè)備上標(biāo)示的是毫米,我們需要將之換算成像素。如對于尺寸為1/8的傳感器哄褒,如果總像素為2048??1536稀蟋,焦距為25毫米,則可以計算出焦距為7246.38呐赡,有效值可以取7250退客。通常可以使用這些信息链嘀,將該值填入?yún)?shù)cameraMatrix萌狂,并在標(biāo)記中設(shè)置cv::CALIB_USE_INTRINSIC_GUESS,另外同時設(shè)置標(biāo)記cv::CALIB_FIX_ASPECT_RATIO也是一個不錯的選擇管闷。

cv::CALIB_FIX_PRINCIPAL_POINT
當(dāng)該標(biāo)記和cv::CALIB_USE_INTRINSIC_GUESS同時被起用時粥脚,表示主點(diǎn)(中心點(diǎn))從參數(shù)cameraMatrix中獲取窃肠,當(dāng)該標(biāo)記被單獨(dú)啟用時包个,表示主點(diǎn)為圖像中心。

cv::CALIB_FIX_ASPECT_RATIO
當(dāng)該標(biāo)記和cv::CALIB_USE_INTRINSIC_GUESS同時被起用時冤留,在優(yōu)化相機(jī)內(nèi)參矩陣時fxfy保持從參數(shù)cameraMatrix中獲取到的原始比例碧囊。當(dāng)該標(biāo)記被單獨(dú)啟用時,它們可以時任意值纤怒,但是它們的比值是相關(guān)的糯而。

cv::CALIB_FIX_FOCAL_LENGTH
當(dāng)該標(biāo)記啟用之后參數(shù)cameraMatrix中的焦距分量fx和fy將不會被更新。

cv::CALIB_FIX_K1, cv::CALIB_FIX_K2, … cv::CALIB_FIX_K6
當(dāng)該標(biāo)記啟用之后distCoeffs中的徑向扭曲參數(shù)K1, K2, … K6將不會被更新泊窘。

cv::CALIB_ZERO_TANGENT_DIST
對于高端相機(jī)而言熄驼,由于出色的制造工藝切向畸變很小,嘗試擬合趨于0的參數(shù)可能導(dǎo)致噪聲烘豹,錯誤值和數(shù)值穩(wěn)定性問題瓜贾。當(dāng)該標(biāo)記啟用時,算法將不再擬合切向扭曲參數(shù)p1和p2携悯,它們被直接設(shè)置為0祭芦。

cv::CALIB_RATIONAL_MODEL
默認(rèn)模式下只會計算前3個徑向扭曲k系數(shù),即使提供的參數(shù)distCoeffs包含8個元素憔鬼。當(dāng)該標(biāo)記被啟用時龟劲,還會計算徑向扭曲吸收k4,k5和k6轴或。

參數(shù)criteria確定了算法內(nèi)部迭代結(jié)束的條件昌跌,當(dāng)包含精度條件時,指的是重投影誤差(Reprojection Error)照雁。它和函數(shù)cv::findHomography()中的含義相同蚕愤,都值得是三維坐標(biāo)重投影到圖像平面上的位置和對應(yīng)點(diǎn)點(diǎn)原始位置點(diǎn)距離平方和。

使用圓點(diǎn)網(wǎng)格校準(zhǔn)相機(jī)的現(xiàn)象越來越頻繁,在這種情況下尤其要小心設(shè)置參數(shù)objectPoints中的值审胸。如第一行的前四個點(diǎn)坐標(biāo)表示為(0, 0, 0), (1, 1, 0), (2, 0, 0), (3, 1, 0)時亥宿,接下來的第二行前四個點(diǎn)坐標(biāo)則需要表示為(0, 2, 0), (1, 3, 0), (2, 2, 0), (3, 3, 0)。

3.4.4 僅計算外部參數(shù)
函數(shù)solvePnP

有時砂沛,你已經(jīng)知道相機(jī)的內(nèi)部參數(shù)烫扼,只想計算其外部參數(shù),通常這種任務(wù)被稱為N點(diǎn)透視(Perspective N-Point)碍庵,或者被稱為PnP問題映企。OpenCV負(fù)責(zé)完成此任務(wù)的函數(shù)原型如下,實(shí)際上函數(shù)cv::calibrateCamera內(nèi)部也會調(diào)用該函數(shù)來完成對應(yīng)任務(wù)静浴。

// objectPoints:特定圖像的某個校準(zhǔn)模式下N個點(diǎn)在模型空間內(nèi)的坐標(biāo)(例如以棋盤原點(diǎn)建立模型坐標(biāo)
//     系堰氓,在計算成像坐標(biāo)點(diǎn)時通過旋轉(zhuǎn)和平移向量移動至世界坐標(biāo)系,再通過內(nèi)參矩陣投影到圖像坐標(biāo)系)
// imagePoints:特定圖像的某個校準(zhǔn)模式下N個點(diǎn)在圖像空間內(nèi)的坐標(biāo)
// cameraMatrix:3??3的相機(jī)內(nèi)參矩陣
// distCoeffs:元素數(shù)為4苹享、5或者8的相機(jī)扭曲系數(shù)
// rvecs:旋轉(zhuǎn)矩陣双絮,表示為Rodrigues形式,即一個三元素向量得问,可以通過函數(shù)cv::Rodirigues轉(zhuǎn)化為矩陣形式
// tvecs:平移矩陣
// useExtrinsicGuess:是否使用參數(shù)rvecs和tvecs內(nèi)部的數(shù)據(jù)作為初始值囤攀,并對其進(jìn)行優(yōu)化
// flags:算法內(nèi)部策略,下文講解
bool cv::solvePnP(cv::InputArray objectPoints, cv::InputArray imagePoints,
                  cv::InputArray cameraMatrix, cv::InputArray distCoeffs,
                  cv::OutputArray rvec, cv::OutputArray tvec,
                  bool useExtrinsicGuess = false, int flags = cv::ITERATIVE);

參數(shù)flags定義了函數(shù)的算法實(shí)現(xiàn)方式宫纬,其取值可以是cv::ITERATIVE焚挠,cv::P3Pcv::EPNP。選項cv::ITERATIVE使用了Levenberg-Marquardt方法優(yōu)化參數(shù)imagePoints和參數(shù)objectPoints經(jīng)過重投影后的誤差漓骚,使其最小化蝌衔。選項cv::P3P基于論文【Complete solution classification for the perspective-three-point problem】中提供的方法,當(dāng)選擇該方式時蝌蹂,需要提供準(zhǔn)確的4個物體坐標(biāo)系下的點(diǎn)坐標(biāo)噩斟,和對應(yīng)的4個圖像坐標(biāo)系下的坐標(biāo)。并且只有該方法成功執(zhí)行后叉信,函數(shù)返回值才為true亩冬。最后選項cv::EPNP基于論文【Accurate Non-Iterative O(n) Solution to the PnP Problem】中提供的方法。最后兩種方法都不是迭代形式硼身,因此它們都比第一種迭代形式的方法更快硅急。

注意盡管上文介紹該函數(shù)的背景是考慮一個靜止的相機(jī),計算在多個視頻幀內(nèi)某個對象相對于靜止相機(jī)的位置佳遂。但是該函數(shù)也能高效的處理逆向問題营袜,如對于一個移動的機(jī)器人而言,此時鏡頭是移動的丑罪,我們更感興趣的是那些靜止的物體荚板,可能是一些固定的物體,也可能是整個場景跪另。在這種情況下也可以使用該函數(shù)拧抖,只需要以相反的方式理解參數(shù)rvectvec的含義即可。

函數(shù)solvePnPRansac

使用函數(shù)cv::solvePnP的一個缺點(diǎn)是其結(jié)構(gòu)很容易受到異常點(diǎn)干擾免绿。在相機(jī)校準(zhǔn)中唧席,這并不是一個問題,因?yàn)槠灞P本身的特征提供了一個穩(wěn)定的方式用于尋找我們關(guān)心的每個特征嘲驾,并能夠通過它們的相對空間關(guān)系驗(yàn)證我們得到的就是我們想要尋找的點(diǎn)淌哟。然而在真實(shí)世界中,確定相機(jī)和非棋盤上某個點(diǎn)的相對關(guān)系時(如使用稀疏關(guān)鍵特征點(diǎn))辽故,可能會出現(xiàn)錯誤的匹配并造成嚴(yán)重的后果徒仓。回想在討論單應(yīng)性時提到過RANSAC方法能有效的處理異常點(diǎn)的影響誊垢,OpenCV也提供了類似函數(shù)來用于處理真實(shí)世界中的數(shù)據(jù)掉弛。

// objectPoints:特定圖像的某個校準(zhǔn)模式下N個點(diǎn)在模型空間內(nèi)的坐標(biāo)(例如以棋盤原點(diǎn)建立模型坐標(biāo)
//     系,在計算成像坐標(biāo)點(diǎn)時通過旋轉(zhuǎn)和平移向量移動至世界坐標(biāo)系彤枢,再通過內(nèi)參矩陣投影到圖像坐標(biāo)系)
// imagePoints:特定圖像的某個校準(zhǔn)模式下N個點(diǎn)在圖像空間內(nèi)的坐標(biāo)
// cameraMatrix:3??3的相機(jī)內(nèi)參矩陣
// distCoeffs:元素數(shù)為4狰晚、5或者8的相機(jī)扭曲系數(shù)
// rvecs:旋轉(zhuǎn)矩陣,表示為Rodrigues形式缴啡,即一個三元素向量,可以通過函數(shù)cv::Rodirigues轉(zhuǎn)化為矩陣形式
// tvecs:平移矩陣
// useExtrinsicGuess:是否使用參數(shù)rvecs和tvecs內(nèi)部的數(shù)據(jù)作為初始值瓷们,并對其進(jìn)行優(yōu)化
// iterationsCount:RANSAC迭代次數(shù)
// reprojectionError:得到的內(nèi)部點(diǎn)集成立的最大允許重投影誤差
// minInliersCount:如果尋找到的內(nèi)部點(diǎn)超過該數(shù)值业栅,認(rèn)為該組點(diǎn)都為內(nèi)部點(diǎn),并結(jié)束算法
// inliers:尋找到的內(nèi)部點(diǎn)索引數(shù)組
// flags:算法內(nèi)部策略谬晕,和其在函數(shù)solvePnP()中的含義相同
bool cv::solvePnPRansac(cv::InputArray objectPoints, cv::InputArray imagePoints,
                        cv::InputArray cameraMatrix, cv::InputArray distCoeffs,
                        cv::OutputArray rvec, cv::OutputArray tvec,
                        bool useExtrinsicGuess = false, int iterationsCount = 100,
                        float reprojectionError = 8.0, int minInliersCount = 100,
                        cv::OutputArray inliers = cv::noArray(), int flags = cv::ITERATIVE)

對于PnP問題碘裕,需要尋找4個點(diǎn)確定投影變換,因此對于每個RANSAC迭代而言都會先選擇4個點(diǎn)攒钳,而重投影誤差reprojectionError的默認(rèn)值和每個點(diǎn)在圖像中的位置以及重投影位置的平均距離相關(guān)帮孔。為參數(shù)minInliersCount設(shè)置合理的值能夠顯著的提升算法的效率,但是該值過低也可能導(dǎo)致大量的問題不撑。

4 矯正

通常我們使用已標(biāo)定的相機(jī)完成兩個任務(wù)文兢,分別是校準(zhǔn)扭曲圖像,和重構(gòu)接收到圖像的三維場景焕檬。這里先介紹第一個任務(wù)姆坚,三維重建將在下個章節(jié)詳細(xì)討論。OpenCV提供一個便利的扭曲矯正函數(shù)cv::undistort()实愚,也提供一對函數(shù)cv::initUndistortRectifyMap()cv::remap()用于圖像矯正兼呵。前者使用函數(shù)cv::calibrateCamera()計算得到的扭曲系數(shù)兔辅,和一張扭曲的圖像作為輸入?yún)?shù),函數(shù)返回校準(zhǔn)后的圖像击喂。后者使我們可以更高效的處理視頻或者來自于同一個相機(jī)的多張圖片维苔。下圖是一副圖像矯正前后的效果。

4.1 矯正映射

在對一張圖像做矯正時懂昂,必須指定輸入圖像的每個像素將被映射到輸出圖像的位置蕉鸳,成為矯正映射(Undistortion Map)或者直接稱為扭曲映射(Distortion Map)。它有如下幾種可用的表示形式忍法。

其中最直接的形式是雙通道浮點(diǎn)型矩陣潮尝,矩陣中的每個元素都可以看作是一個二維浮點(diǎn)型向量,分別表示對應(yīng)位置的輸入圖像像素應(yīng)當(dāng)映射到輸出圖像中的位置饿序。需要注意這里使用的是浮點(diǎn)型數(shù)據(jù)勉失,這意味著在計算最終輸出圖像數(shù)據(jù)時包含插值處理,不過這里通常的插值方法是確定能夠映射輸出圖像中某個像素的多個源像素原探,再適當(dāng)?shù)脑谒鼈冎g進(jìn)行插值乱凿。該表示形式的矯正映射過程如下圖。

第二種表示形式是使用兩個浮點(diǎn)型的數(shù)組咽弦,和第一種表示形式含義類似徒蟆,只是數(shù)據(jù)的組織方式有一點(diǎn)差異。

最后一種是定點(diǎn)表示形式型型,即使用一個雙通道的有符號整型數(shù)據(jù)組成的矩陣(如使用CV_16SC2)段审。該矩陣用于插值的方式和使用雙通道浮點(diǎn)型矩陣形式一致,只是這種數(shù)據(jù)格式處理速度更快闹蒜。如果需要更高的精度寺枉,還可以使用第二個無符號整型數(shù)組(如CV_16UC1)來存儲插值必要信息,即數(shù)組中的每個元素都指向了一個內(nèi)部查詢表绷落。

4.2 在不同形式之間切換

OpenCV提供如下函數(shù)在三種表示形式之前自由切換姥闪。

// map1和map2為原始映射
// 由于映射的表示形式中存在兩個矩陣的形式,因此這里提供兩個參數(shù)砌烁,當(dāng)原始映射只包含1個
// 矩陣時筐喳,參數(shù)map2傳入cv::noArray()
// map1取值CV_16SC2/CV_32FC1/CV_32FC2,map2取值CV_16UC1/CV_32FC1/cv::noArray()
// dstmap1和dstmap2為轉(zhuǎn)換后的映射
// dstmap1type:distmap1的類型函喉,函數(shù)會根據(jù)該類型推斷我們想要使用的映射
// nninterpolation:當(dāng)轉(zhuǎn)化為定點(diǎn)形式時是否需要計算插值表
void cv::convertMaps(cv::InputArray map1, cv::InputArray map2,
                     cv::OutputArray dstmap1, cv::OutputArray dstmap2,
                     int dstmap1type, bool nninterpolation = false);

所有可用的映射轉(zhuǎn)換使用的系數(shù)取值組合如下表避归。

map1 map2 dstmap1type nninterpolation dstmap1 dstmap2
CV_32FC1 CV_32FC1 CV_16SC2 true CV_16SC2 CV_16UC1
CV_32FC1 CV_32FC1 CV_16SC2 false CV_16SC2 cv::noArray()
CV_32FC2 cv::noArray() CV_16SC2 true CV_16SC2 CV_16UC1
CV_32FC2 cv::noArray() CV_16SC2 false CV_16SC2 cv::noArray()
CV_16SC2 CV_16UC1 CV_32FC1 true CV_32FC1 CV_32FC1
CV_16SC2 cv::noArray() CV_32FC1 false CV_32FC1 CV_32FC1
CV_16SC2 CV_16UC1 CV_32FC2 true CV_32FC2 cv::noArray()
CV_16SC2 cv::noArray() CV_32FC2 false CV_32FC2 cv::noArray()

需要注意即使使用了插值表,即參數(shù)nninterpolation設(shè)置為true函似,從浮點(diǎn)型轉(zhuǎn)化到定點(diǎn)型數(shù)據(jù)會損失精度槐脏,因此不應(yīng)期待再從定點(diǎn)類型轉(zhuǎn)換會浮點(diǎn)型時得到的結(jié)果和原始值相同。

4.3 計算矯正映射

實(shí)際上扭曲映射的應(yīng)用很多撇寞,但目前我們感興趣的是如何使用相機(jī)校準(zhǔn)的結(jié)果創(chuàng)建一個矯正效果不錯的圖像顿天。目前為止我們討論的都是單目圖像堂氯,實(shí)際上矯正的其中一個更重要的應(yīng)用是通過一組圖像計算圖像的深度。下一章我們會深入討論立體圖像話題牌废,目前只需要知道有這個應(yīng)用即可咽白,因?yàn)橛成溆嬎愫瘮?shù)中的部分參數(shù)主要就是用于立體視覺的。

基本的過程是先計算矯正映射鸟缕,然后再將其應(yīng)用在特定圖像上晶框。分開操作的原因是在大多數(shù)實(shí)際場景中,只需要計算一次矯正映射懂从,然后對于每個從相機(jī)來的視頻幀依次應(yīng)用這些圖像授段。函數(shù)cv::initUndistortRectifyMap()使用相機(jī)校準(zhǔn)的信息計算矯正映射,其原型如下番甩。

// cameraMatrix:相機(jī)內(nèi)參矩陣侵贵,尺寸為3??3
// distCoeffs:相機(jī)的扭曲系數(shù),元素數(shù)可以是4缘薛、5或者8
// R:旋轉(zhuǎn)變換矩陣窍育,用于補(bǔ)償嵌入式相機(jī)像對于全局坐標(biāo)系的旋轉(zhuǎn)
// newCameraMatrix:額外的相機(jī)內(nèi)參矩陣,用于立體場景
// size:生成的映射矩陣尺寸宴胧,其大小應(yīng)當(dāng)與我們要矯正的圖像一致
// m1type:矯正映射的分量map1的數(shù)據(jù)格式漱抓,函數(shù)根據(jù)該值推測我們需要的映射類型
// 取值為16SC2, 32FC1, 或者32FC2
// map1:矯正映射矩陣1
// map2:矯正映射矩陣2
void cv::initUndistortRectifyMap(cv::InputArray cameraMatrix,
                                 cv::InputArray distCoeffs,
                                 cv::InputArray R,
                                 cv::InputArray newCameraMatrix,
                                 cv::Size size, int m1type,
                                 cv::OutputArray map1, cv::OutputArray map2);

函數(shù)的前兩個參數(shù)為相機(jī)的內(nèi)部參數(shù),可以通過函數(shù)cv::calibrateCamera()計算恕齐。參數(shù)R用于補(bǔ)償嵌入式相機(jī)像對于全局坐標(biāo)系的旋轉(zhuǎn)乞娄,它應(yīng)用于矯正發(fā)生之前。如果不使用傳入cv::noArray()即可檐迟,如果使用需要傳入尺寸為3??3的旋轉(zhuǎn)矩陣补胚。和旋轉(zhuǎn)矩陣類似參數(shù)newCameraMatrix也可以影響圖像矯正的方式,不過它影響的是相機(jī)的中心位置追迟,而不是焦距。在處理單目圖像時不需要使用到該參數(shù)骚腥,傳入cv::noArray()即可敦间。但是在立體圖像分析中,該參數(shù)十分重要束铭,更多細(xì)節(jié)會在下一章繼續(xù)討論廓块。

參數(shù)m1type決定了使用的映射形式,map1map2會填充上對應(yīng)的數(shù)據(jù)契沫。它們之間的對應(yīng)關(guān)系可以參考上表带猴,只是這里默認(rèn)會生成插值表。

4.4 用函數(shù)remap矯正圖像

在計算出矯正映射后懈万,就可以通過函數(shù)cv::remap()矯正輸入圖像拴清,它使用函數(shù)initUndistortRectifyMap計算得到的數(shù)據(jù)靶病,并支持任意形式的矯正映射。

4.5 用函數(shù)undistort矯正圖像

如果只矯正一副圖像口予,或者對每幅圖像都重新計算矯正映射娄周,可以使用如下復(fù)合函數(shù)。

// src:待矯正圖像沪停,二維矩陣
// dst:矯正后的圖像煤辨,二維矩陣
// cameraMatrix:相機(jī)內(nèi)參矩陣,尺寸為3??3
// distCoeffs:相機(jī)的扭曲系數(shù)木张,元素數(shù)可以是4众辨、5或者8
// newCameraMatrix:額外的相機(jī)內(nèi)參矩陣,用于立體場景
void cv::undistort(cv::InputArray src, cv::OutputArray dst,
                  cv::InputArray cameraMatrix, cv::InputArray distCoeffs,
                  cv::InputArray newCameraMatrix = noArray());

4.6 用函數(shù)undistortPoints稀疏矯正

有時可能我們并不需要對整幅圖像進(jìn)行矯正舷礼,而是只希望處理圖像中的部分點(diǎn)鹃彻,此時可以通過如下函數(shù)做稀疏矯正,只處理指定的點(diǎn)且轨。

// src:待處理的像素點(diǎn)集浮声,二維點(diǎn)的向量
// dst:矯正后的像素點(diǎn)集,二維點(diǎn)的向量
// cameraMatrix:相機(jī)內(nèi)參矩陣旋奢,尺寸為3??3
// distCoeffs:相機(jī)的扭曲系數(shù)泳挥,元素數(shù)可以是4、5或者8
// R:表示模型空間的矯正變換矩陣至朗,尺寸為3??3屉符,R1和R2可以由函數(shù)stereoRectify計算,
//     傳入cv::noArray()時使用單位矩陣
// P:額外的相機(jī)內(nèi)參矩陣(3??3)或者是投影矩陣(3??4)锹引,可以由函數(shù)stereoRectify計算矗钟,
//     傳入cv::noArray()時使用單位矩陣
void cv::undistortPoints(cv::InputArray src, cv::OutputArray dst,
                         cv::InputArray cameraMatrix, cv::InputArray distCoeffs,
                         cv::InputArray R = cv::noArray(), cv::InputArray P = cv::noArray());

參數(shù)srcdst都是二維點(diǎn)點(diǎn)向量,可以是由cv::Vec2i組成的N??1數(shù)組嫌变,也可以是浮點(diǎn)型數(shù)據(jù)組成的數(shù)組吨艇,還可以是元素類型為cv::Vec2f的STL風(fēng)格的向量。參數(shù)R和P主要用于立體視覺分析腾啥,在下一章會詳細(xì)介紹东涡。

5 綜合程序

程序Undistortion首先查找用戶提供的棋盤圖像,提取盡量多的能夠獲取到棋盤角點(diǎn)的信息倘待,并計算相機(jī)內(nèi)參矩陣和扭曲系數(shù)疮跑。最后程序進(jìn)入顯示模式,從而展示未扭曲的相機(jī)圖像凸舵。在運(yùn)行該程序時祖娘,需要提供拍照角度顯著不同的照片,否則用于求解矯正參數(shù)的點(diǎn)的矩陣可能是病態(tài)的矩陣(非滿秩)啊奄,可能得到一個糟糕的結(jié)果渐苏,或者什么也沒有得到掀潮。

6 小結(jié)

本章首先簡單介紹咯針孔相機(jī)模型并概述了投影機(jī)和的基本原理,在介紹完一種旋轉(zhuǎn)變換的表示形式整以,即Rodrigues變換后胧辽,我們介紹咯透鏡畸變的概念,并學(xué)習(xí)了在OpenCV中如何使用相機(jī)內(nèi)參矩陣對其建模公黑。

接下來我們學(xué)習(xí)了如何使用棋盤或者圓形網(wǎng)格去標(biāo)定相機(jī)邑商,當(dāng)然還可以使用其他的校準(zhǔn)模式,在本系列文章末尾的后記中會詳細(xì)列舉凡蚜。另外我們深入討論了如何使用棋盤等標(biāo)定模版在不同角度下拍攝的到的圖像計算內(nèi)部和外部參數(shù)人断,也討論了圖像單應(yīng)性的話題,并介紹了外部參數(shù)和內(nèi)部參數(shù)之前的差異朝蜘,還將外部參數(shù)與普遍的姿態(tài)估計問題關(guān)聯(lián)恶迈。然后我們又介紹了如何使用計算得到的相機(jī)內(nèi)部參數(shù)矯正圖像,從而消除真實(shí)透鏡成像時引起的圖像扭曲現(xiàn)象谱醇。

最后我們用一個復(fù)合的示例程序結(jié)束本章暇仲,該程序從磁盤中加載了一系列的棋盤照片,提取這些照片內(nèi)部的角點(diǎn)副渴,計算相機(jī)的內(nèi)部參數(shù)奈附,并使用這些參數(shù)對圖像進(jìn)行矯正。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末煮剧,一起剝皮案震驚了整個濱河市斥滤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌勉盅,老刑警劉巖佑颇,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異草娜,居然都是意外死亡挑胸,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門宰闰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嗜暴,“玉大人,你說我怎么就攤上這事议蟆。” “怎么了萎战?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵咐容,是天一觀的道長。 經(jīng)常有香客問我蚂维,道長戳粒,這世上最難降的妖魔是什么路狮? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮蔚约,結(jié)果婚禮上奄妨,老公的妹妹穿的比我還像新娘。我一直安慰自己苹祟,他們只是感情好砸抛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著树枫,像睡著了一般直焙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上砂轻,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天奔誓,我揣著相機(jī)與錄音,去河邊找鬼搔涝。 笑死厨喂,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的庄呈。 我是一名探鬼主播蜕煌,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼抒痒!你這毒婦竟也來了幌绍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤故响,失蹤者是張志新(化名)和其女友劉穎傀广,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體彩届,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伪冰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年琳钉,在試婚紗的時候發(fā)現(xiàn)自己被綠了睁蕾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡认臊,死狀恐怖寨辩,靈堂內(nèi)的尸體忽然破棺而出吓懈,到底是詐尸還是另有隱情,我是刑警寧澤靡狞,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布耻警,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏甘穿。R本人自食惡果不足惜腮恩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望温兼。 院中可真熱鬧秸滴,春花似錦、人聲如沸募判。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽兰伤。三九已至内颗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敦腔,已是汗流浹背均澳。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留符衔,地道東北人找前。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像判族,于是被迫代替她去往敵國和親躺盛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容