Go 言語(Golang)のユニットテストで、テスト用の画像を作りたい。
でも、画像の作り方を調べると出てくるBounds
ってどう言う意味?
「"golang" 画像 描画 四角」でググっても「"golang" image boundsとは」でググっても何かよくわからなかったり、Go v1.16 系では動かなかったので、自分のググラビリティとして。
TL; DR (今北産業)
-
Bounds()
のBound
は「境界」や「隣接する範囲」などの意味があります。転じて Golang では「領域情報」と同じ意味になります。
日本語で言う「バウンド」の「跳ね返る」ではありません。〜 using the bounds of ...
のような表現は「... と同じ領域情報を使って〜」といった意味になります。 -
Bounds()
は Image オブジェクトのメソッド(クラス関数)です。
戻り値はRectangle
型でイメージの領域(画像サイズ)を返します。(10px の正方形の画像の場合は "(0,0)-(10,10)") - とある画像と同じサイズの画像でゴニョゴニョしたい場合に
Bounds()
で領域情報を取得するなどで利用します。
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 情報を出力
}
- 出力(実行結果)をオンラインで見る @ Go Playground
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-means で探します。
-
つまり、Web の世界でいう「ページのドミナント・カラーやトーン」を決めるために、メインで使われている画像から算出したかったのです。
やりたいことはシンプルです。「透明なキャンパスに、大きさの違う 4 つの色付きの四角を描画しただけの画像」を作成して俺様関数に渡した結果をテストするのに使いたいだけなのです。
ペイント・ツールで作成した画像を使用(手動で作成)しても良いのですが、さまざまなパターンが必要になった場合に面倒です。
そこで「(これを機に)」と、本業(テストを作成すること)を忘れて調べ始めました。
しかし、Golang 初心者であるため、テスト用の画像を Golang で作成する方法がいまいちわかりません。画像を読み込んだり、保存したりするのはわかるのですが。
ネットサーフィンしても、「標準パッケージにはない」だの「いやある。Draw
を使え」だのといった情報はあるものの、deprecated
になった ZR グローバル変数を使っていたりして Go v1.16 で使えるものがありませんでした。
結局、本家の 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
}
- オンラインで動作を見る @ Go Playground