読者です 読者をやめる 読者になる 読者になる

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

アフィン変換とは何ぞやという話【CGAffineTrans】

Objective-C

こんにちは。エンジニアをしています、鷲見と申します。
今回はアフィン変換についてです。

あらまし

Objective-Cにはアフィン変換を行う機能があり、簡単にUIViewの幾何学的変換を行うことができます。
例えばUIViewを回転したい場合は、以下のように書くことができます。

// ビューを30度回転  
CGFloat angle = 30.0 * M_PI / 180.0;  
view.transform = CGAffineTransformMakeRotation(angle);  

この他にもいろいろな機能があるのですが、その中にCGAffineTransformMakeという
アフィン行列を指定してアフィン変換を行う機能があります。
さて、このアフィン行列とは何なのでしょうか?そもそもアフィン変換とは一体何者なのでしょうか?
というわけで本ブログではアフィン変換の理屈について解説してみようと思います。

幾何学的変換

まずは、アフィン変換のことは置いておいて、
単純にオブジェクトを平行移動、拡大・縮小、回転させることを考えます。
デジタルオブジェクトは画素の集合ですから、画素1つ1つに対して、
何らかの処理をかけてやれば、これらのことを実現することができそうです。
ここでは画素の位置を(x, y)で表します。

平行移動
平行移動は以下の計算で行うことが出来ます。
001

ここでtxはx方向への移動量、tyはy方向への移動量です。
全画素分上記の計算を繰り返せばオブジェクトの平行移動が可能です。

上記の計算式は行列を用いることで1つにまとめることができます。
002

拡大・縮小
拡大・縮小は以下の計算で行うことが出来ます※。
003

ここでsxはx方向の拡大率、syはy方向の拡大率です。

上記の計算式を行列を用いて1つにすると以下になります(拡大縮小行列)。
004

回転
回転は少し厄介です。
回転と相性のいい極座標で考えると、以下の計算式で行うことができます。
005
012

ここで、rは動径、φは偏角、θはφからの回転角度です。
xとyを使った計算式の方が扱いやすいので、直交座標に変形します。

加法定理より
006

rcosφ=x、rsinφ=yですから
007

y'についても同様に変形すると
008

上記の計算式を行列を用いて1つにすると以下になります(回転行列)。
009

アフィン変換

上記で表した計算式を使用すれば、オブジェクトを変換することができますが、
これらの計算式をひとつにして汎用的に使用できるようにすると便利です。
010

さらに以下のように、3次元の座標を導入すると上記を1つの行列の乗算にまとめることができます(アフィン行列)。
この行列を用いることで、任意の線形変換(拡大・縮小・回転など)と平行移動を組み合わせた変換ができるようになります。これがアフィン変換です。
011

CGAffineTransformMakeを使ってみる

上記を踏まえた上でCGAffineTransformMakeを使ってみます。

CGAffineTransformMakeのシンタックス

CGAffineTransform CGAffineTransformMake (  
   CGFloat a,  
   CGFloat b,  
   CGFloat c,  
   CGFloat d,  
   CGFloat tx,  
   CGFloat ty  
);  

平行移動

CGFloat tx = 50; // x軸方向に50px移動  
CGFloat ty = 100; // y軸方向に100px移動  
myView.transform = CGAffineTransformMake(1, 0,  
                                         0, 1,  
                                         tx, ty);  

拡大・縮小

CGFloat sx = 2.0f; // x軸方向に2倍  
CGFloat sy = 0.5f; // y軸方向に0.5倍  
myView.transform = CGAffineTransformMake(sx, 0,  
                                         0, sy,  
                                         0, 0);  

回転

CGFloat angle = 45.0 * M_PI / 180; // 45度回転(ラジアンに変換)  
myView.transform = CGAffineTransformMake(cos(angle), -sin(angle),  
                                         sin(angle), cos(angle),  
                                         0, 0);  

以上今回はアフィン変換について解説してみました。
OpenCVやvImageなど、面倒くさいことをしなくても、
この手の変換をしてくれるライブラリが充実してきたのは大変うれしいですね。

※ 座標を等倍しているだけなので、実際には再配列や内挿などの処理が必要です。