LoginSignup
21
10

More than 3 years have passed since last update.

Goで画像からアニメGifを生成する

Last updated at Posted at 2017-12-12

初めに

この記事はGo Advent Calendar 2017の13日目の記事です。

SlackのemojiをGolangであれこれしていた時に、このような記事を見かけました。
Slackで寿司を回転させる技術
gifアニメの仕組みがちょっとだけ分かってきた時期だったので、ひょっとしてこれはGolangでgifアニメ化からアップロードまで一気通貫で出来るのではないか…?と思いながらも中々手をつけずにいました。
今回よい機会だったので、gifアニメを作るツールを作ってみました。

以下サンプルです。

gopher_animated.gif

gopher_bottom_animated.gif

サイズが大きくてすみません!

概要

ソースは以下レポジトリとなります。
gif_anime_creator_go

下記フォーマットの画像をアニメーションGifにすることが出来ます。

  • jpg
  • png
  • gif

アニメーションGifと言ってもシンプルなもので、画像が右から左、上から下などに流れる様なものです。
開始方向と終了方向を選べるので、サンプルの様に下から出てきて下に引っ込むといったことも可能です。

使用方法

gif_anime_creator_go [-start=position] [-end=position] image_file

positionにはleft、right、top、bottomが入れられます。
デフォルトではleftとrightで、左から右に流れるように動きます。

組み合わせは自由に出来ますので、好きな様に動かしてください!
配布用の実行ファイルが用意できていないので、後で用意しようと思っています。

つまずいたポイント

開発中につまずいたポイントを幾つか紹介します。

1.画像を動かすことがうまく出来ない
SubImageで画像を切り取った後に移動後のポジションから描画すればよいと思っていたのですが、いざやってみると動きがおかしい。
思ったような動きにならずにハマりました。

解決策
難しいことをする必要はありませんでした。
描画する際にポジションを移動した分だけずらしてあげればちゃんと描画してくれました。
ポイントとしてはx軸に-(マイナス)を指定すると左に移動、+(プラス)なら右に移動します。
y軸の場合は-は上、+は下です。

// Pointは(x,y)で表現されるので、ここをずらすだけでよい
draw.Draw(Palette, Rectangle, Image, Point, draw.Src)

2.背景色が透明にならない
これは中々悩みました…。
原因はPaletteの中に透明色がないからという事は早い段階で分かったのですが、自然に見えるように減色しつつ透明色を追加する方法が分からず、かなり悩みました…。

解決策
こちらを使わせて頂きました。
soniakeys/quant
指定した数の色に減色してくれるので、255色に減色してから透明色を追加することで色の問題は解決しました。
上記ライブラリはこちらの記事を参考にさせて頂きました。
Go言語で画像の減色を行う
(gifアニメは作ったのか気になります。)

3.残像が残る
gifアニメが出来たのはいいのですが、残像が残ってしまい、まるで瞬獄殺の様になってしまいました。
gifは差分のみ描画されるということは以前の経験でわかっていたのですが、これをどうしたらよいのかは当初分かりませんでした。

解決策
gifアニメは毎フレームの描画のモードがいくつかあって、モードによっては差分しか描画されません。
GolangのGifでEncodeAllを実行すると、デフォルトでは最適化(差分のみ)になります。
この辺りがさっぱり分かっておらず、gifアニメの仕様を見てようやく理解できました。
※参考リンク
GIF Animation and Disposal Methods - GIF Animation Studio
Cover Sheet for the GIF89a Specification
GolangのGIFのstructを見ると、Disposalと言うものがあります。
特に何も指定しないと0がセットされると書かれています。

type GIF struct {
        Image     []*image.Paletted // The successive images.
        Delay     []int             // The successive delay times, one per frame, in 100ths of a second.
        LoopCount int               // The loop count.
        // Disposal is the successive disposal methods, one per frame. For
        // backwards compatibility, a nil Disposal is valid to pass to EncodeAll,
        // and implies that each frame's disposal method is 0 (no disposal
        // specified).
        Disposal []byte

上記資料を確認してDisposalに2をセットすることにより、Restore to background colorの動作をするようになったので、無事に残像が残ることはなくなりました。

4.謎の透明色が画像内に出る
幾つか画像をgifアニメにした時、画像内に透明色が出てしまっておかしくなることがありました。
draw.FloydSteinberg.Drawを使って描画していたので、どうしたらよいのか分からず、解決できるか悩みました。

解決策
draw.FloydSteinberg.Drawを止めて、draw.Drawを使う様にしたら発生しなくなりました。
ただし、FloydSteinbergの方が画質は良かったため、若干完成時の見た目は悪くなりました。
これに関しては今はよい方法が分からないため、一旦このままです…。

残課題

元々Slackでループさせる様にしたかったのですが、今のままだと並べてもスムーズに繋がりません。
この点に関しては今後機能追加したいと考えています。
また、Slackにアップロードするツールとの連携も出来ていないので、こちらも作りたいと思っています。

感想

最初はそこそこ理解しているつもりだったのでそこまでかからないと思っていましたが、やってみるとあちこちでつまずいて想定以上に時間がかかりました。
やっぱりやってみることで学ぶことは多いなーと感じました。

参考リンク

本文に出た以外に、以下リンクを参考にさせて頂きました。
ありがとうございました。
Go 言語でアニメーション GIF を作成する

今後も何かしら作っていきたいと思いますm(_ _)m

21
10
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
21
10