概要
画像の座標を変える処理として、Q.25〜27の画像補間とQ.28〜31のアフィン変換をまとめます。 どちらも出力画像の座標から元画像の参照位置を求める「逆写像」が共通の土台です。
なぜ逆写像を使うのか
入力の各画素を変換先へ移す順写像では、丸めによって複数画素が同じ位置へ集まったり、 出力に未設定の穴ができたりします。出力の全画素を順に走査して入力座標を逆算すれば、 必ず各出力画素へ値を設定できます。
画像補間
逆算した入力座標は小数になるため、周囲の画素から値を決めます。
| 問題 | 参照する画素 | 特徴 |
|---|---|---|
| Q.25 最近傍補間 | 最も近い1画素 | 高速だが輪郭が階段状になりやすい |
| Q.26 Bi-linear補間 | 周囲4画素 | 距離に応じた線形補間で滑らか |
| Q.27 Bi-cubic補間 | 周囲16画素 | 3次関数を使い輪郭を比較的保ちやすい |
Bi-linear補間では、座標の小数部分を重みとして左右を補間し、その結果を上下方向に補間します。
const top = (1 - dx) * p00 + dx * p10;
const bottom = (1 - dx) * p01 + dx * p11;
const value = (1 - dy) * top + dy * bottom;
アフィン変換
アフィン変換は2×3行列で座標を変換します。
[ x' ] [ a b tx ] [ x ]
[ y' ] = [ c d ty ] [ y ]
[ 1 ]
| 問題 | 変換 |
|---|---|
| Q.28 | txとtyによる平行移動 |
| Q.29 | aとdによる拡大・縮小 |
| Q.30 | sinθとcosθによる回転 |
| Q.31 | bまたはcによるせん断 |
回転では、画像中心を原点へ移動してから回し、最後に中心座標を戻します。左上を原点のまま 回転すると、画像がCanvasの外へ大きく移動するためです。
行列の逆変換で入力座標を求めた後、最近傍法などの補間を適用します。つまりアフィン変換は 「座標を求める部分」、画像補間は「小数座標の色を決める部分」として組み合わせられます。
なぜ逆変換で座標を求めるのか
入力画素を順方向に移動すると、出力側に値が書かれない穴や、複数画素が重なる場所ができます。
そこで出力の各座標(x', y')から逆行列を使って入力座標(x, y)を求めます。
[x] [a b tx]⁻¹ [x']
[y] = [c d ty] [y']
[1] [0 0 1] [ 1]
2×2部分の行列式ad - bcが0に近い変換は逆行列を作れません。スケール0などの入力をUI側で
拒否するか、エラーとして扱う必要があります。
補間と境界処理の選択
最近傍法は高速ですが拡大時に階段状の輪郭が目立ちます。バイリニア補間は周囲4画素へ距離に応じた 重みを掛け、滑らかな結果を得ます。入力座標が画像外に出た場合は透明、背景色、端の複製のどれを 使うかを明示します。
画質を比較するときは同じ画像を何度も変換せず、常に原画像から目的のサイズへ一度で変換します。 縮小と拡大を繰り返すと補間誤差が蓄積し、アルゴリズム間の比較が難しくなります。
まとめ
拡大、回転、せん断は見た目が異なりますが、逆行列で入力座標を探し、周囲の画素から値を補間する という流れは共通です。座標変換と補間を別の処理として読むと実装を整理できます。