1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-08-03

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

「"golang" 画像 描画 四角」でググって「"golang" image boundsとは」でググっても何かよくわからなかったり、Go v1.16 系では動かなかったので、自分のググラビリティとして。

TL; DR (今北産業)

  1. Bounds()Bound は「境界」や「隣接する範囲」などの意味があります。転じて Golang では「領域情報」と同じ意味になります。
    日本語で言う「バウンド」の「跳ね返る」ではありません〜 using the bounds of ... のような表現は「... と同じ領域情報を使って〜」といった意味になります。
  2. Bounds() は Image オブジェクトのメソッド(クラス関数)です。
    戻り値は Rectangle 型でイメージの領域(画像サイズ)を返します。(10px の正方形の画像の場合は "(0,0)-(10,10)")
  3. とある画像と同じサイズの画像でゴニョゴニョしたい場合に Bounds() で領域情報を取得するなどで利用します。
    • bound とは @ 英辞郎 on the Web
    • image パッケージの Go ソースコード:
      • image.go | image | src | v1.16.6 @ cs.opensource.google
    • image/color パッケージの Go ソースコード:
      • color.go | color | image | src | v1.16.6 @ cs.opensource.google
四角を描画した画像を作成する例
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 情報を出力
}

TS; DR (四角を描きたいだけなのに)

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

具体的には、"prominentcolor" というパッケージを使った俺様関数の出力結果をテストしたかったのです。

  • github.com/EdlinOrg/prominentcolor @ GitHub
    • prominentcolor is a package to find the K most dominant/prominent colors in an image.
    • prominentcolor はイメージの配色を探すパッケージで、イメージ内で支配力のある色(最も使われている色味)を k-meansk 平均法 で探します。

つまり、Web の世界でいう「ページのドミナント・カラーやトーン」を決めるために、メインで使われている画像から算出したかったのです。

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

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

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

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

ネットサーフィンしても、「標準パッケージにはない」だの「いやある。Draw を使え」だのといった情報はあるものの、deprecated消費期限切れ になった ZR グローバル変数を使っていたりして Go v1.16 で使えるものがありませんでした。

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

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
}

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?