LoginSignup
4
5

More than 3 years have passed since last update.

画像処理の基本的なアルゴリズムをGo言語で復習 4(バイリニア補間)

Last updated at Posted at 2019-05-22

画像処理の代表的なアルゴリズムをGO言語で実装しながら復習するシリーズです。

はじめに

前回に引き続き、画像の拡大縮小に用いられる線形補間について振り返ります。
今回はバイリニア補間(双一次補間)についてです。

バイリニア補間(双一次補間)

注目画素の周辺2×2画素、合計で4画素を使って、画素値を直線的に補間する手法です。
前回よりも周辺画素の影響も考えた処理になっています。
以下図は、画像を横方向4倍にバイリニアで拡大する時の簡単なイメージです。

img1.png

厳密には異なる表現もあるかもしれないですが、ざっくりこんな感じの理解で良いかと。

検証方法

以下手順で検証を行いました。

① 元画像を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
}

実行結果

元画像

Lennagrey.png

実行後画像

Bili2.png

ニアレストネイバーと比べると、多少元画像に近づいているように見えます。
エッジ部分はバイリニア補間の方が滑らかになっています。

元画像と実行後画像の差分

Bili2_dis.png

元画像との差を見てみると、満遍なく差が出ているように見えます。
エッジが滑らかになっている影響でしょうか。

MSE/PSNR

[MSE] value :  +6.462402e+001 [PSNR] value :  +3.002686e+001

前回のニアレストネイバーよりPSNRが上がっています。
今回使った画像では、周辺画素の影響を考えることで、元画像との差分を減らすことが出来ることが分かります。

まとめ

バイリニア補間ではニアレストネイバーに比べ、エッジのギザギザが緩和されていました。
ただこの結果で、「バイリニアの方が優れている」となるよりかは、場合によって使い分けていくのが無難かと思います。処理する画像によっても性質は色々なので。

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5