修論提出&Defense終了記念エントリです
Cult of the Party Parrot
一部界隈ではお馴染みParrotのアニメーションGIFが大量に公開されているサイトがある。
https://cultofthepartyparrot.com
ご丁寧にSlackのカスタムemojiに突っ込むためのzipまで用意していて、これまで数多くのSlackワークスペースがparrotsによって汚染侵略されてきた。
自分の身内のワークスペースには :parrot:
:ultrafastparrot:
:discoparrot:
だけ入れてる。
EXILEのあれみたいに複数羽が連なるGIF
ある日ふと「Choo Choo TRAINの出だしみたいなparrot GIF欲しいな」と思ってしまった。
そして「せっかくだしシェル芸で作ってみたいな」とも思ってしまった。
せっかく修論タスクが完了したので作ることにした。
おことわり
本エントリではImagemagickとAWKを使います。
あと純粋なシェル芸の定義からは逸れます(画像分割と結合のために複数回コマンド打つので)。
導入:parrotsをコマ分割
まずEXILEにする前にparrotたちをコマ分割する
$ convert parrot.gif -coalesce parrot_%d.gif
これで最初のGIFアニメーション(parrot.gif
)がparrot_0.gif
〜 parrot_9.gif
の各コマに分割される。
Step1 :phantomparrot:
最初にこんなのができてしまった。
これはEXILEというより残像だ。というわけで命名「:phantomparrot:
」
このGIFの生成過程を解説する。
まずは分解したparrotたちを重ねて1枚の画像にしなければいけない。
ここでparrot_0.gif
~ parrot_9.gif
を重ね合わせるコマンドを考える。
$ convert parrot_0.gif parrot_1.gif -composite parrot_2.gif -composite output.gif
上記は3枚のparrot静止画を重ねてoutput.gif
にするコマンドだ。
これを10枚分連ねるのは面倒だし、しかもそれぞれの画像が一番上の合成画像をいちいち用意するのも面倒くさい。
というわけで一発で 0 1 2 3 4 5 6 7 8 9 の画像、1 2 3 4 5 6 7 8 9 0 の画像、2 3 4 5 6 7 8 9 0 1 の画像…のように先頭からの順番が1つずつずれたGIF生成コマンドすべてをシェル芸1発で叩き出す。
参考にしたのは以下の記事。
床屋シェル芸 – Qiita
まずは数字だけを1つずらしで表示する。
$ yes "$(echo {0..9})" | xargs -n 11 | head -10 | cut -d' ' -f1-10
0 1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9 0
2 3 4 5 6 7 8 9 0 1
3 4 5 6 7 8 9 0 1 2
4 5 6 7 8 9 0 1 2 3
5 6 7 8 9 0 1 2 3 4
6 7 8 9 0 1 2 3 4 5
7 8 9 0 1 2 3 4 5 6
8 9 0 1 2 3 4 5 6 7
9 0 1 2 3 4 5 6 7 8
0から9の数字を繰り返し出力 → 1行11列に整形 → 先頭10行を切り出す → 10列目まで出力
の流れになっている。
あとはこれにparrot_N.gif
のようにファイル名をつけ、さらに各行がparrots_N.gif
(複数形)の画像を出力するようにコマンドを整えれば良い。
これはawk
でゴリ押す。
$ yes "$(echo parrot_{0..9}.gif)" | xargs -n 11 | head -10 | awk '{printf "convert %s",$1;for (i = 2; i < NF; i++){printf " %s -composite",$i};print " parrots_"NR-1".gif"}'
convert parrot_0.gif parrot_1.gif -composite parrot_2.gif -composite parrot_3.gif -composite parrot_4.gif -composite parrot_5.gif -composite parrot_6.gif -composite parrot_7.gif -composite parrot_8.gif -composite parrot_9.gif -composite parrots_0.gif
convert parrot_1.gif parrot_2.gif -composite parrot_3.gif -composite parrot_4.gif -composite parrot_5.gif -composite parrot_6.gif -composite parrot_7.gif -composite parrot_8.gif -composite parrot_9.gif -composite parrot_0.gif -composite parrots_1.gif
convert parrot_2.gif parrot_3.gif -composite parrot_4.gif -composite parrot_5.gif -composite parrot_6.gif -composite parrot_7.gif -composite parrot_8.gif -composite parrot_9.gif -composite parrot_0.gif -composite parrot_1.gif -composite parrots_2.gif
convert parrot_3.gif parrot_4.gif -composite parrot_5.gif -composite parrot_6.gif -composite parrot_7.gif -composite parrot_8.gif -composite parrot_9.gif -composite parrot_0.gif -composite parrot_1.gif -composite parrot_2.gif -composite parrots_3.gif
convert parrot_4.gif parrot_5.gif -composite parrot_6.gif -composite parrot_7.gif -composite parrot_8.gif -composite parrot_9.gif -composite parrot_0.gif -composite parrot_1.gif -composite parrot_2.gif -composite parrot_3.gif -composite parrots_4.gif
convert parrot_5.gif parrot_6.gif -composite parrot_7.gif -composite parrot_8.gif -composite parrot_9.gif -composite parrot_0.gif -composite parrot_1.gif -composite parrot_2.gif -composite parrot_3.gif -composite parrot_4.gif -composite parrots_5.gif
convert parrot_6.gif parrot_7.gif -composite parrot_8.gif -composite parrot_9.gif -composite parrot_0.gif -composite parrot_1.gif -composite parrot_2.gif -composite parrot_3.gif -composite parrot_4.gif -composite parrot_5.gif -composite parrots_6.gif
convert parrot_7.gif parrot_8.gif -composite parrot_9.gif -composite parrot_0.gif -composite parrot_1.gif -composite parrot_2.gif -composite parrot_3.gif -composite parrot_4.gif -composite parrot_5.gif -composite parrot_6.gif -composite parrots_7.gif
convert parrot_8.gif parrot_9.gif -composite parrot_0.gif -composite parrot_1.gif -composite parrot_2.gif -composite parrot_3.gif -composite parrot_4.gif -composite parrot_5.gif -composite parrot_6.gif -composite parrot_7.gif -composite parrots_8.gif
convert parrot_9.gif parrot_0.gif -composite parrot_1.gif -composite parrot_2.gif -composite parrot_3.gif -composite parrot_4.gif -composite parrot_5.gif -composite parrot_6.gif -composite parrot_7.gif -composite parrot_8.gif -composite parrots_9.gif
最後にこれらのコマンドをパイプでsh
に食わせ、parrots_N.gif
を10枚生成する。
以下のコマンドは先ほどのコマンドの後ろに | sh
がついてるだけである。
$ yes "$(echo parrot_{0..9}.gif)" | xargs -n 11 | head -10 | awk '{printf "convert %s",$1;for (i = 2; i < NF; i++){printf " %s -composite",$i};print " parrots_"NR-1".gif"}' | sh
実行すると、以下のように先頭が1コマずつずれたparrotたちの画像が出来上がる。
最後にこのparrotたちを1枚の無限ループ(-loop 0
)アニメーションgifに仕上げる。
$ convert parrots_[0-9].gif -loop 0 phantomparrot.gif
まとめると以下のようなシェルスクリプトになる。
convert parrot.gif -coalesce parrot_%d.gif
yes "$(echo parrot_{0..9}.gif)" | xargs -n 11 | head -10 | awk '{printf "convert %s",$1;for (i = 2; i < NF; i++){printf " %s -composite",$i};print " parrots_"NR-1".gif"}' | sh
convert parrots_[0-9].gif -loop 0 phantomparrot.gif
Step2 :marchingparrot:
さっきのはどうもEXILEっぽくない。
次に1コマに登場するparrotの量を減らすことを考える。
convert parrot.gif -coalesce parrot_%d.gif
yes "$(echo parrot_{0..9}.gif)" | xargs -n 11 | head -10 | awk '{printf "convert %s",$1;for (i = 2; i < 6; i++){printf " %s -composite",$i};print " parrots_"NR-1".gif"}' | sh
convert parrots_[0-9].gif -loop 0 marchingparrot.gif
Step 1との差分は、awk
のfor
文を i < NF
(列数)ではなく i < 6
に絞った点のみである。
これで出来上がるGIFが以下である。
それっぽくなったが、ちょっと残像感も拭えない?
parrotの行進ということで命名「:marchingparrot:
」
Step3 :exileparrot:
直前のコマが連続しているせいで残像感が出ているため、次は適度にコマを間引きする。
$ yes "$(echo {0..9})" | xargs -n 11 | head -10 | cut -d' ' -f1,3,5,7,9
0 2 4 6 8
1 3 5 7 9
2 4 6 8 0
3 5 7 9 1
4 6 8 0 2
5 7 9 1 3
6 8 0 2 4
7 9 1 3 5
8 0 2 4 6
9 1 3 5 7
Step 1の数列切り取りを改変して、1,3,5,7,9列目だけを抽出するようにする。
あとは同じようにawk
でコマンドを作ってsh
でコマンド実行する。
convert parrot.gif -coalesce parrot_%d.gif
yes "$(echo parrot_{0..9}.gif)" | xargs -n 11 | head -10 | cut -d' ' -f1,3,5,7,9 | awk '{printf "convert %s",$1;for (i = 2; i <= NF; i++){printf " %s -composite",$i};print " parrots_"NR-1".gif"}' | sh
convert parrots_[0-9].gif -loop 0 exileparrot.gif
これはEXILEのあれっぽい!(完)