Augmented Reality 101 in MATLAB
最近@Yu Jiang老師迷上了AR,伴隨著@weijia zhang的Oculus跳票N個月後終於要發貨的日子的臨近,我們來看看如何用MATLAB的Computer Vision System Toolbox實現簡單的增強現實(並沒有什麼關係。
首先
我們需要一點點背景知識,什麼是AR
簡單來說,現在大家普遍說的Augemented Reality就是將一個虛擬的物體放到你拍攝的實物圖像中。
想要做AR, 你必須回答一個問題
一個三維世界中的點
是如何呈現在圖像中
的呢?
我們用個高端手繪來簡單說明下。
下圖中,坐標係為我們生活的三維世界。而右上角那個
坐標系,則是相機的坐標系,我們可以簡單的將相機的光心模擬成
的原點。而成像的平面(或是你相機的膠片,或是數碼相機的CCD),則為當中那個平面
,我們看到的圖片都是在這個平面上的。

接下來就是坐標系之間的轉換了,這裡需要回答一個問題。
要把
坐標系下的
放到
平面上的
,一共要幾步?
答案是比把大象裝冰箱少一步
1.把
2.把坐標系轉換到
坐標系
坐標系轉換到
坐標系
就像所有工科學生以為自己學的是工科,最後發現自己都在學數學一樣,這兩步都可以用兩次矩陣乘法來表示,因為莎士比亞說過,矩陣的實質就是空間轉換。
此處我們就不展開說明了,我們只要知道以下的事實
第一步轉換對應camera extrinsics(旋轉+平移)
第二步轉換對應camera intrinsics
而上圖中的,就是
到
的轉換,也就是將
放到平面上的
變換矩陣,也是extrinsics和intrinsics的組合,叫做camera matrix
然後
為了看看上述知識有什麼用,我們來把一個虛擬的三維立方體放到圖片中。

要知道camera matrix, 我們需要intrinsics和extrinsics
確定intrinsics的步驟被稱為相機的標定(camera calibration),學習computer vision的同學很多都是用Caltech提供的工具。然而從R2014a開始,MATLAB的Computer Vision工具箱自帶了Camera Calibrator App。
確定extrinsics的方法有很多,最簡單的方法是利用一個已知大小,且可以簡單通過圖像處理被測量的平面物體, 這種物體被稱為visual fiducial. 傳統的fiducial marker是用一個簡單的棋盤格圖案。
當然marker的圖案並不局限於棋盤格,任何可以被快速識別的圖案都可以,比如被醜陋人類虐待的機器人手裡的那個箱子上類似二維碼的東西(應該是Prof. Olson的AprilTag)

(圖片來源)
說了這麼多,快來看看MATLAB裡面怎麼做吧!
我們省略確定camera intrinsics的步驟,可以參照MATLAB的官方文檔和Camera Calibrator的文檔,雖然MATLAB中的App很方便,但是拍照的過程異常枯燥,一般我們都是讓實習生做的。
myc:????????
當標定相機後,我們可以得到一個cameraParameters object, 這裡是變數cameraParams.
接下來我們來確定相機的extrinsics和camera matrix。MATLAB的Computer Vision toolbox中自帶的detectCheckerboardPoints,extrinsics, 和cameraMatrix函數,可以直接解決這個問題。
>> I = imread(myImage.jpg);n>>[im, newOrigin] = undistortImage(I, cameraParams, OutputView, full); %首先去除相片的畸變 n>>[pts,boardSize] = detectCheckerboardPoints(im); %找到相片中的fiducial markern>> scale = 25; %我們用的是25mm間隔的棋盤格n>> worldPoints = generateCheckerboardPoints(boardSize,scale); %生成三維世界中假想的一個棋盤格n>> [rotationMat,translation]=extrinsics(pts,worldPoints,cameraParams);%通過圖片中檢測到的棋盤格和其對應的三維世界中的棋盤格生成extrinsics(旋轉+平移)n>>camMat = cameraMatrix(cameraParams,rotationMat,translation); %通過intrinsics和extrinsics建立camera matrixn>>cube = Cubic(5*25); %自己寫的類,一個5*25邊長的立方體n>>imshow(im); %去除畸變的圖片n>>cube.render(gca,camMat); %根據camera matrix把立方體在棋盤格上畫出來n
而我們的Cubic類簡單定義如下
classdef Cubic < handlen propertiesn verticesn faces n scalen patchn endn n methodsn function this = Cubic(scale)n this.scale = scale; %邊長n this.vertices = scale*...n [0 0 0;n 0 0 -1;n 0 1 -1;n 0 1 0;n 1 0 0;n 1 1 0;n 1 0 -1;n 1 1 -1;]; %8個頂點n this.faces = ...n [1 4 3 2;n 7 8 3 2;n 3 4 6 8;n 5 6 8 7;n 1 4 6 5;n 1 2 7 5;]; %6個面 n endn n function render(this,ax,camMat)n % Homogeneous coordinates (x,y,1)n vert = [this.vertices,ones(size(this.vertices,1),1)];n % Transform vertices from 3d world into 2d image planen vert2d = vert*camMat;n % Homogenous coordinates (x,y,1) to (x,y)n for i = 1:size(vert2d,1)n vertImage(i,1:2) = vert2d(i,1:2)/vert2d(i,3);n endn % draw patchn if isempty(this.patch) || ~isvalid(this.patch)n hold(ax,on);n this.patch = patch(Faces,this.faces,Vertices,vertImage,facecolor,r,faceAlpha,0.5);n hold(ax,off);n elsen n this.patch.Vertices= vertImage;n endn drawnow;n n endn n function clear(this)n delete(this.patch) n endn endnendn



因為我們通過extrinsics掌握了棋盤格和圖像平面的關係,這個虛擬的立方體是一直會待在棋盤格上的,不管視角如何變化,然而知乎並不能上傳gif……所以各位腦補下就好了。
這就是增強現實AR的簡單實現,如果大家仔細看的話會發現立方體並不立方,其原因有兩個
1. intrinsic不準,在camera calibration這一步myc嫌麻煩只粗略的用少量圖片進行了標定,誤差較大
Myc:怪我咯????
2. extrinsic不準,這些圖都是從視頻中截下來的,視頻拍攝存在抖動,會對棋盤格的檢測產生影響。
在實際操作中,標定的過程相對可控,所以誤差會較小,所以如何確定extrinsics是決定性的一步。visual fiducial無疑是最簡單的方式,然而有時條件不允許你在牆上給人家到處貼小廣告,於是問題就變得更複雜了。多數情況會採用SLAM再加硬體來提高精度,比如微軟的hololense.
想了解更多Computer Vision?
Multiple View Geometry in Computer Vision
推薦閱讀:
※如何評價 hololens 的『滑鼠出屏』交互?
※如何評價蘋果增強現實APP達到1000款 但開發者熱情大跌?
※数码暴龙(宝贝)是否会像宠物小精灵一样推出口袋妖怪go这样的ar虚拟游戏?

