張正友標(biāo)定的具體原理很多文章已經(jīng)介紹,這里主要結(jié)合源代碼對其中的基本原理及本人遇到的問題進(jìn)行介紹。(僅介紹基本原理供本人復(fù)習(xí),同時(shí)方便他人,如有問題,請及時(shí)指正勿噴)
1. 標(biāo)定基本思路介紹
相機(jī)標(biāo)定,即獲取其內(nèi)參、外參、畸變系數(shù)(內(nèi)參與外參及相機(jī)成像模型的解釋可以參考SLAM入門之視覺里程計(jì))。
具體可以描述為
s
m
?
?
=
K
[
R
T
]
M
s\overset{-}{\mathop{m}}\,=K[\begin{matrix} R & T \\ \end{matrix}]M
sm?=K[R?T?]M
其中s為世界坐標(biāo)系到圖像坐標(biāo)系的尺度因子,K為內(nèi)參矩陣,具體為
K
=
[
f
x
0
c
1
0
f
y
c
2
0
0
1
]
K=\left[ \begin{matrix} {{f}_{x}} & 0 & {{c}_{1}} \\ 0 & {{f}_{y}} & {{c}_{2}} \\ 0 & 0 & 1 \\ \end{matrix} \right]
K=
?fx?00?0fy?0?c1?c2?1?
?
[R T]為旋轉(zhuǎn)矩陣與平移向量,M為三維坐標(biāo)點(diǎn)。
張正友標(biāo)定方法的核心即是采用平面標(biāo)定板,從而可以方便的將標(biāo)定點(diǎn)的真值Z置為0,以此方便計(jì)算。
具體流程主要為
1、計(jì)算相機(jī)內(nèi)參(由于采用平面標(biāo)定板,可以很方面的計(jì)算出單應(yīng)矩陣,然后根據(jù)單應(yīng)矩陣計(jì)算出內(nèi)參)
- 計(jì)算初始相機(jī)外參(計(jì)算出內(nèi)參后,根據(jù)單應(yīng)矩陣直接計(jì)算即可獲得外參)
- 最大似然優(yōu)化相機(jī)內(nèi)參與外參(1、2得到的結(jié)果必定是帶有誤差的,因此需進(jìn)行優(yōu)化,此處誤差函數(shù)不考慮畸變)
- 計(jì)算畸變系數(shù)(以上一步算到的結(jié)果作為真值計(jì)算畸變系數(shù))
- 所有參數(shù)一起最大似然優(yōu)化 (將所有參數(shù)內(nèi)參、外參、畸變系數(shù)一起優(yōu)化)
%% 1. 計(jì)算初始相機(jī)內(nèi)參
K=[];
D=[];
RT_list=[];
[K,H_list] = CalInitK(CaliBoard,img_points);
%% 2. 計(jì)算初始相機(jī)外參
RT_list = CalInitRT(K,H_list);
%% 3. 最大似然優(yōu)化
[K,RT_list] = MinReprojErrorKRT(K,RT_list,CaliBoard,img_points);
%% 4. 計(jì)算畸變系數(shù)
% D : 畸變參數(shù),分別為k1,k2,k3,p1,p2
D = CalDAll(K,RT_list,CaliBoard,img_points);
%% 5. 所有參數(shù)一起最大似然優(yōu)化
[K,D,RT_list] = MinReprojErrorKDRT(K,D,RT_list,CaliBoard,img_points);
2. 相機(jī)內(nèi)參計(jì)算
function [K,H_list] = CalInitK(CaliBoard,img_points)
% 相機(jī)初始內(nèi)參計(jì)算
% 1.計(jì)算單應(yīng)矩陣
% 2.根據(jù)旋轉(zhuǎn)矩陣的特性,構(gòu)建方程
% 3.根據(jù)旋轉(zhuǎn)矩陣的特性,構(gòu)建方程
% inputs,
% CaliBoard : 標(biāo)定板真實(shí)坐標(biāo)點(diǎn)
% img_points :相機(jī)拍攝到的圖像提取到的標(biāo)定板點(diǎn)
%
% outputs,
% K : 相機(jī)內(nèi)參
% H_list : 單應(yīng)矩陣序列
K = [];
H_list = [];
%% 1.計(jì)算單應(yīng)矩陣
for i = 1:size(img_points,1)
img_points_tmp = cell2mat(img_points(i));
index = img_points_tmp(:,1);
tmp1 = CaliBoard(index,1:2);
tmp2 = img_points_tmp(:,2:3);
H = CalH(tmp1,tmp2);
H_list(i,:,:) = H;
end
%% 2.計(jì)算lambda*K^{-T}*k^{-1},lambda為放縮因子
B = CalB(H_list);
%% 3.根據(jù)B=lambda*K^{-T}*k^{-1}計(jì)算K,lambda為放縮因子
v0 = (B(1,2)*B(1,3)-B(1,1)*B(2,3))/(B(1,1)*B(2,2)-B(1,2)*B(1,2));
B_inv = inv(B);
lambda = B_inv(3,3);
B_inv = B_inv/lambda;
c_x = B_inv(1,3);
c_y = B_inv(2,3);
f_x = sqrt((B_inv(1,1)-c_x*c_x));
f_y = sqrt((B_inv(2,2)-c_y*c_y));
K=[f_x,0,c_x;
0,f_y,c_y;
0,0,1];
end
2.1 單應(yīng)矩陣計(jì)算
為方便計(jì)算,張正友采用了平面標(biāo)定板,因此內(nèi)參矩陣與旋轉(zhuǎn)平移矩陣的乘積可以視作單應(yīng)矩陣H即可得
s
m
?
?
=
K
[
R
T
]
M
=
H
M
s\overset{-}{\mathop{m}}\,=K[\begin{matrix} R & T \\ \end{matrix}]M\text{=}HM
sm?=K[R?T?]M=HM
上面的公式若僅依靠這個(gè)變換自然是不成立的,但是考慮到Z為0,則可以變換為
根據(jù)上述公式,可以列出等式
將其變換為對應(yīng)的矩陣(為方便計(jì)算,將
H
33
H_{33}
H33?視為1)
則可得
根據(jù)多個(gè)對應(yīng)點(diǎn)構(gòu)建多個(gè)上述方程,即可求解單應(yīng)矩陣H。如上述矩陣所述,一個(gè)對應(yīng)點(diǎn)可構(gòu)建2個(gè)方程,H矩陣總共有8個(gè)未知數(shù),則至少需要4個(gè)對應(yīng)點(diǎn)。
function H = CalH(CaliBoard,img_points)
% % 相機(jī)單應(yīng)矩陣計(jì)算
% 為方便計(jì)算,張正友采用了平面標(biāo)定板,因此內(nèi)參矩陣與旋轉(zhuǎn)平移矩陣的乘積可以視作單應(yīng)矩陣H即可得
% sm=K[r1 r2 T]M,將其K[r1 r2 T]視為H,重構(gòu)方程可得P1=H*P,s為尺度因子,暫時(shí)隱藏
% 根據(jù)P1=H*P得到兩個(gè)方程,將H視為向量,構(gòu)建方程
% 轉(zhuǎn)變成A*X=B的矩陣形式
% P1(X1,Y1),表示img_points
% P(X,Y),表示CaliBoard
% inputs,
% CaliBoard : 標(biāo)定板真實(shí)坐標(biāo)點(diǎn)
% img_points :相機(jī)拍攝到的圖像提取到的標(biāo)定板點(diǎn)(單張圖像)
%
% outputs,
% H : 單應(yīng)矩陣
H=[];
XX1 = CaliBoard(:,1).*img_points(:,1);
X1Y = img_points(:,1).*CaliBoard(:,2);
XY1 = CaliBoard(:,1).*img_points(:,2);
YY1 = CaliBoard(:,2).*img_points(:,2);
A = zeros(size(CaliBoard,1)*2,8);
for i=1:size(CaliBoard,1)
A(2*i-1,1:3) = [CaliBoard(i,1:2),1];
A(2*i-1,7:8) = [-XX1(i,1),-X1Y(i,1)];
A(2*i,4:6) = [CaliBoard(i,1:2),1];
A(2*i,7:8) = [-XY1(i,1),-YY1(i,1)];
end
B=reshape(img_points.',[size(CaliBoard,1)*size(CaliBoard,2),1]);
H = A\B;
H=[H;1];
H = reshape(H,[3,3])';
end
2.2 基于單應(yīng)矩陣計(jì)算相機(jī)內(nèi)參
如2.1中所說,單應(yīng)矩陣H=K[r1 r2 T],但因?yàn)橛?jì)算H時(shí)添加了
H
99
H_{99}
H99?為1的約束,因此計(jì)算出來的結(jié)果應(yīng)當(dāng)添加一個(gè)尺寸因子,即計(jì)算出來的
H
=
λ
?
K
?
H = \lambda*K*
H=λ?K?[r1 r2 T] ,可轉(zhuǎn)為
k
1
?
[
H
1
H
2
H
3
]
=
λ
?
[
r
1
r
2
T
]
k_1*[H_1 H_2 H_3] = \lambda*[r1 r2 T]
k1??[H1?H2?H3?]=λ?[r1r2T]
其中
H
1
,
H
2
,
H
3
H_1,H_2,H_3
H1?,H2?,H3?為單應(yīng)矩陣的列?;诖私Y(jié)果,以及旋轉(zhuǎn)矩陣的定義特性,可以構(gòu)建兩個(gè)方程,即
為了方便計(jì)算,可將
k
?
T
K
?
1
k^{-T}K^{-1}
k?TK?1視為一個(gè)矩陣B先行求解,可得
其中,由于B為
k
?
T
K
?
1
k^{-T}K^{-1}
k?TK?1,可得
k
?
T
K
?
1
=
(
k
?
T
K
?
1
)
T
k^{-T}K^{-1}=(k^{-T}K^{-1})^T
k?TK?1=(k?TK?1)T,則B為對稱矩陣,可將B定義為
由上式可知B中有6個(gè)未知數(shù),即最少需要3個(gè)H矩陣。同時(shí),上述B中的式子如果計(jì)算出來,可以看出
B
12
B_{12}
B12?為0,因此,增加了一個(gè)約束條件,最少只需要兩個(gè)H即可計(jì)算出結(jié)果。(此處為考慮下面n=2的情況)
附:關(guān)于標(biāo)定圖片數(shù)量n和內(nèi)參獲取的關(guān)系(來自參考文獻(xiàn)[2])
- 如果 n>=3,就可以得到唯一解b(帶有一個(gè)比例因子λ)。
- 如果 n=2,也就是只有兩張標(biāo)定圖片,那么我們可以設(shè)置內(nèi)參中的γ=0(γ表示由于制造誤差產(chǎn)生的兩個(gè)坐標(biāo)軸偏斜參數(shù),通常很小,可忽略),將前面式子(搬運(yùn)到下圖)中γ=0可以看到對應(yīng) B12=0,換句話說,就是增加了一個(gè)約束條件:[0, 1, 0, 0, 0, 0]b = 0。(此處從運(yùn)算角度上不理解,但若僅考慮未知數(shù)的數(shù)目,假設(shè)K只有4個(gè)未知數(shù),則2個(gè)H足夠計(jì)算)
- 如果n=1,只能假設(shè)u0, v0已知(位于圖像中心)且 γ=0,這樣只能解出fx, fy兩個(gè)參數(shù)。
基于此,即可計(jì)算出B的值。(公式圖片來自參考文獻(xiàn)[2])
然后基于B的值直接進(jìn)行求解即可計(jì)算出內(nèi)參。
附:此處本人采用直接計(jì)算出B的逆矩陣,得到 B ? 1 = K ? K T B^{-1}=K*K^{T} B?1=K?KT,即
所得結(jié)果也一致(暫時(shí)不知道區(qū)別)
function B = CalB(H_list)
% 相機(jī)內(nèi)參K^{-T}*k^{-1}計(jì)算
% 根據(jù)旋轉(zhuǎn)矩陣的性質(zhì),每個(gè)單應(yīng)矩陣可以得到兩個(gè)方程
% 最終得到 v_{12}*b=0,(v_{11}-v_{22})*b=0,其中b為[B11,B12,B13,B22,B23,B33]^T
% v為~{{v}_{ij}}={{\left[ \begin{matrix}
% \begin{matrix}
% {{h}_{i1}}{{h}_{j1}} & {{h}_{i1}}{{h}_{j2}}+{{h}_{i2}}{{h}_{j1}} & {{h}_{i2}}{{h}_{j2}} \\
% \end{matrix} & \begin{matrix}
% {{h}_{i3}}{{h}_{j1}}+{{h}_{i1}}{{h}_{j3}} & {{h}_{i3}}{{h}_{j2}}+{{h}_{i2}}{{h}_{j3}} & {{h}_{i3}}{{h}_{j3}} \\
% \end{matrix} \\
% \end{matrix} \right]}^{T}}
% inputs,
% H_list : 單應(yīng)矩陣列表
%
% outputs,
% B : K^{-T}*k^{-1}
B=[];
% 計(jì)算v
for i=1:size(H_list,1)
h = squeeze(H_list(i,:,:));
h=h'; % 公式計(jì)算時(shí)考慮按照列劃分的1,2,3,所以需轉(zhuǎn)置一下
tmp_v(1,1) = h(1,1)*h(2,1);
tmp_v(1,2) = h(1,2)*h(2,1)+h(1,1)*h(2,2);
tmp_v(1,3) = h(1,3)*h(2,1)+h(1,1)*h(2,3);
tmp_v(1,4) = h(1,2)*h(2,2);
tmp_v(1,5) = h(1,3)*h(2,2)+h(1,2)*h(2,3);
tmp_v(1,6) = h(1,3)*h(2,3);
tmp_v(2,1) = h(1,1)*h(1,1)-h(2,1)*h(2,1);
tmp_v(2,2) = 2*(h(1,2)*h(1,1)-h(2,2)*h(2,1));
tmp_v(2,3) = 2*(h(1,3)*h(1,1)-h(2,3)*h(2,1));
tmp_v(2,4) = h(1,2)*h(1,2)-h(2,2)*h(2,2);
tmp_v(2,5) = 2*(h(1,3)*h(1,2)-h(2,3)*h(2,2));
tmp_v(2,6) = h(1,3)*h(1,3)-h(2,3)*h(2,3);
v(2*i-1:2*i,:) = tmp_v;
end
[U,S,V] = JOS_SVD(v);
b=V(:,6)';
B =[b(1,1),b(1,2),b(1,3);
b(1,2),b(1,4),b(1,5);
b(1,3),b(1,5),b(1,6)];
end
3 計(jì)算相機(jī)外參
相機(jī)內(nèi)參知道后,根據(jù)公式
H
=
λ
?
K
?
H = \lambda*K*
H=λ?K?[r1 r2 T] 可直接計(jì)算外參,即
K
?
1
H
=
λ
?
K^{-1}H = \lambda*
K?1H=λ?[r1 r2 T]
由于旋轉(zhuǎn)矩陣每列的模為1,可直接計(jì)算出外參矩陣。
function [RT_list] = CalInitRT(K,H_list)
% 相機(jī)初始內(nèi)參計(jì)算
% r1 = lambda*M^-1*h_1,lambda與求解內(nèi)參時(shí)的不同,
% 個(gè)人理解求第二個(gè)尺度因子時(shí)需添加R為1的約束,求第一個(gè)時(shí)H本身是up to scale的,無關(guān)緊要。
% r2 = lambda*M^-1*h_2
% r3 = r1 x r2
% t = lambda*M_1*h_3
%
% inputs,
% K : 相機(jī)內(nèi)參
% H_list :單應(yīng)矩陣序列
%
% outputs,
% RT_list : 相機(jī)外參序列
RT_list = [];
K_inv = inv(K);
for i=1:size(H_list,1)
h = squeeze(H_list(i,:,:));
lambda = 1/norm(K_inv*h(:,1));
r1 = lambda*K_inv*h(:,1);
r2 = lambda*K_inv*h(:,2);
r3 = cross(r1,r2);
t = lambda*K_inv*h(:,3);
rt = [r1,r2,r3,t];
RT_list(i,:,:) = rt;
end
end
4. 最大似然優(yōu)化
根據(jù)上述計(jì)算出的內(nèi)參、外參矩陣,構(gòu)建最小重投影誤差函數(shù),優(yōu)化內(nèi)參外參。其中需要注意的幾個(gè)點(diǎn)主要為
- 旋轉(zhuǎn)矩陣應(yīng)轉(zhuǎn)換為向量
- 優(yōu)化是參數(shù)輸入進(jìn)去應(yīng)為一個(gè)向量
- 重投影誤差輸出可以為二維矩陣(即X,Y兩個(gè)方向)
function [K_new,RT_list_new] = MinReprojErrorKRT(K,RT_list,CaliBoard,img_points)
% 最小化重投影誤差函數(shù)
% inputs,
% K : 待優(yōu)化相機(jī)內(nèi)參
% RT_list : 待優(yōu)化旋轉(zhuǎn)平移矩陣
% CaliBoard : 標(biāo)定板真實(shí)坐標(biāo)點(diǎn)
% img_points :相機(jī)拍攝到的圖像提取到的標(biāo)定板點(diǎn)
%
% outputs,
% K_new : 優(yōu)化后的相機(jī)內(nèi)參
% RT_list : 優(yōu)化后的旋轉(zhuǎn)平移矩陣
K_new=[];
RT_list_new=[];
% 構(gòu)建變量及初始值
X_init (1:4) = [K(1,1),K(2,2),K(1,3),K(2,3)];
for i=1:size(RT_list,1)
X_init (i*6-1:i*6+1) = rodrigues(squeeze(RT_list(i,:,1:3)));
X_init (2+i*6:4+i*6) = RT_list(i,:,4)';
end
% 優(yōu)化參數(shù)
options = optimoptions(@lsqnonlin,'Algorithm','levenberg-marquardt');
f = @(x)ReprojErrorKRT(x,CaliBoard,img_points);
[result,resnorm,residual ] = lsqnonlin(f,X_init,[],[],options);
%結(jié)果重構(gòu)
K_new = K;
K_new(1,1) = result(1);
K_new(2,2) = result(2);
K_new(1,3) = result(3);
K_new(2,3) = result(4);
for i=1:size(RT_list,1)
RT_list_new(i,:,1:3) = rodrigues(result (i*6-1:i*6+1));
RT_list_new(i,:,4) = result (2+i*6:4+i*6);
end
end
function erros_list = ReprojErrorKRT(X,CaliBoard,img_points)
% 重投影誤差函數(shù)
% inputs,
% X: 待優(yōu)化的參數(shù),
% X(1:4),f_x,f_y,c_x,c_y
% X(5:7) r
% X(8:10) t
% 以此類推,直到結(jié)束
% CaliBoard : 標(biāo)定板真實(shí)坐標(biāo)點(diǎn)
% img_points :相機(jī)拍攝到的圖像提取到的標(biāo)定板點(diǎn)
%
% outputs,
% erros_list : 重投影誤差(x,y兩個(gè)方向,二維矩陣)
repro= 0;
repro_list=[];
erros_list=[];
K=[X(1),0,X(3);
0,X(2),X(4);
0,0,1];
CaliBoard = [CaliBoard(:,1:2),ones(size(CaliBoard,1),1)];
for i=1:size(img_points,1)
img_points_tmp = cell2mat(img_points(i));
index = img_points_tmp(:,1);
CaliBoard_tmp = CaliBoard(index,:);
R= rodrigues( X (i*6-1:i*6+1) );
T= X (2+i*6:4+i*6)';
RT = [R(:,1:2),T];
img_points_proj = K*RT*CaliBoard_tmp';
img_points_proj(1,:)=img_points_proj(1,:)./img_points_proj(3,:);
img_points_proj(2,:)=img_points_proj(2,:)./img_points_proj(3,:);
img_points_proj(3,:)=1;
img_points_proj=img_points_proj';
error =squeeze(img_points_tmp(:,2:3))-img_points_proj(:,1:2);
erros_list=[erros_list;error];
% 調(diào)試用
erros_dis =vecnorm(error,2,2);
repro = repro+sum(erros_dis.*erros_dis);
repro_list=[repro_list;sum(erros_dis.*erros_dis)];
end
end
5. 計(jì)算畸變系數(shù)
在不考慮畸變系數(shù)的情況下優(yōu)化完內(nèi)外參數(shù)后,可以計(jì)算畸變系數(shù)。需要注明,畸變變換是在歸一化平面進(jìn)行的,因此計(jì)算時(shí)可以直接將坐標(biāo)變換到歸一化坐標(biāo)系方便進(jìn)行計(jì)算,也方便進(jìn)行理解。
設(shè)(u,v)為標(biāo)定板根據(jù)外參投影到歸一化平面上的點(diǎn)坐標(biāo),
(
u
d
i
s
,
v
d
i
s
)
(u_{dis},v_{dis})
(udis?,vdis?)為畸變后的坐標(biāo),也即實(shí)際圖像坐標(biāo)根據(jù)內(nèi)參反向映射到歸一化平面。則可得
其中
r
2
=
u
2
+
v
2
r^2=u^2+v^2
r2=u2+v2
將其轉(zhuǎn)化為方程,則是
可以寫成
A
D
=
d
AD=d
AD=d
則可直接計(jì)算出畸變參數(shù)。
附:注意事項(xiàng)
- 注意變換是在歸一化坐標(biāo)系,所以最好的辦法就是轉(zhuǎn)換到歸一化坐標(biāo)系進(jìn)行計(jì)算,不然容易發(fā)生錯(cuò)誤。
function D = CalDAll(K,RT_list,CaliBoard,img_points)
% 畸變參數(shù)計(jì)算函數(shù),徑向畸變與切向畸變
%將函數(shù)記成A*D=d
% inputs,
% K : 相機(jī)內(nèi)參
% RT_list : 旋轉(zhuǎn)平移矩陣
% CaliBoard : 標(biāo)定板真實(shí)坐標(biāo)點(diǎn)
% img_points :相機(jī)拍攝到的圖像提取到的標(biāo)定板點(diǎn)
%
% outputs,
% dis : 畸變參數(shù),分別為k1,k2,k3,p1,p2
img_points_all = [];
CaliBoard = [CaliBoard(:,1:2),ones(size(CaliBoard,1),1)];
img_points__proj_norm_all = [];
% 重投影獲取歸一化坐標(biāo)點(diǎn)
for i=1:size(img_points,1)
img_points_tmp = cell2mat(img_points(i));
RT = [squeeze(RT_list(i,:,1:2)),squeeze(RT_list(i,:,4))'];
%獲取所有圖像點(diǎn)
img_points_all=[img_points_all;squeeze(img_points_tmp(:,2:3))];
% 理想點(diǎn)在歸一化坐標(biāo)系上的點(diǎn) x
img_points_proj_norm = RT*CaliBoard';
img_points_proj_norm(1,:)=img_points_proj_norm(1,:)./img_points_proj_norm(3,:);
img_points_proj_norm(2,:)=img_points_proj_norm(2,:)./img_points_proj_norm(3,:);
img_points_proj_norm(3,:)=1;
img_points_proj_norm=img_points_proj_norm';
img_points__proj_norm_all= [img_points__proj_norm_all;img_points_proj_norm(:,1:2)];
end
% 畸變后的點(diǎn)在歸一化坐標(biāo)系上的點(diǎn) x_dis
img_points_norm_all = inv(K)*[img_points_all,ones(size(img_points_all,1),1)]';
img_points_norm_all =img_points_norm_all';
% 計(jì)算D,d
r2 = sum(img_points__proj_norm_all.*img_points__proj_norm_all,2);
r4 = r2.*r2;
r6 = r2.*r4;
u = img_points__proj_norm_all(:,1);
v = img_points__proj_norm_all(:,2);
u_dis = img_points_norm_all(:,1);
v_dis = img_points_norm_all(:,2);
for i=1:size(img_points_all,1)
A(2*i-1,1) = u(i,1)*r2(i,1);
A(2*i-1,2) = u(i,1)*r4(i,1);
A(2*i-1,3) = u(i,1)*r6(i,1);
A(2*i-1,4) = 2*u(i,1)*v(i,1);
A(2*i-1,5) = r2(i,1)+2*u(i,1)*u(i,1);
A(2*i,1) = v(i,1)*r2(i,1);
A(2*i,2) = v(i,1)*r4(i,1);
A(2*i,3) = v(i,1)*r6(i,1);
A(2*i,4) = r2(i,1)+2*v(i,1)*v(i,1);
A(2*i,5) = 2*u(i,1)*v(i,1);
d(2*i-1,1) = u_dis(i,1)-u(i,1);
d(2*i,1) = v_dis(i,1)-v(i,1);
end
D = A\d;
end
6. 考慮畸變系數(shù)的優(yōu)化
與4中基本一致,只是投影時(shí)考慮到了畸變參數(shù)。
function [K_new,D_new,RT_list_new] = MinReprojErrorKDRT(K,D,RT_list,CaliBoard,img_points)
% 最小化重投影誤差函數(shù)
% inputs,
% K : 待優(yōu)化相機(jī)內(nèi)參
% D: 待優(yōu)化的相機(jī)畸變參數(shù)
% RT_list : 待優(yōu)化旋轉(zhuǎn)平移矩陣
% CaliBoard : 標(biāo)定板真實(shí)坐標(biāo)點(diǎn)
% img_points :相機(jī)拍攝到的圖像提取到的標(biāo)定板點(diǎn)
%
% outputs,
% K_new : 優(yōu)化后的相機(jī)內(nèi)參
% D_new: 優(yōu)化后的相機(jī)畸變參數(shù)
% RT_list : 優(yōu)化后的旋轉(zhuǎn)平移矩陣
K_new=[];
RT_list_new=[];
D_new= [];
% 構(gòu)建變量及初始值
X_init (1:4) = [K(1,1),K(2,2),K(1,3),K(2,3)];
X_init(5:9) = D;
for i=1:size(RT_list,1)
X_init (i*6+4:i*6+6) = rodrigues(squeeze(RT_list(i,:,1:3)));
X_init (7+i*6:9+i*6) = RT_list(i,:,4)';
end
% 優(yōu)化參數(shù)
options = optimoptions(@lsqnonlin,'Algorithm','levenberg-marquardt');
f = @(x)ReprojErrorKDRT(x,CaliBoard,img_points);
[result,resnorm,residual ] = lsqnonlin(f,X_init,[],[],options);
%結(jié)果重構(gòu)
K_new = K;
K_new(1,1) = result(1);
K_new(2,2) = result(2);
K_new(1,3) = result(3);
K_new(2,3) = result(4);
D_new = result(5:9);
for i=1:size(RT_list,1)
RT_list_new(i,:,1:3) = rodrigues(result (i*6+4:i*6+6));
RT_list_new(i,:,4) = result (7+i*6:9+i*6);
end
end
function error = ReprojErrorKDRT(X,CaliBoard,img_points)
% 重投影誤差函數(shù),考慮畸變參數(shù)
% inputs,
% X: 待優(yōu)化的參數(shù),
% X(1:4),f_x,f_y,c_x,c_y
% X(5:9),k1,k2,k3,p1,p2
% X(10:12) r
% X(12:14) t
% 以此類推,直到結(jié)束
% CaliBoard : 標(biāo)定板真實(shí)坐標(biāo)點(diǎn)
% img_points :相機(jī)拍攝到的圖像提取到的標(biāo)定板點(diǎn)
%
% outputs,
% erros_list : 重投影誤差(x,y兩個(gè)方向,二維矩陣)
repro= 0;
repro_list=[];
K=[X(1),0,X(3);
0,X(2),X(4);
0,0,1];
k1 = X(5);
k2 = X(6);
k3 = X(7);
p1 = X(8);
p2 = X(9);
CaliBoard = [CaliBoard(:,1:2),ones(size(CaliBoard,1),1)];
img_points_proj_norm_all=[];
img_points_all=[];
% 重投影獲取像素坐標(biāo)點(diǎn)
for i=1:size(img_points,1)
img_points_tmp = cell2mat(img_points(i));
index = img_points_tmp(:,1);
CaliBoard_tmp = CaliBoard(index,:);
R= rodrigues( X (i*6+4:i*6+6) );
T= X (7+i*6:9+i*6)';
RT = [R(:,1:2),T];
% 理想點(diǎn)在歸一化坐標(biāo)系上的點(diǎn) x
img_points_proj_norm = RT*CaliBoard';
img_points_proj_norm(1,:)=img_points_proj_norm(1,:)./img_points_proj_norm(3,:);
img_points_proj_norm(2,:)=img_points_proj_norm(2,:)./img_points_proj_norm(3,:);
img_points_proj_norm(3,:)=1;
img_points_proj_norm=img_points_proj_norm';
img_points_proj_norm_all= [img_points_proj_norm_all;img_points_proj_norm(:,1:2)];
%獲取所有圖像點(diǎn)
img_points_all=[img_points_all;squeeze(img_points_tmp(:,2:3))];end
% 計(jì)算D,d
r2 = sum(img_points_proj_norm_all.*img_points_proj_norm_all,2);
r4 = r2.*r2;
r6 = r2.*r4;
x = img_points_proj_norm_all(:,1);
y = img_points_proj_norm_all(:,2);
x_dis = x.*(1+k1.*r2+k2.*r4+k3.*r6)+2*p1*(x.*y)+p2*(r2+2*x.*x);
y_dis = y.*(1+k1.*r2+k2.*r4+k3.*r6)+2*p2*(x.*y)+p1*(r2+2*y.*y);
img_points_proj_all = K*[x_dis,y_dis,ones(size(x_dis,1),1)]';
img_points_proj_all = img_points_proj_all';
error = img_points_proj_all(:,1:2)-img_points_all;
end
參考文獻(xiàn)
[1] 張正友標(biāo)定算法原理詳解.https://www.jianshu.com/p/9d2fe4c2e3b7
[2]張正友標(biāo)定法-完整學(xué)習(xí)筆記-從原理到實(shí)戰(zhàn). https://zhuanlan.zhihu.com/p/136827980
[3]張正友標(biāo)定法.https://www.cnblogs.com/linzzz98/articles/13604279.html文章來源:http://www.zghlxwxcb.cn/news/detail-441516.html
相關(guān)資源代碼
部分庫可以下載看
https://download.csdn.net/download/zhangpan333/85445628文章來源地址http://www.zghlxwxcb.cn/news/detail-441516.html
到了這里,關(guān)于張正友相機(jī)標(biāo)定(全流程,含畸變,matlab源代碼解析)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!