バイリニア法,バイキュービック法のプログラム
画像処理の入門本を読んでたらこれらの処理をOpenCVに丸投げしていてブチ切れてしまったので自分で実装してみた.あってるかどうかがだいぶ怪しいので誤ってたら教えて下さい.
画像src
を拡大してrate
倍の画像dst
を作りたい.この時,画素の座標src=(x,y)がdst=(x*rate,y*rate)へ移動するので拡大によって生まれた隙間のpxを補間しなければならない.この補間方法をいくつか実装してみる.
ここではグレースケール画像の2次元配列を扱うが,RGBの3要素2次元配列への拡張は簡単である.
ニアレストネイバー法
ニアレストネイバー法では,隙間のpxの座標を最も近いdest=(x*rate,y*rate)の色にすることで補間を行う.
# 元画像srcの縦横,拡大画像dstの縦横
(h_origin, w_origin) = size(src)
(h, w) = (h_origin*rate, w_origin*rate)
dst = zeros((h,w))
for y_dst in 1:h
for x_dst in 1:w
# (x_src,y_src)は今注目する(x_dst,y_dst)に一番近いsrcの座標
x_src, y_src = Int(round(x_dst/rate)), Int(round(y_dst/rate))
# 端の値をセット
if x_src < 1
x_src = 1
elseif x_src > w_origin
x_src = w_origin
end
if y_src < 1
y_src = 1
elseif y_src > h_origin
y_src = h_origin
end
# 最も近いsrcの色をコピー
dst[y_dst,x_dst] = src[y_src,x_src]
end
end
ただしこの方法だと拡大画像がチェッカーボードのようなモザイク感を醸し出す.
バイリニア法
ニアレストネイバー法では直近の色をそのままコピーしただけだったので色の変化に乏しい画像になってしまった.
バイリニア法ではこの方法に少しアレンジを加え,近傍のpxの色の変化がグラデーションになるように,srcから色をコピーした後に1次関数の傾きで重み付けする方法である.
# 元画像srcの縦横,拡大画像dstの縦横
(h_origin, w_origin) = size(src)
(h, w) = (h_origin*rate, w_origin*rate)
dst = zeros((h,w))
for y_dst in 1:h
for x_dst in 1:w
# (x_src,y_src)は今注目する(x_dst,y_dst)に一番近いsrcの座標
x, y = x_dst/rate, y_dst/rate
x_src, y_src = Int(round(x)), Int(round(y))
# 端の値をセット
if x_src < 2
x_src = 2
elseif x_src > w_origin -1
x_src = w_origin -1
end
if y_src < 2
y_src = 2
elseif y_src > h_origin -1
y_src = h_origin -1
end
# 重み
W1 = (1 - (x - x_src)) * (1- (y - y_src))
W2 = (x - x_src) * (1- (y - y_src))
W3 = (1- (x - x_src)) * (y - y_src)
W4 = (x - x_src) * (y - y_src)
# 拡大画像の画素値を計算
dst[y_dst,x_dst] =
( W1*src[y_src,x_src]
+ W2*src[y_src,x_src+1]
+ W3*src[y_src,x_src+1]
+ W4*src[y_src+1,x_src+1])
end
end
この重みは,
Y = [y_src + 1 - y_dst
y_dst - y_src]
X = [x_src + 1 - x_dst
x_dst - x_src]
S = [src[x_src, y_src] , src[x_src+1, y_src]
src[x_src, y_src+1] , src[x_src+1, y_src+1]]
dsr[x_dst,y_dst] = Y' * S * X
の計算により得られたもので,x_dst
,y_dst
の1次関数になる.
全体的にぼやけた感じになる.
バイキュービック法
更に良い重みが欲しくなる.バイキュービック法では線形な重みではなく定義域[-2,2]のsinc関数
sinc(x) = \frac{sin(πx)}{πx}
の近似を用いる.この多項式近似が3次なのでキュービックの名前がつけられたんだと思う.ほんとか?
# 重みsinc関数
function sinc(d)
a = -0.75
if (d<=1.0)
ret = (a+2.0)*d^3 - (a+3.0)*d^2 + 1
elseif (d<=2.0)
ret = (a)*d^3 - (5.0*a)*d^2 + (8.0*a)*d - (4.0*a)
else
ret = 0
end
return ret
end
for y_dst in 1:h
for x_dst in 1:w
# (x_src,y_src)は今注目する(x_dst,y_dst)に一番近いsrcの座標
x, y = x_dst/rate, y_dst/rate
x_src, y_src = Int(round(x)), Int(round(y))
suma = 0
for i in (y_src-1):(y_src+1)
for j in (x_src-1):(x_src+1)
# 端の値をセット
if x_src < 1
x_src = 1
elseif x_src > w_origin
x_src = w_origin
end
if y_src < 1
y_src = 1
elseif y_src > h_origin
y_src = h_origin
end
# 画素成分の分子の計算
dst[x_dst,y_dst] += src[x_src,y_src] * sinc(abs(x - j)) * sinc(abs(y - i))
# 画素成分の分母の計算
suma += sinc(abs(x - j)) * sinc(abs(y - i))
end
end
# 分子と分母をセット
dst[x_dst,y_dst] /= suma
end
end
この分子と分母の計算では,
dst[x,y] = \frac{\sum_{i,j ∈ 近傍}src[i,j]sinc(i)sinc(j)}{\sum_{i,j ∈ 近傍}sinc(i)sinc(j)}
が起こっている.
わかりやすい → http://koujinz.cocolog-nifty.com/blog/2009/05/bicubic-a97c.html
またsinc関数の定義域をより広めた補間方法も知られる.
まとめ
illustratorやClipStudioでバイリニアとかバイキュービックとか聞かれても何が起こるかわかるようになった.めでたしめでたし.
Author And Source
この問題について(バイリニア法,バイキュービック法のプログラム), 我々は、より多くの情報をここで見つけました https://qiita.com/Ariwaramiya/items/7e95812bc17ed00689a1著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .