画像処理の基本的なアルゴリズムをGo言語で復習 3(ニアレストネイバー)


ZOZOテクノロジーズの制御エンジニアをやっています池田です。
画像処理の代表的なアルゴリズムをGO言語で実装しながら復習するシリーズです。

はじめに

画像の拡大縮小に用いられる、線形補間について振り返っていきたいと思います。
中でも今回は線形補間の中でも単純なニアレストネイバー(最近傍補間)について実装含め検証していきます。

ニアレストネイバー(最近傍補間)

「Nearest neighbor」、直訳すると「最近隣」という意味です。その名の通り、注目画素周辺の画素を参照する補間方法です。
言ってしまえば画素のカサ増しみたいなものです。
以下図は、画像を横方向2倍にニアレストネイバーで拡大する時の簡単なイメージです。

検証方法

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

① 元画像をPhotoshopのバイキュービック法で1/2に縮小
② 縮小した元画像を今回実装したニアレストネイバーで2倍に拡大
③ 元画像とニアレストネイバーで2倍に拡大した画像を比較

ソースコード

NearestNeighbor.go

package NearestNeighbor

import (
    "gocv.io/x/gocv"
)

// ニアレストネイバー法
func Execution(inputImg gocv.Mat, inputImageRows int, inputImageCols int, magnificationRows float64, magnificationCols float64) gocv.Mat {

    // リサイズ後画像サイズ
    resizeImageRows := int(float64(inputImageRows) * magnificationRows)
    resizeImageCols := int(float64(inputImageCols) * magnificationCols)

    // アウトプット画像を定義
    outputImg := gocv.NewMatWithSize(resizeImageRows, resizeImageCols, gocv.IMReadGrayScale)

    // 画像の左上から順に画素を読み込む
    for imgRows := 0; imgRows < resizeImageRows; imgRows++ {
        // 元画像の縦の座標
        nearRows := int(float64(imgRows) / magnificationRows)

        for imgCols := 0; imgCols < resizeImageCols; imgCols++ {
            // 元画像の横の座標
            nearCols := int(float64(imgCols) / magnificationCols)
            nearPixel := inputImg.GetUCharAt(nearRows, nearCols)
            outputImg.SetUCharAt(imgRows, imgCols, nearPixel)
        }
    }

    return outputImg
}

実行結果

元画像

実行後画像

元画像に比べると、全体的にぼやけている様に見えます。
また、エッジの部分がギザギザになって出力されています。

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

元画像との差を見てみると、やはりエッジの部分に差が多く出ています。

MSE/PSNR

[MSE] value :  +6.565691e+001 [PSNR] value :  +2.995800e+001

見た目は結構劣化している様に見えても、画素の差自体は大げさに出ていないみたいですね。

まとめ

拡大縮小のアルゴリズムは評価手法によって結果も大きく変わってきますが、復習がてらの実装なので、単純にPSNRで違いを見てみました。

その他の線形補間についても今後追加で上げていきたいと思います。