相機就是一個帶小孔的盒子
想像一個密封的鞋盒,一面扎了一個極小的針孔,對面貼著一張紙。外面世界反射的光線從這個孔射進來。因為孔非常小,物體上每個點都只有一條細細的光線能夠穿過,並且正好落在紙上的某一個位置。對每個點都這樣處理,整個場景的完整影像就會在後壁上成形——雖然是上下顛倒的,但確確實實是真實的。這就是相機的全部原理:它把一個光線四面八方傳播的三維世界,壓縮成一幅平坦的二維影像。
針孔迫使每一條光線都穿過同一個點,所以影像本質上是一束在同一處交會的直線光線。現代相機用鏡頭代替針孔(為了收集更多光線並保持清晰),用數位感光元件代替紙張——也就是由數百萬個計光單元組成的網格,這些單元叫做像素。但幾何關係是一樣的:世界以光線的形式抵達,感光元件記錄每條光線落在網格上的位置。
一個三維點如何變成一個像素
鞋盒的整潔數學版本叫做針孔相機模型。它把針孔放在原點,並在它前方一個焦距處放一個想像出來的成像平面(放在前方,這樣影像就是正立的而非顛倒的)。世界中的一個點用一條直線與針孔相連;這條光線穿過成像平面的地方,就是這個點出現的位置。剩下的交給相似三角形。
兩個數字主宰著這次壓縮。焦距是成像平面與針孔之間的距離;焦距長會把場景攤開(放大、視野窄),焦距短則把場景擠緊(視野寬)。主點是中心光線——也就是正前方那條——打在感光元件上的位置;它在像素座標裡標出影像中心。知道這兩者,模型就能把光線的方向換算成精確的像素位置(即它的行與列)。
u = fx * (X / Z) + cx # pixel column v = fy * (Y / Z) + cy # pixel row # (X, Y, Z): point in front of the camera, Z = depth # fx, fy: focal length in pixels cx, cy: principal point
兩組參數:鏡頭本身,以及它在世界中的位置
要投影一個點,模型需要兩類知識,而把它們分開來看會很有好處。第一類是固化在相機本身裡的一切:焦距、主點,以及像素網格的形狀。這些是相機內參——相機自身的內部幾何。機器人四處行駛時它們不會改變,因為它們描述的是這個盒子,而不是盒子在哪裡。
第二類是相機所處的位置以及它朝向哪個方向。世界中的一個點通常是在某個固定參考系裡測量的——房間、機器人的底座——但投影的數學卻想要從相機自己的視角來測量它。相機外參就是連接這兩個座標系的旋轉和平移。它們恰恰就是相機的位姿:位置加上朝向。把相機裝在一條移動的機械臂上,外參時時刻刻都在變;而內參紋絲不動。
標定:在信任相機之前先把它量準
沒有人會遞給你一張可以精確到像素的內參規格表——同一型號的兩台相機也會有差異,而真實鏡頭彎折光線的方式是整潔的針孔模型所忽略的。廣角鏡頭邊緣附近的直線會向外鼓出或向內收縮;這就是鏡頭畸變。相機標定就是測量真實內參和畸變的過程,好讓軟體把兩者都糾正回來。
- 列印一張棋盤格,方格尺寸精確已知,把它貼在一個平整、堅硬的物體上。
- 從許多角度和距離拍十幾張以上的照片,讓它出現在畫面的不同區域。
- 軟體在每張照片裡找出每個角點——而它早已知道這些角點在真實棋盤上的位置。
- 它尋找一組內參和畸變,使投影出的角點與偵測到的角點最為吻合,留下最小的殘餘誤差。
回報既直接又實在。假設一條機械臂發現了一個零件,從影像算出它的距離,然後伸手去抓。如果焦距偏差了百分之幾,或者畸變沒有糾正,幾何就會撒謊:機械臂會誤判零件有多遠、有多大,於是要麼搆不著,要麼撞過去。同樣的缺陷會污染立體相機對算出的深度,以及之後每一個依賴它的步驟——所以標定是任何機器人被允許去看、去抓之前那個不動聲色的第一步。它正是機器人上其他每個感測器都需要的感測器標定在視覺上的同類。