失敗は一時の恥

パッケージソフト開発をしているプログラマが気の赴くままに何かを投稿するブログ.

アフィン変換の仕組み

アフィン変換の仕組み

この記事では行列の計算とアフィン変換の仕組みについて簡単に解説します.

この記事について

最近,仕事でアフィン変換を扱うことがありました. アフィン変換で画像のようなデータを回転するといった内容です.

仕事をしながら昔に習ったベクトルと行列を話を思い出したので, 座標の回転やアフィン変換の仕組みについて書いてみようと思います.

この記事では,アフィン変換の回転と平行移動についてのみ触れます.拡大やせん断については触れません.

まずは,行列の基本的な計算の定義を思い出し,回転行列がなぜ回転を表しているのか,点の平行移動はどうするのかといった話をしつつ, アフィン変換はどういう仕組みでデータを変換するのか説明をしていきます.

行列の計算のおさらい

行列 {A} とベルトル {\vec{v}} (平面上の点 {(x, y)}) があったとします.

\[ A = \begin{pmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{pmatrix}, \vec{v} = \begin{pmatrix} x \\ y \end{pmatrix} \]

行列 {A} とベクトル {\vec{v}} の積は,次のように計算します.

\[ A \vec{v} = \begin{pmatrix} a_{11}x + a_{12}y \\ a_{21}x + a_{22}y \end{pmatrix} \]

行列 {A} は, 平面上の点 {(x, y)} を 点 {(a_{11}x + a_{12}y,\ a_{21}x + a_{22}y)} に移動させる操作とみなすことができます.

回転行列

行列によって回転を表すことができます.点 {(x, y)} を原点中心に角 {\theta} だけ回転させたいといった場合は,次のような行列を使用します.

\[ \begin{pmatrix} \cos\theta & - \sin\theta \\ \sin\theta & \cos\theta \end{pmatrix} \]

{(x,y)} を原点中心に角 {\theta} 回転させると,

\[ \begin{pmatrix} x \cos\theta - y \sin\theta\\ x \sin\theta + y \cos\theta \end{pmatrix} \]

という点になります.

この回転行列自体はよく知られていて,今回の話のテーマであるアフィン変換でも利用するものです.

回転行列はなぜ回転を表すのか?

回転行列は実際に角度 {\theta} の回転を表すのですが,この {\sin}{\cos} 関数を含む行列の積は,どういう理由で回転になるのでしょう?

いま,原点からの距離が 1 で,x 軸の正の向きとなす角が {\theta} である点 {p} があるとします. 三角関数を使うと,{p} の座標は次のように表すことができます:

\[ p = \begin{pmatrix}
 \cos\theta \\ \sin\theta \end{pmatrix} \]

この点を,原点中心に角 {\varphi} だけ回転した点を {q} とすると,

\[ q = \begin{pmatrix} \cos(\theta + \varphi) \\ \sin(\theta + \varphi) \end{pmatrix} \]

となります.

f:id:snobutaka:20171230161024p:plain

回転図

ここで,三角関数の加法定理を利用すると,{q} の座標は次のように表すことができます:

\[ q = \begin{pmatrix} \cos\theta\cos\varphi - \sin\theta\sin\varphi \\ \sin\theta\cos\varphi + \cos\theta\sin\varphi \end{pmatrix} \]

加法定理自体の詳しい解説はここでは行いませんが,このように式を展開できるということだけ利用させてもらいます.

これを踏まえて,実際に回転行列と点 {p} の積を計算してみましょう:

\[ \begin{align} \begin{pmatrix} \cos\varphi & -\sin\varphi \\ \sin\varphi & \cos\varphi \end{pmatrix} \begin{pmatrix} \cos\theta \\ \sin\theta \end{pmatrix} &= \begin{pmatrix} \cos\varphi\cos\theta - \sin\varphi\sin\theta \\ \sin\varphi\cos\theta + \cos\varphi\sin\theta \end{pmatrix} \\ &= \begin{pmatrix} \cos\theta\cos\varphi - \sin\theta\sin\varphi \\ \sin\theta\cos\varphi + \cos\theta\sin\varphi \end{pmatrix} \\ &= q \end{align} \]

定義通りに行列の積を計算し,要素の和と積の順番を整理すると,計算結果は {q} そのものであることが分かります.

これで,回転行列が実際に回転を表しているということが確かめられました.

平行移動

{(x, y)}

  • x 軸の正の向きに {\Delta x}
  • y 軸の正の向きに {\Delta y}

だけ平行移動するには,ベクトル {\vec{\Delta v} = \begin{pmatrix} \Delta x \\ \Delta y \end{pmatrix}} との和を取ることになります.

\[ \vec{v} + \vec{\Delta v} = \begin{pmatrix} x + \Delta x \\ y + \Delta y \end{pmatrix} \]

平行移動を単純に行列で表現することはできない

先ほど,原点中心に角 {\theta} の回転を行うには,回転行列を使用すればいいという話をしました. それでは,平行移動も行列で表現できるのでしょうか?

この問いに対しては,「そのままではできない」というのが答えになります.

できない例を挙げると,例えば原点 {(0 ,0)} を行列を使って平行移動することはできません. 原点と任意の行列の積を計算すると,必ず原点自身になってしまうからです.

\[ \begin{pmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{pmatrix} \begin{pmatrix} 0 \\ 0 \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \end{pmatrix} \]

この事実は,行列の線型性という性質が関係しているのですが,ここでは深くは立ち入りません. 興味がある人は,線形代数の教科書・参考書などを読んでみると詳しい話がわかると思います.

一方で,アフィン変換は行列を使いますが平行移動を実現しています. アフィン変換では,ちょっとした工夫をすることで行列で平行移動を計算できるようになっているのです.

アフィン変換

前置きが長くなりましたが,ここからアフィン変換の仕組みについて説明して行きます.

3 × 3 行列と 3 次元ベクトル

アフィン変換は 2 次元平面上の点を対象とした操作ですが,計算自体は 3 次元で行います.

ここで,3 × 3 行列と 3 次元ベクトルの積の計算を確認しておきましょう:

\[ \begin{pmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{pmatrix} \begin{pmatrix} x \\ y \\ z \end{pmatrix} = \begin{pmatrix} a_{11}x + a_{12}y + a_{13}z \\ a_{21}x + a_{22}y + a_{23}z \\ z_{31}x + a_{32}y + a_{33}z \end{pmatrix} \]

ここで,{z = 0} の場合を考え,{x, y} 要素のみに着目すると,積の結果は最初に扱っていた 2 次元の場合と一致します:

\[ \begin{pmatrix} a_{11}x + a_{12}y \\ a_{21}x + a_{22}y \end{pmatrix} \]

アフィン変換の計算は 3 次元で行うといいましたが, 実際の変換対象の値としては {x, y} 成分のみに着目することになります.

回転と平行移動のアフィン変換

それでは,回転と平行移動を行うアフィン変換はどんなものか見てみましょう. それは,次のような行列で表されます:

\[ \begin{pmatrix} \cos\theta & -\sin\theta & \Delta x \\ \sin\theta & \cos\theta & \Delta y \\ 0 & 0 & 1 \end{pmatrix} \]

左上の 2 次元相当部分が回転行列,右上の部分が平行移動の量,他の部分は計算の帳尻合わせのために 0 や 1 として定義します.

アフィン変換で点 {(x, y)} を扱うとき,実際に計算に使うのは次のようなベクトルです:

\[ \begin{pmatrix} x \\ y \\ 1 \end{pmatrix} \]

実際に積を計算してみましょう:

\[ \begin{align} \begin{pmatrix} \cos\theta & -\sin\theta & \Delta x \\ \sin\theta & \cos\theta & \Delta y \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} x \\ y \\ 1 \end{pmatrix} &= \begin{pmatrix} x\cos\theta - y\sin\theta + \Delta x \\ x\sin\theta + y\cos\theta + \Delta y \\ 1 \end{pmatrix} \\ &= \begin{pmatrix} x\cos\theta - y\sin\theta \\ x\sin\theta + y\cos\theta \\ 1 \end{pmatrix} + \begin{pmatrix} \Delta x \\ \Delta y \\ 0 \end{pmatrix} \end{align} \]

計算の結果ですが,{x, y} 成分に着目すると,前述の回転行列の結果に平行移動を加えた値になっています.

回転は,{x, y} 成分に対して回転行列がそのまま適用された格好になっています.

平行移動については,{z} 成分をわざと 1 にしていることによって,平行移動させたい量だけ値が加算されるようになっています. 前の記述で,行列で平行移動を行うことは「そのままではできない」としていましたが,あえて {z} 座標を計算に利用するという工夫によって問題を解決しています.

また,{z} 成分は再び 1 となっています.これは帳尻合わせということで 0 や 1 といった値を当てはめていた部分がうまく働いた結果です. アフィン変換の結果 {z} 座標が 1 になるということは,計算結果に対して繰り返しアフィン変換を適用していくことができるということです.

演習問題

具体的な例として,簡単で分かりやすい例を計算してみましょう. 中途半端な角度を使うと,計算結果が正しいのかよくわからなくなるので,90 度か 45 度の倍数の角と使うと分かりやすいでしょう.

例えば

  • {(1, 0)} を 90 度回転させたら {(0, 1)} になるはずです.そのような変換を行うアフィン変換の行列を書いてみて,実際に計算してみてください.
  • {(1, 0)} を 90 度回転させ,さらに x 軸の正の向きに 1,y 軸の正の向きに 1 だけ平行移動をすると,{(1, 2)} になるはずです.そのような変換を行うアフィン変換の行列を書いてみて,実際に計算してみてください.

といった例を試すと,実際に点が移動するということが理解できると思います.

おわりに

今回は行列の計算の定義にしたがって,アフィン変換がどのように回転と平行移動を実現しているのかを見てみました.

アフィン変換には,今回紹介した回転と平行移動以外にも,拡大やせん断といった使い方があります. 数学的には,行列自体がそういう変換を行う性質を持っているという話であり,仕組みとしては今回説明した範囲と同様 (のはず) です.

行列の一般的な性質は,線形代数と呼ばれる分野でよく扱われる話です. こちらも興味がある方は詳しく調べて見るとよいでしょう.

今回扱わなかったこれらの話題と,実際にプログラムでアフィン変換をしてみるというのは,今後このブログで扱うこともあるかもしれません.

参考文献