2016年01月07日
Go 言語でアニメーション GIF を作成する
Golang でアニメーション GIF を作る手順を 3 通り紹介します。
- フレームごとの画像から生成
- ビデオから生成
- Go 言語で最初から生成
フレームごとの画像から生成
こんな GIF 画像があったとします (ここ より拝借)。
変換結果はこんな感じ。
生成するためのコードはこんな感じ。
package main
import "image"
import "image/gif"
import "os"
func main() {
files := []string{"g1.gif", "g2.gif","g3.gif", "g2.gif"}
// 各フレームの画像を GIF で読み込んで outGif を構築する
outGif := &gif.GIF{}
for _, name := range files {
f, _ := os.Open(name)
inGif, _ := gif.Decode(f)
f.Close()
outGif.Image = append(outGif.Image, inGif.(*image.Paletted))
outGif.Delay = append(outGif.Delay, 0)
}
// out.gif に保存する
f, _ := os.OpenFile("out.gif", os.O_WRONLY|os.O_CREATE, 0600)
defer f.Close()
gif.EncodeAll(f, outGif)
}
注意したいポイントは次の通り。
- フレームの GIF を順番に
gif.Decode
で読み込んでいる。JPEG から生成するには、GIF への変換処理を実装する必要がある (goanigiffy ではgif.Encode
とgif.Decode
を呼んで変換している)。 - GIF アニメーションを生成するには
gif.EncodeAll
を呼ぶ。
ビデオから生成
MPlayer を使って各フレームの画像を抽出してから、goanigiffy で GIF アニメーションを生成する。MPlayer にもアニメーション GIF を生成する機能はあるようだが、ディザリングがしょぼいので、この方法がよいらしい (詳しくは GoAniGiffy を参照)。
Go 言語で最初から生成
こんな感じのものを作ってみた。
各フレームの画像を Go 言語で描画して []*image.Paletted
を作って、gif.EncodeAll
に渡している。
var images []*image.Paletted
var delays []int
// 20 個の画像を生成して円を描く
for step := 0; step < 20; step++ {
img := image.NewPaletted(image.Rect(0, 0, w, h), palette)
images = append(images, img)
delays = append(delays, 0)
// 描画処理 (長いので省略)
}
// rgb.gif に保存する
f, _ := os.OpenFile("rgb.gif", os.O_WRONLY|os.O_CREATE, 0600)
defer f.Close()
gif.EncodeAll(f, &gif.GIF{
Image: images,
Delay: delays,
})
全体のコードはこんな感じ。
package main
import (
"image"
"image/color"
"image/gif"
"math"
"os"
)
type Circle struct {
X, Y, R float64
}
func (c *Circle) Brightness(x, y float64) uint8 {
var dx, dy float64 = c.X - x, c.Y - y
d := math.Sqrt(dx*dx+dy*dy) / c.R
if d > 1 {
return 0
} else {
return 255
}
}
func main() {
var w, h int = 240, 240
var hw, hh float64 = float64(w / 2), float64(h / 2)
circles := []*Circle{&Circle{}, &Circle{}, &Circle{}}
var palette = []color.Color{
color.RGBA{0x00, 0x00, 0x00, 0xff},
color.RGBA{0x00, 0x00, 0xff, 0xff},
color.RGBA{0x00, 0xff, 0x00, 0xff},
color.RGBA{0x00, 0xff, 0xff, 0xff},
color.RGBA{0xff, 0x00, 0x00, 0xff},
color.RGBA{0xff, 0x00, 0xff, 0xff},
color.RGBA{0xff, 0xff, 0x00, 0xff},
color.RGBA{0xff, 0xff, 0xff, 0xff},
}
var images []*image.Paletted
var delays []int
steps := 20
for step := 0; step < steps; step++ {
img := image.NewPaletted(image.Rect(0, 0, w, h), palette)
images = append(images, img)
delays = append(delays, 0)
θ := 2.0 * math.Pi / float64(steps) * float64(step)
for i, circle := range circles {
θ0 := 2 * math.Pi / 3 * float64(i)
circle.X = hw - 40*math.Sin(θ0) - 20*math.Sin(θ0+θ)
circle.Y = hh - 40*math.Cos(θ0) - 20*math.Cos(θ0+θ)
circle.R = 50
}
for x := 0; x < w; x++ {
for y := 0; y < h; y++ {
img.Set(x, y, color.RGBA{
circles[0].Brightness(float64(x), float64(y)),
circles[1].Brightness(float64(x), float64(y)),
circles[2].Brightness(float64(x), float64(y)),
255,
})
}
}
}
f, _ := os.OpenFile("rgb.gif", os.O_WRONLY|os.O_CREATE, 0600)
defer f.Close()
gif.EncodeAll(f, &gif.GIF{
Image: images,
Delay: delays,
})
}
以上です。