Kinect Hacking 102

今天我們來講一個比較枯燥的主題- 校正(Calibration),請大家有心裡準備,這篇有很多數學公式(但是我會盡量簡化,請不要馬上關掉瀏覽器)

先說一下為什麼要校正呢?其實理由很簡單,因為這個世界不是完美的,即使你花大錢買了十幾萬、二十幾萬的相機,昂貴的鏡頭,但是我敢保證照出來的照片和真實的景物還是有一定的誤差,即時那誤差很小,但是絕對是存在的,這時我們可以用一點點數學公式來修正其中的誤差,這對kinect來說,尤其重要,重要的理由,我先賣個關子,等下再解釋。

大家小學的時候,不知道有沒有學過針孔成像(pinhole imaging),這跟以前很流行的針孔攝影機是完全沒關係的喔,簡單的說,就是在張紙上戳一個小洞,然後在紙的一頭放個蠟燭,另一頭放個白紙,把房間關得黑黑的,在白紙上可以看見蠟燭的倒影。

大家可以看看上面那張圖,假設X 是原本物體的高度,Z是物體到針孔的距離,x是投影後物體的高度,f是投影平面到針孔的距離(也就是我們常說得焦距),根據簡單的幾何學定理(相似三角形),我門可以得出x/f = X/Z,也就是x = f*(X/Z),當然這是所謂完美的情況下,可惜我們之前說過,這個世界並不完美,在數位相機的時代,那個投影的面板(x的位置)可以假設為我們的感光面板(也就是常聽到CCD,CMOS),理論上那塊面板應該要放在正中央(也就是要穿過中央那條水平線,我們叫它光軸),但是這基本上是不可能,除非有人願意花大錢去調效到奈米的精確度,於是我們在我們的公式上面加了一個變數叫做c,用c來假設可能的位移,這樣我們的公式就變成了 x= f*(X/Z)+c。要注意的是,影像是一個平面,我上面畫得那個圖只是很單純的一維圖,但是現實中,這是一個二維圖。

所以我們可以導出兩個公式,一個是X軸,一個是Y軸,這裡要注意的是,兩個軸分別用不同的f值,主要的原因是因為這裡我們用f值是以pixel(像素)為單位,我們買相機時,常常聽說這台相機有多少像素,有多好,照起來多清晰(not true,但是不在本篇討論範圍內),那個所謂的像素就是在感光元件中一個一個的小點,理論上pixel應該是正方形的,但是很不幸的,又牽扯到我之前說這世界不完美的理論,在很多廉價的感光元件中,很多pixel是長方形的,所以即時在f相等的情況下,在基本unit pixel的長度不一樣的情況,X,Y會各有其不同的f值。

接下來,我們要講鏡頭的變形,之前我們一直是用針孔成像來假設的,但是現實中,我們不是用一個小孔來成像,而是用一個或多個凸透鏡或是凹透鏡來折射光成像的,這些鏡頭增加了透光率,但是也產生了一些問題,最常見的就是Radial distortions,顧名思義,這種就是以中心開始,越到邊緣,就越嚴重,像我們常說得桶狀變形就是屬於其中的一種。

圖片來源:這裡

另一種變形也是常見的,叫做Tangential Distortions,這通常發生在那片CMOS沒有跟鏡頭平行時,這是很常在便宜的攝影機(像是webcam)看到的。

圖片來源:這裡

各位可以看到,原本Ideal plane應該是要跟lens平行的,但是實際上CCD的位置卻是歪的,要修正上面兩個問題,前人已經推了數學公式可以作修正。

上面兩個是修正radial distortions的,下面兩個是修正Tangential distortions的,大家可以發現,我們又多了好幾個未知數,上面有三個未知數分別是K1,K2,K3,下面有兩個是P1,P2。

最後,我們要進入今天真正的主題 – 校正。其實所謂的校正呢,就是套用以上的數學公式去解出其中我們假設的未知數,在基本校正中,我們假設了四個未知數,在鏡頭變形上,我們假設了五個未知數,大家能回憶國中所學的二元聯立一次方程式的話,兩個未知數要兩個方程式才能解,那九個未知數應該要幾個方程式來解呢,答案很簡單,就是九個。那我們要怎麼湊九個方程式呢?假如我們能夠知道現實世界中一個東西的位置(X,Y)和在感光面積上相對應的位置(x,y),那我們不就可以湊出兩個方程式了(X,Y各一個),但是或許感光面積上的位置,我們可以量出來,但是現實中的位置只怕沒這麼好量,這時我們就要拿出我們的秘密武器-棋盤(chessboard)。

這有什麼好處呢,各位有沒有看到那黑白相間的格子,假如說在每個黑白相交的地方設置一個點,然後假設說,左上角那個相交點是我們的原點,那我們就可以找出其他所有的點的相對位置。假如不懂我在說什麼話,那看看這張圖,應該比較了解。

假設我們設定左上角第一個點為(0,0),那下面那個點就是(0,一個格子的距離),左邊那個點就是(一個格子的距離,0),這樣子依序我們就可以找出很多我們需要的點,來解上面所說得方程式了,但是大家有沒有發現一個問題(life is never easy,huh),就是我們假設左上角那個點是原點,但是就上面那張圖來說,它並不是,每個人拿那個棋盤方法不一樣,有些人可能會拿斜的,拿左邊一點,拿右邊一點,這時我們就需要點座標轉換,座標轉換有兩種,一種是轉角度(拿斜的,拿正的),另一種是轉位置(拿左邊,拿右邊),那我們之前一開始說得公式就需要點變化了,除了f,c以外,我們還得加上座標轉換的變數,因為我們處於一個三圍的世界,每一個軸都會轉一個角度,三個軸,就會轉三個角度,同樣的位移也有三個方向,所以我們又多了六個變數(注1),所以假如我們要解第一個公式(f,c),那我們得要用十個方程式,那就是五個點(x,y各代表一個值),萬一點不夠怎饃辦,沒關係,我們用好幾張圖來解,雖然每張圖座標轉換是不一樣的(要一直拿一樣東西不動,照很多像也是很辛苦的),但是那f,c值是不會變的。(其實用2張以上的圖來解方程式是必須的,主要原因是因為一張圖只要有四個點,我們就可以找出其所有的透視位置,所以無論在這張圖上找出多少點,能用的也就只有四個點)

那我們來說說怎饃校正Kinect吧,記得我在101說過Kinect 有兩個攝像機,一個是很普通的webcam,另一個是IR的攝像機,普通的webcam,我們可以上面所說得方法來解決,但是量景深的呢,我們來看看節圖。

天阿,我們的棋盤變成一整塊了,那我們要怎饃找那些方格呢,嘿嘿,這時我們就要倒退一百步來想,首先,這景深的數據是兩個東西組成的,一個是投影機,另一個是紅外線攝影機,大家有沒有好奇未經過計算的原圖是長什麼樣子呢,感謝網路上神人的努力不懈,目前新版的驅動程式是可以取出紅外線攝影機的畫面的。

大家有沒有看到那些一點點很像雜訊的東西,那可不是Kinect用了廉價貨而產生的雜訊,那是所謂的雷射投影機打出去的光點,景深的數據就是靠那些光點來判別的,但是我在實做時,發現那些光點會干擾程式(注2)去找棋盤中那些黑白相間的點,可以用一個很簡單的解決方法,就是把那投影機用一個東西蓋起來,不讓雷射光打出來,這樣我們得到的畫面就會如下,

這畫面真是一整個黑阿,但是隱約的還是可以看到棋盤的樣子。

但是程式倒是很容易把其中的位置點找出來。

這樣我們就很輕鬆的作完鏡頭的校正了,但是記得我們前面賣得關子嗎?我們幹麼要作鏡頭校正,畢竟我們又不是什麼軍用等級,玩具就算差一點點也沒差多少,當然,花這麼多時間打了這麼多字,當然是有關系的啦,我們先來看看我們說得第一個公式,我們現在已經知道所有我們假設的未知數了,我們也知道x,y (也就是在640x 480中的位置),那我只要知道Z,再加上一點國小算術,我不就可以得到現實生活中的X,Y,那Z是什麼,別忘了,我們有景深攝影機,就是專門顯示Z值,我之前在101也提到了如何轉換呈現實中的尺寸(對了,上次有讀者說,那個公式算出來有負數,其實我覺得這很正常,假如你有仔細看那個公式的架構,我覺得是回歸分析做出來的結果,但是回歸分析結果是線性的,但是實際上,這轉換可能不是線性的,也就會造成了所謂的誤差),知道了X,Y,Z,那我們得到了什麼,沒錯,就是Point Cloud,那可以在經過mesh處理就可以做出3D物件,不過這已經不包含在這次課程裡了。

最後附一個別人的作品。

這裡有某神人寫好整套的校正程式(用C++,我自己是用Python寫得,不過以我目前的速度,要寫完,還要等很久)

注1: 我並沒有列出如何轉換公式,主要原因有二,第一呢,基本上這都有寫好的程式作運算,我自己覺得呢,並不需要太了解公式,有需要的話可以去google,第二,我不會用OpenOffice畫 3×3 matrix (這好像才是主因)

注2:假如有先預習我上次的教科書的話,應該了解,這其實是用一個函式庫叫做OpenCV,基本上,它已經幫你做好需要運算的函數,你只需要了解基本的原理和如何使用即可。

喜歡這篇文章嗎? 分享出去給作者一點鼓勵吧!
  • Kuo

    這個驅動程式目前有支援tuio嘛?

  • Pingback: Tweets that mention Kinect Hacking 102 – MMDays -- Topsy.com()

  • Alastor0325

    謝謝!!

    非常棒的講解!!

  • ting

    請教大大一個問題
    在openkinect的libfreenect裡,他有給一個程式glview讓我們去跑並且看結果
    程式執行後會有深度圖和rgb顏色圖
    請教大大 在glview裡rgb的顏色是如何隨著深度不同做改變阿?
    有請大大能否教學一下 感激