LoginSignup
4

More than 5 years have passed since last update.

golangで画像のサイズ変換を実施する

Posted at

お題目

  1. 最近傍法によるサイズ変換
  2. 線形補間法によるサイズ変換

裏テーマ
1. mainで動かしてたけど、テストで駆動するように変更
2. for文を一箇所にまとめる

最近傍法

要するに、拡大した時に対応するピクセルを比で求めるというだけ。

ソースコード

imageLoopに渡してる、関数の引数xとyが変換後の画像の位置なのですが、それがどこかをdownの関数内で求めてるだけです。
大したことないですな

// 最近傍方
func (ef *effect) ChangeSize() image.Image {
    up := func(val int, rat float64) int { return int(float64(val) * rat) }
    down := func(val int, rat float64) int { return int(float64(val) / rat) }

    // とりあえず正方形にして、0.5倍にする
    ratio := 0.5
    rect := ef.inputImage.Bounds()
    width := up(rect.Size().X, ratio)
    height := width
    newRect := image.Rect(0, 0, width, height)
    yRatio := float64(height) / float64(rect.Size().Y)

    return ef.imageLoop(newRect, func(x, y int) color.RGBA64 {
        r, g, b, a := ef.inputImage.At(down(x, ratio), down(y, yRatio)).RGBA()
        return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
    })
}

ちなみに某技術書を見ながらやってるんだけど、テキストのソースコード間違ってますね。

画像

test_chgsize.jpg

線形補間法

ピクセル間の平均取ればもっといいよねって話
数式は面倒だからwiki参照
数学でやりましたね、この式。なお、テキストはもう少しプログラム寄りに書いてありそうです。

ソースコード

func (ef *effect) senning(x, y int, xRatio, yRatio float64) color.RGBA64 {
    // 比較のための4点とその位置の比を求める関数
    createParam := func(inSize, outSize int, ratio float64) (int, int, float64) {
        v1 := down(outSize, ratio)
        v2 := v1 + 1
        if v2 > inSize-1 {
            v2 = inSize - 1
        }
        v3 := float64(outSize)/ratio - float64(v1)
        return v1, v2, v3
    }
    //  計測点(パラメータ)を作る
    point := ef.inputImage.Bounds().Size()
    j1, j2, q := createParam(point.Y, y, yRatio)
    i1, i2, p := createParam(point.X, x, xRatio)
    positions := [4]struct {
        X int
        Y int
    }{
        {i1, j1},
        {i2, j1},
        {i1, j2},
        {i2, j2},
    }
    // 4点の値を取得する
    var valsR, valsG, valsB, valsA [len(positions)]float64
    for i, v := range positions {
        r, g, b, a := ef.inputImage.At(v.X, v.Y).RGBA()
        valsR[i] = float64(r)
        valsG[i] = float64(g)
        valsB[i] = float64(b)
        valsA[i] = float64(a)
    }
    //各要素の値を求める関数
    con := func(val [4]float64) uint16 {
        return uint16((1.0-q)*((1.0-p)*val[0]+p*val[1]) + q*((1-p)*val[2]+p*val[3]))
    }
    return color.RGBA64{con(valsR), con(valsG), con(valsB), con(valsA)}
}

4点の値を取得するあたりはもう少しやりようがある気がする。
思いつきませんでした。
とりあえず、注目すべきは無名関数conの中ですね、。

uint16((1.0-q)*((1.0-p)*val[0]+p*val[1]) + q*((1-p)*val[2]+p*val[3]))

ちょっと理解しやすくしましょう

$$
\biggl( (1-q) \times \bigl((1-p) \times V_{ij} + p \times V_{i+1j} \bigr) \biggr) + \biggl(q \times \bigl((1-p) \times V_{ij+1} + p \times V_{i+1j+1}) \bigr) \biggr)
$$
1ってのが、変化量の比であると気付けば難しい数式ではないですね。

 出力結果

test_sizesen.jpg
比率を手動で上げてしまえばその違いがよりわかりやすくなります。
情報の復元の方法の一つなんじゃないかな?と思いました。

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