【Golang】image パッケージの Bounds とは（四角が描画された画像を作成したい）

> Go 言語（Golang）のユニットテストで、テスト用の画像を作りたい。
> でも、画像の作り方を調べると出てくる **`Bounds` ってどう言う意味？**

[「"golang" 画像 描画 四角」でググって](https://www.google.com/search?q=%22golang%22+%E7%94%BB%E5%83%8F+%E6%8F%8F%E7%94%BB+%E5%9B%9B%E8%A7%92)も[「"golang" image boundsとは」でググって](https://www.google.com/search?q=%22golang%22+image+bounds%E3%81%A8%E3%81%AF)も何かよくわからなかったり、Go v1.16 系では動かなかったので、自分のググラビリティとして。

## TL; DR <sub><sup><sub><sup>（今北産業）</sup></sub></sup></sub>

1. `Bounds()` の `Bound` は「境界」や「隣接する範囲」などの意味があります。転じて Golang では「領域情報」と同じ意味になります。<br><sub><sup>日本語で言う<strong>「バウンド」の「跳ね返る」ではありません</strong>。`〜 using the bounds of ...` のような表現は「... と同じ領域情報を使って〜」といった意味になります。</sup></sub>
2. `Bounds()` は Image オブジェクトのメソッド（クラス関数）です。<br><sub><sup>戻り値は `Rectangle` 型でイメージの領域（画像サイズ）を返します。（10px の正方形の画像の場合は "(0,0)-(10,10)"）</sup></sub>
3. とある画像と同じサイズの画像でゴニョゴニョしたい場合に `Bounds()` で領域情報を取得するなどで利用します。
    - [bound とは](https://eow.alc.co.jp/search?q=bound) @ 英辞郎 on the Web
    - `image` パッケージの Go ソースコード:
        - [image.go](https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/image/image.go) | image | src | v1.16.6 @ cs.opensource.google
    - `image/color` パッケージの Go ソースコード:
        - [color.go](https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/image/color/color.go) | color | image | src | v1.16.6 @ cs.opensource.google

```go:四角を描画した画像を作成する例
package main

import (
	"github.com/kr/pretty" // 画像情報を見やすいように出力する
	"image"
	"image/color"

	"fmt"
)

func main() {
	// 色の定義（アルファチャンネル付きの RGB 値）
	red := color.RGBA{255, 0, 0, 255}  // 赤, 不透明度 Max 255 = 透過度ゼロ
	blue := color.RGBA{0, 0, 255, 255} // 青, 不透明度 Max 255 = 透過度ゼロ

	// 領域の定義（50x50px の四角形 = 50px の正方形）
	width := 50
	height := 50

	myBound := image.Rectangle{Max: image.Point{X: width, Y: height}}

	// アルファチャンネル付きの RGB キャンバス作成（サイズは myBound）
	canvas := image.NewRGBA(myBound)

	// 赤ボックスの描画（左上原点でキャンバスの 0,0 から 10x10 px の赤い点を打ち続けて四角を塗り潰し）
	for x := 0; x < 10; x++ {
		for y := 0; y < 10; y++ {
			canvas.SetRGBA(x, y, red)
		}
	}

	// 青ボックスの描画（左上原点でキャンバスの中央点から 10x10 px の赤い点を打ち続けて四角を塗り潰し）
	offsetX := width / 2
	offsetY := width / 2

	for x := 0; x < 10; x++ {
		for y := 0; y < 10; y++ {
			canvas.SetRGBA(x+offsetX, y+offsetY, blue)
		}
	}

	fmt.Printf("Result Bounds: %s\n", canvas.Bounds().String())
	fmt.Printf("%# v", pretty.Formatter(canvas)) // Canvas 情報を出力
}

```

- [出力（実行結果）をオンラインで見る](https://play.golang.org/p/OU_t2LTrH87) @ Go Playground

## TS; DR <sub><sup><sub><sup>（四角を描きたいだけなのに）</sup></sub></sup></sub>

Go 言語（以下 Golang）でイメージの画像処理をするパッケージを使った関数のために、**テストで使うダミー画像を作りたかった**のです。

具体的には、"[prominentcolor](https://github.com/EdlinOrg/prominentcolor)" というパッケージを使った俺様関数の出力結果をテストしたかったのです。

- [github.com/EdlinOrg/prominentcolor](https://github.com/EdlinOrg/prominentcolor) @ GitHub
    - `prominentcolor` is a package to find the K most dominant/prominent colors in an image.
    - `prominentcolor` はイメージの配色を探すパッケージで、イメージ内で支配力のある色（最も使われている色味）を <ruby>[k-means](https://ja.wikipedia.org/wiki/K%E5%B9%B3%E5%9D%87%E6%B3%95)<rt>k 平均法</rt></ruby> で探します。

つまり、Web の世界でいう「ページの[ドミナント・カラーやトーン](https://ja.wikipedia.org/wiki/%E9%85%8D%E8%89%B2#%E9%A1%9E%E4%BC%BC%E6%80%A7%E3%81%AE%E3%81%82%E3%82%8B%E9%85%8D%E8%89%B2)」を決めるために、メインで使われている画像から算出したかったのです。

やりたいことはシンプルです。「透明なキャンパスに、大きさの違う 4 つの色付きの四角を描画しただけの画像」を作成して**俺様関数に渡した結果をテストするのに使いたいだけ**なのです。

ペイント・ツールで作成した画像を使用（手動で作成）しても良いのですが、さまざまなパターンが必要になった場合に面倒です。

そこで「（これを機に）」と、本業（テストを作成すること）を忘れて調べ始めました。

しかし、Golang 初心者であるため、テスト用の画像を Golang で作成する方法がいまいちわかりません。画像を読み込んだり、保存したりするのはわかるのですが。

ネットサーフィンしても、「標準パッケージにはない」だの「いやある。`Draw` を使え」だのといった情報はあるものの、[<ruby>`deprecated`<rt>消費期限切れ</rt></ruby> になった ZR グローバル変数](https://pkg.go.dev/image#Rectangle)を使っていたりして Go v1.16 で使えるものがありませんでした。

結局、本家の `image` ライブラリソースコードを読んで、テストを実装しました。

```go:imageライブラリで四角を描画した画像でテストする例
package main

import (
	"errors"
	"image"
	"image/color"
	"testing"

	"github.com/EdlinOrg/prominentcolor"
)

// ----------------------------------------------------------------------------
//  Function
// ----------------------------------------------------------------------------

// GetDominantColors returns 3 most used colors.
func GetDominantColors(image image.Image) ([]interface{}, error) {
	colors, err := prominentcolor.Kmeans(image)
	if err != nil {
		return nil, errors.New("Failed to get dominant colors.")
	}

	colorsHex := make([]interface{}, 3)

	for i, color := range colors {
		colorsHex[i] = "#" + color.AsString()
	}

	return colorsHex, nil
}

// ----------------------------------------------------------------------------
//  Test
// ----------------------------------------------------------------------------

func TestGetDominantColors(t *testing.T) {
	img := genImageRect(t)

	result, err := GetDominantColors(img)
	if err != nil {
		t.Fatal()
	}
	t.Logf("[LOG] Returned value: %#v", result)

	if result[0] != "#FF0000" {
		t.Logf("Expect: #FF0000, got: %+v\n", result[0])
		t.Fail()
	}

	if result[1] != "#00FF00" {
		t.Logf("Expect: #00FF00, got: %+v\n", result[1])
		t.Fail()
	}
	if result[2] != "#0000FF" {
		t.Logf("Expect: #0000FF, got: %+v\n", result[2])
		t.Fail()
	}
}

// ----------------------------------------------------------------------------
//  Helper Function
// ----------------------------------------------------------------------------

// genImageRect generates an image with boxes with different color and size at
// each corner of the image.
func genImageRect(t *testing.T) image.Image {
	t.Helper()

	red := color.RGBA{255, 0, 0, 255}
	green := color.RGBA{0, 255, 0, 255}
	blue := color.RGBA{0, 0, 255, 255}
	gray := color.RGBA{100, 100, 100, 255}

	// Canvas size
	width := 100
	height := 100
	myBound := image.Rectangle{Max: image.Point{X: width, Y: height}}

	// Create image
	canvas := image.NewRGBA(myBound)

	// Draw red square with size 30px @ pos: 0,0
	for x := 0; x < 30; x++ {
		for y := 0; y < 30; y++ {
			canvas.SetRGBA(x, y, red)
		}
	}

	// Draw green square with size 29 @ pos: 0, -29
	for x := 0; x < 29; x++ {
		for y := 0; y < 29; y++ {
			canvas.SetRGBA(x, y+(height-29), green)
		}
	}

	// Draw blue square with size 28 @ pos: -28, 0
	for x := 0; x < 28; x++ {
		for y := 0; y < 28; y++ {
			canvas.SetRGBA(x+(width-28), y, blue)
		}
	}

	// Draw gray square with size 5px @ pos: -5, -5
	for x := 0; x < 5; x++ {
		for y := 0; y < 5; y++ {
			canvas.SetRGBA(x+(width-5), y+(height-5), gray)
		}
	}

	return canvas
}

```

- [オンラインで動作を見る](https://play.golang.org/p/f2T7AMaVF12) @ Go Playground