透視變換與單應性(Homography)矩陣求解

1 平面與球面投影透視

       我們這裏所說的透視,簡單地說就是將三維物理空間中的點經過與光學中心的連線投影到某個面上,這個面可以是平面,也可以是曲面,如圖 1 是一個簡單的示例。平面投影透視的應用最爲廣泛,我們絕大多數的相機鏡頭都是基於這種方式設計的,不過這不代表鏡頭就一定是平面的。其特點是空間中的直線在平面上的投影依然是直線,這樣在某種意義上我們可以認爲是沒有畸變的,當然近大遠小作爲一種物理性質始終不可避免。這種方式實際上就類似於小孔成像,爲了方便我們可以把該投影平面當作是最終的成像平面。但這種方式的缺點也很明顯,由於成像點到成像平面中心的距離與入射角是正切的關係,其有效視角通常比較小。

圖1 透視示例

       爲了擴大鏡頭的視角,我們可以採用球面投影透視。而球面是一個曲面,我們很難設計一個球面的感光器件,所以,我們還需要一個從球面到平面的映射過程,這通常是基於折射的原理的來實現的。如果折射角小於入射角,那麼我們就可以把更大的視角壓縮到一張平面圖像上了,就是魚眼鏡頭的基本設計原理。如圖 2 所示,空間中的一點經過球面投影透視投影到了 PP 點,然後經過折射投影到了 P1P_1 點,但通常成像平面轉換到最終的圖像需要旋轉180°,所以其在魚眼圖像上的座標應該爲 P2P_2。這種方式明顯的缺點是物理空間中的直線在魚眼圖像上通常不再是直線,所以在我們的日常攝影中一般比較少用。關於球面投影透視和魚眼圖像的相關內容這裏不多說,接下來我們主要討論平面投影透視,以及給定兩幅圖像中一些相匹配的特徵點的座標,我們應該怎樣求解相關的變換參數。

圖2 魚眼成像示例

2 透視變換與仿射變換

       一般來說,我們會以鏡頭的光學中心作爲原點建立座標參考系,並且令成像平面爲 z=1z=1,那麼對於物理空間中的一點 X=(X,Y,Z){\bf{X}}=(X,Y,Z),其經過平面投影透視在成像平面上的座標可表示爲
x=(x,y,1)=1Z(X,Y,Z)=1ZX.(1){\bf{x}} = \left( {x,y,1} \right) = \frac{1}{Z}\left( {X,Y,Z} \right) = \frac{1}{Z}{\bf{X}}. \tag{1}另外,我們可以使用一個 3x3 的矩陣 H{\bf{H}} 來描述三維空間中的線性座標變換,即
(X2Y2Z2)=(h11h12h13h21h22h23h31h32h33)(X1Y1Z1).(2)\left( {\begin{array}{c} {{X_2}}\\ {{Y_2}}\\ {{Z_2}} \end{array}} \right) = \left( {\begin{array}{c} {{h_{11}}}&{{h_{12}}}&{{h_{13}}}\\ {{h_{21}}}&{{h_{22}}}&{{h_{23}}}\\ {{h_{31}}}&{{h_{32}}}&{{h_{33}}} \end{array}} \right)\left( {\begin{array}{c} {{X_1}}\\ {{Y_1}}\\ {{Z_1}} \end{array}} \right). \tag{2} 那麼對於成像平面上的點,有
x2=1Z2X2=1Z2HX1=Z1Z2Hx1.(3){{\bf{x}}_2} = \frac{1}{{{Z_2}}}{{\bf{X}}_2} = \frac{1}{{{Z_2}}}{\bf{H}}{{\bf{X}}_1} = \frac{{{Z_1}}}{{{Z_2}}}{\bf{H}}{{\bf{x}}_1}. \tag{3}這就是二維平面圖像的透視變換,我們一般稱 H\bf{H} 爲單應性(Homography)矩陣。然而透視本身是不可逆的,如果僅有一張平面圖像,我們無法知道其原來的 Z1{Z_1} 值,所以我們通常令 Z1=1{Z_1=1}。那麼可得
x2=h11x1+h12y1+h13h31x1+h32y1+h33,y2=h21x1+h22y1+h23h31x1+h32y1+h33.(4){x_2} = \frac{{{h_{11}}{x_1} + {h_{12}}{y_1} + {h_{13}}}}{{{h_{31}}{x_1} + {h_{32}}{y_1} + {h_{33}}}},{\rm{ }}{y_2} = \frac{{{h_{21}}{x_1} + {h_{22}}{y_1} + {h_{23}}}}{{{h_{31}}{x_1} + {h_{32}}{y_1} + {h_{33}}}}.\tag{4}雖然式(2)表示的是一個線性變換,但在透視變換中,因爲我們需要對 Z2{Z_2} 進行歸一化,而各個點的 Z2{Z_2} 並不一定相等,這就引入了非線性的過程,所以透視變換不屬於線性變換,也就是說第一個圖像平面中的平行線在第二個圖像平面中不一定平行。但由於採用的是平面投影透視,其中的直線依然還會保持直線。

       考慮一種特殊的情況,假如令 h31=h32=0,h33=1h_{31}=h_{32}=0, h_{33}=1,即令
A=(a11a12a13a21a22a23001). {\bf{A}}= \left( {\begin{array}{c} {{a_{11}}}&{{a_{12}}}&{{a_{13}}}\\ {{a_{21}}}&{{a_{22}}}&{{a_{23}}}\\ 0&0&1 \end{array}} \right). 那麼對於式(3),我們始終可以保證 Z2=1Z_2=1,這就意味着不再需要對變換後的座標的 zz 軸進行歸一化,這時式(3)就代表了一個平面上的線性變換,z=1z=1 平面上的點經過變換後還是落在 z=1z=1 平面上,我們稱這種變換爲仿射(Affine)變換。由於仿射變換屬於線性變換,平面上的平行線經過變換後依然保持平行。

       從透視變換到仿射變換,實際反映了變換參數自由度的縮減。對於透視變換,其總共有 9 個可變化的參數,因此靈活性最高,通過變換可改變物體在平面上的基本形狀。而仿射變換隻剩下了 6 個可變參數,使得原本的非線性變換轉換爲線性變換,因此無法改變物體中線條的平行性,在一定程度上保留了物體的基本形狀。同樣,我們還可以繼續縮減變換參數的自由度,例如只允許物體在平面上平移、旋轉以及寬高等比例縮放,這時物體的形狀除了大小以外沒有任何改變,因此稱爲剛體(Rigid)或歐氏(Euclidean)變換,即
(x2y21)=(scosθssinθtxssinθscosθty001)(x1y11).(5)\left( {\begin{array}{c} {{x_2}}\\ {{y_2}}\\ 1 \end{array}} \right) = \left( {\begin{array}{c} {s \cdot \cos \theta }&{ - s \cdot \sin \theta }&{tx}\\ {s \cdot \sin \theta }&{s \cdot \cos \theta }&{ty}\\ 0&0&1 \end{array}} \right)\left( {\begin{array}{c} {{x_1}}\\ {{y_1}}\\ 1 \end{array}} \right). \tag{5}那麼這時變換參數的自由度只剩下 4 了,由於。實際上,式(5)還可以繼續分解爲自由度更低的變換的組合,例如
(x2y21)=(10tx01ty001)(s000s0001)(cosθsinθ0sinθcosθ0001)(x1y11).(6)\left( {\begin{array}{c} {{x_2}}\\ {{y_2}}\\ 1 \end{array}} \right) = \left( {\begin{array}{c} 1&0&{tx}\\ 0&1&{ty}\\ 0&0&1 \end{array}} \right)\left( {\begin{array}{c} s&0&0\\ 0&s&0\\ 0&0&1 \end{array}} \right)\left( {\begin{array}{c} {\cos \theta }&{ - \sin \theta }&0\\ {\sin \theta }&{\cos \theta }&0\\ 0&0&1 \end{array}} \right)\left( {\begin{array}{c} {{x_1}}\\ {{y_1}}\\ 1 \end{array}} \right).\tag{6} 也就是先進行只有 1 自由度的旋轉,然後再進行 1 自由度的寬高等比例縮放,最後進行 2 自由度的水平或垂直的平移。實際上,式(5)可以表示任意次數與順序的平移、旋轉、寬高等比例縮放的變換組合的最終形式,但要注意,由於矩陣的乘法不滿足交換律,相同的變換參數採用不同的變換順序有可能導致不同的變換結果。

       綜上,雖然我們只使用了一個簡單的 3x3 矩陣,但這已經足以描述我們日常生活中經常遇到的大部分簡單的運動了,其具體形式如圖3所示,包括平移、旋轉、縮放、仿射以及透視變換等等。

圖3 常見的變換與運動模型

3 仿射變換的參數優化

       假設我們得到物體上的一些點(通常爲特徵點或顯著點)在兩幅平面圖像上對應座標,例如分別爲 (xi1,yi1)(x_{i1}, y_{i1})(xi2,yi2)(x_{i2}, y_{i2}),並且該物體的運動形式爲前面圖 3 所提到的幾種之一,那麼我們應該怎樣根據這些匹配點對來估計相應的變換參數呢?雖然所有這些參數都是來自於一個 3x3 的矩陣,但由於透視變換是非線性變換,而仿射以及其他更低自由度的變換爲線性變換,兩者的參數優化過程還是有很大不同的。接下來我們先討論仿射變換的參數優化過程,而其他更低自由度的變換的參數優化過程基本類似,不再贅述。

       由於參數優化過程中座標是已知量,而參數纔是未知數,將座標變換的式子重排可得
(x11y111000x21y211000000x11y111000x21y211)(a11a12a13a21a22a23)=(x12x22y12y22)Aa=b.(7)\left( {\begin{array}{c} {{x_{11}}}&{{y_{11}}}&1&0&0&0\\ {{x_{21}}}&{{y_{21}}}&1&0&0&0\\ \vdots & \vdots & \vdots & \vdots & \vdots & \vdots \\ 0&0&0&{{x_{11}}}&{{y_{11}}}&1\\ 0&0&0&{{x_{21}}}&{{y_{21}}}&1\\ \vdots & \vdots & \vdots & \vdots & \vdots & \vdots \end{array}} \right)\left( {\begin{array}{c} {{a_{11}}}\\ {{a_{12}}}\\ {{a_{13}}}\\ {{a_{21}}}\\ {{a_{22}}}\\ {{a_{23}}} \end{array}} \right) = \left( {\begin{array}{c} {{x_{12}}}\\ {{x_{22}}}\\ \vdots \\ {{y_{12}}}\\ {{y_{22}}}\\ \vdots \end{array}} \right) \Leftrightarrow {\bf{Aa}} = {\bf{b}}.\tag{7}上述方程組爲超定方程組,那麼根據線性最小二乘法可得
a=(ATA)1ATb,(8){\bf{a}} = {\left( {{{\bf{A}}^T}{\bf{A}}} \right)^{ - 1}}{{\bf{A}}^T}{\bf{b}},\tag{8} 不過這種直接求逆的方法在實際應用過程中不一定是最佳的,我們可以將其轉換爲求解以下線性方程組
ATAa=ATb,(9){{\bf{A}}^T}{\bf{Aa}} = {{\bf{A}}^T}{\bf{b}},\tag{9} 其中
ATA=(i=1nxi1xi1i=1nxi1yi1i=1nxi1000i=1nxi1yi1i=1nyi1yi1i=1nyi1000i=1nxi1i=1nyi1i=1n1000000i=1nxi1xi1i=1nxi1yi1i=1nxi1000i=1nxi1yi1i=1nyi1yi1i=1nyi1000i=1nxi1i=1nyi1i=1n1),{{\bf{A}}^T}{\bf{A}} = \left( {\begin{array}{c} {\sum\limits_{i = 1}^n {{x_{i1}}{x_{i1}}} }&{\sum\limits_{i = 1}^n {{x_{i1}}{y_{i1}}} }&{\sum\limits_{i = 1}^n {{x_{i1}}} }&0&0&0\\ {\sum\limits_{i = 1}^n {{x_{i1}}{y_{i1}}} }&{\sum\limits_{i = 1}^n {{y_{i1}}{y_{i1}}} }&{\sum\limits_{i = 1}^n {{y_{i1}}} }&0&0&0\\ {\sum\limits_{i = 1}^n {{x_{i1}}} }&{\sum\limits_{i = 1}^n {{y_{i1}}} }&{\sum\limits_{i = 1}^n 1 }&0&0&0\\ 0&0&0&{\sum\limits_{i = 1}^n {{x_{i1}}{x_{i1}}} }&{\sum\limits_{i = 1}^n {{x_{i1}}{y_{i1}}} }&{\sum\limits_{i = 1}^n {{x_{i1}}} }\\ 0&0&0&{\sum\limits_{i = 1}^n {{x_{i1}}{y_{i1}}} }&{\sum\limits_{i = 1}^n {{y_{i1}}{y_{i1}}} }&{\sum\limits_{i = 1}^n {{y_{i1}}} }\\ 0&0&0&{\sum\limits_{i = 1}^n {{x_{i1}}} }&{\sum\limits_{i = 1}^n {{y_{i1}}} }&{\sum\limits_{i = 1}^n 1 } \end{array}} \right), ATb=(i=1nxi1xi2i=1nyi1xi2i=1nxi2i=1nxi1yi2i=1nyi1yi2i=1nyi2)T.{{\bf{A}}^T}{\bf{b}} = {\left( {\begin{array}{c} {\sum\limits_{i = 1}^n {{x_{i1}}{x_{i2}}} }&{\sum\limits_{i = 1}^n {{y_{i1}}{x_{i2}}} }&{\sum\limits_{i = 1}^n {{x_{i2}}} }&{\sum\limits_{i = 1}^n {{x_{i1}}{y_{i2}}} }&{\sum\limits_{i = 1}^n {{y_{i1}}{y_{i2}}} }&{\sum\limits_{i = 1}^n {{y_{i2}}} } \end{array}} \right)^T}.

4 透視變換的參數優化

       上面我們可以看到仿射變換的參數優化過程還是比較簡單的,那爲什麼透視變換的參數優化還要單獨去說呢?實際上,由於透視變換引入了一個 zz 軸歸一化的過程,整個參數優化的過程也發生了很大的改變。將式(4)重排可得
(x11y111000x11x12y11x12x12x21y211000x21x22y21x22x22000x11y111x11y12y11y12y12000x21y211x21y22y21y22y22)(h11h12h13h21h22h23h31h32h33)=(0000).(10)\left( {\begin{array}{c} {{x_{11}}}&{{y_{11}}}&1&0&0&0&{ - {x_{11}}{x_{12}}}&{ - {y_{11}}{x_{12}}}&{ - {x_{12}}}\\ {{x_{21}}}&{{y_{21}}}&1&0&0&0&{ - {x_{21}}{x_{22}}}&{ - {y_{21}}{x_{22}}}&{ - {x_{22}}}\\ \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots \\ 0&0&0&{{x_{11}}}&{{y_{11}}}&1&{ - {x_{11}}{y_{12}}}&{ - {y_{11}}{y_{12}}}&{ - {y_{12}}}\\ 0&0&0&{{x_{21}}}&{{y_{21}}}&1&{ - {x_{21}}{y_{22}}}&{ - {y_{21}}{y_{22}}}&{ - {y_{22}}}\\ \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots \end{array}} \right)\left( {\begin{array}{c} {{h_{11}}}\\ {{h_{12}}}\\ {{h_{13}}}\\ {{h_{21}}}\\ {{h_{22}}}\\ {{h_{23}}}\\ {{h_{31}}}\\ {{h_{32}}}\\ {{h_{33}}} \end{array}} \right) = \left( {\begin{array}{c} 0\\ 0\\ \vdots \\ 0\\ 0\\ \vdots \end{array}} \right).\tag{10} 也就是說,透視變換的參數優化變成了一個求解線性齊次方程組 Ah=0{\bf{Ah=0}} 的問題。我們知道,對於線性齊次方程組,如果 h{\bf{h}} 是它的解,那麼 kh{k\bf{h}} 也是它的解,這從式(4)也可以看出,因爲 zz 軸需要進行歸一化,所以 h{\bf{h}} 的長度並不影響最終的結果。更一般地,對於線性非齊次方程組 Ax=b{\bf{Ax=b}},假設 A{\bf{A}} 的行數不少於列數,且列數爲 nn,秩爲 rr,有唯一解的充要條件是 R(A,b)=R(A)=r=n{R({\bf{A, b}})=R({\bf{A}})=r=n},而有無限多解的充要條件是 R(A,b)=R(A)=r<n{R({\bf{A, b}})=R({\bf{A}})=r<n},且對應的齊次方程組 Ax=0{\bf{Ax=0}} 的基礎解系裏的最大無關向量的個數爲 nrn-r,那麼 Ax=b{\bf{Ax=b}} 的通解可以表示爲 Ax=0{\bf{Ax=0}} 基礎解系的線性組合加上 Ax=b{\bf{Ax=b}} 的任意一個解。

       在仿射變換矩陣的優化過程中,只要有三個點不共線,那麼式(7)中左側的矩陣都是列滿秩的,那麼從直覺上判斷式(10)中的矩陣 A{\bf{A}} 是否也一定是列滿秩的?答案是否定的。很明顯,如果式(10)中的矩陣 A{\bf{A}} 是列滿秩的,那麼該方程組只有唯一解,而 0{\bf{0}} 是任意線性齊次方程組的解,也就是該方程組只有零解。然而,如果我們所用的匹配點對確實是經過某個單應性矩陣變換而得到的,爲什麼從匹配點對反推回矩陣參數就只能得到零解了呢?原因在於此時矩陣 A{\bf{A}} 的秩一般最多隻有 8,而不是列滿秩時的 9。因爲這時的秩 r=8r=8,那麼基礎解系中的最大無關向量個數爲 nr=1n-r=1,剛好就對應了實際所用到的單應性矩陣參數。而之所以 A{\bf{A}} 的秩爲 8,是因爲其在矩陣中引入了 x2x_2y2y_2,這就是透視變換與仿射變換的不同之處,這兩個值是與 x1x_1y1y_1 相關的,也就是根據式(4)可得,
h11x1+h12y1+h13h31x1x2h32y1x2h33x2=0,h21x1+h22y1+h23h31x1y2h32y1y2h33y2=0.(11)\begin{array}{l} {{h'}_{11}}{x_1} + {{h'}_{12}}{y_1} + {{h'}_{13}} - {{h'}_{31}}{x_1}{x_2} - {{h'}_{32}}{y_1}{x_2} - {{h'}_{33}}{x_2} = 0,\\ {{h'}_{21}}{x_1} + {{h'}_{22}}{y_1} + {{h'}_{23}} - {{h'}_{31}}{x_1}{y_2} - {{h'}_{32}}{y_1}{y_2} - {{h'}_{33}}{y_2} = 0. \end{array}\tag{11} 其中 h{\bf{h'}} 代表實際的單應性矩陣參數,不是式(10)中的未知數。那就意味着,A{\bf{A}} 中的任意一個非零參數對應的列向量都可以表示成其他列向量的線性組合,那自然地其秩只有 8 了。極端情況下如果 h{\bf{h'}} 前兩行 6 個參數都爲零,那麼 x2x_2y2y_2 都爲零,這時矩陣 A{\bf{A}} 的後三列都爲零,其秩就只有 6 了,不過這種情況一般不考慮。因爲通解中只有一個解向量,而其長度並不影響最終的結果,那麼我們可以令某個非零的分量爲1,例如 h33{h_{33}} 通常是非零的,那麼對應的列就可以移到右側,如式(10)可表示爲
B=(a1a2a8),x=(h11h12h32)T,h33=1,Bx=h11a1+h12a2++h32a8=a9BTBx=BTa9.(12)\begin{array}{l} {\bf{B}} = \left( {\begin{array}{c} {{{\bf{a}}_1}}&{{{\bf{a}}_2}}& \cdots &{{{\bf{a}}_8}} \end{array}} \right),{\rm{ }}{\bf{x}} = {\left( {\begin{array}{c} {{h_{11}}}&{{h_{12}}}& \cdots &{{h_{32}}} \end{array}} \right)^T},{\rm{ }}{h_{33}} = 1,\\ {\bf{Bx}} = {h_{11}}{{\bf{a}}_1} + {h_{12}}{{\bf{a}}_2} + \cdots + {h_{32}}{{\bf{a}}_8} = - {{\bf{a}}_9} \Rightarrow {{\bf{B}}^T}{\bf{Bx}} = - {{\bf{B}}^T}{{\bf{a}}_9}. \end{array}\tag{12} 這時 B{\bf{B}} 是列滿秩的,且有 R(B,a9)=R(B)=8{R({\bf{B, a}}_9)=R({\bf{B}})=8},因此有唯一解,且對應於 h{\bf{h'}}。因爲這時只有 8 個未知數,我們只需要 4 個三點互不共線的匹配點對即可求出相應的單應性矩陣參數,這種方法常用於將某幅圖像投影到另外一幅圖像上,因爲這時我們只需要知道圖像四個角對應的位置即可。

       不過要注意的是,我們不能選取那些本來爲 0 的分量讓其等於 1,雖然一般來說 h33h_{33} 是非零的,但單應性矩陣本身是三維空間中的自由線性變換,除了不讓其第三行全爲 0 也就是讓式(4)可除以外,確實也存在 h33{h_{33}} 可能爲零的的情況。這時,如果 h33=0{h_{33}=0},那麼式(11)表示矩陣 A{\bf{A}} 的前 8 列是線性相關的,也就是 R(B)=7R({\bf{B}})=7,而 R(B,a9)=8R(B){R({\bf{B, a}}_9)=8≠R({\bf{B}})},因此這時式(12)無解。這本身也是因爲 0 是不可能通過縮放得到 1 的。然而,由於我們事前是不能知道參數的哪個分量是非零的,我們想要避免將本身爲零的分量設爲 1,最好的方法還是通過初等行變換將矩陣 A{\bf{A}} 轉換爲行最簡形矩陣,這樣哪些分量爲 0 就十分清楚了。另外,我們也可以通過限制解向量的長度來避免將本來爲 0 的分量設爲 1,例如 h=1{||{\bf{h}}||=1}。但這時由於多了一個二次型約束條件,我們應該怎麼去求解?

       前面我們雖然討論了很多,實際上都是基於所有匹配點都對應同一個單應性矩陣這一假設的,但在優化問題中,我們通常只能最小化匹配誤差,而不能讓其爲零。也就是說,矩陣 A{\bf{A}} 通常是列滿秩的,那麼這時候我們應該怎麼找到一個非零解使得匹配誤差最小?對於線性齊次方程組,我們不能直接使用最小二乘法,因爲如果要最小化 Ah||{\bf{Ah}}||,那很明顯只有 h=0\bf{h=0} 這個極值點了,不符合我們要優化的問題。類似於前面的分析,如果 Ah0{\bf{Ah}} \approx {\bf{0}},那麼對於較小的 kk 值,也有 Akh0{\bf{A}} \cdot k{\bf{h}} \approx {\bf{0}}。因此,解決這個問題的一種簡單的方法是令某個未知數爲常量,如 h33=1h_{33}=1,那麼其在 A{\bf{A}} 中對應的那一列就可以移到方程組的右側,這樣就得到了一個非齊次的的線性方程組,接下來就可以使用最小二乘法來求解最優解了。但是,這種方法的侷限同樣在於,如果實際非零最優解(後面有介紹)中 h33h_{33} 的相比於其他分量的量級很小或者等於 0,那麼 kk 值就很大或者無解,這樣造成的誤差就不一定能忽視了。因此,我們需要從其他的角度來分析。

       我們知道,函數從不同方向接近極值點時其下降速度並不一定相等。以簡單的二次型(只包含 xixjx_ix_j 項的二次函數)
f(x)=f(x,y)=ax2+by2(13)f({\bf{x}}) = f(x,y) = a{x^2} + b{y^2}\tag{13} 爲例,假設a>b>0a > b > 0,那麼其等高線爲一系列的同心橢圓,並且原點爲唯一的極小值點,如圖 4 所示。

圖4 橢圓等高線

如果我們使用一系列垂直於 XOYXOY 平面的二維平面 y=kx{y=kx} 以及 x=0x = 0 去切開該函數,那麼在剖面處我們可以得到一系列的二次曲線。令 z=xsign(y)z=||{\bf{x}}||·sign(y),也就是該點在該方向上離原點的距離,那麼這些曲線可以表示爲 f(z)=cz2f(z)=cz^2,其中 bcab \le c \le a,如圖 5 所示。也就是說,雖然該二次型 f(x){f(x)} 只有原點這麼一個極小值點,但是從不同方向趨近原點時,它們的下降速度是不一樣的。而且從圖 5 可以發現,無論我們選擇哪個方向,其下降速度都會處在 aabb 之間,而這兩者剛好是組成二次型的兩個值,如果從二次型矩陣的角度來講,這剛好就是該矩陣的兩個特徵值。這是否存在着某些巧合?

圖5 剖面二次曲線

       回到我們前面所討論的參數優化問題,我們需要最小化 Ah||{\bf{Ah}}||,其實也就對應着最小化二次型
f(h)=hTATAh.(14)f({\bf{h}}) = {{\bf{h}}^T}{{\bf{A}}^T}{\bf{Ah}}.\tag{14} 雖然其比式(13)的二次型複雜很多,如果我們採用類似的方法使用一些超平面去切割該函數,得到的剖面曲線跟圖 5 也是差不多的。這是因爲 f(kh)=k2f(h){f(k{\bf{h}})=k^2f({\bf{h}})},所以這些曲線其實都是關於縮放係數 k{k} 的只含二次項的一元二次函數,而因爲 A\bf{A} 並不一定列滿秩,ATA{\bf{A}}^T{\bf{A}} 可能是正定或者半正定的,也就是 f(kh)0{f(k{\bf{h}}) \ge 0},那麼得到的剖面曲線要麼是開口向上的二次曲線,要麼爲 0。前面已經提到,如果 Ah0{\bf{Ah}} \approx {\bf{0}},那麼對於較小的 k{k} 值,也有 Akh0{\bf{A}} \cdot k{\bf{h}} \approx {\bf{0}}。從優化的角度來講,在保持較低誤差的情況下,k{k} 的取值範圍應該越大越好,也就是 kk 對優化誤差的影響並不明顯。那麼,結合圖 5,我們應該選擇那些最平緩的曲線,那麼即便 kk 值變得很大,實際的誤差也不會特別的明顯。而對於那些特別陡峭的曲線,我們稍微增大 kk 值,也有可能導致誤差超出我們的預期。因此,式(14)的問題就可以轉化爲求解可以得到最平緩的剖面曲線的平面方向了。爲了描述這些曲線的平緩程度,我們可以比較在各個方向上離原點同樣距離(如 h=1||{\bf{h}}||=1)時函數值的大小,如果該值爲 0,那就意味着 ATA{\bf{A}}^T{\bf{A}} 是半正定的,對應着有一個精確解使得匹配誤差爲 0。這樣,我們實際上就可以得到一個等式約束的最優化問題,即
minimizef(h)=hTATAhsubjecttohTh=1(15)\begin{array}{l} {\rm{minimize }}f({\bf{h}}) = {{\bf{h}}^T}{{\bf{A}}^T}{\bf{Ah}}\\ {\rm{subject to }}{{\bf{h}}^T}{\bf{h}} = 1 \end{array}\tag{15} 對於等式約束的最優化問題,引入拉格朗日算子 λ\lambda,構造拉格朗日函數得
l(h,λ)=hTATAh+λ(1hTh).(16)l\left( {{\bf{h}},\lambda } \right) = {{\bf{h}}^T}{{\bf{A}}^T}{\bf{Ah}} + \lambda \left( {1 - {{\bf{h}}^T}{\bf{h}}} \right).\tag{16} 根據拉格朗日條件,如果 h\bf{h^*}f(h)f(\bf{h}) 在約束條件下的一個極小值點,存在 λ\lambda^*,使得
l(h,λ)=(Dhl(h,λ)TDλl(h,λ)T)=(2ATAh2λh1hTh)=0.(17)\nabla l\left( {{{\bf{h}}^*},{\lambda ^*}} \right) = \left( {\begin{array}{c} {{D_{\bf{h}}}l{{\left( {{{\bf{h}}^*},{\lambda ^*}} \right)}^T}}\\ {{D_\lambda }l{{\left( {{{\bf{h}}^*},{\lambda ^*}} \right)}^T}} \end{array}} \right) = \left( {\begin{array}{c} {2{{\bf{A}}^T}{\bf{A}}{{\bf{h}}^*} - 2{\lambda ^*}{{\bf{h}}^*}}\\ {1 - {{\bf{h}}^{*T}}{{\bf{h}}^*}} \end{array}} \right) = {\bf{0}}.\tag{17} 於是有
ATAh=λh,hTh=1.(18)\begin{array}{c} {{\bf{A}}^T}{\bf{A}}{{\bf{h}}^*} = {\lambda ^*}{{\bf{h}}^*},\\ {{\bf{h}}^{*T}}{{\bf{h}}^*} = 1. \end{array}\tag{18} f(h)=hTATAh=λhTh=λ.(19)f({{\bf{h}}^*}) = {{\bf{h}}^{*T}}{{\bf{A}}^T}{\bf{A}}{{\bf{h}}^*} = {\lambda ^*}{{\bf{h}}^{*T}}{{\bf{h}}^*} = {\lambda ^*}.\tag{19}也就是說,如果 h\bf{h^*}f(h)f(\bf{h}) 在約束條件下的極小值點,那麼其必定爲式(18)中 ATA{\bf{A}}^T{\bf{A}} 的特徵向量,同時 f(h)f(\bf{h^*}) 剛好爲對應的特徵值。這就是爲什麼前面我們提到的圖 5 裏面爲什麼下降最快和最慢的剛好就是兩個特徵值對應的曲線。

       其實,拉格朗日條件只是一個一階必要條件,也就是說滿足式(18)的特徵向量 h\bf{h^*} 還不一定是一個極值點。因爲 ATA{\bf{A}}^T{\bf{A}} 是對稱正定或半正定矩陣,其特徵值必定非負,且不同特徵值對應的特徵向量相互正交,而重根特徵值的特徵向量也可進行正交化得到相同數量的正交特徵向量,所以這些特徵向量可以組成一個標準正交基。那麼,符合約束條件的任意向量可表示爲
y=ibihi,ibi2=1.(20){\bf{y}} = \sum\limits_i {{b_i}{{\bf{h}}_i}} ,{\rm{ }}\sum\limits_i {b_i^2} = 1.\tag{20} 可得
yTATAy=ibihiTiλibihi=iλibi2.(21){{\bf{y}}^T}{{\bf{A}}^T}{\bf{Ay}} = \sum\limits_i {{b_i}{\bf{h}}_i^T} \cdot \sum\limits_i {{\lambda _i}{b_i}{{\bf{h}}_i}} = \sum\limits_i {{\lambda _i}b_i^2} .\tag{21} 如果將特徵值從小到大排列,那麼有
h1TATAh1=λ1yTATAyλ9=h9TATAh9.(22){\bf{h}}_1^T{{\bf{A}}^T}{\bf{A}}{{\bf{h}}_1} = {\lambda _1} \le {{\bf{y}}^T}{{\bf{A}}^T}{\bf{Ay}} \le {\lambda _9} = {\bf{h}}_9^T{{\bf{A}}^T}{\bf{A}}{{\bf{h}}_9}.\tag{22} 因此,最小特徵值對應的特徵向量一定可以保證目標函數值最小,最大特徵值對應的特徵向量一定可以保證目標函數值最大。如果最小或者最大特徵值是唯一的,沒有重根,那麼就可以保證對應的特徵向量是全局極小值點或者全局極大值點。例如當 A\bf{A} 的秩只有 8,也就是可以得到精確解時,剛好有一個 0 特徵值,且僅對應唯一特徵向量,這時可以保證其是全局的最優解,也就是精確解。

       綜上所述,透視變換的參數優化問題可以轉化爲求解式(18)的特徵向量的問題,而且最優的參數向量爲最小特徵值對應的特徵向量。由於特徵向量在某些分量上的值可能很小或者爲零,那麼在最初提到的直接讓 h33h_{33} 爲非零常量的方法中,其首先就排除了那些 h330h_{33} \approx 0 的特徵向量方向,並且不同方向上的 h||\bf{h}|| 不再一致,這樣很難比較不同方向的真正平緩程度,求出來的最優解也不一定是使用式(18)求出來的特徵向量解。所以,令 h33h_{33} 爲常量這種方法雖然比較簡單,但具有比較大的侷限性。另外,因爲這兩種方法都引入了一個約束條件,所以透視變換雖然有 9 個參數,但實際只有 8 自由度。

5 透視變換例子

       例如我們有以下一張圖片,由於手機攝像頭的焦距比較短,所以有明顯的透視效應。我們想要更換屏幕上的壁紙,相當於將一張四平八方的圖片通過透視變換投影到屏幕包圍的區域。前面已經提到,要求解相應的透視變換參數,最少只需要 4 個匹配點對即可。所以,我們只需找到屏幕四個角對應的座標,然後加上我們想要更換的圖片的四個角的座標,構造矩陣 ATA{\bf{A}}^T{\bf{A}},求解特徵值和特徵向量,取最小特徵值對應的特徵向量作爲變換的參數即可。以下是相應的程序腳本以及變換後的效果。



# -*- coding: utf-8 -*-
import numpy as np
from PIL import Image

def solve_homo(ptsrc, ptdst):
    npts = ptsrc.shape[0]
    A = np.zeros([2*npts, 9])
    
    A[:npts, [0, 1]] = np.copy(ptsrc)
    A[:npts, 2] = np.ones(npts)
    
    A[npts:, [3, 4]] = np.copy(ptsrc)
    A[npts:, 5] = np.ones(npts)
    
    A[:npts, 6] = -ptsrc[:, 0] * ptdst[:, 0]
    A[npts:, 6] = -ptsrc[:, 0] * ptdst[:, 1]
    A[:npts, 7] = -ptsrc[:, 1] * ptdst[:, 0]
    A[npts:, 7] = -ptsrc[:, 1] * ptdst[:, 1]
    
    A[:npts, 8] = -ptdst[:, 0]
    A[npts:, 8] = -ptdst[:, 1]
    
    A = A.T.dot(A)
    vals, vecs = np.linalg.eig(A)
    homo_mat = vecs[:, np.argmin(vals)]
    
    if np.max(np.abs(homo_mat)) < 1e-7:
        homo_mat[-1] = 1
    
    if np.abs(homo_mat[-1]) > 1e-7:
        homo_mat /= homo_mat[-1]
    
    return homo_mat

if __name__ == '__main__':
    
    src = Image.open('src.jpg').convert('RGBA') # 希望更換的壁紙
    dst = Image.open('dst.jpg').convert('RGBA') # 目標圖
    w, h = src.size
    W, H = dst.size
    
    # 給出對應座標對, 源座標和目標座標均爲 Nx2 的數組
    ptsrc = np.array([[0,     0], [w-1,    0], [0,    h-1], [w-1,   h-1]])
    ptdst = np.array([[610, 210], [3322, 388], [378, 1594], [2988, 2268]])
    
    # 映射到目標圖實際是爲目標圖上的每個像素找出對應的原圖座標然後插值,
    # 所以這裏實際上應該是求目標圖到原始圖的變換矩陣
    homo_mat = solve_homo(ptdst, ptsrc)
    
    # 利用PIL變換到目標圖,注意其默認h33=1,所以只輸入前8個參數,並且需要進行縮放
    img = src.transform((W, H), Image.PERSPECTIVE, homo_mat[:-1], Image.BICUBIC)
    
    # 利用alpha通道將兩張目標圖粘貼到一起即可得到最終的圖片
    dst.paste(img, img.split()[-1])
    dst = dst.convert('RGB')
    dst.save('new_wallpaper.jpg')

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章