> Source: https://www.ymotongpoo.com/works/goblog-ja/go-image-package/


# Go image パッケージ

[The Go image package](https://blog.golang.org/go-image-package) By Nigel Tao

## はじめに

[image](http://golang.org/pkg/image/) と [image/color](http://golang.org/pkg/image/color/) パッケージはいくつかの型を定義しています。`color.Color` と `color.Model` は色を、`image.Point` と `image.Rectangle` は基本的な2次元幾何学をそれぞれ記述しており、`image.Image` は色の長方形格子を表現するためにそれら2つの概念を1つにまとめます。[別記事](http://golang.org/doc/articles/image_draw.html) では [image/draw](http://golang.org/pkg/image/draw/) を用いた画像の合成について取り上げています。

## Color と Color Model

[Color](http://golang.org/pkg/image/color/#Color) は色として考えられる任意の型をまとめた最小限のメソッドの組を定義したインターフェースです。つまり、赤、緑、青、アルファ値に変換できるものです。CMYK や YCbCr 色空間から変換するといったある種の変換は不可逆かもしれません。

```go
type Color interface {
    // RGBA はこの色のプリマルチプライド・アルファな赤、緑、青、アルファの値を
    // 返します。各値は [0, 0xFFFF] の範囲に収まりますが、0xFFFF までのブレンド
    // 係数を掛けてもオーバーフローしないよう uint32 型で表現されています。
    RGBA() (r, g, b, a uint32)
}
```

戻り値には3つの重要な注意点があります。1つ目に、赤、緑、青はプリマルチプライド・アルファであることです。25% 透明な完全飽和赤は 75% の r を返す RGBA によって表現されます。2つ目に、チャネルの有効範囲は 16 ビットです。100% の赤は 255 ではなく 65535 の r を返す RGBA によって表現されているので、CMYK や YCbCr からの変換はさほど情報量を失いません。3つ目に、最大値は 65535 ですが、戻り値の型は `uint32` となっていることです。これは2つの値の積がオーバーフローしないことを保証するためです。こうした乗算は、[Porter と Duff の](https://en.wikipedia.org/wiki/Alpha_compositing)古典的な代数のスタイルで、第3の色に由来するアルファマスクに従って2つの色をブレンドする際に発生します。

```go
dstr, dstg, dstb, dsta := dst.RGBA()
srcr, srcg, srcb, srca := src.RGBA()
_, _, _, m := mask.RGBA()
const M = 1<<16 - 1
// 結果として得られる赤の値は dstr と srcr をブレンドしたもので、[0, M] の範囲に収まります。
// 緑、青、アルファの計算も同様です。
dstr = (dstr*(M-m) + srcr*m) / M
```

もし非プリマルチプライド・アルファを使って処理を行おうとすると、コード片の最後の行はより複雑になるでしょう。それが `Color` がプリマルチプライド・アルファな値を使う理由です。

image/color パッケージも `Color` インターフェースを実装するいくつかの具象型を定義しています。例えば、[`RGBA`](http://golang.org/pkg/image/color/#RGBA) は古典的な "1 チャネルあたり 8 ビット" の色を表現する構造になっています。

```go
type RGBA struct {
    R, G, B, A uint8
}
```

`RGBA` の `R` フィールドは 0 から 255 の範囲の値をとる 8 ビットのプリマルチプライド・アルファな色であることに注意してください。0 から 65535 の範囲の値をとる 16 ビットのプリマルチプライド・アルファな色を生成するために 0x101 を値に掛けることによって `RGBA` は `Color` インターフェースを満たします。同様に、PNG の画像形式で使われるように [`NRGBA`](http://golang.org/pkg/image/color/#NRGBA) 構造型は 8 ビットの非プリマルチプライド・アルファな色を表現します。`NRGBA` のフィールドを直接操作する際は、その値は非プリマルチプライド・アルファですが、`RGBA` メソッドを呼ぶ際は、その戻り値はプリマルチプライド・アルファです。

[`Model`](http://golang.org/pkg/image/color/#Model) は単純なもので、ある `Color` を、場合によっては非可逆的に、別の `Color` に変換できるものです。例えば、`GrayModel` は任意の `Color` を不飽和化された [`Gray`](http://golang.org/pkg/image/color/#Gray) に変換できます。`Palette` は任意の `Color` を、制限されたパレットの中の `Color` に変換できます。

```go
type Model interface {
    Convert(c Color) Color
}

type Palette []Color
```

## Point と Rectangle

[`Point`](http://golang.org/pkg/image/#Point) は右方向と下方向に増加する軸に従った整数格子上の (x, y) 座標です。それはピクセルでも格子で区切られた正方形でもありません。`Point` は固有の幅や高さ、色を持っていませんが、以下の図では色付きの小さな正方形を用いています。

```go
type Point struct {
    X, Y int
}
```

![go-image-package_image-package-01](./go-image-package_image-package-01.png)

```go
p := image.Point{2, 1}
```

[`Rectangle`](http://golang.org/pkg/image/#Rectangle) は左上と右下の `Point` によって定義される整数格子上にある軸に平行な長方形です。`Rectangle` も固有の色を持っていませんが、以下の図では色付きの細い線で長方形の輪郭を描き、その `Min` と `Max` の `Point` を示しています。

```go
type Rectangle struct {
    Min, Max Point
}
```

便利のため、`image.Rect(x0, y0, x1, y1)` は `image.Rectangle{image.Point{x0, y0}, image.Point{x1, y1}}` と同値ですが、前者のほうがはるかに入力しやすくなっています。

`Rectangle` は左上の点を包含し、右下の点を排他しています。`Point p` と `Rectangle r` に対し、`r.Min.X <= p.X && p.X < r.Max.X` を満たす場合のみ `p.In(r)` と表します。`Y` についても同様です。これは、スライス `s[i0:i1]` が下限を含み上限を排他するのと似ています。（配列やスライスと違い、`Rectangle` はよく非ゼロの原点を含みます。）

![go-image-package_image-package-02](./go-image-package_image-package-02.png)

```go
r := image.Rect(2, 1, 5, 5)
// Dx と Dy は長方形の幅と高さを返します。
fmt.Println(r.Dx(), r.Dy(), image.Pt(0, 0).In(r)) // prints 3 4 false
```

`Point` を `Rectangle` に加えることでその `Rectangle` を平行移動させます。点と長方形は右下の象限内に制限されていません。

![go-image-package_image-package-03](./go-image-package_image-package-03.png)

```go
r := image.Rect(2, 1, 5, 5).Add(image.Pt(-4, -2))
fmt.Println(r.Dx(), r.Dy(), image.Pt(0, 0).In(r)) // prints 3 4 true
```

2つの長方形の交わりはもう1つの長方形になりますが、それは空になることもあります。

![go-image-package_image-package-04](./go-image-package_image-package-04.png)

```go
r := image.Rect(0, 0, 4, 3).Intersect(image.Rect(2, 2, 5, 5))
// Size は長方形の幅と高さを Point として返します。
fmt.Printf("%#v\n", r.Size()) // prints image.Point{X:2, Y:1}
```

点と長方形は値渡しで関数に渡されたり返されたりします。`Rectangle` を引数に取る関数は、2つの `Point` あるいは4つの `int` を引数にとる関数と同じくらい効率的です。

## Image

[Image](http://golang.org/pkg/image/#Image) は `Rectangle` 内の格子で区切られた正方形を `Model` から作られる `Color` に射影します。"(x, y) のピクセル" は点 (x, y), (x+1, y), (x+1, y+1), (x, y+1) によって定義される格子で区切られた正方形の色を参照します。

```go
type Image interface {
    // ColorModel は Image のカラーモデルを返します。
    ColorModel() color.Model
    // Bounds は At が非ゼロの色を返しうる領域を返します。
    // この範囲は必ずしも点 (0, 0) を含むとは限りません。
    Bounds() Rectangle
    // At は (x, y) にあるピクセルの色を返します。
    // At(Bounds().Min.X, Bounds().Min.Y) は格子の左上のピクセルを返します。
    // At(Bounds().Max.X-1, Bounds().Max.Y-1) は右下のピクセルを返します。
    At(x, y int) color.Color
}
```

よくある誤りは `Image` の範囲が (0, 0) から始まると思い込むことです。例えば、アニメーション GIF は複数の `Image` からなる画像列を含みます。典型的には、最初の画像以降のそれぞれの `Image` は変化した領域のピクセルデータだけを保持しており、その領域は (0, 0) から始まるとは限りません。`Image` m のピクセル全体を走査する正しい方法は次のようになります。

```go
b := m.Bounds()
for y := b.Min.Y; y < b.Max.Y; y++ {
    for x := b.Min.X; x < b.Max.X; x++ {
        doStuffWith(m.At(x, y))
    }
}
```

`Image` の実装はピクセルデータのインメモリスライスを元にする必要はありません。例えば、[`Uniform`](http://golang.org/pkg/image/#Uniform) は広大で一様な色の `Image` で、そのインメモリ表現は単なるその色です。

```go
type Uniform struct {
    C color.Color
}
```

それでもなお典型的に、プログラムはスライスベースの画像を求めています。[`RGBA`](http://golang.org/pkg/image/#RGBA) や [`Gray`](http://golang.org/pkg/image/#Gray) のような構造型（他のパッケージでは `image.RGBA` や `image.Gray` として参照する）はピクセルデータのスライスを保持し `Image` インターフェースを実装します。

```go
type RGBA struct {
    // Pix は画像のピクセルを R, G, B, A の順に保持します。(x, y) のピクセルは
    // Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4] から始まります。
    Pix []uint8
    // Stride は縦に隣接するピクセル間の Pix のストライド(バイト数)です。
    Stride int
    // Rect は画像の範囲です。
    Rect Rectangle
}
```

それらの型も一度に1ピクセルを修正する `Set(x, y int, c color.Color)` メソッドを提供します。

```go
m := image.NewRGBA(image.Rect(0, 0, 640, 480))
m.Set(5, 5, color.RGBA{255, 0, 0, 255})
```

大量のピクセルデータを読み書きしたい場合、構造型の `Pix` フィールドに直接アクセスすると効率は上がりますが、その分コードは複雑になります。

スライスベースの `Image` の実装も同じ配列を元にした `Image` を返す `SubImage` メソッドを提供しています。サブスライス `s[i0:i1]` の内容を修正すると元のスライス `s` の内容に影響が及ぶことと同様に、サブ画像のピクセルを修正すると元の画像のピクセルに影響が及びます。

![go-image-package_image-package-05](./go-image-package_image-package-05.png)

```go
m0 := image.NewRGBA(image.Rect(0, 0, 8, 5))
m1 := m0.SubImage(image.Rect(1, 2, 5, 5)).(*image.RGBA)
fmt.Println(m0.Bounds().Dx(), m1.Bounds().Dx()) // prints 8, 4
fmt.Println(m0.Stride == m1.Stride)             // prints true
```

画像の `Pix` フィールドを直接扱う下層のコードでは、`Pix` 全体を走査すると画像の範囲外のピクセルにまで影響が及ぶことがある点に注意してください。上記の例では、`m1.Pix` に含まれるピクセルが青色で示されています。`At` や `Set` メソッドまたは [image/draw パッケージ](http://golang.org/pkg/image/draw/) のような上層のコードは、その操作を画像の範囲内に切り詰めます。

## 画像の形式

標準パッケージライブラリは GIF、JPEG、PNG など多くのありふれた画像形式をサポートしています。もとの画像ファイルのフォーマットが分かっている場合、[`io.Reader`](http://golang.org/pkg/io/#Reader) から直接デコードできます。

```go
import (
    "image/jpeg"
    "image/png"
    "io"
)

// convertJPEGToPNG は JPEG から PNG に変換します。
func convertJPEGToPNG(w io.Writer, r io.Reader) error {
    img, err := jpeg.Decode(r)
    if err != nil {
        return err
    }
    return png.Encode(w, img)
}
```

形式不明な画像データがある場合、[`image.Decode`](http://golang.org/pkg/image/#Decode) 関数は形式を検出できます。認識される形式の集合は実行時に構築され、標準パッケージライブラリ内の形式に制限されていません。画像形式パッケージは典型的に init 関数内で自身の形式を登録し、main パッケージは形式登録という副作用だけを目的に、そのようなパッケージを "アンダースコアインポート" できます。

```go
import (
    "image"
    "image/png"
    "io"

    _ "code.google.com/p/vp8-go/webp"
    _ "image/jpeg"
)

// convertToPNG は認識可能な任意の形式から PNG に変換します。
func convertToPNG(w io.Writer, r io.Reader) error {
    img, _, err := image.Decode(r)
    if err != nil {
        return err
    }
    return png.Encode(w, img)
}
```

*By Nigel Tao*

## あわせて読む
* [HTTP/2 Server Push](https://blog.golang.org/h2push)
* [Introducing HTTP Tracing](https://blog.golang.org/http-tracing)
* [Generating code](https://blog.golang.org/generate)
* [Introducing the Go Race Detector](https://blog.golang.org/race-detector)
* [Go maps in action](https://blog.golang.org/go-maps-in-action)
* [go fmt your code](https://blog.golang.org/go-fmt-your-code)
* [Organizing Go code](https://blog.golang.org/organizing-go-code)
* [Debugging Go programs with the GNU Debugger](https://blog.golang.org/debugging-go-programs-with-gnu-debugger)
* [The Go image/draw package](https://blog.golang.org/go-imagedraw-package)
* [The Laws of Reflection](https://blog.golang.org/laws-of-reflection)
* [Error handling and Go](https://blog.golang.org/error-handling-and-go)
* ["First Class Functions in Go"](https://blog.golang.org/first-class-functions-in-go-and-new-go)
* [Profiling Go Programs](https://blog.golang.org/profiling-go-programs)
* [Spotlight on external Go libraries](https://blog.golang.org/spotlight-on-external-go-libraries)
* [A GIF decoder: an exercise in Go interfaces](https://blog.golang.org/gif-decoder-exercise-in-go-interfaces)
* [Introducing Gofix](https://blog.golang.org/introducing-gofix)
* [Godoc: documenting Go code](https://blog.golang.org/godoc-documenting-go-code)
* [Gobs of data](https://blog.golang.org/gobs-of-data)
* [C? Go? Cgo!](https://blog.golang.org/c-go-cgo)
* [JSON and Go](https://blog.golang.org/json-and-go)
* [Go Slices: usage and internals](https://blog.golang.org/go-slices-usage-and-internals)
* [Go Concurrency Patterns: Timing out, moving on](https://blog.golang.org/go-concurrency-patterns-timing-out-and)
* [Defer, Panic, and Recover](https://blog.golang.org/defer-panic-and-recover)
* [Share Memory By Communicating](https://blog.golang.org/share-memory-by-communicating)
* [JSON-RPC: a tale of interfaces](https://blog.golang.org/json-rpc-tale-of-interfaces)
