Unity3D
Unity
数学
線形代数
Homograpy

QuadWarp用の射影変換(ホモグラフィ)行列の導出

More than 1 year has passed since last update.

前置き

プロジェクションマッピングなどで矩形を歪めて変形するときに、
三角形ポリゴンで行うとuv値の補間が各三角形の中だけで行われるのでテクスチャマッピングが意図した通りになりません


これが

!
こうなるけど

!
ほんとはこうなって欲しい

こんなときに意図通りマッピングするuv値を求めるHomography行列を求めます
答えだけ書いてるところは見たけど導出は見なかったので書いてみます

導出

$(0,0)(0,1)(1,1)(1,0)$の正方形を任意形状の四角形$(x_0',y_0')(x_1',y_1')(x_2',y_2')(x_3',y_3')$にする変換行列を求める

\begin{pmatrix}
x_i'' \\
y_i'' \\
z_i'' \\
\end{pmatrix}
=
\begin{pmatrix}
a & b & c \\
d & e & f \\
g & h & 1
\end{pmatrix}
\begin{pmatrix}
x_i \\
y_i \\
1 \\
\end{pmatrix}
\\
x_i'=\frac{x_i''}{z_i''},\quad y_i'=\frac{y_i''}{z_i''}

方程式の形に直すと

x_i' = \frac{ax_i + by_i + c}{gx_i + hy_i + 1}\quad
y_i' = \frac{dx_i + ey_i + f}{gx_i + hy_i + 1}

$(x_0,y_0)=(0,0), (x_1,y_1)=(0,1), (x_2,y_2)=(1,1), (x_3,y_3)=(1,0)$として、
$(x_0',y_0')~(x_3',y_3')$を入力したときそれを満たす$a~h$が求まればよい
言い換えると$a~h$を$(x_0',y_0')~(x_3',y_3')$で表せられればよい
未知変数$a~h$は8つ、立てられる連立方程式は$(x_0',y_0')~(x_3',y_3')$で8つ、求まりそう

$iが0~3$のときそれぞれ考えると、

$i=0$のとき
$(x_0,y_0)=(0,0)$なので、

x_0' = \frac{a*0 + b*0 + c}{g*0 + h*0 + 1}\quad
y_0' = \frac{d*0 + e * 0 + f}{g*0 + h*0 + 1}\\
c = x_0' \quad
f = y_0' \qquad\cdots①

$i=1$のとき
$(x_1,y_1)=(0,1)$なので

x_1' = \frac{a*0 + b*1 + c}{g*0 + h*1 + 1}\quad
y_1' = \frac{d*0 + e*1 + f}{g*0 + h*1 + 1}\\
x_1' = \frac{b + c}{h + 1}\quad
y_1' = \frac{e + f}{h + 1}\qquad\cdots②

以降同様に、
$i=2$のとき

x_2' = \frac{a + b + c}{g + h + 1}\quad
y_2' = \frac{d + e + f}{g + h + 1}\qquad\cdots③

$i=3$のとき

x_3' = \frac{a + c}{g + 1}\quad
y_3' = \frac{d + f}{g + 1}\qquad\cdots④

①~④で連立方程式8つが出た(いきなり$c,f$もわかった!)ので、あとはゴリゴリ解く

①②より、
b=x_1'h-x_0'+x_1'\quad
e=y_1'h-y_0'+y_1'\qquad\cdots⑤\\
③より
a+b-x_2'g-x_2'h+x_0'-x_2'=0\quad
d+e-y_2'g-y_2'h+y_0'-y_2'=0\qquad\cdots⑥\\
①④より
a=x_3'g-x_0'+x_3'\quad
d=y_3'g-y_0'+y_3\qquad\cdots⑦\\

⑤、⑦を⑥に代入して、$g,h$の式にすると

(x_3'g-x_0'+x_3')+(x_1'h-x_0'+x_1')-x_2'g-x_2'h+x_0'-x_2'=0\\
(y_3'g-y_0'+y_3)+(y_1'h-y_0'+y_1')-y_2'g-y_2'h+y_0'-y_2'=0\\

整理して

(x_1'-x_2')h+(x_3'-x_2')g-x_0'+x_1'-x_2'+x_3'=0\qquad(y_1'-y_2')h+(y_3'-y_2')g-y_0'+y_1'-y_2'+y_3'=0\qquad\cdots⑧

ここでおもむろに以下のように定義して

sx = x_0'-x_1'+x_2'-x_3'\\
sy = y_0'-y_1'+y_2'-y_3'\\
dx1 = x_1'-x_2'\\
dx2 = x_3'-x_2'\\
dy1 = y_1'-y_2'\\
dy2 = y_3'-y_2'

⑧に代入すると

dx1*h+dx2*g-sx=0\qquad dy1*h+dy2*g-sy=0

$g,h$について連立方程式を解いて、

g=\frac{sx*dy1-sy*dx1}{dy1*dx2-dx1*dy2}\quad
h=\frac{sy*dx2-sx*dy2}{dy1*dx2-dx1*dy2}\qquad\cdots⑨

$g,h$が求まったので、⑤,⑦に代入すれば、$a,b,d,e$が求まり、$a~h$のすべてが求まる

めでたしめでたし

実装

上記の変換行列は$(0,0)(0,1)(1,1)(1,0)$を任意の位置に移動させる行列ですが、
uv値はこの逆で、任意の位置を$(0,0)(0,1)(1,1)(1,0)$へ変換すればいいので逆行列をポジションに乗算する実装になります

ソースコード(Unity)

https://github.com/fuqunaga/QuadWarp

参考

http://yaju3d.hatenablog.jp/entry/2013/08/04/152524

連立方程式を行列の式の形にして逆行列で求めるやり方
http://mf-atelier.sakura.ne.jp/mf-atelier/modules/tips/index.php/program/algorithm/a6.html

OpenCVは4点に限らない高度なのやってるっぽい
http://opencv.jp/opencv-2svn/cpp/camera_calibration_and_3d_reconstruction.html#cv-findhomography