一 攝像機成像模型
成像的過程實質上是幾個坐標系的轉換。首先空間中的一點由世界坐標系轉換到攝像機坐標系,然后再將其投影到成像平面(攝像機的CCD),最后再將成像平面上的數據轉換到圖像平面(最后生成的圖像)。

圖1-1
世界空間內的一個點在圖像上成像的過程稱為投影成像過程,這中間轉換過程構成的矩陣M稱為投影矩陣。
攝像機的畸變參數與相機成像時采用的分辨率無關,而fx,fy和光心位置cx,cy與分辨率有關,但是成倍數關系,例如在分辨率320×240分辨率標定得到內參數為fx0,fy0,cx0,cy0,那么在分辨率640×480下對應的參數則為2fx0,2fy0,2cx0,2cy0。
上面這些參數中,f,t所采用的量綱都是mm,而dx和dy指的是每個像素個數的大小,即mm/pixel,那么經過換算最后得到的u、v的量綱自然也就是像素。fx和fy是由f/dx,f/dy計算而來,表示的是長度值為f時對應的像素個數,也即長度值為f(mm)時,在圖像平面的x方向和y方向的像素個數,fx、fy、u0、v0構成A,A右邊的數據為[R|t]M’,看起來[R|t]M’對應的數據的量綱好像是mm,因為M’和t的量綱都是mm,這樣A里面的量綱又為像素,這樣乘起來的量綱就是mm*pixel了,這顯然不對。但是事實情況並不是真的這樣,最后[R|t]M’與A相乘的時候,[R|t]M’的結果是需要進行歸一化的,即[xc yc zc 1] →[xc/zc yc/zc 1],這個時候它的量綱其實就只是倍數了。整個過程如下圖

圖 1-2
OpenCV標定出來的內參數矩陣一般為(分辨率為640×480)
<Camera_Matrix type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
5.3193633829444082e+002 0. 3.1950000000000000e+002 0.
5.3193633829444082e+002 2.3950000000000000e+002 0. 0. 1.</data></Camera_Matrix>
這里的fx≈531.9,fy≈531.9,u0=319.5,v0=239.5。u0和v0比較好理解,差不多就是圖像的中心,但是這里的fx和fy為500多還是有點抽象,雖然前面已經說了表示焦距長度對應的像素個數。根據上圖中u和v的計算公式,還有標定的內參矩陣值,其實可以反推出x’、y’的大致大小,這里u的最大值是640,v的最大值為480,用這些值可以算出x’和y’的最大值分別為0.6025和0.4521,它們分別代表經過Z值歸一化后的比例值,這個值實質上反映了相機的視角。
二 成像畸變
在沒有畸變的情況下,理想的成像的過程是如圖1-2所示
但是在實際的情況中,真正的鏡頭通常有一些形變,主要的變形為徑向形變,也會有輕微的切向形變。所以上面的模型需要擴展為:

圖2-1
k1和k2是徑向形變系數,p1和p2是切向形變系數。OpenCV中沒有考慮高階系數。形變系數跟拍攝的場景無關,因此它們是內參數,而且與拍攝圖像的分辨率無關。參見opencv中文wiki
opencv計算得到的畸變參數形式為:
<Distortion_Coefficients type_id="opencv-matrix">
<rows>5</rows>
<cols>1</cols>
<dt>d</dt>
<data>
6.7442695842244271e-002 2.4180872220967919e-001 0. 0.
-3.3970575589699975e-001</data></Distortion_Coefficients>
對應存放的畸變參數為[k1,k2,p1,p2[,k3[,k4,k5,k6]]
三 畸變校正
根據前面的說明,可以知道攝像機拍下來的圖片是存在畸變的,現在需要根據內參矩陣和畸變參數對存在畸變的圖像進行校正。校正的過程就是就是圖2-1描述的過程。
首先是根據拍攝得到的存在畸變圖像的坐標,計算出根據畸變模型得到的校正圖像對應的坐標,這樣就建立了畸變圖像坐標和校正圖像坐標之間的映射關系。同時計算出來的校正坐標存在小數,所以就需要對其插值。
例如XYsrc(1,1)<-->XYrect(1.5,1.0),那么假如采用簡單的線性插值的話,Irect(1.5,1.0)=[Isrc(1,1)+Isrc(2,1)]*0.5
Irect就是算出來的校正后的像素值。
進行畸變的matlab校正代碼(參見stackoverflow)如下:
- clear;
- clc;
- A =[5.9418398977142772e+002 0 3.1950000000000000e+002;
- 0 5.941839897714e+002 2.3950000000000000e+002;
- 0 0 1];
- D = [6.7442695842244271e-002 2.4180872220967919e-001 0 0 -3.3970575589699975e-001];
- fx = A(1,1);
- fy = A(2,2);
- cx = A(1,3);
- cy = A(2,3);
- k1 = D(1);
- k2 = D(2);
- k3 = D(5);
- p1 = D(3);
- p2 = D(4);
- K = A;
- Idistorted = imread('logi.jpg');
- Idistorted = rgb2gray(Idistorted);
- Idistorted = im2double(Idistorted);
- I = zeros(size(Idistorted));
- [i j] = find(~isnan(I));
- % Xp = the xyz vals of points on the z plane
- Xp = inv(K)*[j i ones(length(i),1)]';
- % Now we calculate how those points distort i.e forward map them through the distortion
- r2 = Xp(1,:).^2+Xp(2,:).^2;
- x = Xp(1,:);
- y = Xp(2,:);
- x = x.*(1+k1*r2 + k2*r2.^2) + 2*p1.*x.*y + p2*(r2 + 2*x.^2);
- y = y.*(1+k1*r2 + k2*r2.^2) + 2*p2.*x.*y + p1*(r2 + 2*y.^2);
- % u and v are now the distorted cooridnates
- u = reshape(fx*x + cx,size(I));
- v = reshape(fy*y + cy,size(I));
- % Now we perform a backward mapping in order to undistort the warped image coordinates
- I = interp2(Idistorted, u, v);
- subplot(121); imagesc(Idistorted);
- subplot(122); imagesc(I);
得到的結果圖為:

