画像処理の代表的なアルゴリズムをGO言語で実装しながら復習するシリーズです。
はじめに
前回に引き続き、画像の拡大縮小に用いられる線形補間について振り返ります。
今回はバイリニア補間(双一次補間)についてです。
バイリニア補間(双一次補間)
注目画素の周辺2×2画素、合計で4画素を使って、画素値を直線的に補間する手法です。
前回よりも周辺画素の影響も考えた処理になっています。
以下図は、画像を横方向4倍にバイリニアで拡大する時の簡単なイメージです。
厳密には異なる表現もあるかもしれないですが、ざっくりこんな感じの理解で良いかと。
検証方法
以下手順で検証を行いました。
① 元画像をPhotoshopのバイキュービック法で1/2に縮小
② 縮小した元画像を今回実装したバイリニア補間で2倍に拡大
③ 元画像とバイリニア補間で2倍に拡大した画像を比較
ソースコード
Bilinear.go
package Bilinear
import (
"fmt"
"gocv.io/x/gocv"
)
func Init() {
}
// バイリニア法
func Execution(preImg gocv.Mat, inputImageRows int, inputImageCols int, scalingRows float64, scalingCols float64) gocv.Mat {
// 重み値を定義
var x float64
var y float64
// リサイズ後画像サイズ
resizeImageRows := int(float64(inputImageRows) * scalingRows)
resizeImageCols := int(float64(inputImageCols) * scalingCols)
// 逆数
reciprocalScalingRows := 1 / scalingRows
reciprocalScalingCols := 1 / scalingCols
// アウトプット画像を定義
outputImg := gocv.NewMatWithSize(resizeImageRows, resizeImageCols, gocv.IMReadGrayScale)
// 画像の左上から順に画素を読み込む
for imgRows := 0; imgRows < resizeImageRows; imgRows++ {
for imgCols := 0; imgCols < resizeImageCols; imgCols++ {
// 双一次補完式
// 元画像の座標定義
// 元画像の縦の座標
inputRows := int(float64(imgRows) / scalingRows)
// 元画像の横の座標
inputCols := int(float64(imgCols) / scalingCols)
// 補完式で使う元画像のpixel
// point(0, 0)
src00 := float64(preImg.GetUCharAt(inputRows+inputImageRows, inputCols+inputImageCols))
// point(0, 1)
src01 := float64(preImg.GetUCharAt(inputRows+inputImageRows, inputCols+inputImageCols+1))
// point(1, 0)
src10 := float64(preImg.GetUCharAt(inputRows+inputImageRows+1, inputCols+inputImageCols))
// point(1, 1)
src11 := float64(preImg.GetUCharAt(inputRows+inputImageRows+1, inputCols+inputImageCols+1))
// 重み値を算出
x = float64(imgCols) * reciprocalScalingCols
y = float64(imgRows) * reciprocalScalingRows
// 小数点以下を抽出
x = x - float64(int(x))
y = y - float64(int(y))
// 拡大後の画素を算出
pixel1 := (1 - x) * (1 - y) * src00
pixel2 := x * (1 - y) * src01
pixel3 := (1 - x) * y * src10
pixel4 := x * y * src11
pixel := pixel1 + pixel2 + pixel3 + pixel4
outputImg.SetUCharAt(imgRows, imgCols, uint8(pixel))
}
}
return outputImg
}
実行結果
元画像
実行後画像
ニアレストネイバーと比べると、多少元画像に近づいているように見えます。
エッジ部分はバイリニア補間の方が滑らかになっています。
元画像と実行後画像の差分
元画像との差を見てみると、満遍なく差が出ているように見えます。
エッジが滑らかになっている影響でしょうか。
MSE/PSNR
[MSE] value : +6.462402e+001 [PSNR] value : +3.002686e+001
前回のニアレストネイバーよりPSNRが上がっています。
今回使った画像では、周辺画素の影響を考えることで、元画像との差分を減らすことが出来ることが分かります。
まとめ
バイリニア補間ではニアレストネイバーに比べ、エッジのギザギザが緩和されていました。
ただこの結果で、「バイリニアの方が優れている」となるよりかは、場合によって使い分けていくのが無難かと思います。処理する画像によっても性質は色々なので。