NumPy

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

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

アフィン変換(平行移動、拡大縮小、回転、スキュー行列)
画像の拡大縮小、回転、平行移動などを行列を使って座標を変換する事をアフィン変換と呼びます。 X,Y座標の二次元デー...

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

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

$$\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を使って例題を解いてみたいと思います。

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.        ]]

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

参考

アフィン変換(平行移動、拡大縮小、回転、スキュー行列)
画像の拡大縮小、回転、平行移動などを行列を使って座標を変換する事をアフィン変換と呼びます。 X,Y座標の二次元デー...
【Python/NumPy】行列の演算(積、逆行列、転置行列、擬似逆行列など)
個人的には、行列は最小二乗法で近似式を求めるときや、アフィン変換を用いて画像の表示やリサイズを行う際に用いるのですが、こ...
【Python/NumPy】座標からホモグラフィ変換行列を求める方法
アフィン変換では長方形を平行四辺形には変換できるものの、台形には変換できないと説明しましたが、任意四角形から任意四角形へ...

コメント

タイトルとURLをコピーしました