262 名前:名無しさん@お腹いっぱい。[sage] 投稿日:2012/06/19(火) 14:28:43.22 ID:i9Ytn3cu0
Gif動画で二枚の絵をフェードイン・フェードアウトしてループするものを作るとき
手作業でもできるけど枚数が多いと面倒なんです。 なにかスクリプトやマクロありませんかね?

というのを見てスクリプトを書いてみようかと思ったので、
せっかくなのでチュートリアル風にしてみた。

1.まず関数の登録とか

他のScript-Fuをコピーして
名前を “fe-gifanime-morph.scm”にして

http://codepad.org/GAaRh4TM

こんな感じに書き換えます。
もちろん新規に作成してもOKです。
名前は別になんでもいいけど、わかりやすい名前にした方が色々と後悔が少ないでしょう。

内容説明

8行目までコメントで

15~25行目までで
script-fu-fe-gifanime-morphという関数を
メニューの”Filters/Animation/Gif Anime Morph”
から呼び出せるように設定。

10~13行目までで空の何もしない関数を定義

(gimp-image-undo-group-start img)と
(gimp-image-undo-group-end img)

で括っておくと、スクリプト内部でいろんな処理を行なってもユーザーは「もとに戻す」一回で戻れるので必ずつけます。

(gimp-displays-flush)は画像表示の再読み込み。
これを内部的には既に変更されてても画面が再描写されるまで変わらないのでこれも必ずつけときます。

まずここまで書いたら何も起きないメニューが追加されてるかを確認しましょう。
GIMPを既に立ち上げてる時にスクリプトの再読み込みは

Filters→Script-Fu→Refresh Scripts

で行います。

2.処理を考える

■どういう処理を具体的にするか考える。思いつかなかったら誰かに質問するか諦める。

■あきらめなかった場合は Help→Procedure Browserを見ながら頑張って書いていく。

自分が考えたのは2枚のレイヤーをそれぞれ

[レイヤー1:透明度  0%][レイヤー2:透明度100%]
[レイヤー1:透明度 20%][レイヤー2:透明度 80%]
[レイヤー1:透明度 40%][レイヤー2:透明度 60%]
[レイヤー1:透明度 60%][レイヤー2:透明度 40%]
[レイヤー1:透明度 80%][レイヤー2:透明度 20%]
[レイヤー1:透明度100%][レイヤー2:透明度 0%]

という風にしてから結合して適切に名前をつけるといいかな、と考えて書き始めました。

3. レイヤーが2枚の時だけ処理

■レイヤーが2枚以外だと何をしていいかわからないので何もしない。
■エラーメッセージぐらい出すべきだと思うけど、面倒臭いので出さない。

http://codepad.org/ZTiRyczM

良くあるミスがifを使うときに
(if 条件文 真の時の処理 偽の時の処理)
なのに、真の時の処理を一つ以上書いてしまってエラーになるというのがあります。

(begin 処理 処理 処理)

とまとめられるので、コレを使ってかきましょう。

4. とりあえず透明度50%同士で一枚作ってみる。

■どの関数使えばいいかとか全然わからないので、まず一枚作ってみる

http://codepad.org/QhvyVYwR

■各レイヤーにには32とかのIDが整数で割り振られてるけどそれを取ってくるのが面倒臭い。
■gimp-layer-set-opacity がlayerを返してくれないのでlayerを返す変数を作る。
■GIFのインデックスモードにしてたので、透明度50にしたら画像が消えて焦る。
●処理前に一旦RGBに変えて、処理後にインデックスモードに変えるないとダメっぽい。

んで、まあコレでとりあえず透明度変えた画像ができました。

5. 半透明のレイヤー2つを合成する

4で作った半透明のレイヤーを合成します。

http://codepad.org/5Lsuq1dw

・2つのレイヤーを合成するときは gimp-image-merge-down を使う。
・やっぱり gimp-image-add-layer がlayerを返してくれないので関数を作る

何故かScript-Fuは関数プログラミングなのに関数プログラミングをしにくい設定になってるんですが、これが使う人が少ない理由の一つなんでしょうか…。

6. 似たような処理を繰り返す

まず、よく使う処理を関数にして分割します。
http://codepad.org/Zn8WEzEx

ちゃんと動いてるのが確認できたので次は上で書いたように透明度を変えて複数のレイヤーを作成。

(add-merged-transparent-layers img layer1 layer2 100 0)
(add-merged-transparent-layers img layer1 layer2 80 20)
(add-merged-transparent-layers img layer1 layer2 60 40)
(add-merged-transparent-layers img layer1 layer2 40 60)
(add-merged-transparent-layers img layer1 layer2 20 80)
(add-merged-transparent-layers img layer1 layer2 0 100)      

そしてこれを書きなおして

(map (lambda (opacity)
       (add-merged-transparent-layers img layer1 layer2
                                    (- 100 opacity) opacity))
     '(0 20 40 60 80 100))

これだと汎用性が低すぎるので今は(元の画像の2枚を抜くと)4枚ある変化中のレイヤー数を指定できるように変えてみる。
http://codepad.org/zBexkAUE

変えたはいいけど、もうちょっと綺麗な書き方はないもんだろうか…。

ユーザーに枚数と時間を決めてもらうためにダイアログを表示する

次にユーザーに枚数を指定してもらうために SF-ADJUSTMENTを追加。
ついでに二枚の画像が入れ替わるための時間も設定(一枚あたり)

SF-ADJUSTMENT "枚数" '(5 1 30 1 5 0 1)
SF-ADJUSTMENT "変化時間(ms)" '(200 50 3000 1 10 0 1)

SF-ADJUSTMENTの引数のそれぞれの意味は

1枚から30枚までを選べて初期値は5枚。
ステップ幅小は1枚で、ステップ幅大は5枚。
intで、Roll Boxでの表示。

となっています。

GIFアニメに必要な名前をつける

GIF画像をアニメとして保存するときに格レイヤが何秒間表示されるかを設定することができます。

Frame 10 (100ms) とかで。

なのでその設定を行います。
http://codepad.org/0GdWVpjk

元からある画像を削除

元のレイヤー2枚はいらないので削除します。

(gimp-image-remove-layer img layer1)
(gimp-image-remove-layer img layer2)

GIFアニメ用に最適化

実装するとなると非常に時間がかかりそうですが、既にプラグインとして提供されてますのでGIFアニメ用に最適化を行っておきます。

(plug-in-animationoptimize 1 img (vector-ref (cadr (gimp-image-get-layers img)) 0))

最後の引数はプラグイン内では使われていないのですが、取り敢えず何かを設定する必要があるので一番上のレイヤーを設定しています。

完成!

以上でスクリプトは完成しました。
http://codepad.org/2lWsiHI0

あとはこれを普通に gif ファイルとして保存する時に「レイヤーを結合して保存する」か「GIFアニメとして保存する」という選択肢が出てきますのでGIFアニメとして保存すればOKです。

細かい秒数設定を変えたい場合はレイヤー名の (200ms) の部分を書き換えればOKです。

フェードアウトもつける

フェードインだけではなくて、フェードアウト機能もあった方がいいということなので付けました。

http://codepad.org/CWevEk4S

参考

Script-Fu 青空教室
http://kreisel.fam.cx/webmaster/file/script-fu/hp.vector.co.jp/authors/VA025935/script-fu/script-fu5.html

Leave a Reply