PixelShaderを使った画像補間 (1) 補間の基本式

ちょっと三連続でこのネタでいきたいと思います。政治的なネタを書きたい気持ちを抑えてきます。

一応三次補間(bi-cubic)とLanczosを使った補間をやってみるつもりです。正しいかどうかはおいておきますが・・・。

補間を行うピクセルの定義

補間を行うとき、補間計算の対象となるピクセルの情報を以下のように定義します。

描画先座標(x,y)での色を定義するとき、その描画元座標を(u,v)とする。このとき、u,vは小数を含む値であるとする。(整数値に切り捨てた値にしない)

またk = [u],l = [v] ([x]はxを超えない最大の整数)とおき、描画元のピクセルの色成分をP_{a,b}、描画先のピクセルの色成分をP’_{x,y}とおく。(a,b,x,yはそれぞれの座標内の値)

このとき、補間対象となる各ピクセルの行列¥mathbf{P}をN次正方行列とする(つまり、補間元で対象となるピクセルの範囲がNxNである)とき、描画元画素行列¥mathbf{P}

¥mathbf{P} = ¥begin{pmatrix} P_{k-(¥frac{N}{2}-1),l-(¥frac{N}{2}-1)} & ¥cdots & P_{k,l-(¥frac{N}{2}-1)} & ¥cdots & P_{k+¥frac{N}{2},l-(¥frac{N}{2}-1)} ¥¥ ¥vdots & ¥ddots & ¥vdots & & ¥vdots ¥¥ P_{k-(¥frac{N}{2}-1),l} & ¥cdots & P_{k,l} & ¥cdots & P_{k+¥frac{N}{2},l} ¥¥ ¥vdots & & ¥vdots & ¥ddots & ¥vdots ¥¥ P_{k-(¥frac{N}{2}-1),l+¥frac{N}{2}} & ¥cdots & P_{k,l+¥frac{N}{2}} & ¥cdots & P_{k+¥frac{N}{2},l+¥frac{N}{2}} ¥end{pmatrix}

とする。

たとえば、P'(x,y)の描画元のピクセルがP(5.6,7.8)だとして、4×4点を使って補間を行おうとすると、行列¥mathbf{P}

¥mathbf{P} = ¥begin{pmatrix} P_{4,6} & P_{5,6} & P_{6,6} & P_{7,6} ¥¥ P_{4,7} & P_{5,7} & P_{6,7} & P_{7,7} ¥¥ P_{4,8} & P_{5,8} & P_{6,8} & P_{7,8} ¥¥ P_{4,9} & P_{5,9} & P_{6,9} & P_{7,9} ¥end{pmatrix}

と定義するわけですね。対象の点を中心として正方形に切り取った画素の集合、といった感じです。

この定義だとNを偶数にしなければ、行列¥mathbf{P}が定義できないのですが、その問題はほとんどありません。それは次の定義をみればわかると思います。

重みを使ったたたみ込み補間式

補間式を重み係数を使って指定されている場合に有効な考え方です。

重み係数関数w(t)が決まっているときに使う方法です。このときは、以下のような計算を利用できます。

重み関数w(t)が定義されていて、t ¥ge M (= ¥frac{N}{2})となるtではw(t)=0となり、二次元空間上での重みw'(a,b)を

w’(a,b) = w(a)w(b)

とするとき、描画先の画素値P'(x,y)は、¥mathbf{P}をN(=2M)次正方行列として、

P’(x,y) = ¥begin{pmatrix} w(l-(M-1)-v), & ¥cdots, & w(l-v), & ¥cdots, & w(l+M-v) ¥end{pmatrix} ¥cdot ¥mathbf{P} ¥cdot ¥begin{pmatrix} w(k-(M-1)-u) ¥¥ ¥vdots ¥¥ w(k-u) ¥¥ ¥vdots ¥¥ w(k+M-u) ¥end{pmatrix}

となる。

重み係数の行列計算部がたたみ込みになっているわけですね。「Nが偶数でないと定義できない」は、そもそもN=2Mということで、Nは2以上の偶数となるので問題にならないわけですね。

一般化して定義すると訳がわからないですね。最低でも実例があった方がいいと思います。

線形補間を上記の式に当てはめてみる

線形補間の重み関数w(t)は

w(t) = ¥begin{cases} 1 - |t| & (|t| < 1) ¥¥ 0 & (|t| ¥ge 1) ¥end{cases}

であり、M = 1となるので、上記の式は

¥mathbf{P} = ¥begin{pmatrix} P_{k,l} & P_{k+1,l} ¥¥ P_{k,l+1} & P_{k+1,l+1} ¥end{pmatrix}

P’(x,y) = ¥begin{pmatrix} w(l-v), & w(l+1-v) ¥end{pmatrix} ¥cdot ¥mathbf{P} ¥cdot ¥begin{pmatrix} w(k-u) ¥¥ w(k+1-u) ¥end{pmatrix}

となります。仮にv = 4.3,u = 7.4としてこの行列を計算してみると

¥begin{align} P’(x,y) & = & ¥begin{pmatrix} 0.7, & 0.3 ¥end{pmatrix} ¥cdot ¥mathbf{P} ¥cdot ¥begin{pmatrix} 0.6 ¥¥ 0.4 ¥end{pmatrix} & ¥¥ & = & ¥begin{pmatrix} 0.7P_{4,7}+0.3P_{5,7}, & 0.7P_{4,8}+0.3P_{5,8} ¥end{pmatrix} ¥begin{pmatrix} 0.6 ¥¥ 0.4 ¥end{pmatrix} & ¥¥ & = & 0.6(0.7P_{4,7}+0.3P_{5,7}) + 0.4(0.7P_{4,8}+0.3P_{5,8}) & ¥¥ ¥end{align}

と、計算を追えばわかると思いますが、線形補間の計算そのものになります。

この描画元画素行列と重み関数の考え方をPixelShaderにそっくり持って行く

のが基本方針です。これだけで三次補間(bi-cubic)もLanczosを使った補間式も持って行くことができます。

計算式を出しているのは

数学的な意味を考えるためです。PixelShaderで書くには?だけを考えるのであればこんなものみる必要はほとんどありません。

が、後々自分で他の式で考えようとするとどうしてもこういうものが必要になってくるので・・・。


コメントを残す

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

この記事のトラックバック用URL