BLOG::はるかさん

はるかさん のブログです。近況情報や技術的な話題など。

go-libwebpとgo-libjpegをつくった

libwebpとlibjpegのGo bindingであるgo-libwebpとgo-libjpegをつくった。

それぞれWebPとJPEGファイルのデコード/エンコードに対応している。どちらもimage.Imageを扱うことが出来てimage/jpegimage/pngと同じ感覚で使えるインターフェースにしている。たとえばgo-libjpegのサンプルコードは次のような感じ。

package main

import (
    "bufio"
    "image/png"
    "log"
    "os"

    "github.com/pixiv/go-libjpeg/jpeg"
)

func main() {
    // Decoding JPEG into image.Image
    io, _ := os.Open("in.jpg")
    img, err := jpeg.Decode(io, &jpeg.DecoderOptions{})
    if err != nil {
        log.Printf("Decode returns error: %v\n", err)
        return
    }

    f, _ := os.Create("out.png")
    b := bufio.NewWriter(f)
    png.Encode(b, img)
    b.Flush()
    f.Close()
}

画像をimage.Imageで扱えるのでimage/pngや他のライブラリとのやりとりも簡単。既にimage/jpegを使ってる場合も少しの書き換えで使える。便利。

性能面では、go-libjpegimage/jpegに比べて1.9倍くらい速い。簡単なベンチマークでは次のような感じだった。

BenchmarkDecode                 1000      26345730 ns/op  # go-libjpegを使ってYCbCrにデコード
BenchmarkDecodeIntoRGB           500      30886383 ns/op  # go-libjpegを使ってRGBにデコード
BenchmarkDecodeWithNativeJPEG    300      49815928 ns/op  # image/jpegを使ってYCbCrにデコード

libjpeg-turboを使うともっと速くて、OS X上でhomebrewのlibjpeg-turbo 1.3.1を使うと次のようになった。image/jpegに比べて5倍くらい速い。

BenchmarkDecode                 2000       9557646 ns/op
BenchmarkDecodeIntoRGB          1000      12676414 ns/op
BenchmarkDecodeWithNativeJPEG    300      45836153 ns/op

オプションによってはさらに速いはずだ。どちらのライブラリもいくつかのオプションは渡せるようになっていて、go-libjpegは縮小デコード (imagemagickのヒントオプションとか言われてるやつ)に、go-libwebpは縮小とクロッピングに対応している。

ただし、あまりデバッグされていないし、最低限のテストしかないし、バージョンも切ってないけど、これからプロダクションで使う予定があるのでそれなりにバグはとれていくと思う。たぶん。プロダクションで動いた暁にはpixivのエンジニアブログに書こうと思います。JPEGのサブサンプリングやWebPのYCbCrまわりで結構はまったけど、それは別の機会にQiitaにでもまとめよう。