Golang Cafe #10 まとめ imageパッケージをみる

2013/12/29に開催された「Golang Cafe #10」についてのまとめです。
今回も参加する予定ではなかったのですが、時間がとれたので参加しました。

今回はimageパッケージを触ってみました。
+TakashiYokoyama氏が用意してくれたサンプルコードはこちらになります。

PNGの画像を読み込んでJPEGに変換する

package main

import (
    "image"
    "image/png"
    "image/jpeg"
    "os"
)

func main() {
    var inFile *os.File
    var outFile *os.File
    var img image.Image
    var err error

    if inFile, err = os.Open("pkg.png"); err != nil {
        println("Error", err)
        return
    }

    defer inFile.Close()

    if img, err = png.Decode(inFile); err != nil {
        println("Error", err)
        return
    }

    if outFile, err = os.Create("pkg.jpg"); err != nil {
        println("Error", err)
        return
    }

    option := &jpeg.Options{Quality: 100}

    if err = jpeg.Encode(outFile, img, option); err != nil {
    //if err = jpeg.Encode(outFile, img, nil); err != nil {
        println()
        return
    }

    defer outFile.Close()
}

手順は以下のとおり。

  1. 対象ファイルをオープンする
  2. オープンしたファイルポインタをDecodeメソッドに渡す->画像を表すImage型(あえて言うなら抽象型)として取得できる
  3. 出力先のファイルポインタと取得したImage型をEncodeメソッドに渡す

パッケージを確認する限りでは、gifjpeg、およびpng間で相互変換できると思われる。
また、Image型は取得系メソッドしか定義されていないため変更することができない。

PNGの画像を読み込んで編集する

先ほどの例で読み込んだ画像を編集することが難しいと書きましたが、Go言語デフォルトのパッケージでは一筋縄にはいきません。
+TakashiYokoyama氏のサンプルから説明すると、

rect := inImage.Bounds()
rgba := image.NewRGBA(rect)
size := rect.Size()

for x := 0; x < size.X; x++ {
    for y := 0; y < size.Y; y++ {
        rgba.Set(x, y, inImage.At(x, y))
    }
}
// image/drawパッケージをインポートして以下のようにしてもよい
//draw.Draw(rgba, rgba.Bounds(), inImage, image.Point{0, 0}, draw.Src)

for i := 0; i < 10; i++ {
    rgba.Set(60, (10 + i), image.Black.At(0, 0))
    rgba.Set(65, (10 + i), image.Black.At(0, 0))
    rgba.Set((58 + i), 13, image.Black.At(0, 0))
    rgba.Set((58 + i), 16, image.Black.At(0, 0))
}

// RGBA型をそのままDecodeメソッドに渡せるので以下の2行は不要です
//outRect := image.Rect(0, 0, rect.Max.X, rect.Max.Y)
//outImage := rgba.SubImage(outRect)

読み込んだImage型をSetメソッドが存在するRGBA型等にコピーします。簡単なコピーメソッドは存在しないのでピクセル単位でコピーする必要があります。
またDrawLineやDrawRectangle、あるいはDrawStringのようなメソッドは用意されていないので、自力でピクセルデータを編集する必要があります。

draw2dパッケージを試す

先ほどの例で書きましたが、Go言語のデフォルトのパッケージではDrawLineやDrawRectangle、あるいはDrawStringのようなメソッドは用意されていません。
GoDocでgraphicsをキーワードに検索したところ多くのパッケージが存在するようです。
今回はその中からdraw2dパッケージを試してみることにしました。
まずはパッケージをダウンロードします。

$go get code.google.com/p/draw2d/draw2d

次に先程の例でもおこなったように編集用にrgba型を作成し値をコピーし、その値を元にImageGraphicContext型を作成します。ImageGraphicContext型にはよく見るDraw系のメソッドが用意されているのでこれらを使用して編集することができます。

// import に "code.google.com/p/draw2d/draw2d" を追加

rgba := image.NewRGBA(inImage.Bounds())

draw.Draw(rgba, rgba.Bounds(), inImage, image.Point{0, 0}, draw.Src)

gc := draw2d.NewGraphicContext(rgba)
gc.MoveTo(10.0, 10.0)
gc.LineTo(100.0, 10.0)
gc.Stroke()
//gc.SetFont(f)
//gc.FillString("aaaaa")

最後の2行はコメントアウトしていますが、便利そうに見えるdraw2dパッケージですが文字列の描画はうまく動作しないようです。(draw2dパッケージのresource/fontディレクトリの内容をコピーするか、フォントディレクトリをして指定する)
ソースコードを確認していくと、読み込むフォントファイルのファイル名を生成する部分で妙な固定文字列が使用されており、フォントファイル名を編集するか、ソースコードを修正しなくてはならなさそうです。
さらには仮にフォントファイルを読み込めたとしてもパッケージ内でフォントファイルの内容をチェックしている部分があり、このチェックに引っかかりうまく読み込めないようです。(WindowsのFontディレクトリを指定してみましたがうまく動作しませんでした)
というわけでこのパッケージも万能というわけではなさそうです。
外にも似たようなパッケージがあるかもしれないので探してみても良いかもしれません。

まとめ

今回はImageの処理を試してみたわけですが、既存の言語と比較するとまだ若干弱いような気もしました。ただ画像を編集するような重い処理は行うなということかもしれません。(メッシュで分けてgoroutineで処理するとかやってみたいですね)
外部パッケージもありますがどういうことができるのかを検証するのにも時間が掛かりそうです。
今後の機能追加に期待したいです。