【Python/NumPy】座標からアフィン変換行列を求める方法

アフィン変換行列は、これまで移動量、スケール、回転角度からアフィン変換行列を求める方法を紹介してきました。

アフィン変換(平行移動、拡大縮小、回転、スキュー行列)

ただ、実際にはアフィン変換前の点とアフィン変換後の点の組み合わせからアフィン変換行列を求めたい場合もあるので、今回はその方法を紹介します。

アフィン変換行列は、こんな感じ↓です。

$$\left(\begin{array}{c}x^{’}\\ y^{‘}\\1\end{array}\right)=\left(\begin{array}{c}a & b & c\\ d & e & f\\0 & 0 & 1\end{array}\right)\left(\begin{array}{c}x\\ y \\ 1\end{array}\right)$$

アフィン変換行列を求めるという事は、この未知数の a, b, c, d, e, f を求める事になります。

この行列を求めるには、アフィン変換前の3点、アフィン変換後の3点の3ペアの座標があれば求める事ができます。

求め方は比較的簡単で、式で書くと以下のようになります。

$$\left(\begin{array}{c}x_0^{‘} & x_1^{‘} & x_2^{‘}\\ y_0^{‘} & y_1^{‘} & y_2^{‘}\\1 & 1 & 1\end{array}\right)
=
\left(\begin{array}{c}a & b & c\\ d & e & f\\0 & 0 & 1\end{array}\right)
\left(\begin{array}{c}x_{0} & x_{1} & x_{2}\\ y_{0} & y_{1} & y_{2} \\ 1 & 1 & 1 \end{array}\right)$$

より、逆行列を用いて、

$$\left(\begin{array}{c}x_0^{‘} & x_1^{‘} & x_2^{‘}\\ y_0^{‘} & y_1^{‘} & y_2^{‘}\\1 & 1 & 1\end{array}\right)
\left(\begin{array}{c}x_{0} & x_{1} & x_{2}\\ y_{0} & y_{1} & y_{2} \\ 1 & 1 & 1 \end{array}\right)^{-1}
=
\left(\begin{array}{c}a & b & c\\ d & e & f\\0 & 0 & 1\end{array}\right)
\left(\begin{array}{c}x_{0} & x_{1} & x_{2}\\ y_{0} & y_{1} & y_{2} \\ 1 & 1 & 1 \end{array}\right)
\left(\begin{array}{c}x_{0} & x_{1} & x_{2}\\ y_{0} & y_{1} & y_{2} \\ 1 & 1 & 1 \end{array}\right)^{-1}$$

$$\left(\begin{array}{c}a & b & c\\ d & e & f\\0 & 0 & 1\end{array}\right)
=
\left(\begin{array}{c}x_0^{‘} & x_1^{‘} & x_2^{‘}\\ y_0^{‘} & y_1^{‘} & y_2^{‘}\\1 & 1 & 1\end{array}\right)
\left(\begin{array}{c}x_{0} & x_{1} & x_{2}\\ y_{0} & y_{1} & y_{2} \\ 1 & 1 & 1 \end{array}\right)^{-1}$$

として求める事ができます。

この計算は行列の積と逆行列を求める事ができれば、解くことができますが、最近勉強しているPythonのNumPyを使って例題を解いてみたいと思います。

変換前 → 変換後
(0, 0) → (200, 100)
(600, 0) → (719.6152, 400)
(0, 400) → (0, 446.4102)

このアフィン変換行列は

$$\left(\begin{array}{c}a & b & c\\ d & e & f\\0 & 0 & 1\end{array}\right)
=
\left(\begin{array}{c}200 & 719.6152 & 0\\ 100 & 400 & 446.4102\\1 & 1 & 1\end{array}\right)
\left(\begin{array}{c}0 & 600 & 0\\ 0 & 0 & 400 \\ 1 & 1 & 1 \end{array}\right)^{-1}$$

で求まります。

この計算をNumPyを使って計算すると、

import numpy as np

src = np.array([[0, 600, 0],
               [0, 0, 400],
               [1, 1, 1]])

dst = np.array([[200, 719.6152, 0],
               [100, 400, 446.4102],
               [1, 1, 1]])

affine = np.matmul(dst, np.linalg.inv(src))

print(affine)

(実行結果)

[[  0.86602533  -0.5        200.        ]
 [  0.5          0.8660255  100.        ]
 [  0.           0.           1.        ]]

として、アフィン変換行列を求める事ができます。

ただし、この計算方法だと、3行1列目、3行2列目の要素が計算誤差できっちりと 0 にならない場合もあり、気持ち悪いので、もう一つの計算方法を紹介します。

アフィン変換の行列の式は

$$\left(\begin{array}{c}x^{’}\\ y^{‘}\\1\end{array}\right)=\left(\begin{array}{c}a & b & c\\ d & e & f\\0 & 0 & 1\end{array}\right)\left(\begin{array}{c}x\\ y \\ 1\end{array}\right)$$

でしたが、この式を展開すると

$$x^{‘}=ax + by + c$$

$$y^{‘}=dx + ey + f$$

この式にアフィン変換前の3点、アフィン変換後の3点の3ペアの座標を代入すると、

$$x_0^{‘}=ax_{0} + by_{0} + c$$

$$y_0^{‘}=dx_{0} + ey_{0} + f$$

$$x_1^{‘}=ax_{1} + by_{1} + c$$

$$y_1^{‘}=dx_{1} + ey_{1} + f$$

$$x_2^{‘}=ax_{2} + by_{2} + c$$

$$y_2^{‘}=dx_{2} + ey_{2} + f$$

となります。

式が6本で未知数が a ~f の6個なので、この連立方程式を求めれば、アフィン変換行列も求まります。

この連立方程式も行列を用いて解きたいと思います。

未知数の部分を6行1列の行列になるように、行列で表すと、

$$\left(\begin{array}{c}x_0^{‘}\\ y_0^{‘}\\x_1^{‘}\\ y_1^{‘}\\x_2^{‘}\\ y_2^{‘} \end{array}\right)
=
\left(\begin{array}{c}x_{0} & y_{0} & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & x_{0} & y_{0} & 1\\x_{1} & y_{1} & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & x_{1} & y_{1} & 1\\x_{2} & y_{2} & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & x_{2} & y_{2} & 1 \end{array}\right)
\left(\begin{array}{c}a\\ b\\c\\d\\e\\f\end{array}\right)$$

となり、6行6列の行列の部分に左側から逆行列を掛ければ、未知数が求まるので、

$$\left(\begin{array}{c}a\\ b\\c\\d\\e\\f\end{array}\right)=\left(\begin{array}{c}x_{0} & y_{0} & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & x_{0} & y_{0} & 1\\x_{1} & y_{1} & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & x_{1} & y_{1} & 1\\x_{2} & y_{2} & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & x_{2} & y_{2} & 1 \end{array}\right)^{-1}
\left(\begin{array}{c}x_0^{‘}\\ y_0^{‘}\\x_1^{‘}\\ y_1^{‘}\\x_2^{‘}\\ y_2^{‘} \end{array}\right)$$

を計算すれば、アフィン変換行列が求まります。

この計算もNumPyを使って計算してみたいと思います。

import numpy as np

mat = np.array([[0, 0, 1, 0, 0, 0],
                [0, 0, 0, 0, 0, 1],
                [600, 0, 1, 0, 0, 0],
                [0, 0, 0, 600, 0, 1],
                [0, 400, 1, 0, 0, 0],
                [0, 0, 0, 0, 400, 1]])

dst = np.array([200, 100, 719.6152, 400, 0, 446.4102]).T

ans = np.matmul(np.linalg.inv(mat), dst)

affine = np.array([[ans[0], ans[1], ans[2]],
                   [ans[3], ans[4], ans[5]],
                   [0, 0, 1]])

print(affine)

(実行結果)

[[  0.86602533  -0.5        200.        ]
 [  0.5          0.8660255  100.        ]
 [  0.           0.           1.        ]]

となり、アフィン変換行列が求まります。

参考

アフィン変換(平行移動、拡大縮小、回転、スキュー行列)

【Python/NumPy】行列の演算(積、逆行列、転置行列、擬似逆行列など)

【Python/NumPy】座標からホモグラフィ変換行列を求める方法

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください