golangで画像圧縮
goで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にしたら元のサイズのままになります。
結果
圧縮前 | 圧縮後 |
![]() | ![]() |
1024 768 | 1000 750 |
763 KB | 137KB |
圧縮方式
圧縮方式は以下を指定します。
それぞれの圧縮アルゴリズムについては下記が参考になるかも。
画素の補間(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によくいるコアラで試したのですが、圧縮後の容量に差は出ませんでした。
肝心の精度(画像の劣化)についてですが、ぶっちゃけわかりません。
![]() | ![]() | ![]() |
元画像 | Nearestによる圧縮 | Lanczos3による圧縮 |
WEBに乗っける用途ならどの圧縮方式を選択してもOKそうです。
簡単に圧縮が実装できちゃうのはありがたいですね。
どうでもいいけどgithubのDownsizing Samples見てたら目が痛くなったよ!