🖼️

[Go]画像をリサイズして、dataサイズを圧縮する

5 min read

はじめに

最近のケータイで撮った画像は高画質になった分、画像のデータサイズが大きくなり、
画像をたくさん表示するwebページが重くなる傾向にあります。
画像のリサイズをすると、データサイズが小さくなるので、速度を落とさずに済みます。

今回はそんなリサイズを試してみます。

今回使用するパッケージは
"github.com/nfnt/resize"
こちらです。

使用する理由は
画像の大きさを指定する際に、
通常はheightとwidthどちらも指定が必要ですが、
このパッケージは片方だけ指定すれば、それに合わせて、元サイズと同じ比率で小さくしてくれるのが良い!!

早速やってみます。

環境

macOS 11.4
go 1.16

今回リサイズしたい写真

  • 桜の写真(jpg)
    sakura.jpg

サイズ 3264 × 2448 で 3.4MBもあります。

  • サメの写真(png)
    shark.png

サイズ 3024 × 4032 なんと 8.2MB!?

この2つの写真をリサイズして画像データのサイズも小さくしようと思います。

ソースツリー

こんな感じで元データをimageフォルダに入れてます。
そして、サムネイル化出来たものをthumbnailフォルダに格納します。

ソースコード


package main

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

	"github.com/nfnt/resize"
)

const imagePath = "image/"
const thumbnailPath = "thumbnail/"
const width = 256
const height = 0

// ここに元データのファイル名を書きます。
var imageName = "sakura.jpg"
// 変更後のファイル名を書きます。
var thumbnailName = "sakura-thumbnail"

func main() {
	resizeImage()
}

func resizeImage() {
	// 元の画像の読み込み
	file := imagePath + imageName
	fileData, err := os.Open(file)
	if err != nil {
		log.Fatal(err)
	}

	// 画像をimage.Image型にdecodeします
	img, data, err := image.Decode(fileData)
	if err != nil {
		log.Fatal(err)
	}
	fileData.Close()

	// ここでリサイズします
	// 片方のサイズを0にするとアスペクト比固定してくれます
	resizedImg := resize.Resize(width, height, img, resize.NearestNeighbor)

	// 書き出すファイル名を指定します
	createFilePath := thumbnailPath + thumbnailName + "." + data
	output, err := os.Create(createFilePath)
	if err != nil {
		log.Fatal(err)
	}
	// 最後にファイルを閉じる
	defer output.Close()

	// 画像のエンコード(書き込み)
	switch data {
	case "png":
		if err := png.Encode(output, resizedImg); err != nil {
			log.Fatal(err)
		}
	case "jpeg", "jpg":
		opts := &jpeg.Options{Quality: 100}
		if err := jpeg.Encode(output, resizedImg, opts); err != nil {
			log.Fatal(err)
		}
	default:
		if err := png.Encode(output, resizedImg); err != nil {
			log.Fatal(err)
		}
	}
}

こんな感じになりました。
下記で少しだけ解説しようと思います。

解説

1

const imagePath = "image/"
const thumbnailPath = "thumbnail/"
const width = 256
const height = 0

// ここに元データのファイル名を書きます。
var imageName = "sakura.jpg"
// 変更後のファイル名を書きます。
var thumbnailName = "sakura-thumbnail"

この部分は後で使う変数や定数を定義しています。
varの部分に元データや変更後のファイル名を書きます。

2

	// 元の画像の読み込み
	file := imagePath + imageName
	fileData, err := os.Open(file)
	if err != nil {
		log.Fatal(err)
	}

	// 画像をimage.Image型にdecodeします
	img, data, err := image.Decode(fileData)
	if err != nil {
		log.Fatal(err)
	}
	fileData.Close()

この部分は元の画像を読み込んでいます。
imageというフォルダにいる画像を取得するため、pathが

"image/sakura.jpg"

になるように、定数と変数を呼び出してます。
あとは、後のresizeで画像をimage.Image型にしておかないと使えないので、
image.Decodeでデコードしています。
その際に、dataという変数に画像の拡張子が返ってきます。

3

	// ここでリサイズします
	// 片方のサイズを0にするとアスペクト比固定してくれます
	resizedImg := resize.Resize(width, height, img, resize.NearestNeighbor)

ここでresizeパッケージでリサイズされます。
引数に(横幅,高さ,画像data,補間フィルタ)を指定します。
私は定数でwidth=256,height=0と指定していますが、リテラルで直接数字を書く事も可能です。
横幅か高さのどちらかを 0 にすれば、指定した方に合わせてアスペクト比で小さくしてくれます。
(つまり同じ割合で、いい感じに小さくしてくれます)
最後の補間フィルタはよく分かりません。リサイズする時の方法みたいなのですが・・・
詳しく知りたい方は下記の記事を参考にされるといいと思います。

https://qiita.com/yoya/items/f167b2598fec98679422

4

	// 書き出すファイル名を指定します
	createFilePath := thumbnailPath + thumbnailName + "." + data
	output, err := os.Create(createFilePath)
	if err != nil {
		log.Fatal(err)
	}
	// 最後にファイルを閉じる
	defer output.Close()

ここでサムネイル化した後のファイル名を指定しています。
これも上で変数にしています。
2.でお伝えしましたが、dataの中に拡張子が入ってますので、それを利用します。

5


	// 画像のエンコード(書き込み)
	switch data {
	case "png":
		if err := png.Encode(output, resizedImg); err != nil {
			log.Fatal(err)
		}
	case "jpeg", "jpg":
		opts := &jpeg.Options{Quality: 100}
		if err := jpeg.Encode(output, resizedImg, opts); err != nil {
			log.Fatal(err)
		}
	default:
		if err := png.Encode(output, resizedImg); err != nil {
			log.Fatal(err)
		}
	}

ここは拡張子によってswitchさせています。
decodeはimageパッケージでjpgでもpngでも対応出来ましたが、
encodeはjpgとpngで分けないと出来なかった為、switch文で切り分けしています。

実行

go run main.go

変数のファイル名はご自由に変更ください。

結果

桜のサムネイル写真
sakura-thumbnail.jpeg

サイズ 256 × 192 で 70KBに減りました。

サメのサムネイル写真
shark-thumbnail.png

サイズ 256 × 342 で 90KBに減りました。

とても小さくなりました。
サイズは色々指定できますので、遊んでみてください。

参考

https://github.com/nfnt/resize

注意

こちらのパッケージは2018年より更新されておりませんので、気になる方は別のパッケージをご利用ください。