9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

GameWithAdvent Calendar 2019

Day 7

Goで画像を扱う話

Last updated at Posted at 2019-12-07

気圧が変動が大きいとへばってしまう体をなんとかしたいと思ってます @sys_cat です。
普段はGameWithで設計書を書いてみたりサーバサイドのコーディングを行ったりしています。最近の推しゲームはアッシュアームズです。細かい動きが可愛いのですよね。
昨日は @fukiworks さんの 「Adobe MAX Japan 2019」で発表された激アツ最新トピック5選 【デザイナー独断】 でした。
デザイナさんとお仕事してると面白いですよね。考え方の方向性が違うのに似た結論に行くこともあるので楽しんで仕事出来ますね。


さて、今回はGoで画像を扱う方法についてです。
弊社はPHPの会社ですがツール周りはGoが多くなってきたんですね。画像を扱う時はよくImageMagicを使うと思うんですがよくある画像の表示、操作は割とGoでも出来ます。

JPEGとPNGを読み込んでみよう

Webサイトで表示する鉄板のJPEGとPNGの画像ファイルを読み込んでサイズなどを取得してみます。

利用する画像

利用する画像は go.dev で紹介されている Brand Guidline からロゴファイルをダウンロードして見ていきます。

縦横の幅を取得する

main.go
package main

import (
	"fmt"
	"image/jpeg"
	"image/png"
	"os"
)

func main() {
	// JPEG
	j, err := os.Open("../tmp/Go-Logo_LightBlue.jpg")
	if err != nil {
		panic(err)
	}
	defer j.Close()
	img, err := jpeg.Decode(j)
	if err != nil {
		panic(err)
	}
	bound := img.Bounds()
	fmt.Printf("Go-Logo_LightBlue.jpg\tX : %d px, Y : %d px\n", bound.Dx(), bound.Dy())

	// PNG
	p, err := os.Open("../tmp/Go-Logo_Yellow.png")
	if err != nil {
		panic(err)
	}
	defer p.Close()
	img, err = png.Decode(p)
	if err != nil {
		panic(err)
	}
	bound = img.Bounds()
	fmt.Printf("Go-Logo_Yellow.png\tX : %d px, Y : %d px\n", bound.Dx(), bound.Dy())
}

結果

$ go run main.go 
Go-Logo_LightBlue.jpg	X : 1061 px, Y : 938 px
Go-Logo_Yellow.png	X : 1062 px, Y : 938 px

基本的に os.Open() で画像ファイルを開いて <image.Type>.Decode(*os.File) でファイルをDecodeしてファイルのデータを取得することが出来ます。

それぞれのファイルのサイズがあってるか確認しましょう。

JPEG
Screenshot from 2019-12-07 18-35-47.png

PNG
Screenshot from 2019-12-07 18-36-13.png

うん、正しく取れてますね。

リサイズしてみよう

Webで画像を扱う時にはリサイズする必要が多かったりしますのでその辺りも行ってみましょう。

画像を取得してリサイズ画像を生成する(ちょっとコードをが冗長です)

main.go
package main

import (
	"fmt"
	"image"
	"golang.org/x/image/draw"
	"image/jpeg"
	"image/png"
	"os"
)

func main() {
	// JPEG
	j, err := os.Open("../tmp/Go-Logo_LightBlue.jpg")
	if err != nil {
		panic(err)
	}
	defer j.Close()
	img, err := jpeg.Decode(j)
	if err != nil {
		panic(err)
	}
	bound := img.Bounds()
	fmt.Printf("Before\tGo-Logo_LightBlue.jpg\tX : %d px, Y : %d px\n", bound.Dx(), bound.Dy())

	dst := image.NewRGBA(image.Rect(0, 0, bound.Dx()/2, bound.Dy()/2))
	draw.CatmullRom.Scale(dst, dst.Bounds(), img, bound, draw.Over, nil)

	jOutput, err := os.Create("./new.jpg")
	if err != nil {
		panic(err)
	}
	defer jOutput.Close()

	err = jpeg.Encode(jOutput, dst, &jpeg.Options{Quality: 100})
	if err != nil {
		panic(err)
	}

	reopen, err := os.Open("./new.jpg")
	if err != nil {
		panic(err)
	}
	defer reopen.Close()
	img, _ = jpeg.Decode(reopen)
	bound = img.Bounds()
	fmt.Printf("After\tGo-Logo_LightBlue.jpg\tX : %d px, Y : %d px\n", bound.Dx(), bound.Dy())

	// PNG
	p, err := os.Open("../tmp/Go-Logo_Yellow.png")
	if err != nil {
		panic(err)
	}
	defer p.Close()
	img, err = png.Decode(p)
	if err != nil {
		panic(err)
	}
	bound = img.Bounds()
	fmt.Printf("Before\tGo-Logo_Yellow.png\tX : %d px, Y : %d px\n", bound.Dx(), bound.Dy())

	dst = image.NewRGBA(image.Rect(0, 0, bound.Dx()/2, bound.Dy()/2))
	draw.CatmullRom.Scale(dst, dst.Bounds(), img, bound, draw.Over, nil)

	pOutput, err := os.Create("./new.png")
	if err != nil {
		panic(err)
	}
	defer pOutput.Close()

	err = png.Encode(pOutput, dst)
	if err != nil {
		panic(err)
	}

	reopen, err = os.Open("./new.png")
	if err != nil {
		panic(err)
	}
	defer reopen.Close()
	img, _ = png.Decode(reopen)
	bound = img.Bounds()
	fmt.Printf("After\tGo-Logo_Yellow.png\tX : %d px, Y : %d px\n", bound.Dx(), bound.Dy())
}

結果

$ go run main.go 
Before	Go-Logo_LightBlue.jpg	X : 1061 px, Y : 938 px
After	Go-Logo_LightBlue.jpg	X : 530 px, Y : 469 px
Before	Go-Logo_Yellow.png	X : 1062 px, Y : 938 px
After	Go-Logo_Yellow.png	X : 531 px, Y : 469 px

画像も正しく作られてますね。PNGだと色の量とか透過とかありますのでサイズだけでも無いみたいなのでサイズについてはあんまり変わりませんね。
JPEGは顕著ですね。ちゃんと半分以下になっています。

Screenshot from 2019-12-07 19-09-14.png

こぼれ話

Decodeimage.Decode(*os.File) で代用することが出来ます。FileTypeを返すのでFileType毎にEncodeすることも出来ますので汎用化できそうですね。

まとめ

普段ImageMagicなどでやっていた変換系の対応をコードで実現出来るのはとても夢があることですね。やろうと思えばBase64の文字列をjpegとして変換することも出来るとのことで画像処理は夢が多いなぁって感じですね。

Goでの画像変換系のネタは割とあるあるネタです。そんな画像ネタを使うのも抵抗がありましたが外部ライブラリを使わずに書けたので一先ずまんぞくです。
今月はもう1本Goについて書く予定なのでそこでWebp周りの読み込みだったりjpeg to webp あたりをやっていこうかなと思ってます。

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?