裏紙に書く程度の内容

golangで画像圧縮

goでResizeを使用して画像を圧縮します。

プログラム初心者でも簡単に画像圧縮できちゃいます。

github.com/nfnt/resize

パッケージのインストール

$ go get github.com/nfnt/resize

かんたん。

使用方法

とりあえず使ってみます。といってもUsageそのまま。

package main

import (
    "github.com/nfnt/resize"
    "image/jpeg"
    "log"
    "os"
)

func main() {

    // open test.jpg
    file, err := os.Open("test.jpg")
    if err != nil {
        log.Fatal(err)
    }

    // decode jpeg into image.Image
    img, err := jpeg.Decode(file)
    if err != nil {
        log.Fatal(err)
    }
    file.Close()

    // Lanczos3 で圧縮
    m := resize.Resize(1000, 0, img, resize.Lanczos3)

    out, err := os.Create("test_resized.jpg")
    if err != nil {
        log.Fatal(err)
    }
    defer out.Close()

    jpeg.Encode(out, m, nil)

}

なるほど、Resizeで横、縦サイズ、圧縮方法を指定すればよろしいらしい。

縦横はどちらかを0にすると縦横比率を維持したままになります。ちなみに両方0にしたら元のサイズのままになります。

結果







































圧縮前圧縮後
testtest_resized
1024 7681000 750
763 KB137KB

圧縮方式

圧縮方式は以下を指定します。

それぞれの圧縮アルゴリズムについては下記が参考になるかも。

画素の補間(Nearest neighbor,Bilinear,Bicubic) 画像処理ソリューション

デジタル・フロンティア-Digital Frontier | DF TALK | 分かる!画像補間 ~基本から応用まで~

NearestNeighbor(最近傍補間)

高速でかつ色数が変化しない所が利点ですが、その分クオリティは低めでギザギザが目立ちます。
特に文字を含む素材や実写の素材ではあまり使わない方がいいでしょう。

Bilinear(双一次補間)

ただ、全体的にぼやけたようになってしまうので、輪郭を強調させたいアニメ調の絵などは苦手な傾向にあります。

Bicubic(双三次補間)

よく用いられている手法の中で特に高クオリティな結果が出せるのがこれ。
ニアレストネイバーやバイリニアに比べると遠くまで見る分速度は落ちますが、
ぼやけもギザギザも少なく、色々な状況に対応できます。

MitchellNetravali

?(よくわかりませんでした)

Lanczos2,3(ランツォシュ)

バイキュービック法と同等以上の高クオリティを期待できる手法がこれ。
ただし他の3手法と比べると実装されていないソフトも多めで、
さらに遠くのピクセルまで参照するため速度は明らかに遅いです。

とりあえず Lanczos3 で圧縮しておけばよさげです。
“速度は明らかに遅いです”ってことなので、一番軽そうな NearestNeighbor と比較してみます。

圧縮方式によるパフォーマンス比較

package main

import (
    "fmt"
    "github.com/nfnt/resize"
    "image"
    "image/jpeg"
    "log"
    "os"
    "time"
)

func main() {

    // open test.jpg
    file, err := os.Open("test.jpg")
    if err != nil {
        log.Fatal(err)
    }

    // decode jpeg into image.Image
    img, err := jpeg.Decode(file)
    if err != nil {
        log.Fatal(err)
    }
    file.Close()

    fmt.Println(time.Now())
    for i := 0; i < 1000; i++ {
        // [NearestNeighbor(最近傍補間)]
        outResizedFile(img, resize.NearestNeighbor, "test_nearest.jpg")
    }
    fmt.Println(time.Now())

    fmt.Println(time.Now())
    for i := 0; i < 1000; i++ {
        // Lanczos3 で圧縮
        outResizedFile(img, resize.Lanczos3, "test_lanczos3.jpg")
    }
    fmt.Println(time.Now())

}

func outResizedFile(img image.Image, interp resize.InterpolationFunction, name string) {

    m := resize.Resize(1000, 0, img, interp)
    out, err := os.Create(name)
    if err != nil {
        log.Fatal(err)
    }
    defer out.Close()

    jpeg.Encode(out, m, nil)

}

NearestNeighbor,Lanczos3でそれぞれ1,000回圧縮してその差を見てみます。

$ go run bench.go
2015-05-14 21:13:34.6554924 +0900 JST
2015-05-14 21:16:29.2344778 +0900 JST ( 2m 55s )
2015-05-14 21:16:29.2354778 +0900 JST
2015-05-14 21:21:13.4067315 +0900 JST ( 4m 44s)

109秒 差が出ました。1000回試行なので平均して1回あたり0.1s差があることになります。
大量に繰り返さないのであれば誤差のレベルかと思います。

windowsによくいるコアラで試したのですが、圧縮後の容量に差は出ませんでした。
肝心の精度(画像の劣化)についてですが、ぶっちゃけわかりません。



























testtest_nearesttest_lanczos3
元画像Nearestによる圧縮Lanczos3による圧縮

WEBに乗っける用途ならどの圧縮方式を選択してもOKそうです。

簡単に圧縮が実装できちゃうのはありがたいですね。

どうでもいいけどgithubのDownsizing Samples見てたら目が痛くなったよ!

Index
広告