LoginSignup
8
1

More than 3 years have passed since last update.

Go言語の標準パッケージだけで画像処理をする その2 (回転、反転)

Last updated at Posted at 2019-12-05

ZOZOテクノロジーズ #5 Advent Calendar 2019の記事です。
昨日は 「Go言語の標準パッケージだけで画像処理をする その1 (入出力)」 の記事を書きました。

本記事では引き続き、Go言語の標準パッケージでの画像処理について書いていきます。

はじめに

「なぜGo言語で画像処理をするのか?ということについては、以下の記事をご覧ください。
Go言語の標準パッケージだけで画像処理をする その1 (入出力)

CEC2C8A6-4EE6-414F-8191-E256170FC0E1.jpeg

Go言語 image パッケージ
Package image

回転

ここではアフィン変換を用いて画像の回転を実装しています。
アフィン変換の詳細については以下を参照していただければと思います。

画像処理ソリューション アフィン変換

以下ソースコードと出力画像になります。

// 回転の処理
func Rotation(inputImage image.Image, mode int) image.Image {

    // 出力画像を定義
    var outputImage image.Image

    switch mode {
    case -1:
        // 右90度回転
        outputImage = Affine(inputImage, 90, inputImage.Bounds().Max.X - 1, 0, 1)
        break
    case 0:
        // 右180度回転
        outputImage = Affine(inputImage, 180, inputImage.Bounds().Max.X - 1, inputImage.Bounds().Max.Y - 1, 1)
        break
    case 1:
        // 右270度回転
        outputImage = Affine(inputImage, 270, 0, inputImage.Bounds().Max.Y - 1, 1)
        break
    default:
        log.Fatal("angle code does not exist")
        break
    }

    return outputImage
}

// アフィン変換の処理
func Affine(inputImage image.Image, angle int, tx int, ty int, scale float64) image.Image {

    // 出力画像を定義
    size := inputImage.Bounds()
    size.Max.X = int(float64(size.Max.X) * scale)
    size.Max.Y = int(float64(size.Max.Y) * scale)

    outputImage := image.NewRGBA(size)

    // ステータスのキャスト
    theta := float64(angle) * math.Pi / 180
    cos := math.Cos(theta)
    sin := math.Sin(theta)

    matrix := [][]float64{{cos * scale, -sin * scale, float64(tx)}, {sin * scale, cos * scale, float64(ty)}, {0.0, 0.0, 1.0}}

    // 左右反転
    for y := size.Min.Y; y < size.Max.Y; y++ {
        for x := size.Min.X; x < size.Max.X; x++ {

            outputX := 0
            outputY := 0
            // 元座標を格納
            origin := []float64{float64(x), float64(y), 1.0}

            // 座標を計算
            for rowKey, rowVal := range matrix {
                var val float64

                for colIndex := 0; colIndex < len(rowVal); colIndex++ {

                    val += origin[colIndex] * rowVal[colIndex]
                }

                // 座標の代入
                switch rowKey {
                case 0:
                    outputX = int(round(val))
                    break
                case 1:
                    outputY = int(round(val))
                    break
                default:
                    break
                }

            }

            if size.Min.X <= outputX && outputX < size.Max.X && size.Min.Y <= outputY && outputY < size.Max.Y {
                outputImage.Set(outputX, outputY, inputImage.At(x, y))
            } else {
                // 何もしない
            }
        }
    }

    return outputImage
}

出力結果

元画像

Lennagrey.png

右90度回転

LennagreyR90.png

右180度

LennagreyR180.png

右270度回転

LennagreyR270.png

反転

画像をフィルタ処理にかける前の前処理で使いたかったので実装しました。
やっていることとしては、画素値を移動させているだけです。

以下ソースコードと出力画像になります。

// 画像を反転させる処理
func Inversion(inputImage image.Image, mode int) image.Image {

    // 出力画像を定義
    var outputImage image.Image

    switch mode {
        case -1:
            // 上下左右反転
            outputImage = upsideDown(inputImage)
            break
        case 0:
            // 上下反転
            outputImage = flipUpsideDown(inputImage)
            break
        case 1:
            // 左右反転
            outputImage = flipHorizontal(inputImage)
            break
        default:
            log.Fatal("angle code does not exist")
            break
    }

    return outputImage

}

// 左右反転の処理
func flipHorizontal(inputImage image.Image) image.Image {

    // 出力画像を定義
    size := inputImage.Bounds()
    outputImage := image.NewRGBA(size)

    // 左右反転
    for y := size.Min.Y; y < size.Max.Y; y++ {
        for x := size.Min.X; x < size.Max.X; x++ {
            outputImage.Set(x, y, inputImage.At(size.Max.X - x - 1, y))
        }
    }

    return outputImage
}

// 上下反転の処理
func flipUpsideDown(inputImage image.Image) image.Image {

    // 出力画像を定義
    size := inputImage.Bounds()
    outputImage := image.NewRGBA(size)

    // 上下反転
    for y := size.Min.Y; y < size.Max.Y; y++ {
        for x := size.Min.X; x < size.Max.X; x++ {
            outputImage.Set(x, y, inputImage.At(x, size.Max.Y - y -1))
        }
    }

    return outputImage
}

// 上下左右反転の処理
func upsideDown(inputImage image.Image) image.Image {

    // 左右反転
    outputImage := flipHorizontal(inputImage)
    // 上下反転
    outputImage = flipUpsideDown(outputImage)

    return outputImage
}

出力結果

元画像

Lennagrey.png

上下左右反転

Lennagrey上下左右.png

左右反転

Lennagrey左右.png

上下反転

Lennagrey上下.png

おわりに

ZOZOテクノロジーズ #5 Advent Calendar 2019 明日は @katsuyan さんによる「Digdagで大きいパラメータを登録すると後続の処理が重くなる」です。

8
1
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
8
1