Go言語の標準パッケージだけで画像処理をする その１ （入出力）

[ZOZOテクノロジーズ #5 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech5)の記事です。
昨日は @satto_sann さんの「[Alexaのアカウントリンクをデバッグする方法](https://qiita.com/satto_sann/items/d6f29adb8e742b8f6216)」でした。

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

# はじめに
最初に明言しておきますが、これはGo言語での画像処理を勧める記事ではありません。
あくまで、自分にメリットがありそうという方のみ参考にしていただければと思います。

自分はZOZOテクノロジーズの中でも、アパレル生産におけるファクトリーオートメーションという分野を担当しています。

担当分野が広いというのと、自動化の対象がアパレル関係ということもあり、
開発環境を実行環境に入れることができなかったり、実行環境により使う言語を変えたりと手間がかかってしまう場面がよく発生します。

特に画像処理関係でプロトタイプをサクッと作る際には、開発環境をその都度作るのが面倒だったりします。

そんな手間を無くせないかなーとふと思い、本記事シリーズを書くことにしました。

![A36FD16C-29EF-4FA3-89A6-717EDFF2409B.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308887/54b2685d-238b-dee0-3043-1c4c18befadc.jpeg)

Go言語 image パッケージ
[Package image](https://golang.org/pkg/image/)

#ファイルの入力
filepathを利用することで、os間のパス違いを意識せずにコードが書けます。
自分は以下のソースのようにdataファイルを用意し、その中に処理したい画像を置いて読み込ませる方法をよく使います。
プロトタイプを軽く動かす時なんかは便利です。

```Go
package main

import (
	"image"
	"log"
	"os"
	"path/filepath"
)

func main() {

	// 実行ファイル直下のパスを取得
	path, _ := os.Getwd()
	dataPath := filepath.Join(path, "data")
	// dataフォルダ直下の画像を一括で読み込む
	jpgPath := filepath.Join(dataPath, "*.jpg")
	jpegPath := filepath.Join(dataPath, "*.jpeg")
	pngPath := filepath.Join(dataPath, "*.png")

	fileList, _ := filepath.Glob(jpgPath)
	f, _ := filepath.Glob(jpegPath)
	fileList = append(fileList, f...)
	f, _ = filepath.Glob(pngPath)
	fileList = append(fileList, f...)

	// 画像を１枚づつ読み込む
	for _, file := range fileList {
		// 画像を読み込む
		img := Input(file)
		println(file, " : " , img)
	}
}

// 画像を読み込む処理
func Input (filePath string) image.Image {

	// 画像を読み込む
	file, err := os.Open(filePath)
	
	// 画像が読み込めなかった場合
	if err != nil {
		log.Fatal(err)
	}

	// 変換
	img, _, err := image.Decode(file)

	return img
}
```

# ファイルの出力

出力したい拡張子によって処理を分けます。
ここではjpgとpngの出力を紹介します。

```Go
const imgQt int = 60

func Output (outputImage image.Image, filePath string, format string) {

	dst, err := os.Create(filePath)

	if err != nil {
		log.Fatal(err)
	}

	switch format {
	case "png":
		// PNGの場合
		err = png.Encode(dst, outputImage)
		if err != nil {
			log.Fatal(err)
		}
		break
	case "jpg":
		// JPGの場合
		qt := jpeg.Options{
			Quality:imgQt,
		}
		err = jpeg.Encode(dst, outputImage, &qt)
		if err != nil {
			log.Fatal(err)
		}
		break
	default:
		// 標準で対応していないフォーマットの場合
		log.Fatal("Unsupported format.")
	}
}
```

# 画素値の参照

以下では読み込んだ画像から画素値を参照したり、操作を行なったりしています。

```Go
func EditPixel(img image.Image) image.Image {

	// アウトプット画像を定義
	size := img.Bounds()
	// 画像のサイズを変える場合
	// size.Max.X = "任意の数値"
	// size.Max.Y = "任意の数値"
	outputImage := image.NewRGBA(size)

	// 画像の左上から順に画素を読み込む
	for imgRow := size.Min.Y; imgRow < size.Max.Y; imgRow++ {
		for imgCol := size.Min.X; imgCol < size.Max.X; imgCol++ {

			// 画素を取得
			pixel := img.At(imgCol, imgRow)
			println("pixel : ", pixel)
			r, g, b, a := pixel.RGBA()
			println("r : ", r, "g : ", g, "b : ", b, "a : ", a)

			// 画素を操作
			var outputColor color.RGBA64
			// 例（真っ黒な画像にする）
			outputColor.R = 0
			outputColor.G = 0
			outputColor.B = 0
			outputColor.A = 100

			outputImage.Set(imgCol, imgRow, outputColor)
		}
	}

	return outputImage
}
```

# おわりに

[ZOZOテクノロジーズ #5 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech5)
明日は「Go言語の標準パッケージだけで画像処理をする その２ （回転、反転）」という記事を書きます。