JPEG画像のリサイズ

Go で画像をリサイズしたいときには、github.com/nfnt/resize を使うといいらしい。

cf. github.com/nfnt/resize

今回はこれを使って、JPEG 画像のサムネイルを作ってみた。サムネイルは 120×120 に納まり、アスペクト比を保つものとした。

package main

import (
    "os"
    "image"
    "image/jpeg"

    "github.com/nfnt/resize"
)

func main() {
    orig_filename := os.Args[1]
    resized_filename := os.Args[2]

    orig_file, _ := os.Open(orig_filename)
    config, _, _ := image.DecodeConfig(orig_file)

    orig_file.Seek(0, 0)
    img, _, _ := image.Decode(orig_file)
    orig_file.Close()

    var resized_img image.Image
    if config.Width >= config.Height {
        resized_img = resize.Resize(120, 0, img, resize.Lanczos3)
    } else {
        resized_img = resize.Resize(0, 120, img, resize.Lanczos3)
    }

    out, _ := os.Create(resized_filename)
    jpeg.Encode(out, resized_img, nil)
    out.Close()
}

少し引っかかった点が2つ。
1つは、rezise.Resize 関数のサイズを指定する第1、第2引数の指定について。単に 120×120 にリサイズしたいだけならそのように指定すればいいけど、これだとアスペクト比が保存されずに正方形のサムネイルができてしまう。アスペクト比を保存するには、幅か高さのうち小さいほうに 0 を指定してやればいいんだけど、画像が横長なのか縦長なのかで場合分けをする必要があった。

もう1つは、場合分けをするために元画像の幅と高さが必要なので、image.DecodeConfig 関数で取得している。けど、その後、そのまま画像のデコードをしようと image.Decode 関数を呼び出してもエラーになってしまうこと。原因は、image.DecodeConfig 関数でファイルの途中まで読み込んでいるので、そのままだと image.Decode 関数はファイルの途中から読み込むことになって正常にデコードできない、ということだと思う。
そこで (*File) Seek 関数で読み込み位置をファイルの先頭に戻している。この関数は引数を2つとり、1つ目は一のオフセット、2つ目はそのオフセットをどこからにするかの指定。0 だとファイルの先頭から、1 だと現在位置から、2 だとファイルの終わりからになる。
というわけで、Seek(0, 0) として読み込み位置をファイルの先頭に戻してから image.Decode を呼び出して成功した。

一応実行例。

^o^ > go run img_resize.go sample.jpg resized.jpg

横長の画像も、縦長の画像もうまくサムネイルができた。